@tangle-network/sandbox-ui 0.91.5 → 0.93.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -119,14 +119,80 @@ interface WfNode {
119
119
  height: number;
120
120
  data: WfNodeData;
121
121
  }
122
+ /**
123
+ * Node ids are PUBLIC contract, not an internal detail. A host keys its live
124
+ * `nodeState` by them, points the graph's `selectedNodeId` at one, and — once it
125
+ * supplies a declared topology — names its own edge endpoints with them. They
126
+ * are bare strings, so a host that re-derives the format at the call site gets
127
+ * no type error when the format changes here; the graph simply renders without
128
+ * edges. The format is therefore written in exactly one place (these helpers)
129
+ * and read everywhere else through them.
130
+ */
131
+ /** The workflow's first (or only) trigger. */
132
+ declare const TRIGGER_NODE_ID = "trigger";
133
+ /** The node for the `index`th entry of a list-form `on:`. Entry 0 IS
134
+ * {@link TRIGGER_NODE_ID}, so a single-trigger graph — the overwhelmingly
135
+ * common one — keeps the plain id a host may already have persisted.
136
+ *
137
+ * `index` is a position in the definition's `on:` list: a non-negative
138
+ * integer. Anything else formats an id no node bears, which a declared
139
+ * topology then rejects by name ("…names "trigger:NaN", which this definition
140
+ * has no step for") — reported there rather than thrown from here, because
141
+ * {@link buildWorkflowGraph} calls this and must never throw. */
142
+ declare function triggerNodeId(index: number): string;
143
+ /** The `on:` entry a trigger node stands for, or null when the id names
144
+ * anything else — so a host can tell a trigger from an action without
145
+ * matching the id format itself. */
146
+ declare function triggerNodeIndex(nodeId: string): number | null;
147
+ /** The node for the `do` entry at `index`. */
148
+ declare function actionNodeId(index: number): string;
149
+ /** The node for the `branchIndex`th fan-out leaf of the `do` entry at
150
+ * `actionIndex` — a `parallel` branch or a `foreach` template. */
151
+ declare function branchNodeId(actionIndex: number, branchIndex: number): string;
122
152
  type WfEdgeKind = "spine" | "fork" | "join";
