@tangle-network/sandbox-ui 0.14.0 → 0.15.2

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 (106) 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-AZ3AWMTM.js +8 -0
  10. package/dist/chunk-CMY7W45U.js +380 -0
  11. package/dist/chunk-EI44GEQ5.js +6 -0
  12. package/dist/chunk-ENMWGVDL.js +858 -0
  13. package/dist/{chunk-5OQ27N57.js → chunk-GPT7VKK6.js} +34 -38
  14. package/dist/chunk-HLZTKSGT.js +2652 -0
  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 +450 -4
  22. package/dist/dashboard.js +20 -891
  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 +251 -256
  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 +13 -12
  37. package/dist/pages.js +91 -115
  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/template-card-DStb8boW.d.ts +183 -0
  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-QMU2PWOU.js +0 -493
  85. package/dist/chunk-RQHJBTEU.js +0 -10
  86. package/dist/chunk-T7HMZEVO.js +0 -216
  87. package/dist/chunk-U6QTHMY6.js +0 -1290
  88. package/dist/chunk-US6JKJKH.js +0 -124
  89. package/dist/chunk-VX3XOUEB.js +0 -63
  90. package/dist/chunk-XLG757B6.js +0 -933
  91. package/dist/chunk-ZMNSRDMH.js +0 -127
  92. package/dist/chunk-ZNCEM5CD.js +0 -316
  93. package/dist/document-editor-pane-A70-EhdQ.d.ts +0 -124
  94. package/dist/document-editor-pane-TLPVRBBU.js +0 -11
  95. package/dist/expanded-tool-detail-Dh99mcbY.d.ts +0 -63
  96. package/dist/file-tabs-BLfxfmAH.d.ts +0 -51
  97. package/dist/parts-CyGkM6Fp.d.ts +0 -50
  98. package/dist/run-CtFZ6s-D.d.ts +0 -41
  99. package/dist/sidebar-drop-zone-tDBsuOH5.d.ts +0 -301
  100. package/dist/sidecar-CFU2W9j1.d.ts +0 -8
  101. package/dist/template-card-BAtvcAkU.d.ts +0 -18
  102. package/dist/tool-call-feed-Bs3MyQMT.d.ts +0 -68
  103. package/dist/tool-display-Ct9nFAzJ.d.ts +0 -32
  104. package/dist/usage-chart-CPTcNlGs.d.ts +0 -73
  105. package/dist/use-sandbox-metrics-DWc0k9Xm.d.ts +0 -153
  106. package/dist/variant-list-BrHYcBCk.d.ts +0 -540
