{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "toner-fuse-streak",
  "title": "Toner Fuse Streak",
  "description": "A loader / progress surface that prints its content in the way a laser printer images a page: a top-to-bottom expose/develop wipe with toner visibly catching behind it, fine edges thinning for 400ms before filling to full density, and a persistent starvation streak on any large fill area, locked down by a single fuse-roller highlight pass. An unbounded charge/wipe/hold/reset loop, not a spinner.",
  "dependencies": [],
  "files": [
    {
      "path": "registry/core/toner-fuse-streak/component.tsx",
      "content": "\"use client\";\n\nimport { useEffect, useRef } from \"react\";\nimport type { CSSProperties, ReactNode } from \"react\";\n\n// ---------------------------------------------------------------------------\n// TonerFuseStreak — a loader / progress surface that \"prints in\" content the\n// way a laser printer images a page, rather than spinning or fading: charge,\n// a top-to-bottom expose/develop wipe, toner catching behind it, then a fuse\n// flash locking it down. Two real xerographic failure artifacts ride along\n// (source: standard charge/expose/develop/transfer/fuse cycle + print-shop\n// service literature) and ARE the identity, not garnish:\n//   - edge deletion: fringing-field effects thin toner right at small-feature\n//     boundaries (hairline rules, serifs) for the first 400ms after the wipe\n//     passes, then it fills to full density.\n//   - toner starvation: a large solid fill area locally depletes the toner\n//     cloud faster than it replenishes, leaving a persistent lighter streak\n//     band toward the trailing edge of the fill.\n//\n// Shape source: `children`, if passed, is mounted off-screen (visibility\n// hidden, never displayed) purely to donate a silhouette — text lines become\n// thin bars via Range.getClientRects() (exact per-line boxes, no manual\n// wrap math), leaf elements with a visible background/border/media tag\n// become filled blocks. No children -> a built-in placeholder (heading +\n// three text lines + one wide media block) that alone is enough to trigger\n// both artifacts, so this ships as `core` without ever requiring a caller\n// snapshot. The traced silhouette is rasterized ONCE per layout into a\n// luminance mask on an offscreen canvas, then downsampled to one toner cell\n// per ~3 mask px (grid: ~140 cells across the container's smaller\n// dimension) — everything the visible canvas draws is that per-cell grid,\n// never the real glyphs, so caller content never actually appears; only its\n// shape does.\n//\n// The wipe cadence (900ms) is deliberately decoupled from and far slower\n// than the real xerographic engine (>20ppm, well under a second per page) —\n// this is the ONE followable event per round-9 legibility rule, and 900ms\n// top-to-bottom is slow enough that a viewer's eye can track the wipe\n// position at any instant.\n//\n// Colour is --border (unresolved shape whisper) and --foreground (toned\n// ink and the luminance-only wipe/fuse highlights) only, read via\n// getComputedStyle(document.documentElement) at mount and re-read on a\n// MutationObserver watching documentElement's class — no paint before that\n// first read. --ns-accent never appears; this is a passive loading state\n// with no interaction.\n// ---------------------------------------------------------------------------\n\nexport interface TonerFuseStreakProps {\n  /** Real content used only to derive the printed silhouette's shape — never\n   *  displayed. Omit to use the built-in heading/text/media placeholder. */\n  children?: ReactNode;\n  /** extra classes merged onto the rendered root element */\n  className?: string;\n  /** inline styles merged onto the root element */\n  style?: CSSProperties;\n}\n\ntype RGB = [number, number, number];\n\nfunction parseColor(raw: string): RGB | null {\n  const s = raw.trim();\n  if (s.startsWith(\"#\")) {\n    let hex = s.slice(1);\n    if (hex.length === 3) hex = hex.split(\"\").map((c) => c + c).join(\"\");\n    if (hex.length < 6) return null;\n    const r = parseInt(hex.slice(0, 2), 16);\n    const g = parseInt(hex.slice(2, 4), 16);\n    const b = parseInt(hex.slice(4, 6), 16);\n    return Number.isNaN(r + g + b) ? null : [r, g, b];\n  }\n  const m = s.match(/rgba?\\(\\s*([\\d.]+)[,\\s]+([\\d.]+)[,\\s]+([\\d.]+)/);\n  return m ? [Number(m[1]), Number(m[2]), Number(m[3])] : null;\n}\n\n// deterministic per-cell/per-region hash — never Math.random, so the streak\n// layout a caller sees is reproducible across a rebuild with the same shape\nfunction hash01(n: number): number {\n  const h = Math.sin(n * 12.9898 + 78.233) * 43758.5453;\n  return h - Math.floor(h);\n}\n\nconst CELLS_ACROSS = 140; // ~120-160 cells span the container's smaller dim\nconst SS = 3; // mask supersample factor: one toner cell per ~3 mask px\nconst WIPE_MS = 900;\nconst HOLD_MS = 1500;\nconst RESET_MS = 200;\nconst CYCLE_MS = WIPE_MS + HOLD_MS + RESET_MS; // 2600\nconst RAMP_IN_MS = 150; // toner visibly \"catching\" behind the wipe line\nconst EDGE_MS = 400; // edge-deletion under-tone window\nconst EDGE_FILL_MS = 150; // then eases up to full density\nconst STREAK_FADE_MS = 400;\nconst FUSE_DELAY_MS = 220; // after the wipe completes\nconst FUSE_SWEEP_MS = 300;\nconst FUSE_DECAY_MS = 180;\nconst FUSE_BRIGHTEN = 0.08;\nconst WIPE_BAND_BOOST = 0.06;\nconst EDGE_RADIUS = 2; // cells — \"gradient over <2 cells\" test window\nconst FILL_THRESH = 0.6; // a cell counts as solid fill for region-finding\nconst INK_THRESH = 0.15; // a cell counts as \"has ink\" at all\nconst STARVE_WIDTH_FRAC = 0.18; // of the smaller grid dimension\n// most-structured non-t0 frame: full density, both artifacts settled, well\n// clear of the transient fuse flash (which decays by ~1600ms)\nconst FREEZE_MS = 1700;\n\ninterface StreakBand {\n  x0: number;\n  x1: number;\n  y0: number;\n  y1: number;\n  reduction: number; // 0..1 opacity reduction at full fade-in\n}\n\ninterface Mask {\n  cols: number;\n  rows: number;\n  density: Float32Array; // 0..1 ink coverage per cell\n  isEdge: Uint8Array; // 1 where a mask edge (gradient over <2 cells) sits\n  bands: StreakBand[];\n}\n\nfunction deriveMask(density: Float32Array, cols: number, rows: number): Mask {\n  const isEdge = new Uint8Array(cols * rows);\n  for (let y = 0; y < rows; y++) {\n    for (let x = 0; x < cols; x++) {\n      const i = y * cols + x;\n      if (density[i] <= INK_THRESH) continue;\n      let edge = false;\n      for (let dy = -EDGE_RADIUS; dy <= EDGE_RADIUS && !edge; dy++) {\n        const ny = y + dy;\n        if (ny < 0 || ny >= rows) continue;\n        for (let dx = -EDGE_RADIUS; dx <= EDGE_RADIUS; dx++) {\n          const nx = x + dx;\n          if (nx < 0 || nx >= cols) continue;\n          if (density[ny * cols + nx] <= INK_THRESH) {\n            edge = true;\n            break;\n          }\n        }\n      }\n      if (edge) isEdge[i] = 1;\n    }\n  }\n\n  // flood-fill contiguous solid-fill regions (4-connectivity) to find\n  // toner-starvation candidates\n  const visited = new Uint8Array(cols * rows);\n  const bands: StreakBand[] = [];\n  const stackX = new Int32Array(cols * rows);\n  const stackY = new Int32Array(cols * rows);\n  for (let y = 0; y < rows; y++) {\n    for (let x = 0; x < cols; x++) {\n      const start = y * cols + x;\n      if (visited[start] || density[start] <= FILL_THRESH) continue;\n      let sp = 0;\n      stackX[sp] = x;\n      stackY[sp] = y;\n      sp++;\n      visited[start] = 1;\n      let minX = x, maxX = x, minY = y, maxY = y;\n      const seed = start;\n      while (sp > 0) {\n        sp--;\n        const cx = stackX[sp];\n        const cy = stackY[sp];\n        if (cx < minX) minX = cx;\n        if (cx > maxX) maxX = cx;\n        if (cy < minY) minY = cy;\n        if (cy > maxY) maxY = cy;\n        const neighbors: [number, number][] = [\n          [cx - 1, cy],\n          [cx + 1, cy],\n          [cx, cy - 1],\n          [cx, cy + 1],\n        ];\n        for (const [nx, ny] of neighbors) {\n          if (nx < 0 || nx >= cols || ny < 0 || ny >= rows) continue;\n          const ni = ny * cols + nx;\n          if (visited[ni] || density[ni] <= FILL_THRESH) continue;\n          visited[ni] = 1;\n          stackX[sp] = nx;\n          stackY[sp] = ny;\n          sp++;\n        }\n      }\n      const width = maxX - minX + 1;\n      const smaller = Math.min(cols, rows);\n      if (width >= STARVE_WIDTH_FRAC * smaller) {\n        const h = maxY - minY + 1;\n        const trailStart = minY + Math.round(0.7 * h);\n        const trailEnd = maxY;\n        const trailSpan = Math.max(1, trailEnd - trailStart + 1);\n        const r1 = hash01(seed * 1.7 + 3.1);\n        const r2 = hash01(seed * 2.3 + 9.7);\n        const r3 = hash01(seed * 3.9 + 1.3);\n        const r4 = hash01(seed * 5.1 + 6.6);\n        const bandHeight = Math.max(1, Math.min(trailSpan, Math.round(1 + r1 * (trailSpan - 1))));\n        const bandY0 = trailStart + Math.floor(r2 * Math.max(0, trailSpan - bandHeight));\n        const bandW = Math.max(1, Math.round(width * (0.5 + r3 * 0.4)));\n        const bandX0 = minX + Math.floor(r4 * Math.max(0, width - bandW));\n        bands.push({\n          x0: bandX0,\n          x1: Math.min(maxX, bandX0 + bandW - 1),\n          y0: bandY0,\n          y1: Math.min(maxY, bandY0 + bandHeight - 1),\n          reduction: 0.18 + hash01(seed * 7.7 + 2.2) * 0.12,\n        });\n      }\n    }\n  }\n\n  return { cols, rows, density, isEdge, bands };\n}\n\n// Built-in placeholder: a heading bar, three text lines, one wide media\n// block — the media block alone clears the 18%-of-smaller-dimension\n// starvation threshold, so the component never depends on caller content.\nfunction placeholderDensity(cols: number, rows: number): Float32Array {\n  const density = new Float32Array(cols * rows);\n  const fillRect = (x0f: number, y0f: number, x1f: number, y1f: number) => {\n    const x0 = Math.max(0, Math.floor(x0f * cols));\n    const x1 = Math.min(cols, Math.ceil(x1f * cols));\n    const y0 = Math.max(0, Math.floor(y0f * rows));\n    const y1 = Math.min(rows, Math.ceil(y1f * rows));\n    for (let y = y0; y < y1; y++) {\n      for (let x = x0; x < x1; x++) density[y * cols + x] = 1;\n    }\n  };\n  fillRect(0.06, 0.08, 0.52, 0.15); // heading\n  fillRect(0.06, 0.24, 0.9, 0.29); // text line 1\n  fillRect(0.06, 0.34, 0.82, 0.39); // text line 2\n  fillRect(0.06, 0.44, 0.66, 0.49); // text line 3 (short, paragraph end)\n  fillRect(0.06, 0.58, 0.86, 0.92); // wide media block — starvation source\n  return density;\n}\n\n// Rasterize `host`'s children into a cols x rows density grid. Text nodes\n// become thin bars from their exact per-line boxes (Range.getClientRects —\n// real wrap geometry, no layout simulation); leaf elements carrying a\n// visible fill (background, border, or media tag) become filled blocks.\nfunction domDensity(host: HTMLElement, hostRect: DOMRect, cols: number, rows: number): Float32Array | null {\n  const maskW = cols * SS;\n  const maskH = rows * SS;\n  if (maskW <= 0 || maskH <= 0) return null;\n  const off = document.createElement(\"canvas\");\n  off.width = maskW;\n  off.height = maskH;\n  const octx = off.getContext(\"2d\");\n  if (!octx) return null;\n  const scale = maskW / Math.max(1, hostRect.width);\n  octx.fillStyle = \"#000\";\n  let painted = false;\n\n  const paintRect = (r: DOMRect) => {\n    const x = (r.left - hostRect.left) * scale;\n    const y = (r.top - hostRect.top) * scale;\n    const w = r.width * scale;\n    const h = r.height * scale;\n    if (w <= 0 || h <= 0) return;\n    octx.fillRect(x, y, w, h);\n    painted = true;\n  };\n\n  const walker = document.createTreeWalker(host, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT);\n  let node: Node | null = walker.currentNode;\n  while (node) {\n    if (node.nodeType === Node.TEXT_NODE) {\n      const text = node.textContent ?? \"\";\n      if (text.trim().length > 0) {\n        const range = document.createRange();\n        range.selectNodeContents(node);\n        const rects = range.getClientRects();\n        for (let i = 0; i < rects.length; i++) {\n          const r = rects[i];\n          // a thin bar within the line box — cap-height-ish, trimmed short\n          // of full width the way a set line of prose rarely fills to the\n          // pixel — this is what donates the fine \"serif\" edge feature\n          const y = r.top + r.height * 0.15;\n          const h = r.height * 0.55;\n          const w = r.width * 0.92;\n          paintRect(new DOMRect(r.left, y, w, h));\n        }\n      }\n    } else if (node.nodeType === Node.ELEMENT_NODE) {\n      const el = node as Element;\n      if (el.children.length === 0) {\n        const tag = el.tagName;\n        const isMedia = tag === \"IMG\" || tag === \"SVG\" || tag === \"CANVAS\" || tag === \"VIDEO\";\n        let filled = isMedia;\n        if (!filled) {\n          const cs = getComputedStyle(el);\n          const bg = parseColorAlpha(cs.backgroundColor);\n          const borderW = parseFloat(cs.borderTopWidth) || 0;\n          filled = bg > 0.05 || borderW > 0;\n        }\n        if (filled) {\n          const r = el.getBoundingClientRect();\n          if (r.width > 0 && r.height > 0) paintRect(r);\n        }\n      }\n    }\n    node = walker.nextNode();\n  }\n\n  if (!painted) return null;\n\n  const img = octx.getImageData(0, 0, maskW, maskH).data;\n  const density = new Float32Array(cols * rows);\n  for (let cy = 0; cy < rows; cy++) {\n    for (let cx = 0; cx < cols; cx++) {\n      let sum = 0;\n      for (let sy = 0; sy < SS; sy++) {\n        const py = cy * SS + sy;\n        for (let sx = 0; sx < SS; sx++) {\n          const px = cx * SS + sx;\n          sum += img[(py * maskW + px) * 4 + 3]; // alpha channel = coverage\n        }\n      }\n      density[cy * cols + cx] = sum / (SS * SS * 255);\n    }\n  }\n  return density;\n}\n\nfunction parseColorAlpha(raw: string): number {\n  const m = raw.match(/rgba?\\([^)]+,\\s*([\\d.]+)\\)/);\n  if (m) return Number(m[1]);\n  if (/^rgb\\(/.test(raw)) return 1;\n  return raw === \"transparent\" ? 0 : 1;\n}\n\nexport function TonerFuseStreak({ children, className = \"\", style }: TonerFuseStreakProps) {\n  const rootRef = useRef<HTMLDivElement>(null);\n  const canvasRef = useRef<HTMLCanvasElement>(null);\n  const measureRef = useRef<HTMLDivElement>(null);\n  const rebuildRef = useRef<(() => void) | null>(null);\n\n  useEffect(() => {\n    const root = rootRef.current;\n    const canvas = canvasRef.current;\n    const measure = measureRef.current;\n    if (!root || !canvas) return;\n    const ctx = canvas.getContext(\"2d\");\n    if (!ctx) return;\n\n    let disposed = false;\n    const mq = window.matchMedia(\"(prefers-reduced-motion: reduce)\");\n    let reduced = mq.matches;\n\n    let fg: RGB | null = null;\n    let border: RGB | null = null;\n    let dark = true;\n    const derive = () => {\n      dark = document.documentElement.classList.contains(\"dark\");\n      const cs = getComputedStyle(document.documentElement);\n      fg = parseColor(cs.getPropertyValue(\"--foreground\")) ?? fg;\n      border = parseColor(cs.getPropertyValue(\"--border\")) ?? border;\n    };\n    derive();\n\n    let w = 0;\n    let h = 0;\n    let dpr = 1;\n    let cellSize = 8;\n    let cols = 0;\n    let rows = 0;\n    let mask: Mask | null = null;\n    let raf = 0;\n    let visible = true;\n    let startedAt = performance.now();\n\n    const buildMask = () => {\n      if (cols <= 0 || rows <= 0) {\n        mask = null;\n        return;\n      }\n      let density: Float32Array | null = null;\n      if (measure && measure.childElementCount > 0) {\n        const hostRect = root.getBoundingClientRect();\n        density = domDensity(measure, hostRect, cols, rows);\n      }\n      if (!density) density = placeholderDensity(cols, rows);\n      mask = deriveMask(density, cols, rows);\n    };\n\n    const draw = (nowMs: number) => {\n      if (!fg || !border || !mask || w <= 0 || h <= 0) return;\n      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n      ctx.clearRect(0, 0, w, h);\n\n      const elapsed = reduced ? FREEZE_MS : nowMs - startedAt;\n      const t = ((elapsed % CYCLE_MS) + CYCLE_MS) % CYCLE_MS;\n      const wipeProgress = Math.min(1, Math.max(0, t / WIPE_MS));\n      const wipeLineY = wipeProgress * h;\n      const inReset = t >= WIPE_MS + HOLD_MS;\n      const resetFrac = inReset ? (t - WIPE_MS - HOLD_MS) / RESET_MS : 0;\n\n      const fullAlpha = dark ? 0.95 : 0.85;\n      const { cols: c, rows: r, density, isEdge, bands } = mask;\n\n      const buckets: (Path2D | null)[] = new Array(101).fill(null);\n      const addCell = (alpha: number, cx: number, cy: number, size: number) => {\n        if (alpha <= 0.003) return;\n        const bi = Math.min(100, Math.max(1, Math.round(alpha * 100)));\n        let path = buckets[bi];\n        if (!path) {\n          path = new Path2D();\n          buckets[bi] = path;\n        }\n        path.rect(cx - size / 2, cy - size / 2, size, size);\n      };\n\n      const cellDot = cellSize * 0.84;\n\n      for (let cy = 0; cy < r; cy++) {\n        const rowTop = cy * cellSize;\n        const rowY = rowTop + cellSize / 2;\n        for (let cx = 0; cx < c; cx++) {\n          const d = density[cy * c + cx];\n          if (d <= 0.01) continue;\n          const colX = cx * cellSize + cellSize / 2;\n          const passed = rowTop < wipeLineY;\n          const sincePass = passed ? wipeLineY - rowTop : -1;\n\n          let alpha: number;\n          if (!passed) {\n            // un-toned: a whisper of the shape in --border, not invisible\n            alpha = 0.04 * d;\n            ctx.save();\n            ctx.fillStyle = `rgba(${border[0]},${border[1]},${border[2]},${alpha})`;\n            ctx.fillRect(colX - cellDot / 2, rowY - cellDot / 2, cellDot, cellDot);\n            ctx.restore();\n            continue;\n          }\n\n          // wipe passed this row's cadence-position ago: toner catches in,\n          // then edge-deletion under-tones thin features, then a\n          // toner-starvation streak (if any) fades its dip in and holds\n          const rampIn = Math.min(1, sincePass / RAMP_IN_MS);\n          let cellAlpha = d * fullAlpha * rampIn;\n\n          const i = cy * c + cx;\n          if (isEdge[i]) {\n            const under = 0.15 + hash01(i * 4.13 + 0.7) * 0.1; // 15-25%\n            let edgeFactor: number;\n            if (sincePass < EDGE_MS) edgeFactor = 1 - under;\n            else {\n              const easeT = Math.min(1, (sincePass - EDGE_MS) / EDGE_FILL_MS);\n              edgeFactor = 1 - under * (1 - easeT);\n            }\n            cellAlpha *= edgeFactor;\n          }\n\n          for (let b = 0; b < bands.length; b++) {\n            const band = bands[b];\n            if (cx >= band.x0 && cx <= band.x1 && cy >= band.y0 && cy <= band.y1) {\n              const fadeT = Math.min(1, sincePass / STREAK_FADE_MS);\n              cellAlpha *= 1 - band.reduction * fadeT;\n              break;\n            }\n          }\n\n          // reset phase: fade this cycle's toned frame back to the un-toned\n          // baseline before the next cycle's wipe begins at t=0\n          if (inReset) {\n            const baseline = 0.04 * d;\n            cellAlpha = cellAlpha + (baseline - cellAlpha) * resetFrac;\n          }\n\n          addCell(cellAlpha, colX, rowY, cellDot);\n        }\n      }\n\n      ctx.fillStyle = `rgb(${fg[0]},${fg[1]},${fg[2]})`;\n      for (let bi = 1; bi <= 100; bi++) {\n        const path = buckets[bi];\n        if (!path) continue;\n        ctx.globalAlpha = bi / 100;\n        ctx.fill(path);\n      }\n      ctx.globalAlpha = 1;\n\n      // the ONE followable event: a soft band riding the wipe line, ~6%\n      // luminance over its surroundings, never --ns-accent\n      if (!inReset && t < WIPE_MS) {\n        const bandH = Math.max(cellSize * 2.5, h * 0.03);\n        const grad = ctx.createLinearGradient(0, wipeLineY - bandH, 0, wipeLineY + bandH);\n        grad.addColorStop(0, `rgba(${fg[0]},${fg[1]},${fg[2]},0)`);\n        grad.addColorStop(0.5, `rgba(${fg[0]},${fg[1]},${fg[2]},${WIPE_BAND_BOOST})`);\n        grad.addColorStop(1, `rgba(${fg[0]},${fg[1]},${fg[2]},0)`);\n        ctx.fillStyle = grad;\n        ctx.fillRect(0, wipeLineY - bandH, w, bandH * 2);\n      }\n\n      // fuse flash: one soft pass at the nip roller's implied contact line,\n      // FUSE_DELAY_MS after the wipe completes, decaying over FUSE_DECAY_MS\n      const fuseStart = WIPE_MS + FUSE_DELAY_MS;\n      const sinceFuse = t - fuseStart;\n      if (sinceFuse >= 0 && sinceFuse < FUSE_SWEEP_MS + FUSE_DECAY_MS) {\n        const nipY = Math.min(1, sinceFuse / FUSE_SWEEP_MS) * h;\n        const decayT = Math.max(0, sinceFuse - FUSE_SWEEP_MS) / FUSE_DECAY_MS;\n        const intensity = FUSE_BRIGHTEN * (1 - Math.min(1, decayT));\n        if (intensity > 0.002) {\n          const bandH = Math.max(cellSize * 4, h * 0.05);\n          const grad = ctx.createLinearGradient(0, nipY - bandH, 0, nipY + bandH);\n          grad.addColorStop(0, `rgba(${fg[0]},${fg[1]},${fg[2]},0)`);\n          grad.addColorStop(0.5, `rgba(${fg[0]},${fg[1]},${fg[2]},${intensity})`);\n          grad.addColorStop(1, `rgba(${fg[0]},${fg[1]},${fg[2]},0)`);\n          ctx.fillStyle = grad;\n          ctx.fillRect(0, Math.max(0, nipY - bandH), w, bandH * 2);\n        }\n      }\n    };\n\n    const loop = (now: number) => {\n      draw(now);\n      if (!reduced && visible) raf = requestAnimationFrame(loop);\n      else raf = 0;\n    };\n    const wake = () => {\n      if (raf === 0 && !reduced && visible) raf = requestAnimationFrame(loop);\n    };\n\n    const resize = () => {\n      const rect = root.getBoundingClientRect();\n      w = rect.width;\n      h = rect.height;\n      if (measure) measure.style.width = `${w}px`;\n      dpr = Math.min(2, window.devicePixelRatio || 1);\n      canvas.width = Math.max(1, Math.round(w * dpr));\n      canvas.height = Math.max(1, Math.round(h * dpr));\n      const smaller = Math.min(w, h);\n      cellSize = smaller > 0 ? smaller / CELLS_ACROSS : 8;\n      cols = cellSize > 0 ? Math.max(1, Math.round(w / cellSize)) : 0;\n      rows = cellSize > 0 ? Math.max(1, Math.round(h / cellSize)) : 0;\n      buildMask();\n      draw(performance.now());\n    };\n    rebuildRef.current = () => {\n      buildMask();\n      draw(performance.now());\n    };\n\n    resize();\n    if (!reduced) wake();\n\n    const ro = new ResizeObserver(resize);\n    ro.observe(root);\n\n    const mo = new MutationObserver(() => {\n      derive();\n      draw(performance.now());\n    });\n    mo.observe(document.documentElement, { attributes: true, attributeFilter: [\"class\"] });\n\n    const onReducedChange = () => {\n      reduced = mq.matches;\n      if (reduced) {\n        cancelAnimationFrame(raf);\n        raf = 0;\n        draw(performance.now());\n      } else {\n        startedAt = performance.now() - ((performance.now() - startedAt) % CYCLE_MS);\n        wake();\n      }\n    };\n    mq.addEventListener(\"change\", onReducedChange);\n\n    const onVisibility = () => {\n      visible = document.visibilityState === \"visible\";\n      if (visible) wake();\n      else {\n        cancelAnimationFrame(raf);\n        raf = 0;\n      }\n    };\n    document.addEventListener(\"visibilitychange\", onVisibility);\n\n    const io = new IntersectionObserver((entries) => {\n      visible = entries[0]?.isIntersecting ?? true;\n      if (visible) wake();\n      else {\n        cancelAnimationFrame(raf);\n        raf = 0;\n      }\n    });\n    io.observe(root);\n\n    document.fonts.ready.then(() => {\n      if (!disposed) resize();\n    });\n\n    return () => {\n      disposed = true;\n      cancelAnimationFrame(raf);\n      ro.disconnect();\n      mo.disconnect();\n      io.disconnect();\n      mq.removeEventListener(\"change\", onReducedChange);\n      document.removeEventListener(\"visibilitychange\", onVisibility);\n      rebuildRef.current = null;\n    };\n  }, []);\n\n  // children changed: rebuild the silhouette once layout settles\n  useEffect(() => {\n    let raf1 = 0;\n    let raf2 = 0;\n    raf1 = requestAnimationFrame(() => {\n      raf2 = requestAnimationFrame(() => rebuildRef.current?.());\n    });\n    return () => {\n      cancelAnimationFrame(raf1);\n      cancelAnimationFrame(raf2);\n    };\n  }, [children]);\n\n  return (\n    <div\n      ref={rootRef}\n      role=\"img\"\n      aria-label=\"Loading\"\n      className={`relative w-full overflow-hidden bg-background ${className}`}\n      style={{ minHeight: 160, ...style }}\n    >\n      <canvas ref={canvasRef} aria-hidden=\"true\" className=\"block h-full w-full\" />\n      {children ? (\n        <div\n          ref={measureRef}\n          aria-hidden=\"true\"\n          style={{ position: \"absolute\", top: 0, left: 0, visibility: \"hidden\", pointerEvents: \"none\" }}\n        >\n          {children}\n        </div>\n      ) : null}\n    </div>\n  );\n}\n\nTonerFuseStreak.displayName = \"TonerFuseStreak\";\n\nexport default TonerFuseStreak;\n",
      "type": "registry:ui",
      "target": "components/ui/toner-fuse-streak.tsx"
    }
  ],
  "cssVars": {
    "theme": {
      "color-ns-accent": "var(--ns-accent)"
    },
    "light": {
      "ns-accent": "#006bff"
    }
  },
  "meta": {
    "collection": "core",
    "tags": [
      "loader",
      "progress",
      "print",
      "xerographic",
      "canvas",
      "ambient",
      "monochrome",
      "skeleton"
    ],
    "instruction": "A card-scale canvas-2D loading surface, `w-full h-full` inside a relatively-positioned root, sourced from the standard xerographic imaging cycle (charge/expose/develop/transfer/fuse) and its two documented failure artifacts (edge deletion at small-feature boundaries, toner starvation on large solid fills). Optional `children` are mounted off-screen (`position:absolute; visibility:hidden; pointer-events:none`) purely to donate a silhouette — they are never displayed. On mount and on every debounced ResizeObserver callback, the root is measured; if children are present, a `document.createTreeWalker` pass over the hidden container rasterizes them into an offscreen canvas: every non-whitespace text node gets a `Range.selectNodeContents` + `getClientRects()` pass (exact per-line boxes, no manual wrap simulation) and each line box is painted as a thin bar (trimmed to ~92% width, ~55% of line height, offset 15% down) — this is what donates the fine 'serif' edge feature; every childless leaf element whose tag is IMG/SVG/CANVAS/VIDEO or whose computed background-color alpha exceeds 0.05 or border-width is nonzero is painted as a solid filled rect at its `getBoundingClientRect()` — this is what donates large fill regions. If no children are passed, or the DOM pass paints nothing, a built-in placeholder (a heading bar, three text lines with a short last line, and one media block spanning 80% of the width) is used instead and is on its own sufficient to trigger both artifacts, so the component ships as `core` without requiring a caller snapshot. The raster is done at one toner cell per ~3px of mask (a `cols*3 x rows*3` offscreen canvas, alpha channel read as coverage), then downsampled by block-averaging into a `cols x rows` density grid where `cols`/`rows` are sized so ~140 cells span the container's SMALLER dimension (`cellSize = min(w,h)/140`). A cell is flagged an 'edge' cell if any neighbour within Chebyshev distance 2 has density below the ink threshold (0.15) — this is the literal reading of 'gradient over <2 cells'. Toner-starvation regions are found by 4-connectivity flood-fill over cells at or above a solid-fill threshold (0.6); any region whose bounding width is at least 18% of the grid's smaller dimension gets one streak band, deterministically hashed (never Math.random) from its seed cell index — a rectangle 8-14px tall (converted to cells) positioned in the trailing 30% of the region's height (the wipe travels top-to-bottom, so 'trailing' is the bottom of the region) spanning 50-90% of the region's width at a hashed x-offset, with a hashed 18-30% opacity reduction. Playback runs on a fixed 2600ms unbounded loop with no external clock dependency: 900ms expose/develop wipe (a horizontal line sweeping top to bottom, `wipeLineY = progress * height`), 1500ms hold at full density, 200ms reset fading every toned cell back to the un-toned baseline before the next cycle's wipe restarts at t=0 (never a hard cut). Cells whose row the wipe has not yet reached render only a 4%-opacity `--border`-derived fill (`alpha = 0.04 * density`) — the un-toned whisper of the shape, never fully invisible. Once the wipe passes a row, that row's cells ramp from 0 to full density over 150ms (toner 'catching' behind the line, the legibility anchor a viewer's eye follows); edge-flagged cells render 15-25% under-toned (hashed per-cell within that range) for 400ms after the wipe passes them, then ease to full density over a further 150ms; cells inside a streak band have their density multiplied by `1 - reduction * min(1, sincePass/400)`, so the dip fades IN over 400ms and then holds steady (a persistent artifact, not a transient) — reset-phase fading is applied after both. 220ms after the wipe completes, a single soft highlight — the fuse-roller 'nip' contact line — sweeps once top to bottom over 300ms and decays over a further 180ms, adding at most +8% luminance (never touching --ns-accent). The wipe line itself is a soft ~6%-luminance band riding `wipeLineY` while `t < 900ms`. All ink is `--foreground` (toned density, wipe band, fuse flash) or `--border` (un-toned baseline) read via `getComputedStyle(document.documentElement)` at mount and re-read on a `MutationObserver` watching `documentElement`'s class; no paint happens before that first successful read. Full toned density reads at 95% `--foreground` alpha in dark theme and 85% in light theme (checked first — light theme needs headroom under the streak's opacity reduction so the dip stays a visible relative dip rather than clipping toward indistinguishable near-white). Cells render as small squares at 84% of cell pitch, bucketed into ~100 discrete alpha levels each accumulated into a shared `Path2D` so a frame costs at most 100 `fill()` calls rather than one per cell. Backing store is DPR-capped at 2, sized off the root's own `getBoundingClientRect` on `ResizeObserver`, which also triggers a full mask rebuild (children layout can reflow with the container). The rAF loop pauses on `document.visibilitychange` and via `IntersectionObserver` when scrolled offscreen, and stops entirely under `prefers-reduced-motion: reduce`, which freezes the clock at 1700ms into the cycle — full density, both artifacts visible and settled, safely clear of the transient fuse flash — rather than the blank t0 frame. Purely decorative and non-interactive: the root is `role=\"img\" aria-label=\"Loading\"`, the canvas is `aria-hidden`, and it must never respond to hover/press beyond default focus-visible chrome should it happen to wrap a control. No dependencies."
  },
  "type": "registry:ui"
}