Repository navigation
Agent chat: links open on the right, never over the chat - #23908
teamleaderleo wants to merge 8 commits into
Conversation
A web link clicked in an agent chat (a reply's link, a pull request chip, the hover card's Open) opened as a tab in the chat's own pane, covering the chat, and with a browser pane on the left it could land there instead. New setting links.openTarget (Settings > General > Open Links from Agent Chats): rightmost (default) opens it in the rightmost split of the chat's screen, or a new split on the chat's right when the chat is the rightmost split; newSplit always splits right of the chat; sameTab keeps the old rules. A pane left of the chat never gets the link. Command-click still opens a background tab in the same target. The decision is one pure rule (ChatLinkPlacement) that LinkOpener.openWeb asks for every web link whose source pane shows an agent chat, so every chat entrypoint shares it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a1602e-25ea-7611-8c79-d16df0c4058f&host=cosmic-gray
|
cmux status (one comment, edited in place; each line is updated by the job that owns it) Review App of this head (app.zip, kept 1 day)
red: cmux-next checks (god files, concurrency, crash safety, l10n); base red: the same failure on the base 1f4437e; does not block |
|
Bead |
|
Owned by nx-chat on big-red · took over from session 01a1602e |
|
Found 13 test failures on Blacksmith runners: Failures
...and 3 more test failures. View all on Blacksmith: run 39725119788, run 39725119849
|
…etting compiles Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a1602e-25ea-7611-8c79-d16df0c4058f&host=cosmic-gray
|
Review (claude subagent, correctness first): SHIP at 76d0537 I found nothing that blocks this. The rule holds its two promises for every layout I traced: the link never lands in a pane left of the chat, and never in the chat's own pane. That covers Findings, most severe first. None of them blocks.
Checked and fine:
Not run: no build or tests (this was a read-only review on a Mac). The CI status for this head was not available to me yet. |
…ane first Drop links.openTarget: a web link from an agent chat now follows the existing Open Links and Files setting (layout.linkPlacement), so there is one key app-wide. Split Right (the default) and New Column put it on the right: the pane right of the chat the person focused last, else the rightmost, else a new split (or column) on the chat's right. Tab in the Same Pane keeps it in the agent pane. A pane that shows another agent chat is never the target, nor any pane left of the chat. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a1602e-25ea-7611-8c79-d16df0c4058f&host=cosmic-gray
|
Review (claude subagent, correctness first): SHIP at 5f903f6 Read the diff against c07d400. The rule does what the brief asks. Split Right and New Column send a link to the right pane focused last, else the rightmost one (right edge, top first), else a new split or column. Same Pane returns nil, so the old rules apply. A lone chat under main pane layout still gets Checked:
Not blocking:
|
|
|
|
|
…omed chat splits Codex review of 5f903f6: with layout.linkPlacement set to Tab in the Same Pane a chat link still went through main pane layout and could dock the chat; now it is a tab in the chat's own pane (the chat dock, which holds only chats, keeps the rules for any page). A zoomed chat ignores the panes its zoom hides and opens a new split (or column) on its right, which ends the zoom. Adds pure tests and LinkOpenerChatLinkTests on a real chat | terminal workspace. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a1602e-25ea-7611-8c79-d16df0c4058f&host=cosmic-gray
release-notary's test_the_repository_highlights_validate refused it: more than 2 sentences and no tryIt action or docs link. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a1602e-25ea-7611-8c79-d16df0c4058f&host=cosmic-gray
… daemon browser tabs Codex review of 95186d9: - From the chat dock with only chats on its right the link asked for a split, which TabMoves refuses there, and the tab stayed over the chat. The chat dock now gets a new column beside it. - A machine whose daemon serves no browser tabs makes a session-local tab that never reports a surface, so a link needing a new split or column opened unselected. Those links now keep the rules for any page (base's target), so the tab opens selected; a pane on the right or the chat's own pane needs no move and still gets it. The chat dock check reads the screen's columns the rule already has (ChatDockRules.isChatDock(_:_:)), not a window lookup. Pure tests and LinkOpenerChatLinkTests cover both, including a real session-local open. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a1602e-25ea-7611-8c79-d16df0c4058f&host=cosmic-gray
|
Orphaned: no activity for 13 h, and no lane holds a lease on it. The orphan sweep could not merge |
|
Stale (13d, conflicts); rebuild fresh from feat-cmux-next if still wanted |
|
Already on feat-cmux-next at 7828506 (links/file opens on the right); this stale PR is already closed. |
![Fix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/fix-with-codesmith-light-3x.png)
Summary
Clicking a GitHub link in an agent chat opened it as a new tab in the chat's own pane, covering the chat. With a browser in a pane on the left, links could land there instead of beside the chat.
A web link from an agent chat (a reply's link, a pull request chip, the hover card's Open) now follows the existing Open Links and Files setting (
layout.linkPlacement), the one key app-wide for where links open:A zoomed chat fills the screen and hides the panes beside it, so its link never goes to one of them: on the right it opens a new split (or column), which ends the zoom (the daemon clears
zoomed_paneon a new split), so the tab is always visible. A link from a hidden chat (another pane zoomed) is not a visible click and keeps the old rules.From the chat dock, which never splits, a link with no pane on the right to take it (every one shows another chat) opens a new column beside the dock. On a machine whose daemon serves no browser tabs (
frontend-browser-tabs-v1), the link's tab is session-local and cannot be moved, so a link that would need a new split or column keeps the rules for any page and opens selected, as on base; a pane on the right, or the chat's own pane, needs no move and still gets it.Command-click still opens a background tab in the same place, without focusing it. Tabs can be dragged anywhere afterwards; nothing moves them back. Under main pane layout, a chat alone on its screen still docks left, so the link lands in the column that slides in on its right.
How it works:
ChatLinkPlacementis one pure rule over the screen's columns in visual order (top and bottom dock bands left out; in the chat's own split tree only panes beside it on its right count).LinkOpener.openWeb, which every chat link already goes through (AgentPaneView.openURL→LinkRouter.open(from:)→openInCmux), asks it whenever the source pane shows an agent chat. Terminal and browser Cmd-clicks, Home, Markdown/file pages and New Tab picks keep their rules, and thelinks.pagesFollowPlacementDebug tunable is untouched for them. Opens with no source view (Help menu, update notes) start from the focused pane, so when that pane is a chat they follow this rule too.Not in this PR: Cmd-T and the + button are handled in #23882 (projects-d7), which wires them through
ChatLinkPlacementonce this lands. File and folder links from a chat (openFile,openFolder) also keep their rules. Thelayout.linkPlacementhelp text still says what a chat opens goes in its pane; #23886 rewrites that string, so this PR leaves the catalog alone to avoid a conflict.Overlap: #23886 (teamleaderleo, reverts #22822) makes every link follow
layout.linkPlacementand owns the setting and its text; this PR adds no setting. #23032 (austinywang) changes how the split is made; this PR changes which pane is chosen. Both touchLinkOpener.targetin different lines; #23886 also edits LinkOpener.swift, and whichever lands second takes the conflict.Testing
ChatLinkPlacementTests(CmuxNextAppTests): the default opens on the right; no right pane gives a new right split (and a new column under New Column), never the left pane; the focused right split wins over the rightmost; with none focused the rightmost wins, right edge first; panes that take no tabs or show another chat are skipped; the chat is never the target for either right setting; Same Pane is a tab in the chat's pane, even zoomed, and in the chat dock falls back to the page rules; a zoomed chat opens a new split (or column), never a tab in a pane its zoom hides; the right edge of a split tree; panes right of the chat in its own tree; the right side of a screen with a browser column on the left and a bottom band.LinkOpenerChatLinkTests(CmuxNextAppTests):LinkOpener.target(from:chatLink:)on a real workspace (an agent chat split beside a terminal, throughWorkspaceContentController): Split Right and New Column give the terminal's pane; the terminal's own links get no chat rule; Tab in the Same Pane gives the chat's pane; with the chat zoomed, a new split (or column) of the chat, not the hidden terminal; from the chat dock with only another chat on its right, a new column; with no daemon browser tabs, the page rules' target, and a realopenWebmakes the session-local tab and selects it.package-test-lane.sh suite,ChatLinkPlacementTests,LinkOpenerChatLinkTests,LinkOpenPlacementTests,LinkOpenerTests,LinkPlacementSettingTests,MainPaneLayoutTests) at 8a1ec9c: queued on the light pool; not yet run, so the new tests have not compiled or run anywhere yet.cmux-ci compile-check --appat 8a1ec9c: passed (65 s, job 5f581b8e1a210e5030ffab9f). It compiles the app, not the tests.scripts/whats-new/test_digest.py(the What's New highlight) passes at the head; it failed at 5f903f6 on this PR's highlight (more than 2 sentences, no docs link), fixed in 95186d9.check-objc-selectors.pyfails onCEFFileDragRelay.swift:162(untouched here);test_issue_1138_sidebar_pr_polling.pytimes out in a 12 s bash timing check of the shell integration (no shell files change here).links.openTargetkey and its strings are removed again.Observed / Not run
Not run: a click in a live chat in a built app.
LinkOpenerChatLinkTestsexercisesLinkOpener.targetfor a chat source on a real workspace, but not the main pane dock branch or the daemon move that makes the split.Changelog
Changed: A link clicked in an agent chat opens on the right (the right pane you used last, or a new split) instead of covering the chat; Open Links and Files set to Tab in the Same Pane keeps it in the chat's pane
Proof
No capture: the change is which pane a chat link opens in; no capture scene drives a link click in a live agent chat yet. The placement is covered by the unit tests above.
Checklist
🤖 Generated with Claude Code
Agent session: cosmic-gray