/** * Viewport for the live human report. `renderQuotaTui` is height-independent by * design: it lays the cards out for the terminal's width and returns however * many lines that takes. The live loop paints into the alternate screen, which * has no scrollback, so a frame taller than the terminal scrolls its own header * and first cards into nothing. This module windows that frame onto the rows * actually available and reports what is off-screen, so every line stays * reachable. Pure string math - no ANSI, no terminal I/O, no derivation. */ export type ScrollStatus = { /** True when the report is taller than the viewport and is being windowed. */ scrollable: boolean; /** Report lines hidden above the window. */ offset: number; /** Largest offset that still fills the window; the bottom of the report. */ maxOffset: number; }; export type ScrolledFrame = { /** The lines to paint, already limited to the viewport height. */ text: string; /** `offset` clamped to the report's real bounds. */ offset: number; maxOffset: number; scrollable: boolean; /** Report lines inside the scrolling region; the page-key step. */ pageLines: number; status: ScrollStatus; }; export type ScrollFrameOptions = { /** Terminal height. Unknown or non-positive means no windowing at all. */ rows?: number; offset?: number; /** * Closing line, rendered by the caller so it can carry the report's styling. * Omitted entirely when absent, which is what the plain loop tests exercise. */ status?: (status: ScrollStatus) => string; }; /** * Window `body` onto `rows` terminal rows. When the whole report plus its * closing line already fits, the frame is the report exactly as it renders at * full height. When it does not, the first line stays pinned at practical * heights, the last row carries the closing line when space permits, and the * rest scrolls. At tiny heights, report content takes priority over both. */ export declare function scrollFrame(body: string, options?: ScrollFrameOptions): ScrolledFrame; /** * Closing-line text: the caller's resting hint while everything fits, and the * scroll affordance - how much is off-screen in each direction, plus the keys * that move it - once the report is being windowed. */ export declare function scrollHint(status: ScrollStatus, restingHint: string): string;