@sanity/workflow-studio-plugin 0.26.0 → 0.28.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,6 @@
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"), Clock = require("@sanity/icons/Clock"), Cog = require("@sanity/icons/Cog"), EllipsisHorizontal = require("@sanity/icons/EllipsisHorizontal"), workflowEngine = require("@sanity/workflow-engine"), ArrowLeft = require("@sanity/icons/ArrowLeft"), Transfer = require("@sanity/icons/Transfer"), WarningOutline = require("@sanity/icons/WarningOutline"), pluralize = require("pluralize-esm"), sanity = require("sanity"), workflowStudio = require("@sanity/workflow-studio"), Add = require("@sanity/icons/Add"), Search = require("@sanity/icons/Search"), 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"), types = require("@sanity/types"), workflowDiagram = require("@sanity/workflow-diagram"), ChevronDown = require("@sanity/icons/ChevronDown"), react$1 = require("motion/react");
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"), workflowEngine = require("@sanity/workflow-engine"), ArrowLeft = require("@sanity/icons/ArrowLeft"), Transfer = require("@sanity/icons/Transfer"), WarningOutline = require("@sanity/icons/WarningOutline"), pluralize = require("pluralize-esm"), sanity = require("sanity"), workflowStudio = require("@sanity/workflow-studio"), Add = require("@sanity/icons/Add"), Search = require("@sanity/icons/Search"), 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"), ChevronDown = require("@sanity/icons/ChevronDown"), ChevronRight = require("@sanity/icons/ChevronRight"), types = require("@sanity/types"), workflowDiagram = require("@sanity/workflow-diagram"), react$1 = require("motion/react");
4
4
 
