Repository navigation
feat(switcher): the session switcher on every Terminal view - #393
Merged
Merged
Conversation
Owner
Author
|
Round 0 (requirements & deletion): proceed to the checklist. Ledger: requirements 9 (unattributed 3) · deletion candidates 2 · no open premises. |
Owner
Author
|
Round 1 (nine-axis checklist): 0 🔴, 3 🟡, 4 🟢. No 🔴, so the ladder ends; the 🟡s are fixed un-re-audited or recorded open. |
ZacxDev
added a commit
that referenced
this pull request
Nov 1, 2026
go test's default timeout is 10 minutes per package. Under -race -cover, internal/api already takes 460-575 s on main's runs, and on a runner that was slower across every package (agentprovision 123 s vs 91 s) it passed 600 s and panicked mid-suite with nothing hung — three times on PR #393, and once on another PR before it. Locally that PR's api package was 362 s against main's 345 s, all tests passing, so the time was the runner's. A real hang still fails, at 20 minutes instead of 10. The pin in internal/modulegate/ci_verdict_steps_test.go is updated to the new line (watched red with the old pin against the new step). Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The operator asked for the bottom bar on the Terminal view too, so a session can be switched from there without going back to the Rendered view. Every Terminal view now draws it as the column's last row: a mirrored window's (with or without a Rendered view; composer enabled, disabled or absent) and a dispatched claude-code agent's. - ui.AgentTerminalView.Switcher: drawn inside the composer as its last row (as on the Rendered view, but in flow, not Pinned), or in a box of exactly its room when there is no composer. The safe-area inset moves to the bar. - mirrorComposer: an in-flow composer holding the bar is now its containing block (relative); before, only its / menu or suggestion row made it so, and the absolute bar would have been placed against the page. - The bar is server-rendered before the terminal's script, at a fixed height, so the terminal's first measure already has the bar's room taken: no second, smaller resize, and a view switch in the bar sends none. - SwitcherCurrent.Back: the pin toggle's no-script return is the Terminal view itself. The header pin a terminal-only window had (TerminalPin) is gone: the bar's pin is that page's pin now, and the pin answer always carries the bar's rows out of band. - The agent Terminal view records a visit, as the mirrored one and the chat page do. Tests: handler tests that every Terminal view draws #session-switcher with its own session current (red at origin/main, green here); a ui test that the bar precedes the terminal's script and sits in its reserved room (mutation-checked); e2e at 390x844 for a mirrored and an agent Terminal view (bar, current, CLS, one size heard by the far side, a tap switches); the pins spec now finds a shell window's pin in its bar. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… aside for the keyboard Y1: a session taller than the page (a pod window grown once to a full desktop height, which termsize never shrinks) was drawn in overflow at scrollTop 0, hiding its prompt and status line on every load. The overflow view now starts at its bottom and returns there on every redraw (a relayout, Fit width, the switcher stepping aside and back), unless the person scrolled it away; entering overflow afresh starts at the bottom again. The page's own scrolls (the sizer collapsing in proposed(), the pin) are not taken for the person's. Y2 (operator decision): the session switcher at a screen's foot steps aside, its room given back, while a soft keyboard is up — a text field focused and the visual viewport >= 150px shorter than its tallest at this width (<html data-keyboard>) — on the Terminal views and the Rendered view; and on a screen <= 30rem tall (a landscape phone) by a CSS media query, on the Terminal views only (the Rendered view's pinned composer reserves a measured height whose first-paint fallback counts the bar). The agent chat page's bar is unchanged. Y3: a Go test that a view=switcher pin whose back is a Terminal view answers with the bar's rows out of band. G5: the bar-below-keys check asserts #term-keys exists. D1: the recordAgentVisit comment gives the real reason, and the recents ledger covers the agent Terminal view's visit. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
ZacxDev
force-pushed
the
feat/switcher-on-terminal-view
branch
from
November 1, 2026 19:32
4d0b9ab to
c0ee49c
Compare
ZacxDev
marked this pull request as ready for review
November 1, 2026 20:00
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
The operator asked: "Include the bottom bar in the agent terminal view as well as in the render view where it already is so that I can easily switch agents from the terminal view until we need to go back."
The session switcher (
internal/ui/switcher.go) now appears on every Terminal view, at the bottom of the column:/agents/{name}/terminal).The bar shows even when the composer is disabled or missing, because switching sessions doesn't depend on being able to send.
How
ui.AgentTerminalView.Switcher. When there's a composer, the bar is drawn inside it as its last row, as on the Rendered view, but in flow rather than Pinned. When there's no composer, it goes in a box exactly the bar's height. The safe-area inset moves from the key bar or form to the bar.mirrorComposer: an in-flow composer that holds the bar now getsrelative. Before, only its/menu or suggestion row set that, so the absolutely placed bar would have been positioned against the page instead of the space reserved for it.SwitcherCurrent.Back: when you pin without script, the page returns to the Terminal view instead of the session's other view.TerminalPin) is removed. The bar's pin replaces it, so a page has only one#switcher-pin. The pin route's answer now always carries the bar's rows out of band.Terminal size (hazard 1)
The page only ever sends a size that covers the far side's window, and
termsize.Decideon the far side never shrinks a window. So the bar can't shrink the remote tmux window in any case.To avoid resize churn, the bar is server-rendered at a fixed height and appears in the document before the terminal's script runs. The terminal's first
proposed()measurement therefore already has the bar's space taken. The pending-until-whole visibility and switching views in the bar change no layout, so no second resize is sent.Side effect on desktop: if this browser had previously grown the window to the full page height, the page is now about 3 rows shorter than the window. It shows that window in its scroll area instead of resizing it. Round 1 (Y1): that scroll area now starts at its bottom and goes back there on every redraw, so the prompt and status line (a TUI's last rows) stay on screen. It stays put if the person scrolled away, until they scroll back to the bottom.
The bar steps aside (round 1, Y2, operator decision)
<html data-keyboard>). It comes back when the keyboard closes. 150px is smaller than any phone keyboard (≈200–350px) and larger than a browser toolbar collapsing (≈50–100px), so the toolbar doesn't make the bar flicker.max-height: 30rem, so the decision is made before the first paint. This applies to the Terminal views only. The Rendered view's pinned composer reserves a measured height whose first-paint fallback includes the bar, so collapsing the bar with a query there would shift the transcript on load. 30rem (480px) is taller than any landscape phone and shorter than the smallest portrait phone's page.Tests
TestTheMirroredTerminalViewsDrawTheSessionSwitcher(3 cases) andTestAnAgentsTerminalViewDrawsTheSessionSwitcherfail at origin/main b60d5d9 (0 #session-switcher) and pass here.TestATerminalViewsSwitcherIsTheColumnsFootAndInPlaceBeforeTheTerminalMeasureschecks that the bar comes before the terminal's script, sits in its reserved space withrelative, and carries the safe-area inset alone. I broke each of the first two on purpose and saw the test fail with that check's own message.pins.spec: a shell window's pin is now found in its bar.Known limitation
When the Terminal view is opened in the view-swap frame over the Rendered view, pinning in one document doesn't refresh the other document's bar until it reloads.
🤖 Generated with Claude Code