@sanity/workflow-studio-plugin 0.22.0 → 0.24.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.
@@ -1,6 +1,247 @@
1
1
  "use strict";
2
2
 
3
- var jsxRuntime = require("react/jsx-runtime"), ui = require("@sanity/ui"), workflowReact = require("@sanity/workflow-react"), react = require("react"), router = require("sanity/router"), index = require("./index.cjs"), ArrowLeft = require("@sanity/icons/ArrowLeft"), workflowDiagram = require("@sanity/workflow-diagram"), workflowEngine = require("@sanity/workflow-engine"), Add = require("@sanity/icons/Add"), Search = require("@sanity/icons/Search"), sanity = require("sanity"), Checkmark = require("@sanity/icons/Checkmark"), Close = require("@sanity/icons/Close"), Filter = require("@sanity/icons/Filter"), workflowComponents = require("@sanity/workflow-components"), endOfDay = require("date-fns/endOfDay"), reactDom = require("react-dom"), Calendar = require("@sanity/icons/Calendar"), workflowStudio = require("@sanity/workflow-studio");
3
+ var jsxRuntime = require("react/jsx-runtime"), ui = require("@sanity/ui"), workflowReact = require("@sanity/workflow-react"), react = require("react"), router = require("sanity/router"), index = require("./index.cjs"), Search = require("@sanity/icons/Search"), workflowEngine = require("@sanity/workflow-engine"), ChevronDown = require("@sanity/icons/ChevronDown"), Inline = require("@sanity/icons/Inline"), StackCompact = require("@sanity/icons/StackCompact"), reactDom = require("react-dom"), types = require("@sanity/types"), sanity = require("sanity"), workflowStudio = require("@sanity/workflow-studio"), WarningOutline = require("@sanity/icons/WarningOutline"), workflowDiagram = require("@sanity/workflow-diagram"), ArrowLeft = require("@sanity/icons/ArrowLeft"), InfoOutline = require("@sanity/icons/InfoOutline"), Transfer = require("@sanity/icons/Transfer"), pluralize = require("pluralize-esm"), Clock = require("@sanity/icons/Clock"), Cog = require("@sanity/icons/Cog"), EllipsisHorizontal = require("@sanity/icons/EllipsisHorizontal"), Add = require("@sanity/icons/Add"), Checkmark = require("@sanity/icons/Checkmark"), Close = require("@sanity/icons/Close"), Filter = require("@sanity/icons/Filter"), workflowComponents = require("@sanity/workflow-components"), endOfDay = require("date-fns/endOfDay"), Calendar = require("@sanity/icons/Calendar");
4
+
5
+ function _interopDefaultCompat(e) {
6
+ return e && typeof e == "object" && "default" in e ? e : {
7
+ default: e
8
+ };
9
+ }
10
+
11
+ var pluralize__default = /* @__PURE__ */ _interopDefaultCompat(pluralize);
12
+
13
+ function MutedNote({text: text}) {
14
+ /* @__PURE__ */
15
+ return jsxRuntime.jsx(ui.Box, {
16
+ paddingY: 3,
17
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
18
+ muted: !0,
19
+ size: 1,
20
+ children: text
21
+ })
22
+ });
23
+ }
24
+
25
+ function TabStatusRow({children: children}) {
26
+ /* @__PURE__ */
27
+ return jsxRuntime.jsx(ui.Box, {
28
+ paddingLeft: 2,
29
+ paddingY: 3,
30
+ children: children
31
+ });
32
+ }
33
+
34
+ const TOOL_PANEL_PADDING = 3, UNREADABLE = {
35
+ readable: !1,
36
+ raw: void 0
37
+ };
38
+
39
+ function defaultStorage() {
40
+ try {
41
+ return globalThis.localStorage;
42
+ } catch {
43
+ return;
44
+ }
45
+ }
46
+
47
+ function readItem(storage, key) {
48
+ if (!storage) return UNREADABLE;
49
+ try {
50
+ const raw = storage.getItem(key);
51
+ return {
52
+ readable: !0,
53
+ raw: raw === null ? void 0 : raw
54
+ };
55
+ } catch {
56
+ return UNREADABLE;
57
+ }
58
+ }
59
+
60
+ function writeItem(args) {
61
+ if (!args.storage) return !1;
62
+ try {
63
+ return args.storage.setItem(args.key, args.value), !0;
64
+ } catch {
65
+ return !1;
66
+ }
67
+ }
68
+
69
+ const BAND_DEFAULT_OPEN = !0, UNGROUPED_BAND_KEY = "section:no-document", PAYLOAD_VERSION = 2, MAX_ENTRIES = 200;
70
+
71
+ function bandStateStorageKey(scope) {
72
+ return `sanity.workflows.tool.bands:${scope}`;
73
+ }
74
+
75
+ function isRecord(value) {
76
+ return typeof value == "object" && value !== null && !Array.isArray(value);
77
+ }
78
+
79
+ function bandEntryOf(value) {
80
+ if (!isRecord(value)) return;
81
+ const {open: open, touched: touched} = value;
82
+ if (!(typeof open != "boolean" || typeof touched != "number") && Number.isFinite(touched)) return {
83
+ open: open,
84
+ touched: touched
85
+ };
86
+ }
87
+
88
+ function parsePayload(raw) {
89
+ try {
90
+ const parsed = JSON.parse(raw);
91
+ if (!isRecord(parsed) || parsed.version !== PAYLOAD_VERSION || !isRecord(parsed.bands)) return;
92
+ const entries = [];
93
+ for (const [key, value] of Object.entries(parsed.bands)) {
94
+ const entry = bandEntryOf(value);
95
+ if (entry === void 0) return;
96
+ entries.push([ key, entry ]);
97
+ }
98
+ return Object.fromEntries(entries);
99
+ } catch {
100
+ return;
101
+ }
102
+ }
103
+
104
+ function readStored(storage, storageKey) {
105
+ const {readable: readable, raw: raw} = readItem(storage, storageKey);
106
+ if (!readable) return {
107
+ entries: void 0,
108
+ corrupt: !1
109
+ };
110
+ if (raw === void 0) return {
111
+ entries: {},
112
+ corrupt: !1
113
+ };
114
+ const entries = parsePayload(raw);
115
+ return {
116
+ entries: entries,
117
+ corrupt: entries === void 0
118
+ };
119
+ }
120
+
121
+ function writeStored(args) {
122
+ return writeItem({
123
+ storage: args.storage,
124
+ key: args.storageKey,
125
+ value: JSON.stringify({
126
+ version: PAYLOAD_VERSION,
127
+ bands: args.entries
128
+ })
129
+ });
130
+ }
131
+
132
+ function baseEntries(args) {
133
+ return args.stored === void 0 ? args.held : args.unpersisted ? mergeByTouched(args.stored, args.held) : args.stored;
134
+ }
135
+
136
+ function mergeByTouched(stored, held) {
137
+ const merged = {
138
+ ...stored
139
+ };
140
+ for (const [key, entry] of Object.entries(held)) {
141
+ const persisted = merged[key];
142
+ (persisted === void 0 || entry.touched >= persisted.touched) && (merged[key] = entry);
143
+ }
144
+ return merged;
145
+ }
146
+
147
+ function prune(entries) {
148
+ const held = Object.entries(entries);
149
+ return held.length <= MAX_ENTRIES ? entries : Object.fromEntries(held.sort(([, a], [, b]) => b.touched - a.touched).slice(0, MAX_ENTRIES));
150
+ }
151
+
152
+ function togglesOf(entries) {
153
+ return new Map(Object.entries(entries).map(([key, entry]) => [ key, entry.open ]));
154
+ }
155
+
156
+ function createBandStateStore(args) {
157
+ const storage = args.storage ?? defaultStorage(), initial = readStored(storage, args.storageKey);
158
+ initial.corrupt && writeStored({
159
+ storage: storage,
160
+ storageKey: args.storageKey,
161
+ entries: {}
162
+ });
163
+ let held = initial.entries ?? {}, toggles = togglesOf(held);
164
+ const subscribers = index.createSubscribers();
165
+ let unpersisted = !1;
166
+ return {
167
+ subscribe: subscribers.subscribe,
168
+ read: () => toggles,
169
+ setOpen(key, open) {
170
+ const stored = readStored(storage, args.storageKey).entries, base = baseEntries({
171
+ stored: stored,
172
+ held: held,
173
+ unpersisted: unpersisted
174
+ });
175
+ held = prune({
176
+ ...base,
177
+ [key]: {
178
+ open: open,
179
+ touched: Date.now()
180
+ }
181
+ }), toggles = togglesOf(held), unpersisted = !writeStored({
182
+ storage: storage,
183
+ storageKey: args.storageKey,
184
+ entries: held
185
+ }), subscribers.notify();
186
+ }
187
+ };
188
+ }
189
+
190
+ function useBandOpenState(args) {
191
+ const {segment: segment, scope: scope} = args, store = react.useMemo(() => createBandStateStore({
192
+ storageKey: bandStateStorageKey(scope)
193
+ }), [ scope ]);
194
+ return react.useMemo(() => ({
195
+ segment: segment,
196
+ store: store
197
+ }), [ segment, store ]);
198
+ }
199
+
200
+ function openOf(store, qualified) {
201
+ return store.read().get(qualified) ?? BAND_DEFAULT_OPEN;
202
+ }
203
+
204
+ function useBandOpen(bands, key) {
205
+ const {segment: segment, store: store} = bands, qualified = `${segment}/${key}`;
206
+ return {
207
+ open: react.useSyncExternalStore(store.subscribe, react.useCallback(() => openOf(store, qualified), [ store, qualified ])),
208
+ toggle: () => store.setOpen(qualified, !openOf(store, qualified))
209
+ };
210
+ }
211
+
212
+ const lastRead = /* @__PURE__ */ new WeakMap, issuedReads = /* @__PURE__ */ new WeakMap;
213
+
214
+ function useDeployedDefinitions() {
215
+ const {engine: engine} = index.useWorkflowContext(), [definitions, setDefinitions] = react.useState(() => lastRead.get(engine)?.rows), [error, setError] = react.useState(void 0);
216
+ return react.useEffect(() => {
217
+ let cancelled = !1;
218
+ setDefinitions(lastRead.get(engine)?.rows), setError(void 0);
219
+ const seq = (issuedReads.get(engine) ?? 0) + 1;
220
+ return issuedReads.set(engine, seq), (async () => {
221
+ const rows = await index.readDeployedDefinitions(engine), latest = workflowEngine.latestDeployedDefinitions(rows), held = lastRead.get(engine);
222
+ (held === void 0 || seq > held.seq) && lastRead.set(engine, {
223
+ seq: seq,
224
+ rows: latest
225
+ }), cancelled || (setDefinitions(latest), setError(void 0));
226
+ })().catch(err => {
227
+ cancelled || setError(index.describeError(err));
228
+ }), () => {
229
+ cancelled = !0;
230
+ };
231
+ }, [ engine ]), {
232
+ definitions: definitions,
233
+ error: error
234
+ };
235
+ }
236
+
237
+ const REVEAL_MAX_WAIT_MS = 2e3;
238
+
239
+ function useRevealGate(args) {
240
+ const {loading: loading, ready: ready} = args, settled = !loading && ready, expired = index.useDelayedFlag(!loading && !ready, REVEAL_MAX_WAIT_MS), [revealed, setRevealed] = react.useState(!1);
241
+ return react.useEffect(() => {
242
+ !revealed && (settled || expired) && setRevealed(!0);
243
+ }, [ expired, revealed, settled ]), revealed || settled || expired;
244
+ }
4
245
 
