@sanity/workflow-studio-plugin 0.22.0 → 0.23.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,30 +1,835 @@
1
1
  "use strict";
2
2
 
3
- var jsxRuntime = require("react/jsx-runtime"), ui = require("@sanity/ui"), workflowReact = require("@sanity/workflow-react"), react = require("react"), router = require("sanity/router"), index = require("./index.cjs"), ArrowLeft = require("@sanity/icons/ArrowLeft"), workflowDiagram = require("@sanity/workflow-diagram"), workflowEngine = require("@sanity/workflow-engine"), Add = require("@sanity/icons/Add"), Search = require("@sanity/icons/Search"), sanity = require("sanity"), Checkmark = require("@sanity/icons/Checkmark"), Close = require("@sanity/icons/Close"), Filter = require("@sanity/icons/Filter"), workflowComponents = require("@sanity/workflow-components"), endOfDay = require("date-fns/endOfDay"), reactDom = require("react-dom"), Calendar = require("@sanity/icons/Calendar"), workflowStudio = require("@sanity/workflow-studio");
3
+ var jsxRuntime = require("react/jsx-runtime"), ui = require("@sanity/ui"), workflowReact = require("@sanity/workflow-react"), react = require("react"), router = require("sanity/router"), index = require("./index.cjs"), workflowDiagram = require("@sanity/workflow-diagram"), sanity = require("sanity"), workflowEngine = require("@sanity/workflow-engine"), InfoOutline = require("@sanity/icons/InfoOutline"), pluralize = require("pluralize-esm"), workflowStudio = require("@sanity/workflow-studio"), ArrowLeft = require("@sanity/icons/ArrowLeft"), Clock = require("@sanity/icons/Clock"), Cog = require("@sanity/icons/Cog"), EllipsisHorizontal = require("@sanity/icons/EllipsisHorizontal"), Add = require("@sanity/icons/Add"), Search = require("@sanity/icons/Search"), Checkmark = require("@sanity/icons/Checkmark"), Close = require("@sanity/icons/Close"), Filter = require("@sanity/icons/Filter"), workflowComponents = require("@sanity/workflow-components"), endOfDay = require("date-fns/endOfDay"), reactDom = require("react-dom"), Calendar = require("@sanity/icons/Calendar");
4
4
 
