@sanity/workflow-studio-plugin 0.30.0 → 0.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,16 +1,16 @@
1
- import { jsx, jsxs, Fragment } from "react/jsx-runtime";
1
+ import { jsx, Fragment, jsxs } from "react/jsx-runtime";
2
2
 
3
3
  import { TimelineIcon } from "@sanity/icons/Timeline";
4
4
 
5
- import { Popover, Tooltip, Box, Stack, Text, useClickOutsideEvent, Flex, TextInput, Button, Select, Checkbox, Autocomplete, Card, TextArea, useToast, Dialog, Switch, TextSkeleton, Code, Badge, useTheme_v2, Spinner, MenuButton, Menu, MenuItem, useElementSize, MenuDivider, TabList, Tab, TabPanel } from "@sanity/ui";
5
+ import { Popover, Tooltip, Box, Stack, Text, useClickOutsideEvent, Flex, TextInput, Button, Select, Checkbox, Autocomplete, Card, TextArea, useToast, Dialog, Switch, useTheme_v2, TextSkeleton, Code, Badge, Spinner, MenuButton, Menu, MenuItem, useElementSize, MenuDivider, TabList, Tab, TabPanel } from "@sanity/ui";
6
6
 
7
7
  import { editFieldTarget, useWorkflowTelemetry, useRefcountedIds } from "@sanity/workflow-react";
8
8
 
9
- import { createContext, useContext, useEffect, useSyncExternalStore, useCallback, useRef, useMemo, useState, Fragment as Fragment$1, createElement, isValidElement, useId, useLayoutEffect, Component, lazy, Suspense } from "react";
9
+ import { createContext, useContext, useEffect, useSyncExternalStore, useCallback, Fragment as Fragment$1, useRef, useMemo, useState, createElement, isValidElement, useId, useLayoutEffect, Component, useEffectEvent, lazy, Suspense } from "react";
10
10
 
11
11
  import { usePaneRouter } from "sanity/structure";
12
12
 
13
- import { WORKFLOW_INSTANCE_TYPE, WORKFLOW_DEFINITION_TYPE, GUARD_DOC_TYPE, tryParseGdr, resourceFromParsed, latestDeployedDefinitions, isStartableDefinition, isSubjectEntry, isInputSourced, acceptsDocumentType, initialFieldIssues, gdrRef, releaseRef, refKindAcceptsTypes, rejectedRefTypes, ActionDisabledError, MutationGuardDeniedError, EditFieldDeniedError, errorMessage, terminalState, findOpenStageEntry, actionRendering, isTodoListEntry, isTodoListItem, isTerminalActivityStatus, describeCondition, sentenceCase, checklistLines, findCurrentActivityEntry, ENGINE_API_VERSION, scalarValidationIssues, classifyPrincipalId, userLoginProvider, toBareId, findStageNode, findActivityNode, isSingleDocRefEntry, isGdr, resolveFieldEntry, isSingleDocRefKind, isNotesEntry, remediationsFor, diagnoseInstance, diagnoseInputFromEvaluation, documentStuckCause, parseDefinitionSnapshot, isUnprimed, isTerminalStage, DEFAULT_TRANSITION_WHEN, instancesQuery, assertReadableModel, projectStartSliceRow, StartNotSettledError, StartNotAllowedError, isRevisionConflict, humanize, unboundRequirementReads, explainStartRequirement, singleSubjectRequirementRefused, evaluateStartFilter, startKindOf, hasSingleSubjectRequirement, readsRootDocument, latestDefinitionsGroq, gdrFromResource, subscriptionDocumentsForInstance, aclPathForResource, definitionLookupGroq, instanceDocId, missingRequiredInputs, documentActionDenials } from "@sanity/workflow-engine";
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, actionRendering, terminalState, findOpenStageEntry, isTodoListEntry, isTodoListItem, isTerminalActivityStatus, assignmentMembers, describeCondition, sentenceCase, checklistLines, findCurrentActivityEntry, ENGINE_API_VERSION, userLoginProvider, classifyPrincipalId, scalarValidationIssues, toBareId, findStageNode, findActivityNode, isSingleDocRefEntry, isGdr, resolveFieldEntry, isSingleDocRefKind, isNotesEntry, remediationsFor, diagnoseInstance, diagnoseInputFromEvaluation, documentStuckCause, parseDefinitionSnapshot, isUnprimed, isTerminalStage, DEFAULT_TRANSITION_WHEN, instancesQuery, assertReadableModel, projectStartSliceRow, StartNotSettledError, StartNotAllowedError, isRevisionConflict, humanize, unboundRequirementReads, explainStartRequirement, singleSubjectRequirementRefused, evaluateStartFilter, startKindOf, hasSingleSubjectRequirement, readsRootDocument, latestDefinitionsGroq, gdrFromResource, subscriptionDocumentsForInstance, aclPathForResource, documentActionDenials, definitionLookupGroq, instanceDocId, missingRequiredInputs, clientGuardDereference } from "@sanity/workflow-engine";
14
14
 
15
15
  import { CheckmarkIcon } from "@sanity/icons/Checkmark";
16
16
 
@@ -18,7 +18,7 @@ import { CloseIcon } from "@sanity/icons/Close";
18
18
 
19
19
  import { formatDistanceToNow } from "date-fns/formatDistanceToNow";
20
20
 
21
- import { sameAssignee, projectMemberRow, providerTitle, ClearableDatePicker, AssigneePicker as AssigneePicker$1, MemberPicker as MemberPicker$1, MemberAvatar, AssigneeBadges as AssigneeBadges$1, AssigneeBadge as AssigneeBadge$1, ProgressBar, memberRoleFor, AssigneeStack, roleLabel, clampProgress, MemberAvatarGroup, progressCaption, DatePicker, roleLabelFor, roleListLabel } from "@sanity/workflow-components";
21
+ import { assignmentMatch, sameAssignee, projectMemberRow, providerTitle, ClearableDatePicker, AssigneePicker as AssigneePicker$1, MemberPicker as MemberPicker$1, MemberAvatar, AssigneeBadges as AssigneeBadges$1, AssigneeBadge as AssigneeBadge$1, ProgressBar, memberRoleFor, AssigneeStack, roleLabel, clampProgress, MemberAvatarGroup, progressCaption, DatePicker, roleLabelFor, roleListLabel } from "@sanity/workflow-components";
22
22
 
23
23
  import { getPublishedId, getDraftId } from "@sanity/client/csm";
24
24
 
@@ -34,8 +34,6 @@ import { UserIcon } from "@sanity/icons/User";
34
34
 
35
35
  import { SearchIcon } from "@sanity/icons/Search";
36
36
 
37
- import { ChevronRightIcon } from "@sanity/icons/ChevronRight";
38
-
39
37
  import { EditIcon } from "@sanity/icons/Edit";
40
38
 
41
39
  import { randomKey } from "@sanity/util/content";
@@ -60,6 +58,8 @@ import { ErrorFilledIcon } from "@sanity/icons/ErrorFilled";
60
58
 
61
59
  import { RemoveCircleIcon } from "@sanity/icons/RemoveCircle";
62
60
 
61
+ import { AddUserIcon } from "@sanity/icons/AddUser";
62
+
63
63
  import { AddIcon } from "@sanity/icons/Add";
64
64
 
65
65
  import { UlistIcon } from "@sanity/icons/Ulist";
@@ -72,6 +72,10 @@ import { ChevronDownIcon } from "@sanity/icons/ChevronDown";
72
72
 
73
73
  import pluralize from "pluralize-esm";
74
74
 
75
+ import { ChevronRightIcon } from "@sanity/icons/ChevronRight";
76
+
77
+ import { styled } from "styled-components";
78
+
75
79
  import { EllipsisHorizontalIcon } from "@sanity/icons/EllipsisHorizontal";
76
80
 
77
81
  import { ClockIcon } from "@sanity/icons/Clock";
@@ -484,9 +488,12 @@ function formatShortAgo(value, now) {
484
488
  }
485
489
 
486
490
  function formatShortSpan(value, now) {
487
- const d = new Date(value);
488
- if (Number.isNaN(d.getTime())) return "—";
489
- const seconds = Math.floor((now.getTime() - d.getTime()) / 1e3);
491
+ return shortSpanOf(new Date(value), now);
492
+ }
493
+
494
+ function shortSpanOf(from, now) {
495
+ if (from === void 0 || Number.isNaN(from.getTime())) return "—";
496
+ const seconds = Math.floor((now.getTime() - from.getTime()) / 1e3);
490
497
  if (seconds < 60) return "now";
491
498
  const minutes = Math.floor(seconds / 60);
492
499
  if (minutes < 60) return `${minutes}m`;
@@ -514,6 +521,14 @@ function formatDate(value) {
514
521
  });
515
522
  }
516
523
 
524
+ function formatDueMoment(value) {
525
+ return storedDateKind(value) === "date" ? formatDate(value) : formatShortDateTime(value);
526
+ }
527
+
528
+ function dueMomentSentence(value, now) {
529
+ return `${isDueDatePast(value, now) ? "Was due" : "Due"} ${formatDueMoment(value)}`;
530
+ }
531
+
517
532
  function formatBoolean(value) {
518
533
  return value ? "True" : "False";
519
534
  }
@@ -772,19 +787,10 @@ function isOpenActivityStatus(status) {
772
787
  return !isTerminalActivityStatus(status);
773
788
  }
774
789
 
775
- function assigneeMatches(member, who) {
776
- return member.type === "user" ? member.id === who.userId : who.roles.includes(member.role);
777
- }
778
-
779
790
  function changedAssignee(current, next) {
780
791
  return next.find(item => !current.some(existing => sameAssignee(item, existing))) ?? current.find(item => !next.some(existing => sameAssignee(item, existing)));
781
792
  }
782
793
 
