From da63081f7314c72f6813e6904dae217ee4abf835 Mon Sep 17 00:00:00 2001 From: Wassim SAMAD Date: Mon, 18 May 2026 08:42:40 -0400 Subject: [PATCH] Phase 5 Stage D: revert curve + whole-item move affordances to legacy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- packages/nodes/src/ceiling/definition.ts | 7 +++---- packages/nodes/src/fence/definition.ts | 26 ++++++++---------------- packages/nodes/src/slab/definition.ts | 10 +++++---- packages/nodes/src/wall/definition.ts | 15 +++++++------- 4 files changed, 25 insertions(+), 33 deletions(-) diff --git a/packages/nodes/src/ceiling/definition.ts b/packages/nodes/src/ceiling/definition.ts index 33318bfe..d6a753d4 100644 --- a/packages/nodes/src/ceiling/definition.ts +++ b/packages/nodes/src/ceiling/definition.ts @@ -58,13 +58,12 @@ export const ceilingDefinition: NodeDefinition = { // with a vertical TSL-gradient connector + ground-shadow lines. tool: () => import('./tool'), - // Stage D: drag/edit affordances. Boundary editor + hole editor - // delegate to the shared ``; move uses the single- - // undo dance. + // Stage D — boundary + hole editors ported. Whole-ceiling move kept + // on the legacy MoveCeilingTool for the same reason as slab (live- + // drag mesh.position one-frame teleport on commit). affordanceTools: { 'boundary-edit': () => import('./boundary-editor'), 'hole-edit': () => import('./hole-editor'), - move: () => import('./move-tool'), }, renderer: { diff --git a/packages/nodes/src/fence/definition.ts b/packages/nodes/src/fence/definition.ts index f29dc289..f828f037 100644 --- a/packages/nodes/src/fence/definition.ts +++ b/packages/nodes/src/fence/definition.ts @@ -74,26 +74,16 @@ export const fenceDefinition: NodeDefinition = { // 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: [ diff --git a/packages/nodes/src/slab/definition.ts b/packages/nodes/src/slab/definition.ts index c98849f3..1a2c2695 100644 --- a/packages/nodes/src/slab/definition.ts +++ b/packages/nodes/src/slab/definition.ts @@ -58,13 +58,15 @@ export const slabDefinition: NodeDefinition = { // with axis/45° snap (Shift to defeat). tool: () => import('./tool'), - // Stage D: drag/edit affordances. Boundary editor + hole editor - // delegate to the shared ``; move uses the single- - // undo dance for the polygon-translate commit. + // Stage D — boundary + hole editors ported (thin + // wrappers, behaviorally identical to legacy). Whole-slab move kept + // on the legacy MoveSlabTool: the live-drag mesh.position port + // introduced a one-frame teleport on commit (geometry rebuild lags + // the position clear), and the legacy already has acceptable perf + // via RAF-batched markDirty. affordanceTools: { 'boundary-edit': () => import('./boundary-editor'), 'hole-edit': () => import('./hole-editor'), - move: () => import('./move-tool'), }, // Stage B: pure geometry function. diff --git a/packages/nodes/src/wall/definition.ts b/packages/nodes/src/wall/definition.ts index 0fb2f0db..5d411e40 100644 --- a/packages/nodes/src/wall/definition.ts +++ b/packages/nodes/src/wall/definition.ts @@ -57,13 +57,14 @@ export const wallDefinition: NodeDefinition = { parametrics: wallParametrics, - // Stage D (in progress): drag-affordance components owned by the kind. - // Only the curve affordance is ported today — the remaining wall - // tools (endpoint drag, whole-wall move, placement) are larger and - // queued for future sessions. - affordanceTools: { - curve: () => import('./curve-tool'), - }, + // Stage D — deferred for wall. The curve port (`curve-tool.tsx` + + // `actions/curve.ts`) needs more work to match the legacy + // CurveWallTool's UX (pre-snap on pointer position, 0.5m grid step, + // Shift override, smooth scene.update without cascade overhead). + // Legacy fallback runs until that lands. Endpoint move / whole-wall + // move / placement are all still legacy too — they're the biggest + // tools and have linked-wall corner cascade logic that needs a + // careful port. renderer: { kind: 'parametric',