@tangle-network/sandbox-ui 0.113.6 → 0.114.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/dist/WorkflowGraph-B-NsdYz_.js +1961 -0
  2. package/dist/assets.d.ts +214 -206
  3. package/dist/assets.js +1057 -908
  4. package/dist/auth-CWe17Hly.js +37 -0
  5. package/dist/auth.d.ts +2 -30
  6. package/dist/auth.js +2 -15
  7. package/dist/chat-BOvFAe6p.js +903 -0
  8. package/dist/chat.d.ts +2 -230
  9. package/dist/chat.js +2 -34
  10. package/dist/connectors.d.ts +52 -49
  11. package/dist/connectors.js +595 -643
  12. package/dist/dashboard-CZ3VgNPR.js +4969 -0
  13. package/dist/dashboard.d.ts +4 -1297
  14. package/dist/dashboard.js +4 -152
  15. package/dist/editor.d.ts +2 -1
  16. package/dist/editor.js +2 -31
  17. package/dist/files.d.ts +2 -6
  18. package/dist/files.js +2 -33
  19. package/dist/globals.css +63 -3
  20. package/dist/hooks.d.ts +146 -148
  21. package/dist/hooks.js +222 -55
  22. package/dist/index-CKRaj1pM.d.ts +776 -0
  23. package/dist/index-CXX5IpH2.d.ts +2 -0
  24. package/dist/index-DPePq7c8.d.ts +1340 -0
  25. package/dist/index-DiyLLZuJ.d.ts +230 -0
  26. package/dist/index-DvmZLMT1.d.ts +30 -0
  27. package/dist/index-I8ZLVtHd.d.ts +330 -0
  28. package/dist/index-PRM636Ex.d.ts +86 -0
  29. package/dist/index.d.ts +21 -33
  30. package/dist/index.js +21 -701
  31. package/dist/info-panel-C9cqDzOL.js +594 -0
  32. package/dist/integrations.d.ts +133 -139
  33. package/dist/integrations.js +491 -673
  34. package/dist/markdown.d.ts +2 -1
  35. package/dist/markdown.js +2 -13
  36. package/dist/model-I-NIxeM-.js +1250 -0
  37. package/dist/model-brand-B9A4H9pJ.d.ts +98 -0
  38. package/dist/model-brand-hMyDTUqB.js +624 -0
  39. package/dist/motion-DkyBRJb9.js +37 -0
  40. package/dist/openui.d.ts +2 -1
  41. package/dist/openui.js +2 -7
  42. package/dist/pages.d.ts +307 -300
  43. package/dist/pages.js +4404 -4181
  44. package/dist/primitives-Dh1vG6J3.js +113 -0
  45. package/dist/primitives.d.ts +2 -74
  46. package/dist/primitives.js +2 -204
  47. package/dist/provider-logo-DjqmConF.js +311 -0
  48. package/dist/run.d.ts +2 -1
  49. package/dist/run.js +2 -23
  50. package/dist/sdk-hooks.d.ts +3 -4
  51. package/dist/sdk-hooks.js +3 -31
  52. package/dist/sidecar-DgH_vQ6m.d.ts +9 -0
  53. package/dist/stores.d.ts +14 -15
  54. package/dist/stores.js +13 -81
  55. package/dist/styles.css +63 -3
  56. package/dist/template-card-L-4gmjx2.d.ts +90 -0
  57. package/dist/terminal-CN9ZgHRt.js +197 -0
  58. package/dist/terminal.d.ts +60 -60
  59. package/dist/terminal.js +2 -10
  60. package/dist/tokens.css +51 -0
  61. package/dist/types.d.ts +4 -11
  62. package/dist/types.js +0 -1
  63. package/dist/use-pty-session-C8cE0adT.js +669 -0
  64. package/dist/use-sidecar-auth-CCTJcrq7.d.ts +145 -0
  65. package/dist/use-sidecar-auth-DkdcufOz.js +742 -0
  66. package/dist/utils-DzWbktWG.js +2 -0
  67. package/dist/utils.d.ts +2 -1
  68. package/dist/utils.js +2 -37
  69. package/dist/workbench-BCaCKl-J.js +8347 -0
  70. package/dist/workbench.d.ts +2 -310
  71. package/dist/workbench.js +2 -44
  72. package/dist/workflows.d.ts +341 -339
  73. package/dist/workflows.js +74 -109
  74. package/dist/workspace-B6AtI6ld.js +2510 -0
  75. package/dist/workspace.d.ts +2 -761
  76. package/dist/workspace.js +2 -51
  77. package/package.json +46 -40
  78. package/dist/WorkflowGraph-2QOORYIR.js +0 -1722
  79. package/dist/chunk-2BUPSB7O.js +0 -0
  80. package/dist/chunk-3KBNPQQF.js +0 -2697
  81. package/dist/chunk-3LIXVEEE.js +0 -279
  82. package/dist/chunk-72UF2H5H.js +0 -707
  83. package/dist/chunk-AI25G5MX.js +0 -944
  84. package/dist/chunk-ANSC5AFC.js +0 -314
  85. package/dist/chunk-AZ3AWMTM.js +0 -8
  86. package/dist/chunk-BGVEHENC.js +0 -10
  87. package/dist/chunk-CZ7CSFO4.js +0 -39
  88. package/dist/chunk-DYXXYGS4.js +0 -4700
  89. package/dist/chunk-EI44GEQ5.js +0 -6
  90. package/dist/chunk-IACRDXER.js +0 -219
  91. package/dist/chunk-IOB2PW5Z.js +0 -55
  92. package/dist/chunk-M4K7ZGR2.js +0 -24
  93. package/dist/chunk-MYZAW4RX.js +0 -665
  94. package/dist/chunk-OMUIPJQZ.js +0 -555
  95. package/dist/chunk-RQZRXKMR.js +0 -398
  96. package/dist/chunk-SOKKTB7W.js +0 -230
  97. package/dist/chunk-TADGS4Q7.js +0 -1010
  98. package/dist/chunk-UCQNOUN3.js +0 -7964
  99. package/dist/chunk-ULPLRBMF.js +0 -34
  100. package/dist/chunk-WID73FPH.js +0 -89
  101. package/dist/chunk-X3YXP7FG.js +0 -38
  102. package/dist/logo-DiAjq759.d.ts +0 -15
  103. package/dist/model-brand-BNnaUKKZ.d.ts +0 -99
  104. package/dist/ports-list-CDIvTNnX.d.ts +0 -17
  105. package/dist/sdk-hooks-Djkjq4cp.d.ts +0 -145
  106. package/dist/template-card-Bn-Bw5xC.d.ts +0 -87