783
- function singleAssigneePick(current, next) {
784
- const changed = changedAssignee(current ? [ current ] : [], next);
785
- if (changed !== void 0) return current && sameAssignee(changed, current) ? null : changed;
786
- }
787
-
788
794
  function isAssigneeShape(value) {
789
795
  if (typeof value != "object" || value === null) return !1;
790
796
  const {type: type, id: id, role: role} = value;
@@ -798,7 +804,11 @@ function isActorShape(value) {
798
804
  }
799
805
 
800
806
  function assigneesOf(entries) {
801
- return entries.flatMap(entry => entry._type === "assignees" ? entry.value : []);
807
+ return [ ...assignmentMembers(entries) ];
808
+ }
809
+
810
+ function userIdsOf(assignees) {
811
+ return assignees.flatMap(assignee => assignee.type === "user" ? [ assignee.id ] : []);
802
812
  }
803
813
 
804
814
  function assigneeEdit(current, next) {
@@ -812,43 +822,36 @@ function assigneeEdit(current, next) {
812
822
  };
813
823
  }
814
824
 
815
- function isItemAssignedTo(item, who) {
816
- return item.assignee != null && assigneeMatches(item.assignee, who);
817
- }
818
-
819
- function matchingAssignees(state, who) {
820
- return state.flatMap(entry => entry._type === "assignees" ? entry.value.filter(a => assigneeMatches(a, who)) : entry._type === "assignee" && entry.value !== null && assigneeMatches(entry.value, who) ? [ entry.value ] : []);
825
+ function assignmentHolding(assignees, who) {
826
+ const match = assignmentMatch(assignees, who);
827
+ return match === void 0 ? "other" : match === "user" ? "you" : "role";
821
828
  }
822
829
 
823
- function isActivityAssignedTo(args) {
824
- return matchingAssignees(args.state, args.who).length > 0;
830
+ function holdingMatchesAssignment(holding, assignment) {
831
+ return assignment === void 0 ? holding !== "other" : holding === assignment;
825
832
  }
826
833
 
827
- function isRoleAssignedTo(args) {
828
- return matchingAssignees(args.state, args.who).some(member => member.type === "role");
834
+ function activityHolding(args) {
835
+ return assignmentHolding(assigneesOf(args.state), args.who);
829
836
  }
830
837
 
831
- function isItemRoleAssignedTo(item, who) {
832
- return isItemAssignedTo(item, who) && item.assignee?.type === "role";
838
+ function itemHolding(item, who) {
839
+ return assignmentHolding(item.assignee ?? [], who);
833
840
  }
834
841
 
835
842
  function assignedWork(args) {
836
- const {instance: instance, committed: committed, who: who} = args, evaluation = args.staleEvaluation === "keep" || isEvaluationCurrent(args) ? args.evaluation : void 0, byActivity = stateByActivity(instance), stateOf = t => byActivity.get(t.activity.name) ?? [], activities = (evaluation?.currentStage.activities ?? []).filter(t => isActivityAssignedTo({
843
+ const {instance: instance, committed: committed, who: who} = args, evaluation = args.staleEvaluation === "keep" || isEvaluationCurrent(args) ? args.evaluation : void 0, byActivity = stateByActivity(instance), stateOf = t => byActivity.get(t.activity.name) ?? [], activities = (evaluation?.currentStage.activities ?? []).filter(t => holdingMatchesAssignment(activityHolding({
837
844
  state: stateOf(t),
838
845
  who: who
839
- })), items = deriveTodoItems({
846
+ }), args.assignment)), items = deriveTodoItems({
840
847
  instance: instance,
841
848
  committed: committed,
842
849
  evaluation: evaluation
843
- }).rows.filter(row => isItemAssignedTo(row.item, who));
850
+ }).rows.filter(row => holdingMatchesAssignment(itemHolding(row.item, who), args.assignment));
844
851
  return {
845
852
  activities: activities,
846
853
  openItems: items.filter(row => !isTodoDone(row.item)),
847
- doneItems: items.filter(row => isTodoDone(row.item)),
848
- roleMatched: activities.some(t => isRoleAssignedTo({
849
- state: stateOf(t),
850
- who: who
851
- })) || items.some(row => isItemRoleAssignedTo(row.item, who))
854
+ doneItems: items.filter(row => isTodoDone(row.item))
852
855
  };
853
856
  }
854
857
 
@@ -873,11 +876,18 @@ function hasOpenAssignedWork(work) {
873
876
  }
874
877
 
875
878
  function assignedTaskCount(args) {
876
- return openAssignedCount(assignedWork(args));
879
+ return openAssignedCount(assignedWork({
880
+ ...args,
881
+ assignment: "you"
882
+ }));
883
+ }
884
+
885
+ function hasForMeRows(work) {
886
+ return work.some(({shown: shown}) => hasAssignedRows(shown));
877
887
  }
878
888
 
879
889
  function forMeWorkOf(entries, identity) {
880
- const work = identity ? entries.map(entry => {
890
+ const section = assignment => identity === void 0 ? [] : entries.map(entry => {
881
891
  const base = {
882
892
  instance: entry.instance,
883
893
  committed: entry.committed,
@@ -887,18 +897,22 @@ function forMeWorkOf(entries, identity) {
887
897
  return {
888
898
  entry: entry,
889
899
  who: identity,
890
- count: assignedTaskCount(base),
900
+ count: openAssignedCount(assignedWork({
901
+ ...base,
902
+ assignment: assignment
903
+ })),
891
904
  shown: assignedWork({
892
905
  ...base,
906
+ assignment: assignment,
893
907
  staleEvaluation: "keep"
894
908
  })
895
909
  };
896
- }) : [];
910
+ }), assignedToUser = section("you"), assignedToRoles = section("role");
897
911
  return {
898
- work: work,
899
- count: work.reduce((n, {count: count}) => n + count, 0),
900
- hasRows: work.some(({shown: shown}) => hasAssignedRows(shown)),
901
- hasRoleMatched: work.some(({shown: shown}) => shown.roleMatched)
912
+ assignedToUser: assignedToUser,
913
+ assignedToRoles: assignedToRoles,
914
+ count: assignedToUser.reduce((n, {count: count}) => n + count, 0),
915
+ hasRows: hasForMeRows([ ...assignedToUser, ...assignedToRoles ])
902
916
  };
903
917
  }
904
918
 
@@ -978,10 +992,29 @@ const ACTIVITY_STATUS_LABEL = {
978
992
  done: "Done",
979
993
  failed: "Failed",
980
994
  skipped: "Skipped"
995
+ }, TASK_STATUS_LABEL = {
996
+ ...ACTIVITY_STATUS_LABEL,
997
+ active: "In progress"
998
+ }, HOLDING_SUFFIX = {
999
+ you: "with you",
1000
+ role: "with your role",
1001
+ other: void 0
981
1002
  };
982
1003
 
1004
+ function taskStatusLine(args) {
1005
+ const label = TASK_STATUS_LABEL[args.status], suffix = isTerminalActivityStatus(args.status) ? void 0 : HOLDING_SUFFIX[args.holding];
1006
+ return suffix === void 0 ? label : `${label} · ${suffix}`;
1007
+ }
1008
+
1009
+ function isHandsOff(args) {
1010
+ return args.classification === "autonomous" && args.completesWithoutCaller === "yes";
1011
+ }
1012
+
983
1013
  function showsAutomatedPill(activityEval) {
984
- return !isTerminalActivityStatus(activityEval.status) && activityEval.classification === "autonomous" && activityEval.autonomy.completesWithoutCaller === "yes";
1014
+ return !isTerminalActivityStatus(activityEval.status) && isHandsOff({
1015
+ classification: activityEval.classification,
1016
+ completesWithoutCaller: activityEval.autonomy.completesWithoutCaller
1017
+ });
985
1018
  }
986
1019
 
987
1020
  function showsBlockedTag(activityEval) {
@@ -1059,9 +1092,10 @@ function deriveActivityPresentation(args) {
1059
1092
  }), blockedReason = status === "active" && actions.length > 0 && actions.every(a => !a.allowed) ? actions.find(a => a.disabledReason)?.disabledReason : void 0, ticks = tickActionNames({
1060
1093
  actions: actions,
1061
1094
  state: args.state
1062
- }), buttonActions = visibleActions.filter(a => !ticks.has(a.action.name)), settled = isTerminalActivityStatus(status);
1095
+ }), buttonActions = visibleActions.filter(a => !ticks.has(a.action.name)), settled = isTerminalActivityStatus(status), holderGate = settled ? void 0 : args.activityEval.actions.find(action => action.holderGate !== void 0)?.holderGate;
1063
1096
  return {
1064
1097
  blockedReason: blockedReason,
1098
+ holderGate: holderGate?.holders,
1065
1099
  settled: settled,
1066
1100
  triggeredActions: settled ? [] : args.activityEval.actions.filter(a => a.triggered === !0),
1067
1101
  manualTarget: kind === "manual" ? manualLink(activity) : void 0,
@@ -1189,7 +1223,7 @@ function deriveActivityDetail(args) {
1189
1223
  title: activityLabel(activityEval.activity),
1190
1224
  state: state,
1191
1225
  editableByName: new Map(activityEditables.map(f => [ f.name, f ])),
1192
- assignFields: new Map(activityEditables.filter(f => f.type === "assignees" && f.editable).map(f => [ f.name, f ]))
1226
+ assignFields: new Map(activityEditables.filter(f => (f.type === "assignee" || f.type === "assignees") && f.editable).map(f => [ f.name, f ]))
1193
1227
  };
1194
1228
  }
1195
1229
 
@@ -1233,6 +1267,35 @@ function useWorkflowInstanceEntry(instanceId, options = {}) {
1233
1267
  }, [ instanceId, evaluate, requestEvaluation, releaseEvaluation ]), useHeldWorkflowEntry(instanceId);
1234
1268
  }
1235
1269
 
1270
+ const DIM = {
1271
+ opacity: .6
1272
+ };
1273
+
1274
+ function Breadcrumb({segments: segments, emphasizeCurrent: emphasizeCurrent = !1}) {
1275
+ /* @__PURE__ */
1276
+ return jsx(Fragment, {
1277
+ children: segments.map((segment, i) => /* @__PURE__ */ jsxs(Fragment$1, {
1278
+ children: [ i > 0 ? /* @__PURE__ */ jsx(Separator, {}) : null,
1279
+ /* @__PURE__ */ jsx("span", {
1280
+ style: emphasizeCurrent && i < segments.length - 1 ? DIM : void 0,
1281
+ children: segment
1282
+ }) ]
1283
+ }, `${i}-${segment}`))
1284
+ });
1285
+ }
1286
+
1287
+ function Separator() {
1288
+ /* @__PURE__ */
1289
+ return jsx("span", {
1290
+ "aria-hidden": !0,
1291
+ style: {
1292
+ ...DIM,
1293
+ margin: "0 0.35em"
1294
+ },
1295
+ children: "/"
1296
+ });
1297
+ }
1298
+
1236
1299
  const WorkflowToolOpened = defineEvent({
1237
1300
  name: "Workflows Studio Plugin Tool Opened",
1238
1301
  version: 3,
@@ -1264,7 +1327,7 @@ const WorkflowDefinitionDetailViewed = defineEvent({
1264
1327
  description: "The form strip's instance line was clicked, opening the document view"
1265
1328
  }), WorkflowDocumentLinkClicked = defineEvent({
1266
1329
  name: "Workflows Studio Plugin Document Link Clicked",
1267
- version: 3,
1330
+ version: 4,
1268
1331
  description: "A document link was clicked, opening the referenced document's editor"
1269
1332
  }), WorkflowStartDialogOpened = defineEvent({
1270
1333
  name: "Workflows Studio Plugin Start Dialog Opened",
@@ -1280,15 +1343,15 @@ const WorkflowDefinitionDetailViewed = defineEvent({
1280
1343
  description: "A fresh document's auto-start ran — one start request per configured workflow"
1281
1344
  }), WorkflowActionControlUsed = defineEvent({
1282
1345
  name: "Workflows Studio Plugin Action Control Used",
1283
- version: 3,
1346
+ version: 4,
1284
1347
  description: "An action-firing control was used, attributed to its UI surface"
1285
1348
  }), WorkflowFieldControlUsed = defineEvent({
1286
1349
  name: "Workflows Studio Plugin Field Control Used",
1287
- version: 2,
1350
+ version: 3,
1288
1351
  description: "A field-editing control committed an edit, attributed to its UI surface"
1289
1352
  }), WorkflowActivityDialogOpened = defineEvent({
1290
1353
  name: "Workflows Studio Plugin Activity Dialog Opened",
1291
- version: 2,
1354
+ version: 3,
1292
1355
  description: "The activity detail dialog was opened"
1293
1356
  }), WorkflowBoardWorkflowSelected = defineEvent({
1294
1357
  name: "Workflows Studio Plugin Board Workflow Selected",
@@ -1300,11 +1363,11 @@ const WorkflowDefinitionDetailViewed = defineEvent({
1300
1363
  description: "A cold-start seeded preview title diverged from the live preview pipeline"
1301
1364
  }), WorkflowTodoToggled = defineEvent({
1302
1365
  name: "Workflows Studio Plugin Todo Toggled",
1303
- version: 1,
1366
+ version: 2,
1304
1367
  description: "A todo checkbox was toggled, attributed to its UI surface and write seam"
1305
1368
  }), WorkflowTodoEdited = defineEvent({
1306
1369
  name: "Workflows Studio Plugin Todo Edited",
1307
- version: 1,
1370
+ version: 2,
1308
1371
  description: "A non-toggle todo-list write, attributed to its UI surface and gesture"
1309
1372
  }), WorkflowAbortDialogOpened = defineEvent({
1310
1373
  name: "Workflows Studio Plugin Abort Dialog Opened",
@@ -1327,26 +1390,6 @@ function LogEventOnMount(props) {
1327
1390
  return useLogEventOnMount(props.event, props.data), null;
1328
1391
  }
1329
1392
 
1330
- function isEmptyValue(value) {
1331
- return value == null || value === "" || Array.isArray(value) && value.length === 0;
1332
- }
1333
-
1334
- function effectiveValidation(kind, validation) {
1335
- return kind !== "progress" ? validation : {
1336
- min: validation?.min ?? 0,
1337
- max: validation?.max ?? 100
1338
- };
1339
- }
1340
-
1341
- function scalarValidationIssue(args) {
1342
- const issues = scalarValidationIssues({
1343
- entryType: args.kind,
1344
- validation: effectiveValidation(args.kind, args.validation),
1345
- value: args.value
1346
- });
1347
- return issues === void 0 ? void 0 : `${args.label}: ${issues.join("; ")}`;
1348
- }
1349
-
1350
1393
  function countBy(rows, keysOf) {
1351
1394
  const counts = /* @__PURE__ */ new Map;
1352
1395
  for (const row of rows) for (const key of keysOf(row)) counts.set(key, (counts.get(key) ?? 0) + 1);
@@ -1394,6 +1437,10 @@ function disambiguatedNames(displays) {
1394
1437
  return displays.map(display => (counts.get(display.name) ?? 0) < 2 || display.loginProvider === void 0 ? display.name : `${display.name} (${providerTitle(display.loginProvider) ?? display.loginProvider})`);
1395
1438
  }
1396
1439
 
1440
+ function uniqueIds(ids) {
1441
+ return [ ...new Set(ids) ];
1442
+ }
1443
+
1397
1444
  function useUserDisplays(ids) {
1398
1445
  const {members: members} = useProjectMembers(), {users: users} = useStudioProjectUsers(), meRaw = useCurrentUser(), me = useMemo(() => meRaw === null ? null : {
1399
1446
  ...meRaw,
@@ -1460,16 +1507,18 @@ function useSelfActor() {
1460
1507
  }
1461
1508
 
1462
1509
  function useAssignmentIdentity() {
1463
- const state = useSelfState();
1464
- if (state.kind !== "resolved") return state;
1465
- const {id: id, roles: roles} = state.self;
1466
- return {
1510
+ const state = useSelfState(), id = state.kind === "resolved" ? state.self.id : void 0, roles = state.kind === "resolved" ? state.self.roles : void 0;
1511
+ return useMemo(() => id === void 0 || roles === void 0 ? state.kind === "pending" ? {
1512
+ kind: "pending"
1513
+ } : {
1514
+ kind: "unavailable"
1515
+ } : {
1467
1516
  kind: "resolved",
1468
1517
  identity: {
1469
1518
  userId: id,
1470
- roles: roles.map(r => r.name)
1519
+ roles: roles.map(role => role.name)
1471
1520
  }
1472
- };
1521
+ }, [ id, roles, state.kind ]);
1473
1522
  }
1474
1523
 
1475
1524
  function identityOf(state) {
@@ -1499,15 +1548,35 @@ function projectMembersFrom(users) {
1499
1548
  }
1500
1549
 
1501
1550
  function useProjectMembers() {
1502
- const {users: users, roles: roles, loading: loading, error: error} = useStudioProjectUsers();
1503
- return {
1504
- members: useMemo(() => projectMembersFrom(users), [ users ]),
1551
+ const {users: users, roles: roles, loading: loading, error: error} = useStudioProjectUsers(), members = useMemo(() => projectMembersFrom(users), [ users ]), message = useMemo(() => error === void 0 ? void 0 : errorMessage(error), [ error ]);
1552
+ return useMemo(() => ({
1553
+ members: members,
1505
1554
  roles: roles,
1506
1555
  loading: loading,
1507
- error: error === void 0 ? void 0 : errorMessage(error)
1556
+ error: message
1557
+ }), [ loading, members, message, roles ]);
1558
+ }
1559
+
1560
+ function isEmptyValue(value) {
1561
+ return value == null || value === "" || Array.isArray(value) && value.length === 0;
1562
+ }
1563
+
1564
+ function effectiveValidation(kind, validation) {
1565
+ return kind !== "progress" ? validation : {
1566
+ min: validation?.min ?? 0,
1567
+ max: validation?.max ?? 100
1508
1568
  };
1509
1569
  }
1510
1570
 
1571
+ function scalarValidationIssue(args) {
1572
+ const issues = scalarValidationIssues({
1573
+ entryType: args.kind,
1574
+ validation: effectiveValidation(args.kind, args.validation),
1575
+ value: args.value
1576
+ });
1577
+ return issues === void 0 ? void 0 : `${args.label}: ${issues.join("; ")}`;
1578
+ }
1579
+
1511
1580
  const passthrough = raw => ({
1512
1581
  value: raw
1513
1582
  }), paramConverter = {
@@ -1870,31 +1939,19 @@ function MemberPicker({selectedIds: selectedIds, onSelect: onSelect, unassignRow
1870
1939
  });
1871
1940
  }
1872
1941
 
1873
- function AssigneePicker({value: value, onChange: onChange, eligibleRoles: eligibleRoles, roleAliases: roleAliases}) {
1942
+ function AssigneePicker({value: value, onChange: onChange, eligibleRoles: eligibleRoles, roleAliases: roleAliases, maxUsers: maxUsers}) {
1874
1943
  const state = useProjectMembers();
1875
1944
  /* @__PURE__ */
1876
1945
  return jsx(AssigneePicker$1, {
1877
1946
  ...state,
1878
1947
  eligibleRoles: eligibleRoles,
1948
+ maxUsers: maxUsers,
1879
1949
  onChange: onChange,
1880
1950
  roleAliases: roleAliases,
1881
1951
  value: value
1882
1952
  });
1883
1953
  }
1884
1954
 
1885
- function SingleAssigneePicker({current: current, onPick: onPick, eligibleRoles: eligibleRoles, roleAliases: roleAliases}) {
1886
- /* @__PURE__ */
1887
- return jsx(AssigneePicker, {
1888
- eligibleRoles: eligibleRoles,
1889
- onChange: next => {
1890
- const picked = singleAssigneePick(current, next);
1891
- picked !== void 0 && onPick(picked);
1892
- },
1893
- roleAliases: roleAliases,
1894
- value: current ? [ current ] : []
1895
- });
1896
- }
1897
-
1898
1955
  function MemberPickerButton({label: label = "Select member…", onChange: onChange, selectedIds: selectedIds, unassignRow: unassignRow = !1, value: value}) {
1899
1956
  const [open, setOpen] = useState(!1), {imageUrl: imageUrl} = useUserDisplay(value?.id ?? ""), selected = selectedIds ?? (value ? /* @__PURE__ */ new Set([ value.id ]) : /* @__PURE__ */ new Set), toggle = () => setOpen(v => !v);
1900
1957
  /* @__PURE__ */
@@ -2565,39 +2622,6 @@ function ParamsActionButton({actionEval: actionEval, instanceId: instanceId, act
2565
2622
  });
2566
2623
  }
2567
2624
 
2568
- const DIM = {
2569
- opacity: .6
2570
- };
2571
-
2572
- function Breadcrumb({segments: segments, emphasizeCurrent: emphasizeCurrent = !1}) {
2573
- /* @__PURE__ */
2574
- return jsx(Fragment, {
2575
- children: segments.map((segment, i) => /* @__PURE__ */ jsxs(Fragment$1, {
2576
- children: [ i > 0 ? /* @__PURE__ */ jsx(Separator, {}) : null,
2577
- /* @__PURE__ */ jsx("span", {
2578
- style: emphasizeCurrent && i < segments.length - 1 ? DIM : void 0,
2579
- children: segment
2580
- }) ]
2581
- }, `${i}-${segment}`))
2582
- });
2583
- }
2584
-
2585
- function Separator() {
2586
- /* @__PURE__ */
2587
- return jsx("span", {
2588
- "aria-hidden": !0,
2589
- style: {
2590
- ...DIM,
2591
- alignItems: "center",
2592
- display: "inline-flex",
2593
- height: "0.7em",
2594
- margin: "0 0.5em",
2595
- overflow: "hidden"
2596
- },
2597
- children: /* @__PURE__ */ jsx(ChevronRightIcon, {})
2598
- });
2599
- }
2600
-
2601
2625
  function stageTitle(definition, stageName) {
2602
2626
  return findStageNode({
2603
2627
  definition: definition,
@@ -2812,10 +2836,81 @@ function docFaceChrome(args) {
2812
2836
  }
2813
2837
  }
2814
2838
 
2839
+ function useEditIntentLink(bareId) {
2840
+ return useMemo(() => bareId === void 0 ? void 0 : function(linkProps) {
2841
+ /* @__PURE__ */
2842
+ return jsx(IntentLink, {
2843
+ ...linkProps,
2844
+ intent: "edit",
2845
+ params: {
2846
+ id: bareId
2847
+ }
2848
+ });
2849
+ }, [ bareId ]);
2850
+ }
2851
+
2852
+ function useSpaceToken(index) {
2853
+ const value = useTheme_v2().space[index];
2854
+ if (value === void 0) throw new Error(`theme is missing space token ${index}`);
2855
+ return value;
2856
+ }
2857
+
2858
+ function useRadiusToken(index) {
2859
+ const value = useTheme_v2().radius[index];
2860
+ if (value === void 0) throw new Error(`theme is missing radius token ${index}`);
2861
+ return value;
2862
+ }
2863
+
2864
+ function useContainerToken(index) {
2865
+ const value = useTheme_v2().container[index];
2866
+ if (value === void 0) throw new Error(`theme is missing container token ${index}`);
2867
+ return value;
2868
+ }
2869
+
2870
+ function useTextCapHeight() {
2871
+ const textSize = useTheme_v2().font.text.sizes[1];
2872
+ if (!textSize) throw new Error("useTextCapHeight: theme is missing text size 1");
2873
+ return textSize.lineHeight - textSize.ascenderHeight - textSize.descenderHeight;
2874
+ }
2875
+
2876
+ function useCapTrimFor(size) {
2877
+ return (useTextCapHeight() - size) / 2;
2878
+ }
2879
+
2880
+ function useTextIconSize() {
2881
+ const textSize = useTheme_v2().font.text.sizes[1];
2882
+ if (!textSize) throw new Error("useTextIconSize: theme is missing text size 1");
2883
+ return textSize.iconSize;
2884
+ }
2885
+
2886
+ function useAvatarSize() {
2887
+ const avatarSize = useTheme_v2().avatar.sizes[0];
2888
+ if (!avatarSize) throw new Error("useAvatarSize: theme is missing avatar size 0");
2889
+ return avatarSize.size;
2890
+ }
2891
+
2892
+ function useAvatarCapTrim() {
2893
+ return useCapTrimFor(useAvatarSize());
2894
+ }
2895
+
2896
+ function useBadgeCapTrim() {
2897
+ const badgePadding = useSpaceToken(1);
2898
+ return {
2899
+ marginBottom: -badgePadding,
2900
+ marginTop: -badgePadding
2901
+ };
2902
+ }
2903
+
2815
2904
  function chipPadding(fill) {
2816
2905
  return fill ? 3 : 2;
2817
2906
  }
2818
2907
 
2908
+ function useChipTrim() {
2909
+ return {
2910
+ margin: -useSpaceToken(chipPadding(!1))
2911
+ };
2912
+ }
2913
+
2819
2914
  function LinkChip({hint: hint, children: children, as: as = "a", fill: fill = !1, ...anchorProps}) {
2820
2915
  /* @__PURE__ */
2821
2916
  return jsx(HoverHint, {
@@ -3014,12 +3109,18 @@ function InlineChipFace({bareId: bareId, schemaType: schemaType}) {
3014
3109
  return jsxs(Flex, {
3015
3110
  align: "center",
3016
3111
  gap: 2,
3112
+ style: {
3113
+ minWidth: 0
3114
+ },
3017
3115
  children: [
3018
3116
  /* @__PURE__ */ jsx(Text, {
3019
3117
  size: 1,
3020
3118
  children: renderTypeIcon(schemaType.icon)
3021
3119
  }), preview.isLoading ? /* @__PURE__ */ jsx(TitleSkeleton, {}) : /* @__PURE__ */ jsx(Text, {
3022
3120
  size: 1,
3121
+ style: {
3122
+ minWidth: 0
3123
+ },
3023
3124
  textOverflow: "ellipsis",
3024
3125
  weight: "medium",
3025
3126
  children: preview.value?.title ?? /* @__PURE__ */ jsx("em", {
@@ -3080,16 +3181,7 @@ function DocRefShell({face: face, layout: layout, source: source}) {
3080
3181
  e.stopPropagation(), telemetry.log(WorkflowDocumentLinkClicked, {
3081
3182
  source: source
3082
3183
  });
3083
- }, EditIntentLink = useMemo(() => bareId === void 0 ? void 0 : function(linkProps) {
3084
- /* @__PURE__ */
3085
- return jsx(IntentLink, {
3086
- ...linkProps,
3087
- intent: "edit",
3088
- params: {
3089
- id: bareId
3090
- }
3091
- });
3092
- }, [ bareId ]);
3184
+ }, EditIntentLink = useEditIntentLink(bareId);
3093
3185
  if (isCompact(layout)) /* @__PURE__ */
3094
3186
  return jsx(CompactDocRef, {
3095
3187
  face: face,
@@ -3252,8 +3344,8 @@ const fieldValueRenderer = {
3252
3344
  array: field => /* @__PURE__ */ jsx(ArrayValueRows, {
3253
3345
  rows: field.value
3254
3346
  }),
3255
- assignee: field => field.value ? /* @__PURE__ */ jsx(AssigneeBadges, {
3256
- assignees: [ field.value ]
3347
+ assignee: field => field.value.length > 0 ? /* @__PURE__ */ jsx(AssigneeBadges, {
3348
+ assignees: field.value
3257
3349
  }) : /* @__PURE__ */ jsx(Empty, {}),
3258
3350
  assignees: field => field.value.length > 0 ? /* @__PURE__ */ jsx(AssigneeBadges, {
3259
3351
  assignees: field.value
@@ -3424,8 +3516,8 @@ function ReadOnlyAssigneeValue({value: value}) {
3424
3516
  padding: 2,
3425
3517
  radius: 2,
3426
3518
  tone: "transparent",
3427
- children: value ? /* @__PURE__ */ jsx(AssigneeBadges, {
3428
- assignees: [ value ]
3519
+ children: value.length > 0 ? /* @__PURE__ */ jsx(AssigneeBadges, {
3520
+ assignees: value
3429
3521
  }) : /* @__PURE__ */ jsx(Text, {
3430
3522
  muted: !0,
3431
3523
  size: 1,
@@ -3464,7 +3556,7 @@ function ReadOnlyValueFace({entry: entry}) {
3464
3556
  });
3465
3557
  }
3466
3558
 
3467
- const VALUE_FACE_KINDS = /* @__PURE__ */ new Set([ "assignees", "object", "array" ]);
3559
+ const VALUE_FACE_KINDS = /* @__PURE__ */ new Set([ "assignee", "assignees", "object", "array" ]);
3468
3560
 
3469
3561
  function ReadOnlyField({entry: entry}) {
3470
3562
  return isSingleDocRefEntry(entry) ? /* @__PURE__ */ jsx(DocPicker, {
@@ -3480,8 +3572,6 @@ function ReadOnlyField({entry: entry}) {
3480
3572
  value: entry.value?.releaseName ?? ""
3481
3573
  }) : entry._type === "actor" ? /* @__PURE__ */ jsx(ReadOnlyActorValue, {
3482
3574
  value: entry.value
3483
- }) : entry._type === "assignee" ? /* @__PURE__ */ jsx(ReadOnlyAssigneeValue, {
3484
- value: entry.value
3485
3575
  }) : VALUE_FACE_KINDS.has(entry._type) ? /* @__PURE__ */ jsx(ReadOnlyValueFace, {
3486
3576
  entry: entry
3487
3577
  }) : /* @__PURE__ */ jsx(FieldInput$1, {
@@ -3707,49 +3797,30 @@ function ActorField({field: field, instanceId: instanceId, onSave: onSave, onUns
3707
3797
  });
3708
3798
  }
3709
3799
 
3710
- function useAssigneePick(args) {
3711
- const {onSave: onSave, onUnset: onUnset} = args, {saving: saving, save: save} = useSaveField({
3712
- instanceId: args.instanceId
3713
- });
3714
- return {
3715
- saving: saving,
3716
- pick: picked => {
3717
- if (!saving) {
3718
- if (picked === null) {
3719
- onUnset !== void 0 && save(onUnset);
3720
- return;
3721
- }
3722
- save(() => onSave(picked));
3723
- }
3724
- }
3725
- };
3726
- }
3727
-
3728
- function AssigneeField({field: field, instanceId: instanceId, onSave: onSave, onUnset: onUnset}) {
3729
- const [open, setOpen] = useState(!1), cur = isAssigneeShape(field.value) ? field.value : null, {pick: pick} = useAssigneePick({
3730
- instanceId: instanceId,
3731
- onSave: onSave,
3732
- onUnset: onUnset
3800
+ function AssigneeField({field: field, instanceId: instanceId, onSave: onSave}) {
3801
+ const [open, setOpen] = useState(!1), cur = Array.isArray(field.value) ? field.value.filter(isAssigneeShape) : [], {saving: saving, save: save} = useSaveField({
3802
+ instanceId: instanceId
3733
3803
  });
3734
3804
  return field.editable ? /* @__PURE__ */ jsx(Flex, {
3735
3805
  align: "center",
3736
3806
  children: /* @__PURE__ */ jsx(DismissablePopover, {
3737
- content: /* @__PURE__ */ jsx(SingleAssigneePicker, {
3738
- current: cur,
3807
+ content: /* @__PURE__ */ jsx(AssigneePicker, {
3739
3808
  eligibleRoles: field.roles,
3740
- onPick: picked => {
3741
- setOpen(!1), pick(picked);
3809
+ maxUsers: 1,
3810
+ onChange: next => {
3811
+ saving || save(() => onSave(next));
3742
3812
  },
3743
- roleAliases: field.roleAliases
3813
+ roleAliases: field.roleAliases,
3814
+ value: cur
3744
3815
  }),
3745
3816
  onDismiss: () => setOpen(!1),
3746
3817
  open: open,
3747
3818
  children: /* @__PURE__ */ jsx(EditableValueButton, {
3748
- hint: cur ? "Reassign" : "Assign",
3819
+ hint: cur.length > 0 ? "Change assignment" : "Assign",
3749
3820
  hintDisabled: open,
3750
3821
  onClick: () => setOpen(v => !v),
3751
- children: cur ? /* @__PURE__ */ jsx(AssigneeBadges, {
3752
- assignees: [ cur ]
3822
+ children: cur.length > 0 ? /* @__PURE__ */ jsx(AssigneeBadges, {
3823
+ assignees: cur
3753
3824
  }) : /* @__PURE__ */ jsx(Text, {
3754
3825
  muted: !0,
3755
3826
  size: 1,
@@ -4009,8 +4080,7 @@ function fieldArm({arm: arm, entry: entry, onUnset: onUnset, onAppend: onAppend,
4009
4080
  ...arm,
4010
4081
  onUnset: onUnset
4011
4082
  }) : field.type === "assignee" ? /* @__PURE__ */ jsx(AssigneeField, {
4012
- ...arm,
4013
- onUnset: onUnset
4083
+ ...arm
4014
4084
  }) : field.type === "assignees" ? /* @__PURE__ */ jsx(ValueDisplay, {
4015
4085
  field: field,
4016
4086
  value: field.value
@@ -4071,6 +4141,17 @@ function EditableField({field: field, entry: entry, description: description, in
4071
4141
  });
4072
4142
  }
4073
4143
 
4144
+ function Hairline({weight: weight, style: style}) {
4145
+ const {color: color} = useTheme_v2();
4146
+ /* @__PURE__ */
4147
+ return jsx("div", {
4148
+ style: {
4149
+ borderTop: `${weight}px solid ${color.border}`,
4150
+ ...style
4151
+ }
4152
+ });
4153
+ }
4154
+
4074
4155
  function useDrainEffects(instanceId) {
4075
4156
  const {drainEffectsFor: drainEffectsFor} = useWorkflowContext(), toast = useWorkflowToast(), [busy, setBusy] = useState(!1);
4076
4157
  return {
@@ -4332,6 +4413,27 @@ function TaskControls({actions: actions, instanceId: instanceId, remedies: remed
4332
4413
  });
4333
4414
  }
4334
4415
 
4416
+ const LABEL = "Open in document";
4417
+
4418
+ function OpenDocumentLink({gdr: gdr, source: source}) {
4419
+ const state = useDocLink(gdr), telemetry = useWorkflowTelemetry(), link = useEditIntentLink(state.kind === "linked" ? state.bareId : void 0);
4420
+ return link === void 0 ? /* @__PURE__ */ jsx(DocPreviewLink, {
4421
+ gdr: gdr,
4422
+ layout: "inline",
4423
+ source: source
4424
+ }) : /* @__PURE__ */ jsx(Button, {
4425
+ as: link,
4426
+ fontSize: 1,
4427
+ iconRight: LaunchIcon,
4428
+ mode: "bleed",
4429
+ onClick: () => telemetry.log(WorkflowDocumentLinkClicked, {
4430
+ source: source
4431
+ }),
4432
+ padding: 2,
4433
+ text: LABEL
4434
+ });
4435
+ }
4436
+
4335
4437
  function MetaRow({children: children, fillHeight: fillHeight, label: label}) {
4336
4438
  /* @__PURE__ */
4337
4439
  return jsxs(Flex, {
@@ -4355,11 +4457,88 @@ function MetaRow({children: children, fillHeight: fillHeight, label: label}) {
4355
4457
  }),
4356
4458
  /* @__PURE__ */ jsx(Box, {
4357
4459
  flex: 1,
4460
+ style: {
4461
+ minWidth: 0
4462
+ },
4358
4463
  children: children
4359
4464
  }) ]
4360
4465
  });
4361
4466
  }
4362
4467
 
4468
+ function RowAssignees({assignees: assignees, hint: hint}) {
4469
+ const ids = userIdsOf(assignees), vocabulary = useProjectMembers(), roles = assignees.flatMap(item => item.type === "role" ? [ memberRoleFor(vocabulary, item.role) ] : []), displays = useUserDisplays(ids);
4470
+ if (ids.length === 0 && roles.length === 0) return null;
4471
+ const bothKinds = roles.length > 0 && displays.length > 0;
4472
+ /* @__PURE__ */
4473
+ return jsx(HoverHint, {
4474
+ content: /* @__PURE__ */ jsxs(Stack, {
4475
+ gap: 3,
4476
+ children: [ roles.length > 0 ? /* @__PURE__ */ jsx(HintSection, {
4477
+ names: roles.map(roleLabel),
4478
+ ...bothKinds ? {
4479
+ label: "Roles"
4480
+ } : {}
4481
+ }) : null, displays.length > 0 ? /* @__PURE__ */ jsx(HintSection, {
4482
+ names: disambiguatedNames(displays),
4483
+ ...bothKinds ? {
4484
+ label: "Members"
4485
+ } : {}
4486
+ }) : null, hint === void 0 ? null : /* @__PURE__ */ jsx(Text, {
4487
+ muted: !0,
4488
+ size: 1,
4489
+ children: hint
4490
+ }) ]
4491
+ }),
4492
+ children: /* @__PURE__ */ jsx(AssigneeStack, {
4493
+ members: displays.map(display => ({
4494
+ id: display.id,
4495
+ displayName: display.name,
4496
+ imageUrl: display.imageUrl
4497
+ })),
4498
+ roles: roles
4499
+ })
4500
+ });
4501
+ }
4502
+
4503
+ function HintSection({label: label, names: names}) {
4504
+ /* @__PURE__ */
4505
+ return jsxs(Stack, {
4506
+ gap: 2,
4507
+ children: [ label === void 0 ? null : /* @__PURE__ */ jsx(Text, {
4508
+ size: 1,
4509
+ weight: "medium",
4510
+ children: label
4511
+ }),
4512
+ /* @__PURE__ */ jsx(Text, {
4513
+ size: 1,
4514
+ children: names.join(", ")
4515
+ }) ]
4516
+ });
4517
+ }
4518
+
4519
+ function SectionHeading({children: children}) {
4520
+ /* @__PURE__ */
4521
+ return jsx(Text, {
4522
+ size: 1,
4523
+ weight: "medium",
4524
+ children: children
4525
+ });
4526
+ }
4527
+
4528
+ function OverlineHeading({children: children}) {
4529
+ /* @__PURE__ */
4530
+ return jsx(Text, {
4531
+ muted: !0,
4532
+ size: 0,
4533
+ style: {
4534
+ letterSpacing: "0.06em",
4535
+ textTransform: "uppercase"
4536
+ },
4537
+ weight: "semibold",
4538
+ children: children
4539
+ });
4540
+ }
4541
+
4363
4542
  function CheckmarkCircleFilledIcon(props) {
4364
4543
  /* @__PURE__ */
4365
4544
  return jsx("svg", {
@@ -4413,58 +4592,6 @@ function ActivityStatusIcon({status: status}) {
4413
4592
  });
4414
4593
  }
4415
4594
 
4416
- function useSpaceToken(index) {
4417
- const value = useTheme_v2().space[index];
4418
- if (value === void 0) throw new Error(`theme is missing space token ${index}`);
4419
- return value;
4420
- }
4421
-
4422
- function useRadiusToken(index) {
4423
- const value = useTheme_v2().radius[index];
4424
- if (value === void 0) throw new Error(`theme is missing radius token ${index}`);
4425
- return value;
4426
- }
4427
-
4428
- function useContainerToken(index) {
4429
- const value = useTheme_v2().container[index];
4430
- if (value === void 0) throw new Error(`theme is missing container token ${index}`);
4431
- return value;
4432
- }
4433
-
4434
- function useTextCapHeight() {
4435
- const textSize = useTheme_v2().font.text.sizes[1];
4436
- if (!textSize) throw new Error("useTextCapHeight: theme is missing text size 1");
4437
- return textSize.lineHeight - textSize.ascenderHeight - textSize.descenderHeight;
4438
- }
4439
-
4440
- function useCapTrimFor(size) {
4441
- return (useTextCapHeight() - size) / 2;
4442
- }
4443
-
4444
- function useTextIconSize() {
4445
- const textSize = useTheme_v2().font.text.sizes[1];
4446
- if (!textSize) throw new Error("useTextIconSize: theme is missing text size 1");
4447
- return textSize.iconSize;
4448
- }
4449
-
4450
- function useAvatarSize() {
4451
- const avatarSize = useTheme_v2().avatar.sizes[0];
4452
- if (!avatarSize) throw new Error("useAvatarSize: theme is missing avatar size 0");
4453
- return avatarSize.size;
4454
- }
4455
-
4456
- function useAvatarCapTrim() {
4457
- return useCapTrimFor(useAvatarSize());
4458
- }
4459
-
4460
- function useBadgeCapTrim() {
4461
- const badgePadding = useSpaceToken(1);
4462
- return {
4463
- marginBottom: -badgePadding,
4464
- marginTop: -badgePadding
4465
- };
4466
- }
4467
-
4468
4595
  function SpinnerSlot({busy: busy, reserve: reserve = !0}) {
4469
4596
  const size = useTextIconSize(), trim = useCapTrimFor(size), spinning = useDelayedFlag(busy, LOADING_CUE_DELAY_MS);
4470
4597
  return !spinning && !reserve ? null : /* @__PURE__ */ jsx(Flex, {
@@ -4555,55 +4682,8 @@ function AvatarButton({avatar: avatar, onClick: onClick, onMouseDown: onMouseDow
4555
4682
  });
4556
4683
  }
4557
4684
 
4558
- function RowAssignees({assignees: assignees, hint: hint}) {
4559
- const ids = assignees.flatMap(item => item.type === "user" ? [ item.id ] : []), vocabulary = useProjectMembers(), roles = assignees.flatMap(item => item.type === "role" ? [ memberRoleFor(vocabulary, item.role) ] : []), displays = useUserDisplays(ids);
4560
- if (ids.length === 0 && roles.length === 0) return null;
4561
- const bothKinds = roles.length > 0 && displays.length > 0;
4562
- /* @__PURE__ */
4563
- return jsx(HoverHint, {
4564
- content: /* @__PURE__ */ jsxs(Stack, {
4565
- gap: 3,
4566
- children: [ roles.length > 0 ? /* @__PURE__ */ jsx(HintSection, {
4567
- names: roles.map(roleLabel),
4568
- ...bothKinds ? {
4569
- label: "Roles"
4570
- } : {}
4571
- }) : null, displays.length > 0 ? /* @__PURE__ */ jsx(HintSection, {
4572
- names: disambiguatedNames(displays),
4573
- ...bothKinds ? {
4574
- label: "Members"
4575
- } : {}
4576
- }) : null, hint === void 0 ? null : /* @__PURE__ */ jsx(Text, {
4577
- muted: !0,
4578
- size: 1,
4579
- children: hint
4580
- }) ]
4581
- }),
4582
- children: /* @__PURE__ */ jsx(AssigneeStack, {
4583
- members: displays.map(display => ({
4584
- id: display.id,
4585
- displayName: display.name,
4586
- imageUrl: display.imageUrl
4587
- })),
4588
- roles: roles
4589
- })
4590
- });
4591
- }
4592
-
4593
- function HintSection({label: label, names: names}) {
4594
- /* @__PURE__ */
4595
- return jsxs(Stack, {
4596
- gap: 2,
4597
- children: [ label === void 0 ? null : /* @__PURE__ */ jsx(Text, {
4598
- size: 1,
4599
- weight: "medium",
4600
- children: label
4601
- }),
4602
- /* @__PURE__ */ jsx(Text, {
4603
- size: 1,
4604
- children: names.join(", ")
4605
- }) ]
4606
- });
4685
+ function assignActivityIcon(assigneeCount) {
4686
+ return assigneeCount > 0 ? AddUserIcon : UserIcon;
4607
4687
  }
4608
4688
 
4609
4689
  function AssignActivityControl({instanceId: instanceId, state: state, surface: surface, assignees: assignees = []}) {
@@ -4663,7 +4743,7 @@ function AssignActivityControl({instanceId: instanceId, state: state, surface: s
4663
4743
  "aria-label": "Assign",
4664
4744
  as: "span",
4665
4745
  fontSize: 1,
4666
- icon: UserIcon,
4746
+ icon: assignActivityIcon(current.length),
4667
4747
  mode: "bleed",
4668
4748
  onClick: handleClick,
4669
4749
  onMouseDown: stopRowMouseDown,
@@ -4696,7 +4776,7 @@ function rowMatches(row, filter) {
4696
4776
  if (filter === void 0) return !0;
4697
4777
  switch (filter.kind) {
4698
4778
  case "mine":
4699
- return isItemAssignedTo(row.item, filter.identity);
4779
+ return holdingMatchesAssignment(itemHolding(row.item, filter.identity), filter.assignment);
4700
4780
 
4701
4781
  case "activity":
4702
4782
  return row.activityName === filter.activity || row.tick?.activity === filter.activity;
@@ -4896,20 +4976,18 @@ function DueDateControl({item: item, editable: editable, onPatch: onPatch}) {
4896
4976
  }
4897
4977
 
4898
4978
  function AssigneeControl({item: item, editable: editable, eligibleRoles: eligibleRoles, onPatch: onPatch, roleAliases: roleAliases}) {
4899
- const [open, setOpen] = useState(!1), current = item.assignee ?? null, chip = current ? /* @__PURE__ */ jsx(RowAssignees, {
4900
- assignees: [ current ]
4979
+ const [open, setOpen] = useState(!1), current = item.assignee ?? [], chip = current.length > 0 ? /* @__PURE__ */ jsx(RowAssignees, {
4980
+ assignees: current
4901
4981
  }) : null;
4902
4982
  return editable ? /* @__PURE__ */ jsx(DismissablePopover, {
4903
- content:
4904
- /* @__PURE__ */ jsx(SingleAssigneePicker, {
4905
- current: current,
4983
+ content: /* @__PURE__ */ jsx(AssigneePicker, {
4906
4984
  eligibleRoles: eligibleRoles,
4907
- onPick: picked => {
4908
- setOpen(!1), onPatch({
4909
- assignee: picked
4910
- });
4911
- },
4912
- roleAliases: roleAliases
4985
+ maxUsers: 1,
4986
+ onChange: picked => onPatch({
4987
+ assignee: [ ...picked ]
4988
+ }),
4989
+ roleAliases: roleAliases,
4990
+ value: current
4913
4991
  }),
4914
4992
  onDismiss: () => setOpen(!1),
4915
4993
  open: open,
@@ -5426,7 +5504,7 @@ function ActionsMenuButton({actions: actions, instanceId: instanceId, activity:
5426
5504
 
5427
5505
  const FOOTER_GAP_PX = 8, MORE_ACTIONS_LABEL = "More actions";
5428
5506
 
5429
- function TerminalFooter({actions: actions, activity: activity, instanceId: instanceId}) {
5507
+ function TerminalFooter({actions: actions, activity: activity, instanceId: instanceId, surface: surface}) {
5430
5508
  const lock = useActionClusterState(), [only, ...rest] = actions;
5431
5509
  return only ? /* @__PURE__ */ jsx(ActionClusterProvider, {
5432
5510
  value: lock,
@@ -5436,12 +5514,13 @@ function TerminalFooter({actions: actions, activity: activity, instanceId: insta
5436
5514
  activity: activity,
5437
5515
  instanceId: instanceId,
5438
5516
  placement: "terminal-footer",
5439
- surface: "activity-dialog"
5517
+ surface: surface
5440
5518
  })
5441
5519
  }) : /* @__PURE__ */ jsx(FittedActions, {
5442
5520
  actions: actions,
5443
5521
  activity: activity,
5444
- instanceId: instanceId
5522
+ instanceId: instanceId,
5523
+ surface: surface
5445
5524
  })
5446
5525
  }) : null;
5447
5526
  }
@@ -5464,7 +5543,7 @@ function FooterRow({children: children, onElement: onElement}) {
5464
5543
  });
5465
5544
  }
5466
5545
 
5467
- function FittedActions({actions: actions, activity: activity, instanceId: instanceId}) {
5546
+ function FittedActions({actions: actions, activity: activity, instanceId: instanceId, surface: surface}) {
5468
5547
  const [footerEl, setFooterEl] = useState(null), [measureEl, setMeasureEl] = useState(null), footerSize = useElementSize(footerEl), measureSize = useElementSize(measureEl), [inline, setInline] = useState(void 0);
5469
5548
  useLayoutEffect(() => {
5470
5549
  if (measureEl === null || footerSize === null || measureSize === null) return;
@@ -5492,7 +5571,7 @@ function FittedActions({actions: actions, activity: activity, instanceId: instan
5492
5571
  activity: activity,
5493
5572
  instanceId: instanceId,
5494
5573
  placement: "terminal-footer",
5495
- surface: "activity-dialog"
5574
+ surface: surface
5496
5575
  }, a.action.name)), overflow.length > 0 ? /* @__PURE__ */ jsx(ActionsMenuButton, {
5497
5576
  actions: overflow,
5498
5577
  activity: activity,
@@ -5500,7 +5579,7 @@ function FittedActions({actions: actions, activity: activity, instanceId: instan
5500
5579
  label: MORE_ACTIONS_LABEL,
5501
5580
  mode: "default",
5502
5581
  placement: "terminal-footer",
5503
- surface: "activity-dialog"
5582
+ surface: surface
5504
5583
  }) : null ]
5505
5584
  }) ]
5506
5585
  });
@@ -5543,33 +5622,43 @@ function MeasureRow({actions: actions, activity: activity, onElement: onElement}
5543
5622
  });
5544
5623
  }
5545
5624
 
5546
- function ActivityDetailDialog({entry: entry, activityName: activityName, breadcrumb: breadcrumb, definition: definition, document: document, source: source, onClose: onClose}) {
5625
+ function ActivityDetailBody({activityName: activityName, definition: definition, document: document, entry: entry, layout: layout, source: source, surface: surface, todoSurface: todoSurface}) {
5547
5626
  const detail = deriveActivityDetail({
5548
5627
  entry: entry,
5549
5628
  activityName: activityName
5550
- }), faultingActivity2 = useInstanceFault(entry)?.activity;
5629
+ }), faultingActivity2 = useInstanceFault(entry)?.activity, identity = useAssignmentIdentity();
5551
5630
  if (!detail) return null;
5552
- const instanceId = entry.instance._id, notice = blockedNotice(detail), fault = /* @__PURE__ */ jsx(InstanceFaultNote, {
5553
- entry: entry,
5554
- scope: {
5555
- kind: "task",
5556
- activityName: activityName
5557
- }
5558
- }), faulted = faultingActivity2 === activityName, hasBody = detail.state.length > 0 || notice !== void 0 || faulted;
5631
+ const instanceId = entry.instance._id, notice = activityNotice(detail), faulted = faultingActivity2 === activityName, hasBody = detail.state.length > 0 || notice !== void 0 || faulted, common = {
5632
+ activityName: activityName,
5633
+ definition: definition,
5634
+ detail: detail,
5635
+ docLinkSource: layout === "panel" ? "task-panel-subject" : "dialog-subject",
5636
+ instanceId: instanceId,
5637
+ surface: surface
5638
+ }, work = hasBody ? /* @__PURE__ */ jsxs(Fragment, {
5639
+ children: [
5640
+ /* @__PURE__ */ jsx(InstanceFaultNote, {
5641
+ entry: entry,
5642
+ scope: {
5643
+ kind: "task",
5644
+ activityName: activityName
5645
+ }
5646
+ }), detail.state.map(field => /* @__PURE__ */ jsx(FieldRow, {
5647
+ activityName: activityName,
5648
+ detail: detail,
5649
+ entry: entry,
5650
+ field: field,
5651
+ instanceId: instanceId,
5652
+ layout: layout,
5653
+ surface: surface,
5654
+ todoSurface: todoSurface
5655
+ }, field._key)), notice ]
5656
+ }) : null, who = identityOf(identity);
5559
5657
  /* @__PURE__ */
5560
- return jsxs(Dialog, {
5561
- footer: detail.presentation.terminalActions.length > 0 ? /* @__PURE__ */ jsx(TerminalFooter, {
5562
- actions: detail.presentation.terminalActions,
5563
- activity: activityName,
5564
- instanceId: instanceId
5565
- }) : void 0,
5566
- header: /* @__PURE__ */ jsx(Breadcrumb, {
5567
- emphasizeCurrent: !0,
5568
- segments: [ ...breadcrumb, detail.title ]
5569
- }),
5570
- id: "workflow-activity-detail",
5571
- onClose: onClose,
5572
- width: 1,
5658
+ return jsxs(Flex, {
5659
+ direction: "column",
5660
+ flex: 1,
5661
+ overflow: "hidden",
5573
5662
  children: [
5574
5663
  /* @__PURE__ */ jsx(LogEventOnMount, {
5575
5664
  data: {
@@ -5577,86 +5666,264 @@ function ActivityDetailDialog({entry: entry, activityName: activityName, breadcr
5577
5666
  source: source
5578
5667
  },
5579
5668
  event: WorkflowActivityDialogOpened
5669
+ }), layout === "panel" ? /* @__PURE__ */ jsx(PanelBody, {
5670
+ ...common,
5671
+ document: document,
5672
+ holding: who === void 0 ? "other" : activityHolding({
5673
+ state: detail.state,
5674
+ who: who
5675
+ }),
5676
+ children: work
5677
+ }) : /* @__PURE__ */ jsx(DialogBody, {
5678
+ ...common,
5679
+ document: document,
5680
+ separated: hasBody,
5681
+ children: work
5682
+ }) ]
5683
+ });
5684
+ }
5685
+
5686
+ function PanelBody({children: children, document: document, holding: holding, ...props}) {
5687
+ const {definition: definition, detail: detail} = props, description = detail.activityEval.activity.description;
5688
+ /* @__PURE__ */
5689
+ return jsxs(Fragment, {
5690
+ children: [
5691
+ /* @__PURE__ */ jsx(Box, {
5692
+ flex: 1,
5693
+ overflow: "auto",
5694
+ padding: 4,
5695
+ children: /* @__PURE__ */ jsxs(Stack, {
5696
+ gap: 5,
5697
+ children: [ description ? /* @__PURE__ */ jsx(Text, {
5698
+ size: 1,
5699
+ children: description
5700
+ }) : null,
5701
+ /* @__PURE__ */ jsxs(Stack, {
5702
+ gap: 4,
5703
+ children: [
5704
+ /* @__PURE__ */ jsx(OverlineHeading, {
5705
+ children: "Details of this task"
5706
+ }),
5707
+ /* @__PURE__ */ jsx(TaskStatusRow, {
5708
+ detail: detail,
5709
+ holding: holding
5710
+ }), children,
5711
+ /* @__PURE__ */ jsx(AutomationNarration, {
5712
+ definition: definition,
5713
+ detail: detail
5714
+ }) ]
5715
+ }),
5716
+ /* @__PURE__ */ jsx(TaskActions, {
5717
+ ...props
5718
+ }) ]
5719
+ })
5720
+ }), document ? /* @__PURE__ */ jsxs(Fragment, {
5721
+ children: [
5722
+ /* @__PURE__ */ jsx(Hairline, {
5723
+ weight: 1
5724
+ }),
5725
+ /* @__PURE__ */ jsx(Box, {
5726
+ padding: 2,
5727
+ children: /* @__PURE__ */ jsx(OpenDocumentLink, {
5728
+ gdr: document,
5729
+ source: props.docLinkSource
5730
+ })
5731
+ }) ]
5732
+ }) : null ]
5733
+ });
5734
+ }
5735
+
5736
+ function TaskStatusRow({detail: detail, holding: holding}) {
5737
+ const {status: status} = detail.activityEval;
5738
+ /* @__PURE__ */
5739
+ return jsxs(Flex, {
5740
+ align: "center",
5741
+ gap: 3,
5742
+ children: [
5743
+ /* @__PURE__ */ jsx(Text, {
5744
+ muted: !0,
5745
+ size: 1,
5746
+ children: "Status"
5580
5747
  }),
5748
+ /* @__PURE__ */ jsxs(Flex, {
5749
+ align: "center",
5750
+ gap: 2,
5751
+ children: [
5752
+ /* @__PURE__ */ jsx(ActivityStatusIcon, {
5753
+ status: status
5754
+ }),
5755
+ /* @__PURE__ */ jsx(Text, {
5756
+ size: 1,
5757
+ children: taskStatusLine({
5758
+ status: status,
5759
+ holding: holding
5760
+ })
5761
+ }) ]
5762
+ }) ]
5763
+ });
5764
+ }
5765
+
5766
+ function AutomationNarration({definition: definition, detail: detail}) {
5767
+ const {triggeredActions: triggeredActions} = detail.presentation;
5768
+ return triggeredActions.length === 0 ? null : /* @__PURE__ */ jsx(Flex, {
5769
+ align: "center",
5770
+ gap: 2,
5771
+ wrap: "wrap",
5772
+ children: triggeredActions.map(a => /* @__PURE__ */ jsx(AutomationActionRow, {
5773
+ actionEval: a,
5774
+ definition: definition
5775
+ }, a.action.name))
5776
+ });
5777
+ }
5778
+
5779
+ function TaskActions({activityName: activityName, detail: detail, instanceId: instanceId, surface: surface}) {
5780
+ const {actions: actions, manualTarget: manualTarget, terminalActions: terminalActions} = detail.presentation, lock = useActionClusterState();
5781
+ return actions.length + terminalActions.length === 0 && !manualTarget ? null : /* @__PURE__ */ jsxs(Fragment, {
5782
+ children: [
5783
+ /* @__PURE__ */ jsx(Hairline, {
5784
+ weight: 1
5785
+ }),
5786
+ /* @__PURE__ */ jsxs(Stack, {
5787
+ gap: 4,
5788
+ children: [
5789
+ /* @__PURE__ */ jsx(OverlineHeading, {
5790
+ children: "Your actions"
5791
+ }),
5792
+ /* @__PURE__ */ jsx(ActionClusterProvider, {
5793
+ value: lock,
5794
+ children: /* @__PURE__ */ jsxs(Flex, {
5795
+ align: "center",
5796
+ gap: 2,
5797
+ wrap: "wrap",
5798
+ children: [ [ ...actions, ...terminalActions ].map(a => /* @__PURE__ */ jsx(ActivityActionRow, {
5799
+ actionEval: a,
5800
+ activity: activityName,
5801
+ instanceId: instanceId,
5802
+ placement: "task-actions",
5803
+ surface: surface
5804
+ }, a.action.name)), manualTarget ? /* @__PURE__ */ jsx(ManualTargetButton, {
5805
+ target: manualTarget
5806
+ }) : null ]
5807
+ })
5808
+ }) ]
5809
+ }) ]
5810
+ });
5811
+ }
5812
+
5813
+ function ManualTargetButton({target: target}) {
5814
+ /* @__PURE__ */
5815
+ return jsx(Button, {
5816
+ as: "a",
5817
+ fontSize: 1,
5818
+ href: target.href,
5819
+ mode: "ghost",
5820
+ padding: 2,
5821
+ rel: "noreferrer",
5822
+ target: "_blank",
5823
+ text: target.label,
5824
+ tone: "caution"
5825
+ });
5826
+ }
5827
+
5828
+ function DialogBody({children: children, document: document, separated: separated, ...props}) {
5829
+ const {activityName: activityName, detail: detail, instanceId: instanceId, surface: surface} = props;
5830
+ /* @__PURE__ */
5831
+ return jsxs(Fragment, {
5832
+ children: [
5833
+ /* @__PURE__ */ jsx(DialogColumn, {
5834
+ meta: /* @__PURE__ */ jsx(MetaBlock, {
5835
+ ...props,
5836
+ document: document
5837
+ }),
5838
+ separated: separated,
5839
+ children: children
5840
+ }), detail.presentation.terminalActions.length > 0 ? /* @__PURE__ */ jsx(TerminalFooter, {
5841
+ actions: detail.presentation.terminalActions,
5842
+ activity: activityName,
5843
+ instanceId: instanceId,
5844
+ surface: surface
5845
+ }) : null ]
5846
+ });
5847
+ }
5848
+
5849
+ function DialogColumn({children: children, meta: meta, separated: separated}) {
5850
+ /* @__PURE__ */
5851
+ return jsxs(Fragment, {
5852
+ children: [
5581
5853
  /* @__PURE__ */ jsx(Card, {
5582
- borderBottom: hasBody,
5854
+ borderBottom: separated,
5583
5855
  style: {
5584
5856
  position: "sticky",
5585
5857
  top: 0,
5586
5858
  zIndex: 2
5587
5859
  },
5588
- children: /* @__PURE__ */ jsx(MetaBlock, {
5589
- activityName: activityName,
5590
- definition: definition,
5591
- detail: detail,
5592
- document: document,
5593
- instanceId: instanceId
5860
+ children: /* @__PURE__ */ jsx(Box, {
5861
+ padding: 4,
5862
+ children: meta
5594
5863
  })
5595
- }), hasBody ? /* @__PURE__ */ jsx(Box, {
5864
+ }), children === null ? null : /* @__PURE__ */ jsx(Box, {
5865
+ flex: 1,
5866
+ overflow: "auto",
5596
5867
  padding: 4,
5597
- children: /* @__PURE__ */ jsxs(Stack, {
5868
+ children: /* @__PURE__ */ jsx(Stack, {
5598
5869
  gap: 5,
5599
- children: [ fault, detail.state.map(field => /* @__PURE__ */ jsx(FieldRow, {
5600
- activityName: activityName,
5601
- detail: detail,
5602
- entry: entry,
5603
- field: field,
5604
- instanceId: instanceId
5605
- }, field._key)), notice ]
5870
+ children: children
5606
5871
  })
5607
- }) : null ]
5872
+ }) ]
5608
5873
  });
5609
5874
  }
5610
5875
 
5611
- function MetaBlock({detail: detail, definition: definition, document: document, instanceId: instanceId, activityName: activityName}) {
5612
- const description = detail.activityEval.activity.description, status = detail.activityEval.status;
5876
+ function MetaBlock({document: document, ...props}) {
5877
+ const {detail: detail} = props, description = detail.activityEval.activity.description, status = detail.activityEval.status;
5613
5878
  /* @__PURE__ */
5614
- return jsx(Box, {
5615
- padding: 4,
5616
- children: /* @__PURE__ */ jsxs(Stack, {
5617
- gap: 4,
5618
- children: [ description ? /* @__PURE__ */ jsx(MetaRow, {
5619
- label: "Description",
5620
- children: /* @__PURE__ */ jsx(Text, {
5879
+ return jsxs(Stack, {
5880
+ gap: 4,
5881
+ children: [ description ? /* @__PURE__ */ jsx(MetaRow, {
5882
+ label: "Description",
5883
+ children: /* @__PURE__ */ jsx(Text, {
5884
+ size: 1,
5885
+ children: description
5886
+ })
5887
+ }) : null,
5888
+ /* @__PURE__ */ jsx(MetaRow, {
5889
+ label: "Status",
5890
+ children: /* @__PURE__ */ jsxs(Flex, {
5891
+ align: "center",
5892
+ gap: 2,
5893
+ children: [
5894
+ /* @__PURE__ */ jsx(ActivityStatusIcon, {
5895
+ status: status
5896
+ }),
5897
+ /* @__PURE__ */ jsx(Text, {
5621
5898
  size: 1,
5622
- children: description
5623
- })
5624
- }) : null,
5625
- /* @__PURE__ */ jsx(MetaRow, {
5626
- label: "Status",
5627
- children: /* @__PURE__ */ jsxs(Flex, {
5628
- align: "center",
5629
- gap: 2,
5630
- children: [
5631
- /* @__PURE__ */ jsx(ActivityStatusIcon, {
5632
- status: status
5633
- }),
5634
- /* @__PURE__ */ jsx(Text, {
5635
- size: 1,
5636
- children: ACTIVITY_STATUS_LABEL[status]
5637
- }) ]
5638
- })
5639
- }), document ? /* @__PURE__ */ jsx(MetaRow, {
5640
- label: "Studio document",
5641
- children: /* @__PURE__ */ jsx(Flex, {
5642
- children: /* @__PURE__ */ jsx(DocPreviewLink, {
5643
- gdr: document,
5644
- layout: "inline",
5645
- source: "dialog-subject"
5646
- })
5647
- })
5648
- }) : null,
5649
- /* @__PURE__ */ jsx(TopActions, {
5650
- activityName: activityName,
5651
- definition: definition,
5652
- detail: detail,
5653
- instanceId: instanceId
5654
- }) ]
5899
+ children: ACTIVITY_STATUS_LABEL[status]
5900
+ }) ]
5901
+ })
5902
+ }), document ? /* @__PURE__ */ jsx(MetaRow, {
5903
+ label: "Studio document",
5904
+ children: /* @__PURE__ */ jsx(DocumentMetaValue, {
5905
+ document: document
5906
+ })
5907
+ }) : null,
5908
+ /* @__PURE__ */ jsx(TopActions, {
5909
+ ...props
5910
+ }) ]
5911
+ });
5912
+ }
5913
+
5914
+ function DocumentMetaValue({document: document}) {
5915
+ /* @__PURE__ */
5916
+ return jsx(Box, {
5917
+ style: useChipTrim(),
5918
+ children: /* @__PURE__ */ jsx(DocPreviewLink, {
5919
+ gdr: document,
5920
+ layout: "inline",
5921
+ source: "dialog-subject"
5655
5922
  })
5656
5923
  });
5657
5924
  }
5658
5925
 
5659
- function TopActions({detail: detail, definition: definition, instanceId: instanceId, activityName: activityName}) {
5926
+ function TopActions({activityName: activityName, definition: definition, detail: detail, instanceId: instanceId, surface: surface}) {
5660
5927
  const p = detail.presentation;
5661
5928
  return p.actions.length === 0 && p.triggeredActions.length === 0 && !p.manualTarget ? null : /* @__PURE__ */ jsxs(Flex, {
5662
5929
  align: "center",
@@ -5667,17 +5934,9 @@ function TopActions({detail: detail, definition: definition, instanceId: instanc
5667
5934
  activity: activityName,
5668
5935
  instanceId: instanceId,
5669
5936
  placement: "dialog-strip",
5670
- surface: "activity-dialog"
5671
- }, a.action.name)), p.manualTarget ? /* @__PURE__ */ jsx(Button, {
5672
- as: "a",
5673
- fontSize: 1,
5674
- href: p.manualTarget.href,
5675
- mode: "ghost",
5676
- padding: 2,
5677
- rel: "noreferrer",
5678
- target: "_blank",
5679
- text: p.manualTarget.label,
5680
- tone: "caution"
5937
+ surface: surface
5938
+ }, a.action.name)), p.manualTarget ? /* @__PURE__ */ jsx(ManualTargetButton, {
5939
+ target: p.manualTarget
5681
5940
  }) : null, p.triggeredActions.map(a => /* @__PURE__ */ jsx(AutomationActionRow, {
5682
5941
  actionEval: a,
5683
5942
  definition: definition
@@ -5685,7 +5944,7 @@ function TopActions({detail: detail, definition: definition, instanceId: instanc
5685
5944
  });
5686
5945
  }
5687
5946
 
5688
- function FieldRow({field: field, detail: detail, entry: entry, activityName: activityName, instanceId: instanceId}) {
5947
+ function FieldRow({field: field, detail: detail, entry: entry, activityName: activityName, instanceId: instanceId, layout: layout, surface: surface, todoSurface: todoSurface}) {
5689
5948
  const head = /* @__PURE__ */ jsx(FieldHead, {
5690
5949
  label: field.title ?? field.name,
5691
5950
  ...field.description === void 0 ? {} : {
@@ -5705,10 +5964,10 @@ function FieldRow({field: field, detail: detail, entry: entry, activityName: act
5705
5964
  activity: activityName
5706
5965
  },
5707
5966
  showEmpty: !0,
5708
- surface: "activity-dialog"
5967
+ surface: todoSurface
5709
5968
  }) ]
5710
5969
  });
5711
- if (field._type === "assignees") {
5970
+ if (field._type === "assignee" || field._type === "assignees") {
5712
5971
  const assignField = detail.assignFields.get(field.name);
5713
5972
  /* @__PURE__ */
5714
5973
  return jsxs(Stack, {
@@ -5718,8 +5977,9 @@ function FieldRow({field: field, detail: detail, entry: entry, activityName: act
5718
5977
  align: "center",
5719
5978
  gap: 2,
5720
5979
  wrap: "wrap",
5721
- children: [
5722
- /* @__PURE__ */ jsx(FieldValue, {
5980
+ children: [ layout === "panel" ? /* @__PURE__ */ jsx(RowAssignees, {
5981
+ assignees: field.value
5982
+ }) : /* @__PURE__ */ jsx(FieldValue, {
5723
5983
  field: field
5724
5984
  }), assignField ? /* @__PURE__ */ jsx(AssignActivityControl, {
5725
5985
  instanceId: instanceId,
@@ -5727,7 +5987,7 @@ function FieldRow({field: field, detail: detail, entry: entry, activityName: act
5727
5987
  kind: "editable",
5728
5988
  field: assignField
5729
5989
  },
5730
- surface: "activity-dialog"
5990
+ surface: surface
5731
5991
  }) : null ]
5732
5992
  }) ]
5733
5993
  });
@@ -5736,7 +5996,7 @@ function FieldRow({field: field, detail: detail, entry: entry, activityName: act
5736
5996
  return editable ? /* @__PURE__ */ jsx(EditableFieldControl, {
5737
5997
  field: editable,
5738
5998
  instanceId: instanceId,
5739
- surface: "activity-dialog"
5999
+ surface: surface
5740
6000
  }) : /* @__PURE__ */ jsxs(Stack, {
5741
6001
  gap: 2,
5742
6002
  children: [ head,
@@ -5746,18 +6006,85 @@ function FieldRow({field: field, detail: detail, entry: entry, activityName: act
5746
6006
  });
5747
6007
  }
5748
6008
 
5749
- function blockedNotice(detail) {
5750
- const reason = detail.presentation.blockedReason;
5751
- if (reason) /* @__PURE__ */
5752
- return jsx(Card, {
6009
+ function activityNotice(detail) {
6010
+ const reason = detail.presentation.blockedReason, holders = detail.presentation.holderGate;
6011
+ if (!reason && holders === void 0) return;
6012
+ let content;
6013
+ return holders === void 0 ? content = /* @__PURE__ */ jsx(Text, {
6014
+ size: 1,
6015
+ children: reason === void 0 ? "" : describeDisabledReason(reason)
6016
+ }) : holders.length === 0 ? content = /* @__PURE__ */ jsx(Text, {
6017
+ size: 1,
6018
+ children: "Only a task holder can act, but no holder is selected."
6019
+ }) : content = /* @__PURE__ */ jsxs(Flex, {
6020
+ align: "center",
6021
+ gap: 2,
6022
+ wrap: "wrap",
6023
+ children: [
6024
+ /* @__PURE__ */ jsx(Text, {
6025
+ size: 1,
6026
+ children: "Only someone holding this task can act. Assigned to"
6027
+ }),
6028
+ /* @__PURE__ */ jsx(RowAssignees, {
6029
+ assignees: holders
6030
+ }) ]
6031
+ }), /* @__PURE__ */ jsx(Card, {
5753
6032
  border: !0,
5754
6033
  padding: 3,
5755
6034
  radius: 2,
5756
6035
  tone: "caution",
5757
- children: /* @__PURE__ */ jsx(Text, {
5758
- size: 1,
5759
- children: describeDisabledReason(reason)
6036
+ children: content
6037
+ });
6038
+ }
6039
+
6040
+ function ActivityDetailDialog({entry: entry, activityName: activityName, breadcrumb: breadcrumb, definition: definition, document: document, source: source, onClose: onClose}) {
6041
+ const detail = deriveActivityDetail({
6042
+ entry: entry,
6043
+ activityName: activityName
6044
+ });
6045
+ return detail ? /* @__PURE__ */ jsx(Dialog, {
6046
+ header: /* @__PURE__ */ jsx(Breadcrumb, {
6047
+ emphasizeCurrent: !0,
6048
+ segments: [ ...breadcrumb, detail.title ]
6049
+ }),
6050
+ id: "workflow-activity-detail",
6051
+ onClose: onClose,
6052
+ width: 1,
6053
+ children: /* @__PURE__ */ jsx(ActivityDetailBody, {
6054
+ activityName: activityName,
6055
+ definition: definition,
6056
+ document: document,
6057
+ entry: entry,
6058
+ layout: "dialog",
6059
+ source: source,
6060
+ surface: "activity-dialog",
6061
+ todoSurface: "activity-dialog"
5760
6062
  })
6063
+ }) : null;
6064
+ }
6065
+
6066
+ const FOR_ME_SECTION_COPY = {
6067
+ assignedTitle: "Tasks assigned to you",
6068
+ roleDescription: "Unassigned tasks, sent to role groups you're part of",
6069
+ roleTitle: "Tasks sent to your roles"
6070
+ };
6071
+
6072
+ function ForMeSectionHeading({description: description, title: title}) {
6073
+ /* @__PURE__ */
6074
+ return jsxs(Stack, {
6075
+ gap: 2,
6076
+ paddingLeft: 2,
6077
+ children: [
6078
+ /* @__PURE__ */ jsx(Text, {
6079
+ as: "h3",
6080
+ size: 2,
6081
+ weight: "semibold",
6082
+ children: title
6083
+ }), typeof description == "string" ? /* @__PURE__ */ jsx(Text, {
6084
+ muted: !0,
6085
+ size: 1,
6086
+ children: description
6087
+ }) : description ]
5761
6088
  });
5762
6089
  }
5763
6090
 
@@ -5777,42 +6104,36 @@ function CountedLabel({count: count, label: label}) {
5777
6104
  });
5778
6105
  }
5779
6106
 
5780
- function CountedHeading({count: count, title: title}) {
5781
- /* @__PURE__ */
5782
- return jsx(CountedLabel, {
5783
- count: count,
5784
- label: /* @__PURE__ */ jsx(Text, {
5785
- size: 1,
5786
- weight: "semibold",
5787
- children: title
5788
- })
5789
- });
5790
- }
5791
-
5792
- function GroupHeading({busy: busy, count: count, title: title, end: end}) {
6107
+ function GroupHeading({busy: busy, count: count, title: title, action: action}) {
5793
6108
  /* @__PURE__ */
5794
6109
  return jsxs(Flex, {
5795
6110
  align: "center",
5796
- gap: 3,
6111
+ gap: 2,
5797
6112
  paddingLeft: 2,
5798
- paddingY: end === void 0 ? 3 : 1,
6113
+ paddingY: action === void 0 ? 3 : 1,
5799
6114
  children: [
5800
- /* @__PURE__ */ jsx(CountedHeading, {
6115
+ /* @__PURE__ */ jsx(CountedLabel, {
5801
6116
  count: count,
5802
- title: title
5803
- }), busy === void 0 ? null : /* @__PURE__ */ jsx(SpinnerSlot, {
6117
+ label: /* @__PURE__ */ jsx(Text, {
6118
+ size: 2,
6119
+ weight: "medium",
6120
+ children: title
6121
+ })
6122
+ }), action, busy === void 0 ? null : /* @__PURE__ */ jsx(SpinnerSlot, {
5804
6123
  busy: busy
5805
- }), end === void 0 ? null : /* @__PURE__ */ jsxs(Fragment, {
5806
- children: [
5807
- /* @__PURE__ */ jsx(Flex, {
5808
- flex: 1
5809
- }), end ]
5810
6124
  }) ]
5811
6125
  });
5812
6126
  }
5813
6127
 
5814
6128
  const EMPTY_METRIC_OPACITY = .5;
5815
6129
 
6130
+ function labelledImgProps(label) {
6131
+ return label === void 0 ? {} : {
6132
+ "aria-label": label,
6133
+ role: "img"
6134
+ };
6135
+ }
6136
+
5816
6137
  function MetricText({children: children, tone: tone}) {
5817
6138
  return tone === void 0 ? /* @__PURE__ */ jsx(Text, {
5818
6139
  muted: !0,
@@ -5825,33 +6146,41 @@ function MetricText({children: children, tone: tone}) {
5825
6146
  });
5826
6147
  }
5827
6148
 
5828
- function MetricPair({Icon: Icon, empty: empty, face: face, tone: tone, value: value}) {
6149
+ function MetricPair({face: face, ...lockup}) {
5829
6150
  /* @__PURE__ */
5830
6151
  return jsx(HoverHint, {
5831
6152
  ...face,
5832
- children: /* @__PURE__ */ jsxs(Flex, {
5833
- align: "center",
5834
- gap: 2,
5835
- style: empty ? {
5836
- opacity: EMPTY_METRIC_OPACITY
5837
- } : void 0,
5838
- children: [
5839
- /* @__PURE__ */ jsx(MetricText, {
5840
- tone: tone,
5841
- children: /* @__PURE__ */ jsx(Icon, {
5842
- style: {
5843
- color: "inherit"
5844
- }
5845
- })
5846
- }),
5847
- /* @__PURE__ */ jsx(MetricText, {
5848
- tone: tone,
5849
- children: value
5850
- }) ]
6153
+ children: /* @__PURE__ */ jsx(MetricLockup, {
6154
+ ...lockup
5851
6155
  })
5852
6156
  });
5853
6157
  }
5854
6158
 
6159
+ function MetricLockup({Icon: Icon, empty: empty, label: label, tone: tone, value: value}) {
6160
+ /* @__PURE__ */
6161
+ return jsxs(Flex, {
6162
+ align: "center",
6163
+ gap: 2,
6164
+ style: empty ? {
6165
+ opacity: EMPTY_METRIC_OPACITY
6166
+ } : void 0,
6167
+ ...labelledImgProps(label),
6168
+ children: [
6169
+ /* @__PURE__ */ jsx(MetricText, {
6170
+ tone: tone,
6171
+ children: /* @__PURE__ */ jsx(Icon, {
6172
+ style: {
6173
+ color: "inherit"
6174
+ }
6175
+ })
6176
+ }),
6177
+ /* @__PURE__ */ jsx(MetricText, {
6178
+ tone: tone,
6179
+ children: value
6180
+ }) ]
6181
+ });
6182
+ }
6183
+
5855
6184
  function AlertGlyph({Icon: Icon, hint: hint, label: label, tone: tone}) {
5856
6185
  /* @__PURE__ */
5857
6186
  return jsx(HoverHint, {
@@ -5860,10 +6189,7 @@ function AlertGlyph({Icon: Icon, hint: hint, label: label, tone: tone}) {
5860
6189
  size: 1,
5861
6190
  tone: tone,
5862
6191
  children: /* @__PURE__ */ jsx(Icon, {
5863
- ...label === void 0 ? {} : {
5864
- "aria-label": label,
5865
- role: "img"
5866
- },
6192
+ ...labelledImgProps(label),
5867
6193
  style: {
5868
6194
  color: "inherit"
5869
6195
  }
@@ -5872,6 +6198,37 @@ function AlertGlyph({Icon: Icon, hint: hint, label: label, tone: tone}) {
5872
6198
  });
5873
6199
  }
5874
6200
 
6201
+ function AlertChip({Icon: Icon, hint: hint, label: label, tone: tone}) {
6202
+ const glyph = useTextIconSize(), disc = glyph + useSpaceToken(1) * 2;
6203
+ /* @__PURE__ */
6204
+ return jsx(HoverHint, {
6205
+ text: hint,
6206
+ children: /* @__PURE__ */ jsx(Card, {
6207
+ radius: "full",
6208
+ style: {
6209
+ alignItems: "center",
6210
+ display: "flex",
6211
+ height: disc,
6212
+ justifyContent: "center",
6213
+ width: disc
6214
+ },
6215
+ tone: tone,
6216
+ children: /* @__PURE__ */ jsx(TextWithTone, {
6217
+ size: 1,
6218
+ tone: tone,
6219
+ children: /* @__PURE__ */ jsx(Icon, {
6220
+ ...labelledImgProps(label),
6221
+ style: {
6222
+ color: "inherit",
6223
+ height: glyph,
6224
+ width: glyph
6225
+ }
6226
+ })
6227
+ })
6228
+ })
6229
+ });
6230
+ }
6231
+
5875
6232
  function ActivityDateControl({instanceId: instanceId, state: state, surface: surface, dueDates: dueDates}) {
5876
6233
  const editField = useEditField(surface), {save: save} = useSaveField({
5877
6234
  instanceId: instanceId
@@ -6222,13 +6579,10 @@ function peopleFaceOf(kind, entry) {
6222
6579
  kind: "assignees",
6223
6580
  assignees: entry?._type === "assignees" ? entry.value : []
6224
6581
  };
6225
- if (kind === "assignee") {
6226
- const value = entry?._type === "assignee" ? entry.value : null;
6227
- return {
6228
- kind: "assignees",
6229
- assignees: value ? [ value ] : []
6230
- };
6231
- }
6582
+ if (kind === "assignee") return {
6583
+ kind: "assignees",
6584
+ assignees: entry?._type === "assignee" ? entry.value : []
6585
+ };
6232
6586
  }
6233
6587
 
6234
6588
  function faceOf(args) {
@@ -6485,7 +6839,7 @@ function AvatarCapAligned({children: children}) {
6485
6839
  }
6486
6840
 
6487
6841
  function AssigneesFace({assignees: assignees}) {
6488
- const vocabulary = useProjectMembers(), userIds = assignees.flatMap(a => a.type === "user" ? [ a.id ] : []), roles = assignees.flatMap(a => a.type === "role" ? [ roleLabelFor(vocabulary, a.role) ] : []);
6842
+ const vocabulary = useProjectMembers(), userIds = userIdsOf(assignees), roles = assignees.flatMap(a => a.type === "role" ? [ roleLabelFor(vocabulary, a.role) ] : []);
6489
6843
  /* @__PURE__ */
6490
6844
  return jsxs(Fragment, {
6491
6845
  children: [ userIds.length > 0 ? /* @__PURE__ */ jsx(AvatarCapAligned, {
@@ -6786,24 +7140,21 @@ function ActorPill({entry: entry, pill: pill, editability: editability, surface:
6786
7140
  }
6787
7141
 
6788
7142
  function AssigneePill({entry: entry, pill: pill, editability: editability, surface: surface}) {
6789
- const [open, setOpen] = useState(!1), {set: set, unset: unset} = useFieldCommit({
7143
+ const [open, setOpen] = useState(!1), {set: set, saving: saving, save: save} = usePillWrite({
6790
7144
  entry: entry,
6791
7145
  editability: editability,
6792
7146
  surface: surface
6793
- }), current = pill.entry?._type === "assignee" ? pill.entry.value : null, {pick: pick} = useAssigneePick({
6794
- instanceId: entry.instance._id,
6795
- onSave: set,
6796
- onUnset: unset
6797
- });
7147
+ }), current = pill.entry?._type === "assignee" ? pill.entry.value : [];
6798
7148
  /* @__PURE__ */
6799
7149
  return jsx(PillPopover, {
6800
- content: /* @__PURE__ */ jsx(SingleAssigneePicker, {
6801
- current: current,
7150
+ content: /* @__PURE__ */ jsx(AssigneePicker, {
6802
7151
  eligibleRoles: editability.roles,
6803
- onPick: picked => {
6804
- setOpen(!1), pick(picked);
7152
+ maxUsers: 1,
7153
+ onChange: next => {
7154
+ saving || save(() => set(next));
6805
7155
  },
6806
- roleAliases: editability.roleAliases
7156
+ roleAliases: editability.roleAliases,
7157
+ value: current
6807
7158
  }),
6808
7159
  onDismiss: () => setOpen(!1),
6809
7160
  onToggle: () => setOpen(v => !v),
@@ -7095,100 +7446,6 @@ function conditionLines(description) {
7095
7446
  });
7096
7447
  }
7097
7448
 
7098
- function Hairline({weight: weight, style: style}) {
7099
- const {color: color} = useTheme_v2();
7100
- /* @__PURE__ */
7101
- return jsx("div", {
7102
- style: {
7103
- borderTop: `${weight}px solid ${color.border}`,
7104
- ...style
7105
- }
7106
- });
7107
- }
7108
-
7109
- function StageGlyph(props) {
7110
- /* @__PURE__ */
7111
- return jsxs("svg", {
7112
- "data-sanity-icon": "stage",
7113
- fill: "none",
7114
- height: "1em",
7115
- viewBox: "0 0 25 25",
7116
- width: "1em",
7117
- xmlns: "http://www.w3.org/2000/svg",
7118
- ...props,
7119
- children: [
7120
- /* @__PURE__ */ jsx("path", {
7121
- d: "M14.7998 12.5C14.7998 11.2295 13.7705 10.2002 12.5 10.2002C11.2295 10.2002 10.2002 11.2295 10.2002 12.5C10.2002 13.7705 11.2295 14.7998 12.5 14.7998V16C10.5668 16 9 14.4332 9 12.5C9 10.5668 10.5668 9 12.5 9C14.4332 9 16 10.5668 16 12.5C16 14.4332 14.4332 16 12.5 16V14.7998C13.7705 14.7998 14.7998 13.7705 14.7998 12.5Z",
7122
- fill: "currentColor"
7123
- }),
7124
- /* @__PURE__ */ jsx("path", {
7125
- d: "M4.5 12.5C4.5 12.1687 4.76863 11.9 5.1 11.9H9.5V13.1H5.1C4.76863 13.1 4.5 12.8314 4.5 12.5V12.5Z",
7126
- fill: "currentColor"
7127
- }),
7128
- /* @__PURE__ */ jsx("path", {
7129
- d: "M15.5 11.9H19.9C20.2314 11.9 20.5 12.1687 20.5 12.5V12.5C20.5 12.8314 20.2314 13.1 19.9 13.1H15.5V11.9Z",
7130
- fill: "currentColor"
7131
- }) ]
7132
- });
7133
- }
7134
-
7135
- function StageFace({completed: completed = !1, detail: detail, glyph: glyph = !0, title: title}) {
7136
- const tone = completed ? "default" : "primary";
7137
- /* @__PURE__ */
7138
- return jsxs(Flex, {
7139
- align: "center",
7140
- as: "span",
7141
- gap: 2,
7142
- children: [ glyph ? /* @__PURE__ */ jsx(TextWithTone, {
7143
- size: 1,
7144
- tone: tone,
7145
- children: completed ? /* @__PURE__ */ jsx(CheckmarkCircleFilledIcon, {
7146
- style: {
7147
- color: "inherit"
7148
- }
7149
- }) : /* @__PURE__ */ jsx(StageGlyph, {
7150
- style: {
7151
- color: "inherit"
7152
- }
7153
- })
7154
- }) : null,
7155
- /* @__PURE__ */ jsx(TextWithTone, {
7156
- size: 1,
7157
- tone: tone,
7158
- weight: "medium",
7159
- children: title
7160
- }), detail === void 0 ? null : /* @__PURE__ */ jsx(Text, {
7161
- muted: !0,
7162
- size: 1,
7163
- children: detail
7164
- }) ]
7165
- });
7166
- }
7167
-
7168
- function StageChip({completed: completed = !1, detail: detail, interactive: interactive = !1, title: title}) {
7169
- const {color: color} = useTheme_v2();
7170
- /* @__PURE__ */
7171
- return jsx(Card, {
7172
- padding: 3,
7173
- radius: 4,
7174
- style: {
7175
- boxShadow: `inset 0 0 0 1px ${completed ? color.badge.default.fg : color.focusRing}`,
7176
- cursor: interactive ? "help" : "default"
7177
- },
7178
- tone: completed ? "default" : "primary",
7179
- ...interactive ? {
7180
- tabIndex: 0
7181
- } : {},
7182
- children: /* @__PURE__ */ jsx(StageFace, {
7183
- completed: completed,
7184
- title: title,
7185
- ...detail === void 0 ? {} : {
7186
- detail: detail
7187
- }
7188
- })
7189
- });
7190
- }
7191
-
7192
7449
  function StartIncompleteBadge({style: style}) {
7193
7450
  /* @__PURE__ */
7194
7451
  return jsx(Badge, {
@@ -7219,7 +7476,7 @@ function abortedToast(title) {
7219
7476
  return {
7220
7477
  id: TOAST_ID.abort,
7221
7478
  status: "info",
7222
- title: `Aborted “${title}”`
7479
+ title: `Canceled “${title}”`
7223
7480
  };
7224
7481
  }
7225
7482
 
@@ -7228,7 +7485,7 @@ function abortAlreadySettledToast(title) {
7228
7485
  id: TOAST_ID.abort,
7229
7486
  status: "info",
7230
7487
  title: `“${title}” had already finished`,
7231
- description: "Nothing was aborted — the workflow reached a terminal state first."
7488
+ description: "Nothing was canceled — the workflow reached a terminal state first."
7232
7489
  };
7233
7490
  }
7234
7491
 
@@ -7236,8 +7493,8 @@ function abortUnconfirmedToast(args) {
7236
7493
  return {
7237
7494
  id: TOAST_ID.abort,
7238
7495
  status: "warning",
7239
- title: `Couldn’t confirm “${args.title}” was aborted`,
7240
- description: `${describeError(args.err)} — the abort may still have committed, so check the workflow’s state before trying again.`
7496
+ title: `Couldn’t confirm “${args.title}” was canceled`,
7497
+ description: `${describeError(args.err)} — the cancellation may still have committed, so check the workflow’s state before trying again.`
7241
7498
  };
7242
7499
  }
7243
7500
 
@@ -7283,7 +7540,7 @@ function AbortWorkflowDialog({entry: entry, onClose: onClose}) {
7283
7540
  mode: "bleed",
7284
7541
  onClick: onClose,
7285
7542
  padding: 2,
7286
- text: "Cancel"
7543
+ text: "Keep workflow"
7287
7544
  }),
7288
7545
  /* @__PURE__ */ jsx(Button, {
7289
7546
  disabled: reason === void 0,
@@ -7293,12 +7550,12 @@ function AbortWorkflowDialog({entry: entry, onClose: onClose}) {
7293
7550
  confirm();
7294
7551
  },
7295
7552
  padding: 2,
7296
- text: "Abort workflow",
7553
+ text: "Cancel workflow",
7297
7554
  tone: "critical"
7298
7555
  }) ]
7299
7556
  })
7300
7557
  }),
7301
- header: "Abort workflow",
7558
+ header: "Cancel workflow",
7302
7559
  id: `abort-workflow-${instance._id}`,
7303
7560
  onClose: () => {
7304
7561
  pending || onClose();
@@ -7311,7 +7568,7 @@ function AbortWorkflowDialog({entry: entry, onClose: onClose}) {
7311
7568
  children: [
7312
7569
  /* @__PURE__ */ jsxs(Text, {
7313
7570
  size: 1,
7314
- children: [ "Abort the ",
7571
+ children: [ "Cancel the ",
7315
7572
  /* @__PURE__ */ jsx("strong", {
7316
7573
  children: title
7317
7574
  }), " workflow? This cannot be undone." ]
@@ -7783,7 +8040,7 @@ function InstanceCardMenu({entry: entry, title: title}) {
7783
8040
  /* @__PURE__ */ jsx(MenuDivider, {}),
7784
8041
  /* @__PURE__ */ jsx(MenuItem, {
7785
8042
  onClick: () => setDialog("abort"),
7786
- text: "Abort workflow…",
8043
+ text: "Cancel workflow…",
7787
8044
  tone: "critical"
7788
8045
  }) ]
7789
8046
  }) : null ]
@@ -7802,90 +8059,97 @@ function InstanceCardMenu({entry: entry, title: title}) {
7802
8059
  });
7803
8060
  }
7804
8061
 
8062
+ const HeaderHoverZone = styled.div`
8063
+ position: relative;
8064
+
8065
+ /* Resting opacity cannot be inline: it outranks this hover rule. */
8066
+ & > [data-header-fill] {
8067
+ opacity: 0;
8068
+ }
8069
+
8070
+ & > button:hover ~ [data-header-fill] {
8071
+ opacity: 1;
8072
+ }
8073
+
8074
+ /* Bleed paints surrounding-card white; :not() leaves Sanity hover/open tones. */
8075
+ & [data-ui='MenuButton'] {
8076
+ cursor: pointer;
8077
+ }
8078
+
8079
+ & [data-ui='MenuButton']:not(:hover):not([aria-expanded='true']) {
8080
+ background-color: transparent;
8081
+ }
8082
+ `;
8083
+
7805
8084
  function InstanceCard({entry: entry, children: children, defaultOpen: defaultOpen}) {
7806
- const definition = useDefinition(entry), {instance: instance} = entry, settled = !isLiveEntry(entry), [open, setOpen] = useState(defaultOpen ?? !settled), title = instanceTitle(instance, definition), menuSlot = useSpaceToken(2) * 2 + useTextIconSize();
8085
+ const definition = useDefinition(entry), {instance: instance} = entry, settled = !isLiveEntry(entry), [open, setOpen] = useState(defaultOpen ?? !settled), title = instanceTitle(instance, definition);
7807
8086
  /* @__PURE__ */
7808
8087
  return jsxs(Card, {
7809
8088
  border: !0,
7810
8089
  overflow: "hidden",
7811
8090
  radius: 4,
7812
8091
  children: [
7813
- /* @__PURE__ */ jsxs(Box, {
7814
- style: {
7815
- position: "relative"
7816
- },
7817
- children: [
7818
- /* @__PURE__ */ jsx(Card, {
7819
- "aria-expanded": open,
7820
- as: "button",
7821
- onClick: () => setOpen(value => !value),
7822
- padding: 3,
7823
- style: {
7824
- display: "block",
7825
- textAlign: "left",
7826
- width: "100%"
7827
- },
7828
- ...open ? {
7829
- tone: "transparent"
7830
- } : {},
7831
- children: /* @__PURE__ */ jsxs(Flex, {
7832
- align: "center",
7833
- gap: 3,
7834
- children: [
7835
- /* @__PURE__ */ jsx(Text, {
7836
- muted: !0,
7837
- size: 1,
7838
- children: open ? /* @__PURE__ */ jsx(ChevronDownIcon, {}) : /* @__PURE__ */ jsx(ChevronRightIcon, {})
7839
- }),
7840
- /* @__PURE__ */ jsxs(Stack, {
7841
- flex: 1,
7842
- gap: 2,
7843
- style: {
7844
- minWidth: 0
7845
- },
7846
- children: [
7847
- /* @__PURE__ */ jsx(Text, {
7848
- size: 1,
7849
- textOverflow: "ellipsis",
7850
- weight: "medium",
7851
- children: title
7852
- }),
7853
- /* @__PURE__ */ jsx(HeaderStage, {
7854
- entry: entry
7855
- }) ]
7856
- }),
7857
- /* @__PURE__ */ jsxs(Flex, {
7858
- align: "center",
7859
- flex: "none",
7860
- children: [
7861
- /* @__PURE__ */ jsx(HeaderBadges, {
7862
- instance: instance,
7863
- settled: settled
7864
- }),
7865
- /* @__PURE__ */ jsx(Box, {
7866
- flex: "none",
7867
- style: {
7868
- width: menuSlot
7869
- }
7870
- }) ]
7871
- }) ]
7872
- })
8092
+ /* @__PURE__ */ jsxs(HeaderHoverZone, {
8093
+ children: [
8094
+ /* @__PURE__ */ jsx(Card, {
8095
+ "aria-expanded": open,
8096
+ "aria-label": title,
8097
+ as: "button",
8098
+ onClick: () => setOpen(value => !value),
8099
+ style: {
8100
+ backgroundColor: "transparent",
8101
+ cursor: "pointer",
8102
+ inset: 0,
8103
+ position: "absolute",
8104
+ width: "100%"
8105
+ }
7873
8106
  }),
7874
- /* @__PURE__ */ jsx(Flex, {
8107
+ /* @__PURE__ */ jsx(Card, {
8108
+ "data-header-fill": !0,
8109
+ style: {
8110
+ inset: 0,
8111
+ pointerEvents: "none",
8112
+ position: "absolute"
8113
+ },
8114
+ tone: "transparent"
8115
+ }),
8116
+ /* @__PURE__ */ jsxs(Flex, {
7875
8117
  align: "center",
8118
+ gap: 2,
8119
+ padding: 3,
7876
8120
  style: {
7877
- bottom: 0,
7878
- position: "absolute",
7879
- right: 0,
7880
- top: 0
8121
+ pointerEvents: "none",
8122
+ position: "relative"
7881
8123
  },
7882
- children: /* @__PURE__ */ jsx(Box, {
7883
- paddingRight: 3,
8124
+ children: [
8125
+ /* @__PURE__ */ jsx(HeaderTitleLine, {
8126
+ entry: entry
8127
+ }),
8128
+ /* @__PURE__ */ jsx(Box, {
8129
+ flex: "none",
8130
+ style: {
8131
+ pointerEvents: "auto"
8132
+ },
7884
8133
  children: /* @__PURE__ */ jsx(InstanceCardMenu, {
7885
8134
  entry: entry,
7886
8135
  title: title
7887
8136
  })
7888
- })
8137
+ }),
8138
+ /* @__PURE__ */ jsx(Box, {
8139
+ flex: 1
8140
+ }),
8141
+ /* @__PURE__ */ jsx(HeaderBadges, {
8142
+ instance: instance,
8143
+ settled: settled
8144
+ }),
8145
+ /* @__PURE__ */ jsx(Box, {
8146
+ paddingRight: 1,
8147
+ children: /* @__PURE__ */ jsx(Text, {
8148
+ muted: !0,
8149
+ size: 1,
8150
+ children: open ? /* @__PURE__ */ jsx(ChevronDownIcon, {}) : /* @__PURE__ */ jsx(ChevronRightIcon, {})
8151
+ })
8152
+ }) ]
7889
8153
  }) ]
7890
8154
  }), open ? /* @__PURE__ */ jsxs(Fragment, {
7891
8155
  children: [
@@ -7900,24 +8164,39 @@ function InstanceCard({entry: entry, children: children, defaultOpen: defaultOpe
7900
8164
  });
7901
8165
  }
7902
8166
 
7903
- function HeaderStage({entry: entry}) {
8167
+ function HeaderTitleLine({entry: entry}) {
7904
8168
  const definition = useDefinition(entry), badgeTrim = useBadgeCapTrim(), face = instanceStageFace({
7905
8169
  definition: definition,
7906
8170
  instance: entry.instance
7907
8171
  });
7908
8172
  /* @__PURE__ */
7909
- return jsx(Flex, {
8173
+ return jsxs(Flex, {
7910
8174
  align: "center",
7911
- children: face === void 0 ? /* @__PURE__ */ jsx(StartIncompleteBadge, {
8175
+ gap: 2,
8176
+ style: {
8177
+ minWidth: 0
8178
+ },
8179
+ children: [
8180
+ /* @__PURE__ */ jsx(Text, {
8181
+ size: 1,
8182
+ textOverflow: "ellipsis",
8183
+ weight: "medium",
8184
+ children: instanceTitle(entry.instance, definition)
8185
+ }), face === void 0 ? /* @__PURE__ */ jsx(StartIncompleteBadge, {
7912
8186
  style: badgeTrim
7913
- }) : /* @__PURE__ */ jsx(StageFace, {
7914
- ...face.detail === void 0 ? {} : {
7915
- detail: face.detail
7916
- },
7917
- completed: face.completed,
7918
- glyph: !1,
7919
- title: face.title
7920
- })
8187
+ }) : /* @__PURE__ */ jsxs(Fragment, {
8188
+ children: [
8189
+ /* @__PURE__ */ jsx(Text, {
8190
+ muted: !0,
8191
+ size: 1,
8192
+ textOverflow: "ellipsis",
8193
+ children: `/ ${face.title}`
8194
+ }), face.detail === void 0 ? null : /* @__PURE__ */ jsx(Text, {
8195
+ muted: !0,
8196
+ size: 1,
8197
+ children: face.detail
8198
+ }) ]
8199
+ }) ]
7921
8200
  });
7922
8201
  }
7923
8202
 
@@ -7928,7 +8207,10 @@ function HeaderBadges({instance: instance, settled: settled}) {
7928
8207
  align: "center",
7929
8208
  flex: "none",
7930
8209
  gap: 2,
7931
- style: badgeTrim,
8210
+ style: {
8211
+ ...badgeTrim,
8212
+ pointerEvents: "auto"
8213
+ },
7932
8214
  children: [
7933
8215
  /* @__PURE__ */ jsx(AbortedBadge, {
7934
8216
  instance: instance
@@ -7967,6 +8249,90 @@ function PartOfBand({instance: instance}) {
7967
8249
  });
7968
8250
  }
7969
8251
 
8252
+ function StageGlyph(props) {
8253
+ /* @__PURE__ */
8254
+ return jsxs("svg", {
8255
+ "data-sanity-icon": "stage",
8256
+ fill: "none",
8257
+ height: "1em",
8258
+ viewBox: "0 0 25 25",
8259
+ width: "1em",
8260
+ xmlns: "http://www.w3.org/2000/svg",
8261
+ ...props,
8262
+ children: [
8263
+ /* @__PURE__ */ jsx("path", {
8264
+ d: "M14.7998 12.5C14.7998 11.2295 13.7705 10.2002 12.5 10.2002C11.2295 10.2002 10.2002 11.2295 10.2002 12.5C10.2002 13.7705 11.2295 14.7998 12.5 14.7998V16C10.5668 16 9 14.4332 9 12.5C9 10.5668 10.5668 9 12.5 9C14.4332 9 16 10.5668 16 12.5C16 14.4332 14.4332 16 12.5 16V14.7998C13.7705 14.7998 14.7998 13.7705 14.7998 12.5Z",
8265
+ fill: "currentColor"
8266
+ }),
8267
+ /* @__PURE__ */ jsx("path", {
8268
+ d: "M4.5 12.5C4.5 12.1687 4.76863 11.9 5.1 11.9H9.5V13.1H5.1C4.76863 13.1 4.5 12.8314 4.5 12.5V12.5Z",
8269
+ fill: "currentColor"
8270
+ }),
8271
+ /* @__PURE__ */ jsx("path", {
8272
+ d: "M15.5 11.9H19.9C20.2314 11.9 20.5 12.1687 20.5 12.5V12.5C20.5 12.8314 20.2314 13.1 19.9 13.1H15.5V11.9Z",
8273
+ fill: "currentColor"
8274
+ }) ]
8275
+ });
8276
+ }
8277
+
8278
+ function StageFace({completed: completed = !1, detail: detail, title: title}) {
8279
+ const tone = completed ? "default" : "primary";
8280
+ /* @__PURE__ */
8281
+ return jsxs(Flex, {
8282
+ align: "center",
8283
+ as: "span",
8284
+ gap: 2,
8285
+ children: [
8286
+ /* @__PURE__ */ jsx(TextWithTone, {
8287
+ size: 1,
8288
+ tone: tone,
8289
+ children: completed ? /* @__PURE__ */ jsx(CheckmarkCircleFilledIcon, {
8290
+ style: {
8291
+ color: "inherit"
8292
+ }
8293
+ }) : /* @__PURE__ */ jsx(StageGlyph, {
8294
+ style: {
8295
+ color: "inherit"
8296
+ }
8297
+ })
8298
+ }),
8299
+ /* @__PURE__ */ jsx(TextWithTone, {
8300
+ size: 1,
8301
+ tone: tone,
8302
+ weight: "medium",
8303
+ children: title
8304
+ }), detail === void 0 ? null : /* @__PURE__ */ jsx(Text, {
8305
+ muted: !0,
8306
+ size: 1,
8307
+ children: detail
8308
+ }) ]
8309
+ });
8310
+ }
8311
+
8312
+ function StageChip({completed: completed = !1, detail: detail, interactive: interactive = !1, title: title}) {
8313
+ const {color: color} = useTheme_v2();
8314
+ /* @__PURE__ */
8315
+ return jsx(Card, {
8316
+ padding: 3,
8317
+ radius: 4,
8318
+ style: {
8319
+ boxShadow: `inset 0 0 0 1px ${completed ? color.badge.default.fg : color.focusRing}`,
8320
+ cursor: interactive ? "help" : "default"
8321
+ },
8322
+ tone: completed ? "default" : "primary",
8323
+ ...interactive ? {
8324
+ tabIndex: 0
8325
+ } : {},
8326
+ children: /* @__PURE__ */ jsx(StageFace, {
8327
+ completed: completed,
8328
+ title: title,
8329
+ ...detail === void 0 ? {} : {
8330
+ detail: detail
8331
+ }
8332
+ })
8333
+ });
8334
+ }
8335
+
7970
8336
  const NODE_ICON = {
7971
8337
  done: CheckmarkCircleIcon
7972
8338
  };
@@ -8033,6 +8399,7 @@ function SpineNodeView({definition: definition, node: node, transitions: transit
8033
8399
  "data-testid": `spine-node-${node.name}`,
8034
8400
  flush: !0,
8035
8401
  children: /* @__PURE__ */ jsx(InertPill, {
8402
+ filled: node.state === "upcoming" || node.state === "revisit",
8036
8403
  children: /* @__PURE__ */ jsxs(Flex, {
8037
8404
  align: "center",
8038
8405
  gap: 2,
@@ -8050,14 +8417,16 @@ function SpineNodeView({definition: definition, node: node, transitions: transit
8050
8417
  });
8051
8418
  }
8052
8419
 
8053
- function InertPill({children: children}) {
8420
+ function InertPill({children: children, filled: filled = !1}) {
8054
8421
  const {color: color} = useTheme_v2();
8055
8422
  /* @__PURE__ */
8056
8423
  return jsx(Card, {
8057
8424
  padding: 3,
8058
8425
  radius: 4,
8059
8426
  style: {
8060
- backgroundColor: "transparent",
8427
+ ...filled ? {} : {
8428
+ backgroundColor: "transparent"
8429
+ },
8061
8430
  boxShadow: `inset 0 0 0 1px ${color.border}`,
8062
8431
  cursor: "help"
8063
8432
  },
@@ -8203,9 +8572,7 @@ function InstanceSnapshotBody({entry: entry, onOpenActivity: onOpenActivity, sur
8203
8572
  /* @__PURE__ */ jsx(Card, {
8204
8573
  borderBottom: !0,
8205
8574
  padding: 2,
8206
- ...definition ? {} : {
8207
- tone: "caution"
8208
- },
8575
+ tone: definition ? "transparent" : "caution",
8209
8576
  children: definition ? /* @__PURE__ */ jsx(WorkflowSpine, {
8210
8577
  definition: definition,
8211
8578
  evaluation: entry.evaluation,
@@ -8359,21 +8726,6 @@ function InvalidDocNotice({invalid: invalid}) {
8359
8726
  });
8360
8727
  }
8361
8728
 
8362
- function RoleAssignedNotice() {
8363
- /* @__PURE__ */
8364
- return jsx(Box, {
8365
- padding: 2,
8366
- children: /* @__PURE__ */ jsx(Text, {
8367
- muted: !0,
8368
- size: 1,
8369
- style: {
8370
- fontStyle: "italic"
8371
- },
8372
- children: "Including tasks assigned to your role"
8373
- })
8374
- });
8375
- }
8376
-
8377
8729
  function TabSwitch({ariaControls: ariaControls, idPrefix: idPrefix, options: options, selected: selected, onSelect: onSelect}) {
8378
8730
  /* @__PURE__ */
8379
8731
  return jsx(TabList, {
@@ -9140,7 +9492,7 @@ function StartWorkflowMenuItem({mapping: mapping, docId: docId, initialValue: in
9140
9492
  }) : item;
9141
9493
  }
9142
9494
 
9143
- function ForMeList({entry: entry, work: work, identity: identity, onOpenActivity: onOpenActivity}) {
9495
+ function ForMeList({entry: entry, work: work, identity: identity, assignment: assignment, onOpenActivity: onOpenActivity}) {
9144
9496
  const definition = useDefinition(entry), {instance: instance} = entry, itemCount = work.openItems.length + work.doneItems.length;
9145
9497
  if (!hasAssignedRows(work)) return null;
9146
9498
  const activityState = stateByActivity(instance), breadcrumb = instanceBreadcrumb(instance, definition);
@@ -9164,7 +9516,8 @@ function ForMeList({entry: entry, work: work, identity: identity, onOpenActivity
9164
9516
  entry: entry,
9165
9517
  filter: {
9166
9518
  kind: "mine",
9167
- identity: identity
9519
+ identity: identity,
9520
+ assignment: assignment
9168
9521
  },
9169
9522
  surface: "document-view-for-me"
9170
9523
  }) : null ]
@@ -9279,10 +9632,7 @@ function WorkflowsPanel({entries: entries, documentId: documentId, docTypeMappin
9279
9632
  padding: 3,
9280
9633
  children: [
9281
9634
  /* @__PURE__ */ jsx(GroupHeading, {
9282
- busy: booting,
9283
- count: active.length,
9284
- end:
9285
- /* @__PURE__ */ jsx(Box, {
9635
+ action: /* @__PURE__ */ jsx(Flex, {
9286
9636
  style: {
9287
9637
  visibility: view === "for-me" ? "hidden" : "visible"
9288
9638
  },
@@ -9296,6 +9646,7 @@ function WorkflowsPanel({entries: entries, documentId: documentId, docTypeMappin
9296
9646
  tone: "default"
9297
9647
  })
9298
9648
  }),
9649
+ busy: booting,
9299
9650
  title: "Active Workflows"
9300
9651
  }),
9301
9652
  /* @__PURE__ */ jsx(TabSwitch, {
@@ -9399,21 +9750,50 @@ function OverviewEmptyState({mappings: mappings, docId: docId, initialValue: ini
9399
9750
 
9400
9751
  function ForMeTab({forMe: forMe, assignedWorkUnknown: assignedWorkUnknown, onOpenActivity: onOpenActivity}) {
9401
9752
  return forMe.hasRows ? /* @__PURE__ */ jsxs(Stack, {
9402
- gap: GROUP_GAP,
9753
+ gap: 6,
9403
9754
  paddingBottom: 4,
9404
- children: [ forMe.hasRoleMatched ? /* @__PURE__ */ jsx(RoleAssignedNotice, {}) : null, forMe.work.map(({entry: entry, who: who, shown: shown}) => /* @__PURE__ */ jsx(ForMeList, {
9405
- entry: entry,
9406
- identity: who,
9407
- work: shown,
9408
- onOpenActivity: activityName => onOpenActivity({
9409
- instanceId: entry.instance._id,
9410
- activityName: activityName,
9411
- stage: entry.instance.currentStage
9412
- })
9413
- }, entry.instance._id)) ]
9755
+ children: [
9756
+ /* @__PURE__ */ jsx(DocumentForMeSection, {
9757
+ assignment: "you",
9758
+ onOpenActivity: onOpenActivity,
9759
+ title: FOR_ME_SECTION_COPY.assignedTitle,
9760
+ work: forMe.assignedToUser
9761
+ }),
9762
+ /* @__PURE__ */ jsx(DocumentForMeSection, {
9763
+ assignment: "role",
9764
+ description: FOR_ME_SECTION_COPY.roleDescription,
9765
+ onOpenActivity: onOpenActivity,
9766
+ title: FOR_ME_SECTION_COPY.roleTitle,
9767
+ work: forMe.assignedToRoles
9768
+ }) ]
9414
9769
  }) : assignedWorkUnknown ? null : /* @__PURE__ */ jsx(ForMeEmptyState, {});
9415
9770
  }
9416
9771
 
9772
+ function DocumentForMeSection({assignment: assignment, description: description, onOpenActivity: onOpenActivity, title: title, work: work}) {
9773
+ return hasForMeRows(work) ? /* @__PURE__ */ jsxs(Stack, {
9774
+ gap: 3,
9775
+ children: [
9776
+ /* @__PURE__ */ jsx(ForMeSectionHeading, {
9777
+ description: description,
9778
+ title: title
9779
+ }),
9780
+ /* @__PURE__ */ jsx(Stack, {
9781
+ gap: GROUP_GAP,
9782
+ children: work.map(({entry: entry, who: who, shown: shown}) => /* @__PURE__ */ jsx(ForMeList, {
9783
+ assignment: assignment,
9784
+ entry: entry,
9785
+ identity: who,
9786
+ work: shown,
9787
+ onOpenActivity: activityName => onOpenActivity({
9788
+ instanceId: entry.instance._id,
9789
+ activityName: activityName,
9790
+ stage: entry.instance.currentStage
9791
+ })
9792
+ }, entry.instance._id))
9793
+ }) ]
9794
+ }) : null;
9795
+ }
9796
+
9417
9797
  function SectionActivityDialog({entry: entry, activityName: activityName, onClose: onClose}) {
9418
9798
  const definition = useDefinition(entry), {instance: instance} = entry;
9419
9799
  /* @__PURE__ */
@@ -9435,6 +9815,44 @@ function workflowDefaultDocumentNode() {
9435
9815
  return S => S.document().views([ S.view.form(), workflowsView(S) ]);
9436
9816
  }
9437
9817
 
9818
+ function publishedMutation(args) {
9819
+ const {draft: draft, published: published, publishedId: publishedId} = args;
9820
+ if (draft !== null) return {
9821
+ before: published === null ? null : {
9822
+ ...published,
9823
+ _id: publishedId
9824
+ },
9825
+ after: {
9826
+ ...draft,
9827
+ _id: publishedId
9828
+ },
9829
+ action: published === null ? "create" : "update"
9830
+ };
9831
+ }
9832
+
9833
+ function deletionMutation(document, id) {
9834
+ if (document !== null) return {
9835
+ before: {
9836
+ ...document,
9837
+ _id: id
9838
+ },
9839
+ after: null,
9840
+ action: "delete"
9841
+ };
9842
+ }
9843
+
9844
+ function lakeMutationForDocumentAction(args) {
9845
+ const {action: action, draft: draft, published: published} = args, publishedId = getPublishedId(args.id);
9846
+ if (action === "publish") return publishedMutation({
9847
+ draft: draft,
9848
+ published: published,
9849
+ publishedId: publishedId
9850
+ });
9851
+ if (action === "unpublish") return deletionMutation(published, publishedId);
9852
+ const current = draft ?? published;
9853
+ return deletionMutation(current, current?._id ?? args.id);
9854
+ }
9855
+
9438
9856
  function lockTitleFor(args) {
9439
9857
  const {denials: denials, entries: entries} = args;
9440
9858
  if (denials.length === 0) return;
@@ -9796,6 +10214,34 @@ function useDefaultLayout(props) {
9796
10214
  return useMemo(() => props.renderDefault(props), [ props ]);
9797
10215
  }
9798
10216
 
10217
+ const WARM_DEMAND_MS = 3e4, WARM_DEMAND_LIMIT = 4;
10218
+
10219
+ function useWarmDemand(args) {
10220
+ const {acquire: acquire, release: release} = args, warm = useRef(/* @__PURE__ */ new Map), cool = useCallback(id => {
10221
+ const expiry = warm.current.get(id);
10222
+ expiry !== void 0 && (clearTimeout(expiry), warm.current.delete(id), release(id));
10223
+ }, [ release ]);
10224
+ useEffect(() => () => {
10225
+ for (const expiry of warm.current.values()) clearTimeout(expiry);
10226
+ warm.current.clear();
10227
+ }, []);
10228
+ const acquireWarm = useCallback(id => {
10229
+ acquire(id), cool(id);
10230
+ }, [ acquire, cool ]), releaseWarm = useCallback(id => {
10231
+ const expiry = warm.current.get(id);
10232
+ for (expiry === void 0 ? acquire(id) : clearTimeout(expiry), release(id), warm.current.delete(id),
10233
+ warm.current.set(id, setTimeout(() => cool(id), WARM_DEMAND_MS)); warm.current.size > WARM_DEMAND_LIMIT; ) {
10234
+ const oldest = warm.current.keys().next().value;
10235
+ if (oldest === void 0) return;
10236
+ cool(oldest);
10237
+ }
10238
+ }, [ acquire, release, cool ]);
10239
+ return {
10240
+ acquire: acquireWarm,
10241
+ release: releaseWarm
10242
+ };
10243
+ }
10244
+
9799
10245
  const notReady = (..._args) => Promise.reject(new Error("workflow session not ready yet")), NO_REGISTERED_DOCUMENTS = [], DEMAND_RELEASE_GRACE_MS = 500;
9800
10246
 
9801
10247
  function workspaceParts(config) {
@@ -9859,17 +10305,6 @@ function DiscoverySubscription(props) {
9859
10305
  }, [ instances, invalid, registered, onSnapshot ]), null;
9860
10306
  }
9861
10307
 
9862
- function engineOptionsFrom(config) {
9863
- return {
9864
- ...config.effectHandlers ? {
9865
- effectHandlers: config.effectHandlers
9866
- } : {},
9867
- ...config.resourceClients ? {
9868
- resourceClients: config.resourceClients
9869
- } : {}
9870
- };
9871
- }
9872
-
9873
10308
  function WorkflowProvider(props) {
9874
10309
  const {config: config} = props, defaultLayout = useDefaultLayout(props), studioClient = useClient({
9875
10310
  apiVersion: WORKFLOW_API_VERSION
@@ -9883,11 +10318,18 @@ function WorkflowProvider(props) {
9883
10318
  id: `${projectId}.${contentDataset}`
9884
10319
  },
9885
10320
  tag: config.tag
9886
- }), [ projectId, workflowDataset, contentDataset, config.tag ]), engine = useWorkflowEngine({
10321
+ }), [ projectId, workflowDataset, contentDataset, config.tag ]), effects = useMemo(() => ({
10322
+ missingHandler: "skip",
10323
+ ...config.effectHandlers ? {
10324
+ handlers: config.effectHandlers
10325
+ } : {}
10326
+ }), [ config.effectHandlers ]), engine = useWorkflowEngine({
9887
10327
  workflowResource: binding.engineResource,
9888
10328
  tag: config.tag,
9889
- missingHandler: "skip",
9890
- ...engineOptionsFrom(config)
10329
+ effects: effects,
10330
+ ...config.resourceClients ? {
10331
+ resourceClients: config.resourceClients
10332
+ } : {}
9891
10333
  }), grantsFromPath = aclPathForResource({
9892
10334
  type: "dataset",
9893
10335
  id: `${projectId}.${workflowDataset}`
@@ -9897,8 +10339,14 @@ function WorkflowProvider(props) {
9897
10339
  registerActive(id), retainRegistration(id);
9898
10340
  }, [ registerActive, retainRegistration ]), unregister = useCallback(id => {
9899
10341
  unregisterActive(id), releaseRegistration(id);
9900
- }, [ unregisterActive, releaseRegistration ]), {ids: requested, acquire: requestInstance, release: releaseInstance} = useRefcountedIds(), {ids: evaluationDemand, acquire: requestEvaluation, release: releaseEvaluation} = useRefcountedIds({
10342
+ }, [ unregisterActive, releaseRegistration ]), {ids: requested, acquire: acquireRequested, release: dropRequested} = useRefcountedIds(), {acquire: requestInstance, release: releaseInstance} = useWarmDemand({
10343
+ acquire: acquireRequested,
10344
+ release: dropRequested
10345
+ }), {ids: evaluationDemand, acquire: acquireEvaluation, release: dropEvaluation} = useRefcountedIds({
9901
10346
  releaseDelayMs: DEMAND_RELEASE_GRACE_MS
10347
+ }), {acquire: requestEvaluation, release: releaseEvaluation} = useWarmDemand({
10348
+ acquire: acquireEvaluation,
10349
+ release: dropEvaluation
9902
10350
  }), [seeded, setSeeded] = useState(/* @__PURE__ */ new Map), seedInstance = useCallback(instance => {
9903
10351
  setSeeded(prev => new Map(prev).set(instance._id, instance));
9904
10352
  }, []), [startRequest2, setStartRequest] = useState(void 0), openStartDialog = useCallback(r => setStartRequest(r), []), closeStartDialog = useCallback(() => setStartRequest(void 0), []), [entriesStore] = useState(createEntriesStore);
@@ -10011,7 +10459,7 @@ function WorkflowProvider(props) {
10011
10459
  mappingStarts: starts,
10012
10460
  mappingFields: fields,
10013
10461
  latestDefinitionVersions: latestVersions,
10014
- effectHandlers: engine.effectHandlers,
10462
+ effectHandlers: engine.effects.handlers,
10015
10463
  autoStartByType: autoStartByType,
10016
10464
  autoStartDefinitions: autoStartDefinitions
10017
10465
  }), [ register, unregister, isDocResolved, entriesStore, actualTypes, requestInstance, releaseInstance, requestEvaluation, releaseEvaluation, seedInstance, openStartDialog, closeStartDialog, startRequest2, loading, discoveryInvalid, fireActionFor, editFieldFor, previewFieldFor, discardFieldPreviewFor, drainEffectsFor, completeEffectFor, engine, binding, mappings, config.previewHydration, issues, starts, fields, latestVersions, autoStartByType, autoStartDefinitions ]);
@@ -10116,7 +10564,43 @@ function logOnce(messages, logged) {
10116
10564
  for (const message of messages) logged.has(message) || (logged.add(message), console.warn(`[workflow-studio-plugin] ${message}`));
10117
10565
  }
10118
10566
 
10119
- function AssigneesInput({value: value, onChange: onChange, eligibleRoles: eligibleRoles, roleAliases: roleAliases}) {
10567
+ function sameGuardIds(previous, next) {
10568
+ return previous.length === next.length && previous.every((guard, index) => guard._id === next[index]?._id);
10569
+ }
10570
+
10571
+ function useActionLockDenials(args) {
10572
+ const [denials, setDenials] = useState([]), applyDenials = denied => setDenials(previous => sameGuardIds(previous, denied) ? previous : denied), currentMutation = useEffectEvent(() => lakeMutationForDocumentAction({
10573
+ action: args.action,
10574
+ id: args.id,
10575
+ draft: args.draft,
10576
+ published: args.published
10577
+ }));
10578
+ return useEffect(() => {
10579
+ if (applyDenials([]), args.guards.length === 0) return;
10580
+ const mutation = currentMutation();
10581
+ if (mutation === void 0) return;
10582
+ let cancelled = !1;
10583
+ return (async () => {
10584
+ const denied = await documentActionDenials({
10585
+ mutation: mutation,
10586
+ resource: args.resource,
10587
+ guards: args.guards,
10588
+ dereference: args.dereference,
10589
+ ...args.identity !== void 0 ? {
10590
+ identity: args.identity
10591
+ } : {}
10592
+ });
10593
+ cancelled || applyDenials(denied);
10594
+ })().catch(error => {
10595
+ console.error("[workflow-studio-plugin] document-action guard evaluation failed:", error);
10596
+ }), () => {
10597
+ cancelled = !0;
10598
+ };
10599
+ }, [ args.id, args.draft?._id, args.draft?._rev, args.published?._id, args.published?._rev, args.guards, args.action, args.resource, args.identity, args.dereference ]),
10600
+ denials;
10601
+ }
10602
+
10603
+ function AssigneesInput({value: value, onChange: onChange, eligibleRoles: eligibleRoles, roleAliases: roleAliases, maxUsers: maxUsers}) {
10120
10604
  const [open, setOpen] = useState(!1), remove = index => onChange(value.filter((_, i) => i !== index));
10121
10605
  /* @__PURE__ */
10122
10606
  return jsxs(Stack, {
@@ -10151,6 +10635,7 @@ function AssigneesInput({value: value, onChange: onChange, eligibleRoles: eligib
10151
10635
  children: /* @__PURE__ */ jsx(DismissablePopover, {
10152
10636
  content: /* @__PURE__ */ jsx(AssigneePicker, {
10153
10637
  eligibleRoles: eligibleRoles,
10638
+ maxUsers: maxUsers,
10154
10639
  onChange: next => onChange([ ...next ]),
10155
10640
  roleAliases: roleAliases,
10156
10641
  value: value
@@ -10517,6 +11002,13 @@ const docRefInput = ({entry: entry, docType: docType, releaseId: releaseId, valu
10517
11002
  roleAliases: roleAliases,
10518
11003
  value: value ?? []
10519
11004
  }),
11005
+ assignee: ({entry: entry, roleAliases: roleAliases, value: value, onChange: onChange}) => /* @__PURE__ */ jsx(AssigneesInput, {
11006
+ eligibleRoles: entry.roles,
11007
+ maxUsers: 1,
11008
+ onChange: onChange,
11009
+ roleAliases: roleAliases,
11010
+ value: value ?? []
11011
+ }),
10520
11012
  array: ({entry: entry, value: value, onChange: onChange}) => {
10521
11013
  const editor = arrayEditorKind(entry);
10522
11014
  if (editor === "notes") /* @__PURE__ */ return jsx(NotesStartInput, {
@@ -11346,9 +11838,9 @@ function StripLines({active: active, docId: docId, finished: finished, initialVa
11346
11838
 
11347
11839
  function lineHint(entry, identity) {
11348
11840
  const forMe = forMeWorkOf([ entry ], identity);
11349
- return forMe.work.some(({shown: shown}) => hasOpenAssignedWork(shown)) ? {
11841
+ return forMe.assignedToUser.some(({shown: shown}) => hasOpenAssignedWork(shown)) ? {
11350
11842
  content: /* @__PURE__ */ jsx(AssignedTasksHint, {
11351
- work: forMe.work
11843
+ work: forMe.assignedToUser
11352
11844
  }),
11353
11845
  wide: !0
11354
11846
  } : unassignedLineHint({
@@ -11557,31 +12049,21 @@ const workflowsTool = {
11557
12049
  }, LOCKABLE_ACTIONS = /* @__PURE__ */ new Map([ [ "delete", "delete" ], [ "publish", "publish" ], [ "unpublish", "unpublish" ] ]);
11558
12050
 
11559
12051
  function useDocumentActionLock(action, props) {
11560
- const {binding: binding} = useWorkflowContext(), identity = useCurrentUser()?.id, doc = props.draft ?? props.published, {entries: entries} = useWorkflowsForDocument(doc?._id ?? props.id), active = useMemo(() => entries.filter(isLiveEntry), [ entries ]), guards = useMemo(() => active.flatMap(entry => entry.guards ?? []), [ active ]), [denials, setDenials] = useState([]), resource = binding.contentResource, applyDenials = denied => setDenials(prev => prev.length === 0 && denied.length === 0 ? prev : denied);
11561
- return useEffect(() => {
11562
- if (!doc || guards.length === 0) {
11563
- applyDenials([]);
11564
- return;
11565
- }
11566
- let cancelled = !1;
11567
- return (async () => {
11568
- const denied = await documentActionDenials({
11569
- doc: doc,
11570
- resource: resource,
11571
- action: action,
11572
- guards: guards,
11573
- ...identity !== void 0 ? {
11574
- identity: identity
11575
- } : {}
11576
- });
11577
- cancelled || applyDenials(denied);
11578
- })().catch(err => {
11579
- console.error("[workflow-studio-plugin] document-action guard evaluation failed:", err),
11580
- cancelled || applyDenials([]);
11581
- }), () => {
11582
- cancelled = !0;
11583
- };
11584
- }, [ doc?._id, doc?._rev, guards, action, resource, identity ]), lockTitleFor({
12052
+ const {binding: binding} = useWorkflowContext(), client = useClient({
12053
+ apiVersion: WORKFLOW_API_VERSION
12054
+ }), dereference = useMemo(() => clientGuardDereference(client), [ client ]), identity = useCurrentUser()?.id, doc = props.draft ?? props.published, {entries: entries} = useWorkflowsForDocument(doc?._id ?? props.id), active = useMemo(() => entries.filter(isLiveEntry), [ entries ]), guards = useMemo(() => active.flatMap(entry => entry.guards ?? []), [ active ]), resource = binding.contentResource, denials = useActionLockDenials({
12055
+ action: action,
12056
+ id: props.id,
12057
+ draft: props.draft,
12058
+ published: props.published,
12059
+ resource: resource,
12060
+ guards: guards,
12061
+ dereference: dereference,
12062
+ ...identity !== void 0 ? {
12063
+ identity: identity
12064
+ } : {}
12065
+ });
12066
+ return lockTitleFor({
11585
12067
  denials: denials,
11586
12068
  entries: active
11587
12069
  });
@@ -11645,4 +12127,4 @@ function WorkflowRootInput(props) {
11645
12127
  });
11646
12128
  }
11647
12129
 
11648
- export { AbortWorkflowDialog, ActivityDetailDialog, ActivityLog, AgoStamp, AlertGlyph, CodeChip, CountedHeading, DismissablePopover, DocPreviewLink, DocRefFace, EmptyState, HoverHint, InstanceFaultNote, InstanceSnapshotBody, InvalidDocNotice, LoadingRow, LogEventOnMount, MetaRow, MetricPair, NoteBanner, RUN_SEARCH_PARAM, RoleAssignedNotice, RowAssignees, SpinnerSlot, TOOL_TABS, TabSwitch, UnreadableDocsNote, WORKFLOW_API_VERSION, WorkflowBoardWorkflowSelected, WorkflowDefinitionDetailViewed, WorkflowInstanceDetailViewed, WorkflowTitleSeedDrifted, WorkflowToolOpened, assigneesOf, countBy, definitionFingerprint, definitionsRouteOf, describeError, dueDatesOf, findActivity, formatShortDateTime, formatTimeAgo, gdrLocality, identityOf, instanceBreadcrumb, instanceTitle, isDueDatePast, isEvaluationStale, isLiveEntry, isOpenActivityStatus, landingState, mappingIssueDetail, namesNoDeployedDefinition, openActivityGone, openableSchemaType, parseStoredDateValue, readDeployedDefinitions, stageTitle, tabSelectionNavigates, toolRoute, toolTabState, useAssignmentIdentity, useAvatarSize, useCapTrimFor, useContainerToken, useCurrentUserId, useDefinition, useLogEventOnMount, useMinuteClock, useProjectMembers, useRadiusToken, useReleaseTitles, useSpaceToken, useTextIconSize, useUserDisplays, useWorkflowContext, useWorkflowInstanceEntry, workflowDefaultDocumentNode, workflowState, workflowStudioPlugin, workflowsView };
12130
+ export { AbortWorkflowDialog, ActivityDetailBody, ActivityDetailDialog, ActivityLog, AgoStamp, AlertChip, AlertGlyph, Breadcrumb, CodeChip, CountedLabel, DismissablePopover, DocPreviewLink, DocRefFace, EmptyState, FOR_ME_SECTION_COPY, ForMeEmptyState, ForMeSectionHeading, HoverHint, InstanceFaultNote, InstanceSnapshotBody, InvalidDocNotice, LoadingRow, LogEventOnMount, MetaRow, MetricLockup, MetricPair, NoteBanner, OverlineHeading, RUN_SEARCH_PARAM, RowAssignees, SectionHeading, SpinnerSlot, TOOL_TABS, TabSwitch, UnreadableDocsNote, WORKFLOW_API_VERSION, WorkflowBoardWorkflowSelected, WorkflowDefinitionDetailViewed, WorkflowInstanceDetailViewed, WorkflowTitleSeedDrifted, WorkflowToolOpened, activityLabel, assigneesOf, assignmentHolding, countBy, definitionFingerprint, definitionsRouteOf, deriveActivityDetail, describeError, disambiguatedNames, dueDatesOf, dueMomentSentence, findActivity, formatDate, formatShortAgo, formatShortDateTime, formatTimeAgo, gdrLocality, identityOf, instanceBreadcrumb, instanceTitle, isDueDatePast, isEvaluationStale, isHandsOff, isLiveEntry, isOpenActivityStatus, landingState, mappingIssueDetail, namesNoDeployedDefinition, openActivityGone, openableSchemaType, parseStoredDateValue, readDeployedDefinitions, shortSpanOf, stageTitle, tabSelectionNavigates, toolRoute, toolTabState, uniqueIds, useAssignmentIdentity, useAvatarSize, useCapTrimFor, useContainerToken, useCurrentUserId, useDefinition, useLogEventOnMount, useMinuteClock, useProjectMembers, useRadiusToken, useReleaseTitles, useSpaceToken, useTextIconSize, useUserDisplays, useWorkflowContext, useWorkflowInstanceEntry, userIdsOf, workflowDefaultDocumentNode, workflowState, workflowStudioPlugin, workflowsView };