Mind map: changing the root node color does not propagate to other viewers and is not persisted on reload #94
Labels
No labels
prio_critical
prio_low
type_bug
type_contact
type_issue
type_lead
type_question
type_story
type_task
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
lhumina_code/hero_whiteboard#94
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Editing the root node color on a mind map has two problems:
Steps to reproduce
Central Idearoot node.panel (e.g. blue → black).
Expected
Actual
reload picks up any change — and even then it picks up the old color,
because the new value was never persisted.
change was never saved.
Implementation Spec — Issue #94: Mind map root color does not sync or persist
Objective
Fix two related bugs in the mind-map Properties panel so that changing the root node's color (and, by symmetry, the root node's text) is both persisted to the server and broadcast live to all other viewers, using the existing realtime/persistence infra. No new RPC methods, no schema changes, smallest possible diff.
Root Cause (one bug, one fix)
In
properties.js, theprop-mm-root-colorandprop-mm-root-textinputlisteners mutatecurrentNode._mmState.tree.color/.textand callWhiteboardMindmap.redraw(currentNode)but never callWhiteboardSync.onUpdate(currentNode).WhiteboardSync.onUpdate(node)already does BOTH in this codebase:rpcCall('object.update', { ..., data })— persistence (so reload retains the new color)wsSendThrottled({ type: 'object.updated', data })— live broadcast (so tab B updates without reload)serializeForServeralready pulls_mmState.treeintodata.treeformindmapobjects, and the receiver branch inapplySyncUpdatealready replaces_mmStatewholesale fromdata.treeand callsWhiteboardMindmap.redraw(node). Color is stored per tree node ontree.color— there is no separaterootColor/rootFillfield; the root just renders withfill: node.colorwhile non-root nodes render withfill: '#2b3035'andstroke: node.color. So as soon as the root'stree.colorchange reaches the server / peers, both rendering and persistence work end-to-end. The receiver path already handles it; only the sender path is broken.Comparison points that already work and prove the model:
prop-mm-titleinput handler — callsWhiteboardSync.onUpdate(currentNode).prop-mm-directionchange handler — callsWhiteboardSync.onUpdate(currentNode).editMindmapNode— callsWhiteboardSync.onUpdate(group).WhiteboardSync.onUpdate(group).<input type="color">firesinputcontinuously while the user drags, butWhiteboardSync.onUpdatealready debounces RPC writes viapendingUpdates/scheduleFlushand the WS broadcast goes throughwsSendThrottled. No additional throttling needed.Requirements
prop-mm-root-text), since it has the identical bug.Files to Modify
crates/hero_whiteboard_ui/static/web/js/whiteboard/properties.js— add one line to each of two existing handlers.Files to Leave Alone
crates/hero_whiteboard_ui/static/web/js/whiteboard/mindmap.js— render and data model are correct; root color is correctly read fromtree.color;_mmState.treeis the source of truth.crates/hero_whiteboard_ui/static/web/js/whiteboard/sync.js— bothserializeForServer(sender) andapplySyncUpdate(receiver) formindmapalready handle full tree replacement correctly.object.updatealready accepts thedatablob.Step-by-Step Plan
Step 1 — Make root color change sync + persist
File:
crates/hero_whiteboard_ui/static/web/js/whiteboard/properties.jsIn the
prop-mm-root-colorinputhandler, appendWhiteboardSync.onUpdate(currentNode);afterWhiteboardMindmap.redraw(currentNode);. The handler should match the shape of theprop-mm-titlehandler.After change, the body reads:
Dependencies: none.
Step 2 — Make root text change sync + persist (same bug, same fix)
File:
crates/hero_whiteboard_ui/static/web/js/whiteboard/properties.jsIn the
prop-mm-root-textinputhandler, appendWhiteboardSync.onUpdate(currentNode);afterWhiteboardMindmap.redraw(currentNode);. Body becomes:Dependencies: none.
Acceptance Criteria
properties.js; no other files changed.Notes
WhiteboardSync.onUpdate(currentNode). Persistence and live sync are inseparable here —onUpdateperforms both influshUpdates.tree.colorper node; there is norootColor/rootFillfield. The root simply renders withfill: node.colorwhere non-roots usefill: '#2b3035'andstroke: node.color.applySyncUpdatereplaces_mmStatewholesale fromdata.tree, then callsWhiteboardMindmap.redraw(node), so the new root color renders correctly on remote tabs without further changes.inputevents fire many times per second;pendingUpdatescollapses them to one node entry andscheduleFlushdebounces the actual RPC. Final state is what gets persisted.prop-mm-*handlers could share a tiny helper that mutates state, redraws, and syncs.Test Results
Implementation Summary
One file changed, two added lines (
+2/-0). No server / SDK / OpenRPC / DB changes.Root cause
The
prop-mm-root-colorandprop-mm-root-textinputlisteners inproperties.jsmutatedcurrentNode._mmState.tree.color/.textand re-rendered the mind map locally, but they never calledWhiteboardSync.onUpdate(currentNode). That single function is what handles both:rpcCall('object.update', { ..., data })— persistencewsSendThrottled({ type: 'object.updated', data })— live broadcastSo the same gap caused both observed bugs: changes weren't persisted (so reload reverted them) and weren't broadcast (so other tabs never saw them).
crates/hero_whiteboard_ui/static/web/js/whiteboard/properties.jsWhiteboardSync.onUpdate(currentNode);in theprop-mm-root-textinput handler, afterWhiteboardMindmap.redraw(currentNode);.prop-mm-root-colorinput handler.Both handlers now match the shape of the working
prop-mm-titlehandler. The receiver path insync.jsapplySyncUpdatealready replaces_mmStatewholesale fromdata.treeand callsWhiteboardMindmap.redraw(node), so no additional changes are needed for remote rendering.Verification
cargo fmt --all -- --check: cleancargo check --workspace: cleancargo clippy --workspace -- -D warnings: cleancargo test --workspace --lib: passnode --check properties.js: cleanManual smoke test
Notes
inputevents fire many times per second during dragging;pendingUpdatescollapses them to one node entry andscheduleFlushdebounces the actual RPC, so only the final state is persisted.tree.color. There is no separaterootColor/rootFillfield — the root simply renders withfill: node.colorwhile non-roots usefill: '#2b3035'andstroke: node.color.