Skip to content

Add export chat as Markdown feature#370

Open
erkin98 wants to merge 1 commit into
jupyterlab:mainfrom
erkin98:feat/export-chat-button
Open

Add export chat as Markdown feature#370
erkin98 wants to merge 1 commit into
jupyterlab:mainfrom
erkin98:feat/export-chat-button

Conversation

@erkin98

@erkin98 erkin98 commented Feb 24, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds a download button to the chat section toolbar that exports the conversation as a .md file
  • Adds an exportChat command accessible from the command palette
  • Introduces downloadChatAsMarkdown and formatChatAsMarkdown utility functions in @jupyter/chat

The exported Markdown includes sender names, timestamps, and preserves message formatting. The file downloads directly to the user's machine via a temporary anchor element.

Changes

Package File Change
@jupyter/chat src/utils.ts New formatChatAsMarkdown and downloadChatAsMarkdown functions
@jupyter/chat src/index.ts Re-export downloadChatAsMarkdown
@jupyter/chat src/widgets/multichat-panel.tsx Export button in ChatSection toolbar
jupyterlab-chat src/token.ts exportChat command ID
jupyterlab-chat-extension src/index.ts Command registration + palette entry

Test plan

  • Open a chat with several messages, click the download icon — verify .md file downloads
  • Open command palette, run "Export chat as Markdown" — same behavior
  • Verify exported Markdown has correct sender names and timestamps
  • Verify the command is disabled when no chat widget is focused

Closes #145

@github-actions

Copy link
Copy Markdown
Contributor

Binder 👈 Launch a Binder on branch erkin98/jupyter-chat/feat%2Fexport-chat-button

@brichet brichet added the enhancement New feature or request label Mar 12, 2026
@brichet

brichet commented Mar 12, 2026

Copy link
Copy Markdown
Collaborator

Thanks @erkin98 for working on this, it looks great.

Looks like it picked up some conflicts with main.

Also, the related issue should probably be #145 instead.

Adds a download button to the chat section toolbar and a command palette
entry that exports the current chat conversation as a Markdown file.

The export formats messages with sender names, timestamps, and proper
Markdown structure. The file is downloaded directly to the user's machine.

Changes:
- Add `downloadChatAsMarkdown` and `formatChatAsMarkdown` utilities
- Add export button to `ChatSection` toolbar (download icon)
- Add `exportChat` command to the command palette
- Export `downloadChatAsMarkdown` from `@jupyter/chat` public API

Closes jupyterlab#328
@erkin98 erkin98 force-pushed the feat/export-chat-button branch from afd50fe to 5b47122 Compare March 12, 2026 14:23
@erkin98

erkin98 commented Mar 12, 2026

Copy link
Copy Markdown
Contributor Author

Rebased on main, conflicts are resolved. Fixed the linked issue to #145 too, good catch.

@brichet brichet left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks @erkin98 for rebasing the PR.

I have some comments below.

document.body.appendChild(a); // Firefox requires DOM attachment
a.click();
document.body.removeChild(a);
setTimeout(() => URL.revokeObjectURL(url), 100); // delayed to avoid premature revoke

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
setTimeout(() => URL.revokeObjectURL(url), 100); // delayed to avoid premature revoke
requestAnimationFrame(() => URL.revokeObjectURL(url));

It might be more robust to use the requestAnimationFrame to cleanup the URL.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good call — requestAnimationFrame is cleaner since it guarantees the download has started before we revoke. Will update.

for (const msg of visibleMessages) {
const displayName = msg.sender.display_name || msg.sender.username || 'Unknown';
const timestamp = new Date(msg.time * 1000).toLocaleString();
const body = typeof msg.body === 'string' ? msg.body : '[Rich content]';

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Maybe we should wait for #386, which changes the API. The body will always be a string after that.
Instead, the message can contain mime_model, that replaces the body content with a rich content. This mime model should always be serializable, so maybe it could also be interesting to export it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Makes sense to wait for #386. Once body is always a string, the type guard goes away and we can focus on how to handle mime_model in the export — either include its serialized form or render it as a placeholder depending on the mime type. I'll rebase on top of #386 once it lands.

});

// Command to export the current chat as a Markdown file.
commands.addCommand(CommandIDs.exportChat, {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This command could also be added in the toolbar of the main area widget https://github.com/jupyterlab/jupyter-chat/blob/main/packages/jupyterlab-chat-extension/schema/chat-panel.json

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good idea — I'll add the export command to the chat panel toolbar schema so it's accessible from the main area widget too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

Add functionality to export chats

2 participants