Add drag-to-reorder for sites in the agentic UI sidebar - #4134
Conversation
(ported from origin/extract-site-centric-sidebar cbddbce; component files verbatim) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
📊 Performance Test ResultsComparing bf36769 vs trunk app-size
site-editor
site-startup
Results are median values from multiple test runs. Legend: 🟢 Improvement (faster) | 🔴 Regression (slower) | ⚪ No change (<50ms diff) |
katinthehatsite
left a comment
There was a problem hiding this comment.
The changes mostly look good. I noticed one brief flash of the old order if I change the order and then refresh the app (cmd + r). The old sort order briefly flashes and then I could see the new order:
Screen.Recording.2026-07-13.at.1.49.43.PM.mov
katinthehatsite
left a comment
There was a problem hiding this comment.
Additionally observation is that if you have a lot of sites, I am finding that the dragging and scrollbar interaction gets awkward when you cannot drag the desired site to the btottom easily:
Screen.Recording.2026-07-13.at.1.53.31.PM.mov
nice find @katinthehatsite I added 2 new changes that should improve that behavior
|
|
@katinthehatsite the flickering issue should be resolved now, can you take another look, please? |
katinthehatsite
left a comment
There was a problem hiding this comment.
I think this looks good now, I don't see the same issues as before and the interaction with the scrollbar is much better 👍
…rder # Conflicts: # apps/ui/src/components/site-list/index.test.tsx # apps/ui/src/components/site-list/index.tsx
## Related issues - Related to STU-1985 - Final extraction from #3998 (built on the already-landed #4131, #4133, and #4134) ## How AI was used in this PR Claude (Fable 5) extracted and adapted this change from the #3998 reference branch under close direction, preserving Shaun Andrews' original work via co-authored commits, and re-verified the result (typecheck, unit tests, light/dark visual pass in the browser UI). Code was human-reviewed throughout. ## Proposed Changes This is the capstone of the site-centric sidebar work: the sidebar becomes a flat list of sites, with no per-site chat sublists. - Clicking a site opens its most recent (non-archived) chat, or starts a new chat when the site has none. Chat history is no longer browsed from the sidebar — that moved below the composer in #4133. - Each site row carries a single activity indicator that aggregates all of the site's sessions, with priority: live-site sync > needs an answer > working > new message. This keeps long site lists calm while still surfacing what needs attention. - Row quick actions (site actions menu + start/stop status button) appear on hover/focus; drag-to-reorder from #4134 keeps working on the flat rows. - Status colors move to shared `--studio-color-status-*` tokens: the wpds palette has no vivid status colors (`--wpds-color-fg-content-success` resolves to near-black at dot size), which also fixes the session header's running dot rendering almost black. - Chrome polish: full-bleed 44px site icon with overlaid status badge in the site dropdown, flattened site icons, a visible hover tint on the sidebar create button, the floating sidebar toggle moved to the bottom-left, and aligned settings-header padding. Two product decisions to be aware of (intentional for now): - **Unread "new message" badges are in-memory only** — they reset (seed as all-seen) on every launch. - **The current chat is indicated via `aria-current` + active-row styling**, not the literal "Current chat" label from the mockups. ## Testing Instructions - Start Studio with `npm start` and enable the `Agentic UI` feature flag. - Confirm the sidebar shows a flat list of sites (no chat sublists): clicking a site with chats opens its latest chat; a site without chats opens the new-chat view. - Hover a row: the site actions menu and start/stop button appear; right side shows a green dot for running sites, a play affordance for stopped ones. - Send a prompt on one site, switch to another: the first row shows a working spinner, then a "New message" badge when the turn completes; answering a pending question clears the "Needs an answer" indicator. - Drag a site row to reorder; the order persists across restarts. - Delete a site you're currently viewing and confirm you land back on the home view. - Check the sidebar and site dropdown in **both light and dark mode**. ## Pre-merge Checklist - [x] Have you checked for TypeScript, React or other console errors? 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Shaun Andrews <191598+shaunandrews@users.noreply.github.com>
…#4130) ## Related issues - Related to STU-1985 — extracted from #3998 as part of splitting it into smaller PRs. ## How AI was used in this PR The core change was cherry-picked from #3998 (authored there with AI assistance). Claude Code ported it onto trunk, resolved merge conflicts (including the moves to id-based session↔site matching from #4131 and the signed-out gates from #4183), addressed review feedback, and wrote the unit tests for the redirect logic. All code was reviewed and verified locally (lint, typecheck, apps/ui test suite). ## Proposed Changes Studio's agentic UI always lands on the first site (in raw fetch order) at launch, regardless of where the user was working. This PR makes the app reopen on the site the user last worked with: - The dashboard remembers the last visited site in `localStorage`. - On launch, the `/` index route opens that site's newest active chat (or a new chat if it has none) — matching exactly what clicking the site in the site-centric sidebar (#4191) does. - If the remembered site no longer exists (deleted) or nothing is stored yet, it falls back to the **top site in sidebar order**, respecting the manual ordering from #4134 instead of raw fetch order. - The sidebar auto-scrolls to keep the restored site's row visible when it sits below the fold (thanks @sejas for catching this!). An earlier revision restored the exact last-visited *session*. We simplified to site-level restore to stay consistent with the site-centric sidebar model: sites are the primary navigation unit, and a site always opens its newest active chat. No launch-time cost: the index route already fetched sites and sessions before redirecting; this adds only a synchronous `localStorage` read. ## Testing Instructions 1. Launch Studio with the agentic UI and open a chat on a site other than the top one in the sidebar. 2. Quit and relaunch — you should land on that same site (its newest active chat). 3. With enough sites to make the sidebar scroll, work on a site near the bottom, relaunch — the sidebar should scroll so the selected site's row is visible. 4. Archive the chat you were on, relaunch — you should land on that site's newest remaining active chat, or a new chat if none are left. 5. Delete the site you were on, relaunch — you should land on your sidebar's top site, with no errors. 6. Drag a different site to the top of the sidebar, clear the app's localStorage, relaunch — the fallback should follow your sidebar order. ## Pre-merge Checklist - [x] Have you checked for TypeScript, React or other console errors? 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Shaun Andrews <shaun@automattic.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Related issues
extract-site-centric-sidebar)How AI was used in this PR
Claude Code ported the
ReorderableListcomponent verbatim from #3998 (Shaun's commit, preserved as author), adapted the order-persistence wiring from the reference branch onto trunk's group-based sidebar, and adapted/extended the tests. All changes reviewed and verified locally (lint, typecheck, unit tests).Proposed Changes
Sites in the agentic UI sidebar can now be reordered by dragging, so users can arrange their list to match how they work instead of being stuck with an imposed order.
prefers-reduced-motion); clicks, chat links, and action buttons keep working as before.sortOrderappdata field the legacy sidebar uses, so a custom order survives restarts and stays consistent between both sidebars. The renderer owns the order for the session — seeded once at startup, changed only by drags — so background refetches can never reorder or flicker the list; the saved order is also patched into the persisted query cache so a reload paints the right order immediately (review feedback). In the browser (studio ui/ hosted), where there is no appdata, the order is kept inlocalStorage.sortOrder(falling back to name), so the list stays stable and only changes when the user rearranges it. Chats not assigned to a known site (the old "Unassigned" group) are no longer shown; that logic gets cleaned up in a follow-up PR.The drag mechanics live in a generic
ReorderableListcomponent (pointer-event based, 4px drag threshold, drop hit-testing against rects captured at drag start, post-drag click suppression) that later pieces of the #3998 split will reuse.Testing Instructions
npm start, open the agentic UI with a few sites created.Pre-merge Checklist