Phase 5 Stage D: fix bend history + move-tool loop + grid-snap sfx
Three user-reported regressions, all in the Stage D move/curve ports: 1. **Fence/wall bend cancelled the fence creation on Ctrl-Z.** The action.commit's `return false` shortcut on "no offset change" bypassed the dance entirely — pastStates wasn't touched, so the next Ctrl-Z fell through to whatever preceded activation (typically the create step). Fix: always run the dance, even on no-op commits. First Ctrl-Z then absorbs a silent no-op entry, subsequent presses roll back real prior actions. Same fix applied to fence move-endpoint, fence move, slab move, ceiling move. 2. **Slab/ceiling move 'maximum update depth exceeded' loop.** The `useScene` selector in `SlabMoveTool` returned a freshly-allocated `[sx, sz]` tuple on every call. Zustand's `Object.is` equality failed each comparison → re-subscribe → re-render → loop. Fix: subscribe to the stable live-node reference and derive the center via `useMemo`. Same recipe for fence/ceiling move-tools, including memoizing the `originalCenter` fallback that was getting a new array per render. 3. **No grid-snap sfx during move drag.** Action `preview` now tracks the last snapped pointer on a mutable `lastSnapped` ctx field and emits `sfx:grid-snap` when it changes between ticks. Matches the legacy MoveFenceTool's per-tick sound. Locking test for foot-gun 1 lives in `packages/core/src/services/single-undo-dance.test.ts`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
d43cd569d6
commit
c2c2f66427
@@ -183,11 +183,12 @@ export const moveFenceEndpointDragAction: DragAction<MoveFenceEndpointCtx, MoveF
|
||||
},
|
||||
|
||||
commit: (draft, ctx, scene) => {
|
||||
// Reject when the drag didn't move OR the resulting fence would
|
||||
// be shorter than the minimum length. createDragSession.cancel()
|
||||
// restores originals via the snapshot.
|
||||
const hasChanged = !samePoint(draft.movingPoint, ctx.originalMovingPoint)
|
||||
if (!hasChanged) return false
|
||||
// Min-length rejection still matters — too-short fence is invalid
|
||||
// and should bounce back via the cancel path (snapshot restore).
|
||||
// But the "no-change" rejection is removed: see
|
||||
// fence/actions/curve.ts for the rationale (no-op drag must still
|
||||
// push a pastState entry to avoid Ctrl-Z cancelling the fence
|
||||
// creation that preceded the activation).
|
||||
if (!isWallLongEnough(draft.start, draft.end)) return false
|
||||
|
||||
// Single-undo dance: revert to originals (paused history → no
|
||||
|
||||
Reference in New Issue
Block a user