Skip to content

Commit edddea4

Browse files
authored
Merge pull request #39 from Zeus-Deus/workspaces-overview-ui
feat(workspaces): account-wide overview + cross-device sync + safety net
2 parents a56074d + 217e47f commit edddea4

40 files changed

Lines changed: 7660 additions & 61 deletions

docs/INDEX.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ If the docs themselves feel stale or scattered, also read `docs/reference/DOCS_R
6666
- Project memory: `docs/features/project-memory.md`
6767
- Command palette: `docs/features/command-palette.md`
6868
- Workspace creation: `docs/features/workspace-creation.md`
69+
- Workspaces overview (full-screen device-grouped list with filters + push/pull actions): `docs/features/workspaces-overview.md`
70+
- Workspaces sync (cross-device workspace registry powering the overview's sibling-device rows): `docs/features/workspaces-sync.md`
6971
- IDE integration: `docs/features/ide-integration.md`
7072
- Notifications: `docs/features/notifications.md`
7173
- Auto-update: `docs/features/auto-update.md`
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
# Workspaces Overview
2+
3+
- Purpose: Describe the full-screen "Workspaces" overlay that lists every workspace this device tracks — local + every remote host it has pushed to — with filters, search, and per-row push/pull/open actions.
4+
- Audience: Anyone touching the sidebar, the workspace push/pull flow, or the overview UI itself.
5+
- Authority: Canonical feature doc for the overview surface.
6+
- Update when: The sidebar entry, the overview layout, the filter set, or the per-row actions change.
7+
- Read next: `docs/features/remote-hosts.md` for the push/pull pipeline underneath, `docs/features/workspace-creation.md` for the new-workspace flow.
8+
9+
## What This Feature Is
10+
11+
A single pane that answers "where do all my workspaces live?" — across this device and every host it has pushed to. Reached from the left sidebar (the `Workspaces` button under `Automations`), opens as a full-screen overlay with the same chrome as Settings and Automations.
12+
13+
Before this feature the sidebar only showed workspaces grouped by project on the current device. Once you pushed a workspace to a remote host the cloud icon told you it was remote, but there was no surface for "show me everything I have, everywhere." The overview is that surface.
14+
15+
## Current Model
16+
17+
### Entry point
18+
19+
- `src/components/layout/sidebar-action-row.tsx` renders the `Workspaces` button under `Automations`. Click → `useUIStore.setShowWorkspacesOverview(true)`.
20+
- `src/components/layout/app-shell.tsx` early-returns `<WorkspacesOverviewView />` whenever `showWorkspacesOverview` is true — same pattern as `showAutomations` and `showSettings`.
21+
- Escape and the back button both close the overlay.
22+
23+
### Data source
24+
25+
- Workspaces come from `useAppStore((s) => s.appState?.workspaces)`. Every `WorkspaceSnapshot` already carries `host_id?: number | null` (added in the cloud-push series — see `remote-hosts.md`). `null` means local; a number references the local `hosts` table row id.
26+
- Host metadata comes from `useHostsStore()` — the same shared cache the `DevicePicker` and workspace context menu use, so the overview pays one IPC round-trip on first mount and reads from cache thereafter.
27+
- Project names + the basename / "Home" disambiguation reuse `groupWorkspacesByProject(workspaces, homeDir)` from `src/stores/app-store.ts`, so projects appear with the exact labels the sidebar gives them.
28+
29+
### Layout
30+
31+
- Sticky filter bar at the top: search, project, device, status, sort.
32+
- Result-count row underneath with a `New workspace` shortcut.
33+
- Body groups workspaces into **device sections**, in this order:
34+
1. `This device` (the local bucket — emerald accent + custom laptop glyph)
35+
2. Each configured host in the order they appear in `Settings → Hosts` (sky-blue accent + cloud icon)
36+
3. Any `Removed host` orphan section, if a workspace still references a deleted host id (so rows never silently disappear)
37+
- Each device section header shows the host name + ssh target, a workspace count, and a "filtered" pill if any of the bucket's workspaces are hidden by the active filter.
38+
- Workspaces render as compact two-column cards (single column under `md`): status dot · title · project name · branch · git stats · hover-reveal action menu.
39+
40+
### Status semantics
41+
42+
The status dot to the left of each workspace title has two precedence layers. **Live agent status wins** when present — sourced from `getWorkspaceStatus(workspace.surfaces, appState.pane_statuses)`, the same path the sidebar uses, so when a pane flips between idle / working / waiting-on-input / review the overview row repaints automatically without polling:
43+
44+
| Live status | Treatment |
45+
|---|---|
46+
| `working` | Amber pulsing dot via `<StatusIndicator>`; meta line shows `· agent working` |
47+
| `permission` | Red pulsing dot; meta line shows `· needs input` |
48+
| `review` | Emerald dot; meta line shows `· ready to review` |
49+
50+
When no live status is set, the static dot falls back to:
51+
52+
| Color | Meaning |
53+
|---|---|
54+
| Emerald | Currently open in this app (matches `appState.active_workspace_id`) |
55+
| Sky blue | Lives on a remote host |
56+
| Violet | OpenFlow workspace |
57+
| Amber | Push or pull in flight (replaces the action menu with a spinner) |
58+
| Muted | Local, not currently attached |
59+
60+
The attached card also gets an emerald border + soft ring so it's findable at a glance even when the dot scrolls off.
61+
62+
### Filters
63+
64+
- **Search** — case-insensitive substring match against title, branch, or project name. Clear button appears once non-empty.
65+
- **Project** — exact-path match using the project paths that `groupWorkspacesByProject` derives.
66+
- **Device**`All`, `This device`, or a specific host.
67+
- **Status**`Any status`, `Currently open`, `On a remote host`, `Has uncommitted work`.
68+
- **Sort**`Recently active` (notifications + dirty-git proxy → name tie-break), `Name`, `Branch`.
69+
- A `Clear` chip appears whenever any non-default filter is set.
70+
71+
### Per-row actions
72+
73+
The trailing `` menu (hover- and focus-revealed) holds:
74+
75+
- **Open workspace** — calls `activateWorkspace(workspaceId)`, closes the overlay, lands the user in the workspace.
76+
- **Copy branch name** — clipboard copy, disabled when no branch.
77+
- **Rename…**`window.prompt` + `renameWorkspace`.
78+
- **Push to host…** — submenu of every configured host. Each entry calls `workspacePushToHost(workspaceId, hostId)` (reuses the same Tauri command the sidebar context menu uses). Disabled with a tooltip when zero hosts are configured.
79+
- **Pull back to this device** — only appears for workspaces with a non-null `host_id`. Calls `workspacePullBack(workspaceId)`.
80+
- **Delete worktree… / Close workspace…** — destructive, gated by `window.confirm`. Worktrees call `closeWorkspaceWithWorktree` with the same flags as the sidebar's remove dialog.
81+
82+
Whole card is also clickable — clicking opens. The action menu stops event propagation so it never accidentally opens the workspace while you're navigating the submenu.
83+
84+
The push/pull flight indicator lives on the shared `useAppStore.workspacePushPullInFlight` slot, so the same workspace shows a spinner in the sidebar **and** the overview at the same time — there's exactly one in-flight push or pull per workspace.
85+
86+
## What Works Today
87+
88+
- Sidebar button under Automations opens the full-screen overview; Escape and back button close it.
89+
- Every workspace this device tracks is listed, grouped by device.
90+
- Pushed workspaces visibly migrate to the matching device bucket once the push succeeds.
91+
- Filters: search (title / branch / project), project, device, status, sort.
92+
- Per-row actions: open, copy branch, rename, push to any configured host, pull back, delete.
93+
- Empty states for "no workspaces yet" (offers `New workspace`) and "filters hide everything" (offers `Clear filters`).
94+
- "Removed host" orphan bucket prevents workspaces from silently disappearing when a host row is deleted.
95+
- One push/pull spinner per workspace, shared with the sidebar.
96+
- Live agent status (working / needs-input / ready-to-review) reflected on every row in real time — same source as the sidebar's `StatusIndicator`, no polling.
97+
- First-run welcome banner (`WelcomeBanner` component) with three state-aware variants — brand-new (no devices, no siblings, with `Add a device` CTA), device-configured (nudges first push), has-siblings (counts visible cross-device workspaces with pull instructions). Dismissable; persists in localStorage and never re-shows.
98+
99+
## Current Constraints
100+
101+
- **Sibling-device adoption is deferred.** The overview SHOWS workspaces from other devices of the same account (via the cross-device workspace sync — see `docs/features/workspaces-sync.md`). The "Pull to this device" action on sibling rows is disabled in v1; the affordance is visible with a "coming soon" tooltip and will land in a follow-up. Until then, sibling-device rows are read-only — you can see them and their metadata, but you adopt them by visiting the device they live on.
102+
- **No created-at field** on `WorkspaceSnapshot` yet, so the "Created within" time filter UI is intentionally **not** rendered — would be misleading without the data. The `Recently active` sort uses a proxy (notification count + dirty-git heuristic + name tie-break) until a real `last_active_at` is plumbed through.
103+
- **No bulk actions.** Push N workspaces in one go isn't supported; each row pushes individually.
104+
- **Window-prompt rename.** Matches the sidebar context menu; if/when that switches to an inline edit the overview should follow.
105+
- **Delete uses window.confirm**, not the richer `RemoveWorkspaceDialog` the sidebar pops. Fine for now — the overview is the "manage many at once" surface where a heavyweight modal per row would be noisy — but if `RemoveWorkspaceDialog` grows more functionality we should reconsider.
106+
107+
## Important Touch Points
108+
109+
- `src/components/workspaces-overview/workspaces-overview-view.tsx` — full-screen overlay shell (WindowChrome + back button + Escape).
110+
- `src/components/workspaces-overview/workspaces-overview-section.tsx` — filter bar, device bucketing, sort.
111+
- `src/components/workspaces-overview/workspace-overview-row.tsx` — card component + action menu + push/pull wiring.
112+
- `src/components/layout/sidebar-action-row.tsx` — sidebar entry point.
113+
- `src/components/layout/app-shell.tsx` — overlay mount.
114+
- `src/stores/ui-store.ts``showWorkspacesOverview` boolean + setter.
115+
- `src/stores/app-store.ts``workspaces`, `active_workspace_id`, `workspacePushPullInFlight`, `groupWorkspacesByProject`.
116+
- `src/stores/hosts-store.ts` — shared `useHostsStore` cache.
117+
- `src/tauri/commands.ts``workspacePushToHost`, `workspacePullBack`, `activateWorkspace`, `renameWorkspace`, `closeWorkspace`, `closeWorkspaceWithWorktree`.
118+
- `src/tauri/types.ts``WorkspaceSnapshot.host_id` (the field the overview keys off).
119+
120+
## Notes
121+
122+
- The overview is read-only with regard to the workspace data model — it does not introduce new persistence, new Tauri commands, or new sync paths. Every action it surfaces was already shipping in the sidebar context menu; the overview just makes them discoverable from one place.
123+
- When cross-device workspace sync ships, the only change here should be that `useAppStore.workspaces` includes the synced remote rows. The bucketing already groups by `host_id` correctly, so no UI rework is expected.
124+
- Status colors and density follow `docs/features/automations.md`'s reference implementation so the two pane types feel like the same family.

0 commit comments

Comments
 (0)