Skip to content

feat(web): add workspace panes and transitions - #28845

Open
maria-rcks wants to merge 400 commits into
maria/redesign/5-voicefrom
maria/redesign/6-workspace-panes
Open

maria-rcks wants to merge 400 commits into
maria/redesign/5-voicefrom
maria/redesign/6-workspace-panes

Conversation

@maria-rcks

@maria-rcks maria-rcks commented Nov 1, 2026 •

Copy link
Copy Markdown
Collaborator

Part 6 of 6 of the T3 Code redesign. Chats, files, terminals and the browser open as panes in a workspace instead of fixed side panels.

  • workspace panes with tab strips: reorder by drag, overflow scrolling that reveals the active tab, restoring closed tabs, and terminal panes in tab drop targets.
  • layout hints and a guided tour introduce the workspace and stay inside the viewport on mobile.
  • ⌥⌘ + scroll moves between columns. Pane backgrounds paint without a retained compositor layer, and hints follow compositor motion without a continuous repaint.
  • the usage page reuses the floating settings shell.

The niri parts are behind Settings > Labs > Scrolling workspaces, off by default: the sideways scrolling strip and swipes, extra workspaces and the overview, and the layout tour and hints. With it off, panes shrink to fit; splits, tabs, side panes and per-project layout templates stay on for everyone. cmd+N and cmd+shift+O open main's project picker when there are several projects, and new threads replace the focused tab instead of opening a new one.

Scrolling workspaces off: six panes shrink to fit

Settings > Labs with the toggle off

cmd+N opens main's project picker

Needs approval: Theo for vite.config.ts (lint ceilings only); Theo or Julius for packages/contracts (ipc, keybindings, server, settings, including the new scrollingWorkspacesEnabled client setting, which decodes to false).

Stack

Do not merge: this stack still needs review. Each PR targets the one below it.

  1. feat(web): establish redesign primitives and shell #28839 1-primitives-shell
  2. feat(web): redesign composer and conversation timeline #28840 2-composer-timeline
  3. feat(web): redesign review panels and supporting pages #28841 3-panels-pages
  4. feat(web): redesign settings and setup surfaces #28842 4-settings-setup
  5. feat(web): style dictation with the redesign primitives #28844 5-voice
  6. feat(web): add workspace panes and transitions #28845 6-workspace-panes (this PR)

Evidence

Captured against base 5ba6597504b681819ff657e5e0808c54b37edb42 and head f6f00d4f4fe175e66f5d7fc6018ace6a1c841ca3, using separate seeded worktrees and the same replay scenarios at 1280 × 720. Dark mode unless labeled light. No code, PR, or branch changes.

Chat, terminal, and the same server/package.json file: the fixed right panel becomes an independent workspace column.

before: chat and terminal beside package.json in the fixed right panel

after: chat and terminal beside package.json in a workspace column

Browser: the same example.com page remains usable beside the chat and terminal, with the file and browser represented as workspace tabs.

before: example.com in the fixed right panel beside chat and terminal

after: example.com in a workspace pane beside chat and terminal

Workspace tab strip: overflow scrolling and active filename reveal were exercised with package.json, README.md, tsconfig.json, vite.config.ts, and the browser. Measured 466 px of tab content in a 326 px viewport; selecting vite.config.ts revealed its filename. Native drag reordered vite.config.ts before README.md and before tsconfig.json in separate passes. The base has no workspace pane tab strip; its fixed panel baseline is shown above.

after: overflowing pane tab strip reveals the selected vite.config.ts filename

Restore a closed chat: Ctrl+Shift+T reopened Replay: todo-list with its existing conversation. Closing and restoring a browser also retained its URL and page. Chat-tab restore has no equivalent on the base.

after: restored Replay todo-list chat retains its conversation beside the browser

Layout introduction and guided tour are new on this layer. Opened Show me, advanced through all five steps, entered the overview lesson, and finished back in the workspace.

