Flow

A node graph editor with typed ports, routed edges, schema-driven nodes, a node library, an inspector, automatic layout and optional run state.

<tp-flow>
Voice chain
Default density Compact density Restart from the initial state Show the copyable source

Usage

The value is a flow document, { version: 1, nodes, edges }, where each node names a type from the schema, a position and its data, and each edge connects a source node and port to a target node and port. schema describes the node types, their ports and the control each property shows; it is a property, so it is always set from script, along with icons when definitions name icons. load(document) replaces the document, and default-value (JSON) or defaultValue seeds an uncontrolled instance.

The widget fills its container’s inline size and needs a block size: give it one directly, as these examples do, or let it fill a sized parent. Its canvas never gets shorter than the size’s minimum (30rem by default, 22.5rem at size="sm"), so keep the block size at least that tall. Drag nodes to move them, drag from a port to another port to connect, scroll or pinch to pan and zoom, and press to remove the selection; undoes any of it.

size="sm" tightens the node width, ports, handles and the intrinsic height.

tp-flow is part of @tweakpad/ui/register/widgets. The separate @tweakpad/ui/flow entry is the DOM-free engine (parseFlowDocument, serializeFlowDocument, validateDocument, layoutFlow, FlowModel and the routing and run helpers). Apps do not need it to render a flow; import it when the same documents are validated, laid out or migrated in a worker or on a server.

Routing and layout can leave the main thread. In a Worker, build a FlowModel from the document, create a planner with createRoutingPlanner(model, options), run it in slices (run(budgetMs) returns { done, routed, remaining, elapsed } and resumes where it stopped; pending stays true until routing and nudging are both finished) and send the routes back as typed arrays: packRoutes(planner.routes.values()) packs ids, kinds, offsets, coordinates, flags, hashes, radii and stubs into transferable buffers, and unpackRoutes(packed) rebuilds the FlowRoute map on the other side. layoutFlow(model, direction, options) returns positions, sizes and the changes list; post the changes and apply them with flow.propose(changes, 'layout') followed by flow.commit('layout') so the lanes, the history and a controlled owner see one layout transaction.

Schema and ports

A node definition sets type, label, shape (card by default, compact, panel or group), a tone, its ports and its properties. Ports have an id, a direction (input or output) and a dataType; a connection is refused when the types do not match, so the schema’s dataTypes decide what may connect, and their tones paint the ports. show-port-labels shows each port’s label beside it, which suits panel nodes where every row has its own port.

A release then dispatches the cancelable tp-connect with { connection, edge, reason, sourceEvent } (a handler may change detail.edge, for instance its kind or label), proposes the edge-add with the reason connect and commits.

With connectOnClick (default) a click on a port starts a connection (aria-pressed) and a click on another port completes it. From the keyboard, Enter on a focused port starts a connection, arrows and Tab cycle the valid candidates (each announced), Enter confirms and Escape cancels. A selected edge has a reconnect anchor at each end reconnectable allows: an invisible grab area on the edge just beyond the socket (a move cursor), so the socket itself keeps starting new connections. Dragging an anchor re-targets the edge after the same validation (metadata.reconnect: { edgeId, end }); the old edge hides while dragging and comes back when the drop lands nowhere. A focused anchor shows its ring, and Enter or Space on it starts a keyboard reconnect: the arrows cycle the valid sockets (each announced), Enter or Space confirms and Escape cancels. connect(connection, edge?) and reconnect(edgeId, end, connection) do the same from code; getNodeConnections(id) lists a node’s edges.

Properties name a key of the node data and a control: slider, number, text, textarea, select, switch, checkbox, color or easing, each rendered by the library component of that name. Editing a control inside a node proposes a change to the document like any other edit, so it can be undone.

propertyControls replaces any of them (and must supply custom): a factory (context) => TemplateResult receives { node, definition, value, disabled, readOnly, propose, commit, port, id } and renders a control that proposes through propose(value, reason) and settles with commit(reason). A node whose type has no definition renders as a card with the shape’s default size and no ports.

In a node body, slider, number, text, select, switch and checkbox render their inline variant: the control is the whole row and shows the property’s label, and a slider’s value can be typed as well as dragged. The inspector shows every control in a labelled Field. A factory’s context has inline set in a node body, so a replacement control can do the same.

Edge kinds

An edge’s kind is smoothstep (the default), bezier, step, straight or orthogonal; rules.defaultEdgeKind sets it for new connections. animated marches a dashed stroke from source to target so the direction reads at a glance, and adds a closed arrow that keeps it readable when reduced motion pauses the dash. label renders as a Badge pill on the path.

Kinds and animation
Default density Compact density Restart from the initial state Show the copyable source

markerStart and markerEnd take arrow, arrowclosed, circle, diamond or a definition { type, width, height, color, strokeWidth, orient, units }; markers are defined once per flow and paint with the edge’s stroke. label is a shorthand for labels: [{ text }]; each label sits at position (0–1 along the path, 0.5 by default) with align and placement (on, above, below) and renders as an outline Badge pill. edgeTemplates, keyed by edge id or kind, replaces the label content with a template (context) => TemplateResult over { edge, kind, selected, label }.

Each edge has a 20 px invisible hit path: a click selects it, hovering marks it, a double press on a segment adds a waypoint there (track-press), and waypoints are must-pass points the path visits in order: a straight edge becomes a polyline through them, step and smoothstep an axis-aligned path with their corners that leaves and enters along the port sides, a bezier a smooth curve, and routed edges route through them (edgePathThrough in the engine). A selected edge shows its waypoint handles (focusable; arrows nudge, Delete removes) and, on a routed (orthogonal) path, a handle on each interior segment that moves the segment along its normal while the rest re-routes (the segments at the sockets stay put); both produce edge-waypoints changes. The handles sit above the paths, sockets and labels, and a press released without moving only selects a handle: the selected handle (the one a press or the keyboard last focused) wears the active ring and is what Delete and the arrows act on. Delete removes a selected waypoint and selects the next one (else the previous one); with none left, focus returns to the selected edge. Edges take focus through hidden proxies (E cycles the focused node’s edges, focusEdge(id) from code) and Delete removes the focused or selected edges.

Declarative nodes

Nodes and edges can be written as tp-flow-node and tp-flow-edge children. They are adopted into the document by id; x and y place a node, data is JSON, and an edge takes source, source-port, target, target-port, kind, label, animated and marker-end. Dragging writes back only the x, y, width and height attributes. Content inside a node renders in its body, and a library control with data-flow-property edits that key of the node data.

Authored nodes
Default density Compact density Restart from the initial state Show the copyable source

Authored markup suits a fixed or server-rendered graph. For graphs people build and save, keep the document as data and use load() or value.

Node library and inspector

