Same shape as wall milestone B — thin renderer + system re-export, no geometry / floor-plan / tool ports yet (later milestones). Feature flag NEXT_PUBLIC_USE_REGISTRY_FOR_FENCE gates the dispatch flip. Files added (packages/nodes/src/fence/): - schema.ts: re-exports FenceNode from core. - parametrics.ts: dimensions / posts / style fields for the auto- inspector. Endpoints + curveOffset edited via tools, not in parametrics. - feature-flag.ts: mirrors the wall flag pattern. - definition.ts: capabilities (snappable + surfaces sides + selectable + duplicable + deletable), relations (linkedBy endpoint-match, no hosts, no affectsSpatial — matches legacy), parametrics, renderer, system, toolHints (Left click / Shift / Esc — fence has no helper file today so this adds a panel where there wasn't one). Tool field absent: fence has 4 tools (build, curve, move, move-endpoint) wired through editor state, not the registry dispatch — they keep running unchanged. - renderer.tsx: thin placeholder mesh + markDirty on mount + node events + DEFAULT_STAIR_MATERIAL (matches legacy material reuse). Verification log fires once on first mount. - system.tsx: re-exports the legacy FenceSystem from viewer. Verification log on mount/unmount confirms the bundle activates. - index.ts: barrel. Files changed: - packages/viewer/src/index.ts: new exports for FenceSystem and DEFAULT_STAIR_MATERIAL so the @pascal-app/nodes bundle can compose them without reaching into viewer internals. - packages/viewer/src/components/renderers/fence/fence-renderer.tsx: paired one-shot legacy verification log so the dispatch path is unambiguous from the browser console. - packages/nodes/src/index.ts: conditional fenceEntries appended to builtinPlugin.nodes based on isFenceRegistryEnabled. With the flag off (default), behavior is unchanged; with it on, Phase 0 shims switch fence to the registry path — legacy <FenceRenderer> and <LegacySystem kind="fence"><FenceSystem /></LegacySystem> short- circuit, the bundled system.tsx re-mounts FenceSystem via RegisteredSystems, and the new renderer takes over the dispatch. No behavior change with the flag off. With it on, behavior should be byte-identical (same FenceSystem code, same priority, same geometry path). Phase 5 batch order continues with slab / ceiling / door / window / item / etc. as flagged migrations after fence parity signs off. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
55 lines
1.6 KiB
TypeScript
55 lines
1.6 KiB
TypeScript
'use client'
|
|
|
|
import { type FenceNode, useRegistry, useScene } from '@pascal-app/core'
|
|
import { DEFAULT_STAIR_MATERIAL, useNodeEvents } from '@pascal-app/viewer'
|
|
import { useEffect, useLayoutEffect, useMemo, useRef } from 'react'
|
|
import type { Mesh } from 'three'
|
|
|
|
/**
|
|
* Thin fence renderer — registers an empty mesh, marks the node dirty so
|
|
* `FenceSystem` (re-exported via `./system`) fills the geometry next
|
|
* frame, and wires pointer events through `useNodeEvents`.
|
|
*
|
|
* Behaviorally identical to the legacy `FenceRenderer` in
|
|
* `@pascal-app/viewer/components/renderers/fence/fence-renderer.tsx`.
|
|
* Phase 0 shims pick which one mounts based on `nodeRegistry.has('fence')`.
|
|
*
|
|
* Material is `DEFAULT_STAIR_MATERIAL` (legacy reuse; fence and stairs
|
|
* share the wood-tone preset).
|
|
*/
|
|
let didLogFirstRegistryFenceMount = false
|
|
|
|
const FenceRenderer = ({ node }: { node: FenceNode }) => {
|
|
const ref = useRef<Mesh>(null!)
|
|
const handlers = useNodeEvents(node, 'fence')
|
|
const material = useMemo(() => DEFAULT_STAIR_MATERIAL, [])
|
|
|
|
useRegistry(node.id, 'fence', ref)
|
|
useLayoutEffect(() => {
|
|
useScene.getState().markDirty(node.id)
|
|
}, [node.id])
|
|
|
|
useEffect(() => {
|
|
if (didLogFirstRegistryFenceMount) return
|
|
didLogFirstRegistryFenceMount = true
|
|
console.info(
|
|
'[fence:registry] first registry-driven FenceRenderer mounted — legacy FenceRenderer is NOT in use',
|
|
)
|
|
}, [])
|
|
|
|
return (
|
|
<mesh
|
|
castShadow
|
|
material={material}
|
|
receiveShadow
|
|
ref={ref}
|
|
visible={node.visible}
|
|
{...handlers}
|
|
>
|
|
<boxGeometry args={[0, 0, 0]} />
|
|
</mesh>
|
|
)
|
|
}
|
|
|
|
export default FenceRenderer
|