@capgo/cli
Version:
A CLI to upload to capgo servers
92 lines (91 loc) • 4.68 kB
TypeScript
/**
* 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;