App Studio is in release 3.0 as Beta: a paid add-on that no edition includes by default. A licence or a trial licence that includes the app_studio feature switches it on. The module is on by default, so the licence decides. This reference may change while App Studio is in Beta; the SDK changelog records each change.
Generated from the SDK sources by scripts/appstudio-sdk-docs.cjs for runtime-v1. Do not edit; run npm run appstudio:docs:sdk.
@maestrohub/scene
Beta: look up the scene kit an app uses to draw its line in 3D, with stations, buffers, finished goods and operators following live topics.
- Runtime
- runtime-v1
- Exports
- 47 (22 values, 25 types)
The App Studio scene kit: a live 3D production line drawn from a scene spec and bound to topics (Beta in 3.0).
Runtime: runtime-v1. 47 exports (22 values, 25 types). Import with:
import { ... } from '@maestrohub/scene';
Components
Scene
function Scene({ spec, values, onSelect, height, quality, view, focus, overlay, hud }: SceneProps): JSX.Element
Scene draws a production line from a scene spec and binds it to live
topics: station state drives the stack light and motion, a rise in the
good counter sends a part down the line, a rise in the reject counter
drops one into the scrap bin, a buffer holds as many parts as its level.
Needs uns:read for live topics (not for values).
Example
import scene from './scene.json';
<Scene spec={scene} onSelect={(id) => setSelected(id)} />
ValueStream
function ValueStream({ spec, values, part }: ValueStreamProps): JSX.Element | null
ValueStream draws a scene's value stream as a flat VSM sheet, bound to
its live topics (or to values). Renders nothing when the scene has no
vsm.
Example
<ValueStream spec={scene} />
Functions
bufferFill
function bufferFill(raw: unknown, capacity: number): number
How many parts a buffer shows: the level clamped to 0..capacity.
counter
function counter(raw: unknown): number | null
A counter value as a number (null when absent or not numeric).
durationText
function durationText(raw: unknown): string
A duration in seconds as text: "0.32 s", "12 s", "41 min", "1.7 h"; "–" when absent.
isOffBelt
function isOffBelt(kind: StationKind): boolean
True for process equipment, which stands off the belt.
levelOf
function levelOf(raw: unknown, levelMax?: number): number | null
A tank's fill 0..1: a height against levelMax when given, else a share (0..1 or 0..100); null when absent.
mediumColor
function mediumColor(id: string | undefined): string
A medium's colour (product's for an unknown or absent one).
percentText
function percentText(raw: unknown): string
A share (0..1 or 0..100) as a whole percentage; "–" when absent.
pipeFlowing
function pipeFlowing(spec: SceneSpec, p: PipeSpec, values: Record<string, unknown>): boolean
Whether a pipe flows: its flow topic above zero, or (no flow topic) its from-station running.
plainValue
function plainValue(v: unknown): unknown
Reads a plain value out of a topic value: the value itself, or its value / state field.
positionOf
function positionOf(raw: unknown): number | null
A valve's opening 0..1, from a share (0..1 or 0..100), a word (open, closed, moving) or a boolean; null when absent.
progress
function progress(raw: unknown): number | null
Cycle progress as 0..1 (accepts 0..100), null when absent.
readoutAlert
function readoutAlert(raw: unknown, r: ReadoutSpec): 0 | 1 | 2
0 fine, 1 past the warning, 2 at or past the alarm. Not a number is never an alert.
readoutText
function readoutText(raw: unknown): string
A readout's value as text: whole numbers as they are, others to one decimal.
sceneTopics
function sceneTopics(spec: SceneSpec): string[]
Every topic path a spec binds, once each.
stageRow
function stageRow(key: "cycle" | "uptime" | "oee", bind: StageSpec["bind"], values: Record<string, unknown>): { text: string; title?: string; }
A stage box's row, as the flat sheet, the lead-time ladder and the
overlay cards all show it: the cycle time as a duration, uptime and OEE
as percentages. When the stage's scheduled topic is false every row
reads "Not scheduled" (the reason as title): none of the three is
published then, so what is left on their topics is the last scheduled
window's. A missing scheduled topic or value is no data yet, not "not
scheduled": the rows then read as they are. A scheduled stage whose
oeeComputed topic is false reads "–" for OEE, with the reason: no
OEE was published, so what is on its topic is an older window's.
stationState
function stationState(raw: unknown, own?: Record<string, StationState>): StationState
stationState maps a raw state value to a kit state: the station's own map first, then the shared default map (upper-cased), booleans as running/off. Unknown values read as idle, never as running.
webglAvailable
function webglAvailable(): boolean
True when this browser can draw WebGL at all.
worstTopics
function worstTopics(v: VsmSpec | undefined): string[]
A value stream's worst-stage topics, one or many.
Constants
SCENE_VOCABULARY
const SCENE_VOCABULARY: { version: number; kinds: StationKind[]; states: StationState[]; defaultStates: Record<string, StationState>; qualities: SceneQuality[]; maxStations: number; maxBuffers: number; maxCapacity: number; idPattern: string; maxLabelOffset: number; maxAreas: number; maxLines: number; maxViews: number; maxCoordinate: number; facings: number[]; lineSpacing: { min: number; max: number; default: number; }; besideOffset: number; offBelt: StationKind[]; media: { id: Medium; color: string; }[]; products: Product[]; maxReadouts: number; maxUnit: number; maxStages: number; maxInventories: number; maxSystems: number; maxPipes: number; maxVia: number; rackHeight: number; pipeLevels: ("rack" | "floor")[]; }
The shared vocabulary (also read by the server).
Members
besideOffset:numberdefaultStates:Record<string, StationState>facings:number[]idPattern:stringkinds:StationKind[]lineSpacing:{ min: number; max: number; default: number; }maxAreas:numbermaxBuffers:numbermaxCapacity:numbermaxCoordinate:numbermaxInventories:numbermaxLabelOffset:numbermaxLines:numbermaxPipes:numbermaxReadouts:numbermaxStages:numbermaxStations:numbermaxSystems:numbermaxUnit:numbermaxVia:numbermaxViews:numbermedia:{ id: Medium; color: string; }[]offBelt:StationKind[]pipeLevels:("rack" | "floor")[]products:Product[]qualities:SceneQuality[]rackHeight:numberstates:StationState[]version:number
STATE_COLOR
const STATE_COLOR: Record<StationState, string>
The colour each state shows on the stack light and the station label.
Members
blocked:stringfaulted:stringidle:stringmaintenance:stringoff:stringrunning:stringstarved:string
Types
AreaSpec
interface AreaSpec
A hall or zone on the floor: painted, walled low, named. at is its centre.
Members
at:FloorPointid:stringlabel?:string | undefinedsize:{ w: number; d: number; }: Width along x and depth along z, in metres.
BufferSpec
interface BufferSpec
A buffer conveyor on the line, filled by a level topic: after a station
(after), or before one (before), which is how a raw material buffer
sits ahead of the first station. Exactly one of the two.
Members
after?:string | undefined: The station this buffer follows.before?:string | undefined: The station this buffer feeds: the buffer sits just upstream of it.bind?:{ level?: string; } | undefinedcapacity:number: Parts the buffer holds when full.id:stringlabel?:string | undefinedlabelOffset?:LabelOffset | undefined: Moves the buffer's label.
FloorPoint
interface FloorPoint
A place on the floor, in metres.
Members
x:numberz:number
InventorySpec
interface InventorySpec
An inventory triangle at a station (a tank, a buffer's station): how much waits there, and for how long.
Members
at:stringbind?:{ amount?: string; wait?: string; } | undefinedid:stringlabel?:string | undefinedunit?:string | undefined
LabelOffset
interface LabelOffset
Moves a label from where the kit puts it, in metres along the line (x),
up (y) and towards the viewer (z); each at most maxLabelOffset. For
labels that crowd each other near the end of a long line.
Members
x?:number | undefinedy?:number | undefinedz?:number | undefined
LineSpec
interface LineSpec
A line of stations. Its first belt station stands at at; the rest
follow every spacing metres (default 7) in the facing direction:
0 runs along +x, 90 along -z, 180 along -x, 270 along +z.
Members
area?:string | undefined: The area it stands in (for grouping and the 2D view).at:FloorPointfacing?:0 | 90 | 180 | 270 | undefinedid:stringlabel?:string | undefinedproduct?:Product | undefined: What rides the belt (default part).spacing?:number | undefined
Medium
type Medium = 'product' | 'syrup' | 'water' | 'steam' | 'air' | 'gas' | 'cip';
What fills a tank or a header, and the colour it shows.
ModelRef
interface ModelRef
A .glb model stored as an attachment on an app record (it replaces the kit's body for the station).
Members
attachment:string: The attachment id of the .glb.collection:string: The storage collection holding the record.key:string: The record's key.scale?:number | undefined: Uniform scale applied to the model (default 1).
OperatorSpec
interface OperatorSpec
A person on the line. They stand at home; when a station they cover
faults, they walk to it, work on it, and walk back once it recovers.
Members
covers?:string[] | undefined: Stations they answer (default: onlyhome).home:string: The station they stand at when nothing needs them.id:stringlabel?:string | undefinedlabelOffset?:LabelOffset | undefined: Moves the operator's name tag.vest?:string | undefined: Vest colour (default hi-vis orange).
OutputSpec
interface OutputSpec
Where finished parts pile up: pallets after a station, filled in layers.
Members
after:string: The station that puts parts here (often the last one, an unload robot).bind?:{ count?: string; } | undefinedid:stringlabel?:string | undefinedlabelOffset?:LabelOffset | undefined: Moves the output's label (the count above its pallet).perPallet?:number | undefined: Parts on one full pallet (default 24); a full pallet moves to the shipped stack.
PipeSpec
interface PipeSpec
A pipe between two stations, in its medium's colour, moving while it
flows. It runs on the overhead rack between lines (each medium at its
own height) and along the floor within a line; level overrides that,
via steers it through floor points.
Members
bind?:{ flow?: string; } | undefined: Flow through it: beads move while it is above zero. Without it, the pipe flows while its from-station runs.from:stringid:stringlabel?:string | undefinedlevel?:"rack" | "floor" | undefinedmedium:Mediumto:stringvia?:FloorPoint[] | undefined
Product
type Product = 'part' | 'bottle' | 'can' | 'case';
What rides a line's belt.
ReadoutSpec
interface ReadoutSpec
A value a station shows on its screen, its label and its panel. Past
warn it needs attention (amber), at or past alarm it is in alarm
(red: the stack light turns red, a pump shakes, operators come). The
limits apply on the when side: above (the default) or below.
Members
alarm?:number | undefinedlabel:stringtopic:stringunit?:string | undefinedwarn?:number | undefinedwhen?:"above" | "below" | undefined
SceneHud
interface SceneHud
The slots of Scene's hud: each is any React content.
Members
bottom?:ReactNode: Along the bottom, between the corners (filling the page, the value stream's ladder goes here).bottomLeft?:ReactNodebottomRight?:ReactNodetopLeft?:ReactNodetopRight?:ReactNode
SceneProps
interface SceneProps
Props for Scene.
Members
focus?:string | undefined: A station's id: the camera flies in on it (unset: back to the view).height?:number | "fill" | undefined: Height in pixels (default 460), or "fill": the height the parent gives it, for a canvas that fills the page (give the parent a height, e.g.height: '100vh'or a flex child). The width follows the container.hud?:SceneHud | undefined: An app's own panels, floating over the picture: in its corners and along its bottom. The layer between them lets clicks through to the scene. Without WebGL they stand above the cards instead.onSelect?:((stationId: string) => void) | undefined: A station was clicked (its id).overlay?:"vsm" | undefined: "vsm" shows the value stream over the plant (stage boxes, inventory triangles, systems) with its ladder underneath.quality?:SceneQuality | undefined: Overrides the spec's quality.spec:SceneSpec: The scene: usuallyimport scene from './scene.json'.values?:Record<string, unknown> | undefined: Topic path → value, instead of live topics: sample data, a replay, or values the app computed. When set, the scene subscribes to nothing.view?:string | undefined: A view's id fromspec.views: the camera flies there (unset: the whole scene).
SceneQuality
type SceneQuality = 'low' | 'balanced' | 'high';
Rendering quality: balanced is the default for shop-floor screens.
SceneSpec
interface SceneSpec
The whole scene: what src/scene.json holds.
Members
areas?:AreaSpec[] | undefined: Halls and zones on the floor (a plant).buffers?:BufferSpec[] | undefinedlines?:LineSpec[] | undefined: Lines of stations; without them the scene is one straight line.operators?:OperatorSpec[] | undefinedoutputs?:OutputSpec[] | undefinedpipes?:PipeSpec[] | undefined: Pipes between stations.quality?:SceneQuality | undefinedstations:StationSpec[]version:1views?:ViewSpec[] | undefined: Named camera views;<Scene view>flies to one.vsm?:VsmSpec | undefined: The value stream layer.
StageSpec
interface StageSpec
A process box: some stations, with their cycle time, uptime and OEE.
Members
bind?:{ cycle?: string; uptime?: string; oee?: string; scheduled?: string; oeeComputed?: string; } | undefined:scheduled: a topic whose value is false when the stage had no planned production in the window. Its uptime and OEE then read "Not scheduled" (availability is undefined without planned time), not a blank or the last share still on their topics.oeeComputed: a topic whose value is false when the stage was scheduled but its OEE could not be computed in the window (too few counter readings). OEE then reads"–"with the reason, not the last OEE still on its topic. Not scheduled wins over not computed.id:stringlabel?:string | undefinedstations:string[]
StationKind
type StationKind = 'cnc' | 'washer' | 'tester' | 'robot' | 'generic' | 'filler' | 'capper' | 'labeller' | 'palletizer' | 'tank' | 'pump' | 'valve' | 'boiler' | 'compressor' | 'header' | 'instrument' | 'heat_exchanger';
The kinds of station the kit can draw.
StationSpec
interface StationSpec
One station on the line, drawn in spec order from left to right.
Members
beside?:string | undefined: Stands next to this station of the same line, off the belt: a standby pump beside its duty pump. Takes no place in the line's order.bind?:{ state?: string; good?: string; bad?: string; cycle?: string; level?: string; flow?: string; position?: string; speed?: string; } | undefined: Topic paths the station reads.id:string: Unique within the scene; also the keyonSelectreports.kind:StationKindlabel?:string | undefined: Shown above the station; defaults to the id.labelOffset?:LabelOffset | undefined: Moves the station's label.levelMax?:number | undefined: A tank's level reading when full, when the level is not a share: 3.0 for a 3 m tank read in metres.line?:string | undefined: The line it stands on (a scene withlines; every station then names one).medium?:Medium | undefined: What fills a tank or a header (default product).model?:ModelRef | undefined: A stored .glb drawn instead of the kit body.readouts?:ReadoutSpec[] | undefined: Up to four values it shows, with limits.states?:Record<string, StationState> | undefined: Raw state value → kit state, for values the default map does not know.
StationState
type StationState = 'running' | 'idle' | 'starved' | 'blocked' | 'faulted' | 'maintenance' | 'off';
The states a station shows (stack light, motion, label).
SystemSpec
interface SystemSpec
An information system (ERP, MES, CMMS) and the lines or areas it talks to.
Members
bind?:{ value?: string; } | undefinedid:stringlabel?:string | undefinedto?:string[] | undefined
ValueStreamProps
interface ValueStreamProps
Props for ValueStream.
Members
part?:"all" | "ladder" | undefined: "ladder" draws only the lead-time ladder (what <Scene overlay="vsm"> puts under the plant).spec:SceneSpec: The scene; only itsvsmis drawn.values?:Record<string, unknown> | undefined: Topic path → value instead of live topics (then nothing is subscribed).
ViewSpec
interface ViewSpec
A named camera view: an area, a line, some stations, or (none) the whole scene.
Members
area?:string | undefinedid:stringlabel?:string | undefinedline?:string | undefinedstations?:string[] | undefined
VsmSpec
interface VsmSpec
The value stream layer. Every number is a topic a pipeline publishes
(cycle time and waits in seconds, shares as 0..1 or 0..100); the kit
only shows them, over the plant (<Scene overlay="vsm">) and as a flat
sheet (<ValueStream>).
Members
inventories?:InventorySpec[] | undefinedladder?:{ steps: string[]; bind?: { lead?: string; valueAdded?: string; }; } | undefined: The lead-time ladder: stage and inventory ids in the order the product meets them.stages?:StageSpec[] | undefinedsystems?:SystemSpec[] | undefinedworst?:string | string[] | undefined: A topic (or one per line) whose value is the id of the stage losing most now: it carries the kaizen burst.