Skip to content

fix(messages): exclude row chrome from cross-message copies - #645

Merged
wesbillman merged 3 commits into
mainfrom
copy-gets-thumbs-up
Oct 7, 2026
Merged

wesbillman merged 3 commits into
mainfrom
copy-gets-thumbs-up

Conversation

@matt2e

@matt2e matt2e commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Copying across messages now excludes quick-reaction glyphs, hover-only timestamps, and screen-reader-only metadata while preserving visible bylines and message bodies. Native quick reactions render through CSS content to keep glyphs out of DOM text selections.

Adds a colocated selection regression test and one browser case (none removed) for real-engine selection behavior that jsdom cannot verify.

Validation: diff whitespace check passed at f2c88f8; local and origin branch heads match. Test execution, fail-then-pass evidence, Chromium/WebKit checks, agent review, and human app verification remain unconfirmed; keeping this PR in draft.

Browser copy/paste comparison — Before 27d581e5 (parent of 43c33924) vs After f2c88f8e. The same synthetic messages are rendered by each revision’s production frontend in Chromium 153. Both sides visibly drag-select across messages, copy with ⌘C, and paste with ⌘V into an empty plain-text area. Before leaks 👍 ❤️ 😂, hidden dates and repeated bylines; After keeps the visible author/time and message bodies. Steps are synchronized and pasted text is unmodified. Expand the player for readability.

cross-message-copy-before-after.mp4

Capture verification: both native clipboard/paste checks passed, and selection = clipboard = pasted text on each revision. This adds Chromium recording evidence; WebKit/native checks, review and human verification are not attested by this recording.

@matt2e
matt2e marked this pull request as ready for review October 6, 2026 07:50
@matt2e
matt2e requested review from a team, comp615 and wesbillman as code owners October 6, 2026 07:50

@wesbillman wesbillman 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.

One change needed: make the custom-emoji clipboard path honor the new row-chrome exclusions (inline P2).

Star Lord’s automated source review via Wes’s account. Reviewed head f2c88f8ebd5c0e2d0bf4d6be1356889dbc665012 against base 27d581e5ce0dc5bd6eb6cd09f6dd0c22545f4306. Source-only: no tests or app execution; hosted checks snapshot was successful except skipped Windows native validation. The attached recording was inspected through six sampled frames, not independently reproduced or exhaustively inspected.

margin-inline-start: auto;
/* Controls are not conversation text: a selection that spans rows passes
through this bar, and engines copy button labels the user never saw. */
user-select: none;

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.

[P2] Apply these exclusions to custom-emoji clipboard serialization too

A cross-message selection containing a rendered custom emoji still copies the hidden dates, continuation bylines and hover timestamps. The bundled Emoji plugin registers copyEmoji on document (src/bundled/emoji/index.tsx:36–39); that handler clones the entire DOM range, replaces img[data-copy-emoji] with shortcodes, and writes selectedText(fragment) to the clipboard (copy-emoji.ts:17–37). Its text walker ignores user-select: none, so these CSS-only exclusions are bypassed. A custom quick-reaction image inside an intervening action bar can activate the same path even when the message bodies contain no emoji.

Make the existing serializer omit the marked non-copyable chrome while retaining body shortcodes, and add a cross-row regression that observes the copy-event payload with a custom emoji present. The new Selection.toString() assertion alone never invokes this handler.

matt2e and others added 3 commits October 7, 2026 13:07
Copying a selection that spanned messages included text the reader never
saw: the hover action bar's quick-reaction glyphs (👍 ❤️ 😂), each
continuation's hover-only clock, and the screen-reader-only full dates
and bylines. Chromium and WebKit copy Selection.toString(), which includes
button labels, opacity-0 text and visually hidden text.

Mark the action bar, the continuation clock and the rows' sr-only spans
user-select: none, which both engines honour in Selection.toString() and
the copy payload. Render native quick-reaction glyphs as CSS content so no
text node exists for selections or DOM-walking copy handlers to read.

A jsdom test spans two real rows and asserts the glyphs are absent from
the selection; a Playwright case proves in both engines that a selection
across rows yields only the visible byline and bodies.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Matt Toohey <contact@matttoohey.com>
Remove the inline comment above the quick-reaction glyph as requested in review. Rendering and copy behavior remain unchanged.

Signed-off-by: Matt Toohey <contact@matttoohey.com>
A selection spanning messages copied cleanly unless it contained a custom
emoji image. The copy handler then re-added the row chrome 43c3392
excluded with user-select: none: hidden full dates, continuation bylines
and hover clocks, and the shortcode of a custom quick reaction in a
hovered action bar.

Skip any subtree whose computed user-select is none in the shared
selection serializer, which message surfaces and the emoji plugin both
use. The emoji plugin now checks the live range for a selectable custom
emoji, since detached clones have no computed style, so a custom emoji
that sits only in excluded chrome leaves the engine's own copy in place.

New jsdom tests cover cross-row exclusion, excluded-only emoji, boundary
slicing and break/block/table separators. A Playwright case copies across
two real rows in Chromium and WebKit and asserts the hidden full dates
are absent; it fails without the serializer check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Matt Toohey <contact@matttoohey.com>
@matt2e
matt2e force-pushed the copy-gets-thumbs-up branch from f2c88f8 to 6915020 Compare October 7, 2026 05:52

@wesbillman wesbillman 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.

No further changes requested: the prior clipboard-exclusion finding is addressed by the shared live-DOM serializer, with copy-event regression coverage added.

Star Lord’s automated source review via Wes’s account; head 691502082a0db177c4dd80c71193748b6fabd4fe, base 5aeeda7ecd723eef84212c4578611f59412d6af8. Source-only: tests, app/browser behavior and CI were not validated. Attachment inspection covered six cached sample frames of the old-head recording, not the full video/audio or current-head runtime behavior.

@wesbillman
wesbillman merged commit d1eabb7 into main Oct 7, 2026
22 checks passed
@wesbillman
wesbillman deleted the copy-gets-thumbs-up branch October 7, 2026 15:21
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.

2 participants