Aller au contenu

Visual editor

The editor lives at /workflows/{slug}/edit and is reached from:

  • The “Edit” click on a card in /workflows.
  • The “Create workflow” form, which redirects to the editor of the freshly-created workflow.

Direct URL works for bookmarks. /workflows/{slug} redirects to /workflows/{slug}/edit — there is no separate read-only detail page in v1.

┌──────────┬─────────────────────────────────┬────────────────┐
│ PALETTE │ [Auto inputs] Validate|Save|Run │ PROPERTIES │
│ ├─────────────────────────────────┤ │
│ INPUTS │ │ (visible when │
│ ├ Force │ │ a node is │
│ ├ Length│ REACT FLOW CANVAS │ selected — │
│ ├ … │ │ branched by │
│ └Custom…│ │ node kind) │
│ │ │ │
│ Packages │ │ │
│ ├ func │ │ │
│ ├ func │ │ │
└──────────┴─────────────────────────────────┴────────────────┘

The top of the palette is dedicated to input nodes. Two paths:

  1. Type cards — one draggable card per primitive and physical canonical type (Force, Length, Moment, …). Drop on the canvas ⇒ a typed input node appears with its label pre-filled (and editable later).
  2. Custom… button — opens a modal to declare any other shape:
    • Label — displayed on the card and in the run form.
    • Type expression — canonical name (Force), parameterised (list[Force], dict[str, Length]) or inline struct as JSON: {"d": "Length", "p": "Length", "As": "Area"}.
    • Description (optional) — renders under the field at run time.
    • Required at run submission (toggle).

Every input node has a single output port named value; edges from the node use that port.

  1. In the palette’s package sections, locate the function — functions are grouped by owning package. Only ready versions are displayed.
  2. Drag the function card onto the canvas. The drop position becomes the node position.
  3. The new node is auto-named — function_name by default, with a numeric suffix (force-calc-2) on collisions inside the same graph.

The toolbar hosts an Auto inputs switch. When on, dropping a function node also creates:

  • One input node per input port declared on the function’s io_spec.
  • Each input node is pre-typed from the port type (Force, struct, list…).
  • Edges are drawn automatically from each input node’s value to the matching function port.

The auto-created input nodes are placed on the left of the function node, stacked vertically around the function’s Y position. Left off, the user manually drags types from the palette — useful when you want to share an existing input node across two functions.

  • Click a source handle (right of a node), drag to a target handle (left of another node), release.
  • The editor calls POST /types/check-connection asynchronously before materialising the edge.

ALLOWED

Edge committed. Tooltip on hover shows the sous-typage reason — Force → Numeric explains “Numeric is a descendant of Float”.

REFUSED

Destructive toast surfaces "Force → Length · …". The edge is not added to the canvas; no DB state, no validation noise.

The type system reference covers the exact rules.

Click a node. A right-hand panel opens. The panel branches on the node’s kind.

FieldMeaning
Node key (read-only)The stable identifier used in edges and run outputs.
Display labelFree-form override of the function name. Saved as custom_label.
Function versionDropdown listing every ready version of the same function. Switching drops edges whose ports no longer exist in the new signature (FRO-wkf-05).
Source packageLink to /packages/{id} for the owning package.
FieldMeaning
Node key (read-only)Becomes the key of the run submission payload (inputs[node_key]).
LabelShown on the node card and as the title of the run-form card.
Type expressionTextarea accepting a canonical name, a parameterised form, or struct JSON. Validated on blur — malformed JSON surfaces an inline error.
DescriptionOptional free text rendered under the field at run time.
RequiredWhen off, the run form allows leaving the field blank and the function receives its default.

Close the panel with the × button or click the canvas background.

Validate runs a dry-run of the full graph validation against POST /workflows/{slug}/validate. Unlike the save path, this endpoint:

  • Never persists anything, never creates a version.
  • Always returns 200 OK with a structured report — even on errors.
  • Is owner-only (same rule as save).

The dialog categorises issues:

  • Cycles — a → b → a paths.
  • Unconnected function inputs — strict-mode violations: a function port has no incoming edge. Wire an input node (or an upstream output) to fix.
  • Invalid input-node types — a type_expr the catalog cannot parse.
  • Subtyping violations — per-edge reason.
  • Duplicate target ports — two edges pointing at the same input.
  • Unknown source/target ports — edge references a port that does not exist in the current io_spec.
  • Missing typed I/O — function nodes whose pinned version has no io_spec.
  • Unknown function versions / nodes — dangling references.

Save version POSTs to /workflows/{slug}/versions. The backend runs the full validation; on success a new version (monotonic integer) is created and becomes is_latest=true. On validation failure the call returns 409 with the same report shape as Validate.

Old versions are immutable and remain queryable — runs reference the version they were started against, not the current head.

The Run button is disabled while the canvas has no node or no version has ever been saved. When enabled, it navigates to /workflows/{slug}/runs/new — see execution for the full flow.

Out of scope for the v1 editor — planned for v1.5 / v2:

  • Visual struct builder — structs are declared as JSON in the Custom Input dialog; a field-by-field builder would be nicer.
  • Auto-layout — arrange nodes automatically (dagre / ELK).
  • Undo / redo — the editor is stateless between reloads for v1.
  • Multi-selection & bulk operations.
  • Copy / paste of sub-graphs.
  • Logic nodesif, for (FRO-wkf-15).
  • Import / export YAML (FRO-wkf-13).