{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "claude-message",
  "title": "Claude Message",
  "description": "User bubbles and serif assistant replies with slots for search, sources and actions.",
  "dependencies": [
    "cn"
  ],
  "registryDependencies": [
    "collapsible"
  ],
  "files": [
    {
      "path": "registry/notra/claude/components/claude-message.tsx",
      "content": "import { cn } from \"cn\";\n\nimport type { ClaudeMessageProps } from \"../types/claude\";\n\nconst ACTIONS_REVEAL_CLASS =\n  \"opacity-0 transition-opacity duration-150 group-focus-within/claude-message:opacity-100 motion-reduce:transition-none [@media(hover:hover)]:group-hover/claude-message:opacity-100 [@media(hover:none)]:opacity-100\";\n\nexport const ClaudeMessage = ({\n  actions,\n  children,\n  className,\n  from,\n  search,\n  sources,\n  ...props\n}: ClaudeMessageProps) => {\n  if (from === \"user\") {\n    return (\n      <div\n        className={cn(\n          \"group/claude-message font-claude flex flex-col items-end gap-1\",\n          className\n        )}\n        data-from=\"user\"\n        data-slot=\"claude-message\"\n        {...props}\n      >\n        <div className=\"bg-claude-bubble text-claude-fg max-w-[75%] rounded-xl px-4 py-2.5 text-[0.9375rem] leading-6 wrap-break-word\">\n          {children}\n        </div>\n        {actions && <div className={ACTIONS_REVEAL_CLASS}>{actions}</div>}\n      </div>\n    );\n  }\n\n  return (\n    <div\n      className={cn(\n        \"group/claude-message font-claude flex w-full flex-col items-start gap-3\",\n        className\n      )}\n      data-from=\"assistant\"\n      data-slot=\"claude-message\"\n      {...props}\n    >\n      {search}\n      {(children != null || sources) && (\n        <div className=\"font-claude-serif text-claude-fg flex max-w-full flex-col gap-4 text-[0.9375rem] leading-[1.65] wrap-break-word\">\n          {children}\n          {sources}\n        </div>\n      )}\n      {actions && <div className={ACTIONS_REVEAL_CLASS}>{actions}</div>}\n    </div>\n  );\n};\n",
      "type": "registry:component",
      "target": "components/claude/components/claude-message.tsx"
    },
    {
      "path": "registry/notra/claude/types/claude.ts",
      "content": "import type { ComponentProps, ReactNode } from \"react\";\n\nimport type { Collapsible } from \"@/components/ui/collapsible\";\n\nexport type ClaudeModelId =\n  | \"fable-5.1\"\n  | \"sonnet-5.5\"\n  | \"opus-5.5\"\n  | \"opus-5\"\n  | \"fable-5\"\n  | \"sonnet-5\"\n  | \"haiku-4.5\"\n  | \"opus-4.8\"\n  | \"opus-4.7\"\n  | \"opus-4.6\"\n  | \"opus-3\"\n  | \"sonnet-4.6\";\n\nexport type ClaudeEffortId = \"low\" | \"medium\" | \"high\" | \"extra\" | \"max\";\n\nexport interface ClaudeModelOption {\n  description: string;\n  group: \"latest\" | \"previous\";\n  id: ClaudeModelId;\n  label: string;\n}\n\nexport interface ClaudeEffortOption {\n  badge?: string;\n  id: ClaudeEffortId;\n  info?: string;\n  label: string;\n}\n\nexport type ClaudeMessageRole = \"user\" | \"assistant\";\n\nexport interface ClaudeSearchResult {\n  domain: string;\n  title: string;\n}\n\nexport interface ClaudeSearchGroup {\n  count: number;\n  query: string;\n  results: ClaudeSearchResult[];\n}\n\nexport interface ClaudeStepThought {\n  text: ReactNode;\n  type: \"thought\";\n}\n\nexport interface ClaudeStepTool {\n  code?: string;\n  content?: ReactNode;\n  count?: number;\n  detail?: ReactNode;\n  label: ReactNode;\n  logo?: ReactNode;\n  results?: ClaudeSearchResult[];\n  tool?: string;\n  type: \"tool\";\n}\n\nexport type ClaudeStepItem = ClaudeStepThought | ClaudeStepTool;\n\nexport type ClaudeSearchStepIcon = \"clock\" | \"check\" | \"error\";\n\nexport interface ClaudeSearchStep {\n  icon: ClaudeSearchStepIcon;\n  label: string;\n}\n\nexport interface ClaudeSourceItem {\n  domain: string;\n  favicon?: string;\n  href: string;\n  title: string;\n}\n\nexport interface ClaudeSpinnerFrame {\n  inner: number;\n  jitter?: number;\n  outer: number;\n  rays: number;\n  strokeWidth: number;\n}\n\nexport interface ClaudeSpinnerProps extends ComponentProps<\"span\"> {\n  animated?: boolean;\n  reducedMotion?: boolean;\n  size?: number;\n}\n\nexport interface ClaudeThinkingProps extends ComponentProps<\"div\"> {\n  intervalMs?: number;\n  reducedMotion?: boolean;\n  verbs?: readonly string[];\n}\n\nexport interface ClaudeSearchProps extends Omit<\n  ComponentProps<typeof Collapsible>,\n  \"children\"\n> {\n  groups?: readonly ClaudeSearchGroup[];\n  items?: readonly ClaudeStepItem[];\n  logo?: ReactNode;\n  queryLabel?: ReactNode;\n  reducedMotion?: boolean;\n  resultsLabel?: (count: number) => ReactNode;\n  sequential?: boolean;\n  steps?: readonly ClaudeSearchStep[];\n  summary?: ReactNode;\n  thought?: ReactNode;\n  verb?: ReactNode;\n}\n\nexport interface ClaudeSourcesProps extends ComponentProps<\"div\"> {\n  label?: ReactNode;\n  sources: readonly ClaudeSourceItem[];\n}\n\nexport interface ClaudeSourceLinkProps extends Omit<\n  ComponentProps<\"a\">,\n  \"href\"\n> {\n  source: ClaudeSourceItem;\n}\n\nexport interface ClaudeMessageProps extends ComponentProps<\"div\"> {\n  actions?: ReactNode;\n  from: ClaudeMessageRole;\n  search?: ReactNode;\n  sources?: ReactNode;\n}\n\nexport interface ClaudeActionsProps extends ComponentProps<\"div\"> {\n  from?: ClaudeMessageRole;\n  onBadResponse?: () => void;\n  onEdit?: () => void;\n  onGoodResponse?: () => void;\n  onReadAloud?: () => void;\n  onRetry?: () => void;\n  text: string;\n  timestamp?: ReactNode;\n}\n\nexport interface ClaudeActionButtonProps {\n  children: ReactNode;\n  label: string;\n  onClick?: () => void;\n}\n\nexport interface ClaudeModelSelectorProps {\n  className?: string;\n  effort: ClaudeEffortId;\n  effortLabel?: ReactNode;\n  model: ClaudeModelId;\n  moreModelsLabel?: ReactNode;\n  onEffortChange?: (effort: ClaudeEffortId) => void;\n  onModelChange?: (model: ClaudeModelId) => void;\n}\n\nexport interface ClaudeComposerProps extends Omit<\n  ComponentProps<\"div\">,\n  \"onSubmit\"\n> {\n  busy?: boolean;\n  defaultEffort?: ClaudeEffortId;\n  defaultModel?: ClaudeModelId;\n  disclaimer?: ReactNode;\n  effort?: ClaudeEffortId;\n  model?: ClaudeModelId;\n  onEffortChange?: (effort: ClaudeEffortId) => void;\n  onModelChange?: (model: ClaudeModelId) => void;\n  contextUsage?: number;\n  mode?: ReactNode;\n  onModeClick?: () => void;\n  onPlusSelect?: (item: ClaudePlusMenuItemId) => void;\n  onSend?: (text: string) => void;\n  onStop?: () => void;\n  placeholder?: string;\n}\n\nexport type ClaudePlusMenuItemId =\n  | \"add-files\"\n  | \"add-plugins\"\n  | \"research\"\n  | `skill:${string}`\n  | `connector:${string}`\n  | `design-system:${string}`;\n\nexport interface ClaudePlusMenuProps {\n  className?: string;\n  onSelect?: (item: ClaudePlusMenuItemId) => void;\n}\n\nexport interface ClaudeUsageRingProps extends ComponentProps<\"div\"> {\n  label?: string;\n  size?: number;\n  value: number;\n}\n\nexport interface ClaudeDemoSearch {\n  groups: ClaudeSearchGroup[];\n  items?: ClaudeStepItem[];\n  steps?: ClaudeSearchStep[];\n  summary?: string;\n  thought?: string;\n  verb: string;\n}\n\nexport interface ClaudeDemoMessage {\n  from: ClaudeMessageRole;\n  id: string;\n  search?: ClaudeDemoSearch;\n  sources?: ClaudeSourceItem[];\n  text: string;\n}\n\nexport interface ClaudeTextSegment {\n  offset: number;\n  text: string;\n}\n\nexport type ClaudePlaybackPhase =\n  | { status: \"idle\" }\n  | { status: \"playing\"; source: \"script\" | \"send\"; thinking: boolean };\n",
      "type": "registry:lib",
      "target": "components/claude/types/claude.ts"
    }
  ],
  "cssVars": {
    "theme": {
      "color-claude-bg": "var(--claude-bg)",
      "color-claude-fg": "var(--claude-fg)",
      "color-claude-border": "var(--claude-border)",
      "color-claude-muted": "var(--claude-muted)",
      "color-claude-subtle": "var(--claude-subtle)",
      "color-claude-faint": "var(--claude-faint)",
      "color-claude-hover": "var(--claude-hover)",
      "color-claude-bubble": "var(--claude-bubble)",
      "color-claude-input": "var(--claude-input)",
      "color-claude-input-border": "var(--claude-input-border)",
      "color-claude-input-shadow": "var(--claude-input-shadow)",
      "color-claude-send": "var(--claude-send)",
      "color-claude-send-hover": "var(--claude-send-hover)",
      "color-claude-send-fg": "var(--claude-send-fg)",
      "color-claude-accent": "var(--claude-accent)",
      "color-claude-check": "var(--claude-check)",
      "color-claude-rail": "var(--claude-rail)",
      "color-claude-text": "var(--claude-text)",
      "color-claude-meta": "var(--claude-meta)",
      "color-claude-card": "var(--claude-card)",
      "color-claude-card-border": "var(--claude-card-border)",
      "color-claude-card-divider": "var(--claude-card-divider)",
      "color-claude-step": "var(--claude-step)",
      "color-claude-danger": "var(--claude-danger)",
      "color-claude-pill": "var(--claude-pill)",
      "color-claude-pill-border": "var(--claude-pill-border)",
      "color-claude-pill-fg": "var(--claude-pill-fg)",
      "color-claude-popover": "var(--claude-popover)",
      "color-claude-popover-border": "var(--claude-popover-border)",
      "color-claude-popover-shadow": "var(--claude-popover-shadow)",
      "color-claude-tooltip": "var(--claude-tooltip)",
      "color-claude-tooltip-fg": "var(--claude-tooltip-fg)",
      "font-claude": "\"Anthropic Sans\", ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", sans-serif",
      "font-claude-serif": "\"Anthropic Serif\", ui-serif, Georgia, Cambria, \"Times New Roman\", Times, serif",
      "color-claude-usage": "var(--claude-usage)"
    },
    "light": {
      "claude-bg": "#fcfcfb",
      "claude-fg": "#0b0b0b",
      "claude-border": "#e3e3e2",
      "claude-muted": "#898781",
      "claude-subtle": "#898781",
      "claude-faint": "#9d9d9d",
      "claude-hover": "#e8e8e7",
      "claude-bubble": "#f0f0ef",
      "claude-input": "#ffffff",
      "claude-input-border": "#e0e0df",
      "claude-input-shadow": "rgb(0 0 0 / 3%)",
      "claude-send": "#0b0b0b",
      "claude-send-hover": "#000000",
      "claude-send-fg": "#ffffff",
      "claude-accent": "#d97757",
      "claude-check": "#2c84db",
      "claude-rail": "#e3e3e2",
      "claude-text": "#52514e",
      "claude-meta": "#898781",
      "claude-card": "#f0f0ef",
      "claude-card-border": "#e3e3e2",
      "claude-card-divider": "#e3e3e2",
      "claude-step": "#52514e",
      "claude-danger": "#b53333",
      "claude-pill": "#f0f0ef",
      "claude-pill-border": "rgb(0 0 0 / 6%)",
      "claude-pill-fg": "#52514e",
      "claude-popover": "#ffffff",
      "claude-popover-border": "rgb(0 0 0 / 8%)",
      "claude-popover-shadow": "rgb(0 0 0 / 10%)",
      "claude-tooltip": "#0b0b0b",
      "claude-tooltip-fg": "#fcfcfb",
      "claude-usage": "#009300"
    },
    "dark": {
      "claude-bg": "#262624",
      "claude-fg": "#faf9f5",
      "claude-border": "#3d3d3a",
      "claude-muted": "#9c9a92",
      "claude-subtle": "#9c9a92",
      "claude-faint": "#85837c",
      "claude-hover": "#3a3a37",
      "claude-bubble": "#141413",
      "claude-input": "#30302e",
      "claude-input-border": "#44433f",
      "claude-input-shadow": "transparent",
      "claude-send": "#faf9f5",
      "claude-send-hover": "#ffffff",
      "claude-send-fg": "#141413",
      "claude-accent": "#d97757",
      "claude-check": "#5a9ee6",
      "claude-rail": "#3d3d3a",
      "claude-text": "#c2c0b6",
      "claude-meta": "#9c9a92",
      "claude-card": "#1f1e1d",
      "claude-card-border": "#3d3d3a",
      "claude-card-divider": "#3d3d3a",
      "claude-step": "#c2c0b6",
      "claude-danger": "#e5776b",
      "claude-pill": "#1f1e1d",
      "claude-pill-border": "rgb(255 255 255 / 8%)",
      "claude-pill-fg": "#c2c0b6",
      "claude-popover": "#30302e",
      "claude-popover-border": "rgb(222 220 209 / 15%)",
      "claude-popover-shadow": "rgb(0 0 0 / 40%)",
      "claude-tooltip": "#faf9f5",
      "claude-tooltip-fg": "#141413",
      "claude-usage": "#3fb950"
    }
  },
  "type": "registry:component"
}