palette adds the node library: palette alone (or palette="sidebar") floats a sidebar at the inline start, and palette="menu" opens a Dialog of columns. The schema’s categories group the definitions. Open it with the floating “+” Toggle, then drag a chip onto the canvas, or click it and then click where the node should go. default-palette-open starts it open.

  • sidebar: a bar floating over the canvas at the inline start, on the same surface and inset as the inspector, its width set by the size axis, with a “Node library” header and a Close Button (Escape closes it too). Under the search an Accordion section per group (several may be open; open of the category sets the start) lists the definitions; while searching, the matching groups replace the sections. It stays open after an insertion; the panels at the inline start move clear of it, and fitting the view centers the graph in the canvas it leaves uncovered.
  • menu: an “Add node” Dialog modal to the widget over a blurred backdrop (container modality: the canvas behind it is inert, Escape and its Close Button close it) whose groups lay out as columns under their headings, wrapping to further rows when they do not fit. It closes after an insertion and focus moves to the new node.
  • Library items: each definition is a compact outline Button chip with its icon and label (the description, or strings.dragToPlace, as its tooltip) under the toned heading of its group; the search filters by label, description, type and group. Chips are dragged onto the canvas and a drop inserts the node centered on the drop point; the menu turns transparent while a chip is dragged, so the whole canvas takes the drop, and comes back when the drag is cancelled. A click on a chip arms placement instead, the single-pointer alternative to dragging: the pane shows a copy cursor, the live region reads strings.announcePlacing, the next press on the pane places the node there and Escape cancels. Enter or Space on a focused chip inserts at the center of the uncovered canvas. With a pending anchor (the “+” of an edge label or the point of a released connection) any activation inserts there and connects. An insertion proposes with metadata.insert: { type, at } and selects the new node.

The inspector holds settings the schema places off the node with placement: "inspector"; compact nodes keep all their properties there. With inspector="auto" (the default) a double press on such a node opens it, as on “Paid orders” here, and it then follows the selection. inspector-commit="explicit" holds edits until Save.

Library and inspector
Default density Compact density Restart from the initial state Show the copyable source

It enters and leaves fading while it slides 2.5rem from the inline end (instant under reduced motion), keeps showing its node while it leaves, and reports the end of either motion through tp-open-change-complete. The canvas keeps its size: while it is open, panels at the inline end move clear of it and middle panels center in the space it leaves, and a node it covers when it opens on that node (or follows the selection to it) pans the least distance to sit clear of it. Only a node whose definition places at least one property in the inspector (placement, see Schema) has one; its header shows the node’s title, and Tabs switch Settings (one Field per visible inspector property, edited by the control its definition names; the node body shows the others), Wiring (the node’s incoming and outgoing connections as two Lists of List items; activating one focuses the edge) and Run (the node’s status, attempts, duration, message and error). A selection alone never opens it. Opened by a double press the inspector leaves focus on the canvas; opened from Settings or the menu it focuses its first field; closing returns focus to the node.

With a node library, every edge label carries a “+” Button that opens the library to insert a node on that edge, and releasing a new connection on the empty pane opens it to add one at the drop point.

palette-open and inspector-open (with their default- forms) make the panels controlled; setPaletteOpen() and setInspectorOpen() open them from your own controls, and both report through tp-open-change. inspector="manual" leaves opening entirely to that lane, and inspector="none" never shows it.

Controls, minimap and panels

controls lists the actions of the built-in toolbar in order: zoom-in zoom-out fit tool undo redo lock by default, with zoom (a zoom field), layout and run available on request and controls="" for no toolbar. controls-position and controls-orientation place it. The bar is one tab stop: its controls, the node library Toggle and the zoom field’s menu trigger included, are reached with the arrow keys. In a vertical bar, ArrowDown and ArrowUp on the zoom trigger open its menu, so list zoom last or reach the controls after it with Home and End. minimap adds an overview in minimap-position, and background draws dots, lines, cross or none.

The built-in bar takes controls, controls-position and controls-orientation; zoom is the zoom field: the current level (type a percentage and press Enter) and a menu of fit (Shift+1) and preset levels (100% is Shift+0).

Panels anchor to the corners and edge middles (top-start, top-center, top-end, center-start, center-end, bottom-start, bottom-center, bottom-end). A tp-flow-controls child slotted into one is another standard bar the widget keeps in sync; Buttons inside it join its keyboard navigation. Any other slotted content is a panel of your own.

Toolbars and panels
Default density Compact density Restart from the initial state Show the copyable source
Tidy up

layout(direction) runs the automatic layout as one undoable step and reports tp-layout-complete. auto-layout (horizontal, vertical or smart) keeps the graph laid out as nodes are added or dropped.

Automatic layout
Default density Compact density Restart from the initial state Show the copyable source
Horizontal Vertical Smart Drag a node, or switch the direction.

In an automatic mode a node you drag is pinned where you drop it and the rest of the graph flows around it in one undoable layout step; a node with pinned: true in the document starts that way.

The document’s layout settings (nodeSpacing 40, rankSpacing 80, componentSpacing 60, pack) feed the engine, layoutEngine injects another implementation of FlowLayoutEngine, and the root is aria-busy while the engine runs. The result is deterministic and translated so the top-left of the laid-out nodes stays where it was. smart picks the axis the explicit port sides imply, else the orientation whose aspect is closest to the viewport, and packs several components as a grid.

After a layout the focused node (else the single selected node) keeps its screen position: the viewport is compensated in the same frame. The transition plays the flow-layout state role (flowMotionRoles.layout): moved nodes glide from where they were while the edge and label layers fade in at their final routes, which the router computed on the committed positions. It takes --tp-duration-normal and --tp-easing-standard, is a single step under reduced motion, and a consumer can claim it through tp-motion-request.

alignSelection(mode) (left, center-x, right, top, center-y, bottom) and distributeSelection(axis, spacing?) (x or y, even by default or a gap) arrange the selected nodes as one transaction; the context menu of a multi-node selection offers align left, align top and distribute horizontally or vertically.

Listening to changes

Every edit is a cancelable tp-value-change whose detail.metadata.changes lists the fine-grained changes (node-add, node-position, edge-add, node-data and so on). One tp-value-commit follows each settled interaction, such as a drag release or a new connection; save there. toJSON() returns the canonical document without runtime state. tp-selection-change, tp-viewport-change and tp-history-change report the other lanes.

Commit and selection
Default density Compact density Restart from the initial state Show the copyable source
Press a node, drag it or connect two ports.

Lane events of the composed controls never leave the widget: a Slider inside a node stops at the node and becomes a node-data proposal with metadata.nodeId, property and surface; a vetoed widget proposal cancels the control’s own proposal so the control reverts; the inspector’s fields, the controls toolbar, the minimap, the palette and the context menu report through the widget’s lanes the same way. Events of consumer content inside an authored node pass through unless the element carries data-flow-property.

Cancel a tp-value-change to refuse an edit, or set isValidConnection to a function that returns false or a message for connections the schema cannot express. For a controlled widget, supply value before the element connects and write each accepted detail.value back to it.

Controlled owner
Default density Compact density Restart from the initial state Show the copyable source
Try connecting Classify topic straight to Send auto-reply.

A controlled owner writes the accepted document back to value synchronously and the widget adopts it by identity; an owner that publishes later keeps the proposed geometry on screen as a transient overlay until it does, and the interaction commits once it has; an owner that never writes the accepted proposals back sees them rolled back 120 ms after the interaction settles, without a commit or a history entry; an owner that cancels a proposal in the middle of a drag leaves the node at its last accepted position without ending the gesture, and a cancelled release emits no commit.

