Phase 5 Stage D: fence curve affordance → registry DragAction

First Stage D port — `CurveFenceTool` (178 LoC legacy) split into a pure
`DragAction` primitive (`packages/nodes/src/fence/actions/curve.ts`) plus
a thin React wrapper (`packages/nodes/src/fence/curve-tool.tsx`) that
feeds it through `useDragAction`. The kind declares the affordance via
`def.affordanceTools.curve`; ToolManager lazy-loads it at runtime when
`useEditor.curvingFence` activates. Falls back to the legacy
`CurveFenceTool` for any kind that hasn't been ported.

The lazy-load dispatch dodges the editor→nodes circular dep (nodes
already depends on editor for `useDragAction` + `CursorSphere`).

Establishes the pattern for the remaining fence affordances
(`MoveFenceEndpoint`, `MoveFence`, placement) and for slab/ceiling/wall
D ports.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Wassim SAMAD
2026-05-15 17:18:30 -04:00
co-authored by Claude Opus 4.7
parent 95645d8e37
commit 8ca9686b27
6 changed files with 249 additions and 1 deletions
+10
View File
@@ -67,6 +67,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).
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'),
},
toolHints: [
{ key: 'Left click', label: 'Set fence start / end' },