feat: add Export Chat (Markdown / PDF) to session kebab menu#625
feat: add Export Chat (Markdown / PDF) to session kebab menu#625jeremyeder wants to merge 5 commits intoambient-code:mainfrom
Conversation
Add human-readable chat export via a new "Export chat" submenu in the session kebab menu, with "As Markdown" and "As PDF" options. Processes AG-UI events client-side into structured markdown with metadata header, User/Assistant sections, and collapsible tool call details. PDF export renders styled HTML in a new window and triggers the browser print dialog. Also deduplicates the blob-download pattern by extracting a shared triggerDownload() utility now used by both the new export and the existing JSON export in the session details modal. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This comment has been minimized.
This comment has been minimized.
- Throw on window.open() failure so popup-blocker errors surface as toast notification instead of silently failing - Add isExportEvent type guard instead of bare `as` cast from unknown - Extract truncation magic numbers to named constants - Add `void` operator on async onClick handlers for clarity - Clean up timeout on load event in PDF export Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Claude Code ReviewSummaryThis PR adds Markdown and PDF export functionality to the session kebab menu, with clean client-side event processing and shared utility extraction. The implementation follows project standards well, with strong type safety, proper React patterns, and security considerations. A few minor improvements could enhance robustness and user experience. Overall Assessment: ✅ Approved with recommendations Issues by Severity🚫 Blocker IssuesNone identified. 🔴 Critical Issues1. Error Handling - Print Dialog Blocked
// Current: throws error
if (\!printWindow) {
throw new Error('Failed to open print window...');
}
// Better: catch in session-header.tsx handleExport
try {
exportAsPdf(markdown, filename);
} catch (err) {
errorToast(err instanceof Error ? err.message : 'Failed to export chat');
}🟡 Major Issues1. Race Condition in Print Trigger
// Current: can print twice
const timeoutId = setTimeout(() => printWindow.print(), 500);
printWindow.addEventListener('load', () => {
clearTimeout(timeoutId); // Clears timeout but still calls print()
printWindow.print(); // This runs even if timeout already fired
});
let printed = false;
const timeoutId = setTimeout(() => {
if (\!printed) {
printed = true;
printWindow.print();
}
}, 500);
printWindow.addEventListener('load', () => {
clearTimeout(timeoutId);
if (\!printed) {
printed = true;
printWindow.print();
}
}, { once: true });2. Missing Loading State Reset on Error
3. Type Safety - Inline Type vs Import
type ExportEvent = {
type: string;
role?: string;
delta?: string;
// ... duplicates AGUIEventType structure
};
🔵 Minor Issues1. Magic Numbers Should Be Constants
/** Maximum length for tool arguments before truncation (readability) */
const MAX_TOOL_ARGS_LENGTH = 2000;
/** Print dialog timeout fallback (allows slow browsers to load) */
const PRINT_DIALOG_TIMEOUT_MS = 500;2. PDF Export User Feedback
3. File Download Cleanup Timing
link.click();
setTimeout(() => URL.revokeObjectURL(url), 100);4. Empty State Handling
5. Markdown Table Edge Case
if (parsed.length === 0 || header.length === 0) return '';Positive Highlights✅ Excellent Type Safety - Zero ✅ Security Conscious - ✅ Clean Architecture - Proper separation: utilities in ✅ Loading States - Disabled buttons with spinner icons during export ( ✅ Accessibility - Menu uses Shadcn dropdowns with proper ARIA attributes, keyboard navigation ✅ Code Reuse - Refactored duplicate ✅ Documentation - Clear JSDoc comments explaining function purposes and parameters RecommendationsHigh Priority
Medium Priority
Low Priority
Pre-Merge ChecklistBased on CLAUDE.md standards:
Testing RecommendationsBefore merging, verify:
Files Review Summary
Overall Code Quality: 4.5/5 ⭐⭐⭐⭐☆ Great work on this feature! The code is well-structured and follows project conventions. Address the critical error handling issues and this will be ready to merge. 🔍 View AI decision process (logs available for 90 days) 📋 View memory system files loaded (click to expand)What Amber Loaded for Code ReviewAmber automatically loaded these repository standards from the memory system:
Impact: This review used your repository's specific code quality standards, security patterns, and best practices from the memory system (PRs #359, #360) - not just generic code review guidelines. |
…oast fix
- Add emoji indicators (👤 User / 🤖 Assistant / 🔧 Tool) to exported markdown
- Color-code role headings in PDF export (blue for User, green for Assistant)
- Add visual accent border on tool detail blocks in PDF
- Add username and session hyperlink to export metadata table
- Support markdown links in PDF export (http/https only, XSS-safe)
- Add "Save to my Google Drive" option via session MCP server
- Add MCP invoke proxy route for frontend-to-backend MCP tool calls
- Fix sticky toast: add duration={5000} to ToastProvider
- Fix broken tw-animate-css Tailwind plugin (CSS-only package, not a JS plugin)
- Export use-google hooks from queries barrel
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Claude Code ReviewSummaryThis PR adds export chat functionality (Markdown/PDF/Google Drive) to the session kebab menu. The implementation demonstrates solid code quality with proper XSS prevention, good TypeScript practices, and well-structured utility functions. However, there are several critical issues around error handling, React Query usage, and frontend patterns that must be addressed before merge. Issues by Severity🚫 Blocker Issues1. Missing React Query for Data Fetching
2. Missing Error Handling in MCP Invoke Route
🔴 Critical Issues3. Async Handler Without React Query Mutation
4. Type Safety: Weak Type Guard
5. Security: Incomplete URL Validation
🟡 Major Issues6. Unnecessary Hook Calls
7. Missing Empty State Handling
8. Inconsistent Error Messages
🔵 Minor Issues9. Magic Numbers: Truncation limits hardcoded (export-chat.ts:25-27) Positive Highlights✅ Excellent XSS Prevention with escapeHtml RecommendationsPriority 1 (Must Fix Before Merge):
Priority 2 (Should Fix Before Merge): Priority 3 (Nice to Have): Pre-Commit Checklist
Overall Assessment: Good implementation with solid security practices, but requires critical fixes to React Query usage and error handling before merge. 🤖 Reviewed with Claude Code 🔍 View AI decision process (logs available for 90 days) 📋 View memory system files loaded (click to expand)What Amber Loaded for Code ReviewAmber automatically loaded these repository standards from the memory system:
Impact: This review used your repository's specific code quality standards, security patterns, and best practices from the memory system (PRs #359, #360) - not just generic code review guidelines. |
- Add guard flag to prevent double-print in PDF export - Delay URL.revokeObjectURL to avoid race with download start - Add null guard on exportData.aguiEvents - Use URL() constructor for more robust link validation in PDF Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Claude Code ReviewSummaryThis PR adds chat export functionality (Markdown/PDF) to the session kebab menu with Google Drive integration. The implementation is generally solid with good security practices (XSS prevention via HTML escaping). However, there are several issues that need attention ranging from critical type safety violations to architectural concerns. Issues by SeverityCritical Issues
Major Issues
Minor Issues8-13. Documentation, constants, type guards, JSDoc improvements Positive Highlights
RecommendationsPriority 1 (Before Merge):
Recommendation: Fix Critical and Major issues before merge. Minor issues can be addressed in follow-up PRs. 🔍 View AI decision process (logs available for 90 days) 📋 View memory system files loaded (click to expand)What Amber Loaded for Code ReviewAmber automatically loaded these repository standards from the memory system:
Impact: This review used your repository's specific code quality standards, security patterns, and best practices from the memory system (PRs #359, #360) - not just generic code review guidelines. |
- Use React Query (useSessionExport + refetch) instead of direct
getSessionExport() call, per project data-fetching standards
- Strengthen isExportEvent type guard to validate type is a string
- Improve MCP invoke route error handling: preserve structured JSON
errors from backend, wrap plain text in { error } envelope
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Claude Code ReviewSummaryThis PR adds a comprehensive chat export feature with Markdown, PDF, and Google Drive export options. The implementation demonstrates strong attention to security (XSS prevention), code organization (extraction of shared utilities), and user experience (loading states, proper error handling). The code follows frontend development patterns well and includes thoughtful defensive programming practices. Overall Assessment: Strong implementation with a few minor improvements needed before merge. Issues by Severity🟡 Major Issues1. Type Safety Violation -
result = result.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_match: string, text: string, url: string) => {
2. Missing Error Handling in Google Drive Export
3. Inconsistent Loading State Management
🔵 Minor Issues1. Hardcoded Magic Numbers
// Prevents excessively large markdown files that may crash browsers
const MAX_TOOL_ARGS_LENGTH = 2000;
const MAX_ERROR_LENGTH = 1000;
const MAX_RESULT_LENGTH = 2000;2. Window.open Popup Blocker Handling
throw new Error('Failed to open print window. Please allow popups for this site in your browser settings and try again.');3. Missing Component Size Check
4. Toast Duration Configuration Change
5. Tailwind Plugin Removal
6. URL Validation in Markdown Conversion
7. Missing Test Coverage Mention
Positive Highlights✅ Excellent Security Practices
✅ Code Reuse & Refactoring
✅ Robust Error Handling
✅ Comprehensive Loading States
✅ Accessibility & UX
✅ TypeScript Type Safety (mostly)
✅ React Query Integration
✅ PDF Export Implementation
RecommendationsPriority 1 (Before Merge)
Priority 2 (Follow-up)
Priority 3 (Nice-to-Have)
Code Quality Checklist
Final VerdictRecommend: Merge after addressing Priority 1 items This is a well-crafted feature with excellent attention to security and user experience. The one critical type safety violation should be fixed before merge, and the tailwind plugin removal should be verified. Everything else can be addressed in follow-up work. Great job on the security-conscious implementation (HTML escaping, URL validation) and the thoughtful refactoring to extract shared utilities! 🎉 🤖 Reviewed by Claude Code using repository standards from CLAUDE.md and .claude/context/** 🔍 View AI decision process (logs available for 90 days) 📋 View memory system files loaded (click to expand)What Amber Loaded for Code ReviewAmber automatically loaded these repository standards from the memory system:
Impact: This review used your repository's specific code quality standards, security patterns, and best practices from the memory system (PRs #359, #360) - not just generic code review guidelines. |
Summary
kebab-onlyandfullrender modes) with two options: As Markdown and As PDFutils/export-chat.tsprocesses AG-UI events client-side into structured markdown with metadata header, User/Assistant sections, and collapsible<details>blocks for tool callswindow.print()(browser native "Save as PDF")triggerDownload()utility, now used by both new export and existing JSON export insession-details-modal.tsxFiles changed
components/frontend/src/utils/export-chat.tsconvertEventsToMarkdown,downloadAsMarkdown,exportAsPdf,triggerDownloadcomponents/frontend/src/app/projects/[name]/sessions/[sessionName]/session-header.tsxDropdownMenuSubwith loading statescomponents/frontend/src/components/session-details-modal.tsxdownloadFilewith sharedtriggerDownloadimportTest plan
.mdfile downloads with readable conversation (metadata table, User/Assistant headings, tool calls as<details>)triggerDownloadrefactornpm run buildpasses with 0 errors, 0 warnings🤖 Generated with Claude Code