Repository navigation
feat: add rich-text selection copy with a separate shortcut - #2191
Merged
backnotprop merged 8 commits intoOct 26, 2026
Merged
Conversation
Co-authored-by: utpalsinghdev <utpalsinghdev@users.noreply.github.com>
The owner's call on backnotprop#2191: Copy as rich text stays as a selection-toolbar button (desktop and touch); everything else behaves as on main. - Remove the Mod+Alt+C shortcut (registry entry, toolbar handler, the bridge's rich-copy forwarding and message) and the matcher change in shortcuts/core.ts it needed. Cmd+Option+C is Chrome's Inspect Element. - Vim `y` copies plain text again (useVimSelection, the bridge's vim-copy message, HtmlViewer and the shortcut text reverted). - Ctrl+C / Cmd+C: the Viewer's copy handler is main's again; the extra keydown path is gone. The button's plain-HTTP fallback takes its own execCommand copy event (window capture, only during that call) so no page copy handler sees it. - No BRIDGE_PROTOCOL_VERSION bump: the selection message's html and clipboardText fields are optional; a malformed one is dropped without dropping the selection. - The button gets its own icon (the copy sheets with a T). - Tests: the button writes text/html + text/plain; Ctrl+C and Vim y make the same clipboard calls as main; no surface binds Alt+C; the sanitizer keeps no script, handler or unsafe link. Docs in guides-and-shared-ui.md. Co-authored-by: utpalsinghdev <utpalsinghdev@users.noreply.github.com>
…loopback Review fixes on backnotprop#2191: - The rich HTML is built only when Copy as rich text is pressed. The toolbar takes a builder (getRichCopy). In the Viewer it spans the pending highlight's marks; on HTML surfaces the parent asks the bridge (rich-copy-request) and the bridge builds the pending range's markup inside a try, answering rich-copy with html, text and tooLarge. The selection message only carries richCopy: true, so an older bridge shows no button. The asynchronous answer is handed to the clipboard as promise blobs inside the click, so the write starts in the gesture. - The sanitized HTML is capped at 512 KiB (the bridge still carries at most 256 KiB). Past a cap, plain text is copied and the button says "too large for rich text", not "unavailable". - Links to /api/html-assets/ lose their href on this origin and on every loopback spelling at any port (localhost, *.localhost, 127.0.0.0/8, [::1], 0.0.0.0, IPv4-mapped), judged after decoding (repeated), backslashes, dot segments, repeated slashes and case. Co-authored-by: utpalsinghdev <utpalsinghdev@users.noreply.github.com>
Second review of backnotprop#2191: - Links: every same-origin or loopback http(s) link loses its href and keeps its words (token routes that decode past the asset path, local document paths, live-app proxy links). [::7f00:1] counts as loopback. title attributes are dropped. - Safari / WKWebView: the promised text/html blob never rejects. With no usable markup (timeout, too large, none) it is the plain text, escaped, in a <pre>, so one write inside the gesture always copies something, and the tooltip still says plain text and why. - HTML surfaces: the bridge sends the text up to 256 KiB (cut and marked past it) even when the markup is too large; without an answer the quote is copied and, when the bridge cut it (textCut), the tooltip says "first N characters only". The tooltip names what was copied (richCopyOutcomeLabel), never "rich clipboard unavailable" for a timeout. - configurePlannotatorUI({ richCopy }) (utils/richCopyPolicy), default false: a @plannotator/ui host keeps its toolbar. Plannotator's plan review / annotate, code review and Inbox window turn it on. Documented in packages/ui/HANDOFF.md and the guide. - Tests: the 1.5 s timeout, a late answer, forged and wrong-id answers, one write for missing / too-large markup, sanitized HTML growing past 512 KiB, the cut text, and the host default off (htmlRichCopy.test.tsx, added to the DOM list). Co-authored-by: utpalsinghdev <utpalsinghdev@users.noreply.github.com>
… a fixed 25 ms The 300,000-character selection test missed its message under the full DOM run's load. Co-authored-by: utpalsinghdev <utpalsinghdev@users.noreply.github.com>
Owner feedback on backnotprop#2191: where richCopy is on, the toolbar's existing Copy button (same copy icon, tooltip "Copy") copies rich text, text/html plus text/plain in one write, and shows also where hideCopyButton hid it (the Viewer on desktop). The separate "Copy as rich text" button and its T icon are gone. "Copied!" after a rich copy; a plain-text fallback keeps its note. With richCopy off, Copy is exactly as before. Ctrl+C stays plain. Co-authored-by: utpalsinghdev <utpalsinghdev@users.noreply.github.com>
Co-authored-by: utpal singh <utpalsingh915@gmail.com>
backnotprop
marked this pull request as ready for review
October 26, 2026 18:41
This was referenced Oct 26, 2026
utpalsinghdev
pushed a commit
to utpalsinghdev/plannotator
that referenced
this pull request
Oct 26, 2026
The rich-copy answer built its text from Range.toString(), the raw source text: block line breaks were lost and source indentation came through. Plain text now comes from the selection's own text, as in 0.28.31, still capped at 256 KiB and marked when cut. Regression from backnotprop#2191.
1 task
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.
Summary
Adds a dedicated rich-text copy action for document selections while preserving regular Markdown copying.
Closes #1972.
Changes
text/htmlandtext/plaintogether, preserving headings, emphasis, links, nested lists, code, and tables.Keyboard shortcuts and copy actions
yin Vim modeThe rich-copy shortcut requires a captured selection and does not intercept typing in editable controls or run during IME composition. macOS Option+C is handled through its physical key when the keyboard layout produces a symbol.
Verification
bun run typecheckpassed.bun test scripts/dom-test-allowlist.test.tspassed.git diff --checkpassed.Full CI/e2e and Outlook/Slack/macOS paste checks remain unverified.