5
246
  function documentRefsOf(instance) {
6
247
  const seen = /* @__PURE__ */ new Map;
@@ -8,24 +249,2093 @@ function documentRefsOf(instance) {
8
249
  return [ ...seen.values() ];
9
250
  }
10
251
 
11
- function InstanceTaskLists({entry: entry, onOpenActivity: onOpenActivity}) {
12
- return index.instanceNotice(entry) || /* @__PURE__ */ jsxRuntime.jsx(index.StaleLock, {
13
- stale: index.isEvaluationStale(entry),
14
- children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, {
252
+ function byTitleThenName(a, b) {
253
+ return a.title.localeCompare(b.title) || a.name.localeCompare(b.name);
254
+ }
255
+
256
+ function byResolvedTitle(titleOf, keyOf) {
257
+ return (a, b) => {
258
+ const aTitle = titleOf(a), bTitle = titleOf(b);
259
+ return aTitle !== void 0 && bTitle !== void 0 ? aTitle.localeCompare(bTitle) || keyOf(a).localeCompare(keyOf(b)) : aTitle !== void 0 ? -1 : bTitle !== void 0 ? 1 : keyOf(a).localeCompare(keyOf(b));
260
+ };
261
+ }
262
+
263
+ const BOARD_EMPTY_STAGE_NOTE = "No items";
264
+
265
+ function faceOf(instance) {
266
+ const document = documentRefsOf(instance)[0];
267
+ return document ? {
268
+ kind: "document",
269
+ document: document
270
+ } : {
271
+ kind: "instance",
272
+ title: index.instanceTitle(instance, index.definitionSnapshotOf(instance))
273
+ };
274
+ }
275
+
276
+ function boardRuns(instances) {
277
+ return instances.filter(instance => workflowEngine.terminalState(instance) !== "aborted");
278
+ }
279
+
280
+ function groupsOf(args) {
281
+ const declared = args.definition.stages.map(stage => stage.name), retired = [ ...args.byStage.keys() ].filter(stage => !declared.includes(stage)).sort();
282
+ return [ ...declared, ...retired ].map(stage => ({
283
+ stage: stage,
284
+ title: index.stageTitle(args.definition, stage),
285
+ cards: args.byStage.get(stage) ?? [],
286
+ retired: !declared.includes(stage)
287
+ }));
288
+ }
289
+
290
+ function deriveBoard(args) {
291
+ const byStage = /* @__PURE__ */ new Map;
292
+ for (const instance of boardRuns(args.instances)) {
293
+ const card = {
294
+ instanceId: instance._id,
295
+ face: faceOf(instance)
296
+ }, held = byStage.get(instance.currentStage);
297
+ held ? held.push(card) : byStage.set(instance.currentStage, [ card ]);
298
+ }
299
+ return {
300
+ groups: groupsOf({
301
+ definition: args.definition,
302
+ byStage: byStage
303
+ })
304
+ };
305
+ }
306
+
307
+ function orderBoardByPreviewTitle(board, titles) {
308
+ const compare = byResolvedTitle(card => card.face.kind === "instance" ? card.face.title : titles.get(card.face.document.id), card => card.instanceId);
309
+ return {
310
+ groups: board.groups.map(group => ({
311
+ ...group,
312
+ cards: [ ...group.cards ].sort(compare)
313
+ }))
314
+ };
315
+ }
316
+
317
+ function boardDocuments(board) {
318
+ return board.groups.flatMap(group => group.cards.flatMap(card => card.face.kind === "document" ? [ card.face.document ] : []));
319
+ }
320
+
321
+ function BoardCard({card: card, size: size}) {
322
+ return size === "slim" ? card.face.kind === "instance" ? /* @__PURE__ */ jsxRuntime.jsx(InstanceFace, {
323
+ size: size,
324
+ title: card.face.title
325
+ }) : /* @__PURE__ */ jsxRuntime.jsx(index.DocPreviewLink, {
326
+ gdr: card.face.document,
327
+ layout: "row",
328
+ source: "board-card"
329
+ }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Card, {
330
+ border: !0,
331
+ radius: 3,
332
+ children: card.face.kind === "instance" ? /* @__PURE__ */ jsxRuntime.jsx(InstanceFace, {
333
+ size: size,
334
+ title: card.face.title
335
+ }) : /* @__PURE__ */ jsxRuntime.jsx(index.DocPreviewLink, {
336
+ gdr: card.face.document,
337
+ layout: "bare",
338
+ source: "board-card"
339
+ })
340
+ });
341
+ }
342
+
343
+ function InstanceFace({size: size, title: title}) {
344
+ const fill = size === "slim";
345
+ /* @__PURE__ */
346
+ return jsxRuntime.jsx(ui.Box, {
347
+ padding: fill ? index.chipPadding(!0) : index.NOTICE_PADDING,
348
+ style: fill ? {
349
+ width: "100%"
350
+ } : {},
351
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
352
+ size: 1,
353
+ textOverflow: "ellipsis",
354
+ weight: "medium",
355
+ children: title
356
+ })
357
+ });
358
+ }
359
+
360
+ function StageGroupHeading({group: group}) {
361
+ /* @__PURE__ */
362
+ return jsxRuntime.jsxs(ui.Flex, {
363
+ align: "center",
364
+ gap: 2,
365
+ children: [
366
+ /* @__PURE__ */ jsxRuntime.jsx(index.CountedLabel, {
367
+ count: group.cards.length,
368
+ label: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
369
+ size: 1,
370
+ weight: "medium",
371
+ children: group.title
372
+ })
373
+ }), group.retired ?
374
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
375
+ muted: !0,
376
+ size: 1,
377
+ children: "No longer in this workflow"
378
+ }) : null ]
379
+ });
380
+ }
381
+
382
+ const COLUMN_WIDTH = 320, PANEL_FADE = "linear-gradient(to bottom, rgb(0 0 0) 50%, rgb(0 0 0 / 0) 75%)";
383
+
384
+ function StageColumn({group: group}) {
385
+ /* @__PURE__ */
386
+ return jsxRuntime.jsxs(ui.Box, {
387
+ flex: "none",
388
+ style: {
389
+ display: "flex",
390
+ flexDirection: "column",
391
+ position: "relative",
392
+ width: COLUMN_WIDTH
393
+ },
394
+ children: [
395
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Card, {
396
+ "aria-hidden": !0,
397
+ radius: 3,
398
+ style: {
399
+ inset: 0,
400
+ maskImage: PANEL_FADE,
401
+ position: "absolute",
402
+ WebkitMaskImage: PANEL_FADE
403
+ },
404
+ tone: "transparent"
405
+ }),
406
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
407
+ flex: "none",
408
+ paddingX: 3,
409
+ paddingY: 4,
410
+ style: {
411
+ position: "relative"
412
+ },
413
+ children: /* @__PURE__ */ jsxRuntime.jsx(StageGroupHeading, {
414
+ group: group
415
+ })
416
+ }), group.cards.length === 0 ?
417
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, {
418
+ align: "center",
419
+ flex: 1,
420
+ justify: "center",
421
+ padding: 2,
422
+ style: {
423
+ position: "relative"
424
+ },
425
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
426
+ muted: !0,
427
+ size: 1,
428
+ children: BOARD_EMPTY_STAGE_NOTE
429
+ })
430
+ }) :
431
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
432
+ flex: 1,
433
+ overflow: "auto",
434
+ paddingBottom: 2,
435
+ paddingX: 2,
436
+ style: {
437
+ minHeight: 0,
438
+ position: "relative"
439
+ },
440
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Stack, {
441
+ gap: 1,
442
+ children: group.cards.map(card => /* @__PURE__ */ jsxRuntime.jsx(BoardCard, {
443
+ card: card,
444
+ size: "block"
445
+ }, card.instanceId))
446
+ })
447
+ }) ]
448
+ });
449
+ }
450
+
451
+ function StageGroups({board: board}) {
452
+ const topGap = index.useSpaceToken(TOOL_PANEL_PADDING);
453
+ /* @__PURE__ */
454
+ return jsxRuntime.jsx(ui.Box, {
455
+ flex: 1,
456
+ style: {
457
+ minHeight: 0,
458
+ position: "relative"
459
+ },
460
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
461
+ style: {
462
+ bottom: 0,
463
+ left: 0,
464
+ overflowX: "auto",
465
+ overflowY: "hidden",
466
+ position: "absolute",
467
+ right: 0,
468
+ top: topGap
469
+ },
470
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, {
471
+ align: "stretch",
472
+ gap: 2,
473
+ paddingX: TOOL_PANEL_PADDING,
474
+ style: {
475
+ boxSizing: "border-box",
476
+ height: "100%",
477
+ width: "max-content"
478
+ },
479
+ children: board.groups.map(group => /* @__PURE__ */ jsxRuntime.jsx(StageColumn, {
480
+ group: group
481
+ }, group.stage))
482
+ })
483
+ })
484
+ });
485
+ }
486
+
487
+ function isPlainClick(event) {
488
+ return !event.altKey && !event.ctrlKey && !event.metaKey && !event.shiftKey;
489
+ }
490
+
491
+ const BOARD_LAYOUTS = [ "columns", "stack" ], BOARD_DEFAULT_LAYOUT = "columns";
492
+
493
+ function boardLayoutStorageKey(scope) {
494
+ return `sanity.workflows.tool.board-layout:${scope}`;
495
+ }
496
+
497
+ function layoutOf(raw) {
498
+ return BOARD_LAYOUTS.find(layout => layout === raw) ?? BOARD_DEFAULT_LAYOUT;
499
+ }
500
+
501
+ function createBoardLayoutStore(args) {
502
+ const storage = args.storage ?? defaultStorage();
503
+ return {
504
+ read: () => layoutOf(readItem(storage, args.storageKey).raw),
505
+ set(layout) {
506
+ return writeItem({
507
+ storage: storage,
508
+ key: args.storageKey,
509
+ value: layout
510
+ }), layout;
511
+ }
512
+ };
513
+ }
514
+
515
+ const PICKER_RADIUS = 3;
516
+
517
+ function pickerLabel(face) {
518
+ return face.kind === "named" ? face.title : face.kind === "loading" ? "Loading" : "Select a workflow";
519
+ }
520
+
521
+ function WorkflowPicker({face: face, onSelect: onSelect, options: options}) {
522
+ const empty = options.length === 0, innerRadius = index.useRadiusToken(PICKER_RADIUS) - 1;
523
+ /* @__PURE__ */
524
+ return jsxRuntime.jsx(ui.Card, {
525
+ border: !0,
526
+ radius: PICKER_RADIUS,
527
+ children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, {
528
+ align: "center",
529
+ children: [
530
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
531
+ paddingLeft: 2,
532
+ paddingRight: 2,
533
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
534
+ muted: !0,
535
+ size: 1,
536
+ children: "Workflow"
537
+ })
538
+ }),
539
+ /* @__PURE__ */ jsxRuntime.jsx(ui.MenuButton, {
540
+ button: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, {
541
+ fontSize: 1,
542
+ gap: 2,
543
+ iconRight: ChevronDown.ChevronDownIcon,
544
+ mode: "bleed",
545
+ disabled: empty,
546
+ padding: 2,
547
+ radius: 0,
548
+ style: {
549
+ borderRadius: `0 ${innerRadius}px ${innerRadius}px 0`
550
+ },
551
+ text: pickerLabel(face)
552
+ }),
553
+ id: "workflows-board-picker",
554
+ menu: /* @__PURE__ */ jsxRuntime.jsx(ui.Menu, {
555
+ children: options.map(option => /* @__PURE__ */ jsxRuntime.jsx(ui.MenuItem, {
556
+ fontSize: 1,
557
+ onClick: () => onSelect(option.name),
558
+ pressed: face.kind === "named" && option.name === face.name,
559
+ text: option.title
560
+ }, option.name))
561
+ }),
562
+ popover: {
563
+ constrainSize: !0,
564
+ fallbackPlacements: [ "top-start" ],
565
+ placement: "bottom-start",
566
+ portal: !0
567
+ }
568
+ }) ]
569
+ })
570
+ });
571
+ }
572
+
573
+ const LAYOUT_LABELS = {
574
+ columns: {
575
+ icon: Inline.InlineIcon,
576
+ label: "Show as boards"
577
+ },
578
+ stack: {
579
+ icon: StackCompact.StackCompactIcon,
580
+ label: "Show as list"
581
+ }
582
+ };
583
+
584
+ function LayoutToggle({layout: layout, onChange: onChange}) {
585
+ /* @__PURE__ */
586
+ return jsxRuntime.jsx(ui.Flex, {
587
+ align: "center",
588
+ gap: 1,
589
+ children: BOARD_LAYOUTS.map(value => {
590
+ const {icon: icon, label: label} = LAYOUT_LABELS[value];
591
+ /* @__PURE__ */
592
+ return jsxRuntime.jsx(index.HoverHint, {
593
+ text: label,
594
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, {
595
+ "aria-label": label,
596
+ "aria-pressed": value === layout,
597
+ fontSize: 1,
598
+ icon: icon,
599
+ mode: "bleed",
600
+ onClick: () => onChange(value),
601
+ padding: 2,
602
+ selected: value === layout
603
+ })
604
+ }, value);
605
+ })
606
+ });
607
+ }
608
+
609
+ function DefinitionLink({name: name, onOpen: onOpen}) {
610
+ const {resolvePathFromState: resolvePathFromState} = router.useRouter();
611
+ /* @__PURE__ */
612
+ return jsxRuntime.jsx(ui.Button, {
613
+ as: "a",
614
+ fontSize: 1,
615
+ href: resolvePathFromState({
616
+ definitionName: name
617
+ }),
618
+ mode: "bleed",
619
+ onClick: event => {
620
+ isPlainClick(event) && (event.preventDefault(), onOpen(name));
621
+ },
622
+ padding: 2,
623
+ text: "View definition"
624
+ });
625
+ }
626
+
627
+ function BoardControlsPortal({definitionName: definitionName, layout: layout, onChangeLayout: onChangeLayout, face: face, onOpenDefinition: onOpenDefinition, onSelectWorkflow: onSelectWorkflow, options: options, slot: slot}) {
628
+ return slot ? reactDom.createPortal(
629
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, {
630
+ align: "center",
631
+ gap: 2,
632
+ children: [
633
+ /* @__PURE__ */ jsxRuntime.jsx(WorkflowPicker, {
634
+ face: face,
635
+ onSelect: onSelectWorkflow,
636
+ options: options
637
+ }), definitionName === void 0 ? null : /* @__PURE__ */ jsxRuntime.jsx(DefinitionLink, {
638
+ name: definitionName,
639
+ onOpen: onOpenDefinition
640
+ }),
641
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
642
+ flex: 1
643
+ }),
644
+ /* @__PURE__ */ jsxRuntime.jsx(LayoutToggle, {
645
+ layout: layout,
646
+ onChange: onChangeLayout
647
+ }) ]
648
+ }), slot) : null;
649
+ }
650
+
651
+ function boardWorkflowOptions(definitions) {
652
+ return definitions.map(definition => ({
653
+ name: definition.name,
654
+ title: definition.title
655
+ })).sort(byTitleThenName);
656
+ }
657
+
658
+ function boardLandingName(selection) {
659
+ if (!(selection.kind !== "resolved" || selection.via !== "default")) return selection.definition.name;
660
+ }
661
+
662
+ function boardPickerFace(args) {
663
+ const {selection: selection, routeName: routeName} = args;
664
+ if (selection.kind === "resolved") {
665
+ const {name: name, title: title} = selection.definition;
666
+ return {
667
+ kind: "named",
668
+ name: name,
669
+ title: title
670
+ };
671
+ }
672
+ return selection.kind === "loading" && routeName !== void 0 ? {
673
+ kind: "loading"
674
+ } : {
675
+ kind: "none"
676
+ };
677
+ }
678
+
679
+ function boardSelectedPayload(selection) {
680
+ if (selection.kind === "resolved") return {
681
+ via: selection.via,
682
+ ...index.definitionFingerprint(selection.definition)
683
+ };
684
+ }
685
+
686
+ function resolveBoardSelection(args) {
687
+ const {definitions: definitions, routeName: routeName} = args;
688
+ if (definitions === void 0) return {
689
+ kind: "loading"
690
+ };
691
+ if (routeName !== void 0) {
692
+ const named = definitions.find(definition => definition.name === routeName);
693
+ return named ? {
694
+ kind: "resolved",
695
+ definition: named,
696
+ via: "address"
697
+ } : {
698
+ kind: "unknown",
699
+ name: routeName
700
+ };
701
+ }
702
+ const [landing] = [ ...definitions ].sort(byTitleThenName);
703
+ return landing ? {
704
+ kind: "resolved",
705
+ definition: landing,
706
+ via: "default"
707
+ } : {
708
+ kind: "empty"
709
+ };
710
+ }
711
+
712
+ function StageBand({bands: bands, group: group}) {
713
+ const {open: open, toggle: toggle} = useBandOpen(bands, group.stage);
714
+ /* @__PURE__ */
715
+ return jsxRuntime.jsx(index.CollapsibleBand, {
716
+ background: !0,
717
+ onToggle: toggle,
718
+ open: open,
719
+ sticky: !0,
720
+ title: group.title,
721
+ header: /* @__PURE__ */ jsxRuntime.jsx(StageGroupHeading, {
722
+ group: group
723
+ }),
724
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Stack, {
725
+ gap: 1,
726
+ paddingLeft: 5,
727
+ children: group.cards.length === 0 ?
728
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
729
+ padding: 3,
730
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
731
+ muted: !0,
732
+ size: 1,
733
+ children: BOARD_EMPTY_STAGE_NOTE
734
+ })
735
+ }) : group.cards.map(card => /* @__PURE__ */ jsxRuntime.jsx(BoardCard, {
736
+ card: card,
737
+ size: "slim"
738
+ }, card.instanceId))
739
+ })
740
+ });
741
+ }
742
+
743
+ function BoardStack({bands: bands, board: board}) {
744
+ /* @__PURE__ */
745
+ return jsxRuntime.jsx(ui.Stack, {
746
+ gap: 1,
747
+ paddingBottom: 4,
748
+ paddingTop: 3,
749
+ paddingX: TOOL_PANEL_PADDING,
750
+ children: board.groups.map(group => /* @__PURE__ */ jsxRuntime.jsx(StageBand, {
751
+ bands: bands,
752
+ group: group
753
+ }, group.stage))
754
+ });
755
+ }
756
+
757
+ const ALIAS_PREFIX = "__seed_", MAX_PATH_DEPTH = 3, IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
758
+
759
+ function previewSelectOf(schemaType) {
760
+ const select = schemaType.preview?.select;
761
+ if (!(select === void 0 || Object.keys(select).length === 0)) return select;
762
+ }
763
+
764
+ function fieldTypeOf(parent, name) {
765
+ if (!(!("fields" in parent) || !Array.isArray(parent.fields))) return parent.fields.find(field => field.name === name)?.type;
766
+ }
767
+
768
+ function stepOf(args) {
769
+ const {parent: parent, segment: segment, terminal: terminal} = args, fieldType = fieldTypeOf(parent, segment);
770
+ if (fieldType !== void 0) {
771
+ if (terminal) return {
772
+ part: segment,
773
+ next: fieldType
774
+ };
775
+ if (types.isReferenceSchemaType(fieldType)) {
776
+ const target = fieldType.to.length === 1 ? fieldType.to[0] : void 0;
777
+ return target === void 0 ? void 0 : {
778
+ part: `${segment}->`,
779
+ next: target
780
+ };
781
+ }
782
+ return {
783
+ part: `${segment}.`,
784
+ next: fieldType
785
+ };
786
+ }
787
+ }
788
+
789
+ function compilePath(root, path) {
790
+ const segments = path.split(".");
791
+ if (segments.length > MAX_PATH_DEPTH || segments.some(s => !IDENTIFIER.test(s))) return;
792
+ let parent = root;
793
+ const parts = [];
794
+ for (const [index2, segment] of segments.entries()) {
795
+ const step = stepOf({
796
+ parent: parent,
797
+ segment: segment,
798
+ terminal: index2 === segments.length - 1
799
+ });
800
+ if (step === void 0) return;
801
+ parts.push(step.part), parent = step.next;
802
+ }
803
+ return parts.join("");
804
+ }
805
+
806
+ function selectArmOf(schemaType) {
807
+ const select = previewSelectOf(schemaType);
808
+ if (select === void 0) return;
809
+ const entries = [];
810
+ for (const [key, path] of Object.entries(select)) {
811
+ const compiled = compilePath(schemaType, path);
812
+ if (compiled === void 0) return;
813
+ entries.push(`${JSON.stringify(ALIAS_PREFIX + key)}: ${compiled}`);
814
+ }
815
+ return `_type == ${JSON.stringify(schemaType.name)} => {${entries.join(", ")}}`;
816
+ }
817
+
818
+ function planTitleSeed(types2) {
819
+ const seedable = /* @__PURE__ */ new Map, arms = [];
820
+ for (const type of types2) {
821
+ const arm = selectArmOf(type);
822
+ arm !== void 0 && (seedable.set(type.name, type), arms.push(arm));
823
+ }
824
+ return {
825
+ query: `*[_id in $ids]{_id, _type${arms.length > 0 ? `, ...select(${arms.join(", ")})` : ""}}`,
826
+ seedable: seedable
827
+ };
828
+ }
829
+
830
+ function isSeedRow(value) {
831
+ if (typeof value != "object" || value === null) return !1;
832
+ const row = value;
833
+ return typeof row._id == "string" && typeof row._type == "string";
834
+ }
835
+
836
+ function seededTitleOf(row, schemaType) {
837
+ const select = previewSelectOf(schemaType);
838
+ if (select === void 0) return;
839
+ const selected = {};
840
+ for (const key of Object.keys(select)) selected[key] = row[ALIAS_PREFIX + key];
841
+ const prepare = schemaType.preview?.prepare;
842
+ let title;
843
+ if (prepare === void 0) title = selected.title; else try {
844
+ title = prepare(selected).title;
845
+ } catch (err) {
846
+ console.error(`[workflow-studio-plugin] preview prepare for "${schemaType.name}" failed on seed:`, err);
847
+ return;
848
+ }
849
+ return typeof title == "string" ? title : void 0;
850
+ }
851
+
852
+ function seedFromRows(args) {
853
+ const rows = args.rows.filter(isSeedRow), byBareId = new Map(rows.map(row => [ row._id, row ])), types2 = new Map(rows.map(row => [ row._id, row._type ])), titles = /* @__PURE__ */ new Map, resolved = /* @__PURE__ */ new Set;
854
+ for (const {gdrId: gdrId, bareId: bareId} of args.docs) {
855
+ const row = byBareId.get(bareId);
856
+ if (row === void 0) continue;
857
+ const schemaType = args.seedable.get(row._type);
858
+ if (schemaType === void 0) continue;
859
+ resolved.add(gdrId);
860
+ const title = seededTitleOf(row, schemaType);
861
+ title !== void 0 && titles.set(gdrId, title);
862
+ }
863
+ return {
864
+ types: types2,
865
+ titles: {
866
+ titles: titles,
867
+ resolved: resolved
868
+ }
869
+ };
870
+ }
871
+
872
+ function titleDrift(seed, live) {
873
+ const drifted = [];
874
+ for (const gdrId of seed.resolved) {
875
+ if (!live.resolved.has(gdrId)) continue;
876
+ const seeded = seed.titles.get(gdrId), observed = live.titles.get(gdrId);
877
+ seeded !== observed && drifted.push({
878
+ gdrId: gdrId,
879
+ seeded: seeded,
880
+ observed: observed
881
+ });
882
+ }
883
+ return drifted;
884
+ }
885
+
886
+ function mergeSeededTitles(live, seed) {
887
+ if (seed.resolved.size === 0) return live;
888
+ const titles = new Map(live.titles), resolved = new Set(live.resolved);
889
+ for (const gdrId of seed.resolved) {
890
+ if (live.resolved.has(gdrId)) continue;
891
+ resolved.add(gdrId);
892
+ const title = seed.titles.get(gdrId);
893
+ title !== void 0 && titles.set(gdrId, title);
894
+ }
895
+ return {
896
+ titles: titles,
897
+ resolved: resolved
898
+ };
899
+ }
900
+
901
+ const NO_TITLES = {
902
+ titles: /* @__PURE__ */ new Map,
903
+ resolved: /* @__PURE__ */ new Set
904
+ };
905
+
906
+ function useDocPreviewTitles(documents) {
907
+ const previewStore = sanity.useDocumentPreviewStore(), schema = sanity.useSchema(), {perspectiveStack: perspectiveStack} = sanity.usePerspective(), {actualTypes: actualTypes, binding: binding} = index.useWorkflowContext(), [state, setState] = react.useState(NO_TITLES), key = documents.map(doc => doc.id).join("|");
908
+ return react.useEffect(() => {
909
+ const titles = /* @__PURE__ */ new Map, resolved = /* @__PURE__ */ new Set, previews = /* @__PURE__ */ new Map, publish = () => setState({
910
+ titles: new Map(titles),
911
+ resolved: new Set(resolved)
912
+ }), localDocs = documents.flatMap(doc => {
913
+ const {parsed: parsed, local: local} = index.gdrLocality(doc.id, binding.contentResource);
914
+ return !parsed || !local ? (resolved.add(doc.id), []) : [ {
915
+ doc: doc,
916
+ bareId: parsed.documentId
917
+ } ];
918
+ }), untracks = localDocs.map(({bareId: bareId}) => actualTypes.track(bareId)), dropPreview = docId => {
919
+ previews.get(docId)?.unsubscribe(), previews.delete(docId), titles.delete(docId);
920
+ }, observe = ({docId: docId, bareId: bareId, typeName: typeName}) => {
921
+ const schemaType = index.openableSchemaType(schema, typeName);
922
+ if (!schemaType) return !1;
923
+ if (previews.get(docId)?.typeName === typeName) return !0;
924
+ dropPreview(docId), resolved.delete(docId);
925
+ const subscription = previewStore.observeForPreview({
926
+ _id: bareId,
927
+ _type: schemaType.name
928
+ }, schemaType, {
929
+ perspective: perspectiveStack
930
+ }).subscribe({
931
+ next: event => {
932
+ resolved.add(docId);
933
+ const title = event.snapshot?.title;
934
+ typeof title == "string" ? titles.set(docId, title) : titles.delete(docId), publish();
935
+ },
936
+ error: err => {
937
+ console.error(`[workflow-studio-plugin] preview for "${docId}" failed:`, err), resolved.add(docId),
938
+ publish();
939
+ }
940
+ });
941
+ return previews.set(docId, {
942
+ typeName: typeName,
943
+ unsubscribe: () => subscription.unsubscribe()
944
+ }), !0;
945
+ }, evaluate = () => {
946
+ for (const {doc: doc, bareId: bareId} of localDocs) {
947
+ const probe = actualTypes.read(bareId);
948
+ if (probe === null) {
949
+ dropPreview(doc.id), resolved.add(doc.id);
950
+ continue;
951
+ }
952
+ !observe({
953
+ docId: doc.id,
954
+ bareId: bareId,
955
+ typeName: probe ?? doc.type
956
+ }) && typeof probe == "string" && (dropPreview(doc.id), resolved.add(doc.id));
957
+ }
958
+ publish();
959
+ }, unsubscribeProbes = actualTypes.subscribe(evaluate);
960
+ return evaluate(), () => {
961
+ unsubscribeProbes(), untracks.forEach(untrack => untrack()), previews.forEach(preview => preview.unsubscribe());
962
+ };
963
+ }, [ key, previewStore, schema, actualTypes, binding.contentResource.id, perspectiveStack.join("|") ]),
964
+ state;
965
+ }
966
+
967
+ function usePreviewSeed(documents) {
968
+ const client = sanity.useClient({
969
+ apiVersion: index.WORKFLOW_API_VERSION
970
+ }), schema = sanity.useSchema(), {perspectiveStack: perspectiveStack} = sanity.usePerspective(), {actualTypes: actualTypes, binding: binding} = index.useWorkflowContext(), [seed, setSeed] = react.useState(NO_TITLES), docs = react.useMemo(() => documents.flatMap(doc => {
971
+ const {parsed: parsed, local: local} = index.gdrLocality(doc.id, binding.contentResource);
972
+ return parsed === void 0 || !local ? [] : [ {
973
+ gdrId: doc.id,
974
+ bareId: parsed.documentId
975
+ } ];
976
+ }), [ documents, binding.contentResource ]), key = docs.map(doc => doc.gdrId).join("|");
977
+ return react.useEffect(() => {
978
+ if (setSeed(NO_TITLES), key === "") return;
979
+ const plan = planTitleSeed(schema.getTypeNames().flatMap(name => index.openableSchemaType(schema, name) ?? []));
980
+ let cancelled = !1;
981
+ return client.fetch(plan.query, {
982
+ ids: docs.map(doc => doc.bareId)
983
+ }, {
984
+ tag: "workflows.preview-seed",
985
+ ...perspectiveStack.length > 0 ? {
986
+ perspective: [ ...perspectiveStack ]
987
+ } : {}
988
+ }).then(rows => {
989
+ if (cancelled) return;
990
+ const result = seedFromRows({
991
+ rows: rows,
992
+ docs: docs,
993
+ seedable: plan.seedable
994
+ });
995
+ actualTypes.seed(result.types), setSeed(result.titles);
996
+ }).catch(err => {
997
+ cancelled || console.error("[workflow-studio-plugin] preview seed failed:", err);
998
+ }), () => {
999
+ cancelled = !0;
1000
+ };
1001
+ }, [ key, client, schema, actualTypes, binding.contentResource.id, perspectiveStack.join("|") ]),
1002
+ seed;
1003
+ }
1004
+
1005
+ function useSeededTitles(args) {
1006
+ const {ordering: ordering, warm: warm} = args, seed = usePreviewSeed(warm), live = useDocPreviewTitles(ordering), telemetry = workflowReact.useWorkflowTelemetry(), warned = react.useRef(/* @__PURE__ */ new Set);
1007
+ return react.useEffect(() => {
1008
+ const fresh = titleDrift(seed, live).filter(drift => !warned.current.has(drift.gdrId));
1009
+ if (fresh.length !== 0) {
1010
+ warned.current.size === 0 && telemetry.log(index.WorkflowTitleSeedDrifted, {
1011
+ driftCount: fresh.length
1012
+ });
1013
+ for (const drift of fresh) warned.current.add(drift.gdrId), console.warn(`[workflow-studio-plugin] seeded preview title for "${drift.gdrId}" diverged from the live pipeline — the seed projection or prepare mirroring is drifting from Studio preview resolution`);
1014
+ }
1015
+ }, [ seed, live, telemetry ]), react.useMemo(() => {
1016
+ const merged = mergeSeededTitles(live, seed);
1017
+ return {
1018
+ ...merged,
1019
+ ready: ordering.every(doc => merged.resolved.has(doc.id))
1020
+ };
1021
+ }, [ ordering, live, seed ]);
1022
+ }
1023
+
1024
+ function useGroupTitles(groups) {
1025
+ const ordering = react.useMemo(() => groups.withDocuments.map(group => group.document), [ groups ]), warm = react.useMemo(() => groups.withDocuments.flatMap(group => group.documents), [ groups ]);
1026
+ return useSeededTitles({
1027
+ ordering: ordering,
1028
+ warm: warm
1029
+ });
1030
+ }
1031
+
1032
+ const NO_DOCUMENTS = [];
1033
+
1034
+ function useBoard(args) {
1035
+ const {definition: definition, instances: instances} = args, derived = react.useMemo(() => definition ? deriveBoard({
1036
+ instances: instances,
1037
+ definition: definition
1038
+ }) : void 0, [ definition, instances ]), documents = react.useMemo(() => derived ? boardDocuments(derived) : NO_DOCUMENTS, [ derived ]), {titles: titles, ready: ready} = useSeededTitles({
1039
+ ordering: documents,
1040
+ warm: documents
1041
+ }), board = react.useMemo(() => derived ? orderBoardByPreviewTitle(derived, titles) : void 0, [ derived, titles ]);
1042
+ return react.useMemo(() => ({
1043
+ board: board,
1044
+ ready: ready
1045
+ }), [ board, ready ]);
1046
+ }
1047
+
1048
+ const INSTANCE_CAP = 200;
1049
+
1050
+ function useCappedInstances(filter) {
1051
+ const {engine: engine} = index.useWorkflowContext(), capped = react.useMemo(() => ({
1052
+ ...filter,
1053
+ limit: INSTANCE_CAP
1054
+ }), [ filter ]), {instances: instances, loading: loading, unreadable: unreadable} = workflowStudio.useWorkflowInstances({
1055
+ engine: engine,
1056
+ filter: capped
1057
+ });
1058
+ return react.useMemo(() => {
1059
+ const rows = instances ?? [];
1060
+ return {
1061
+ rows: rows,
1062
+ truncated: rows.length + unreadable.length === INSTANCE_CAP,
1063
+ loading: loading,
1064
+ unreadable: unreadable
1065
+ };
1066
+ }, [ instances, loading, unreadable ]);
1067
+ }
1068
+
1069
+ const BOARD_CAP_NOTE = `Showing the latest ${INSTANCE_CAP} workflows — older ones aren’t on the board.`, NOTHING = {
1070
+ ids: []
1071
+ };
1072
+
1073
+ function useBoardInstances(definitionName) {
1074
+ const filter = react.useMemo(() => definitionName === void 0 ? NOTHING : {
1075
+ definition: definitionName,
1076
+ includeCompleted: !0
1077
+ }, [ definitionName ]);
1078
+ return useCappedInstances(filter);
1079
+ }
1080
+
1081
+ function useBoardLayout(scope) {
1082
+ const store = react.useMemo(() => createBoardLayoutStore({
1083
+ storageKey: boardLayoutStorageKey(scope)
1084
+ }), [ scope ]), [held, setHeld] = react.useState(() => ({
1085
+ store: store,
1086
+ layout: store.read()
1087
+ }));
1088
+ return held.store !== store && setHeld({
1089
+ store: store,
1090
+ layout: store.read()
1091
+ }), {
1092
+ layout: held.layout,
1093
+ setLayout: layout => setHeld({
1094
+ store: store,
1095
+ layout: store.set(layout)
1096
+ })
1097
+ };
1098
+ }
1099
+
1100
+ function DocumentsTab({filterSlot: filterSlot, onOpenDefinition: onOpenDefinition, onSelectWorkflow: onSelectWorkflow, routeDefinition: routeDefinition}) {
1101
+ const {binding: binding} = index.useWorkflowContext(), telemetry = workflowReact.useWorkflowTelemetry(), {definitions: definitions, error: error} = useDeployedDefinitions(), {layout: layout, setLayout: setLayout} = useBoardLayout(binding.engineResource.id), bands = useBandOpenState({
1102
+ segment: "documents",
1103
+ scope: binding.engineResource.id
1104
+ }), selection = resolveBoardSelection({
1105
+ definitions: definitions,
1106
+ routeName: routeDefinition
1107
+ }), selected = selection.kind === "resolved" ? selection.definition : void 0, instances = useBoardInstances(selected?.name), {board: board, ready: ready} = useBoard({
1108
+ definition: selected,
1109
+ instances: instances.rows
1110
+ }), revealed = useRevealGate({
1111
+ loading: instances.loading,
1112
+ ready: ready
1113
+ }), landing = boardLandingName(selection);
1114
+ react.useEffect(() => {
1115
+ landing !== void 0 && onSelectWorkflow(landing, {
1116
+ replace: !0
1117
+ });
1118
+ }, [ landing, onSelectWorkflow ]);
1119
+ const selectedPayload = boardSelectedPayload(selection);
1120
+ /* @__PURE__ */
1121
+ return jsxRuntime.jsxs(ui.Flex, {
1122
+ direction: "column",
1123
+ flex: 1,
1124
+ style: {
1125
+ minHeight: 0
1126
+ },
1127
+ children: [
1128
+ /* @__PURE__ */ jsxRuntime.jsx(BoardControlsPortal, {
1129
+ definitionName: selected?.name,
1130
+ layout: layout,
1131
+ onOpenDefinition: onOpenDefinition,
1132
+ onChangeLayout: next => {
1133
+ next !== layout && (telemetry.log(index.WorkflowBoardLayoutChanged, {
1134
+ layout: next
1135
+ }), setLayout(next));
1136
+ },
1137
+ onSelectWorkflow: onSelectWorkflow,
1138
+ options: boardWorkflowOptions(definitions ?? []),
1139
+ face: boardPickerFace({
1140
+ selection: selection,
1141
+ routeName: routeDefinition
1142
+ }),
1143
+ slot: filterSlot
1144
+ }),
1145
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, {
1146
+ direction: "column",
1147
+ flex: 1,
1148
+ gap: 2,
1149
+ style: {
1150
+ minHeight: 0
1151
+ },
1152
+ children: [ instances.unreadable.length === 0 ? null : /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
1153
+ paddingX: TOOL_PANEL_PADDING,
1154
+ children: /* @__PURE__ */ jsxRuntime.jsx(index.UnreadableDocsNote, {
1155
+ unreadable: instances.unreadable
1156
+ })
1157
+ }),
1158
+ /* @__PURE__ */ jsxRuntime.jsx(BoardBody, {
1159
+ bands: bands,
1160
+ board: board,
1161
+ error: error,
1162
+ layout: layout,
1163
+ loading: !revealed,
1164
+ selection: selection
1165
+ }), instances.truncated ? /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
1166
+ paddingX: TOOL_PANEL_PADDING,
1167
+ children: /* @__PURE__ */ jsxRuntime.jsx(MutedNote, {
1168
+ text: BOARD_CAP_NOTE
1169
+ })
1170
+ }) : null ]
1171
+ }), selectedPayload === void 0 ? null :
1172
+ /* @__PURE__ */ jsxRuntime.jsx(index.LogEventOnMount, {
1173
+ data: selectedPayload,
1174
+ event: index.WorkflowBoardWorkflowSelected
1175
+ }, selected?.name) ]
1176
+ });
1177
+ }
1178
+
1179
+ function BoardBody({bands: bands, board: board, error: error, layout: layout, loading: loading, selection: selection}) {
1180
+ return error !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(StatusBox, {
1181
+ children: /* @__PURE__ */ jsxRuntime.jsx(MutedNote, {
1182
+ text: `Could not load the deployed workflows: ${error}`
1183
+ })
1184
+ }) : selection.kind === "loading" ? /* @__PURE__ */ jsxRuntime.jsx(StatusBox, {
1185
+ children: /* @__PURE__ */ jsxRuntime.jsx(TabStatusRow, {
1186
+ children: /* @__PURE__ */ jsxRuntime.jsx(index.LoadingRow, {
1187
+ label: "Loading workflows…"
1188
+ })
1189
+ })
1190
+ }) : selection.kind === "empty" ? /* @__PURE__ */ jsxRuntime.jsx(StatusBox, {
1191
+ children: /* @__PURE__ */ jsxRuntime.jsx(index.EmptyState, {
1192
+ description: "Deploy a workflow to see its documents here",
1193
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Search.SearchIcon, {}),
1194
+ title: "No workflows deployed"
1195
+ })
1196
+ }) : selection.kind === "unknown" ? /* @__PURE__ */ jsxRuntime.jsx(StatusBox, {
1197
+ children: /* @__PURE__ */ jsxRuntime.jsx(index.EmptyState, {
1198
+ description: `No deployed workflow is named “${selection.name}” — pick one above`,
1199
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Search.SearchIcon, {}),
1200
+ title: "Workflow not found"
1201
+ })
1202
+ }) : loading || board === void 0 ? /* @__PURE__ */ jsxRuntime.jsx(StatusBox, {
1203
+ children: /* @__PURE__ */ jsxRuntime.jsx(TabStatusRow, {
1204
+ children: /* @__PURE__ */ jsxRuntime.jsx(index.LoadingRow, {
1205
+ label: "Loading documents…"
1206
+ })
1207
+ })
1208
+ }) : layout === "stack" ? /* @__PURE__ */ jsxRuntime.jsx(BoardStack, {
1209
+ bands: bands,
1210
+ board: board
1211
+ }) : /* @__PURE__ */ jsxRuntime.jsx(StageGroups, {
1212
+ board: board
1213
+ });
1214
+ }
1215
+
1216
+ function StatusBox({children: children}) {
1217
+ /* @__PURE__ */
1218
+ return jsxRuntime.jsx(ui.Flex, {
1219
+ direction: "column",
1220
+ flex: 1,
1221
+ paddingX: TOOL_PANEL_PADDING,
1222
+ children: children
1223
+ });
1224
+ }
1225
+
1226
+ function definitionCatalog(args) {
1227
+ const rejected = [ ...args.issues.values() ];
1228
+ return args.definitions.map(definition => ({
1229
+ name: definition.name,
1230
+ title: definition.title ?? definition.name,
1231
+ description: definition.description,
1232
+ activeCount: args.inFlight.filter(instance => instance.definition === definition.name).length,
1233
+ docTypes: [ ...new Set(args.mappings.filter(mapping => mapping.definition === definition.name).map(mapping => mapping.docType)) ],
1234
+ brokenBindings: rejected.filter(issue => issue.definition === definition.name && issue.version === definition.version).map(issue => ({
1235
+ docType: issue.docType,
1236
+ detail: index.mappingIssueDetail(issue)
1237
+ })).sort((a, b) => a.docType.localeCompare(b.docType))
1238
+ })).sort(byTitleThenName);
1239
+ }
1240
+
1241
+ function undeployedWorkflowNotices(args) {
1242
+ return [ ...args.issues.values() ].filter(index.namesNoDeployedDefinition).sort((a, b) => a.definition.localeCompare(b.definition) || a.docType.localeCompare(b.docType)).map(issue => `“${issue.definition}” is set up to run on ${args.titleOf(issue.docType) ?? issue.docType} documents, but no deployed workflow has that name`);
1243
+ }
1244
+
1245
+ function docTypeLabels(docTypes, titleOf) {
1246
+ return docTypes.map(name => ({
1247
+ name: name,
1248
+ title: titleOf(name) ?? name
1249
+ })).sort(byTitleThenName);
1250
+ }
1251
+
1252
+ function startPhrase(definition) {
1253
+ if (definition.lifecycle === "child") return "Started by a parent workflow";
1254
+ if (workflowEngine.startKindOf(definition) === "autonomous") return "Starts automatically";
1255
+ const subject = (definition.fields ?? []).find(workflowEngine.isSubjectEntry);
1256
+ return subject !== void 0 && workflowEngine.isInputSourced(subject) ? "Started manually, from a document" : "Started manually";
1257
+ }
1258
+
1259
+ function deployedAtOf(definition) {
1260
+ return typeof definition._updatedAt == "string" ? definition._updatedAt : void 0;
1261
+ }
1262
+
1263
+ function deployedPhrase(definition) {
1264
+ const deployedAt = deployedAtOf(definition);
1265
+ if (deployedAt === void 0) return `v${definition.version}`;
1266
+ const ago = index.formatTimeAgo(deployedAt);
1267
+ return ago === "" ? index.formatShortDateTime(deployedAt) : `${index.formatShortDateTime(deployedAt)} (${ago})`;
1268
+ }
1269
+
1270
+ function DetailNotFound({back: back, message: message}) {
1271
+ /* @__PURE__ */
1272
+ return jsxRuntime.jsxs(ui.Stack, {
1273
+ gap: 4,
1274
+ children: [
1275
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
1276
+ muted: !0,
1277
+ size: 1,
1278
+ children: message
1279
+ }),
1280
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, {
1281
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, {
1282
+ fontSize: 1,
1283
+ gap: 2,
1284
+ icon: ArrowLeft.ArrowLeftIcon,
1285
+ mode: "ghost",
1286
+ onClick: back.go,
1287
+ padding: 2,
1288
+ text: back.label
1289
+ })
1290
+ }) ]
1291
+ });
1292
+ }
1293
+
1294
+ function DetailTitle({busy: busy = !1, documentGdr: documentGdr, title: title}) {
1295
+ const chipPullback = -index.useSpaceToken(1);
1296
+ /* @__PURE__ */
1297
+ return jsxRuntime.jsxs(ui.Flex, {
1298
+ align: "center",
1299
+ gap: 2,
1300
+ paddingLeft: 2,
1301
+ children: [
1302
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
1303
+ style: {
1304
+ minWidth: 0
1305
+ },
1306
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
1307
+ size: 1,
1308
+ textOverflow: "ellipsis",
1309
+ weight: "semibold",
1310
+ children: title
1311
+ })
1312
+ }), documentGdr === void 0 ? null : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, {
1313
+ children: [
1314
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
1315
+ muted: !0,
1316
+ size: 1,
1317
+ children: "in"
1318
+ }),
1319
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, {
1320
+ align: "center",
1321
+ flex: "none",
1322
+ style: {
1323
+ height: 0,
1324
+ marginLeft: chipPullback
1325
+ },
1326
+ children: /* @__PURE__ */ jsxRuntime.jsx(index.DocPreviewLink, {
1327
+ gdr: documentGdr,
1328
+ layout: "inline",
1329
+ source: "detail-title"
1330
+ })
1331
+ }) ]
1332
+ }),
1333
+ /* @__PURE__ */ jsxRuntime.jsx(index.SpinnerSlot, {
1334
+ busy: busy
1335
+ }) ]
1336
+ });
1337
+ }
1338
+
1339
+ const INSTANCE_CAP_COUNT_NOTE = `Counts from the latest ${INSTANCE_CAP} workflows — older ones aren’t counted.`, NEWEST_INSTANCES = {
1340
+ includeCompleted: !0
1341
+ };
1342
+
1343
+ function useToolInstances() {
1344
+ const {rows: rows, truncated: truncated, loading: loading, unreadable: unreadable} = useCappedInstances(NEWEST_INSTANCES);
1345
+ return react.useMemo(() => ({
1346
+ inFlight: rows.filter(instance => workflowEngine.terminalState(instance) === "in-flight"),
1347
+ settled: rows.filter(instance => workflowEngine.terminalState(instance) !== "in-flight"),
1348
+ truncated: truncated,
1349
+ loading: loading,
1350
+ unreadable: unreadable
1351
+ }), [ rows, truncated, loading, unreadable ]);
1352
+ }
1353
+
1354
+ function DefinitionDetail({definitionName: definitionName, backToList: backToList}) {
1355
+ const {definitions: definitions, error: error} = useDeployedDefinitions(), definition = definitions?.find(row => row.name === definitionName), loading = definitions === void 0 && error === void 0, viewed = loading || error !== void 0 ? null : /* @__PURE__ */ jsxRuntime.jsx(index.LogEventOnMount, {
1356
+ data: {
1357
+ found: definition !== void 0,
1358
+ ...index.definitionFingerprint(definition)
1359
+ },
1360
+ event: index.WorkflowDefinitionDetailViewed
1361
+ });
1362
+ return !loading && error === void 0 && definition === void 0 ? /* @__PURE__ */ jsxRuntime.jsxs(ui.Box, {
1363
+ paddingX: 3,
1364
+ children: [ viewed,
1365
+ /* @__PURE__ */ jsxRuntime.jsx(TabStatusRow, {
1366
+ children: /* @__PURE__ */ jsxRuntime.jsx(DetailNotFound, {
1367
+ back: backToList,
1368
+ message: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, {
1369
+ children: [ "Unable to find a definition with name “",
1370
+ /* @__PURE__ */ jsxRuntime.jsx("strong", {
1371
+ children: definitionName
1372
+ }), "”." ]
1373
+ })
1374
+ })
1375
+ }) ]
1376
+ }) : /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, {
1377
+ direction: "column",
1378
+ height: "fill",
1379
+ overflow: "hidden",
1380
+ children: [ viewed,
1381
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
1382
+ flex: "none",
1383
+ paddingBottom: 2,
1384
+ paddingTop: 3,
1385
+ paddingX: 3,
1386
+ children: loading ? /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
1387
+ paddingLeft: 2,
1388
+ children: /* @__PURE__ */ jsxRuntime.jsx(index.LoadingRow, {
1389
+ label: "Loading definition…"
1390
+ })
1391
+ }) : /* @__PURE__ */ jsxRuntime.jsx(DetailTitle, {
1392
+ title: definition?.title ?? definitionName
1393
+ })
1394
+ }),
1395
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
1396
+ flex: 1,
1397
+ overflow: "auto",
1398
+ paddingX: 3,
1399
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, {
1400
+ direction: "column",
1401
+ paddingBottom: 5,
1402
+ paddingLeft: 2,
1403
+ paddingTop: 4,
1404
+ style: {
1405
+ boxSizing: "border-box",
1406
+ minHeight: "100%"
1407
+ },
1408
+ children: /* @__PURE__ */ jsxRuntime.jsx(DefinitionBody, {
1409
+ definition: definition,
1410
+ error: error
1411
+ })
1412
+ })
1413
+ }) ]
1414
+ });
1415
+ }
1416
+
1417
+ function DefinitionBody({definition: definition, error: error}) {
1418
+ return error !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(MutedNote, {
1419
+ text: `Could not load the deployed workflows: ${error}`
1420
+ }) : definition === void 0 ? null : /* @__PURE__ */ jsxRuntime.jsx(DefinitionFacts, {
1421
+ definition: definition
1422
+ });
1423
+ }
1424
+
1425
+ function SetupIssues({bindings: bindings}) {
1426
+ const schema = sanity.useSchema();
1427
+ return bindings.length === 0 ? null : /* @__PURE__ */ jsxRuntime.jsx(ui.Card, {
1428
+ padding: 3,
1429
+ radius: 3,
1430
+ tone: "caution",
1431
+ children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, {
1432
+ align: "flex-start",
1433
+ gap: 3,
1434
+ children: [
1435
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
1436
+ size: 1,
1437
+ children: /* @__PURE__ */ jsxRuntime.jsx(WarningOutline.WarningOutlineIcon, {})
1438
+ }),
1439
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, {
1440
+ flex: 1,
1441
+ gap: 3,
1442
+ children: [
1443
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
1444
+ size: 1,
1445
+ weight: "semibold",
1446
+ children: "Setup issue"
1447
+ }), bindings.map(binding => /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
1448
+ size: 1,
1449
+ children: `${schema.get(binding.docType)?.title ?? binding.docType}: ${binding.detail}.`
1450
+ }, binding.docType)),
1451
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
1452
+ muted: !0,
1453
+ size: 1,
1454
+ children: "Editors see this workflow disabled on these document types until a developer fixes it."
1455
+ }) ]
1456
+ }) ]
1457
+ })
1458
+ });
1459
+ }
1460
+
1461
+ function DefinitionFacts({definition: definition}) {
1462
+ const {mappingIssues: mappingIssues, mappings: mappings} = index.useWorkflowContext(), schema = sanity.useSchema(), instances = useToolInstances(), snapshotWidth = index.useContainerToken(2), [row] = definitionCatalog({
1463
+ definitions: [ definition ],
1464
+ mappings: mappings,
1465
+ inFlight: instances.inFlight,
1466
+ issues: mappingIssues
1467
+ }), runsOn = docTypeLabels(row?.docTypes ?? [], name => schema.get(name)?.title).map(docType => docType.title);
1468
+ /* @__PURE__ */
1469
+ return jsxRuntime.jsxs(ui.Flex, {
1470
+ direction: "column",
1471
+ flex: 1,
1472
+ gap: 4,
1473
+ children: [ row === void 0 || row.brokenBindings.length === 0 ? null : /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
1474
+ flex: "none",
1475
+ children: /* @__PURE__ */ jsxRuntime.jsx(SetupIssues, {
1476
+ bindings: row.brokenBindings
1477
+ })
1478
+ }),
1479
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
1480
+ flex: "none",
1481
+ style: {
1482
+ maxWidth: snapshotWidth
1483
+ },
1484
+ children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, {
1485
+ gap: 4,
1486
+ children: [
1487
+ /* @__PURE__ */ jsxRuntime.jsx(index.MetaRow, {
1488
+ label: "Deployed",
1489
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
1490
+ size: 1,
1491
+ children: deployedPhrase(definition)
1492
+ })
1493
+ }),
1494
+ /* @__PURE__ */ jsxRuntime.jsx(index.MetaRow, {
1495
+ label: "Active instances",
1496
+ children: instances.loading ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
1497
+ muted: !0,
1498
+ size: 1,
1499
+ children: "—"
1500
+ }) : /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, {
1501
+ gap: 2,
1502
+ children: [
1503
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
1504
+ size: 1,
1505
+ children: row?.activeCount ?? 0
1506
+ }), instances.truncated ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
1507
+ muted: !0,
1508
+ size: 1,
1509
+ children: INSTANCE_CAP_COUNT_NOTE
1510
+ }) : null ]
1511
+ })
1512
+ }),
1513
+ /* @__PURE__ */ jsxRuntime.jsx(index.MetaRow, {
1514
+ label: "Trigger",
1515
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
1516
+ size: 1,
1517
+ children: startPhrase(definition)
1518
+ })
1519
+ }),
1520
+ /* @__PURE__ */ jsxRuntime.jsx(index.MetaRow, {
1521
+ label: "Document types",
1522
+ children: runsOn.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
1523
+ muted: !0,
1524
+ size: 1,
1525
+ children: "No document types in this Studio"
1526
+ }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
1527
+ size: 1,
1528
+ children: runsOn.join(", ")
1529
+ })
1530
+ }),
1531
+ /* @__PURE__ */ jsxRuntime.jsx(index.MetaRow, {
1532
+ label: "Description",
1533
+ children: definition.description === void 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
1534
+ muted: !0,
1535
+ size: 1,
1536
+ children: /* @__PURE__ */ jsxRuntime.jsx("em", {
1537
+ children: "No description"
1538
+ })
1539
+ }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
1540
+ size: 1,
1541
+ style: {
1542
+ whiteSpace: "pre-wrap"
1543
+ },
1544
+ children: definition.description
1545
+ })
1546
+ }) ]
1547
+ })
1548
+ }),
1549
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, {
1550
+ direction: "column",
1551
+ flex: 1,
1552
+ style: {
1553
+ minHeight: 340
1554
+ },
1555
+ children: /* @__PURE__ */ jsxRuntime.jsx(index.MetaRow, {
1556
+ fillHeight: !0,
1557
+ label: "Stages",
1558
+ children: /* @__PURE__ */ jsxRuntime.jsx(workflowDiagram.WorkflowDiagram, {
1559
+ definition: definition,
1560
+ explain: !0,
1561
+ fill: !0,
1562
+ height: "100%"
1563
+ })
1564
+ })
1565
+ }) ]
1566
+ });
1567
+ }
1568
+
1569
+ const MAX_VISIBLE_DOC_TYPES = 4, COLUMNS = {
1570
+ definition: 6,
1571
+ instances: 2,
1572
+ docTypes: 2
1573
+ };
1574
+
1575
+ function Col({flex: flex, children: children}) {
1576
+ /* @__PURE__ */
1577
+ return jsxRuntime.jsx(ui.Box, {
1578
+ flex: flex,
1579
+ paddingRight: 3,
1580
+ children: children
1581
+ });
1582
+ }
1583
+
1584
+ function DefinitionsTab({onOpenDefinition: onOpenDefinition}) {
1585
+ const {definitions: definitions, error: error} = useDeployedDefinitions(), {mappingIssues: mappingIssues, mappings: mappings} = index.useWorkflowContext(), schema = sanity.useSchema(), instances = useToolInstances(), undeployed = /* @__PURE__ */ jsxRuntime.jsx(UndeployedWorkflows, {
1586
+ notices: undeployedWorkflowNotices({
1587
+ issues: mappingIssues,
1588
+ titleOf: name => schema.get(name)?.title
1589
+ })
1590
+ });
1591
+ if (error !== void 0) /* @__PURE__ */
1592
+ return jsxRuntime.jsx(TabStatusRow, {
1593
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
1594
+ muted: !0,
1595
+ size: 1,
1596
+ children: `Could not load the deployed workflows: ${error}`
1597
+ })
1598
+ });
1599
+ if (definitions === void 0) /* @__PURE__ */
1600
+ return jsxRuntime.jsx(TabStatusRow, {
1601
+ children: /* @__PURE__ */ jsxRuntime.jsx(index.LoadingRow, {
1602
+ label: "Loading definitions…"
1603
+ })
1604
+ });
1605
+ if (definitions.length === 0) /* @__PURE__ */
1606
+ return jsxRuntime.jsxs(ui.Flex, {
1607
+ direction: "column",
1608
+ flex: 1,
1609
+ gap: 3,
1610
+ paddingBottom: 4,
1611
+ paddingTop: 3,
1612
+ children: [ undeployed,
1613
+ /* @__PURE__ */ jsxRuntime.jsx(index.EmptyState, {
1614
+ description: "Workflow definitions deployed to this project will appear here",
1615
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Transfer.TransferIcon, {}),
1616
+ title: "No definitions deployed"
1617
+ }) ]
1618
+ });
1619
+ const rows = definitionCatalog({
1620
+ definitions: definitions,
1621
+ mappings: mappings,
1622
+ inFlight: instances.inFlight,
1623
+ issues: mappingIssues
1624
+ }), active = rows.reduce((sum, row) => sum + row.activeCount, 0);
1625
+ /* @__PURE__ */
1626
+ return jsxRuntime.jsxs(ui.Stack, {
1627
+ gap: 3,
1628
+ paddingBottom: 4,
1629
+ paddingTop: 3,
1630
+ children: [ undeployed,
1631
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
1632
+ paddingX: 2,
1633
+ children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, {
1634
+ align: "center",
1635
+ gap: 2,
1636
+ children: [
1637
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
1638
+ size: 1,
1639
+ weight: "semibold",
1640
+ children: pluralize__default.default("definition", rows.length, !0)
1641
+ }),
1642
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
1643
+ muted: !0,
1644
+ size: 1,
1645
+ children: instances.loading ? "counting active instances…" : activeSummary(active)
1646
+ }) ]
1647
+ })
1648
+ }),
1649
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, {
1650
+ gap: 2,
1651
+ children: [
1652
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Card, {
1653
+ paddingX: 2,
1654
+ paddingY: 2,
1655
+ style: {
1656
+ position: "sticky",
1657
+ top: 0,
1658
+ zIndex: 1
1659
+ },
1660
+ children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, {
1661
+ align: "center",
1662
+ gap: 4,
1663
+ children: [
1664
+ /* @__PURE__ */ jsxRuntime.jsx(Col, {
1665
+ flex: COLUMNS.definition,
1666
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
1667
+ muted: !0,
1668
+ size: 1,
1669
+ weight: "medium",
1670
+ children: "Definition"
1671
+ })
1672
+ }),
1673
+ /* @__PURE__ */ jsxRuntime.jsx(Col, {
1674
+ flex: COLUMNS.instances,
1675
+ children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, {
1676
+ align: "center",
1677
+ gap: 2,
1678
+ children: [
1679
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
1680
+ muted: !0,
1681
+ size: 1,
1682
+ weight: "medium",
1683
+ children: "Active instances"
1684
+ }),
1685
+ /* @__PURE__ */ jsxRuntime.jsx(index.HoverHint, {
1686
+ text: "Counts only active instances — completed and aborted workflows are not included",
1687
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
1688
+ muted: !0,
1689
+ size: 1,
1690
+ style: {
1691
+ opacity: .7
1692
+ },
1693
+ children: /* @__PURE__ */ jsxRuntime.jsx(InfoOutline.InfoOutlineIcon, {})
1694
+ })
1695
+ }) ]
1696
+ })
1697
+ }),
1698
+ /* @__PURE__ */ jsxRuntime.jsx(Col, {
1699
+ flex: COLUMNS.docTypes,
1700
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
1701
+ muted: !0,
1702
+ size: 1,
1703
+ weight: "medium",
1704
+ children: "Document types"
1705
+ })
1706
+ }) ]
1707
+ })
1708
+ }),
1709
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Stack, {
1710
+ gap: 1,
1711
+ children: rows.map(row => /* @__PURE__ */ jsxRuntime.jsx(DefinitionRow, {
1712
+ countPending: instances.loading,
1713
+ onOpen: () => onOpenDefinition(row.name),
1714
+ row: row
1715
+ }, row.name))
1716
+ }) ]
1717
+ }), instances.truncated ? /* @__PURE__ */ jsxRuntime.jsx(MutedNote, {
1718
+ text: INSTANCE_CAP_COUNT_NOTE
1719
+ }) : null ]
1720
+ });
1721
+ }
1722
+
1723
+ function activeSummary(active) {
1724
+ return `${pluralize__default.default("active instance", active, !0)}`;
1725
+ }
1726
+
1727
+ const ROW_ISSUES_HINT = "This definition has issues";
1728
+
1729
+ function UndeployedWorkflows({notices: notices}) {
1730
+ return notices.length === 0 ? null :
1731
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Stack, {
1732
+ gap: 2,
1733
+ paddingBottom: 1,
1734
+ children: notices.map(notice => /* @__PURE__ */ jsxRuntime.jsx(index.CautionNote, {
1735
+ label: notice
1736
+ }, notice))
1737
+ });
1738
+ }
1739
+
1740
+ function DefinitionRow({countPending: countPending, onOpen: onOpen, row: row}) {
1741
+ const flagged = row.brokenBindings.length > 0, card = /* @__PURE__ */ jsxRuntime.jsx(ui.Card, {
1742
+ as: "button",
1743
+ onClick: onOpen,
1744
+ paddingX: 2,
1745
+ paddingY: 3,
1746
+ radius: 2,
1747
+ style: {
1748
+ textAlign: "left",
1749
+ width: "100%"
1750
+ },
1751
+ ...flagged ? {
1752
+ tone: "caution"
1753
+ } : {},
1754
+ children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, {
1755
+ align: "flex-start",
1756
+ gap: 4,
1757
+ children: [
1758
+ /* @__PURE__ */ jsxRuntime.jsx(Col, {
1759
+ flex: COLUMNS.definition,
1760
+ children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, {
1761
+ align: "flex-start",
1762
+ gap: 3,
1763
+ paddingLeft: flagged ? 1 : 0,
1764
+ children: [ flagged ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
1765
+ size: 1,
1766
+ children: /* @__PURE__ */ jsxRuntime.jsx(WarningOutline.WarningOutlineIcon, {})
1767
+ }) : null,
1768
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, {
1769
+ flex: 1,
1770
+ gap: 3,
1771
+ style: {
1772
+ minWidth: 0
1773
+ },
1774
+ children: [
1775
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
1776
+ size: 1,
1777
+ weight: "semibold",
1778
+ children: row.title
1779
+ }), row.description === void 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
1780
+ muted: !0,
1781
+ size: 1,
1782
+ children: /* @__PURE__ */ jsxRuntime.jsx("em", {
1783
+ children: "No description"
1784
+ })
1785
+ }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
1786
+ muted: !0,
1787
+ size: 1,
1788
+ children: row.description
1789
+ }) ]
1790
+ }) ]
1791
+ })
1792
+ }),
1793
+ /* @__PURE__ */ jsxRuntime.jsx(Col, {
1794
+ flex: COLUMNS.instances,
1795
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
1796
+ muted: !0,
1797
+ size: 1,
1798
+ children: countPending ? "—" : `${row.activeCount} active`
1799
+ })
1800
+ }),
1801
+ /* @__PURE__ */ jsxRuntime.jsx(Col, {
1802
+ flex: COLUMNS.docTypes,
1803
+ children: /* @__PURE__ */ jsxRuntime.jsx(DocTypeChips, {
1804
+ docTypes: row.docTypes
1805
+ })
1806
+ }) ]
1807
+ })
1808
+ });
1809
+ return flagged ? /* @__PURE__ */ jsxRuntime.jsx(index.HoverHint, {
1810
+ fill: !0,
1811
+ text: ROW_ISSUES_HINT,
1812
+ children: card
1813
+ }) : card;
1814
+ }
1815
+
1816
+ function DocTypeChips({docTypes: docTypes}) {
1817
+ const schema = sanity.useSchema(), badgeTrim = index.useBadgeCapTrim();
1818
+ if (docTypes.length === 0) /* @__PURE__ */
1819
+ return jsxRuntime.jsx(ui.Text, {
1820
+ muted: !0,
1821
+ size: 1,
1822
+ children: "—"
1823
+ });
1824
+ const labels = docTypeLabels(docTypes, name => schema.get(name)?.title), shown = labels.slice(0, MAX_VISIBLE_DOC_TYPES), overflow = labels.length - shown.length;
1825
+ /* @__PURE__ */
1826
+ return jsxRuntime.jsxs(ui.Flex, {
1827
+ align: "center",
1828
+ gap: 2,
1829
+ style: badgeTrim,
1830
+ wrap: "wrap",
1831
+ children: [ shown.map(docType =>
1832
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, {
1833
+ fontSize: 1,
1834
+ style: {
1835
+ minWidth: 0
1836
+ },
1837
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", {
1838
+ style: {
1839
+ display: "block",
1840
+ overflow: "hidden",
1841
+ textOverflow: "ellipsis",
1842
+ whiteSpace: "nowrap"
1843
+ },
1844
+ children: docType.title
1845
+ })
1846
+ }, docType.name)), overflow > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, {
1847
+ muted: !0,
1848
+ size: 1,
1849
+ children: [ "+", overflow, " more" ]
1850
+ }) : null ]
1851
+ });
1852
+ }
1853
+
1854
+ function normalizeAbortReason(raw) {
1855
+ const trimmed = raw.trim();
1856
+ return trimmed === "" ? void 0 : trimmed;
1857
+ }
1858
+
1859
+ function abortedToast(title) {
1860
+ return {
1861
+ id: index.TOAST_ID.abort,
1862
+ status: "info",
1863
+ title: `Aborted “${title}”`
1864
+ };
1865
+ }
1866
+
1867
+ function abortAlreadySettledToast(title) {
1868
+ return {
1869
+ id: index.TOAST_ID.abort,
1870
+ status: "info",
1871
+ title: `“${title}” had already finished`,
1872
+ description: "Nothing was aborted — the workflow reached a terminal state first."
1873
+ };
1874
+ }
1875
+
1876
+ function abortUnconfirmedToast(args) {
1877
+ return {
1878
+ id: index.TOAST_ID.abort,
1879
+ status: "warning",
1880
+ title: `Couldn’t confirm “${args.title}” was aborted`,
1881
+ description: `${index.describeError(args.err)} — the abort may still have committed, so check the workflow’s state before trying again.`
1882
+ };
1883
+ }
1884
+
1885
+ function AbortWorkflowDialog({entry: entry, onClose: onClose}) {
1886
+ const {engine: engine} = index.useWorkflowContext(), definition = index.useDefinition(entry), toast = index.useWorkflowToast(), [typedReason, setTypedReason] = react.useState(""), [pending, setPending] = react.useState(!1), {instance: instance} = entry, title = index.instanceTitle(instance, definition), reason = normalizeAbortReason(typedReason), reasonInputId = `abort-workflow-reason-${instance._id}`, confirm = async () => {
1887
+ if (reason !== void 0) {
1888
+ setPending(!0);
1889
+ try {
1890
+ const {changed: changed} = await engine.abortInstance({
1891
+ instanceId: instance._id,
1892
+ reason: reason
1893
+ });
1894
+ toast.push(changed ? abortedToast(title) : abortAlreadySettledToast(title)), onClose();
1895
+ } catch (err) {
1896
+ toast.push(abortUnconfirmedToast({
1897
+ title: title,
1898
+ err: err
1899
+ })), setPending(!1);
1900
+ }
1901
+ }
1902
+ };
1903
+ /* @__PURE__ */
1904
+ return jsxRuntime.jsx(ui.Dialog, {
1905
+ footer: /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
1906
+ padding: 3,
1907
+ children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, {
1908
+ gap: 2,
1909
+ justify: "flex-end",
1910
+ children: [
1911
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, {
1912
+ disabled: pending,
1913
+ fontSize: 1,
1914
+ mode: "bleed",
1915
+ onClick: onClose,
1916
+ padding: 2,
1917
+ text: "Cancel"
1918
+ }),
1919
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, {
1920
+ disabled: reason === void 0,
1921
+ fontSize: 1,
1922
+ loading: pending,
1923
+ onClick: () => {
1924
+ confirm();
1925
+ },
1926
+ padding: 2,
1927
+ text: "Abort workflow",
1928
+ tone: "critical"
1929
+ }) ]
1930
+ })
1931
+ }),
1932
+ header: "Abort workflow",
1933
+ id: `abort-workflow-${instance._id}`,
1934
+ onClose: () => {
1935
+ pending || onClose();
1936
+ },
1937
+ width: 0,
1938
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
1939
+ padding: 4,
1940
+ children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, {
1941
+ gap: 5,
1942
+ children: [
1943
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, {
1944
+ size: 1,
1945
+ children: [ "Abort the ",
1946
+ /* @__PURE__ */ jsxRuntime.jsx("strong", {
1947
+ children: title
1948
+ }), " workflow? This cannot be undone." ]
1949
+ }),
1950
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, {
1951
+ gap: 3,
1952
+ children: [
1953
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
1954
+ as: "label",
1955
+ htmlFor: reasonInputId,
1956
+ size: 1,
1957
+ weight: "medium",
1958
+ children: "Reason"
1959
+ }),
1960
+ /* @__PURE__ */ jsxRuntime.jsx(ui.TextInput, {
1961
+ autoFocus: !0,
1962
+ fontSize: 1,
1963
+ id: reasonInputId,
1964
+ onChange: event => setTypedReason(event.currentTarget.value),
1965
+ placeholder: "Why is this workflow being stopped?",
1966
+ value: typedReason
1967
+ }) ]
1968
+ }) ]
1969
+ })
1970
+ })
1971
+ });
1972
+ }
1973
+
1974
+ function AbortWorkflowSection({entry: entry}) {
1975
+ const [aborting, setAborting] = react.useState(!1);
1976
+ return index.isLiveEntry(entry) ? /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, {
1977
+ gap: 4,
1978
+ children: [
1979
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
1980
+ size: 1,
1981
+ weight: "semibold",
1982
+ children: "Manage workflow"
1983
+ }),
1984
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, {
1985
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, {
1986
+ fontSize: 1,
1987
+ mode: "ghost",
1988
+ onClick: () => setAborting(!0),
1989
+ padding: 2,
1990
+ text: "Abort workflow…",
1991
+ tone: "critical"
1992
+ })
1993
+ }), aborting ? /* @__PURE__ */ jsxRuntime.jsx(AbortWorkflowDialog, {
1994
+ entry: entry,
1995
+ onClose: () => setAborting(!1)
1996
+ }) : null ]
1997
+ }) : null;
1998
+ }
1999
+
2000
+ function makeTitleResolver(definition) {
2001
+ const activities = (definition?.stages ?? []).flatMap(s => s.activities ?? []);
2002
+ return {
2003
+ action: (activityName, actionName) => activities.find(t => t.name === activityName)?.actions?.find(a => a.name === actionName)?.title ?? actionName,
2004
+ activity: name => activities.find(t => t.name === name)?.title ?? name,
2005
+ stage: name => index.stageTitle(definition, name)
2006
+ };
2007
+ }
2008
+
2009
+ function attributed(actor, ...parts) {
2010
+ if (actor) return {
2011
+ actorId: actor.id,
2012
+ parts: parts
2013
+ };
2014
+ const [first, ...rest] = parts;
2015
+ return typeof first != "string" ? {
2016
+ actorId: void 0,
2017
+ parts: parts
2018
+ } : {
2019
+ actorId: void 0,
2020
+ parts: [ first.charAt(0).toUpperCase() + first.slice(1), ...rest ]
2021
+ };
2022
+ }
2023
+
2024
+ function standalone(...parts) {
2025
+ return {
2026
+ actorId: void 0,
2027
+ parts: parts
2028
+ };
2029
+ }
2030
+
2031
+ const historyLiner = {
2032
+ stageEntered: (h, titles) => attributed(h.actor, `entered ${titles.stage(h.stage)}${h.fromStage ? ` from ${titles.stage(h.fromStage)}` : ""}`),
2033
+ stageExited: (h, titles) => attributed(h.actor, `left ${titles.stage(h.stage)} for ${titles.stage(h.toStage)}`),
2034
+ activityStatusChanged: (h, titles) => attributed(h.actor, `changed ${titles.activity(h.activity)}: ${h.from} → ${h.to}`),
2035
+ actionFired: (h, titles) => {
2036
+ const phrase = `fired ${titles.action(h.activity, h.action)} on ${titles.activity(h.activity)}`;
2037
+ return h.triggered ? standalone(`The workflow ${phrase}`) : attributed(h.actor, phrase);
2038
+ },
2039
+ transitionFired: (h, titles) => attributed(h.actor, `moved ${titles.stage(h.fromStage)} → ${titles.stage(h.toStage)}`),
2040
+ effectQueued: h => standalone("Effect ", {
2041
+ code: h.effect
2042
+ }, " queued"),
2043
+ effectCompleted: h => {
2044
+ const detail = h.detail ? ` — ${h.detail}` : "";
2045
+ return h.actor ? attributed(h.actor, "resolved effect ", {
2046
+ code: h.effect
2047
+ }, ` — ${h.status}${detail}`) : standalone("Effect ", {
2048
+ code: h.effect
2049
+ }, ` ${h.status}${detail}`);
2050
+ },
2051
+ effectClaimReleased: h => attributed(h.actor, "released a stale claim on effect ", {
2052
+ code: h.effect
2053
+ }, ` ${h.via === "sweep" ? "via the sweeper" : "via a drain takeover"}`),
2054
+ spawned: (h, titles) => attributed(h.actor, `spawned a child workflow from ${titles.activity(h.activity)}`),
2055
+ subworkflowAdopted: (h, titles) => standalone(`Re-adopted child workflow into ${titles.activity(h.activity)} on re-entering ${titles.stage(h.stage)}`),
2056
+ subworkflowResolved: (h, titles) => standalone(`Child workflow of ${titles.activity(h.activity)} ${h.status === "done" ? "completed" : "was aborted"}`),
2057
+ subworkflowOrphaned: h => standalone(`Orphaned child workflow reached terminal — ${h.detail}`),
2058
+ aborted: (h, titles) => attributed(h.actor, `aborted the workflow at ${titles.stage(h.stage)}${h.reason ? ` — ${h.reason}` : ""}`),
2059
+ opApplied: (h, titles) => h.action ? attributed(h.actor, `changed state via action ${titles.action(h.activity ?? "", h.action)}`) : h.activity ? attributed(h.actor, `changed state via activity ${titles.activity(h.activity)}`) : attributed(h.actor, "changed state via ", {
2060
+ code: h.opType
2061
+ }),
2062
+ fieldQueryDiscarded: h => standalone({
2063
+ code: JSON.stringify(h)
2064
+ })
2065
+ };
2066
+
2067
+ function historyLine(h, titles) {
2068
+ return historyLiner[h._type](h, titles);
2069
+ }
2070
+
2071
+ function PendingEffectRows({instance: instance, now: now}) {
2072
+ const {drainEffectsFor: drainEffectsFor, completeEffectFor: completeEffectFor, effectHandlers: effectHandlers} = index.useWorkflowContext(), toast = index.useWorkflowToast(), [busy, setBusy] = react.useState(!1), pending = instance.pendingEffects ?? [];
2073
+ if (pending.length === 0) return null;
2074
+ const runRegistered = async () => {
2075
+ setBusy(!0);
2076
+ try {
2077
+ await drainEffectsFor(instance._id), toast.push({
2078
+ id: index.TOAST_ID.effectsDrain,
2079
+ status: "info",
2080
+ title: "Ran the registered handlers"
2081
+ });
2082
+ } catch (err) {
2083
+ toast.push({
2084
+ id: index.TOAST_ID.effectsDrain,
2085
+ status: "error",
2086
+ title: "Failed to run pending effects",
2087
+ description: index.describeError(err)
2088
+ });
2089
+ } finally {
2090
+ setBusy(!1);
2091
+ }
2092
+ }, resolve = async (effectKey, status) => {
2093
+ setBusy(!0);
2094
+ try {
2095
+ await completeEffectFor(instance._id, {
2096
+ effectKey: effectKey,
2097
+ status: status,
2098
+ detail: "Resolved in Studio"
2099
+ }), toast.push({
2100
+ id: index.TOAST_ID.effectResolve,
2101
+ status: "info",
2102
+ title: `Effect marked as ${status}`
2103
+ });
2104
+ } catch (err) {
2105
+ toast.push({
2106
+ id: index.TOAST_ID.effectResolve,
2107
+ status: "error",
2108
+ title: "Failed to resolve the effect",
2109
+ description: index.describeError(err)
2110
+ });
2111
+ } finally {
2112
+ setBusy(!1);
2113
+ }
2114
+ };
2115
+ /* @__PURE__ */
2116
+ return jsxRuntime.jsx(jsxRuntime.Fragment, {
2117
+ children: pending.map(fx => /* @__PURE__ */ jsxRuntime.jsx(PendingEffectRow, {
2118
+ busy: busy,
2119
+ effect: fx,
2120
+ instanceId: instance._id,
2121
+ now: now,
2122
+ onResolve: status => resolve(fx._key, status),
2123
+ onRunHandlers: runRegistered,
2124
+ registered: fx.name in effectHandlers
2125
+ }, fx._key))
2126
+ });
2127
+ }
2128
+
2129
+ function PendingEffectRow({busy: busy, effect: effect, instanceId: instanceId, now: now, onResolve: onResolve, onRunHandlers: onRunHandlers, registered: registered}) {
2130
+ const label = effect.title ?? effect.name, avatarSize = index.useAvatarSize();
2131
+ /* @__PURE__ */
2132
+ return jsxRuntime.jsxs(ui.Flex, {
2133
+ align: "center",
2134
+ gap: 2,
2135
+ children: [
2136
+ /* @__PURE__ */ jsxRuntime.jsx(FeedGlyph, {
2137
+ hint: "Waiting to run",
2138
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Clock.ClockIcon, {})
2139
+ }),
2140
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, {
2141
+ align: "center",
2142
+ flex: 1,
2143
+ gap: 1,
2144
+ style: {
2145
+ minWidth: 0
2146
+ },
2147
+ children: [
2148
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
2149
+ style: {
2150
+ minWidth: 0
2151
+ },
2152
+ children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, {
2153
+ size: 1,
2154
+ textOverflow: "ellipsis",
2155
+ children: [ effect.title ?? /* @__PURE__ */ jsxRuntime.jsx(CodeChip, {
2156
+ value: effect.name
2157
+ }), registered ? " — ready to run in Studio" : " — waiting for the runtime" ]
2158
+ })
2159
+ }),
2160
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
2161
+ muted: !0,
2162
+ size: 1,
2163
+ children: "·"
2164
+ }),
2165
+ /* @__PURE__ */ jsxRuntime.jsx(index.HoverHint, {
2166
+ text: index.formatDateTime(effect.queuedAt),
2167
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
2168
+ muted: !0,
2169
+ size: 1,
2170
+ style: {
2171
+ whiteSpace: "nowrap"
2172
+ },
2173
+ children: index.formatShortAgo(effect.queuedAt, now)
2174
+ })
2175
+ }) ]
2176
+ }),
2177
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, {
2178
+ align: "center",
2179
+ flex: "none",
2180
+ style: {
2181
+ height: avatarSize
2182
+ },
2183
+ children: /* @__PURE__ */ jsxRuntime.jsx(index.HintedMenuButton, {
2184
+ button: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, {
2185
+ "aria-label": `Resolve ${label}`,
2186
+ disabled: busy,
2187
+ fontSize: 1,
2188
+ icon: EllipsisHorizontal.EllipsisHorizontalIcon,
2189
+ mode: "bleed",
2190
+ padding: 2
2191
+ }),
2192
+ hint: "Resolve",
2193
+ id: `pending-effect-menu-${instanceId}-${effect._key}`,
2194
+ menu: /* @__PURE__ */ jsxRuntime.jsxs(ui.Menu, {
2195
+ children: [ registered ? /* @__PURE__ */ jsxRuntime.jsx(ui.MenuItem, {
2196
+ onClick: onRunHandlers,
2197
+ text: "Run registered handlers"
2198
+ }) : null,
2199
+ /* @__PURE__ */ jsxRuntime.jsx(ui.MenuItem, {
2200
+ onClick: () => onResolve("done"),
2201
+ text: "Mark done"
2202
+ }),
2203
+ /* @__PURE__ */ jsxRuntime.jsx(ui.MenuItem, {
2204
+ onClick: () => onResolve("failed"),
2205
+ text: "Mark failed",
2206
+ tone: "critical"
2207
+ }) ]
2208
+ }),
2209
+ popover: {
2210
+ placement: "bottom-end",
2211
+ portal: !0
2212
+ }
2213
+ })
2214
+ }) ]
2215
+ });
2216
+ }
2217
+
2218
+ function ActivityLog({instance: instance, definition: definition}) {
2219
+ const titles = react.useMemo(() => makeTitleResolver(definition), [ definition ]), entries = react.useMemo(() => instance.history.toReversed(), [ instance.history ]), [now, setNow] = react.useState(() => /* @__PURE__ */ new Date);
2220
+ return react.useEffect(() => {
2221
+ const timer = setInterval(() => setNow(/* @__PURE__ */ new Date), 6e4);
2222
+ return () => clearInterval(timer);
2223
+ }, []), entries.length === 0 && (instance.pendingEffects ?? []).length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
2224
+ muted: !0,
2225
+ size: 1,
2226
+ children: "No activity yet"
2227
+ }) : /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, {
2228
+ gap: 3,
2229
+ children: [
2230
+ /* @__PURE__ */ jsxRuntime.jsx(PendingEffectRows, {
2231
+ instance: instance,
2232
+ now: now
2233
+ }), entries.map(h => /* @__PURE__ */ jsxRuntime.jsx(HistoryRow, {
2234
+ entry: h,
2235
+ now: now,
2236
+ titles: titles
2237
+ }, h._key)) ]
2238
+ });
2239
+ }
2240
+
2241
+ function HistoryRow({entry: entry, now: now, titles: titles}) {
2242
+ const line = historyLine(entry, titles);
2243
+ /* @__PURE__ */
2244
+ return jsxRuntime.jsxs(ui.Flex, {
2245
+ align: "center",
2246
+ gap: 2,
2247
+ children: [ line.actorId === void 0 ? /* @__PURE__ */ jsxRuntime.jsx(FeedGlyph, {
2248
+ hint: "The workflow",
2249
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Cog.CogIcon, {})
2250
+ }) : /* @__PURE__ */ jsxRuntime.jsx(index.UserAvatar, {
2251
+ id: line.actorId
2252
+ }),
2253
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, {
2254
+ align: "center",
2255
+ flex: 1,
2256
+ gap: 1,
2257
+ style: {
2258
+ minWidth: 0
2259
+ },
15
2260
  children: [
16
- /* @__PURE__ */ jsxRuntime.jsx(index.ActivitiesList, {
17
- entry: entry,
18
- onOpenActivity: onOpenActivity
2261
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
2262
+ style: {
2263
+ minWidth: 0
2264
+ },
2265
+ children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, {
2266
+ size: 1,
2267
+ textOverflow: "ellipsis",
2268
+ children: [ line.actorId === void 0 ? null : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, {
2269
+ children: [
2270
+ /* @__PURE__ */ jsxRuntime.jsx(ActorNameSpan, {
2271
+ id: line.actorId
2272
+ }), " " ]
2273
+ }), line.parts.map((part, index2) => typeof part == "string" ? part : /* @__PURE__ */ jsxRuntime.jsx(CodeChip, {
2274
+ value: part.code
2275
+ }, index2)) ]
2276
+ })
2277
+ }),
2278
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
2279
+ muted: !0,
2280
+ size: 1,
2281
+ children: "·"
19
2282
  }),
20
- /* @__PURE__ */ jsxRuntime.jsx(index.TodoItemsList, {
21
- entry: entry,
22
- surface: "tool-instance-detail"
2283
+ /* @__PURE__ */ jsxRuntime.jsx(index.HoverHint, {
2284
+ text: index.formatDateTime(entry.at),
2285
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
2286
+ muted: !0,
2287
+ size: 1,
2288
+ style: {
2289
+ whiteSpace: "nowrap"
2290
+ },
2291
+ children: index.formatShortAgo(entry.at, now)
2292
+ })
23
2293
  }) ]
