Skip to content

feat(ui): add copy format options -- Markdown and Email-ready HTML#19035

Open
andrewdunndev wants to merge 1 commit intoanomalyco:devfrom
andrewdunndev:feat/copy-format-options
Open

feat(ui): add copy format options -- Markdown and Email-ready HTML#19035
andrewdunndev wants to merge 1 commit intoanomalyco:devfrom
andrewdunndev:feat/copy-format-options

Conversation

@andrewdunndev
Copy link

Issue for this PR

Closes #14041
Relates to #10693

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

Replaces the single copy IconButton on assistant text parts with a DropdownMenu offering three copy modes:

Mode Clipboard format Use case
Copy response text/plain Current behavior, unchanged
Copy as Markdown text/html + text/plain via ClipboardItem Paste into Notion, Obsidian, GitHub with formatting preserved
Copy for Email text/html (inline styles) + text/plain Paste into Gmail, Outlook, Apple Mail with correct rendering

The email HTML sanitization strips all classes/styles from rendered HTML and inlines email-safe CSS on key elements (monospace code blocks, bordered tables, styled blockquotes, blue links, Arial 14px base font to match Gmail default).

Security measures:

  • All HTML sanitized via DOMPurify before DOM parsing or clipboard write, using the same config as Markdown.tsx
  • Media tags forbidden (img, video, audio, iframe) to prevent network loads from untrusted content
  • Safe anchor attributes enforced (rel="noopener noreferrer")
  • Centralized writeClipboard() helper with three-tier fallback: ClipboardItemwriteTexttextarea execCommand

Scope: packages/ui/src/components/message-part.tsx (copy handler + DropdownMenu) and packages/ui/src/i18n/en.ts (2 new keys: ui.message.copyMarkdown, ui.message.copyEmail). Uses the existing DropdownMenu component (Kobalte). No API changes.

i18n note: Two new keys added to en.ts only. Other locale files will need translations in a follow-up.

How did you verify your code works?

  • TypeScript type check passes: bun run --filter @opencode-ai/ui typecheck exits 0
  • Cross-model code review (GPT-5 Codex via OpenCode @review agent) identified 2 critical XSS issues in the initial implementation (unsanitized marked.parse output going to innerHTML and clipboard). Both fixed with DOMPurify sanitization before submission.
  • Manual review of DropdownMenu pattern against existing usage in session-review.tsx confirms consistency
  • ClipboardItem feature detection ensures graceful degradation on platforms without rich clipboard support (TUI via opentui, some Tauri builds)

Screenshots / recordings

No screenshots yet. The change replaces the single copy icon button with a dropdown menu that appears on click, showing three options. Visual appearance matches the existing DropdownMenu usage in session review.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

Replace the single copy IconButton on assistant text parts with a
DropdownMenu offering three copy modes:

- Copy response (existing behavior, plain text)
- Copy as Markdown (sanitized HTML + text/plain via ClipboardItem,
  so markdown-aware apps render formatting)
- Copy for Email (sanitized HTML with inline email-safe styles for
  Gmail, Outlook, Apple Mail)

Security:
- All HTML sanitized via DOMPurify before DOM parsing or clipboard
  write (matches Markdown.tsx config)
- Media tags forbidden (img, video, audio, iframe) to prevent
  network loads from untrusted content
- Safe anchor attributes enforced (rel=noopener noreferrer)
- Centralized writeClipboard() with ClipboardItem -> writeText ->
  textarea fallback chain for cross-platform compatibility

Uses the existing DropdownMenu component (Kobalte). Scoped to
packages/ui/src/components/message-part.tsx and en.ts i18n strings.

Closes anomalyco#14041
Relates to anomalyco#10693
@github-actions
Copy link
Contributor

The following comment was made by an LLM, it may be inaccurate:

Found one potentially related PR:

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE]: Copy message as raw markdown

1 participant