@tangle-network/sandbox-ui 0.14.0 → 0.15.1

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 (104) hide show
  1. package/dist/auth.d.ts +1 -74
  2. package/dist/auth.js +1 -4
  3. package/dist/chat.d.ts +1 -136
  4. package/dist/chat.js +2 -15
  5. package/dist/chunk-2BUPSB7O.js +0 -0
  6. package/dist/chunk-3J6FG3FJ.js +18 -0
  7. package/dist/chunk-76IQLPW2.js +206 -0
  8. package/dist/chunk-7ZA5SEK3.js +239 -0
  9. package/dist/chunk-AHBZCBDO.js +2960 -0
  10. package/dist/chunk-AZ3AWMTM.js +8 -0
  11. package/dist/chunk-CMY7W45U.js +380 -0
  12. package/dist/{chunk-QMU2PWOU.js → chunk-DNZ4DTNA.js} +71 -17
  13. package/dist/chunk-EI44GEQ5.js +6 -0
  14. package/dist/{chunk-5OQ27N57.js → chunk-GPT7VKK6.js} +34 -38
  15. package/dist/chunk-JBGKGLD7.js +16 -0
  16. package/dist/chunk-NJNME4J4.js +14 -0
  17. package/dist/chunk-QPAJR74X.js +20 -0
  18. package/dist/chunk-TK46XFLM.js +28 -0
  19. package/dist/chunk-WID73FPH.js +89 -0
  20. package/dist/chunk-YVXK4XRO.js +30 -0
  21. package/dist/dashboard.d.ts +538 -4
  22. package/dist/dashboard.js +15 -886
  23. package/dist/editor.d.ts +1 -120
  24. package/dist/editor.js +1 -5
  25. package/dist/files.d.ts +1 -129
  26. package/dist/files.js +2 -7
  27. package/dist/globals.css +2 -1265
  28. package/dist/hooks.d.ts +114 -11
  29. package/dist/hooks.js +17 -88
  30. package/dist/index.d.ts +24 -99
  31. package/dist/index.js +247 -252
  32. package/dist/markdown.d.ts +1 -29
  33. package/dist/markdown.js +2 -2
  34. package/dist/openui.d.ts +8 -115
  35. package/dist/openui.js +1 -6
  36. package/dist/pages.d.ts +1 -2
  37. package/dist/pages.js +68 -66
  38. package/dist/primitives.d.ts +14 -49
  39. package/dist/primitives.js +69 -77
  40. package/dist/run.d.ts +1 -14
  41. package/dist/run.js +2 -22
  42. package/dist/sdk-hooks.d.ts +3 -283
  43. package/dist/sdk-hooks.js +10 -14
  44. package/dist/stores.d.ts +2 -14
  45. package/dist/stores.js +11 -39
  46. package/dist/styles.css +2 -1265
  47. package/dist/{usage-chart-CPTcNlGs.d.ts → template-card-UhV3pmRC.d.ts} +16 -1
  48. package/dist/types.d.ts +11 -8
  49. package/dist/types.js +1 -0
  50. package/dist/utils.d.ts +1 -44
  51. package/dist/utils.js +6 -12
  52. package/dist/workspace.d.ts +5 -10
  53. package/dist/workspace.js +3 -19
  54. package/package.json +19 -54
  55. package/dist/active-sessions-store-CeOmXgv5.d.ts +0 -85
  56. package/dist/artifact-pane-Bh45Ssco.d.ts +0 -24
  57. package/dist/branding-DCi5VEik.d.ts +0 -13
  58. package/dist/button-CMQuQEW_.d.ts +0 -17
  59. package/dist/chat-container-f4yEs6KN.d.ts +0 -106
  60. package/dist/chunk-34A66VBG.js +0 -214
  61. package/dist/chunk-34I7UFSX.js +0 -92
  62. package/dist/chunk-36QY2W5G.js +0 -802
  63. package/dist/chunk-4CLN43XT.js +0 -45
  64. package/dist/chunk-54SQQMMM.js +0 -156
  65. package/dist/chunk-66EZOYZR.js +0 -102
  66. package/dist/chunk-BX6AQMUS.js +0 -183
  67. package/dist/chunk-DI3NZ5ZX.js +0 -192
  68. package/dist/chunk-DPGIXDAI.js +0 -220
  69. package/dist/chunk-DXMIEK4K.js +0 -1426
  70. package/dist/chunk-GSZA3TSY.js +0 -79
  71. package/dist/chunk-HB5Y37YU.js +0 -54
  72. package/dist/chunk-LQNEZDRM.js +0 -109
  73. package/dist/chunk-MA7YKRUP.js +0 -131
  74. package/dist/chunk-MKTSMWVD.js +0 -109
  75. package/dist/chunk-MQXABZTB.js +0 -1348
  76. package/dist/chunk-MT5FJ3ZT.js +0 -186
  77. package/dist/chunk-NKUPJC34.js +0 -2070
  78. package/dist/chunk-OEX7NZE3.js +0 -321
  79. package/dist/chunk-OKLQVY3Y.js +0 -139
  80. package/dist/chunk-Q56BYXQF.js +0 -61
  81. package/dist/chunk-QD4QE5P5.js +0 -40
  82. package/dist/chunk-QDH5GEGY.js +0 -630
  83. package/dist/chunk-QID2OOMG.js +0 -133
  84. package/dist/chunk-RQHJBTEU.js +0 -10
  85. package/dist/chunk-T7HMZEVO.js +0 -216
  86. package/dist/chunk-U6QTHMY6.js +0 -1290
  87. package/dist/chunk-US6JKJKH.js +0 -124
  88. package/dist/chunk-VX3XOUEB.js +0 -63
  89. package/dist/chunk-XLG757B6.js +0 -933
  90. package/dist/chunk-ZMNSRDMH.js +0 -127
  91. package/dist/chunk-ZNCEM5CD.js +0 -316
  92. package/dist/document-editor-pane-A70-EhdQ.d.ts +0 -124
  93. package/dist/document-editor-pane-TLPVRBBU.js +0 -11
  94. package/dist/expanded-tool-detail-Dh99mcbY.d.ts +0 -63
  95. package/dist/file-tabs-BLfxfmAH.d.ts +0 -51
  96. package/dist/parts-CyGkM6Fp.d.ts +0 -50
  97. package/dist/run-CtFZ6s-D.d.ts +0 -41
  98. package/dist/sidebar-drop-zone-tDBsuOH5.d.ts +0 -301
  99. package/dist/sidecar-CFU2W9j1.d.ts +0 -8
  100. package/dist/template-card-BAtvcAkU.d.ts +0 -18
  101. package/dist/tool-call-feed-Bs3MyQMT.d.ts +0 -68
  102. package/dist/tool-display-Ct9nFAzJ.d.ts +0 -32
  103. package/dist/use-sandbox-metrics-DWc0k9Xm.d.ts +0 -153
  104. package/dist/variant-list-BrHYcBCk.d.ts +0 -540