2294
+ }) ]
2295
+ });
2296
+ }
2297
+
2298
+ function ActorNameSpan({id: id}) {
2299
+ const {name: name} = index.useUserDisplay(id), {font: font} = ui.useTheme_v2();
2300
+ /* @__PURE__ */
2301
+ return jsxRuntime.jsx("span", {
2302
+ style: {
2303
+ fontWeight: font.text.weights.medium
2304
+ },
2305
+ children: name
2306
+ });
2307
+ }
2308
+
2309
+ function CodeChip({value: value}) {
2310
+ /* @__PURE__ */
2311
+ return jsxRuntime.jsx("code", {
2312
+ children: value
2313
+ });
2314
+ }
2315
+
2316
+ function FeedGlyph({hint: hint, icon: icon}) {
2317
+ const size = index.useAvatarSize();
2318
+ /* @__PURE__ */
2319
+ return jsxRuntime.jsx(index.HoverHint, {
2320
+ text: hint,
2321
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, {
2322
+ align: "center",
2323
+ flex: "none",
2324
+ justify: "center",
2325
+ style: {
2326
+ height: size,
2327
+ width: size
2328
+ },
2329
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
2330
+ muted: !0,
2331
+ size: 1,
2332
+ children: icon
2333
+ })
24
2334
  })
