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>()