@sanity/workflow-studio-plugin 0.31.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";
@@ -517,6 +521,14 @@ function formatDate(value) {
517
521
  });
518
522
  }
519
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
+
520
532
  function formatBoolean(value) {
521
533
  return value ? "True" : "False";
522
534
  }
@@ -775,19 +787,10 @@ function isOpenActivityStatus(status) {
775
787
  return !isTerminalActivityStatus(status);
776
788
  }
777
789
 
778
- function assigneeMatches(member, who) {
779
- return member.type === "user" ? member.id === who.userId : who.roles.includes(member.role);
780
- }
781
-
782
790
  function changedAssignee(current, next) {
783
791
  return next.find(item => !current.some(existing => sameAssignee(item, existing))) ?? current.find(item => !next.some(existing => sameAssignee(item, existing)));
784
792
  }
785
793
 
786
- function singleAssigneePick(current, next) {
787
- const changed = changedAssignee(current ? [ current ] : [], next);
788
- if (changed !== void 0) return current && sameAssignee(changed, current) ? null : changed;
789
- }
790
-
791
794
  function isAssigneeShape(value) {
792
795
  if (typeof value != "object" || value === null) return !1;
793
796
  const {type: type, id: id, role: role} = value;
@@ -801,7 +804,7 @@ function isActorShape(value) {
801
804
  }
802
805
 
803
806
  function assigneesOf(entries) {
804
- return entries.flatMap(entry => entry._type === "assignees" ? entry.value : []);
807
+ return [ ...assignmentMembers(entries) ];
805
808
  }
806
809
 
807
810
  function userIdsOf(assignees) {
@@ -819,43 +822,36 @@ function assigneeEdit(current, next) {
819
822
  };
820
823
  }
821
824
 
822
- function isItemAssignedTo(item, who) {
823
- return item.assignee != null && assigneeMatches(item.assignee, who);
824
- }
825
-
826
- function matchingAssignees(state, who) {
827
- 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";
828
828
  }
829
829
 
830
- function isActivityAssignedTo(args) {
831
- return matchingAssignees(args.state, args.who).length > 0;
830
+ function holdingMatchesAssignment(holding, assignment) {
831
+ return assignment === void 0 ? holding !== "other" : holding === assignment;
832
832
  }
833
833
 
834
- function isRoleAssignedTo(args) {
835
- return matchingAssignees(args.state, args.who).some(member => member.type === "role");
834
+ function activityHolding(args) {
835
+ return assignmentHolding(assigneesOf(args.state), args.who);
836
836
  }
837
837
 
838
- function isItemRoleAssignedTo(item, who) {
839
- return isItemAssignedTo(item, who) && item.assignee?.type === "role";
838
+ function itemHolding(item, who) {
839
+ return assignmentHolding(item.assignee ?? [], who);
840
840
  }
841
841
 
842
842
  function assignedWork(args) {
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 => 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({
844
844
  state: stateOf(t),
845
845
  who: who
846
- })), items = deriveTodoItems({
846
+ }), args.assignment)), items = deriveTodoItems({
847
847
  instance: instance,
848
848
  committed: committed,
849
849
  evaluation: evaluation
850
- }).rows.filter(row => isItemAssignedTo(row.item, who));
850
+ }).rows.filter(row => holdingMatchesAssignment(itemHolding(row.item, who), args.assignment));
851
851
  return {
852
852
  activities: activities,
853
853
  openItems: items.filter(row => !isTodoDone(row.item)),
854
- doneItems: items.filter(row => isTodoDone(row.item)),
855
- roleMatched: activities.some(t => isRoleAssignedTo({
856
- state: stateOf(t),
857
- who: who
858
- })) || items.some(row => isItemRoleAssignedTo(row.item, who))
854
+ doneItems: items.filter(row => isTodoDone(row.item))
859
855
  };
860
856
  }
861
857
 
@@ -880,11 +876,18 @@ function hasOpenAssignedWork(work) {
880
876
  }
881
877
 
882
878
  function assignedTaskCount(args) {
883
- 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));
884
887
  }
885
888
 
886
889
  function forMeWorkOf(entries, identity) {
887
- const work = identity ? entries.map(entry => {
890
+ const section = assignment => identity === void 0 ? [] : entries.map(entry => {
888
891
  const base = {
889
892
  instance: entry.instance,
890
893
  committed: entry.committed,
@@ -894,18 +897,22 @@ function forMeWorkOf(entries, identity) {
894
897
  return {
895
898
  entry: entry,
896
899
  who: identity,
897
- count: assignedTaskCount(base),
900
+ count: openAssignedCount(assignedWork({
901
+ ...base,
902
+ assignment: assignment
903
+ })),
898
904
  shown: assignedWork({
899
905
  ...base,
906
+ assignment: assignment,
900
907
  staleEvaluation: "keep"
901
908
  })
902
909
  };
903
- }) : [];
910
+ }), assignedToUser = section("you"), assignedToRoles = section("role");
904
911
  return {
905
- work: work,
906
- count: work.reduce((n, {count: count}) => n + count, 0),
907
- hasRows: work.some(({shown: shown}) => hasAssignedRows(shown)),
908
- 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 ])
909
916
  };
910
917
  }
911
918
 
@@ -985,8 +992,20 @@ const ACTIVITY_STATUS_LABEL = {
985
992
  done: "Done",
986
993
  failed: "Failed",
987
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
988
1002
  };
989
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
+
990
1009
  function isHandsOff(args) {
991
1010
  return args.classification === "autonomous" && args.completesWithoutCaller === "yes";
992
1011
  }
@@ -1073,9 +1092,10 @@ function deriveActivityPresentation(args) {
1073
1092
  }), blockedReason = status === "active" && actions.length > 0 && actions.every(a => !a.allowed) ? actions.find(a => a.disabledReason)?.disabledReason : void 0, ticks = tickActionNames({
1074
1093
  actions: actions,
1075
1094
  state: args.state
1076
- }), 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;
1077
1096
  return {
1078
1097
  blockedReason: blockedReason,
1098
+ holderGate: holderGate?.holders,
1079
1099
  settled: settled,
1080
1100
  triggeredActions: settled ? [] : args.activityEval.actions.filter(a => a.triggered === !0),
1081
1101
  manualTarget: kind === "manual" ? manualLink(activity) : void 0,
@@ -1203,7 +1223,7 @@ function deriveActivityDetail(args) {
1203
1223
  title: activityLabel(activityEval.activity),
1204
1224
  state: state,
1205
1225
  editableByName: new Map(activityEditables.map(f => [ f.name, f ])),
1206
- 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 ]))
1207
1227
  };
1208
1228
  }
1209
1229
 
@@ -1247,6 +1267,35 @@ function useWorkflowInstanceEntry(instanceId, options = {}) {
1247
1267
  }, [ instanceId, evaluate, requestEvaluation, releaseEvaluation ]), useHeldWorkflowEntry(instanceId);
1248
1268
  }
