/** * footer.ts — pipeline-overview status widget in the TUI footer area. * * Renders the full ordered pipeline (phases for a single check, or checks for * `/pygienium-all`) as a **multi-line `belowEditor` widget** (via * `ExtensionUIContext.setWidget`): one bulleted, color-themed line per step, * with the live step marked `●` and completed/failed/skipped/pending steps * carrying their terminal glyph. This is the pygienium analogue of piolium's * `phase-status-strip` widget — the footer-side *overview* view. * * The chat-side *detail* view is the live tool-event stream (see * `pygienium-stream` in `index.ts`): each `tool_execution_start/end` and * assistant turn is posted as its own chat message. The two never overlap: * the footer owns the `belowEditor` slot, the stream owns the chat history. * * Presentation-only and mode-aware: in print/JSON mode (no TUI) the footer is * a no-op — stdout progress stays owned by the phase strip — so it can be * driven unconditionally from the runners. * * Generic on a list of {@link FooterItem}s so both a single-check run * (items = phases) and a `/pygienium-all` run (items = checks) reuse one * renderer: the runner decides the granularity, the footer only draws it. * * @module pygienium/footer */ import type { ExtensionUIContext } from "@earendil-works/pi-coding-agent"; /** Widget key pygienium writes its pipeline-overview widget under. */ export const FOOTER_STATUS_KEY = "pygienium"; /** Status of a single pipeline item, carried into the footer line. */ export type ItemStatus = | "pending" | "running" | "complete" | "failed" | "skipped"; /** * Marker per item status, mirroring piolium's phase-status-strip glyphs: * `·`=pending (to come), `●`=running (cursor), `✓`/`✗`/`↷`=terminal. * Kept short so a multi-phase pipeline fits one widget column. */ export const FOOTER_MARKER: Record = { pending: "·", running: "●", complete: "✓", failed: "✗", skipped: "↷", }; /** Theme subset the footer renders against (`ui.theme` satisfies this). */ export interface FooterTheme { fg(color: string, text: string): string; } /** One labelled step in the pipeline overview. */ export interface FooterItem { /** Short label (a phase label like "Scanning" or a check label). */ label: string; /** Current status of this step. */ status: ItemStatus; } export interface PipelineFooterOptions { /** UI context; writes go to `ui.setWidget` (belowEditor). */ ui?: ExtensionUIContext; /** Dialog-capable UI available (TUI / RPC). When false, footer is a no-op. */ hasUI?: boolean; /** Widget key (defaults to {@link FOOTER_STATUS_KEY}). */ statusKey?: string; /** * Whether to render the footer (default true). Set false when an outer * run (e.g. `/pygienium-all`) already owns the footer, so two overviews * never compete over the same widget slot — mirrors the phase strip's * `widget` flag. */ enabled?: boolean; } export interface PipelineFooter { /** Declare the full pipeline at once; `cursor` (if given) marks `running`. */ setPipeline(title: string, items: FooterItem[], cursor?: number): void; /** Set a single item's status; optionally move the cursor too. */ setItem(index: number, status: ItemStatus, cursor?: number): void; /** Advance the cursor to an item (marks it `running`). */ setCursor(index: number): void; /** Snapshot of current items (for tests — no UI required). */ getItems(): FooterItem[]; /** Snapshot of the last rendered title (for tests). */ getTitle(): string; /** Clear the footer widget. Safe to call repeatedly. */ done(): void; } /** Map an item status to a piolium-style theme color token. */ export function footerColor(status: ItemStatus, isCurrent: boolean): string { if (status === "complete") return "success"; if (status === "failed") return "error"; if (status === "skipped") return "warning"; if (status === "running" || isCurrent) return "accent"; return "dim"; } /** Width for the per-step index prefix (`1.` … `12.`). */ function indexWidth(total: number): number { return total >= 10 ? 2 : 1; } /** * Render the pipeline as a list of bulleted, color-themed lines — the pure * core of the footer widget, exported so tests assert on layout without a * TUI. Each line is `• .