import { Container, type TUI } from '@earendil-works/pi-tui'; import { type AgentSessionEvent } from '@earendil-works/pi-coding-agent'; import type { BrokerSnapshot } from '../../../core/runtime/broker-protocol.js'; import { type AttachPalette } from '../config.js'; import type { CondensedHistoryMode } from '../../../types.js'; export interface ChatViewOptions { /** Working dir used by `ToolExecutionComponent` for path/diff display. Defaults * to the viewer's cwd; T7 may pass the node's working dir for nicer paths. */ cwd?: string; /** Render inline images in tool output (terminal-dependent). Default true. */ showImages?: boolean; /** Inline image width in cells. Default 60 (pi's default). */ imageWidthCells?: number; /** Collapse the assistant thinking block. Default false. */ hideThinking?: boolean; /** Label shown for a hidden thinking block. Default "Thinking...". */ hiddenThinkingLabel?: string; /** Start tool output expanded. Default false. */ toolOutputExpanded?: boolean; /** Sink for footer/header-only events ChatView does not itself render * (queue_update, session_info_changed, thinking_level_changed). The attach * command (T7) wires a footer here; absent → these events are no-ops. */ onFooterEvent?: (event: AgentSessionEvent) => void; /** Theme palette for the activity spinner / dim status / error lines. Absent → * the module defaults (faint + ANSI red) are used. */ palette?: AttachPalette; /** Trailing refresh-yield cycles rendered at full fidelity; older cycles are * condensed to prose and frozen (see frozen-history.ts). Absent → the user's * `live_cycles` config setting, itself defaulting to 2. */ liveCycles?: number; /** Which messages of a condensed cycle survive into the frozen block. Absent → * the user's `condensed_history` config setting, itself defaulting to `none` * (cycle dividers only). */ condensedHistory?: CondensedHistoryMode; /** Pre-colored banner lines pinned as the FIRST child of the chat container, * above history (the crouton wordmark). Sits at the very top of the transcript * and scrolls up into scrollback as the chat grows. Absent → no banner. */ banner?: ReadonlyArray; } export declare class ChatView { private readonly tui; /** The chat scroll area handed in by the attach command. */ private readonly container; /** Settled history is cached independently from the live status area so picker * navigation does not re-render a long conversation on every key repeat. */ private readonly historyContainer; /** Activity/status area below history. Holds working/compaction/retry loaders. */ private readonly statusContainer; /** Where `append()` puts a component. Normally `historyContainer`; retargeted to * a scratch container while a frozen block is being built, so the very same * message-rendering path serves both. */ private appendTarget; /** Trailing cycles kept at full fidelity (config `live_cycles`). */ private readonly liveCycles; /** Messages kept from a condensed cycle (config `condensed_history`). */ private readonly condensedHistory; private readonly cwd; private readonly showImages; private readonly imageWidthCells; private hideThinking; private readonly hiddenThinkingLabel; private toolOutputExpanded; private readonly onFooterEvent; /** Theme-derived colorizers for the activity/status area. */ private readonly spinnerStyle; private readonly dimStyle; private readonly errorStyle; /** Bold accent for the expanded crtr-context label. */ private readonly labelStyle; /** The assistant message component currently being streamed (between * message_start and message_end for an assistant turn). */ private streamingComponent; /** Tool-call id → its execution component, for the duration of the call. */ private readonly pendingTools; /** The `!` bash component currently streaming (between bash_start and bash_end). */ private bashComponent; /** The single active activity loader (working/compaction/retry), if any. */ private activityLoader; /** True while applySnapshot is mid-build; live broker frames defer instead of * mutating the partially-rebuilt view. */ private applyingSnapshot; /** Bumped on every applySnapshot. A build suspended at a yield compares this * after resuming and abandons itself when a newer welcome has bumped it, so * the newest welcome wins and stale state never overwrites the live view. */ private snapshotGeneration; /** Live frames captured during an in-flight build (bound closures that re-run * the original entry point), replayed in order when the build completes and * dropped when a newer welcome supersedes the build. */ private readonly deferredFrames; /** Text of the most recent assistant message, kept current from the welcome * snapshot AND the live event stream so `/copy` (getLastAssistantText) works * whether the message arrived before or after attach. */ private lastAssistantText; /** Recent transcript text, oldest → newest, for the file-review path picker. */ private readonly transcriptTexts; /** Existing file paths found anywhere in the rendered transcript, newest first. */ private readonly transcriptFilePaths; constructor(tui: TUI, container: Container, opts?: ChatViewOptions); /** Render the full message history from a `welcome` snapshot. Ports pi's * `renderSessionContext` (interactive-mode.js:2553): assistant messages spawn * tool-execution components matched to their toolResult messages; everything * else routes through `addMessageToChat`. The TUI's differential renderer + * reflow-on-resize is automatic — no scrollback reconstruction needed. */ applySnapshot(snapshot: BrokerSnapshot): Promise; /** * Render the condensed form of the already-finished cycles ONCE and hand back a * frozen line block (or undefined when nothing survived condensation). * * Reuses `addMessageToChat` verbatim by retargeting `append()` at a scratch * container, so condensed history is styled exactly like live history — the same * renderer, with the tool traffic filtered out and the result flattened to text. * Yields on the same cadence as the live build so a very long history still lets * queued keystrokes through, and abandons itself when a newer welcome supersedes * this generation. */ private buildFrozenBlock; /** Defer a live broker frame when a snapshot build is in flight. Returns true * (and enqueues the replay closure) while building so the caller returns * without mutating the half-rebuilt view; false when the caller may proceed. * This is the single-writer gate every live-frame entry point passes through. */ private deferIfBuilding; /** Replay deferred live frames in wire order. The gate is already open, so each * closure re-enters its entry point and dispatches immediately. */ private replayDeferredFrames; /** Clear live turn state after the broker reports completion or the viewer observes dormancy. */ settleRun(): void; /** Stop any spinning activity loader. A one-shot `crtr surface attach` process lets its * timers die with the process, but if T7 reuses a ChatView across reconnects in * one process it should call this on detach to avoid a leaked interval. */ dispose(): void; /** Ctrl+O / app.tools.expand — flip global tool-output expansion and apply it to * every rendered component that supports it. Returns the new expanded state so * the caller can surface a notice. Condensed cycles hold no tool components and * are frozen as text, so they are unaffected by design. */ toggleToolsExpanded(): boolean; /** Ctrl+T / app.thinking.toggle — flip thinking-block visibility and apply it to * every assistant message (the in-flight streaming bubble is itself a child of * the container, so the loop covers it too). Returns the new HIDDEN state. */ toggleThinking(): boolean; handleEvent(event: AgentSessionEvent): void; bashStart(command: string, excludeFromContext?: boolean): void; bashOutput(chunk: string): void; bashEnd(result: { exitCode: number | undefined; cancelled: boolean; truncated: boolean; fullOutputPath?: string; }): void; private addMessageToChat; /** Tool component with native viewer renderers for crouter-owned tool output. */ private makeToolComponent; /** Append conversation content above the independent live status area — or into * the scratch container while a frozen block is under construction. */ private append; /** Approximate render width for inline mermaid diagrams. Only affects * termrender's trailing padding (stripped in the transform), so the terminal * column count is close enough without threading the live pane width here. */ private renderWidth; /** Number of rendered conversation components in the container currently being * appended to (history, or the frozen block's scratch container). */ private chatChildCount; /** Clear all chat content. */ private resetChat; private makeLoader; /** Let a self-updating history child invalidate only its own cached lines. */ private componentTui; /** Complete and release the live bash component before ChatView gives up ownership. */ private settleBashComponent; /** Swap the single active activity indicator (working/compaction/retry). */ private setActivity; private showStatus; private showError; private crtrOutputTheme; /** Extract the tool-call content items from an assistant message. */ private assistantToolCalls; /** Existing file paths from the rendered transcript, newest first. */ getTranscriptFilePathsNewestFirst(): string[]; /** The text of the most recent assistant message, for `/copy`. Undefined when * no assistant message has been seen (or it had no text content). */ getLastAssistantText(): string | undefined; private pushTranscriptText; private pushTranscriptValue; /** Concatenate the text blocks of a user message (string or content array). */ private userMessageText; }