You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
feat(spaces): add nested ownership and unified navigation - #323
Add nested Space ownership with canonical child entries, reparenting, promotion, cycle protection, and confirmed subtree deletion. Persist hierarchy metadata in disk, SQLite, and PostgreSQL storage.
Support collecting and moving mixed selections, including Frames, owned child entries, shortcuts, and conversations, with ownership compensation on failure.
Share the searchable hierarchy picker between Move and Add Space Shortcut. Integrate responsive breadcrumbs and shortcut-return navigation into the existing header.
Simplify Space cards, add Home top-level/all views and search, and filter World to top-level entries without deleting its saved layout.
Keep creation, collection, and movement in the source Space, with explicit destination navigation and a readiness-gated, reduced-motion-aware entrance animation.
Fix slow-metadata parent selection, stale hidden World selections, and initial World viewport fitting. Update architecture documentation and regression coverage.
Validation
Repository-level pnpm format, pnpm lint:fix, and pnpm typecheck.
Localization parity, license headers, and patch whitespace checks.
140 server tests covering hierarchy, mixed movement, conversation lifecycle, World entries, and deletion.
118 SQLite contract and integration tests.
115 targeted frontend tests and 7 shared shortcut-engine tests.
14 Playwright scenarios covering Space workflows, responsive header geometry, metadata races, selection cleanup, and viewport readiness.
Scope and limitations
Adds nullable parent metadata through SQLite/PostgreSQL schema migration version 2; existing Spaces remain top-level.
Hierarchy serialization is process-local, not a distributed lock or a crash-atomic transaction across Spaces.
PostgreSQL remote integration tests and large-Workspace performance measurements were not run.
Add canonical child Space ownership, mixed-selection collection and movement, protected recursive deletion, and hierarchy metadata across storage backends.
Unify Space pickers and breadcrumbs, refine shortcut cards and stay-in-place workflows, and cover metadata-loading, World selection, and viewport readiness regressions.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The flow-space rectangle check runs before the screen-space tap check. At 10% zoom, a touch moving 3px on each axis is below the 8px tap threshold but produces a 30×30 flow rectangle. Child creation therefore collects enclosed nodes instead of creating an empty child. When onClick is provided, recognize an eligible tap before committing a rectangle, while preserving Frame-only behavior. Add a zoomed-out tap regression test.
Preserve hierarchy when metadata refresh fails
apps/web/src/utils/spaceHierarchy.ts:19
A missing summary can also mean an invalidated cache entry, not an unknown Space. refreshSpaceTitles([id]) deletes the entire summary before fetching and leaves it absent on failure (workspaceStore.ts:193–197,250–258). A successful rename triggers this path, so a previously known top-level Space is hidden from World until metadata refresh succeeds; nested Spaces also lose their breadcrumb and canonical-entry information. Preserve last-confirmed hierarchy independently of invalidated counts/timestamps, while still hiding genuinely unknown entries during initial loading. Cover failed post-rename refreshes in the regression tests.
Validate merged PUT identities and query only changed shortcut targets. Protect destructive commands and history restoration while ownership metadata is unavailable. Reconcile partial deletion, retain hierarchy on refresh failure, clean World selection after load, and prefer screen-space taps over zoomed rectangles.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Addressed the six inline findings and the two additional findings from the latest Copilot review in 6c64082.
Structural PUT validates merged node identities, preserving geometry-only saves while rejecting explicit entry removal/retargeting.
Hierarchy protection now compares authoritative before/after identities and reads only affected shortcut targets through the existing Space handle. Unchanged identities perform no hierarchy reads. This avoids the full-Workspace scan without introducing a second index/cache; sidecar deletion uses the same guard.
Partial deletion reconciles confirmed IDs, clears their Preview persistence, refreshes metadata, and leaves a deleted current Space while retaining the error. Deleted roots do not offer a futile retry.
World selection cleanup reruns after load completion.
Destructive commands and undo/redo check ownership before state changes or sidecar effects. Unsafe history restoration is refused without consuming the snapshot; geometry undo/redo remains supported. Received card insertions refresh ownership before destructive actions can proceed.
Metadata refresh failures retain confirmed hierarchy while invalidating counts/timestamps.
Child creation prioritizes screen-space taps over flow-space rectangles at low zoom, retaining Frame-only behavior.
Validation: repository format, lint:fix and typecheck; 149 focused server tests; 90 focused web tests; 16 Space browser scenarios, including immediate Undo after child creation and navigation after partial deletion. No remote-storage runtime was rerun locally for this revision; the new CI run remains pending.
[P2] Avoid listing the entire Workspace for every deleted subtree member. deleteSpaceTree() already computes a descendant-first plan under the hierarchy lock, then calls deleteSpace(id) for each member. This added list() reloads all remaining Spaces each time; SQL listings also fetch and decode every state_json. Deleting an N-Space tree can therefore perform quadratic topology reads while blocking other hierarchy operations. Keep the standalone deletion check, but let the validated subtree loop use an internal deletion path that preserves deletion fences and cleanup without repeating the listing.
Recheck the captured Workspace after awaiting refreshSpaceTitles(). If the user switches Workspaces during that request, the dialog can unmount while remove() continues and invokes the old onDeleted callback. SpaceActions compares the deleted IDs with the live Canvas ID, so a copied Workspace with matching IDs can be incorrectly navigated to Home. Apply the same Workspace guard used above immediately before the callback.
Exit fullscreen Preview before waiting for React Flow
Exit fullscreen Preview before waiting for React Flow. If the user expands Preview while the move-success toast is visible and then chooses Open destination, fullscreen state survives navigation. MainLayout keeps Canvas unmounted, so rfInstance stays null and the destination focus intent cannot run. Mirror the existing node deep-link handler: after validating the intent and loading state, reset fullscreen and return; only then check rfInstance. Include isPreviewFullscreen in the effect dependencies.
Publish entry insertion only after parent persistence
apps/web/src/store/canvasStore.ts:1952
This refresh can finish before ownership is persisted. setSpaceParent() publishes the entry insertion through executeOnServerAlreadyLocked() before awaiting repository.setParent(), while the list endpoint does not wait for the hierarchy lock. On PostgreSQL, another tab can read the old parent and cache it as ready, with no later event to correct it. The canonical entry then appears as a shortcut, and Delete takes the ordinary-node path instead of opening subtree confirmation (the server still protects the entry). Publish the insertion after the parent write completes, or make the metadata read wait for the hierarchy operation.
Node-sidecar deletion now holds the existing Workspace operation lease from before Canvas-mutex queueing through completion, with a captured Workspace-bound handle and finally-based release.
Successful subtree deletion rechecks Workspace identity after metadata refresh before invoking the navigation callback.
Per the agreed UX, Open destination preserves fullscreen Preview and consumes the Canvas focus intent without selecting or fitting nodes. Data loading, sync, and Layers remain active; exiting fullscreen does not replay the skipped focus.
Child entry insertion uses the existing deferred-publication pattern extracted from Space Move. Publication waits for parent persistence to settle, including failed/uncertain writes whose retained cards must still be synchronized; failures remain failures.
Regression coverage includes queued/in-flight deletion activation guards and lease release on failure, Workspace changes during refresh, fullscreen focus consumption, and parent-write success/refusal/failure/after-commit failure. Root format/lint completed; typechecking passed after correcting a test mock return type (the affected web check was rerun). All 204 targeted server and 25 targeted web tests passed.
The previously retained ordinary-save listing and missing-ownership threads are covered by existing fixes plus the regressions added in 0655894: ordinary commands/delta replay/structural saves do not call membership list(), and Frame deletion has no sidecar effects with unavailable ownership metadata. This update does not resolve the separate main-branch merge conflict.
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
Destructive multi-Space operations and cross-backend migrations need human review, with a deletion safeguard unresolved and PostgreSQL integration validation outstanding.
Resolve selected node and edge removal in one execution so child-Space confirmation and ownership guards cannot leave partial edge deletion. Keep edge-only deletion unchanged and cover cancellation plus single-step undo.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Filter restored selections against current hierarchy visibility
apps/web/src/hooks/areaSelectionSession.ts:71
Apply the visibility check when restoring a cancelled selection too. If a selected World entry becomes nested during an area-selection gesture, CanvasPage clears its selection, but cancel() restores the captured IDs through unguarded applySelection(). The cleanup effect does not depend on selection changes, so the hidden entry and its edges can remain selected for clipboard or Preview actions. Filter nodes and incident edges against the current hierarchy in applySelection(), and add a regression test that changes hierarchy metadata between preview and cancellation.
Set Note H1 to 24px with smaller heading and body sizes while preserving other cards and expanded editors. Invalidate cached heights and update typography coverage and documentation.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Apply captured fonts before geometry in Frame and multi-selection resize so Space cards shrink against the correct minimum width. Cover single-tick release and nested scaling through the real executor, and update the Note H1 browser expectation.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Reuse React Flow nodeLookup instead of scanning all Canvas nodes per Question subscription. Preserve row and column card behavior and cover layout changes, reparenting, and standalone reload.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Preserve nested Space list filtering and hierarchy controls alongside upstream destination-title caching and loading fixes.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
Two unresolved P2 regressions block approval: minimum-width Space cards cannot shrink via corner controls, and sub-threshold node touches can create child Spaces.
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
Cross-backend migrations, hierarchy lifecycle operations, and broad UI changes warrant final human review; PostgreSQL remote integration tests were not run.
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
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.
Summary
Validation
pnpm format,pnpm lint:fix, andpnpm typecheck.Scope and limitations