feat(app): add copy rich text option for message content#16056
feat(app): add copy rich text option for message content#16056anduimagui wants to merge 12 commits intoanomalyco:devfrom
Conversation
|
The following comment was made by an LLM, it may be inaccurate: Found one potential related PR: PR #11638: feat(tui): add rich text clipboard support for copying formatted content This PR may be related as it also deals with rich text clipboard support, though it appears to be for TUI (terminal UI) while PR #16056 is focused on desktop. The PRs target different platforms but share the same core feature of handling rich text in clipboard operations with explicit payload formats. |
|
yh this PR intentionally scopes to desktop/webview chat UI @adamdotdevin |
8db1ade to
5275451
Compare
# Conflicts: # packages/ui/src/components/message-part.tsx
Move markdown clipboard cleanup and copy behavior into shared clipboard helpers so text-part rendering can stay focused on message display and future rich-copy reuse avoids conflicts in message-part.
Allow the markdown clipboard helper to accept queryable roots instead of ParentNode so shared UI consumers typecheck cleanly across package boundaries and CI environments.
|
I just closed #19035 which overlapped with this PR. Your approach is cleaner -- dedicated utility modules, tests, and the settings toggle is a better UX than the per-message dropdown I was building. Two things from my implementation that might be worth considering here: 1. Email-specific copy mode Your rich text copy preserves the rendered styles, which works well for Notion/GitHub/Docs. But when pasting into Gmail or Outlook, the app-specific CSS classes don't render. I built an "email sanitization" pass that strips all classes/styles and inlines email-safe CSS directly on elements: // Strip all classes/styles, then inline email-safe CSS
div.querySelectorAll("a").forEach(a =>
a.setAttribute("style", "color: #1a73e8; text-decoration: underline;"))
div.querySelectorAll("code").forEach(code => {
if (code.parentElement?.tagName !== "PRE")
code.setAttribute("style", "background: #f1f3f4; padding: 1px 4px; border-radius: 3px; font-family: monospace;")
})
// ... pre, blockquote, table, th/td similarly
// Wrap in: <div style="font-family: Arial, Helvetica, sans-serif; font-size: 14px;">This could be a third copy mode ("Copy for Email") or a variant of your rich text mode that detects the target. Either way, the inline styles are what make it render correctly in email clients. 2. DOMPurify before DOM parsing Your Happy to contribute either of these as a follow-up PR if this one lands, or as additions here if you'd prefer. |
Sanitize rich clipboard markup before serializing it and strip unsafe embedded media from copied assistant responses. Keep coverage for the clipboard serializer so the rich copy path stays safe in fallback environments too.
Issue for this PR
Closes #10693
Type of change
What does this PR do?
This fixes assistant response copy behavior for desktop so rich text paste targets (for example Apple Mail) no longer fall back to Times when copying from rendered markdown.
It adds explicit clipboard payloads (
text/plain+text/html) for markdown selection copy and assistant rich copy, includes inline fallback font/link/code styles in copied HTML, and introduces a desktop settings control for assistant copy mode (Plain text,Rich text,Ask each time) with rich text as the default for new settings. The settings row was also adjusted so the selector stays aligned on the right, and the copy button icon remains consistent across modes.How did you verify your code works?
bun test src/components/markdown-copy.test.tsinpackages/uibun test src/components/session-review-search.test.tsinpackages/uibun test src/i18n/parity.test.tsinpackages/appbun turbo typecheckpassed before pushScreenshots / recordings
Copy options in settings:

Message copy button dropdown in chat:

Checklist