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

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