import { type Component, type Container } from "@earendil-works/pi-tui"; /** * Roles that participate in pi's chat spacing contract. * * Assistant turns own their leading whitespace internally, and tool rows attach * directly under the assistant/tool-call row they belong to. User-like rows get * one blank line when they are not the first row in the transcript. */ export type ChatTranscriptRole = "assistant" | "thinking" | "tool" | "user" | "custom" | "notice" | "system" | "summary"; export interface ChatTranscriptEntryLike { readonly role: ChatTranscriptRole; } export type ChatTranscriptRenderer = (entry: TEntry) => Component; export type ChatTranscriptCacheKey = (entry: TEntry, index: number) => string; /** * A run of rows inside a windowed component, tagged with the identity of the * thing that produced them. * * `id` is compared with `===` only, and is never rendered. Entry *objects* are * the natural id for a transcript: cache keys carry the entry's index and so * change for every survivor of a splice, while the entry objects themselves are * moved, not rebuilt. */ export interface RowWindowSegment { readonly id: unknown; readonly rows: number; } export declare function addChatTranscriptEntry(container: Container, component: Component, role: ChatTranscriptRole): void; /** * Reusable pi chat transcript scaffold for extension surfaces. * * This intentionally mirrors InteractiveMode.addMessageToChat spacing without * coupling consumers to a full AgentSession. Extension UIs can bring their own * message model while still rendering inside the same Container/Spacer rhythm * as the main chat. */ export declare class ChatTranscriptComponent implements Component { private readonly entries; private readonly renderEntry; readonly supportsRowWindow: boolean; private readonly cacheKey; private blockCache; /** Per-entry heights recorded by the most recent `renderAllRows`. */ private staticSegments; /** Width those heights were measured at; they mean nothing at another width. */ private staticSegmentsWidth; constructor(entries: readonly TEntry[], renderEntry: ChatTranscriptRenderer, cacheKey?: ChatTranscriptCacheKey); render(width: number): string[]; rowCount(width: number): number; /** * One segment per cached entry block, identified by the entry object itself. */ rowSegments(width: number): readonly RowWindowSegment[]; renderRows(width: number, startRow: number, endRow: number): string[]; invalidate(): void; private ensureBlockCache; /** * Render every entry, recording each one's height as it goes. * * The heights are what `rowSegments` reports on this path. They are a * by-product of work this method already does, so identifying rows costs * nothing extra and, unlike a cache key, cannot miss an entry mutated in * place -- which is the behaviour a transcript without a cache key exists to * provide. */ private renderAllRows; private renderEntryBlock; } /** * Sticky-bottom, scrollable viewport for chat-like component stacks. * * Pi's main interactive chat gets terminal scrollback for free. Extension * overlays render into a fixed rectangle, so they need an explicit viewport * with the same sticky-bottom default plus keyboard and mouse history controls. */ export declare class ScrollableComponentViewport implements Component { private components; private visibleRows; private scrollFromBottom; private lastLineCount; private lastComponentSegments; private lastComponentRowCounts; private lastWidth; private maxScroll; /** * Absolute row `scrollTo` asked for, still waiting for the render that can * honour it. See `scrollTo` for why the request has to outlive the call. */ private pendingScrollRow; private readonly keybindings; constructor(keybindings?: unknown); setComponents(components: readonly Component[]): void; setVisibleRows(rows: number): void; getScrollFromBottom(): number; getMaxScroll(): number; scrollToBottom(): void; scrollToTop(): void; scrollBy(deltaRows: number): void; /** * Park the viewport with `row` as its first visible row. * * The absolute counterpart of `scrollBy`, for a caller that already knows * which row it wants on screen. * * The request is *also* recorded for the next render, and that is the half * that matters. `scrollFromBottom` is a distance from the bottom, so the * row it names moves whenever the stack grows; and the render that follows * re-derives the offset from the previous frame's layout to hold a * scrolled-up reader's content still, which would drag this request back * with it. A caller who has just measured rows the last render never saw * would otherwise be answered with the old layout's clamp and the old * frame's anchor. The next render resolves the row against the layout it * actually paints; any other scroll before then withdraws the request. */ scrollTo(row: number): void; /** * Total rows the component stack occupies at `width`, on and off screen. * * This measures rather than paints: it is what lets a search read the whole * transcript instead of the window the reader happens to be parked on. */ rowCount(width: number): number; /** * Rows `startRow` (inclusive) through `endRow` (exclusive) of the whole * stack, independent of the current scroll offset. Deliberately not paired * with `supportsRowWindow`: a viewport is a scroll container rather than a * windowed component, and an enclosing viewport must keep measuring it by * its visible height. */ renderRows(width: number, startRow: number, endRow: number): string[]; handleInput(data: string): boolean; render(width: number): string[]; invalidate(): void; private measureComponentRows; private renderVisibleRows; private pageSize; private clampScroll; } export declare class ScrollableChatTranscriptComponent implements Component { private readonly viewport; private readonly transcript; constructor(entries: readonly TEntry[], renderEntry: ChatTranscriptRenderer); setVisibleRows(rows: number): void; handleInput(data: string): boolean; render(width: number): string[]; invalidate(): void; getScrollFromBottom(): number; getMaxScroll(): number; scrollToBottom(): void; } export declare function mouseWheelDeltaRows(data: string): number; //# sourceMappingURL=chat-transcript.d.ts.map