5
- function documentRefsOf(instance) {
6
- const seen = /* @__PURE__ */ new Map;
7
- for (const ref of workflowEngine.entryDocRefs(instance.fields)) seen.has(ref.id) || seen.set(ref.id, ref);
8
- return [ ...seen.values() ];
5
+ function _interopDefaultCompat(e) {
6
+ return e && typeof e == "object" && "default" in e ? e : {
7
+ default: e
8
+ };
9
9
  }
10
10
 
11
- function InstanceTaskLists({entry: entry, onOpenActivity: onOpenActivity}) {
12
- return index.instanceNotice(entry) || /* @__PURE__ */ jsxRuntime.jsx(index.StaleLock, {
13
- stale: index.isEvaluationStale(entry),
14
- children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, {
11
+ var pluralize__default = /* @__PURE__ */ _interopDefaultCompat(pluralize);
12
+
13
+ function MutedNote({text: text}) {
14
+ /* @__PURE__ */
15
+ return jsxRuntime.jsx(ui.Box, {
16
+ paddingY: 3,
17
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
18
+ muted: !0,
19
+ size: 1,
20
+ children: text
21
+ })
22
+ });
23
+ }
24
+
25
+ function definitionCatalog(args) {
26
+ return args.definitions.map(definition => ({
27
+ name: definition.name,
28
+ title: definition.title ?? definition.name,
29
+ description: definition.description,
30
+ activeCount: args.inFlight.filter(instance => instance.definition === definition.name).length,
31
+ docTypes: [ ...new Set(args.mappings.filter(mapping => mapping.definition === definition.name).map(mapping => mapping.docType)) ]
32
+ })).sort((a, b) => a.title.localeCompare(b.title) || a.name.localeCompare(b.name));
33
+ }
34
+
35
+ function docTypeLabels(docTypes, titleOf) {
36
+ return docTypes.map(name => ({
37
+ name: name,
38
+ title: titleOf(name) ?? name
39
+ })).sort((a, b) => a.title.localeCompare(b.title) || a.name.localeCompare(b.name));
40
+ }
41
+
42
+ function startPhrase(definition) {
43
+ if (definition.lifecycle === "child") return "Spawned by a parent workflow";
44
+ if (workflowEngine.startKindOf(definition) === "autonomous") return "Starts automatically";
45
+ const subject = (definition.fields ?? []).find(workflowEngine.isSubjectEntry);
46
+ return subject !== void 0 && workflowEngine.isInputSourced(subject) ? "Started manually, from a document" : "Started manually";
47
+ }
48
+
49
+ function deployedAtOf(definition) {
50
+ return typeof definition._updatedAt == "string" ? definition._updatedAt : void 0;
51
+ }
52
+
53
+ function DetailTitle({busy: busy = !1, documentGdr: documentGdr, title: title}) {
54
+ const chipPullback = -index.useSpaceToken(1);
55
+ /* @__PURE__ */
56
+ return jsxRuntime.jsxs(ui.Flex, {
57
+ align: "center",
58
+ gap: 2,
59
+ paddingLeft: 2,
60
+ children: [
61
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
62
+ style: {
63
+ minWidth: 0
64
+ },
65
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
66
+ size: 1,
67
+ textOverflow: "ellipsis",
68
+ weight: "semibold",
69
+ children: title
70
+ })
71
+ }), documentGdr === void 0 ? null : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, {
72
+ children: [
73
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
74
+ muted: !0,
75
+ size: 1,
76
+ children: "in"
77
+ }),
78
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, {
79
+ align: "center",
80
+ flex: "none",
81
+ style: {
82
+ height: 0,
83
+ marginLeft: chipPullback
84
+ },
85
+ children: /* @__PURE__ */ jsxRuntime.jsx(index.DocPreviewLink, {
86
+ gdr: documentGdr,
87
+ layout: "inline"
88
+ })
89
+ }) ]
90
+ }),
91
+ /* @__PURE__ */ jsxRuntime.jsx(index.SpinnerSlot, {
92
+ busy: busy
93
+ }) ]
94
+ });
95
+ }
96
+
97
+ const lastRead = /* @__PURE__ */ new WeakMap, issuedReads = /* @__PURE__ */ new WeakMap;
98
+
99
+ function useDeployedDefinitions() {
100
+ const {engine: engine} = index.useWorkflowContext(), [definitions, setDefinitions] = react.useState(() => lastRead.get(engine)?.rows), [error, setError] = react.useState(void 0);
101
+ return react.useEffect(() => {
102
+ let cancelled = !1;
103
+ setDefinitions(lastRead.get(engine)?.rows), setError(void 0);
104
+ const seq = (issuedReads.get(engine) ?? 0) + 1;
105
+ return issuedReads.set(engine, seq), (async () => {
106
+ const rows = await index.readDeployedDefinitions(engine), latest = workflowEngine.latestDeployedDefinitions(rows), held = lastRead.get(engine);
107
+ (held === void 0 || seq > held.seq) && lastRead.set(engine, {
108
+ seq: seq,
109
+ rows: latest
110
+ }), cancelled || (setDefinitions(latest), setError(void 0));
111
+ })().catch(err => {
112
+ cancelled || setError(index.describeError(err));
113
+ }), () => {
114
+ cancelled = !0;
115
+ };
116
+ }, [ engine ]), {
117
+ definitions: definitions,
118
+ error: error
119
+ };
120
+ }
121
+
122
+ function DefinitionDetail({definitionName: definitionName}) {
123
+ const {definitions: definitions, error: error} = useDeployedDefinitions(), snapshotWidth = index.useContainerToken(2), definition = definitions?.find(row => row.name === definitionName);
124
+ /* @__PURE__ */
125
+ return jsxRuntime.jsxs(ui.Flex, {
126
+ direction: "column",
127
+ height: "fill",
128
+ overflow: "hidden",
129
+ children: [
130
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
131
+ flex: "none",
132
+ paddingBottom: 2,
133
+ paddingTop: 3,
134
+ paddingX: 3,
135
+ children: /* @__PURE__ */ jsxRuntime.jsx(DetailTitle, {
136
+ busy: definitions === void 0 && error === void 0,
137
+ title: definition?.title ?? definitionName
138
+ })
139
+ }),
140
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
141
+ flex: 1,
142
+ overflow: "auto",
143
+ paddingX: 3,
144
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
145
+ paddingBottom: 5,
146
+ paddingLeft: 2,
147
+ paddingTop: 2,
148
+ style: {
149
+ maxWidth: snapshotWidth
150
+ },
151
+ children: /* @__PURE__ */ jsxRuntime.jsx(DefinitionBody, {
152
+ definition: definition,
153
+ error: error,
154
+ loaded: definitions !== void 0
155
+ })
156
+ })
157
+ }) ]
158
+ });
159
+ }
160
+
161
+ function DefinitionBody({definition: definition, error: error, loaded: loaded}) {
162
+ return error !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(MutedNote, {
163
+ text: `Could not load the deployed workflows: ${error}`
164
+ }) : loaded ? definition === void 0 ? /* @__PURE__ */ jsxRuntime.jsx(MutedNote, {
165
+ text: "No deployed workflow definition with this name — it may have been retracted."
166
+ }) : /* @__PURE__ */ jsxRuntime.jsx(DefinitionFacts, {
167
+ definition: definition
168
+ }) : /* @__PURE__ */ jsxRuntime.jsx(index.LoadingRow, {
169
+ label: "Loading definition…"
170
+ });
171
+ }
172
+
173
+ function DefinitionFacts({definition: definition}) {
174
+ const {binding: binding, mappings: mappings} = index.useWorkflowContext(), schema = sanity.useSchema(), deployedAt = deployedAtOf(definition), [row] = definitionCatalog({
175
+ definitions: [ definition ],
176
+ mappings: mappings,
177
+ inFlight: []
178
+ }), runsOn = docTypeLabels(row?.docTypes ?? [], name => schema.get(name)?.title).map(docType => docType.title);
179
+ /* @__PURE__ */
180
+ return jsxRuntime.jsxs(ui.Stack, {
181
+ gap: 4,
182
+ children: [
183
+ /* @__PURE__ */ jsxRuntime.jsx(index.MetaRow, {
184
+ label: "Deployed",
185
+ children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, {
186
+ size: 1,
187
+ children: [ deployedAt === void 0 ? `v${definition.version}` : index.formatDate(deployedAt), " ·", " ", binding.tag ]
188
+ })
189
+ }),
190
+ /* @__PURE__ */ jsxRuntime.jsx(index.MetaRow, {
191
+ label: "Trigger",
192
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
193
+ size: 1,
194
+ children: startPhrase(definition)
195
+ })
196
+ }),
197
+ /* @__PURE__ */ jsxRuntime.jsx(index.MetaRow, {
198
+ label: "Document types",
199
+ children: runsOn.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
200
+ muted: !0,
201
+ size: 1,
202
+ children: "No document types in this Studio"
203
+ }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
204
+ size: 1,
205
+ children: runsOn.join(", ")
206
+ })
207
+ }),
208
+ /* @__PURE__ */ jsxRuntime.jsx(index.MetaRow, {
209
+ label: "Description",
210
+ children: definition.description === void 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
211
+ muted: !0,
212
+ size: 1,
213
+ children: /* @__PURE__ */ jsxRuntime.jsx("em", {
214
+ children: "No description"
215
+ })
216
+ }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
217
+ size: 1,
218
+ style: {
219
+ whiteSpace: "pre-wrap"
220
+ },
221
+ children: definition.description
222
+ })
223
+ }),
224
+ /* @__PURE__ */ jsxRuntime.jsx(workflowDiagram.WorkflowDiagram, {
225
+ definition: definition,
226
+ explain: !0,
227
+ height: 340
228
+ }) ]
229
+ });
230
+ }
231
+
232
+ function TabStatusRow({children: children}) {
233
+ /* @__PURE__ */
234
+ return jsxRuntime.jsx(ui.Box, {
235
+ paddingLeft: 2,
236
+ paddingY: 3,
237
+ children: children
238
+ });
239
+ }
240
+
241
+ const INSTANCE_CAP = 200, NEWEST_INSTANCES = {
242
+ includeCompleted: !0,
243
+ limit: INSTANCE_CAP
244
+ };
245
+
246
+ function useToolInstances() {
247
+ const {engine: engine} = index.useWorkflowContext(), {instances: instances, loading: loading, unreadable: unreadable} = workflowStudio.useWorkflowInstances({
248
+ engine: engine,
249
+ filter: NEWEST_INSTANCES
250
+ });
251
+ return react.useMemo(() => {
252
+ const rows = instances ?? [];
253
+ return {
254
+ inFlight: rows.filter(instance => workflowEngine.terminalState(instance) === "in-flight"),
255
+ settled: rows.filter(instance => workflowEngine.terminalState(instance) !== "in-flight"),
256
+ truncated: rows.length + unreadable.length === INSTANCE_CAP,
257
+ loading: loading,
258
+ unreadable: unreadable
259
+ };
260
+ }, [ instances, loading, unreadable ]);
261
+ }
262
+
263
+ const MAX_VISIBLE_DOC_TYPES = 4, COLUMNS = {
264
+ definition: 6,
265
+ instances: 2,
266
+ docTypes: 2
267
+ };
268
+
269
+ function Col({flex: flex, children: children}) {
270
+ /* @__PURE__ */
271
+ return jsxRuntime.jsx(ui.Box, {
272
+ flex: flex,
273
+ paddingRight: 3,
274
+ children: children
275
+ });
276
+ }
277
+
278
+ function DefinitionsTab({onOpenDefinition: onOpenDefinition}) {
279
+ const {definitions: definitions, error: error} = useDeployedDefinitions(), {mappings: mappings} = index.useWorkflowContext(), instances = useToolInstances();
280
+ if (error !== void 0) /* @__PURE__ */
281
+ return jsxRuntime.jsx(TabStatusRow, {
282
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
283
+ muted: !0,
284
+ size: 1,
285
+ children: `Could not load the deployed workflows: ${error}`
286
+ })
287
+ });
288
+ if (definitions === void 0) /* @__PURE__ */
289
+ return jsxRuntime.jsx(TabStatusRow, {
290
+ children: /* @__PURE__ */ jsxRuntime.jsx(index.LoadingRow, {
291
+ label: "Loading definitions…"
292
+ })
293
+ });
294
+ if (definitions.length === 0) /* @__PURE__ */
295
+ return jsxRuntime.jsx(TabStatusRow, {
296
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
297
+ muted: !0,
298
+ size: 1,
299
+ children: "No workflow definitions are deployed"
300
+ })
301
+ });
302
+ const rows = definitionCatalog({
303
+ definitions: definitions,
304
+ mappings: mappings,
305
+ inFlight: instances.inFlight
306
+ }), active = rows.reduce((sum, row) => sum + row.activeCount, 0);
307
+ /* @__PURE__ */
308
+ return jsxRuntime.jsxs(ui.Stack, {
309
+ gap: 3,
310
+ paddingBottom: 4,
311
+ children: [
312
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
313
+ paddingTop: 3,
314
+ paddingX: 2,
315
+ children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, {
316
+ align: "center",
317
+ gap: 2,
318
+ children: [
319
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
320
+ size: 1,
321
+ weight: "semibold",
322
+ children: pluralize__default.default("definition", rows.length, !0)
323
+ }),
324
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
325
+ muted: !0,
326
+ size: 1,
327
+ children: instances.loading ? "counting active instances…" : activeSummary(active)
328
+ }) ]
329
+ })
330
+ }),
331
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, {
332
+ gap: 2,
15
333
  children: [
16
- /* @__PURE__ */ jsxRuntime.jsx(index.ActivitiesList, {
17
- entry: entry,
18
- onOpenActivity: onOpenActivity
334
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Card, {
335
+ paddingX: 2,
336
+ paddingY: 2,
337
+ style: {
338
+ position: "sticky",
339
+ top: 0,
340
+ zIndex: 1
341
+ },
342
+ children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, {
343
+ align: "center",
344
+ gap: 4,
345
+ children: [
346
+ /* @__PURE__ */ jsxRuntime.jsx(Col, {
347
+ flex: COLUMNS.definition,
348
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
349
+ muted: !0,
350
+ size: 1,
351
+ weight: "medium",
352
+ children: "Definition"
353
+ })
354
+ }),
355
+ /* @__PURE__ */ jsxRuntime.jsx(Col, {
356
+ flex: COLUMNS.instances,
357
+ children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, {
358
+ align: "center",
359
+ gap: 2,
360
+ children: [
361
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
362
+ muted: !0,
363
+ size: 1,
364
+ weight: "medium",
365
+ children: "Active instances"
366
+ }),
367
+ /* @__PURE__ */ jsxRuntime.jsx(index.HoverHint, {
368
+ text: "Counts only active instances — completed and aborted workflows are not included",
369
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
370
+ muted: !0,
371
+ size: 1,
372
+ style: {
373
+ opacity: .7
374
+ },
375
+ children: /* @__PURE__ */ jsxRuntime.jsx(InfoOutline.InfoOutlineIcon, {})
376
+ })
377
+ }) ]
378
+ })
379
+ }),
380
+ /* @__PURE__ */ jsxRuntime.jsx(Col, {
381
+ flex: COLUMNS.docTypes,
382
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
383
+ muted: !0,
384
+ size: 1,
385
+ weight: "medium",
386
+ children: "Document types"
387
+ })
388
+ }) ]
389
+ })
390
+ }),
391
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Stack, {
392
+ gap: 1,
393
+ children: rows.map(row => /* @__PURE__ */ jsxRuntime.jsx(DefinitionRow, {
394
+ countPending: instances.loading,
395
+ onOpen: () => onOpenDefinition(row.name),
396
+ row: row
397
+ }, row.name))
398
+ }) ]
399
+ }), instances.truncated ? /* @__PURE__ */ jsxRuntime.jsx(MutedNote, {
400
+ text: `Counts from the latest ${INSTANCE_CAP} workflows — older ones aren't counted.`
401
+ }) : null ]
402
+ });
403
+ }
404
+
405
+ function activeSummary(active) {
406
+ return `${pluralize__default.default("active instance", active, !0)}`;
407
+ }
408
+
409
+ function DefinitionRow({countPending: countPending, onOpen: onOpen, row: row}) {
410
+ /* @__PURE__ */
411
+ return jsxRuntime.jsx(ui.Card, {
412
+ as: "button",
413
+ onClick: onOpen,
414
+ paddingX: 2,
415
+ paddingY: 3,
416
+ radius: 2,
417
+ style: {
418
+ textAlign: "left"
419
+ },
420
+ children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, {
421
+ align: "flex-start",
422
+ gap: 4,
423
+ children: [
424
+ /* @__PURE__ */ jsxRuntime.jsx(Col, {
425
+ flex: COLUMNS.definition,
426
+ children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, {
427
+ gap: 3,
428
+ style: {
429
+ minWidth: 0
430
+ },
431
+ children: [
432
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
433
+ size: 1,
434
+ weight: "semibold",
435
+ children: row.title
436
+ }), row.description === void 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
437
+ muted: !0,
438
+ size: 1,
439
+ children: /* @__PURE__ */ jsxRuntime.jsx("em", {
440
+ children: "No description"
441
+ })
442
+ }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
443
+ muted: !0,
444
+ size: 1,
445
+ children: row.description
446
+ }) ]
447
+ })
448
+ }),
449
+ /* @__PURE__ */ jsxRuntime.jsx(Col, {
450
+ flex: COLUMNS.instances,
451
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
452
+ muted: !0,
453
+ size: 1,
454
+ children: countPending ? "—" : `${row.activeCount} active`
455
+ })
456
+ }),
457
+ /* @__PURE__ */ jsxRuntime.jsx(Col, {
458
+ flex: COLUMNS.docTypes,
459
+ children: /* @__PURE__ */ jsxRuntime.jsx(DocTypeChips, {
460
+ docTypes: row.docTypes
461
+ })
462
+ }) ]
463
+ })
464
+ });
465
+ }
466
+
467
+ function DocTypeChips({docTypes: docTypes}) {
468
+ const schema = sanity.useSchema(), badgeTrim = index.useBadgeCapTrim();
469
+ if (docTypes.length === 0) /* @__PURE__ */
470
+ return jsxRuntime.jsx(ui.Text, {
471
+ muted: !0,
472
+ size: 1,
473
+ children: "—"
474
+ });
475
+ const labels = docTypeLabels(docTypes, name => schema.get(name)?.title), shown = labels.slice(0, MAX_VISIBLE_DOC_TYPES), overflow = labels.length - shown.length;
476
+ /* @__PURE__ */
477
+ return jsxRuntime.jsxs(ui.Flex, {
478
+ align: "center",
479
+ gap: 2,
480
+ style: badgeTrim,
481
+ wrap: "wrap",
482
+ children: [ shown.map(docType => /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, {
483
+ fontSize: 1,
484
+ children: docType.title
485
+ }, docType.name)), overflow > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, {
486
+ muted: !0,
487
+ size: 1,
488
+ children: [ "+", overflow, " more" ]
489
+ }) : null ]
490
+ });
491
+ }
492
+
493
+ function makeTitleResolver(definition) {
494
+ const activities = (definition?.stages ?? []).flatMap(s => s.activities ?? []);
495
+ return {
496
+ action: (activityName, actionName) => activities.find(t => t.name === activityName)?.actions?.find(a => a.name === actionName)?.title ?? actionName,
497
+ activity: name => activities.find(t => t.name === name)?.title ?? name,
498
+ stage: name => index.stageTitle(definition, name)
499
+ };
500
+ }
501
+
502
+ function attributed(actor, ...parts) {
503
+ if (actor) return {
504
+ actorId: actor.id,
505
+ parts: parts
506
+ };
507
+ const [first, ...rest] = parts;
508
+ return typeof first != "string" ? {
509
+ actorId: void 0,
510
+ parts: parts
511
+ } : {
512
+ actorId: void 0,
513
+ parts: [ first.charAt(0).toUpperCase() + first.slice(1), ...rest ]
514
+ };
515
+ }
516
+
517
+ function standalone(...parts) {
518
+ return {
519
+ actorId: void 0,
520
+ parts: parts
521
+ };
522
+ }
523
+
524
+ const historyLiner = {
525
+ stageEntered: (h, titles) => attributed(h.actor, `entered ${titles.stage(h.stage)}${h.fromStage ? ` from ${titles.stage(h.fromStage)}` : ""}`),
526
+ stageExited: (h, titles) => attributed(h.actor, `left ${titles.stage(h.stage)} for ${titles.stage(h.toStage)}`),
527
+ activityStatusChanged: (h, titles) => attributed(h.actor, `changed ${titles.activity(h.activity)}: ${h.from} → ${h.to}`),
528
+ actionFired: (h, titles) => {
529
+ const phrase = `fired ${titles.action(h.activity, h.action)} on ${titles.activity(h.activity)}`;
530
+ return h.triggered ? standalone(`The workflow ${phrase}`) : attributed(h.actor, phrase);
531
+ },
532
+ transitionFired: (h, titles) => attributed(h.actor, `moved ${titles.stage(h.fromStage)} → ${titles.stage(h.toStage)}`),
533
+ effectQueued: h => standalone("Effect ", {
534
+ code: h.effect
535
+ }, " queued"),
536
+ effectCompleted: h => {
537
+ const detail = h.detail ? ` — ${h.detail}` : "";
538
+ return h.actor ? attributed(h.actor, "resolved effect ", {
539
+ code: h.effect
540
+ }, ` — ${h.status}${detail}`) : standalone("Effect ", {
541
+ code: h.effect
542
+ }, ` ${h.status}${detail}`);
543
+ },
544
+ effectClaimReleased: h => attributed(h.actor, "released a stale claim on effect ", {
545
+ code: h.effect
546
+ }, ` ${h.via === "sweep" ? "via the sweeper" : "via a drain takeover"}`),
547
+ spawned: (h, titles) => attributed(h.actor, `spawned a child workflow from ${titles.activity(h.activity)}`),
548
+ subworkflowAdopted: (h, titles) => standalone(`Re-adopted child workflow into ${titles.activity(h.activity)} on re-entering ${titles.stage(h.stage)}`),
549
+ subworkflowResolved: (h, titles) => standalone(`Child workflow of ${titles.activity(h.activity)} ${h.status === "done" ? "completed" : "was aborted"}`),
550
+ subworkflowOrphaned: h => standalone(`Orphaned child workflow reached terminal — ${h.detail}`),
551
+ aborted: (h, titles) => attributed(h.actor, `aborted the workflow at ${titles.stage(h.stage)}${h.reason ? ` — ${h.reason}` : ""}`),
552
+ opApplied: (h, titles) => h.action ? attributed(h.actor, `changed state via action ${titles.action(h.activity ?? "", h.action)}`) : h.activity ? attributed(h.actor, `changed state via activity ${titles.activity(h.activity)}`) : attributed(h.actor, "changed state via ", {
553
+ code: h.opType
554
+ }),
555
+ fieldQueryDiscarded: h => standalone({
556
+ code: JSON.stringify(h)
557
+ })
558
+ };
559
+
560
+ function historyLine(h, titles) {
561
+ return historyLiner[h._type](h, titles);
562
+ }
563
+
564
+ function PendingEffectRows({instance: instance, now: now}) {
565
+ const {drainEffectsFor: drainEffectsFor, completeEffectFor: completeEffectFor, effectHandlers: effectHandlers} = index.useWorkflowContext(), toast = index.useClosableToast(), [busy, setBusy] = react.useState(!1), pending = instance.pendingEffects ?? [];
566
+ if (pending.length === 0) return null;
567
+ const runRegistered = async () => {
568
+ setBusy(!0);
569
+ try {
570
+ await drainEffectsFor(instance._id), toast.push({
571
+ status: "success",
572
+ title: "Ran registered effect handlers"
573
+ });
574
+ } catch (err) {
575
+ toast.push({
576
+ status: "error",
577
+ title: "Drain failed",
578
+ description: index.describeError(err)
579
+ });
580
+ } finally {
581
+ setBusy(!1);
582
+ }
583
+ }, resolve = async (effectKey, status) => {
584
+ setBusy(!0);
585
+ try {
586
+ await completeEffectFor(instance._id, {
587
+ effectKey: effectKey,
588
+ status: status,
589
+ detail: "Resolved in Studio"
590
+ }), toast.push({
591
+ status: "success",
592
+ title: `Effect marked ${status}`
593
+ });
594
+ } catch (err) {
595
+ toast.push({
596
+ status: "error",
597
+ title: "Couldn’t resolve effect",
598
+ description: index.describeError(err)
599
+ });
600
+ } finally {
601
+ setBusy(!1);
602
+ }
603
+ };
604
+ /* @__PURE__ */
605
+ return jsxRuntime.jsx(jsxRuntime.Fragment, {
606
+ children: pending.map(fx => /* @__PURE__ */ jsxRuntime.jsx(PendingEffectRow, {
607
+ busy: busy,
608
+ effect: fx,
609
+ instanceId: instance._id,
610
+ now: now,
611
+ onResolve: status => resolve(fx._key, status),
612
+ onRunHandlers: runRegistered,
613
+ registered: fx.name in effectHandlers
614
+ }, fx._key))
615
+ });
616
+ }
617
+
618
+ function PendingEffectRow({busy: busy, effect: effect, instanceId: instanceId, now: now, onResolve: onResolve, onRunHandlers: onRunHandlers, registered: registered}) {
619
+ const label = effect.title ?? effect.name, avatarSize = index.useAvatarSize();
620
+ /* @__PURE__ */
621
+ return jsxRuntime.jsxs(ui.Flex, {
622
+ align: "center",
623
+ gap: 2,
624
+ children: [
625
+ /* @__PURE__ */ jsxRuntime.jsx(FeedGlyph, {
626
+ hint: "Waiting to run",
627
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Clock.ClockIcon, {})
628
+ }),
629
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, {
630
+ align: "center",
631
+ flex: 1,
632
+ gap: 1,
633
+ style: {
634
+ minWidth: 0
635
+ },
636
+ children: [
637
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
638
+ style: {
639
+ minWidth: 0
640
+ },
641
+ children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, {
642
+ size: 1,
643
+ textOverflow: "ellipsis",
644
+ children: [ effect.title ?? /* @__PURE__ */ jsxRuntime.jsx(CodeChip, {
645
+ value: effect.name
646
+ }), registered ? " — ready to run in Studio" : " — waiting for the runtime" ]
647
+ })
648
+ }),
649
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
650
+ muted: !0,
651
+ size: 1,
652
+ children: "·"
653
+ }),
654
+ /* @__PURE__ */ jsxRuntime.jsx(index.HoverHint, {
655
+ text: index.formatDateTime(effect.queuedAt),
656
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
657
+ muted: !0,
658
+ size: 1,
659
+ style: {
660
+ whiteSpace: "nowrap"
661
+ },
662
+ children: index.formatShortAgo(effect.queuedAt, now)
663
+ })
664
+ }) ]
665
+ }),
666
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, {
667
+ align: "center",
668
+ flex: "none",
669
+ style: {
670
+ height: avatarSize
671
+ },
672
+ children: /* @__PURE__ */ jsxRuntime.jsx(index.HintedMenuButton, {
673
+ button: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, {
674
+ "aria-label": `Resolve ${label}`,
675
+ disabled: busy,
676
+ fontSize: 1,
677
+ icon: EllipsisHorizontal.EllipsisHorizontalIcon,
678
+ mode: "bleed",
679
+ padding: 2
680
+ }),
681
+ hint: "Resolve",
682
+ id: `pending-effect-menu-${instanceId}-${effect._key}`,
683
+ menu: /* @__PURE__ */ jsxRuntime.jsxs(ui.Menu, {
684
+ children: [ registered ? /* @__PURE__ */ jsxRuntime.jsx(ui.MenuItem, {
685
+ onClick: onRunHandlers,
686
+ text: "Run registered handlers"
687
+ }) : null,
688
+ /* @__PURE__ */ jsxRuntime.jsx(ui.MenuItem, {
689
+ onClick: () => onResolve("done"),
690
+ text: "Mark done"
691
+ }),
692
+ /* @__PURE__ */ jsxRuntime.jsx(ui.MenuItem, {
693
+ onClick: () => onResolve("failed"),
694
+ text: "Mark failed",
695
+ tone: "critical"
696
+ }) ]
697
+ }),
698
+ popover: {
699
+ placement: "bottom-end",
700
+ portal: !0
701
+ }
702
+ })
703
+ }) ]
704
+ });
705
+ }
706
+
707
+ function ActivityLog({instance: instance, definition: definition}) {
708
+ const titles = react.useMemo(() => makeTitleResolver(definition), [ definition ]), entries = react.useMemo(() => instance.history.toReversed(), [ instance.history ]), [now, setNow] = react.useState(() => /* @__PURE__ */ new Date);
709
+ return react.useEffect(() => {
710
+ const timer = setInterval(() => setNow(/* @__PURE__ */ new Date), 6e4);
711
+ return () => clearInterval(timer);
712
+ }, []), entries.length === 0 && (instance.pendingEffects ?? []).length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
713
+ muted: !0,
714
+ size: 1,
715
+ children: "No activity yet"
716
+ }) : /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, {
717
+ gap: 3,
718
+ children: [
719
+ /* @__PURE__ */ jsxRuntime.jsx(PendingEffectRows, {
720
+ instance: instance,
721
+ now: now
722
+ }), entries.map(h => /* @__PURE__ */ jsxRuntime.jsx(HistoryRow, {
723
+ entry: h,
724
+ now: now,
725
+ titles: titles
726
+ }, h._key)) ]
727
+ });
728
+ }
729
+
730
+ function HistoryRow({entry: entry, now: now, titles: titles}) {
731
+ const line = historyLine(entry, titles);
732
+ /* @__PURE__ */
733
+ return jsxRuntime.jsxs(ui.Flex, {
734
+ align: "center",
735
+ gap: 2,
736
+ children: [ line.actorId === void 0 ? /* @__PURE__ */ jsxRuntime.jsx(FeedGlyph, {
737
+ hint: "The workflow",
738
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Cog.CogIcon, {})
739
+ }) : /* @__PURE__ */ jsxRuntime.jsx(index.UserAvatar, {
740
+ id: line.actorId
741
+ }),
742
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, {
743
+ align: "center",
744
+ flex: 1,
745
+ gap: 1,
746
+ style: {
747
+ minWidth: 0
748
+ },
749
+ children: [
750
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
751
+ style: {
752
+ minWidth: 0
753
+ },
754
+ children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, {
755
+ size: 1,
756
+ textOverflow: "ellipsis",
757
+ children: [ line.actorId === void 0 ? null : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, {
758
+ children: [
759
+ /* @__PURE__ */ jsxRuntime.jsx(ActorNameSpan, {
760
+ id: line.actorId
761
+ }), " " ]
762
+ }), line.parts.map((part, index2) => typeof part == "string" ? part : /* @__PURE__ */ jsxRuntime.jsx(CodeChip, {
763
+ value: part.code
764
+ }, index2)) ]
765
+ })
766
+ }),
767
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
768
+ muted: !0,
769
+ size: 1,
770
+ children: "·"
19
771
  }),