5
5
  function _interopDefaultCompat(e) {
6
6
  return e && typeof e == "object" && "default" in e ? e : {
@@ -10,16 +10,6 @@ function _interopDefaultCompat(e) {
10
10
 
11
11
  var pluralize__default = /* @__PURE__ */ _interopDefaultCompat(pluralize);
12
12
 
13
- function CodeChip({value: value}) {
14
- /* @__PURE__ */
15
- return jsxRuntime.jsx("code", {
16
- style: {
17
- overflowWrap: "anywhere"
18
- },
19
- children: value
20
- });
21
- }
22
-
23
13
  function SectionHeading({children: children}) {
24
14
  /* @__PURE__ */
25
15
  return jsxRuntime.jsx(ui.Text, {
@@ -29,146 +19,6 @@ function SectionHeading({children: children}) {
29
19
  });
30
20
  }
31
21
 
32
- function normalizeAbortReason(raw) {
33
- const trimmed = raw.trim();
34
- return trimmed === "" ? void 0 : trimmed;
35
- }
36
-
37
- function abortCommitReport(args) {
38
- return args.changed ? {
39
- outcome: "aborted",
40
- toast: abortedToast(args.title)
41
- } : {
42
- outcome: "already-settled",
43
- toast: abortAlreadySettledToast(args.title)
44
- };
45
- }
46
-
47
- function abortedToast(title) {
48
- return {
49
- id: index.TOAST_ID.abort,
50
- status: "info",
51
- title: `Aborted “${title}”`
52
- };
53
- }
54
-
55
- function abortAlreadySettledToast(title) {
56
- return {
57
- id: index.TOAST_ID.abort,
58
- status: "info",
59
- title: `“${title}” had already finished`,
60
- description: "Nothing was aborted — the workflow reached a terminal state first."
61
- };
62
- }
63
-
64
- function abortUnconfirmedToast(args) {
65
- return {
66
- id: index.TOAST_ID.abort,
67
- status: "warning",
68
- title: `Couldn’t confirm “${args.title}” was aborted`,
69
- description: `${index.describeError(args.err)} — the abort may still have committed, so check the workflow’s state before trying again.`
70
- };
71
- }
72
-
73
- function AbortWorkflowDialog({entry: entry, onClose: onClose}) {
74
- const {engine: engine} = index.useWorkflowContext(), definition = index.useDefinition(entry), toast = index.useWorkflowToast(), telemetry = workflowReact.useWorkflowTelemetry(), [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}`;
75
- index.useLogEventOnMount(index.WorkflowAbortDialogOpened, {
76
- instanceId: instance._id
77
- });
78
- const confirm = async () => {
79
- if (reason === void 0) return;
80
- const log = outcome => telemetry.log(index.WorkflowAbortDialogSubmitted, {
81
- instanceId: instance._id,
82
- outcome: outcome
83
- });
84
- setPending(!0);
85
- try {
86
- const {changed: changed} = await engine.abortInstance({
87
- instanceId: instance._id,
88
- reason: reason
89
- }), report = abortCommitReport({
90
- title: title,
91
- changed: changed
92
- });
93
- toast.push(report.toast), log(report.outcome), onClose();
94
- } catch (err) {
95
- toast.push(abortUnconfirmedToast({
96
- title: title,
97
- err: err
98
- })), log("unconfirmed"), setPending(!1);
99
- }
100
- };
101
- /* @__PURE__ */
102
- return jsxRuntime.jsx(ui.Dialog, {
103
- footer: /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
104
- padding: 3,
105
- children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, {
106
- gap: 2,
107
- justify: "flex-end",
108
- children: [
109
- /* @__PURE__ */ jsxRuntime.jsx(ui.Button, {
110
- disabled: pending,
111
- fontSize: 1,
112
- mode: "bleed",
113
- onClick: onClose,
114
- padding: 2,
115
- text: "Cancel"
116
- }),
117
- /* @__PURE__ */ jsxRuntime.jsx(ui.Button, {
118
- disabled: reason === void 0,
119
- fontSize: 1,
120
- loading: pending,
121
- onClick: () => {
122
- confirm();
123
- },
124
- padding: 2,
125
- text: "Abort workflow",
126
- tone: "critical"
127
- }) ]
128
- })
129
- }),
130
- header: "Abort workflow",
131
- id: `abort-workflow-${instance._id}`,
132
- onClose: () => {
133
- pending || onClose();
134
- },
135
- width: 0,
136
- children: /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
137
- padding: 4,
138
- children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, {
139
- gap: 5,
140
- children: [
141
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, {
142
- size: 1,
143
- children: [ "Abort the ",
144
- /* @__PURE__ */ jsxRuntime.jsx("strong", {
145
- children: title
146
- }), " workflow? This cannot be undone." ]
147
- }),
148
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, {
149
- gap: 3,
150
- children: [
151
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
152
- as: "label",
153
- htmlFor: reasonInputId,
154
- size: 1,
155
- weight: "medium",
156
- children: "Reason"
157
- }),
158
- /* @__PURE__ */ jsxRuntime.jsx(ui.TextInput, {
159
- autoFocus: !0,
160
- fontSize: 1,
161
- id: reasonInputId,
162
- onChange: event => setTypedReason(event.currentTarget.value),
163
- placeholder: "Why is this workflow being stopped?",
164
- value: typedReason
165
- }) ]
166
- }) ]
167
- })
168
- })
169
- });
170
- }
171
-
172
22
  function AbortWorkflowSection({entry: entry}) {
173
23
  const [aborting, setAborting] = react.useState(!1);
174
24
  return index.isLiveEntry(entry) ? /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, {
@@ -186,368 +36,13 @@ function AbortWorkflowSection({entry: entry}) {
186
36
  text: "Abort workflow…",
187
37
  tone: "critical"
188
38
  })
189
- }), aborting ? /* @__PURE__ */ jsxRuntime.jsx(AbortWorkflowDialog, {
39
+ }), aborting ? /* @__PURE__ */ jsxRuntime.jsx(index.AbortWorkflowDialog, {
190
40
  entry: entry,
191
41
  onClose: () => setAborting(!1)
192
42
  }) : null ]
193
43
  }) : null;
194
44
  }
195
45
 
196
- function makeTitleResolver(definition) {
197
- const activities = (definition?.stages ?? []).flatMap(s => s.activities ?? []);
198
- return {
199
- action: (activityName, actionName) => activities.find(t => t.name === activityName)?.actions?.find(a => a.name === actionName)?.title ?? actionName,
200
- activity: name => activities.find(t => t.name === name)?.title ?? name,
201
- stage: name => index.stageTitle(definition, name)
202
- };
203
- }
204
-
205
- function attributed(actor, ...parts) {
206
- if (actor) return {
207
- actorId: actor.id,
208
- parts: parts
209
- };
210
- const [first, ...rest] = parts;
211
- return typeof first != "string" ? {
212
- actorId: void 0,
213
- parts: parts
214
- } : {
215
- actorId: void 0,
216
- parts: [ first.charAt(0).toUpperCase() + first.slice(1), ...rest ]
217
- };
218
- }
219
-
220
- function standalone(...parts) {
221
- return {
222
- actorId: void 0,
223
- parts: parts
224
- };
225
- }
226
-
227
- const historyLiner = {
228
- stageEntered: (h, titles) => attributed(h.actor, `entered ${titles.stage(h.stage)}${h.fromStage ? ` from ${titles.stage(h.fromStage)}` : ""}`),
229
- stageExited: (h, titles) => attributed(h.actor, `left ${titles.stage(h.stage)} for ${titles.stage(h.toStage)}`),
230
- activityStatusChanged: (h, titles) => attributed(h.actor, `changed ${titles.activity(h.activity)}: ${h.from} → ${h.to}`),
231
- actionFired: (h, titles) => {
232
- const phrase = `fired ${titles.action(h.activity, h.action)} on ${titles.activity(h.activity)}`;
233
- return h.triggered ? standalone(`The workflow ${phrase}`) : attributed(h.actor, phrase);
234
- },
235
- transitionFired: (h, titles) => attributed(h.actor, `moved ${titles.stage(h.fromStage)} → ${titles.stage(h.toStage)}`),
236
- effectQueued: h => standalone("Effect ", {
237
- code: h.effect
238
- }, " queued"),
239
- effectCompleted: h => {
240
- const detail = h.detail ? ` — ${h.detail}` : "";
241
- return h.actor ? attributed(h.actor, "resolved effect ", {
242
- code: h.effect
243
- }, ` — ${h.status}${detail}`) : standalone("Effect ", {
244
- code: h.effect
245
- }, ` ${h.status}${detail}`);
246
- },
247
- effectClaimReleased: h => attributed(h.actor, "released a stale claim on effect ", {
248
- code: h.effect
249
- }, ` ${h.via === "sweep" ? "via the sweeper" : "via a drain takeover"}`),
250
- spawned: (h, titles) => attributed(h.actor, `spawned a child workflow from ${titles.activity(h.activity)}`),
251
- subworkflowAdopted: (h, titles) => standalone(`Re-adopted child workflow into ${titles.activity(h.activity)} on re-entering ${titles.stage(h.stage)}`),
252
- subworkflowResolved: (h, titles) => standalone(`Child workflow of ${titles.activity(h.activity)} ${h.status === "done" ? "completed" : "was aborted"}`),
253
- subworkflowOrphaned: h => standalone(`Orphaned child workflow reached terminal — ${h.detail}`),
254
- aborted: (h, titles) => attributed(h.actor, `aborted the workflow at ${titles.stage(h.stage)}${h.reason ? ` — ${h.reason}` : ""}`),
255
- 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 ", {
256
- code: h.opType
257
- }),
258
- fieldQueryDiscarded: h => standalone({
259
- code: JSON.stringify(h)
260
- })
261
- };
262
-
263
- function historyLine(h, titles) {
264
- return historyLiner[h._type](h, titles);
265
- }
266
-
267
- function historyWindow(args) {
268
- const {entries: entries, expanded: expanded} = args;
269
- return {
270
- overflows: entries.length > 6,
271
- shown: expanded ? entries : entries.slice(0, 6)
272
- };
273
- }
274
-
275
- function PendingEffectRows({instance: instance, now: now}) {
276
- const {drainEffectsFor: drainEffectsFor, completeEffectFor: completeEffectFor, effectHandlers: effectHandlers} = index.useWorkflowContext(), toast = index.useWorkflowToast(), [busy, setBusy] = react.useState(!1), pending = instance.pendingEffects ?? [];
277
- if (pending.length === 0) return null;
278
- const runRegistered = async () => {
279
- setBusy(!0);
280
- try {
281
- await drainEffectsFor(instance._id), toast.push({
282
- id: index.TOAST_ID.effectsDrain,
283
- status: "info",
284
- title: "Ran the registered handlers"
285
- });
286
- } catch (err) {
287
- toast.push({
288
- id: index.TOAST_ID.effectsDrain,
289
- status: "error",
290
- title: "Failed to run pending effects",
291
- description: index.describeError(err)
292
- });
293
- } finally {
294
- setBusy(!1);
295
- }
296
- }, resolve = async (effectKey, status) => {
297
- setBusy(!0);
298
- try {
299
- await completeEffectFor(instance._id, {
300
- effectKey: effectKey,
301
- status: status,
302
- detail: "Resolved in Studio"
303
- }), toast.push({
304
- id: index.TOAST_ID.effectResolve,
305
- status: "info",
306
- title: `Effect marked as ${status}`
307
- });
308
- } catch (err) {
309
- toast.push({
310
- id: index.TOAST_ID.effectResolve,
311
- status: "error",
312
- title: "Failed to resolve the effect",
313
- description: index.describeError(err)
314
- });
315
- } finally {
316
- setBusy(!1);
317
- }
318
- };
319
- /* @__PURE__ */
320
- return jsxRuntime.jsx(jsxRuntime.Fragment, {
321
- children: pending.map(fx => /* @__PURE__ */ jsxRuntime.jsx(PendingEffectRow, {
322
- busy: busy,
323
- effect: fx,
324
- instanceId: instance._id,
325
- now: now,
326
- onResolve: status => resolve(fx._key, status),
327
- onRunHandlers: runRegistered,
328
- registered: fx.name in effectHandlers
329
- }, fx._key))
330
- });
331
- }
332
-
333
- function PendingEffectRow({busy: busy, effect: effect, instanceId: instanceId, now: now, onResolve: onResolve, onRunHandlers: onRunHandlers, registered: registered}) {
334
- const label = effect.title ?? effect.name, avatarSize = index.useAvatarSize();
335
- /* @__PURE__ */
336
- return jsxRuntime.jsxs(ui.Flex, {
337
- align: "center",
338
- gap: 2,
339
- children: [
340
- /* @__PURE__ */ jsxRuntime.jsx(FeedGlyph, {
341
- hint: "Waiting to run",
342
- icon: /* @__PURE__ */ jsxRuntime.jsx(Clock.ClockIcon, {})
343
- }),
344
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, {
345
- align: "center",
346
- flex: 1,
347
- gap: 1,
348
- style: {
349
- minWidth: 0
350
- },
351
- children: [
352
- /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
353
- style: {
354
- minWidth: 0
355
- },
356
- children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, {
357
- size: 1,
358
- textOverflow: "ellipsis",
359
- children: [ effect.title ?? /* @__PURE__ */ jsxRuntime.jsx(CodeChip, {
360
- value: effect.name
361
- }), registered ? " — ready to run in Studio" : " — waiting for the runtime" ]
362
- })
363
- }),
364
- /* @__PURE__ */ jsxRuntime.jsx(FeedStamp, {
365
- at: effect.queuedAt,
366
- now: now
367
- }) ]
368
- }),
369
- /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, {
370
- align: "center",
371
- flex: "none",
372
- style: {
373
- height: avatarSize
374
- },
375
- children: /* @__PURE__ */ jsxRuntime.jsx(index.HintedMenuButton, {
376
- button: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, {
377
- "aria-label": `Resolve ${label}`,
378
- disabled: busy,
379
- fontSize: 1,
380
- icon: EllipsisHorizontal.EllipsisHorizontalIcon,
381
- mode: "bleed",
382
- padding: 2
383
- }),
384
- hint: "Resolve",
385
- id: `pending-effect-menu-${instanceId}-${effect._key}`,
386
- menu: /* @__PURE__ */ jsxRuntime.jsxs(ui.Menu, {
387
- children: [ registered ? /* @__PURE__ */ jsxRuntime.jsx(ui.MenuItem, {
388
- onClick: onRunHandlers,
389
- text: "Run registered handlers"
390
- }) : null,
391
- /* @__PURE__ */ jsxRuntime.jsx(ui.MenuItem, {
392
- onClick: () => onResolve("done"),
393
- text: "Mark done"
394
- }),
395
- /* @__PURE__ */ jsxRuntime.jsx(ui.MenuItem, {
396
- onClick: () => onResolve("failed"),
397
- text: "Mark failed",
398
- tone: "critical"
399
- }) ]
400
- }),
401
- popover: {
402
- placement: "bottom-end",
403
- portal: !0
404
- }
405
- })
406
- }) ]
407
- });
408
- }
409
-
410
- function ActivityLog({instance: instance, definition: definition}) {
411
- const titles = react.useMemo(() => makeTitleResolver(definition), [ definition ]), entries = react.useMemo(() => instance.history.toReversed(), [ instance.history ]), [now, setNow] = react.useState(() => /* @__PURE__ */ new Date), [expanded, setExpanded] = react.useState(!1);
412
- react.useEffect(() => {
413
- const timer = setInterval(() => setNow(/* @__PURE__ */ new Date), 6e4);
414
- return () => clearInterval(timer);
415
- }, []);
416
- const history = historyWindow({
417
- entries: entries,
418
- expanded: expanded
419
- });
420
- return entries.length === 0 && (instance.pendingEffects ?? []).length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
421
- muted: !0,
422
- size: 1,
423
- children: "No activity yet"
424
- }) : /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, {
425
- gap: 3,
426
- children: [
427
- /* @__PURE__ */ jsxRuntime.jsx(PendingEffectRows, {
428
- instance: instance,
429
- now: now
430
- }), history.shown.map(h => /* @__PURE__ */ jsxRuntime.jsx(HistoryRow, {
431
- entry: h,
432
- now: now,
433
- titles: titles
434
- }, h._key)), history.overflows ? /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, {
435
- children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, {
436
- fontSize: 1,
437
- mode: "bleed",
438
- onClick: () => setExpanded(!expanded),
439
- padding: 2,
440
- text: expanded ? "Show less history" : "Show all history"
441
- })
442
- }) : null ]
443
- });
444
- }
445
-
446
- function FeedStamp({at: at, now: now}) {
447
- /* @__PURE__ */
448
- return jsxRuntime.jsxs(ui.Flex, {
449
- align: "center",
450
- flex: "none",
451
- gap: 1,
452
- children: [
453
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
454
- muted: !0,
455
- size: 1,
456
- children: "·"
457
- }),
458
- /* @__PURE__ */ jsxRuntime.jsx(index.HoverHint, {
459
- text: index.formatDateTime(at),
460
- children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
461
- muted: !0,
462
- size: 1,
463
- style: {
464
- whiteSpace: "nowrap"
465
- },
466
- children: index.formatShortAgo(at, now)
467
- })
468
- }) ]
469
- });
470
- }
471
-
472
- function HistoryRow({entry: entry, now: now, titles: titles}) {
473
- const line = historyLine(entry, titles);
474
- /* @__PURE__ */
475
- return jsxRuntime.jsxs(ui.Flex, {
476
- align: "center",
477
- gap: 2,
478
- children: [ line.actorId === void 0 ? /* @__PURE__ */ jsxRuntime.jsx(FeedGlyph, {
479
- hint: "The workflow",
480
- icon: /* @__PURE__ */ jsxRuntime.jsx(Cog.CogIcon, {})
481
- }) : /* @__PURE__ */ jsxRuntime.jsx(index.UserAvatar, {
482
- id: line.actorId
483
- }),
484
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, {
485
- align: "center",
486
- flex: 1,
487
- gap: 1,
488
- style: {
489
- minWidth: 0
490
- },
491
- children: [
492
- /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
493
- style: {
494
- minWidth: 0
495
- },
496
- children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, {
497
- muted: !0,
498
- size: 1,
499
- textOverflow: "ellipsis",
500
- children: [ line.actorId === void 0 ? null : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, {
501
- children: [
502
- /* @__PURE__ */ jsxRuntime.jsx(ActorNameSpan, {
503
- id: line.actorId
504
- }), " " ]
505
- }), line.parts.map((part, index2) => typeof part == "string" ? part : /* @__PURE__ */ jsxRuntime.jsx(CodeChip, {
506
- value: part.code
507
- }, index2)) ]
508
- })
509
- }),
510
- /* @__PURE__ */ jsxRuntime.jsx(FeedStamp, {
511
- at: entry.at,
512
- now: now
513
- }) ]
514
- }) ]
515
- });
516
- }
517
-
518
- function ActorNameSpan({id: id}) {
519
- const {name: name} = index.useUserDisplay(id), {font: font} = ui.useTheme_v2();
520
- /* @__PURE__ */
521
- return jsxRuntime.jsx("span", {
522
- style: {
523
- fontWeight: font.text.weights.medium
524
- },
525
- children: name
526
- });
527
- }
528
-
529
- function FeedGlyph({hint: hint, icon: icon}) {
530
- const size = index.useAvatarSize();
531
- /* @__PURE__ */
532
- return jsxRuntime.jsx(index.HoverHint, {
533
- text: hint,
534
- children: /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, {
535
- align: "center",
536
- flex: "none",
537
- justify: "center",
538
- style: {
539
- height: size,
540
- width: size
541
- },
542
- children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
543
- muted: !0,
544
- size: 1,
545
- children: icon
546
- })
547
- })
548
- });
549
- }
550
-
551
46
  function documentRefsOf(instance) {
552
47
  const seen = /* @__PURE__ */ new Map;
553
48
  for (const ref of workflowEngine.entryDocRefs(instance.fields)) seen.has(ref.id) || seen.set(ref.id, ref);
@@ -781,7 +276,7 @@ function NotFoundNote({id: id}) {
781
276
  /* @__PURE__ */
782
277
  return jsxRuntime.jsxs(jsxRuntime.Fragment, {
783
278
  children: [ "Unable to find workflow with id “",
784
- /* @__PURE__ */ jsxRuntime.jsx(CodeChip, {
279
+ /* @__PURE__ */ jsxRuntime.jsx(index.CodeChip, {
785
280
  value: id
786
281
  }), "”." ]
787
282
  });
@@ -836,7 +331,7 @@ function InstanceDetailBody({entry: entry, framed: framed, subject: subject}) {
836
331
  /* @__PURE__ */ jsxRuntime.jsx(SectionHeading, {
837
332
  children: "History"
838
333
  }),
839
- /* @__PURE__ */ jsxRuntime.jsx(ActivityLog, {
334
+ /* @__PURE__ */ jsxRuntime.jsx(index.ActivityLog, {
840
335
  definition: definition,
841
336
  instance: instance
842
337
  }) ]
@@ -941,13 +436,45 @@ function deployedPhrase(definition) {
941
436
  return ago === "" ? index.formatShortDateTime(deployedAt) : `${index.formatShortDateTime(deployedAt)} (${ago})`;
942
437
  }
943
438
 
944
- const TOOL_PANEL_PADDING = 3, TOOL_HEADING_PADDING = 2;
439
+ const TOOL_PANEL_PADDING = 3, TOOL_HEADING_PADDING = 2, logged = /* @__PURE__ */ new WeakSet;
440
+
441
+ function logReadFailure(what, error) {
442
+ if (typeof error == "object" && error !== null) {
443
+ if (logged.has(error)) return;
444
+ logged.add(error);
445
+ }
446
+ console.error(`[workflow-studio-plugin] could not load ${what}:`, error);
447
+ }
448
+
449
+ function useReadFailureLog(what, error) {
450
+ react.useEffect(() => {
451
+ error !== void 0 && logReadFailure(what, error);
452
+ }, [ what, error ]);
453
+ }
454
+
455
+ const INSTANCE_CAP = 200, INSTANCES_READ_FAILED = "Couldn’t load workflow data. Try again later.";
945
456
 
946
- function catalogReadError(error) {
947
- return `Could not load the deployed workflows: ${error}`;
457
+ function useCappedInstances(filter) {
458
+ const {engine: engine} = index.useWorkflowContext(), capped = react.useMemo(() => ({
459
+ ...filter,
460
+ limit: INSTANCE_CAP
461
+ }), [ filter ]), {instances: instances, loading: loading, unreadable: unreadable, error: error} = workflowStudio.useWorkflowInstances({
462
+ engine: engine,
463
+ filter: capped
464
+ });
465
+ return useReadFailureLog("workflow data", error), react.useMemo(() => {
466
+ const rows = instances ?? [];
467
+ return {
468
+ rows: rows,
469
+ truncated: rows.length + unreadable.length === INSTANCE_CAP,
470
+ loading: loading,
471
+ unreadable: unreadable,
472
+ error: error === void 0 ? void 0 : workflowEngine.errorMessage(error)
473
+ };
474
+ }, [ instances, loading, unreadable, error ]);
948
475
  }
949
476
 
950
- const lastRead = /* @__PURE__ */ new WeakMap, issuedReads = /* @__PURE__ */ new WeakMap;
477
+ const CATALOG_READ_FAILED = "Couldn’t load the deployed workflows. Try again later.", lastRead = /* @__PURE__ */ new WeakMap, issuedReads = /* @__PURE__ */ new WeakMap;
951
478
 
952
479
  function useDeployedDefinitions() {
953
480
  const {engine: engine} = index.useWorkflowContext(), [definitions, setDefinitions] = react.useState(() => lastRead.get(engine)?.rows), [error, setError] = react.useState(void 0);
@@ -962,7 +489,7 @@ function useDeployedDefinitions() {
962
489
  rows: latest
963
490
  }), cancelled || (setDefinitions(latest), setError(void 0));
964
491
  })().catch(err => {
965
- cancelled || setError(index.describeError(err));
492
+ logReadFailure("the deployed workflows", err), cancelled || setError(index.describeError(err));
966
493
  }), () => {
967
494
  cancelled = !0;
968
495
  };
@@ -972,46 +499,30 @@ function useDeployedDefinitions() {
972
499
  };
973
500
  }
974
501
 
975
- const INSTANCE_CAP = 200;
976
-
977
- function useCappedInstances(filter) {
978
- const {engine: engine} = index.useWorkflowContext(), capped = react.useMemo(() => ({
979
- ...filter,
980
- limit: INSTANCE_CAP
981
- }), [ filter ]), {instances: instances, loading: loading, unreadable: unreadable} = workflowStudio.useWorkflowInstances({
982
- engine: engine,
983
- filter: capped
984
- });
985
- return react.useMemo(() => {
986
- const rows = instances ?? [];
987
- return {
988
- rows: rows,
989
- truncated: rows.length + unreadable.length === INSTANCE_CAP,
990
- loading: loading,
991
- unreadable: unreadable
992
- };
993
- }, [ instances, loading, unreadable ]);
994
- }
995
-
996
502
  const INSTANCE_CAP_COUNT_NOTE = `Counts from the latest ${INSTANCE_CAP} workflows — older ones aren’t counted.`, NEWEST_INSTANCES = {
997
503
  includeCompleted: !0
998
504
  };
999
505
 
1000
506
  function useToolInstances() {
1001
- const {rows: rows, truncated: truncated, loading: loading, unreadable: unreadable} = useCappedInstances(NEWEST_INSTANCES);
507
+ const {rows: rows, truncated: truncated, loading: loading, unreadable: unreadable, error: error} = useCappedInstances(NEWEST_INSTANCES);
1002
508
  return react.useMemo(() => ({
1003
509
  inFlight: rows.filter(instance => workflowEngine.terminalState(instance) === "in-flight"),
1004
510
  settled: rows.filter(instance => workflowEngine.terminalState(instance) !== "in-flight"),
1005
511
  truncated: truncated,
1006
512
  loading: loading,
1007
- unreadable: unreadable
1008
- }), [ rows, truncated, loading, unreadable ]);
513
+ unreadable: unreadable,
514
+ error: error
515
+ }), [ rows, truncated, loading, unreadable, error ]);
1009
516
  }
1010
517
 
1011
518
  function activeSummary(active) {
1012
519
  return active === 0 ? "No active instances" : pluralize__default.default("active instance", active, !0);
1013
520
  }
1014
521
 
522
+ function activeHeadline(args) {
523
+ if (args.error === void 0) return args.loading ? "counting active instances…" : activeSummary(args.active);
524
+ }
525
+
1015
526
  const CARD_MIN_WIDTH = 280, CARD_RADIUS$1 = 3, CARD_PADDING = 4, CARD_MIN_HEIGHT = 180;
1016
527
 
1017
528
  function OverviewTab({onOpenWorkflow: onOpenWorkflow}) {
@@ -1021,13 +532,9 @@ function OverviewTab({onOpenWorkflow: onOpenWorkflow}) {
1021
532
  titleOf: name => schema.get(name)?.title
1022
533
  })
1023
534
  });
1024
- if (error !== void 0) /* @__PURE__ */
1025
- return jsxRuntime.jsx(TabStatusRow, {
1026
- children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
1027
- muted: !0,
1028
- size: 1,
1029
- children: catalogReadError(error)
1030
- })
535
+ if (error !== void 0) /* @__PURE__ */ return jsxRuntime.jsx(index.NoteBanner, {
536
+ label: CATALOG_READ_FAILED,
537
+ tone: "critical"
1031
538
  });
1032
539
  if (definitions === void 0) /* @__PURE__ */
1033
540
  return jsxRuntime.jsx(TabStatusRow, {
@@ -1054,13 +561,20 @@ function OverviewTab({onOpenWorkflow: onOpenWorkflow}) {
1054
561
  mappings: mappings,
1055
562
  inFlight: instances.inFlight,
1056
563
  issues: mappingIssues
1057
- }), active = rows.reduce((sum, row) => sum + row.activeCount, 0);
564
+ }), active = rows.reduce((sum, row) => sum + row.activeCount, 0), activity = activeHeadline({
565
+ active: active,
566
+ error: instances.error,
567
+ loading: instances.loading
568
+ });
1058
569
  /* @__PURE__ */
1059
570
  return jsxRuntime.jsxs(ui.Stack, {
1060
571
  gap: 3,
1061
572
  paddingBottom: 4,
1062
573
  paddingTop: 3,
1063
- children: [ undeployed,
574
+ children: [ undeployed, instances.error === void 0 ? null : /* @__PURE__ */ jsxRuntime.jsx(index.NoteBanner, {
575
+ label: INSTANCES_READ_FAILED,
576
+ tone: "critical"
577
+ }),
1064
578
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, {
1065
579
  align: "center",
1066
580
  gap: 2,
@@ -1070,11 +584,10 @@ function OverviewTab({onOpenWorkflow: onOpenWorkflow}) {
1070
584
  size: 1,
1071
585
  weight: "semibold",
1072
586
  children: pluralize__default.default("workflow", rows.length, !0)
1073
- }),
1074
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
587
+ }), activity === void 0 ? null : /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
1075
588
  muted: !0,
1076
589
  size: 1,
1077
- children: instances.loading ? "counting active instances…" : activeSummary(active)
590
+ children: activity
1078
591
  }) ]
1079
592
  }),
1080
593
  /* @__PURE__ */ jsxRuntime.jsx(ui.Grid, {
@@ -1084,7 +597,7 @@ function OverviewTab({onOpenWorkflow: onOpenWorkflow}) {
1084
597
  gridTemplateColumns: `repeat(auto-fill, minmax(${CARD_MIN_WIDTH}px, 1fr))`
1085
598
  },
1086
599
  children: rows.map(row => /* @__PURE__ */ jsxRuntime.jsx(WorkflowCard, {
1087
- countPending: instances.loading,
600
+ countKnown: instances.error === void 0 && !instances.loading,
1088
601
  onOpen: () => onOpenWorkflow(row.name),
1089
602
  row: row
1090
603
  }, row.name))
@@ -1101,13 +614,13 @@ function UndeployedWorkflows({notices: notices}) {
1101
614
  /* @__PURE__ */ jsxRuntime.jsx(ui.Stack, {
1102
615
  gap: 2,
1103
616
  paddingBottom: 1,
1104
- children: notices.map(notice => /* @__PURE__ */ jsxRuntime.jsx(index.CautionNote, {
617
+ children: notices.map(notice => /* @__PURE__ */ jsxRuntime.jsx(index.NoteBanner, {
1105
618
  label: notice
1106
619
  }, notice))
1107
620
  });
1108
621
  }
1109
622
 
1110
- function WorkflowCard({countPending: countPending, onOpen: onOpen, row: row}) {
623
+ function WorkflowCard({countKnown: countKnown, onOpen: onOpen, row: row}) {
1111
624
  const flagged = row.brokenBindings.length > 0, card = /* @__PURE__ */ jsxRuntime.jsx(ui.Card, {
1112
625
  as: "button",
1113
626
  onClick: onOpen,
@@ -1142,11 +655,11 @@ function WorkflowCard({countPending: countPending, onOpen: onOpen, row: row}) {
1142
655
  size: 1,
1143
656
  weight: "semibold",
1144
657
  children: row.title
1145
- }), countPending ? null : /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
658
+ }), countKnown ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
1146
659
  muted: !0,
1147
660
  size: 1,
1148
661
  children: activeSummary(row.activeCount)
1149
- }) ]
662
+ }) : null ]
1150
663
  }) ]
1151
664
  })
1152
665
  });
@@ -1200,7 +713,7 @@ function DefinitionPickerPanel({onPick: onPick}) {
1200
713
  const {definitions: definitions, error: error} = useStartableDefinitions(), [query, setQuery] = react.useState(""), visible = react.useMemo(() => {
1201
714
  const q = query.trim().toLowerCase();
1202
715
  return definitions ? q ? definitions.filter(d => `${d.title} ${d.name}`.toLowerCase().includes(q)) : definitions : [];
1203
- }, [ definitions, query ]);
716
+ }, [ definitions, query ]), settled = error === void 0 ? definitions : void 0;
1204
717
  /* @__PURE__ */
1205
718
  return jsxRuntime.jsx(ui.Box, {
1206
719
  padding: 2,
@@ -1217,13 +730,14 @@ function DefinitionPickerPanel({onPick: onPick}) {
1217
730
  onChange: event => setQuery(event.currentTarget.value),
1218
731
  placeholder: "Filter workflows…",
1219
732
  value: query
1220
- }), error ? /* @__PURE__ */ jsxRuntime.jsx(MutedNote, {
1221
- text: catalogReadError(error)
733
+ }), error ? /* @__PURE__ */ jsxRuntime.jsx(index.NoteBanner, {
734
+ label: CATALOG_READ_FAILED,
735
+ tone: "critical"
1222
736
  }) : null, !definitions && !error ? /* @__PURE__ */ jsxRuntime.jsx(index.LoadingRow, {
1223
737
  label: "Loading workflows…"
1224
- }) : null, definitions && definitions.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(MutedNote, {
738
+ }) : null, settled?.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(MutedNote, {
1225
739
  text: "No startable workflows are deployed"
1226
- }) : null, definitions && definitions.length > 0 && visible.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(MutedNote, {
740
+ }) : null, settled !== void 0 && settled.length > 0 && visible.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(MutedNote, {
1227
741
  text: "No workflows match"
1228
742
  }) : null,
1229
743
  /* @__PURE__ */ jsxRuntime.jsx(ui.Stack, {
@@ -1549,11 +1063,15 @@ function taskRowsOf(args) {
1549
1063
  open: index.isOpenActivityStatus(activity.status),
1550
1064
  dates: index.datesOf(fields),
1551
1065
  dueDates: index.dueDatesOf(fields),
1552
- assigneeIds: index.assigneeUserIdsOf(fields),
1066
+ assignees: index.assigneesOf(fields),
1553
1067
  assignedToMe: identity !== void 0 && index.isActivityAssignedTo({
1554
1068
  state: fields,
1555
1069
  who: identity
1556
1070
  }),
1071
+ roleMatched: identity !== void 0 && index.isRoleAssignedTo({
1072
+ state: fields,
1073
+ who: identity
1074
+ }),
1557
1075
  assignControl: index.rowAssignControlState(controlArgs),
1558
1076
  dateControl: index.rowDateControlState(controlArgs)
1559
1077
  };
@@ -1636,6 +1154,11 @@ function onlyMyTasks(groups) {
1636
1154
  });
1637
1155
  }
1638
1156
 
1157
+ function hasRoleMatchedTasks(groups) {
1158
+ const bandHasOne = band => band.rows.some(row => row.roleMatched);
1159
+ return groups.withDocuments.some(group => group.instances.some(bandHasOne)) || groups.withoutDocuments.some(bandHasOne);
1160
+ }
1161
+
1639
1162
  function rowActivityEval(entry, row) {
1640
1163
  const evaluation = entry?.evaluation;
1641
1164
  if (!(evaluation === void 0 || evaluation.currentStage.stage.name !== row.stage)) return evaluation.currentStage.activities.find(a => a.activity.name === row.activityName);
@@ -1646,7 +1169,7 @@ function taskRowRender(row, entry) {
1646
1169
  return entry?.evaluation === void 0 || activityEval === void 0 ? {
1647
1170
  face: index.committedRowFace(row),
1648
1171
  assignState: row.assignControl,
1649
- assigneeIds: row.assigneeIds,
1172
+ assignees: row.assignees,
1650
1173
  dateState: row.dateControl,
1651
1174
  dueDates: row.dueDates,
1652
1175
  terminalActions: [],
@@ -1779,7 +1302,7 @@ function OrphanedWorkflowsNote({orphans: orphans}) {
1779
1302
  /* @__PURE__ */
1780
1303
  return jsxRuntime.jsxs(jsxRuntime.Fragment, {
1781
1304
  children: [
1782
- /* @__PURE__ */ jsxRuntime.jsx(index.CautionNote, {
1305
+ /* @__PURE__ */ jsxRuntime.jsx(index.NoteBanner, {
1783
1306
  action: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, {
1784
1307
  fontSize: 1,
1785
1308
  mode: "ghost",
@@ -2083,7 +1606,7 @@ function statusArm(row, statuses) {
2083
1606
 
2084
1607
  function assigneeArm(row, assigneeIds) {
2085
1608
  const arm = activeArm(assigneeIds);
2086
- return arm ? arm.some(id => row.assigneeIds.includes(id)) : !0;
1609
+ return arm ? row.assignees.some(item => item.type === "user" && arm.includes(item.id)) : !0;
2087
1610
  }
2088
1611
 
2089
1612
  function rowMatches(args) {
@@ -2607,6 +2130,55 @@ function FilterRowPortal({filters: filters, onChange: onChange, options: options
2607
2130
  }), slot) : null;
2608
2131
  }
2609
2132
 
2133
+ function CollapsibleBand({header: header, children: children, background: background = !1, defaultOpen: defaultOpen = !0, onToggle: onToggle, open: controlledOpen, sticky: sticky = !1, title: title}) {
2134
+ const [heldOpen, setHeldOpen] = react.useState(defaultOpen), open = controlledOpen ?? heldOpen, toggle = onToggle ?? (() => setHeldOpen(value => !value)), verb = open ? "Collapse" : "Expand";
2135
+ /* @__PURE__ */
2136
+ return jsxRuntime.jsxs(ui.Stack, {
2137
+ gap: 1,
2138
+ children: [
2139
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Card, {
2140
+ onDoubleClick: event => {
2141
+ event.target instanceof Element && event.target.closest("a, button") || toggle();
2142
+ },
2143
+ padding: 1,
2144
+ radius: 3,
2145
+ style: {
2146
+ userSelect: "none",
2147
+ ...sticky ? {
2148
+ position: "sticky",
2149
+ top: 0,
2150
+ zIndex: 1
2151
+ } : {}
2152
+ },
2153
+ tone: background ? "transparent" : "inherit",
2154
+ children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, {
2155
+ align: "center",
2156
+ children: [
2157
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, {
2158
+ "aria-label": `${verb} ${title}`,
2159
+ fontSize: 1,
2160
+ icon: open ? ChevronDown.ChevronDownIcon : ChevronRight.ChevronRightIcon,
2161
+ mode: "bleed",
2162
+ onClick: toggle,
2163
+ padding: 2
2164
+ }), header ]
2165
+ })
2166
+ }), open ? children : null ]
2167
+ });
2168
+ }
2169
+
2170
+ function TrailingHairline() {
2171
+ /* @__PURE__ */
2172
+ return jsxRuntime.jsx(ui.Box, {
2173
+ flex: 1,
2174
+ paddingLeft: 1,
2175
+ paddingRight: 1,
2176
+ children: /* @__PURE__ */ jsxRuntime.jsx(index.Hairline, {
2177
+ weight: .5
2178
+ })
2179
+ });
2180
+ }
2181
+
2610
2182
  function isPlainClick(event) {
2611
2183
  return !event.altKey && !event.ctrlKey && !event.metaKey && !event.shiftKey;
2612
2184
  }
@@ -2680,7 +2252,7 @@ function targetOf(row) {
2680
2252
  function DocumentBand({group: group, ...wiring}) {
2681
2253
  const {open: open, toggle: toggle} = useBandOpen(wiring.bands, group.key);
2682
2254
  /* @__PURE__ */
2683
- return jsxRuntime.jsx(index.CollapsibleBand, {
2255
+ return jsxRuntime.jsx(CollapsibleBand, {
2684
2256
  background: !0,
2685
2257
  onToggle: toggle,
2686
2258
  open: open,
@@ -2707,7 +2279,7 @@ function DocumentBand({group: group, ...wiring}) {
2707
2279
  function UngroupedBand({instances: instances, ...wiring}) {
2708
2280
  const {open: open, toggle: toggle} = useBandOpen(wiring.bands, UNGROUPED_BAND_KEY);
2709
2281
  /* @__PURE__ */
2710
- return jsxRuntime.jsx(index.CollapsibleBand, {
2282
+ return jsxRuntime.jsx(CollapsibleBand, {
2711
2283
  background: !0,
2712
2284
  onToggle: toggle,
2713
2285
  open: open,
@@ -2739,7 +2311,7 @@ function InstanceBands({instances: instances, ...wiring}) {
2739
2311
  function InstanceBand({bands: bands, instance: instance, onOpenInstance: onOpenInstance, onOpenTask: onOpenTask, showTerminalActions: showTerminalActions}) {
2740
2312
  const {open: open, toggle: toggle} = useBandOpen(bands, instance.instanceId), {resolvePathFromState: resolvePathFromState} = router.useRouter();
2741
2313
  /* @__PURE__ */
2742
- return jsxRuntime.jsx(index.CollapsibleBand, {
2314
+ return jsxRuntime.jsx(CollapsibleBand, {
2743
2315
  onToggle: toggle,
2744
2316
  open: open,
2745
2317
  title: instance.title,
@@ -2776,7 +2348,7 @@ function InstanceBand({bands: bands, instance: instance, onOpenInstance: onOpenI
2776
2348
  }) ]
2777
2349
  })
2778
2350
  }),
2779
- /* @__PURE__ */ jsxRuntime.jsx(index.TrailingHairline, {}) ]
2351
+ /* @__PURE__ */ jsxRuntime.jsx(TrailingHairline, {}) ]
2780
2352
  }),
2781
2353
  children: /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
2782
2354
  paddingLeft: 5,
@@ -2812,7 +2384,7 @@ function TaskActivityRow({row: row, onOpen: onOpen, showTerminalActions: showTer
2812
2384
  stale: render.stale,
2813
2385
  children: /* @__PURE__ */ jsxRuntime.jsx(index.ActivityRow, {
2814
2386
  assignState: render.assignState,
2815
- assigneeIds: render.assigneeIds,
2387
+ assignees: render.assignees,
2816
2388
  dateState: render.dateState,
2817
2389
  dueDates: render.dueDates,
2818
2390
  face: render.face,
@@ -3066,7 +2638,7 @@ function useOrphanedWorkflows(groups) {
3066
2638
  }), [ groups, contentResource, missing ]);
3067
2639
  }
3068
2640
 
3069
- function TasksTab({filterSlot: filterSlot, instances: instances, loading: loading, identity: identity, onOpenInstance: onOpenInstance, onOpenTask: onOpenTask, tab: tab, truncated: truncated, unreadable: unreadable}) {
2641
+ function TasksTab({error: error, filterSlot: filterSlot, instances: instances, loading: loading, identity: identity, onOpenInstance: onOpenInstance, onOpenTask: onOpenTask, tab: tab, truncated: truncated, unreadable: unreadable}) {
3070
2642
  const mineOnly = tab === "for-me", {binding: binding} = index.useWorkflowContext(), bands = useBandOpenState({
3071
2643
  segment: TASK_BAND_SEGMENTS[tab],
3072
2644
  scope: binding.engineResource.id
@@ -3084,6 +2656,10 @@ function TasksTab({filterSlot: filterSlot, instances: instances, loading: loadin
3084
2656
  }), cut = mineOnly ? onlyMyTasks(filtered) : filtered;
3085
2657
  return orderGroupsByPreviewTitle(cut, titles.titles);
3086
2658
  }, [ listed, filters, mineOnly, titles ]);
2659
+ if (error !== void 0) /* @__PURE__ */ return jsxRuntime.jsx(index.NoteBanner, {
2660
+ label: INSTANCES_READ_FAILED,
2661
+ tone: "critical"
2662
+ });
3087
2663
  if (loading || !revealed) /* @__PURE__ */
3088
2664
  return jsxRuntime.jsx(TabStatusRow, {
3089
2665
  children: /* @__PURE__ */ jsxRuntime.jsx(index.LoadingRow, {
@@ -3123,7 +2699,7 @@ function TasksTab({filterSlot: filterSlot, instances: instances, loading: loadin
3123
2699
  }),
3124
2700
  /* @__PURE__ */ jsxRuntime.jsx(OrphanedWorkflowsNote, {
3125
2701
  orphans: orphans
3126
- }), body, truncated ? /* @__PURE__ */ jsxRuntime.jsx(MutedNote, {
2702
+ }), mineOnly && hasRoleMatchedTasks(visible) ? /* @__PURE__ */ jsxRuntime.jsx(index.RoleAssignedNotice, {}) : null, body, truncated ? /* @__PURE__ */ jsxRuntime.jsx(MutedNote, {
3127
2703
  text: `Tasks from the latest ${INSTANCE_CAP} workflows — older ones aren’t listed.`
3128
2704
  }) : null ]
3129
2705
  }) ]
@@ -3501,6 +3077,16 @@ function useBoardInstances(definitionName) {
3501
3077
  return useCappedInstances(filter);
3502
3078
  }
3503
3079
 
3080
+ function activeCountFace(args) {
3081
+ return args.error !== void 0 || args.loading ? {
3082
+ count: void 0,
3083
+ note: void 0
3084
+ } : {
3085
+ count: args.count,
3086
+ note: args.truncated ? INSTANCE_CAP_COUNT_NOTE : void 0
3087
+ };
3088
+ }
3089
+
3504
3090
  function DefinitionPanel({definition: definition}) {
3505
3091
  /* @__PURE__ */
3506
3092
  return jsxRuntime.jsx(ui.Box, {
@@ -3563,6 +3149,23 @@ function SetupIssues({bindings: bindings}) {
3563
3149
  });
3564
3150
  }
3565
3151
 
3152
+ function ActiveCount({face: face}) {
3153
+ /* @__PURE__ */
3154
+ return jsxRuntime.jsxs(ui.Stack, {
3155
+ gap: 2,
3156
+ children: [
3157
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
3158
+ muted: face.count === void 0,
3159
+ size: 1,
3160
+ children: face.count ?? "—"
3161
+ }), face.note === void 0 ? null : /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
3162
+ muted: !0,
3163
+ size: 1,
3164
+ children: face.note
3165
+ }) ]
3166
+ });
3167
+ }
3168
+
3566
3169
  function DefinitionFacts({definition: definition}) {
3567
3170
  const {mappingIssues: mappingIssues, mappings: mappings} = index.useWorkflowContext(), schema = sanity.useSchema(), instances = useToolInstances(), snapshotWidth = index.useContainerToken(2), [row] = definitionCatalog({
3568
3171
  definitions: [ definition ],
@@ -3580,6 +3183,12 @@ function DefinitionFacts({definition: definition}) {
3580
3183
  children: /* @__PURE__ */ jsxRuntime.jsx(SetupIssues, {
3581
3184
  bindings: row.brokenBindings
3582
3185
  })
3186
+ }), instances.error === void 0 ? null : /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
3187
+ flex: "none",
3188
+ children: /* @__PURE__ */ jsxRuntime.jsx(index.NoteBanner, {
3189
+ label: INSTANCES_READ_FAILED,
3190
+ tone: "critical"
3191
+ })
3583
3192
  }),
3584
3193
  /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
3585
3194
  flex: "none",
@@ -3598,21 +3207,13 @@ function DefinitionFacts({definition: definition}) {
3598
3207
  }),
3599
3208
  /* @__PURE__ */ jsxRuntime.jsx(index.MetaRow, {
3600
3209
  label: "Active instances",
3601
- children: instances.loading ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
3602
- muted: !0,
3603
- size: 1,
3604
- children: "—"
3605
- }) : /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, {
3606
- gap: 2,
3607
- children: [
3608
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
3609
- size: 1,
3610
- children: row?.activeCount ?? 0
3611
- }), instances.truncated ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
3612
- muted: !0,
3613
- size: 1,
3614
- children: INSTANCE_CAP_COUNT_NOTE
3615
- }) : null ]
3210
+ children: /* @__PURE__ */ jsxRuntime.jsx(ActiveCount, {
3211
+ face: activeCountFace({
3212
+ count: row?.activeCount ?? 0,
3213
+ error: instances.error,
3214
+ loading: instances.loading,
3215
+ truncated: instances.truncated
3216
+ })
3616
3217
  })
3617
3218
  }),
3618
3219
  /* @__PURE__ */ jsxRuntime.jsx(index.MetaRow, {
@@ -4149,11 +3750,11 @@ function usePageSelection(page) {
4149
3750
  }
4150
3751
 
4151
3752
  function WorkflowPage({onOpenWorkflow: onOpenWorkflow, onSelectPage: onSelectPage, page: page, workflowName: workflowName}) {
4152
- const {definitions: definitions, error: error} = useDeployedDefinitions(), selection = resolveWorkflowSelection({
3753
+ const {definitions: definitions, error: catalogError} = useDeployedDefinitions(), selection = resolveWorkflowSelection({
4153
3754
  definitions: definitions,
4154
3755
  routeName: workflowName
4155
3756
  }), selected = resolvedDefinition(selection), view = workflowPageView({
4156
- error: error,
3757
+ error: catalogError,
4157
3758
  page: page,
4158
3759
  selection: selection
4159
3760
  }), instances = useBoardInstances(view.streamName), {board: board, ready: ready, titles: titles} = useBoard({
@@ -4165,9 +3766,9 @@ function WorkflowPage({onOpenWorkflow: onOpenWorkflow, onSelectPage: onSelectPag
4165
3766
  })), revealed = useRevealGate({
4166
3767
  loading: instances.loading,
4167
3768
  ready: ready
4168
- }), waiting = index.useDelayedFlag(selection.kind === "loading" && error === void 0, WAIT_CUE_DELAY_MS);
3769
+ }), waiting = index.useDelayedFlag(selection.kind === "loading" && catalogError === void 0, WAIT_CUE_DELAY_MS);
4169
3770
  if (selection.kind === "loading") /* @__PURE__ */ return jsxRuntime.jsx(UnresolvedPage, {
4170
- error: error,
3771
+ error: catalogError,
4171
3772
  waiting: waiting
4172
3773
  });
4173
3774
  const wiring = {
@@ -4226,7 +3827,8 @@ function WorkflowPage({onOpenWorkflow: onOpenWorkflow, onSelectPage: onSelectPag
4226
3827
  /* @__PURE__ */ jsxRuntime.jsx(PageBody, {
4227
3828
  board: board,
4228
3829
  catalogEmpty: (definitions ?? []).length === 0,
4229
- error: error,
3830
+ catalogError: catalogError,
3831
+ instancesError: instances.error,
4230
3832
  loading: !revealed,
4231
3833
  page: page,
4232
3834
  selection: selection,
@@ -4264,8 +3866,8 @@ function UnresolvedPage({error: error, waiting: waiting}) {
4264
3866
  overflow: "hidden",
4265
3867
  children: error === void 0 ? /* @__PURE__ */ jsxRuntime.jsx(WaitingFace, {
4266
3868
  waiting: waiting
4267
- }) : /* @__PURE__ */ jsxRuntime.jsx(CatalogErrorFace, {
4268
- error: error
3869
+ }) : /* @__PURE__ */ jsxRuntime.jsx(ReadErrorFace, {
3870
+ text: CATALOG_READ_FAILED
4269
3871
  })
4270
3872
  });
4271
3873
  }
@@ -4281,18 +3883,24 @@ function WaitingFace({waiting: waiting}) {
4281
3883
  });
4282
3884
  }
4283
3885
 
4284
- function CatalogErrorFace({error: error}) {
3886
+ const BANNER_LANE_INSET = 2;
3887
+
3888
+ function ReadErrorFace({text: text}) {
4285
3889
  /* @__PURE__ */
4286
3890
  return jsxRuntime.jsx(StatusBox, {
4287
- children: /* @__PURE__ */ jsxRuntime.jsx(MutedNote, {
4288
- text: catalogReadError(error)
3891
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
3892
+ paddingTop: BANNER_LANE_INSET,
3893
+ children: /* @__PURE__ */ jsxRuntime.jsx(index.NoteBanner, {
3894
+ label: text,
3895
+ tone: "critical"
3896
+ })
4289
3897
  })
4290
3898
  });
4291
3899
  }
4292
3900
 
4293
- function PageBody({board: board, catalogEmpty: catalogEmpty, error: error, loading: loading, page: page, selection: selection, wiring: wiring}) {
4294
- if (error !== void 0) /* @__PURE__ */ return jsxRuntime.jsx(CatalogErrorFace, {
4295
- error: error
3901
+ function PageBody({board: board, catalogEmpty: catalogEmpty, catalogError: catalogError, instancesError: instancesError, loading: loading, page: page, selection: selection, wiring: wiring}) {
3902
+ if (catalogError !== void 0) /* @__PURE__ */ return jsxRuntime.jsx(ReadErrorFace, {
3903
+ text: CATALOG_READ_FAILED
4296
3904
  });
4297
3905
  if (selection.kind === "unknown") {
4298
3906
  const face = unknownWorkflowFace({
@@ -4310,6 +3918,8 @@ function PageBody({board: board, catalogEmpty: catalogEmpty, error: error, loadi
4310
3918
  }
4311
3919
  return page === "definition" ? /* @__PURE__ */ jsxRuntime.jsx(DefinitionPanel, {
4312
3920
  definition: selection.definition
3921
+ }) : instancesError !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(ReadErrorFace, {
3922
+ text: INSTANCES_READ_FAILED
4313
3923
  }) : loading || board === void 0 ? /* @__PURE__ */ jsxRuntime.jsx(StatusBox, {
4314
3924
  children: /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
4315
3925
  paddingTop: LANE_PADDING,
@@ -4476,6 +4086,7 @@ function HomePanel({filterSlot: filterSlot, identity: identity, instances: insta
4476
4086
  return tab === "overview" ? /* @__PURE__ */ jsxRuntime.jsx(OverviewTab, {
4477
4087
  onOpenWorkflow: onOpenWorkflow
4478
4088
  }) : /* @__PURE__ */ jsxRuntime.jsx(TasksTab, {
4089
+ error: instances.error,
4479
4090
  filterSlot: filterSlot,
4480
4091
  identity: identity,
4481
4092
  instances: [ ...instances.inFlight, ...instances.settled ],