{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "opencode-message",
  "title": "OpenCode Message",
  "description": "User prompts on a blue-ruled panel and indented assistant replies with OpenCode's markdown colors.",
  "dependencies": [
    "cn"
  ],
  "registryDependencies": [
    "collapsible"
  ],
  "files": [
    {
      "path": "registry/notra/opencode/components/opencode-message.tsx",
      "content": "import { cn } from \"cn\";\n\nimport type { OpencodeMessageProps } from \"../types/opencode\";\n\nconst REPLY_MARKUP_CLASS =\n  \"[&_:is(h1,h2,h3,h4)]:text-opencode-orange [&_:is(h1,h2,h3,h4)]:text-[length:inherit] [&_:is(h1,h2,h3,h4)]:font-bold [&_code]:text-opencode-green [&_code]:font-[inherit] [&_code]:text-[length:inherit] [&_li]:ps-[2ch] [&_li]:before:-ms-[2ch] [&_li]:before:inline-block [&_li]:before:w-[2ch] [&_li]:before:text-opencode-orange [&_li]:before:content-['-'] [&_ol]:list-none [&_strong]:text-opencode-orange [&_strong]:font-bold [&_ul]:list-none [&>*+*]:mt-[1lh]\";\n\nexport const OpencodeMessage = ({\n  actions,\n  children,\n  className,\n  from = \"assistant\",\n  search,\n  ...props\n}: OpencodeMessageProps) => {\n  if (from === \"user\") {\n    return (\n      <div\n        className={cn(\n          \"border-opencode-blue bg-opencode-panel text-opencode-fg border-s-2 py-[1lh] ps-[calc(3ch-2px)] pe-[2ch] wrap-break-word whitespace-pre-wrap\",\n          className\n        )}\n        data-from=\"user\"\n        data-slot=\"opencode-message\"\n        {...props}\n      >\n        {children}\n      </div>\n    );\n  }\n\n  return (\n    <div\n      className={cn(\"flex w-full min-w-0 flex-col gap-[1lh]\", className)}\n      data-from=\"assistant\"\n      data-slot=\"opencode-message\"\n      {...props}\n    >\n      {search}\n      <div\n        className={cn(\n          \"text-opencode-fg max-w-full ps-[3ch] wrap-break-word\",\n          REPLY_MARKUP_CLASS\n        )}\n      >\n        {children}\n      </div>\n      {actions}\n    </div>\n  );\n};\n",
      "type": "registry:component",
      "target": "components/opencode/components/opencode-message.tsx"
    },
    {
      "path": "registry/notra/opencode/types/opencode.ts",
      "content": "import type { ScrollArea as ScrollAreaPrimitive } from \"@base-ui/react/scroll-area\";\nimport type { ComponentProps, ReactNode } from \"react\";\n\nimport type { Collapsible } from \"@/components/ui/collapsible\";\n\nexport type OpencodeActivityKind = \"thought\" | \"read\" | \"search\" | \"tool\";\n\nexport type OpencodeMessageAuthor = \"user\" | \"assistant\";\n\nexport type OpencodeMcpStatus = \"Connected\" | \"Disconnected\" | \"Failed\";\n\nexport type OpencodeWindowProps = ComponentProps<\"div\">;\n\nexport type OpencodeScrollAreaProps = ScrollAreaPrimitive.Root.Props;\n\nexport interface OpencodeLogoProps extends Omit<\n  ComponentProps<\"svg\">,\n  \"height\" | \"width\"\n> {\n  /** Multiplies the 234 by 42 base size. */\n  scale?: number;\n}\n\nexport interface OpencodeMessageProps extends ComponentProps<\"div\"> {\n  /** Rendered under an assistant reply. */\n  actions?: ReactNode;\n  from?: OpencodeMessageAuthor;\n  /** Rendered above an assistant reply, usually `OpencodeSources`. */\n  search?: ReactNode;\n}\n\nexport interface OpencodeTurnFooterProps extends ComponentProps<\"div\"> {\n  agent?: string;\n  /** Marks a turn that was stopped with Escape. */\n  interrupted?: boolean;\n  /** Wall time of the turn. Omit it while the turn is still running. */\n  duration?: string;\n  model?: string;\n}\n\nexport interface OpencodeActivityProps extends ComponentProps<\n  typeof Collapsible\n> {\n  /** Arguments after the label, like a path or a query. */\n  detail?: string;\n  /** Shown after a dot, like `1.4s`. */\n  duration?: string;\n  kind?: OpencodeActivityKind;\n  /** Defaults to `Thought`, `Read` or `Web Search` by kind. */\n  label?: string;\n  /** Renders a running thought as an animated `Thinking` line. */\n  pending?: boolean;\n  /** Freezes the thinking spinner. Defaults to the user's preference. */\n  reducedMotion?: boolean;\n}\n\nexport interface OpencodeProgressProps extends ComponentProps<\"span\"> {\n  /** Animates the scanner. When false every cell rests as a dot. */\n  active?: boolean;\n  /** Freezes the scanner. Defaults to the user's preference. */\n  reducedMotion?: boolean;\n}\n\nexport interface OpencodeComposerProps extends Omit<\n  ComponentProps<\"input\">,\n  \"children\" | \"className\" | \"type\"\n> {\n  agent?: string;\n  /**\n   * Shows the scanner and `esc interrupt` instead of the cwd. Enter does not\n   * send while busy and Escape calls `onStop`.\n   */\n  busy?: boolean;\n  className?: string;\n  /** Context usage on the status row, like `63.4K (6%)`. */\n  context?: string;\n  /** Working directory shown on the status row while idle. */\n  cwd?: string;\n  effort?: string;\n  inputClassName?: string;\n  model?: string;\n  /**\n   * Called with the trimmed text on Enter. An uncontrolled composer 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  provider?: string;\n}\n\nexport interface OpencodeMcpServer {\n  name: string;\n  status?: OpencodeMcpStatus;\n}\n\nexport interface OpencodeSidebarProps extends Omit<\n  ComponentProps<\"aside\">,\n  \"title\"\n> {\n  /** Git branch appended to the cwd. Its last segment is highlighted. */\n  branch?: string;\n  cwd?: string;\n  lsp?: ReactNode;\n  mcpOpen?: ComponentProps<typeof Collapsible>[\"open\"];\n  onMcpOpenChange?: ComponentProps<typeof Collapsible>[\"onOpenChange\"];\n  servers?: OpencodeMcpServer[];\n  spent?: string;\n  title?: ReactNode;\n  tokens?: string;\n  used?: string;\n  version?: string;\n}\n\nexport interface OpencodeSource {\n  domain: string;\n  title: string;\n  url?: string;\n}\n\nexport interface OpencodeSourcesLabels {\n  citedSources: (count: number) => string;\n  openSource: (title: string, domain: string) => string;\n}\n\nexport interface OpencodeSourcesProps extends Omit<\n  ComponentProps<typeof Collapsible>,\n  \"children\"\n> {\n  labels?: OpencodeSourcesLabels;\n  queries?: readonly string[];\n  reducedMotion?: boolean;\n  sequential?: boolean;\n  sources: readonly OpencodeSource[];\n}\n\nexport interface OpencodeDemoActivity {\n  body?: string;\n  detail?: string;\n  duration?: string;\n  id: string;\n  kind: OpencodeActivityKind;\n  label?: string;\n}\n\n/** A run of reply text: plain, `code` (green) or strong (orange). */\nexport type OpencodeDemoSpan = string | { code: string } | { strong: string };\n\nexport interface OpencodeDemoListItem {\n  id: string;\n  spans: OpencodeDemoSpan[];\n}\n\nexport type OpencodeDemoBlock =\n  | { heading: string; id: string }\n  | { id: string; items: OpencodeDemoListItem[] }\n  | { id: string; text: OpencodeDemoSpan[] };\n\nexport interface OpencodeDemoTurn {\n  activities: OpencodeDemoActivity[];\n  duration?: string;\n  id: string;\n  prompt: string;\n  reply: OpencodeDemoBlock[];\n}\n\nexport interface OpencodeDemoSession {\n  agent: string;\n  branch: string;\n  context: string;\n  cwd: string;\n  effort: string;\n  model: string;\n  provider: string;\n  servers: OpencodeMcpServer[];\n  spent: string;\n  title: string;\n  tokens: string;\n  used: string;\n  version: string;\n}\n\nexport type OpencodeTurnStatus =\n  | \"working\"\n  | \"streaming\"\n  | \"done\"\n  | \"interrupted\";\n\n/** A scripted answer the demo plays back when you send a prompt. */\nexport interface OpencodeReply {\n  /** Thoughts and tool calls, played in order before the reply. */\n  activities: OpencodeDemoActivity[];\n  reply: OpencodeDemoBlock[];\n}\n\nexport interface OpencodeChatActivity extends OpencodeDemoActivity {\n  /** A thought still thinking, shown with the spinner. */\n  pending?: boolean;\n  /** Still playing back. Dropped from the transcript when interrupted. */\n  running?: boolean;\n}\n\nexport interface OpencodeChatTurn {\n  activities: OpencodeChatActivity[];\n  duration?: string;\n  id: string;\n  prompt: string;\n  reply: OpencodeDemoBlock[];\n  status: OpencodeTurnStatus;\n}\n\nexport interface OpencodeChat {\n  busy: boolean;\n  send: (text: string) => Promise<void>;\n  stop: () => void;\n  turns: OpencodeChatTurn[];\n}\n",
      "type": "registry:lib",
      "target": "components/opencode/types/opencode.ts"
    }
  ],
  "cssVars": {
    "theme": {
      "color-opencode-bg": "var(--opencode-bg)",
      "color-opencode-panel": "var(--opencode-panel)",
      "color-opencode-sidebar": "var(--opencode-sidebar)",
      "color-opencode-fg": "var(--opencode-fg)",
      "color-opencode-muted": "var(--opencode-muted)",
      "color-opencode-subtle": "var(--opencode-subtle)",
      "color-opencode-blue": "var(--opencode-blue)",
      "color-opencode-orange": "var(--opencode-orange)",
      "color-opencode-green": "var(--opencode-green)",
      "color-opencode-logo-open": "var(--opencode-logo-open)",
      "color-opencode-logo-code": "var(--opencode-logo-code)",
      "color-opencode-logo-fill": "var(--opencode-logo-fill)",
      "font-opencode": "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace"
    },
    "light": {
      "opencode-bg": "#ffffff",
      "opencode-panel": "#f5f5f5",
      "opencode-sidebar": "#fafafa",
      "opencode-fg": "#1a1a1a",
      "opencode-muted": "#6f6f6f",
      "opencode-subtle": "#e1e1e1",
      "opencode-blue": "#3b7dd8",
      "opencode-orange": "#c47a14",
      "opencode-green": "#2f8a4a",
      "opencode-logo-open": "#8a8a8a",
      "opencode-logo-code": "#1a1a1a",
      "opencode-logo-fill": "#dcdcdc"
    },
    "dark": {
      "opencode-bg": "#090909",
      "opencode-panel": "#191919",
      "opencode-sidebar": "#121212",
      "opencode-fg": "#e9e9e9",
      "opencode-muted": "#737373",
      "opencode-subtle": "#262626",
      "opencode-blue": "#3b91f9",
      "opencode-orange": "#fd9718",
      "opencode-green": "#4fd47b",
      "opencode-logo-open": "#737373",
      "opencode-logo-code": "#e9e9e9",
      "opencode-logo-fill": "#2a2a2a"
    }
  },
  "type": "registry:component"
}