Middleware runs inside the engine for every change, including undo and redo, before the proposal reaches the owner: it can veto a deletion, rewrite positions, or log every transaction. The model getter exposes the live FlowModel for queries (incomers, outgoers, topologicalOrder, hasCycle, nodeRect, ports, validate); mutate it through the widget’s methods so the lanes, history and owners stay in step.

confirm-delete asks before a deletion removes nodes, from the keyboard, the node toolbar or the context menu: an Alert dialog names the node, or the count, and removes the nodes with their edges in one change.

Run state

The widget shows execution but never runs anything. Assign a run snapshot or a trace of run events to run, or stream events with pushRunEvent(), and each node shows its status, duration or error while the edges show which branches were taken. runnable adds Run actions that dispatch tp-run-request with the nodes to run in order, for your backend to answer.

A finished run in view mode
Default density Compact density Restart from the initial state Show the copyable source

pushRunEvent(event) feeds a live run one event at a time: the first event starts a live player on the current graph, which follows later edits (added nodes join idle, removed ones leave). clearRun(), or run = null, clears it.

  • node_progress reports a fraction from 0 to 1 and an optional message; node_waiting a reason (an approval, an external system); node_warning a message that does not fail the node. Warnings accumulate until the node starts again.
  • run_started with scope: string[] is a partial run: only those nodes reset, the others keep their earlier results, and the end of the run marks unreached and cancelled nodes inside the scope only. run_failed keeps its error on the run.

Node statuses (idle, pending, running, waiting, success, failed, skipped, cancelled) show in the status footer: a Spinner or a Marker, the detail and a status Badge. The detail is the error of a failed node, “Waiting for: reason” for a waiting one, a Progress bar while a running node reports progress, else its message or duration; a warning Badge counts its warnings. Edge statuses derive from their nodes: an explicit edge_taken marks an edge taken; a skipped or cancelled source makes it disabled; a source finished through this port with its target running or waiting makes it active (the dash flows), finished makes it taken, finished through another port makes it not-taken; otherwise inactive. No status is conveyed by color alone.

A trace gets a player (FlowRunPlayer: seek(index), step(), back(), toStart(), toEnd(), play(speed), pause(), push(event), setTrace(trace), setGraph(graph)), and listing run in controls shows a transport in the controls toolbar: step back, play or pause, step forward and a position Slider over the steps.

runnable adds the Run actions; execute and run-summary put Run and the status counts in your toolbars, here the opt-in header, whose header-start, header-center and header-end slots appear only when one has content. The widget asks through tp-run-request with the scope and the node ids in execution order, and your backend answers with run events through pushRunEvent(). This mock backend waits on an approval and fails the publish step. validate shows design problems before any run: the CDN purge has an unconnected required input.

Run requests
Default density Compact density Restart from the initial state Show the copyable source
Product photo import

The node toolbar gets Run node, the context menu Run node, Run from here and Run up to here (and Run graph on the pane), Mod+Enter runs the graph, and the execute control offers Run and its scopes. These work in read-only and view mode, for monitors, but not when the widget is disabled. Each asks through tp-run-request with { action, scope, nodeId, nodeIds, reason, sourceEvent }:

  • action is run, stop (Stop, while a run is going) or clear (Clear run).
  • scope is graph, node (the node alone), from (the node and everything downstream of it) or to (everything upstream of it, then the node); nodeIds lists the scope in execution order (flowRunScope(graph, scope, nodeId) computes the same from the engine).
  • requestRun(scope, nodeId?) and requestStop() ask the same from your own controls.

validate shows the document’s validation issues: the widget validates after loading and after every change that is not geometry (the schema’s rules, required ports left unconnected, missing endpoints, a definition’s own validate(node, context)). issues adds problems from elsewhere, such as a backend’s lint, as FlowIssue objects ({ code, severity, message, nodeId, edgeId, portId }). problems is the merged list, and tp-issues-change reports it whenever it changes.

Modes and forms

readonly keeps pan, zoom, selection and run state but refuses every edit. mode="view" also hides the ports, the node toolbar and the node library, for monitors. locked refuses edits while the toolbar stays usable, and the lock control toggles it. disabled makes the canvas inert.

The widget is a form control: the document submits as JSON under name, required fails on an empty document, and a form reset restores the default document. Inside tp-field the Field’s label names the graph (over label) and focuses the canvas when pressed, and the description and the error describe it.

Read-only dependency viewer
Default density Compact density Restart from the initial state Show the copyable source

router="orthogonal" routes every edge around the padded node boxes and spreads parallel segments apart. With mode="view" and readonly, pan, zoom, selection and the minimap still work; the minimap pans on press and drag.

router selects the routing policy: auto (default) routes the orthogonal kind through the obstacle-aware router and draws the other kinds directly, orthogonal routes every edge, none draws every kind directly, and a FlowRouter instance ({ route(request), nudge?(routes, context) }) replaces the built-in router. Routes therefore keep at least half the edge spacing between parallel runs wherever the gaps between nodes are at least two paddings plus half the edge spacing (30 px with the defaults); in a narrower gap, edges that meet at facing ports can share a short run, and an edge between facing ports closer than two paddings has no lane at all and takes the smoothstep fallback (which loops out past both ports). Routing and nudging run within a per-frame budget and resume on the next frame, so a drag re-routes only what moved.

In a form
Default density Compact density Restart from the initial state Show the copyable source
Save chain Reset
Move or connect a step, then save.

default-value or defaultValue is what Reset restores.

Nodes

Shapes come from the definition: card composes a Card with an icon tile, the title, the description and a status footer (the workflow monitor); compact is an icon tile with a caption (pipelines); panel is a Card with a toned header and List item rows of label and control, each row’s port aligned with it (material and shader graphs); group is a dashed, resizable container with a label whose children are positioned relative to it. Before measurement a node takes its definition’s size, else the shape default: card 240 × 96, compact 96 × 68, panel 220 × 160, group 320 × 240. Measurement follows the rendered box and the port positions; a persisted size comes from a resize or a declaration.

nodeTemplates replaces the body per type: a template (context) => TemplateResult receives { node, definition, selected, dragging, lod, status, ports(side), control(key) } and renders what it likes, calling ports('left') for the port elements of a side and control('retries') for the library control bound to a property. The host keeps the group role, the focus, the drag grip, the ports and the resizer.

Groups are nodes whose definition has shape: 'group'; membership is parentId, positions are parent-relative, extent: 'parent' keeps a child inside, a dragged child near the border expands the parent, deleting a group deletes its descendants and the dependent edges, and a child paints above its parent with the selection elevated.

