import type { Output } from "./shared.js"; /** TTY + no opt-outs → the pretty renderer; anything else keeps plain output. NO_COLOR and CI follow the "present and non-empty" convention. ONE copy of that law, in core, because the boot summary degrades by the same rule. */ export declare function usePrettyOutput(stream?: { isTTY?: boolean; }, env?: Record): boolean; export interface SelectOption { value: string; label: string; /** Dim parenthetical after the label (e.g. what detection found). */ hint?: string; } /** The slice of a readable TTY stream the select loop needs (injectable for tests — a plain emitter drives the keypress parser without a PTY). */ export interface SelectInput { isTTY?: boolean; setRawMode?(mode: boolean): unknown; resume?(): unknown; pause?(): unknown; on(event: "data", listener: (chunk: Buffer | string) => void): unknown; off(event: "data", listener: (chunk: Buffer | string) => void): unknown; } export interface PrettyOutput extends Output { /** Braille spinner for a slow phase; any log/error line clears the frame. */ spin(label: string): void; stopSpin(): void; /** The styled [Y/n] confirm — Enter accepts the default, answer echoed. */ confirm(question: string, defaultYes?: boolean): Promise; /** The styled select — arrows move, Enter accepts, number keys pick directly; collapses to the chosen answer. Number keys cover options 1-9 only: keep lists at nine options or fewer (a longer list stays arrow-navigable, but two-digit entry is deliberately not built). */ select(question: string, options: SelectOption[], defaultIndex?: number): Promise; /** A free-text answer; Enter returns `defaultValue` where one is given, else "" and the caller decides what a skip means. The echoed receipt shows what the answer actually WAS, so an accepted default never reads as "skipped". Non-TTY stdin never prompts — "" stands. */ text(question: string, hint?: string, defaultValue?: string): Promise; /** A secret: the typing is not echoed and only a masked receipt reaches the transcript. The value itself is NEVER written to the terminal. */ secret(question: string, hint?: string): Promise; /** A pretty-only result block. It has no plain sibling on purpose: callers keep emitting their plain lines, and this restyles nothing — it is for blocks the pretty run composes itself. */ block(title: string, lines: string[], marker?: "◆" | "◇"): void; /** The staggered sibling of block(): waits for the banner arrival, plays an optional spinner beat (the detection narration), then lands the lines ~stepMs apart so the section arrives as a rhythm instead of a burst. A line carrying its own beat gets a labeled spinner moment first — the scan finds things one at a time. Pretty-only, like block(). */ revealBlock(title: string, lines: Array, options?: { stepMs?: number; beat?: string; }): Promise; /** The `└ Done in Xs` footer (red `Failed` when the command exits non-zero); `stats` is the dim tail that says what the run actually achieved, and a dim star line closes the run. */ done(durationMs: number, ok: boolean, stats?: string): void; /** Settles when the banner has finished arriving. Printing never waits on it and nothing cuts it short: the tagline, the header and the scan all print BELOW the art while the frames repaint above them, so the run keeps talking through the wave. This is for the one caller that wants the arrival SEEN — it awaits whatever is left after its own work, and pays nothing it did not already spend. A cancel still settles it instantly: Ctrl-C aborts, which paints the finished mark before the run ends. */ arrived: Promise; } /** The one measurement in this file: terminal CELLS a string occupies, with SGR sequences taking none. Every width, wrap point and row count derives from this — a miscount here puts the select's cursor-up on the wrong row. */ export declare function displayWidth(text: string): number; /** The plain-terminal select for non-pretty interactive runs: numbered list + readline. Non-TTY runs never prompt — the default option stands; an empty, garbage, or out-of-range answer also settles on the default. Streams are injectable for tests only; call sites use the defaults. */ export declare function plainSelect(question: string, options: SelectOption[], defaultIndex?: number, input?: NodeJS.ReadableStream & { isTTY?: boolean; }, output?: NodeJS.WritableStream & { isTTY?: boolean; }): Promise; /** The plain-terminal free-text prompt — plainSelect's sibling, same non-TTY guard: a piped run never prompts and answers "". Enter takes `defaultValue` where one is given, so "" always means "nobody was asked", never "the person accepted the default". */ export declare function plainText(question: string, hint?: string, defaultValue?: string, input?: NodeJS.ReadableStream & { isTTY?: boolean; }, output?: NodeJS.WritableStream & { isTTY?: boolean; }): Promise; /** The plain-terminal secret prompt — same non-TTY guard as plainSelect; the typing is swallowed and only the masked receipt is echoed. */ export declare function plainSecret(question: string, hint?: string, input?: NodeJS.ReadableStream & { isTTY?: boolean; }, output?: NodeJS.WritableStream & { isTTY?: boolean; }): Promise; export interface PrettyOptions { /** The header command — `┌ vendo init`. */ command?: string; write?: (chunk: string) => void; input?: SelectInput; promptOutput?: NodeJS.WritableStream & { isTTY?: boolean; }; /** The banner above the header — it arrives as an animation and settles. */ banner?: boolean; env?: Record; /** Terminal width to wrap to. Unset follows the real stdout, and an unknown width (a pipe, an injected test writer) never wraps. */ columns?: number; } export declare function createPrettyOutput(options?: PrettyOptions): PrettyOutput;