Wrap legacy systems in LegacySystem + mount RegisteredSystems (shim 3/4)
Two additions plus a viewer JSX rewire: - legacy-system.tsx: <LegacySystem kind="..."> wrapper that renders its children only when nodeRegistry.has(kind) is false. Lets one wrapper cover all legacy systems for a kind (door has DoorSystem and DoorAnimationSystem — both belong to 'door' so they yield together). - registered-systems.tsx: <RegisteredSystems /> iterates the registry, filters entries that contribute a system, sorts by system.priority (default 5; e.g. wall mitering at 8 runs after door cuts at 3), mounts each via React.lazy. Today empty registry = renders nothing. - viewer/index.tsx: every existing per-kind system is wrapped in LegacySystem. RegisteredSystems is mounted alongside. With the registry empty (Phase 0), every LegacySystem passes through unchanged and RegisteredSystems is a no-op — zero behavior change. Once a kind registers in Phase 2+, its legacy systems yield and its registry-contributed system runs in their place. 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
0fd7af216c
commit
7dd3ffed3f
@@ -12,8 +12,8 @@ import { DoorSystem } from '../../systems/door/door-system'
|
||||
import { ElevatorInteractionSystem } from '../../systems/elevator/elevator-interaction-system'
|
||||
import { FenceSystem } from '../../systems/fence/fence-system'
|
||||
import { GuideSystem } from '../../systems/guide/guide-system'
|
||||
import { ItemLightSystem } from '../../systems/item-light/item-light-system'
|
||||
import { ItemSystem } from '../../systems/item/item-system'
|
||||
import { ItemLightSystem } from '../../systems/item-light/item-light-system'
|
||||
import { LevelSystem } from '../../systems/level/level-system'
|
||||
import { RoofSystem } from '../../systems/roof/roof-system'
|
||||
import { ScanSystem } from '../../systems/scan/scan-system'
|
||||
@@ -27,9 +27,11 @@ import { ZoneSystem } from '../../systems/zone/zone-system'
|
||||
import { ErrorBoundary } from '../error-boundary'
|
||||
import { SceneRenderer } from '../renderers/scene-renderer'
|
||||
import FrameLimiter from './frame-limiter'
|
||||
import { LegacySystem } from './legacy-system'
|
||||
import { Lights } from './lights'
|
||||
import { PerfMonitor } from './perf-monitor'
|
||||
import PostProcessing, { DEFAULT_HOVER_STYLES, type HoverStyles } from './post-processing'
|
||||
import { RegisteredSystems } from './registered-systems'
|
||||
import { SceneBvh } from './scene-bvh'
|
||||
import { SelectionManager } from './selection-manager'
|
||||
import { ViewerCamera } from './viewer-camera'
|
||||
@@ -223,30 +225,76 @@ const Viewer: React.FC<ViewerProps> = ({
|
||||
)}
|
||||
|
||||
{/* Default Systems */}
|
||||
<LevelSystem />
|
||||
<GuideSystem />
|
||||
<ScanSystem />
|
||||
<WallCutout />
|
||||
<LegacySystem kind="level">
|
||||
<LevelSystem />
|
||||
</LegacySystem>
|
||||
<LegacySystem kind="guide">
|
||||
<GuideSystem />
|
||||
</LegacySystem>
|
||||
<LegacySystem kind="scan">
|
||||
<ScanSystem />
|
||||
</LegacySystem>
|
||||
<LegacySystem kind="wall">
|
||||
<WallCutout />
|
||||
</LegacySystem>
|
||||
{/* Core systems */}
|
||||
<CeilingSystem />
|
||||
<DoorAnimationSystem />
|
||||
<ElevatorRuntimeSystem />
|
||||
<ElevatorInteractionSystem />
|
||||
<ElevatorOpeningSystem />
|
||||
<WindowAnimationSystem />
|
||||
<DoorSystem />
|
||||
<FenceSystem />
|
||||
<ItemSystem />
|
||||
<RoofSystem />
|
||||
<SlabSystem />
|
||||
<StairSystem />
|
||||
<WallSystem />
|
||||
<WindowSystem />
|
||||
<ZoneSystem />
|
||||
<LegacySystem kind="ceiling">
|
||||
<CeilingSystem />
|
||||
</LegacySystem>
|
||||
<LegacySystem kind="door">
|
||||
<DoorAnimationSystem />
|
||||
</LegacySystem>
|
||||
<LegacySystem kind="elevator">
|
||||
<ElevatorRuntimeSystem />
|
||||
</LegacySystem>
|
||||
<LegacySystem kind="elevator">
|
||||
<ElevatorInteractionSystem />
|
||||
</LegacySystem>
|
||||
<LegacySystem kind="elevator">
|
||||
<ElevatorOpeningSystem />
|
||||
</LegacySystem>
|
||||
<LegacySystem kind="window">
|
||||
<WindowAnimationSystem />
|
||||
</LegacySystem>
|
||||
<LegacySystem kind="door">
|
||||
<DoorSystem />
|
||||
</LegacySystem>
|
||||
<LegacySystem kind="fence">
|
||||
<FenceSystem />
|
||||
</LegacySystem>
|
||||
<LegacySystem kind="item">
|
||||
<ItemSystem />
|
||||
</LegacySystem>
|
||||
<LegacySystem kind="roof">
|
||||
<RoofSystem />
|
||||
</LegacySystem>
|
||||
<LegacySystem kind="slab">
|
||||
<SlabSystem />
|
||||
</LegacySystem>
|
||||
<LegacySystem kind="stair">
|
||||
<StairSystem />
|
||||
</LegacySystem>
|
||||
<LegacySystem kind="wall">
|
||||
<WallSystem />
|
||||
</LegacySystem>
|
||||
<LegacySystem kind="window">
|
||||
<WindowSystem />
|
||||
</LegacySystem>
|
||||
<LegacySystem kind="zone">
|
||||
<ZoneSystem />
|
||||
</LegacySystem>
|
||||
{/* Mounts systems contributed by registry-backed kinds. Today the
|
||||
registry is empty so this renders nothing. Once kinds register
|
||||
(Phase 2+), each kind's registered system runs here and its
|
||||
legacy counterpart above short-circuits via the LegacySystem
|
||||
wrapper (which checks nodeRegistry.has). */}
|
||||
<RegisteredSystems />
|
||||
<PostProcessing hoverStyles={hoverStyles} />
|
||||
{/* <DebugRenderer /> */}
|
||||
|
||||
<ItemLightSystem />
|
||||
<LegacySystem kind="item">
|
||||
<ItemLightSystem />
|
||||
</LegacySystem>
|
||||
{selectionManager === 'default' && <SelectionManager />}
|
||||
{(perf || PERF_OVERLAY_ENABLED) && <PerfMonitor />}
|
||||
{children}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
'use client'
|
||||
|
||||
import { nodeRegistry } from '@pascal-app/core'
|
||||
import type { ReactNode } from 'react'
|
||||
|
||||
/**
|
||||
* Wraps a legacy per-kind system component so it short-circuits the moment a
|
||||
* NodeDefinition for the same kind appears in the registry. Lets us migrate
|
||||
* one kind at a time without editing each legacy system file individually.
|
||||
*
|
||||
* Multiple legacy systems can belong to the same kind (e.g. door has both
|
||||
* `<DoorSystem>` and `<DoorAnimationSystem>`) — wrap them together so they
|
||||
* yield as a unit when the kind registers.
|
||||
*
|
||||
* Removed in Phase 6 alongside the legacy systems themselves.
|
||||
*/
|
||||
export function LegacySystem({ kind, children }: { kind: string; children: ReactNode }) {
|
||||
if (nodeRegistry.has(kind)) return null
|
||||
return <>{children}</>
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
'use client'
|
||||
|
||||
import { type AnyNodeDefinition, nodeRegistry } from '@pascal-app/core'
|
||||
import { type ComponentType, lazy, Suspense, useMemo } from 'react'
|
||||
|
||||
const DEFAULT_PRIORITY = 5
|
||||
|
||||
// Cache lazy components keyed by the module-loader function so React.lazy
|
||||
// isn't re-invoked across renders.
|
||||
const lazyCache = new WeakMap<() => Promise<unknown>, ComponentType>()
|
||||
|
||||
function loadSystem(def: AnyNodeDefinition): ComponentType | null {
|
||||
if (!def.system) return null
|
||||
const cached = lazyCache.get(def.system.module)
|
||||
if (cached) return cached
|
||||
const Comp = lazy(def.system.module as () => Promise<{ default: ComponentType }>)
|
||||
lazyCache.set(def.system.module, Comp)
|
||||
return Comp
|
||||
}
|
||||
|
||||
/**
|
||||
* Mounts every registered node kind's system component, ordered by
|
||||
* `system.priority` (default {@link DEFAULT_PRIORITY}).
|
||||
*
|
||||
* Today the registry is empty so this component mounts nothing — coexists
|
||||
* with legacy `*-System` components in `<Viewer>`. Once kinds register via
|
||||
* `@pascal-app/nodes`, each kind's registry-driven system takes over and
|
||||
* its legacy counterpart short-circuits via the `nodeRegistry.has(kind)`
|
||||
* guard added to each legacy system.
|
||||
*/
|
||||
export function RegisteredSystems() {
|
||||
const entries = useMemo(() => {
|
||||
return Array.from(nodeRegistry.entries())
|
||||
.filter(([, def]) => def.system != null)
|
||||
.sort(([, a], [, b]) => {
|
||||
const pa = a.system?.priority ?? DEFAULT_PRIORITY
|
||||
const pb = b.system?.priority ?? DEFAULT_PRIORITY
|
||||
return pa - pb
|
||||
})
|
||||
}, [])
|
||||
|
||||
if (entries.length === 0) return null
|
||||
|
||||
return (
|
||||
<Suspense fallback={null}>
|
||||
{entries.map(([kind, def]) => {
|
||||
const Comp = loadSystem(def)
|
||||
if (!Comp) return null
|
||||
return <Comp key={`registered-system:${kind}`} />
|
||||
})}
|
||||
</Suspense>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user