This component no longer exists. It was in ns-ui’s manifest when we first indexed it and is not there now — the registry served its inventory on 2026-08-07 and this item was not on it. The install command below will fail. It is kept here because a tutorial somewhere still tells people to run it.
Hero Long Exposure
ns-ui/hero-long-exposureRemovedComponent
Hero card whose canvas behaves like a long-exposure photograph: cursor movement burns light streaks that never clear per frame, only decay on a 4s half-life, accumulating a light painting of the session.
Live preview
live · rendered by the registry
Rendered by ns-ui on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://design.helpmarq.com/r/hero-long-exposure.jsonSource
1"use client";23import { useEffect, useRef, type ReactNode } from "react";45// ---------------------------------------------------------------------------6// SolargraphHero — hero surface that behaves like a long-exposure photograph:7// cursor movement burns light streaks into offscreen accumulation buffers that8// are never cleared per frame, only decayed on a slow half-life, so the canvas9// builds a "light painting" of the whole session. Persistence, not live10// emission, is the differentiator. Buffers store pure intensity (white ink);11// theme tint is applied at composite time via a source-in pass, so a theme12// flip recolors the entire accumulated exposure instantly. Canvas 2D,13// refs-only hot path, sleeps when the pointer rests and residual energy14// decays under an epsilon.15// ---------------------------------------------------------------------------1617type Vec3 = [number, number, number];1819function parseColor(raw: string): Vec3 | null {20 const s = raw.trim();21 if (s.startsWith("#")) {22 const hex = s.slice(1);23 if (hex.length === 3) {24 const r = parseInt(hex.slice(0, 1) + hex.slice(0, 1), 16);25 const g = parseInt(hex.slice(1, 2) + hex.slice(1, 2), 16);26 const b = parseInt(hex.slice(2, 3) + hex.slice(2, 3), 16);27 return Number.isNaN(r + g + b) ? null : [r, g, b];28 }29 if (hex.length >= 6) {30 const r = parseInt(hex.slice(0, 2), 16);31 const g = parseInt(hex.slice(2, 4), 16);32 const b = parseInt(hex.slice(4, 6), 16);33 return Number.isNaN(r + g + b) ? null : [r, g, b];34 }35 return null;36 }37 const m = s.match(/rgba?\(\s*([\d.]+)[,\s]+([\d.]+)[,\s]+([\d.]+)/);38 return m ? [Number(m[1]), Number(m[2]), Number(m[3])] : null;39}4041function mix(a: Vec3, b: Vec3, t: number): Vec3 {42 return [43 Math.round(a[0] + (b[0] - a[0]) * t),44 Math.round(a[1] + (b[1] - a[1]) * t),45 Math.round(a[2] + (b[2] - a[2]) * t),46 ];47}4849function css(c: Vec3): string {50 return `rgb(${c[0]},${c[1]},${c[2]})`;51}5253// deterministic prng — buffer picks + the seeded reduced-motion exposure54function mulberry32(a: number) {55 return () => {56 a |= 0;57 a = (a + 0x6d2b79f5) | 0;58 let t = Math.imul(a ^ (a >>> 15), 1 | a);59 t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;60 return ((t ^ (t >>> 14)) >>> 0) / 4294967296;61 };62}6364const SLEEP_MS = 500; // pointer-idle threshold before the loop may sleep65const ENERGY_EPS = 0.02; // residual-exposure floor — below this, sleep + wipe66// … truncated
