@sanity/workflow-studio-plugin 0.23.0 → 0.25.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 { Tooltip, Box, Stack, Text, useClickOutsideEvent, Popover, Flex, TextInput, Button, Select, Checkbox, Autocomplete, Card, TextArea, useToast, Dialog, Switch, TextSkeleton, Code, Spinner, useTheme_v2, Badge, MenuButton, Menu, MenuItem, useElementSize, 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, useTheme_v2, Spinner, Badge, MenuButton, Menu, MenuItem, useElementSize, 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 { tryParseGdr, resourceFromParsed, latestDeployedDefinitions, isStartableDefinition, isSubjectEntry, isInputSourced, acceptsDocumentType, missingRequiredInputs, initialFieldIssues, gdrRef, releaseRef, refKindAcceptsTypes, rejectedRefTypes, ActionDisabledError, MutationGuardDeniedError, errorMessage, findOpenStageEntry, actionRendering, terminalState, isTodoListEntry, isTodoListItem, isTerminalActivityStatus, describeCondition, sentenceCase, checklistLines, findCurrentActivityEntry, ENGINE_API_VERSION, WORKFLOW_INSTANCE_TYPE, WORKFLOW_DEFINITION_TYPE, GUARD_DOC_TYPE, scalarValidationIssues, classifyPrincipalId, 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, 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, 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";
14
14
 
15
15
  import { CheckmarkIcon } from "@sanity/icons/Checkmark";
16
16
 
@@ -26,7 +26,7 @@ import { defineEvent } from "@sanity/telemetry";
26
26
 
27
27
  import { useStudioProjectUsers, useStudioObserver, useWorkflowEngine, useWorkflowInstances, useWorkflowSession } from "@sanity/workflow-studio";
28
28
 
29
- import { useCurrentUser, SanityDefaultPreview, Preview, useValuePreview, useClient, useSchema, useWorkspace, TextWithTone, usePerspective, useIsReleaseActive, useDocumentPreviewStore, useActiveReleases, useTemplates, useInitialValueResolverContext, resolveInitialValue, definePlugin, isObjectInputProps } from "sanity";
29
+ import { useCurrentUser, Preview, SanityDefaultPreview, useValuePreview, useClient, useSchema, useWorkspace, TextWithTone, usePerspective, useIsReleaseActive, useDocumentPreviewStore, useActiveReleases, useTemplates, useInitialValueResolverContext, resolveInitialValue, definePlugin, isObjectInputProps } from "sanity";
30
30
 
31
31
  import { CalendarIcon } from "@sanity/icons/Calendar";
32
32
 
@@ -78,6 +78,18 @@ import { switchMap, distinctUntilChanged } from "rxjs/operators";
78
78
 
79
79
  import { TransferIcon } from "@sanity/icons/Transfer";
80
80
 
81
+ const WORKFLOW_SYSTEM_TYPES = [ WORKFLOW_INSTANCE_TYPE, WORKFLOW_DEFINITION_TYPE, GUARD_DOC_TYPE ];
82
+
83
+ function contentDocumentTypes(schema) {
84
+ return schema.getTypeNames().filter(name => schema.get(name)?.type?.name === "document" && !WORKFLOW_SYSTEM_TYPES.includes(name));
85
+ }
86
+
87
+ const SYSTEM_TYPE_PREFIX = "sanity.";
88
+
89
+ function isSystemDocumentType(name) {
90
+ return name.startsWith(SYSTEM_TYPE_PREFIX) || WORKFLOW_SYSTEM_TYPES.includes(name);
91
+ }
92
+
81
93
  function isLocalContentResource(resource, contentResource) {
82
94
  return resource?.type === "dataset" && resource.id === contentResource.id;
83
95
  }
@@ -102,14 +114,14 @@ function assertUniqueWorkflowMappings(mappings) {
102
114
 
103
115
  function discoverWorkflowMappings(args) {
104
116
  assertUniqueWorkflowMappings(args.overrides ?? []);
105
- const discovered = latestDeployedDefinitions(args.definitions).flatMap(definition => {
117
+ const bindable = [ ...args.schemaContentTypes ].filter(name => !isSystemDocumentType(name)), discovered = latestDeployedDefinitions(args.definitions).flatMap(definition => {
106
118
  if (!isStartableDefinition(definition)) return [];
107
119
  const subject = (definition.fields ?? []).find(isSubjectEntry);
108
120
  if (subject === void 0 || !isInputSourced(subject)) return [];
109
121
  const source = {
110
122
  fields: [ ...definition.fields ?? [] ]
111
123
  };
112
- return [ ...args.schemaContentTypes ].filter(docType => acceptsDocumentType(source, docType)).map(docType => ({
124
+ return bindable.filter(docType => acceptsDocumentType(source, docType)).map(docType => ({
113
125
  docType: docType,
114
126
  definition: definition.name,
115
127
  label: definition.title ?? definition.name
@@ -240,14 +252,11 @@ function rowProblems(args) {
240
252
  mapping: args.mapping,
241
253
  contentResource: args.contentResource,
242
254
  declared: declared
243
- }), missing = missingRequiredInputs({
244
- entryDefs: declared,
245
- initialFields: seeded
246
- }), consumptionProblems = initialFieldIssues({
255
+ });
256
+ return [ ...initialFieldIssues({
247
257
  workflowFields: declared,
248
258
  initialFields: seeded
249
- }).flatMap(issue => seedConsumptionProblem(issue) ?? []);
250
- return [ ...missing.map(m => `its start would not provide the required entry "${m.name}" (${m.type})`), ...consumptionProblems, ...seeded.flatMap(seed => seedReferenceProblems({
259
+ }).flatMap(issue => seedConsumptionProblem(issue) ?? []), ...seeded.flatMap(seed => seedReferenceProblems({
251
260
  seed: seed,
252
261
  declared: declared
253
262
  })), ...seeded.flatMap(seed => seededSchemaProblems({
@@ -257,12 +266,24 @@ function rowProblems(args) {
257
266
  })) ];
258
267
  }
259
268
 
269
+ const NOT_DEPLOYED = "the definition is not deployed — deploy it or fix the mapping row";
270
+
271
+ function namesNoDeployedDefinition(issue) {
272
+ return issue.version === void 0;
273
+ }
274
+
260
275
  function mappingIssues(args) {
261
276
  const latestByName = new Map(latestDeployedDefinitions(args.definitions).map(d => [ d.name, d ])), issues = /* @__PURE__ */ new Map;
262
277
  for (const mapping of args.mappings) {
263
- const definition = latestByName.get(mapping.definition);
278
+ const identity = {
279
+ docType: mapping.docType,
280
+ definition: mapping.definition
281
+ }, definition = latestByName.get(mapping.definition);
264
282
  if (definition === void 0) {
265
- issues.set(mappingKey(mapping), `Workflow mapping for document type "${mapping.docType}" points at definition "${mapping.definition}", which is not deployed — deploy it or fix the mapping row.`);
283
+ issues.set(mappingKey(mapping), {
284
+ ...identity,
285
+ problems: [ NOT_DEPLOYED ]
286
+ });
266
287
  continue;
267
288
  }
268
289
  const problems = rowProblems({
@@ -271,11 +292,31 @@ function mappingIssues(args) {
271
292
  contentResource: args.contentResource,
272
293
  schemaContentTypes: args.schemaContentTypes
273
294
  });
274
- problems.length > 0 && issues.set(mappingKey(mapping), `Workflow mapping for document type "${mapping.docType}" points at definition "${mapping.definition}" (v${definition.version}), but ${problems.join("; ")}.`);
295
+ problems.length > 0 && issues.set(mappingKey(mapping), {
296
+ ...identity,
297
+ version: definition.version,
298
+ problems: problems
299
+ });
275
300
  }
276
301
  return issues;
277
302
  }
278
303
 
304
+ function sameMappingIssue(left, right) {
305
+ return left.docType === right.docType && left.definition === right.definition && left.version === right.version && left.problems.length === right.problems.length && left.problems.every((problem, index) => problem === right.problems[index]);
306
+ }
307
+
308
+ function mappingIssueDetail(issue) {
309
+ return issue.problems.join("; ");
310
+ }
311
+
312
+ function versionSuffix(issue) {
313
+ return issue.version === void 0 ? "" : ` (v${issue.version})`;
314
+ }
315
+
316
+ function mappingIssueLogLines(issues) {
317
+ return [ ...issues.values() ].map(issue => `The "${issue.docType}" → "${issue.definition}" mapping row${versionSuffix(issue)} is disabled: ${mappingIssueDetail(issue)}.`);
318
+ }
319
+
279
320
  const WORKFLOWS_VIEW_ID = "workflows", WORKFLOWS_TAB_PARAM = "workflowsTab";
280
321
 
281
322
  function withoutWorkflowsTab(params) {
@@ -318,10 +359,16 @@ function landingDefaultOpen(landing, instanceId) {
318
359
  if (!(landing === void 0 || !landing.instanceIds.includes(instanceId))) return instanceId === landing.focusedId;
319
360
  }
320
361
 
321
- const WORKFLOWS_TOOL_NAME = "workflows", WORKFLOW_INTENT = "workflow";
362
+ const WORKFLOWS_TOOL_NAME = "workflows";
363
+
364
+ function workflowsToolAvailable(tools) {
365
+ return tools.some(tool => tool.name === WORKFLOWS_TOOL_NAME);
366
+ }
367
+
368
+ const WORKFLOW_INTENT = "workflow";
322
369
 
323
- function pathSegmentTab(args) {
324
- return args.tabs.find(tab => tab === args.segment) ?? args.fallback;
370
+ function pathSegmentName(args) {
371
+ return args.names.find(name => name === args.segment);
325
372
  }
326
373
 
327
374
  function handlesWorkflowIntent(intent, params) {
@@ -335,11 +382,23 @@ function workflowIntentState(intent, params) {
335
382
  } : null;
336
383
  }
337
384
 
338
- const pad2 = n => String(n).padStart(2, "0");
385
+ const pad2 = n => String(n).padStart(2, "0"), DUE_DATE_FIELD_KINDS = [ "dueDate", "dueDatetime" ], DATE_FIELD_KINDS = [ "date", "datetime", ...DUE_DATE_FIELD_KINDS ], DATE_FIELD_KIND_SET = new Set(DATE_FIELD_KINDS), DUE_DATE_FIELD_KIND_SET = new Set(DUE_DATE_FIELD_KINDS);
386
+
387
+ function isDateFieldKind(kind) {
388
+ return DATE_FIELD_KIND_SET.has(kind);
389
+ }
390
+
391
+ function isDueDateFieldKind(kind) {
392
+ return DUE_DATE_FIELD_KIND_SET.has(kind);
393
+ }
394
+
395
+ function hasTimeOfDay(kind) {
396
+ return kind === "datetime" || kind === "dueDatetime";
397
+ }
339
398
 
340
399
  function parseDateFieldValue(value, kind) {
341
400
  if (typeof value != "string" || value === "") return;
342
- const parsed2 = kind === "date" ? parseLocalDate(value) : new Date(value);
401
+ const parsed2 = hasTimeOfDay(kind) ? new Date(value) : parseLocalDate(value);
343
402
  return Number.isNaN(parsed2.getTime()) ? void 0 : parsed2;
344
403
  }
345
404
 
@@ -355,16 +414,24 @@ function parseStoredDateValue(value) {
355
414
  }
356
415
 
357
416
  function serializeDateFieldValue(date, kind) {
358
- return kind === "datetime" ? date.toISOString() : `${date.getFullYear()}-${pad2(date.getMonth() + 1)}-${pad2(date.getDate())}`;
417
+ return hasTimeOfDay(kind) ? date.toISOString() : `${date.getFullYear()}-${pad2(date.getMonth() + 1)}-${pad2(date.getDate())}`;
418
+ }
419
+
420
+ class EffectsIncompleteError extends Error {
421
+ reason;
422
+ constructor(reason, options) {
423
+ super(`The change was committed, but running its effects failed: ${reason}`, options),
424
+ this.name = "EffectsIncompleteError", this.reason = reason;
425
+ }
359
426
  }
360
427
 
361
428
  const editDisabledSentence = {
362
429
  "edit-window-closed": () => "Can only be edited while its activity is active",
363
- "editor-not-permitted": () => "You don't have permission to edit this",
430
+ "editor-not-permitted": () => "You dont have permission to edit this",
364
431
  "instance-aborted": () => "This workflow was aborted",
365
432
  "instance-completed": () => "This workflow is already complete",
366
433
  "mutation-guard-denied": () => "An earlier step is holding this document",
367
- "not-editable": () => "This isn't editable here"
434
+ "not-editable": () => "This isnt editable here"
368
435
  };
369
436
 
370
437
  function describeEditDisabledReason(reason) {
@@ -437,24 +504,43 @@ const activityNotActiveSentence = {
437
504
  }, disabledSentence = {
438
505
  "activity-not-active": r => activityNotActiveSentence[r.status],
439
506
  "cascade-fired": () => "This happens automatically — the workflow runs it when its condition is met.",
440
- "filter-failed": () => "Not available to you yet — you may not have the required role, it may be assigned to someone else, or a needed value isn't set.",
507
+ "filter-failed": () => "Not available to you yet — you may not have the required role, it may be assigned to someone else, or a needed value isnt set.",
441
508
  "instance-aborted": () => "This workflow was aborted.",
442
509
  "instance-completed": () => "This workflow is already complete.",
443
510
  "mutation-guard-denied": () => "An earlier step is holding this document.",
444
- "requirements-unmet": () => "This step isn't ready yet — a required condition hasn't been met.",
511
+ "requirements-unmet": () => "This step isnt ready yet — a required condition hasnt been met.",
445
512
  "stage-terminal": () => "This stage is finished.",
446
- "subject-permission-denied": () => "You don't have permission to change the document this step works on."
513
+ "subject-permission-denied": () => "You dont have permission to change the document this step works on."
447
514
  };
448
515
 
449
516
  function describeDisabledReason(reason) {
450
517
  return disabledSentence[reason.kind](reason);
451
518
  }
452
519
 
520
+ function readRejection(err) {
521
+ return err instanceof ActionDisabledError ? {
522
+ kind: "refusal",
523
+ message: describeDisabledReason(err.reason)
524
+ } : err instanceof MutationGuardDeniedError ? {
525
+ kind: "refusal",
526
+ message: describeDisabledReason({
527
+ kind: "mutation-guard-denied",
528
+ denied: err.denied
529
+ })
530
+ } : err instanceof EditFieldDeniedError ? {
531
+ kind: "refusal",
532
+ message: describeEditDisabledReason(err.reason)
533
+ } : err instanceof EffectsIncompleteError ? {
534
+ kind: "committed-incomplete",
535
+ message: err.reason
536
+ } : {
537
+ kind: "failure",
538
+ message: errorMessage(err)
539
+ };
540
+ }
541
+
453
542
  function describeError(err) {
454
- return err instanceof ActionDisabledError ? describeDisabledReason(err.reason) : err instanceof MutationGuardDeniedError ? describeDisabledReason({
455
- kind: "mutation-guard-denied",
456
- denied: err.denied
457
- }) : errorMessage(err);
543
+ return readRejection(err).message;
458
544
  }
459
545
 
460
546
  function hrefIfHttp(value) {
@@ -510,6 +596,28 @@ function isTodoOverdue(item, today = /* @__PURE__ */ new Date) {
510
596
  return !item.dueDate || isTodoDone(item) ? !1 : item.dueDate < serializeDateFieldValue(today, "date");
511
597
  }
512
598
 
599
+ function todoEditKind(patch) {
600
+ return "assignee" in patch ? "assignee" : "due-date";
601
+ }
602
+
603
+ function assertNamesOneRow(items, key) {
604
+ const matches = items.filter(row => row._key === key).length;
605
+ if (matches !== 1) throw new Error(matches === 0 ? "This to-do is no longer in the stored list. Reload and try again." : `${matches} to-dos in this list share one id, so they cannot be told apart. The stored list has to be repaired before it can be edited.`);
606
+ }
607
+
608
+ function todoItemsPatched(args) {
609
+ const {items: items, key: key, patch: patch} = args;
610
+ return assertNamesOneRow(items, key), items.map(row => row._key === key ? {
611
+ ...row,
612
+ ...patch
613
+ } : row);
614
+ }
615
+
616
+ function todoItemsWithout(args) {
617
+ const {items: items, key: key} = args;
618
+ return assertNamesOneRow(items, key), items.filter(row => row._key !== key);
619
+ }
620
+
513
621
  function tickOpsOf(action) {
514
622
  return (action.ops ?? []).flatMap(o => o.type === "field.updateWhere" ? [ o ] : []);
515
623
  }
@@ -950,11 +1058,23 @@ function rowAssignControlState(args) {
950
1058
 
951
1059
  function rowDateControlState(args) {
952
1060
  return rowControlState({
953
- kinds: [ "date", "datetime" ],
1061
+ kinds: DUE_DATE_FIELD_KINDS,
954
1062
  ...args
955
1063
  });
956
1064
  }
957
1065
 
1066
+ function dateValuesOf(state, matches) {
1067
+ return state.filter(entry => matches(entry._type)).map(entry => entry.value).filter(value => typeof value == "string" && value !== "");
1068
+ }
1069
+
1070
+ function datesOf(state) {
1071
+ return dateValuesOf(state, isDateFieldKind);
1072
+ }
1073
+
1074
+ function dueDatesOf(state) {
1075
+ return dateValuesOf(state, isDueDateFieldKind);
1076
+ }
1077
+
958
1078
  function committedRowFace(row) {
959
1079
  return {
960
1080
  activityName: row.activityName,
@@ -966,7 +1086,11 @@ function committedRowFace(row) {
966
1086
  }
967
1087
 
968
1088
  function activityRowProps(args) {
969
- const {activityEval: activityEval, activityState: activityState, editableFields: editableFields} = args, state = activityState.get(activityEval.activity.name) ?? [];
1089
+ const {activityEval: activityEval, activityState: activityState, editableFields: editableFields} = args, state = activityState.get(activityEval.activity.name) ?? [], controlArgs = {
1090
+ activity: activityEval.activity,
1091
+ status: activityEval.status,
1092
+ editableFields: editableFields
1093
+ };
970
1094
  return {
971
1095
  face: {
972
1096
  activityName: activityEval.activity.name,
@@ -975,12 +1099,10 @@ function activityRowProps(args) {
975
1099
  automated: showsAutomatedPill(activityEval),
976
1100
  blocked: showsBlockedTag(activityEval)
977
1101
  },
978
- assignState: rowAssignControlState({
979
- activity: activityEval.activity,
980
- status: activityEval.status,
981
- editableFields: editableFields
982
- }),
1102
+ assignState: rowAssignControlState(controlArgs),
983
1103
  assigneeIds: assigneeUserIdsOf(state),
1104
+ dateState: rowDateControlState(controlArgs),
1105
+ dueDates: dueDatesOf(state),
984
1106
  terminalActions: rowTerminalActions({
985
1107
  activityEval: activityEval,
986
1108
  state: state,
@@ -990,12 +1112,12 @@ function activityRowProps(args) {
990
1112
  }
991
1113
 
992
1114
  function dateControlKind(state) {
993
- if (state.kind === "editable" && (state.field.type === "date" || state.field.type === "datetime")) return state.field.type;
1115
+ if (state.kind === "editable" && isDueDateFieldKind(state.field.type)) return state.field.type;
994
1116
  }
995
1117
 
996
1118
  function dateControlValue(args) {
997
- const {state: state, dates: dates} = args;
998
- return state.kind === "editable" ? typeof state.field.value == "string" && state.field.value !== "" ? state.field.value : void 0 : dates.find(value => parseStoredDateValue(value) !== void 0);
1119
+ const {state: state, dueDates: dueDates} = args;
1120
+ return state.kind === "editable" ? typeof state.field.value == "string" && state.field.value !== "" ? state.field.value : void 0 : dueDates.find(value => parseStoredDateValue(value) !== void 0);
999
1121
  }
1000
1122
 
1001
1123
  function activityStateOf(instance, activityName) {
@@ -1020,7 +1142,7 @@ function deriveActivityDetail(args) {
1020
1142
  };
1021
1143
  }
1022
1144
 
1023
- const WORKFLOW_API_VERSION = ENGINE_API_VERSION, WORKFLOW_SYSTEM_TYPES = [ WORKFLOW_INSTANCE_TYPE, WORKFLOW_DEFINITION_TYPE, GUARD_DOC_TYPE ], EMPTY_ENTRIES = [], WorkflowContext = createContext(null);
1145
+ const WORKFLOW_API_VERSION = ENGINE_API_VERSION, EMPTY_ENTRIES = [], WorkflowContext = createContext(null);
1024
1146
 
1025
1147
  function useWorkflowContext() {
1026
1148
  const value = useContext(WorkflowContext);
@@ -1062,7 +1184,7 @@ function useWorkflowInstanceEntry(instanceId, options = {}) {
1062
1184
 
1063
1185
  const WorkflowToolOpened = defineEvent({
1064
1186
  name: "Editorial Workflows Studio Plugin Tool Opened",
1065
- version: 1,
1187
+ version: 2,
1066
1188
  description: "The Workflows tool was opened or its tab was switched"
1067
1189
  }), WorkflowDocumentViewOpened = defineEvent({
1068
1190
  name: "Editorial Workflows Studio Plugin Document View Opened",
@@ -1072,10 +1194,27 @@ const WorkflowToolOpened = defineEvent({
1072
1194
  name: "Editorial Workflows Studio Plugin Instance Detail Viewed",
1073
1195
  version: 1,
1074
1196
  description: "The Workflows tool's per-instance detail view was opened"
1197
+ });
1198
+
1199
+ function definitionFingerprint(definition) {
1200
+ const hash = definition?.contentHash;
1201
+ return hash === void 0 ? {} : {
1202
+ definitionContentHash: hash
1203
+ };
1204
+ }
1205
+
1206
+ const WorkflowDefinitionDetailViewed = defineEvent({
1207
+ name: "Editorial Workflows Studio Plugin Definition Detail Viewed",
1208
+ version: 1,
1209
+ description: "A workflow's definition page was opened in the Workflows tool"
1075
1210
  }), WorkflowFormStripClicked = defineEvent({
1076
1211
  name: "Editorial Workflows Studio Plugin Form Strip Clicked",
1077
1212
  version: 1,
1078
1213
  description: "The form strip's instance line was clicked, opening the document view"
1214
+ }), WorkflowDocumentLinkClicked = defineEvent({
1215
+ name: "Editorial Workflows Studio Plugin Document Link Clicked",
1216
+ version: 2,
1217
+ description: "A document link was clicked, opening the referenced document's editor"
1079
1218
  }), WorkflowStartDialogOpened = defineEvent({
1080
1219
  name: "Editorial Workflows Studio Plugin Start Dialog Opened",
1081
1220
  version: 1,
@@ -1090,8 +1229,12 @@ const WorkflowToolOpened = defineEvent({
1090
1229
  description: "A fresh document's auto-start ran — one start request per configured workflow"
1091
1230
  }), WorkflowActionControlUsed = defineEvent({
1092
1231
  name: "Editorial Workflows Studio Plugin Action Control Used",
1093
- version: 1,
1232
+ version: 2,
1094
1233
  description: "An action-firing control was used, attributed to its UI surface"
1234
+ }), WorkflowFieldControlUsed = defineEvent({
1235
+ name: "Editorial Workflows Studio Plugin Field Control Used",
1236
+ version: 1,
1237
+ description: "A field-editing control committed an edit, attributed to its UI surface"
1095
1238
  }), WorkflowActivityDialogOpened = defineEvent({
1096
1239
  name: "Editorial Workflows Studio Plugin Activity Dialog Opened",
1097
1240
  version: 1,
@@ -1100,10 +1243,30 @@ const WorkflowToolOpened = defineEvent({
1100
1243
  name: "Editorial Workflows Studio Plugin Task Filters Applied",
1101
1244
  version: 1,
1102
1245
  description: "The tool's task-filter menu closed with a changed selection"
1246
+ }), WorkflowBoardWorkflowSelected = defineEvent({
1247
+ name: "Editorial Workflows Studio Plugin Board Workflow Selected",
1248
+ version: 2,
1249
+ description: "The workflow documents page settled on the workflow it's showing"
1250
+ }), WorkflowTitleSeedDrifted = defineEvent({
1251
+ name: "Editorial Workflows Studio Plugin Title Seed Drifted",
1252
+ version: 1,
1253
+ description: "A cold-start seeded preview title diverged from the live preview pipeline"
1103
1254
  }), WorkflowTodoToggled = defineEvent({
1104
1255
  name: "Editorial Workflows Studio Plugin Todo Toggled",
1105
1256
  version: 1,
1106
1257
  description: "A todo checkbox was toggled, attributed to its UI surface and write seam"
1258
+ }), WorkflowTodoEdited = defineEvent({
1259
+ name: "Editorial Workflows Studio Plugin Todo Edited",
1260
+ version: 1,
1261
+ description: "A non-toggle todo-list write, attributed to its UI surface and gesture"
1262
+ }), WorkflowAbortDialogOpened = defineEvent({
1263
+ name: "Editorial Workflows Studio Plugin Abort Dialog Opened",
1264
+ version: 1,
1265
+ description: "The abort-workflow confirm was opened"
1266
+ }), WorkflowAbortDialogSubmitted = defineEvent({
1267
+ name: "Editorial Workflows Studio Plugin Abort Dialog Submitted",
1268
+ version: 1,
1269
+ description: "The abort-workflow confirm was submitted, carrying the attempt's outcome"
1107
1270
  });
1108
1271
 
1109
1272
  function useLogEventOnMount(event, data) {
@@ -1193,20 +1356,32 @@ function useUserDisplay(id) {
1193
1356
  };
1194
1357
  }
1195
1358
 
1196
- const warnedNotMember = /* @__PURE__ */ new Set;
1359
+ const warnedSelfUnavailable = /* @__PURE__ */ new Set;
1360
+
1361
+ function warnSelfUnavailableOnce(key, message) {
1362
+ warnedSelfUnavailable.has(key) || (warnedSelfUnavailable.add(key), console.warn(message));
1363
+ }
1364
+
1365
+ function bridgedSelfId(users, meId) {
1366
+ const classified = classifyPrincipalId(meId);
1367
+ return users.find(user => user.membership.id === meId)?.profile?.sanityUserId ?? classified.globalId ?? (classified.namespace === "project" ? void 0 : meId);
1368
+ }
1197
1369
 
1198
1370
  function useBridgedSelf() {
1199
- const me = useCurrentUser(), {users: users, loading: loading} = useStudioProjectUsers();
1200
- if (!me || loading) return;
1201
- const classified = classifyPrincipalId(me.id), id = users.find(user => user.membership.id === me.id)?.profile?.sanityUserId ?? classified.globalId ?? (classified.namespace === "project" ? void 0 : me.id);
1202
- if (id === void 0) {
1203
- warnedNotMember.has(me.id) || (warnedNotMember.add(me.id), console.warn(`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.`));
1204
- return;
1205
- }
1206
- return {
1371
+ const me = useCurrentUser(), {users: users, loading: loading, error: error} = useStudioProjectUsers();
1372
+ if (!me) return;
1373
+ const id = bridgedSelfId(users, me.id);
1374
+ if (id !== void 0) return {
1207
1375
  id: id,
1208
1376
  roles: me.roles ?? []
1209
1377
  };
1378
+ if (!loading) {
1379
+ if (error !== void 0) {
1380
+ 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.`);
1381
+ return;
1382
+ }
1383
+ 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.`);
1384
+ }
1210
1385
  }
1211
1386
 
1212
1387
  function useSelfActor() {
@@ -1326,9 +1501,50 @@ function buildParams(decls, values) {
1326
1501
  };
1327
1502
  }
1328
1503
 
1329
- const NEVER_MS = 2 ** 31 - 1;
1504
+ function useDelayedFlag(active, delayMs) {
1505
+ const [held, setHeld] = useState(!1);
1506
+ return useEffect(() => {
1507
+ if (!active) {
1508
+ setHeld(!1);
1509
+ return;
1510
+ }
1511
+ const timer = setTimeout(() => setHeld(!0), delayMs);
1512
+ return () => clearTimeout(timer);
1513
+ }, [ active, delayMs ]), held && active;
1514
+ }
1515
+
1516
+ const NEVER_MS = 2 ** 31 - 1, OPEN_DELAY_MS = 300;
1330
1517
 
1331
1518
  function HoverHint(props) {
1519
+ return props.anchor !== void 0 ? /* @__PURE__ */ jsx(AnchoredHint, {
1520
+ ...props
1521
+ }) : /* @__PURE__ */ jsx(WrappingHint, {
1522
+ ...props
1523
+ });
1524
+ }
1525
+
1526
+ function AnchoredHint(props) {
1527
+ const {disabled: disabled = !1} = props, open = useDelayedFlag(props.open === !0 && !disabled, OPEN_DELAY_MS);
1528
+ /* @__PURE__ */
1529
+ return jsx(Popover, {
1530
+ animate: !0,
1531
+ content: /* @__PURE__ */ jsx(HintBody, {
1532
+ ...props
1533
+ }),
1534
+ open: open,
1535
+ padding: hintPadding(props),
1536
+ placement: props.placement ?? "top",
1537
+ portal: !0,
1538
+ radius: 3,
1539
+ referenceElement: props.anchor
1540
+ });
1541
+ }
1542
+
1543
+ function hintPadding(props) {
1544
+ return props.content !== void 0 && props.flush ? 0 : 2;
1545
+ }
1546
+
1547
+ function WrappingHint(props) {
1332
1548
  const {children: children, disabled: disabled = !1} = props;
1333
1549
  /* @__PURE__ */
1334
1550
  return jsx(Tooltip, {
@@ -1338,17 +1554,20 @@ function HoverHint(props) {
1338
1554
  }),
1339
1555
  delay: {
1340
1556
  close: 0,
1341
- open: disabled ? NEVER_MS : 300
1557
+ open: disabled ? NEVER_MS : OPEN_DELAY_MS
1342
1558
  },
1343
- padding: props.content !== void 0 && props.flush ? 0 : 2,
1344
- placement: "top",
1559
+ padding: hintPadding(props),
1560
+ placement: props.placement ?? "top",
1345
1561
  portal: !0,
1346
1562
  radius: 3,
1347
1563
  children: /* @__PURE__ */ jsx(Box, {
1348
1564
  "data-testid": props["data-testid"],
1349
1565
  style: {
1350
- display: "inline-flex",
1351
- maxWidth: "100%"
1566
+ display: props.fill ? "flex" : "inline-flex",
1567
+ maxWidth: "100%",
1568
+ ...props.fill ? {
1569
+ width: "100%"
1570
+ } : {}
1352
1571
  },
1353
1572
  children: children
1354
1573
  })
@@ -1436,7 +1655,7 @@ function DismissablePopover({children: children, content: content, onDismiss: on
1436
1655
  function DateFieldInput({kind: kind, value: value, onChange: onChange, readOnly: readOnly = !1}) {
1437
1656
  const [open, setOpen] = useState(!1), parsed2 = parseDateFieldValue(value, kind), close = () => setOpen(!1), toggle = () => {
1438
1657
  readOnly || setOpen(v => !v);
1439
- };
1658
+ }, pickHint = hasTimeOfDay(kind) ? "Pick a date and time…" : "Pick a date…";
1440
1659
  /* @__PURE__ */
1441
1660
  return jsx(DismissablePopover, {
1442
1661
  content: /* @__PURE__ */ jsx(ClearableDatePicker, {
@@ -1447,7 +1666,7 @@ function DateFieldInput({kind: kind, value: value, onChange: onChange, readOnly:
1447
1666
  onPick: next => {
1448
1667
  close(), onChange(serializeDateFieldValue(next, kind));
1449
1668
  },
1450
- selectTime: kind === "datetime",
1669
+ selectTime: hasTimeOfDay(kind),
1451
1670
  value: parsed2
1452
1671
  }),
1453
1672
  onDismiss: close,
@@ -1460,7 +1679,7 @@ function DateFieldInput({kind: kind, value: value, onChange: onChange, readOnly:
1460
1679
  children: /* @__PURE__ */ jsx(TextInput, {
1461
1680
  fontSize: 1,
1462
1681
  onClick: toggle,
1463
- placeholder: readOnly ? "" : kind === "datetime" ? "Pick a date and time…" : "Pick a date…",
1682
+ placeholder: readOnly ? "" : pickHint,
1464
1683
  readOnly: !0,
1465
1684
  value: parsed2 ? formatDate(String(value)) : ""
1466
1685
  })
@@ -1504,7 +1723,7 @@ function ChoiceSelect({options: options, value: value, onChange: onChange, readO
1504
1723
  });
1505
1724
  }
1506
1725
 
1507
- const SCALAR_INPUT_KINDS = [ "string", "text", "url", "number", "progress", "date", "dateTime", "datetime", "boolean" ], SCALAR_INPUT_KIND_SET = new Set(SCALAR_INPUT_KINDS);
1726
+ const SCALAR_INPUT_KINDS = [ "string", "text", "url", "number", "progress", ...DATE_FIELD_KINDS, "dateTime", "boolean" ], SCALAR_INPUT_KIND_SET = new Set(SCALAR_INPUT_KINDS);
1508
1727
 
1509
1728
  function isScalarInputKind(kind) {
1510
1729
  return SCALAR_INPUT_KIND_SET.has(kind);
@@ -1522,8 +1741,8 @@ function ScalarInput({kind: kind, value: value, onChange: onChange, options: opt
1522
1741
  }) : kind === "boolean" ? /* @__PURE__ */ jsx(Checkbox, {
1523
1742
  checked: value === !0,
1524
1743
  onChange: e => onChange(e.currentTarget.checked)
1525
- }) : kind === "date" || kind === "dateTime" || kind === "datetime" ? /* @__PURE__ */ jsx(DateFieldInput, {
1526
- kind: kind === "date" ? "date" : "datetime",
1744
+ }) : isDateFieldKind(kind) || kind === "dateTime" ? /* @__PURE__ */ jsx(DateFieldInput, {
1745
+ kind: kind === "dateTime" ? "datetime" : kind,
1527
1746
  onChange: next => onChange(next ?? ""),
1528
1747
  value: value
1529
1748
  }) : /* @__PURE__ */ jsx(TextInput, {
@@ -1629,11 +1848,16 @@ function useStubValuePreview(id, schemaType) {
1629
1848
  };
1630
1849
  }
1631
1850
 
1851
+ function PreviewPlaceholder() {
1852
+ /* @__PURE__ */
1853
+ return jsx(SanityDefaultPreview, {
1854
+ isPlaceholder: !0
1855
+ });
1856
+ }
1857
+
1632
1858
  function StubPreview({id: id, schemaType: schemaType}) {
1633
1859
  const {value: value, preview: preview} = useStubValuePreview(id, schemaType);
1634
- return preview.isLoading ? /* @__PURE__ */ jsx(SanityDefaultPreview, {
1635
- isPlaceholder: !0
1636
- }) : /* @__PURE__ */ jsx(Preview, {
1860
+ return preview.isLoading ? /* @__PURE__ */ jsx(PreviewPlaceholder, {}) : /* @__PURE__ */ jsx(Preview, {
1637
1861
  layout: "default",
1638
1862
  schemaType: schemaType,
1639
1863
  skipVisibilityCheck: !0,
@@ -1866,14 +2090,87 @@ function ParamsForm({decls: decls, values: values, onChange: onChange}) {
1866
2090
  });
1867
2091
  }
1868
2092
 
1869
- function useClosableToast() {
2093
+ const TOAST_ID = {
2094
+ abort: "workflow-abort",
2095
+ detailsCopy: "workflow-details-copy",
2096
+ documentCreate: "workflow-document-create",
2097
+ documentSave: "workflow-document-save",
2098
+ effectResolve: "workflow-effect-resolve",
2099
+ effectsDrain: "workflow-effects-drain",
2100
+ effectsIncomplete: "workflow-effects-incomplete",
2101
+ orphanSettle: "workflow-orphan-settle",
2102
+ start: "workflow-start"
2103
+ };
2104
+
2105
+ function instanceToastId(gesture, instanceId) {
2106
+ return `workflow-${gesture}:${instanceId}`;
2107
+ }
2108
+
2109
+ function actionFireToastId(args) {
2110
+ return `workflow-action-fire:${args.instanceId}:${args.activity}:${args.action}`;
2111
+ }
2112
+
2113
+ const REASON_TOAST_DURATION = 2e4, DISMISS_SENTINEL_DURATION = .01;
2114
+
2115
+ function carriesReason(params) {
2116
+ return params.description !== void 0 && params.status !== "info";
2117
+ }
2118
+
2119
+ function withConvention(params) {
2120
+ return {
2121
+ ...params,
2122
+ closable: !0,
2123
+ ...carriesReason(params) ? {
2124
+ duration: REASON_TOAST_DURATION
2125
+ } : {}
2126
+ };
2127
+ }
2128
+
2129
+ function useWorkflowToast() {
1870
2130
  const toast = useToast();
1871
2131
  return useMemo(() => ({
1872
- ...toast,
1873
- push: params => toast.push({
1874
- ...params,
1875
- closable: !0
1876
- })
2132
+ push: params => {
2133
+ toast.push(withConvention(params));
2134
+ },
2135
+ dismiss: id => {
2136
+ toast.push({
2137
+ id: id,
2138
+ duration: DISMISS_SENTINEL_DURATION
2139
+ });
2140
+ }
2141
+ }), [ toast ]);
2142
+ }
2143
+
2144
+ const COMMITTED_INCOMPLETE_TITLE = "Saved, but its follow-up didn’t run";
2145
+
2146
+ function rejectionToast(reading, titles) {
2147
+ return reading.kind === "committed-incomplete" ? {
2148
+ id: TOAST_ID.effectsIncomplete,
2149
+ status: "warning",
2150
+ title: COMMITTED_INCOMPLETE_TITLE,
2151
+ description: reading.message
2152
+ } : reading.kind === "refusal" ? {
2153
+ id: titles.id,
2154
+ status: "warning",
2155
+ title: titles.refused,
2156
+ description: reading.message
2157
+ } : {
2158
+ id: titles.id,
2159
+ status: "error",
2160
+ title: titles.failed,
2161
+ description: reading.message
2162
+ };
2163
+ }
2164
+
2165
+ function useRejectionReport() {
2166
+ const toast = useWorkflowToast();
2167
+ return useMemo(() => ({
2168
+ rejected: args => {
2169
+ const reading = readRejection(args.err);
2170
+ reading.kind !== "refusal" && console.error(`[workflow-studio-plugin] ${args.context} rejected:`, args.err),
2171
+ toast.push(rejectionToast(reading, args));
2172
+ },
2173
+ dismiss: toast.dismiss
1877
2174
  }), [ toast ]);
1878
2175
  }
1879
2176
 
@@ -1896,10 +2193,21 @@ function useActionClusterPending() {
1896
2193
  }
1897
2194
 
1898
2195
  function useFireAction(args) {
1899
- const {instanceId: instanceId, activity: activity, action: action, label: label, surface: surface, viaMenu: viaMenu} = args, {fireActionFor: fireActionFor} = useWorkflowContext(), telemetry = useWorkflowTelemetry(), toast = useClosableToast(), cluster = useActionClusterLock(), [localPending, setLocalPending] = useState(!1), pending = localPending || cluster?.pending === !0;
2196
+ const {instanceId: instanceId, activity: activity, action: action, label: label, surface: surface, placement: placement, viaMenu: viaMenu} = args, {fireActionFor: fireActionFor} = useWorkflowContext(), telemetry = useWorkflowTelemetry(), report = useRejectionReport(), cluster = useActionClusterLock(), [localPending, setLocalPending] = useState(!1), pending = localPending || cluster?.pending === !0, toastId = actionFireToastId({
2197
+ instanceId: instanceId,
2198
+ activity: activity,
2199
+ action: action
2200
+ });
1900
2201
  return {
1901
2202
  fire: useCallback(async params => {
1902
2203
  if (pending) return !1;
2204
+ const log = success => telemetry.log(WorkflowActionControlUsed, {
2205
+ instanceId: instanceId,
2206
+ surface: surface,
2207
+ placement: placement,
2208
+ viaMenu: viaMenu,
2209
+ success: success
2210
+ });
1903
2211
  setLocalPending(!0), cluster?.onFireChange(!0);
1904
2212
  try {
1905
2213
  return await fireActionFor(instanceId, {
@@ -1908,31 +2216,19 @@ function useFireAction(args) {
1908
2216
  ...params && Object.keys(params).length > 0 ? {
1909
2217
  params: params
1910
2218
  } : {}
1911
- }), toast.push({
1912
- status: "success",
1913
- title: `${label} done`
1914
- }), telemetry.log(WorkflowActionControlUsed, {
1915
- instanceId: instanceId,
1916
- surface: surface,
1917
- viaMenu: viaMenu,
1918
- success: !0
1919
- }), !0;
2219
+ }), report.dismiss(toastId), log(!0), !0;
1920
2220
  } catch (err) {
1921
- return console.error(`[workflow-studio-plugin] firing "${action}" on "${activity}" failed:`, err),
1922
- toast.push({
1923
- status: "error",
1924
- title: `${label} failed`,
1925
- description: describeError(err)
1926
- }), telemetry.log(WorkflowActionControlUsed, {
1927
- instanceId: instanceId,
1928
- surface: surface,
1929
- viaMenu: viaMenu,
1930
- success: !1
1931
- }), !1;
2221
+ return report.rejected({
2222
+ err: err,
2223
+ context: `firing "${action}" on "${activity}"`,
2224
+ id: toastId,
2225
+ refused: `“${label}” isn’t available right now`,
2226
+ failed: `Failed to complete “${label}”`
2227
+ }), log(!1), !1;
1932
2228
  } finally {
1933
2229
  setLocalPending(!1), cluster?.onFireChange(!1);
1934
2230
  }
1935
- }, [ fireActionFor, instanceId, activity, action, label, pending, toast, cluster, telemetry, surface, viaMenu ]),
2231
+ }, [ fireActionFor, instanceId, activity, action, label, pending, report, toastId, cluster, telemetry, surface, placement, viaMenu ]),
1936
2232
  pending: pending
1937
2233
  };
1938
2234
  }
@@ -1973,13 +2269,14 @@ function RowClickShield({active: active, children: children}) {
1973
2269
  });
1974
2270
  }
1975
2271
 
1976
- function FireButton({instanceId: instanceId, activity: activity, action: action, label: label, surface: surface, mode: mode = "default", chrome: chrome, tone: tone = "default", icon: icon}) {
2272
+ function FireButton({instanceId: instanceId, activity: activity, action: action, label: label, surface: surface, placement: placement, mode: mode = "default", chrome: chrome, tone: tone = "default", icon: icon}) {
1977
2273
  const {fire: fire, pending: pending} = useFireAction({
1978
2274
  instanceId: instanceId,
1979
2275
  activity: activity,
1980
2276
  action: action,
1981
2277
  label: label,
1982
2278
  surface: surface,
2279
+ placement: placement,
1983
2280
  viaMenu: !1
1984
2281
  }), handleClick = e => {
1985
2282
  chrome?.inButtonCard === !0 && e.stopPropagation(), !pending && fire();
@@ -2001,7 +2298,7 @@ function FireButton({instanceId: instanceId, activity: activity, action: action,
2001
2298
  });
2002
2299
  }
2003
2300
 
2004
- function ActivityActionRow({actionEval: actionEval, instanceId: instanceId, activity: activity, surface: surface, chrome: chrome}) {
2301
+ function ActivityActionRow({actionEval: actionEval, instanceId: instanceId, activity: activity, surface: surface, placement: placement, chrome: chrome}) {
2005
2302
  const rowKind = actionRowKind(actionEval);
2006
2303
  return rowKind.kind === "disabled" ? /* @__PURE__ */ jsx(DisabledActionButton, {
2007
2304
  actionEval: actionEval,
@@ -2012,12 +2309,14 @@ function ActivityActionRow({actionEval: actionEval, instanceId: instanceId, acti
2012
2309
  instanceId: instanceId,
2013
2310
  activity: activity,
2014
2311
  chrome: chrome,
2312
+ placement: placement,
2015
2313
  surface: surface
2016
2314
  }) : /* @__PURE__ */ jsx(ParamsActionButton, {
2017
2315
  actionEval: actionEval,
2018
2316
  instanceId: instanceId,
2019
2317
  activity: activity,
2020
2318
  chrome: chrome,
2319
+ placement: placement,
2021
2320
  surface: surface
2022
2321
  });
2023
2322
  }
@@ -2043,7 +2342,7 @@ function DisabledActionButton({actionEval: actionEval, activity: activity, chrom
2043
2342
  }) : button;
2044
2343
  }
2045
2344
 
2046
- function PlainActionButton({actionEval: actionEval, instanceId: instanceId, activity: activity, surface: surface, chrome: chrome}) {
2345
+ function PlainActionButton({actionEval: actionEval, instanceId: instanceId, activity: activity, surface: surface, placement: placement, chrome: chrome}) {
2047
2346
  const {action: action} = actionEval, label = actionLabel(action), btn = actionButtonFace({
2048
2347
  actionEval: actionEval,
2049
2348
  activityName: activity
@@ -2055,6 +2354,7 @@ function PlainActionButton({actionEval: actionEval, instanceId: instanceId, acti
2055
2354
  mode: btn.mode,
2056
2355
  activity: activity,
2057
2356
  chrome: chrome,
2357
+ placement: placement,
2058
2358
  surface: surface,
2059
2359
  tone: btn.tone
2060
2360
  });
@@ -2064,13 +2364,14 @@ function PlainActionButton({actionEval: actionEval, instanceId: instanceId, acti
2064
2364
  }) : button;
2065
2365
  }
2066
2366
 
2067
- function ParamsActionDialog({actionEval: actionEval, instanceId: instanceId, activity: activity, surface: surface, viaMenu: viaMenu, onClose: onClose}) {
2367
+ function ParamsActionDialog({actionEval: actionEval, instanceId: instanceId, activity: activity, surface: surface, placement: placement, viaMenu: viaMenu, onClose: onClose}) {
2068
2368
  const {action: action} = actionEval, label = actionLabel(action), decls = action.params ?? [], [values, setValues] = useState({}), {fire: fire, pending: pending} = useFireAction({
2069
2369
  instanceId: instanceId,
2070
2370
  activity: activity,
2071
2371
  action: action.name,
2072
2372
  label: label,
2073
2373
  surface: surface,
2374
+ placement: placement,
2074
2375
  viaMenu: viaMenu
2075
2376
  }), built = buildParams(decls, values), confirm = async () => {
2076
2377
  await fire(built.params) && onClose();
@@ -2126,7 +2427,7 @@ function ParamsActionDialog({actionEval: actionEval, instanceId: instanceId, act
2126
2427
  });
2127
2428
  }
2128
2429
 
2129
- function ParamsActionButton({actionEval: actionEval, instanceId: instanceId, activity: activity, surface: surface, chrome: chrome}) {
2430
+ function ParamsActionButton({actionEval: actionEval, instanceId: instanceId, activity: activity, surface: surface, placement: placement, chrome: chrome}) {
2130
2431
  const [open, setOpen] = useState(!1), btn = actionButtonFace({
2131
2432
  actionEval: actionEval,
2132
2433
  activityName: activity
@@ -2152,6 +2453,7 @@ function ParamsActionButton({actionEval: actionEval, instanceId: instanceId, act
2152
2453
  activity: activity,
2153
2454
  instanceId: instanceId,
2154
2455
  onClose: () => setOpen(!1),
2456
+ placement: placement,
2155
2457
  surface: surface,
2156
2458
  viaMenu: !1
2157
2459
  })
@@ -2325,7 +2627,9 @@ function FieldInput$1({kind: kind, value: value, onChange: onChange, onEnter: on
2325
2627
  });
2326
2628
 
2327
2629
  case "date":
2630
+ case "dueDate":
2328
2631
  case "datetime":
2632
+ case "dueDatetime":
2329
2633
  /* @__PURE__ */
2330
2634
  return jsx(DateFieldInput, {
2331
2635
  kind: kind,
@@ -2371,22 +2675,64 @@ function FieldInput$1({kind: kind, value: value, onChange: onChange, onEnter: on
2371
2675
  }
2372
2676
  }
2373
2677
 
2374
- function LinkChip({hint: hint, children: children, as: as = "a", ...anchorProps}) {
2678
+ function isCompact(layout) {
2679
+ return layout === "inline" || layout === "row";
2680
+ }
2681
+
2682
+ function docFaceChrome(args) {
2683
+ const {layout: layout, state: state} = args, compact = isCompact(layout);
2684
+ switch (state.kind) {
2685
+ case "linked":
2686
+ return {
2687
+ bareId: state.bareId,
2688
+ selfInset: !compact
2689
+ };
2690
+
2691
+ case "foreign":
2692
+ return {};
2693
+
2694
+ case "missing":
2695
+ return {
2696
+ tone: "caution"
2697
+ };
2698
+
2699
+ case "unopenable":
2700
+ return {};
2701
+
2702
+ case "pending":
2703
+ return compact ? {} : {
2704
+ selfInset: !0
2705
+ };
2706
+
2707
+ case "unresolvable":
2708
+ return {};
2709
+ }
2710
+ }
2711
+
2712
+ function chipPadding(fill) {
2713
+ return fill ? 3 : 2;
2714
+ }
2715
+
2716
+ function LinkChip({hint: hint, children: children, as: as = "a", fill: fill = !1, ...anchorProps}) {
2375
2717
  /* @__PURE__ */
2376
2718
  return jsx(HoverHint, {
2719
+ fill: fill,
2377
2720
  text: hint,
2378
2721
  children: /* @__PURE__ */ jsx(Card, {
2379
2722
  __unstable_focusRing: !0,
2380
2723
  as: as,
2381
2724
  "data-as": "a",
2382
- padding: 2,
2725
+ padding: chipPadding(fill),
2383
2726
  radius: 3,
2384
2727
  style: {
2385
2728
  alignItems: "center",
2386
2729
  color: "inherit",
2387
2730
  display: "flex",
2388
2731
  minWidth: 0,
2389
- textDecoration: "none"
2732
+ textDecoration: "none",
2733
+ ...fill ? {
2734
+ width: "100%"
2735
+ } : {}
2390
2736
  },
2391
2737
  tone: "inherit",
2392
2738
  ...anchorProps,
@@ -2505,7 +2851,7 @@ function DocRefFace({gdr: gdr}) {
2505
2851
  });
2506
2852
  }
2507
2853
 
2508
- function ForeignRefNotice({bareId: bareId, resource: resource}) {
2854
+ function foreignNotice(bareId, resource) {
2509
2855
  const location = resource.type === "dataset" ? `another dataset (${resource.id})` : `${resource.type} (${resource.id})`;
2510
2856
  /* @__PURE__ */
2511
2857
  return jsxs(Text, {
@@ -2515,44 +2861,22 @@ function ForeignRefNotice({bareId: bareId, resource: resource}) {
2515
2861
  });
2516
2862
  }
2517
2863
 
2518
- function MissingDocNotice({bareId: bareId, layout: layout}) {
2519
- return layout === "inline" ?
2520
- /* @__PURE__ */ jsx(Box, {
2521
- padding: 2,
2522
- children: /* @__PURE__ */ jsxs(Flex, {
2523
- align: "center",
2524
- gap: 2,
2525
- children: [
2526
- /* @__PURE__ */ jsx(Text, {
2527
- muted: !0,
2528
- size: 1,
2529
- children: /* @__PURE__ */ jsx(WarningOutlineIcon, {})
2530
- }),
2531
- /* @__PURE__ */ jsx(Text, {
2532
- muted: !0,
2533
- size: 1,
2534
- children: "Document unavailable"
2535
- }) ]
2536
- })
2537
- }) : /* @__PURE__ */ jsx(Card, {
2538
- border: !0,
2539
- padding: 2,
2540
- radius: 3,
2541
- tone: "caution",
2542
- children: /* @__PURE__ */ jsxs(Flex, {
2543
- align: "center",
2544
- gap: 2,
2545
- children: [
2546
- /* @__PURE__ */ jsx(Text, {
2547
- size: 1,
2548
- children: /* @__PURE__ */ jsx(WarningOutlineIcon, {})
2549
- }),
2550
- /* @__PURE__ */ jsxs(Text, {
2551
- muted: !0,
2552
- size: 1,
2553
- children: [ "Document unavailable — ", bareId ]
2554
- }) ]
2555
- })
2864
+ function missingNotice(bareId, layout) {
2865
+ /* @__PURE__ */
2866
+ return jsxs(Flex, {
2867
+ align: "center",
2868
+ gap: 2,
2869
+ children: [
2870
+ /* @__PURE__ */ jsx(Text, {
2871
+ muted: !0,
2872
+ size: 1,
2873
+ children: /* @__PURE__ */ jsx(WarningOutlineIcon, {})
2874
+ }),
2875
+ /* @__PURE__ */ jsx(Text, {
2876
+ muted: !0,
2877
+ size: 1,
2878
+ children: isCompact(layout) ? "Document unavailable" : `Document unavailable — ${bareId}`
2879
+ }) ]
2556
2880
  });
2557
2881
  }
2558
2882
 
@@ -2569,6 +2893,18 @@ function renderTypeIcon(icon) {
2569
2893
  return typeof icon == "function" ? createElement(icon) : isValidElement(icon) ? icon : /* @__PURE__ */ jsx(DocumentIcon, {});
2570
2894
  }
2571
2895
 
2896
+ function TitleSkeleton() {
2897
+ /* @__PURE__ */
2898
+ return jsx(TextSkeleton, {
2899
+ animated: !0,
2900
+ radius: 1,
2901
+ size: 1,
2902
+ style: {
2903
+ width: 96
2904
+ }
2905
+ });
2906
+ }
2907
+
2572
2908
  function InlineChipFace({bareId: bareId, schemaType: schemaType}) {
2573
2909
  const {preview: preview} = useStubValuePreview(bareId, schemaType);
2574
2910
  /* @__PURE__ */
@@ -2579,14 +2915,7 @@ function InlineChipFace({bareId: bareId, schemaType: schemaType}) {
2579
2915
  /* @__PURE__ */ jsx(Text, {
2580
2916
  size: 1,
2581
2917
  children: renderTypeIcon(schemaType.icon)
2582
- }), preview.isLoading ? /* @__PURE__ */ jsx(TextSkeleton, {
2583
- animated: !0,
2584
- radius: 1,
2585
- size: 1,
2586
- style: {
2587
- width: 96
2588
- }
2589
- }) : /* @__PURE__ */ jsx(Text, {
2918
+ }), preview.isLoading ? /* @__PURE__ */ jsx(TitleSkeleton, {}) : /* @__PURE__ */ jsx(Text, {
2590
2919
  size: 1,
2591
2920
  textOverflow: "ellipsis",
2592
2921
  weight: "medium",
@@ -2597,8 +2926,59 @@ function InlineChipFace({bareId: bareId, schemaType: schemaType}) {
2597
2926
  });
2598
2927
  }
2599
2928
 
2600
- function LinkedDocPreview({bareId: bareId, layout: layout, schemaType: schemaType}) {
2601
- const EditIntentLink = useMemo(() => function(linkProps) {
2929
+ const NOTICE_PADDING = 2;
2930
+
2931
+ function CompactDocRef({face: face, fill: fill, link: link, onClick: onClick}) {
2932
+ return link === void 0 ? /* @__PURE__ */ jsx(Box, {
2933
+ padding: chipPadding(fill),
2934
+ style: fill ? {
2935
+ width: "100%"
2936
+ } : {},
2937
+ children: face.content
2938
+ }) : /* @__PURE__ */ jsx(LinkChip, {
2939
+ as: link,
2940
+ fill: fill,
2941
+ hint: "Open document",
2942
+ onClick: onClick,
2943
+ children: face.content
2944
+ });
2945
+ }
2946
+
2947
+ const PENDING_FACE_MAX_WAIT_MS = 4e3;
2948
+
2949
+ function PendingDocFace({bareId: bareId, layout: layout}) {
2950
+ const expired = useDelayedFlag(!0, PENDING_FACE_MAX_WAIT_MS), compact = isCompact(layout);
2951
+ if (expired) {
2952
+ const id = /* @__PURE__ */ jsx(Text, {
2953
+ muted: !0,
2954
+ size: 1,
2955
+ children: bareId
2956
+ });
2957
+ return compact ? id : /* @__PURE__ */ jsx(Box, {
2958
+ padding: NOTICE_PADDING,
2959
+ children: id
2960
+ });
2961
+ }
2962
+ return compact ?
2963
+ /* @__PURE__ */ jsxs(Flex, {
2964
+ align: "center",
2965
+ gap: 2,
2966
+ children: [
2967
+ /* @__PURE__ */ jsx(Text, {
2968
+ muted: !0,
2969
+ size: 1,
2970
+ children: /* @__PURE__ */ jsx(DocumentIcon, {})
2971
+ }),
2972
+ /* @__PURE__ */ jsx(TitleSkeleton, {}) ]
2973
+ }) : /* @__PURE__ */ jsx(PreviewPlaceholder, {});
2974
+ }
2975
+
2976
+ function DocRefShell({face: face, layout: layout, source: source}) {
2977
+ const telemetry = useWorkflowTelemetry(), {bareId: bareId} = face, handleClick = e => {
2978
+ e.stopPropagation(), telemetry.log(WorkflowDocumentLinkClicked, {
2979
+ source: source
2980
+ });
2981
+ }, EditIntentLink = useMemo(() => bareId === void 0 ? void 0 : function(linkProps) {
2602
2982
  /* @__PURE__ */
2603
2983
  return jsx(IntentLink, {
2604
2984
  ...linkProps,
@@ -2608,58 +2988,52 @@ function LinkedDocPreview({bareId: bareId, layout: layout, schemaType: schemaTyp
2608
2988
  }
2609
2989
  });
2610
2990
  }, [ bareId ]);
2611
- return layout === "inline" ? /* @__PURE__ */ jsx(LinkChip, {
2612
- as: EditIntentLink,
2613
- hint: "Open document",
2614
- onClick: e => e.stopPropagation(),
2615
- children: /* @__PURE__ */ jsx(InlineChipFace, {
2616
- bareId: bareId,
2617
- schemaType: schemaType
2618
- })
2619
- }) : /* @__PURE__ */ jsx(Card, {
2991
+ if (isCompact(layout)) /* @__PURE__ */
2992
+ return jsx(CompactDocRef, {
2993
+ face: face,
2994
+ fill: layout === "row",
2995
+ link: EditIntentLink,
2996
+ onClick: handleClick
2997
+ });
2998
+ const padding = face.selfInset === !0 ? 0 : NOTICE_PADDING;
2999
+ /* @__PURE__ */
3000
+ return jsx(Card, {
2620
3001
  __unstable_focusRing: !0,
2621
- as: EditIntentLink,
2622
- border: !0,
2623
- "data-as": "a",
2624
- onClick: e => e.stopPropagation(),
2625
- padding: 1,
3002
+ border: layout === "default",
3003
+ padding: padding,
2626
3004
  radius: 3,
2627
- style: {
2628
- color: "inherit",
2629
- textDecoration: "none",
2630
- display: "block"
3005
+ tone: face.tone ?? (layout === "bare" ? "inherit" : "default"),
3006
+ ...EditIntentLink === void 0 ? {} : {
3007
+ as: EditIntentLink,
3008
+ "data-as": "a",
3009
+ onClick: handleClick,
3010
+ style: {
3011
+ color: "inherit",
3012
+ textDecoration: "none",
3013
+ display: "block"
3014
+ }
2631
3015
  },
2632
- children: /* @__PURE__ */ jsx(StubPreview, {
2633
- id: bareId,
2634
- schemaType: schemaType
2635
- })
3016
+ children: face.content
2636
3017
  });
2637
3018
  }
2638
3019
 
2639
- function DocPreviewLink({gdr: gdr, layout: layout = "default"}) {
2640
- const state = useDocLink(gdr);
3020
+ function faceContent(args) {
3021
+ const {gdr: gdr, layout: layout, state: state} = args, compact = isCompact(layout);
2641
3022
  switch (state.kind) {
2642
3023
  case "linked":
2643
- /* @__PURE__ */
2644
- return jsx(LinkedDocPreview, {
3024
+ return compact ? /* @__PURE__ */ jsx(InlineChipFace, {
2645
3025
  bareId: state.bareId,
2646
- layout: layout,
3026
+ schemaType: state.schemaType
3027
+ }) : /* @__PURE__ */ jsx(StubPreview, {
3028
+ id: state.bareId,
2647
3029
  schemaType: state.schemaType
2648
3030
  });
2649
3031
 
2650
3032
  case "foreign":
2651
- /* @__PURE__ */
2652
- return jsx(ForeignRefNotice, {
2653
- bareId: state.bareId,
2654
- resource: state.resource
2655
- });
3033
+ return foreignNotice(state.bareId, state.resource);
2656
3034
 
2657
3035
  case "missing":
2658
- /* @__PURE__ */
2659
- return jsx(MissingDocNotice, {
2660
- bareId: state.bareId,
2661
- layout: layout
2662
- });
3036
+ return missingNotice(state.bareId, layout);
2663
3037
 
2664
3038
  case "unopenable":
2665
3039
  /* @__PURE__ */
@@ -2670,10 +3044,9 @@ function DocPreviewLink({gdr: gdr, layout: layout = "default"}) {
2670
3044
 
2671
3045
  case "pending":
2672
3046
  /* @__PURE__ */
2673
- return jsx(Text, {
2674
- muted: !0,
2675
- size: 1,
2676
- children: state.bareId
3047
+ return jsx(PendingDocFace, {
3048
+ bareId: state.bareId,
3049
+ layout: layout
2677
3050
  });
2678
3051
 
2679
3052
  case "unresolvable":
@@ -2686,6 +3059,27 @@ function DocPreviewLink({gdr: gdr, layout: layout = "default"}) {
2686
3059
  }
2687
3060
  }
2688
3061
 
3062
+ function docFace(args) {
3063
+ return {
3064
+ ...docFaceChrome(args),
3065
+ content: faceContent(args)
3066
+ };
3067
+ }
3068
+
3069
+ function DocPreviewLink({gdr: gdr, layout: layout = "default", source: source}) {
3070
+ const state = useDocLink(gdr);
3071
+ /* @__PURE__ */
3072
+ return jsx(DocRefShell, {
3073
+ face: docFace({
3074
+ gdr: gdr,
3075
+ layout: layout,
3076
+ state: state
3077
+ }),
3078
+ layout: layout,
3079
+ source: source
3080
+ });
3081
+ }
3082
+
2689
3083
  function ReleaseLink({releaseName: releaseName, label: label}) {
2690
3084
  const {basePath: basePath} = useWorkspace(), href = `${basePath === "/" ? "" : basePath}/releases/${releaseName}`;
2691
3085
  /* @__PURE__ */
@@ -2744,7 +3138,8 @@ function renderTextValue(field) {
2744
3138
 
2745
3139
  function renderSingleDocRef(field) {
2746
3140
  return field.value ? /* @__PURE__ */ jsx(DocPreviewLink, {
2747
- gdr: field.value
3141
+ gdr: field.value,
3142
+ source: "field-value"
2748
3143
  }) : /* @__PURE__ */ jsx(Empty, {});
2749
3144
  }
2750
3145
 
@@ -2766,12 +3161,15 @@ const fieldValueRenderer = {
2766
3161
  children: formatBoolean(field.value)
2767
3162
  }),
2768
3163
  date: renderTextValue,
3164
+ dueDate: renderTextValue,
2769
3165
  datetime: renderTextValue,
3166
+ dueDatetime: renderTextValue,
2770
3167
  "doc.ref": renderSingleDocRef,
2771
3168
  "doc.refs": field => field.value.length > 0 ? /* @__PURE__ */ jsx(Stack, {
2772
3169
  gap: 2,
2773
3170
  children: field.value.map(ref => /* @__PURE__ */ jsx(DocPreviewLink, {
2774
- gdr: ref
3171
+ gdr: ref,
3172
+ source: "field-value"
2775
3173
  }, ref.id))
2776
3174
  }) : /* @__PURE__ */ jsx(Empty, {}),
2777
3175
  number: renderTextValue,
@@ -2993,6 +3391,27 @@ function ReadOnlyField({entry: entry}) {
2993
3391
  });
2994
3392
  }
2995
3393
 
3394
+ function useEditField(surface) {
3395
+ const {editFieldFor: editFieldFor} = useWorkflowContext(), telemetry = useWorkflowTelemetry();
3396
+ return useCallback(async ({instanceId: instanceId, field: field, ...change}) => {
3397
+ const log = success => telemetry.log(WorkflowFieldControlUsed, {
3398
+ instanceId: instanceId,
3399
+ surface: surface,
3400
+ fieldKind: field.type,
3401
+ success: success
3402
+ });
3403
+ try {
3404
+ await editFieldFor(instanceId, {
3405
+ ...change,
3406
+ target: editFieldTarget(field)
3407
+ });
3408
+ } catch (err) {
3409
+ throw log(!1), err;
3410
+ }
3411
+ log(!0);
3412
+ }, [ editFieldFor, surface, telemetry ]);
3413
+ }
3414
+
2996
3415
  function sameValue(a, b) {
2997
3416
  return (a ?? void 0) === (b ?? void 0);
2998
3417
  }
@@ -3029,11 +3448,12 @@ function useFieldDraft(args) {
3029
3448
  };
3030
3449
  }
3031
3450
 
3032
- function EditableFieldControl({instanceId: instanceId, field: field}) {
3033
- const {editFieldFor: editFieldFor, previewFieldFor: previewFieldFor, discardFieldPreviewFor: discardFieldPreviewFor} = useWorkflowContext(), selfActor = useSelfActor(), held = useHeldWorkflowEntry(instanceId), entry = held ? resolveFieldEntry(held.instance, field) : void 0, evaluation = held?.evaluation, target = editFieldTarget(field), consequence = fieldConsequence(evaluation, field.name), advanceTo = advanceTargetTitle(evaluation, field.name);
3451
+ function EditableFieldControl({instanceId: instanceId, field: field, surface: surface}) {
3452
+ const {previewFieldFor: previewFieldFor, discardFieldPreviewFor: discardFieldPreviewFor} = useWorkflowContext(), editField = useEditField(surface), selfActor = useSelfActor(), held = useHeldWorkflowEntry(instanceId), entry = held ? resolveFieldEntry(held.instance, field) : void 0, evaluation = held?.evaluation, target = editFieldTarget(field), consequence = fieldConsequence(evaluation, field.name), advanceTo = advanceTargetTitle(evaluation, field.name);
3034
3453
  /* @__PURE__ */
3035
3454
  return jsx(EditableField, {
3036
3455
  field: field,
3456
+ instanceId: instanceId,
3037
3457
  ...entry === void 0 ? {} : {
3038
3458
  entry: entry
3039
3459
  },
@@ -3046,30 +3466,33 @@ function EditableFieldControl({instanceId: instanceId, field: field}) {
3046
3466
  ...advanceTo === void 0 ? {} : {
3047
3467
  advanceTo: advanceTo
3048
3468
  },
3049
- onSave: value => editFieldFor(instanceId, {
3050
- target: target,
3469
+ onSave: value => editField({
3470
+ instanceId: instanceId,
3471
+ field: field,
3051
3472
  mode: "set",
3052
3473
  value: value
3053
- }).then(() => {}),
3474
+ }),
3054
3475
  onPreview: value => previewFieldFor(instanceId, {
3055
3476
  target: target,
3056
3477
  mode: "set",
3057
3478
  value: value
3058
3479
  }),
3059
3480
  onDiscardPreview: () => discardFieldPreviewFor(instanceId, target),
3060
- onUnset: () => editFieldFor(instanceId, {
3061
- target: target,
3481
+ onUnset: () => editField({
3482
+ instanceId: instanceId,
3483
+ field: field,
3062
3484
  mode: "unset"
3063
- }).then(() => {}),
3064
- onAppend: body => editFieldFor(instanceId, {
3065
- target: target,
3485
+ }),
3486
+ onAppend: body => editField({
3487
+ instanceId: instanceId,
3488
+ field: field,
3066
3489
  mode: "append",
3067
3490
  value: noteRow({
3068
3491
  body: body,
3069
3492
  actor: selfActor,
3070
3493
  at: /* @__PURE__ */ (new Date).toISOString()
3071
3494
  })
3072
- }).then(() => {})
3495
+ })
3073
3496
  });
3074
3497
  }
3075
3498
 
@@ -3097,28 +3520,34 @@ function EditableValueButton({children: children, hint: hint, hintDisabled: hint
3097
3520
  });
3098
3521
  }
3099
3522
 
3100
- function useSaveFailureToast() {
3101
- const toast = useClosableToast();
3102
- return err => {
3103
- console.error("[workflow-studio-plugin] field save failed:", err), toast.push({
3104
- status: "error",
3105
- title: "Saving failed",
3106
- description: describeError(err)
3107
- });
3108
- };
3523
+ function useFieldSaveReport(instanceId) {
3524
+ const report = useRejectionReport();
3525
+ return useMemo(() => {
3526
+ const id = instanceToastId("field-save", instanceId);
3527
+ return {
3528
+ rejected: err => report.rejected({
3529
+ err: err,
3530
+ context: "field save",
3531
+ id: id,
3532
+ refused: "This field can’t be edited right now",
3533
+ failed: "Failed to save the field"
3534
+ }),
3535
+ dismiss: () => report.dismiss(id)
3536
+ };
3537
+ }, [ report, instanceId ]);
3109
3538
  }
3110
3539
 
3111
- function useSaveField(args = {}) {
3112
- const reportFailure = useSaveFailureToast(), [saving, setSaving] = useState(!1);
3540
+ function useSaveField(args) {
3541
+ const report = useFieldSaveReport(args.instanceId), [saving, setSaving] = useState(!1);
3113
3542
  return {
3114
3543
  saving: saving,
3115
3544
  save: async commit => {
3116
3545
  if (!saving) {
3117
3546
  setSaving(!0);
3118
3547
  try {
3119
- await commit(), args.onSaved?.();
3548
+ await commit(), report.dismiss(), args.onSaved?.();
3120
3549
  } catch (err) {
3121
- reportFailure(err);
3550
+ report.rejected(err);
3122
3551
  } finally {
3123
3552
  setSaving(!1);
3124
3553
  }
@@ -3128,7 +3557,9 @@ function useSaveField(args = {}) {
3128
3557
  }
3129
3558
 
3130
3559
  function useActorPick(args) {
3131
- const {current: current, onSave: onSave, onUnset: onUnset} = args, {saving: saving, save: save} = useSaveField();
3560
+ const {current: current, onSave: onSave, onUnset: onUnset} = args, {saving: saving, save: save} = useSaveField({
3561
+ instanceId: args.instanceId
3562
+ });
3132
3563
  return {
3133
3564
  saving: saving,
3134
3565
  pick: member => {
@@ -3137,9 +3568,10 @@ function useActorPick(args) {
3137
3568
  };
3138
3569
  }
3139
3570
 
3140
- function ActorField({field: field, onSave: onSave, onUnset: onUnset}) {
3571
+ function ActorField({field: field, instanceId: instanceId, onSave: onSave, onUnset: onUnset}) {
3141
3572
  const [open, setOpen] = useState(!1), cur = isActorShape(field.value) ? field.value : null, {pick: pick} = useActorPick({
3142
3573
  current: cur,
3574
+ instanceId: instanceId,
3143
3575
  onSave: onSave,
3144
3576
  onUnset: onUnset
3145
3577
  }), pickAndClose = member => {
@@ -3174,7 +3606,9 @@ function ActorField({field: field, onSave: onSave, onUnset: onUnset}) {
3174
3606
  }
3175
3607
 
3176
3608
  function useAssigneePick(args) {
3177
- const {current: current, onSave: onSave, onUnset: onUnset} = args, {saving: saving, save: save} = useSaveField();
3609
+ const {current: current, onSave: onSave, onUnset: onUnset} = args, {saving: saving, save: save} = useSaveField({
3610
+ instanceId: args.instanceId
3611
+ });
3178
3612
  return {
3179
3613
  saving: saving,
3180
3614
  pick: assignee => {
@@ -3189,9 +3623,10 @@ function useAssigneePick(args) {
3189
3623
  };
3190
3624
  }
3191
3625
 
3192
- function AssigneeField({field: field, onSave: onSave, onUnset: onUnset}) {
3626
+ function AssigneeField({field: field, instanceId: instanceId, onSave: onSave, onUnset: onUnset}) {
3193
3627
  const [open, setOpen] = useState(!1), cur = isAssigneeShape(field.value) ? field.value : null, {pick: pick} = useAssigneePick({
3194
3628
  current: cur,
3629
+ instanceId: instanceId,
3195
3630
  onSave: onSave,
3196
3631
  onUnset: onUnset
3197
3632
  }), pickAndClose = assignee => {
@@ -3231,8 +3666,10 @@ function arrayRowsOf(value) {
3231
3666
  return Array.isArray(value) ? value.filter(row => typeof row == "object" && row !== null) : [];
3232
3667
  }
3233
3668
 
3234
- function NotesField({field: field, onAppend: onAppend}) {
3235
- const [body, setBody] = useState(""), {saving: saving, save: save} = useSaveField(), rows = arrayRowsOf(field.value), trimmed = body.trim(), add = () => {
3669
+ function NotesField({field: field, instanceId: instanceId, onAppend: onAppend}) {
3670
+ const [body, setBody] = useState(""), {saving: saving, save: save} = useSaveField({
3671
+ instanceId: instanceId
3672
+ }), rows = arrayRowsOf(field.value), trimmed = body.trim(), add = () => {
3236
3673
  saving || trimmed === "" || (setBody(""), save(async () => {
3237
3674
  try {
3238
3675
  await onAppend(trimmed);
@@ -3273,11 +3710,13 @@ function NotesField({field: field, onAppend: onAppend}) {
3273
3710
  });
3274
3711
  }
3275
3712
 
3276
- function DateField({field: field, onSave: onSave, onUnset: onUnset}) {
3277
- const {save: save} = useSaveField();
3713
+ function DateField({field: field, kind: kind, instanceId: instanceId, onSave: onSave, onUnset: onUnset}) {
3714
+ const {save: save} = useSaveField({
3715
+ instanceId: instanceId
3716
+ });
3278
3717
  /* @__PURE__ */
3279
3718
  return jsx(DateFieldInput, {
3280
- kind: field.type === "datetime" ? "datetime" : "date",
3719
+ kind: kind,
3281
3720
  onChange: next => {
3282
3721
  save(() => next === null && onUnset !== void 0 ? onUnset() : onSave(next));
3283
3722
  },
@@ -3285,8 +3724,10 @@ function DateField({field: field, onSave: onSave, onUnset: onUnset}) {
3285
3724
  });
3286
3725
  }
3287
3726
 
3288
- function DocRefField({field: field, entry: entry, onSave: onSave, onUnset: onUnset}) {
3289
- const {save: save} = useSaveField(), value = isGdr(field.value) ? field.value : null, types = entry !== void 0 && isSingleDocRefEntry(entry) ? entry.types : void 0;
3727
+ function DocRefField({field: field, entry: entry, instanceId: instanceId, onSave: onSave, onUnset: onUnset}) {
3728
+ const {save: save} = useSaveField({
3729
+ instanceId: instanceId
3730
+ }), value = isGdr(field.value) ? field.value : null, types = entry !== void 0 && isSingleDocRefEntry(entry) ? entry.types : void 0;
3290
3731
  return field.editable ? /* @__PURE__ */ jsx(DocPicker, {
3291
3732
  onChange: ref => {
3292
3733
  save(() => ref === null && onUnset !== void 0 ? onUnset() : onSave(ref));
@@ -3301,8 +3742,10 @@ function DocRefField({field: field, entry: entry, onSave: onSave, onUnset: onUns
3301
3742
  });
3302
3743
  }
3303
3744
 
3304
- function BooleanSwitchField({field: field, onSave: onSave}) {
3305
- const {save: save} = useSaveField(), committed = field.value === !0;
3745
+ function BooleanSwitchField({field: field, instanceId: instanceId, onSave: onSave}) {
3746
+ const {save: save} = useSaveField({
3747
+ instanceId: instanceId
3748
+ }), committed = field.value === !0;
3306
3749
  /* @__PURE__ */
3307
3750
  return jsx(Flex, {
3308
3751
  align: "center",
@@ -3315,8 +3758,10 @@ function BooleanSwitchField({field: field, onSave: onSave}) {
3315
3758
  });
3316
3759
  }
3317
3760
 
3318
- function ChoiceField({field: field, options: options, onSave: onSave, onUnset: onUnset}) {
3319
- const {save: save} = useSaveField();
3761
+ function ChoiceField({field: field, instanceId: instanceId, options: options, onSave: onSave, onUnset: onUnset}) {
3762
+ const {save: save} = useSaveField({
3763
+ instanceId: instanceId
3764
+ });
3320
3765
  /* @__PURE__ */
3321
3766
  return jsx(ChoiceSelect, {
3322
3767
  onChange: value => {
@@ -3390,8 +3835,8 @@ function FieldEditRow({field: field, onSave: onSave, onCancel: onCancel, saving:
3390
3835
  });
3391
3836
  }
3392
3837
 
3393
- function GenericField({field: field, onSave: onSave, onPreview: onPreview, onDiscardPreview: onDiscardPreview}) {
3394
- const reportFailure = useSaveFailureToast(), canSave = value => scalarValidationIssue({
3838
+ function GenericField({field: field, onSave: onSave, instanceId: instanceId, onPreview: onPreview, onDiscardPreview: onDiscardPreview}) {
3839
+ const report = useFieldSaveReport(instanceId), canSave = value => scalarValidationIssue({
3395
3840
  kind: field.type,
3396
3841
  label: field.title ?? field.name,
3397
3842
  validation: field.validation,
@@ -3402,7 +3847,7 @@ function GenericField({field: field, onSave: onSave, onPreview: onPreview, onDis
3402
3847
  onPreview: onPreview,
3403
3848
  onDiscardPreview: onDiscardPreview,
3404
3849
  canSave: canSave,
3405
- onSaveFailure: reportFailure
3850
+ onSaveFailure: report.rejected
3406
3851
  }), validationIssue = scalarValidationIssue({
3407
3852
  kind: field.type,
3408
3853
  label: field.title ?? field.name,
@@ -3448,9 +3893,10 @@ function AdvanceHint({stageTitle: stageTitle2}) {
3448
3893
  });
3449
3894
  }
3450
3895
 
3451
- function arrayArm({field: field, entry: entry, onAppend: onAppend}) {
3896
+ function arrayArm({field: field, entry: entry, instanceId: instanceId, onAppend: onAppend}) {
3452
3897
  return field.editable && onAppend !== void 0 && entry !== void 0 && isNotesEntry(entry) ? /* @__PURE__ */ jsx(NotesField, {
3453
3898
  field: field,
3899
+ instanceId: instanceId,
3454
3900
  onAppend: onAppend
3455
3901
  }) : /* @__PURE__ */ jsx(ArrayValueRows, {
3456
3902
  rows: arrayRowsOf(field.value)
@@ -3458,7 +3904,7 @@ function arrayArm({field: field, entry: entry, onAppend: onAppend}) {
3458
3904
  }
3459
3905
 
3460
3906
  function fieldArm({arm: arm, entry: entry, onUnset: onUnset, onAppend: onAppend, onPreview: onPreview, onDiscardPreview: onDiscardPreview}) {
3461
- const {field: field} = arm;
3907
+ const {field: field, instanceId: instanceId} = arm;
3462
3908
  return field.type === "actor" ? /* @__PURE__ */ jsx(ActorField, {
3463
3909
  ...arm,
3464
3910
  onUnset: onUnset
@@ -3471,11 +3917,13 @@ function fieldArm({arm: arm, entry: entry, onUnset: onUnset, onAppend: onAppend,
3471
3917
  }) : field.type === "array" ? arrayArm({
3472
3918
  field: field,
3473
3919
  entry: entry,
3920
+ instanceId: instanceId,
3474
3921
  onAppend: onAppend
3475
3922
  }) : field.type === "boolean" && field.editable ? /* @__PURE__ */ jsx(BooleanSwitchField, {
3476
3923
  ...arm
3477
- }) : (field.type === "date" || field.type === "datetime") && field.editable ? /* @__PURE__ */ jsx(DateField, {
3924
+ }) : isDateFieldKind(field.type) && field.editable ? /* @__PURE__ */ jsx(DateField, {
3478
3925
  ...arm,
3926
+ kind: field.type,
3479
3927
  onUnset: onUnset
3480
3928
  }) : isSingleDocRefKind(field.type) ? /* @__PURE__ */ jsx(DocRefField, {
3481
3929
  ...arm,
@@ -3488,9 +3936,10 @@ function fieldArm({arm: arm, entry: entry, onUnset: onUnset, onAppend: onAppend,
3488
3936
  });
3489
3937
  }
3490
3938
 
3491
- function EditableField({field: field, entry: entry, description: description, onSave: onSave, onUnset: onUnset, onAppend: onAppend, onPreview: onPreview, onDiscardPreview: onDiscardPreview, consequence: consequence, advanceTo: advanceTo}) {
3939
+ function EditableField({field: field, entry: entry, description: description, instanceId: instanceId, onSave: onSave, onUnset: onUnset, onAppend: onAppend, onPreview: onPreview, onDiscardPreview: onDiscardPreview, consequence: consequence, advanceTo: advanceTo}) {
3492
3940
  const label = field.title ?? field.name, arm = {
3493
3941
  field: field,
3942
+ instanceId: instanceId,
3494
3943
  onSave: onSave
3495
3944
  }, content = entry?.options === void 0 ? fieldArm({
3496
3945
  arm: arm,
@@ -3522,11 +3971,15 @@ function EditableField({field: field, entry: entry, description: description, on
3522
3971
  });
3523
3972
  }
3524
3973
 
3525
- function MetaRow({label: label, children: children}) {
3974
+ function MetaRow({children: children, fillHeight: fillHeight, label: label}) {
3526
3975
  /* @__PURE__ */
3527
3976
  return jsxs(Flex, {
3528
- align: "flex-start",
3529
3977
  gap: 4,
3978
+ ...fillHeight ? {
3979
+ flex: 1
3980
+ } : {
3981
+ align: "flex-start"
3982
+ },
3530
3983
  children: [
3531
3984
  /* @__PURE__ */ jsx(Box, {
3532
3985
  style: {
@@ -3599,27 +4052,6 @@ function ActivityStatusIcon({status: status}) {
3599
4052
  });
3600
4053
  }
3601
4054
 
3602
- function LoadingRow({label: label, padding: padding}) {
3603
- /* @__PURE__ */
3604
- return jsxs(Flex, {
3605
- align: "center",
3606
- gap: 2,
3607
- ...padding === void 0 ? {} : {
3608
- padding: padding
3609
- },
3610
- children: [
3611
- /* @__PURE__ */ jsx(Spinner, {
3612
- muted: !0,
3613
- size: 1
3614
- }),
3615
- /* @__PURE__ */ jsx(Text, {
3616
- muted: !0,
3617
- size: 1,
3618
- children: label
3619
- }) ]
3620
- });
3621
- }
3622
-
3623
4055
  function useSpaceToken(index) {
3624
4056
  const value = useTheme_v2().space[index];
3625
4057
  if (value === void 0) throw new Error(`theme is missing space token ${index}`);
@@ -3666,6 +4098,46 @@ function useBadgeCapTrim() {
3666
4098
  };
3667
4099
  }
3668
4100
 
4101
+ function SpinnerSlot({busy: busy}) {
4102
+ const size = useTextIconSize(), trim = useCapTrimFor(size);
4103
+ /* @__PURE__ */
4104
+ return jsx(Flex, {
4105
+ align: "center",
4106
+ flex: "none",
4107
+ justify: "center",
4108
+ style: {
4109
+ height: size,
4110
+ marginBottom: trim,
4111
+ marginTop: trim,
4112
+ width: size
4113
+ },
4114
+ children: busy ? /* @__PURE__ */ jsx(Spinner, {
4115
+ muted: !0,
4116
+ size: 1
4117
+ }) : null
4118
+ });
4119
+ }
4120
+
4121
+ function LoadingRow({label: label, padding: padding}) {
4122
+ /* @__PURE__ */
4123
+ return jsxs(Flex, {
4124
+ align: "center",
4125
+ gap: 2,
4126
+ ...padding === void 0 ? {} : {
4127
+ padding: padding
4128
+ },
4129
+ children: [
4130
+ /* @__PURE__ */ jsx(SpinnerSlot, {
4131
+ busy: !0
4132
+ }),
4133
+ /* @__PURE__ */ jsx(Text, {
4134
+ muted: !0,
4135
+ size: 1,
4136
+ children: label
4137
+ }) ]
4138
+ });
4139
+ }
4140
+
3669
4141
  function AvatarDisplay({avatar: avatar, "aria-label": ariaLabel}) {
3670
4142
  const trim = useAvatarCapTrim();
3671
4143
  /* @__PURE__ */
@@ -3746,8 +4218,8 @@ function UserAvatarGroup({ids: ids, hint: hint}) {
3746
4218
  });
3747
4219
  }
3748
4220
 
3749
- function AssignActivityControl({instanceId: instanceId, state: state, assigneeIds: assigneeIds = []}) {
3750
- const {editFieldFor: editFieldFor} = useWorkflowContext(), toast = useClosableToast(), [open, setOpen] = useState(!1), [busy, setBusy] = useState(!1);
4221
+ function AssignActivityControl({instanceId: instanceId, state: state, surface: surface, assigneeIds: assigneeIds = []}) {
4222
+ const editField = useEditField(surface), report = useRejectionReport(), [open, setOpen] = useState(!1), [busy, setBusy] = useState(!1);
3751
4223
  if (state.kind === "none") return assigneeIds.length === 0 ? null : /* @__PURE__ */ jsx(AvatarDisplay, {
3752
4224
  "aria-label": "Assignees",
3753
4225
  avatar: /* @__PURE__ */ jsx(UserAvatarGroup, {
@@ -3768,23 +4240,27 @@ function AssignActivityControl({instanceId: instanceId, state: state, assigneeId
3768
4240
  if (!(busy || field === void 0)) {
3769
4241
  setBusy(!0);
3770
4242
  try {
3771
- selectedIds.has(memberId) ? await editFieldFor(instanceId, {
3772
- target: editFieldTarget(field),
4243
+ selectedIds.has(memberId) ? await editField({
4244
+ instanceId: instanceId,
4245
+ field: field,
3773
4246
  mode: "set",
3774
4247
  value: members.filter(m => !(m.type === "user" && m.id === memberId))
3775
- }) : await editFieldFor(instanceId, {
3776
- target: editFieldTarget(field),
4248
+ }) : await editField({
4249
+ instanceId: instanceId,
4250
+ field: field,
3777
4251
  mode: "append",
3778
4252
  value: {
3779
4253
  type: "user",
3780
4254
  id: memberId
3781
4255
  }
3782
- });
4256
+ }), report.dismiss(instanceToastId("assign", instanceId));
3783
4257
  } catch (err) {
3784
- toast.push({
3785
- status: "error",
3786
- title: "Assigning failed",
3787
- description: describeError(err)
4258
+ report.rejected({
4259
+ err: err,
4260
+ context: "assignment",
4261
+ id: instanceToastId("assign", instanceId),
4262
+ refused: "Assignees can’t be changed right now",
4263
+ failed: "Failed to update assignees"
3788
4264
  });
3789
4265
  } finally {
3790
4266
  setBusy(!1);
@@ -3851,6 +4327,11 @@ function appendTargetFor(targets, filter) {
3851
4327
  if (filter?.kind !== "mine") return filter?.kind === "activity" ? targets.find(t => t.activity === filter.activity) : filter?.kind === "field" ? targets.find(t => t.scope === filter.scope && t.field === filter.field && (filter.scope !== "activity" || t.activity === filter.activity)) : targets[0];
3852
4328
  }
3853
4329
 
4330
+ function canRemoveRow(args) {
4331
+ const {editTarget: editTarget, appendTarget: appendTarget} = args;
4332
+ return editTarget === void 0 || appendTarget === void 0 ? !1 : editTarget.scope === appendTarget.scope && editTarget.field === appendTarget.field && editTarget.activity === appendTarget.activity;
4333
+ }
4334
+
3854
4335
  function looksLikeDefinition(value) {
3855
4336
  return typeof value == "object" && value !== null && Array.isArray(value.stages);
3856
4337
  }
@@ -4092,6 +4573,21 @@ function AddItemControl({onAdd: onAdd, button: button, busy: busy}) {
4092
4573
  });
4093
4574
  }
4094
4575
 
4576
+ function RemoveItemButton({onClick: onClick}) {
4577
+ /* @__PURE__ */
4578
+ return jsx(HoverHint, {
4579
+ text: "Remove item",
4580
+ children: /* @__PURE__ */ jsx(Button, {
4581
+ "aria-label": "Remove item",
4582
+ fontSize: 1,
4583
+ icon: CloseIcon,
4584
+ mode: "bleed",
4585
+ onClick: onClick,
4586
+ padding: 2
4587
+ })
4588
+ });
4589
+ }
4590
+
4095
4591
  function BreadcrumbTail({segments: segments, style: style}) {
4096
4592
  /* @__PURE__ */
4097
4593
  return jsx(Text, {
@@ -4158,7 +4654,7 @@ function WorkRow({lead: lead, children: children, end: end, onOpen: onOpen}) {
4158
4654
  });
4159
4655
  }
4160
4656
 
4161
- function TodoItemRowView({breadcrumb: breadcrumb, row: row, onToggle: onToggle, onPatch: onPatch}) {
4657
+ function TodoItemRowView({breadcrumb: breadcrumb, row: row, onToggle: onToggle, onPatch: onPatch, onRemove: onRemove}) {
4162
4658
  const {canClick: canClick, hint: hint} = rowInteractivity(row), editable = row.editTarget !== void 0, checkbox = /* @__PURE__ */ jsx(TodoCheckbox, {
4163
4659
  canClick: canClick,
4164
4660
  checked: isTodoDone(row.item),
@@ -4177,6 +4673,8 @@ function TodoItemRowView({breadcrumb: breadcrumb, row: row, onToggle: onToggle,
4177
4673
  editable: editable,
4178
4674
  item: row.item,
4179
4675
  onPatch: onPatch
4676
+ }), onRemove === void 0 ? null : /* @__PURE__ */ jsx(RemoveItemButton, {
4677
+ onClick: onRemove
4180
4678
  }) ]
4181
4679
  }),
4182
4680
  lead:
@@ -4203,37 +4701,41 @@ function TodoItemRowView({breadcrumb: breadcrumb, row: row, onToggle: onToggle,
4203
4701
  }
4204
4702
 
4205
4703
  function TodoItemsList({breadcrumb: breadcrumb, entry: entry, filter: filter, surface: surface}) {
4206
- const {instance: instance} = entry, definition = useDefinition(entry), {editFieldFor: editFieldFor, fireActionFor: fireActionFor} = useWorkflowContext(), telemetry = useWorkflowTelemetry(), toast = useClosableToast(), [busy, setBusy] = useState(!1), derivation = deriveTodoItems(entry), rows = derivation.rows.filter(row => rowMatches(row, filter)), appendTarget = appendTargetFor(derivation.appendTargets, filter), run = async op => {
4704
+ const {instance: instance} = entry, definition = useDefinition(entry), {editFieldFor: editFieldFor, fireActionFor: fireActionFor} = useWorkflowContext(), telemetry = useWorkflowTelemetry(), report = useRejectionReport(), [busy, setBusy] = useState(!1), derivation = deriveTodoItems(entry), rows = derivation.rows.filter(row => rowMatches(row, filter)), appendTarget = appendTargetFor(derivation.appendTargets, filter), run = async op => {
4207
4705
  if (busy) return !1;
4208
4706
  setBusy(!0);
4209
4707
  try {
4210
- return await op(), !0;
4708
+ return await op(), report.dismiss(instanceToastId("todo-write", instance._id)),
4709
+ !0;
4211
4710
  } catch (err) {
4212
- return toast.push({
4213
- status: "error",
4214
- title: "Updating to-dos failed",
4215
- description: describeError(err)
4711
+ return report.rejected({
4712
+ err: err,
4713
+ context: "to-do write",
4714
+ id: instanceToastId("todo-write", instance._id),
4715
+ refused: "To-dos can’t be changed right now",
4716
+ failed: "Failed to update to-dos"
4216
4717
  }), !1;
4217
4718
  } finally {
4218
4719
  setBusy(!1);
4219
4720
  }
4220
- }, patchItem = (row, patch) => {
4221
- if (!row.editTarget) return Promise.resolve();
4222
- const target = row.editTarget, next = row.items.map(it => it._key === row.item._key ? {
4223
- ...it,
4224
- ...patch
4225
- } : it);
4721
+ }, writeItems = (row, nextItems) => {
4722
+ if (!row.editTarget) return Promise.resolve(!1);
4723
+ const target = row.editTarget;
4226
4724
  return run(() => editFieldFor(instance._id, {
4227
4725
  target: target,
4228
4726
  mode: "set",
4229
- value: next
4727
+ value: nextItems()
4230
4728
  }));
4231
- }, toggleRow = async row => {
4729
+ }, patchItem = (row, patch) => writeItems(row, () => todoItemsPatched({
4730
+ items: row.items,
4731
+ key: row.item._key,
4732
+ patch: patch
4733
+ })), toggleRow = async row => {
4232
4734
  if (busy) return;
4233
4735
  if (row.editTarget) {
4234
4736
  const done = !isTodoDone(row.item), success = await patchItem(row, {
4235
4737
  status: toggledTodoStatus(row.item)
4236
- }) === !0;
4738
+ });
4237
4739
  telemetry.log(WorkflowTodoToggled, {
4238
4740
  instanceId: instance._id,
4239
4741
  surface: surface,
@@ -4257,14 +4759,44 @@ function TodoItemsList({breadcrumb: breadcrumb, entry: entry, filter: filter, su
4257
4759
  success: success
4258
4760
  });
4259
4761
  }
4260
- }, addItem = label => appendTarget ? run(() => editFieldFor(instance._id, {
4261
- target: appendTarget,
4262
- mode: "append",
4263
- value: {
4264
- label: label,
4265
- status: "open"
4266
- }
4267
- })) : Promise.resolve(!1);
4762
+ }, addItem = async label => {
4763
+ if (busy || !appendTarget) return !1;
4764
+ const success = await run(() => editFieldFor(instance._id, {
4765
+ target: appendTarget,
4766
+ mode: "append",
4767
+ value: {
4768
+ label: label,
4769
+ status: "open"
4770
+ }
4771
+ }));
4772
+ return telemetry.log(WorkflowTodoEdited, {
4773
+ instanceId: instance._id,
4774
+ surface: surface,
4775
+ kind: "add",
4776
+ success: success
4777
+ }), success;
4778
+ }, editRow = async (row, patch) => {
4779
+ if (busy || row.editTarget === void 0) return;
4780
+ const success = await patchItem(row, patch);
4781
+ telemetry.log(WorkflowTodoEdited, {
4782
+ instanceId: instance._id,
4783
+ surface: surface,
4784
+ kind: todoEditKind(patch),
4785
+ success: success
4786
+ });
4787
+ }, removeRow = async row => {
4788
+ if (busy || row.editTarget === void 0) return;
4789
+ const success = await writeItems(row, () => todoItemsWithout({
4790
+ items: row.items,
4791
+ key: row.item._key
4792
+ }));
4793
+ telemetry.log(WorkflowTodoEdited, {
4794
+ instanceId: instance._id,
4795
+ surface: surface,
4796
+ kind: "remove",
4797
+ success: success
4798
+ });
4799
+ };
4268
4800
  if (rows.length === 0 && !appendTarget) return null;
4269
4801
  const rowKey = row => `${row.scope}:${row.activityName ?? ""}:${row.fieldName}:${row.item._key}`, rowBreadcrumb = row => {
4270
4802
  if (breadcrumb === void 0 || row.activityName === void 0) return breadcrumb;
@@ -4284,8 +4816,16 @@ function TodoItemsList({breadcrumb: breadcrumb, entry: entry, filter: filter, su
4284
4816
  children: rows.map(row => /* @__PURE__ */ jsx(TodoItemRowView, {
4285
4817
  breadcrumb: rowBreadcrumb(row),
4286
4818
  onPatch: patch => {
4287
- patchItem(row, patch);
4819
+ editRow(row, patch);
4288
4820
  },
4821
+ ...canRemoveRow({
4822
+ editTarget: row.editTarget,
4823
+ appendTarget: appendTarget
4824
+ }) ? {
4825
+ onRemove: () => {
4826
+ removeRow(row);
4827
+ }
4828
+ } : {},
4289
4829
  onToggle: () => {
4290
4830
  toggleRow(row);
4291
4831
  },
@@ -4373,7 +4913,7 @@ const ACTIONS_MENU_TRIGGER = {
4373
4913
  padding: 2
4374
4914
  };
4375
4915
 
4376
- function ActionMenuItem({actionEval: actionEval, instanceId: instanceId, activity: activity, surface: surface, onCollectParams: onCollectParams}) {
4916
+ function ActionMenuItem({actionEval: actionEval, instanceId: instanceId, activity: activity, surface: surface, placement: placement, onCollectParams: onCollectParams}) {
4377
4917
  const rowKind = actionRowKind(actionEval), label = actionTriggerLabel(actionEval), {tone: tone, icon: icon} = actionButtonFace({
4378
4918
  actionEval: actionEval,
4379
4919
  activityName: activity
@@ -4385,6 +4925,7 @@ function ActionMenuItem({actionEval: actionEval, instanceId: instanceId, activit
4385
4925
  action: actionEval.action.name,
4386
4926
  label: label,
4387
4927
  surface: surface,
4928
+ placement: placement,
4388
4929
  viaMenu: !0
4389
4930
  });
4390
4931
  return rowKind.kind === "disabled" ? /* @__PURE__ */ jsx(MenuItem, {
@@ -4421,7 +4962,7 @@ function rowShield(menuButton) {
4421
4962
  });
4422
4963
  }
4423
4964
 
4424
- function ActionsMenuButton({actions: actions, instanceId: instanceId, activity: activity, label: label, surface: surface, mode: mode = "ghost", inButtonCard: inButtonCard = !1}) {
4965
+ function ActionsMenuButton({actions: actions, instanceId: instanceId, activity: activity, label: label, surface: surface, placement: placement, mode: mode = "ghost", inButtonCard: inButtonCard = !1}) {
4425
4966
  const [paramsAction, setParamsAction] = useState(void 0), clusterPending = useActionClusterPending(), menuId = useId(), menuButton = /* @__PURE__ */ jsx(MenuButton, {
4426
4967
  button: /* @__PURE__ */ jsx(Button, {
4427
4968
  ...ACTIONS_MENU_TRIGGER,
@@ -4441,6 +4982,7 @@ function ActionsMenuButton({actions: actions, instanceId: instanceId, activity:
4441
4982
  activity: activity,
4442
4983
  instanceId: instanceId,
4443
4984
  onCollectParams: () => setParamsAction(a),
4985
+ placement: placement,
4444
4986
  surface: surface
4445
4987
  }, a.action.name))
4446
4988
  }),
@@ -4458,6 +5000,7 @@ function ActionsMenuButton({actions: actions, instanceId: instanceId, activity:
4458
5000
  activity: activity,
4459
5001
  instanceId: instanceId,
4460
5002
  onClose: () => setParamsAction(void 0),
5003
+ placement: placement,
4461
5004
  surface: surface,
4462
5005
  viaMenu: !0
4463
5006
  })
@@ -4476,7 +5019,8 @@ function TerminalFooter({actions: actions, activity: activity, instanceId: insta
4476
5019
  actionEval: only,
4477
5020
  activity: activity,
4478
5021
  instanceId: instanceId,
4479
- surface: "terminal-footer"
5022
+ placement: "terminal-footer",
5023
+ surface: "activity-dialog"
4480
5024
  })
4481
5025
  }) : /* @__PURE__ */ jsx(FittedActions, {
4482
5026
  actions: actions,
@@ -4531,14 +5075,16 @@ function FittedActions({actions: actions, activity: activity, instanceId: instan
4531
5075
  actionEval: a,
4532
5076
  activity: activity,
4533
5077
  instanceId: instanceId,
4534
- surface: "terminal-footer"
5078
+ placement: "terminal-footer",
5079
+ surface: "activity-dialog"
4535
5080
  }, a.action.name)), overflow.length > 0 ? /* @__PURE__ */ jsx(ActionsMenuButton, {
4536
5081
  actions: overflow,
4537
5082
  activity: activity,
4538
5083
  instanceId: instanceId,
4539
5084
  label: MORE_ACTIONS_LABEL,
4540
5085
  mode: "default",
4541
- surface: "terminal-footer"
5086
+ placement: "terminal-footer",
5087
+ surface: "activity-dialog"
4542
5088
  }) : null ]
4543
5089
  }) ]
4544
5090
  });
@@ -4673,7 +5219,8 @@ function MetaBlock({detail: detail, definition: definition, document: document,
4673
5219
  children: /* @__PURE__ */ jsx(Flex, {
4674
5220
  children: /* @__PURE__ */ jsx(DocPreviewLink, {
4675
5221
  gdr: document,
4676
- layout: "inline"
5222
+ layout: "inline",
5223
+ source: "dialog-subject"
4677
5224
  })
4678
5225
  })
4679
5226
  }) : null,
@@ -4697,7 +5244,8 @@ function TopActions({detail: detail, definition: definition, instanceId: instanc
4697
5244
  actionEval: a,
4698
5245
  activity: activityName,
4699
5246
  instanceId: instanceId,
4700
- surface: "dialog-strip"
5247
+ placement: "dialog-strip",
5248
+ surface: "activity-dialog"
4701
5249
  }, a.action.name)), p.manualTarget ? /* @__PURE__ */ jsx(Button, {
4702
5250
  as: "a",
4703
5251
  fontSize: 1,
@@ -4755,7 +5303,8 @@ function FieldRow({field: field, detail: detail, entry: entry, activityName: act
4755
5303
  state: {
4756
5304
  kind: "editable",
4757
5305
  field: assignField
4758
- }
5306
+ },
5307
+ surface: "activity-dialog"
4759
5308
  }) : null ]
4760
5309
  }) ]
4761
5310
  });
@@ -4763,7 +5312,8 @@ function FieldRow({field: field, detail: detail, entry: entry, activityName: act
4763
5312
  const editable = detail.editableByName.get(field.name);
4764
5313
  return editable ? /* @__PURE__ */ jsx(EditableFieldControl, {
4765
5314
  field: editable,
4766
- instanceId: instanceId
5315
+ instanceId: instanceId,
5316
+ surface: "activity-dialog"
4767
5317
  }) : /* @__PURE__ */ jsxs(Stack, {
4768
5318
  gap: 2,
4769
5319
  children: [ head,
@@ -4837,42 +5387,22 @@ function ForMeEmptyState() {
4837
5387
  description: "No activities or to-dos are assigned to you",
4838
5388
  icon: /* @__PURE__ */ jsx(CheckmarkCircleIcon, {}),
4839
5389
  title: "Nothing to do!"
4840
- });
4841
- }
4842
-
4843
- function CountedLabel({count: count, label: label}) {
4844
- return count === void 0 || count === 0 ? /* @__PURE__ */ jsx(Fragment, {
4845
- children: label
4846
- }) : /* @__PURE__ */ jsxs(Flex, {
4847
- align: "center",
4848
- gap: 2,
4849
- children: [ label,
4850
- /* @__PURE__ */ jsx(Text, {
4851
- muted: !0,
4852
- size: 1,
4853
- weight: "regular",
4854
- children: count
4855
- }) ]
4856
- });
4857
- }
4858
-
4859
- function SpinnerSlot({busy: busy}) {
4860
- const size = useTextIconSize(), trim = useCapTrimFor(size);
4861
- /* @__PURE__ */
4862
- return jsx(Flex, {
4863
- align: "center",
4864
- flex: "none",
4865
- justify: "center",
4866
- style: {
4867
- height: size,
4868
- marginBottom: trim,
4869
- marginTop: trim,
4870
- width: size
4871
- },
4872
- children: busy ? /* @__PURE__ */ jsx(Spinner, {
5390
+ });
5391
+ }
5392
+
5393
+ function CountedLabel({count: count, label: label}) {
5394
+ return count === void 0 || count === 0 ? /* @__PURE__ */ jsx(Fragment, {
5395
+ children: label
5396
+ }) : /* @__PURE__ */ jsxs(Flex, {
5397
+ align: "center",
5398
+ gap: 2,
5399
+ children: [ label,
5400
+ /* @__PURE__ */ jsx(Text, {
4873
5401
  muted: !0,
4874
- size: 1
4875
- }) : null
5402
+ size: 1,
5403
+ weight: "regular",
5404
+ children: count
5405
+ }) ]
4876
5406
  });
4877
5407
  }
4878
5408
 
@@ -4902,6 +5432,92 @@ function GroupHeading({busy: busy, count: count, title: title, end: end}) {
4902
5432
  });
4903
5433
  }
4904
5434
 
5435
+ function ActivityDateControl({instanceId: instanceId, state: state, surface: surface, dueDates: dueDates}) {
5436
+ const editField = useEditField(surface), {save: save} = useSaveField({
5437
+ instanceId: instanceId
5438
+ }), [open, setOpen] = useState(!1), raw = dateControlValue({
5439
+ state: state,
5440
+ dueDates: dueDates
5441
+ }), display = raw === void 0 ? null : /* @__PURE__ */ jsx(Text, {
5442
+ muted: !0,
5443
+ size: 1,
5444
+ children: formatDate(raw)
5445
+ });
5446
+ if (state.kind === "none") return display;
5447
+ if (state.kind === "closed") return display === null ? null : /* @__PURE__ */ jsx(HoverHint, {
5448
+ text: state.reason,
5449
+ children: /* @__PURE__ */ jsx(Button, {
5450
+ "aria-label": "Date",
5451
+ as: "span",
5452
+ fontSize: 1,
5453
+ mode: "bleed",
5454
+ onClick: stopRowClick,
5455
+ onMouseDown: stopRowMouseDown,
5456
+ padding: 2,
5457
+ children: display
5458
+ })
5459
+ });
5460
+ const kind = dateControlKind(state), commit = (mode, value) => {
5461
+ state.kind === "editable" && save(() => editField({
5462
+ instanceId: instanceId,
5463
+ field: state.field,
5464
+ ...mode === "set" ? {
5465
+ mode: mode,
5466
+ value: value
5467
+ } : {
5468
+ mode: mode
5469
+ }
5470
+ }));
5471
+ }, handleClick = e => {
5472
+ e.stopPropagation(), setOpen(v => !v);
5473
+ };
5474
+ /* @__PURE__ */
5475
+ return jsx(DismissablePopover, {
5476
+ content: /* @__PURE__ */ jsx(Box, {
5477
+ onClick: stopRowClick,
5478
+ children: state.kind === "loading" || kind === void 0 ? /* @__PURE__ */ jsx(LoadingRow, {
5479
+ label: "Loading…",
5480
+ padding: 3
5481
+ }) : /* @__PURE__ */ jsx(ClearableDatePicker, {
5482
+ clearLabel: "Clear date",
5483
+ onClear: () => {
5484
+ setOpen(!1), commit("unset");
5485
+ },
5486
+ onPick: next => {
5487
+ hasTimeOfDay(kind) || setOpen(!1), commit("set", serializeDateFieldValue(next, kind));
5488
+ },
5489
+ selectTime: hasTimeOfDay(kind),
5490
+ value: parseDateFieldValue(state.field.value, kind)
5491
+ })
5492
+ }),
5493
+ onDismiss: () => setOpen(!1),
5494
+ open: open,
5495
+ children: display ? /* @__PURE__ */ jsx(Button, {
5496
+ "aria-label": "Change date",
5497
+ as: "span",
5498
+ fontSize: 1,
5499
+ mode: "bleed",
5500
+ onClick: handleClick,
5501
+ onMouseDown: stopRowMouseDown,
5502
+ padding: 2,
5503
+ children: display
5504
+ }) : /* @__PURE__ */ jsx(HoverHint, {
5505
+ disabled: open,
5506
+ text: "Set date",
5507
+ children: /* @__PURE__ */ jsx(Button, {
5508
+ "aria-label": "Set date",
5509
+ as: "span",
5510
+ fontSize: 1,
5511
+ icon: CalendarIcon,
5512
+ mode: "bleed",
5513
+ onClick: handleClick,
5514
+ onMouseDown: stopRowMouseDown,
5515
+ padding: 2
5516
+ })
5517
+ })
5518
+ });
5519
+ }
5520
+
4905
5521
  function settledDim(settled) {
4906
5522
  return settled ? {
4907
5523
  opacity: .66
@@ -4932,7 +5548,7 @@ function ActivityRowTitle({dimmed: dimmed, children: children}) {
4932
5548
  });
4933
5549
  }
4934
5550
 
4935
- function RowTerminalActions({actions: actions, activity: activity, instanceId: instanceId}) {
5551
+ function RowTerminalActions({actions: actions, activity: activity, instanceId: instanceId, surface: surface}) {
4936
5552
  const lock = useActionClusterState(), [only, ...rest] = actions;
4937
5553
  return only ? /* @__PURE__ */ jsx(ActionClusterProvider, {
4938
5554
  value: lock,
@@ -4944,14 +5560,16 @@ function RowTerminalActions({actions: actions, activity: activity, instanceId: i
4944
5560
  inButtonCard: !0
4945
5561
  },
4946
5562
  instanceId: instanceId,
4947
- surface: "inline-row"
5563
+ placement: "inline-row",
5564
+ surface: surface
4948
5565
  }) : /* @__PURE__ */ jsx(ActionsMenuButton, {
4949
5566
  actions: actions,
4950
5567
  activity: activity,
4951
5568
  inButtonCard: !0,
4952
5569
  instanceId: instanceId,
4953
5570
  label: "Select action",
4954
- surface: "inline-row"
5571
+ placement: "inline-row",
5572
+ surface: surface
4955
5573
  })
4956
5574
  }) : null;
4957
5575
  }
@@ -4972,7 +5590,7 @@ function TitleTag({hint: hint, tone: tone, children: children}) {
4972
5590
  });
4973
5591
  }
4974
5592
 
4975
- function ActivityRow({face: face, breadcrumb: breadcrumb, assigneeIds: assigneeIds, assignState: assignState, dateControl: dateControl, instanceId: instanceId, onOpen: onOpen, terminalActions: terminalActions = []}) {
5593
+ function ActivityRow({face: face, breadcrumb: breadcrumb, assigneeIds: assigneeIds, assignState: assignState, dateState: dateState, dueDates: dueDates, instanceId: instanceId, onOpen: onOpen, surface: surface, terminalActions: terminalActions = []}) {
4976
5594
  const settled = isTerminalActivityStatus(face.status), dimmed = settledDim(settled);
4977
5595
  /* @__PURE__ */
4978
5596
  return jsxs(WorkRow, {
@@ -4980,12 +5598,20 @@ function ActivityRow({face: face, breadcrumb: breadcrumb, assigneeIds: assigneeI
4980
5598
  children: [ settled || terminalActions.length === 0 ? null : /* @__PURE__ */ jsx(RowTerminalActions, {
4981
5599
  actions: terminalActions,
4982
5600
  activity: face.activityName,
4983
- instanceId: instanceId
4984
- }), dateControl,
5601
+ instanceId: instanceId,
5602
+ surface: surface
5603
+ }),
5604
+ /* @__PURE__ */ jsx(ActivityDateControl, {
5605
+ dueDates: dueDates,
5606
+ instanceId: instanceId,
5607
+ state: dateState,
5608
+ surface: surface
5609
+ }),
4985
5610
  /* @__PURE__ */ jsx(AssignActivityControl, {
4986
5611
  assigneeIds: assigneeIds,
4987
5612
  instanceId: instanceId,
4988
- state: assignState
5613
+ state: assignState,
5614
+ surface: surface
4989
5615
  }) ]
4990
5616
  }),
4991
5617
  lead: /* @__PURE__ */ jsx(ActivityStatusLead, {
@@ -5010,7 +5636,7 @@ function ActivityRow({face: face, breadcrumb: breadcrumb, assigneeIds: assigneeI
5010
5636
  });
5011
5637
  }
5012
5638
 
5013
- function ActivitiesList({entry: entry, onOpenActivity: onOpenActivity}) {
5639
+ function ActivitiesList({entry: entry, onOpenActivity: onOpenActivity, surface: surface}) {
5014
5640
  const activities = (entry.evaluation?.currentStage.activities ?? []).filter(t => !t.scopedOut), activityState = stateByActivity(entry.instance);
5015
5641
  return activities.length === 0 ?
5016
5642
  /* @__PURE__ */ jsx(Box, {
@@ -5030,7 +5656,8 @@ function ActivitiesList({entry: entry, onOpenActivity: onOpenActivity}) {
5030
5656
  editableFields: entry.evaluation?.editableFields
5031
5657
  }),
5032
5658
  instanceId: entry.instance._id,
5033
- onOpen: () => onOpenActivity(t.activity.name)
5659
+ onOpen: () => onOpenActivity(t.activity.name),
5660
+ surface: surface
5034
5661
  }, t.activity.name))
5035
5662
  });
5036
5663
  }
@@ -5094,7 +5721,9 @@ const EDITOR_BY_KIND = {
5094
5721
  assignees: "assignees",
5095
5722
  boolean: "scalar",
5096
5723
  date: "scalar",
5724
+ dueDate: "scalar",
5097
5725
  datetime: "scalar",
5726
+ dueDatetime: "scalar",
5098
5727
  number: "scalar",
5099
5728
  progress: "scalar",
5100
5729
  string: "scalar",
@@ -5104,7 +5733,9 @@ const EDITOR_BY_KIND = {
5104
5733
  array: e => e.value.length > 0 ? pluralize("item", e.value.length, !0) : void 0,
5105
5734
  boolean: e => e.value === null ? void 0 : formatBoolean(e.value),
5106
5735
  date: e => e.value === null ? void 0 : formatDate(e.value),
5736
+ dueDate: e => e.value === null ? void 0 : formatDate(e.value),
5107
5737
  datetime: e => e.value === null ? void 0 : formatDateTime(e.value),
5738
+ dueDatetime: e => e.value === null ? void 0 : formatDateTime(e.value),
5108
5739
  "doc.refs": e => e.value.length > 0 ? pluralize("document", e.value.length, !0) : void 0,
5109
5740
  object: e => e.value === null ? void 0 : "Set",
5110
5741
  "release.ref": e => e.value === null ? void 0 : e.value.releaseName
@@ -5228,7 +5859,7 @@ function TodoFieldDialog({entry: entry, scope: scope, field: field, title: title
5228
5859
 
5229
5860
  const MAX_VISIBLE_PILLS = 4;
5230
5861
 
5231
- function FieldPills({entry: entry, scope: scope, definition: definition}) {
5862
+ function FieldPills({entry: entry, scope: scope, definition: definition, surface: surface}) {
5232
5863
  const [expanded, setExpanded] = useState(!1), pills = deriveFieldPills({
5233
5864
  scope: scope,
5234
5865
  definition: definition,
@@ -5246,7 +5877,8 @@ function FieldPills({entry: entry, scope: scope, definition: definition}) {
5246
5877
  children: [ visible.map(pill => /* @__PURE__ */ jsx(FieldPill, {
5247
5878
  entry: entry,
5248
5879
  pill: pill,
5249
- scope: scope
5880
+ scope: scope,
5881
+ surface: surface
5250
5882
  }, pill.name)), overflow > 0 ?
5251
5883
  /* @__PURE__ */ jsx(Button, {
5252
5884
  mode: "bleed",
@@ -5280,7 +5912,9 @@ function isEmptyFace(face) {
5280
5912
 
5281
5913
  const KIND_ICON = {
5282
5914
  date: CalendarIcon,
5915
+ dueDate: CalendarIcon,
5283
5916
  datetime: CalendarIcon,
5917
+ dueDatetime: CalendarIcon,
5284
5918
  "doc.ref": DocumentIcon,
5285
5919
  "doc.refs": DocumentIcon,
5286
5920
  subject: DocumentIcon
@@ -5432,18 +6066,20 @@ function PillButton({children: children, pill: pill, hintDisabled: hintDisabled
5432
6066
  });
5433
6067
  }
5434
6068
 
5435
- function useFieldCommit({entry: entry, editability: editability}) {
5436
- const {editFieldFor: editFieldFor} = useWorkflowContext(), target = editFieldTarget(editability);
6069
+ function useFieldCommit({entry: entry, editability: editability, surface: surface}) {
6070
+ const editField = useEditField(surface), instanceId = entry.instance._id;
5437
6071
  return {
5438
- set: value => editFieldFor(entry.instance._id, {
5439
- target: target,
6072
+ set: value => editField({
6073
+ instanceId: instanceId,
6074
+ field: editability,
5440
6075
  mode: "set",
5441
6076
  value: value
5442
- }).then(() => {}),
5443
- unset: () => editFieldFor(entry.instance._id, {
5444
- target: target,
6077
+ }),
6078
+ unset: () => editField({
6079
+ instanceId: instanceId,
6080
+ field: editability,
5445
6081
  mode: "unset"
5446
- }).then(() => {})
6082
+ })
5447
6083
  };
5448
6084
  }
5449
6085
 
@@ -5513,11 +6149,28 @@ function ReadOnlyPill({pill: pill}) {
5513
6149
  });
5514
6150
  }
5515
6151
 
5516
- function ScalarPill({entry: entry, pill: pill, editability: editability}) {
5517
- const [open, setOpen] = useState(!1), {set: set} = useFieldCommit({
6152
+ function usePillWrite(args) {
6153
+ const {entry: entry, editability: editability, surface: surface, onSaved: onSaved} = args, commit = useFieldCommit({
6154
+ entry: entry,
6155
+ editability: editability,
6156
+ surface: surface
6157
+ }), saver = useSaveField({
6158
+ instanceId: entry.instance._id,
6159
+ ...onSaved === void 0 ? {} : {
6160
+ onSaved: onSaved
6161
+ }
6162
+ });
6163
+ return {
6164
+ ...commit,
6165
+ ...saver
6166
+ };
6167
+ }
6168
+
6169
+ function ScalarPill({entry: entry, pill: pill, editability: editability, surface: surface}) {
6170
+ const [open, setOpen] = useState(!1), {set: set, saving: saving, save: save} = usePillWrite({
5518
6171
  entry: entry,
5519
- editability: editability
5520
- }), {saving: saving, save: save} = useSaveField({
6172
+ editability: editability,
6173
+ surface: surface,
5521
6174
  onSaved: () => setOpen(!1)
5522
6175
  });
5523
6176
  /* @__PURE__ */
@@ -5543,18 +6196,21 @@ function ScalarPill({entry: entry, pill: pill, editability: editability}) {
5543
6196
  });
5544
6197
  }
5545
6198
 
5546
- function DatePill({entry: entry, pill: pill, editability: editability, kind: kind}) {
6199
+ function DatePill({entry: entry, pill: pill, editability: editability, surface: surface, kind: kind}) {
5547
6200
  const [open, setOpen] = useState(!1), {set: set} = useFieldCommit({
5548
6201
  entry: entry,
5549
- editability: editability
5550
- }), {save: save} = useSaveField();
6202
+ editability: editability,
6203
+ surface: surface
6204
+ }), {save: save} = useSaveField({
6205
+ instanceId: entry.instance._id
6206
+ });
5551
6207
  /* @__PURE__ */
5552
6208
  return jsx(PillPopover, {
5553
6209
  content: /* @__PURE__ */ jsx(DatePicker, {
5554
6210
  onSelect: next => {
5555
- kind === "date" && setOpen(!1), save(() => set(serializeDateFieldValue(next, kind)));
6211
+ hasTimeOfDay(kind) || setOpen(!1), save(() => set(serializeDateFieldValue(next, kind)));
5556
6212
  },
5557
- selectTime: kind === "datetime",
6213
+ selectTime: hasTimeOfDay(kind),
5558
6214
  value: parseDateFieldValue(pill.entry?.value, kind)
5559
6215
  }),
5560
6216
  onDismiss: () => setOpen(!1),
@@ -5564,11 +6220,12 @@ function DatePill({entry: entry, pill: pill, editability: editability, kind: kin
5564
6220
  });
5565
6221
  }
5566
6222
 
5567
- function BooleanPill({entry: entry, pill: pill, editability: editability}) {
5568
- const {set: set} = useFieldCommit({
6223
+ function BooleanPill({entry: entry, pill: pill, editability: editability, surface: surface}) {
6224
+ const {set: set, save: save} = usePillWrite({
5569
6225
  entry: entry,
5570
- editability: editability
5571
- }), {save: save} = useSaveField(), chrome = usePillChrome(), current = pill.entry?._type === "boolean" ? pill.entry.value : null, menuId = useId();
6226
+ editability: editability,
6227
+ surface: surface
6228
+ }), chrome = usePillChrome(), current = pill.entry?._type === "boolean" ? pill.entry.value : null, menuId = useId();
5572
6229
  /* @__PURE__ */
5573
6230
  return jsx(PillHint, {
5574
6231
  pill: pill,
@@ -5612,12 +6269,14 @@ function BooleanPill({entry: entry, pill: pill, editability: editability}) {
5612
6269
  });
5613
6270
  }
5614
6271
 
5615
- function ActorPill({entry: entry, pill: pill, editability: editability}) {
6272
+ function ActorPill({entry: entry, pill: pill, editability: editability, surface: surface}) {
5616
6273
  const [open, setOpen] = useState(!1), {set: set, unset: unset} = useFieldCommit({
5617
6274
  entry: entry,
5618
- editability: editability
6275
+ editability: editability,
6276
+ surface: surface
5619
6277
  }), current = isActorShape(pill.entry?.value) ? pill.entry.value : null, {pick: pick} = useActorPick({
5620
6278
  current: current,
6279
+ instanceId: entry.instance._id,
5621
6280
  onSave: set,
5622
6281
  onUnset: unset
5623
6282
  });
@@ -5637,12 +6296,14 @@ function ActorPill({entry: entry, pill: pill, editability: editability}) {
5637
6296
  });
5638
6297
  }
5639
6298
 
5640
- function AssigneePill({entry: entry, pill: pill, editability: editability}) {
6299
+ function AssigneePill({entry: entry, pill: pill, editability: editability, surface: surface}) {
5641
6300
  const [open, setOpen] = useState(!1), {set: set, unset: unset} = useFieldCommit({
5642
6301
  entry: entry,
5643
- editability: editability
6302
+ editability: editability,
6303
+ surface: surface
5644
6304
  }), current = pill.entry?._type === "assignee" ? pill.entry.value : null, {pick: pick} = useAssigneePick({
5645
6305
  current: current,
6306
+ instanceId: entry.instance._id,
5646
6307
  onSave: set,
5647
6308
  onUnset: unset
5648
6309
  });
@@ -5662,11 +6323,12 @@ function AssigneePill({entry: entry, pill: pill, editability: editability}) {
5662
6323
  });
5663
6324
  }
5664
6325
 
5665
- function AssigneesPill({entry: entry, pill: pill, editability: editability}) {
5666
- const [open, setOpen] = useState(!1), {set: set} = useFieldCommit({
6326
+ function AssigneesPill({entry: entry, pill: pill, editability: editability, surface: surface}) {
6327
+ const [open, setOpen] = useState(!1), {set: set, saving: saving, save: save} = usePillWrite({
5667
6328
  entry: entry,
5668
- editability: editability
5669
- }), {saving: saving, save: save} = useSaveField(), current = pill.face.kind === "assignees" ? pill.face.assignees : [];
6329
+ editability: editability,
6330
+ surface: surface
6331
+ }), current = pill.face.kind === "assignees" ? pill.face.assignees : [];
5670
6332
  /* @__PURE__ */
5671
6333
  return jsx(PillPopover, {
5672
6334
  content: /* @__PURE__ */ jsx(AssigneePicker, {
@@ -5709,46 +6371,53 @@ function TodoPill({entry: entry, pill: pill, scope: scope}) {
5709
6371
  });
5710
6372
  }
5711
6373
 
5712
- function ScalarEditorPill({entry: entry, pill: pill, editability: editability}) {
6374
+ function ScalarEditorPill({entry: entry, pill: pill, editability: editability, surface: surface}) {
5713
6375
  return editability.type === "boolean" ? /* @__PURE__ */ jsx(BooleanPill, {
5714
6376
  editability: editability,
5715
6377
  entry: entry,
5716
- pill: pill
5717
- }) : editability.type === "date" || editability.type === "datetime" ? /* @__PURE__ */ jsx(DatePill, {
6378
+ pill: pill,
6379
+ surface: surface
6380
+ }) : isDateFieldKind(editability.type) ? /* @__PURE__ */ jsx(DatePill, {
5718
6381
  editability: editability,
5719
6382
  entry: entry,
5720
6383
  kind: editability.type,
5721
- pill: pill
6384
+ pill: pill,
6385
+ surface: surface
5722
6386
  }) : /* @__PURE__ */ jsx(ScalarPill, {
5723
6387
  editability: editability,
5724
6388
  entry: entry,
5725
- pill: pill
6389
+ pill: pill,
6390
+ surface: surface
5726
6391
  });
5727
6392
  }
5728
6393
 
5729
- function EditorPill({entry: entry, pill: pill, editability: editability}) {
6394
+ function EditorPill({entry: entry, pill: pill, editability: editability, surface: surface}) {
5730
6395
  return pill.editor === "scalar" ? /* @__PURE__ */ jsx(ScalarEditorPill, {
5731
6396
  editability: editability,
5732
6397
  entry: entry,
5733
- pill: pill
6398
+ pill: pill,
6399
+ surface: surface
5734
6400
  }) : pill.editor === "actor" ? /* @__PURE__ */ jsx(ActorPill, {
5735
6401
  editability: editability,
5736
6402
  entry: entry,
5737
- pill: pill
6403
+ pill: pill,
6404
+ surface: surface
5738
6405
  }) : pill.editor === "assignee" ? /* @__PURE__ */ jsx(AssigneePill, {
5739
6406
  editability: editability,
5740
6407
  entry: entry,
5741
- pill: pill
6408
+ pill: pill,
6409
+ surface: surface
5742
6410
  }) : pill.editor === "assignees" ? /* @__PURE__ */ jsx(AssigneesPill, {
5743
6411
  editability: editability,
5744
6412
  entry: entry,
5745
- pill: pill
6413
+ pill: pill,
6414
+ surface: surface
5746
6415
  }) : /* @__PURE__ */ jsx(ReadOnlyPill, {
5747
6416
  pill: pill
5748
6417
  });
5749
6418
  }
5750
6419
 
5751
- function FieldPill({entry: entry, pill: pill, scope: scope}) {
6420
+ function FieldPill({entry: entry, pill: pill, scope: scope, surface: surface}) {
5752
6421
  if (pill.editor === "todoList" && pill.entry !== void 0) /* @__PURE__ */
5753
6422
  return jsx(TodoPill, {
5754
6423
  entry: entry,
@@ -5761,7 +6430,8 @@ function FieldPill({entry: entry, pill: pill, scope: scope}) {
5761
6430
  }) : /* @__PURE__ */ jsx(EditorPill, {
5762
6431
  editability: editability,
5763
6432
  entry: entry,
5764
- pill: pill
6433
+ pill: pill,
6434
+ surface: surface
5765
6435
  });
5766
6436
  }
5767
6437
 
@@ -5892,7 +6562,7 @@ function InstanceToolLink({entry: entry}) {
5892
6562
  }
5893
6563
  });
5894
6564
  }, [ instanceId ]);
5895
- return tools.some(tool => tool.name === WORKFLOWS_TOOL_NAME) ? /* @__PURE__ */ jsx(Flex, {
6565
+ return workflowsToolAvailable(tools) ? /* @__PURE__ */ jsx(Flex, {
5896
6566
  children: /* @__PURE__ */ jsx(Button, {
5897
6567
  as: WorkflowIntentLink,
5898
6568
  fontSize: 1,
@@ -6095,6 +6765,7 @@ function StageGlyph(props) {
6095
6765
  }
6096
6766
 
6097
6767
  function StageFace({completed: completed = !1, detail: detail, title: title}) {
6768
+ const tone = completed ? "default" : "primary";
6098
6769
  /* @__PURE__ */
6099
6770
  return jsxs(Flex, {
6100
6771
  align: "center",
@@ -6103,8 +6774,8 @@ function StageFace({completed: completed = !1, detail: detail, title: title}) {
6103
6774
  children: [
6104
6775
  /* @__PURE__ */ jsx(TextWithTone, {
6105
6776
  size: 1,
6106
- tone: "primary",
6107
- children: completed ? /* @__PURE__ */ jsx(CheckmarkCircleIcon, {
6777
+ tone: tone,
6778
+ children: completed ? /* @__PURE__ */ jsx(CheckmarkCircleFilledIcon, {
6108
6779
  style: {
6109
6780
  color: "inherit"
6110
6781
  }
@@ -6116,7 +6787,7 @@ function StageFace({completed: completed = !1, detail: detail, title: title}) {
6116
6787
  }),
6117
6788
  /* @__PURE__ */ jsx(TextWithTone, {
6118
6789
  size: 1,
6119
- tone: "primary",
6790
+ tone: tone,
6120
6791
  weight: "medium",
6121
6792
  children: title
6122
6793
  }), detail === void 0 ? null : /* @__PURE__ */ jsx(Text, {
@@ -6134,10 +6805,10 @@ function StageChip({completed: completed = !1, detail: detail, interactive: inte
6134
6805
  padding: 3,
6135
6806
  radius: 4,
6136
6807
  style: {
6137
- boxShadow: `inset 0 0 0 1px ${color.focusRing}`,
6808
+ boxShadow: `inset 0 0 0 1px ${completed ? color.badge.default.fg : color.focusRing}`,
6138
6809
  cursor: interactive ? "help" : "default"
6139
6810
  },
6140
- tone: "primary",
6811
+ tone: completed ? "default" : "primary",
6141
6812
  ...interactive ? {
6142
6813
  tabIndex: 0
6143
6814
  } : {},
@@ -6365,7 +7036,8 @@ function WorkflowInstanceSection({defaultOpen: defaultOpen, entry: entry, onOpen
6365
7036
  children: [
6366
7037
  /* @__PURE__ */ jsx(InstanceSnapshotBody, {
6367
7038
  entry: entry,
6368
- onOpenActivity: onOpenActivity
7039
+ onOpenActivity: onOpenActivity,
7040
+ surface: "document-view"
6369
7041
  }),
6370
7042
  /* @__PURE__ */ jsx(InstanceToolLink, {
6371
7043
  entry: entry
@@ -6374,7 +7046,7 @@ function WorkflowInstanceSection({defaultOpen: defaultOpen, entry: entry, onOpen
6374
7046
  });
6375
7047
  }
6376
7048
 
6377
- function InstanceSnapshotBody({entry: entry, onOpenActivity: onOpenActivity}) {
7049
+ function InstanceSnapshotBody({entry: entry, onOpenActivity: onOpenActivity, surface: surface}) {
6378
7050
  const definition = useDefinition(entry), {instance: instance} = entry;
6379
7051
  /* @__PURE__ */
6380
7052
  return jsxs(Stack, {
@@ -6385,7 +7057,8 @@ function InstanceSnapshotBody({entry: entry, onOpenActivity: onOpenActivity}) {
6385
7057
  /* @__PURE__ */ jsx(FieldPills, {
6386
7058
  definition: definition,
6387
7059
  entry: entry,
6388
- scope: "workflow"
7060
+ scope: "workflow",
7061
+ surface: surface
6389
7062
  }),
6390
7063
  /* @__PURE__ */ jsxs(Card, {
6391
7064
  border: !0,
@@ -6410,14 +7083,15 @@ function InstanceSnapshotBody({entry: entry, onOpenActivity: onOpenActivity}) {
6410
7083
  padding: 2,
6411
7084
  children: /* @__PURE__ */ jsx(StageSection, {
6412
7085
  entry: entry,
6413
- onOpenActivity: onOpenActivity
7086
+ onOpenActivity: onOpenActivity,
7087
+ surface: surface
6414
7088
  })
6415
7089
  }) ]
6416
7090
  }) ]
6417
7091
  });
6418
7092
  }
6419
7093
 
6420
- function StageSection({entry: entry, onOpenActivity: onOpenActivity}) {
7094
+ function StageSection({entry: entry, onOpenActivity: onOpenActivity, surface: surface}) {
6421
7095
  const definition = useDefinition(entry), notice = instanceNotice(entry);
6422
7096
  return notice !== null ? notice : /* @__PURE__ */ jsx(StaleLock, {
6423
7097
  stale: isEvaluationStale(entry),
@@ -6427,11 +7101,13 @@ function StageSection({entry: entry, onOpenActivity: onOpenActivity}) {
6427
7101
  /* @__PURE__ */ jsx(FieldPills, {
6428
7102
  definition: definition,
6429
7103
  entry: entry,
6430
- scope: "stage"
7104
+ scope: "stage",
7105
+ surface: surface
6431
7106
  }),
6432
7107
  /* @__PURE__ */ jsx(ActivitiesList, {
6433
7108
  entry: entry,
6434
- onOpenActivity: onOpenActivity
7109
+ onOpenActivity: onOpenActivity,
7110
+ surface: surface
6435
7111
  }) ]
6436
7112
  })
6437
7113
  });
@@ -6440,8 +7116,9 @@ function StageSection({entry: entry, onOpenActivity: onOpenActivity}) {
6440
7116
  function CautionNote({action: action, label: label}) {
6441
7117
  /* @__PURE__ */
6442
7118
  return jsxs(Card, {
6443
- padding: 2,
6444
7119
  paddingLeft: 3,
7120
+ paddingRight: 2,
7121
+ paddingY: 3,
6445
7122
  radius: 3,
6446
7123
  style: {
6447
7124
  alignItems: "center",
@@ -6469,18 +7146,20 @@ function CautionNote({action: action, label: label}) {
6469
7146
  }
6470
7147
 
6471
7148
  function CopyDetailsButton({unreadable: unreadable}) {
6472
- const toast = useClosableToast();
7149
+ const toast = useWorkflowToast();
6473
7150
  /* @__PURE__ */
6474
7151
  return jsx(Button, {
6475
7152
  fontSize: 1,
6476
7153
  mode: "ghost",
6477
7154
  onClick: () => {
6478
7155
  navigator.clipboard.writeText(unreadableDocsReport(unreadable)).then(() => toast.push({
6479
- status: "success",
6480
- title: "Details copied"
7156
+ id: TOAST_ID.detailsCopy,
7157
+ status: "info",
7158
+ title: "Details copied to clipboard"
6481
7159
  }), () => toast.push({
7160
+ id: TOAST_ID.detailsCopy,
6482
7161
  status: "error",
6483
- title: "Could not copy the details"
7162
+ title: "Failed to copy the details"
6484
7163
  }));
6485
7164
  },
6486
7165
  padding: 2,
@@ -6506,7 +7185,7 @@ const HEADLINES = {
6506
7185
  };
6507
7186
 
6508
7187
  function remediation(invalid) {
6509
- return invalid.reason === "model-ahead" ? `"${invalid.documentId}" was created by a newer version of the workflow engine than this Studio can read. It needs a plugin update that supports the newer data model — share the details with your Studio maintainers.` : `This version of the Studio can't read "${invalid.documentId}" — it was likely created by a newer or different version of the workflow tooling. Share the details with your Studio maintainers.`;
7188
+ return invalid.reason === "model-ahead" ? `"${invalid.documentId}" was created by a newer version of the workflow engine than this Studio can read. It needs a plugin update that supports the newer data model — share the details with your Studio maintainers.` : `This version of the Studio cant read "${invalid.documentId}" — it was likely created by a newer or different version of the workflow tooling. Share the details with your Studio maintainers.`;
6510
7189
  }
6511
7190
 
6512
7191
  function InvalidDocNotice({invalid: invalid}) {
@@ -6557,18 +7236,6 @@ function TabSwitch({ariaControls: ariaControls, idPrefix: idPrefix, options: opt
6557
7236
  });
6558
7237
  }
6559
7238
 
6560
- function useDelayedFlag(active, delayMs) {
6561
- const [held, setHeld] = useState(!1);
6562
- return useEffect(() => {
6563
- if (!active) {
6564
- setHeld(!1);
6565
- return;
6566
- }
6567
- const timer = setTimeout(() => setHeld(!0), delayMs);
6568
- return () => clearTimeout(timer);
6569
- }, [ active, delayMs ]), held && active;
6570
- }
6571
-
6572
7239
  function openActivityGone(args) {
6573
7240
  const {entry: entry, target: target} = args;
6574
7241
  if (entry === void 0 || entry.instance.currentStage !== target.stage) return !0;
@@ -6633,18 +7300,16 @@ function coveredFieldNames(ctx) {
6633
7300
  return release && names.add(release.name), names;
6634
7301
  }
6635
7302
 
6636
- const OPTIONAL_INIT_KINDS = /* @__PURE__ */ new Set([ "array", "assignees", "doc.refs" ]);
6637
-
6638
7303
  function inputInitFields(args) {
6639
7304
  return (args.definition?.fields ?? []).filter(entry => entry.initialValue?.type === "input" && !args.covered.has(entry.name));
6640
7305
  }
6641
7306
 
6642
- function gatesStart(entry) {
6643
- return !OPTIONAL_INIT_KINDS.has(entry.type);
7307
+ function isRequiredEntry(entry) {
7308
+ return entry.required === !0;
6644
7309
  }
6645
7310
 
6646
7311
  function missingInitFields(initFields, values) {
6647
- return initFields.filter(entry => gatesStart(entry) && !isFieldFilled(entry, values[entry.name] ?? null));
7312
+ return initFields.filter(entry => isRequiredEntry(entry) && !isFieldFilled(entry, values[entry.name] ?? null));
6648
7313
  }
6649
7314
 
6650
7315
  function numericFilled(value) {
@@ -6701,7 +7366,9 @@ const asInitial = candidate => candidate, scalarInitialValue = (entry, value) =>
6701
7366
  value: value === !0
6702
7367
  }),
6703
7368
  date: scalarInitialValue,
7369
+ dueDate: scalarInitialValue,
6704
7370
  datetime: scalarInitialValue,
7371
+ dueDatetime: scalarInitialValue,
6705
7372
  "doc.ref": singleRefInitialValue,
6706
7373
  "doc.refs": (entry, value) => asInitial({
6707
7374
  type: entry.type,
@@ -6852,15 +7519,36 @@ function classifyStartError(err) {
6852
7519
  };
6853
7520
  }
6854
7521
 
7522
+ function startOutcomeToast(outcome, label) {
7523
+ return outcome.kind === "started-not-settled" ? {
7524
+ id: TOAST_ID.start,
7525
+ status: "warning",
7526
+ title: `“${label}” started but didn’t finish`,
7527
+ description: outcome.description
7528
+ } : outcome.kind === "not-allowed" ? {
7529
+ id: TOAST_ID.start,
7530
+ status: "warning",
7531
+ title: `“${label}” can’t be started right now`,
7532
+ description: outcome.description
7533
+ } : {
7534
+ id: TOAST_ID.start,
7535
+ status: "error",
7536
+ title: `Failed to start “${label}”`,
7537
+ description: outcome.description
7538
+ };
7539
+ }
7540
+
6855
7541
  function boundReleaseId(mapping, selectedReleaseId) {
6856
7542
  return mapping?.perspectiveField !== void 0 && selectedReleaseId !== void 0 ? selectedReleaseId : void 0;
6857
7543
  }
6858
7544
 
7545
+ const MAPPING_BROKEN_HINT = docTypeLabel => `Unavailable — this workflow isn’t set up correctly for ${docTypeLabel} documents`;
7546
+
6859
7547
  function startGate(args) {
6860
7548
  const {mapping: mapping, mappingIssue: mappingIssue, selectedReleaseId: selectedReleaseId, releaseActive: releaseActive} = args;
6861
7549
  if (mappingIssue !== void 0) return {
6862
7550
  blocked: !0,
6863
- tooltip: mappingIssue
7551
+ tooltip: MAPPING_BROKEN_HINT(args.docTypeLabel)
6864
7552
  };
6865
7553
  if (mapping.perspectiveField?.required === !0 && selectedReleaseId === void 0) return {
6866
7554
  blocked: !0,
@@ -6872,7 +7560,7 @@ function startGate(args) {
6872
7560
  };
6873
7561
  if (args.startFilterFailed === !0) return {
6874
7562
  blocked: !0,
6875
- tooltip: `${mapping.label} can't start for this document right now — the workflow's start condition isn't met`
7563
+ tooltip: `${mapping.label} cant start for this document right now — the workflows start condition isnt met`
6876
7564
  };
6877
7565
  if (args.unmetRequirement !== void 0) {
6878
7566
  const requirement = args.unmetRequirement;
@@ -7134,7 +7822,7 @@ function showRowOnError(key, mapping) {
7134
7822
  }
7135
7823
 
7136
7824
  function useStartWorkflow(args) {
7137
- const {mapping: mapping, docId: docId, initialValue: initialValue, source: source} = args, toast = useClosableToast(), {engine: engine, openStartDialog: openStartDialog, mappingIssues: mappingIssues2, mappingStarts: mappingStarts} = useWorkflowContext(), observer = useStudioObserver({
7825
+ const {mapping: mapping, docId: docId, initialValue: initialValue, source: source} = args, toast = useWorkflowToast(), schema = useSchema(), {engine: engine, openStartDialog: openStartDialog, mappingIssues: mappingIssues2, mappingStarts: mappingStarts} = useWorkflowContext(), observer = useStudioObserver({
7138
7826
  engine: engine
7139
7827
  }), [starting, setStarting] = useState(!1), {selectedReleaseId: selectedReleaseId} = usePerspective(), releaseActive = useIsReleaseActive(), startBlock = mappingStarts.get(mappingKey(mapping)), hidden = startKindOf({
7140
7828
  start: startBlock
@@ -7154,6 +7842,7 @@ function useStartWorkflow(args) {
7154
7842
  }), {blocked: blocked, tooltip: tooltip} = startGate({
7155
7843
  mapping: mapping,
7156
7844
  mappingIssue: mappingIssues2.get(mappingKey(mapping)),
7845
+ docTypeLabel: schema.get(mapping.docType)?.title ?? mapping.docType,
7157
7846
  selectedReleaseId: selectedReleaseId,
7158
7847
  releaseActive: releaseActive,
7159
7848
  startFilterFailed: filterFailed,
@@ -7177,8 +7866,9 @@ function useStartWorkflow(args) {
7177
7866
  selectedReleaseId: selectedReleaseId,
7178
7867
  ensureDocumentExists: observer.ensureDocumentExists,
7179
7868
  onPersistFailure: err => toast.push({
7869
+ id: TOAST_ID.documentSave,
7180
7870
  status: "error",
7181
- title: "Could not persist the document before starting",
7871
+ title: "Failed to save the document",
7182
7872
  description: describeError(err)
7183
7873
  }),
7184
7874
  openStartDialog: request => openStartDialog({
@@ -7319,7 +8009,8 @@ function ForMeList({entry: entry, work: work, identity: identity, onOpenActivity
7319
8009
  }),
7320
8010
  breadcrumb: breadcrumb,
7321
8011
  instanceId: instance._id,
7322
- onOpen: () => onOpenActivity(activityEval.activity.name)
8012
+ onOpen: () => onOpenActivity(activityEval.activity.name),
8013
+ surface: "document-view"
7323
8014
  }, activityEval.activity.name)), itemCount > 0 ? /* @__PURE__ */ jsx(TodoItemsList, {
7324
8015
  breadcrumb: breadcrumb,
7325
8016
  entry: entry,
@@ -7495,7 +8186,7 @@ function WorkflowsPanel({entries: entries, documentId: documentId, docTypeMappin
7495
8186
  children: /* @__PURE__ */ jsx(Flex, {
7496
8187
  direction: "column",
7497
8188
  flex: 1,
7498
- children: view === "overview" ? /* @__PURE__ */ jsx(OverviewTab, {
8189
+ children: view === "overview" ? /* @__PURE__ */ jsx(OverviewSections, {
7499
8190
  active: active,
7500
8191
  empty: /* @__PURE__ */ jsx(OverviewEmptyState, {
7501
8192
  docId: documentId,
@@ -7519,7 +8210,7 @@ function WorkflowsPanel({entries: entries, documentId: documentId, docTypeMappin
7519
8210
  });
7520
8211
  }
7521
8212
 
7522
- function OverviewTab({active: active, empty: empty, finished: finished, sectionFor: sectionFor}) {
8213
+ function OverviewSections({active: active, empty: empty, finished: finished, sectionFor: sectionFor}) {
7523
8214
  /* @__PURE__ */
7524
8215
  return jsxs(Stack, {
7525
8216
  gap: 5,
@@ -7667,6 +8358,23 @@ function workflowIssue(args) {
7667
8358
  if (!isInputSourced(subject)) return `autoStart: "${docType}" → workflow "${name}" has a self-filling subject (not caller-provided), so the document can't be its subject — skipped.`;
7668
8359
  }
7669
8360
 
8361
+ function createSubscribers() {
8362
+ const listeners = /* @__PURE__ */ new Set;
8363
+ return {
8364
+ subscribe(listener) {
8365
+ return listeners.add(listener), () => {
8366
+ listeners.delete(listener);
8367
+ };
8368
+ },
8369
+ notify() {
8370
+ for (const listener of [ ...listeners ]) listener();
8371
+ },
8372
+ clear() {
8373
+ listeners.clear();
8374
+ }
8375
+ };
8376
+ }
8377
+
7670
8378
  function actualType$(previews, bareId) {
7671
8379
  return previews.unstable_observeDocumentPairAvailability(bareId).pipe(switchMap(({draft: draft, published: published}) => draft.available ? previews.observeDocumentTypeFromId(getDraftId(bareId)) : published.available ? previews.observeDocumentTypeFromId(bareId) : draft.reason === "PERMISSION_DENIED" || published.reason === "PERMISSION_DENIED" ? of(void 0) : previews.unstable_observeVersionDocumentIds(bareId).pipe(switchMap(([firstVersionId]) => firstVersionId === void 0 ? of(null) : previews.observeDocumentTypeFromId(firstVersionId)))), distinctUntilChanged());
7672
8380
  }
@@ -7674,12 +8382,10 @@ function actualType$(previews, bareId) {
7674
8382
  const REBUILD_DELAY_MS = 5e3;
7675
8383
 
7676
8384
  function createActualTypeProbeStore(previews) {
7677
- const verdicts = /* @__PURE__ */ new Map, live = /* @__PURE__ */ new Map, listeners = /* @__PURE__ */ new Set, notify = () => {
7678
- for (const listener of [ ...listeners ]) listener();
7679
- }, start = (bareId, entry) => actualType$(previews, bareId).subscribe({
8385
+ const verdicts = /* @__PURE__ */ new Map, live = /* @__PURE__ */ new Map, subscribers = createSubscribers(), start = (bareId, entry) => actualType$(previews, bareId).subscribe({
7680
8386
  next: value => {
7681
8387
  verdicts.has(bareId) && verdicts.get(bareId) === value || (verdicts.set(bareId, value),
7682
- notify());
8388
+ subscribers.notify());
7683
8389
  },
7684
8390
  error: err => {
7685
8391
  console.error(`[workflow-studio-plugin] actual-type probe for "${bareId}" failed:`, err),
@@ -7689,12 +8395,14 @@ function createActualTypeProbeStore(previews) {
7689
8395
  }
7690
8396
  });
7691
8397
  return {
7692
- subscribe(listener) {
7693
- return listeners.add(listener), () => {
7694
- listeners.delete(listener);
7695
- };
7696
- },
8398
+ subscribe: subscribers.subscribe,
7697
8399
  read: bareId => verdicts.get(bareId),
8400
+ seed(types) {
8401
+ let changed = !1;
8402
+ for (const [bareId, actualType] of types) verdicts.has(bareId) || (verdicts.set(bareId, actualType),
8403
+ changed = !0);
8404
+ changed && subscribers.notify();
8405
+ },
7698
8406
  track(bareId) {
7699
8407
  let entry = live.get(bareId);
7700
8408
  if (entry === void 0) {
@@ -7715,10 +8423,6 @@ function createActualTypeProbeStore(previews) {
7715
8423
  };
7716
8424
  }
7717
8425
 
7718
- function contentDocumentTypes(schema) {
7719
- return schema.getTypeNames().filter(name => schema.get(name)?.type?.name === "document" && !WORKFLOW_SYSTEM_TYPES.includes(name));
7720
- }
7721
-
7722
8426
  async function readDeployedDefinitions(engine) {
7723
8427
  return (await engine.query({
7724
8428
  groq: latestDefinitionsGroq()
@@ -7893,7 +8597,7 @@ const REPORT_FLUSH_MS = 50, EMPTY_COMMITTED = {
7893
8597
 
7894
8598
  function createEntriesStore() {
7895
8599
  let committed = EMPTY_COMMITTED, reports = /* @__PURE__ */ new Map, byInstance = /* @__PURE__ */ new Map, byDocument = /* @__PURE__ */ new Map;
7896
- const listeners = /* @__PURE__ */ new Set;
8600
+ const subscribers = createSubscribers();
7897
8601
  let flushTimer;
7898
8602
  const recompute = () => {
7899
8603
  flushTimer !== void 0 && (clearTimeout(flushTimer), flushTimer = void 0);
@@ -7906,10 +8610,8 @@ function createEntriesStore() {
7906
8610
  byInstance: nextByInstance,
7907
8611
  previous: byDocument
7908
8612
  });
7909
- if (!(nextByInstance === byInstance && nextByDocument === byDocument)) {
7910
- byInstance = nextByInstance, byDocument = nextByDocument;
7911
- for (const listener of [ ...listeners ]) listener();
7912
- }
8613
+ nextByInstance === byInstance && nextByDocument === byDocument || (byInstance = nextByInstance,
8614
+ byDocument = nextByDocument, subscribers.notify());
7913
8615
  };
7914
8616
  return {
7915
8617
  setCommitted(inputs) {
@@ -7925,13 +8627,9 @@ function createEntriesStore() {
7925
8627
  getReport: instanceId => reports.get(instanceId),
7926
8628
  getInstance: instanceId => byInstance.get(instanceId),
7927
8629
  getDocument: docId => byDocument.get(docId) ?? EMPTY_ENTRIES,
7928
- subscribe(listener) {
7929
- return listeners.add(listener), () => {
7930
- listeners.delete(listener);
7931
- };
7932
- },
8630
+ subscribe: subscribers.subscribe,
7933
8631
  dispose() {
7934
- flushTimer !== void 0 && clearTimeout(flushTimer), flushTimer = void 0, listeners.clear();
8632
+ flushTimer !== void 0 && clearTimeout(flushTimer), flushTimer = void 0, subscribers.clear();
7935
8633
  }
7936
8634
  };
7937
8635
  }
@@ -8061,7 +8759,7 @@ function WorkflowProvider(props) {
8061
8759
  try {
8062
8760
  await (drainRef.current?.(instanceId));
8063
8761
  } catch (err) {
8064
- throw new Error(`The change was committed, but running its effects failed: ${describeError(err)}`, {
8762
+ throw new EffectsIncompleteError(describeError(err), {
8065
8763
  cause: err
8066
8764
  });
8067
8765
  }
@@ -8192,12 +8890,19 @@ function WorkflowProvider(props) {
8192
8890
  });
8193
8891
  }
8194
8892
 
8195
- function sameEntries(a, b) {
8196
- if (a.size !== b.size) return !1;
8197
- for (const [key, value] of b) if (a.get(key) !== value) return !1;
8198
- return !0;
8893
+ function entriesSameBy(equal) {
8894
+ return (a, b) => {
8895
+ if (a.size !== b.size) return !1;
8896
+ for (const [key, value] of b) {
8897
+ const previous = a.get(key);
8898
+ if (previous === void 0 || !equal(previous, value)) return !1;
8899
+ }
8900
+ return !0;
8901
+ };
8199
8902
  }
8200
8903
 
8904
+ const sameIssues = entriesSameBy(sameMappingIssue), sameVersions = entriesSameBy(Object.is);
8905
+
8201
8906
  function sameMappings(a, b) {
8202
8907
  return a.length === b.length && a.every((mapping, index) => {
8203
8908
  const other = b[index];
@@ -8221,10 +8926,7 @@ function useDeployedDefinitions(args) {
8221
8926
  contentResource: contentResource,
8222
8927
  schemaContentTypes: schemaContentTypes
8223
8928
  });
8224
- for (const [rowKey, message] of found) {
8225
- const signature = `${rowKey}|${message}`;
8226
- logged.current.has(signature) || (logged.current.add(signature), console.error(`[workflow-studio-plugin] ${message}`));
8227
- }
8929
+ logOnce(mappingIssueLogLines(found), logged.current);
8228
8930
  const resolvedAutoStart = resolveAutoStart({
8229
8931
  autoStart: mappingAutoStartMap(effectiveMappings),
8230
8932
  knownDocTypes: new Set(schema.getTypeNames()),
@@ -8232,7 +8934,7 @@ function useDeployedDefinitions(args) {
8232
8934
  });
8233
8935
  if (logOnce(resolvedAutoStart.warnings, logged.current), cancelled) return;
8234
8936
  setMappings(prev => sameMappings(prev, effectiveMappings) ? prev : effectiveMappings),
8235
- setIssues(prev => sameEntries(prev, found) ? prev : found), setStarts(mappingStartBlocks({
8937
+ setIssues(prev => sameIssues(prev, found) ? prev : found), setStarts(mappingStartBlocks({
8236
8938
  mappings: effectiveMappings,
8237
8939
  definitions: definitions
8238
8940
  })), setFields(mappingDeclaredFields({
@@ -8240,7 +8942,7 @@ function useDeployedDefinitions(args) {
8240
8942
  definitions: definitions
8241
8943
  }));
8242
8944
  const versions = new Map(latestDeployedDefinitions(definitions).map(d => [ d.name, d.version ]));
8243
- setLatestVersions(prev => sameEntries(prev, versions) ? prev : versions), setAutoStartByType(resolvedAutoStart.byType),
8945
+ setLatestVersions(prev => sameVersions(prev, versions) ? prev : versions), setAutoStartByType(resolvedAutoStart.byType),
8244
8946
  setAutoStartDefinitions(resolvedAutoStart.definitions);
8245
8947
  })().catch(err => {
8246
8948
  console.error("[workflow-studio-plugin] mapping discovery failed — keeping the last resolved workflow state:", err);
@@ -8384,16 +9086,8 @@ function ChecklistInput({value: value, onChange: onChange}) {
8384
9086
  value: it.label
8385
9087
  })
8386
9088
  }),
8387
- /* @__PURE__ */ jsx(HoverHint, {
8388
- text: "Remove item",
8389
- children: /* @__PURE__ */ jsx(Button, {
8390
- "aria-label": "Remove item",
8391
- fontSize: 1,
8392
- icon: CloseIcon,
8393
- mode: "bleed",
8394
- onClick: () => remove(i),
8395
- padding: 2
8396
- })
9089
+ /* @__PURE__ */ jsx(RemoveItemButton, {
9090
+ onClick: () => remove(i)
8397
9091
  }) ]
8398
9092
  }, it._key)),
8399
9093
  /* @__PURE__ */ jsx(Flex, {
@@ -8497,7 +9191,7 @@ function useLatestDefinition(definitionName) {
8497
9191
  function useCreateDoc(args) {
8498
9192
  const {releaseId: releaseId, onCreated: onCreated} = args, {engine: engine, binding: binding} = useWorkflowContext(), observer = useStudioObserver({
8499
9193
  engine: engine
8500
- }), schema = useSchema(), templates = useTemplates(), resolverContext = useInitialValueResolverContext(), toast = useClosableToast(), [creating, setCreating] = useState(!1);
9194
+ }), schema = useSchema(), templates = useTemplates(), resolverContext = useInitialValueResolverContext(), toast = useWorkflowToast(), [creating, setCreating] = useState(!1);
8501
9195
  return {
8502
9196
  creating: creating,
8503
9197
  createDoc: async type => {
@@ -8522,8 +9216,9 @@ function useCreateDoc(args) {
8522
9216
  }));
8523
9217
  } catch (err) {
8524
9218
  toast.push({
9219
+ id: TOAST_ID.documentCreate,
8525
9220
  status: "error",
8526
- title: "Could not create document",
9221
+ title: "Failed to create the document",
8527
9222
  description: describeError(err)
8528
9223
  });
8529
9224
  } finally {
@@ -8735,7 +9430,7 @@ function InitFieldRow({entry: entry, docType: docType, releaseId: releaseId, val
8735
9430
  /* @__PURE__ */ jsx(Text, {
8736
9431
  size: 1,
8737
9432
  weight: "medium",
8738
- children: gatesStart(entry) ? `${label} (Required)` : label
9433
+ children: isRequiredEntry(entry) ? `${label} (Required)` : label
8739
9434
  }), entry.description ? /* @__PURE__ */ jsx(Text, {
8740
9435
  muted: !0,
8741
9436
  size: 1,
@@ -8840,31 +9535,12 @@ function ResumeNote({onStartNew: onStartNew}) {
8840
9535
 
8841
9536
  function handleStartError(args) {
8842
9537
  const {label: label, toast: toast, seedInstance: seedInstance, onClose: onClose} = args, outcome = classifyStartError(args.err);
8843
- if (outcome.kind === "started-not-settled") {
8844
- toast.push({
8845
- status: "warning",
8846
- title: `${label} started, but didn’t finish auto-advancing`,
8847
- description: outcome.description
8848
- }), outcome.instance !== void 0 && seedInstance(outcome.instance), onClose();
8849
- return;
8850
- }
8851
- if (outcome.kind === "not-allowed") {
8852
- toast.push({
8853
- status: "warning",
8854
- title: `${label} can't be started right now`,
8855
- description: outcome.description
8856
- });
8857
- return;
8858
- }
8859
- toast.push({
8860
- status: "error",
8861
- title: `Failed to start ${label}`,
8862
- description: outcome.description
8863
- });
9538
+ toast.push(startOutcomeToast(outcome, label)), outcome.kind === "started-not-settled" && (outcome.instance !== void 0 && seedInstance(outcome.instance),
9539
+ onClose());
8864
9540
  }
8865
9541
 
8866
9542
  function StartWorkflowForm({request: request, onClose: onClose}) {
8867
- const {definition: definitionName, label: label, mapping: mapping, subjectDocId: subjectDocId} = request, {engine: engine, binding: binding, seedInstance: seedInstance} = useWorkflowContext(), telemetry = useWorkflowTelemetry(), toast = useClosableToast(), {def: definition, error: definitionError} = useLatestDefinition(definitionName), {selectedReleaseId: selectedReleaseId} = usePerspective(), releaseId = boundReleaseId(mapping, selectedReleaseId), [values, setValues] = useState({}), [attempt, setAttempt] = useState(void 0), starting = attempt !== void 0, [heldInstanceId, setHeldInstanceId] = useState(void 0), [startNew, setStartNew] = useState(!1), {entries: entries} = useWorkflowsForDocument(subjectDocId), resumeEntry = presentedResumeEntry({
9543
+ const {definition: definitionName, label: label, mapping: mapping, subjectDocId: subjectDocId} = request, {engine: engine, binding: binding, seedInstance: seedInstance} = useWorkflowContext(), telemetry = useWorkflowTelemetry(), toast = useWorkflowToast(), {def: definition, error: definitionError} = useLatestDefinition(definitionName), {selectedReleaseId: selectedReleaseId} = usePerspective(), releaseId = boundReleaseId(mapping, selectedReleaseId), [values, setValues] = useState({}), [attempt, setAttempt] = useState(void 0), starting = attempt !== void 0, [heldInstanceId, setHeldInstanceId] = useState(void 0), [startNew, setStartNew] = useState(!1), {entries: entries} = useWorkflowsForDocument(subjectDocId), resumeEntry = presentedResumeEntry({
8868
9544
  entries: entries,
8869
9545
  definitionName: definitionName,
8870
9546
  heldInstanceId: heldInstanceId,
@@ -8904,8 +9580,9 @@ function StartWorkflowForm({request: request, onClose: onClose}) {
8904
9580
  values: values
8905
9581
  }));
8906
9582
  toast.push({
8907
- status: "success",
8908
- title: `${definition?.title ?? label} started`,
9583
+ id: TOAST_ID.start,
9584
+ status: "info",
9585
+ title: `“${definition?.title ?? label}” started`,
8909
9586
  description: `Stage: ${stageTitle(definition ?? void 0, result.instance.currentStage)}`
8910
9587
  }), seedInstance(result.instance), onClose();
8911
9588
  } catch (err) {
@@ -9407,6 +10084,22 @@ function finishedLinesOf(entries) {
9407
10084
  return [ ...latestByType.values() ].sort((a, b) => b.at - a.at).map(({entry: entry}) => entry);
9408
10085
  }
9409
10086
 
10087
+ function openStageTaskCount(evaluation) {
10088
+ if (evaluation !== void 0) return evaluation.currentStage.activities.filter(activity => isOpenActivityStatus(activity.status)).length;
10089
+ }
10090
+
10091
+ const OPEN_VIEW = "Open the Workflows view";
10092
+
10093
+ function unassignedLineHint(args) {
10094
+ return !args.identityKnown || args.openTaskCount === void 0 ? {
10095
+ text: OPEN_VIEW
10096
+ } : args.openTaskCount === 0 ? {
10097
+ text: "No open tasks in this stage"
10098
+ } : {
10099
+ text: `${pluralize("open task", args.openTaskCount, !0)}, none assigned to you`
10100
+ };
10101
+ }
10102
+
9410
10103
  function WorkflowFormStrip(props) {
9411
10104
  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 => {
9412
10105
  telemetry.log(WorkflowFormStripClicked), setParams(focusedViewParams(params, focusId));
@@ -9451,18 +10144,17 @@ function StripLines({active: active, docId: docId, finished: finished, initialVa
9451
10144
  });
9452
10145
  }
9453
10146
 
9454
- function lineHint(identity, forMe) {
10147
+ function lineHint(entry, identity) {
10148
+ const forMe = forMeWorkOf([ entry ], identity);
9455
10149
  return forMe.work.some(({shown: shown}) => hasOpenAssignedWork(shown)) ? {
9456
10150
  content: /* @__PURE__ */ jsx(AssignedTasksHint, {
9457
10151
  work: forMe.work
9458
10152
  }),
9459
10153
  wide: !0
9460
- } : identity !== void 0 ? {
9461
- text: "Open the Workflows view",
9462
- description: "Nothing here is assigned to you"
9463
- } : {
9464
- text: "Open the Workflows view"
9465
- };
10154
+ } : unassignedLineHint({
10155
+ identityKnown: identity !== void 0,
10156
+ openTaskCount: openStageTaskCount(entry.evaluation)
10157
+ });
9466
10158
  }
9467
10159
 
9468
10160
  function StripLine({aside: aside, children: children, hint: hint, onOpen: onOpen}) {
@@ -9495,7 +10187,7 @@ function InstanceLine({entry: entry, onOpen: onOpen}) {
9495
10187
  aside: unprimed ? void 0 : /* @__PURE__ */ jsx(TaskCountSpinner, {
9496
10188
  entry: entry
9497
10189
  }),
9498
- hint: lineHint(identity, forMeWorkOf([ entry ], identity)),
10190
+ hint: lineHint(entry, identity),
9499
10191
  onOpen: onOpen,
9500
10192
  children: [
9501
10193
  /* @__PURE__ */ jsx(Flex, {
@@ -9600,6 +10292,103 @@ function StartWorkflowDialogHost() {
9600
10292
  }, `${startRequest2.definition}:${startRequest2.subjectDocId ?? ""}`) : null;
9601
10293
  }
9602
10294
 
10295
+ const TOOL_TAB_LABELS = {
10296
+ overview: "Workflows",
10297
+ "all-documents": "All documents",
10298
+ "for-me": "For me"
10299
+ }, TOOL_TAB_VALUES = Object.keys(TOOL_TAB_LABELS), TOOL_TABS = TOOL_TAB_VALUES.map(value => ({
10300
+ value: value,
10301
+ label: TOOL_TAB_LABELS[value]
10302
+ })), LANDING_TAB = "overview", TASKS_TAB = "all-documents", WORKFLOW_PAGE_LABELS = {
10303
+ workflow: "Documents",
10304
+ definition: "Definition"
10305
+ }, WORKFLOW_PAGE_VALUES = Object.keys(WORKFLOW_PAGE_LABELS), DEFAULT_WORKFLOW_PAGE = "workflow", WORKFLOW_PAGES = WORKFLOW_PAGE_VALUES.filter(page => page !== DEFAULT_WORKFLOW_PAGE), WORKFLOW_PAGE_TABS = WORKFLOW_PAGE_VALUES.map(value => ({
10306
+ value: value,
10307
+ label: WORKFLOW_PAGE_LABELS[value]
10308
+ })), toolRouter = route.create("/", [ route.create("/instance/:instanceId"), route.create(`/${LANDING_TAB}/:workflowName`, [ route.create("/:workflowPage") ]), route.create("/:workflowsTab") ]);
10309
+
10310
+ function toolTabState(tab) {
10311
+ return {
10312
+ workflowsTab: tab
10313
+ };
10314
+ }
10315
+
10316
+ function landingState() {
10317
+ return toolTabState(LANDING_TAB);
10318
+ }
10319
+
10320
+ function workflowState(workflowName) {
10321
+ return {
10322
+ workflowName: workflowName
10323
+ };
10324
+ }
10325
+
10326
+ function instanceState(instanceId) {
10327
+ return {
10328
+ instanceId: instanceId
10329
+ };
10330
+ }
10331
+
10332
+ function tabSelectionNavigates(current, tab) {
10333
+ return !(current.kind === "home" && tab === current.tab);
10334
+ }
10335
+
10336
+ function toolTabLabel(tab) {
10337
+ return TOOL_TAB_LABELS[tab];
10338
+ }
10339
+
10340
+ function backToTabLabel(tab) {
10341
+ return `Back to ${toolTabLabel(tab)}`;
10342
+ }
10343
+
10344
+ function workflowPageState(page, workflowName) {
10345
+ return page === DEFAULT_WORKFLOW_PAGE ? workflowState(workflowName) : {
10346
+ ...workflowState(workflowName),
10347
+ workflowPage: page
10348
+ };
10349
+ }
10350
+
10351
+ function toolRoute(state) {
10352
+ const {instanceId: instanceId, workflowName: workflowName} = state;
10353
+ if (typeof instanceId == "string") return {
10354
+ kind: "instance",
10355
+ instanceId: instanceId,
10356
+ tab: TASKS_TAB
10357
+ };
10358
+ if (typeof workflowName == "string") return workflowRoute(workflowName, state.workflowPage);
10359
+ const tab = pathSegmentName({
10360
+ segment: state.workflowsTab,
10361
+ names: TOOL_TAB_VALUES
10362
+ });
10363
+ return tab === void 0 ? {
10364
+ kind: "unnamed",
10365
+ tab: LANDING_TAB
10366
+ } : {
10367
+ kind: "home",
10368
+ tab: tab
10369
+ };
10370
+ }
10371
+
10372
+ function workflowRoute(workflowName, pageSegment) {
10373
+ if (pageSegment === void 0) return {
10374
+ kind: "workflow",
10375
+ workflowName: workflowName,
10376
+ tab: LANDING_TAB
10377
+ };
10378
+ const page = pathSegmentName({
10379
+ segment: pageSegment,
10380
+ names: WORKFLOW_PAGES
10381
+ });
10382
+ return page === void 0 ? {
10383
+ kind: "unnamed",
10384
+ tab: LANDING_TAB
10385
+ } : {
10386
+ kind: page,
10387
+ workflowName: workflowName,
10388
+ tab: LANDING_TAB
10389
+ };
10390
+ }
10391
+
9603
10392
  const WorkflowsToolRoot = lazy(() => import("./workflows-tool-root.js"));
9604
10393
 
9605
10394
  function LazyWorkflowsTool() {
@@ -9615,7 +10404,7 @@ const workflowsTool = {
9615
10404
  title: "Workflows",
9616
10405
  icon: TransferIcon,
9617
10406
  component: LazyWorkflowsTool,
9618
- router: route.create("/", [ route.create("/instance/:instanceId"), route.create("/definition/:definitionName"), route.create("/:workflowsTab") ]),
10407
+ router: toolRouter,
9619
10408
  canHandleIntent: (intent, params) => handlesWorkflowIntent(intent, params),
9620
10409
  getIntentState: (intent, params) => workflowIntentState(intent, params)
9621
10410
  }, LOCKABLE_ACTIONS = /* @__PURE__ */ new Map([ [ "delete", "delete" ], [ "publish", "publish" ], [ "unpublish", "unpublish" ] ]);
@@ -9709,4 +10498,4 @@ function WorkflowRootInput(props) {
9709
10498
  });
9710
10499
  }
9711
10500
 
9712
- export { ActivityDetailDialog, ActivityRow, BreadcrumbTail, CautionNote, CollapsibleBand, DismissablePopover, DocPreviewLink, DocRefFace, ForMeEmptyState, HintedMenuButton, HoverHint, InstanceSnapshotBody, InvalidDocNotice, LinkChip, LoadingRow, MetaRow, SpinnerSlot, StageFace, StaleLock, TabSwitch, TrailingHairline, UnreadableDocsNote, UserAvatar, WORKFLOW_API_VERSION, WorkflowInstanceDetailViewed, WorkflowTaskFiltersApplied, WorkflowToolOpened, activityRowProps, assigneeUserIdsOf, committedRowFace, dateControlKind, dateControlValue, definitionSnapshotOf, describeError, findActivity, findActivityNode, formatDate, formatDateTime, formatShortAgo, gdrLocality, instanceBreadcrumb, instanceTitle, isActivityAssignedTo, isEvaluationStale, isOpenActivityStatus, openActivityGone, openableSchemaType, parseDateFieldValue, parseStoredDateValue, pathSegmentTab, readDeployedDefinitions, rowAssignControlState, rowDateControlState, serializeDateFieldValue, stageTitle, stateByActivity, stopRowClick, stopRowMouseDown, useAssignmentIdentity, useAvatarSize, useBadgeCapTrim, useClosableToast, useContainerToken, useDefinition, useLogEventOnMount, useProjectMembers, useSaveField, useSpaceToken, useUserDisplay, useUserDisplays, useWorkflowContext, useWorkflowInstanceEntry, workflowDefaultDocumentNode, workflowStudioPlugin, workflowsView };
10501
+ export { ActivityDetailDialog, ActivityRow, BreadcrumbTail, CautionNote, CollapsibleBand, CountedLabel, DismissablePopover, DocPreviewLink, DocRefFace, EmptyState, ForMeEmptyState, Hairline, HintedMenuButton, HoverHint, InstanceSnapshotBody, InvalidDocNotice, LinkChip, LoadingRow, LogEventOnMount, MetaRow, SpinnerSlot, StageFace, StaleLock, TOAST_ID, TOOL_TABS, TabSwitch, TrailingHairline, UnreadableDocsNote, UserAvatar, WORKFLOW_API_VERSION, WORKFLOW_PAGE_TABS, WorkflowAbortDialogOpened, WorkflowAbortDialogSubmitted, WorkflowBoardWorkflowSelected, WorkflowDefinitionDetailViewed, WorkflowInstanceDetailViewed, WorkflowTaskFiltersApplied, WorkflowTitleSeedDrifted, WorkflowToolOpened, activityRowProps, assigneeUserIdsOf, backToTabLabel, committedRowFace, createSubscribers, datesOf, definitionFingerprint, definitionSnapshotOf, describeError, dueDatesOf, findActivity, findActivityNode, formatDate, formatDateTime, formatShortAgo, 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, useAvatarSize, useBadgeCapTrim, useContainerToken, useDefinition, useDelayedFlag, useLogEventOnMount, useProjectMembers, useSpaceToken, useUserDisplay, useUserDisplays, useWorkflowContext, useWorkflowInstanceEntry, useWorkflowToast, workflowDefaultDocumentNode, workflowPageState, workflowState, workflowStudioPlugin, workflowsView };