@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 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"), ELK = require("elkjs/lib/elk.bundled.js"), icons = require("@sanity/icons"), reactDom = require("react-dom");
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: "complete-when",
118
- activity: activity
119
- }, a.completeWhenInsight), gate({
120
- kind: "fail-when",
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
- filter: "true"
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
- const DEFAULT_WIDTH = 180, DEFAULT_HEIGHT = 72, elk = new ELK__default.default, 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;
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 ?? DEFAULT_WIDTH,
629
- h: n.height ?? DEFAULT_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 ?? DEFAULT_WIDTH,
681
- height: n.height ?? DEFAULT_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: DEFAULT_WIDTH,
736
- h: DEFAULT_HEIGHT
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: DEFAULT_WIDTH,
820
- h: DEFAULT_HEIGHT
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 gates = useExplain()?.stageGates(stage) ?? [];
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: [ shown.map((gate, i) => /* @__PURE__ */ jsxRuntime.jsx(ExplainedGateView, {
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: "Filter",
1660
- value: t.filter,
1710
+ label: "When",
1711
+ value: t.when,
1661
1712
  mono: !0
1662
- }, ...t.effects?.length ? [ {
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, GROQ filter and effects only in devMode); a
43
- * gated transition additionally carries a lock marker on the line.
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, GROQ filters
89
- * and effects. Never on for end users. */
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, GROQ filter and effects only in devMode); a
43
- * gated transition additionally carries a lock marker on the line.
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, GROQ filters
89
- * and effects. Never on for end users. */
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: "complete-when",
92
- activity: activity
93
- }, a.completeWhenInsight), gate({
94
- kind: "fail-when",
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
- filter: "true"
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
- const DEFAULT_WIDTH = 180, DEFAULT_HEIGHT = 72, elk = new ELK, 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;
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 ?? DEFAULT_WIDTH,
603
- h: n.height ?? DEFAULT_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 ?? DEFAULT_WIDTH,
655
- height: n.height ?? DEFAULT_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: DEFAULT_WIDTH,
710
- h: DEFAULT_HEIGHT
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: DEFAULT_WIDTH,
794
- h: DEFAULT_HEIGHT
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 gates = useExplain()?.stageGates(stage) ?? [];
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: [ shown.map((gate, i) => /* @__PURE__ */ jsx(ExplainedGateView, {
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: "Filter",
1634
- value: t.filter,
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.1.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.15.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.10.0",
62
- "@sanity/workflow-examples": "0.5.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",