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:
co-authored by
Claude Opus 4.7
parent
9eced06f32
commit
8d65be17fa
@@ -3,7 +3,7 @@
|
||||
import { type AnyNode, getScaledDimensions, ItemNode, useScene } from '@pascal-app/core'
|
||||
import { useViewer } from '@pascal-app/viewer'
|
||||
import { Copy, Link, Link2Off, Move, Trash2 } from 'lucide-react'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { useCallback, useRef, useState } from 'react'
|
||||
import { sfxEmitter } from '../../../lib/sfx-bus'
|
||||
import { cn } from '../../../lib/utils'
|
||||
import useEditor from '../../../store/use-editor'
|
||||
@@ -16,7 +16,6 @@ import { PanelWrapper } from './panel-wrapper'
|
||||
export function ItemPanel() {
|
||||
const selectedId = useViewer((s) => s.selection.selectedIds[0])
|
||||
const setSelection = useViewer((s) => s.setSelection)
|
||||
const updateNode = useScene((s) => s.updateNode)
|
||||
const deleteNode = useScene((s) => s.deleteNode)
|
||||
const setMovingNode = useEditor((s) => s.setMovingNode)
|
||||
|
||||
@@ -26,18 +25,26 @@ export function ItemPanel() {
|
||||
|
||||
const [uniformScale, setUniformScale] = useState(true)
|
||||
|
||||
// Panel slider-drag fix recipe (plans/editor-node-registry.md). Item
|
||||
// panel has scale + position + rotation sliders — same Maximum update
|
||||
// depth cascade risk as fence / wall / etc. without the nodeRef.
|
||||
const nodeRef = useRef(node)
|
||||
nodeRef.current = node
|
||||
|
||||
const handleUpdate = useCallback(
|
||||
(updates: Partial<ItemNode>) => {
|
||||
if (!(selectedId && node)) return
|
||||
updateNode(selectedId as AnyNode['id'], updates)
|
||||
if (!selectedId) return
|
||||
const n = nodeRef.current
|
||||
if (!n) return
|
||||
useScene.getState().updateNode(selectedId as AnyNode['id'], updates)
|
||||
|
||||
if (node.asset.attachTo === 'wall' && node.parentId) {
|
||||
if (n.asset.attachTo === 'wall' && n.parentId) {
|
||||
requestAnimationFrame(() => {
|
||||
useScene.getState().dirtyNodes.add(node.parentId as AnyNode['id'])
|
||||
useScene.getState().dirtyNodes.add(n.parentId as AnyNode['id'])
|
||||
})
|
||||
}
|
||||
},
|
||||
[selectedId, node, updateNode],
|
||||
[selectedId],
|
||||
)
|
||||
|
||||
const handleClose = useCallback(() => {
|
||||
|
||||
Reference in New Issue
Block a user