20
- /* @__PURE__ */ jsxRuntime.jsx(index.TodoItemsList, {
21
- entry: entry,
22
- surface: "tool-instance-detail"
772
+ /* @__PURE__ */ jsxRuntime.jsx(index.HoverHint, {
773
+ text: index.formatDateTime(entry.at),
774
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
775
+ muted: !0,
776
+ size: 1,
777
+ style: {
778
+ whiteSpace: "nowrap"
779
+ },
780
+ children: index.formatShortAgo(entry.at, now)
781
+ })
23
782
  }) ]
783
+ }) ]
784
+ });
785
+ }
786
+
787
+ function ActorNameSpan({id: id}) {
788
+ const {name: name} = index.useUserDisplay(id), {font: font} = ui.useTheme_v2();
789
+ /* @__PURE__ */
790
+ return jsxRuntime.jsx("span", {
791
+ style: {
792
+ fontWeight: font.text.weights.medium
793
+ },
794
+ children: name
795
+ });
796
+ }
797
+
798
+ function CodeChip({value: value}) {
799
+ /* @__PURE__ */
800
+ return jsxRuntime.jsx("code", {
801
+ children: value
802
+ });
803
+ }
804
+
805
+ function FeedGlyph({hint: hint, icon: icon}) {
806
+ const size = index.useAvatarSize();
807
+ /* @__PURE__ */
808
+ return jsxRuntime.jsx(index.HoverHint, {
809
+ text: hint,
810
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, {
811
+ align: "center",
812
+ flex: "none",
813
+ justify: "center",
814
+ style: {
815
+ height: size,
816
+ width: size
817
+ },
818
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
819
+ muted: !0,
820
+ size: 1,
821
+ children: icon
822
+ })
24
823
  })
