{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "opencode-turn-footer",
  "title": "OpenCode Turn Footer",
  "description": "The blue square, agent, model and duration line that closes an assistant turn.",
  "dependencies": [
    "cn"
  ],
  "registryDependencies": [
    "collapsible"
  ],
  "files": [
    {
      "path": "registry/notra/opencode/components/opencode-turn-footer.tsx",
      "content": "import { cn } from \"cn\";\n\nimport {\n  OPENCODE_DEFAULT_AGENT,\n  OPENCODE_DEFAULT_MODEL,\n} from \"../constants/opencode\";\nimport type { OpencodeTurnFooterProps } from \"../types/opencode\";\n\nexport const OpencodeTurnFooter = ({\n  agent = OPENCODE_DEFAULT_AGENT,\n  className,\n  duration,\n  interrupted = false,\n  model = OPENCODE_DEFAULT_MODEL,\n  ...props\n}: OpencodeTurnFooterProps) => (\n  <div\n    className={cn(\n      \"text-opencode-muted flex min-w-0 items-center gap-[1ch] ps-[3ch]\",\n      className\n    )}\n    data-slot=\"opencode-turn-footer\"\n    {...props}\n  >\n    <span\n      aria-hidden=\"true\"\n      className=\"border-opencode-blue grid size-[1.1em] shrink-0 place-items-center border-[1.5px]\"\n    >\n      <span className=\"bg-opencode-blue size-[0.5em]\" />\n    </span>\n    <span className=\"text-opencode-fg\">{agent}</span>\n    <span aria-hidden=\"true\">·</span>\n    <span className=\"min-w-0 truncate\">{model}</span>\n    {duration && (\n      <>\n        <span aria-hidden=\"true\">·</span>\n        <span className=\"shrink-0 tabular-nums\">{duration}</span>\n      </>\n    )}\n    {interrupted && (\n      <>\n        <span aria-hidden=\"true\">·</span>\n        <span className=\"text-opencode-orange shrink-0\">interrupted</span>\n      </>\n    )}\n  </div>\n);\n",
      "type": "registry:component",
      "target": "components/opencode/components/opencode-turn-footer.tsx"
    },
    {
      "path": "registry/notra/opencode/constants/opencode.ts",
      "content": "import type {\n  OpencodeActivityKind,\n  OpencodeMcpServer,\n  OpencodeMcpStatus,\n} from \"../types/opencode\";\n\nexport const OPENCODE_DEFAULT_AGENT = \"Build\";\n\nexport const OPENCODE_DEFAULT_MODEL = \"Writer 0.1\";\n\nexport const OPENCODE_DEFAULT_PROVIDER = \"Notra\";\n\nexport const OPENCODE_DEFAULT_EFFORT = \"high\";\n\nexport const OPENCODE_DEFAULT_PLACEHOLDER =\n  'Ask anything... \"Draft this week\\'s changelog\"';\n\nexport const OPENCODE_DEFAULT_VERSION = \"1.18.33\";\n\nexport const OPENCODE_DEFAULT_CWD = \"~/acme/web\";\n\nexport const OPENCODE_DEFAULT_SERVERS: OpencodeMcpServer[] = [\n  { name: \"notra\", status: \"Connected\" },\n  { name: \"github\", status: \"Connected\" },\n  { name: \"linear\", status: \"Connected\" },\n];\n\nexport const OPENCODE_MCP_STATUS_CLASS: Record<OpencodeMcpStatus, string> = {\n  Connected: \"text-opencode-green\",\n  Disconnected: \"text-opencode-muted\",\n  Failed: \"text-opencode-orange\",\n};\n\nexport const OPENCODE_ACTIVITY_GLYPH: Record<OpencodeActivityKind, string> = {\n  read: \"→\",\n  search: \"◈\",\n  thought: \"\",\n  tool: \"⚙\",\n};\n\nexport const OPENCODE_ACTIVITY_LABEL: Record<OpencodeActivityKind, string> = {\n  read: \"Read\",\n  search: \"Web Search\",\n  thought: \"Thought\",\n  tool: \"Tool\",\n};\n\nexport const OPENCODE_THINKING_FRAMES = [\n  \"⠋\",\n  \"⠙\",\n  \"⠹\",\n  \"⠸\",\n  \"⠼\",\n  \"⠴\",\n  \"⠦\",\n  \"⠧\",\n  \"⠇\",\n  \"⠏\",\n] as const;\n\nexport const OPENCODE_THINKING_FRAME_MS = 80;\n\nexport const OPENCODE_PROGRESS_CELLS = 8;\n\nexport const OPENCODE_PROGRESS_FRAME_MS = 90;\n\nexport const OPENCODE_PROGRESS_TRAIL = [1, 0.55, 0.32, 0.18] as const;\n\nexport const OPENCODE_SEARCH_HEADER_MS = 140;\n\nexport const OPENCODE_SEARCH_QUERY_MS = 280;\n\nexport const OPENCODE_SEARCH_STAGGER_MS = 56;\n\nexport const OPENCODE_SEARCH_SOURCES_MS = 160;\n\nexport const OPENCODE_CHAT_TIMING = {\n  thinkMs: 1300,\n  toolMs: 600,\n  blockMs: 260,\n  reducedMaxMs: 60,\n} as const;\n\nexport const OPENCODE_REDUCED_MOTION_QUERY = \"(prefers-reduced-motion: reduce)\";\n\nexport const OPENCODE_LOGO_ROWS = [\n  \".................................c.....\",\n  \"oooo.oooo.oooo.ooo..cccc.cccc.cccc.cccc\",\n  \"o++o.o++o.o++o.o..o.c....c++c.c++c.c++c\",\n  \"o++o.o++o.oooo.o..o.c....c++c.c++c.cccc\",\n  \"o++o.oooo.o....o..o.c....c++c.c++c.c...\",\n  \"oooo.o....oooo.o..o.cccc.cccc.cccc.cccc\",\n  \".....o.................................\",\n] as const;\n\nexport const OPENCODE_LOGO_CELL = 6;\n",
      "type": "registry:lib",
      "target": "components/opencode/constants/opencode.ts"
    },
    {
      "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"
}