UNPKG

eve

Version:

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

187 lines (186 loc) 7.56 kB
/** Pure borderless setup menus. Interaction and terminal lifecycle belong to the renderer. */ import type { ChannelSetupAction, PromptOption } from "#setup/cli/index.js"; import { type SearchActionOption, type SelectState } from "#setup/cli/select-state.js"; import type { PlannerNavigation, SelectMetadata, SelectNotice } from "#setup/prompter.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; /** Inert context rendered beneath the heading and above the controls. */ description?: string; /** Labeled facts rendered beneath the description and above the controls. */ metadata?: readonly SelectMetadata[]; error?: string; /** Outcome lines from earlier menu laps, shown beneath the options. */ notices?: readonly SelectNotice[]; /** Optional batch-planner navigation grammar. */ navigation?: PlannerNavigation; } 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; footerHints?: readonly 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"; layout?: "stacked"; 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"; title?: string; 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; /** Progress owned by an enclosing journey, visible through nested questions and waits. */ navigation?: PlannerNavigation; lines: readonly FlowPanelLine[]; content: FlowPanelContent; } export declare function flowMessageRows(lines: readonly FlowPanelLine[], theme: Theme): string[]; /** * Paints the setup 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[]; /** * A dim, background-free selection badge carrying the Enter affordance, e.g. * `↵`, `↵ change`, `↵ validate`. */ export declare function enterBadge(theme: Theme, label?: string): 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 {};