25
2335
  });
26
2336
  }
27
2337
 
28
- function WorkflowInstanceDetail({instanceId: instanceId, onBack: onBack}) {
2338
+ function WorkflowInstanceDetail({instanceId: instanceId, backToList: backToList}) {
29
2339
  const entry = index.useWorkflowInstanceEntry(instanceId);
30
2340
  index.useLogEventOnMount(index.WorkflowInstanceDetailViewed, {
31
2341
  instanceId: instanceId
@@ -35,49 +2345,32 @@ function WorkflowInstanceDetail({instanceId: instanceId, onBack: onBack}) {
35
2345
  const timer = setTimeout(() => setGraceOver(!0), 2500);
36
2346
  return () => clearTimeout(timer);
37
2347
  }, [ instanceId ]), entry?.invalid ? /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
38
- padding: 4,
39
- children: /* @__PURE__ */ jsxRuntime.jsx(index.InvalidDocNotice, {
40
- invalid: entry.invalid
41
- })
42
- }) : entry ? /* @__PURE__ */ jsxRuntime.jsx(InstanceDetailPanel, {
43
- entry: entry,
44
- onBack: onBack
45
- }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
46
- padding: 4,
47
- children: graceOver ? /* @__PURE__ */ jsxRuntime.jsx(NotFound, {
48
- id: instanceId,
49
- onBack: onBack
50
- }) : /* @__PURE__ */ jsxRuntime.jsx(index.LoadingRow, {
51
- label: "Loading workflow…"
52
- })
53
- });
54
- }
55
-
56
- function NotFound({id: id, onBack: onBack}) {
57
- /* @__PURE__ */
58
- return jsxRuntime.jsxs(ui.Stack, {
59
- gap: 4,
60
- children: [
61
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, {
62
- muted: !0,
63
- size: 1,
64
- children: [ "No workflow with id “", id, "” — it may have been deleted." ]
65
- }),
66
- /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, {
67
- children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, {
68
- fontSize: 1,
69
- icon: ArrowLeft.ArrowLeftIcon,
70
- mode: "ghost",
71
- onClick: onBack,
72
- padding: 2,
73
- text: "Back to workflows"
2348
+ padding: 4,
2349
+ children: /* @__PURE__ */ jsxRuntime.jsx(index.InvalidDocNotice, {
2350
+ invalid: entry.invalid
2351
+ })
2352
+ }) : entry ? /* @__PURE__ */ jsxRuntime.jsx(InstanceDetailPanel, {
2353
+ entry: entry
2354
+ }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
2355
+ paddingX: 3,
2356
+ children: /* @__PURE__ */ jsxRuntime.jsx(TabStatusRow, {
2357
+ children: graceOver ? /* @__PURE__ */ jsxRuntime.jsx(DetailNotFound, {
2358
+ back: backToList,
2359
+ message: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, {
2360
+ children: [ "Unable to find workflow with id “",
2361
+ /* @__PURE__ */ jsxRuntime.jsx("strong", {
2362
+ children: instanceId
2363
+ }), "”." ]
2364
+ })
2365
+ }) : /* @__PURE__ */ jsxRuntime.jsx(index.LoadingRow, {
2366
+ label: "Loading workflow…"
74
2367
  })
75
- }) ]
2368
+ })
76
2369
  });
