Repository navigation
Conversation
1ce0c0b to
9d63f6b
Compare
ApprovabilityVerdict: Approved at Macroscope's review found this PR approvable — This change reduces unnecessary workspace-switch rendering and separates chat animation state while preserving snooze behavior. Its effects are limited to client-side presentation, with no changes to product defaults or persistent data operations. No code changes detected at You can add or adjust custom eligibility rules. Learn more. |
bfeb51d to
cdb88a3
Compare
Dismissing prior approval to re-evaluate 22f52d4
61b6aa9 to
f434d60
Compare
…emote lineage rows into workspace panes
…arts under the tabs The terminal pane's split and close actions move to a mirrored right notch, as a panel pane's do, and its "+" follows the last tab like the chat tab strip's. The shell drops the band and side gutter around it, so it starts right under the notch with its text in line with the tab icons.
- ThreadServersPanel, ThreadAutomationsPanel: drop the empty-card visibility cases, a visual fix proven by captures - ThreadAutomationsPanel: drop toBeDefined asserts on elements the next line already uses - ThreadTerminalDrawer: drop the add-to-chat menu item case, it mirrors an optional prop - KeybindingsSettings.logic: drop the default label copy check - settingsSearch: fold seven identical layout rows into one case - contracts settings: drop the onboardingCompletedAt decode/encode round trip, a schema mirror
runInNewLayoutTab cleared whatever request the store held once its own action settled, so a second cmd+N started before the first settled lost its request and replaced the first new tab instead of adding one. Each request now carries its own ticket and an action clears only its own.
The chip and divider drags kept their gesture in a press ref plus a drag state, and the release read the drag from the last render. A flick whose move had not rendered yet lost its drop: the release click appended the pane on the right and the ghost and drop zone stayed on screen. Each gesture is now one explicit phase the handlers read directly; the state only draws the canvas.
A plain open (sidebar click, link, palette) lands in the focused pane's ghost tab. A ghost or launcher is replaced in place, a kept tab gets the ghost right after it, and a thread already open is focused where it is. A focused surface targets its thread's chat, or opens the ghost beside itself when that thread has no chat. A window's lone ghost looks like any other tab, so one pane with no arranging behaves as before. Middle-click opens a kept tab, alt-click opens to the side, and new threads (cmd+N) still replace the focused tab. Sending a message no longer keeps a ghost, for sidebar and lineage ghosts alike. A thread's terminal and side panes hide when an open swaps its chat off a pane and come back when an open brings it back. The sidebar's current row follows a focused surface's thread. Layouts saved before make their lone chat the ghost.
Panes other than the focused one draw their active tab a tier down, in the muted foreground, and their other tabs in the dim foreground, so the window has one full-strength tab label. A single pane is unchanged.
Thread details reads the context breakdown from compaction.logic, where main moved the meter's helpers, and the context file keeps only the segment bar it still renders. cmd+N no longer reads the removed legacy sidebar setting, ChatView drops the compact reason nothing shows anymore, and the settings search filter wraps as the formatter expects.
…debar Pressing Ctrl or Cmd re-rendered the whole sidebar and the composer's send controls on keydown and again on keyup, so every chord like Mod+K paid two full sidebar renders before its own work. Held modifiers now live in one shared store; the sidebar and composer subscribe to a derived boolean (hints shown after the hold delay, queue mode) and re-render only when it flips. A quick chord never flips it. Shortcut labels walked every keybinding per lookup (96 rules, several lookups per render). Effective shortcuts are now computed once per keybindings array and context. Keydown resolution skips rules for other keys before evaluating when-clauses. cmd+K open, keydown to list painted, median/p90 ms (10-12 runs, interleaved with main and nightly, production builds, shared load): before: this 374/724, main 530/793, nightly 703/979 (load ~22) after: this 262/349, main 473/569, nightly 433/527 (load ~15) Ctrl keydown task during cmd+K: 19-42 ms -> 5.5 ms (trace). Layer 1 (establish redesign primitives and shell); all touched lines are main's code.
…very row The virtualized scroll fade sets --fade-size and the mask in custom properties, which inherit by default. Each time a fade turned on or off (opening cmd+K, the first scroll in any faded list) Chrome restyled every row below the scroller. Both properties are only read on the element that sets them, so they are now registered as non-inherited, like the surface and overview properties. Pixels are unchanged (palette open, scrolled, typed and model picker screenshots are byte-identical). cmd+K open, frame after the list renders (trace, same machine): ResizeObserver pass 38-42 ms -> 18.5 ms restyled elements 835 -> 319 Layer 1 (establish redesign primitives and shell); the fade utility is main's code.
…eline Every chat view render re-rendered ChatComposer because `setThreadError` was a new function each render, and every canvas measurement re-rendered the whole MessagesTimeline because it read `registerTimeline` from the canvas layout context. `setThreadError` is now a stable callback, and the timeline reads its register function from its own context, so canvas resizes reach only the details card and the preview mini player. Stack layer 1 (feat(web): establish redesign primitives and shell); the code is from main. Measured with the layer 2 footer and banner commits that follow (prod build, trusted input, 12 samples each, interleaved; median / p90 ms): - details panel open: 132.5 / 191.9 -> 99.5 / 130.2 (main 95.9 / 142.8, nightly 94.2 / 134.2) - thread switch, short: 380.2 / 428.6 -> 308.8 / 445.1 (main 298.4 / 356.9, nightly 303.5 / 357.9) - thread switch, long: 449.9 / 608.1 -> 408.8 / 502.3 (main 449.8 / 529.5, nightly 488.0 / 741.4) Details toggle commits render 138 + 33 components instead of 182 + 106 for open and 32 instead of 76 + 75 for close.
…oss chat view renders The timeline footer was a new element on every chat view render, because the compiler leaves an element unmemoized when it depends on the provider list or the resolved handoff, so every ChatView render re-rendered the whole MessagesTimeline. The footer is now a TimelineFooter component that reads the providers and resolves the handoff itself, built outside the condition where the compiler memoizes it. An empty composer banner list was a new array each render, which re-created the ChatComposer element. The empty case now uses the shared constant before the element is built. Stack layer 2 (feat(web): redesign composer and conversation timeline); the footer shape and the tray banner list come from that layer. Measured together with the layer 1 commit before it (prod build, trusted input, 12 samples each, interleaved; median / p90 ms): - details panel open: 132.5 / 191.9 -> 99.5 / 130.2 (main 95.9 / 142.8, nightly 94.2 / 134.2) - thread switch, short: 380.2 / 428.6 -> 308.8 / 445.1 (main 298.4 / 356.9, nightly 303.5 / 357.9) - thread switch, long: 449.9 / 608.1 -> 408.8 / 502.3 (main 449.8 / 529.5, nightly 488.0 / 741.4)
…red every frame The terminal hint was positioned from the host by measuring the focused pane, host and composer on every animation frame through the browser surface tracker while the terminal pane slid in, and it still trailed the pane by a frame. The terminal hint now renders inside the focused pane, which slides in on top, where its resting classes already center it, so the pane's own transform carries it. Only a lift above a taller composer is measured, once on mount and on resize. Column and workspace hints keep the host placement: their pane can pass behind the panes it crosses, and the hint stays above. Stack layer 6 (feat(web): add workspace panes and transitions). Main-thread CPU in the 700 ms after each open (trace, 2x6 opens each): before 400-443 ms, 56-57 forced layouts after 255-260 ms, 21-22 forced layouts main 180 ms, 12 forced layouts Terminal open wall-clock (first frame with the terminal on screen) is unchanged within noise; the saving is in the frames of the slide after it. Interleaved, n=30 each, median / p90 ms, three runs: before 209.6 / 242.3, after 203.3 / 239.3 (main 185.6, nightly 183.0) before 245.1 / 268.4, after 228.9 / 260.6 (main 193.9, nightly 194.4) before 209.1 / 242.4, after 211.3 / 248.4 (main 176.2, nightly 187.4) Thread switch (short), n=30: 350.1 -> 340.3 ms, unchanged within noise.
cmd+D toggles the diff where Settings > Layout puts the side pane, as a normal tab, instead of opening an empty launcher pane. A diff tab hidden behind another tab comes forward. A new pane to the right moves to cmd+\, and a saved copy of the old cmd+D binding is dropped on startup.
8673be9 to
94e50e3
Compare
94e50e3 to
e976636
Compare
juliusmarminge
left a comment
There was a problem hiding this comment.
Thanks @flambo for measuring the workspace-switch rendering. #29150, merged as a0056dddb1, already stabilizes the sidebar snooze callback. Once the redesign base includes that fix, please cut this PR down to the separate off-screen chat and panel-animation changes.
|
Note 🤖 Claude Opus 5.5 on behalf of Oliver @juliusmarminge thanks, the sidebar part is removed. This PR is now only the off-screen chat and panel-animation changes. The #29150 port for the redesign is in #33528 (onto layer 1). |
Note
🤖 Claude Opus 5.5 on behalf of Oliver
Fix for the redesign stack, targets layer 6 (#28845).
Problem
Every workspace switch (Ctrl+Alt+U/I) re-rendered every mounted chat, even chats in workspaces the switch never touches. With four chat workspaces, one warmed switch ran ChatView 9 times and ChatComposer 11 times.
There were two causes:
splitflag changed for every chat's pane state when the slide began and again when it landed, including chats not on screen.Fix
Numbers
Dev build with the React Compiler on, headless Chromium at 1280×800, four Replay chat workspaces (all visited) plus an empty one. Render counts come from a React commit hook. All 8 warmed switches gave the same counts:
The savings grow with the number of chats outside the switch. Rapid bursts improve less, because most mounted chats join the slide.
Before / after
What to look for: before, workspace shortcuts cycle through four Replay chats; after, the same sequence still works with the off-screen chat change.
https://gh-file-drop-api-prod-galwoqjslzlnws6s.oliver-boorstein.workers.dev/f/1a909bfdd8b3fcbc/31615-1x.mp4
Motion slowed 5× in the page:
https://gh-file-drop-api-prod-galwoqjslzlnws6s.oliver-boorstein.workers.dev/f/e1fd2a5cd40a6499/31615-5x.mp4
These development recordings verify shortcut behavior. They do not establish the render counts above.
Validation
Claude Opus 5.5 (Claude Code)