25
824
  });
26
825
  }
27
826
 
827
+ function documentRefsOf(instance) {
828
+ const seen = /* @__PURE__ */ new Map;
829
+ for (const ref of workflowEngine.entryDocRefs(instance.fields)) seen.has(ref.id) || seen.set(ref.id, ref);
830
+ return [ ...seen.values() ];
831
+ }
832
+
28
833
  function WorkflowInstanceDetail({instanceId: instanceId, onBack: onBack}) {
29
834
  const entry = index.useWorkflowInstanceEntry(instanceId);
30
835
  index.useLogEventOnMount(index.WorkflowInstanceDetailViewed, {
@@ -40,15 +845,16 @@ function WorkflowInstanceDetail({instanceId: instanceId, onBack: onBack}) {
40
845
  invalid: entry.invalid
41
846
  })
42
847
  }) : entry ? /* @__PURE__ */ jsxRuntime.jsx(InstanceDetailPanel, {
43
- entry: entry,
44
- onBack: onBack
848
+ entry: entry
45
849
  }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
46
- padding: 4,
47
- children: graceOver ? /* @__PURE__ */ jsxRuntime.jsx(NotFound, {
48
- id: instanceId,
49
- onBack: onBack
50
- }) : /* @__PURE__ */ jsxRuntime.jsx(index.LoadingRow, {
51
- label: "Loading workflow…"
850
+ paddingX: 3,
851
+ children: /* @__PURE__ */ jsxRuntime.jsx(TabStatusRow, {
852
+ children: graceOver ? /* @__PURE__ */ jsxRuntime.jsx(NotFound, {
853
+ id: instanceId,
854
+ onBack: onBack
855
+ }) : /* @__PURE__ */ jsxRuntime.jsx(index.LoadingRow, {
856
+ label: "Loading workflow…"
857
+ })
52
858
  })
53
859
  });
54
860
  }