1249
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
+
1250
1299
  const WorkflowToolOpened = defineEvent({
1251
1300
  name: "Workflows Studio Plugin Tool Opened",
1252
1301
  version: 3,
@@ -1278,7 +1327,7 @@ const WorkflowDefinitionDetailViewed = defineEvent({
1278
1327
  description: "The form strip's instance line was clicked, opening the document view"
1279
1328
  }), WorkflowDocumentLinkClicked = defineEvent({
1280
1329
  name: "Workflows Studio Plugin Document Link Clicked",
1281
- version: 3,
1330
+ version: 4,
1282
1331
  description: "A document link was clicked, opening the referenced document's editor"
1283
1332
  }), WorkflowStartDialogOpened = defineEvent({
1284
1333
  name: "Workflows Studio Plugin Start Dialog Opened",
@@ -1294,15 +1343,15 @@ const WorkflowDefinitionDetailViewed = defineEvent({
1294
1343
  description: "A fresh document's auto-start ran — one start request per configured workflow"
1295
1344
  }), WorkflowActionControlUsed = defineEvent({
1296
1345
  name: "Workflows Studio Plugin Action Control Used",
1297
- version: 3,
1346
+ version: 4,
1298
1347
  description: "An action-firing control was used, attributed to its UI surface"
1299
1348
  }), WorkflowFieldControlUsed = defineEvent({
1300
1349
  name: "Workflows Studio Plugin Field Control Used",
1301
- version: 2,
1350
+ version: 3,
1302
1351
  description: "A field-editing control committed an edit, attributed to its UI surface"
1303
1352
  }), WorkflowActivityDialogOpened = defineEvent({
1304
1353
  name: "Workflows Studio Plugin Activity Dialog Opened",
1305
- version: 2,
1354
+ version: 3,
1306
1355
  description: "The activity detail dialog was opened"
1307
1356
  }), WorkflowBoardWorkflowSelected = defineEvent({
1308
1357
  name: "Workflows Studio Plugin Board Workflow Selected",
@@ -1314,11 +1363,11 @@ const WorkflowDefinitionDetailViewed = defineEvent({
1314
1363
  description: "A cold-start seeded preview title diverged from the live preview pipeline"
1315
1364
  }), WorkflowTodoToggled = defineEvent({
1316
1365
  name: "Workflows Studio Plugin Todo Toggled",
1317
- version: 1,
1366
+ version: 2,
1318
1367
  description: "A todo checkbox was toggled, attributed to its UI surface and write seam"
1319
1368
  }), WorkflowTodoEdited = defineEvent({
1320
1369
  name: "Workflows Studio Plugin Todo Edited",
1321
- version: 1,
1370
+ version: 2,
1322
1371
  description: "A non-toggle todo-list write, attributed to its UI surface and gesture"
1323
1372
  }), WorkflowAbortDialogOpened = defineEvent({
1324
1373
  name: "Workflows Studio Plugin Abort Dialog Opened",
@@ -1341,26 +1390,6 @@ function LogEventOnMount(props) {
1341
1390
  return useLogEventOnMount(props.event, props.data), null;
1342
1391
  }
1343
1392
 
1344
- function isEmptyValue(value) {
1345
- return value == null || value === "" || Array.isArray(value) && value.length === 0;
1346
- }
1347
-
1348
- function effectiveValidation(kind, validation) {
1349
- return kind !== "progress" ? validation : {
1350
- min: validation?.min ?? 0,
1351
- max: validation?.max ?? 100
1352
- };
1353
- }
1354
-
1355
- function scalarValidationIssue(args) {
1356
- const issues = scalarValidationIssues({
1357
- entryType: args.kind,
1358
- validation: effectiveValidation(args.kind, args.validation),
1359
- value: args.value
1360
- });
1361
- return issues === void 0 ? void 0 : `${args.label}: ${issues.join("; ")}`;
1362
- }
1363
-
1364
1393
  function countBy(rows, keysOf) {
1365
1394
  const counts = /* @__PURE__ */ new Map;
1366
1395
  for (const row of rows) for (const key of keysOf(row)) counts.set(key, (counts.get(key) ?? 0) + 1);
@@ -1478,16 +1507,18 @@ function useSelfActor() {
1478
1507
  }
1479
1508
 
1480
1509
  function useAssignmentIdentity() {
1481
- const state = useSelfState();
1482
- if (state.kind !== "resolved") return state;
1483
- const {id: id, roles: roles} = state.self;
1484
- 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
+ } : {
1485
1516
  kind: "resolved",
1486
1517
  identity: {
1487
1518
  userId: id,
1488
- roles: roles.map(r => r.name)
1519
+ roles: roles.map(role => role.name)
1489
1520
  }
1490
- };
1521
+ }, [ id, roles, state.kind ]);
1491
1522
  }
1492
1523
 
1493
1524
  function identityOf(state) {
@@ -1517,15 +1548,35 @@ function projectMembersFrom(users) {
1517
1548
  }
1518
1549
 
1519
1550
  function useProjectMembers() {
1520
- const {users: users, roles: roles, loading: loading, error: error} = useStudioProjectUsers();
1521
- return {
1522
- 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,
1523
1554
  roles: roles,
1524
1555
  loading: loading,
1525
- 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
1526
1568
  };
1527
1569
  }
1528
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
+
1529
1580
  const passthrough = raw => ({
1530
1581
  value: raw
1531
1582
  }), paramConverter = {
@@ -1888,31 +1939,19 @@ function MemberPicker({selectedIds: selectedIds, onSelect: onSelect, unassignRow
1888
1939
  });
1889
1940
  }
1890
1941
 
1891
- function AssigneePicker({value: value, onChange: onChange, eligibleRoles: eligibleRoles, roleAliases: roleAliases}) {
1942
+ function AssigneePicker({value: value, onChange: onChange, eligibleRoles: eligibleRoles, roleAliases: roleAliases, maxUsers: maxUsers}) {
1892
1943
  const state = useProjectMembers();
1893
1944
  /* @__PURE__ */
1894
1945
  return jsx(AssigneePicker$1, {
1895
1946
  ...state,
1896
1947
  eligibleRoles: eligibleRoles,
1948
+ maxUsers: maxUsers,
1897
1949
  onChange: onChange,
1898
1950
  roleAliases: roleAliases,
1899
1951
  value: value
1900
1952
  });
1901
1953
  }
1902
1954
 
1903
- function SingleAssigneePicker({current: current, onPick: onPick, eligibleRoles: eligibleRoles, roleAliases: roleAliases}) {
1904
- /* @__PURE__ */
1905
- return jsx(AssigneePicker, {
1906
- eligibleRoles: eligibleRoles,
1907
- onChange: next => {
1908
- const picked = singleAssigneePick(current, next);
1909
- picked !== void 0 && onPick(picked);
1910
- },
1911
- roleAliases: roleAliases,
1912
- value: current ? [ current ] : []
1913
- });
1914
- }
1915
-
1916
1955
  function MemberPickerButton({label: label = "Select member…", onChange: onChange, selectedIds: selectedIds, unassignRow: unassignRow = !1, value: value}) {
1917
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);
1918
1957
  /* @__PURE__ */
@@ -2583,39 +2622,6 @@ function ParamsActionButton({actionEval: actionEval, instanceId: instanceId, act
2583
2622
  });
2584
2623
  }
2585
2624
 
2586
- const DIM = {
2587
- opacity: .6
2588
- };
2589
-
2590
- function Breadcrumb({segments: segments, emphasizeCurrent: emphasizeCurrent = !1}) {
2591
- /* @__PURE__ */
2592
- return jsx(Fragment, {
2593
- children: segments.map((segment, i) => /* @__PURE__ */ jsxs(Fragment$1, {
2594
- children: [ i > 0 ? /* @__PURE__ */ jsx(Separator, {}) : null,
2595
- /* @__PURE__ */ jsx("span", {
2596
- style: emphasizeCurrent && i < segments.length - 1 ? DIM : void 0,
2597
- children: segment
2598
- }) ]
2599
- }, `${i}-${segment}`))
2600
- });
2601
- }
2602
-
2603
- function Separator() {
2604
- /* @__PURE__ */
2605
- return jsx("span", {
2606
- "aria-hidden": !0,
2607
- style: {
2608
- ...DIM,
2609
- alignItems: "center",
2610
- display: "inline-flex",
2611
- height: "0.7em",
2612
- margin: "0 0.5em",
2613
- overflow: "hidden"
2614
- },
2615
- children: /* @__PURE__ */ jsx(ChevronRightIcon, {})
2616
- });
2617
- }
2618
-
2619
2625
  function stageTitle(definition, stageName) {
2620
2626
  return findStageNode({
2621
2627
  definition: definition,
@@ -2830,10 +2836,81 @@ function docFaceChrome(args) {
2830
2836
  }
2831
2837
  }
2832
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
+
2833
2904
  function chipPadding(fill) {
2834
2905
  return fill ? 3 : 2;
2835
2906
  }
2836
2907
 
2908
+ function useChipTrim() {
2909
+ return {
2910
+ margin: -useSpaceToken(chipPadding(!1))
2911
+ };
2912
+ }
2913
+
2837
2914
  function LinkChip({hint: hint, children: children, as: as = "a", fill: fill = !1, ...anchorProps}) {
2838
2915
  /* @__PURE__ */
2839
2916
  return jsx(HoverHint, {
@@ -3032,12 +3109,18 @@ function InlineChipFace({bareId: bareId, schemaType: schemaType}) {
3032
3109
  return jsxs(Flex, {
3033
3110
  align: "center",
3034
3111
  gap: 2,
3112
+ style: {
3113
+ minWidth: 0
3114
+ },
3035
3115
  children: [
3036
3116
  /* @__PURE__ */ jsx(Text, {
3037
3117
  size: 1,
3038
3118
  children: renderTypeIcon(schemaType.icon)
3039
3119
  }), preview.isLoading ? /* @__PURE__ */ jsx(TitleSkeleton, {}) : /* @__PURE__ */ jsx(Text, {
3040
3120
  size: 1,
3121
+ style: {
3122
+ minWidth: 0
3123
+ },
3041
3124
  textOverflow: "ellipsis",
3042
3125
  weight: "medium",
3043
3126
  children: preview.value?.title ?? /* @__PURE__ */ jsx("em", {
@@ -3098,16 +3181,7 @@ function DocRefShell({face: face, layout: layout, source: source}) {
3098
3181
  e.stopPropagation(), telemetry.log(WorkflowDocumentLinkClicked, {
3099
3182
  source: source
3100
3183
  });
3101
- }, EditIntentLink = useMemo(() => bareId === void 0 ? void 0 : function(linkProps) {
3102
- /* @__PURE__ */
3103
- return jsx(IntentLink, {
3104
- ...linkProps,
3105
- intent: "edit",
3106
- params: {
3107
- id: bareId
3108
- }
3109
- });
3110
- }, [ bareId ]);
3184
+ }, EditIntentLink = useEditIntentLink(bareId);
3111
3185
  if (isCompact(layout)) /* @__PURE__ */
3112
3186
  return jsx(CompactDocRef, {
3113
3187
  face: face,
@@ -3270,8 +3344,8 @@ const fieldValueRenderer = {
3270
3344
  array: field => /* @__PURE__ */ jsx(ArrayValueRows, {
3271
3345
  rows: field.value
3272
3346
  }),
3273
- assignee: field => field.value ? /* @__PURE__ */ jsx(AssigneeBadges, {
3274
- assignees: [ field.value ]
3347
+ assignee: field => field.value.length > 0 ? /* @__PURE__ */ jsx(AssigneeBadges, {
3348
+ assignees: field.value
3275
3349
  }) : /* @__PURE__ */ jsx(Empty, {}),
3276
3350
  assignees: field => field.value.length > 0 ? /* @__PURE__ */ jsx(AssigneeBadges, {
3277
3351
  assignees: field.value
@@ -3442,8 +3516,8 @@ function ReadOnlyAssigneeValue({value: value}) {
3442
3516
  padding: 2,
3443
3517
  radius: 2,
3444
3518
  tone: "transparent",
3445
- children: value ? /* @__PURE__ */ jsx(AssigneeBadges, {
3446
- assignees: [ value ]
3519
+ children: value.length > 0 ? /* @__PURE__ */ jsx(AssigneeBadges, {
3520
+ assignees: value
3447
3521
  }) : /* @__PURE__ */ jsx(Text, {
3448
3522
  muted: !0,
3449
3523
  size: 1,
@@ -3482,7 +3556,7 @@ function ReadOnlyValueFace({entry: entry}) {
3482
3556
  });
3483
3557
  }
3484
3558
 
3485
- const VALUE_FACE_KINDS = /* @__PURE__ */ new Set([ "assignees", "object", "array" ]);
3559
+ const VALUE_FACE_KINDS = /* @__PURE__ */ new Set([ "assignee", "assignees", "object", "array" ]);
3486
3560
 
3487
3561
  function ReadOnlyField({entry: entry}) {
3488
3562
  return isSingleDocRefEntry(entry) ? /* @__PURE__ */ jsx(DocPicker, {
@@ -3498,8 +3572,6 @@ function ReadOnlyField({entry: entry}) {
3498
3572
  value: entry.value?.releaseName ?? ""
3499
3573
  }) : entry._type === "actor" ? /* @__PURE__ */ jsx(ReadOnlyActorValue, {
3500
3574
  value: entry.value
3501
- }) : entry._type === "assignee" ? /* @__PURE__ */ jsx(ReadOnlyAssigneeValue, {
3502
- value: entry.value
3503
3575
  }) : VALUE_FACE_KINDS.has(entry._type) ? /* @__PURE__ */ jsx(ReadOnlyValueFace, {
3504
3576
  entry: entry
3505
3577
  }) : /* @__PURE__ */ jsx(FieldInput$1, {
@@ -3725,49 +3797,30 @@ function ActorField({field: field, instanceId: instanceId, onSave: onSave, onUns
3725
3797
  });
3726
3798
  }
3727
3799
 
3728
- function useAssigneePick(args) {
3729
- const {onSave: onSave, onUnset: onUnset} = args, {saving: saving, save: save} = useSaveField({
3730
- instanceId: args.instanceId
3731
- });
3732
- return {
3733
- saving: saving,
3734
- pick: picked => {
3735
- if (!saving) {
3736
- if (picked === null) {
3737
- onUnset !== void 0 && save(onUnset);
3738
- return;
3739
- }
3740
- save(() => onSave(picked));
3741
- }
3742
- }
3743
- };
3744
- }
3745
-
3746
- function AssigneeField({field: field, instanceId: instanceId, onSave: onSave, onUnset: onUnset}) {
3747
- const [open, setOpen] = useState(!1), cur = isAssigneeShape(field.value) ? field.value : null, {pick: pick} = useAssigneePick({
3748
- instanceId: instanceId,
3749
- onSave: onSave,
3750
- 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
3751
3803
  });
3752
3804
  return field.editable ? /* @__PURE__ */ jsx(Flex, {
3753
3805
  align: "center",
3754
3806
  children: /* @__PURE__ */ jsx(DismissablePopover, {
3755
- content: /* @__PURE__ */ jsx(SingleAssigneePicker, {
3756
- current: cur,
3807
+ content: /* @__PURE__ */ jsx(AssigneePicker, {
3757
3808
  eligibleRoles: field.roles,
3758
- onPick: picked => {
3759
- setOpen(!1), pick(picked);
3809
+ maxUsers: 1,
3810
+ onChange: next => {
3811
+ saving || save(() => onSave(next));
3760
3812
  },
3761
- roleAliases: field.roleAliases
3813
+ roleAliases: field.roleAliases,
3814
+ value: cur
3762
3815
  }),
3763
3816
  onDismiss: () => setOpen(!1),
3764
3817
  open: open,
3765
3818
  children: /* @__PURE__ */ jsx(EditableValueButton, {
3766
- hint: cur ? "Reassign" : "Assign",
3819
+ hint: cur.length > 0 ? "Change assignment" : "Assign",
3767
3820
  hintDisabled: open,
3768
3821
  onClick: () => setOpen(v => !v),
3769
- children: cur ? /* @__PURE__ */ jsx(AssigneeBadges, {
3770
- assignees: [ cur ]
3822
+ children: cur.length > 0 ? /* @__PURE__ */ jsx(AssigneeBadges, {
3823
+ assignees: cur
3771
3824
  }) : /* @__PURE__ */ jsx(Text, {
3772
3825
  muted: !0,
3773
3826
  size: 1,
@@ -4027,8 +4080,7 @@ function fieldArm({arm: arm, entry: entry, onUnset: onUnset, onAppend: onAppend,
4027
4080
  ...arm,
4028
4081
  onUnset: onUnset
4029
4082
  }) : field.type === "assignee" ? /* @__PURE__ */ jsx(AssigneeField, {
4030
- ...arm,
4031
- onUnset: onUnset
4083
+ ...arm
4032
4084
  }) : field.type === "assignees" ? /* @__PURE__ */ jsx(ValueDisplay, {
4033
4085
  field: field,
4034
4086
  value: field.value
@@ -4089,12 +4141,23 @@ function EditableField({field: field, entry: entry, description: description, in
4089
4141
  });
4090
4142
  }
4091
4143
 
4092
- function useDrainEffects(instanceId) {
4093
- const {drainEffectsFor: drainEffectsFor} = useWorkflowContext(), toast = useWorkflowToast(), [busy, setBusy] = useState(!1);
4094
- return {
4095
- busy: busy,
4096
- run: async () => {
4097
- setBusy(!0);
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
+
4155
+ function useDrainEffects(instanceId) {
4156
+ const {drainEffectsFor: drainEffectsFor} = useWorkflowContext(), toast = useWorkflowToast(), [busy, setBusy] = useState(!1);
4157
+ return {
4158
+ busy: busy,
4159
+ run: async () => {
4160
+ setBusy(!0);
4098
4161
  try {
4099
4162
  await drainEffectsFor(instanceId), toast.push({
4100
4163
  id: TOAST_ID.effectsDrain,
@@ -4350,6 +4413,27 @@ function TaskControls({actions: actions, instanceId: instanceId, remedies: remed
4350
4413
  });
4351
4414
  }
4352
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
+
4353
4437
  function MetaRow({children: children, fillHeight: fillHeight, label: label}) {
4354
4438
  /* @__PURE__ */
4355
4439
  return jsxs(Flex, {
@@ -4373,11 +4457,88 @@ function MetaRow({children: children, fillHeight: fillHeight, label: label}) {
4373
4457
  }),
4374
4458
  /* @__PURE__ */ jsx(Box, {
4375
4459
  flex: 1,
4460
+ style: {
4461
+ minWidth: 0
4462
+ },
4376
4463
  children: children
4377
4464
  }) ]
4378
4465
  });
4379
4466
  }
4380
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
+
4381
4542
  function CheckmarkCircleFilledIcon(props) {
4382
4543
  /* @__PURE__ */
4383
4544
  return jsx("svg", {
@@ -4431,58 +4592,6 @@ function ActivityStatusIcon({status: status}) {
4431
4592
  });
4432
4593
  }
4433
4594
 
4434
- function useSpaceToken(index) {
4435
- const value = useTheme_v2().space[index];
4436
- if (value === void 0) throw new Error(`theme is missing space token ${index}`);
4437
- return value;
4438
- }
4439
-
4440
- function useRadiusToken(index) {
4441
- const value = useTheme_v2().radius[index];
4442
- if (value === void 0) throw new Error(`theme is missing radius token ${index}`);
4443
- return value;
4444
- }
4445
-
4446
- function useContainerToken(index) {
4447
- const value = useTheme_v2().container[index];
4448
- if (value === void 0) throw new Error(`theme is missing container token ${index}`);
4449
- return value;
4450
- }
4451
-
4452
- function useTextCapHeight() {
4453
- const textSize = useTheme_v2().font.text.sizes[1];
4454
- if (!textSize) throw new Error("useTextCapHeight: theme is missing text size 1");
4455
- return textSize.lineHeight - textSize.ascenderHeight - textSize.descenderHeight;
4456
- }
4457
-
4458
- function useCapTrimFor(size) {
4459
- return (useTextCapHeight() - size) / 2;
4460
- }
4461
-
4462
- function useTextIconSize() {
4463
- const textSize = useTheme_v2().font.text.sizes[1];
4464
- if (!textSize) throw new Error("useTextIconSize: theme is missing text size 1");
4465
- return textSize.iconSize;
4466
- }
4467
-
4468
- function useAvatarSize() {
4469
- const avatarSize = useTheme_v2().avatar.sizes[0];
4470
- if (!avatarSize) throw new Error("useAvatarSize: theme is missing avatar size 0");
4471
- return avatarSize.size;
4472
- }
4473
-
4474
- function useAvatarCapTrim() {
4475
- return useCapTrimFor(useAvatarSize());
4476
- }
4477
-
4478
- function useBadgeCapTrim() {
4479
- const badgePadding = useSpaceToken(1);
4480
- return {
4481
- marginBottom: -badgePadding,
4482
- marginTop: -badgePadding
4483
- };
4484
- }
4485
-
4486
4595
  function SpinnerSlot({busy: busy, reserve: reserve = !0}) {
4487
4596
  const size = useTextIconSize(), trim = useCapTrimFor(size), spinning = useDelayedFlag(busy, LOADING_CUE_DELAY_MS);
4488
4597
  return !spinning && !reserve ? null : /* @__PURE__ */ jsx(Flex, {
@@ -4573,55 +4682,8 @@ function AvatarButton({avatar: avatar, onClick: onClick, onMouseDown: onMouseDow
4573
4682
  });
4574
4683
  }
4575
4684
 
4576
- function RowAssignees({assignees: assignees, hint: hint}) {
4577
- const ids = userIdsOf(assignees), vocabulary = useProjectMembers(), roles = assignees.flatMap(item => item.type === "role" ? [ memberRoleFor(vocabulary, item.role) ] : []), displays = useUserDisplays(ids);
4578
- if (ids.length === 0 && roles.length === 0) return null;
4579
- const bothKinds = roles.length > 0 && displays.length > 0;
4580
- /* @__PURE__ */
4581
- return jsx(HoverHint, {
4582
- content: /* @__PURE__ */ jsxs(Stack, {
4583
- gap: 3,
4584
- children: [ roles.length > 0 ? /* @__PURE__ */ jsx(HintSection, {
4585
- names: roles.map(roleLabel),
4586
- ...bothKinds ? {
4587
- label: "Roles"
4588
- } : {}
4589
- }) : null, displays.length > 0 ? /* @__PURE__ */ jsx(HintSection, {
4590
- names: disambiguatedNames(displays),
4591
- ...bothKinds ? {
4592
- label: "Members"
4593
- } : {}
4594
- }) : null, hint === void 0 ? null : /* @__PURE__ */ jsx(Text, {
4595
- muted: !0,
4596
- size: 1,
4597
- children: hint
4598
- }) ]
4599
- }),
4600
- children: /* @__PURE__ */ jsx(AssigneeStack, {
4601
- members: displays.map(display => ({
4602
- id: display.id,
4603
- displayName: display.name,
4604
- imageUrl: display.imageUrl
4605
- })),
4606
- roles: roles
4607
- })
4608
- });
4609
- }
4610
-
4611
- function HintSection({label: label, names: names}) {
4612
- /* @__PURE__ */
4613
- return jsxs(Stack, {
4614
- gap: 2,
4615
- children: [ label === void 0 ? null : /* @__PURE__ */ jsx(Text, {
4616
- size: 1,
4617
- weight: "medium",
4618
- children: label
4619
- }),
4620
- /* @__PURE__ */ jsx(Text, {
4621
- size: 1,
4622
- children: names.join(", ")
4623
- }) ]
4624
- });
4685
+ function assignActivityIcon(assigneeCount) {
4686
+ return assigneeCount > 0 ? AddUserIcon : UserIcon;
4625
4687
  }
4626
4688
 
4627
4689
  function AssignActivityControl({instanceId: instanceId, state: state, surface: surface, assignees: assignees = []}) {
@@ -4681,7 +4743,7 @@ function AssignActivityControl({instanceId: instanceId, state: state, surface: s
4681
4743
  "aria-label": "Assign",
4682
4744
  as: "span",
4683
4745
  fontSize: 1,
4684
- icon: UserIcon,
4746
+ icon: assignActivityIcon(current.length),
4685
4747
  mode: "bleed",
4686
4748
  onClick: handleClick,
4687
4749
  onMouseDown: stopRowMouseDown,
@@ -4714,7 +4776,7 @@ function rowMatches(row, filter) {
4714
4776
  if (filter === void 0) return !0;
4715
4777
  switch (filter.kind) {
4716
4778
  case "mine":
4717
- return isItemAssignedTo(row.item, filter.identity);
4779
+ return holdingMatchesAssignment(itemHolding(row.item, filter.identity), filter.assignment);
4718
4780
 
4719
4781
  case "activity":
4720
4782
  return row.activityName === filter.activity || row.tick?.activity === filter.activity;
@@ -4914,20 +4976,18 @@ function DueDateControl({item: item, editable: editable, onPatch: onPatch}) {
4914
4976
  }
4915
4977
 
4916
4978
  function AssigneeControl({item: item, editable: editable, eligibleRoles: eligibleRoles, onPatch: onPatch, roleAliases: roleAliases}) {
4917
- const [open, setOpen] = useState(!1), current = item.assignee ?? null, chip = current ? /* @__PURE__ */ jsx(RowAssignees, {
4918
- assignees: [ current ]
4979
+ const [open, setOpen] = useState(!1), current = item.assignee ?? [], chip = current.length > 0 ? /* @__PURE__ */ jsx(RowAssignees, {
4980
+ assignees: current
4919
4981
  }) : null;
4920
4982
  return editable ? /* @__PURE__ */ jsx(DismissablePopover, {
4921
- content:
4922
- /* @__PURE__ */ jsx(SingleAssigneePicker, {
4923
- current: current,
4983
+ content: /* @__PURE__ */ jsx(AssigneePicker, {
4924
4984
  eligibleRoles: eligibleRoles,
4925
- onPick: picked => {
4926
- setOpen(!1), onPatch({
4927
- assignee: picked
4928
- });
4929
- },
4930
- roleAliases: roleAliases
4985
+ maxUsers: 1,
4986
+ onChange: picked => onPatch({
4987
+ assignee: [ ...picked ]
4988
+ }),
4989
+ roleAliases: roleAliases,
4990
+ value: current
4931
4991
  }),
4932
4992
  onDismiss: () => setOpen(!1),
4933
4993
  open: open,
@@ -5444,7 +5504,7 @@ function ActionsMenuButton({actions: actions, instanceId: instanceId, activity:
5444
5504
 
5445
5505
  const FOOTER_GAP_PX = 8, MORE_ACTIONS_LABEL = "More actions";
5446
5506
 
5447
- function TerminalFooter({actions: actions, activity: activity, instanceId: instanceId}) {
5507
+ function TerminalFooter({actions: actions, activity: activity, instanceId: instanceId, surface: surface}) {
5448
5508
  const lock = useActionClusterState(), [only, ...rest] = actions;
5449
5509
  return only ? /* @__PURE__ */ jsx(ActionClusterProvider, {
5450
5510
  value: lock,
@@ -5454,12 +5514,13 @@ function TerminalFooter({actions: actions, activity: activity, instanceId: insta
5454
5514
  activity: activity,
5455
5515
  instanceId: instanceId,
5456
5516
  placement: "terminal-footer",
5457
- surface: "activity-dialog"
5517
+ surface: surface
5458
5518
  })
5459
5519
  }) : /* @__PURE__ */ jsx(FittedActions, {
5460
5520
  actions: actions,
5461
5521
  activity: activity,
5462
- instanceId: instanceId
5522
+ instanceId: instanceId,
5523
+ surface: surface
5463
5524
  })
5464
5525
  }) : null;
5465
5526
  }
@@ -5482,7 +5543,7 @@ function FooterRow({children: children, onElement: onElement}) {
5482
5543
  });
5483
5544
  }
5484
5545
 
5485
- function FittedActions({actions: actions, activity: activity, instanceId: instanceId}) {
5546
+ function FittedActions({actions: actions, activity: activity, instanceId: instanceId, surface: surface}) {
5486
5547
  const [footerEl, setFooterEl] = useState(null), [measureEl, setMeasureEl] = useState(null), footerSize = useElementSize(footerEl), measureSize = useElementSize(measureEl), [inline, setInline] = useState(void 0);
5487
5548
  useLayoutEffect(() => {
5488
5549
  if (measureEl === null || footerSize === null || measureSize === null) return;
@@ -5510,7 +5571,7 @@ function FittedActions({actions: actions, activity: activity, instanceId: instan
5510
5571
  activity: activity,
5511
5572
  instanceId: instanceId,
5512
5573
  placement: "terminal-footer",
5513
- surface: "activity-dialog"
5574
+ surface: surface
5514
5575
  }, a.action.name)), overflow.length > 0 ? /* @__PURE__ */ jsx(ActionsMenuButton, {
5515
5576
  actions: overflow,
5516
5577
  activity: activity,
@@ -5518,7 +5579,7 @@ function FittedActions({actions: actions, activity: activity, instanceId: instan
5518
5579
  label: MORE_ACTIONS_LABEL,
5519
5580
  mode: "default",
5520
5581
  placement: "terminal-footer",
5521
- surface: "activity-dialog"
5582
+ surface: surface
5522
5583
  }) : null ]
5523
5584
  }) ]
5524
5585
  });
@@ -5561,33 +5622,43 @@ function MeasureRow({actions: actions, activity: activity, onElement: onElement}
5561
5622
  });
5562
5623
  }
5563
5624
 
5564
- 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}) {
5565
5626
  const detail = deriveActivityDetail({
5566
5627
  entry: entry,
5567
5628
  activityName: activityName
5568
- }), faultingActivity2 = useInstanceFault(entry)?.activity;
5629
+ }), faultingActivity2 = useInstanceFault(entry)?.activity, identity = useAssignmentIdentity();
5569
5630
  if (!detail) return null;
5570
- const instanceId = entry.instance._id, notice = blockedNotice(detail), fault = /* @__PURE__ */ jsx(InstanceFaultNote, {
5571
- entry: entry,
5572
- scope: {
5573
- kind: "task",
5574
- activityName: activityName
5575
- }
5576
- }), 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);
5577
5657
  /* @__PURE__ */
5578
- return jsxs(Dialog, {
5579
- footer: detail.presentation.terminalActions.length > 0 ? /* @__PURE__ */ jsx(TerminalFooter, {
5580
- actions: detail.presentation.terminalActions,
5581
- activity: activityName,
5582
- instanceId: instanceId
5583
- }) : void 0,
5584
- header: /* @__PURE__ */ jsx(Breadcrumb, {
5585
- emphasizeCurrent: !0,
5586
- segments: [ ...breadcrumb, detail.title ]
5587
- }),
5588
- id: "workflow-activity-detail",
5589
- onClose: onClose,
5590
- width: 1,
5658
+ return jsxs(Flex, {
5659
+ direction: "column",
5660
+ flex: 1,
5661
+ overflow: "hidden",
5591
5662
  children: [
5592
5663
  /* @__PURE__ */ jsx(LogEventOnMount, {
5593
5664
  data: {
@@ -5595,86 +5666,264 @@ function ActivityDetailDialog({entry: entry, activityName: activityName, breadcr
5595
5666
  source: source
5596
5667
  },
5597
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"
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
5598
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: [
5599
5853
  /* @__PURE__ */ jsx(Card, {
5600
- borderBottom: hasBody,
5854
+ borderBottom: separated,
5601
5855
  style: {
5602
5856
  position: "sticky",
5603
5857
  top: 0,
5604
5858
  zIndex: 2
5605
5859
  },
5606
- children: /* @__PURE__ */ jsx(MetaBlock, {
5607
- activityName: activityName,
5608
- definition: definition,
5609
- detail: detail,
5610
- document: document,
5611
- instanceId: instanceId
5860
+ children: /* @__PURE__ */ jsx(Box, {
5861
+ padding: 4,
5862
+ children: meta
5863
+ })
5864
+ }), children === null ? null : /* @__PURE__ */ jsx(Box, {
5865
+ flex: 1,
5866
+ overflow: "auto",
5867
+ padding: 4,
5868
+ children: /* @__PURE__ */ jsx(Stack, {
5869
+ gap: 5,
5870
+ children: children
5871
+ })
5872
+ }) ]
5873
+ });
5874
+ }
5875
+
5876
+ function MetaBlock({document: document, ...props}) {
5877
+ const {detail: detail} = props, description = detail.activityEval.activity.description, status = detail.activityEval.status;
5878
+ /* @__PURE__ */
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
5612
5886
  })
5613
- }), hasBody ? /* @__PURE__ */ jsx(Box, {
5614
- padding: 4,
5615
- children: /* @__PURE__ */ jsxs(Stack, {
5616
- gap: 5,
5617
- children: [ fault, detail.state.map(field => /* @__PURE__ */ jsx(FieldRow, {
5618
- activityName: activityName,
5619
- detail: detail,
5620
- entry: entry,
5621
- field: field,
5622
- instanceId: instanceId
5623
- }, field._key)), notice ]
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, {
5898
+ size: 1,
5899
+ children: ACTIVITY_STATUS_LABEL[status]
5900
+ }) ]
5624
5901
  })
5625
- }) : null ]
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
+ }) ]
5626
5911
  });
5627
5912
  }
5628
5913
 
5629
- function MetaBlock({detail: detail, definition: definition, document: document, instanceId: instanceId, activityName: activityName}) {
5630
- const description = detail.activityEval.activity.description, status = detail.activityEval.status;
5914
+ function DocumentMetaValue({document: document}) {
5631
5915
  /* @__PURE__ */
5632
5916
  return jsx(Box, {
5633
- padding: 4,
5634
- children: /* @__PURE__ */ jsxs(Stack, {
5635
- gap: 4,
5636
- children: [ description ? /* @__PURE__ */ jsx(MetaRow, {
5637
- label: "Description",
5638
- children: /* @__PURE__ */ jsx(Text, {
5639
- size: 1,
5640
- children: description
5641
- })
5642
- }) : null,
5643
- /* @__PURE__ */ jsx(MetaRow, {
5644
- label: "Status",
5645
- children: /* @__PURE__ */ jsxs(Flex, {
5646
- align: "center",
5647
- gap: 2,
5648
- children: [
5649
- /* @__PURE__ */ jsx(ActivityStatusIcon, {
5650
- status: status
5651
- }),
5652
- /* @__PURE__ */ jsx(Text, {
5653
- size: 1,
5654
- children: ACTIVITY_STATUS_LABEL[status]
5655
- }) ]
5656
- })
5657
- }), document ? /* @__PURE__ */ jsx(MetaRow, {
5658
- label: "Studio document",
5659
- children: /* @__PURE__ */ jsx(Flex, {
5660
- children: /* @__PURE__ */ jsx(DocPreviewLink, {
5661
- gdr: document,
5662
- layout: "inline",
5663
- source: "dialog-subject"
5664
- })
5665
- })
5666
- }) : null,
5667
- /* @__PURE__ */ jsx(TopActions, {
5668
- activityName: activityName,
5669
- definition: definition,
5670
- detail: detail,
5671
- instanceId: instanceId
5672
- }) ]
5917
+ style: useChipTrim(),
5918
+ children: /* @__PURE__ */ jsx(DocPreviewLink, {
5919
+ gdr: document,
5920
+ layout: "inline",
5921
+ source: "dialog-subject"
5673
5922
  })
5674
5923
  });
5675
5924
  }
5676
5925
 
5677
- function TopActions({detail: detail, definition: definition, instanceId: instanceId, activityName: activityName}) {
5926
+ function TopActions({activityName: activityName, definition: definition, detail: detail, instanceId: instanceId, surface: surface}) {
5678
5927
  const p = detail.presentation;
5679
5928
  return p.actions.length === 0 && p.triggeredActions.length === 0 && !p.manualTarget ? null : /* @__PURE__ */ jsxs(Flex, {
5680
5929
  align: "center",
@@ -5685,17 +5934,9 @@ function TopActions({detail: detail, definition: definition, instanceId: instanc
5685
5934
  activity: activityName,
5686
5935
  instanceId: instanceId,
5687
5936
  placement: "dialog-strip",
5688
- surface: "activity-dialog"
5689
- }, a.action.name)), p.manualTarget ? /* @__PURE__ */ jsx(Button, {
5690
- as: "a",
5691
- fontSize: 1,
5692
- href: p.manualTarget.href,
5693
- mode: "ghost",
5694
- padding: 2,
5695
- rel: "noreferrer",
5696
- target: "_blank",
5697
- text: p.manualTarget.label,
5698
- tone: "caution"
5937
+ surface: surface
5938
+ }, a.action.name)), p.manualTarget ? /* @__PURE__ */ jsx(ManualTargetButton, {
5939
+ target: p.manualTarget
5699
5940
  }) : null, p.triggeredActions.map(a => /* @__PURE__ */ jsx(AutomationActionRow, {
5700
5941
  actionEval: a,
5701
5942
  definition: definition
@@ -5703,7 +5944,7 @@ function TopActions({detail: detail, definition: definition, instanceId: instanc
5703
5944
  });
5704
5945
  }
5705
5946
 
5706
- 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}) {
5707
5948
  const head = /* @__PURE__ */ jsx(FieldHead, {
5708
5949
  label: field.title ?? field.name,
5709
5950
  ...field.description === void 0 ? {} : {
@@ -5723,10 +5964,10 @@ function FieldRow({field: field, detail: detail, entry: entry, activityName: act
5723
5964
  activity: activityName
5724
5965
  },
5725
5966
  showEmpty: !0,
5726
- surface: "activity-dialog"
5967
+ surface: todoSurface
5727
5968
  }) ]
5728
5969
  });
5729
- if (field._type === "assignees") {
5970
+ if (field._type === "assignee" || field._type === "assignees") {
5730
5971
  const assignField = detail.assignFields.get(field.name);
5731
5972
  /* @__PURE__ */
5732
5973
  return jsxs(Stack, {
@@ -5736,8 +5977,9 @@ function FieldRow({field: field, detail: detail, entry: entry, activityName: act
5736
5977
  align: "center",
5737
5978
  gap: 2,
5738
5979
  wrap: "wrap",
5739
- children: [
5740
- /* @__PURE__ */ jsx(FieldValue, {
5980
+ children: [ layout === "panel" ? /* @__PURE__ */ jsx(RowAssignees, {
5981
+ assignees: field.value
5982
+ }) : /* @__PURE__ */ jsx(FieldValue, {
5741
5983
  field: field
5742
5984
  }), assignField ? /* @__PURE__ */ jsx(AssignActivityControl, {
5743
5985
  instanceId: instanceId,
@@ -5745,7 +5987,7 @@ function FieldRow({field: field, detail: detail, entry: entry, activityName: act
5745
5987
  kind: "editable",
5746
5988
  field: assignField
5747
5989
  },
5748
- surface: "activity-dialog"
5990
+ surface: surface
5749
5991
  }) : null ]
5750
5992
  }) ]
5751
5993
  });
@@ -5754,7 +5996,7 @@ function FieldRow({field: field, detail: detail, entry: entry, activityName: act
5754
5996
  return editable ? /* @__PURE__ */ jsx(EditableFieldControl, {
5755
5997
  field: editable,
5756
5998
  instanceId: instanceId,
5757
- surface: "activity-dialog"
5999
+ surface: surface
5758
6000
  }) : /* @__PURE__ */ jsxs(Stack, {
5759
6001
  gap: 2,
5760
6002
  children: [ head,
@@ -5764,18 +6006,85 @@ function FieldRow({field: field, detail: detail, entry: entry, activityName: act
5764
6006
  });
5765
6007
  }
5766
6008
 
5767
- function blockedNotice(detail) {
5768
- const reason = detail.presentation.blockedReason;
5769
- if (reason) /* @__PURE__ */
5770
- 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, {
5771
6032
  border: !0,
5772
6033
  padding: 3,
5773
6034
  radius: 2,
5774
6035
  tone: "caution",
5775
- children: /* @__PURE__ */ jsx(Text, {
5776
- size: 1,
5777
- 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"
5778
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 ]
5779
6088
  });
5780
6089
  }
5781
6090
 
@@ -5795,36 +6104,23 @@ function CountedLabel({count: count, label: label}) {
5795
6104
  });
5796
6105
  }
5797
6106
 
5798
- function CountedHeading({count: count, title: title}) {
5799
- /* @__PURE__ */
5800
- return jsx(CountedLabel, {
5801
- count: count,
5802
- label: /* @__PURE__ */ jsx(Text, {
5803
- size: 1,
5804
- weight: "semibold",
5805
- children: title
5806
- })
5807
- });
5808
- }
5809
-
5810
- function GroupHeading({busy: busy, count: count, title: title, end: end}) {
6107
+ function GroupHeading({busy: busy, count: count, title: title, action: action}) {
5811
6108
  /* @__PURE__ */
5812
6109
  return jsxs(Flex, {
5813
6110
  align: "center",
5814
- gap: 3,
6111
+ gap: 2,
5815
6112
  paddingLeft: 2,
5816
- paddingY: end === void 0 ? 3 : 1,
6113
+ paddingY: action === void 0 ? 3 : 1,
5817
6114
  children: [
5818
- /* @__PURE__ */ jsx(CountedHeading, {
6115
+ /* @__PURE__ */ jsx(CountedLabel, {
5819
6116
  count: count,
5820
- title: title
5821
- }), 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, {
5822
6123
  busy: busy
5823
- }), end === void 0 ? null : /* @__PURE__ */ jsxs(Fragment, {
5824
- children: [
5825
- /* @__PURE__ */ jsx(Flex, {
5826
- flex: 1
5827
- }), end ]
5828
6124
  }) ]
5829
6125
  });
5830
6126
  }
@@ -6283,13 +6579,10 @@ function peopleFaceOf(kind, entry) {
6283
6579
  kind: "assignees",
6284
6580
  assignees: entry?._type === "assignees" ? entry.value : []
6285
6581
  };
6286
- if (kind === "assignee") {
6287
- const value = entry?._type === "assignee" ? entry.value : null;
6288
- return {
6289
- kind: "assignees",
6290
- assignees: value ? [ value ] : []
6291
- };
6292
- }
6582
+ if (kind === "assignee") return {
6583
+ kind: "assignees",
6584
+ assignees: entry?._type === "assignee" ? entry.value : []
6585
+ };
6293
6586
  }
6294
6587
 
6295
6588
  function faceOf(args) {
@@ -6847,24 +7140,21 @@ function ActorPill({entry: entry, pill: pill, editability: editability, surface:
6847
7140
  }
6848
7141
 
6849
7142
  function AssigneePill({entry: entry, pill: pill, editability: editability, surface: surface}) {
6850
- const [open, setOpen] = useState(!1), {set: set, unset: unset} = useFieldCommit({
7143
+ const [open, setOpen] = useState(!1), {set: set, saving: saving, save: save} = usePillWrite({
6851
7144
  entry: entry,
6852
7145
  editability: editability,
6853
7146
  surface: surface
6854
- }), current = pill.entry?._type === "assignee" ? pill.entry.value : null, {pick: pick} = useAssigneePick({
6855
- instanceId: entry.instance._id,
6856
- onSave: set,
6857
- onUnset: unset
6858
- });
7147
+ }), current = pill.entry?._type === "assignee" ? pill.entry.value : [];
6859
7148
  /* @__PURE__ */
6860
7149
  return jsx(PillPopover, {
6861
- content: /* @__PURE__ */ jsx(SingleAssigneePicker, {
6862
- current: current,
7150
+ content: /* @__PURE__ */ jsx(AssigneePicker, {
6863
7151
  eligibleRoles: editability.roles,
6864
- onPick: picked => {
6865
- setOpen(!1), pick(picked);
7152
+ maxUsers: 1,
7153
+ onChange: next => {
7154
+ saving || save(() => set(next));
6866
7155
  },
6867
- roleAliases: editability.roleAliases
7156
+ roleAliases: editability.roleAliases,
7157
+ value: current
6868
7158
  }),
6869
7159
  onDismiss: () => setOpen(!1),
6870
7160
  onToggle: () => setOpen(v => !v),
@@ -7156,100 +7446,6 @@ function conditionLines(description) {
7156
7446
  });
7157
7447
  }
7158
7448
 
7159
- function Hairline({weight: weight, style: style}) {
7160
- const {color: color} = useTheme_v2();
7161
- /* @__PURE__ */
7162
- return jsx("div", {
7163
- style: {
7164
- borderTop: `${weight}px solid ${color.border}`,
7165
- ...style
7166
- }
7167
- });
7168
- }
7169
-
7170
- function StageGlyph(props) {
7171
- /* @__PURE__ */
7172
- return jsxs("svg", {
7173
- "data-sanity-icon": "stage",
7174
- fill: "none",
7175
- height: "1em",
7176
- viewBox: "0 0 25 25",
7177
- width: "1em",
7178
- xmlns: "http://www.w3.org/2000/svg",
7179
- ...props,
7180
- children: [
7181
- /* @__PURE__ */ jsx("path", {
7182
- 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",
7183
- fill: "currentColor"
7184
- }),
7185
- /* @__PURE__ */ jsx("path", {
7186
- 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",
7187
- fill: "currentColor"
7188
- }),
7189
- /* @__PURE__ */ jsx("path", {
7190
- 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",
7191
- fill: "currentColor"
7192
- }) ]
7193
- });
7194
- }
7195
-
7196
- function StageFace({completed: completed = !1, detail: detail, glyph: glyph = !0, title: title}) {
7197
- const tone = completed ? "default" : "primary";
7198
- /* @__PURE__ */
7199
- return jsxs(Flex, {
7200
- align: "center",
7201
- as: "span",
7202
- gap: 2,
7203
- children: [ glyph ? /* @__PURE__ */ jsx(TextWithTone, {
7204
- size: 1,
7205
- tone: tone,
7206
- children: completed ? /* @__PURE__ */ jsx(CheckmarkCircleFilledIcon, {
7207
- style: {
7208
- color: "inherit"
7209
- }
7210
- }) : /* @__PURE__ */ jsx(StageGlyph, {
7211
- style: {
7212
- color: "inherit"
7213
- }
7214
- })
7215
- }) : null,
7216
- /* @__PURE__ */ jsx(TextWithTone, {
7217
- size: 1,
7218
- tone: tone,
7219
- weight: "medium",
7220
- children: title
7221
- }), detail === void 0 ? null : /* @__PURE__ */ jsx(Text, {
7222
- muted: !0,
7223
- size: 1,
7224
- children: detail
7225
- }) ]
7226
- });
7227
- }
7228
-
7229
- function StageChip({completed: completed = !1, detail: detail, interactive: interactive = !1, title: title}) {
7230
- const {color: color} = useTheme_v2();
7231
- /* @__PURE__ */
7232
- return jsx(Card, {
7233
- padding: 3,
7234
- radius: 4,
7235
- style: {
7236
- boxShadow: `inset 0 0 0 1px ${completed ? color.badge.default.fg : color.focusRing}`,
7237
- cursor: interactive ? "help" : "default"
7238
- },
7239
- tone: completed ? "default" : "primary",
7240
- ...interactive ? {
7241
- tabIndex: 0
7242
- } : {},
7243
- children: /* @__PURE__ */ jsx(StageFace, {
7244
- completed: completed,
7245
- title: title,
7246
- ...detail === void 0 ? {} : {
7247
- detail: detail
7248
- }
7249
- })
7250
- });
7251
- }
7252
-
7253
7449
  function StartIncompleteBadge({style: style}) {
7254
7450
  /* @__PURE__ */
7255
7451
  return jsx(Badge, {
@@ -7280,7 +7476,7 @@ function abortedToast(title) {
7280
7476
  return {
7281
7477
  id: TOAST_ID.abort,
7282
7478
  status: "info",
7283
- title: `Aborted “${title}”`
7479
+ title: `Canceled “${title}”`
7284
7480
  };
7285
7481
  }
7286
7482
 
@@ -7289,7 +7485,7 @@ function abortAlreadySettledToast(title) {
7289
7485
  id: TOAST_ID.abort,
7290
7486
  status: "info",
7291
7487
  title: `“${title}” had already finished`,
7292
- description: "Nothing was aborted — the workflow reached a terminal state first."
7488
+ description: "Nothing was canceled — the workflow reached a terminal state first."
7293
7489
  };
7294
7490
  }
7295
7491
 
@@ -7297,8 +7493,8 @@ function abortUnconfirmedToast(args) {
7297
7493
  return {
7298
7494
  id: TOAST_ID.abort,
7299
7495
  status: "warning",
7300
- title: `Couldn’t confirm “${args.title}” was aborted`,
7301
- 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.`
7302
7498
  };
7303
7499
  }
7304
7500
 
@@ -7344,7 +7540,7 @@ function AbortWorkflowDialog({entry: entry, onClose: onClose}) {
7344
7540
  mode: "bleed",
7345
7541
  onClick: onClose,
7346
7542
  padding: 2,
7347
- text: "Cancel"
7543
+ text: "Keep workflow"
7348
7544
  }),
7349
7545
  /* @__PURE__ */ jsx(Button, {
7350
7546
  disabled: reason === void 0,
@@ -7354,12 +7550,12 @@ function AbortWorkflowDialog({entry: entry, onClose: onClose}) {
7354
7550
  confirm();
7355
7551
  },
7356
7552
  padding: 2,
7357
- text: "Abort workflow",
7553
+ text: "Cancel workflow",
7358
7554
  tone: "critical"
7359
7555
  }) ]
7360
7556
  })
7361
7557
  }),
7362
- header: "Abort workflow",
7558
+ header: "Cancel workflow",
7363
7559
  id: `abort-workflow-${instance._id}`,
7364
7560
  onClose: () => {
7365
7561
  pending || onClose();
@@ -7372,7 +7568,7 @@ function AbortWorkflowDialog({entry: entry, onClose: onClose}) {
7372
7568
  children: [
7373
7569
  /* @__PURE__ */ jsxs(Text, {
7374
7570
  size: 1,
7375
- children: [ "Abort the ",
7571
+ children: [ "Cancel the ",
7376
7572
  /* @__PURE__ */ jsx("strong", {
7377
7573
  children: title
7378
7574
  }), " workflow? This cannot be undone." ]
@@ -7844,7 +8040,7 @@ function InstanceCardMenu({entry: entry, title: title}) {
7844
8040
  /* @__PURE__ */ jsx(MenuDivider, {}),
7845
8041
  /* @__PURE__ */ jsx(MenuItem, {
7846
8042
  onClick: () => setDialog("abort"),
7847
- text: "Abort workflow…",
8043
+ text: "Cancel workflow…",
7848
8044
  tone: "critical"
7849
8045
  }) ]
7850
8046
  }) : null ]
@@ -7863,90 +8059,97 @@ function InstanceCardMenu({entry: entry, title: title}) {
7863
8059
  });
7864
8060
  }
7865
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
+
7866
8084
  function InstanceCard({entry: entry, children: children, defaultOpen: defaultOpen}) {
7867
- 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);
7868
8086
  /* @__PURE__ */
7869
8087
  return jsxs(Card, {
7870
8088
  border: !0,
7871
8089
  overflow: "hidden",
7872
8090
  radius: 4,
7873
8091
  children: [
7874
- /* @__PURE__ */ jsxs(Box, {
7875
- style: {
7876
- position: "relative"
7877
- },
7878
- children: [
7879
- /* @__PURE__ */ jsx(Card, {
7880
- "aria-expanded": open,
7881
- as: "button",
7882
- onClick: () => setOpen(value => !value),
7883
- padding: 3,
7884
- style: {
7885
- display: "block",
7886
- textAlign: "left",
7887
- width: "100%"
7888
- },
7889
- ...open ? {
7890
- tone: "transparent"
7891
- } : {},
7892
- children: /* @__PURE__ */ jsxs(Flex, {
7893
- align: "center",
7894
- gap: 3,
7895
- children: [
7896
- /* @__PURE__ */ jsx(Text, {
7897
- muted: !0,
7898
- size: 1,
7899
- children: open ? /* @__PURE__ */ jsx(ChevronDownIcon, {}) : /* @__PURE__ */ jsx(ChevronRightIcon, {})
7900
- }),
7901
- /* @__PURE__ */ jsxs(Stack, {
7902
- flex: 1,
7903
- gap: 2,
7904
- style: {
7905
- minWidth: 0
7906
- },
7907
- children: [
7908
- /* @__PURE__ */ jsx(Text, {
7909
- size: 1,
7910
- textOverflow: "ellipsis",
7911
- weight: "medium",
7912
- children: title
7913
- }),
7914
- /* @__PURE__ */ jsx(HeaderStage, {
7915
- entry: entry
7916
- }) ]
7917
- }),
7918
- /* @__PURE__ */ jsxs(Flex, {
7919
- align: "center",
7920
- flex: "none",
7921
- children: [
7922
- /* @__PURE__ */ jsx(HeaderBadges, {
7923
- instance: instance,
7924
- settled: settled
7925
- }),
7926
- /* @__PURE__ */ jsx(Box, {
7927
- flex: "none",
7928
- style: {
7929
- width: menuSlot
7930
- }
7931
- }) ]
7932
- }) ]
7933
- })
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
+ }
7934
8106
  }),
7935
- /* @__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, {
7936
8117
  align: "center",
8118
+ gap: 2,
8119
+ padding: 3,
7937
8120
  style: {
7938
- bottom: 0,
7939
- position: "absolute",
7940
- right: 0,
7941
- top: 0
8121
+ pointerEvents: "none",
8122
+ position: "relative"
7942
8123
  },
7943
- children: /* @__PURE__ */ jsx(Box, {
7944
- 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
+ },
7945
8133
  children: /* @__PURE__ */ jsx(InstanceCardMenu, {
7946
8134
  entry: entry,
7947
8135
  title: title
7948
8136
  })
7949
- })
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
+ }) ]
7950
8153
  }) ]
7951
8154
  }), open ? /* @__PURE__ */ jsxs(Fragment, {
7952
8155
  children: [
@@ -7961,24 +8164,39 @@ function InstanceCard({entry: entry, children: children, defaultOpen: defaultOpe
7961
8164
  });
7962
8165
  }
7963
8166
 
7964
- function HeaderStage({entry: entry}) {
8167
+ function HeaderTitleLine({entry: entry}) {
7965
8168
  const definition = useDefinition(entry), badgeTrim = useBadgeCapTrim(), face = instanceStageFace({
7966
8169
  definition: definition,
7967
8170
  instance: entry.instance
7968
8171
  });
7969
8172
  /* @__PURE__ */
7970
- return jsx(Flex, {
8173
+ return jsxs(Flex, {
7971
8174
  align: "center",
7972
- 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, {
7973
8186
  style: badgeTrim
7974
- }) : /* @__PURE__ */ jsx(StageFace, {
7975
- ...face.detail === void 0 ? {} : {
7976
- detail: face.detail
7977
- },
7978
- completed: face.completed,
7979
- glyph: !1,
7980
- title: face.title
7981
- })
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
+ }) ]
7982
8200
  });
7983
8201
  }
7984
8202
 
@@ -7989,7 +8207,10 @@ function HeaderBadges({instance: instance, settled: settled}) {
7989
8207
  align: "center",
7990
8208
  flex: "none",
7991
8209
  gap: 2,
7992
- style: badgeTrim,
8210
+ style: {
8211
+ ...badgeTrim,
8212
+ pointerEvents: "auto"
8213
+ },
7993
8214
  children: [
7994
8215
  /* @__PURE__ */ jsx(AbortedBadge, {
7995
8216
  instance: instance
@@ -8028,6 +8249,90 @@ function PartOfBand({instance: instance}) {
8028
8249
  });
8029
8250
  }
8030
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
+
8031
8336
  const NODE_ICON = {
8032
8337
  done: CheckmarkCircleIcon
8033
8338
  };
@@ -8094,6 +8399,7 @@ function SpineNodeView({definition: definition, node: node, transitions: transit
8094
8399
  "data-testid": `spine-node-${node.name}`,
8095
8400
  flush: !0,
8096
8401
  children: /* @__PURE__ */ jsx(InertPill, {
8402
+ filled: node.state === "upcoming" || node.state === "revisit",
8097
8403
  children: /* @__PURE__ */ jsxs(Flex, {
8098
8404
  align: "center",
8099
8405
  gap: 2,
@@ -8111,14 +8417,16 @@ function SpineNodeView({definition: definition, node: node, transitions: transit
8111
8417
  });
8112
8418
  }
8113
8419
 
8114
- function InertPill({children: children}) {
8420
+ function InertPill({children: children, filled: filled = !1}) {
8115
8421
  const {color: color} = useTheme_v2();
8116
8422
  /* @__PURE__ */
8117
8423
  return jsx(Card, {
8118
8424
  padding: 3,
8119
8425
  radius: 4,
8120
8426
  style: {
8121
- backgroundColor: "transparent",
8427
+ ...filled ? {} : {
8428
+ backgroundColor: "transparent"
8429
+ },
8122
8430
  boxShadow: `inset 0 0 0 1px ${color.border}`,
8123
8431
  cursor: "help"
8124
8432
  },
@@ -8264,9 +8572,7 @@ function InstanceSnapshotBody({entry: entry, onOpenActivity: onOpenActivity, sur
8264
8572
  /* @__PURE__ */ jsx(Card, {
8265
8573
  borderBottom: !0,
8266
8574
  padding: 2,
8267
- ...definition ? {} : {
8268
- tone: "caution"
8269
- },
8575
+ tone: definition ? "transparent" : "caution",
8270
8576
  children: definition ? /* @__PURE__ */ jsx(WorkflowSpine, {
8271
8577
  definition: definition,
8272
8578
  evaluation: entry.evaluation,
@@ -8420,21 +8726,6 @@ function InvalidDocNotice({invalid: invalid}) {
8420
8726
  });
8421
8727
  }
8422
8728
 
8423
- function RoleAssignedNotice() {
8424
- /* @__PURE__ */
8425
- return jsx(Box, {
8426
- padding: 2,
8427
- children: /* @__PURE__ */ jsx(Text, {
8428
- muted: !0,
8429
- size: 1,
8430
- style: {
8431
- fontStyle: "italic"
8432
- },
8433
- children: "Including tasks assigned to your role"
8434
- })
8435
- });
8436
- }
8437
-
8438
8729
  function TabSwitch({ariaControls: ariaControls, idPrefix: idPrefix, options: options, selected: selected, onSelect: onSelect}) {
8439
8730
  /* @__PURE__ */
8440
8731
  return jsx(TabList, {
@@ -9201,7 +9492,7 @@ function StartWorkflowMenuItem({mapping: mapping, docId: docId, initialValue: in
9201
9492
  }) : item;
9202
9493
  }
9203
9494
 
9204
- function ForMeList({entry: entry, work: work, identity: identity, onOpenActivity: onOpenActivity}) {
9495
+ function ForMeList({entry: entry, work: work, identity: identity, assignment: assignment, onOpenActivity: onOpenActivity}) {
9205
9496
  const definition = useDefinition(entry), {instance: instance} = entry, itemCount = work.openItems.length + work.doneItems.length;
9206
9497
  if (!hasAssignedRows(work)) return null;
9207
9498
  const activityState = stateByActivity(instance), breadcrumb = instanceBreadcrumb(instance, definition);
@@ -9225,7 +9516,8 @@ function ForMeList({entry: entry, work: work, identity: identity, onOpenActivity
9225
9516
  entry: entry,
9226
9517
  filter: {
9227
9518
  kind: "mine",
9228
- identity: identity
9519
+ identity: identity,
9520
+ assignment: assignment
9229
9521
  },
9230
9522
  surface: "document-view-for-me"
9231
9523
  }) : null ]
@@ -9340,10 +9632,7 @@ function WorkflowsPanel({entries: entries, documentId: documentId, docTypeMappin
9340
9632
  padding: 3,
9341
9633
  children: [
9342
9634
  /* @__PURE__ */ jsx(GroupHeading, {
9343
- busy: booting,
9344
- count: active.length,
9345
- end:
9346
- /* @__PURE__ */ jsx(Box, {
9635
+ action: /* @__PURE__ */ jsx(Flex, {
9347
9636
  style: {
9348
9637
  visibility: view === "for-me" ? "hidden" : "visible"
9349
9638
  },
@@ -9357,6 +9646,7 @@ function WorkflowsPanel({entries: entries, documentId: documentId, docTypeMappin
9357
9646
  tone: "default"
9358
9647
  })
9359
9648
  }),
9649
+ busy: booting,
9360
9650
  title: "Active Workflows"
9361
9651
  }),
9362
9652
  /* @__PURE__ */ jsx(TabSwitch, {
@@ -9460,21 +9750,50 @@ function OverviewEmptyState({mappings: mappings, docId: docId, initialValue: ini
9460
9750
 
9461
9751
  function ForMeTab({forMe: forMe, assignedWorkUnknown: assignedWorkUnknown, onOpenActivity: onOpenActivity}) {
9462
9752
  return forMe.hasRows ? /* @__PURE__ */ jsxs(Stack, {
9463
- gap: GROUP_GAP,
9753
+ gap: 6,
9464
9754
  paddingBottom: 4,
9465
- children: [ forMe.hasRoleMatched ? /* @__PURE__ */ jsx(RoleAssignedNotice, {}) : null, forMe.work.map(({entry: entry, who: who, shown: shown}) => /* @__PURE__ */ jsx(ForMeList, {
9466
- entry: entry,
9467
- identity: who,
9468
- work: shown,
9469
- onOpenActivity: activityName => onOpenActivity({
9470
- instanceId: entry.instance._id,
9471
- activityName: activityName,
9472
- stage: entry.instance.currentStage
9473
- })
9474
- }, 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
+ }) ]
9475
9769
  }) : assignedWorkUnknown ? null : /* @__PURE__ */ jsx(ForMeEmptyState, {});
9476
9770
  }
9477
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
+
9478
9797
  function SectionActivityDialog({entry: entry, activityName: activityName, onClose: onClose}) {
9479
9798
  const definition = useDefinition(entry), {instance: instance} = entry;
9480
9799
  /* @__PURE__ */
@@ -9496,6 +9815,44 @@ function workflowDefaultDocumentNode() {
9496
9815
  return S => S.document().views([ S.view.form(), workflowsView(S) ]);
9497
9816
  }
9498
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
+
9499
9856
  function lockTitleFor(args) {
9500
9857
  const {denials: denials, entries: entries} = args;
9501
9858
  if (denials.length === 0) return;
@@ -9857,6 +10214,34 @@ function useDefaultLayout(props) {
9857
10214
  return useMemo(() => props.renderDefault(props), [ props ]);
9858
10215
  }
9859
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
+
9860
10245
  const notReady = (..._args) => Promise.reject(new Error("workflow session not ready yet")), NO_REGISTERED_DOCUMENTS = [], DEMAND_RELEASE_GRACE_MS = 500;
9861
10246
 
9862
10247
  function workspaceParts(config) {
@@ -9920,17 +10305,6 @@ function DiscoverySubscription(props) {
9920
10305
  }, [ instances, invalid, registered, onSnapshot ]), null;
9921
10306
  }
9922
10307
 
9923
- function engineOptionsFrom(config) {
9924
- return {
9925
- ...config.effectHandlers ? {
9926
- effectHandlers: config.effectHandlers
9927
- } : {},
9928
- ...config.resourceClients ? {
9929
- resourceClients: config.resourceClients
9930
- } : {}
9931
- };
9932
- }
9933
-
9934
10308
  function WorkflowProvider(props) {
9935
10309
  const {config: config} = props, defaultLayout = useDefaultLayout(props), studioClient = useClient({
9936
10310
  apiVersion: WORKFLOW_API_VERSION
@@ -9944,11 +10318,18 @@ function WorkflowProvider(props) {
9944
10318
  id: `${projectId}.${contentDataset}`
9945
10319
  },
9946
10320
  tag: config.tag
9947
- }), [ 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({
9948
10327
  workflowResource: binding.engineResource,
9949
10328
  tag: config.tag,
9950
- missingHandler: "skip",
9951
- ...engineOptionsFrom(config)
10329
+ effects: effects,
10330
+ ...config.resourceClients ? {
10331
+ resourceClients: config.resourceClients
10332
+ } : {}
9952
10333
  }), grantsFromPath = aclPathForResource({
9953
10334
  type: "dataset",
9954
10335
  id: `${projectId}.${workflowDataset}`
@@ -9958,8 +10339,14 @@ function WorkflowProvider(props) {
9958
10339
  registerActive(id), retainRegistration(id);
9959
10340
  }, [ registerActive, retainRegistration ]), unregister = useCallback(id => {
9960
10341
  unregisterActive(id), releaseRegistration(id);
9961
- }, [ 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({
9962
10346
  releaseDelayMs: DEMAND_RELEASE_GRACE_MS
10347
+ }), {acquire: requestEvaluation, release: releaseEvaluation} = useWarmDemand({
10348
+ acquire: acquireEvaluation,
10349
+ release: dropEvaluation
9963
10350
  }), [seeded, setSeeded] = useState(/* @__PURE__ */ new Map), seedInstance = useCallback(instance => {
9964
10351
  setSeeded(prev => new Map(prev).set(instance._id, instance));
9965
10352
  }, []), [startRequest2, setStartRequest] = useState(void 0), openStartDialog = useCallback(r => setStartRequest(r), []), closeStartDialog = useCallback(() => setStartRequest(void 0), []), [entriesStore] = useState(createEntriesStore);
@@ -10072,7 +10459,7 @@ function WorkflowProvider(props) {
10072
10459
  mappingStarts: starts,
10073
10460
  mappingFields: fields,
10074
10461
  latestDefinitionVersions: latestVersions,
10075
- effectHandlers: engine.effectHandlers,
10462
+ effectHandlers: engine.effects.handlers,
10076
10463
  autoStartByType: autoStartByType,
10077
10464
  autoStartDefinitions: autoStartDefinitions
10078
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 ]);
@@ -10177,7 +10564,43 @@ function logOnce(messages, logged) {
10177
10564
  for (const message of messages) logged.has(message) || (logged.add(message), console.warn(`[workflow-studio-plugin] ${message}`));
10178
10565
  }
10179
10566
 
10180
- 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}) {
10181
10604
  const [open, setOpen] = useState(!1), remove = index => onChange(value.filter((_, i) => i !== index));
10182
10605
  /* @__PURE__ */
10183
10606
  return jsxs(Stack, {
@@ -10212,6 +10635,7 @@ function AssigneesInput({value: value, onChange: onChange, eligibleRoles: eligib
10212
10635
  children: /* @__PURE__ */ jsx(DismissablePopover, {
10213
10636
  content: /* @__PURE__ */ jsx(AssigneePicker, {
10214
10637
  eligibleRoles: eligibleRoles,
10638
+ maxUsers: maxUsers,
10215
10639
  onChange: next => onChange([ ...next ]),
10216
10640
  roleAliases: roleAliases,
10217
10641
  value: value
@@ -10578,6 +11002,13 @@ const docRefInput = ({entry: entry, docType: docType, releaseId: releaseId, valu
10578
11002
  roleAliases: roleAliases,
10579
11003
  value: value ?? []
10580
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
+ }),
10581
11012
  array: ({entry: entry, value: value, onChange: onChange}) => {
10582
11013
  const editor = arrayEditorKind(entry);
10583
11014
  if (editor === "notes") /* @__PURE__ */ return jsx(NotesStartInput, {
@@ -11407,9 +11838,9 @@ function StripLines({active: active, docId: docId, finished: finished, initialVa
11407
11838
 
11408
11839
  function lineHint(entry, identity) {
11409
11840
  const forMe = forMeWorkOf([ entry ], identity);
11410
- return forMe.work.some(({shown: shown}) => hasOpenAssignedWork(shown)) ? {
11841
+ return forMe.assignedToUser.some(({shown: shown}) => hasOpenAssignedWork(shown)) ? {
11411
11842
  content: /* @__PURE__ */ jsx(AssignedTasksHint, {
11412
- work: forMe.work
11843
+ work: forMe.assignedToUser
11413
11844
  }),
11414
11845
  wide: !0
11415
11846
  } : unassignedLineHint({
@@ -11618,31 +12049,21 @@ const workflowsTool = {
11618
12049
  }, LOCKABLE_ACTIONS = /* @__PURE__ */ new Map([ [ "delete", "delete" ], [ "publish", "publish" ], [ "unpublish", "unpublish" ] ]);
11619
12050
 
11620
12051
  function useDocumentActionLock(action, props) {
11621
- 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);
11622
- return useEffect(() => {
11623
- if (!doc || guards.length === 0) {
11624
- applyDenials([]);
11625
- return;
11626
- }
11627
- let cancelled = !1;
11628
- return (async () => {
11629
- const denied = await documentActionDenials({
11630
- doc: doc,
11631
- resource: resource,
11632
- action: action,
11633
- guards: guards,
11634
- ...identity !== void 0 ? {
11635
- identity: identity
11636
- } : {}
11637
- });
11638
- cancelled || applyDenials(denied);
11639
- })().catch(err => {
11640
- console.error("[workflow-studio-plugin] document-action guard evaluation failed:", err),
11641
- cancelled || applyDenials([]);
11642
- }), () => {
11643
- cancelled = !0;
11644
- };
11645
- }, [ 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({
11646
12067
  denials: denials,
11647
12068
  entries: active
11648
12069
  });
@@ -11706,4 +12127,4 @@ function WorkflowRootInput(props) {
11706
12127
  });
11707
12128
  }
11708
12129
 
11709
- export { AbortWorkflowDialog, ActivityDetailDialog, ActivityLog, AgoStamp, AlertChip, AlertGlyph, CodeChip, CountedHeading, DismissablePopover, DocPreviewLink, DocRefFace, EmptyState, HoverHint, InstanceFaultNote, InstanceSnapshotBody, InvalidDocNotice, LoadingRow, LogEventOnMount, MetaRow, MetricLockup, MetricPair, NoteBanner, RUN_SEARCH_PARAM, RoleAssignedNotice, SpinnerSlot, TOOL_TABS, TabSwitch, UnreadableDocsNote, WORKFLOW_API_VERSION, WorkflowBoardWorkflowSelected, WorkflowDefinitionDetailViewed, WorkflowInstanceDetailViewed, WorkflowTitleSeedDrifted, WorkflowToolOpened, activityLabel, assigneesOf, countBy, definitionFingerprint, definitionsRouteOf, describeError, disambiguatedNames, dueDatesOf, findActivity, 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 };
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 };