[BUG] Fullscreen TUI: clicks on OSC 8 status line links are dropped in some sessions (no tmux)
Summary
In "tui": "fullscreen", clicking an OSC 8 hyperlink in a custom status line opens the link only some of the time. When it fails, nothing happens: no browser/app opens, no error. Both http:// and vscode:// links are affected.
Not tmux-related (unlike #23438 / #27047 / #37216 / #45173). No tmux, no multiplexer.
Environment
- Claude Code 2.1.288 (Homebrew cask), macOS (Darwin 27.0.0)
- Ghostty 1.3.1,
TERM=xterm-ghostty,TERM_PROGRAM=ghostty settings.json:"tui": "fullscreen",statusLine.type = "command"
Repro
Status line script:
#!/bin/sh
printf 'pre \033]8;;https://example.com/aaa\033\\AAAA\033]8;;\033\\ mid \033]8;;vscode://file/tmp/bbb\033\\BBBB\033]8;;\033\\ end'
{ "tui": "fullscreen", "statusLine": { "type": "command", "command": "/path/to/sl.sh" } }
Start claude, click AAAA / BBBB repeatedly over a few sessions. Some sessions open every click; others open none.
Reproduced without a real terminal
To rule out Ghostty and the status line script, I drove claude in a Python PTY (pyte screen, 140×40), with an open shim on PATH that logs its args, and sent SGR mouse press/release (CSI < 0;x;y M / m) on the link cells. Fullscreen enables ?1000/1002/1003/1006, so Claude Code (not the terminal) owns the click. It re-emits the links as OSC 8 ;id=<rand>;<url>.
With identical input and the same binary, results were bimodal per session:
| Run batch | Clicks opened |
|---|---|
| Batch A (several sessions) | 18/18, 12/12 (plain, Shift, Alt, Ctrl, 150 ms hold) |
| Batch B (same harness, minutes later) | 0/6 × 5 sessions, 0/60 over 100 s in one session |
| Clean env (all CLAUDE* vars stripped) | 0/45 |
Varying XTVERSION reply (none / DA-only / ghostty 1.3.1), click hold time, modifiers, startup wait (6–40 s), and focus-in timing did not reliably change the outcome. Within one session the result is consistent: either every click opens or none does. That suggests something decided per session or at mount, rather than per-click timing.
--debug-file logs nothing on click, so it's not possible to see where the click is dropped. Probe output is fine: XTVERSION: terminal identified as "ghostty 1.3.1", fullscreen boot canary: healthy.
Leads from the 2.1.288 bundle (unconfirmed)
Ink.onHyperlinkClickis a single slot that two components write: the REPL (TR()) and the fleet/agent view (q.onHyperlinkClick=(Re)=>{GOt(Re)}). Each cleanup sets it toundefinedwithout checking it still owns it. If one unmounts after the other mounts, link clicks are dead for the rest of the session. This matches the per-session all-or-nothing behavior.- The REPL registers the handler only
if (wc()), wherewc() = ET() == null && !pWe().pWe()is true for an inheritedCLAUDE_CODE_CHILD_SESSIONmarker.ET()readsdynamicTeamContext?.agentId, which can change at runtime. A session that starts as (or becomes) a child or team session gets no link handler, with no message. - Separate from the per-session failure: the click gate drops a press within 400 ms of focus-in (
pressIsWindowActivation). The open is delayed 500 ms (r7t), and a second press within 1 cell cancels it. So the first click on an unfocused window never opens a link, and a quick re-click cancels the first. These may be intentional, but neither gives feedback.
Expected
A click on a status line hyperlink in fullscreen opens it every time, or a debug log line says why it was dropped (no handler registered, window-activation click, stray, cancelled by multi-click).