@@ -76,8 +882,8 @@ function NotFound({id: id, onBack: onBack}) {
76
882
  });
77
883
  }
78
884
 
79
- function InstanceDetailPanel({entry: entry, onBack: onBack}) {
80
- const definition = index.useDefinition(entry), [openActivity, setOpenActivity] = react.useState(null), {instance: instance} = entry;
885
+ function InstanceDetailPanel({entry: entry}) {
886
+ const definition = index.useDefinition(entry), snapshotWidth = index.useContainerToken(2), [openActivity, setOpenActivity] = react.useState(null), {instance: instance, ready: ready} = entry;
81
887
  return react.useEffect(() => {
82
888
  openActivity && index.openActivityGone({
83
889
  entry: entry,
@@ -88,36 +894,52 @@ function InstanceDetailPanel({entry: entry, onBack: onBack}) {
88
894
  height: "fill",
89
895
  overflow: "hidden",
90
896
  children: [
91
- /* @__PURE__ */ jsxRuntime.jsx(DetailHeader, {
92
- definition: definition,
93
- entry: entry,
94
- onBack: onBack
897
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
898
+ flex: "none",
899
+ paddingBottom: 2,
900
+ paddingTop: 3,
901
+ paddingX: 3,
902
+ children: /* @__PURE__ */ jsxRuntime.jsx(DetailTitle, {
903
+ busy: !ready || index.isEvaluationStale(entry),
904
+ documentGdr: documentRefsOf(instance)[0],
905
+ title: index.instanceTitle(instance, definition)
906
+ })
95
907
  }),
96
908
  /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
97
909
  flex: 1,
98
910
  overflow: "auto",
99
- paddingX: 4,
100
- children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, {
101
- gap: 4,
911
+ paddingX: 3,
912
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
102
913
  paddingBottom: 5,
103
- children: [
104
- /* @__PURE__ */ jsxRuntime.jsx(StudioDocumentsSection, {
105
- instance: instance
106
- }),
107
- /* @__PURE__ */ jsxRuntime.jsx(StageDiagramSection, {
108
- definition: definition,
109
- entry: entry
110
- }),
111
- /* @__PURE__ */ jsxRuntime.jsx(index.GroupHeading, {
112
- title: "All tasks"
113
- }),
114
- /* @__PURE__ */ jsxRuntime.jsx(InstanceTaskLists, {
115
- entry: entry,
116
- onOpenActivity: name => setOpenActivity({
117
- stage: instance.currentStage,
118
- activityName: name
119
- })
120
- }) ]
914
+ paddingLeft: 2,
915
+ paddingTop: 2,
916
+ style: {
917
+ maxWidth: snapshotWidth
918
+ },
919
+ children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, {
920
+ gap: 6,
921
+ children: [
922
+ /* @__PURE__ */ jsxRuntime.jsx(index.InstanceSnapshotBody, {
923
+ entry: entry,
924
+ onOpenActivity: name => setOpenActivity({
925
+ stage: instance.currentStage,
926
+ activityName: name
927
+ })
928
+ }),
929
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, {
930
+ gap: 4,
931
+ children: [
932
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
933
+ size: 1,
934
+ weight: "semibold",
935
+ children: "History"
936
+ }),
937
+ /* @__PURE__ */ jsxRuntime.jsx(ActivityLog, {
938
+ definition: definition,
939
+ instance: instance
940
+ }) ]
941
+ }) ]
942
+ })
121
943
  })
122
944
  }), openActivity ? /* @__PURE__ */ jsxRuntime.jsx(index.ActivityDetailDialog, {
123
945
  activityName: openActivity.activityName,
@@ -130,130 +952,14 @@ function InstanceDetailPanel({entry: entry, onBack: onBack}) {
130
952
  });
131
953
  }
132
954
 
133
- function DetailHeader({entry: entry, definition: definition, onBack: onBack}) {
134
- const {instance: instance, ready: ready} = entry;
135
- /* @__PURE__ */
136
- return jsxRuntime.jsx(ui.Card, {
137
- flex: "none",
138
- paddingX: 4,
139
- paddingY: 3,
140
- children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, {
141
- align: "center",
142
- gap: 3,
143
- wrap: "wrap",
144
- children: [
145
- /* @__PURE__ */ jsxRuntime.jsx(index.HoverHint, {
146
- text: "Back to workflows",
147
- children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, {
148
- "aria-label": "Back to workflows",
149
- fontSize: 1,
150
- icon: ArrowLeft.ArrowLeftIcon,
151
- mode: "bleed",
152
- onClick: onBack,
153
- padding: 2
154
- })
155
- }),
156
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, {
157
- gap: 2,
158
- children: [
159
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
160
- size: 1,
161
- weight: "semibold",
162
- children: index.instanceTitle(instance, definition)
163
- }),
164
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, {
165
- muted: !0,
166
- size: 1,
167
- children: [ "Started ", index.formatDate(instance.startedAt) ]
168
- }) ]
169
- }), workflowEngine.terminalState(instance) === "in-flight" ? /* @__PURE__ */ jsxRuntime.jsx(index.StageChip, {
170
- title: index.stageTitle(definition, instance.currentStage)
171
- }) : /* @__PURE__ */ jsxRuntime.jsx(index.AbortedBadge, {
172
- instance: instance
173
- }),
174
- /* @__PURE__ */ jsxRuntime.jsx(index.SpinnerSlot, {
175
- busy: !ready || index.isEvaluationStale(entry)
176
- }) ]
177
- })
178
- });
179
- }
180
-
181
- function StudioDocumentsSection({instance: instance}) {
182
- const refs = documentRefsOf(instance);
183
- return refs.length === 0 ? null : /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, {
184
- gap: 3,
185
- marginTop: 3,
186
- children: [
187
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
188
- muted: !0,
189
- size: 1,
190
- weight: "medium",
191
- children: refs.length === 1 ? "Studio document" : "Studio documents"
192
- }),
193
- /* @__PURE__ */ jsxRuntime.jsx(ui.Stack, {
194
- gap: 2,
195
- children: refs.map(ref => /* @__PURE__ */ jsxRuntime.jsx(index.DocPreviewLink, {
196
- gdr: ref
197
- }, ref.id))
198
- }) ]
199
- });
200
- }
201
-
202
- function StageDiagramSection({entry: entry, definition: definition}) {
203
- const {instance: instance} = entry;
204
- return definition ?
205
- /* @__PURE__ */ jsxRuntime.jsx(workflowDiagram.WorkflowDiagram, {
206
- currentStage: workflowEngine.terminalState(instance) === "in-flight" ? instance.currentStage : void 0,
207
- definition: definition,
208
- evaluation: entry.evaluation,
209
- explain: !0,
210
- guardCount: entry.guards?.length,
211
- height: 340,
212
- history: instance.history
213
- }, instance._id) : /* @__PURE__ */ jsxRuntime.jsx(ui.Card, {
214
- border: !0,
215
- marginTop: 3,
216
- padding: 3,
217
- radius: 2,
218
- tone: "caution",
219
- children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
220
- muted: !0,
221
- size: 1,
222
- children: "Couldn’t load this workflow’s full details — the stage diagram is unavailable, and technical names show instead of titles."
223
- })
224
- });
225
- }
226
-
227
- function MutedNote({text: text}) {
228
- /* @__PURE__ */
229
- return jsxRuntime.jsx(ui.Box, {
230
- paddingY: 3,
231
- children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
232
- muted: !0,
233
- size: 1,
234
- children: text
235
- })
236
- });
237
- }
238
-
239
955
  function useStartableDefinitions() {
240
- const {engine: engine} = index.useWorkflowContext(), [definitions, setDefinitions] = react.useState(void 0), [error, setError] = react.useState(void 0);
241
- return react.useEffect(() => {
242
- let cancelled = !1;
243
- return (async () => {
244
- const rows = await index.readDeployedDefinitions(engine), startable = workflowEngine.latestDeployedDefinitions(rows).filter(row => workflowEngine.isStartableDefinition(row)).map(row => ({
245
- name: row.name,
246
- title: row.title ?? row.name,
247
- description: row.description
248
- }));
249
- cancelled || (setDefinitions(startable), setError(void 0));
250
- })().catch(err => {
251
- cancelled || setError(index.describeError(err));
252
- }), () => {
253
- cancelled = !0;
254
- };
255
- }, [ engine ]), {
256
- definitions: definitions,
956
+ const {definitions: definitions, error: error} = useDeployedDefinitions();
957
+ return {
958
+ definitions: react.useMemo(() => definitions?.filter(row => workflowEngine.isStartableDefinition(row)).map(row => ({
959
+ name: row.name,
960
+ title: row.title ?? row.name,
961
+ description: row.description
962
+ })), [ definitions ]),
257
963
  error: error
258
964
  };
259
965
  }
@@ -632,6 +1338,17 @@ function deriveTaskGroups(args) {
632
1338
  };
633
1339
  }
