feat: multiple workspaces (recents, ⌃R switcher, multi-window)#9
Merged
Conversation
Generalize the single remembered storage choice into a workspace registry: every opened folder (plus in-browser IndexedDB on web) is remembered with recency, surfaced as an "Open Recent" submenu in the orb menu and a VS Code-style ⌃R switcher (filter, ↵ open, ⌘↵ new window, ⌘⌫ remove). On desktop each workspace can open in its own native window; several at once, with focus-if-open so the same folder never lands in two windows. Relaunch restores the last-active workspace only. - Registry: IndexedDB bumped v1→v2 with a `workspaces` store + last-active pointer (src/storage/workspaceRegistry.ts, absorbs+deletes handlePersistence.ts); lazy, idempotent migration of the old single choice. - useNotesStorage reworked into a workspace lifecycle (bootstrap by per-window assignment → last-active → choosing; openWorkspace/openInNewWindow), with an opSeq supersession guard on every async path incl. the post-activation writes. - Rust: a WindowWorkspaces label→wsId map (+ a pending set so a concurrent open can't spawn a duplicate) and 4 commands; ws-N windows cloned from the main window config; main-only hide-on-close; focused/visible-window-targeted menu:about. - Desktop is folder-first; the in-browser row is offered on web only. - Shared useListboxNav hook + highlightMatch power both the switcher and the move-to picker (the latter's latent dead-keyboard bug fixed along the way); useShortcuts skips global chords while a modal is open. Includes fixes from a max-effort /code-review pass (15 findings: activation races, IndexedDB open onblocked/onversionchange, reset() clearLastActive, error-surfacing on failed opens, and more). All tests/typecheck/lint/cargo green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- open_workspace_window: rewrite the label-pruning loop as `while let` (clippy::while-let-loop, -D warnings) - reformat CLAUDE.md emphasis to Prettier's `_switch_` style Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The five "one shared picker" tests open a real Gravity popup and render the full icon catalog in jsdom (~2-3s locally). On CI's slower runner they exceed Vitest's 5000ms default and time out. The heaviest test in the block (reopen) already carried an explicit 15_000 timeout — extend the same to its siblings. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This PR adds a batch of new DOM tests (workspace switcher, multi-window), increasing parallel load on CI's runner and slowing every test — enough to push the already-heavy icon-picker popup tests past Vitest's 5s default (one flaked at 5087ms after passing at 4321ms the prior run). Set testTimeout=15s on the dom project so every boundary-slow Gravity-render test gets headroom, and drop the per-test 15_000 overrides I'd added to NoteList (the global supersedes them; that file now matches main). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Generalizes the single remembered storage choice into a workspace registry — every opened folder (plus in-browser IndexedDB on web) is remembered with recency and surfaced three ways:
On relaunch the main window restores the last-active workspace only. Desktop is folder-first; in-browser storage is offered on the web build only.
How
src/storage/workspaceRegistry.ts, absorbs + deleteshandlePersistence.ts): IndexedDB bumped v1→v2 with aworkspacesstore + a last-active pointer; a lazy, idempotent migration seeds the old single choice into a deterministic entry.useNotesStoragereworked into a workspace lifecycle (bootstrap by per-window assignment → last-active → choosing;openWorkspace/openInNewWindow/removeWorkspace), with anopSeqsupersession ticket on every async path (including the detached post-activation writes).src-tauri/src/lib.rs): aWindowWorkspaceslabel→wsId map (+ apendingset so a concurrent open can't spawn a duplicate) and four commands;ws-Nwindows cloned from the main-window config; main-only hide-on-close; focused/visible-window-targetedmenu:about. Capabilities gainws-*+allow-destroy/allow-set-title/allow-set-theme.useListboxNav+highlightMatchback both the switcher and the ⌘⇧M move-to picker (fixing the latter's latent Dialog-focus-manager dead-keyboard bug);useShortcutsskips global chords while a[role="dialog"]modal is open.gravity-notes:<wsId>:*localStorage keys (adopting the legacy un-namespaced value once).Review
Includes the fixes from a max-effort
/code-reviewpass — 15 findings verified and addressed (activation races, IndexedDBopenonblocked/onversionchange,reset()clearing the launch pointer, error-surfacing on failed opens, the duplicate-window race, and more).Test plan
npm test(834 passing),npm run typecheck,npm run lint(0 errors),cargo test(21 passing),npm run build— all green.workspaceRegistry.test.ts,WorkspaceSwitcherDialog.test.tsx; reworkeduseNotesStorage+useShortcuts+ dialog/TopBar/FolderGate tests.🤖 Generated with Claude Code