mono/packages/kbot/ref/utils/exportRenderer.tsx
2026-04-01 01:05:48 +02:00

98 lines
16 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useRef } from 'react';
import stripAnsi from 'strip-ansi';
import { Messages } from '../components/Messages.js';
import { KeybindingProvider } from '../keybindings/KeybindingContext.js';
import { loadKeybindingsSyncWithWarnings } from '../keybindings/loadUserBindings.js';
import type { KeybindingContextName } from '../keybindings/types.js';
import { AppStateProvider } from '../state/AppState.js';
import type { Tools } from '../Tool.js';
import type { Message } from '../types/message.js';
import { renderToAnsiString } from './staticRender.js';
/**
* Minimal keybinding provider for static/headless renders.
* Provides keybinding context without the ChordInterceptor (which uses useInput
* and would hang in headless renders with no stdin).
*/
function StaticKeybindingProvider({
children
}: {
children: React.ReactNode;
}): React.ReactNode {
const {
bindings
} = loadKeybindingsSyncWithWarnings();
const pendingChordRef = useRef(null);
const handlerRegistryRef = useRef(new Map());
const activeContexts = useRef(new Set<KeybindingContextName>()).current;
return <KeybindingProvider bindings={bindings} pendingChordRef={pendingChordRef} pendingChord={null} setPendingChord={() => {}} activeContexts={activeContexts} registerActiveContext={() => {}} unregisterActiveContext={() => {}} handlerRegistryRef={handlerRegistryRef}>
{children}
</KeybindingProvider>;
}
// Upper-bound how many NormalizedMessages a Message can produce.
// normalizeMessages splits one Message with N content blocks into N
// NormalizedMessages — 1:1 with block count. String content = 1 block.
// AttachmentMessage etc. have no .message and normalize to ≤1.
function normalizedUpperBound(m: Message): number {
if (!('message' in m)) return 1;
const c = m.message.content;
return Array.isArray(c) ? c.length : 1;
}
/**
* Streams rendered messages in chunks, ANSI codes preserved. Each chunk is a
* fresh renderToAnsiString — yoga layout tree + Ink's screen buffer are sized
* to the tallest CHUNK instead of the full session. Measured (Mar 2026,
* 538-msg session): 55% plateau RSS vs a single full render. The sink owns
* the output — write to stdout for `[` dump-to-scrollback, appendFile for `v`.
*
* Messages.renderRange slices AFTER normalize→group→collapse, so tool-call
* grouping stays correct across chunk seams; buildMessageLookups runs on
* the full normalized array so tool_use↔tool_result resolves regardless of
* which chunk each landed in.
*/
export async function streamRenderedMessages(messages: Message[], tools: Tools, sink: (ansiChunk: string) => void | Promise<void>, {
columns,
verbose = false,
chunkSize = 40,
onProgress
}: {
columns?: number;
verbose?: boolean;
chunkSize?: number;
onProgress?: (rendered: number) => void;
} = {}): Promise<void> {
const renderChunk = (range: readonly [number, number]) => renderToAnsiString(<AppStateProvider>
<StaticKeybindingProvider>
<Messages messages={messages} tools={tools} commands={[]} verbose={verbose} toolJSX={null} toolUseConfirmQueue={[]} inProgressToolUseIDs={new Set()} isMessageSelectorVisible={false} conversationId="export" screen="prompt" streamingToolUses={[]} showAllInTranscript={true} isLoading={false} renderRange={range} />
</StaticKeybindingProvider>
</AppStateProvider>, columns);
// renderRange indexes into the post-collapse array whose length we can't
// see from here — normalize splits each Message into one NormalizedMessage
// per content block (unbounded per message), collapse merges some back.
// Ceiling is the exact normalize output count + chunkSize so the loop
// always reaches the empty slice where break fires (collapse only shrinks).
let ceiling = chunkSize;
for (const m of messages) ceiling += normalizedUpperBound(m);
for (let offset = 0; offset < ceiling; offset += chunkSize) {
const ansi = await renderChunk([offset, offset + chunkSize]);
if (stripAnsi(ansi).trim() === '') break;
await sink(ansi);
onProgress?.(offset + chunkSize);
}
}
/**
* Renders messages to a plain text string suitable for export.
* Uses the same React rendering logic as the interactive UI.
*/
export async function renderMessagesToPlainText(messages: Message[], tools: Tools = [], columns?: number): Promise<string> {
const parts: string[] = [];
await streamRenderedMessages(messages, tools, chunk => void parts.push(stripAnsi(chunk)), {
columns
});
return parts.join('');
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZVJlZiIsInN0cmlwQW5zaSIsIk1lc3NhZ2VzIiwiS2V5YmluZGluZ1Byb3ZpZGVyIiwibG9hZEtleWJpbmRpbmdzU3luY1dpdGhXYXJuaW5ncyIsIktleWJpbmRpbmdDb250ZXh0TmFtZSIsIkFwcFN0YXRlUHJvdmlkZXIiLCJUb29scyIsIk1lc3NhZ2UiLCJyZW5kZXJUb0Fuc2lTdHJpbmciLCJTdGF0aWNLZXliaW5kaW5nUHJvdmlkZXIiLCJjaGlsZHJlbiIsIlJlYWN0Tm9kZSIsImJpbmRpbmdzIiwicGVuZGluZ0Nob3JkUmVmIiwiaGFuZGxlclJlZ2lzdHJ5UmVmIiwiTWFwIiwiYWN0aXZlQ29udGV4dHMiLCJTZXQiLCJjdXJyZW50Iiwibm9ybWFsaXplZFVwcGVyQm91bmQiLCJtIiwiYyIsIm1lc3NhZ2UiLCJjb250ZW50IiwiQXJyYXkiLCJpc0FycmF5IiwibGVuZ3RoIiwic3RyZWFtUmVuZGVyZWRNZXNzYWdlcyIsIm1lc3NhZ2VzIiwidG9vbHMiLCJzaW5rIiwiYW5zaUNodW5rIiwiUHJvbWlzZSIsImNvbHVtbnMiLCJ2ZXJib3NlIiwiY2h1bmtTaXplIiwib25Qcm9ncmVzcyIsInJlbmRlcmVkIiwicmVuZGVyQ2h1bmsiLCJyYW5nZSIsImNlaWxpbmciLCJvZmZzZXQiLCJhbnNpIiwidHJpbSIsInJlbmRlck1lc3NhZ2VzVG9QbGFpblRleHQiLCJwYXJ0cyIsImNodW5rIiwicHVzaCIsImpvaW4iXSwic291cmNlcyI6WyJleHBvcnRSZW5kZXJlci50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IFJlYWN0LCB7IHVzZVJlZiB9IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHN0cmlwQW5zaSBmcm9tICdzdHJpcC1hbnNpJ1xuaW1wb3J0IHsgTWVzc2FnZXMgfSBmcm9tICcuLi9jb21wb25lbnRzL01lc3NhZ2VzLmpzJ1xuaW1wb3J0IHsgS2V5YmluZGluZ1Byb3ZpZGVyIH0gZnJvbSAnLi4va2V5YmluZGluZ3MvS2V5YmluZGluZ0NvbnRleHQuanMnXG5pbXBvcnQgeyBsb2FkS2V5YmluZGluZ3NTeW5jV2l0aFdhcm5pbmdzIH0gZnJvbSAnLi4va2V5YmluZGluZ3MvbG9hZFVzZXJCaW5kaW5ncy5qcydcbmltcG9ydCB0eXBlIHsgS2V5YmluZGluZ0NvbnRleHROYW1lIH0gZnJvbSAnLi4va2V5YmluZGluZ3MvdHlwZXMuanMnXG5pbXBvcnQgeyBBcHBTdGF0ZVByb3ZpZGVyIH0gZnJvbSAnLi4vc3RhdGUvQXBwU3RhdGUuanMnXG5pbXBvcnQgdHlwZSB7IFRvb2xzIH0gZnJvbSAnLi4vVG9vbC5qcydcbmltcG9ydCB0eXBlIHsgTWVzc2FnZSB9IGZyb20gJy4uL3R5cGVzL21lc3NhZ2UuanMnXG5pbXBvcnQgeyByZW5kZXJUb0Fuc2lTdHJpbmcgfSBmcm9tICcuL3N0YXRpY1JlbmRlci5qcydcblxuLyoqXG4gKiBNaW5pbWFsIGtleWJpbmRpbmcgcHJvdmlkZXIgZm9yIHN0YXRpYy9oZWFkbGVzcyByZW5kZXJzLlxuICogUHJvdmlkZXMga2V5YmluZGluZyBjb250ZXh0IHdpdGhvdXQgdGhlIENob3JkSW50ZXJjZXB0b3IgKHdoaWNoIHVzZXMgdXNlSW5wdXRcbiAqIGFuZCB3b3VsZCBoYW5nIGluIGhlYWRsZXNzIHJlbmRlcnMgd2l0aCBubyBzdGRpbikuXG4gKi9cbmZ1bmN0aW9uIFN0YXRpY0tleWJpbmRpbmdQcm92aWRlcih7XG4gIGNoaWxkcmVuLFxufToge1xuICBjaGlsZHJlbjogUmVhY3QuUmVhY3ROb2RlXG59KTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgeyBiaW5kaW5ncyB9ID0gbG9hZEtleWJpbmRpbmdzU3luY1dpdGhXYXJuaW5ncygpXG4gIGNvbnN0IHBlbmRpbmdDaG9yZFJlZiA9IHVzZVJlZihudWxsKVxuICBjb25zdCBoYW5kbGVyUmVnaXN0cnlSZWYgPSB1c2VSZWYobmV3IE1hcCgpKVxuICBjb25zdCBhY3RpdmVDb250ZXh0cyA9IHVzZVJlZihuZXcgU2V0PEtleWJpbmRpbmdDb250ZXh0TmFtZT4oKSkuY3VycmVudFxuXG4gIHJldHVybiAoXG4gICAgPEtleWJpbmRpbmdQcm92aWRlclxuICAgICAgYmluZGluZ3M9e2JpbmRpbmdzfVxuICAgICAgcGVuZGluZ0Nob3JkUmVmPXtwZW5kaW5nQ2hvcmRSZWZ9XG4gICAgICBwZW5kaW5nQ2hvcmQ9e251bGx9XG4gICAgICBzZXRQZW5kaW5nQ2hvcmQ9eygpID0+IHt9fVxuICAgICAgYWN0aXZlQ29udGV4dHM9e2FjdGl2ZUNvbnRleHRzfVxuICAgICAgcmVnaXN0ZXJBY3RpdmVDb250ZXh0PXsoKSA9PiB7fX1cbiAgICAgIHVucmVnaXN0ZXJBY3RpdmVDb250ZXh0PXsoKSA9PiB7fX1cbiAgICAgIGhhbmRsZXJSZWdpc3RyeVJlZj17aGFuZGxlclJlZ2lzdHJ5UmVmfVxuICAgID5cbiAgICAgIHtjaGlsZHJlbn1cbiAgICA8L0tleWJpbmRpbmdQcm92aWRlcj5cbiAgKVxufVxuXG4vLyBVcHBlci1ib3VuZCBob3cgbWFueSBOb3JtYWxpemVkTWVzc2FnZXMgYSBNZXNzYWdlIGNhbiBwcm9kdWNlLlxuLy8gbm9ybWFsaXplTWVzc2FnZXMgc3BsaXRzIG9uZSBNZXNzYWdlIHdpdGggTiBjb250ZW50IGJsb2NrcyBpbnRvIE5cbi8vIE5vcm1hbGl6ZWRNZXNzYWdlcyDigJQgMToxIHdpdGggYmxvY2sgY291bnQuIFN0cmluZyBjb250ZW50ID0gMSBibG9jay5cbi8vIEF0dGFjaG1lbnRNZXNzYWdlIGV0Yy4gaGF2ZSBubyAubWVzc2FnZSBhbmQgbm9ybWFsaXplIHRvIOKJpDEuXG5mdW5jdGlvbiBub3JtYWxpemVkVXBwZXJCb3VuZChtOiBNZXNzYWdlKTogbnVtYmVyIHtcbiAgaWYgKCEoJ21lc3NhZ2UnIGluIG0pKSByZXR1cm4gMVxuICBjb25zdCBjID0gbS5tZXNzYWdlLmNvbnRlbnRcbiAgcmV0dXJuIEFycmF5LmlzQXJyYXkoYykgPyBjLmxlbmd0aCA6IDFcbn1cblxuLyoqXG4gKiBTdHJlYW1zIHJlbmRlcmVkIG1lc3NhZ2VzIGluIGNodW5rcywgQU5TSSBjb2RlcyBwcmVzZXJ2ZWQuIEVhY2ggY2h1bmsgaXMgYVxuICogZnJlc2ggcmVuZGVyVG9BbnNpU3RyaW5nIOKAlCB5b2dhIGxheW91dCB0cmVlICsgSW5rJ3Mgc2NyZWVuIGJ1ZmZlciBhcmUgc2l6ZWRcbiAqIHRvIHRoZSB0YWxsZXN0IENIVU5LIGluc3RlYWQgb2YgdGhlIGZ1bGwgc2Vzc2lvbi4gTWVhc3VyZWQgKE1hciAyMDI2LFxuICogNTM4LW1zZyBzZXNzaW9uKTog4oiSNTUlIHBsYXRlYXUgUlNTIHZzIGEgc2luZ2xlIGZ1bGwgcmVuZGVyLiBUaGUgc2luayBvd25zXG4gKiB0aGUgb3V0cHV0IOKAlCB3cml0ZSB0byBzdGRvdXQgZm9yIGBbYCBkdW1wLXRvLXNjcm9sbGJhY2ssIGFwcGVuZEZpbGUgZm9yIGB2YC5cbiAqXG4gKiBNZXNzYWdlcy5yZW5kZXJSYW5nZSBzbGljZXMgQUZURVIgbm9ybWFsaXpl4oaSZ3JvdXDihpJjb2xsYXBzZSwgc28gdG9vbC1jYWxsXG4gKiBncm91cGluZyBzdGF5cyBjb3JyZWN0IGFjcm9zcyBjaHVuayBzZWFtczsgYnVpbGRNZXNzYWdlTG9va3VwcyBydW5zIG9uXG4gKiB0aGUgZnVsbCBub3JtYWxpemVkIGFycmF5IHNvIHRvb2xfdXNl4oaUdG9vbF9yZXN1bHQgcmVzb2x2ZXMgcmVnYXJkbGVzcyBvZlxuICogd2hpY2ggY2h1bmsgZWFjaCBsYW5kZWQgaW4uXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBzdHJlYW1SZW5kZXJlZE1lc3NhZ2VzKFxuICBtZXNzYWdlczogTWVzc2FnZVtdLFxuICB0b29sczogVG9vbHMsXG4gIHNpbms6IChhbnNpQ2h1bms6IHN0cmluZykgPT4gdm9pZCB8IFByb21pc2U8dm9pZD4sXG4gIHtcbiAgICBjb2x1bW5zLFxuICAgIHZlcmJvc2UgPSBmYWxzZSxcbiAgICBjaHVua1NpemUgPSA0MCxcbiAgICBvblByb2dyZXNzLFxuICB9OiB7XG4gICAgY29sdW1ucz86IG51bWJlclxuICAgIHZlcmJvc2U/OiBib29sZWFuXG4gICAgY2h1bmtTaXplPzogbnVtYmVyXG4gICAgb25Qcm9ncmVzcz86IChyZW5kZXJlZDogbnVtYmVyKSA9PiB2b2lkXG4gIH0gPSB7fSxcbik6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCByZW5kZXJDaHVuayA9IChyYW5nZTogcmVhZG9ubHkgW251bWJlciwgbnVtYmVyXSkgPT5cbiAgICByZW5kZXJUb0Fuc2lTdHJpbmcoXG4gICAgICA8QXBwU3RhdGVQcm92aWRlcj5cbiAgICAgICAgPFN0YXRpY0tleWJpbmRpbmdQcm92aWRlcj5cbiAgICAgICAgICA8TWVzc2FnZXNcbiAgICAgICAgICAgIG1lc3NhZ2VzPXttZXNzYWdlc31cbiAgICAgICAgICAgIHRvb2xzPXt0b29sc31cbiAgICAgICAgICAgIGNvbW1hbmRzPXtbXX1cbiAgICAgICAgICAgIHZlcmJvc2U9e3ZlcmJvc2V9XG4gICAgICAgICAgICB0b29sSlNYPXtudWxsfVxuICAgICAgICAgICAgdG9vbFVzZUNvbmZpcm1RdWV1ZT17W119XG4gICAgICAgICAgICBpblByb2dyZXNzVG9vbFVzZUlEcz17bmV3IFNldCgpfVxuICAgICAgICAgICAgaXNNZXNzYWdlU2VsZWN0b3JWaXNpYmxlPXtmYWxzZX1cbiAgICAgICAgICAgIGNvbnZlcnNhdGlvbklkPVwiZXhwb3J0XCJcbiAgICAgICAgICAgIHNjcmVlbj1cInByb21wdFwiXG4gICAgICAgICAgICBzdHJlYW1pbmdUb29sVXNlcz17W119XG4gICAgICAgICAgICBzaG93QWxsSW5UcmFuc2NyaXB0PXt0cnVlfVxuICAgICAgICAgICAgaXNMb2FkaW5nPXtmYWxzZX1cbiAgICAgICAgICAgIHJlbmRlclJhbmdlPXtyYW5nZX1cbiAgICAgICAgICAvPlxuICAgICAgICA8L1N0YXRpY0tleWJpbmRpbmdQcm92aWRlcj5cbiAgICAgIDwvQXBwU3RhdGVQcm92aWRlcj4sXG4gICAgICBjb2x1bW5zLFxuICAgIClcblxuICAvLyByZW5kZXJSYW5nZSBpbmRleGVzIGludG8gdGhlIHBvc3QtY29sbGFwc2UgYXJyYXkgd2hvc2UgbGVuZ3RoIHdlIGNhbid0XG4gIC8vIHNlZSBmcm9tIGhlcmUg4oCUIG5vcm1hbGl6ZSBzcGxpdHMgZWFjaCBNZXNzYWdlIGludG8gb25lIE5vcm1hbGl6ZWRNZXNzYWdlXG4gIC8vIHBlciBjb250ZW50IGJsb2NrICh1bmJvdW5kZWQgcGVyIG1lc3NhZ2UpLCBjb2xsYXBzZSBtZXJnZXMgc29tZSBiYWNrLlxuICAvLyBDZWlsaW5nIGlzIHRoZSBleGFjdCBub3JtYWxpemUgb3V0cHV0IGNvdW50ICsgY2h1bmtTaXplIHNvIHRoZSBsb29wXG4gIC8vIGFsd2F5cyByZWFjaGVzIHRoZSBlbXB0eSBzbGljZSB3aGVyZSBicmVhayBmaXJlcyAoY29sbGFwc2Ugb25seSBzaHJpbmtzKS5cbiAgbGV0IGNlaWxpbmcgPSBjaHVua1NpemVcbiAgZm9yIChjb25zdCBtIG9mIG1lc3NhZ2VzKSBjZWlsaW5nICs9IG5vcm1hbGl6ZWRVcHBlckJvdW5kKG0pXG4gIGZvciAobGV0IG9mZnNldCA9IDA7IG9mZnNldCA8IGNlaWxpbmc7IG9mZnNldCArPSBjaHVua1NpemUpIHtcbiAgICBjb25zdCBhbnNpID0gYXdhaXQgcmVuZGVyQ2h1bmsoW29mZnNldCwgb2Zmc2V0ICsgY2h1bmtTaXplXSlcbiAgICBpZiAoc3RyaXBBbnNpKGFuc2kpLnRyaW0oKSA9PT0gJycpIGJyZWFrXG4gICAgYXdhaXQgc2luayhhbnNpKVxuICAgIG9uUHJvZ3Jlc3M/LihvZmZzZXQgKyBjaHVua1NpemUpXG4gIH1cbn1cblxuLyoqXG4gKiBSZW5kZXJzIG1lc3NhZ2VzIHRvIGEgcGxhaW4gdGV4dCBzdHJpbmcgc3VpdGFibGUgZm9yIGV4cG9ydC5cbiAqIFVzZXMgdGhlIHNhbWUgUmVhY3QgcmVuZGVyaW5nIGxvZ2ljIGFzIHRoZSBpbnRlcmFjdGl2ZSBVSS5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHJlbmRlck1lc3NhZ2VzVG9QbGFpblRleHQoXG4gIG1lc3NhZ2VzOiBNZXNzYWdlW10sXG4gIHRvb2xzOiBUb29scyA9IFtdLFxuICBjb2x1bW5zPzogbnVtYmVyLFxuKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgY29uc3QgcGFydHM6IHN0cmluZ1tdID0gW11cbiAgYXdhaXQgc3RyZWFtUmVuZGVyZWRNZXNzYWdlcyhcbiAgICBtZXNzYWdlcyxcbiAgICB0b29scyxcbiAgICBjaHVuayA9PiB2b2lkIHBhcnRzLnB1c2goc3RyaXBBbnNpKGNodW5rKSksXG4gICAgeyBjb2x1bW5zIH0sXG4gIClcbiAgcmV0dXJuIHBhcnRzLmpvaW4oJycpXG59XG4iXSwibWFwcGluZ3MiOiJBQUFBLE9BQU9BLEtBQUssSUFBSUMsTUFBTSxRQUFRLE9BQU87QUFDckMsT0FBT0MsU0FBUyxNQUFNLFlBQVk7QUFDbEMsU0FBU0MsUUFBUSxRQUFRLDJCQUEyQjtBQUNwRCxTQUFTQyxrQkFBa0IsUUFBUSxxQ0FBcUM7QUFDeEUsU0FBU0MsK0JBQStCLFFBQVEsb0NBQW9DO0FBQ3BGLGNBQWNDLHFCQUFxQixRQUFRLHlCQUF5QjtBQUNwRSxTQUFTQyxnQkFBZ0IsUUFBUSxzQkFBc0I7QUFDdkQsY0FBY0MsS0FBSyxRQUFRLFlBQVk7QUFDdkMsY0FBY0MsT0FBTyxRQUFRLHFCQUFxQjtBQUNsRCxTQUFTQyxrQkFBa0IsUUFBUSxtQkFBbUI7O0FBRXREO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxTQUFTQyx3QkFBd0JBLENBQUM7RUFDaENDO0FBR0YsQ0FGQyxFQUFFO0VBQ0RBLFFBQVEsRUFBRVosS0FBSyxDQUFDYSxTQUFTO0FBQzNCLENBQUMsQ0FBQyxFQUFFYixLQUFLLENBQUNhLFNBQVMsQ0FBQztFQUNsQixNQUFNO0lBQUVDO0VBQVMsQ0FBQyxHQUFHVCwrQkFBK0IsQ0FBQyxDQUFDO0VBQ3RELE1BQU1VLGVBQWUsR0FBR2QsTUFBTSxDQUFDLElBQUksQ0FBQztFQUNwQyxNQUFNZSxrQkFBa0IsR0FBR2YsTUFBTSxDQUFDLElBQUlnQixHQUFHLENBQUMsQ0FBQyxDQUFDO0VBQzVDLE1BQU1DLGNB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