634
1340
 
1341
+ function orderGroupsByPreviewTitle(groups, titles) {
1342
+ const withDocuments = [ ...groups.withDocuments ].sort((a, b) => {
1343
+ const aTitle = titles.get(a.document.id), bTitle = titles.get(b.document.id);
1344
+ return aTitle !== void 0 && bTitle !== void 0 ? aTitle.localeCompare(bTitle) || a.key.localeCompare(b.key) : aTitle !== void 0 ? -1 : bTitle !== void 0 ? 1 : a.key.localeCompare(b.key);
1345
+ });
1346
+ return {
1347
+ ...groups,
1348
+ withDocuments: withDocuments
1349
+ };
1350
+ }
1351
+
635
1352
  function cutTaskGroups(groups, cut) {
636
1353
  const cutBands = bands => bands.filter(band => cut.keepBand?.(band) ?? !0).map(band => ({
637
1354
  ...band,
@@ -1073,30 +1790,64 @@ const NOTHING = {
1073
1790
  };
1074
1791
 
1075
1792
  function useDocPreviewTitles(documents) {
1076
- const previewStore = sanity.useDocumentPreviewStore(), schema = sanity.useSchema(), {binding: binding} = index.useWorkflowContext(), [state, setState] = react.useState(NOTHING), key = documents.map(doc => doc.id).join("|");
1793
+ const previewStore = sanity.useDocumentPreviewStore(), schema = sanity.useSchema(), {perspectiveStack: perspectiveStack} = sanity.usePerspective(), {actualTypes: actualTypes, binding: binding} = index.useWorkflowContext(), [state, setState] = react.useState(NOTHING), key = documents.map(doc => doc.id).join("|");
1077
1794
  return react.useEffect(() => {
1078
- const titles = /* @__PURE__ */ new Map, resolved = /* @__PURE__ */ new Set, publish = () => setState({
1795
+ const titles = /* @__PURE__ */ new Map, resolved = /* @__PURE__ */ new Set, previews = /* @__PURE__ */ new Map, publish = () => setState({
1079
1796
  titles: new Map(titles),
1080
1797
  resolved: new Set(resolved)
1081
- }), subscriptions = documents.flatMap(doc => {
1082
- const {parsed: parsedRef, local: local} = index.gdrLocality(doc.id, binding.contentResource), schemaType = schema.get(doc.type);
1083
- return !parsedRef || !local || !schemaType ? (resolved.add(doc.id), []) : [ previewStore.observeForPreview({
1084
- _type: "reference",
1085
- _ref: parsedRef.documentId
1086
- }, schemaType).subscribe({
1798
+ }), localDocs = documents.flatMap(doc => {
1799
+ const {parsed: parsed, local: local} = index.gdrLocality(doc.id, binding.contentResource);
1800
+ return !parsed || !local ? (resolved.add(doc.id), []) : [ {
1801
+ doc: doc,
1802
+ bareId: parsed.documentId
1803
+ } ];
1804
+ }), untracks = localDocs.map(({bareId: bareId}) => actualTypes.track(bareId)), dropPreview = docId => {
1805
+ previews.get(docId)?.unsubscribe(), previews.delete(docId), titles.delete(docId);
1806
+ }, observe = ({docId: docId, bareId: bareId, typeName: typeName}) => {
1807
+ const schemaType = index.openableSchemaType(schema, typeName);
1808
+ if (!schemaType) return !1;
1809
+ if (previews.get(docId)?.typeName === typeName) return !0;
1810
+ dropPreview(docId), resolved.delete(docId);
1811
+ const subscription = previewStore.observeForPreview({
1812
+ _id: bareId,
1813
+ _type: schemaType.name
1814
+ }, schemaType, {
1815
+ perspective: perspectiveStack
1816
+ }).subscribe({
1087
1817
  next: event => {
1088
- resolved.add(doc.id);
1818
+ resolved.add(docId);
1089
1819
  const title = event.snapshot?.title;
1090
- typeof title == "string" && titles.set(doc.id, title), publish();
1820
+ typeof title == "string" ? titles.set(docId, title) : titles.delete(docId), publish();
1091
1821
  },
1092
1822
  error: err => {
1093
- console.error(`[workflow-studio-plugin] preview for "${doc.id}" failed:`, err),
1094
- resolved.add(doc.id), publish();
1823
+ console.error(`[workflow-studio-plugin] preview for "${docId}" failed:`, err), resolved.add(docId),
1824
+ publish();
1095
1825
  }
1096
- }) ];
1097
- });
1098
- return publish(), () => subscriptions.forEach(sub => sub.unsubscribe());
1099
- }, [ key, previewStore, schema, binding.contentResource.id ]), state;
1826
+ });
1827
+ return previews.set(docId, {
1828
+ typeName: typeName,
1829
+ unsubscribe: () => subscription.unsubscribe()
1830
+ }), !0;
1831
+ }, evaluate = () => {
1832
+ for (const {doc: doc, bareId: bareId} of localDocs) {
1833
+ const probe = actualTypes.read(bareId);
1834
+ if (probe === null) {
1835
+ dropPreview(doc.id), resolved.add(doc.id);
1836
+ continue;
1837
+ }
1838
+ !observe({
1839
+ docId: doc.id,
1840
+ bareId: bareId,
1841
+ typeName: probe ?? doc.type
1842
+ }) && typeof probe == "string" && (dropPreview(doc.id), resolved.add(doc.id));
1843
+ }
1844
+ publish();
1845
+ }, unsubscribeProbes = actualTypes.subscribe(evaluate);
1846
+ return evaluate(), () => {
1847
+ unsubscribeProbes(), untracks.forEach(untrack => untrack()), previews.forEach(preview => preview.unsubscribe());
1848
+ };
1849
+ }, [ key, previewStore, schema, actualTypes, binding.contentResource.id, perspectiveStack.join("|") ]),
1850
+ state;
1100
1851
  }
1101
1852
 
1102
1853
  const HITS_CAP = 6;
@@ -1721,13 +2472,12 @@ function RefChips({refs: refs, max: max = 2}) {
1721
2472
  });
1722
2473
  }
1723
2474
 
1724
- const EMPTY_NOTE = "No tasks match — adjust the filters, or start a workflow", UNGROUPED_TITLE = "Workflows without Studio documents";
2475
+ const UNGROUPED_TITLE = "Workflows without Studio documents";
1725
2476
 
