Scene schemas

JSON Schema for the scene primitives, so a consumer never hand-copies our attributes. No DOM, no Babylon — importable from a headless runner.

Why this exists

tosijs-3d-ensemble generates its property panels from JSON Schema, and wrote one for b3d-skybox by hand: a copy of our attributes, maintained in another repository, against a component they do not own. It drifted in both directions within one release — their schema exposed 6 of 16 attributes (the other ten were never hidden deliberately, just not copied), and their applyFog defaulted true where ours defaults false. Same name, same component, opposite behaviour, and nothing anywhere failed.

lightSettingsSchema had already shown the alternative: mark one field and the whole lamp appears, with no schema on their side to maintain. This is that, for the rest of the scene.

These carry NO x-widget

Deliberately, and it is the one thing to get right. A widget token says "hand this whole value to a custom editor" — correct for a light program, wrong here, because a consumer's generated panel already renders numbers, colours, booleans and enums perfectly well. Adding a token would point at an editor that does not exist and break the panel that works.

What these DO carry is everything initAttributes cannot say: ranges, units, enum members, colour formats, and which sliders want a log scale.

format, not x-widget, is what a control keys off

format: 'color' is JSON Schema's own spelling, and it stays the one to read. The dispatch a consumer wants is:

// property.format === 'color'  →  a field that knows it holds a colour
ui.inputField({ value: current, type: 'color', handleChange })
// or the full picker
color3d({ value: current, handleChange })

FieldType gained 'color' for exactly this, so the two vocabularies line up: a schema says format, a field says type, and neither has to guess. x-widget stays reserved for values JSON Schema has no word for at all — a light program, a curve — where the answer really is "hand the whole thing to a custom editor".

Asked by tosijs-3d-ensemble (#72), whose generated panel had eight colour properties it could not render and had to fall back to unvalidated hex text.

The host has one too: sceneSchemas.b3d

<tosi-b3d> itself carries scene-level settings: the clear colour, glow, the ambient occlusion look (ssaoStrength, ssaoRadius), the scene wind and the time scale. b3dSchema() describes those and ONLY those. The host's other attributes configure a viewer rather than a scene (camera limits, quality tier, pixel ratio, gamepad and panel chrome, XR and pause behaviour) and are declined by name in SCENE_OMITTED.b3d, so a document never stores someone's laptop. Asked by tosijs-3d-ensemble (#101).

Drift is a test, not a promise

The defaults here are duplicated from the components — there is no way to read them without importing Babylon, which is exactly what makes this module usable. So scene-schemas.test.ts imports the real components and fails if an attribute is missing, extra, or disagrees on its default. The copy is allowed to exist because it cannot silently rot.