77
2370
  }
78
2371
 
79
- function InstanceDetailPanel({entry: entry, onBack: onBack}) {
80
- const definition = index.useDefinition(entry), [openActivity, setOpenActivity] = react.useState(null), {instance: instance} = entry;
2372
+ function InstanceDetailPanel({entry: entry}) {
2373
+ const definition = index.useDefinition(entry), snapshotWidth = index.useContainerToken(2), [openActivity, setOpenActivity] = react.useState(null), {instance: instance, ready: ready} = entry;
81
2374
  return react.useEffect(() => {
82
2375
  openActivity && index.openActivityGone({
83
2376
  entry: entry,
@@ -88,36 +2381,56 @@ function InstanceDetailPanel({entry: entry, onBack: onBack}) {
88
2381
  height: "fill",
89
2382
  overflow: "hidden",
90
2383
  children: [
91
- /* @__PURE__ */ jsxRuntime.jsx(DetailHeader, {
92
- definition: definition,
93
- entry: entry,
94
- onBack: onBack
2384
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
2385
+ flex: "none",
2386
+ paddingBottom: 2,
2387
+ paddingTop: 3,
2388
+ paddingX: 3,
2389
+ children: /* @__PURE__ */ jsxRuntime.jsx(DetailTitle, {
2390
+ busy: !ready || index.isEvaluationStale(entry),
2391
+ documentGdr: documentRefsOf(instance)[0],
2392
+ title: index.instanceTitle(instance, definition)
2393
+ })
95
2394
  }),
96
2395
  /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
97
2396
  flex: 1,
98
2397
  overflow: "auto",
99
- paddingX: 4,
100
- children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, {
101
- gap: 4,
2398
+ paddingX: 3,
2399
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
102
2400
  paddingBottom: 5,
103
- children: [
104
- /* @__PURE__ */ jsxRuntime.jsx(StudioDocumentsSection, {
105
- instance: instance
106
- }),
107
- /* @__PURE__ */ jsxRuntime.jsx(StageDiagramSection, {
108
- definition: definition,
109
- entry: entry
110
- }),
111
- /* @__PURE__ */ jsxRuntime.jsx(index.GroupHeading, {
112
- title: "All tasks"
113
- }),
114
- /* @__PURE__ */ jsxRuntime.jsx(InstanceTaskLists, {
115
- entry: entry,
116
- onOpenActivity: name => setOpenActivity({
117
- stage: instance.currentStage,
118
- activityName: name
119
- })
120
- }) ]
2401
+ paddingLeft: 2,
2402
+ paddingTop: 2,
2403
+ style: {
2404
+ maxWidth: snapshotWidth
2405
+ },
2406
+ children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, {
2407
+ gap: 6,
2408
+ children: [
2409
+ /* @__PURE__ */ jsxRuntime.jsx(index.InstanceSnapshotBody, {
2410
+ entry: entry,
2411
+ onOpenActivity: name => setOpenActivity({
2412
+ stage: instance.currentStage,
2413
+ activityName: name
2414
+ }),
2415
+ surface: "tool-instance-detail"
2416
+ }),
2417
+ /* @__PURE__ */ jsxRuntime.jsx(AbortWorkflowSection, {
2418
+ entry: entry
2419
+ }),
2420
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, {
2421
+ gap: 4,
2422
+ children: [
2423
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
2424
+ size: 1,
2425
+ weight: "semibold",
2426
+ children: "History"
2427
+ }),
2428
+ /* @__PURE__ */ jsxRuntime.jsx(ActivityLog, {
2429
+ definition: definition,
2430
+ instance: instance
2431
+ }) ]
2432
+ }) ]
2433
+ })
121
2434
  })
122
2435
  }), openActivity ? /* @__PURE__ */ jsxRuntime.jsx(index.ActivityDetailDialog, {
123
2436
  activityName: openActivity.activityName,
@@ -130,130 +2443,14 @@ function InstanceDetailPanel({entry: entry, onBack: onBack}) {
130
2443
  });
131
2444
  }
132
2445
 
133
- function DetailHeader({entry: entry, definition: definition, onBack: onBack}) {
134
- const {instance: instance, ready: ready} = entry;
135
- /* @__PURE__ */
136
- return jsxRuntime.jsx(ui.Card, {
137
- flex: "none",
138
- paddingX: 4,
139
- paddingY: 3,
140
- children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, {
141
- align: "center",
142
- gap: 3,
143
- wrap: "wrap",
144
- children: [
145
- /* @__PURE__ */ jsxRuntime.jsx(index.HoverHint, {
146
- text: "Back to workflows",
147
- children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, {
148
- "aria-label": "Back to workflows",
149
- fontSize: 1,
150
- icon: ArrowLeft.ArrowLeftIcon,
151
- mode: "bleed",
152
- onClick: onBack,
153
- padding: 2
154
- })
155
- }),
156
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, {
157
- gap: 2,
158
- children: [
159
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
160
- size: 1,
161
- weight: "semibold",
162
- children: index.instanceTitle(instance, definition)
163
- }),
164
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, {
165
- muted: !0,
166
- size: 1,
167
- children: [ "Started ", index.formatDate(instance.startedAt) ]
168
- }) ]
169
- }), workflowEngine.terminalState(instance) === "in-flight" ? /* @__PURE__ */ jsxRuntime.jsx(index.StageChip, {
170
- title: index.stageTitle(definition, instance.currentStage)
171
- }) : /* @__PURE__ */ jsxRuntime.jsx(index.AbortedBadge, {
172
- instance: instance
173
- }),
174
- /* @__PURE__ */ jsxRuntime.jsx(index.SpinnerSlot, {
175
- busy: !ready || index.isEvaluationStale(entry)
176
- }) ]
177
- })
178
- });
179
- }
180
-
181
- function StudioDocumentsSection({instance: instance}) {
182
- const refs = documentRefsOf(instance);
183
- return refs.length === 0 ? null : /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, {
184
- gap: 3,
185
- marginTop: 3,
186
- children: [
187
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
188
- muted: !0,
189
- size: 1,
190
- weight: "medium",
191
- children: refs.length === 1 ? "Studio document" : "Studio documents"
192
- }),
193
- /* @__PURE__ */ jsxRuntime.jsx(ui.Stack, {
194
- gap: 2,
195
- children: refs.map(ref => /* @__PURE__ */ jsxRuntime.jsx(index.DocPreviewLink, {
196
- gdr: ref
197
- }, ref.id))
198
- }) ]
199
- });
200
- }
201
-
202
- function StageDiagramSection({entry: entry, definition: definition}) {
203
- const {instance: instance} = entry;
204
- return definition ?
205
- /* @__PURE__ */ jsxRuntime.jsx(workflowDiagram.WorkflowDiagram, {
206
- currentStage: workflowEngine.terminalState(instance) === "in-flight" ? instance.currentStage : void 0,
207
- definition: definition,
208
- evaluation: entry.evaluation,
209
- explain: !0,
210
- guardCount: entry.guards?.length,
211
- height: 340,
212
- history: instance.history
213
- }, instance._id) : /* @__PURE__ */ jsxRuntime.jsx(ui.Card, {
214
- border: !0,
215
- marginTop: 3,
216
- padding: 3,
217
- radius: 2,
218
- tone: "caution",
219
- children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
220
- muted: !0,
221
- size: 1,
222
- children: "Couldn’t load this workflow’s full details — the stage diagram is unavailable, and technical names show instead of titles."
223
- })
224
- });
225
- }
226
-
227
- function MutedNote({text: text}) {
228
- /* @__PURE__ */
229
- return jsxRuntime.jsx(ui.Box, {
230
- paddingY: 3,
231
- children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
232
- muted: !0,
233
- size: 1,
234
- children: text
235
- })
236
- });
237
- }
238
-
239
2446
  function useStartableDefinitions() {
240
- const {engine: engine} = index.useWorkflowContext(), [definitions, setDefinitions] = react.useState(void 0), [error, setError] = react.useState(void 0);
241
- return react.useEffect(() => {
242
- let cancelled = !1;
243
- return (async () => {
244
- const rows = await index.readDeployedDefinitions(engine), startable = workflowEngine.latestDeployedDefinitions(rows).filter(row => workflowEngine.isStartableDefinition(row)).map(row => ({
245
- name: row.name,
246
- title: row.title ?? row.name,
247
- description: row.description
248
- }));
249
- cancelled || (setDefinitions(startable), setError(void 0));
250
- })().catch(err => {
251
- cancelled || setError(index.describeError(err));
252
- }), () => {
253
- cancelled = !0;
254
- };
255
- }, [ engine ]), {
256
- definitions: definitions,
2447
+ const {definitions: definitions, error: error} = useDeployedDefinitions();
2448
+ return {
2449
+ definitions: react.useMemo(() => definitions?.filter(row => workflowEngine.isStartableDefinition(row)).map(row => ({
2450
+ name: row.name,
2451
+ title: row.title ?? row.name,
2452
+ description: row.description
2453
+ })), [ definitions ]),
257
2454
  error: error
258
2455
  };
259
2456
  }
@@ -343,196 +2540,55 @@ function DefinitionPickerPanel({onPick: onPick}) {
343
2540
  textOverflow: "ellipsis",
344
2541
  children: definition.description
345
2542
  }) : null ]