A press on a node selects it (Shift adds, Command or Control toggles) and a drag moves every selected draggable node by the same delta, keeping the press offset, snapping to snapGrid when snap is on, clamped by the parent extent, auto-panning near the pane edge; the drag proposes once per frame and commits once on release. Escape restores the pre-drag positions (escape-key), a cancelled pointer restores them (pointer), and a lost capture settles at the last position. A selected resizable node shows the resizer: four focusable corner handles, the definition’s minSize and maxSize, Shift keeping the aspect ratio, arrows resizing by a grid step and Escape restoring; the result is a node-size change. pinned keeps a node in place during automatic layout (the node toolbar and the context menu pin and unpin).

Only the nodes and edges inside the viewport plus an overscan margin have DOM: virtualize (default) pools tp-flow-node hosts and rebinds them as the view moves, unmeasured nodes are materialized until measured, the focused node stays materialized, and visibleNodeIds and visibleEdgeIds list the current set; virtualize="false" renders everything.

Viewport

A left drag pans in the pan tool, while Space is held or with the middle button. wheel sets what a plain wheel does: pan (default), zoom toward the pointer, or none to leave the page’s scrolling alone; Control or Command with the wheel (a trackpad pinch) always zooms toward the pointer and Shift swaps the axes. The wheel works anywhere over the canvas, nodes and panels included: a wheel over a node or panel goes to its content only when something there can scroll in that direction (a long list, a textarea), and otherwise moves the canvas. A wheel stream keeps the owner its first wheel chose, the way the browser latches native scrolling, so nodes that pan under a stationary pointer never stop it; the stream ends after a 500 ms pause, when the pointer moves more than 10 px, or when Control or Command changes. On touch, one finger on the empty pane pans in either tool and a second finger pinches around the midpoint (a touch press never starts a marquee); a double press on the empty pane zooms in by 1.2, and cooperativeGestures hands plain wheel and one-finger pane gestures to the page. Node, connection, marquee and resize drags auto-pan near the pane edges.

Selection and editing

The selection lane is { nodes, edges } (tp-selection-change): a press selects, Shift adds, Command or Control toggles, and a press on a control inside a node selects that node while the control keeps the press; in the select tool a press on the empty pane starts a marquee that selects nodes it touches (selectionMode="partial") or encloses (full), Shift extends it, a press without movement clears, and Shift with a press in the pan tool also starts a marquee. Command or Control with A selects everything and Escape clears. select({ nodes, edges }), selectAll() and clearSelection() do the same from code; selection is never written into the document.

Copy, cut, paste and duplicate (Mod+C, Mod+X, Mod+V, Mod+D; copy(), cut(), paste(payload?, { at }), duplicate()) exchange application/x-tweakpad-flow+json with a JSON text fallback through the clipboard: a paste remaps ids, drops the nodes one grid step away or at the pointer, keeps the edges among them and selects what it pasted. Keys typed into an editable control inside a node stay with the control. nudgeSelection(delta) moves the selection by a delta; keyboard nudges coalesce into one history entry.

Undo and redo (Mod+Z, Mod+Shift+Z, the Undo and Redo controls; undo(), redo(), canUndo, canRedo) replay the engine’s history: one entry per gesture, nudges coalesced, measurements, viewport and selection never recorded. tp-history-change reports every history change with { canUndo, canRedo, undoLabel?, redoLabel?, depth } (the labels name the step, such as move or connect), so a consumer bar can enable its own buttons. Each step is proposed through the value lane with metadata.history set to undo or redo, so a controlled owner can veto it. Delete and Backspace, the node toolbar and the context menu delete the selection: the cancelable tp-delete carries the nodes (with their descendants) and the dependent edges, the deletion is one transaction, focus moves to the nearest remaining node, and deletable: false keeps an element out of it. Edge-only deletions, Cut and deleteElements({ nodes, edges }) never ask; the method runs the deletion at once, after tp-delete.

The context menu is a Menu with the context invocation (right click, Shift+F10, the ContextMenu key, a long press). Its subject comes from the press: a node (or the selection it belongs to), an edge or the pane. A press anywhere outside the open menu, the canvas included, closes it and keeps its meaning (a press on the pane also clears the selection); a right press elsewhere moves the menu to the new subject. Pointer presses focus nodes without the keyboard focus ring. The default items are duplicate, copy, cut, pin or unpin (in automatic layout) and delete for a node (preceded by align left, align top, distribute horizontally and distribute vertically when two or more nodes are selected); add waypoint and delete for an edge; paste, select all, fit view and lay out for the pane. With runnable, Run node, Run from here and Run up to here lead the node items and Run graph the pane items. Each item carries its icon and, where a key binding exists, its shortcut as a Kbd (Mod+D, Mod+C, Mod+X, Mod+V, Mod+A, Shift+1, Backspace); delete is the destructive variant. contextMenu(subject) renders the items instead: subject is { kind: 'node', nodeId, selection }, { kind: 'edge', edgeId, selection } or { kind: 'pane', point, selection }; items with the built-in values (duplicate, copy, cut, delete, pin, unpin, align-left, align-top, distribute-x, distribute-y, add-waypoint, paste, select-all, fit, layout, run-node, run-from, run-to, run-graph) keep their behavior, items with other values handle their own activation, and contextMenu = false removes the menu. A node with inspector settings gets a Settings item first (settings; it opens the inspector and focuses its first field). The node toolbar appears above a single selected node with Settings (only when the node has inspector settings), duplicate, pin (in automatic layout; pressed while pinned) and delete, led by Run node with runnable (which also shows the toolbar, with Run alone, when the widget is read-only or in view mode); nodeToolbar(context) renders its content from { node, definition } and nodeToolbar = false hides it.

Renderer

The widget resolves what to draw and a renderer draws it; the default SVG renderer keeps one <svg> per flow with a group per visible edge, and a WebGL renderer implements the same FlowRenderer interface:

