Skip to content

feat: emit osc 52 for pane copies - #42

Merged
backnotprop merged 2 commits into
mainfrom
feat/osc52-pane-copies
Sep 9, 2026
Merged

feat: emit osc 52 for pane copies#42
backnotprop merged 2 commits into
mainfrom
feat/osc52-pane-copies

Conversation

@backnotprop

Copy link
Copy Markdown
Contributor

Pane copies now also emit OSC 52, so a copy made inside a pane reaches the clipboard of the machine the person is sitting at when the plugin runs on a remote Herdr server (Herdr 0.9.0 forwards OSC 52 from pane output to the viewing client).

What changed

  • New src/pane-clipboard.ts and rust/src/pane_clipboard.rs: the OSC 52 encoder (ESC ] 52 ; c ; <base64> BEL over the raw UTF-8 bytes) and a wrapper that runs the native clipboard write first, then emits the sequence to the pane's terminal. Byte-identical across both runtimes, with byte-exact tests including empty and multi-byte text. Oversized payloads are emitted whole, never truncated.
  • The manager's y, c and Shift+C copies in both runtimes go through the wrapper. Semantics: a copy that reached either destination is a success, so on a server without a clipboard tool, copies and copy-and-archive still work through the client's terminal. A copy that reached neither fails with the native error, as before.
  • Parity harness: the PTY sessions now retain the raw byte stream; every manager copy case compares the emitted OSC 52 sequences across runtimes, and three new store.manager.osc52-remote-copy* cases run with the native writer failing, including an absolute assertion that Shift+C archives (3 sets, 0 active). A negative control (no-op emitter) produces 14 divergences, so the checks are not vacuous.
  • Docs: parity proof and README.

Not covered here: the global actions copy-context and copy-archive run with piped stdout and no terminal, so they cannot use OSC 52; that needs a Herdr plugin clipboard API (to be raised upstream).

Validation

  • bun test 80 pass; bunx tsc --noEmit clean; cargo fmt/clippy/test in rust/ clean, 64 + 4 pass
  • bash scripts/parity-lite.sh: 510 observables, 94 screens, zero divergences / 1 deliberate
  • Live on Herdr 0.9.0 in a disposable session with the server's PATH lacking pbcopy: y and Shift+C from the manager each delivered one OSC 52 sequence carrying the full Markdown to the client's terminal, the local native clipboard stayed untouched, and Shift+C archived the set and cleared the active list.

refs #40

Manager pane copies now write the OSC 52 clipboard sequence to the pane
terminal in addition to the native clipboard writer, so Herdr 0.9.0
forwards the copy to the viewing client when the plugin runs on a remote
server. When only the terminal copy lands, the manager stays open and
reports it instead of hiding the native failure.

The global copy-context and copy-archive actions run with piped stdout
and no terminal, so they are unchanged. The parity harness now retains
the raw PTY stream and compares the emitted sequences and their payloads
in both runtimes.

refs #40
A remote server commonly has no clipboard tool at all, so the native
write fails on every copy there. Reporting that as a failure defeated
the case the change is for: the OSC 52 copy had already reached the
person's clipboard, and copy-and-archive returned before its archive
step, so Shift+C never archived.

Either destination landing is now a successful copy, in both runtimes.
Only a copy that reached neither carries the native error, which a pane
with a live terminal cannot produce. Manager copy failure is therefore
unreachable, so the copy-failure steps are gone from the all-views
parity case and three osc52-remote-* cases replace them, asserting that
Shift+C still writes its archive and clears the active list when only
the terminal copy landed.

refs #40
@backnotprop
backnotprop merged commit 86e29ad into main Sep 9, 2026
4 checks passed
@backnotprop
backnotprop deleted the feat/osc52-pane-copies branch September 9, 2026 16:43
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.

1 participant