123
153
  interface WfEdge {
124
154
  id: string;
125
155
  source: string;
126
156
  target: string;
127
157
  /** Spine = trigger→action→action; fork = fan-out into a branch leaf; join =
128
- * a branch leaf reconverging onto the next spine node. Drives edge styling. */
158
+ * a branch leaf reconverging onto the next spine node. Drives edge styling.
159
+ *
160
+ * A DECLARED edge ({@link BuildWorkflowGraphOptions.edges}) is a `spine`
161
+ * edge: it is the flow. Fork edges survive a declared topology unchanged (a
162
+ * branch leaf is this module's own node, which no declared spec addresses);
163
+ * join edges do not exist under one, because what follows a fan-out is then
164
+ * declared rather than inferred from list position. */
129
165
  kind: WfEdgeKind;
166
+ /** Short, already-human summary of the edge's guard, when it carries one.
167
+ * Supplied by {@link WfEdgeSpec.whenLabel} — this module never interprets a
168
+ * condition, it only places the label its host wrote. */
169
+ whenLabel?: string;
170
+ /** True when this edge closes a cycle: it points back at a node that is
171
+ * already on the path reaching it. Rendered distinctly (dashed, with the
172
+ * visit budget) because such an edge is the one that can run a node twice. */
173
+ backEdge?: boolean;
174
+ }
175
+ /**
176
+ * One edge of a DECLARED topology — the caller's answer to "what actually
177
+ * connects to what", replacing the positional spine this module would otherwise
178
+ * infer from `do`-list order.
179
+ *
180
+ * Endpoints are node ids from THIS graph, so name them with {@link actionNodeId}
181
+ * / {@link branchNodeId} / {@link TRIGGER_NODE_ID} rather than by hand. Edges
182
+ * into a node that has none are how a root is identified: every trigger node
183
+ * gets an edge to every root, so the caller declares only the topology it owns
184
+ * and never has to restate what the trigger connects to.
185
+ *
186
+ * The guard arrives PRE-SUMMARIZED (`whenLabel`). A condition's schema belongs
187
+ * to the system that compiles and evaluates it — this library renders graphs and
188
+ * has no business owning a second, drifting interpretation of one. The same
189
+ * summary the host writes here is then the one it can show elsewhere (a skipped
190
+ * step's row), which is what keeps the two readings identical.
191
+ */
192
+ interface WfEdgeSpec {
193
+ from: string;
194
+ to: string;
195
+ whenLabel?: string;
130
196
  }
131
197
  interface WfGraph {
132
198
  nodes: WfNode[];
@@ -151,12 +217,29 @@ interface BuildWorkflowGraphOptions {
151
217
  /** Collapse every node to the fixed icon-tile size, and pitch the layers for
152
218
  * it. Defaults to `false` (the full, expanded card). */
153
219
  compact?: boolean;
220
+ /**
221
+ * The graph's DECLARED topology. Omit — the default — and edges are inferred
222
+ * from `do`-list order: a linear spine, which is exactly right for a workflow
223
+ * that runs as one, and wrong for any workflow whose definition declares its
224
+ * own edges (`needs`, guards, cycles). Supply it and the inferred spine is
225
+ * replaced wholesale by these edges, the layout is re-ranked to the shape they
226
+ * describe (so a diamond reads as a diamond rather than a chain drawn over
227
+ * one), and cycle-closing edges are marked {@link WfEdge.backEdge}.
228
+ *
229
+ * An edge naming a node this graph has no slot for is an ERROR
230
+ * ({@link WfGraph.error}), never a quiet fall back to the positional spine:
231
+ * the two disagreeing means the topology and the definition came from
232
+ * different places, and a graph that draws edges the run will not take is
233
+ * worse than one that says it cannot be drawn.
234
+ */
235
+ edges?: readonly WfEdgeSpec[];
154
236
  }
155
237
  /** Build a positioned graph from a workflow YAML string. Never throws —
156
238
  * malformed YAML or an empty definition returns an `error` the UI can fall
157
239
  * back on (e.g. show the raw YAML while authoring). `reserveRunState` leaves
158
240
  * room for the rows live run state adds (see {@link nodeHeight}); `direction`
159
- * picks the flow axis (default "LR"); `compact` collapses nodes to icon tiles. */
241
+ * picks the flow axis (default "LR"); `compact` collapses nodes to icon tiles;
242
+ * `edges` replaces the inferred positional spine with a declared topology. */
160
243
  declare function buildWorkflowGraph(yaml: string, options?: BuildWorkflowGraphOptions): WfGraph;
161
244
 
162
245
  interface WorkflowGraphProps {
@@ -187,11 +270,46 @@ interface WorkflowGraphProps {
187
270
  * fresh record each update — e.g. from a poll/SSE tick — satisfies this.
188
271
  */
189
272
  nodeState?: Record<string, WfNodeState>;
273
+ /**
274
+ * The graph's DECLARED topology, replacing the spine inferred from `do`-list
275
+ * order — see {@link WfEdgeSpec}. Name endpoints with the exported id helpers
276
+ * (`actionNodeId`, `branchNodeId`, `TRIGGER_NODE_ID`).
277
+ *
278
+ * Immutability contract, as for `nodeState`: the layout memo keys on this
279
+ * array's reference, so pass a stable one (a `useMemo`, or a value derived
280
+ * once per fetch) rather than a fresh literal each render.
281
+ */
282
+ edges?: readonly WfEdgeSpec[];
283
+ /** Per-node visit budget for a cyclic graph, shown on cycle-closing edges.
284
+ * Meaningless without `edges` (an inferred spine cannot loop). */
285
+ maxNodeVisits?: number;
286
+ /** The node to ring as selected — e.g. the one whose detail panel is open.
287
+ * Selection is the host's state; the graph only reflects it. */
288
+ selectedNodeId?: string;
190
289
  /** Click handler for a node (e.g. open a detail drawer). Absent ⇒ nodes are
191
290
  * non-interactive on click. */
192
291
  onNodeClick?: (nodeId: string, data: WfNodeData) => void;
292
+ /**
293
+ * Editing gestures. Supplying `onEdgeConnect` turns the canvas from a diagram
294
+ * into an EDITOR: node handles become visible and draggable, an edge can be
295
+ * selected and removed with Delete/Backspace, and clicking one asks to edit
296
+ * its guard. Omit all three — the default — and the graph stays the read-only
297
+ * visualisation it has always been.
298
+ *
299
+ * Every callback speaks node ids (`actionNodeId`, `branchNodeId`), never
300
+ * positions: this component reports the gesture, and turning it into a
301
+ * definition edit is the host's job — it owns the YAML, and only it knows
302
+ * what a `needs` row is. Fan-out and trigger edges never fire any of these
303
+ * ({@link isEditableEdge}), because neither is a row in anyone's topology.
304
+ *
305
+ * The canvas holds no pending state: an accepted edit comes back as new
306
+ * `yaml` + `edges`, and a rejected one simply never arrives.
307
+ */
308
+ onEdgeConnect?: (sourceId: string, targetId: string) => void;
309
+ onEdgeDelete?: (sourceId: string, targetId: string) => void;
310
+ onEdgeClick?: (sourceId: string, targetId: string) => void;
193
311
  }
194
312
 
195
313
  declare function WorkflowGraph(props: WorkflowGraphProps): React.JSX.Element;
196
314
 
197
- export { type BuildWorkflowGraphOptions, type WfEdge, type WfGraph, type WfNode, type WfNodeData, type WfNodeState, type WfNodeStatus, type WfNodeTone, WorkflowGraph as WorkflowGraphLazy, type WorkflowGraphProps, buildWorkflowGraph };
315
+ export { type BuildWorkflowGraphOptions, TRIGGER_NODE_ID, type WfEdge, type WfEdgeKind, type WfEdgeSpec, type WfGraph, type WfNode, type WfNodeData, type WfNodeState, type WfNodeStatus, type WfNodeTone, WorkflowGraph as WorkflowGraphLazy, type WorkflowGraphProps, actionNodeId, branchNodeId, buildWorkflowGraph, triggerNodeId, triggerNodeIndex };
package/dist/workflows.js CHANGED
@@ -1,6 +1,11 @@
1
1
  import {
2
- buildWorkflowGraph
3
- } from "./chunk-2NS724P4.js";
2
+ TRIGGER_NODE_ID,
3
+ actionNodeId,
4
+ branchNodeId,
5
+ buildWorkflowGraph,
6
+ triggerNodeId,
7
+ triggerNodeIndex
8
+ } from "./chunk-YOSNN3QU.js";
4
9
  import {
5
10
  ModelBrandStack,
6
11
  modelBrandFor
@@ -31,7 +36,7 @@ function retryImport(factory, retries = 2, delayMs = 350) {
31
36
  function lazyWorkflowGraph() {
32
37
  return lazy(
33
38
  () => retryImport(
34
- () => import("./WorkflowGraph-GUX7XN7Y.js").then((m) => ({ default: m.WorkflowGraph }))
39
+ () => import("./WorkflowGraph-TKDBFK3V.js").then((m) => ({ default: m.WorkflowGraph }))
35
40
  )
36
41
  );
37
42
  }
@@ -103,7 +108,12 @@ function WorkflowGraph(props) {
103
108
  }
104
109
  export {
105
110
  ModelBrandStack,
111
+ TRIGGER_NODE_ID,
106
112
  WorkflowGraph as WorkflowGraphLazy,
113
+ actionNodeId,
114
+ branchNodeId,
107
115
  buildWorkflowGraph,
108
- modelBrandFor
116
+ modelBrandFor,
117
+ triggerNodeId,
118
+ triggerNodeIndex
109
119
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/sandbox-ui",
3
- "version": "0.91.5",
3
+ "version": "0.93.0",
4
4
  "description": "Unified UI component library for Tangle Sandbox — primitives, chat, dashboard, terminal, editor, and workspace components",
5
5
  "repository": {
6
6
  "type": "git",