@sanity/workflow-diagram 0.0.0 → 0.2.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 ADDED
@@ -0,0 +1,78 @@
1
+ # @sanity/workflow-diagram
2
+
3
+ ## 0.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - e5f5b77: **BREAKING:** follow the engine's actions-only payload model.
8
+ - `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".
9
+ - 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.
10
+ - 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.
11
+
12
+ ### Patch Changes
13
+
14
+ - Updated dependencies [11e2d6f]
15
+ - Updated dependencies [7510fd5]
16
+ - Updated dependencies [01e8042]
17
+ - Updated dependencies [79c54db]
18
+ - Updated dependencies [6a46db1]
19
+ - Updated dependencies [35b9b85]
20
+ - Updated dependencies [e5f5b77]
21
+ - Updated dependencies [01e8042]
22
+ - @sanity/workflow-engine@0.16.0
23
+
24
+ ## 0.1.0
25
+
26
+ ### Minor Changes
27
+
28
+ - bae7eb8: An opt-in `explain` prop phrases the workflow in human language: transition tooltips gain the filter's requirements under a "Transitions when:" lead-in (the title line above already names the transition), and stage tooltips list the stage-altitude gates (activity visibility/requirements/auto-completion, stage- and activity-scope field editability — never per-action button filters, and grouped glance edges keep listing titles only). Derived from the definition alone (no instance required) via the engine's describe surface, loaded on demand so non-explain consumers don't bundle the GROQ evaluator; no verdict marks in this static tier — a checklist with no instance has nothing to judge.
29
+
30
+ A live tier on top: pass the engine's `evaluation` alongside `explain` and the current stage's tooltips upgrade to real ✓/✗/? verdicts phrased from the evaluation's insight — synchronously, since the insight is pre-computed. Everywhere the evaluation doesn't speak (every other stage) falls back to the static tier's neutral bullets.
31
+
32
+ - 409f551: New package: renders a workflow definition as a stage diagram. ELK layered layout with `@xyflow/react` rendering; the primary rail follows the workflow's spine (longest simple path — authored transition order encodes engine precedence, not prominence); current-stage and visited-path highlighting from instance history; gated transitions carry a lock marker; tooltips open on hover anywhere along a line (title, description, and dev-mode machinery). Ships a local neighborhood view (`next` / `previous` / `both`), a grouped glance mode (`[N stages] → current → [M stages]` with click-to-expand), a static frozen mode, and a content-hugging canvas that centers itself when the graph is smaller than its box.
33
+
34
+ ### Patch Changes
35
+
36
+ - Updated dependencies [4ca17bb]
37
+ - Updated dependencies [5143707]
38
+ - Updated dependencies [0709ab1]
39
+ - Updated dependencies [6bbf3ca]
40
+ - Updated dependencies [9f5a40f]
41
+ - Updated dependencies [d192d68]
42
+ - Updated dependencies [6c21009]
43
+ - Updated dependencies [109f900]
44
+ - Updated dependencies [109f900]
45
+ - Updated dependencies [444b0e6]
46
+ - Updated dependencies [2511bb5]
47
+ - Updated dependencies [d4fd8e6]
48
+ - Updated dependencies [30ed0e8]
49
+ - Updated dependencies [2834704]
50
+ - Updated dependencies [2511bb5]
51
+ - Updated dependencies [2511bb5]
52
+ - Updated dependencies [2511bb5]
53
+ - Updated dependencies [4607d59]
54
+ - Updated dependencies [52f6024]
55
+ - Updated dependencies [a8f1cb9]
56
+ - Updated dependencies [adf32f7]
57
+ - Updated dependencies [5cb0850]
58
+ - Updated dependencies [70a136c]
59
+ - Updated dependencies [91540de]
60
+ - Updated dependencies [8455fbb]
61
+ - Updated dependencies [325c2d1]
62
+ - Updated dependencies [d6e92ed]
63
+ - Updated dependencies [6c21009]
64
+ - Updated dependencies [7ffdc77]
65
+ - Updated dependencies [325c2d1]
66
+ - Updated dependencies [7ffdc77]
67
+ - Updated dependencies [ced40e4]
68
+ - Updated dependencies [9b1b40d]
69
+ - Updated dependencies [8c319b2]
70
+ - Updated dependencies [dc64968]
71
+ - Updated dependencies [23cf131]
72
+ - Updated dependencies [d192d68]
73
+ - Updated dependencies [15337f1]
74
+ - Updated dependencies [2d1a8e1]
75
+ - Updated dependencies [12cdbd6]
76
+ - Updated dependencies [437b544]
77
+ - Updated dependencies [ced40e4]
78
+ - @sanity/workflow-engine@0.15.0
package/dist/index.cjs CHANGED
@@ -81,9 +81,15 @@ function indexExplanations(sites, definition) {
81
81
  live: !1
82
82
  }), byStage.set(site.stage, list);
83
83
  }
84
+ const activitiesByStage = new Map(definition.stages.map(stage => [ stage.name, (stage.activities ?? []).map(activity => ({
85
+ name: activity.name,
86
+ title: activity.title ?? activity.name,
87
+ classification: workflowEngine.deriveExecutorClassification(activity)
88
+ })) ]));
84
89
  return {
85
90
  transition: (stage, name) => transitions.get(`${stage}#${name}`),
