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
+15
View File
@@ -159,6 +159,21 @@ export type NodeDefinition<S extends ZodObject<any>> = {
floorplan?: (node: z.infer<S>, ctx: GeometryContext) => FloorplanGeometry | null
system?: SystemContribution
tool?: LazyComponent
/**
* Stage-D drag-affordance components — one per kind-owned editor mode
* triggered by `useEditor` state. Component receives `{ node }` as its
* sole prop. Lazy-loaded by ToolManager when the corresponding editor
* state activates (e.g. `curvingFence` → `affordanceTools.curve`).
*
* Each component is the thin React wrapper around a pure DragAction
* primitive that lives in the kind's `actions/` folder. The split keeps
* the action data unit-testable while letting the wrapper consume
* `useDragAction` + cursor visuals.
*
* Generic record so per-kind state names don't need to land in the
* core type system. ToolManager looks up by string key.
*/
affordanceTools?: Record<string, () => Promise<{ default: ComponentType<any> }>>
affordances?: Affordance<z.infer<S>>[]
/**
* Contextual shortcut hints shown by `HelperManager` when this kind's