MoveTool: dispatch registry-first so spawn move uses MoveRegistryNodeTool

Shim ordering bug: the `nodeRegistry.has(movingNode.type)` check sat at
the END of the dispatch chain, AFTER the per-kind `if (movingNode.type
=== 'spawn') return <MoveSpawnTool>` branches. Spawn (now registered
in builtinPlugin) was therefore still routing to the legacy
MoveSpawnTool — which uses the broken useLiveTransforms pattern and
makes the spawn mesh disappear during drag.

Moved the registry check to the TOP, matching the registry-first
dispatch model the Phase 0 shims use everywhere else (NodeRenderer,
ToolManager, system guards). Now any kind registered via
@pascal-app/nodes routes to MoveRegistryNodeTool — same smooth
imperative drag for shelf, spawn, and every future kind. Legacy
per-kind movers below run only for kinds not yet in the registry.

This is exactly how the Phase 5 progressive consolidation works: as
kinds migrate to the registry, their legacy movers stop being reached
and can be deleted.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Wassim SAMAD
2026-05-14 16:26:29 -04:00
co-authored by Claude Opus 4.7
parent 166e860bfe
commit d254582597
@@ -102,6 +102,17 @@ export const MoveTool: React.FC<{
const movingNode = useEditor((state) => state.movingNode)
if (!movingNode) return null
// Registry-first dispatch. Any kind registered via @pascal-app/nodes
// gets the imperative MoveRegistryNodeTool (smooth, framerate-locked
// motion via sceneRegistry — see plan: "Validated patterns from the
// spike"). The legacy per-kind movers below are short-circuited for
// any kind in the registry — that's how Phase 5 will progressively
// delete them as kinds migrate.
if (nodeRegistry.has(movingNode.type)) {
return <MoveRegistryNodeTool node={movingNode} />
}
if (movingNode.type === 'building')
return <MoveBuildingContent node={movingNode as BuildingNode} />
if (movingNode.type === 'door') return <MoveDoorTool node={movingNode as DoorNode} />
@@ -119,13 +130,5 @@ export const MoveTool: React.FC<{
return <MoveSpawnTool node={movingNode as SpawnNode} onCommitted={onSpawnMoved} />
if (movingNode.type === 'stair' || movingNode.type === 'stair-segment')
return <MoveRoofTool node={movingNode as StairNode | StairSegmentNode} />
// Registry-driven kinds (any NodeDefinition with `capabilities.movable`)
// get a generic position+rotation mover. Phase 4 may consolidate this
// with the per-kind movers above when they all collapse to the same
// shape. Must come BEFORE the MoveItemContent fallback because that
// assumes the node is an ItemNode.
if (nodeRegistry.has(movingNode.type)) {
return <MoveRegistryNodeTool node={movingNode} />
}
return <MoveItemContent movingNode={movingNode as ItemNode} />
}