fix(desktop): solve #6009 — expanded sidebar Projects section silently collapsed with no visible chevron

Draft
GI
3mo ago
+90-2

What the bug is

The expanded sidebar renders the PROJECTS header and the add-project (+) button with no project rows underneath, while the collapsed icon rail shows every project. Cmd+R doesn't clear it, and the only way to see the projects again is to collapse the sidebar back down.

Root cause

DashboardSidebar gates both the pinned section and every project group on one flag:

// DashboardSidebar.tsx:213,220
{(isCollapsed || !workspacesListCollapsed) && ( ... )}

workspacesListCollapsed comes from renderer/stores/sidebar-workspaces-collapse, which is used in exactly two places: this gate and DashboardSidebarWorkspacesHeader. Both the rail and the expanded list render the same orderedGroups, so this gate is the only difference between the two views — a blank expanded Projects section means the flag is true, and nothing else in the codebase can set it.

Three things turn that state into a sidebar that reads as broken:

  1. The whole header row is the toggle. DashboardSidebarWorkspacesHeader is a full-width role="button" (including the flex spacer next to the + button) with an onClick and an Enter/Space onKeyDown, so one stray click or a Space press while the row has focus empties the list.
  2. The state was invisible. The chevron was opacity-0 until group-hover/group-focus-visible, so a collapsed section looked identical to an empty one — no cue, and no hint that clicking the header restores it.
  3. It's persisted (zustand persist), so a reload can't clear it, and the rail ignores the flag entirely, so collapse → expand round-trips right back to the empty view.

The macOS App Management grant is coincidental — nothing in apps/desktop or packages/host-service reads that permission, and no code path other than the header toggle writes this store.

The fix

DashboardSidebarWorkspacesHeader now keeps the chevron at opacity-100 while the list is collapsed (hover-fade is retained only in the expanded state, where the rows already show the state) and exposes aria-expanded. The collapsed section is now self-explanatory and one click from restored.

Tests

New DashboardSidebarWorkspacesHeader.test.tsx (bun test, renderToStaticMarkup — matching the existing StepProgress.test.tsx pattern):

  • reproduction: with the list collapsed, the chevron class list must not be opacity-0/hover-gated — failed before the fix, passes now
  • reproduction: the header must report aria-expanded="false" when collapsed — the attribute was absent before
  • guard: the expanded state still fades the chevron in on hover and rotates it

bun test in apps/desktop: 2282 pass, 0 fail. Zustand feeds getInitialState() to useSyncExternalStore during SSR, so the test stands the store in with a plain selector to render both disclosure states — noted in a comment.

Not fixed here (deliberately out of scope)

The accidental-toggle surface itself: the full-width row hit area (spacer included) and the Space-key handler are the likely trigger, and narrowing the toggle to the label + chevron is a UX call I'd rather leave to a maintainer. Left as-is too: the collapsed rail ignores the flag, which is what makes the reporter's collapse/expand workaround only temporary — but making the rail honor it would hide the project icons entirely, the very symptom they reported on v2.

Refs #6009


Open in Stage

Summary by cubic

Fixes the empty Projects section in the expanded sidebar by keeping the chevron visible when collapsed and exposing the state via aria attributes. Fixes #6009.

  • Bug Fixes
    • Keep the Projects chevron opacity-100 while collapsed; retain hover fade only when expanded; rotation unchanged.
    • Add aria-expanded to the header for clear state and accessibility.
    • Add tests to verify chevron visibility when collapsed and aria-expanded behavior; ensure expanded hover/rotation still works.