Registry-first dispatch in ToolManager (shim 4/4)
Before mounting any legacy build tool, ToolManager checks whether the active tool's kind has a registered NodeDefinition with a tool contribution. If yes, the registry tool wins; the legacy tool map and special-cased spawn/column/elevator branches are skipped for that kind. Lazy-loaded via React.lazy (cached by loader) and wrapped in Suspense. Today the registry is empty, so useRegistryTool is always false and every code path renders unchanged. The moment a kind registers (Phase 2+), its registry tool takes over without further edits here. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
7dd3ffed3f
commit
2a8eb3e1de
@@ -2,10 +2,12 @@ import {
|
|||||||
type AnyNodeId,
|
type AnyNodeId,
|
||||||
type BuildingNode,
|
type BuildingNode,
|
||||||
type CeilingNode,
|
type CeilingNode,
|
||||||
|
nodeRegistry,
|
||||||
type SlabNode,
|
type SlabNode,
|
||||||
useScene,
|
useScene,
|
||||||
} from '@pascal-app/core'
|
} from '@pascal-app/core'
|
||||||
import { useViewer } from '@pascal-app/viewer'
|
import { useViewer } from '@pascal-app/viewer'
|
||||||
|
import { type ComponentType, lazy, Suspense } from 'react'
|
||||||
import useEditor, { type Phase, type Tool } from '../../store/use-editor'
|
import useEditor, { type Phase, type Tool } from '../../store/use-editor'
|
||||||
import { CeilingBoundaryEditor } from './ceiling/ceiling-boundary-editor'
|
import { CeilingBoundaryEditor } from './ceiling/ceiling-boundary-editor'
|
||||||
import { CeilingHoleEditor } from './ceiling/ceiling-hole-editor'
|
import { CeilingHoleEditor } from './ceiling/ceiling-hole-editor'
|
||||||
@@ -32,6 +34,21 @@ import { WindowTool } from './window/window-tool'
|
|||||||
import { ZoneBoundaryEditor } from './zone/zone-boundary-editor'
|
import { ZoneBoundaryEditor } from './zone/zone-boundary-editor'
|
||||||
import { ZoneTool } from './zone/zone-tool'
|
import { ZoneTool } from './zone/zone-tool'
|
||||||
|
|
||||||
|
// Cache lazy tool components keyed by their loader so React.lazy isn't
|
||||||
|
// re-invoked across renders.
|
||||||
|
const lazyToolCache = new WeakMap<() => Promise<unknown>, ComponentType>()
|
||||||
|
|
||||||
|
function getRegistryTool(tool: Tool | null): ComponentType | null {
|
||||||
|
if (!tool) return null
|
||||||
|
const def = nodeRegistry.get(tool)
|
||||||
|
if (!def?.tool) return null
|
||||||
|
const cached = lazyToolCache.get(def.tool)
|
||||||
|
if (cached) return cached
|
||||||
|
const Comp = lazy(def.tool as () => Promise<{ default: ComponentType }>)
|
||||||
|
lazyToolCache.set(def.tool, Comp)
|
||||||
|
return Comp
|
||||||
|
}
|
||||||
|
|
||||||
const tools: Record<Phase, Partial<Record<Tool, React.FC>>> = {
|
const tools: Record<Phase, Partial<Record<Tool, React.FC>>> = {
|
||||||
site: {
|
site: {
|
||||||
'property-line': SiteBoundaryEditor,
|
'property-line': SiteBoundaryEditor,
|
||||||
@@ -127,7 +144,14 @@ export const ToolManager: React.FC = () => {
|
|||||||
// Show build tools when in build mode
|
// Show build tools when in build mode
|
||||||
const showBuildTool = mode === 'build' && tool !== null
|
const showBuildTool = mode === 'build' && tool !== null
|
||||||
|
|
||||||
const BuildToolComponent = showBuildTool ? tools[phase]?.[tool] : null
|
// Registry-first: if the active tool's kind has a NodeDefinition with a
|
||||||
|
// tool contribution, the registry-driven tool takes over. Otherwise fall
|
||||||
|
// through to the legacy `tools` map below. Today the registry is empty so
|
||||||
|
// RegistryToolComponent is always null — zero behavior change.
|
||||||
|
const RegistryToolComponent = showBuildTool ? getRegistryTool(tool) : null
|
||||||
|
const useRegistryTool = RegistryToolComponent != null
|
||||||
|
|
||||||
|
const BuildToolComponent = showBuildTool && !useRegistryTool ? tools[phase]?.[tool] : null
|
||||||
const handlePlacedNodeSelected = (nodeId: AnyNodeId) => {
|
const handlePlacedNodeSelected = (nodeId: AnyNodeId) => {
|
||||||
setSelection({ selectedIds: [nodeId] })
|
setSelection({ selectedIds: [nodeId] })
|
||||||
}
|
}
|
||||||
@@ -174,13 +198,21 @@ export const ToolManager: React.FC = () => {
|
|||||||
onSpawnMoved={handlePlacedNodeSelected}
|
onSpawnMoved={handlePlacedNodeSelected}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{!movingNode && showBuildTool && tool === 'spawn' && (
|
{/* Registry-first: when the active tool's kind has a registered
|
||||||
|
NodeDefinition with a tool contribution, mount it here. Today
|
||||||
|
the registry is empty so this branch never fires. */}
|
||||||
|
{!movingNode && useRegistryTool && RegistryToolComponent && (
|
||||||
|
<Suspense fallback={null}>
|
||||||
|
<RegistryToolComponent />
|
||||||
|
</Suspense>
|
||||||
|
)}
|
||||||
|
{!movingNode && !useRegistryTool && showBuildTool && tool === 'spawn' && (
|
||||||
<SpawnTool currentLevelId={activeLevelId ?? null} onPlaced={handlePlacedNodeSelected} />
|
<SpawnTool currentLevelId={activeLevelId ?? null} onPlaced={handlePlacedNodeSelected} />
|
||||||
)}
|
)}
|
||||||
{!movingNode && showBuildTool && tool === 'column' && (
|
{!movingNode && !useRegistryTool && showBuildTool && tool === 'column' && (
|
||||||
<ColumnTool currentLevelId={activeLevelId ?? null} onPlaced={handlePlacedNodeSelected} />
|
<ColumnTool currentLevelId={activeLevelId ?? null} onPlaced={handlePlacedNodeSelected} />
|
||||||
)}
|
)}
|
||||||
{!movingNode && showBuildTool && tool === 'elevator' && (
|
{!movingNode && !useRegistryTool && showBuildTool && tool === 'elevator' && (
|
||||||
<ElevatorTool
|
<ElevatorTool
|
||||||
buildingId={buildingId as BuildingNode['id'] | null}
|
buildingId={buildingId as BuildingNode['id'] | null}
|
||||||
levelId={activeLevelId ?? null}
|
levelId={activeLevelId ?? null}
|
||||||
|
|||||||
Reference in New Issue
Block a user