/** * KHAELOR * File: src/tui/components/verify-strip.ts * Description: The verify strip — live check progress that contracts to `✓ verified 4.2s` on success (TUI v2 §5.4). * * Author: Simon-Pierre Boucher * Contact: contact@spboucher.ai */ import { truncateAnsi } from "../renderer/ansi.js"; import type { Theme } from "../theme.js"; export interface VerifyCheckState { check: string; state: "running" | "ok" | "failed"; durationMs?: number; /** First error lines, shown inline on failure. */ errorHead?: string[]; } /** * Live strip while checks run: `⟳ verify typecheck ✓ 1.2s · tests ⠧ · lint ✓` * All-passed contraction: `✓ verified 4.2s` * Failure keeps the failing check pinned with its first error lines. */ export function renderVerifyStrip( checks: readonly VerifyCheckState[], width: number, theme: Theme, ): string[] { if (checks.length === 0) return []; const allDone = checks.every((check) => check.state !== "running"); const allOk = allDone && checks.every((check) => check.state === "ok"); if (allOk) { const totalMs = Math.max(...checks.map((check) => check.durationMs ?? 0)); return [ truncateAnsi( ` ${theme.paint("success", "✓")} ${theme.paint("success", `verified ${(totalMs / 1000).toFixed(1)}s`)}` + ` ${theme.paint("dim", checks.map((check) => check.check).join(" · "))}`, width, ), ]; } const parts = checks.map((check) => { if (check.state === "running") return `${check.check} ${theme.paint("teal", "⠧")}`; if (check.state === "ok") { const secs = check.durationMs !== undefined ? ` ${(check.durationMs / 1000).toFixed(1)}s` : ""; return `${check.check} ${theme.paint("success", "✓")}${theme.paint("dim", secs)}`; } return theme.paint("error", `${check.check} ✗`); }); const lines = [ truncateAnsi( ` ${theme.paint("accent", "⟳")} ${theme.paint("bold", "verify")} ${parts.join(theme.paint("dim", " · "))}`, width, ), ]; for (const check of checks) { if (check.state === "failed" && check.errorHead !== undefined) { for (const errorLine of check.errorHead.slice(0, 3)) { lines.push(truncateAnsi(` ${theme.paint("dim", errorLine)}`, width)); } } } return lines; }