after: tabs side by side introduction with four layout diagrams

after: guided tour anchors its first step to the new tab control

Usage adopts the floating settings shell. Both refs show the same real provider error, with matching selections, in dark mode.

before: dark usage limits page reports the Codex JSON-RPC error

after: dark usage limits page in the floating settings shell reports the same error

Usage, matching light-mode pair.

before: light usage limits page with the Codex JSON-RPC error

after: light usage limits page in the floating settings shell with the same error

Nine-second recording: native tab drag, horizontal column scrolling, closing the chat, and restoring it. Reviewed the exported sequence, then played the uploaded video to completion and checked seeking.

after: native pane tab reorder, column scrolling, and closing and restoring a chat

Findings and limits:

  • The recording shows a brief empty pane during chat restoration around 5–6 seconds, before its conversation returns. This is observed app behavior, not a successful no-blank-frame result.
  • Usage retrieval is unverified: both refs report Codex could not read usage (JSON-RPC -32600). No usage data was fabricated.
  • This is a Linux web browser. Ctrl+Alt plus horizontal scroll moved the workspace strip from 223 px to 0 px. The macOS ⌥⌘ path, Electron desktop, native mobile, and three-finger gestures remain unverified.
  • Browser automation intermittently disconnected. Native drag succeeded after targeting the visible pane specifically; exploratory pointer-event checks were not used in the final recording.
  • No automated test suite or production-build checks were run for this evidence-only task.

Verification

Every layer, rebased on main 9469095, passes typecheck, knip, lint and format on Blacksmith on its own. At layer 6, the web suite passes (unit and browser). The desktop suite fails one libsecret test, which fails the same way on main. GitHub CI runs the full suite on every layer.

Done with Claude Code (Opus 5.5) and Codex (gpt-6.1-sol high) children in T3 Code.

🤖 Generated with Claude Code

@maria-rcks maria-rcks added the DO NOT MERGE Experimental pull request. Do not merge. label Nov 1, 2026
@github-actions github-actions Bot added the vouch:trusted Author is trusted by repo permissions or the VOUCHED list. label Nov 1, 2026
@macroscopeapp

macroscopeapp Bot commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

Macroscope skipped reviewing this pull request. Per-review cost limit exceeded (workspace setting).

This review would cost an estimated $35.80, which exceeds your per-review limit of $15.00.

The top 3 files driving up this estimate:

File Diff Size Estimate
apps/web/src/layout/SurfaceHost.tsx 84.53KB $3.10
apps/web/src/layout/layoutModel.ts 78.31KB $2.87
apps/web/src/layout/Overview.tsx 55.80KB $2.05

Tip

To get this pull request reviewed, you can:

  1. Comment @macroscope-app on this PR to request a manual review (monthly spend limits still apply).
  2. Exclude the file(s) above from review by adding a pattern to your .macroscope/ignore.md — note that creating this file replaces Macroscope's built-in default ignores rather than extending them.
  3. Raise your cost limit in your workspace billing settings.

Turn off this reminder going forward

@macroscopeapp

macroscopeapp Bot commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs Human Approval

Macroscope's review found this PR requires human approval — This introduces workspace panes and transitions, including changing the default panel animation duration from 0 to 150 ms. That product-default change and a new lint-suppression directive require human review.

Needs human approval because:

  • Per-review cost limit exceeded (workspace setting). Approvability relies on correctness review in order to determine eligibility

Review your spending limits in Billing settings, or comment @macroscope-app review this PR to bypass the limit and review now. You can add or adjust custom eligibility rules. Learn more.

@github-actions

github-actions Bot commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ℹ️ No successful main baseline artifact is available yet. This run establishes the initial measurement.

