Repository navigation
fix(box): links a program draws as text, like "Authorize with Codex", open with ⌘-click - #49
Merged
Merged
Conversation
… open with ⌘-click berthd's tmux dropped OSC 8 hyperlinks on the way to the app: the app attaches as xterm-256color, tmux's terminal-features don't give xterm the hyperlinks feature, and ghostty-web doesn't answer the DA/XTVERSION queries tmux could learn it from. tmux 3.4+ now gets terminal-features[90] "xterm*:hyperlinks" (guarded by version, so older tmux skips it). In the app, a link's text is remembered in the pieces tmux draws it in, and adjacent runs with the same address are one link. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What was broken
In a box's terminal, fx's "Sign in with Codex" screen shows
Open Authorize with Codex, where "Authorize with Codex" is an OSC 8 hyperlink. It showed underlined, but ⌘-click opened nothing. Plain-text addresses already worked after #48.Cause, from the bytes
I captured on
devl(tmux 3.6, fx 0.0.13, not signed in) with a separate tmux server running fx under a copy of berthd's tmux.conf, then recorded what a client attached the way berthd attaches the app (tmux -u … attach,TERM=xterm-256color) receives.capture-pane -eshowsESC[4m ESC]8;id=fx-codex-auth;https://auth.openai.com/oauth/authorize?…&originator=fx ESC\ Authorize with Codex ESC[0m ESC]8;; ESC\ESC]8;and justOpen ESC[4mAuthorize with Codex ESC(B ESC[m ESC[K: the underline, with no address.terminal-featuresincludingxterm*:hyperlinks, the same attach gets the link whole:ESC[4m ESC]8;id=tmux3;https://auth.openai.com/oauth/authorize?… ESC\ Authorize with Codex ESC]8;; ESC\ ESC(B ESC[m ESC[Ktmux list-clientson berthd's server shows the app's real client for the fx session asxterm-256color features=bpaste,ccolour,clipboard,cstyle,focus,title, withouthyperlinks.ESC[c,ESC[>candESC[>q(XTVERSION), which it sends on attach. ghostty-web 0.4 answers none of them (it answers DSRESC[6n/ESC[5nonly; checked against its WASM in node).So the app's OSC 8 tracker from #48 never saw an address to remember. Two smaller things also showed up. ghostty-web 0.4's
hyperlink_idis 1 for every link cell, so it marks a cell as part of some link and doesn't tell links apart. And tmux redraws a link in pieces when only some of its cells change, so the text it sends with the link can be a piece, not the whole label.Fix
box: berthd's tmux.conf adds
set -s terminal-features[90] "xterm*:hyperlinks"inside%if "#{m/r:^(next-|[4-9]|3\.([4-9]|[1-9][0-9])),#{version}}". Hyperlinks came in tmux 3.4. I checked the guard in Docker:terminal-featuresoption and skips the line without a config error.The fixed index keeps it to one entry when berthd re-sources the file into a running server.
app, hardening:
HyperlinkTrackernow remembers a link's text in the pieces it was drawn in: a new piece starts when the cursor moves or the line breaks while the link is on.uriForalso matches text made up of a link's pieces (inPieces). The OSC 8 provider merges a run into the one before it when they touch and share an address. Hovering any word of "Authorize with Codex" gives the whole label as one link, and ⌘-click opens the full address.Tests
TestAttachPassesOSC8Hyperlinksattaches to a session that prints an OSC 8 link and expects it in the client's bytes. It fails without the config change, and skips on tmux older than 3.4.term-links.test.ts): fx's own bytes and tmux's attach bytes as captured, with a made-up address. Any word of the label gives the address; the SGR-only bytes berthd's tmux used to send give none. A link drawn in pieces with cursor moves, re-opened per piece, or across a line break is found; text only partly made of a link's pieces is not.terminal-links.spec.ts): replays the attach-shaped screen and an "in pieces" screen (each piece its own tmux hyperlink, second piece first). Hovering at "Authorize", "with" or "Codex" gives the whole label as the link range, ⌘-click opens the full address, and "Open" doesn't. The "in pieces" case fails on main's app code.Checks
go vet ./...,go test -race ./internal/box/pnpm typecheck:plugins && pnpm check:titles && pnpm check:csp && pnpm check:themes && pnpm test && pnpm build,npx tsc -p e2eE2E_PORT=1426 npx playwright test e2e/terminal-links.spec.ts e2e/home-terminal.spec.ts: 10 passed. The new tests also passed with--repeat-each=5.docs-sitebuild (changelog)Boxes need the new berthd. On start it re-sources tmux.conf into the running server, so the next attach (reopen the terminal or reconnect) gets links; already-attached clients keep their old features. Boxes on tmux older than 3.4 can't pass OSC 8 at all.
🤖 Generated with Claude Code