Skip to content

added feature: Export Chat Transcript (Download as Markdown / JSON) - #114

Merged
piyushdotcomm merged 1 commit into
piyushdotcomm:developfrom
akash3911:export-feature
Jun 1, 2026
Merged

added feature: Export Chat Transcript (Download as Markdown / JSON)#114
piyushdotcomm merged 1 commit into
piyushdotcomm:developfrom
akash3911:export-feature

Conversation

@akash3911

@akash3911 akash3911 commented May 15, 2026

Copy link
Copy Markdown

Summary

  • Added chat transcript export functionality in the AI chat panel.
  • Users can now download conversations as:
    • Markdown (.md)
    • JSON (.json)
  • Added a dropdown export menu with download options.
  • Implemented reusable export utility functions for:
    • Markdown formatting
    • JSON formatting
    • Timestamp-based filename generation
    • Browser-side file downloads

Why it changed

This feature allows users to save, share, and reuse chat conversations outside the application. It improves usability by providing portable conversation exports without requiring backend changes.


Type of change

  • Bug fix
  • New feature
  • Refactor
  • Documentation
  • Test or CI improvement
  • Starter template change

Related issue

Closes #107


Validation

  • npm run lint
  • npm test
  • npm run build

List any additional manual verification you performed:

  • Verified Markdown export downloads correctly
  • Verified JSON export downloads correctly
  • Verified export button is disabled when no messages exist
  • Verified timestamp-based filenames are generated properly
  • Verified exported files contain chat messages in expected format

Checklist

  • I kept this PR focused on one primary change
  • I updated documentation if behavior changed
  • I did not commit secrets, local logs, or scratch files
  • I am requesting review on the correct scope

Summary by CodeRabbit

  • New Features
    • Added chat history export feature enabling users to download conversations in Markdown or JSON format
    • Export dropdown menu integrated into the chat panel header alongside the existing Clear chat option
    • Exported files include message metadata and timestamps for easy sharing and archival

Review Change Stack

@akash3911
akash3911 requested a review from piyushdotcomm as a code owner May 15, 2026 13:38
@qodo-code-review

Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@github-actions github-actions Bot added the enhancement New feature or request label May 15, 2026
@github-actions

Copy link
Copy Markdown

👋 Thanks for opening a PR, @akash3911!

Your PR has entered the 🚦 PR Review Pipeline.

Standard PR detected — your PR will follow the standard review pipeline.


What happens next

Stage Reviewer Checks
Stage 1 — Automated Validation 🤖 Bot DCO · Format · AI/Slop · Duplicate
Stage 2 — Human Review 👥 Maintainer Code + Quality Review
Stage 3 — PA / Maintainer Review 🔑 Project Admin Final Merge Decision

A pipeline status comment will appear below and update automatically as your PR progresses.


