···146146N1. Define the StatusBar view model (core, pure TS)
147147------------------------------------------------------------------------------
148148149149-/packages/core/src/ui/statusbar/types.ts
150150-[ ] Define StatusBarVM (single object the UI renders):
149149+/packages/core/src/ui/statusbar.ts
150150+[x] Define StatusBarVM (single object the UI renders):
151151 - cursorWorld: { x, y } " world coords (always)
152152 - cursorScreen: { x, y }? " optional dev-only
153153 - zoomPct: number " e.g. 100, 67, 250
···179179N2. Provide selectors / derivations for StatusBarVM
180180------------------------------------------------------------------------------
181181182182-/packages/core/src/ui/statusbar/selectors.ts
183183-[ ] Implement pure functions:
182182+/packages/core/src/ui/statusbar.ts
183183+[x] Implement pure functions:
184184 - getZoomPct(state) -> number
185185 - getToolId(state) -> ToolId
186186 - getSelectionSummary(state) -> { count, kind?, bounds? }
187187 - getSnapSummary(state) -> snap summary (default safe values)
188188189189Cursor position source:
190190-[ ] Define a minimal CursorState in core (NOT persisted):
190190+[x] Define a minimal CursorState in core (NOT persisted):
191191 - cursorWorld: Vec2
192192 - cursorScreen?: Vec2
193193 - lastMoveAt: number
194194195195-[ ] Add updateCursor(world, screen?) action + reducer handler (or direct setter)
195195+[x] Add updateCursor(world, screen?) action + reducer handler (or direct setter)
196196 that ONLY touches CursorState (no history command, no persistence).
197197198198(DoD):
···202202N3. Wire cursor updates from pointer movement (apps/web)
203203------------------------------------------------------------------------------
204204205205-/apps/web/src/lib/pointer.ts
206206-[ ] On pointermove (or mousemove when not captured):
205205+/apps/web/src/lib/input.ts
206206+[x] On pointermove (or mousemove when not captured):
207207 - compute world coords using camera.screenToWorld
208208 - dispatch updateCursor(world, screen)
209209210210Performance:
211211-[ ] Throttle cursor updates:
211211+[x] Throttle cursor updates:
212212 - v0: requestAnimationFrame coalescing (only publish latest per frame)
213213 - avoid flooding render/history/persistence
214214···224224done; persistence is already hooked to history in Milestone M).
225225226226/apps/web/src/lib/status.ts
227227-[ ] Extend your persistence sink (from Milestone M) to expose a small status:
227227+[x] Extend your persistence sink (from Milestone M) to expose a small status:
228228 - pendingWrites counter (increment on enqueue, decrement on commit)
229229 - lastSavedAt timestamp (set on successful commit)
230230 - lastError (set on failed commit)
231231-[ ] Use Dexie liveQuery to observe the current board’s updatedAt from IndexedDB
231231+[x] Use Dexie liveQuery to observe the current board’s updatedAt from IndexedDB
232232 and reflect it in the UI (helps confirm persisted state across tabs).
233233234234(DoD):