feat(desktop): add workspace pinning to the v2 dashboard sidebar
What
Right-click any workspace row in the v2 dashboard sidebar → Pin. The workspace moves into a new Pinned section at the very top of the sidebar, above all project groups, with a small project avatar for cross-project context. Unpin returns it to exactly the project/group/position it came from. Pins persist across restarts. In the collapsed rail, pins render as an icon stack above the project thumbnails.
How
- Storage: nullable
pinnedAt(epoch ms) onsidebarStatein thev2WorkspaceLocalStatelocalStorage collection — heal-forward, so no migration, no Postgres/Electric/host-service changes, and live-query reactivity for free. - Mutation: idempotent
setWorkspacePinned(workspaceId, projectId, pinned)inuseDashboardSidebarState. Repeat pins keep the original pin time; pinning an auto-included local main workspace creates its local-state row first. - Ordering: pinned section sorts by
pinnedAtascending — new pins append at the bottom, and pins never reorder themselves. - Partition, not mirror: pinned rows are split out before the project tree is built (
partitionSidebarWorkspacesByPinned+buildDashboardSidebarPinnedWorkspaces, sharing a newdecorateSidebarWorkspacehelper), so a pinned workspace renders only in the Pinned section. - Lifecycle:
tombstoneSidebarWorkspaceRecord(the soleisHidden = truewriter) clearspinnedAt, so a hidden/removed workspace can never resurrect pre-pinned. - Rename: the pre-existing "pinned" concept (local-main-first sort) is now
isLocalMainWorkspace/normalizeMainFirstto avoid vocabulary collision. - Header style: the Pinned header uses the same 10px uppercase micro-label treatment as the PROJECTS header.
Known v1 interactions with #5956: the project filter doesn't filter the Pinned section (pins stay visible), and pinned rows don't contribute to a project's "last updated" sort ranking.
Validation
bun run typecheckclean,bun run lintclean- Schema heal + partition/pinned-builder unit tests added; full
_authenticatedroute suite 250/250 - ExecPlan included at
apps/desktop/plans/done/20260725-1947-sidebar-workspace-pinning.md
https://claude.ai/code/session_01WThJeeNfE44b1Xd1ZQsaLr
Summary by cubic
Add workspace pinning to the v2 dashboard sidebar. Right‑click a workspace to Pin it into a new top Pinned section with a project avatar; Unpin returns it to the same project/section/order, and pins persist across restarts.
-
New Features
- New Pinned section above projects; collapsed rail shows an icon stack with a divider.
- Pinned rows show a project avatar, keep PR status, and the active‑project UI works when the active workspace is pinned.
- Storage:
sidebarState.pinnedAtinv2WorkspaceLocalState(heal‑forward); ordered by pin time ascending; tombstoning unpins; removing a project clears pins on kept main‑workspace rows. - API: idempotent
setWorkspacePinned(workspaceId, projectId, pinned); creates a local‑state row when pinning an auto‑included main workspace. - Partitioned rendering: pins are removed from project groups; project filter does not hide pins; pins don’t affect a project’s “last updated” sort; context menu adds Pin/Unpin as the first item.
-
Refactors
- Renamed the old “pinned” concept to
isLocalMainWorkspaceandnormalizeMainFirst. - Added pinned partition/build helpers and tests; updated fixtures.
- Renamed the old “pinned” concept to
Summary by CodeRabbit
- New Features
- Workspaces can now be pinned or unpinned from the sidebar context menu.
- Pinned workspaces are shown in a dedicated top “Pinned” section, with support for both collapsed and expanded sidebar layouts.
- Pinned state persists across app restarts, with newly pinned items added to the bottom of the pinned list.
- Bug Fixes
- Workspaces that are hidden or removed no longer remain in the Pinned section.
- Unpinning returns a workspace to its prior project/section and ordering position.