Provider Metric Main baseline This PR Impact PR ceiling
Codex Total thread wire — 5.0 KiB — 6.8 KiB ✅
Codex Thread snapshot wire — 3.8 KiB — 4.9 KiB ✅
Codex Live turn WebSocket wire — 1.2 KiB — 2.0 KiB ✅
Codex Live turn WebSocket decoded — 21.5 KiB — 24.4 KiB ✅
Codex Live turn messages — 1 — 8 ✅
Claude Total thread wire — 5.0 KiB — 6.8 KiB ✅
Claude Thread snapshot wire — 3.8 KiB — 4.9 KiB ✅
Claude Live turn WebSocket wire — 1.2 KiB — 2.0 KiB ✅
Claude Live turn WebSocket decoded — 21.9 KiB — 24.4 KiB ✅
Claude Live turn messages — 1 — 8 ✅

Other scenarios

No main baseline exists yet for these 64 scenario measurements.

All 64 scenario measurements
Provider Scenario Metric Main baseline This PR Impact PR ceiling
Codex short turn resume WebSocket decoded — 10.5 KiB — 12.7 KiB ✅
Codex short turn resume WebSocket messages — 2 — 2 ✅
Codex long turn running, cold open HTTP decoded — 281.7 KiB — 302.7 KiB ✅
Codex long turn running, cold open rows — 75 — 75 ✅
Codex long turn running, history page HTTP decoded — 92.4 KiB — 107.4 KiB ✅
Codex long turn running, history page rows — 75 — 75 ✅
Codex long turn finished, cold open HTTP decoded — 381.5 KiB — 410.2 KiB ✅
Codex long turn finished, cold open rows — 151 — 151 ✅
Codex long turn finished, socket cold open WebSocket decoded — 381.6 KiB — 410.2 KiB ✅
Codex long turn finished, socket cold open WebSocket messages — 2 — 2 ✅
Codex long turn finished, socket cold open rows — 151 — 151 ✅
Codex long turn finished, work page HTTP decoded — 186.2 KiB — 200.2 KiB ✅
Codex long turn finished, work page rows — 150 — 150 ✅
Codex idle thread resume WebSocket decoded — 65 B — 200 B ✅
Codex idle thread resume WebSocket messages — 1 — 1 ✅
Codex fleet shell snapshot HTTP decoded — 41.6 KiB — 53.7 KiB ✅
Codex fleet shell snapshot rows — 27 — 27 ✅
Codex fleet shell subscribe WebSocket decoded — 65 B — 200 B ✅
Codex fleet shell subscribe WebSocket messages — 1 — 1 ✅
Codex fleet shell resume check HTTP decoded — 18 B — 100 B ✅
Codex fleet shell resume check rows — 0 — 0 ✅
Codex stale shell reload HTTP decoded — 41.6 KiB — 53.7 KiB ✅
Codex stale shell reload rows — 27 — 27 ✅
Codex stale shell resume WebSocket decoded — 140 B — 400 B ✅
Codex stale shell resume WebSocket messages — 2 — 2 ✅
Codex stale todo list cold open HTTP decoded — 72.4 KiB — 83.0 KiB ✅
Codex stale todo list cold open rows — 75 — 75 ✅
Codex stale todo list cold open nodes — 2 — 4 ✅
Codex stale todo list cold open subagents — 2 — 4 ✅
Codex stale todo list cold open plans — 1 — 2 ✅
Codex stale todo list cold open providerTurns — 1 — 2 ✅
Codex stale todo list cold open checkpoints — 1 — 2 ✅
Claude short turn resume WebSocket decoded — 10.6 KiB — 12.7 KiB ✅
Claude short turn resume WebSocket messages — 2 — 2 ✅
Claude long turn running, cold open HTTP decoded — 283.3 KiB — 302.7 KiB ✅
Claude long turn running, cold open rows — 75 — 75 ✅
Claude long turn running, history page HTTP decoded — 93.4 KiB — 107.4 KiB ✅
Claude long turn running, history page rows — 75 — 75 ✅
Claude long turn finished, cold open HTTP decoded — 384.2 KiB — 410.2 KiB ✅
Claude long turn finished, cold open rows — 151 — 151 ✅
Claude long turn finished, socket cold open WebSocket decoded — 384.3 KiB — 410.2 KiB ✅
Claude long turn finished, socket cold open WebSocket messages — 2 — 2 ✅
Claude long turn finished, socket cold open rows — 151 — 151 ✅
Claude long turn finished, work page HTTP decoded — 188.1 KiB — 200.2 KiB ✅
Claude long turn finished, work page rows — 150 — 150 ✅
Claude idle thread resume WebSocket decoded — 66 B — 200 B ✅
Claude idle thread resume WebSocket messages — 1 — 1 ✅
Claude fleet shell snapshot HTTP decoded — 42.2 KiB — 53.7 KiB ✅
Claude fleet shell snapshot rows — 27 — 27 ✅
Claude fleet shell subscribe WebSocket decoded — 66 B — 200 B ✅
Claude fleet shell subscribe WebSocket messages — 1 — 1 ✅
Claude fleet shell resume check HTTP decoded — 18 B — 100 B ✅
Claude fleet shell resume check rows — 0 — 0 ✅
Claude stale shell reload HTTP decoded — 42.2 KiB — 53.7 KiB ✅
Claude stale shell reload rows — 27 — 27 ✅
Claude stale shell resume WebSocket decoded — 140 B — 400 B ✅
Claude stale shell resume WebSocket messages — 2 — 2 ✅
Claude stale todo list cold open HTTP decoded — 73.4 KiB — 83.0 KiB ✅
Claude stale todo list cold open rows — 75 — 75 ✅
Claude stale todo list cold open nodes — 2 — 4 ✅
Claude stale todo list cold open subagents — 2 — 4 ✅
Claude stale todo list cold open plans — 1 — 2 ✅
Claude stale todo list cold open providerTurns — 1 — 2 ✅
Claude stale todo list cold open checkpoints — 1 — 2 ✅

