The tool, before you install it.
Every shot on this page is the software itself — the editor, the pickers, the datasource browser, the project manager — working on one project: a bottling line called Aquavane, against a live OPC-UA server. The operator screens at the end are what came out of it.
Click any shot to open it full size.
The editor is the same browser tab.
Tree on the left, the real page in the middle, the selected widget's properties on the right. The preview is the runtime — same renderer, same live tag values — so the thing you are dragging around is already reading the machine.
The same page, any house style.
Nothing below is a redraw. It is the built-in widget page from that editor — same page file, same bindings, same live tags — rendered under different themes. Colour roles, type family, corner radii and shadow all come from the token file, so a customer's brand is a theme you add, not a project you fork or a stylesheet you maintain.
The theme is picked per project and can be switched at runtime, so the same build serves the plant's own palette and a machine builder's white label without a second deployment.
Your widgets sit next to the built-ins.
Drop a .tsx into custom-widgets/ and it hot-compiles into this catalog — description, icon, typed properties and all. No Node toolchain, no rebuild of the core, and no second-class treatment: a house widget you wrote three projects ago is offered on the same dialog, in its own category, as the containers and buttons that ship with the platform.
- Categories, not one long list — layout, content, navigation, inputs, and one per widget pack the project carries.
- Every entry describes itself, because the description comes from the widget's own schema.
- Reuse across projects is a folder copy, and the properties come with it.
Any property. Any source. No scripting.
Every property has a type and a source. Leave the source as a literal and you have typed a number in a box; swap it and the same property now reads a tag, branches on a comparison, resolves through a dictionary, or answers differently on a phone than on a panel PC.
The sources nest, so a colour can switch on a live tag, inside a label that resolves through a translation, inside a layout that branches by viewport. One idea, learned once, and it covers every property on every widget — including the ones on the custom widgets you write yourself.
Every property type, its own picker.
A source that points at a variable, an image or an icon opens a picker built for what it holds — filtered, searchable, and previewed at the size the operator will actually see it. Drop your own SVGs and images into the project and they show up in these pickers alongside the built-ins.
One project. Panel PC, tablet, phone.
Not three projects kept in step, and not a desktop layout squeezed onto a handset. Every layout property — direction, size, spacing, whether a widget is on the screen at all — takes the same $viewport source as any other property, so one page file serves the panel at the machine, the tablet in the QA office and the phone in the maintenance engineer's pocket.
The viewport class — phone, tablet, laptop — is just another value to switch on. Bind a container's direction to it and a row becomes a column below a breakpoint; bind a widget's visible to it and the detail that would not survive the shrink is simply absent instead of unreadable.
- The shell branches too — the sidebar's overlay and expanded properties turn the navigation into a dismissable drawer on a phone and an icon rail on a tablet.
- No CSS, no media queries — it is the same binding language as every other property, edited in the same panel.
- Check it before you ship it — the editor's canvas has laptop, tablet and phone presets, so the branch is verified in the same tab that authored it.
// stack the cards on a phone, keep the row on a laptop "direction": { "$switch": { "value": { "$viewport": { "field": "size" } }, "cases": [ { "when": "phone", "then": "column" }, { "when": "tablet", "then": "column" } ], "default": "row" } }
An alarm that tells you what to do about it.
Every alarm carries a code, a severity, a plain-language explanation and a list of resolutions — authored once, shown wherever that alarm surfaces. The night-shift operator gets the same three steps the process engineer would have given them.
- Toast, list and dialog are three views of one definition. Change the text once.
- Acknowledgement is per alarm, with an acknowledge all for the shift handover.
- An illustration slot takes a project image, so the dialog can point at the actual head that jammed.
- The event log keeps the pairs — raised at, cleared at, and whether anyone ever acked it.
Both previews in the centre pane above are the real widgets, not a rendering of them — the popup and the detail dialog the operator will get, redrawn as you type the description and the resolutions.
Logging is a list of tags, not a second product.
Pick the variables worth keeping, set how often and for how long, and the trend widgets have something to draw. It is in the open-source build — no historian add-on, no separate time-series database to stand up and back up.
Recipes, language and theme are editors, not exports.
Three things every plant asks for late in the project, and all three are first-class here — stored as JSON and CSV beside the pages, so they diff, review and travel with the rest of the project.
$loc is here, searchable, with a column you add rather than a project you clone. The runtime switches language without a reload.
Several projects, one server, one page to run them from.
The manager is the front door of the installation. Projects live in ordinary folders, start and stop independently, and move between installations as a zip or straight over the LAN.
What the server is doing, while it is doing it.
Two questions come up on every commissioning day — is it actually subscribed to that tag, and did my widget compile. Both have a page rather than a log file.
Same screens, on your machine, this afternoon.
Both the Docker image and the portable zip boot with a seeded example project, so there is something to click before you have a PLC connected. No trial to request, no quote to wait for.