346
- })
347
- }, definition.name))
348
- }) ]
349
- })
350
- });
351
- }
352
-
353
- function ToolActivityDialog({target: target, onClose: onClose}) {
354
- const entry = index.useWorkflowInstanceEntry(target.instanceId), definition = index.useDefinition(entry), resolvedOnce = react.useRef(!1);
355
- return entry && (resolvedOnce.current = !0), react.useEffect(() => {
356
- resolvedOnce.current && index.openActivityGone({
357
- entry: entry,
358
- target: target
359
- }) && onClose();
360
- }, [ entry, target, onClose ]), entry?.invalid ? /* @__PURE__ */ jsxRuntime.jsx(ui.Dialog, {
361
- header: "Workflow unavailable",
362
- id: "workflow-activity-detail",
363
- onClose: onClose,
364
- width: 1,
365
- children: /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
366
- padding: 4,
367
- children: /* @__PURE__ */ jsxRuntime.jsx(index.InvalidDocNotice, {
368
- invalid: entry.invalid
369
- })
370
- })
371
- }) : entry?.evaluation ? /* @__PURE__ */ jsxRuntime.jsx(index.ActivityDetailDialog, {
372
- activityName: target.activityName,
373
- breadcrumb: index.instanceBreadcrumb(entry.instance, definition),
374
- definition: definition,
375
- document: documentRefsOf(entry.instance)[0],
376
- entry: entry,
377
- onClose: onClose,
378
- source: "tool-task-list"
379
- }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Dialog, {
380
- header: "Loading activity…",
381
- id: "workflow-activity-detail",
382
- onClose: onClose,
383
- width: 1,
384
- children: /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
385
- padding: 4,
386
- children: /* @__PURE__ */ jsxRuntime.jsx(index.LoadingRow, {
387
- label: "Fetching the live workflow state…"
388
- })
389
- })
390
- });
391
- }
392
-
393
- const GROUP_DEFAULT_OPEN = !0;
394
-
395
- function instanceBandDefaultOpen(segment) {
396
- return segment === "for-me";
397
- }
398
-
399
- const UNGROUPED_BAND_KEY = "section:no-document", PAYLOAD_VERSION = 1, MAX_ENTRIES = 200;
400
-
401
- function bandStateStorageKey(scope) {
402
- return `sanity.workflows.tool.bands:${scope}`;
403
- }
404
-
405
- function isRecord(value) {
406
- return typeof value == "object" && value !== null && !Array.isArray(value);
407
- }
408
-
409
- function bandEntryOf(value) {
410
- if (!isRecord(value)) return;
411
- const {open: open, touched: touched} = value;
412
- if (!(typeof open != "boolean" || typeof touched != "number") && Number.isFinite(touched)) return {
413
- open: open,
414
- touched: touched
415
- };
416
- }
417
-
418
- function parsePayload(raw) {
419
- try {
420
- const parsed = JSON.parse(raw);
421
- if (!isRecord(parsed) || parsed.version !== PAYLOAD_VERSION || !isRecord(parsed.bands)) return;
422
- const entries = [];
423
- for (const [key, value] of Object.entries(parsed.bands)) {
424
- const entry = bandEntryOf(value);
425
- if (entry === void 0) return;
426
- entries.push([ key, entry ]);
427
- }
428
- return Object.fromEntries(entries);
429
- } catch {
430
- return;
431
- }
432
- }
433
-
434
- function readStored(storage, storageKey) {
435
- if (!storage) return {
436
- entries: void 0,
437
- corrupt: !1
438
- };
439
- let raw;
440
- try {
441
- raw = storage.getItem(storageKey);
442
- } catch {
443
- return {
444
- entries: void 0,
445
- corrupt: !1
446
- };
447
- }
448
- if (raw === null) return {
449
- entries: {},
450
- corrupt: !1
451
- };
452
- const entries = parsePayload(raw);
453
- return {
454
- entries: entries,
455
- corrupt: entries === void 0
456
- };
457
- }
458
-
459
- function writeStored(args) {
460
- if (!args.storage) return !1;
461
- try {
462
- return args.storage.setItem(args.storageKey, JSON.stringify({
463
- version: PAYLOAD_VERSION,
464
- bands: args.entries
465
- })), !0;
466
- } catch {
467
- return !1;
468
- }
469
- }
470
-
471
- function baseEntries(args) {
472
- return args.stored === void 0 ? args.held : args.unpersisted ? mergeByTouched(args.stored, args.held) : args.stored;
473
- }
474
-
475
- function mergeByTouched(stored, held) {
476
- const merged = {
477
- ...stored
478
- };
479
- for (const [key, entry] of Object.entries(held)) {
480
- const persisted = merged[key];
481
- (persisted === void 0 || entry.touched >= persisted.touched) && (merged[key] = entry);
482
- }
483
- return merged;
484
- }
485
-
486
- function prune(entries) {
487
- const held = Object.entries(entries);
488
- return held.length <= MAX_ENTRIES ? entries : Object.fromEntries(held.sort(([, a], [, b]) => b.touched - a.touched).slice(0, MAX_ENTRIES));
489
- }
490
-
491
- function togglesOf(entries) {
492
- return new Map(Object.entries(entries).map(([key, entry]) => [ key, entry.open ]));
493
- }
494
-
495
- function defaultStorage() {
496
- try {
497
- return globalThis.localStorage;
498
- } catch {
499
- return;
500
- }
2543
+ })
2544
+ }, definition.name))
2545
+ }) ]
2546
+ })
2547
+ });
501
2548
  }
502
2549
 
503
- function createBandStateStore(args) {
504
- const storage = args.storage ?? defaultStorage(), initial = readStored(storage, args.storageKey);
505
- initial.corrupt && writeStored({
506
- storage: storage,
507
- storageKey: args.storageKey,
508
- entries: {}
2550
+ function ToolActivityDialog({target: target, onClose: onClose}) {
2551
+ const entry = index.useWorkflowInstanceEntry(target.instanceId), definition = index.useDefinition(entry), resolvedOnce = react.useRef(!1);
2552
+ return entry && (resolvedOnce.current = !0), react.useEffect(() => {
2553
+ resolvedOnce.current && index.openActivityGone({
2554
+ entry: entry,
2555
+ target: target
2556
+ }) && onClose();
2557
+ }, [ entry, target, onClose ]), entry?.invalid ? /* @__PURE__ */ jsxRuntime.jsx(ui.Dialog, {
2558
+ header: "Workflow unavailable",
2559
+ id: "workflow-activity-detail",
2560
+ onClose: onClose,
2561
+ width: 1,
2562
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
2563
+ padding: 4,
2564
+ children: /* @__PURE__ */ jsxRuntime.jsx(index.InvalidDocNotice, {
2565
+ invalid: entry.invalid
2566
+ })
2567
+ })
2568
+ }) : entry?.evaluation ? /* @__PURE__ */ jsxRuntime.jsx(index.ActivityDetailDialog, {
2569
+ activityName: target.activityName,
2570
+ breadcrumb: index.instanceBreadcrumb(entry.instance, definition),
2571
+ definition: definition,
2572
+ document: documentRefsOf(entry.instance)[0],
2573
+ entry: entry,
2574
+ onClose: onClose,
2575
+ source: "tool-task-list"
2576
+ }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Dialog, {
2577
+ header: "Loading activity…",
2578
+ id: "workflow-activity-detail",
2579
+ onClose: onClose,
2580
+ width: 1,
2581
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
2582
+ padding: 4,
2583
+ children: /* @__PURE__ */ jsxRuntime.jsx(index.LoadingRow, {
2584
+ label: "Fetching the live workflow state…"
2585
+ })
2586
+ })
509
2587
  });
510
- let held = initial.entries ?? {}, unpersisted = !1;
511
- return {
512
- read: () => togglesOf(held),
513
- setOpen(key, open) {
514
- const stored = readStored(storage, args.storageKey).entries, base = baseEntries({
515
- stored: stored,
516
- held: held,
517
- unpersisted: unpersisted
518
- });
519
- return held = prune({
520
- ...base,
521
- [key]: {
522
- open: open,
523
- touched: Date.now()
524
- }
525
- }), unpersisted = !writeStored({
526
- storage: storage,
527
- storageKey: args.storageKey,
528
- entries: held
529
- }), togglesOf(held);
530
- }
531
- };
532
2588
  }
533
2589
 
534
2590
  function datesOf(state) {
535
- return state.filter(entry => entry._type === "date" || entry._type === "datetime").map(entry => entry.value).filter(value => typeof value == "string" && value !== "");
2591
+ return state.filter(entry => index.isDateFieldKind(entry._type)).map(entry => entry.value).filter(value => typeof value == "string" && value !== "");
536
2592
  }
537
2593
 
538
2594
  function spawnOrigins(instances) {
@@ -632,6 +2688,14 @@ function deriveTaskGroups(args) {
632
2688
  };
633
2689
  }
634
2690
 