@@ -1,4700 +0,0 @@
1
- import {
2
- Button,
3
- Logo
4
- } from "./chunk-ANSC5AFC.js";
5
- import {
6
- moonshot_default
7
- } from "./chunk-RQZRXKMR.js";
8
- import {
9
- MOTION_CONTROL,
10
- MOTION_SURFACE,
11
- MOTION_TRAVEL
12
- } from "./chunk-BGVEHENC.js";
13
- import {
14
- cn
15
- } from "./chunk-EI44GEQ5.js";
16
-
17
- // src/dashboard/app-sidebar.tsx
18
- import * as React3 from "react";
19
- import { createPortal as createPortal2 } from "react-dom";
20
- import { Avatar, AvatarFallback, AvatarImage } from "@tangle-network/ui/primitives";
21
- import {
22
- DropdownMenu,
23
- DropdownMenuContent,
24
- DropdownMenuItem,
25
- DropdownMenuLabel,
26
- DropdownMenuSeparator,
27
- DropdownMenuSub,
28
- DropdownMenuSubContent,
29
- DropdownMenuSubTrigger,
30
- DropdownMenuTrigger
31
- } from "@tangle-network/ui/primitives";
32
- import { Skeleton, useTheme } from "@tangle-network/ui/primitives";
33
- import { Check, Monitor, MoreHorizontal, PanelLeft } from "lucide-react";
34
-
35
- // src/dashboard/sidebar-context.tsx
36
- import * as React from "react";
37
- import { jsx } from "react/jsx-runtime";
38
- var PANEL_OPEN_KEY = "sandbox-sidebar-panel-open";
39
- var RAIL_COLLAPSED_KEY = "sandbox-sidebar-rail-collapsed";
40
- var SIDEBAR_MODE_KEY = "sandbox-sidebar-mode";
41
- var SIDEBAR_RAIL_WIDTH = 56;
42
- var SIDEBAR_PANEL_WIDTH = 268;
43
- var SIDEBAR_TOTAL_WIDTH = SIDEBAR_RAIL_WIDTH + SIDEBAR_PANEL_WIDTH;
44
- var SIDEBAR_MOBILE_WIDTH = 256;
45
- var SIDEBAR_RAIL_LABELED_WIDTH = 248;
46
- var SidebarContext = React.createContext(null);
47
- function readStorage(key, fallback) {
48
- if (typeof window === "undefined") return fallback;
49
- try {
50
- return localStorage.getItem(key) ?? fallback;
51
- } catch {
52
- return fallback;
53
- }
54
- }
55
- function writeStorage(key, value) {
56
- if (typeof window === "undefined") return;
57
- try {
58
- localStorage.setItem(key, value);
59
- } catch {
60
- }
61
- }
62
- function SidebarProvider({
63
- defaultPanelOpen = true,
64
- defaultMode = "projects",
65
- hasPanels = true,
66
- labeledRail = false,
67
- panelOpen: controlledPanelOpen,
68
- onPanelOpenChange,
69
- railCollapsed: controlledRailCollapsed,
70
- onRailCollapsedChange,
71
- defaultRailCollapsed = false,
72
- children
73
- }) {
74
- const isControlled = controlledPanelOpen !== void 0;
75
- const [uncontrolledPanelOpen, setUncontrolledPanelOpen] = React.useState(
76
- () => isControlled ? controlledPanelOpen : readStorage(PANEL_OPEN_KEY, String(defaultPanelOpen)) === "true"
77
- );
78
- const panelOpen = isControlled ? controlledPanelOpen : uncontrolledPanelOpen;
79
- const isRailControlled = controlledRailCollapsed !== void 0;
80
- const [uncontrolledRailCollapsed, setUncontrolledRailCollapsed] = React.useState(
81
- () => isRailControlled ? controlledRailCollapsed : readStorage(RAIL_COLLAPSED_KEY, String(defaultRailCollapsed)) === "true"
82
- );
83
- const railCollapsed = isRailControlled ? controlledRailCollapsed : uncontrolledRailCollapsed;
84
- const [mode, setModeState] = React.useState(
85
- () => isControlled ? defaultMode : readStorage(SIDEBAR_MODE_KEY, defaultMode)
86
- );
87
- const [hidden, setHidden] = React.useState(false);
88
- const setPanelOpen = React.useCallback((open) => {
89
- if (!isControlled) {
90
- setUncontrolledPanelOpen(open);
91
- writeStorage(PANEL_OPEN_KEY, String(open));
92
- }
93
- onPanelOpenChange?.(open);
94
- }, [isControlled, onPanelOpenChange]);
95
- const togglePanel = React.useCallback(() => {
96
- setPanelOpen(!panelOpen);
97
- }, [setPanelOpen, panelOpen]);
98
- const setRailCollapsed = React.useCallback((collapsed) => {
99
- if (!isRailControlled) {
100
- setUncontrolledRailCollapsed(collapsed);
101
- writeStorage(RAIL_COLLAPSED_KEY, String(collapsed));
102
- }
103
- onRailCollapsedChange?.(collapsed);
104
- }, [isRailControlled, onRailCollapsedChange]);
105
- const toggleRail = React.useCallback(() => {
106
- setRailCollapsed(!railCollapsed);
107
- }, [setRailCollapsed, railCollapsed]);
108
- const setMode = React.useCallback((m) => {
109
- setModeState(m);
110
- if (!isControlled) writeStorage(SIDEBAR_MODE_KEY, m);
111
- }, [isControlled]);
112
- const switchMode = React.useCallback((m) => {
113
- if (mode === m) {
114
- setPanelOpen(!panelOpen);
115
- return;
116
- }
117
- setModeState(m);
118
- if (!isControlled) writeStorage(SIDEBAR_MODE_KEY, m);
119
- setPanelOpen(true);
120
- }, [mode, panelOpen, isControlled, setPanelOpen]);
121
- const railWidth = labeledRail && !railCollapsed ? SIDEBAR_RAIL_LABELED_WIDTH : SIDEBAR_RAIL_WIDTH;
122
- const contentMargin = hidden ? 0 : panelOpen && hasPanels ? railWidth + SIDEBAR_PANEL_WIDTH : railWidth;
123
- const value = React.useMemo(
124
- () => ({
125
- panelOpen,
126
- setPanelOpen,
127
- togglePanel,
128
- mode,
129
- setMode,
130
- switchMode,
131
- hidden,
132
- setHidden,
133
- contentMargin,
134
- hasPanels,
135
- railCollapsed,
136
- setRailCollapsed,
137
- toggleRail,
138
- railWidth
139
- }),
140
- [panelOpen, setPanelOpen, togglePanel, mode, setMode, switchMode, hidden, setHidden, contentMargin, hasPanels, railCollapsed, setRailCollapsed, toggleRail, railWidth]
141
- );
142
- return /* @__PURE__ */ jsx(SidebarContext.Provider, { value, children });
143
- }
144
- function useSidebar() {
145
- const ctx = React.useContext(SidebarContext);
146
- if (!ctx) throw new Error("useSidebar must be used within a SidebarProvider");
147
- return ctx;
148
- }
149
-
150
- // src/dashboard/rail-tooltip.tsx
151
- import * as React2 from "react";
152
- import { createPortal } from "react-dom";
153
- import { jsx as jsx2, jsxs } from "react/jsx-runtime";
154
- var RAIL_FLOATING_SURFACE = "rounded-md border border-[var(--md3-outline-variant)] bg-surface-container-highest shadow-[0_8px_30px_rgba(0,0,0,0.45)] ring-1 ring-[#ffffff14]";
155
- function RailTooltip({ label, children, disabled, className }) {
156
- const ref = React2.useRef(null);
157
- const timer = React2.useRef(null);
158
- const [coords, setCoords] = React2.useState(null);
159
- React2.useEffect(() => () => {
160
- if (timer.current) clearTimeout(timer.current);
161
- }, []);
162
- React2.useEffect(() => {
163
- if (!disabled) return;
164
- if (timer.current) {
165
- clearTimeout(timer.current);
166
- timer.current = null;
167
- }
168
- setCoords(null);
169
- }, [disabled]);
170
- const open = () => {
171
- if (disabled) return;
172
- if (timer.current) clearTimeout(timer.current);
173
- timer.current = setTimeout(() => {
174
- const el = ref.current;
175
- if (!el) return;
176
- const r = el.getBoundingClientRect();
177
- setCoords({ top: r.top + r.height / 2, left: r.right + 8 });
178
- }, 250);
179
- };
180
- const close = () => {
181
- if (timer.current) clearTimeout(timer.current);
182
- setCoords(null);
183
- };
184
- return /* @__PURE__ */ jsxs(
185
- "span",
186
- {
187
- ref,
188
- className: cn("relative flex shrink-0", className),
189
- onMouseEnter: open,
190
- onMouseLeave: close,
191
- onFocusCapture: open,
192
- onBlurCapture: close,
193
- children: [
194
- children,
195
- !disabled && coords !== null && typeof document !== "undefined" && createPortal(
196
- // Two elements, because the centering transform and the entrance
197
- // transform cannot share one: `.agent-pop-in` fills forwards to
198
- // `transform: none`, which would eat the `translateY(-50%)` and leave
199
- // every tooltip half a line low. The outer span owns the position, the
200
- // inner one owns the motion.
201
- /* @__PURE__ */ jsx2(
202
- "span",
203
- {
204
- style: { position: "fixed", top: coords.top, left: coords.left, transform: "translateY(-50%)" },
205
- className: "pointer-events-none z-[70]",
206
- children: /* @__PURE__ */ jsx2(
207
- "span",
208
- {
209
- role: "tooltip",
210
- "aria-hidden": "true",
211
- className: cn(
212
- // Appears in place beside the trigger after the open delay, so
213
- // it scales up rather than travelling — @see .agent-pop-in.
214
- "agent-pop-in block whitespace-nowrap px-2 py-1 text-xs font-medium text-popover-foreground",
215
- RAIL_FLOATING_SURFACE
216
- ),
217
- children: label
218
- }
219
- )
220
- }
221
- ),
222
- document.body
223
- )
224
- ]
225
- }
226
- );
227
- }
228
-
229
- // src/dashboard/app-sidebar.tsx
230
- import { focusRing } from "@tangle-network/ui/utils";
231
- import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
232
- function DefaultLink({
233
- href,
234
- to,
235
- className,
236
- children,
237
- ...rest
238
- }) {
239
- return /* @__PURE__ */ jsx3("a", { href: href ?? to, className, ...rest, children });
240
- }
241
- function getInitials(name, email) {
242
- if (name) return name.split(" ").map((w) => w[0]).filter(Boolean).slice(0, 2).join("").toUpperCase();
243
- if (email) return email[0].toUpperCase();
244
- return "?";
245
- }
246
- function SettingsIcon({ className }) {
247
- return /* @__PURE__ */ jsxs2("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: [
248
- /* @__PURE__ */ jsx3("title", { children: "Settings icon" }),
249
- /* @__PURE__ */ jsx3("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" }),
250
- /* @__PURE__ */ jsx3("circle", { cx: "12", cy: "12", r: "3" })
251
- ] });
252
- }
253
- function LogOutIcon({ className }) {
254
- return /* @__PURE__ */ jsxs2("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: [
255
- /* @__PURE__ */ jsx3("title", { children: "Log out icon" }),
256
- /* @__PURE__ */ jsx3("path", { d: "M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" }),
257
- /* @__PURE__ */ jsx3("polyline", { points: "16,17 21,12 16,7" }),
258
- /* @__PURE__ */ jsx3("line", { x1: "21", x2: "9", y1: "12", y2: "12" })
259
- ] });
260
- }
261
- function SunIcon({ className }) {
262
- return /* @__PURE__ */ jsxs2("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: [
263
- /* @__PURE__ */ jsx3("title", { children: "Light mode icon" }),
264
- /* @__PURE__ */ jsx3("circle", { cx: "12", cy: "12", r: "4" }),
265
- /* @__PURE__ */ jsx3("path", { d: "M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41" })
266
- ] });
267
- }
268
- function MoonIcon({ className }) {
269
- return /* @__PURE__ */ jsxs2("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: [
270
- /* @__PURE__ */ jsx3("title", { children: "Dark mode icon" }),
271
- /* @__PURE__ */ jsx3("path", { d: "M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z" })
272
- ] });
273
- }
274
- function RailThemeToggle({ className }) {
275
- const { theme, setTheme } = useTheme();
276
- const [mounted, setMounted] = React3.useState(false);
277
- React3.useEffect(() => setMounted(true), []);
278
- const isDark = mounted && (theme === "dark" || theme === "system" && typeof window !== "undefined" && window.matchMedia("(prefers-color-scheme: dark)").matches);
279
- return /* @__PURE__ */ jsx3(
280
- "button",
281
- {
282
- type: "button",
283
- onClick: () => setTheme(isDark ? "light" : "dark"),
284
- "aria-label": "Toggle theme",
285
- title: isDark ? "Switch to light mode" : "Switch to dark mode",
286
- className: cn(
287
- "flex h-9 w-9 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors",
288
- MOTION_CONTROL,
289
- "hover:bg-[var(--accent-surface-soft)] hover:text-foreground",
290
- focusRing,
291
- className
292
- ),
293
- children: isDark ? /* @__PURE__ */ jsx3(SunIcon, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx3(MoonIcon, { className: "h-4 w-4" })
294
- }
295
- );
296
- }
297
- function ChevronsLeftIcon({ className }) {
298
- return /* @__PURE__ */ jsxs2("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: [
299
- /* @__PURE__ */ jsx3("title", { children: "Collapse sidebar" }),
300
- /* @__PURE__ */ jsx3("path", { d: "m11 17-5-5 5-5M18 17l-5-5 5-5" })
301
- ] });
302
- }
303
- function ChevronsRightIcon({ className }) {
304
- return /* @__PURE__ */ jsxs2("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: [
305
- /* @__PURE__ */ jsx3("title", { children: "Expand sidebar" }),
306
- /* @__PURE__ */ jsx3("path", { d: "m6 17 5-5-5-5M13 17l5-5-5-5" })
307
- ] });
308
- }
309
- function RailCollapseToggle({ collapsed, showLabel, onToggle, className }) {
310
- const label = collapsed ? "Expand sidebar" : "Collapse sidebar";
311
- const Icon2 = collapsed ? ChevronsRightIcon : ChevronsLeftIcon;
312
- return /* @__PURE__ */ jsx3(RailTooltip, { label, disabled: showLabel, className: showLabel ? "w-full" : void 0, children: /* @__PURE__ */ jsxs2(
313
- "button",
314
- {
315
- type: "button",
316
- onClick: onToggle,
317
- "aria-label": label,
318
- "aria-pressed": collapsed,
319
- className: cn(
320
- "flex shrink-0 items-center rounded-md text-muted-foreground transition-colors",
321
- MOTION_CONTROL,
322
- showLabel ? "h-9 w-full justify-start gap-2.5 px-2.5" : "h-9 w-9 justify-center",
323
- "hover:bg-[var(--accent-surface-soft)] hover:text-foreground",
324
- focusRing,
325
- className
326
- ),
327
- children: [
328
- /* @__PURE__ */ jsx3(Icon2, { className: "h-[17px] w-[17px] shrink-0" }),
329
- showLabel && /* @__PURE__ */ jsx3("span", { className: "text-[13.5px] font-medium", children: label })
330
- ]
331
- }
332
- ) });
333
- }
334
- function PanelToggleButton({ collapsed, onToggle, className }) {
335
- const label = collapsed ? "Expand sidebar" : "Collapse sidebar";
336
- return /* @__PURE__ */ jsx3(
337
- "button",
338
- {
339
- type: "button",
340
- onClick: onToggle,
341
- "aria-label": label,
342
- "aria-pressed": collapsed,
343
- title: label,
344
- className: cn(
345
- "flex h-8 w-8 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors",
346
- MOTION_CONTROL,
347
- "hover:bg-[var(--accent-surface-soft)] hover:text-foreground",
348
- focusRing,
349
- className
350
- ),
351
- children: /* @__PURE__ */ jsx3(PanelLeft, { className: "h-[18px] w-[18px] shrink-0" })
352
- }
353
- );
354
- }
355
- function isRenderable(node) {
356
- return React3.Children.toArray(node).some(
357
- (child) => React3.isValidElement(child) && child.type === React3.Fragment ? isRenderable(child.props.children) : true
358
- );
359
- }
360
- function RailHeader({ brand, brandHref, children, collapsed, onToggle, collapsible = true, LinkComponent, className }) {
361
- const Link = LinkComponent ?? DefaultLink;
362
- const hasBrand = isRenderable(brand);
363
- const hasChildren = isRenderable(children);
364
- const brandNode = brandHref !== void 0 ? /* @__PURE__ */ jsx3(
365
- Link,
366
- {
367
- href: brandHref,
368
- to: brandHref,
369
- "aria-label": "Home",
370
- className: cn("flex items-center justify-center rounded-md p-1 transition-colors hover:bg-[var(--accent-surface-soft)]", MOTION_CONTROL),
371
- children: brand
372
- }
373
- ) : /* @__PURE__ */ jsx3("span", { className: "flex items-center justify-center p-1", children: brand });
374
- return /* @__PURE__ */ jsx3(
375
- "div",
376
- {
377
- className: cn(
378
- "flex h-14 shrink-0 items-center border-b border-[var(--md3-outline-variant)]",
379
- collapsed ? "justify-center px-2" : "gap-2 px-3",
380
- className
381
- ),
382
- children: collapsed ? !hasBrand && hasChildren ? (
383
- // No brand mark: `children` (SidebarLayout's `railHeaderContent`) is
384
- // the only rail-top content the consumer gave us, so render it here
385
- // too. Dropping it left an EMPTY header — the collapsed rail showed
386
- // nothing at all where the product's mark belongs. With neither a
387
- // brand nor children, the plain expand button below still owns the
388
- // rail top so the rail stays expandable.
389
- //
390
- // Rendered outside the expand button on purpose: content that owns
391
- // the rail top is routinely interactive itself (a mark that expands
392
- // the rail on click), and a <button> inside a <button> is invalid
393
- // HTML that React refuses to hydrate.
394
- //
395
- // The explicit "Expand sidebar" control stays in the tree for
396
- // keyboard and assistive-tech users (the content may be a passive
397
- // switcher, and the empty-rail click fallback is mouse-only). It sits
398
- // over the content, invisible and non-interactive until it receives
399
- // keyboard focus, so it costs no rail width.
400
- /* @__PURE__ */ jsxs2("div", { className: "relative flex min-w-0 items-center justify-center", children: [
401
- children,
402
- collapsible && /* @__PURE__ */ jsx3(
403
- PanelToggleButton,
404
- {
405
- collapsed,
406
- onToggle,
407
- className: "pointer-events-none absolute inset-0 m-auto bg-background opacity-0 focus-visible:pointer-events-auto focus-visible:opacity-100"
408
- }
409
- )
410
- ] })
411
- ) : collapsible ? /* @__PURE__ */ jsx3(RailTooltip, { label: "Expand sidebar", children: /* @__PURE__ */ jsxs2(
412
- "button",
413
- {
414
- type: "button",
415
- onClick: onToggle,
416
- "aria-label": "Expand sidebar",
417
- "aria-pressed": true,
418
- className: cn("group/brand relative flex h-9 w-9 items-center justify-center rounded-md transition-colors hover:bg-[var(--accent-surface-soft)]", focusRing, MOTION_CONTROL),
419
- children: [
420
- /* @__PURE__ */ jsx3("span", { className: cn("flex items-center justify-center transition-opacity group-hover/brand:opacity-0", MOTION_CONTROL), children: brand }),
421
- /* @__PURE__ */ jsx3("span", { className: cn("pointer-events-none absolute inset-0 flex items-center justify-center text-foreground opacity-0 transition-opacity group-hover/brand:opacity-100", MOTION_CONTROL), children: /* @__PURE__ */ jsx3(PanelLeft, { className: "h-[18px] w-[18px]" }) })
422
- ]
423
- }
424
- ) }) : brandNode : /* @__PURE__ */ jsxs2(Fragment2, { children: [
425
- hasBrand && brandNode,
426
- /* @__PURE__ */ jsx3("div", { className: "min-w-0 flex-1", children }),
427
- collapsible && /* @__PURE__ */ jsx3(PanelToggleButton, { collapsed, onToggle })
428
- ] })
429
- }
430
- );
431
- }
432
- function Sidebar({ children, className, style }) {
433
- const { panelOpen, hidden, hasPanels, railWidth } = useSidebar();
434
- return /* @__PURE__ */ jsx3(
435
- "div",
436
- {
437
- "data-sidebar": "true",
438
- className: cn(
439
- "fixed inset-y-0 left-0 z-40 flex bg-surface-container-low border-r border-[var(--md3-outline-variant)] transition-[transform,width]",
440
- // The rail is a full-height surface travelling — the slow tier. It and
441
- // SidebarContent's margin MUST read the same token or the page content
442
- // visibly lags the edge it is supposed to be attached to.
443
- MOTION_TRAVEL,
444
- hidden && "-translate-x-full",
445
- className
446
- ),
447
- style: { width: panelOpen && hasPanels ? railWidth + SIDEBAR_PANEL_WIDTH : railWidth, ...style },
448
- children
449
- }
450
- );
451
- }
452
- function SidebarRail({ children, className, wide = false }) {
453
- const { railWidth } = useSidebar();
454
- return /* @__PURE__ */ jsx3(
455
- "div",
456
- {
457
- className: cn(
458
- "flex flex-col h-full shrink-0 bg-transparent",
459
- wide && "w-full",
460
- className
461
- ),
462
- style: wide ? void 0 : { width: railWidth },
463
- children
464
- }
465
- );
466
- }
467
- function SidebarRailHeader({ children, className }) {
468
- return /* @__PURE__ */ jsx3("div", { className: cn("flex h-14 shrink-0 items-center justify-center border-b border-[var(--md3-outline-variant)]", className), children });
469
- }
470
- function SidebarRailNav({ children, className, onClick }) {
471
- return /* @__PURE__ */ jsx3("nav", { onClick, className: cn("flex flex-col items-center gap-1 py-3 flex-1 min-h-0 overflow-y-auto", className), children });
472
- }
473
- function SidebarRailFooter({ children, className }) {
474
- return /* @__PURE__ */ jsx3("div", { className: cn("flex flex-col items-center gap-1 pb-3 shrink-0", className), children });
475
- }
476
- function RailSeparator({ className }) {
477
- return /* @__PURE__ */ jsx3("div", { className: cn("my-2 h-px w-10 bg-[var(--md3-outline-variant)]", className) });
478
- }
479
- function RailButton({ icon: Icon2, label, isActive, badge, onClick, className, showLabel, asChild, children, variant = "normal", style }) {
480
- const classes = cn(
481
- "relative flex shrink-0 items-center justify-center rounded-md transition-colors",
482
- MOTION_CONTROL,
483
- showLabel ? "w-full justify-start px-2.5 h-9 gap-2.5" : "w-9 h-9 justify-center",
484
- // Quiet press feedback via color only — no scale on the full-width row.
485
- focusRing,
486
- // The active item (route matches) and any explicit primary share one
487
- // emphasized look — a filled accent pill with an accent ring — so the
488
- // current destination clearly stands out from the default rows.
489
- variant === "primary" || isActive ? "bg-[var(--accent-surface-strong)] text-[var(--accent-text)] font-medium ring-1 ring-inset ring-[var(--border-accent)]" : "text-muted-foreground hover:bg-[var(--accent-surface-soft)] hover:text-foreground",
490
- className
491
- );
492
- const iconWithBadge = /* @__PURE__ */ jsxs2("span", { className: "relative flex shrink-0 items-center justify-center", children: [
493
- /* @__PURE__ */ jsx3(Icon2, { className: "h-[17px] w-[17px] shrink-0" }),
494
- badge !== void 0 && badge > 0 && /* @__PURE__ */ jsx3("span", { className: "absolute -top-1.5 -right-1.5 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", children: badge > 99 ? "99+" : badge })
495
- ] });
496
- const content = /* @__PURE__ */ jsxs2(Fragment2, { children: [
497
- iconWithBadge,
498
- showLabel && /* @__PURE__ */ jsx3("span", { className: "text-[13.5px] font-medium", children: label })
499
- ] });
500
- if (asChild && React3.isValidElement(children)) {
501
- const child = children;
502
- const merged = React3.cloneElement(
503
- child,
504
- {
505
- className: cn(classes, child.props.className),
506
- "aria-label": child.props["aria-label"] ?? label,
507
- // The layout's `style` wins the merge, not the child's. It is where
508
- // `--stagger-index` arrives, and a child that happened to carry a
509
- // `style` of its own would otherwise drop the variable silently and
510
- // collapse the whole rail's entrance to one flash — a defect with no
511
- // error and no visible cause.
512
- style: { ...child.props.style, ...style }
513
- },
514
- content
515
- );
516
- return /* @__PURE__ */ jsx3(RailTooltip, { label, disabled: showLabel, className: showLabel ? "w-full" : void 0, children: merged });
517
- }
518
- return /* @__PURE__ */ jsx3(RailTooltip, { label, disabled: showLabel, className: showLabel ? "w-full" : void 0, children: /* @__PURE__ */ jsx3("button", { type: "button", onClick, "aria-label": label, className: classes, style, children: content }) });
519
- }
520
- function RailFlyout({
521
- icon: Icon2,
522
- label,
523
- isActive,
524
- showLabel,
525
- title,
526
- children,
527
- className,
528
- style
529
- }) {
530
- const [open, setOpen] = React3.useState(false);
531
- const ref = React3.useRef(null);
532
- React3.useEffect(() => {
533
- if (!open) return;
534
- const onPointer = (e) => {
535
- if (ref.current && !ref.current.contains(e.target)) setOpen(false);
536
- };
537
- const onKey = (e) => {
538
- if (e.key === "Escape") setOpen(false);
539
- };
540
- document.addEventListener("mousedown", onPointer);
541
- document.addEventListener("keydown", onKey);
542
- return () => {
543
- document.removeEventListener("mousedown", onPointer);
544
- document.removeEventListener("keydown", onKey);
545
- };
546
- }, [open]);
547
- const active = isActive || open;
548
- const triggerClasses = cn(
549
- "relative flex shrink-0 items-center rounded-md transition-colors",
550
- MOTION_CONTROL,
551
- showLabel ? "w-full justify-start px-2.5 h-9 gap-2.5" : "w-9 h-9 justify-center",
552
- focusRing,
553
- active ? "bg-[var(--accent-surface-strong)] text-[var(--accent-text)]" : "text-muted-foreground hover:bg-[var(--accent-surface-soft)] hover:text-foreground",
554
- className
555
- );
556
- return /* @__PURE__ */ jsxs2("div", { ref, className: cn("relative", showLabel && "w-full"), style, children: [
557
- /* @__PURE__ */ jsx3(RailTooltip, { label, disabled: showLabel || open, className: showLabel ? "w-full" : void 0, children: /* @__PURE__ */ jsxs2(
558
- "button",
559
- {
560
- type: "button",
561
- "aria-label": label,
562
- "aria-haspopup": "menu",
563
- "aria-expanded": open,
564
- onClick: () => setOpen((v) => !v),
565
- className: triggerClasses,
566
- children: [
567
- /* @__PURE__ */ jsx3(Icon2, { className: "h-[17px] w-[17px] shrink-0" }),
568
- showLabel && /* @__PURE__ */ jsx3("span", { className: "text-sm font-medium", children: label }),
569
- showLabel && /* @__PURE__ */ jsx3(
570
- ChevronRightIcon,
571
- {
572
- className: cn(
573
- "ml-auto h-4 w-4 shrink-0 text-muted-foreground/60 transition-transform",
574
- MOTION_CONTROL,
575
- open && "rotate-180"
576
- )
577
- }
578
- )
579
- ]
580
- }
581
- ) }),
582
- open && // Anchored to the right edge of the rail item; full-height-independent
583
- // floating card. `onClickCapture` closes after a child link/button fires
584
- // so selecting a sub-surface dismisses the flyout.
585
- /* @__PURE__ */ jsxs2(
586
- "div",
587
- {
588
- role: "menu",
589
- onClickCapture: (e) => {
590
- const el = e.target;
591
- if (el.closest("a,button")) setOpen(false);
592
- },
593
- className: cn(
594
- "absolute left-full top-0 z-50 ml-2 min-w-[12rem] p-1.5",
595
- // A flyout appears in place beside its trigger; it did not travel
596
- // from anywhere, so it scales up rather than sliding.
597
- "agent-pop-in",
598
- RAIL_FLOATING_SURFACE
599
- ),
600
- children: [
601
- /* @__PURE__ */ jsx3("p", { className: "px-2 pb-1 pt-1 text-[10px] font-semibold uppercase tracking-wider text-muted-foreground/70 select-none", children: title ?? label }),
602
- children
603
- ]
604
- }
605
- )
606
- ] });
607
- }
608
- function ChevronRightIcon({ className }) {
609
- return /* @__PURE__ */ jsxs2("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: [
610
- /* @__PURE__ */ jsx3("title", { children: "Expand" }),
611
- /* @__PURE__ */ jsx3("path", { d: "m9 18 6-6-6-6" })
612
- ] });
613
- }
614
- function useExpandableItems(subItems, loadSubItems) {
615
- const [items, setItems] = React3.useState(subItems ?? null);
616
- const [loading, setLoading] = React3.useState(false);
617
- const loadedRef = React3.useRef(subItems !== void 0);
618
- React3.useEffect(() => {
619
- if (subItems !== void 0) {
620
- setItems(subItems);
621
- loadedRef.current = true;
622
- }
623
- }, [subItems]);
624
- const ensureLoaded = React3.useCallback(() => {
625
- if (loadedRef.current || !loadSubItems) return;
626
- loadedRef.current = true;
627
- setLoading(true);
628
- Promise.resolve(loadSubItems()).then(
629
- (resolved) => {
630
- setItems(resolved);
631
- setLoading(false);
632
- },
633
- () => {
634
- setItems([]);
635
- setLoading(false);
636
- loadedRef.current = false;
637
- }
638
- );
639
- }, [loadSubItems]);
640
- return { items, loading, ensureLoaded };
641
- }
642
- function SubItemsSkeleton() {
643
- const widths = ["w-[88%]", "w-[76%]", "w-[82%]", "w-[64%]"];
644
- return /* @__PURE__ */ jsx3("div", { className: "flex flex-col gap-1.5 px-2 py-1.5", children: widths.map((w, i) => /* @__PURE__ */ jsx3(Skeleton, { className: cn("h-4", w) }, i)) });
645
- }
646
- function RailExpandable({
647
- icon: Icon2,
648
- label,
649
- href,
650
- isActive,
651
- showLabel,
652
- subItems,
653
- loadSubItems,
654
- activeSubIds,
655
- emptyLabel = "Nothing here yet",
656
- defaultOpen,
657
- onNavigate,
658
- LinkComponent,
659
- className,
660
- style
661
- }) {
662
- const Link = LinkComponent ?? DefaultLink;
663
- const [open, setOpen] = React3.useState(defaultOpen ?? false);
664
- const { items, loading, ensureLoaded } = useExpandableItems(subItems, loadSubItems);
665
- React3.useEffect(() => {
666
- if (defaultOpen) ensureLoaded();
667
- }, [defaultOpen, ensureLoaded]);
668
- const [everOpened, setEverOpened] = React3.useState(defaultOpen ?? false);
669
- const discloseRef = React3.useRef(null);
670
- const discloseId = React3.useId();
671
- React3.useEffect(() => {
672
- const el = discloseRef.current;
673
- if (!el) return;
674
- if (open) el.removeAttribute("inert");
675
- else el.setAttribute("inert", "");
676
- }, [open]);
677
- const activeSet = React3.useMemo(() => new Set(activeSubIds ?? []), [activeSubIds]);
678
- const triggerRef = React3.useRef(null);
679
- const closeTimer = React3.useRef(null);
680
- const [coords, setCoords] = React3.useState(null);
681
- React3.useEffect(() => () => {
682
- if (closeTimer.current) clearTimeout(closeTimer.current);
683
- }, []);
684
- const subLink = (item) => {
685
- const FIcon = item.icon;
686
- const active = activeSet.has(item.id);
687
- const link = /* @__PURE__ */ jsxs2(
688
- Link,
689
- {
690
- href: item.href,
691
- to: item.href,
692
- onClick: onNavigate,
693
- ...item.prefetch !== void 0 ? { prefetch: item.prefetch } : {},
694
- className: cn(
695
- "flex min-w-0 flex-1 items-center gap-2 rounded-md px-2.5 text-[12px] transition-colors",
696
- MOTION_CONTROL,
697
- active ? "h-7 bg-[var(--accent-surface-strong)] font-medium text-[var(--accent-text)]" : item.emphasis ? "py-2 font-medium text-[var(--accent-text)] hover:underline" : "h-7 text-muted-foreground hover:bg-[var(--accent-surface-soft)] hover:text-foreground"
698
- ),
699
- children: [
700
- item.unread && !item.isLoading && !active && /* @__PURE__ */ jsx3("span", { className: "h-1.5 w-1.5 shrink-0 rounded-full bg-primary", "aria-hidden": true }),
701
- FIcon ? /* @__PURE__ */ jsx3(FIcon, { className: "h-4 w-4 shrink-0" }) : null,
702
- /* @__PURE__ */ jsx3(
703
- "span",
704
- {
705
- className: cn(
706
- "min-w-0 flex-1 truncate",
707
- item.isLoading && "agent-shimmer",
708
- item.unread && !active && "font-medium text-foreground"
709
- ),
710
- ...item.isLoading ? {
711
- role: "status",
712
- "aria-label": "Agent responding",
713
- // The sweep through the glyphs is the ONLY thing separating "this
714
- // agent is still working" from "this row is stale", so it opts out
715
- // of the reduced-motion floor. Decorative motion must not.
716
- "data-motion": "essential"
717
- } : {},
718
- children: item.label
719
- }
720
- )
721
- ]
722
- },
723
- item.id
724
- );
725
- if (!showLabel || !item.actions?.length) return link;
726
- return /* @__PURE__ */ jsxs2("div", { className: "group/sub relative flex items-center", children: [
727
- link,
728
- /* @__PURE__ */ jsxs2(DropdownMenu, { children: [
729
- /* @__PURE__ */ jsx3(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx3(
730
- "button",
731
- {
732
- type: "button",
733
- "aria-label": "Chat actions",
734
- className: cn(
735
- "absolute right-1 flex h-6 w-6 shrink-0 items-center justify-center rounded-md bg-[var(--accent-surface-soft)] text-muted-foreground opacity-0 transition hover:text-foreground focus-visible:opacity-100 group-hover/sub:opacity-100 data-[state=open]:bg-[var(--accent-surface-strong)] data-[state=open]:opacity-100",
736
- MOTION_CONTROL
737
- ),
738
- children: /* @__PURE__ */ jsx3(MoreHorizontal, { className: "h-3.5 w-3.5" })
739
- }
740
- ) }),
741
- /* @__PURE__ */ jsx3(DropdownMenuContent, { align: "end", className: "w-40", children: item.actions.map((action) => /* @__PURE__ */ jsxs2(
742
- DropdownMenuItem,
743
- {
744
- onSelect: action.onSelect,
745
- className: cn(
746
- "gap-2",
747
- action.destructive && "text-destructive focus:text-destructive"
748
- ),
749
- children: [
750
- action.icon ? /* @__PURE__ */ jsx3(action.icon, { className: "h-3.5 w-3.5" }) : null,
751
- action.label
752
- ]
753
- },
754
- action.id
755
- )) })
756
- ] })
757
- ] }, item.id);
758
- };
759
- const list = loading ? /* @__PURE__ */ jsx3(SubItemsSkeleton, {}) : items && items.length > 0 ? items.map(subLink) : /* @__PURE__ */ jsx3("p", { className: "px-2.5 py-1.5 text-xs text-muted-foreground", children: emptyLabel });
760
- if (!showLabel) {
761
- const triggerClasses = cn(
762
- "relative flex h-9 w-9 items-center justify-center rounded-md transition-colors",
763
- MOTION_CONTROL,
764
- focusRing,
765
- isActive || open ? "bg-[var(--accent-surface-strong)] text-[var(--accent-text)] font-medium ring-1 ring-inset ring-[var(--border-accent)]" : "text-muted-foreground hover:bg-[var(--accent-surface-soft)] hover:text-foreground"
766
- );
767
- const openFlyout = () => {
768
- if (closeTimer.current) {
769
- clearTimeout(closeTimer.current);
770
- closeTimer.current = null;
771
- }
772
- ensureLoaded();
773
- const el = triggerRef.current;
774
- if (el) {
775
- const r = el.getBoundingClientRect();
776
- const m = 8;
777
- const left = r.right + m;
778
- const spaceBelow = window.innerHeight - r.top - m;
779
- const spaceAbove = r.bottom - m;
780
- setCoords(
781
- spaceBelow >= spaceAbove ? { left, top: r.top, maxHeight: spaceBelow } : { left, bottom: window.innerHeight - r.bottom, maxHeight: spaceAbove }
782
- );
783
- }
784
- setOpen(true);
785
- };
786
- const scheduleClose = () => {
787
- if (closeTimer.current) clearTimeout(closeTimer.current);
788
- closeTimer.current = setTimeout(() => setOpen(false), 140);
789
- };
790
- const cancelClose = () => {
791
- if (closeTimer.current) {
792
- clearTimeout(closeTimer.current);
793
- closeTimer.current = null;
794
- }
795
- };
796
- return /* @__PURE__ */ jsxs2(
797
- "div",
798
- {
799
- ref: triggerRef,
800
- className: cn("relative", className),
801
- style,
802
- onMouseEnter: openFlyout,
803
- onMouseLeave: scheduleClose,
804
- onFocusCapture: openFlyout,
805
- onBlurCapture: (e) => {
806
- if (!e.currentTarget.contains(e.relatedTarget)) scheduleClose();
807
- },
808
- children: [
809
- href ? /* @__PURE__ */ jsx3(Link, { href, to: href, onClick: onNavigate, "aria-label": label, className: triggerClasses, children: /* @__PURE__ */ jsx3(Icon2, { className: "h-[17px] w-[17px] shrink-0" }) }) : /* @__PURE__ */ jsx3("button", { type: "button", "aria-label": label, "aria-haspopup": "menu", "aria-expanded": open, className: triggerClasses, children: /* @__PURE__ */ jsx3(Icon2, { className: "h-[17px] w-[17px] shrink-0" }) }),
810
- open && coords !== null && typeof document !== "undefined" && createPortal2(
811
- /* @__PURE__ */ jsxs2(
812
- "div",
813
- {
814
- role: "menu",
815
- style: {
816
- position: "fixed",
817
- left: coords.left,
818
- top: coords.top,
819
- bottom: coords.bottom,
820
- maxHeight: coords.maxHeight
821
- },
822
- onMouseEnter: cancelClose,
823
- onMouseLeave: scheduleClose,
824
- onClickCapture: (e) => {
825
- if (e.target.closest("a")) setOpen(false);
826
- },
827
- className: cn("agent-pop-in z-[70] flex w-60 flex-col overflow-hidden p-1.5", RAIL_FLOATING_SURFACE),
828
- children: [
829
- /* @__PURE__ */ jsx3("p", { className: "shrink-0 px-2 pb-1 pt-1 text-[10px] font-semibold uppercase tracking-wider text-muted-foreground/70 select-none", children: label }),
830
- /* @__PURE__ */ jsx3("div", { className: "flex min-h-0 flex-1 flex-col gap-0.5 overflow-y-auto", children: list })
831
- ]
832
- }
833
- ),
834
- document.body
835
- )
836
- ]
837
- }
838
- );
839
- }
840
- const toggle = () => {
841
- setOpen((v) => {
842
- const next = !v;
843
- if (next) {
844
- ensureLoaded();
845
- setEverOpened(true);
846
- }
847
- return next;
848
- });
849
- };
850
- return /* @__PURE__ */ jsxs2("div", { className: cn("w-full", className), style, children: [
851
- /* @__PURE__ */ jsxs2(
852
- "div",
853
- {
854
- className: cn(
855
- "group/exp relative flex h-9 w-full items-center rounded-md transition-colors",
856
- MOTION_CONTROL,
857
- isActive ? "bg-[var(--accent-surface-strong)] text-[var(--accent-text)] font-medium ring-1 ring-inset ring-[var(--border-accent)]" : "text-muted-foreground hover:bg-[var(--accent-surface-soft)] hover:text-foreground"
858
- ),
859
- children: [
860
- /* @__PURE__ */ jsxs2(
861
- "button",
862
- {
863
- type: "button",
864
- onClick: toggle,
865
- "aria-expanded": open,
866
- "aria-controls": discloseId,
867
- "aria-label": open ? `Collapse ${label}` : `Expand ${label}`,
868
- className: `relative flex h-9 w-9 shrink-0 items-center justify-center rounded-md ${focusRing}`,
869
- children: [
870
- /* @__PURE__ */ jsx3(
871
- Icon2,
872
- {
873
- className: cn("h-[17px] w-[17px] shrink-0 opacity-100 transition-opacity group-hover/exp:opacity-0", MOTION_CONTROL)
874
- }
875
- ),
876
- /* @__PURE__ */ jsx3(
877
- ChevronRightIcon,
878
- {
879
- className: cn(
880
- "absolute h-4 w-4 opacity-0 transition-all group-hover/exp:opacity-100",
881
- MOTION_CONTROL,
882
- open && "rotate-90"
883
- )
884
- }
885
- )
886
- ]
887
- }
888
- ),
889
- href ? /* @__PURE__ */ jsx3(
890
- Link,
891
- {
892
- href,
893
- to: href,
894
- onClick: onNavigate,
895
- className: "flex h-9 min-w-0 flex-1 items-center pr-2.5 text-[13.5px] font-medium",
896
- children: /* @__PURE__ */ jsx3("span", { className: "truncate", children: label })
897
- }
898
- ) : /* @__PURE__ */ jsx3(
899
- "button",
900
- {
901
- type: "button",
902
- onClick: toggle,
903
- className: "flex h-9 min-w-0 flex-1 items-center pr-2.5 text-left text-[13.5px] font-medium",
904
- children: /* @__PURE__ */ jsx3("span", { className: "truncate", children: label })
905
- }
906
- )
907
- ]
908
- }
909
- ),
910
- /* @__PURE__ */ jsx3("div", { ref: discloseRef, id: discloseId, className: "agent-disclose", "data-open": open ? "true" : "false", children: /* @__PURE__ */ jsx3("div", { children: /* @__PURE__ */ jsx3("div", { className: "flex flex-col gap-0.5 pl-3 pt-0.5", children: everOpened ? list : null }) }) })
911
- ] });
912
- }
913
- function RailModeButton({ mode, icon, label, badge, className, showLabel, style }) {
914
- const { panelOpen, mode: currentMode, switchMode } = useSidebar();
915
- return /* @__PURE__ */ jsx3(
916
- RailButton,
917
- {
918
- icon,
919
- label,
920
- isActive: mode === currentMode && panelOpen,
921
- badge,
922
- onClick: () => switchMode(mode),
923
- className,
924
- showLabel,
925
- style
926
- }
927
- );
928
- }
929
- function SidebarPanel({ children, className }) {
930
- const { panelOpen } = useSidebar();
931
- return /* @__PURE__ */ jsx3(
932
- "div",
933
- {
934
- className: cn(
935
- "transition-[opacity] h-full overflow-hidden border-l border-[var(--md3-outline-variant)] bg-surface-container-low",
936
- MOTION_SURFACE,
937
- panelOpen ? "w-[268px] opacity-100" : "w-0 opacity-0 pointer-events-none",
938
- className
939
- ),
940
- children: /* @__PURE__ */ jsx3("div", { className: "flex flex-col h-full w-[268px]", children })
941
- }
942
- );
943
- }
944
- function SidebarPanelHeader({ children, title, className }) {
945
- return /* @__PURE__ */ jsx3("div", { className: cn("flex h-14 items-center px-4 border-b border-[var(--md3-outline-variant)] shrink-0", className), children: children ?? /* @__PURE__ */ jsx3("h2", { className: "text-sm font-semibold text-foreground", children: title }) });
946
- }
947
- function SidebarPanelContent({ children, className }) {
948
- return /* @__PURE__ */ jsx3("div", { className: cn("flex-1 overflow-y-auto px-2 py-2", className), children });
949
- }
950
- function SidebarContent({ children, className }) {
951
- const { contentMargin } = useSidebar();
952
- return /* @__PURE__ */ jsx3(
953
- "main",
954
- {
955
- className: cn(
956
- "min-h-screen transition-[margin-left] lg:ml-[var(--sb-content-margin,0px)]",
957
- // Same tier as the rail it is keeping pace with — @see Sidebar.
958
- MOTION_TRAVEL,
959
- className
960
- ),
961
- style: { "--sb-content-margin": `${contentMargin}px` },
962
- children
963
- }
964
- );
965
- }
966
- var APPEARANCE_META = {
967
- light: { label: "Light", Icon: SunIcon },
968
- dark: { label: "Dark", Icon: MoonIcon },
969
- system: { label: "System", Icon: Monitor }
970
- };
971
- function AppearanceMenuSection({ appearance, divider = true }) {
972
- const modes = appearance.modes ?? ["light", "dark", "system"];
973
- const [systemDark, setSystemDark] = React3.useState(false);
974
- React3.useEffect(() => {
975
- if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
976
- const mq = window.matchMedia("(prefers-color-scheme: dark)");
977
- setSystemDark(mq.matches);
978
- const onChange = () => setSystemDark(mq.matches);
979
- mq.addEventListener("change", onChange);
980
- return () => mq.removeEventListener("change", onChange);
981
- }, []);
982
- const resolved = appearance.value === "system" ? systemDark ? "dark" : "light" : appearance.value;
983
- const TriggerIcon = resolved === "dark" ? MoonIcon : SunIcon;
984
- const caption = appearance.value === "system" ? `System (${resolved === "dark" ? "Dark" : "Light"})` : APPEARANCE_META[appearance.value].label;
985
- return /* @__PURE__ */ jsxs2(Fragment2, { children: [
986
- divider && /* @__PURE__ */ jsx3(DropdownMenuSeparator, {}),
987
- /* @__PURE__ */ jsxs2(DropdownMenuSub, { children: [
988
- /* @__PURE__ */ jsxs2(DropdownMenuSubTrigger, { children: [
989
- /* @__PURE__ */ jsx3("span", { "aria-hidden": "true", className: "mr-2 inline-flex shrink-0", children: /* @__PURE__ */ jsx3(TriggerIcon, { className: "h-4 w-4" }) }),
990
- /* @__PURE__ */ jsxs2("div", { className: "flex min-w-0 flex-col leading-tight", children: [
991
- /* @__PURE__ */ jsx3("span", { children: "Appearance" }),
992
- /* @__PURE__ */ jsx3("span", { className: "text-xs text-muted-foreground", children: caption })
993
- ] })
994
- ] }),
995
- /* @__PURE__ */ jsx3(DropdownMenuSubContent, { sideOffset: 6, className: "min-w-[12rem]", children: modes.map((m) => {
996
- const { label, Icon: Icon2 } = APPEARANCE_META[m];
997
- const active = appearance.value === m;
998
- return /* @__PURE__ */ jsxs2(
999
- DropdownMenuItem,
1000
- {
1001
- onSelect: (e) => {
1002
- e.preventDefault();
1003
- appearance.onChange(m);
1004
- },
1005
- className: "gap-3 py-2 text-[14px]",
1006
- children: [
1007
- /* @__PURE__ */ jsx3("span", { "aria-hidden": "true", className: "inline-flex shrink-0", children: /* @__PURE__ */ jsx3(Icon2, { className: "h-[18px] w-[18px]" }) }),
1008
- /* @__PURE__ */ jsx3("span", { className: "flex-1", children: label }),
1009
- active && /* @__PURE__ */ jsx3("span", { "aria-hidden": "true", className: "inline-flex shrink-0 text-[var(--accent-text)]", children: /* @__PURE__ */ jsx3(Check, { className: "h-[18px] w-[18px]" }) })
1010
- ]
1011
- },
1012
- m
1013
- );
1014
- }) })
1015
- ] })
1016
- ] });
1017
- }
1018
- function ProfileAvatar({
1019
- user,
1020
- isLoading = false,
1021
- onLogout,
1022
- onSettingsClick,
1023
- settingsHref = "/dashboard/settings",
1024
- children,
1025
- className,
1026
- showDetails = false,
1027
- appearance,
1028
- LinkComponent
1029
- }) {
1030
- const Link = LinkComponent ?? DefaultLink;
1031
- return /* @__PURE__ */ jsxs2(DropdownMenu, { children: [
1032
- /* @__PURE__ */ jsx3(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs2(
1033
- "button",
1034
- {
1035
- type: "button",
1036
- className: cn(
1037
- "flex items-center rounded-lg transition-colors hover:bg-[var(--accent-surface-soft)]",
1038
- MOTION_CONTROL,
1039
- focusRing,
1040
- showDetails ? "w-full gap-2.5 px-3 py-2 text-left" : "justify-center w-12 h-12",
1041
- className
1042
- ),
1043
- "aria-label": "User menu",
1044
- children: [
1045
- isLoading ? /* @__PURE__ */ jsx3(Skeleton, { className: "h-7 w-7 shrink-0 rounded-full" }) : /* @__PURE__ */ jsxs2(Avatar, { className: "h-7 w-7 shrink-0", children: [
1046
- user?.avatarUrl && /* @__PURE__ */ jsx3(AvatarImage, { src: user.avatarUrl, alt: "" }),
1047
- /* @__PURE__ */ jsx3(AvatarFallback, { className: "text-[10px] bg-violet-500/20 text-violet-300", children: getInitials(user?.name, user?.email) })
1048
- ] }),
1049
- showDetails && /* @__PURE__ */ jsx3("div", { className: "min-w-0 flex-1", children: isLoading ? /* @__PURE__ */ jsx3(Skeleton, { className: "h-3.5 w-24" }) : /* @__PURE__ */ jsx3("p", { className: "truncate text-sm font-medium text-foreground", children: user?.name ?? user?.email ?? "Not signed in" }) })
1050
- ]
1051
- }
1052
- ) }),
1053
- /* @__PURE__ */ jsxs2(DropdownMenuContent, { side: "top", align: "start", sideOffset: 8, className: "w-72", children: [
1054
- /* @__PURE__ */ jsx3(DropdownMenuLabel, { className: "p-0 font-normal", children: /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-3 px-2 py-3", children: [
1055
- /* @__PURE__ */ jsxs2(Avatar, { className: "h-12 w-12 shrink-0", children: [
1056
- user?.avatarUrl && /* @__PURE__ */ jsx3(AvatarImage, { src: user.avatarUrl, alt: "" }),
1057
- /* @__PURE__ */ jsx3(AvatarFallback, { className: "text-sm bg-[var(--surface-violet-bg)] text-[var(--surface-violet-text)]", children: getInitials(user?.name, user?.email) })
1058
- ] }),
1059
- /* @__PURE__ */ jsx3("div", { className: "min-w-0 flex-1", children: isLoading ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
1060
- /* @__PURE__ */ jsx3(Skeleton, { className: "h-4 w-24 mb-1" }),
1061
- /* @__PURE__ */ jsx3(Skeleton, { className: "h-3 w-32" })
1062
- ] }) : /* @__PURE__ */ jsxs2(Fragment2, { children: [
1063
- /* @__PURE__ */ jsx3("p", { className: "text-sm font-semibold truncate", children: user?.name ?? user?.email ?? "Not logged in" }),
1064
- user?.email && user?.name && /* @__PURE__ */ jsx3("p", { className: "text-xs text-muted-foreground truncate", children: user.email }),
1065
- user?.tier && /* @__PURE__ */ jsxs2("p", { className: "text-xs text-muted-foreground capitalize", children: [
1066
- user.tier,
1067
- " Plan"
1068
- ] })
1069
- ] }) })
1070
- ] }) }),
1071
- /* @__PURE__ */ jsx3(DropdownMenuSeparator, {}),
1072
- children,
1073
- appearance && /* @__PURE__ */ jsx3(AppearanceMenuSection, { appearance, divider: Boolean(children) }),
1074
- onSettingsClick ? /* @__PURE__ */ jsxs2(DropdownMenuItem, { onClick: onSettingsClick, children: [
1075
- /* @__PURE__ */ jsx3(SettingsIcon, { className: "mr-2 h-4 w-4", "aria-hidden": "true" }),
1076
- "Settings"
1077
- ] }) : /* @__PURE__ */ jsx3(DropdownMenuItem, { asChild: true, children: /* @__PURE__ */ jsxs2(Link, { href: settingsHref, to: settingsHref, className: "flex items-center", children: [
1078
- /* @__PURE__ */ jsx3(SettingsIcon, { className: "mr-2 h-4 w-4", "aria-hidden": "true" }),
1079
- "Settings"
1080
- ] }) }),
1081
- onLogout && /* @__PURE__ */ jsxs2(Fragment2, { children: [
1082
- /* @__PURE__ */ jsx3(DropdownMenuSeparator, {}),
1083
- /* @__PURE__ */ jsxs2(DropdownMenuItem, { className: "text-[var(--surface-danger-text)]", onClick: onLogout, children: [
1084
- /* @__PURE__ */ jsx3(LogOutIcon, { className: "mr-2 h-4 w-4", "aria-hidden": "true" }),
1085
- "Sign Out"
1086
- ] })
1087
- ] })
1088
- ] })
1089
- ] });
1090
- }
1091
-
1092
- // src/dashboard/credit-balance.tsx
1093
- import * as React4 from "react";
1094
- import { focusFieldWithin } from "@tangle-network/ui/utils";
1095
- import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
1096
- function CreditBalance({
1097
- amount,
1098
- description = "Credits are automatically deducted based on hourly Sandbox usage and Agent operations.",
1099
- onTopUp,
1100
- quickAmounts = [10, 25, 100],
1101
- className
1102
- }) {
1103
- const [topUpValue, setTopUpValue] = React4.useState("50.00");
1104
- const topUpId = React4.useId();
1105
- return /* @__PURE__ */ jsxs3(
1106
- "div",
1107
- {
1108
- className: cn(
1109
- "bg-surface-container p-5 rounded-xl flex flex-col justify-between border border-[var(--md3-outline-variant)]",
1110
- className
1111
- ),
1112
- children: [
1113
- /* @__PURE__ */ jsxs3("div", { children: [
1114
- /* @__PURE__ */ jsx4("h3", { className: "text-sm font-semibold text-muted-foreground uppercase tracking-widest mb-2", children: "Available Credits" }),
1115
- /* @__PURE__ */ jsxs3("div", { className: "text-4xl font-extrabold text-primary tracking-tighter mb-2", children: [
1116
- "$",
1117
- amount.toFixed(2)
1118
- ] }),
1119
- /* @__PURE__ */ jsx4("p", { className: "text-sm text-muted-foreground leading-relaxed", children: description })
1120
- ] }),
1121
- onTopUp && /* @__PURE__ */ jsxs3("div", { className: "space-y-2.5 mt-5", children: [
1122
- /* @__PURE__ */ jsx4("label", { htmlFor: topUpId, className: "block text-xs font-medium text-muted-foreground", children: "Top-up amount (USD)" }),
1123
- /* @__PURE__ */ jsxs3("div", { className: `bg-surface-container-low border p-1 rounded-lg flex items-center ${focusFieldWithin}`, children: [
1124
- /* @__PURE__ */ jsx4(
1125
- "input",
1126
- {
1127
- id: topUpId,
1128
- type: "text",
1129
- inputMode: "decimal",
1130
- value: `$${topUpValue}`,
1131
- onChange: (e) => {
1132
- const raw = e.target.value.replace(/[^0-9.]/g, "");
1133
- setTopUpValue(raw.match(/^(\d*\.?\d{0,2})/)?.[1] ?? "");
1134
- },
1135
- className: "bg-transparent border-none text-foreground font-mono text-lg min-w-0 flex-1 focus:ring-0 px-4 outline-none"
1136
- }
1137
- ),
1138
- /* @__PURE__ */ jsx4(
1139
- "button",
1140
- {
1141
- type: "button",
1142
- onClick: () => {
1143
- const parsed = Number.parseFloat(topUpValue);
1144
- if (!Number.isFinite(parsed) || parsed <= 0) return;
1145
- onTopUp(parsed);
1146
- },
1147
- className: "shrink-0 whitespace-nowrap bg-[var(--accent-surface-soft)] border border-[var(--md3-outline-variant)] text-[var(--accent-text)] px-5 py-3 rounded-md font-bold text-xs uppercase tracking-widest active:scale-95 transition-transform hover:bg-[var(--accent-surface-strong)]",
1148
- children: "Top Up"
1149
- }
1150
- )
1151
- ] }),
1152
- /* @__PURE__ */ jsx4("div", { className: "flex justify-between gap-2", children: quickAmounts.filter((qa) => Number.isFinite(qa) && qa > 0).map((qa) => /* @__PURE__ */ jsxs3(
1153
- "button",
1154
- {
1155
- type: "button",
1156
- onClick: () => {
1157
- const rounded = parseFloat(qa.toFixed(2));
1158
- setTopUpValue(qa.toFixed(2));
1159
- onTopUp(rounded);
1160
- },
1161
- className: "flex-1 py-2 text-center text-xs font-mono tabular-nums text-muted-foreground border border-[var(--md3-outline-variant)] rounded-md hover:bg-surface-container-high hover:text-foreground transition-colors uppercase",
1162
- children: [
1163
- "+$",
1164
- qa.toLocaleString("en-US")
1165
- ]
1166
- },
1167
- qa
1168
- )) })
1169
- ] })
1170
- ]
1171
- }
1172
- );
1173
- }
1174
-
1175
- // src/dashboard/out-of-credits.tsx
1176
- import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
1177
- var INSUFFICIENT_BALANCE_CODE = "insufficient_balance";
1178
- function parseInsufficientBalance(payload, opts = {}) {
1179
- if (!payload || typeof payload !== "object") return null;
1180
- const body = payload;
1181
- const code = typeof body.code === "string" ? body.code : void 0;
1182
- if (code !== INSUFFICIENT_BALANCE_CODE) return null;
1183
- const manageUrl = typeof body.manageUrl === "string" && body.manageUrl.length > 0 ? body.manageUrl : opts.defaultManageUrl;
1184
- if (!manageUrl) return null;
1185
- return {
1186
- manageUrl,
1187
- plan: typeof body.plan === "string" ? body.plan : void 0,
1188
- remainingBalanceUsd: typeof body.remainingBalanceUsd === "number" ? body.remainingBalanceUsd : void 0,
1189
- message: typeof body.message === "string" ? body.message : typeof body.error === "string" ? body.error : void 0
1190
- };
1191
- }
1192
- function OutOfCreditsModal({
1193
- balance,
1194
- onClose,
1195
- ctaLabel = "Add credits",
1196
- className
1197
- }) {
1198
- if (!balance) return null;
1199
- return /* @__PURE__ */ jsx5(
1200
- "div",
1201
- {
1202
- role: "dialog",
1203
- "aria-modal": "true",
1204
- "aria-label": "Out of credits",
1205
- className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4",
1206
- onClick: onClose,
1207
- children: /* @__PURE__ */ jsxs4(
1208
- "div",
1209
- {
1210
- className: cn(
1211
- "bg-surface-container-highest border border-[var(--md3-outline-variant)] rounded-xl p-6 max-w-md w-full shadow-[0_8px_30px_rgba(0,0,0,0.45)] ring-1 ring-[#ffffff14]",
1212
- className
1213
- ),
1214
- onClick: (e) => e.stopPropagation(),
1215
- children: [
1216
- /* @__PURE__ */ jsx5("h3", { className: "text-lg font-bold text-foreground mb-1", children: "You're out of credits" }),
1217
- /* @__PURE__ */ jsxs4("p", { className: "text-sm text-muted-foreground leading-relaxed mb-4", children: [
1218
- balance.message ?? "Add credits or start a subscription to keep going. Usage is billed to your Tangle account.",
1219
- typeof balance.remainingBalanceUsd === "number" && /* @__PURE__ */ jsxs4("span", { className: "block mt-1", children: [
1220
- "Balance: $",
1221
- balance.remainingBalanceUsd.toFixed(2),
1222
- balance.plan ? ` \xB7 ${balance.plan} plan` : ""
1223
- ] })
1224
- ] }),
1225
- /* @__PURE__ */ jsxs4("div", { className: "flex gap-2 justify-end", children: [
1226
- onClose && /* @__PURE__ */ jsx5(Button, { variant: "outline", onClick: onClose, children: "Not now" }),
1227
- /* @__PURE__ */ jsx5(Button, { asChild: true, children: /* @__PURE__ */ jsx5("a", { href: balance.manageUrl, target: "_blank", rel: "noopener noreferrer", children: ctaLabel }) })
1228
- ] })
1229
- ]
1230
- }
1231
- )
1232
- }
1233
- );
1234
- }
1235
-
1236
- // src/dashboard/cluster-status-bar.tsx
1237
- import * as React5 from "react";
1238
- import { Fragment as Fragment4, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
1239
- function ClusterStatusBar({ items, latency, className }) {
1240
- if (!items?.length) return null;
1241
- return /* @__PURE__ */ jsx6("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__ */ jsxs5("div", { className: "flex items-center gap-4 overflow-x-auto rounded-full border border-[var(--md3-outline-variant)] bg-surface-container px-5 py-2.5 shadow-sm", children: [
1242
- items.map((item, i) => /* @__PURE__ */ jsxs5(React5.Fragment, { children: [
1243
- i > 0 && /* @__PURE__ */ jsx6("span", { "aria-hidden": true, className: "h-5 w-px shrink-0 bg-border" }),
1244
- /* @__PURE__ */ jsxs5("div", { className: "flex shrink-0 items-center gap-2", children: [
1245
- item.icon ? /* @__PURE__ */ jsx6("span", { className: "flex shrink-0 items-center text-[var(--md3-primary)] [&_svg]:h-[18px] [&_svg]:w-[18px]", children: item.icon }) : null,
1246
- /* @__PURE__ */ jsxs5("div", { className: "flex flex-col leading-tight", children: [
1247
- /* @__PURE__ */ jsx6("span", { className: "text-[10px] font-bold uppercase tracking-wider text-[var(--md3-on-surface-variant)]", children: item.label }),
1248
- /* @__PURE__ */ jsx6("span", { className: cn("text-sm font-bold tabular-nums text-foreground", item.valueClass), children: item.value })
1249
- ] })
1250
- ] })
1251
- ] }, i)),
1252
- latency && /* @__PURE__ */ jsxs5(Fragment4, { children: [
1253
- /* @__PURE__ */ jsx6("span", { "aria-hidden": true, className: "h-5 w-px shrink-0 bg-border" }),
1254
- /* @__PURE__ */ jsxs5("div", { className: "flex shrink-0 items-center gap-2", children: [
1255
- /* @__PURE__ */ jsxs5("span", { className: "relative flex h-2.5 w-2.5 items-center justify-center", children: [
1256
- /* @__PURE__ */ jsx6("span", { className: "absolute inline-flex h-full w-full animate-ping rounded-full bg-green-400 opacity-75", "data-motion": "essential" }),
1257
- /* @__PURE__ */ jsx6("span", { className: "relative inline-flex h-2 w-2 rounded-full bg-green-500" })
1258
- ] }),
1259
- /* @__PURE__ */ jsx6("span", { className: "font-mono text-xs font-bold text-green-400", children: latency })
1260
- ] })
1261
- ] })
1262
- ] }) });
1263
- }
1264
-
1265
- // src/dashboard/resource-meter.tsx
1266
- import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
1267
- function getBarColor(percent) {
1268
- if (percent >= 90) return "bg-[var(--code-error)]";
1269
- if (percent >= 70) return "bg-[var(--surface-warning-text)]";
1270
- return "bg-primary";
1271
- }
1272
- function ResourceMeter({ label, value, max = 100, unit, valueLabel, icon, className }) {
1273
- const percent = max > 0 ? Math.max(0, Math.min(value / max * 100, 100)) : 0;
1274
- const barColor = getBarColor(percent);
1275
- const readout = valueLabel ?? (unit ? `${value}${unit}/${max}${unit}` : `${Math.round(percent)}%`);
1276
- return /* @__PURE__ */ jsxs6("div", { className: cn("flex items-center gap-3", className), children: [
1277
- /* @__PURE__ */ jsxs6("span", { className: "flex shrink-0 items-center gap-1.5 text-xs font-mono text-muted-foreground uppercase tracking-wide", children: [
1278
- icon ? /* @__PURE__ */ jsx7("span", { className: "flex shrink-0 items-center [&_svg]:h-3.5 [&_svg]:w-3.5", children: icon }) : null,
1279
- /* @__PURE__ */ jsx7("span", { children: label })
1280
- ] }),
1281
- /* @__PURE__ */ jsx7("div", { className: "h-1.5 min-w-0 flex-1 bg-muted rounded-full overflow-hidden", children: /* @__PURE__ */ jsx7(
1282
- "div",
1283
- {
1284
- className: cn("h-full rounded-full transition-all duration-500", barColor),
1285
- style: { width: `${percent}%` }
1286
- }
1287
- ) }),
1288
- /* @__PURE__ */ jsx7("span", { className: "shrink-0 text-xs font-mono tabular-nums text-muted-foreground", children: readout })
1289
- ] });
1290
- }
1291
-
1292
- // src/dashboard/resource-snapshot.tsx
1293
- import { Skeleton as Skeleton2 } from "@tangle-network/ui/primitives";
1294
- import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
1295
- function ResourceSnapshot({
1296
- title = "Resources",
1297
- action,
1298
- items,
1299
- loading = false,
1300
- error = null,
1301
- className
1302
- }) {
1303
- const rowCount = items.length > 0 ? items.length : 3;
1304
- return /* @__PURE__ */ jsxs7(
1305
- "div",
1306
- {
1307
- className: cn(
1308
- "flex flex-col rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container p-6",
1309
- className
1310
- ),
1311
- children: [
1312
- /* @__PURE__ */ jsxs7("div", { className: "mb-4 flex items-center justify-between gap-2", children: [
1313
- /* @__PURE__ */ jsx8("h3", { className: "font-display font-semibold text-foreground text-sm", children: title }),
1314
- action
1315
- ] }),
1316
- error ? /* @__PURE__ */ jsx8("p", { className: "text-[var(--surface-danger-text)] text-xs", children: error }) : loading ? /* @__PURE__ */ jsx8("div", { className: "space-y-3", children: Array.from({ length: rowCount }).map((_, i) => /* @__PURE__ */ jsx8(Skeleton2, { className: "h-4 w-full" }, i)) }) : /* @__PURE__ */ jsx8("div", { className: "space-y-3", children: items.map((item, idx) => /* @__PURE__ */ jsx8(
1317
- ResourceMeter,
1318
- {
1319
- label: item.label,
1320
- value: item.value,
1321
- max: item.max,
1322
- unit: item.unit,
1323
- valueLabel: item.valueLabel,
1324
- icon: item.icon
1325
- },
1326
- item.id ?? `${item.label}-${idx}`
1327
- )) })
1328
- ]
1329
- }
1330
- );
1331
- }
1332
-
1333
- // src/dashboard/activity-feed.tsx
1334
- import * as React6 from "react";
1335
- import { Skeleton as Skeleton3 } from "@tangle-network/ui/primitives";
1336
- import { timeAgo } from "@tangle-network/ui/utils";
1337
- import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
1338
- function ActivityFeed({
1339
- title = "Recent activity",
1340
- action,
1341
- items,
1342
- loading = false,
1343
- maxItems = 6,
1344
- emptyLabel = "No recent activity",
1345
- className
1346
- }) {
1347
- const sorted = React6.useMemo(
1348
- () => [...items].sort((a, b) => (b.timestamp ?? 0) - (a.timestamp ?? 0)).slice(0, maxItems),
1349
- [items, maxItems]
1350
- );
1351
- return /* @__PURE__ */ jsxs8(
1352
- "div",
1353
- {
1354
- className: cn(
1355
- "flex flex-col rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container p-6",
1356
- className
1357
- ),
1358
- children: [
1359
- /* @__PURE__ */ jsxs8("div", { className: "mb-4 flex items-center justify-between gap-2", children: [
1360
- /* @__PURE__ */ jsx9("h3", { className: "font-display font-semibold text-foreground text-sm", children: title }),
1361
- action
1362
- ] }),
1363
- loading ? /* @__PURE__ */ jsx9("div", { className: "space-y-3", children: Array.from({ length: 3 }).map((_, i) => /* @__PURE__ */ jsx9(Skeleton3, { className: "h-9 w-full" }, i)) }) : sorted.length === 0 ? /* @__PURE__ */ jsx9("p", { className: "text-muted-foreground text-sm", children: emptyLabel }) : /* @__PURE__ */ jsx9("ul", { className: "space-y-3", children: sorted.map((item) => /* @__PURE__ */ jsxs8("li", { className: "flex items-start gap-3", children: [
1364
- item.icon && /* @__PURE__ */ jsx9("span", { className: "mt-0.5 shrink-0 text-muted-foreground", children: item.icon }),
1365
- /* @__PURE__ */ jsxs8("div", { className: "min-w-0 flex-1", children: [
1366
- /* @__PURE__ */ jsx9("p", { className: "truncate font-medium text-foreground text-sm", children: item.title }),
1367
- item.detail && /* @__PURE__ */ jsx9("p", { className: "truncate text-muted-foreground text-xs", children: item.detail })
1368
- ] }),
1369
- item.timestamp != null && /* @__PURE__ */ jsx9("span", { className: "shrink-0 text-muted-foreground text-xs tabular-nums", children: timeAgo(item.timestamp) })
1370
- ] }, item.id)) })
1371
- ]
1372
- }
1373
- );
1374
- }
1375
-
1376
- // src/dashboard/sandbox-card.tsx
1377
- import {
1378
- DropdownMenu as DropdownMenu2,
1379
- DropdownMenuContent as DropdownMenuContent2,
1380
- DropdownMenuItem as DropdownMenuItem2,
1381
- DropdownMenuSeparator as DropdownMenuSeparator2,
1382
- DropdownMenuTrigger as DropdownMenuTrigger2
1383
- } from "@tangle-network/ui/primitives";
1384
- import {
1385
- MoreVertical,
1386
- PowerOff,
1387
- Power,
1388
- Copy,
1389
- Clock,
1390
- Activity,
1391
- BarChart2,
1392
- Trash2,
1393
- Network,
1394
- Play,
1395
- Plus,
1396
- Users,
1397
- Cpu,
1398
- MemoryStick,
1399
- HardDrive,
1400
- Box
1401
- } from "lucide-react";
1402
- import { focusRing as focusRing2 } from "@tangle-network/ui/utils";
1403
- import { Fragment as Fragment5, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
1404
- function canAdminSandbox(sandbox) {
1405
- if (!sandbox.team) return true;
1406
- return sandbox.team.role === "owner" || sandbox.team.role === "admin";
1407
- }
1408
- var STATUS_META = {
1409
- running: { label: "Running", color: "var(--status-running)" },
1410
- hibernating: { label: "Hibernating", color: "var(--status-warm)" },
1411
- provisioning: { label: "Provisioning", color: "var(--status-creating)" },
1412
- creating: { label: "Creating", color: "var(--status-creating)" },
1413
- stopped: { label: "Stopped", color: "var(--status-stopped)" },
1414
- failed: { label: "Failed", color: "var(--status-error)" },
1415
- archived: { label: "Archived", color: "var(--status-deleted)" }
1416
- };
1417
- function Spec({ icon, value, unit }) {
1418
- return /* @__PURE__ */ jsxs9("span", { className: "flex items-center gap-1.5", children: [
1419
- /* @__PURE__ */ jsx10("span", { className: "text-muted-foreground", "aria-hidden": "true", children: icon }),
1420
- /* @__PURE__ */ jsx10("span", { className: "font-mono text-xs tabular-nums text-foreground", children: value }),
1421
- /* @__PURE__ */ jsx10("span", { className: "text-[10px] text-muted-foreground", children: unit })
1422
- ] });
1423
- }
1424
- function SandboxCard({
1425
- sandbox,
1426
- onOpenIDE,
1427
- onOpenTerminal,
1428
- onWake,
1429
- onRestore,
1430
- onDelete,
1431
- onStop,
1432
- onResume,
1433
- onFork,
1434
- onKeepAlive,
1435
- onUsage,
1436
- onHealth,
1437
- className
1438
- }) {
1439
- const isRunning = sandbox.status === "running";
1440
- const isTransitioning = sandbox.status === "provisioning" || sandbox.status === "creating";
1441
- const isStopped = !isRunning && !isTransitioning;
1442
- const isHibernating = sandbox.status === "hibernating";
1443
- const resolveResumeHandler = (status2) => {
1444
- if (onResume) return onResume;
1445
- if (status2 === "hibernating") return onWake;
1446
- return void 0;
1447
- };
1448
- const resumeLabel = isHibernating ? "Wake Sandbox" : "Resume Sandbox";
1449
- const resumeHandler = isStopped ? resolveResumeHandler(sandbox.status) : void 0;
1450
- const status = STATUS_META[sandbox.status];
1451
- const isArchived = sandbox.status === "archived";
1452
- const cpuPercent = sandbox.cpuPercent ?? 0;
1453
- const ramUsed = sandbox.ramUsed ?? 0;
1454
- const ramTotal = sandbox.ramTotal ?? 0;
1455
- const diskUsed = sandbox.diskUsed;
1456
- const diskTotal = sandbox.diskTotal;
1457
- const specs = [
1458
- sandbox.vcpu != null && { key: "vcpu", icon: /* @__PURE__ */ jsx10(Cpu, { className: "h-3.5 w-3.5" }), value: sandbox.vcpu, unit: "vCPU" },
1459
- ramTotal > 0 && { key: "ram", icon: /* @__PURE__ */ jsx10(MemoryStick, { className: "h-3.5 w-3.5" }), value: ramTotal, unit: "GB RAM" },
1460
- diskTotal != null && { key: "disk", icon: /* @__PURE__ */ jsx10(HardDrive, { className: "h-3.5 w-3.5" }), value: diskTotal, unit: "GB SSD" }
1461
- ].filter(Boolean);
1462
- return /* @__PURE__ */ jsxs9("div", { className: cn(
1463
- "group relative flex flex-col justify-between overflow-hidden rounded-lg border bg-surface-container p-5 transition-colors",
1464
- isRunning ? "border-[var(--status-running)]/30" : "border-[var(--md3-outline-variant)]",
1465
- "hover:border-foreground/15",
1466
- className
1467
- ), children: [
1468
- /* @__PURE__ */ jsxs9("div", { className: "flex items-start justify-between", children: [
1469
- /* @__PURE__ */ jsxs9("div", { children: [
1470
- /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-2", children: [
1471
- /* @__PURE__ */ jsx10("h3", { className: "truncate text-sm font-bold text-foreground", children: sandbox.name }),
1472
- sandbox.team && /* @__PURE__ */ jsxs9(
1473
- "span",
1474
- {
1475
- 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)]",
1476
- title: `Shared with ${sandbox.team.name ?? "Team"} \xB7 ${sandbox.team.role}`,
1477
- children: [
1478
- /* @__PURE__ */ jsx10(Users, { className: "h-3 w-3", "aria-hidden": "true" }),
1479
- sandbox.team.name ?? "Team"
1480
- ]
1481
- }
1482
- )
1483
- ] }),
1484
- /* @__PURE__ */ jsxs9("p", { className: "mt-0.5 font-mono text-[10px] tracking-wider text-muted-foreground uppercase", children: [
1485
- sandbox.nodeId || "Unknown Node",
1486
- sandbox.team && /* @__PURE__ */ jsxs9("span", { className: "ml-2 normal-case tracking-normal", children: [
1487
- "\xB7 your role: ",
1488
- sandbox.team.role
1489
- ] })
1490
- ] })
1491
- ] }),
1492
- /* @__PURE__ */ jsxs9(DropdownMenu2, { children: [
1493
- /* @__PURE__ */ jsx10(DropdownMenuTrigger2, { asChild: true, children: /* @__PURE__ */ jsx10(
1494
- "button",
1495
- {
1496
- type: "button",
1497
- className: `rounded-md p-1.5 text-muted-foreground transition-colors hover:bg-surface-container-high hover:text-foreground ${focusRing2}`,
1498
- "aria-label": "Sandbox options",
1499
- children: /* @__PURE__ */ jsx10(MoreVertical, { className: "h-4 w-4" })
1500
- }
1501
- ) }),
1502
- /* @__PURE__ */ jsxs9(DropdownMenuContent2, { align: "end", className: "min-w-[180px]", children: [
1503
- isRunning && /* @__PURE__ */ jsxs9(Fragment5, { children: [
1504
- onStop && /* @__PURE__ */ jsxs9(DropdownMenuItem2, { onClick: () => onStop(sandbox.id), children: [
1505
- /* @__PURE__ */ jsx10(PowerOff, { className: "mr-2 h-4 w-4" }),
1506
- " Stop Sandbox"
1507
- ] }),
1508
- onKeepAlive && /* @__PURE__ */ jsxs9(DropdownMenuItem2, { onClick: () => onKeepAlive(sandbox.id), children: [
1509
- /* @__PURE__ */ jsx10(Clock, { className: "mr-2 h-4 w-4" }),
1510
- " Keep Alive"
1511
- ] }),
1512
- (onStop || onKeepAlive) && /* @__PURE__ */ jsx10(DropdownMenuSeparator2, {}),
1513
- onUsage && /* @__PURE__ */ jsxs9(DropdownMenuItem2, { onClick: () => onUsage(sandbox.id), children: [
1514
- /* @__PURE__ */ jsx10(BarChart2, { className: "mr-2 h-4 w-4" }),
1515
- " View Usage"
1516
- ] }),
1517
- onHealth && /* @__PURE__ */ jsxs9(DropdownMenuItem2, { onClick: () => onHealth(sandbox.id), children: [
1518
- /* @__PURE__ */ jsx10(Activity, { className: "mr-2 h-4 w-4" }),
1519
- " Health Check"
1520
- ] }),
1521
- (onUsage || onHealth) && /* @__PURE__ */ jsx10(DropdownMenuSeparator2, {}),
1522
- onFork && /* @__PURE__ */ jsxs9(Fragment5, { children: [
1523
- /* @__PURE__ */ jsxs9(DropdownMenuItem2, { onClick: () => onFork(sandbox.id), children: [
1524
- /* @__PURE__ */ jsx10(Copy, { className: "mr-2 h-4 w-4" }),
1525
- " Fork Sandbox"
1526
- ] }),
1527
- /* @__PURE__ */ jsx10(DropdownMenuSeparator2, {})
1528
- ] })
1529
- ] }),
1530
- isStopped && /* @__PURE__ */ jsxs9(Fragment5, { children: [
1531
- resumeHandler && /* @__PURE__ */ jsxs9(DropdownMenuItem2, { onClick: () => resumeHandler(sandbox.id), children: [
1532
- /* @__PURE__ */ jsx10(Power, { className: "mr-2 h-4 w-4" }),
1533
- " ",
1534
- resumeLabel
1535
- ] }),
1536
- onFork && /* @__PURE__ */ jsxs9(DropdownMenuItem2, { onClick: () => onFork(sandbox.id), children: [
1537
- /* @__PURE__ */ jsx10(Copy, { className: "mr-2 h-4 w-4" }),
1538
- " Fork Sandbox"
1539
- ] }),
1540
- (resumeHandler || onFork) && /* @__PURE__ */ jsx10(DropdownMenuSeparator2, {})
1541
- ] }),
1542
- onDelete && canAdminSandbox(sandbox) && /* @__PURE__ */ jsxs9(DropdownMenuItem2, { className: "text-destructive focus:bg-destructive/10 focus:text-destructive", onClick: () => onDelete(sandbox.id), children: [
1543
- /* @__PURE__ */ jsx10(Trash2, { className: "mr-2 h-4 w-4" }),
1544
- " Delete Sandbox"
1545
- ] })
1546
- ] })
1547
- ] })
1548
- ] }),
1549
- /* @__PURE__ */ jsxs9("div", { className: "my-4 space-y-3", children: [
1550
- /* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-between", children: [
1551
- /* @__PURE__ */ jsxs9("span", { className: "inline-flex items-center gap-1.5 rounded-full border border-[var(--md3-outline-variant)] bg-surface-container-high px-2 py-0.5 text-[11px] font-medium text-foreground", children: [
1552
- /* @__PURE__ */ jsxs9("span", { className: "relative flex h-1.5 w-1.5", children: [
1553
- isRunning && // The ping renders only while the sandbox IS running, so it is
1554
- // live state, not decoration — the same call SandboxTable makes
1555
- // for its running/provisioning dot.
1556
- /* @__PURE__ */ jsx10(
1557
- "span",
1558
- {
1559
- className: "absolute inline-flex h-full w-full animate-ping rounded-full opacity-75",
1560
- style: { backgroundColor: status.color },
1561
- "data-motion": "essential"
1562
- }
1563
- ),
1564
- /* @__PURE__ */ jsx10("span", { className: "relative inline-flex h-1.5 w-1.5 rounded-full", style: { backgroundColor: status.color } })
1565
- ] }),
1566
- status.label
1567
- ] }),
1568
- isRunning && sandbox.uptime && /* @__PURE__ */ jsxs9("span", { className: "font-mono text-[10px] tabular-nums text-muted-foreground", children: [
1569
- "up ",
1570
- sandbox.uptime
1571
- ] })
1572
- ] }),
1573
- isRunning ? /* @__PURE__ */ jsxs9("div", { className: "space-y-2 rounded-md border border-[var(--md3-outline-variant)] bg-surface-container-high p-3", children: [
1574
- /* @__PURE__ */ jsx10(ResourceMeter, { label: "CPU", value: cpuPercent, max: 100, icon: /* @__PURE__ */ jsx10(Cpu, { className: "h-3 w-3" }) }),
1575
- ramTotal > 0 && /* @__PURE__ */ jsx10(
1576
- ResourceMeter,
1577
- {
1578
- label: "MEM",
1579
- value: ramUsed,
1580
- max: ramTotal,
1581
- valueLabel: `${ramUsed} / ${ramTotal} GB`,
1582
- icon: /* @__PURE__ */ jsx10(MemoryStick, { className: "h-3 w-3" })
1583
- }
1584
- ),
1585
- diskUsed != null && diskTotal != null && diskTotal > 0 && /* @__PURE__ */ jsx10(
1586
- ResourceMeter,
1587
- {
1588
- label: "DISK",
1589
- value: diskUsed,
1590
- max: diskTotal,
1591
- valueLabel: `${diskUsed} / ${diskTotal} GB`,
1592
- icon: /* @__PURE__ */ jsx10(HardDrive, { className: "h-3 w-3" })
1593
- }
1594
- )
1595
- ] }) : isTransitioning ? /* @__PURE__ */ jsxs9("div", { className: "rounded-md border border-[var(--md3-outline-variant)] bg-surface-container-high p-3", children: [
1596
- /* @__PURE__ */ jsxs9("div", { className: "mb-1.5 flex justify-between text-[11px] font-medium text-muted-foreground", children: [
1597
- /* @__PURE__ */ jsx10("span", { children: sandbox.provisioningMessage || "Starting\u2026" }),
1598
- /* @__PURE__ */ jsxs9("span", { className: "font-mono tabular-nums", children: [
1599
- sandbox.provisioningPercent ?? 0,
1600
- "%"
1601
- ] })
1602
- ] }),
1603
- /* @__PURE__ */ jsx10("div", { className: "h-1 w-full overflow-hidden rounded-full bg-muted", children: /* @__PURE__ */ jsx10(
1604
- "div",
1605
- {
1606
- className: "h-full rounded-full bg-primary transition-all duration-500",
1607
- style: { width: `${sandbox.provisioningPercent || 5}%` }
1608
- }
1609
- ) })
1610
- ] }) : isArchived && sandbox.archivedAt ? /* @__PURE__ */ jsx10("p", { className: "font-mono text-[11px] text-muted-foreground", children: sandbox.archivedAt }) : null,
1611
- specs.length > 0 && /* @__PURE__ */ jsx10("div", { className: "flex items-center gap-4 rounded-md border border-[var(--md3-outline-variant)] bg-surface-container-high px-3 py-2", children: specs.map((s) => /* @__PURE__ */ jsx10(Spec, { icon: s.icon, value: s.value, unit: s.unit }, s.key)) }),
1612
- sandbox.customImage && /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-1.5 truncate font-mono text-[10px] text-muted-foreground", children: [
1613
- /* @__PURE__ */ jsx10(Box, { className: "h-3 w-3 shrink-0", "aria-hidden": "true" }),
1614
- /* @__PURE__ */ jsx10("span", { className: "truncate", title: sandbox.customImage, children: sandbox.customImage })
1615
- ] })
1616
- ] }),
1617
- /* @__PURE__ */ jsx10("div", { className: "border-t border-[var(--md3-outline-variant)] pt-3", children: isRunning ? /* @__PURE__ */ jsxs9(
1618
- "button",
1619
- {
1620
- type: "button",
1621
- onClick: () => onOpenIDE?.(sandbox.id),
1622
- 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]",
1623
- children: [
1624
- /* @__PURE__ */ jsx10(Network, { className: "h-4 w-4" }),
1625
- "Connect Session"
1626
- ]
1627
- }
1628
- ) : /* @__PURE__ */ jsxs9(
1629
- "button",
1630
- {
1631
- type: "button",
1632
- onClick: () => resumeHandler?.(sandbox.id),
1633
- disabled: isTransitioning || !resumeHandler,
1634
- className: cn(
1635
- "flex w-full items-center justify-center gap-2 rounded-md px-4 py-2 text-sm font-semibold transition-colors border",
1636
- isTransitioning || !resumeHandler ? "bg-muted text-muted-foreground cursor-not-allowed border-[var(--md3-outline-variant)]" : "bg-surface-container-high text-foreground hover:bg-surface-container-highest border-[var(--md3-outline-variant)] active:scale-[0.97]"
1637
- ),
1638
- children: [
1639
- /* @__PURE__ */ jsx10(Play, { className: "h-4 w-4" }),
1640
- isTransitioning ? "Starting..." : resumeLabel
1641
- ]
1642
- }
1643
- ) })
1644
- ] });
1645
- }
1646
- function NewSandboxCard({ onClick, className }) {
1647
- return /* @__PURE__ */ jsxs9(
1648
- "button",
1649
- {
1650
- type: "button",
1651
- onClick,
1652
- className: cn(
1653
- "border-2 border-dashed border-[var(--md3-outline-variant)] rounded-lg p-5 flex flex-col items-center justify-center text-center cursor-pointer hover:border-foreground/20 hover:bg-surface-container-high transition-colors w-full min-h-[160px]",
1654
- className
1655
- ),
1656
- children: [
1657
- /* @__PURE__ */ jsx10("div", { className: "flex h-12 w-12 items-center justify-center rounded-lg bg-surface-container-high text-muted-foreground", children: /* @__PURE__ */ jsx10(Plus, { className: "h-6 w-6" }) }),
1658
- /* @__PURE__ */ jsx10("span", { className: "mt-4 text-sm font-semibold text-foreground", children: "New Sandbox" }),
1659
- /* @__PURE__ */ jsx10("span", { className: "mt-1 text-xs text-muted-foreground", children: "Spin up an isolated sandbox" })
1660
- ]
1661
- }
1662
- );
1663
- }
1664
-
1665
- // src/dashboard/sandbox-table.tsx
1666
- import * as React7 from "react";
1667
- import {
1668
- Activity as Activity2,
1669
- BarChart2 as BarChart22,
1670
- ChevronLeft,
1671
- ChevronRight,
1672
- Clock as Clock2,
1673
- Code2,
1674
- Copy as Copy2,
1675
- ExternalLink,
1676
- Key,
1677
- MoreVertical as MoreVertical2,
1678
- Play as Play2,
1679
- PowerOff as PowerOff2,
1680
- RefreshCw,
1681
- Terminal,
1682
- Trash2 as Trash22,
1683
- User,
1684
- Users as Users2
1685
- } from "lucide-react";
1686
- import {
1687
- DropdownMenu as DropdownMenu3,
1688
- DropdownMenuContent as DropdownMenuContent3,
1689
- DropdownMenuItem as DropdownMenuItem3,
1690
- DropdownMenuSeparator as DropdownMenuSeparator3,
1691
- DropdownMenuTrigger as DropdownMenuTrigger3
1692
- } from "@tangle-network/ui/primitives";
1693
- import { focusRing as focusRing3 } from "@tangle-network/ui/utils";
1694
- import { Fragment as Fragment7, jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
1695
- var statusColors = {
1696
- running: { dot: "bg-[var(--code-success)] animate-pulse", text: "text-[var(--code-success)]", bar: "bg-[var(--code-success)]" },
1697
- hibernating: { dot: "bg-muted-foreground", text: "text-muted-foreground", bar: "bg-muted-foreground" },
1698
- provisioning: { dot: "bg-primary animate-pulse", text: "text-primary", bar: "bg-primary" },
1699
- creating: { dot: "bg-primary animate-pulse", text: "text-primary", bar: "bg-primary" },
1700
- stopped: { dot: "bg-muted-foreground", text: "text-foreground", bar: "bg-muted-foreground" },
1701
- failed: { dot: "bg-[var(--code-error)]", text: "text-[var(--code-error)]", bar: "bg-[var(--code-error)]" },
1702
- archived: { dot: "bg-border", text: "text-muted-foreground", bar: "bg-border" }
1703
- };
1704
- function isResumable(status) {
1705
- return status !== "running" && status !== "provisioning" && status !== "creating";
1706
- }
1707
- function MiniMeter({ label, percent, className }) {
1708
- return /* @__PURE__ */ jsxs10("div", { className: cn("space-y-1", className), children: [
1709
- /* @__PURE__ */ jsxs10("div", { className: "flex justify-between text-[10px] font-mono text-muted-foreground", children: [
1710
- /* @__PURE__ */ jsx11("span", { className: "font-bold", children: label }),
1711
- /* @__PURE__ */ jsxs10("span", { className: "text-primary", children: [
1712
- percent,
1713
- "%"
1714
- ] })
1715
- ] }),
1716
- /* @__PURE__ */ jsx11("div", { className: "h-1.5 w-full bg-muted rounded-full overflow-hidden", children: /* @__PURE__ */ jsx11("div", { className: "h-full bg-primary rounded-full", style: { width: `${percent}%` } }) })
1717
- ] });
1718
- }
1719
- function SandboxTable({
1720
- sandboxes,
1721
- page = 1,
1722
- pageSize = 10,
1723
- total,
1724
- onPageChange,
1725
- onOpenIDE,
1726
- onOpenTerminal,
1727
- onSSH,
1728
- onResume,
1729
- onWake,
1730
- onMore,
1731
- onDelete,
1732
- onStop,
1733
- onKeepAlive,
1734
- onUsage,
1735
- onHealth,
1736
- onFork,
1737
- className
1738
- }) {
1739
- const totalCount = total ?? sandboxes.length;
1740
- const totalPages = Math.ceil(totalCount / pageSize);
1741
- const hasTeamSandboxes = sandboxes.some((sb) => sb.team !== void 0);
1742
- const resolveResumeHandler = (status) => {
1743
- if (onResume) return onResume;
1744
- if (status === "hibernating") return onWake;
1745
- return void 0;
1746
- };
1747
- const resolveRowClick = (sb) => {
1748
- if (sb.status === "running") {
1749
- return onOpenIDE ? () => onOpenIDE(sb.id) : void 0;
1750
- }
1751
- if (!isResumable(sb.status)) return void 0;
1752
- const handler = resolveResumeHandler(sb.status);
1753
- return handler ? () => handler(sb.id) : void 0;
1754
- };
1755
- return /* @__PURE__ */ jsxs10("div", { className: cn("w-full", className), children: [
1756
- /* @__PURE__ */ jsx11("div", { className: "w-full bg-surface-container rounded-2xl overflow-hidden border border-[var(--md3-outline-variant)]", children: /* @__PURE__ */ jsx11("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxs10("table", { className: "w-full text-left border-collapse", children: [
1757
- /* @__PURE__ */ jsx11("thead", { children: /* @__PURE__ */ jsxs10("tr", { className: "bg-surface-container-high border-b border-[var(--md3-outline-variant)]", children: [
1758
- /* @__PURE__ */ jsx11("th", { className: "px-6 py-4 text-xs font-semibold text-muted-foreground uppercase tracking-wider", children: "Status" }),
1759
- /* @__PURE__ */ jsx11("th", { className: "px-6 py-4 text-xs font-semibold text-muted-foreground uppercase tracking-wider", children: "Sandbox Name" }),
1760
- hasTeamSandboxes && /* @__PURE__ */ jsx11("th", { className: "px-6 py-4 text-xs font-semibold text-muted-foreground uppercase tracking-wider", children: "Scope" }),
1761
- /* @__PURE__ */ jsx11("th", { className: "px-6 py-4 text-xs font-semibold text-muted-foreground uppercase tracking-wider", children: "Environment" }),
1762
- /* @__PURE__ */ jsx11("th", { className: "px-6 py-4 text-xs font-semibold text-muted-foreground uppercase tracking-wider", children: "Resources" }),
1763
- /* @__PURE__ */ jsx11("th", { className: "px-6 py-4 text-xs font-semibold text-muted-foreground uppercase tracking-wider text-right", children: "Actions" })
1764
- ] }) }),
1765
- /* @__PURE__ */ jsx11("tbody", { className: "divide-y divide-border", children: sandboxes.map((sb) => {
1766
- const sc = statusColors[sb.status] ?? statusColors.stopped;
1767
- const isActive = sb.status === "running";
1768
- const isHibernating = sb.status === "hibernating";
1769
- const isProvisioning = sb.status === "provisioning";
1770
- const resumeHandler = isResumable(sb.status) ? resolveResumeHandler(sb.status) : void 0;
1771
- const onRowClick = resolveRowClick(sb);
1772
- const resumeLabel = isHibernating ? "Wake Up" : "Resume";
1773
- const stopRowClick = (e) => e.stopPropagation();
1774
- return (
1775
- // onClick is a sighted-user convenience only. We
1776
- // deliberately do NOT add role="button" / tabIndex /
1777
- // an onKeyDown handler to the row — overriding a
1778
- // <tr>'s implicit row role with "button" collapses
1779
- // the per-cell announcements (Status, Environment,
1780
- // Resources…) that screen-reader users navigate
1781
- // through. Keyboard and assistive-tech users reach
1782
- // the same actions through the real <button>
1783
- // elements inside the actions cell (Resume, Open
1784
- // IDE, Delete, …), which keep their native
1785
- // semantics. Mouse users get the click-anywhere
1786
- // affordance; nobody loses access.
1787
- /* @__PURE__ */ jsxs10(
1788
- "tr",
1789
- {
1790
- className: cn(
1791
- "group relative transition-colors",
1792
- onRowClick ? "cursor-pointer hover:bg-surface-container-high" : "hover:bg-surface-container-high"
1793
- ),
1794
- onClick: onRowClick,
1795
- children: [
1796
- /* @__PURE__ */ jsx11("td", { className: "px-6 py-5 whitespace-nowrap", children: /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-2", children: [
1797
- /* @__PURE__ */ jsx11(
1798
- "span",
1799
- {
1800
- className: cn("flex h-2.5 w-2.5 rounded-full", sc.dot),
1801
- ...sc.dot.includes("animate-") ? { "data-motion": "essential" } : {}
1802
- }
1803
- ),
1804
- /* @__PURE__ */ jsx11("span", { className: cn("text-xs font-bold uppercase tracking-wide", sc.text), children: sb.status.charAt(0).toUpperCase() + sb.status.slice(1) })
1805
- ] }) }),
1806
- /* @__PURE__ */ jsx11("td", { className: "px-6 py-5", children: /* @__PURE__ */ jsxs10("div", { className: "flex flex-col", children: [
1807
- /* @__PURE__ */ jsx11("span", { className: "text-sm font-bold text-foreground group-hover:text-primary transition-colors", children: sb.name }),
1808
- sb.nodeId && /* @__PURE__ */ jsx11("span", { className: "text-[10px] font-mono text-muted-foreground", children: sb.nodeId })
1809
- ] }) }),
1810
- hasTeamSandboxes && /* @__PURE__ */ jsx11("td", { className: "px-6 py-5", children: sb.team ? /* @__PURE__ */ jsxs10(
1811
- "div",
1812
- {
1813
- 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)]",
1814
- title: `Shared with ${sb.team.name ?? "Team"} \xB7 ${sb.team.role}`,
1815
- children: [
1816
- /* @__PURE__ */ jsx11(Users2, { className: "h-3 w-3", "aria-hidden": "true" }),
1817
- /* @__PURE__ */ jsx11("span", { children: sb.team.name ?? "Team" }),
1818
- /* @__PURE__ */ jsxs10("span", { className: "font-normal text-muted-foreground", children: [
1819
- "\xB7 ",
1820
- sb.team.role
1821
- ] })
1822
- ]
1823
- }
1824
- ) : /* @__PURE__ */ jsxs10(
1825
- "div",
1826
- {
1827
- className: "inline-flex items-center gap-1.5 rounded-full bg-surface-container-high px-2.5 py-1 text-[11px] font-medium text-muted-foreground",
1828
- title: "Personal sandbox",
1829
- children: [
1830
- /* @__PURE__ */ jsx11(User, { className: "h-3 w-3", "aria-hidden": "true" }),
1831
- "Personal"
1832
- ]
1833
- }
1834
- ) }),
1835
- /* @__PURE__ */ jsx11("td", { className: "px-6 py-5", children: /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-3", children: [
1836
- sb.imageIcon && /* @__PURE__ */ jsx11("div", { className: "w-8 h-8 rounded-lg bg-surface-container-high flex items-center justify-center", children: sb.imageIcon }),
1837
- sb.image && /* @__PURE__ */ jsx11("span", { className: "text-xs font-bold text-foreground", children: sb.image })
1838
- ] }) }),
1839
- /* @__PURE__ */ jsx11("td", { className: "px-6 py-5", children: isActive ? /* @__PURE__ */ jsxs10("div", { className: "space-y-3 w-48", children: [
1840
- /* @__PURE__ */ jsx11(MiniMeter, { label: "CPU", percent: sb.cpuPercent ?? 0 }),
1841
- /* @__PURE__ */ jsx11(MiniMeter, { label: "RAM", percent: sb.ramTotal ? Math.round((sb.ramUsed ?? 0) / sb.ramTotal * 100) : 0 })
1842
- ] }) : isProvisioning ? /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-2 text-primary italic text-[10px] font-bold", children: [
1843
- /* @__PURE__ */ jsx11(RefreshCw, { className: "h-3.5 w-3.5 animate-spin", "data-motion": "essential" }),
1844
- sb.provisioningMessage ?? "Allocating nodes..."
1845
- ] }) : isHibernating ? /* @__PURE__ */ jsxs10("div", { className: "space-y-3 w-48 opacity-30", children: [
1846
- /* @__PURE__ */ jsx11(MiniMeter, { label: "CPU", percent: 0 }),
1847
- /* @__PURE__ */ jsx11(MiniMeter, { label: "RAM", percent: 0 })
1848
- ] }) : null }),
1849
- /* @__PURE__ */ jsx11("td", { className: "px-6 py-5 text-right", children: /* @__PURE__ */ jsxs10("div", { className: "flex items-center justify-end gap-1", children: [
1850
- isActive && /* @__PURE__ */ jsxs10(Fragment7, { children: [
1851
- /* @__PURE__ */ jsx11("button", { type: "button", onClick: (e) => {
1852
- stopRowClick(e);
1853
- onOpenIDE?.(sb.id);
1854
- }, className: "p-2 rounded-lg hover:bg-surface-container-high text-muted-foreground hover:text-foreground transition-all active:scale-90", title: "Open IDE", children: /* @__PURE__ */ jsx11(Code2, { className: "h-4 w-4" }) }),
1855
- /* @__PURE__ */ jsx11("button", { type: "button", onClick: (e) => {
1856
- stopRowClick(e);
1857
- onOpenTerminal?.(sb.id);
1858
- }, className: "p-2 rounded-lg hover:bg-surface-container-high text-muted-foreground hover:text-foreground transition-all active:scale-90", title: "Terminal", children: /* @__PURE__ */ jsx11(Terminal, { className: "h-4 w-4" }) }),
1859
- /* @__PURE__ */ jsx11("button", { type: "button", onClick: (e) => {
1860
- stopRowClick(e);
1861
- onSSH?.(sb.id);
1862
- }, className: "p-2 rounded-lg hover:bg-surface-container-high text-muted-foreground hover:text-foreground transition-all active:scale-90", title: "SSH", children: /* @__PURE__ */ jsx11(Key, { className: "h-4 w-4" }) })
1863
- ] }),
1864
- resumeHandler && /* @__PURE__ */ jsxs10(
1865
- "button",
1866
- {
1867
- type: "button",
1868
- onClick: (e) => {
1869
- stopRowClick(e);
1870
- resumeHandler(sb.id);
1871
- },
1872
- className: "inline-flex items-center gap-1.5 px-3 py-1.5 rounded-lg border border-[var(--md3-outline-variant)] text-primary text-[10px] font-bold uppercase tracking-wider hover:bg-[var(--accent-surface-soft)] active:scale-95 transition-all",
1873
- title: resumeLabel,
1874
- children: [
1875
- /* @__PURE__ */ jsx11(Play2, { className: "h-3 w-3" }),
1876
- resumeLabel
1877
- ]
1878
- }
1879
- ),
1880
- (() => {
1881
- const runItem = (handler) => (e) => {
1882
- e.stopPropagation();
1883
- handler(sb.id);
1884
- };
1885
- const overflowSections = [];
1886
- if (isActive) {
1887
- const lifecycle = [];
1888
- if (onStop) lifecycle.push(
1889
- /* @__PURE__ */ jsxs10(DropdownMenuItem3, { onClick: runItem(onStop), children: [
1890
- /* @__PURE__ */ jsx11(PowerOff2, { className: "mr-2 h-4 w-4" }),
1891
- " Stop Sandbox"
1892
- ] }, "stop")
1893
- );
1894
- if (onKeepAlive) lifecycle.push(
1895
- /* @__PURE__ */ jsxs10(DropdownMenuItem3, { onClick: runItem(onKeepAlive), children: [
1896
- /* @__PURE__ */ jsx11(Clock2, { className: "mr-2 h-4 w-4" }),
1897
- " Keep Alive"
1898
- ] }, "keep-alive")
1899
- );
1900
- if (lifecycle.length) overflowSections.push(lifecycle);
1901
- const observability = [];
1902
- if (onUsage) observability.push(
1903
- /* @__PURE__ */ jsxs10(DropdownMenuItem3, { onClick: runItem(onUsage), children: [
1904
- /* @__PURE__ */ jsx11(BarChart22, { className: "mr-2 h-4 w-4" }),
1905
- " View Usage"
1906
- ] }, "usage")
1907
- );
1908
- if (onHealth) observability.push(
1909
- /* @__PURE__ */ jsxs10(DropdownMenuItem3, { onClick: runItem(onHealth), children: [
1910
- /* @__PURE__ */ jsx11(Activity2, { className: "mr-2 h-4 w-4" }),
1911
- " Health Check"
1912
- ] }, "health")
1913
- );
1914
- if (observability.length) overflowSections.push(observability);
1915
- if (onFork) overflowSections.push([
1916
- /* @__PURE__ */ jsxs10(DropdownMenuItem3, { onClick: runItem(onFork), children: [
1917
- /* @__PURE__ */ jsx11(Copy2, { className: "mr-2 h-4 w-4" }),
1918
- " Fork Sandbox"
1919
- ] }, "fork")
1920
- ]);
1921
- } else if (isResumable(sb.status)) {
1922
- if (onFork) overflowSections.push([
1923
- /* @__PURE__ */ jsxs10(DropdownMenuItem3, { onClick: runItem(onFork), children: [
1924
- /* @__PURE__ */ jsx11(Copy2, { className: "mr-2 h-4 w-4" }),
1925
- " Fork Sandbox"
1926
- ] }, "fork")
1927
- ]);
1928
- }
1929
- if (onMore) overflowSections.push([
1930
- /* @__PURE__ */ jsxs10(DropdownMenuItem3, { onClick: runItem(onMore), children: [
1931
- /* @__PURE__ */ jsx11(ExternalLink, { className: "mr-2 h-4 w-4" }),
1932
- " View Details"
1933
- ] }, "view-details")
1934
- ]);
1935
- if (overflowSections.length === 0) return null;
1936
- return /* @__PURE__ */ jsxs10(DropdownMenu3, { children: [
1937
- /* @__PURE__ */ jsx11(DropdownMenuTrigger3, { asChild: true, children: /* @__PURE__ */ jsx11(
1938
- "button",
1939
- {
1940
- type: "button",
1941
- onClick: stopRowClick,
1942
- className: `p-2 rounded-lg hover:bg-surface-container-high text-muted-foreground hover:text-foreground transition-all active:scale-90 ${focusRing3}`,
1943
- "aria-label": `More actions for ${sb.name}`,
1944
- title: "More actions",
1945
- children: /* @__PURE__ */ jsx11(MoreVertical2, { className: "h-4 w-4" })
1946
- }
1947
- ) }),
1948
- /* @__PURE__ */ jsx11(DropdownMenuContent3, { align: "end", className: "min-w-[180px]", children: overflowSections.map((section, sectionIdx) => /* @__PURE__ */ jsxs10(React7.Fragment, { children: [
1949
- sectionIdx > 0 && /* @__PURE__ */ jsx11(DropdownMenuSeparator3, {}),
1950
- section
1951
- ] }, sectionIdx)) })
1952
- ] });
1953
- })(),
1954
- onDelete && canAdminSandbox(sb) && /* @__PURE__ */ jsx11("button", { type: "button", "aria-label": `Delete ${sb.name}`, onClick: (e) => {
1955
- stopRowClick(e);
1956
- onDelete(sb.id);
1957
- }, 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 ${focusRing3}`, title: "Delete", children: /* @__PURE__ */ jsx11(Trash22, { className: "h-4 w-4" }) })
1958
- ] }) })
1959
- ]
1960
- },
1961
- sb.id
1962
- )
1963
- );
1964
- }) })
1965
- ] }) }) }),
1966
- totalPages > 1 && /* @__PURE__ */ jsxs10("div", { className: "mt-6 flex flex-col md:flex-row justify-between items-center text-muted-foreground text-xs font-medium gap-4", children: [
1967
- /* @__PURE__ */ jsxs10("p", { children: [
1968
- "Showing ",
1969
- sandboxes.length,
1970
- " of ",
1971
- totalCount,
1972
- " active sandboxes"
1973
- ] }),
1974
- /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-2", children: [
1975
- /* @__PURE__ */ jsx11("button", { type: "button", onClick: () => onPageChange?.(page - 1), disabled: page <= 1, className: "p-2 rounded-lg border border-[var(--md3-outline-variant)] hover:bg-surface-container-high transition-colors disabled:opacity-30", children: /* @__PURE__ */ jsx11(ChevronLeft, { className: "h-4 w-4" }) }),
1976
- Array.from({ length: Math.min(totalPages, 5) }, (_, i) => i + 1).map((p) => /* @__PURE__ */ jsx11(
1977
- "button",
1978
- {
1979
- type: "button",
1980
- onClick: () => onPageChange?.(p),
1981
- className: cn(
1982
- "px-3 py-1 rounded-lg transition-colors",
1983
- p === page ? "bg-[var(--accent-surface-soft)] text-primary border border-[var(--md3-outline)]" : "hover:bg-surface-container-high"
1984
- ),
1985
- children: p
1986
- },
1987
- p
1988
- )),
1989
- /* @__PURE__ */ jsx11("button", { type: "button", onClick: () => onPageChange?.(page + 1), disabled: page >= totalPages, className: "p-2 rounded-lg border border-[var(--md3-outline-variant)] hover:bg-surface-container-high transition-colors disabled:opacity-30", children: /* @__PURE__ */ jsx11(ChevronRight, { className: "h-4 w-4" }) })
1990
- ] })
1991
- ] })
1992
- ] });
1993
- }
1994
-
1995
- // src/dashboard/invoice-table.tsx
1996
- import { Download, FileText } from "lucide-react";
1997
- import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
1998
- var statusStyle = {
1999
- paid: "bg-[var(--accent-surface-soft)] text-primary border border-[var(--md3-outline-variant)]",
2000
- pending: "bg-[var(--surface-warning-bg)] text-[var(--surface-warning-text)] border border-[var(--surface-warning-border)]",
2001
- failed: "bg-[var(--surface-danger-bg)] text-[var(--surface-danger-text)] border border-[var(--surface-danger-border)]"
2002
- };
2003
- function InvoiceTable({ invoices, onExportAll, onLoadMore, onViewInvoice, hasMore, className }) {
2004
- return /* @__PURE__ */ jsxs11("section", { className, children: [
2005
- /* @__PURE__ */ jsxs11("div", { className: "flex justify-between items-center mb-6 px-2", children: [
2006
- /* @__PURE__ */ jsx12("h2", { className: "text-2xl font-bold text-foreground tracking-tight", children: "Invoice History" }),
2007
- onExportAll && /* @__PURE__ */ jsxs11("button", { type: "button", onClick: onExportAll, className: "text-[10px] font-mono text-primary uppercase tracking-widest flex items-center gap-2 hover:underline", children: [
2008
- /* @__PURE__ */ jsx12(Download, { className: "h-3.5 w-3.5" }),
2009
- "Export All"
2010
- ] })
2011
- ] }),
2012
- /* @__PURE__ */ jsx12("div", { className: "bg-surface-container rounded-xl overflow-hidden border border-[var(--md3-outline-variant)]", children: /* @__PURE__ */ jsxs11("table", { className: "w-full text-left border-collapse", children: [
2013
- /* @__PURE__ */ jsx12("thead", { children: /* @__PURE__ */ jsxs11("tr", { className: "bg-surface-container-high border-b border-[var(--md3-outline-variant)]", children: [
2014
- /* @__PURE__ */ jsx12("th", { className: "px-6 py-4 font-mono text-[10px] text-muted-foreground uppercase tracking-widest", children: "Invoice ID" }),
2015
- /* @__PURE__ */ jsx12("th", { className: "px-6 py-4 font-mono text-[10px] text-muted-foreground uppercase tracking-widest", children: "Date" }),
2016
- /* @__PURE__ */ jsx12("th", { className: "px-6 py-4 font-mono text-[10px] text-muted-foreground uppercase tracking-widest", children: "Amount" }),
2017
- /* @__PURE__ */ jsx12("th", { className: "px-6 py-4 font-mono text-[10px] text-muted-foreground uppercase tracking-widest", children: "Status" }),
2018
- /* @__PURE__ */ jsx12("th", { className: "px-6 py-4 font-mono text-[10px] text-muted-foreground uppercase tracking-widest text-right", children: "Action" })
2019
- ] }) }),
2020
- /* @__PURE__ */ jsx12("tbody", { className: "divide-y divide-border", children: invoices.map((inv) => /* @__PURE__ */ jsxs11("tr", { className: "hover:bg-surface-container-high transition-colors", children: [
2021
- /* @__PURE__ */ jsx12("td", { className: "px-6 py-5 font-mono text-xs text-foreground", children: inv.id }),
2022
- /* @__PURE__ */ jsx12("td", { className: "px-6 py-5 text-sm text-foreground", children: inv.date }),
2023
- /* @__PURE__ */ jsxs11("td", { className: "px-6 py-5 text-sm font-bold text-foreground", children: [
2024
- "$",
2025
- inv.amount.toFixed(2)
2026
- ] }),
2027
- /* @__PURE__ */ jsx12("td", { className: "px-6 py-5", children: /* @__PURE__ */ jsx12("span", { className: cn("px-2 py-1 text-[10px] font-mono rounded uppercase", statusStyle[inv.status] ?? statusStyle.paid), children: inv.status }) }),
2028
- /* @__PURE__ */ jsx12("td", { className: "px-6 py-5 text-right", children: /* @__PURE__ */ jsx12("button", { type: "button", onClick: () => onViewInvoice?.(inv.id), className: "text-muted-foreground hover:text-foreground transition-colors", children: /* @__PURE__ */ jsx12(FileText, { className: "h-4 w-4" }) }) })
2029
- ] }, inv.id)) })
2030
- ] }) }),
2031
- hasMore && onLoadMore && /* @__PURE__ */ jsx12("div", { className: "mt-6 flex justify-center", children: /* @__PURE__ */ jsx12("button", { type: "button", onClick: onLoadMore, className: "px-8 py-2 text-[10px] font-mono text-muted-foreground border border-[var(--md3-outline-variant)] rounded-full hover:bg-surface-container-high transition-colors uppercase tracking-widest", children: "Load More History" }) })
2032
- ] });
2033
- }
2034
-
2035
- // src/dashboard/plan-cards.tsx
2036
- import { Check as Check2 } from "lucide-react";
2037
- import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
2038
- function PlanCards({ plans, className }) {
2039
- return /* @__PURE__ */ jsxs12("section", { className, children: [
2040
- /* @__PURE__ */ jsx13("h2", { className: "text-2xl font-bold text-foreground tracking-tight mb-5 px-2", children: "Subscription Plans" }),
2041
- /* @__PURE__ */ jsx13("div", { className: "grid grid-cols-1 md:grid-cols-3 gap-6", children: plans.map((plan) => /* @__PURE__ */ jsxs12(
2042
- "div",
2043
- {
2044
- className: cn(
2045
- "p-5 rounded-xl transition-all relative overflow-hidden border",
2046
- plan.popular ? "bg-surface-container-high border-[var(--md3-outline-variant)]" : "bg-surface-container border-[var(--md3-outline-variant)] hover:bg-surface-container-high hover:border-primary/20"
2047
- ),
2048
- children: [
2049
- plan.popular && /* @__PURE__ */ jsx13("div", { className: "absolute top-0 right-0 bg-[var(--accent-surface-soft)] border-l border-b border-[var(--md3-outline-variant)] px-4 py-1 text-[10px] font-bold text-[var(--accent-text)] uppercase tracking-widest rounded-bl-lg", children: "Popular" }),
2050
- /* @__PURE__ */ jsxs12("div", { className: "mb-4", children: [
2051
- /* @__PURE__ */ jsx13("div", { className: cn("text-xs font-mono uppercase tracking-widest mb-2", plan.popular ? "text-primary" : "text-muted-foreground"), children: plan.name }),
2052
- /* @__PURE__ */ jsxs12("div", { className: "text-3xl font-bold text-foreground", children: [
2053
- "$",
2054
- plan.price,
2055
- /* @__PURE__ */ jsxs12("span", { className: "text-sm font-normal text-muted-foreground tracking-normal", children: [
2056
- "/",
2057
- plan.period ?? "mo"
2058
- ] })
2059
- ] })
2060
- ] }),
2061
- /* @__PURE__ */ jsx13("ul", { className: "space-y-2 mb-5 text-sm text-muted-foreground", children: plan.features.map((f, i) => /* @__PURE__ */ jsxs12("li", { className: "flex items-center gap-2", children: [
2062
- /* @__PURE__ */ jsx13(Check2, { className: "h-3.5 w-3.5 text-primary shrink-0" }),
2063
- f.text
2064
- ] }, i)) }),
2065
- /* @__PURE__ */ jsx13(
2066
- "button",
2067
- {
2068
- type: "button",
2069
- onClick: () => plan.onSelect?.(plan.id),
2070
- className: cn(
2071
- "w-full py-3 rounded-lg text-xs font-bold uppercase tracking-widest transition-all border",
2072
- plan.current ? "border-[var(--md3-outline-variant)] text-muted-foreground hover:border-primary/20 hover:text-foreground" : plan.popular ? "bg-[var(--accent-surface-soft)] border-[var(--md3-outline-variant)] text-[var(--accent-text)] hover:bg-[var(--accent-surface-strong)] active:scale-95 transition-transform" : "border-[var(--md3-outline-variant)] text-foreground hover:border-primary/20 hover:text-primary"
2073
- ),
2074
- children: plan.ctaLabel ?? (plan.current ? "Current Plan" : "Upgrade Now")
2075
- }
2076
- )
2077
- ]
2078
- },
2079
- plan.id
2080
- )) })
2081
- ] });
2082
- }
2083
-
2084
- // src/dashboard/backend-selector.tsx
2085
- import { ChevronDown } from "lucide-react";
2086
- import * as Select from "@radix-ui/react-select";
2087
- import { focusRing as focusRing4 } from "@tangle-network/ui/utils";
2088
- import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
2089
- function BackendSelector({
2090
- backends,
2091
- selected,
2092
- onChange,
2093
- variant = "pill",
2094
- label = "Model",
2095
- placeholder = "Select a model",
2096
- className,
2097
- triggerClassName
2098
- }) {
2099
- const current = backends.find((b) => b.type === selected);
2100
- const triggerText = current?.label ?? placeholder;
2101
- const ariaLabel = label ? `${label}: ${triggerText}` : triggerText;
2102
- const trigger = variant === "pill" ? /* @__PURE__ */ jsxs13(
2103
- Select.Trigger,
2104
- {
2105
- "aria-label": ariaLabel,
2106
- className: cn(
2107
- "inline-flex h-8 items-center gap-1.5 rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container",
2108
- "px-2.5 text-xs font-medium text-foreground shadow-sm",
2109
- "transition-colors duration-[var(--transition-fast)]",
2110
- "hover:border-[var(--md3-outline-variant)] hover:bg-surface-container-high",
2111
- focusRing4,
2112
- "data-[state=open]:border-[var(--md3-outline)] data-[state=open]:bg-surface-container-high",
2113
- "disabled:opacity-50 disabled:cursor-not-allowed",
2114
- triggerClassName
2115
- ),
2116
- children: [
2117
- current?.icon,
2118
- /* @__PURE__ */ jsx14("span", { className: cn("truncate max-w-[140px]", current ? void 0 : "text-muted-foreground"), children: triggerText }),
2119
- current?.description && /* @__PURE__ */ jsx14("span", { className: "sr-only", children: current.description }),
2120
- /* @__PURE__ */ jsx14(Select.Icon, { asChild: true, children: /* @__PURE__ */ jsx14(ChevronDown, { className: "h-3 w-3 shrink-0 text-muted-foreground transition-transform duration-[var(--transition-fast)] data-[state=open]:rotate-180" }) })
2121
- ]
2122
- }
2123
- ) : /* @__PURE__ */ jsxs13(
2124
- Select.Trigger,
2125
- {
2126
- "aria-label": ariaLabel,
2127
- className: cn(
2128
- "flex w-full items-center justify-between gap-2 rounded-[var(--radius-md)]",
2129
- "border border-[var(--md3-outline-variant)] bg-surface-container px-3 py-2.5 text-sm text-left",
2130
- "transition-colors duration-[var(--transition-fast)]",
2131
- "hover:border-[var(--md3-outline-variant)] hover:bg-surface-container-high",
2132
- focusRing4,
2133
- "data-[state=open]:border-[var(--md3-outline)] data-[state=open]:bg-surface-container-high",
2134
- "disabled:opacity-50 disabled:cursor-not-allowed",
2135
- triggerClassName
2136
- ),
2137
- children: [
2138
- /* @__PURE__ */ jsxs13("span", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
2139
- current?.icon,
2140
- /* @__PURE__ */ jsx14("span", { className: cn("min-w-0 truncate", current ? "font-medium text-foreground" : "text-muted-foreground"), children: triggerText }),
2141
- current?.description && /* @__PURE__ */ jsx14("span", { className: "sr-only", children: current.description })
2142
- ] }),
2143
- /* @__PURE__ */ jsx14(Select.Icon, { asChild: true, children: /* @__PURE__ */ jsx14(ChevronDown, { className: "h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-[var(--transition-fast)] data-[state=open]:rotate-180" }) })
2144
- ]
2145
- }
2146
- );
2147
- return /* @__PURE__ */ jsxs13("div", { className: cn(variant === "field" ? "space-y-1.5" : "inline-flex", className), children: [
2148
- variant === "field" && label && /* @__PURE__ */ jsx14("label", { className: "block text-xs font-medium text-muted-foreground uppercase tracking-[0.06em]", children: label }),
2149
- /* @__PURE__ */ jsxs13(Select.Root, { value: selected, onValueChange: onChange, children: [
2150
- trigger,
2151
- /* @__PURE__ */ jsx14(Select.Portal, { children: /* @__PURE__ */ jsx14(
2152
- Select.Content,
2153
- {
2154
- position: "popper",
2155
- sideOffset: 4,
2156
- className: cn(
2157
- "z-50 overflow-hidden",
2158
- variant === "pill" ? "min-w-[240px] max-w-[320px]" : "w-[var(--radix-select-trigger-width)]",
2159
- "rounded-[var(--radius-md)] border border-[var(--md3-outline-variant)]",
2160
- "bg-surface-container-highest shadow-[0_8px_30px_rgba(0,0,0,0.45)] ring-1 ring-[#ffffff14]",
2161
- "data-[state=open]:animate-in data-[state=closed]:animate-out",
2162
- "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
2163
- "data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
2164
- "data-[side=bottom]:slide-in-from-top-1"
2165
- ),
2166
- children: /* @__PURE__ */ jsx14(Select.Viewport, { className: "p-1", children: backends.map((backend) => /* @__PURE__ */ jsxs13(
2167
- Select.Item,
2168
- {
2169
- value: backend.type,
2170
- className: cn(
2171
- "relative flex cursor-pointer select-none items-start gap-2.5 rounded-[var(--radius-sm)]",
2172
- "px-3 py-2 text-sm outline-none",
2173
- "transition-colors duration-[var(--transition-fast)]",
2174
- "hover:bg-accent/50 focus:bg-accent/50",
2175
- "data-[state=checked]:bg-[var(--accent-surface-soft)] data-[state=checked]:text-[var(--brand-primary)]"
2176
- ),
2177
- children: [
2178
- backend.icon && /* @__PURE__ */ jsx14("span", { className: "mt-0.5 shrink-0", children: backend.icon }),
2179
- /* @__PURE__ */ jsxs13("span", { className: "flex min-w-0 flex-col", children: [
2180
- /* @__PURE__ */ jsx14(Select.ItemText, { children: /* @__PURE__ */ jsx14("span", { className: "font-medium", children: backend.label }) }),
2181
- backend.description && /* @__PURE__ */ jsx14("span", { className: "mt-0.5 text-xs text-muted-foreground data-[state=checked]:text-[var(--accent-text)]", children: backend.description })
2182
- ] })
2183
- ]
2184
- },
2185
- backend.type
2186
- )) })
2187
- }
2188
- ) })
2189
- ] })
2190
- ] });
2191
- }
2192
-
2193
- // src/dashboard/harness-logo.tsx
2194
- import { Bot, Plug, Terminal as Terminal2 } from "lucide-react";
2195
-
2196
- // node_modules/.pnpm/@lobehub+icons-static-svg@1.94.0/node_modules/@lobehub/icons-static-svg/icons/amp.svg
2197
- var amp_default = 'data:image/svg+xml,<svg fill="currentColor" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Amp</title><path d="M15.087 23.18L12.03 24l-2.097-7.823-5.738 5.738-2.251-2.251 5.718-5.719-7.769-2.082.82-3.057 11.294 3.08 3.08 11.295z"></path><path d="M19.505 18.762l-3.057.82-2.564-9.573-9.572-2.564.819-3.057 11.295 3.079 3.08 11.295z"></path><path d="M23.893 14.374l-3.057.82-2.565-9.572L8.7 3.057 9.52 0l11.295 3.08 3.079 11.294z"></path></svg>';
2198
-
2199
- // node_modules/.pnpm/@lobehub+icons-static-svg@1.94.0/node_modules/@lobehub/icons-static-svg/icons/claudecode.svg
2200
- var claudecode_default = 'data:image/svg+xml,<svg fill="currentColor" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Claude Code</title><path clip-rule="evenodd" d="M20.998 10.949H24v3.102h-3v3.028h-1.487V20H18v-2.921h-1.487V20H15v-2.921H9V20H7.488v-2.921H6V20H4.487v-2.921H3V14.05H0V10.95h3V5h17.998v5.949zM6 10.949h1.488V8.102H6v2.847zm10.51 0H18V8.102h-1.49v2.847z"></path></svg>';
2201
-
2202
- // node_modules/.pnpm/@lobehub+icons-static-svg@1.94.0/node_modules/@lobehub/icons-static-svg/icons/codex.svg
2203
- var codex_default = 'data:image/svg+xml,<svg fill="currentColor" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Codex</title><path clip-rule="evenodd" d="M8.086.457a6.105 6.105 0 013.046-.415c1.333.153 2.521.72 3.564 1.7a.117.117 0 00.107.029c1.408-.346 2.762-.224 4.061.366l.063.03.154.076c1.357.703 2.33 1.77 2.918 3.198.278.679.418 1.388.421 2.126a5.655 5.655 0 01-.18 1.631.167.167 0 00.04.155 5.982 5.982 0 011.578 2.891c.385 1.901-.01 3.615-1.183 5.14l-.182.22a6.063 6.063 0 01-2.934 1.851.162.162 0 00-.108.102c-.255.736-.511 1.364-.987 1.992-1.199 1.582-2.962 2.462-4.948 2.451-1.583-.008-2.986-.587-4.21-1.736a.145.145 0 00-.14-.032c-.518.167-1.04.191-1.604.185a5.924 5.924 0 01-2.595-.622 6.058 6.058 0 01-2.146-1.781c-.203-.269-.404-.522-.551-.821a7.74 7.74 0 01-.495-1.283 6.11 6.11 0 01-.017-3.064.166.166 0 00.008-.074.115.115 0 00-.037-.064 5.958 5.958 0 01-1.38-2.202 5.196 5.196 0 01-.333-1.589 6.915 6.915 0 01.188-2.132c.45-1.484 1.309-2.648 2.577-3.493.282-.188.55-.334.802-.438.286-.12.573-.22.861-.304a.129.129 0 00.087-.087A6.016 6.016 0 015.635 2.31C6.315 1.464 7.132.846 8.086.457zm-.804 7.85a.848.848 0 00-1.473.842l1.694 2.965-1.688 2.848a.849.849 0 001.46.864l1.94-3.272a.849.849 0 00.007-.854l-1.94-3.393zm5.446 6.24a.849.849 0 000 1.695h4.848a.849.849 0 000-1.696h-4.848z"></path></svg>';
2204
-
2205
- // node_modules/.pnpm/@lobehub+icons-static-svg@1.94.0/node_modules/@lobehub/icons-static-svg/icons/hermesagent.svg
2206
- var hermesagent_default = 'data:image/svg+xml,<svg fill="currentColor" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Hermes Agent</title><path d="M5.938 12.835c.127-.039.285.02.373.143.028.038.036.092.046.14.003.014-.02.033-.04.05-.124-.098-.24-.194-.354-.291-.011-.01-.016-.027-.025-.042zM8.396 9.412c.195-.032.39-.06.588-.05a.54.54 0 01.148.026c.202.071.402.147.601.224.028.01.05.036.075.055l-.013.027a9.203 9.203 0 01-.26-.089c-.115-.038-.213-.077-.315-.098-.25-.05-.25-.046-.292-.014l.574.144c.275.139.55.276.823.417.042.022.09.057.107.098.026.06.063.076.117.072.066-.006.132-.017.213-.027l-.04.086c.051.08.142.02.216.064-.074.13-.247.09-.334.199l.061.074-.12.087c0 .106-.038.168-.306.243l.026.085-.196.042.07.124h-.25l-.007.137c-.081-.01-.161-.018-.244-.027l-.053.123c-.027-.008-.052-.011-.073-.023-.067-.038-.128-.056-.195.006-.019.017-.063.014-.093.008-.026-.006-.05-.029-.07-.042-.11.095-.11.095-.208.003-.057.046-.12.074-.186.011-.063.027-.123-.02-.178-.014-.07.007-.097-.035-.133-.07l-.13.033c-.013-.236-.194-.19-.34-.203.005-.072.05-.092.095-.094a.474.474 0 01.159.022c.164.05.32.12.496.138.203.021.405.029.601-.015.265-.059.52-.149.707-.365.049-.056.083-.127.117-.195.019-.038.02-.084-.02-.116a1.397 1.397 0 00-.382-.217c.024.12-.031.182-.115.221 0 .014-.004.025 0 .03.08.115.084.16-.007.267a1.39 1.39 0 01-.218.211.477.477 0 01-.641-.05 1.36 1.36 0 01-.133-.152c-.078-.107-.076-.108-.033-.236-.165-.08-.128-.226-.104-.364.008-.05.028-.096.049-.163-.04.014-.067.017-.087.032a.897.897 0 00-.316.357c-.007.016-.01.034-.02.047-.012.015-.034.038-.045.035-.02-.006-.037-.027-.05-.045-.008-.012-.007-.032-.012-.057h-.126l.053-.172a14.82 14.82 0 00-.039-.049l.11-.284c-.06.026-.091.044-.124.051-.03.007-.064 0-.095 0 0-.031-.01-.07.004-.092.149-.22.305-.428.593-.476z"></path><path d="M8.06 10.788c-.003-.038-.004-.075.037-.062.016.006.034.048.028.067-.01.04-.038.032-.064-.005z"></path><path clip-rule="evenodd" d="M11.981.009c.226-.012.453-.011.679 0 .247.01.495.024.74.062.401.064.798.157 1.19.273.463.138.92.299 1.356.511a7.31 7.31 0 012.948 2.642c.292.469.536.963.739 1.479.219.556.446 1.11.623 1.683.204.654.329 1.326.458 1.997.097.504.182 1.01.29 1.511.156.722.329 1.44.494 2.16.186.812.4 1.615.63 2.415.102.355.193.713.282 1.072.11.436.202.876.254 1.323.031.278.066.557.073.837a7.56 7.56 0 01-.017.88c-.037.413-.1.818-.226 1.212a5.017 5.017 0 01-.915 1.649l-.13.156.018.023c.043-.023.088-.041.127-.068.2-.138.373-.307.531-.49.4-.46.721-.973.975-1.529a3.59 3.59 0 00.325-1.72c-.024-.424-.097-.834-.3-1.213-.013-.027-.015-.06-.03-.121.05.035.082.048.101.072.107.13.22.258.315.398.33.494.46 1.052.486 1.64a3.75 3.75 0 01-.47 1.97c-.36.655-.887 1.14-1.526 1.506-.193.111-.394.21-.595.308-.157.078-.248.211-.318.365a.522.522 0 00-.033.406.359.359 0 01.013.139c-.005.077-.077.155-.14.162-.054.006-.125-.043-.15-.116a1.206 1.206 0 01-.06-.233c-.04-.314-.155-.6-.308-.87a3.906 3.906 0 00-.73-.91 2.129 2.129 0 00-.897-.524 4.093 4.093 0 00-.692-.131c-.075-.008-.15-.04-.22.01.18.06.363.11.538.18.434.173.82.43 1.18.728.308.255.58.543.794.884.098.155.186.315.227.496.027.123.042.25.067.375.013.062-.002.109-.053.144-.047.033-.122.034-.163-.01a.455.455 0 01-.08-.14c-.03-.073-.038-.159-.078-.225a7.314 7.314 0 00-1.423-1.664c-.16-.137-.329-.26-.537-.323-.376-.114-.753-.203-1.15-.154-.213.025-.427.032-.64.053a1.6 1.6 0 00-.736.278 5.14 5.14 0 00-.834.72c-.329.342-.642.699-.955 1.055-.136.155-.264.319-.314.531a5.227 5.227 0 00-.012.051.096.096 0 01-.09.076h-.31c-.046 0-.082-.048-.072-.094.023-.108.045-.216.07-.324.075-.325.19-.635.368-.917.024-.039.04-.088.104-.08l.01.049.027.077c.28-.435.571-.834.996-1.135.283-.204.584-.378.89-.55a.196.196 0 00-.098-.002c-.162.043-.325.084-.485.134-.402.124-.764.33-1.11.566-.147.1-.298.193-.414.333a7.314 7.314 0 00-1.07 1.767.845.845 0 00-.04.12.075.075 0 01-.072.056h-.494c-.04 0-.062-.051-.036-.082.123-.14.246-.282.377-.415.275-.281.58-.532.777-.884.027-.048.063-.09.095-.135.238-.333.54-.607.818-.902.082-.086.175-.16.26-.24.029-.027.053-.057.079-.085l-.018-.025-.135.041c-.034.017-.07.031-.102.05-.248.144-.494.292-.743.433-.408.23-.825.439-1.209.711-.281.2-.591.358-.889.533-.02.012-.044.015-.08.028-.015-.135.143-.201.108-.336-.033.014-.064.02-.085.038-.111.096-.227.19-.328.296-.148.157-.284.325-.425.488-.125.143-.25.286-.373.431A.153.153 0 019.89 24H8.762a.316.316 0 00.016-.042c.028-.09.085-.172.083-.28-.091-.018-.162.001-.212.077a4.45 4.45 0 00-.136.215c-.01.016-.024.03-.042.03h-.093c-.019 0-.029-.022-.017-.037.071-.088.14-.178.209-.268.001-.002-.006-.012-.012-.024-.014.004-.03.006-.045.013-.176.09-.352.181-.527.274a.363.363 0 01-.168.042H5.202c-.026 0-.039-.036-.019-.053.21-.178.402-.374.558-.605.335-.496.538-1.047.667-1.629.004-.02-.003-.043-.006-.091-.037.048-.059.072-.076.1a1.943 1.943 0 01-.334.415c-.28.258-.59.448-.983.464-.297.012-.588 0-.865-.127-.46-.21-.722-.57-.794-1.072-.025-.17-.017-.171-.182-.219A3.513 3.513 0 011.97 20.6a2.286 2.286 0 01-.808-1.13 3.569 3.569 0 01-.16-1.245c.002-.034.016-.067.024-.1.032.023.046.043.05.066.033.153.059.308.096.46.086.355.257.664.516.92.258.256.571.419.91.532.358.118.717.138 1.07-.016a1.89 1.89 0 00.621-.452c.328-.348.533-.76.648-1.223.009-.034.005-.071.007-.11-.015.006-.026.006-.03.011-.031.05-.064.1-.093.152-.284.502-.679.887-1.196 1.135-.351.17-.718.255-1.11.159a1.607 1.607 0 01-.971-.64 2.006 2.006 0 01-.368-.924 2.903 2.903 0 01.02-.886c.05-.439.466-1.17.742-1.271-.02.063-.035.112-.053.16-.043.116-.097.227-.13.345a1.901 1.901 0 00-.05.82c.033.212.09.416.204.6.147.236.346.407.62.465.11.023.225.014.338.018a.576.576 0 00.386-.131c.164-.128.282-.292.366-.481.168-.375.24-.777.309-1.179.05-.296.093-.594.133-.893.039-.281.071-.563.104-.845.026-.232.048-.464.074-.696.024-.228.052-.455.076-.683.024-.227.047-.455.069-.683.013-.14.022-.28.034-.42l.037-.417c.022-.25.041-.5.065-.748.008-.082-.02-.132-.09-.177a2.46 2.46 0 01-.492-.418c-.1-.109-.188-.228-.282-.342-.035-.042-.056-.097-.116-.118a2.084 2.084 0 00.275.597c.06.092.131.176.196.265.063.086.182.115.234.226-.028.003-.046.01-.06.006a4.74 4.74 0 01-.22-.057 2.71 2.71 0 01-1.287-.819c-.435-.487-.656-1.076-.71-1.723a5.206 5.206 0 01.014-1.06c.072-.602.22-1.186.45-1.745.155-.376.338-.741.526-1.102.205-.393.466-.75.765-1.076.512-.559 1.104-1.024 1.726-1.448.717-.49 1.478-.898 2.277-1.233C8.244.828 8.767.632 9.31.494c.655-.166 1.31-.33 1.982-.415.229-.03.458-.058.688-.07zm-1.847 22.82c-.07.06-.147.111-.207.18-.238.27-.464.549-.668.869l-.044.108a.177.177 0 00.093-.057c.174-.19.351-.378.519-.574.104-.122.195-.255.288-.386.024-.034.03-.08.046-.12l-.027-.02zm1.65-3.695a5.51 5.51 0 00-.653.593l-.37.386a.963.963 0 01-.377.25 1.372 1.372 0 01-.467.09c-.044 0-.087.006-.151.012.028.058.043.097.064.131.15.242.301.482.45.724.136.22.276.438.399.666.068.125.105.267.156.404.077.027.14-.018.202-.048.29-.135.579-.274.867-.412.213-.101.437-.186.636-.31.347-.215.68-.455 1.018-.685.015-.01.026-.028.042-.046-.023-.019-.038-.037-.056-.044-.287-.111-.527-.3-.77-.482a5.319 5.319 0 01-.506-.42 1.757 1.757 0 01-.41-.653c-.019-.049-.045-.095-.075-.156zm-5.847.264c-.06.096-.097.194-.132.293a3.38 3.38 0 01-.555 1.01c-.2.25-.455.412-.762.493-.23.06-.464.076-.7.07-.048-.002-.097.002-.158.005.016.04.021.066.035.085.1.145.23.246.4.295.157.046.316.034.498.023.181-.037.343-.115.485-.234.238-.199.402-.454.536-.732.175-.363.264-.751.342-1.144.01-.053.008-.11.011-.164zm14.945-4.586c.008.029.016.057.027.107.024.155.051.31.072.464.03.219.067.437.078.657.017.344.027.689-.014 1.033-.037.315-.063.633-.116.946a6.153 6.153 0 01-.46 1.518c-.008.018-.01.039-.02.082.047-.03.077-.042.098-.064.085-.083.17-.167.248-.255.271-.305.458-.66.596-1.043.18-.498.228-1.011.145-1.531-.103-.65-.33-1.263-.597-1.881a9.055 9.055 0 00-.024-.055l-.033.022zM5.797 8.29a.26.26 0 00.018.153c.124.251.25.501.379.75.025.049.066.09.03.163-.284.06-.578.119-.88.255.059.038.097.06.132.087.042.032.112.058.09.12-.01.033-.075.048-.117.072.017.01.043.021.067.036.166.102.33.207.447.368.138.192.229.404.188.644-.079.469-.306.85-.69 1.132-.054.04-.106.083-.161.122a.243.243 0 00-.103.245.77.77 0 00.055.195c.083.196.22.35.375.492.083.076.159.164.222.257a.37.37 0 01.025.377c-.023.05-.05.099-.076.148-.03.06-.028.111.022.162.041.042.08.089.112.138.038.058.078.079.147.05a.486.486 0 01.333-.006c.16.046.302.126.444.21.13.077.264.149.4.219.067.035.14.05.219.026.071-.022.124.01.145.076.02.064-.003.108-.074.139-.07.03-.137.063-.209.088-.1.035-.201.073-.314.077-.013-.107.11-.088.127-.159-.206-.126-.643-.145-.801-.034.063.112.035.21-.096.313-.13-.1-.025-.202.002-.3a.209.209 0 00-.249.17c-.015.101.067.216.178.224.108.007.218-.005.326-.012.06-.005.12-.027.199 0-.103.123-.248.127-.357.19.002.05.07.086.019.131-.053.048-.095-.001-.132-.03-.08-.063-.16-.126-.231-.197a.474.474 0 01-.157-.311.52.52 0 00-.043-.172c-.032-.074-.032-.137.033-.19-.018-.03-.028-.053-.045-.072a1.222 1.222 0 01-.196-.369c-.053-.137-.046-.264.048-.381.024-.03.05-.06.064-.095a.664.664 0 00.047-.168c.017-.165-.064-.287-.182-.387-.186-.156-.36-.322-.46-.551-.005-.011-.024-.017-.037-.026-.011.017-.024.027-.025.038-.019.185-.045.37-.052.557-.014.377.058.743.162 1.104.118.41.289.798.488 1.173.267.502.537 1.002.812 1.5.055.098.13.189.208.27.198.202.452.272.724.273.202 0 .404-.006.605-.026.295-.03.59-.073.884-.113.183-.025.365-.057.548-.08.21-.026.38.073.522.21.16.156.305.327.447.5.22.265.397.56.554.867.05.098.07.1.147.03.13-.121.26-.242.394-.36.067-.059.088-.12.067-.213a3.535 3.535 0 01-.085-.796c.002-.157.006-.314.018-.471.015-.224.03-.45.06-.672a59.114 59.114 0 01.362-2.298c.087-.493.182-.984.268-1.477.06-.347.118-.694.162-1.043.034-.273.055-.55.063-.825.011-.332.003-.665.002-.998 0-.077.004-.155-.01-.23-.028-.142-.01-.155-.162-.19a5.826 5.826 0 00-.607-.107c-.146-.018-.207-.053-.221-.19-.006-.049-.025-.098-.041-.146-.009-.025-.024-.048-.046-.09l-.025.264c-.009.096-.029.116-.127.115-.055 0-.11-.008-.164-.008-.476 0-.952-.008-1.426.032-.095.008-.173-.015-.226-.103-.04-.066-.088-.126-.134-.186-.063-.084-.086-.093-.182-.06-.195.068-.388.138-.582.21a2.71 2.71 0 00-.675.394.986.986 0 01-.323.168c-.033.01-.07.008-.127.013.02-.066.024-.114.047-.15.064-.105.135-.205.205-.306.023-.033.049-.063.073-.095l-.015-.023-.201.037c-.146.04-.296.07-.437.122-.148.053-.266.023-.386-.072a3.623 3.623 0 01-.733-.786l-.093-.132zm8.592 8.963l-.147.09c-.22.134-.44.266-.659.402-.093.058-.184.12-.27.188-.085.07-.124.161-.072.272.047.1.093.2.147.294.047.08.124.138.213.147.11.01.228.012.336-.012.217-.05.372-.205.528-.357a.291.291 0 00.087-.308c-.046-.18-.079-.365-.118-.547-.011-.052-.027-.103-.045-.169zm-.257-2.409c-.12.291-.205.597-.325.91-.151.433-.294.87-.435 1.323.036-.01.054-.01.067-.018.261-.16.522-.324.785-.484.054-.033.071-.078.065-.138-.012-.13-.024-.262-.034-.393l-.068-.886c-.008-.103-.02-.206-.029-.31-.009 0-.017-.002-.026-.004zm3.081-8.13l.099.285c.08.231.159.463.24.714l.58 1.952c.187.63.372 1.262.558 1.893.114.382.235.762.343 1.146.072.257.126.519.186.799.044.206.087.413.127.64.034.106.023.226.077.325l.025-.006-.068-.362c-.038-.206-.077-.412-.113-.638-.015-.07-.029-.141-.046-.211-.095-.396-.177-.796-.29-1.187-.196-.685-.413-1.364-.618-2.046-.165-.549-.322-1.1-.488-1.648-.069-.227-.15-.45-.226-.695l-.117-.336c-.037-.107-.075-.216-.115-.322-.04-.106-.084-.21-.127-.314a7.558 7.558 0 01-.027.01zM6.225 14.304c-.063-.001-.115.014-.134.083a.35.35 0 00.41.012 4.533 4.533 0 00-.276-.095zM5.23 11.98c-.026-.027-.057-.048-.075.002-.012.032-.007.07-.01.113.082-.037.082-.037.085-.115zm.062-1.189a.135.135 0 00-.088.056.197.197 0 00-.025.11c.005.152.01.306.026.457a.751.751 0 00.066.218c.061.136.157.167.288.101.055-.027.06-.054.025-.11a4.52 4.52 0 01-.129-.211c-.015-.068-.066-.131-.033-.207.04-.09-.076-.116-.074-.19V10.874c-.003-.038-.006-.087-.056-.083zm-.017-.968a.867.867 0 00-.467.127c-.076.045-.084.07-.05.158.034.087.07.173.115.254.064.117.09.125.21.077a.657.657 0 01.336-.053c.202.022.357.136.504.264l.092.077c.007-.006.014-.013.022-.018-.019-.105-.035-.226-.149-.264-.157-.053-.324-.075-.508-.117l-.24-.005c.24-.169.452-.044.687.009-.063-.115-.153-.147-.23-.193-.082-.05-.17-.092-.25-.144-.06-.037-.12-.08-.072-.172zm10.233.325c-.23-.01-.427.08-.608.211-.034.026-.06.065-.105.117.087.026.15.046.232.065.044-.015.088-.03.13-.046.306-.114.61-.115.904.031.126.063.237.04.366-.005-.02-.031-.03-.054-.045-.071a.986.986 0 00-.448-.273c-.14-.044-.284-.024-.426-.03zM7.99 6.483a.308.308 0 00.002.133c.08.321.156.643.242.962.104.387.27.75.456 1.103.02.037.061.08.098.087a.404.404 0 00.253-.051l-.472-.84c-.23-.448-.405-.92-.579-1.394zM10.397.497c-.2-.008-.405.004-.603.034-.236.035-.47.087-.7.152-.287.08-.569.18-.852.273-.04.013-.074.038-.11.058.028.014.05.018.07.014.287-.068.58-.085.873-.09.134-.002.269.009.402.025.19.024.382.048.57.09.456.104.874.3 1.265.556.464.306.888.66 1.257 1.078.205.232.395.475.56.739.17.274.315.561.449.856.273.601.456 1.232.6 1.876.04.173.07.348.1.524.017.104.065.167.17.19.122.028.2.105.22.251-.003.102-.06.174-.129.24a1.065 1.065 0 00-.268.358.164.164 0 00.083-.039c.08-.086.162-.172.235-.265a.56.56 0 00.13-.333c.009-.05.022-.1.024-.15.007-.124-.017-.15-.143-.168-.025-.004-.049-.014-.073-.015-.082-.007-.125-.063-.137-.131-.033-.198-.004-.355.247-.408.086-.018.174-.03.26-.042.158-.023.315-.053.473-.067.14-.012.19.033.226.167.008.029.018.057.021.087.019.179-.008.225-.141.288-.027.013-.055.024-.078.042a.148.148 0 00-.051.067c-.039.144.073.382.206.445l.673.32c.023.011.05.015.075.023l.018-.026c-.015-.008-.032-.013-.044-.024a2.27 2.27 0 00-.544-.32 4.898 4.898 0 00-.173-.075.203.203 0 01-.126-.191c-.003-.085.045-.154.128-.187l.059-.025c.099-.044.118-.076.112-.187a.384.384 0 00-.008-.063c-.067-.294-.123-.59-.205-.88a9.478 9.478 0 00-.826-2.036 7.465 7.465 0 00-1.39-1.805 4.536 4.536 0 00-1.177-.824 3.656 3.656 0 00-1.016-.328 6.155 6.155 0 00-.712-.074zm6.719 5.955c.01.014.018.028.038.034l-.022-.044-.016.01zM4.103 3.917a.062.062 0 01-.03.012.455.455 0 01-.04.039c-.01.01-.02.02-.045.04l-.363.354c-.088.085-.17.178-.266.253-.284.22-.425.53-.544.855a.132.132 0 00-.007.071c.013.055.033.108.052.168l.074.026c-.017.056-.03.105-.047.152-.058.164-.118.327-.175.491-.005.015.008.036.019.077.08-.175.158-.33.225-.489.228-.544.484-1.074.819-1.561.09-.133.182-.266.283-.401.004-.006.007-.013.022-.03.001-.016.003-.032.015-.04l.008-.017zm12.976 2.408a.023.023 0 01.009.019.073.073 0 00-.006.01.188.188 0 00.007.02l.018.022c.002-.007.007-.016.005-.021-.003-.01-.012-.018-.02-.038a1.331 1.331 0 01-.013-.012zM4.199 4.48c-.003.004-.008.008-.027.014-.005.013-.011.025-.031.047a2.085 2.085 0 01-.124.167c-.048.07-.116.055-.181.041-.134-.028-.228.016-.287.143-.089.187-.187.37-.273.56-.049.108-.11.216-.118.36.081.003.154.007.228.008h.228a2.563 2.563 0 01-.079.264c-.01.052-.022.103-.033.155l.02.004c.018-.046.037-.092.067-.153.066-.142.13-.285.2-.426.02-.04.034-.1.116-.092 0 .043.004.084 0 .124-.005.045-.017.09-.028.143.141.043.086.174.115.269.102-.022.104-.195.248-.144v.205l.017.002.439-1.059c-.13 0-.246-.02-.358.033-.024.011-.058-.001-.108-.004.075-.15.139-.278.211-.417a.128.128 0 01.025-.036c0-.015-.001-.03.008-.038l.006-.02c-.005.006-.01.011-.028.017-.004.012-.009.024-.026.045a.085.085 0 01-.032.033c-.123.157-.09.164-.258.106-.079-.027-.078-.028-.047-.144.028-.046.056-.093.098-.15 0-.016-.001-.032.007-.042L4.2 4.48zm2.073-.67c-.003.006-.007.011-.027.016-.094.125-.194.246-.28.377-.155.238-.301.481-.451.723-.14.224-.345.368-.575.481-.017.008-.04.006-.079.011.012-.059.016-.109.033-.153a6.076 6.076 0 01.229-.518l-.007-.02a.138.138 0 01-.035.025c-.028.05-.055.1-.093.164-.26.424-.443.817-.442.95.024.004.048.011.073.013.177.013.188.007.26-.165.03-.07.077-.12.147-.15l.175-.07c.044-.018.085-.057.146-.032.003.05-.01.11.014.145.042.062.044.125.047.193.002.049.017.098.026.147.029-.034.039-.065.05-.097.142-.39.277-.782.428-1.17.1-.256.22-.504.33-.756.013-.03.013-.067.03-.092V3.81zm3.987-.34c0 .045.01.084.021.123.042.16.094.318.124.48.024.133.023.27.028.406 0 .033-.019.067-.032.11-.094-.058-.047-.158-.106-.215h-.125c-.015.072-.01.152-.046.2-.066.085-.155.154-.236.227-.043.038-.078.018-.103-.025l-.046-.087c-.065.035-.117.069-.172.093-.116.051-.235.095-.35.147-.085.038-.09.053-.07.147.014.075.034.148.047.223.013.072.05.109.123.124.233.05.462.115.657.265.058-.102.058-.102.168-.151.03-.014.06-.03.092-.042.08-.03.115-.017.15.06.023.048.041.098.066.158.06-.14-.042-.267.017-.416.157.18.24.39.375.567a.235.235 0 00.022-.098c.002-.124 0-.247.002-.371 0-.034.013-.067.02-.1l.032-.003c.11.155.13.354.226.52a3.036 3.036 0 00-.01-.392c-.004-.045 0-.074.05-.088.08.036.116.14.215.158-.03-.275-.423-1.137-.798-1.635-.114-.127-.2-.28-.34-.386zm-2.667.696c-.019.034-.03.05-.037.067-.061.185-.125.37-.18.556-.031.105-.087.169-.195.19-.09.019-.178.052-.268.073-.038.009-.089.015-.118-.003-.024-.016-.025-.069-.036-.106-.064.076-.082.087-.17.047-.133-.062-.262-.135-.393-.201-.048-.025-.093-.063-.17-.03-.043.12-.091.25-.137.382-.099.28-.087.242.095.453.046.048.102.03.154.023.054-.009.106-.03.16-.036.13-.013.26-.08.367-.015.204-.064.387-.122.571-.178.05-.015.089.005.114.054.022.042.034.093.082.121.038-.056-.013-.128.063-.178l.14.241-.042-1.46zm.278.358c-.096-.01-.107.01-.11.108-.002.038-.003.078.002.115.03.2.099.386.174.57.002.006.012.01.022.015l.078-.05c.052.036.081.088.153.088.205-.002.41.014.616.012.099-.001.158.042.205.12.018.03.024.077.088.066l-.08-.394c-.05-.195-.085-.395-.172-.589-.057.057-.114.068-.18.046a.72.72 0 00-.135-.028c-.22-.028-.44-.059-.66-.08zm10.254-1.727c.089.163.155.316.139.491-.016.168.026.342-.044.516-.047-.033-.088-.082-.112-.075-.117.035-.164-.057-.227-.115a4.772 4.772 0 01-.286-.29l-.104-.113a4.856 4.856 0 01-.023.019c.035.046.07.093.11.156.04.064.084.127.122.193.034.058.065.118.031.205-.082-.01-.164-.019-.246-.032-.06-.01-.101 0-.124.07-.031.098-.037.096-.15.09.02.042.036.08.057.116.041.074.03.138-.03.196-.06.06-.118.122-.178.181a.175.175 0 01-.185.046c-.222-.061-.447-.113-.67-.174-.032-.009-.063-.04-.086-.068-.03-.04-.052-.087-.08-.13-.044-.07-.09-.138-.136-.207a.18.18 0 00-.014.105c.012.127.03.253.035.38.005.1-.024.12-.121.104-.104-.017-.206-.04-.31-.058-.064-.012-.131-.028-.202.03l.081.208c.09 0 .166-.01.237.002a.819.819 0 01.458.251c.078.083.154.168.241.26l.018-.005c-.004-.006-.008-.013-.01-.04.014-.056-.062-.118.018-.178.031.03.064.057.088.09.058.078.111.159.169.257l.089.141.024-.013a2093.819 2093.819 0 01-.427-.934c.055.007.083.007.108.016.193.07.385.142.577.216.074.028.147.06.219.094.062.028.112.018.157-.033.05-.056.102-.112.154-.167.05-.051.095-.046.132.014.016.025.026.053.04.08.071.138.143.277.217.433l.159.308.025-.011c-.044-.106-.07-.218-.138-.334-.057-.182-.168-.346-.206-.545.136.034.362.326.567.732l.057.074.018-.011a1.563 1.563 0 01-.052-.127c-.046-.145-.097-.29-.136-.436-.022-.083-.036-.173.022-.26l.109.058-.026-.207.027-.016c.022.02.05.036.065.06.073.108.143.22.215.33.01.016.029.029.043.043-.036-.217-.2-.38-.229-.626l.155.112c.014-.166.012-.319.042-.465.032-.158-.023-.297-.063-.445.024.004.036.006.055.025.092.124.183.249.277.371.02.027.05.047.069.087l.04.063.019-.015a.293.293 0 01-.053-.082 27.922 27.922 0 01-.332-.49c-.221-.311-.363-.467-.485-.521zm-6.57.327c-.003.161.092.275.069.415l-.368.087c.09.139.032.237-.052.331-.05.057-.092.122-.143.178-.037.04-.046.078-.018.126l.16.275c.029.048.072.066.128.064.076-.003.152 0 .228-.001.116-.003.216.022.275.137.006.014.02.024.044.052.004-.059-.003-.098.01-.13.016-.04.04-.099.072-.108.084-.023.173-.024.26-.03.013-.001.027.018.04.029l.071.065c.019-.11-.082-.198-.024-.31l.126.04c-.026-.123-.07-.245-.071-.366 0-.123.051-.243.115-.36.107.062.16.156.234.253.183.265.36.533.494.834.165-.078.27.068.407.088-.003-.106-.133-.441-.197-.492a.142.142 0 00-.102-.028c-.06.011-.119.039-.191.063-.025-.039-.056-.078-.077-.122a3.936 3.936 0 00-.473-.783c-.076-.094-.16-.182-.228-.26l-.391.285c-.049.035-.094.03-.132-.017l-.169-.207c-.025-.03-.053-.059-.097-.108z"></path></svg>';
2207
-
2208
- // node_modules/.pnpm/@lobehub+icons-static-svg@1.94.0/node_modules/@lobehub/icons-static-svg/icons/openclaw.svg
2209
- var openclaw_default = 'data:image/svg+xml,<svg fill="currentColor" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>OpenClaw</title><g clip-path="url(%23a)"><path d="M9.046 7.104a.527.527 0 110 1.055.527.527 0 010-1.055z"></path><path d="M15.376 7.104a.528.528 0 110 1.056.528.528 0 010-1.056z"></path><path clip-rule="evenodd" d="M16.877 1.912c.58-.27 1.14-.323 1.616-.037a.317.317 0 01-.326.542c-.227-.136-.547-.153-1.022.068-.352.165-.765.45-1.234.866 2.683 1.17 4.4 3.5 5.148 5.921a6.421 6.421 0 00-.704.184c-.578.016-1.174.204-1.502.735-.338.55-.268 1.276.072 2.069l.005.012.007.014c.523 1.045 1.318 1.91 2.2 2.284-.912 3.274-3.44 6.144-5.972 6.988v2.109h-2.11v-2.11c-1.043.417-2.086.01-2.11 0v2.11h-2.11v-2.11c-2.531-.843-5.061-3.713-5.973-6.987.882-.373 1.678-1.238 2.2-2.284l.007-.014.006-.012c.34-.793.41-1.518.071-2.069-.327-.531-.923-.719-1.503-.735a6.409 6.409 0 00-.704-.183c.749-2.421 2.466-4.751 5.149-5.922-.47-.416-.88-.701-1.234-.866-.474-.221-.794-.204-1.021-.068a.318.318 0 01-.435-.109.317.317 0 01.109-.433c.476-.286 1.036-.233 1.615.037.49.229 1.031.628 1.621 1.182A9.924 9.924 0 0112 2.568c1.199 0 2.284.19 3.256.526.59-.554 1.13-.953 1.62-1.182zM8.835 6.577a1.266 1.266 0 100 2.532 1.266 1.266 0 000-2.532zm6.33 0a1.267 1.267 0 100 2.533 1.267 1.267 0 000-2.533z"></path><path d="M.395 13.118c-.966-1.932-.163-3.863 2.41-3.365v-.001l.05.01c.084.018.17.038.26.06.033.009.067.017.1.027.084.022.168.048.255.076l.09.027c.528 0 .95.158 1.16.501.212.343.212.87-.105 1.61-.085.17-.178.333-.276.489l-.01.017a4.967 4.967 0 01-.62.791l-.019.02c-1.092 1.117-2.496 1.336-3.295-.262z"></path><path d="M21.193 9.753c2.574-.5 3.378 1.433 2.411 3.365-.58 1.159-1.476 1.361-2.342.96l-.011-.005a2.419 2.419 0 01-.114-.056l-.019-.01a2.751 2.751 0 01-.115-.067l-.023-.014c-.035-.022-.071-.044-.106-.068l-.05-.035c-.55-.388-1.062-1.007-1.44-1.76-.276-.647-.311-1.132-.174-1.472.176-.439.636-.639 1.23-.639.032-.011.066-.02.099-.03.08-.026.16-.05.238-.072l.117-.03a5.502 5.502 0 01.3-.067z"></path></g><defs><linearGradient gradientUnits="userSpaceOnUse" id="b" x1="-.659" x2="27.023" y1=".458" y2="22.855"><stop stop-color="%23FF4D4D"></stop><stop offset="1" stop-color="%23991B1B"></stop></linearGradient><linearGradient gradientUnits="userSpaceOnUse" id="c" x1="-.659" x2="27.023" y1=".458" y2="22.855"><stop stop-color="%23FF4D4D"></stop><stop offset="1" stop-color="%23991B1B"></stop></linearGradient><linearGradient gradientUnits="userSpaceOnUse" id="d" x1="-.659" x2="27.023" y1=".458" y2="22.855"><stop stop-color="%23FF4D4D"></stop><stop offset="1" stop-color="%23991B1B"></stop></linearGradient><linearGradient gradientUnits="userSpaceOnUse" id="e" x1="-.659" x2="27.023" y1=".458" y2="22.855"><stop stop-color="%23FF4D4D"></stop><stop offset="1" stop-color="%23991B1B"></stop></linearGradient><linearGradient gradientUnits="userSpaceOnUse" id="f" x1="-.659" x2="27.023" y1=".458" y2="22.855"><stop stop-color="%23FF4D4D"></stop><stop offset="1" stop-color="%23991B1B"></stop></linearGradient><clipPath id="a"><path d="M0 0h24v24H0z"></path></clipPath></defs></svg>';
2210
-
2211
- // node_modules/.pnpm/@lobehub+icons-static-svg@1.94.0/node_modules/@lobehub/icons-static-svg/icons/opencode.svg
2212
- var opencode_default = 'data:image/svg+xml,<svg fill="currentColor" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>opencode</title><path d="M16 6H8v12h8V6zm4 16H4V2h16v20z"></path></svg>';
2213
-
2214
- // src/dashboard/harness-logo.tsx
2215
- import { jsx as jsx15 } from "react/jsx-runtime";
2216
- var HARNESS_BRAND = {
2217
- opencode: { label: "OpenCode", logoUrl: opencode_default },
2218
- "claude-code": { label: "Claude Code", logoUrl: claudecode_default },
2219
- codex: { label: "Codex", logoUrl: codex_default },
2220
- amp: { label: "AMP", logoUrl: amp_default },
2221
- "kimi-code": { label: "Kimi Code", logoUrl: moonshot_default },
2222
- openclaw: { label: "OpenClaw", logoUrl: openclaw_default },
2223
- hermes: { label: "Hermes", logoUrl: hermesagent_default },
2224
- // No published brand mark — honest lucide glyphs.
2225
- "factory-droids": { label: "Factory Droids", icon: Bot },
2226
- nanoclaw: { label: "NanoClaw", icon: Plug },
2227
- "cli-base": { label: "CLI base", icon: Terminal2 }
2228
- };
2229
- function HarnessLogo({ type, size = 16, className }) {
2230
- const brand = HARNESS_BRAND[type] ?? { label: type, icon: Bot };
2231
- const Icon2 = brand.icon;
2232
- return /* @__PURE__ */ jsx15(
2233
- "span",
2234
- {
2235
- title: brand.label,
2236
- "aria-label": brand.label,
2237
- className: cn(
2238
- "inline-flex shrink-0 items-center justify-center overflow-hidden rounded-full bg-background shadow-sm ring-1 ring-border",
2239
- className
2240
- ),
2241
- style: { height: size, width: size },
2242
- children: brand.logoUrl ? /* @__PURE__ */ jsx15(
2243
- "span",
2244
- {
2245
- "aria-hidden": true,
2246
- className: "h-[72%] w-[72%]",
2247
- style: {
2248
- backgroundColor: "hsl(var(--foreground))",
2249
- maskImage: cssUrl(brand.logoUrl),
2250
- maskSize: "contain",
2251
- maskRepeat: "no-repeat",
2252
- maskPosition: "center",
2253
- WebkitMaskImage: cssUrl(brand.logoUrl),
2254
- WebkitMaskSize: "contain",
2255
- WebkitMaskRepeat: "no-repeat",
2256
- WebkitMaskPosition: "center"
2257
- }
2258
- }
2259
- ) : Icon2 ? /* @__PURE__ */ jsx15(Icon2, { className: "h-[72%] w-[72%] text-muted-foreground" }) : null
2260
- }
2261
- );
2262
- }
2263
- function cssUrl(url) {
2264
- return `url("${url.replace(/[\\"'#\n]/g, (char) => encodeURIComponent(char))}")`;
2265
- }
2266
-
2267
- // src/dashboard/harness-picker.tsx
2268
- import { jsx as jsx16 } from "react/jsx-runtime";
2269
- var HARNESS_OPTIONS = [
2270
- {
2271
- type: "opencode",
2272
- label: "OpenCode",
2273
- description: "Broad model support, deterministic streaming",
2274
- chatCapable: true
2275
- },
2276
- {
2277
- type: "claude-code",
2278
- label: "Claude Code",
2279
- description: "Native Claude skills and tools",
2280
- chatCapable: true
2281
- },
2282
- {
2283
- type: "codex",
2284
- label: "Codex",
2285
- description: "OpenAI's Codex CLI",
2286
- chatCapable: true
2287
- },
2288
- {
2289
- type: "amp",
2290
- label: "AMP",
2291
- description: "Sourcegraph's coding agent",
2292
- chatCapable: true
2293
- },
2294
- {
2295
- type: "factory-droids",
2296
- label: "Factory Droids",
2297
- description: "Factory's Droid agent",
2298
- chatCapable: true
2299
- },
2300
- {
2301
- type: "kimi-code",
2302
- label: "Kimi Code",
2303
- description: "Moonshot's Kimi Code CLI",
2304
- chatCapable: true
2305
- },
2306
- {
2307
- type: "openclaw",
2308
- label: "OpenClaw",
2309
- description: "Routes to Claude, Codex, or Gemini",
2310
- chatCapable: true
2311
- },
2312
- {
2313
- type: "nanoclaw",
2314
- label: "NanoClaw",
2315
- description: "Local socket-bridge backend",
2316
- chatCapable: true
2317
- },
2318
- {
2319
- type: "hermes",
2320
- label: "Hermes",
2321
- description: "Hermes inference-router agent",
2322
- chatCapable: true
2323
- },
2324
- {
2325
- type: "cli-base",
2326
- label: "CLI base (no agent)",
2327
- description: "Shell tools only, for non-AI scheduled tasks",
2328
- chatCapable: false
2329
- }
2330
- ];
2331
- var chatCapableHarnesses = HARNESS_OPTIONS.filter((h) => h.chatCapable).map((h) => h.type);
2332
- function HarnessPicker({
2333
- value,
2334
- onChange,
2335
- available,
2336
- optionsOverride,
2337
- label = "Agent harness",
2338
- ...rest
2339
- }) {
2340
- const allowed = new Set(available ?? HARNESS_OPTIONS.map((h) => h.type));
2341
- const backends = HARNESS_OPTIONS.filter((h) => allowed.has(h.type)).map((h) => {
2342
- const override = optionsOverride?.[h.type];
2343
- return {
2344
- type: h.type,
2345
- label: override?.label ?? h.label,
2346
- description: override?.description ?? h.description,
2347
- icon: /* @__PURE__ */ jsx16(HarnessLogo, { type: h.type, size: 16 })
2348
- };
2349
- });
2350
- return /* @__PURE__ */ jsx16(
2351
- BackendSelector,
2352
- {
2353
- backends,
2354
- selected: value,
2355
- onChange: (next) => onChange(next),
2356
- label,
2357
- ...rest
2358
- }
2359
- );
2360
- }
2361
-
2362
- // src/dashboard/dashboard-layout.tsx
2363
- import * as React9 from "react";
2364
- import { Plus as Plus2, Bell } from "lucide-react";
2365
-
2366
- // src/dashboard/use-brand-theme-sync.ts
2367
- import * as React8 from "react";
2368
- function useBrandThemeSync() {
2369
- React8.useEffect(() => {
2370
- if (typeof document === "undefined") return;
2371
- const el = document.documentElement;
2372
- const sync = () => {
2373
- const dark = el.classList.contains("dark");
2374
- el.classList.toggle("light", !dark);
2375
- const next = dark ? "dark" : "light";
2376
- if (el.getAttribute("data-theme") !== next) el.setAttribute("data-theme", next);
2377
- };
2378
- sync();
2379
- const observer = new MutationObserver(sync);
2380
- observer.observe(el, { attributes: true, attributeFilter: ["class"] });
2381
- return () => observer.disconnect();
2382
- }, []);
2383
- }
2384
-
2385
- // src/dashboard/dashboard-layout.tsx
2386
- import { Fragment as Fragment9, jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
2387
- function MenuIcon({ className }) {
2388
- return /* @__PURE__ */ jsxs14("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: [
2389
- /* @__PURE__ */ jsx17("title", { children: "Menu icon" }),
2390
- /* @__PURE__ */ jsx17("line", { x1: "4", x2: "20", y1: "12", y2: "12" }),
2391
- /* @__PURE__ */ jsx17("line", { x1: "4", x2: "20", y1: "6", y2: "6" }),
2392
- /* @__PURE__ */ jsx17("line", { x1: "4", x2: "20", y1: "18", y2: "18" })
2393
- ] });
2394
- }
2395
- function XIcon({ className }) {
2396
- return /* @__PURE__ */ jsxs14("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: [
2397
- /* @__PURE__ */ jsx17("title", { children: "Close icon" }),
2398
- /* @__PURE__ */ jsx17("path", { d: "M18 6 6 18" }),
2399
- /* @__PURE__ */ jsx17("path", { d: "m6 6 12 12" })
2400
- ] });
2401
- }
2402
- function formatNotifDate(raw) {
2403
- const d = new Date(raw);
2404
- return Number.isNaN(d.getTime()) ? raw : d.toLocaleString(void 0, { month: "short", day: "numeric", hour: "2-digit", minute: "2-digit" });
2405
- }
2406
- function DefaultLink2({
2407
- href,
2408
- to,
2409
- className,
2410
- children,
2411
- ...rest
2412
- }) {
2413
- return /* @__PURE__ */ jsx17("a", { href: href ?? to, className, ...rest, children });
2414
- }
2415
- function DashboardLayoutInner({
2416
- children,
2417
- variant = "sandbox",
2418
- navItems,
2419
- modeItems = [],
2420
- panels = [],
2421
- activeNavId,
2422
- user,
2423
- isLoading = false,
2424
- onLogout,
2425
- onSettingsClick,
2426
- settingsHref = "/dashboard/settings",
2427
- onNewSandbox,
2428
- className,
2429
- sidebarClassName,
2430
- contentClassName,
2431
- topNavLinks,
2432
- activeTopNavHref,
2433
- topBarLeading,
2434
- LinkComponent = DefaultLink2,
2435
- logoHref = "/",
2436
- labeledRail = false,
2437
- footer,
2438
- railFooter,
2439
- profileMenuItems,
2440
- appearance,
2441
- notifications: notifData
2442
- }) {
2443
- useBrandThemeSync();
2444
- const Link = LinkComponent;
2445
- const [mobileMenuOpen, setMobileMenuOpen] = React9.useState(false);
2446
- const [notificationsOpen, setNotificationsOpen] = React9.useState(false);
2447
- const notifRef = React9.useRef(null);
2448
- React9.useEffect(() => {
2449
- if (!notificationsOpen) return;
2450
- const handler = (e) => {
2451
- if (notifRef.current && !notifRef.current.contains(e.target)) {
2452
- setNotificationsOpen(false);
2453
- }
2454
- };
2455
- const keyHandler = (e) => {
2456
- if (e.key === "Escape") setNotificationsOpen(false);
2457
- };
2458
- document.addEventListener("mousedown", handler);
2459
- document.addEventListener("keydown", keyHandler);
2460
- return () => {
2461
- document.removeEventListener("mousedown", handler);
2462
- document.removeEventListener("keydown", keyHandler);
2463
- };
2464
- }, [notificationsOpen]);
2465
- const { contentMargin, hidden, mode, hasPanels, panelOpen, toggleRail, railCollapsed } = useSidebar();
2466
- const modeSet = React9.useMemo(() => new Set(modeItems), [modeItems]);
2467
- const sidebarUser = React9.useMemo(
2468
- () => user ? { email: user.email, name: user.name, tier: user.tier, avatarUrl: user.avatarUrl } : void 0,
2469
- [user?.email, user?.name, user?.tier, user?.avatarUrl]
2470
- );
2471
- const activePanel = panels.find((p) => p.mode === mode);
2472
- const buildSidebarContent = React9.useCallback(
2473
- (showLabels, allowCollapse) => /* @__PURE__ */ jsxs14(Fragment9, { children: [
2474
- /* @__PURE__ */ jsxs14(SidebarRail, { wide: showLabels, children: [
2475
- /* @__PURE__ */ jsx17(
2476
- RailHeader,
2477
- {
2478
- brand: /* @__PURE__ */ jsx17(Logo, { variant, size: "sm", iconOnly: !showLabels }),
2479
- brandHref: logoHref,
2480
- collapsed: !showLabels,
2481
- onToggle: toggleRail,
2482
- collapsible: allowCollapse,
2483
- LinkComponent: Link
2484
- }
2485
- ),
2486
- /* @__PURE__ */ jsx17(
2487
- SidebarRailNav,
2488
- {
2489
- className: cn(showLabels ? "px-2" : void 0, !showLabels && allowCollapse && "cursor-pointer"),
2490
- onClick: !showLabels && allowCollapse ? (e) => {
2491
- if (e.target === e.currentTarget) toggleRail();
2492
- } : void 0,
2493
- children: navItems.map((item, i) => {
2494
- const isMode = modeSet.has(item.id);
2495
- const prevIsMode = i > 0 && modeSet.has(navItems[i - 1].id);
2496
- const showSep = i > 0 && isMode && !prevIsMode;
2497
- const arrival = { className: "agent-arrive", style: { "--stagger-index": i } };
2498
- return /* @__PURE__ */ jsxs14(React9.Fragment, { children: [
2499
- showSep && /* @__PURE__ */ jsx17(
2500
- RailSeparator,
2501
- {
2502
- className: showLabels ? "w-full" : void 0
2503
- }
2504
- ),
2505
- isMode ? /* @__PURE__ */ jsx17(
2506
- RailModeButton,
2507
- {
2508
- mode: item.id,
2509
- icon: item.icon,
2510
- label: item.label,
2511
- badge: item.badge,
2512
- showLabel: showLabels,
2513
- ...arrival
2514
- }
2515
- ) : /* @__PURE__ */ jsx17(
2516
- RailButton,
2517
- {
2518
- icon: item.icon,
2519
- label: item.label,
2520
- isActive: activeNavId === item.id,
2521
- showLabel: showLabels,
2522
- asChild: true,
2523
- ...arrival,
2524
- children: /* @__PURE__ */ jsx17(Link, { href: item.href, to: item.href })
2525
- }
2526
- )
2527
- ] }, item.id);
2528
- })
2529
- }
2530
- ),
2531
- /* @__PURE__ */ jsx17(SidebarRailFooter, { className: cn("border-t border-[var(--md3-outline-variant)] pt-2", showLabels && "items-stretch px-2"), children: railFooter !== void 0 ? showLabels ? /* @__PURE__ */ jsxs14("div", { className: "flex w-full items-center gap-1", children: [
2532
- /* @__PURE__ */ jsx17("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx17(
2533
- ProfileAvatar,
2534
- {
2535
- user: sidebarUser,
2536
- isLoading,
2537
- onLogout,
2538
- onSettingsClick,
2539
- settingsHref,
2540
- showDetails: showLabels,
2541
- appearance,
2542
- LinkComponent,
2543
- children: profileMenuItems
2544
- }
2545
- ) }),
2546
- /* @__PURE__ */ jsx17("div", { className: "shrink-0", children: railFooter })
2547
- ] }) : /* @__PURE__ */ jsxs14("div", { className: "flex flex-col items-center gap-1", children: [
2548
- railFooter,
2549
- /* @__PURE__ */ jsx17(
2550
- ProfileAvatar,
2551
- {
2552
- user: sidebarUser,
2553
- isLoading,
2554
- onLogout,
2555
- onSettingsClick,
2556
- settingsHref,
2557
- showDetails: showLabels,
2558
- appearance,
2559
- LinkComponent,
2560
- children: profileMenuItems
2561
- }
2562
- )
2563
- ] }) : /* @__PURE__ */ jsx17(
2564
- ProfileAvatar,
2565
- {
2566
- user: sidebarUser,
2567
- isLoading,
2568
- onLogout,
2569
- onSettingsClick,
2570
- settingsHref,
2571
- showDetails: showLabels,
2572
- appearance,
2573
- LinkComponent,
2574
- children: profileMenuItems
2575
- }
2576
- ) })
2577
- ] }),
2578
- panels.length > 0 && /* @__PURE__ */ jsxs14(SidebarPanel, { children: [
2579
- /* @__PURE__ */ jsx17(SidebarPanelHeader, { title: activePanel?.title ?? mode }),
2580
- /* @__PURE__ */ jsx17(SidebarPanelContent, { children: activePanel?.content })
2581
- ] })
2582
- ] }),
2583
- // biome-ignore lint/correctness/useExhaustiveDependencies: intentional — only the inputs that actually affect the sidebar tree
2584
- [
2585
- Link,
2586
- variant,
2587
- logoHref,
2588
- labeledRail,
2589
- toggleRail,
2590
- railCollapsed,
2591
- navItems,
2592
- modeSet,
2593
- activeNavId,
2594
- onSettingsClick,
2595
- settingsHref,
2596
- railFooter,
2597
- sidebarUser,
2598
- isLoading,
2599
- onLogout,
2600
- LinkComponent,
2601
- profileMenuItems,
2602
- appearance,
2603
- panels,
2604
- activePanel,
2605
- mode
2606
- ]
2607
- );
2608
- const sidebarContent = React9.useMemo(
2609
- () => buildSidebarContent(labeledRail && !railCollapsed, labeledRail),
2610
- [buildSidebarContent, labeledRail, railCollapsed]
2611
- );
2612
- const mobileSidebarContent = React9.useMemo(() => buildSidebarContent(true, false), [buildSidebarContent]);
2613
- return /* @__PURE__ */ jsxs14("div", { className: cn("min-h-screen bg-surface text-foreground", className), children: [
2614
- /* @__PURE__ */ jsxs14(
2615
- "nav",
2616
- {
2617
- className: cn(
2618
- "fixed top-0 z-50 bg-surface-container-low border-b border-[var(--md3-outline-variant)] flex justify-between items-center px-8 h-14 font-sans text-[13px] tracking-tight transition-[left,width]",
2619
- MOTION_TRAVEL
2620
- ),
2621
- style: {
2622
- left: hidden ? 0 : contentMargin,
2623
- width: hidden ? "100%" : `calc(100% - ${contentMargin}px)`
2624
- },
2625
- children: [
2626
- /* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-8", children: [
2627
- /* @__PURE__ */ jsx17(Link, { href: logoHref, to: logoHref, className: cn("lg:hidden flex items-center p-1 rounded-md hover:bg-surface-container-high transition-colors", MOTION_CONTROL), children: /* @__PURE__ */ jsx17(Logo, { variant, size: "sm", iconOnly: true }) }),
2628
- topBarLeading,
2629
- topNavLinks && topNavLinks.length > 0 && /* @__PURE__ */ jsx17("div", { className: "hidden md:flex gap-6", children: topNavLinks.map((link) => /* @__PURE__ */ jsx17(
2630
- Link,
2631
- {
2632
- href: link.href,
2633
- to: link.href,
2634
- className: cn(
2635
- "transition-all px-2 py-1 rounded",
2636
- MOTION_CONTROL,
2637
- activeTopNavHref === link.href ? "text-foreground border-b-2 border-primary pb-1" : "text-muted-foreground hover:text-foreground hover:bg-surface-container-high"
2638
- ),
2639
- children: link.label
2640
- },
2641
- link.href
2642
- )) })
2643
- ] }),
2644
- /* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-4", children: [
2645
- onNewSandbox && /* @__PURE__ */ jsxs14(
2646
- "button",
2647
- {
2648
- type: "button",
2649
- onClick: onNewSandbox,
2650
- className: cn("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", MOTION_CONTROL),
2651
- children: [
2652
- /* @__PURE__ */ jsx17(Plus2, { className: "h-3.5 w-3.5" }),
2653
- "New Sandbox"
2654
- ]
2655
- }
2656
- ),
2657
- /* @__PURE__ */ jsxs14("div", { className: "relative", ref: notifRef, children: [
2658
- /* @__PURE__ */ jsxs14(
2659
- "button",
2660
- {
2661
- type: "button",
2662
- className: cn("relative text-muted-foreground hover:text-foreground transition-colors p-2 rounded-lg hover:bg-surface-container-high", MOTION_CONTROL),
2663
- onClick: () => setNotificationsOpen(!notificationsOpen),
2664
- "aria-label": "Notifications",
2665
- "aria-expanded": notificationsOpen,
2666
- children: [
2667
- /* @__PURE__ */ jsx17(Bell, { className: "h-4 w-4" }),
2668
- (notifData?.unreadCount ?? 0) > 0 && /* @__PURE__ */ jsx17("span", { className: "absolute top-1 right-1 h-2 w-2 rounded-full bg-destructive" })
2669
- ]
2670
- }
2671
- ),
2672
- notificationsOpen && /* @__PURE__ */ jsxs14("div", { className: "absolute right-0 top-full mt-2 w-80 rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container-highest shadow-[0_8px_30px_rgba(0,0,0,0.45)] ring-1 ring-[#ffffff14] z-50", children: [
2673
- /* @__PURE__ */ jsxs14("div", { className: "flex items-center justify-between border-b border-[var(--md3-outline-variant)] px-4 py-3", children: [
2674
- /* @__PURE__ */ jsx17("p", { className: "font-bold text-foreground text-sm", children: "Notifications" }),
2675
- (notifData?.unreadCount ?? 0) > 0 && notifData?.onMarkAllRead && /* @__PURE__ */ jsx17(
2676
- "button",
2677
- {
2678
- type: "button",
2679
- onClick: () => {
2680
- notifData.onMarkAllRead?.();
2681
- },
2682
- className: "text-primary text-xs font-medium hover:underline",
2683
- children: "Mark all read"
2684
- }
2685
- )
2686
- ] }),
2687
- !notifData?.items || notifData.items.length === 0 ? /* @__PURE__ */ jsxs14("div", { className: "flex flex-col items-center justify-center px-4 py-8 text-center", children: [
2688
- /* @__PURE__ */ jsx17(Bell, { className: "h-8 w-8 text-muted-foreground/40 mb-2" }),
2689
- /* @__PURE__ */ jsx17("p", { className: "text-muted-foreground text-sm", children: "No notifications yet" }),
2690
- /* @__PURE__ */ jsx17("p", { className: "text-muted-foreground/60 text-xs mt-1", children: "We'll notify you about important updates" })
2691
- ] }) : /* @__PURE__ */ jsx17("div", { className: "max-h-80 overflow-y-auto", children: notifData.items.map((n) => /* @__PURE__ */ jsxs14(
2692
- "button",
2693
- {
2694
- type: "button",
2695
- className: cn(
2696
- "w-full text-left px-4 py-3 border-b border-[var(--md3-outline-variant)] last:border-0 transition-colors",
2697
- MOTION_CONTROL,
2698
- n.read ? "cursor-default" : "bg-primary/5 hover:bg-white/5"
2699
- ),
2700
- onClick: () => {
2701
- if (!n.read) notifData.onMarkRead?.(n.id);
2702
- },
2703
- children: [
2704
- /* @__PURE__ */ jsx17("p", { className: cn("text-sm", !n.read ? "font-semibold text-foreground" : "text-muted-foreground"), children: n.title }),
2705
- /* @__PURE__ */ jsx17("p", { className: "text-xs text-muted-foreground mt-0.5 line-clamp-2", children: n.message }),
2706
- /* @__PURE__ */ jsx17("p", { className: "text-[10px] text-muted-foreground/50 mt-1", children: formatNotifDate(n.createdAt) })
2707
- ]
2708
- },
2709
- n.id
2710
- )) })
2711
- ] })
2712
- ] })
2713
- ] }),
2714
- /* @__PURE__ */ jsx17(
2715
- "button",
2716
- {
2717
- type: "button",
2718
- onClick: () => setMobileMenuOpen(!mobileMenuOpen),
2719
- className: "rounded-md p-2 hover:bg-surface-container-high lg:hidden",
2720
- "aria-label": mobileMenuOpen ? "Close menu" : "Open menu",
2721
- "aria-expanded": mobileMenuOpen,
2722
- children: mobileMenuOpen ? /* @__PURE__ */ jsx17(XIcon, { className: "h-6 w-6" }) : /* @__PURE__ */ jsx17(MenuIcon, { className: "h-6 w-6" })
2723
- }
2724
- )
2725
- ]
2726
- }
2727
- ),
2728
- mobileMenuOpen && /* @__PURE__ */ jsx17("div", { className: "fixed inset-0 z-30 bg-black/50 lg:hidden", onClick: () => setMobileMenuOpen(false), "aria-hidden": "true" }),
2729
- /* @__PURE__ */ jsx17(
2730
- "aside",
2731
- {
2732
- className: cn(
2733
- "fixed top-14 bottom-0 left-0 z-30 flex bg-surface-container-low transition-transform lg:hidden",
2734
- MOTION_TRAVEL,
2735
- mobileMenuOpen ? "translate-x-0" : "-translate-x-full"
2736
- ),
2737
- style: {
2738
- width: panelOpen && hasPanels ? SIDEBAR_MOBILE_WIDTH + SIDEBAR_PANEL_WIDTH : SIDEBAR_MOBILE_WIDTH
2739
- },
2740
- children: mobileSidebarContent
2741
- }
2742
- ),
2743
- /* @__PURE__ */ jsx17(Sidebar, { className: cn("hidden lg:flex", sidebarClassName), children: sidebarContent }),
2744
- /* @__PURE__ */ jsx17(SidebarContent, { className: cn("pt-16 px-6 pb-8 lg:px-8 bg-surface", contentClassName), children }),
2745
- footer
2746
- ] });
2747
- }
2748
- function DashboardLayout({ defaultPanelOpen, defaultMode, labeledRail, defaultRailCollapsed, ...props }) {
2749
- return /* @__PURE__ */ jsx17(SidebarProvider, { defaultPanelOpen, defaultMode, labeledRail, defaultRailCollapsed, hasPanels: (props.panels?.length ?? 0) > 0, children: /* @__PURE__ */ jsx17(DashboardLayoutInner, { defaultPanelOpen, defaultMode, labeledRail, ...props }) });
2750
- }
2751
-
2752
- // src/dashboard/sidebar-layout.tsx
2753
- import * as React10 from "react";
2754
- import { Fragment as Fragment10, jsx as jsx18, jsxs as jsxs15 } from "react/jsx-runtime";
2755
- var HIDE_BELOW_CLASS = {
2756
- md: "max-md:hidden",
2757
- lg: "max-lg:hidden"
2758
- };
2759
- var SHOW_BELOW_CLASS = {
2760
- md: "md:hidden",
2761
- lg: "lg:hidden"
2762
- };
2763
- function MenuIcon2({ className }) {
2764
- return /* @__PURE__ */ jsx18("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx18("path", { d: "M3 6h18M3 12h18M3 18h18" }) });
2765
- }
2766
- function CloseIcon({ className }) {
2767
- return /* @__PURE__ */ jsx18("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx18("path", { d: "M18 6 6 18M6 6l12 12" }) });
2768
- }
2769
- function DefaultLink3({
2770
- href,
2771
- to,
2772
- className,
2773
- children,
2774
- ...rest
2775
- }) {
2776
- return /* @__PURE__ */ jsx18("a", { href: href ?? to, className, ...rest, children });
2777
- }
2778
- function SidebarLayoutInner({
2779
- children,
2780
- navItems,
2781
- activeId,
2782
- panel,
2783
- logo,
2784
- logoHref,
2785
- railHeaderContent,
2786
- user,
2787
- isLoading = false,
2788
- onLogout,
2789
- onSettingsClick,
2790
- settingsHref,
2791
- profileMenuItems,
2792
- appearance,
2793
- railFooter,
2794
- LinkComponent,
2795
- hideBelow,
2796
- railLabels = false,
2797
- closePanelOnNavigate = false,
2798
- className,
2799
- sidebarClassName,
2800
- contentClassName
2801
- }) {
2802
- useBrandThemeSync();
2803
- const Link = LinkComponent ?? DefaultLink3;
2804
- const { panelOpen, togglePanel, setPanelOpen, railCollapsed, toggleRail } = useSidebar();
2805
- const handleNavClick = closePanelOnNavigate && panelOpen ? () => setPanelOpen(false) : void 0;
2806
- const expandOnEmptyClick = railLabels && railCollapsed ? (e) => {
2807
- if (e.target === e.currentTarget) toggleRail();
2808
- } : void 0;
2809
- const hasProfile = user !== void 0 || onLogout !== void 0 || onSettingsClick !== void 0;
2810
- const showLabels = railLabels && !railCollapsed;
2811
- const [mobileNavOpen, setMobileNavOpen] = React10.useState(false);
2812
- const closeMobileNav = React10.useCallback(() => setMobileNavOpen(false), []);
2813
- function renderNavItems({
2814
- showLabels: showLabels2,
2815
- onNavigate
2816
- }) {
2817
- return navItems.map((item, index) => {
2818
- const arrival = { className: "agent-arrive", style: { "--stagger-index": index } };
2819
- if (item.expandable) {
2820
- return /* @__PURE__ */ jsx18(
2821
- RailExpandable,
2822
- {
2823
- icon: item.icon,
2824
- label: item.label,
2825
- href: item.href,
2826
- isActive: activeId === item.id,
2827
- showLabel: showLabels2,
2828
- subItems: item.subItems,
2829
- loadSubItems: item.loadSubItems,
2830
- activeSubIds: item.subActiveIds,
2831
- emptyLabel: item.emptyLabel,
2832
- defaultOpen: item.defaultOpen,
2833
- onNavigate,
2834
- LinkComponent,
2835
- ...arrival
2836
- },
2837
- item.id
2838
- );
2839
- }
2840
- if (item.flyoutItems && item.flyoutItems.length > 0) {
2841
- const activeSet = new Set(item.flyoutActiveIds ?? []);
2842
- return /* @__PURE__ */ jsx18(
2843
- RailFlyout,
2844
- {
2845
- icon: item.icon,
2846
- label: item.label,
2847
- title: item.label,
2848
- showLabel: showLabels2,
2849
- isActive: activeId === item.id || item.flyoutItems.some((f) => activeSet.has(f.id)),
2850
- ...arrival,
2851
- children: item.flyoutItems.map((f) => {
2852
- const FIcon = f.icon;
2853
- const active = activeSet.has(f.id);
2854
- return /* @__PURE__ */ jsxs15(
2855
- Link,
2856
- {
2857
- href: f.href,
2858
- to: f.href,
2859
- onClick: onNavigate,
2860
- ...f.prefetch !== void 0 ? { prefetch: f.prefetch } : {},
2861
- className: cn(
2862
- "flex h-9 items-center gap-2.5 rounded-lg px-2.5 text-sm transition-colors",
2863
- MOTION_CONTROL,
2864
- active ? "bg-[var(--accent-surface-strong)] font-medium text-[var(--accent-text)]" : "text-muted-foreground hover:bg-[var(--accent-surface-soft)] hover:text-[var(--accent-text)]"
2865
- ),
2866
- children: [
2867
- /* @__PURE__ */ jsx18(FIcon, { className: "h-4 w-4 shrink-0" }),
2868
- /* @__PURE__ */ jsx18("span", { className: "truncate", children: f.label })
2869
- ]
2870
- },
2871
- f.id
2872
- );
2873
- })
2874
- },
2875
- item.id
2876
- );
2877
- }
2878
- return item.togglesPanel ? /* @__PURE__ */ jsx18(
2879
- RailButton,
2880
- {
2881
- icon: item.icon,
2882
- label: item.label,
2883
- badge: item.badge,
2884
- isActive: panelOpen,
2885
- onClick: () => {
2886
- togglePanel();
2887
- onNavigate?.();
2888
- },
2889
- showLabel: showLabels2,
2890
- ...arrival
2891
- },
2892
- item.id
2893
- ) : /* @__PURE__ */ jsx18(RailButton, { icon: item.icon, label: item.label, badge: item.badge, isActive: activeId === item.id, showLabel: showLabels2, variant: item.variant, asChild: true, ...arrival, children: /* @__PURE__ */ jsx18(
2894
- Link,
2895
- {
2896
- href: item.href,
2897
- to: item.href,
2898
- onClick: onNavigate,
2899
- ...item.prefetch !== void 0 ? { prefetch: item.prefetch } : {}
2900
- }
2901
- ) }, item.id);
2902
- });
2903
- }
2904
- return /* @__PURE__ */ jsxs15("div", { className: cn("min-h-screen bg-surface text-foreground", className), children: [
2905
- /* @__PURE__ */ jsx18("div", { className: cn(hideBelow && HIDE_BELOW_CLASS[hideBelow]), children: /* @__PURE__ */ jsxs15(Sidebar, { className: sidebarClassName, children: [
2906
- /* @__PURE__ */ jsxs15(SidebarRail, { children: [
2907
- (logo !== void 0 || railHeaderContent !== void 0 || railLabels) && /* @__PURE__ */ jsx18(
2908
- RailHeader,
2909
- {
2910
- brand: logo,
2911
- brandHref: logoHref,
2912
- collapsed: !showLabels,
2913
- onToggle: toggleRail,
2914
- collapsible: railLabels,
2915
- LinkComponent,
2916
- children: railHeaderContent
2917
- }
2918
- ),
2919
- /* @__PURE__ */ jsx18(SidebarRailNav, { className: cn(showLabels ? "px-2" : void 0, expandOnEmptyClick && "cursor-pointer"), onClick: expandOnEmptyClick, children: renderNavItems({ showLabels, onNavigate: handleNavClick }) }),
2920
- (railFooter !== void 0 || hasProfile) && /* @__PURE__ */ jsxs15(SidebarRailFooter, { className: cn("border-t border-[var(--md3-outline-variant)] pt-2", showLabels && "items-stretch px-2"), children: [
2921
- railFooter,
2922
- hasProfile && /* @__PURE__ */ jsx18(
2923
- ProfileAvatar,
2924
- {
2925
- user: user ?? void 0,
2926
- isLoading,
2927
- onLogout,
2928
- onSettingsClick,
2929
- settingsHref,
2930
- showDetails: showLabels,
2931
- appearance,
2932
- LinkComponent: Link,
2933
- children: profileMenuItems
2934
- }
2935
- )
2936
- ] })
2937
- ] }),
2938
- panel != null && /* @__PURE__ */ jsx18(SidebarPanel, { children: panel })
2939
- ] }) }),
2940
- /* @__PURE__ */ jsxs15(SidebarContent, { className: contentClassName, children: [
2941
- hideBelow && /* @__PURE__ */ jsxs15(
2942
- "header",
2943
- {
2944
- className: cn(
2945
- "flex h-14 shrink-0 items-center gap-1 border-b border-[var(--md3-outline-variant)] bg-surface px-2",
2946
- SHOW_BELOW_CLASS[hideBelow]
2947
- ),
2948
- children: [
2949
- /* @__PURE__ */ jsx18(
2950
- "button",
2951
- {
2952
- type: "button",
2953
- "aria-label": "Open navigation",
2954
- "aria-expanded": mobileNavOpen,
2955
- "aria-controls": "sidebar-mobile-nav",
2956
- "aria-haspopup": "dialog",
2957
- onClick: () => setMobileNavOpen(true),
2958
- className: cn("flex size-10 shrink-0 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-[var(--accent-surface-soft)] hover:text-foreground", MOTION_CONTROL),
2959
- children: /* @__PURE__ */ jsx18(MenuIcon2, { className: "size-5" })
2960
- }
2961
- ),
2962
- /* @__PURE__ */ jsx18("div", { className: "flex min-w-0 flex-1 items-center", children: railHeaderContent ?? (logo !== void 0 ? /* @__PURE__ */ jsx18(Link, { href: logoHref, to: logoHref, className: "flex min-w-0 items-center", children: logo }) : null) }),
2963
- hasProfile && /* @__PURE__ */ jsx18(
2964
- ProfileAvatar,
2965
- {
2966
- user: user ?? void 0,
2967
- isLoading,
2968
- onLogout,
2969
- onSettingsClick,
2970
- settingsHref,
2971
- appearance,
2972
- LinkComponent: Link,
2973
- children: profileMenuItems
2974
- }
2975
- )
2976
- ]
2977
- }
2978
- ),
2979
- children
2980
- ] }),
2981
- hideBelow && mobileNavOpen && /* @__PURE__ */ jsx18(
2982
- MobileNavDrawer,
2983
- {
2984
- breakpoint: hideBelow,
2985
- onClose: closeMobileNav,
2986
- header: railHeaderContent ?? logo,
2987
- footer: railFooter !== void 0 || hasProfile ? /* @__PURE__ */ jsxs15(Fragment10, { children: [
2988
- railFooter,
2989
- hasProfile && /* @__PURE__ */ jsx18(
2990
- ProfileAvatar,
2991
- {
2992
- user: user ?? void 0,
2993
- isLoading,
2994
- onLogout,
2995
- onSettingsClick,
2996
- settingsHref,
2997
- showDetails: true,
2998
- appearance,
2999
- LinkComponent: Link,
3000
- children: profileMenuItems
3001
- }
3002
- )
3003
- ] }) : null,
3004
- panel,
3005
- children: renderNavItems({
3006
- showLabels: true,
3007
- onNavigate: () => {
3008
- handleNavClick?.();
3009
- closeMobileNav();
3010
- }
3011
- })
3012
- }
3013
- )
3014
- ] });
3015
- }
3016
- function MobileNavDrawer({
3017
- breakpoint,
3018
- onClose,
3019
- header,
3020
- footer,
3021
- panel,
3022
- children
3023
- }) {
3024
- const panelRef = React10.useRef(null);
3025
- React10.useEffect(() => {
3026
- const onKeyDown = (event) => {
3027
- if (event.key === "Escape") {
3028
- event.stopPropagation();
3029
- onClose();
3030
- return;
3031
- }
3032
- if (event.key !== "Tab") return;
3033
- const root = panelRef.current;
3034
- if (!root) return;
3035
- const focusable = root.querySelectorAll(
3036
- 'a[href],button:not([disabled]),input:not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex="-1"])'
3037
- );
3038
- if (focusable.length === 0) return;
3039
- const first = focusable[0];
3040
- const last = focusable[focusable.length - 1];
3041
- if (event.shiftKey && document.activeElement === first) {
3042
- event.preventDefault();
3043
- last.focus();
3044
- } else if (!event.shiftKey && document.activeElement === last) {
3045
- event.preventDefault();
3046
- first.focus();
3047
- }
3048
- };
3049
- document.addEventListener("keydown", onKeyDown, true);
3050
- return () => document.removeEventListener("keydown", onKeyDown, true);
3051
- }, [onClose]);
3052
- React10.useEffect(() => {
3053
- const previous = document.body.style.overflow;
3054
- document.body.style.overflow = "hidden";
3055
- return () => {
3056
- document.body.style.overflow = previous;
3057
- };
3058
- }, []);
3059
- React10.useEffect(() => {
3060
- panelRef.current?.querySelector("button,a[href]")?.focus();
3061
- }, []);
3062
- return /* @__PURE__ */ jsxs15("div", { className: cn("fixed inset-0 z-50", SHOW_BELOW_CLASS[breakpoint]), children: [
3063
- /* @__PURE__ */ jsx18(
3064
- "button",
3065
- {
3066
- type: "button",
3067
- "aria-label": "Close navigation",
3068
- tabIndex: -1,
3069
- onClick: onClose,
3070
- className: "absolute inset-0 bg-black/50"
3071
- }
3072
- ),
3073
- /* @__PURE__ */ jsxs15(
3074
- "div",
3075
- {
3076
- ref: panelRef,
3077
- id: "sidebar-mobile-nav",
3078
- role: "dialog",
3079
- "aria-modal": "true",
3080
- "aria-label": "Navigation",
3081
- style: { width: SIDEBAR_MOBILE_WIDTH },
3082
- className: "agent-arrive absolute inset-y-0 left-0 flex max-w-[85vw] flex-col border-r border-[var(--md3-outline-variant)] bg-surface-container-low shadow-xl",
3083
- children: [
3084
- /* @__PURE__ */ jsxs15("div", { className: "flex h-14 shrink-0 items-center gap-1 border-b border-[var(--md3-outline-variant)] px-2", children: [
3085
- /* @__PURE__ */ jsx18("div", { className: "flex min-w-0 flex-1 items-center", children: header }),
3086
- /* @__PURE__ */ jsx18(
3087
- "button",
3088
- {
3089
- type: "button",
3090
- "aria-label": "Close navigation",
3091
- onClick: onClose,
3092
- className: cn("flex size-9 shrink-0 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-[var(--accent-surface-soft)] hover:text-foreground", MOTION_CONTROL),
3093
- children: /* @__PURE__ */ jsx18(CloseIcon, { className: "size-5" })
3094
- }
3095
- )
3096
- ] }),
3097
- /* @__PURE__ */ jsxs15("nav", { "aria-label": "Sections", className: "flex min-h-0 flex-1 flex-col gap-0.5 overflow-y-auto px-2 py-2", children: [
3098
- children,
3099
- panel != null && /* @__PURE__ */ jsx18("div", { className: "mt-2 border-t border-[var(--md3-outline-variant)] pt-2", children: panel })
3100
- ] }),
3101
- footer != null && /* @__PURE__ */ jsx18("div", { className: "shrink-0 border-t border-[var(--md3-outline-variant)] px-2 py-2", children: footer })
3102
- ]
3103
- }
3104
- )
3105
- ] });
3106
- }
3107
- function SidebarLayout({
3108
- panelOpen,
3109
- onPanelOpenChange,
3110
- defaultPanelOpen,
3111
- railCollapsed,
3112
- onRailCollapsedChange,
3113
- defaultRailCollapsed,
3114
- ...props
3115
- }) {
3116
- return /* @__PURE__ */ jsx18(
3117
- SidebarProvider,
3118
- {
3119
- panelOpen,
3120
- onPanelOpenChange,
3121
- defaultPanelOpen,
3122
- railCollapsed,
3123
- onRailCollapsedChange,
3124
- defaultRailCollapsed,
3125
- hasPanels: props.panel != null,
3126
- labeledRail: props.railLabels ?? false,
3127
- children: /* @__PURE__ */ jsx18(SidebarLayoutInner, { ...props })
3128
- }
3129
- );
3130
- }
3131
-
3132
- // src/dashboard/profile-selector.tsx
3133
- import { Check as Check3, ChevronDown as ChevronDown2, Plus as Plus3, Settings } from "lucide-react";
3134
- import { Button as Button2 } from "@tangle-network/ui/primitives";
3135
- import { Badge } from "@tangle-network/ui/primitives";
3136
- import {
3137
- DropdownMenu as DropdownMenu4,
3138
- DropdownMenuContent as DropdownMenuContent4,
3139
- DropdownMenuItem as DropdownMenuItem4,
3140
- DropdownMenuLabel as DropdownMenuLabel2,
3141
- DropdownMenuSeparator as DropdownMenuSeparator4,
3142
- DropdownMenuTrigger as DropdownMenuTrigger4
3143
- } from "@tangle-network/ui/primitives";
3144
- import { Fragment as Fragment11, jsx as jsx19, jsxs as jsxs16 } from "react/jsx-runtime";
3145
- function ProfileSelector({
3146
- profiles,
3147
- selectedId,
3148
- onSelect,
3149
- onCreateClick,
3150
- onManageClick,
3151
- label = "Profile",
3152
- placeholder = "Default (no custom profile)",
3153
- showMetrics = true,
3154
- className
3155
- }) {
3156
- const selected = profiles.find((p) => p.id === selectedId);
3157
- const builtinProfiles = profiles.filter((p) => p.is_builtin);
3158
- const customProfiles = profiles.filter((p) => !p.is_builtin);
3159
- return /* @__PURE__ */ jsxs16("div", { className, children: [
3160
- label && /* @__PURE__ */ jsx19("label", { className: "mb-2 block font-medium text-sm", children: label }),
3161
- /* @__PURE__ */ jsxs16(DropdownMenu4, { children: [
3162
- /* @__PURE__ */ jsx19(DropdownMenuTrigger4, { asChild: true, children: /* @__PURE__ */ jsxs16(Button2, { variant: "outline", className: "w-full justify-between", children: [
3163
- /* @__PURE__ */ jsx19("span", { className: "truncate", children: selected ? selected.name : placeholder }),
3164
- /* @__PURE__ */ jsx19(ChevronDown2, { className: "ml-2 h-4 w-4 shrink-0 opacity-50" })
3165
- ] }) }),
3166
- /* @__PURE__ */ jsxs16(DropdownMenuContent4, { className: "w-[300px]", align: "start", children: [
3167
- /* @__PURE__ */ jsxs16(
3168
- DropdownMenuItem4,
3169
- {
3170
- onClick: () => onSelect(null),
3171
- className: "flex items-center justify-between",
3172
- children: [
3173
- /* @__PURE__ */ jsx19("span", { children: placeholder }),
3174
- !selectedId && /* @__PURE__ */ jsx19(Check3, { className: "h-4 w-4 text-[var(--surface-success-text)]" })
3175
- ]
3176
- }
3177
- ),
3178
- builtinProfiles.length > 0 && /* @__PURE__ */ jsxs16(Fragment11, { children: [
3179
- /* @__PURE__ */ jsx19(DropdownMenuSeparator4, {}),
3180
- /* @__PURE__ */ jsx19(DropdownMenuLabel2, { children: "Built-in Profiles" }),
3181
- builtinProfiles.map((profile) => /* @__PURE__ */ jsxs16(
3182
- DropdownMenuItem4,
3183
- {
3184
- onClick: () => onSelect(profile),
3185
- className: "flex flex-col items-start gap-1",
3186
- children: [
3187
- /* @__PURE__ */ jsxs16("div", { className: "flex w-full items-center justify-between", children: [
3188
- /* @__PURE__ */ jsxs16("div", { className: "flex items-center gap-2", children: [
3189
- /* @__PURE__ */ jsx19("span", { className: "font-medium", children: profile.name }),
3190
- profile.extends && /* @__PURE__ */ jsxs16(Badge, { variant: "secondary", className: "border-0 text-xs", children: [
3191
- "extends ",
3192
- profile.extends
3193
- ] })
3194
- ] }),
3195
- selectedId === profile.id && /* @__PURE__ */ jsx19(Check3, { className: "h-4 w-4 text-[var(--surface-success-text)]" })
3196
- ] }),
3197
- profile.description && /* @__PURE__ */ jsx19("span", { className: "line-clamp-1 text-muted-foreground text-xs", children: profile.description })
3198
- ]
3199
- },
3200
- profile.id
3201
- ))
3202
- ] }),
3203
- customProfiles.length > 0 && /* @__PURE__ */ jsxs16(Fragment11, { children: [
3204
- /* @__PURE__ */ jsx19(DropdownMenuSeparator4, {}),
3205
- /* @__PURE__ */ jsx19(DropdownMenuLabel2, { children: "Custom Profiles" }),
3206
- customProfiles.map((profile) => /* @__PURE__ */ jsxs16(
3207
- DropdownMenuItem4,
3208
- {
3209
- onClick: () => onSelect(profile),
3210
- className: "flex flex-col items-start gap-1",
3211
- children: [
3212
- /* @__PURE__ */ jsxs16("div", { className: "flex w-full items-center justify-between", children: [
3213
- /* @__PURE__ */ jsxs16("div", { className: "flex items-center gap-2", children: [
3214
- /* @__PURE__ */ jsx19("span", { className: "font-medium", children: profile.name }),
3215
- profile.model && /* @__PURE__ */ jsx19(Badge, { variant: "secondary", className: "border-0 text-xs", children: profile.model.split("/").pop() })
3216
- ] }),
3217
- selectedId === profile.id && /* @__PURE__ */ jsx19(Check3, { className: "h-4 w-4 text-[var(--surface-success-text)]" })
3218
- ] }),
3219
- profile.description && /* @__PURE__ */ jsx19("span", { className: "line-clamp-1 text-muted-foreground text-xs", children: profile.description }),
3220
- showMetrics && profile.metrics && profile.metrics.total_runs > 0 && /* @__PURE__ */ jsxs16("div", { className: "flex gap-3 text-muted-foreground text-xs", children: [
3221
- /* @__PURE__ */ jsxs16("span", { children: [
3222
- profile.metrics.total_runs,
3223
- " runs"
3224
- ] }),
3225
- /* @__PURE__ */ jsxs16("span", { children: [
3226
- profile.metrics.success_rate.toFixed(0),
3227
- "% success"
3228
- ] }),
3229
- /* @__PURE__ */ jsxs16("span", { children: [
3230
- "~",
3231
- (profile.metrics.avg_duration_ms / 1e3).toFixed(1),
3232
- "s avg"
3233
- ] })
3234
- ] })
3235
- ]
3236
- },
3237
- profile.id
3238
- ))
3239
- ] }),
3240
- (onCreateClick || onManageClick) && /* @__PURE__ */ jsxs16(Fragment11, { children: [
3241
- /* @__PURE__ */ jsx19(DropdownMenuSeparator4, {}),
3242
- onCreateClick && /* @__PURE__ */ jsxs16(
3243
- DropdownMenuItem4,
3244
- {
3245
- onClick: onCreateClick,
3246
- className: "text-[var(--surface-info-text)]",
3247
- children: [
3248
- /* @__PURE__ */ jsx19(Plus3, { className: "mr-2 h-4 w-4" }),
3249
- "Create New Profile"
3250
- ]
3251
- }
3252
- ),
3253
- onManageClick && /* @__PURE__ */ jsxs16(
3254
- DropdownMenuItem4,
3255
- {
3256
- onClick: onManageClick,
3257
- className: "text-muted-foreground",
3258
- children: [
3259
- /* @__PURE__ */ jsx19(Settings, { className: "mr-2 h-4 w-4" }),
3260
- "Manage Profiles"
3261
- ]
3262
- }
3263
- )
3264
- ] })
3265
- ] })
3266
- ] })
3267
- ] });
3268
- }
3269
- function ProfileComparison({
3270
- profiles,
3271
- className
3272
- }) {
3273
- const profilesWithMetrics = profiles.filter(
3274
- (p) => p.metrics && p.metrics.total_runs > 0
3275
- );
3276
- if (profilesWithMetrics.length === 0) {
3277
- return null;
3278
- }
3279
- const bestSuccess = profilesWithMetrics.reduce(
3280
- (best, p) => (p.metrics?.success_rate ?? 0) > (best.metrics?.success_rate ?? 0) ? p : best
3281
- );
3282
- const fastestProfile = profilesWithMetrics.reduce(
3283
- (best, p) => (p.metrics?.avg_duration_ms ?? Number.POSITIVE_INFINITY) < (best.metrics?.avg_duration_ms ?? Number.POSITIVE_INFINITY) ? p : best
3284
- );
3285
- return /* @__PURE__ */ jsxs16("div", { className: `rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container p-4 ${className ?? ""}`, children: [
3286
- /* @__PURE__ */ jsx19("h4", { className: "mb-3 font-medium text-sm", children: "Profile Performance" }),
3287
- /* @__PURE__ */ jsx19("div", { className: "space-y-3", children: profilesWithMetrics.map((profile) => {
3288
- const isBestSuccess = profile.id === bestSuccess.id;
3289
- const isFastest = profile.id === fastestProfile.id;
3290
- return /* @__PURE__ */ jsxs16(
3291
- "div",
3292
- {
3293
- className: "flex items-center justify-between gap-4",
3294
- children: [
3295
- /* @__PURE__ */ jsxs16("div", { className: "flex items-center gap-2", children: [
3296
- /* @__PURE__ */ jsx19("span", { className: "font-medium", children: profile.name }),
3297
- isBestSuccess && /* @__PURE__ */ jsx19(Badge, { className: "border border-[var(--surface-success-border)] bg-[var(--surface-success-bg)] text-[var(--surface-success-text)] text-xs", children: "Best Success" }),
3298
- isFastest && !isBestSuccess && /* @__PURE__ */ jsx19(Badge, { className: "border border-[var(--surface-info-border)] bg-[var(--surface-info-bg)] text-[var(--surface-info-text)] text-xs", children: "Fastest" })
3299
- ] }),
3300
- /* @__PURE__ */ jsxs16("div", { className: "flex items-center gap-4 text-sm", children: [
3301
- /* @__PURE__ */ jsxs16("span", { children: [
3302
- /* @__PURE__ */ jsx19("span", { className: "text-muted-foreground", children: "Success:" }),
3303
- " ",
3304
- /* @__PURE__ */ jsxs16(
3305
- "span",
3306
- {
3307
- 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)]",
3308
- children: [
3309
- profile.metrics?.success_rate.toFixed(0),
3310
- "%"
3311
- ]
3312
- }
3313
- )
3314
- ] }),
3315
- /* @__PURE__ */ jsxs16("span", { children: [
3316
- /* @__PURE__ */ jsx19("span", { className: "text-muted-foreground", children: "Avg:" }),
3317
- " ",
3318
- ((profile.metrics?.avg_duration_ms ?? 0) / 1e3).toFixed(1),
3319
- "s"
3320
- ] }),
3321
- /* @__PURE__ */ jsxs16("span", { className: "text-muted-foreground", children: [
3322
- profile.metrics?.total_runs,
3323
- " runs"
3324
- ] })
3325
- ] })
3326
- ]
3327
- },
3328
- profile.id
3329
- );
3330
- }) })
3331
- ] });
3332
- }
3333
-
3334
- // src/dashboard/metric-area-chart.tsx
3335
- import * as React11 from "react";
3336
- import { jsx as jsx20, jsxs as jsxs17 } from "react/jsx-runtime";
3337
- var TONE_VARS = {
3338
- primary: "var(--brand-primary, hsl(var(--primary)))",
3339
- success: "var(--status-running, #22c55e)",
3340
- warning: "var(--status-creating, #eab308)",
3341
- danger: "var(--status-error, #ef4444)"
3342
- };
3343
- function buildSegments(points, width, height, yMax) {
3344
- if (points.length === 0 || yMax <= 0) return [];
3345
- const t0 = points[0].at;
3346
- const t1 = points[points.length - 1].at;
3347
- const span = Math.max(1, t1 - t0);
3348
- const x = (at) => points.length === 1 ? width / 2 : (at - t0) / span * width;
3349
- const y = (v) => height - Math.min(1, Math.max(0, v / yMax)) * height;
3350
- const segments = [];
3351
- let run = [];
3352
- const flush = () => {
3353
- if (run.length === 0) return;
3354
- const line = run.map((p, i) => `${i === 0 ? "M" : "L"}${p.px.toFixed(2)},${p.py.toFixed(2)}`).join(" ");
3355
- const first = run[0];
3356
- const last = run[run.length - 1];
3357
- const area = `${line} L${last.px.toFixed(2)},${height} L${first.px.toFixed(2)},${height} Z`;
3358
- segments.push({ path: line, area });
3359
- run = [];
3360
- };
3361
- for (const point of points) {
3362
- if (point.value === null) {
3363
- flush();
3364
- continue;
3365
- }
3366
- run.push({ px: x(point.at), py: y(point.value) });
3367
- }
3368
- flush();
3369
- return segments;
3370
- }
3371
- function MetricAreaChart({
3372
- points,
3373
- label,
3374
- formatValue,
3375
- maxValue,
3376
- detail,
3377
- tone = "primary",
3378
- height = 96,
3379
- emptyState,
3380
- className
3381
- }) {
3382
- const gradientId = React11.useId();
3383
- const width = 400;
3384
- const values = points.map((p) => p.value).filter((v) => v !== null);
3385
- const latest = values.length > 0 ? values[values.length - 1] : null;
3386
- const observedMax = values.length > 0 ? Math.max(...values) : 0;
3387
- const yMax = maxValue ?? (observedMax > 0 ? observedMax * 1.1 : 1);
3388
- const segments = buildSegments(points, width, height, yMax);
3389
- const color = TONE_VARS[tone];
3390
- return /* @__PURE__ */ jsxs17(
3391
- "div",
3392
- {
3393
- className: cn(
3394
- "rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container p-4 shadow-sm",
3395
- className
3396
- ),
3397
- "data-testid": `metric-chart-${label.toLowerCase().replace(/\s+/g, "-")}`,
3398
- children: [
3399
- /* @__PURE__ */ jsxs17("div", { className: "flex items-baseline justify-between gap-3", children: [
3400
- /* @__PURE__ */ jsx20("p", { className: "font-medium text-muted-foreground text-xs uppercase tracking-[0.12em]", children: label }),
3401
- /* @__PURE__ */ jsxs17("div", { className: "text-right", children: [
3402
- /* @__PURE__ */ jsx20("span", { className: "font-semibold text-foreground text-xl tabular-nums tracking-tight", children: latest === null ? "\u2014" : formatValue(latest) }),
3403
- detail && /* @__PURE__ */ jsx20("span", { className: "ml-1.5 text-muted-foreground text-xs", children: detail })
3404
- ] })
3405
- ] }),
3406
- /* @__PURE__ */ jsx20("div", { className: "mt-3", children: values.length === 0 ? /* @__PURE__ */ jsx20(
3407
- "div",
3408
- {
3409
- className: "flex items-center justify-center text-muted-foreground text-xs",
3410
- style: { height },
3411
- children: emptyState ?? "Waiting for samples\u2026"
3412
- }
3413
- ) : /* @__PURE__ */ jsxs17(
3414
- "svg",
3415
- {
3416
- viewBox: `0 0 ${width} ${height}`,
3417
- preserveAspectRatio: "none",
3418
- className: "block w-full",
3419
- style: { height },
3420
- role: "img",
3421
- "aria-label": `${label} chart`,
3422
- children: [
3423
- [0.25, 0.5, 0.75].map((fraction) => /* @__PURE__ */ jsx20(
3424
- "line",
3425
- {
3426
- x1: 0,
3427
- x2: width,
3428
- y1: height * fraction,
3429
- y2: height * fraction,
3430
- stroke: "currentColor",
3431
- className: "text-border",
3432
- strokeWidth: 1,
3433
- strokeDasharray: "3 5",
3434
- vectorEffect: "non-scaling-stroke"
3435
- },
3436
- fraction
3437
- )),
3438
- /* @__PURE__ */ jsx20("defs", { children: /* @__PURE__ */ jsxs17("linearGradient", { id: gradientId, x1: "0", y1: "0", x2: "0", y2: "1", children: [
3439
- /* @__PURE__ */ jsx20("stop", { offset: "0%", stopColor: color, stopOpacity: 0.28 }),
3440
- /* @__PURE__ */ jsx20("stop", { offset: "100%", stopColor: color, stopOpacity: 0.02 })
3441
- ] }) }),
3442
- segments.map((segment) => /* @__PURE__ */ jsxs17(React11.Fragment, { children: [
3443
- /* @__PURE__ */ jsx20("path", { d: segment.area, fill: `url(#${gradientId})` }),
3444
- /* @__PURE__ */ jsx20(
3445
- "path",
3446
- {
3447
- d: segment.path,
3448
- fill: "none",
3449
- stroke: color,
3450
- strokeWidth: 1.75,
3451
- vectorEffect: "non-scaling-stroke",
3452
- strokeLinejoin: "round",
3453
- strokeLinecap: "round"
3454
- }
3455
- )
3456
- ] }, segment.path))
3457
- ]
3458
- }
3459
- ) }),
3460
- /* @__PURE__ */ jsxs17("div", { className: "mt-1.5 flex justify-between text-[10px] text-muted-foreground tabular-nums", children: [
3461
- /* @__PURE__ */ jsx20("span", { children: values.length > 0 ? formatValue(0) : "" }),
3462
- /* @__PURE__ */ jsx20("span", { children: values.length > 0 ? formatValue(yMax) : "" })
3463
- ] })
3464
- ]
3465
- }
3466
- );
3467
- }
3468
-
3469
- // src/dashboard/variant-list.tsx
3470
- import {
3471
- Check as Check4,
3472
- CheckCircle2,
3473
- Clock as Clock3,
3474
- ExternalLink as ExternalLink2,
3475
- Loader2,
3476
- Timer,
3477
- X,
3478
- XCircle
3479
- } from "lucide-react";
3480
- import { Button as Button3 } from "@tangle-network/ui/primitives";
3481
- import { Badge as Badge2 } from "@tangle-network/ui/primitives";
3482
- import { Fragment as Fragment13, jsx as jsx21, jsxs as jsxs18 } from "react/jsx-runtime";
3483
- var statusConfig = {
3484
- pending: {
3485
- icon: Clock3,
3486
- color: "text-[var(--surface-warning-text)]",
3487
- bg: "bg-[var(--surface-warning-bg)]",
3488
- border: "border-[var(--surface-warning-border)]",
3489
- label: "Pending",
3490
- animate: false
3491
- },
3492
- running: {
3493
- icon: Loader2,
3494
- color: "text-primary",
3495
- bg: "bg-[var(--accent-surface-soft)]",
3496
- border: "border-[var(--md3-outline-variant)]",
3497
- label: "Running",
3498
- animate: true
3499
- },
3500
- completed: {
3501
- icon: CheckCircle2,
3502
- color: "text-[var(--surface-success-text)]",
3503
- bg: "bg-[var(--surface-success-bg)]",
3504
- border: "border-[var(--surface-success-border)]",
3505
- label: "Completed",
3506
- animate: false
3507
- },
3508
- failed: {
3509
- icon: XCircle,
3510
- color: "text-[var(--surface-danger-text)]",
3511
- bg: "bg-[var(--surface-danger-bg)]",
3512
- border: "border-[var(--surface-danger-border)]",
3513
- label: "Failed",
3514
- animate: false
3515
- },
3516
- cancelled: {
3517
- icon: XCircle,
3518
- color: "text-[var(--surface-neutral-text)]",
3519
- bg: "bg-[var(--surface-neutral-bg)]",
3520
- border: "border-[var(--surface-neutral-border)]",
3521
- label: "Cancelled",
3522
- animate: false
3523
- }
3524
- };
3525
- var outcomeConfig = {
3526
- pending_review: {
3527
- color: "text-[var(--surface-warning-text)]",
3528
- bg: "bg-[var(--surface-warning-bg)]",
3529
- border: "border-[var(--surface-warning-border)]",
3530
- label: "Pending Review"
3531
- },
3532
- accepted: {
3533
- color: "text-[var(--surface-success-text)]",
3534
- bg: "bg-[var(--surface-success-bg)]",
3535
- border: "border-[var(--surface-success-border)]",
3536
- label: "Accepted"
3537
- },
3538
- rejected: {
3539
- color: "text-[var(--surface-danger-text)]",
3540
- bg: "bg-[var(--surface-danger-bg)]",
3541
- border: "border-[var(--surface-danger-border)]",
3542
- label: "Rejected"
3543
- },
3544
- merged_with_conflicts: {
3545
- color: "text-[var(--surface-orange-text)]",
3546
- bg: "bg-[var(--surface-orange-bg)]",
3547
- border: "border-[var(--surface-orange-border)]",
3548
- label: "Merged (conflicts)"
3549
- },
3550
- expired: {
3551
- color: "text-[var(--surface-neutral-text)]",
3552
- bg: "bg-[var(--surface-neutral-bg)]",
3553
- border: "border-[var(--surface-neutral-border)]",
3554
- label: "Expired"
3555
- }
3556
- };
3557
- function VariantList({
3558
- variants,
3559
- selectedId,
3560
- onSelect,
3561
- onAccept,
3562
- onReject,
3563
- isActioning,
3564
- className
3565
- }) {
3566
- return /* @__PURE__ */ jsx21("div", { className: `space-y-2 ${className || ""}`, children: variants.map((variant) => {
3567
- const status = statusConfig[variant.status];
3568
- const StatusIcon = status.icon;
3569
- const isSelected = variant.id === selectedId;
3570
- return /* @__PURE__ */ jsxs18(
3571
- "div",
3572
- {
3573
- className: `cursor-pointer rounded-lg border px-3 py-2.5 transition-colors ${isSelected ? "border-primary/30 bg-[var(--accent-surface-soft)]" : "border-[var(--md3-outline-variant)] bg-surface-container hover:border-primary/20 hover:bg-surface-container-high"}`,
3574
- onClick: () => onSelect?.(variant.id),
3575
- children: [
3576
- /* @__PURE__ */ jsxs18("div", { className: "flex items-center gap-2", children: [
3577
- /* @__PURE__ */ jsxs18(Badge2, { className: `shrink-0 ${status.bg} ${status.border} ${status.color}`, children: [
3578
- /* @__PURE__ */ jsx21(
3579
- StatusIcon,
3580
- {
3581
- className: `mr-1 h-3 w-3 ${status.animate ? "animate-spin" : ""}`,
3582
- ...status.animate ? { "data-motion": "essential" } : {}
3583
- }
3584
- ),
3585
- status.label
3586
- ] }),
3587
- /* @__PURE__ */ jsx21("span", { className: "truncate text-sm font-medium text-foreground", children: variant.label }),
3588
- variant.sublabel && /* @__PURE__ */ jsxs18("span", { className: "shrink-0 text-xs text-muted-foreground", children: [
3589
- "(",
3590
- variant.sublabel,
3591
- ")"
3592
- ] }),
3593
- variant.durationMs && /* @__PURE__ */ jsxs18("span", { className: "flex shrink-0 items-center gap-1 text-xs text-muted-foreground", children: [
3594
- /* @__PURE__ */ jsx21(Timer, { className: "h-3 w-3" }),
3595
- (variant.durationMs / 1e3).toFixed(1),
3596
- "s"
3597
- ] }),
3598
- /* @__PURE__ */ jsxs18("div", { className: "ml-auto flex shrink-0 items-center gap-1.5", children: [
3599
- variant.outcome && /* @__PURE__ */ jsx21(
3600
- Badge2,
3601
- {
3602
- className: `${outcomeConfig[variant.outcome].bg} ${outcomeConfig[variant.outcome].border} ${outcomeConfig[variant.outcome].color}`,
3603
- children: outcomeConfig[variant.outcome].label
3604
- }
3605
- ),
3606
- variant.status === "completed" && variant.outcome === "pending_review" && onAccept && onReject && /* @__PURE__ */ jsxs18(Fragment13, { children: [
3607
- /* @__PURE__ */ jsxs18(
3608
- Button3,
3609
- {
3610
- variant: "outline",
3611
- size: "sm",
3612
- 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)]",
3613
- onClick: (e) => {
3614
- e.stopPropagation();
3615
- onAccept(variant.id);
3616
- },
3617
- disabled: isActioning === variant.id,
3618
- children: [
3619
- /* @__PURE__ */ jsx21(Check4, { className: "mr-1 h-3 w-3" }),
3620
- "Accept"
3621
- ]
3622
- }
3623
- ),
3624
- /* @__PURE__ */ jsxs18(
3625
- Button3,
3626
- {
3627
- variant: "outline",
3628
- size: "sm",
3629
- 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)]",
3630
- onClick: (e) => {
3631
- e.stopPropagation();
3632
- onReject(variant.id);
3633
- },
3634
- disabled: isActioning === variant.id,
3635
- children: [
3636
- /* @__PURE__ */ jsx21(X, { className: "mr-1 h-3 w-3" }),
3637
- "Reject"
3638
- ]
3639
- }
3640
- )
3641
- ] }),
3642
- variant.detailsUrl && /* @__PURE__ */ jsx21(
3643
- Button3,
3644
- {
3645
- variant: "ghost",
3646
- size: "sm",
3647
- className: "h-7 w-7 p-0 text-muted-foreground hover:text-foreground",
3648
- onClick: (e) => {
3649
- e.stopPropagation();
3650
- window.open(variant.detailsUrl, "_blank");
3651
- },
3652
- children: /* @__PURE__ */ jsx21(ExternalLink2, { className: "h-3.5 w-3.5" })
3653
- }
3654
- )
3655
- ] })
3656
- ] }),
3657
- variant.error && /* @__PURE__ */ jsx21("p", { className: "mt-1.5 text-xs text-[var(--surface-danger-text)]", children: variant.error }),
3658
- variant.summary && /* @__PURE__ */ jsx21("p", { className: "mt-1.5 line-clamp-2 text-xs text-muted-foreground", children: variant.summary })
3659
- ]
3660
- },
3661
- variant.id
3662
- );
3663
- }) });
3664
- }
3665
-
3666
- // src/dashboard/system-logs.tsx
3667
- import { Terminal as Terminal3 } from "lucide-react";
3668
- import { useEffect as useEffect6, useRef as useRef5, useState as useState7 } from "react";
3669
- import { jsx as jsx22, jsxs as jsxs19 } from "react/jsx-runtime";
3670
- function SystemLogsViewer({ apiUrl, token, className }) {
3671
- const [logs, setLogs] = useState7([]);
3672
- const [error, setError] = useState7(null);
3673
- const [isFollowing, setIsFollowing] = useState7(true);
3674
- const scrollRef = useRef5(null);
3675
- useEffect6(() => {
3676
- let timeoutId;
3677
- let backoff = 2e3;
3678
- const controller = new AbortController();
3679
- async function fetchLogs() {
3680
- try {
3681
- const res = await fetch(`${apiUrl}/logs`, {
3682
- headers: {
3683
- Authorization: `Bearer ${token}`
3684
- },
3685
- signal: controller.signal
3686
- });
3687
- if (!res.ok) throw new Error(`HTTP Error: ${res.status}`);
3688
- const data = await res.json();
3689
- if (!controller.signal.aborted) {
3690
- setLogs(data.logs || []);
3691
- setError(null);
3692
- backoff = 2e3;
3693
- }
3694
- } catch (err) {
3695
- if (err.name === "AbortError") return;
3696
- if (!controller.signal.aborted) {
3697
- setError(err instanceof Error ? err.message : "Failed to fetch logs");
3698
- backoff = Math.min(backoff * 2, 3e4);
3699
- }
3700
- }
3701
- if (!controller.signal.aborted) {
3702
- timeoutId = setTimeout(fetchLogs, backoff);
3703
- }
3704
- }
3705
- fetchLogs();
3706
- return () => {
3707
- controller.abort();
3708
- clearTimeout(timeoutId);
3709
- };
3710
- }, [apiUrl, token]);
3711
- useEffect6(() => {
3712
- if (isFollowing && scrollRef.current) {
3713
- scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
3714
- }
3715
- }, [logs, isFollowing]);
3716
- const handleScroll = () => {
3717
- if (!scrollRef.current) return;
3718
- const { scrollTop, scrollHeight, clientHeight } = scrollRef.current;
3719
- const isAtBottom = scrollHeight - scrollTop - clientHeight < 20;
3720
- setIsFollowing((prev) => prev === isAtBottom ? prev : isAtBottom);
3721
- };
3722
- return /* @__PURE__ */ jsxs19("div", { className: cn("flex flex-col h-full bg-surface-dim text-foreground font-mono text-sm leading-relaxed overflow-hidden rounded-lg border border-[var(--md3-outline-variant)]", className), children: [
3723
- /* @__PURE__ */ jsxs19("div", { className: "flex-none flex items-center justify-between border-b border-[var(--md3-outline-variant)] bg-surface-container-high backdrop-blur-md px-4 py-2", children: [
3724
- /* @__PURE__ */ jsxs19("div", { className: "flex items-center gap-2", children: [
3725
- /* @__PURE__ */ jsx22(Terminal3, { className: "h-4 w-4 text-primary animate-pulse" }),
3726
- /* @__PURE__ */ jsx22("span", { className: "font-bold text-xs uppercase tracking-widest text-muted-foreground", children: "System Traces" })
3727
- ] }),
3728
- /* @__PURE__ */ jsxs19("div", { className: "flex items-center gap-3", children: [
3729
- error && /* @__PURE__ */ jsxs19("span", { className: "text-destructive text-xs flex items-center gap-1", children: [
3730
- /* @__PURE__ */ jsx22("span", { className: "w-2 h-2 rounded-full bg-destructive animate-ping" }),
3731
- " Error fetching logs"
3732
- ] }),
3733
- /* @__PURE__ */ jsx22(
3734
- "button",
3735
- {
3736
- onClick: () => {
3737
- setIsFollowing(!isFollowing);
3738
- if (!isFollowing && scrollRef.current) {
3739
- scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
3740
- }
3741
- },
3742
- 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-surface-container-high text-muted-foreground border border-[var(--md3-outline-variant)] hover:bg-accent hover:text-foreground"),
3743
- children: isFollowing ? "Auto-Scroll ON" : "Auto-Scroll OFF"
3744
- }
3745
- )
3746
- ] })
3747
- ] }),
3748
- /* @__PURE__ */ jsx22(
3749
- "div",
3750
- {
3751
- ref: scrollRef,
3752
- onScroll: handleScroll,
3753
- className: "flex-1 overflow-y-auto p-4 space-y-1",
3754
- children: logs.length === 0 && !error ? /* @__PURE__ */ jsx22("div", { className: "flex h-full items-center justify-center text-muted-foreground italic", children: "Waiting for orchestrator logs..." }) : logs.map((log, i) => /* @__PURE__ */ jsxs19("div", { className: "break-words", children: [
3755
- /* @__PURE__ */ jsxs19("span", { className: "text-muted-foreground mr-3 select-none", children: [
3756
- "[",
3757
- log.timestamp || i.toString().padStart(4, "0"),
3758
- "]"
3759
- ] }),
3760
- /* @__PURE__ */ jsxs19("span", { className: "text-primary/70 mr-2", children: [
3761
- "[",
3762
- log.level,
3763
- "]"
3764
- ] }),
3765
- /* @__PURE__ */ jsxs19("span", { className: "text-muted-foreground mr-2", children: [
3766
- "[",
3767
- log.scope,
3768
- "]"
3769
- ] }),
3770
- /* @__PURE__ */ jsx22("span", { className: log.level.toUpperCase() === "ERROR" || log.message.toLowerCase().includes("failed") ? "text-destructive" : log.level.toUpperCase() === "WARN" ? "text-warning" : "text-foreground", children: log.message })
3771
- ] }, `${log.timestamp}-${log.scope}-${i}`))
3772
- }
3773
- )
3774
- ] });
3775
- }
3776
-
3777
- // src/dashboard/usage-summary.tsx
3778
- import { Clock as Clock4, Layers, MessageSquare, DollarSign } from "lucide-react";
3779
- import { StatCard } from "@tangle-network/ui/primitives";
3780
- import { Skeleton as Skeleton4 } from "@tangle-network/ui/primitives";
3781
- import { jsx as jsx23, jsxs as jsxs20 } from "react/jsx-runtime";
3782
- function UsageSummary({ data, loading = false, className }) {
3783
- if (loading || !data) {
3784
- return /* @__PURE__ */ jsx23("div", { className: cn("grid grid-cols-2 gap-4 lg:grid-cols-4", className), children: Array.from({ length: 4 }).map((_, i) => /* @__PURE__ */ jsx23(Skeleton4, { className: "h-28 rounded-xl" }, i)) });
3785
- }
3786
- return /* @__PURE__ */ jsxs20("div", { className: cn("grid grid-cols-2 gap-4 lg:grid-cols-4", className), children: [
3787
- /* @__PURE__ */ jsx23(
3788
- StatCard,
3789
- {
3790
- variant: "sandbox",
3791
- title: "Compute Hours",
3792
- value: data.computeHours.toFixed(1),
3793
- subtitle: "This billing period",
3794
- icon: /* @__PURE__ */ jsx23(Clock4, { className: "h-5 w-5" })
3795
- }
3796
- ),
3797
- /* @__PURE__ */ jsx23(
3798
- StatCard,
3799
- {
3800
- variant: "sandbox",
3801
- title: "Active Sessions",
3802
- value: data.activeSessions,
3803
- subtitle: "Currently running",
3804
- icon: /* @__PURE__ */ jsx23(Layers, { className: "h-5 w-5" })
3805
- }
3806
- ),
3807
- /* @__PURE__ */ jsx23(
3808
- StatCard,
3809
- {
3810
- variant: "sandbox",
3811
- title: "Messages Sent",
3812
- value: data.messagesSent.toLocaleString(),
3813
- subtitle: "Agent interactions",
3814
- icon: /* @__PURE__ */ jsx23(MessageSquare, { className: "h-5 w-5" })
3815
- }
3816
- ),
3817
- /* @__PURE__ */ jsx23(
3818
- StatCard,
3819
- {
3820
- variant: "sandbox",
3821
- title: "Estimated Cost",
3822
- value: `$${data.estimatedCost.toFixed(2)}`,
3823
- subtitle: "This billing period",
3824
- icon: /* @__PURE__ */ jsx23(DollarSign, { className: "h-5 w-5" })
3825
- }
3826
- )
3827
- ] });
3828
- }
3829
-
3830
- // src/dashboard/git-panel.tsx
3831
- import { GitBranch, GitCommit, FileEdit, FilePlus, File } from "lucide-react";
3832
- import { jsx as jsx24, jsxs as jsxs21 } from "react/jsx-runtime";
3833
- function GitPanel({ status, log, loading = false, onRefresh, className }) {
3834
- if (loading) {
3835
- return /* @__PURE__ */ jsxs21(
3836
- "div",
3837
- {
3838
- className: cn("rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container overflow-hidden", className),
3839
- "aria-busy": "true",
3840
- children: [
3841
- /* @__PURE__ */ jsx24("span", { className: "sr-only", children: "Loading git info\u2026" }),
3842
- /* @__PURE__ */ jsx24("div", { className: "flex items-center justify-between border-b border-[var(--md3-outline-variant)] bg-surface-container-high px-4 py-3", children: /* @__PURE__ */ jsxs21("div", { className: "flex items-center gap-2", children: [
3843
- /* @__PURE__ */ jsx24(GitBranch, { className: "h-4 w-4 text-muted-foreground" }),
3844
- /* @__PURE__ */ jsx24("div", { className: "h-3.5 w-24 animate-pulse rounded bg-muted" })
3845
- ] }) }),
3846
- /* @__PURE__ */ jsxs21("div", { className: "px-4 py-3", children: [
3847
- /* @__PURE__ */ jsx24("div", { className: "mb-2 h-2.5 w-20 animate-pulse rounded bg-muted" }),
3848
- /* @__PURE__ */ jsx24("div", { className: "space-y-2.5", children: [0, 1, 2].map((row) => /* @__PURE__ */ jsxs21("div", { className: "flex items-start gap-2", children: [
3849
- /* @__PURE__ */ jsx24(GitCommit, { className: "mt-0.5 h-3 w-3 shrink-0 text-muted-foreground" }),
3850
- /* @__PURE__ */ jsx24("div", { className: "h-3 w-3/4 animate-pulse rounded bg-muted" })
3851
- ] }, row)) })
3852
- ] })
3853
- ]
3854
- }
3855
- );
3856
- }
3857
- if (!status) {
3858
- return /* @__PURE__ */ jsx24("div", { className: cn("rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container p-5", className), children: /* @__PURE__ */ jsxs21("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
3859
- /* @__PURE__ */ jsx24(GitBranch, { className: "h-4 w-4" }),
3860
- "No git repository detected"
3861
- ] }) });
3862
- }
3863
- const changedCount = status.staged.length + status.modified.length + status.untracked.length;
3864
- return /* @__PURE__ */ jsxs21("div", { className: cn("rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container overflow-hidden", className), children: [
3865
- /* @__PURE__ */ jsxs21("div", { className: "flex items-center justify-between border-b border-[var(--md3-outline-variant)] bg-surface-container-high px-4 py-3", children: [
3866
- /* @__PURE__ */ jsxs21("div", { className: "flex items-center gap-2", children: [
3867
- /* @__PURE__ */ jsx24(GitBranch, { className: "h-4 w-4 text-primary" }),
3868
- /* @__PURE__ */ jsx24("span", { className: "text-sm font-bold text-foreground", children: status.branch }),
3869
- status.isDirty && /* @__PURE__ */ jsxs21("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: [
3870
- changedCount,
3871
- " change",
3872
- changedCount !== 1 ? "s" : ""
3873
- ] })
3874
- ] }),
3875
- onRefresh && /* @__PURE__ */ jsx24("button", { type: "button", onClick: onRefresh, className: "text-xs text-muted-foreground hover:text-foreground transition-colors", children: "Refresh" })
3876
- ] }),
3877
- changedCount > 0 && /* @__PURE__ */ jsx24("div", { className: "border-b border-[var(--md3-outline-variant)] px-4 py-3", children: /* @__PURE__ */ jsxs21("div", { className: "space-y-1.5", children: [
3878
- status.staged.map((f) => /* @__PURE__ */ jsxs21("div", { className: "flex items-center gap-2 text-xs", children: [
3879
- /* @__PURE__ */ jsx24(FilePlus, { className: "h-3 w-3 text-[var(--surface-success-text)]" }),
3880
- /* @__PURE__ */ jsx24("span", { className: "font-mono text-foreground truncate", children: f }),
3881
- /* @__PURE__ */ jsx24("span", { className: "text-[var(--surface-success-text)] text-[10px] font-bold ml-auto", children: "STAGED" })
3882
- ] }, `s-${f}`)),
3883
- status.modified.map((f) => /* @__PURE__ */ jsxs21("div", { className: "flex items-center gap-2 text-xs", children: [
3884
- /* @__PURE__ */ jsx24(FileEdit, { className: "h-3 w-3 text-[var(--surface-warning-text)]" }),
3885
- /* @__PURE__ */ jsx24("span", { className: "font-mono text-foreground truncate", children: f })
3886
- ] }, `m-${f}`)),
3887
- status.untracked.map((f) => /* @__PURE__ */ jsxs21("div", { className: "flex items-center gap-2 text-xs", children: [
3888
- /* @__PURE__ */ jsx24(File, { className: "h-3 w-3 text-muted-foreground" }),
3889
- /* @__PURE__ */ jsx24("span", { className: "font-mono text-muted-foreground truncate", children: f })
3890
- ] }, `u-${f}`))
3891
- ] }) }),
3892
- log.length > 0 && /* @__PURE__ */ jsxs21("div", { className: "px-4 py-3", children: [
3893
- /* @__PURE__ */ jsx24("div", { className: "mb-2 text-[10px] font-bold uppercase tracking-widest text-muted-foreground", children: "Recent Commits" }),
3894
- /* @__PURE__ */ jsx24("div", { className: "space-y-2", children: log.slice(0, 5).map((commit) => /* @__PURE__ */ jsxs21("div", { className: "flex items-start gap-2 text-xs", children: [
3895
- /* @__PURE__ */ jsx24(GitCommit, { className: "h-3 w-3 text-muted-foreground mt-0.5 shrink-0" }),
3896
- /* @__PURE__ */ jsxs21("div", { className: "min-w-0", children: [
3897
- /* @__PURE__ */ jsx24("span", { className: "font-mono text-primary mr-1.5", children: commit.shortSha }),
3898
- /* @__PURE__ */ jsx24("span", { className: "text-foreground", children: commit.message })
3899
- ] })
3900
- ] }, commit.shortSha)) })
3901
- ] }),
3902
- (status.ahead > 0 || status.behind > 0) && /* @__PURE__ */ jsxs21("div", { className: "border-t border-[var(--md3-outline-variant)] px-4 py-2 flex items-center gap-3 text-xs text-muted-foreground", children: [
3903
- status.ahead > 0 && /* @__PURE__ */ jsxs21("span", { children: [
3904
- "\u2191 ",
3905
- status.ahead,
3906
- " ahead"
3907
- ] }),
3908
- status.behind > 0 && /* @__PURE__ */ jsxs21("span", { children: [
3909
- "\u2193 ",
3910
- status.behind,
3911
- " behind"
3912
- ] })
3913
- ] })
3914
- ] });
3915
- }
3916
-
3917
- // src/dashboard/ports-list.tsx
3918
- import * as React13 from "react";
3919
- import { Copy as Copy3, Check as Check5, Globe, Plus as Plus4, Trash2 as Trash23 } from "lucide-react";
3920
- import { focusField, focusRing as focusRing5 } from "@tangle-network/ui/utils";
3921
- import { jsx as jsx25, jsxs as jsxs22 } from "react/jsx-runtime";
3922
- function PortsList({ ports, onExposePort, onRemovePort, isExposing = false, className }) {
3923
- const [newPort, setNewPort] = React13.useState("");
3924
- const [copiedPort, setCopiedPort] = React13.useState(null);
3925
- const [statusMessage, setStatusMessage] = React13.useState("");
3926
- const copyTimerRef = React13.useRef(null);
3927
- const pendingRemovalRef = React13.useRef(null);
3928
- const removeButtonsRef = React13.useRef(/* @__PURE__ */ new Map());
3929
- const portInputRef = React13.useRef(null);
3930
- const portInputId = React13.useId();
3931
- const portErrorId = React13.useId();
3932
- const portNumber = Number(newPort);
3933
- const validPort = /^\d+$/.test(newPort) && portNumber >= 1 && portNumber <= 65535;
3934
- const duplicatePort = validPort && ports.some((port) => port.port === portNumber);
3935
- const portError = newPort === "" ? null : !validPort ? "Enter a whole port number from 1 to 65535." : duplicatePort ? `Port ${portNumber} is already exposed.` : null;
3936
- React13.useEffect(() => {
3937
- return () => {
3938
- if (copyTimerRef.current) clearTimeout(copyTimerRef.current);
3939
- };
3940
- }, []);
3941
- React13.useEffect(() => {
3942
- const pending = pendingRemovalRef.current;
3943
- if (!pending || ports.some((port) => port.port === pending.removed)) return;
3944
- setStatusMessage(`Port ${pending.removed} removed.`);
3945
- if (pending.restoreFocus) {
3946
- const nextButton = pending.next == null ? null : removeButtonsRef.current.get(pending.next);
3947
- const focusTarget = nextButton ?? removeButtonsRef.current.values().next().value ?? portInputRef.current;
3948
- focusTarget?.focus();
3949
- }
3950
- pendingRemovalRef.current = null;
3951
- }, [ports]);
3952
- const handleCopy = async (url, port) => {
3953
- try {
3954
- await navigator.clipboard.writeText(url);
3955
- setCopiedPort(port);
3956
- if (copyTimerRef.current) clearTimeout(copyTimerRef.current);
3957
- copyTimerRef.current = setTimeout(() => setCopiedPort(null), 2e3);
3958
- } catch (err) {
3959
- console.warn("Clipboard write failed:", err);
3960
- }
3961
- };
3962
- const handleExpose = () => {
3963
- if (validPort && !duplicatePort && !isExposing) {
3964
- onExposePort(portNumber);
3965
- setNewPort("");
3966
- }
3967
- };
3968
- return /* @__PURE__ */ jsxs22("div", { className: cn("space-y-4", className), children: [
3969
- /* @__PURE__ */ jsx25("span", { role: "status", className: "sr-only", children: statusMessage }),
3970
- ports.length > 0 ? /* @__PURE__ */ jsxs22("div", { className: "overflow-hidden rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container text-sm", children: [
3971
- /* @__PURE__ */ jsxs22("div", { "aria-hidden": "true", className: "hidden border-b border-[var(--md3-outline-variant)] bg-surface-container-high px-3 py-2.5 text-xs font-medium text-muted-foreground sm:grid sm:grid-cols-[4rem_minmax(0,1fr)_5.5rem_3rem] sm:gap-x-2", children: [
3972
- /* @__PURE__ */ jsx25("span", { children: "Port" }),
3973
- /* @__PURE__ */ jsx25("span", { children: "Public URL" }),
3974
- /* @__PURE__ */ jsx25("span", { children: "Status" })
3975
- ] }),
3976
- /* @__PURE__ */ jsx25("ul", { className: "divide-y divide-border", children: ports.map((p, index) => /* @__PURE__ */ jsxs22("li", { className: "grid min-w-0 grid-cols-[minmax(0,1fr)_auto] items-center gap-x-2 gap-y-1 px-3 py-2 sm:grid-cols-[4rem_minmax(0,1fr)_5.5rem_3rem] sm:py-1", children: [
3977
- /* @__PURE__ */ jsxs22("span", { className: "col-start-1 row-start-1 font-mono text-xs text-foreground", children: [
3978
- /* @__PURE__ */ jsx25("span", { className: "sr-only", children: "Port " }),
3979
- p.port
3980
- ] }),
3981
- /* @__PURE__ */ jsxs22(
3982
- "button",
3983
- {
3984
- type: "button",
3985
- "aria-label": copiedPort === p.port ? `Copied public URL for port ${p.port}` : `Copy public URL for port ${p.port}: ${p.url}`,
3986
- onClick: () => handleCopy(p.url, p.port),
3987
- className: `col-span-2 row-start-3 flex min-h-10 min-w-0 items-center gap-2 rounded text-left font-mono text-xs text-[var(--accent-text)] hover:underline sm:col-span-1 sm:col-start-2 sm:row-start-1 ${focusRing5}`,
3988
- children: [
3989
- /* @__PURE__ */ jsx25("span", { "aria-hidden": "true", className: "min-w-0 flex-1 break-all sm:truncate", children: p.url }),
3990
- copiedPort === p.port ? /* @__PURE__ */ jsx25(Check5, { className: "h-4 w-4 shrink-0 text-[var(--surface-success-text)]", "aria-hidden": "true" }) : /* @__PURE__ */ jsx25(Copy3, { className: "h-4 w-4 shrink-0", "aria-hidden": "true" })
3991
- ]
3992
- }
3993
- ),
3994
- /* @__PURE__ */ jsxs22("span", { className: cn(
3995
- "col-start-1 row-start-2 inline-flex items-center justify-self-start rounded-full px-2 py-0.5 text-[10px] font-bold uppercase tracking-wider sm:col-start-3 sm:row-start-1",
3996
- p.status === "active" ? "bg-[var(--surface-success-bg)] text-[var(--surface-success-text)]" : "bg-[var(--surface-warning-bg)] text-[var(--surface-warning-text)]"
3997
- ), children: [
3998
- /* @__PURE__ */ jsx25("span", { className: "sr-only", children: "Status: " }),
3999
- p.status
4000
- ] }),
4001
- onRemovePort && /* @__PURE__ */ jsx25(
4002
- "button",
4003
- {
4004
- type: "button",
4005
- "aria-label": `Remove port ${p.port}`,
4006
- ref: (button) => {
4007
- if (button) removeButtonsRef.current.set(p.port, button);
4008
- else removeButtonsRef.current.delete(p.port);
4009
- },
4010
- onClick: (event) => {
4011
- pendingRemovalRef.current = {
4012
- removed: p.port,
4013
- next: ports[index + 1]?.port ?? ports[index - 1]?.port ?? null,
4014
- restoreFocus: event.detail === 0
4015
- };
4016
- onRemovePort(p.port);
4017
- },
4018
- className: `col-start-2 row-start-1 inline-flex min-h-10 min-w-10 items-center justify-center rounded text-muted-foreground transition-colors hover:text-destructive sm:col-start-4 ${focusRing5}`,
4019
- children: /* @__PURE__ */ jsx25(Trash23, { className: "h-4 w-4", "aria-hidden": "true" })
4020
- }
4021
- )
4022
- ] }, p.port)) })
4023
- ] }) : /* @__PURE__ */ jsxs22("div", { className: "rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container p-6 text-center", children: [
4024
- /* @__PURE__ */ jsx25(Globe, { className: "mx-auto h-8 w-8 text-muted-foreground mb-2" }),
4025
- /* @__PURE__ */ jsx25("p", { className: "text-sm text-muted-foreground", children: "No ports exposed yet" })
4026
- ] }),
4027
- /* @__PURE__ */ jsxs22("div", { className: "flex flex-wrap items-end gap-3", children: [
4028
- /* @__PURE__ */ jsxs22("div", { className: "min-w-0 flex-[1_1_10rem]", children: [
4029
- /* @__PURE__ */ jsx25("label", { htmlFor: portInputId, className: "mb-1 block text-xs font-medium text-muted-foreground", children: "Port number" }),
4030
- /* @__PURE__ */ jsx25(
4031
- "input",
4032
- {
4033
- ref: portInputRef,
4034
- id: portInputId,
4035
- type: "number",
4036
- min: 1,
4037
- max: 65535,
4038
- step: 1,
4039
- "aria-invalid": Boolean(portError),
4040
- "aria-describedby": portError ? portErrorId : void 0,
4041
- placeholder: "3000",
4042
- value: newPort,
4043
- onChange: (e) => setNewPort(e.target.value),
4044
- onKeyDown: (e) => e.key === "Enter" && handleExpose(),
4045
- className: `w-full min-w-0 rounded-lg border bg-surface-container-low px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground ${focusField}`
4046
- }
4047
- )
4048
- ] }),
4049
- /* @__PURE__ */ jsxs22(
4050
- "button",
4051
- {
4052
- type: "button",
4053
- onClick: handleExpose,
4054
- disabled: !validPort || duplicatePort || isExposing,
4055
- className: `inline-flex shrink-0 items-center gap-2 rounded-lg bg-[var(--btn-primary-bg)] px-4 py-2 text-sm font-semibold text-[var(--btn-primary-text)] shadow-sm transition-colors hover:bg-[var(--btn-primary-hover)] disabled:cursor-not-allowed disabled:opacity-55 disabled:shadow-none ${focusRing5}`,
4056
- children: [
4057
- /* @__PURE__ */ jsx25(Plus4, { className: "h-4 w-4" }),
4058
- "Expose"
4059
- ]
4060
- }
4061
- )
4062
- ] }),
4063
- portError && /* @__PURE__ */ jsx25("p", { id: portErrorId, className: "text-xs text-[var(--surface-danger-text)]", children: portError })
4064
- ] });
4065
- }
4066
-
4067
- // src/dashboard/process-list.tsx
4068
- import * as React14 from "react";
4069
- import { Activity as Activity3, Plus as Plus5, Skull, Terminal as Terminal4 } from "lucide-react";
4070
- import { focusField as focusField2, focusRing as focusRing6 } from "@tangle-network/ui/utils";
4071
- import { jsx as jsx26, jsxs as jsxs23 } from "react/jsx-runtime";
4072
- function formatUptime(startedAt) {
4073
- if (!startedAt) return "-";
4074
- const ms = Date.now() - new Date(startedAt).getTime();
4075
- if (Number.isNaN(ms) || ms < 0) return "-";
4076
- if (ms < 6e4) return `${Math.floor(ms / 1e3)}s`;
4077
- if (ms < 36e5) return `${Math.floor(ms / 6e4)}m`;
4078
- return `${Math.floor(ms / 36e5)}h ${Math.floor(ms % 36e5 / 6e4)}m`;
4079
- }
4080
- function ProcessList({ processes, onSpawn, onKill, loading = false, className }) {
4081
- const [newCommand, setNewCommand] = React14.useState("");
4082
- const [statusMessage, setStatusMessage] = React14.useState("");
4083
- const commandId = React14.useId();
4084
- const commandInputRef = React14.useRef(null);
4085
- const pendingKillRef = React14.useRef(null);
4086
- React14.useEffect(() => {
4087
- const pending = pendingKillRef.current;
4088
- if (!pending || processes.some((process) => process.pid === pending.pid && process.running)) return;
4089
- const stopped = processes.find((process) => process.pid === pending.pid);
4090
- setStatusMessage(stopped?.exitCode == null ? `Process ${pending.pid} stopped.` : `Process ${pending.pid} exited with code ${stopped.exitCode}.`);
4091
- if (pending.restoreFocus) commandInputRef.current?.focus();
4092
- pendingKillRef.current = null;
4093
- }, [processes]);
4094
- const handleSpawn = () => {
4095
- const cmd = newCommand.trim();
4096
- if (cmd) {
4097
- onSpawn(cmd);
4098
- setNewCommand("");
4099
- }
4100
- };
4101
- return /* @__PURE__ */ jsxs23("div", { className: cn("space-y-4", className), children: [
4102
- /* @__PURE__ */ jsx26("span", { role: "status", className: "sr-only", children: statusMessage }),
4103
- loading ? /* @__PURE__ */ jsxs23("div", { className: "rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container p-6 text-center", children: [
4104
- /* @__PURE__ */ jsx26(Activity3, { className: "mx-auto h-6 w-6 text-muted-foreground animate-spin mb-2", "data-motion": "essential" }),
4105
- /* @__PURE__ */ jsx26("p", { className: "text-sm text-muted-foreground", children: "Loading processes..." })
4106
- ] }) : processes.length > 0 ? /* @__PURE__ */ jsx26("div", { className: "rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container overflow-hidden", children: /* @__PURE__ */ jsx26("ul", { "aria-label": "Processes", className: "divide-y divide-border", children: processes.map((p) => {
4107
- const age = formatUptime(p.startedAt);
4108
- return /* @__PURE__ */ jsxs23("li", { className: "flex min-w-0 items-start gap-3 px-3 py-3", children: [
4109
- /* @__PURE__ */ jsxs23("div", { className: "min-w-0 flex-1", children: [
4110
- /* @__PURE__ */ jsx26("code", { className: "block break-all font-mono text-xs leading-5 text-foreground", children: p.command }),
4111
- /* @__PURE__ */ jsxs23("div", { className: "mt-2 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted-foreground", children: [
4112
- /* @__PURE__ */ jsxs23("span", { className: "font-mono tabular-nums", children: [
4113
- "PID ",
4114
- p.pid
4115
- ] }),
4116
- /* @__PURE__ */ jsx26("span", { className: cn(
4117
- "inline-flex items-center rounded-full px-2 py-0.5 text-[10px] font-bold uppercase tracking-wider",
4118
- p.running ? "bg-[var(--surface-success-bg)] text-[var(--surface-success-text)]" : "bg-surface-container-high text-muted-foreground"
4119
- ), children: p.running ? "running" : `exited (${p.exitCode ?? "?"})` }),
4120
- age !== "-" && /* @__PURE__ */ jsx26("span", { className: "font-mono tabular-nums", children: p.running ? `Uptime ${age}` : `Started ${age} ago` })
4121
- ] })
4122
- ] }),
4123
- p.running && /* @__PURE__ */ jsx26(
4124
- "button",
4125
- {
4126
- type: "button",
4127
- onClick: (event) => {
4128
- pendingKillRef.current = { pid: p.pid, restoreFocus: event.detail === 0 };
4129
- onKill(p.pid);
4130
- },
4131
- className: `flex size-10 shrink-0 items-center justify-center rounded text-muted-foreground transition-colors hover:bg-destructive/10 hover:text-destructive ${focusRing6}`,
4132
- "aria-label": `Kill process ${p.pid}`,
4133
- title: `Kill process ${p.pid}`,
4134
- children: /* @__PURE__ */ jsx26(Skull, { "aria-hidden": "true", className: "h-4 w-4" })
4135
- }
4136
- )
4137
- ] }, `${p.pid}-${p.startedAt ?? p.command}`);
4138
- }) }) }) : /* @__PURE__ */ jsxs23("div", { className: "rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container p-6 text-center", children: [
4139
- /* @__PURE__ */ jsx26(Terminal4, { className: "mx-auto h-8 w-8 text-muted-foreground mb-2" }),
4140
- /* @__PURE__ */ jsx26("p", { className: "text-sm text-muted-foreground", children: "No processes running" })
4141
- ] }),
4142
- /* @__PURE__ */ jsxs23("form", { onSubmit: (event) => {
4143
- event.preventDefault();
4144
- handleSpawn();
4145
- }, className: "flex min-w-0 flex-wrap items-end gap-2", children: [
4146
- /* @__PURE__ */ jsxs23("div", { className: "min-w-0 flex-[1_1_12rem]", children: [
4147
- /* @__PURE__ */ jsx26("label", { htmlFor: commandId, className: "mb-1 block text-xs font-medium text-muted-foreground", children: "Command" }),
4148
- /* @__PURE__ */ jsx26(
4149
- "input",
4150
- {
4151
- ref: commandInputRef,
4152
- id: commandId,
4153
- type: "text",
4154
- placeholder: "node app.js",
4155
- value: newCommand,
4156
- onChange: (e) => setNewCommand(e.target.value),
4157
- className: `w-full min-w-0 rounded-lg border bg-surface-container-low px-3 py-2 text-sm font-mono text-foreground placeholder:text-muted-foreground ${focusField2}`
4158
- }
4159
- )
4160
- ] }),
4161
- /* @__PURE__ */ jsxs23(
4162
- "button",
4163
- {
4164
- type: "submit",
4165
- disabled: !newCommand.trim(),
4166
- className: `inline-flex h-10 shrink-0 items-center gap-2 rounded-lg border border-[var(--border-accent)] bg-[var(--accent-surface-soft)] px-3 text-sm font-medium text-[var(--accent-text)] transition-colors hover:bg-[var(--btn-primary-bg)] hover:text-[var(--btn-primary-text)] disabled:opacity-50 ${focusRing6}`,
4167
- children: [
4168
- /* @__PURE__ */ jsx26(Plus5, { "aria-hidden": "true", className: "h-4 w-4" }),
4169
- "Spawn"
4170
- ]
4171
- }
4172
- )
4173
- ] })
4174
- ] });
4175
- }
4176
-
4177
- // src/dashboard/network-config.tsx
4178
- import * as React15 from "react";
4179
- import { Network as Network2, Plus as Plus6, Trash2 as Trash24, ShieldAlert } from "lucide-react";
4180
- import { focusField as focusField3, focusRing as focusRing7 } from "@tangle-network/ui/utils";
4181
- import { jsx as jsx27, jsxs as jsxs24 } from "react/jsx-runtime";
4182
- function NetworkConfig({ config, onUpdate, loading = false, className }) {
4183
- const [newCidr, setNewCidr] = React15.useState("");
4184
- const isValidCidr = (value) => {
4185
- const match = value.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\/(\d{1,2})$/);
4186
- if (!match) return false;
4187
- const octets = [match[1], match[2], match[3], match[4]].map(Number);
4188
- const prefix = Number(match[5]);
4189
- return octets.every((o) => o <= 255) && prefix <= 32;
4190
- };
4191
- const handleAddCidr = () => {
4192
- const cidr = newCidr.trim();
4193
- if (cidr && config && isValidCidr(cidr) && !config.allowList.includes(cidr)) {
4194
- onUpdate({ allowList: [...config.allowList, cidr] });
4195
- setNewCidr("");
4196
- }
4197
- };
4198
- const handleRemoveCidr = (cidr) => {
4199
- if (config) {
4200
- onUpdate({ allowList: config.allowList.filter((c) => c !== cidr) });
4201
- }
4202
- };
4203
- if (loading || !config) {
4204
- return /* @__PURE__ */ jsxs24("div", { className: cn("rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container p-6 text-center", className), children: [
4205
- /* @__PURE__ */ jsx27(Network2, { className: "mx-auto h-6 w-6 text-muted-foreground animate-pulse mb-2", "data-motion": "essential" }),
4206
- /* @__PURE__ */ jsx27("p", { className: "text-sm text-muted-foreground", children: "Loading network configuration..." })
4207
- ] });
4208
- }
4209
- return /* @__PURE__ */ jsxs24("div", { className: cn("space-y-4", className), children: [
4210
- /* @__PURE__ */ jsxs24("div", { className: "flex items-center justify-between rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container px-4 py-3", children: [
4211
- /* @__PURE__ */ jsxs24("div", { className: "flex items-center gap-3", children: [
4212
- /* @__PURE__ */ jsx27(ShieldAlert, { className: "h-4 w-4 text-muted-foreground" }),
4213
- /* @__PURE__ */ jsxs24("div", { children: [
4214
- /* @__PURE__ */ jsx27("p", { className: "text-sm font-medium text-foreground", children: "Block Outbound Traffic" }),
4215
- /* @__PURE__ */ jsx27("p", { className: "text-xs text-muted-foreground", children: "Prevent the sandbox from making external network requests" })
4216
- ] })
4217
- ] }),
4218
- /* @__PURE__ */ jsx27(
4219
- "button",
4220
- {
4221
- type: "button",
4222
- role: "switch",
4223
- "aria-checked": config.blockOutbound,
4224
- "aria-label": "Block outbound traffic",
4225
- onClick: () => onUpdate({ blockOutbound: !config.blockOutbound }),
4226
- className: cn(
4227
- "relative inline-flex h-6 w-11 items-center rounded-full transition-colors",
4228
- config.blockOutbound ? "bg-destructive" : "bg-surface-container-high"
4229
- ),
4230
- children: /* @__PURE__ */ jsx27(
4231
- "span",
4232
- {
4233
- className: cn(
4234
- "inline-block h-4 w-4 rounded-full bg-white transition-transform shadow-sm",
4235
- config.blockOutbound ? "translate-x-6" : "translate-x-1"
4236
- )
4237
- }
4238
- )
4239
- }
4240
- )
4241
- ] }),
4242
- /* @__PURE__ */ jsxs24("div", { children: [
4243
- /* @__PURE__ */ jsx27("h4", { className: "text-xs font-medium text-muted-foreground mb-2 uppercase tracking-wider", children: "Allowlist (CIDR)" }),
4244
- config.allowList.length > 0 ? /* @__PURE__ */ jsx27("div", { className: "space-y-1.5 mb-3", children: config.allowList.map((cidr) => /* @__PURE__ */ jsxs24("div", { className: "flex items-center justify-between rounded border border-[var(--md3-outline-variant)] bg-surface-container-high px-3 py-2", children: [
4245
- /* @__PURE__ */ jsx27("span", { className: "font-mono text-xs text-foreground", children: cidr }),
4246
- /* @__PURE__ */ jsx27(
4247
- "button",
4248
- {
4249
- type: "button",
4250
- "aria-label": `Remove ${cidr} from allowlist`,
4251
- onClick: () => handleRemoveCidr(cidr),
4252
- className: `p-2 text-muted-foreground hover:text-destructive transition-colors rounded ${focusRing7}`,
4253
- children: /* @__PURE__ */ jsx27(Trash24, { className: "h-3 w-3" })
4254
- }
4255
- )
4256
- ] }, cidr)) }) : /* @__PURE__ */ jsx27("p", { className: "text-xs text-muted-foreground mb-3", children: "No CIDR rules configured. All traffic is allowed." }),
4257
- /* @__PURE__ */ jsxs24("div", { className: "flex items-center gap-3", children: [
4258
- /* @__PURE__ */ jsx27(
4259
- "input",
4260
- {
4261
- type: "text",
4262
- "aria-label": "CIDR to allow",
4263
- placeholder: "CIDR (e.g. 10.0.0.0/8)",
4264
- value: newCidr,
4265
- onChange: (e) => setNewCidr(e.target.value),
4266
- onKeyDown: (e) => e.key === "Enter" && handleAddCidr(),
4267
- className: `flex-1 rounded-lg border bg-surface-container-low px-3 py-2 text-sm font-mono text-foreground placeholder:text-muted-foreground ${focusField3}`
4268
- }
4269
- ),
4270
- /* @__PURE__ */ jsxs24(
4271
- "button",
4272
- {
4273
- type: "button",
4274
- onClick: handleAddCidr,
4275
- disabled: !newCidr.trim(),
4276
- 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",
4277
- children: [
4278
- /* @__PURE__ */ jsx27(Plus6, { className: "h-4 w-4" }),
4279
- "Add"
4280
- ]
4281
- }
4282
- )
4283
- ] })
4284
- ] })
4285
- ] });
4286
- }
4287
-
4288
- // src/dashboard/backend-config.tsx
4289
- import * as React16 from "react";
4290
- import { Bot as Bot2, Plus as Plus7, Trash2 as Trash25, Server, Wrench } from "lucide-react";
4291
- import { focusField as focusField4, focusRing as focusRing8 } from "@tangle-network/ui/utils";
4292
- import { Fragment as Fragment14, jsx as jsx28, jsxs as jsxs25 } from "react/jsx-runtime";
4293
- function BackendConfig({
4294
- status,
4295
- mcpServers,
4296
- onAddMcp,
4297
- onRemoveMcp,
4298
- onRestart,
4299
- loading = false,
4300
- className
4301
- }) {
4302
- const [showAddMcp, setShowAddMcp] = React16.useState(false);
4303
- const [mcpName, setMcpName] = React16.useState("");
4304
- const [mcpCommand, setMcpCommand] = React16.useState("");
4305
- const [mcpArgs, setMcpArgs] = React16.useState("");
4306
- const handleAddMcp = () => {
4307
- const name = mcpName.trim();
4308
- const command = mcpCommand.trim();
4309
- if (name && command) {
4310
- onAddMcp({
4311
- name,
4312
- command,
4313
- args: mcpArgs.trim() ? mcpArgs.trim().split(/\s+/) : void 0
4314
- });
4315
- setMcpName("");
4316
- setMcpCommand("");
4317
- setMcpArgs("");
4318
- setShowAddMcp(false);
4319
- }
4320
- };
4321
- if (loading || !status) {
4322
- return /* @__PURE__ */ jsxs25("div", { className: cn("rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container p-6 text-center", className), children: [
4323
- /* @__PURE__ */ jsx28(Bot2, { className: "mx-auto h-6 w-6 text-muted-foreground animate-pulse mb-2", "data-motion": "essential" }),
4324
- /* @__PURE__ */ jsx28("p", { className: "text-sm text-muted-foreground", children: "Loading backend status..." })
4325
- ] });
4326
- }
4327
- return /* @__PURE__ */ jsxs25("div", { className: cn("space-y-4", className), children: [
4328
- /* @__PURE__ */ jsxs25("div", { className: "rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container overflow-hidden", children: [
4329
- /* @__PURE__ */ jsxs25("div", { className: "px-4 py-3 border-b border-[var(--md3-outline-variant)] bg-surface-container-high flex items-center justify-between", children: [
4330
- /* @__PURE__ */ jsx28("h4", { className: "text-xs font-medium text-muted-foreground uppercase tracking-wider", children: "Agent Status" }),
4331
- onRestart ? /* @__PURE__ */ jsx28(
4332
- "button",
4333
- {
4334
- type: "button",
4335
- onClick: onRestart,
4336
- className: "inline-flex items-center gap-1.5 rounded-md bg-surface-container-high px-2.5 py-1 text-xs font-medium text-foreground hover:bg-surface-container-highest transition-colors border border-[var(--md3-outline-variant)]",
4337
- children: "Restart"
4338
- }
4339
- ) : null
4340
- ] }),
4341
- /* @__PURE__ */ jsx28("div", { className: "p-4", children: /* @__PURE__ */ jsxs25("dl", { className: "grid grid-cols-[100px_1fr] gap-y-3 text-sm", children: [
4342
- /* @__PURE__ */ jsx28("dt", { className: "text-muted-foreground", children: "Status" }),
4343
- /* @__PURE__ */ jsx28("dd", { children: /* @__PURE__ */ jsx28("span", { className: cn(
4344
- "inline-flex items-center gap-1.5 rounded-full px-2 py-0.5 text-[10px] font-bold uppercase tracking-wider",
4345
- status.running ? "bg-[var(--surface-success-bg)] text-[var(--surface-success-text)]" : "bg-destructive/10 text-destructive"
4346
- ), children: status.running ? "Running" : "Stopped" }) }),
4347
- /* @__PURE__ */ jsx28("dt", { className: "text-muted-foreground", children: "Model" }),
4348
- /* @__PURE__ */ jsx28("dd", { className: "font-mono text-xs", children: status.model ?? "Default" }),
4349
- status.provider && /* @__PURE__ */ jsxs25(Fragment14, { children: [
4350
- /* @__PURE__ */ jsx28("dt", { className: "text-muted-foreground", children: "Provider" }),
4351
- /* @__PURE__ */ jsx28("dd", { className: "font-mono text-xs", children: status.provider })
4352
- ] })
4353
- ] }) })
4354
- ] }),
4355
- /* @__PURE__ */ jsxs25("div", { className: "rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container overflow-hidden", children: [
4356
- /* @__PURE__ */ jsxs25("div", { className: "px-4 py-3 border-b border-[var(--md3-outline-variant)] bg-surface-container-high flex items-center justify-between", children: [
4357
- /* @__PURE__ */ jsxs25("h4", { className: "text-xs font-medium text-muted-foreground uppercase tracking-wider flex items-center gap-2", children: [
4358
- /* @__PURE__ */ jsx28(Wrench, { className: "h-3.5 w-3.5" }),
4359
- "MCP Servers"
4360
- ] }),
4361
- /* @__PURE__ */ jsxs25(
4362
- "button",
4363
- {
4364
- type: "button",
4365
- onClick: () => setShowAddMcp(!showAddMcp),
4366
- 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",
4367
- children: [
4368
- /* @__PURE__ */ jsx28(Plus7, { className: "h-3 w-3" }),
4369
- "Add"
4370
- ]
4371
- }
4372
- )
4373
- ] }),
4374
- mcpServers.length > 0 ? /* @__PURE__ */ jsx28("div", { className: "divide-y divide-border", children: mcpServers.map((s) => /* @__PURE__ */ jsxs25("div", { className: "flex items-center justify-between px-4 py-3", children: [
4375
- /* @__PURE__ */ jsxs25("div", { className: "flex items-center gap-3 min-w-0", children: [
4376
- /* @__PURE__ */ jsx28(Server, { className: "h-3.5 w-3.5 text-muted-foreground shrink-0" }),
4377
- /* @__PURE__ */ jsxs25("div", { className: "min-w-0", children: [
4378
- /* @__PURE__ */ jsx28("p", { className: "text-sm font-medium text-foreground truncate", children: s.name }),
4379
- /* @__PURE__ */ jsxs25("p", { className: "text-xs font-mono text-muted-foreground truncate", children: [
4380
- s.command,
4381
- " ",
4382
- s.args?.join(" ") ?? ""
4383
- ] })
4384
- ] })
4385
- ] }),
4386
- /* @__PURE__ */ jsxs25("div", { className: "flex items-center gap-2 shrink-0", children: [
4387
- s.status && /* @__PURE__ */ jsx28("span", { className: cn(
4388
- "inline-flex items-center rounded-full px-1.5 py-0.5 text-[9px] font-bold uppercase",
4389
- s.status === "running" ? "bg-[var(--surface-success-bg)] text-[var(--surface-success-text)]" : s.status === "error" ? "bg-destructive/10 text-destructive" : "bg-surface-container-high text-muted-foreground"
4390
- ), children: s.status }),
4391
- /* @__PURE__ */ jsx28(
4392
- "button",
4393
- {
4394
- type: "button",
4395
- "aria-label": `Remove ${s.name} MCP server`,
4396
- onClick: () => onRemoveMcp(s.name),
4397
- className: `p-2 text-muted-foreground hover:text-destructive transition-colors rounded ${focusRing8}`,
4398
- children: /* @__PURE__ */ jsx28(Trash25, { className: "h-3.5 w-3.5" })
4399
- }
4400
- )
4401
- ] })
4402
- ] }, s.name)) }) : /* @__PURE__ */ jsx28("div", { className: "p-4 text-center", children: /* @__PURE__ */ jsx28("p", { className: "text-xs text-muted-foreground", children: "No MCP servers configured" }) }),
4403
- showAddMcp && /* @__PURE__ */ jsxs25("div", { className: "p-4 border-t border-[var(--md3-outline-variant)] bg-surface-container-high space-y-2", children: [
4404
- /* @__PURE__ */ jsxs25("label", { className: "block space-y-1 text-xs font-medium text-muted-foreground", children: [
4405
- /* @__PURE__ */ jsx28("span", { children: "Server name" }),
4406
- /* @__PURE__ */ jsx28(
4407
- "input",
4408
- {
4409
- type: "text",
4410
- value: mcpName,
4411
- onChange: (e) => setMcpName(e.target.value),
4412
- className: `w-full rounded-lg border bg-surface-container-low px-3 py-2 text-sm text-foreground ${focusField4}`
4413
- }
4414
- )
4415
- ] }),
4416
- /* @__PURE__ */ jsxs25("label", { className: "block space-y-1 text-xs font-medium text-muted-foreground", children: [
4417
- /* @__PURE__ */ jsx28("span", { children: "Command" }),
4418
- /* @__PURE__ */ jsx28(
4419
- "input",
4420
- {
4421
- type: "text",
4422
- placeholder: "npx @server/mcp",
4423
- value: mcpCommand,
4424
- onChange: (e) => setMcpCommand(e.target.value),
4425
- className: `w-full rounded-lg border bg-surface-container-low px-3 py-2 text-sm font-mono text-foreground placeholder:text-muted-foreground ${focusField4}`
4426
- }
4427
- )
4428
- ] }),
4429
- /* @__PURE__ */ jsxs25("label", { className: "block space-y-1 text-xs font-medium text-muted-foreground", children: [
4430
- /* @__PURE__ */ jsx28("span", { children: "Arguments (optional)" }),
4431
- /* @__PURE__ */ jsx28(
4432
- "input",
4433
- {
4434
- type: "text",
4435
- placeholder: "Space-separated arguments",
4436
- value: mcpArgs,
4437
- onChange: (e) => setMcpArgs(e.target.value),
4438
- className: `w-full rounded-lg border bg-surface-container-low px-3 py-2 text-sm font-mono text-foreground placeholder:text-muted-foreground ${focusField4}`
4439
- }
4440
- )
4441
- ] }),
4442
- /* @__PURE__ */ jsxs25("div", { className: "flex justify-end gap-2 pt-1", children: [
4443
- /* @__PURE__ */ jsx28(
4444
- "button",
4445
- {
4446
- type: "button",
4447
- onClick: () => setShowAddMcp(false),
4448
- className: "rounded-md px-3 py-1.5 text-xs font-medium text-muted-foreground hover:text-foreground transition-colors",
4449
- children: "Cancel"
4450
- }
4451
- ),
4452
- /* @__PURE__ */ jsx28(
4453
- "button",
4454
- {
4455
- type: "button",
4456
- onClick: handleAddMcp,
4457
- disabled: !mcpName.trim() || !mcpCommand.trim(),
4458
- 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",
4459
- children: "Add Server"
4460
- }
4461
- )
4462
- ] })
4463
- ] })
4464
- ] })
4465
- ] });
4466
- }
4467
-
4468
- // src/dashboard/snapshot-list.tsx
4469
- import * as React17 from "react";
4470
- import { Camera, Clock as Clock5, HardDrive as HardDrive2, Plus as Plus8, RotateCcw } from "lucide-react";
4471
- import { focusField as focusField5 } from "@tangle-network/ui/utils";
4472
- import { jsx as jsx29, jsxs as jsxs26 } from "react/jsx-runtime";
4473
- function formatBytes(bytes) {
4474
- if (bytes == null || bytes < 0) return "-";
4475
- if (bytes === 0) return "0 B";
4476
- const k = 1024;
4477
- const sizes = ["B", "KB", "MB", "GB", "TB"];
4478
- const i = Math.min(Math.floor(Math.log(bytes) / Math.log(k)), sizes.length - 1);
4479
- return `${Number.parseFloat((bytes / k ** i).toFixed(1))} ${sizes[i]}`;
4480
- }
4481
- function formatDate(dateStr) {
4482
- const d = new Date(dateStr);
4483
- if (Number.isNaN(d.getTime())) return dateStr;
4484
- return d.toLocaleDateString(void 0, { month: "short", day: "numeric", hour: "2-digit", minute: "2-digit" });
4485
- }
4486
- function SnapshotList({ snapshots, onCreate, onRestore, onSaveAsTemplate, loading = false, className }) {
4487
- const [showCreate, setShowCreate] = React17.useState(false);
4488
- const [tags, setTags] = React17.useState("");
4489
- const handleCreate = () => {
4490
- const tagList = tags.trim() ? tags.trim().split(",").map((t) => t.trim()).filter(Boolean) : void 0;
4491
- onCreate(tagList);
4492
- setTags("");
4493
- setShowCreate(false);
4494
- };
4495
- return /* @__PURE__ */ jsxs26("div", { className: cn("space-y-4", className), children: [
4496
- /* @__PURE__ */ jsxs26("div", { className: "flex items-center justify-between", children: [
4497
- /* @__PURE__ */ jsx29("h3", { className: "text-sm font-bold text-foreground", children: "Snapshots" }),
4498
- /* @__PURE__ */ jsxs26(
4499
- "button",
4500
- {
4501
- type: "button",
4502
- onClick: () => setShowCreate(!showCreate),
4503
- 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",
4504
- children: [
4505
- /* @__PURE__ */ jsx29(Plus8, { className: "h-3.5 w-3.5" }),
4506
- "Create Snapshot"
4507
- ]
4508
- }
4509
- )
4510
- ] }),
4511
- showCreate && /* @__PURE__ */ jsxs26("div", { className: "rounded-lg border border-primary/20 bg-primary/5 p-4 space-y-3", children: [
4512
- /* @__PURE__ */ jsx29("p", { className: "text-sm text-foreground font-medium", children: "Create a new snapshot of the current sandbox state." }),
4513
- /* @__PURE__ */ jsx29(
4514
- "input",
4515
- {
4516
- type: "text",
4517
- placeholder: "Tags (comma-separated, optional)",
4518
- value: tags,
4519
- onChange: (e) => setTags(e.target.value),
4520
- onKeyDown: (e) => e.key === "Enter" && handleCreate(),
4521
- className: `w-full rounded-lg border bg-surface-container-low px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground ${focusField5}`
4522
- }
4523
- ),
4524
- /* @__PURE__ */ jsxs26("div", { className: "flex justify-end gap-2", children: [
4525
- /* @__PURE__ */ jsx29(
4526
- "button",
4527
- {
4528
- type: "button",
4529
- onClick: () => setShowCreate(false),
4530
- className: "rounded-md px-3 py-1.5 text-xs font-medium text-muted-foreground hover:text-foreground transition-colors",
4531
- children: "Cancel"
4532
- }
4533
- ),
4534
- /* @__PURE__ */ jsxs26(
4535
- "button",
4536
- {
4537
- type: "button",
4538
- onClick: handleCreate,
4539
- className: "rounded-md bg-primary px-3 py-1.5 text-xs font-medium text-primary-foreground hover:bg-primary/90 transition-colors",
4540
- children: [
4541
- /* @__PURE__ */ jsx29(Camera, { className: "h-3 w-3 mr-1.5 inline" }),
4542
- "Create"
4543
- ]
4544
- }
4545
- )
4546
- ] })
4547
- ] }),
4548
- loading ? /* @__PURE__ */ jsxs26("div", { className: "rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container p-6 text-center", children: [
4549
- /* @__PURE__ */ jsx29(Camera, { className: "mx-auto h-6 w-6 text-muted-foreground animate-pulse mb-2", "data-motion": "essential" }),
4550
- /* @__PURE__ */ jsx29("p", { className: "text-sm text-muted-foreground", children: "Loading snapshots..." })
4551
- ] }) : snapshots.length > 0 ? /* @__PURE__ */ jsx29("div", { className: "rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container overflow-hidden", children: /* @__PURE__ */ jsxs26("table", { className: "w-full text-sm", children: [
4552
- /* @__PURE__ */ jsx29("thead", { className: "bg-surface-container-high border-b border-[var(--md3-outline-variant)]", children: /* @__PURE__ */ jsxs26("tr", { children: [
4553
- /* @__PURE__ */ jsx29("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "ID" }),
4554
- /* @__PURE__ */ jsx29("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Created" }),
4555
- /* @__PURE__ */ jsx29("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Size" }),
4556
- /* @__PURE__ */ jsx29("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Tags" }),
4557
- /* @__PURE__ */ jsx29("th", { className: "px-4 py-2.5 text-right text-xs font-medium text-muted-foreground w-24" })
4558
- ] }) }),
4559
- /* @__PURE__ */ jsx29("tbody", { className: "divide-y divide-border", children: snapshots.map((s) => /* @__PURE__ */ jsxs26("tr", { children: [
4560
- /* @__PURE__ */ jsx29("td", { className: "px-4 py-3 font-mono text-xs text-foreground", children: s.id.slice(0, 12) }),
4561
- /* @__PURE__ */ jsx29("td", { className: "px-4 py-3 text-xs text-muted-foreground", children: /* @__PURE__ */ jsxs26("span", { className: "inline-flex items-center gap-1.5", children: [
4562
- /* @__PURE__ */ jsx29(Clock5, { className: "h-3 w-3" }),
4563
- formatDate(s.createdAt)
4564
- ] }) }),
4565
- /* @__PURE__ */ jsx29("td", { className: "px-4 py-3 text-xs text-muted-foreground", children: /* @__PURE__ */ jsxs26("span", { className: "inline-flex items-center gap-1.5", children: [
4566
- /* @__PURE__ */ jsx29(HardDrive2, { className: "h-3 w-3" }),
4567
- formatBytes(s.sizeBytes)
4568
- ] }) }),
4569
- /* @__PURE__ */ jsx29("td", { className: "px-4 py-3", children: s.tags?.length ? /* @__PURE__ */ jsx29("div", { className: "flex items-center gap-1 flex-wrap", children: s.tags.map((tag) => /* @__PURE__ */ jsx29("span", { className: "rounded-full bg-surface-container-high px-2 py-0.5 text-[10px] font-medium text-muted-foreground border border-[var(--md3-outline-variant)]", children: tag }, tag)) }) : /* @__PURE__ */ jsx29("span", { className: "text-xs text-muted-foreground", children: "-" }) }),
4570
- /* @__PURE__ */ jsx29("td", { className: "px-4 py-3 text-right", children: /* @__PURE__ */ jsxs26("div", { className: "flex items-center justify-end gap-2", children: [
4571
- /* @__PURE__ */ jsxs26(
4572
- "button",
4573
- {
4574
- type: "button",
4575
- onClick: () => onRestore(s.id),
4576
- className: "inline-flex items-center gap-1.5 rounded-md bg-surface-container-high px-2.5 py-1 text-xs font-medium text-foreground hover:bg-surface-container-highest transition-colors border border-[var(--md3-outline-variant)]",
4577
- title: "Restore to new sandbox",
4578
- children: [
4579
- /* @__PURE__ */ jsx29(RotateCcw, { className: "h-3 w-3" }),
4580
- "Restore"
4581
- ]
4582
- }
4583
- ),
4584
- onSaveAsTemplate && /* @__PURE__ */ jsx29(
4585
- "button",
4586
- {
4587
- type: "button",
4588
- onClick: () => onSaveAsTemplate(s.id),
4589
- 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",
4590
- title: "Save as reusable template",
4591
- children: "Save as Template"
4592
- }
4593
- )
4594
- ] }) })
4595
- ] }, s.id)) })
4596
- ] }) }) : /* @__PURE__ */ jsxs26("div", { className: "rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container p-6 text-center", children: [
4597
- /* @__PURE__ */ jsx29(Camera, { className: "mx-auto h-8 w-8 text-muted-foreground mb-2" }),
4598
- /* @__PURE__ */ jsx29("p", { className: "text-sm text-muted-foreground", children: "No snapshots yet" }),
4599
- /* @__PURE__ */ jsx29("p", { className: "text-xs text-muted-foreground mt-1", children: "Create a snapshot to save the current state of your sandbox." })
4600
- ] })
4601
- ] });
4602
- }
4603
-
4604
- // src/dashboard/promo-banner.tsx
4605
- import { Fragment as Fragment15, jsx as jsx30, jsxs as jsxs27 } from "react/jsx-runtime";
4606
- function PromoBanner({
4607
- title,
4608
- description,
4609
- buttonLabel,
4610
- href,
4611
- onClick,
4612
- icon,
4613
- disabled = false,
4614
- className
4615
- }) {
4616
- const buttonClasses = cn(
4617
- "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",
4618
- disabled ? "opacity-50 cursor-not-allowed" : "hover:bg-[var(--btn-primary-hover)]"
4619
- );
4620
- const buttonContent = /* @__PURE__ */ jsxs27(Fragment15, { children: [
4621
- buttonLabel,
4622
- /* @__PURE__ */ jsxs27("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: [
4623
- /* @__PURE__ */ jsx30("path", { d: "M5 12h14" }),
4624
- /* @__PURE__ */ jsx30("path", { d: "m12 5 7 7-7 7" })
4625
- ] })
4626
- ] });
4627
- return /* @__PURE__ */ jsxs27("div", { className: cn("relative overflow-hidden rounded-xl bg-[var(--brand-strong)] p-8 md:flex md:items-center md:justify-between", className), children: [
4628
- /* @__PURE__ */ jsxs27("div", { className: "relative z-10", children: [
4629
- /* @__PURE__ */ jsx30("h3", { className: "text-xl font-bold text-[var(--brand-strong-text)]", children: title }),
4630
- /* @__PURE__ */ jsx30("p", { className: "mt-2 max-w-md text-sm text-[var(--brand-strong-text-muted)]", children: description }),
4631
- href && !disabled ? /* @__PURE__ */ jsx30("a", { href, target: "_blank", rel: "noopener noreferrer", onClick, className: buttonClasses, children: buttonContent }) : /* @__PURE__ */ jsx30("button", { type: "button", onClick, disabled, className: buttonClasses, children: buttonContent })
4632
- ] }),
4633
- icon && /* @__PURE__ */ jsx30("div", { className: "relative z-10 mt-6 flex items-center md:mt-0", children: /* @__PURE__ */ jsx30("div", { className: "flex h-16 w-16 items-center justify-center rounded-2xl border border-white/10 bg-white/10", children: icon }) }),
4634
- /* @__PURE__ */ jsx30("div", { className: "pointer-events-none absolute inset-y-0 right-0 w-1/3 bg-gradient-to-l from-white/5 to-transparent" })
4635
- ] });
4636
- }
4637
-
4638
- export {
4639
- SIDEBAR_RAIL_WIDTH,
4640
- SIDEBAR_PANEL_WIDTH,
4641
- SIDEBAR_TOTAL_WIDTH,
4642
- SIDEBAR_MOBILE_WIDTH,
4643
- SIDEBAR_RAIL_LABELED_WIDTH,
4644
- SidebarProvider,
4645
- useSidebar,
4646
- RailTooltip,
4647
- RailThemeToggle,
4648
- RailCollapseToggle,
4649
- RailHeader,
4650
- Sidebar,
4651
- SidebarRail,
4652
- SidebarRailHeader,
4653
- SidebarRailNav,
4654
- SidebarRailFooter,
4655
- RailSeparator,
4656
- RailButton,
4657
- RailFlyout,
4658
- RailExpandable,
4659
- RailModeButton,
4660
- SidebarPanel,
4661
- SidebarPanelHeader,
4662
- SidebarPanelContent,
4663
- SidebarContent,
4664
- ProfileAvatar,
4665
- CreditBalance,
4666
- INSUFFICIENT_BALANCE_CODE,
4667
- parseInsufficientBalance,
4668
- OutOfCreditsModal,
4669
- ClusterStatusBar,
4670
- ResourceMeter,
4671
- ResourceSnapshot,
4672
- ActivityFeed,
4673
- canAdminSandbox,
4674
- SandboxCard,
4675
- NewSandboxCard,
4676
- SandboxTable,
4677
- InvoiceTable,
4678
- PlanCards,
4679
- BackendSelector,
4680
- HARNESS_BRAND,
4681
- HarnessLogo,
4682
- HARNESS_OPTIONS,
4683
- chatCapableHarnesses,
4684
- HarnessPicker,
4685
- DashboardLayout,
4686
- SidebarLayout,
4687
- ProfileSelector,
4688
- ProfileComparison,
4689
- MetricAreaChart,
4690
- VariantList,
4691
- SystemLogsViewer,
4692
- UsageSummary,
4693
- GitPanel,
4694
- PortsList,
4695
- ProcessList,
4696
- NetworkConfig,
4697
- BackendConfig,
4698
- SnapshotList,
4699
- PromoBanner
4700
- };