diff --git a/web/src/client/components/session-card.ts b/web/src/client/components/session-card.ts index 35d3178d..203b749b 100644 --- a/web/src/client/components/session-card.ts +++ b/web/src/client/components/session-card.ts @@ -2,6 +2,7 @@ import { LitElement, html, PropertyValues } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; import './terminal.js'; import type { Terminal } from './terminal.js'; +import { CastConverter } from '../utils/cast-converter.js'; export interface Session { id: string; @@ -99,25 +100,11 @@ export class SessionCard extends LitElement { const castContent = await response.text(); - // Clear terminal and write snapshot data + // Clear terminal first this.terminal.clear(); - // Parse cast file and write content - const lines = castContent.trim().split('\n'); - for (const line of lines) { - if (line.trim()) { - try { - const event = JSON.parse(line); - if (event.length >= 3 && event[1] === 'o') { - // Output event: [timestamp, 'o', data] - this.terminal.write(event[2], false); // Don't follow cursor for snapshot - } - } catch (_e) { - // Skip invalid lines - continue; - } - } - } + // Use the new super-fast dump method that handles everything in one operation + await CastConverter.dumpToTerminal(this.terminal, castContent); // Scroll to bottom after loading this.terminal.queueCallback(() => { diff --git a/web/src/client/utils/cast-converter.ts b/web/src/client/utils/cast-converter.ts index 3a6a64a3..1c9a46d3 100644 --- a/web/src/client/utils/cast-converter.ts +++ b/web/src/client/utils/cast-converter.ts @@ -208,4 +208,56 @@ export class CastConverter { } } } + + /** + * Dump entire cast content to terminal instantly as a single write operation. + * This is the fastest way to load cast content - builds one string and writes it all at once. + * Handles resize events by applying the final dimensions to the terminal. + * + * @param terminal - DOM terminal instance with write() and setTerminalSize() methods + * @param castContent - Raw cast file content + * @returns Promise that resolves when dump is complete + */ + static async dumpToTerminal( + terminal: { + write: (data: string, followCursor?: boolean) => void; + setTerminalSize?: (cols: number, rows: number) => void; + }, + castContent: string + ): Promise { + const converted = this.convertCast(castContent); + + // Track final terminal dimensions from resize events + let finalCols = converted.header?.width || 80; + let finalRows = converted.header?.height || 24; + + // Build up output string and track final resize dimensions + const outputChunks: string[] = []; + + for (const event of converted.events) { + if (event.type === 'o') { + // Output event - add to content + outputChunks.push(event.data); + } else if (event.type === 'r') { + // Resize event - track final dimensions + const match = event.data.match(/^(\d+)x(\d+)$/); + if (match) { + finalCols = parseInt(match[1], 10); + finalRows = parseInt(match[2], 10); + } + } + // Ignore 'i' (input) events for dump + } + + // Apply final terminal size first if we have resize capability + if (terminal.setTerminalSize) { + terminal.setTerminalSize(finalCols, finalRows); + } + + // Write all content at once as a single operation (fastest possible) + const allContent = outputChunks.join(''); + if (allContent) { + terminal.write(allContent, false); // Don't follow cursor during dump for performance + } + } }