@sanity/workflow-studio-plugin 0.27.0 → 0.29.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.
@@ -2,7 +2,7 @@ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
2
 
3
3
  import { TimelineIcon } from "@sanity/icons/Timeline";
4
4
 
5
- import { Popover, Tooltip, Box, Stack, Text, useClickOutsideEvent, Flex, TextInput, Button, Select, Checkbox, Autocomplete, Card, TextArea, useToast, Dialog, Switch, TextSkeleton, Code, useTheme_v2, Spinner, Badge, MenuButton, Menu, MenuItem, useElementSize, MenuDivider, TabList, Tab, TabPanel } from "@sanity/ui";
5
+ import { Popover, Tooltip, Box, Stack, Text, useClickOutsideEvent, Flex, TextInput, Button, Select, Checkbox, Autocomplete, Card, TextArea, useToast, Dialog, Switch, TextSkeleton, Code, Badge, useTheme_v2, Spinner, MenuButton, Menu, MenuItem, useElementSize, MenuDivider, TabList, Tab, TabPanel } from "@sanity/ui";
6
6
 
7
7
  import { editFieldTarget, useWorkflowTelemetry, useRefcountedIds } from "@sanity/workflow-react";
8
8
 
@@ -10,7 +10,7 @@ import { createContext, useContext, useEffect, useSyncExternalStore, useCallback
10
10
 
11
11
  import { usePaneRouter } from "sanity/structure";
12
12
 
13
- import { WORKFLOW_INSTANCE_TYPE, WORKFLOW_DEFINITION_TYPE, GUARD_DOC_TYPE, tryParseGdr, resourceFromParsed, latestDeployedDefinitions, isStartableDefinition, isSubjectEntry, isInputSourced, acceptsDocumentType, initialFieldIssues, gdrRef, releaseRef, refKindAcceptsTypes, rejectedRefTypes, ActionDisabledError, MutationGuardDeniedError, EditFieldDeniedError, errorMessage, terminalState, findOpenStageEntry, actionRendering, isTodoListEntry, isTodoListItem, isTerminalActivityStatus, describeCondition, sentenceCase, checklistLines, findCurrentActivityEntry, ENGINE_API_VERSION, scalarValidationIssues, classifyPrincipalId, userLoginProvider, toBareId, isSingleDocRefEntry, isGdr, resolveFieldEntry, isSingleDocRefKind, isNotesEntry, parseDefinitionSnapshot, isUnprimed, isTerminalStage, DEFAULT_TRANSITION_WHEN, instancesQuery, assertReadableModel, projectStartSliceRow, StartNotSettledError, StartNotAllowedError, humanize, unboundRequirementReads, explainStartRequirement, singleSubjectRequirementRefused, evaluateStartFilter, startKindOf, hasSingleSubjectRequirement, readsRootDocument, latestDefinitionsGroq, gdrFromResource, subscriptionDocumentsForInstance, aclPathForResource, definitionLookupGroq, instanceDocId, missingRequiredInputs, documentActionDenials } from "@sanity/workflow-engine";
13
+ import { WORKFLOW_INSTANCE_TYPE, WORKFLOW_DEFINITION_TYPE, GUARD_DOC_TYPE, tryParseGdr, resourceFromParsed, latestDeployedDefinitions, isStartableDefinition, isSubjectEntry, isInputSourced, acceptsDocumentType, initialFieldIssues, gdrRef, releaseRef, refKindAcceptsTypes, rejectedRefTypes, ActionDisabledError, MutationGuardDeniedError, EditFieldDeniedError, errorMessage, terminalState, findOpenStageEntry, actionRendering, isTodoListEntry, isTodoListItem, isTerminalActivityStatus, describeCondition, sentenceCase, checklistLines, findCurrentActivityEntry, ENGINE_API_VERSION, scalarValidationIssues, classifyPrincipalId, userLoginProvider, toBareId, findStageNode, findActivityNode, isSingleDocRefEntry, isGdr, resolveFieldEntry, isSingleDocRefKind, isNotesEntry, remediationsFor, diagnoseInstance, diagnoseInputFromEvaluation, documentStuckCause, parseDefinitionSnapshot, isUnprimed, isTerminalStage, DEFAULT_TRANSITION_WHEN, instancesQuery, assertReadableModel, projectStartSliceRow, StartNotSettledError, StartNotAllowedError, isRevisionConflict, humanize, unboundRequirementReads, explainStartRequirement, singleSubjectRequirementRefused, evaluateStartFilter, startKindOf, hasSingleSubjectRequirement, readsRootDocument, latestDefinitionsGroq, gdrFromResource, subscriptionDocumentsForInstance, aclPathForResource, definitionLookupGroq, instanceDocId, missingRequiredInputs, documentActionDenials } from "@sanity/workflow-engine";
14
14
 
15
15
  import { CheckmarkIcon } from "@sanity/icons/Checkmark";
16
16
 
@@ -52,6 +52,8 @@ import { isDocumentSchemaType } from "@sanity/types";
52
52
 
53
53
  import { LaunchIcon } from "@sanity/icons/Launch";
54
54
 
55
+ import { ErrorOutlineIcon } from "@sanity/icons/ErrorOutline";
56
+
55
57
  import { EmptyIcon } from "@sanity/icons/Empty";
56
58
 
57
59
  import { ErrorFilledIcon } from "@sanity/icons/ErrorFilled";
@@ -407,16 +409,25 @@ function parseLocalDate(value) {
407
409
  return parsed2.getFullYear() === year && parsed2.getMonth() === (month ?? NaN) - 1 && parsed2.getDate() === day ? parsed2 : /* @__PURE__ */ new Date(NaN);
408
410
  }
409
411
 
412
+ function storedDateKind(value) {
413
+ return /^\d{4}-\d{2}-\d{2}$/.test(value) ? "date" : "datetime";
414
+ }
415
+
410
416
  function parseStoredDateValue(value) {
411
- if (typeof value != "string") return;
412
- const kind = /^\d{4}-\d{2}-\d{2}$/.test(value) ? "date" : "datetime";
413
- return parseDateFieldValue(value, kind);
417
+ if (typeof value == "string") return parseDateFieldValue(value, storedDateKind(value));
414
418
  }
415
419
 
416
420
  function serializeDateFieldValue(date, kind) {
417
421
  return hasTimeOfDay(kind) ? date.toISOString() : `${date.getFullYear()}-${pad2(date.getMonth() + 1)}-${pad2(date.getDate())}`;
418
422
  }
419
423
 
424
+ function isDueDatePast(value, now) {
425
+ const kind = storedDateKind(value);
426
+ if (!hasTimeOfDay(kind)) return value < serializeDateFieldValue(now, kind);
427
+ const due = parseDateFieldValue(value, kind);
428
+ return due !== void 0 && due.getTime() < now.getTime();
429
+ }
430
+
420
431
  class EffectsIncompleteError extends Error {
421
432
  reason;
422
433
  constructor(reason, options) {
@@ -597,7 +608,7 @@ function isLiveEntry(entry) {
597
608
  const isTodoDone = item => item.status === "done", toggledTodoStatus = item => isTodoDone(item) ? "open" : "done";
598
609
 
599
610
  function isTodoOverdue(item, today = /* @__PURE__ */ new Date) {
600
- return !item.dueDate || isTodoDone(item) ? !1 : item.dueDate < serializeDateFieldValue(today, "date");
611
+ return !item.dueDate || isTodoDone(item) ? !1 : isDueDatePast(item.dueDate, today);
601
612
  }
602
613
 
603
614
  function todoEditKind(patch) {
@@ -796,13 +807,25 @@ function isItemAssignedTo(item, who) {
796
807
  return item.assignee != null && assigneeMatches(item.assignee, who);
797
808
  }
798
809
 
810
+ function matchingAssignees(state, who) {
811
+ return state.flatMap(entry => entry._type === "assignees" ? entry.value.filter(a => assigneeMatches(a, who)) : entry._type === "assignee" && entry.value !== null && assigneeMatches(entry.value, who) ? [ entry.value ] : []);
812
+ }
813
+
799
814
  function isActivityAssignedTo(args) {
800
- return args.state.some(entry => entry._type === "assignees" ? entry.value.some(a => assigneeMatches(a, args.who)) : entry._type === "assignee" ? entry.value !== null && assigneeMatches(entry.value, args.who) : !1);
815
+ return matchingAssignees(args.state, args.who).length > 0;
816
+ }
817
+
818
+ function isRoleAssignedTo(args) {
819
+ return matchingAssignees(args.state, args.who).some(member => member.type === "role");
820
+ }
821
+
822
+ function isItemRoleAssignedTo(item, who) {
823
+ return isItemAssignedTo(item, who) && item.assignee?.type === "role";
801
824
  }
802
825
 
803
826
  function assignedWork(args) {
804
- const {instance: instance, committed: committed, who: who} = args, evaluation = args.staleEvaluation === "keep" || isEvaluationCurrent(args) ? args.evaluation : void 0, byActivity = stateByActivity(instance), activities = (evaluation?.currentStage.activities ?? []).filter(t => isActivityAssignedTo({
805
- state: byActivity.get(t.activity.name) ?? [],
827
+ const {instance: instance, committed: committed, who: who} = args, evaluation = args.staleEvaluation === "keep" || isEvaluationCurrent(args) ? args.evaluation : void 0, byActivity = stateByActivity(instance), stateOf = t => byActivity.get(t.activity.name) ?? [], activities = (evaluation?.currentStage.activities ?? []).filter(t => isActivityAssignedTo({
828
+ state: stateOf(t),
806
829
  who: who
807
830
  })), items = deriveTodoItems({
808
831
  instance: instance,
@@ -812,7 +835,11 @@ function assignedWork(args) {
812
835
  return {
813
836
  activities: activities,
814
837
  openItems: items.filter(row => !isTodoDone(row.item)),
815
- doneItems: items.filter(row => isTodoDone(row.item))
838
+ doneItems: items.filter(row => isTodoDone(row.item)),
839
+ roleMatched: activities.some(t => isRoleAssignedTo({
840
+ state: stateOf(t),
841
+ who: who
842
+ })) || items.some(row => isItemRoleAssignedTo(row.item, who))
816
843
  };
817
844
  }
818
845
 
@@ -861,7 +888,8 @@ function forMeWorkOf(entries, identity) {
861
888
  return {
862
889
  work: work,
863
890
  count: work.reduce((n, {count: count}) => n + count, 0),
864
- hasRows: work.some(({shown: shown}) => hasAssignedRows(shown))
891
+ hasRows: work.some(({shown: shown}) => hasAssignedRows(shown)),
892
+ hasRoleMatched: work.some(({shown: shown}) => shown.roleMatched)
865
893
  };
866
894
  }
867
895
 
@@ -901,7 +929,7 @@ function concludesHere(actionEval, activityName) {
901
929
  return changesOwnStatus(actionEval.action, activityName) || actionEval.firing?.exitsStage === !0;
902
930
  }
903
931
 
904
- const SEMANTIC_FACE = {
932
+ const DECISION_SEMANTIC_FACE = {
905
933
  "decision.accept": {
906
934
  tone: "positive",
907
935
  icon: CheckmarkIcon
@@ -912,6 +940,15 @@ const SEMANTIC_FACE = {
912
940
  }
913
941
  };
914
942
 
943
+ function isDecisionSemantic(value) {
944
+ return Object.hasOwn(DECISION_SEMANTIC_FACE, value);
945
+ }
946
+
947
+ function decisionSemanticFace(semantics) {
948
+ const semantic = semantics.find(isDecisionSemantic);
949
+ return semantic === void 0 ? void 0 : DECISION_SEMANTIC_FACE[semantic];
950
+ }
951
+
915
952
  function actionButtonFace(args) {
916
953
  const mode = concludesHere(args.actionEval, args.activityName) ? "default" : "ghost";
917
954
  if (actionAdvanceStatus(args.actionEval.action, args.activityName) === "failed") return {
@@ -919,7 +956,7 @@ function actionButtonFace(args) {
919
956
  tone: "critical",
920
957
  icon: void 0
921
958
  };
922
- const [semantic] = args.actionEval.semantics ?? [], face = semantic === void 0 ? void 0 : SEMANTIC_FACE[semantic];
959
+ const face = decisionSemanticFace(args.actionEval.semantics ?? []);
923
960
  return {
924
961
  mode: mode,
925
962
  tone: face?.tone ?? "default",
@@ -1386,21 +1423,32 @@ function bridgedSelfId(users, meId) {
1386
1423
  return users.find(user => user.membership.id === meId)?.profile?.sanityUserId ?? classified.globalId ?? (classified.namespace === "project" ? void 0 : meId);
1387
1424
  }
1388
1425
 
1389
- function useBridgedSelf() {
1426
+ function useSelfState() {
1390
1427
  const me = useCurrentUser(), {users: users, loading: loading, error: error} = useStudioProjectUsers();
1391
- if (!me) return;
1392
- const id = bridgedSelfId(users, me.id);
1393
- if (id !== void 0) return {
1394
- id: id,
1395
- roles: me.roles ?? []
1428
+ if (!me) return {
1429
+ kind: "unavailable"
1396
1430
  };
1397
- if (!loading) {
1398
- if (error !== void 0) {
1399
- warnSelfUnavailableOnce(`load:${me.id}`, `workflow: the project member directory failed to load ("${errorMessage(error)}") — the logged-in user ("${me.id}") is project-scoped, and only that directory maps it to the account-global identity the engine stores, so workflow assignment and authoring controls stay disabled. A mounted view does not retry on its own; the next consumer to ask reloads the directory once its backoff has passed.`);
1400
- return;
1431
+ const id = bridgedSelfId(users, me.id);
1432
+ return id !== void 0 ? {
1433
+ kind: "resolved",
1434
+ self: {
1435
+ id: id,
1436
+ roles: me.roles ?? []
1401
1437
  }
1402
- warnSelfUnavailableOnce(`unresolved:${me.id}`, `workflow: the project member directory cannot resolve the logged-in user ("${me.id}") to an account-global identity — the engine stores account-global user ids only, so workflow assignment and authoring controls stay disabled. Typically this user is not a member of this project.`);
1403
- }
1438
+ } : loading ? {
1439
+ kind: "pending"
1440
+ } : error !== void 0 ? (warnSelfUnavailableOnce(`load:${me.id}`, `workflow: the project member directory failed to load ("${errorMessage(error)}") — the logged-in user ("${me.id}") is project-scoped, and only that directory maps it to the account-global identity the engine stores, so workflow assignment and authoring controls stay disabled. A mounted view does not retry on its own; the next consumer to ask reloads the directory once its backoff has passed.`),
1441
+ {
1442
+ kind: "unavailable"
1443
+ }) : (warnSelfUnavailableOnce(`unresolved:${me.id}`, `workflow: the project member directory cannot resolve the logged-in user ("${me.id}") to an account-global identity — the engine stores account-global user ids only, so workflow assignment and authoring controls stay disabled. Typically this user is not a member of this project.`),
1444
+ {
1445
+ kind: "unavailable"
1446
+ });
1447
+ }
1448
+
1449
+ function useBridgedSelf() {
1450
+ const state = useSelfState();
1451
+ return state.kind === "resolved" ? state.self : void 0;
1404
1452
  }
1405
1453
 
1406
1454
  function useSelfActor() {
@@ -1409,13 +1457,22 @@ function useSelfActor() {
1409
1457
  }
1410
1458
 
1411
1459
  function useAssignmentIdentity() {
1412
- const self = useBridgedSelf();
1413
- if (self !== void 0) return {
1414
- userId: self.id,
1415
- roles: self.roles.map(r => r.name)
1460
+ const state = useSelfState();
1461
+ if (state.kind !== "resolved") return state;
1462
+ const {id: id, roles: roles} = state.self;
1463
+ return {
1464
+ kind: "resolved",
1465
+ identity: {
1466
+ userId: id,
1467
+ roles: roles.map(r => r.name)
1468
+ }
1416
1469
  };
1417
1470
  }
1418
1471
 
1472
+ function identityOf(state) {
1473
+ return state.kind === "resolved" ? state.identity : void 0;
1474
+ }
1475
+
1419
1476
  const projectMembersByUsers = /* @__PURE__ */ new WeakMap;
1420
1477
 
1421
1478
  function projectMembersFrom(users) {
@@ -1435,9 +1492,10 @@ function projectMembersFrom(users) {
1435
1492
  }
1436
1493
 
1437
1494
  function useProjectMembers() {
1438
- const {users: users, loading: loading, error: error} = useStudioProjectUsers();
1495
+ const {users: users, roles: roles, loading: loading, error: error} = useStudioProjectUsers();
1439
1496
  return {
1440
1497
  members: useMemo(() => projectMembersFrom(users), [ users ]),
1498
+ roles: roles,
1441
1499
  loading: loading,
1442
1500
  error: error === void 0 ? void 0 : errorMessage(error)
1443
1501
  };
@@ -1522,6 +1580,8 @@ function buildParams(decls, values) {
1522
1580
  };
1523
1581
  }
1524
1582
 
1583
+ const LOADING_CUE_DELAY_MS = 400;
1584
+
1525
1585
  function useDelayedFlag(active, delayMs) {
1526
1586
  const [held, setHeld] = useState(!1);
1527
1587
  return useEffect(() => {
@@ -1791,10 +1851,12 @@ function PickedValueRow({children: children, onRequestChange: onRequestChange})
1791
1851
  }
1792
1852
 
1793
1853
  function MemberPicker({selectedIds: selectedIds, onSelect: onSelect, unassignRow: unassignRow = !1}) {
1794
- const state = useProjectMembers();
1854
+ const {error: error, loading: loading, members: members} = useProjectMembers();
1795
1855
  /* @__PURE__ */
1796
1856
  return jsx(MemberPicker$1, {
1797
- ...state,
1857
+ error: error,
1858
+ loading: loading,
1859
+ members: members,
1798
1860
  onSelect: onSelect,
1799
1861
  selectedIds: selectedIds,
1800
1862
  unassignRow: unassignRow
@@ -2527,11 +2589,10 @@ function Separator() {
2527
2589
  }
2528
2590
 
2529
2591
  function stageTitle(definition, stageName) {
2530
- return definition?.stages.find(stage => stage.name === stageName)?.title ?? stageName;
2531
- }
2532
-
2533
- function findActivityNode(args) {
2534
- return args.definition?.stages.find(s => s.name === args.stageName)?.activities?.find(a => a.name === args.activityName);
2592
+ return findStageNode({
2593
+ definition: definition,
2594
+ stageName: stageName
2595
+ })?.title ?? stageName;
2535
2596
  }
2536
2597
 
2537
2598
  function findActivity(args) {
@@ -2578,20 +2639,20 @@ function noteRow(args) {
2578
2639
  }
2579
2640
 
2580
2641
  function AssigneeBadge({assignee: assignee}) {
2581
- const {members: members} = useProjectMembers();
2642
+ const vocabulary = useProjectMembers();
2582
2643
  /* @__PURE__ */
2583
2644
  return jsx(AssigneeBadge$1, {
2584
2645
  assignee: assignee,
2585
- members: members
2646
+ vocabulary: vocabulary
2586
2647
  });
2587
2648
  }
2588
2649
 
2589
2650
  function AssigneeBadges({assignees: assignees}) {
2590
- const {members: members} = useProjectMembers();
2651
+ const vocabulary = useProjectMembers();
2591
2652
  /* @__PURE__ */
2592
2653
  return jsx(AssigneeBadges$1, {
2593
2654
  assignees: assignees,
2594
- members: members
2655
+ vocabulary: vocabulary
2595
2656
  });
2596
2657
  }
2597
2658
 
@@ -3999,6 +4060,267 @@ function EditableField({field: field, entry: entry, description: description, in
3999
4060
  });
4000
4061
  }
4001
4062
 
4063
+ function useDrainEffects(instanceId) {
4064
+ const {drainEffectsFor: drainEffectsFor} = useWorkflowContext(), toast = useWorkflowToast(), [busy, setBusy] = useState(!1);
4065
+ return {
4066
+ busy: busy,
4067
+ run: async () => {
4068
+ setBusy(!0);
4069
+ try {
4070
+ await drainEffectsFor(instanceId), toast.push({
4071
+ id: TOAST_ID.effectsDrain,
4072
+ status: "info",
4073
+ title: "Ran the registered handlers"
4074
+ });
4075
+ } catch (err) {
4076
+ toast.push({
4077
+ id: TOAST_ID.effectsDrain,
4078
+ status: "error",
4079
+ title: "Failed to run pending effects",
4080
+ description: describeError(err)
4081
+ });
4082
+ } finally {
4083
+ setBusy(!1);
4084
+ }
4085
+ }
4086
+ };
4087
+ }
4088
+
4089
+ function isRunnable(verb) {
4090
+ return verb === "drain-effects";
4091
+ }
4092
+
4093
+ function effectLabel(effect) {
4094
+ return effect.title ?? effect.name;
4095
+ }
4096
+
4097
+ const ACTION_LABEL = {
4098
+ "drain-effects": "Run the step again"
4099
+ }, REMEDY_TEXT = {
4100
+ "retry-effect": "The step has to run again before this workflow can continue.",
4101
+ "reset-activity": "Reset the task to try it again, or skip it.",
4102
+ "set-stage": "Move the workflow on to its next stage yourself."
4103
+ };
4104
+
4105
+ function detailOf(cause) {
4106
+ switch (cause.kind) {
4107
+ case "failed-effect":
4108
+ return `The step “${effectLabel(cause.effect)}” failed`;
4109
+
4110
+ case "hung-effect":
4111
+ return `The step “${effectLabel(cause.effect)}” didn’t finish`;
4112
+
4113
+ case "failed-activity":
4114
+ return "This task failed";
4115
+
4116
+ default:
4117
+ return summaryOf(cause);
4118
+ }
4119
+ }
4120
+
4121
+ function summaryOf(cause) {
4122
+ switch (cause.kind) {
4123
+ case "failed-effect":
4124
+ return "An automated step failed";
4125
+
4126
+ case "hung-effect":
4127
+ return "An automated step didn’t finish";
4128
+
4129
+ case "failed-activity":
4130
+ return "A task failed";
4131
+
4132
+ case "no-transition-fires":
4133
+ return "Every task is done, but nothing moves this workflow on";
4134
+
4135
+ case "transition-unevaluable":
4136
+ return "This workflow is waiting on something it can’t read yet, and will move on by itself";
4137
+ }
4138
+ }
4139
+
4140
+ function activityDeclaringEffect(args) {
4141
+ return findStageNode(args)?.activities?.find(activity => (activity.actions ?? []).some(action => (action.effects ?? []).some(effect => effect.name === args.effectName)))?.name;
4142
+ }
4143
+
4144
+ function faultingActivity(args) {
4145
+ const {cause: cause} = args;
4146
+ if (cause.kind === "failed-activity") return cause.activity;
4147
+ if (cause.kind === "failed-effect" || cause.kind === "hung-effect") return activityDeclaringEffect({
4148
+ definition: args.definition,
4149
+ effectName: cause.effect.name,
4150
+ stageName: args.stageName
4151
+ });
4152
+ }
4153
+
4154
+ function isRecoverable(cause) {
4155
+ return cause.kind === "transition-unevaluable";
4156
+ }
4157
+
4158
+ function diagnosisOf(args) {
4159
+ if (args.evaluation !== void 0) return diagnoseInstance(diagnoseInputFromEvaluation(args.evaluation));
4160
+ const cause = documentStuckCause({
4161
+ instance: args.instance,
4162
+ definition: args.definition
4163
+ });
4164
+ return cause === void 0 ? void 0 : {
4165
+ state: "stuck",
4166
+ cause: cause
4167
+ };
4168
+ }
4169
+
4170
+ function instanceFault(args) {
4171
+ const diagnosis = diagnosisOf(args);
4172
+ if (diagnosis === void 0 || diagnosis.state !== "stuck") return;
4173
+ const verbs = remediationsFor(diagnosis).map(remediation2 => remediation2.verb);
4174
+ return {
4175
+ activity: faultingActivity({
4176
+ cause: diagnosis.cause,
4177
+ definition: args.definition,
4178
+ stageName: args.instance.currentStage
4179
+ }),
4180
+ detail: detailOf(diagnosis.cause),
4181
+ summary: summaryOf(diagnosis.cause),
4182
+ remedies: verbs.flatMap(verb => REMEDY_TEXT[verb] ?? []),
4183
+ actions: verbs.flatMap(verb => isRunnable(verb) ? [ {
4184
+ verb: verb,
4185
+ label: ACTION_LABEL[verb]
4186
+ } ] : []),
4187
+ recoverable: isRecoverable(diagnosis.cause)
4188
+ };
4189
+ }
4190
+
4191
+ function looksLikeDefinition(value) {
4192
+ return typeof value == "object" && value !== null && Array.isArray(value.stages);
4193
+ }
4194
+
4195
+ const parsed = /* @__PURE__ */ new WeakMap;
4196
+
4197
+ function definitionSnapshotOf(instance) {
4198
+ if (parsed.has(instance)) return parsed.get(instance);
4199
+ let definition;
4200
+ try {
4201
+ const candidate = parseDefinitionSnapshot(instance);
4202
+ if (!looksLikeDefinition(candidate)) throw new Error(`definitionSnapshot on "${instance._id}" parsed to a non-definition shape`);
4203
+ definition = candidate;
4204
+ } catch (err) {
4205
+ console.error("[workflow-studio-plugin] unreadable definitionSnapshot:", err), definition = void 0;
4206
+ }
4207
+ return parsed.set(instance, definition), definition;
4208
+ }
4209
+
4210
+ function useDefinition(entry) {
4211
+ return useMemo(() => {
4212
+ if (entry) return entry.evaluation ? entry.evaluation.definition : definitionSnapshotOf(entry.instance);
4213
+ }, [ entry ]);
4214
+ }
4215
+
4216
+ function AbortedBadge({instance: instance}) {
4217
+ return terminalState(instance) !== "aborted" ? null : /* @__PURE__ */ jsx(Badge, {
4218
+ fontSize: 1,
4219
+ tone: "critical",
4220
+ children: "Aborted"
4221
+ });
4222
+ }
4223
+
4224
+ function OlderDefinitionBadge({instance: instance}) {
4225
+ const {latestDefinitionVersions: latestDefinitionVersions} = useWorkflowContext(), latest = latestDefinitionVersions.get(instance.definition);
4226
+ return latest === void 0 || latest <= instance.pinnedVersion ? null : /* @__PURE__ */ jsx(HoverHint, {
4227
+ description: "Running workflows keep the version they started on",
4228
+ text: `Started on v${instance.pinnedVersion}, latest is now v${latest}`,
4229
+ textWeight: "medium",
4230
+ children: /* @__PURE__ */ jsx(Badge, {
4231
+ fontSize: 1,
4232
+ tone: "caution",
4233
+ children: "Older version"
4234
+ })
4235
+ });
4236
+ }
4237
+
4238
+ function useInstanceFault(entry) {
4239
+ return instanceFault({
4240
+ definition: useDefinition(entry),
4241
+ evaluation: entry.evaluation,
4242
+ instance: entry.instance
4243
+ });
4244
+ }
4245
+
4246
+ function InstanceFaultNote({entry: entry, scope: scope}) {
4247
+ const fault = useInstanceFault(entry);
4248
+ if (fault === void 0 || scope.kind === "task" && fault.activity !== scope.activityName) return null;
4249
+ const Icon = fault.recoverable ? WarningOutlineIcon : ErrorOutlineIcon;
4250
+ /* @__PURE__ */
4251
+ return jsx(Card, {
4252
+ padding: 3,
4253
+ radius: 3,
4254
+ tone: fault.recoverable ? "caution" : "critical",
4255
+ children: /* @__PURE__ */ jsxs(Flex, {
4256
+ align: "flex-start",
4257
+ gap: 3,
4258
+ children: [
4259
+ /* @__PURE__ */ jsx(Text, {
4260
+ size: 1,
4261
+ children: /* @__PURE__ */ jsx(Icon, {})
4262
+ }),
4263
+ /* @__PURE__ */ jsxs(Stack, {
4264
+ flex: 1,
4265
+ gap: 3,
4266
+ children: [
4267
+ /* @__PURE__ */ jsx(Text, {
4268
+ size: 1,
4269
+ weight: "medium",
4270
+ children: scope.kind === "run" ? fault.summary : fault.detail
4271
+ }), scope.kind === "run" ? /* @__PURE__ */ jsx(RunControl, {
4272
+ activity: fault.activity,
4273
+ onOpenActivity: scope.onOpenActivity
4274
+ }) : /* @__PURE__ */ jsx(TaskControls, {
4275
+ actions: fault.actions,
4276
+ instanceId: entry.instance._id,
4277
+ remedies: fault.remedies
4278
+ }) ]
4279
+ }) ]
4280
+ })
4281
+ });
4282
+ }
4283
+
4284
+ function RunControl({activity: activity, onOpenActivity: onOpenActivity}) {
4285
+ return activity === void 0 ? null : /* @__PURE__ */ jsx(Flex, {
4286
+ children: /* @__PURE__ */ jsx(Button, {
4287
+ fontSize: 1,
4288
+ mode: "ghost",
4289
+ onClick: () => onOpenActivity(activity),
4290
+ padding: 2,
4291
+ text: "View task"
4292
+ })
4293
+ });
4294
+ }
4295
+
4296
+ const RUN_VERB = {
4297
+ "drain-effects": drain => drain.run()
4298
+ };
4299
+
4300
+ function TaskControls({actions: actions, instanceId: instanceId, remedies: remedies}) {
4301
+ const drain = useDrainEffects(instanceId);
4302
+ /* @__PURE__ */
4303
+ return jsxs(Fragment, {
4304
+ children: [ remedies.length === 0 ? null : /* @__PURE__ */ jsx(Text, {
4305
+ muted: !0,
4306
+ size: 1,
4307
+ children: remedies.join(" ")
4308
+ }), actions.length === 0 ? null : /* @__PURE__ */ jsx(Flex, {
4309
+ gap: 2,
4310
+ children: actions.map(action => /* @__PURE__ */ jsx(Button, {
4311
+ disabled: drain.busy,
4312
+ fontSize: 1,
4313
+ mode: "ghost",
4314
+ onClick: () => {
4315
+ RUN_VERB[action.verb](drain);
4316
+ },
4317
+ padding: 2,
4318
+ text: action.label
4319
+ }, action.verb))
4320
+ }) ]
4321
+ });
4322
+ }
4323
+
4002
4324
  function MetaRow({children: children, fillHeight: fillHeight, label: label}) {
4003
4325
  /* @__PURE__ */
4004
4326
  return jsxs(Flex, {
@@ -4127,7 +4449,7 @@ function useBadgeCapTrim() {
4127
4449
  }
4128
4450
 
4129
4451
  function SpinnerSlot({busy: busy}) {
4130
- const size = useTextIconSize(), trim = useCapTrimFor(size);
4452
+ const size = useTextIconSize(), trim = useCapTrimFor(size), spinning = useDelayedFlag(busy, LOADING_CUE_DELAY_MS);
4131
4453
  /* @__PURE__ */
4132
4454
  return jsx(Flex, {
4133
4455
  align: "center",
@@ -4139,7 +4461,7 @@ function SpinnerSlot({busy: busy}) {
4139
4461
  marginTop: trim,
4140
4462
  width: size
4141
4463
  },
4142
- children: busy ? /* @__PURE__ */ jsx(Spinner, {
4464
+ children: spinning ? /* @__PURE__ */ jsx(Spinner, {
4143
4465
  muted: !0,
4144
4466
  size: 1
4145
4467
  }) : null
@@ -4147,6 +4469,7 @@ function SpinnerSlot({busy: busy}) {
4147
4469
  }
4148
4470
 
4149
4471
  function LoadingRow({label: label, padding: padding}) {
4472
+ const announced = useDelayedFlag(!0, LOADING_CUE_DELAY_MS);
4150
4473
  /* @__PURE__ */
4151
4474
  return jsxs(Flex, {
4152
4475
  align: "center",
@@ -4157,12 +4480,11 @@ function LoadingRow({label: label, padding: padding}) {
4157
4480
  children: [
4158
4481
  /* @__PURE__ */ jsx(SpinnerSlot, {
4159
4482
  busy: !0
4160
- }),
4161
- /* @__PURE__ */ jsx(Text, {
4483
+ }), announced ? /* @__PURE__ */ jsx(Text, {
4162
4484
  muted: !0,
4163
4485
  size: 1,
4164
4486
  children: label
4165
- }) ]
4487
+ }) : null ]
4166
4488
  });
4167
4489
  }
4168
4490
 
@@ -4218,7 +4540,7 @@ function AvatarButton({avatar: avatar, onClick: onClick, onMouseDown: onMouseDow
4218
4540
  }
4219
4541
 
4220
4542
  function RowAssignees({assignees: assignees, hint: hint}) {
4221
- const ids = assignees.flatMap(item => item.type === "user" ? [ item.id ] : []), {members: members} = useProjectMembers(), roles = assignees.flatMap(item => item.type === "role" ? [ memberRoleFor(members, item.role) ] : []), displays = useUserDisplays(ids);
4543
+ const ids = assignees.flatMap(item => item.type === "user" ? [ item.id ] : []), vocabulary = useProjectMembers(), roles = assignees.flatMap(item => item.type === "role" ? [ memberRoleFor(vocabulary, item.role) ] : []), displays = useUserDisplays(ids);
4222
4544
  if (ids.length === 0 && roles.length === 0) return null;
4223
4545
  const bothKinds = roles.length > 0 && displays.length > 0;
4224
4546
  /* @__PURE__ */
@@ -4429,49 +4751,40 @@ function ForMeEmptyState() {
4429
4751
  });
4430
4752
  }
4431
4753
 
4432
- function looksLikeDefinition(value) {
4433
- return typeof value == "object" && value !== null && Array.isArray(value.stages);
4434
- }
4435
-
4436
- const parsed = /* @__PURE__ */ new WeakMap;
4437
-
4438
- function definitionSnapshotOf(instance) {
4439
- if (parsed.has(instance)) return parsed.get(instance);
4440
- let definition;
4441
- try {
4442
- const candidate = parseDefinitionSnapshot(instance);
4443
- if (!looksLikeDefinition(candidate)) throw new Error(`definitionSnapshot on "${instance._id}" parsed to a non-definition shape`);
4444
- definition = candidate;
4445
- } catch (err) {
4446
- console.error("[workflow-studio-plugin] unreadable definitionSnapshot:", err), definition = void 0;
4447
- }
4448
- return parsed.set(instance, definition), definition;
4449
- }
4754
+ const OVERDUE_HINT = "Now overdue";
4450
4755
 
4451
- function useDefinition(entry) {
4452
- return useMemo(() => {
4453
- if (entry) return entry.evaluation ? entry.evaluation.definition : definitionSnapshotOf(entry.instance);
4454
- }, [ entry ]);
4756
+ function hintFace(args) {
4757
+ return args.overdue ? args.reason === void 0 ? {
4758
+ text: OVERDUE_HINT
4759
+ } : {
4760
+ text: OVERDUE_HINT,
4761
+ description: args.reason
4762
+ } : args.reason === void 0 ? void 0 : {
4763
+ text: args.reason
4764
+ };
4455
4765
  }
4456
4766
 
4457
- function AbortedBadge({instance: instance}) {
4458
- return terminalState(instance) !== "aborted" ? null : /* @__PURE__ */ jsx(Badge, {
4459
- fontSize: 1,
4460
- tone: "critical",
4461
- children: "Aborted"
4767
+ function DueDate({dueDate: dueDate, hintDisabled: hintDisabled, overdue: overdue, reason: reason}) {
4768
+ const face = hintFace({
4769
+ overdue: overdue,
4770
+ reason: reason
4771
+ }), text = overdue ?
4772
+ /* @__PURE__ */ jsx(TextWithTone, {
4773
+ size: 1,
4774
+ tone: "caution",
4775
+ children: formatDate(dueDate)
4776
+ }) : /* @__PURE__ */ jsx(Text, {
4777
+ muted: !0,
4778
+ size: 1,
4779
+ children: formatDate(dueDate)
4462
4780
  });
4463
- }
4464
-
4465
- function OlderDefinitionBadge({instance: instance}) {
4466
- const {latestDefinitionVersions: latestDefinitionVersions} = useWorkflowContext(), latest = latestDefinitionVersions.get(instance.definition);
4467
- return latest === void 0 || latest <= instance.pinnedVersion ? null : /* @__PURE__ */ jsx(HoverHint, {
4468
- description: "Running workflows keep the version they started on",
4469
- text: `Started on v${instance.pinnedVersion}, latest is now v${latest}`,
4470
- textWeight: "medium",
4471
- children: /* @__PURE__ */ jsx(Badge, {
4472
- fontSize: 1,
4473
- tone: "caution",
4474
- children: "Older version"
4781
+ return face === void 0 ? text :
4782
+ /* @__PURE__ */ jsx(Flex, {
4783
+ align: "center",
4784
+ children: /* @__PURE__ */ jsx(HoverHint, {
4785
+ ...face,
4786
+ disabled: hintDisabled === !0,
4787
+ children: text
4475
4788
  })
4476
4789
  });
4477
4790
  }
@@ -4511,18 +4824,6 @@ function CheckboxSlot({children: children}) {
4511
4824
  });
4512
4825
  }
4513
4826
 
4514
- function DueDateText({dueDate: dueDate, overdue: overdue}) {
4515
- return overdue ? /* @__PURE__ */ jsx(TextWithTone, {
4516
- size: 1,
4517
- tone: "caution",
4518
- children: formatDate(dueDate)
4519
- }) : /* @__PURE__ */ jsx(Text, {
4520
- muted: !0,
4521
- size: 1,
4522
- children: formatDate(dueDate)
4523
- });
4524
- }
4525
-
4526
4827
  function DueDateEditor({dueDate: dueDate, onDone: onDone, onPatch: onPatch}) {
4527
4828
  /* @__PURE__ */
4528
4829
  return jsx(ClearableDatePicker, {
@@ -4542,8 +4843,9 @@ function DueDateEditor({dueDate: dueDate, onDone: onDone, onPatch: onPatch}) {
4542
4843
  }
4543
4844
 
4544
4845
  function DueDateControl({item: item, editable: editable, onPatch: onPatch}) {
4545
- const [open, setOpen] = useState(!1), overdue = isTodoOverdue(item), display = item.dueDate ? /* @__PURE__ */ jsx(DueDateText, {
4846
+ const [open, setOpen] = useState(!1), overdue = isTodoOverdue(item), display = item.dueDate ? /* @__PURE__ */ jsx(DueDate, {
4546
4847
  dueDate: item.dueDate,
4848
+ hintDisabled: open,
4547
4849
  overdue: overdue
4548
4850
  }) : null;
4549
4851
  return editable ? /* @__PURE__ */ jsx(DismissablePopover, {
@@ -4554,18 +4856,14 @@ function DueDateControl({item: item, editable: editable, onPatch: onPatch}) {
4554
4856
  }),
4555
4857
  onDismiss: () => setOpen(!1),
4556
4858
  open: open,
4557
- children: display ? /* @__PURE__ */ jsx(HoverHint, {
4558
- disabled: !overdue || open,
4559
- text: "Now overdue",
4560
- children: /* @__PURE__ */ jsx(Button, {
4561
- "aria-label": "Change due date",
4562
- fontSize: 1,
4563
- mode: "bleed",
4564
- onClick: () => setOpen(v => !v),
4565
- padding: 2,
4566
- tone: overdue ? "caution" : "default",
4567
- children: display
4568
- })
4859
+ children: display ? /* @__PURE__ */ jsx(Button, {
4860
+ "aria-label": "Change due date",
4861
+ fontSize: 1,
4862
+ mode: "bleed",
4863
+ onClick: () => setOpen(v => !v),
4864
+ padding: 2,
4865
+ tone: overdue ? "caution" : "default",
4866
+ children: display
4569
4867
  }) : /* @__PURE__ */ jsx(HoverHint, {
4570
4868
  disabled: open,
4571
4869
  text: "Set due date",
@@ -4579,9 +4877,6 @@ function DueDateControl({item: item, editable: editable, onPatch: onPatch}) {
4579
4877
  radius: 2
4580
4878
  })
4581
4879
  })
4582
- }) : display && overdue ? /* @__PURE__ */ jsx(HoverHint, {
4583
- text: "Now overdue",
4584
- children: display
4585
4880
  }) : display;
4586
4881
  }
4587
4882
 
@@ -5234,9 +5529,15 @@ function ActivityDetailDialog({entry: entry, activityName: activityName, breadcr
5234
5529
  const detail = deriveActivityDetail({
5235
5530
  entry: entry,
5236
5531
  activityName: activityName
5237
- });
5532
+ }), faultingActivity2 = useInstanceFault(entry)?.activity;
5238
5533
  if (!detail) return null;
5239
- const instanceId = entry.instance._id, notice = blockedNotice(detail), hasBody = detail.state.length > 0 || notice !== void 0;
5534
+ const instanceId = entry.instance._id, notice = blockedNotice(detail), fault = /* @__PURE__ */ jsx(InstanceFaultNote, {
5535
+ entry: entry,
5536
+ scope: {
5537
+ kind: "task",
5538
+ activityName: activityName
5539
+ }
5540
+ }), faulted = faultingActivity2 === activityName, hasBody = detail.state.length > 0 || notice !== void 0 || faulted;
5240
5541
  /* @__PURE__ */
5241
5542
  return jsxs(Dialog, {
5242
5543
  footer: detail.presentation.terminalActions.length > 0 ? /* @__PURE__ */ jsx(TerminalFooter, {
@@ -5277,7 +5578,7 @@ function ActivityDetailDialog({entry: entry, activityName: activityName, breadcr
5277
5578
  padding: 4,
5278
5579
  children: /* @__PURE__ */ jsxs(Stack, {
5279
5580
  gap: 5,
5280
- children: [ detail.state.map(field => /* @__PURE__ */ jsx(FieldRow, {
5581
+ children: [ fault, detail.state.map(field => /* @__PURE__ */ jsx(FieldRow, {
5281
5582
  activityName: activityName,
5282
5583
  detail: detail,
5283
5584
  entry: entry,
@@ -5458,6 +5759,18 @@ function CountedLabel({count: count, label: label}) {
5458
5759
  });
5459
5760
  }
5460
5761
 
5762
+ function CountedHeading({count: count, title: title}) {
5763
+ /* @__PURE__ */
5764
+ return jsx(CountedLabel, {
5765
+ count: count,
5766
+ label: /* @__PURE__ */ jsx(Text, {
5767
+ size: 1,
5768
+ weight: "semibold",
5769
+ children: title
5770
+ })
5771
+ });
5772
+ }
5773
+
5461
5774
  function GroupHeading({busy: busy, count: count, title: title, end: end}) {
5462
5775
  /* @__PURE__ */
5463
5776
  return jsxs(Flex, {
@@ -5466,13 +5779,9 @@ function GroupHeading({busy: busy, count: count, title: title, end: end}) {
5466
5779
  paddingLeft: 2,
5467
5780
  paddingY: end === void 0 ? 3 : 1,
5468
5781
  children: [
5469
- /* @__PURE__ */ jsx(CountedLabel, {
5782
+ /* @__PURE__ */ jsx(CountedHeading, {
5470
5783
  count: count,
5471
- label: /* @__PURE__ */ jsx(Text, {
5472
- size: 1,
5473
- weight: "semibold",
5474
- children: title
5475
- })
5784
+ title: title
5476
5785
  }), busy === void 0 ? null : /* @__PURE__ */ jsx(SpinnerSlot, {
5477
5786
  busy: busy
5478
5787
  }), end === void 0 ? null : /* @__PURE__ */ jsxs(Fragment, {
@@ -5484,21 +5793,81 @@ function GroupHeading({busy: busy, count: count, title: title, end: end}) {
5484
5793
  });
5485
5794
  }
5486
5795
 
5796
+ const EMPTY_METRIC_OPACITY = .5;
5797
+
5798
+ function MetricText({children: children, tone: tone}) {
5799
+ return tone === void 0 ? /* @__PURE__ */ jsx(Text, {
5800
+ muted: !0,
5801
+ size: 1,
5802
+ children: children
5803
+ }) : /* @__PURE__ */ jsx(TextWithTone, {
5804
+ size: 1,
5805
+ tone: tone,
5806
+ children: children
5807
+ });
5808
+ }
5809
+
5810
+ function MetricPair({Icon: Icon, empty: empty, face: face, tone: tone, value: value}) {
5811
+ /* @__PURE__ */
5812
+ return jsx(HoverHint, {
5813
+ ...face,
5814
+ children: /* @__PURE__ */ jsxs(Flex, {
5815
+ align: "center",
5816
+ gap: 2,
5817
+ style: empty ? {
5818
+ opacity: EMPTY_METRIC_OPACITY
5819
+ } : void 0,
5820
+ children: [
5821
+ /* @__PURE__ */ jsx(MetricText, {
5822
+ tone: tone,
5823
+ children: /* @__PURE__ */ jsx(Icon, {
5824
+ style: {
5825
+ color: "inherit"
5826
+ }
5827
+ })
5828
+ }),
5829
+ /* @__PURE__ */ jsx(MetricText, {
5830
+ tone: tone,
5831
+ children: value
5832
+ }) ]
5833
+ })
5834
+ });
5835
+ }
5836
+
5837
+ function AlertGlyph({Icon: Icon, hint: hint, tone: tone}) {
5838
+ /* @__PURE__ */
5839
+ return jsx(HoverHint, {
5840
+ text: hint,
5841
+ children: /* @__PURE__ */ jsx(TextWithTone, {
5842
+ size: 1,
5843
+ tone: tone,
5844
+ children: /* @__PURE__ */ jsx(Icon, {
5845
+ style: {
5846
+ color: "inherit"
5847
+ }
5848
+ })
5849
+ })
5850
+ });
5851
+ }
5852
+
5487
5853
  function ActivityDateControl({instanceId: instanceId, state: state, surface: surface, dueDates: dueDates}) {
5488
5854
  const editField = useEditField(surface), {save: save} = useSaveField({
5489
5855
  instanceId: instanceId
5490
5856
  }), [open, setOpen] = useState(!1), raw = dateControlValue({
5491
5857
  state: state,
5492
5858
  dueDates: dueDates
5493
- }), display = raw === void 0 ? null : /* @__PURE__ */ jsx(Text, {
5494
- muted: !0,
5495
- size: 1,
5496
- children: formatDate(raw)
5859
+ }), overdue = raw !== void 0 && isDueDatePast(raw, /* @__PURE__ */ new Date), dueDate = reason => raw === void 0 ? null : /* @__PURE__ */ jsx(DueDate, {
5860
+ dueDate: raw,
5861
+ hintDisabled: open,
5862
+ overdue: overdue,
5863
+ ...reason === void 0 ? {} : {
5864
+ reason: reason
5865
+ }
5497
5866
  });
5498
- if (state.kind === "none") return display;
5499
- if (state.kind === "closed") return display === null ? null : /* @__PURE__ */ jsx(HoverHint, {
5500
- text: state.reason,
5501
- children: /* @__PURE__ */ jsx(Button, {
5867
+ if (state.kind === "none") return dueDate();
5868
+ if (state.kind === "closed") {
5869
+ const display = dueDate(state.reason);
5870
+ return display === null ? null : /* @__PURE__ */ jsx(Button, {
5502
5871
  "aria-label": "Date",
5503
5872
  as: "span",
5504
5873
  fontSize: 1,
@@ -5507,8 +5876,8 @@ function ActivityDateControl({instanceId: instanceId, state: state, surface: sur
5507
5876
  onMouseDown: stopRowMouseDown,
5508
5877
  padding: 2,
5509
5878
  children: display
5510
- })
5511
- });
5879
+ });
5880
+ }
5512
5881
  const kind = dateControlKind(state), commit = (mode, value) => {
5513
5882
  state.kind === "editable" && save(() => editField({
5514
5883
  instanceId: instanceId,
@@ -5544,7 +5913,7 @@ function ActivityDateControl({instanceId: instanceId, state: state, surface: sur
5544
5913
  }),
5545
5914
  onDismiss: () => setOpen(!1),
5546
5915
  open: open,
5547
- children: display ? /* @__PURE__ */ jsx(Button, {
5916
+ children: raw !== void 0 ? /* @__PURE__ */ jsx(Button, {
5548
5917
  "aria-label": "Change date",
5549
5918
  as: "span",
5550
5919
  fontSize: 1,
@@ -5552,7 +5921,7 @@ function ActivityDateControl({instanceId: instanceId, state: state, surface: sur
5552
5921
  onClick: handleClick,
5553
5922
  onMouseDown: stopRowMouseDown,
5554
5923
  padding: 2,
5555
- children: display
5924
+ children: dueDate()
5556
5925
  }) : /* @__PURE__ */ jsx(HoverHint, {
5557
5926
  disabled: open,
5558
5927
  text: "Set date",
@@ -5642,7 +6011,7 @@ function TitleTag({hint: hint, tone: tone, children: children}) {
5642
6011
  });
5643
6012
  }
5644
6013
 
5645
- function ActivityRow({face: face, breadcrumb: breadcrumb, assignees: assignees, assignState: assignState, dateState: dateState, dueDates: dueDates, instanceId: instanceId, onOpen: onOpen, surface: surface, terminalActions: terminalActions = []}) {
6014
+ function ActivityRow({face: face, breadcrumb: breadcrumb, assignees: assignees, assignState: assignState, dateState: dateState, dueDates: dueDates, faultHint: faultHint, instanceId: instanceId, onOpen: onOpen, surface: surface, terminalActions: terminalActions = []}) {
5646
6015
  const settled = isTerminalActivityStatus(face.status), dimmed = settledDim(settled);
5647
6016
  /* @__PURE__ */
5648
6017
  return jsxs(WorkRow, {
@@ -5675,6 +6044,10 @@ function ActivityRow({face: face, breadcrumb: breadcrumb, assignees: assignees,
5675
6044
  /* @__PURE__ */ jsx(ActivityRowTitle, {
5676
6045
  dimmed: dimmed,
5677
6046
  children: face.title
6047
+ }), faultHint === void 0 || face.status === "failed" ? null : /* @__PURE__ */ jsx(AlertGlyph, {
6048
+ Icon: ErrorOutlineIcon,
6049
+ hint: faultHint,
6050
+ tone: "critical"
5678
6051
  }), face.automated ? /* @__PURE__ */ jsx(TitleTag, {
5679
6052
  hint: "Completes on its own — no user action is needed",
5680
6053
  children: "Automated"
@@ -5690,7 +6063,7 @@ function ActivityRow({face: face, breadcrumb: breadcrumb, assignees: assignees,
5690
6063
  }
5691
6064
 
5692
6065
  function ActivitiesList({entry: entry, onOpenActivity: onOpenActivity, surface: surface}) {
5693
- const activities = (entry.evaluation?.currentStage.activities ?? []).filter(t => !t.scopedOut), activityState = stateByActivity(entry.instance);
6066
+ const activities = (entry.evaluation?.currentStage.activities ?? []).filter(t => !t.scopedOut), activityState = stateByActivity(entry.instance), fault = useInstanceFault(entry);
5694
6067
  return activities.length === 0 ?
5695
6068
  /* @__PURE__ */ jsx(Box, {
5696
6069
  paddingX: 1,
@@ -5708,6 +6081,9 @@ function ActivitiesList({entry: entry, onOpenActivity: onOpenActivity, surface:
5708
6081
  activityState: activityState,
5709
6082
  editableFields: entry.evaluation?.editableFields
5710
6083
  }),
6084
+ ...fault?.activity === t.activity.name ? {
6085
+ faultHint: fault.summary
6086
+ } : {},
5711
6087
  instanceId: entry.instance._id,
5712
6088
  onOpen: () => onOpenActivity(t.activity.name),
5713
6089
  surface: surface
@@ -5854,7 +6230,10 @@ function progressFillPercent(face) {
5854
6230
  }
5855
6231
 
5856
6232
  function deriveFieldPills(args) {
5857
- const {scope: scope, definition: definition, instance: instance, committed: committed, evaluation: evaluation} = args, declared = scope === "workflow" ? definition?.fields ?? [] : definition?.stages.find(s => s.name === instance.currentStage)?.fields ?? [], entries = scope === "workflow" ? instance.fields : findOpenStageEntry(instance)?.fields ?? [], entriesByName = new Map(entries.map(entry => [ entry.name, entry ])), editables = isEvaluationCurrent({
6233
+ const {scope: scope, definition: definition, instance: instance, committed: committed, evaluation: evaluation} = args, declared = scope === "workflow" ? definition?.fields ?? [] : findStageNode({
6234
+ definition: definition,
6235
+ stageName: instance.currentStage
6236
+ })?.fields ?? [], entries = scope === "workflow" ? instance.fields : findOpenStageEntry(instance)?.fields ?? [], entriesByName = new Map(entries.map(entry => [ entry.name, entry ])), editables = isEvaluationCurrent({
5858
6237
  committed: committed,
5859
6238
  evaluation: evaluation
5860
6239
  }) ? editablesByName(evaluation, scope) : /* @__PURE__ */ new Map, declaredNames = new Set(declared.map(d => d.name));
@@ -6088,7 +6467,7 @@ function AvatarCapAligned({children: children}) {
6088
6467
  }
6089
6468
 
6090
6469
  function AssigneesFace({assignees: assignees}) {
6091
- const {members: members} = useProjectMembers(), userIds = assignees.flatMap(a => a.type === "user" ? [ a.id ] : []), roles = assignees.flatMap(a => a.type === "role" ? [ roleLabelFor(members, a.role) ] : []);
6470
+ const vocabulary = useProjectMembers(), userIds = assignees.flatMap(a => a.type === "user" ? [ a.id ] : []), roles = assignees.flatMap(a => a.type === "role" ? [ roleLabelFor(vocabulary, a.role) ] : []);
6092
6471
  /* @__PURE__ */
6093
6472
  return jsxs(Fragment, {
6094
6473
  children: [ userIds.length > 0 ? /* @__PURE__ */ jsx(AvatarCapAligned, {
@@ -7044,6 +7423,27 @@ function HintedMenuButton({hint: hint, ...menuButtonProps}) {
7044
7423
  });
7045
7424
  }
7046
7425
 
7426
+ function InlineEmphasis({children: children}) {
7427
+ const {font: font} = useTheme_v2();
7428
+ /* @__PURE__ */
7429
+ return jsx("span", {
7430
+ style: {
7431
+ fontWeight: font.text.weights.medium
7432
+ },
7433
+ children: children
7434
+ });
7435
+ }
7436
+
7437
+ const MINUTE_MS = 6e4;
7438
+
7439
+ function useMinuteClock() {
7440
+ const [now, setNow] = useState(() => /* @__PURE__ */ new Date);
7441
+ return useEffect(() => {
7442
+ const timer = setInterval(() => setNow(/* @__PURE__ */ new Date), MINUTE_MS);
7443
+ return () => clearInterval(timer);
7444
+ }, []), now;
7445
+ }
7446
+
7047
7447
  function historyWindow(args) {
7048
7448
  const {entries: entries, expanded: expanded} = args;
7049
7449
  return {
@@ -7053,28 +7453,10 @@ function historyWindow(args) {
7053
7453
  }
7054
7454
 
7055
7455
  function PendingEffectRows({instance: instance, now: now}) {
7056
- const {drainEffectsFor: drainEffectsFor, completeEffectFor: completeEffectFor, effectHandlers: effectHandlers} = useWorkflowContext(), toast = useWorkflowToast(), [busy, setBusy] = useState(!1), pending = instance.pendingEffects ?? [];
7456
+ const {completeEffectFor: completeEffectFor, effectHandlers: effectHandlers} = useWorkflowContext(), toast = useWorkflowToast(), drain = useDrainEffects(instance._id), [resolving, setResolving] = useState(!1), pending = instance.pendingEffects ?? [];
7057
7457
  if (pending.length === 0) return null;
7058
- const runRegistered = async () => {
7059
- setBusy(!0);
7060
- try {
7061
- await drainEffectsFor(instance._id), toast.push({
7062
- id: TOAST_ID.effectsDrain,
7063
- status: "info",
7064
- title: "Ran the registered handlers"
7065
- });
7066
- } catch (err) {
7067
- toast.push({
7068
- id: TOAST_ID.effectsDrain,
7069
- status: "error",
7070
- title: "Failed to run pending effects",
7071
- description: describeError(err)
7072
- });
7073
- } finally {
7074
- setBusy(!1);
7075
- }
7076
- }, resolve = async (effectKey, status) => {
7077
- setBusy(!0);
7458
+ const resolve = async (effectKey, status) => {
7459
+ setResolving(!0);
7078
7460
  try {
7079
7461
  await completeEffectFor(instance._id, {
7080
7462
  effectKey: effectKey,
@@ -7093,18 +7475,20 @@ function PendingEffectRows({instance: instance, now: now}) {
7093
7475
  description: describeError(err)
7094
7476
  });
7095
7477
  } finally {
7096
- setBusy(!1);
7478
+ setResolving(!1);
7097
7479
  }
7098
7480
  };
7099
7481
  /* @__PURE__ */
7100
7482
  return jsx(Fragment, {
7101
7483
  children: pending.map(fx => /* @__PURE__ */ jsx(PendingEffectRow, {
7102
- busy: busy,
7484
+ busy: drain.busy || resolving,
7103
7485
  effect: fx,
7104
7486
  instanceId: instance._id,
7105
7487
  now: now,
7106
7488
  onResolve: status => resolve(fx._key, status),
7107
- onRunHandlers: runRegistered,
7489
+ onRunHandlers: () => {
7490
+ drain.run();
7491
+ },
7108
7492
  registered: fx.name in effectHandlers
7109
7493
  }, fx._key))
7110
7494
  });
@@ -7188,12 +7572,7 @@ function PendingEffectRow({busy: busy, effect: effect, instanceId: instanceId, n
7188
7572
  }
7189
7573
 
7190
7574
  function ActivityLog({instance: instance, definition: definition, windowed: windowed = !0}) {
7191
- const titles = useMemo(() => makeTitleResolver(definition), [ definition ]), entries = useMemo(() => instance.history.toReversed(), [ instance.history ]), [now, setNow] = useState(() => /* @__PURE__ */ new Date), [expanded, setExpanded] = useState(!1);
7192
- useEffect(() => {
7193
- const timer = setInterval(() => setNow(/* @__PURE__ */ new Date), 6e4);
7194
- return () => clearInterval(timer);
7195
- }, []);
7196
- const history = historyWindow({
7575
+ const titles = useMemo(() => makeTitleResolver(definition), [ definition ]), entries = useMemo(() => instance.history.toReversed(), [ instance.history ]), now = useMinuteClock(), [expanded, setExpanded] = useState(!1), history = historyWindow({
7197
7576
  entries: entries,
7198
7577
  expanded: expanded || !windowed
7199
7578
  });
@@ -7296,12 +7675,9 @@ function HistoryRow({entry: entry, now: now, titles: titles}) {
7296
7675
  }
7297
7676
 
7298
7677
  function ActorNameSpan({id: id}) {
7299
- const {name: name} = useUserDisplay(id), {font: font} = useTheme_v2();
7678
+ const {name: name} = useUserDisplay(id);
7300
7679
  /* @__PURE__ */
7301
- return jsx("span", {
7302
- style: {
7303
- fontWeight: font.text.weights.medium
7304
- },
7680
+ return jsx(InlineEmphasis, {
7305
7681
  children: name
7306
7682
  });
7307
7683
  }
@@ -7709,7 +8085,6 @@ function StageOverviewCard({overview: overview}) {
7709
8085
  }
7710
8086
 
7711
8087
  function AdvanceCard({advance: advance}) {
7712
- const {font: font} = useTheme_v2();
7713
8088
  /* @__PURE__ */
7714
8089
  return jsx(Card, {
7715
8090
  padding: 3,
@@ -7721,10 +8096,7 @@ function AdvanceCard({advance: advance}) {
7721
8096
  /* @__PURE__ */ jsxs(Text, {
7722
8097
  size: 1,
7723
8098
  children: [ "Advances to ",
7724
- /* @__PURE__ */ jsx("span", {
7725
- style: {
7726
- fontWeight: font.text.weights.medium
7727
- },
8099
+ /* @__PURE__ */ jsx(InlineEmphasis, {
7728
8100
  children: advance.toTitle
7729
8101
  }), ADVANCE_TAILS[advance.kind] ]
7730
8102
  }), advance.kind !== "conditions" ? null : advance.lines.map((line, index) =>
@@ -7848,18 +8220,23 @@ function StageSection({entry: entry, onOpenActivity: onOpenActivity, surface: su
7848
8220
  });
7849
8221
  }
7850
8222
 
7851
- function CautionNote({action: action, label: label}) {
8223
+ const BANNER_PADDING = 3, ICONS = {
8224
+ caution: /* @__PURE__ */ jsx(WarningOutlineIcon, {}),
8225
+ critical: /* @__PURE__ */ jsx(ErrorOutlineIcon, {})
8226
+ };
8227
+
8228
+ function NoteBanner({action: action, label: label, tone: tone = "caution"}) {
7852
8229
  /* @__PURE__ */
7853
8230
  return jsxs(Card, {
7854
8231
  paddingLeft: 3,
7855
8232
  paddingRight: 2,
7856
- paddingY: 3,
8233
+ paddingY: BANNER_PADDING,
7857
8234
  radius: 3,
7858
8235
  style: {
7859
8236
  alignItems: "center",
7860
8237
  display: "flex"
7861
8238
  },
7862
- tone: "caution",
8239
+ tone: tone,
7863
8240
  children: [
7864
8241
  /* @__PURE__ */ jsxs(Flex, {
7865
8242
  align: "center",
@@ -7869,7 +8246,7 @@ function CautionNote({action: action, label: label}) {
7869
8246
  /* @__PURE__ */ jsx(Text, {
7870
8247
  muted: !0,
7871
8248
  size: 1,
7872
- children: /* @__PURE__ */ jsx(WarningOutlineIcon, {})
8249
+ children: ICONS[tone]
7873
8250
  }),
7874
8251
  /* @__PURE__ */ jsx(Text, {
7875
8252
  muted: !0,
@@ -7906,7 +8283,7 @@ function UnreadableDocsNote({unreadable: unreadable}) {
7906
8283
  if (unreadable.length === 0) return null;
7907
8284
  const label = unreadable.length === 1 ? "1 workflow document isn’t listed — this Studio can’t read it. Share the details with your Studio maintainers." : `${unreadable.length} workflow documents aren’t listed — this Studio can’t read them. Share the details with your Studio maintainers.`;
7908
8285
  /* @__PURE__ */
7909
- return jsx(CautionNote, {
8286
+ return jsx(NoteBanner, {
7910
8287
  action: /* @__PURE__ */ jsx(CopyDetailsButton, {
7911
8288
  unreadable: unreadable
7912
8289
  }),
@@ -7952,6 +8329,21 @@ function InvalidDocNotice({invalid: invalid}) {
7952
8329
  });
7953
8330
  }
7954
8331
 
8332
+ function RoleAssignedNotice() {
8333
+ /* @__PURE__ */
8334
+ return jsx(Box, {
8335
+ padding: 2,
8336
+ children: /* @__PURE__ */ jsx(Text, {
8337
+ muted: !0,
8338
+ size: 1,
8339
+ style: {
8340
+ fontStyle: "italic"
8341
+ },
8342
+ children: "Including tasks assigned to your role"
8343
+ })
8344
+ });
8345
+ }
8346
+
7955
8347
  function TabSwitch({ariaControls: ariaControls, idPrefix: idPrefix, options: options, selected: selected, onSelect: onSelect}) {
7956
8348
  /* @__PURE__ */
7957
8349
  return jsx(TabList, {
@@ -8216,8 +8608,15 @@ function presentedResumeEntry(args) {
8216
8608
  if (!args.startNew) return findResumeEntry(args);
8217
8609
  }
8218
8610
 
8611
+ function settledByAnotherWriter(cause, instance) {
8612
+ return isRevisionConflict(cause) && terminalState(instance) === "completed";
8613
+ }
8614
+
8219
8615
  function classifyStartError(err) {
8220
- return err instanceof StartNotSettledError ? {
8616
+ return err instanceof StartNotSettledError ? err.instance !== void 0 && settledByAnotherWriter(err.cause, err.instance) ? {
8617
+ kind: "started-settled-elsewhere",
8618
+ instance: err.instance
8619
+ } : {
8221
8620
  kind: "started-not-settled",
8222
8621
  instance: err.instance,
8223
8622
  description: describeError(err.cause)
@@ -8230,8 +8629,16 @@ function classifyStartError(err) {
8230
8629
  };
8231
8630
  }
8232
8631
 
8632
+ function startProducedRun(outcome) {
8633
+ return outcome.kind === "started-not-settled" || outcome.kind === "started-settled-elsewhere";
8634
+ }
8635
+
8233
8636
  function startOutcomeToast(outcome, label) {
8234
- return outcome.kind === "started-not-settled" ? {
8637
+ return outcome.kind === "started-settled-elsewhere" ? {
8638
+ id: TOAST_ID.start,
8639
+ status: "info",
8640
+ title: `“${label}” started and finished`
8641
+ } : outcome.kind === "started-not-settled" ? {
8235
8642
  id: TOAST_ID.start,
8236
8643
  status: "warning",
8237
8644
  title: `“${label}” started but didn’t finish`,
@@ -8797,7 +9204,7 @@ function DiscoverySpinner() {
8797
9204
  });
8798
9205
  }
8799
9206
 
8800
- const BOOT_CUE_DELAY_MS = 400, GROUP_GAP = 2;
9207
+ const GROUP_GAP = 2;
8801
9208
 
8802
9209
  function useWorkflowsTab() {
8803
9210
  const {params: params, setParams: setParams} = usePaneRouter();
@@ -8822,7 +9229,7 @@ function useOpenActivity(entries) {
8822
9229
  }
8823
9230
 
8824
9231
  function WorkflowsPanel({entries: entries, documentId: documentId, docTypeMappings: docTypeMappings, initialValue: initialValue}) {
8825
- const identity = useAssignmentIdentity(), panelId = useId(), {params: params} = usePaneRouter(), {view: view, setView: setView} = useWorkflowsTab(), telemetry = useWorkflowTelemetry(), {openActivity: openActivity, setOpenActivity: setOpenActivity, dialogEntry: dialogEntry} = useOpenActivity(entries), active = entries.filter(isLiveEntry), finished = entries.filter(e => !isLiveEntry(e)), forMe = forMeWorkOf(active, identity), [landing] = useState(() => focusedLanding(params?.[WORKFLOWS_FOCUS_PARAM], entries.map(e => e.instance._id))), assignedWorkUnknown = active.some(e => e.invalid !== void 0), booting = useDelayedFlag(active.some(e => !e.ready && e.invalid === void 0), BOOT_CUE_DELAY_MS), sectionFor = e => /* @__PURE__ */ jsx(WorkflowInstanceSection, {
9232
+ const identity = useAssignmentIdentity(), panelId = useId(), {params: params} = usePaneRouter(), {view: view, setView: setView} = useWorkflowsTab(), telemetry = useWorkflowTelemetry(), {openActivity: openActivity, setOpenActivity: setOpenActivity, dialogEntry: dialogEntry} = useOpenActivity(entries), active = entries.filter(isLiveEntry), finished = entries.filter(e => !isLiveEntry(e)), forMe = forMeWorkOf(active, identityOf(identity)), [landing] = useState(() => focusedLanding(params?.[WORKFLOWS_FOCUS_PARAM], entries.map(e => e.instance._id))), assignedWorkUnknown = active.some(e => e.invalid !== void 0), booting = active.some(e => !e.ready && e.invalid === void 0), sectionFor = e => /* @__PURE__ */ jsx(WorkflowInstanceSection, {
8826
9233
  defaultOpen: landingDefaultOpen(landing, e.instance._id),
8827
9234
  entry: e,
8828
9235
  onOpenActivity: activityName => setOpenActivity({
@@ -8962,10 +9369,10 @@ function OverviewEmptyState({mappings: mappings, docId: docId, initialValue: ini
8962
9369
  }
8963
9370
 
8964
9371
  function ForMeTab({forMe: forMe, assignedWorkUnknown: assignedWorkUnknown, onOpenActivity: onOpenActivity}) {
8965
- return forMe.hasRows ? /* @__PURE__ */ jsx(Stack, {
9372
+ return forMe.hasRows ? /* @__PURE__ */ jsxs(Stack, {
8966
9373
  gap: GROUP_GAP,
8967
9374
  paddingBottom: 4,
8968
- children: forMe.work.map(({entry: entry, who: who, shown: shown}) => /* @__PURE__ */ jsx(ForMeList, {
9375
+ children: [ forMe.hasRoleMatched ? /* @__PURE__ */ jsx(RoleAssignedNotice, {}) : null, forMe.work.map(({entry: entry, who: who, shown: shown}) => /* @__PURE__ */ jsx(ForMeList, {
8969
9376
  entry: entry,
8970
9377
  identity: who,
8971
9378
  work: shown,
@@ -8974,7 +9381,7 @@ function ForMeTab({forMe: forMe, assignedWorkUnknown: assignedWorkUnknown, onOpe
8974
9381
  activityName: activityName,
8975
9382
  stage: entry.instance.currentStage
8976
9383
  })
8977
- }, entry.instance._id))
9384
+ }, entry.instance._id)) ]
8978
9385
  }) : assignedWorkUnknown ? null : /* @__PURE__ */ jsx(ForMeEmptyState, {});
8979
9386
  }
8980
9387
 
@@ -10211,7 +10618,7 @@ function ResumeNote({onStartNew: onStartNew}) {
10211
10618
 
10212
10619
  function handleStartError(args) {
10213
10620
  const {label: label, toast: toast, seedInstance: seedInstance, onClose: onClose} = args, outcome = classifyStartError(args.err);
10214
- toast.push(startOutcomeToast(outcome, label)), outcome.kind === "started-not-settled" && (outcome.instance !== void 0 && seedInstance(outcome.instance),
10621
+ toast.push(startOutcomeToast(outcome, label)), startProducedRun(outcome) && (outcome.instance !== void 0 && seedInstance(outcome.instance),
10215
10622
  onClose());
10216
10623
  }
10217
10624
 
@@ -10564,7 +10971,7 @@ async function startRequest(engine, request) {
10564
10971
  };
10565
10972
  } catch (err) {
10566
10973
  const outcome = classifyStartError(err);
10567
- return outcome.kind === "started-not-settled" ? outcome.instance ? {
10974
+ return startProducedRun(outcome) ? outcome.instance ? {
10568
10975
  instance: outcome.instance
10569
10976
  } : {} : outcome.kind === "not-allowed" ? {} : {
10570
10977
  failure: outcome.description
@@ -10765,6 +11172,28 @@ function unassignedLineHint(args) {
10765
11172
  };
10766
11173
  }
10767
11174
 
11175
+ function taskCountState(args) {
11176
+ const {entry: entry, identity: identity} = args, freshness = entry.invalid === void 0 ? evaluationFreshness({
11177
+ committed: entry.committed,
11178
+ evaluation: entry.evaluation
11179
+ }) : void 0;
11180
+ return identity.kind === "unavailable" ? {
11181
+ kind: "unknowable"
11182
+ } : identity.kind === "pending" || freshness === "pending" ? {
11183
+ kind: "pending"
11184
+ } : freshness === "stale" ? {
11185
+ kind: "stale"
11186
+ } : {
11187
+ kind: "ready",
11188
+ tasks: assignedTaskCount({
11189
+ instance: entry.instance,
11190
+ committed: entry.committed,
11191
+ evaluation: entry.evaluation,
11192
+ who: identity.identity
11193
+ })
11194
+ };
11195
+ }
11196
+
10768
11197
  function WorkflowFormStrip(props) {
10769
11198
  const {mappings: mappings} = props, {isDocResolved: isDocResolved, discoveryInvalid: discoveryInvalid} = useWorkflowContext(), {docId: docId, entries: entries} = useWorkflowsForDocument(props.value?._id ?? null), {params: params, setParams: setParams} = usePaneRouter(), telemetry = useWorkflowTelemetry(), openWorkflowsView = focusId => {
10770
11199
  telemetry.log(WorkflowFormStripClicked), setParams(focusedViewParams(params, focusId));
@@ -10852,7 +11281,7 @@ function InstanceLine({entry: entry, onOpen: onOpen}) {
10852
11281
  aside: unprimed ? void 0 : /* @__PURE__ */ jsx(TaskCountSpinner, {
10853
11282
  entry: entry
10854
11283
  }),
10855
- hint: lineHint(entry, identity),
11284
+ hint: lineHint(entry, identityOf(identity)),
10856
11285
  onOpen: onOpen,
10857
11286
  children: [
10858
11287
  /* @__PURE__ */ jsx(Flex, {
@@ -10911,28 +11340,16 @@ function finishedHint(instance) {
10911
11340
  }
10912
11341
 
10913
11342
  function useTaskCountState(entry) {
10914
- const identity = useAssignmentIdentity(), freshness = entry.invalid === void 0 ? evaluationFreshness({
10915
- committed: entry.committed,
10916
- evaluation: entry.evaluation
10917
- }) : void 0;
10918
- return !identity || freshness === "pending" ? {
10919
- kind: "pending"
10920
- } : freshness === "stale" ? {
10921
- kind: "stale"
10922
- } : {
10923
- kind: "ready",
10924
- tasks: assignedTaskCount({
10925
- instance: entry.instance,
10926
- committed: entry.committed,
10927
- evaluation: entry.evaluation,
10928
- who: identity
10929
- })
10930
- };
11343
+ const identity = useAssignmentIdentity();
11344
+ return taskCountState({
11345
+ entry: entry,
11346
+ identity: identity
11347
+ });
10931
11348
  }
10932
11349
 
10933
11350
  function TaskCountText({entry: entry}) {
10934
11351
  const state = useTaskCountState(entry);
10935
- return state.kind === "pending" || state.kind === "ready" && state.tasks === 0 ? null : /* @__PURE__ */ jsx(Text, {
11352
+ return state.kind === "pending" || state.kind === "unknowable" || state.kind === "ready" && state.tasks === 0 ? null : /* @__PURE__ */ jsx(Text, {
10936
11353
  muted: !0,
10937
11354
  size: 1,
10938
11355
  children: state.kind === "stale" ? "Updating…" : pluralize("assigned task", state.tasks, !0)
@@ -11163,4 +11580,4 @@ function WorkflowRootInput(props) {
11163
11580
  });
11164
11581
  }
11165
11582
 
11166
- export { AbortWorkflowDialog, ActivityDetailDialog, ActivityLog, ActivityRow, BreadcrumbTail, CautionNote, CodeChip, CountedLabel, DismissablePopover, DocPreviewLink, DocRefFace, EmptyState, ForMeEmptyState, Hairline, HoverHint, InstanceSnapshotBody, InvalidDocNotice, LinkChip, LoadingRow, LogEventOnMount, MetaRow, SpinnerSlot, StageFace, StaleLock, TOAST_ID, TOOL_TABS, TabSwitch, UnreadableDocsNote, WORKFLOW_API_VERSION, WORKFLOW_PAGE_TABS, WorkflowBoardWorkflowSelected, WorkflowDefinitionDetailViewed, WorkflowInstanceDetailViewed, WorkflowTaskFiltersApplied, WorkflowTitleSeedDrifted, WorkflowToolOpened, activityRowProps, assigneesOf, backToTabLabel, committedRowFace, createSubscribers, datesOf, definitionFingerprint, definitionSnapshotOf, describeError, dueDatesOf, findActivity, findActivityNode, formatDate, formatShortDateTime, formatTimeAgo, gdrLocality, instanceBreadcrumb, instanceState, instanceTitle, isActivityAssignedTo, isEvaluationStale, isLiveEntry, isOpenActivityStatus, landingState, mappingIssueDetail, namesNoDeployedDefinition, openActivityGone, openableSchemaType, parseStoredDateValue, readDeployedDefinitions, rowAssignControlState, rowDateControlState, stageTitle, stateByActivity, tabSelectionNavigates, toolRoute, toolTabState, useAssignmentIdentity, useBadgeCapTrim, useContainerToken, useDefinition, useDelayedFlag, useLogEventOnMount, useProjectMembers, useSpaceToken, useUserDisplays, useWorkflowContext, useWorkflowInstanceEntry, useWorkflowToast, workflowDefaultDocumentNode, workflowPageState, workflowState, workflowStudioPlugin, workflowsView };
11583
+ export { AbortWorkflowDialog, ActivityDetailDialog, ActivityLog, ActivityRow, AlertGlyph, BreadcrumbTail, CodeChip, CountedHeading, DismissablePopover, DocPreviewLink, DocRefFace, EmptyState, ForMeEmptyState, Hairline, HoverHint, InlineEmphasis, InstanceFaultNote, InstanceSnapshotBody, InvalidDocNotice, LinkChip, LoadingRow, LogEventOnMount, MetaRow, MetricPair, NoteBanner, RoleAssignedNotice, SpinnerSlot, StageFace, StaleLock, TOAST_ID, TOOL_TABS, TabSwitch, UnreadableDocsNote, WORKFLOW_API_VERSION, WORKFLOW_PAGE_TABS, WorkflowBoardWorkflowSelected, WorkflowDefinitionDetailViewed, WorkflowInstanceDetailViewed, WorkflowTaskFiltersApplied, WorkflowTitleSeedDrifted, WorkflowToolOpened, activityRowProps, assigneesOf, backToTabLabel, committedRowFace, createSubscribers, datesOf, definitionFingerprint, definitionSnapshotOf, describeError, dueDatesOf, findActivity, formatDate, formatShortAgo, formatShortDateTime, formatTimeAgo, gdrLocality, identityOf, instanceBreadcrumb, instanceState, instanceTitle, isActivityAssignedTo, isDueDatePast, isEvaluationStale, isLiveEntry, isOpenActivityStatus, isRoleAssignedTo, landingState, mappingIssueDetail, namesNoDeployedDefinition, openActivityGone, openableSchemaType, parseStoredDateValue, readDeployedDefinitions, rowAssignControlState, rowDateControlState, stageTitle, stateByActivity, tabSelectionNavigates, toolRoute, toolTabState, useAssignmentIdentity, useContainerToken, useDefinition, useLogEventOnMount, useMinuteClock, useProjectMembers, useSpaceToken, useUserDisplays, useWorkflowContext, useWorkflowInstanceEntry, useWorkflowToast, workflowDefaultDocumentNode, workflowPageState, workflowState, workflowStudioPlugin, workflowsView };