UNPKG

@capgo/cli

Version:
92 lines (91 loc) 4.68 kB
/** * Fit estimation for the AI analysis result step in the onboarding TUI. * * The on-failure AI flow can return a multi-screen markdown diagnosis. If * that text doesn't fit in the user's current terminal viewport we MUST * route it through the scrollable `FullscreenAiViewer` — otherwise the * earlier lines scroll out of view and the onboarding wizard ends up in * an unreadable state. * * The estimator deliberately errs on the side of "doesn't fit": a * false-positive scroll is fine (just one more keystroke for the user), * but a false-negative inline render is bad UX (text disappears off the * top of the screen). */ export declare const AI_RESULT_CHROME_ROWS = 10; /** Strip ANSI SGR escape codes so length matches what the user actually sees. */ export declare function stripAnsi(text: string): string; /** * Estimate how many terminal rows a multi-line, possibly ANSI-styled string * will occupy when rendered by Ink at the given column width. * * Each logical line (split on '\n') becomes `ceil(visibleLen / cols)` rows, * with a floor of 1 to account for empty lines that still consume a row. */ export declare function estimateRenderedRows(text: string, terminalCols: number): number; /** * Decide whether the AI analysis text should be routed through the * scrollable fullscreen viewer. Conservative — prefers true (scroll) when * the estimate is close to the available row budget. * * @param text The AI analysis markdown (already rendered to ANSI). * @param terminalRows Total terminal rows from `useStdout().stdout?.rows`. * @param terminalCols Total terminal cols from `useStdout().stdout?.columns`. * @param chromeRows Reserved rows for the surrounding wizard chrome. * Defaults to `AI_RESULT_CHROME_ROWS`. */ export declare function isAiAnalysisTooTall(text: string, terminalRows: number, terminalCols: number, chromeRows?: number): boolean; export type AiResultStep = 'ai-analysis-result' | 'ai-analysis-result-scroll'; /** * Decide which AI-result step should be active for the CURRENT terminal size. * * Routing is BIDIRECTIONAL and driven by the single `isAiAnalysisTooTall` * predicate, so it settles deterministically at any size — at a given size * exactly one outcome is stable, so it can't oscillate: * - inline + now too tall (terminal shrank) → scroll * - scroll + now fits (terminal grew) → inline ← the missing case * * Before, only the inline→scroll direction existed: once the viewer opened * (e.g. after shrinking), growing the terminal never returned to the inline * render — the user was stuck in the scroll viewer showing "all N lines" with * empty space. * * `viewedFull` (the user manually dismissed the viewer with Esc/Enter) pins the * inline step so a later resize can't shove a dismissed analysis back into the * viewer. It only gates the inline→scroll direction; leaving the viewer when it * fits is always allowed. * * @returns the step to switch to, or `null` when the current step is already * correct (so the caller can skip a no-op `setStep`). */ export declare function resolveAiResultRoute(params: { current: AiResultStep; text: string | null; viewedFull: boolean; terminalRows: number; terminalCols: number; }): AiResultStep | null; /** * Pick the slice of `lines` starting at `scrollOffset` that PACKS the * `viewportRows` rendered rows of a terminal `terminalCols` wide. * * Packs lines until the cumulative wrapped row count reaches or exceeds * `viewportRows`, INCLUDING the line that crosses the boundary. That last line * may render past the viewport; the viewer clips it with `overflow: hidden` so * the visible area is always FULL of text when more lines remain. (Stopping * before the boundary line — the old behaviour — left the unused rows as an * empty gap when a long line couldn't fully fit.) * * Always returns at least one line if the input is non-empty and the * `scrollOffset` is in-range — even if that line wraps to more rows than the * viewport. */ export declare function pickVisibleLines(lines: string[], scrollOffset: number, viewportRows: number, terminalCols: number): string[]; /** * Compute the largest `scrollOffset` that still keeps content visible at the * bottom of the viewport — i.e. the offset where the LAST line is rendered * within the viewport. Walks backwards from the end, packing as many tail * lines as fit (accounting for wrap), and returns the offset of the first * fully-visible tail line. */ export declare function computeMaxScrollOffset(lines: string[], viewportRows: number, terminalCols: number): number;