feat(web): open terminal dev server links without asking - #156
Merged
Conversation
Cmd-clicking a loopback link in the terminal popped a two-item menu — open in preview, or open in the browser — every single time. Activating the link is already a deliberate gesture, so the extra click bought nothing on the one destination that can actually reach the port. Loopback links now open straight in the integrated browser, and everything else still goes straight to the system browser. The preview chrome already carries an open-in-system-browser button, so the way out survives, and a failed open still falls back to the system browser. This drops the menu, and with it TerminalLinkContextMenuShowError and the localApi and position arguments the menu needed.
incognitojam
force-pushed
the
t3code/terminal-loopback-links
branch
from
August 15, 2026 22:36
188915c to
4cc31f3
Compare
yngatech-nightly Bot
pushed a commit
that referenced
this pull request
Aug 18, 2026
> [!NOTE] > TL;DR: Cmd-clicking an `http://localhost:5173` link in the terminal now opens the integrated browser directly instead of popping a two-item chooser first. Non-loopback links are unchanged. Follow-up to #153, but independent of it. ## The problem Activating a loopback link in the terminal showed a menu at the cursor — "Open in preview" / "Open in browser" — on every single click. Activation is already a deliberate modifier gesture (cmd-click on macOS, ctrl-click elsewhere), so the chooser added a second click to every visit to a dev server you are iterating on, for a decision that is nearly always the same one. Non-loopback URLs never showed the menu; they already went straight to the system browser. So the menu was the only place in the app where following a link asked a question first. ## What changed `openTerminalLinkInPreview` opens loopback links in the integrated browser and hands everything else to the system browser, with no menu in between. That deletes the menu plumbing: `TerminalLinkContextMenuShowError`, and the `localApi` and `position` arguments that only existed to position and show it — 107 lines out, 63 in. The loopback test, the failure fallback, the interrupt handling, and the origin-only error logging (so a link carrying a token in its query is not logged) are all unchanged. ## Reverse state The way back out is the integrated browser's own open-in-system-browser button in `PreviewChromeRow`, which is one click and already there. I deliberately did not invent a modifier for "system browser instead": cmd/ctrl is already spent on activation itself, and a shift variant would be undiscoverable. If it turns out people want the choice back, a setting is the honest shape for it, not a menu on every click. ## Relationship to #153 Same reasoning applied to a different surface, and the two together make the rule uniform: a loopback link opens in the integrated browser because it is the only browser that can reach the environment's ports; every other host opens in the system browser, where the reader is signed in. They touch disjoint files and can land in either order, so this is a separate PR rather than a stacked one. ## Surfaces - **Desktop:** new behavior. - **Web in a plain browser:** unchanged. `isPreviewSupportedInRuntime()` is false, so links fall through to the system browser exactly as before. - **Mobile:** no terminal drawer, nothing to change. ## Verification - `vp test run apps/web/src/components/preview/openTerminalLinkInPreview.test.ts` - `vp run @t3tools/web#typecheck` - `vp check` on the three changed files ## Checklist - [x] This PR is focused on removing one chooser from one gesture - [x] I explained what changed and why - [ ] No before/after image: the terminal link looks identical, and the visible difference is the absence of a menu. Verified manually. - [x] No video is needed because this change adds no motion or timing behavior --- Written by an agent (Claude Code, claude-opus-5).
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.
Note
TL;DR: Cmd-clicking an
http://localhost:5173link in the terminal now opens the integrated browser directly instead of popping a two-item chooser first. Non-loopback links are unchanged. Follow-up to #153, but independent of it.The problem
Activating a loopback link in the terminal showed a menu at the cursor — "Open in preview" / "Open in browser" — on every single click. Activation is already a deliberate modifier gesture (cmd-click on macOS, ctrl-click elsewhere), so the chooser added a second click to every visit to a dev server you are iterating on, for a decision that is nearly always the same one.
Non-loopback URLs never showed the menu; they already went straight to the system browser. So the menu was the only place in the app where following a link asked a question first.
What changed
openTerminalLinkInPreviewopens loopback links in the integrated browser and hands everything else to the system browser, with no menu in between. That deletes the menu plumbing:TerminalLinkContextMenuShowError, and thelocalApiandpositionarguments that only existed to position and show it — 107 lines out, 63 in.The loopback test, the failure fallback, the interrupt handling, and the origin-only error logging (so a link carrying a token in its query is not logged) are all unchanged.
Reverse state
The way back out is the integrated browser's own open-in-system-browser button in
PreviewChromeRow, which is one click and already there. I deliberately did not invent a modifier for "system browser instead": cmd/ctrl is already spent on activation itself, and a shift variant would be undiscoverable. If it turns out people want the choice back, a setting is the honest shape for it, not a menu on every click.Relationship to #153
Same reasoning applied to a different surface, and the two together make the rule uniform: a loopback link opens in the integrated browser because it is the only browser that can reach the environment's ports; every other host opens in the system browser, where the reader is signed in.
They touch disjoint files and can land in either order, so this is a separate PR rather than a stacked one.
Surfaces
isPreviewSupportedInRuntime()is false, so links fall through to the system browser exactly as before.Verification
vp test run apps/web/src/components/preview/openTerminalLinkInPreview.test.tsvp run @t3tools/web#typecheckvp checkon the three changed filesChecklist
Written by an agent (Claude Code, claude-opus-5).