@sanity/workflow-diagram 0.1.0 → 0.3.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.
- package/CHANGELOG.md +43 -0
- package/dist/index.cjs +86 -39
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +86 -33
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,48 @@
|
|
|
1
1
|
# @sanity/workflow-diagram
|
|
2
2
|
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- f9389e5: The explain tooltips' activity roster narrates causal waits: an activity that does not complete without a caller shows what it waits on ("Waits on caller action …") under its executor-classification line, on both the static and live tiers.
|
|
8
|
+
- b43de49: elkjs now loads lazily on the first layout instead of riding the package's static import graph. Bundlers split the ~1.4 MB (minified) ELK engine into an async chunk fetched only when a diagram actually renders, cutting the package's synchronous bundle cost by roughly 72% (about 600 KB → 168 KB gzipped on the critical path).
|
|
9
|
+
|
|
10
|
+
### Patch Changes
|
|
11
|
+
|
|
12
|
+
- Updated dependencies [f9389e5]
|
|
13
|
+
- Updated dependencies [d0c62ea]
|
|
14
|
+
- Updated dependencies [c3eed2e]
|
|
15
|
+
- Updated dependencies [df4bd80]
|
|
16
|
+
- Updated dependencies [092a0d4]
|
|
17
|
+
- Updated dependencies [5a1a9fe]
|
|
18
|
+
- Updated dependencies [e3a7ba2]
|
|
19
|
+
- Updated dependencies [30fed9e]
|
|
20
|
+
- Updated dependencies [1321ba5]
|
|
21
|
+
- Updated dependencies [e683875]
|
|
22
|
+
- Updated dependencies [a8ace4d]
|
|
23
|
+
- @sanity/workflow-engine@0.17.0
|
|
24
|
+
|
|
25
|
+
## 0.2.0
|
|
26
|
+
|
|
27
|
+
### Minor Changes
|
|
28
|
+
|
|
29
|
+
- e5f5b77: **BREAKING:** follow the engine's actions-only payload model.
|
|
30
|
+
- `FlatTransition` follows the engine's pure-edge transition shape: the condition key is `when` (was `filter`), and transitions carry no effects — the dev tooltip's Effects row is gone and its GROQ row is labeled "When".
|
|
31
|
+
- The explain surface narrates cascade triggers: a `when` action's trigger phrases as a stage gate ("… fires automatically when:") on both tiers — automation, never a button — replacing the deleted `completeWhen`/`failWhen` gates. fireAction filters (button grain) stay off the stage tooltip.
|
|
32
|
+
- The stage tooltip's explain block gains an activity roster: each activity labeled with its derived executor classification (autonomous / interactive / off-system / hybrid), live from the evaluation on the current stage and derived from the definition elsewhere.
|
|
33
|
+
|
|
34
|
+
### Patch Changes
|
|
35
|
+
|
|
36
|
+
- Updated dependencies [11e2d6f]
|
|
37
|
+
- Updated dependencies [7510fd5]
|
|
38
|
+
- Updated dependencies [01e8042]
|
|
39
|
+
- Updated dependencies [79c54db]
|
|
40
|
+
- Updated dependencies [6a46db1]
|
|
41
|
+
- Updated dependencies [35b9b85]
|
|
42
|
+
- Updated dependencies [e5f5b77]
|
|
43
|
+
- Updated dependencies [01e8042]
|
|
44
|
+
- @sanity/workflow-engine@0.16.0
|
|
45
|
+
|
|
3
46
|
## 0.1.0
|
|
4
47
|
|
|
5
48
|
### Minor Changes
|
package/dist/index.cjs
CHANGED
|
@@ -32,15 +32,7 @@ var jsxRuntime = require("react/jsx-runtime");
|
|
|
32
32
|
|
|
33
33
|
require("@xyflow/react/dist/style.css");
|
|
34
34
|
|
|
35
|
-
var ui = require("@sanity/ui"), react$1 = require("@xyflow/react"), react = require("react"), workflowEngine = require("@sanity/workflow-engine"),
|
|
36
|
-
|
|
37
|
-
function _interopDefaultCompat(e) {
|
|
38
|
-
return e && typeof e == "object" && "default" in e ? e : {
|
|
39
|
-
default: e
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
var ELK__default = /* @__PURE__ */ _interopDefaultCompat(ELK);
|
|
35
|
+
var ui = require("@sanity/ui"), react$1 = require("@xyflow/react"), react = require("react"), workflowEngine = require("@sanity/workflow-engine"), icons = require("@sanity/icons"), reactDom = require("react-dom");
|
|
44
36
|
|
|
45
37
|
const DevModeContext = react.createContext(!1);
|
|
46
38
|
|
|
@@ -54,6 +46,12 @@ function useHoveredStage() {
|
|
|
54
46
|
return react.useContext(HoveredStageContext);
|
|
55
47
|
}
|
|
56
48
|
|
|
49
|
+
function narratedWaits(answer, definition) {
|
|
50
|
+
return answer.completesWithoutCaller === "yes" ? [] : workflowEngine.narrateAutonomyWaits(answer.waitsOn, {
|
|
51
|
+
definition: definition
|
|
52
|
+
}).map(workflowEngine.sentenceCase);
|
|
53
|
+
}
|
|
54
|
+
|
|
57
55
|
const ExplainContext = react.createContext(void 0);
|
|
58
56
|
|
|
59
57
|
function useExplain() {
|
|
@@ -81,9 +79,19 @@ function indexExplanations(sites, definition) {
|
|
|
81
79
|
live: !1
|
|
82
80
|
}), byStage.set(site.stage, list);
|
|
83
81
|
}
|
|
82
|
+
const autonomy = workflowEngine.deriveWorkflowAutonomy(definition), activitiesByStage = new Map(definition.stages.map(stage => {
|
|
83
|
+
const stageAutonomy = workflowEngine.stageAutonomyOf(autonomy, stage.name);
|
|
84
|
+
return [ stage.name, (stage.activities ?? []).map(activity => ({
|
|
85
|
+
name: activity.name,
|
|
86
|
+
title: activity.title ?? activity.name,
|
|
87
|
+
classification: workflowEngine.deriveExecutorClassification(activity),
|
|
88
|
+
waits: narratedWaits(workflowEngine.activityAutonomyOf(stageAutonomy, activity.name), definition)
|
|
89
|
+
})) ];
|
|
90
|
+
}));
|
|
84
91
|
return {
|
|
85
92
|
transition: (stage, name) => transitions.get(`${stage}#${name}`),
|
|
86
|
-
stageGates: stage => byStage.get(stage) ?? []
|
|
93
|
+
stageGates: stage => byStage.get(stage) ?? [],
|
|
94
|
+
stageActivities: stage => activitiesByStage.get(stage) ?? []
|
|
87
95
|
};
|
|
88
96
|
}
|
|
89
97
|
|
|
@@ -113,13 +121,11 @@ function indexLiveExplanations(evaluation) {
|
|
|
113
121
|
activity: activity,
|
|
114
122
|
requirement: requirement
|
|
115
123
|
}, insight);
|
|
116
|
-
gate({
|
|
117
|
-
kind: "
|
|
118
|
-
activity: activity
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
activity: activity
|
|
122
|
-
}, a.failWhenInsight);
|
|
124
|
+
for (const action of a.actions) gate({
|
|
125
|
+
kind: "action-when",
|
|
126
|
+
activity: activity,
|
|
127
|
+
action: action.action.name
|
|
128
|
+
}, action.whenInsight);
|
|
123
129
|
}
|
|
124
130
|
for (const field of evaluation.editableFields) {
|
|
125
131
|
if (field.scope === "workflow") continue;
|
|
@@ -135,9 +141,16 @@ function indexLiveExplanations(evaluation) {
|
|
|
135
141
|
};
|
|
136
142
|
gate(address, field.insight);
|
|
137
143
|
}
|
|
144
|
+
const activities = evaluation.currentStage.activities.map(a => ({
|
|
145
|
+
name: a.activity.name,
|
|
146
|
+
title: a.activity.title ?? a.activity.name,
|
|
147
|
+
classification: a.classification,
|
|
148
|
+
waits: narratedWaits(a.autonomy, evaluation.definition)
|
|
149
|
+
}));
|
|
138
150
|
return {
|
|
139
151
|
transition: (stage, name) => transitions.get(`${stage}#${name}`),
|
|
140
|
-
stageGates: stage => stage === stageName ? gates : []
|
|
152
|
+
stageGates: stage => stage === stageName ? gates : [],
|
|
153
|
+
stageActivities: stage => stage === stageName ? activities : []
|
|
141
154
|
};
|
|
142
155
|
}
|
|
143
156
|
|
|
@@ -147,6 +160,10 @@ function mergeExplainData(live, fallback) {
|
|
|
147
160
|
stageGates: stage => {
|
|
148
161
|
const gates = live.stageGates(stage);
|
|
149
162
|
return gates.length > 0 ? gates : fallback.stageGates(stage);
|
|
163
|
+
},
|
|
164
|
+
stageActivities: stage => {
|
|
165
|
+
const activities = live.stageActivities(stage);
|
|
166
|
+
return activities.length > 0 ? activities : fallback.stageActivities(stage);
|
|
150
167
|
}
|
|
151
168
|
};
|
|
152
169
|
}
|
|
@@ -352,7 +369,7 @@ function groupEdge(agg, route) {
|
|
|
352
369
|
name: "grouped",
|
|
353
370
|
from: route.from,
|
|
354
371
|
to: route.to,
|
|
355
|
-
|
|
372
|
+
when: "true"
|
|
356
373
|
},
|
|
357
374
|
wasTraversed: agg.traversed,
|
|
358
375
|
isPrimary: !0,
|
|
@@ -551,7 +568,15 @@ function GhostNode({data: data}) {
|
|
|
551
568
|
});
|
|
552
569
|
}
|
|
553
570
|
|
|
554
|
-
|
|
571
|
+
let elkLoad;
|
|
572
|
+
|
|
573
|
+
function loadElk() {
|
|
574
|
+
return elkLoad ??= import("elkjs/lib/elk.bundled.js").then(mod => new mod.default).catch(err => {
|
|
575
|
+
throw elkLoad = void 0, err;
|
|
576
|
+
}), elkLoad;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
const STAGE_CHAR_PX = 8.5, STAGE_PAD_X = 16, STAGE_ICON_W = 25, STAGE_PAD_Y = 13, STAGE_LINE_H = 18, STAGE_MAX_TEXT_WIDTH = 150, STAGE_MIN_TEXT_WIDTH = 44;
|
|
555
580
|
|
|
556
581
|
function wrapStageLabel(title) {
|
|
557
582
|
const words = title.trim().split(/\s+/), lines = [];
|
|
@@ -624,9 +649,9 @@ async function layoutGraph(nodes, edges) {
|
|
|
624
649
|
nodes: [ ...nodes ],
|
|
625
650
|
edges: [ ...edges ]
|
|
626
651
|
};
|
|
627
|
-
const railNodes = new Set(edges.flatMap(e => edgeFlags(e).isPrimary ? [ e.source, e.target ] : [])), boxOf = new Map(nodes.map(n => [ n.id, {
|
|
628
|
-
w: n.width ??
|
|
629
|
-
h: n.height ??
|
|
652
|
+
const elk = await loadElk(), railNodes = new Set(edges.flatMap(e => edgeFlags(e).isPrimary ? [ e.source, e.target ] : [])), boxOf = new Map(nodes.map(n => [ n.id, {
|
|
653
|
+
w: n.width ?? 180,
|
|
654
|
+
h: n.height ?? 72
|
|
630
655
|
} ])), portMeta = /* @__PURE__ */ new Map, portFor = args => {
|
|
631
656
|
const id = `${args.node}.${args.key}`, list = portMeta.get(args.node) ?? [];
|
|
632
657
|
return list.push({
|
|
@@ -677,8 +702,8 @@ async function layoutGraph(nodes, edges) {
|
|
|
677
702
|
layoutOptions: defaultLayoutOptions(),
|
|
678
703
|
children: nodes.map(n => ({
|
|
679
704
|
id: n.id,
|
|
680
|
-
width: n.width ??
|
|
681
|
-
height: n.height ??
|
|
705
|
+
width: n.width ?? 180,
|
|
706
|
+
height: n.height ?? 72,
|
|
682
707
|
layoutOptions: {
|
|
683
708
|
...layerConstraintFor(n).layoutOptions,
|
|
684
709
|
"elk.portConstraints": constraint
|
|
@@ -732,8 +757,8 @@ function faceOffRailPorts(args) {
|
|
|
732
757
|
function pass1Centers(pass1, boxOf) {
|
|
733
758
|
return new Map((pass1.children ?? []).map(c => {
|
|
734
759
|
const box = boxOf.get(c.id) ?? {
|
|
735
|
-
w:
|
|
736
|
-
h:
|
|
760
|
+
w: 180,
|
|
761
|
+
h: 72
|
|
737
762
|
};
|
|
738
763
|
return [ c.id, {
|
|
739
764
|
cx: (c.x ?? 0) + box.w / 2,
|
|
@@ -816,8 +841,8 @@ function pinnedPortPositions(args) {
|
|
|
816
841
|
for (const child of args.pass1.children ?? []) pinChildPorts(child, {
|
|
817
842
|
meta: args.portMeta.get(child.id) ?? [],
|
|
818
843
|
box: args.boxOf.get(child.id) ?? {
|
|
819
|
-
w:
|
|
820
|
-
h:
|
|
844
|
+
w: 180,
|
|
845
|
+
h: 72
|
|
821
846
|
},
|
|
822
847
|
centers: centers,
|
|
823
848
|
out: out
|
|
@@ -1463,14 +1488,40 @@ function MetaTable({rows: rows, description: description, title: title}) {
|
|
|
1463
1488
|
});
|
|
1464
1489
|
}
|
|
1465
1490
|
|
|
1491
|
+
function ActivityRoster({activities: activities}) {
|
|
1492
|
+
/* @__PURE__ */
|
|
1493
|
+
return jsxRuntime.jsx(ui.Stack, {
|
|
1494
|
+
space: 2,
|
|
1495
|
+
children: activities.map(activity => /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, {
|
|
1496
|
+
space: 1,
|
|
1497
|
+
children: [
|
|
1498
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Text, {
|
|
1499
|
+
size: 1,
|
|
1500
|
+
muted: !0,
|
|
1501
|
+
children: [ activity.title, " — ", workflowEngine.EXECUTOR_CLASSIFICATION_DISPLAY[activity.classification].title ]
|
|
1502
|
+
}), activity.waits.map((wait, i) => /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
|
|
1503
|
+
size: 1,
|
|
1504
|
+
muted: !0,
|
|
1505
|
+
style: {
|
|
1506
|
+
paddingLeft: 12,
|
|
1507
|
+
lineHeight: 1.4
|
|
1508
|
+
},
|
|
1509
|
+
children: wait
|
|
1510
|
+
}, i)) ]
|
|
1511
|
+
}, activity.name))
|
|
1512
|
+
});
|
|
1513
|
+
}
|
|
1514
|
+
|
|
1466
1515
|
function StageExplanation({stage: stage}) {
|
|
1467
|
-
const
|
|
1468
|
-
if (gates.length === 0) return null;
|
|
1516
|
+
const explain = useExplain(), gates = explain?.stageGates(stage) ?? [], activities = explain?.stageActivities(stage) ?? [];
|
|
1517
|
+
if (gates.length === 0 && activities.length === 0) return null;
|
|
1469
1518
|
const {shown: shown, overflowNote: overflowNote} = cappedGates(gates);
|
|
1470
1519
|
/* @__PURE__ */
|
|
1471
1520
|
return jsxRuntime.jsxs(ui.Stack, {
|
|
1472
1521
|
space: 3,
|
|
1473
|
-
children: [
|
|
1522
|
+
children: [ activities.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(ActivityRoster, {
|
|
1523
|
+
activities: activities
|
|
1524
|
+
}) : null, shown.map((gate, i) => /* @__PURE__ */ jsxRuntime.jsx(ExplainedGateView, {
|
|
1474
1525
|
gate: gate
|
|
1475
1526
|
}, i)), overflowNote === void 0 ? null : /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
|
|
1476
1527
|
size: 1,
|
|
@@ -1656,14 +1707,10 @@ function devRows(t) {
|
|
|
1656
1707
|
value: t.name,
|
|
1657
1708
|
mono: !0
|
|
1658
1709
|
}, {
|
|
1659
|
-
label: "
|
|
1660
|
-
value: t.
|
|
1710
|
+
label: "When",
|
|
1711
|
+
value: t.when,
|
|
1661
1712
|
mono: !0
|
|
1662
|
-
}
|
|
1663
|
-
label: "Effects",
|
|
1664
|
-
value: t.effects.map(e => e.name).join(", "),
|
|
1665
|
-
mono: !0
|
|
1666
|
-
} ] : [] ];
|
|
1713
|
+
} ];
|
|
1667
1714
|
}
|
|
1668
1715
|
|
|
1669
1716
|
const LOCK_SIZE = 18;
|
package/dist/index.d.cts
CHANGED
|
@@ -39,8 +39,8 @@ export declare type LocalViewShow = "next" | "previous" | "both";
|
|
|
39
39
|
* Stage diagram for a workflow definition: stages + transitions laid out
|
|
40
40
|
* left-to-right, current stage highlighted, visited path toned by history.
|
|
41
41
|
* Hovering anywhere along a transition line opens its detail tooltip (title,
|
|
42
|
-
* description; machine name
|
|
43
|
-
*
|
|
42
|
+
* description; machine name and GROQ trigger only in devMode); a gated
|
|
43
|
+
* transition additionally carries a lock marker on the line.
|
|
44
44
|
*/
|
|
45
45
|
export declare function WorkflowDiagram({
|
|
46
46
|
definition,
|
|
@@ -85,8 +85,8 @@ export declare interface WorkflowDiagramProps {
|
|
|
85
85
|
/** Frozen canvas: no pan/zoom, no zoom controls — shows its initial fit.
|
|
86
86
|
* Tooltips, hover highlight and selection stay live. */
|
|
87
87
|
static?: boolean | undefined;
|
|
88
|
-
/** Tooling mode: tooltips additionally show machine names
|
|
89
|
-
*
|
|
88
|
+
/** Tooling mode: tooltips additionally show machine names and raw GROQ
|
|
89
|
+
* conditions. Never on for end users. */
|
|
90
90
|
devMode?: boolean | undefined;
|
|
91
91
|
/** Explain the workflow in human language: tooltips gain each gate's
|
|
92
92
|
* phrased requirements ("«Review decision» must be 'approve'") — derived
|
package/dist/index.d.ts
CHANGED
|
@@ -39,8 +39,8 @@ export declare type LocalViewShow = "next" | "previous" | "both";
|
|
|
39
39
|
* Stage diagram for a workflow definition: stages + transitions laid out
|
|
40
40
|
* left-to-right, current stage highlighted, visited path toned by history.
|
|
41
41
|
* Hovering anywhere along a transition line opens its detail tooltip (title,
|
|
42
|
-
* description; machine name
|
|
43
|
-
*
|
|
42
|
+
* description; machine name and GROQ trigger only in devMode); a gated
|
|
43
|
+
* transition additionally carries a lock marker on the line.
|
|
44
44
|
*/
|
|
45
45
|
export declare function WorkflowDiagram({
|
|
46
46
|
definition,
|
|
@@ -85,8 +85,8 @@ export declare interface WorkflowDiagramProps {
|
|
|
85
85
|
/** Frozen canvas: no pan/zoom, no zoom controls — shows its initial fit.
|
|
86
86
|
* Tooltips, hover highlight and selection stay live. */
|
|
87
87
|
static?: boolean | undefined;
|
|
88
|
-
/** Tooling mode: tooltips additionally show machine names
|
|
89
|
-
*
|
|
88
|
+
/** Tooling mode: tooltips additionally show machine names and raw GROQ
|
|
89
|
+
* conditions. Never on for end users. */
|
|
90
90
|
devMode?: boolean | undefined;
|
|
91
91
|
/** Explain the workflow in human language: tooltips gain each gate's
|
|
92
92
|
* phrased requirements ("«Review decision» must be 'approve'") — derived
|
package/dist/index.js
CHANGED
|
@@ -8,9 +8,7 @@ import { MarkerType, getSmoothStepPath, Handle, Position, BaseEdge, EdgeLabelRen
|
|
|
8
8
|
|
|
9
9
|
import { createContext, useContext, Fragment, useState, useMemo, useEffect, useRef, useCallback } from "react";
|
|
10
10
|
|
|
11
|
-
import { sentenceCase, describeSiteHeading, describeCondition, checklistLines, isTerminalStage } from "@sanity/workflow-engine";
|
|
12
|
-
|
|
13
|
-
import ELK from "elkjs/lib/elk.bundled.js";
|
|
11
|
+
import { sentenceCase, describeSiteHeading, deriveWorkflowAutonomy, stageAutonomyOf, activityAutonomyOf, deriveExecutorClassification, describeCondition, narrateAutonomyWaits, checklistLines, isTerminalStage, EXECUTOR_CLASSIFICATION_DISPLAY } from "@sanity/workflow-engine";
|
|
14
12
|
|
|
15
13
|
import { CheckmarkCircleIcon, CircleIcon, LockIcon } from "@sanity/icons";
|
|
16
14
|
|
|
@@ -28,6 +26,12 @@ function useHoveredStage() {
|
|
|
28
26
|
return useContext(HoveredStageContext);
|
|
29
27
|
}
|
|
30
28
|
|
|
29
|
+
function narratedWaits(answer, definition) {
|
|
30
|
+
return answer.completesWithoutCaller === "yes" ? [] : narrateAutonomyWaits(answer.waitsOn, {
|
|
31
|
+
definition: definition
|
|
32
|
+
}).map(sentenceCase);
|
|
33
|
+
}
|
|
34
|
+
|
|
31
35
|
const ExplainContext = createContext(void 0);
|
|
32
36
|
|
|
33
37
|
function useExplain() {
|
|
@@ -55,9 +59,19 @@ function indexExplanations(sites, definition) {
|
|
|
55
59
|
live: !1
|
|
56
60
|
}), byStage.set(site.stage, list);
|
|
57
61
|
}
|
|
62
|
+
const autonomy = deriveWorkflowAutonomy(definition), activitiesByStage = new Map(definition.stages.map(stage => {
|
|
63
|
+
const stageAutonomy = stageAutonomyOf(autonomy, stage.name);
|
|
64
|
+
return [ stage.name, (stage.activities ?? []).map(activity => ({
|
|
65
|
+
name: activity.name,
|
|
66
|
+
title: activity.title ?? activity.name,
|
|
67
|
+
classification: deriveExecutorClassification(activity),
|
|
68
|
+
waits: narratedWaits(activityAutonomyOf(stageAutonomy, activity.name), definition)
|
|
69
|
+
})) ];
|
|
70
|
+
}));
|
|
58
71
|
return {
|
|
59
72
|
transition: (stage, name) => transitions.get(`${stage}#${name}`),
|
|
60
|
-
stageGates: stage => byStage.get(stage) ?? []
|
|
73
|
+
stageGates: stage => byStage.get(stage) ?? [],
|
|
74
|
+
stageActivities: stage => activitiesByStage.get(stage) ?? []
|
|
61
75
|
};
|
|
62
76
|
}
|
|
63
77
|
|
|
@@ -87,13 +101,11 @@ function indexLiveExplanations(evaluation) {
|
|
|
87
101
|
activity: activity,
|
|
88
102
|
requirement: requirement
|
|
89
103
|
}, insight);
|
|
90
|
-
gate({
|
|
91
|
-
kind: "
|
|
92
|
-
activity: activity
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
activity: activity
|
|
96
|
-
}, a.failWhenInsight);
|
|
104
|
+
for (const action of a.actions) gate({
|
|
105
|
+
kind: "action-when",
|
|
106
|
+
activity: activity,
|
|
107
|
+
action: action.action.name
|
|
108
|
+
}, action.whenInsight);
|
|
97
109
|
}
|
|
98
110
|
for (const field of evaluation.editableFields) {
|
|
99
111
|
if (field.scope === "workflow") continue;
|
|
@@ -109,9 +121,16 @@ function indexLiveExplanations(evaluation) {
|
|
|
109
121
|
};
|
|
110
122
|
gate(address, field.insight);
|
|
111
123
|
}
|
|
124
|
+
const activities = evaluation.currentStage.activities.map(a => ({
|
|
125
|
+
name: a.activity.name,
|
|
126
|
+
title: a.activity.title ?? a.activity.name,
|
|
127
|
+
classification: a.classification,
|
|
128
|
+
waits: narratedWaits(a.autonomy, evaluation.definition)
|
|
129
|
+
}));
|
|
112
130
|
return {
|
|
113
131
|
transition: (stage, name) => transitions.get(`${stage}#${name}`),
|
|
114
|
-
stageGates: stage => stage === stageName ? gates : []
|
|
132
|
+
stageGates: stage => stage === stageName ? gates : [],
|
|
133
|
+
stageActivities: stage => stage === stageName ? activities : []
|
|
115
134
|
};
|
|
116
135
|
}
|
|
117
136
|
|
|
@@ -121,6 +140,10 @@ function mergeExplainData(live, fallback) {
|
|
|
121
140
|
stageGates: stage => {
|
|
122
141
|
const gates = live.stageGates(stage);
|
|
123
142
|
return gates.length > 0 ? gates : fallback.stageGates(stage);
|
|
143
|
+
},
|
|
144
|
+
stageActivities: stage => {
|
|
145
|
+
const activities = live.stageActivities(stage);
|
|
146
|
+
return activities.length > 0 ? activities : fallback.stageActivities(stage);
|
|
124
147
|
}
|
|
125
148
|
};
|
|
126
149
|
}
|
|
@@ -326,7 +349,7 @@ function groupEdge(agg, route) {
|
|
|
326
349
|
name: "grouped",
|
|
327
350
|
from: route.from,
|
|
328
351
|
to: route.to,
|
|
329
|
-
|
|
352
|
+
when: "true"
|
|
330
353
|
},
|
|
331
354
|
wasTraversed: agg.traversed,
|
|
332
355
|
isPrimary: !0,
|
|
@@ -525,7 +548,15 @@ function GhostNode({data: data}) {
|
|
|
525
548
|
});
|
|
526
549
|
}
|
|
527
550
|
|
|
528
|
-
|
|
551
|
+
let elkLoad;
|
|
552
|
+
|
|
553
|
+
function loadElk() {
|
|
554
|
+
return elkLoad ??= import("elkjs/lib/elk.bundled.js").then(mod => new mod.default).catch(err => {
|
|
555
|
+
throw elkLoad = void 0, err;
|
|
556
|
+
}), elkLoad;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
const STAGE_CHAR_PX = 8.5, STAGE_PAD_X = 16, STAGE_ICON_W = 25, STAGE_PAD_Y = 13, STAGE_LINE_H = 18, STAGE_MAX_TEXT_WIDTH = 150, STAGE_MIN_TEXT_WIDTH = 44;
|
|
529
560
|
|
|
530
561
|
function wrapStageLabel(title) {
|
|
531
562
|
const words = title.trim().split(/\s+/), lines = [];
|
|
@@ -598,9 +629,9 @@ async function layoutGraph(nodes, edges) {
|
|
|
598
629
|
nodes: [ ...nodes ],
|
|
599
630
|
edges: [ ...edges ]
|
|
600
631
|
};
|
|
601
|
-
const railNodes = new Set(edges.flatMap(e => edgeFlags(e).isPrimary ? [ e.source, e.target ] : [])), boxOf = new Map(nodes.map(n => [ n.id, {
|
|
602
|
-
w: n.width ??
|
|
603
|
-
h: n.height ??
|
|
632
|
+
const elk = await loadElk(), railNodes = new Set(edges.flatMap(e => edgeFlags(e).isPrimary ? [ e.source, e.target ] : [])), boxOf = new Map(nodes.map(n => [ n.id, {
|
|
633
|
+
w: n.width ?? 180,
|
|
634
|
+
h: n.height ?? 72
|
|
604
635
|
} ])), portMeta = /* @__PURE__ */ new Map, portFor = args => {
|
|
605
636
|
const id = `${args.node}.${args.key}`, list = portMeta.get(args.node) ?? [];
|
|
606
637
|
return list.push({
|
|
@@ -651,8 +682,8 @@ async function layoutGraph(nodes, edges) {
|
|
|
651
682
|
layoutOptions: defaultLayoutOptions(),
|
|
652
683
|
children: nodes.map(n => ({
|
|
653
684
|
id: n.id,
|
|
654
|
-
width: n.width ??
|
|
655
|
-
height: n.height ??
|
|
685
|
+
width: n.width ?? 180,
|
|
686
|
+
height: n.height ?? 72,
|
|
656
687
|
layoutOptions: {
|
|
657
688
|
...layerConstraintFor(n).layoutOptions,
|
|
658
689
|
"elk.portConstraints": constraint
|
|
@@ -706,8 +737,8 @@ function faceOffRailPorts(args) {
|
|
|
706
737
|
function pass1Centers(pass1, boxOf) {
|
|
707
738
|
return new Map((pass1.children ?? []).map(c => {
|
|
708
739
|
const box = boxOf.get(c.id) ?? {
|
|
709
|
-
w:
|
|
710
|
-
h:
|
|
740
|
+
w: 180,
|
|
741
|
+
h: 72
|
|
711
742
|
};
|
|
712
743
|
return [ c.id, {
|
|
713
744
|
cx: (c.x ?? 0) + box.w / 2,
|
|
@@ -790,8 +821,8 @@ function pinnedPortPositions(args) {
|
|
|
790
821
|
for (const child of args.pass1.children ?? []) pinChildPorts(child, {
|
|
791
822
|
meta: args.portMeta.get(child.id) ?? [],
|
|
792
823
|
box: args.boxOf.get(child.id) ?? {
|
|
793
|
-
w:
|
|
794
|
-
h:
|
|
824
|
+
w: 180,
|
|
825
|
+
h: 72
|
|
795
826
|
},
|
|
796
827
|
centers: centers,
|
|
797
828
|
out: out
|
|
@@ -1437,14 +1468,40 @@ function MetaTable({rows: rows, description: description, title: title}) {
|
|
|
1437
1468
|
});
|
|
1438
1469
|
}
|
|
1439
1470
|
|
|
1471
|
+
function ActivityRoster({activities: activities}) {
|
|
1472
|
+
/* @__PURE__ */
|
|
1473
|
+
return jsx(Stack, {
|
|
1474
|
+
space: 2,
|
|
1475
|
+
children: activities.map(activity => /* @__PURE__ */ jsxs(Stack, {
|
|
1476
|
+
space: 1,
|
|
1477
|
+
children: [
|
|
1478
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
1479
|
+
size: 1,
|
|
1480
|
+
muted: !0,
|
|
1481
|
+
children: [ activity.title, " — ", EXECUTOR_CLASSIFICATION_DISPLAY[activity.classification].title ]
|
|
1482
|
+
}), activity.waits.map((wait, i) => /* @__PURE__ */ jsx(Text, {
|
|
1483
|
+
size: 1,
|
|
1484
|
+
muted: !0,
|
|
1485
|
+
style: {
|
|
1486
|
+
paddingLeft: 12,
|
|
1487
|
+
lineHeight: 1.4
|
|
1488
|
+
},
|
|
1489
|
+
children: wait
|
|
1490
|
+
}, i)) ]
|
|
1491
|
+
}, activity.name))
|
|
1492
|
+
});
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1440
1495
|
function StageExplanation({stage: stage}) {
|
|
1441
|
-
const
|
|
1442
|
-
if (gates.length === 0) return null;
|
|
1496
|
+
const explain = useExplain(), gates = explain?.stageGates(stage) ?? [], activities = explain?.stageActivities(stage) ?? [];
|
|
1497
|
+
if (gates.length === 0 && activities.length === 0) return null;
|
|
1443
1498
|
const {shown: shown, overflowNote: overflowNote} = cappedGates(gates);
|
|
1444
1499
|
/* @__PURE__ */
|
|
1445
1500
|
return jsxs(Stack, {
|
|
1446
1501
|
space: 3,
|
|
1447
|
-
children: [
|
|
1502
|
+
children: [ activities.length > 0 ? /* @__PURE__ */ jsx(ActivityRoster, {
|
|
1503
|
+
activities: activities
|
|
1504
|
+
}) : null, shown.map((gate, i) => /* @__PURE__ */ jsx(ExplainedGateView, {
|
|
1448
1505
|
gate: gate
|
|
1449
1506
|
}, i)), overflowNote === void 0 ? null : /* @__PURE__ */ jsx(Text, {
|
|
1450
1507
|
size: 1,
|
|
@@ -1630,14 +1687,10 @@ function devRows(t) {
|
|
|
1630
1687
|
value: t.name,
|
|
1631
1688
|
mono: !0
|
|
1632
1689
|
}, {
|
|
1633
|
-
label: "
|
|
1634
|
-
value: t.
|
|
1635
|
-
mono: !0
|
|
1636
|
-
}, ...t.effects?.length ? [ {
|
|
1637
|
-
label: "Effects",
|
|
1638
|
-
value: t.effects.map(e => e.name).join(", "),
|
|
1690
|
+
label: "When",
|
|
1691
|
+
value: t.when,
|
|
1639
1692
|
mono: !0
|
|
1640
|
-
} ]
|
|
1693
|
+
} ];
|
|
1641
1694
|
}
|
|
1642
1695
|
|
|
1643
1696
|
const LOCK_SIZE = 18;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanity/workflow-diagram",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "React component rendering a workflow definition as a stage diagram — ELK layered layout, current-stage and visited-path highlighting, transition tooltips on line hover.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"diagram",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"@sanity/icons": "^3.7.7",
|
|
45
45
|
"@xyflow/react": "^12.11.1",
|
|
46
46
|
"elkjs": "^0.11.1",
|
|
47
|
-
"@sanity/workflow-engine": "0.
|
|
47
|
+
"@sanity/workflow-engine": "0.17.0"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@sanity/pkg-utils": "^10.5.3",
|
|
@@ -58,8 +58,8 @@
|
|
|
58
58
|
"react-dom": "^19.2.7",
|
|
59
59
|
"vite": "^8.1.2",
|
|
60
60
|
"vitest": "^4.1.8",
|
|
61
|
-
"@sanity/workflow-engine-test": "0.
|
|
62
|
-
"@sanity/workflow-examples": "0.
|
|
61
|
+
"@sanity/workflow-engine-test": "0.12.0",
|
|
62
|
+
"@sanity/workflow-examples": "0.7.0"
|
|
63
63
|
},
|
|
64
64
|
"peerDependencies": {
|
|
65
65
|
"@sanity/ui": "^3.3.0",
|