2691
+ function orderGroupsByPreviewTitle(groups, titles) {
2692
+ const withDocuments = [ ...groups.withDocuments ].sort(byResolvedTitle(group => titles.get(group.document.id), group => group.key));
2693
+ return {
2694
+ ...groups,
2695
+ withDocuments: withDocuments
2696
+ };
2697
+ }
2698
+
635
2699
  function cutTaskGroups(groups, cut) {
636
2700
  const cutBands = bands => bands.filter(band => cut.keepBand?.(band) ?? !0).map(band => ({
637
2701
  ...band,
@@ -777,11 +2841,13 @@ function workflowTally(workflows) {
777
2841
  function settleToast(outcome) {
778
2842
  const total = outcome.settled + outcome.skipped.length + outcome.failures.length;
779
2843
  if (outcome.skipped.length === 0 && outcome.failures.length === 0) return {
780
- status: "success",
2844
+ id: index.TOAST_ID.orphanSettle,
2845
+ status: "info",
781
2846
  title: outcome.settled === 1 ? "Cleaned up 1 workflow" : `Cleaned up ${outcome.settled} workflows`
782
2847
  };
783
2848
  const lines = [ ...outcome.skipped.map(workflow => `${workflow.title}: its document exists again — left running`), ...outcome.failures.map(failure => `${failure.workflow.title}: ${failure.message}`) ];
784
2849
  return {
2850
+ id: index.TOAST_ID.orphanSettle,
785
2851
  status: "warning",
786
2852
  title: `Cleaned up ${outcome.settled} of ${total} workflows`,
787
2853
  description: lines.join(`\n`)
@@ -790,9 +2856,10 @@ function settleToast(outcome) {
790
2856
 
791
2857
  function recheckFailedToast(err) {
792
2858
  return {
2859
+ id: index.TOAST_ID.orphanSettle,
793
2860
  status: "error",
794
- title: "Nothing was cleaned up — the documents could not be re-checked",
795
- description: index.describeError(err)
2861
+ title: "Nothing was cleaned up",
2862
+ description: `The documents could not be re-checked: ${index.describeError(err)}`
796
2863
  };
797
2864
  }
798
2865
 
@@ -822,7 +2889,7 @@ function OrphanedWorkflowsNote({orphans: orphans}) {
822
2889
  function SettleOrphansDialog({workflows: workflows, onClose: onClose}) {
823
2890
  const {engine: engine} = index.useWorkflowContext(), client = sanity.useClient({
824
2891
  apiVersion: index.WORKFLOW_API_VERSION
825
- }), toast = index.useClosableToast(), [pending, setPending] = react.useState(!1), confirm = async () => {
2892
+ }), toast = index.useWorkflowToast(), [pending, setPending] = react.useState(!1), confirm = async () => {
826
2893
  setPending(!0);
827
2894
  try {
828
2895
  const outcome = await settleOrphans({
@@ -1067,38 +3134,6 @@ function applyTaskFilters(args) {
1067
3134
  });
1068
3135
  }
1069
3136
 
1070
- const NOTHING = {
1071
- titles: /* @__PURE__ */ new Map,
1072
- resolved: /* @__PURE__ */ new Set
1073
- };
1074
-
1075
- function useDocPreviewTitles(documents) {
1076
- const previewStore = sanity.useDocumentPreviewStore(), schema = sanity.useSchema(), {binding: binding} = index.useWorkflowContext(), [state, setState] = react.useState(NOTHING), key = documents.map(doc => doc.id).join("|");
1077
- return react.useEffect(() => {
1078
- const titles = /* @__PURE__ */ new Map, resolved = /* @__PURE__ */ new Set, publish = () => setState({
1079
- titles: new Map(titles),
1080
- resolved: new Set(resolved)
1081
- }), subscriptions = documents.flatMap(doc => {
1082
- const {parsed: parsedRef, local: local} = index.gdrLocality(doc.id, binding.contentResource), schemaType = schema.get(doc.type);
1083
- return !parsedRef || !local || !schemaType ? (resolved.add(doc.id), []) : [ previewStore.observeForPreview({
1084
- _type: "reference",
1085
- _ref: parsedRef.documentId
1086
- }, schemaType).subscribe({
1087
- next: event => {
1088
- resolved.add(doc.id);
1089
- const title = event.snapshot?.title;
1090
- typeof title == "string" && titles.set(doc.id, title), publish();
1091
- },
1092
- error: err => {
1093
- console.error(`[workflow-studio-plugin] preview for "${doc.id}" failed:`, err),
1094
- resolved.add(doc.id), publish();
1095
- }
1096
- }) ];
1097
- });
1098
- return publish(), () => subscriptions.forEach(sub => sub.unsubscribe());
1099
- }, [ key, previewStore, schema, binding.contentResource.id ]), state;
1100
- }
1101
-
1102
3137
  const HITS_CAP = 6;
1103
3138
 
1104
3139
  function TaskFilterMenu({filters: filters, options: options, onChange: onChange}) {
@@ -1597,8 +3632,10 @@ function FilterRowPortal({filters: filters, onChange: onChange, options: options
1597
3632
  }), slot) : null;
1598
3633
  }
1599
3634
 
1600
- function ActivityDateControl({instanceId: instanceId, state: state, dates: dates}) {
1601
- const {editFieldFor: editFieldFor} = index.useWorkflowContext(), {save: save} = index.useSaveField(), [open, setOpen] = react.useState(!1), raw = index.dateControlValue({
3635
+ function ActivityDateControl({instanceId: instanceId, state: state, surface: surface, dates: dates}) {
3636
+ const editField = index.useEditField(surface), {save: save} = index.useSaveField({
3637
+ instanceId: instanceId
3638
+ }), [open, setOpen] = react.useState(!1), raw = index.dateControlValue({
1602
3639
  state: state,
1603
3640
  dates: dates
1604
3641
  }), display = raw === void 0 ? null : /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
@@ -1621,15 +3658,16 @@ function ActivityDateControl({instanceId: instanceId, state: state, dates: dates
1621
3658
  })
1622
3659
  });
1623
3660
  const kind = index.dateControlKind(state), commit = (mode, value) => {
1624
- state.kind === "editable" && save(() => editFieldFor(instanceId, {
1625
- target: workflowReact.editFieldTarget(state.field),
3661
+ state.kind === "editable" && save(() => editField({
3662
+ instanceId: instanceId,
3663
+ field: state.field,
1626
3664
  ...mode === "set" ? {
1627
3665
  mode: mode,
1628
3666
  value: value
1629
3667
  } : {
1630
3668
  mode: mode
1631
3669
  }
1632
- }).then(() => {}));
3670
+ }));
1633
3671
  }, handleClick = e => {
1634
3672
  e.stopPropagation(), setOpen(v => !v);
1635
3673
  };
@@ -1646,9 +3684,9 @@ function ActivityDateControl({instanceId: instanceId, state: state, dates: dates
1646
3684
  setOpen(!1), commit("unset");
1647
3685
  },
1648
3686
  onPick: next => {
1649
- kind === "date" && setOpen(!1), commit("set", index.serializeDateFieldValue(next, kind));
3687
+ index.hasTimeOfDay(kind) || setOpen(!1), commit("set", index.serializeDateFieldValue(next, kind));
1650
3688
  },
1651
- selectTime: kind === "datetime",
3689
+ selectTime: index.hasTimeOfDay(kind),
1652
3690
  value: index.parseDateFieldValue(state.field.value, kind)
1653
3691
  })
1654
3692
  }),
@@ -1712,7 +3750,8 @@ function RefChips({refs: refs, max: max = 2}) {
1712
3750
  wrap: "wrap",
1713
3751
  children: [ shown.map(ref => /* @__PURE__ */ jsxRuntime.jsx(index.DocPreviewLink, {
1714
3752
  gdr: ref,
1715
- layout: "inline"
3753
+ layout: "inline",
3754
+ source: "doc-ref-chip"
1716
3755
  }, ref.id)), overflow > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, {
1717
3756
  muted: !0,
1718
3757
  size: 1,
@@ -1721,57 +3760,18 @@ function RefChips({refs: refs, max: max = 2}) {
1721
3760
  });
1722
3761
  }
1723
3762
 
1724
- const EMPTY_NOTE = "No tasks match — adjust the filters, or start a workflow", UNGROUPED_TITLE = "Workflows without Studio documents";
3763
+ const UNGROUPED_TITLE = "Workflows without Studio documents";
1725
3764
 
1726
- function TaskGroupList({bands: bands, groups: groups, instanceDefaultOpen: instanceDefaultOpen, onOpenTask: onOpenTask, onOpenWorkflow: onOpenWorkflow, showTerminalActions: showTerminalActions}) {
1727
- return groups.withDocuments.length === 0 && groups.withoutDocuments.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(MutedNote, {
1728
- text: EMPTY_NOTE
1729
- }) : /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, {
1730
- gap: 2,
1731
- children: [ groups.withDocuments.map(group => /* @__PURE__ */ jsxRuntime.jsx(index.CollapsibleBand, {
1732
- background: !0,
1733
- onToggle: () => bands.toggle(group.key, GROUP_DEFAULT_OPEN),
1734
- open: bands.isOpen(group.key, GROUP_DEFAULT_OPEN),
1735
- sticky: !0,
1736
- title: workflowEngine.toBareId(group.document.id),
1737
- header: /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, {
1738
- align: "center",
1739
- flex: "none",
1740
- style: {
1741
- maxWidth: "60%"
1742
- },
1743
- children: /* @__PURE__ */ jsxRuntime.jsx(RefChips, {
1744
- max: 2,
1745
- refs: group.documents
1746
- })
1747
- }),
1748
- children: /* @__PURE__ */ jsxRuntime.jsx(InstanceBands, {
1749
- bands: bands,
1750
- instanceDefaultOpen: instanceDefaultOpen,
1751
- instances: group.instances,
1752
- onOpenTask: onOpenTask,
1753
- onOpenWorkflow: onOpenWorkflow,
1754
- showTerminalActions: showTerminalActions
1755
- })
1756
- }, group.key)), groups.withoutDocuments.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(index.CollapsibleBand, {
1757
- background: !0,
1758
- onToggle: () => bands.toggle(UNGROUPED_BAND_KEY, GROUP_DEFAULT_OPEN),
1759
- open: bands.isOpen(UNGROUPED_BAND_KEY, GROUP_DEFAULT_OPEN),
1760
- sticky: !0,
1761
- title: UNGROUPED_TITLE,
1762
- header: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
1763
- size: 1,
1764
- weight: "semibold",
1765
- children: UNGROUPED_TITLE
1766
- }),
1767
- children: /* @__PURE__ */ jsxRuntime.jsx(InstanceBands, {
1768
- bands: bands,
1769
- instanceDefaultOpen: instanceDefaultOpen,
1770
- instances: groups.withoutDocuments,
1771
- onOpenTask: onOpenTask,
1772
- onOpenWorkflow: onOpenWorkflow,
1773
- showTerminalActions: showTerminalActions
1774
- })
3765
+ function TaskGroupList({groups: groups, ...wiring}) {
3766
+ /* @__PURE__ */
3767
+ return jsxRuntime.jsxs(ui.Stack, {
3768
+ gap: 1,
3769
+ children: [ groups.withDocuments.map(group => /* @__PURE__ */ jsxRuntime.jsx(DocumentBand, {
3770
+ group: group,
3771
+ ...wiring
3772
+ }, group.key)), groups.withoutDocuments.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(UngroupedBand, {
3773
+ instances: groups.withoutDocuments,
3774
+ ...wiring
1775
3775
  }) : null ]
1776
3776
  });
1777
3777
  }
@@ -1784,38 +3784,81 @@ function targetOf(row) {
1784
3784
  };
1785
3785
  }
1786
3786
 
1787
- function InstanceBands({bands: bands, instanceDefaultOpen: instanceDefaultOpen, instances: instances, onOpenTask: onOpenTask, onOpenWorkflow: onOpenWorkflow, showTerminalActions: showTerminalActions}) {
3787
+ function DocumentBand({group: group, ...wiring}) {
3788
+ const {open: open, toggle: toggle} = useBandOpen(wiring.bands, group.key);
3789
+ /* @__PURE__ */
3790
+ return jsxRuntime.jsx(index.CollapsibleBand, {
3791
+ background: !0,
3792
+ onToggle: toggle,
3793
+ open: open,
3794
+ sticky: !0,
3795
+ title: workflowEngine.toBareId(group.document.id),
3796
+ header: /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, {
3797
+ align: "center",
3798
+ flex: "none",
3799
+ style: {
3800
+ maxWidth: "60%"
3801
+ },
3802
+ children: /* @__PURE__ */ jsxRuntime.jsx(RefChips, {
3803
+ max: 2,
3804
+ refs: group.documents
3805
+ })
3806
+ }),
3807
+ children: /* @__PURE__ */ jsxRuntime.jsx(InstanceBands, {
3808
+ instances: group.instances,
3809
+ ...wiring
3810
+ })
3811
+ });
3812
+ }
3813
+
3814
+ function UngroupedBand({instances: instances, ...wiring}) {
3815
+ const {open: open, toggle: toggle} = useBandOpen(wiring.bands, UNGROUPED_BAND_KEY);
3816
+ /* @__PURE__ */
3817
+ return jsxRuntime.jsx(index.CollapsibleBand, {
3818
+ background: !0,
3819
+ onToggle: toggle,
3820
+ open: open,
3821
+ sticky: !0,
3822
+ title: UNGROUPED_TITLE,
3823
+ header: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
3824
+ size: 1,
3825
+ weight: "semibold",
3826
+ children: UNGROUPED_TITLE
3827
+ }),
3828
+ children: /* @__PURE__ */ jsxRuntime.jsx(InstanceBands, {
3829
+ instances: instances,
3830
+ ...wiring
3831
+ })
3832
+ });
3833
+ }
3834
+
3835
+ function InstanceBands({instances: instances, ...wiring}) {
1788
3836
  /* @__PURE__ */
1789
3837
  return jsxRuntime.jsx(ui.Stack, {
1790
3838
  gap: 1,
1791
- children: instances.map(band => /* @__PURE__ */ jsxRuntime.jsx(InstanceBand, {
1792
- band: band,
1793
- bands: bands,
1794
- instanceDefaultOpen: instanceDefaultOpen,
1795
- onOpenTask: onOpenTask,
1796
- onOpenWorkflow: onOpenWorkflow,
1797
- showTerminalActions: showTerminalActions
1798
- }, band.instanceId))
3839
+ children: instances.map(instance => /* @__PURE__ */ jsxRuntime.jsx(InstanceBand, {
3840
+ instance: instance,
3841
+ ...wiring
3842
+ }, instance.instanceId))
1799
3843
  });
1800
3844
  }
1801
3845
 
1802
- function InstanceBand({band: band, bands: bands, instanceDefaultOpen: instanceDefaultOpen, onOpenTask: onOpenTask, onOpenWorkflow: onOpenWorkflow, showTerminalActions: showTerminalActions}) {
1803
- const {resolvePathFromState: resolvePathFromState} = router.useRouter();
3846
+ function InstanceBand({bands: bands, instance: instance, onOpenTask: onOpenTask, onOpenWorkflow: onOpenWorkflow, showTerminalActions: showTerminalActions}) {
3847
+ const {open: open, toggle: toggle} = useBandOpen(bands, instance.instanceId), {resolvePathFromState: resolvePathFromState} = router.useRouter();
1804
3848
  /* @__PURE__ */
1805
3849
  return jsxRuntime.jsx(index.CollapsibleBand, {
1806
- onToggle: () => bands.toggle(band.instanceId, instanceDefaultOpen),
1807
- open: bands.isOpen(band.instanceId, instanceDefaultOpen),
1808
- title: band.title,
3850
+ onToggle: toggle,
3851
+ open: open,
3852
+ title: instance.title,
1809
3853
  header: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, {
1810
3854
  children: [
1811
3855
  /* @__PURE__ */ jsxRuntime.jsx(index.LinkChip, {
1812
3856
  hint: "View workflow",
1813
3857
  href: resolvePathFromState({
1814
- instanceId: band.instanceId
3858
+ instanceId: instance.instanceId
1815
3859
  }),
1816
3860
  onClick: event => {
1817
- event.metaKey || event.ctrlKey || event.shiftKey || event.altKey || (event.preventDefault(),
1818
- onOpenWorkflow(band.instanceId));
3861
+ isPlainClick(event) && (event.preventDefault(), onOpenWorkflow(instance.instanceId));
1819
3862
  },
1820
3863
  children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, {
1821
3864
  align: "center",
@@ -1831,12 +3874,12 @@ function InstanceBand({band: band, bands: bands, instanceDefaultOpen: instanceDe
1831
3874
  },
1832
3875
  textOverflow: "ellipsis",
1833
3876
  weight: "medium",
1834
- children: band.title
1835
- }), band.breadcrumb === void 0 ? null : /* @__PURE__ */ jsxRuntime.jsx(index.BreadcrumbTail, {
1836
- segments: [ band.breadcrumb ]
3877
+ children: instance.title
3878
+ }), instance.breadcrumb === void 0 ? null : /* @__PURE__ */ jsxRuntime.jsx(index.BreadcrumbTail, {
3879
+ segments: [ instance.breadcrumb ]
1837
3880
  }),
1838
3881
  /* @__PURE__ */ jsxRuntime.jsx(index.StageFace, {
1839
- title: band.stageTitle
3882
+ title: instance.stageTitle
1840
3883
  }) ]
1841
3884
  })
1842
3885
  }),
@@ -1846,7 +3889,7 @@ function InstanceBand({band: band, bands: bands, instanceDefaultOpen: instanceDe
1846
3889
  paddingLeft: 5,
1847
3890
  children: /* @__PURE__ */ jsxRuntime.jsx(TaskRows, {
1848
3891
  onOpenTask: onOpenTask,
1849
- rows: band.rows,
3892
+ rows: instance.rows,
1850
3893
  showTerminalActions: showTerminalActions
1851
3894
  })
1852
3895
  })
@@ -1880,40 +3923,19 @@ function TaskActivityRow({row: row, onOpen: onOpen, showTerminalActions: showTer
1880
3923
  dateControl: /* @__PURE__ */ jsxRuntime.jsx(ActivityDateControl, {
1881
3924
  dates: render.dates,
1882
3925
  instanceId: row.instanceId,
1883
- state: render.dateState
3926
+ state: render.dateState,
3927
+ surface: "tool-task-list"
1884
3928
  }),
1885
3929
  face: render.face,
1886
3930
  instanceId: row.instanceId,
1887
3931
  onOpen: onOpen,
3932
+ surface: "tool-task-list",
1888
3933
  terminalActions: showTerminalActions ? render.terminalActions : []
1889
3934
  })
1890
3935
  })
1891
3936
  });
1892
3937
  }
1893
3938
 
1894
- function useBandOpenState(args) {
1895
- const {segment: segment, scope: scope} = args, store = react.useMemo(() => createBandStateStore({
1896
- storageKey: bandStateStorageKey(scope)
1897
- }), [ scope ]), [held, setHeld] = react.useState(() => ({
1898
- store: store,
1899
- toggles: store.read()
1900
- }));
1901
- held.store !== store && setHeld({
1902
- store: store,
1903
- toggles: store.read()
1904
- });
1905
- const qualify = key => `${segment}/${key}`, isOpen = (key, defaultOpen) => held.toggles.get(qualify(key)) ?? defaultOpen;
1906
- return {
1907
- isOpen: isOpen,
1908
- toggle: (key, defaultOpen) => {
1909
- setHeld({
1910
- store: store,
1911
- toggles: store.setOpen(qualify(key), !isOpen(key, defaultOpen))
1912
- });
1913
- }
1914
- };
1915
- }
1916
-
1917
3939
  const NO_MISSING = /* @__PURE__ */ new Set;
1918
3940
 
1919
3941
  function sameIdSet(a, b) {
@@ -1946,28 +3968,6 @@ function useOrphanedWorkflows(groups) {
1946
3968
  }), [ groups, contentResource, missing ]);
1947
3969
  }
1948
3970
 
1949
- const INSTANCE_CAP = 200, NEWEST_INSTANCES = {
1950
- includeCompleted: !0,
1951
- limit: INSTANCE_CAP
1952
- };
1953
-
1954
- function useToolInstances() {
1955
- const {engine: engine} = index.useWorkflowContext(), {instances: instances, loading: loading, unreadable: unreadable} = workflowStudio.useWorkflowInstances({
1956
- engine: engine,
1957
- filter: NEWEST_INSTANCES
1958
- });
1959
- return react.useMemo(() => {
1960
- const rows = instances ?? [];
1961
- return {
1962
- inFlight: rows.filter(instance => workflowEngine.terminalState(instance) === "in-flight"),
1963
- settled: rows.filter(instance => workflowEngine.terminalState(instance) !== "in-flight"),
1964
- truncated: rows.length + unreadable.length === INSTANCE_CAP,
1965
- loading: loading,
1966
- unreadable: unreadable
1967
- };
1968
- }, [ instances, loading, unreadable ]);
1969
- }
1970
-
1971
3971
  function TasksTab({filterSlot: filterSlot, instances: instances, loading: loading, identity: identity, onOpenTask: onOpenTask, onOpenWorkflow: onOpenWorkflow, segment: segment, truncated: truncated, unreadable: unreadable}) {
1972
3972
  const mineOnly = segment === "for-me", {binding: binding} = index.useWorkflowContext(), bands = useBandOpenState({
1973
3973
  segment: segment,
@@ -1975,21 +3975,38 @@ function TasksTab({filterSlot: filterSlot, instances: instances, loading: loadin
1975
3975
  }), [filters, setFilters] = react.useState({}), groups = react.useMemo(() => deriveTaskGroups({
1976
3976
  instances: instances,
1977
3977
  identity: identity
1978
- }), [ instances, identity ]), orphans = useOrphanedWorkflows(groups), listed = react.useMemo(() => hideOrphanedGroups(groups, orphans.groupKeys), [ groups, orphans.groupKeys ]), options = react.useMemo(() => taskFilterOptions(listed), [ listed ]), visible = react.useMemo(() => {
3978
+ }), [ instances, identity ]), orphans = useOrphanedWorkflows(groups), listed = react.useMemo(() => hideOrphanedGroups(groups, orphans.groupKeys), [ groups, orphans.groupKeys ]), options = react.useMemo(() => taskFilterOptions(listed), [ listed ]), titles = useGroupTitles(listed), revealed = useRevealGate({
3979
+ loading: loading,
3980
+ ready: titles.ready
3981
+ }), visible = react.useMemo(() => {
1979
3982
  const filtered = applyTaskFilters({
1980
3983
  groups: listed,
1981
3984
  filters: filters,
1982
3985
  now: /* @__PURE__ */ new Date
1983
- });
1984
- return mineOnly ? onlyMyTasks(filtered) : filtered;
1985
- }, [ listed, filters, mineOnly ]);
1986
- return loading ?
1987
- /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
1988
- paddingLeft: 2,
3986
+ }), cut = mineOnly ? onlyMyTasks(filtered) : filtered;
3987
+ return orderGroupsByPreviewTitle(cut, titles.titles);
3988
+ }, [ listed, filters, mineOnly, titles ]);
3989
+ if (loading || !revealed) /* @__PURE__ */
3990
+ return jsxRuntime.jsx(TabStatusRow, {
1989
3991
  children: /* @__PURE__ */ jsxRuntime.jsx(index.LoadingRow, {
1990
3992
  label: "Loading tasks…"
1991
3993
  })
1992
- }) : /* @__PURE__ */ jsxRuntime.jsxs(ui.Box, {
3994
+ });
3995
+ const empty = visible.withDocuments.length === 0 && visible.withoutDocuments.length === 0;
3996
+ let body;
3997
+ return empty ? mineOnly ? body = /* @__PURE__ */ jsxRuntime.jsx(index.ForMeEmptyState, {}) : body = /* @__PURE__ */ jsxRuntime.jsx(index.EmptyState, {
3998
+ description: "Adjust the filters, or start a workflow",
3999
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Search.SearchIcon, {}),
4000
+ title: "No tasks match"
4001
+ }) : body = /* @__PURE__ */ jsxRuntime.jsx(TaskGroupList, {
4002
+ bands: bands,
4003
+ groups: visible,
4004
+ onOpenTask: onOpenTask,
4005
+ onOpenWorkflow: onOpenWorkflow,
4006
+ showTerminalActions: mineOnly
4007
+ }), /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, {
4008
+ direction: "column",
4009
+ flex: 1,
1993
4010
  children: [
1994
4011
  /* @__PURE__ */ jsxRuntime.jsx(FilterRowPortal, {
1995
4012
  filters: filters,
@@ -1997,91 +4014,133 @@ function TasksTab({filterSlot: filterSlot, instances: instances, loading: loadin
1997
4014
  options: options,
1998
4015
  slot: filterSlot
1999
4016
  }),
2000
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, {
4017
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, {
4018
+ direction: "column",
4019
+ flex: 1,
2001
4020
  gap: 2,
4021
+ paddingBottom: 4,
2002
4022
  children: [
2003
4023
  /* @__PURE__ */ jsxRuntime.jsx(index.UnreadableDocsNote, {
2004
4024
  unreadable: unreadable
2005
4025
  }),
2006
4026
  /* @__PURE__ */ jsxRuntime.jsx(OrphanedWorkflowsNote, {
2007
4027
  orphans: orphans
2008
- }),
2009
- /* @__PURE__ */ jsxRuntime.jsx(TaskGroupList, {
2010
- bands: bands,
2011
- groups: visible,
2012
- instanceDefaultOpen: instanceBandDefaultOpen(segment),
2013
- onOpenTask: onOpenTask,
2014
- onOpenWorkflow: onOpenWorkflow,
2015
- showTerminalActions: mineOnly
2016
- }) ]
2017
- }), truncated ? /* @__PURE__ */ jsxRuntime.jsx(MutedNote, {
2018
- text: `Tasks from the latest ${INSTANCE_CAP} workflows — older ones aren't listed.`
2019
- }) : null ]
4028
+ }), body, truncated ? /* @__PURE__ */ jsxRuntime.jsx(MutedNote, {
4029
+ text: `Tasks from the latest ${INSTANCE_CAP} workflows — older ones aren’t listed.`
4030
+ }) : null ]
4031
+ }) ]
2020
4032
  });
