Connector style change is not undoable; undo deletes the connector #202
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
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
lhumina_code/hero_whiteboard#202
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
Changing a connector's line style cannot be undone. Pressing Undo right after a style change does not revert the style — instead it removes the connector entirely. Redo does not restore the style either.
Steps to reproduce
Expected: the connector reverts to its previous line style and stays on the canvas.
Actual: the connector is deleted (Undo pops the connector's create action because the style change was never recorded), and Redo cannot bring the style back.
Root cause
cycleLineStyleincrates/hero_whiteboard_admin/static/web/js/whiteboard/connectors.jsdestroys and recreates the connector to apply the new style but never pushes a history action for the change. The undo/redo stack therefore has no entry for the style change, so Undo operates on the previous action (typically the connector's own create), which removes the connector.Expected behavior
A connector style change should push a single undoable history action that, on undo, restores the previous line style (and on redo, re-applies the new one) while keeping the connector's stable identity intact across the destroy/recreate cycle.
Implementation Spec for Issue #202
Objective
Make a connector line-style change (straight/curved/elbow) a single undoable/redoable history step. After a style change, one Undo reverts the connector to its previous line style while keeping it on the canvas (same logical connector), and Redo re-applies the new style. The connector's stable identity must remain intact, and undo/redo must not double-persist beyond what the existing connector update/broadcast path already does.
Requirements
_fromSync).Files to Modify
crates/hero_whiteboard_admin/static/web/js/whiteboard/connectors.js—cycleLineStylerecords one undoablecustomhistory action for genuine user-driven style changes, guarded against sync and replay.crates/hero_whiteboard_admin/static/web/js/whiteboard/selection_toolbar.js— wrap the toolbar's multi-step style loop inWhiteboardHistory.batch(...)so it collapses to a single undo step.No backend/Rust changes.
Implementation Plan
Step 1 — In
cycleLineStyle, capture the previous style, and after the existing recreate + non-sync server update + broadcast, push onetype: 'custom'history action. Guard the push with!opts._fromSyncand the existing replay guard!(typeof WhiteboardHistory !== 'undefined' && WhiteboardHistory.isEnabled && WhiteboardHistory.isEnabled() === false). Theundo/redoclosures resolve the live id via the stablehidToIdmap at call time and step the connector forward through the style cycle (bounded loop) until it reaches the previous / new style respectively, reusing the single identity-preserving, persisting code path.Step 2 — In
selection_toolbar.js_renderConnector, wrap the loop that callscycleLineStyleup to N times inWhiteboardHistory.batch(function(){ ... })so the multiple pushes flush as one batch entry; one Undo reverts straight to the original style.Step 3 — Review only: confirm replay (
_enabled = falseduring undo/redo) suppresses nested pushes via the Step 1 guard, while the replayedcycleLineStylestill persists and broadcasts the reverted/re-applied style once per step.Acceptance Criteria
Notes
type: 'custom'withundo/redofunctions, matching existing connector history entries;type: 'update'is unsuitable because connectors are not in the object registry.cycleLineStylesingle-responsibility (one push per forward step), consistent with how connector create/delete each push one entry per logical op.Test Results
Note: the fix is JS-only (no Rust source changed); the workspace lib tests are run as a regression guard.
Implementation Summary
Root cause
cycleLineStyle(crates/hero_whiteboard_admin/static/web/js/whiteboard/connectors.js) destroyed and recreated the connector to apply a new line style but pushed no history action. Undo therefore popped the previous action (the connector'"'"'s own create) and deleted the connector; Redo could not restore the style.Changes
crates/hero_whiteboard_admin/static/web/js/whiteboard/connectors.jscycleLineStylenow captures the previous style and, after the existing recreate + non-sync server update + broadcast, pushes onetype: '"'"'custom'"'"'history action.!opts._fromSyncand the same replay guard used by the existing create/delete pushes (!(typeof WhiteboardHistory !== '"'"'undefined'"'"' && WhiteboardHistory.isEnabled && WhiteboardHistory.isEnabled() === false)), so sync/remote changes and history replay do not record nested actions.hid) before the destroy/recreate if it does not already have one (mirroringdeleteConnector), so undo/redo resolve the live connector throughhidToIdat call time.undo/redostep the connector forward through the bounded style cycle until it reaches the previous / new style, reusing the single identity-preserving code path that also persists to the server and broadcasts to peers.crates/hero_whiteboard_admin/static/web/js/whiteboard/selection_toolbar.jscycleLineStyleup to three times to reach the selected style) is wrapped inWhiteboardHistory.batch(...), so a single dropdown change is one undo step rather than one per internal cycle.Test results
cargo test --workspace --lib(matches CI): compiled cleanly, no failures. These crates carry no unit tests, so this is a regression guard only; the change is JS-only with no Rust source modified.node --checkpassed for both edited JS files (connectors.js,selection_toolbar.js).Notes
type: '"'"'custom'"'"'actions (connectors are not in the object registry, sotype: '"'"'update'"'"'snapshotting would not capture connector style).Scope update
The fix was extended beyond line style: the same defect affected connector color and stroke width, whose toolbar handlers also mutated the connector directly without recording a history action (so Undo deleted the connector).
Final state of the fix:
cycleLineStylerecords a guardedcustomundo/redo action for line-style changes; the toolbar's multi-step change is batched into one undo step.WhiteboardConnectors.setStyle(id, { stroke, strokeWidth })records one guardedcustomundo/redo action per color/width change; the toolbar color picker and stroke-width slider now route through it.Verified manually: changing a connector's line style, color, or thickness and pressing Undo now reverts the change and keeps the connector on the canvas; Redo re-applies it.