/** * Mission Control TUI Idle View * * Displays when no mission is active: * - Agents/Skills readiness * - Past runs list (if any exist) * * Keyboard: * - Enter: Trigger init new mission * - Esc: Close view * - Up/Down: Navigate past runs */ import type { Theme } from "@mariozechner/pi-coding-agent"; import type { Run } from "../state.js"; import { PastRunsComponent, extractPastRuns } from "./past-runs.js"; import { truncateToWidth } from "@mariozechner/pi-tui"; export interface IdleViewProps { pastRuns: Run[]; agentsReady: boolean; skillsReady: boolean; } export interface IdleViewCallbacks { onInitMission: () => void; onClose: () => void; } /** * Render the idle view */ export function renderIdleView( width: number, height: number, props: IdleViewProps, callbacks: IdleViewCallbacks, theme: Theme ): string[] { void props; void callbacks; return new Array(height).fill("").map((_line, index) => (index === 0 ? truncateToWidth(theme.fg("dim", "No active mission"), width) : "")); } /** * Idle view component class * * Composes PastRunsComponent and adds the idle-specific UI */ export class IdleViewComponent { private pastRuns: PastRunsComponent; private props: { agentsReady: boolean; skillsReady: boolean }; private callbacks: IdleViewCallbacks; private showRuns: boolean = false; constructor(callbacks: IdleViewCallbacks) { this.callbacks = callbacks; this.pastRuns = new PastRunsComponent(); this.props = { agentsReady: true, skillsReady: true }; } update(pastRuns: Run[], agentsReady = true, skillsReady = true): void { this.props.agentsReady = agentsReady; this.props.skillsReady = skillsReady; const items = extractPastRuns(pastRuns); this.pastRuns.updateRuns(items); this.showRuns = items.length > 0; } handleInput(data: string, isEnter: boolean, isEscape: boolean, isUp: boolean, isDown: boolean): void { if (isEnter) { this.callbacks.onInitMission(); } else if (isEscape) { this.callbacks.onClose(); } else if (this.showRuns) { if (isUp) { this.pastRuns.navigateUp(); } else if (isDown) { this.pastRuns.navigateDown(); } } } render(width: number, height: number, theme: Theme): string[] { const lines: string[] = []; const { agentsReady, skillsReady } = this.props; const readiness = agentsReady && skillsReady ? theme.fg("dim", "Ready to initialize a mission") : theme.fg("muted", "Waiting for Mission Control resources"); lines.push(truncateToWidth(readiness, width)); lines.push(""); if (this.showRuns) { const runsHeight = Math.min(this.pastRuns['props'].runs.length + 4, Math.max(4, height - 2)); const runsLines = this.pastRuns.render(width, runsHeight, theme); lines.push(...runsLines); } else { lines.push(truncateToWidth(theme.fg("muted", "No past runs yet."), width)); } // Pad to height while (lines.length < height) { lines.push(""); } return lines.slice(0, height); } invalidate(): void { this.pastRuns.invalidate(); } }