2021
4033
  }
2022
4034
 
2023
- const TOOL_TABS = [ {
2024
- value: "overview",
2025
- label: "Overview"
2026
- }, {
2027
- value: "for-me",
2028
- label: "For me"
2029
- } ], [firstToolTab, ...restToolTabs] = TOOL_TABS, TOOL_TAB_CODEC = index.workflowsTabCodec([ firstToolTab.value, ...restToolTabs.map(tab => tab.value) ]);
4035
+ const TOOL_TAB_LABELS = {
4036
+ definitions: "Definitions",
4037
+ documents: "Documents",
4038
+ tasks: "Tasks",
4039
+ "for-me": "For me"
4040
+ }, TOOL_TAB_VALUES = Object.keys(TOOL_TAB_LABELS), TOOL_TABS = TOOL_TAB_VALUES.map(value => ({
4041
+ value: value,
4042
+ label: TOOL_TAB_LABELS[value]
4043
+ })), DEFAULT_TOOL_TAB = "definitions";
4044
+
4045
+ function toolTabState(tab) {
4046
+ return tab === DEFAULT_TOOL_TAB ? {} : {
4047
+ workflowsTab: tab
4048
+ };
4049
+ }
2030
4050
 
2031
- function useToolTab() {
2032
- const router$1 = router.useRouter(), params = Object.fromEntries(router$1.state._searchParams ?? []);
4051
+ function boardState(definitionName) {
2033
4052
  return {
2034
- tab: TOOL_TAB_CODEC.read(params),
2035
- setTab: next => router$1.navigate({
2036
- _searchParams: definedEntries(TOOL_TAB_CODEC.write(params, next))
2037
- })
4053
+ boardDefinition: definitionName
2038
4054
  };
2039
4055
  }
2040
4056
 
2041
- function definedEntries(params) {
2042
- return Object.entries(params).filter(entry => entry[1] !== void 0);
4057
+ function tabSelectionNavigates(route, tab) {
4058
+ return !(route.kind === "home" && tab === route.tab);
4059
+ }
4060
+
4061
+ function backToAllLabel(tab) {
4062
+ return `Back to all ${TOOL_TAB_LABELS[tab].toLowerCase()}`;
2043
4063
  }
2044
4064
 
2045
- function carryingSearchParams(state, searchParams) {
2046
- return searchParams === void 0 ? state : {
2047
- ...state,
2048
- _searchParams: searchParams
4065
+ function toolRoute(state) {
4066
+ const {instanceId: instanceId, definitionName: definitionName, boardDefinition: boardDefinition} = state;
4067
+ if (typeof instanceId == "string") return {
4068
+ kind: "instance",
4069
+ instanceId: instanceId,
4070
+ tab: "tasks"
4071
+ };
4072
+ if (typeof definitionName == "string") return {
4073
+ kind: "definition",
4074
+ definitionName: definitionName,
4075
+ tab: "definitions"
4076
+ };
4077
+ if (typeof boardDefinition == "string") return {
4078
+ kind: "home",
4079
+ tab: "documents",
4080
+ boardDefinition: boardDefinition
4081
+ };
4082
+ const tab = index.pathSegmentTab({
4083
+ segment: state.workflowsTab,
4084
+ tabs: TOOL_TAB_VALUES,
4085
+ fallback: DEFAULT_TOOL_TAB
4086
+ });
4087
+ return tab === void 0 ? {
4088
+ kind: "not-found",
4089
+ tab: void 0
4090
+ } : {
4091
+ kind: "home",
4092
+ tab: tab,
4093
+ boardDefinition: void 0
2049
4094
  };
2050
4095
  }
2051
4096
 
4097
+ function InstanceStreamKeepAlive() {
4098
+ return useToolInstances(), null;
4099
+ }
4100
+
2052
4101
  function WorkflowsToolRoot() {
2053
- const router$1 = router.useRouter(), {seedInstance: seedInstance} = index.useWorkflowContext(), {tab: tab} = useToolTab();
2054
- index.useLogEventOnMount(index.WorkflowToolOpened, {
2055
- tab: tab,
4102
+ const router$1 = router.useRouter(), {seedInstance: seedInstance} = index.useWorkflowContext(), telemetry = workflowReact.useWorkflowTelemetry(), [filterSlot, setFilterSlot] = react.useState(null), route = toolRoute(router$1.state), isHome = route.kind === "home" || route.kind === "not-found", [instanceStreamLatched, setInstanceStreamLatched] = react.useState(isHome);
4103
+ react.useEffect(() => {
4104
+ isHome && setInstanceStreamLatched(!0);
4105
+ }, [ isHome ]);
4106
+ const setTab = next => router$1.navigate(toolTabState(next)), selectBoardWorkflow = react.useCallback((name, opts) => router$1.navigate(boardState(name), opts?.replace === !0 ? {
4107
+ replace: !0
4108
+ } : {}), [ router$1 ]), backToList = tab => ({
4109
+ label: backToAllLabel(tab),
4110
+ go: () => setTab(tab)
4111
+ }), badSegment = route.kind === "not-found";
4112
+ react.useEffect(() => {
4113
+ badSegment && router$1.navigate({}, {
4114
+ replace: !0
4115
+ });
4116
+ }, [ badSegment, router$1 ]), index.useLogEventOnMount(index.WorkflowToolOpened, {
4117
+ tab: route.tab ?? "not-found",
2056
4118
  via: "open"
2057
4119
  });
2058
- const instanceId = typeof router$1.state.instanceId == "string" ? router$1.state.instanceId : void 0;
2059
- return instanceId ? /* @__PURE__ */ jsxRuntime.jsx(WorkflowInstanceDetail, {
2060
- instanceId: instanceId,
2061
- onBack: () => router$1.navigate(carryingSearchParams({}, router$1.state._searchParams))
2062
- }, instanceId) : /* @__PURE__ */ jsxRuntime.jsx(WorkflowsHome, {
4120
+ let panel;
4121
+ return route.kind === "instance" ? panel = /* @__PURE__ */ jsxRuntime.jsx(WorkflowInstanceDetail, {
4122
+ instanceId: route.instanceId,
4123
+ backToList: backToList(route.tab)
4124
+ }, route.instanceId) : route.kind === "definition" ? panel = /* @__PURE__ */ jsxRuntime.jsx(DefinitionDetail, {
4125
+ definitionName: route.definitionName,
4126
+ backToList: backToList(route.tab)
4127
+ }, route.definitionName) : route.kind === "not-found" ? panel = null : panel = /* @__PURE__ */ jsxRuntime.jsx(WorkflowsHome, {
4128
+ boardDefinition: route.boardDefinition,
4129
+ filterSlot: filterSlot,
4130
+ onOpenDefinition: name => router$1.navigate({
4131
+ definitionName: name
4132
+ }),
2063
4133
  onOpenInstance: instance => {
2064
- typeof instance != "string" && seedInstance(instance), router$1.navigate(carryingSearchParams({
4134
+ typeof instance != "string" && seedInstance(instance), router$1.navigate({
2065
4135
  instanceId: typeof instance == "string" ? instance : instance._id
2066
- }, router$1.state._searchParams));
2067
- }
2068
- });
2069
- }
2070
-
2071
- function WorkflowsHome({onOpenInstance: onOpenInstance}) {
2072
- const {tab: tab, setTab: setTab} = useToolTab(), telemetry = workflowReact.useWorkflowTelemetry(), instances = useToolInstances(), identity = index.useAssignmentIdentity(), [openTask, setOpenTask] = react.useState(null), openInstance = target => {
2073
- if (typeof target != "string") return onOpenInstance(target);
2074
- const held = [ ...instances.inFlight, ...instances.settled ].find(i => i._id === target);
2075
- onOpenInstance(held ?? target);
2076
- }, [filterSlot, setFilterSlot] = react.useState(null), scrollerRef = react.useRef(null);
2077
- return react.useEffect(() => {
2078
- scrollerRef.current?.scrollTo({
2079
- top: 0
2080
- });
2081
- }, [ tab ]),
4136
+ });
4137
+ },
4138
+ onSelectWorkflow: selectBoardWorkflow,
4139
+ tab: route.tab
4140
+ }),
2082
4141
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Card, {
2083
4142
  height: "fill",
2084
- children: [
4143
+ children: [ instanceStreamLatched ? /* @__PURE__ */ jsxRuntime.jsx(InstanceStreamKeepAlive, {}) : null,
2085
4144
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, {
2086
4145
  direction: "column",
2087
4146
  height: "fill",
@@ -2090,75 +4149,103 @@ function WorkflowsHome({onOpenInstance: onOpenInstance}) {
2090
4149
  /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
2091
4150
  flex: "none",
2092
4151
  padding: 3,
2093
- children: /* @__PURE__ */ jsxRuntime.jsx(ui.Container, {
2094
- width: 2,
2095
- children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, {
4152
+ children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, {
4153
+ gap: 3,
4154
+ children: [
4155
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, {
4156
+ align: "center",
2096
4157
  gap: 3,
4158
+ paddingLeft: 2,
2097
4159
  children: [
2098
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, {
2099
- align: "center",
2100
- gap: 3,
2101
- paddingLeft: 2,
2102
- children: [
2103
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
2104
- size: 2,
2105
- weight: "semibold",
2106
- children: "Workflows"
2107
- }),
2108
- /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
2109
- flex: 1
2110
- }),
2111
- /* @__PURE__ */ jsxRuntime.jsx(NewWorkflowButton, {}) ]
4160
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
4161
+ size: 1,
4162
+ weight: "semibold",
4163
+ children: "Workflows"
2112
4164
  }),
2113
- /* @__PURE__ */ jsxRuntime.jsx(index.TabSwitch, {
2114
- ariaControls: "workflows-tool-panel",
2115
- idPrefix: "workflows-tool-tab",
2116
- onSelect: next => {
2117
- next !== tab && telemetry.log(index.WorkflowToolOpened, {
2118
- tab: next,
2119
- via: "tab-switch"
2120
- }), setTab(next);
2121
- },
2122
- options: TOOL_TABS,
2123
- selected: tab
4165
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
4166
+ flex: 1
2124
4167
  }),
2125
- /* @__PURE__ */ jsxRuntime.jsx("div", {
2126
- ref: setFilterSlot
2127
- }) ]
2128
- })
4168
+ /* @__PURE__ */ jsxRuntime.jsx(NewWorkflowButton, {}) ]
4169
+ }),
4170
+ /* @__PURE__ */ jsxRuntime.jsx(index.TabSwitch, {
4171
+ ariaControls: "workflows-tool-panel",
4172
+ idPrefix: "workflows-tool-tab",
4173
+ onSelect: next => {
4174
+ tabSelectionNavigates(route, next) && (next !== route.tab && telemetry.log(index.WorkflowToolOpened, {
4175
+ tab: next,
4176
+ via: "tab-switch"
4177
+ }), setTab(next));
4178
+ },
4179
+ options: TOOL_TABS,
4180
+ selected: route.tab
4181
+ }),
4182
+ /* @__PURE__ */ jsxRuntime.jsx("div", {
4183
+ ref: setFilterSlot
4184
+ }) ]
2129
4185
  })
2130
4186
  }),
2131
4187
  /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
2132
- ref: scrollerRef,
2133
4188
  flex: 1,
2134
- overflow: "auto",
2135
- paddingX: 3,
4189
+ overflow: "hidden",
4190
+ children: panel
4191
+ }) ]
4192
+ }) ]
4193
+ });
4194
+ }
4195
+
4196
+ function WorkflowsHome({boardDefinition: boardDefinition, filterSlot: filterSlot, onOpenDefinition: onOpenDefinition, onOpenInstance: onOpenInstance, onSelectWorkflow: onSelectWorkflow, tab: tab}) {
4197
+ const instances = useToolInstances(), identity = index.useAssignmentIdentity(), [openTask, setOpenTask] = react.useState(null), openInstance = target => {
4198
+ if (typeof target != "string") return onOpenInstance(target);
4199
+ const held = [ ...instances.inFlight, ...instances.settled ].find(i => i._id === target);
4200
+ onOpenInstance(held ?? target);
4201
+ }, scrollerRef = react.useRef(null);
4202
+ react.useEffect(() => {
4203
+ scrollerRef.current?.scrollTo({
4204
+ top: 0
4205
+ });
4206
+ }, [ tab ]);
4207
+ const bleeds = tab === "documents";
4208
+ /* @__PURE__ */
4209
+ return jsxRuntime.jsxs(ui.Flex, {
4210
+ direction: "column",
4211
+ height: "fill",
4212
+ overflow: "hidden",
4213
+ children: [
4214
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
4215
+ ref: scrollerRef,
4216
+ flex: 1,
4217
+ overflow: "auto",
4218
+ paddingX: bleeds ? 0 : TOOL_PANEL_PADDING,
4219
+ style: {
4220
+ scrollbarGutter: bleeds ? "auto" : "stable"
4221
+ },
4222
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.TabPanel, {
4223
+ "aria-labelledby": `workflows-tool-tab-${tab}`,
4224
+ id: "workflows-tool-panel",
2136
4225
  style: {
2137
- scrollbarGutter: "stable"
4226
+ display: "flex",
4227
+ flexDirection: "column",
4228
+ minHeight: "100%"
2138
4229
  },
2139
- children: /* @__PURE__ */ jsxRuntime.jsx(ui.Container, {
2140
- width: 2,
2141
- children: /* @__PURE__ */ jsxRuntime.jsx(ui.TabPanel, {
2142
- "aria-labelledby": `workflows-tool-tab-${tab}`,
2143
- id: "workflows-tool-panel",
2144
- children: /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
2145
- paddingBottom: 4,
2146
- paddingTop: 1,
2147
- children: /* @__PURE__ */ jsxRuntime.jsx(TasksTab, {
2148
- filterSlot: filterSlot,
2149
- identity: identity,
2150
- instances: [ ...instances.inFlight, ...instances.settled ],
2151
- loading: instances.loading,
2152
- onOpenTask: setOpenTask,
2153
- onOpenWorkflow: openInstance,
2154
- segment: tab,
2155
- truncated: instances.truncated,
2156
- unreadable: instances.unreadable
2157
- })
2158
- })
4230
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, {
4231
+ direction: "column",
4232
+ flex: 1,
4233
+ style: {
4234
+ minHeight: 0
4235
+ },
4236
+ children: /* @__PURE__ */ jsxRuntime.jsx(HomePanel, {
4237
+ boardDefinition: boardDefinition,
4238
+ filterSlot: filterSlot,
4239
+ identity: identity,
4240
+ instances: instances,
4241
+ onOpenDefinition: onOpenDefinition,
4242
+ onOpenTask: setOpenTask,
4243
+ onOpenWorkflow: openInstance,
4244
+ onSelectWorkflow: onSelectWorkflow,
4245
+ tab: tab
2159
4246
  })
2160
4247
  })
2161
- }) ]
4248
+ })
2162
4249
  }), openTask ? /* @__PURE__ */ jsxRuntime.jsx(ToolActivityDialog, {
2163
4250
  onClose: () => setOpenTask(null),
2164
4251
  target: openTask
@@ -2166,4 +4253,25 @@ function WorkflowsHome({onOpenInstance: onOpenInstance}) {
2166
4253
  });
2167
4254
  }
2168
4255
 
4256
+ function HomePanel({boardDefinition: boardDefinition, filterSlot: filterSlot, identity: identity, instances: instances, onOpenDefinition: onOpenDefinition, onOpenTask: onOpenTask, onOpenWorkflow: onOpenWorkflow, onSelectWorkflow: onSelectWorkflow, tab: tab}) {
4257
+ return tab === "definitions" ? /* @__PURE__ */ jsxRuntime.jsx(DefinitionsTab, {
4258
+ onOpenDefinition: onOpenDefinition
4259
+ }) : tab === "documents" ? /* @__PURE__ */ jsxRuntime.jsx(DocumentsTab, {
4260
+ filterSlot: filterSlot,
4261
+ onOpenDefinition: onOpenDefinition,
4262
+ onSelectWorkflow: onSelectWorkflow,
4263
+ routeDefinition: boardDefinition
4264
+ }) : /* @__PURE__ */ jsxRuntime.jsx(TasksTab, {
4265
+ filterSlot: filterSlot,
4266
+ identity: identity,
4267
+ instances: [ ...instances.inFlight, ...instances.settled ],
4268
+ loading: instances.loading,
4269
+ onOpenTask: onOpenTask,
4270
+ onOpenWorkflow: onOpenWorkflow,
4271
+ segment: tab,
4272
+ truncated: instances.truncated,
4273
+ unreadable: instances.unreadable
4274
+ });
4275
+ }
4276
+
2169
4277
  exports.default = WorkflowsToolRoot;