{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "ai-transcription",
  "title": "AI: Transcription",
  "description": "A composable transcript with timestamped segments and active playback highlighting.",
  "registryDependencies": [
    "@nx-ui/utils",
    "@nx-ui/ai-use-controllable-state"
  ],
  "files": [
    {
      "path": "components/ai/transcription.tsx",
      "content": "// Ported from vercel/ai-elements@6a9d5b1822ffb10bba4bd97175f01edd7d8651cd.\n\"use client\";\n\nimport { useControllableState } from \"./use-controllable-state\";\nimport { cn } from \"@/lib/utils\";\ntype TranscriptionResult = { segments: { text: string; startSecond: number; endSecond: number }[] };\nimport type { ComponentProps, ReactNode } from \"react\";\nimport { createContext, useCallback, useContext, useMemo } from \"react\";\n\ntype TranscriptionSegment = TranscriptionResult[\"segments\"][number];\n\ninterface TranscriptionContextValue {\n  segments: TranscriptionSegment[];\n  currentTime: number;\n  onTimeUpdate: (time: number) => void;\n  onSeek?: (time: number) => void;\n}\n\nconst TranscriptionContext = createContext<TranscriptionContextValue | null>(\n  null\n);\n\nconst useTranscription = () => {\n  const context = useContext(TranscriptionContext);\n  if (!context) {\n    throw new Error(\n      \"Transcription components must be used within Transcription\"\n    );\n  }\n  return context;\n};\n\nexport type TranscriptionProps = Omit<ComponentProps<\"div\">, \"children\"> & {\n  segments: TranscriptionSegment[];\n  currentTime?: number;\n  onSeek?: (time: number) => void;\n  children: (segment: TranscriptionSegment, index: number) => ReactNode;\n};\n\nexport const Transcription = ({\n  segments,\n  currentTime: externalCurrentTime,\n  onSeek,\n  className,\n  children,\n  ...props\n}: TranscriptionProps) => {\n  const [currentTime, setCurrentTime] = useControllableState({\n    defaultProp: 0,\n    onChange: onSeek,\n    prop: externalCurrentTime,\n  });\n\n  const contextValue = useMemo(\n    () => ({ currentTime: currentTime ?? 0, onSeek, onTimeUpdate: setCurrentTime, segments }),\n    [currentTime, onSeek, setCurrentTime, segments]\n  );\n\n  return (\n    <TranscriptionContext.Provider value={contextValue}>\n      <div\n        className={cn(\n          \"flex flex-wrap gap-1 text-sm leading-relaxed\",\n          className\n        )}\n        data-slot=\"transcription\"\n        {...props}\n      >\n        {segments\n          .filter((segment) => segment.text.trim())\n          .map((segment, index) => children(segment, index))}\n      </div>\n    </TranscriptionContext.Provider>\n  );\n};\n\nexport type TranscriptionSegmentProps = ComponentProps<\"button\"> & {\n  segment: TranscriptionSegment;\n  index: number;\n};\n\nexport const TranscriptionSegment = ({\n  segment,\n  index,\n  className,\n  onClick,\n  ...props\n}: TranscriptionSegmentProps) => {\n  const { currentTime, onSeek } = useTranscription();\n\n  const isActive =\n    currentTime >= segment.startSecond && currentTime < segment.endSecond;\n  const isPast = currentTime >= segment.endSecond;\n\n  const handleClick = useCallback(\n    (event: React.MouseEvent<HTMLButtonElement>) => {\n      if (onSeek) {\n        onSeek(segment.startSecond);\n      }\n      onClick?.(event);\n    },\n    [onSeek, segment.startSecond, onClick]\n  );\n\n  return (\n    <button\n      className={cn(\n        \"inline text-left\",\n        isActive && \"text-primary\",\n        isPast && \"text-muted-foreground\",\n        !(isActive || isPast) && \"text-muted-foreground/60\",\n        onSeek && \"cursor-pointer hover:text-foreground\",\n        !onSeek && \"cursor-default\",\n        className\n      )}\n      data-active={isActive}\n      data-index={index}\n      data-slot=\"transcription-segment\"\n      onClick={handleClick}\n      type=\"button\"\n      {...props}\n    >\n      {segment.text}\n    </button>\n  );\n};\n",
      "type": "registry:ui",
      "target": "components/ai/transcription.tsx"
    }
  ],
  "type": "registry:ui"
}