Skip to content

fix(clipboard): salvage #16020 with native/tmux success signal + xterm keybinding#16035

Merged
teknium1 merged 4 commits into
mainfrom
hermes/hermes-44a5c20e
Apr 26, 2026
Merged

fix(clipboard): salvage #16020 with native/tmux success signal + xterm keybinding#16035
teknium1 merged 4 commits into
mainfrom
hermes/hermes-44a5c20e

Conversation

@teknium1

Copy link
Copy Markdown
Contributor

Salvages #16020 onto current main with three corrections. Replaces "copied N characters" false-positive with a truth signal that counts native-tool and tmux-buffer paths, restores the xterm Ctrl+Shift+C convention in the dashboard, and points users at HERMES_TUI_FORCE_OSC52 in the failure toast.

Summary

  1. Truth signal for /copy
    setClipboard() now returns { sequence, success }. success is true when any clipboard path was attempted: native tool fired (pbcopy/wl-copy/xclip/xsel/clip.exe), tmux buffer loaded, or an OSC 52 sequence was written. /copy and the contributor's "honest feedback" toast now only report failure when literally no path was taken (e.g. headless Linux without tmux). Before this fix, local Linux + tmux users saw "clipboard copy failed" even when xclip had already succeeded fire-and-forget.

  2. Dashboard keybinding reverted to Ctrl+Shift+C on non-Mac
    Bare Ctrl+C in a terminal emulator must stay SIGINT — matches xterm, gnome-terminal, konsole, Windows Terminal. The contributor's real fix (direct navigator.clipboard.writeText() inside the keydown handler to preserve user gesture) is kept. term.write("\x1b") replaced with term.clearSelection() so highlight clears without relying on TUI input mode.

  3. Error toast points at the fix env var, not just the debug var
    Toast now reads: "clipboard copy failed — try HERMES_TUI_FORCE_OSC52=1 to force the escape sequence; HERMES_TUI_DEBUG_CLIPBOARD=1 for details".

Also: TypeScript interfaces for copySelection/copySelectionNoClear updated to Promise<string> across hermes-ink.d.ts, interfaces.ts, use-selection.ts, and the createSlashHandler.test.ts mock (the contributor made ink.tsx async but missed the declared types — CI typecheck was failing).

Changes

