Registry-first dispatch in NodeRenderer (shim 2/4)
NodeRenderer now checks the registry first. Registered kinds load their renderer module via React.lazy (cached by RendererSource so the Suspense boundary is stable across re-renders). Unregistered kinds fall through to the legacy chain below. Today the registry is empty (Phase 0 builtinPlugin.nodes is []), so every node still hits the legacy chain — no behavior change. The moment a kind registers in @pascal-app/nodes (Phase 2 onward), the registry path takes over without further edits here. GLB / instanced-GLB RendererSource kinds are typed but not yet honored — they get their built-in renderers in Phase 5. 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
4c3feea052
commit
0fd7af216c
@@ -1,6 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import { useScene, type AnyNode } from '@pascal-app/core'
|
||||
import { type AnyNode, nodeRegistry, type RendererSource, useScene } from '@pascal-app/core'
|
||||
import { type ComponentType, lazy, Suspense } from 'react'
|
||||
import { BuildingRenderer } from './building/building-renderer'
|
||||
import { CeilingRenderer } from './ceiling/ceiling-renderer'
|
||||
import { ColumnRenderer } from './column/column-renderer'
|
||||
@@ -10,23 +11,60 @@ import { FenceRenderer } from './fence/fence-renderer'
|
||||
import { GuideRenderer } from './guide/guide-renderer'
|
||||
import { ItemRenderer } from './item/item-renderer'
|
||||
import { LevelRenderer } from './level/level-renderer'
|
||||
import { RoofSegmentRenderer } from './roof-segment/roof-segment-renderer'
|
||||
import { RoofRenderer } from './roof/roof-renderer'
|
||||
import { RoofSegmentRenderer } from './roof-segment/roof-segment-renderer'
|
||||
import { ScanRenderer } from './scan/scan-renderer'
|
||||
import { SiteRenderer } from './site/site-renderer'
|
||||
import { SlabRenderer } from './slab/slab-renderer'
|
||||
import { SpawnRenderer } from './spawn/spawn-renderer'
|
||||
import { StairSegmentRenderer } from './stair-segment/stair-segment-renderer'
|
||||
import { StairRenderer } from './stair/stair-renderer'
|
||||
import { StairSegmentRenderer } from './stair-segment/stair-segment-renderer'
|
||||
import { WallRenderer } from './wall/wall-renderer'
|
||||
import { WindowRenderer } from './window/window-renderer'
|
||||
import { ZoneRenderer } from './zone/zone-renderer'
|
||||
|
||||
// Cache lazy components by their RendererSource so React.lazy isn't re-invoked
|
||||
// on every render — that would create a new Suspense boundary each time.
|
||||
const lazyCache = new WeakMap<RendererSource<AnyNode>, ComponentType<{ node: AnyNode }>>()
|
||||
|
||||
function getRegistryRenderer(
|
||||
source: RendererSource<AnyNode>,
|
||||
): ComponentType<{ node: AnyNode }> | null {
|
||||
const cached = lazyCache.get(source)
|
||||
if (cached) return cached
|
||||
// GLB / instanced-GLB sources lower onto built-in renderers landed in
|
||||
// Phase 5 — for now only parametric (lazy module) sources are honored.
|
||||
if (source.kind !== 'parametric') return null
|
||||
const Comp = lazy(source.module) as unknown as ComponentType<{ node: AnyNode }>
|
||||
lazyCache.set(source, Comp)
|
||||
return Comp
|
||||
}
|
||||
|
||||
function RegistryRenderer({ node }: { node: AnyNode }) {
|
||||
const def = nodeRegistry.get(node.type)
|
||||
if (!def) return null
|
||||
const Renderer = getRegistryRenderer(def.renderer as RendererSource<AnyNode>)
|
||||
if (!Renderer) return null
|
||||
return (
|
||||
<Suspense fallback={null}>
|
||||
<Renderer node={node} />
|
||||
</Suspense>
|
||||
)
|
||||
}
|
||||
|
||||
export const NodeRenderer = ({ nodeId }: { nodeId: AnyNode['id'] }) => {
|
||||
const node = useScene((state) => state.nodes[nodeId])
|
||||
|
||||
if (!node) return null
|
||||
|
||||
// Registry-first: if a NodeDefinition is registered for this kind (via
|
||||
// @pascal-app/nodes or a future plugin), it owns the render. Falls through
|
||||
// to the legacy chain below for kinds not yet migrated. Legacy chain is
|
||||
// removed in Phase 6 once every kind is registry-backed.
|
||||
if (nodeRegistry.has(node.type)) {
|
||||
return <RegistryRenderer node={node} />
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{node.type === 'site' && <SiteRenderer node={node} />}
|
||||
|
||||
Reference in New Issue
Block a user