@sanity/workflow-studio-plugin 0.20.0 → 0.22.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.
@@ -4,11 +4,13 @@ import { TimelineIcon } from "@sanity/icons/Timeline";
4
4
 
5
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, MenuDivider } from "@sanity/ui";
6
6
 
7
- import { createContext, useContext, useEffect, useSyncExternalStore, useCallback, useMemo, useRef, useState, Fragment as Fragment$1, createElement, isValidElement, useId, useLayoutEffect, Component, lazy, Suspense } from "react";
7
+ import { editFieldTarget, useWorkflowTelemetry, useRefcountedIds } from "@sanity/workflow-react";
8
+
9
+ import { createContext, useContext, useEffect, useSyncExternalStore, useCallback, useRef, useMemo, useState, Fragment as Fragment$1, createElement, isValidElement, useId, useLayoutEffect, Component, lazy, Suspense } from "react";
8
10
 
9
11
  import { usePaneRouter } from "sanity/structure";
10
12
 
11
- import { tryParseGdr, resourceFromParsed, latestDeployedDefinitions, isStartableDefinition, isSubjectEntry, isInputSourced, acceptsDocumentType, missingRequiredInputs, initialFieldIssues, gdrRef, releaseRef, refKindAcceptsTypes, rejectedRefTypes, ActionDisabledError, MutationGuardDeniedError, errorMessage, findOpenStageEntry, actionRendering, terminalState, ENGINE_API_VERSION, WORKFLOW_INSTANCE_TYPE, WORKFLOW_DEFINITION_TYPE, GUARD_DOC_TYPE, isTodoListEntry, isTodoListItem, isTerminalActivityStatus, describeCondition, sentenceCase, checklistLines, findCurrentActivityEntry, scalarValidationIssues, toBareId, isSingleDocRefEntry, isGdr, resolveFieldEntry, isSingleDocRefKind, isNotesEntry, parseDefinitionSnapshot, isUnprimed, StartNotSettledError, StartNotAllowedError, unboundAllowedReads, explainStartAllowed, evaluateStartFilter, startKindOf, readsRootDocument, instancesQuery, assertReadableModel, isTerminalStage, DEFAULT_TRANSITION_WHEN, definitionsListGroq, gdrFromResource, subscriptionDocumentsForInstance, aclPathForResource, definitionLookupGroq, instanceDocId, documentActionDenials } from "@sanity/workflow-engine";
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, instancesQuery, assertReadableModel, projectStartSliceRow, isUnprimed, StartNotSettledError, StartNotAllowedError, humanize, unboundRequirementReads, explainStartRequirement, singleSubjectRequirementRefused, evaluateStartFilter, startKindOf, hasSingleSubjectRequirement, readsRootDocument, isTerminalStage, DEFAULT_TRANSITION_WHEN, latestDefinitionsGroq, gdrFromResource, subscriptionDocumentsForInstance, aclPathForResource, definitionLookupGroq, instanceDocId, documentActionDenials } from "@sanity/workflow-engine";
12
14
 
13
15
  import { CheckmarkIcon } from "@sanity/icons/Checkmark";
14
16
 
@@ -16,16 +18,18 @@ import { CloseIcon } from "@sanity/icons/Close";
16
18
 
17
19
  import { formatDistanceToNow } from "date-fns/formatDistanceToNow";
18
20
 
19
- import { getPublishedId } from "@sanity/client/csm";
21
+ import { sameAssignee, projectMemberRow, ClearableDatePicker, MemberPicker as MemberPicker$1, AssigneePicker as AssigneePicker$1, MemberAvatar, AssigneeBadges as AssigneeBadges$1, AssigneeBadge as AssigneeBadge$1, ProgressBar, MemberAvatarGroup, clampProgress, progressCaption, DatePicker, roleListLabel } from "@sanity/workflow-components";
22
+
23
+ import { getPublishedId, getDraftId } from "@sanity/client/csm";
24
+
25
+ import { defineEvent } from "@sanity/telemetry";
20
26
 
21
27
  import { useStudioProjectUsers, useStudioObserver, useWorkflowEngine, useWorkflowInstances, useWorkflowSession } from "@sanity/workflow-studio";
22
28
 
23
- import { useCurrentUser, SanityDefaultPreview, Preview, useValuePreview, useClient, useSchema, useWorkspace, TextWithTone, usePerspective, useIsReleaseActive, useActiveReleases, useTemplates, useInitialValueResolverContext, resolveInitialValue, definePlugin, isObjectInputProps } from "sanity";
29
+ import { useCurrentUser, SanityDefaultPreview, Preview, useValuePreview, useClient, useSchema, useWorkspace, TextWithTone, usePerspective, useIsReleaseActive, useDocumentPreviewStore, useActiveReleases, useTemplates, useInitialValueResolverContext, resolveInitialValue, definePlugin, isObjectInputProps } from "sanity";
24
30
 
25
31
  import { CalendarIcon } from "@sanity/icons/Calendar";
26
32
 
27
- import { ClearableDatePicker, MemberPicker as MemberPicker$1, AssigneePicker as AssigneePicker$1, MemberAvatar, AssigneeBadges as AssigneeBadges$1, AssigneeBadge as AssigneeBadge$1, ProgressBar, MemberAvatarGroup, clampProgress, progressCaption, DatePicker, roleListLabel } from "@sanity/workflow-components";
28
-
29
33
  import { UserIcon } from "@sanity/icons/User";
30
34
 
31
35
  import { SearchIcon } from "@sanity/icons/Search";
@@ -70,7 +74,9 @@ import { EllipsisHorizontalIcon } from "@sanity/icons/EllipsisHorizontal";
70
74
 
71
75
  import { ArrowUpIcon } from "@sanity/icons/ArrowUp";
72
76
 
73
- import { useRefcountedIds } from "@sanity/workflow-react";
77
+ import { of } from "rxjs";
78
+
79
+ import { switchMap, distinctUntilChanged } from "rxjs/operators";
74
80
 
75
81
  import { TransferIcon } from "@sanity/icons/Transfer";
76
82
 
@@ -485,67 +491,6 @@ function isLiveEntry(entry) {
485
491
  return terminalState(entry.instance) === "in-flight";
486
492
  }
487
493
 
488
- const WORKFLOW_API_VERSION = ENGINE_API_VERSION, WORKFLOW_SYSTEM_TYPES = [ WORKFLOW_INSTANCE_TYPE, WORKFLOW_DEFINITION_TYPE, GUARD_DOC_TYPE ];
489
-
490
- function editTargetOf(field) {
491
- return {
492
- scope: field.scope,
493
- field: field.name,
494
- ...field.activity ? {
495
- activity: field.activity
496
- } : {}
497
- };
498
- }
499
-
500
- const EMPTY_ENTRIES = [], WorkflowContext = createContext(null);
501
-
502
- function useWorkflowContext() {
503
- const value = useContext(WorkflowContext);
504
- if (!value) throw new Error("useWorkflowContext must render inside the workflow plugin provider");
505
- return value;
506
- }
507
-
508
- function useWorkflowsForDocument(rawId) {
509
- return useDocumentWorkflowEntries(rawId, !0);
510
- }
511
-
512
- function useCommittedWorkflowsForDocument(rawId) {
513
- return useDocumentWorkflowEntries(rawId, !1);
514
- }
515
-
516
- function useDocumentWorkflowEntries(rawId, evaluate) {
517
- const {register: register, unregister: unregister, requestEvaluation: requestEvaluation, releaseEvaluation: releaseEvaluation, entries: entries} = useWorkflowContext(), docId = rawId ? getPublishedId(rawId) : void 0;
518
- useEffect(() => {
519
- if (docId) return register(docId), () => unregister(docId);
520
- }, [ docId, register, unregister ]);
521
- const list = useSyncExternalStore(entries.subscribe, useCallback(() => docId ? entries.getDocument(docId) : EMPTY_ENTRIES, [ entries, docId ])), evaluationKey = evaluate ? list.map(entry => entry.instance._id).join("\0") : "";
522
- return useEffect(() => {
523
- if (!evaluate) return;
524
- const ids = evaluationKey === "" ? [] : evaluationKey.split("\0");
525
- for (const id of ids) requestEvaluation(id);
526
- return () => {
527
- for (const id of ids) releaseEvaluation(id);
528
- };
529
- }, [ evaluate, evaluationKey, requestEvaluation, releaseEvaluation ]), {
530
- docId: docId,
531
- entries: list
532
- };
533
- }
534
-
535
- function useHeldWorkflowEntry(instanceId) {
536
- const {entries: entries} = useWorkflowContext();
537
- return useSyncExternalStore(entries.subscribe, useCallback(() => instanceId ? entries.getInstance(instanceId) : void 0, [ entries, instanceId ]));
538
- }
539
-
540
- function useWorkflowInstanceEntry(instanceId, options = {}) {
541
- const {requestInstance: requestInstance, releaseInstance: releaseInstance, requestEvaluation: requestEvaluation, releaseEvaluation: releaseEvaluation} = useWorkflowContext(), evaluate = options.evaluate ?? !0;
542
- return useEffect(() => {
543
- if (instanceId) return requestInstance(instanceId), () => releaseInstance(instanceId);
544
- }, [ instanceId, requestInstance, releaseInstance ]), useEffect(() => {
545
- if (!(!instanceId || !evaluate)) return requestEvaluation(instanceId), () => releaseEvaluation(instanceId);
546
- }, [ instanceId, evaluate, requestEvaluation, releaseEvaluation ]), useHeldWorkflowEntry(instanceId);
547
- }
548
-
549
494
  const isTodoDone = item => item.status === "done", toggledTodoStatus = item => isTodoDone(item) ? "open" : "done";
550
495
 