1726
2477
  function TaskGroupList({bands: bands, groups: groups, instanceDefaultOpen: instanceDefaultOpen, onOpenTask: onOpenTask, onOpenWorkflow: onOpenWorkflow, showTerminalActions: showTerminalActions}) {
1727
- return groups.withDocuments.length === 0 && groups.withoutDocuments.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(MutedNote, {
1728
- text: EMPTY_NOTE
1729
- }) : /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, {
1730
- gap: 2,
2478
+ /* @__PURE__ */
2479
+ return jsxRuntime.jsxs(ui.Stack, {
2480
+ gap: 1,
1731
2481
  children: [ groups.withDocuments.map(group => /* @__PURE__ */ jsxRuntime.jsx(index.CollapsibleBand, {
1732
2482
  background: !0,
1733
2483
  onToggle: () => bands.toggle(group.key, GROUP_DEFAULT_OPEN),
@@ -1946,28 +2696,6 @@ function useOrphanedWorkflows(groups) {
1946
2696
  }), [ groups, contentResource, missing ]);
1947
2697
  }
1948
2698
 
1949
- const INSTANCE_CAP = 200, NEWEST_INSTANCES = {
1950
- includeCompleted: !0,
1951
- limit: INSTANCE_CAP
1952
- };
1953
-
1954
- function useToolInstances() {
1955
- const {engine: engine} = index.useWorkflowContext(), {instances: instances, loading: loading, unreadable: unreadable} = workflowStudio.useWorkflowInstances({
1956
- engine: engine,
1957
- filter: NEWEST_INSTANCES
1958
- });
1959
- return react.useMemo(() => {
1960
- const rows = instances ?? [];
1961
- return {
1962
- inFlight: rows.filter(instance => workflowEngine.terminalState(instance) === "in-flight"),
1963
- settled: rows.filter(instance => workflowEngine.terminalState(instance) !== "in-flight"),
1964
- truncated: rows.length + unreadable.length === INSTANCE_CAP,
1965
- loading: loading,
1966
- unreadable: unreadable
1967
- };
1968
- }, [ instances, loading, unreadable ]);
1969
- }
1970
-
1971
2699
  function TasksTab({filterSlot: filterSlot, instances: instances, loading: loading, identity: identity, onOpenTask: onOpenTask, onOpenWorkflow: onOpenWorkflow, segment: segment, truncated: truncated, unreadable: unreadable}) {
1972
2700
  const mineOnly = segment === "for-me", {binding: binding} = index.useWorkflowContext(), bands = useBandOpenState({
1973
2701
  segment: segment,
@@ -1975,21 +2703,38 @@ function TasksTab({filterSlot: filterSlot, instances: instances, loading: loadin
1975
2703
  }), [filters, setFilters] = react.useState({}), groups = react.useMemo(() => deriveTaskGroups({
1976
2704
  instances: instances,
1977
2705
  identity: identity
1978
- }), [ instances, identity ]), orphans = useOrphanedWorkflows(groups), listed = react.useMemo(() => hideOrphanedGroups(groups, orphans.groupKeys), [ groups, orphans.groupKeys ]), options = react.useMemo(() => taskFilterOptions(listed), [ listed ]), visible = react.useMemo(() => {
2706
+ }), [ instances, identity ]), orphans = useOrphanedWorkflows(groups), listed = react.useMemo(() => hideOrphanedGroups(groups, orphans.groupKeys), [ groups, orphans.groupKeys ]), options = react.useMemo(() => taskFilterOptions(listed), [ listed ]), leadDocuments = react.useMemo(() => listed.withDocuments.map(group => group.document), [ listed ]), previewTitles = useDocPreviewTitles(leadDocuments), visible = react.useMemo(() => {
1979
2707
  const filtered = applyTaskFilters({
1980
2708
  groups: listed,
1981
2709
  filters: filters,
1982
2710
  now: /* @__PURE__ */ new Date
1983
- });
1984
- return mineOnly ? onlyMyTasks(filtered) : filtered;
1985
- }, [ listed, filters, mineOnly ]);
1986
- return loading ?
1987
- /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
1988
- paddingLeft: 2,
2711
+ }), cut = mineOnly ? onlyMyTasks(filtered) : filtered;
2712
+ return orderGroupsByPreviewTitle(cut, previewTitles.titles);
2713
+ }, [ listed, filters, mineOnly, previewTitles ]);
2714
+ if (loading) /* @__PURE__ */
2715
+ return jsxRuntime.jsx(TabStatusRow, {
1989
2716
  children: /* @__PURE__ */ jsxRuntime.jsx(index.LoadingRow, {
1990
2717
  label: "Loading tasks…"
1991
2718
  })
1992
- }) : /* @__PURE__ */ jsxRuntime.jsxs(ui.Box, {
2719
+ });
2720
+ const empty = visible.withDocuments.length === 0 && visible.withoutDocuments.length === 0;
2721
+ let body;
2722
+ return empty ? mineOnly ? body = /* @__PURE__ */ jsxRuntime.jsx(index.ForMeEmptyState, {}) : body = /* @__PURE__ */ jsxRuntime.jsx(TabStatusRow, {
2723
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
2724
+ muted: !0,
2725
+ size: 1,
2726
+ children: "No tasks match — adjust the filters, or start a workflow"
2727
+ })
2728
+ }) : body = /* @__PURE__ */ jsxRuntime.jsx(TaskGroupList, {
2729
+ bands: bands,
2730
+ groups: visible,
2731
+ instanceDefaultOpen: instanceBandDefaultOpen(segment),
2732
+ onOpenTask: onOpenTask,
2733
+ onOpenWorkflow: onOpenWorkflow,
2734
+ showTerminalActions: mineOnly
2735
+ }), /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, {
2736
+ direction: "column",
2737
+ flex: 1,
1993
2738
  children: [
1994
2739
  /* @__PURE__ */ jsxRuntime.jsx(FilterRowPortal, {
1995
2740
  filters: filters,
@@ -1997,91 +2742,78 @@ function TasksTab({filterSlot: filterSlot, instances: instances, loading: loadin
1997
2742
  options: options,
1998
2743
  slot: filterSlot
1999
2744
  }),
2000
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, {
2745
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, {
2746
+ direction: "column",
2747
+ flex: 1,
2001
2748
  gap: 2,
2749
+ paddingBottom: 4,
2002
2750
  children: [
2003
2751
  /* @__PURE__ */ jsxRuntime.jsx(index.UnreadableDocsNote, {
2004
2752
  unreadable: unreadable
2005
2753
  }),
2006
2754
  /* @__PURE__ */ jsxRuntime.jsx(OrphanedWorkflowsNote, {
2007
2755
  orphans: orphans
2008
- }),
2009
- /* @__PURE__ */ jsxRuntime.jsx(TaskGroupList, {
2010
- bands: bands,
2011
- groups: visible,
2012
- instanceDefaultOpen: instanceBandDefaultOpen(segment),
2013
- onOpenTask: onOpenTask,
2014
- onOpenWorkflow: onOpenWorkflow,
2015
- showTerminalActions: mineOnly
2016
- }) ]
2017
- }), truncated ? /* @__PURE__ */ jsxRuntime.jsx(MutedNote, {
2018
- text: `Tasks from the latest ${INSTANCE_CAP} workflows — older ones aren't listed.`
2019
- }) : null ]
2756
+ }), body, truncated ? /* @__PURE__ */ jsxRuntime.jsx(MutedNote, {
2757
+ text: `Tasks from the latest ${INSTANCE_CAP} workflows — older ones aren't listed.`
2758
+ }) : null ]
2759
+ }) ]
2020
2760
  });
2021
2761
  }
2022
2762
 
2023
2763
  const TOOL_TABS = [ {
2024
2764
  value: "overview",
2025
2765
  label: "Overview"
2766
+ }, {
2767
+ value: "definitions",
2768
+ label: "Definitions"
2026
2769
  }, {
2027
2770
  value: "for-me",
2028
2771
  label: "For me"
2029
- } ], [firstToolTab, ...restToolTabs] = TOOL_TABS, TOOL_TAB_CODEC = index.workflowsTabCodec([ firstToolTab.value, ...restToolTabs.map(tab => tab.value) ]);
2772
+ } ], TOOL_TAB_VALUES = TOOL_TABS.map(tab => tab.value);
2030
2773
 
