/** * Pure rendering for the bordered setup flow panel — the input-region variant * a setup command runs inside for its whole duration (the Claude Code * `/model`-panel grammar): a full-width rule, the command as a blue title, * the flow's recent progress lines, and the active question (numbered option * rows or a text field) or the ephemeral status spinner. Behavior state comes * from the shared select reducer (`#setup/cli/select-state.js`); this module * only paints rows, so the renderer hosts lifecycle and keys while tests * assert on strings. * * Column grammar: the panel adds a one-space left margin to every row under * the rule, while each section contributes two more spaces. Titles, spinners, * notices, filter prompts, and option-state glyphs therefore all begin at * column 3. */ import type { ChannelSetupAction, PromptOption } from "#setup/cli/index.js"; import { type SelectState } from "#setup/cli/select-state.js"; import type { SelectNotice } from "#setup/prompter.js"; import { type LineState } from "./line-editor.js"; import type { Theme } from "./theme.js"; /** One row of a setup select panel; the shared prompt-option shape. */ export type SetupPanelOption = PromptOption; interface SetupQuestionPanelBase { message: string; error?: string; /** Outcome lines from earlier menu laps, shown beneath the options. */ notices?: readonly SelectNotice[]; } interface SetupSelectPanelBase extends SetupQuestionPanelBase { options: readonly SetupPanelOption[]; select: SelectState; } interface SetupEditableRow { /** * The row whose hint is a live rename field. Hovering it (cursor on the row) * makes it editable directly — typing and backspace edit the name in place — * so the editor's text and a blinking caret render only on that row. */ optionValue: string; editor: LineState; defaultValue: string; formatHint: (value: string) => string; caretVisible: boolean; } /** * Select presentation variants. The discriminant owns the interaction grammar * so feature combinations are deliberate rather than resolved by conditional * precedence inside the renderer. */ type SetupOptionSelectPanelState = (SetupSelectPanelBase & { kind: "single"; }) | (SetupSelectPanelBase & { kind: "search"; placeholder?: string; }) | (SetupSelectPanelBase & { kind: "multi"; }) | (SetupSelectPanelBase & { kind: "searchable-multi"; placeholder?: string; }) | (SetupSelectPanelBase & { kind: "stacked"; }) | (SetupSelectPanelBase & { kind: "task-list"; }) | (SetupSelectPanelBase & { kind: "editable"; edit: SetupEditableRow; }); interface SetupActionsPanelState { kind: "actions"; /** Inert explanation rendered above, and separately from, the action group. */ context: string; actions: readonly ChannelSetupAction[]; /** No action is focused until the user moves into the action group. */ cursor: number | undefined; } export type SetupSelectPanelState = SetupOptionSelectPanelState | SetupActionsPanelState; export interface SetupTextPanelState { message: string; editor: LineState; placeholder?: string; mask: boolean; error?: string; /** Context lines shown above the message; gone once the question settles. */ notices?: readonly SelectNotice[]; } export interface SetupAcknowledgePanelState { message: string; lines: readonly string[]; } /** One progress line shown inside the flow panel while it runs. */ export interface FlowPanelLine { text: string; tone: "info" | "success" | "warning" | "error"; /** * Subprocess output a warning/error settle pulled in as its evidence. * Renders like any info line in the panel, but survives the panel close * alongside the diagnostic it explains (a plain info line does not). */ evidence?: boolean; } export type FlowPanelContent = { kind: "question"; rows: readonly string[]; /** The install wait keeps its spinner above the concurrent actions. */ status?: { text: string; frame: string; }; } | { kind: "status"; status: { text: string; frame: string; }; /** Latest child-process output shown transiently beneath the status. */ preview?: string; } | { kind: "preview"; text: string; frame: string; } | { kind: "idle"; frame: string; }; /** The whole bordered section: title, recent progress, and one explicit mode. */ export interface FlowPanelState { /** The invoked command, e.g. "/deploy". Empty renders no title row. */ title: string; lines: readonly FlowPanelLine[]; content: FlowPanelContent; } /** * Paints the bordered flow panel. Everything a running command produces lives * here — progress, questions, the status spinner — and the panel vanishes * wholesale when the command resolves; only the command echo and the elbow * outcome persist in the transcript. */ export declare function renderFlowPanel(state: FlowPanelState, theme: Theme, width: number): string[]; /** * Paints a selection section for the flow panel. Ordinary selects use the * shared option reducer; concurrent actions render an explicit context row and * independent action group. A searchable select windows the option list around * the cursor and advertises the rest with a count footer. */ export declare function renderSelectQuestion(state: SetupSelectPanelState, theme: Theme, width: number): string[]; /** Paints a text question section: message, a caret-bearing input line, hints. */ export declare function renderTextQuestion(state: SetupTextPanelState, theme: Theme, width: number, caretVisible: boolean): string[]; /** * Paints a static acknowledgement section (for the flow panel): a heading and * dim body lines where option rows normally sit, held until the user * dismisses it. There is nothing to cancel — the text is the point — so the * footer advertises only enter. */ export declare function renderAcknowledgeQuestion(state: SetupAcknowledgePanelState, theme: Theme, width: number): string[]; export {};