feat: add Export Chat (Markdown / PDF) to session kebab menu#625
feat: add Export Chat (Markdown / PDF) to session kebab menu#625Gkrumbach07 merged 7 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. |
- Fix type safety: explicitly type regex callback parameters in export-chat.ts:398 - Remove unused tw-animate-css import from globals.css (all animations are defined in tailwind config) - Add validation for Google Drive MCP response to ensure file creation succeeded Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Claude Code ReviewSummaryThis PR adds human-readable chat export functionality to the session kebab menu with Markdown and PDF output formats. The implementation extracts AG-UI events into structured conversation blocks and introduces a reusable Overall Assessment: Strong implementation with a few critical security and usability issues that need addressing before merge. Issues by Severity🚫 Blocker Issues1. Hardcoded Email in Google Drive Export
// CURRENT (WRONG):
args: { user_google_email: 'jeder@redhat.com', file_name: filename, content, mime_type: 'text/markdown' }
// SHOULD BE:
args: { user_google_email: me?.email || '', file_name: filename, content, mime_type: 'text/markdown' }🔴 Critical Issues1. Type Safety Violations - Multiple
// Define types based on AGUIEventType enum
type AGUIEvent =
| { type: AGUIEventType.TEXT_MESSAGE_START; role: string; timestamp?: string }
| { type: AGUIEventType.TEXT_MESSAGE_CONTENT; delta: string }
| { type: AGUIEventType.TEXT_MESSAGE_END }
| { type: AGUIEventType.TOOL_CALL_START; toolCallId: string; toolCallName: string; timestamp?: string }
// ... etc
export type SessionExportResponse = {
sessionId: string;
projectName: string;
exportDate: string;
aguiEvents: AGUIEvent[];
legacyMessages?: Message[]; // Define Message type
hasLegacy: boolean;
};2. Unsafe Type Assertion in
result = result.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (match, text, url) => {
// Now text and url are properly typed3. Missing Error Boundary for PDF Export
// In handleExport:
case 'pdf':
try {
exportAsPdf(markdown, filename);
} catch (err) {
throw new Error('Failed to open print window. Please allow popups for this site.');
}
break;4. Google Drive Export UI Issues
🟡 Major Issues1. Missing Loading State for Google Drive Export
2. Inconsistent Button Placement
3. Markdown HTML Pass-Through Security Concern
const ALLOWED_TAGS = ['details', 'summary'];
if (/^\s*<\/?(?:details|summary)/.test(line)) {
const tag = line.match(/<\/?([a-z]+)/)?.[1];
if (\!tag || \!ALLOWED_TAGS.includes(tag)) {
continue; // Skip potentially malicious tags
}
// ... rest of logic
}4. No Tests for Export Utilities
5. PDF Print Dialog May Double-Fire
🔵 Minor Issues1. Magic Numbers Without Constants
const TOOL_ARGS_MAX_LENGTH = 2000; // Limit for readability in exports
const TOOL_ERROR_MAX_LENGTH = 1000;
const TOOL_RESULT_MAX_LENGTH = 2000;
const PDF_PRINT_TIMEOUT_MS = 500; // Fallback if load event doesn't fire2. Inconsistent Icon Usage
3. Type Repetition
4. Missing JSDoc for Complex Functions
5. Unused Import in Modal
Positive Highlights✅ Excellent XSS Protection - ✅ Clean Separation of Concerns - Export logic separated into utility file, not mixed with UI ✅ Reusable Download Utility - ✅ Good Loading States - All export buttons show loading spinners ( ✅ Proper URL Validation - PDF export validates URLs before creating links (export-chat.ts:399-407) ✅ Accessibility - Print dialog is native browser feature (good for accessibility) ✅ Graceful Truncation - Tool outputs truncated to prevent massive exports ✅ React Query Integration - Uses ✅ Guard Flag for Double-Print - Prevents duplicate print dialogs (export-chat.ts:290-294) RecommendationsPriority 1 (Must Fix Before Merge)
Priority 2 (Should Fix)
Priority 3 (Nice to Have)
Testing RecommendationsAdd these test cases:
Overall: This is a well-crafted feature with good security practices. The main issues are type safety violations (critical for TypeScript projects) and the hardcoded email (blocker). Once these are addressed, this will be a solid addition to the platform. 🤖 Reviewed by Claude Sonnet 4.5 🔍 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. |
| { | ||
| server: serverName, | ||
| tool: 'create_drive_file', | ||
| args: { user_google_email: 'jeder@redhat.com', file_name: filename, content, mime_type: 'text/markdown' }, |
Replace hardcoded email with the current user's email from useCurrentUser() hook. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Claude Code ReviewSummaryThis PR adds export functionality to the session kebab menu with three export formats: Markdown, PDF, and Google Drive. The implementation includes a new utility module for processing AG-UI events into human-readable markdown, an MCP invoke API route for Google Drive integration, and security fixes for XSS prevention. Overall Assessment: High-quality implementation with good security practices. A few minor issues to address before merge, primarily around type safety and error handling. Issues by Severity🚫 Blocker IssuesNone 🔴 Critical Issues1. XSS Risk in URL rendering (export-chat.ts:398-408)
2. Type Safety: Missing type definitions (export-chat.ts:10-19)
🟡 Major Issues1. Error Handling: Silent failures (session-header.tsx:80-104)
2. React Query Pattern: Missing error states (session-header.tsx:41)
3. API Client: No response validation (route.ts:49)
🔵 Minor Issues
Positive Highlights
RecommendationsPriority 1 (Fix before merge)
Priority 2 (Nice to have)
Priority 3 (Future improvements)
Overall AssessmentThis is a well-implemented feature that follows most project standards. The code quality is high, with good separation of concerns, proper React Query usage, and excellent security awareness. The main concerns are:
Once the Priority 1 items are addressed, this PR is ready to merge. 🤖 Reviewed by Claude Code with memory context from CLAUDE.md and .claude/context/* files 🔍 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. |
|
fixed |
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