Conversation
Text and other non-file clips copied on a Linux (Wayland or X11) peer never reached the macOS pasteboard. The Linux side sends X11 selection-atom names (STRING, TEXT, UTF8_STRING) and raw MIME types (text/plain, text/html), and the NSPasteboard backend handed them to AppKit verbatim as UTIs. AppKit rejects every one, the pasteboard ends up empty, and the daemon logs "native clipboard was empty immediately after publishing". The reverse direction already works because macOS emits public.* UTIs and wl-copy/X11 accept arbitrary names. Normalize representation type names on the receive/apply side: - publish_format() maps known atom/MIME names to their UTI equivalents for the NSPasteboard backend, passes already-valid reverse-DNS UTIs through unchanged, and drops names AppKit cannot represent (rather than logging an invalid-UTI error for each). Non-macOS backends (wl-copy, X11) still receive every name verbatim. - generic_publish_entries() applies the mapping across a clip and collapses the several names that normalize to one UTI into a single pasteboard entry. When they collide it keeps the bytes from the most UTF-8-authoritative source, so non-ASCII text from an X11 app whose STRING target is Latin-1 is not published as mislabeled UTF-8. - A text/uri-list that resolves to no local files is still published verbatim on the non-macOS backends; it is only suppressed when a native file list was published in its place. Also fixes an incidental bug: the old apply loop published a second file-format representation (e.g. text/uri-list alongside public.file-url) raw, adding a stale remote-path target next to the correct Files-derived one. Capture/send and the wire format are untouched. Known limitation, left for a follow-up: the mapping is byte-preserving by design, so a source that offers only a legacy-encoded flavor (X11 STRING as Latin-1, or a UTF-16 text/html) still lands those bytes under a UTF-8 UTI. Fixes standardagents#8 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qd7JneqSJWFAbRHauiHDbR
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.
Problem
Fixes #8. Copying on a Linux (Wayland or X11) peer and pasting on macOS does nothing. The macOS daemon receives the clip but fails to apply it: the Linux side's format names — X11 selection atoms (
STRING,TEXT,UTF8_STRING) and raw MIME types (text/plain,text/plain;charset=utf-8) — are handed toNSPasteboardverbatim as UTIs. Modern AppKit rejects each one ('STRING' is not a valid UTI string), the pasteboard ends up empty, and the daemon logsnative clipboard was empty immediately after publishing.macOS → Linux already works, because macOS emits
public.*UTIs andwl-copy/ X11 accept arbitrary MIME and atom strings.Fix
Receive-side normalization in
src/clipboard.rs, applied when publishing to the native clipboard:publish_format(name, target)— for theNSPasteboardbackend, maps known X11 atom / MIME names to their UTI equivalents (STRING/TEXT/UTF8_STRING/text/plain*→public.utf8-plain-text,text/html→public.html,image/png→public.png, …), passes strings that are already valid reverse-DNS UTIs through unchanged, and drops names it cannot represent (rather than letting AppKit log an invalid-UTI error for each). Non-macOS backends (wl-copy, X11) still receive every name verbatim — their current behaviour is correct.generic_publish_entries()— runs the mapping across a clip's non-file representations and collapses the several names that normalize to one UTI into a single pasteboard entry. On a collision it keeps the bytes from the most UTF-8-authoritative source (public.utf8-plain-text>UTF8_STRING/…;charset=utf-8>text/plain>TEXT>STRING), so non-ASCII text copied from an X11 app — whoseSTRINGtarget is Latin-1 under ICCCM — is not published as mislabeled UTF-8.text/uri-listthat resolves to no local files is still published verbatim on the non-macOS backends; it is only suppressed when a native file list was published in its place.Incidental fix along the way: the old apply loop published a second file-format representation (e.g.
text/uri-listalongsidepublic.file-url) raw, adding a stale remote-path target next to the correctFiles-derived one.Scope / non-goals
STRINGas Latin-1, or a UTF-16text/htmlfrom Firefox on Linux) still lands those bytes under a UTF-8 UTI. Charset transcoding would be a separate change.Testing
src/clipboard.rscovering the mapping table (case-insensitive), UTI pass-through, the drop set, the Passthrough invariant, duplicate collapse with byte-level tie-break, and the uri-list passthrough gate. These run on both CI OS legs since the mapper is notcfg-gated.tests/headless_x11.rsextended with an arbitrary-MIME-name round-trip assertion (Linux passthrough regression guard).pbpasteverification on a real Linux + macOS peer pair — to be added to this PR before it's ready for review.🤖 Generated with Claude Code