While you wait

  • Sign all commits (git commit -s)
  • Link your issue (Closes #123)
  • Use a feature branch (not main)
  • Avoid unrelated changes

This comment is posted only once.

@coderabbitai

coderabbitai Bot commented May 15, 2026

Copy link
Copy Markdown

Walkthrough

The PR implements client-side chat history export. A new module provides Markdown and JSON formatters, filename generation, and browser download logic. The chat panel component adds a dropdown menu in the header offering both export formats, with validation and toast feedback for success or errors.

Changes

Chat Export Implementation

Layer / File(s) Summary
Export utility functions and formatters
modules/playground/lib/chat-export.ts
ExportMessage interface, exportToMarkdown and exportToJSON formatters with role labels, timestamps, and tool invocation extraction; generateFilename creates timestamped .md/.json names; downloadFile triggers browser downloads via Blob/object URL; exportChatAsMarkdown and exportChatAsJSON wrapper functions.
Chat panel export UI and handlers
modules/playground/components/ai-chat-panel.tsx
Dropdown-menu and Download icon imports; exportChatAsMarkdown and exportChatAsJSON imports; handleExportMarkdown and handleExportJSON handlers validate messages and show success/error toasts; header action area replaced with export dropdown (Markdown/JSON options) and clear-chat button.

Sequence Diagram

sequenceDiagram
  participant User
  participant AIChatPanel
  participant DropdownMenu
  participant ExportHandlers
  participant ExportUtils
  User->>AIChatPanel: clicks Export dropdown
  AIChatPanel->>DropdownMenu: renders Markdown/JSON options
  User->>DropdownMenu: selects Markdown or JSON
  DropdownMenu->>ExportHandlers: triggers handler
  ExportHandlers->>ExportHandlers: validate messages exist
  ExportHandlers->>ExportUtils: call export function
  ExportUtils->>ExportUtils: format and download
  ExportHandlers->>User: show success/error toast
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A rabbit's verse on transcripts fine:
Chat histories now download with flair,
Markdown and JSON, timestamped line,
Conversations saved and easy to share! 📥✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main feature addition: export chat transcripts in Markdown and JSON formats.
Description check ✅ Passed The description covers all required sections: summary of changes, type of change, related issue, validation steps, and checklist items.
Linked Issues check ✅ Passed The PR implementation fully addresses issue #107 objectives: export button in UI, Markdown and JSON download support, client-side implementation, timestamped filenames, and proper message collection.
Out of Scope Changes check ✅ Passed All changes are focused on the export feature: chat panel UI updates for the export dropdown, and new export utility module for Markdown/JSON formatting and file downloads.
Docstring Coverage ✅ Passed Docstring coverage is 85.71% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@modules/playground/components/ai-chat-panel.tsx`:
- Around line 361-369: Add explicit aria-label attributes to the icon-only
<Button> elements in ai-chat-panel (the ones using the <Download> icon and the
other icon-only button around lines referenced) so screen readers get a reliable
description instead of relying on title; update the <Button> components that
currently have title="Export chat" (and the other icon-only button) to include a
matching aria-label (e.g., aria-label="Export chat") while preserving
disabled={messages.length === 0}, size="icon", variant="ghost", and existing
className props.

In `@modules/playground/lib/chat-export.ts`:
- Around line 63-64: The exported JSON currently sets messageCount to
messages.length before synthetic entries are removed, causing a mismatch; after
the synthetic-message filtering step (the code that removes synthetic messages
from messages), recompute and set messageCount using the filtered array (e.g.,
use filteredMessages.length or the post-filter messages variable) so the
messageCount and messages fields in the export match; update any other places
that rely on messages.length to use the filtered collection instead.
- Around line 84-91: The map that builds toolInvocations is exporting raw
ti.input (in the toolInvocations map over rawParts), which may contain
hidden/internal payloads; change it to export only a sanitized/visible input
(e.g., use a new helper like sanitizeToolInput(ti) or prefer an explicit field
such as ti.inputVisible or ti.visibleInput) and ensure the sanitizer strips
internal fields before serialization; update the mapping (the toolInvocations
block) to call that sanitizer or select only safe properties (toolName,
toolCallId, state, visibleInput) instead of exporting ti.input verbatim.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 0220ae3c-8abe-4600-bd1d-19d741b4f3fe

📥 Commits

Reviewing files that changed from the base of the PR and between 1697384 and a45fa06.

📒 Files selected for processing (2)
  • modules/playground/components/ai-chat-panel.tsx
  • modules/playground/lib/chat-export.ts

Comment on lines +361 to +369
<Button
size="icon"
variant="ghost"
className="h-8 w-8 text-muted-foreground hover:text-foreground hover:bg-muted/50 transition-colors"
title="Export chat"
disabled={messages.length === 0}
>
<Download className="h-4 w-4" />
</Button>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add explicit aria-label to icon-only action buttons.

These controls currently rely on title; screen-reader support for title is inconsistent. Add aria-label for reliable accessibility.

Suggested fix
                                     <Button 
                                         size="icon" 
                                         variant="ghost" 
                                         className="h-8 w-8 text-muted-foreground hover:text-foreground hover:bg-muted/50 transition-colors" 
                                         title="Export chat"
+                                        aria-label="Export chat"
                                         disabled={messages.length === 0}
                                     >
                                         <Download className="h-4 w-4" />
                                     </Button>
...
-                            <Button size="icon" variant="ghost" className="h-8 w-8 text-muted-foreground hover:text-destructive hover:bg-destructive/10 transition-colors" onClick={clearChat} title="Clear chat">
+                            <Button size="icon" variant="ghost" className="h-8 w-8 text-muted-foreground hover:text-destructive hover:bg-destructive/10 transition-colors" onClick={clearChat} title="Clear chat" aria-label="Clear chat">
                                 <Trash2 className="h-4 w-4" />
                             </Button>

Also applies to: 380-382

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@modules/playground/components/ai-chat-panel.tsx` around lines 361 - 369, Add
explicit aria-label attributes to the icon-only <Button> elements in
ai-chat-panel (the ones using the <Download> icon and the other icon-only button
around lines referenced) so screen readers get a reliable description instead of
relying on title; update the <Button> components that currently have
title="Export chat" (and the other icon-only button) to include a matching
aria-label (e.g., aria-label="Export chat") while preserving
disabled={messages.length === 0}, size="icon", variant="ghost", and existing
className props.

Comment on lines +63 to +64
messageCount: messages.length,
messages: messages

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

messageCount can be incorrect after filtering synthetic messages.

Line 63 uses the original array length, but Line 94 removes entries. Metadata can disagree with messages.length in the exported JSON.

Suggested fix
 export function exportToJSON(messages: any[]): string {
+  const exportedMessages = messages
+    .map((msg) => {
+      // ...existing mapping logic
+    })
+    .filter(Boolean);
+
   const exportData = {
     version: "1.0",
     exportedAt: new Date().toISOString(),
-    messageCount: messages.length,
-    messages: messages
-      .map((msg) => {
-        // ...
-      })
-      .filter(Boolean),
+    messageCount: exportedMessages.length,
+    messages: exportedMessages,
   };

Also applies to: 94-94

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@modules/playground/lib/chat-export.ts` around lines 63 - 64, The exported
JSON currently sets messageCount to messages.length before synthetic entries are
removed, causing a mismatch; after the synthetic-message filtering step (the
code that removes synthetic messages from messages), recompute and set
messageCount using the filtered array (e.g., use filteredMessages.length or the
post-filter messages variable) so the messageCount and messages fields in the
export match; update any other places that rely on messages.length to use the
filtered collection instead.

Comment on lines +84 to +91
toolInvocations: rawParts
.filter((p: any) => p.type?.startsWith("tool-") || p.type === "tool-invocation")
.map((ti: any) => ({
toolName: ti.toolName ?? ti.type?.split("-").slice(1).join("-"),
toolCallId: ti.toolCallId,
input: ti.input,
state: ti.state,
})),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid exporting non-visible tool inputs by default.

Line 89 serializes ti.input, which can include hidden/internal payloads not shown in the transcript UI. That creates a privacy leak risk in exported files.

Suggested fix
         return {
           id: msg.id,
           role: msg.role,
           content: textContent,
           timestamp: msg.createdAt ? new Date(msg.createdAt).toISOString() : null,
           toolInvocations: rawParts
             .filter((p: any) => p.type?.startsWith("tool-") || p.type === "tool-invocation")
             .map((ti: any) => ({
               toolName: ti.toolName ?? ti.type?.split("-").slice(1).join("-"),
               toolCallId: ti.toolCallId,
-              input: ti.input,
               state: ti.state,
             })),
         };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@modules/playground/lib/chat-export.ts` around lines 84 - 91, The map that
builds toolInvocations is exporting raw ti.input (in the toolInvocations map
over rawParts), which may contain hidden/internal payloads; change it to export
only a sanitized/visible input (e.g., use a new helper like
sanitizeToolInput(ti) or prefer an explicit field such as ti.inputVisible or
ti.visibleInput) and ensure the sanitizer strips internal fields before
serialization; update the mapping (the toolInvocations block) to call that
sanitizer or select only safe properties (toolName, toolCallId, state,
visibleInput) instead of exporting ti.input verbatim.

@akash3911

Copy link
Copy Markdown
Author

@piyushdotcomm can you review the PR

@piyushdotcomm piyushdotcomm left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

show me a screenrecording or screenshot

@piyushdotcomm
piyushdotcomm changed the base branch from main to develop June 1, 2026 15:38

@piyushdotcomm piyushdotcomm left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@piyushdotcomm
piyushdotcomm merged commit 6976199 into piyushdotcomm:develop Jun 1, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request gssoc:approved

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: add chat transcript export as Markdown/JSON

2 participants