Baseline: unavailable · PR result: fb2762f · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 108.5 KiB
  • Claude decoded thread snapshot: 108.8 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

@maria-rcks
maria-rcks added this pull request to stack #28864 November 1, 2026 20:00
@maria-rcks
maria-rcks force-pushed the maria/redesign/6-workspace-panes branch from f6f00d4 to b653b87 Compare November 1, 2026 20:11
@coderabbitai

coderabbitai Bot commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

Explain Change →

Finishing Touches

Generate docstrings

  • Commit to this branch
  • Create a new PR
Generate unit tests (beta)

  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Checks visible timeline lists a second after a turn ends, the fold
collapses, the thread is shown or the row count changes, confirms the gap
a few seconds later, keeps the last 20 reports in localStorage and shows
one toast per thread with a copy action. Exposes window.t3dev.gapReports,
copyGapReports and checkGapsNow. Remove once the gap's cause is fixed.
A pull request open as a tab beside a chat answered only the copy-number shortcut, so Cmd+Shift+C did nothing there. The pull request detail now answers copy link while its pane is focused, through the same copy target and confirmation as the thread and the Pull requests page.
Each tab's header rendered its own tab strip, so switching tabs swapped
the strip out from under anything drawn in it. The strip is now one node
per pane that the shown header holds; each header portals its tabs into
its own layer, which stays mounted while the header is hidden and moves
into the strip when it shows. The strip carries the joined tab's shape,
placed under the active tab once per tab change and hidden until the
connected pane tabs draw it. Looks unchanged.
From md up, a pane's top is a bar in the chrome's color. The focused
pane's active tab is a piece of the pane rising into it, joined by
concave ears, with one lit rim from its top round the ears and 36px out
along the pane's edge, where it fades. An unfocused pane's active tab is
a soft pill. Switching tabs slides the shape to the new tab over the
panel animation duration with ease-out, transform only, and the labels
fade between tiers on the same clock; focus moving between panes
cross-fades the shape and the pill. Reduced motion and a duration of 0
turn both off.

