{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "boot-log",
  "title": "Boot Log",
  "description": "A self test that prints itself line by line, at its final height from the first frame.",
  "registryDependencies": [
    "@afterglow/caret"
  ],
  "files": [
    {
      "path": "registry/terminal/components/boot-log.tsx",
      "content": "\"use client\";\n\nimport { useEffect, useState } from \"react\";\n\nimport { cn } from \"@/lib/utils\";\nimport { Caret } from \"@/registry/terminal/components/caret\";\n\ntype BootTone = \"default\" | \"dim\" | \"ok\" | \"warn\" | \"error\";\n\ntype BootLine = string | { text: string; tone?: BootTone };\n\nconst TONE_CLASS: Record<BootTone, string> = {\n  default: \"text-phosphor-bright\",\n  dim: \"text-phosphor-dim\",\n  error: \"text-signal\",\n  ok: \"text-phosphor\",\n  warn: \"text-warning\",\n};\n\nfunction toLine(line: BootLine): { text: string; tone: BootTone } {\n  return typeof line === \"string\"\n    ? { text: line, tone: \"default\" }\n    : { text: line.text, tone: line.tone ?? \"default\" };\n}\n\nfunction BootLog({\n  className,\n  lines,\n  interval = 240,\n  prefix = \">\",\n  onComplete,\n  ...props\n}: Omit<React.ComponentProps<\"ol\">, \"children\"> & {\n  lines: BootLine[];\n  interval?: number;\n  prefix?: string;\n  onComplete?: () => void;\n}) {\n  const [printed, setPrinted] = useState(0);\n  const total = lines.length;\n\n  useEffect(() => {\n    if (printed >= total) {\n      onComplete?.();\n      return;\n    }\n\n    const reduced = window.matchMedia(\"(prefers-reduced-motion: reduce)\");\n    if (reduced.matches) {\n      setPrinted(total);\n      return;\n    }\n\n    const timer = window.setTimeout(() => setPrinted((n) => n + 1), interval);\n    return () => window.clearTimeout(timer);\n  }, [printed, total, interval, onComplete]);\n\n  return (\n    <ol\n      className={cn(\n        \"m-0 grid list-none gap-1 p-0 font-mono text-xs\",\n        className\n      )}\n      data-slot=\"boot-log\"\n      {...props}\n    >\n      {lines.map(toLine).map((line, index) => {\n        const shown = index < printed;\n\n        return (\n          <li\n            aria-hidden={shown ? undefined : \"true\"}\n            className={cn(\n              \"flex gap-2 leading-relaxed\",\n              TONE_CLASS[line.tone],\n              shown ? \"animate-type\" : \"invisible\"\n            )}\n            key={line.text}\n          >\n            <span className=\"shrink-0 text-phosphor-dim\">{prefix}</span>\n            <span className=\"min-w-0\">{line.text}</span>\n            {shown && index === printed - 1 && printed < total && (\n              <Caret className=\"self-center\" />\n            )}\n          </li>\n        );\n      })}\n    </ol>\n  );\n}\n\nexport type { BootLine, BootTone };\nexport { BootLog };\n",
      "type": "registry:component"
    }
  ],
  "type": "registry:component"
}