@@ -0,0 +1,2960 @@
1
+ import {
2
+ Logo
3
+ } from "./chunk-7ZA5SEK3.js";
4
+ import {
5
+ cn
6
+ } from "./chunk-EI44GEQ5.js";
7
+
8
+ // src/dashboard/app-sidebar.tsx
9
+ import { Avatar, AvatarFallback, AvatarImage } from "@tangle-network/ui/primitives";
10
+ import {
11
+ DropdownMenu,
12
+ DropdownMenuContent,
13
+ DropdownMenuItem,
14
+ DropdownMenuLabel,
15
+ DropdownMenuSeparator,
16
+ DropdownMenuTrigger
17
+ } from "@tangle-network/ui/primitives";
18
+ import { Skeleton } from "@tangle-network/ui/primitives";
19
+
20
+ // src/dashboard/sidebar-context.tsx
21
+ import * as React from "react";
22
+ import { jsx } from "react/jsx-runtime";
23
+ var PANEL_OPEN_KEY = "sandbox-sidebar-panel-open";
24
+ var SIDEBAR_MODE_KEY = "sandbox-sidebar-mode";
25
+ var SIDEBAR_RAIL_WIDTH = 64;
26
+ var SIDEBAR_PANEL_WIDTH = 260;
27
+ var SIDEBAR_TOTAL_WIDTH = SIDEBAR_RAIL_WIDTH + SIDEBAR_PANEL_WIDTH;
28
+ var SIDEBAR_MOBILE_WIDTH = 256;
29
+ var SidebarContext = React.createContext(null);
30
+ function readStorage(key, fallback) {
31
+ if (typeof window === "undefined") return fallback;
32
+ try {
33
+ return localStorage.getItem(key) ?? fallback;
34
+ } catch {
35
+ return fallback;
36
+ }
37
+ }
38
+ function writeStorage(key, value) {
39
+ if (typeof window === "undefined") return;
40
+ try {
41
+ localStorage.setItem(key, value);
42
+ } catch {
43
+ }
44
+ }
45
+ function SidebarProvider({
46
+ defaultPanelOpen = true,
47
+ defaultMode = "projects",
48
+ hasPanels = true,
49
+ children
50
+ }) {
51
+ const [panelOpen, setPanelOpenState] = React.useState(
52
+ () => readStorage(PANEL_OPEN_KEY, String(defaultPanelOpen)) === "true"
53
+ );
54
+ const [mode, setModeState] = React.useState(
55
+ () => readStorage(SIDEBAR_MODE_KEY, defaultMode)
56
+ );
57
+ const [hidden, setHidden] = React.useState(false);
58
+ const setPanelOpen = React.useCallback((open) => {
59
+ setPanelOpenState(open);
60
+ writeStorage(PANEL_OPEN_KEY, String(open));
61
+ }, []);
62
+ const togglePanel = React.useCallback(() => {
63
+ setPanelOpenState((prev) => {
64
+ const next = !prev;
65
+ writeStorage(PANEL_OPEN_KEY, String(next));
66
+ return next;
67
+ });
68
+ }, []);
69
+ const setMode = React.useCallback((m) => {
70
+ setModeState(m);
71
+ writeStorage(SIDEBAR_MODE_KEY, m);
72
+ }, []);
73
+ const switchModeStable = React.useCallback((m) => {
74
+ setModeState((prevMode) => {
75
+ if (prevMode === m) {
76
+ setPanelOpenState((prevOpen) => {
77
+ const next = !prevOpen;
78
+ writeStorage(PANEL_OPEN_KEY, String(next));
79
+ return next;
80
+ });
81
+ return prevMode;
82
+ }
83
+ setPanelOpenState(() => {
84
+ writeStorage(PANEL_OPEN_KEY, "true");
85
+ return true;
86
+ });
87
+ writeStorage(SIDEBAR_MODE_KEY, m);
88
+ return m;
89
+ });
90
+ }, []);
91
+ const contentMargin = hidden ? 0 : panelOpen && hasPanels ? SIDEBAR_TOTAL_WIDTH : SIDEBAR_RAIL_WIDTH;
92
+ const value = React.useMemo(
93
+ () => ({
94
+ panelOpen,
95
+ setPanelOpen,
96
+ togglePanel,
97
+ mode,
98
+ setMode,
99
+ switchMode: switchModeStable,
100
+ hidden,
101
+ setHidden,
102
+ contentMargin,
103
+ hasPanels
104
+ }),
105
+ [panelOpen, setPanelOpen, togglePanel, mode, setMode, switchModeStable, hidden, setHidden, contentMargin, hasPanels]
106
+ );
107
+ return /* @__PURE__ */ jsx(SidebarContext.Provider, { value, children });
108
+ }
109
+ function useSidebar() {
110
+ const ctx = React.useContext(SidebarContext);
111
+ if (!ctx) throw new Error("useSidebar must be used within a SidebarProvider");
112
+ return ctx;
113
+ }
114
+
115
+ // src/dashboard/app-sidebar.tsx
116
+ import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
117
+ function DefaultLink({
118
+ href,
119
+ to,
120
+ className,
121
+ children,
122
+ ...rest
123
+ }) {
124
+ return /* @__PURE__ */ jsx2("a", { href: href ?? to, className, ...rest, children });
125
+ }
126
+ function getInitials(name, email) {
127
+ if (name) return name.split(" ").map((w) => w[0]).filter(Boolean).slice(0, 2).join("").toUpperCase();
128
+ if (email) return email[0].toUpperCase();
129
+ return "?";
130
+ }
131
+ function SettingsIcon({ className }) {
132
+ return /* @__PURE__ */ jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, children: [
133
+ /* @__PURE__ */ jsx2("title", { children: "Settings icon" }),
134
+ /* @__PURE__ */ jsx2("path", { d: "M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z" }),
135
+ /* @__PURE__ */ jsx2("circle", { cx: "12", cy: "12", r: "3" })
136
+ ] });
137
+ }
138
+ function LogOutIcon({ className }) {
139
+ return /* @__PURE__ */ jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, children: [
140
+ /* @__PURE__ */ jsx2("title", { children: "Log out icon" }),
141
+ /* @__PURE__ */ jsx2("path", { d: "M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" }),
142
+ /* @__PURE__ */ jsx2("polyline", { points: "16,17 21,12 16,7" }),
143
+ /* @__PURE__ */ jsx2("line", { x1: "21", x2: "9", y1: "12", y2: "12" })
144
+ ] });
145
+ }
146
+ function Sidebar({ children, className, style }) {
147
+ const { panelOpen, hidden, hasPanels } = useSidebar();
148
+ return /* @__PURE__ */ jsx2(
149
+ "div",
150
+ {
151
+ "data-sidebar": "true",
152
+ className: cn(
153
+ "fixed inset-y-0 left-0 z-40 flex bg-card border-r border-border transition-[transform,width] duration-200 ease-in-out",
154
+ hidden && "-translate-x-full",
155
+ className
156
+ ),
157
+ style: { width: panelOpen && hasPanels ? SIDEBAR_TOTAL_WIDTH : SIDEBAR_RAIL_WIDTH, ...style },
158
+ children
159
+ }
160
+ );
161
+ }
162
+ function SidebarRail({ children, className, wide = false }) {
163
+ return /* @__PURE__ */ jsx2(
164
+ "div",
165
+ {
166
+ className: cn(
167
+ "flex flex-col h-full shrink-0 bg-transparent",
168
+ wide ? "w-full" : "w-16",
169
+ className
170
+ ),
171
+ children
172
+ }
173
+ );
174
+ }
175
+ function SidebarRailHeader({ children, className }) {
176
+ return /* @__PURE__ */ jsx2("div", { className: cn("flex h-14 items-center justify-center border-b border-border", className), children });
177
+ }
178
+ function SidebarRailNav({ children, className }) {
179
+ return /* @__PURE__ */ jsx2("nav", { className: cn("flex flex-col items-center gap-1 py-3 flex-1", className), children });
180
+ }
181
+ function SidebarRailFooter({ children, className }) {
182
+ return /* @__PURE__ */ jsx2("div", { className: cn("flex flex-col items-center gap-1 pb-3", className), children });
183
+ }
184
+ function RailSeparator({ className }) {
185
+ return /* @__PURE__ */ jsx2("div", { className: cn("my-2 h-px w-10 bg-[var(--md3-outline-variant)]", className) });
186
+ }
187
+ function RailButton({ icon: Icon2, label, isActive, badge, onClick, className, showLabel }) {
188
+ return /* @__PURE__ */ jsxs(
189
+ "button",
190
+ {
191
+ type: "button",
192
+ onClick,
193
+ title: label,
194
+ className: cn(
195
+ "group relative flex items-center justify-center rounded-xl transition-all duration-200",
196
+ showLabel ? "w-full justify-start px-3 h-11 gap-3" : "w-11 h-11 justify-center",
197
+ "hover:bg-[var(--accent-surface-soft)] hover:text-[var(--accent-text)]",
198
+ "active:scale-95",
199
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary",
200
+ isActive && "bg-[var(--accent-surface-strong)] text-[var(--accent-text)]",
201
+ !isActive && "text-muted-foreground",
202
+ className
203
+ ),
204
+ children: [
205
+ /* @__PURE__ */ jsx2(Icon2, { className: "h-5 w-5 shrink-0" }),
206
+ showLabel && /* @__PURE__ */ jsx2("span", { className: "text-sm font-medium", children: label }),
207
+ badge !== void 0 && badge > 0 && /* @__PURE__ */ jsx2("span", { className: "absolute -top-1 -right-1 flex h-4 min-w-4 items-center justify-center rounded-full bg-primary text-[9px] font-bold text-[var(--md3-on-primary)] px-1 shadow-sm", children: badge > 99 ? "99+" : badge })
208
+ ]
209
+ }
210
+ );
211
+ }
212
+ function RailModeButton({ mode, icon, label, badge, className, showLabel }) {
213
+ const { panelOpen, mode: currentMode, switchMode } = useSidebar();
214
+ return /* @__PURE__ */ jsx2(
215
+ RailButton,
216
+ {
217
+ icon,
218
+ label,
219
+ isActive: mode === currentMode && panelOpen,
220
+ badge,
221
+ onClick: () => switchMode(mode),
222
+ className,
223
+ showLabel
224
+ }
225
+ );
226
+ }
227
+ function SidebarPanel({ children, className }) {
228
+ const { panelOpen } = useSidebar();
229
+ return /* @__PURE__ */ jsx2(
230
+ "div",
231
+ {
232
+ className: cn(
233
+ "transition-[opacity] duration-150 h-full overflow-hidden border-l border-border bg-card",
234
+ panelOpen ? "w-[260px] opacity-100" : "w-0 opacity-0 pointer-events-none",
235
+ className
236
+ ),
237
+ children: /* @__PURE__ */ jsx2("div", { className: "flex flex-col h-full w-[260px]", children })
238
+ }
239
+ );
240
+ }
241
+ function SidebarPanelHeader({ children, title, className }) {
242
+ return /* @__PURE__ */ jsx2("div", { className: cn("flex h-14 items-center px-4 border-b border-border shrink-0", className), children: children ?? /* @__PURE__ */ jsx2("h2", { className: "text-sm font-semibold text-foreground", children: title }) });
243
+ }
244
+ function SidebarPanelContent({ children, className }) {
245
+ return /* @__PURE__ */ jsx2("div", { className: cn("flex-1 overflow-y-auto px-2 py-2", className), children });
246
+ }
247
+ function SidebarContent({ children, className }) {
248
+ const { contentMargin } = useSidebar();
249
+ return /* @__PURE__ */ jsx2(
250
+ "main",
251
+ {
252
+ className: cn(
253
+ "min-h-screen transition-[margin-left] duration-200 ease-in-out lg:ml-[var(--sb-content-margin,0px)]",
254
+ className
255
+ ),
256
+ style: { "--sb-content-margin": `${contentMargin}px` },
257
+ children
258
+ }
259
+ );
260
+ }
261
+ function ProfileAvatar({
262
+ user,
263
+ isLoading = false,
264
+ onLogout,
265
+ onSettingsClick,
266
+ settingsHref = "/dashboard/settings",
267
+ children,
268
+ className,
269
+ LinkComponent
270
+ }) {
271
+ const Link = LinkComponent ?? DefaultLink;
272
+ return /* @__PURE__ */ jsxs(DropdownMenu, { children: [
273
+ /* @__PURE__ */ jsx2(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx2(
274
+ "button",
275
+ {
276
+ type: "button",
277
+ className: cn(
278
+ "flex items-center justify-center w-12 h-12 rounded-lg transition-colors hover:bg-[var(--accent-surface-soft)]",
279
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary",
280
+ className
281
+ ),
282
+ "aria-label": "User menu",
283
+ children: isLoading ? /* @__PURE__ */ jsx2(Skeleton, { className: "h-7 w-7 rounded-full" }) : /* @__PURE__ */ jsxs(Avatar, { className: "h-7 w-7", children: [
284
+ user?.avatarUrl && /* @__PURE__ */ jsx2(AvatarImage, { src: user.avatarUrl, alt: "" }),
285
+ /* @__PURE__ */ jsx2(AvatarFallback, { className: "text-[10px] bg-violet-500/20 text-violet-300", children: getInitials(user?.name, user?.email) })
286
+ ] })
287
+ }
288
+ ) }),
289
+ /* @__PURE__ */ jsxs(DropdownMenuContent, { side: "right", align: "end", sideOffset: 8, className: "w-72", children: [
290
+ /* @__PURE__ */ jsx2(DropdownMenuLabel, { className: "p-0 font-normal", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 px-2 py-3", children: [
291
+ /* @__PURE__ */ jsxs(Avatar, { className: "h-12 w-12 shrink-0", children: [
292
+ user?.avatarUrl && /* @__PURE__ */ jsx2(AvatarImage, { src: user.avatarUrl, alt: "" }),
293
+ /* @__PURE__ */ jsx2(AvatarFallback, { className: "text-sm bg-[var(--surface-violet-bg)] text-[var(--surface-violet-text)]", children: getInitials(user?.name, user?.email) })
294
+ ] }),
295
+ /* @__PURE__ */ jsx2("div", { className: "min-w-0 flex-1", children: isLoading ? /* @__PURE__ */ jsxs(Fragment, { children: [
296
+ /* @__PURE__ */ jsx2(Skeleton, { className: "h-4 w-24 mb-1" }),
297
+ /* @__PURE__ */ jsx2(Skeleton, { className: "h-3 w-32" })
298
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
299
+ /* @__PURE__ */ jsx2("p", { className: "text-sm font-semibold truncate", children: user?.name ?? user?.email ?? "Not logged in" }),
300
+ user?.email && user?.name && /* @__PURE__ */ jsx2("p", { className: "text-xs text-muted-foreground truncate", children: user.email }),
301
+ user?.tier && /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground capitalize", children: [
302
+ user.tier,
303
+ " Plan"
304
+ ] })
305
+ ] }) })
306
+ ] }) }),
307
+ /* @__PURE__ */ jsx2(DropdownMenuSeparator, {}),
308
+ children,
309
+ onSettingsClick ? /* @__PURE__ */ jsxs(DropdownMenuItem, { onClick: onSettingsClick, children: [
310
+ /* @__PURE__ */ jsx2(SettingsIcon, { className: "mr-2 h-4 w-4", "aria-hidden": "true" }),
311
+ "Settings"
312
+ ] }) : /* @__PURE__ */ jsx2(DropdownMenuItem, { asChild: true, children: /* @__PURE__ */ jsxs(Link, { href: settingsHref, to: settingsHref, className: "flex items-center", children: [
313
+ /* @__PURE__ */ jsx2(SettingsIcon, { className: "mr-2 h-4 w-4", "aria-hidden": "true" }),
314
+ "Settings"
315
+ ] }) }),
316
+ onLogout && /* @__PURE__ */ jsxs(Fragment, { children: [
317
+ /* @__PURE__ */ jsx2(DropdownMenuSeparator, {}),
318
+ /* @__PURE__ */ jsxs(DropdownMenuItem, { className: "text-[var(--surface-danger-text)]", onClick: onLogout, children: [
319
+ /* @__PURE__ */ jsx2(LogOutIcon, { className: "mr-2 h-4 w-4", "aria-hidden": "true" }),
320
+ "Sign Out"
321
+ ] })
322
+ ] })
323
+ ] })
324
+ ] });
325
+ }
326
+
327
+ // src/dashboard/credit-balance.tsx
328
+ import * as React2 from "react";
329
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
330
+ function CreditBalance({
331
+ amount,
332
+ description = "Credits are automatically deducted based on hourly Sandbox usage and Agent operations.",
333
+ onTopUp,
334
+ quickAmounts = [10, 25, 100],
335
+ className
336
+ }) {
337
+ const [topUpValue, setTopUpValue] = React2.useState("50.00");
338
+ return /* @__PURE__ */ jsxs2(
339
+ "div",
340
+ {
341
+ className: cn(
342
+ "bg-card p-5 rounded-xl flex flex-col justify-between border border-border",
343
+ className
344
+ ),
345
+ children: [
346
+ /* @__PURE__ */ jsxs2("div", { children: [
347
+ /* @__PURE__ */ jsx3("h3", { className: "text-sm font-semibold text-muted-foreground uppercase tracking-widest mb-2", children: "Available Credits" }),
348
+ /* @__PURE__ */ jsxs2("div", { className: "text-4xl font-extrabold text-primary tracking-tighter mb-2", children: [
349
+ "$",
350
+ amount.toFixed(2)
351
+ ] }),
352
+ /* @__PURE__ */ jsx3("p", { className: "text-sm text-muted-foreground leading-relaxed", children: description })
353
+ ] }),
354
+ onTopUp && /* @__PURE__ */ jsxs2("div", { className: "space-y-2.5 mt-5", children: [
355
+ /* @__PURE__ */ jsxs2("div", { className: "bg-card border border-border p-1 rounded-lg flex items-center", children: [
356
+ /* @__PURE__ */ jsx3(
357
+ "input",
358
+ {
359
+ type: "text",
360
+ value: `$${topUpValue}`,
361
+ onChange: (e) => {
362
+ const raw = e.target.value.replace(/[^0-9.]/g, "");
363
+ setTopUpValue(raw.match(/^(\d*\.?\d{0,2})/)?.[1] ?? "");
364
+ },
365
+ className: "bg-transparent border-none text-foreground font-mono text-lg w-full focus:ring-0 px-4 outline-none"
366
+ }
367
+ ),
368
+ /* @__PURE__ */ jsx3(
369
+ "button",
370
+ {
371
+ type: "button",
372
+ onClick: () => {
373
+ const parsed = Number.parseFloat(topUpValue);
374
+ if (!Number.isFinite(parsed) || parsed <= 0) return;
375
+ onTopUp(parsed);
376
+ },
377
+ className: "bg-[var(--accent-surface-soft)] border border-border text-[var(--accent-text)] px-6 py-3 rounded-md font-bold text-xs uppercase tracking-widest active:scale-95 transition-transform hover:bg-[var(--accent-surface-strong)]",
378
+ children: "Top Up"
379
+ }
380
+ )
381
+ ] }),
382
+ /* @__PURE__ */ jsx3("div", { className: "flex justify-between gap-2", children: quickAmounts.filter((qa) => Number.isFinite(qa) && qa > 0).map((qa) => /* @__PURE__ */ jsxs2(
383
+ "button",
384
+ {
385
+ type: "button",
386
+ onClick: () => {
387
+ const rounded = parseFloat(qa.toFixed(2));
388
+ setTopUpValue(qa.toFixed(2));
389
+ onTopUp(rounded);
390
+ },
391
+ className: "flex-1 py-2 text-[10px] font-mono text-muted-foreground border border-border rounded-md hover:bg-muted/50 hover:text-foreground transition-colors uppercase",
392
+ children: [
393
+ "+$",
394
+ qa
395
+ ]
396
+ },
397
+ qa
398
+ )) })
399
+ ] })
400
+ ]
401
+ }
402
+ );
403
+ }
404
+
405
+ // src/dashboard/cluster-status-bar.tsx
406
+ import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
407
+ function ClusterStatusBar({ items, latency, className }) {
408
+ if (!items?.length) return null;
409
+ return /* @__PURE__ */ jsx4("div", { className: cn("fixed bottom-6 left-1/2 -translate-x-1/2 z-40 w-max max-w-[90vw] animate-in slide-in-from-bottom flex justify-center", className), children: /* @__PURE__ */ jsxs3("div", { className: "overflow-hidden rounded-full border border-border bg-card px-6 py-3 flex flex-wrap sm:flex-nowrap items-center justify-between gap-8 shadow-sm", children: [
410
+ /* @__PURE__ */ jsx4("div", { className: "flex items-center gap-6", children: items.map((item, i) => /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2", children: [
411
+ /* @__PURE__ */ jsx4("span", { className: "text-[var(--md3-primary)] [&_svg]:h-[18px] [&_svg]:w-[18px]", children: item.icon }),
412
+ /* @__PURE__ */ jsxs3("div", { className: "flex flex-col", children: [
413
+ /* @__PURE__ */ jsx4("span", { className: "text-[10px] text-[var(--md3-on-surface-variant)] uppercase tracking-wider font-bold", children: item.label }),
414
+ /* @__PURE__ */ jsx4("span", { className: cn("text-xs font-bold text-foreground", item.valueClass), children: item.value })
415
+ ] })
416
+ ] }, i)) }),
417
+ latency && /* @__PURE__ */ jsxs3(Fragment2, { children: [
418
+ /* @__PURE__ */ jsx4("div", { className: "h-6 w-px bg-border" }),
419
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-3", children: [
420
+ /* @__PURE__ */ jsxs3("div", { className: "relative flex h-2.5 w-2.5 items-center justify-center", children: [
421
+ /* @__PURE__ */ jsx4("span", { className: "absolute inline-flex h-full w-full animate-ping rounded-full bg-green-400 opacity-75" }),
422
+ /* @__PURE__ */ jsx4("span", { className: "relative inline-flex h-2 w-2 rounded-full bg-green-500" })
423
+ ] }),
424
+ /* @__PURE__ */ jsx4("span", { className: "text-xs font-mono text-green-400 font-bold", children: latency })
425
+ ] })
426
+ ] })
427
+ ] }) });
428
+ }
429
+
430
+ // src/dashboard/resource-meter.tsx
431
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
432
+ function getBarColor(percent) {
433
+ if (percent >= 90) return "bg-[var(--code-error)]";
434
+ if (percent >= 70) return "bg-[var(--surface-warning-text)]";
435
+ return "bg-primary";
436
+ }
437
+ function ResourceMeter({ label, value, max = 100, unit, icon, className }) {
438
+ const percent = max > 0 ? Math.min(value / max * 100, 100) : 0;
439
+ const barColor = getBarColor(percent);
440
+ return /* @__PURE__ */ jsxs4("div", { className: cn("flex items-center gap-3", className), children: [
441
+ /* @__PURE__ */ jsxs4("span", { className: "flex shrink-0 items-center gap-1 text-[10px] font-mono text-muted-foreground uppercase tracking-wide", children: [
442
+ icon,
443
+ label
444
+ ] }),
445
+ /* @__PURE__ */ jsx5("div", { className: "h-1.5 min-w-0 flex-1 bg-card rounded-full overflow-hidden", children: /* @__PURE__ */ jsx5(
446
+ "div",
447
+ {
448
+ className: cn("h-full rounded-full transition-all duration-500", barColor),
449
+ style: { width: `${percent}%` }
450
+ }
451
+ ) }),
452
+ /* @__PURE__ */ jsx5("span", { className: "shrink-0 text-[10px] font-mono tabular-nums text-muted-foreground", children: unit ? `${value}${unit}/${max}${unit}` : `${Math.round(percent)}%` })
453
+ ] });
454
+ }
455
+
456
+ // src/dashboard/sandbox-card.tsx
457
+ import {
458
+ DropdownMenu as DropdownMenu2,
459
+ DropdownMenuContent as DropdownMenuContent2,
460
+ DropdownMenuItem as DropdownMenuItem2,
461
+ DropdownMenuSeparator as DropdownMenuSeparator2,
462
+ DropdownMenuTrigger as DropdownMenuTrigger2
463
+ } from "@tangle-network/ui/primitives";
464
+ import {
465
+ MoreVertical,
466
+ PowerOff,
467
+ Power,
468
+ Copy,
469
+ Clock,
470
+ Activity,
471
+ BarChart2,
472
+ Trash2,
473
+ Terminal,
474
+ Code2,
475
+ Network,
476
+ Play,
477
+ Plus,
478
+ Users
479
+ } from "lucide-react";
480
+ import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
481
+ function canAdminSandbox(sandbox) {
482
+ if (!sandbox.team) return true;
483
+ return sandbox.team.role === "owner" || sandbox.team.role === "admin";
484
+ }
485
+ function SandboxCard({
486
+ sandbox,
487
+ onOpenIDE,
488
+ onOpenTerminal,
489
+ onWake,
490
+ onRestore,
491
+ onDelete,
492
+ onStop,
493
+ onResume,
494
+ onFork,
495
+ onKeepAlive,
496
+ onUsage,
497
+ onHealth,
498
+ className
499
+ }) {
500
+ const isRunning = sandbox.status === "running";
501
+ const isTransitioning = sandbox.status === "provisioning" || sandbox.status === "creating";
502
+ const isStopped = !isRunning && !isTransitioning;
503
+ return /* @__PURE__ */ jsxs5("div", { className: cn(
504
+ "group relative flex flex-col justify-between overflow-hidden rounded-lg border bg-card p-5 transition-colors",
505
+ isRunning ? "border-[var(--status-running)]/30" : "border-border",
506
+ "hover:border-foreground/15",
507
+ className
508
+ ), children: [
509
+ /* @__PURE__ */ jsxs5("div", { className: "flex items-start justify-between", children: [
510
+ /* @__PURE__ */ jsxs5("div", { children: [
511
+ /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-2", children: [
512
+ /* @__PURE__ */ jsxs5("h3", { className: "flex items-center gap-2 text-sm font-bold text-foreground", children: [
513
+ sandbox.name,
514
+ isRunning && /* @__PURE__ */ jsxs5("span", { className: "relative flex h-2 w-2", children: [
515
+ /* @__PURE__ */ jsx6("span", { className: "absolute inline-flex h-full w-full animate-pulse rounded-full bg-[var(--status-running)] opacity-75" }),
516
+ /* @__PURE__ */ jsx6("span", { className: "relative inline-flex h-1.5 w-1.5 rounded-full bg-[var(--status-running)]" })
517
+ ] })
518
+ ] }),
519
+ sandbox.team && /* @__PURE__ */ jsxs5(
520
+ "span",
521
+ {
522
+ className: "inline-flex items-center gap-1 rounded-full bg-[var(--accent-surface-soft)] px-2 py-0.5 text-[10px] font-semibold text-[var(--accent-text)]",
523
+ title: `Shared with ${sandbox.team.name ?? "Team"} \xB7 ${sandbox.team.role}`,
524
+ children: [
525
+ /* @__PURE__ */ jsx6(Users, { className: "h-3 w-3", "aria-hidden": "true" }),
526
+ sandbox.team.name ?? "Team"
527
+ ]
528
+ }
529
+ )
530
+ ] }),
531
+ /* @__PURE__ */ jsxs5("p", { className: "mt-0.5 font-mono text-[10px] tracking-wider text-muted-foreground uppercase", children: [
532
+ sandbox.nodeId || "Unknown Node",
533
+ sandbox.team && /* @__PURE__ */ jsxs5("span", { className: "ml-2 normal-case tracking-normal", children: [
534
+ "\xB7 your role: ",
535
+ sandbox.team.role
536
+ ] })
537
+ ] })
538
+ ] }),
539
+ /* @__PURE__ */ jsxs5(DropdownMenu2, { children: [
540
+ /* @__PURE__ */ jsx6(DropdownMenuTrigger2, { asChild: true, children: /* @__PURE__ */ jsx6(
541
+ "button",
542
+ {
543
+ type: "button",
544
+ className: "rounded-md p-1.5 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground outline-none",
545
+ "aria-label": "Sandbox options",
546
+ children: /* @__PURE__ */ jsx6(MoreVertical, { className: "h-4 w-4" })
547
+ }
548
+ ) }),
549
+ /* @__PURE__ */ jsxs5(DropdownMenuContent2, { align: "end", className: "min-w-[180px]", children: [
550
+ isRunning && /* @__PURE__ */ jsxs5(Fragment3, { children: [
551
+ onStop && /* @__PURE__ */ jsxs5(DropdownMenuItem2, { onClick: () => onStop(sandbox.id), children: [
552
+ /* @__PURE__ */ jsx6(PowerOff, { className: "mr-2 h-4 w-4" }),
553
+ " Stop Sandbox"
554
+ ] }),
555
+ onKeepAlive && /* @__PURE__ */ jsxs5(DropdownMenuItem2, { onClick: () => onKeepAlive(sandbox.id), children: [
556
+ /* @__PURE__ */ jsx6(Clock, { className: "mr-2 h-4 w-4" }),
557
+ " Keep Alive"
558
+ ] }),
559
+ (onStop || onKeepAlive) && /* @__PURE__ */ jsx6(DropdownMenuSeparator2, {}),
560
+ onUsage && /* @__PURE__ */ jsxs5(DropdownMenuItem2, { onClick: () => onUsage(sandbox.id), children: [
561
+ /* @__PURE__ */ jsx6(BarChart2, { className: "mr-2 h-4 w-4" }),
562
+ " View Usage"
563
+ ] }),
564
+ onHealth && /* @__PURE__ */ jsxs5(DropdownMenuItem2, { onClick: () => onHealth(sandbox.id), children: [
565
+ /* @__PURE__ */ jsx6(Activity, { className: "mr-2 h-4 w-4" }),
566
+ " Health Check"
567
+ ] }),
568
+ (onUsage || onHealth) && /* @__PURE__ */ jsx6(DropdownMenuSeparator2, {}),
569
+ onFork && /* @__PURE__ */ jsxs5(Fragment3, { children: [
570
+ /* @__PURE__ */ jsxs5(DropdownMenuItem2, { onClick: () => onFork(sandbox.id), children: [
571
+ /* @__PURE__ */ jsx6(Copy, { className: "mr-2 h-4 w-4" }),
572
+ " Fork Sandbox"
573
+ ] }),
574
+ /* @__PURE__ */ jsx6(DropdownMenuSeparator2, {})
575
+ ] })
576
+ ] }),
577
+ isStopped && /* @__PURE__ */ jsxs5(Fragment3, { children: [
578
+ onResume && /* @__PURE__ */ jsxs5(DropdownMenuItem2, { onClick: () => onResume(sandbox.id), children: [
579
+ /* @__PURE__ */ jsx6(Power, { className: "mr-2 h-4 w-4" }),
580
+ " Resume Sandbox"
581
+ ] }),
582
+ onFork && /* @__PURE__ */ jsxs5(DropdownMenuItem2, { onClick: () => onFork(sandbox.id), children: [
583
+ /* @__PURE__ */ jsx6(Copy, { className: "mr-2 h-4 w-4" }),
584
+ " Fork Sandbox"
585
+ ] }),
586
+ (onResume || onFork) && /* @__PURE__ */ jsx6(DropdownMenuSeparator2, {})
587
+ ] }),
588
+ onDelete && canAdminSandbox(sandbox) && /* @__PURE__ */ jsxs5(DropdownMenuItem2, { className: "text-destructive focus:bg-destructive/10 focus:text-destructive", onClick: () => onDelete(sandbox.id), children: [
589
+ /* @__PURE__ */ jsx6(Trash2, { className: "mr-2 h-4 w-4" }),
590
+ " Delete Sandbox"
591
+ ] })
592
+ ] })
593
+ ] })
594
+ ] }),
595
+ /* @__PURE__ */ jsxs5("div", { className: "my-4", children: [
596
+ /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-3 rounded-md border border-border bg-muted/30 p-3", children: [
597
+ /* @__PURE__ */ jsx6("div", { className: cn("flex h-10 w-10 items-center justify-center rounded-md", isRunning ? "bg-[var(--surface-success-bg)]" : "bg-muted"), children: sandbox.imageIcon ? sandbox.imageIcon : sandbox.image?.includes("node") ? /* @__PURE__ */ jsx6(Code2, { className: cn("h-5 w-5", isRunning ? "text-[var(--surface-success-text)]" : "text-muted-foreground") }) : /* @__PURE__ */ jsx6(Terminal, { className: cn("h-5 w-5", isRunning ? "text-[var(--surface-success-text)]" : "text-muted-foreground") }) }),
598
+ /* @__PURE__ */ jsxs5("div", { className: "flex flex-col", children: [
599
+ /* @__PURE__ */ jsx6("span", { className: "text-[10px] text-muted-foreground uppercase tracking-widest font-bold", children: "Environment" }),
600
+ /* @__PURE__ */ jsx6("span", { className: "text-xs font-medium text-foreground font-mono mt-0.5", children: sandbox.image || "Universal" })
601
+ ] })
602
+ ] }),
603
+ isTransitioning && /* @__PURE__ */ jsxs5("div", { className: "mt-3", children: [
604
+ /* @__PURE__ */ jsxs5("div", { className: "flex justify-between text-xs text-muted-foreground font-medium mb-1", children: [
605
+ /* @__PURE__ */ jsx6("span", { children: sandbox.provisioningMessage || "Starting..." }),
606
+ /* @__PURE__ */ jsxs5("span", { children: [
607
+ sandbox.provisioningPercent || 0,
608
+ "%"
609
+ ] })
610
+ ] }),
611
+ /* @__PURE__ */ jsx6("div", { className: "h-1 w-full overflow-hidden rounded-full bg-muted", children: /* @__PURE__ */ jsx6(
612
+ "div",
613
+ {
614
+ className: "h-full bg-primary transition-all duration-500 rounded-full",
615
+ style: { width: `${sandbox.provisioningPercent || 5}%` }
616
+ }
617
+ ) })
618
+ ] })
619
+ ] }),
620
+ /* @__PURE__ */ jsx6("div", { className: "border-t border-border pt-3", children: isRunning ? /* @__PURE__ */ jsxs5(
621
+ "button",
622
+ {
623
+ type: "button",
624
+ onClick: () => onOpenIDE?.(sandbox.id),
625
+ className: "flex w-full items-center justify-center gap-2 rounded-md bg-[var(--btn-primary-bg)] px-4 py-2 text-sm font-semibold text-[var(--btn-primary-text)] transition-colors hover:bg-[var(--btn-primary-hover)] active:scale-[0.97]",
626
+ children: [
627
+ /* @__PURE__ */ jsx6(Network, { className: "h-4 w-4" }),
628
+ "Connect Session"
629
+ ]
630
+ }
631
+ ) : /* @__PURE__ */ jsxs5(
632
+ "button",
633
+ {
634
+ type: "button",
635
+ onClick: () => onWake?.(sandbox.id),
636
+ disabled: isTransitioning,
637
+ className: cn(
638
+ "flex w-full items-center justify-center gap-2 rounded-md px-4 py-2 text-sm font-semibold transition-colors border",
639
+ isTransitioning ? "bg-muted text-muted-foreground cursor-not-allowed border-border" : "bg-card text-foreground hover:bg-muted border-border active:scale-[0.97]"
640
+ ),
641
+ children: [
642
+ /* @__PURE__ */ jsx6(Play, { className: "h-4 w-4" }),
643
+ isTransitioning ? "Starting..." : "Wake Sandbox"
644
+ ]
645
+ }
646
+ ) })
647
+ ] });
648
+ }
649
+ function NewSandboxCard({ onClick, className }) {
650
+ return /* @__PURE__ */ jsxs5(
651
+ "button",
652
+ {
653
+ type: "button",
654
+ onClick,
655
+ className: cn(
656
+ "border-2 border-dashed border-border rounded-lg p-5 flex flex-col items-center justify-center text-center cursor-pointer hover:border-foreground/20 hover:bg-muted/30 transition-colors w-full min-h-[160px]",
657
+ className
658
+ ),
659
+ children: [
660
+ /* @__PURE__ */ jsx6("div", { className: "flex h-12 w-12 items-center justify-center rounded-lg bg-muted text-muted-foreground", children: /* @__PURE__ */ jsx6(Plus, { className: "h-6 w-6" }) }),
661
+ /* @__PURE__ */ jsx6("span", { className: "mt-4 text-sm font-semibold text-foreground", children: "New Sandbox" }),
662
+ /* @__PURE__ */ jsx6("span", { className: "mt-1 text-xs text-muted-foreground", children: "Deploy a new isolated environment" })
663
+ ]
664
+ }
665
+ );
666
+ }
667
+
668
+ // src/dashboard/sandbox-table.tsx
669
+ import { Terminal as Terminal2, Code2 as Code22, Key, Trash2 as Trash22, RefreshCw, ChevronLeft, ChevronRight, Users as Users2, User } from "lucide-react";
670
+ import { Fragment as Fragment4, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
671
+ var statusColors = {
672
+ running: { dot: "bg-[var(--code-success)] animate-pulse", text: "text-[var(--code-success)]", bar: "bg-[var(--code-success)]" },
673
+ hibernating: { dot: "bg-muted-foreground", text: "text-muted-foreground", bar: "bg-muted-foreground" },
674
+ provisioning: { dot: "bg-primary animate-pulse", text: "text-primary", bar: "bg-primary" },
675
+ creating: { dot: "bg-primary animate-pulse", text: "text-primary", bar: "bg-primary" },
676
+ stopped: { dot: "bg-muted-foreground", text: "text-foreground", bar: "bg-muted-foreground" },
677
+ failed: { dot: "bg-[var(--code-error)]", text: "text-[var(--code-error)]", bar: "bg-[var(--code-error)]" },
678
+ archived: { dot: "bg-border", text: "text-muted-foreground", bar: "bg-border" }
679
+ };
680
+ function MiniMeter({ label, percent, className }) {
681
+ return /* @__PURE__ */ jsxs6("div", { className: cn("space-y-1", className), children: [
682
+ /* @__PURE__ */ jsxs6("div", { className: "flex justify-between text-[10px] font-mono text-muted-foreground", children: [
683
+ /* @__PURE__ */ jsx7("span", { className: "font-bold", children: label }),
684
+ /* @__PURE__ */ jsxs6("span", { className: "text-primary", children: [
685
+ percent,
686
+ "%"
687
+ ] })
688
+ ] }),
689
+ /* @__PURE__ */ jsx7("div", { className: "h-1.5 w-full bg-background rounded-full overflow-hidden", children: /* @__PURE__ */ jsx7("div", { className: "h-full bg-primary rounded-full", style: { width: `${percent}%` } }) })
690
+ ] });
691
+ }
692
+ function SandboxTable({
693
+ sandboxes,
694
+ page = 1,
695
+ pageSize = 10,
696
+ total,
697
+ onPageChange,
698
+ onOpenIDE,
699
+ onOpenTerminal,
700
+ onSSH,
701
+ onWake,
702
+ onMore,
703
+ onDelete,
704
+ className
705
+ }) {
706
+ const totalCount = total ?? sandboxes.length;
707
+ const totalPages = Math.ceil(totalCount / pageSize);
708
+ const hasTeamSandboxes = sandboxes.some((sb) => sb.team !== void 0);
709
+ return /* @__PURE__ */ jsxs6("div", { className: cn("w-full", className), children: [
710
+ /* @__PURE__ */ jsx7("div", { className: "w-full bg-card rounded-2xl overflow-hidden border border-border", children: /* @__PURE__ */ jsx7("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxs6("table", { className: "w-full text-left border-collapse", children: [
711
+ /* @__PURE__ */ jsx7("thead", { children: /* @__PURE__ */ jsxs6("tr", { className: "bg-background border-b border-border", children: [
712
+ /* @__PURE__ */ jsx7("th", { className: "px-6 py-4 text-xs font-semibold text-muted-foreground uppercase tracking-wider", children: "Status" }),
713
+ /* @__PURE__ */ jsx7("th", { className: "px-6 py-4 text-xs font-semibold text-muted-foreground uppercase tracking-wider", children: "Sandbox Name" }),
714
+ hasTeamSandboxes && /* @__PURE__ */ jsx7("th", { className: "px-6 py-4 text-xs font-semibold text-muted-foreground uppercase tracking-wider", children: "Scope" }),
715
+ /* @__PURE__ */ jsx7("th", { className: "px-6 py-4 text-xs font-semibold text-muted-foreground uppercase tracking-wider", children: "Environment" }),
716
+ /* @__PURE__ */ jsx7("th", { className: "px-6 py-4 text-xs font-semibold text-muted-foreground uppercase tracking-wider", children: "Resources" }),
717
+ /* @__PURE__ */ jsx7("th", { className: "px-6 py-4 text-xs font-semibold text-muted-foreground uppercase tracking-wider text-right", children: "Actions" })
718
+ ] }) }),
719
+ /* @__PURE__ */ jsx7("tbody", { className: "divide-y divide-border", children: sandboxes.map((sb) => {
720
+ const sc = statusColors[sb.status] ?? statusColors.stopped;
721
+ const isActive = sb.status === "running";
722
+ const isHibernating = sb.status === "hibernating";
723
+ const isProvisioning = sb.status === "provisioning";
724
+ return /* @__PURE__ */ jsxs6("tr", { className: "hover:bg-muted/50 transition-colors group relative", children: [
725
+ /* @__PURE__ */ jsx7("td", { className: "px-6 py-5 whitespace-nowrap", children: /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-2", children: [
726
+ /* @__PURE__ */ jsx7("span", { className: cn("flex h-2.5 w-2.5 rounded-full", sc.dot) }),
727
+ /* @__PURE__ */ jsx7("span", { className: cn("text-xs font-bold uppercase tracking-wide", sc.text), children: sb.status.charAt(0).toUpperCase() + sb.status.slice(1) })
728
+ ] }) }),
729
+ /* @__PURE__ */ jsx7("td", { className: "px-6 py-5", children: /* @__PURE__ */ jsxs6("div", { className: "flex flex-col", children: [
730
+ /* @__PURE__ */ jsx7("span", { className: "text-sm font-bold text-foreground group-hover:text-primary transition-colors", children: sb.name }),
731
+ sb.nodeId && /* @__PURE__ */ jsx7("span", { className: "text-[10px] font-mono text-muted-foreground", children: sb.nodeId })
732
+ ] }) }),
733
+ hasTeamSandboxes && /* @__PURE__ */ jsx7("td", { className: "px-6 py-5", children: sb.team ? /* @__PURE__ */ jsxs6(
734
+ "div",
735
+ {
736
+ className: "inline-flex items-center gap-1.5 rounded-full bg-[var(--accent-surface-soft)] px-2.5 py-1 text-[11px] font-semibold text-[var(--accent-text)]",
737
+ title: `Shared with ${sb.team.name ?? "Team"} \xB7 ${sb.team.role}`,
738
+ children: [
739
+ /* @__PURE__ */ jsx7(Users2, { className: "h-3 w-3", "aria-hidden": "true" }),
740
+ /* @__PURE__ */ jsx7("span", { children: sb.team.name ?? "Team" }),
741
+ /* @__PURE__ */ jsxs6("span", { className: "font-normal text-muted-foreground", children: [
742
+ "\xB7 ",
743
+ sb.team.role
744
+ ] })
745
+ ]
746
+ }
747
+ ) : /* @__PURE__ */ jsxs6(
748
+ "div",
749
+ {
750
+ className: "inline-flex items-center gap-1.5 rounded-full bg-muted px-2.5 py-1 text-[11px] font-medium text-muted-foreground",
751
+ title: "Personal sandbox",
752
+ children: [
753
+ /* @__PURE__ */ jsx7(User, { className: "h-3 w-3", "aria-hidden": "true" }),
754
+ "Personal"
755
+ ]
756
+ }
757
+ ) }),
758
+ /* @__PURE__ */ jsx7("td", { className: "px-6 py-5", children: /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-3", children: [
759
+ sb.imageIcon && /* @__PURE__ */ jsx7("div", { className: "w-8 h-8 rounded-lg bg-muted/50 flex items-center justify-center", children: sb.imageIcon }),
760
+ sb.image && /* @__PURE__ */ jsx7("span", { className: "text-xs font-bold text-foreground", children: sb.image })
761
+ ] }) }),
762
+ /* @__PURE__ */ jsx7("td", { className: "px-6 py-5", children: isActive ? /* @__PURE__ */ jsxs6("div", { className: "space-y-3 w-48", children: [
763
+ /* @__PURE__ */ jsx7(MiniMeter, { label: "CPU", percent: sb.cpuPercent ?? 0 }),
764
+ /* @__PURE__ */ jsx7(MiniMeter, { label: "RAM", percent: sb.ramTotal ? Math.round((sb.ramUsed ?? 0) / sb.ramTotal * 100) : 0 })
765
+ ] }) : isProvisioning ? /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-2 text-primary italic text-[10px] font-bold", children: [
766
+ /* @__PURE__ */ jsx7(RefreshCw, { className: "h-3.5 w-3.5 animate-spin" }),
767
+ sb.provisioningMessage ?? "Allocating nodes..."
768
+ ] }) : isHibernating ? /* @__PURE__ */ jsxs6("div", { className: "space-y-3 w-48 opacity-30", children: [
769
+ /* @__PURE__ */ jsx7(MiniMeter, { label: "CPU", percent: 0 }),
770
+ /* @__PURE__ */ jsx7(MiniMeter, { label: "RAM", percent: 0 })
771
+ ] }) : null }),
772
+ /* @__PURE__ */ jsx7("td", { className: "px-6 py-5 text-right", children: /* @__PURE__ */ jsxs6("div", { className: "flex items-center justify-end gap-1", children: [
773
+ isActive && /* @__PURE__ */ jsxs6(Fragment4, { children: [
774
+ /* @__PURE__ */ jsx7("button", { type: "button", onClick: () => onOpenIDE?.(sb.id), className: "p-2 rounded-lg hover:bg-muted text-muted-foreground hover:text-foreground transition-all active:scale-90", title: "Open IDE", children: /* @__PURE__ */ jsx7(Code22, { className: "h-4 w-4" }) }),
775
+ /* @__PURE__ */ jsx7("button", { type: "button", onClick: () => onOpenTerminal?.(sb.id), className: "p-2 rounded-lg hover:bg-muted text-muted-foreground hover:text-foreground transition-all active:scale-90", title: "Terminal", children: /* @__PURE__ */ jsx7(Terminal2, { className: "h-4 w-4" }) }),
776
+ /* @__PURE__ */ jsx7("button", { type: "button", onClick: () => onSSH?.(sb.id), className: "p-2 rounded-lg hover:bg-muted text-muted-foreground hover:text-foreground transition-all active:scale-90", title: "SSH", children: /* @__PURE__ */ jsx7(Key, { className: "h-4 w-4" }) })
777
+ ] }),
778
+ isHibernating && /* @__PURE__ */ jsx7("button", { type: "button", onClick: () => onWake?.(sb.id), className: "px-3 py-1.5 rounded-lg border border-border text-primary text-[10px] font-bold uppercase tracking-wider hover:bg-[var(--accent-surface-soft)] active:scale-95 transition-all", children: "Wake Up" }),
779
+ onMore && /* @__PURE__ */ jsx7("button", { type: "button", onClick: () => onMore(sb.id), className: "p-2 rounded-lg hover:bg-muted text-muted-foreground hover:text-foreground transition-all active:scale-90", children: /* @__PURE__ */ jsx7(Code22, { className: "h-4 w-4" }) }),
780
+ onDelete && canAdminSandbox(sb) && /* @__PURE__ */ jsx7("button", { type: "button", onClick: () => onDelete(sb.id), className: "p-2 rounded-lg hover:bg-[var(--surface-danger-bg)] text-muted-foreground hover:text-[var(--surface-danger-text)] transition-all active:scale-90", title: "Delete", children: /* @__PURE__ */ jsx7(Trash22, { className: "h-4 w-4" }) })
781
+ ] }) })
782
+ ] }, sb.id);
783
+ }) })
784
+ ] }) }) }),
785
+ totalPages > 1 && /* @__PURE__ */ jsxs6("div", { className: "mt-6 flex flex-col md:flex-row justify-between items-center text-muted-foreground text-xs font-medium gap-4", children: [
786
+ /* @__PURE__ */ jsxs6("p", { children: [
787
+ "Showing ",
788
+ sandboxes.length,
789
+ " of ",
790
+ totalCount,
791
+ " active sandboxes"
792
+ ] }),
793
+ /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-2", children: [
794
+ /* @__PURE__ */ jsx7("button", { type: "button", onClick: () => onPageChange?.(page - 1), disabled: page <= 1, className: "p-2 rounded-lg border border-border hover:bg-muted/50 transition-colors disabled:opacity-30", children: /* @__PURE__ */ jsx7(ChevronLeft, { className: "h-4 w-4" }) }),
795
+ Array.from({ length: Math.min(totalPages, 5) }, (_, i) => i + 1).map((p) => /* @__PURE__ */ jsx7(
796
+ "button",
797
+ {
798
+ type: "button",
799
+ onClick: () => onPageChange?.(p),
800
+ className: cn(
801
+ "px-3 py-1 rounded-lg transition-colors",
802
+ p === page ? "bg-[var(--accent-surface-soft)] text-primary border border-border" : "hover:bg-muted/50"
803
+ ),
804
+ children: p
805
+ },
806
+ p
807
+ )),
808
+ /* @__PURE__ */ jsx7("button", { type: "button", onClick: () => onPageChange?.(page + 1), disabled: page >= totalPages, className: "p-2 rounded-lg border border-border hover:bg-muted/50 transition-colors disabled:opacity-30", children: /* @__PURE__ */ jsx7(ChevronRight, { className: "h-4 w-4" }) })
809
+ ] })
810
+ ] })
811
+ ] });
812
+ }
813
+
814
+ // src/dashboard/invoice-table.tsx
815
+ import { Download, FileText } from "lucide-react";
816
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
817
+ var statusStyle = {
818
+ paid: "bg-[var(--accent-surface-soft)] text-primary border border-border",
819
+ pending: "bg-[var(--surface-warning-bg)] text-[var(--surface-warning-text)] border border-[var(--surface-warning-border)]",
820
+ failed: "bg-[var(--surface-danger-bg)] text-[var(--surface-danger-text)] border border-[var(--surface-danger-border)]"
821
+ };
822
+ function InvoiceTable({ invoices, onExportAll, onLoadMore, onViewInvoice, hasMore, className }) {
823
+ return /* @__PURE__ */ jsxs7("section", { className, children: [
824
+ /* @__PURE__ */ jsxs7("div", { className: "flex justify-between items-center mb-6 px-2", children: [
825
+ /* @__PURE__ */ jsx8("h2", { className: "text-2xl font-bold text-foreground tracking-tight", children: "Invoice History" }),
826
+ onExportAll && /* @__PURE__ */ jsxs7("button", { type: "button", onClick: onExportAll, className: "text-[10px] font-mono text-primary uppercase tracking-widest flex items-center gap-2 hover:underline", children: [
827
+ /* @__PURE__ */ jsx8(Download, { className: "h-3.5 w-3.5" }),
828
+ "Export All"
829
+ ] })
830
+ ] }),
831
+ /* @__PURE__ */ jsx8("div", { className: "bg-card rounded-xl overflow-hidden border border-border", children: /* @__PURE__ */ jsxs7("table", { className: "w-full text-left border-collapse", children: [
832
+ /* @__PURE__ */ jsx8("thead", { children: /* @__PURE__ */ jsxs7("tr", { className: "bg-background border-b border-border", children: [
833
+ /* @__PURE__ */ jsx8("th", { className: "px-6 py-4 font-mono text-[10px] text-muted-foreground uppercase tracking-widest", children: "Invoice ID" }),
834
+ /* @__PURE__ */ jsx8("th", { className: "px-6 py-4 font-mono text-[10px] text-muted-foreground uppercase tracking-widest", children: "Date" }),
835
+ /* @__PURE__ */ jsx8("th", { className: "px-6 py-4 font-mono text-[10px] text-muted-foreground uppercase tracking-widest", children: "Amount" }),
836
+ /* @__PURE__ */ jsx8("th", { className: "px-6 py-4 font-mono text-[10px] text-muted-foreground uppercase tracking-widest", children: "Status" }),
837
+ /* @__PURE__ */ jsx8("th", { className: "px-6 py-4 font-mono text-[10px] text-muted-foreground uppercase tracking-widest text-right", children: "Action" })
838
+ ] }) }),
839
+ /* @__PURE__ */ jsx8("tbody", { className: "divide-y divide-border", children: invoices.map((inv) => /* @__PURE__ */ jsxs7("tr", { className: "hover:bg-muted/50 transition-colors", children: [
840
+ /* @__PURE__ */ jsx8("td", { className: "px-6 py-5 font-mono text-xs text-foreground", children: inv.id }),
841
+ /* @__PURE__ */ jsx8("td", { className: "px-6 py-5 text-sm text-foreground", children: inv.date }),
842
+ /* @__PURE__ */ jsxs7("td", { className: "px-6 py-5 text-sm font-bold text-foreground", children: [
843
+ "$",
844
+ inv.amount.toFixed(2)
845
+ ] }),
846
+ /* @__PURE__ */ jsx8("td", { className: "px-6 py-5", children: /* @__PURE__ */ jsx8("span", { className: cn("px-2 py-1 text-[10px] font-mono rounded uppercase", statusStyle[inv.status] ?? statusStyle.paid), children: inv.status }) }),
847
+ /* @__PURE__ */ jsx8("td", { className: "px-6 py-5 text-right", children: /* @__PURE__ */ jsx8("button", { type: "button", onClick: () => onViewInvoice?.(inv.id), className: "text-muted-foreground hover:text-foreground transition-colors", children: /* @__PURE__ */ jsx8(FileText, { className: "h-4 w-4" }) }) })
848
+ ] }, inv.id)) })
849
+ ] }) }),
850
+ hasMore && onLoadMore && /* @__PURE__ */ jsx8("div", { className: "mt-6 flex justify-center", children: /* @__PURE__ */ jsx8("button", { type: "button", onClick: onLoadMore, className: "px-8 py-2 text-[10px] font-mono text-muted-foreground border border-border rounded-full hover:bg-muted/50 transition-colors uppercase tracking-widest", children: "Load More History" }) })
851
+ ] });
852
+ }
853
+
854
+ // src/dashboard/plan-cards.tsx
855
+ import { Check } from "lucide-react";
856
+ import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
857
+ function PlanCards({ plans, className }) {
858
+ return /* @__PURE__ */ jsxs8("section", { className, children: [
859
+ /* @__PURE__ */ jsx9("h2", { className: "text-2xl font-bold text-foreground tracking-tight mb-5 px-2", children: "Subscription Plans" }),
860
+ /* @__PURE__ */ jsx9("div", { className: "grid grid-cols-1 md:grid-cols-3 gap-6", children: plans.map((plan) => /* @__PURE__ */ jsxs8(
861
+ "div",
862
+ {
863
+ className: cn(
864
+ "p-5 rounded-xl transition-all relative overflow-hidden border",
865
+ plan.popular ? "bg-muted/50 border-border" : "bg-card border-border hover:bg-muted/50 hover:border-primary/20"
866
+ ),
867
+ children: [
868
+ plan.popular && /* @__PURE__ */ jsx9("div", { className: "absolute top-0 right-0 bg-[var(--accent-surface-soft)] border-l border-b border-border px-4 py-1 text-[10px] font-bold text-[var(--accent-text)] uppercase tracking-widest rounded-bl-lg", children: "Popular" }),
869
+ /* @__PURE__ */ jsxs8("div", { className: "mb-4", children: [
870
+ /* @__PURE__ */ jsx9("div", { className: cn("text-xs font-mono uppercase tracking-widest mb-2", plan.popular ? "text-primary" : "text-muted-foreground"), children: plan.name }),
871
+ /* @__PURE__ */ jsxs8("div", { className: "text-3xl font-bold text-foreground", children: [
872
+ "$",
873
+ plan.price,
874
+ /* @__PURE__ */ jsxs8("span", { className: "text-sm font-normal text-muted-foreground tracking-normal", children: [
875
+ "/",
876
+ plan.period ?? "mo"
877
+ ] })
878
+ ] })
879
+ ] }),
880
+ /* @__PURE__ */ jsx9("ul", { className: "space-y-2 mb-5 text-sm text-muted-foreground", children: plan.features.map((f, i) => /* @__PURE__ */ jsxs8("li", { className: "flex items-center gap-2", children: [
881
+ /* @__PURE__ */ jsx9(Check, { className: "h-3.5 w-3.5 text-primary shrink-0" }),
882
+ f.text
883
+ ] }, i)) }),
884
+ /* @__PURE__ */ jsx9(
885
+ "button",
886
+ {
887
+ type: "button",
888
+ onClick: () => plan.onSelect?.(plan.id),
889
+ className: cn(
890
+ "w-full py-3 rounded-lg text-xs font-bold uppercase tracking-widest transition-all border",
891
+ plan.current ? "border-border text-muted-foreground hover:border-primary/20 hover:text-foreground" : plan.popular ? "bg-[var(--accent-surface-soft)] border-border text-[var(--accent-text)] hover:bg-[var(--accent-surface-strong)] active:scale-95 transition-transform" : "border-border text-foreground hover:border-primary/20 hover:text-primary"
892
+ ),
893
+ children: plan.ctaLabel ?? (plan.current ? "Current Plan" : "Upgrade Now")
894
+ }
895
+ )
896
+ ]
897
+ },
898
+ plan.id
899
+ )) })
900
+ ] });
901
+ }
902
+
903
+ // src/dashboard/backend-selector.tsx
904
+ import { ChevronDown } from "lucide-react";
905
+ import * as Select from "@radix-ui/react-select";
906
+ import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
907
+ function BackendSelector({
908
+ backends,
909
+ selected,
910
+ onChange,
911
+ label = "Model",
912
+ placeholder = "Select a model",
913
+ className
914
+ }) {
915
+ const current = backends.find((b) => b.type === selected);
916
+ return /* @__PURE__ */ jsxs9("div", { className: cn("space-y-1.5", className), children: [
917
+ label && /* @__PURE__ */ jsx10("label", { className: "block text-xs font-medium text-muted-foreground uppercase tracking-[0.06em]", children: label }),
918
+ /* @__PURE__ */ jsxs9(Select.Root, { value: selected, onValueChange: onChange, children: [
919
+ /* @__PURE__ */ jsxs9(
920
+ Select.Trigger,
921
+ {
922
+ className: cn(
923
+ "flex w-full items-center justify-between gap-2 rounded-[var(--radius-md)]",
924
+ "border border-border bg-card",
925
+ "px-3 py-2.5 text-sm text-left",
926
+ "transition-colors duration-[var(--transition-fast)]",
927
+ "hover:border-primary/20 hover:bg-accent/30",
928
+ "focus:outline-none focus:border-primary/30",
929
+ "data-[state=open]:border-primary/30 data-[state=open]:bg-accent/30"
930
+ ),
931
+ children: [
932
+ /* @__PURE__ */ jsx10("div", { className: "min-w-0 flex-1", children: current ? /* @__PURE__ */ jsxs9("div", { children: [
933
+ /* @__PURE__ */ jsx10("span", { className: "font-medium text-foreground", children: current.label }),
934
+ current.description && /* @__PURE__ */ jsx10("span", { className: "ml-2 text-xs text-muted-foreground", children: current.description })
935
+ ] }) : /* @__PURE__ */ jsx10("span", { className: "text-muted-foreground", children: placeholder }) }),
936
+ /* @__PURE__ */ jsx10(Select.Icon, { asChild: true, children: /* @__PURE__ */ jsx10(ChevronDown, { className: "h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-[var(--transition-fast)] data-[state=open]:rotate-180" }) })
937
+ ]
938
+ }
939
+ ),
940
+ /* @__PURE__ */ jsx10(Select.Portal, { children: /* @__PURE__ */ jsx10(
941
+ Select.Content,
942
+ {
943
+ position: "popper",
944
+ sideOffset: 4,
945
+ className: cn(
946
+ "z-50 w-[var(--radix-select-trigger-width)] overflow-hidden",
947
+ "rounded-[var(--radius-md)] border border-border",
948
+ "bg-card shadow-[var(--shadow-dropdown)]",
949
+ "data-[state=open]:animate-in data-[state=closed]:animate-out",
950
+ "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
951
+ "data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
952
+ "data-[side=bottom]:slide-in-from-top-1"
953
+ ),
954
+ children: /* @__PURE__ */ jsx10(Select.Viewport, { className: "p-1", children: backends.map((backend) => /* @__PURE__ */ jsxs9(
955
+ Select.Item,
956
+ {
957
+ value: backend.type,
958
+ className: cn(
959
+ "relative flex cursor-pointer select-none flex-col rounded-[var(--radius-sm)]",
960
+ "px-3 py-2.5 text-sm outline-none",
961
+ "transition-colors duration-[var(--transition-fast)]",
962
+ "hover:bg-accent/50 focus:bg-accent/50",
963
+ "data-[state=checked]:bg-[var(--accent-surface-soft)] data-[state=checked]:text-[var(--brand-primary)]"
964
+ ),
965
+ children: [
966
+ /* @__PURE__ */ jsx10(Select.ItemText, { children: /* @__PURE__ */ jsx10("span", { className: "font-medium", children: backend.label }) }),
967
+ backend.description && /* @__PURE__ */ jsx10("span", { className: "mt-0.5 text-xs text-muted-foreground data-[state=checked]:text-[var(--accent-text)]", children: backend.description })
968
+ ]
969
+ },
970
+ backend.type
971
+ )) })
972
+ }
973
+ ) })
974
+ ] })
975
+ ] });
976
+ }
977
+
978
+ // src/dashboard/harness-picker.tsx
979
+ import { jsx as jsx11 } from "react/jsx-runtime";
980
+ var HARNESS_OPTIONS = [
981
+ {
982
+ type: "opencode",
983
+ label: "OpenCode",
984
+ description: "Default agent \u2014 broad model support, deterministic streaming"
985
+ },
986
+ {
987
+ type: "claude-code",
988
+ label: "Claude Code",
989
+ description: "Native Claude skills and tools (requires ANTHROPIC_API_KEY)"
990
+ },
991
+ {
992
+ type: "codex",
993
+ label: "Codex",
994
+ description: "OpenAI Codex CLI (requires OPENAI_API_KEY)"
995
+ },
996
+ {
997
+ type: "amp",
998
+ label: "AMP",
999
+ description: "Sourcegraph AMP agent"
1000
+ },
1001
+ {
1002
+ type: "factory-droids",
1003
+ label: "Factory Droids",
1004
+ description: "Factory Droid agent"
1005
+ },
1006
+ {
1007
+ type: "cli-base",
1008
+ label: "CLI base (no agent)",
1009
+ description: "Shell tools only \u2014 for non-AI scheduled tasks"
1010
+ }
1011
+ ];
1012
+ function HarnessPicker({
1013
+ value,
1014
+ onChange,
1015
+ available,
1016
+ optionsOverride,
1017
+ label = "Agent harness",
1018
+ ...rest
1019
+ }) {
1020
+ const allowed = new Set(available ?? HARNESS_OPTIONS.map((h) => h.type));
1021
+ const backends = HARNESS_OPTIONS.filter((h) => allowed.has(h.type)).map((h) => {
1022
+ const override = optionsOverride?.[h.type];
1023
+ return {
1024
+ type: h.type,
1025
+ label: override?.label ?? h.label,
1026
+ description: override?.description ?? h.description
1027
+ };
1028
+ });
1029
+ return /* @__PURE__ */ jsx11(
1030
+ BackendSelector,
1031
+ {
1032
+ backends,
1033
+ selected: value,
1034
+ onChange: (next) => onChange(next),
1035
+ label,
1036
+ ...rest
1037
+ }
1038
+ );
1039
+ }
1040
+
1041
+ // src/dashboard/model-picker.tsx
1042
+ import * as React3 from "react";
1043
+ import { ChevronDown as ChevronDown2, Search, Sparkles, Zap, Brain, Star, Loader2 } from "lucide-react";
1044
+ import * as Popover from "@radix-ui/react-dropdown-menu";
1045
+ import { Fragment as Fragment5, jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
1046
+ function canonicalModelId(model) {
1047
+ const id = model.id;
1048
+ if (id.includes("/")) return id;
1049
+ const provider = model._provider ?? model.provider;
1050
+ return provider ? `${provider}/${id}` : id;
1051
+ }
1052
+ function formatPricing(pricing) {
1053
+ const prompt = Number(pricing?.prompt ?? 0);
1054
+ const completion = Number(pricing?.completion ?? 0);
1055
+ if (!prompt && !completion) return null;
1056
+ const fmt = (n) => {
1057
+ const perM = n * 1e6;
1058
+ if (perM === 0) return "0";
1059
+ if (perM >= 1) return `$${perM.toFixed(2)}`;
1060
+ return `$${perM.toFixed(2)}`;
1061
+ };
1062
+ return `${fmt(prompt)} / ${fmt(completion)} per 1M`;
1063
+ }
1064
+ function formatContext(ctx) {
1065
+ if (!ctx) return null;
1066
+ if (ctx >= 1e6) return `${(ctx / 1e6).toFixed(1)}M ctx`;
1067
+ if (ctx >= 1e3) return `${Math.round(ctx / 1e3)}k ctx`;
1068
+ return `${ctx} ctx`;
1069
+ }
1070
+ var DEFAULT_PRESETS = [
1071
+ {
1072
+ id: "fast",
1073
+ label: "Fast",
1074
+ hint: "Cheapest, lowest latency",
1075
+ icon: Zap,
1076
+ match: (models) => {
1077
+ const ids = models.map(canonicalModelId);
1078
+ return ids.find((m) => /gpt-5\.\d+-mini$/.test(m)) ?? ids.find((m) => /gpt-5-mini$/.test(m)) ?? ids.find((m) => m.endsWith("/claude-haiku-4.5")) ?? ids.find((m) => /haiku/.test(m));
1079
+ }
1080
+ },
1081
+ {
1082
+ id: "balanced",
1083
+ label: "Balanced",
1084
+ hint: "Best value for most chat",
1085
+ icon: Sparkles,
1086
+ match: (models) => {
1087
+ const ids = models.map(canonicalModelId);
1088
+ return ids.find((m) => /^openai\/gpt-5\.\d+$/.test(m)) ?? ids.find((m) => /^openai\/gpt-5$/.test(m)) ?? ids.find((m) => m.endsWith("/claude-sonnet-4-6")) ?? ids.find((m) => /sonnet/.test(m));
1089
+ }
1090
+ },
1091
+ {
1092
+ id: "best",
1093
+ label: "Best",
1094
+ hint: "Hardest reasoning, highest quality",
1095
+ icon: Brain,
1096
+ match: (models) => {
1097
+ const ids = models.map(canonicalModelId);
1098
+ return ids.find((m) => /^openai\/gpt-5\.\d+-pro$/.test(m)) ?? ids.find((m) => /^openai\/o3$/.test(m)) ?? ids.find((m) => m.endsWith("/claude-opus-4-7")) ?? ids.find((m) => /opus/.test(m));
1099
+ }
1100
+ }
1101
+ ];
1102
+ function ModelPicker({
1103
+ value,
1104
+ onChange,
1105
+ models,
1106
+ loading = false,
1107
+ recents,
1108
+ presets = DEFAULT_PRESETS,
1109
+ excludeProviders,
1110
+ modalities,
1111
+ variant = "field",
1112
+ label = "Model",
1113
+ placeholder = "Choose a model",
1114
+ className,
1115
+ disabled
1116
+ }) {
1117
+ const [query, setQuery] = React3.useState("");
1118
+ const [open, setOpen] = React3.useState(false);
1119
+ const filtered = React3.useMemo(() => {
1120
+ const excluded = new Set((excludeProviders ?? []).map((p) => p.toLowerCase()));
1121
+ const allowedModalities = modalities ? new Set(modalities) : null;
1122
+ const q = query.trim().toLowerCase();
1123
+ return models.filter((m) => {
1124
+ const provider = (m._provider ?? m.provider ?? "").toLowerCase();
1125
+ if (excluded.has(provider)) return false;
1126
+ if (allowedModalities && m.architecture?.modality && !allowedModalities.has(m.architecture.modality)) return false;
1127
+ if (!q) return true;
1128
+ const id = canonicalModelId(m).toLowerCase();
1129
+ const name = (m.name ?? "").toLowerCase();
1130
+ return id.includes(q) || name.includes(q) || provider.includes(q);
1131
+ });
1132
+ }, [models, query, modalities, excludeProviders]);
1133
+ const grouped = React3.useMemo(() => {
1134
+ const groups = /* @__PURE__ */ new Map();
1135
+ for (const m of filtered) {
1136
+ const provider = m._provider ?? m.provider ?? "other";
1137
+ const list = groups.get(provider);
1138
+ if (list) list.push(m);
1139
+ else groups.set(provider, [m]);
1140
+ }
1141
+ return Array.from(groups.entries()).sort(([a], [b]) => a.localeCompare(b));
1142
+ }, [filtered]);
1143
+ const current = React3.useMemo(
1144
+ () => models.find((m) => canonicalModelId(m) === value),
1145
+ [models, value]
1146
+ );
1147
+ const currentLabel = current?.name ?? current?.id ?? value;
1148
+ const recentIds = React3.useMemo(() => {
1149
+ if (!recents?.length) return [];
1150
+ const lookup = new Map(models.map((m) => [canonicalModelId(m), m]));
1151
+ return recents.map((id) => lookup.get(id)).filter((m) => Boolean(m)).slice(0, 4);
1152
+ }, [recents, models]);
1153
+ const handleSelect = (id) => {
1154
+ onChange(id);
1155
+ setOpen(false);
1156
+ setQuery("");
1157
+ };
1158
+ const trigger = variant === "pill" ? /* @__PURE__ */ jsxs10(
1159
+ "button",
1160
+ {
1161
+ type: "button",
1162
+ disabled,
1163
+ className: cn(
1164
+ "inline-flex items-center gap-1.5 rounded-full border border-border bg-card",
1165
+ "px-2.5 py-1 text-xs font-medium text-foreground",
1166
+ "transition-colors duration-[var(--transition-fast)]",
1167
+ "hover:border-primary/30 hover:bg-accent/30",
1168
+ "focus:outline-none focus:border-primary/40",
1169
+ "data-[state=open]:border-primary/40 data-[state=open]:bg-accent/30",
1170
+ "disabled:opacity-50 disabled:cursor-not-allowed",
1171
+ className
1172
+ ),
1173
+ children: [
1174
+ /* @__PURE__ */ jsx12(Sparkles, { className: "h-3 w-3 text-muted-foreground" }),
1175
+ /* @__PURE__ */ jsx12("span", { className: "truncate max-w-[160px]", children: currentLabel || placeholder }),
1176
+ /* @__PURE__ */ jsx12(ChevronDown2, { className: "h-3 w-3 text-muted-foreground transition-transform data-[state=open]:rotate-180" })
1177
+ ]
1178
+ }
1179
+ ) : /* @__PURE__ */ jsxs10(
1180
+ "button",
1181
+ {
1182
+ type: "button",
1183
+ disabled,
1184
+ className: cn(
1185
+ "flex w-full items-center justify-between gap-2 rounded-[var(--radius-md)]",
1186
+ "border border-border bg-card px-3 py-2.5 text-sm text-left",
1187
+ "transition-colors duration-[var(--transition-fast)]",
1188
+ "hover:border-primary/20 hover:bg-accent/30",
1189
+ "focus:outline-none focus:border-primary/30",
1190
+ "data-[state=open]:border-primary/30 data-[state=open]:bg-accent/30",
1191
+ "disabled:opacity-50 disabled:cursor-not-allowed",
1192
+ className
1193
+ ),
1194
+ children: [
1195
+ /* @__PURE__ */ jsx12("span", { className: cn("truncate", current ? "text-foreground font-medium" : "text-muted-foreground"), children: currentLabel || placeholder }),
1196
+ /* @__PURE__ */ jsx12(ChevronDown2, { className: "h-4 w-4 shrink-0 text-muted-foreground transition-transform data-[state=open]:rotate-180" })
1197
+ ]
1198
+ }
1199
+ );
1200
+ return /* @__PURE__ */ jsxs10("div", { className: cn(variant === "field" ? "space-y-1.5" : "inline-flex", variant === "field" ? className : void 0), children: [
1201
+ variant === "field" && label && /* @__PURE__ */ jsx12("label", { className: "block text-xs font-medium text-muted-foreground uppercase tracking-[0.06em]", children: label }),
1202
+ /* @__PURE__ */ jsxs10(Popover.Root, { open, onOpenChange: setOpen, children: [
1203
+ /* @__PURE__ */ jsx12(Popover.Trigger, { asChild: true, children: trigger }),
1204
+ /* @__PURE__ */ jsx12(Popover.Portal, { children: /* @__PURE__ */ jsxs10(
1205
+ Popover.Content,
1206
+ {
1207
+ sideOffset: 4,
1208
+ align: variant === "pill" ? "start" : "start",
1209
+ className: cn(
1210
+ "z-50 w-[var(--radix-dropdown-menu-trigger-width)] min-w-[320px] max-w-[460px]",
1211
+ "max-h-[440px] overflow-hidden flex flex-col",
1212
+ "rounded-[var(--radius-md)] border border-border bg-card shadow-[var(--shadow-dropdown)]",
1213
+ "data-[state=open]:animate-in data-[state=closed]:animate-out",
1214
+ "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
1215
+ "data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95"
1216
+ ),
1217
+ children: [
1218
+ /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-2 border-b border-border px-3 py-2", children: [
1219
+ /* @__PURE__ */ jsx12(Search, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }),
1220
+ /* @__PURE__ */ jsx12(
1221
+ "input",
1222
+ {
1223
+ type: "text",
1224
+ value: query,
1225
+ onChange: (e) => setQuery(e.target.value),
1226
+ placeholder: "Search models...",
1227
+ autoFocus: true,
1228
+ className: "flex-1 bg-transparent text-sm outline-none placeholder:text-muted-foreground"
1229
+ }
1230
+ ),
1231
+ loading && /* @__PURE__ */ jsx12(Loader2, { className: "h-3.5 w-3.5 animate-spin text-muted-foreground" })
1232
+ ] }),
1233
+ /* @__PURE__ */ jsxs10("div", { className: "flex-1 overflow-y-auto", children: [
1234
+ !query && presets.length > 0 && /* @__PURE__ */ jsx12(Section, { label: "Presets", children: presets.map((preset) => {
1235
+ const Icon2 = preset.icon ?? Star;
1236
+ const matchedId = preset.match(models);
1237
+ if (!matchedId) return null;
1238
+ const matched = models.find((m) => canonicalModelId(m) === matchedId);
1239
+ const isCurrent = matchedId === value;
1240
+ return /* @__PURE__ */ jsxs10(
1241
+ PickerItem,
1242
+ {
1243
+ onSelect: () => handleSelect(matchedId),
1244
+ active: isCurrent,
1245
+ children: [
1246
+ /* @__PURE__ */ jsx12(Icon2, { className: "h-3.5 w-3.5 shrink-0 text-[var(--accent-text)]" }),
1247
+ /* @__PURE__ */ jsxs10("div", { className: "min-w-0 flex-1", children: [
1248
+ /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-1.5", children: [
1249
+ /* @__PURE__ */ jsx12("span", { className: "text-sm font-medium", children: preset.label }),
1250
+ /* @__PURE__ */ jsxs10("span", { className: "text-[10px] text-muted-foreground truncate", children: [
1251
+ "\u2192 ",
1252
+ matched?.name ?? matchedId
1253
+ ] })
1254
+ ] }),
1255
+ /* @__PURE__ */ jsx12("span", { className: "text-xs text-muted-foreground", children: preset.hint })
1256
+ ] })
1257
+ ]
1258
+ },
1259
+ preset.id
1260
+ );
1261
+ }) }),
1262
+ !query && recentIds.length > 0 && /* @__PURE__ */ jsx12(Section, { label: "Recent", children: recentIds.map((m) => /* @__PURE__ */ jsx12(
1263
+ ModelRow,
1264
+ {
1265
+ model: m,
1266
+ active: canonicalModelId(m) === value,
1267
+ onSelect: handleSelect
1268
+ },
1269
+ `recent-${canonicalModelId(m)}`
1270
+ )) }),
1271
+ grouped.length === 0 ? /* @__PURE__ */ jsx12("div", { className: "px-3 py-8 text-center text-xs text-muted-foreground", children: loading ? "Loading models..." : query ? "No models match." : "No models available." }) : grouped.map(([provider, list]) => /* @__PURE__ */ jsx12(Section, { label: provider, children: list.map((m) => /* @__PURE__ */ jsx12(
1272
+ ModelRow,
1273
+ {
1274
+ model: m,
1275
+ active: canonicalModelId(m) === value,
1276
+ onSelect: handleSelect
1277
+ },
1278
+ canonicalModelId(m)
1279
+ )) }, provider))
1280
+ ] }),
1281
+ /* @__PURE__ */ jsxs10("div", { className: "border-t border-border px-3 py-1.5 text-[10px] text-muted-foreground", children: [
1282
+ filtered.length,
1283
+ " of ",
1284
+ models.length,
1285
+ " model",
1286
+ models.length === 1 ? "" : "s"
1287
+ ] })
1288
+ ]
1289
+ }
1290
+ ) })
1291
+ ] })
1292
+ ] });
1293
+ }
1294
+ function Section({ label, children }) {
1295
+ return /* @__PURE__ */ jsxs10("div", { className: "py-1", children: [
1296
+ /* @__PURE__ */ jsx12("div", { className: "px-3 pt-1.5 pb-0.5 text-[10px] font-mono uppercase tracking-widest text-muted-foreground", children: label }),
1297
+ /* @__PURE__ */ jsx12("div", { children })
1298
+ ] });
1299
+ }
1300
+ function PickerItem({
1301
+ onSelect,
1302
+ active,
1303
+ children
1304
+ }) {
1305
+ return /* @__PURE__ */ jsx12(
1306
+ Popover.Item,
1307
+ {
1308
+ onSelect: (e) => {
1309
+ e.preventDefault();
1310
+ onSelect();
1311
+ },
1312
+ className: cn(
1313
+ "flex cursor-pointer items-start gap-2 px-3 py-2 outline-none",
1314
+ "transition-colors duration-[var(--transition-fast)]",
1315
+ "hover:bg-accent/40 focus:bg-accent/40",
1316
+ active && "bg-[var(--accent-surface-soft)] text-[var(--accent-text)]"
1317
+ ),
1318
+ children
1319
+ }
1320
+ );
1321
+ }
1322
+ function ModelRow({
1323
+ model,
1324
+ active,
1325
+ onSelect
1326
+ }) {
1327
+ const id = canonicalModelId(model);
1328
+ const pricing = formatPricing(model.pricing);
1329
+ const ctx = formatContext(model.context_length);
1330
+ return /* @__PURE__ */ jsx12(PickerItem, { onSelect: () => onSelect(id), active, children: /* @__PURE__ */ jsxs10("div", { className: "min-w-0 flex-1", children: [
1331
+ /* @__PURE__ */ jsxs10("div", { className: "flex items-baseline justify-between gap-2", children: [
1332
+ /* @__PURE__ */ jsx12("span", { className: "text-sm font-medium truncate", children: model.name ?? model.id }),
1333
+ ctx && /* @__PURE__ */ jsx12("span", { className: "shrink-0 text-[10px] text-muted-foreground", children: ctx })
1334
+ ] }),
1335
+ /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-2 text-[11px] text-muted-foreground", children: [
1336
+ /* @__PURE__ */ jsx12("span", { className: "truncate", children: id }),
1337
+ pricing && /* @__PURE__ */ jsxs10(Fragment5, { children: [
1338
+ /* @__PURE__ */ jsx12("span", { className: "shrink-0", children: "\xB7" }),
1339
+ /* @__PURE__ */ jsx12("span", { className: "shrink-0 font-mono", children: pricing })
1340
+ ] })
1341
+ ] })
1342
+ ] }) });
1343
+ }
1344
+
1345
+ // src/dashboard/dashboard-layout.tsx
1346
+ import * as React4 from "react";
1347
+ import { Plus as Plus2, Bell } from "lucide-react";
1348
+ import { Fragment as Fragment7, jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
1349
+ function SettingsIconSmall({ className }) {
1350
+ return /* @__PURE__ */ jsxs11("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, children: [
1351
+ /* @__PURE__ */ jsx13("title", { children: "Settings" }),
1352
+ /* @__PURE__ */ jsx13("path", { d: "M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z" }),
1353
+ /* @__PURE__ */ jsx13("circle", { cx: "12", cy: "12", r: "3" })
1354
+ ] });
1355
+ }
1356
+ function MenuIcon({ className }) {
1357
+ return /* @__PURE__ */ jsxs11("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, children: [
1358
+ /* @__PURE__ */ jsx13("title", { children: "Menu icon" }),
1359
+ /* @__PURE__ */ jsx13("line", { x1: "4", x2: "20", y1: "12", y2: "12" }),
1360
+ /* @__PURE__ */ jsx13("line", { x1: "4", x2: "20", y1: "6", y2: "6" }),
1361
+ /* @__PURE__ */ jsx13("line", { x1: "4", x2: "20", y1: "18", y2: "18" })
1362
+ ] });
1363
+ }
1364
+ function XIcon({ className }) {
1365
+ return /* @__PURE__ */ jsxs11("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, children: [
1366
+ /* @__PURE__ */ jsx13("title", { children: "Close icon" }),
1367
+ /* @__PURE__ */ jsx13("path", { d: "M18 6 6 18" }),
1368
+ /* @__PURE__ */ jsx13("path", { d: "m6 6 12 12" })
1369
+ ] });
1370
+ }
1371
+ function formatNotifDate(raw) {
1372
+ const d = new Date(raw);
1373
+ return Number.isNaN(d.getTime()) ? raw : d.toLocaleString(void 0, { month: "short", day: "numeric", hour: "2-digit", minute: "2-digit" });
1374
+ }
1375
+ function DefaultLink2({
1376
+ href,
1377
+ to,
1378
+ className,
1379
+ children,
1380
+ ...rest
1381
+ }) {
1382
+ return /* @__PURE__ */ jsx13("a", { href: href ?? to, className, ...rest, children });
1383
+ }
1384
+ function DashboardLayoutInner({
1385
+ children,
1386
+ variant = "sandbox",
1387
+ navItems,
1388
+ modeItems = [],
1389
+ panels = [],
1390
+ activeNavId,
1391
+ user,
1392
+ isLoading = false,
1393
+ onLogout,
1394
+ onSettingsClick,
1395
+ settingsHref = "/dashboard/settings",
1396
+ onNewSandbox,
1397
+ className,
1398
+ sidebarClassName,
1399
+ contentClassName,
1400
+ topNavLinks,
1401
+ activeTopNavHref,
1402
+ LinkComponent = DefaultLink2,
1403
+ footer,
1404
+ railFooter,
1405
+ profileMenuItems,
1406
+ notifications: notifData
1407
+ }) {
1408
+ const Link = LinkComponent;
1409
+ const [mobileMenuOpen, setMobileMenuOpen] = React4.useState(false);
1410
+ const [notificationsOpen, setNotificationsOpen] = React4.useState(false);
1411
+ const notifRef = React4.useRef(null);
1412
+ React4.useEffect(() => {
1413
+ if (!notificationsOpen) return;
1414
+ const handler = (e) => {
1415
+ if (notifRef.current && !notifRef.current.contains(e.target)) {
1416
+ setNotificationsOpen(false);
1417
+ }
1418
+ };
1419
+ const keyHandler = (e) => {
1420
+ if (e.key === "Escape") setNotificationsOpen(false);
1421
+ };
1422
+ document.addEventListener("mousedown", handler);
1423
+ document.addEventListener("keydown", keyHandler);
1424
+ return () => {
1425
+ document.removeEventListener("mousedown", handler);
1426
+ document.removeEventListener("keydown", keyHandler);
1427
+ };
1428
+ }, [notificationsOpen]);
1429
+ const { contentMargin, hidden, mode, hasPanels, panelOpen } = useSidebar();
1430
+ const modeSet = React4.useMemo(() => new Set(modeItems), [modeItems]);
1431
+ const sidebarUser = React4.useMemo(
1432
+ () => user ? { email: user.email, name: user.name, tier: user.tier, avatarUrl: user.avatarUrl } : void 0,
1433
+ [user?.email, user?.name, user?.tier, user?.avatarUrl]
1434
+ );
1435
+ const activePanel = panels.find((p) => p.mode === mode);
1436
+ const buildSidebarContent = React4.useCallback(
1437
+ (showLabels) => /* @__PURE__ */ jsxs11(Fragment7, { children: [
1438
+ /* @__PURE__ */ jsxs11(SidebarRail, { wide: showLabels, children: [
1439
+ /* @__PURE__ */ jsx13(SidebarRailHeader, { children: /* @__PURE__ */ jsx13(Link, { href: "/", to: "/", className: "p-1 rounded-md transition-colors hover:bg-muted/50", children: /* @__PURE__ */ jsx13(Logo, { variant, size: "sm", iconOnly: true }) }) }),
1440
+ /* @__PURE__ */ jsx13(SidebarRailNav, { children: navItems.map((item, i) => {
1441
+ const isMode = modeSet.has(item.id);
1442
+ const prevIsMode = i > 0 && modeSet.has(navItems[i - 1].id);
1443
+ const showSep = i > 0 && isMode && !prevIsMode;
1444
+ return /* @__PURE__ */ jsxs11(React4.Fragment, { children: [
1445
+ showSep && /* @__PURE__ */ jsx13(RailSeparator, {}),
1446
+ isMode ? /* @__PURE__ */ jsx13(
1447
+ RailModeButton,
1448
+ {
1449
+ mode: item.id,
1450
+ icon: item.icon,
1451
+ label: item.label,
1452
+ badge: item.badge,
1453
+ showLabel: showLabels
1454
+ }
1455
+ ) : /* @__PURE__ */ jsx13(Link, { href: item.href, to: item.href, children: /* @__PURE__ */ jsx13(
1456
+ RailButton,
1457
+ {
1458
+ icon: item.icon,
1459
+ label: item.label,
1460
+ isActive: activeNavId === item.id,
1461
+ showLabel: showLabels
1462
+ }
1463
+ ) })
1464
+ ] }, item.id);
1465
+ }) }),
1466
+ /* @__PURE__ */ jsxs11(SidebarRailFooter, { children: [
1467
+ onSettingsClick ? /* @__PURE__ */ jsx13(RailButton, { icon: SettingsIconSmall, label: "Settings", onClick: onSettingsClick, showLabel: showLabels }) : /* @__PURE__ */ jsx13(Link, { href: settingsHref, to: settingsHref, children: /* @__PURE__ */ jsx13(RailButton, { icon: SettingsIconSmall, label: "Settings", showLabel: showLabels }) }),
1468
+ railFooter,
1469
+ /* @__PURE__ */ jsx13(RailSeparator, {}),
1470
+ /* @__PURE__ */ jsx13(
1471
+ ProfileAvatar,
1472
+ {
1473
+ user: sidebarUser,
1474
+ isLoading,
1475
+ onLogout,
1476
+ onSettingsClick,
1477
+ settingsHref,
1478
+ LinkComponent,
1479
+ children: profileMenuItems
1480
+ }
1481
+ )
1482
+ ] })
1483
+ ] }),
1484
+ panels.length > 0 && /* @__PURE__ */ jsxs11(SidebarPanel, { children: [
1485
+ /* @__PURE__ */ jsx13(SidebarPanelHeader, { title: activePanel?.title ?? mode }),
1486
+ /* @__PURE__ */ jsx13(SidebarPanelContent, { children: activePanel?.content })
1487
+ ] })
1488
+ ] }),
1489
+ // biome-ignore lint/correctness/useExhaustiveDependencies: intentional — only the inputs that actually affect the sidebar tree
1490
+ [
1491
+ Link,
1492
+ variant,
1493
+ navItems,
1494
+ modeSet,
1495
+ activeNavId,
1496
+ onSettingsClick,
1497
+ settingsHref,
1498
+ railFooter,
1499
+ sidebarUser,
1500
+ isLoading,
1501
+ onLogout,
1502
+ LinkComponent,
1503
+ profileMenuItems,
1504
+ panels,
1505
+ activePanel,
1506
+ mode
1507
+ ]
1508
+ );
1509
+ const sidebarContent = React4.useMemo(() => buildSidebarContent(false), [buildSidebarContent]);
1510
+ const mobileSidebarContent = React4.useMemo(() => buildSidebarContent(true), [buildSidebarContent]);
1511
+ return /* @__PURE__ */ jsxs11("div", { className: cn("min-h-screen bg-background text-foreground", className), children: [
1512
+ /* @__PURE__ */ jsxs11(
1513
+ "nav",
1514
+ {
1515
+ className: "fixed top-0 z-50 bg-card border-b border-border flex justify-between items-center px-8 h-14 font-sans text-[13px] tracking-tight transition-[left,width] duration-200 ease-in-out",
1516
+ style: {
1517
+ left: hidden ? 0 : contentMargin,
1518
+ width: hidden ? "100%" : `calc(100% - ${contentMargin}px)`
1519
+ },
1520
+ children: [
1521
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-8", children: [
1522
+ /* @__PURE__ */ jsx13(Link, { href: "/", to: "/", className: "lg:hidden flex items-center p-1 rounded-md hover:bg-muted/50 transition-colors", children: /* @__PURE__ */ jsx13(Logo, { variant, size: "sm", iconOnly: true }) }),
1523
+ topNavLinks && topNavLinks.length > 0 && /* @__PURE__ */ jsx13("div", { className: "hidden md:flex gap-6", children: topNavLinks.map((link) => /* @__PURE__ */ jsx13(
1524
+ Link,
1525
+ {
1526
+ href: link.href,
1527
+ to: link.href,
1528
+ className: cn(
1529
+ "transition-all duration-300 px-2 py-1 rounded",
1530
+ activeTopNavHref === link.href ? "text-foreground border-b-2 border-primary pb-1" : "text-muted-foreground hover:text-foreground hover:bg-muted/50"
1531
+ ),
1532
+ children: link.label
1533
+ },
1534
+ link.href
1535
+ )) })
1536
+ ] }),
1537
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-4", children: [
1538
+ onNewSandbox && /* @__PURE__ */ jsxs11(
1539
+ "button",
1540
+ {
1541
+ type: "button",
1542
+ onClick: onNewSandbox,
1543
+ className: "hidden md:flex items-center gap-2 bg-[var(--btn-primary-bg)] border border-[var(--border-accent)] text-[var(--btn-primary-text)] px-4 py-2 rounded-lg font-bold hover:bg-[var(--btn-primary-hover)] transition-all active:scale-95 text-xs",
1544
+ children: [
1545
+ /* @__PURE__ */ jsx13(Plus2, { className: "h-3.5 w-3.5" }),
1546
+ "New Sandbox"
1547
+ ]
1548
+ }
1549
+ ),
1550
+ /* @__PURE__ */ jsxs11("div", { className: "relative", ref: notifRef, children: [
1551
+ /* @__PURE__ */ jsxs11(
1552
+ "button",
1553
+ {
1554
+ type: "button",
1555
+ className: "relative text-muted-foreground hover:text-foreground transition-colors p-2 rounded-lg hover:bg-muted/50",
1556
+ onClick: () => setNotificationsOpen(!notificationsOpen),
1557
+ "aria-label": "Notifications",
1558
+ "aria-expanded": notificationsOpen,
1559
+ children: [
1560
+ /* @__PURE__ */ jsx13(Bell, { className: "h-4 w-4" }),
1561
+ (notifData?.unreadCount ?? 0) > 0 && /* @__PURE__ */ jsx13("span", { className: "absolute top-1 right-1 h-2 w-2 rounded-full bg-destructive" })
1562
+ ]
1563
+ }
1564
+ ),
1565
+ notificationsOpen && /* @__PURE__ */ jsxs11("div", { className: "absolute right-0 top-full mt-2 w-80 rounded-lg border border-border bg-card shadow-lg z-50", children: [
1566
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-center justify-between border-b border-border px-4 py-3", children: [
1567
+ /* @__PURE__ */ jsx13("p", { className: "font-bold text-foreground text-sm", children: "Notifications" }),
1568
+ (notifData?.unreadCount ?? 0) > 0 && notifData?.onMarkAllRead && /* @__PURE__ */ jsx13(
1569
+ "button",
1570
+ {
1571
+ type: "button",
1572
+ onClick: () => {
1573
+ notifData.onMarkAllRead?.();
1574
+ },
1575
+ className: "text-primary text-xs font-medium hover:underline",
1576
+ children: "Mark all read"
1577
+ }
1578
+ )
1579
+ ] }),
1580
+ !notifData?.items || notifData.items.length === 0 ? /* @__PURE__ */ jsxs11("div", { className: "flex flex-col items-center justify-center px-4 py-8 text-center", children: [
1581
+ /* @__PURE__ */ jsx13(Bell, { className: "h-8 w-8 text-muted-foreground/40 mb-2" }),
1582
+ /* @__PURE__ */ jsx13("p", { className: "text-muted-foreground text-sm", children: "No notifications yet" }),
1583
+ /* @__PURE__ */ jsx13("p", { className: "text-muted-foreground/60 text-xs mt-1", children: "We'll notify you about important updates" })
1584
+ ] }) : /* @__PURE__ */ jsx13("div", { className: "max-h-80 overflow-y-auto", children: notifData.items.map((n) => /* @__PURE__ */ jsxs11(
1585
+ "button",
1586
+ {
1587
+ type: "button",
1588
+ className: cn(
1589
+ "w-full text-left px-4 py-3 border-b border-border last:border-0 transition-colors",
1590
+ n.read ? "cursor-default" : "bg-primary/5 hover:bg-muted/50"
1591
+ ),
1592
+ onClick: () => {
1593
+ if (!n.read) notifData.onMarkRead?.(n.id);
1594
+ },
1595
+ children: [
1596
+ /* @__PURE__ */ jsx13("p", { className: cn("text-sm", !n.read ? "font-semibold text-foreground" : "text-muted-foreground"), children: n.title }),
1597
+ /* @__PURE__ */ jsx13("p", { className: "text-xs text-muted-foreground mt-0.5 line-clamp-2", children: n.message }),
1598
+ /* @__PURE__ */ jsx13("p", { className: "text-[10px] text-muted-foreground/50 mt-1", children: formatNotifDate(n.createdAt) })
1599
+ ]
1600
+ },
1601
+ n.id
1602
+ )) })
1603
+ ] })
1604
+ ] })
1605
+ ] }),
1606
+ /* @__PURE__ */ jsx13(
1607
+ "button",
1608
+ {
1609
+ type: "button",
1610
+ onClick: () => setMobileMenuOpen(!mobileMenuOpen),
1611
+ className: "rounded-md p-2 hover:bg-muted/50 lg:hidden",
1612
+ "aria-label": mobileMenuOpen ? "Close menu" : "Open menu",
1613
+ "aria-expanded": mobileMenuOpen,
1614
+ children: mobileMenuOpen ? /* @__PURE__ */ jsx13(XIcon, { className: "h-6 w-6" }) : /* @__PURE__ */ jsx13(MenuIcon, { className: "h-6 w-6" })
1615
+ }
1616
+ )
1617
+ ]
1618
+ }
1619
+ ),
1620
+ mobileMenuOpen && /* @__PURE__ */ jsx13("div", { className: "fixed inset-0 z-30 bg-black/50 lg:hidden", onClick: () => setMobileMenuOpen(false), "aria-hidden": "true" }),
1621
+ /* @__PURE__ */ jsx13(
1622
+ "aside",
1623
+ {
1624
+ className: cn(
1625
+ "fixed top-14 bottom-0 left-0 z-30 flex bg-background transition-transform duration-200 lg:hidden",
1626
+ mobileMenuOpen ? "translate-x-0" : "-translate-x-full"
1627
+ ),
1628
+ style: {
1629
+ width: panelOpen && hasPanels ? SIDEBAR_MOBILE_WIDTH + SIDEBAR_PANEL_WIDTH : SIDEBAR_MOBILE_WIDTH
1630
+ },
1631
+ children: mobileSidebarContent
1632
+ }
1633
+ ),
1634
+ /* @__PURE__ */ jsx13(Sidebar, { className: cn("hidden lg:flex", sidebarClassName), children: sidebarContent }),
1635
+ /* @__PURE__ */ jsx13(SidebarContent, { className: cn("pt-16 px-6 pb-8 lg:px-8 bg-background", contentClassName), children }),
1636
+ footer
1637
+ ] });
1638
+ }
1639
+ function DashboardLayout({ defaultPanelOpen, defaultMode, ...props }) {
1640
+ return /* @__PURE__ */ jsx13(SidebarProvider, { defaultPanelOpen, defaultMode, hasPanels: (props.panels?.length ?? 0) > 0, children: /* @__PURE__ */ jsx13(DashboardLayoutInner, { defaultPanelOpen, defaultMode, ...props }) });
1641
+ }
1642
+
1643
+ // src/dashboard/profile-selector.tsx
1644
+ import { Check as Check2, ChevronDown as ChevronDown3, Plus as Plus3, Settings } from "lucide-react";
1645
+ import { Button } from "@tangle-network/ui/primitives";
1646
+ import { Badge } from "@tangle-network/ui/primitives";
1647
+ import {
1648
+ DropdownMenu as DropdownMenu3,
1649
+ DropdownMenuContent as DropdownMenuContent3,
1650
+ DropdownMenuItem as DropdownMenuItem3,
1651
+ DropdownMenuLabel as DropdownMenuLabel2,
1652
+ DropdownMenuSeparator as DropdownMenuSeparator3,
1653
+ DropdownMenuTrigger as DropdownMenuTrigger3
1654
+ } from "@tangle-network/ui/primitives";
1655
+ import { Fragment as Fragment8, jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
1656
+ function ProfileSelector({
1657
+ profiles,
1658
+ selectedId,
1659
+ onSelect,
1660
+ onCreateClick,
1661
+ onManageClick,
1662
+ label = "Profile",
1663
+ placeholder = "Default (no custom profile)",
1664
+ showMetrics = true,
1665
+ className
1666
+ }) {
1667
+ const selected = profiles.find((p) => p.id === selectedId);
1668
+ const builtinProfiles = profiles.filter((p) => p.is_builtin);
1669
+ const customProfiles = profiles.filter((p) => !p.is_builtin);
1670
+ return /* @__PURE__ */ jsxs12("div", { className, children: [
1671
+ label && /* @__PURE__ */ jsx14("label", { className: "mb-2 block font-medium text-sm", children: label }),
1672
+ /* @__PURE__ */ jsxs12(DropdownMenu3, { children: [
1673
+ /* @__PURE__ */ jsx14(DropdownMenuTrigger3, { asChild: true, children: /* @__PURE__ */ jsxs12(Button, { variant: "outline", className: "w-full justify-between", children: [
1674
+ /* @__PURE__ */ jsx14("span", { className: "truncate", children: selected ? selected.name : placeholder }),
1675
+ /* @__PURE__ */ jsx14(ChevronDown3, { className: "ml-2 h-4 w-4 shrink-0 opacity-50" })
1676
+ ] }) }),
1677
+ /* @__PURE__ */ jsxs12(DropdownMenuContent3, { className: "w-[300px]", align: "start", children: [
1678
+ /* @__PURE__ */ jsxs12(
1679
+ DropdownMenuItem3,
1680
+ {
1681
+ onClick: () => onSelect(null),
1682
+ className: "flex items-center justify-between",
1683
+ children: [
1684
+ /* @__PURE__ */ jsx14("span", { children: placeholder }),
1685
+ !selectedId && /* @__PURE__ */ jsx14(Check2, { className: "h-4 w-4 text-[var(--surface-success-text)]" })
1686
+ ]
1687
+ }
1688
+ ),
1689
+ builtinProfiles.length > 0 && /* @__PURE__ */ jsxs12(Fragment8, { children: [
1690
+ /* @__PURE__ */ jsx14(DropdownMenuSeparator3, {}),
1691
+ /* @__PURE__ */ jsx14(DropdownMenuLabel2, { children: "Built-in Profiles" }),
1692
+ builtinProfiles.map((profile) => /* @__PURE__ */ jsxs12(
1693
+ DropdownMenuItem3,
1694
+ {
1695
+ onClick: () => onSelect(profile),
1696
+ className: "flex flex-col items-start gap-1",
1697
+ children: [
1698
+ /* @__PURE__ */ jsxs12("div", { className: "flex w-full items-center justify-between", children: [
1699
+ /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-2", children: [
1700
+ /* @__PURE__ */ jsx14("span", { className: "font-medium", children: profile.name }),
1701
+ profile.extends && /* @__PURE__ */ jsxs12(Badge, { variant: "secondary", className: "border-0 text-xs", children: [
1702
+ "extends ",
1703
+ profile.extends
1704
+ ] })
1705
+ ] }),
1706
+ selectedId === profile.id && /* @__PURE__ */ jsx14(Check2, { className: "h-4 w-4 text-[var(--surface-success-text)]" })
1707
+ ] }),
1708
+ profile.description && /* @__PURE__ */ jsx14("span", { className: "line-clamp-1 text-muted-foreground text-xs", children: profile.description })
1709
+ ]
1710
+ },
1711
+ profile.id
1712
+ ))
1713
+ ] }),
1714
+ customProfiles.length > 0 && /* @__PURE__ */ jsxs12(Fragment8, { children: [
1715
+ /* @__PURE__ */ jsx14(DropdownMenuSeparator3, {}),
1716
+ /* @__PURE__ */ jsx14(DropdownMenuLabel2, { children: "Custom Profiles" }),
1717
+ customProfiles.map((profile) => /* @__PURE__ */ jsxs12(
1718
+ DropdownMenuItem3,
1719
+ {
1720
+ onClick: () => onSelect(profile),
1721
+ className: "flex flex-col items-start gap-1",
1722
+ children: [
1723
+ /* @__PURE__ */ jsxs12("div", { className: "flex w-full items-center justify-between", children: [
1724
+ /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-2", children: [
1725
+ /* @__PURE__ */ jsx14("span", { className: "font-medium", children: profile.name }),
1726
+ profile.model && /* @__PURE__ */ jsx14(Badge, { variant: "secondary", className: "border-0 text-xs", children: profile.model.split("/").pop() })
1727
+ ] }),
1728
+ selectedId === profile.id && /* @__PURE__ */ jsx14(Check2, { className: "h-4 w-4 text-[var(--surface-success-text)]" })
1729
+ ] }),
1730
+ profile.description && /* @__PURE__ */ jsx14("span", { className: "line-clamp-1 text-muted-foreground text-xs", children: profile.description }),
1731
+ showMetrics && profile.metrics && profile.metrics.total_runs > 0 && /* @__PURE__ */ jsxs12("div", { className: "flex gap-3 text-muted-foreground text-xs", children: [
1732
+ /* @__PURE__ */ jsxs12("span", { children: [
1733
+ profile.metrics.total_runs,
1734
+ " runs"
1735
+ ] }),
1736
+ /* @__PURE__ */ jsxs12("span", { children: [
1737
+ profile.metrics.success_rate.toFixed(0),
1738
+ "% success"
1739
+ ] }),
1740
+ /* @__PURE__ */ jsxs12("span", { children: [
1741
+ "~",
1742
+ (profile.metrics.avg_duration_ms / 1e3).toFixed(1),
1743
+ "s avg"
1744
+ ] })
1745
+ ] })
1746
+ ]
1747
+ },
1748
+ profile.id
1749
+ ))
1750
+ ] }),
1751
+ (onCreateClick || onManageClick) && /* @__PURE__ */ jsxs12(Fragment8, { children: [
1752
+ /* @__PURE__ */ jsx14(DropdownMenuSeparator3, {}),
1753
+ onCreateClick && /* @__PURE__ */ jsxs12(
1754
+ DropdownMenuItem3,
1755
+ {
1756
+ onClick: onCreateClick,
1757
+ className: "text-[var(--surface-info-text)]",
1758
+ children: [
1759
+ /* @__PURE__ */ jsx14(Plus3, { className: "mr-2 h-4 w-4" }),
1760
+ "Create New Profile"
1761
+ ]
1762
+ }
1763
+ ),
1764
+ onManageClick && /* @__PURE__ */ jsxs12(
1765
+ DropdownMenuItem3,
1766
+ {
1767
+ onClick: onManageClick,
1768
+ className: "text-muted-foreground",
1769
+ children: [
1770
+ /* @__PURE__ */ jsx14(Settings, { className: "mr-2 h-4 w-4" }),
1771
+ "Manage Profiles"
1772
+ ]
1773
+ }
1774
+ )
1775
+ ] })
1776
+ ] })
1777
+ ] })
1778
+ ] });
1779
+ }
1780
+ function ProfileComparison({
1781
+ profiles,
1782
+ className
1783
+ }) {
1784
+ const profilesWithMetrics = profiles.filter(
1785
+ (p) => p.metrics && p.metrics.total_runs > 0
1786
+ );
1787
+ if (profilesWithMetrics.length === 0) {
1788
+ return null;
1789
+ }
1790
+ const bestSuccess = profilesWithMetrics.reduce(
1791
+ (best, p) => (p.metrics?.success_rate ?? 0) > (best.metrics?.success_rate ?? 0) ? p : best
1792
+ );
1793
+ const fastestProfile = profilesWithMetrics.reduce(
1794
+ (best, p) => (p.metrics?.avg_duration_ms ?? Number.POSITIVE_INFINITY) < (best.metrics?.avg_duration_ms ?? Number.POSITIVE_INFINITY) ? p : best
1795
+ );
1796
+ return /* @__PURE__ */ jsxs12("div", { className: `rounded-lg border border-border p-4 ${className ?? ""}`, children: [
1797
+ /* @__PURE__ */ jsx14("h4", { className: "mb-3 font-medium text-sm", children: "Profile Performance" }),
1798
+ /* @__PURE__ */ jsx14("div", { className: "space-y-3", children: profilesWithMetrics.map((profile) => {
1799
+ const isBestSuccess = profile.id === bestSuccess.id;
1800
+ const isFastest = profile.id === fastestProfile.id;
1801
+ return /* @__PURE__ */ jsxs12(
1802
+ "div",
1803
+ {
1804
+ className: "flex items-center justify-between gap-4",
1805
+ children: [
1806
+ /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-2", children: [
1807
+ /* @__PURE__ */ jsx14("span", { className: "font-medium", children: profile.name }),
1808
+ isBestSuccess && /* @__PURE__ */ jsx14(Badge, { className: "border border-[var(--surface-success-border)] bg-[var(--surface-success-bg)] text-[var(--surface-success-text)] text-xs", children: "Best Success" }),
1809
+ isFastest && !isBestSuccess && /* @__PURE__ */ jsx14(Badge, { className: "border border-[var(--surface-info-border)] bg-[var(--surface-info-bg)] text-[var(--surface-info-text)] text-xs", children: "Fastest" })
1810
+ ] }),
1811
+ /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-4 text-sm", children: [
1812
+ /* @__PURE__ */ jsxs12("span", { children: [
1813
+ /* @__PURE__ */ jsx14("span", { className: "text-muted-foreground", children: "Success:" }),
1814
+ " ",
1815
+ /* @__PURE__ */ jsxs12(
1816
+ "span",
1817
+ {
1818
+ className: (profile.metrics?.success_rate ?? 0) >= 80 ? "text-[var(--surface-success-text)]" : (profile.metrics?.success_rate ?? 0) >= 50 ? "text-[var(--surface-warning-text)]" : "text-[var(--surface-danger-text)]",
1819
+ children: [
1820
+ profile.metrics?.success_rate.toFixed(0),
1821
+ "%"
1822
+ ]
1823
+ }
1824
+ )
1825
+ ] }),
1826
+ /* @__PURE__ */ jsxs12("span", { children: [
1827
+ /* @__PURE__ */ jsx14("span", { className: "text-muted-foreground", children: "Avg:" }),
1828
+ " ",
1829
+ ((profile.metrics?.avg_duration_ms ?? 0) / 1e3).toFixed(1),
1830
+ "s"
1831
+ ] }),
1832
+ /* @__PURE__ */ jsxs12("span", { className: "text-muted-foreground", children: [
1833
+ profile.metrics?.total_runs,
1834
+ " runs"
1835
+ ] })
1836
+ ] })
1837
+ ]
1838
+ },
1839
+ profile.id
1840
+ );
1841
+ }) })
1842
+ ] });
1843
+ }
1844
+
1845
+ // src/dashboard/variant-list.tsx
1846
+ import {
1847
+ Check as Check3,
1848
+ CheckCircle2,
1849
+ Clock as Clock2,
1850
+ ExternalLink,
1851
+ Loader2 as Loader22,
1852
+ Timer,
1853
+ X,
1854
+ XCircle
1855
+ } from "lucide-react";
1856
+ import { Button as Button2 } from "@tangle-network/ui/primitives";
1857
+ import { Badge as Badge2 } from "@tangle-network/ui/primitives";
1858
+ import { Fragment as Fragment9, jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
1859
+ var statusConfig = {
1860
+ pending: {
1861
+ icon: Clock2,
1862
+ color: "text-[var(--surface-warning-text)]",
1863
+ bg: "bg-[var(--surface-warning-bg)]",
1864
+ border: "border-[var(--surface-warning-border)]",
1865
+ label: "Pending",
1866
+ animate: false
1867
+ },
1868
+ running: {
1869
+ icon: Loader22,
1870
+ color: "text-primary",
1871
+ bg: "bg-[var(--accent-surface-soft)]",
1872
+ border: "border-border",
1873
+ label: "Running",
1874
+ animate: true
1875
+ },
1876
+ completed: {
1877
+ icon: CheckCircle2,
1878
+ color: "text-[var(--surface-success-text)]",
1879
+ bg: "bg-[var(--surface-success-bg)]",
1880
+ border: "border-[var(--surface-success-border)]",
1881
+ label: "Completed",
1882
+ animate: false
1883
+ },
1884
+ failed: {
1885
+ icon: XCircle,
1886
+ color: "text-[var(--surface-danger-text)]",
1887
+ bg: "bg-[var(--surface-danger-bg)]",
1888
+ border: "border-[var(--surface-danger-border)]",
1889
+ label: "Failed",
1890
+ animate: false
1891
+ },
1892
+ cancelled: {
1893
+ icon: XCircle,
1894
+ color: "text-[var(--surface-neutral-text)]",
1895
+ bg: "bg-[var(--surface-neutral-bg)]",
1896
+ border: "border-[var(--surface-neutral-border)]",
1897
+ label: "Cancelled",
1898
+ animate: false
1899
+ }
1900
+ };
1901
+ var outcomeConfig = {
1902
+ pending_review: {
1903
+ color: "text-[var(--surface-warning-text)]",
1904
+ bg: "bg-[var(--surface-warning-bg)]",
1905
+ border: "border-[var(--surface-warning-border)]",
1906
+ label: "Pending Review"
1907
+ },
1908
+ accepted: {
1909
+ color: "text-[var(--surface-success-text)]",
1910
+ bg: "bg-[var(--surface-success-bg)]",
1911
+ border: "border-[var(--surface-success-border)]",
1912
+ label: "Accepted"
1913
+ },
1914
+ rejected: {
1915
+ color: "text-[var(--surface-danger-text)]",
1916
+ bg: "bg-[var(--surface-danger-bg)]",
1917
+ border: "border-[var(--surface-danger-border)]",
1918
+ label: "Rejected"
1919
+ },
1920
+ merged_with_conflicts: {
1921
+ color: "text-[var(--surface-orange-text)]",
1922
+ bg: "bg-[var(--surface-orange-bg)]",
1923
+ border: "border-[var(--surface-orange-border)]",
1924
+ label: "Merged (conflicts)"
1925
+ },
1926
+ expired: {
1927
+ color: "text-[var(--surface-neutral-text)]",
1928
+ bg: "bg-[var(--surface-neutral-bg)]",
1929
+ border: "border-[var(--surface-neutral-border)]",
1930
+ label: "Expired"
1931
+ }
1932
+ };
1933
+ function VariantList({
1934
+ variants,
1935
+ selectedId,
1936
+ onSelect,
1937
+ onAccept,
1938
+ onReject,
1939
+ isActioning,
1940
+ className
1941
+ }) {
1942
+ return /* @__PURE__ */ jsx15("div", { className: `space-y-2 ${className || ""}`, children: variants.map((variant) => {
1943
+ const status = statusConfig[variant.status];
1944
+ const StatusIcon = status.icon;
1945
+ const isSelected = variant.id === selectedId;
1946
+ return /* @__PURE__ */ jsxs13(
1947
+ "div",
1948
+ {
1949
+ className: `cursor-pointer rounded-lg border px-3 py-2.5 transition-colors ${isSelected ? "border-primary/30 bg-[var(--accent-surface-soft)]" : "border-border bg-card hover:border-primary/20 hover:bg-muted/50"}`,
1950
+ onClick: () => onSelect?.(variant.id),
1951
+ children: [
1952
+ /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-2", children: [
1953
+ /* @__PURE__ */ jsxs13(Badge2, { className: `shrink-0 ${status.bg} ${status.border} ${status.color}`, children: [
1954
+ /* @__PURE__ */ jsx15(
1955
+ StatusIcon,
1956
+ {
1957
+ className: `mr-1 h-3 w-3 ${status.animate ? "animate-spin" : ""}`
1958
+ }
1959
+ ),
1960
+ status.label
1961
+ ] }),
1962
+ /* @__PURE__ */ jsx15("span", { className: "truncate text-sm font-medium text-foreground", children: variant.label }),
1963
+ variant.sublabel && /* @__PURE__ */ jsxs13("span", { className: "shrink-0 text-xs text-muted-foreground", children: [
1964
+ "(",
1965
+ variant.sublabel,
1966
+ ")"
1967
+ ] }),
1968
+ variant.durationMs && /* @__PURE__ */ jsxs13("span", { className: "flex shrink-0 items-center gap-1 text-xs text-muted-foreground", children: [
1969
+ /* @__PURE__ */ jsx15(Timer, { className: "h-3 w-3" }),
1970
+ (variant.durationMs / 1e3).toFixed(1),
1971
+ "s"
1972
+ ] }),
1973
+ /* @__PURE__ */ jsxs13("div", { className: "ml-auto flex shrink-0 items-center gap-1.5", children: [
1974
+ variant.outcome && /* @__PURE__ */ jsx15(
1975
+ Badge2,
1976
+ {
1977
+ className: `${outcomeConfig[variant.outcome].bg} ${outcomeConfig[variant.outcome].border} ${outcomeConfig[variant.outcome].color}`,
1978
+ children: outcomeConfig[variant.outcome].label
1979
+ }
1980
+ ),
1981
+ variant.status === "completed" && variant.outcome === "pending_review" && onAccept && onReject && /* @__PURE__ */ jsxs13(Fragment9, { children: [
1982
+ /* @__PURE__ */ jsxs13(
1983
+ Button2,
1984
+ {
1985
+ variant: "outline",
1986
+ size: "sm",
1987
+ className: "h-7 border-[var(--surface-success-border)] bg-[var(--surface-success-bg)] px-2 text-xs text-[var(--surface-success-text)] hover:bg-[var(--surface-success-border)]",
1988
+ onClick: (e) => {
1989
+ e.stopPropagation();
1990
+ onAccept(variant.id);
1991
+ },
1992
+ disabled: isActioning === variant.id,
1993
+ children: [
1994
+ /* @__PURE__ */ jsx15(Check3, { className: "mr-1 h-3 w-3" }),
1995
+ "Accept"
1996
+ ]
1997
+ }
1998
+ ),
1999
+ /* @__PURE__ */ jsxs13(
2000
+ Button2,
2001
+ {
2002
+ variant: "outline",
2003
+ size: "sm",
2004
+ className: "h-7 border-[var(--surface-danger-border)] bg-[var(--surface-danger-bg)] px-2 text-xs text-[var(--surface-danger-text)] hover:bg-[var(--surface-danger-border)]",
2005
+ onClick: (e) => {
2006
+ e.stopPropagation();
2007
+ onReject(variant.id);
2008
+ },
2009
+ disabled: isActioning === variant.id,
2010
+ children: [
2011
+ /* @__PURE__ */ jsx15(X, { className: "mr-1 h-3 w-3" }),
2012
+ "Reject"
2013
+ ]
2014
+ }
2015
+ )
2016
+ ] }),
2017
+ variant.detailsUrl && /* @__PURE__ */ jsx15(
2018
+ Button2,
2019
+ {
2020
+ variant: "ghost",
2021
+ size: "sm",
2022
+ className: "h-7 w-7 p-0 text-muted-foreground hover:text-foreground",
2023
+ onClick: (e) => {
2024
+ e.stopPropagation();
2025
+ window.open(variant.detailsUrl, "_blank");
2026
+ },
2027
+ children: /* @__PURE__ */ jsx15(ExternalLink, { className: "h-3.5 w-3.5" })
2028
+ }
2029
+ )
2030
+ ] })
2031
+ ] }),
2032
+ variant.error && /* @__PURE__ */ jsx15("p", { className: "mt-1.5 text-xs text-[var(--surface-danger-text)]", children: variant.error }),
2033
+ variant.summary && /* @__PURE__ */ jsx15("p", { className: "mt-1.5 line-clamp-2 text-xs text-muted-foreground", children: variant.summary })
2034
+ ]
2035
+ },
2036
+ variant.id
2037
+ );
2038
+ }) });
2039
+ }
2040
+
2041
+ // src/dashboard/system-logs.tsx
2042
+ import { Terminal as Terminal3 } from "lucide-react";
2043
+ import { useEffect as useEffect2, useRef as useRef2, useState as useState5 } from "react";
2044
+ import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
2045
+ function SystemLogsViewer({ apiUrl, token, className }) {
2046
+ const [logs, setLogs] = useState5([]);
2047
+ const [error, setError] = useState5(null);
2048
+ const [isFollowing, setIsFollowing] = useState5(true);
2049
+ const scrollRef = useRef2(null);
2050
+ useEffect2(() => {
2051
+ let timeoutId;
2052
+ let backoff = 2e3;
2053
+ const controller = new AbortController();
2054
+ async function fetchLogs() {
2055
+ try {
2056
+ const res = await fetch(`${apiUrl}/debug/logs`, {
2057
+ headers: {
2058
+ Authorization: `Bearer ${token}`
2059
+ },
2060
+ signal: controller.signal
2061
+ });
2062
+ if (!res.ok) throw new Error(`HTTP Error: ${res.status}`);
2063
+ const data = await res.json();
2064
+ if (!controller.signal.aborted) {
2065
+ setLogs(data.logs || []);
2066
+ setError(null);
2067
+ backoff = 2e3;
2068
+ }
2069
+ } catch (err) {
2070
+ if (err.name === "AbortError") return;
2071
+ if (!controller.signal.aborted) {
2072
+ setError(err instanceof Error ? err.message : "Failed to fetch logs");
2073
+ backoff = Math.min(backoff * 2, 3e4);
2074
+ }
2075
+ }
2076
+ if (!controller.signal.aborted) {
2077
+ timeoutId = setTimeout(fetchLogs, backoff);
2078
+ }
2079
+ }
2080
+ fetchLogs();
2081
+ return () => {
2082
+ controller.abort();
2083
+ clearTimeout(timeoutId);
2084
+ };
2085
+ }, [apiUrl, token]);
2086
+ useEffect2(() => {
2087
+ if (isFollowing && scrollRef.current) {
2088
+ scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
2089
+ }
2090
+ }, [logs, isFollowing]);
2091
+ const handleScroll = () => {
2092
+ if (!scrollRef.current) return;
2093
+ const { scrollTop, scrollHeight, clientHeight } = scrollRef.current;
2094
+ const isAtBottom = scrollHeight - scrollTop - clientHeight < 20;
2095
+ setIsFollowing((prev) => prev === isAtBottom ? prev : isAtBottom);
2096
+ };
2097
+ return /* @__PURE__ */ jsxs14("div", { className: cn("flex flex-col h-full bg-background text-foreground font-mono text-sm leading-relaxed overflow-hidden rounded-lg border border-border", className), children: [
2098
+ /* @__PURE__ */ jsxs14("div", { className: "flex-none flex items-center justify-between border-b border-border bg-muted/50 backdrop-blur-md px-4 py-2", children: [
2099
+ /* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-2", children: [
2100
+ /* @__PURE__ */ jsx16(Terminal3, { className: "h-4 w-4 text-primary animate-pulse" }),
2101
+ /* @__PURE__ */ jsx16("span", { className: "font-bold text-xs uppercase tracking-widest text-muted-foreground", children: "System Traces" })
2102
+ ] }),
2103
+ /* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-3", children: [
2104
+ error && /* @__PURE__ */ jsxs14("span", { className: "text-destructive text-xs flex items-center gap-1", children: [
2105
+ /* @__PURE__ */ jsx16("span", { className: "w-2 h-2 rounded-full bg-destructive animate-ping" }),
2106
+ " Error fetching logs"
2107
+ ] }),
2108
+ /* @__PURE__ */ jsx16(
2109
+ "button",
2110
+ {
2111
+ onClick: () => {
2112
+ setIsFollowing(!isFollowing);
2113
+ if (!isFollowing && scrollRef.current) {
2114
+ scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
2115
+ }
2116
+ },
2117
+ className: cn("px-3 py-1 text-[10px] font-bold uppercase rounded-md transition-colors", isFollowing ? "bg-primary/20 text-primary border border-primary/20" : "bg-muted text-muted-foreground border border-border hover:bg-accent hover:text-foreground"),
2118
+ children: isFollowing ? "Auto-Scroll ON" : "Auto-Scroll OFF"
2119
+ }
2120
+ )
2121
+ ] })
2122
+ ] }),
2123
+ /* @__PURE__ */ jsx16(
2124
+ "div",
2125
+ {
2126
+ ref: scrollRef,
2127
+ onScroll: handleScroll,
2128
+ className: "flex-1 overflow-y-auto p-4 space-y-1",
2129
+ children: logs.length === 0 && !error ? /* @__PURE__ */ jsx16("div", { className: "flex h-full items-center justify-center text-muted-foreground italic", children: "Waiting for orchestrator logs..." }) : logs.map((log, i) => /* @__PURE__ */ jsxs14("div", { className: "break-words", children: [
2130
+ /* @__PURE__ */ jsxs14("span", { className: "text-muted-foreground mr-3 select-none", children: [
2131
+ "[",
2132
+ log.timestamp || i.toString().padStart(4, "0"),
2133
+ "]"
2134
+ ] }),
2135
+ /* @__PURE__ */ jsxs14("span", { className: "text-primary/70 mr-2", children: [
2136
+ "[",
2137
+ log.level,
2138
+ "]"
2139
+ ] }),
2140
+ /* @__PURE__ */ jsxs14("span", { className: "text-muted-foreground mr-2", children: [
2141
+ "[",
2142
+ log.scope,
2143
+ "]"
2144
+ ] }),
2145
+ /* @__PURE__ */ jsx16("span", { className: log.level.toUpperCase() === "ERROR" || log.message.toLowerCase().includes("failed") ? "text-destructive" : log.level.toUpperCase() === "WARN" ? "text-warning" : "text-foreground", children: log.message })
2146
+ ] }, `${log.timestamp}-${log.scope}-${i}`))
2147
+ }
2148
+ )
2149
+ ] });
2150
+ }
2151
+
2152
+ // src/dashboard/usage-summary.tsx
2153
+ import { Clock as Clock3, Layers, MessageSquare, DollarSign } from "lucide-react";
2154
+ import { StatCard } from "@tangle-network/ui/primitives";
2155
+ import { Skeleton as Skeleton2 } from "@tangle-network/ui/primitives";
2156
+ import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
2157
+ function UsageSummary({ data, loading = false, className }) {
2158
+ if (loading || !data) {
2159
+ return /* @__PURE__ */ jsx17("div", { className: cn("grid grid-cols-2 gap-4 lg:grid-cols-4", className), children: Array.from({ length: 4 }).map((_, i) => /* @__PURE__ */ jsx17(Skeleton2, { className: "h-28 rounded-xl" }, i)) });
2160
+ }
2161
+ return /* @__PURE__ */ jsxs15("div", { className: cn("grid grid-cols-2 gap-4 lg:grid-cols-4", className), children: [
2162
+ /* @__PURE__ */ jsx17(
2163
+ StatCard,
2164
+ {
2165
+ variant: "sandbox",
2166
+ title: "Compute Hours",
2167
+ value: data.computeHours.toFixed(1),
2168
+ subtitle: "This billing period",
2169
+ icon: /* @__PURE__ */ jsx17(Clock3, { className: "h-5 w-5" })
2170
+ }
2171
+ ),
2172
+ /* @__PURE__ */ jsx17(
2173
+ StatCard,
2174
+ {
2175
+ variant: "sandbox",
2176
+ title: "Active Sessions",
2177
+ value: data.activeSessions,
2178
+ subtitle: "Currently running",
2179
+ icon: /* @__PURE__ */ jsx17(Layers, { className: "h-5 w-5" })
2180
+ }
2181
+ ),
2182
+ /* @__PURE__ */ jsx17(
2183
+ StatCard,
2184
+ {
2185
+ variant: "sandbox",
2186
+ title: "Messages Sent",
2187
+ value: data.messagesSent.toLocaleString(),
2188
+ subtitle: "Agent interactions",
2189
+ icon: /* @__PURE__ */ jsx17(MessageSquare, { className: "h-5 w-5" })
2190
+ }
2191
+ ),
2192
+ /* @__PURE__ */ jsx17(
2193
+ StatCard,
2194
+ {
2195
+ variant: "sandbox",
2196
+ title: "Estimated Cost",
2197
+ value: `$${data.estimatedCost.toFixed(2)}`,
2198
+ subtitle: "This billing period",
2199
+ icon: /* @__PURE__ */ jsx17(DollarSign, { className: "h-5 w-5" })
2200
+ }
2201
+ )
2202
+ ] });
2203
+ }
2204
+
2205
+ // src/dashboard/git-panel.tsx
2206
+ import { GitBranch, GitCommit, FileEdit, FilePlus, File } from "lucide-react";
2207
+ import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
2208
+ function GitPanel({ status, log, loading = false, onRefresh, className }) {
2209
+ if (loading) {
2210
+ return /* @__PURE__ */ jsx18("div", { className: cn("rounded-lg border border-border bg-card p-5", className), children: /* @__PURE__ */ jsxs16("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
2211
+ /* @__PURE__ */ jsx18("div", { className: "h-4 w-4 animate-spin rounded-full border-2 border-primary border-t-transparent" }),
2212
+ "Loading git info..."
2213
+ ] }) });
2214
+ }
2215
+ if (!status) {
2216
+ return /* @__PURE__ */ jsx18("div", { className: cn("rounded-lg border border-border bg-card p-5", className), children: /* @__PURE__ */ jsxs16("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
2217
+ /* @__PURE__ */ jsx18(GitBranch, { className: "h-4 w-4" }),
2218
+ "No git repository detected"
2219
+ ] }) });
2220
+ }
2221
+ const changedCount = status.staged.length + status.modified.length + status.untracked.length;
2222
+ return /* @__PURE__ */ jsxs16("div", { className: cn("rounded-lg border border-border bg-card overflow-hidden", className), children: [
2223
+ /* @__PURE__ */ jsxs16("div", { className: "flex items-center justify-between border-b border-border bg-muted/30 px-4 py-3", children: [
2224
+ /* @__PURE__ */ jsxs16("div", { className: "flex items-center gap-2", children: [
2225
+ /* @__PURE__ */ jsx18(GitBranch, { className: "h-4 w-4 text-primary" }),
2226
+ /* @__PURE__ */ jsx18("span", { className: "text-sm font-bold text-foreground", children: status.branch }),
2227
+ status.isDirty && /* @__PURE__ */ jsxs16("span", { className: "rounded-full bg-[var(--surface-warning-bg)] px-1.5 py-0.5 text-[10px] font-bold text-[var(--surface-warning-text)]", children: [
2228
+ changedCount,
2229
+ " change",
2230
+ changedCount !== 1 ? "s" : ""
2231
+ ] })
2232
+ ] }),
2233
+ onRefresh && /* @__PURE__ */ jsx18("button", { type: "button", onClick: onRefresh, className: "text-xs text-muted-foreground hover:text-foreground transition-colors", children: "Refresh" })
2234
+ ] }),
2235
+ changedCount > 0 && /* @__PURE__ */ jsx18("div", { className: "border-b border-border px-4 py-3", children: /* @__PURE__ */ jsxs16("div", { className: "space-y-1.5", children: [
2236
+ status.staged.map((f) => /* @__PURE__ */ jsxs16("div", { className: "flex items-center gap-2 text-xs", children: [
2237
+ /* @__PURE__ */ jsx18(FilePlus, { className: "h-3 w-3 text-[var(--surface-success-text)]" }),
2238
+ /* @__PURE__ */ jsx18("span", { className: "font-mono text-foreground truncate", children: f }),
2239
+ /* @__PURE__ */ jsx18("span", { className: "text-[var(--surface-success-text)] text-[10px] font-bold ml-auto", children: "STAGED" })
2240
+ ] }, `s-${f}`)),
2241
+ status.modified.map((f) => /* @__PURE__ */ jsxs16("div", { className: "flex items-center gap-2 text-xs", children: [
2242
+ /* @__PURE__ */ jsx18(FileEdit, { className: "h-3 w-3 text-[var(--surface-warning-text)]" }),
2243
+ /* @__PURE__ */ jsx18("span", { className: "font-mono text-foreground truncate", children: f })
2244
+ ] }, `m-${f}`)),
2245
+ status.untracked.map((f) => /* @__PURE__ */ jsxs16("div", { className: "flex items-center gap-2 text-xs", children: [
2246
+ /* @__PURE__ */ jsx18(File, { className: "h-3 w-3 text-muted-foreground" }),
2247
+ /* @__PURE__ */ jsx18("span", { className: "font-mono text-muted-foreground truncate", children: f })
2248
+ ] }, `u-${f}`))
2249
+ ] }) }),
2250
+ log.length > 0 && /* @__PURE__ */ jsxs16("div", { className: "px-4 py-3", children: [
2251
+ /* @__PURE__ */ jsx18("div", { className: "mb-2 text-[10px] font-bold uppercase tracking-widest text-muted-foreground", children: "Recent Commits" }),
2252
+ /* @__PURE__ */ jsx18("div", { className: "space-y-2", children: log.slice(0, 5).map((commit) => /* @__PURE__ */ jsxs16("div", { className: "flex items-start gap-2 text-xs", children: [
2253
+ /* @__PURE__ */ jsx18(GitCommit, { className: "h-3 w-3 text-muted-foreground mt-0.5 shrink-0" }),
2254
+ /* @__PURE__ */ jsxs16("div", { className: "min-w-0", children: [
2255
+ /* @__PURE__ */ jsx18("span", { className: "font-mono text-primary mr-1.5", children: commit.shortSha }),
2256
+ /* @__PURE__ */ jsx18("span", { className: "text-foreground", children: commit.message })
2257
+ ] })
2258
+ ] }, commit.shortSha)) })
2259
+ ] }),
2260
+ (status.ahead > 0 || status.behind > 0) && /* @__PURE__ */ jsxs16("div", { className: "border-t border-border px-4 py-2 flex items-center gap-3 text-xs text-muted-foreground", children: [
2261
+ status.ahead > 0 && /* @__PURE__ */ jsxs16("span", { children: [
2262
+ "\u2191 ",
2263
+ status.ahead,
2264
+ " ahead"
2265
+ ] }),
2266
+ status.behind > 0 && /* @__PURE__ */ jsxs16("span", { children: [
2267
+ "\u2193 ",
2268
+ status.behind,
2269
+ " behind"
2270
+ ] })
2271
+ ] })
2272
+ ] });
2273
+ }
2274
+
2275
+ // src/dashboard/ports-list.tsx
2276
+ import * as React6 from "react";
2277
+ import { Copy as Copy2, Check as Check4, Globe, Plus as Plus4, Trash2 as Trash23 } from "lucide-react";
2278
+ import { jsx as jsx19, jsxs as jsxs17 } from "react/jsx-runtime";
2279
+ function PortsList({ ports, onExposePort, onRemovePort, isExposing = false, className }) {
2280
+ const [newPort, setNewPort] = React6.useState("");
2281
+ const [copiedPort, setCopiedPort] = React6.useState(null);
2282
+ const copyTimerRef = React6.useRef(null);
2283
+ React6.useEffect(() => {
2284
+ return () => {
2285
+ if (copyTimerRef.current) clearTimeout(copyTimerRef.current);
2286
+ };
2287
+ }, []);
2288
+ const handleCopy = async (url, port) => {
2289
+ try {
2290
+ await navigator.clipboard.writeText(url);
2291
+ setCopiedPort(port);
2292
+ if (copyTimerRef.current) clearTimeout(copyTimerRef.current);
2293
+ copyTimerRef.current = setTimeout(() => setCopiedPort(null), 2e3);
2294
+ } catch (err) {
2295
+ console.warn("Clipboard write failed:", err);
2296
+ }
2297
+ };
2298
+ const handleExpose = () => {
2299
+ const port = parseInt(newPort, 10);
2300
+ if (port > 0 && port <= 65535) {
2301
+ onExposePort(port);
2302
+ setNewPort("");
2303
+ }
2304
+ };
2305
+ return /* @__PURE__ */ jsxs17("div", { className: cn("space-y-4", className), children: [
2306
+ ports.length > 0 ? /* @__PURE__ */ jsx19("div", { className: "rounded-lg border border-border overflow-hidden", children: /* @__PURE__ */ jsxs17("table", { className: "w-full text-sm", children: [
2307
+ /* @__PURE__ */ jsx19("thead", { className: "bg-muted/30 border-b border-border", children: /* @__PURE__ */ jsxs17("tr", { children: [
2308
+ /* @__PURE__ */ jsx19("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Port" }),
2309
+ /* @__PURE__ */ jsx19("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Public URL" }),
2310
+ /* @__PURE__ */ jsx19("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Status" }),
2311
+ /* @__PURE__ */ jsx19("th", { className: "px-4 py-2.5 text-right text-xs font-medium text-muted-foreground w-20" })
2312
+ ] }) }),
2313
+ /* @__PURE__ */ jsx19("tbody", { className: "divide-y divide-border", children: ports.map((p) => /* @__PURE__ */ jsxs17("tr", { children: [
2314
+ /* @__PURE__ */ jsx19("td", { className: "px-4 py-3 font-mono text-xs text-foreground", children: p.port }),
2315
+ /* @__PURE__ */ jsx19("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsxs17(
2316
+ "button",
2317
+ {
2318
+ type: "button",
2319
+ onClick: () => handleCopy(p.url, p.port),
2320
+ className: "flex items-center gap-2 font-mono text-xs text-primary hover:underline cursor-pointer group",
2321
+ children: [
2322
+ /* @__PURE__ */ jsx19("span", { className: "truncate max-w-[300px]", children: p.url }),
2323
+ copiedPort === p.port ? /* @__PURE__ */ jsx19(Check4, { className: "h-3 w-3 text-[var(--surface-success-text)] shrink-0" }) : /* @__PURE__ */ jsx19(Copy2, { className: "h-3 w-3 opacity-0 group-hover:opacity-100 transition-opacity shrink-0" })
2324
+ ]
2325
+ }
2326
+ ) }),
2327
+ /* @__PURE__ */ jsx19("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsx19("span", { className: cn(
2328
+ "inline-flex items-center gap-1.5 rounded-full px-2 py-0.5 text-[10px] font-bold uppercase tracking-wider",
2329
+ p.status === "active" ? "bg-[var(--surface-success-bg)] text-[var(--surface-success-text)]" : "bg-[var(--surface-warning-bg)] text-[var(--surface-warning-text)]"
2330
+ ), children: p.status }) }),
2331
+ /* @__PURE__ */ jsx19("td", { className: "px-4 py-3 text-right", children: onRemovePort && /* @__PURE__ */ jsx19(
2332
+ "button",
2333
+ {
2334
+ type: "button",
2335
+ onClick: () => onRemovePort(p.port),
2336
+ className: "p-1 text-muted-foreground hover:text-destructive transition-colors rounded",
2337
+ children: /* @__PURE__ */ jsx19(Trash23, { className: "h-3.5 w-3.5" })
2338
+ }
2339
+ ) })
2340
+ ] }, p.port)) })
2341
+ ] }) }) : /* @__PURE__ */ jsxs17("div", { className: "rounded-lg border border-border bg-muted/20 p-6 text-center", children: [
2342
+ /* @__PURE__ */ jsx19(Globe, { className: "mx-auto h-8 w-8 text-muted-foreground mb-2" }),
2343
+ /* @__PURE__ */ jsx19("p", { className: "text-sm text-muted-foreground", children: "No ports exposed yet" })
2344
+ ] }),
2345
+ /* @__PURE__ */ jsxs17("div", { className: "flex items-center gap-3", children: [
2346
+ /* @__PURE__ */ jsx19(
2347
+ "input",
2348
+ {
2349
+ type: "number",
2350
+ min: 1,
2351
+ max: 65535,
2352
+ placeholder: "Port (e.g. 3000)",
2353
+ value: newPort,
2354
+ onChange: (e) => setNewPort(e.target.value),
2355
+ onKeyDown: (e) => e.key === "Enter" && handleExpose(),
2356
+ className: "flex-1 rounded-lg border border-border bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring"
2357
+ }
2358
+ ),
2359
+ /* @__PURE__ */ jsxs17(
2360
+ "button",
2361
+ {
2362
+ type: "button",
2363
+ onClick: handleExpose,
2364
+ disabled: !newPort || isExposing,
2365
+ className: "inline-flex items-center gap-2 rounded-lg bg-primary/20 border border-primary/30 px-4 py-2 text-sm font-medium text-primary hover:bg-primary hover:text-primary-foreground transition-colors disabled:opacity-50",
2366
+ children: [
2367
+ /* @__PURE__ */ jsx19(Plus4, { className: "h-4 w-4" }),
2368
+ "Expose"
2369
+ ]
2370
+ }
2371
+ )
2372
+ ] })
2373
+ ] });
2374
+ }
2375
+
2376
+ // src/dashboard/process-list.tsx
2377
+ import * as React7 from "react";
2378
+ import { Activity as Activity2, Plus as Plus5, Skull, Terminal as Terminal4 } from "lucide-react";
2379
+ import { jsx as jsx20, jsxs as jsxs18 } from "react/jsx-runtime";
2380
+ function formatUptime(startedAt) {
2381
+ if (!startedAt) return "-";
2382
+ const ms = Date.now() - new Date(startedAt).getTime();
2383
+ if (Number.isNaN(ms) || ms < 0) return "-";
2384
+ if (ms < 6e4) return `${Math.floor(ms / 1e3)}s`;
2385
+ if (ms < 36e5) return `${Math.floor(ms / 6e4)}m`;
2386
+ return `${Math.floor(ms / 36e5)}h ${Math.floor(ms % 36e5 / 6e4)}m`;
2387
+ }
2388
+ function ProcessList({ processes, onSpawn, onKill, loading = false, className }) {
2389
+ const [newCommand, setNewCommand] = React7.useState("");
2390
+ const handleSpawn = () => {
2391
+ const cmd = newCommand.trim();
2392
+ if (cmd) {
2393
+ onSpawn(cmd);
2394
+ setNewCommand("");
2395
+ }
2396
+ };
2397
+ return /* @__PURE__ */ jsxs18("div", { className: cn("space-y-4", className), children: [
2398
+ loading ? /* @__PURE__ */ jsxs18("div", { className: "rounded-lg border border-border bg-muted/20 p-6 text-center", children: [
2399
+ /* @__PURE__ */ jsx20(Activity2, { className: "mx-auto h-6 w-6 text-muted-foreground animate-spin mb-2" }),
2400
+ /* @__PURE__ */ jsx20("p", { className: "text-sm text-muted-foreground", children: "Loading processes..." })
2401
+ ] }) : processes.length > 0 ? /* @__PURE__ */ jsx20("div", { className: "rounded-lg border border-border overflow-hidden", children: /* @__PURE__ */ jsxs18("table", { className: "w-full text-sm", children: [
2402
+ /* @__PURE__ */ jsx20("thead", { className: "bg-muted/30 border-b border-border", children: /* @__PURE__ */ jsxs18("tr", { children: [
2403
+ /* @__PURE__ */ jsx20("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "PID" }),
2404
+ /* @__PURE__ */ jsx20("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Command" }),
2405
+ /* @__PURE__ */ jsx20("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Status" }),
2406
+ /* @__PURE__ */ jsx20("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Uptime" }),
2407
+ /* @__PURE__ */ jsx20("th", { className: "px-4 py-2.5 text-right text-xs font-medium text-muted-foreground w-20" })
2408
+ ] }) }),
2409
+ /* @__PURE__ */ jsx20("tbody", { className: "divide-y divide-border", children: processes.map((p) => /* @__PURE__ */ jsxs18("tr", { children: [
2410
+ /* @__PURE__ */ jsx20("td", { className: "px-4 py-3 font-mono text-xs text-foreground", children: p.pid }),
2411
+ /* @__PURE__ */ jsx20("td", { className: "px-4 py-3 font-mono text-xs text-foreground truncate max-w-[250px]", children: p.command }),
2412
+ /* @__PURE__ */ jsx20("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsx20("span", { className: cn(
2413
+ "inline-flex items-center gap-1.5 rounded-full px-2 py-0.5 text-[10px] font-bold uppercase tracking-wider",
2414
+ p.running ? "bg-[var(--surface-success-bg)] text-[var(--surface-success-text)]" : "bg-muted text-muted-foreground"
2415
+ ), children: p.running ? "running" : `exited (${p.exitCode ?? "?"})` }) }),
2416
+ /* @__PURE__ */ jsx20("td", { className: "px-4 py-3 font-mono text-xs text-muted-foreground", children: formatUptime(p.startedAt) }),
2417
+ /* @__PURE__ */ jsx20("td", { className: "px-4 py-3 text-right", children: p.running && /* @__PURE__ */ jsx20(
2418
+ "button",
2419
+ {
2420
+ type: "button",
2421
+ onClick: () => onKill(p.pid),
2422
+ className: "p-1 text-muted-foreground hover:text-destructive transition-colors rounded",
2423
+ title: "Kill process",
2424
+ children: /* @__PURE__ */ jsx20(Skull, { className: "h-3.5 w-3.5" })
2425
+ }
2426
+ ) })
2427
+ ] }, `${p.pid}-${p.startedAt ?? p.command}`)) })
2428
+ ] }) }) : /* @__PURE__ */ jsxs18("div", { className: "rounded-lg border border-border bg-muted/20 p-6 text-center", children: [
2429
+ /* @__PURE__ */ jsx20(Terminal4, { className: "mx-auto h-8 w-8 text-muted-foreground mb-2" }),
2430
+ /* @__PURE__ */ jsx20("p", { className: "text-sm text-muted-foreground", children: "No processes running" })
2431
+ ] }),
2432
+ /* @__PURE__ */ jsxs18("div", { className: "flex items-center gap-3", children: [
2433
+ /* @__PURE__ */ jsx20(
2434
+ "input",
2435
+ {
2436
+ type: "text",
2437
+ placeholder: "Command (e.g. node server.js)",
2438
+ value: newCommand,
2439
+ onChange: (e) => setNewCommand(e.target.value),
2440
+ onKeyDown: (e) => e.key === "Enter" && handleSpawn(),
2441
+ className: "flex-1 rounded-lg border border-border bg-background px-3 py-2 text-sm font-mono text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring"
2442
+ }
2443
+ ),
2444
+ /* @__PURE__ */ jsxs18(
2445
+ "button",
2446
+ {
2447
+ type: "button",
2448
+ onClick: handleSpawn,
2449
+ disabled: !newCommand.trim(),
2450
+ className: "inline-flex items-center gap-2 rounded-lg bg-primary/20 border border-primary/30 px-4 py-2 text-sm font-medium text-primary hover:bg-primary hover:text-primary-foreground transition-colors disabled:opacity-50",
2451
+ children: [
2452
+ /* @__PURE__ */ jsx20(Plus5, { className: "h-4 w-4" }),
2453
+ "Spawn"
2454
+ ]
2455
+ }
2456
+ )
2457
+ ] })
2458
+ ] });
2459
+ }
2460
+
2461
+ // src/dashboard/network-config.tsx
2462
+ import * as React8 from "react";
2463
+ import { Network as Network2, Plus as Plus6, Trash2 as Trash24, ShieldAlert } from "lucide-react";
2464
+ import { jsx as jsx21, jsxs as jsxs19 } from "react/jsx-runtime";
2465
+ function NetworkConfig({ config, onUpdate, loading = false, className }) {
2466
+ const [newCidr, setNewCidr] = React8.useState("");
2467
+ const isValidCidr = (value) => {
2468
+ const match = value.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\/(\d{1,2})$/);
2469
+ if (!match) return false;
2470
+ const octets = [match[1], match[2], match[3], match[4]].map(Number);
2471
+ const prefix = Number(match[5]);
2472
+ return octets.every((o) => o <= 255) && prefix <= 32;
2473
+ };
2474
+ const handleAddCidr = () => {
2475
+ const cidr = newCidr.trim();
2476
+ if (cidr && config && isValidCidr(cidr) && !config.allowList.includes(cidr)) {
2477
+ onUpdate({ allowList: [...config.allowList, cidr] });
2478
+ setNewCidr("");
2479
+ }
2480
+ };
2481
+ const handleRemoveCidr = (cidr) => {
2482
+ if (config) {
2483
+ onUpdate({ allowList: config.allowList.filter((c) => c !== cidr) });
2484
+ }
2485
+ };
2486
+ if (loading || !config) {
2487
+ return /* @__PURE__ */ jsxs19("div", { className: cn("rounded-lg border border-border bg-muted/20 p-6 text-center", className), children: [
2488
+ /* @__PURE__ */ jsx21(Network2, { className: "mx-auto h-6 w-6 text-muted-foreground animate-pulse mb-2" }),
2489
+ /* @__PURE__ */ jsx21("p", { className: "text-sm text-muted-foreground", children: "Loading network configuration..." })
2490
+ ] });
2491
+ }
2492
+ return /* @__PURE__ */ jsxs19("div", { className: cn("space-y-4", className), children: [
2493
+ /* @__PURE__ */ jsxs19("div", { className: "flex items-center justify-between rounded-lg border border-border bg-card px-4 py-3", children: [
2494
+ /* @__PURE__ */ jsxs19("div", { className: "flex items-center gap-3", children: [
2495
+ /* @__PURE__ */ jsx21(ShieldAlert, { className: "h-4 w-4 text-muted-foreground" }),
2496
+ /* @__PURE__ */ jsxs19("div", { children: [
2497
+ /* @__PURE__ */ jsx21("p", { className: "text-sm font-medium text-foreground", children: "Block Outbound Traffic" }),
2498
+ /* @__PURE__ */ jsx21("p", { className: "text-xs text-muted-foreground", children: "Prevent the sandbox from making external network requests" })
2499
+ ] })
2500
+ ] }),
2501
+ /* @__PURE__ */ jsx21(
2502
+ "button",
2503
+ {
2504
+ type: "button",
2505
+ role: "switch",
2506
+ "aria-checked": config.blockOutbound,
2507
+ "aria-label": "Block outbound traffic",
2508
+ onClick: () => onUpdate({ blockOutbound: !config.blockOutbound }),
2509
+ className: cn(
2510
+ "relative inline-flex h-6 w-11 items-center rounded-full transition-colors",
2511
+ config.blockOutbound ? "bg-destructive" : "bg-muted"
2512
+ ),
2513
+ children: /* @__PURE__ */ jsx21(
2514
+ "span",
2515
+ {
2516
+ className: cn(
2517
+ "inline-block h-4 w-4 rounded-full bg-white transition-transform shadow-sm",
2518
+ config.blockOutbound ? "translate-x-6" : "translate-x-1"
2519
+ )
2520
+ }
2521
+ )
2522
+ }
2523
+ )
2524
+ ] }),
2525
+ /* @__PURE__ */ jsxs19("div", { children: [
2526
+ /* @__PURE__ */ jsx21("h4", { className: "text-xs font-medium text-muted-foreground mb-2 uppercase tracking-wider", children: "Allowlist (CIDR)" }),
2527
+ config.allowList.length > 0 ? /* @__PURE__ */ jsx21("div", { className: "space-y-1.5 mb-3", children: config.allowList.map((cidr) => /* @__PURE__ */ jsxs19("div", { className: "flex items-center justify-between rounded border border-border bg-muted/20 px-3 py-2", children: [
2528
+ /* @__PURE__ */ jsx21("span", { className: "font-mono text-xs text-foreground", children: cidr }),
2529
+ /* @__PURE__ */ jsx21(
2530
+ "button",
2531
+ {
2532
+ type: "button",
2533
+ onClick: () => handleRemoveCidr(cidr),
2534
+ className: "p-1 text-muted-foreground hover:text-destructive transition-colors rounded",
2535
+ children: /* @__PURE__ */ jsx21(Trash24, { className: "h-3 w-3" })
2536
+ }
2537
+ )
2538
+ ] }, cidr)) }) : /* @__PURE__ */ jsx21("p", { className: "text-xs text-muted-foreground mb-3", children: "No CIDR rules configured. All traffic is allowed." }),
2539
+ /* @__PURE__ */ jsxs19("div", { className: "flex items-center gap-3", children: [
2540
+ /* @__PURE__ */ jsx21(
2541
+ "input",
2542
+ {
2543
+ type: "text",
2544
+ placeholder: "CIDR (e.g. 10.0.0.0/8)",
2545
+ value: newCidr,
2546
+ onChange: (e) => setNewCidr(e.target.value),
2547
+ onKeyDown: (e) => e.key === "Enter" && handleAddCidr(),
2548
+ className: "flex-1 rounded-lg border border-border bg-background px-3 py-2 text-sm font-mono text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring"
2549
+ }
2550
+ ),
2551
+ /* @__PURE__ */ jsxs19(
2552
+ "button",
2553
+ {
2554
+ type: "button",
2555
+ onClick: handleAddCidr,
2556
+ disabled: !newCidr.trim(),
2557
+ className: "inline-flex items-center gap-2 rounded-lg bg-primary/20 border border-primary/30 px-4 py-2 text-sm font-medium text-primary hover:bg-primary hover:text-primary-foreground transition-colors disabled:opacity-50",
2558
+ children: [
2559
+ /* @__PURE__ */ jsx21(Plus6, { className: "h-4 w-4" }),
2560
+ "Add"
2561
+ ]
2562
+ }
2563
+ )
2564
+ ] })
2565
+ ] })
2566
+ ] });
2567
+ }
2568
+
2569
+ // src/dashboard/backend-config.tsx
2570
+ import * as React9 from "react";
2571
+ import { Bot, Plus as Plus7, RefreshCw as RefreshCw2, Trash2 as Trash25, Server, Wrench } from "lucide-react";
2572
+ import { Fragment as Fragment10, jsx as jsx22, jsxs as jsxs20 } from "react/jsx-runtime";
2573
+ function BackendConfig({
2574
+ status,
2575
+ mcpServers,
2576
+ onAddMcp,
2577
+ onRemoveMcp,
2578
+ onRestart,
2579
+ loading = false,
2580
+ className
2581
+ }) {
2582
+ const [showAddMcp, setShowAddMcp] = React9.useState(false);
2583
+ const [mcpName, setMcpName] = React9.useState("");
2584
+ const [mcpCommand, setMcpCommand] = React9.useState("");
2585
+ const [mcpArgs, setMcpArgs] = React9.useState("");
2586
+ const handleAddMcp = () => {
2587
+ const name = mcpName.trim();
2588
+ const command = mcpCommand.trim();
2589
+ if (name && command) {
2590
+ onAddMcp({
2591
+ name,
2592
+ command,
2593
+ args: mcpArgs.trim() ? mcpArgs.trim().split(/\s+/) : void 0
2594
+ });
2595
+ setMcpName("");
2596
+ setMcpCommand("");
2597
+ setMcpArgs("");
2598
+ setShowAddMcp(false);
2599
+ }
2600
+ };
2601
+ if (loading || !status) {
2602
+ return /* @__PURE__ */ jsxs20("div", { className: cn("rounded-lg border border-border bg-muted/20 p-6 text-center", className), children: [
2603
+ /* @__PURE__ */ jsx22(Bot, { className: "mx-auto h-6 w-6 text-muted-foreground animate-pulse mb-2" }),
2604
+ /* @__PURE__ */ jsx22("p", { className: "text-sm text-muted-foreground", children: "Loading backend status..." })
2605
+ ] });
2606
+ }
2607
+ return /* @__PURE__ */ jsxs20("div", { className: cn("space-y-4", className), children: [
2608
+ /* @__PURE__ */ jsxs20("div", { className: "rounded-lg border border-border bg-card overflow-hidden", children: [
2609
+ /* @__PURE__ */ jsxs20("div", { className: "px-4 py-3 border-b border-border bg-muted/30 flex items-center justify-between", children: [
2610
+ /* @__PURE__ */ jsx22("h4", { className: "text-xs font-medium text-muted-foreground uppercase tracking-wider", children: "Agent Status" }),
2611
+ /* @__PURE__ */ jsxs20(
2612
+ "button",
2613
+ {
2614
+ type: "button",
2615
+ onClick: onRestart,
2616
+ className: "inline-flex items-center gap-1.5 rounded-md bg-muted px-2.5 py-1 text-xs font-medium text-foreground hover:bg-muted/80 transition-colors border border-border",
2617
+ children: [
2618
+ /* @__PURE__ */ jsx22(RefreshCw2, { className: "h-3 w-3" }),
2619
+ "Restart"
2620
+ ]
2621
+ }
2622
+ )
2623
+ ] }),
2624
+ /* @__PURE__ */ jsx22("div", { className: "p-4", children: /* @__PURE__ */ jsxs20("dl", { className: "grid grid-cols-[100px_1fr] gap-y-3 text-sm", children: [
2625
+ /* @__PURE__ */ jsx22("dt", { className: "text-muted-foreground", children: "Status" }),
2626
+ /* @__PURE__ */ jsx22("dd", { children: /* @__PURE__ */ jsx22("span", { className: cn(
2627
+ "inline-flex items-center gap-1.5 rounded-full px-2 py-0.5 text-[10px] font-bold uppercase tracking-wider",
2628
+ status.running ? "bg-[var(--surface-success-bg)] text-[var(--surface-success-text)]" : "bg-destructive/10 text-destructive"
2629
+ ), children: status.running ? "Running" : "Stopped" }) }),
2630
+ /* @__PURE__ */ jsx22("dt", { className: "text-muted-foreground", children: "Model" }),
2631
+ /* @__PURE__ */ jsx22("dd", { className: "font-mono text-xs", children: status.model ?? "Default" }),
2632
+ status.provider && /* @__PURE__ */ jsxs20(Fragment10, { children: [
2633
+ /* @__PURE__ */ jsx22("dt", { className: "text-muted-foreground", children: "Provider" }),
2634
+ /* @__PURE__ */ jsx22("dd", { className: "font-mono text-xs", children: status.provider })
2635
+ ] })
2636
+ ] }) })
2637
+ ] }),
2638
+ /* @__PURE__ */ jsxs20("div", { className: "rounded-lg border border-border bg-card overflow-hidden", children: [
2639
+ /* @__PURE__ */ jsxs20("div", { className: "px-4 py-3 border-b border-border bg-muted/30 flex items-center justify-between", children: [
2640
+ /* @__PURE__ */ jsxs20("h4", { className: "text-xs font-medium text-muted-foreground uppercase tracking-wider flex items-center gap-2", children: [
2641
+ /* @__PURE__ */ jsx22(Wrench, { className: "h-3.5 w-3.5" }),
2642
+ "MCP Servers"
2643
+ ] }),
2644
+ /* @__PURE__ */ jsxs20(
2645
+ "button",
2646
+ {
2647
+ type: "button",
2648
+ onClick: () => setShowAddMcp(!showAddMcp),
2649
+ className: "inline-flex items-center gap-1.5 rounded-md bg-primary/20 border border-primary/30 px-2.5 py-1 text-xs font-medium text-primary hover:bg-primary hover:text-primary-foreground transition-colors",
2650
+ children: [
2651
+ /* @__PURE__ */ jsx22(Plus7, { className: "h-3 w-3" }),
2652
+ "Add"
2653
+ ]
2654
+ }
2655
+ )
2656
+ ] }),
2657
+ mcpServers.length > 0 ? /* @__PURE__ */ jsx22("div", { className: "divide-y divide-border", children: mcpServers.map((s) => /* @__PURE__ */ jsxs20("div", { className: "flex items-center justify-between px-4 py-3", children: [
2658
+ /* @__PURE__ */ jsxs20("div", { className: "flex items-center gap-3 min-w-0", children: [
2659
+ /* @__PURE__ */ jsx22(Server, { className: "h-3.5 w-3.5 text-muted-foreground shrink-0" }),
2660
+ /* @__PURE__ */ jsxs20("div", { className: "min-w-0", children: [
2661
+ /* @__PURE__ */ jsx22("p", { className: "text-sm font-medium text-foreground truncate", children: s.name }),
2662
+ /* @__PURE__ */ jsxs20("p", { className: "text-xs font-mono text-muted-foreground truncate", children: [
2663
+ s.command,
2664
+ " ",
2665
+ s.args?.join(" ") ?? ""
2666
+ ] })
2667
+ ] })
2668
+ ] }),
2669
+ /* @__PURE__ */ jsxs20("div", { className: "flex items-center gap-2 shrink-0", children: [
2670
+ s.status && /* @__PURE__ */ jsx22("span", { className: cn(
2671
+ "inline-flex items-center rounded-full px-1.5 py-0.5 text-[9px] font-bold uppercase",
2672
+ s.status === "running" ? "bg-[var(--surface-success-bg)] text-[var(--surface-success-text)]" : s.status === "error" ? "bg-destructive/10 text-destructive" : "bg-muted text-muted-foreground"
2673
+ ), children: s.status }),
2674
+ /* @__PURE__ */ jsx22(
2675
+ "button",
2676
+ {
2677
+ type: "button",
2678
+ onClick: () => onRemoveMcp(s.name),
2679
+ className: "p-1 text-muted-foreground hover:text-destructive transition-colors rounded",
2680
+ children: /* @__PURE__ */ jsx22(Trash25, { className: "h-3.5 w-3.5" })
2681
+ }
2682
+ )
2683
+ ] })
2684
+ ] }, s.name)) }) : /* @__PURE__ */ jsx22("div", { className: "p-4 text-center", children: /* @__PURE__ */ jsx22("p", { className: "text-xs text-muted-foreground", children: "No MCP servers configured" }) }),
2685
+ showAddMcp && /* @__PURE__ */ jsxs20("div", { className: "p-4 border-t border-border bg-muted/10 space-y-2", children: [
2686
+ /* @__PURE__ */ jsx22(
2687
+ "input",
2688
+ {
2689
+ type: "text",
2690
+ placeholder: "Server name",
2691
+ value: mcpName,
2692
+ onChange: (e) => setMcpName(e.target.value),
2693
+ className: "w-full rounded-lg border border-border bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring"
2694
+ }
2695
+ ),
2696
+ /* @__PURE__ */ jsx22(
2697
+ "input",
2698
+ {
2699
+ type: "text",
2700
+ placeholder: "Command (e.g. npx @server/mcp)",
2701
+ value: mcpCommand,
2702
+ onChange: (e) => setMcpCommand(e.target.value),
2703
+ className: "w-full rounded-lg border border-border bg-background px-3 py-2 text-sm font-mono text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring"
2704
+ }
2705
+ ),
2706
+ /* @__PURE__ */ jsx22(
2707
+ "input",
2708
+ {
2709
+ type: "text",
2710
+ placeholder: "Arguments (space-separated, optional)",
2711
+ value: mcpArgs,
2712
+ onChange: (e) => setMcpArgs(e.target.value),
2713
+ className: "w-full rounded-lg border border-border bg-background px-3 py-2 text-sm font-mono text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring"
2714
+ }
2715
+ ),
2716
+ /* @__PURE__ */ jsxs20("div", { className: "flex justify-end gap-2 pt-1", children: [
2717
+ /* @__PURE__ */ jsx22(
2718
+ "button",
2719
+ {
2720
+ type: "button",
2721
+ onClick: () => setShowAddMcp(false),
2722
+ className: "rounded-md px-3 py-1.5 text-xs font-medium text-muted-foreground hover:text-foreground transition-colors",
2723
+ children: "Cancel"
2724
+ }
2725
+ ),
2726
+ /* @__PURE__ */ jsx22(
2727
+ "button",
2728
+ {
2729
+ type: "button",
2730
+ onClick: handleAddMcp,
2731
+ disabled: !mcpName.trim() || !mcpCommand.trim(),
2732
+ className: "rounded-md bg-primary px-3 py-1.5 text-xs font-medium text-primary-foreground hover:bg-primary/90 transition-colors disabled:opacity-50",
2733
+ children: "Add Server"
2734
+ }
2735
+ )
2736
+ ] })
2737
+ ] })
2738
+ ] })
2739
+ ] });
2740
+ }
2741
+
2742
+ // src/dashboard/snapshot-list.tsx
2743
+ import * as React10 from "react";
2744
+ import { Camera, Clock as Clock4, HardDrive, Plus as Plus8, RotateCcw } from "lucide-react";
2745
+ import { jsx as jsx23, jsxs as jsxs21 } from "react/jsx-runtime";
2746
+ function formatBytes(bytes) {
2747
+ if (bytes == null || bytes < 0) return "-";
2748
+ if (bytes === 0) return "0 B";
2749
+ const k = 1024;
2750
+ const sizes = ["B", "KB", "MB", "GB", "TB"];
2751
+ const i = Math.min(Math.floor(Math.log(bytes) / Math.log(k)), sizes.length - 1);
2752
+ return `${Number.parseFloat((bytes / k ** i).toFixed(1))} ${sizes[i]}`;
2753
+ }
2754
+ function formatDate(dateStr) {
2755
+ const d = new Date(dateStr);
2756
+ if (Number.isNaN(d.getTime())) return dateStr;
2757
+ return d.toLocaleDateString(void 0, { month: "short", day: "numeric", hour: "2-digit", minute: "2-digit" });
2758
+ }
2759
+ function SnapshotList({ snapshots, onCreate, onRestore, onSaveAsTemplate, loading = false, className }) {
2760
+ const [showCreate, setShowCreate] = React10.useState(false);
2761
+ const [tags, setTags] = React10.useState("");
2762
+ const handleCreate = () => {
2763
+ const tagList = tags.trim() ? tags.trim().split(",").map((t) => t.trim()).filter(Boolean) : void 0;
2764
+ onCreate(tagList);
2765
+ setTags("");
2766
+ setShowCreate(false);
2767
+ };
2768
+ return /* @__PURE__ */ jsxs21("div", { className: cn("space-y-4", className), children: [
2769
+ /* @__PURE__ */ jsxs21("div", { className: "flex items-center justify-between", children: [
2770
+ /* @__PURE__ */ jsx23("h3", { className: "text-sm font-bold text-foreground", children: "Snapshots" }),
2771
+ /* @__PURE__ */ jsxs21(
2772
+ "button",
2773
+ {
2774
+ type: "button",
2775
+ onClick: () => setShowCreate(!showCreate),
2776
+ className: "inline-flex items-center gap-1.5 rounded-lg bg-primary/20 border border-primary/30 px-3 py-1.5 text-xs font-medium text-primary hover:bg-primary hover:text-primary-foreground transition-colors",
2777
+ children: [
2778
+ /* @__PURE__ */ jsx23(Plus8, { className: "h-3.5 w-3.5" }),
2779
+ "Create Snapshot"
2780
+ ]
2781
+ }
2782
+ )
2783
+ ] }),
2784
+ showCreate && /* @__PURE__ */ jsxs21("div", { className: "rounded-lg border border-primary/20 bg-primary/5 p-4 space-y-3", children: [
2785
+ /* @__PURE__ */ jsx23("p", { className: "text-sm text-foreground font-medium", children: "Create a new snapshot of the current sandbox state." }),
2786
+ /* @__PURE__ */ jsx23(
2787
+ "input",
2788
+ {
2789
+ type: "text",
2790
+ placeholder: "Tags (comma-separated, optional)",
2791
+ value: tags,
2792
+ onChange: (e) => setTags(e.target.value),
2793
+ onKeyDown: (e) => e.key === "Enter" && handleCreate(),
2794
+ className: "w-full rounded-lg border border-border bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring"
2795
+ }
2796
+ ),
2797
+ /* @__PURE__ */ jsxs21("div", { className: "flex justify-end gap-2", children: [
2798
+ /* @__PURE__ */ jsx23(
2799
+ "button",
2800
+ {
2801
+ type: "button",
2802
+ onClick: () => setShowCreate(false),
2803
+ className: "rounded-md px-3 py-1.5 text-xs font-medium text-muted-foreground hover:text-foreground transition-colors",
2804
+ children: "Cancel"
2805
+ }
2806
+ ),
2807
+ /* @__PURE__ */ jsxs21(
2808
+ "button",
2809
+ {
2810
+ type: "button",
2811
+ onClick: handleCreate,
2812
+ className: "rounded-md bg-primary px-3 py-1.5 text-xs font-medium text-primary-foreground hover:bg-primary/90 transition-colors",
2813
+ children: [
2814
+ /* @__PURE__ */ jsx23(Camera, { className: "h-3 w-3 mr-1.5 inline" }),
2815
+ "Create"
2816
+ ]
2817
+ }
2818
+ )
2819
+ ] })
2820
+ ] }),
2821
+ loading ? /* @__PURE__ */ jsxs21("div", { className: "rounded-lg border border-border bg-muted/20 p-6 text-center", children: [
2822
+ /* @__PURE__ */ jsx23(Camera, { className: "mx-auto h-6 w-6 text-muted-foreground animate-pulse mb-2" }),
2823
+ /* @__PURE__ */ jsx23("p", { className: "text-sm text-muted-foreground", children: "Loading snapshots..." })
2824
+ ] }) : snapshots.length > 0 ? /* @__PURE__ */ jsx23("div", { className: "rounded-lg border border-border overflow-hidden", children: /* @__PURE__ */ jsxs21("table", { className: "w-full text-sm", children: [
2825
+ /* @__PURE__ */ jsx23("thead", { className: "bg-muted/30 border-b border-border", children: /* @__PURE__ */ jsxs21("tr", { children: [
2826
+ /* @__PURE__ */ jsx23("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "ID" }),
2827
+ /* @__PURE__ */ jsx23("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Created" }),
2828
+ /* @__PURE__ */ jsx23("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Size" }),
2829
+ /* @__PURE__ */ jsx23("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Tags" }),
2830
+ /* @__PURE__ */ jsx23("th", { className: "px-4 py-2.5 text-right text-xs font-medium text-muted-foreground w-24" })
2831
+ ] }) }),
2832
+ /* @__PURE__ */ jsx23("tbody", { className: "divide-y divide-border", children: snapshots.map((s) => /* @__PURE__ */ jsxs21("tr", { children: [
2833
+ /* @__PURE__ */ jsx23("td", { className: "px-4 py-3 font-mono text-xs text-foreground", children: s.id.slice(0, 12) }),
2834
+ /* @__PURE__ */ jsx23("td", { className: "px-4 py-3 text-xs text-muted-foreground", children: /* @__PURE__ */ jsxs21("span", { className: "inline-flex items-center gap-1.5", children: [
2835
+ /* @__PURE__ */ jsx23(Clock4, { className: "h-3 w-3" }),
2836
+ formatDate(s.createdAt)
2837
+ ] }) }),
2838
+ /* @__PURE__ */ jsx23("td", { className: "px-4 py-3 text-xs text-muted-foreground", children: /* @__PURE__ */ jsxs21("span", { className: "inline-flex items-center gap-1.5", children: [
2839
+ /* @__PURE__ */ jsx23(HardDrive, { className: "h-3 w-3" }),
2840
+ formatBytes(s.sizeBytes)
2841
+ ] }) }),
2842
+ /* @__PURE__ */ jsx23("td", { className: "px-4 py-3", children: s.tags?.length ? /* @__PURE__ */ jsx23("div", { className: "flex items-center gap-1 flex-wrap", children: s.tags.map((tag) => /* @__PURE__ */ jsx23("span", { className: "rounded-full bg-muted px-2 py-0.5 text-[10px] font-medium text-muted-foreground border border-border", children: tag }, tag)) }) : /* @__PURE__ */ jsx23("span", { className: "text-xs text-muted-foreground", children: "-" }) }),
2843
+ /* @__PURE__ */ jsx23("td", { className: "px-4 py-3 text-right", children: /* @__PURE__ */ jsxs21("div", { className: "flex items-center justify-end gap-2", children: [
2844
+ /* @__PURE__ */ jsxs21(
2845
+ "button",
2846
+ {
2847
+ type: "button",
2848
+ onClick: () => onRestore(s.id),
2849
+ className: "inline-flex items-center gap-1.5 rounded-md bg-muted px-2.5 py-1 text-xs font-medium text-foreground hover:bg-muted/80 transition-colors border border-border",
2850
+ title: "Restore to new sandbox",
2851
+ children: [
2852
+ /* @__PURE__ */ jsx23(RotateCcw, { className: "h-3 w-3" }),
2853
+ "Restore"
2854
+ ]
2855
+ }
2856
+ ),
2857
+ onSaveAsTemplate && /* @__PURE__ */ jsx23(
2858
+ "button",
2859
+ {
2860
+ type: "button",
2861
+ onClick: () => onSaveAsTemplate(s.id),
2862
+ className: "inline-flex items-center gap-1.5 rounded-md bg-primary/10 px-2.5 py-1 text-xs font-medium text-primary hover:bg-primary/20 transition-colors border border-primary/20",
2863
+ title: "Save as reusable template",
2864
+ children: "Save as Template"
2865
+ }
2866
+ )
2867
+ ] }) })
2868
+ ] }, s.id)) })
2869
+ ] }) }) : /* @__PURE__ */ jsxs21("div", { className: "rounded-lg border border-border bg-muted/20 p-6 text-center", children: [
2870
+ /* @__PURE__ */ jsx23(Camera, { className: "mx-auto h-8 w-8 text-muted-foreground mb-2" }),
2871
+ /* @__PURE__ */ jsx23("p", { className: "text-sm text-muted-foreground", children: "No snapshots yet" }),
2872
+ /* @__PURE__ */ jsx23("p", { className: "text-xs text-muted-foreground mt-1", children: "Create a snapshot to save the current state of your sandbox." })
2873
+ ] })
2874
+ ] });
2875
+ }
2876
+
2877
+ // src/dashboard/promo-banner.tsx
2878
+ import { Fragment as Fragment11, jsx as jsx24, jsxs as jsxs22 } from "react/jsx-runtime";
2879
+ function PromoBanner({
2880
+ title,
2881
+ description,
2882
+ buttonLabel,
2883
+ href,
2884
+ onClick,
2885
+ icon,
2886
+ disabled = false,
2887
+ className
2888
+ }) {
2889
+ const buttonClasses = cn(
2890
+ "mt-6 inline-flex items-center gap-2 rounded-md border border-white/20 bg-[var(--btn-primary-bg)] px-4 py-2 text-sm font-medium text-[var(--btn-primary-text)] transition-colors",
2891
+ disabled ? "opacity-50 cursor-not-allowed" : "hover:bg-[var(--btn-primary-hover)]"
2892
+ );
2893
+ const buttonContent = /* @__PURE__ */ jsxs22(Fragment11, { children: [
2894
+ buttonLabel,
2895
+ /* @__PURE__ */ jsxs22("svg", { "aria-hidden": "true", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "h-4 w-4", children: [
2896
+ /* @__PURE__ */ jsx24("path", { d: "M5 12h14" }),
2897
+ /* @__PURE__ */ jsx24("path", { d: "m12 5 7 7-7 7" })
2898
+ ] })
2899
+ ] });
2900
+ return /* @__PURE__ */ jsxs22("div", { className: cn("relative overflow-hidden rounded-xl bg-[var(--brand-strong)] p-8 md:flex md:items-center md:justify-between", className), children: [
2901
+ /* @__PURE__ */ jsxs22("div", { className: "relative z-10", children: [
2902
+ /* @__PURE__ */ jsx24("h3", { className: "text-xl font-bold text-[var(--brand-strong-text)]", children: title }),
2903
+ /* @__PURE__ */ jsx24("p", { className: "mt-2 max-w-md text-sm text-[var(--brand-strong-text-muted)]", children: description }),
2904
+ href && !disabled ? /* @__PURE__ */ jsx24("a", { href, target: "_blank", rel: "noopener noreferrer", onClick, className: buttonClasses, children: buttonContent }) : /* @__PURE__ */ jsx24("button", { type: "button", onClick, disabled, className: buttonClasses, children: buttonContent })
2905
+ ] }),
2906
+ icon && /* @__PURE__ */ jsx24("div", { className: "relative z-10 mt-6 flex items-center md:mt-0", children: /* @__PURE__ */ jsx24("div", { className: "flex h-16 w-16 items-center justify-center rounded-2xl border border-white/10 bg-white/10", children: icon }) }),
2907
+ /* @__PURE__ */ jsx24("div", { className: "pointer-events-none absolute inset-y-0 right-0 w-1/3 bg-gradient-to-l from-white/5 to-transparent" })
2908
+ ] });
2909
+ }
2910
+
2911
+ export {
2912
+ SIDEBAR_RAIL_WIDTH,
2913
+ SIDEBAR_PANEL_WIDTH,
2914
+ SIDEBAR_TOTAL_WIDTH,
2915
+ SIDEBAR_MOBILE_WIDTH,
2916
+ SidebarProvider,
2917
+ useSidebar,
2918
+ Sidebar,
2919
+ SidebarRail,
2920
+ SidebarRailHeader,
2921
+ SidebarRailNav,
2922
+ SidebarRailFooter,
2923
+ RailSeparator,
2924
+ RailButton,
2925
+ RailModeButton,
2926
+ SidebarPanel,
2927
+ SidebarPanelHeader,
2928
+ SidebarPanelContent,
2929
+ SidebarContent,
2930
+ ProfileAvatar,
2931
+ CreditBalance,
2932
+ ClusterStatusBar,
2933
+ ResourceMeter,
2934
+ canAdminSandbox,
2935
+ SandboxCard,
2936
+ NewSandboxCard,
2937
+ SandboxTable,
2938
+ InvoiceTable,
2939
+ PlanCards,
2940
+ BackendSelector,
2941
+ HARNESS_OPTIONS,
2942
+ HarnessPicker,
2943
+ canonicalModelId,
2944
+ formatPricing,
2945
+ formatContext,
2946
+ ModelPicker,
2947
+ DashboardLayout,
2948
+ ProfileSelector,
2949
+ ProfileComparison,
2950
+ VariantList,
2951
+ SystemLogsViewer,
2952
+ UsageSummary,
2953
+ GitPanel,
2954
+ PortsList,
2955
+ ProcessList,
2956
+ NetworkConfig,
2957
+ BackendConfig,
2958
+ SnapshotList,
2959
+ PromoBanner
2960
+ };