@workflow/web 5.0.0-beta.1 → 5.0.0-beta.10

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.
Files changed (39) hide show
  1. package/README.md +1 -1
  2. package/build/client/assets/encryption-B7z5c3Mu.js +51 -0
  3. package/build/client/assets/entry.client-DtOpLtMQ.js +11899 -0
  4. package/build/client/assets/highlighted-body-B3W2YXNL-khW-t_B-.js +18 -0
  5. package/build/client/assets/home-DKDz-tU-.js +3993 -0
  6. package/build/client/assets/index-BsV8i_Jn.js +9432 -0
  7. package/build/client/assets/index-CIZByfnL.js +308 -0
  8. package/build/client/assets/manifest-a33ce94a.js +1 -0
  9. package/build/client/assets/mermaid-3ZIDBTTL-DPZjJwuX.js +48560 -0
  10. package/build/client/assets/root-BYtlAgHl.css +1 -0
  11. package/build/client/assets/root-DJKbeamD.js +690 -0
  12. package/build/client/assets/run-detail-ekQSEj_z.js +5794 -0
  13. package/build/client/assets/server-build-ByHD2s6b.css +1 -0
  14. package/build/client/assets/worker-CqHKTA7D.js +115 -0
  15. package/build/client/assets/workflow-graph-viewer-DjlqbXVx.js +16175 -0
  16. package/build/client/assets/{workflow-graph-viewer-yls6qlc0.css → workflow-graph-viewer-DnlNuQQH.css} +1 -1
  17. package/build/server/assets/{app-DaJqmGT8.js → app-Cgrcb-fS.js} +1 -1
  18. package/build/server/assets/{highlighted-body-B3W2YXNL-xowfciga.js → highlighted-body-B3W2YXNL-BCA9A7WB.js} +5 -6
  19. package/build/server/assets/index-VP-TIWDS.js +89 -0
  20. package/build/server/assets/{mermaid-3ZIDBTTL-nU8V7hWp.js → mermaid-3ZIDBTTL-CbLetKvR.js} +6 -7
  21. package/build/server/assets/{server-build-rhQJXUIP.js → server-build-ClDPbfo3.js} +40674 -39791
  22. package/build/server/assets/{token-BZ35B6LQ.js → token-BxPrGSQ5.js} +2 -2
  23. package/build/server/assets/{token-util-vRsmZMJV.js → token-util-Bmrakd6M.js} +2 -2
  24. package/build/server/index.js +1 -1
  25. package/package.json +8 -7
  26. package/build/client/assets/encryption-8OvC6eoJ.js +0 -1
  27. package/build/client/assets/entry.client-BiVEcoV8.js +0 -29
  28. package/build/client/assets/highlighted-body-B3W2YXNL-epbVCzUZ.js +0 -1
  29. package/build/client/assets/home-cYqw84jW.js +0 -40
  30. package/build/client/assets/index-BmZS3y7B.js +0 -59
  31. package/build/client/assets/manifest-2d7ab41e.js +0 -1
  32. package/build/client/assets/mermaid-3ZIDBTTL-CCXSRVd3.js +0 -210
  33. package/build/client/assets/root-aMfmV5uh.css +0 -1
  34. package/build/client/assets/root-kDmPgm4u.js +0 -26
  35. package/build/client/assets/run-detail-B5k2OYOu.js +0 -37
  36. package/build/client/assets/server-build-CiTtikBY.css +0 -1
  37. package/build/client/assets/worker-D-HfgUMl.js +0 -1
  38. package/build/client/assets/workflow-graph-viewer-B5c_xYyF.js +0 -161
  39. package/build/server/assets/index-CFBIzXaR.js +0 -81
