Add conversation export to the V2 UI - #1395
Merged
Paul Lizer (paullizer) merged 2 commits intoSep 3, 2026
Merged
Conversation
The V2 interface could export a single message as Word, PowerPoint or an email draft, but had no way to export a conversation. Anyone needing a transcript had to switch back to the classic interface. Adds a stepped export wizard (review, format, packaging, intro summary, download) for JSON, Markdown and PDF, as a single file or a ZIP, reachable from a conversation's rail menu, the new rail selection mode for bulk exports, and the conversation details dialog. No backend change: the wizard drives POST /api/conversations/export and /api/conversations/export/visual-scan, which already served the classic wizard, so both interfaces produce identical files. The Mermaid runtime moves out of MermaidDiagram.tsx into lib/mermaidRuntime.ts. An export covers conversations that were never rendered, so there is no mounted component to read an SVG from. Both callers now share one hardened runtime, mirroring the classic chat-mermaid-runtime.js split; the security tests follow the boundary to its new home and additionally assert that nothing else drives mermaid directly. Also fixes two bugs found on the way: - svgRaster read width="100%" as the number 100, rasterizing a 1094x541 diagram into a 100px sliver. This also affected the existing per-diagram PNG download. - The wizard is portalled to document.body. The rail sits inside a .glass element, and a backdrop-filter makes an element a containing block for fixed-position descendants, which trapped the dialog in the 280px sidebar. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Conflicts resolved: - config.py: 0.261.038 was taken by the shared-conversations work that landed meanwhile, so the export feature renumbers to 0.261.040. - ConversationRail.tsx: shared conversations and conversation export both changed the row. Combined rather than chosen -- the icon import keeps Download/X alongside LogOut/Users, and the row keeps isSelected alongside shareable/shared, so Share, Leave and Export all sit in the same menu. - release_notes.md: both sides added a section at the top. Kept both, with the export section renumbered so versions stay in descending order. The selection-mode row also gains the pin indicator the normal row has, so a pinned conversation is still recognisable while picking conversations. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds whole-conversation export to the V2 (React) interface.
Important
This PR targets
paullizer-react-v2-ui, notDevelopment. The V2 UI only exists on that branch.Why
The V2 interface could export a single message as Word, PowerPoint or an email draft, but had no way to export a conversation. Anyone who needed a transcript had to switch back to the classic interface.
What
A stepped export wizard — Select -> Format -> Packaging -> Summary -> Download — mirroring the classic one, so both interfaces can be described by one set of instructions.
Selectmode in the conversation rail with checkboxes, All/Clear and a live count...menu, the selection toolbar, and the conversation details dialogNo backend changes. The wizard drives
POST /api/conversations/exportandPOST /api/conversations/export/visual-scan, which already served the classic wizard, so both interfaces produce identical files down to the filename.Notable implementation detail
The Mermaid runtime moves out of
MermaidDiagram.tsxintolib/mermaidRuntime.ts. A conversation export covers messages that were never rendered, so there is no mounted component to read an SVG from. Both callers now share one hardened runtime — the same separation the classic interface already makes withchat-mermaid-runtime.js.Diagrams are rasterized in the browser rather than server-side, which avoids launching headless Chromium once per export.
Bugs fixed along the way
width="100%", whichparseFloatread as the number100, so a 1094x541 diagram came out 100x541. This was also silently affecting the existing per-diagram PNG download in the chat..glasselement, and a non-nonebackdrop-filtermakes an element a containing block for fixed-position descendants, sofixed inset-0resolved to the sidebar rather than the viewport. Now portalled todocument.body. This affected 2 of the 3 entry points.Findings 2 and 3 came out of an independent code review of this change.
Security
Extracting the runtime relocated the DOMPurify boundary and mermaid hardening (
securityLevel: 'strict',htmlLabels: false,suppressErrorRendering: true). Three security tests were retargeted rather than weakened, and tightened:lib/**/*.ts, which was previously invisible — the sink scan only looked atcomponents/**/*.tsx.Rebased onto the latest base
Merged
origin/paullizer-react-v2-uiafter #1394 (shared conversations) and #1396 (admin Appearance parity) landed. Three conflicts, all resolved by combining rather than choosing:ConversationRail.tsx— shared conversations and export both changed the row. Share, Leave and Export now sit in the same menu, and the row keepsisSelectedalongsideshareable/shared.release_notes.md— both sides added a top section; both kept, in descending version order.config.py—0.261.038was taken by the shared-conversations work, so this renumbers to0.261.040.Validation
test_v2_conversation_export.py: 15/15, including 33 TypeScript behaviour checkstsc -bclean;npm run buildsucceedsDocs and version
docs/explanation/features/V2_CONVERSATION_EXPORT.mdREACT_V2_UI.mdupdated (new section; removed the now-wrong "conversation export ... not wired" limitation; corrected a stale bundle-size figure)0.261.039->0.261.040