@tangle-network/sandbox-ui 0.51.0 → 0.52.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.
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "./chunk-GTJ5PSK5.js";
|
|
4
4
|
import {
|
|
5
5
|
buildWorkflowGraph
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-DEN4GUA7.js";
|
|
7
7
|
import {
|
|
8
8
|
providerLabel
|
|
9
9
|
} from "./chunk-7JLB7UZ3.js";
|
|
@@ -15,7 +15,9 @@ import {
|
|
|
15
15
|
Controls,
|
|
16
16
|
Handle,
|
|
17
17
|
Position,
|
|
18
|
-
ReactFlow
|
|
18
|
+
ReactFlow,
|
|
19
|
+
useEdgesState,
|
|
20
|
+
useNodesState
|
|
19
21
|
} from "@xyflow/react";
|
|
20
22
|
import "@xyflow/react/dist/style.css";
|
|
21
23
|
import {
|
|
@@ -27,7 +29,9 @@ import {
|
|
|
27
29
|
|
|
28
30
|
// src/workflows/flow-graph.ts
|
|
29
31
|
function buildFlowGraph(yaml, nodeState) {
|
|
30
|
-
const graph = buildWorkflowGraph(yaml
|
|
32
|
+
const graph = buildWorkflowGraph(yaml, {
|
|
33
|
+
reserveRunState: nodeState !== void 0
|
|
34
|
+
});
|
|
31
35
|
return {
|
|
32
36
|
error: graph.error,
|
|
33
37
|
nodes: graph.nodes.map(
|
|
@@ -47,6 +51,22 @@ function buildFlowGraph(yaml, nodeState) {
|
|
|
47
51
|
}))
|
|
48
52
|
};
|
|
49
53
|
}
|
|
54
|
+
function sameRunState(a, b) {
|
|
55
|
+
if (a === b) return true;
|
|
56
|
+
if (!a || !b) return false;
|
|
57
|
+
const aKeys = Object.keys(a);
|
|
58
|
+
const bKeys = Object.keys(b);
|
|
59
|
+
if (aKeys.length !== bKeys.length) return false;
|
|
60
|
+
return aKeys.every((k) => a[k] === b[k]);
|
|
61
|
+
}
|
|
62
|
+
function mergeRunState(prev, baseDataById, nodeState) {
|
|
63
|
+
return prev.map((n) => {
|
|
64
|
+
const base = baseDataById.get(n.id) ?? n.data;
|
|
65
|
+
const s = nodeState?.[n.id];
|
|
66
|
+
const nextData = s ? { ...base, state: s } : base;
|
|
67
|
+
return sameRunState(n.data.state, nextData.state) ? n : { ...n, data: nextData };
|
|
68
|
+
});
|
|
69
|
+
}
|
|
50
70
|
|
|
51
71
|
// src/workflows/format.ts
|
|
52
72
|
function fmtDuration(ms) {
|
|
@@ -240,22 +260,33 @@ function WorkflowGraph({
|
|
|
240
260
|
onNodeClick
|
|
241
261
|
}) {
|
|
242
262
|
const colorMode = useColorMode();
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
|
|
263
|
+
const hasRunOverlay = nodeState !== void 0;
|
|
264
|
+
const structural = useMemo(
|
|
265
|
+
() => buildFlowGraph(yaml, hasRunOverlay ? {} : void 0),
|
|
266
|
+
[yaml, hasRunOverlay]
|
|
246
267
|
);
|
|
268
|
+
const [nodes, setNodes, onNodesChange] = useNodesState(structural.nodes);
|
|
269
|
+
const [edges, setEdges, onEdgesChange] = useEdgesState(structural.edges);
|
|
270
|
+
useEffect(() => {
|
|
271
|
+
setNodes(structural.nodes);
|
|
272
|
+
setEdges(structural.edges);
|
|
273
|
+
}, [structural, setNodes, setEdges]);
|
|
274
|
+
useEffect(() => {
|
|
275
|
+
const baseById = new Map(structural.nodes.map((n) => [n.id, n.data]));
|
|
276
|
+
setNodes((prev) => mergeRunState(prev, baseById, nodeState));
|
|
277
|
+
}, [nodeState, structural, setNodes]);
|
|
247
278
|
const handleNodeClick = useCallback(
|
|
248
279
|
(_event, node) => {
|
|
249
280
|
onNodeClick?.(node.id, node.data);
|
|
250
281
|
},
|
|
251
282
|
[onNodeClick]
|
|
252
283
|
);
|
|
253
|
-
if (error || nodes.length === 0) {
|
|
284
|
+
if (structural.error || structural.nodes.length === 0) {
|
|
254
285
|
return /* @__PURE__ */ jsx(
|
|
255
286
|
"div",
|
|
256
287
|
{
|
|
257
288
|
className: `flex items-center justify-center rounded-lg border border-border border-dashed p-4 text-center text-text-muted text-xs ${className ?? ""}`,
|
|
258
|
-
children: error ?? "Nothing to show"
|
|
289
|
+
children: structural.error ?? "Nothing to show"
|
|
259
290
|
}
|
|
260
291
|
);
|
|
261
292
|
}
|
|
@@ -265,6 +296,8 @@ function WorkflowGraph({
|
|
|
265
296
|
{
|
|
266
297
|
nodes,
|
|
267
298
|
edges,
|
|
299
|
+
onNodesChange,
|
|
300
|
+
onEdgesChange,
|
|
268
301
|
nodeTypes: NODE_TYPES,
|
|
269
302
|
colorMode,
|
|
270
303
|
onNodeClick: onNodeClick ? handleNodeClick : void 0,
|
|
@@ -273,6 +306,8 @@ function WorkflowGraph({
|
|
|
273
306
|
proOptions: { hideAttribution: true },
|
|
274
307
|
nodesDraggable: !isPreview,
|
|
275
308
|
nodesConnectable: false,
|
|
309
|
+
deleteKeyCode: null,
|
|
310
|
+
edgesReconnectable: false,
|
|
276
311
|
elementsSelectable: !isPreview,
|
|
277
312
|
edgesFocusable: false,
|
|
278
313
|
zoomOnScroll: false,
|
|
@@ -4,10 +4,22 @@ import {
|
|
|
4
4
|
|
|
5
5
|
// src/workflows/model.ts
|
|
6
6
|
import { parse as parseYaml } from "yaml";
|
|
7
|
-
var ROW_GAP = 112;
|
|
8
7
|
var BRANCH_X = 300;
|
|
9
|
-
var
|
|
10
|
-
var
|
|
8
|
+
var NODE_GAP = 44;
|
|
9
|
+
var BRANCH_GAP = 24;
|
|
10
|
+
var CARD_CHROME = 20;
|
|
11
|
+
var TITLE_ROW = 22;
|
|
12
|
+
var TEXT_ROW = 20;
|
|
13
|
+
var META_ROW = 44;
|
|
14
|
+
var PREVIEW_ROW = 34;
|
|
15
|
+
function estimateNodeHeight(data, withRunState) {
|
|
16
|
+
let h = CARD_CHROME + TITLE_ROW;
|
|
17
|
+
if (data.subtitle) h += TEXT_ROW;
|
|
18
|
+
if (withRunState || data.model) h += META_ROW;
|
|
19
|
+
if (withRunState) h += PREVIEW_ROW;
|
|
20
|
+
if (data.provider) h += TEXT_ROW;
|
|
21
|
+
return h;
|
|
22
|
+
}
|
|
11
23
|
function asRecord(v) {
|
|
12
24
|
return v && typeof v === "object" && !Array.isArray(v) ? v : {};
|
|
13
25
|
}
|
|
@@ -165,7 +177,8 @@ function describeTrigger(on) {
|
|
|
165
177
|
tone: "trigger"
|
|
166
178
|
};
|
|
167
179
|
}
|
|
168
|
-
function buildWorkflowGraph(yaml) {
|
|
180
|
+
function buildWorkflowGraph(yaml, options) {
|
|
181
|
+
const reserveRunState = options?.reserveRunState ?? false;
|
|
169
182
|
if (!yaml || yaml.trim() === "") {
|
|
170
183
|
return { nodes: [], edges: [], error: "No definition" };
|
|
171
184
|
}
|
|
@@ -193,13 +206,10 @@ function buildWorkflowGraph(yaml) {
|
|
|
193
206
|
});
|
|
194
207
|
};
|
|
195
208
|
if (def.on) {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
position: { x: 0, y },
|
|
199
|
-
data: describeTrigger(def.on)
|
|
200
|
-
});
|
|
209
|
+
const data = describeTrigger(def.on);
|
|
210
|
+
nodes.push({ id: "trigger", position: { x: 0, y }, data });
|
|
201
211
|
prevId = "trigger";
|
|
202
|
-
y +=
|
|
212
|
+
y += estimateNodeHeight(data, false) + NODE_GAP;
|
|
203
213
|
}
|
|
204
214
|
actions.forEach((action, i) => {
|
|
205
215
|
const id = `a${i}`;
|
|
@@ -207,6 +217,7 @@ function buildWorkflowGraph(yaml) {
|
|
|
207
217
|
nodes.push({ id, position: { x: 0, y }, data });
|
|
208
218
|
if (prevId) addEdge(prevId, id, false);
|
|
209
219
|
prevId = id;
|
|
220
|
+
const spineHeight = estimateNodeHeight(data, reserveRunState);
|
|
210
221
|
const rec = asRecord(action);
|
|
211
222
|
const children = "parallel" in rec ? Array.isArray(asRecord(rec.parallel).branches) ? asRecord(rec.parallel).branches : [] : "foreach" in rec ? (
|
|
212
223
|
// A foreach with no `do` template has no child to render.
|
|
@@ -214,22 +225,23 @@ function buildWorkflowGraph(yaml) {
|
|
|
214
225
|
) : [];
|
|
215
226
|
if (children.length > 0) {
|
|
216
227
|
let by = y;
|
|
228
|
+
let branchStackHeight = 0;
|
|
217
229
|
children.forEach((child, j) => {
|
|
218
230
|
const cid = `${id}-b${j}`;
|
|
231
|
+
const childData = describeAction(child);
|
|
219
232
|
nodes.push({
|
|
220
233
|
id: cid,
|
|
221
234
|
position: { x: BRANCH_X, y: by },
|
|
222
|
-
data:
|
|
235
|
+
data: childData
|
|
223
236
|
});
|
|
224
237
|
addEdge(id, cid, true);
|
|
225
|
-
|
|
238
|
+
const childHeight = estimateNodeHeight(childData, reserveRunState);
|
|
239
|
+
by += childHeight + BRANCH_GAP;
|
|
240
|
+
branchStackHeight += childHeight + BRANCH_GAP;
|
|
226
241
|
});
|
|
227
|
-
y += Math.max(
|
|
228
|
-
ROW_GAP,
|
|
229
|
-
children.length * BRANCH_ROW_GAP + BRANCH_STACK_PADDING
|
|
230
|
-
);
|
|
242
|
+
y += Math.max(spineHeight, branchStackHeight) + NODE_GAP;
|
|
231
243
|
} else {
|
|
232
|
-
y +=
|
|
244
|
+
y += spineHeight + NODE_GAP;
|
|
233
245
|
}
|
|
234
246
|
});
|
|
235
247
|
return { nodes, edges, error: null };
|
package/dist/workflows.d.ts
CHANGED
|
@@ -18,6 +18,12 @@ type WfNodeStatus = "queued" | "running" | "succeeded" | "failed";
|
|
|
18
18
|
* merged onto {@link WfNodeData.state} at render time. Absent ⇒ the graph shows
|
|
19
19
|
* the static definition only (the assistant-proposal preview path). Driven from
|
|
20
20
|
* a workflow run's per-action results so the graph animates as the run executes.
|
|
21
|
+
*
|
|
22
|
+
* Keep every field PRIMITIVE: `sameRunState` (flow-graph.ts) shallow-compares
|
|
23
|
+
* with `===` to decide whether a render tick changed a node. A nested object/
|
|
24
|
+
* array field would compare by reference (a structurally-equal value reads as
|
|
25
|
+
* changed → a harmless extra render, never a stale node) — if one is ever needed,
|
|
26
|
+
* teach `sameRunState` to compare it.
|
|
21
27
|
*/
|
|
22
28
|
interface WfNodeState {
|
|
23
29
|
status: WfNodeStatus;
|
|
@@ -85,10 +91,22 @@ interface WfGraph {
|
|
|
85
91
|
/** Set when the YAML couldn't be parsed into a renderable graph. */
|
|
86
92
|
error: string | null;
|
|
87
93
|
}
|
|
94
|
+
/** Options for {@link buildWorkflowGraph}. */
|
|
95
|
+
interface BuildWorkflowGraphOptions {
|
|
96
|
+
/**
|
|
97
|
+
* Reserve vertical space for the rows live run state adds to a spine node (the
|
|
98
|
+
* meta-chip row + the output/error preview), so the RUN view's spine never
|
|
99
|
+
* overlaps once a node starts or terminates. The static/preview layout (no run
|
|
100
|
+
* overlay) leaves this off to stay compact. Defaults to `false`.
|
|
101
|
+
*/
|
|
102
|
+
reserveRunState?: boolean;
|
|
103
|
+
}
|
|
88
104
|
/** Build a positioned graph from a workflow YAML string. Never throws —
|
|
89
105
|
* malformed YAML or an empty definition returns an `error` the UI can fall
|
|
90
|
-
* back on (e.g. show the raw YAML while authoring).
|
|
91
|
-
|
|
106
|
+
* back on (e.g. show the raw YAML while authoring). Pass
|
|
107
|
+
* `{ reserveRunState: true }` for the run view so the spine leaves room for the
|
|
108
|
+
* rows live run state adds (see {@link estimateNodeHeight}). */
|
|
109
|
+
declare function buildWorkflowGraph(yaml: string, options?: BuildWorkflowGraphOptions): WfGraph;
|
|
92
110
|
|
|
93
111
|
interface WorkflowGraphProps {
|
|
94
112
|
/** Workflow YAML to render. */
|
|
@@ -116,4 +134,4 @@ interface WorkflowGraphProps {
|
|
|
116
134
|
|
|
117
135
|
declare function WorkflowGraph(props: WorkflowGraphProps): react_jsx_runtime.JSX.Element;
|
|
118
136
|
|
|
119
|
-
export { type WfEdge, type WfGraph, type WfNode, type WfNodeData, type WfNodeState, type WfNodeStatus, type WfNodeTone, WorkflowGraph as WorkflowGraphLazy, type WorkflowGraphProps, buildWorkflowGraph };
|
|
137
|
+
export { type BuildWorkflowGraphOptions, type WfEdge, type WfGraph, type WfNode, type WfNodeData, type WfNodeState, type WfNodeStatus, type WfNodeTone, WorkflowGraph as WorkflowGraphLazy, type WorkflowGraphProps, buildWorkflowGraph };
|
package/dist/workflows.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
buildWorkflowGraph
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-DEN4GUA7.js";
|
|
4
4
|
import "./chunk-7JLB7UZ3.js";
|
|
5
5
|
import "./chunk-U67V476Y.js";
|
|
6
6
|
|
|
@@ -27,7 +27,7 @@ function retryImport(factory, retries = 2, delayMs = 350) {
|
|
|
27
27
|
function lazyWorkflowGraph() {
|
|
28
28
|
return lazy(
|
|
29
29
|
() => retryImport(
|
|
30
|
-
() => import("./WorkflowGraph-
|
|
30
|
+
() => import("./WorkflowGraph-FSS7GW5O.js").then((m) => ({ default: m.WorkflowGraph }))
|
|
31
31
|
)
|
|
32
32
|
);
|
|
33
33
|
}
|
package/package.json
CHANGED