@@ -0,0 +1,3993 @@
1
+ import { r as reactExports, D as ReactDOM, j as jsxRuntimeExports, A as Link, f as reactDomExports, C as useSearchParams, B as useNavigate, G as useLocation, w as withComponentProps } from "./index-BsV8i_Jn.js";
2
+ import { R as ResolveHookModal, S as Send, a as Skeleton, u as unwrapOrThrow, W as WorkflowWebAPIError, b as RelativeTime, A as Alert, C as CircleAlert, c as AlertTitle, d as AlertDescription, g as getErrorMessage, e as CopyableText, D as DropdownMenu, f as DropdownMenuTrigger, E as Ellipsis, h as DropdownMenuContent, i as DropdownMenuItem, j as ChevronRight, r as resumeHook, k as createCollection, l as hideOthers, m as ReactRemoveScroll, n as useDirection, o as ChevronDown, p as ChevronUp, q as Check, X, s as useControllableState$1, P as Primitive$1, t as composeEventHandlers$1, v as Presence, w as useSize, x as createContextScope$1, y as cancelRun, z as reenqueueRun, B as parseWorkflowName, F as StatusBadge, Z as Zap, G as CircleX, H as RunActionsDropdownItems, I as unwrapServerActionResult, J as WorkflowGraphViewer, T as Tabs, K as TabsList, L as TabsTrigger, M as TabsContent, N as ErrorBoundary } from "./workflow-graph-viewer-DjlqbXVx.js";
3
+ import { g as useLayoutEffect2, P as Primitive, c as createLucideIcon, h as useToast, d as cn, D as DEFAULT_PAGE_SIZE, i as fetchRuns, j as fetchHooks, k as getPaginationDisplay, T as Tooltip, a as TooltipTrigger, B as Button, b as TooltipContent, l as fetchEvents, m as useCallbackRef, n as useComposedRefs, A as Anchor, o as composeEventHandlers, p as useId, q as createPopperScope, s as DismissableLayer, v as createContextScope, w as createSlot, C as Content$1, x as useControllableState, y as Root2$1, V as VISUALLY_HIDDEN_STYLES, z as Arrow, E as useComposedRefs$1, u as useServerConfig, t as toast, L as LoaderCircle, F as fetchRun, G as cva, H as Presence$1, I as createContext2, J as fetchWorkflowsManifest } from "./mermaid-3ZIDBTTL-DPZjJwuX.js";
4
+ import "./index-CIZByfnL.js";
5
+ var PORTAL_NAME$2 = "Portal";
6
+ var Portal$2 = reactExports.forwardRef((props, forwardedRef) => {
7
+ var _a;
8
+ const { container: containerProp, ...portalProps } = props;
9
+ const [mounted, setMounted] = reactExports.useState(false);
10
+ useLayoutEffect2(() => setMounted(true), []);
11
+ const container = containerProp || mounted && ((_a = globalThis == null ? void 0 : globalThis.document) == null ? void 0 : _a.body);
12
+ return container ? ReactDOM.createPortal(/* @__PURE__ */ jsxRuntimeExports.jsx(Primitive.div, { ...portalProps, ref: forwardedRef }), container) : null;
13
+ });
14
+ Portal$2.displayName = PORTAL_NAME$2;
15
+ /**
16
+ * @license lucide-react v0.575.0 - ISC
17
+ *
18
+ * This source code is licensed under the ISC license.
19
+ * See the LICENSE file in the root directory of this source tree.
20
+ */
21
+ const __iconNode$6 = [
22
+ ["path", { d: "m3 16 4 4 4-4", key: "1co6wj" }],
23
+ ["path", { d: "M7 20V4", key: "1yoxec" }],
24
+ ["path", { d: "M20 8h-5", key: "1vsyxs" }],
25
+ ["path", { d: "M15 10V6.5a2.5 2.5 0 0 1 5 0V10", key: "ag13bf" }],
26
+ ["path", { d: "M15 14h5l-5 6h5", key: "ur5jdg" }]
27
+ ];
28
+ const ArrowDownAZ = createLucideIcon("arrow-down-a-z", __iconNode$6);
29
+ /**
30
+ * @license lucide-react v0.575.0 - ISC
31
+ *
32
+ * This source code is licensed under the ISC license.
33
+ * See the LICENSE file in the root directory of this source tree.
34
+ */
35
+ const __iconNode$5 = [
36
+ ["path", { d: "m3 8 4-4 4 4", key: "11wl7u" }],
37
+ ["path", { d: "M7 4v16", key: "1glfcx" }],
38
+ ["path", { d: "M20 8h-5", key: "1vsyxs" }],
39
+ ["path", { d: "M15 10V6.5a2.5 2.5 0 0 1 5 0V10", key: "ag13bf" }],
40
+ ["path", { d: "M15 14h5l-5 6h5", key: "ur5jdg" }]
41
+ ];
42
+ const ArrowUpAZ = createLucideIcon("arrow-up-a-z", __iconNode$5);
43
+ /**
44
+ * @license lucide-react v0.575.0 - ISC
45
+ *
46
+ * This source code is licensed under the ISC license.
47
+ * See the LICENSE file in the root directory of this source tree.
48
+ */
49
+ const __iconNode$4 = [["path", { d: "m15 18-6-6 6-6", key: "1wnfg3" }]];
50
+ const ChevronLeft = createLucideIcon("chevron-left", __iconNode$4);
51
+ /**
52
+ * @license lucide-react v0.575.0 - ISC
53
+ *
54
+ * This source code is licensed under the ISC license.
55
+ * See the LICENSE file in the root directory of this source tree.
56
+ */
57
+ const __iconNode$3 = [
58
+ ["path", { d: "M15 6a9 9 0 0 0-9 9V3", key: "1cii5b" }],
59
+ ["circle", { cx: "18", cy: "6", r: "3", key: "1h7g24" }],
60
+ ["circle", { cx: "6", cy: "18", r: "3", key: "fqmcym" }]
61
+ ];
62
+ const GitBranch = createLucideIcon("git-branch", __iconNode$3);
63
+ /**
64
+ * @license lucide-react v0.575.0 - ISC
65
+ *
66
+ * This source code is licensed under the ISC license.
67
+ * See the LICENSE file in the root directory of this source tree.
68
+ */
69
+ const __iconNode$2 = [["path", { d: "M5 12h14", key: "1ays0h" }]];
70
+ const Minus = createLucideIcon("minus", __iconNode$2);
71
+ /**
72
+ * @license lucide-react v0.575.0 - ISC
73
+ *
74
+ * This source code is licensed under the ISC license.
75
+ * See the LICENSE file in the root directory of this source tree.
76
+ */
77
+ const __iconNode$1 = [
78
+ ["path", { d: "M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8", key: "v9h5vc" }],
79
+ ["path", { d: "M21 3v5h-5", key: "1q7to0" }],
80
+ ["path", { d: "M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16", key: "3uifl3" }],
81
+ ["path", { d: "M8 16H3v5", key: "1cv678" }]
82
+ ];
83
+ const RefreshCw = createLucideIcon("refresh-cw", __iconNode$1);
84
+ /**
85
+ * @license lucide-react v0.575.0 - ISC
86
+ *
87
+ * This source code is licensed under the ISC license.
88
+ * See the LICENSE file in the root directory of this source tree.
89
+ */
90
+ const __iconNode = [
91
+ ["rect", { width: "8", height: "8", x: "3", y: "3", rx: "2", key: "by2w9f" }],
92
+ ["path", { d: "M7 11v4a2 2 0 0 0 2 2h4", key: "xkn7yn" }],
93
+ ["rect", { width: "8", height: "8", x: "13", y: "13", rx: "2", key: "1cgmvn" }]
94
+ ];
95
+ const Workflow = createLucideIcon("workflow", __iconNode);
96
+ function useHookActions({ onResolve, callbacks }) {
97
+ const toast2 = useToast();
98
+ const [isResolving, setIsResolving] = reactExports.useState(false);
99
+ const [selectedHook, setSelectedHook] = reactExports.useState(null);
100
+ const openResolveModal = reactExports.useCallback((hook) => {
101
+ setSelectedHook(hook);
102
+ }, []);
103
+ const closeResolveModal = reactExports.useCallback(() => {
104
+ setSelectedHook(null);
105
+ }, []);
106
+ const handleResolve = reactExports.useCallback(async (payload) => {
107
+ var _a;
108
+ if (isResolving || !selectedHook)
109
+ return;
110
+ try {
111
+ setIsResolving(true);
112
+ await onResolve(selectedHook, payload);
113
+ toast2.success("Hook resolved", {
114
+ description: "The payload has been sent and the hook resolved."
115
+ });
116
+ setSelectedHook(null);
117
+ (_a = callbacks == null ? void 0 : callbacks.onSuccess) == null ? void 0 : _a.call(callbacks);
118
+ } catch (err) {
119
+ console.error("Failed to resolve hook:", err);
120
+ toast2.error("Failed to resolve hook", {
121
+ description: err instanceof Error ? err.message : "An unknown error occurred"
122
+ });
123
+ } finally {
124
+ setIsResolving(false);
125
+ }
126
+ }, [onResolve, selectedHook, isResolving, callbacks]);
127
+ return {
128
+ isResolving,
129
+ selectedHook,
130
+ openResolveModal,
131
+ closeResolveModal,
132
+ handleResolve
133
+ };
134
+ }
135
+ function ResolveHookDropdownItem({ hook, stopPropagation = false, onResolveClick, DropdownMenuItem: DropdownMenuItem2 }) {
136
+ const handleClick = (e) => {
137
+ if (stopPropagation)
138
+ e.stopPropagation();
139
+ onResolveClick(hook);
140
+ };
141
+ return jsxRuntimeExports.jsxs(DropdownMenuItem2, { onClick: handleClick, children: [jsxRuntimeExports.jsx(Send, { className: "h-4 w-4 mr-2" }), "Resolve Hook"] });
142
+ }
143
+ function HookResolveModalWrapper({ hookActions }) {
144
+ const { selectedHook, closeResolveModal, handleResolve, isResolving } = hookActions;
145
+ return jsxRuntimeExports.jsx(ResolveHookModal, { isOpen: selectedHook !== null, onClose: closeResolveModal, onSubmit: handleResolve, isSubmitting: isResolving });
146
+ }
147
+ const Card = reactExports.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntimeExports.jsx(
148
+ "div",
149
+ {
150
+ ref,
151
+ className: cn(
152
+ "rounded-lg border bg-card text-card-foreground shadow-sm",
153
+ className
154
+ ),
155
+ ...props
156
+ }
157
+ ));
158
+ Card.displayName = "Card";
159
+ const CardHeader = reactExports.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntimeExports.jsx(
160
+ "div",
161
+ {
162
+ ref,
163
+ className: cn("flex flex-col space-y-1.5 p-6", className),
164
+ ...props
165
+ }
166
+ ));
167
+ CardHeader.displayName = "CardHeader";
168
+ const CardTitle = reactExports.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntimeExports.jsx(
169
+ "div",
170
+ {
171
+ ref,
172
+ className: cn(
173
+ "text-2xl font-semibold leading-none tracking-tight",
174
+ className
175
+ ),
176
+ ...props
177
+ }
178
+ ));
179
+ CardTitle.displayName = "CardTitle";
180
+ const CardDescription = reactExports.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntimeExports.jsx(
181
+ "div",
182
+ {
183
+ ref,
184
+ className: cn("text-sm text-muted-foreground", className),
185
+ ...props
186
+ }
187
+ ));
188
+ CardDescription.displayName = "CardDescription";
189
+ const CardContent = reactExports.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntimeExports.jsx("div", { ref, className: cn("p-6 pt-0", className), ...props }));
190
+ CardContent.displayName = "CardContent";
191
+ const CardFooter = reactExports.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntimeExports.jsx(
192
+ "div",
193
+ {
194
+ ref,
195
+ className: cn("flex items-center p-6 pt-0", className),
196
+ ...props
197
+ }
198
+ ));
199
+ CardFooter.displayName = "CardFooter";
200
+ const DocsLink = reactExports.forwardRef(
201
+ ({ className, href, children, ...props }, ref) => {
202
+ const fullHref = href.startsWith("http") ? href : `https://workflow-sdk.dev/docs/${href.replace(/^\//, "")}`;
203
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
204
+ Link,
205
+ {
206
+ href: fullHref,
207
+ className: cn(
208
+ "font-medium underline underline-offset-4 transition-colors",
209
+ className
210
+ ),
211
+ style: {
212
+ color: "var(--ds-blue-600)"
213
+ },
214
+ target: "_blank",
215
+ rel: "noopener noreferrer",
216
+ ref,
217
+ ...props,
218
+ children
219
+ }
220
+ );
221
+ }
222
+ );
223
+ DocsLink.displayName = "DocsLink";
224
+ const Table = reactExports.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "relative w-full overflow-auto", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
225
+ "table",
226
+ {
227
+ ref,
228
+ className: cn("w-full caption-bottom text-sm", className),
229
+ ...props
230
+ }
231
+ ) }));
232
+ Table.displayName = "Table";
233
+ const TableHeader = reactExports.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntimeExports.jsx("thead", { ref, className: cn("[&_tr]:border-b", className), ...props }));
234
+ TableHeader.displayName = "TableHeader";
235
+ const TableBody = reactExports.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntimeExports.jsx(
236
+ "tbody",
237
+ {
238
+ ref,
239
+ className: cn("[&_tr:last-child]:border-0", className),
240
+ ...props
241
+ }
242
+ ));
243
+ TableBody.displayName = "TableBody";
244
+ const TableFooter = reactExports.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntimeExports.jsx(
245
+ "tfoot",
246
+ {
247
+ ref,
248
+ className: cn(
249
+ "border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
250
+ className
251
+ ),
252
+ ...props
253
+ }
254
+ ));
255
+ TableFooter.displayName = "TableFooter";
256
+ const TableRow = reactExports.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntimeExports.jsx(
257
+ "tr",
258
+ {
259
+ ref,
260
+ className: cn(
261
+ "border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
262
+ className
263
+ ),
264
+ ...props
265
+ }
266
+ ));
267
+ TableRow.displayName = "TableRow";
268
+ const TableHead = reactExports.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntimeExports.jsx(
269
+ "th",
270
+ {
271
+ ref,
272
+ className: cn(
273
+ "h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
274
+ className
275
+ ),
276
+ ...props
277
+ }
278
+ ));
279
+ TableHead.displayName = "TableHead";
280
+ const TableCell = reactExports.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntimeExports.jsx(
281
+ "td",
282
+ {
283
+ ref,
284
+ className: cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className),
285
+ ...props
286
+ }
287
+ ));
288
+ TableCell.displayName = "TableCell";
289
+ const TableCaption = reactExports.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntimeExports.jsx(
290
+ "caption",
291
+ {
292
+ ref,
293
+ className: cn("mt-4 text-sm text-muted-foreground", className),
294
+ ...props
295
+ }
296
+ ));
297
+ TableCaption.displayName = "TableCaption";
298
+ function TableSkeleton({
299
+ rows = DEFAULT_PAGE_SIZE,
300
+ variant = "runs"
301
+ }) {
302
+ const renderRow = (i) => {
303
+ switch (variant) {
304
+ case "runs":
305
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
306
+ "div",
307
+ {
308
+ className: "grid grid-cols-[1fr_1.5fr_0.8fr_1fr_1fr_40px] items-center gap-4 py-3 px-4",
309
+ children: [
310
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Skeleton, { className: "h-4 w-3/4" }),
311
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Skeleton, { className: "h-4 w-4/5" }),
312
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1", children: [
313
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Skeleton, { className: "h-3 w-14" }),
314
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Skeleton, { className: "h-2 w-8" })
315
+ ] }),
316
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Skeleton, { className: "h-3 w-3/4" }),
317
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Skeleton, { className: "h-3 w-3/4" }),
318
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Skeleton, { className: "h-6 w-6 rounded" })
319
+ ]
320
+ },
321
+ `skeleton-row-${i}`
322
+ );
323
+ case "hooks":
324
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
325
+ "div",
326
+ {
327
+ className: "grid grid-cols-[1.2fr_1.2fr_0.8fr_1fr_0.5fr_40px] items-center gap-4 py-3 px-4",
328
+ children: [
329
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Skeleton, { className: "h-4 w-4/5" }),
330
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Skeleton, { className: "h-4 w-4/5" }),
331
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Skeleton, { className: "h-4 w-3/4" }),
332
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Skeleton, { className: "h-3 w-3/4" }),
333
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Skeleton, { className: "h-4 w-8" }),
334
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Skeleton, { className: "h-6 w-6 rounded" })
335
+ ]
336
+ },
337
+ `skeleton-row-${i}`
338
+ );
339
+ case "workflows":
340
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
341
+ "div",
342
+ {
343
+ className: "grid grid-cols-[1fr_1.5fr_0.5fr] items-center gap-4 py-3 px-4",
344
+ children: [
345
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Skeleton, { className: "h-4 w-3/4" }),
346
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Skeleton, { className: "h-4 w-4/5" }),
347
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Skeleton, { className: "h-5 w-16 rounded-full mx-auto" })
348
+ ]
349
+ },
350
+ `skeleton-row-${i}`
351
+ );
352
+ default:
353
+ return null;
354
+ }
355
+ };
356
+ const renderHeader = () => {
357
+ switch (variant) {
358
+ case "runs":
359
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-[1fr_1.5fr_0.8fr_1fr_1fr_40px] items-center gap-4 py-3 px-4 border-b", children: [
360
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Skeleton, { className: "h-4 w-20" }),
361
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Skeleton, { className: "h-4 w-14" }),
362
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Skeleton, { className: "h-4 w-12" }),
363
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Skeleton, { className: "h-4 w-14" }),
364
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Skeleton, { className: "h-4 w-20" }),
365
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-6" })
366
+ ] });
367
+ case "hooks":
368
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-[1.2fr_1.2fr_0.8fr_1fr_0.5fr_40px] items-center gap-4 py-3 px-4 border-b", children: [
369
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Skeleton, { className: "h-4 w-16" }),
370
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Skeleton, { className: "h-4 w-14" }),
371
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Skeleton, { className: "h-4 w-12" }),
372
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Skeleton, { className: "h-4 w-14" }),
373
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Skeleton, { className: "h-4 w-20" }),
374
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-6" })
375
+ ] });
376
+ case "workflows":
377
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-[1fr_1.5fr_0.5fr] items-center gap-4 py-3 px-4 border-b", children: [
378
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Skeleton, { className: "h-4 w-20" }),
379
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Skeleton, { className: "h-4 w-10" }),
380
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Skeleton, { className: "h-4 w-12 mx-auto" })
381
+ ] });
382
+ default:
383
+ return null;
384
+ }
385
+ };
386
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Card, { className: "overflow-hidden mt-4 bg-background", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(CardContent, { className: "p-0", children: [
387
+ renderHeader(),
388
+ Array.from({ length: rows }, (_, i) => renderRow(i))
389
+ ] }) });
390
+ }
391
+ function pluralize(singular, plural, count2) {
392
+ return count2 === 1 ? singular : plural;
393
+ }
394
+ function usePaginatedList(fetchFn) {
395
+ var _a;
396
+ const [cursor, setCursor] = reactExports.useState(void 0);
397
+ const [hasMore, setHasMore] = reactExports.useState(false);
398
+ const [currentPage, setCurrentPage] = reactExports.useState(0);
399
+ const [pageHistory, setPageHistory] = reactExports.useState([
400
+ void 0
401
+ ]);
402
+ const [maxPagesVisited, setMaxPagesVisited] = reactExports.useState(1);
403
+ const [allPageResults, setAllPageResults] = reactExports.useState(/* @__PURE__ */ new Map([[0, { data: null, isLoading: false, error: null }]]));
404
+ const pageCache = reactExports.useRef(/* @__PURE__ */ new Map());
405
+ const fetchPage = reactExports.useCallback(
406
+ async (pageIndex, pageCursor, force = false) => {
407
+ const cacheKey = pageCursor ?? "initial";
408
+ setAllPageResults((prev) => {
409
+ var _a2;
410
+ const newMap = new Map(prev);
411
+ newMap.set(pageIndex, {
412
+ data: ((_a2 = prev.get(pageIndex)) == null ? void 0 : _a2.data) ?? null,
413
+ isLoading: true,
414
+ error: null
415
+ });
416
+ return newMap;
417
+ });
418
+ if (!force && pageCache.current.has(cacheKey)) {
419
+ const cached = pageCache.current.get(cacheKey);
420
+ if (cached) {
421
+ setAllPageResults((prev) => {
422
+ const newMap = new Map(prev);
423
+ newMap.set(pageIndex, {
424
+ data: cached.data,
425
+ isLoading: false,
426
+ error: null
427
+ });
428
+ return newMap;
429
+ });
430
+ setCursor(cached.cursor);
431
+ setHasMore(cached.hasMore);
432
+ return;
433
+ }
434
+ }
435
+ try {
436
+ const result = await fetchFn(pageCursor);
437
+ pageCache.current.set(cacheKey, {
438
+ data: result.data,
439
+ cursor: result.cursor,
440
+ hasMore: result.hasMore
441
+ });
442
+ setAllPageResults((prev) => {
443
+ const newMap = new Map(prev);
444
+ newMap.set(pageIndex, {
445
+ data: result.data,
446
+ isLoading: false,
447
+ error: null
448
+ });
449
+ return newMap;
450
+ });
451
+ setCursor(result.cursor);
452
+ setHasMore(result.hasMore);
453
+ } catch (err) {
454
+ const error = err instanceof Error ? err : new WorkflowWebAPIError(String(err), { layer: "client" });
455
+ setAllPageResults((prev) => {
456
+ var _a2;
457
+ const newMap = new Map(prev);
458
+ newMap.set(pageIndex, {
459
+ data: ((_a2 = prev.get(pageIndex)) == null ? void 0 : _a2.data) ?? null,
460
+ isLoading: false,
461
+ error
462
+ });
463
+ return newMap;
464
+ });
465
+ }
466
+ },
467
+ [fetchFn]
468
+ );
469
+ reactExports.useEffect(() => {
470
+ fetchPage(0, void 0, true);
471
+ }, [fetchPage]);
472
+ const nextPage = reactExports.useCallback(() => {
473
+ if (hasMore && cursor) {
474
+ const newPage = currentPage + 1;
475
+ setPageHistory((prev) => [...prev, cursor]);
476
+ setCurrentPage(newPage);
477
+ setMaxPagesVisited((prev) => Math.max(prev, newPage + 1));
478
+ if (!allPageResults.has(newPage)) {
479
+ setAllPageResults((prev) => {
480
+ const newMap = new Map(prev);
481
+ newMap.set(newPage, { data: null, isLoading: true, error: null });
482
+ return newMap;
483
+ });
484
+ }
485
+ fetchPage(newPage, cursor);
486
+ }
487
+ }, [hasMore, cursor, fetchPage, currentPage, allPageResults]);
488
+ const previousPage = reactExports.useCallback(() => {
489
+ if (currentPage > 0) {
490
+ const newPage = currentPage - 1;
491
+ const prevCursor = pageHistory[newPage];
492
+ setCurrentPage(newPage);
493
+ fetchPage(newPage, prevCursor);
494
+ }
495
+ }, [currentPage, pageHistory, fetchPage]);
496
+ const reload = reactExports.useCallback(() => {
497
+ pageCache.current.clear();
498
+ setAllPageResults(
499
+ /* @__PURE__ */ new Map([[0, { data: null, isLoading: true, error: null }]])
500
+ );
501
+ setCurrentPage(0);
502
+ setPageHistory([void 0]);
503
+ setMaxPagesVisited(1);
504
+ fetchPage(0, void 0, true);
505
+ }, [fetchPage]);
506
+ const refresh = reactExports.useCallback(() => {
507
+ const currentCursor = pageHistory[currentPage];
508
+ const cacheKey = currentCursor ?? "initial";
509
+ pageCache.current.delete(cacheKey);
510
+ fetchPage(currentPage, currentCursor, true);
511
+ }, [fetchPage, currentPage, pageHistory]);
512
+ const currentPageResult = allPageResults.get(currentPage) ?? {
513
+ data: null,
514
+ isLoading: true,
515
+ error: null
516
+ };
517
+ const globalError = ((_a = Array.from(allPageResults.values()).find((p) => p.error)) == null ? void 0 : _a.error) ?? null;
518
+ const globalLoading = Array.from(allPageResults.values()).some(
519
+ (p) => p.isLoading
520
+ );
521
+ const totalPages = hasMore ? currentPage + 2 : currentPage + 1;
522
+ const currentPageNumber = currentPage + 1;
523
+ const isOnLastVisitedPage = currentPageNumber === maxPagesVisited;
524
+ const showPlus = isOnLastVisitedPage && hasMore;
525
+ const pageInfo = getPaginationDisplay(
526
+ currentPageNumber,
527
+ maxPagesVisited,
528
+ showPlus
529
+ );
530
+ return {
531
+ data: currentPageResult,
532
+ allData: Array.from(allPageResults.values()),
533
+ error: globalError,
534
+ isLoading: globalLoading,
535
+ currentPage,
536
+ totalPages,
537
+ nextPage,
538
+ previousPage,
539
+ hasNextPage: hasMore,
540
+ hasPreviousPage: currentPage > 0,
541
+ reload,
542
+ refresh,
543
+ pageInfo
544
+ };
545
+ }
546
+ function useWorkflowRuns(env, params) {
547
+ const { workflowName, status, limit = 10, sortOrder = "desc" } = params;
548
+ const fetchFn = reactExports.useCallback(
549
+ (cursor) => unwrapOrThrow(
550
+ fetchRuns(env, { cursor, sortOrder, limit, workflowName, status })
551
+ ),
552
+ [env, workflowName, limit, sortOrder, status]
553
+ );
554
+ return usePaginatedList(fetchFn);
555
+ }
556
+ function useWorkflowHooks(env, params) {
557
+ const { runId, limit = 10, sortOrder = "desc" } = params;
558
+ const fetchFn = reactExports.useCallback(
559
+ (cursor) => unwrapOrThrow(fetchHooks(env, { runId, cursor, sortOrder, limit })),
560
+ [env, runId, limit, sortOrder]
561
+ );
562
+ return usePaginatedList(fetchFn);
563
+ }
564
+ function HooksTable({
565
+ runId,
566
+ onHookClick,
567
+ selectedHookId
568
+ }) {
569
+ const [lastRefreshTime, setLastRefreshTime] = reactExports.useState(
570
+ () => /* @__PURE__ */ new Date()
571
+ );
572
+ const env = reactExports.useMemo(() => ({}), []);
573
+ const {
574
+ data,
575
+ error,
576
+ nextPage,
577
+ previousPage,
578
+ hasNextPage,
579
+ hasPreviousPage,
580
+ refresh,
581
+ pageInfo
582
+ } = useWorkflowHooks(env, {
583
+ runId,
584
+ sortOrder: "desc"
585
+ });
586
+ const hookActions = useHookActions({
587
+ onResolve: async (hook, payload) => {
588
+ await resumeHook(env, hook.token, payload);
589
+ },
590
+ callbacks: {
591
+ onSuccess: refresh
592
+ }
593
+ });
594
+ const loading = data.isLoading;
595
+ const hooks = data.data ?? [];
596
+ const onRefresh = () => {
597
+ setLastRefreshTime(() => /* @__PURE__ */ new Date());
598
+ refresh();
599
+ };
600
+ const [invocationData, setInvocationData] = reactExports.useState(/* @__PURE__ */ new Map());
601
+ reactExports.useEffect(() => {
602
+ if (!hooks.length || !runId) return;
603
+ const fetchInvocations = async () => {
604
+ const initialData = /* @__PURE__ */ new Map();
605
+ for (const hook of hooks) {
606
+ initialData.set(hook.hookId, {
607
+ count: 0,
608
+ hasMore: false,
609
+ loading: true
610
+ });
611
+ }
612
+ setInvocationData(initialData);
613
+ try {
614
+ const serverResult = await fetchEvents(env, runId, {
615
+ sortOrder: "asc",
616
+ limit: 1e3
617
+ });
618
+ if (!serverResult.success) {
619
+ setInvocationData((prev) => {
620
+ const updated = new Map(prev);
621
+ for (const hook of hooks) {
622
+ updated.set(hook.hookId, {
623
+ count: 0,
624
+ hasMore: false,
625
+ loading: false
626
+ });
627
+ }
628
+ return updated;
629
+ });
630
+ return;
631
+ }
632
+ const allEvents = serverResult.data.data;
633
+ const hookIds = new Set(hooks.map((h) => h.hookId));
634
+ const counts = /* @__PURE__ */ new Map();
635
+ for (const event of allEvents) {
636
+ if (event.eventType === "hook_received" && event.correlationId && hookIds.has(event.correlationId)) {
637
+ counts.set(
638
+ event.correlationId,
639
+ (counts.get(event.correlationId) ?? 0) + 1
640
+ );
641
+ }
642
+ }
643
+ setInvocationData((prev) => {
644
+ const updated = new Map(prev);
645
+ for (const hook of hooks) {
646
+ updated.set(hook.hookId, {
647
+ count: counts.get(hook.hookId) ?? 0,
648
+ hasMore: serverResult.data.hasMore,
649
+ loading: false
650
+ });
651
+ }
652
+ return updated;
653
+ });
654
+ } catch {
655
+ setInvocationData((prev) => {
656
+ const updated = new Map(prev);
657
+ for (const hook of hooks) {
658
+ updated.set(hook.hookId, {
659
+ count: 0,
660
+ hasMore: false,
661
+ loading: false
662
+ });
663
+ }
664
+ return updated;
665
+ });
666
+ }
667
+ };
668
+ fetchInvocations();
669
+ }, [hooks, env, runId]);
670
+ const renderInvocationCount = (hook) => {
671
+ const data2 = invocationData.get(hook.hookId);
672
+ if (!data2 || data2.loading) {
673
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-muted-foreground text-xs", children: "..." });
674
+ }
675
+ if (data2.count instanceof Error) {
676
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-muted-foreground", children: "Error" });
677
+ }
678
+ if (data2.count === 0) {
679
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-muted-foreground", children: "0" });
680
+ }
681
+ const displayText = data2.hasMore ? `${data2.count}+` : `${data2.count}`;
682
+ if (data2.hasMore) {
683
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
684
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-semibold cursor-help", children: displayText }) }),
685
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { className: "max-w-xs", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs", children: "Showing first 100 invocations. There may be more." }) })
686
+ ] });
687
+ }
688
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-semibold", children: displayText });
689
+ };
690
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
691
+ /* @__PURE__ */ jsxRuntimeExports.jsx(HookResolveModalWrapper, { hookActions }),
692
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [
693
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-end gap-2", children: [
694
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-muted-foreground", children: "Last refreshed" }),
695
+ lastRefreshTime && /* @__PURE__ */ jsxRuntimeExports.jsx(
696
+ RelativeTime,
697
+ {
698
+ date: lastRefreshTime,
699
+ className: "text-sm text-muted-foreground",
700
+ type: "distance"
701
+ }
702
+ )
703
+ ] }),
704
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center gap-4", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
705
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
706
+ Button,
707
+ {
708
+ variant: "outline",
709
+ size: "sm",
710
+ onClick: onRefresh,
711
+ disabled: loading,
712
+ children: [
713
+ /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: loading ? "animate-spin" : "" }),
714
+ "Refresh"
715
+ ]
716
+ }
717
+ ) }),
718
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: "Note that this resets pages" })
719
+ ] }) })
720
+ ] }),
721
+ error ? /* @__PURE__ */ jsxRuntimeExports.jsxs(Alert, { variant: "destructive", children: [
722
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { className: "h-4 w-4" }),
723
+ /* @__PURE__ */ jsxRuntimeExports.jsx(AlertTitle, { children: "Error loading hooks" }),
724
+ /* @__PURE__ */ jsxRuntimeExports.jsx(AlertDescription, { children: getErrorMessage(error) })
725
+ ] }) : !loading && (!hooks || hooks.length === 0) ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-center py-8 text-muted-foreground", children: [
726
+ "No active hooks found. ",
727
+ /* @__PURE__ */ jsxRuntimeExports.jsx("br", {}),
728
+ /* @__PURE__ */ jsxRuntimeExports.jsx(DocsLink, { href: "https://workflow-sdk.dev/docs/api-reference/workflow/create-hook", children: "Learn how to create a hook" })
729
+ ] }) : loading && !(data == null ? void 0 : data.data) ? /* @__PURE__ */ jsxRuntimeExports.jsx(TableSkeleton, { variant: "hooks" }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
730
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Card, { className: "overflow-hidden mt-4 bg-background", children: /* @__PURE__ */ jsxRuntimeExports.jsx(CardContent, { className: "p-0 max-h-[calc(100vh-280px)] overflow-auto", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Table, { children: [
731
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TableHeader, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(TableRow, { children: [
732
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TableHead, { className: "sticky top-0 bg-background z-10 border-b shadow-sm h-10", children: "Hook ID" }),
733
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TableHead, { className: "sticky top-0 bg-background z-10 border-b shadow-sm h-10", children: "Run ID" }),
734
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TableHead, { className: "sticky top-0 bg-background z-10 border-b shadow-sm h-10", children: "Token" }),
735
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TableHead, { className: "sticky top-0 bg-background z-10 border-b shadow-sm h-10", children: "Created" }),
736
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TableHead, { className: "sticky top-0 bg-background z-10 border-b shadow-sm h-10", children: "Invocations" }),
737
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TableHead, { className: "sticky top-0 bg-background z-10 border-b shadow-sm h-10 w-10" })
738
+ ] }) }),
739
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TableBody, { children: hooks.map((hook) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
740
+ TableRow,
741
+ {
742
+ className: "cursor-pointer group relative",
743
+ onClick: () => onHookClick(hook.hookId, hook.runId),
744
+ "data-selected": hook.hookId === selectedHookId,
745
+ children: [
746
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TableCell, { className: "font-mono text-xs py-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx(CopyableText, { text: hook.hookId, overlay: true, children: hook.hookId }) }),
747
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TableCell, { className: "font-mono text-xs py-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx(CopyableText, { text: hook.runId, overlay: true, children: hook.runId }) }),
748
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TableCell, { className: "font-mono text-xs py-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx(CopyableText, { text: hook.token, overlay: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-muted-foreground", children: "••••••••••••" }) }) }),
749
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TableCell, { className: "py-2 text-muted-foreground text-xs", children: hook.createdAt ? /* @__PURE__ */ jsxRuntimeExports.jsx(RelativeTime, { date: hook.createdAt }) : "-" }),
750
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TableCell, { className: "py-2", children: renderInvocationCount(hook) }),
751
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TableCell, { className: "py-2", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(DropdownMenu, { children: [
752
+ /* @__PURE__ */ jsxRuntimeExports.jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
753
+ Button,
754
+ {
755
+ variant: "ghost",
756
+ size: "icon",
757
+ className: "h-8 w-8",
758
+ onClick: (e) => e.stopPropagation(),
759
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(Ellipsis, { className: "h-4 w-4" })
760
+ }
761
+ ) }),
762
+ /* @__PURE__ */ jsxRuntimeExports.jsx(DropdownMenuContent, { align: "end", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
763
+ ResolveHookDropdownItem,
764
+ {
765
+ hook,
766
+ stopPropagation: true,
767
+ onResolveClick: hookActions.openResolveModal,
768
+ DropdownMenuItem
769
+ }
770
+ ) })
771
+ ] }) })
772
+ ]
773
+ },
774
+ hook.hookId
775
+ )) })
776
+ ] }) }) }),
777
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between mt-4", children: [
778
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-sm text-muted-foreground", children: pageInfo }),
779
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2 items-center", children: [
780
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
781
+ Button,
782
+ {
783
+ variant: "outline",
784
+ size: "sm",
785
+ onClick: previousPage,
786
+ disabled: !hasPreviousPage,
787
+ children: [
788
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronLeft, {}),
789
+ "Previous"
790
+ ]
791
+ }
792
+ ),
793
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
794
+ Button,
795
+ {
796
+ variant: "outline",
797
+ size: "sm",
798
+ onClick: nextPage,
799
+ disabled: !hasNextPage,
800
+ children: [
801
+ "Next",
802
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, {})
803
+ ]
804
+ }
805
+ )
806
+ ] })
807
+ ] })
808
+ ] })
809
+ ] });
810
+ }
811
+ function clamp(value, [min, max]) {
812
+ return Math.min(max, Math.max(min, value));
813
+ }
814
+ var count = 0;
815
+ function useFocusGuards() {
816
+ reactExports.useEffect(() => {
817
+ const edgeGuards = document.querySelectorAll("[data-radix-focus-guard]");
818
+ document.body.insertAdjacentElement("afterbegin", edgeGuards[0] ?? createFocusGuard());
819
+ document.body.insertAdjacentElement("beforeend", edgeGuards[1] ?? createFocusGuard());
820
+ count++;
821
+ return () => {
822
+ if (count === 1) {
823
+ document.querySelectorAll("[data-radix-focus-guard]").forEach((node) => node.remove());
824
+ }
825
+ count--;
826
+ };
827
+ }, []);
828
+ }
829
+ function createFocusGuard() {
830
+ const element = document.createElement("span");
831
+ element.setAttribute("data-radix-focus-guard", "");
832
+ element.tabIndex = 0;
833
+ element.style.outline = "none";
834
+ element.style.opacity = "0";
835
+ element.style.position = "fixed";
836
+ element.style.pointerEvents = "none";
837
+ return element;
838
+ }
839
+ var AUTOFOCUS_ON_MOUNT = "focusScope.autoFocusOnMount";
840
+ var AUTOFOCUS_ON_UNMOUNT = "focusScope.autoFocusOnUnmount";
841
+ var EVENT_OPTIONS = { bubbles: false, cancelable: true };
842
+ var FOCUS_SCOPE_NAME = "FocusScope";
843
+ var FocusScope = reactExports.forwardRef((props, forwardedRef) => {
844
+ const {
845
+ loop = false,
846
+ trapped = false,
847
+ onMountAutoFocus: onMountAutoFocusProp,
848
+ onUnmountAutoFocus: onUnmountAutoFocusProp,
849
+ ...scopeProps
850
+ } = props;
851
+ const [container, setContainer] = reactExports.useState(null);
852
+ const onMountAutoFocus = useCallbackRef(onMountAutoFocusProp);
853
+ const onUnmountAutoFocus = useCallbackRef(onUnmountAutoFocusProp);
854
+ const lastFocusedElementRef = reactExports.useRef(null);
855
+ const composedRefs = useComposedRefs(forwardedRef, (node) => setContainer(node));
856
+ const focusScope = reactExports.useRef({
857
+ paused: false,
858
+ pause() {
859
+ this.paused = true;
860
+ },
861
+ resume() {
862
+ this.paused = false;
863
+ }
864
+ }).current;
865
+ reactExports.useEffect(() => {
866
+ if (trapped) {
867
+ let handleFocusIn2 = function(event) {
868
+ if (focusScope.paused || !container) return;
869
+ const target = event.target;
870
+ if (container.contains(target)) {
871
+ lastFocusedElementRef.current = target;
872
+ } else {
873
+ focus(lastFocusedElementRef.current, { select: true });
874
+ }
875
+ }, handleFocusOut2 = function(event) {
876
+ if (focusScope.paused || !container) return;
877
+ const relatedTarget = event.relatedTarget;
878
+ if (relatedTarget === null) return;
879
+ if (!container.contains(relatedTarget)) {
880
+ focus(lastFocusedElementRef.current, { select: true });
881
+ }
882
+ }, handleMutations2 = function(mutations) {
883
+ const focusedElement = document.activeElement;
884
+ if (focusedElement !== document.body) return;
885
+ for (const mutation of mutations) {
886
+ if (mutation.removedNodes.length > 0) focus(container);
887
+ }
888
+ };
889
+ document.addEventListener("focusin", handleFocusIn2);
890
+ document.addEventListener("focusout", handleFocusOut2);
891
+ const mutationObserver = new MutationObserver(handleMutations2);
892
+ if (container) mutationObserver.observe(container, { childList: true, subtree: true });
893
+ return () => {
894
+ document.removeEventListener("focusin", handleFocusIn2);
895
+ document.removeEventListener("focusout", handleFocusOut2);
896
+ mutationObserver.disconnect();
897
+ };
898
+ }
899
+ }, [trapped, container, focusScope.paused]);
900
+ reactExports.useEffect(() => {
901
+ if (container) {
902
+ focusScopesStack.add(focusScope);
903
+ const previouslyFocusedElement = document.activeElement;
904
+ const hasFocusedCandidate = container.contains(previouslyFocusedElement);
905
+ if (!hasFocusedCandidate) {
906
+ const mountEvent = new CustomEvent(AUTOFOCUS_ON_MOUNT, EVENT_OPTIONS);
907
+ container.addEventListener(AUTOFOCUS_ON_MOUNT, onMountAutoFocus);
908
+ container.dispatchEvent(mountEvent);
909
+ if (!mountEvent.defaultPrevented) {
910
+ focusFirst(removeLinks(getTabbableCandidates(container)), { select: true });
911
+ if (document.activeElement === previouslyFocusedElement) {
912
+ focus(container);
913
+ }
914
+ }
915
+ }
916
+ return () => {
917
+ container.removeEventListener(AUTOFOCUS_ON_MOUNT, onMountAutoFocus);
918
+ setTimeout(() => {
919
+ const unmountEvent = new CustomEvent(AUTOFOCUS_ON_UNMOUNT, EVENT_OPTIONS);
920
+ container.addEventListener(AUTOFOCUS_ON_UNMOUNT, onUnmountAutoFocus);
921
+ container.dispatchEvent(unmountEvent);
922
+ if (!unmountEvent.defaultPrevented) {
923
+ focus(previouslyFocusedElement ?? document.body, { select: true });
924
+ }
925
+ container.removeEventListener(AUTOFOCUS_ON_UNMOUNT, onUnmountAutoFocus);
926
+ focusScopesStack.remove(focusScope);
927
+ }, 0);
928
+ };
929
+ }
930
+ }, [container, onMountAutoFocus, onUnmountAutoFocus, focusScope]);
931
+ const handleKeyDown = reactExports.useCallback(
932
+ (event) => {
933
+ if (!loop && !trapped) return;
934
+ if (focusScope.paused) return;
935
+ const isTabKey = event.key === "Tab" && !event.altKey && !event.ctrlKey && !event.metaKey;
936
+ const focusedElement = document.activeElement;
937
+ if (isTabKey && focusedElement) {
938
+ const container2 = event.currentTarget;
939
+ const [first, last] = getTabbableEdges(container2);
940
+ const hasTabbableElementsInside = first && last;
941
+ if (!hasTabbableElementsInside) {
942
+ if (focusedElement === container2) event.preventDefault();
943
+ } else {
944
+ if (!event.shiftKey && focusedElement === last) {
945
+ event.preventDefault();
946
+ if (loop) focus(first, { select: true });
947
+ } else if (event.shiftKey && focusedElement === first) {
948
+ event.preventDefault();
949
+ if (loop) focus(last, { select: true });
950
+ }
951
+ }
952
+ }
953
+ },
954
+ [loop, trapped, focusScope.paused]
955
+ );
956
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Primitive.div, { tabIndex: -1, ...scopeProps, ref: composedRefs, onKeyDown: handleKeyDown });
957
+ });
958
+ FocusScope.displayName = FOCUS_SCOPE_NAME;
959
+ function focusFirst(candidates, { select = false } = {}) {
960
+ const previouslyFocusedElement = document.activeElement;
961
+ for (const candidate of candidates) {
962
+ focus(candidate, { select });
963
+ if (document.activeElement !== previouslyFocusedElement) return;
964
+ }
965
+ }
966
+ function getTabbableEdges(container) {
967
+ const candidates = getTabbableCandidates(container);
968
+ const first = findVisible(candidates, container);
969
+ const last = findVisible(candidates.reverse(), container);
970
+ return [first, last];
971
+ }
972
+ function getTabbableCandidates(container) {
973
+ const nodes = [];
974
+ const walker = document.createTreeWalker(container, NodeFilter.SHOW_ELEMENT, {
975
+ acceptNode: (node) => {
976
+ const isHiddenInput = node.tagName === "INPUT" && node.type === "hidden";
977
+ if (node.disabled || node.hidden || isHiddenInput) return NodeFilter.FILTER_SKIP;
978
+ return node.tabIndex >= 0 ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;
979
+ }
980
+ });
981
+ while (walker.nextNode()) nodes.push(walker.currentNode);
982
+ return nodes;
983
+ }
984
+ function findVisible(elements, container) {
985
+ for (const element of elements) {
986
+ if (!isHidden(element, { upTo: container })) return element;
987
+ }
988
+ }
989
+ function isHidden(node, { upTo }) {
990
+ if (getComputedStyle(node).visibility === "hidden") return true;
991
+ while (node) {
992
+ if (upTo !== void 0 && node === upTo) return false;
993
+ if (getComputedStyle(node).display === "none") return true;
994
+ node = node.parentElement;
995
+ }
996
+ return false;
997
+ }
998
+ function isSelectableInput(element) {
999
+ return element instanceof HTMLInputElement && "select" in element;
1000
+ }
1001
+ function focus(element, { select = false } = {}) {
1002
+ if (element && element.focus) {
1003
+ const previouslyFocusedElement = document.activeElement;
1004
+ element.focus({ preventScroll: true });
1005
+ if (element !== previouslyFocusedElement && isSelectableInput(element) && select)
1006
+ element.select();
1007
+ }
1008
+ }
1009
+ var focusScopesStack = createFocusScopesStack();
1010
+ function createFocusScopesStack() {
1011
+ let stack = [];
1012
+ return {
1013
+ add(focusScope) {
1014
+ const activeFocusScope = stack[0];
1015
+ if (focusScope !== activeFocusScope) {
1016
+ activeFocusScope == null ? void 0 : activeFocusScope.pause();
1017
+ }
1018
+ stack = arrayRemove(stack, focusScope);
1019
+ stack.unshift(focusScope);
1020
+ },
1021
+ remove(focusScope) {
1022
+ var _a;
1023
+ stack = arrayRemove(stack, focusScope);
1024
+ (_a = stack[0]) == null ? void 0 : _a.resume();
1025
+ }
1026
+ };
1027
+ }
1028
+ function arrayRemove(array, item) {
1029
+ const updatedArray = [...array];
1030
+ const index = updatedArray.indexOf(item);
1031
+ if (index !== -1) {
1032
+ updatedArray.splice(index, 1);
1033
+ }
1034
+ return updatedArray;
1035
+ }
1036
+ function removeLinks(items) {
1037
+ return items.filter((item) => item.tagName !== "A");
1038
+ }
1039
+ function usePrevious$1(value) {
1040
+ const ref = reactExports.useRef({ value, previous: value });
1041
+ return reactExports.useMemo(() => {
1042
+ if (ref.current.value !== value) {
1043
+ ref.current.previous = ref.current.value;
1044
+ ref.current.value = value;
1045
+ }
1046
+ return ref.current.previous;
1047
+ }, [value]);
1048
+ }
1049
+ var OPEN_KEYS = [" ", "Enter", "ArrowUp", "ArrowDown"];
1050
+ var SELECTION_KEYS = [" ", "Enter"];
1051
+ var SELECT_NAME = "Select";
1052
+ var [Collection, useCollection, createCollectionScope] = createCollection(SELECT_NAME);
1053
+ var [createSelectContext] = createContextScope(SELECT_NAME, [
1054
+ createCollectionScope,
1055
+ createPopperScope
1056
+ ]);
1057
+ var usePopperScope = createPopperScope();
1058
+ var [SelectProvider, useSelectContext] = createSelectContext(SELECT_NAME);
1059
+ var [SelectNativeOptionsProvider, useSelectNativeOptionsContext] = createSelectContext(SELECT_NAME);
1060
+ var Select$1 = (props) => {
1061
+ const {
1062
+ __scopeSelect,
1063
+ children,
1064
+ open: openProp,
1065
+ defaultOpen,
1066
+ onOpenChange,
1067
+ value: valueProp,
1068
+ defaultValue,
1069
+ onValueChange,
1070
+ dir,
1071
+ name,
1072
+ autoComplete,
1073
+ disabled,
1074
+ required,
1075
+ form
1076
+ } = props;
1077
+ const popperScope = usePopperScope(__scopeSelect);
1078
+ const [trigger, setTrigger] = reactExports.useState(null);
1079
+ const [valueNode, setValueNode] = reactExports.useState(null);
1080
+ const [valueNodeHasChildren, setValueNodeHasChildren] = reactExports.useState(false);
1081
+ const direction = useDirection(dir);
1082
+ const [open, setOpen] = useControllableState({
1083
+ prop: openProp,
1084
+ defaultProp: defaultOpen ?? false,
1085
+ onChange: onOpenChange,
1086
+ caller: SELECT_NAME
1087
+ });
1088
+ const [value, setValue] = useControllableState({
1089
+ prop: valueProp,
1090
+ defaultProp: defaultValue,
1091
+ onChange: onValueChange,
1092
+ caller: SELECT_NAME
1093
+ });
1094
+ const triggerPointerDownPosRef = reactExports.useRef(null);
1095
+ const isFormControl = trigger ? form || !!trigger.closest("form") : true;
1096
+ const [nativeOptionsSet, setNativeOptionsSet] = reactExports.useState(/* @__PURE__ */ new Set());
1097
+ const nativeSelectKey = Array.from(nativeOptionsSet).map((option) => option.props.value).join(";");
1098
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Root2$1, { ...popperScope, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
1099
+ SelectProvider,
1100
+ {
1101
+ required,
1102
+ scope: __scopeSelect,
1103
+ trigger,
1104
+ onTriggerChange: setTrigger,
1105
+ valueNode,
1106
+ onValueNodeChange: setValueNode,
1107
+ valueNodeHasChildren,
1108
+ onValueNodeHasChildrenChange: setValueNodeHasChildren,
1109
+ contentId: useId(),
1110
+ value,
1111
+ onValueChange: setValue,
1112
+ open,
1113
+ onOpenChange: setOpen,
1114
+ dir: direction,
1115
+ triggerPointerDownPosRef,
1116
+ disabled,
1117
+ children: [
1118
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Collection.Provider, { scope: __scopeSelect, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1119
+ SelectNativeOptionsProvider,
1120
+ {
1121
+ scope: props.__scopeSelect,
1122
+ onNativeOptionAdd: reactExports.useCallback((option) => {
1123
+ setNativeOptionsSet((prev) => new Set(prev).add(option));
1124
+ }, []),
1125
+ onNativeOptionRemove: reactExports.useCallback((option) => {
1126
+ setNativeOptionsSet((prev) => {
1127
+ const optionsSet = new Set(prev);
1128
+ optionsSet.delete(option);
1129
+ return optionsSet;
1130
+ });
1131
+ }, []),
1132
+ children
1133
+ }
1134
+ ) }),
1135
+ isFormControl ? /* @__PURE__ */ jsxRuntimeExports.jsxs(
1136
+ SelectBubbleInput,
1137
+ {
1138
+ "aria-hidden": true,
1139
+ required,
1140
+ tabIndex: -1,
1141
+ name,
1142
+ autoComplete,
1143
+ value,
1144
+ onChange: (event) => setValue(event.target.value),
1145
+ disabled,
1146
+ form,
1147
+ children: [
1148
+ value === void 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "" }) : null,
1149
+ Array.from(nativeOptionsSet)
1150
+ ]
1151
+ },
1152
+ nativeSelectKey
1153
+ ) : null
1154
+ ]
1155
+ }
1156
+ ) });
1157
+ };
1158
+ Select$1.displayName = SELECT_NAME;
1159
+ var TRIGGER_NAME$1 = "SelectTrigger";
1160
+ var SelectTrigger$1 = reactExports.forwardRef(
1161
+ (props, forwardedRef) => {
1162
+ const { __scopeSelect, disabled = false, ...triggerProps } = props;
1163
+ const popperScope = usePopperScope(__scopeSelect);
1164
+ const context = useSelectContext(TRIGGER_NAME$1, __scopeSelect);
1165
+ const isDisabled = context.disabled || disabled;
1166
+ const composedRefs = useComposedRefs(forwardedRef, context.onTriggerChange);
1167
+ const getItems = useCollection(__scopeSelect);
1168
+ const pointerTypeRef = reactExports.useRef("touch");
1169
+ const [searchRef, handleTypeaheadSearch, resetTypeahead] = useTypeaheadSearch((search) => {
1170
+ const enabledItems = getItems().filter((item) => !item.disabled);
1171
+ const currentItem = enabledItems.find((item) => item.value === context.value);
1172
+ const nextItem = findNextItem(enabledItems, search, currentItem);
1173
+ if (nextItem !== void 0) {
1174
+ context.onValueChange(nextItem.value);
1175
+ }
1176
+ });
1177
+ const handleOpen = (pointerEvent) => {
1178
+ if (!isDisabled) {
1179
+ context.onOpenChange(true);
1180
+ resetTypeahead();
1181
+ }
1182
+ if (pointerEvent) {
1183
+ context.triggerPointerDownPosRef.current = {
1184
+ x: Math.round(pointerEvent.pageX),
1185
+ y: Math.round(pointerEvent.pageY)
1186
+ };
1187
+ }
1188
+ };
1189
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Anchor, { asChild: true, ...popperScope, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1190
+ Primitive.button,
1191
+ {
1192
+ type: "button",
1193
+ role: "combobox",
1194
+ "aria-controls": context.contentId,
1195
+ "aria-expanded": context.open,
1196
+ "aria-required": context.required,
1197
+ "aria-autocomplete": "none",
1198
+ dir: context.dir,
1199
+ "data-state": context.open ? "open" : "closed",
1200
+ disabled: isDisabled,
1201
+ "data-disabled": isDisabled ? "" : void 0,
1202
+ "data-placeholder": shouldShowPlaceholder(context.value) ? "" : void 0,
1203
+ ...triggerProps,
1204
+ ref: composedRefs,
1205
+ onClick: composeEventHandlers(triggerProps.onClick, (event) => {
1206
+ event.currentTarget.focus();
1207
+ if (pointerTypeRef.current !== "mouse") {
1208
+ handleOpen(event);
1209
+ }
1210
+ }),
1211
+ onPointerDown: composeEventHandlers(triggerProps.onPointerDown, (event) => {
1212
+ pointerTypeRef.current = event.pointerType;
1213
+ const target = event.target;
1214
+ if (target.hasPointerCapture(event.pointerId)) {
1215
+ target.releasePointerCapture(event.pointerId);
1216
+ }
1217
+ if (event.button === 0 && event.ctrlKey === false && event.pointerType === "mouse") {
1218
+ handleOpen(event);
1219
+ event.preventDefault();
1220
+ }
1221
+ }),
1222
+ onKeyDown: composeEventHandlers(triggerProps.onKeyDown, (event) => {
1223
+ const isTypingAhead = searchRef.current !== "";
1224
+ const isModifierKey = event.ctrlKey || event.altKey || event.metaKey;
1225
+ if (!isModifierKey && event.key.length === 1) handleTypeaheadSearch(event.key);
1226
+ if (isTypingAhead && event.key === " ") return;
1227
+ if (OPEN_KEYS.includes(event.key)) {
1228
+ handleOpen();
1229
+ event.preventDefault();
1230
+ }
1231
+ })
1232
+ }
1233
+ ) });
1234
+ }
1235
+ );
1236
+ SelectTrigger$1.displayName = TRIGGER_NAME$1;
1237
+ var VALUE_NAME = "SelectValue";
1238
+ var SelectValue$1 = reactExports.forwardRef(
1239
+ (props, forwardedRef) => {
1240
+ const { __scopeSelect, className, style, children, placeholder = "", ...valueProps } = props;
1241
+ const context = useSelectContext(VALUE_NAME, __scopeSelect);
1242
+ const { onValueNodeHasChildrenChange } = context;
1243
+ const hasChildren = children !== void 0;
1244
+ const composedRefs = useComposedRefs(forwardedRef, context.onValueNodeChange);
1245
+ useLayoutEffect2(() => {
1246
+ onValueNodeHasChildrenChange(hasChildren);
1247
+ }, [onValueNodeHasChildrenChange, hasChildren]);
1248
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
1249
+ Primitive.span,
1250
+ {
1251
+ ...valueProps,
1252
+ ref: composedRefs,
1253
+ style: { pointerEvents: "none" },
1254
+ children: shouldShowPlaceholder(context.value) ? /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: placeholder }) : children
1255
+ }
1256
+ );
1257
+ }
1258
+ );
1259
+ SelectValue$1.displayName = VALUE_NAME;
1260
+ var ICON_NAME = "SelectIcon";
1261
+ var SelectIcon = reactExports.forwardRef(
1262
+ (props, forwardedRef) => {
1263
+ const { __scopeSelect, children, ...iconProps } = props;
1264
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Primitive.span, { "aria-hidden": true, ...iconProps, ref: forwardedRef, children: children || "▼" });
1265
+ }
1266
+ );
1267
+ SelectIcon.displayName = ICON_NAME;
1268
+ var PORTAL_NAME$1 = "SelectPortal";
1269
+ var SelectPortal = (props) => {
1270
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Portal$2, { asChild: true, ...props });
1271
+ };
1272
+ SelectPortal.displayName = PORTAL_NAME$1;
1273
+ var CONTENT_NAME$1 = "SelectContent";
1274
+ var SelectContent$1 = reactExports.forwardRef(
1275
+ (props, forwardedRef) => {
1276
+ const context = useSelectContext(CONTENT_NAME$1, props.__scopeSelect);
1277
+ const [fragment, setFragment] = reactExports.useState();
1278
+ useLayoutEffect2(() => {
1279
+ setFragment(new DocumentFragment());
1280
+ }, []);
1281
+ if (!context.open) {
1282
+ const frag = fragment;
1283
+ return frag ? reactDomExports.createPortal(
1284
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SelectContentProvider, { scope: props.__scopeSelect, children: /* @__PURE__ */ jsxRuntimeExports.jsx(Collection.Slot, { scope: props.__scopeSelect, children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: props.children }) }) }),
1285
+ frag
1286
+ ) : null;
1287
+ }
1288
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(SelectContentImpl, { ...props, ref: forwardedRef });
1289
+ }
1290
+ );
1291
+ SelectContent$1.displayName = CONTENT_NAME$1;
1292
+ var CONTENT_MARGIN = 10;
1293
+ var [SelectContentProvider, useSelectContentContext] = createSelectContext(CONTENT_NAME$1);
1294
+ var CONTENT_IMPL_NAME = "SelectContentImpl";
1295
+ var Slot$1 = createSlot("SelectContent.RemoveScroll");
1296
+ var SelectContentImpl = reactExports.forwardRef(
1297
+ (props, forwardedRef) => {
1298
+ const {
1299
+ __scopeSelect,
1300
+ position = "item-aligned",
1301
+ onCloseAutoFocus,
1302
+ onEscapeKeyDown,
1303
+ onPointerDownOutside,
1304
+ //
1305
+ // PopperContent props
1306
+ side,
1307
+ sideOffset,
1308
+ align,
1309
+ alignOffset,
1310
+ arrowPadding,
1311
+ collisionBoundary,
1312
+ collisionPadding,
1313
+ sticky,
1314
+ hideWhenDetached,
1315
+ avoidCollisions,
1316
+ //
1317
+ ...contentProps
1318
+ } = props;
1319
+ const context = useSelectContext(CONTENT_NAME$1, __scopeSelect);
1320
+ const [content, setContent] = reactExports.useState(null);
1321
+ const [viewport, setViewport] = reactExports.useState(null);
1322
+ const composedRefs = useComposedRefs(forwardedRef, (node) => setContent(node));
1323
+ const [selectedItem, setSelectedItem] = reactExports.useState(null);
1324
+ const [selectedItemText, setSelectedItemText] = reactExports.useState(
1325
+ null
1326
+ );
1327
+ const getItems = useCollection(__scopeSelect);
1328
+ const [isPositioned, setIsPositioned] = reactExports.useState(false);
1329
+ const firstValidItemFoundRef = reactExports.useRef(false);
1330
+ reactExports.useEffect(() => {
1331
+ if (content) return hideOthers(content);
1332
+ }, [content]);
1333
+ useFocusGuards();
1334
+ const focusFirst2 = reactExports.useCallback(
1335
+ (candidates) => {
1336
+ const [firstItem, ...restItems] = getItems().map((item) => item.ref.current);
1337
+ const [lastItem] = restItems.slice(-1);
1338
+ const PREVIOUSLY_FOCUSED_ELEMENT = document.activeElement;
1339
+ for (const candidate of candidates) {
1340
+ if (candidate === PREVIOUSLY_FOCUSED_ELEMENT) return;
1341
+ candidate == null ? void 0 : candidate.scrollIntoView({ block: "nearest" });
1342
+ if (candidate === firstItem && viewport) viewport.scrollTop = 0;
1343
+ if (candidate === lastItem && viewport) viewport.scrollTop = viewport.scrollHeight;
1344
+ candidate == null ? void 0 : candidate.focus();
1345
+ if (document.activeElement !== PREVIOUSLY_FOCUSED_ELEMENT) return;
1346
+ }
1347
+ },
1348
+ [getItems, viewport]
1349
+ );
1350
+ const focusSelectedItem = reactExports.useCallback(
1351
+ () => focusFirst2([selectedItem, content]),
1352
+ [focusFirst2, selectedItem, content]
1353
+ );
1354
+ reactExports.useEffect(() => {
1355
+ if (isPositioned) {
1356
+ focusSelectedItem();
1357
+ }
1358
+ }, [isPositioned, focusSelectedItem]);
1359
+ const { onOpenChange, triggerPointerDownPosRef } = context;
1360
+ reactExports.useEffect(() => {
1361
+ if (content) {
1362
+ let pointerMoveDelta = { x: 0, y: 0 };
1363
+ const handlePointerMove = (event) => {
1364
+ var _a, _b;
1365
+ pointerMoveDelta = {
1366
+ x: Math.abs(Math.round(event.pageX) - (((_a = triggerPointerDownPosRef.current) == null ? void 0 : _a.x) ?? 0)),
1367
+ y: Math.abs(Math.round(event.pageY) - (((_b = triggerPointerDownPosRef.current) == null ? void 0 : _b.y) ?? 0))
1368
+ };
1369
+ };
1370
+ const handlePointerUp = (event) => {
1371
+ if (pointerMoveDelta.x <= 10 && pointerMoveDelta.y <= 10) {
1372
+ event.preventDefault();
1373
+ } else {
1374
+ if (!content.contains(event.target)) {
1375
+ onOpenChange(false);
1376
+ }
1377
+ }
1378
+ document.removeEventListener("pointermove", handlePointerMove);
1379
+ triggerPointerDownPosRef.current = null;
1380
+ };
1381
+ if (triggerPointerDownPosRef.current !== null) {
1382
+ document.addEventListener("pointermove", handlePointerMove);
1383
+ document.addEventListener("pointerup", handlePointerUp, { capture: true, once: true });
1384
+ }
1385
+ return () => {
1386
+ document.removeEventListener("pointermove", handlePointerMove);
1387
+ document.removeEventListener("pointerup", handlePointerUp, { capture: true });
1388
+ };
1389
+ }
1390
+ }, [content, onOpenChange, triggerPointerDownPosRef]);
1391
+ reactExports.useEffect(() => {
1392
+ const close = () => onOpenChange(false);
1393
+ window.addEventListener("blur", close);
1394
+ window.addEventListener("resize", close);
1395
+ return () => {
1396
+ window.removeEventListener("blur", close);
1397
+ window.removeEventListener("resize", close);
1398
+ };
1399
+ }, [onOpenChange]);
1400
+ const [searchRef, handleTypeaheadSearch] = useTypeaheadSearch((search) => {
1401
+ const enabledItems = getItems().filter((item) => !item.disabled);
1402
+ const currentItem = enabledItems.find((item) => item.ref.current === document.activeElement);
1403
+ const nextItem = findNextItem(enabledItems, search, currentItem);
1404
+ if (nextItem) {
1405
+ setTimeout(() => nextItem.ref.current.focus());
1406
+ }
1407
+ });
1408
+ const itemRefCallback = reactExports.useCallback(
1409
+ (node, value, disabled) => {
1410
+ const isFirstValidItem = !firstValidItemFoundRef.current && !disabled;
1411
+ const isSelectedItem = context.value !== void 0 && context.value === value;
1412
+ if (isSelectedItem || isFirstValidItem) {
1413
+ setSelectedItem(node);
1414
+ if (isFirstValidItem) firstValidItemFoundRef.current = true;
1415
+ }
1416
+ },
1417
+ [context.value]
1418
+ );
1419
+ const handleItemLeave = reactExports.useCallback(() => content == null ? void 0 : content.focus(), [content]);
1420
+ const itemTextRefCallback = reactExports.useCallback(
1421
+ (node, value, disabled) => {
1422
+ const isFirstValidItem = !firstValidItemFoundRef.current && !disabled;
1423
+ const isSelectedItem = context.value !== void 0 && context.value === value;
1424
+ if (isSelectedItem || isFirstValidItem) {
1425
+ setSelectedItemText(node);
1426
+ }
1427
+ },
1428
+ [context.value]
1429
+ );
1430
+ const SelectPosition = position === "popper" ? SelectPopperPosition : SelectItemAlignedPosition;
1431
+ const popperContentProps = SelectPosition === SelectPopperPosition ? {
1432
+ side,
1433
+ sideOffset,
1434
+ align,
1435
+ alignOffset,
1436
+ arrowPadding,
1437
+ collisionBoundary,
1438
+ collisionPadding,
1439
+ sticky,
1440
+ hideWhenDetached,
1441
+ avoidCollisions
1442
+ } : {};
1443
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
1444
+ SelectContentProvider,
1445
+ {
1446
+ scope: __scopeSelect,
1447
+ content,
1448
+ viewport,
1449
+ onViewportChange: setViewport,
1450
+ itemRefCallback,
1451
+ selectedItem,
1452
+ onItemLeave: handleItemLeave,
1453
+ itemTextRefCallback,
1454
+ focusSelectedItem,
1455
+ selectedItemText,
1456
+ position,
1457
+ isPositioned,
1458
+ searchRef,
1459
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(ReactRemoveScroll, { as: Slot$1, allowPinchZoom: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1460
+ FocusScope,
1461
+ {
1462
+ asChild: true,
1463
+ trapped: context.open,
1464
+ onMountAutoFocus: (event) => {
1465
+ event.preventDefault();
1466
+ },
1467
+ onUnmountAutoFocus: composeEventHandlers(onCloseAutoFocus, (event) => {
1468
+ var _a;
1469
+ (_a = context.trigger) == null ? void 0 : _a.focus({ preventScroll: true });
1470
+ event.preventDefault();
1471
+ }),
1472
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1473
+ DismissableLayer,
1474
+ {
1475
+ asChild: true,
1476
+ disableOutsidePointerEvents: true,
1477
+ onEscapeKeyDown,
1478
+ onPointerDownOutside,
1479
+ onFocusOutside: (event) => event.preventDefault(),
1480
+ onDismiss: () => context.onOpenChange(false),
1481
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1482
+ SelectPosition,
1483
+ {
1484
+ role: "listbox",
1485
+ id: context.contentId,
1486
+ "data-state": context.open ? "open" : "closed",
1487
+ dir: context.dir,
1488
+ onContextMenu: (event) => event.preventDefault(),
1489
+ ...contentProps,
1490
+ ...popperContentProps,
1491
+ onPlaced: () => setIsPositioned(true),
1492
+ ref: composedRefs,
1493
+ style: {
1494
+ // flex layout so we can place the scroll buttons properly
1495
+ display: "flex",
1496
+ flexDirection: "column",
1497
+ // reset the outline by default as the content MAY get focused
1498
+ outline: "none",
1499
+ ...contentProps.style
1500
+ },
1501
+ onKeyDown: composeEventHandlers(contentProps.onKeyDown, (event) => {
1502
+ const isModifierKey = event.ctrlKey || event.altKey || event.metaKey;
1503
+ if (event.key === "Tab") event.preventDefault();
1504
+ if (!isModifierKey && event.key.length === 1) handleTypeaheadSearch(event.key);
1505
+ if (["ArrowUp", "ArrowDown", "Home", "End"].includes(event.key)) {
1506
+ const items = getItems().filter((item) => !item.disabled);
1507
+ let candidateNodes = items.map((item) => item.ref.current);
1508
+ if (["ArrowUp", "End"].includes(event.key)) {
1509
+ candidateNodes = candidateNodes.slice().reverse();
1510
+ }
1511
+ if (["ArrowUp", "ArrowDown"].includes(event.key)) {
1512
+ const currentElement = event.target;
1513
+ const currentIndex = candidateNodes.indexOf(currentElement);
1514
+ candidateNodes = candidateNodes.slice(currentIndex + 1);
1515
+ }
1516
+ setTimeout(() => focusFirst2(candidateNodes));
1517
+ event.preventDefault();
1518
+ }
1519
+ })
1520
+ }
1521
+ )
1522
+ }
1523
+ )
1524
+ }
1525
+ ) })
1526
+ }
1527
+ );
1528
+ }
1529
+ );
1530
+ SelectContentImpl.displayName = CONTENT_IMPL_NAME;
1531
+ var ITEM_ALIGNED_POSITION_NAME = "SelectItemAlignedPosition";
1532
+ var SelectItemAlignedPosition = reactExports.forwardRef((props, forwardedRef) => {
1533
+ const { __scopeSelect, onPlaced, ...popperProps } = props;
1534
+ const context = useSelectContext(CONTENT_NAME$1, __scopeSelect);
1535
+ const contentContext = useSelectContentContext(CONTENT_NAME$1, __scopeSelect);
1536
+ const [contentWrapper, setContentWrapper] = reactExports.useState(null);
1537
+ const [content, setContent] = reactExports.useState(null);
1538
+ const composedRefs = useComposedRefs(forwardedRef, (node) => setContent(node));
1539
+ const getItems = useCollection(__scopeSelect);
1540
+ const shouldExpandOnScrollRef = reactExports.useRef(false);
1541
+ const shouldRepositionRef = reactExports.useRef(true);
1542
+ const { viewport, selectedItem, selectedItemText, focusSelectedItem } = contentContext;
1543
+ const position = reactExports.useCallback(() => {
1544
+ if (context.trigger && context.valueNode && contentWrapper && content && viewport && selectedItem && selectedItemText) {
1545
+ const triggerRect = context.trigger.getBoundingClientRect();
1546
+ const contentRect = content.getBoundingClientRect();
1547
+ const valueNodeRect = context.valueNode.getBoundingClientRect();
1548
+ const itemTextRect = selectedItemText.getBoundingClientRect();
1549
+ if (context.dir !== "rtl") {
1550
+ const itemTextOffset = itemTextRect.left - contentRect.left;
1551
+ const left = valueNodeRect.left - itemTextOffset;
1552
+ const leftDelta = triggerRect.left - left;
1553
+ const minContentWidth = triggerRect.width + leftDelta;
1554
+ const contentWidth = Math.max(minContentWidth, contentRect.width);
1555
+ const rightEdge = window.innerWidth - CONTENT_MARGIN;
1556
+ const clampedLeft = clamp(left, [
1557
+ CONTENT_MARGIN,
1558
+ // Prevents the content from going off the starting edge of the
1559
+ // viewport. It may still go off the ending edge, but this can be
1560
+ // controlled by the user since they may want to manage overflow in a
1561
+ // specific way.
1562
+ // https://github.com/radix-ui/primitives/issues/2049
1563
+ Math.max(CONTENT_MARGIN, rightEdge - contentWidth)
1564
+ ]);
1565
+ contentWrapper.style.minWidth = minContentWidth + "px";
1566
+ contentWrapper.style.left = clampedLeft + "px";
1567
+ } else {
1568
+ const itemTextOffset = contentRect.right - itemTextRect.right;
1569
+ const right = window.innerWidth - valueNodeRect.right - itemTextOffset;
1570
+ const rightDelta = window.innerWidth - triggerRect.right - right;
1571
+ const minContentWidth = triggerRect.width + rightDelta;
1572
+ const contentWidth = Math.max(minContentWidth, contentRect.width);
1573
+ const leftEdge = window.innerWidth - CONTENT_MARGIN;
1574
+ const clampedRight = clamp(right, [
1575
+ CONTENT_MARGIN,
1576
+ Math.max(CONTENT_MARGIN, leftEdge - contentWidth)
1577
+ ]);
1578
+ contentWrapper.style.minWidth = minContentWidth + "px";
1579
+ contentWrapper.style.right = clampedRight + "px";
1580
+ }
1581
+ const items = getItems();
1582
+ const availableHeight = window.innerHeight - CONTENT_MARGIN * 2;
1583
+ const itemsHeight = viewport.scrollHeight;
1584
+ const contentStyles = window.getComputedStyle(content);
1585
+ const contentBorderTopWidth = parseInt(contentStyles.borderTopWidth, 10);
1586
+ const contentPaddingTop = parseInt(contentStyles.paddingTop, 10);
1587
+ const contentBorderBottomWidth = parseInt(contentStyles.borderBottomWidth, 10);
1588
+ const contentPaddingBottom = parseInt(contentStyles.paddingBottom, 10);
1589
+ const fullContentHeight = contentBorderTopWidth + contentPaddingTop + itemsHeight + contentPaddingBottom + contentBorderBottomWidth;
1590
+ const minContentHeight = Math.min(selectedItem.offsetHeight * 5, fullContentHeight);
1591
+ const viewportStyles = window.getComputedStyle(viewport);
1592
+ const viewportPaddingTop = parseInt(viewportStyles.paddingTop, 10);
1593
+ const viewportPaddingBottom = parseInt(viewportStyles.paddingBottom, 10);
1594
+ const topEdgeToTriggerMiddle = triggerRect.top + triggerRect.height / 2 - CONTENT_MARGIN;
1595
+ const triggerMiddleToBottomEdge = availableHeight - topEdgeToTriggerMiddle;
1596
+ const selectedItemHalfHeight = selectedItem.offsetHeight / 2;
1597
+ const itemOffsetMiddle = selectedItem.offsetTop + selectedItemHalfHeight;
1598
+ const contentTopToItemMiddle = contentBorderTopWidth + contentPaddingTop + itemOffsetMiddle;
1599
+ const itemMiddleToContentBottom = fullContentHeight - contentTopToItemMiddle;
1600
+ const willAlignWithoutTopOverflow = contentTopToItemMiddle <= topEdgeToTriggerMiddle;
1601
+ if (willAlignWithoutTopOverflow) {
1602
+ const isLastItem = items.length > 0 && selectedItem === items[items.length - 1].ref.current;
1603
+ contentWrapper.style.bottom = "0px";
1604
+ const viewportOffsetBottom = content.clientHeight - viewport.offsetTop - viewport.offsetHeight;
1605
+ const clampedTriggerMiddleToBottomEdge = Math.max(
1606
+ triggerMiddleToBottomEdge,
1607
+ selectedItemHalfHeight + // viewport might have padding bottom, include it to avoid a scrollable viewport
1608
+ (isLastItem ? viewportPaddingBottom : 0) + viewportOffsetBottom + contentBorderBottomWidth
1609
+ );
1610
+ const height = contentTopToItemMiddle + clampedTriggerMiddleToBottomEdge;
1611
+ contentWrapper.style.height = height + "px";
1612
+ } else {
1613
+ const isFirstItem = items.length > 0 && selectedItem === items[0].ref.current;
1614
+ contentWrapper.style.top = "0px";
1615
+ const clampedTopEdgeToTriggerMiddle = Math.max(
1616
+ topEdgeToTriggerMiddle,
1617
+ contentBorderTopWidth + viewport.offsetTop + // viewport might have padding top, include it to avoid a scrollable viewport
1618
+ (isFirstItem ? viewportPaddingTop : 0) + selectedItemHalfHeight
1619
+ );
1620
+ const height = clampedTopEdgeToTriggerMiddle + itemMiddleToContentBottom;
1621
+ contentWrapper.style.height = height + "px";
1622
+ viewport.scrollTop = contentTopToItemMiddle - topEdgeToTriggerMiddle + viewport.offsetTop;
1623
+ }
1624
+ contentWrapper.style.margin = `${CONTENT_MARGIN}px 0`;
1625
+ contentWrapper.style.minHeight = minContentHeight + "px";
1626
+ contentWrapper.style.maxHeight = availableHeight + "px";
1627
+ onPlaced == null ? void 0 : onPlaced();
1628
+ requestAnimationFrame(() => shouldExpandOnScrollRef.current = true);
1629
+ }
1630
+ }, [
1631
+ getItems,
1632
+ context.trigger,
1633
+ context.valueNode,
1634
+ contentWrapper,
1635
+ content,
1636
+ viewport,
1637
+ selectedItem,
1638
+ selectedItemText,
1639
+ context.dir,
1640
+ onPlaced
1641
+ ]);
1642
+ useLayoutEffect2(() => position(), [position]);
1643
+ const [contentZIndex, setContentZIndex] = reactExports.useState();
1644
+ useLayoutEffect2(() => {
1645
+ if (content) setContentZIndex(window.getComputedStyle(content).zIndex);
1646
+ }, [content]);
1647
+ const handleScrollButtonChange = reactExports.useCallback(
1648
+ (node) => {
1649
+ if (node && shouldRepositionRef.current === true) {
1650
+ position();
1651
+ focusSelectedItem == null ? void 0 : focusSelectedItem();
1652
+ shouldRepositionRef.current = false;
1653
+ }
1654
+ },
1655
+ [position, focusSelectedItem]
1656
+ );
1657
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
1658
+ SelectViewportProvider,
1659
+ {
1660
+ scope: __scopeSelect,
1661
+ contentWrapper,
1662
+ shouldExpandOnScrollRef,
1663
+ onScrollButtonChange: handleScrollButtonChange,
1664
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1665
+ "div",
1666
+ {
1667
+ ref: setContentWrapper,
1668
+ style: {
1669
+ display: "flex",
1670
+ flexDirection: "column",
1671
+ position: "fixed",
1672
+ zIndex: contentZIndex
1673
+ },
1674
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1675
+ Primitive.div,
1676
+ {
1677
+ ...popperProps,
1678
+ ref: composedRefs,
1679
+ style: {
1680
+ // When we get the height of the content, it includes borders. If we were to set
1681
+ // the height without having `boxSizing: 'border-box'` it would be too big.
1682
+ boxSizing: "border-box",
1683
+ // We need to ensure the content doesn't get taller than the wrapper
1684
+ maxHeight: "100%",
1685
+ ...popperProps.style
1686
+ }
1687
+ }
1688
+ )
1689
+ }
1690
+ )
1691
+ }
1692
+ );
1693
+ });
1694
+ SelectItemAlignedPosition.displayName = ITEM_ALIGNED_POSITION_NAME;
1695
+ var POPPER_POSITION_NAME = "SelectPopperPosition";
1696
+ var SelectPopperPosition = reactExports.forwardRef((props, forwardedRef) => {
1697
+ const {
1698
+ __scopeSelect,
1699
+ align = "start",
1700
+ collisionPadding = CONTENT_MARGIN,
1701
+ ...popperProps
1702
+ } = props;
1703
+ const popperScope = usePopperScope(__scopeSelect);
1704
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
1705
+ Content$1,
1706
+ {
1707
+ ...popperScope,
1708
+ ...popperProps,
1709
+ ref: forwardedRef,
1710
+ align,
1711
+ collisionPadding,
1712
+ style: {
1713
+ // Ensure border-box for floating-ui calculations
1714
+ boxSizing: "border-box",
1715
+ ...popperProps.style,
1716
+ // re-namespace exposed content custom properties
1717
+ ...{
1718
+ "--radix-select-content-transform-origin": "var(--radix-popper-transform-origin)",
1719
+ "--radix-select-content-available-width": "var(--radix-popper-available-width)",
1720
+ "--radix-select-content-available-height": "var(--radix-popper-available-height)",
1721
+ "--radix-select-trigger-width": "var(--radix-popper-anchor-width)",
1722
+ "--radix-select-trigger-height": "var(--radix-popper-anchor-height)"
1723
+ }
1724
+ }
1725
+ }
1726
+ );
1727
+ });
1728
+ SelectPopperPosition.displayName = POPPER_POSITION_NAME;
1729
+ var [SelectViewportProvider, useSelectViewportContext] = createSelectContext(CONTENT_NAME$1, {});
1730
+ var VIEWPORT_NAME = "SelectViewport";
1731
+ var SelectViewport = reactExports.forwardRef(
1732
+ (props, forwardedRef) => {
1733
+ const { __scopeSelect, nonce, ...viewportProps } = props;
1734
+ const contentContext = useSelectContentContext(VIEWPORT_NAME, __scopeSelect);
1735
+ const viewportContext = useSelectViewportContext(VIEWPORT_NAME, __scopeSelect);
1736
+ const composedRefs = useComposedRefs(forwardedRef, contentContext.onViewportChange);
1737
+ const prevScrollTopRef = reactExports.useRef(0);
1738
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
1739
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
1740
+ "style",
1741
+ {
1742
+ dangerouslySetInnerHTML: {
1743
+ __html: `[data-radix-select-viewport]{scrollbar-width:none;-ms-overflow-style:none;-webkit-overflow-scrolling:touch;}[data-radix-select-viewport]::-webkit-scrollbar{display:none}`
1744
+ },
1745
+ nonce
1746
+ }
1747
+ ),
1748
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Collection.Slot, { scope: __scopeSelect, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1749
+ Primitive.div,
1750
+ {
1751
+ "data-radix-select-viewport": "",
1752
+ role: "presentation",
1753
+ ...viewportProps,
1754
+ ref: composedRefs,
1755
+ style: {
1756
+ // we use position: 'relative' here on the `viewport` so that when we call
1757
+ // `selectedItem.offsetTop` in calculations, the offset is relative to the viewport
1758
+ // (independent of the scrollUpButton).
1759
+ position: "relative",
1760
+ flex: 1,
1761
+ // Viewport should only be scrollable in the vertical direction.
1762
+ // This won't work in vertical writing modes, so we'll need to
1763
+ // revisit this if/when that is supported
1764
+ // https://developer.chrome.com/blog/vertical-form-controls
1765
+ overflow: "hidden auto",
1766
+ ...viewportProps.style
1767
+ },
1768
+ onScroll: composeEventHandlers(viewportProps.onScroll, (event) => {
1769
+ const viewport = event.currentTarget;
1770
+ const { contentWrapper, shouldExpandOnScrollRef } = viewportContext;
1771
+ if ((shouldExpandOnScrollRef == null ? void 0 : shouldExpandOnScrollRef.current) && contentWrapper) {
1772
+ const scrolledBy = Math.abs(prevScrollTopRef.current - viewport.scrollTop);
1773
+ if (scrolledBy > 0) {
1774
+ const availableHeight = window.innerHeight - CONTENT_MARGIN * 2;
1775
+ const cssMinHeight = parseFloat(contentWrapper.style.minHeight);
1776
+ const cssHeight = parseFloat(contentWrapper.style.height);
1777
+ const prevHeight = Math.max(cssMinHeight, cssHeight);
1778
+ if (prevHeight < availableHeight) {
1779
+ const nextHeight = prevHeight + scrolledBy;
1780
+ const clampedNextHeight = Math.min(availableHeight, nextHeight);
1781
+ const heightDiff = nextHeight - clampedNextHeight;
1782
+ contentWrapper.style.height = clampedNextHeight + "px";
1783
+ if (contentWrapper.style.bottom === "0px") {
1784
+ viewport.scrollTop = heightDiff > 0 ? heightDiff : 0;
1785
+ contentWrapper.style.justifyContent = "flex-end";
1786
+ }
1787
+ }
1788
+ }
1789
+ }
1790
+ prevScrollTopRef.current = viewport.scrollTop;
1791
+ })
1792
+ }
1793
+ ) })
1794
+ ] });
1795
+ }
1796
+ );
1797
+ SelectViewport.displayName = VIEWPORT_NAME;
1798
+ var GROUP_NAME = "SelectGroup";
1799
+ var [SelectGroupContextProvider, useSelectGroupContext] = createSelectContext(GROUP_NAME);
1800
+ var SelectGroup = reactExports.forwardRef(
1801
+ (props, forwardedRef) => {
1802
+ const { __scopeSelect, ...groupProps } = props;
1803
+ const groupId = useId();
1804
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(SelectGroupContextProvider, { scope: __scopeSelect, id: groupId, children: /* @__PURE__ */ jsxRuntimeExports.jsx(Primitive.div, { role: "group", "aria-labelledby": groupId, ...groupProps, ref: forwardedRef }) });
1805
+ }
1806
+ );
1807
+ SelectGroup.displayName = GROUP_NAME;
1808
+ var LABEL_NAME = "SelectLabel";
1809
+ var SelectLabel$1 = reactExports.forwardRef(
1810
+ (props, forwardedRef) => {
1811
+ const { __scopeSelect, ...labelProps } = props;
1812
+ const groupContext = useSelectGroupContext(LABEL_NAME, __scopeSelect);
1813
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Primitive.div, { id: groupContext.id, ...labelProps, ref: forwardedRef });
1814
+ }
1815
+ );
1816
+ SelectLabel$1.displayName = LABEL_NAME;
1817
+ var ITEM_NAME = "SelectItem";
1818
+ var [SelectItemContextProvider, useSelectItemContext] = createSelectContext(ITEM_NAME);
1819
+ var SelectItem$1 = reactExports.forwardRef(
1820
+ (props, forwardedRef) => {
1821
+ const {
1822
+ __scopeSelect,
1823
+ value,
1824
+ disabled = false,
1825
+ textValue: textValueProp,
1826
+ ...itemProps
1827
+ } = props;
1828
+ const context = useSelectContext(ITEM_NAME, __scopeSelect);
1829
+ const contentContext = useSelectContentContext(ITEM_NAME, __scopeSelect);
1830
+ const isSelected = context.value === value;
1831
+ const [textValue, setTextValue] = reactExports.useState(textValueProp ?? "");
1832
+ const [isFocused, setIsFocused] = reactExports.useState(false);
1833
+ const composedRefs = useComposedRefs(
1834
+ forwardedRef,
1835
+ (node) => {
1836
+ var _a;
1837
+ return (_a = contentContext.itemRefCallback) == null ? void 0 : _a.call(contentContext, node, value, disabled);
1838
+ }
1839
+ );
1840
+ const textId = useId();
1841
+ const pointerTypeRef = reactExports.useRef("touch");
1842
+ const handleSelect = () => {
1843
+ if (!disabled) {
1844
+ context.onValueChange(value);
1845
+ context.onOpenChange(false);
1846
+ }
1847
+ };
1848
+ if (value === "") {
1849
+ throw new Error(
1850
+ "A <Select.Item /> must have a value prop that is not an empty string. This is because the Select value can be set to an empty string to clear the selection and show the placeholder."
1851
+ );
1852
+ }
1853
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
1854
+ SelectItemContextProvider,
1855
+ {
1856
+ scope: __scopeSelect,
1857
+ value,
1858
+ disabled,
1859
+ textId,
1860
+ isSelected,
1861
+ onItemTextChange: reactExports.useCallback((node) => {
1862
+ setTextValue((prevTextValue) => prevTextValue || ((node == null ? void 0 : node.textContent) ?? "").trim());
1863
+ }, []),
1864
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1865
+ Collection.ItemSlot,
1866
+ {
1867
+ scope: __scopeSelect,
1868
+ value,
1869
+ disabled,
1870
+ textValue,
1871
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1872
+ Primitive.div,
1873
+ {
1874
+ role: "option",
1875
+ "aria-labelledby": textId,
1876
+ "data-highlighted": isFocused ? "" : void 0,
1877
+ "aria-selected": isSelected && isFocused,
1878
+ "data-state": isSelected ? "checked" : "unchecked",
1879
+ "aria-disabled": disabled || void 0,
1880
+ "data-disabled": disabled ? "" : void 0,
1881
+ tabIndex: disabled ? void 0 : -1,
1882
+ ...itemProps,
1883
+ ref: composedRefs,
1884
+ onFocus: composeEventHandlers(itemProps.onFocus, () => setIsFocused(true)),
1885
+ onBlur: composeEventHandlers(itemProps.onBlur, () => setIsFocused(false)),
1886
+ onClick: composeEventHandlers(itemProps.onClick, () => {
1887
+ if (pointerTypeRef.current !== "mouse") handleSelect();
1888
+ }),
1889
+ onPointerUp: composeEventHandlers(itemProps.onPointerUp, () => {
1890
+ if (pointerTypeRef.current === "mouse") handleSelect();
1891
+ }),
1892
+ onPointerDown: composeEventHandlers(itemProps.onPointerDown, (event) => {
1893
+ pointerTypeRef.current = event.pointerType;
1894
+ }),
1895
+ onPointerMove: composeEventHandlers(itemProps.onPointerMove, (event) => {
1896
+ var _a;
1897
+ pointerTypeRef.current = event.pointerType;
1898
+ if (disabled) {
1899
+ (_a = contentContext.onItemLeave) == null ? void 0 : _a.call(contentContext);
1900
+ } else if (pointerTypeRef.current === "mouse") {
1901
+ event.currentTarget.focus({ preventScroll: true });
1902
+ }
1903
+ }),
1904
+ onPointerLeave: composeEventHandlers(itemProps.onPointerLeave, (event) => {
1905
+ var _a;
1906
+ if (event.currentTarget === document.activeElement) {
1907
+ (_a = contentContext.onItemLeave) == null ? void 0 : _a.call(contentContext);
1908
+ }
1909
+ }),
1910
+ onKeyDown: composeEventHandlers(itemProps.onKeyDown, (event) => {
1911
+ var _a;
1912
+ const isTypingAhead = ((_a = contentContext.searchRef) == null ? void 0 : _a.current) !== "";
1913
+ if (isTypingAhead && event.key === " ") return;
1914
+ if (SELECTION_KEYS.includes(event.key)) handleSelect();
1915
+ if (event.key === " ") event.preventDefault();
1916
+ })
1917
+ }
1918
+ )
1919
+ }
1920
+ )
1921
+ }
1922
+ );
1923
+ }
1924
+ );
1925
+ SelectItem$1.displayName = ITEM_NAME;
1926
+ var ITEM_TEXT_NAME = "SelectItemText";
1927
+ var SelectItemText = reactExports.forwardRef(
1928
+ (props, forwardedRef) => {
1929
+ const { __scopeSelect, className, style, ...itemTextProps } = props;
1930
+ const context = useSelectContext(ITEM_TEXT_NAME, __scopeSelect);
1931
+ const contentContext = useSelectContentContext(ITEM_TEXT_NAME, __scopeSelect);
1932
+ const itemContext = useSelectItemContext(ITEM_TEXT_NAME, __scopeSelect);
1933
+ const nativeOptionsContext = useSelectNativeOptionsContext(ITEM_TEXT_NAME, __scopeSelect);
1934
+ const [itemTextNode, setItemTextNode] = reactExports.useState(null);
1935
+ const composedRefs = useComposedRefs(
1936
+ forwardedRef,
1937
+ (node) => setItemTextNode(node),
1938
+ itemContext.onItemTextChange,
1939
+ (node) => {
1940
+ var _a;
1941
+ return (_a = contentContext.itemTextRefCallback) == null ? void 0 : _a.call(contentContext, node, itemContext.value, itemContext.disabled);
1942
+ }
1943
+ );
1944
+ const textContent = itemTextNode == null ? void 0 : itemTextNode.textContent;
1945
+ const nativeOption = reactExports.useMemo(
1946
+ () => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: itemContext.value, disabled: itemContext.disabled, children: textContent }, itemContext.value),
1947
+ [itemContext.disabled, itemContext.value, textContent]
1948
+ );
1949
+ const { onNativeOptionAdd, onNativeOptionRemove } = nativeOptionsContext;
1950
+ useLayoutEffect2(() => {
1951
+ onNativeOptionAdd(nativeOption);
1952
+ return () => onNativeOptionRemove(nativeOption);
1953
+ }, [onNativeOptionAdd, onNativeOptionRemove, nativeOption]);
1954
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
1955
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Primitive.span, { id: itemContext.textId, ...itemTextProps, ref: composedRefs }),
1956
+ itemContext.isSelected && context.valueNode && !context.valueNodeHasChildren ? reactDomExports.createPortal(itemTextProps.children, context.valueNode) : null
1957
+ ] });
1958
+ }
1959
+ );
1960
+ SelectItemText.displayName = ITEM_TEXT_NAME;
1961
+ var ITEM_INDICATOR_NAME = "SelectItemIndicator";
1962
+ var SelectItemIndicator = reactExports.forwardRef(
1963
+ (props, forwardedRef) => {
1964
+ const { __scopeSelect, ...itemIndicatorProps } = props;
1965
+ const itemContext = useSelectItemContext(ITEM_INDICATOR_NAME, __scopeSelect);
1966
+ return itemContext.isSelected ? /* @__PURE__ */ jsxRuntimeExports.jsx(Primitive.span, { "aria-hidden": true, ...itemIndicatorProps, ref: forwardedRef }) : null;
1967
+ }
1968
+ );
1969
+ SelectItemIndicator.displayName = ITEM_INDICATOR_NAME;
1970
+ var SCROLL_UP_BUTTON_NAME = "SelectScrollUpButton";
1971
+ var SelectScrollUpButton$1 = reactExports.forwardRef((props, forwardedRef) => {
1972
+ const contentContext = useSelectContentContext(SCROLL_UP_BUTTON_NAME, props.__scopeSelect);
1973
+ const viewportContext = useSelectViewportContext(SCROLL_UP_BUTTON_NAME, props.__scopeSelect);
1974
+ const [canScrollUp, setCanScrollUp] = reactExports.useState(false);
1975
+ const composedRefs = useComposedRefs(forwardedRef, viewportContext.onScrollButtonChange);
1976
+ useLayoutEffect2(() => {
1977
+ if (contentContext.viewport && contentContext.isPositioned) {
1978
+ let handleScroll2 = function() {
1979
+ const canScrollUp2 = viewport.scrollTop > 0;
1980
+ setCanScrollUp(canScrollUp2);
1981
+ };
1982
+ const viewport = contentContext.viewport;
1983
+ handleScroll2();
1984
+ viewport.addEventListener("scroll", handleScroll2);
1985
+ return () => viewport.removeEventListener("scroll", handleScroll2);
1986
+ }
1987
+ }, [contentContext.viewport, contentContext.isPositioned]);
1988
+ return canScrollUp ? /* @__PURE__ */ jsxRuntimeExports.jsx(
1989
+ SelectScrollButtonImpl,
1990
+ {
1991
+ ...props,
1992
+ ref: composedRefs,
1993
+ onAutoScroll: () => {
1994
+ const { viewport, selectedItem } = contentContext;
1995
+ if (viewport && selectedItem) {
1996
+ viewport.scrollTop = viewport.scrollTop - selectedItem.offsetHeight;
1997
+ }
1998
+ }
1999
+ }
2000
+ ) : null;
2001
+ });
2002
+ SelectScrollUpButton$1.displayName = SCROLL_UP_BUTTON_NAME;
2003
+ var SCROLL_DOWN_BUTTON_NAME = "SelectScrollDownButton";
2004
+ var SelectScrollDownButton$1 = reactExports.forwardRef((props, forwardedRef) => {
2005
+ const contentContext = useSelectContentContext(SCROLL_DOWN_BUTTON_NAME, props.__scopeSelect);
2006
+ const viewportContext = useSelectViewportContext(SCROLL_DOWN_BUTTON_NAME, props.__scopeSelect);
2007
+ const [canScrollDown, setCanScrollDown] = reactExports.useState(false);
2008
+ const composedRefs = useComposedRefs(forwardedRef, viewportContext.onScrollButtonChange);
2009
+ useLayoutEffect2(() => {
2010
+ if (contentContext.viewport && contentContext.isPositioned) {
2011
+ let handleScroll2 = function() {
2012
+ const maxScroll = viewport.scrollHeight - viewport.clientHeight;
2013
+ const canScrollDown2 = Math.ceil(viewport.scrollTop) < maxScroll;
2014
+ setCanScrollDown(canScrollDown2);
2015
+ };
2016
+ const viewport = contentContext.viewport;
2017
+ handleScroll2();
2018
+ viewport.addEventListener("scroll", handleScroll2);
2019
+ return () => viewport.removeEventListener("scroll", handleScroll2);
2020
+ }
2021
+ }, [contentContext.viewport, contentContext.isPositioned]);
2022
+ return canScrollDown ? /* @__PURE__ */ jsxRuntimeExports.jsx(
2023
+ SelectScrollButtonImpl,
2024
+ {
2025
+ ...props,
2026
+ ref: composedRefs,
2027
+ onAutoScroll: () => {
2028
+ const { viewport, selectedItem } = contentContext;
2029
+ if (viewport && selectedItem) {
2030
+ viewport.scrollTop = viewport.scrollTop + selectedItem.offsetHeight;
2031
+ }
2032
+ }
2033
+ }
2034
+ ) : null;
2035
+ });
2036
+ SelectScrollDownButton$1.displayName = SCROLL_DOWN_BUTTON_NAME;
2037
+ var SelectScrollButtonImpl = reactExports.forwardRef((props, forwardedRef) => {
2038
+ const { __scopeSelect, onAutoScroll, ...scrollIndicatorProps } = props;
2039
+ const contentContext = useSelectContentContext("SelectScrollButton", __scopeSelect);
2040
+ const autoScrollTimerRef = reactExports.useRef(null);
2041
+ const getItems = useCollection(__scopeSelect);
2042
+ const clearAutoScrollTimer = reactExports.useCallback(() => {
2043
+ if (autoScrollTimerRef.current !== null) {
2044
+ window.clearInterval(autoScrollTimerRef.current);
2045
+ autoScrollTimerRef.current = null;
2046
+ }
2047
+ }, []);
2048
+ reactExports.useEffect(() => {
2049
+ return () => clearAutoScrollTimer();
2050
+ }, [clearAutoScrollTimer]);
2051
+ useLayoutEffect2(() => {
2052
+ var _a;
2053
+ const activeItem = getItems().find((item) => item.ref.current === document.activeElement);
2054
+ (_a = activeItem == null ? void 0 : activeItem.ref.current) == null ? void 0 : _a.scrollIntoView({ block: "nearest" });
2055
+ }, [getItems]);
2056
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
2057
+ Primitive.div,
2058
+ {
2059
+ "aria-hidden": true,
2060
+ ...scrollIndicatorProps,
2061
+ ref: forwardedRef,
2062
+ style: { flexShrink: 0, ...scrollIndicatorProps.style },
2063
+ onPointerDown: composeEventHandlers(scrollIndicatorProps.onPointerDown, () => {
2064
+ if (autoScrollTimerRef.current === null) {
2065
+ autoScrollTimerRef.current = window.setInterval(onAutoScroll, 50);
2066
+ }
2067
+ }),
2068
+ onPointerMove: composeEventHandlers(scrollIndicatorProps.onPointerMove, () => {
2069
+ var _a;
2070
+ (_a = contentContext.onItemLeave) == null ? void 0 : _a.call(contentContext);
2071
+ if (autoScrollTimerRef.current === null) {
2072
+ autoScrollTimerRef.current = window.setInterval(onAutoScroll, 50);
2073
+ }
2074
+ }),
2075
+ onPointerLeave: composeEventHandlers(scrollIndicatorProps.onPointerLeave, () => {
2076
+ clearAutoScrollTimer();
2077
+ })
2078
+ }
2079
+ );
2080
+ });
2081
+ var SEPARATOR_NAME = "SelectSeparator";
2082
+ var SelectSeparator$1 = reactExports.forwardRef(
2083
+ (props, forwardedRef) => {
2084
+ const { __scopeSelect, ...separatorProps } = props;
2085
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Primitive.div, { "aria-hidden": true, ...separatorProps, ref: forwardedRef });
2086
+ }
2087
+ );
2088
+ SelectSeparator$1.displayName = SEPARATOR_NAME;
2089
+ var ARROW_NAME = "SelectArrow";
2090
+ var SelectArrow = reactExports.forwardRef(
2091
+ (props, forwardedRef) => {
2092
+ const { __scopeSelect, ...arrowProps } = props;
2093
+ const popperScope = usePopperScope(__scopeSelect);
2094
+ const context = useSelectContext(ARROW_NAME, __scopeSelect);
2095
+ const contentContext = useSelectContentContext(ARROW_NAME, __scopeSelect);
2096
+ return context.open && contentContext.position === "popper" ? /* @__PURE__ */ jsxRuntimeExports.jsx(Arrow, { ...popperScope, ...arrowProps, ref: forwardedRef }) : null;
2097
+ }
2098
+ );
2099
+ SelectArrow.displayName = ARROW_NAME;
2100
+ var BUBBLE_INPUT_NAME = "SelectBubbleInput";
2101
+ var SelectBubbleInput = reactExports.forwardRef(
2102
+ ({ __scopeSelect, value, ...props }, forwardedRef) => {
2103
+ const ref = reactExports.useRef(null);
2104
+ const composedRefs = useComposedRefs(forwardedRef, ref);
2105
+ const prevValue = usePrevious$1(value);
2106
+ reactExports.useEffect(() => {
2107
+ const select = ref.current;
2108
+ if (!select) return;
2109
+ const selectProto = window.HTMLSelectElement.prototype;
2110
+ const descriptor = Object.getOwnPropertyDescriptor(
2111
+ selectProto,
2112
+ "value"
2113
+ );
2114
+ const setValue = descriptor.set;
2115
+ if (prevValue !== value && setValue) {
2116
+ const event = new Event("change", { bubbles: true });
2117
+ setValue.call(select, value);
2118
+ select.dispatchEvent(event);
2119
+ }
2120
+ }, [prevValue, value]);
2121
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
2122
+ Primitive.select,
2123
+ {
2124
+ ...props,
2125
+ style: { ...VISUALLY_HIDDEN_STYLES, ...props.style },
2126
+ ref: composedRefs,
2127
+ defaultValue: value
2128
+ }
2129
+ );
2130
+ }
2131
+ );
2132
+ SelectBubbleInput.displayName = BUBBLE_INPUT_NAME;
2133
+ function shouldShowPlaceholder(value) {
2134
+ return value === "" || value === void 0;
2135
+ }
2136
+ function useTypeaheadSearch(onSearchChange) {
2137
+ const handleSearchChange = useCallbackRef(onSearchChange);
2138
+ const searchRef = reactExports.useRef("");
2139
+ const timerRef = reactExports.useRef(0);
2140
+ const handleTypeaheadSearch = reactExports.useCallback(
2141
+ (key) => {
2142
+ const search = searchRef.current + key;
2143
+ handleSearchChange(search);
2144
+ (function updateSearch(value) {
2145
+ searchRef.current = value;
2146
+ window.clearTimeout(timerRef.current);
2147
+ if (value !== "") timerRef.current = window.setTimeout(() => updateSearch(""), 1e3);
2148
+ })(search);
2149
+ },
2150
+ [handleSearchChange]
2151
+ );
2152
+ const resetTypeahead = reactExports.useCallback(() => {
2153
+ searchRef.current = "";
2154
+ window.clearTimeout(timerRef.current);
2155
+ }, []);
2156
+ reactExports.useEffect(() => {
2157
+ return () => window.clearTimeout(timerRef.current);
2158
+ }, []);
2159
+ return [searchRef, handleTypeaheadSearch, resetTypeahead];
2160
+ }
2161
+ function findNextItem(items, search, currentItem) {
2162
+ const isRepeated = search.length > 1 && Array.from(search).every((char) => char === search[0]);
2163
+ const normalizedSearch = isRepeated ? search[0] : search;
2164
+ const currentItemIndex = currentItem ? items.indexOf(currentItem) : -1;
2165
+ let wrappedItems = wrapArray(items, Math.max(currentItemIndex, 0));
2166
+ const excludeCurrentItem = normalizedSearch.length === 1;
2167
+ if (excludeCurrentItem) wrappedItems = wrappedItems.filter((v) => v !== currentItem);
2168
+ const nextItem = wrappedItems.find(
2169
+ (item) => item.textValue.toLowerCase().startsWith(normalizedSearch.toLowerCase())
2170
+ );
2171
+ return nextItem !== currentItem ? nextItem : void 0;
2172
+ }
2173
+ function wrapArray(array, startIndex) {
2174
+ return array.map((_, index) => array[(startIndex + index) % array.length]);
2175
+ }
2176
+ var Root2 = Select$1;
2177
+ var Trigger = SelectTrigger$1;
2178
+ var Value = SelectValue$1;
2179
+ var Icon = SelectIcon;
2180
+ var Portal$1 = SelectPortal;
2181
+ var Content2 = SelectContent$1;
2182
+ var Viewport = SelectViewport;
2183
+ var Label = SelectLabel$1;
2184
+ var Item = SelectItem$1;
2185
+ var ItemText = SelectItemText;
2186
+ var ItemIndicator = SelectItemIndicator;
2187
+ var ScrollUpButton = SelectScrollUpButton$1;
2188
+ var ScrollDownButton = SelectScrollDownButton$1;
2189
+ var Separator = SelectSeparator$1;
2190
+ const Select = Root2;
2191
+ const SelectValue = Value;
2192
+ const SelectTrigger = reactExports.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
2193
+ Trigger,
2194
+ {
2195
+ ref,
2196
+ className: cn(
2197
+ "flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
2198
+ className
2199
+ ),
2200
+ ...props,
2201
+ children: [
2202
+ children,
2203
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Icon, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "h-4 w-4 opacity-50" }) })
2204
+ ]
2205
+ }
2206
+ ));
2207
+ SelectTrigger.displayName = Trigger.displayName;
2208
+ const SelectScrollUpButton = reactExports.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntimeExports.jsx(
2209
+ ScrollUpButton,
2210
+ {
2211
+ ref,
2212
+ className: cn(
2213
+ "flex cursor-default items-center justify-center py-1",
2214
+ className
2215
+ ),
2216
+ ...props,
2217
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronUp, { className: "h-4 w-4" })
2218
+ }
2219
+ ));
2220
+ SelectScrollUpButton.displayName = ScrollUpButton.displayName;
2221
+ const SelectScrollDownButton = reactExports.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntimeExports.jsx(
2222
+ ScrollDownButton,
2223
+ {
2224
+ ref,
2225
+ className: cn(
2226
+ "flex cursor-default items-center justify-center py-1",
2227
+ className
2228
+ ),
2229
+ ...props,
2230
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "h-4 w-4" })
2231
+ }
2232
+ ));
2233
+ SelectScrollDownButton.displayName = ScrollDownButton.displayName;
2234
+ const SelectContent = reactExports.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsxRuntimeExports.jsx(Portal$1, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
2235
+ Content2,
2236
+ {
2237
+ ref,
2238
+ className: cn(
2239
+ "relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
2240
+ position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
2241
+ className
2242
+ ),
2243
+ position,
2244
+ ...props,
2245
+ children: [
2246
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SelectScrollUpButton, {}),
2247
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
2248
+ Viewport,
2249
+ {
2250
+ className: cn(
2251
+ "p-1",
2252
+ position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
2253
+ ),
2254
+ children
2255
+ }
2256
+ ),
2257
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SelectScrollDownButton, {})
2258
+ ]
2259
+ }
2260
+ ) }));
2261
+ SelectContent.displayName = Content2.displayName;
2262
+ const SelectLabel = reactExports.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntimeExports.jsx(
2263
+ Label,
2264
+ {
2265
+ ref,
2266
+ className: cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className),
2267
+ ...props
2268
+ }
2269
+ ));
2270
+ SelectLabel.displayName = Label.displayName;
2271
+ const SelectItem = reactExports.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
2272
+ Item,
2273
+ {
2274
+ ref,
2275
+ className: cn(
2276
+ "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
2277
+ className
2278
+ ),
2279
+ ...props,
2280
+ children: [
2281
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ItemIndicator, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "h-4 w-4" }) }) }),
2282
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ItemText, { children })
2283
+ ]
2284
+ }
2285
+ ));
2286
+ SelectItem.displayName = Item.displayName;
2287
+ const SelectSeparator = reactExports.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntimeExports.jsx(
2288
+ Separator,
2289
+ {
2290
+ ref,
2291
+ className: cn("-mx-1 my-1 h-px bg-muted", className),
2292
+ ...props
2293
+ }
2294
+ ));
2295
+ SelectSeparator.displayName = Separator.displayName;
2296
+ function useTableSelection({
2297
+ getItemId
2298
+ }) {
2299
+ const [selectedIds, setSelectedIds] = reactExports.useState(/* @__PURE__ */ new Set());
2300
+ const isSelected = reactExports.useCallback(
2301
+ (item) => selectedIds.has(getItemId(item)),
2302
+ [selectedIds, getItemId]
2303
+ );
2304
+ const toggleSelection = reactExports.useCallback(
2305
+ (item) => {
2306
+ const id = getItemId(item);
2307
+ setSelectedIds((prev) => {
2308
+ const next = new Set(prev);
2309
+ if (next.has(id)) {
2310
+ next.delete(id);
2311
+ } else {
2312
+ next.add(id);
2313
+ }
2314
+ return next;
2315
+ });
2316
+ },
2317
+ [getItemId]
2318
+ );
2319
+ const selectAll = reactExports.useCallback(
2320
+ (items) => {
2321
+ setSelectedIds(new Set(items.map(getItemId)));
2322
+ },
2323
+ [getItemId]
2324
+ );
2325
+ const clearSelection = reactExports.useCallback(() => {
2326
+ setSelectedIds(/* @__PURE__ */ new Set());
2327
+ }, []);
2328
+ const isAllSelected = reactExports.useCallback(
2329
+ (items) => {
2330
+ if (items.length === 0) return false;
2331
+ return items.every((item) => selectedIds.has(getItemId(item)));
2332
+ },
2333
+ [selectedIds, getItemId]
2334
+ );
2335
+ const isSomeSelected = reactExports.useCallback(
2336
+ (items) => {
2337
+ if (items.length === 0) return false;
2338
+ const someSelected = items.some(
2339
+ (item) => selectedIds.has(getItemId(item))
2340
+ );
2341
+ const allSelected = items.every(
2342
+ (item) => selectedIds.has(getItemId(item))
2343
+ );
2344
+ return someSelected && !allSelected;
2345
+ },
2346
+ [selectedIds, getItemId]
2347
+ );
2348
+ const toggleSelectAll = reactExports.useCallback(
2349
+ (items) => {
2350
+ if (isAllSelected(items)) {
2351
+ clearSelection();
2352
+ } else {
2353
+ selectAll(items);
2354
+ }
2355
+ },
2356
+ [isAllSelected, clearSelection, selectAll]
2357
+ );
2358
+ const selectById = reactExports.useCallback((id) => {
2359
+ setSelectedIds((prev) => {
2360
+ const next = new Set(prev);
2361
+ next.add(id);
2362
+ return next;
2363
+ });
2364
+ }, []);
2365
+ const deselectById = reactExports.useCallback((id) => {
2366
+ setSelectedIds((prev) => {
2367
+ const next = new Set(prev);
2368
+ next.delete(id);
2369
+ return next;
2370
+ });
2371
+ }, []);
2372
+ const selectionCount = selectedIds.size;
2373
+ return reactExports.useMemo(
2374
+ () => ({
2375
+ selectedIds,
2376
+ isSelected,
2377
+ toggleSelection,
2378
+ selectAll,
2379
+ clearSelection,
2380
+ toggleSelectAll,
2381
+ selectionCount,
2382
+ isAllSelected,
2383
+ isSomeSelected,
2384
+ selectById,
2385
+ deselectById
2386
+ }),
2387
+ [
2388
+ selectedIds,
2389
+ isSelected,
2390
+ toggleSelection,
2391
+ selectAll,
2392
+ clearSelection,
2393
+ toggleSelectAll,
2394
+ selectionCount,
2395
+ isAllSelected,
2396
+ isSomeSelected,
2397
+ selectById,
2398
+ deselectById
2399
+ ]
2400
+ );
2401
+ }
2402
+ function SelectionBar({
2403
+ selectionCount,
2404
+ onClearSelection,
2405
+ actions,
2406
+ itemLabel = "items",
2407
+ className
2408
+ }) {
2409
+ if (selectionCount === 0) {
2410
+ return null;
2411
+ }
2412
+ const label = selectionCount === 1 ? itemLabel.replace(/s$/, "") : itemLabel;
2413
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
2414
+ "div",
2415
+ {
2416
+ className: cn(
2417
+ "fixed bottom-6 left-1/2 -translate-x-1/2 z-50",
2418
+ "flex items-center gap-3 px-4 py-2.5 rounded-lg",
2419
+ "bg-primary text-primary-foreground shadow-lg",
2420
+ "animate-in fade-in slide-in-from-bottom-4 duration-200",
2421
+ className
2422
+ ),
2423
+ children: [
2424
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-sm font-medium", children: [
2425
+ selectionCount,
2426
+ " ",
2427
+ label,
2428
+ " selected"
2429
+ ] }),
2430
+ actions && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center gap-2 border-l border-primary-foreground/20 pl-3", children: actions }),
2431
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
2432
+ Button,
2433
+ {
2434
+ variant: "ghost",
2435
+ size: "sm",
2436
+ className: "h-7 px-2 text-primary-foreground hover:bg-primary-foreground/10 hover:text-primary-foreground",
2437
+ onClick: onClearSelection,
2438
+ children: [
2439
+ /* @__PURE__ */ jsxRuntimeExports.jsx(X, { className: "h-4 w-4" }),
2440
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sr-only", children: "Clear selection" })
2441
+ ]
2442
+ }
2443
+ )
2444
+ ]
2445
+ }
2446
+ );
2447
+ }
2448
+ function usePrevious(value) {
2449
+ const ref = reactExports.useRef({ value, previous: value });
2450
+ return reactExports.useMemo(() => {
2451
+ if (ref.current.value !== value) {
2452
+ ref.current.previous = ref.current.value;
2453
+ ref.current.value = value;
2454
+ }
2455
+ return ref.current.previous;
2456
+ }, [value]);
2457
+ }
2458
+ var CHECKBOX_NAME = "Checkbox";
2459
+ var [createCheckboxContext] = createContextScope$1(CHECKBOX_NAME);
2460
+ var [CheckboxProvider, useCheckboxContext] = createCheckboxContext(CHECKBOX_NAME);
2461
+ var Checkbox$1 = reactExports.forwardRef(
2462
+ (props, forwardedRef) => {
2463
+ const {
2464
+ __scopeCheckbox,
2465
+ name,
2466
+ checked: checkedProp,
2467
+ defaultChecked,
2468
+ required,
2469
+ disabled,
2470
+ value = "on",
2471
+ onCheckedChange,
2472
+ form,
2473
+ ...checkboxProps
2474
+ } = props;
2475
+ const [button, setButton] = reactExports.useState(null);
2476
+ const composedRefs = useComposedRefs$1(forwardedRef, (node) => setButton(node));
2477
+ const hasConsumerStoppedPropagationRef = reactExports.useRef(false);
2478
+ const isFormControl = button ? form || !!button.closest("form") : true;
2479
+ const [checked = false, setChecked] = useControllableState$1({
2480
+ prop: checkedProp,
2481
+ defaultProp: defaultChecked,
2482
+ onChange: onCheckedChange
2483
+ });
2484
+ const initialCheckedStateRef = reactExports.useRef(checked);
2485
+ reactExports.useEffect(() => {
2486
+ const form2 = button == null ? void 0 : button.form;
2487
+ if (form2) {
2488
+ const reset = () => setChecked(initialCheckedStateRef.current);
2489
+ form2.addEventListener("reset", reset);
2490
+ return () => form2.removeEventListener("reset", reset);
2491
+ }
2492
+ }, [button, setChecked]);
2493
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(CheckboxProvider, { scope: __scopeCheckbox, state: checked, disabled, children: [
2494
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
2495
+ Primitive$1.button,
2496
+ {
2497
+ type: "button",
2498
+ role: "checkbox",
2499
+ "aria-checked": isIndeterminate(checked) ? "mixed" : checked,
2500
+ "aria-required": required,
2501
+ "data-state": getState$1(checked),
2502
+ "data-disabled": disabled ? "" : void 0,
2503
+ disabled,
2504
+ value,
2505
+ ...checkboxProps,
2506
+ ref: composedRefs,
2507
+ onKeyDown: composeEventHandlers$1(props.onKeyDown, (event) => {
2508
+ if (event.key === "Enter") event.preventDefault();
2509
+ }),
2510
+ onClick: composeEventHandlers$1(props.onClick, (event) => {
2511
+ setChecked((prevChecked) => isIndeterminate(prevChecked) ? true : !prevChecked);
2512
+ if (isFormControl) {
2513
+ hasConsumerStoppedPropagationRef.current = event.isPropagationStopped();
2514
+ if (!hasConsumerStoppedPropagationRef.current) event.stopPropagation();
2515
+ }
2516
+ })
2517
+ }
2518
+ ),
2519
+ isFormControl && /* @__PURE__ */ jsxRuntimeExports.jsx(
2520
+ BubbleInput,
2521
+ {
2522
+ control: button,
2523
+ bubbles: !hasConsumerStoppedPropagationRef.current,
2524
+ name,
2525
+ value,
2526
+ checked,
2527
+ required,
2528
+ disabled,
2529
+ form,
2530
+ style: { transform: "translateX(-100%)" },
2531
+ defaultChecked: isIndeterminate(defaultChecked) ? false : defaultChecked
2532
+ }
2533
+ )
2534
+ ] });
2535
+ }
2536
+ );
2537
+ Checkbox$1.displayName = CHECKBOX_NAME;
2538
+ var INDICATOR_NAME = "CheckboxIndicator";
2539
+ var CheckboxIndicator = reactExports.forwardRef(
2540
+ (props, forwardedRef) => {
2541
+ const { __scopeCheckbox, forceMount, ...indicatorProps } = props;
2542
+ const context = useCheckboxContext(INDICATOR_NAME, __scopeCheckbox);
2543
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Presence, { present: forceMount || isIndeterminate(context.state) || context.state === true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
2544
+ Primitive$1.span,
2545
+ {
2546
+ "data-state": getState$1(context.state),
2547
+ "data-disabled": context.disabled ? "" : void 0,
2548
+ ...indicatorProps,
2549
+ ref: forwardedRef,
2550
+ style: { pointerEvents: "none", ...props.style }
2551
+ }
2552
+ ) });
2553
+ }
2554
+ );
2555
+ CheckboxIndicator.displayName = INDICATOR_NAME;
2556
+ var BubbleInput = (props) => {
2557
+ const { control, checked, bubbles = true, defaultChecked, ...inputProps } = props;
2558
+ const ref = reactExports.useRef(null);
2559
+ const prevChecked = usePrevious(checked);
2560
+ const controlSize = useSize(control);
2561
+ reactExports.useEffect(() => {
2562
+ const input = ref.current;
2563
+ const inputProto = window.HTMLInputElement.prototype;
2564
+ const descriptor = Object.getOwnPropertyDescriptor(inputProto, "checked");
2565
+ const setChecked = descriptor.set;
2566
+ if (prevChecked !== checked && setChecked) {
2567
+ const event = new Event("click", { bubbles });
2568
+ input.indeterminate = isIndeterminate(checked);
2569
+ setChecked.call(input, isIndeterminate(checked) ? false : checked);
2570
+ input.dispatchEvent(event);
2571
+ }
2572
+ }, [prevChecked, checked, bubbles]);
2573
+ const defaultCheckedRef = reactExports.useRef(isIndeterminate(checked) ? false : checked);
2574
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
2575
+ "input",
2576
+ {
2577
+ type: "checkbox",
2578
+ "aria-hidden": true,
2579
+ defaultChecked: defaultChecked ?? defaultCheckedRef.current,
2580
+ ...inputProps,
2581
+ tabIndex: -1,
2582
+ ref,
2583
+ style: {
2584
+ ...props.style,
2585
+ ...controlSize,
2586
+ position: "absolute",
2587
+ pointerEvents: "none",
2588
+ opacity: 0,
2589
+ margin: 0
2590
+ }
2591
+ }
2592
+ );
2593
+ };
2594
+ function isIndeterminate(checked) {
2595
+ return checked === "indeterminate";
2596
+ }
2597
+ function getState$1(checked) {
2598
+ return isIndeterminate(checked) ? "indeterminate" : checked ? "checked" : "unchecked";
2599
+ }
2600
+ var Root$1 = Checkbox$1;
2601
+ var Indicator = CheckboxIndicator;
2602
+ const Checkbox = reactExports.forwardRef(({ className, checked, indeterminate, onCheckedChange, ...props }, ref) => {
2603
+ const id = reactExports.useId();
2604
+ const checkboxId = props.id ?? id;
2605
+ const checkedState = indeterminate ? "indeterminate" : checked ?? false;
2606
+ return (
2607
+ // Label provides the click grace area and proper a11y association
2608
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
2609
+ "label",
2610
+ {
2611
+ htmlFor: checkboxId,
2612
+ className: "p-2 -m-2 inline-flex items-center justify-center cursor-pointer",
2613
+ onClick: (e) => e.stopPropagation(),
2614
+ onKeyDown: (e) => e.stopPropagation(),
2615
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(
2616
+ Root$1,
2617
+ {
2618
+ ref,
2619
+ id: checkboxId,
2620
+ checked: checkedState,
2621
+ onCheckedChange: (state) => {
2622
+ onCheckedChange == null ? void 0 : onCheckedChange(state === true);
2623
+ },
2624
+ className: cn(
2625
+ "peer h-4 w-4 shrink-0 rounded-sm border border-primary shadow",
2626
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
2627
+ "disabled:cursor-not-allowed disabled:opacity-50",
2628
+ "data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
2629
+ "data-[state=indeterminate]:bg-primary data-[state=indeterminate]:text-primary-foreground",
2630
+ className
2631
+ ),
2632
+ ...props,
2633
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(Indicator, { className: "flex items-center justify-center text-current", children: indeterminate ? /* @__PURE__ */ jsxRuntimeExports.jsx(Minus, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "h-3.5 w-3.5" }) })
2634
+ }
2635
+ )
2636
+ }
2637
+ )
2638
+ );
2639
+ });
2640
+ Checkbox.displayName = "Checkbox";
2641
+ function RunActionsDropdownContentInner({
2642
+ env,
2643
+ runId,
2644
+ runStatus,
2645
+ onSuccess
2646
+ }) {
2647
+ const [events, setEvents] = reactExports.useState(void 0);
2648
+ const [isLoading, setIsLoading] = reactExports.useState(true);
2649
+ const [run, setRun] = reactExports.useState(void 0);
2650
+ const status = (run == null ? void 0 : run.status) || runStatus;
2651
+ reactExports.useEffect(() => {
2652
+ setIsLoading(true);
2653
+ Promise.all([
2654
+ fetchRun(env, runId, "none"),
2655
+ fetchEvents(env, runId, { limit: 1e3, sortOrder: "desc" })
2656
+ ]).then(([runResult, eventsResult]) => {
2657
+ if (runResult.success) {
2658
+ setRun(runResult.data);
2659
+ }
2660
+ if (eventsResult.success) {
2661
+ setEvents(eventsResult.data.data);
2662
+ }
2663
+ }).catch((err) => {
2664
+ console.error("Failed to fetch run or events:", err);
2665
+ }).finally(() => {
2666
+ setIsLoading(false);
2667
+ });
2668
+ }, [env, runId]);
2669
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
2670
+ RunActionsDropdownItems,
2671
+ {
2672
+ env,
2673
+ runId,
2674
+ runStatus: status,
2675
+ events,
2676
+ eventsLoading: isLoading,
2677
+ stopPropagation: true,
2678
+ callbacks: { onSuccess }
2679
+ }
2680
+ );
2681
+ }
2682
+ function LazyDropdownMenu({
2683
+ env,
2684
+ runId,
2685
+ runStatus,
2686
+ onSuccess
2687
+ }) {
2688
+ const [isOpen, setIsOpen] = reactExports.useState(false);
2689
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(DropdownMenu, { open: isOpen, onOpenChange: setIsOpen, children: [
2690
+ /* @__PURE__ */ jsxRuntimeExports.jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
2691
+ Button,
2692
+ {
2693
+ variant: "ghost",
2694
+ size: "icon",
2695
+ className: "h-8 w-8",
2696
+ onClick: (e) => e.stopPropagation(),
2697
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(Ellipsis, { className: "h-4 w-4" })
2698
+ }
2699
+ ) }),
2700
+ isOpen && /* @__PURE__ */ jsxRuntimeExports.jsx(DropdownMenuContent, { align: "end", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
2701
+ RunActionsDropdownContentInner,
2702
+ {
2703
+ env,
2704
+ runId,
2705
+ runStatus,
2706
+ onSuccess
2707
+ }
2708
+ ) })
2709
+ ] });
2710
+ }
2711
+ const statusMap = {
2712
+ pending: { label: "Pending", color: "bg-neutral-600 dark:bg-neutral-400" },
2713
+ running: { label: "Running", color: "bg-blue-600 dark:bg-blue-400" },
2714
+ completed: { label: "Completed", color: "bg-green-600 dark:bg-green-400" },
2715
+ failed: { label: "Failed", color: "bg-red-600 dark:bg-red-400" },
2716
+ cancelled: { label: "Cancelled", color: "bg-gray-600 dark:bg-gray-400" }
2717
+ };
2718
+ function useWorkflowFilter() {
2719
+ const navigate = useNavigate();
2720
+ const { pathname } = useLocation();
2721
+ const [searchParams] = useSearchParams();
2722
+ return reactExports.useCallback(
2723
+ (value) => {
2724
+ const params = new URLSearchParams(searchParams.toString());
2725
+ if (value === "all") {
2726
+ params.delete("workflow");
2727
+ params.delete("status");
2728
+ } else {
2729
+ params.set("workflow", value);
2730
+ }
2731
+ navigate(`${pathname}?${params.toString()}`);
2732
+ },
2733
+ [navigate, pathname, searchParams]
2734
+ );
2735
+ }
2736
+ function useStatusFilter() {
2737
+ const navigate = useNavigate();
2738
+ const { pathname } = useLocation();
2739
+ const [searchParams] = useSearchParams();
2740
+ return reactExports.useCallback(
2741
+ (value) => {
2742
+ const params = new URLSearchParams(searchParams.toString());
2743
+ if (value === "all") {
2744
+ params.delete("status");
2745
+ } else {
2746
+ params.set("status", value);
2747
+ }
2748
+ navigate(`${pathname}?${params.toString()}`);
2749
+ },
2750
+ [navigate, pathname, searchParams]
2751
+ );
2752
+ }
2753
+ function FilterControls({
2754
+ workflowNameFilter,
2755
+ status,
2756
+ seenWorkflowNames,
2757
+ sortOrder,
2758
+ loading,
2759
+ statusFilterRequiresWorkflowNameFilter,
2760
+ onWorkflowChange,
2761
+ onStatusChange,
2762
+ onSortToggle,
2763
+ onRefresh,
2764
+ lastRefreshTime
2765
+ }) {
2766
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [
2767
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-end gap-2", children: [
2768
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-muted-foreground", children: "Last refreshed" }),
2769
+ lastRefreshTime && /* @__PURE__ */ jsxRuntimeExports.jsx(
2770
+ RelativeTime,
2771
+ {
2772
+ date: lastRefreshTime,
2773
+ className: "text-sm text-muted-foreground",
2774
+ type: "distance"
2775
+ }
2776
+ )
2777
+ ] }),
2778
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-4", children: [
2779
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
2780
+ Select,
2781
+ {
2782
+ value: workflowNameFilter ?? "all",
2783
+ onValueChange: onWorkflowChange,
2784
+ disabled: loading,
2785
+ children: [
2786
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SelectTrigger, { className: "w-[180px] h-9", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SelectValue, { placeholder: "Filter by workflow" }) }),
2787
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(SelectContent, { children: [
2788
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: "all", children: "All Workflows" }),
2789
+ Array.from(seenWorkflowNames).sort().map((name) => {
2790
+ var _a;
2791
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: name, children: ((_a = parseWorkflowName(name)) == null ? void 0 : _a.shortName) || name }, name);
2792
+ })
2793
+ ] })
2794
+ ]
2795
+ }
2796
+ ),
2797
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
2798
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
2799
+ Select,
2800
+ {
2801
+ value: status || "all",
2802
+ onValueChange: onStatusChange,
2803
+ disabled: loading || statusFilterRequiresWorkflowNameFilter && !workflowNameFilter,
2804
+ children: [
2805
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SelectTrigger, { className: "w-[140px] h-9", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SelectValue, { placeholder: "Filter by status" }) }),
2806
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(SelectContent, { children: [
2807
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: "all", children: "Any status" }),
2808
+ Object.entries(statusMap).map(
2809
+ ([status2, { label, color }]) => /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: status2, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center", children: [
2810
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
2811
+ "span",
2812
+ {
2813
+ className: `${color} size-1.5 rounded-full mr-2`
2814
+ }
2815
+ ),
2816
+ label
2817
+ ] }) }, status2)
2818
+ )
2819
+ ] })
2820
+ ]
2821
+ }
2822
+ ) }) }),
2823
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: statusFilterRequiresWorkflowNameFilter && workflowNameFilter === "all" ? "Select a workflow first to filter by status" : "Filter runs by status" })
2824
+ ] }),
2825
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
2826
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
2827
+ Button,
2828
+ {
2829
+ variant: "outline",
2830
+ size: "sm",
2831
+ onClick: onSortToggle,
2832
+ disabled: loading,
2833
+ children: [
2834
+ sortOrder === "desc" ? /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowDownAZ, { className: "h-4 w-4" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowUpAZ, { className: "h-4 w-4" }),
2835
+ sortOrder === "desc" ? "Newest" : "Oldest"
2836
+ ]
2837
+ }
2838
+ ) }),
2839
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: sortOrder === "desc" ? "Showing newest first" : "Showing oldest first" })
2840
+ ] }),
2841
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
2842
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
2843
+ Button,
2844
+ {
2845
+ variant: "outline",
2846
+ size: "sm",
2847
+ onClick: onRefresh,
2848
+ disabled: loading,
2849
+ children: [
2850
+ /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: loading ? "animate-spin" : "" }),
2851
+ "Refresh"
2852
+ ]
2853
+ }
2854
+ ) }),
2855
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: "Note that this resets pages" })
2856
+ ] })
2857
+ ] })
2858
+ ] });
2859
+ }
2860
+ function RunsTable({ onRunClick }) {
2861
+ var _a, _b, _c, _d;
2862
+ const [searchParams] = useSearchParams();
2863
+ const handleWorkflowFilter = useWorkflowFilter();
2864
+ const handleStatusFilter = useStatusFilter();
2865
+ const { serverConfig } = useServerConfig();
2866
+ const rawStatus = searchParams.get("status");
2867
+ const validStatuses = Object.keys(statusMap);
2868
+ const status = rawStatus === "all" || rawStatus && validStatuses.includes(rawStatus) ? rawStatus : void 0;
2869
+ const workflowNameFilter = searchParams.get("workflow");
2870
+ const [sortOrder, setSortOrder] = reactExports.useState("desc");
2871
+ const [lastRefreshTime, setLastRefreshTime] = reactExports.useState(
2872
+ () => /* @__PURE__ */ new Date()
2873
+ );
2874
+ const [hasLoadedOnce, setHasLoadedOnce] = reactExports.useState(false);
2875
+ const env = reactExports.useMemo(() => ({}), []);
2876
+ const isLocal = serverConfig.backendId === "local" || serverConfig.backendId === "@workflow/world-local";
2877
+ const localDataDirPath = (_a = serverConfig.displayInfo) == null ? void 0 : _a["local.dataDirPath"];
2878
+ const localShortName = (_b = serverConfig.displayInfo) == null ? void 0 : _b["local.shortName"];
2879
+ const statusFilterRequiresWorkflowNameFilter = ((_c = serverConfig.backendId) == null ? void 0 : _c.includes("vercel")) || false;
2880
+ const [seenWorkflowNames, setSeenWorkflowNames] = reactExports.useState(
2881
+ /* @__PURE__ */ new Set()
2882
+ );
2883
+ const {
2884
+ data,
2885
+ error,
2886
+ nextPage,
2887
+ previousPage,
2888
+ hasNextPage,
2889
+ hasPreviousPage,
2890
+ reload,
2891
+ refresh,
2892
+ pageInfo
2893
+ } = useWorkflowRuns(env, {
2894
+ sortOrder,
2895
+ workflowName: workflowNameFilter === "all" ? void 0 : workflowNameFilter,
2896
+ status: status === "all" ? void 0 : status
2897
+ });
2898
+ const selection = useTableSelection({
2899
+ getItemId: (run) => run.runId
2900
+ });
2901
+ const runs = data.data ?? [];
2902
+ const [isBulkCancelling, setIsBulkCancelling] = reactExports.useState(false);
2903
+ const [isBulkReenqueuing, setIsBulkReenqueuing] = reactExports.useState(false);
2904
+ const isLocalAndHasMissingData = isLocal && (!localDataDirPath || !((_d = data == null ? void 0 : data.data) == null ? void 0 : _d.length));
2905
+ reactExports.useEffect(() => {
2906
+ if (data.data && data.data.length > 0) {
2907
+ const newNames = new Set(data.data.map((run) => run.workflowName));
2908
+ setSeenWorkflowNames((prev) => {
2909
+ const updated = new Set(prev);
2910
+ for (const name of newNames) {
2911
+ updated.add(name);
2912
+ }
2913
+ return updated;
2914
+ });
2915
+ }
2916
+ }, [data.data]);
2917
+ const loading = data.isLoading;
2918
+ reactExports.useEffect(() => {
2919
+ if (!loading && !hasLoadedOnce) {
2920
+ setHasLoadedOnce(true);
2921
+ }
2922
+ }, [loading, hasLoadedOnce]);
2923
+ reactExports.useEffect(() => {
2924
+ setHasLoadedOnce(false);
2925
+ }, [workflowNameFilter, status, sortOrder]);
2926
+ const onReload = reactExports.useCallback(() => {
2927
+ setLastRefreshTime(() => /* @__PURE__ */ new Date());
2928
+ setHasLoadedOnce(false);
2929
+ reload();
2930
+ }, [reload]);
2931
+ const onRefresh = reactExports.useCallback(() => {
2932
+ setLastRefreshTime(() => /* @__PURE__ */ new Date());
2933
+ refresh();
2934
+ }, [refresh]);
2935
+ const selectedRuns = reactExports.useMemo(() => {
2936
+ return runs.filter((run) => selection.selectedIds.has(run.runId));
2937
+ }, [runs, selection.selectedIds]);
2938
+ const cancellableSelectedRuns = reactExports.useMemo(() => {
2939
+ return selectedRuns.filter(
2940
+ (run) => run.status === "pending" || run.status === "running"
2941
+ );
2942
+ }, [selectedRuns]);
2943
+ const hasCancellableSelection = cancellableSelectedRuns.length > 0;
2944
+ const handleBulkCancel = reactExports.useCallback(async () => {
2945
+ if (isBulkCancelling || cancellableSelectedRuns.length === 0) return;
2946
+ setIsBulkCancelling(true);
2947
+ try {
2948
+ const results = await Promise.allSettled(
2949
+ cancellableSelectedRuns.map((run) => cancelRun(env, run.runId))
2950
+ );
2951
+ const succeeded = results.filter((r) => r.status === "fulfilled").length;
2952
+ const failed = results.filter((r) => r.status === "rejected").length;
2953
+ if (failed === 0) {
2954
+ toast.success(
2955
+ `Cancelled ${succeeded} run${succeeded !== 1 ? "s" : ""}`
2956
+ );
2957
+ } else if (succeeded === 0) {
2958
+ toast.error(`Failed to cancel ${failed} run${failed !== 1 ? "s" : ""}`);
2959
+ } else {
2960
+ toast.warning(
2961
+ `Cancelled ${succeeded} run${succeeded !== 1 ? "s" : ""}, ${failed} failed`
2962
+ );
2963
+ }
2964
+ selection.clearSelection();
2965
+ onReload();
2966
+ } catch (err) {
2967
+ toast.error("Failed to cancel runs", {
2968
+ description: err instanceof Error ? err.message : "Unknown error"
2969
+ });
2970
+ } finally {
2971
+ setIsBulkCancelling(false);
2972
+ }
2973
+ }, [env, cancellableSelectedRuns, isBulkCancelling, selection, onReload]);
2974
+ const handleBulkReenqueue = reactExports.useCallback(async () => {
2975
+ if (isBulkReenqueuing || selectedRuns.length === 0) return;
2976
+ setIsBulkReenqueuing(true);
2977
+ try {
2978
+ const results = await Promise.allSettled(
2979
+ selectedRuns.map((run) => reenqueueRun(env, run.runId))
2980
+ );
2981
+ const succeeded = results.filter((r) => r.status === "fulfilled").length;
2982
+ const failed = results.filter((r) => r.status === "rejected").length;
2983
+ if (failed === 0) {
2984
+ toast.success(
2985
+ `Re-enqueued ${succeeded} run${succeeded !== 1 ? "s" : ""}`
2986
+ );
2987
+ } else if (succeeded === 0) {
2988
+ toast.error(
2989
+ `Failed to re-enqueue ${failed} run${failed !== 1 ? "s" : ""}`
2990
+ );
2991
+ } else {
2992
+ toast.warning(
2993
+ `Re-enqueued ${succeeded} run${succeeded !== 1 ? "s" : ""}, ${failed} failed`
2994
+ );
2995
+ }
2996
+ selection.clearSelection();
2997
+ onReload();
2998
+ } catch (err) {
2999
+ toast.error("Failed to re-enqueue runs", {
3000
+ description: err instanceof Error ? err.message : "Unknown error"
3001
+ });
3002
+ } finally {
3003
+ setIsBulkReenqueuing(false);
3004
+ }
3005
+ }, [env, selectedRuns, isBulkReenqueuing, selection, onReload]);
3006
+ const toggleSortOrder = () => {
3007
+ setSortOrder((prev) => prev === "desc" ? "asc" : "desc");
3008
+ };
3009
+ reactExports.useEffect(() => {
3010
+ if (isLocalAndHasMissingData) {
3011
+ const interval = setInterval(() => {
3012
+ onRefresh();
3013
+ }, 5e3);
3014
+ return () => clearInterval(interval);
3015
+ }
3016
+ }, [isLocalAndHasMissingData, onRefresh]);
3017
+ reactExports.useEffect(() => {
3018
+ const handleVisibilityChange = () => {
3019
+ if (document.visibilityState === "visible" && lastRefreshTime) {
3020
+ const timeSinceLastRefresh = Date.now() - lastRefreshTime.getTime();
3021
+ if (timeSinceLastRefresh >= 1e4) {
3022
+ onReload();
3023
+ }
3024
+ }
3025
+ };
3026
+ document.addEventListener("visibilitychange", handleVisibilityChange);
3027
+ return () => {
3028
+ document.removeEventListener("visibilitychange", handleVisibilityChange);
3029
+ };
3030
+ }, [lastRefreshTime, onReload]);
3031
+ const localDirText = /* @__PURE__ */ jsxRuntimeExports.jsx("code", { className: "font-mono", children: localShortName || "current directory" });
3032
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
3033
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
3034
+ FilterControls,
3035
+ {
3036
+ workflowNameFilter,
3037
+ status,
3038
+ seenWorkflowNames,
3039
+ sortOrder,
3040
+ loading,
3041
+ statusFilterRequiresWorkflowNameFilter,
3042
+ onWorkflowChange: handleWorkflowFilter,
3043
+ onStatusChange: handleStatusFilter,
3044
+ onSortToggle: toggleSortOrder,
3045
+ onRefresh: onReload,
3046
+ lastRefreshTime
3047
+ }
3048
+ ),
3049
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Card, { className: "overflow-hidden mt-4 bg-background", children: /* @__PURE__ */ jsxRuntimeExports.jsx(CardContent, { className: "p-0 max-h-[calc(100vh-280px)] overflow-auto", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Table, { children: [
3050
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TableHeader, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(TableRow, { children: [
3051
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TableHead, { className: "sticky top-0 bg-background z-10 border-b shadow-sm h-10 w-10", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
3052
+ Checkbox,
3053
+ {
3054
+ checked: selection.isAllSelected(runs),
3055
+ indeterminate: selection.isSomeSelected(runs),
3056
+ onCheckedChange: () => selection.toggleSelectAll(runs),
3057
+ "aria-label": "Select all runs",
3058
+ disabled: !runs.length
3059
+ }
3060
+ ) }),
3061
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TableHead, { className: "sticky top-0 bg-background z-10 border-b shadow-sm h-10", children: "Workflow" }),
3062
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TableHead, { className: "sticky top-0 bg-background z-10 border-b shadow-sm h-10", children: "Run ID" }),
3063
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TableHead, { className: "sticky top-0 bg-background z-10 border-b shadow-sm h-10", children: "Status" }),
3064
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TableHead, { className: "sticky top-0 bg-background z-10 border-b shadow-sm h-10", children: "Started" }),
3065
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TableHead, { className: "sticky top-0 bg-background z-10 border-b shadow-sm h-10", children: "Completed" }),
3066
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TableHead, { className: "sticky top-0 bg-background z-10 border-b shadow-sm h-10 w-10" })
3067
+ ] }) }),
3068
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TableBody, { children: error ? /* @__PURE__ */ jsxRuntimeExports.jsx(TableRow, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(TableCell, { colSpan: 7, className: "h-[400px]", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center justify-center h-full", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Alert, { variant: "destructive", className: "max-w-md", children: [
3069
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { className: "h-4 w-4" }),
3070
+ /* @__PURE__ */ jsxRuntimeExports.jsx(AlertTitle, { children: "Error loading runs" }),
3071
+ /* @__PURE__ */ jsxRuntimeExports.jsx(AlertDescription, { children: getErrorMessage(error) })
3072
+ ] }) }) }) }) : loading && !hasLoadedOnce ? /* @__PURE__ */ jsxRuntimeExports.jsx(TableRow, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(TableCell, { colSpan: 7, className: "h-[400px]", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center justify-center h-full", children: /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "h-8 w-8 animate-spin text-muted-foreground" }) }) }) }) : !data.data || data.data.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx(TableRow, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(TableCell, { colSpan: 7, className: "h-[400px]", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-sm text-center text-muted-foreground flex flex-col items-center justify-center gap-3 h-full", children: [
3073
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-sm", children: [
3074
+ "No workflow runs found",
3075
+ isLocalAndHasMissingData ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
3076
+ " in ",
3077
+ localDirText
3078
+ ] }) : "",
3079
+ "."
3080
+ ] }),
3081
+ isLocalAndHasMissingData && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm flex items-center gap-2", children: "This view will update once you run a workflow." })
3082
+ ] }) }) }) : runs.map((run) => {
3083
+ var _a2;
3084
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
3085
+ TableRow,
3086
+ {
3087
+ className: "cursor-pointer group relative",
3088
+ onClick: () => onRunClick(run.runId),
3089
+ "data-selected": selection.isSelected(run),
3090
+ children: [
3091
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TableCell, { className: "py-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
3092
+ Checkbox,
3093
+ {
3094
+ checked: selection.isSelected(run),
3095
+ onCheckedChange: () => selection.toggleSelection(run),
3096
+ "aria-label": `Select run ${run.runId}`
3097
+ }
3098
+ ) }),
3099
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TableCell, { className: "py-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx(CopyableText, { text: run.workflowName, overlay: true, children: ((_a2 = parseWorkflowName(run.workflowName)) == null ? void 0 : _a2.shortName) || "?" }) }),
3100
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TableCell, { className: "font-mono text-xs py-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx(CopyableText, { text: run.runId, overlay: true, children: run.runId }) }),
3101
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TableCell, { className: "py-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
3102
+ StatusBadge,
3103
+ {
3104
+ status: run.status,
3105
+ context: run,
3106
+ durationMs: run.startedAt ? (run.completedAt ? new Date(run.completedAt).getTime() : Date.now()) - new Date(run.startedAt).getTime() : void 0
3107
+ }
3108
+ ) }),
3109
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TableCell, { className: "py-2 text-muted-foreground text-xs", children: run.startedAt ? /* @__PURE__ */ jsxRuntimeExports.jsx(RelativeTime, { date: run.startedAt }) : "-" }),
3110
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TableCell, { className: "py-2 text-muted-foreground text-xs", children: run.completedAt ? /* @__PURE__ */ jsxRuntimeExports.jsx(RelativeTime, { date: run.completedAt }) : "-" }),
3111
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TableCell, { className: "py-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
3112
+ LazyDropdownMenu,
3113
+ {
3114
+ env,
3115
+ runId: run.runId,
3116
+ runStatus: run.status,
3117
+ onSuccess: onReload
3118
+ }
3119
+ ) })
3120
+ ]
3121
+ },
3122
+ run.runId
3123
+ );
3124
+ }) })
3125
+ ] }) }) }),
3126
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between mt-4", children: [
3127
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-sm text-muted-foreground", children: pageInfo }),
3128
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2 items-center", children: [
3129
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
3130
+ Button,
3131
+ {
3132
+ variant: "outline",
3133
+ size: "sm",
3134
+ onClick: previousPage,
3135
+ disabled: !hasPreviousPage,
3136
+ children: [
3137
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronLeft, {}),
3138
+ "Previous"
3139
+ ]
3140
+ }
3141
+ ),
3142
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
3143
+ Button,
3144
+ {
3145
+ variant: "outline",
3146
+ size: "sm",
3147
+ onClick: nextPage,
3148
+ disabled: !hasNextPage,
3149
+ children: [
3150
+ "Next",
3151
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, {})
3152
+ ]
3153
+ }
3154
+ )
3155
+ ] })
3156
+ ] }),
3157
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
3158
+ SelectionBar,
3159
+ {
3160
+ selectionCount: selection.selectionCount,
3161
+ onClearSelection: selection.clearSelection,
3162
+ itemLabel: "runs",
3163
+ actions: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
3164
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
3165
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
3166
+ Button,
3167
+ {
3168
+ variant: "ghost",
3169
+ size: "sm",
3170
+ className: "h-7 text-primary-foreground hover:bg-primary-foreground/10 hover:text-primary-foreground",
3171
+ onClick: handleBulkReenqueue,
3172
+ disabled: isBulkReenqueuing || selectedRuns.length === 0,
3173
+ children: [
3174
+ isBulkReenqueuing ? /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "h-4 w-4 mr-1 animate-spin" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Zap, { className: "h-4 w-4 mr-1" }),
3175
+ isBulkReenqueuing ? "Re-enqueuing..." : "Re-enqueue"
3176
+ ]
3177
+ }
3178
+ ) }),
3179
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: "Re-enqueue the workflow orchestration layer for selected runs. Useful if workflows appear stuck." })
3180
+ ] }),
3181
+ hasCancellableSelection && /* @__PURE__ */ jsxRuntimeExports.jsxs(
3182
+ Button,
3183
+ {
3184
+ variant: "ghost",
3185
+ size: "sm",
3186
+ className: "h-7 text-primary-foreground hover:bg-primary-foreground/10 hover:text-primary-foreground",
3187
+ onClick: handleBulkCancel,
3188
+ disabled: isBulkCancelling,
3189
+ children: [
3190
+ isBulkCancelling ? /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "h-4 w-4 mr-1 animate-spin" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(CircleX, { className: "h-4 w-4 mr-1" }),
3191
+ "Cancel",
3192
+ " ",
3193
+ cancellableSelectedRuns.length !== selection.selectionCount ? `${cancellableSelectedRuns.length} ` : "",
3194
+ isBulkCancelling ? "cancelling..." : ""
3195
+ ]
3196
+ }
3197
+ )
3198
+ ] })
3199
+ }
3200
+ )
3201
+ ] });
3202
+ }
3203
+ const badgeVariants = cva(
3204
+ "inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
3205
+ {
3206
+ variants: {
3207
+ variant: {
3208
+ default: "border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80",
3209
+ secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
3210
+ destructive: "border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80",
3211
+ outline: "text-foreground"
3212
+ }
3213
+ },
3214
+ defaultVariants: {
3215
+ variant: "default"
3216
+ }
3217
+ }
3218
+ );
3219
+ function Badge({ className, variant, ...props }) {
3220
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: cn(badgeVariants({ variant }), className), ...props });
3221
+ }
3222
+ var DIALOG_NAME = "Dialog";
3223
+ var [createDialogContext] = createContextScope(DIALOG_NAME);
3224
+ var [DialogProvider, useDialogContext] = createDialogContext(DIALOG_NAME);
3225
+ var Dialog = (props) => {
3226
+ const {
3227
+ __scopeDialog,
3228
+ children,
3229
+ open: openProp,
3230
+ defaultOpen,
3231
+ onOpenChange,
3232
+ modal = true
3233
+ } = props;
3234
+ const triggerRef = reactExports.useRef(null);
3235
+ const contentRef = reactExports.useRef(null);
3236
+ const [open, setOpen] = useControllableState({
3237
+ prop: openProp,
3238
+ defaultProp: defaultOpen ?? false,
3239
+ onChange: onOpenChange,
3240
+ caller: DIALOG_NAME
3241
+ });
3242
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
3243
+ DialogProvider,
3244
+ {
3245
+ scope: __scopeDialog,
3246
+ triggerRef,
3247
+ contentRef,
3248
+ contentId: useId(),
3249
+ titleId: useId(),
3250
+ descriptionId: useId(),
3251
+ open,
3252
+ onOpenChange: setOpen,
3253
+ onOpenToggle: reactExports.useCallback(() => setOpen((prevOpen) => !prevOpen), [setOpen]),
3254
+ modal,
3255
+ children
3256
+ }
3257
+ );
3258
+ };
3259
+ Dialog.displayName = DIALOG_NAME;
3260
+ var TRIGGER_NAME = "DialogTrigger";
3261
+ var DialogTrigger = reactExports.forwardRef(
3262
+ (props, forwardedRef) => {
3263
+ const { __scopeDialog, ...triggerProps } = props;
3264
+ const context = useDialogContext(TRIGGER_NAME, __scopeDialog);
3265
+ const composedTriggerRef = useComposedRefs(forwardedRef, context.triggerRef);
3266
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
3267
+ Primitive.button,
3268
+ {
3269
+ type: "button",
3270
+ "aria-haspopup": "dialog",
3271
+ "aria-expanded": context.open,
3272
+ "aria-controls": context.contentId,
3273
+ "data-state": getState(context.open),
3274
+ ...triggerProps,
3275
+ ref: composedTriggerRef,
3276
+ onClick: composeEventHandlers(props.onClick, context.onOpenToggle)
3277
+ }
3278
+ );
3279
+ }
3280
+ );
3281
+ DialogTrigger.displayName = TRIGGER_NAME;
3282
+ var PORTAL_NAME = "DialogPortal";
3283
+ var [PortalProvider, usePortalContext] = createDialogContext(PORTAL_NAME, {
3284
+ forceMount: void 0
3285
+ });
3286
+ var DialogPortal = (props) => {
3287
+ const { __scopeDialog, forceMount, children, container } = props;
3288
+ const context = useDialogContext(PORTAL_NAME, __scopeDialog);
3289
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(PortalProvider, { scope: __scopeDialog, forceMount, children: reactExports.Children.map(children, (child) => /* @__PURE__ */ jsxRuntimeExports.jsx(Presence$1, { present: forceMount || context.open, children: /* @__PURE__ */ jsxRuntimeExports.jsx(Portal$2, { asChild: true, container, children: child }) })) });
3290
+ };
3291
+ DialogPortal.displayName = PORTAL_NAME;
3292
+ var OVERLAY_NAME = "DialogOverlay";
3293
+ var DialogOverlay = reactExports.forwardRef(
3294
+ (props, forwardedRef) => {
3295
+ const portalContext = usePortalContext(OVERLAY_NAME, props.__scopeDialog);
3296
+ const { forceMount = portalContext.forceMount, ...overlayProps } = props;
3297
+ const context = useDialogContext(OVERLAY_NAME, props.__scopeDialog);
3298
+ return context.modal ? /* @__PURE__ */ jsxRuntimeExports.jsx(Presence$1, { present: forceMount || context.open, children: /* @__PURE__ */ jsxRuntimeExports.jsx(DialogOverlayImpl, { ...overlayProps, ref: forwardedRef }) }) : null;
3299
+ }
3300
+ );
3301
+ DialogOverlay.displayName = OVERLAY_NAME;
3302
+ var Slot = createSlot("DialogOverlay.RemoveScroll");
3303
+ var DialogOverlayImpl = reactExports.forwardRef(
3304
+ (props, forwardedRef) => {
3305
+ const { __scopeDialog, ...overlayProps } = props;
3306
+ const context = useDialogContext(OVERLAY_NAME, __scopeDialog);
3307
+ return (
3308
+ // Make sure `Content` is scrollable even when it doesn't live inside `RemoveScroll`
3309
+ // ie. when `Overlay` and `Content` are siblings
3310
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ReactRemoveScroll, { as: Slot, allowPinchZoom: true, shards: [context.contentRef], children: /* @__PURE__ */ jsxRuntimeExports.jsx(
3311
+ Primitive.div,
3312
+ {
3313
+ "data-state": getState(context.open),
3314
+ ...overlayProps,
3315
+ ref: forwardedRef,
3316
+ style: { pointerEvents: "auto", ...overlayProps.style }
3317
+ }
3318
+ ) })
3319
+ );
3320
+ }
3321
+ );
3322
+ var CONTENT_NAME = "DialogContent";
3323
+ var DialogContent = reactExports.forwardRef(
3324
+ (props, forwardedRef) => {
3325
+ const portalContext = usePortalContext(CONTENT_NAME, props.__scopeDialog);
3326
+ const { forceMount = portalContext.forceMount, ...contentProps } = props;
3327
+ const context = useDialogContext(CONTENT_NAME, props.__scopeDialog);
3328
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Presence$1, { present: forceMount || context.open, children: context.modal ? /* @__PURE__ */ jsxRuntimeExports.jsx(DialogContentModal, { ...contentProps, ref: forwardedRef }) : /* @__PURE__ */ jsxRuntimeExports.jsx(DialogContentNonModal, { ...contentProps, ref: forwardedRef }) });
3329
+ }
3330
+ );
3331
+ DialogContent.displayName = CONTENT_NAME;
3332
+ var DialogContentModal = reactExports.forwardRef(
3333
+ (props, forwardedRef) => {
3334
+ const context = useDialogContext(CONTENT_NAME, props.__scopeDialog);
3335
+ const contentRef = reactExports.useRef(null);
3336
+ const composedRefs = useComposedRefs(forwardedRef, context.contentRef, contentRef);
3337
+ reactExports.useEffect(() => {
3338
+ const content = contentRef.current;
3339
+ if (content) return hideOthers(content);
3340
+ }, []);
3341
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
3342
+ DialogContentImpl,
3343
+ {
3344
+ ...props,
3345
+ ref: composedRefs,
3346
+ trapFocus: context.open,
3347
+ disableOutsidePointerEvents: true,
3348
+ onCloseAutoFocus: composeEventHandlers(props.onCloseAutoFocus, (event) => {
3349
+ var _a;
3350
+ event.preventDefault();
3351
+ (_a = context.triggerRef.current) == null ? void 0 : _a.focus();
3352
+ }),
3353
+ onPointerDownOutside: composeEventHandlers(props.onPointerDownOutside, (event) => {
3354
+ const originalEvent = event.detail.originalEvent;
3355
+ const ctrlLeftClick = originalEvent.button === 0 && originalEvent.ctrlKey === true;
3356
+ const isRightClick = originalEvent.button === 2 || ctrlLeftClick;
3357
+ if (isRightClick) event.preventDefault();
3358
+ }),
3359
+ onFocusOutside: composeEventHandlers(
3360
+ props.onFocusOutside,
3361
+ (event) => event.preventDefault()
3362
+ )
3363
+ }
3364
+ );
3365
+ }
3366
+ );
3367
+ var DialogContentNonModal = reactExports.forwardRef(
3368
+ (props, forwardedRef) => {
3369
+ const context = useDialogContext(CONTENT_NAME, props.__scopeDialog);
3370
+ const hasInteractedOutsideRef = reactExports.useRef(false);
3371
+ const hasPointerDownOutsideRef = reactExports.useRef(false);
3372
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
3373
+ DialogContentImpl,
3374
+ {
3375
+ ...props,
3376
+ ref: forwardedRef,
3377
+ trapFocus: false,
3378
+ disableOutsidePointerEvents: false,
3379
+ onCloseAutoFocus: (event) => {
3380
+ var _a, _b;
3381
+ (_a = props.onCloseAutoFocus) == null ? void 0 : _a.call(props, event);
3382
+ if (!event.defaultPrevented) {
3383
+ if (!hasInteractedOutsideRef.current) (_b = context.triggerRef.current) == null ? void 0 : _b.focus();
3384
+ event.preventDefault();
3385
+ }
3386
+ hasInteractedOutsideRef.current = false;
3387
+ hasPointerDownOutsideRef.current = false;
3388
+ },
3389
+ onInteractOutside: (event) => {
3390
+ var _a, _b;
3391
+ (_a = props.onInteractOutside) == null ? void 0 : _a.call(props, event);
3392
+ if (!event.defaultPrevented) {
3393
+ hasInteractedOutsideRef.current = true;
3394
+ if (event.detail.originalEvent.type === "pointerdown") {
3395
+ hasPointerDownOutsideRef.current = true;
3396
+ }
3397
+ }
3398
+ const target = event.target;
3399
+ const targetIsTrigger = (_b = context.triggerRef.current) == null ? void 0 : _b.contains(target);
3400
+ if (targetIsTrigger) event.preventDefault();
3401
+ if (event.detail.originalEvent.type === "focusin" && hasPointerDownOutsideRef.current) {
3402
+ event.preventDefault();
3403
+ }
3404
+ }
3405
+ }
3406
+ );
3407
+ }
3408
+ );
3409
+ var DialogContentImpl = reactExports.forwardRef(
3410
+ (props, forwardedRef) => {
3411
+ const { __scopeDialog, trapFocus, onOpenAutoFocus, onCloseAutoFocus, ...contentProps } = props;
3412
+ const context = useDialogContext(CONTENT_NAME, __scopeDialog);
3413
+ const contentRef = reactExports.useRef(null);
3414
+ const composedRefs = useComposedRefs(forwardedRef, contentRef);
3415
+ useFocusGuards();
3416
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
3417
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
3418
+ FocusScope,
3419
+ {
3420
+ asChild: true,
3421
+ loop: true,
3422
+ trapped: trapFocus,
3423
+ onMountAutoFocus: onOpenAutoFocus,
3424
+ onUnmountAutoFocus: onCloseAutoFocus,
3425
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(
3426
+ DismissableLayer,
3427
+ {
3428
+ role: "dialog",
3429
+ id: context.contentId,
3430
+ "aria-describedby": context.descriptionId,
3431
+ "aria-labelledby": context.titleId,
3432
+ "data-state": getState(context.open),
3433
+ ...contentProps,
3434
+ ref: composedRefs,
3435
+ onDismiss: () => context.onOpenChange(false)
3436
+ }
3437
+ )
3438
+ }
3439
+ ),
3440
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
3441
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TitleWarning, { titleId: context.titleId }),
3442
+ /* @__PURE__ */ jsxRuntimeExports.jsx(DescriptionWarning, { contentRef, descriptionId: context.descriptionId })
3443
+ ] })
3444
+ ] });
3445
+ }
3446
+ );
3447
+ var TITLE_NAME = "DialogTitle";
3448
+ var DialogTitle = reactExports.forwardRef(
3449
+ (props, forwardedRef) => {
3450
+ const { __scopeDialog, ...titleProps } = props;
3451
+ const context = useDialogContext(TITLE_NAME, __scopeDialog);
3452
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Primitive.h2, { id: context.titleId, ...titleProps, ref: forwardedRef });
3453
+ }
3454
+ );
3455
+ DialogTitle.displayName = TITLE_NAME;
3456
+ var DESCRIPTION_NAME = "DialogDescription";
3457
+ var DialogDescription = reactExports.forwardRef(
3458
+ (props, forwardedRef) => {
3459
+ const { __scopeDialog, ...descriptionProps } = props;
3460
+ const context = useDialogContext(DESCRIPTION_NAME, __scopeDialog);
3461
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Primitive.p, { id: context.descriptionId, ...descriptionProps, ref: forwardedRef });
3462
+ }
3463
+ );
3464
+ DialogDescription.displayName = DESCRIPTION_NAME;
3465
+ var CLOSE_NAME = "DialogClose";
3466
+ var DialogClose = reactExports.forwardRef(
3467
+ (props, forwardedRef) => {
3468
+ const { __scopeDialog, ...closeProps } = props;
3469
+ const context = useDialogContext(CLOSE_NAME, __scopeDialog);
3470
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
3471
+ Primitive.button,
3472
+ {
3473
+ type: "button",
3474
+ ...closeProps,
3475
+ ref: forwardedRef,
3476
+ onClick: composeEventHandlers(props.onClick, () => context.onOpenChange(false))
3477
+ }
3478
+ );
3479
+ }
3480
+ );
3481
+ DialogClose.displayName = CLOSE_NAME;
3482
+ function getState(open) {
3483
+ return open ? "open" : "closed";
3484
+ }
3485
+ var TITLE_WARNING_NAME = "DialogTitleWarning";
3486
+ var [WarningProvider, useWarningContext] = createContext2(TITLE_WARNING_NAME, {
3487
+ contentName: CONTENT_NAME,
3488
+ titleName: TITLE_NAME,
3489
+ docsSlug: "dialog"
3490
+ });
3491
+ var TitleWarning = ({ titleId }) => {
3492
+ const titleWarningContext = useWarningContext(TITLE_WARNING_NAME);
3493
+ const MESSAGE = `\`${titleWarningContext.contentName}\` requires a \`${titleWarningContext.titleName}\` for the component to be accessible for screen reader users.
3494
+
3495
+ If you want to hide the \`${titleWarningContext.titleName}\`, you can wrap it with our VisuallyHidden component.
3496
+
3497
+ For more information, see https://radix-ui.com/primitives/docs/components/${titleWarningContext.docsSlug}`;
3498
+ reactExports.useEffect(() => {
3499
+ if (titleId) {
3500
+ const hasTitle = document.getElementById(titleId);
3501
+ if (!hasTitle) console.error(MESSAGE);
3502
+ }
3503
+ }, [MESSAGE, titleId]);
3504
+ return null;
3505
+ };
3506
+ var DESCRIPTION_WARNING_NAME = "DialogDescriptionWarning";
3507
+ var DescriptionWarning = ({ contentRef, descriptionId }) => {
3508
+ const descriptionWarningContext = useWarningContext(DESCRIPTION_WARNING_NAME);
3509
+ const MESSAGE = `Warning: Missing \`Description\` or \`aria-describedby={undefined}\` for {${descriptionWarningContext.contentName}}.`;
3510
+ reactExports.useEffect(() => {
3511
+ var _a;
3512
+ const describedById = (_a = contentRef.current) == null ? void 0 : _a.getAttribute("aria-describedby");
3513
+ if (descriptionId && describedById) {
3514
+ const hasDescription = document.getElementById(descriptionId);
3515
+ if (!hasDescription) console.warn(MESSAGE);
3516
+ }
3517
+ }, [MESSAGE, contentRef, descriptionId]);
3518
+ return null;
3519
+ };
3520
+ var Root = Dialog;
3521
+ var Portal = DialogPortal;
3522
+ var Overlay = DialogOverlay;
3523
+ var Content = DialogContent;
3524
+ var Title = DialogTitle;
3525
+ var Description = DialogDescription;
3526
+ var Close = DialogClose;
3527
+ function Sheet({ ...props }) {
3528
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Root, { "data-slot": "sheet", ...props });
3529
+ }
3530
+ function SheetPortal({
3531
+ ...props
3532
+ }) {
3533
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Portal, { "data-slot": "sheet-portal", ...props });
3534
+ }
3535
+ function SheetOverlay({
3536
+ className,
3537
+ ...props
3538
+ }) {
3539
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
3540
+ Overlay,
3541
+ {
3542
+ "data-slot": "sheet-overlay",
3543
+ className: cn(
3544
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
3545
+ className
3546
+ ),
3547
+ ...props
3548
+ }
3549
+ );
3550
+ }
3551
+ function SheetContent({
3552
+ className,
3553
+ children,
3554
+ side = "right",
3555
+ ...props
3556
+ }) {
3557
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(SheetPortal, { children: [
3558
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SheetOverlay, {}),
3559
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
3560
+ Content,
3561
+ {
3562
+ "data-slot": "sheet-content",
3563
+ onSubmit: (e) => e.preventDefault(),
3564
+ className: cn(
3565
+ "bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
3566
+ side === "right" && "data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm",
3567
+ side === "left" && "data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm",
3568
+ side === "top" && "data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b",
3569
+ side === "bottom" && "data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 h-auto border-t",
3570
+ className
3571
+ ),
3572
+ ...props,
3573
+ children: [
3574
+ children,
3575
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(Close, { className: "ring-offset-background focus:ring-ring data-[state=open]:bg-secondary absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none", children: [
3576
+ /* @__PURE__ */ jsxRuntimeExports.jsx(X, { className: "size-4" }),
3577
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sr-only", children: "Close" })
3578
+ ] })
3579
+ ]
3580
+ }
3581
+ )
3582
+ ] });
3583
+ }
3584
+ function SheetHeader({ className, ...props }) {
3585
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
3586
+ "div",
3587
+ {
3588
+ "data-slot": "sheet-header",
3589
+ className: cn("flex flex-col gap-1.5 p-4", className),
3590
+ ...props
3591
+ }
3592
+ );
3593
+ }
3594
+ function SheetTitle({
3595
+ className,
3596
+ ...props
3597
+ }) {
3598
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
3599
+ Title,
3600
+ {
3601
+ "data-slot": "sheet-title",
3602
+ className: cn("text-foreground font-semibold", className),
3603
+ ...props
3604
+ }
3605
+ );
3606
+ }
3607
+ function SheetDescription({
3608
+ className,
3609
+ ...props
3610
+ }) {
3611
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
3612
+ Description,
3613
+ {
3614
+ "data-slot": "sheet-description",
3615
+ className: cn("text-muted-foreground text-sm", className),
3616
+ ...props
3617
+ }
3618
+ );
3619
+ }
3620
+ const LAYOUT = {
3621
+ HORIZONTAL_SPACING: 280,
3622
+ VERTICAL_SPACING: 320,
3623
+ START_X: 250,
3624
+ START_Y: 50
3625
+ };
3626
+ function calculateNodePositions(rawNodes, edges) {
3627
+ if (rawNodes.length === 0) return [];
3628
+ const outgoing = /* @__PURE__ */ new Map();
3629
+ const incoming = /* @__PURE__ */ new Map();
3630
+ for (const edge of edges) {
3631
+ const out = outgoing.get(edge.source) || [];
3632
+ out.push(edge.target);
3633
+ outgoing.set(edge.source, out);
3634
+ const inc = incoming.get(edge.target) || [];
3635
+ inc.push(edge.source);
3636
+ incoming.set(edge.target, inc);
3637
+ }
3638
+ const startNode = rawNodes.find((n) => n.data.nodeKind === "workflow_start");
3639
+ if (!startNode) {
3640
+ return rawNodes.map((node, idx) => ({
3641
+ id: node.id,
3642
+ type: node.type,
3643
+ data: {
3644
+ label: node.data.label,
3645
+ nodeKind: node.data.nodeKind,
3646
+ stepId: node.data.stepId
3647
+ },
3648
+ metadata: node.metadata,
3649
+ position: {
3650
+ x: LAYOUT.START_X,
3651
+ y: LAYOUT.START_Y + idx * LAYOUT.VERTICAL_SPACING
3652
+ }
3653
+ }));
3654
+ }
3655
+ const layers = /* @__PURE__ */ new Map();
3656
+ const visited = /* @__PURE__ */ new Set();
3657
+ const queue = [startNode.id];
3658
+ layers.set(startNode.id, 0);
3659
+ while (queue.length > 0) {
3660
+ const nodeId = queue.shift();
3661
+ if (visited.has(nodeId)) continue;
3662
+ visited.add(nodeId);
3663
+ const currentLayer = layers.get(nodeId);
3664
+ const targets = outgoing.get(nodeId) || [];
3665
+ for (const target of targets) {
3666
+ if (target === nodeId || visited.has(target)) continue;
3667
+ if (!layers.has(target)) {
3668
+ layers.set(target, currentLayer + 1);
3669
+ queue.push(target);
3670
+ } else {
3671
+ const existingLayer = layers.get(target);
3672
+ if (currentLayer + 1 > existingLayer) {
3673
+ layers.set(target, currentLayer + 1);
3674
+ if (!queue.includes(target)) {
3675
+ queue.push(target);
3676
+ }
3677
+ }
3678
+ }
3679
+ }
3680
+ }
3681
+ let maxLayer = Math.max(...Array.from(layers.values()), 0);
3682
+ for (const node of rawNodes) {
3683
+ if (!layers.has(node.id)) {
3684
+ maxLayer++;
3685
+ layers.set(node.id, maxLayer);
3686
+ }
3687
+ }
3688
+ const nodesByLayer = /* @__PURE__ */ new Map();
3689
+ for (const node of rawNodes) {
3690
+ const layer = layers.get(node.id) ?? 0;
3691
+ const layerNodes = nodesByLayer.get(layer) || [];
3692
+ layerNodes.push(node);
3693
+ nodesByLayer.set(layer, layerNodes);
3694
+ }
3695
+ const positioned = [];
3696
+ for (const node of rawNodes) {
3697
+ const layer = layers.get(node.id) ?? 0;
3698
+ const layerNodes = nodesByLayer.get(layer) || [node];
3699
+ const indexInLayer = layerNodes.findIndex((n) => n.id === node.id);
3700
+ const layerWidth = layerNodes.length;
3701
+ const totalWidth = (layerWidth - 1) * LAYOUT.HORIZONTAL_SPACING;
3702
+ const startX = LAYOUT.START_X - totalWidth / 2;
3703
+ positioned.push({
3704
+ id: node.id,
3705
+ type: node.type,
3706
+ data: {
3707
+ label: node.data.label,
3708
+ nodeKind: node.data.nodeKind,
3709
+ stepId: node.data.stepId
3710
+ },
3711
+ metadata: node.metadata,
3712
+ position: {
3713
+ x: startX + indexInLayer * LAYOUT.HORIZONTAL_SPACING,
3714
+ y: LAYOUT.START_Y + layer * LAYOUT.VERTICAL_SPACING
3715
+ }
3716
+ });
3717
+ }
3718
+ return positioned;
3719
+ }
3720
+ function adaptManifest(raw) {
3721
+ const workflows = {};
3722
+ console.log("[adaptManifest] Raw manifest version:", raw == null ? void 0 : raw.version);
3723
+ console.log(
3724
+ "[adaptManifest] Raw workflows keys:",
3725
+ Object.keys((raw == null ? void 0 : raw.workflows) || {})
3726
+ );
3727
+ if (!(raw == null ? void 0 : raw.workflows)) {
3728
+ console.log("[adaptManifest] No workflows in manifest, returning empty");
3729
+ return { version: (raw == null ? void 0 : raw.version) || "1.0.0", workflows: {} };
3730
+ }
3731
+ for (const [filePath, workflowsInFile] of Object.entries(raw.workflows)) {
3732
+ for (const [workflowName, entry] of Object.entries(workflowsInFile)) {
3733
+ const positionedNodes = calculateNodePositions(
3734
+ entry.graph.nodes,
3735
+ entry.graph.edges
3736
+ );
3737
+ const workflowGraph = {
3738
+ workflowId: entry.workflowId,
3739
+ workflowName,
3740
+ filePath,
3741
+ nodes: positionedNodes,
3742
+ edges: entry.graph.edges
3743
+ };
3744
+ workflows[entry.workflowId] = workflowGraph;
3745
+ }
3746
+ }
3747
+ console.log(
3748
+ "[adaptManifest] Adapted workflows count:",
3749
+ Object.keys(workflows).length
3750
+ );
3751
+ return {
3752
+ version: raw.version,
3753
+ workflows
3754
+ };
3755
+ }
3756
+ function useWorkflowGraphManifest() {
3757
+ const [manifest, setManifest] = reactExports.useState(null);
3758
+ const [loading, setLoading] = reactExports.useState(true);
3759
+ const [error, setError] = reactExports.useState(null);
3760
+ const isFetchingRef = reactExports.useRef(false);
3761
+ const env = reactExports.useMemo(() => ({}), []);
3762
+ const fetchManifest = reactExports.useCallback(async () => {
3763
+ if (isFetchingRef.current) {
3764
+ return;
3765
+ }
3766
+ isFetchingRef.current = true;
3767
+ setLoading(true);
3768
+ setError(null);
3769
+ try {
3770
+ console.log("[useWorkflowGraphManifest] Fetching with env:", env);
3771
+ const { result: rawManifest, error: error2 } = await unwrapServerActionResult(
3772
+ fetchWorkflowsManifest(env)
3773
+ );
3774
+ if (error2) {
3775
+ setError(error2);
3776
+ return;
3777
+ }
3778
+ console.log(
3779
+ "[useWorkflowGraphManifest] Raw manifest after unwrap:",
3780
+ rawManifest
3781
+ );
3782
+ console.log(
3783
+ "[useWorkflowGraphManifest] Workflows in raw:",
3784
+ Object.keys((rawManifest == null ? void 0 : rawManifest.workflows) || {})
3785
+ );
3786
+ const adaptedManifest = adaptManifest(rawManifest);
3787
+ console.log(
3788
+ "[useWorkflowGraphManifest] Adapted manifest workflows:",
3789
+ Object.keys(adaptedManifest.workflows)
3790
+ );
3791
+ setManifest(adaptedManifest);
3792
+ } catch (err) {
3793
+ const error2 = err instanceof WorkflowWebAPIError ? err : err instanceof Error ? new WorkflowWebAPIError(err.message, {
3794
+ cause: err,
3795
+ layer: "client"
3796
+ }) : new WorkflowWebAPIError(String(err), { layer: "client" });
3797
+ setError(error2);
3798
+ setManifest(null);
3799
+ } finally {
3800
+ setLoading(false);
3801
+ isFetchingRef.current = false;
3802
+ }
3803
+ }, [env]);
3804
+ reactExports.useEffect(() => {
3805
+ fetchManifest();
3806
+ }, [fetchManifest]);
3807
+ return {
3808
+ manifest,
3809
+ loading,
3810
+ error,
3811
+ refetch: fetchManifest
3812
+ };
3813
+ }
3814
+ function WorkflowsList({ onWorkflowSelect }) {
3815
+ const [sheetOpen, setSheetOpen] = reactExports.useState(false);
3816
+ const [selectedWorkflow, setSelectedWorkflow] = reactExports.useState(null);
3817
+ const {
3818
+ manifest: graphManifest,
3819
+ loading,
3820
+ error: graphError
3821
+ } = useWorkflowGraphManifest();
3822
+ const workflows = graphManifest ? Object.values(graphManifest.workflows) : [];
3823
+ const sortedWorkflows = reactExports.useMemo(
3824
+ () => [...workflows].sort(
3825
+ (a, b) => a.workflowName.localeCompare(b.workflowName)
3826
+ ),
3827
+ [workflows]
3828
+ );
3829
+ const selectedWorkflowStepCount = reactExports.useMemo(
3830
+ () => (selectedWorkflow == null ? void 0 : selectedWorkflow.nodes.filter((node) => node.data.nodeKind === "step").length) ?? 0,
3831
+ [selectedWorkflow]
3832
+ );
3833
+ const handleViewWorkflow = (workflow) => {
3834
+ setSelectedWorkflow(workflow);
3835
+ setSheetOpen(true);
3836
+ onWorkflowSelect == null ? void 0 : onWorkflowSelect(workflow.workflowName);
3837
+ };
3838
+ if (loading) {
3839
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(TableSkeleton, { variant: "workflows", rows: 6 });
3840
+ }
3841
+ if (graphError) {
3842
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(Alert, { variant: "destructive", children: [
3843
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { className: "h-4 w-4" }),
3844
+ /* @__PURE__ */ jsxRuntimeExports.jsx(AlertTitle, { children: "Error Loading Workflows" }),
3845
+ /* @__PURE__ */ jsxRuntimeExports.jsx(AlertDescription, { children: graphError.message })
3846
+ ] });
3847
+ }
3848
+ if (workflows.length === 0) {
3849
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Card, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(CardContent, { className: "p-12 text-center", children: [
3850
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Workflow, { className: "mx-auto h-12 w-12 text-muted-foreground mb-4" }),
3851
+ /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-lg font-semibold mb-2", children: "No Workflows Found" }),
3852
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-muted-foreground", children: "No workflow definitions were found in the graph manifest." })
3853
+ ] }) });
3854
+ }
3855
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
3856
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Card, { className: "overflow-hidden bg-background", children: /* @__PURE__ */ jsxRuntimeExports.jsx(CardContent, { className: "p-0 max-h-[calc(100vh-200px)] overflow-auto", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Table, { children: [
3857
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TableHeader, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(TableRow, { children: [
3858
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TableHead, { className: "sticky top-0 bg-background z-10 border-b shadow-sm h-10", children: "Workflow" }),
3859
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TableHead, { className: "sticky top-0 bg-background z-10 border-b shadow-sm h-10", children: "File" }),
3860
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TableHead, { className: "text-center sticky top-0 bg-background z-10 border-b shadow-sm h-10", children: "Steps" })
3861
+ ] }) }),
3862
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TableBody, { children: sortedWorkflows.map((workflow) => {
3863
+ const stepCount = workflow.nodes.filter(
3864
+ (node) => node.data.nodeKind === "step"
3865
+ ).length;
3866
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
3867
+ TableRow,
3868
+ {
3869
+ className: "cursor-pointer",
3870
+ onClick: () => handleViewWorkflow(workflow),
3871
+ children: [
3872
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TableCell, { className: "py-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-medium", children: workflow.workflowName }) }),
3873
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TableCell, { className: "py-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx("code", { className: "text-xs text-muted-foreground", children: workflow.filePath }) }),
3874
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TableCell, { className: "text-center py-2", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Badge, { variant: "secondary", className: "gap-1", children: [
3875
+ /* @__PURE__ */ jsxRuntimeExports.jsx(GitBranch, { className: "h-3 w-3" }),
3876
+ stepCount,
3877
+ " ",
3878
+ pluralize("step", "steps", stepCount)
3879
+ ] }) })
3880
+ ]
3881
+ },
3882
+ workflow.workflowId
3883
+ );
3884
+ }) })
3885
+ ] }) }) }),
3886
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Sheet, { open: sheetOpen, onOpenChange: setSheetOpen, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
3887
+ SheetContent,
3888
+ {
3889
+ side: "right",
3890
+ className: "w-[75vw] max-w-[75vw] sm:max-w-[75vw]",
3891
+ children: [
3892
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(SheetHeader, { children: [
3893
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(SheetTitle, { className: "flex items-center gap-2", children: [
3894
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Workflow, { className: "h-5 w-5" }),
3895
+ selectedWorkflow == null ? void 0 : selectedWorkflow.workflowName
3896
+ ] }),
3897
+ selectedWorkflow && /* @__PURE__ */ jsxRuntimeExports.jsx(SheetDescription, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
3898
+ /* @__PURE__ */ jsxRuntimeExports.jsx("code", { className: "text-xs", children: selectedWorkflow.filePath }),
3899
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Badge, { variant: "outline", className: "gap-1", children: [
3900
+ /* @__PURE__ */ jsxRuntimeExports.jsx(GitBranch, { className: "h-3 w-3" }),
3901
+ selectedWorkflowStepCount,
3902
+ " ",
3903
+ pluralize("step", "steps", selectedWorkflowStepCount)
3904
+ ] }) })
3905
+ ] }) })
3906
+ ] }),
3907
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-6 h-[calc(100vh-180px)]", children: selectedWorkflow && /* @__PURE__ */ jsxRuntimeExports.jsx(WorkflowGraphViewer, { workflow: selectedWorkflow }) })
3908
+ ]
3909
+ }
3910
+ ) })
3911
+ ] });
3912
+ }
3913
+ const home = withComponentProps(function Home() {
3914
+ const navigate = useNavigate();
3915
+ const {
3916
+ serverConfig
3917
+ } = useServerConfig();
3918
+ const [searchParams, setSearchParams] = useSearchParams();
3919
+ const sidebar = searchParams.get("sidebar");
3920
+ const hookId = searchParams.get("hookId");
3921
+ const tab = searchParams.get("tab") || "runs";
3922
+ const setTab = (value) => {
3923
+ setSearchParams((prev) => {
3924
+ prev.set("tab", value);
3925
+ return prev;
3926
+ }, {
3927
+ replace: true
3928
+ });
3929
+ };
3930
+ const selectedHookId = sidebar === "hook" && hookId ? hookId : void 0;
3931
+ const isLocalBackend = serverConfig.backendId === "local" || serverConfig.backendId === "@workflow/world-local";
3932
+ const handleRunClick = (runId, streamId) => {
3933
+ if (!streamId) {
3934
+ navigate(`/run/${runId}`);
3935
+ } else {
3936
+ navigate(`/run/${runId}/streams/${streamId}`);
3937
+ }
3938
+ };
3939
+ const handleHookSelect = (hookId2, runId) => {
3940
+ if (hookId2) {
3941
+ navigate(`/run/${runId}?sidebar=hook&hookId=${hookId2}`);
3942
+ } else {
3943
+ navigate(`/run/${runId}`);
3944
+ }
3945
+ };
3946
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", {
3947
+ className: "max-w-7xl mx-auto px-4",
3948
+ children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Tabs, {
3949
+ value: tab,
3950
+ onValueChange: setTab,
3951
+ className: "w-full",
3952
+ children: [/* @__PURE__ */ jsxRuntimeExports.jsxs(TabsList, {
3953
+ className: "mb-4",
3954
+ children: [/* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, {
3955
+ value: "runs",
3956
+ children: "Runs"
3957
+ }), /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, {
3958
+ value: "hooks",
3959
+ children: "Hooks"
3960
+ }), isLocalBackend && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, {
3961
+ value: "workflows",
3962
+ children: "Workflows"
3963
+ })]
3964
+ }), /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, {
3965
+ value: "runs",
3966
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(ErrorBoundary, {
3967
+ title: "Failed to load workflow runs",
3968
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(RunsTable, {
3969
+ onRunClick: handleRunClick
3970
+ })
3971
+ })
3972
+ }), /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, {
3973
+ value: "hooks",
3974
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(ErrorBoundary, {
3975
+ title: "Failed to load hooks",
3976
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(HooksTable, {
3977
+ onHookClick: handleHookSelect,
3978
+ selectedHookId
3979
+ })
3980
+ })
3981
+ }), isLocalBackend && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, {
3982
+ value: "workflows",
3983
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(ErrorBoundary, {
3984
+ title: "Failed to load workflow graph data",
3985
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(WorkflowsList, {})
3986
+ })
3987
+ })]
3988
+ })
3989
+ });
3990
+ });
3991
+ export {
3992
+ home as default
3993
+ };