{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "codex-composer",
  "title": "Codex Composer",
  "description": "The prompt band with a block cursor, the model and directory status line and shortcut hints.",
  "dependencies": [
    "cn"
  ],
  "registryDependencies": [
    "card",
    "collapsible",
    "input-group",
    "kbd"
  ],
  "files": [
    {
      "path": "registry/notra/codex/components/codex-composer.tsx",
      "content": "\"use client\";\n\nimport { cn } from \"cn\";\nimport { useState } from \"react\";\nimport type { ChangeEvent, FormEvent, KeyboardEvent } from \"react\";\n\nimport {\n  InputGroup,\n  InputGroupAddon,\n  InputGroupInput,\n} from \"@/components/ui/input-group\";\nimport { Kbd } from \"@/components/ui/kbd\";\n\nimport {\n  CODEX_DEFAULT_CWD,\n  CODEX_DEFAULT_MODEL,\n  CODEX_DEFAULT_PLACEHOLDER,\n  CODEX_ROW_CLASS,\n} from \"../constants/codex\";\nimport type { CodexComposerProps } from \"../types/codex\";\n\nconst CodexDot = () => <span className=\"text-codex-dim\"> · </span>;\n\nconst CodexKey = ({ children }: { children: string }) => (\n  <Kbd className=\"text-codex-fg font-codex inline h-auto min-w-0 rounded-none bg-transparent p-0 text-[length:inherit] leading-[inherit] font-bold\">\n    {children}\n  </Kbd>\n);\n\nexport const CodexComposer = ({\n  \"aria-label\": ariaLabel,\n  busy = false,\n  className,\n  context,\n  cwd = CODEX_DEFAULT_CWD,\n  defaultValue,\n  effort,\n  inputClassName,\n  model = CODEX_DEFAULT_MODEL,\n  onChange,\n  onKeyDown,\n  onSend,\n  onStop,\n  placeholder = CODEX_DEFAULT_PLACEHOLDER,\n  task,\n  value,\n  warnings,\n  ...props\n}: CodexComposerProps) => {\n  const [draft, setDraft] = useState(String(defaultValue ?? \"\"));\n  const isControlled = value !== undefined;\n  const text = isControlled ? String(value) : draft;\n  const isEmpty = text.length === 0;\n  const [cursorChar = \" \", ...restChars] = Array.from(placeholder);\n\n  const handleChange = (event: ChangeEvent<HTMLInputElement>) => {\n    setDraft(event.target.value);\n    onChange?.(event);\n  };\n\n  const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {\n    if (event.key === \"Escape\" && busy && onStop) {\n      event.preventDefault();\n      onStop();\n    }\n    onKeyDown?.(event);\n  };\n\n  const handleSubmit = (event: FormEvent<HTMLFormElement>) => {\n    event.preventDefault();\n    const message = text.trim();\n    if (!message || busy || !onSend) {\n      return;\n    }\n    onSend(message);\n    if (!isControlled) {\n      setDraft(\"\");\n    }\n  };\n\n  return (\n    <form\n      className={cn(\n        \"font-codex text-codex-dim min-w-0 text-[0.8125rem] leading-[1.3]\",\n        className\n      )}\n      data-slot=\"codex-composer\"\n      onSubmit={handleSubmit}\n    >\n      <InputGroup\n        className={cn(\n          CODEX_ROW_CLASS,\n          \"bg-codex-composer dark:bg-codex-composer h-auto rounded-none border-0 py-[1.3em] has-[[data-slot=input-group-control]:focus-visible]:ring-0\"\n        )}\n      >\n        <InputGroupAddon className=\"text-codex-fg order-none cursor-text justify-start p-0 text-[length:inherit] leading-[inherit] font-bold\">\n          <span aria-hidden=\"true\">›</span>\n        </InputGroupAddon>\n        <div className=\"relative min-w-0\">\n          {isEmpty && (\n            <span\n              aria-hidden=\"true\"\n              className=\"text-codex-placeholder pointer-events-none absolute inset-0 truncate\"\n            >\n              <span className=\"bg-codex-cursor text-codex-fg\">\n                {cursorChar}\n              </span>\n              {restChars.join(\"\")}\n            </span>\n          )}\n          <InputGroupInput\n            aria-label={ariaLabel ?? placeholder}\n            className={cn(\n              \"text-codex-fg caret-codex-cursor h-auto p-0 font-[inherit] text-[0.8125rem] leading-[1.3] md:text-[0.8125rem]\",\n              isEmpty && \"caret-transparent\",\n              inputClassName\n            )}\n            autoComplete=\"off\"\n            enterKeyHint=\"send\"\n            onChange={handleChange}\n            onKeyDown={handleKeyDown}\n            type=\"text\"\n            value={text}\n            {...props}\n          />\n        </div>\n      </InputGroup>\n      <div className={cn(CODEX_ROW_CLASS, \"mt-[0.5em]\")}>\n        <p className=\"col-start-2 min-w-0 truncate\">\n          <span className=\"text-codex-yellow\">\n            {effort ? `${model} ${effort}` : model}\n          </span>\n          <CodexDot />\n          <span className=\"text-codex-green\">{cwd}</span>\n          {task && (\n            <>\n              <CodexDot />\n              <span className=\"text-codex-yellow\">{task}</span>\n            </>\n          )}\n        </p>\n        <div className=\"col-start-2 flex min-w-0 flex-wrap justify-between gap-x-[2ch]\">\n          <p>\n            <CodexKey>←</CodexKey> for agents\n            <CodexDot />\n            <CodexKey>?</CodexKey> for shortcuts\n          </p>\n          {warnings ? (\n            <p>\n              <span className=\"text-codex-amber\">\n                <span aria-hidden=\"true\" className=\"inline-block w-[1ch]\">\n                  ⚠\n                </span>\n                {` ${warnings} ${warnings === 1 ? \"warning\" : \"warnings\"}`}\n              </span>\n              <CodexDot />\n              <CodexKey>f2</CodexKey> to view\n            </p>\n          ) : (\n            context && <p>{context}</p>\n          )}\n        </div>\n      </div>\n    </form>\n  );\n};\n",
      "type": "registry:component",
      "target": "components/codex/components/codex-composer.tsx"
    },
    {
      "path": "registry/notra/codex/constants/codex.ts",
      "content": "import type { CodexExecStatus } from \"../types/codex\";\n\nexport const CODEX_DEFAULT_VERSION = \"0.159.2\";\n\nexport const CODEX_DEFAULT_MODEL = \"gpt-6.1-codex\";\n\nexport const CODEX_DEFAULT_CWD = \"~/acme/web\";\n\nexport const CODEX_DEFAULT_PLACEHOLDER = \"Ask Codex to do anything\";\n\nexport const CODEX_DEFAULT_GREETING = \"Anything interesting on the docket?\";\n\nexport const CODEX_EXEC_PREVIEW_LINES = 3;\n\nexport const CODEX_SHIMMER_MS = 2000;\n\nexport const CODEX_BULLET_BLINK_MS = 1200;\n\nexport const CODEX_CHAT_TIMING = {\n  thinkMs: 1100,\n  execMs: 1000,\n  wordMs: 26,\n  reducedMaxMs: 60,\n  tickMs: 1000,\n} as const;\n\nexport const CODEX_EXEC_STATUS_LABEL: Record<CodexExecStatus, string> = {\n  failed: \"Ran\",\n  ran: \"Ran\",\n  running: \"Running\",\n};\n\nexport const CODEX_EXEC_STATUS_CLASS: Record<CodexExecStatus, string> = {\n  failed: \"text-codex-red\",\n  ran: \"text-codex-success\",\n  running: \"text-codex-muted animate-pulse motion-reduce:animate-none\",\n};\n\nexport const CODEX_SHELL_TOKEN_CLASS = {\n  argument: \"text-codex-arg\",\n  command: \"text-codex-command\",\n  flag: \"text-codex-flag\",\n  operator: \"text-codex-operator\",\n  space: \"\",\n  string: \"text-codex-green\",\n} as const;\n\nexport const CODEX_ROW_CLASS =\n  \"grid grid-cols-[2ch_minmax(0,1fr)] px-[1ch] font-codex text-[0.8125rem] leading-[1.3]\";\n",
      "type": "registry:lib",
      "target": "components/codex/constants/codex.ts"
    },
    {
      "path": "registry/notra/codex/types/codex.ts",
      "content": "import type { ComponentProps, ReactNode } from \"react\";\n\nimport type { Card } from \"@/components/ui/card\";\nimport type { Collapsible } from \"@/components/ui/collapsible\";\n\nexport type CodexExecStatus = \"ran\" | \"running\" | \"failed\";\n\nexport type CodexMessageAuthor = \"user\" | \"assistant\";\n\nexport type CodexShellTokenKind =\n  | \"command\"\n  | \"flag\"\n  | \"string\"\n  | \"operator\"\n  | \"argument\"\n  | \"space\";\n\nexport interface CodexShellToken {\n  kind: CodexShellTokenKind;\n  text: string;\n}\n\nexport type CodexExploredVerb = \"Read\" | \"Search\" | \"List\";\n\nexport interface CodexExploredItem {\n  id: string;\n  scope?: string;\n  target: string;\n  verb: CodexExploredVerb;\n}\n\nexport interface CodexTerminalProps extends Omit<\n  ComponentProps<typeof Card>,\n  \"title\"\n> {\n  footer?: ReactNode;\n  title?: ReactNode;\n}\n\nexport interface CodexHeaderProps extends ComponentProps<\"div\"> {\n  cwd?: string;\n  greeting?: ReactNode;\n  version?: string;\n}\n\nexport interface CodexMessageProps extends ComponentProps<\"div\"> {\n  from?: CodexMessageAuthor;\n}\n\nexport interface CodexReasoningProps extends ComponentProps<\"div\"> {\n  children?: ReactNode;\n}\n\nexport interface CodexExecProps extends Omit<\n  ComponentProps<typeof Collapsible>,\n  \"children\"\n> {\n  command: string;\n  output?: string;\n  previewLines?: number;\n  status?: CodexExecStatus;\n}\n\nexport interface CodexExploredProps extends Omit<\n  ComponentProps<typeof Collapsible>,\n  \"children\"\n> {\n  active?: boolean;\n  details?: CodexExploredItem[];\n  items: CodexExploredItem[];\n}\n\nexport interface CodexWorkingProps extends ComponentProps<\"div\"> {\n  elapsed?: string;\n  label?: string;\n}\n\nexport interface CodexInterruptedProps extends ComponentProps<\"div\"> {\n  children?: ReactNode;\n}\n\nexport interface CodexTableProps extends ComponentProps<\"table\"> {\n  codeColumns?: number[];\n  headers: string[];\n  rows: ReactNode[][];\n}\n\nexport interface CodexListProps extends ComponentProps<\"ul\"> {\n  items: ReactNode[];\n}\n\nexport interface CodexComposerProps extends Omit<\n  ComponentProps<\"input\">,\n  \"className\" | \"type\"\n> {\n  /** A turn is running: Enter does not send and Escape calls `onStop`. */\n  busy?: boolean;\n  className?: string;\n  context?: string;\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  task?: string;\n  warnings?: number;\n}\n\nexport interface CodexDemoExec {\n  command: string;\n  id: string;\n  output?: string;\n  status: CodexExecStatus;\n}\n\nexport interface CodexDemoHighlight {\n  id: string;\n  label: string;\n  text: string;\n}\n\nexport interface CodexDemoSession {\n  composer: {\n    effort: string;\n    model: string;\n    placeholder: string;\n    task: string;\n    warnings: number;\n  };\n  exec: {\n    command: string;\n    output: string;\n  };\n  explored: CodexExploredItem[];\n  exploredDetails: CodexExploredItem[];\n  followUp: string;\n  header: {\n    cwd: string;\n    version: string;\n  };\n  highlights: CodexDemoHighlight[];\n  intro: string;\n  reasoning: string;\n  summary: string;\n  table: {\n    headers: string[];\n    rows: string[][];\n  };\n  tableIntro: string;\n  title: string;\n  userMessage: string;\n}\n\nexport type CodexTurnStatus = \"working\" | \"streaming\" | \"done\" | \"interrupted\";\n\n/** A scripted answer the demo plays back when you send a prompt. */\nexport interface CodexReply {\n  /** Commands Codex runs before it answers, with their final status. */\n  execs: CodexDemoExec[];\n  /** The answer. Blank lines split paragraphs, backticks mark code. */\n  text: string;\n}\n\nexport interface CodexChatTurn {\n  execs: CodexDemoExec[];\n  id: string;\n  prompt: string;\n  reply: string;\n  status: CodexTurnStatus;\n}\n\nexport interface CodexChat {\n  busy: boolean;\n  /** Whole seconds since the running turn started. */\n  elapsed: number;\n  send: (text: string) => Promise<void>;\n  stop: () => void;\n  turns: CodexChatTurn[];\n}\n",
      "type": "registry:lib",
      "target": "components/codex/types/codex.ts"
    }
  ],
  "cssVars": {
    "theme": {
      "color-codex-bg": "var(--codex-bg)",
      "color-codex-titlebar": "var(--codex-titlebar)",
      "color-codex-dot": "var(--codex-dot)",
      "color-codex-title": "var(--codex-title)",
      "color-codex-shadow": "var(--codex-shadow)",
      "color-codex-ring": "var(--codex-ring)",
      "color-codex-fg": "var(--codex-fg)",
      "color-codex-dim": "var(--codex-dim)",
      "color-codex-muted": "var(--codex-muted)",
      "color-codex-rule": "var(--codex-rule)",
      "color-codex-band": "var(--codex-band)",
      "color-codex-composer": "var(--codex-composer)",
      "color-codex-placeholder": "var(--codex-placeholder)",
      "color-codex-cursor": "var(--codex-cursor)",
      "color-codex-blue": "var(--codex-blue)",
      "color-codex-command": "var(--codex-command)",
      "color-codex-arg": "var(--codex-arg)",
      "color-codex-operator": "var(--codex-operator)",
      "color-codex-flag": "var(--codex-flag)",
      "color-codex-green": "var(--codex-green)",
      "color-codex-success": "var(--codex-success)",
      "color-codex-yellow": "var(--codex-yellow)",
      "color-codex-amber": "var(--codex-amber)",
      "color-codex-red": "var(--codex-red)",
      "font-codex": "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace"
    },
    "light": {
      "codex-bg": "#ffffff",
      "codex-titlebar": "#ececec",
      "codex-dot": "#d4d4d4",
      "codex-title": "#00000073",
      "codex-shadow": "#00000026",
      "codex-ring": "#0000001a",
      "codex-fg": "#1a1a1a",
      "codex-dim": "#5c5c5c",
      "codex-muted": "#767676",
      "codex-rule": "#e0e0e0",
      "codex-band": "#f0f0f0",
      "codex-composer": "#f3f3f3",
      "codex-placeholder": "#8a8a8a",
      "codex-cursor": "#d6006a",
      "codex-blue": "#0a6fd6",
      "codex-command": "#1f5fbf",
      "codex-arg": "#3d4f8f",
      "codex-operator": "#0f7f70",
      "codex-flag": "#c0364d",
      "codex-green": "#1f8a3a",
      "codex-success": "#00852a",
      "codex-yellow": "#8f6200",
      "codex-amber": "#8a6420",
      "codex-red": "#d12f2f"
    },
    "dark": {
      "codex-bg": "#0f0f0f",
      "codex-titlebar": "#1c1c1c",
      "codex-dot": "#3a3a3a",
      "codex-title": "#ffffff66",
      "codex-shadow": "#00000033",
      "codex-ring": "#ffffff00",
      "codex-fg": "#f7f7f7",
      "codex-dim": "#909090",
      "codex-muted": "#777777",
      "codex-rule": "#343434",
      "codex-band": "#2d2d2d",
      "codex-composer": "#252525",
      "codex-placeholder": "#868686",
      "codex-cursor": "#fc0076",
      "codex-blue": "#3c9ffc",
      "codex-command": "#70abfa",
      "codex-arg": "#c3cff4",
      "codex-operator": "#6cdfcd",
      "codex-flag": "#f3909f",
      "codex-green": "#85df8d",
      "codex-success": "#00a12e",
      "codex-yellow": "#fcdb9b",
      "codex-amber": "#be9952",
      "codex-red": "#ff6b6b"
    }
  },
  "type": "registry:component"
}