interface FlowRenderer { readonly capabilities: { edgeFocus: boolean }; attach(host: FlowRenderHost): void; detach(): void; setViewport(viewport: FlowViewport): void; syncEdges(edges: readonly RenderedEdge[]): void; // the full visible set; diff by id setConnectionLine(line: ConnectionLineState | null): void; setSelectionBox(rect: FlowRect | null): void; // the marquee, in flow coordinates resize(size: FlowSize, devicePixelRatio: number): void; themeChanged(): void; hitTest?(point: FlowPoint, tolerance: number): FlowRendererHit | undefined; }

On attach the host supplies the layer element to append to, the owner window, a per-instance id for marker names, the size, the pixel ratio, the viewport, the reduced-motion and forced-colors flags, token(name) to read a design token (--tp-primary) through the host’s computed style and requestRender() to ask the widget for a pass; renderers never schedule frames or read globals themselves. Each RenderedEdge carries the edge, its kind, the resolved path (points, d, bbox, center, length), both endpoints, the stacking z, the selected, hovered, focused, animated, routed and fallback flags, the run status, the dash direction, the tone, the stroke width, the marker specs, the resolved labels and whether it is interactive.

Keyboard

The canvas is one tab stop that enters at the last focused node (the first node at first). Mod is Command on macOS and Control elsewhere.

KeysAction Move focus between nodes (the nearest node in that direction) Focus the first or the last nodeNudge the selection by one grid stepNudge the selection by four grid stepsEnter the focused node (Tab then cycles its ports and controls); on a port, start or confirm a connectionToggle the focused node in the selection; held, pan with the pointerCancel the gesture or an armed placement, leave the node, clear the selection, close the inspector or the node library Delete the selection (after tp-delete) Undo, redo Copy, cut, paste, duplicate the selectionSelect everythingAsk to run the graph (runnable)+ -Zoom in, zoom out Zoom to fit, zoom to 100% (the digit key, whatever character Shift types on the layout) Select tool, pan toolCycle the focused node’s edges Open the context menu

Inside a node, Tab moves through its ports and controls and Escape returns to the node; keys typed into an editable control stay with it. Waypoint handles and resize handles take arrows, Delete and Escape of their own. Focusing a node outside the viewport (focusNode(id), an arrow key) reveals it through setCenter. The hidden instructions paragraph (flow-instructions, described by the root) summarizes this table for assistive technology.

Constituents and accessibility

The root is an application region (role="application") named by label (“Flow” by default) with a role description and the hidden instructions; it is aria-busy while the engine lays out. Each node is a group with the role description “node”, named by getNodeAriaLabel or its label, definition label, run status and “, selected”; the nodes form one roving tab stop over the model order. Ports are native buttons named “Node: Port input” or “… output” with aria-keyshortcuts and aria-pressed while click-connecting. Edges are groups with the role description “edge” named “Edge from A to B, label”, focusable through overlay proxies so focus works with every renderer. A polite live region announces moves (“Moved Send auto-reply to 700, 380”), connections, the candidate during a keyboard connection, deletions, the selection count, layout completion, the zoom level after a keyboard zoom and run transitions. Under reduced motion the dash flow, the viewport and layout tweens and the selection transitions are off; under forced colors the strokes use CanvasText and Highlight and every status keeps its text and icon. strings overrides every visible and accessible string (FlowStrings), locale formats the fields.

Parts and customization

The composed Card of a node is registered as flow-node-card, its List item rows as flow-node-row, the toolbar Buttons and Toggles as flow-control and the node library’s chips as flow-palette-item; tp-flow-node exports its parts, so tp-flow::part(flow-node-title) reaches rendered and authored nodes alike and partPresentation hooks reach the widget parts and the registered nested parts.

Every color resolves through tokens: the frame and the background through --tp-background, --tp-foreground and --tp-border, cards through the Card recipe, rows through List item, pills through Badge and Marker, ports, waypoints and handles through the shared thumb recipe painted with --tp-primary, edges through a foreground mix that turns --tp-primary when selected or focused, the marquee through a translucent --tp-accent fill with a dotted --tp-primary outline, run statuses through --tp-warning (pending, waiting), --tp-primary (running, active), --tp-success (success, taken), --tp-destructive (failed) and --tp-muted-foreground (skipped, cancelled, not taken, disabled), typed tones through --tp-chart-1 to --tp-chart-5 and the status tokens.

API Reference

Every element also accepts motion-policy.

Attributes

AttributePropertyTypeDefaultDescriptiondefault-valuedefaultValueFlowDocument | undefined—Starting document of an uncontrolled flow, as JSON in the attribute; form reset returns to it.viewportviewportFlowViewport—The pan and zoom of the canvas, x y zoom as an attribute; setting it makes the viewport controlled.default-viewportdefaultViewportFlowViewport | undefined—Starting viewport of an uncontrolled viewport, as x y zoom in the attribute.selectionselectionFlowSelection—The selected nodes and edges, JSON as an attribute; setting it makes the selection controlled. It is never written into the document.default-selectiondefaultSelectionFlowSelection | undefined—Starting selection of an uncontrolled selection, as JSON in the attribute.tooltoolFlowTool—The pointer tool: select, where a drag on the pane draws a marquee, or pan; setting it makes the tool controlled.default-tooldefaultToolFlowTool | undefined—Starting pointer tool of an uncontrolled tool choice.modemodeFlowMode—Design for editing, or view for reading and monitoring with editing off; setting it makes the mode controlled.default-modedefaultModeFlowMode | undefined—Starting mode of an uncontrolled mode choice.inspector-openinspectorOpenboolean—Whether the inspector panel shows; setting it makes the open state controlled.default-inspector-opendefaultInspectorOpenboolean | undefined—Opens the inspector on first render, for an uncontrolled inspector.auto-layoutautoLayoutFlowAutoLayout'none'Lays the graph out automatically after every change, in a direction or the one that fits best; dragged nodes are pinned where they drop.snapsnapbooleanfalseSnaps dragged nodes to the grid.snap-gridsnapGridnumber16Grid spacing in flow units, for snapping, nudging and the background pattern.min-zoomminZoomnumber0.1Smallest zoom factor.max-zoommaxZoomnumber4Largest zoom factor.detail-zoomdetailZoomnumber0.3Zoom below which nodes render at low detail, keeping their size, so a zoomed-out graph stays fast.virtualizevirtualizebooleantrueRenders only the nodes and edges in view; turn it off to keep every element in the page.fit-viewfitViewbooleanfalseFits the graph into view on first render.fit-paddingfitPaddingnumber48Padding in pixels around the graph when fitting it into view.backgroundbackgroundFlowBackground'dots'The pattern behind the graph, drawn at the grid spacing.wheelwheelFlowWheelPolicy'pan'What a plain mouse wheel does over the canvas: pan, zoom, or nothing so the page scrolls; a pinch always zooms.cooperative-gesturescooperativeGesturesbooleanfalseLeaves plain wheel and one-finger gestures to the page, for a flow embedded in a scrolling document.connection-radiusconnectionRadiusnumber20Distance in pixels within which a dragged connection snaps to a port.connection-modeconnectionMode'strict' | 'loose''strict'Connects outputs to inputs only, or any two ports.connect-on-clickconnectOnClickbooleantrueLets a click on one port and then another connect them, as an alternative to dragging.selection-modeselectionModeFlowSelectionMode'partial'Whether the marquee selects the nodes it touches or only those it encloses.sizesize'sm' | 'default' | 'lg''default'Scales the nodes, ports, edges, grid, panels and the minimum height together.labellabelstring''Accessible name of the canvas; a surrounding Field supplies it otherwise.localelocalestring''Locale used to format the numbers in node and inspector fields.show-port-labelsshowPortLabelsbooleanfalseShows each port's label beside it.show-portsshowPortsbooleanfalseKeeps ports visible in mode="view" (they hide there by default).inspectorinspectorFlowInspectorPolicy'auto'When the inspector panel shows: opened from a node by a double press, its toolbar or its menu; only through the open state; or never.inspector-commitinspectorCommit'live' | 'explicit''live'Whether inspector edits apply as they happen, or wait for its Save button.controlscontrolsreadonly FlowControlAction[][ 'zoom-in', 'zoom-out', 'fit', 'tool', 'undo', 'redo', 'lock', ]Built-in controls; layout, undo, redo and run opt in.controls-positioncontrolsPositionFlowPanelPosition'bottom-start'Where the built-in controls bar sits over the canvas.controls-orientationcontrolsOrientation'vertical' | 'horizontal''vertical'Lays the built-in controls bar out in a column or a row.minimapminimapbooleanfalseShows an overview of the whole graph that pans and zooms the view.minimap-positionminimapPositionFlowPanelPosition'bottom-end'Where the minimap sits over the canvas.palettepaletteFlowPaletteLayout'none'The node library: a sidebar at the inline start, a menu over the widget, or none.palette-openpaletteOpenboolean—Whether the node library shows; setting it makes the open state controlled.default-palette-opendefaultPaletteOpenboolean | undefined—Opens the node library on first render, for an uncontrolled library.palette-triggerpaletteTriggerFlowPanelPosition | 'none''top-start'Where the floating "+" that opens the library sits; none leaves opening to the API.confirm-deleteconfirmDeletebooleanfalseUser deletions that remove nodes ask first, in a dialog over the blurred widget.runnablerunnablebooleanfalseRun requests (opt-in): the node toolbar's Run, the context menu's run items, Mod+Enter and the execute control dispatch tp-run-request. The widget never executes anything.validatevalidatebooleanfalseShows the document's validation issues (opt-in): it validates after load and each change.lockedlockedbooleanfalseThe lock control: editing refused while locked (pan, zoom and selection stay).

Properties

PropertyTypeDefaultDescriptionschemaFlowSchema | undefined—The node library: node types with their ports, properties and shapes, data types, categories and connection rules.nodeTemplatesReadonly<Record<string, FlowNodeTemplate>>{}Templates that replace the body of a node type, keyed by type.edgeTemplatesReadonly<Record<string, FlowEdgeTemplate>>{}Templates that replace an edge's label content, keyed by edge id or kind.propertyControlsFlowPropertyControlsdefaultPropertyControlsThe control factories that edit node properties, keyed by control name; replace any built-in one and supply custom.iconsReadonly<Record<string, IconDefinition>>{}Icon definitions, keyed by the names the schema's definitions use.renderer'svg' | FlowRenderer'svg'What paints edges, the connection line and the marquee: the built-in SVG renderer or another implementation of the renderer contract.router'auto' | 'orthogonal' | 'none' | FlowRouter'auto'How edges are routed: obstacle-aware routing for orthogonal edges only, for every edge, for none, or a router of your own.layoutEngineFlowLayoutEngine | undefined—Replaces the built-in layered layout used by layout() and automatic layout.isValidConnectionFlowConnectionPredicate | undefined—Final check of a connection after the schema rules; return false, or a message, to refuse it.middlewarereadonly FlowMiddleware[][]Hooks that run inside the engine for every change, undo and redo included: rewrite or veto changes before they apply, observe them after.nodeToolbarFlowNodeToolbarTemplate | false | undefined—Content of the toolbar over a selected node; false hides the toolbar.contextMenuFlowContextMenuTemplate | false | undefined—Items of the context menu for a node, an edge or the pane; false removes the menu.getNodeAriaLabel| (( node: FlowNode, definition: ReturnType<FlowModel['definitionOf']>, status: FlowNodeRunState | undefined, ) => string) | undefined—Builds a node's accessible name; by default its label, type, run status and selection.getEdgeAriaLabel((edge: FlowEdge) => string) | undefined—Builds an edge's accessible name; by default the nodes it connects and its label.runFlowRun | FlowTrace | nullnullA run snapshot, or a trace of run events played through the built-in stepper.stringsPartial<FlowStrings>{}Replaces built-in texts and accessible names for localization.zoomPresetsreadonly number[]DEFAULT_ZOOM_PRESETSZoom levels the zoom field's menu offers (1 is 100%).issuesreadonly FlowIssue[][]Further problems to show (a backend's lint), merged with the validation issues.onValueChange((event: TpValueChangeEvent<FlowDocument>) => void) | undefined—Called synchronously with each tp-value-change proposal, before listeners; a controlled owner writes value back here to accept it.onValueCommitted((value: FlowDocument, previous: FlowDocument, reason: ChangeReason) => void) | undefined—Called with each settled document, once per gesture, connection, key or field commit.flowIdread-only——A unique id of this flow, prefixed on the ids its rendering creates.modelread-onlyFlowModel—The live engine model, for queries such as incomers, outgoers or cycles; change the graph through the widget's methods.nodesread-onlyreadonly FlowNode[]—The nodes of the current document.edgesread-onlyreadonly FlowEdge[]—The edges of the current document.controlledread-onlyboolean—Whether the document is owned by the application through value.canUndoread-onlyboolean—Whether the history has a step to undo.canRedoread-onlyboolean—Whether the history has a step to redo.focusedNodeIdread-onlystring | undefined—The node that holds or last held keyboard focus.visibleNodeIdsread-onlyreadonly string[]—The nodes currently rendered: those in view plus a margin.visibleEdgeIdsread-onlyreadonly string[]—The edges currently rendered.rendererCapabilitiesread-only——What the active renderer supports, such as painting edge focus itself.draggingread-onlyboolean—Whether nodes are being dragged.connectingread-onlyboolean—Whether a connection is being drawn.selectingread-onlyboolean—Whether a marquee selection is being drawn.editableread-onlyboolean—Whether the graph accepts edits: design mode, not read-only, disabled or locked.lodread-onlyFlowLod—The level of detail nodes render at, low below detailZoom.canvasSizeread-onlyFlowSize—The canvas box in screen pixels.nodeLayerread-onlyNodeLayer—The layer that renders the node hosts in view; used by the widget's own parts.edgeLayerread-onlyEdgeLayer—The layer that resolves the edges in view and hands them to the renderer; used by the widget's own parts.announcerread-onlyLiveAnnouncer—The live region that announces moves, connections and run changes; used by the widget's own parts.resolvedStringsread-onlyFlowStrings—The built-in strings with strings applied.viewportOwnerread-onlyViewportOwner—The controller that pans, zooms and animates the canvas; used by the widget's own parts.laneread-onlyDocumentLane—The controller of the document's proposals, commits and rollbacks; used by the widget's own parts.routesread-onlyReadonlyMap<string, FlowEdgePath>—The routing planner's current routes by edge id (empty without a planner).playerread-onlyFlowRunPlayer | undefined—The stepper over a trace given to run; undefined for a snapshot or no run.runStateread-onlyFlowRun | undefined—The run currently visualized (the player's state for traces).runSummaryread-onlyFlowRunSummary—Node counts per status (and the nodes with warnings) of the visualized run.problemsread-onlyreadonly FlowIssue[]—Problems shown on nodes, ports and edges: validation issues (validate) and issues.

Events

EventTypeDescriptiontp-value-changeTpValueChangeEvent<FlowDocument<FlowNode<Record<string, unknown>>, FlowEdge<Record<string, unknown>>>>Cancelable document proposal; detail.metadata.changes lists the fine-grained changes and metadata.surface where they came from.tp-value-commitTpValueCommitEvent<FlowDocument<FlowNode<Record<string, unknown>>, FlowEdge<Record<string, unknown>>>>A settled document, once per gesture, connection, key or field commit.tp-viewport-changeTpValueChangeEvent<FlowViewport>Cancelable viewport proposal, at most once per frame.tp-selection-changeTpValueChangeEvent<FlowSelection>Cancelable selection proposal, { nodes, edges }.tp-tool-changeTpValueChangeEvent<"select" | "pan">Cancelable proposal of the select or pan tool.tp-mode-changeTpValueChangeEvent<"view" | "design">Cancelable proposal of design or view mode.tp-open-changeTpValueChangeEvent<boolean>Cancelable proposal to open or close the inspector, the node library or the context menu; detail.metadata.surface names which.tp-open-change-completeCustomEvent<{ open: boolean; surface: string; }>After the inspector finishes entering or leaving.tp-connectCustomEvent<{ connection: FlowConnection; edge: FlowEdge<Record<string, unknown>>; reason: ChangeReason; sourceEvent: Event | undefined; }>Cancelable, before a connection is added; detail.connection and the detail.edge to add, which a listener may change.tp-deleteCustomEvent<{ nodes: FlowNode<Record<string, unknown>>[]; edges: FlowEdge<Record<string, unknown>>[]; reason: ChangeReason; sourceEvent: Event | undefined; }>Cancelable, before a deletion; detail.nodes with their descendants and every dependent detail.edges.tp-history-changeCustomEvent<FlowHistoryState>The undo history changed; { canUndo, canRedo, undoLabel, redoLabel, depth }, for a bar of your own.tp-layout-completeCustomEvent<{ direction: FlowLayoutDirection; stats: FlowLayoutStats; bounds: FlowRect; }>A layout applied; { direction, stats, bounds }.tp-run-requestCustomEvent<FlowRunRequestDetail>With runnable, a request to run or stop the graph or part of it; the widget never runs anything itself.tp-run-changeCustomEvent<{ index?: number; length?: number; playing?: boolean; mode?: FlowRunPlayerMode; run: FlowRun | undefined; summary: FlowRunSummary; }>The run state changed; { run, summary } and the stepper's position.tp-issues-changeCustomEvent<FlowIssuesChangeDetail>The problems shown on nodes, ports and edges changed; { issues }.tp-shortcut-changeTpShortcutChangeEventThe canvas's keyboard shortcuts changed, for hints that show them.

Methods

MethodReturnsDescriptionsetValue(document, reason, sourceEvent?)booleanReplaces the document from an object or JSON text as one proposal; returns whether it was accepted.load(document)booleanReplaces the document programmatically; returns whether it was accepted.toJSON(options)FlowDocumentThe canonical document, without runtime state, with the current viewport unless viewport is false and the selection flags when selection is true.propose(changes, reason, metadata, sourceEvent?)—Proposes a list of fine-grained changes on the document lane; follow with commit() to settle them.commit(reason, sourceEvent?, metadata?)booleanSettles the proposed changes as one history entry and one tp-value-commit.addNodes(nodes, reason)string[]Adds nodes, creating ids where missing; returns the ids, or none when refused.addEdges(edges, reason)string[]Adds edges, creating ids where missing; returns the ids, or none when refused.updateNode(id, patch, reason)booleanChanges a node's position, data or other fields; returns whether it was accepted.updateNodeData(id, data, reason)booleanMerges values into a node's data; returns whether it was accepted.updateEdge(id, patch, reason)booleanChanges an edge's fields; returns whether it was accepted.deleteElements(ids, reason, sourceEvent?)booleanDeletes nodes (with descendants and incident edges) and edges after a cancelable tp-delete.connect(connection, edge, reason, sourceEvent?)string | undefinedConnects two ports after validation and a cancelable tp-connect.undo(reason, sourceEvent?)booleanUndoes the last history step through the value lane; returns whether it applied.redo(reason, sourceEvent?)booleanRedoes the next history step through the value lane; returns whether it applied.select(selection, reason, sourceEvent?)booleanProposes a selection of nodes and edges; returns whether it was accepted.selectAll(reason, sourceEvent?)booleanSelects every selectable node and edge.clearSelection(reason, sourceEvent?)booleanClears the selection.setTool(tool, reason, sourceEvent?)booleanProposes another pointer tool; returns whether it was accepted.setMode(mode, reason, sourceEvent?)booleanProposes another mode; returns whether it was accepted.setPaletteOpen(open, reason, sourceEvent?)booleanOpens or closes the node library (palette), proposing on its open lane.setInspectorOpen(open, reason, sourceEvent?)booleanProposes to open or close the inspector; returns whether it was accepted.setViewport(viewport, options)Promise<boolean>Moves the view to a viewport, animated over duration; resolves when it settles.fitViewTo(options)Promise<boolean>Fits the graph, or the given nodes, into view; resolves when the view settles. The fit never magnifies past 1 unless maxZoom allows it.zoomTo(zoom, options)Promise<boolean>Zooms to a factor around a point, the center by default; resolves when the view settles.zoomIn(options)Promise<boolean>Zooms in one step.zoomOut(options)Promise<boolean>Zooms out one step.setCenter(point, options)Promise<boolean>Centers the view on a point in flow coordinates, optionally at a zoom; resolves when the view settles.screenToFlow(point)FlowPointClient (viewport) coordinates → flow coordinates.flowToScreen(point)FlowPointFlow coordinates → client (viewport) coordinates.getNodesBounds(ids?)FlowRectThe box around the given nodes, or around every node, in flow coordinates.getIntersectingNodes(rect, partially)string[]The nodes inside a rectangle in flow coordinates, or touching it when partially is true.getNodeConnections(id)FlowEdge[]The edges connected to a node.focusNode(id, options)Promise<void>Focuses a node, first bringing it into view unless reveal is false.focusEdge(id, options?)voidFocuses an edge.focusPort(nodeId, portId)voidFocuses a port of a node.copy()Promise<boolean>Copies the selection to the clipboard (and an in-memory fallback).cut(reason, sourceEvent?)Promise<boolean>Copies the selection to the clipboard and deletes it; resolves with whether it did.paste(payload, options)string[]Pastes a payload (default: the last copied one) one grid step away or at at.duplicate(reason, sourceEvent?)string[]Duplicates the selection one grid step away.alignSelection(mode, reason, sourceEvent?)booleanAligns the selected nodes (two or more) to the selection's edge or centre; one transaction.distributeSelection(axis, spacing, reason, sourceEvent?)booleanDistributes the selected nodes (three or more) evenly, or by spacing, along an axis.nudgeSelection(delta, reason, sourceEvent?)booleanMoves the selected nodes, or the focused one, by a delta; successive nudges share one history step.reconnect(edgeId, end, connection, sourceEvent?)booleanRe-targets one end of an edge after validation (metadata.reconnect).layout(direction?, options)FlowLayoutResult | undefinedRuns the layout engine and applies its positions as one layout transaction, then dispatches tp-layout-complete.routeEdges()numberRoutes every routable edge now (synchronously) and returns the number of routes.setPortState(nodeId, portId, state)voidSets transient port states (candidates, validity) and re-binds the affected host.clearPortStates()voidClears every transient port state, such as connection candidates and their validity.runTransport(action, index?, speed?)booleanDrives the stepper: play/pause, step/back, seek, start/end.pushRunEvent(event)voidFeeds a live run, one backend event at a time: the first event starts a live player on the current graph, later ones append to it. clearRun() (or run = null) ends it.clearRun()voidClears the visualized run: a snapshot, a trace or a live run.requestRun(scope, nodeId?, sourceEvent?, reason)booleanAsks the backend to run (tp-run-request, runnable only): the whole graph, one node, a node and its downstream (from) or its upstream and it (to). Returns whether it asked.requestStop(sourceEvent?, reason)booleanAsks the backend to stop the current run (tp-run-request with stop).assignPanels()voidAssigns authored panel children to their slots (manual slot assignment).

Slots

SlotDescriptiondefaulttp-flow-node and tp-flow-edge children, adopted into the document by id.top-startContent over the canvas at the top start corner, such as a panel or a bar.top-centerContent over the canvas at the middle of the top edge.top-endContent over the canvas at the top end corner.center-startContent over the canvas at the middle of the start edge.center-endContent over the canvas at the middle of the end edge.bottom-startContent over the canvas at the bottom start corner.bottom-centerContent over the canvas at the middle of the bottom edge.bottom-endContent over the canvas at the bottom end corner.header-startThe start of a bar above the canvas, such as a breadcrumb; the bar shows while one of its slots has content.header-centerThe middle of the bar above the canvas, such as a run summary.header-endThe end of the bar above the canvas, such as actions.

CSS parts

PartDescription::part(flow)The root: the header, the node library, the canvas, the inspector and the panels.::part(flow-edge-focus)The focusable stand-in of an edge for the keyboard.::part(flow-edge-hit)The wider invisible path that takes presses on an edge.::part(flow-header)The bar above the canvas that holds the header slots; it shows while one has content.::part(flow-instructions)The hidden keyboard instructions that describe the canvas.::part(flow-pane)The surface under the graph that takes pans, marquees and placements.::part(flow-selection)The marquee drawn while selecting by area.::part(flow-viewport)The panned and zoomed layer that holds the edges, the nodes and their labels.

<tp-flow-node>

Attributes

AttributePropertyTypeDefaultDescriptionaccessible-nameaccessibleNamestring''The node's accessible name; its label when empty.selectedselectedbooleanfalseWhether the node is selected.focusedfocusedbooleanfalseWhether the node holds keyboard focus within the flow.draggingdraggingbooleanfalseWhether the node is being dragged.connectingconnectingbooleanfalseWhether a connection is being drawn in the flow, which shows the ports' candidate states.lodlodFlowLod'high'The level of detail the node renders at; low when the flow is zoomed far out.editableeditablebooleantrueWhether the node's controls and resizer accept edits.connectableconnectablebooleantrueWhether the node's ports start and accept connections.layout-directionlayoutDirectionFlowLayoutDirection'horizontal'The direction the graph is laid out in, which places the ports on its sides.show-port-labelsshowPortLabelsbooleanfalseShows each port's label beside it.

Properties

PropertyTypeDefaultDescriptionnodeFlowNode | undefined—The model record; undefined for an authored element not yet adopted.definitionFlowNodeDefinition | undefined—The schema definition of the node's type, which decides its shape, ports and properties; set by the flow.templateFlowNodeTemplate | undefined—The template that replaces the node's body, from the flow's nodeTemplates.propertyControlsFlowPropertyControlsdefaultPropertyControlsThe control factories for the node's properties, from the flow.iconsReadonly<Record<string, IconDefinition>>{}The icon definitions the node's definition names, from the flow.tonesReadonly<Record<string, string>>{}Tone keys per schema data type, for port paint.stringsFlowStringsDEFAULT_STRINGSThe flow's resolved strings, for the node's accessible names.portsreadonly FlowResolvedPort[][]The node's ports with their sides and positions, resolved by the flow.portStatesReadonly<Record<string, FlowPortState>>{}Transient states of the ports by id, such as connection candidates and their validity.statusFlowNodeRunState | undefined—The node's run state, shown as its status.issuesreadonly FlowIssue[][]Problems the widget shows on this node (validation issues and supplied ones).presentationOwnerread-onlyHTMLElement | null—The flow whose presentation styles the node.nodeIdread-onlystring—The id of the node in the document.shaperead-onlyFlowNodeShape—The shape the node renders as, from its definition: card, compact, panel or group.labelread-onlystring—The node's visible label: its own, else its definition's, else its type.resizableread-onlyboolean—Whether the node may be resized (node flag, then definition).

Events

EventTypeDescriptiontp-field-valueCustomEvent<{ value: number | null; previousValue: number | null; reason: ChangeReason; }>Reported by the node's numeric fields and stopped at the node, which turns their edits into proposals of its data; it never reaches the page.

Methods

MethodReturnsDescriptionfocus(options?)voidThe host element itself is the focusable group.measurePorts(zoom)FlowPortGeometry[]Ports' boxes relative to the host box in flow units (the layer supplies the zoom).portElement(portId)TpFlowPort | nullThe port element for a port id, or null.

Slots

SlotDescriptiondefaultAuthored content of the node body; a library control with data-flow-property="key" edits that key of the node's data.

CSS parts

PartDescription::part(flow-node)A node's box, in every shape.

<tp-flow-edge>

<tp-flow-controls>

Attributes

AttributePropertyTypeDefaultDescriptionactionsactionsreadonly FlowControlAction[]['zoom-in', 'zoom-out', 'fit', 'tool', 'lock']Actions in display order; tool renders the select/pan pair, run the transport.tooltoolFlowTool'select'The pointer tool the select and pan pair shows as pressed; the flow keeps it in sync.lockedlockedbooleanfalseWhether the lock control shows the graph as locked; the flow keeps it in sync.can-undocanUndobooleanfalseEnables the undo control; the flow keeps it in sync with its history.can-redocanRedobooleanfalseEnables the redo control; the flow keeps it in sync with its history.zoomzoomnumber1The current zoom factor shown by the zoom field (the root publishes it).

Properties

PropertyTypeDefaultDescriptionpaletteOpenbooleanfalsePressed state of the palette action: the node library is open.stringsPartial<FlowStrings> | undefined—Replaces the controls' labels and accessible names; the flow passes its own.runStateFlowRunTransportState | undefined—The run stepper's position and playback state, shown by the transport; the flow publishes it.runControlFlowRunControlState | undefined—What execute and run-summary show (the root publishes it).zoomPresetsreadonly number[][0.5, 1, 2]Zoom levels the zoom field's menu offers, where 1 is 100%.presentationOwnerread-onlyHTMLElement | null—The flow: the shadow host of the built-in bar, the ancestor of an authored one.

Methods

MethodReturnsDescriptionfocus(options?)voidFocuses the bar's first enabled control.

Slots

SlotDescriptiondefaultYour own Buttons, Toggles or Button groups, after the built-in controls and in the bar's arrow-key focus.

CSS parts

PartDescription::part(flow-controls)A controls bar.::part(flow-run-summary)The counts of a run's node statuses in a controls bar.