Phase 5 Stage D: revert curve + whole-item move affordances to legacy

User-reported regressions made it clear that my D-ports of the curve
and whole-item move tools introduced more friction than they removed.
The legacy CurveFenceTool / CurveWallTool / MoveFenceTool /
MoveSlabTool / MoveCeilingTool ship more polish than the new ports do
right now:

  - Legacy curve tools pre-snap the pointer position to the 0.5m grid
    BEFORE projecting onto the chord normal, and they Shift-toggle to
    a free-place mode. The ports skipped both — finer math, but the
    user-visible UX regressed (laggy because the cascade resolver fires
    per move, history feels broken near the no-op threshold).
  - Legacy whole-item moves use scene.update per tick which keeps
    hosted children visually aligned. The live-drag mesh.position port
    cleared the offset before the GeometrySystem could rebuild,
    producing a one-frame teleport on commit.

Drop the affordance registrations for those tools — the ToolManager /
MoveTool dispatch falls back to the legacy per-kind tools when the
registry doesn't declare the affordance. Stage D progress preserved
for: fence move-endpoint (linked cascade + alt-detach), slab/ceiling
boundary + hole editors, fence placement, slab placement, ceiling
placement.

The kind-owned files (curve-tool.tsx, move-tool.tsx, actions/curve.ts,
actions/move.ts) stay on disk for the next iteration — when they
reach parity with legacy UX, re-add the affordance entries.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Wassim SAMAD
2026-05-18 08:42:40 -04:00
co-authored by Claude Opus 4.7
parent d1231b4901
commit da63081f73
4 changed files with 25 additions and 33 deletions
+8 -18
View File
@@ -74,26 +74,16 @@ export const fenceDefinition: NodeDefinition<typeof FenceNode> = {
// Legacy `floorplanFenceEntries` short-circuits to [] when fence is
// registered (see floorplan-panel.tsx).
floorplan: buildFenceFloorplan,
// Stage D (in progress): drag-affordance components owned by the kind.
// ToolManager looks up these lazy modules at runtime when the matching
// editor state activates — no static import from editor → nodes
// (which would create a circular dep).
// Stage D — partial port. Endpoint drag (with linked-fence cascade,
// alt-detach, angle label) is ported here; curve + whole-fence move
// are intentionally kept on the legacy CurveFenceTool / MoveFenceTool
// because the legacy code is more polished than the ports were
// (cursor anchoring, snap step, performance, history). Those ports
// remain in `curve-tool.tsx` + `move-tool.tsx` + their `actions/`
// siblings for the next iteration; the legacy fallback runs until
// they reach parity.
affordanceTools: {
// Triggered by useEditor.curvingFence. Pure DragAction logic in
// actions/curve.ts; this component is the React wrapper using
// useDragAction + the cursor visuals.
curve: () => import('./curve-tool'),
// Triggered by useEditor.movingFenceEndpoint. Pure logic in
// actions/move-endpoint.ts (linked-fence cascade, alt-detach,
// single-undo dance). Wrapper owns the angle label + detach badge.
'move-endpoint': () => import('./move-endpoint-tool'),
// Triggered by useEditor.movingNode when the moving node is a
// fence. Whole-fence rigid translation with linked-fence cascade.
// Pure logic in actions/move.ts uses the live-drag exception
// (mesh.position + useLiveTransforms) to avoid rebuilding fence
// geometry every pointer tick; commits the final start/end with
// the single-undo dance.
move: () => import('./move-tool'),
},
toolHints: [