feat(site/src): animate agents panels - #30002
tracyjohnsonux wants to merge 10 commits into
Conversation
Slide the agents chats sidebar and right panel open and closed with a short width transition instead of toggling display. Content is pinned to its full width during the slide so it moves behind the edge rather than reflowing every frame. Window resizing animates too: crossing below the side-by-side breakpoint slides the right panel out with a keyframe animation, and a left sidebar that was auto-collapsed to make room for the panel is restored once the window is wide enough again.
On chat and settings detail routes the mobile layout hides the chats sidebar. Crossing the sm breakpoint now slides it out or in with keyframe animations instead of snapping, keeping the layout side by side while it slides out. Window-resize clamping now re-derives the sidebar width from the persisted value, so a sidebar squeezed by a narrow window grows back to the user's chosen width.
|
/coder-agents-review |
|
Chat: Review posted | View chat Review history
deep-review v0.13.0 | Round 7 | Last posted: Round 7, 57 findings (3 P2, 19 P3, 27 P4, 8 Nit), COMMENT. Review Finding inventoryFinding inventory: PR #30002Findings
Contested and acknowledgedCRF-1 (P3, AgentsPageLayout.tsx:678) - sm crossing slide state has no test
CRF-2 (P3, RightPanel.tsx:316) - pinning and narrow slide-out state untested
CRF-3 (P4, RightPanel.tsx:444) - closed lg class drops border-l during close
CRF-11 (P3, AgentsPageLayout.tsx:678) - breakpoint-crossing slides
CRF-1 (P3, AgentsPageLayout.tsx:717) - round 3 dispute
CRF-35 (P4, RightPanel.test.tsx:394) - single unexplained test failure
CRF-51 (P4, RightPanel.tsx:396) - Expand sidebar slides partway open, then re-collapses
CRF-51 (P4, RightPanel.tsx:396) - round 7 re-check after the restore removal
Round logRound 1Full panel, round 1. Classified multi-domain (TSX and CSS keyframes), target 9 to 12. Netero first pass: 2 P3, 1 P4, 1 Nit, 1 Note, 1 out of scope; no P0, so the panel ran. Law not run (284 effective additions). Panel: Ging-TS, Ging-React, Nami, Killua, Luffy (triggered specialists), Pariston, Mafuuu (core), Bisky (tests changed), Gon, Leorio (once-per-PR floor), Hisoka, Komugi (pool), Chopper (random wildcard from the distilled set). 13 panel reviewers, one over the target because Gon and Leorio were placed in round 1 by the floor. Cross-check:
2 P2, 10 P3, 11 P4, 5 Nit, 2 Note (body), 1 out of scope, 2 dropped. Reviewed against 5dde49d..1aee330. Round 2Restructure round: the fix commit f5f777a added three fields to the exported Dispute check-in: CRF-1 re-raised by Netero; CRF-2 and CRF-3 closed by Netero (1/1); CRF-11 closed by Luffy (1/1). Cross-check:
5 P3 new, 7 P4 new, 2 Nit new, 1 P3 re-raised (CRF-1), 3 contested closed, 1 dropped, 1 out of scope new. Reviewed against 1aee330..f5f777a (full diff against 5dde49d). Round 3Restructure round: 2efe7ca changed the signature of the exported Dispute check-in: CRF-1 re-raised by Netero (new evidence: the slide transition can be a pure function tested like Cross-check:
3 P3 new, 2 P4 new, 1 Note (body), 1 P3 re-raised (CRF-1), 1 contested closed (CRF-35), 6 dropped. Reviewed against f5f777a..2efe7ca (full diff against 5dde49d). Round 4Restructure round: c0453ae added the exported Dispute check-in: CRF-51 closed by Nami (1/1); the underlying button behavior moved to out of scope (CRF-66). Cross-check:
1 P3 new, 3 P4 new, 2 out of scope new, 1 contested closed (CRF-51), 7 dropped. Reviewed against 2efe7ca..c0453ae (full diff against 5dde49d). Round 5Restructure round: 0f9e6bb added the exported hook Cross-check:
1 P4 new, 1 Note (body), 2 out of scope new, 7 dropped. Reviewed against c0453ae..0f9e6bb (full diff against 5dde49d). Round 6Fix round: dcd5870 (+17 -4, 4 files) applies the CRF-75 fix as the round-5 comment described it (four Cross-check:
2 P4 new, 1 dropped. Reviewed against 0f9e6bb..dcd5870 (full diff against 5dde49d). Round 7Restructure round: 0757b36 (+54 -502, 10 files) removed the auto-collapsed sidebar restore instead of patching CRF-86 and CRF-87: it deleted the exports CRF-51 re-check: Luffy noted the R4 closure cited the removed restore. The orchestrator asked the R7 Nami agent to judge it explicitly; Nami accepted (1/1) against the current code. Cross-check:
1 P2, 1 P4, 1 Nit new, 1 out of scope new, 2 dropped, 1 closure reconfirmed (CRF-51). Reviewed against dcd5870..0757b36 (full diff against 5dde49d). About deep-reviewCRF = Coder Review Finding (P0-P4, Nit, Note)
|
There was a problem hiding this comment.
This PR animates the agents sidebar and right panel on toggle and across the sm/lg breakpoints, and restores a sidebar the right panel auto-collapsed. Findings: 2 P2, 10 P3, 11 P4, 5 Nit.
Moving the collapse reason into AgentsPageLayout (the remount finding on RightPanel.tsx:347) also removes the mirroring effect and the redundant ref write.
Out of scope (needs a ticket or explicit acceptance by a human):
site/src/pages/AgentsPage/components/RightPanel/RightPanel.tsx:263: the resize handler clampswidthwithMath.min(prev, ...)and the effect at line 354 saves it, so narrowing the window permanently overwrites the saved right panel width, the same squeezed-width problem this PR fixes for the left sidebar.
Notes:
ResizableChatsSidebarFrame.tsx:134:--panel-widthand--agents-left-sidebar-widthhold the same value on the frame;AgentsPageLayout.stories.tsxreads the second one.RightPanel.tsx:444: atlg, content inside the closed zero-width panel reportsisIntersecting: trueto IntersectionObserver (checked in headless Chromium;display: nonereportedfalse), so xterm keeps rendering output untilTerminalPaneldetaches it after 30s.
🤖 This review was automatically generated with Coder Agents.
Track why the sidebar collapsed in AgentsPageLayout so a right panel remount no longer forgets it collapsed the sidebar, and measure the sidebar's rendered width when deciding to restore it. Restoring now also leaves room for the panel's current width. Keep the user's sidebar width in memory so failed storage writes survive window resizes, clear stale breakpoint slides during render, and share one slide duration between transitions and keyframes.
|
/coder-agents-review |
There was a problem hiding this comment.
This round reviews f5f777a, which addresses the round-1 findings. New findings: 5 P3, 7 P4, 2 Nit. The layout-level crossing test is still requested on its thread.
Verified fixed: the collapse reason now lives in AgentsPageLayout, the restore check reads the frame's width and leaves room for the panel's width, the sidebar keeps the user's width in memory, the auto-collapse tests stub ResizeObserver, and stale slides are reset during render. The defenses for the pinning and slide-out tests, the closed-panel border, and the crossing slides are accepted.
Two findings come from round-1 fixes: the in-memory width ref starts from an already-clamped width, and the rewritten reset comment names a cause that no longer applies.
Out of scope (needs a ticket or explicit acceptance by a human):
site/src/pages/AgentsPage/components/RightPanel/RightPanel.tsx:46:getSideBySideMaxWidthstill readslgasinnerWidth < 1024while the rest ofRightPanelnow usesbelowLgViewportMediaQuery, so changing one makes the width clamp and the side-by-side switch disagree.site/src/pages/AgentsPage/components/RightPanel/RightPanel.tsx:268: still open from round 1; the resize clamp overwrites the saved right panel width.
🤖 This review was automatically generated with Coder Agents.
Restore a sidebar collapsed for the right panel when the panel closes, seed the sidebar's in-memory width from the unclamped stored value, and skip the restore check when the sidebar width is unknown. Move the collapse-reason updater into an exported function that the layout and tests share.
|
/coder-agents-review |
There was a problem hiding this comment.
This round reviews 2efe7ca, which addresses the round-2 findings. New findings: 3 P3, 2 P4. The layout-level crossing test is re-raised on its thread with a unit-test approach.
Verified fixed: closing the panel restores a sidebar it collapsed, the frame seeds its in-memory width from the unclamped stored width, the harness uses the layout's applyNarrowWidthCollapse, the width getter returns undefined instead of 0, the auto-collapse effect no longer depends on the panel width, and a drag clears isAnimatingOpen. The defense for the single remount-test failure is accepted, with a correction on its thread.
If the crossing test is not added, a human reviewer needs to accept that gap.
Out of scope (needs a ticket or explicit acceptance by a human):
site/src/pages/AgentsPage/components/RightPanel/RightPanel.tsx:46:getSideBySideMaxWidthstill readslgthroughRIGHT_PANEL_SIDE_BY_SIDE_BREAKPOINT_WIDTH = 1024while the rest ofRightPanelusesbelowLgViewportMediaQuery, so changing one makes the width clamp and the side-by-side switch disagree.site/src/pages/AgentsPage/components/RightPanel/RightPanel.tsx:263: the resize clamp overwrites the saved right panel width.
Notes:
site/src/pages/AgentsPage/components/RightPanel/RightPanel.tsx:377: dragging the panel narrower does not restore a sidebar collapsed to fit it; the restore check runs again only on a window or container resize, a sidebar or expand change, or a panel close.
🤖 This review was automatically generated with Coder Agents.
Track open right panels in AgentsPageLayout so a narrow-width sidebar collapse clears whenever no open panel remains, including on routes that never mount one. Move the viewport slide transition into a pure function, share the sidebar width CSS variable between the frame and its reader, and rename the clamped width loader.
|
/coder-agents-review |
There was a problem hiding this comment.
This round reviews c0453ae, which addresses the round-3 findings. New findings: 1 P3, 3 P4.
Verified fixed: the crossing slide logic is a pure function with an it.each table, open panels register with the layout so a narrow-width collapse clears on close and on every view without a panel (chat switches excluded), the width read shares a constant with the frame and has a frame test, and the doc and naming fixes landed. The defense for the expand-button re-collapse is accepted; the button's behavior in that width range is listed below for a human decision.
Out of scope (needs a ticket or explicit acceptance by a human):
site/src/pages/AgentsPage/components/ChatTopBar.tsx:199: when the sidebar and the right panel do not both fit, the auto-collapse effect overrides "Expand sidebar", so the button has no effect in that width range (also true before this PR); the choice is between letting the expand squeeze the panel and disabling the button.site/src/pages/AgentsPage/components/AgentsSkeletons.tsx:40: sets--agents-left-sidebar-widthby literal, andAgentsPageLayout.stories.tsx:542and:667read it by literal, so renamingLEFT_SIDEBAR_WIDTH_VARleaves them on the old name.site/src/pages/AgentsPage/components/RightPanel/RightPanel.tsx:46:getSideBySideMaxWidthstill readslgthroughRIGHT_PANEL_SIDE_BY_SIDE_BREAKPOINT_WIDTH = 1024while the rest ofRightPanelusesbelowLgViewportMediaQuery, so changing one makes the width clamp and the side-by-side switch disagree.site/src/pages/AgentsPage/components/RightPanel/RightPanel.tsx:263: the resize clamp overwrites the saved right panel width.
🤖 This review was automatically generated with Coder Agents.
|
/coder-agents-review |
There was a problem hiding this comment.
This round reviews 0f9e6bb, which addresses the round-4 findings. New findings: 1 P4.
Verified fixed: the registration comment, the readExpandedLeftSidebarWidth rename and doc, the routes comment, and the shared useSidebarCollapseState hook, which the RightPanel harness now runs. Deleting only the registerOpenRightPanel line from the layout's outlet context now fails biome lint --error-on-warnings (unused variable), so the round-4 scenario is caught.
Out of scope (needs a ticket or explicit acceptance by a human):
site/src/pages/AgentsPage/AgentEmbedPage.tsx:108: the embed keeps sidebar collapse state and a toggle for a sidebar it never renders, soChatTopBarshows "Expand sidebar" whenever anything sets that state.site/src/pages/AgentsPage/hooks/useChatDraftAttachments.test.ts: "uses the default 10 MiB budget for non-Anthropic providers" failed once in a fullsrc/pages/AgentsPagerun and passed 17 of 17 alone, so it can fail CI on unrelated PRs.site/src/pages/AgentsPage/components/ChatTopBar.tsx:199: when the sidebar and the right panel do not both fit, "Expand sidebar" has no effect (also true before this PR); the choice is between letting the expand squeeze the panel and disabling the button.site/src/pages/AgentsPage/components/AgentsSkeletons.tsx:40: sets--agents-left-sidebar-widthby literal, andAgentsPageLayout.stories.tsx:542and:667read it by literal, so renamingLEFT_SIDEBAR_WIDTH_VARleaves them on the old name.site/src/pages/AgentsPage/components/RightPanel/RightPanel.tsx:46:getSideBySideMaxWidthreadslgthroughRIGHT_PANEL_SIDE_BY_SIDE_BREAKPOINT_WIDTH = 1024while the rest ofRightPanelusesbelowLgViewportMediaQuery, so changing one makes the width clamp and the side-by-side switch disagree.site/src/pages/AgentsPage/components/RightPanel/RightPanel.tsx:263: the resize clamp overwrites the saved right panel width.
Notes:
site/src/pages/AgentsPage/AgentEmbedPage.tsx:219: the embed does not passonSidebarCollapsedByNarrowWidthChange, so its right panel no longer collapses the sidebar the embed never renders; before this PR a narrow embed could show "Expand sidebar" (inferred from the code, not run). The PR description does not mention this change.
🤖 This review was automatically generated with Coder Agents.
|
/coder-agents-review |
There was a problem hiding this comment.
This round reviews dcd5870, which makes the four narrow-width outlet fields required. New findings: 2 P4.
Verified fixed: omitting any of the four fields from a producer now fails tsc (TS2741), and the layout, the embed, and both story contexts set them. The embed change is now in the PR description.
Out of scope (needs a ticket or explicit acceptance by a human):
site/src/pages/AgentsPage/AgentEmbedPage.tsx:108: the embed keeps sidebar collapse state and a toggle for a sidebar it never renders, soChatTopBarshows "Expand sidebar" whenever anything sets that state.site/src/pages/AgentsPage/hooks/useChatDraftAttachments.test.ts: "uses the default 10 MiB budget for non-Anthropic providers" failed once in a fullsrc/pages/AgentsPagerun and passed 17 of 17 alone, so it can fail CI on unrelated PRs.site/src/pages/AgentsPage/components/ChatTopBar.tsx:199: when the sidebar and the right panel do not both fit, "Expand sidebar" has no effect (also true before this PR); the choice is between letting the expand squeeze the panel and disabling the button.site/src/pages/AgentsPage/components/AgentsSkeletons.tsx:40: sets--agents-left-sidebar-widthby literal, andAgentsPageLayout.stories.tsx:542and:667read it by literal, so renamingLEFT_SIDEBAR_WIDTH_VARleaves them on the old name.site/src/pages/AgentsPage/components/RightPanel/RightPanel.tsx:46:getSideBySideMaxWidthreadslgthroughRIGHT_PANEL_SIDE_BY_SIDE_BREAKPOINT_WIDTH = 1024while the rest ofRightPanelusesbelowLgViewportMediaQuery, so changing one makes the width clamp and the side-by-side switch disagree.site/src/pages/AgentsPage/components/RightPanel/RightPanel.tsx:263: the resize clamp overwrites the saved right panel width.
🤖 This review was automatically generated with Coder Agents.
|
/coder-agents-review |
There was a problem hiding this comment.
This round reviews 0757b36, which removes the auto-collapsed sidebar restore and leaves the panel animations and the sidebar width regrow. New findings: 1 P2, 1 P4, 1 Nit.
Resolved by the removal: RightPanel.test.tsx no longer imports the page module, and the description no longer makes the embed claim.
Out of scope (needs a ticket or explicit acceptance by a human):
site/src/pages/AgentsPage/AgentEmbedPage.tsx:108: the embed keeps sidebar collapse state and a toggle for a sidebar it never renders, soChatTopBarshows "Expand sidebar" whenever anything sets that state.site/src/pages/AgentsPage/hooks/useChatDraftAttachments.test.ts: "uses the default 10 MiB budget for non-Anthropic providers" failed once in a fullsrc/pages/AgentsPagerun and passed 17 of 17 alone, so it can fail CI on unrelated PRs.site/src/pages/AgentsPage/components/ChatTopBar.tsx:199: when the sidebar and the right panel do not both fit, "Expand sidebar" has no lasting effect: the sidebar slides partway open and the right panel's auto-collapse closes it again (before this PR it flashed for one frame); the choice is between letting the expand squeeze the panel and disabling the button.site/src/pages/AgentsPage/components/RightPanel/RightPanel.tsx:43:getSideBySideMaxWidthreadslgthroughRIGHT_PANEL_SIDE_BY_SIDE_BREAKPOINT_WIDTH = 1024while the rest ofRightPanelusesbelowLgViewportMediaQuery, so changing one makes the width clamp and the side-by-side switch disagree.site/src/pages/AgentsPage/components/RightPanel/RightPanel.tsx:263: the resize clamp overwrites the saved right panel width, so the panel stays at the squeezed width after the window widens.site/src/pages/AgentsPage/components/ChatsSidebar/ResizableChatsSidebarFrame.tsx:121: before this PR too, the mount-time maximum capped the rendered sidebar after the window widened, so a drag or End press past it movedaria-valuenowbut not the sidebar edge.
🤖 This review was automatically generated with Coder Agents.
| const [width, setWidth] = useState(() => clampLeftSidebarWidth(storedWidth)); | ||
| // The width the user chose, kept in memory because storage writes can fail. | ||
| const userWidth = useRef(storedWidth); | ||
| const maxWidth = getLeftSidebarMaxWidth(); |
There was a problem hiding this comment.
P2 [CRF-89] The React Compiler computes maxWidth once per mount, so a sidebar that grows back after the window widens stays capped at the mount-time maximum, and the new fixed-width content wrapper is clipped. (Ging-React)
getLeftSidebarMaxWidth() has no reactive inputs, so the compiled component evaluates it once inside a react.memo_cache_sentinel guard, and --agents-left-sidebar-max-width and aria-valuemax never change after mount. With a stored width of 600, mounting at 800px and widening to 1440px gives aria-valuenow 600 (so grows a sidebar squeezed at mount back to the stored width passes) but aria-valuemax 440 and a 440px max-width variable, so sm:max-w-(--agents-left-sidebar-max-width) holds the frame at 440px and sm:overflow-hidden cuts off the right 160px of the 600px wrapper until reload (from the classes, not run in a browser); a drag past 440px after the widen ends the same way, while before this PR the children filled the capped frame. Keep maxWidth in state with useState(getLeftSidebarMaxWidth), set it in the resize handler next to setWidth, and assert aria-valuemax in that test.
🤖
| const [prevIsSideBySide, setPrevIsSideBySide] = useState(isSideBySide); | ||
| if (visualOpen !== prevVisualOpen) { | ||
| setPrevVisualOpen(visualOpen); | ||
| setIsAnimatingOpen(visualOpen && !isPointerResizing && !isBelowLg); |
There was a problem hiding this comment.
P4 [CRF-90] Opening the panel straight into expanded mode sets isAnimatingOpen, and nothing clears it, so after the user leaves expanded mode the content stays pinned to --panel-width until the next close or drag. (Mafuuu, Komugi)
AgentChatPageView.tsx:376-379 keeps expansion through a close and restores it with the panel, so a reopen renders visualOpen and visualExpanded together; the expanded panel has no transition class, and leaving expanded mode changes width from auto, which does not transition, so no transitionend arrives (Chromium fired no transition events for that change). Rendering (isOpen, isExpanded) as (false, false), (true, true), then (true, false) in jsdom leaves lg:w-(--panel-width) on the content wrapper, and expanding during the 200ms open slide should end the same way (inferred). The pinned content is 1px wider than the bordered box and loses its left column (items-end), and more whenever the box renders narrower than the width state. Set the flag only for side-by-side opens (setIsAnimatingOpen(isSideBySide && !isPointerResizing) at line 326) and clear it whenever isSideBySide turns false.
🤖
| const handle = renderHandle(); | ||
|
|
||
| vi.stubGlobal("innerWidth", 700); | ||
| fireEvent(window, new Event("resize")); |
There was a problem hiding this comment.
Nit [CRF-91] The expected widths "340" here and "440" at line 118 are bare literals for innerWidth - AGENTS_MAIN_PANEL_MIN_WIDTH. (Gon)
getLeftSidebarMaxWidth() takes the smallest of 660, 70% of the window, and the window minus 360, and the test does not say which applies at 700px and 800px, while the tests above derive their expectations from LEFT_SIDEBAR_DEFAULT_WIDTH. Import AGENTS_MAIN_PANEL_MIN_WIDTH and write String(700 - AGENTS_MAIN_PANEL_MIN_WIDTH) and String(800 - AGENTS_MAIN_PANEL_MIN_WIDTH).
🤖
The agents chats sidebar and right panel now slide open and closed (200ms, ease-out) instead of snapping, including when the window is resized.
lgbreakpoint, which auto-closes the right panel, now slides it out.smbreakpoint (where mobile hides the sidebar) slides the left sidebar out or back in.Implementation notes
panel-slide-out/panel-slide-inkeyframe animations (inindex.css) rather than transitions: the breakpoint's CSS changes before React re-renders, so there is no reliable starting width to transition from. The animations read--panel-widthand holdmin-widthat 0.ResizableChatsSidebarFrametakesisCollapsedand collapses tow-0+invisible(sm+) instead ofsm:hiddenso the width can transition;invisiblekeeps collapsed content out of the tab order and accessibility tree, assm:hiddendid. It also takesviewportSlide("in"/"out"), driven byAgentsPageLayoutonsmcrossings. While sliding out, the layout is keptflex-rowso the sidebar stays beside the chat.RightPanel's closed state atlgis a zero-widthinvisibleflex item instead ofdisplay: none, so the width can transition. Content is pinned to--panel-widthwhile closed or animating open, then released ontransitionend.Generated by Coder Agents on behalf of @tracyjohnsonux.