|
1 | 1 | # Phase 6: Spatial canvas polish |
2 | 2 |
|
3 | 3 | > **Prerequisites:** Phase 5 done. |
4 | | -> **Status:** Complete |
| 4 | +> **Status:** In progress (2026-05-31 — status corrected after codebase audit) |
5 | 5 |
|
6 | 6 | --- |
7 | 7 |
|
8 | 8 | ## Goal |
9 | 9 |
|
10 | | -All remaining spatial interactions from the legacy checklist. |
| 10 | +Achieve parity with the legacy `/pages/:pageId` immersive spatial canvas experience. A DeepNotes page is **not** a card stack — it is a fullscreen application shell with an infinite canvas at its center, surrounded by toolbars, sidebars, and floating UI. |
11 | 11 |
|
12 | 12 | --- |
13 | 13 |
|
14 | 14 | ## Deliverables |
15 | 15 |
|
16 | | -1. **Selection** |
17 | | - - Multi-select (ctrl/cmd + click). |
18 | | - - Box selection (drag on empty canvas). |
19 | | - - Select all (`Ctrl+A`). |
20 | | - - Active element / active region tracking. |
21 | | - |
22 | | -2. **Containers** |
23 | | - - Note can contain child notes (container section enabled). |
24 | | - - Spatial container: children positioned freely inside parent. |
25 | | - - Horizontal container: children arranged in a row. |
26 | | - - Drag child out to detach. |
27 | | - - Drag note into container to attach. |
28 | | - |
29 | | -3. **Clipboard** |
30 | | - - Cut / copy / paste notes and arrows. |
31 | | - - Cross-page paste (requires serialization format). |
32 | | - |
33 | | -4. **Alignment + distribution** |
34 | | - - Align left / center / right / top / middle / bottom. |
35 | | - - Distribute horizontally / vertically. |
36 | | - |
37 | | -5. **Undo / redo** |
38 | | - - `Ctrl+Z` / `Ctrl+Shift+Z` for note operations (move, create, delete, resize). |
39 | | - - Must integrate with Yjs undo manager or a custom command stack. |
40 | | - |
41 | | -6. **Immersive page layout and state screens** |
42 | | - - Rebuild `PageEditorView.vue` as a full-screen spatial shell (no scrolling card stack): |
43 | | - - `MainToolbar` (shadcn): breadcrumb path, zoom controls, undo/redo, share, find/replace toggle. |
44 | | - - `LeftSidebar` (shadcn): Recent pages, Favorites, Selected elements, Current path. |
45 | | - - `RightSidebar` (shadcn): Page properties, Snapshots, Management, Backlinks, Collab status — collapsible panels. |
46 | | - - `TableContextMenu` (shadcn): right-click context menu on canvas. |
47 | | - - `LoadingOverlay`: centered spinner during page bootstrap. |
48 | | - - Global CSS for spatial routes: `user-select: none`, `overflow: hidden`, `touch-action: none`, `position: fixed` on body. |
49 | | - - Dedicated fullscreen state screens replacing inline error cards: |
50 | | - - `DisplayErrorScreen`, `DisplayNonExistentScreen`, `DisplayPageDeletedScreen`, `DisplayGroupDeletedScreen`, `DisplayInvitedScreen`, `DisplayRejectedScreen`, `DisplayUnauthorizedScreen`, `DisplayPasswordScreen`. |
51 | | - - Remove `PageEditorTiptapCard.vue` from the page route. The Tiptap rich-text editor becomes the head/body editing surface inside `DisplayNote.vue` only. |
52 | | - |
53 | | -7. **Note visual parity (legacy style, no Quasar)** |
54 | | - - `DisplayNote.vue` must match legacy visual behavior using Tailwind/shadcn primitives: |
55 | | - - Background color + border color from `note.color` (or hardcoded legacy color map), with subtle transparency (`/10` tint or legacy equivalent). |
56 | | - - Selection ring: `ring-2 ring-primary` or legacy blue `#2196f3`. |
57 | | - - Drag opacity: `0.7` during drag/resize. |
58 | | - - `Teleport` to a global `.display-overlay` during drag/resize to avoid parent clipping. |
59 | | - - `NoteDropZones`: invisible zones on container notes for drag-to-attach. |
60 | | - - `NoteArrowHandles`: 4 directional handles on selected notes to initiate arrow creation. |
61 | | - - `ArrowLinkZones`: zones on note edges for arrow reconnection. |
62 | | - - `NoteLinkIcon`: external-link indicator when `link.url` is set. |
63 | | - - `NoteResizeHandles`: 8 handles (nw, n, ne, e, se, s, sw, w) with correct cursors. |
64 | | - - Scrollbar handling in `NoteContent`: allow touch scroll inside notes, prevent pull-to-refresh on body. |
65 | | - - Note frame `border-radius` (`rounded-md` or legacy `7px`), shadow, and min-width matching legacy. |
66 | | - - Container section: spatial and horizontal layouts with correct child note positioning and offset math. |
67 | | - |
68 | | -8. **Arrow visual parity (legacy style, no Quasar)** |
69 | | - - `DisplayArrow.vue` must support: |
70 | | - - **Curve body** (`CurveArrow.vue`) and **line body** (`LineArrow.vue`). |
71 | | - - **Arrow heads**: `OpenHead.vue` at source/target with rotation. |
72 | | - - **Arrow label**: editable `Y.XmlFragment` rendered as an SVG `foreignObject` or HTML overlay positioned along the curve. |
73 | | - - **Hitbox**: invisible thick stroke (`stroke-width: 20`, `stroke-opacity: 0`) for easy grabbing. |
74 | | - - Drag-to-reconnect: grab an arrow endpoint and drop it onto another note. |
75 | | - - Color matching note color logic. |
76 | | - - All arrow SVG must use `overflow: visible` and absolute positioning within the world coordinate system. |
77 | | - |
78 | | -9. **Find and replace** |
79 | | - - Search across all note head/body text. |
80 | | - - Replace text. |
81 | | - |
82 | | -10. **Visual polish** |
83 | | - - Grid background. |
84 | | - - Note color inheritance. |
85 | | - - Collapsing notes. |
86 | | - - Z-index ordering. |
87 | | - - Read-only notes. |
| 16 | +### 1. Selection |
| 17 | +| Item | Status | Notes | |
| 18 | +|------|--------|-------| |
| 19 | +| Multi-select (ctrl/cmd + click) | **Done** | `SpatialPageView.vue` handles toggle via Ctrl+click | |
| 20 | +| Box selection (drag on empty canvas) | **Done** | Threshold-based drag-to-box-select implemented | |
| 21 | +| Select all (`Ctrl+A`) | **Done** | `onKeyDown` in `SpatialPageView.vue` | |
| 22 | +| Active element / active region tracking | **Partial** | `useSpatialSelection` has `activeId` but no active-region concept | |
| 23 | + |
| 24 | +### 2. Containers |
| 25 | +| Item | Status | Notes | |
| 26 | +|------|--------|-------| |
| 27 | +| Note can contain child notes | **Done** | `container.enabled` and `container.children` wired in Yjs | |
| 28 | +| Spatial container (free child positioning) | **Done** | Children rendered with world offset inside parent | |
| 29 | +| Horizontal container (children in a row) | **Not started** | No horizontal layout logic | |
| 30 | +| Drag child out to detach | **Done** | `onNoteDragEnd` + `moveNoteOutOfContainer` | |
| 31 | +| Drag note into container to attach | **Done** | Overlap-area heuristic in `SpatialPageView.vue` | |
| 32 | + |
| 33 | +### 3. Clipboard |
| 34 | +| Item | Status | Notes | |
| 35 | +|------|--------|-------| |
| 36 | +| Cut / copy / paste notes and arrows | **Done** | `copySelection`, `pastePayload` implemented | |
| 37 | +| Cross-page paste | **Not started** | Needs serialization format + clipboard persistence | |
| 38 | + |
| 39 | +### 4. Alignment + distribution |
| 40 | +| Item | Status | Notes | |
| 41 | +|------|--------|-------| |
| 42 | +| Align left/center/right/top/middle/bottom | **Done** | Keyboard shortcuts (Ctrl+Shift+L/C/R/T/M/B) | |
| 43 | +| Distribute horizontally / vertically | **Done** | Keyboard shortcuts (Ctrl+Shift+H/V) | |
| 44 | + |
| 45 | +### 5. Undo / redo |
| 46 | +| Item | Status | Notes | |
| 47 | +|------|--------|-------| |
| 48 | +| `Ctrl+Z` / `Ctrl+Shift+Z` | **Done** | `useSpatialUndoRedo` wraps Yjs UndoManager | |
| 49 | + |
| 50 | +### 6. Immersive page layout and state screens |
| 51 | +| Item | Status | Notes | |
| 52 | +|------|--------|-------| |
| 53 | +| Fullscreen `PageEditorView.vue` shell | **Not started** | Still a `max-w-3xl` card stack in `DefaultLayout.vue` | |
| 54 | +| `MainToolbar` (shadcn) | **Not started** | No page-specific toolbar exists | |
| 55 | +| `LeftSidebar` (shadcn) — Recent, Favorites, Selected, Current path | **Not started** | Zero sidebar infrastructure on page route | |
| 56 | +| `RightSidebar` (shadcn) — Note/Page/Arrow properties | **Not started** | No property panels exist | |
| 57 | +| `TableContextMenu` (shadcn) — right-click on canvas | **Not started** | No context menu on canvas | |
| 58 | +| `LoadingOverlay` during page bootstrap | **Partial** | Inline loading text in cards only | |
| 59 | +| Global CSS for spatial routes (`user-select: none`, `overflow: hidden`, `touch-action: none`) | **Not started** | `DefaultLayout.vue` does the opposite | |
| 60 | +| Remove `PageEditorTiptapCard.vue` from page route | **Not started** | Still rendered at bottom of card stack | |
| 61 | +| Dedicated fullscreen state screens (8 total) | **Not started** | No `DisplayErrorScreen`, `DisplayNonExistentScreen`, etc. | |
| 62 | + |
| 63 | +### 7. Note visual parity (legacy style, no Quasar) |
| 64 | +| Item | Status | Notes | |
| 65 | +|------|--------|-------| |
| 66 | +| Background + border color from `note.color` | **Partial** | Hardcoded 10-color map with `/18` opacity tint | |
| 67 | +| Selection ring | **Partial** | `ring-2 ring-primary` exists, but not legacy blue `#2196f3` | |
| 68 | +| Drag opacity (`0.7`) | **Not started** | No opacity change during drag | |
| 69 | +| `Teleport` to global overlay during drag/resize | **Not started** | Notes drag inside parent; no overlay portal | |
| 70 | +| `NoteDropZones` | **Not started** | No invisible drop zones on container notes | |
| 71 | +| `NoteArrowHandles` — 4 directional arrow handles | **Partial** | 4 small dots exist, but not full arrow-creation flow | |
| 72 | +| `ArrowLinkZones` | **Not started** | No edge zones for arrow reconnection | |
| 73 | +| `NoteLinkIcon` (external link indicator) | **Not started** | No link icon when `link.url` set | |
| 74 | +| `NoteResizeHandles` — 8 handles | **Done** | NW, N, NE, E, SE, S, SW, W with correct cursors | |
| 75 | +| Scrollbar handling in `NoteContent` | **Not started** | No pull-to-refresh prevention | |
| 76 | +| Note frame `border-radius`, shadow, min-width | **Partial** | `rounded-md border shadow-sm` used; exact pixel parity untested | |
| 77 | +| Container section — spatial layout | **Done** | Free child positioning inside parent | |
| 78 | +| Container section — horizontal layout | **Not started** | No row layout | |
| 79 | + |
| 80 | +### 8. Arrow visual parity (legacy style, no Quasar) |
| 81 | +| Item | Status | Notes | |
| 82 | +|------|--------|-------| |
| 83 | +| Curve body (`CurveArrow.vue`) | **Not started** | Only straight SVG `<line>` exists | |
| 84 | +| Line body (`LineArrow.vue`) | **Partial** | Straight line is default, but no `bodyType` switching | |
| 85 | +| Arrow heads (`OpenHead.vue`) | **Not started** | No arrowheads rendered | |
| 86 | +| Arrow label (editable `Y.XmlFragment`) | **Not started** | No label support | |
| 87 | +| Hitbox (thick invisible stroke) | **Not started** | Thin `cursor-pointer` line only | |
| 88 | +| Drag-to-reconnect | **Not started** | No endpoint grabbing | |
| 89 | +| Color matching note color logic | **Not started** | Stroke uses `currentColor` or `var(--primary)` | |
| 90 | + |
| 91 | +### 9. Find and replace |
| 92 | +| Item | Status | Notes | |
| 93 | +|------|--------|-------| |
| 94 | +| Search across note head/body | **Partial** | `find-replace.ts` exists but no UI triggered from page editor | |
| 95 | +| Replace text | **Partial** | Logic exists but no UI in page editor | |
| 96 | + |
| 97 | +### 10. Visual polish |
| 98 | +| Item | Status | Notes | |
| 99 | +|------|--------|-------| |
| 100 | +| Grid background | **Done** | CSS `linear-gradient` grid in `SpatialWorldCanvas.vue` | |
| 101 | +| Note color inheritance | **Done** | `inherit` flag + parent color cascade | |
| 102 | +| Collapsing notes | **Done** | Chevron toggle + collapsed state wired | |
| 103 | +| Z-index ordering | **Done** | `notesByZIndex` computed sort | |
| 104 | +| Read-only notes | **Partial** | `opacity-70` class, but no full read-only styling | |
88 | 105 |
|
89 | 106 | --- |
90 | 107 |
|
91 | 108 | ## Verification |
92 | 109 |
|
93 | | -- Each deliverable has a test (unit, component, or integration). |
94 | | -- Phase 1 checklist is >80% marked done. |
| 110 | +- [ ] Each deliverable has a test (unit, component, or integration). |
| 111 | +- [ ] Phase 1 checklist is >80% marked done. |
95 | 112 |
|
96 | 113 | --- |
97 | 114 |
|
|
0 commit comments