{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "elements-read-aloud",
  "type": "registry:component",
  "title": "Read aloud",
  "description": "An answer played back, the spoken word lit as it goes, speed under your thumb.",
  "registryDependencies": [
    "https://r.assistant-ui.com/elements-surfaces.json",
    "https://r.assistant-ui.com/elements-range.json"
  ],
  "dependencies": [
    "lucide-react"
  ],
  "files": [
    {
      "type": "registry:component",
      "path": "components/assistant-ui/elements/read-aloud.tsx",
      "content": "\"use client\";\n\nimport type { ComponentProps } from \"react\";\nimport { PauseIcon, PlayIcon, Volume2Icon } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\nimport { field, ghostButton, mono, paper } from \"./surfaces\";\nimport { pct } from \"../utils/range\";\n\nexport function ReadAloud({\n  words,\n  spokenIndex,\n  playing,\n  rate,\n  elapsed,\n  duration,\n  onToggle,\n  onRateChange,\n  className,\n  ...props\n}: Omit<\n  ComponentProps<\"div\">,\n  | \"children\"\n  | \"words\"\n  | \"spokenIndex\"\n  | \"playing\"\n  | \"rate\"\n  | \"elapsed\"\n  | \"duration\"\n  | \"onToggle\"\n  | \"onRateChange\"\n> & {\n  words: readonly string[];\n  spokenIndex: number;\n  playing: boolean;\n  rate: number;\n  elapsed: string;\n  duration: string;\n  onToggle?: () => void;\n  onRateChange?: () => void;\n}) {\n  const progress = pct(spokenIndex, words.length);\n\n  return (\n    <div\n      data-slot=\"read-aloud\"\n      className={cn(\n        paper,\n        \"flex w-full max-w-sm flex-col gap-3 rounded-2xl p-3.5\",\n        className,\n      )}\n\n      {...props}\n    >\n      <p className=\"text-[13.5px] leading-relaxed\">\n        {words.map((word, i) => (\n          <span\n            key={`${i}-${word}`}\n            className={cn(\n              \"transition-colors duration-200 motion-reduce:transition-none\",\n              i < spokenIndex\n                ? \"text-foreground/40\"\n                : i === spokenIndex\n                  ? \"text-foreground/95 rounded bg-blue-500/12 dark:bg-blue-400/15\"\n                  : \"text-foreground/70\",\n            )}\n          >\n            {word}{\" \"}\n          </span>\n        ))}\n      </p>\n\n      <div className=\"flex items-center gap-2.5\">\n        <button\n          type=\"button\"\n          aria-label={playing ? \"Pause\" : \"Play\"}\n          onClick={onToggle}\n          className={cn(ghostButton, \"bg-foreground/[0.06] size-8 shrink-0\")}\n        >\n          {playing ? (\n            <PauseIcon className=\"size-3.5\" />\n          ) : (\n            <PlayIcon className=\"size-3.5 translate-x-px\" />\n          )}\n        </button>\n\n        <span className=\"bg-foreground/[0.08] h-[3px] min-w-0 flex-1 overflow-hidden rounded-full\">\n          <span\n            className=\"block h-full rounded-full bg-blue-500 transition-[width] duration-200 ease-linear motion-reduce:transition-none dark:bg-blue-400\"\n            style={{ width: `${progress}%` }}\n          />\n        </span>\n\n        <span className={cn(mono, \"text-foreground/35 shrink-0 tabular-nums\")}>\n          {elapsed} / {duration}\n        </span>\n\n        <button\n          type=\"button\"\n          aria-label={`Playback speed, currently ${rate} times`}\n          onClick={onRateChange}\n          className={cn(\n            field,\n            mono,\n            \"text-foreground/55 hover:text-foreground/90 shrink-0 rounded-full px-2 py-1 tabular-nums transition-colors\",\n          )}\n        >\n          {rate}×\n        </button>\n\n        <Volume2Icon className=\"text-foreground/25 size-3.5 shrink-0\" />\n      </div>\n    </div>\n  );\n}\n"
    }
  ]
}