Add /export command to export sessions as self-contained HTML

- Add exportSessionToHtml function that generates beautifully formatted HTML exports
- HTML includes session metadata, all messages, tool calls, tool results, thinking blocks, and images
- Support for ANSI color codes in tool output (converted to HTML)
- Self-contained with inline CSS (dark theme, responsive design, print-friendly)
- Add /export slash command to TUI with optional filename parameter
- Add agent and coding-agent to dev script for watch mode
- Increment coding-agent version to 0.6.1

Usage: /export [optional-filename.html]
This commit is contained in:
Mario Zechner 2025-11-12 16:21:59 +01:00
parent e533aebacd
commit e467a80b5b
4 changed files with 499 additions and 3 deletions

View file

@ -11,6 +11,7 @@ import {
TUI,
} from "@mariozechner/pi-tui";
import chalk from "chalk";
import { exportSessionToHtml } from "../export-html.js";
import type { SessionManager } from "../session-manager.js";
import { AssistantMessageComponent } from "./assistant-message.js";
import { CustomEditor } from "./custom-editor.js";
@ -77,8 +78,16 @@ export class TuiRenderer {
description: "Select model (opens selector UI)",
};
const exportCommand: SlashCommand = {
name: "export",
description: "Export session to HTML file",
};
// Setup autocomplete for file paths and slash commands
const autocompleteProvider = new CombinedAutocompleteProvider([thinkingCommand, modelCommand], process.cwd());
const autocompleteProvider = new CombinedAutocompleteProvider(
[thinkingCommand, modelCommand, exportCommand],
process.cwd(),
);
this.editor.setAutocompleteProvider(autocompleteProvider);
}
@ -151,6 +160,13 @@ export class TuiRenderer {
return;
}
// Check for /export command
if (text.startsWith("/export")) {
this.handleExportCommand(text);
this.editor.setText("");
return;
}
if (this.onInputCallback) {
this.onInputCallback(text);
}
@ -516,6 +532,29 @@ export class TuiRenderer {
this.ui.setFocus(this.editor);
}
private handleExportCommand(text: string): void {
// Parse optional filename from command: /export [filename]
const parts = text.split(/\s+/);
const outputPath = parts.length > 1 ? parts[1] : undefined;
try {
// Export session to HTML
const filePath = exportSessionToHtml(this.sessionManager, this.agent.state, outputPath);
// Show success message in chat
this.chatContainer.addChild(new Text("", 0, 0)); // Spacer
this.chatContainer.addChild(new Text(chalk.green(`✓ Session exported to: ${filePath}`), 0, 0));
this.ui.requestRender();
} catch (error: any) {
// Show error message in chat
this.chatContainer.addChild(new Text("", 0, 0)); // Spacer
this.chatContainer.addChild(
new Text(chalk.red(`✗ Failed to export session: ${error.message || "Unknown error"}`), 0, 0),
);
this.ui.requestRender();
}
}
stop(): void {
if (this.loadingAnimation) {
this.loadingAnimation.stop();