2031
- function useToolTab() {
2032
- const router$1 = router.useRouter(), params = Object.fromEntries(router$1.state._searchParams ?? []);
2033
- return {
2034
- tab: TOOL_TAB_CODEC.read(params),
2035
- setTab: next => router$1.navigate({
2036
- _searchParams: definedEntries(TOOL_TAB_CODEC.write(params, next))
2037
- })
2038
- };
2039
- }
2040
-
2041
- function definedEntries(params) {
2042
- return Object.entries(params).filter(entry => entry[1] !== void 0);
2043
- }
2044
-
2045
- function carryingSearchParams(state, searchParams) {
2046
- return searchParams === void 0 ? state : {
2047
- ...state,
2048
- _searchParams: searchParams
2049
- };
2774
+ function InstanceStreamKeepAlive() {
2775
+ return useToolInstances(), null;
2050
2776
  }
2051
2777
 
2052
2778
  function WorkflowsToolRoot() {
2053
- const router$1 = router.useRouter(), {seedInstance: seedInstance} = index.useWorkflowContext(), {tab: tab} = useToolTab();
2779
+ const router$1 = router.useRouter(), {seedInstance: seedInstance} = index.useWorkflowContext(), telemetry = workflowReact.useWorkflowTelemetry(), [filterSlot, setFilterSlot] = react.useState(null), goHome = () => router$1.navigate({}), instanceId = typeof router$1.state.instanceId == "string" ? router$1.state.instanceId : void 0, definitionName = typeof router$1.state.definitionName == "string" ? router$1.state.definitionName : void 0, isHome = instanceId === void 0 && definitionName === void 0, [instanceStreamLatched, setInstanceStreamLatched] = react.useState(isHome);
2780
+ react.useEffect(() => {
2781
+ isHome && setInstanceStreamLatched(!0);
2782
+ }, [ isHome ]);
2783
+ let tab;
2784
+ instanceId !== void 0 ? tab = "overview" : definitionName !== void 0 ? tab = "definitions" : tab = index.pathSegmentTab({
2785
+ segment: router$1.state.workflowsTab,
2786
+ tabs: TOOL_TAB_VALUES,
2787
+ fallback: "overview"
2788
+ });
2789
+ const setTab = next => router$1.navigate(next === "overview" ? {} : {
2790
+ workflowsTab: next
2791
+ });
2054
2792
  index.useLogEventOnMount(index.WorkflowToolOpened, {
2055
2793
  tab: tab,
2056
2794
  via: "open"
2057
2795
  });
2058
- const instanceId = typeof router$1.state.instanceId == "string" ? router$1.state.instanceId : void 0;
2059
- return instanceId ? /* @__PURE__ */ jsxRuntime.jsx(WorkflowInstanceDetail, {
2796
+ let panel;
2797
+ return instanceId !== void 0 ? panel = /* @__PURE__ */ jsxRuntime.jsx(WorkflowInstanceDetail, {
2060
2798
  instanceId: instanceId,
2061
- onBack: () => router$1.navigate(carryingSearchParams({}, router$1.state._searchParams))
2062
- }, instanceId) : /* @__PURE__ */ jsxRuntime.jsx(WorkflowsHome, {
2799
+ onBack: goHome
2800
+ }, instanceId) : definitionName !== void 0 ? panel = /* @__PURE__ */ jsxRuntime.jsx(DefinitionDetail, {
2801
+ definitionName: definitionName
2802
+ }, definitionName) : panel = /* @__PURE__ */ jsxRuntime.jsx(WorkflowsHome, {
2803
+ filterSlot: filterSlot,
2804
+ onOpenDefinition: name => router$1.navigate({
2805
+ definitionName: name
2806
+ }),
2063
2807
  onOpenInstance: instance => {
2064
- typeof instance != "string" && seedInstance(instance), router$1.navigate(carryingSearchParams({
2808
+ typeof instance != "string" && seedInstance(instance), router$1.navigate({
2065
2809
  instanceId: typeof instance == "string" ? instance : instance._id
2066
- }, router$1.state._searchParams));
2067
- }
2068
- });
2069
- }
2070
-
2071
- function WorkflowsHome({onOpenInstance: onOpenInstance}) {
2072
- const {tab: tab, setTab: setTab} = useToolTab(), telemetry = workflowReact.useWorkflowTelemetry(), instances = useToolInstances(), identity = index.useAssignmentIdentity(), [openTask, setOpenTask] = react.useState(null), openInstance = target => {
2073
- if (typeof target != "string") return onOpenInstance(target);
2074
- const held = [ ...instances.inFlight, ...instances.settled ].find(i => i._id === target);
2075
- onOpenInstance(held ?? target);
2076
- }, [filterSlot, setFilterSlot] = react.useState(null), scrollerRef = react.useRef(null);
2077
- return react.useEffect(() => {
2078
- scrollerRef.current?.scrollTo({
2079
- top: 0
2080
- });
2081
- }, [ tab ]),
2810
+ });
2811
+ },
2812
+ tab: tab
2813
+ }),
2082
2814
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Card, {
2083
2815
  height: "fill",
2084
- children: [
2816
+ children: [ instanceStreamLatched ? /* @__PURE__ */ jsxRuntime.jsx(InstanceStreamKeepAlive, {}) : null,
2085
2817
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, {
2086
2818
  direction: "column",
2087
2819
  height: "fill",
@@ -2090,75 +2822,99 @@ function WorkflowsHome({onOpenInstance: onOpenInstance}) {
2090
2822
  /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
2091
2823
  flex: "none",
2092
2824
  padding: 3,
2093
- children: /* @__PURE__ */ jsxRuntime.jsx(ui.Container, {
2094
- width: 2,
2095
- children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, {
2825
+ children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, {
2826
+ gap: 3,
2827
+ children: [
2828
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, {
2829
+ align: "center",
2096
2830
  gap: 3,
2831
+ paddingLeft: 2,
2097
2832
  children: [
2098
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, {
2099
- align: "center",
2100
- gap: 3,
2101
- paddingLeft: 2,
2102
- children: [
2103
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
2104
- size: 2,
2105
- weight: "semibold",
2106
- children: "Workflows"
2107
- }),
2108
- /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
2109
- flex: 1
2110
- }),
2111
- /* @__PURE__ */ jsxRuntime.jsx(NewWorkflowButton, {}) ]
2833
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
2834
+ size: 1,
2835
+ weight: "semibold",
2836
+ children: "Workflows"
2112
2837
  }),
2113
- /* @__PURE__ */ jsxRuntime.jsx(index.TabSwitch, {
2114
- ariaControls: "workflows-tool-panel",
2115
- idPrefix: "workflows-tool-tab",
2116
- onSelect: next => {
2117
- next !== tab && telemetry.log(index.WorkflowToolOpened, {
2118
- tab: next,
2119
- via: "tab-switch"
2120
- }), setTab(next);
2121
- },
2122
- options: TOOL_TABS,
2123
- selected: tab
2838
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
2839
+ flex: 1
2124
2840
  }),
2125
- /* @__PURE__ */ jsxRuntime.jsx("div", {
2126
- ref: setFilterSlot
2127
- }) ]
2128
- })
2841
+ /* @__PURE__ */ jsxRuntime.jsx(NewWorkflowButton, {}) ]
2842
+ }),
2843
+ /* @__PURE__ */ jsxRuntime.jsx(index.TabSwitch, {
2844
+ ariaControls: "workflows-tool-panel",
2845
+ idPrefix: "workflows-tool-tab",
2846
+ onSelect: next => {
2847
+ next !== tab && telemetry.log(index.WorkflowToolOpened, {
2848
+ tab: next,
2849
+ via: "tab-switch"
2850
+ }), setTab(next);
2851
+ },
2852
+ options: TOOL_TABS,
2853
+ selected: tab
2854
+ }),
2855
+ /* @__PURE__ */ jsxRuntime.jsx("div", {
2856
+ ref: setFilterSlot
2857
+ }) ]
2129
2858
  })
2130
2859
  }),
2131
2860
  /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
2132
- ref: scrollerRef,
2133
2861
  flex: 1,
2134
- overflow: "auto",
2135
- paddingX: 3,
2862
+ overflow: "hidden",
2863
+ children: panel
2864
+ }) ]
2865
+ }) ]
2866
+ });
2867
+ }
2868
+
2869
+ function WorkflowsHome({filterSlot: filterSlot, onOpenDefinition: onOpenDefinition, onOpenInstance: onOpenInstance, tab: tab}) {
2870
+ const instances = useToolInstances(), identity = index.useAssignmentIdentity(), [openTask, setOpenTask] = react.useState(null), openInstance = target => {
2871
+ if (typeof target != "string") return onOpenInstance(target);
2872
+ const held = [ ...instances.inFlight, ...instances.settled ].find(i => i._id === target);
2873
+ onOpenInstance(held ?? target);
2874
+ }, scrollerRef = react.useRef(null);
2875
+ return react.useEffect(() => {
2876
+ scrollerRef.current?.scrollTo({
2877
+ top: 0
2878
+ });
2879
+ }, [ tab ]), /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, {
2880
+ direction: "column",
2881
+ height: "fill",
2882
+ overflow: "hidden",
2883
+ children: [
2884
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
2885
+ ref: scrollerRef,
2886
+ flex: 1,
2887
+ overflow: "auto",
2888
+ paddingX: 3,
2889
+ style: {
2890
+ scrollbarGutter: "stable"
2891
+ },
2892
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.TabPanel, {
2893
+ "aria-labelledby": `workflows-tool-tab-${tab}`,
2894
+ id: "workflows-tool-panel",
2136
2895
  style: {
2137
- scrollbarGutter: "stable"
2896
+ display: "flex",
2897
+ flexDirection: "column",
2898
+ minHeight: "100%"
2138
2899
  },
2139
- children: /* @__PURE__ */ jsxRuntime.jsx(ui.Container, {
2140
- width: 2,
2141
- children: /* @__PURE__ */ jsxRuntime.jsx(ui.TabPanel, {
2142
- "aria-labelledby": `workflows-tool-tab-${tab}`,
2143
- id: "workflows-tool-panel",
2144
- children: /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
2145
- paddingBottom: 4,
2146
- paddingTop: 1,
2147
- children: /* @__PURE__ */ jsxRuntime.jsx(TasksTab, {
2148
- filterSlot: filterSlot,
2149
- identity: identity,
2150
- instances: [ ...instances.inFlight, ...instances.settled ],
2151
- loading: instances.loading,
2152
- onOpenTask: setOpenTask,
2153
- onOpenWorkflow: openInstance,
2154
- segment: tab,
2155
- truncated: instances.truncated,
2156
- unreadable: instances.unreadable
2157
- })
2158
- })
2900
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, {
2901
+ direction: "column",
2902
+ flex: 1,
2903
+ children: tab === "definitions" ? /* @__PURE__ */ jsxRuntime.jsx(DefinitionsTab, {
2904
+ onOpenDefinition: onOpenDefinition
2905
+ }) : /* @__PURE__ */ jsxRuntime.jsx(TasksTab, {
2906
+ filterSlot: filterSlot,
2907
+ identity: identity,
2908
+ instances: [ ...instances.inFlight, ...instances.settled ],
2909
+ loading: instances.loading,
2910
+ onOpenTask: setOpenTask,
2911
+ onOpenWorkflow: openInstance,
2912
+ segment: tab,
2913
+ truncated: instances.truncated,
2914
+ unreadable: instances.unreadable
2159
2915
  })
2160
2916
  })
2161
- }) ]
2917
+ })
2162
2918
  }), openTask ? /* @__PURE__ */ jsxRuntime.jsx(ToolActivityDialog, {
2163
2919
  onClose: () => setOpenTask(null),
2164
2920
  target: openTask