File Change
ui-tui/packages/hermes-ink/src/ink/termio/osc.ts setClipboard returns {sequence, success}; copyNative returns boolean; HERMES_TUI_FORCE_OSC52 env var
ui-tui/packages/hermes-ink/src/ink/termio/osc.test.ts +2 tests for HERMES_TUI_FORCE_OSC52 precedence
ui-tui/packages/hermes-ink/src/ink/ink.tsx Use new {sequence, success} shape; new debug message
ui-tui/packages/hermes-ink/src/ink/hooks/use-selection.ts copySelection/copySelectionNoClear typed as Promise<string>
ui-tui/src/app/interfaces.ts SelectionApi.copySelection: Promise<string>
ui-tui/src/types/hermes-ink.d.ts same
ui-tui/src/app/slash/commands/core.ts Error toast points at HERMES_TUI_FORCE_OSC52
ui-tui/src/__tests__/createSlashHandler.test.ts Mock returns Promise<string>
web/src/pages/ChatPage.tsx Direct writeText in keydown (contributor's fix); keybinding reverted to Ctrl+Shift+C; term.clearSelection()

Credits

Closes #16019.
Closes #16020.

Validation

Before After
TUI local Linux + tmux + xclip present /copy → "copied N characters" but clipboard empty (PR #16020: "clipboard copy failed" false-negative) /copy → "copied N characters", xclip fires, paste works
TUI headless (no DISPLAY, no tmux) "copied N characters" but clipboard empty "clipboard copy failed — try HERMES_TUI_FORCE_OSC52=1..."
TUI SSH OSC 52 emitted, toast says "copied N" Same (unchanged)
Dashboard Ctrl+C with selection (non-Mac) (nothing — Ctrl+Shift+C was the modifier) Same — Ctrl+C still SIGINT (xterm convention)
Dashboard Ctrl+Shift+C with selection (non-Mac) Sometimes fails with "Document is not focused" Direct writeText in keydown preserves user gesture
Dashboard Cmd+C with selection (Mac) Worked on main Same
Typecheck PR #16020 branch: TS2345 on use-selection.ts Clean (npm run type-check passes)
osc.test.ts 3/3 passing 5/5 passing (+ 2 new tests for HERMES_TUI_FORCE_OSC52)
Full ui-tui test suite 224 passing on main/branch 224 passing on this PR (6 pre-existing failures in terminalSetup.test.ts / unrelated .test.ts import errors — same on origin/main)
npm run build Succeeds Succeeds
npm run lint Clean Clean

0xharryriddle and others added 4 commits April 26, 2026 05:38
…etection

Problem: Ctrl+C in Hermes TUI shows 'copied' but clipboard often empty.
Root causes:
- Native Linux tools (xclip, wl-copy) require DISPLAY/WAYLAND_DISPLAY; in
  headless Docker/SSH they fail or hang.
- OSC 52 fallback requires terminal emulator support; when absent, sequence
  is dropped silently.
- Dashboard OSC 52 → Clipboard API path fails due to missing user gesture;
  errors were silently caught.
- User feedback 'copied selection' was shown unconditionally, regardless of
  success.

Solution implemented:
- Short-circuit Linux native clipboard probing when no display server is
  present (no DISPLAY and no WAYLAND_DISPLAY). Avoids futile attempts and
  timeouts.
- Add HERMES_TUI_DEBUG_CLIPBOARD env var (1/true). When set, TUI logs to
  stderr which clipboard path is used, probe results on Linux, and whether
  OSC 52 was emitted. Greatly improves diagnosability.
- Improve dashboard clipboard error handling: replace empty catch blocks
  with console.warn messages for OSC 52 decode/Write failures and direct
  copy/paste errors. Makes browser permission/user-gesture failures visible
  in DevTools.
- Add comprehensive clipboard troubleshooting documentation to README and
  AGENTS, covering OSC 52 verification, tmux config, Docker/headless
  constraints, env vars, dashboard caveats, and fallback strategies.

Technical details:
-  in ui-tui/packages/hermes-ink/src/ink/termio/osc.ts:
  - Early return on Linux if both DISPLAY and WAYLAND_DISPLAY unset.
  - Refactor probe sequence to async  with 500ms timeout,
    caching result; subsequent copies use cached tool immediately.
  - Emit debug logs when HERMES_TUI_DEBUG_CLIPBOARD=1.
-  in ink.tsx: log when OSC 52 not emitted (native
  or tmux path in use) in debug mode.
- : OSC 52 handler and Ctrl+Shift+C handler now
  log warnings to console on Clipboard API rejection with error message.
- Documentation: new 'Clipboard Troubleshooting' section in README; new
  'Clipboard environment variables and pitfalls' subsection in AGENTS.md
  (Known Pitfalls).

Tests: full ui-tui test suite (292 tests) passes; clipboard and OSC tests
unaffected. No breaking changes.

Files changed:
- ui-tui/packages/hermes-ink/src/ink/termio/osc.ts
- ui-tui/packages/hermes-ink/src/ink/ink.tsx
- web/src/pages/ChatPage.tsx
- README.md
- AGENTS.md
- CHANGELOG.md (new)
…RMES_TUI_FORCE_OSC52

- Dashboard copy: direct Clipboard API on Ctrl+C/Cmd+C (user gesture);
  send Escape to TUI to clear selection; Ctrl+Shift+C kept as fallback.
- TUI /copy: copySelection() async; only reports success if OSC52 emitted.
- Add HERMES_TUI_FORCE_OSC52 env var to override native-tool detection.
- Fixes "copied N chars" false-positive when clipboard backend absent.

Changes:
  web/src/pages/ChatPage.tsx — direct navigator.clipboard.writeText
  ui-tui/packages/hermes-ink/src/ink/ink.tsx — async copySelection
  ui-tui/packages/hermes-ink/src/ink/termio/osc.ts — HERMES_TUI_FORCE_OSC52
  ui-tui/src/app/slash/commands/core.ts — async /copy with honest feedback
…board

Follow-up on #16020 salvage. Three corrections:

1. Truth signal for /copy
   Before: success was 'OSC 52 sequence was emitted to stdout'. That's
   false on local Linux inside tmux (emitSequence=false), so /copy kept
   printing 'clipboard copy failed' to users whose xclip/wl-copy had
   already succeeded fire-and-forget.
   Fix: setClipboard() now returns { sequence, success } where success =
   native-fired OR tmux-buffer-loaded OR osc52-emitted. copyNative()
   returns a boolean telling setClipboard whether a native attempt was
   made. /copy only shows 'failed' when literally no path was taken.

2. Dashboard keybinding
   Before: Ctrl+C for copy on non-Mac (Ctrl+Shift+C for paste).
   That swallows SIGINT when a stale selection is present and breaks
   the xterm/gnome-terminal/konsole/Windows-Terminal convention where
   Ctrl+C in a terminal emulator is always SIGINT. The real bug was
   that clipboard writes lost user-gesture through OSC-52 round-trips,
   which the direct writeText already fixes.
   Fix: revert copyModifier to Ctrl+Shift+C on non-Mac. Direct
   writeText in the keydown handler preserves user gesture. term.write
   Escape replaced with term.clearSelection() (works without relying
   on TUI input mode).

3. Error toast text
   Before: 'see HERMES_TUI_DEBUG_CLIPBOARD' — tells users how to
   debug but not how to fix.
   Fix: point users at HERMES_TUI_FORCE_OSC52=1 first (the actual
   escape hatch), mention the debug var second.
@teknium1 teknium1 merged commit e8441c4 into main Apr 26, 2026
9 of 10 checks passed
@teknium1 teknium1 deleted the hermes/hermes-44a5c20e branch April 26, 2026 12:46
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/tui Terminal UI (ui-tui/ + tui_gateway/) comp/cli CLI entry point, hermes_cli/, setup wizard labels Apr 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/cli CLI entry point, hermes_cli/, setup wizard comp/tui Terminal UI (ui-tui/ + tui_gateway/) P2 Medium — degraded but workaround exists type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Clipboard copy shows "copied N chars" but nothing is actually copied

3 participants