zidane 4.1.7 → 4.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{index-bgh-k8Mv.d.ts → agent-CMIhYhDz.d.ts} +2032 -1993
- package/dist/agent-CMIhYhDz.d.ts.map +1 -0
- package/dist/chat.d.ts +7 -6
- package/dist/chat.d.ts.map +1 -1
- package/dist/chat.js +2 -2
- package/dist/contexts.d.ts +1 -1
- package/dist/{index-BB4kuRh3.d.ts → index-CXVvqTQj.d.ts} +1 -1
- package/dist/{index-BB4kuRh3.d.ts.map → index-CXVvqTQj.d.ts.map} +1 -1
- package/dist/{index-Ds5YpvfZ.d.ts → index-D6Dd6Kc0.d.ts} +3 -3
- package/dist/{index-Ds5YpvfZ.d.ts.map → index-D6Dd6Kc0.d.ts.map} +1 -1
- package/dist/{index-DRoG_udt.d.ts → index-DAaKyadO.d.ts} +2 -2
- package/dist/{index-DRoG_udt.d.ts.map → index-DAaKyadO.d.ts.map} +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +6 -6
- package/dist/{interpolate-CukJwP2G.js → interpolate-BydkV1eT.js} +3 -1
- package/dist/interpolate-BydkV1eT.js.map +1 -0
- package/dist/{mcp-8wClKY-3.js → mcp-Dw-fRPVk.js} +61 -65
- package/dist/mcp-Dw-fRPVk.js.map +1 -0
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.js +1 -1
- package/dist/{presets-BzkJDW1K.js → presets-4zCJzCYw.js} +2 -2
- package/dist/{presets-BzkJDW1K.js.map → presets-4zCJzCYw.js.map} +1 -1
- package/dist/presets.d.ts +1 -1
- package/dist/presets.js +1 -1
- package/dist/providers.d.ts +1 -1
- package/dist/session/sqlite.d.ts +13 -2
- package/dist/session/sqlite.d.ts.map +1 -1
- package/dist/session/sqlite.js +70 -27
- package/dist/session/sqlite.js.map +1 -1
- package/dist/{session-Cn68UASv.js → session-B1RN0uoi.js} +42 -30
- package/dist/{session-Cn68UASv.js.map → session-B1RN0uoi.js.map} +1 -1
- package/dist/session.d.ts +1 -1
- package/dist/session.js +1 -1
- package/dist/skills.d.ts +2 -2
- package/dist/skills.js +1 -1
- package/dist/{stats-BT9l57RS.js → stats-DZIsGqzu.js} +15 -5
- package/dist/stats-DZIsGqzu.js.map +1 -0
- package/dist/{theme-BlXO6yHe.d.ts → theme-Caf4AvTO.d.ts} +147 -13
- package/dist/theme-Caf4AvTO.d.ts.map +1 -0
- package/dist/{theme-context-MungM3SY.js → theme-context-DQM2lx4U.js} +212 -72
- package/dist/theme-context-DQM2lx4U.js.map +1 -0
- package/dist/{tools-C8kDot0H.js → tools-BdQENveS.js} +409 -312
- package/dist/tools-BdQENveS.js.map +1 -0
- package/dist/tools.d.ts +2 -2
- package/dist/tools.js +1 -1
- package/dist/tui.d.ts +64 -7
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +481 -143
- package/dist/tui.js.map +1 -1
- package/dist/types.d.ts +3 -3
- package/dist/types.js +1 -1
- package/package.json +6 -1
- package/dist/index-bgh-k8Mv.d.ts.map +0 -1
- package/dist/interpolate-CukJwP2G.js.map +0 -1
- package/dist/mcp-8wClKY-3.js.map +0 -1
- package/dist/stats-BT9l57RS.js.map +0 -1
- package/dist/theme-BlXO6yHe.d.ts.map +0 -1
- package/dist/theme-context-MungM3SY.js.map +0 -1
- package/dist/tools-C8kDot0H.js.map +0 -1
package/dist/tui.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tui.js","names":["EmptyState"],"sources":["../src/tui/theme.ts","../src/tui/components.tsx","../src/tui/modal.tsx","../src/tui/model-picker.tsx","../src/tui/screens.tsx","../src/tui/settings-modal.tsx","../src/tui/app.tsx","../src/tui/tree-sitter.ts","../src/tui/index.tsx"],"sourcesContent":["import type { Theme } from '../chat/theme'\nimport { RGBA, SyntaxStyle } from '@opentui/core'\nimport { useMemo } from 'react'\nimport { useTheme } from '../chat/theme-context'\n\n/**\n * Convert the renderer-agnostic `Theme.syntax` map (hex strings + plain\n * booleans) into an OpenTUI `SyntaxStyle`. Used both for the markdown\n * structural captures (`markup.heading`, `markup.bold`, …) and the\n * embedded Tree-sitter language tokens (`keyword`, `string`, `function`,\n * …) — OpenTUI's `<markdown>` re-uses the same `SyntaxStyle` for the\n * fenced-code renderable, so one table drives both surfaces.\n */\nexport function buildMdStyle(theme: Theme): SyntaxStyle {\n interface Style {\n fg?: RGBA\n bg?: RGBA\n bold?: boolean\n italic?: boolean\n underline?: boolean\n dim?: boolean\n }\n const styles: Record<string, Style> = {}\n for (const [token, style] of Object.entries(theme.syntax)) {\n const out: Style = {}\n if (style.fg)\n out.fg = RGBA.fromHex(style.fg)\n if (style.bg)\n out.bg = RGBA.fromHex(style.bg)\n if (style.bold)\n out.bold = true\n if (style.italic)\n out.italic = true\n if (style.underline)\n out.underline = true\n if (style.dim)\n out.dim = true\n styles[token] = out\n }\n return SyntaxStyle.fromStyles(styles)\n}\n\n/**\n * Active markdown / syntax-highlighting style, memoized per theme. Reading\n * this in a component subscribes it to theme changes — a `Settings.theme`\n * flip immediately re-renders the affected `<markdown>` instances.\n */\nexport function useMdStyle(): SyntaxStyle {\n const theme = useTheme()\n return useMemo(() => buildMdStyle(theme), [theme])\n}\n","import type { Picked, Settings, StreamEvent } from '../chat/types'\nimport { memo, useEffect, useMemo, useState } from 'react'\nimport { fmtTokens } from '../chat/format'\nimport { useColors } from '../chat/theme-context'\nimport { useMdStyle } from './theme'\n\n/**\n * Memoized so a flush that mutates only the trailing event doesn't force the\n * entire transcript to re-render. Each event holds a stable reference until\n * its content changes (we only ever recreate the streaming-markdown tail).\n *\n * The outer wrapper handles top-margin per kind (and per neighbor) so spacing\n * is the single source of truth for inter-event breathing room.\n */\nconst EventLine = memo(\n ({ event, previous, depthOffset = 0 }: {\n event: StreamEvent\n previous?: StreamEvent\n /**\n * Subtract from the event's depth before computing left indent. The\n * subagent box already provides one level of visual offset via its\n * border + padding, so events rendered inside it pass `depthOffset: 1`\n * to avoid double-indenting.\n */\n depthOffset?: number\n }) => (\n <box\n style={{\n marginTop: marginTopFor(event, previous),\n // Pin width to the scrollbox content area + lock the row against\n // shrinking. Without these, a streamed markdown growing taller can\n // make Yoga renegotiate widths for *every* sibling row, which is the\n // visible \"the text jiggles a column to the left, then back\" effect.\n alignSelf: 'stretch',\n flexShrink: 0,\n flexDirection: 'column',\n }}\n >\n <EventLineImpl event={event} depthOffset={depthOffset} />\n </box>\n ),\n)\n\n/**\n * `@opentui/react` extends `React.JSX.IntrinsicElements`, so `onSubmit` on `<input>`\n * gets intersected with the DOM `SubmitEvent` shape and demands an unhelpful overload.\n * The OpenTUI input runtime fires `(value: string) => void`; this helper isolates\n * the cast so each call site stays readable.\n */\nexport function onInputSubmit(handler: (value: string) => void): never {\n return handler as unknown as never\n}\n\n// ---------------------------------------------------------------------------\n// Footer — single status bar: hints on the left, provider info in the middle,\n// context-window indicator on the right.\n// ---------------------------------------------------------------------------\n\nexport interface Hint {\n key: string\n label: string\n}\n\nexport interface ContextUsage {\n used: number\n max: number\n}\n\nexport function Footer({\n hints,\n picked,\n context,\n}: {\n hints: Hint[]\n picked: Picked | null\n context: ContextUsage | null\n}) {\n const COLOR = useColors()\n return (\n <box style={{ flexDirection: 'row', height: 1, paddingLeft: 1, paddingRight: 1 }}>\n <text fg={COLOR.dim}>\n {hints.map((h, i) => (\n <span key={i}>\n {i > 0 && <span fg={COLOR.mute}> · </span>}\n <span fg={COLOR.warn}>{h.key}</span>\n <span fg={COLOR.dim}>{` ${h.label}`}</span>\n </span>\n ))}\n </text>\n {picked && <ProviderBadge picked={picked} />}\n <box style={{ flexGrow: 1 }} />\n {context && <ContextIndicator context={context} />}\n </box>\n )\n}\n\nfunction ProviderBadge({ picked }: { picked: Picked }) {\n const COLOR = useColors()\n const source = picked.provider.methods[0].source\n return (\n <text fg={COLOR.dim}>\n <span fg={COLOR.mute}> · </span>\n <span fg={COLOR.accent}>{picked.provider.label}</span>\n <span fg={COLOR.mute}> · </span>\n <span fg={COLOR.model}>{picked.model}</span>\n <span fg={COLOR.mute}> · </span>\n <span fg={source === 'oauth' ? COLOR.accent : COLOR.warn}>{source}</span>\n </text>\n )\n}\n\nfunction ContextIndicator({ context }: { context: ContextUsage }) {\n const COLOR = useColors()\n const ratio = context.max > 0 ? context.used / context.max : 0\n const pct = Math.round(ratio * 100)\n const color = ratio >= 0.85 ? COLOR.error : ratio >= 0.6 ? COLOR.warn : COLOR.dim\n return (\n <text fg={COLOR.dim}>\n <span fg={COLOR.mute}>ctx </span>\n <span fg={color}>{fmtTokens(context.used)}</span>\n <span fg={COLOR.mute}>{` / ${fmtTokens(context.max)} `}</span>\n <span fg={color}>{`(${pct}%)`}</span>\n </text>\n )\n}\n\n// ---------------------------------------------------------------------------\n// Spinner — animated braille used while a run is streaming.\n// ---------------------------------------------------------------------------\n\nconst SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']\nconst SPINNER_INTERVAL_MS = 80\n\nexport function Spinner({ label }: { label: string }) {\n const [frame, setFrame] = useState(0)\n const COLOR = useColors()\n\n useEffect(() => {\n const id = setInterval(() => setFrame(f => (f + 1) % SPINNER_FRAMES.length), SPINNER_INTERVAL_MS)\n return () => clearInterval(id)\n }, [])\n\n return (\n <text fg={COLOR.warn}>\n {SPINNER_FRAMES[frame]}\n <span fg={COLOR.dim}>{` ${label}`}</span>\n </text>\n )\n}\n\n// ---------------------------------------------------------------------------\n// Transcript — scrollbox with sticky-bottom and structured event rendering.\n// ---------------------------------------------------------------------------\n\nexport function Transcript({ events, settings }: { events: StreamEvent[], settings: Settings }) {\n const items = useMemo(() => partitionTranscript(events, settings), [events, settings])\n\n if (items.length === 0)\n return <EmptyState />\n\n return (\n <scrollbox\n // Never claim keyboard focus: the textarea is the chat's primary input,\n // so up/down/page-up/page-down should stay with it (or with the modal\n // when one is open). Mouse-wheel scrolling still works.\n focusable={false}\n style={{ flexGrow: 1, paddingLeft: 1, paddingRight: 1 }}\n stickyScroll\n stickyStart=\"bottom\"\n >\n {items.map((item, i) => (\n item.kind === 'event'\n ? <EventLine key={i} event={item.event} previous={item.previous} />\n : <SubagentBlock key={i} events={item.events} previous={item.previous} />\n ))}\n </scrollbox>\n )\n}\n\n/**\n * Per-event visibility — filters honor user toggles and the\n * `hideSubagentOutput` setting. When subagent output is hidden:\n * - Child-agent events are filtered down to the `spawn-start` /\n * `spawn-end` markers so the user still sees \"🌱 working… 🌳 done\".\n * - The parent's `tool-result` for `spawn` is hidden too. Its body\n * duplicates `spawn-end`'s stats line *and* the parent's next markdown\n * turn (\"Here's what the sub-agent found: …\"). Showing it again\n * produced an extra `┃ [sub-agent child-1] Completed …` block that\n * the user just wanted gone.\n *\n * Exported so the visibility matrix can be unit-tested without rendering.\n */\nexport function isVisible(event: StreamEvent, settings: Settings): boolean {\n if (settings.hideSubagentOutput) {\n if (isChild(event))\n return event.kind === 'spawn-start' || event.kind === 'spawn-end'\n if (event.kind === 'tool-result' && event.tool === 'spawn')\n return false\n }\n switch (event.kind) {\n case 'thinking': return settings.showThinking\n case 'tool': return settings.showToolCalls\n case 'tool-result': return settings.showToolResults\n default: return true\n }\n}\n\n/**\n * Output of `partitionTranscript`. Single events render as a normal\n * `EventLine`; child-event runs render as a bordered subagent box.\n */\ntype TranscriptItem\n = | { kind: 'event', event: StreamEvent, previous?: StreamEvent }\n | { kind: 'child-run', events: StreamEvent[], previous?: StreamEvent }\n\n/**\n * Walk the visible-event list once and group consecutive child events\n * (`depth > 0`) into runs so we can wrap each run in a single bordered\n * subagent box.\n *\n * When `hideSubagentOutput` is on, `isVisible` already filters most child\n * events out; the surviving `spawn-start` / `spawn-end` markers render as\n * plain entries (no boxing) — there's nothing meaningful to box.\n */\nfunction partitionTranscript(events: StreamEvent[], settings: Settings): TranscriptItem[] {\n const visible = events.filter(e => isVisible(e, settings))\n if (visible.length === 0)\n return []\n\n // Hide-mode: spawn-start/end are the only child events left. Don't box\n // them — they're already standalone \"subagent N is working/done\" lines.\n if (settings.hideSubagentOutput) {\n return visible.map((event, i) => ({ kind: 'event', event, previous: visible[i - 1] }))\n }\n\n const items: TranscriptItem[] = []\n let run: StreamEvent[] = []\n let runPrevious: StreamEvent | undefined\n\n const flush = () => {\n if (run.length > 0) {\n items.push({ kind: 'child-run', events: run, previous: runPrevious })\n run = []\n runPrevious = undefined\n }\n }\n\n for (let i = 0; i < visible.length; i++) {\n const event = visible[i]\n if (isChild(event)) {\n if (run.length === 0)\n runPrevious = visible[i - 1]\n run.push(event)\n }\n else {\n flush()\n items.push({ kind: 'event', event, previous: visible[i - 1] })\n }\n }\n flush()\n return items\n}\n\n/**\n * Bordered container for one run of subagent events. The box's border +\n * left padding give the visual \"this is a subagent\" affordance, so events\n * inside render with `depthOffset: 1` — a direct child of the parent\n * (depth 1) sits flush against the box's inner padding rather than being\n * indented twice. Grandchildren (depth ≥ 2) still indent further, so\n * nested subagents remain visually distinct.\n */\nfunction SubagentBlock({ events, previous }: { events: StreamEvent[], previous?: StreamEvent }) {\n const COLOR = useColors()\n const childIds = useMemo(() => {\n const set = new Set<string>()\n for (const e of events) {\n if (e.childId)\n set.add(e.childId)\n }\n return Array.from(set)\n }, [events])\n\n const title = childIds.length === 0\n ? ' subagent '\n : childIds.length === 1\n ? ` ${childIds[0]} `\n : ` subagents · ${childIds.join(', ')} `\n\n // Keep the same vertical breathing room a markdown/spawn-start event would\n // get standalone — without this the box looks glued to the parent's tool\n // call above it.\n const marginTop = previous ? 1 : 0\n\n return (\n <box\n title={title}\n style={{\n border: true,\n borderColor: COLOR.mute,\n paddingLeft: 1,\n paddingRight: 1,\n paddingTop: 0,\n paddingBottom: 0,\n marginTop,\n flexDirection: 'column',\n flexShrink: 0,\n alignSelf: 'stretch',\n }}\n >\n {events.map((evt, i) => (\n <EventLine key={i} event={evt} previous={events[i - 1]} depthOffset={1} />\n ))}\n </box>\n )\n}\n\nfunction EmptyState() {\n const COLOR = useColors()\n return (\n <box style={{ flexGrow: 1, alignItems: 'center', justifyContent: 'center' }}>\n <text fg={COLOR.mute}>no messages yet — type below to start</text>\n </box>\n )\n}\n\n// ---------------------------------------------------------------------------\n// EventLine — dispatches per kind. Subagent events render via indented dim\n// blocks; the `depth` field drives the left indent.\n// ---------------------------------------------------------------------------\n\n/** Left-pad applied per depth level (in columns). */\nconst INDENT_PER_DEPTH = 2\n\nfunction indentFor(depth: number | undefined): number {\n return depth && depth > 0 ? depth * INDENT_PER_DEPTH : 0\n}\n\nfunction isChild(event: StreamEvent): boolean {\n return (event.depth ?? 0) > 0\n}\n\n/**\n * Shared row geometry for every transcript event.\n *\n * `alignSelf: 'stretch'` + `flexShrink: 0` together pin each row to the\n * scrollbox's content width and prevent flex re-negotiation when neighboring\n * rows grow or shrink (streaming markdown, late-arriving tool results, etc.).\n * Without this, Yoga is free to re-compute widths every render and the\n * visible text appears to \"wiggle\" between columns as the stream advances.\n */\nfunction rowStyle(paddingLeft: number) {\n return {\n paddingLeft,\n flexDirection: 'column' as const,\n flexShrink: 0,\n alignSelf: 'stretch' as const,\n }\n}\n\n/**\n * Default top-margin per kind. Spacing intent:\n * - `info` / `markdown` / `tool` / `error` / `spawn-start` open a new block\n * so they each get one row of breathing room above.\n * - `thinking` / `tool-result` / `spawn-end` continue the previous block\n * and stay flush.\n *\n * Context-aware overrides live in `marginTopFor` — e.g. consecutive tool\n * round-trips collapse to a tight list regardless of whether outputs are shown.\n */\nconst MARGIN_TOP: Record<StreamEvent['kind'], number> = {\n 'separator': 0,\n 'info': 1,\n 'thinking': 0,\n 'tool': 1,\n 'tool-result': 0,\n 'error': 1,\n 'markdown': 1,\n 'spawn-start': 1,\n 'spawn-end': 0,\n}\n\nconst TOOL_KINDS: ReadonlySet<StreamEvent['kind']> = new Set(['tool', 'tool-result'])\n\n/**\n * Resolve the top margin for an event given the one rendered just before it.\n *\n * Context-aware rules:\n *\n * - A `tool` / `tool-result` event right after another `tool` / `tool-result`\n * collapses to a tight list — call→result pairs and back-to-back calls\n * read as one logical block.\n * - A parent-level event (`depth === 0`) right after a subagent event\n * (`depth > 0`) collapses too. The subagent's `🌳` end marker (and, in\n * show mode, the subagent box's bottom border) already provides the\n * separation; adding the event's default `marginTop` on top would\n * produce the visible \"line jump\" between a subagent's outcome and the\n * parent's follow-up. Either form of marker is enough — we don't want\n * both.\n *\n * Exported so the spacing matrix can be unit-tested without rendering.\n */\nexport function marginTopFor(event: StreamEvent, previous: StreamEvent | undefined): number {\n if (TOOL_KINDS.has(event.kind) && previous && TOOL_KINDS.has(previous.kind))\n return 0\n const eventDepth = event.depth ?? 0\n const previousDepth = previous?.depth ?? 0\n if (eventDepth === 0 && previousDepth > 0)\n return 0\n return MARGIN_TOP[event.kind] ?? 0\n}\n\nfunction EventLineImpl({ event, depthOffset = 0 }: { event: StreamEvent, depthOffset?: number }) {\n const COLOR = useColors()\n const safeText = event.text === '' ? ' ' : event.text\n const effectiveDepth = Math.max(0, (event.depth ?? 0) - depthOffset)\n const row = rowStyle(indentFor(effectiveDepth))\n\n // Subagent text is dimmed across the board so the visual hierarchy is\n // obvious even before reading the indent.\n const child = isChild(event)\n\n switch (event.kind) {\n case 'separator':\n return <text> </text>\n case 'info':\n return <UserPromptBlock text={safeText} />\n case 'thinking':\n return (\n <box style={row}>\n <text fg={COLOR.dim}>{safeText}</text>\n </box>\n )\n case 'tool':\n return (\n <box style={row}>\n <text fg={child ? COLOR.dim : COLOR.model}>\n <span fg={COLOR.mute}>↳ </span>\n {safeText}\n </text>\n </box>\n )\n case 'tool-result':\n return <ToolResultBlock text={event.text} indent={row.paddingLeft} />\n case 'error':\n return (\n <box style={row}>\n <text fg={COLOR.error}>\n <span fg={COLOR.error}>✗ </span>\n {safeText}\n </text>\n </box>\n )\n case 'markdown':\n return (\n <box style={row}>\n <MarkdownBlock text={event.text} streaming={event.streaming ?? false} dim={child} />\n </box>\n )\n case 'spawn-start':\n return (\n <box style={row}>\n <text fg={COLOR.dim}>\n <span fg={COLOR.accent}>🌱 </span>\n <span fg={COLOR.dim}>{`[${event.childId ?? 'child'}] `}</span>\n <span fg={COLOR.dim}>{safeText}</span>\n </text>\n </box>\n )\n case 'spawn-end':\n // Use a 2-cell emoji (like the 🌱 sprout on spawn-start) so the `[…]`\n // label lands in the same column on every row of a subagent's life\n // cycle — the previous `✓` glyph is 1 cell wide and shifted the\n // label one column left of the start marker.\n return (\n <box style={row}>\n <text fg={COLOR.dim}>\n <span fg={COLOR.accent}>🌳 </span>\n <span fg={COLOR.dim}>{`[${event.childId ?? 'child'}] `}</span>\n <span fg={COLOR.mute}>{safeText}</span>\n </text>\n </box>\n )\n default:\n return <text>{safeText}</text>\n }\n}\n\n/** User prompt — bordered to rhyme with the prompt input box below. */\nfunction UserPromptBlock({ text }: { text: string }) {\n const COLOR = useColors()\n return (\n <box\n style={{\n border: true,\n borderColor: COLOR.borderActive,\n paddingLeft: 1,\n paddingRight: 1,\n }}\n >\n <text fg={COLOR.brand}>{text}</text>\n </box>\n )\n}\n\n/**\n * Markdown block. Renders either live-streaming markdown (with `streaming`\n * on, while deltas are still appending) or finalized markdown (after\n * `turn:after`, or every entry on a reloaded transcript).\n *\n * `internalBlockMode` is the load-bearing knob for layout: the OpenTUI\n * default (`\"coalesced\"`) fuses adjacent top-level blocks into one render\n * block, which is the right tradeoff for finalized markdown — fewer flex\n * children, fewer layout passes, the parser already knows the final shape.\n * During streaming, that same coalescing makes earlier paragraphs visually\n * re-flow on every token, so we switch to `\"top-level\"` (each block its\n * own renderable, only the trailing one is unstable).\n *\n * `internalBlockMode` is set only at construction by OpenTUI — there's no\n * setter — so a `<MarkdownBlock>` keeps whichever mode it was born with.\n * Live blocks start `streaming=true` → top-level; reloaded blocks start\n * `streaming=false` → coalesced. Each variant stays optimal for its\n * lifecycle.\n *\n * Note: we don't pre-process unclosed delimiters. OpenTUI's markdown\n * parser already renders partial input reasonably during streaming (the\n * trailing block reflows as tokens close), and the simplicity is worth\n * accepting a brief literal `**` before the closer arrives. Persisted\n * reloads come from completed assistant turns whose markdown is closed.\n */\nfunction MarkdownBlock({ text, streaming, dim }: { text: string, streaming: boolean, dim: boolean }) {\n const COLOR = useColors()\n const mdStyle = useMdStyle()\n return (\n <markdown\n content={text}\n syntaxStyle={mdStyle}\n streaming={streaming}\n internalBlockMode={streaming ? 'top-level' : 'coalesced'}\n fg={dim ? COLOR.dim : undefined}\n />\n )\n}\n\n// ---------------------------------------------------------------------------\n// Tool result — left-bar block, truncated. Caps at TOOL_RESULT_MAX_LINES so a\n// 200-line file doesn't drown the transcript.\n// ---------------------------------------------------------------------------\n\nconst TOOL_RESULT_MAX_LINES = 6\n\nfunction ToolResultBlock({ text, indent }: { text: string, indent: number }) {\n const COLOR = useColors()\n const lines = text.split('\\n')\n const visible = lines.slice(0, TOOL_RESULT_MAX_LINES)\n const omitted = Math.max(0, lines.length - TOOL_RESULT_MAX_LINES)\n\n return (\n <box style={{ paddingLeft: indent, flexDirection: 'column' }}>\n {visible.map((line, i) => (\n <text key={i} fg={COLOR.mute}>\n <span fg={COLOR.borderActive}>┃ </span>\n {line || ' '}\n </text>\n ))}\n {omitted > 0 && (\n <text fg={COLOR.mute}>\n <span fg={COLOR.borderActive}>┃ </span>\n {`… ${omitted} more line${omitted === 1 ? '' : 's'}`}\n </text>\n )}\n </box>\n )\n}\n","import type { ReactNode } from 'react'\nimport { useKeyboard, useTerminalDimensions } from '@opentui/react'\nimport { createContext, useContext, useMemo, useState } from 'react'\nimport { useColors, useSurfaces } from '../chat/theme-context'\n\n// ---------------------------------------------------------------------------\n// Modal layer\n//\n// A single, app-wide overlay slot. `ModalRoot` renders the active node on top\n// of its children via OpenTUI's `position: 'absolute'` so the host UI stays\n// mounted underneath (state preserved, no remount on close).\n//\n// Components anywhere in the tree call `useModal().open(...)` to push a node\n// and `.close()` to dismiss. Escape is captured by `Modal` itself when it's\n// open, so the App's top-level keyboard handler doesn't fight it.\n// ---------------------------------------------------------------------------\n\ninterface ModalApi {\n open: (node: ReactNode) => void\n close: () => void\n isOpen: boolean\n}\n\nconst ModalContext = createContext<ModalApi | null>(null)\n\nexport function ModalRoot({ children }: { children: ReactNode }) {\n const [active, setActive] = useState<ReactNode | null>(null)\n\n const api = useMemo<ModalApi>(() => ({\n open: node => setActive(node),\n close: () => setActive(null),\n get isOpen() { return active !== null },\n }), [active])\n\n return (\n <ModalContext.Provider value={api}>\n <box style={{ flexDirection: 'column', flexGrow: 1 }}>\n {children}\n </box>\n {active && (\n // Transparent backdrop — host UI remains visible behind. The modal is\n // opaque (see <Modal/>) and sits centered; the dimming the eye perceives\n // comes from the modal's solid panel against the surrounding content,\n // not from a backdrop fill (OpenTUI cells have no alpha to blend with).\n <box\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n alignItems: 'center',\n justifyContent: 'center',\n zIndex: 100,\n }}\n >\n {active}\n </box>\n )}\n </ModalContext.Provider>\n )\n}\n\nexport function useModal(): ModalApi {\n const ctx = useContext(ModalContext)\n if (!ctx)\n throw new Error('useModal must be used inside <ModalRoot>')\n return ctx\n}\n\n/**\n * Focus computed against the modal layer.\n *\n * Pass a component's preferred focus state and this returns `false` whenever a\n * modal is open — so focused inputs (textarea, selects) release their focus and\n * stop intercepting keys behind the overlay. Pair with `focusable={false}` on\n * \"passive\" focusables (scrollbox) so the renderer doesn't cycle focus into\n * them when the primary input blurs.\n */\nexport function useModalAwareFocus(preferred: boolean = true): boolean {\n const { isOpen } = useModal()\n return preferred && !isOpen\n}\n\n// ---------------------------------------------------------------------------\n// Modal — bordered content panel with built-in esc-to-close keyboard handling.\n//\n// Consumers pass their own content (forms, selects, lists). The modal owns:\n// - border + title + minimal padding\n// - sizing constraints (width caps at a comfortable column width)\n// - escape key → onClose\n// ---------------------------------------------------------------------------\n\nexport interface ModalProps {\n title?: string\n /** Called when the user presses esc. Defaults to `useModal().close()` if available. */\n onClose?: () => void\n children: ReactNode\n /** Preferred width in columns. Modal grows to this when the terminal allows. */\n maxWidth?: number\n /** Floor on the width, so content stays minimally legible. */\n minWidth?: number\n /** Columns of breathing room kept on each side of the modal. */\n horizontalMargin?: number\n}\n\n/**\n * Responsive modal — picks a width based on the live terminal size.\n *\n * - On a wide terminal, the modal grows to `maxWidth` so descriptions sit on\n * one line and don't wrap.\n * - On a narrow terminal, the modal shrinks down to `minWidth`, keeping a\n * small horizontal margin from the screen edges. Text inside wraps naturally.\n *\n * Uses `useTerminalDimensions()` so it reflows on `SIGWINCH` without remount.\n */\nexport function Modal({\n title,\n onClose,\n children,\n maxWidth = 92,\n minWidth = 44,\n horizontalMargin = 4,\n}: ModalProps) {\n const ctx = useContext(ModalContext)\n const dismiss = onClose ?? ctx?.close\n const COLOR = useColors()\n const SURFACE = useSurfaces()\n\n useKeyboard((key) => {\n if (key.name === 'escape')\n dismiss?.()\n })\n\n const { width: termWidth } = useTerminalDimensions()\n const width = Math.max(minWidth, Math.min(maxWidth, termWidth - horizontalMargin * 2))\n\n return (\n <box\n title={title ? ` ${title} ` : undefined}\n style={{\n border: true,\n borderColor: COLOR.borderActive,\n backgroundColor: SURFACE.modal,\n paddingTop: 1,\n paddingBottom: 1,\n paddingLeft: 2,\n paddingRight: 2,\n width,\n flexDirection: 'column',\n gap: 1,\n }}\n >\n {children}\n </box>\n )\n}\n","import type { ModelInfo } from '../chat/providers'\nimport { useMemo } from 'react'\nimport { fmtTokens } from '../chat/format'\nimport { useColors, useSelectStyle } from '../chat/theme-context'\nimport { Modal } from './modal'\n\n/** Cap the visible scroll window so a 30-model list doesn't push the modal off-screen. */\nconst VISIBLE_ROW_CAP = 12\n\n/**\n * Modal that lists the available models for the current provider and lets\n * the user pick one. Options come from the active `ProviderDescriptor` —\n * either its declared `models` list or, when absent, pi-ai's built-in\n * registry looked up via `piProviderId`.\n *\n * Each row shows: `● selected · name (ctx N · reasoning · vision)`.\n */\nexport function ModelPickerModal({\n models,\n currentModelId,\n onPick,\n}: {\n models: readonly ModelInfo[]\n currentModelId: string\n onPick: (modelId: string) => void\n}) {\n const COLOR = useColors()\n const SELECT_THEME = useSelectStyle()\n // Hooks must always run in the same order — keep them above any conditional\n // return. The \"no models\" branch becomes JSX below.\n const initialIndex = useMemo(\n () => models.findIndex(m => m.id === currentModelId),\n [models, currentModelId],\n )\n\n const options = useMemo(\n () => models.map(m => ({\n name: `${m.id === currentModelId ? '● ' : ' '}${m.name ?? m.id}`,\n description: describeModel(m),\n value: m.id,\n })),\n [models, currentModelId],\n )\n\n if (models.length === 0)\n return <EmptyState />\n\n const visibleRows = Math.min(options.length, VISIBLE_ROW_CAP)\n const currentMissing = initialIndex < 0\n // `<select>` requires a non-negative index; clamp here and surface the\n // mismatch via a banner so the user knows their previously-saved model isn't\n // available in the current registry.\n const safeIndex = currentMissing ? 0 : initialIndex\n\n return (\n <Modal title=\"select model\">\n {currentMissing && (\n <text fg={COLOR.warn}>\n {`Current model \"${currentModelId}\" is not in this registry — pick one below to switch.`}\n </text>\n )}\n <select\n {...SELECT_THEME}\n focused\n options={options}\n wrapSelection\n selectedIndex={safeIndex}\n showScrollIndicator={options.length > visibleRows}\n style={{ height: visibleRows }}\n onSelect={(_idx, option) => {\n if (option)\n onPick(option.value as string)\n }}\n />\n <text fg={COLOR.mute}>\n <span fg={COLOR.warn}>↑↓</span>\n {' navigate · '}\n <span fg={COLOR.warn}>↵</span>\n {' select · '}\n <span fg={COLOR.warn}>esc</span>\n {' close'}\n </text>\n </Modal>\n )\n}\n\nfunction EmptyState() {\n const COLOR = useColors()\n return (\n <Modal title=\"select model\">\n <text fg={COLOR.dim}>No models available for this provider.</text>\n <text fg={COLOR.mute}>\n Set\n <span fg={COLOR.model}> models </span>\n on the provider descriptor (or a\n <span fg={COLOR.model}> piProviderId </span>\n that pi-ai recognizes) to populate this list.\n </text>\n </Modal>\n )\n}\n\n/** \"ctx 200k · reasoning · vision\" — compact per-model description. */\nfunction describeModel(m: ModelInfo): string {\n const parts: string[] = [`ctx ${fmtTokens(m.contextWindow)}`]\n if (m.reasoning)\n parts.push('reasoning')\n if (m.input?.includes('image'))\n parts.push('vision')\n return parts.join(' · ')\n}\n","import type { InputRenderable, KeyEvent, TextareaRenderable } from '@opentui/core'\nimport type { ReactNode } from 'react'\nimport type { ProviderAuth } from '../chat/auth'\nimport type { ProviderDescriptor } from '../chat/providers'\nimport type { ApprovalDecision, ApprovalRequest } from '../chat/safe-mode-context'\nimport type { SessionMeta, Settings, StreamEvent } from '../chat/types'\nimport { defaultTextareaKeyBindings } from '@opentui/core'\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport { detectAuth } from '../chat/auth'\nimport { useConfig } from '../chat/config-context'\nimport { setProviderCredential } from '../chat/credentials'\nimport { ageString, shortId } from '../chat/format'\nimport { runOAuthLogin, supportsOAuth } from '../chat/oauth'\nimport { suggestSafelistEntry } from '../chat/safe-mode'\nimport { useColors, useSelectStyle } from '../chat/theme-context'\nimport { Spinner, Transcript } from './components'\nimport { useModalAwareFocus } from './modal'\n\n/**\n * Build a key-binding set for the prompt textarea / API-key input. Strips the\n * default `return` action and reinstalls it with our preferred meaning, so the\n * binding wins regardless of modifier state. Pass `allowShiftReturnNewline`\n * to enable `shift+enter` → newline (multi-line input).\n */\nfunction makeSubmitBindings(allowShiftReturnNewline: boolean) {\n const base = defaultTextareaKeyBindings.filter(b => b.name !== 'return')\n return allowShiftReturnNewline\n ? [...base, { name: 'return', action: 'submit' as const }, { name: 'return', shift: true, action: 'newline' as const }]\n : [...base, { name: 'return', action: 'submit' as const }]\n}\n\nconst TEXTAREA_BINDINGS = makeSubmitBindings(true)\nconst API_KEY_INPUT_BINDINGS = makeSubmitBindings(false)\n\n/**\n * Look up a `{ key }` item by the value of a `<select>` option. Used by every\n * screen that mixes keyed-entry rows with sentinel \"+ new\" / \"← back\" rows —\n * sentinel handling stays explicit at the call site, this helper just trims\n * the boilerplate `.find(i => i.key === ...)` typing.\n */\nfunction findByKey<T extends { key: string }>(items: readonly T[], value: unknown): T | undefined {\n return typeof value === 'string' ? items.find(i => i.key === value) : undefined\n}\n\n// ---------------------------------------------------------------------------\n// AuthScreen — first-run provider picker.\n// ---------------------------------------------------------------------------\n\n/**\n * Sentinel value used by the picker's \"+ add / re-configure\" option. Lives\n * outside the provider key namespace (key strings are at least one char, no\n * leading `__`) so we can't collide with a real registry entry.\n */\nconst WIZARD_OPTION_VALUE = '__wizard__'\n\nexport function AuthScreen({ onPick }: { onPick: (p: ProviderAuth) => void }) {\n const config = useConfig()\n const { providers: registry } = config\n const focused = useModalAwareFocus()\n const COLOR = useColors()\n const SELECT_THEME = useSelectStyle()\n\n // `providers` is state, not a memo, so the wizard can imperatively\n // refresh it after writing credentials without us reaching for `useMemo`\n // dep tricks. The effect below seeds it on mount + whenever the registry\n // changes; `refresh` does the wizard-triggered re-detect.\n const [providers, setProviders] = useState<ProviderAuth[]>([])\n const refresh = useCallback(\n () => setProviders(detectAuth(config.paths.dir, registry)),\n [config.paths.dir, registry],\n )\n useEffect(() => { refresh() }, [refresh])\n\n // Explicit \"show the wizard\" flag — set when the user picks the\n // `+ add or re-configure` option, cleared when they save or cancel back\n // to the picker. Without it, the picker would be sticky once any provider\n // had credentials.\n const [forceWizard, setForceWizard] = useState(false)\n\n const available = useMemo(() => providers.filter(p => p.available), [providers])\n\n const onWizardDone = useCallback(() => {\n setForceWizard(false)\n refresh()\n }, [refresh])\n\n if (available.length === 0 || forceWizard) {\n // \"← back\" only makes sense when we got here from the picker (forceWizard)\n // AND there's still something to go back to. The `&&` guards against the\n // edge case where credentials disappear between renders.\n const canCancel = forceWizard && available.length > 0\n return (\n <SetupWizard\n registry={registry}\n dataDir={config.paths.dir}\n onConfigured={onWizardDone}\n onCancel={canCancel ? () => setForceWizard(false) : undefined}\n />\n )\n }\n\n const options = [\n ...available.map(p => ({\n name: p.label,\n description: p.methods.map(m => m.detail).join(' · '),\n value: p.key,\n })),\n {\n name: '+ add or re-configure a provider',\n description: 'launch the setup wizard',\n value: WIZARD_OPTION_VALUE,\n },\n ]\n\n return (\n <box\n title=\" pick a provider \"\n style={{\n border: true,\n borderColor: COLOR.border,\n padding: 1,\n flexDirection: 'column',\n flexGrow: 1,\n }}\n >\n <select\n {...SELECT_THEME}\n focused={focused}\n options={options}\n wrapSelection\n onSelect={(_idx, option) => {\n if (!option)\n return\n if (option.value === WIZARD_OPTION_VALUE) {\n setForceWizard(true)\n return\n }\n const provider = findByKey(available, option.value)\n if (provider)\n onPick(provider)\n }}\n style={{ flexGrow: 1 }}\n />\n </box>\n )\n}\n\n// ---------------------------------------------------------------------------\n// SetupWizard — first-run credential setup. Three steps:\n// 1. Pick provider\n// 2. Pick auth method (apikey · OAuth where the descriptor supports it)\n// 3a. Enter API key → save to credentials.json → refresh AuthScreen\n// 3b. Open browser → OAuth callback server → save tokens → refresh\n//\n// Everything is driven by the host's {@link ProviderDescriptor}s — no\n// hardcoded provider metadata lives here.\n// ---------------------------------------------------------------------------\n\ntype WizardStep\n = | { kind: 'pick-provider' }\n | { kind: 'pick-method', descriptor: ProviderDescriptor }\n | { kind: 'enter-apikey', descriptor: ProviderDescriptor }\n | { kind: 'oauth-running', descriptor: ProviderDescriptor }\n\nfunction SetupWizard({\n registry,\n dataDir,\n onConfigured,\n onCancel,\n}: {\n registry: Readonly<Record<string, ProviderDescriptor>>\n dataDir: string\n onConfigured: () => void\n /**\n * Available only when the wizard was opened from the picker\n * (\"+ add or re-configure\"). On first launch (no providers yet) it's\n * undefined and the wizard has no cancel affordance — the user must set\n * up a provider to proceed.\n */\n onCancel?: () => void\n}) {\n const [step, setStep] = useState<WizardStep>({ kind: 'pick-provider' })\n const [error, setError] = useState<string | null>(null)\n\n const descriptors = useMemo(() => Object.values(registry), [registry])\n\n const onPickProvider = useCallback((descriptor: ProviderDescriptor) => {\n setError(null)\n setStep({ kind: 'pick-method', descriptor })\n }, [])\n\n const onPickMethod = useCallback((descriptor: ProviderDescriptor, method: 'apikey' | 'oauth') => {\n setError(null)\n if (method === 'apikey') {\n setStep({ kind: 'enter-apikey', descriptor })\n }\n else {\n setStep({ kind: 'oauth-running', descriptor })\n }\n }, [])\n\n const onApiKeySubmit = useCallback((descriptor: ProviderDescriptor, value: string) => {\n const trimmed = value.trim()\n if (!trimmed) {\n setError('API key cannot be empty.')\n return\n }\n try {\n setProviderCredential(dataDir, descriptor, { kind: 'apikey', value: trimmed })\n // Also expose via env so providers created later in this process pick it\n // up. Skip for descriptors with no env-var convention (custom providers\n // that resolve credentials some other way).\n if (descriptor.envKey)\n process.env[descriptor.envKey] = trimmed\n onConfigured()\n }\n catch (err) {\n setError(err instanceof Error ? err.message : String(err))\n }\n }, [dataDir, onConfigured])\n\n if (descriptors.length === 0)\n return <EmptyRegistryNotice />\n\n if (step.kind === 'pick-provider') {\n return (\n <PickProviderStep\n descriptors={descriptors}\n error={error}\n onPick={onPickProvider}\n onCancel={onCancel}\n />\n )\n }\n\n if (step.kind === 'pick-method')\n return <PickMethodStep descriptor={step.descriptor} error={error} onPick={onPickMethod} />\n\n if (step.kind === 'enter-apikey')\n return <EnterApiKeyStep descriptor={step.descriptor} error={error} onSubmit={onApiKeySubmit} />\n\n return (\n <OAuthRunningStep\n descriptor={step.descriptor}\n dataDir={dataDir}\n onSuccess={onConfigured}\n onError={(msg) => {\n setError(msg)\n setStep({ kind: 'pick-method', descriptor: step.descriptor })\n }}\n />\n )\n}\n\n/**\n * Shared wrapper for every wizard step — same border + padding + flex layout\n * with a customizable title and accent color. Footnote slot at the bottom for\n * an error banner.\n */\nfunction WizardPanel({\n title,\n accent,\n error,\n children,\n}: {\n title: string\n accent?: string\n error?: string | null\n children: ReactNode\n}) {\n const COLOR = useColors()\n return (\n <box\n title={title}\n style={{\n border: true,\n borderColor: accent ?? COLOR.border,\n padding: 1,\n gap: 1,\n flexDirection: 'column',\n flexGrow: 1,\n }}\n >\n {children}\n {error && <text fg={COLOR.error}>{error}</text>}\n </box>\n )\n}\n\n/** \"esc to exit\" footer hint shared by every wizard step that doesn't offer a \"← back\" affordance. */\nfunction WizardEscHint() {\n const COLOR = useColors()\n return <text fg={COLOR.dim}>esc to exit</text>\n}\n\nfunction EmptyRegistryNotice() {\n const COLOR = useColors()\n return (\n <WizardPanel title=\" no providers configured \" accent={COLOR.error}>\n <text fg={COLOR.error}>This TUI has no providers registered.</text>\n <text fg={COLOR.dim}>\n Pass providers via\n <span fg={COLOR.model}>{' runTui({ providers }) '}</span>\n or use the built-ins via\n <span fg={COLOR.model}>{' BUILTIN_PROVIDERS '}</span>\n .\n </text>\n </WizardPanel>\n )\n}\n\n/** Sentinel option value used for the wizard's \"← back to picker\" entry. */\nconst WIZARD_BACK_VALUE = '__back__'\n\nfunction PickProviderStep({\n descriptors,\n error,\n onPick,\n onCancel,\n}: {\n descriptors: readonly ProviderDescriptor[]\n error: string | null\n onPick: (descriptor: ProviderDescriptor) => void\n /** When set, adds a \"← back\" option that calls this to bail out without saving. */\n onCancel?: () => void\n}) {\n const focused = useModalAwareFocus()\n const COLOR = useColors()\n const SELECT_THEME = useSelectStyle()\n const options = [\n ...descriptors.map((d) => {\n const methods: string[] = supportsOAuth(d) ? ['API key', 'OAuth'] : ['API key']\n return { name: d.label, description: methods.join(' · '), value: d.key }\n }),\n ...(onCancel\n ? [{ name: '← back', description: 'return to the provider list', value: WIZARD_BACK_VALUE }]\n : []),\n ]\n\n // Different copy + title for the two entry paths: first-launch users need\n // the \"credentials live here\" hint; re-config users already know how the\n // TUI works and just want to pick a provider.\n const title = onCancel\n ? ' add or re-configure a provider '\n : ' welcome to zidane · pick a provider '\n\n return (\n <WizardPanel title={title} error={error}>\n {!onCancel && (\n <text fg={COLOR.dim}>\n No provider credentials yet. Pick a provider to configure — keys are stored in\n <span fg={COLOR.model}>{' ~/.zidane/credentials.json '}</span>\n (owner-only).\n </text>\n )}\n <select\n {...SELECT_THEME}\n focused={focused}\n options={options}\n wrapSelection\n onSelect={(_idx, option) => {\n if (!option)\n return\n if (option.value === WIZARD_BACK_VALUE) {\n onCancel?.()\n return\n }\n const descriptor = findByKey(descriptors, option.value)\n if (descriptor)\n onPick(descriptor)\n }}\n style={{ flexGrow: 1 }}\n />\n </WizardPanel>\n )\n}\n\nfunction PickMethodStep({\n descriptor,\n error,\n onPick,\n}: {\n descriptor: ProviderDescriptor\n error: string | null\n onPick: (descriptor: ProviderDescriptor, method: 'apikey' | 'oauth') => void\n}) {\n const focused = useModalAwareFocus()\n const SELECT_THEME = useSelectStyle()\n\n const options = useMemo(() => {\n interface MethodOption { name: string, description: string, value: 'apikey' | 'oauth' }\n const items: MethodOption[] = [\n { name: 'API key', description: `paste your ${descriptor.label} API key`, value: 'apikey' },\n ]\n if (supportsOAuth(descriptor)) {\n // OAuth hint comes from the descriptor — built-in `anthropicDescriptor`\n // sets it to \"Claude Pro/Max subscription\". Hosts adding OAuth on their\n // own providers set their own hint (or omit it).\n const hint = descriptor.oauthHint ? ` (${descriptor.oauthHint})` : ''\n items.push({\n name: 'OAuth',\n description: `browser-based sign-in${hint}`,\n value: 'oauth',\n })\n }\n return items\n }, [descriptor])\n\n return (\n <WizardPanel title={` configure ${descriptor.label} — pick auth method `} error={error}>\n <WizardEscHint />\n <select\n {...SELECT_THEME}\n focused={focused}\n options={options}\n wrapSelection\n onSelect={(_idx, option) => {\n if (option)\n onPick(descriptor, option.value)\n }}\n style={{ flexGrow: 1 }}\n />\n </WizardPanel>\n )\n}\n\nfunction EnterApiKeyStep({\n descriptor,\n error,\n onSubmit,\n}: {\n descriptor: ProviderDescriptor\n error: string | null\n onSubmit: (descriptor: ProviderDescriptor, value: string) => void\n}) {\n const focused = useModalAwareFocus()\n const inputRef = useRef<InputRenderable | null>(null)\n const COLOR = useColors()\n\n const submit = useCallback(() => {\n const value = inputRef.current?.value ?? ''\n onSubmit(descriptor, value)\n }, [descriptor, onSubmit])\n\n return (\n <WizardPanel title={` configure ${descriptor.label} — paste API key `} error={error}>\n <text fg={COLOR.dim}>\n Paste your\n {` ${descriptor.label} `}\n API key and press\n <span fg={COLOR.model}> enter </span>\n to save. Esc to exit.\n </text>\n <box\n style={{\n border: true,\n borderColor: COLOR.borderActive,\n paddingLeft: 1,\n paddingRight: 1,\n height: 3,\n }}\n >\n <input\n ref={inputRef}\n focused={focused}\n keyBindings={API_KEY_INPUT_BINDINGS}\n placeholder={descriptor.apiKeyPlaceholder ?? 'API key…'}\n onSubmit={submit}\n style={{ flexGrow: 1 }}\n />\n </box>\n </WizardPanel>\n )\n}\n\nfunction OAuthRunningStep({\n descriptor,\n dataDir,\n onSuccess,\n onError,\n}: {\n descriptor: ProviderDescriptor\n dataDir: string\n onSuccess: () => void\n onError: (msg: string) => void\n}) {\n const [url, setUrl] = useState<string | null>(null)\n const [status, setStatus] = useState('starting browser…')\n const COLOR = useColors()\n\n useEffect(() => {\n const ac = new AbortController()\n let cancelled = false\n\n void (async () => {\n try {\n const creds = await runOAuthLogin(descriptor, {\n onUrl: (loginUrl) => {\n if (cancelled)\n return\n setUrl(loginUrl)\n setStatus('waiting for browser callback…')\n },\n onProgress: (message) => {\n if (!cancelled)\n setStatus(message)\n },\n signal: ac.signal,\n })\n if (cancelled)\n return\n // `creds` carries `{ access, refresh, expires, ...extras }`. Spread\n // verbatim — the storage shape only adds our `kind` tag on top.\n setProviderCredential(dataDir, descriptor, { kind: 'oauth', ...creds })\n onSuccess()\n }\n catch (err) {\n if (cancelled)\n return\n const message = err instanceof Error ? err.message : String(err)\n onError(message)\n }\n })()\n\n return () => { cancelled = true; ac.abort() }\n }, [descriptor, dataDir, onSuccess, onError])\n\n return (\n <WizardPanel title={` configure ${descriptor.label} — OAuth `}>\n <WizardEscHint />\n <Spinner label={status} />\n {url && (\n <box style={{ flexDirection: 'column', gap: 0 }}>\n <text fg={COLOR.dim}>If the browser didn't open, visit:</text>\n <text fg={COLOR.model}>{url}</text>\n </box>\n )}\n </WizardPanel>\n )\n}\n\n// ---------------------------------------------------------------------------\n// SessionsScreen — list of sessions with a New entry on top.\n// ---------------------------------------------------------------------------\n\nconst NEW_VALUE = '__new__'\n\nexport function SessionsScreen({\n sessions,\n currentId,\n onPick,\n onCreate,\n}: {\n sessions: SessionMeta[]\n currentId: string | null\n onPick: (id: string) => void\n onCreate: () => void\n}) {\n const focused = useModalAwareFocus()\n const COLOR = useColors()\n const SELECT_THEME = useSelectStyle()\n const options = useMemo(() => {\n const items: { name: string, description: string, value: string }[] = [\n { name: '+ new session', description: 'start fresh', value: NEW_VALUE },\n ]\n for (const s of sessions) {\n const marker = s.id === currentId ? '● ' : ' '\n const turnLabel = `${s.turnCount} turn${s.turnCount === 1 ? '' : 's'}`\n items.push({\n name: `${marker}${s.title}`,\n description: `#${shortId(s.id)} · ${turnLabel} · ${ageString(s.updatedAt)}`,\n value: s.id,\n })\n }\n return items\n }, [sessions, currentId])\n\n return (\n <box\n title=\" sessions \"\n style={{\n border: true,\n borderColor: COLOR.border,\n padding: 1,\n flexDirection: 'column',\n flexGrow: 1,\n }}\n >\n <select\n {...SELECT_THEME}\n focused={focused}\n options={options}\n wrapSelection\n onSelect={(_idx, option) => {\n if (!option)\n return\n if (option.value === NEW_VALUE)\n onCreate()\n else if (typeof option.value === 'string')\n onPick(option.value)\n }}\n style={{ flexGrow: 1 }}\n />\n </box>\n )\n}\n\n// ---------------------------------------------------------------------------\n// ChatScreen — transcript + auto-growing multi-line input + (running) spinner.\n// Enter inserts a newline; shift+enter submits; ctrl+↑↓ cycles prompt history.\n// ---------------------------------------------------------------------------\n\n/** Visible content lines: 1 minimum, 5 maximum (textarea scrolls past 5). */\nconst MIN_CONTENT_LINES = 1\nconst MAX_CONTENT_LINES = 5\n\nexport function ChatScreen({\n events,\n busy,\n settings,\n onSubmit,\n session,\n pending,\n onApproval,\n}: {\n events: StreamEvent[]\n busy: boolean\n settings: Settings\n onSubmit: (prompt: string) => void\n session: SessionMeta | null\n /** Head of the safe-mode approval queue, or `null` when nothing is pending. */\n pending: ApprovalRequest | null\n /** Resolve the active prompt with the user's pick. */\n onApproval: (decision: ApprovalDecision) => void\n}) {\n const COLOR = useColors()\n const title = useMemo(() => {\n if (!session)\n return ' untitled '\n const turns = `${session.turnCount} turn${session.turnCount === 1 ? '' : 's'}`\n return ` ${session.title} · #${shortId(session.id)} · ${turns} `\n }, [session])\n\n // Prior user prompts sourced from the transcript itself — kept in submit\n // order so the history-navigation feels like a regular shell.\n const userPrompts = useMemo(\n () => events.filter(e => e.kind === 'info').map(e => e.text.replace(/^❯ /, '')),\n [events],\n )\n\n return (\n <box style={{ flexDirection: 'column', flexGrow: 1 }}>\n <box\n title={title}\n style={{\n border: true,\n borderColor: COLOR.border,\n flexGrow: 1,\n flexDirection: 'column',\n }}\n >\n <Transcript events={events} settings={settings} />\n </box>\n\n {/*\n Priority: pending approval wins over busy (a paused run still has\n `busy === true` while the gate awaits). The picker takes the prompt\n slot so the user can decide without losing context.\n */}\n {pending\n ? <ApprovalBlock request={pending} onPick={onApproval} />\n : busy\n ? <BusyBlock />\n : <PromptBlock userPrompts={userPrompts} onSubmit={onSubmit} />}\n </box>\n )\n}\n\n/** Max chars per scalar argument in the approval preview. */\nconst APPROVAL_ARG_MAX = 80\n\n/**\n * Render `{ path: 'x.ts', contents: 'long string' }` as\n * `path: \"x.ts\", contents: \"long string…\"` — readable, per-key, truncated\n * per value rather than dumping `JSON.stringify(input)` (which produces an\n * illegible 50KB blob for `write_file` etc.).\n */\nfunction formatApprovalArgs(input: Record<string, unknown>): string {\n const parts: string[] = []\n for (const [key, raw] of Object.entries(input)) {\n let value: string\n if (typeof raw === 'string') {\n const escaped = raw.replace(/\\n/g, '\\\\n')\n value = escaped.length > APPROVAL_ARG_MAX\n ? `\"${escaped.slice(0, APPROVAL_ARG_MAX)}…\"`\n : `\"${escaped}\"`\n }\n else {\n const json = JSON.stringify(raw)\n value = json.length > APPROVAL_ARG_MAX ? `${json.slice(0, APPROVAL_ARG_MAX)}…` : json\n }\n parts.push(`${key}: ${value}`)\n }\n return parts.join(', ')\n}\n\n// `description` is required by OpenTUI's `SelectOption` shape even when the\n// select is rendered with `showDescription={false}` — passing an empty string\n// keeps the type happy and is invisible at the render layer.\ninterface DecisionOption { name: string, description: string, value: ApprovalDecision }\n\n/**\n * Inline approval picker — replaces the chat input while a tool call is\n * pending. Three options:\n * - **accept once** — let this call execute, don't persist anything.\n * - **accept + remember** — execute + add a `projects.json` entry so the\n * same shape doesn't prompt again in this directory.\n * - **deny** — refuse the call. The model gets `Blocked: …` and adapts.\n *\n * Esc aborts the whole run via the parent keyboard handler; per-call\n * accept/deny only happens through the select below.\n *\n * Layout is fully pinned so the picker never overlaps with itself or the\n * transcript above:\n *\n * - Outer `<box>` has an explicit `height`. The slot below the transcript\n * adapts (the chat container is column-flex), so we control exactly how\n * many rows we occupy.\n * - Summary row is a `<box height: 1, overflow: hidden>` wrapping a\n * `<text wrapMode=\"none\">` — a 500-char tool-call preview can never\n * wrap to row 2 and push the select off-screen.\n * - `<select showDescription={false}>` keeps each option to exactly one\n * row. Hints live in the `name` string after a `·` separator. Without\n * this, the default `showDescription: true` makes every option take 2\n * rows, and a `height: options.length` select would overdraw into the\n * summary above (the original bug).\n */\nfunction ApprovalBlock({\n request,\n onPick,\n}: {\n request: ApprovalRequest\n onPick: (decision: ApprovalDecision) => void\n}) {\n const focused = useModalAwareFocus()\n const COLOR = useColors()\n const SELECT_THEME = useSelectStyle()\n\n const summary = useMemo(\n () => `${request.tool}(${formatApprovalArgs(request.input)})`,\n [request.tool, request.input],\n )\n\n const options = useMemo<DecisionOption[]>(() => {\n const safelistEntry = suggestSafelistEntry(request.tool, request.input)\n return [\n { name: 'accept once · allow this call only', description: '', value: 'accept-once' },\n { name: `accept + remember · add \"${safelistEntry}\" to projects.json`, description: '', value: 'accept-safelist' },\n { name: 'deny · refuse — the model will see Blocked', description: '', value: 'deny' },\n ]\n }, [request.tool, request.input])\n\n // border (2) + summary (1) + one row per option = total outer height.\n const height = 2 + 1 + options.length\n\n return (\n <box\n title=\" approve tool call · esc to abort run \"\n style={{\n border: true,\n borderColor: COLOR.warn,\n paddingLeft: 1,\n paddingRight: 1,\n paddingTop: 0,\n paddingBottom: 0,\n height,\n flexDirection: 'column',\n flexShrink: 0,\n }}\n >\n <box style={{ height: 1, overflow: 'hidden', flexShrink: 0 }}>\n <text fg={COLOR.model} wrapMode=\"none\">\n <span fg={COLOR.warn}>↳ </span>\n {summary}\n </text>\n </box>\n <select\n {...SELECT_THEME}\n focused={focused}\n options={options}\n showDescription={false}\n wrapSelection\n onSelect={(_idx, option) => {\n if (option)\n onPick(option.value)\n }}\n style={{ height: options.length, flexShrink: 0 }}\n />\n </box>\n )\n}\n\nfunction BusyBlock() {\n const COLOR = useColors()\n return (\n <box\n style={{\n border: true,\n borderColor: COLOR.warn,\n paddingLeft: 1,\n paddingRight: 1,\n height: 3,\n }}\n >\n <Spinner label=\"streaming response — esc to abort\" />\n </box>\n )\n}\n\nfunction PromptBlock({\n userPrompts,\n onSubmit,\n}: {\n userPrompts: string[]\n onSubmit: (prompt: string) => void\n}) {\n const focused = useModalAwareFocus()\n const COLOR = useColors()\n const textareaRef = useRef<TextareaRenderable | null>(null)\n /** Auto-grow: visible content rows the textarea currently occupies (clamped 1..5). */\n const [contentLines, setContentLines] = useState(MIN_CONTENT_LINES)\n /**\n * History navigation state. `null` = not navigating (textarea owns its content).\n * Once the user enters history (up at top), we snapshot the draft and cycle.\n */\n const historyRef = useRef<{ idx: number, draft: string } | null>(null)\n\n const syncLines = useCallback(() => {\n // `lineCount` is logical (newline-separated) line count — what the user\n // means by \"jumping lines\". Wrap-aware visual lines (`virtualLineCount`)\n // would also grow on long single lines, which isn't what we want.\n const lines = textareaRef.current?.lineCount ?? MIN_CONTENT_LINES\n setContentLines(Math.max(MIN_CONTENT_LINES, lines))\n }, [])\n\n const submit = useCallback(() => {\n const value = textareaRef.current?.plainText ?? ''\n if (!value.trim())\n return\n onSubmit(value)\n textareaRef.current?.clear()\n historyRef.current = null\n setContentLines(MIN_CONTENT_LINES)\n }, [onSubmit])\n\n const cycleHistory = useCallback((direction: -1 | 1) => {\n if (userPrompts.length === 0 || !textareaRef.current)\n return\n\n if (historyRef.current === null) {\n // Entering history mode: snapshot the draft so we can restore on the\n // way forward past the most-recent entry.\n historyRef.current = {\n idx: userPrompts.length,\n draft: textareaRef.current.plainText,\n }\n }\n\n const nextIdx = historyRef.current.idx + direction\n\n if (nextIdx < 0)\n return // already at oldest\n\n if (nextIdx >= userPrompts.length) {\n // Walked off the end → restore the original draft, leave history mode.\n textareaRef.current.setText(historyRef.current.draft)\n textareaRef.current.gotoBufferEnd()\n historyRef.current = null\n }\n else {\n textareaRef.current.setText(userPrompts[nextIdx])\n textareaRef.current.gotoBufferEnd()\n historyRef.current.idx = nextIdx\n }\n syncLines()\n }, [userPrompts, syncLines])\n\n /**\n * Up/Down at the buffer boundary cycles prompt history (fish/zsh pattern).\n * Mid-buffer up/down move the cursor normally — handled by the default\n * `move-up` / `move-down` actions in `TEXTAREA_BINDINGS`.\n */\n const onKeyDown = useCallback((event: KeyEvent) => {\n if (event.ctrl || event.shift || event.meta)\n return\n if (event.name !== 'up' && event.name !== 'down')\n return\n const buffer = textareaRef.current\n if (!buffer)\n return\n const cursorRow = buffer.logicalCursor.row\n if (event.name === 'up' && cursorRow === 0)\n cycleHistory(-1)\n else if (event.name === 'down' && cursorRow === buffer.lineCount - 1)\n cycleHistory(1)\n }, [cycleHistory])\n\n // Content area + 2 lines of border = total box height.\n const contentHeight = Math.min(MAX_CONTENT_LINES, contentLines)\n const boxHeight = contentHeight + 2\n\n return (\n <box\n style={{\n border: true,\n borderColor: COLOR.borderActive,\n paddingLeft: 1,\n paddingRight: 1,\n height: boxHeight,\n flexDirection: 'column',\n }}\n >\n <textarea\n ref={textareaRef}\n // Release focus while a modal is open so its `handleKeyPress` stops\n // routing keys behind the overlay (see `useModalAwareFocus`).\n focused={focused}\n keyBindings={TEXTAREA_BINDINGS}\n placeholder=\"Ask zidane… (enter = send · shift+enter = newline · ↑↓ at edges = history)\"\n // Fill the box's inner area so the textarea's own scroll engine kicks\n // in once content exceeds the visible rows (past 5 logical lines).\n style={{ flexGrow: 1, height: '100%' }}\n onSubmit={submit}\n onContentChange={syncLines}\n onKeyDown={onKeyDown}\n />\n </box>\n )\n}\n","import type { BooleanSettingKey } from '../chat/settings-context'\nimport type { Settings } from '../chat/types'\nimport { useKeyboard } from '@opentui/react'\nimport { useCallback, useMemo, useState } from 'react'\nimport { SETTINGS_CHOICES, SETTINGS_TOGGLES, useSettings } from '../chat/settings-context'\nimport { useColors } from '../chat/theme-context'\nimport { Modal } from './modal'\n\n// ---------------------------------------------------------------------------\n// SettingsModal — vertical list. Up/Down navigates, Enter/Space activates the\n// focused row (toggles a flag, cycles a choice, or invokes an action). Esc\n// dismisses (handled by the parent <Modal>).\n//\n// Three row kinds:\n// - `toggle` — a boolean stored under `settings[key]` (see SETTINGS_TOGGLES)\n// - `choice` — a string stored under `settings[key]` cycling through a fixed\n// options list (see SETTINGS_CHOICES — e.g. the theme picker)\n// - `action` — a one-shot callback (e.g. \"Re-configure providers\")\n//\n// Hosts that mount the modal pass `actions` to wire in app-level actions\n// like re-opening the auth panel. Toggle and choice rows are static —\n// sourced from the corresponding tables in `chat/settings-context.tsx` so a\n// GUI settings panel can share the same labels.\n// ---------------------------------------------------------------------------\n\ninterface ToggleItem {\n kind: 'toggle'\n key: BooleanSettingKey\n label: string\n description: string\n}\ninterface ChoiceItem {\n kind: 'choice'\n key: keyof Settings\n label: string\n description: string\n options: readonly { value: unknown, label: string }[]\n}\ninterface ActionItem {\n kind: 'action'\n id: string\n label: string\n description: string\n onPick: () => void\n}\ntype SettingsItem = ToggleItem | ChoiceItem | ActionItem\n\nexport interface SettingsActions {\n /**\n * Re-open the auth screen so the user can switch providers or run the\n * wizard for a new/existing one. Wiring this callback adds a\n * \"Re-configure providers\" row to the modal.\n */\n onReauth?: () => void\n}\n\nexport function SettingsModal({ actions }: { actions?: SettingsActions } = {}) {\n const { settings, toggle, setSetting } = useSettings()\n const [cursor, setCursorRaw] = useState(0)\n const COLOR = useColors()\n\n // Build the row list dynamically so the modal only shows action rows whose\n // callbacks are wired. Toggle/choice rows are always present.\n const items: readonly SettingsItem[] = useMemo(() => {\n const toggleItems: ToggleItem[] = SETTINGS_TOGGLES.map(t => ({ kind: 'toggle' as const, ...t }))\n const choiceItems: ChoiceItem[] = SETTINGS_CHOICES.map(c => ({ kind: 'choice' as const, ...c }))\n const actionItems: ActionItem[] = []\n if (actions?.onReauth) {\n actionItems.push({\n kind: 'action',\n id: 'reauth',\n label: 'Authentication',\n description: 'switch provider, add another, or re-authenticate',\n onPick: actions.onReauth,\n })\n }\n return [...toggleItems, ...choiceItems, ...actionItems]\n }, [actions])\n\n // The modal re-mounts on each open via `modal.open(<SettingsModal/>)`, but\n // if the host swaps `actions` while the modal is open (e.g. tears down the\n // re-auth wiring mid-session), the cursor might now point past the end of\n // the truncated row list. Clamp on read so we never index out of bounds.\n const safeCursor = Math.min(cursor, items.length - 1)\n const setCursor = useCallback(\n (update: (c: number) => number) =>\n setCursorRaw(prev => Math.min(Math.max(0, update(prev)), items.length - 1)),\n [items.length],\n )\n\n useKeyboard((key) => {\n if (key.name === 'up' || (key.ctrl && key.name === 'p')) {\n setCursor(c => c - 1)\n }\n else if (key.name === 'down' || (key.ctrl && key.name === 'n')) {\n setCursor(c => c + 1)\n }\n else if (key.name === 'return' || key.name === 'space') {\n const item = items[safeCursor]\n if (!item)\n return\n if (item.kind === 'toggle') {\n toggle(item.key)\n }\n else if (item.kind === 'choice') {\n // Cycle to the next option, wrapping. Single-option choice = no-op\n // (find returns the only entry; (idx+1)%1 === 0 → same value).\n const current = settings[item.key]\n const idx = item.options.findIndex(o => o.value === current)\n const next = item.options[(idx + 1) % item.options.length]\n if (next)\n setSetting(item.key, next.value as Settings[typeof item.key])\n }\n else {\n item.onPick()\n }\n }\n })\n\n // Index where the post-toggle block starts — separator divides transcript\n // filter toggles (top) from app-level settings (theme picker, auth, …).\n // Falls back to -1 when only toggles exist.\n const firstNonToggleIndex = items.findIndex(i => i.kind !== 'toggle')\n\n return (\n <Modal title=\"settings\">\n <box style={{ flexDirection: 'column' }}>\n {items.map((item, i) => (\n <box\n key={item.kind === 'action' ? item.id : item.key}\n style={{ flexDirection: 'column' }}\n >\n {/*\n Full-width separator between transcript-filter toggles and the\n app-level block (theme picker, authentication, …).\n `border={['top']}` draws only the top edge, which yoga stretches\n to the parent's content width — no need to measure the modal\n manually. Vertical margin gives the rule breathing room from\n the rows it separates.\n */}\n {i === firstNonToggleIndex && i > 0 && (\n <box\n style={{\n border: ['top'],\n borderColor: COLOR.mute,\n height: 1,\n marginTop: 1,\n marginBottom: 1,\n }}\n />\n )}\n {item.kind === 'toggle' && (\n <ToggleRow\n label={item.label}\n description={item.description}\n enabled={settings[item.key]}\n focused={i === safeCursor}\n />\n )}\n {item.kind === 'choice' && (\n <ChoiceRow\n label={item.label}\n description={item.description}\n value={\n item.options.find(o => o.value === settings[item.key])?.label\n ?? String(settings[item.key])\n }\n cyclable={item.options.length > 1}\n focused={i === safeCursor}\n />\n )}\n {item.kind === 'action' && (\n <ActionRow\n label={item.label}\n description={item.description}\n focused={i === safeCursor}\n />\n )}\n </box>\n ))}\n </box>\n <text fg={COLOR.mute}>\n <span fg={COLOR.warn}>↑↓</span>\n {' navigate · '}\n <span fg={COLOR.warn}>↵</span>\n {' toggle/cycle/select · '}\n <span fg={COLOR.warn}>esc</span>\n {' close'}\n </text>\n </Modal>\n )\n}\n\n/**\n * Toggle row — `▶` marker · checkbox · label · description.\n *\n * Rendered as one `<text>` so OpenTUI's word-wrap handles narrow terminals\n * automatically: on wide screens everything sits on one line; on narrow ones\n * the trailing description wraps under the label without breaking the row.\n */\nfunction ToggleRow({\n label,\n description,\n enabled,\n focused,\n}: {\n label: string\n description: string\n enabled: boolean\n focused: boolean\n}) {\n const COLOR = useColors()\n return (\n <text fg={focused ? COLOR.brand : COLOR.dim}>\n <span fg={focused ? COLOR.brand : COLOR.mute}>{focused ? '▶ ' : ' '}</span>\n <span fg={enabled ? COLOR.accent : COLOR.mute}>{enabled ? '[✓] ' : '[ ] '}</span>\n <span fg={focused ? COLOR.brand : COLOR.dim}>{label}</span>\n <span fg={COLOR.mute}>{` ${description}`}</span>\n </text>\n )\n}\n\n/**\n * Choice row — `▶` marker · label · `:` · current value · description.\n *\n * Cycles through `options` on enter/space. When only one option is\n * available (`cyclable=false`) the row still renders with the current\n * value but the enter handler is a no-op — we surface this via the absence\n * of the trailing `›` affordance so it visually reads as informational.\n */\nfunction ChoiceRow({\n label,\n description,\n value,\n cyclable,\n focused,\n}: {\n label: string\n description: string\n value: string\n cyclable: boolean\n focused: boolean\n}) {\n const COLOR = useColors()\n return (\n <text fg={focused ? COLOR.brand : COLOR.dim}>\n <span fg={focused ? COLOR.brand : COLOR.mute}>{focused ? '▶ ' : ' '}</span>\n <span fg={focused ? COLOR.brand : COLOR.dim}>{label}</span>\n <span fg={COLOR.mute}>{': '}</span>\n <span fg={focused ? COLOR.brand : COLOR.accent}>{value}</span>\n <span fg={COLOR.mute}>{` ${description}`}</span>\n {focused && cyclable && <span fg={COLOR.brand}>{' ↻'}</span>}\n </text>\n )\n}\n\n/**\n * Action row — cursor marker · label · description · (focus-only) trailing arrow.\n *\n * The label sits in the same column as a toggle row's `[✓]` checkbox (right\n * after the 2-col cursor slot). The trailing `›` only renders when focused\n * so it reads as a \"this row runs\" affordance, not a static decoration on\n * every action.\n */\nfunction ActionRow({\n label,\n description,\n focused,\n}: {\n label: string\n description: string\n focused: boolean\n}) {\n const COLOR = useColors()\n return (\n <text fg={focused ? COLOR.brand : COLOR.dim}>\n <span fg={focused ? COLOR.brand : COLOR.mute}>{focused ? '▶ ' : ' '}</span>\n <span fg={focused ? COLOR.brand : COLOR.accent}>{label}</span>\n <span fg={COLOR.mute}>{` ${description}`}</span>\n {focused && <span fg={COLOR.brand}>{' ›'}</span>}\n </text>\n )\n}\n","import type { Agent } from '../agent'\nimport type { ProviderAuth, ProviderKey } from '../chat/auth'\nimport type { ResolvedConfig } from '../chat/config'\nimport type { Picked, Screen, SessionMeta, Settings, StreamEvent } from '../chat/types'\nimport type { Session } from '../session'\nimport type { ContextUsage, Hint } from './components'\nimport { useKeyboard, useRenderer } from '@opentui/react'\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport { createAgent } from '../agent'\nimport { ConfigProvider, useConfig } from '../chat/config-context'\nimport { getContextWindow } from '../chat/providers'\nimport { addToSafelist, getSafelist, isOnSafelist, suggestSafelistEntry } from '../chat/safe-mode'\nimport { SafeModeProvider, useSafeModeActions, useSafeModeQueue } from '../chat/safe-mode-context'\nimport { DEFAULT_SETTINGS, SettingsProvider, useSettings } from '../chat/settings-context'\nimport {\n eventsFromTurns,\n lastContextSizeFromTurns,\n listSessionMeta,\n stripSpawnTokensLine,\n titleFromTurns,\n toolCallPreview,\n toolResultText,\n} from '../chat/store'\nimport {\n finalizeStreamingMarkdown,\n finalizeStreamingMarkdownForOwner,\n turnContextSize,\n useStreamBuffer,\n} from '../chat/streaming'\nimport { resolveTheme } from '../chat/theme'\nimport { ThemeProvider } from '../chat/theme-context'\nimport { createSession, loadSession } from '../session'\nimport { formatTokenUsage } from '../stats'\nimport { Footer } from './components'\nimport { ModalRoot, useModal } from './modal'\nimport { ModelPickerModal } from './model-picker'\nimport { AuthScreen, ChatScreen, SessionsScreen } from './screens'\nimport { SettingsModal } from './settings-modal'\n\n/**\n * Surface failures that are normally silenced (teardown / save) when the\n * `ZIDANE_DEBUG` env var is set. Logging via `console.error` would otherwise\n * trigger OpenTUI's error console overlay and clutter the UI for end users.\n */\nconst debugLog: (label: string, err: unknown) => void\n = process.env.ZIDANE_DEBUG\n ? (label, err) => console.error(`[zidane/tui] ${label}:`, err)\n : () => {}\n\n/**\n * Top-level TUI component. Accepts a fully-resolved `ResolvedConfig` and wires\n * everything (settings, modal layer, screens, footer) underneath it.\n *\n * Hosts can either drive this via `runTui()` for the standard bootstrap or\n * mount `<App config={resolveConfig(...)} />` themselves inside a renderer\n * they already own.\n */\nexport function App({ config }: { config: ResolvedConfig }) {\n const initialSettings = useMemo(\n () => ({ ...DEFAULT_SETTINGS, ...config.initialSettings }),\n [config.initialSettings],\n )\n\n const onSettingsChange = useCallback(\n (settings: Settings) => config.stateStore.save({ ...config.stateStore.load(), settings }),\n [config.stateStore],\n )\n\n return (\n <ConfigProvider config={config}>\n <SettingsProvider initial={initialSettings} onChange={onSettingsChange}>\n <ThemedShell />\n </SettingsProvider>\n </ConfigProvider>\n )\n}\n\n/**\n * Reads `settings.theme` from the surrounding `SettingsProvider`, resolves\n * it to a `Theme`, and mounts everything else underneath a `ThemeProvider`.\n * Split out so `App` doesn't need to call `useSettings` (which would force\n * it to live inside its own provider — invalid).\n *\n * `resolveTheme` falls back to `DEFAULT_THEME` on unknown ids, so an\n * out-of-date `state.json` (theme renamed / removed) never breaks rendering.\n */\nfunction ThemedShell() {\n const { settings } = useSettings()\n const theme = useMemo(() => resolveTheme(settings.theme), [settings.theme])\n return (\n <ThemeProvider theme={theme}>\n <SafeModeProvider>\n <ModalRoot>\n <AppShell />\n </ModalRoot>\n </SafeModeProvider>\n </ThemeProvider>\n )\n}\n\nfunction AppShell() {\n const renderer = useRenderer()\n const modal = useModal()\n const config = useConfig()\n const { settings } = useSettings()\n // `useSafeModeQueue` re-renders on every push/pop; `useSafeModeActions`\n // hands back a stable object, so anything memoizing over the actions\n // (gate handlers, abort callback) keeps a single identity across queue\n // churn. See safe-mode-context.tsx.\n const queue = useSafeModeQueue()\n const { requestApproval, resolveHead, denyAll } = useSafeModeActions()\n\n // Destructure stable identities up-front so callbacks/effects can depend on\n // them directly (avoids the \"whole config in deps\" footgun — see S1 review).\n const {\n providers: providerRegistry,\n preset,\n store,\n stateStore,\n modelsFor,\n resumeProvider,\n initialPicked,\n initialState,\n } = config\n const lastResumedSessionId = initialState.lastSessionId\n const dataDir = config.paths.dir\n\n // -------------------------------------------------------------------------\n // Safe-mode plumbing.\n //\n // Hook handlers run inside the agent loop — they need stable references to\n // the latest \"is safe-mode on?\" flag and to the cwd (the project key in\n // `projects.json`). Refs decouple registration from React re-renders so we\n // don't re-register on every settings flip.\n // -------------------------------------------------------------------------\n\n const safeModeEnabledRef = useRef(settings.safeMode)\n useEffect(() => { safeModeEnabledRef.current = settings.safeMode }, [settings.safeMode])\n\n // `process.cwd()` is the project key in `projects.json`. Captured once\n // per AppShell mount with `useState` lazy init — `useMemo([])` would\n // technically allow React to recompute under memory pressure, `useState`\n // is a hard guarantee.\n const [projectDir] = useState(() => process.cwd())\n\n // In-memory cache for the project's safelist so `gateDecision` doesn't\n // re-read `projects.json` from disk on every tool call (matters when a\n // parallel batch fires dozens of gate hooks in the same microtask). The\n // ref is seeded lazily and explicitly refreshed when we persist an entry\n // — the TUI is the only writer, so external invalidation isn't needed.\n const safelistRef = useRef<readonly string[] | null>(null)\n const readSafelist = useCallback((): readonly string[] => {\n if (safelistRef.current === null)\n safelistRef.current = getSafelist(dataDir, projectDir)\n return safelistRef.current\n }, [dataDir, projectDir])\n // Drop the cache on a project switch (only happens across mounts today,\n // but cheap insurance for future code that swaps `projectDir`).\n useEffect(() => { safelistRef.current = null }, [dataDir, projectDir])\n\n /**\n * Single source of truth for \"should this call execute?\". Returns true to\n * let the call through, false to refuse it. Handles three short-circuits:\n *\n * - Safe-mode globally off → always allow.\n * - Call covered by the project safelist or the implicit read-only set\n * → always allow without prompting.\n * - Otherwise → prompt the user and act on their decision (including\n * persisting a new safelist entry on \"accept + safelist\").\n *\n * Wired into the parent agent via `tool:gate` / `mcp:tool:gate`, and to\n * every subagent (transitively, for free) via `child:tool:gate` /\n * `child:mcp:tool:gate` — see the bubble in `src/tools/spawn.ts`.\n */\n const gateDecision = useCallback(\n async (tool: string, input: Record<string, unknown>): Promise<boolean> => {\n if (!safeModeEnabledRef.current)\n return true\n if (isOnSafelist(readSafelist(), tool, input))\n return true\n const decision = await requestApproval(tool, input)\n if (decision === 'deny')\n return false\n if (decision === 'accept-safelist') {\n const entry = suggestSafelistEntry(tool, input)\n addToSafelist(dataDir, projectDir, entry)\n safelistRef.current = null // force re-read on next gate\n }\n return true\n },\n [dataDir, projectDir, requestApproval, readSafelist],\n )\n\n // Initial screen + picked seed from the resolved config so a returning user\n // lands straight on chat (or sessions) without an auth-screen flash.\n const [screen, setScreen] = useState<Screen>(() => {\n if (!resumeProvider)\n return 'auth'\n return lastResumedSessionId ? 'chat' : 'sessions'\n })\n const [picked, setPicked] = useState<Picked | null>(() => initialPicked)\n const [sessions, setSessions] = useState<SessionMeta[]>([])\n const [currentSession, setCurrentSession] = useState<SessionMeta | null>(null)\n const [events, setEvents] = useState<StreamEvent[]>([])\n const [busy, setBusy] = useState(false)\n /** Token count from the most recent assistant turn (caching-aware). */\n const [lastInputTokens, setLastInputTokens] = useState(0)\n\n const agentRef = useRef<Agent | null>(null)\n const sessionRef = useRef<Session | null>(null)\n\n const stream = useStreamBuffer(setEvents)\n\n const makePicked = useCallback((provider: ProviderAuth, modelId?: string): Picked | null => {\n // If the host narrowed the provider registry we may have a detected auth\n // with no descriptor to back it. Return null and let the caller bail; the\n // AuthScreen already filters these out, so this is a belt-and-braces guard.\n const descriptor = providerRegistry[provider.key]\n if (!descriptor)\n return null\n const remembered = initialState.lastModelByProvider?.[provider.key]\n // Prefer remembered → descriptor default → factory probe. Factories may\n // throw on missing credentials at construction time; by the time we get\n // here we've already detected auth, so the probe is safe, but the\n // descriptor default lets us avoid constructing in the common case.\n const model = modelId ?? remembered ?? descriptor.defaultModel ?? descriptor.factory().meta.defaultModel\n return { provider, model }\n }, [providerRegistry, initialState])\n\n // -------------------------------------------------------------------------\n // Agent lifecycle: build a fresh Agent bound to the active Session and wire\n // streaming hooks. Re-created on every session switch so context never bleeds.\n // -------------------------------------------------------------------------\n\n const buildAgent = useCallback((session: Session, key: ProviderKey): Agent => {\n const descriptor = providerRegistry[key]\n if (!descriptor)\n throw new Error(`No provider registered for key \"${key}\"`)\n const agent = createAgent({ ...preset, provider: descriptor.factory(), session })\n\n // Safe-mode gates -----------------------------------------------------\n // Four hooks: native vs MCP, parent vs subagent. The `child:*:gate`\n // events bubble from every subagent's hook bus via `bubbleHooks` (see\n // `src/tools/spawn.ts`) with the **same** gate ctx, so writes to\n // `block` / `reason` here propagate straight back to the child's loop.\n // A denied call never reaches `tool:before`, so it doesn't pollute the\n // transcript — the model receives `Blocked: …` and adapts.\n const applyGate = async (\n name: string,\n input: Record<string, unknown>,\n ctx: { block: boolean, reason: string },\n ): Promise<void> => {\n if (ctx.block) // already refused by a higher-priority gate (skills/budgets/dedup)\n return\n if (!(await gateDecision(name, input))) {\n ctx.block = true\n ctx.reason = 'User denied this tool call'\n }\n }\n agent.hooks.hook('tool:gate', ctx => applyGate(ctx.name, ctx.input, ctx))\n agent.hooks.hook('child:tool:gate', ctx => applyGate(ctx.name, ctx.input, ctx))\n agent.hooks.hook('mcp:tool:gate', ctx => applyGate(ctx.displayName, ctx.input, ctx))\n agent.hooks.hook('child:mcp:tool:gate', ctx => applyGate(ctx.displayName, ctx.input, ctx))\n\n // Parent streams ------------------------------------------------------\n agent.hooks.hook('stream:thinking', ({ delta }) => stream.queueStreamDelta('thinking', delta))\n agent.hooks.hook('stream:text', ({ delta }) => stream.queueStreamDelta('markdown', delta))\n agent.hooks.hook('tool:before', ({ name, input }) => {\n stream.appendImmediate({ kind: 'tool', text: toolCallPreview(name, input), tool: name })\n })\n agent.hooks.hook('tool:after', ({ name, result }) => {\n // Spawn tool-results carry a `Tokens: …` summary that's already shown\n // by the spawn-end marker right above the tool-result — strip it from\n // the displayed string to avoid the duplicate / format-drift block.\n const raw = toolResultText(result)\n const text = name === 'spawn' ? stripSpawnTokensLine(raw) : raw\n stream.appendImmediate({ kind: 'tool-result', text, tool: name })\n })\n agent.hooks.hook('mcp:tool:after', ({ displayName, result }) => {\n stream.appendImmediate({ kind: 'tool-result', text: toolResultText(result), tool: displayName })\n })\n agent.hooks.hook('turn:after', ({ usage }) => {\n if (usage)\n setLastInputTokens(turnContextSize(usage))\n stream.flushAndUpdate(finalizeStreamingMarkdown)\n })\n\n // Subagent streams ----------------------------------------------------\n agent.hooks.hook('spawn:before', ({ id, task, depth }) => {\n const taskPreview = task.length > 80 ? `${task.slice(0, 80)}…` : task\n stream.appendImmediate({\n kind: 'spawn-start',\n text: taskPreview,\n childId: id,\n depth: depth ?? 1,\n })\n })\n agent.hooks.hook('spawn:complete', ({ id, depth, status, stats }) => {\n const tag = status === 'aborted' ? 'aborted' : status === 'error' ? 'error' : 'done'\n stream.appendImmediate({\n kind: 'spawn-end',\n text: `${tag} ${formatTokenUsage(stats)}`,\n childId: id,\n depth: depth ?? 1,\n })\n })\n agent.hooks.hook('spawn:error', ({ id, depth, error }) => {\n stream.appendImmediate({\n kind: 'error',\n text: `[${id}] ${error.message}`,\n childId: id,\n depth: depth ?? 1,\n })\n })\n agent.hooks.hook('child:stream:thinking', ({ delta, childId, depth }) => {\n stream.queueStreamDelta('thinking', delta, { childId, depth })\n })\n agent.hooks.hook('child:stream:text', ({ delta, childId, depth }) => {\n stream.queueStreamDelta('markdown', delta, { childId, depth })\n })\n agent.hooks.hook('child:tool:before', ({ name, input, childId, depth }) => {\n stream.appendImmediate({\n kind: 'tool',\n text: toolCallPreview(name, input),\n tool: name,\n childId,\n depth,\n })\n })\n agent.hooks.hook('child:tool:after', ({ name, result, childId, depth }) => {\n stream.appendImmediate({\n kind: 'tool-result',\n text: toolResultText(result),\n tool: name,\n childId,\n depth,\n })\n })\n agent.hooks.hook('child:stream:end', ({ childId }) => {\n // Finalize the child's trailing markdown block so the next child event\n // (tool call, spawn-end) doesn't keep appending into it.\n stream.flushAndUpdate(prev => finalizeStreamingMarkdownForOwner(prev, childId))\n })\n\n return agent\n }, [providerRegistry, preset, stream, gateDecision])\n\n // -------------------------------------------------------------------------\n // Session activation — load (or create) a Session, rebuild the agent, replay\n // turns into the transcript, and persist the resume hint.\n // -------------------------------------------------------------------------\n\n const refreshSessions = useCallback(async () => {\n const list = await listSessionMeta(store)\n setSessions(list)\n return list\n }, [store])\n\n const teardown = useCallback(async () => {\n stream.reset()\n await agentRef.current?.destroy().catch(err => debugLog('agent.destroy failed', err))\n agentRef.current = null\n sessionRef.current = null\n }, [stream])\n\n const activateSession = useCallback(async (id: string | null, key: ProviderKey) => {\n await teardown()\n\n const loaded = id ? await loadSession(store, id) : null\n const session = loaded ?? await createSession({ store, ...(id ? { id } : {}) })\n\n sessionRef.current = session\n agentRef.current = buildAgent(session, key)\n\n setEvents(eventsFromTurns(session.turns, session.runs))\n setLastInputTokens(lastContextSizeFromTurns(session.turns))\n setCurrentSession({\n id: session.id,\n title: titleFromTurns(session.turns) ?? 'untitled',\n turnCount: session.turns.length,\n updatedAt: Date.now(),\n })\n setScreen('chat')\n stateStore.save({\n ...stateStore.load(),\n lastProvider: key,\n lastSessionId: session.id,\n })\n }, [teardown, buildAgent, store, stateStore])\n\n // -------------------------------------------------------------------------\n // Resume on launch — when a previous provider was remembered, jump straight\n // into the last session (or the sessions list if it's been deleted).\n // -------------------------------------------------------------------------\n\n // Narrow the deps to exactly what the effect reads. Depending on `config`\n // as a whole would re-fire if any future refactor made the resolved config\n // non-stable — and re-firing here re-runs `activateSession`, destroying and\n // rebuilding the agent in a loop (this was the original leak).\n useEffect(() => {\n if (!resumeProvider)\n return\n let cancelled = false\n void (async () => {\n if (lastResumedSessionId) {\n const data = await store.load(lastResumedSessionId)\n if (cancelled)\n return\n if (data) {\n await activateSession(lastResumedSessionId, resumeProvider.key)\n return\n }\n }\n const list = await refreshSessions()\n if (cancelled)\n return\n if (list.length === 0)\n await activateSession(null, resumeProvider.key)\n else\n setScreen('sessions')\n })()\n return () => { cancelled = true }\n }, [activateSession, refreshSessions, resumeProvider, lastResumedSessionId, store])\n\n // -------------------------------------------------------------------------\n // Screen actions.\n // -------------------------------------------------------------------------\n\n const onPickProvider = useCallback(async (p: ProviderAuth) => {\n const next = makePicked(p)\n if (!next)\n return // provider isn't in the registry — guarded by AuthScreen, defensive here\n setPicked(next)\n stateStore.save({ ...stateStore.load(), lastProvider: p.key })\n const list = await refreshSessions()\n if (list.length === 0)\n await activateSession(null, p.key)\n else\n setScreen('sessions')\n }, [refreshSessions, activateSession, makePicked, stateStore])\n\n const onCreateSession = useCallback(async () => {\n if (picked)\n await activateSession(null, picked.provider.key)\n }, [picked, activateSession])\n\n const onSwitchSession = useCallback(async (id: string) => {\n if (picked)\n await activateSession(id, picked.provider.key)\n }, [picked, activateSession])\n\n const onOpenSessions = useCallback(async () => {\n await refreshSessions()\n setScreen('sessions')\n }, [refreshSessions])\n\n const onAbort = useCallback(() => {\n // Flush any pending approvals so the run can unwind cleanly; otherwise\n // `agent.abort()` only flips the run's signal and our gate handlers\n // would still hang in `await requestApproval(...)`.\n denyAll()\n agentRef.current?.abort()\n }, [denyAll])\n\n const onPickModel = useCallback((modelId: string) => {\n setPicked((prev) => {\n if (!prev)\n return prev\n // Remember per-provider so the next launch resumes the right model.\n const prior = stateStore.load()\n stateStore.save({\n ...prior,\n lastModelByProvider: { ...prior.lastModelByProvider, [prev.provider.key]: modelId },\n })\n return { ...prev, model: modelId }\n })\n modal.close()\n }, [modal, stateStore])\n\n const onSubmitPrompt = useCallback(async (prompt: string) => {\n const agent = agentRef.current\n const session = sessionRef.current\n if (!agent || !session || !picked || !prompt.trim())\n return\n\n if (events.length > 0)\n stream.appendImmediate({ kind: 'separator', text: '' })\n stream.appendImmediate({ kind: 'info', text: `❯ ${prompt}` })\n setBusy(true)\n\n try {\n await agent.run({ model: picked.model, prompt })\n await session.save().catch(err => debugLog('session.save failed', err))\n setCurrentSession(prev => prev\n ? {\n ...prev,\n title: titleFromTurns(session.turns) ?? prev.title,\n turnCount: session.turns.length,\n updatedAt: Date.now(),\n }\n : prev)\n }\n catch (err) {\n stream.appendImmediate({ kind: 'error', text: err instanceof Error ? err.message : String(err) })\n }\n finally {\n stream.flushAndUpdate(finalizeStreamingMarkdown)\n setBusy(false)\n }\n }, [picked, events.length, stream])\n\n // -------------------------------------------------------------------------\n // Top-level keyboard. Each screen handles its own primary action; this only\n // routes esc + the modal shortcuts.\n //\n // The callback is recreated on every render, but OpenTUI's `useKeyboard`\n // wraps it with `useEffectEvent` internally — registration only happens once\n // and the handler always sees the latest closure. No leak.\n // -------------------------------------------------------------------------\n\n // Settings → \"Re-configure providers\" jumps back to the auth screen. We\n // capture this as a callback so the modal closes cleanly before the screen\n // transition (avoiding a brief flash of the modal over the new screen).\n const onReauth = useCallback(() => {\n modal.close()\n setScreen('auth')\n }, [modal])\n\n // Head of the safe-mode approval queue. `null` means \"nothing pending\"\n // and the chat screen shows the prompt input as usual. Defined here so the\n // keyboard handler and the footer hints both see it.\n const pendingApproval = queue[0] ?? null\n\n useKeyboard((key) => {\n if (modal.isOpen)\n return\n if (key.ctrl && key.name === ',' && screen !== 'auth') {\n modal.open(<SettingsModal actions={{ onReauth }} />)\n return\n }\n if (key.ctrl && key.name === 'm' && screen === 'chat' && picked && !busy) {\n modal.open(\n <ModelPickerModal\n models={modelsFor(picked.provider.key)}\n currentModelId={picked.model}\n onPick={onPickModel}\n />,\n )\n return\n }\n if (key.name !== 'escape')\n return\n // Esc always aborts the in-flight run (whether or not a prompt is\n // pending) — `onAbort` denies every queued approval and flips the\n // agent's signal. Per-call accept/deny lives in the approval picker\n // itself; this keeps \"esc = stop everything\" as a single rule.\n if (busy || pendingApproval)\n return onAbort()\n if (screen === 'chat')\n return onOpenSessions()\n if (screen === 'sessions') {\n if (currentSession)\n setScreen('chat')\n else\n renderer.destroy()\n return\n }\n // auth screen — if the user got here from settings (picked already\n // exists), bounce them back to wherever they were. Only exit on a true\n // first-launch (no provider picked yet).\n if (picked) {\n setScreen(currentSession ? 'chat' : 'sessions')\n return\n }\n renderer.destroy()\n })\n\n // -------------------------------------------------------------------------\n // Derived footer state.\n // -------------------------------------------------------------------------\n\n const hints: Hint[] = useMemo(\n () => buildHints(screen, busy, !!pendingApproval, currentSession),\n [screen, busy, pendingApproval, currentSession],\n )\n\n const contextUsage: ContextUsage | null = useMemo(() => {\n if (screen !== 'chat' || !picked)\n return null\n const descriptor = providerRegistry[picked.provider.key]\n if (!descriptor)\n return null\n // `getContextWindow` already checks `descriptor.models` first and falls\n // back to pi-ai's registry — one call covers both cases.\n const max = getContextWindow(descriptor, picked.model)\n return max ? { used: lastInputTokens, max } : null\n }, [screen, picked, lastInputTokens, providerRegistry])\n\n // Drop the agent + clear timers when the app unmounts.\n useEffect(() => () => { void teardown() }, [teardown])\n\n return (\n <box style={{ flexDirection: 'column', flexGrow: 1 }}>\n <box style={{ flexDirection: 'column', flexGrow: 1, paddingLeft: 1, paddingRight: 1 }}>\n {screen === 'auth' && <AuthScreen onPick={onPickProvider} />}\n {screen === 'sessions' && (\n <SessionsScreen\n sessions={sessions}\n currentId={currentSession?.id ?? null}\n onPick={onSwitchSession}\n onCreate={onCreateSession}\n />\n )}\n {screen === 'chat' && (\n <ChatScreen\n events={events}\n busy={busy}\n settings={settings}\n onSubmit={onSubmitPrompt}\n session={currentSession}\n pending={pendingApproval}\n onApproval={resolveHead}\n />\n )}\n </box>\n <Footer hints={hints} picked={picked} context={contextUsage} />\n </box>\n )\n}\n\nfunction buildHints(\n screen: Screen,\n busy: boolean,\n pending: boolean,\n currentSession: SessionMeta | null,\n): Hint[] {\n if (pending)\n return [{ key: '↑↓', label: 'navigate' }, { key: '↵', label: 'select' }, { key: 'esc', label: 'abort run' }]\n if (busy)\n return [{ key: 'esc', label: 'abort' }]\n if (screen === 'auth')\n return [{ key: '↑↓', label: 'navigate' }, { key: '↵', label: 'select' }, { key: 'esc', label: 'exit' }]\n if (screen === 'sessions') {\n return [\n { key: '↑↓', label: 'navigate' },\n { key: '↵', label: 'open' },\n { key: 'ctrl+,', label: 'settings' },\n { key: 'esc', label: currentSession ? 'back' : 'exit' },\n ]\n }\n return [\n { key: '↵', label: 'send' },\n { key: 'ctrl+m', label: 'model' },\n { key: 'ctrl+,', label: 'settings' },\n { key: 'esc', label: 'sessions' },\n ]\n}\n","import type { FiletypeParserOptions } from '@opentui/core'\nimport { addDefaultParsers, getTreeSitterClient } from '@opentui/core'\n\n/**\n * Register Tree-sitter parsers for the languages we'd like highlighted\n * inside fenced markdown code blocks.\n *\n * OpenTUI ships JS/TS/Markdown/Zig out of the box. Anything else needs a\n * Tree-sitter `.wasm` grammar + a `highlights.scm` capture query. We fetch\n * both from the upstream Tree-sitter grammar repos; OpenTUI's worker\n * caches them under its data path (`~/.local/share/opentui/...` by\n * default) so the download is a one-shot cost per language per machine.\n *\n * `aliases` lets a single grammar handle multiple fence info-strings — e.g.\n * `bash` also matches ` ```sh ` and ` ```shell `. The model picks fences\n * inconsistently across providers; aliases save us from missing highlights\n * on synonyms.\n *\n * Runtime caveats:\n * - **First use** of a language triggers an HTTPS download. Subsequent\n * uses (same machine, same data path) are instant.\n * - **Compiled binaries** (`bun --compile`) still work — the data path\n * is a writable OS dir, not the bunfs. Air-gapped deployments would\n * need to either pre-populate the cache or migrate to local-file\n * vendoring via `with { type: 'file' }` imports (see\n * https://opentui.com/docs/reference/tree-sitter/#use-local-files).\n * - If a download fails (offline / firewall), the language renders as\n * plain `markup.raw.block` — no crash, just no syntax color.\n *\n * Versions are pinned in the WASM URLs so a grammar repo's `master`\n * landing a breaking change can't silently affect us.\n */\nconst EXTRA_PARSERS: FiletypeParserOptions[] = [\n {\n filetype: 'python',\n aliases: ['py'],\n wasm: 'https://github.com/tree-sitter/tree-sitter-python/releases/download/v0.23.6/tree-sitter-python.wasm',\n queries: {\n highlights: ['https://raw.githubusercontent.com/tree-sitter/tree-sitter-python/v0.23.6/queries/highlights.scm'],\n },\n },\n {\n filetype: 'bash',\n aliases: ['sh', 'shell', 'zsh'],\n wasm: 'https://github.com/tree-sitter/tree-sitter-bash/releases/download/v0.23.3/tree-sitter-bash.wasm',\n queries: {\n highlights: ['https://raw.githubusercontent.com/tree-sitter/tree-sitter-bash/v0.23.3/queries/highlights.scm'],\n },\n },\n {\n filetype: 'json',\n wasm: 'https://github.com/tree-sitter/tree-sitter-json/releases/download/v0.24.8/tree-sitter-json.wasm',\n queries: {\n highlights: ['https://raw.githubusercontent.com/tree-sitter/tree-sitter-json/v0.24.8/queries/highlights.scm'],\n },\n },\n {\n filetype: 'rust',\n aliases: ['rs'],\n wasm: 'https://github.com/tree-sitter/tree-sitter-rust/releases/download/v0.23.2/tree-sitter-rust.wasm',\n queries: {\n highlights: ['https://raw.githubusercontent.com/tree-sitter/tree-sitter-rust/v0.23.2/queries/highlights.scm'],\n },\n },\n {\n filetype: 'go',\n aliases: ['golang'],\n wasm: 'https://github.com/tree-sitter/tree-sitter-go/releases/download/v0.23.4/tree-sitter-go.wasm',\n queries: {\n highlights: ['https://raw.githubusercontent.com/tree-sitter/tree-sitter-go/v0.23.4/queries/highlights.scm'],\n },\n },\n {\n filetype: 'yaml',\n aliases: ['yml'],\n wasm: 'https://github.com/tree-sitter-grammars/tree-sitter-yaml/releases/download/v0.7.0/tree-sitter-yaml.wasm',\n queries: {\n highlights: ['https://raw.githubusercontent.com/tree-sitter-grammars/tree-sitter-yaml/v0.7.0/queries/highlights.scm'],\n },\n },\n {\n filetype: 'html',\n aliases: ['htm'],\n wasm: 'https://github.com/tree-sitter/tree-sitter-html/releases/download/v0.23.2/tree-sitter-html.wasm',\n queries: {\n highlights: ['https://raw.githubusercontent.com/tree-sitter/tree-sitter-html/v0.23.2/queries/highlights.scm'],\n },\n },\n {\n filetype: 'css',\n wasm: 'https://github.com/tree-sitter/tree-sitter-css/releases/download/v0.23.2/tree-sitter-css.wasm',\n queries: {\n highlights: ['https://raw.githubusercontent.com/tree-sitter/tree-sitter-css/v0.23.2/queries/highlights.scm'],\n },\n },\n]\n\nlet registered = false\n\n/**\n * Register the extra Tree-sitter parsers + start the worker. Idempotent —\n * subsequent calls are no-ops. Safe to invoke from `runTui()` and from\n * composition hosts that mount `<App>` directly.\n */\nexport async function setupTreeSitter(): Promise<void> {\n if (registered)\n return\n registered = true\n addDefaultParsers(EXTRA_PARSERS)\n // Initialize the global client. The actual grammar `.wasm` downloads are\n // lazy — they happen on the first fence of each language — but the worker\n // needs to be alive before any markdown renders.\n await getTreeSitterClient().initialize()\n}\n","import type { ChatOptions } from '../chat/config'\nimport { createCliRenderer } from '@opentui/core'\nimport { createRoot } from '@opentui/react'\nimport { resolveConfig } from '../chat/config'\nimport { App } from './app'\nimport { setupTreeSitter } from './tree-sitter'\n\n// ---------------------------------------------------------------------------\n// Public API\n//\n// `runTui(options)` is the one-shot launcher. For consumers who want to embed\n// the App inside a renderer they already control, the OpenTUI-specific\n// building blocks below are also exported individually.\n//\n// The renderer-agnostic engine (auth, credentials, providers, safe-mode,\n// store, streaming, settings, theme palette, formatters, markdown healer,\n// `resolveConfig`, …) lives in `zidane/chat`. Import from there directly if\n// you're building a non-TUI shell.\n// ---------------------------------------------------------------------------\n\n/**\n * Tracks whether `runTui` has been invoked in this process. `createCliRenderer`\n * installs signal handlers + hijacks stdin; calling it a second time produces\n * undefined behavior. We bail loudly instead.\n */\nlet runTuiInvoked = false\n\n/**\n * Boot a full chat TUI with sensible defaults. **Does not return** under\n * normal use — it terminates the host process via `process.exit(0)` once\n * the user dismisses the renderer (Ctrl+C / Esc on the auth screen / a\n * non-zero exit code is mapped via the `catch` path below).\n *\n * **One-shot:** this function may only be invoked once per process. The\n * underlying OpenTUI renderer wires up global terminal state on init.\n *\n * **Why it exits the process:** after the renderer's `destroy()` restores\n * the terminal, React's reconciler and OpenTUI's internal listeners can\n * keep the Node/Bun event loop open indefinitely — the script appears to\n * hang in `bun run`, and under `bun --watch run` the watcher waits forever\n * for the child to exit. Forcing a clean exit here is the contract that\n * makes `runTui` a true one-shot launcher.\n *\n * Hosts that need post-renderer cleanup should mount `<App config={...} />`\n * against their own `createCliRenderer()` instead of calling `runTui()`.\n *\n * Env-var overrides (handy when launching from CI or restricted shells):\n * - `ZIDANE_STORAGE_DIR` — sets `storageDir`\n * - `ZIDANE_PREFIX` — sets `prefix`\n *\n * Hosts building on top of `zidane/tui` typically want their own env vars\n * (e.g. `MYAPP_STORAGE_DIR`); read them in your launch script and forward\n * to `runTui({ storageDir, prefix })`.\n *\n * ```ts\n * import { BUILTIN_PROVIDERS } from 'zidane/chat'\n * import { runTui } from 'zidane/tui'\n * import { createRemoteStore } from 'zidane/session' // for the `store` option\n *\n * await runTui() // ~/.zidane/sessions.db + state.json\n * await runTui({ prefix: '.myapp' }) // ~/.myapp/...\n * await runTui({ storageDir: '/data', prefix: 'myapp' })\n * await runTui({ providers: { ...BUILTIN_PROVIDERS, mine: myDescriptor } })\n * await runTui({ store: createRemoteStore({ url: '…' }) })\n * ```\n */\nexport async function runTui(options: ChatOptions = {}): Promise<never> {\n if (runTuiInvoked) {\n throw new Error(\n 'runTui() can only be invoked once per process. '\n + 'Compose `<App config={resolveConfig(...)} />` against your own renderer '\n + 'if you need to run multiple TUIs in the same lifetime.',\n )\n }\n runTuiInvoked = true\n\n // Register extra Tree-sitter parsers (python, bash, json, rust, go,\n // yaml, html, css) so fenced code blocks in those languages get\n // syntax highlighting. The grammar `.wasm` files are downloaded\n // lazily on first use and cached under OpenTUI's data path. Swallow\n // init errors — if the worker can't start (offline, sandboxed FS),\n // the renderer still works, those fences just render as plain text.\n await setupTreeSitter().catch((err) => {\n const cause = err instanceof Error ? err.message : String(err)\n process.stderr.write(`[zidane/tui] tree-sitter setup failed: ${cause}\\n`)\n })\n\n // `resolveConfig` wires `ZIDANE_CREDENTIALS_PATH` and applies stored API\n // keys into `process.env` internally — both happen before any provider\n // factory is constructed, so the wizard can render even on fresh installs\n // where the harness providers would otherwise throw \"No API key found\".\n const config = resolveConfig(options)\n\n // Promise + explicit resolver: avoid the `let done!: ...` non-null trick.\n let done: () => void = () => {}\n const exited = new Promise<void>((resolve) => { done = resolve })\n\n const renderer = await createCliRenderer({\n exitOnCtrlC: true,\n onDestroy: () => done(),\n })\n\n createRoot(renderer).render(<App config={config} />)\n\n await exited\n\n // Force a clean exit. See JSDoc above for rationale — without this the\n // process hangs after the renderer's `destroy()` returns, which manifests\n // as a stuck `bun --watch` parent and a terminal that needs a second Ctrl+C\n // to actually surrender control.\n process.exit(0)\n}\n\n// ---------------------------------------------------------------------------\n// TUI-specific composition exports.\n//\n// Renderer-agnostic engine (auth, store, safe-mode, settings, providers,\n// streaming hooks, theme palette, …) is in `zidane/chat`. Don't re-export it\n// from here — keep `zidane/tui` focused on OpenTUI presentation primitives so\n// non-TUI consumers (a GUI, an SDK, a CI script) never pull OpenTUI in.\n// ---------------------------------------------------------------------------\n\nexport { App } from './app'\nexport {\n type ContextUsage,\n Footer,\n type Hint,\n isVisible,\n marginTopFor,\n onInputSubmit,\n Spinner,\n Transcript,\n} from './components'\nexport { Modal, type ModalProps, ModalRoot, useModal, useModalAwareFocus } from './modal'\nexport { ModelPickerModal } from './model-picker'\nexport { AuthScreen, ChatScreen, SessionsScreen } from './screens'\nexport { type SettingsActions, SettingsModal } from './settings-modal'\nexport { buildMdStyle, useMdStyle } from './theme'\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"tui.js","names":["VISIBLE_ROW_CAP","EmptyState","EmptyState"],"sources":["../src/tui/modal.tsx","../src/tui/agent-picker.tsx","../src/tui/theme.ts","../src/tui/components.tsx","../src/tui/model-picker.tsx","../src/tui/screens.tsx","../src/tui/settings-modal.tsx","../src/tui/app.tsx","../src/tui/tree-sitter.ts","../src/tui/index.tsx"],"sourcesContent":["/** @jsxImportSource @opentui/react */\nimport type { ReactNode } from 'react'\nimport { useKeyboard, useTerminalDimensions } from '@opentui/react'\nimport { createContext, useContext, useMemo, useState } from 'react'\nimport { useColors, useSurfaces } from '../chat/theme-context'\n\n// ---------------------------------------------------------------------------\n// Modal layer\n//\n// A single, app-wide overlay slot. `ModalRoot` renders the active node on top\n// of its children via OpenTUI's `position: 'absolute'` so the host UI stays\n// mounted underneath (state preserved, no remount on close).\n//\n// Components anywhere in the tree call `useModal().open(...)` to push a node\n// and `.close()` to dismiss. Escape is captured by `Modal` itself when it's\n// open, so the App's top-level keyboard handler doesn't fight it.\n// ---------------------------------------------------------------------------\n\ninterface ModalApi {\n open: (node: ReactNode) => void\n close: () => void\n isOpen: boolean\n}\n\nconst ModalContext = createContext<ModalApi | null>(null)\n\nexport function ModalRoot({ children }: { children: ReactNode }) {\n const [active, setActive] = useState<ReactNode | null>(null)\n\n const api = useMemo<ModalApi>(() => ({\n open: node => setActive(node),\n close: () => setActive(null),\n get isOpen() { return active !== null },\n }), [active])\n\n return (\n <ModalContext.Provider value={api}>\n <box style={{ flexDirection: 'column', flexGrow: 1 }}>\n {children}\n </box>\n {active && (\n // Transparent backdrop — host UI remains visible behind. The modal is\n // opaque (see <Modal/>) and sits centered; the dimming the eye perceives\n // comes from the modal's solid panel against the surrounding content,\n // not from a backdrop fill (OpenTUI cells have no alpha to blend with).\n <box\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n alignItems: 'center',\n justifyContent: 'center',\n zIndex: 100,\n }}\n >\n {active}\n </box>\n )}\n </ModalContext.Provider>\n )\n}\n\nexport function useModal(): ModalApi {\n const ctx = useContext(ModalContext)\n if (!ctx)\n throw new Error('useModal must be used inside <ModalRoot>')\n return ctx\n}\n\n/**\n * Focus computed against the modal layer.\n *\n * Pass a component's preferred focus state and this returns `false` whenever a\n * modal is open — so focused inputs (textarea, selects) release their focus and\n * stop intercepting keys behind the overlay. Pair with `focusable={false}` on\n * \"passive\" focusables (scrollbox) so the renderer doesn't cycle focus into\n * them when the primary input blurs.\n */\nexport function useModalAwareFocus(preferred: boolean = true): boolean {\n const { isOpen } = useModal()\n return preferred && !isOpen\n}\n\n// ---------------------------------------------------------------------------\n// Modal — bordered content panel with built-in esc-to-close keyboard handling.\n//\n// Consumers pass their own content (forms, selects, lists). The modal owns:\n// - border + title + minimal padding\n// - sizing constraints (width caps at a comfortable column width)\n// - escape key → onClose\n// ---------------------------------------------------------------------------\n\nexport interface ModalProps {\n title?: string\n /** Called when the user presses esc. Defaults to `useModal().close()` if available. */\n onClose?: () => void\n children: ReactNode\n /** Preferred width in columns. Modal grows to this when the terminal allows. */\n maxWidth?: number\n /** Floor on the width, so content stays minimally legible. */\n minWidth?: number\n /** Columns of breathing room kept on each side of the modal. */\n horizontalMargin?: number\n}\n\n/**\n * Responsive modal — picks a width based on the live terminal size.\n *\n * - On a wide terminal, the modal grows to `maxWidth` so descriptions sit on\n * one line and don't wrap.\n * - On a narrow terminal, the modal shrinks down to `minWidth`, keeping a\n * small horizontal margin from the screen edges. Text inside wraps naturally.\n *\n * Uses `useTerminalDimensions()` so it reflows on `SIGWINCH` without remount.\n */\nexport function Modal({\n title,\n onClose,\n children,\n maxWidth = 92,\n minWidth = 44,\n horizontalMargin = 4,\n}: ModalProps) {\n const ctx = useContext(ModalContext)\n const dismiss = onClose ?? ctx?.close\n const COLOR = useColors()\n const SURFACE = useSurfaces()\n\n useKeyboard((key) => {\n if (key.name === 'escape')\n dismiss?.()\n })\n\n const { width: termWidth } = useTerminalDimensions()\n const width = Math.max(minWidth, Math.min(maxWidth, termWidth - horizontalMargin * 2))\n\n return (\n <box\n title={title ? ` ${title} ` : undefined}\n style={{\n border: true,\n borderColor: COLOR.borderActive,\n backgroundColor: SURFACE.modal,\n paddingTop: 1,\n paddingBottom: 1,\n paddingLeft: 2,\n paddingRight: 2,\n width,\n flexDirection: 'column',\n gap: 1,\n }}\n >\n {children}\n </box>\n )\n}\n","/** @jsxImportSource @opentui/react */\nimport type { AgentAccent, AgentProfile, AgentRegistry } from '../chat/agents'\nimport { useMemo } from 'react'\nimport { useColors, useSelectStyle } from '../chat/theme-context'\nimport { Modal } from './modal'\n\n/** Cap the scroll window — a long custom registry shouldn't push the modal off-screen. */\nconst VISIBLE_ROW_CAP = 10\n\n/**\n * Modal that lists the registered {@link AgentProfile}s and lets the user\n * pick one. Rows show: `● selected · label description`.\n *\n * The accent column is intentionally compact (single-char marker) — the\n * profile's `accent` color is read from the active theme so Build and Plan\n * stand apart at a glance without taking horizontal space.\n *\n * Used by `App` (Ctrl+A binding) and exported for hosts that want to drive\n * agent switching from elsewhere in their own composition.\n */\nexport function AgentPickerModal({\n agents,\n currentAgentId,\n onPick,\n}: {\n agents: AgentRegistry\n currentAgentId: string\n onPick: (id: string) => void\n}) {\n const COLOR = useColors()\n const SELECT_THEME = useSelectStyle()\n\n const profiles = useMemo(() => Object.values(agents), [agents])\n\n const initialIndex = useMemo(\n () => profiles.findIndex(p => p.id === currentAgentId),\n [profiles, currentAgentId],\n )\n\n const options = useMemo(\n () => profiles.map(p => ({\n name: `${p.id === currentAgentId ? '● ' : ' '}${p.label}`,\n description: p.description,\n value: p.id,\n })),\n [profiles, currentAgentId],\n )\n\n if (profiles.length === 0)\n return <EmptyState />\n\n const visibleRows = Math.min(options.length, VISIBLE_ROW_CAP)\n const currentMissing = initialIndex < 0\n // `<select>` requires a non-negative index; clamp here and surface the\n // mismatch via a banner so the user knows their persisted profile is gone.\n const safeIndex = currentMissing ? 0 : initialIndex\n\n return (\n <Modal title=\"select agent\">\n {currentMissing && (\n <text fg={COLOR.warn}>\n {`Current agent \"${currentAgentId}\" is not in this registry — pick one below.`}\n </text>\n )}\n <select\n {...SELECT_THEME}\n focused\n options={options}\n wrapSelection\n selectedIndex={safeIndex}\n showScrollIndicator={options.length > visibleRows}\n style={{ height: visibleRows }}\n onSelect={(_idx, option) => {\n if (option)\n onPick(option.value as string)\n }}\n />\n <text fg={COLOR.mute}>\n <span fg={COLOR.warn}>↑↓</span>\n {' navigate · '}\n <span fg={COLOR.warn}>↵</span>\n {' select · '}\n <span fg={COLOR.warn}>esc</span>\n {' close'}\n </text>\n </Modal>\n )\n}\n\nfunction EmptyState() {\n const COLOR = useColors()\n return (\n <Modal title=\"select agent\">\n <text fg={COLOR.dim}>No agents registered.</text>\n <text fg={COLOR.mute}>\n Pass an\n <span fg={COLOR.model}>{' agents '}</span>\n registry to\n <span fg={COLOR.model}>{' runTui({ agents }) '}</span>\n to populate this list.\n </text>\n </Modal>\n )\n}\n\n/**\n * Resolve a profile's `accent` token to a concrete theme color via the\n * caller's color palette. Exposed for the Footer badge so all surfaces\n * stay in sync with the picker's row tinting.\n */\nexport function accentColor(\n accent: AgentAccent | undefined,\n COLOR: { brand: string, accent: string, warn: string, model: string },\n): string {\n switch (accent) {\n case 'brand': return COLOR.brand\n case 'warn': return COLOR.warn\n case 'model': return COLOR.model\n case 'accent':\n default:\n return COLOR.accent\n }\n}\n\n/** Re-export so consumers don't need to import the type from `zidane/chat` separately. */\nexport type { AgentProfile }\n","import type { ReactNode } from 'react'\nimport type { Theme } from '../chat/theme'\nimport { RGBA, SyntaxStyle } from '@opentui/core'\nimport { createContext, createElement, useContext, useMemo } from 'react'\nimport { useTheme } from '../chat/theme-context'\n\n/**\n * Convert the renderer-agnostic `Theme.syntax` map (hex strings + plain\n * booleans) into an OpenTUI `SyntaxStyle`. Used both for the markdown\n * structural captures (`markup.heading`, `markup.bold`, …) and the\n * embedded Tree-sitter language tokens (`keyword`, `string`, `function`,\n * …) — OpenTUI's `<markdown>` re-uses the same `SyntaxStyle` for the\n * fenced-code renderable, so one table drives both surfaces.\n */\nexport function buildMdStyle(theme: Theme): SyntaxStyle {\n interface Style {\n fg?: RGBA\n bg?: RGBA\n bold?: boolean\n italic?: boolean\n underline?: boolean\n dim?: boolean\n }\n const styles: Record<string, Style> = {}\n for (const [token, style] of Object.entries(theme.syntax)) {\n const out: Style = {}\n if (style.fg)\n out.fg = RGBA.fromHex(style.fg)\n if (style.bg)\n out.bg = RGBA.fromHex(style.bg)\n if (style.bold)\n out.bold = true\n if (style.italic)\n out.italic = true\n if (style.underline)\n out.underline = true\n if (style.dim)\n out.dim = true\n styles[token] = out\n }\n return SyntaxStyle.fromStyles(styles)\n}\n\n// ---------------------------------------------------------------------------\n// MdStyleProvider\n//\n// `SyntaxStyle.fromStyles(...)` allocates a sizable, pure-function-of-theme\n// object that every `<markdown>` instance can share. Building it inside a\n// per-instance `useMemo` (the previous approach) re-ran the conversion N\n// times on a theme switch — N being the number of markdown blocks in the\n// active transcript, easily 50+ on a long session.\n//\n// This context computes it once per theme and broadcasts the result to\n// every `useMdStyle()` consumer. Mounted by `ThemedShell` in app.tsx,\n// directly underneath `ThemeProvider` so the upstream theme is in scope.\n// ---------------------------------------------------------------------------\n\nconst MdStyleContext = createContext<SyntaxStyle | null>(null)\n\nexport function MdStyleProvider({ children }: { children: ReactNode }) {\n const theme = useTheme()\n const style = useMemo(() => buildMdStyle(theme), [theme])\n // Authored with `createElement` rather than JSX so this module stays a\n // plain `.ts` file — keeps the OpenTUI `jsxImportSource` pragma noise\n // out of a one-element provider definition.\n return createElement(MdStyleContext.Provider, { value: style }, children)\n}\n\n/**\n * Active markdown / syntax-highlighting style. Returns a single shared\n * `SyntaxStyle` instance for the active theme — built once at provider\n * mount, re-built on theme switch. A `Settings.theme` flip re-paints every\n * `<markdown>` that reads this hook.\n *\n * Throws if used outside `<MdStyleProvider>` so a missing wiring shows up\n * loudly in development rather than silently rendering plain text.\n */\nexport function useMdStyle(): SyntaxStyle {\n const style = useContext(MdStyleContext)\n if (!style)\n throw new Error('useMdStyle must be used inside <MdStyleProvider>')\n return style\n}\n","/** @jsxImportSource @opentui/react */\nimport type { AgentProfile } from '../chat/agents'\nimport type { Picked, Settings, StreamEvent } from '../chat/types'\nimport { useTerminalDimensions } from '@opentui/react'\nimport { memo, useEffect, useMemo, useState } from 'react'\nimport { fmtTokens } from '../chat/format'\nimport { useColors } from '../chat/theme-context'\nimport { accentColor } from './agent-picker'\nimport { useMdStyle } from './theme'\n\n/**\n * Memoized so a flush that mutates only the trailing event doesn't force the\n * entire transcript to re-render. Each event holds a stable reference until\n * its content changes (we only ever recreate the streaming-markdown tail).\n *\n * The outer wrapper handles top-margin per kind (and per neighbor) so spacing\n * is the single source of truth for inter-event breathing room.\n */\nconst EventLine = memo(\n ({ event, previous, depthOffset = 0 }: {\n event: StreamEvent\n previous?: StreamEvent\n /**\n * Subtract from the event's depth before computing left indent. The\n * subagent box already provides one level of visual offset via its\n * border + padding, so events rendered inside it pass `depthOffset: 1`\n * to avoid double-indenting.\n */\n depthOffset?: number\n }) => (\n <box\n style={{\n marginTop: marginTopFor(event, previous),\n // Pin width to the scrollbox content area + lock the row against\n // shrinking. Without these, a streamed markdown growing taller can\n // make Yoga renegotiate widths for *every* sibling row, which is the\n // visible \"the text jiggles a column to the left, then back\" effect.\n alignSelf: 'stretch',\n flexShrink: 0,\n flexDirection: 'column',\n }}\n >\n <EventLineImpl event={event} depthOffset={depthOffset} />\n </box>\n ),\n)\n\n/**\n * `@opentui/react` extends `React.JSX.IntrinsicElements`, so `onSubmit` on `<input>`\n * gets intersected with the DOM `SubmitEvent` shape and demands an unhelpful overload.\n * The OpenTUI input runtime fires `(value: string) => void`; this helper isolates\n * the cast so each call site stays readable.\n */\nexport function onInputSubmit(handler: (value: string) => void): never {\n return handler as unknown as never\n}\n\n// ---------------------------------------------------------------------------\n// Footer — single status bar: hints on the left, provider info in the middle,\n// context-window indicator on the right.\n// ---------------------------------------------------------------------------\n\nexport interface Hint {\n key: string\n label: string\n}\n\nexport interface ContextUsage {\n used: number\n max: number\n}\n\n/**\n * Footer status bar. Renders as a single row when the terminal is wide enough,\n * otherwise stacks into two rows (agent + hints + provider on top, context\n * bottom-right) and falls back to one segment per row at terminal widths\n * where even that overflows. The width tiering is driven by plain-text\n * length estimates of each segment — close enough since the segments are\n * ASCII-heavy and worst case we under-estimate the breakpoint by a few cells.\n *\n * Agent badge is **always rendered first** so the active profile (Build /\n * Plan / host-custom) is the anchor on the left edge of every layout tier.\n */\nexport function Footer({\n hints,\n picked,\n agent,\n context,\n}: {\n hints: Hint[]\n picked: Picked | null\n /**\n * Active agent profile. When set, renders as the colored leading badge\n * on the bottom bar so the current mode is the first thing the eye lands\n * on. Pass `null` to hide entirely (single-profile registries, or\n * non-chat screens where switching is meaningless).\n */\n agent: AgentProfile | null\n context: ContextUsage | null\n}) {\n const { width } = useTerminalDimensions()\n\n // 1ch padding on each side. Plus 1ch breathing room between left block and\n // right block in single-row layout (the spacer's minimum width).\n const inner = Math.max(0, width - 2)\n const hW = hintsLength(hints)\n const aW = agent ? agentBadgeLength(agent) : 0\n const pW = picked ? providerBadgeLength(picked) : 0\n const cW = context ? contextIndicatorLength(context) : 0\n\n const oneRowFits = aW + hW + pW + (cW > 0 ? cW + 1 : 0) <= inner\n const leftRowFits = aW + hW + pW <= inner\n\n if (oneRowFits) {\n return (\n <box style={{ flexDirection: 'row', height: 1, paddingLeft: 1, paddingRight: 1 }}>\n {agent && <AgentBadge agent={agent} position=\"leading\" />}\n <HintsText hints={hints} />\n {picked && <ProviderBadge picked={picked} />}\n <box style={{ flexGrow: 1 }} />\n {context && <ContextIndicator context={context} />}\n </box>\n )\n }\n\n // Two rows: agent + hints + provider on top, context bottom-right.\n if (leftRowFits) {\n return (\n <box style={{ flexDirection: 'column', paddingLeft: 1, paddingRight: 1 }}>\n <box style={{ flexDirection: 'row', height: 1 }}>\n {agent && <AgentBadge agent={agent} position=\"leading\" />}\n <HintsText hints={hints} />\n {picked && <ProviderBadge picked={picked} />}\n </box>\n {context && (\n <box style={{ flexDirection: 'row', height: 1 }}>\n <box style={{ flexGrow: 1 }} />\n <ContextIndicator context={context} />\n </box>\n )}\n </box>\n )\n }\n\n // One segment per row. The provider / agent badges' inline separators\n // would dangle on a standalone row, so render them with the `standalone`\n // position which drops them.\n return (\n <box style={{ flexDirection: 'column', paddingLeft: 1, paddingRight: 1 }}>\n {agent && (\n <box style={{ flexDirection: 'row', height: 1 }}>\n <AgentBadge agent={agent} position=\"standalone\" />\n </box>\n )}\n <box style={{ flexDirection: 'row', height: 1 }}>\n <HintsText hints={hints} />\n </box>\n {picked && (\n <box style={{ flexDirection: 'row', height: 1 }}>\n <ProviderBadge picked={picked} standalone />\n </box>\n )}\n {context && (\n <box style={{ flexDirection: 'row', height: 1 }}>\n <ContextIndicator context={context} />\n </box>\n )}\n </box>\n )\n}\n\nfunction HintsText({ hints }: { hints: Hint[] }) {\n const COLOR = useColors()\n return (\n <text fg={COLOR.dim}>\n {hints.map((h, i) => (\n <span key={i}>\n {i > 0 && <span fg={COLOR.mute}> · </span>}\n <span fg={COLOR.warn}>{h.key}</span>\n <span fg={COLOR.dim}>{` ${h.label}`}</span>\n </span>\n ))}\n </text>\n )\n}\n\n/**\n * Colored badge for the active {@link AgentProfile}. The badge anchors the\n * left edge of the bottom bar so the current mode (Build / Plan / host\n * custom) is the first thing the eye lands on. Accent color comes from\n * the profile's `accent` token, resolved against the active theme via\n * `accentColor()` so picker rows and footer tinting stay in sync.\n *\n * Two render positions:\n *\n * - `leading` (default) — in-row at the very start. Renders just the\n * label and a trailing ` · ` separator, e.g. `Build · `. Tight,\n * prominent, and visually distinct from the surrounding hint text.\n * - `standalone` — one-row-per-segment fallback for narrow terminals.\n * Renders `agent <label>` with no separators so the row reads\n * correctly on its own.\n */\nfunction AgentBadge({\n agent,\n position = 'leading',\n}: {\n agent: AgentProfile\n position?: 'leading' | 'standalone'\n}) {\n const COLOR = useColors()\n const fg = accentColor(agent.accent, COLOR)\n if (position === 'standalone') {\n return (\n <text fg={COLOR.dim}>\n <span fg={COLOR.mute}>agent </span>\n <span fg={fg}>{agent.label}</span>\n </text>\n )\n }\n return (\n <text fg={COLOR.dim}>\n <span fg={fg}>{agent.label}</span>\n <span fg={COLOR.mute}>{' · '}</span>\n </text>\n )\n}\n\nfunction ProviderBadge({ picked, standalone = false }: { picked: Picked, standalone?: boolean }) {\n const COLOR = useColors()\n const source = picked.provider.methods[0].source\n return (\n <text fg={COLOR.dim}>\n {!standalone && <span fg={COLOR.mute}> · </span>}\n <span fg={COLOR.accent}>{picked.provider.label}</span>\n <span fg={COLOR.mute}> · </span>\n <span fg={COLOR.model}>{picked.model}</span>\n <span fg={COLOR.mute}> · </span>\n <span fg={source === 'oauth' ? COLOR.accent : COLOR.warn}>{source}</span>\n </text>\n )\n}\n\nfunction ContextIndicator({ context }: { context: ContextUsage }) {\n const COLOR = useColors()\n const ratio = context.max > 0 ? context.used / context.max : 0\n const pct = Math.round(ratio * 100)\n const color = ratio >= 0.85 ? COLOR.error : ratio >= 0.6 ? COLOR.warn : COLOR.dim\n return (\n <text fg={COLOR.dim}>\n <span fg={COLOR.mute}>ctx </span>\n <span fg={color}>{fmtTokens(context.used)}</span>\n <span fg={COLOR.mute}>{` / ${fmtTokens(context.max)} `}</span>\n <span fg={color}>{`(${pct}%)`}</span>\n </text>\n )\n}\n\n// ---------------------------------------------------------------------------\n// Plain-text width estimates for the footer's responsive tiering.\n//\n// We approximate with `.length` instead of `stringWidth` because every glyph\n// involved (hint keys, provider names, model ids, token counts) is either\n// ASCII or a 1-cell symbol. The estimates only need to be precise enough to\n// pick a layout tier — being off by a column at the breakpoint is harmless.\n// ---------------------------------------------------------------------------\n\nfunction hintsLength(hints: Hint[]): number {\n if (hints.length === 0)\n return 0\n // \" · \" between hints, \"<key> <label>\" per hint.\n return hints.reduce(\n (sum, h, i) => sum + h.key.length + 1 + h.label.length + (i > 0 ? 3 : 0),\n 0,\n )\n}\n\nfunction providerBadgeLength(picked: Picked): number {\n const source = picked.provider.methods[0].source\n // Inline form: \" · <provider> · <model> · <source>\".\n return 3 + picked.provider.label.length + 3 + picked.model.length + 3 + source.length\n}\n\nfunction agentBadgeLength(agent: AgentProfile): number {\n // Leading form: \"<label> · \". The standalone form (\"agent <label>\") is\n // only used in the narrow-terminal fallback where everything lives on\n // its own row — the leading-form estimate is what gates layout-tier\n // selection above.\n return agent.label.length + 3\n}\n\nfunction contextIndicatorLength(context: ContextUsage): number {\n const ratio = context.max > 0 ? context.used / context.max : 0\n const pct = Math.round(ratio * 100)\n // \"ctx <used> / <max> (<pct>%)\"\n return 4 + fmtTokens(context.used).length + 3 + fmtTokens(context.max).length\n + 2 + String(pct).length + 2\n}\n\n// ---------------------------------------------------------------------------\n// Spinner — animated braille used while a run is streaming.\n// ---------------------------------------------------------------------------\n\nconst SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']\nconst SPINNER_INTERVAL_MS = 80\n\nexport function Spinner({ label }: { label: string }) {\n const [frame, setFrame] = useState(0)\n const COLOR = useColors()\n\n useEffect(() => {\n const id = setInterval(() => setFrame(f => (f + 1) % SPINNER_FRAMES.length), SPINNER_INTERVAL_MS)\n return () => clearInterval(id)\n }, [])\n\n return (\n <text fg={COLOR.warn}>\n {SPINNER_FRAMES[frame]}\n <span fg={COLOR.dim}>{` ${label}`}</span>\n </text>\n )\n}\n\n// ---------------------------------------------------------------------------\n// Transcript — scrollbox with sticky-bottom and structured event rendering.\n// ---------------------------------------------------------------------------\n\nexport function Transcript({ events, settings }: { events: StreamEvent[], settings: Settings }) {\n const items = useMemo(() => partitionTranscript(events, settings), [events, settings])\n\n if (items.length === 0)\n return <EmptyState />\n\n return (\n <scrollbox\n // Never claim keyboard focus: the textarea is the chat's primary input,\n // so up/down/page-up/page-down should stay with it (or with the modal\n // when one is open). Mouse-wheel scrolling still works.\n focusable={false}\n style={{ flexGrow: 1, paddingLeft: 1, paddingRight: 1 }}\n stickyScroll\n stickyStart=\"bottom\"\n >\n {items.map((item, i) => (\n item.kind === 'event'\n ? <EventLine key={i} event={item.event} previous={item.previous} />\n : <SubagentBlock key={i} events={item.events} previous={item.previous} />\n ))}\n </scrollbox>\n )\n}\n\n/**\n * Per-event visibility — filters honor user toggles and the\n * `hideSubagentOutput` setting. When subagent output is hidden:\n * - Child-agent events are filtered down to the `spawn-start` /\n * `spawn-end` markers so the user still sees \"🌱 working… 🌳 done\".\n * - The parent's `tool-result` for `spawn` is hidden too. Its body\n * duplicates `spawn-end`'s stats line *and* the parent's next markdown\n * turn (\"Here's what the sub-agent found: …\"). Showing it again\n * produced an extra `┃ [sub-agent child-1] Completed …` block that\n * the user just wanted gone.\n *\n * Exported so the visibility matrix can be unit-tested without rendering.\n */\nexport function isVisible(event: StreamEvent, settings: Settings): boolean {\n if (settings.hideSubagentOutput) {\n if (isChild(event))\n return event.kind === 'spawn-start' || event.kind === 'spawn-end'\n if (event.kind === 'tool-result' && event.tool === 'spawn')\n return false\n }\n switch (event.kind) {\n case 'thinking': return settings.showThinking\n case 'tool': return settings.showToolCalls\n case 'tool-result': return settings.showToolResults\n default: return true\n }\n}\n\n/**\n * Output of `partitionTranscript`. Single events render as a normal\n * `EventLine`; child-event runs render as a bordered subagent box.\n */\ntype TranscriptItem\n = | { kind: 'event', event: StreamEvent, previous?: StreamEvent }\n | { kind: 'child-run', events: StreamEvent[], previous?: StreamEvent }\n\n/**\n * Walk the visible-event list once and group consecutive child events\n * (`depth > 0`) into runs so we can wrap each run in a single bordered\n * subagent box.\n *\n * When `hideSubagentOutput` is on, `isVisible` already filters most child\n * events out; the surviving `spawn-start` / `spawn-end` markers render as\n * plain entries (no boxing) — there's nothing meaningful to box.\n */\nfunction partitionTranscript(events: StreamEvent[], settings: Settings): TranscriptItem[] {\n const visible = events.filter(e => isVisible(e, settings))\n if (visible.length === 0)\n return []\n\n // Hide-mode: spawn-start/end are the only child events left. Don't box\n // them — they're already standalone \"subagent N is working/done\" lines.\n if (settings.hideSubagentOutput) {\n return visible.map((event, i) => ({ kind: 'event', event, previous: visible[i - 1] }))\n }\n\n const items: TranscriptItem[] = []\n let run: StreamEvent[] = []\n let runPrevious: StreamEvent | undefined\n\n const flush = () => {\n if (run.length > 0) {\n items.push({ kind: 'child-run', events: run, previous: runPrevious })\n run = []\n runPrevious = undefined\n }\n }\n\n for (let i = 0; i < visible.length; i++) {\n const event = visible[i]\n if (isChild(event)) {\n if (run.length === 0)\n runPrevious = visible[i - 1]\n run.push(event)\n }\n else {\n flush()\n items.push({ kind: 'event', event, previous: visible[i - 1] })\n }\n }\n flush()\n return items\n}\n\n/**\n * Bordered container for one run of subagent events. The box's border +\n * left padding give the visual \"this is a subagent\" affordance, so events\n * inside render with `depthOffset: 1` — a direct child of the parent\n * (depth 1) sits flush against the box's inner padding rather than being\n * indented twice. Grandchildren (depth ≥ 2) still indent further, so\n * nested subagents remain visually distinct.\n */\nfunction SubagentBlock({ events, previous }: { events: StreamEvent[], previous?: StreamEvent }) {\n const COLOR = useColors()\n const childIds = useMemo(() => {\n const set = new Set<string>()\n for (const e of events) {\n if (e.childId)\n set.add(e.childId)\n }\n return Array.from(set)\n }, [events])\n\n const title = childIds.length === 0\n ? ' subagent '\n : childIds.length === 1\n ? ` ${childIds[0]} `\n : ` subagents · ${childIds.join(', ')} `\n\n // Keep the same vertical breathing room a markdown/spawn-start event would\n // get standalone — without this the box looks glued to the parent's tool\n // call above it.\n const marginTop = previous ? 1 : 0\n\n return (\n <box\n title={title}\n style={{\n border: true,\n borderColor: COLOR.mute,\n paddingLeft: 1,\n paddingRight: 1,\n paddingTop: 0,\n paddingBottom: 0,\n marginTop,\n flexDirection: 'column',\n flexShrink: 0,\n alignSelf: 'stretch',\n }}\n >\n {events.map((evt, i) => (\n <EventLine key={i} event={evt} previous={events[i - 1]} depthOffset={1} />\n ))}\n </box>\n )\n}\n\nfunction EmptyState() {\n const COLOR = useColors()\n return (\n <box style={{ flexGrow: 1, alignItems: 'center', justifyContent: 'center' }}>\n <text fg={COLOR.mute}>no messages yet — type below to start</text>\n </box>\n )\n}\n\n// ---------------------------------------------------------------------------\n// EventLine — dispatches per kind. Subagent events render via indented dim\n// blocks; the `depth` field drives the left indent.\n// ---------------------------------------------------------------------------\n\n/** Left-pad applied per depth level (in columns). */\nconst INDENT_PER_DEPTH = 2\n\nfunction indentFor(depth: number | undefined): number {\n return depth && depth > 0 ? depth * INDENT_PER_DEPTH : 0\n}\n\nfunction isChild(event: StreamEvent): boolean {\n return (event.depth ?? 0) > 0\n}\n\n/**\n * Shared row geometry for every transcript event.\n *\n * `alignSelf: 'stretch'` + `flexShrink: 0` together pin each row to the\n * scrollbox's content width and prevent flex re-negotiation when neighboring\n * rows grow or shrink (streaming markdown, late-arriving tool results, etc.).\n * Without this, Yoga is free to re-compute widths every render and the\n * visible text appears to \"wiggle\" between columns as the stream advances.\n */\nfunction rowStyle(paddingLeft: number) {\n return {\n paddingLeft,\n flexDirection: 'column' as const,\n flexShrink: 0,\n alignSelf: 'stretch' as const,\n }\n}\n\n/**\n * Default top-margin per kind. Spacing intent:\n * - `info` / `markdown` / `tool` / `error` / `spawn-start` open a new block\n * so they each get one row of breathing room above.\n * - `thinking` / `tool-result` / `spawn-end` continue the previous block\n * and stay flush.\n *\n * Context-aware overrides live in `marginTopFor` — e.g. consecutive tool\n * round-trips collapse to a tight list regardless of whether outputs are shown.\n */\nconst MARGIN_TOP: Record<StreamEvent['kind'], number> = {\n 'separator': 0,\n 'info': 1,\n 'thinking': 0,\n 'tool': 1,\n 'tool-result': 0,\n 'error': 1,\n 'markdown': 1,\n 'spawn-start': 1,\n 'spawn-end': 0,\n}\n\nconst TOOL_KINDS: ReadonlySet<StreamEvent['kind']> = new Set(['tool', 'tool-result'])\n\n/**\n * Resolve the top margin for an event given the one rendered just before it.\n *\n * Context-aware rules:\n *\n * - A `tool` / `tool-result` event right after another `tool` / `tool-result`\n * collapses to a tight list — call→result pairs and back-to-back calls\n * read as one logical block.\n * - A parent-level event (`depth === 0`) right after a subagent event\n * (`depth > 0`) collapses too. The subagent's `🌳` end marker (and, in\n * show mode, the subagent box's bottom border) already provides the\n * separation; adding the event's default `marginTop` on top would\n * produce the visible \"line jump\" between a subagent's outcome and the\n * parent's follow-up. Either form of marker is enough — we don't want\n * both.\n *\n * Exported so the spacing matrix can be unit-tested without rendering.\n */\nexport function marginTopFor(event: StreamEvent, previous: StreamEvent | undefined): number {\n if (TOOL_KINDS.has(event.kind) && previous && TOOL_KINDS.has(previous.kind))\n return 0\n const eventDepth = event.depth ?? 0\n const previousDepth = previous?.depth ?? 0\n if (eventDepth === 0 && previousDepth > 0)\n return 0\n return MARGIN_TOP[event.kind] ?? 0\n}\n\nfunction EventLineImpl({ event, depthOffset = 0 }: { event: StreamEvent, depthOffset?: number }) {\n const COLOR = useColors()\n const safeText = event.text === '' ? ' ' : event.text\n const effectiveDepth = Math.max(0, (event.depth ?? 0) - depthOffset)\n const row = rowStyle(indentFor(effectiveDepth))\n\n // Subagent text is dimmed across the board so the visual hierarchy is\n // obvious even before reading the indent.\n const child = isChild(event)\n\n switch (event.kind) {\n case 'separator':\n return <text> </text>\n case 'info':\n return <UserPromptBlock text={safeText} />\n case 'thinking':\n return (\n <box style={row}>\n <text fg={COLOR.dim}>{safeText}</text>\n </box>\n )\n case 'tool':\n return (\n <box style={row}>\n <text fg={child ? COLOR.dim : COLOR.model}>\n <span fg={COLOR.mute}>↳ </span>\n {safeText}\n </text>\n </box>\n )\n case 'tool-result':\n return <ToolResultBlock text={event.text} indent={row.paddingLeft} />\n case 'error':\n return (\n <box style={row}>\n <text fg={COLOR.error}>\n <span fg={COLOR.error}>✗ </span>\n {safeText}\n </text>\n </box>\n )\n case 'markdown':\n return (\n <box style={row}>\n <MarkdownBlock text={event.text} streaming={event.streaming ?? false} dim={child} />\n </box>\n )\n case 'spawn-start':\n return (\n <box style={row}>\n <text fg={COLOR.dim}>\n <span fg={COLOR.accent}>🌱 </span>\n <span fg={COLOR.dim}>{`[${event.childId ?? 'child'}] `}</span>\n <span fg={COLOR.dim}>{safeText}</span>\n </text>\n </box>\n )\n case 'spawn-end':\n // Use a 2-cell emoji (like the 🌱 sprout on spawn-start) so the `[…]`\n // label lands in the same column on every row of a subagent's life\n // cycle — the previous `✓` glyph is 1 cell wide and shifted the\n // label one column left of the start marker.\n return (\n <box style={row}>\n <text fg={COLOR.dim}>\n <span fg={COLOR.accent}>🌳 </span>\n <span fg={COLOR.dim}>{`[${event.childId ?? 'child'}] `}</span>\n <span fg={COLOR.mute}>{safeText}</span>\n </text>\n </box>\n )\n default:\n return <text>{safeText}</text>\n }\n}\n\n/** User prompt — bordered to rhyme with the prompt input box below. */\nfunction UserPromptBlock({ text }: { text: string }) {\n const COLOR = useColors()\n return (\n <box\n style={{\n border: true,\n borderColor: COLOR.borderActive,\n paddingLeft: 1,\n paddingRight: 1,\n }}\n >\n <text fg={COLOR.brand}>{text}</text>\n </box>\n )\n}\n\n/**\n * Markdown block. Renders either live-streaming markdown (with `streaming`\n * on, while deltas are still appending) or finalized markdown (after\n * `turn:after`, or every entry on a reloaded transcript).\n *\n * `internalBlockMode` is the load-bearing knob for layout: the OpenTUI\n * default (`\"coalesced\"`) fuses adjacent top-level blocks into one render\n * block, which is the right tradeoff for finalized markdown — fewer flex\n * children, fewer layout passes, the parser already knows the final shape.\n * During streaming, that same coalescing makes earlier paragraphs visually\n * re-flow on every token, so we switch to `\"top-level\"` (each block its\n * own renderable, only the trailing one is unstable).\n *\n * `internalBlockMode` is set only at construction by OpenTUI — there's no\n * setter — so a `<MarkdownBlock>` keeps whichever mode it was born with.\n * Live blocks start `streaming=true` → top-level; reloaded blocks start\n * `streaming=false` → coalesced. Each variant stays optimal for its\n * lifecycle.\n *\n * Note: we don't pre-process unclosed delimiters. OpenTUI's markdown\n * parser already renders partial input reasonably during streaming (the\n * trailing block reflows as tokens close), and the simplicity is worth\n * accepting a brief literal `**` before the closer arrives. Persisted\n * reloads come from completed assistant turns whose markdown is closed.\n */\nfunction MarkdownBlock({ text, streaming, dim }: { text: string, streaming: boolean, dim: boolean }) {\n const COLOR = useColors()\n const mdStyle = useMdStyle()\n return (\n <markdown\n content={text}\n syntaxStyle={mdStyle}\n streaming={streaming}\n internalBlockMode={streaming ? 'top-level' : 'coalesced'}\n fg={dim ? COLOR.dim : undefined}\n />\n )\n}\n\n// ---------------------------------------------------------------------------\n// Tool result — left-bar block, truncated. Caps at TOOL_RESULT_MAX_LINES so a\n// 200-line file doesn't drown the transcript.\n// ---------------------------------------------------------------------------\n\nconst TOOL_RESULT_MAX_LINES = 6\n\nfunction ToolResultBlock({ text, indent }: { text: string, indent: number }) {\n const COLOR = useColors()\n const lines = text.split('\\n')\n const visible = lines.slice(0, TOOL_RESULT_MAX_LINES)\n const omitted = Math.max(0, lines.length - TOOL_RESULT_MAX_LINES)\n\n return (\n <box style={{ paddingLeft: indent, flexDirection: 'column' }}>\n {visible.map((line, i) => (\n <text key={i} fg={COLOR.mute}>\n <span fg={COLOR.borderActive}>┃ </span>\n {line || ' '}\n </text>\n ))}\n {omitted > 0 && (\n <text fg={COLOR.mute}>\n <span fg={COLOR.borderActive}>┃ </span>\n {`… ${omitted} more line${omitted === 1 ? '' : 's'}`}\n </text>\n )}\n </box>\n )\n}\n","/** @jsxImportSource @opentui/react */\nimport type { ModelInfo } from '../chat/providers'\nimport { useMemo } from 'react'\nimport { fmtTokens } from '../chat/format'\nimport { useColors, useSelectStyle } from '../chat/theme-context'\nimport { Modal } from './modal'\n\n/** Cap the visible scroll window so a 30-model list doesn't push the modal off-screen. */\nconst VISIBLE_ROW_CAP = 12\n\n/**\n * Modal that lists the available models for the current provider and lets\n * the user pick one. Options come from the active `ProviderDescriptor` —\n * either its declared `models` list or, when absent, pi-ai's built-in\n * registry looked up via `piProviderId`.\n *\n * Each row shows: `● selected · name (ctx N · reasoning · vision)`.\n */\nexport function ModelPickerModal({\n models,\n currentModelId,\n onPick,\n}: {\n models: readonly ModelInfo[]\n currentModelId: string\n onPick: (modelId: string) => void\n}) {\n const COLOR = useColors()\n const SELECT_THEME = useSelectStyle()\n // Hooks must always run in the same order — keep them above any conditional\n // return. The \"no models\" branch becomes JSX below.\n const initialIndex = useMemo(\n () => models.findIndex(m => m.id === currentModelId),\n [models, currentModelId],\n )\n\n const options = useMemo(\n () => models.map(m => ({\n name: `${m.id === currentModelId ? '● ' : ' '}${m.name ?? m.id}`,\n description: describeModel(m),\n value: m.id,\n })),\n [models, currentModelId],\n )\n\n if (models.length === 0)\n return <EmptyState />\n\n const visibleRows = Math.min(options.length, VISIBLE_ROW_CAP)\n const currentMissing = initialIndex < 0\n // `<select>` requires a non-negative index; clamp here and surface the\n // mismatch via a banner so the user knows their previously-saved model isn't\n // available in the current registry.\n const safeIndex = currentMissing ? 0 : initialIndex\n\n return (\n <Modal title=\"select model\">\n {currentMissing && (\n <text fg={COLOR.warn}>\n {`Current model \"${currentModelId}\" is not in this registry — pick one below to switch.`}\n </text>\n )}\n <select\n {...SELECT_THEME}\n focused\n options={options}\n wrapSelection\n selectedIndex={safeIndex}\n showScrollIndicator={options.length > visibleRows}\n style={{ height: visibleRows }}\n onSelect={(_idx, option) => {\n if (option)\n onPick(option.value as string)\n }}\n />\n <text fg={COLOR.mute}>\n <span fg={COLOR.warn}>↑↓</span>\n {' navigate · '}\n <span fg={COLOR.warn}>↵</span>\n {' select · '}\n <span fg={COLOR.warn}>esc</span>\n {' close'}\n </text>\n </Modal>\n )\n}\n\nfunction EmptyState() {\n const COLOR = useColors()\n return (\n <Modal title=\"select model\">\n <text fg={COLOR.dim}>No models available for this provider.</text>\n <text fg={COLOR.mute}>\n Set\n <span fg={COLOR.model}> models </span>\n on the provider descriptor (or a\n <span fg={COLOR.model}> piProviderId </span>\n that pi-ai recognizes) to populate this list.\n </text>\n </Modal>\n )\n}\n\n/** \"ctx 200k · reasoning · vision\" — compact per-model description. */\nfunction describeModel(m: ModelInfo): string {\n const parts: string[] = [`ctx ${fmtTokens(m.contextWindow)}`]\n if (m.reasoning)\n parts.push('reasoning')\n if (m.input?.includes('image'))\n parts.push('vision')\n return parts.join(' · ')\n}\n","/** @jsxImportSource @opentui/react */\nimport type { InputRenderable, KeyEvent, TextareaRenderable } from '@opentui/core'\nimport type { ReactNode } from 'react'\nimport type { ProviderAuth } from '../chat/auth'\nimport type { ProviderDescriptor } from '../chat/providers'\nimport type { ApprovalDecision, ApprovalRequest } from '../chat/safe-mode-context'\nimport type { SessionMeta, Settings, StreamEvent } from '../chat/types'\nimport { defaultTextareaKeyBindings } from '@opentui/core'\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport { detectAuth } from '../chat/auth'\nimport { useConfig } from '../chat/config-context'\nimport { setProviderCredential } from '../chat/credentials'\nimport { ageString, shortId } from '../chat/format'\nimport { runOAuthLogin, supportsOAuth } from '../chat/oauth'\nimport { suggestSafelistEntry } from '../chat/safe-mode'\nimport { useColors, useSelectStyle } from '../chat/theme-context'\nimport { Spinner, Transcript } from './components'\nimport { useModalAwareFocus } from './modal'\n\n/**\n * Build a key-binding set for the prompt textarea / API-key input. Strips the\n * default `return` action and reinstalls it with our preferred meaning, so the\n * binding wins regardless of modifier state. Pass `allowShiftReturnNewline`\n * to enable `shift+enter` → newline (multi-line input).\n */\nfunction makeSubmitBindings(allowShiftReturnNewline: boolean) {\n const base = defaultTextareaKeyBindings.filter(b => b.name !== 'return')\n return allowShiftReturnNewline\n ? [...base, { name: 'return', action: 'submit' as const }, { name: 'return', shift: true, action: 'newline' as const }]\n : [...base, { name: 'return', action: 'submit' as const }]\n}\n\nconst TEXTAREA_BINDINGS = makeSubmitBindings(true)\nconst API_KEY_INPUT_BINDINGS = makeSubmitBindings(false)\n\n/**\n * Look up a `{ key }` item by the value of a `<select>` option. Used by every\n * screen that mixes keyed-entry rows with sentinel \"+ new\" / \"← back\" rows —\n * sentinel handling stays explicit at the call site, this helper just trims\n * the boilerplate `.find(i => i.key === ...)` typing.\n */\nfunction findByKey<T extends { key: string }>(items: readonly T[], value: unknown): T | undefined {\n return typeof value === 'string' ? items.find(i => i.key === value) : undefined\n}\n\n// ---------------------------------------------------------------------------\n// AuthScreen — first-run provider picker.\n// ---------------------------------------------------------------------------\n\n/**\n * Sentinel value used by the picker's \"+ add / re-configure\" option. Lives\n * outside the provider key namespace (key strings are at least one char, no\n * leading `__`) so we can't collide with a real registry entry.\n */\nconst WIZARD_OPTION_VALUE = '__wizard__'\n\nexport function AuthScreen({ onPick }: { onPick: (p: ProviderAuth) => void }) {\n const config = useConfig()\n const { providers: registry } = config\n const focused = useModalAwareFocus()\n const COLOR = useColors()\n const SELECT_THEME = useSelectStyle()\n\n // `providers` is state, not a memo, so the wizard can imperatively\n // refresh it after writing credentials without us reaching for `useMemo`\n // dep tricks. The effect below seeds it on mount + whenever the registry\n // changes; `refresh` does the wizard-triggered re-detect.\n const [providers, setProviders] = useState<ProviderAuth[]>([])\n const refresh = useCallback(\n () => setProviders(detectAuth(config.paths.dir, registry)),\n [config.paths.dir, registry],\n )\n useEffect(() => { refresh() }, [refresh])\n\n // Explicit \"show the wizard\" flag — set when the user picks the\n // `+ add or re-configure` option, cleared when they save or cancel back\n // to the picker. Without it, the picker would be sticky once any provider\n // had credentials.\n const [forceWizard, setForceWizard] = useState(false)\n\n const available = useMemo(() => providers.filter(p => p.available), [providers])\n\n const onWizardDone = useCallback(() => {\n setForceWizard(false)\n refresh()\n }, [refresh])\n\n if (available.length === 0 || forceWizard) {\n // \"← back\" only makes sense when we got here from the picker (forceWizard)\n // AND there's still something to go back to. The `&&` guards against the\n // edge case where credentials disappear between renders.\n const canCancel = forceWizard && available.length > 0\n return (\n <SetupWizard\n registry={registry}\n dataDir={config.paths.dir}\n onConfigured={onWizardDone}\n onCancel={canCancel ? () => setForceWizard(false) : undefined}\n />\n )\n }\n\n const options = [\n ...available.map(p => ({\n name: p.label,\n description: p.methods.map(m => m.detail).join(' · '),\n value: p.key,\n })),\n {\n name: '+ add or re-configure a provider',\n description: 'launch the setup wizard',\n value: WIZARD_OPTION_VALUE,\n },\n ]\n\n return (\n <box\n title=\" pick a provider \"\n style={{\n border: true,\n borderColor: COLOR.border,\n padding: 1,\n flexDirection: 'column',\n flexGrow: 1,\n }}\n >\n <select\n {...SELECT_THEME}\n focused={focused}\n options={options}\n wrapSelection\n onSelect={(_idx, option) => {\n if (!option)\n return\n if (option.value === WIZARD_OPTION_VALUE) {\n setForceWizard(true)\n return\n }\n const provider = findByKey(available, option.value)\n if (provider)\n onPick(provider)\n }}\n style={{ flexGrow: 1 }}\n />\n </box>\n )\n}\n\n// ---------------------------------------------------------------------------\n// SetupWizard — first-run credential setup. Three steps:\n// 1. Pick provider\n// 2. Pick auth method (apikey · OAuth where the descriptor supports it)\n// 3a. Enter API key → save to credentials.json → refresh AuthScreen\n// 3b. Open browser → OAuth callback server → save tokens → refresh\n//\n// Everything is driven by the host's {@link ProviderDescriptor}s — no\n// hardcoded provider metadata lives here.\n// ---------------------------------------------------------------------------\n\ntype WizardStep\n = | { kind: 'pick-provider' }\n | { kind: 'pick-method', descriptor: ProviderDescriptor }\n | { kind: 'enter-apikey', descriptor: ProviderDescriptor }\n | { kind: 'oauth-running', descriptor: ProviderDescriptor }\n\nfunction SetupWizard({\n registry,\n dataDir,\n onConfigured,\n onCancel,\n}: {\n registry: Readonly<Record<string, ProviderDescriptor>>\n dataDir: string\n onConfigured: () => void\n /**\n * Available only when the wizard was opened from the picker\n * (\"+ add or re-configure\"). On first launch (no providers yet) it's\n * undefined and the wizard has no cancel affordance — the user must set\n * up a provider to proceed.\n */\n onCancel?: () => void\n}) {\n const [step, setStep] = useState<WizardStep>({ kind: 'pick-provider' })\n const [error, setError] = useState<string | null>(null)\n\n const descriptors = useMemo(() => Object.values(registry), [registry])\n\n const onPickProvider = useCallback((descriptor: ProviderDescriptor) => {\n setError(null)\n setStep({ kind: 'pick-method', descriptor })\n }, [])\n\n const onPickMethod = useCallback((descriptor: ProviderDescriptor, method: 'apikey' | 'oauth') => {\n setError(null)\n if (method === 'apikey') {\n setStep({ kind: 'enter-apikey', descriptor })\n }\n else {\n setStep({ kind: 'oauth-running', descriptor })\n }\n }, [])\n\n const onApiKeySubmit = useCallback((descriptor: ProviderDescriptor, value: string) => {\n const trimmed = value.trim()\n if (!trimmed) {\n setError('API key cannot be empty.')\n return\n }\n try {\n setProviderCredential(dataDir, descriptor, { kind: 'apikey', value: trimmed })\n // Also expose via env so providers created later in this process pick it\n // up. Skip for descriptors with no env-var convention (custom providers\n // that resolve credentials some other way).\n if (descriptor.envKey)\n process.env[descriptor.envKey] = trimmed\n onConfigured()\n }\n catch (err) {\n setError(err instanceof Error ? err.message : String(err))\n }\n }, [dataDir, onConfigured])\n\n if (descriptors.length === 0)\n return <EmptyRegistryNotice />\n\n if (step.kind === 'pick-provider') {\n return (\n <PickProviderStep\n descriptors={descriptors}\n error={error}\n onPick={onPickProvider}\n onCancel={onCancel}\n />\n )\n }\n\n if (step.kind === 'pick-method')\n return <PickMethodStep descriptor={step.descriptor} error={error} onPick={onPickMethod} />\n\n if (step.kind === 'enter-apikey')\n return <EnterApiKeyStep descriptor={step.descriptor} error={error} onSubmit={onApiKeySubmit} />\n\n return (\n <OAuthRunningStep\n descriptor={step.descriptor}\n dataDir={dataDir}\n onSuccess={onConfigured}\n onError={(msg) => {\n setError(msg)\n setStep({ kind: 'pick-method', descriptor: step.descriptor })\n }}\n />\n )\n}\n\n/**\n * Shared wrapper for every wizard step — same border + padding + flex layout\n * with a customizable title and accent color. Footnote slot at the bottom for\n * an error banner.\n */\nfunction WizardPanel({\n title,\n accent,\n error,\n children,\n}: {\n title: string\n accent?: string\n error?: string | null\n children: ReactNode\n}) {\n const COLOR = useColors()\n return (\n <box\n title={title}\n style={{\n border: true,\n borderColor: accent ?? COLOR.border,\n padding: 1,\n gap: 1,\n flexDirection: 'column',\n flexGrow: 1,\n }}\n >\n {children}\n {error && <text fg={COLOR.error}>{error}</text>}\n </box>\n )\n}\n\n/** \"esc to exit\" footer hint shared by every wizard step that doesn't offer a \"← back\" affordance. */\nfunction WizardEscHint() {\n const COLOR = useColors()\n return <text fg={COLOR.dim}>esc to exit</text>\n}\n\nfunction EmptyRegistryNotice() {\n const COLOR = useColors()\n return (\n <WizardPanel title=\" no providers configured \" accent={COLOR.error}>\n <text fg={COLOR.error}>This TUI has no providers registered.</text>\n <text fg={COLOR.dim}>\n Pass providers via\n <span fg={COLOR.model}>{' runTui({ providers }) '}</span>\n or use the built-ins via\n <span fg={COLOR.model}>{' BUILTIN_PROVIDERS '}</span>\n .\n </text>\n </WizardPanel>\n )\n}\n\n/** Sentinel option value used for the wizard's \"← back to picker\" entry. */\nconst WIZARD_BACK_VALUE = '__back__'\n\nfunction PickProviderStep({\n descriptors,\n error,\n onPick,\n onCancel,\n}: {\n descriptors: readonly ProviderDescriptor[]\n error: string | null\n onPick: (descriptor: ProviderDescriptor) => void\n /** When set, adds a \"← back\" option that calls this to bail out without saving. */\n onCancel?: () => void\n}) {\n const focused = useModalAwareFocus()\n const COLOR = useColors()\n const SELECT_THEME = useSelectStyle()\n const options = [\n ...descriptors.map((d) => {\n const methods: string[] = supportsOAuth(d) ? ['API key', 'OAuth'] : ['API key']\n return { name: d.label, description: methods.join(' · '), value: d.key }\n }),\n ...(onCancel\n ? [{ name: '← back', description: 'return to the provider list', value: WIZARD_BACK_VALUE }]\n : []),\n ]\n\n // Different copy + title for the two entry paths: first-launch users need\n // the \"credentials live here\" hint; re-config users already know how the\n // TUI works and just want to pick a provider.\n const title = onCancel\n ? ' add or re-configure a provider '\n : ' welcome to zidane · pick a provider '\n\n return (\n <WizardPanel title={title} error={error}>\n {!onCancel && (\n <text fg={COLOR.dim}>\n No provider credentials yet. Pick a provider to configure — keys are stored in\n <span fg={COLOR.model}>{' ~/.zidane/credentials.json '}</span>\n (owner-only).\n </text>\n )}\n <select\n {...SELECT_THEME}\n focused={focused}\n options={options}\n wrapSelection\n onSelect={(_idx, option) => {\n if (!option)\n return\n if (option.value === WIZARD_BACK_VALUE) {\n onCancel?.()\n return\n }\n const descriptor = findByKey(descriptors, option.value)\n if (descriptor)\n onPick(descriptor)\n }}\n style={{ flexGrow: 1 }}\n />\n </WizardPanel>\n )\n}\n\nfunction PickMethodStep({\n descriptor,\n error,\n onPick,\n}: {\n descriptor: ProviderDescriptor\n error: string | null\n onPick: (descriptor: ProviderDescriptor, method: 'apikey' | 'oauth') => void\n}) {\n const focused = useModalAwareFocus()\n const SELECT_THEME = useSelectStyle()\n\n const options = useMemo(() => {\n interface MethodOption { name: string, description: string, value: 'apikey' | 'oauth' }\n const items: MethodOption[] = [\n { name: 'API key', description: `paste your ${descriptor.label} API key`, value: 'apikey' },\n ]\n if (supportsOAuth(descriptor)) {\n // OAuth hint comes from the descriptor — built-in `anthropicDescriptor`\n // sets it to \"Claude Pro/Max subscription\". Hosts adding OAuth on their\n // own providers set their own hint (or omit it).\n const hint = descriptor.oauthHint ? ` (${descriptor.oauthHint})` : ''\n items.push({\n name: 'OAuth',\n description: `browser-based sign-in${hint}`,\n value: 'oauth',\n })\n }\n return items\n }, [descriptor])\n\n return (\n <WizardPanel title={` configure ${descriptor.label} — pick auth method `} error={error}>\n <WizardEscHint />\n <select\n {...SELECT_THEME}\n focused={focused}\n options={options}\n wrapSelection\n onSelect={(_idx, option) => {\n if (option)\n onPick(descriptor, option.value)\n }}\n style={{ flexGrow: 1 }}\n />\n </WizardPanel>\n )\n}\n\nfunction EnterApiKeyStep({\n descriptor,\n error,\n onSubmit,\n}: {\n descriptor: ProviderDescriptor\n error: string | null\n onSubmit: (descriptor: ProviderDescriptor, value: string) => void\n}) {\n const focused = useModalAwareFocus()\n const inputRef = useRef<InputRenderable | null>(null)\n const COLOR = useColors()\n\n const submit = useCallback(() => {\n const value = inputRef.current?.value ?? ''\n onSubmit(descriptor, value)\n }, [descriptor, onSubmit])\n\n return (\n <WizardPanel title={` configure ${descriptor.label} — paste API key `} error={error}>\n <text fg={COLOR.dim}>\n Paste your\n {` ${descriptor.label} `}\n API key and press\n <span fg={COLOR.model}> enter </span>\n to save. Esc to exit.\n </text>\n <box\n style={{\n border: true,\n borderColor: COLOR.borderActive,\n paddingLeft: 1,\n paddingRight: 1,\n height: 3,\n }}\n >\n <input\n ref={inputRef}\n focused={focused}\n keyBindings={API_KEY_INPUT_BINDINGS}\n placeholder={descriptor.apiKeyPlaceholder ?? 'API key…'}\n onSubmit={submit}\n style={{ flexGrow: 1 }}\n />\n </box>\n </WizardPanel>\n )\n}\n\nfunction OAuthRunningStep({\n descriptor,\n dataDir,\n onSuccess,\n onError,\n}: {\n descriptor: ProviderDescriptor\n dataDir: string\n onSuccess: () => void\n onError: (msg: string) => void\n}) {\n const [url, setUrl] = useState<string | null>(null)\n const [status, setStatus] = useState('starting browser…')\n const COLOR = useColors()\n\n useEffect(() => {\n const ac = new AbortController()\n let cancelled = false\n\n void (async () => {\n try {\n const creds = await runOAuthLogin(descriptor, {\n onUrl: (loginUrl) => {\n if (cancelled)\n return\n setUrl(loginUrl)\n setStatus('waiting for browser callback…')\n },\n onProgress: (message) => {\n if (!cancelled)\n setStatus(message)\n },\n signal: ac.signal,\n })\n if (cancelled)\n return\n // `creds` carries `{ access, refresh, expires, ...extras }`. Spread\n // verbatim — the storage shape only adds our `kind` tag on top.\n setProviderCredential(dataDir, descriptor, { kind: 'oauth', ...creds })\n onSuccess()\n }\n catch (err) {\n if (cancelled)\n return\n const message = err instanceof Error ? err.message : String(err)\n onError(message)\n }\n })()\n\n return () => { cancelled = true; ac.abort() }\n }, [descriptor, dataDir, onSuccess, onError])\n\n return (\n <WizardPanel title={` configure ${descriptor.label} — OAuth `}>\n <WizardEscHint />\n <Spinner label={status} />\n {url && (\n <box style={{ flexDirection: 'column', gap: 0 }}>\n <text fg={COLOR.dim}>If the browser didn't open, visit:</text>\n <text fg={COLOR.model}>{url}</text>\n </box>\n )}\n </WizardPanel>\n )\n}\n\n// ---------------------------------------------------------------------------\n// SessionsScreen — list of sessions with a New entry on top.\n// ---------------------------------------------------------------------------\n\nconst NEW_VALUE = '__new__'\n\nexport function SessionsScreen({\n sessions,\n currentId,\n onPick,\n onCreate,\n}: {\n sessions: SessionMeta[]\n currentId: string | null\n onPick: (id: string) => void\n onCreate: () => void\n}) {\n const focused = useModalAwareFocus()\n const COLOR = useColors()\n const SELECT_THEME = useSelectStyle()\n const options = useMemo(() => {\n const items: { name: string, description: string, value: string }[] = [\n { name: '+ new session', description: 'start fresh', value: NEW_VALUE },\n ]\n for (const s of sessions) {\n const marker = s.id === currentId ? '● ' : ' '\n const turnLabel = `${s.turnCount} turn${s.turnCount === 1 ? '' : 's'}`\n items.push({\n name: `${marker}${s.title}`,\n description: `#${shortId(s.id)} · ${turnLabel} · ${ageString(s.updatedAt)}`,\n value: s.id,\n })\n }\n return items\n }, [sessions, currentId])\n\n return (\n <box\n title=\" sessions \"\n style={{\n border: true,\n borderColor: COLOR.border,\n padding: 1,\n flexDirection: 'column',\n flexGrow: 1,\n }}\n >\n <select\n {...SELECT_THEME}\n focused={focused}\n options={options}\n wrapSelection\n onSelect={(_idx, option) => {\n if (!option)\n return\n if (option.value === NEW_VALUE)\n onCreate()\n else if (typeof option.value === 'string')\n onPick(option.value)\n }}\n style={{ flexGrow: 1 }}\n />\n </box>\n )\n}\n\n// ---------------------------------------------------------------------------\n// ChatScreen — transcript + auto-growing multi-line input + (running) spinner.\n// Enter inserts a newline; shift+enter submits; ctrl+↑↓ cycles prompt history.\n// ---------------------------------------------------------------------------\n\n/** Visible content lines: 1 minimum, 5 maximum (textarea scrolls past 5). */\nconst MIN_CONTENT_LINES = 1\nconst MAX_CONTENT_LINES = 5\n\nexport function ChatScreen({\n events,\n busy,\n settings,\n onSubmit,\n session,\n pending,\n onApproval,\n}: {\n events: StreamEvent[]\n busy: boolean\n settings: Settings\n onSubmit: (prompt: string) => void\n session: SessionMeta | null\n /** Head of the safe-mode approval queue, or `null` when nothing is pending. */\n pending: ApprovalRequest | null\n /** Resolve the active prompt with the user's pick. */\n onApproval: (decision: ApprovalDecision) => void\n}) {\n const COLOR = useColors()\n const title = useMemo(() => {\n if (!session)\n return ' untitled '\n const turns = `${session.turnCount} turn${session.turnCount === 1 ? '' : 's'}`\n return ` ${session.title} · #${shortId(session.id)} · ${turns} `\n }, [session])\n\n // Prior user prompts sourced from the transcript itself — kept in submit\n // order so the history-navigation feels like a regular shell.\n const userPrompts = useMemo(\n () => events.filter(e => e.kind === 'info').map(e => e.text.replace(/^❯ /, '')),\n [events],\n )\n\n return (\n <box style={{ flexDirection: 'column', flexGrow: 1 }}>\n <box\n title={title}\n style={{\n border: true,\n borderColor: COLOR.border,\n flexGrow: 1,\n flexDirection: 'column',\n }}\n >\n <Transcript events={events} settings={settings} />\n </box>\n\n {/*\n Priority: pending approval wins over busy (a paused run still has\n `busy === true` while the gate awaits). The picker takes the prompt\n slot so the user can decide without losing context.\n */}\n {pending\n ? <ApprovalBlock request={pending} onPick={onApproval} />\n : busy\n ? <BusyBlock />\n : <PromptBlock userPrompts={userPrompts} onSubmit={onSubmit} />}\n </box>\n )\n}\n\n/** Max chars per scalar argument in the approval preview. */\nconst APPROVAL_ARG_MAX = 80\n\n/**\n * Render `{ path: 'x.ts', contents: 'long string' }` as\n * `path: \"x.ts\", contents: \"long string…\"` — readable, per-key, truncated\n * per value rather than dumping `JSON.stringify(input)` (which produces an\n * illegible 50KB blob for `write_file` etc.).\n */\nfunction formatApprovalArgs(input: Record<string, unknown>): string {\n const parts: string[] = []\n for (const [key, raw] of Object.entries(input)) {\n let value: string\n if (typeof raw === 'string') {\n const escaped = raw.replace(/\\n/g, '\\\\n')\n value = escaped.length > APPROVAL_ARG_MAX\n ? `\"${escaped.slice(0, APPROVAL_ARG_MAX)}…\"`\n : `\"${escaped}\"`\n }\n else {\n const json = JSON.stringify(raw)\n value = json.length > APPROVAL_ARG_MAX ? `${json.slice(0, APPROVAL_ARG_MAX)}…` : json\n }\n parts.push(`${key}: ${value}`)\n }\n return parts.join(', ')\n}\n\n// `description` is required by OpenTUI's `SelectOption` shape even when the\n// select is rendered with `showDescription={false}` — passing an empty string\n// keeps the type happy and is invisible at the render layer.\ninterface DecisionOption { name: string, description: string, value: ApprovalDecision }\n\n/**\n * Inline approval picker — replaces the chat input while a tool call is\n * pending. Three options:\n * - **accept once** — let this call execute, don't persist anything.\n * - **accept + remember** — execute + add a `projects.json` entry so the\n * same shape doesn't prompt again in this directory.\n * - **deny** — refuse the call. The model gets `Blocked: …` and adapts.\n *\n * Esc aborts the whole run via the parent keyboard handler; per-call\n * accept/deny only happens through the select below.\n *\n * Layout is fully pinned so the picker never overlaps with itself or the\n * transcript above:\n *\n * - Outer `<box>` has an explicit `height`. The slot below the transcript\n * adapts (the chat container is column-flex), so we control exactly how\n * many rows we occupy.\n * - Summary row is a `<box height: 1, overflow: hidden>` wrapping a\n * `<text wrapMode=\"none\">` — a 500-char tool-call preview can never\n * wrap to row 2 and push the select off-screen.\n * - `<select showDescription={false}>` keeps each option to exactly one\n * row. Hints live in the `name` string after a `·` separator. Without\n * this, the default `showDescription: true` makes every option take 2\n * rows, and a `height: options.length` select would overdraw into the\n * summary above (the original bug).\n */\nfunction ApprovalBlock({\n request,\n onPick,\n}: {\n request: ApprovalRequest\n onPick: (decision: ApprovalDecision) => void\n}) {\n const focused = useModalAwareFocus()\n const COLOR = useColors()\n const SELECT_THEME = useSelectStyle()\n\n const summary = useMemo(\n () => `${request.tool}(${formatApprovalArgs(request.input)})`,\n [request.tool, request.input],\n )\n\n const options = useMemo<DecisionOption[]>(() => {\n const safelistEntry = suggestSafelistEntry(request.tool, request.input)\n return [\n { name: 'accept once · allow this call only', description: '', value: 'accept-once' },\n { name: `accept + remember · add \"${safelistEntry}\" to projects.json`, description: '', value: 'accept-safelist' },\n { name: 'deny · refuse — the model will see Blocked', description: '', value: 'deny' },\n ]\n }, [request.tool, request.input])\n\n // border (2) + summary (1) + one row per option = total outer height.\n const height = 2 + 1 + options.length\n\n return (\n <box\n title=\" approve tool call · esc to abort run \"\n style={{\n border: true,\n borderColor: COLOR.warn,\n paddingLeft: 1,\n paddingRight: 1,\n paddingTop: 0,\n paddingBottom: 0,\n height,\n flexDirection: 'column',\n flexShrink: 0,\n }}\n >\n <box style={{ height: 1, overflow: 'hidden', flexShrink: 0 }}>\n <text fg={COLOR.model} wrapMode=\"none\">\n <span fg={COLOR.warn}>↳ </span>\n {summary}\n </text>\n </box>\n <select\n {...SELECT_THEME}\n focused={focused}\n options={options}\n showDescription={false}\n wrapSelection\n onSelect={(_idx, option) => {\n if (option)\n onPick(option.value)\n }}\n style={{ height: options.length, flexShrink: 0 }}\n />\n </box>\n )\n}\n\nfunction BusyBlock() {\n const COLOR = useColors()\n return (\n <box\n style={{\n border: true,\n borderColor: COLOR.warn,\n paddingLeft: 1,\n paddingRight: 1,\n height: 3,\n }}\n >\n <Spinner label=\"streaming response — esc to abort\" />\n </box>\n )\n}\n\nfunction PromptBlock({\n userPrompts,\n onSubmit,\n}: {\n userPrompts: string[]\n onSubmit: (prompt: string) => void\n}) {\n const focused = useModalAwareFocus()\n const COLOR = useColors()\n const textareaRef = useRef<TextareaRenderable | null>(null)\n /** Auto-grow: visible content rows the textarea currently occupies (clamped 1..5). */\n const [contentLines, setContentLines] = useState(MIN_CONTENT_LINES)\n /**\n * History navigation state. `null` = not navigating (textarea owns its content).\n * Once the user enters history (up at top), we snapshot the draft and cycle.\n */\n const historyRef = useRef<{ idx: number, draft: string } | null>(null)\n\n const syncLines = useCallback(() => {\n // `lineCount` is logical (newline-separated) line count — what the user\n // means by \"jumping lines\". Wrap-aware visual lines (`virtualLineCount`)\n // would also grow on long single lines, which isn't what we want.\n const lines = textareaRef.current?.lineCount ?? MIN_CONTENT_LINES\n setContentLines(Math.max(MIN_CONTENT_LINES, lines))\n }, [])\n\n const submit = useCallback(() => {\n const value = textareaRef.current?.plainText ?? ''\n if (!value.trim())\n return\n onSubmit(value)\n textareaRef.current?.clear()\n historyRef.current = null\n setContentLines(MIN_CONTENT_LINES)\n }, [onSubmit])\n\n const cycleHistory = useCallback((direction: -1 | 1) => {\n if (userPrompts.length === 0 || !textareaRef.current)\n return\n\n if (historyRef.current === null) {\n // Entering history mode: snapshot the draft so we can restore on the\n // way forward past the most-recent entry.\n historyRef.current = {\n idx: userPrompts.length,\n draft: textareaRef.current.plainText,\n }\n }\n\n const nextIdx = historyRef.current.idx + direction\n\n if (nextIdx < 0)\n return // already at oldest\n\n if (nextIdx >= userPrompts.length) {\n // Walked off the end → restore the original draft, leave history mode.\n textareaRef.current.setText(historyRef.current.draft)\n textareaRef.current.gotoBufferEnd()\n historyRef.current = null\n }\n else {\n textareaRef.current.setText(userPrompts[nextIdx])\n textareaRef.current.gotoBufferEnd()\n historyRef.current.idx = nextIdx\n }\n syncLines()\n }, [userPrompts, syncLines])\n\n /**\n * Up/Down at the buffer boundary cycles prompt history (fish/zsh pattern).\n * Mid-buffer up/down move the cursor normally — handled by the default\n * `move-up` / `move-down` actions in `TEXTAREA_BINDINGS`.\n */\n const onKeyDown = useCallback((event: KeyEvent) => {\n if (event.ctrl || event.shift || event.meta)\n return\n if (event.name !== 'up' && event.name !== 'down')\n return\n const buffer = textareaRef.current\n if (!buffer)\n return\n const cursorRow = buffer.logicalCursor.row\n if (event.name === 'up' && cursorRow === 0)\n cycleHistory(-1)\n else if (event.name === 'down' && cursorRow === buffer.lineCount - 1)\n cycleHistory(1)\n }, [cycleHistory])\n\n // Content area + 2 lines of border = total box height.\n const contentHeight = Math.min(MAX_CONTENT_LINES, contentLines)\n const boxHeight = contentHeight + 2\n\n return (\n <box\n style={{\n border: true,\n borderColor: COLOR.borderActive,\n paddingLeft: 1,\n paddingRight: 1,\n height: boxHeight,\n flexDirection: 'column',\n }}\n >\n <textarea\n ref={textareaRef}\n // Release focus while a modal is open so its `handleKeyPress` stops\n // routing keys behind the overlay (see `useModalAwareFocus`).\n focused={focused}\n keyBindings={TEXTAREA_BINDINGS}\n placeholder=\"Ask zidane… (enter = send · shift+enter = newline · ↑↓ at edges = history)\"\n // Fill the box's inner area so the textarea's own scroll engine kicks\n // in once content exceeds the visible rows (past 5 logical lines).\n style={{ flexGrow: 1, height: '100%' }}\n onSubmit={submit}\n onContentChange={syncLines}\n onKeyDown={onKeyDown}\n />\n </box>\n )\n}\n","/** @jsxImportSource @opentui/react */\nimport type { BooleanSettingKey } from '../chat/settings-context'\nimport type { Settings } from '../chat/types'\nimport { useKeyboard } from '@opentui/react'\nimport { useCallback, useMemo, useState } from 'react'\nimport { SETTINGS_CHOICES, SETTINGS_TOGGLES, useSettings } from '../chat/settings-context'\nimport { useColors } from '../chat/theme-context'\nimport { Modal } from './modal'\n\n// ---------------------------------------------------------------------------\n// SettingsModal — vertical list. Up/Down navigates, Enter/Space activates the\n// focused row (toggles a flag, cycles a choice, or invokes an action). Esc\n// dismisses (handled by the parent <Modal>).\n//\n// Three row kinds:\n// - `toggle` — a boolean stored under `settings[key]` (see SETTINGS_TOGGLES)\n// - `choice` — a string stored under `settings[key]` cycling through a fixed\n// options list (see SETTINGS_CHOICES — e.g. the theme picker)\n// - `action` — a one-shot callback (e.g. \"Re-configure providers\")\n//\n// Hosts that mount the modal pass `actions` to wire in app-level actions\n// like re-opening the auth panel. Toggle and choice rows are static —\n// sourced from the corresponding tables in `chat/settings-context.tsx` so a\n// GUI settings panel can share the same labels.\n// ---------------------------------------------------------------------------\n\ninterface ToggleItem {\n kind: 'toggle'\n key: BooleanSettingKey\n label: string\n description: string\n}\ninterface ChoiceItem {\n kind: 'choice'\n key: keyof Settings\n label: string\n description: string\n options: readonly { value: unknown, label: string }[]\n}\ninterface ActionItem {\n kind: 'action'\n id: string\n label: string\n description: string\n onPick: () => void\n}\ntype SettingsItem = ToggleItem | ChoiceItem | ActionItem\n\nexport interface SettingsActions {\n /**\n * Re-open the auth screen so the user can switch providers or run the\n * wizard for a new/existing one. Wiring this callback adds a\n * \"Re-configure providers\" row to the modal.\n */\n onReauth?: () => void\n}\n\nexport function SettingsModal({ actions }: { actions?: SettingsActions } = {}) {\n const { settings, toggle, setSetting } = useSettings()\n const [cursor, setCursorRaw] = useState(0)\n const COLOR = useColors()\n\n // Build the row list dynamically so the modal only shows action rows whose\n // callbacks are wired. Toggle/choice rows are always present.\n const items: readonly SettingsItem[] = useMemo(() => {\n const toggleItems: ToggleItem[] = SETTINGS_TOGGLES.map(t => ({ kind: 'toggle' as const, ...t }))\n const choiceItems: ChoiceItem[] = SETTINGS_CHOICES.map(c => ({ kind: 'choice' as const, ...c }))\n const actionItems: ActionItem[] = []\n if (actions?.onReauth) {\n actionItems.push({\n kind: 'action',\n id: 'reauth',\n label: 'Authentication',\n description: 'switch provider, add another, or re-authenticate',\n onPick: actions.onReauth,\n })\n }\n return [...toggleItems, ...choiceItems, ...actionItems]\n }, [actions])\n\n // The modal re-mounts on each open via `modal.open(<SettingsModal/>)`, but\n // if the host swaps `actions` while the modal is open (e.g. tears down the\n // re-auth wiring mid-session), the cursor might now point past the end of\n // the truncated row list. Clamp on read so we never index out of bounds.\n const safeCursor = Math.min(cursor, items.length - 1)\n const setCursor = useCallback(\n (update: (c: number) => number) =>\n setCursorRaw(prev => Math.min(Math.max(0, update(prev)), items.length - 1)),\n [items.length],\n )\n\n useKeyboard((key) => {\n if (key.name === 'up' || (key.ctrl && key.name === 'p')) {\n setCursor(c => c - 1)\n }\n else if (key.name === 'down' || (key.ctrl && key.name === 'n')) {\n setCursor(c => c + 1)\n }\n else if (key.name === 'return' || key.name === 'space') {\n const item = items[safeCursor]\n if (!item)\n return\n if (item.kind === 'toggle') {\n toggle(item.key)\n }\n else if (item.kind === 'choice') {\n // Cycle to the next option, wrapping. Single-option choice = no-op\n // (find returns the only entry; (idx+1)%1 === 0 → same value).\n const current = settings[item.key]\n const idx = item.options.findIndex(o => o.value === current)\n const next = item.options[(idx + 1) % item.options.length]\n if (next)\n setSetting(item.key, next.value as Settings[typeof item.key])\n }\n else {\n item.onPick()\n }\n }\n })\n\n // Index where the post-toggle block starts — separator divides transcript\n // filter toggles (top) from app-level settings (theme picker, auth, …).\n // Falls back to -1 when only toggles exist.\n const firstNonToggleIndex = items.findIndex(i => i.kind !== 'toggle')\n\n return (\n <Modal title=\"settings\">\n <box style={{ flexDirection: 'column' }}>\n {items.map((item, i) => (\n <box\n key={item.kind === 'action' ? item.id : item.key}\n style={{ flexDirection: 'column' }}\n >\n {/*\n Full-width separator between transcript-filter toggles and the\n app-level block (theme picker, authentication, …).\n `border={['top']}` draws only the top edge, which yoga stretches\n to the parent's content width — no need to measure the modal\n manually. Vertical margin gives the rule breathing room from\n the rows it separates.\n */}\n {i === firstNonToggleIndex && i > 0 && (\n <box\n style={{\n border: ['top'],\n borderColor: COLOR.mute,\n height: 1,\n marginTop: 1,\n marginBottom: 1,\n }}\n />\n )}\n {item.kind === 'toggle' && (\n <ToggleRow\n label={item.label}\n description={item.description}\n enabled={settings[item.key]}\n focused={i === safeCursor}\n />\n )}\n {item.kind === 'choice' && (\n <ChoiceRow\n label={item.label}\n description={item.description}\n value={\n item.options.find(o => o.value === settings[item.key])?.label\n ?? String(settings[item.key])\n }\n cyclable={item.options.length > 1}\n focused={i === safeCursor}\n />\n )}\n {item.kind === 'action' && (\n <ActionRow\n label={item.label}\n description={item.description}\n focused={i === safeCursor}\n />\n )}\n </box>\n ))}\n </box>\n <text fg={COLOR.mute}>\n <span fg={COLOR.warn}>↑↓</span>\n {' navigate · '}\n <span fg={COLOR.warn}>↵</span>\n {' toggle/cycle/select · '}\n <span fg={COLOR.warn}>esc</span>\n {' close'}\n </text>\n </Modal>\n )\n}\n\n/**\n * Toggle row — `▶` marker · checkbox · label · description.\n *\n * Rendered as one `<text>` so OpenTUI's word-wrap handles narrow terminals\n * automatically: on wide screens everything sits on one line; on narrow ones\n * the trailing description wraps under the label without breaking the row.\n */\nfunction ToggleRow({\n label,\n description,\n enabled,\n focused,\n}: {\n label: string\n description: string\n enabled: boolean\n focused: boolean\n}) {\n const COLOR = useColors()\n return (\n <text fg={focused ? COLOR.brand : COLOR.dim}>\n <span fg={focused ? COLOR.brand : COLOR.mute}>{focused ? '▶ ' : ' '}</span>\n <span fg={enabled ? COLOR.accent : COLOR.mute}>{enabled ? '[✓] ' : '[ ] '}</span>\n <span fg={focused ? COLOR.brand : COLOR.dim}>{label}</span>\n <span fg={COLOR.mute}>{` ${description}`}</span>\n </text>\n )\n}\n\n/**\n * Choice row — `▶` marker · label · `:` · current value · description.\n *\n * Cycles through `options` on enter/space. When only one option is\n * available (`cyclable=false`) the row still renders with the current\n * value but the enter handler is a no-op — we surface this via the absence\n * of the trailing `›` affordance so it visually reads as informational.\n */\nfunction ChoiceRow({\n label,\n description,\n value,\n cyclable,\n focused,\n}: {\n label: string\n description: string\n value: string\n cyclable: boolean\n focused: boolean\n}) {\n const COLOR = useColors()\n return (\n <text fg={focused ? COLOR.brand : COLOR.dim}>\n <span fg={focused ? COLOR.brand : COLOR.mute}>{focused ? '▶ ' : ' '}</span>\n <span fg={focused ? COLOR.brand : COLOR.dim}>{label}</span>\n <span fg={COLOR.mute}>{': '}</span>\n <span fg={focused ? COLOR.brand : COLOR.accent}>{value}</span>\n <span fg={COLOR.mute}>{` ${description}`}</span>\n {focused && cyclable && <span fg={COLOR.brand}>{' ↻'}</span>}\n </text>\n )\n}\n\n/**\n * Action row — cursor marker · label · description · (focus-only) trailing arrow.\n *\n * The label sits in the same column as a toggle row's `[✓]` checkbox (right\n * after the 2-col cursor slot). The trailing `›` only renders when focused\n * so it reads as a \"this row runs\" affordance, not a static decoration on\n * every action.\n */\nfunction ActionRow({\n label,\n description,\n focused,\n}: {\n label: string\n description: string\n focused: boolean\n}) {\n const COLOR = useColors()\n return (\n <text fg={focused ? COLOR.brand : COLOR.dim}>\n <span fg={focused ? COLOR.brand : COLOR.mute}>{focused ? '▶ ' : ' '}</span>\n <span fg={focused ? COLOR.brand : COLOR.accent}>{label}</span>\n <span fg={COLOR.mute}>{` ${description}`}</span>\n {focused && <span fg={COLOR.brand}>{' ›'}</span>}\n </text>\n )\n}\n","/** @jsxImportSource @opentui/react */\nimport type { Agent } from '../agent'\nimport type { AgentProfile } from '../chat/agents'\nimport type { ProviderAuth, ProviderKey } from '../chat/auth'\nimport type { ResolvedConfig } from '../chat/config'\nimport type { Picked, Screen, SessionMeta, Settings, StreamEvent } from '../chat/types'\nimport type { Session } from '../session'\nimport type { ContextUsage, Hint } from './components'\nimport { useKeyboard, useRenderer } from '@opentui/react'\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport { createAgent } from '../agent'\nimport { ConfigProvider, useConfig } from '../chat/config-context'\nimport { getContextWindow } from '../chat/providers'\nimport { addToSafelist, getSafelist, isOnSafelist, suggestSafelistEntry } from '../chat/safe-mode'\nimport { SafeModeProvider, useSafeModeActions, useSafeModeQueue } from '../chat/safe-mode-context'\nimport { DEFAULT_SETTINGS, SettingsProvider, useSettings } from '../chat/settings-context'\nimport {\n eventsFromTurns,\n lastContextSizeFromTurns,\n listSessionMeta,\n stripSpawnTokensLine,\n titleFromTurns,\n toolCallPreview,\n toolResultText,\n} from '../chat/store'\nimport {\n finalizeStreamingMarkdown,\n finalizeStreamingMarkdownForOwner,\n turnContextSize,\n useStreamBuffer,\n} from '../chat/streaming'\nimport { resolveTheme } from '../chat/theme'\nimport { ThemeProvider } from '../chat/theme-context'\nimport { createSession, loadSession } from '../session'\nimport { formatTokenUsage } from '../stats'\nimport { AgentPickerModal } from './agent-picker'\nimport { Footer } from './components'\nimport { ModalRoot, useModal } from './modal'\nimport { ModelPickerModal } from './model-picker'\nimport { AuthScreen, ChatScreen, SessionsScreen } from './screens'\nimport { SettingsModal } from './settings-modal'\nimport { MdStyleProvider } from './theme'\n\n/**\n * Surface failures that are normally silenced (teardown / save) when the\n * `ZIDANE_DEBUG` env var is set. Logging via `console.error` would otherwise\n * trigger OpenTUI's error console overlay and clutter the UI for end users.\n */\nconst debugLog: (label: string, err: unknown) => void\n = process.env.ZIDANE_DEBUG\n ? (label, err) => console.error(`[zidane/tui] ${label}:`, err)\n : () => {}\n\n/**\n * Top-level TUI component. Accepts a fully-resolved `ResolvedConfig` and wires\n * everything (settings, modal layer, screens, footer) underneath it.\n *\n * Hosts can either drive this via `runTui()` for the standard bootstrap or\n * mount `<App config={resolveConfig(...)} />` themselves inside a renderer\n * they already own.\n */\nexport function App({ config }: { config: ResolvedConfig }) {\n const initialSettings = useMemo(\n () => ({ ...DEFAULT_SETTINGS, ...config.initialSettings }),\n [config.initialSettings],\n )\n\n const onSettingsChange = useCallback(\n (settings: Settings) => config.stateStore.save({ ...config.stateStore.load(), settings }),\n [config.stateStore],\n )\n\n return (\n <ConfigProvider config={config}>\n <SettingsProvider initial={initialSettings} onChange={onSettingsChange}>\n <ThemedShell />\n </SettingsProvider>\n </ConfigProvider>\n )\n}\n\n/**\n * Reads `settings.theme` from the surrounding `SettingsProvider`, resolves\n * it to a `Theme`, and mounts everything else underneath a `ThemeProvider`.\n * Split out so `App` doesn't need to call `useSettings` (which would force\n * it to live inside its own provider — invalid).\n *\n * `resolveTheme` falls back to `DEFAULT_THEME` on unknown ids, so an\n * out-of-date `state.json` (theme renamed / removed) never breaks rendering.\n */\nfunction ThemedShell() {\n const { settings } = useSettings()\n const theme = useMemo(() => resolveTheme(settings.theme), [settings.theme])\n return (\n <ThemeProvider theme={theme}>\n <MdStyleProvider>\n <SafeModeProvider>\n <ModalRoot>\n <AppShell />\n </ModalRoot>\n </SafeModeProvider>\n </MdStyleProvider>\n </ThemeProvider>\n )\n}\n\nfunction AppShell() {\n const renderer = useRenderer()\n const modal = useModal()\n const config = useConfig()\n const { settings } = useSettings()\n // `useSafeModeQueue` re-renders on every push/pop; `useSafeModeActions`\n // hands back a stable object, so anything memoizing over the actions\n // (gate handlers, abort callback) keeps a single identity across queue\n // churn. See safe-mode-context.tsx.\n const queue = useSafeModeQueue()\n const { requestApproval, resolveHead, denyAll } = useSafeModeActions()\n\n // Destructure stable identities up-front so callbacks/effects can depend on\n // them directly (avoids the \"whole config in deps\" footgun — see S1 review).\n const {\n providers: providerRegistry,\n agents: agentRegistry,\n initialAgentId,\n store,\n stateStore,\n modelsFor,\n resumeProvider,\n initialPicked,\n initialState,\n } = config\n const lastResumedSessionId = initialState.lastSessionId\n const dataDir = config.paths.dir\n\n // Active agent profile. Held in a ref alongside state so `buildAgent` —\n // which the resume effect depends on — stays referentially stable across\n // profile switches (otherwise the effect would re-fire and re-activate\n // the originally-resumed session, clobbering any session the user\n // navigated to in between). State drives the picker's UI; the ref carries\n // the latest value into `buildAgent` synchronously.\n const [pickedAgent, setPickedAgent] = useState<AgentProfile>(\n () => agentRegistry[initialAgentId] ?? Object.values(agentRegistry)[0],\n )\n const pickedAgentRef = useRef(pickedAgent)\n\n // -------------------------------------------------------------------------\n // Safe-mode plumbing.\n //\n // Hook handlers run inside the agent loop — they need stable references to\n // the latest \"is safe-mode on?\" flag and to the cwd (the project key in\n // `projects.json`). Refs decouple registration from React re-renders so we\n // don't re-register on every settings flip.\n // -------------------------------------------------------------------------\n\n const safeModeEnabledRef = useRef(settings.safeMode)\n useEffect(() => { safeModeEnabledRef.current = settings.safeMode }, [settings.safeMode])\n\n // `process.cwd()` is the project key in `projects.json`. Captured once\n // per AppShell mount with `useState` lazy init — `useMemo([])` would\n // technically allow React to recompute under memory pressure, `useState`\n // is a hard guarantee.\n const [projectDir] = useState(() => process.cwd())\n\n // In-memory cache for the project's safelist so `gateDecision` doesn't\n // re-read `projects.json` from disk on every tool call (matters when a\n // parallel batch fires dozens of gate hooks in the same microtask). The\n // ref is seeded lazily and explicitly refreshed when we persist an entry\n // — the TUI is the only writer, so external invalidation isn't needed.\n const safelistRef = useRef<readonly string[] | null>(null)\n const readSafelist = useCallback((): readonly string[] => {\n if (safelistRef.current === null)\n safelistRef.current = getSafelist(dataDir, projectDir)\n return safelistRef.current\n }, [dataDir, projectDir])\n // Drop the cache on a project switch (only happens across mounts today,\n // but cheap insurance for future code that swaps `projectDir`).\n useEffect(() => { safelistRef.current = null }, [dataDir, projectDir])\n\n /**\n * Single source of truth for \"should this call execute?\". Returns true to\n * let the call through, false to refuse it. Handles three short-circuits:\n *\n * - Safe-mode globally off → always allow.\n * - Call covered by the project safelist or the implicit read-only set\n * → always allow without prompting.\n * - Otherwise → prompt the user and act on their decision (including\n * persisting a new safelist entry on \"accept + safelist\").\n *\n * Wired into the parent agent via `tool:gate` / `mcp:tool:gate`, and to\n * every subagent (transitively, for free) via `child:tool:gate` /\n * `child:mcp:tool:gate` — see the bubble in `src/tools/spawn.ts`.\n */\n const gateDecision = useCallback(\n async (tool: string, input: Record<string, unknown>): Promise<boolean> => {\n if (!safeModeEnabledRef.current)\n return true\n if (isOnSafelist(readSafelist(), tool, input))\n return true\n const decision = await requestApproval(tool, input)\n if (decision === 'deny')\n return false\n if (decision === 'accept-safelist') {\n const entry = suggestSafelistEntry(tool, input)\n addToSafelist(dataDir, projectDir, entry)\n safelistRef.current = null // force re-read on next gate\n }\n return true\n },\n [dataDir, projectDir, requestApproval, readSafelist],\n )\n\n // Initial screen + picked seed from the resolved config so a returning user\n // lands straight on chat (or sessions) without an auth-screen flash.\n const [screen, setScreen] = useState<Screen>(() => {\n if (!resumeProvider)\n return 'auth'\n return lastResumedSessionId ? 'chat' : 'sessions'\n })\n const [picked, setPicked] = useState<Picked | null>(() => initialPicked)\n const [sessions, setSessions] = useState<SessionMeta[]>([])\n const [currentSession, setCurrentSession] = useState<SessionMeta | null>(null)\n const [events, setEvents] = useState<StreamEvent[]>([])\n const [busy, setBusy] = useState(false)\n /** Token count from the most recent assistant turn (caching-aware). */\n const [lastInputTokens, setLastInputTokens] = useState(0)\n\n const agentRef = useRef<Agent | null>(null)\n const sessionRef = useRef<Session | null>(null)\n\n const stream = useStreamBuffer(setEvents)\n\n const makePicked = useCallback((provider: ProviderAuth, modelId?: string): Picked | null => {\n // If the host narrowed the provider registry we may have a detected auth\n // with no descriptor to back it. Return null and let the caller bail; the\n // AuthScreen already filters these out, so this is a belt-and-braces guard.\n const descriptor = providerRegistry[provider.key]\n if (!descriptor)\n return null\n const remembered = initialState.lastModelByProvider?.[provider.key]\n // Prefer remembered → descriptor default → factory probe. Factories may\n // throw on missing credentials at construction time; by the time we get\n // here we've already detected auth, so the probe is safe, but the\n // descriptor default lets us avoid constructing in the common case.\n const model = modelId ?? remembered ?? descriptor.defaultModel ?? descriptor.factory().meta.defaultModel\n return { provider, model }\n }, [providerRegistry, initialState])\n\n // -------------------------------------------------------------------------\n // Agent lifecycle: build a fresh Agent bound to the active Session and wire\n // streaming hooks. Re-created on every session switch so context never bleeds.\n // -------------------------------------------------------------------------\n\n const buildAgent = useCallback((session: Session, key: ProviderKey): Agent => {\n const descriptor = providerRegistry[key]\n if (!descriptor)\n throw new Error(`No provider registered for key \"${key}\"`)\n // Read the latest profile via the ref so a switch made between renders\n // (onPickAgent → activateSession in the same tick) picks up the new\n // preset without forcing `buildAgent` to depend on `pickedAgent`.\n const profile = pickedAgentRef.current\n const agent = createAgent({ ...profile.preset, provider: descriptor.factory(), session })\n\n // Safe-mode gates -----------------------------------------------------\n // Four hooks: native vs MCP, parent vs subagent. The `child:*:gate`\n // events bubble from every subagent's hook bus via `bubbleHooks` (see\n // `src/tools/spawn.ts`) with the **same** gate ctx, so writes to\n // `block` / `reason` here propagate straight back to the child's loop.\n // A denied call never reaches `tool:before`, so it doesn't pollute the\n // transcript — the model receives `Blocked: …` and adapts.\n const applyGate = async (\n name: string,\n input: Record<string, unknown>,\n ctx: { block: boolean, reason: string },\n ): Promise<void> => {\n if (ctx.block) // already refused by a higher-priority gate (skills/budgets/dedup)\n return\n if (!(await gateDecision(name, input))) {\n ctx.block = true\n ctx.reason = 'User denied this tool call'\n }\n }\n agent.hooks.hook('tool:gate', ctx => applyGate(ctx.name, ctx.input, ctx))\n agent.hooks.hook('child:tool:gate', ctx => applyGate(ctx.name, ctx.input, ctx))\n agent.hooks.hook('mcp:tool:gate', ctx => applyGate(ctx.displayName, ctx.input, ctx))\n agent.hooks.hook('child:mcp:tool:gate', ctx => applyGate(ctx.displayName, ctx.input, ctx))\n\n // Parent streams ------------------------------------------------------\n agent.hooks.hook('stream:thinking', ({ delta }) => stream.queueStreamDelta('thinking', delta))\n agent.hooks.hook('stream:text', ({ delta }) => stream.queueStreamDelta('markdown', delta))\n agent.hooks.hook('tool:before', ({ name, input }) => {\n stream.appendImmediate({ kind: 'tool', text: toolCallPreview(name, input), tool: name })\n })\n agent.hooks.hook('tool:after', ({ name, result }) => {\n // Spawn tool-results carry a `Tokens: …` summary that's already shown\n // by the spawn-end marker right above the tool-result — strip it from\n // the displayed string to avoid the duplicate / format-drift block.\n const raw = toolResultText(result)\n const text = name === 'spawn' ? stripSpawnTokensLine(raw) : raw\n stream.appendImmediate({ kind: 'tool-result', text, tool: name })\n })\n agent.hooks.hook('mcp:tool:after', ({ displayName, result }) => {\n stream.appendImmediate({ kind: 'tool-result', text: toolResultText(result), tool: displayName })\n })\n agent.hooks.hook('turn:after', ({ usage }) => {\n if (usage)\n setLastInputTokens(turnContextSize(usage))\n stream.flushAndUpdate(finalizeStreamingMarkdown)\n })\n\n // Subagent streams ----------------------------------------------------\n agent.hooks.hook('spawn:before', ({ id, task, depth }) => {\n const taskPreview = task.length > 80 ? `${task.slice(0, 80)}…` : task\n stream.appendImmediate({\n kind: 'spawn-start',\n text: taskPreview,\n childId: id,\n depth: depth ?? 1,\n })\n })\n agent.hooks.hook('spawn:complete', ({ id, depth, status, stats }) => {\n const tag = status === 'aborted' ? 'aborted' : status === 'error' ? 'error' : 'done'\n stream.appendImmediate({\n kind: 'spawn-end',\n text: `${tag} ${formatTokenUsage(stats)}`,\n childId: id,\n depth: depth ?? 1,\n })\n })\n agent.hooks.hook('spawn:error', ({ id, depth, error }) => {\n stream.appendImmediate({\n kind: 'error',\n text: `[${id}] ${error.message}`,\n childId: id,\n depth: depth ?? 1,\n })\n })\n agent.hooks.hook('child:stream:thinking', ({ delta, childId, depth }) => {\n stream.queueStreamDelta('thinking', delta, { childId, depth })\n })\n agent.hooks.hook('child:stream:text', ({ delta, childId, depth }) => {\n stream.queueStreamDelta('markdown', delta, { childId, depth })\n })\n agent.hooks.hook('child:tool:before', ({ name, input, childId, depth }) => {\n stream.appendImmediate({\n kind: 'tool',\n text: toolCallPreview(name, input),\n tool: name,\n childId,\n depth,\n })\n })\n agent.hooks.hook('child:tool:after', ({ name, result, childId, depth }) => {\n stream.appendImmediate({\n kind: 'tool-result',\n text: toolResultText(result),\n tool: name,\n childId,\n depth,\n })\n })\n agent.hooks.hook('child:stream:end', ({ childId }) => {\n // Finalize the child's trailing markdown block so the next child event\n // (tool call, spawn-end) doesn't keep appending into it.\n stream.flushAndUpdate(prev => finalizeStreamingMarkdownForOwner(prev, childId))\n })\n\n return agent\n }, [providerRegistry, stream, gateDecision])\n\n // -------------------------------------------------------------------------\n // Session activation — load (or create) a Session, rebuild the agent, replay\n // turns into the transcript, and persist the resume hint.\n // -------------------------------------------------------------------------\n\n const refreshSessions = useCallback(async () => {\n const list = await listSessionMeta(store)\n setSessions(list)\n return list\n }, [store])\n\n const teardown = useCallback(async () => {\n // Abort the active run + drain the safe-mode queue *before* destroying\n // the agent. Without this:\n // - `agent.destroy()` is not an abort — it just closes MCP and the\n // execution handle, leaving any in-flight `run()` to keep firing\n // stream/tool hooks. Those write into the shared `stream` buffer,\n // so deltas from the doomed run land in the new session's\n // transcript after `activateSession` mounts it.\n // - Pending approvals (head-of-queue + tail) stay parked, so the\n // next prompt that opens the chat screen inherits them.\n //\n // Order matters: `denyAll()` first so any gate handler currently\n // awaiting `requestApproval(...)` resolves with `deny` and the loop\n // unwinds cleanly; `abort()` second so the abort signal interrupts the\n // next iteration; `destroy()` last to close handles after the loop\n // unwound. Each step is independently best-effort — exceptions are\n // logged and swallowed so a partial failure doesn't leak state.\n try {\n denyAll()\n }\n catch (err) {\n debugLog('teardown: denyAll failed', err)\n }\n try {\n agentRef.current?.abort()\n }\n catch (err) {\n debugLog('teardown: agent.abort failed', err)\n }\n stream.reset()\n await agentRef.current?.destroy().catch(err => debugLog('agent.destroy failed', err))\n agentRef.current = null\n sessionRef.current = null\n }, [stream, denyAll])\n\n const activateSession = useCallback(async (id: string | null, key: ProviderKey) => {\n await teardown()\n\n const loaded = id ? await loadSession(store, id) : null\n const session = loaded ?? await createSession({ store, ...(id ? { id } : {}) })\n\n sessionRef.current = session\n agentRef.current = buildAgent(session, key)\n\n setEvents(eventsFromTurns(session.turns, session.runs))\n setLastInputTokens(lastContextSizeFromTurns(session.turns, session.runs))\n setCurrentSession({\n id: session.id,\n title: titleFromTurns(session.turns) ?? 'untitled',\n turnCount: session.turns.length,\n updatedAt: Date.now(),\n })\n setScreen('chat')\n stateStore.save({\n ...stateStore.load(),\n lastProvider: key,\n lastSessionId: session.id,\n })\n }, [teardown, buildAgent, store, stateStore])\n\n // -------------------------------------------------------------------------\n // Resume on launch — when a previous provider was remembered, jump straight\n // into the last session (or the sessions list if it's been deleted).\n // -------------------------------------------------------------------------\n\n // Narrow the deps to exactly what the effect reads. Depending on `config`\n // as a whole would re-fire if any future refactor made the resolved config\n // non-stable — and re-firing here re-runs `activateSession`, destroying and\n // rebuilding the agent in a loop (this was the original leak).\n useEffect(() => {\n if (!resumeProvider)\n return\n let cancelled = false\n void (async () => {\n if (lastResumedSessionId) {\n const data = await store.load(lastResumedSessionId)\n if (cancelled)\n return\n if (data) {\n await activateSession(lastResumedSessionId, resumeProvider.key)\n return\n }\n }\n const list = await refreshSessions()\n if (cancelled)\n return\n if (list.length === 0)\n await activateSession(null, resumeProvider.key)\n else\n setScreen('sessions')\n })()\n return () => { cancelled = true }\n }, [activateSession, refreshSessions, resumeProvider, lastResumedSessionId, store])\n\n // -------------------------------------------------------------------------\n // Screen actions.\n // -------------------------------------------------------------------------\n\n const onPickProvider = useCallback(async (p: ProviderAuth) => {\n const next = makePicked(p)\n if (!next)\n return // provider isn't in the registry — guarded by AuthScreen, defensive here\n setPicked(next)\n stateStore.save({ ...stateStore.load(), lastProvider: p.key })\n const list = await refreshSessions()\n if (list.length === 0)\n await activateSession(null, p.key)\n else\n setScreen('sessions')\n }, [refreshSessions, activateSession, makePicked, stateStore])\n\n const onCreateSession = useCallback(async () => {\n if (picked)\n await activateSession(null, picked.provider.key)\n }, [picked, activateSession])\n\n const onSwitchSession = useCallback(async (id: string) => {\n if (picked)\n await activateSession(id, picked.provider.key)\n }, [picked, activateSession])\n\n const onOpenSessions = useCallback(async () => {\n await refreshSessions()\n setScreen('sessions')\n }, [refreshSessions])\n\n const onAbort = useCallback(() => {\n // Flush any pending approvals so the run can unwind cleanly; otherwise\n // `agent.abort()` only flips the run's signal and our gate handlers\n // would still hang in `await requestApproval(...)`.\n denyAll()\n agentRef.current?.abort()\n }, [denyAll])\n\n const onPickModel = useCallback((modelId: string) => {\n setPicked((prev) => {\n if (!prev)\n return prev\n // Remember per-provider so the next launch resumes the right model.\n const prior = stateStore.load()\n stateStore.save({\n ...prior,\n lastModelByProvider: { ...prior.lastModelByProvider, [prev.provider.key]: modelId },\n })\n return { ...prev, model: modelId }\n })\n modal.close()\n }, [modal, stateStore])\n\n // Agent profile switch. The ref is written synchronously so a follow-up\n // `activateSession` call in the same tick rebuilds against the new\n // preset; the state update keeps the footer badge + picker re-render in\n // sync. When the agent change happens mid-session we re-activate the\n // active session, which destroys the old agent and creates a fresh one\n // bound to the new profile (`buildAgent` reads the ref).\n const onPickAgent = useCallback(async (id: string) => {\n const profile = agentRegistry[id]\n if (!profile)\n return // unknown id — picker already filters, this is a defensive guard\n pickedAgentRef.current = profile\n setPickedAgent(profile)\n stateStore.save({ ...stateStore.load(), lastAgent: id })\n modal.close()\n // Rebuild the active agent so the new system prompt + tool set take\n // effect on the *next* turn. Conversation history is preserved (we\n // load the same session id); only the live agent's bindings change.\n if (picked && currentSession && !busy)\n await activateSession(currentSession.id, picked.provider.key)\n }, [agentRegistry, picked, currentSession, busy, activateSession, stateStore, modal])\n\n // Shift+Tab — cycle to the next registered profile, wrapping. Insertion\n // order in `agentRegistry` is the cycle order (matches the picker), so\n // pressing the key twice in a 2-profile registry returns to the original\n // — the same toggle muscle memory other agentic TUIs train. Reads the\n // ref directly so the lookup always sees the latest profile even if\n // state hasn't flushed yet between rapid presses.\n const onCycleAgent = useCallback(async () => {\n const ids = Object.keys(agentRegistry)\n if (ids.length <= 1)\n return\n const currentIdx = ids.indexOf(pickedAgentRef.current.id)\n const nextId = ids[(currentIdx + 1) % ids.length]\n await onPickAgent(nextId)\n }, [agentRegistry, onPickAgent])\n\n // `events.length` is read through a ref so the callback's identity stays\n // stable across delta-by-delta state updates (events grow on every\n // streamed markdown token). Without the ref the callback rebinds every\n // delta, which cascades into `ChatScreen` → `PromptBlock` → the textarea's\n // `onKeyDown` rebinding, defeating any future memoization downstream.\n const eventsLengthRef = useRef(0)\n eventsLengthRef.current = events.length\n\n const onSubmitPrompt = useCallback(async (prompt: string) => {\n const agent = agentRef.current\n const session = sessionRef.current\n if (!agent || !session || !picked || !prompt.trim())\n return\n\n if (eventsLengthRef.current > 0)\n stream.appendImmediate({ kind: 'separator', text: '' })\n stream.appendImmediate({ kind: 'info', text: `❯ ${prompt}` })\n setBusy(true)\n\n try {\n await agent.run({ model: picked.model, prompt })\n await session.save().catch(err => debugLog('session.save failed', err))\n setCurrentSession(prev => prev\n ? {\n ...prev,\n title: titleFromTurns(session.turns) ?? prev.title,\n turnCount: session.turns.length,\n updatedAt: Date.now(),\n }\n : prev)\n }\n catch (err) {\n stream.appendImmediate({ kind: 'error', text: err instanceof Error ? err.message : String(err) })\n }\n finally {\n stream.flushAndUpdate(finalizeStreamingMarkdown)\n setBusy(false)\n }\n }, [picked, stream])\n\n // -------------------------------------------------------------------------\n // Top-level keyboard. Each screen handles its own primary action; this only\n // routes esc + the modal shortcuts.\n //\n // The callback is recreated on every render, but OpenTUI's `useKeyboard`\n // wraps it with `useEffectEvent` internally — registration only happens once\n // and the handler always sees the latest closure. No leak.\n // -------------------------------------------------------------------------\n\n // Head of the safe-mode approval queue. `null` means \"nothing pending\"\n // and the chat screen shows the prompt input as usual. Defined here so the\n // keyboard handler and the footer hints both see it.\n const pendingApproval = queue[0] ?? null\n\n // Settings → \"Re-configure providers\" jumps back to the auth screen. We\n // capture this as a callback so the modal closes cleanly before the screen\n // transition (avoiding a brief flash of the modal over the new screen).\n //\n // Gated on `!busy && !pendingApproval`: switching providers mid-stream\n // would have the old agent keep firing stream/tool hooks into the shared\n // buffer after the new session mounts, leaking deltas + approvals across\n // sessions. The Settings modal still renders the row when wiring is\n // present; the no-op gate just refuses the action until the run finishes.\n // For the gating to be reactive the dep must include `busy` so the modal\n // re-binds when the run state flips.\n const onReauth = useCallback(() => {\n if (busy || pendingApproval)\n return\n modal.close()\n setScreen('auth')\n }, [modal, busy, pendingApproval])\n\n // Drives both the `ctrl+a` shortcut wiring and the hint visibility — a\n // single-profile registry hides the affordance entirely so it doesn't\n // suggest a picker that has no choices to offer. Defined before\n // `useKeyboard` so the closure reads a stable, hoisted-friendly binding.\n const hasMultipleAgents = useMemo(\n () => Object.keys(agentRegistry).length > 1,\n [agentRegistry],\n )\n\n useKeyboard((key) => {\n if (modal.isOpen)\n return\n if (key.ctrl && key.name === ',' && screen !== 'auth') {\n modal.open(<SettingsModal actions={{ onReauth }} />)\n return\n }\n if (key.ctrl && key.name === 'm' && screen === 'chat' && picked && !busy) {\n modal.open(\n <ModelPickerModal\n models={modelsFor(picked.provider.key)}\n currentModelId={picked.model}\n onPick={onPickModel}\n />,\n )\n return\n }\n // Ctrl+A — agent profile picker. Only meaningful when more than one\n // profile is registered; hosts with a single-agent registry never see\n // a useful list, so we suppress the shortcut to avoid an empty modal.\n if (key.ctrl && key.name === 'a' && screen === 'chat' && hasMultipleAgents && !busy) {\n modal.open(\n <AgentPickerModal\n agents={agentRegistry}\n currentAgentId={pickedAgent.id}\n onPick={onPickAgent}\n />,\n )\n return\n }\n // Shift+Tab — quick-cycle through agent profiles without opening the\n // picker. Gated on the same conditions as Ctrl+A (chat screen, idle,\n // multi-profile registry) so the textarea never sees it as input and\n // single-agent setups don't get a no-op binding.\n if (key.shift && key.name === 'tab' && screen === 'chat' && hasMultipleAgents && !busy) {\n void onCycleAgent()\n return\n }\n if (key.name !== 'escape')\n return\n // Esc always aborts the in-flight run (whether or not a prompt is\n // pending) — `onAbort` denies every queued approval and flips the\n // agent's signal. Per-call accept/deny lives in the approval picker\n // itself; this keeps \"esc = stop everything\" as a single rule.\n if (busy || pendingApproval)\n return onAbort()\n if (screen === 'chat')\n return onOpenSessions()\n if (screen === 'sessions') {\n if (currentSession)\n setScreen('chat')\n else\n renderer.destroy()\n return\n }\n // auth screen — if the user got here from settings (picked already\n // exists), bounce them back to wherever they were. Only exit on a true\n // first-launch (no provider picked yet).\n if (picked) {\n setScreen(currentSession ? 'chat' : 'sessions')\n return\n }\n renderer.destroy()\n })\n\n // -------------------------------------------------------------------------\n // Derived footer state.\n // -------------------------------------------------------------------------\n\n const hints: Hint[] = useMemo(\n () => buildHints(screen, busy, !!pendingApproval, currentSession, hasMultipleAgents),\n [screen, busy, pendingApproval, currentSession, hasMultipleAgents],\n )\n\n const contextUsage: ContextUsage | null = useMemo(() => {\n if (screen !== 'chat' || !picked)\n return null\n const descriptor = providerRegistry[picked.provider.key]\n if (!descriptor)\n return null\n // `getContextWindow` already checks `descriptor.models` first and falls\n // back to pi-ai's registry — one call covers both cases.\n const max = getContextWindow(descriptor, picked.model)\n return max ? { used: lastInputTokens, max } : null\n }, [screen, picked, lastInputTokens, providerRegistry])\n\n // Drop the agent + clear timers when the app unmounts.\n useEffect(() => () => { void teardown() }, [teardown])\n\n return (\n <box style={{ flexDirection: 'column', flexGrow: 1 }}>\n <box style={{ flexDirection: 'column', flexGrow: 1, paddingLeft: 1, paddingRight: 1 }}>\n {screen === 'auth' && <AuthScreen onPick={onPickProvider} />}\n {screen === 'sessions' && (\n <SessionsScreen\n sessions={sessions}\n currentId={currentSession?.id ?? null}\n onPick={onSwitchSession}\n onCreate={onCreateSession}\n />\n )}\n {screen === 'chat' && (\n <ChatScreen\n events={events}\n busy={busy}\n settings={settings}\n onSubmit={onSubmitPrompt}\n session={currentSession}\n pending={pendingApproval}\n onApproval={resolveHead}\n />\n )}\n </box>\n <Footer\n hints={hints}\n picked={picked}\n agent={screen === 'chat' && hasMultipleAgents ? pickedAgent : null}\n context={contextUsage}\n />\n </box>\n )\n}\n\nfunction buildHints(\n screen: Screen,\n busy: boolean,\n pending: boolean,\n currentSession: SessionMeta | null,\n hasMultipleAgents: boolean,\n): Hint[] {\n if (pending)\n return [{ key: '↑↓', label: 'navigate' }, { key: '↵', label: 'select' }, { key: 'esc', label: 'abort run' }]\n if (busy)\n return [{ key: 'esc', label: 'abort' }]\n if (screen === 'auth')\n return [{ key: '↑↓', label: 'navigate' }, { key: '↵', label: 'select' }, { key: 'esc', label: 'exit' }]\n if (screen === 'sessions') {\n return [\n { key: '↑↓', label: 'navigate' },\n { key: '↵', label: 'open' },\n { key: 'ctrl+,', label: 'settings' },\n { key: 'esc', label: currentSession ? 'back' : 'exit' },\n ]\n }\n // Chat screen. Advertise `shift+tab` as the primary agent-switch\n // affordance — fast cycling through profiles without opening the\n // picker. Ctrl+A still opens the descriptive picker for power users,\n // but isn't advertised here to keep the hint row short. Both are\n // suppressed when there's only one profile to switch between.\n return [\n { key: '↵', label: 'send' },\n ...(hasMultipleAgents ? [{ key: 'shift+tab', label: 'agent' }] : []),\n { key: 'ctrl+m', label: 'model' },\n { key: 'ctrl+,', label: 'settings' },\n { key: 'esc', label: 'sessions' },\n ]\n}\n","import type { FiletypeParserOptions } from '@opentui/core'\nimport { addDefaultParsers, getTreeSitterClient } from '@opentui/core'\n\n/**\n * Register Tree-sitter parsers for the languages we'd like highlighted\n * inside fenced markdown code blocks.\n *\n * OpenTUI ships JS/TS/Markdown/Zig out of the box. Anything else needs a\n * Tree-sitter `.wasm` grammar + a `highlights.scm` capture query. We fetch\n * both from the upstream Tree-sitter grammar repos; OpenTUI's worker\n * caches them under its data path (`~/.local/share/opentui/...` by\n * default) so the download is a one-shot cost per language per machine.\n *\n * `aliases` lets a single grammar handle multiple fence info-strings — e.g.\n * `bash` also matches ` ```sh ` and ` ```shell `. The model picks fences\n * inconsistently across providers; aliases save us from missing highlights\n * on synonyms.\n *\n * Runtime caveats:\n * - **First use** of a language triggers an HTTPS download. Subsequent\n * uses (same machine, same data path) are instant.\n * - **Compiled binaries** (`bun --compile`) still work — the data path\n * is a writable OS dir, not the bunfs. Air-gapped deployments would\n * need to either pre-populate the cache or migrate to local-file\n * vendoring via `with { type: 'file' }` imports (see\n * https://opentui.com/docs/reference/tree-sitter/#use-local-files).\n * - If a download fails (offline / firewall), the language renders as\n * plain `markup.raw.block` — no crash, just no syntax color.\n *\n * Versions are pinned in the WASM URLs so a grammar repo's `master`\n * landing a breaking change can't silently affect us.\n */\nconst EXTRA_PARSERS: FiletypeParserOptions[] = [\n {\n filetype: 'python',\n aliases: ['py'],\n wasm: 'https://github.com/tree-sitter/tree-sitter-python/releases/download/v0.23.6/tree-sitter-python.wasm',\n queries: {\n highlights: ['https://raw.githubusercontent.com/tree-sitter/tree-sitter-python/v0.23.6/queries/highlights.scm'],\n },\n },\n {\n filetype: 'bash',\n aliases: ['sh', 'shell', 'zsh'],\n wasm: 'https://github.com/tree-sitter/tree-sitter-bash/releases/download/v0.23.3/tree-sitter-bash.wasm',\n queries: {\n highlights: ['https://raw.githubusercontent.com/tree-sitter/tree-sitter-bash/v0.23.3/queries/highlights.scm'],\n },\n },\n {\n filetype: 'json',\n wasm: 'https://github.com/tree-sitter/tree-sitter-json/releases/download/v0.24.8/tree-sitter-json.wasm',\n queries: {\n highlights: ['https://raw.githubusercontent.com/tree-sitter/tree-sitter-json/v0.24.8/queries/highlights.scm'],\n },\n },\n {\n filetype: 'rust',\n aliases: ['rs'],\n wasm: 'https://github.com/tree-sitter/tree-sitter-rust/releases/download/v0.23.2/tree-sitter-rust.wasm',\n queries: {\n highlights: ['https://raw.githubusercontent.com/tree-sitter/tree-sitter-rust/v0.23.2/queries/highlights.scm'],\n },\n },\n {\n filetype: 'go',\n aliases: ['golang'],\n wasm: 'https://github.com/tree-sitter/tree-sitter-go/releases/download/v0.23.4/tree-sitter-go.wasm',\n queries: {\n highlights: ['https://raw.githubusercontent.com/tree-sitter/tree-sitter-go/v0.23.4/queries/highlights.scm'],\n },\n },\n {\n filetype: 'yaml',\n aliases: ['yml'],\n wasm: 'https://github.com/tree-sitter-grammars/tree-sitter-yaml/releases/download/v0.7.0/tree-sitter-yaml.wasm',\n queries: {\n highlights: ['https://raw.githubusercontent.com/tree-sitter-grammars/tree-sitter-yaml/v0.7.0/queries/highlights.scm'],\n },\n },\n {\n filetype: 'html',\n aliases: ['htm'],\n wasm: 'https://github.com/tree-sitter/tree-sitter-html/releases/download/v0.23.2/tree-sitter-html.wasm',\n queries: {\n highlights: ['https://raw.githubusercontent.com/tree-sitter/tree-sitter-html/v0.23.2/queries/highlights.scm'],\n },\n },\n {\n filetype: 'css',\n wasm: 'https://github.com/tree-sitter/tree-sitter-css/releases/download/v0.23.2/tree-sitter-css.wasm',\n queries: {\n highlights: ['https://raw.githubusercontent.com/tree-sitter/tree-sitter-css/v0.23.2/queries/highlights.scm'],\n },\n },\n]\n\nlet registered = false\n\n/**\n * Register the extra Tree-sitter parsers + start the worker. Idempotent —\n * subsequent calls are no-ops. Safe to invoke from `runTui()` and from\n * composition hosts that mount `<App>` directly.\n */\nexport async function setupTreeSitter(): Promise<void> {\n if (registered)\n return\n registered = true\n addDefaultParsers(EXTRA_PARSERS)\n // Initialize the global client. The actual grammar `.wasm` downloads are\n // lazy — they happen on the first fence of each language — but the worker\n // needs to be alive before any markdown renders.\n await getTreeSitterClient().initialize()\n}\n","/** @jsxImportSource @opentui/react */\nimport type { ChatOptions } from '../chat/config'\nimport { createCliRenderer } from '@opentui/core'\nimport { createRoot } from '@opentui/react'\nimport { resolveConfig } from '../chat/config'\nimport { createTuiStore } from '../session/sqlite'\nimport { App } from './app'\nimport { setupTreeSitter } from './tree-sitter'\n\n// ---------------------------------------------------------------------------\n// Public API\n//\n// `runTui(options)` is the one-shot launcher. For consumers who want to embed\n// the App inside a renderer they already control, the OpenTUI-specific\n// building blocks below are also exported individually.\n//\n// The renderer-agnostic engine (auth, credentials, providers, safe-mode,\n// store, streaming, settings, theme palette, formatters, markdown healer,\n// `resolveConfig`, …) lives in `zidane/chat`. Import from there directly if\n// you're building a non-TUI shell.\n// ---------------------------------------------------------------------------\n\n/**\n * Tracks whether `runTui` has been invoked in this process. `createCliRenderer`\n * installs signal handlers + hijacks stdin; calling it a second time produces\n * undefined behavior. We bail loudly instead.\n */\nlet runTuiInvoked = false\n\n/**\n * Boot a full chat TUI with sensible defaults. **Does not return** under\n * normal use — it terminates the host process via `process.exit(0)` once\n * the user dismisses the renderer (Ctrl+C / Esc on the auth screen / a\n * non-zero exit code is mapped via the `catch` path below).\n *\n * **One-shot:** this function may only be invoked once per process. The\n * underlying OpenTUI renderer wires up global terminal state on init.\n *\n * **Why it exits the process:** after the renderer's `destroy()` restores\n * the terminal, React's reconciler and OpenTUI's internal listeners can\n * keep the Node/Bun event loop open indefinitely — the script appears to\n * hang in `bun run`, and under `bun --watch run` the watcher waits forever\n * for the child to exit. Forcing a clean exit here is the contract that\n * makes `runTui` a true one-shot launcher.\n *\n * Hosts that need post-renderer cleanup should mount `<App config={...} />`\n * against their own `createCliRenderer()` instead of calling `runTui()`.\n *\n * Env-var overrides (handy when launching from CI or restricted shells):\n * - `ZIDANE_STORAGE_DIR` — sets `storageDir`\n * - `ZIDANE_PREFIX` — sets `prefix`\n *\n * Hosts building on top of `zidane/tui` typically want their own env vars\n * (e.g. `MYAPP_STORAGE_DIR`); read them in your launch script and forward\n * to `runTui({ storageDir, prefix })`.\n *\n * ```ts\n * import { BUILTIN_AGENTS, BUILTIN_PROVIDERS } from 'zidane/chat'\n * import { runTui } from 'zidane/tui'\n * import { createRemoteStore } from 'zidane/session' // for the `store` option\n *\n * await runTui() // ~/.zidane/sessions.db + state.json\n * await runTui({ prefix: '.myapp' }) // ~/.myapp/...\n * await runTui({ storageDir: '/data', prefix: 'myapp' })\n * await runTui({ providers: { ...BUILTIN_PROVIDERS, mine: myDescriptor } })\n * await runTui({ store: createRemoteStore({ url: '…' }) })\n * await runTui({ agents: { ...BUILTIN_AGENTS, debug: myDebugProfile } })\n * ```\n */\nexport async function runTui(options: Partial<ChatOptions> = {}): Promise<never> {\n if (runTuiInvoked) {\n throw new Error(\n 'runTui() can only be invoked once per process. '\n + 'Compose `<App config={resolveConfig(...)} />` against your own renderer '\n + 'if you need to run multiple TUIs in the same lifetime.',\n )\n }\n runTuiInvoked = true\n\n // Register extra Tree-sitter parsers (python, bash, json, rust, go,\n // yaml, html, css) so fenced code blocks in those languages get\n // syntax highlighting. The grammar `.wasm` files are downloaded\n // lazily on first use and cached under OpenTUI's data path. Swallow\n // init errors — if the worker can't start (offline, sandboxed FS),\n // the renderer still works, those fences just render as plain text.\n await setupTreeSitter().catch((err) => {\n const cause = err instanceof Error ? err.message : String(err)\n process.stderr.write(`[zidane/tui] tree-sitter setup failed: ${cause}\\n`)\n })\n\n // `resolveConfig` wires `ZIDANE_CREDENTIALS_PATH` and applies stored API\n // keys into `process.env` internally — both happen before any provider\n // factory is constructed, so the wizard can render even on fresh installs\n // where the harness providers would otherwise throw \"No API key found\".\n //\n // `store` is injected as a factory so `zidane/chat` stays free of\n // `bun:sqlite`. Callers can override by passing their own `store` in\n // `options`; the explicit fallback keeps `runTui()` usable with zero args\n // while letting hosts swap in their own adapter.\n const config = resolveConfig({\n ...options,\n store: options.store ?? (paths => createTuiStore(paths.db)),\n })\n\n // Promise + explicit resolver: avoid the `let done!: ...` non-null trick.\n let done: () => void = () => {}\n const exited = new Promise<void>((resolve) => { done = resolve })\n\n const renderer = await createCliRenderer({\n exitOnCtrlC: true,\n onDestroy: () => done(),\n // OpenTUI debounces resize events by 100ms by default, which freezes the\n // current frame while the user drags the terminal corner and produces the\n // visible \"snap\" once the drag stops. Setting this to 0 re-lays out on\n // every SIGWINCH — the OS already caps the rate, so we just track it.\n debounceDelay: 0,\n })\n\n createRoot(renderer).render(<App config={config} />)\n\n await exited\n\n // Force a clean exit. See JSDoc above for rationale — without this the\n // process hangs after the renderer's `destroy()` returns, which manifests\n // as a stuck `bun --watch` parent and a terminal that needs a second Ctrl+C\n // to actually surrender control.\n process.exit(0)\n}\n\n// ---------------------------------------------------------------------------\n// TUI-specific composition exports.\n//\n// Renderer-agnostic engine (auth, store, safe-mode, settings, providers,\n// streaming hooks, theme palette, …) is in `zidane/chat`. Don't re-export it\n// from here — keep `zidane/tui` focused on OpenTUI presentation primitives so\n// non-TUI consumers (a GUI, an SDK, a CI script) never pull OpenTUI in.\n// ---------------------------------------------------------------------------\n\nexport { accentColor, AgentPickerModal } from './agent-picker'\nexport { App } from './app'\nexport {\n type ContextUsage,\n Footer,\n type Hint,\n isVisible,\n marginTopFor,\n onInputSubmit,\n Spinner,\n Transcript,\n} from './components'\nexport { Modal, type ModalProps, ModalRoot, useModal, useModalAwareFocus } from './modal'\nexport { ModelPickerModal } from './model-picker'\nexport { AuthScreen, ChatScreen, SessionsScreen } from './screens'\nexport { type SettingsActions, SettingsModal } from './settings-modal'\nexport { buildMdStyle, useMdStyle } from './theme'\n"],"mappings":"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"}
|