feat: render a workflow from JSON passed in from outside (claude) - #14
Conversation
Add `WorkflowViewer`: hand it workflow JSON (a config object, a JSON string of one, or a payload carrying one under `workflow`) and it shows the workflow's units as cards plus the selected subworkflow's units as a flowchart, with no store, router or dependency injection to wire up first. `createWorkflowFromConfig()` turns that JSON into the instances the components read by building a wode `Workflow`, filling in the entity lists its constructor cannot do without (`workflows` is a required prop that external configs routinely omit). A config the entity rejects — partial, or written against another schema version — falls back to a read-only plain-JSON view instead of leaving the container blank; the JSON comes from outside, so it is not ours to assume complete. The standalone bundle now exposes `window.renderWorkflow(workflowConfig, container, options)`, the same contract as wave.js' `window.renderThreeDEditor(materialConfig, container)`, so a notebook or any other page can embed a workflow view through the api-examples viewer helpers with `render_function="renderWorkflow"`. Stylesheets are emitted as `main.css` to keep that URL stable next to `main.js`, and the demo page mounts into its own container so importing the bundle only registers schemas and defines the global rather than rendering anything. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014TXqtpnFoKB8rHjkqUpAmM
Review feedback: use the schemas from ESSE rather than hand-rolled types. `WorkflowConfig` is now `Partial<WorkflowSchema>` (partial because JSON from outside is not ours to assume complete), units are ESSE's `WorkflowUnitSchema` and the subworkflow-level union wode composes from ESSE unit schemas, a container config is keyed off `JobSchema`, and the subworkflow stand-in extends `SubworkflowSchema` with only what the entities add on top — `id`, `unitsInstances`, `modelInstance`, `setIsDraft`, `toJSON`. `WorkflowViewer` follows: its units, subworkflows and `onUnitSelect` are typed, not `any`. The two casts that remain are the boundary where external JSON becomes an entity config, and both are commented: past them a config the entity rejects lands in the read-only fallback rather than as a type error at the call site. Behaviour is unchanged — the same 44 tests pass, and both the demo page and a page calling `window.renderWorkflow` from the built bundle render as before. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014TXqtpnFoKB8rHjkqUpAmM
…E schemas Review feedback: WorkflowLike/SubworkflowLike were a third description of a workflow, alongside the ESSE schemas (the JSON) and the wode entities (the instances). They existed only to type the plain-JSON stand-ins that adaptWorkflowConfig/adaptSubworkflowConfig built when the entity constructor rejected a config — and those stand-ins only reached WorkflowUnitCard, OverviewAccordion and Properties, all of which declare `subworkflow: WodeSubworkflow`, because WorkflowUnitsFlowchart typed `workflow` as `any`. The `any` was laundering a type the components would otherwise have refused. So: JSON in is ESSE's WorkflowSchema/JobSchema, what comes out is a wode Workflow, and nothing describes a workflow in between. The stand-ins, both Like interfaces, the UnitInstance wrappers and isWorkflowLike are gone, along with ~140 lines of adapter code. The fallback they served turns out to be dead weight: the entity rejects a config only when it is not valid ESSE (an execution unit without `application`, which the schema requires), and all 57 standata workflows construct cleanly. Rather than half-render JSON nobody can vouch for, the viewer now reports the problem in place, naming the schema and what it requires. WorkflowUnitsFlowchart is typed to Workflow/AnyWorkflowUnit, and the header chips come from the entity's own `usedApplications` roll-up, which dedupes and covers nested workflows. Verified in a browser on both entry points: the demo page renders 57 standata workflows (cards, chips, flowchart, switching), window.renderWorkflow() from the built bundle renders with no page errors, and invalid-ESSE JSON shows the error in place instead of a blank container. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H3qj13tVSuzTFc3qsFzBbE
WorkflowUnitsFlowchart enabled the card's Delete action off `editable` alone, while defaulting `onRemove` to a no-op. WorkflowViewer sets `editable` but wires no `onUnitRemove`, so `editable: true` rendered an enabled Delete button whose click did nothing. Gate the action on a handler actually being there; callers that pass one are unaffected. `editable` on the read-only viewer therefore means the Copy action, which works on its own — say so in the prop docs and the README instead of promising 'editing affordances'. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H3qj13tVSuzTFc3qsFzBbE
timurbazhirov
left a comment
There was a problem hiding this comment.
Review pass over the whole PR. The ESSE-schemas point is addressed in the thread above; the rest of what I found, with the fixes pushed:
1. Parallel type hierarchy (fixed, 7ed3cbb). Covered in the thread — the two *Like interfaces and the JSON stand-ins are gone, any no longer launders stand-ins past components that declare subworkflow: WodeSubworkflow, and the fallback they existed for was only reachable with configs that aren't valid ESSE.
2. editable enabled a Delete button that did nothing (fixed, a05d08f). WorkflowUnitsFlowchart gated the card's Delete on editable alone while defaulting onRemove to a no-op. WorkflowViewer sets editable but wires no onUnitRemove, so editable: true rendered an enabled Delete whose click went nowhere. It is now gated on a handler actually being present; callers that pass one are unaffected. On the read-only viewer editable means the Copy action, which does work on its own — the prop doc and the README say that now instead of promising "editing affordances".
3. Header chips were hand-rolled (folded into 7ed3cbb). The viewer walked subworkflowInstances and deduped labels itself. Workflow.usedApplications already does exactly this, dedupes, and covers nested workflows, which the hand-rolled version missed.
Verified rather than assumed: both entry points driven in a real browser (demo page and window.renderWorkflow() from the built bundle), tsc, both builds, and 43 tests — one of which constructs every one of the 57 standata workflows, so the entity path is exercised against real configs rather than a hand-written fixture.
Two things I left alone, flagging rather than changing:
assetFileNames: "main.[ext]"invite.config.tsis right for the stablemain.cssURL, but it is a collision risk the moment the bundle emits a second asset of any one type (a font, an image) — they would all want the same filename. Fine today: the build emits onlymain.css.npm run lintcannot run here —@exabyte-io/eslint-configis not indevDependencies, soeslintexits before doing anything. Pre-existing and CI skips eslint (skip-eslint: 'true'), so nothing in this PR is affected, but the lint script is currently decorative. Prettier I checked file by file: my changes add no formatting drift. (The repo-wideprettier --checkfailures come from a prettier 2 → 3 JSX>-placement change and hit untouched files too.)
Generated by Claude Code
`assetFileNames: "main.[ext]"` gave the stylesheet the stable URL a host page needs — the way wave.js' main.css has one — but it named every asset by its extension alone, so a second asset of any one type (a font, an image) would collide on the same filename. Raised in review; fine today only because the bundle emits nothing but main.css. Keep the stable name for the stylesheet, hash the rest. Verified against the built bundle: main.css and main.js still serve at their documented URLs, and a page loading the stylesheet by URL gets React Flow's styles. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014TXqtpnFoKB8rHjkqUpAmM
|
Picked up the first of the two flagged items — the asset-name collision risk was mine, and it's fixed in 3383e92.
Verified against a fresh build: The second item I'm leaving alone deliberately: Generated by Claude Code |
Add
WorkflowViewer: hand it workflow JSON and it shows the workflow's units as cards plus the selected subworkflow's units as a flowchart, with no store, router or dependency injection to wire up first.The types are the ecosystem's own, with nothing in between: JSON going in is
@mat3ra/esse'sWorkflowSchemaorJobSchema(as the object, or a JSON string of one), and what comes out ofcreateWorkflowFromConfig()is a@mat3ra/wodeWorkflow— the entity that gives the cards their subworkflows, model instances, properties and statuses. A host app that already holds aWorkflowcan pass it straight through. The constructor's required entity lists (units,subworkflows,workflows) are defaulted when a config omits the empty ones, asSubworkflow.toJSON()-derived payloads do.The entity validates against the ESSE schemas on the way in, so a config that is not valid ESSE is reported in place, naming the schema and what it requires, rather than half-rendering JSON nobody can vouch for:
The standalone bundle exposes
window.renderWorkflow(workflowConfig, container, options), the same contract as wave.js'window.renderThreeDEditor(materialConfig, container), so a notebook or any other page can embed a workflow view through the api-examples viewer helpers withrender_function="renderWorkflow". Stylesheets are emitted asmain.cssto keep that URL stable next tomain.js, and the demo page mounts into its own container so importing the bundle only registers schemas and defines the global rather than rendering anything.Checked in a browser on both entry points — the demo page and
window.renderWorkflow()from the built bundle — plustsc, both builds, and 43 tests, one of which constructs every one of the 57 standata workflows.