Third Stage D affordance port (302 LoC legacy → action + thin wrapper). The action (`actions/move.ts`) uses the live-drag exception documented in editor/wiki/architecture/tools.md: visual-only updates via `sceneRegistry.nodes.get(id).position` + `useLiveTransforms` during the drag, no scene mutations. Avoids re-rebuilding the fence geometry (many posts + infill panels) every pointer tick. Commit performs the single-undo dance — writes final start/end to scene, geometry rebuilds once, Ctrl-Z reverses the whole drag. Linked-fence cascade follows the same shape as MoveFenceEndpoint — any fence in the same parent that shared an endpoint at activation moves with the drag. `getRegistryAffordanceTool` extracted to `tools/shared/affordance-dispatch.ts` so move-tool.tsx and tool-manager.tsx share the lazy-load helper (no duplicate caches). MoveTool dispatch gains a generic `affordanceTools.move` check after the capability-driven `movable` shortcut — fence routes through here; wall / slab / etc. fall through to their legacy per-kind chain until their D ports land. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
31 lines
1.2 KiB
TypeScript
31 lines
1.2 KiB
TypeScript
import { nodeRegistry } from '@pascal-app/core'
|
|
import { type ComponentType, lazy } from 'react'
|
|
|
|
/**
|
|
* Phase 5 Stage D — runtime lazy-load of a kind's affordance tool.
|
|
*
|
|
* The editor can't statically import from `@pascal-app/nodes` (the
|
|
* nodes package depends on editor — static imports would cycle). The
|
|
* kind declares its drag-affordance components in
|
|
* `def.affordanceTools[<key>]: () => import('./<name>-tool')`; this
|
|
* helper resolves that to a `React.lazy` component at the call site.
|
|
*
|
|
* Returns null when the kind doesn't declare the affordance — callers
|
|
* mount the legacy fallback in that case.
|
|
*/
|
|
const lazyToolCache = new WeakMap<() => Promise<unknown>, ComponentType>()
|
|
|
|
export function getRegistryAffordanceTool(
|
|
kind: string,
|
|
affordance: string,
|
|
): ComponentType<any> | null {
|
|
const def = nodeRegistry.get(kind)
|
|
const loader = def?.affordanceTools?.[affordance]
|
|
if (!loader) return null
|
|
const cached = lazyToolCache.get(loader)
|
|
if (cached) return cached
|
|
const Comp = lazy(loader as () => Promise<{ default: ComponentType<any> }>)
|
|
lazyToolCache.set(loader, Comp as unknown as ComponentType)
|
|
return Comp as unknown as ComponentType<any>
|
|
}
|