feat(desktop): add top bar ports dropdown as an alternative ports layout

Merged
AV
3mo ago
+691-569

Summary

Adds a topbar ports-display mode as an A/B-able alternative to the existing inline sidebar chip — both coexist, gated by a new settings toggle so they can be compared side by side.

  • Today, detected dev-server ports render as a chip under each workspace item in the sidebar ("inline" mode, unchanged default).
  • This adds a compact top-bar pill (radio-tower icon + live count, hidden when the total is 0) that opens a workspace-grouped dropdown: clickable group headers (with a remote tag for non-local hosts and a hover-revealed close-all), and per-port rows showing a :port + label/process with hover-revealed open / open-in-external-browser / copy / close actions. The footer shows the live total and a global close-all.
  • Settings → Experimental gets a Switch labeled "Ports in top bar dropdown".

Implementation notes

  • Single zustand store (stores/inline-workspace-ports.ts) is now mode-based ("inline" | "topbar") instead of boolean, with a migration that folds both the old boolean shape and a since-removed "panel" mode into the new shape.
  • The existing ports-data provider (DashboardSidebarPortsProvider) is hoisted from the sidebar up to _dashboard/layout.tsx so it's reachable from the top bar and the v2 workspace tab bar too, and survives remounts on workspace navigation instead of blinking the pill to empty state.
  • useDashboardSidebarPortsData / useDashboardSidebarPortKill moved from DashboardSidebarPortsList/hooks/ to DashboardSidebar/hooks/ (pure relocation, all import sites updated) since they're now shared by the inline chip, the topbar dropdown, and the workspace context menu.
  • The pill mounts in exactly one of three mutually-exclusive chrome locations depending on what's visible: the expanded sidebar header, the TopBar, or the v2 workspace tab bar's trailing slot (collapsed rail).
  • Along the way, fixed a pane-store flash on v2 workspace switches (useV2WorkspacePaneLayout now seeds synchronously from the persisted collection row instead of an empty state) and gated BackgroundTerminalsButton on isLayoutReady to stop it flashing a bogus count — both needed since the new pill shares the same tab-bar trailing slot.

Supersedes #5998.

Test plan

  • bun run lint clean
  • bun run --cwd apps/desktop typecheck clean
  • Verified live in the running dev app via CDP: settings toggle switches to topbar mode and persists (localStorage confirms {"state":{"mode":"topbar"},"version":2}); pill renders in the sidebar header next to the nav controls with the correct live count
  • Full manual pass of the remaining checklist (dropdown open/copy/close actions, three-way pill placement across chrome states, migration from the old boolean/"panel" shapes, no tab-bar flash on fast workspace switching) — recommend a manual spot-check before merge

https://claude.ai/code/session_01QMevhGg9VnuZC6SnQbV6qE


Summary by cubic

Adds a top‑bar ports dropdown as an alternative to the inline sidebar chip to consolidate dev‑server ports and reduce sidebar noise. Previously ports only appeared inline; new mode shows a compact pill with a live count that opens a workspace‑grouped dropdown with open, copy URL, close, and close‑all actions.

  • Settings: Experimental toggle “Ports in top bar dropdown” switches between “inline” (default) and “topbar”. The inline-workspace-ports store now persists a mode; prior boolean and removed "panel" values migrate to "inline"/"topbar".
  • Data flow: DashboardSidebarPortsProvider is hoisted to _dashboard/layout.tsx; the “enabled” gate moved into the data hook so the provider’s identity is stable and the dashboard does not remount on sidebar collapse/expand. Polling runs only when v2 is on and either topbar mode is active or inline mode with the sidebar open and not collapsed.
  • Placement: the pill renders in one place based on visible chrome—expanded sidebar header, the TopBar, or the v2 workspace tab bar trailing slot—and hides when the total count is 0. The top bar suppresses a duplicate when the sidebar hosts chrome.
  • Actions: per‑port open (primary/external), copy URL (local ports only; via shared clipboard hook), and close; per‑workspace and global close‑all with toasts.
  • Cleanup: removed the bottom DashboardSidebarPortsList; shared hooks useDashboardSidebarPortsData/useDashboardSidebarPortKill moved under DashboardSidebar/hooks/; added TopBarPortsDropdown.
  • Related fixes: seed useV2WorkspacePaneLayout from the persisted row and only sync once the live query is ready to prevent a tab‑bar flash; gate BackgroundTerminalsButton on isLayoutReady; bump minimum workspace sidebar width to 225.

Summary by CodeRabbit

  • New Features
    • Added a top-bar ports dropdown showing live workspace ports.
    • Open ports, copy URLs, navigate to workspaces, or close individual, grouped, or all ports.
    • Added experimental settings for top-bar or inline port display.
  • Improvements
    • Port data now refreshes and updates live across the dashboard.
    • Preserved workspace pane layout during navigation.
    • Slightly increased the minimum workspace sidebar width.