@@ -0,0 +1,2652 @@
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/dashboard-layout.tsx
1042
+ import * as React3 from "react";
1043
+ import { Plus as Plus2, Bell } from "lucide-react";
1044
+ import { Fragment as Fragment6, jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
1045
+ function SettingsIconSmall({ className }) {
1046
+ return /* @__PURE__ */ jsxs10("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: [
1047
+ /* @__PURE__ */ jsx12("title", { children: "Settings" }),
1048
+ /* @__PURE__ */ jsx12("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" }),
1049
+ /* @__PURE__ */ jsx12("circle", { cx: "12", cy: "12", r: "3" })
1050
+ ] });
1051
+ }
1052
+ function MenuIcon({ className }) {
1053
+ return /* @__PURE__ */ jsxs10("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: [
1054
+ /* @__PURE__ */ jsx12("title", { children: "Menu icon" }),
1055
+ /* @__PURE__ */ jsx12("line", { x1: "4", x2: "20", y1: "12", y2: "12" }),
1056
+ /* @__PURE__ */ jsx12("line", { x1: "4", x2: "20", y1: "6", y2: "6" }),
1057
+ /* @__PURE__ */ jsx12("line", { x1: "4", x2: "20", y1: "18", y2: "18" })
1058
+ ] });
1059
+ }
1060
+ function XIcon({ className }) {
1061
+ return /* @__PURE__ */ jsxs10("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: [
1062
+ /* @__PURE__ */ jsx12("title", { children: "Close icon" }),
1063
+ /* @__PURE__ */ jsx12("path", { d: "M18 6 6 18" }),
1064
+ /* @__PURE__ */ jsx12("path", { d: "m6 6 12 12" })
1065
+ ] });
1066
+ }
1067
+ function formatNotifDate(raw) {
1068
+ const d = new Date(raw);
1069
+ return Number.isNaN(d.getTime()) ? raw : d.toLocaleString(void 0, { month: "short", day: "numeric", hour: "2-digit", minute: "2-digit" });
1070
+ }
1071
+ function DefaultLink2({
1072
+ href,
1073
+ to,
1074
+ className,
1075
+ children,
1076
+ ...rest
1077
+ }) {
1078
+ return /* @__PURE__ */ jsx12("a", { href: href ?? to, className, ...rest, children });
1079
+ }
1080
+ function DashboardLayoutInner({
1081
+ children,
1082
+ variant = "sandbox",
1083
+ navItems,
1084
+ modeItems = [],
1085
+ panels = [],
1086
+ activeNavId,
1087
+ user,
1088
+ isLoading = false,
1089
+ onLogout,
1090
+ onSettingsClick,
1091
+ settingsHref = "/dashboard/settings",
1092
+ onNewSandbox,
1093
+ className,
1094
+ sidebarClassName,
1095
+ contentClassName,
1096
+ topNavLinks,
1097
+ activeTopNavHref,
1098
+ LinkComponent = DefaultLink2,
1099
+ footer,
1100
+ railFooter,
1101
+ profileMenuItems,
1102
+ notifications: notifData
1103
+ }) {
1104
+ const Link = LinkComponent;
1105
+ const [mobileMenuOpen, setMobileMenuOpen] = React3.useState(false);
1106
+ const [notificationsOpen, setNotificationsOpen] = React3.useState(false);
1107
+ const notifRef = React3.useRef(null);
1108
+ React3.useEffect(() => {
1109
+ if (!notificationsOpen) return;
1110
+ const handler = (e) => {
1111
+ if (notifRef.current && !notifRef.current.contains(e.target)) {
1112
+ setNotificationsOpen(false);
1113
+ }
1114
+ };
1115
+ const keyHandler = (e) => {
1116
+ if (e.key === "Escape") setNotificationsOpen(false);
1117
+ };
1118
+ document.addEventListener("mousedown", handler);
1119
+ document.addEventListener("keydown", keyHandler);
1120
+ return () => {
1121
+ document.removeEventListener("mousedown", handler);
1122
+ document.removeEventListener("keydown", keyHandler);
1123
+ };
1124
+ }, [notificationsOpen]);
1125
+ const { contentMargin, hidden, mode, hasPanels, panelOpen } = useSidebar();
1126
+ const modeSet = React3.useMemo(() => new Set(modeItems), [modeItems]);
1127
+ const sidebarUser = React3.useMemo(
1128
+ () => user ? { email: user.email, name: user.name, tier: user.tier, avatarUrl: user.avatarUrl } : void 0,
1129
+ [user?.email, user?.name, user?.tier, user?.avatarUrl]
1130
+ );
1131
+ const activePanel = panels.find((p) => p.mode === mode);
1132
+ const buildSidebarContent = React3.useCallback(
1133
+ (showLabels) => /* @__PURE__ */ jsxs10(Fragment6, { children: [
1134
+ /* @__PURE__ */ jsxs10(SidebarRail, { wide: showLabels, children: [
1135
+ /* @__PURE__ */ jsx12(SidebarRailHeader, { children: /* @__PURE__ */ jsx12(Link, { href: "/", to: "/", className: "p-1 rounded-md transition-colors hover:bg-muted/50", children: /* @__PURE__ */ jsx12(Logo, { variant, size: "sm", iconOnly: true }) }) }),
1136
+ /* @__PURE__ */ jsx12(SidebarRailNav, { children: navItems.map((item, i) => {
1137
+ const isMode = modeSet.has(item.id);
1138
+ const prevIsMode = i > 0 && modeSet.has(navItems[i - 1].id);
1139
+ const showSep = i > 0 && isMode && !prevIsMode;
1140
+ return /* @__PURE__ */ jsxs10(React3.Fragment, { children: [
1141
+ showSep && /* @__PURE__ */ jsx12(RailSeparator, {}),
1142
+ isMode ? /* @__PURE__ */ jsx12(
1143
+ RailModeButton,
1144
+ {
1145
+ mode: item.id,
1146
+ icon: item.icon,
1147
+ label: item.label,
1148
+ badge: item.badge,
1149
+ showLabel: showLabels
1150
+ }
1151
+ ) : /* @__PURE__ */ jsx12(Link, { href: item.href, to: item.href, children: /* @__PURE__ */ jsx12(
1152
+ RailButton,
1153
+ {
1154
+ icon: item.icon,
1155
+ label: item.label,
1156
+ isActive: activeNavId === item.id,
1157
+ showLabel: showLabels
1158
+ }
1159
+ ) })
1160
+ ] }, item.id);
1161
+ }) }),
1162
+ /* @__PURE__ */ jsxs10(SidebarRailFooter, { children: [
1163
+ onSettingsClick ? /* @__PURE__ */ jsx12(RailButton, { icon: SettingsIconSmall, label: "Settings", onClick: onSettingsClick, showLabel: showLabels }) : /* @__PURE__ */ jsx12(Link, { href: settingsHref, to: settingsHref, children: /* @__PURE__ */ jsx12(RailButton, { icon: SettingsIconSmall, label: "Settings", showLabel: showLabels }) }),
1164
+ railFooter,
1165
+ /* @__PURE__ */ jsx12(RailSeparator, {}),
1166
+ /* @__PURE__ */ jsx12(
1167
+ ProfileAvatar,
1168
+ {
1169
+ user: sidebarUser,
1170
+ isLoading,
1171
+ onLogout,
1172
+ onSettingsClick,
1173
+ settingsHref,
1174
+ LinkComponent,
1175
+ children: profileMenuItems
1176
+ }
1177
+ )
1178
+ ] })
1179
+ ] }),
1180
+ panels.length > 0 && /* @__PURE__ */ jsxs10(SidebarPanel, { children: [
1181
+ /* @__PURE__ */ jsx12(SidebarPanelHeader, { title: activePanel?.title ?? mode }),
1182
+ /* @__PURE__ */ jsx12(SidebarPanelContent, { children: activePanel?.content })
1183
+ ] })
1184
+ ] }),
1185
+ // biome-ignore lint/correctness/useExhaustiveDependencies: intentional — only the inputs that actually affect the sidebar tree
1186
+ [
1187
+ Link,
1188
+ variant,
1189
+ navItems,
1190
+ modeSet,
1191
+ activeNavId,
1192
+ onSettingsClick,
1193
+ settingsHref,
1194
+ railFooter,
1195
+ sidebarUser,
1196
+ isLoading,
1197
+ onLogout,
1198
+ LinkComponent,
1199
+ profileMenuItems,
1200
+ panels,
1201
+ activePanel,
1202
+ mode
1203
+ ]
1204
+ );
1205
+ const sidebarContent = React3.useMemo(() => buildSidebarContent(false), [buildSidebarContent]);
1206
+ const mobileSidebarContent = React3.useMemo(() => buildSidebarContent(true), [buildSidebarContent]);
1207
+ return /* @__PURE__ */ jsxs10("div", { className: cn("min-h-screen bg-background text-foreground", className), children: [
1208
+ /* @__PURE__ */ jsxs10(
1209
+ "nav",
1210
+ {
1211
+ 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",
1212
+ style: {
1213
+ left: hidden ? 0 : contentMargin,
1214
+ width: hidden ? "100%" : `calc(100% - ${contentMargin}px)`
1215
+ },
1216
+ children: [
1217
+ /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-8", children: [
1218
+ /* @__PURE__ */ jsx12(Link, { href: "/", to: "/", className: "lg:hidden flex items-center p-1 rounded-md hover:bg-muted/50 transition-colors", children: /* @__PURE__ */ jsx12(Logo, { variant, size: "sm", iconOnly: true }) }),
1219
+ topNavLinks && topNavLinks.length > 0 && /* @__PURE__ */ jsx12("div", { className: "hidden md:flex gap-6", children: topNavLinks.map((link) => /* @__PURE__ */ jsx12(
1220
+ Link,
1221
+ {
1222
+ href: link.href,
1223
+ to: link.href,
1224
+ className: cn(
1225
+ "transition-all duration-300 px-2 py-1 rounded",
1226
+ activeTopNavHref === link.href ? "text-foreground border-b-2 border-primary pb-1" : "text-muted-foreground hover:text-foreground hover:bg-muted/50"
1227
+ ),
1228
+ children: link.label
1229
+ },
1230
+ link.href
1231
+ )) })
1232
+ ] }),
1233
+ /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-4", children: [
1234
+ onNewSandbox && /* @__PURE__ */ jsxs10(
1235
+ "button",
1236
+ {
1237
+ type: "button",
1238
+ onClick: onNewSandbox,
1239
+ 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",
1240
+ children: [
1241
+ /* @__PURE__ */ jsx12(Plus2, { className: "h-3.5 w-3.5" }),
1242
+ "New Sandbox"
1243
+ ]
1244
+ }
1245
+ ),
1246
+ /* @__PURE__ */ jsxs10("div", { className: "relative", ref: notifRef, children: [
1247
+ /* @__PURE__ */ jsxs10(
1248
+ "button",
1249
+ {
1250
+ type: "button",
1251
+ className: "relative text-muted-foreground hover:text-foreground transition-colors p-2 rounded-lg hover:bg-muted/50",
1252
+ onClick: () => setNotificationsOpen(!notificationsOpen),
1253
+ "aria-label": "Notifications",
1254
+ "aria-expanded": notificationsOpen,
1255
+ children: [
1256
+ /* @__PURE__ */ jsx12(Bell, { className: "h-4 w-4" }),
1257
+ (notifData?.unreadCount ?? 0) > 0 && /* @__PURE__ */ jsx12("span", { className: "absolute top-1 right-1 h-2 w-2 rounded-full bg-destructive" })
1258
+ ]
1259
+ }
1260
+ ),
1261
+ notificationsOpen && /* @__PURE__ */ jsxs10("div", { className: "absolute right-0 top-full mt-2 w-80 rounded-lg border border-border bg-card shadow-lg z-50", children: [
1262
+ /* @__PURE__ */ jsxs10("div", { className: "flex items-center justify-between border-b border-border px-4 py-3", children: [
1263
+ /* @__PURE__ */ jsx12("p", { className: "font-bold text-foreground text-sm", children: "Notifications" }),
1264
+ (notifData?.unreadCount ?? 0) > 0 && notifData?.onMarkAllRead && /* @__PURE__ */ jsx12(
1265
+ "button",
1266
+ {
1267
+ type: "button",
1268
+ onClick: () => {
1269
+ notifData.onMarkAllRead?.();
1270
+ },
1271
+ className: "text-primary text-xs font-medium hover:underline",
1272
+ children: "Mark all read"
1273
+ }
1274
+ )
1275
+ ] }),
1276
+ !notifData?.items || notifData.items.length === 0 ? /* @__PURE__ */ jsxs10("div", { className: "flex flex-col items-center justify-center px-4 py-8 text-center", children: [
1277
+ /* @__PURE__ */ jsx12(Bell, { className: "h-8 w-8 text-muted-foreground/40 mb-2" }),
1278
+ /* @__PURE__ */ jsx12("p", { className: "text-muted-foreground text-sm", children: "No notifications yet" }),
1279
+ /* @__PURE__ */ jsx12("p", { className: "text-muted-foreground/60 text-xs mt-1", children: "We'll notify you about important updates" })
1280
+ ] }) : /* @__PURE__ */ jsx12("div", { className: "max-h-80 overflow-y-auto", children: notifData.items.map((n) => /* @__PURE__ */ jsxs10(
1281
+ "button",
1282
+ {
1283
+ type: "button",
1284
+ className: cn(
1285
+ "w-full text-left px-4 py-3 border-b border-border last:border-0 transition-colors",
1286
+ n.read ? "cursor-default" : "bg-primary/5 hover:bg-muted/50"
1287
+ ),
1288
+ onClick: () => {
1289
+ if (!n.read) notifData.onMarkRead?.(n.id);
1290
+ },
1291
+ children: [
1292
+ /* @__PURE__ */ jsx12("p", { className: cn("text-sm", !n.read ? "font-semibold text-foreground" : "text-muted-foreground"), children: n.title }),
1293
+ /* @__PURE__ */ jsx12("p", { className: "text-xs text-muted-foreground mt-0.5 line-clamp-2", children: n.message }),
1294
+ /* @__PURE__ */ jsx12("p", { className: "text-[10px] text-muted-foreground/50 mt-1", children: formatNotifDate(n.createdAt) })
1295
+ ]
1296
+ },
1297
+ n.id
1298
+ )) })
1299
+ ] })
1300
+ ] })
1301
+ ] }),
1302
+ /* @__PURE__ */ jsx12(
1303
+ "button",
1304
+ {
1305
+ type: "button",
1306
+ onClick: () => setMobileMenuOpen(!mobileMenuOpen),
1307
+ className: "rounded-md p-2 hover:bg-muted/50 lg:hidden",
1308
+ "aria-label": mobileMenuOpen ? "Close menu" : "Open menu",
1309
+ "aria-expanded": mobileMenuOpen,
1310
+ children: mobileMenuOpen ? /* @__PURE__ */ jsx12(XIcon, { className: "h-6 w-6" }) : /* @__PURE__ */ jsx12(MenuIcon, { className: "h-6 w-6" })
1311
+ }
1312
+ )
1313
+ ]
1314
+ }
1315
+ ),
1316
+ mobileMenuOpen && /* @__PURE__ */ jsx12("div", { className: "fixed inset-0 z-30 bg-black/50 lg:hidden", onClick: () => setMobileMenuOpen(false), "aria-hidden": "true" }),
1317
+ /* @__PURE__ */ jsx12(
1318
+ "aside",
1319
+ {
1320
+ className: cn(
1321
+ "fixed top-14 bottom-0 left-0 z-30 flex bg-background transition-transform duration-200 lg:hidden",
1322
+ mobileMenuOpen ? "translate-x-0" : "-translate-x-full"
1323
+ ),
1324
+ style: {
1325
+ width: panelOpen && hasPanels ? SIDEBAR_MOBILE_WIDTH + SIDEBAR_PANEL_WIDTH : SIDEBAR_MOBILE_WIDTH
1326
+ },
1327
+ children: mobileSidebarContent
1328
+ }
1329
+ ),
1330
+ /* @__PURE__ */ jsx12(Sidebar, { className: cn("hidden lg:flex", sidebarClassName), children: sidebarContent }),
1331
+ /* @__PURE__ */ jsx12(SidebarContent, { className: cn("pt-16 px-6 pb-8 lg:px-8 bg-background", contentClassName), children }),
1332
+ footer
1333
+ ] });
1334
+ }
1335
+ function DashboardLayout({ defaultPanelOpen, defaultMode, ...props }) {
1336
+ return /* @__PURE__ */ jsx12(SidebarProvider, { defaultPanelOpen, defaultMode, hasPanels: (props.panels?.length ?? 0) > 0, children: /* @__PURE__ */ jsx12(DashboardLayoutInner, { defaultPanelOpen, defaultMode, ...props }) });
1337
+ }
1338
+
1339
+ // src/dashboard/profile-selector.tsx
1340
+ import { Check as Check2, ChevronDown as ChevronDown2, Plus as Plus3, Settings } from "lucide-react";
1341
+ import { Button } from "@tangle-network/ui/primitives";
1342
+ import { Badge } from "@tangle-network/ui/primitives";
1343
+ import {
1344
+ DropdownMenu as DropdownMenu3,
1345
+ DropdownMenuContent as DropdownMenuContent3,
1346
+ DropdownMenuItem as DropdownMenuItem3,
1347
+ DropdownMenuLabel as DropdownMenuLabel2,
1348
+ DropdownMenuSeparator as DropdownMenuSeparator3,
1349
+ DropdownMenuTrigger as DropdownMenuTrigger3
1350
+ } from "@tangle-network/ui/primitives";
1351
+ import { Fragment as Fragment7, jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
1352
+ function ProfileSelector({
1353
+ profiles,
1354
+ selectedId,
1355
+ onSelect,
1356
+ onCreateClick,
1357
+ onManageClick,
1358
+ label = "Profile",
1359
+ placeholder = "Default (no custom profile)",
1360
+ showMetrics = true,
1361
+ className
1362
+ }) {
1363
+ const selected = profiles.find((p) => p.id === selectedId);
1364
+ const builtinProfiles = profiles.filter((p) => p.is_builtin);
1365
+ const customProfiles = profiles.filter((p) => !p.is_builtin);
1366
+ return /* @__PURE__ */ jsxs11("div", { className, children: [
1367
+ label && /* @__PURE__ */ jsx13("label", { className: "mb-2 block font-medium text-sm", children: label }),
1368
+ /* @__PURE__ */ jsxs11(DropdownMenu3, { children: [
1369
+ /* @__PURE__ */ jsx13(DropdownMenuTrigger3, { asChild: true, children: /* @__PURE__ */ jsxs11(Button, { variant: "outline", className: "w-full justify-between", children: [
1370
+ /* @__PURE__ */ jsx13("span", { className: "truncate", children: selected ? selected.name : placeholder }),
1371
+ /* @__PURE__ */ jsx13(ChevronDown2, { className: "ml-2 h-4 w-4 shrink-0 opacity-50" })
1372
+ ] }) }),
1373
+ /* @__PURE__ */ jsxs11(DropdownMenuContent3, { className: "w-[300px]", align: "start", children: [
1374
+ /* @__PURE__ */ jsxs11(
1375
+ DropdownMenuItem3,
1376
+ {
1377
+ onClick: () => onSelect(null),
1378
+ className: "flex items-center justify-between",
1379
+ children: [
1380
+ /* @__PURE__ */ jsx13("span", { children: placeholder }),
1381
+ !selectedId && /* @__PURE__ */ jsx13(Check2, { className: "h-4 w-4 text-[var(--surface-success-text)]" })
1382
+ ]
1383
+ }
1384
+ ),
1385
+ builtinProfiles.length > 0 && /* @__PURE__ */ jsxs11(Fragment7, { children: [
1386
+ /* @__PURE__ */ jsx13(DropdownMenuSeparator3, {}),
1387
+ /* @__PURE__ */ jsx13(DropdownMenuLabel2, { children: "Built-in Profiles" }),
1388
+ builtinProfiles.map((profile) => /* @__PURE__ */ jsxs11(
1389
+ DropdownMenuItem3,
1390
+ {
1391
+ onClick: () => onSelect(profile),
1392
+ className: "flex flex-col items-start gap-1",
1393
+ children: [
1394
+ /* @__PURE__ */ jsxs11("div", { className: "flex w-full items-center justify-between", children: [
1395
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-2", children: [
1396
+ /* @__PURE__ */ jsx13("span", { className: "font-medium", children: profile.name }),
1397
+ profile.extends && /* @__PURE__ */ jsxs11(Badge, { variant: "secondary", className: "border-0 text-xs", children: [
1398
+ "extends ",
1399
+ profile.extends
1400
+ ] })
1401
+ ] }),
1402
+ selectedId === profile.id && /* @__PURE__ */ jsx13(Check2, { className: "h-4 w-4 text-[var(--surface-success-text)]" })
1403
+ ] }),
1404
+ profile.description && /* @__PURE__ */ jsx13("span", { className: "line-clamp-1 text-muted-foreground text-xs", children: profile.description })
1405
+ ]
1406
+ },
1407
+ profile.id
1408
+ ))
1409
+ ] }),
1410
+ customProfiles.length > 0 && /* @__PURE__ */ jsxs11(Fragment7, { children: [
1411
+ /* @__PURE__ */ jsx13(DropdownMenuSeparator3, {}),
1412
+ /* @__PURE__ */ jsx13(DropdownMenuLabel2, { children: "Custom Profiles" }),
1413
+ customProfiles.map((profile) => /* @__PURE__ */ jsxs11(
1414
+ DropdownMenuItem3,
1415
+ {
1416
+ onClick: () => onSelect(profile),
1417
+ className: "flex flex-col items-start gap-1",
1418
+ children: [
1419
+ /* @__PURE__ */ jsxs11("div", { className: "flex w-full items-center justify-between", children: [
1420
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-2", children: [
1421
+ /* @__PURE__ */ jsx13("span", { className: "font-medium", children: profile.name }),
1422
+ profile.model && /* @__PURE__ */ jsx13(Badge, { variant: "secondary", className: "border-0 text-xs", children: profile.model.split("/").pop() })
1423
+ ] }),
1424
+ selectedId === profile.id && /* @__PURE__ */ jsx13(Check2, { className: "h-4 w-4 text-[var(--surface-success-text)]" })
1425
+ ] }),
1426
+ profile.description && /* @__PURE__ */ jsx13("span", { className: "line-clamp-1 text-muted-foreground text-xs", children: profile.description }),
1427
+ showMetrics && profile.metrics && profile.metrics.total_runs > 0 && /* @__PURE__ */ jsxs11("div", { className: "flex gap-3 text-muted-foreground text-xs", children: [
1428
+ /* @__PURE__ */ jsxs11("span", { children: [
1429
+ profile.metrics.total_runs,
1430
+ " runs"
1431
+ ] }),
1432
+ /* @__PURE__ */ jsxs11("span", { children: [
1433
+ profile.metrics.success_rate.toFixed(0),
1434
+ "% success"
1435
+ ] }),
1436
+ /* @__PURE__ */ jsxs11("span", { children: [
1437
+ "~",
1438
+ (profile.metrics.avg_duration_ms / 1e3).toFixed(1),
1439
+ "s avg"
1440
+ ] })
1441
+ ] })
1442
+ ]
1443
+ },
1444
+ profile.id
1445
+ ))
1446
+ ] }),
1447
+ (onCreateClick || onManageClick) && /* @__PURE__ */ jsxs11(Fragment7, { children: [
1448
+ /* @__PURE__ */ jsx13(DropdownMenuSeparator3, {}),
1449
+ onCreateClick && /* @__PURE__ */ jsxs11(
1450
+ DropdownMenuItem3,
1451
+ {
1452
+ onClick: onCreateClick,
1453
+ className: "text-[var(--surface-info-text)]",
1454
+ children: [
1455
+ /* @__PURE__ */ jsx13(Plus3, { className: "mr-2 h-4 w-4" }),
1456
+ "Create New Profile"
1457
+ ]
1458
+ }
1459
+ ),
1460
+ onManageClick && /* @__PURE__ */ jsxs11(
1461
+ DropdownMenuItem3,
1462
+ {
1463
+ onClick: onManageClick,
1464
+ className: "text-muted-foreground",
1465
+ children: [
1466
+ /* @__PURE__ */ jsx13(Settings, { className: "mr-2 h-4 w-4" }),
1467
+ "Manage Profiles"
1468
+ ]
1469
+ }
1470
+ )
1471
+ ] })
1472
+ ] })
1473
+ ] })
1474
+ ] });
1475
+ }
1476
+ function ProfileComparison({
1477
+ profiles,
1478
+ className
1479
+ }) {
1480
+ const profilesWithMetrics = profiles.filter(
1481
+ (p) => p.metrics && p.metrics.total_runs > 0
1482
+ );
1483
+ if (profilesWithMetrics.length === 0) {
1484
+ return null;
1485
+ }
1486
+ const bestSuccess = profilesWithMetrics.reduce(
1487
+ (best, p) => (p.metrics?.success_rate ?? 0) > (best.metrics?.success_rate ?? 0) ? p : best
1488
+ );
1489
+ const fastestProfile = profilesWithMetrics.reduce(
1490
+ (best, p) => (p.metrics?.avg_duration_ms ?? Number.POSITIVE_INFINITY) < (best.metrics?.avg_duration_ms ?? Number.POSITIVE_INFINITY) ? p : best
1491
+ );
1492
+ return /* @__PURE__ */ jsxs11("div", { className: `rounded-lg border border-border p-4 ${className ?? ""}`, children: [
1493
+ /* @__PURE__ */ jsx13("h4", { className: "mb-3 font-medium text-sm", children: "Profile Performance" }),
1494
+ /* @__PURE__ */ jsx13("div", { className: "space-y-3", children: profilesWithMetrics.map((profile) => {
1495
+ const isBestSuccess = profile.id === bestSuccess.id;
1496
+ const isFastest = profile.id === fastestProfile.id;
1497
+ return /* @__PURE__ */ jsxs11(
1498
+ "div",
1499
+ {
1500
+ className: "flex items-center justify-between gap-4",
1501
+ children: [
1502
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-2", children: [
1503
+ /* @__PURE__ */ jsx13("span", { className: "font-medium", children: profile.name }),
1504
+ isBestSuccess && /* @__PURE__ */ jsx13(Badge, { className: "border border-[var(--surface-success-border)] bg-[var(--surface-success-bg)] text-[var(--surface-success-text)] text-xs", children: "Best Success" }),
1505
+ isFastest && !isBestSuccess && /* @__PURE__ */ jsx13(Badge, { className: "border border-[var(--surface-info-border)] bg-[var(--surface-info-bg)] text-[var(--surface-info-text)] text-xs", children: "Fastest" })
1506
+ ] }),
1507
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-4 text-sm", children: [
1508
+ /* @__PURE__ */ jsxs11("span", { children: [
1509
+ /* @__PURE__ */ jsx13("span", { className: "text-muted-foreground", children: "Success:" }),
1510
+ " ",
1511
+ /* @__PURE__ */ jsxs11(
1512
+ "span",
1513
+ {
1514
+ 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)]",
1515
+ children: [
1516
+ profile.metrics?.success_rate.toFixed(0),
1517
+ "%"
1518
+ ]
1519
+ }
1520
+ )
1521
+ ] }),
1522
+ /* @__PURE__ */ jsxs11("span", { children: [
1523
+ /* @__PURE__ */ jsx13("span", { className: "text-muted-foreground", children: "Avg:" }),
1524
+ " ",
1525
+ ((profile.metrics?.avg_duration_ms ?? 0) / 1e3).toFixed(1),
1526
+ "s"
1527
+ ] }),
1528
+ /* @__PURE__ */ jsxs11("span", { className: "text-muted-foreground", children: [
1529
+ profile.metrics?.total_runs,
1530
+ " runs"
1531
+ ] })
1532
+ ] })
1533
+ ]
1534
+ },
1535
+ profile.id
1536
+ );
1537
+ }) })
1538
+ ] });
1539
+ }
1540
+
1541
+ // src/dashboard/variant-list.tsx
1542
+ import {
1543
+ Check as Check3,
1544
+ CheckCircle2,
1545
+ Clock as Clock2,
1546
+ ExternalLink,
1547
+ Loader2,
1548
+ Timer,
1549
+ X,
1550
+ XCircle
1551
+ } from "lucide-react";
1552
+ import { Button as Button2 } from "@tangle-network/ui/primitives";
1553
+ import { Badge as Badge2 } from "@tangle-network/ui/primitives";
1554
+ import { Fragment as Fragment8, jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
1555
+ var statusConfig = {
1556
+ pending: {
1557
+ icon: Clock2,
1558
+ color: "text-[var(--surface-warning-text)]",
1559
+ bg: "bg-[var(--surface-warning-bg)]",
1560
+ border: "border-[var(--surface-warning-border)]",
1561
+ label: "Pending",
1562
+ animate: false
1563
+ },
1564
+ running: {
1565
+ icon: Loader2,
1566
+ color: "text-primary",
1567
+ bg: "bg-[var(--accent-surface-soft)]",
1568
+ border: "border-border",
1569
+ label: "Running",
1570
+ animate: true
1571
+ },
1572
+ completed: {
1573
+ icon: CheckCircle2,
1574
+ color: "text-[var(--surface-success-text)]",
1575
+ bg: "bg-[var(--surface-success-bg)]",
1576
+ border: "border-[var(--surface-success-border)]",
1577
+ label: "Completed",
1578
+ animate: false
1579
+ },
1580
+ failed: {
1581
+ icon: XCircle,
1582
+ color: "text-[var(--surface-danger-text)]",
1583
+ bg: "bg-[var(--surface-danger-bg)]",
1584
+ border: "border-[var(--surface-danger-border)]",
1585
+ label: "Failed",
1586
+ animate: false
1587
+ },
1588
+ cancelled: {
1589
+ icon: XCircle,
1590
+ color: "text-[var(--surface-neutral-text)]",
1591
+ bg: "bg-[var(--surface-neutral-bg)]",
1592
+ border: "border-[var(--surface-neutral-border)]",
1593
+ label: "Cancelled",
1594
+ animate: false
1595
+ }
1596
+ };
1597
+ var outcomeConfig = {
1598
+ pending_review: {
1599
+ color: "text-[var(--surface-warning-text)]",
1600
+ bg: "bg-[var(--surface-warning-bg)]",
1601
+ border: "border-[var(--surface-warning-border)]",
1602
+ label: "Pending Review"
1603
+ },
1604
+ accepted: {
1605
+ color: "text-[var(--surface-success-text)]",
1606
+ bg: "bg-[var(--surface-success-bg)]",
1607
+ border: "border-[var(--surface-success-border)]",
1608
+ label: "Accepted"
1609
+ },
1610
+ rejected: {
1611
+ color: "text-[var(--surface-danger-text)]",
1612
+ bg: "bg-[var(--surface-danger-bg)]",
1613
+ border: "border-[var(--surface-danger-border)]",
1614
+ label: "Rejected"
1615
+ },
1616
+ merged_with_conflicts: {
1617
+ color: "text-[var(--surface-orange-text)]",
1618
+ bg: "bg-[var(--surface-orange-bg)]",
1619
+ border: "border-[var(--surface-orange-border)]",
1620
+ label: "Merged (conflicts)"
1621
+ },
1622
+ expired: {
1623
+ color: "text-[var(--surface-neutral-text)]",
1624
+ bg: "bg-[var(--surface-neutral-bg)]",
1625
+ border: "border-[var(--surface-neutral-border)]",
1626
+ label: "Expired"
1627
+ }
1628
+ };
1629
+ function VariantList({
1630
+ variants,
1631
+ selectedId,
1632
+ onSelect,
1633
+ onAccept,
1634
+ onReject,
1635
+ isActioning,
1636
+ className
1637
+ }) {
1638
+ return /* @__PURE__ */ jsx14("div", { className: `space-y-2 ${className || ""}`, children: variants.map((variant) => {
1639
+ const status = statusConfig[variant.status];
1640
+ const StatusIcon = status.icon;
1641
+ const isSelected = variant.id === selectedId;
1642
+ return /* @__PURE__ */ jsxs12(
1643
+ "div",
1644
+ {
1645
+ 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"}`,
1646
+ onClick: () => onSelect?.(variant.id),
1647
+ children: [
1648
+ /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-2", children: [
1649
+ /* @__PURE__ */ jsxs12(Badge2, { className: `shrink-0 ${status.bg} ${status.border} ${status.color}`, children: [
1650
+ /* @__PURE__ */ jsx14(
1651
+ StatusIcon,
1652
+ {
1653
+ className: `mr-1 h-3 w-3 ${status.animate ? "animate-spin" : ""}`
1654
+ }
1655
+ ),
1656
+ status.label
1657
+ ] }),
1658
+ /* @__PURE__ */ jsx14("span", { className: "truncate text-sm font-medium text-foreground", children: variant.label }),
1659
+ variant.sublabel && /* @__PURE__ */ jsxs12("span", { className: "shrink-0 text-xs text-muted-foreground", children: [
1660
+ "(",
1661
+ variant.sublabel,
1662
+ ")"
1663
+ ] }),
1664
+ variant.durationMs && /* @__PURE__ */ jsxs12("span", { className: "flex shrink-0 items-center gap-1 text-xs text-muted-foreground", children: [
1665
+ /* @__PURE__ */ jsx14(Timer, { className: "h-3 w-3" }),
1666
+ (variant.durationMs / 1e3).toFixed(1),
1667
+ "s"
1668
+ ] }),
1669
+ /* @__PURE__ */ jsxs12("div", { className: "ml-auto flex shrink-0 items-center gap-1.5", children: [
1670
+ variant.outcome && /* @__PURE__ */ jsx14(
1671
+ Badge2,
1672
+ {
1673
+ className: `${outcomeConfig[variant.outcome].bg} ${outcomeConfig[variant.outcome].border} ${outcomeConfig[variant.outcome].color}`,
1674
+ children: outcomeConfig[variant.outcome].label
1675
+ }
1676
+ ),
1677
+ variant.status === "completed" && variant.outcome === "pending_review" && onAccept && onReject && /* @__PURE__ */ jsxs12(Fragment8, { children: [
1678
+ /* @__PURE__ */ jsxs12(
1679
+ Button2,
1680
+ {
1681
+ variant: "outline",
1682
+ size: "sm",
1683
+ 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)]",
1684
+ onClick: (e) => {
1685
+ e.stopPropagation();
1686
+ onAccept(variant.id);
1687
+ },
1688
+ disabled: isActioning === variant.id,
1689
+ children: [
1690
+ /* @__PURE__ */ jsx14(Check3, { className: "mr-1 h-3 w-3" }),
1691
+ "Accept"
1692
+ ]
1693
+ }
1694
+ ),
1695
+ /* @__PURE__ */ jsxs12(
1696
+ Button2,
1697
+ {
1698
+ variant: "outline",
1699
+ size: "sm",
1700
+ 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)]",
1701
+ onClick: (e) => {
1702
+ e.stopPropagation();
1703
+ onReject(variant.id);
1704
+ },
1705
+ disabled: isActioning === variant.id,
1706
+ children: [
1707
+ /* @__PURE__ */ jsx14(X, { className: "mr-1 h-3 w-3" }),
1708
+ "Reject"
1709
+ ]
1710
+ }
1711
+ )
1712
+ ] }),
1713
+ variant.detailsUrl && /* @__PURE__ */ jsx14(
1714
+ Button2,
1715
+ {
1716
+ variant: "ghost",
1717
+ size: "sm",
1718
+ className: "h-7 w-7 p-0 text-muted-foreground hover:text-foreground",
1719
+ onClick: (e) => {
1720
+ e.stopPropagation();
1721
+ window.open(variant.detailsUrl, "_blank");
1722
+ },
1723
+ children: /* @__PURE__ */ jsx14(ExternalLink, { className: "h-3.5 w-3.5" })
1724
+ }
1725
+ )
1726
+ ] })
1727
+ ] }),
1728
+ variant.error && /* @__PURE__ */ jsx14("p", { className: "mt-1.5 text-xs text-[var(--surface-danger-text)]", children: variant.error }),
1729
+ variant.summary && /* @__PURE__ */ jsx14("p", { className: "mt-1.5 line-clamp-2 text-xs text-muted-foreground", children: variant.summary })
1730
+ ]
1731
+ },
1732
+ variant.id
1733
+ );
1734
+ }) });
1735
+ }
1736
+
1737
+ // src/dashboard/system-logs.tsx
1738
+ import { Terminal as Terminal3 } from "lucide-react";
1739
+ import { useEffect as useEffect2, useRef as useRef2, useState as useState4 } from "react";
1740
+ import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
1741
+ function SystemLogsViewer({ apiUrl, token, className }) {
1742
+ const [logs, setLogs] = useState4([]);
1743
+ const [error, setError] = useState4(null);
1744
+ const [isFollowing, setIsFollowing] = useState4(true);
1745
+ const scrollRef = useRef2(null);
1746
+ useEffect2(() => {
1747
+ let timeoutId;
1748
+ let backoff = 2e3;
1749
+ const controller = new AbortController();
1750
+ async function fetchLogs() {
1751
+ try {
1752
+ const res = await fetch(`${apiUrl}/debug/logs`, {
1753
+ headers: {
1754
+ Authorization: `Bearer ${token}`
1755
+ },
1756
+ signal: controller.signal
1757
+ });
1758
+ if (!res.ok) throw new Error(`HTTP Error: ${res.status}`);
1759
+ const data = await res.json();
1760
+ if (!controller.signal.aborted) {
1761
+ setLogs(data.logs || []);
1762
+ setError(null);
1763
+ backoff = 2e3;
1764
+ }
1765
+ } catch (err) {
1766
+ if (err.name === "AbortError") return;
1767
+ if (!controller.signal.aborted) {
1768
+ setError(err instanceof Error ? err.message : "Failed to fetch logs");
1769
+ backoff = Math.min(backoff * 2, 3e4);
1770
+ }
1771
+ }
1772
+ if (!controller.signal.aborted) {
1773
+ timeoutId = setTimeout(fetchLogs, backoff);
1774
+ }
1775
+ }
1776
+ fetchLogs();
1777
+ return () => {
1778
+ controller.abort();
1779
+ clearTimeout(timeoutId);
1780
+ };
1781
+ }, [apiUrl, token]);
1782
+ useEffect2(() => {
1783
+ if (isFollowing && scrollRef.current) {
1784
+ scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
1785
+ }
1786
+ }, [logs, isFollowing]);
1787
+ const handleScroll = () => {
1788
+ if (!scrollRef.current) return;
1789
+ const { scrollTop, scrollHeight, clientHeight } = scrollRef.current;
1790
+ const isAtBottom = scrollHeight - scrollTop - clientHeight < 20;
1791
+ setIsFollowing((prev) => prev === isAtBottom ? prev : isAtBottom);
1792
+ };
1793
+ return /* @__PURE__ */ jsxs13("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: [
1794
+ /* @__PURE__ */ jsxs13("div", { className: "flex-none flex items-center justify-between border-b border-border bg-muted/50 backdrop-blur-md px-4 py-2", children: [
1795
+ /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-2", children: [
1796
+ /* @__PURE__ */ jsx15(Terminal3, { className: "h-4 w-4 text-primary animate-pulse" }),
1797
+ /* @__PURE__ */ jsx15("span", { className: "font-bold text-xs uppercase tracking-widest text-muted-foreground", children: "System Traces" })
1798
+ ] }),
1799
+ /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-3", children: [
1800
+ error && /* @__PURE__ */ jsxs13("span", { className: "text-destructive text-xs flex items-center gap-1", children: [
1801
+ /* @__PURE__ */ jsx15("span", { className: "w-2 h-2 rounded-full bg-destructive animate-ping" }),
1802
+ " Error fetching logs"
1803
+ ] }),
1804
+ /* @__PURE__ */ jsx15(
1805
+ "button",
1806
+ {
1807
+ onClick: () => {
1808
+ setIsFollowing(!isFollowing);
1809
+ if (!isFollowing && scrollRef.current) {
1810
+ scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
1811
+ }
1812
+ },
1813
+ 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"),
1814
+ children: isFollowing ? "Auto-Scroll ON" : "Auto-Scroll OFF"
1815
+ }
1816
+ )
1817
+ ] })
1818
+ ] }),
1819
+ /* @__PURE__ */ jsx15(
1820
+ "div",
1821
+ {
1822
+ ref: scrollRef,
1823
+ onScroll: handleScroll,
1824
+ className: "flex-1 overflow-y-auto p-4 space-y-1",
1825
+ children: logs.length === 0 && !error ? /* @__PURE__ */ jsx15("div", { className: "flex h-full items-center justify-center text-muted-foreground italic", children: "Waiting for orchestrator logs..." }) : logs.map((log, i) => /* @__PURE__ */ jsxs13("div", { className: "break-words", children: [
1826
+ /* @__PURE__ */ jsxs13("span", { className: "text-muted-foreground mr-3 select-none", children: [
1827
+ "[",
1828
+ log.timestamp || i.toString().padStart(4, "0"),
1829
+ "]"
1830
+ ] }),
1831
+ /* @__PURE__ */ jsxs13("span", { className: "text-primary/70 mr-2", children: [
1832
+ "[",
1833
+ log.level,
1834
+ "]"
1835
+ ] }),
1836
+ /* @__PURE__ */ jsxs13("span", { className: "text-muted-foreground mr-2", children: [
1837
+ "[",
1838
+ log.scope,
1839
+ "]"
1840
+ ] }),
1841
+ /* @__PURE__ */ jsx15("span", { className: log.level.toUpperCase() === "ERROR" || log.message.toLowerCase().includes("failed") ? "text-destructive" : log.level.toUpperCase() === "WARN" ? "text-warning" : "text-foreground", children: log.message })
1842
+ ] }, `${log.timestamp}-${log.scope}-${i}`))
1843
+ }
1844
+ )
1845
+ ] });
1846
+ }
1847
+
1848
+ // src/dashboard/usage-summary.tsx
1849
+ import { Clock as Clock3, Layers, MessageSquare, DollarSign } from "lucide-react";
1850
+ import { StatCard } from "@tangle-network/ui/primitives";
1851
+ import { Skeleton as Skeleton2 } from "@tangle-network/ui/primitives";
1852
+ import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
1853
+ function UsageSummary({ data, loading = false, className }) {
1854
+ if (loading || !data) {
1855
+ return /* @__PURE__ */ jsx16("div", { className: cn("grid grid-cols-2 gap-4 lg:grid-cols-4", className), children: Array.from({ length: 4 }).map((_, i) => /* @__PURE__ */ jsx16(Skeleton2, { className: "h-28 rounded-xl" }, i)) });
1856
+ }
1857
+ return /* @__PURE__ */ jsxs14("div", { className: cn("grid grid-cols-2 gap-4 lg:grid-cols-4", className), children: [
1858
+ /* @__PURE__ */ jsx16(
1859
+ StatCard,
1860
+ {
1861
+ variant: "sandbox",
1862
+ title: "Compute Hours",
1863
+ value: data.computeHours.toFixed(1),
1864
+ subtitle: "This billing period",
1865
+ icon: /* @__PURE__ */ jsx16(Clock3, { className: "h-5 w-5" })
1866
+ }
1867
+ ),
1868
+ /* @__PURE__ */ jsx16(
1869
+ StatCard,
1870
+ {
1871
+ variant: "sandbox",
1872
+ title: "Active Sessions",
1873
+ value: data.activeSessions,
1874
+ subtitle: "Currently running",
1875
+ icon: /* @__PURE__ */ jsx16(Layers, { className: "h-5 w-5" })
1876
+ }
1877
+ ),
1878
+ /* @__PURE__ */ jsx16(
1879
+ StatCard,
1880
+ {
1881
+ variant: "sandbox",
1882
+ title: "Messages Sent",
1883
+ value: data.messagesSent.toLocaleString(),
1884
+ subtitle: "Agent interactions",
1885
+ icon: /* @__PURE__ */ jsx16(MessageSquare, { className: "h-5 w-5" })
1886
+ }
1887
+ ),
1888
+ /* @__PURE__ */ jsx16(
1889
+ StatCard,
1890
+ {
1891
+ variant: "sandbox",
1892
+ title: "Estimated Cost",
1893
+ value: `$${data.estimatedCost.toFixed(2)}`,
1894
+ subtitle: "This billing period",
1895
+ icon: /* @__PURE__ */ jsx16(DollarSign, { className: "h-5 w-5" })
1896
+ }
1897
+ )
1898
+ ] });
1899
+ }
1900
+
1901
+ // src/dashboard/git-panel.tsx
1902
+ import { GitBranch, GitCommit, FileEdit, FilePlus, File } from "lucide-react";
1903
+ import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
1904
+ function GitPanel({ status, log, loading = false, onRefresh, className }) {
1905
+ if (loading) {
1906
+ return /* @__PURE__ */ jsx17("div", { className: cn("rounded-lg border border-border bg-card p-5", className), children: /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
1907
+ /* @__PURE__ */ jsx17("div", { className: "h-4 w-4 animate-spin rounded-full border-2 border-primary border-t-transparent" }),
1908
+ "Loading git info..."
1909
+ ] }) });
1910
+ }
1911
+ if (!status) {
1912
+ return /* @__PURE__ */ jsx17("div", { className: cn("rounded-lg border border-border bg-card p-5", className), children: /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
1913
+ /* @__PURE__ */ jsx17(GitBranch, { className: "h-4 w-4" }),
1914
+ "No git repository detected"
1915
+ ] }) });
1916
+ }
1917
+ const changedCount = status.staged.length + status.modified.length + status.untracked.length;
1918
+ return /* @__PURE__ */ jsxs15("div", { className: cn("rounded-lg border border-border bg-card overflow-hidden", className), children: [
1919
+ /* @__PURE__ */ jsxs15("div", { className: "flex items-center justify-between border-b border-border bg-muted/30 px-4 py-3", children: [
1920
+ /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-2", children: [
1921
+ /* @__PURE__ */ jsx17(GitBranch, { className: "h-4 w-4 text-primary" }),
1922
+ /* @__PURE__ */ jsx17("span", { className: "text-sm font-bold text-foreground", children: status.branch }),
1923
+ status.isDirty && /* @__PURE__ */ jsxs15("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: [
1924
+ changedCount,
1925
+ " change",
1926
+ changedCount !== 1 ? "s" : ""
1927
+ ] })
1928
+ ] }),
1929
+ onRefresh && /* @__PURE__ */ jsx17("button", { type: "button", onClick: onRefresh, className: "text-xs text-muted-foreground hover:text-foreground transition-colors", children: "Refresh" })
1930
+ ] }),
1931
+ changedCount > 0 && /* @__PURE__ */ jsx17("div", { className: "border-b border-border px-4 py-3", children: /* @__PURE__ */ jsxs15("div", { className: "space-y-1.5", children: [
1932
+ status.staged.map((f) => /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-2 text-xs", children: [
1933
+ /* @__PURE__ */ jsx17(FilePlus, { className: "h-3 w-3 text-[var(--surface-success-text)]" }),
1934
+ /* @__PURE__ */ jsx17("span", { className: "font-mono text-foreground truncate", children: f }),
1935
+ /* @__PURE__ */ jsx17("span", { className: "text-[var(--surface-success-text)] text-[10px] font-bold ml-auto", children: "STAGED" })
1936
+ ] }, `s-${f}`)),
1937
+ status.modified.map((f) => /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-2 text-xs", children: [
1938
+ /* @__PURE__ */ jsx17(FileEdit, { className: "h-3 w-3 text-[var(--surface-warning-text)]" }),
1939
+ /* @__PURE__ */ jsx17("span", { className: "font-mono text-foreground truncate", children: f })
1940
+ ] }, `m-${f}`)),
1941
+ status.untracked.map((f) => /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-2 text-xs", children: [
1942
+ /* @__PURE__ */ jsx17(File, { className: "h-3 w-3 text-muted-foreground" }),
1943
+ /* @__PURE__ */ jsx17("span", { className: "font-mono text-muted-foreground truncate", children: f })
1944
+ ] }, `u-${f}`))
1945
+ ] }) }),
1946
+ log.length > 0 && /* @__PURE__ */ jsxs15("div", { className: "px-4 py-3", children: [
1947
+ /* @__PURE__ */ jsx17("div", { className: "mb-2 text-[10px] font-bold uppercase tracking-widest text-muted-foreground", children: "Recent Commits" }),
1948
+ /* @__PURE__ */ jsx17("div", { className: "space-y-2", children: log.slice(0, 5).map((commit) => /* @__PURE__ */ jsxs15("div", { className: "flex items-start gap-2 text-xs", children: [
1949
+ /* @__PURE__ */ jsx17(GitCommit, { className: "h-3 w-3 text-muted-foreground mt-0.5 shrink-0" }),
1950
+ /* @__PURE__ */ jsxs15("div", { className: "min-w-0", children: [
1951
+ /* @__PURE__ */ jsx17("span", { className: "font-mono text-primary mr-1.5", children: commit.shortSha }),
1952
+ /* @__PURE__ */ jsx17("span", { className: "text-foreground", children: commit.message })
1953
+ ] })
1954
+ ] }, commit.shortSha)) })
1955
+ ] }),
1956
+ (status.ahead > 0 || status.behind > 0) && /* @__PURE__ */ jsxs15("div", { className: "border-t border-border px-4 py-2 flex items-center gap-3 text-xs text-muted-foreground", children: [
1957
+ status.ahead > 0 && /* @__PURE__ */ jsxs15("span", { children: [
1958
+ "\u2191 ",
1959
+ status.ahead,
1960
+ " ahead"
1961
+ ] }),
1962
+ status.behind > 0 && /* @__PURE__ */ jsxs15("span", { children: [
1963
+ "\u2193 ",
1964
+ status.behind,
1965
+ " behind"
1966
+ ] })
1967
+ ] })
1968
+ ] });
1969
+ }
1970
+
1971
+ // src/dashboard/ports-list.tsx
1972
+ import * as React5 from "react";
1973
+ import { Copy as Copy2, Check as Check4, Globe, Plus as Plus4, Trash2 as Trash23 } from "lucide-react";
1974
+ import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
1975
+ function PortsList({ ports, onExposePort, onRemovePort, isExposing = false, className }) {
1976
+ const [newPort, setNewPort] = React5.useState("");
1977
+ const [copiedPort, setCopiedPort] = React5.useState(null);
1978
+ const copyTimerRef = React5.useRef(null);
1979
+ React5.useEffect(() => {
1980
+ return () => {
1981
+ if (copyTimerRef.current) clearTimeout(copyTimerRef.current);
1982
+ };
1983
+ }, []);
1984
+ const handleCopy = async (url, port) => {
1985
+ try {
1986
+ await navigator.clipboard.writeText(url);
1987
+ setCopiedPort(port);
1988
+ if (copyTimerRef.current) clearTimeout(copyTimerRef.current);
1989
+ copyTimerRef.current = setTimeout(() => setCopiedPort(null), 2e3);
1990
+ } catch (err) {
1991
+ console.warn("Clipboard write failed:", err);
1992
+ }
1993
+ };
1994
+ const handleExpose = () => {
1995
+ const port = parseInt(newPort, 10);
1996
+ if (port > 0 && port <= 65535) {
1997
+ onExposePort(port);
1998
+ setNewPort("");
1999
+ }
2000
+ };
2001
+ return /* @__PURE__ */ jsxs16("div", { className: cn("space-y-4", className), children: [
2002
+ ports.length > 0 ? /* @__PURE__ */ jsx18("div", { className: "rounded-lg border border-border overflow-hidden", children: /* @__PURE__ */ jsxs16("table", { className: "w-full text-sm", children: [
2003
+ /* @__PURE__ */ jsx18("thead", { className: "bg-muted/30 border-b border-border", children: /* @__PURE__ */ jsxs16("tr", { children: [
2004
+ /* @__PURE__ */ jsx18("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Port" }),
2005
+ /* @__PURE__ */ jsx18("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Public URL" }),
2006
+ /* @__PURE__ */ jsx18("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Status" }),
2007
+ /* @__PURE__ */ jsx18("th", { className: "px-4 py-2.5 text-right text-xs font-medium text-muted-foreground w-20" })
2008
+ ] }) }),
2009
+ /* @__PURE__ */ jsx18("tbody", { className: "divide-y divide-border", children: ports.map((p) => /* @__PURE__ */ jsxs16("tr", { children: [
2010
+ /* @__PURE__ */ jsx18("td", { className: "px-4 py-3 font-mono text-xs text-foreground", children: p.port }),
2011
+ /* @__PURE__ */ jsx18("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsxs16(
2012
+ "button",
2013
+ {
2014
+ type: "button",
2015
+ onClick: () => handleCopy(p.url, p.port),
2016
+ className: "flex items-center gap-2 font-mono text-xs text-primary hover:underline cursor-pointer group",
2017
+ children: [
2018
+ /* @__PURE__ */ jsx18("span", { className: "truncate max-w-[300px]", children: p.url }),
2019
+ copiedPort === p.port ? /* @__PURE__ */ jsx18(Check4, { className: "h-3 w-3 text-[var(--surface-success-text)] shrink-0" }) : /* @__PURE__ */ jsx18(Copy2, { className: "h-3 w-3 opacity-0 group-hover:opacity-100 transition-opacity shrink-0" })
2020
+ ]
2021
+ }
2022
+ ) }),
2023
+ /* @__PURE__ */ jsx18("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsx18("span", { className: cn(
2024
+ "inline-flex items-center gap-1.5 rounded-full px-2 py-0.5 text-[10px] font-bold uppercase tracking-wider",
2025
+ p.status === "active" ? "bg-[var(--surface-success-bg)] text-[var(--surface-success-text)]" : "bg-[var(--surface-warning-bg)] text-[var(--surface-warning-text)]"
2026
+ ), children: p.status }) }),
2027
+ /* @__PURE__ */ jsx18("td", { className: "px-4 py-3 text-right", children: onRemovePort && /* @__PURE__ */ jsx18(
2028
+ "button",
2029
+ {
2030
+ type: "button",
2031
+ onClick: () => onRemovePort(p.port),
2032
+ className: "p-1 text-muted-foreground hover:text-destructive transition-colors rounded",
2033
+ children: /* @__PURE__ */ jsx18(Trash23, { className: "h-3.5 w-3.5" })
2034
+ }
2035
+ ) })
2036
+ ] }, p.port)) })
2037
+ ] }) }) : /* @__PURE__ */ jsxs16("div", { className: "rounded-lg border border-border bg-muted/20 p-6 text-center", children: [
2038
+ /* @__PURE__ */ jsx18(Globe, { className: "mx-auto h-8 w-8 text-muted-foreground mb-2" }),
2039
+ /* @__PURE__ */ jsx18("p", { className: "text-sm text-muted-foreground", children: "No ports exposed yet" })
2040
+ ] }),
2041
+ /* @__PURE__ */ jsxs16("div", { className: "flex items-center gap-3", children: [
2042
+ /* @__PURE__ */ jsx18(
2043
+ "input",
2044
+ {
2045
+ type: "number",
2046
+ min: 1,
2047
+ max: 65535,
2048
+ placeholder: "Port (e.g. 3000)",
2049
+ value: newPort,
2050
+ onChange: (e) => setNewPort(e.target.value),
2051
+ onKeyDown: (e) => e.key === "Enter" && handleExpose(),
2052
+ 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"
2053
+ }
2054
+ ),
2055
+ /* @__PURE__ */ jsxs16(
2056
+ "button",
2057
+ {
2058
+ type: "button",
2059
+ onClick: handleExpose,
2060
+ disabled: !newPort || isExposing,
2061
+ 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",
2062
+ children: [
2063
+ /* @__PURE__ */ jsx18(Plus4, { className: "h-4 w-4" }),
2064
+ "Expose"
2065
+ ]
2066
+ }
2067
+ )
2068
+ ] })
2069
+ ] });
2070
+ }
2071
+
2072
+ // src/dashboard/process-list.tsx
2073
+ import * as React6 from "react";
2074
+ import { Activity as Activity2, Plus as Plus5, Skull, Terminal as Terminal4 } from "lucide-react";
2075
+ import { jsx as jsx19, jsxs as jsxs17 } from "react/jsx-runtime";
2076
+ function formatUptime(startedAt) {
2077
+ if (!startedAt) return "-";
2078
+ const ms = Date.now() - new Date(startedAt).getTime();
2079
+ if (Number.isNaN(ms) || ms < 0) return "-";
2080
+ if (ms < 6e4) return `${Math.floor(ms / 1e3)}s`;
2081
+ if (ms < 36e5) return `${Math.floor(ms / 6e4)}m`;
2082
+ return `${Math.floor(ms / 36e5)}h ${Math.floor(ms % 36e5 / 6e4)}m`;
2083
+ }
2084
+ function ProcessList({ processes, onSpawn, onKill, loading = false, className }) {
2085
+ const [newCommand, setNewCommand] = React6.useState("");
2086
+ const handleSpawn = () => {
2087
+ const cmd = newCommand.trim();
2088
+ if (cmd) {
2089
+ onSpawn(cmd);
2090
+ setNewCommand("");
2091
+ }
2092
+ };
2093
+ return /* @__PURE__ */ jsxs17("div", { className: cn("space-y-4", className), children: [
2094
+ loading ? /* @__PURE__ */ jsxs17("div", { className: "rounded-lg border border-border bg-muted/20 p-6 text-center", children: [
2095
+ /* @__PURE__ */ jsx19(Activity2, { className: "mx-auto h-6 w-6 text-muted-foreground animate-spin mb-2" }),
2096
+ /* @__PURE__ */ jsx19("p", { className: "text-sm text-muted-foreground", children: "Loading processes..." })
2097
+ ] }) : processes.length > 0 ? /* @__PURE__ */ jsx19("div", { className: "rounded-lg border border-border overflow-hidden", children: /* @__PURE__ */ jsxs17("table", { className: "w-full text-sm", children: [
2098
+ /* @__PURE__ */ jsx19("thead", { className: "bg-muted/30 border-b border-border", children: /* @__PURE__ */ jsxs17("tr", { children: [
2099
+ /* @__PURE__ */ jsx19("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "PID" }),
2100
+ /* @__PURE__ */ jsx19("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Command" }),
2101
+ /* @__PURE__ */ jsx19("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Status" }),
2102
+ /* @__PURE__ */ jsx19("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Uptime" }),
2103
+ /* @__PURE__ */ jsx19("th", { className: "px-4 py-2.5 text-right text-xs font-medium text-muted-foreground w-20" })
2104
+ ] }) }),
2105
+ /* @__PURE__ */ jsx19("tbody", { className: "divide-y divide-border", children: processes.map((p) => /* @__PURE__ */ jsxs17("tr", { children: [
2106
+ /* @__PURE__ */ jsx19("td", { className: "px-4 py-3 font-mono text-xs text-foreground", children: p.pid }),
2107
+ /* @__PURE__ */ jsx19("td", { className: "px-4 py-3 font-mono text-xs text-foreground truncate max-w-[250px]", children: p.command }),
2108
+ /* @__PURE__ */ jsx19("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsx19("span", { className: cn(
2109
+ "inline-flex items-center gap-1.5 rounded-full px-2 py-0.5 text-[10px] font-bold uppercase tracking-wider",
2110
+ p.running ? "bg-[var(--surface-success-bg)] text-[var(--surface-success-text)]" : "bg-muted text-muted-foreground"
2111
+ ), children: p.running ? "running" : `exited (${p.exitCode ?? "?"})` }) }),
2112
+ /* @__PURE__ */ jsx19("td", { className: "px-4 py-3 font-mono text-xs text-muted-foreground", children: formatUptime(p.startedAt) }),
2113
+ /* @__PURE__ */ jsx19("td", { className: "px-4 py-3 text-right", children: p.running && /* @__PURE__ */ jsx19(
2114
+ "button",
2115
+ {
2116
+ type: "button",
2117
+ onClick: () => onKill(p.pid),
2118
+ className: "p-1 text-muted-foreground hover:text-destructive transition-colors rounded",
2119
+ title: "Kill process",
2120
+ children: /* @__PURE__ */ jsx19(Skull, { className: "h-3.5 w-3.5" })
2121
+ }
2122
+ ) })
2123
+ ] }, `${p.pid}-${p.startedAt ?? p.command}`)) })
2124
+ ] }) }) : /* @__PURE__ */ jsxs17("div", { className: "rounded-lg border border-border bg-muted/20 p-6 text-center", children: [
2125
+ /* @__PURE__ */ jsx19(Terminal4, { className: "mx-auto h-8 w-8 text-muted-foreground mb-2" }),
2126
+ /* @__PURE__ */ jsx19("p", { className: "text-sm text-muted-foreground", children: "No processes running" })
2127
+ ] }),
2128
+ /* @__PURE__ */ jsxs17("div", { className: "flex items-center gap-3", children: [
2129
+ /* @__PURE__ */ jsx19(
2130
+ "input",
2131
+ {
2132
+ type: "text",
2133
+ placeholder: "Command (e.g. node server.js)",
2134
+ value: newCommand,
2135
+ onChange: (e) => setNewCommand(e.target.value),
2136
+ onKeyDown: (e) => e.key === "Enter" && handleSpawn(),
2137
+ 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"
2138
+ }
2139
+ ),
2140
+ /* @__PURE__ */ jsxs17(
2141
+ "button",
2142
+ {
2143
+ type: "button",
2144
+ onClick: handleSpawn,
2145
+ disabled: !newCommand.trim(),
2146
+ 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",
2147
+ children: [
2148
+ /* @__PURE__ */ jsx19(Plus5, { className: "h-4 w-4" }),
2149
+ "Spawn"
2150
+ ]
2151
+ }
2152
+ )
2153
+ ] })
2154
+ ] });
2155
+ }
2156
+
2157
+ // src/dashboard/network-config.tsx
2158
+ import * as React7 from "react";
2159
+ import { Network as Network2, Plus as Plus6, Trash2 as Trash24, ShieldAlert } from "lucide-react";
2160
+ import { jsx as jsx20, jsxs as jsxs18 } from "react/jsx-runtime";
2161
+ function NetworkConfig({ config, onUpdate, loading = false, className }) {
2162
+ const [newCidr, setNewCidr] = React7.useState("");
2163
+ const isValidCidr = (value) => {
2164
+ const match = value.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\/(\d{1,2})$/);
2165
+ if (!match) return false;
2166
+ const octets = [match[1], match[2], match[3], match[4]].map(Number);
2167
+ const prefix = Number(match[5]);
2168
+ return octets.every((o) => o <= 255) && prefix <= 32;
2169
+ };
2170
+ const handleAddCidr = () => {
2171
+ const cidr = newCidr.trim();
2172
+ if (cidr && config && isValidCidr(cidr) && !config.allowList.includes(cidr)) {
2173
+ onUpdate({ allowList: [...config.allowList, cidr] });
2174
+ setNewCidr("");
2175
+ }
2176
+ };
2177
+ const handleRemoveCidr = (cidr) => {
2178
+ if (config) {
2179
+ onUpdate({ allowList: config.allowList.filter((c) => c !== cidr) });
2180
+ }
2181
+ };
2182
+ if (loading || !config) {
2183
+ return /* @__PURE__ */ jsxs18("div", { className: cn("rounded-lg border border-border bg-muted/20 p-6 text-center", className), children: [
2184
+ /* @__PURE__ */ jsx20(Network2, { className: "mx-auto h-6 w-6 text-muted-foreground animate-pulse mb-2" }),
2185
+ /* @__PURE__ */ jsx20("p", { className: "text-sm text-muted-foreground", children: "Loading network configuration..." })
2186
+ ] });
2187
+ }
2188
+ return /* @__PURE__ */ jsxs18("div", { className: cn("space-y-4", className), children: [
2189
+ /* @__PURE__ */ jsxs18("div", { className: "flex items-center justify-between rounded-lg border border-border bg-card px-4 py-3", children: [
2190
+ /* @__PURE__ */ jsxs18("div", { className: "flex items-center gap-3", children: [
2191
+ /* @__PURE__ */ jsx20(ShieldAlert, { className: "h-4 w-4 text-muted-foreground" }),
2192
+ /* @__PURE__ */ jsxs18("div", { children: [
2193
+ /* @__PURE__ */ jsx20("p", { className: "text-sm font-medium text-foreground", children: "Block Outbound Traffic" }),
2194
+ /* @__PURE__ */ jsx20("p", { className: "text-xs text-muted-foreground", children: "Prevent the sandbox from making external network requests" })
2195
+ ] })
2196
+ ] }),
2197
+ /* @__PURE__ */ jsx20(
2198
+ "button",
2199
+ {
2200
+ type: "button",
2201
+ role: "switch",
2202
+ "aria-checked": config.blockOutbound,
2203
+ "aria-label": "Block outbound traffic",
2204
+ onClick: () => onUpdate({ blockOutbound: !config.blockOutbound }),
2205
+ className: cn(
2206
+ "relative inline-flex h-6 w-11 items-center rounded-full transition-colors",
2207
+ config.blockOutbound ? "bg-destructive" : "bg-muted"
2208
+ ),
2209
+ children: /* @__PURE__ */ jsx20(
2210
+ "span",
2211
+ {
2212
+ className: cn(
2213
+ "inline-block h-4 w-4 rounded-full bg-white transition-transform shadow-sm",
2214
+ config.blockOutbound ? "translate-x-6" : "translate-x-1"
2215
+ )
2216
+ }
2217
+ )
2218
+ }
2219
+ )
2220
+ ] }),
2221
+ /* @__PURE__ */ jsxs18("div", { children: [
2222
+ /* @__PURE__ */ jsx20("h4", { className: "text-xs font-medium text-muted-foreground mb-2 uppercase tracking-wider", children: "Allowlist (CIDR)" }),
2223
+ config.allowList.length > 0 ? /* @__PURE__ */ jsx20("div", { className: "space-y-1.5 mb-3", children: config.allowList.map((cidr) => /* @__PURE__ */ jsxs18("div", { className: "flex items-center justify-between rounded border border-border bg-muted/20 px-3 py-2", children: [
2224
+ /* @__PURE__ */ jsx20("span", { className: "font-mono text-xs text-foreground", children: cidr }),
2225
+ /* @__PURE__ */ jsx20(
2226
+ "button",
2227
+ {
2228
+ type: "button",
2229
+ onClick: () => handleRemoveCidr(cidr),
2230
+ className: "p-1 text-muted-foreground hover:text-destructive transition-colors rounded",
2231
+ children: /* @__PURE__ */ jsx20(Trash24, { className: "h-3 w-3" })
2232
+ }
2233
+ )
2234
+ ] }, cidr)) }) : /* @__PURE__ */ jsx20("p", { className: "text-xs text-muted-foreground mb-3", children: "No CIDR rules configured. All traffic is allowed." }),
2235
+ /* @__PURE__ */ jsxs18("div", { className: "flex items-center gap-3", children: [
2236
+ /* @__PURE__ */ jsx20(
2237
+ "input",
2238
+ {
2239
+ type: "text",
2240
+ placeholder: "CIDR (e.g. 10.0.0.0/8)",
2241
+ value: newCidr,
2242
+ onChange: (e) => setNewCidr(e.target.value),
2243
+ onKeyDown: (e) => e.key === "Enter" && handleAddCidr(),
2244
+ 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"
2245
+ }
2246
+ ),
2247
+ /* @__PURE__ */ jsxs18(
2248
+ "button",
2249
+ {
2250
+ type: "button",
2251
+ onClick: handleAddCidr,
2252
+ disabled: !newCidr.trim(),
2253
+ 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",
2254
+ children: [
2255
+ /* @__PURE__ */ jsx20(Plus6, { className: "h-4 w-4" }),
2256
+ "Add"
2257
+ ]
2258
+ }
2259
+ )
2260
+ ] })
2261
+ ] })
2262
+ ] });
2263
+ }
2264
+
2265
+ // src/dashboard/backend-config.tsx
2266
+ import * as React8 from "react";
2267
+ import { Bot, Plus as Plus7, RefreshCw as RefreshCw2, Trash2 as Trash25, Server, Wrench } from "lucide-react";
2268
+ import { Fragment as Fragment9, jsx as jsx21, jsxs as jsxs19 } from "react/jsx-runtime";
2269
+ function BackendConfig({
2270
+ status,
2271
+ mcpServers,
2272
+ onAddMcp,
2273
+ onRemoveMcp,
2274
+ onRestart,
2275
+ loading = false,
2276
+ className
2277
+ }) {
2278
+ const [showAddMcp, setShowAddMcp] = React8.useState(false);
2279
+ const [mcpName, setMcpName] = React8.useState("");
2280
+ const [mcpCommand, setMcpCommand] = React8.useState("");
2281
+ const [mcpArgs, setMcpArgs] = React8.useState("");
2282
+ const handleAddMcp = () => {
2283
+ const name = mcpName.trim();
2284
+ const command = mcpCommand.trim();
2285
+ if (name && command) {
2286
+ onAddMcp({
2287
+ name,
2288
+ command,
2289
+ args: mcpArgs.trim() ? mcpArgs.trim().split(/\s+/) : void 0
2290
+ });
2291
+ setMcpName("");
2292
+ setMcpCommand("");
2293
+ setMcpArgs("");
2294
+ setShowAddMcp(false);
2295
+ }
2296
+ };
2297
+ if (loading || !status) {
2298
+ return /* @__PURE__ */ jsxs19("div", { className: cn("rounded-lg border border-border bg-muted/20 p-6 text-center", className), children: [
2299
+ /* @__PURE__ */ jsx21(Bot, { className: "mx-auto h-6 w-6 text-muted-foreground animate-pulse mb-2" }),
2300
+ /* @__PURE__ */ jsx21("p", { className: "text-sm text-muted-foreground", children: "Loading backend status..." })
2301
+ ] });
2302
+ }
2303
+ return /* @__PURE__ */ jsxs19("div", { className: cn("space-y-4", className), children: [
2304
+ /* @__PURE__ */ jsxs19("div", { className: "rounded-lg border border-border bg-card overflow-hidden", children: [
2305
+ /* @__PURE__ */ jsxs19("div", { className: "px-4 py-3 border-b border-border bg-muted/30 flex items-center justify-between", children: [
2306
+ /* @__PURE__ */ jsx21("h4", { className: "text-xs font-medium text-muted-foreground uppercase tracking-wider", children: "Agent Status" }),
2307
+ /* @__PURE__ */ jsxs19(
2308
+ "button",
2309
+ {
2310
+ type: "button",
2311
+ onClick: onRestart,
2312
+ 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",
2313
+ children: [
2314
+ /* @__PURE__ */ jsx21(RefreshCw2, { className: "h-3 w-3" }),
2315
+ "Restart"
2316
+ ]
2317
+ }
2318
+ )
2319
+ ] }),
2320
+ /* @__PURE__ */ jsx21("div", { className: "p-4", children: /* @__PURE__ */ jsxs19("dl", { className: "grid grid-cols-[100px_1fr] gap-y-3 text-sm", children: [
2321
+ /* @__PURE__ */ jsx21("dt", { className: "text-muted-foreground", children: "Status" }),
2322
+ /* @__PURE__ */ jsx21("dd", { children: /* @__PURE__ */ jsx21("span", { className: cn(
2323
+ "inline-flex items-center gap-1.5 rounded-full px-2 py-0.5 text-[10px] font-bold uppercase tracking-wider",
2324
+ status.running ? "bg-[var(--surface-success-bg)] text-[var(--surface-success-text)]" : "bg-destructive/10 text-destructive"
2325
+ ), children: status.running ? "Running" : "Stopped" }) }),
2326
+ /* @__PURE__ */ jsx21("dt", { className: "text-muted-foreground", children: "Model" }),
2327
+ /* @__PURE__ */ jsx21("dd", { className: "font-mono text-xs", children: status.model ?? "Default" }),
2328
+ status.provider && /* @__PURE__ */ jsxs19(Fragment9, { children: [
2329
+ /* @__PURE__ */ jsx21("dt", { className: "text-muted-foreground", children: "Provider" }),
2330
+ /* @__PURE__ */ jsx21("dd", { className: "font-mono text-xs", children: status.provider })
2331
+ ] })
2332
+ ] }) })
2333
+ ] }),
2334
+ /* @__PURE__ */ jsxs19("div", { className: "rounded-lg border border-border bg-card overflow-hidden", children: [
2335
+ /* @__PURE__ */ jsxs19("div", { className: "px-4 py-3 border-b border-border bg-muted/30 flex items-center justify-between", children: [
2336
+ /* @__PURE__ */ jsxs19("h4", { className: "text-xs font-medium text-muted-foreground uppercase tracking-wider flex items-center gap-2", children: [
2337
+ /* @__PURE__ */ jsx21(Wrench, { className: "h-3.5 w-3.5" }),
2338
+ "MCP Servers"
2339
+ ] }),
2340
+ /* @__PURE__ */ jsxs19(
2341
+ "button",
2342
+ {
2343
+ type: "button",
2344
+ onClick: () => setShowAddMcp(!showAddMcp),
2345
+ 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",
2346
+ children: [
2347
+ /* @__PURE__ */ jsx21(Plus7, { className: "h-3 w-3" }),
2348
+ "Add"
2349
+ ]
2350
+ }
2351
+ )
2352
+ ] }),
2353
+ mcpServers.length > 0 ? /* @__PURE__ */ jsx21("div", { className: "divide-y divide-border", children: mcpServers.map((s) => /* @__PURE__ */ jsxs19("div", { className: "flex items-center justify-between px-4 py-3", children: [
2354
+ /* @__PURE__ */ jsxs19("div", { className: "flex items-center gap-3 min-w-0", children: [
2355
+ /* @__PURE__ */ jsx21(Server, { className: "h-3.5 w-3.5 text-muted-foreground shrink-0" }),
2356
+ /* @__PURE__ */ jsxs19("div", { className: "min-w-0", children: [
2357
+ /* @__PURE__ */ jsx21("p", { className: "text-sm font-medium text-foreground truncate", children: s.name }),
2358
+ /* @__PURE__ */ jsxs19("p", { className: "text-xs font-mono text-muted-foreground truncate", children: [
2359
+ s.command,
2360
+ " ",
2361
+ s.args?.join(" ") ?? ""
2362
+ ] })
2363
+ ] })
2364
+ ] }),
2365
+ /* @__PURE__ */ jsxs19("div", { className: "flex items-center gap-2 shrink-0", children: [
2366
+ s.status && /* @__PURE__ */ jsx21("span", { className: cn(
2367
+ "inline-flex items-center rounded-full px-1.5 py-0.5 text-[9px] font-bold uppercase",
2368
+ 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"
2369
+ ), children: s.status }),
2370
+ /* @__PURE__ */ jsx21(
2371
+ "button",
2372
+ {
2373
+ type: "button",
2374
+ onClick: () => onRemoveMcp(s.name),
2375
+ className: "p-1 text-muted-foreground hover:text-destructive transition-colors rounded",
2376
+ children: /* @__PURE__ */ jsx21(Trash25, { className: "h-3.5 w-3.5" })
2377
+ }
2378
+ )
2379
+ ] })
2380
+ ] }, s.name)) }) : /* @__PURE__ */ jsx21("div", { className: "p-4 text-center", children: /* @__PURE__ */ jsx21("p", { className: "text-xs text-muted-foreground", children: "No MCP servers configured" }) }),
2381
+ showAddMcp && /* @__PURE__ */ jsxs19("div", { className: "p-4 border-t border-border bg-muted/10 space-y-2", children: [
2382
+ /* @__PURE__ */ jsx21(
2383
+ "input",
2384
+ {
2385
+ type: "text",
2386
+ placeholder: "Server name",
2387
+ value: mcpName,
2388
+ onChange: (e) => setMcpName(e.target.value),
2389
+ 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"
2390
+ }
2391
+ ),
2392
+ /* @__PURE__ */ jsx21(
2393
+ "input",
2394
+ {
2395
+ type: "text",
2396
+ placeholder: "Command (e.g. npx @server/mcp)",
2397
+ value: mcpCommand,
2398
+ onChange: (e) => setMcpCommand(e.target.value),
2399
+ 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"
2400
+ }
2401
+ ),
2402
+ /* @__PURE__ */ jsx21(
2403
+ "input",
2404
+ {
2405
+ type: "text",
2406
+ placeholder: "Arguments (space-separated, optional)",
2407
+ value: mcpArgs,
2408
+ onChange: (e) => setMcpArgs(e.target.value),
2409
+ 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"
2410
+ }
2411
+ ),
2412
+ /* @__PURE__ */ jsxs19("div", { className: "flex justify-end gap-2 pt-1", children: [
2413
+ /* @__PURE__ */ jsx21(
2414
+ "button",
2415
+ {
2416
+ type: "button",
2417
+ onClick: () => setShowAddMcp(false),
2418
+ className: "rounded-md px-3 py-1.5 text-xs font-medium text-muted-foreground hover:text-foreground transition-colors",
2419
+ children: "Cancel"
2420
+ }
2421
+ ),
2422
+ /* @__PURE__ */ jsx21(
2423
+ "button",
2424
+ {
2425
+ type: "button",
2426
+ onClick: handleAddMcp,
2427
+ disabled: !mcpName.trim() || !mcpCommand.trim(),
2428
+ 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",
2429
+ children: "Add Server"
2430
+ }
2431
+ )
2432
+ ] })
2433
+ ] })
2434
+ ] })
2435
+ ] });
2436
+ }
2437
+
2438
+ // src/dashboard/snapshot-list.tsx
2439
+ import * as React9 from "react";
2440
+ import { Camera, Clock as Clock4, HardDrive, Plus as Plus8, RotateCcw } from "lucide-react";
2441
+ import { jsx as jsx22, jsxs as jsxs20 } from "react/jsx-runtime";
2442
+ function formatBytes(bytes) {
2443
+ if (bytes == null || bytes < 0) return "-";
2444
+ if (bytes === 0) return "0 B";
2445
+ const k = 1024;
2446
+ const sizes = ["B", "KB", "MB", "GB", "TB"];
2447
+ const i = Math.min(Math.floor(Math.log(bytes) / Math.log(k)), sizes.length - 1);
2448
+ return `${Number.parseFloat((bytes / k ** i).toFixed(1))} ${sizes[i]}`;
2449
+ }
2450
+ function formatDate(dateStr) {
2451
+ const d = new Date(dateStr);
2452
+ if (Number.isNaN(d.getTime())) return dateStr;
2453
+ return d.toLocaleDateString(void 0, { month: "short", day: "numeric", hour: "2-digit", minute: "2-digit" });
2454
+ }
2455
+ function SnapshotList({ snapshots, onCreate, onRestore, onSaveAsTemplate, loading = false, className }) {
2456
+ const [showCreate, setShowCreate] = React9.useState(false);
2457
+ const [tags, setTags] = React9.useState("");
2458
+ const handleCreate = () => {
2459
+ const tagList = tags.trim() ? tags.trim().split(",").map((t) => t.trim()).filter(Boolean) : void 0;
2460
+ onCreate(tagList);
2461
+ setTags("");
2462
+ setShowCreate(false);
2463
+ };
2464
+ return /* @__PURE__ */ jsxs20("div", { className: cn("space-y-4", className), children: [
2465
+ /* @__PURE__ */ jsxs20("div", { className: "flex items-center justify-between", children: [
2466
+ /* @__PURE__ */ jsx22("h3", { className: "text-sm font-bold text-foreground", children: "Snapshots" }),
2467
+ /* @__PURE__ */ jsxs20(
2468
+ "button",
2469
+ {
2470
+ type: "button",
2471
+ onClick: () => setShowCreate(!showCreate),
2472
+ 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",
2473
+ children: [
2474
+ /* @__PURE__ */ jsx22(Plus8, { className: "h-3.5 w-3.5" }),
2475
+ "Create Snapshot"
2476
+ ]
2477
+ }
2478
+ )
2479
+ ] }),
2480
+ showCreate && /* @__PURE__ */ jsxs20("div", { className: "rounded-lg border border-primary/20 bg-primary/5 p-4 space-y-3", children: [
2481
+ /* @__PURE__ */ jsx22("p", { className: "text-sm text-foreground font-medium", children: "Create a new snapshot of the current sandbox state." }),
2482
+ /* @__PURE__ */ jsx22(
2483
+ "input",
2484
+ {
2485
+ type: "text",
2486
+ placeholder: "Tags (comma-separated, optional)",
2487
+ value: tags,
2488
+ onChange: (e) => setTags(e.target.value),
2489
+ onKeyDown: (e) => e.key === "Enter" && handleCreate(),
2490
+ 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"
2491
+ }
2492
+ ),
2493
+ /* @__PURE__ */ jsxs20("div", { className: "flex justify-end gap-2", children: [
2494
+ /* @__PURE__ */ jsx22(
2495
+ "button",
2496
+ {
2497
+ type: "button",
2498
+ onClick: () => setShowCreate(false),
2499
+ className: "rounded-md px-3 py-1.5 text-xs font-medium text-muted-foreground hover:text-foreground transition-colors",
2500
+ children: "Cancel"
2501
+ }
2502
+ ),
2503
+ /* @__PURE__ */ jsxs20(
2504
+ "button",
2505
+ {
2506
+ type: "button",
2507
+ onClick: handleCreate,
2508
+ className: "rounded-md bg-primary px-3 py-1.5 text-xs font-medium text-primary-foreground hover:bg-primary/90 transition-colors",
2509
+ children: [
2510
+ /* @__PURE__ */ jsx22(Camera, { className: "h-3 w-3 mr-1.5 inline" }),
2511
+ "Create"
2512
+ ]
2513
+ }
2514
+ )
2515
+ ] })
2516
+ ] }),
2517
+ loading ? /* @__PURE__ */ jsxs20("div", { className: "rounded-lg border border-border bg-muted/20 p-6 text-center", children: [
2518
+ /* @__PURE__ */ jsx22(Camera, { className: "mx-auto h-6 w-6 text-muted-foreground animate-pulse mb-2" }),
2519
+ /* @__PURE__ */ jsx22("p", { className: "text-sm text-muted-foreground", children: "Loading snapshots..." })
2520
+ ] }) : snapshots.length > 0 ? /* @__PURE__ */ jsx22("div", { className: "rounded-lg border border-border overflow-hidden", children: /* @__PURE__ */ jsxs20("table", { className: "w-full text-sm", children: [
2521
+ /* @__PURE__ */ jsx22("thead", { className: "bg-muted/30 border-b border-border", children: /* @__PURE__ */ jsxs20("tr", { children: [
2522
+ /* @__PURE__ */ jsx22("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "ID" }),
2523
+ /* @__PURE__ */ jsx22("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Created" }),
2524
+ /* @__PURE__ */ jsx22("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Size" }),
2525
+ /* @__PURE__ */ jsx22("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Tags" }),
2526
+ /* @__PURE__ */ jsx22("th", { className: "px-4 py-2.5 text-right text-xs font-medium text-muted-foreground w-24" })
2527
+ ] }) }),
2528
+ /* @__PURE__ */ jsx22("tbody", { className: "divide-y divide-border", children: snapshots.map((s) => /* @__PURE__ */ jsxs20("tr", { children: [
2529
+ /* @__PURE__ */ jsx22("td", { className: "px-4 py-3 font-mono text-xs text-foreground", children: s.id.slice(0, 12) }),
2530
+ /* @__PURE__ */ jsx22("td", { className: "px-4 py-3 text-xs text-muted-foreground", children: /* @__PURE__ */ jsxs20("span", { className: "inline-flex items-center gap-1.5", children: [
2531
+ /* @__PURE__ */ jsx22(Clock4, { className: "h-3 w-3" }),
2532
+ formatDate(s.createdAt)
2533
+ ] }) }),
2534
+ /* @__PURE__ */ jsx22("td", { className: "px-4 py-3 text-xs text-muted-foreground", children: /* @__PURE__ */ jsxs20("span", { className: "inline-flex items-center gap-1.5", children: [
2535
+ /* @__PURE__ */ jsx22(HardDrive, { className: "h-3 w-3" }),
2536
+ formatBytes(s.sizeBytes)
2537
+ ] }) }),
2538
+ /* @__PURE__ */ jsx22("td", { className: "px-4 py-3", children: s.tags?.length ? /* @__PURE__ */ jsx22("div", { className: "flex items-center gap-1 flex-wrap", children: s.tags.map((tag) => /* @__PURE__ */ jsx22("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__ */ jsx22("span", { className: "text-xs text-muted-foreground", children: "-" }) }),
2539
+ /* @__PURE__ */ jsx22("td", { className: "px-4 py-3 text-right", children: /* @__PURE__ */ jsxs20("div", { className: "flex items-center justify-end gap-2", children: [
2540
+ /* @__PURE__ */ jsxs20(
2541
+ "button",
2542
+ {
2543
+ type: "button",
2544
+ onClick: () => onRestore(s.id),
2545
+ 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",
2546
+ title: "Restore to new sandbox",
2547
+ children: [
2548
+ /* @__PURE__ */ jsx22(RotateCcw, { className: "h-3 w-3" }),
2549
+ "Restore"
2550
+ ]
2551
+ }
2552
+ ),
2553
+ onSaveAsTemplate && /* @__PURE__ */ jsx22(
2554
+ "button",
2555
+ {
2556
+ type: "button",
2557
+ onClick: () => onSaveAsTemplate(s.id),
2558
+ 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",
2559
+ title: "Save as reusable template",
2560
+ children: "Save as Template"
2561
+ }
2562
+ )
2563
+ ] }) })
2564
+ ] }, s.id)) })
2565
+ ] }) }) : /* @__PURE__ */ jsxs20("div", { className: "rounded-lg border border-border bg-muted/20 p-6 text-center", children: [
2566
+ /* @__PURE__ */ jsx22(Camera, { className: "mx-auto h-8 w-8 text-muted-foreground mb-2" }),
2567
+ /* @__PURE__ */ jsx22("p", { className: "text-sm text-muted-foreground", children: "No snapshots yet" }),
2568
+ /* @__PURE__ */ jsx22("p", { className: "text-xs text-muted-foreground mt-1", children: "Create a snapshot to save the current state of your sandbox." })
2569
+ ] })
2570
+ ] });
2571
+ }
2572
+
2573
+ // src/dashboard/promo-banner.tsx
2574
+ import { Fragment as Fragment10, jsx as jsx23, jsxs as jsxs21 } from "react/jsx-runtime";
2575
+ function PromoBanner({
2576
+ title,
2577
+ description,
2578
+ buttonLabel,
2579
+ href,
2580
+ onClick,
2581
+ icon,
2582
+ disabled = false,
2583
+ className
2584
+ }) {
2585
+ const buttonClasses = cn(
2586
+ "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",
2587
+ disabled ? "opacity-50 cursor-not-allowed" : "hover:bg-[var(--btn-primary-hover)]"
2588
+ );
2589
+ const buttonContent = /* @__PURE__ */ jsxs21(Fragment10, { children: [
2590
+ buttonLabel,
2591
+ /* @__PURE__ */ jsxs21("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: [
2592
+ /* @__PURE__ */ jsx23("path", { d: "M5 12h14" }),
2593
+ /* @__PURE__ */ jsx23("path", { d: "m12 5 7 7-7 7" })
2594
+ ] })
2595
+ ] });
2596
+ return /* @__PURE__ */ jsxs21("div", { className: cn("relative overflow-hidden rounded-xl bg-[var(--brand-strong)] p-8 md:flex md:items-center md:justify-between", className), children: [
2597
+ /* @__PURE__ */ jsxs21("div", { className: "relative z-10", children: [
2598
+ /* @__PURE__ */ jsx23("h3", { className: "text-xl font-bold text-[var(--brand-strong-text)]", children: title }),
2599
+ /* @__PURE__ */ jsx23("p", { className: "mt-2 max-w-md text-sm text-[var(--brand-strong-text-muted)]", children: description }),
2600
+ href && !disabled ? /* @__PURE__ */ jsx23("a", { href, target: "_blank", rel: "noopener noreferrer", onClick, className: buttonClasses, children: buttonContent }) : /* @__PURE__ */ jsx23("button", { type: "button", onClick, disabled, className: buttonClasses, children: buttonContent })
2601
+ ] }),
2602
+ icon && /* @__PURE__ */ jsx23("div", { className: "relative z-10 mt-6 flex items-center md:mt-0", children: /* @__PURE__ */ jsx23("div", { className: "flex h-16 w-16 items-center justify-center rounded-2xl border border-white/10 bg-white/10", children: icon }) }),
2603
+ /* @__PURE__ */ jsx23("div", { className: "pointer-events-none absolute inset-y-0 right-0 w-1/3 bg-gradient-to-l from-white/5 to-transparent" })
2604
+ ] });
2605
+ }
2606
+
2607
+ export {
2608
+ SIDEBAR_RAIL_WIDTH,
2609
+ SIDEBAR_PANEL_WIDTH,
2610
+ SIDEBAR_TOTAL_WIDTH,
2611
+ SIDEBAR_MOBILE_WIDTH,
2612
+ SidebarProvider,
2613
+ useSidebar,
2614
+ Sidebar,
2615
+ SidebarRail,
2616
+ SidebarRailHeader,
2617
+ SidebarRailNav,
2618
+ SidebarRailFooter,
2619
+ RailSeparator,
2620
+ RailButton,
2621
+ RailModeButton,
2622
+ SidebarPanel,
2623
+ SidebarPanelHeader,
2624
+ SidebarPanelContent,
2625
+ SidebarContent,
2626
+ ProfileAvatar,
2627
+ CreditBalance,
2628
+ ClusterStatusBar,
2629
+ ResourceMeter,
2630
+ canAdminSandbox,
2631
+ SandboxCard,
2632
+ NewSandboxCard,
2633
+ SandboxTable,
2634
+ InvoiceTable,
2635
+ PlanCards,
2636
+ BackendSelector,
2637
+ HARNESS_OPTIONS,
2638
+ HarnessPicker,
2639
+ DashboardLayout,
2640
+ ProfileSelector,
2641
+ ProfileComparison,
2642
+ VariantList,
2643
+ SystemLogsViewer,
2644
+ UsageSummary,
2645
+ GitPanel,
2646
+ PortsList,
2647
+ ProcessList,
2648
+ NetworkConfig,
2649
+ BackendConfig,
2650
+ SnapshotList,
2651
+ PromoBanner
2652
+ };