Schema-driven panels

The editor must not know what destroyable means — only how to render an editor for it from a description. Same for a tool's options. So there is one renderer, JSON Schema in, SVG UI widgets out, and it serves both.

const panel = schemaPanel({
  schema: turretSchema,
  values: piece.features.turret,
  handleChange: (key, value) => edit(`turret ${key}`, () => { ... }),
})

Two hand-rolled panels would drift apart inside a week — the tool panel would grow a nicer number field, the property panel would grow a better label, and neither would get the other. Building it once is also what makes a consumer's feature a first-class citizen: it renders because it has a schema, not because someone added a case for it.

Widget mapping

schema widget
number with minimum/maximum slider3d
boolean toggle3d
string with enum select3d
string inputField (an SVG keyboard, so it works in a headset)

x-unit joins the VALUE for a picker and the label otherwise, because a number without its unit is how a range of 260 metres gets typed into a field that wanted kilometres.

Anything unrecognised renders as a disabled label showing the value, not nothing: a field an author cannot see is a field they will assume is unset.