86
- stageGates: stage => byStage.get(stage) ?? []
91
+ stageGates: stage => byStage.get(stage) ?? [],
92
+ stageActivities: stage => activitiesByStage.get(stage) ?? []
87
93
  };
88
94
  }
89
95
 
@@ -113,13 +119,11 @@ function indexLiveExplanations(evaluation) {
113
119
  activity: activity,
114
120
  requirement: requirement
115
121
  }, insight);
116
- gate({
117
- kind: "complete-when",
118
- activity: activity
119
- }, a.completeWhenInsight), gate({
120
- kind: "fail-when",
121
- activity: activity
122
- }, a.failWhenInsight);
122
+ for (const action of a.actions) gate({
123
+ kind: "action-when",
124
+ activity: activity,
125
+ action: action.action.name
126
+ }, action.whenInsight);
123
127
  }
124
128
  for (const field of evaluation.editableFields) {
125
129
  if (field.scope === "workflow") continue;
@@ -135,9 +139,15 @@ function indexLiveExplanations(evaluation) {
135
139
  };
136
140
  gate(address, field.insight);
137
141
  }
142
+ const activities = evaluation.currentStage.activities.map(a => ({
143
+ name: a.activity.name,
144
+ title: a.activity.title ?? a.activity.name,
145
+ classification: a.classification
146
+ }));
138
147
  return {
139
148
  transition: (stage, name) => transitions.get(`${stage}#${name}`),
140
- stageGates: stage => stage === stageName ? gates : []
149
+ stageGates: stage => stage === stageName ? gates : [],
150
+ stageActivities: stage => stage === stageName ? activities : []
141
151
  };
142
152
  }
143
153
 
@@ -147,6 +157,10 @@ function mergeExplainData(live, fallback) {
147
157
  stageGates: stage => {
148
158
  const gates = live.stageGates(stage);
149
159
  return gates.length > 0 ? gates : fallback.stageGates(stage);
160
+ },
161
+ stageActivities: stage => {
162
+ const activities = live.stageActivities(stage);
163
+ return activities.length > 0 ? activities : fallback.stageActivities(stage);
150
164
  }
151
165
  };
152
166
  }
@@ -352,7 +366,7 @@ function groupEdge(agg, route) {
352
366
  name: "grouped",
353
367
  from: route.from,
354
368
  to: route.to,
355
- filter: "true"
369
+ when: "true"
356
370
  },
357
371
  wasTraversed: agg.traversed,
358
372
  isPrimary: !0,
@@ -1463,14 +1477,28 @@ function MetaTable({rows: rows, description: description, title: title}) {
1463
1477
  });
1464
1478
  }
1465
1479
 
