diff --git a/packages/editor/src/components/ui/panels/panel-wrapper.tsx b/packages/editor/src/components/ui/panels/panel-wrapper.tsx index 3f9aa19f..951a0127 100644 --- a/packages/editor/src/components/ui/panels/panel-wrapper.tsx +++ b/packages/editor/src/components/ui/panels/panel-wrapper.tsx @@ -7,7 +7,11 @@ import { cn } from '../../../lib/utils' interface PanelWrapperProps { title: string - icon?: string + /** Either a URL path (legacy panels pass `/icons/floor.png` etc., + * rendered via next/image) OR a React node (registry-driven + * inspector renders `` from + * `def.presentation.icon`). */ + icon?: string | React.ReactNode onClose?: () => void onReset?: () => void onBack?: () => void @@ -58,9 +62,18 @@ export function PanelWrapper({ )} - {icon && ( - - )} + {icon && + (typeof icon === 'string' ? ( + + ) : ( + {icon} + ))}

{title}

diff --git a/packages/editor/src/components/ui/panels/parametric-inspector.tsx b/packages/editor/src/components/ui/panels/parametric-inspector.tsx index ee0105e0..eec0dba5 100644 --- a/packages/editor/src/components/ui/panels/parametric-inspector.tsx +++ b/packages/editor/src/components/ui/panels/parametric-inspector.tsx @@ -3,11 +3,13 @@ import { type AnyNode, type AnyNodeId, + type IconRef, nodeRegistry, type ParamField, useScene, } from '@pascal-app/core' import { useViewer } from '@pascal-app/viewer' +import { Icon } from '@iconify/react' import { Move, Trash2 } from 'lucide-react' import { type ComponentType, lazy, Suspense, useCallback } from 'react' import { sfxEmitter } from '../../../lib/sfx-bus' @@ -94,11 +96,12 @@ export function ParametricInspector() { const presentation = def.presentation const title = presentation?.label ?? nodeType ?? '' + const iconNode = renderIcon(presentation?.icon) const canMove = !!def.capabilities.movable const canDelete = def.capabilities.deletable !== false return ( - + {parametrics.groups.map((group, gi) => ( {group.fields.map((field, fi) => ( @@ -132,6 +135,27 @@ export function ParametricInspector() { ) } +function renderIcon(ref: IconRef | undefined): React.ReactNode | undefined { + if (!ref) return undefined + if (ref.kind === 'iconify') { + return + } + if (ref.kind === 'svg') { + return ( + + + + ) + } + // `component`: lazy-loaded custom icon component. Suspense-safe. + const LazyIcon = lazy(ref.module) + return ( + + + + ) +} + // Cache lazy custom panel components by their loader so React.lazy isn't // re-invoked across renders. const customPanelCache = new WeakMap<() => Promise, ComponentType>()