UNPKG

eve

Version:

Filesystem-first framework for durable backend AI agents that run anywhere.

215 lines (214 loc) 8.69 kB
/** * 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 SearchActionOption, type SelectState } from "#setup/cli/select-state.js"; import type { SelectNotice } from "#setup/prompter.js"; import type { ModelSettingsRequest } from "#setup/flows/model.js"; import { type ModelEditorState } from "./model-editor.js"; import type { ProviderPickerPhase } from "./provider-picker.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<string>; 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[]; searchAction?: SearchActionOption; select: SelectState; /** Live frame rendered beside a searchable input while it loads replacement rows. */ loadingFrame?: string; /** A dim-inverse affordance appended to the cursor row, e.g. ` ↵ change `. */ cursorBadge?: string; } /** * A menu row that turns into an inline editor while the cursor rests on it. * `optionValue` names the row; the `editor` discriminant chooses the widget — * an in-place rename field, or a masked provider-key field with its own * validation phases. Rename defaults stay placeholders until typing begins; * provider keys edit in place. Layout and inline editing are orthogonal, so * the editor travels as a payload rather than as its own panel `kind`. */ interface SetupInlineEditRow { optionValue: string; caretVisible: boolean; editor: { kind: "rename"; editor: LineState; defaultValue: string; formatHint: (value: string) => string; } | { kind: "key"; phase: ProviderPickerPhase; }; } /** * Select presentation variants. The discriminant owns the interaction grammar * so feature combinations are deliberate rather than resolved by conditional * precedence inside the renderer. Inline editing is the exception: it composes * with a layout instead of defining one, so `inline-edit` carries both. */ type SetupOptionSelectPanelState = (SetupSelectPanelBase & { kind: "single"; }) | (SetupSelectPanelBase & { kind: "search"; layout?: "task-list"; placeholder?: string; }) | (SetupSelectPanelBase & { kind: "multi"; }) | (SetupSelectPanelBase & { kind: "searchable-multi"; placeholder?: string; }) | (SetupSelectPanelBase & { kind: "stacked"; }) | (SetupSelectPanelBase & { kind: "task-list"; }) | (SetupSelectPanelBase & { kind: "inline-edit"; layout: "stacked" | "task-list"; edit: SetupInlineEditRow; }); 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; } /** One already-resolved animation frame and its active color. */ export interface FlowPanelIndicator { glyph: string; color: "green" | "yellow"; } /** One live flow status after its animation frame and visual intent are resolved. */ export type FlowPanelStatus = { kind: "progress"; text: string; indicator: FlowPanelIndicator; } | { kind: "external-action"; text: string; emphasis: string; indicator: FlowPanelIndicator; }; export type FlowPanelContent = { kind: "question"; rows: readonly string[]; /** The install wait keeps its indicator above the concurrent actions. */ status?: FlowPanelStatus; } | { kind: "status"; status: FlowPanelStatus; /** Latest child-process output shown transiently beneath the status. */ preview?: string; } | { kind: "preview"; text: string; indicator: FlowPanelIndicator; } | { kind: "idle"; indicator: FlowPanelIndicator; }; /** 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 indicator — 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[]; /** The composite Change-model screen's inputs: the resolved request plus live state. */ export interface ModelEditorPanelInput { request: ModelSettingsRequest; state: ModelEditorState; } /** * A dim, background-free selection badge carrying the Enter affordance, e.g. * `↵`, `↵ change`, `↵ validate`. */ export declare function enterBadge(theme: Theme, label?: string): string; /** * A discrete reasoning track: `●` below the current notch, `◉` on it, `○` * above, joined by `─` connectors. `accent` paints the covered stretch — * notches and connectors up to the current position — blue. `index` -1 means * unset: an all-`○` track with no fill. */ export declare function reasoningTrack(input: { count: number; index: number; connectorWidth: number; accent: boolean; theme: Theme; }): string; /** * Paints the Change-model screen: a value menu whose reasoning and tier rows * adjust inline with left/right, and whose Model row opens the searchable * catalog. */ export declare function renderModelEditorQuestion(input: ModelEditorPanelInput, theme: Theme, width: number): string[]; /** Paints a text question section: message, a block-cursor 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 {};