1480
+ function ActivityRoster({activities: activities}) {
1481
+ /* @__PURE__ */
1482
+ return jsxRuntime.jsx(ui.Stack, {
1483
+ space: 2,
1484
+ children: activities.map(activity => /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, {
1485
+ size: 1,
1486
+ muted: !0,
1487
+ children: [ activity.title, " — ", workflowEngine.EXECUTOR_CLASSIFICATION_DISPLAY[activity.classification].title ]
1488
+ }, activity.name))
1489
+ });
1490
+ }
1491
+
1466
1492
  function StageExplanation({stage: stage}) {
1467
- const gates = useExplain()?.stageGates(stage) ?? [];
1468
- if (gates.length === 0) return null;
1493
+ const explain = useExplain(), gates = explain?.stageGates(stage) ?? [], activities = explain?.stageActivities(stage) ?? [];
1494
+ if (gates.length === 0 && activities.length === 0) return null;
1469
1495
  const {shown: shown, overflowNote: overflowNote} = cappedGates(gates);
1470
1496
  /* @__PURE__ */
1471
1497
  return jsxRuntime.jsxs(ui.Stack, {
1472
1498
  space: 3,
1473
- children: [ shown.map((gate, i) => /* @__PURE__ */ jsxRuntime.jsx(ExplainedGateView, {
1499
+ children: [ activities.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(ActivityRoster, {
1500
+ activities: activities
1501
+ }) : null, shown.map((gate, i) => /* @__PURE__ */ jsxRuntime.jsx(ExplainedGateView, {
1474
1502
  gate: gate
1475
1503
  }, i)), overflowNote === void 0 ? null : /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
1476
1504
  size: 1,
@@ -1656,14 +1684,10 @@ function devRows(t) {
1656
1684
  value: t.name,
1657
1685
  mono: !0
1658
1686
  }, {
1659
- label: "Filter",
1660
- value: t.filter,
1687
+ label: "When",
1688
+ value: t.when,
1661
1689
  mono: !0
1662
- }, ...t.effects?.length ? [ {
1663
- label: "Effects",
1664
- value: t.effects.map(e => e.name).join(", "),
1665
- mono: !0
1666
- } ] : [] ];
1690
+ } ];
1667
1691
  }
1668
1692
 
1669
1693
  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,7 +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";
11
+ import { sentenceCase, describeSiteHeading, deriveExecutorClassification, describeCondition, checklistLines, isTerminalStage, EXECUTOR_CLASSIFICATION_DISPLAY } from "@sanity/workflow-engine";
12
12
 
13
13
  import ELK from "elkjs/lib/elk.bundled.js";
14
14
 
@@ -55,9 +55,15 @@ function indexExplanations(sites, definition) {
55
55
  live: !1
56
56
  }), byStage.set(site.stage, list);
57
57
  }
58
+ const activitiesByStage = new Map(definition.stages.map(stage => [ stage.name, (stage.activities ?? []).map(activity => ({
59
+ name: activity.name,
60
+ title: activity.title ?? activity.name,
61
+ classification: deriveExecutorClassification(activity)
62
+ })) ]));
58
63
  return {
59
64
  transition: (stage, name) => transitions.get(`${stage}#${name}`),
60
- stageGates: stage => byStage.get(stage) ?? []
65
+ stageGates: stage => byStage.get(stage) ?? [],
66
+ stageActivities: stage => activitiesByStage.get(stage) ?? []
61
67
  };
62
68
  }
63
69
 
@@ -87,13 +93,11 @@ function indexLiveExplanations(evaluation) {
87
93
  activity: activity,
88
94
  requirement: requirement
89
95
  }, insight);
90
- gate({
91
- kind: "complete-when",
92
- activity: activity
93
- }, a.completeWhenInsight), gate({
94
- kind: "fail-when",
95
- activity: activity
96
- }, a.failWhenInsight);
96
+ for (const action of a.actions) gate({
97
+ kind: "action-when",
98
+ activity: activity,
99
+ action: action.action.name
100
+ }, action.whenInsight);
97
101
  }
98
102
  for (const field of evaluation.editableFields) {
99
103
  if (field.scope === "workflow") continue;
@@ -109,9 +113,15 @@ function indexLiveExplanations(evaluation) {
109
113
  };
110
114
  gate(address, field.insight);
111
115
  }
