Phase 5 batch kind: item migrates to registry (always-on)

Item is the first kind to use the `def.renderer` escape hatch (custom
React component with `useGLTF` + drei + interactive widgets) — not
expressible as a pure `def.geometry`. Catalog-backed + multi-host
(free / wall / wall-side / ceiling).

Files added (packages/nodes/src/item/):
 - schema.ts: re-exports ItemNode from core.
 - parametrics.ts: empty groups[]. Item parametrics come from the
   asset's catalog-defined interactive controls (toggles / sliders /
   temperature) — too dynamic for the auto-inspector at Stage A.
   Legacy ItemPanel renders the catalog-driven controls; Phase 5
   Stage E will likely use parametrics.customPanel.
 - definition.ts: capabilities (no `movable` — item move is bespoke
   MoveItemContent that handles attachTo transitions floor↔wall↔
   ceiling mid-drag; capability-driven dispatch keeps legacy mover),
   parametrics, renderer (wrap-export of ItemRenderer), system
   bundling ItemSystem + ItemLightSystem, toolHints matching the
   user's screenshot (Place item / R rotate ccw / T rotate cw /
   Shift free place / Esc cancel). defaults() casts an object
   literal with a stub asset since asset is required by the schema;
   createNode re-parses through ItemNode at runtime.
 - renderer.tsx: wrap-export of legacy ItemRenderer (~280 lines with
   useGLTF + interactive widgets — too much to duplicate at Stage A).
 - system.tsx: bundles ItemSystem + ItemLightSystem.
 - index.ts: barrel.

Files changed:
 - packages/viewer/src/index.ts: new public exports for ItemRenderer,
   ItemSystem, ItemLightSystem.
 - packages/nodes/src/index.ts: appends itemDefinition.
 - packages/editor/src/components/ui/panels/item-panel.tsx: panel
   slider-drag fix recipe applied (nodeRef pattern, drop subscribed
   updateNode dep, drop node from useCallback deps). Item panel has
   scale + position + rotation sliders all subject to the cascade.

Item is the registry's stress test for `def.renderer` escape hatch.
GLB loading via useGLTF + drei works as-is; nothing in the registry
forces a pure-geometry shape on kinds that don't fit.

Phase 5 progress: shelf  spawn  wall  fence  slab  ceiling 
door  window  item . Nine kinds on the registry. Stair / roof /
zone / containers remain.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Wassim SAMAD
2026-05-15 15:23:08 -04:00
co-authored by Claude Opus 4.7
parent 9eced06f32
commit 8d65be17fa
9 changed files with 206 additions and 13 deletions
+9 -6
View File
@@ -2,6 +2,7 @@ import type { AnyNodeDefinition, Plugin } from '@pascal-app/core'
import { ceilingDefinition } from './ceiling'
import { doorDefinition } from './door'
import { fenceDefinition } from './fence'
import { itemDefinition } from './item'
import { shelfDefinition } from './shelf'
import { slabDefinition } from './slab'
import { spawnDefinition } from './spawn'
@@ -19,12 +20,12 @@ import { windowDefinition } from './window'
* `loadPlugin` call path. This is intentional: the API is stress-tested
* by built-ins before any third-party plugin lands.
*
* All four current kinds are registered unconditionally. Parity is
* verified by comparing against deployed production rather than an
* in-app env-var flag toggle. Legacy paths still exist in `viewer/` and
* `editor/` for kinds undergoing migration; they short-circuit via the
* Phase 0 `<LegacySystem>` wrapper + `NodeRenderer`'s registry-first
* dispatch. Phase 6 deletes the legacy paths.
* All kinds are registered unconditionally. Parity is verified by
* comparing against deployed production rather than an in-app env-var
* flag toggle. Legacy paths still exist in `viewer/` and `editor/` for
* kinds undergoing migration; they short-circuit via the Phase 0
* `<LegacySystem>` wrapper + `NodeRenderer`'s registry-first dispatch.
* Phase 6 deletes the legacy paths.
*/
export const builtinPlugin: Plugin = {
id: 'pascal:core',
@@ -38,12 +39,14 @@ export const builtinPlugin: Plugin = {
ceilingDefinition as unknown as AnyNodeDefinition,
doorDefinition as unknown as AnyNodeDefinition,
windowDefinition as unknown as AnyNodeDefinition,
itemDefinition as unknown as AnyNodeDefinition,
],
}
export { ceilingDefinition } from './ceiling'
export { doorDefinition } from './door'
export { fenceDefinition } from './fence'
export { itemDefinition } from './item'
export { shelfDefinition } from './shelf'
export { slabDefinition } from './slab'
export { spawnDefinition } from './spawn'