/** * The three meter primitives the status row is built from. * * One component per kind of question, because a health question and a capacity * question do not look alike: * * - {@link healthMeter} — "is this healthy?" Discrete levels, no bar. This is the * SSH chip's four pips, generalised. * - {@link capacityMeter} — "how much is left / how much is used?" A bar and a * percent. The *direction* is a parameter, which is what lets quota (remaining) * and context (used) share one implementation. * - {@link performanceValue} — "how fast is it going?" A bare number, neutral, * with a mark for the estimate and a dimmer form for a finished step. * * Nothing here knows what a row is, how wide it is, or in what order chips go: * that is `footer-budget.ts`, and keeping the two apart is what stops a meter * from growing a layout opinion. * * @module dsh-ssh-tui/footer-meters */ import { type ThresholdLevel } from './theme.js'; import { type ColorDepth } from './color-depth.js'; import { type ThroughputView } from './throughput.js'; /** * The row's visual vocabulary, as reusable primitives. * * Three components, because the row answers three different kinds of question: * * - {@link healthMeter} — "is this healthy?" Discrete levels, no bar. This is * the SSH chip's four pips, generalised; nothing else on the row copies it, * because a health question and a capacity question do not look alike. * - {@link capacityMeter} — "how much is left / how much is used?" A bar with a * percent. The *direction* is a parameter, which is what lets quota (remaining) * and context (used) share one implementation. * - {@link performanceValue} — "how fast is it going?" A bare number, neutral, * with a mark for the estimate and a dimmer form for a finished step. * * All three take `ascii` and answer with the project's own fallback glyphs, and * none of them reach for a threshold colour on their own: the caller decides * whether the number it is drawing has bands at all. */ /** How many level marks a health meter has: the four pips the SSH chip always had. */ export declare const HEALTH_METER_LEVELS = 4; /** Full and empty level marks. The empty one is the same cell, hollow. */ export declare const HEALTH_MARK_FILLED = "\u25CF"; export declare const HEALTH_MARK_EMPTY = "\u25CB"; /** ASCII fallback for the same four levels, from the project's own glyph table. */ export declare const HEALTH_ASCII_FILLED = "*"; export declare const HEALTH_ASCII_EMPTY = "o"; /** Filled and empty cells of a capacity meter, matching the existing quota bar. */ export declare const CAPACITY_CELL_FILLED = "\u2588"; export declare const CAPACITY_CELL_EMPTY = "\u2591"; /** ASCII fallback for the capacity meter, from the project's own glyph table. */ export declare const CAPACITY_ASCII_FILLED = "#"; export declare const CAPACITY_ASCII_EMPTY = "."; /** Segment counts by row budget. Three is the smallest bar that still reads. */ export declare const CAPACITY_SEGMENTS_WIDE = 8; export declare const CAPACITY_SEGMENTS_MEDIUM = 5; export declare const CAPACITY_SEGMENTS_NARROW = 3; /** Which way a capacity meter reads. */ export type CapacityBasis = 'used' | 'remaining'; /** Options for one capacity meter; everything but the percent is optional. */ export interface CapacityMeterOptions { /** Segment count when the row has room. */ segments?: number; /** `used` reads left to right as usage, `remaining` as what is left. */ basis?: CapacityBasis; /** Leading label, e.g. `5Hr` / `CTX`. */ label?: string; /** Trailing text after the percent, e.g. `720K/1M`. */ detail?: string; /** Drop the bar entirely, keeping `CTX 72%`. */ percentOnly?: boolean; /** Quota bands (50 / 25 remaining) instead of context bands (80 / 95 used). */ quota?: boolean; /** Glyphs the terminal can actually draw; defaults to the terminal's own state. */ ascii?: boolean; /** Paint the filled run and the percent with the band's accent. */ color?: boolean; /** Colour depth the accent is downgraded for. */ depth?: ColorDepth; } /** * Colours for a capacity meter's filled portion. * * Context and quota answer different questions and have different runways, so * they get different bands — but each one reuses a threshold the session already * enforces, so the meter can never disagree with the alert beside it: * context reuses the pressure levels `contextPressureView` uses (warn at 80%, * danger at 95%, the same numbers that drive the compaction prompt), and quota * reuses the first two of `QUOTA_ALERT_THRESHOLDS` (50 / 25 remaining), which is * where the session starts telling the user about it. */ export declare const CONTEXT_METER_WARN_PERCENT: number; export declare const CONTEXT_METER_DANGER_PERCENT: number; /** First two entries of `quota.ts`'s `QUOTA_ALERT_THRESHOLDS`, in remaining percent. */ export declare const QUOTA_METER_WARN_PERCENT = 50; export declare const QUOTA_METER_DANGER_PERCENT = 25; /** Which band a capacity reading falls in, given what the number means. */ export declare function capacityLevel(percent: number, basis?: CapacityBasis, quota?: boolean): ThresholdLevel; /** * One capacity meter: `CTX ███████░░░ 61%`. * * An unusable percent paints a hollow bar with a question mark rather than `0%`: * a reading nobody took is not a reading of zero, and `?%` is the same claim the * quota chip has always made before its first reading lands. * * Colour, when asked for, lands only on the cells that carry the meaning — the * filled run and the percent. Tinting the label and the empty cells would make * the whole chip one colour and lose the hierarchy it exists to create. */ export declare function capacityMeter(percent: number | undefined, options?: CapacityMeterOptions): { text: string; level: ThresholdLevel; }; /** * One health meter: `●●●●`. * * Discrete levels, never a bar: a link is one of five states, and four pips say * which at a glance. This is the component the SSH chip has always been — it * simply became reusable when the quota and context meters arrived, so that the * two things that *are* capacities stopped being drawn as pips. */ export declare function healthMeter(level: number, options?: { levels?: number; ascii?: boolean; }): string; /** Options for {@link performanceValue}. */ export interface PerformanceValueOptions { /** `live` is the streaming estimate, `settled` the last completed step. */ state: 'live' | 'settled' | 'unavailable'; /** `tok/s` in full, `t/s` when the row is out of room. */ short?: boolean; /** Paint the live value at the row's primary intensity. */ color?: boolean; } /** Which form of the throughput number can be painted at all. */ export declare function performanceValueState(throughput: ThroughputView | undefined, running: boolean): PerformanceValueOptions['state']; /** * The performance value: `160 tok/s`, `~160 tok/s`, `— tok/s`. * * Tokens per second is neutral telemetry, so this component never reaches for a * threshold colour: the harness publishes no performance classification, and the * same number means different things on a reasoning model, a small fast one, and * a long-context request. `~` marks the live estimate — characters per second * scaled by this session's own tokens-per-character — and the settled value has * no mark because it is exact. A live value is lifted out of the row's muted * style because it is the one number on the row that is *moving*; every theme * understands "default intensity", and the mono theme keeps the distinction. */ export declare function performanceValue(throughput: ThroughputView | undefined, running: boolean, options?: Partial): string;