116
+ const activities = evaluation.currentStage.activities.map(a => ({
117
+ name: a.activity.name,
118
+ title: a.activity.title ?? a.activity.name,
119
+ classification: a.classification
120
+ }));
112
121
  return {
113
122
  transition: (stage, name) => transitions.get(`${stage}#${name}`),
114
- stageGates: stage => stage === stageName ? gates : []
123
+ stageGates: stage => stage === stageName ? gates : [],
124
+ stageActivities: stage => stage === stageName ? activities : []
115
125
  };
116
126
  }
117
127
 
@@ -121,6 +131,10 @@ function mergeExplainData(live, fallback) {
121
131
  stageGates: stage => {
122
132
  const gates = live.stageGates(stage);
123
133
  return gates.length > 0 ? gates : fallback.stageGates(stage);
134
+ },
135
+ stageActivities: stage => {
136
+ const activities = live.stageActivities(stage);
137
+ return activities.length > 0 ? activities : fallback.stageActivities(stage);
124
138
  }
125
139
  };
126
140
  }
@@ -326,7 +340,7 @@ function groupEdge(agg, route) {
326
340
  name: "grouped",
327
341
  from: route.from,
328
342
  to: route.to,
329
- filter: "true"
343
+ when: "true"
330
344
  },
331
345
  wasTraversed: agg.traversed,
332
346
  isPrimary: !0,
@@ -1437,14 +1451,28 @@ function MetaTable({rows: rows, description: description, title: title}) {
1437
1451
  });
1438
1452
  }
1439
1453
 
1454
+ function ActivityRoster({activities: activities}) {
1455
+ /* @__PURE__ */
1456
+ return jsx(Stack, {
1457
+ space: 2,
1458
+ children: activities.map(activity => /* @__PURE__ */ jsxs(Text, {
1459
+ size: 1,
1460
+ muted: !0,
1461
+ children: [ activity.title, " — ", EXECUTOR_CLASSIFICATION_DISPLAY[activity.classification].title ]
1462
+ }, activity.name))
1463
+ });
1464
+ }
1465
+
1440
1466
  function StageExplanation({stage: stage}) {
1441
- const gates = useExplain()?.stageGates(stage) ?? [];
1442
- if (gates.length === 0) return null;
1467
+ const explain = useExplain(), gates = explain?.stageGates(stage) ?? [], activities = explain?.stageActivities(stage) ?? [];
1468
+ if (gates.length === 0 && activities.length === 0) return null;
1443
1469
  const {shown: shown, overflowNote: overflowNote} = cappedGates(gates);
1444
1470
  /* @__PURE__ */
1445
1471
  return jsxs(Stack, {
1446
1472
  space: 3,
1447
- children: [ shown.map((gate, i) => /* @__PURE__ */ jsx(ExplainedGateView, {
1473
+ children: [ activities.length > 0 ? /* @__PURE__ */ jsx(ActivityRoster, {
1474
+ activities: activities
1475
+ }) : null, shown.map((gate, i) => /* @__PURE__ */ jsx(ExplainedGateView, {
1448
1476
  gate: gate
1449
1477
  }, i)), overflowNote === void 0 ? null : /* @__PURE__ */ jsx(Text, {
1450
1478
  size: 1,
@@ -1630,14 +1658,10 @@ function devRows(t) {
1630
1658
  value: t.name,
1631
1659
  mono: !0
1632
1660
  }, {
1633
- label: "Filter",
1634
- value: t.filter,
1661
+ label: "When",
1662
+ value: t.when,
1635
1663
  mono: !0
1636
- }, ...t.effects?.length ? [ {
1637
- label: "Effects",
1638
- value: t.effects.map(e => e.name).join(", "),
1639
- mono: !0
1640
- } ] : [] ];
1664
+ } ];
1641
1665
  }
1642
1666
 
1643
1667
  const LOCK_SIZE = 18;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanity/workflow-diagram",
3
- "version": "0.0.0",
3
+ "version": "0.2.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.14.0"
47
+ "@sanity/workflow-engine": "0.16.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.9.0",
62
- "@sanity/workflow-examples": "0.4.0"
61
+ "@sanity/workflow-engine-test": "0.11.0",
62
+ "@sanity/workflow-examples": "0.6.0"
63
63
  },
64
64
  "peerDependencies": {
65
65
  "@sanity/ui": "^3.3.0",