Inspector

The properties panel for whatever is selected — the other half of a canvas. Driven by a field schema rather than by children, because the fields change with the selection and writing that as JSX means a switch statement per kind at the call site.

src/components/ui/inspector.tsx

About

From the component's own source, where it was written.

The properties panel for whatever is selected.

The other half of a canvas: NodeCanvas owns position and wiring, this owns everything else about the selected thing. Also the right shape for a layer panel, a chart configurator, or any "click a thing, edit its fields" surface.

Driven by a schema rather than by children, because the fields change with the selection — a model node and a tool node have almost nothing in common, and writing that as JSX means a switch statement per node kind at the call site. Describe the fields and the panel renders them.

**One onChange for the whole object, not one per field.** A panel that emits (key, value) pushes the merge onto every caller, and the merge is where the stale-closure bugs live. This emits the next object.

Sections are flat and always visible. An inspector is read at a glance while doing something else; collapsing groups saves vertical space and costs the glance, which is the whole point of the panel.

Install

npx astralyx-ui add inspector

Usage

import { Inspector } from '@/components/ui/inspector'

<Inspector sections={sections} value={node.data} onChange={updateNode} title={node.name} />

Composer

Change the props and the source below follows.

Plan the reply

Model call

Step

plan

Model

Zero for anything that touches money.

Props
tsx
<Inspector
  sections={sections}
  value={value}
  onChange={setValue}
  title="Plan the reply"
/>

Examples

Editing a canvas node

Nothing selected

API

Generated from the source, so it cannot fall behind the code. One table per exported component, then hooks, functions and types.

Inspector

Extends Omit<ComponentProps<'div'>, 'onChange' | 'title'>.

PropTypeDefaultDescription
sectionsInspectorSection[]required{ label?, fields }. Fields are text, textarea, number, boolean, select or readonly.
valueInspectorValuerequiredOne onChange for the whole object, not one per field. A (key, value) callback pushes the merge onto every caller, and the merge is where the stale-closure bugs live.
onChange(next: InspectorValue) => voidrequiredOne onChange for the whole object, not one per field. A (key, value) callback pushes the merge onto every caller, and the merge is where the stale-closure bugs live.
titleReactNode—Panel heading — usually the selected thing’s name.
descriptionReactNode—Panel heading — usually the selected thing’s name.
emptyLabelstring'Nothing selected.'Pass an empty sections array alongside it.

Types

InspectorSection

FieldTypeDescription
label?string—
fieldsInspectorField[]—
TypeDefinitionDescription
InspectorField
| { type: 'text'; key: string; label: string; placeholder?: string; hint?: ReactNode }
| { type: 'textarea'; key: string; label: string; rows?: number; placeholder?: string; hint?: ReactNode }
| { type: 'number'; key: string; label: string; min?: number; max?: number; step?: number; hint?: ReactNode }
| { type: 'boolean'; key: string; label: string; hint?: ReactNode }
| {
    type: 'select'
    key: string
    label: string
    options: { value: string; label: string }[]
    hint?: ReactNode
  }
/** Not editable — an id, a computed total, a timestamp. */
| { type: 'readonly'; key: string; label: string; hint?: ReactNode }
The properties panel for whatever is selected.
InspectorValue
Record<string, unknown>
—

Behaviour

TopicDetailDescription
accessibilityField + useFieldControlEvery control is wired to its label and hint through Field’s context, so ids and aria-describedby are generated rather than hand-managed.