/** * The log console. * * Lines arrive faster than the metrics poll, so the console never rebuilds: * rows that have fallen out of the window are removed from the front, new ones * are appended, and the rest are patched in place. Only a change that genuinely * reorders the window — a filter moving under us, or a fold opening — falls * back to a rebuild. * * It is deliberately NOT a live region. A tail that announced itself would read * every arriving line aloud forever; what matters about the stream is announced * through the page's status line instead, once per state change. */ import type { ConsoleActionVm, ConsoleBannerVm, ConsoleNoticeVm, ConsoleVm, LogBadgeVm, LogRowVm, } from "../../core/select.js"; import { consoleFocusRestore, countNewLines, taskKey } from "../../core/select.js"; import type { View } from "../dom.js"; import { clear, el, setAttr, setStyle, setText, setVar, syncRows } from "../dom.js"; export interface ConsoleHandlers { /** * Toggles one args fold, keyed by the run's first `seq`. `forced` says the * fold is held open by the active query, so the press changes only what * happens once that query clears. */ onFold: (seq: number, forced: boolean, count: number) => void; /** Opens a trace on one `(port, task)`, anchored at the row that opened it. */ onTrace: (port: number, task: number, anchorSeq: number) => void; /** Closes the open trace: `[ back ]`, `Escape`, or the same cell again. */ onExitTrace: () => void; /** The button inside a notice or banner: clear filters, show all models. */ onAction: (kind: ConsoleActionVm["kind"]) => void; } /** How close to the bottom still counts as following the tail, in pixels. */ const FOLLOW_THRESHOLD_PX = 24; interface BadgeCell { root: HTMLElement; } interface LogRow { root: HTMLElement; key: string; seq: number; /** The run key this row toggles; only meaningful on a fold row. */ foldSeq: number; foldForced: boolean; foldCount: number; fold: boolean; ts: HTMLElement; level: HTMLElement; model: HTMLElement; /** * Always present so the row's shape never changes and the incremental patcher * is untouched; hidden when the row has no task. A `