Skip to content

feat: support Claude Code fullscreen mouse mode#151

Merged
gbasin merged 11 commits into
masterfrom
feat/claude-fullscreen-mouse
Jun 25, 2026
Merged

feat: support Claude Code fullscreen mouse mode#151
gbasin merged 11 commits into
masterfrom
feat/claude-fullscreen-mouse

Conversation

@gbasin

@gbasin gbasin commented Jun 25, 2026

Copy link
Copy Markdown
Owner

Summary

  • launch new Claude Code panes with fullscreen/no-flicker mode enabled by default, with an env opt-out
  • forward fullscreen mouse interactions through Agentboard, including desktop wheel/click handling and iOS tap/long-press selection
  • bridge tmux/Claude clipboard offers to the browser with mobile-friendly copy prompts
  • fix grouped tmux attach desync and small-phone mobile toolbar wrapping
  • bump package version to 0.4.0 and document the new behavior in README

Validation

  • bun run lint && bun run typecheck && bun run test
  • production mobile smoke at 320x568 against isolated tmux/session data
  • live source-run cutover on local Agentboard instance using existing tmux session

gbasin added 11 commits June 25, 2026 09:58
Detect when a pane's app owns the mouse (alternate screen + mouse tracking)
and stop hijacking wheel/clicks into tmux copy-mode, so Claude /tui fullscreen
click-to-expand / click-to-position-cursor / wheel work in the browser terminal.
Per-window: classic and fullscreen agents can coexist. Covers desktop + iOS touch.

- server: report #{alternate_on}/#{mouse_any_flag} on tmux-copy-mode-status;
  launch agentboard-created windows with CLAUDE_CODE_NO_FLICKER=1 (tmux -e),
  gated by config.claudeNoFlicker (AGENTBOARD_CLAUDE_NO_FLICKER=0 to disable).
- client: appMouseRef gates setTmuxCopyMode + wheel + iOS touch-scroll; re-assert
  mouse tracking on false->true; 750ms poll keeps state fresh; iOS tap->SGR click.
@gbasin gbasin merged commit 1274fb4 into master Jun 25, 2026
5 checks passed
@gbasin gbasin deleted the feat/claude-fullscreen-mouse branch June 25, 2026 19:38
gbasin added a commit that referenced this pull request Jun 25, 2026
## What

On iOS, in Claude Code fullscreen/app-mouse mode, a clean tap forwarded
the SGR click to the app and returned early — so `focusTerminalInput()`
never ran and the on-screen keyboard didn't activate. This focuses the
xterm helper textarea on that tap path, keeping the focus tied to the
live `touchend` user gesture (which iOS Safari requires) without
double-sending a click to tmux/Claude.

Regression introduced by the app-mouse tap path added in #151.

## Changes

- `src/client/components/Terminal.tsx`: call `focusTerminalInput()`
before `preventDefault()` in the `sendTapClickToApp` success branch.
- `src/client/__tests__/useTerminal.test.tsx`: assert `focusCalls === 1`
on both the app-mouse tap path and the copy-mode tap path.

## Verification

`bun run lint && bun run typecheck && bun run test` — all green locally
(lint 0 errors, typecheck clean, 908 pass / 0 fail).

🤖 Generated with [Claude Code](https://claude.com/claude-code)
gbasin added a commit that referenced this pull request Jun 26, 2026
## What

Hardens the tmux paste-buffer → browser clipboard path (the relay that
serves agent copies to the browser, especially iOS Safari). Originated
from an audit of the clipboard machinery added in #151; findings were
verified with independent reviewers before and after each change.

## Changes

- **Deterministic delivery (#1):** pin tmux `set-clipboard on` so an
app's OSC 52 copy actually lands in a paste buffer the poll can read
(the modern `external` default forwards OSC 52 outward but stores no
buffer). Server-global, so it's **opt-out** via
`AGENTBOARD_TMUX_SET_CLIPBOARD=0` (documented in README).
- **No event-loop blocking (#2):** the 750ms buffer poll now uses async
`Bun.spawn` instead of `spawnSync`, with a kill-timer so a hung tmux
can't wedge the poll, and `stderr: 'ignore'` to avoid an undrained pipe.
- **No clipboard clobber (#3):** offers are gated on a created-time
correlation — only buffers created at/after the arming gesture are
offered, so a pre-existing/cross-window buffer can't overwrite the
clipboard. Arming covers all selection styles (drag,
double/triple-click).
- **Correct recency (#5):** same-second buffers tiebreak by tmux's
monotonic `buffer<N>` index, not size.
- **Concurrency safety:** a watch-generation token invalidates stale
async work across attach/re-attach, and freshness is re-validated after
each async tmux read before delivery.

## Review

Audited, then independently reviewed across three rounds (codex +
workflow reviewers). Round 1 caught generation races (fixed); round 2
confirmed them fixed and caught a slack edge (fixed); the residual is an
irreducible same-second + sub-attach-window case that fails safe. One
efficiency item (coalescing per-connection polls) is deferred as a
documented known-limitation.

## Verification

`bun run lint && bun run typecheck && bun run test` — green (lint 0,
typecheck clean, 912 pass / 0 fail), including 4 dedicated clipboard
tests.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
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