Skip to content

feat(M3): Yjs local persistence + autosave + undo/redo#6

Merged
pedrobritx merged 2 commits into
mainfrom
claude/modest-curie-SWrWP
May 28, 2026
Merged

feat(M3): Yjs local persistence + autosave + undo/redo#6
pedrobritx merged 2 commits into
mainfrom
claude/modest-curie-SWrWP

Conversation

@pedrobritx
Copy link
Copy Markdown
Owner

Summary

  • Yjs local persistence: Replaces the in-memory Zustand shape store with a Y.Doc backed by y-indexeddb. Canvas state survives page reload with zero explicit save calls — IndexedDB syncs on every transaction automatically.
  • Autosave indicator: SaveStatus component reads synced + lastSaved from the shape store and shows a fixed "Saved just now / Xs ago" badge once the board is loaded.
  • Undo/redo: Y.UndoManager scoped per page; Ctrl+Z / Ctrl+Shift+Z (and Ctrl+Y) wired in CanvasStage. Stack state exposed via useUndoManager hook.
  • Zero breaking changes to tools/renderers: public ShapeStore interface (listShapes, addShape, updateShape, deleteShape, transact) is unchanged.

Changed files

File Change
packages/sync/src/boardDoc.ts NEW — module-level Y.Doc cache keyed by boardId
packages/sync/src/indexedDbProvider.ts NEW — IndexeddbPersistence cache keyed by boardId
packages/sync/src/pageMap.ts NEW — findPageMap (read-only) + getPageMap (create-on-write)
packages/sync/src/index.ts Replaced stub with real exports
packages/canvas/src/store/shapeStore.ts Rewritten — Y.Doc-backed, adds initBoard, synced, lastSaved
packages/canvas/src/hooks/useUndoManager.ts NEW — Y.UndoManager hook per page
packages/canvas/src/CanvasStage.tsx Add useUndoManager, wire Ctrl+Z/Y/Shift+Z
apps/web/src/routes/Board.tsx Await initBoard, show spinner, render <SaveStatus />
apps/web/src/features/canvas/SaveStatus.tsx NEW — fixed badge with relative timestamp

Test plan

  • Open a board, draw shapes — reload the page — shapes persist
  • Draw shapes, Ctrl+Z — shapes undo step by step
  • Ctrl+Shift+Z / Ctrl+Y — shapes redo
  • "Saved just now" badge appears in bottom-right after drawing
  • Loading spinner shows briefly on first open; disappears once IndexedDB syncs
  • Navigate to a different board — spinner shows, previous board's shapes don't bleed through
  • pnpm typecheck passes (all 5 packages)

https://claude.ai/code/session_01GqTM9M6Nsoy4GTWQsdYUxg


Generated by Claude Code

claude added 2 commits May 28, 2026 17:36
Replaces the in-memory Zustand shape store with a Yjs Y.Doc persisted
to IndexedDB via y-indexeddb. Canvas state survives page reload without
any explicit save action.

- @notux/sync: boardDoc (Y.Doc cache), indexedDbProvider (IndexedDB
  persistence cache), pageMap (getPageMap / findPageMap helpers)
- shapeStore: rewritten around Y.Map<pageId, Y.Map<shapeId, YShape>>;
  adds initBoard(boardId) with promise-cache for idempotency, synced
  and lastSaved Zustand fields for UI
- useUndoManager hook: Y.UndoManager scoped per page, exposes undo/redo
  + canUndo/canRedo; wired to Ctrl+Z / Ctrl+Shift+Z in CanvasStage
- Board.tsx: awaits initBoard before rendering canvas, shows spinner
- SaveStatus.tsx: fixed badge showing "Saved [relative time]" once synced

https://claude.ai/code/session_01GqTM9M6Nsoy4GTWQsdYUxg
The lockfile was missing @notux/sync and yjs entries for packages/canvas
because pnpm install was run before the manual package.json edits were
applied. This caused CI --frozen-lockfile to fail immediately.

https://claude.ai/code/session_01GqTM9M6Nsoy4GTWQsdYUxg
@pedrobritx pedrobritx marked this pull request as ready for review May 28, 2026 17:46
@pedrobritx pedrobritx merged commit 6cda203 into main May 28, 2026
4 of 7 checks passed
@pedrobritx pedrobritx deleted the claude/modest-curie-SWrWP branch May 28, 2026 17:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants