/** * @license * Copyright 2026 Steven Roussey * SPDX-License-Identifier: Apache-2.0 */ import type { CensusList } from "./runCensus"; /** * How a run's rows are fitted into the terminal, independent of what draws * them. * * Two rules shape everything here. * * **The live region never shrinks.** A block whose height tracks its content * drags the footer up the screen every time a list gets shorter, and a footer * that moves is a footer nobody can read. The region grows to fit what arrives * and then holds that height ({@link stickyRegionHeight}) until the terminal * itself changes size. * * **Depth pays for the overflow.** When the tree wants more rows than the * terminal has, the rows that go are the innermost ones — a Map's per-item * detail — because the ancestors are the context that makes the detail legible. * Losing the Map's own row to show six more of its items is exactly backwards, * so {@link planRunViewport} shrinks the deepest list first and only climbs * when that list is down to a single row. */ /** Rows one list shows before it starts hiding siblings. */ export declare const MAX_VISIBLE_LIST_ROWS = 6; /** A truncated list always keeps at least this many rows — an empty parent says nothing. */ export declare const MIN_VISIBLE_LIST_ROWS = 1; export interface RunViewportPlan { /** Visible sibling count per list key. Absent keys fall back to {@link MAX_VISIBLE_LIST_ROWS}. */ readonly caps: ReadonlyMap; /** Rows the plan expects to draw. */ readonly rows: number; /** Sibling rows hidden across every list. */ readonly hidden: number; /** True when even the minimum plan overflows the budget. */ readonly overflowing: boolean; } export declare const EMPTY_RUN_VIEWPORT_PLAN: RunViewportPlan; /** The cap a list draws with, given a plan that may not mention it. */ export declare function listCap(plan: RunViewportPlan, listKey: string): number; /** * The slice of a list that is drawn: the tail. * * Rows are sorted completed-first, so the tail is the work in flight. A list * that dropped its tail would animate a spinner nobody can see. */ export declare function visibleSlice(rows: readonly T[], cap: number): readonly T[]; /** * Chooses how many siblings each list shows so the tree fits `budget` rows. * * Deepest-first, widest-first among equals: the list that gives up a row is the * one whose rows are the most redundant with the rows around them. A list at * {@link MIN_VISIBLE_LIST_ROWS} is out of the running, which is what stops the * search from erasing a parent to save a child. */ export declare function planRunViewport(root: CensusList, budget: number): RunViewportPlan; /** * The height the live region holds this frame. * * Grows to whatever the content needs, never shrinks on its own, and is capped * by what the terminal can show. `held` coming back larger than `budget` is the * resize case — the window got shorter, and the region has to give the rows * back rather than scroll the prompt off the top. */ export declare function stickyRegionHeight(args: { readonly naturalRows: number; readonly heldRows: number; readonly budgetRows: number; }): number; /** * Rows of content scrolled off the top of a tail-pinned region. * * The region shows the end of the content, because the end is the live work. * Everything earlier is above the fold, and the gutter is what says so. */ export declare function tailScrollOffset(naturalRows: number, visibleRows: number): number; /** Track and thumb glyphs of the scroll gutter. Both are one cell wide in every font. */ export declare const SCROLL_TRACK_GLYPH = "\u2502"; export declare const SCROLL_THUMB_GLYPH = "\u2503"; /** * A one-column scrollbar drawn beside a clipped region: one glyph per visible * row, a thumb whose length and position report how much is hidden and where * the view sits. * * The gutter rather than a summary line because it costs no rows — the thing in * shortest supply when content is being hidden in the first place — and because * it sits next to the rows it describes instead of at an edge the eye has to go * looking for. */ export declare function scrollGutter(args: { readonly totalRows: number; readonly visibleRows: number; readonly offsetRows: number; }): string[]; /** * What a truncated sibling list is not showing, as one line. * * Reports by outcome rather than by position: "42 done" is the fact an operator * wants, and "rows 1–42" is the fact a scrollbar already carries. */ export declare function hiddenSiblingsLine(hiddenStatuses: readonly string[], glyph?: string): string;