/** * conversation-viewer.ts — Live conversation overlay for viewing agent sessions. * * Displays a scrollable, live-updating view of an agent's conversation. * Subscribes to session events for real-time streaming updates. */ import type { AgentSession } from "@earendil-works/pi-coding-agent"; import type { AgentRecord } from "../types.js"; import type { AgentActivity } from "./agent-ui-types.js"; import { type Theme } from "./theme.js"; import { type Component, type TUI } from "./tui-shim.js"; /** Height ceiling shared by the overlay's `maxHeight` and the viewer's internal viewport cap. */ export declare const VIEWPORT_HEIGHT_PCT = 70; export declare class ConversationViewer implements Component { private tui; private session; private record; private activity; private theme; private done; private scrollOffset; private autoScroll; private unsubscribe; private lastInnerW; private closed; /** Timestamp of the last actual render for rate limiting. */ private lastRenderTime; /** True while a microtask render is pending. */ private renderPending; /** Coalesce timer handle for scheduling a render after rate-limit window. */ private coalesceTimer; /** Minimum gap between consecutive renders (60 fps cap). */ private static readonly MIN_RENDER_GAP_MS; /** Cached content lines from the last buildContentLines() call. */ private cachedContentLines; /** Number of messages processed into cachedContentLines. */ private cachedMessageCount; /** Width used when building cachedContentLines (invalidates on resize). */ private cachedWidth; constructor(tui: TUI, session: AgentSession, record: AgentRecord, activity: AgentActivity | undefined, theme: Theme, done: (result: undefined) => void); /** * Debounced render request with rate limiting. * Coalesces rapid session events into a single render via queueMicrotask. * Falls back to setTimeout when rate-limited to ensure the final state * is always rendered, even if session events stop during a rate-limit window. * Never renders more than ~60fps. */ private requestRender; handleInput(data: string): void; render(width: number): string[]; invalidate(): void; dispose(): void; /** * Incremental content cache: only processes NEW messages since last render. * Falls back to full rebuild when width changes or cache is empty. */ private buildContentLinesCached; /** * Update only the running spinner line at the end (cheap: no message re-processing). */ private updateRunningLine; /** * Render a single message into the output lines array. * Extracted from buildContentLines for incremental reuse. */ private renderMessage; private viewportHeight; private chromeLines; private invocationLine; private buildContentLines; }