herdr embeds `libghostty-vt` as a headless terminal emulator per pane,
and its readonly stream handler silently drops `.clipboard_contents`
actions. As a result, any child process that writes OSC 52 to copy to
the system clipboard (for example Amp, which enables mouse reporting
and handles selection internally) has its sequence swallowed before it
can reach the host terminal — the user sees the child's "copied" toast
but the system clipboard is never updated.
This change adds an `Osc52Forwarder` that observes raw PTY bytes in
`process_pty_bytes`, reconstructs completed `ESC ] 52 ; (c|) ; <base64>
(BEL|ST)` sequences (write/clear only — queries are dropped since herdr
has no path to reply to the child), and returns them through the
existing `ProcessBytesResult`. The reader task forwards the bytes to
the main loop via a new `AppEvent::ClipboardWrite`, which is written
to stdout from `handle_internal_event`. Routing through the main loop
keeps stdout writes serialized with `terminal.draw` and the other
existing host-terminal emitters, so the new forwarder does not race
with rendering.
Existing herdr-native selection (mouse drag in panes without mouse
reporting) is unchanged and still writes through `selection::write_osc52`.
The Down(Right) handler forwarded the click to the inner pane runtime
when mouse reporting was active, then early-returned before creating the
context menu state. For panes running a TUI that enables mouse reporting
(e.g. Claude Code), right-clicking focused the pane but never opened the
herdr context menu, even though right-click on plain shells or Codex
still worked.
Stop forwarding Down(Right) to the pane runtime altogether. Right-click
on a pane now always focuses it and opens the herdr context menu,
matching how terminal emulators like Ghostty, Alacritty, Kitty and iTerm2
treat right-click. This means the previous context menu actions
(close pane, split, fullscreen, etc.) now also reach the intended pane
via focus_pane before the menu is displayed.
Remove Up(Right) and Drag(Right) from the middle/right forward branch so
the inner TUI never sees an Up or Drag without a matching Down. Middle
button forwarding is preserved because it is still used for paste in
many terminal protocols.
Tighten the existing right-click regression test with assertions that
Mode::ContextMenu is entered and context_menu is populated. Without
these extra checks the earlier version of the test only verified focus
and failed to catch this exact bug.
The Down(Left) handler called forward_pane_mouse_button before focus_pane
and early-returned when the forward succeeded, which happens for any pane
running a TUI that has enabled mouse reporting (e.g. Claude Code). As a
result, clicking such a pane forwarded the click to the inner TUI but
never updated herdr's focus state, leaving the previous pane highlighted.
Reorder the handler to focus first and forward after, matching the
pattern already used by handle_terminal_wheel. Shells and TUIs without
mouse reporting (e.g. Codex) were unaffected because their forward
returned false and the focus update happened in the fallthrough branch.
Add a regression test that installs a pane runtime with mouse reporting
enabled via the '\x1b[?1002h' DECSET sequence and asserts that a
Down(Left) event retargets focus.