Light, black chrome keeps today's notch: its paint is scoped to
.black-chrome and the modal pane, so it can be deleted on its own.
On the connected bar the joined tab names its kind (Diff, Files) and its
breadcrumb and scope controls follow it on the bar as plain controls, so
a wide breadcrumb no longer stretches the joined tab out of the strip.
The breadcrumb gives way before the tabs when the pane is narrow. Black
chrome and phones keep it inside the tab.
…when it lands

The shape waited for the switch's commit and the incoming chat's layout,
about 120 ms, while the labels changed at once. It now starts sliding on
the press, measured from the tabs as they are, on its own compositor
layers, so it keeps moving while the switch renders; a press that does not
end in that tab showing brings it back. Labels read as active only under
the landed shape. Every title gets the active one's room, so activating a
tab moves no other tab. Where tabs are scrolled out, the strip fades at
that edge as scroll areas do.
… half-width pane

On the bar the breadcrumb shares the row with the pane's tabs, so the diff
treats it as crowded: the head ref folds away below 36rem and the view
controls fold into their menu, leaving the scope and the base in full.
The text field mic becomes the composer's bars and x where it sits, the
live transcript streams into the field itself, and errors take the same
slot, so no row is added and the buttons beside it stay put.
…emselves on hover

Main's details-panel test drops the timeline registrar the canvas context no longer carries.
The details button took the joined tab's 5px shift twice, once through the controls' top and once through their translate, so it sat 5px below the tabs in a single pane. It now takes only the translate, as it already did in split panes.
The suggestion and the composer placeholder now share the editor's text box,
line height and wrapping, so Tab no longer nudges the words or resizes the
composer. The Tab hint can't add a line and hides when it doesn't fit. The
sidebar rename field keeps the title's line height.
… button and the timeline steady

The sidebar slide moved the whole pane as one block from its final layout,
so everything anchored to the right edge or centered took a detour: the
details card swung 248px, the details button was clipped out of view while
opening, and the timeline and composer jumped 213px before sliding back.
Elements marked data-sidebar-slide-track now go straight from where they
were to where they land, the slide waits for the chat lanes' resize
observers before measuring, and while opening the pane clips by its shape
so what stays at its right edge stays visible.
…imer

A new worktree's checkout and setup script now show as ordinary work rows under
"Working for", which counts from send through setup into the agent's work. When
setup ends the stages fold into one "Set up worktree" row that opens on click.
A failed or cancelled setup keeps its stage rows above the run's own outcome.
Runs that prepared a worktree carry that fact on their summary, so the final
"Worked for" counts from send too.
…otion

Moves the track edge, the wait for resize observers and the clip lifting
out of sidebar.tsx into motion/slideTrack.ts, unchanged.
Opening the diff, files or terminal panel, splitting or closing a pane,
and toggling the sidebar beside split panes now move every pane edge,
the details button, the tab bar's end corner, the timeline and the
composer straight from where they were to where they land, together.

Before, the pane making room resized on the first frame (timeline and
details button jumped up to 532px) while the new pane slid into an empty
gap for 326ms on a spring; beside split panes the sidebar slide moved the
edge between them 136px the wrong way and back.

Pane motion now uses the sidebar slide's ease-out for the Panel
animations duration, snapshots tracked elements before React commits a
new layout, and slides them from there once the chat lanes re-measure,
keeping lanes on their pane's steady side. A sidebar toggle hands split
panes to pane motion with a snapshot taken before its own commit.
…nline

The reason sits in full above the field, the link is its host as a text link
with the full address in a tooltip below it, the input drops the repeated label,
and Save and Decline wrap together.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

DO NOT MERGE Experimental pull request. Do not merge. size:XXL 1,000+ changed lines (additions + deletions). vouch:trusted Author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants