{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "claude-code-status",
  "title": "Claude Code Status",
  "description": "The orange spinner line while Claude works and the dim \"✻ Worked for 19s\" summary after a turn.",
  "dependencies": [
    "cn"
  ],
  "registryDependencies": [
    "card",
    "collapsible"
  ],
  "files": [
    {
      "path": "registry/notra/claude-code/components/claude-code-status.tsx",
      "content": "\"use client\";\n\nimport { cn } from \"cn\";\nimport { useEffect, useState } from \"react\";\n\nimport {\n  CLAUDE_CODE_RESULT_GLYPH,\n  CLAUDE_CODE_SPINNER_FRAMES,\n  CLAUDE_CODE_SPINNER_INTERVAL_MS,\n} from \"../constants/claude-code\";\nimport { renderClaudeCodeInline } from \"../lib/claude-code-inline\";\nimport type {\n  ClaudeCodeInterruptedProps,\n  ClaudeCodeSpinnerProps,\n  ClaudeCodeTurnSummaryProps,\n} from \"../types/claude-code\";\n\nconst FRAME_CYCLE = [\n  ...CLAUDE_CODE_SPINNER_FRAMES,\n  ...CLAUDE_CODE_SPINNER_FRAMES.slice(1, -1).reverse(),\n];\n\nconst REDUCED_MOTION_QUERY = \"(prefers-reduced-motion: reduce)\";\n\nconst useSpinnerFrame = (paused: boolean) => {\n  const [frame, setFrame] = useState(0);\n\n  useEffect(() => {\n    if (paused || window.matchMedia(REDUCED_MOTION_QUERY).matches) {\n      return;\n    }\n    const id = window.setInterval(() => {\n      setFrame((current) => (current + 1) % FRAME_CYCLE.length);\n    }, CLAUDE_CODE_SPINNER_INTERVAL_MS);\n    return () => window.clearInterval(id);\n  }, [paused]);\n\n  return FRAME_CYCLE[frame];\n};\n\nconst formatTokens = (tokens: number) =>\n  `↓ ${tokens.toLocaleString(\"en-US\")} tokens`;\n\nexport const ClaudeCodeSpinner = ({\n  className,\n  details = [],\n  elapsed,\n  paused = false,\n  tip,\n  tokens,\n  verb = \"Thinking\",\n  ...props\n}: ClaudeCodeSpinnerProps) => {\n  const glyph = useSpinnerFrame(paused);\n  const meta = [\n    elapsed,\n    tokens === undefined ? undefined : formatTokens(tokens),\n    ...details,\n  ].filter(Boolean);\n\n  return (\n    <div\n      className={cn(\n        \"font-claude-code text-claude-code-muted flex min-w-0 flex-col text-[0.8125rem] leading-5\",\n        className\n      )}\n      data-slot=\"claude-code-spinner\"\n      role=\"status\"\n      {...props}\n    >\n      <p className=\"grid grid-cols-[2ch_minmax(0,1fr)]\">\n        <span aria-hidden=\"true\" className=\"text-claude-code-accent\">\n          {glyph}\n        </span>\n        <span className=\"min-w-0\">\n          <span className=\"text-claude-code-accent\">{verb}…</span>\n          {meta.length > 0 && <> ({meta.join(\" · \")})</>}\n        </span>\n      </p>\n      {tip && (\n        <p className=\"grid grid-cols-[3ch_minmax(0,1fr)] pl-[2ch]\">\n          <span aria-hidden=\"true\">{CLAUDE_CODE_RESULT_GLYPH}</span>\n          <span className=\"min-w-0\">{renderClaudeCodeInline(tip)}</span>\n        </p>\n      )}\n    </div>\n  );\n};\n\nexport const ClaudeCodeTurnSummary = ({\n  className,\n  doneAt,\n  duration,\n  verb = \"Worked\",\n  ...props\n}: ClaudeCodeTurnSummaryProps) => (\n  <p\n    className={cn(\n      \"font-claude-code text-claude-code-muted grid min-w-0 grid-cols-[2ch_minmax(0,1fr)] text-[0.8125rem] leading-5\",\n      className\n    )}\n    data-slot=\"claude-code-turn-summary\"\n    {...props}\n  >\n    <span aria-hidden=\"true\">✻</span>\n    <span className=\"min-w-0\">\n      {verb} for {duration}\n      {doneAt && <> · done {doneAt}</>}\n    </span>\n  </p>\n);\n\nexport const ClaudeCodeInterrupted = ({\n  className,\n  hint = \"What should Claude do instead?\",\n  ...props\n}: ClaudeCodeInterruptedProps) => (\n  <p\n    className={cn(\n      \"font-claude-code text-claude-code-muted grid min-w-0 grid-cols-[3ch_minmax(0,1fr)] pl-[2ch] text-[0.8125rem] leading-5\",\n      className\n    )}\n    data-slot=\"claude-code-interrupted\"\n    role=\"status\"\n    {...props}\n  >\n    <span aria-hidden=\"true\">{CLAUDE_CODE_RESULT_GLYPH}</span>\n    <span className=\"min-w-0\">\n      <span className=\"text-claude-code-error\">Interrupted</span>\n      {hint && <> · {hint}</>}\n    </span>\n  </p>\n);\n",
      "type": "registry:component",
      "target": "components/claude-code/components/claude-code-status.tsx"
    },
    {
      "path": "registry/notra/claude-code/types/claude-code.ts",
      "content": "import type {\n  ChangeEventHandler,\n  ComponentProps,\n  KeyboardEventHandler,\n  ReactNode,\n  Ref,\n} from \"react\";\n\nimport type { Card } from \"@/components/ui/card\";\nimport type { Collapsible } from \"@/components/ui/collapsible\";\n\nexport type ClaudeCodeMode =\n  | \"auto\"\n  | \"manual\"\n  | \"accept-edits\"\n  | \"plan\"\n  | \"bypass\";\n\nexport type ClaudeCodeEffort =\n  | \"low\"\n  | \"medium\"\n  | \"high\"\n  | \"xhigh\"\n  | \"max\"\n  | \"ultracode\";\n\nexport type ClaudeCodeTodoStatus = \"done\" | \"active\" | \"todo\";\n\nexport type ClaudeCodeToolCallStatus = \"success\" | \"error\" | \"pending\";\n\nexport interface ClaudeCodeModeConfig {\n  className: string;\n  /** Whether the status line shows the shift+tab hint. */\n  cycles: boolean;\n  label: string;\n}\n\nexport interface ClaudeCodeEffortConfig {\n  className: string;\n  glyph: string;\n}\n\nexport interface ClaudeCodeTodoConfig {\n  glyph: string;\n  labelClassName: string;\n  srLabel: string;\n}\n\nexport interface ClaudeCodeToolCallConfig {\n  className: string;\n  srLabel: string;\n}\n\nexport interface ClaudeCodeTodo {\n  label: string;\n  status: ClaudeCodeTodoStatus;\n}\n\nexport interface ClaudeCodePullRequest {\n  href?: string;\n  number: number;\n}\n\nexport interface ClaudeCodeTerminalProps extends Omit<\n  ComponentProps<typeof Card>,\n  \"title\"\n> {\n  contentClassName?: string;\n  /** Pinned under the scrolling transcript, like Claude Code's prompt. */\n  footer?: ReactNode;\n  title?: ReactNode;\n}\n\nexport interface ClaudeCodeLogoProps extends ComponentProps<\"svg\"> {\n  size?: number;\n}\n\nexport interface ClaudeCodeHeaderProps extends ComponentProps<\"div\"> {\n  cwd?: string;\n  model?: string;\n  org?: string;\n  /** Dim `⎿` lines under the notices, like hook output or tips. */\n  tips?: string[];\n  version?: string;\n  /** Notice lines printed under the header. Supports `code` and **bold**. */\n  whatsNew?: string[];\n}\n\nexport interface ClaudeCodeMessageProps extends ComponentProps<\"div\"> {\n  from?: \"user\" | \"assistant\";\n}\n\nexport interface ClaudeCodeThinkingProps extends Omit<\n  ComponentProps<typeof Collapsible>,\n  \"children\"\n> {\n  children?: ReactNode;\n  label?: ReactNode;\n}\n\nexport interface ClaudeCodeSpinnerProps extends ComponentProps<\"div\"> {\n  /** Extra fields in the parentheses, like `thinking`. */\n  details?: string[];\n  elapsed?: string;\n  /** Freeze the glyph, for screenshots and reduced motion. */\n  paused?: boolean;\n  tip?: string;\n  tokens?: number;\n  verb?: string;\n}\n\nexport interface ClaudeCodeTurnSummaryProps extends ComponentProps<\"div\"> {\n  doneAt?: string;\n  duration: string;\n  verb?: string;\n}\n\nexport interface ClaudeCodeInterruptedProps extends ComponentProps<\"p\"> {\n  /** Shown after `Interrupted ·`. */\n  hint?: ReactNode;\n}\n\nexport interface ClaudeCodeTodoListProps extends ComponentProps<\"div\"> {\n  /** The tool line above the list. Pass `null` to leave it out. */\n  heading?: string | null;\n  todos: ClaudeCodeTodo[];\n}\n\nexport interface ClaudeCodeToolCallProps extends Omit<\n  ComponentProps<typeof Collapsible>,\n  \"children\"\n> {\n  arg?: string;\n  children?: ReactNode;\n  result: ReactNode;\n  status?: ClaudeCodeToolCallStatus;\n  tool: string;\n}\n\nexport interface ClaudeCodeToolSummaryProps extends Omit<\n  ComponentProps<typeof Collapsible>,\n  \"children\"\n> {\n  children?: ReactNode;\n  count: number;\n  noun?: string;\n  verb?: string;\n}\n\nexport interface ClaudeCodePromptProps extends Omit<\n  ComponentProps<\"form\">,\n  \"defaultValue\" | \"onChange\" | \"onKeyDown\" | \"onSubmit\"\n> {\n  /** A turn is running: Enter does not send and Escape calls `onStop`. */\n  busy?: boolean;\n  defaultValue?: string;\n  effort?: ClaudeCodeEffort | false;\n  inputClassName?: string;\n  inputRef?: Ref<HTMLInputElement>;\n  mode?: ClaudeCodeMode;\n  onChange?: ChangeEventHandler<HTMLInputElement>;\n  onKeyDown?: KeyboardEventHandler<HTMLInputElement>;\n  /**\n   * Called with the trimmed text on Enter. An uncontrolled prompt clears\n   * afterwards; with `value`, clear it yourself here.\n   */\n  onSend?: (text: string) => void;\n  /** Called on Escape while `busy`, to interrupt the running turn. */\n  onStop?: () => void;\n  placeholder?: string;\n  pullRequest?: ClaudeCodePullRequest;\n  value?: string;\n}\n\nexport interface ClaudeCodeSessionHeader {\n  cwd: string;\n  model: string;\n  org: string;\n  tips: string[];\n  version: string;\n  whatsNew: string[];\n}\n\nexport interface ClaudeCodeSessionToolCall {\n  arg?: string;\n  detail?: string;\n  id: string;\n  result: string;\n  status?: ClaudeCodeToolCallStatus;\n  tool: string;\n}\n\nexport interface ClaudeCodeSessionSpinner {\n  details: string[];\n  elapsed: string;\n  tip: string;\n  tokens: number;\n  verb: string;\n}\n\nexport interface ClaudeCodeSessionTurn {\n  answer: string;\n  commands: ClaudeCodeSessionToolCall[];\n  id: string;\n  prompt: string;\n  summary: ClaudeCodeSessionSummary;\n  thinking?: string;\n  todos?: ClaudeCodeTodo[];\n}\n\nexport interface ClaudeCodeSessionSummary {\n  doneAt: string;\n  duration: string;\n  verb: string;\n}\n\nexport type ClaudeCodeTurnStatus =\n  | \"working\"\n  | \"streaming\"\n  | \"done\"\n  | \"interrupted\";\n\n/** A scripted answer the demo plays back when you send a prompt. */\nexport interface ClaudeCodeReply {\n  answer: string;\n  commands: ClaudeCodeSessionToolCall[];\n  /** The spinner verb while the turn runs, like `Sketching`. */\n  spinnerVerb: string;\n  /** The verb on the summary line once the turn is done, like `Cooked`. */\n  summaryVerb: string;\n}\n\nexport interface ClaudeCodeChatTurn extends Omit<\n  ClaudeCodeSessionTurn,\n  \"summary\"\n> {\n  status: ClaudeCodeTurnStatus;\n  summary?: ClaudeCodeSessionSummary;\n}\n\nexport interface ClaudeCodeChat {\n  busy: boolean;\n  /** Whole seconds since the running turn started. */\n  elapsed: number;\n  send: (text: string) => Promise<void>;\n  spinnerVerb: string;\n  stop: () => void;\n  /** Tokens streamed in the running turn so far. */\n  tokens: number;\n  turns: ClaudeCodeChatTurn[];\n}\n\nexport interface ClaudeCodeSessionPendingTurn {\n  prompt: string;\n  spinner: ClaudeCodeSessionSpinner;\n  todos: ClaudeCodeTodo[];\n  toolCall: ClaudeCodeSessionToolCall;\n}\n\nexport interface ClaudeCodeSession {\n  header: ClaudeCodeSessionHeader;\n  pending: ClaudeCodeSessionPendingTurn;\n  promptPlaceholder: string;\n  pullRequest: ClaudeCodePullRequest;\n  title: string;\n  turns: ClaudeCodeSessionTurn[];\n}\n",
      "type": "registry:lib",
      "target": "components/claude-code/types/claude-code.ts"
    },
    {
      "path": "registry/notra/claude-code/constants/claude-code.ts",
      "content": "import type {\n  ClaudeCodeEffort,\n  ClaudeCodeEffortConfig,\n  ClaudeCodeMode,\n  ClaudeCodeModeConfig,\n  ClaudeCodeTodoConfig,\n  ClaudeCodeTodoStatus,\n  ClaudeCodeToolCallConfig,\n  ClaudeCodeToolCallStatus,\n} from \"../types/claude-code\";\n\nexport const CLAUDE_CODE_MODE_ORDER: ClaudeCodeMode[] = [\n  \"manual\",\n  \"accept-edits\",\n  \"plan\",\n  \"auto\",\n  \"bypass\",\n];\n\nexport const CLAUDE_CODE_EFFORT_ORDER: ClaudeCodeEffort[] = [\n  \"low\",\n  \"medium\",\n  \"high\",\n  \"xhigh\",\n  \"max\",\n  \"ultracode\",\n];\n\nexport const CLAUDE_CODE_MODES: Record<ClaudeCodeMode, ClaudeCodeModeConfig> = {\n  \"accept-edits\": {\n    className: \"text-claude-code-mode-accept-edits\",\n    cycles: true,\n    label: \"⏵⏵ accept edits on\",\n  },\n  auto: {\n    className: \"text-claude-code-mode-auto\",\n    cycles: true,\n    label: \"⏵⏵ auto mode on\",\n  },\n  bypass: {\n    className: \"text-claude-code-mode-bypass\",\n    cycles: true,\n    label: \"⏵⏵ bypass permissions on\",\n  },\n  manual: {\n    className: \"text-claude-code-muted\",\n    cycles: false,\n    label: \"? for shortcuts\",\n  },\n  plan: {\n    className: \"text-claude-code-mode-plan\",\n    cycles: true,\n    label: \"⏸ plan mode on\",\n  },\n};\n\nexport const CLAUDE_CODE_EFFORTS: Record<\n  ClaudeCodeEffort,\n  ClaudeCodeEffortConfig\n> = {\n  high: { className: \"text-claude-code-muted\", glyph: \"◕\" },\n  low: { className: \"text-claude-code-muted\", glyph: \"○\" },\n  max: { className: \"text-claude-code-muted\", glyph: \"●\" },\n  medium: { className: \"text-claude-code-muted\", glyph: \"◑\" },\n  ultracode: { className: \"text-claude-code-accent\", glyph: \"✦\" },\n  xhigh: { className: \"text-claude-code-muted\", glyph: \"◉\" },\n};\n\nexport const CLAUDE_CODE_TODOS: Record<\n  ClaudeCodeTodoStatus,\n  ClaudeCodeTodoConfig\n> = {\n  active: {\n    glyph: \"☐\",\n    labelClassName: \"text-claude-code-strong font-bold\",\n    srLabel: \"In progress\",\n  },\n  done: {\n    glyph: \"☒\",\n    labelClassName: \"text-claude-code-muted line-through\",\n    srLabel: \"Done\",\n  },\n  todo: {\n    glyph: \"☐\",\n    labelClassName: \"text-claude-code-fg\",\n    srLabel: \"To do\",\n  },\n};\n\nexport const CLAUDE_CODE_TOOL_CALLS: Record<\n  ClaudeCodeToolCallStatus,\n  ClaudeCodeToolCallConfig\n> = {\n  error: { className: \"text-claude-code-error\", srLabel: \"Failed\" },\n  pending: {\n    className:\n      \"text-claude-code-muted animate-pulse motion-reduce:animate-none\",\n    srLabel: \"Running\",\n  },\n  success: { className: \"text-claude-code-success\", srLabel: \"Done\" },\n};\n\nexport const CLAUDE_CODE_SPINNER_FRAMES = [\"·\", \"✢\", \"✳\", \"✶\", \"✻\", \"✽\"];\n\nexport const CLAUDE_CODE_SPINNER_INTERVAL_MS = 120;\n\nexport const CLAUDE_CODE_RESULT_GLYPH = \"⎿\";\n\nexport const CLAUDE_CODE_CHAT_TIMING = {\n  thinkMs: 1200,\n  toolMs: 900,\n  wordMs: 28,\n  reducedMaxMs: 60,\n  tickMs: 1000,\n} as const;\n\nexport const CLAUDE_CODE_MASCOT_SIZE = { height: 14, width: 22 };\n\nexport const CLAUDE_CODE_MASCOT_BODY: [number, number, number, number][] = [\n  [2, 0, 18, 10],\n  [0, 4, 22, 3],\n  [3, 10, 2, 4],\n  [7, 10, 2, 4],\n  [13, 10, 2, 4],\n  [17, 10, 2, 4],\n];\n\nexport const CLAUDE_CODE_MASCOT_EYES: [number, number, number, number][] = [\n  [6, 2, 2, 3],\n  [14, 2, 2, 3],\n];\n",
      "type": "registry:lib",
      "target": "components/claude-code/constants/claude-code.ts"
    },
    {
      "path": "registry/notra/claude-code/lib/claude-code-inline.tsx",
      "content": "import type { ReactNode } from \"react\";\n\nconst INLINE_TOKEN = /(`[^`]+`|\\*\\*[^*]+\\*\\*)/g;\n\nexport const renderClaudeCodeInline = (text: string): ReactNode[] => {\n  let offset = 0;\n\n  return text.split(INLINE_TOKEN).map((part) => {\n    const key = `${offset}-${part}`;\n    offset += part.length;\n\n    if (part.length > 2 && part.startsWith(\"`\") && part.endsWith(\"`\")) {\n      return (\n        <code className=\"text-claude-code-link font-claude-code\" key={key}>\n          {part.slice(1, -1)}\n        </code>\n      );\n    }\n\n    if (part.length > 4 && part.startsWith(\"**\") && part.endsWith(\"**\")) {\n      return (\n        <strong className=\"text-claude-code-strong font-bold\" key={key}>\n          {part.slice(2, -2)}\n        </strong>\n      );\n    }\n\n    return part;\n  });\n};\n\nexport const renderClaudeCodeChildren = (children: ReactNode): ReactNode =>\n  typeof children === \"string\" ? renderClaudeCodeInline(children) : children;\n",
      "type": "registry:lib",
      "target": "components/claude-code/lib/claude-code-inline.tsx"
    }
  ],
  "cssVars": {
    "theme": {
      "color-claude-code-bg": "var(--claude-code-bg)",
      "color-claude-code-chrome": "var(--claude-code-chrome)",
      "color-claude-code-traffic": "var(--claude-code-traffic)",
      "color-claude-code-title": "var(--claude-code-title)",
      "color-claude-code-shadow": "var(--claude-code-shadow)",
      "color-claude-code-ring": "var(--claude-code-ring)",
      "color-claude-code-fg": "var(--claude-code-fg)",
      "color-claude-code-strong": "var(--claude-code-strong)",
      "color-claude-code-accent": "var(--claude-code-accent)",
      "color-claude-code-muted": "var(--claude-code-muted)",
      "color-claude-code-rule": "var(--claude-code-rule)",
      "color-claude-code-link": "var(--claude-code-link)",
      "color-claude-code-pr": "var(--claude-code-pr)",
      "color-claude-code-user-bg": "var(--claude-code-user-bg)",
      "color-claude-code-user-prompt": "var(--claude-code-user-prompt)",
      "color-claude-code-success": "var(--claude-code-success)",
      "color-claude-code-error": "var(--claude-code-error)",
      "color-claude-code-mode-auto": "var(--claude-code-mode-auto)",
      "color-claude-code-mode-accept-edits": "var(--claude-code-mode-accept-edits)",
      "color-claude-code-mode-plan": "var(--claude-code-mode-plan)",
      "color-claude-code-mode-bypass": "var(--claude-code-mode-bypass)",
      "font-claude-code": "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace"
    },
    "light": {
      "claude-code-bg": "#ffffff",
      "claude-code-chrome": "#ececec",
      "claude-code-traffic": "#d4d4d4",
      "claude-code-title": "#00000073",
      "claude-code-shadow": "#00000026",
      "claude-code-ring": "#0000001a",
      "claude-code-fg": "#1f1f1f",
      "claude-code-strong": "#000000",
      "claude-code-accent": "#d77757",
      "claude-code-muted": "#666666",
      "claude-code-rule": "#999999",
      "claude-code-link": "#5769f7",
      "claude-code-pr": "#966c1e",
      "claude-code-user-bg": "#f0f0f0",
      "claude-code-user-prompt": "#999999",
      "claude-code-success": "#2c7a39",
      "claude-code-error": "#ab2b3f",
      "claude-code-mode-auto": "#966c1e",
      "claude-code-mode-accept-edits": "#8700ff",
      "claude-code-mode-plan": "#006666",
      "claude-code-mode-bypass": "#d1245a"
    },
    "dark": {
      "claude-code-bg": "#0f0f0f",
      "claude-code-chrome": "#1c1c1c",
      "claude-code-traffic": "#3a3a3a",
      "claude-code-title": "#ffffff66",
      "claude-code-shadow": "#0000004d",
      "claude-code-ring": "#ffffff1a",
      "claude-code-fg": "#f7f7f7",
      "claude-code-strong": "#ffffff",
      "claude-code-accent": "#e06443",
      "claude-code-muted": "#8c8c8c",
      "claude-code-rule": "#7b7b7b",
      "claude-code-link": "#a4b0fc",
      "claude-code-pr": "#d9b559",
      "claude-code-user-bg": "#2e2e2e",
      "claude-code-user-prompt": "#5c5c5c",
      "claude-code-success": "#4eba65",
      "claude-code-error": "#ff6b80",
      "claude-code-mode-auto": "#e5c07b",
      "claude-code-mode-accept-edits": "#af87ff",
      "claude-code-mode-plan": "#48968c",
      "claude-code-mode-bypass": "#fd526f"
    }
  },
  "type": "registry:component"
}