How environments work

One definition, read by the compiler and drawn for the reader.

#One definition, two sides

An environment is what an author writes as canvas.graph, canvas.code or canvas.greeting. Its definition serves two parts of the system that never meet:

  • The compiler reads what an author may write: the environment’s attributes, the child elements in its vocabulary, and what its body may hold. A document that breaks these rules fails to compile, before any reader sees it.
  • The reader’s view uses what the environment does: its Surface, its animation handlers, its toolbar and footer, and the state it keeps.

Keeping both in one definition means an attribute cannot be accepted by the compiler and unknown to the surface, or the other way round.

#Registration

EnvironmentRegistry.register hands the definition to both sides at once. Hosts can then mount the environment by name, and its compile-side part is read three times on every compile:

  1. The library builder turns its attributes and vocabulary into the rules the compiler checks a document against.
  2. The refiner pass re-reads values the compiler left as text: the value types the environment invented, and !cue.to chains, which become keyframe tracks.
  3. The semantic pass checks rules that span the document, then runs the environment’s own validate on each of its nodes.

Nothing registers itself. Compiling with an empty registry throws rather than reporting every canvas.* node as unknown, which would look like a mistake in the document.

#Where JSX goes

An environment renders React in exactly two places: its Surface, and the two bands of its footer. Everything else is data the host draws.

  • The toolbar is a list of buttons, toggles and separators. The host renders them, so every environment’s toolbar looks and behaves the same, and the host’s own controls (present, step position) cannot be displaced.
  • The footer is where bespoke controls belong. Its Bar shows what the environment reports, such as a cursor reading; its Panel, behind a chevron, holds what the reader operates, such as a legend or a console. A scene has one footer, shared by all its environments; the host owns its frame.

#Extending the language

The SDK knows the value types the language has: numbers, durations, intervals, colours, cue anchors. An environment that needs a type of its own declares it in its compile spec, in two halves:

  • valueTypes tells the compiler the type exists and what to accept. A type with its own syntax is declared as a string.
  • types holds a refiner for each: the function that parses that string after the compile. The compiler runs as WebAssembly and cannot call your JavaScript, so parsing happens afterwards.

validate is for rules no single attribute can express, such as two children that may not both be present. The Compiling reference lists the parsing helpers the built-in environments use.

#Steps and cues

A scene’s timeline belongs to the host, not to any environment. The engine calls in: onStep on every step change, and a verb handler when a cued element in the environment’s vocabulary enters or leaves. An environment never runs its own clock for the scene.

A cue acts only within its own environment. Two environments in one scene stay in time because they share step numbers, not because one drives the other.

An attribute declared animatable can be animated with !cue.to, and ctx.animation.attrValue returns its value at this moment. For a value shape the SDK does not know how to blend, an environment supplies its own interpolations.

#State

An environment keeps state in one of two tiers, declared in storage:

  • Session state lasts while the document is open. It survives the reader scrolling away and back, and is never saved. An environment written with continue: in a later scene takes it over, shaped by serializeForContinue.
  • Database state, with tiers: 'session+db', is saved per reader and is there on their next visit. It is for environments that record a reader’s answers.

A surface and its footer are separate React trees. Both read the session through useEnvironmentSession, which is how they share state.

Last updated