551
496
  function isTodoOverdue(item, today = /* @__PURE__ */ new Date) {
@@ -621,7 +566,7 @@ function buildTickIndex(evaluation) {
621
566
  function todoFieldEditability(args) {
622
567
  const {evaluation: evaluation, source: source, fieldName: fieldName} = args, match = evaluation?.editableFields.find(f => f.name === fieldName && f.scope === source.scope && (source.scope !== "activity" || f.activity === source.activityName));
623
568
  return match ? match.editable ? {
624
- editTarget: editTargetOf(match)
569
+ editTarget: editFieldTarget(match)
625
570
  } : match.disabledReason === void 0 ? {} : {
626
571
  editDisabledReason: match.disabledReason
627
572
  } : {};
@@ -686,8 +631,8 @@ function assigneeMatches(member, who) {
686
631
  return member.type === "user" ? member.id === who.userId : who.roles.includes(member.role);
687
632
  }
688
633
 
689
- function sameAssignee(a, b) {
690
- return a.type === "user" && b.type === "user" ? a.id === b.id : a.type === "role" && b.type === "role" ? a.role === b.role : !1;
634
+ function changedAssignee(current, next) {
635
+ return next.find(item => !current.some(existing => sameAssignee(item, existing))) ?? current.find(item => !next.some(existing => sameAssignee(item, existing)));
691
636
  }
692
637
 
693
638
  function isAssigneeShape(value) {
@@ -1062,6 +1007,112 @@ function deriveActivityDetail(args) {
1062
1007
  };
1063
1008
  }
1064
1009
 
1010
+ const WORKFLOW_API_VERSION = ENGINE_API_VERSION, WORKFLOW_SYSTEM_TYPES = [ WORKFLOW_INSTANCE_TYPE, WORKFLOW_DEFINITION_TYPE, GUARD_DOC_TYPE ], EMPTY_ENTRIES = [], WorkflowContext = createContext(null);
1011
+
1012
+ function useWorkflowContext() {
1013
+ const value = useContext(WorkflowContext);
1014
+ if (!value) throw new Error("useWorkflowContext must render inside the workflow plugin provider");
1015
+ return value;
1016
+ }
1017
+
1018
+ function useWorkflowsForDocument(rawId) {
1019
+ return useDocumentWorkflowEntries(rawId, !0);
1020
+ }
1021
+
1022
+ function useCommittedWorkflowsForDocument(rawId) {
1023
+ return useDocumentWorkflowEntries(rawId, !1);
1024
+ }
1025
+
1026
+ function useDocumentWorkflowEntries(rawId, evaluate) {
1027
+ const {register: register, unregister: unregister, requestEvaluation: requestEvaluation, releaseEvaluation: releaseEvaluation, entries: entries} = useWorkflowContext(), docId = rawId ? getPublishedId(rawId) : void 0;
1028
+ useEffect(() => {
1029
+ if (docId) return register(docId), () => unregister(docId);
1030
+ }, [ docId, register, unregister ]);
1031
+ const list = useSyncExternalStore(entries.subscribe, useCallback(() => docId ? entries.getDocument(docId) : EMPTY_ENTRIES, [ entries, docId ])), evaluationKey = evaluate ? list.map(entry => entry.instance._id).join("\0") : "";
1032
+ return useEffect(() => {
1033
+ if (!evaluate) return;
1034
+ const ids = evaluationKey === "" ? [] : evaluationKey.split("\0");
1035
+ for (const id of ids) requestEvaluation(id);
1036
+ return () => {
1037
+ for (const id of ids) releaseEvaluation(id);
1038
+ };
1039
+ }, [ evaluate, evaluationKey, requestEvaluation, releaseEvaluation ]), {
1040
+ docId: docId,
1041
+ entries: list
1042
+ };
1043
+ }
1044
+
1045
+ function useHeldWorkflowEntry(instanceId) {
1046
+ const {entries: entries} = useWorkflowContext();
1047
+ return useSyncExternalStore(entries.subscribe, useCallback(() => instanceId ? entries.getInstance(instanceId) : void 0, [ entries, instanceId ]));
1048
+ }
1049
+
1050
+ function useWorkflowInstanceEntry(instanceId, options = {}) {
1051
+ const {requestInstance: requestInstance, releaseInstance: releaseInstance, requestEvaluation: requestEvaluation, releaseEvaluation: releaseEvaluation} = useWorkflowContext(), evaluate = options.evaluate ?? !0;
1052
+ return useEffect(() => {
1053
+ if (instanceId) return requestInstance(instanceId), () => releaseInstance(instanceId);
1054
+ }, [ instanceId, requestInstance, releaseInstance ]), useEffect(() => {
1055
+ if (!(!instanceId || !evaluate)) return requestEvaluation(instanceId), () => releaseEvaluation(instanceId);
1056
+ }, [ instanceId, evaluate, requestEvaluation, releaseEvaluation ]), useHeldWorkflowEntry(instanceId);
1057
+ }
1058
+
1059
+ const WorkflowToolOpened = defineEvent({
1060
+ name: "Editorial Workflows Studio Plugin Tool Opened",
1061
+ version: 1,
1062
+ description: "The Workflows tool was opened or its tab was switched"
1063
+ }), WorkflowDocumentViewOpened = defineEvent({
1064
+ name: "Editorial Workflows Studio Plugin Document View Opened",
1065
+ version: 1,
1066
+ description: "The Workflows document view was opened or its tab was switched"
1067
+ }), WorkflowInstanceDetailViewed = defineEvent({
1068
+ name: "Editorial Workflows Studio Plugin Instance Detail Viewed",
1069
+ version: 1,
1070
+ description: "The Workflows tool's per-instance detail view was opened"
1071
+ }), WorkflowFormStripClicked = defineEvent({
1072
+ name: "Editorial Workflows Studio Plugin Form Strip Clicked",
1073
+ version: 1,
1074
+ description: "The form strip's instance line was clicked, opening the document view"
1075
+ }), WorkflowStartDialogOpened = defineEvent({
1076
+ name: "Editorial Workflows Studio Plugin Start Dialog Opened",
1077
+ version: 1,
1078
+ description: "The start-workflow dialog was opened"
1079
+ }), WorkflowStartDialogSubmitted = defineEvent({
1080
+ name: "Editorial Workflows Studio Plugin Start Dialog Submitted",
1081
+ version: 1,
1082
+ description: "The start-workflow dialog's confirm was pressed"
1083
+ }), WorkflowAutoStartRan = defineEvent({
1084
+ name: "Editorial Workflows Studio Plugin Auto Start Ran",
1085
+ version: 1,
1086
+ description: "A fresh document's auto-start ran — one start request per configured workflow"
1087
+ }), WorkflowActionControlUsed = defineEvent({
1088
+ name: "Editorial Workflows Studio Plugin Action Control Used",
1089
+ version: 1,
1090
+ description: "An action-firing control was used, attributed to its UI surface"
1091
+ }), WorkflowActivityDialogOpened = defineEvent({
1092
+ name: "Editorial Workflows Studio Plugin Activity Dialog Opened",
1093
+ version: 1,
1094
+ description: "The activity detail dialog was opened"
1095
+ }), WorkflowTaskFiltersApplied = defineEvent({
1096
+ name: "Editorial Workflows Studio Plugin Task Filters Applied",
1097
+ version: 1,
1098
+ description: "The tool's task-filter menu closed with a changed selection"
1099
+ }), WorkflowTodoToggled = defineEvent({
1100
+ name: "Editorial Workflows Studio Plugin Todo Toggled",
1101
+ version: 1,
1102
+ description: "A todo checkbox was toggled, attributed to its UI surface and write seam"
1103
+ });
1104
+
1105
+ function useLogEventOnMount(event, data) {
1106
+ const telemetry = useWorkflowTelemetry(), logged = useRef(!1);
1107
+ useEffect(() => {
1108
+ logged.current || (logged.current = !0, telemetry.log(event, data));
1109
+ }, [ telemetry, event, data ]);
1110
+ }
1111
+
1112
+ function LogEventOnMount(props) {
1113
+ return useLogEventOnMount(props.event, props.data), null;
1114
+ }
1115
+
1065
1116
  function isEmptyValue(value) {
1066
1117
  return value == null || value === "" || Array.isArray(value) && value.length === 0;
1067
1118
  }
@@ -1104,8 +1155,12 @@ function memberIndex(members) {
1104
1155
 
1105
1156
  const selfUserFor = (id, me) => me != null && me.id === id ? me : void 0;
1106
1157
 
1158
+ function globalIdOf(users, projectScopedId) {
1159
+ return users.find(user => user.membership.id === projectScopedId)?.profile?.sanityUserId ?? projectScopedId;
1160
+ }
1161
+
1107
1162
  function userDisplayFor(args) {
1108
- const member = memberIndex(args.members).get(args.id), self = selfUserFor(args.id, args.me);
1163
+ const matchId = args.matchId ?? args.id, member = memberIndex(args.members).get(matchId), self = selfUserFor(matchId, args.me);
1109
1164
  return {
1110
1165
  id: args.id,
1111
1166
  name: member?.displayName ?? self?.name ?? args.id,
@@ -1114,28 +1169,52 @@ function userDisplayFor(args) {
1114
1169
  }
1115
1170
 
1116
1171
  function useUserDisplays(ids) {
1117
- const {members: members} = useProjectMembers(), me = useCurrentUser();
1172
+ const {members: members} = useProjectMembers(), {users: users} = useStudioProjectUsers(), meRaw = useCurrentUser(), me = useMemo(() => meRaw === null ? null : {
1173
+ ...meRaw,
1174
+ id: globalIdOf(users, meRaw.id)
1175
+ }, [ meRaw, users ]);
1118
1176
  return useMemo(() => ids.map(id => userDisplayFor({
1119
1177
  id: id,
1120
1178
  members: members,
1121
- me: me
1122
- })), [ ids, me, members ]);
1179
+ me: me,
1180
+ matchId: globalIdOf(users, id)
1181
+ })), [ ids, me, members, users ]);
1123
1182
  }
1124
1183
 
1125
1184
  function useUserDisplay(id) {
1126
- const {members: members} = useProjectMembers(), me = useCurrentUser();
1127
- return useMemo(() => userDisplayFor({
1185
+ const ids = useMemo(() => [ id ], [ id ]), [display] = useUserDisplays(ids);
1186
+ return display ?? {
1187
+ name: id,
1188
+ imageUrl: void 0
1189
+ };
1190
+ }
1191
+
1192
+ const warnedNotMember = /* @__PURE__ */ new Set;
1193
+
1194
+ function useBridgedSelf() {
1195
+ const me = useCurrentUser(), {users: users, loading: loading} = useStudioProjectUsers();
1196
+ if (!me || loading) return;
1197
+ 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);
1198
+ if (id === void 0) {
1199
+ 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.`));
1200
+ return;
1201
+ }
1202
+ return {
1128
1203
  id: id,
1129
- members: members,
1130
- me: me
1131
- }), [ id, me, members ]);
1204
+ roles: me.roles ?? []
1205
+ };
1206
+ }
1207
+
1208
+ function useSelfActor() {
1209
+ const self = useBridgedSelf();
1210
+ return self === void 0 ? void 0 : personActor(self);
1132
1211
  }
1133
1212
 
1134
1213
  function useAssignmentIdentity() {
1135
- const me = useCurrentUser();
1136
- if (me) return {
1137
- userId: me.id,
1138
- roles: (me.roles ?? []).map(r => r.name)
1214
+ const self = useBridgedSelf();
1215
+ if (self !== void 0) return {
1216
+ userId: self.id,
1217
+ roles: self.roles.map(r => r.name)
1139
1218
  };
1140
1219
  }
1141
1220
 
@@ -1144,16 +1223,13 @@ const projectMembersByUsers = /* @__PURE__ */ new WeakMap;
1144
1223
  function projectMembersFrom(users) {
1145
1224
  const cached = projectMembersByUsers.get(users);
1146
1225
  if (cached) return cached;
1147
- const members = users.map(({membership: membership, profile: profile}) => ({
1148
- id: membership.id,
1149
- displayName: profile?.displayName ?? membership.id,
1150
- ...profile?.email ? {
1151
- email: profile.email
1152
- } : {},
1153
- ...profile?.imageUrl ? {
1154
- imageUrl: profile.imageUrl
1155
- } : {},
1156
- roles: (membership.roles ?? []).map(role => role.name)
1226
+ const members = users.map(({membership: membership, profile: profile}) => projectMemberRow({
1227
+ membershipId: membership.id,
1228
+ roles: membership.roles,
1229
+ sanityUserId: profile?.sanityUserId,
1230
+ displayName: profile?.displayName,
1231
+ email: profile?.email,
1232
+ imageUrl: profile?.imageUrl ?? void 0
1157
1233
  }));
1158
1234
  return projectMembersByUsers.set(users, members), members;
1159
1235
  }
@@ -1481,12 +1557,12 @@ function MemberPicker({selectedIds: selectedIds, onSelect: onSelect, unassignRow
1481
1557
  });
1482
1558
  }
1483
1559
 
1484
- function AssigneePicker({value: value, onToggle: onToggle}) {
1560
+ function AssigneePicker({value: value, onChange: onChange}) {
1485
1561
  const state = useProjectMembers();
1486
1562
  /* @__PURE__ */
1487
1563
  return jsx(AssigneePicker$1, {
1488
1564
  ...state,
1489
- onToggle: onToggle,
1565
+ onChange: onChange,
1490
1566
  value: value
1491
1567
  });
1492
1568
  }
@@ -1816,7 +1892,7 @@ function useActionClusterPending() {
1816
1892
  }
1817
1893
 
1818
1894
  function useFireAction(args) {
1819
- const {instanceId: instanceId, activity: activity, action: action, label: label} = args, {fireActionFor: fireActionFor} = useWorkflowContext(), toast = useClosableToast(), cluster = useActionClusterLock(), [localPending, setLocalPending] = useState(!1), pending = localPending || cluster?.pending === !0;
1895
+ 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;
1820
1896
  return {
1821
1897
  fire: useCallback(async params => {
1822
1898
  if (pending) return !1;
@@ -1831,6 +1907,11 @@ function useFireAction(args) {
1831
1907
  }), toast.push({
1832
1908
  status: "success",
1833
1909
  title: `${label} done`
1910
+ }), telemetry.log(WorkflowActionControlUsed, {
1911
+ instanceId: instanceId,
1912
+ surface: surface,
1913
+ viaMenu: viaMenu,
1914
+ success: !0
1834
1915
  }), !0;
1835
1916
  } catch (err) {
1836
1917
  return console.error(`[workflow-studio-plugin] firing "${action}" on "${activity}" failed:`, err),
@@ -1838,11 +1919,16 @@ function useFireAction(args) {
1838
1919
  status: "error",
1839
1920
  title: `${label} failed`,
1840
1921
  description: describeError(err)
1922
+ }), telemetry.log(WorkflowActionControlUsed, {
1923
+ instanceId: instanceId,
1924
+ surface: surface,
1925
+ viaMenu: viaMenu,
1926
+ success: !1
1841
1927
  }), !1;
1842
1928
  } finally {
1843
1929
  setLocalPending(!1), cluster?.onFireChange(!1);
1844
1930
  }
1845
- }, [ fireActionFor, instanceId, activity, action, label, pending, toast, cluster ]),
1931
+ }, [ fireActionFor, instanceId, activity, action, label, pending, toast, cluster, telemetry, surface, viaMenu ]),
1846
1932
  pending: pending
1847
1933
  };
1848
1934
  }
@@ -1883,12 +1969,14 @@ function RowClickShield({active: active, children: children}) {
1883
1969
  });
1884
1970
  }
1885
1971
 
1886
- function FireButton({instanceId: instanceId, activity: activity, action: action, label: label, mode: mode = "default", chrome: chrome, tone: tone = "default", icon: icon}) {
1972
+ function FireButton({instanceId: instanceId, activity: activity, action: action, label: label, surface: surface, mode: mode = "default", chrome: chrome, tone: tone = "default", icon: icon}) {
1887
1973
  const {fire: fire, pending: pending} = useFireAction({
1888
1974
  instanceId: instanceId,
1889
1975
  activity: activity,
1890
1976
  action: action,
1891
- label: label
1977
+ label: label,
1978
+ surface: surface,
1979
+ viaMenu: !1
1892
1980
  }), handleClick = e => {
1893
1981
  chrome?.inButtonCard === !0 && e.stopPropagation(), !pending && fire();
1894
1982
  };
@@ -1909,7 +1997,7 @@ function FireButton({instanceId: instanceId, activity: activity, action: action,
1909
1997
  });
1910
1998
  }
1911
1999
 
1912
- function ActivityActionRow({actionEval: actionEval, instanceId: instanceId, activity: activity, chrome: chrome}) {
2000
+ function ActivityActionRow({actionEval: actionEval, instanceId: instanceId, activity: activity, surface: surface, chrome: chrome}) {
1913
2001
  const rowKind = actionRowKind(actionEval);
1914
2002
  return rowKind.kind === "disabled" ? /* @__PURE__ */ jsx(DisabledActionButton, {
1915
2003
  actionEval: actionEval,
@@ -1919,12 +2007,14 @@ function ActivityActionRow({actionEval: actionEval, instanceId: instanceId, acti
1919
2007
  actionEval: actionEval,
1920
2008
  instanceId: instanceId,
1921
2009
  activity: activity,
1922
- chrome: chrome
2010
+ chrome: chrome,
2011
+ surface: surface
1923
2012
  }) : /* @__PURE__ */ jsx(ParamsActionButton, {
1924
2013
  actionEval: actionEval,
1925
2014
  instanceId: instanceId,
1926
2015
  activity: activity,
1927
- chrome: chrome
2016
+ chrome: chrome,
2017
+ surface: surface
1928
2018
  });
1929
2019
  }
1930
2020
 
@@ -1949,7 +2039,7 @@ function DisabledActionButton({actionEval: actionEval, activity: activity, chrom
1949
2039
  }) : button;
1950
2040
  }
1951
2041
 
1952
- function PlainActionButton({actionEval: actionEval, instanceId: instanceId, activity: activity, chrome: chrome}) {
2042
+ function PlainActionButton({actionEval: actionEval, instanceId: instanceId, activity: activity, surface: surface, chrome: chrome}) {
1953
2043
  const {action: action} = actionEval, label = actionLabel(action), btn = actionButtonFace({
1954
2044
  actionEval: actionEval,
1955
2045
  activityName: activity
@@ -1961,6 +2051,7 @@ function PlainActionButton({actionEval: actionEval, instanceId: instanceId, acti
1961
2051
  mode: btn.mode,
1962
2052
  activity: activity,
1963
2053
  chrome: chrome,
2054
+ surface: surface,
1964
2055
  tone: btn.tone
1965
2056
  });
1966
2057
  return action.description ? /* @__PURE__ */ jsx(HoverHint, {
@@ -1969,12 +2060,14 @@ function PlainActionButton({actionEval: actionEval, instanceId: instanceId, acti
1969
2060
  }) : button;
1970
2061
  }
1971
2062
 
1972
- function ParamsActionDialog({actionEval: actionEval, instanceId: instanceId, activity: activity, onClose: onClose}) {
2063
+ function ParamsActionDialog({actionEval: actionEval, instanceId: instanceId, activity: activity, surface: surface, viaMenu: viaMenu, onClose: onClose}) {
1973
2064
  const {action: action} = actionEval, label = actionLabel(action), decls = action.params ?? [], [values, setValues] = useState({}), {fire: fire, pending: pending} = useFireAction({
1974
2065
  instanceId: instanceId,
1975
2066
  activity: activity,
1976
2067
  action: action.name,
1977
- label: label
2068
+ label: label,
2069
+ surface: surface,
2070
+ viaMenu: viaMenu
1978
2071
  }), built = buildParams(decls, values), confirm = async () => {
1979
2072
  await fire(built.params) && onClose();
1980
2073
  };
@@ -2029,7 +2122,7 @@ function ParamsActionDialog({actionEval: actionEval, instanceId: instanceId, act
2029
2122
  });
2030
2123
  }
2031
2124
 
2032
- function ParamsActionButton({actionEval: actionEval, instanceId: instanceId, activity: activity, chrome: chrome}) {
2125
+ function ParamsActionButton({actionEval: actionEval, instanceId: instanceId, activity: activity, surface: surface, chrome: chrome}) {
2033
2126
  const [open, setOpen] = useState(!1), btn = actionButtonFace({
2034
2127
  actionEval: actionEval,
2035
2128
  activityName: activity
@@ -2054,7 +2147,9 @@ function ParamsActionButton({actionEval: actionEval, instanceId: instanceId, act
2054
2147
  actionEval: actionEval,
2055
2148
  activity: activity,
2056
2149
  instanceId: instanceId,
2057
- onClose: () => setOpen(!1)
2150
+ onClose: () => setOpen(!1),
2151
+ surface: surface,
2152
+ viaMenu: !1
2058
2153
  })
2059
2154
  }) : null ]
2060
2155
  });
@@ -2348,27 +2443,10 @@ function useLocalDocRef(gdr) {
2348
2443
  }
2349
2444
 
2350
2445
  function useActualDocType(bareId) {
2351
- const client = useClient({
2352
- apiVersion: WORKFLOW_API_VERSION
2353
- }), [probe, setProbe] = useState();
2446
+ const {actualTypes: actualTypes} = useWorkflowContext();
2354
2447
  return useEffect(() => {
2355
- if (!bareId) return;
2356
- let cancelled = !1;
2357
- return client.fetch("*[_id == $id || _id == $draft || _id in path($version)][0]._type", {
2358
- id: bareId,
2359
- draft: `drafts.${bareId}`,
2360
- version: `versions.*.${bareId}`
2361
- }, {
2362
- perspective: "raw"
2363
- }).then(result => {
2364
- cancelled || setProbe({
2365
- id: bareId,
2366
- value: result
2367
- });
2368
- }).catch(() => {}), () => {
2369
- cancelled = !0;
2370
- };
2371
- }, [ client, bareId ]), probe !== void 0 && probe.id === bareId ? probe.value : void 0;
2448
+ if (bareId !== null) return actualTypes.track(bareId);
2449
+ }, [ actualTypes, bareId ]), useSyncExternalStore(actualTypes.subscribe, useCallback(() => bareId === null ? void 0 : actualTypes.read(bareId), [ actualTypes, bareId ]));
2372
2450
  }
2373
2451
 
2374
2452
  function useDocLink(gdr) {
@@ -2948,7 +3026,7 @@ function useFieldDraft(args) {
2948
3026
  }
2949
3027
 
2950
3028
  function EditableFieldControl({instanceId: instanceId, field: field}) {
2951
- const {editFieldFor: editFieldFor, previewFieldFor: previewFieldFor, discardFieldPreviewFor: discardFieldPreviewFor} = useWorkflowContext(), me = useCurrentUser(), held = useHeldWorkflowEntry(instanceId), entry = held ? resolveFieldEntry(held.instance, field) : void 0, evaluation = held?.evaluation, target = editTargetOf(field), consequence = fieldConsequence(evaluation, field.name), advanceTo = advanceTargetTitle(evaluation, field.name);
3029
+ 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);
2952
3030
  /* @__PURE__ */
2953
3031
  return jsx(EditableField, {
2954
3032
  field: field,
@@ -2984,7 +3062,7 @@ function EditableFieldControl({instanceId: instanceId, field: field}) {
2984
3062
  mode: "append",
2985
3063
  value: noteRow({
2986
3064
  body: body,
2987
- actor: me ? personActor(me) : void 0,
3065
+ actor: selfActor,
2988
3066
  at: /* @__PURE__ */ (new Date).toISOString()
2989
3067
  })
2990
3068
  }).then(() => {})
@@ -3119,7 +3197,10 @@ function AssigneeField({field: field, onSave: onSave, onUnset: onUnset}) {
3119
3197
  align: "center",
3120
3198
  children: /* @__PURE__ */ jsx(DismissablePopover, {
3121
3199
  content: /* @__PURE__ */ jsx(AssigneePicker, {
3122
- onToggle: pickAndClose,
3200
+ onChange: next => {
3201
+ const changed = changedAssignee(cur ? [ cur ] : [], next);
3202
+ changed && pickAndClose(changed);
3203
+ },
3123
3204
  value: cur ? [ cur ] : []
3124
3205
  }),
3125
3206
  onDismiss: () => setOpen(!1),
@@ -3644,11 +3725,11 @@ function AssignActivityControl({instanceId: instanceId, state: state, assigneeId
3644
3725
  setBusy(!0);
3645
3726
  try {
3646
3727
  selectedIds.has(memberId) ? await editFieldFor(instanceId, {
3647
- target: editTargetOf(field),
3728
+ target: editFieldTarget(field),
3648
3729
  mode: "set",
3649
3730
  value: members.filter(m => !(m.type === "user" && m.id === memberId))
3650
3731
  }) : await editFieldFor(instanceId, {
3651
- target: editTargetOf(field),
3732
+ target: editFieldTarget(field),
3652
3733
  mode: "append",
3653
3734
  value: {
3654
3735
  type: "user",
@@ -4077,8 +4158,8 @@ function TodoItemRowView({breadcrumb: breadcrumb, row: row, onToggle: onToggle,
4077
4158
  });
4078
4159
  }
4079
4160
 
4080
- function TodoItemsList({breadcrumb: breadcrumb, entry: entry, filter: filter}) {
4081
- const {instance: instance} = entry, definition = useDefinition(entry), {editFieldFor: editFieldFor, fireActionFor: fireActionFor} = useWorkflowContext(), 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 => {
4161
+ function TodoItemsList({breadcrumb: breadcrumb, entry: entry, filter: filter, surface: surface}) {
4162
+ 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 => {
4082
4163
  if (busy) return !1;
4083
4164
  setBusy(!0);
4084
4165
  try {
@@ -4103,15 +4184,35 @@ function TodoItemsList({breadcrumb: breadcrumb, entry: entry, filter: filter}) {
4103
4184
  mode: "set",
4104
4185
  value: next
4105
4186
  }));
4106
- }, toggleRow = row => {
4107
- if (row.editTarget) return patchItem(row, {
4108
- status: toggledTodoStatus(row.item)
4109
- });
4187
+ }, toggleRow = async row => {
4188
+ if (busy) return;
4189
+ if (row.editTarget) {
4190
+ const done = !isTodoDone(row.item), success = await patchItem(row, {
4191
+ status: toggledTodoStatus(row.item)
4192
+ }) === !0;
4193
+ telemetry.log(WorkflowTodoToggled, {
4194
+ instanceId: instance._id,
4195
+ surface: surface,
4196
+ via: "field-edit",
4197
+ done: done,
4198
+ success: success
4199
+ });
4200
+ return;
4201
+ }
4110
4202
  const tick = row.tick;
4111
- return tick && tick.actionEval.allowed && !isTodoDone(row.item) ? run(() => fireActionFor(instance._id, {
4112
- activity: tick.activity,
4113
- action: tick.actionEval.action.name
4114
- })) : Promise.resolve();
4203
+ if (tick && tick.actionEval.allowed && !isTodoDone(row.item)) {
4204
+ const success = await run(() => fireActionFor(instance._id, {
4205
+ activity: tick.activity,
4206
+ action: tick.actionEval.action.name
4207
+ }));
4208
+ telemetry.log(WorkflowTodoToggled, {
4209
+ instanceId: instance._id,
4210
+ surface: surface,
4211
+ via: "action",
4212
+ done: !0,
4213
+ success: success
4214
+ });
4215
+ }
4115
4216
  }, addItem = label => appendTarget ? run(() => editFieldFor(instance._id, {
4116
4217
  target: appendTarget,
4117
4218
  mode: "append",
@@ -4228,7 +4329,7 @@ const ACTIONS_MENU_TRIGGER = {
4228
4329
  padding: 2
4229
4330
  };
4230
4331
 
4231
- function ActionMenuItem({actionEval: actionEval, instanceId: instanceId, activity: activity, onCollectParams: onCollectParams}) {
4332
+ function ActionMenuItem({actionEval: actionEval, instanceId: instanceId, activity: activity, surface: surface, onCollectParams: onCollectParams}) {
4232
4333
  const rowKind = actionRowKind(actionEval), label = actionTriggerLabel(actionEval), {tone: tone, icon: icon} = actionButtonFace({
4233
4334
  actionEval: actionEval,
4234
4335
  activityName: activity
@@ -4238,7 +4339,9 @@ function ActionMenuItem({actionEval: actionEval, instanceId: instanceId, activit
4238
4339
  instanceId: instanceId,
4239
4340
  activity: activity,
4240
4341
  action: actionEval.action.name,
4241
- label: label
4342
+ label: label,
4343
+ surface: surface,
4344
+ viaMenu: !0
4242
4345
  });
4243
4346
  return rowKind.kind === "disabled" ? /* @__PURE__ */ jsx(MenuItem, {
4244
4347
  disabled: !0,
@@ -4274,7 +4377,7 @@ function rowShield(menuButton) {
4274
4377
  });
4275
4378
  }
4276
4379
 
4277
- function ActionsMenuButton({actions: actions, instanceId: instanceId, activity: activity, label: label, mode: mode = "ghost", inButtonCard: inButtonCard = !1}) {
4380
+ function ActionsMenuButton({actions: actions, instanceId: instanceId, activity: activity, label: label, surface: surface, mode: mode = "ghost", inButtonCard: inButtonCard = !1}) {
4278
4381
  const [paramsAction, setParamsAction] = useState(void 0), clusterPending = useActionClusterPending(), menuId = useId(), menuButton = /* @__PURE__ */ jsx(MenuButton, {
4279
4382
  button: /* @__PURE__ */ jsx(Button, {
4280
4383
  ...ACTIONS_MENU_TRIGGER,
@@ -4293,7 +4396,8 @@ function ActionsMenuButton({actions: actions, instanceId: instanceId, activity:
4293
4396
  actionEval: a,
4294
4397
  activity: activity,
4295
4398
  instanceId: instanceId,
4296
- onCollectParams: () => setParamsAction(a)
4399
+ onCollectParams: () => setParamsAction(a),
4400
+ surface: surface
4297
4401
  }, a.action.name))
4298
4402
  }),
4299
4403
  popover: {
@@ -4309,7 +4413,9 @@ function ActionsMenuButton({actions: actions, instanceId: instanceId, activity:
4309
4413
  actionEval: paramsAction,
4310
4414
  activity: activity,
4311
4415
  instanceId: instanceId,
4312
- onClose: () => setParamsAction(void 0)
4416
+ onClose: () => setParamsAction(void 0),
4417
+ surface: surface,
4418
+ viaMenu: !0
4313
4419
  })
4314
4420
  }) : null ]
4315
4421
  });
@@ -4325,7 +4431,8 @@ function TerminalFooter({actions: actions, activity: activity, instanceId: insta
4325
4431
  children: /* @__PURE__ */ jsx(ActivityActionRow, {
4326
4432
  actionEval: only,
4327
4433
  activity: activity,
4328
- instanceId: instanceId
4434
+ instanceId: instanceId,
4435
+ surface: "terminal-footer"
4329
4436
  })
4330
4437
  }) : /* @__PURE__ */ jsx(FittedActions, {
4331
4438
  actions: actions,
@@ -4379,13 +4486,15 @@ function FittedActions({actions: actions, activity: activity, instanceId: instan
4379
4486
  children: [ inline === void 0 ? null : actions.slice(0, inline).map(a => /* @__PURE__ */ jsx(ActivityActionRow, {
4380
4487
  actionEval: a,
4381
4488
  activity: activity,
4382
- instanceId: instanceId
4489
+ instanceId: instanceId,
4490
+ surface: "terminal-footer"
4383
4491
  }, a.action.name)), overflow.length > 0 ? /* @__PURE__ */ jsx(ActionsMenuButton, {
4384
4492
  actions: overflow,
4385
4493
  activity: activity,
4386
4494
  instanceId: instanceId,
4387
4495
  label: MORE_ACTIONS_LABEL,
4388
- mode: "default"
4496
+ mode: "default",
4497
+ surface: "terminal-footer"
4389
4498
  }) : null ]
4390
4499
  }) ]
4391
4500
  });
@@ -4428,7 +4537,7 @@ function MeasureRow({actions: actions, activity: activity, onElement: onElement}
4428
4537
  });
4429
4538
  }
4430
4539
 
4431
- function ActivityDetailDialog({entry: entry, activityName: activityName, breadcrumb: breadcrumb, definition: definition, document: document, onClose: onClose}) {
4540
+ function ActivityDetailDialog({entry: entry, activityName: activityName, breadcrumb: breadcrumb, definition: definition, document: document, source: source, onClose: onClose}) {
4432
4541
  const detail = deriveActivityDetail({
4433
4542
  entry: entry,
4434
4543
  activityName: activityName
@@ -4450,6 +4559,13 @@ function ActivityDetailDialog({entry: entry, activityName: activityName, breadcr
4450
4559
  onClose: onClose,
4451
4560
  width: 1,
4452
4561
  children: [
4562
+ /* @__PURE__ */ jsx(LogEventOnMount, {
4563
+ data: {
4564
+ instanceId: instanceId,
4565
+ source: source
4566
+ },
4567
+ event: WorkflowActivityDialogOpened
4568
+ }),
4453
4569
  /* @__PURE__ */ jsx(Card, {
4454
4570
  borderBottom: hasBody,
4455
4571
  style: {
@@ -4557,7 +4673,8 @@ function TopActions({detail: detail, definition: definition, instanceId: instanc
4557
4673
  children: [ p.actions.map(a => /* @__PURE__ */ jsx(ActivityActionRow, {
4558
4674
  actionEval: a,
4559
4675
  activity: activityName,
4560
- instanceId: instanceId
4676
+ instanceId: instanceId,
4677
+ surface: "dialog-strip"
4561
4678
  }, a.action.name)), p.manualTarget ? /* @__PURE__ */ jsx(Button, {
4562
4679
  as: "a",
4563
4680
  fontSize: 1,
@@ -4593,7 +4710,8 @@ function FieldRow({field: field, detail: detail, entry: entry, activityName: act
4593
4710
  scope: "activity",
4594
4711
  field: field.name,
4595
4712
  activity: activityName
4596
- }
4713
+ },
4714
+ surface: "activity-dialog"
4597
4715
  }) ]
4598
4716
  });
4599
4717
  if (field._type === "assignees") {
@@ -4746,29 +4864,7 @@ function GroupHeading({busy: busy, count: count, title: title, end: end}) {
4746
4864
  });
4747
4865
  }
4748
4866
 
4749
- function CopyDetailsButton({unreadable: unreadable}) {
4750
- const toast = useClosableToast();
4751
- /* @__PURE__ */
4752
- return jsx(Button, {
4753
- fontSize: 1,
4754
- mode: "ghost",
4755
- onClick: () => {
4756
- navigator.clipboard.writeText(unreadableDocsReport(unreadable)).then(() => toast.push({
4757
- status: "success",
4758
- title: "Details copied"
4759
- }), () => toast.push({
4760
- status: "error",
4761
- title: "Could not copy the details"
4762
- }));
4763
- },
4764
- padding: 2,
4765
- text: "Copy details"
4766
- });
4767
- }
4768
-
4769
- function UnreadableDocsNote({unreadable: unreadable}) {
4770
- if (unreadable.length === 0) return null;
4771
- const label = unreadable.length === 1 ? "1 workflow document isn’t listed — this Studio can’t read it. Share the details with your Studio maintainers." : `${unreadable.length} workflow documents aren’t listed — this Studio can’t read them. Share the details with your Studio maintainers.`;
4867
+ function CautionNote({action: action, label: label}) {
4772
4868
  /* @__PURE__ */
4773
4869
  return jsxs(Card, {
4774
4870
  padding: 2,
@@ -4795,10 +4891,39 @@ function UnreadableDocsNote({unreadable: unreadable}) {
4795
4891
  size: 1,
4796
4892
  children: label
4797
4893
  }) ]
4798
- }),
4799
- /* @__PURE__ */ jsx(CopyDetailsButton, {
4894
+ }), action ]
4895
+ });
4896
+ }
4897
+
4898
+ function CopyDetailsButton({unreadable: unreadable}) {
4899
+ const toast = useClosableToast();
4900
+ /* @__PURE__ */
4901
+ return jsx(Button, {
4902
+ fontSize: 1,
4903
+ mode: "ghost",
4904
+ onClick: () => {
4905
+ navigator.clipboard.writeText(unreadableDocsReport(unreadable)).then(() => toast.push({
4906
+ status: "success",
4907
+ title: "Details copied"
4908
+ }), () => toast.push({
4909
+ status: "error",
4910
+ title: "Could not copy the details"
4911
+ }));
4912
+ },
4913
+ padding: 2,
4914
+ text: "Copy details"
4915
+ });
4916
+ }
4917
+
4918
+ function UnreadableDocsNote({unreadable: unreadable}) {
4919
+ if (unreadable.length === 0) return null;
4920
+ const label = unreadable.length === 1 ? "1 workflow document isn’t listed — this Studio can’t read it. Share the details with your Studio maintainers." : `${unreadable.length} workflow documents aren’t listed — this Studio can’t read them. Share the details with your Studio maintainers.`;
4921
+ /* @__PURE__ */
4922
+ return jsx(CautionNote, {
4923
+ action: /* @__PURE__ */ jsx(CopyDetailsButton, {
4800
4924
  unreadable: unreadable
4801
- }) ]
4925
+ }),
4926
+ label: label
4802
4927
  });
4803
4928
  }
4804
4929
 
@@ -5120,6 +5245,25 @@ function buildStartRequest(args) {
5120
5245
  };
5121
5246
  }
5122
5247
 
5248
+ function sharedPreflightDataset(engine, tag) {
5249
+ let pending;
5250
+ return () => {
5251
+ if (pending !== void 0) return pending;
5252
+ const {query: query, params: params} = instancesQuery({
5253
+ tag: tag,
5254
+ filter: {
5255
+ includeCompleted: !0
5256
+ }
5257
+ });
5258
+ return pending = engine.query({
5259
+ groq: query,
5260
+ params: params
5261
+ }).then(rows => rows.map(assertReadableModel).map(projectStartSliceRow)).finally(() => {
5262
+ pending = void 0;
5263
+ }), pending;
5264
+ };
5265
+ }
5266
+
5123
5267
  function findResumeEntry(args) {
5124
5268
  return args.entries.find(e => isUnprimed(e.instance) && e.instance.definition === args.definitionName && (args.heldInstanceId === void 0 || e.instance._id === args.heldInstanceId));
5125
5269
  }
@@ -5135,7 +5279,8 @@ function classifyStartError(err) {
5135
5279
  instance: err.instance,
5136
5280
  description: describeError(err.cause)
5137
5281
  } : err instanceof StartNotAllowedError ? {
5138
- kind: "not-allowed"
5282
+ kind: "not-allowed",
5283
+ description: err.unmetRequirements.map(requirementCopy).join("; ")
5139
5284
  } : {
5140
5285
  kind: "failed",
5141
5286
  description: describeError(err)
@@ -5148,22 +5293,30 @@ function boundReleaseId(mapping, selectedReleaseId) {
5148
5293
 
5149
5294
  function startGate(args) {
5150
5295
  const {mapping: mapping, mappingIssue: mappingIssue, selectedReleaseId: selectedReleaseId, releaseActive: releaseActive} = args;
5151
- return mappingIssue !== void 0 ? {
5296
+ if (mappingIssue !== void 0) return {
5152
5297
  blocked: !0,
5153
5298
  tooltip: mappingIssue
5154
- } : mapping.perspectiveField?.required === !0 && selectedReleaseId === void 0 ? {
5299
+ };
5300
+ if (mapping.perspectiveField?.required === !0 && selectedReleaseId === void 0) return {
5155
5301
  blocked: !0,
5156
5302
  tooltip: "Pick a release in the perspective dropdown first"
5157
- } : mapping.perspectiveField !== void 0 && selectedReleaseId !== void 0 && !releaseActive ? {
5303
+ };
5304
+ if (mapping.perspectiveField !== void 0 && selectedReleaseId !== void 0 && !releaseActive) return {
5158
5305
  blocked: !0,
5159
5306
  tooltip: `Release "${selectedReleaseId}" is not active — only active releases can host new workflows`
5160
- } : args.startFilterFailed === !0 ? {
5307
+ };
5308
+ if (args.startFilterFailed === !0) return {
5161
5309
  blocked: !0,
5162
5310
  tooltip: `${mapping.label} can't start for this document right now — the workflow's start condition isn't met`
5163
- } : args.startAllowedRefused === !0 ? {
5164
- blocked: !0,
5165
- tooltip: `${mapping.label} can't be started for this document right now — the workflow doesn't allow another run with these inputs`
5166
- } : {
5311
+ };
5312
+ if (args.unmetRequirement !== void 0) {
5313
+ const requirement = args.unmetRequirement;
5314
+ return {
5315
+ blocked: !0,
5316
+ tooltip: requirementCopy(requirement)
5317
+ };
5318
+ }
5319
+ return {
5167
5320
  blocked: !1,
5168
5321
  tooltip: void 0
5169
5322
  };
@@ -5186,10 +5339,10 @@ async function preflightFilter(args) {
5186
5339
  });
5187
5340
  }
5188
5341
 
5189
- async function preflightAllowed(args) {
5190
- const {allowed: allowed, mapping: mapping, fields: fields, scope: scope} = args;
5191
- return unboundAllowedReads(allowed, fields).length > 0 ? !1 : (await explainStartAllowed({
5192
- allowed: allowed,
5342
+ async function preflightGroqRequirement(args) {
5343
+ const {query: query, mapping: mapping, fields: fields, scope: scope} = args;
5344
+ return unboundRequirementReads(query, fields).length > 0 ? !1 : (await explainStartRequirement({
5345
+ query: query,
5193
5346
  definition: {
5194
5347
  name: mapping.definition
5195
5348
  },
@@ -5198,6 +5351,25 @@ async function preflightAllowed(args) {
5198
5351
  })).outcome === "unsatisfied";
5199
5352
  }
5200
5353
 
5354
+ async function preflightSingleSubjectRequirement(args) {
5355
+ const {mapping: mapping, start: start, declaredFields: declaredFields, fields: fields, scope: scope} = args;
5356
+ return singleSubjectRequirementRefused({
5357
+ definition: {
5358
+ name: mapping.definition,
5359
+ ...declaredFields !== void 0 ? {
5360
+ fields: [ ...declaredFields ]
5361
+ } : {},
5362
+ start: start
5363
+ },
5364
+ fields: fields,
5365
+ scope: scope
5366
+ });
5367
+ }
5368
+
5369
+ function requirementCopy(requirement) {
5370
+ return requirement.description ?? requirement.title ?? humanize(requirement.name);
5371
+ }
5372
+
5201
5373
  async function performStart(args) {
5202
5374
  const {mapping: mapping, docId: docId, initialValue: initialValue, selectedReleaseId: selectedReleaseId} = args, releaseId = boundReleaseId(mapping, selectedReleaseId);
5203
5375
  try {
@@ -5224,7 +5396,21 @@ async function performStart(args) {
5224
5396
  }
5225
5397
 
5226
5398
  function useStartVerdicts(args) {
5227
- const {mapping: mapping, start: start, docId: docId, initialValue: initialValue, selectedReleaseId: selectedReleaseId} = args, skip = args.hidden || args.resumable;
5399
+ const {mapping: mapping, start: start, docId: docId, initialValue: initialValue, selectedReleaseId: selectedReleaseId, entries: entries} = args, {engine: engine, binding: binding} = useWorkflowContext(), fetchDataset = useMemo(() => sharedPreflightDataset(engine, binding.tag), [ engine, binding.tag ]), skip = args.hidden || args.resumable, groqRequirements = useMemo(() => start?.requirements?.filter(requirement => requirement.type === "groq") ?? [], [ start?.requirements ]), refusedGroq = useGroqRequirementVerdicts({
5400
+ mapping: mapping,
5401
+ requirements: groqRequirements,
5402
+ docId: docId,
5403
+ selectedReleaseId: selectedReleaseId,
5404
+ skip: skip,
5405
+ fetchDataset: fetchDataset
5406
+ }), singleSubjectRefused = useSingleSubjectVerdict({
5407
+ mapping: mapping,
5408
+ start: start,
5409
+ docId: docId,
5410
+ selectedReleaseId: selectedReleaseId,
5411
+ entries: entries,
5412
+ skip: skip
5413
+ }), unmetRequirement = start?.requirements?.find(requirement => requirement.type === "singleSubject" ? singleSubjectRefused : refusedGroq.includes(requirement.name));
5228
5414
  return {
5229
5415
  filterFailed: useFilterVerdict({
5230
5416
  mapping: mapping,
@@ -5232,25 +5418,15 @@ function useStartVerdicts(args) {
5232
5418
  docId: docId,
5233
5419
  initialValue: initialValue,
5234
5420
  selectedReleaseId: selectedReleaseId,
5235
- skip: skip
5421
+ skip: skip,
5422
+ fetchDataset: fetchDataset
5236
5423
  }),
5237
- allowedRefused: useAllowedVerdict({
5238
- mapping: mapping,
5239
- allowed: start?.allowed,
5240
- docId: docId,
5241
- selectedReleaseId: selectedReleaseId,
5242
- skip: skip
5243
- })
5424
+ unmetRequirement: unmetRequirement
5244
5425
  };
5245
5426
  }
5246
5427
 
5247
5428
  function preflightScope(args) {
5248
- const {engine: engine, tag: tag, mapping: mapping, contentResource: contentResource, docId: docId} = args, {query: query, params: params} = instancesQuery({
5249
- tag: tag,
5250
- filter: {
5251
- includeCompleted: !0
5252
- }
5253
- });
5429
+ const {tag: tag, mapping: mapping, contentResource: contentResource, docId: docId, fetchDataset: fetchDataset} = args;
5254
5430
  return {
5255
5431
  tag: tag,
5256
5432
  now: /* @__PURE__ */ (new Date).toISOString(),
@@ -5259,15 +5435,12 @@ function preflightScope(args) {
5259
5435
  contentResource: contentResource,
5260
5436
  documentId: docId
5261
5437
  }).id,
5262
- fetchDataset: async () => (await engine.query({
5263
- groq: query,
5264
- params: params
5265
- })).map(assertReadableModel)
5438
+ fetchDataset: fetchDataset
5266
5439
  };
5267
5440
  }
5268
5441
 
5269
5442
  function useFilterVerdict(args) {
5270
- const {mapping: mapping, filter: filter, docId: docId, initialValue: initialValue, selectedReleaseId: selectedReleaseId, skip: skip} = args, {engine: engine, binding: binding} = useWorkflowContext(), [failed, setFailed] = useState(!1), rootRead = filter !== void 0 && readsRootDocument(filter), candidateValue = rootRead ? initialValue : null;
5443
+ const {mapping: mapping, filter: filter, docId: docId, initialValue: initialValue, selectedReleaseId: selectedReleaseId, skip: skip, fetchDataset: fetchDataset} = args, {binding: binding} = useWorkflowContext(), [failed, setFailed] = useState(!1), rootRead = filter !== void 0 && readsRootDocument(filter), candidateValue = rootRead ? initialValue : null;
5271
5444
  return useEffect(() => {
5272
5445
  if (skip || filter === void 0 || docId === void 0) {
5273
5446
  setFailed(!1);
@@ -5281,57 +5454,112 @@ function useFilterVerdict(args) {
5281
5454
  candidateValue: candidateValue,
5282
5455
  selectedReleaseId: selectedReleaseId,
5283
5456
  scope: preflightScope({
5284
- engine: engine,
5285
5457
  tag: binding.tag,
5286
5458
  mapping: mapping,
5287
5459
  contentResource: binding.contentResource,
5288
- docId: docId
5460
+ docId: docId,
5461
+ fetchDataset: fetchDataset
5289
5462
  })
5290
5463
  }).catch(showRowOnError("start.filter", mapping)).then(verdict => {
5291
5464
  cancelled || setFailed(verdict);
5292
5465
  }), () => {
5293
5466
  cancelled = !0;
5294
5467
  };
5295
- }, [ filter, rootRead, skip, docId, selectedReleaseId, candidateValue, mapping, engine, binding ]),
5468
+ }, [ filter, rootRead, skip, docId, selectedReleaseId, candidateValue, mapping, binding, fetchDataset ]),
5296
5469
  failed;
5297
5470
  }
5298
5471
 
5299
- function useAllowedVerdict(args) {
5300
- const {mapping: mapping, allowed: allowed, docId: docId, selectedReleaseId: selectedReleaseId, skip: skip} = args, {engine: engine, binding: binding, mappingFields: mappingFields} = useWorkflowContext(), [refused, setRefused] = useState(!1);
5472
+ function preflightSeedFields(args) {
5473
+ const ctx = {
5474
+ mapping: args.mapping,
5475
+ subjectDocId: args.docId,
5476
+ selectedReleaseId: args.selectedReleaseId,
5477
+ contentResource: args.contentResource,
5478
+ declaredFields: args.declaredFields
5479
+ };
5480
+ return Object.fromEntries(buildStartFields({
5481
+ ctx: ctx,
5482
+ initFields: [],
5483
+ values: {}
5484
+ }).map(field => [ field.name, field.value ]));
5485
+ }
5486
+
5487
+ function useGroqRequirementVerdicts(args) {
5488
+ const {mapping: mapping, requirements: requirements, docId: docId, selectedReleaseId: selectedReleaseId, skip: skip, fetchDataset: fetchDataset} = args, {binding: binding, mappingFields: mappingFields} = useWorkflowContext(), [refused, setRefused] = useState([]);
5301
5489
  return useEffect(() => {
5302
- if (skip || allowed === void 0 || docId === void 0) {
5303
- setRefused(!1);
5490
+ if (skip || requirements.length === 0 || docId === void 0) {
5491
+ setRefused(current => current.length === 0 ? current : []);
5304
5492
  return;
5305
5493
  }
5306
5494
  let cancelled = !1;
5307
- const ctx = {
5495
+ const fields = preflightSeedFields({
5308
5496
  mapping: mapping,
5309
- subjectDocId: docId,
5497
+ docId: docId,
5310
5498
  selectedReleaseId: selectedReleaseId,
5311
5499
  contentResource: binding.contentResource,
5312
5500
  declaredFields: mappingFields.get(mappingKey(mapping))
5313
- }, fields = Object.fromEntries(buildStartFields({
5314
- ctx: ctx,
5315
- initFields: [],
5316
- values: {}
5317
- }).map(field => [ field.name, field.value ]));
5318
- return preflightAllowed({
5319
- allowed: allowed,
5501
+ }), scope = preflightScope({
5502
+ tag: binding.tag,
5320
5503
  mapping: mapping,
5321
- fields: fields,
5504
+ contentResource: binding.contentResource,
5505
+ docId: docId,
5506
+ fetchDataset: fetchDataset
5507
+ });
5508
+ return Promise.all(requirements.map(async requirement => ({
5509
+ name: requirement.name,
5510
+ refused: await preflightGroqRequirement({
5511
+ query: requirement.query,
5512
+ mapping: mapping,
5513
+ fields: fields,
5514
+ scope: scope
5515
+ }).catch(showRowOnError(`start.requirements.${requirement.name}`, mapping))
5516
+ }))).then(verdicts => {
5517
+ if (!cancelled) {
5518
+ const next = verdicts.filter(verdict => verdict.refused).map(verdict => verdict.name);
5519
+ setRefused(current => current.length === next.length && current.every((name, index) => name === next[index]) ? current : next);
5520
+ }
5521
+ }), () => {
5522
+ cancelled = !0;
5523
+ };
5524
+ }, [ requirements, skip, docId, selectedReleaseId, mapping, binding, mappingFields, fetchDataset ]),
5525
+ refused;
5526
+ }
5527
+
5528
+ function useSingleSubjectVerdict(args) {
5529
+ const {mapping: mapping, start: start, docId: docId, selectedReleaseId: selectedReleaseId, entries: entries, skip: skip} = args, {binding: binding, mappingFields: mappingFields} = useWorkflowContext(), [refused, setRefused] = useState(!1), dedupe = start !== void 0 && hasSingleSubjectRequirement({
5530
+ start: start
5531
+ });
5532
+ return useEffect(() => {
5533
+ if (skip || !dedupe || start === void 0 || docId === void 0) {
5534
+ setRefused(!1);
5535
+ return;
5536
+ }
5537
+ let cancelled = !1;
5538
+ const declaredFields = mappingFields.get(mappingKey(mapping));
5539
+ return preflightSingleSubjectRequirement({
5540
+ mapping: mapping,
5541
+ start: start,
5542
+ declaredFields: declaredFields,
5543
+ fields: preflightSeedFields({
5544
+ mapping: mapping,
5545
+ docId: docId,
5546
+ selectedReleaseId: selectedReleaseId,
5547
+ contentResource: binding.contentResource,
5548
+ declaredFields: declaredFields
5549
+ }),
5322
5550
  scope: preflightScope({
5323
- engine: engine,
5324
5551
  tag: binding.tag,
5325
5552
  mapping: mapping,
5553
+ docId: docId,
5326
5554
  contentResource: binding.contentResource,
5327
- docId: docId
5555
+ fetchDataset: () => Promise.resolve(entries.map(({instance: instance}) => projectStartSliceRow(instance)))
5328
5556
  })
5329
- }).catch(showRowOnError("start.allowed", mapping)).then(verdict => {
5557
+ }).catch(showRowOnError("singleSubject requirement", mapping)).then(verdict => {
5330
5558
  cancelled || setRefused(verdict);
5331
5559
  }), () => {
5332
5560
  cancelled = !0;
5333
5561
  };
5334
- }, [ allowed, skip, docId, selectedReleaseId, mapping, engine, binding, mappingFields ]),
5562
+ }, [ dedupe, start, skip, docId, selectedReleaseId, entries, mapping, binding, mappingFields ]),
5335
5563
  refused;
5336
5564
  }
5337
5565
 
@@ -5341,7 +5569,7 @@ function showRowOnError(key, mapping) {
5341
5569
  }
5342
5570
 
5343
5571
  function useStartWorkflow(args) {
5344
- const {mapping: mapping, docId: docId, initialValue: initialValue} = args, toast = useClosableToast(), {engine: engine, openStartDialog: openStartDialog, mappingIssues: mappingIssues2, mappingStarts: mappingStarts} = useWorkflowContext(), observer = useStudioObserver({
5572
+ const {mapping: mapping, docId: docId, initialValue: initialValue, source: source} = args, toast = useClosableToast(), {engine: engine, openStartDialog: openStartDialog, mappingIssues: mappingIssues2, mappingStarts: mappingStarts} = useWorkflowContext(), observer = useStudioObserver({
5345
5573
  engine: engine
5346
5574
  }), [starting, setStarting] = useState(!1), {selectedReleaseId: selectedReleaseId} = usePerspective(), releaseActive = useIsReleaseActive(), startBlock = mappingStarts.get(mappingKey(mapping)), hidden = startKindOf({
5347
5575
  start: startBlock
@@ -5349,12 +5577,13 @@ function useStartWorkflow(args) {
5349
5577
  entries: entries,
5350
5578
  definitionName: mapping.definition,
5351
5579
  heldInstanceId: void 0
5352
- }) !== void 0, {filterFailed: filterFailed, allowedRefused: allowedRefused} = useStartVerdicts({
5580
+ }) !== void 0, {filterFailed: filterFailed, unmetRequirement: unmetRequirement} = useStartVerdicts({
5353
5581
  mapping: mapping,
5354
5582
  start: startBlock,
5355
5583
  docId: docId,
5356
5584
  initialValue: initialValue,
5357
5585
  selectedReleaseId: selectedReleaseId,
5586
+ entries: entries,
5358
5587
  hidden: hidden,
5359
5588
  resumable: resumable
5360
5589
  }), {blocked: blocked, tooltip: tooltip} = startGate({
@@ -5363,7 +5592,9 @@ function useStartWorkflow(args) {
5363
5592
  selectedReleaseId: selectedReleaseId,
5364
5593
  releaseActive: releaseActive,
5365
5594
  startFilterFailed: filterFailed,
5366
- startAllowedRefused: allowedRefused
5595
+ ...unmetRequirement !== void 0 ? {
5596
+ unmetRequirement: unmetRequirement
5597
+ } : {}
5367
5598
  });
5368
5599
  return {
5369
5600
  blocked: blocked,
@@ -5385,7 +5616,10 @@ function useStartWorkflow(args) {
5385
5616
  title: "Could not persist the document before starting",
5386
5617
  description: describeError(err)
5387
5618
  }),
5388
- openStartDialog: openStartDialog
5619
+ openStartDialog: request => openStartDialog({
5620
+ ...request,
5621
+ source: source
5622
+ })
5389
5623
  });
5390
5624
  } finally {
5391
5625
  setStarting(!1);
@@ -5395,11 +5629,12 @@ function useStartWorkflow(args) {
5395
5629
  };
5396
5630
  }
5397
5631
 
5398
- function StartWorkflowButton({mapping: mapping, docId: docId, initialValue: initialValue, mode: mode = "default", label: label, iconOnly: iconOnly = !1, tone: tone = "primary"}) {
5632
+ function StartWorkflowButton({mapping: mapping, docId: docId, initialValue: initialValue, source: source, mode: mode = "default", label: label, iconOnly: iconOnly = !1, tone: tone = "primary"}) {
5399
5633
  const {blocked: blocked, tooltip: tooltip, hidden: hidden, starting: starting, start: start} = useStartWorkflow({
5400
5634
  mapping: mapping,
5401
5635
  docId: docId,
5402
- initialValue: initialValue
5636
+ initialValue: initialValue,
5637
+ source: source
5403
5638
  });
5404
5639
  if (hidden) return null;
5405
5640
  const startLabel = label ?? `Start ${mapping.label}`, button = /* @__PURE__ */ jsx(Button, {
@@ -5432,7 +5667,7 @@ function useStartableMappings(mappings) {
5432
5667
  }) !== "autonomous");
5433
5668
  }
5434
5669
 
5435
- function StartWorkflowControl({mappings: mappings, docId: docId, initialValue: initialValue, label: label, iconOnly: iconOnly = !1, mode: mode = "default", tone: tone = "primary"}) {
5670
+ function StartWorkflowControl({mappings: mappings, docId: docId, initialValue: initialValue, source: source, label: label, iconOnly: iconOnly = !1, mode: mode = "default", tone: tone = "primary"}) {
5436
5671
  const menuId = useId(), startable = useStartableMappings(mappings), [first] = startable;
5437
5672
  if (first === void 0) return null;
5438
5673
  if (startable.length === 1) /* @__PURE__ */
@@ -5445,6 +5680,7 @@ function StartWorkflowControl({mappings: mappings, docId: docId, initialValue: i
5445
5680
  },
5446
5681
  mapping: first,
5447
5682
  mode: mode,
5683
+ source: source,
5448
5684
  tone: tone
5449
5685
  });
5450
5686
  const menuLabel = label ?? "Start a workflow";
@@ -5470,7 +5706,8 @@ function StartWorkflowControl({mappings: mappings, docId: docId, initialValue: i
5470
5706
  children: startable.map(mapping => /* @__PURE__ */ jsx(StartWorkflowMenuItem, {
5471
5707
  docId: docId,
5472
5708
  initialValue: initialValue,
5473
- mapping: mapping
5709
+ mapping: mapping,
5710
+ source: source
5474
5711
  }, mappingKey(mapping)))
5475
5712
  }),
5476
5713
  popover: {
@@ -5481,11 +5718,12 @@ function StartWorkflowControl({mappings: mappings, docId: docId, initialValue: i
5481
5718
  });
5482
5719
  }
5483
5720
 
5484
- function StartWorkflowMenuItem({mapping: mapping, docId: docId, initialValue: initialValue}) {
5721
+ function StartWorkflowMenuItem({mapping: mapping, docId: docId, initialValue: initialValue, source: source}) {
5485
5722
  const {blocked: blocked, tooltip: tooltip, starting: starting, start: start} = useStartWorkflow({
5486
5723
  mapping: mapping,
5487
5724
  docId: docId,
5488
- initialValue: initialValue
5725
+ initialValue: initialValue,
5726
+ source: source
5489
5727
  }), item = /* @__PURE__ */ jsx(MenuItem, {
5490
5728
  disabled: blocked || starting || !docId,
5491
5729
  onClick: () => {
@@ -5540,13 +5778,15 @@ function RowTerminalActions({actions: actions, activity: activity, instanceId: i
5540
5778
  mode: "ghost",
5541
5779
  inButtonCard: !0
5542
5780
  },
5543
- instanceId: instanceId
5781
+ instanceId: instanceId,
5782
+ surface: "inline-row"
5544
5783
  }) : /* @__PURE__ */ jsx(ActionsMenuButton, {
5545
5784
  actions: actions,
5546
5785
  activity: activity,
5547
5786
  inButtonCard: !0,
5548
5787
  instanceId: instanceId,
5549
- label: "Select action"
5788
+ label: "Select action",
5789
+ surface: "inline-row"
5550
5790
  })
5551
5791
  }) : null;
5552
5792
  }
@@ -5644,7 +5884,8 @@ function ForMeList({entry: entry, work: work, identity: identity, onOpenActivity
5644
5884
  filter: {
5645
5885
  kind: "mine",
5646
5886
  identity: identity
5647
- }
5887
+ },
5888
+ surface: "document-view-for-me"
5648
5889
  }) : null ]
5649
5890
  })
5650
5891
  });
@@ -5853,7 +6094,8 @@ function TodoFieldDialog({entry: entry, scope: scope, field: field, title: title
5853
6094
  kind: "field",
5854
6095
  scope: scope,
5855
6096
  field: field
5856
- }
6097
+ },
6098
+ surface: "todo-field-dialog"
5857
6099
  })
5858
6100
  })
5859
6101
  });
@@ -6066,7 +6308,7 @@ function PillButton({children: children, pill: pill, hintDisabled: hintDisabled
6066
6308
  }
6067
6309
 
6068
6310
  function useFieldCommit({entry: entry, editability: editability}) {
6069
- const {editFieldFor: editFieldFor} = useWorkflowContext(), target = editTargetOf(editability);
6311
+ const {editFieldFor: editFieldFor} = useWorkflowContext(), target = editFieldTarget(editability);
6070
6312
  return {
6071
6313
  set: value => editFieldFor(entry.instance._id, {
6072
6314
  target: target,
@@ -6282,8 +6524,9 @@ function AssigneePill({entry: entry, pill: pill, editability: editability}) {
6282
6524
  /* @__PURE__ */
6283
6525
  return jsx(PillPopover, {
6284
6526
  content: /* @__PURE__ */ jsx(AssigneePicker, {
6285
- onToggle: assignee => {
6286
- setOpen(!1), pick(assignee);
6527
+ onChange: next => {
6528
+ const changed = changedAssignee(current ? [ current ] : [], next);
6529
+ changed && (setOpen(!1), pick(changed));
6287
6530
  },
6288
6531
  value: current ? [ current ] : []
6289
6532
  }),
@@ -6302,10 +6545,8 @@ function AssigneesPill({entry: entry, pill: pill, editability: editability}) {
6302
6545
  /* @__PURE__ */
6303
6546
  return jsx(PillPopover, {
6304
6547
  content: /* @__PURE__ */ jsx(AssigneePicker, {
6305
- onToggle: assignee => {
6306
- if (saving) return;
6307
- const next = current.some(a => sameAssignee(a, assignee)) ? current.filter(a => !sameAssignee(a, assignee)) : [ ...current, assignee ];
6308
- save(() => set(next));
6548
+ onChange: next => {
6549
+ saving || save(() => set(next));
6309
6550
  },
6310
6551
  value: current
6311
6552
  }),
@@ -6410,15 +6651,15 @@ function Hairline({weight: weight, style: style}) {
6410
6651
  });
6411
6652
  }
6412
6653
 
6413
- function CollapsibleBand({header: header, children: children, background: background = !1, defaultOpen: defaultOpen = !0, sticky: sticky = !1, title: title}) {
6414
- const [open, setOpen] = useState(defaultOpen), verb = open ? "Collapse" : "Expand";
6654
+ function CollapsibleBand({header: header, children: children, background: background = !1, defaultOpen: defaultOpen = !0, onToggle: onToggle, open: controlledOpen, sticky: sticky = !1, title: title}) {
6655
+ const [heldOpen, setHeldOpen] = useState(defaultOpen), open = controlledOpen ?? heldOpen, toggle = onToggle ?? (() => setHeldOpen(value => !value)), verb = open ? "Collapse" : "Expand";
6415
6656
  /* @__PURE__ */
6416
6657
  return jsxs(Stack, {
6417
6658
  gap: 1,
6418
6659
  children: [
6419
6660
  /* @__PURE__ */ jsx(Card, {
6420
6661
  onDoubleClick: event => {
6421
- event.target instanceof Element && event.target.closest("a, button") || setOpen(v => !v);
6662
+ event.target instanceof Element && event.target.closest("a, button") || toggle();
6422
6663
  },
6423
6664
  padding: 1,
6424
6665
  radius: 3,
@@ -6439,7 +6680,7 @@ function CollapsibleBand({header: header, children: children, background: backgr
6439
6680
  fontSize: 1,
6440
6681
  icon: open ? ChevronDownIcon : ChevronRightIcon,
6441
6682
  mode: "bleed",
6442
- onClick: () => setOpen(v => !v),
6683
+ onClick: toggle,
6443
6684
  padding: 2
6444
6685
  }), header ]
6445
6686
  })
@@ -7350,7 +7591,12 @@ function StageSection({entry: entry, onOpenActivity: onOpenActivity}) {
7350
7591
  }
7351
7592
 
7352
7593
  const VIEW_TAB_CODEC = workflowsTabCodec([ "overview", "for-me" ]), WorkflowsView = props => {
7353
- const {documentId: documentId, schemaType: schemaType} = props, {isDocResolved: isDocResolved, mappings: mappings, discoveryInvalid: discoveryInvalid} = useWorkflowContext(), {entries: entries} = useWorkflowsForDocument(documentId), resolved = isDocResolved(documentId), docTypeMappings = mappingsForDocType(mappings, schemaType.name);
7594
+ const {documentId: documentId, schemaType: schemaType} = props, {isDocResolved: isDocResolved, mappings: mappings, discoveryInvalid: discoveryInvalid} = useWorkflowContext(), {entries: entries} = useWorkflowsForDocument(documentId), {view: view} = useWorkflowsTab();
7595
+ useLogEventOnMount(WorkflowDocumentViewOpened, {
7596
+ tab: view,
7597
+ via: "open"
7598
+ });
7599
+ const resolved = isDocResolved(documentId), docTypeMappings = mappingsForDocType(mappings, schemaType.name);
7354
7600
  if (entries.length === 0) return discoveryInvalid !== void 0 ? /* @__PURE__ */ jsx(Box, {
7355
7601
  padding: 4,
7356
7602
  children: /* @__PURE__ */ jsx(InvalidDocNotice, {
@@ -7428,7 +7674,7 @@ function useOpenActivity(entries) {
7428
7674
  }
7429
7675
 
7430
7676
  function WorkflowsPanel({entries: entries, documentId: documentId, docTypeMappings: docTypeMappings, initialValue: initialValue}) {
7431
- const identity = useAssignmentIdentity(), panelId = useId(), {params: params} = usePaneRouter(), {view: view, setView: setView} = useWorkflowsTab(), {openActivity: openActivity, setOpenActivity: setOpenActivity, dialogEntry: dialogEntry} = useOpenActivity(entries), active = entries.filter(isLiveEntry), finished = entries.filter(e => !isLiveEntry(e)), forMe = forMeWorkOf(active, identity), [landing] = useState(() => focusedLanding(params?.[WORKFLOWS_FOCUS_PARAM], entries.map(e => e.instance._id))), assignedWorkUnknown = active.some(e => e.invalid !== void 0), booting = useDelayedFlag(active.some(e => !e.ready && e.invalid === void 0), BOOT_CUE_DELAY_MS), sectionFor = e => /* @__PURE__ */ jsx(WorkflowInstanceSection, {
7677
+ const identity = useAssignmentIdentity(), panelId = useId(), {params: params} = usePaneRouter(), {view: view, setView: setView} = useWorkflowsTab(), telemetry = useWorkflowTelemetry(), {openActivity: openActivity, setOpenActivity: setOpenActivity, dialogEntry: dialogEntry} = useOpenActivity(entries), active = entries.filter(isLiveEntry), finished = entries.filter(e => !isLiveEntry(e)), forMe = forMeWorkOf(active, identity), [landing] = useState(() => focusedLanding(params?.[WORKFLOWS_FOCUS_PARAM], entries.map(e => e.instance._id))), assignedWorkUnknown = active.some(e => e.invalid !== void 0), booting = useDelayedFlag(active.some(e => !e.ready && e.invalid === void 0), BOOT_CUE_DELAY_MS), sectionFor = e => /* @__PURE__ */ jsx(WorkflowInstanceSection, {
7432
7678
  defaultOpen: landingDefaultOpen(landing, e.instance._id),
7433
7679
  entry: e,
7434
7680
  onOpenActivity: activityName => setOpenActivity({
@@ -7462,6 +7708,7 @@ function WorkflowsPanel({entries: entries, documentId: documentId, docTypeMappin
7462
7708
  initialValue: initialValue,
7463
7709
  mappings: docTypeMappings,
7464
7710
  mode: "bleed",
7711
+ source: "document-view-header",
7465
7712
  tone: "default"
7466
7713
  })
7467
7714
  }),
@@ -7470,7 +7717,12 @@ function WorkflowsPanel({entries: entries, documentId: documentId, docTypeMappin
7470
7717
  /* @__PURE__ */ jsx(TabSwitch, {
7471
7718
  ariaControls: `${panelId}-panel`,
7472
7719
  idPrefix: panelId,
7473
- onSelect: setView,
7720
+ onSelect: next => {
7721
+ next !== view && telemetry.log(WorkflowDocumentViewOpened, {
7722
+ tab: next,
7723
+ via: "tab-switch"
7724
+ }), setView(next);
7725
+ },
7474
7726
  options: [ {
7475
7727
  value: "overview",
7476
7728
  label: "Overview"
@@ -7544,6 +7796,7 @@ function OverviewEmptyState({mappings: mappings, docId: docId, initialValue: ini
7544
7796
  label: "Start workflow",
7545
7797
  mappings: mappings,
7546
7798
  mode: "ghost",
7799
+ source: "document-view-empty-state",
7547
7800
  tone: "default"
7548
7801
  })
7549
7802
  },
@@ -7577,7 +7830,8 @@ function SectionActivityDialog({entry: entry, activityName: activityName, onClos
7577
7830
  breadcrumb: instanceBreadcrumb(instance, definition),
7578
7831
  definition: definition,
7579
7832
  entry: entry,
7580
- onClose: onClose
7833
+ onClose: onClose,
7834
+ source: "document-view"
7581
7835
  });
7582
7836
  }
7583
7837
 
@@ -7659,13 +7913,61 @@ function workflowIssue(args) {
7659
7913
  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.`;
7660
7914
  }
7661
7915
 
7916
+ function actualType$(previews, bareId) {
7917
+ 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());
7918
+ }
7919
+
7920
+ const REBUILD_DELAY_MS = 5e3;
7921
+
7922
+ function createActualTypeProbeStore(previews) {
7923
+ const verdicts = /* @__PURE__ */ new Map, live = /* @__PURE__ */ new Map, listeners = /* @__PURE__ */ new Set, notify = () => {
7924
+ for (const listener of [ ...listeners ]) listener();
7925
+ }, start = (bareId, entry) => actualType$(previews, bareId).subscribe({
7926
+ next: value => {
7927
+ verdicts.has(bareId) && verdicts.get(bareId) === value || (verdicts.set(bareId, value),
7928
+ notify());
7929
+ },
7930
+ error: err => {
7931
+ console.error(`[workflow-studio-plugin] actual-type probe for "${bareId}" failed:`, err),
7932
+ entry.subscription = void 0, entry.rebuildTimer = setTimeout(() => {
7933
+ entry.rebuildTimer = void 0, !(live.get(bareId) !== entry || entry.count === 0) && (entry.subscription = start(bareId, entry));
7934
+ }, REBUILD_DELAY_MS);
7935
+ }
7936
+ });
7937
+ return {
7938
+ subscribe(listener) {
7939
+ return listeners.add(listener), () => {
7940
+ listeners.delete(listener);
7941
+ };
7942
+ },
7943
+ read: bareId => verdicts.get(bareId),
7944
+ track(bareId) {
7945
+ let entry = live.get(bareId);
7946
+ if (entry === void 0) {
7947
+ const created = {
7948
+ count: 0,
7949
+ subscription: void 0,
7950
+ rebuildTimer: void 0
7951
+ };
7952
+ live.set(bareId, created), created.subscription = start(bareId, created), entry = created;
7953
+ }
7954
+ entry.count += 1;
7955
+ let released = !1;
7956
+ return () => {
7957
+ released || (released = !0, entry.count -= 1, !(entry.count > 0) && (entry.subscription?.unsubscribe(),
7958
+ entry.rebuildTimer !== void 0 && clearTimeout(entry.rebuildTimer), live.get(bareId) === entry && live.delete(bareId)));
7959
+ };
7960
+ }
7961
+ };
7962
+ }
7963
+
7662
7964
  function contentDocumentTypes(schema) {
7663
7965
  return schema.getTypeNames().filter(name => schema.get(name)?.type?.name === "document" && !WORKFLOW_SYSTEM_TYPES.includes(name));
7664
7966
  }
7665
7967
 
7666
7968
  async function readDeployedDefinitions(engine) {
7667
7969
  return (await engine.query({
7668
- groq: definitionsListGroq("desc")
7970
+ groq: latestDefinitionsGroq()
7669
7971
  })).map(assertReadableModel);
7670
7972
  }
7671
7973
 
@@ -7999,7 +8301,7 @@ function WorkflowProvider(props) {
7999
8301
  setSeeded(prev => new Map(prev).set(instance._id, instance));
8000
8302
  }, []), [startRequest2, setStartRequest] = useState(void 0), openStartDialog = useCallback(r => setStartRequest(r), []), closeStartDialog = useCallback(() => setStartRequest(void 0), []), [entriesStore] = useState(createEntriesStore);
8001
8303
  useEffect(() => () => entriesStore.dispose(), [ entriesStore ]);
8002
- const onReport = useCallback((instanceId, report) => {
8304
+ const previewStore = useDocumentPreviewStore(), actualTypes = useMemo(() => createActualTypeProbeStore(previewStore), [ previewStore ]), onReport = useCallback((instanceId, report) => {
8003
8305
  entriesStore.report(instanceId, report);
8004
8306
  }, [ entriesStore ]), drainRef = useRef(void 0), drainCommitted = useCallback(async instanceId => {
8005
8307
  try {
@@ -8082,6 +8384,7 @@ function WorkflowProvider(props) {
8082
8384
  unregister: unregister,
8083
8385
  isDocResolved: isDocResolved,
8084
8386
  entries: entriesStore,
8387
+ actualTypes: actualTypes,
8085
8388
  requestInstance: requestInstance,
8086
8389
  releaseInstance: releaseInstance,
8087
8390
  requestEvaluation: requestEvaluation,
@@ -8108,7 +8411,7 @@ function WorkflowProvider(props) {
8108
8411
  effectHandlers: engine.effectHandlers,
8109
8412
  autoStartByType: autoStartByType,
8110
8413
  autoStartDefinitions: autoStartDefinitions
8111
- }), [ register, unregister, isDocResolved, entriesStore, requestInstance, releaseInstance, requestEvaluation, releaseEvaluation, seedInstance, openStartDialog, closeStartDialog, startRequest2, loading, discoveryInvalid, fireActionFor, editFieldFor, previewFieldFor, discardFieldPreviewFor, drainEffectsFor, completeEffectFor, engine, binding, mappings, issues, starts, fields, latestVersions, autoStartByType, autoStartDefinitions ]);
8414
+ }), [ register, unregister, isDocResolved, entriesStore, actualTypes, requestInstance, releaseInstance, requestEvaluation, releaseEvaluation, seedInstance, openStartDialog, closeStartDialog, startRequest2, loading, discoveryInvalid, fireActionFor, editFieldFor, previewFieldFor, discardFieldPreviewFor, drainEffectsFor, completeEffectFor, engine, binding, mappings, issues, starts, fields, latestVersions, autoStartByType, autoStartDefinitions ]);
8112
8415
  /* @__PURE__ */
8113
8416
  return jsxs(WorkflowContext.Provider, {
8114
8417
  value: value,
@@ -8581,12 +8884,12 @@ function DocRefsFieldInput({entry: entry, mappedType: mappedType, value: value,
8581
8884
  }
8582
8885
 
8583
8886
  function NotesStartInput({onChange: onChange}) {
8584
- const me = useCurrentUser(), [body, setBody] = useState(""), update = next => {
8887
+ const selfActor = useSelfActor(), [body, setBody] = useState(""), update = next => {
8585
8888
  setBody(next);
8586
8889
  const trimmed = next.trim();
8587
8890
  onChange(trimmed === "" ? [] : [ noteRow({
8588
8891
  body: trimmed,
8589
- actor: me ? personActor(me) : void 0,
8892
+ actor: selfActor,
8590
8893
  at: /* @__PURE__ */ (new Date).toISOString()
8591
8894
  }) ]);
8592
8895
  };
@@ -8795,7 +9098,7 @@ function handleStartError(args) {
8795
9098
  toast.push({
8796
9099
  status: "warning",
8797
9100
  title: `${label} can't be started right now`,
8798
- description: "The workflow does not allow another run with these inputs."
9101
+ description: outcome.description
8799
9102
  });
8800
9103
  return;
8801
9104
  }
@@ -8807,7 +9110,7 @@ function handleStartError(args) {
8807
9110
  }
8808
9111
 
8809
9112
  function StartWorkflowForm({request: request, onClose: onClose}) {
8810
- const {definition: definitionName, label: label, mapping: mapping, subjectDocId: subjectDocId} = request, {engine: engine, binding: binding, seedInstance: seedInstance} = useWorkflowContext(), 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({
9113
+ 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({
8811
9114
  entries: entries,
8812
9115
  definitionName: definitionName,
8813
9116
  heldInstanceId: heldInstanceId,
@@ -8830,7 +9133,10 @@ function StartWorkflowForm({request: request, onClose: onClose}) {
8830
9133
  [name]: v
8831
9134
  })), heldInstanceId !== void 0 && declineResume();
8832
9135
  }, onStart = async () => {
8833
- setAttempt({
9136
+ telemetry.log(WorkflowStartDialogSubmitted, {
9137
+ source: request.source,
9138
+ resumed: resuming
9139
+ }), setAttempt({
8834
9140
  resume: resumeEntry
8835
9141
  });
8836
9142
  const instanceId = resumeEntry?.instance._id ?? heldInstanceId ?? instanceDocId(binding.tag);
@@ -8910,8 +9216,9 @@ function StartWorkflowForm({request: request, onClose: onClose}) {
8910
9216
  }
8911
9217
 
8912
9218
  function StartWorkflowDialog({request: request, onClose: onClose}) {
8913
- /* @__PURE__ */
8914
- return jsx(Dialog, {
9219
+ return useLogEventOnMount(WorkflowStartDialogOpened, {
9220
+ source: request.source
9221
+ }), /* @__PURE__ */ jsx(Dialog, {
8915
9222
  header: `Start ${request.label}`,
8916
9223
  id: "start-workflow",
8917
9224
  onClose: onClose,
@@ -9050,7 +9357,7 @@ function AutoStartGate(props) {
9050
9357
  }
9051
9358
 
9052
9359
  function AutoStartDriver(props) {
9053
- const {rawDocId: rawDocId, docType: docType, workflows: workflows, value: value, fallback: fallback} = props, {isDocResolved: isDocResolved, autoStartDefinitions: autoStartDefinitions, engine: engine, binding: binding, seedInstance: seedInstance} = useWorkflowContext(), observer = useStudioObserver({
9360
+ const {rawDocId: rawDocId, docType: docType, workflows: workflows, value: value, fallback: fallback} = props, {isDocResolved: isDocResolved, autoStartDefinitions: autoStartDefinitions, engine: engine, binding: binding, seedInstance: seedInstance} = useWorkflowContext(), telemetry = useWorkflowTelemetry(), observer = useStudioObserver({
9054
9361
  engine: engine
9055
9362
  }), {selectedReleaseId: selectedReleaseId} = usePerspective(), {entries: entries} = useWorkflowsForDocument(rawDocId), docId = getPublishedId(rawDocId), valueRef = useRef(value);
9056
9363
  valueRef.current = value;
@@ -9064,7 +9371,7 @@ function AutoStartDriver(props) {
9064
9371
  if (held !== void 0) return held;
9065
9372
  const id = instanceDocId(binding.tag);
9066
9373
  return instanceIds.current.set(workflow, id), id;
9067
- }, [ binding.tag ]), [fired, setFired] = useState(!1), [settled, setSettled] = useState(!1), [failure, setFailure] = useState(void 0), firedRef = useRef(!1), startAll = useCallback(async values => {
9374
+ }, [ binding.tag ]), [fired, setFired] = useState(!1), [settled, setSettled] = useState(!1), [failure, setFailure] = useState(void 0), firedRef = useRef(!1), startAll = useCallback(async (values, mode) => {
9068
9375
  if (!firedRef.current) {
9069
9376
  firedRef.current = !0, setFired(!0);
9070
9377
  try {
@@ -9076,20 +9383,25 @@ function AutoStartDriver(props) {
9076
9383
  releaseId: selectedReleaseId
9077
9384
  } : {}
9078
9385
  });
9386
+ const requests = buildAutoStartRequests({
9387
+ workflows: pendingRef.current,
9388
+ definitions: autoStartDefinitions,
9389
+ subject: {
9390
+ docId: docId,
9391
+ type: docType
9392
+ },
9393
+ contentResource: binding.contentResource,
9394
+ values: values,
9395
+ instanceIdFor: instanceIdFor
9396
+ });
9397
+ telemetry.log(WorkflowAutoStartRan, {
9398
+ mode: mode,
9399
+ workflowCount: requests.length
9400
+ });
9079
9401
  const {failure: failure2} = await runStarts({
9080
9402
  engine: engine,
9081
9403
  seed: seedInstance,
9082
- requests: buildAutoStartRequests({
9083
- workflows: pendingRef.current,
9084
- definitions: autoStartDefinitions,
9085
- subject: {
9086
- docId: docId,
9087
- type: docType
9088
- },
9089
- contentResource: binding.contentResource,
9090
- values: values,
9091
- instanceIdFor: instanceIdFor
9092
- })
9404
+ requests: requests
9093
9405
  });
9094
9406
  failure2 !== void 0 && setFailure(failure2);
9095
9407
  } catch (err) {
@@ -9098,7 +9410,7 @@ function AutoStartDriver(props) {
9098
9410
  setSettled(!0);
9099
9411
  }
9100
9412
  }
9101
- }, [ autoStartDefinitions, docId, docType, binding, engine, observer, seedInstance, instanceIdFor, selectedReleaseId ]), retry = useCallback(() => {
9413
+ }, [ autoStartDefinitions, docId, docType, binding, engine, observer, seedInstance, instanceIdFor, selectedReleaseId, telemetry ]), retry = useCallback(() => {
9102
9414
  firedRef.current = !1, setFired(!1), setSettled(!1), setFailure(void 0);
9103
9415
  }, []), action = autoStartAction({
9104
9416
  resolved: definitionsResolved && isDocResolved(docId),
@@ -9108,7 +9420,7 @@ function AutoStartDriver(props) {
9108
9420
  settled: settled
9109
9421
  });
9110
9422
  return useEffect(() => {
9111
- action === "silent-start" && startAll({});
9423
+ action === "silent-start" && startAll({}, "silent");
9112
9424
  }, [ action, startAll ]), failure !== void 0 ? /* @__PURE__ */ jsx(AutoStartFailure, {
9113
9425
  message: failure,
9114
9426
  onProceed: () => setFailure(void 0),
@@ -9120,7 +9432,7 @@ function AutoStartDriver(props) {
9120
9432
  docType: docType,
9121
9433
  inputs: inputs,
9122
9434
  onStart: values => {
9123
- startAll(values);
9435
+ startAll(values, "collect");
9124
9436
  },
9125
9437
  starting: fired,
9126
9438
  workflowCount: pending.length
@@ -9342,7 +9654,9 @@ function finishedLinesOf(entries) {
9342
9654
  }
9343
9655
 
9344
9656
  function WorkflowFormStrip(props) {
9345
- const {mappings: mappings} = props, {isDocResolved: isDocResolved, discoveryInvalid: discoveryInvalid} = useWorkflowContext(), {docId: docId, entries: entries} = useWorkflowsForDocument(props.value?._id ?? null), {params: params, setParams: setParams} = usePaneRouter(), openWorkflowsView = focusId => setParams(focusedViewParams(params, focusId)), active = entries.filter(isLiveEntry), finished = finishedLinesOf(entries), invalid = surfacedInvalid(discoveryInvalid, entries), resolved = docId !== void 0 && isDocResolved(docId);
9657
+ 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 => {
9658
+ telemetry.log(WorkflowFormStripClicked), setParams(focusedViewParams(params, focusId));
9659
+ }, active = entries.filter(isLiveEntry), finished = finishedLinesOf(entries), invalid = surfacedInvalid(discoveryInvalid, entries), resolved = docId !== void 0 && isDocResolved(docId);
9346
9660
  /* @__PURE__ */
9347
9661
  return jsxs(Stack, {
9348
9662
  gap: 4,
@@ -9376,7 +9690,8 @@ function StripLines({active: active, docId: docId, finished: finished, initialVa
9376
9690
  initialValue: initialValue,
9377
9691
  label: "Start workflow",
9378
9692
  mappings: mappings,
9379
- mode: "bleed"
9693
+ mode: "bleed",
9694
+ source: "form-strip"
9380
9695
  })
9381
9696
  }) : null ]
9382
9697
  });
@@ -9696,4 +10011,4 @@ function WorkflowRootInput(props) {
9696
10011
  });
9697
10012
  }
9698
10013
 
9699
- export { AbortedBadge, ActivitiesList, ActivityDetailDialog, ActivityRow, BreadcrumbTail, CollapsibleBand, DismissablePopover, DocPreviewLink, DocRefFace, GroupHeading, HoverHint, InvalidDocNotice, LinkChip, LoadingRow, SpinnerSlot, StageChip, StageFace, StaleLock, TabSwitch, TodoItemsList, TrailingHairline, UnreadableDocsNote, activityRowProps, assigneeUserIdsOf, committedRowFace, dateControlKind, dateControlValue, definitionSnapshotOf, describeError, editTargetOf, findActivity, findActivityNode, formatDate, gdrLocality, instanceBreadcrumb, instanceNotice, instanceTitle, isActivityAssignedTo, isEvaluationStale, isOpenActivityStatus, openActivityGone, parseDateFieldValue, parseStoredDateValue, readDeployedDefinitions, rowAssignControlState, rowDateControlState, serializeDateFieldValue, stageTitle, stateByActivity, stopRowClick, stopRowMouseDown, useAssignmentIdentity, useDefinition, useProjectMembers, useSaveField, useUserDisplays, useWorkflowContext, useWorkflowInstanceEntry, workflowDefaultDocumentNode, workflowStudioPlugin, workflowsTabCodec, workflowsView };
10014
+ export { AbortedBadge, ActivitiesList, ActivityDetailDialog, ActivityRow, BreadcrumbTail, CautionNote, CollapsibleBand, DismissablePopover, DocPreviewLink, DocRefFace, GroupHeading, HoverHint, InvalidDocNotice, LinkChip, LoadingRow, SpinnerSlot, StageChip, StageFace, StaleLock, TabSwitch, TodoItemsList, TrailingHairline, UnreadableDocsNote, WORKFLOW_API_VERSION, WorkflowInstanceDetailViewed, WorkflowTaskFiltersApplied, WorkflowToolOpened, activityRowProps, assigneeUserIdsOf, committedRowFace, dateControlKind, dateControlValue, definitionSnapshotOf, describeError, findActivity, findActivityNode, formatDate, gdrLocality, instanceBreadcrumb, instanceNotice, instanceTitle, isActivityAssignedTo, isEvaluationStale, isOpenActivityStatus, openActivityGone, parseDateFieldValue, parseStoredDateValue, readDeployedDefinitions, rowAssignControlState, rowDateControlState, serializeDateFieldValue, stageTitle, stateByActivity, stopRowClick, stopRowMouseDown, useAssignmentIdentity, useClosableToast, useDefinition, useLogEventOnMount, useProjectMembers, useSaveField, useUserDisplays, useWorkflowContext, useWorkflowInstanceEntry, workflowDefaultDocumentNode, workflowStudioPlugin, workflowsTabCodec, workflowsView };