@tangle-network/sandbox-ui 0.14.0 → 0.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth.d.ts +1 -74
- package/dist/auth.js +1 -4
- package/dist/chat.d.ts +1 -136
- package/dist/chat.js +2 -15
- package/dist/chunk-2BUPSB7O.js +0 -0
- package/dist/chunk-3J6FG3FJ.js +18 -0
- package/dist/chunk-76IQLPW2.js +206 -0
- package/dist/chunk-7ZA5SEK3.js +239 -0
- package/dist/chunk-AHBZCBDO.js +2960 -0
- package/dist/chunk-AZ3AWMTM.js +8 -0
- package/dist/chunk-CMY7W45U.js +380 -0
- package/dist/{chunk-QMU2PWOU.js → chunk-DNZ4DTNA.js} +71 -17
- package/dist/chunk-EI44GEQ5.js +6 -0
- package/dist/{chunk-5OQ27N57.js → chunk-GPT7VKK6.js} +34 -38
- package/dist/chunk-JBGKGLD7.js +16 -0
- package/dist/chunk-NJNME4J4.js +14 -0
- package/dist/chunk-QPAJR74X.js +20 -0
- package/dist/chunk-TK46XFLM.js +28 -0
- package/dist/chunk-WID73FPH.js +89 -0
- package/dist/chunk-YVXK4XRO.js +30 -0
- package/dist/dashboard.d.ts +538 -4
- package/dist/dashboard.js +15 -886
- package/dist/editor.d.ts +1 -120
- package/dist/editor.js +1 -5
- package/dist/files.d.ts +1 -129
- package/dist/files.js +2 -7
- package/dist/globals.css +2 -1265
- package/dist/hooks.d.ts +114 -11
- package/dist/hooks.js +17 -88
- package/dist/index.d.ts +24 -99
- package/dist/index.js +247 -252
- package/dist/markdown.d.ts +1 -29
- package/dist/markdown.js +2 -2
- package/dist/openui.d.ts +8 -115
- package/dist/openui.js +1 -6
- package/dist/pages.d.ts +1 -2
- package/dist/pages.js +68 -66
- package/dist/primitives.d.ts +14 -49
- package/dist/primitives.js +69 -77
- package/dist/run.d.ts +1 -14
- package/dist/run.js +2 -22
- package/dist/sdk-hooks.d.ts +3 -283
- package/dist/sdk-hooks.js +10 -14
- package/dist/stores.d.ts +2 -14
- package/dist/stores.js +11 -39
- package/dist/styles.css +2 -1265
- package/dist/{usage-chart-CPTcNlGs.d.ts → template-card-UhV3pmRC.d.ts} +16 -1
- package/dist/types.d.ts +11 -8
- package/dist/types.js +1 -0
- package/dist/utils.d.ts +1 -44
- package/dist/utils.js +6 -12
- package/dist/workspace.d.ts +5 -10
- package/dist/workspace.js +3 -19
- package/package.json +19 -54
- package/dist/active-sessions-store-CeOmXgv5.d.ts +0 -85
- package/dist/artifact-pane-Bh45Ssco.d.ts +0 -24
- package/dist/branding-DCi5VEik.d.ts +0 -13
- package/dist/button-CMQuQEW_.d.ts +0 -17
- package/dist/chat-container-f4yEs6KN.d.ts +0 -106
- package/dist/chunk-34A66VBG.js +0 -214
- package/dist/chunk-34I7UFSX.js +0 -92
- package/dist/chunk-36QY2W5G.js +0 -802
- package/dist/chunk-4CLN43XT.js +0 -45
- package/dist/chunk-54SQQMMM.js +0 -156
- package/dist/chunk-66EZOYZR.js +0 -102
- package/dist/chunk-BX6AQMUS.js +0 -183
- package/dist/chunk-DI3NZ5ZX.js +0 -192
- package/dist/chunk-DPGIXDAI.js +0 -220
- package/dist/chunk-DXMIEK4K.js +0 -1426
- package/dist/chunk-GSZA3TSY.js +0 -79
- package/dist/chunk-HB5Y37YU.js +0 -54
- package/dist/chunk-LQNEZDRM.js +0 -109
- package/dist/chunk-MA7YKRUP.js +0 -131
- package/dist/chunk-MKTSMWVD.js +0 -109
- package/dist/chunk-MQXABZTB.js +0 -1348
- package/dist/chunk-MT5FJ3ZT.js +0 -186
- package/dist/chunk-NKUPJC34.js +0 -2070
- package/dist/chunk-OEX7NZE3.js +0 -321
- package/dist/chunk-OKLQVY3Y.js +0 -139
- package/dist/chunk-Q56BYXQF.js +0 -61
- package/dist/chunk-QD4QE5P5.js +0 -40
- package/dist/chunk-QDH5GEGY.js +0 -630
- package/dist/chunk-QID2OOMG.js +0 -133
- package/dist/chunk-RQHJBTEU.js +0 -10
- package/dist/chunk-T7HMZEVO.js +0 -216
- package/dist/chunk-U6QTHMY6.js +0 -1290
- package/dist/chunk-US6JKJKH.js +0 -124
- package/dist/chunk-VX3XOUEB.js +0 -63
- package/dist/chunk-XLG757B6.js +0 -933
- package/dist/chunk-ZMNSRDMH.js +0 -127
- package/dist/chunk-ZNCEM5CD.js +0 -316
- package/dist/document-editor-pane-A70-EhdQ.d.ts +0 -124
- package/dist/document-editor-pane-TLPVRBBU.js +0 -11
- package/dist/expanded-tool-detail-Dh99mcbY.d.ts +0 -63
- package/dist/file-tabs-BLfxfmAH.d.ts +0 -51
- package/dist/parts-CyGkM6Fp.d.ts +0 -50
- package/dist/run-CtFZ6s-D.d.ts +0 -41
- package/dist/sidebar-drop-zone-tDBsuOH5.d.ts +0 -301
- package/dist/sidecar-CFU2W9j1.d.ts +0 -8
- package/dist/template-card-BAtvcAkU.d.ts +0 -18
- package/dist/tool-call-feed-Bs3MyQMT.d.ts +0 -68
- package/dist/tool-display-Ct9nFAzJ.d.ts +0 -32
- package/dist/use-sandbox-metrics-DWc0k9Xm.d.ts +0 -153
- package/dist/variant-list-BrHYcBCk.d.ts +0 -540
package/dist/dashboard.js
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
TemplateCard
|
|
4
|
-
} from "./chunk-VX3XOUEB.js";
|
|
5
|
-
import {
|
|
2
|
+
BackendConfig,
|
|
6
3
|
BackendSelector,
|
|
7
4
|
ClusterStatusBar,
|
|
8
5
|
CreditBalance,
|
|
9
6
|
DashboardLayout,
|
|
7
|
+
GitPanel,
|
|
10
8
|
HARNESS_OPTIONS,
|
|
11
9
|
HarnessPicker,
|
|
12
10
|
InvoiceTable,
|
|
13
11
|
ModelPicker,
|
|
12
|
+
NetworkConfig,
|
|
14
13
|
NewSandboxCard,
|
|
15
14
|
PlanCards,
|
|
15
|
+
PortsList,
|
|
16
|
+
ProcessList,
|
|
16
17
|
ProfileAvatar,
|
|
17
18
|
ProfileComparison,
|
|
18
19
|
ProfileSelector,
|
|
20
|
+
PromoBanner,
|
|
19
21
|
RailButton,
|
|
20
22
|
RailModeButton,
|
|
21
23
|
RailSeparator,
|
|
@@ -36,899 +38,26 @@ import {
|
|
|
36
38
|
SidebarRailFooter,
|
|
37
39
|
SidebarRailHeader,
|
|
38
40
|
SidebarRailNav,
|
|
41
|
+
SnapshotList,
|
|
42
|
+
SystemLogsViewer,
|
|
43
|
+
UsageSummary,
|
|
39
44
|
VariantList,
|
|
40
45
|
canAdminSandbox,
|
|
41
46
|
canonicalModelId,
|
|
42
47
|
formatContext,
|
|
43
48
|
formatPricing,
|
|
44
49
|
useSidebar
|
|
45
|
-
} from "./chunk-
|
|
50
|
+
} from "./chunk-AHBZCBDO.js";
|
|
46
51
|
import {
|
|
47
52
|
BillingDashboard,
|
|
53
|
+
InfoPanel,
|
|
48
54
|
PricingPage,
|
|
55
|
+
TemplateCard,
|
|
49
56
|
UsageChart,
|
|
50
57
|
formatPrice
|
|
51
|
-
} from "./chunk-
|
|
52
|
-
import
|
|
53
|
-
|
|
54
|
-
} from "./chunk-OKLQVY3Y.js";
|
|
55
|
-
import "./chunk-34A66VBG.js";
|
|
56
|
-
import {
|
|
57
|
-
Skeleton
|
|
58
|
-
} from "./chunk-66EZOYZR.js";
|
|
59
|
-
import "./chunk-ZMNSRDMH.js";
|
|
60
|
-
import "./chunk-MKTSMWVD.js";
|
|
61
|
-
import {
|
|
62
|
-
cn
|
|
63
|
-
} from "./chunk-RQHJBTEU.js";
|
|
64
|
-
|
|
65
|
-
// src/dashboard/system-logs.tsx
|
|
66
|
-
import { Terminal } from "lucide-react";
|
|
67
|
-
import { useEffect, useRef, useState } from "react";
|
|
68
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
69
|
-
function SystemLogsViewer({ apiUrl, token, className }) {
|
|
70
|
-
const [logs, setLogs] = useState([]);
|
|
71
|
-
const [error, setError] = useState(null);
|
|
72
|
-
const [isFollowing, setIsFollowing] = useState(true);
|
|
73
|
-
const scrollRef = useRef(null);
|
|
74
|
-
useEffect(() => {
|
|
75
|
-
let timeoutId;
|
|
76
|
-
let backoff = 2e3;
|
|
77
|
-
const controller = new AbortController();
|
|
78
|
-
async function fetchLogs() {
|
|
79
|
-
try {
|
|
80
|
-
const res = await fetch(`${apiUrl}/debug/logs`, {
|
|
81
|
-
headers: {
|
|
82
|
-
Authorization: `Bearer ${token}`
|
|
83
|
-
},
|
|
84
|
-
signal: controller.signal
|
|
85
|
-
});
|
|
86
|
-
if (!res.ok) throw new Error(`HTTP Error: ${res.status}`);
|
|
87
|
-
const data = await res.json();
|
|
88
|
-
if (!controller.signal.aborted) {
|
|
89
|
-
setLogs(data.logs || []);
|
|
90
|
-
setError(null);
|
|
91
|
-
backoff = 2e3;
|
|
92
|
-
}
|
|
93
|
-
} catch (err) {
|
|
94
|
-
if (err.name === "AbortError") return;
|
|
95
|
-
if (!controller.signal.aborted) {
|
|
96
|
-
setError(err instanceof Error ? err.message : "Failed to fetch logs");
|
|
97
|
-
backoff = Math.min(backoff * 2, 3e4);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
if (!controller.signal.aborted) {
|
|
101
|
-
timeoutId = setTimeout(fetchLogs, backoff);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
fetchLogs();
|
|
105
|
-
return () => {
|
|
106
|
-
controller.abort();
|
|
107
|
-
clearTimeout(timeoutId);
|
|
108
|
-
};
|
|
109
|
-
}, [apiUrl, token]);
|
|
110
|
-
useEffect(() => {
|
|
111
|
-
if (isFollowing && scrollRef.current) {
|
|
112
|
-
scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
|
|
113
|
-
}
|
|
114
|
-
}, [logs, isFollowing]);
|
|
115
|
-
const handleScroll = () => {
|
|
116
|
-
if (!scrollRef.current) return;
|
|
117
|
-
const { scrollTop, scrollHeight, clientHeight } = scrollRef.current;
|
|
118
|
-
const isAtBottom = scrollHeight - scrollTop - clientHeight < 20;
|
|
119
|
-
setIsFollowing((prev) => prev === isAtBottom ? prev : isAtBottom);
|
|
120
|
-
};
|
|
121
|
-
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col h-full bg-background text-foreground font-mono text-sm leading-relaxed overflow-hidden rounded-lg border border-border", className), children: [
|
|
122
|
-
/* @__PURE__ */ jsxs("div", { className: "flex-none flex items-center justify-between border-b border-border bg-muted/50 backdrop-blur-md px-4 py-2", children: [
|
|
123
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
124
|
-
/* @__PURE__ */ jsx(Terminal, { className: "h-4 w-4 text-primary animate-pulse" }),
|
|
125
|
-
/* @__PURE__ */ jsx("span", { className: "font-bold text-xs uppercase tracking-widest text-muted-foreground", children: "System Traces" })
|
|
126
|
-
] }),
|
|
127
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
128
|
-
error && /* @__PURE__ */ jsxs("span", { className: "text-destructive text-xs flex items-center gap-1", children: [
|
|
129
|
-
/* @__PURE__ */ jsx("span", { className: "w-2 h-2 rounded-full bg-destructive animate-ping" }),
|
|
130
|
-
" Error fetching logs"
|
|
131
|
-
] }),
|
|
132
|
-
/* @__PURE__ */ jsx(
|
|
133
|
-
"button",
|
|
134
|
-
{
|
|
135
|
-
onClick: () => {
|
|
136
|
-
setIsFollowing(!isFollowing);
|
|
137
|
-
if (!isFollowing && scrollRef.current) {
|
|
138
|
-
scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
|
|
139
|
-
}
|
|
140
|
-
},
|
|
141
|
-
className: cn("px-3 py-1 text-[10px] font-bold uppercase rounded-md transition-colors", isFollowing ? "bg-primary/20 text-primary border border-primary/20" : "bg-muted text-muted-foreground border border-border hover:bg-accent hover:text-foreground"),
|
|
142
|
-
children: isFollowing ? "Auto-Scroll ON" : "Auto-Scroll OFF"
|
|
143
|
-
}
|
|
144
|
-
)
|
|
145
|
-
] })
|
|
146
|
-
] }),
|
|
147
|
-
/* @__PURE__ */ jsx(
|
|
148
|
-
"div",
|
|
149
|
-
{
|
|
150
|
-
ref: scrollRef,
|
|
151
|
-
onScroll: handleScroll,
|
|
152
|
-
className: "flex-1 overflow-y-auto p-4 space-y-1",
|
|
153
|
-
children: logs.length === 0 && !error ? /* @__PURE__ */ jsx("div", { className: "flex h-full items-center justify-center text-muted-foreground italic", children: "Waiting for orchestrator logs..." }) : logs.map((log, i) => /* @__PURE__ */ jsxs("div", { className: "break-words", children: [
|
|
154
|
-
/* @__PURE__ */ jsxs("span", { className: "text-muted-foreground mr-3 select-none", children: [
|
|
155
|
-
"[",
|
|
156
|
-
log.timestamp || i.toString().padStart(4, "0"),
|
|
157
|
-
"]"
|
|
158
|
-
] }),
|
|
159
|
-
/* @__PURE__ */ jsxs("span", { className: "text-primary/70 mr-2", children: [
|
|
160
|
-
"[",
|
|
161
|
-
log.level,
|
|
162
|
-
"]"
|
|
163
|
-
] }),
|
|
164
|
-
/* @__PURE__ */ jsxs("span", { className: "text-muted-foreground mr-2", children: [
|
|
165
|
-
"[",
|
|
166
|
-
log.scope,
|
|
167
|
-
"]"
|
|
168
|
-
] }),
|
|
169
|
-
/* @__PURE__ */ jsx("span", { className: log.level.toUpperCase() === "ERROR" || log.message.toLowerCase().includes("failed") ? "text-destructive" : log.level.toUpperCase() === "WARN" ? "text-warning" : "text-foreground", children: log.message })
|
|
170
|
-
] }, `${log.timestamp}-${log.scope}-${i}`))
|
|
171
|
-
}
|
|
172
|
-
)
|
|
173
|
-
] });
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// src/dashboard/usage-summary.tsx
|
|
177
|
-
import { Clock, Layers, MessageSquare, DollarSign } from "lucide-react";
|
|
178
|
-
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
179
|
-
function UsageSummary({ data, loading = false, className }) {
|
|
180
|
-
if (loading || !data) {
|
|
181
|
-
return /* @__PURE__ */ jsx2("div", { className: cn("grid grid-cols-2 gap-4 lg:grid-cols-4", className), children: Array.from({ length: 4 }).map((_, i) => /* @__PURE__ */ jsx2(Skeleton, { className: "h-28 rounded-xl" }, i)) });
|
|
182
|
-
}
|
|
183
|
-
return /* @__PURE__ */ jsxs2("div", { className: cn("grid grid-cols-2 gap-4 lg:grid-cols-4", className), children: [
|
|
184
|
-
/* @__PURE__ */ jsx2(
|
|
185
|
-
StatCard,
|
|
186
|
-
{
|
|
187
|
-
variant: "sandbox",
|
|
188
|
-
title: "Compute Hours",
|
|
189
|
-
value: data.computeHours.toFixed(1),
|
|
190
|
-
subtitle: "This billing period",
|
|
191
|
-
icon: /* @__PURE__ */ jsx2(Clock, { className: "h-5 w-5" })
|
|
192
|
-
}
|
|
193
|
-
),
|
|
194
|
-
/* @__PURE__ */ jsx2(
|
|
195
|
-
StatCard,
|
|
196
|
-
{
|
|
197
|
-
variant: "sandbox",
|
|
198
|
-
title: "Active Sessions",
|
|
199
|
-
value: data.activeSessions,
|
|
200
|
-
subtitle: "Currently running",
|
|
201
|
-
icon: /* @__PURE__ */ jsx2(Layers, { className: "h-5 w-5" })
|
|
202
|
-
}
|
|
203
|
-
),
|
|
204
|
-
/* @__PURE__ */ jsx2(
|
|
205
|
-
StatCard,
|
|
206
|
-
{
|
|
207
|
-
variant: "sandbox",
|
|
208
|
-
title: "Messages Sent",
|
|
209
|
-
value: data.messagesSent.toLocaleString(),
|
|
210
|
-
subtitle: "Agent interactions",
|
|
211
|
-
icon: /* @__PURE__ */ jsx2(MessageSquare, { className: "h-5 w-5" })
|
|
212
|
-
}
|
|
213
|
-
),
|
|
214
|
-
/* @__PURE__ */ jsx2(
|
|
215
|
-
StatCard,
|
|
216
|
-
{
|
|
217
|
-
variant: "sandbox",
|
|
218
|
-
title: "Estimated Cost",
|
|
219
|
-
value: `$${data.estimatedCost.toFixed(2)}`,
|
|
220
|
-
subtitle: "This billing period",
|
|
221
|
-
icon: /* @__PURE__ */ jsx2(DollarSign, { className: "h-5 w-5" })
|
|
222
|
-
}
|
|
223
|
-
)
|
|
224
|
-
] });
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// src/dashboard/git-panel.tsx
|
|
228
|
-
import { GitBranch, GitCommit, FileEdit, FilePlus, File } from "lucide-react";
|
|
229
|
-
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
230
|
-
function GitPanel({ status, log, loading = false, onRefresh, className }) {
|
|
231
|
-
if (loading) {
|
|
232
|
-
return /* @__PURE__ */ jsx3("div", { className: cn("rounded-lg border border-border bg-card p-5", className), children: /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
|
|
233
|
-
/* @__PURE__ */ jsx3("div", { className: "h-4 w-4 animate-spin rounded-full border-2 border-primary border-t-transparent" }),
|
|
234
|
-
"Loading git info..."
|
|
235
|
-
] }) });
|
|
236
|
-
}
|
|
237
|
-
if (!status) {
|
|
238
|
-
return /* @__PURE__ */ jsx3("div", { className: cn("rounded-lg border border-border bg-card p-5", className), children: /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
|
|
239
|
-
/* @__PURE__ */ jsx3(GitBranch, { className: "h-4 w-4" }),
|
|
240
|
-
"No git repository detected"
|
|
241
|
-
] }) });
|
|
242
|
-
}
|
|
243
|
-
const changedCount = status.staged.length + status.modified.length + status.untracked.length;
|
|
244
|
-
return /* @__PURE__ */ jsxs3("div", { className: cn("rounded-lg border border-border bg-card overflow-hidden", className), children: [
|
|
245
|
-
/* @__PURE__ */ jsxs3("div", { className: "flex items-center justify-between border-b border-border bg-muted/30 px-4 py-3", children: [
|
|
246
|
-
/* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2", children: [
|
|
247
|
-
/* @__PURE__ */ jsx3(GitBranch, { className: "h-4 w-4 text-primary" }),
|
|
248
|
-
/* @__PURE__ */ jsx3("span", { className: "text-sm font-bold text-foreground", children: status.branch }),
|
|
249
|
-
status.isDirty && /* @__PURE__ */ jsxs3("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: [
|
|
250
|
-
changedCount,
|
|
251
|
-
" change",
|
|
252
|
-
changedCount !== 1 ? "s" : ""
|
|
253
|
-
] })
|
|
254
|
-
] }),
|
|
255
|
-
onRefresh && /* @__PURE__ */ jsx3("button", { type: "button", onClick: onRefresh, className: "text-xs text-muted-foreground hover:text-foreground transition-colors", children: "Refresh" })
|
|
256
|
-
] }),
|
|
257
|
-
changedCount > 0 && /* @__PURE__ */ jsx3("div", { className: "border-b border-border px-4 py-3", children: /* @__PURE__ */ jsxs3("div", { className: "space-y-1.5", children: [
|
|
258
|
-
status.staged.map((f) => /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2 text-xs", children: [
|
|
259
|
-
/* @__PURE__ */ jsx3(FilePlus, { className: "h-3 w-3 text-[var(--surface-success-text)]" }),
|
|
260
|
-
/* @__PURE__ */ jsx3("span", { className: "font-mono text-foreground truncate", children: f }),
|
|
261
|
-
/* @__PURE__ */ jsx3("span", { className: "text-[var(--surface-success-text)] text-[10px] font-bold ml-auto", children: "STAGED" })
|
|
262
|
-
] }, `s-${f}`)),
|
|
263
|
-
status.modified.map((f) => /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2 text-xs", children: [
|
|
264
|
-
/* @__PURE__ */ jsx3(FileEdit, { className: "h-3 w-3 text-[var(--surface-warning-text)]" }),
|
|
265
|
-
/* @__PURE__ */ jsx3("span", { className: "font-mono text-foreground truncate", children: f })
|
|
266
|
-
] }, `m-${f}`)),
|
|
267
|
-
status.untracked.map((f) => /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2 text-xs", children: [
|
|
268
|
-
/* @__PURE__ */ jsx3(File, { className: "h-3 w-3 text-muted-foreground" }),
|
|
269
|
-
/* @__PURE__ */ jsx3("span", { className: "font-mono text-muted-foreground truncate", children: f })
|
|
270
|
-
] }, `u-${f}`))
|
|
271
|
-
] }) }),
|
|
272
|
-
log.length > 0 && /* @__PURE__ */ jsxs3("div", { className: "px-4 py-3", children: [
|
|
273
|
-
/* @__PURE__ */ jsx3("div", { className: "mb-2 text-[10px] font-bold uppercase tracking-widest text-muted-foreground", children: "Recent Commits" }),
|
|
274
|
-
/* @__PURE__ */ jsx3("div", { className: "space-y-2", children: log.slice(0, 5).map((commit) => /* @__PURE__ */ jsxs3("div", { className: "flex items-start gap-2 text-xs", children: [
|
|
275
|
-
/* @__PURE__ */ jsx3(GitCommit, { className: "h-3 w-3 text-muted-foreground mt-0.5 shrink-0" }),
|
|
276
|
-
/* @__PURE__ */ jsxs3("div", { className: "min-w-0", children: [
|
|
277
|
-
/* @__PURE__ */ jsx3("span", { className: "font-mono text-primary mr-1.5", children: commit.shortSha }),
|
|
278
|
-
/* @__PURE__ */ jsx3("span", { className: "text-foreground", children: commit.message })
|
|
279
|
-
] })
|
|
280
|
-
] }, commit.shortSha)) })
|
|
281
|
-
] }),
|
|
282
|
-
(status.ahead > 0 || status.behind > 0) && /* @__PURE__ */ jsxs3("div", { className: "border-t border-border px-4 py-2 flex items-center gap-3 text-xs text-muted-foreground", children: [
|
|
283
|
-
status.ahead > 0 && /* @__PURE__ */ jsxs3("span", { children: [
|
|
284
|
-
"\u2191 ",
|
|
285
|
-
status.ahead,
|
|
286
|
-
" ahead"
|
|
287
|
-
] }),
|
|
288
|
-
status.behind > 0 && /* @__PURE__ */ jsxs3("span", { children: [
|
|
289
|
-
"\u2193 ",
|
|
290
|
-
status.behind,
|
|
291
|
-
" behind"
|
|
292
|
-
] })
|
|
293
|
-
] })
|
|
294
|
-
] });
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
// src/dashboard/ports-list.tsx
|
|
298
|
-
import * as React2 from "react";
|
|
299
|
-
import { Copy, Check, Globe, Plus, Trash2 } from "lucide-react";
|
|
300
|
-
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
301
|
-
function PortsList({ ports, onExposePort, onRemovePort, isExposing = false, className }) {
|
|
302
|
-
const [newPort, setNewPort] = React2.useState("");
|
|
303
|
-
const [copiedPort, setCopiedPort] = React2.useState(null);
|
|
304
|
-
const copyTimerRef = React2.useRef(null);
|
|
305
|
-
React2.useEffect(() => {
|
|
306
|
-
return () => {
|
|
307
|
-
if (copyTimerRef.current) clearTimeout(copyTimerRef.current);
|
|
308
|
-
};
|
|
309
|
-
}, []);
|
|
310
|
-
const handleCopy = async (url, port) => {
|
|
311
|
-
try {
|
|
312
|
-
await navigator.clipboard.writeText(url);
|
|
313
|
-
setCopiedPort(port);
|
|
314
|
-
if (copyTimerRef.current) clearTimeout(copyTimerRef.current);
|
|
315
|
-
copyTimerRef.current = setTimeout(() => setCopiedPort(null), 2e3);
|
|
316
|
-
} catch (err) {
|
|
317
|
-
console.warn("Clipboard write failed:", err);
|
|
318
|
-
}
|
|
319
|
-
};
|
|
320
|
-
const handleExpose = () => {
|
|
321
|
-
const port = parseInt(newPort, 10);
|
|
322
|
-
if (port > 0 && port <= 65535) {
|
|
323
|
-
onExposePort(port);
|
|
324
|
-
setNewPort("");
|
|
325
|
-
}
|
|
326
|
-
};
|
|
327
|
-
return /* @__PURE__ */ jsxs4("div", { className: cn("space-y-4", className), children: [
|
|
328
|
-
ports.length > 0 ? /* @__PURE__ */ jsx4("div", { className: "rounded-lg border border-border overflow-hidden", children: /* @__PURE__ */ jsxs4("table", { className: "w-full text-sm", children: [
|
|
329
|
-
/* @__PURE__ */ jsx4("thead", { className: "bg-muted/30 border-b border-border", children: /* @__PURE__ */ jsxs4("tr", { children: [
|
|
330
|
-
/* @__PURE__ */ jsx4("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Port" }),
|
|
331
|
-
/* @__PURE__ */ jsx4("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Public URL" }),
|
|
332
|
-
/* @__PURE__ */ jsx4("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Status" }),
|
|
333
|
-
/* @__PURE__ */ jsx4("th", { className: "px-4 py-2.5 text-right text-xs font-medium text-muted-foreground w-20" })
|
|
334
|
-
] }) }),
|
|
335
|
-
/* @__PURE__ */ jsx4("tbody", { className: "divide-y divide-border", children: ports.map((p) => /* @__PURE__ */ jsxs4("tr", { children: [
|
|
336
|
-
/* @__PURE__ */ jsx4("td", { className: "px-4 py-3 font-mono text-xs text-foreground", children: p.port }),
|
|
337
|
-
/* @__PURE__ */ jsx4("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsxs4(
|
|
338
|
-
"button",
|
|
339
|
-
{
|
|
340
|
-
type: "button",
|
|
341
|
-
onClick: () => handleCopy(p.url, p.port),
|
|
342
|
-
className: "flex items-center gap-2 font-mono text-xs text-primary hover:underline cursor-pointer group",
|
|
343
|
-
children: [
|
|
344
|
-
/* @__PURE__ */ jsx4("span", { className: "truncate max-w-[300px]", children: p.url }),
|
|
345
|
-
copiedPort === p.port ? /* @__PURE__ */ jsx4(Check, { className: "h-3 w-3 text-[var(--surface-success-text)] shrink-0" }) : /* @__PURE__ */ jsx4(Copy, { className: "h-3 w-3 opacity-0 group-hover:opacity-100 transition-opacity shrink-0" })
|
|
346
|
-
]
|
|
347
|
-
}
|
|
348
|
-
) }),
|
|
349
|
-
/* @__PURE__ */ jsx4("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsx4("span", { className: cn(
|
|
350
|
-
"inline-flex items-center gap-1.5 rounded-full px-2 py-0.5 text-[10px] font-bold uppercase tracking-wider",
|
|
351
|
-
p.status === "active" ? "bg-[var(--surface-success-bg)] text-[var(--surface-success-text)]" : "bg-[var(--surface-warning-bg)] text-[var(--surface-warning-text)]"
|
|
352
|
-
), children: p.status }) }),
|
|
353
|
-
/* @__PURE__ */ jsx4("td", { className: "px-4 py-3 text-right", children: onRemovePort && /* @__PURE__ */ jsx4(
|
|
354
|
-
"button",
|
|
355
|
-
{
|
|
356
|
-
type: "button",
|
|
357
|
-
onClick: () => onRemovePort(p.port),
|
|
358
|
-
className: "p-1 text-muted-foreground hover:text-destructive transition-colors rounded",
|
|
359
|
-
children: /* @__PURE__ */ jsx4(Trash2, { className: "h-3.5 w-3.5" })
|
|
360
|
-
}
|
|
361
|
-
) })
|
|
362
|
-
] }, p.port)) })
|
|
363
|
-
] }) }) : /* @__PURE__ */ jsxs4("div", { className: "rounded-lg border border-border bg-muted/20 p-6 text-center", children: [
|
|
364
|
-
/* @__PURE__ */ jsx4(Globe, { className: "mx-auto h-8 w-8 text-muted-foreground mb-2" }),
|
|
365
|
-
/* @__PURE__ */ jsx4("p", { className: "text-sm text-muted-foreground", children: "No ports exposed yet" })
|
|
366
|
-
] }),
|
|
367
|
-
/* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-3", children: [
|
|
368
|
-
/* @__PURE__ */ jsx4(
|
|
369
|
-
"input",
|
|
370
|
-
{
|
|
371
|
-
type: "number",
|
|
372
|
-
min: 1,
|
|
373
|
-
max: 65535,
|
|
374
|
-
placeholder: "Port (e.g. 3000)",
|
|
375
|
-
value: newPort,
|
|
376
|
-
onChange: (e) => setNewPort(e.target.value),
|
|
377
|
-
onKeyDown: (e) => e.key === "Enter" && handleExpose(),
|
|
378
|
-
className: "flex-1 rounded-lg border border-border bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring"
|
|
379
|
-
}
|
|
380
|
-
),
|
|
381
|
-
/* @__PURE__ */ jsxs4(
|
|
382
|
-
"button",
|
|
383
|
-
{
|
|
384
|
-
type: "button",
|
|
385
|
-
onClick: handleExpose,
|
|
386
|
-
disabled: !newPort || isExposing,
|
|
387
|
-
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",
|
|
388
|
-
children: [
|
|
389
|
-
/* @__PURE__ */ jsx4(Plus, { className: "h-4 w-4" }),
|
|
390
|
-
"Expose"
|
|
391
|
-
]
|
|
392
|
-
}
|
|
393
|
-
)
|
|
394
|
-
] })
|
|
395
|
-
] });
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
// src/dashboard/process-list.tsx
|
|
399
|
-
import * as React3 from "react";
|
|
400
|
-
import { Activity, Plus as Plus2, Skull, Terminal as Terminal2 } from "lucide-react";
|
|
401
|
-
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
402
|
-
function formatUptime(startedAt) {
|
|
403
|
-
if (!startedAt) return "-";
|
|
404
|
-
const ms = Date.now() - new Date(startedAt).getTime();
|
|
405
|
-
if (Number.isNaN(ms) || ms < 0) return "-";
|
|
406
|
-
if (ms < 6e4) return `${Math.floor(ms / 1e3)}s`;
|
|
407
|
-
if (ms < 36e5) return `${Math.floor(ms / 6e4)}m`;
|
|
408
|
-
return `${Math.floor(ms / 36e5)}h ${Math.floor(ms % 36e5 / 6e4)}m`;
|
|
409
|
-
}
|
|
410
|
-
function ProcessList({ processes, onSpawn, onKill, loading = false, className }) {
|
|
411
|
-
const [newCommand, setNewCommand] = React3.useState("");
|
|
412
|
-
const handleSpawn = () => {
|
|
413
|
-
const cmd = newCommand.trim();
|
|
414
|
-
if (cmd) {
|
|
415
|
-
onSpawn(cmd);
|
|
416
|
-
setNewCommand("");
|
|
417
|
-
}
|
|
418
|
-
};
|
|
419
|
-
return /* @__PURE__ */ jsxs5("div", { className: cn("space-y-4", className), children: [
|
|
420
|
-
loading ? /* @__PURE__ */ jsxs5("div", { className: "rounded-lg border border-border bg-muted/20 p-6 text-center", children: [
|
|
421
|
-
/* @__PURE__ */ jsx5(Activity, { className: "mx-auto h-6 w-6 text-muted-foreground animate-spin mb-2" }),
|
|
422
|
-
/* @__PURE__ */ jsx5("p", { className: "text-sm text-muted-foreground", children: "Loading processes..." })
|
|
423
|
-
] }) : processes.length > 0 ? /* @__PURE__ */ jsx5("div", { className: "rounded-lg border border-border overflow-hidden", children: /* @__PURE__ */ jsxs5("table", { className: "w-full text-sm", children: [
|
|
424
|
-
/* @__PURE__ */ jsx5("thead", { className: "bg-muted/30 border-b border-border", children: /* @__PURE__ */ jsxs5("tr", { children: [
|
|
425
|
-
/* @__PURE__ */ jsx5("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "PID" }),
|
|
426
|
-
/* @__PURE__ */ jsx5("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Command" }),
|
|
427
|
-
/* @__PURE__ */ jsx5("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Status" }),
|
|
428
|
-
/* @__PURE__ */ jsx5("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Uptime" }),
|
|
429
|
-
/* @__PURE__ */ jsx5("th", { className: "px-4 py-2.5 text-right text-xs font-medium text-muted-foreground w-20" })
|
|
430
|
-
] }) }),
|
|
431
|
-
/* @__PURE__ */ jsx5("tbody", { className: "divide-y divide-border", children: processes.map((p) => /* @__PURE__ */ jsxs5("tr", { children: [
|
|
432
|
-
/* @__PURE__ */ jsx5("td", { className: "px-4 py-3 font-mono text-xs text-foreground", children: p.pid }),
|
|
433
|
-
/* @__PURE__ */ jsx5("td", { className: "px-4 py-3 font-mono text-xs text-foreground truncate max-w-[250px]", children: p.command }),
|
|
434
|
-
/* @__PURE__ */ jsx5("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsx5("span", { className: cn(
|
|
435
|
-
"inline-flex items-center gap-1.5 rounded-full px-2 py-0.5 text-[10px] font-bold uppercase tracking-wider",
|
|
436
|
-
p.running ? "bg-[var(--surface-success-bg)] text-[var(--surface-success-text)]" : "bg-muted text-muted-foreground"
|
|
437
|
-
), children: p.running ? "running" : `exited (${p.exitCode ?? "?"})` }) }),
|
|
438
|
-
/* @__PURE__ */ jsx5("td", { className: "px-4 py-3 font-mono text-xs text-muted-foreground", children: formatUptime(p.startedAt) }),
|
|
439
|
-
/* @__PURE__ */ jsx5("td", { className: "px-4 py-3 text-right", children: p.running && /* @__PURE__ */ jsx5(
|
|
440
|
-
"button",
|
|
441
|
-
{
|
|
442
|
-
type: "button",
|
|
443
|
-
onClick: () => onKill(p.pid),
|
|
444
|
-
className: "p-1 text-muted-foreground hover:text-destructive transition-colors rounded",
|
|
445
|
-
title: "Kill process",
|
|
446
|
-
children: /* @__PURE__ */ jsx5(Skull, { className: "h-3.5 w-3.5" })
|
|
447
|
-
}
|
|
448
|
-
) })
|
|
449
|
-
] }, `${p.pid}-${p.startedAt ?? p.command}`)) })
|
|
450
|
-
] }) }) : /* @__PURE__ */ jsxs5("div", { className: "rounded-lg border border-border bg-muted/20 p-6 text-center", children: [
|
|
451
|
-
/* @__PURE__ */ jsx5(Terminal2, { className: "mx-auto h-8 w-8 text-muted-foreground mb-2" }),
|
|
452
|
-
/* @__PURE__ */ jsx5("p", { className: "text-sm text-muted-foreground", children: "No processes running" })
|
|
453
|
-
] }),
|
|
454
|
-
/* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-3", children: [
|
|
455
|
-
/* @__PURE__ */ jsx5(
|
|
456
|
-
"input",
|
|
457
|
-
{
|
|
458
|
-
type: "text",
|
|
459
|
-
placeholder: "Command (e.g. node server.js)",
|
|
460
|
-
value: newCommand,
|
|
461
|
-
onChange: (e) => setNewCommand(e.target.value),
|
|
462
|
-
onKeyDown: (e) => e.key === "Enter" && handleSpawn(),
|
|
463
|
-
className: "flex-1 rounded-lg border border-border bg-background px-3 py-2 text-sm font-mono text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring"
|
|
464
|
-
}
|
|
465
|
-
),
|
|
466
|
-
/* @__PURE__ */ jsxs5(
|
|
467
|
-
"button",
|
|
468
|
-
{
|
|
469
|
-
type: "button",
|
|
470
|
-
onClick: handleSpawn,
|
|
471
|
-
disabled: !newCommand.trim(),
|
|
472
|
-
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",
|
|
473
|
-
children: [
|
|
474
|
-
/* @__PURE__ */ jsx5(Plus2, { className: "h-4 w-4" }),
|
|
475
|
-
"Spawn"
|
|
476
|
-
]
|
|
477
|
-
}
|
|
478
|
-
)
|
|
479
|
-
] })
|
|
480
|
-
] });
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
// src/dashboard/network-config.tsx
|
|
484
|
-
import * as React4 from "react";
|
|
485
|
-
import { Network, Plus as Plus3, Trash2 as Trash22, ShieldAlert } from "lucide-react";
|
|
486
|
-
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
487
|
-
function NetworkConfig({ config, onUpdate, loading = false, className }) {
|
|
488
|
-
const [newCidr, setNewCidr] = React4.useState("");
|
|
489
|
-
const isValidCidr = (value) => {
|
|
490
|
-
const match = value.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\/(\d{1,2})$/);
|
|
491
|
-
if (!match) return false;
|
|
492
|
-
const octets = [match[1], match[2], match[3], match[4]].map(Number);
|
|
493
|
-
const prefix = Number(match[5]);
|
|
494
|
-
return octets.every((o) => o <= 255) && prefix <= 32;
|
|
495
|
-
};
|
|
496
|
-
const handleAddCidr = () => {
|
|
497
|
-
const cidr = newCidr.trim();
|
|
498
|
-
if (cidr && config && isValidCidr(cidr) && !config.allowList.includes(cidr)) {
|
|
499
|
-
onUpdate({ allowList: [...config.allowList, cidr] });
|
|
500
|
-
setNewCidr("");
|
|
501
|
-
}
|
|
502
|
-
};
|
|
503
|
-
const handleRemoveCidr = (cidr) => {
|
|
504
|
-
if (config) {
|
|
505
|
-
onUpdate({ allowList: config.allowList.filter((c) => c !== cidr) });
|
|
506
|
-
}
|
|
507
|
-
};
|
|
508
|
-
if (loading || !config) {
|
|
509
|
-
return /* @__PURE__ */ jsxs6("div", { className: cn("rounded-lg border border-border bg-muted/20 p-6 text-center", className), children: [
|
|
510
|
-
/* @__PURE__ */ jsx6(Network, { className: "mx-auto h-6 w-6 text-muted-foreground animate-pulse mb-2" }),
|
|
511
|
-
/* @__PURE__ */ jsx6("p", { className: "text-sm text-muted-foreground", children: "Loading network configuration..." })
|
|
512
|
-
] });
|
|
513
|
-
}
|
|
514
|
-
return /* @__PURE__ */ jsxs6("div", { className: cn("space-y-4", className), children: [
|
|
515
|
-
/* @__PURE__ */ jsxs6("div", { className: "flex items-center justify-between rounded-lg border border-border bg-card px-4 py-3", children: [
|
|
516
|
-
/* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-3", children: [
|
|
517
|
-
/* @__PURE__ */ jsx6(ShieldAlert, { className: "h-4 w-4 text-muted-foreground" }),
|
|
518
|
-
/* @__PURE__ */ jsxs6("div", { children: [
|
|
519
|
-
/* @__PURE__ */ jsx6("p", { className: "text-sm font-medium text-foreground", children: "Block Outbound Traffic" }),
|
|
520
|
-
/* @__PURE__ */ jsx6("p", { className: "text-xs text-muted-foreground", children: "Prevent the sandbox from making external network requests" })
|
|
521
|
-
] })
|
|
522
|
-
] }),
|
|
523
|
-
/* @__PURE__ */ jsx6(
|
|
524
|
-
"button",
|
|
525
|
-
{
|
|
526
|
-
type: "button",
|
|
527
|
-
role: "switch",
|
|
528
|
-
"aria-checked": config.blockOutbound,
|
|
529
|
-
"aria-label": "Block outbound traffic",
|
|
530
|
-
onClick: () => onUpdate({ blockOutbound: !config.blockOutbound }),
|
|
531
|
-
className: cn(
|
|
532
|
-
"relative inline-flex h-6 w-11 items-center rounded-full transition-colors",
|
|
533
|
-
config.blockOutbound ? "bg-destructive" : "bg-muted"
|
|
534
|
-
),
|
|
535
|
-
children: /* @__PURE__ */ jsx6(
|
|
536
|
-
"span",
|
|
537
|
-
{
|
|
538
|
-
className: cn(
|
|
539
|
-
"inline-block h-4 w-4 rounded-full bg-white transition-transform shadow-sm",
|
|
540
|
-
config.blockOutbound ? "translate-x-6" : "translate-x-1"
|
|
541
|
-
)
|
|
542
|
-
}
|
|
543
|
-
)
|
|
544
|
-
}
|
|
545
|
-
)
|
|
546
|
-
] }),
|
|
547
|
-
/* @__PURE__ */ jsxs6("div", { children: [
|
|
548
|
-
/* @__PURE__ */ jsx6("h4", { className: "text-xs font-medium text-muted-foreground mb-2 uppercase tracking-wider", children: "Allowlist (CIDR)" }),
|
|
549
|
-
config.allowList.length > 0 ? /* @__PURE__ */ jsx6("div", { className: "space-y-1.5 mb-3", children: config.allowList.map((cidr) => /* @__PURE__ */ jsxs6("div", { className: "flex items-center justify-between rounded border border-border bg-muted/20 px-3 py-2", children: [
|
|
550
|
-
/* @__PURE__ */ jsx6("span", { className: "font-mono text-xs text-foreground", children: cidr }),
|
|
551
|
-
/* @__PURE__ */ jsx6(
|
|
552
|
-
"button",
|
|
553
|
-
{
|
|
554
|
-
type: "button",
|
|
555
|
-
onClick: () => handleRemoveCidr(cidr),
|
|
556
|
-
className: "p-1 text-muted-foreground hover:text-destructive transition-colors rounded",
|
|
557
|
-
children: /* @__PURE__ */ jsx6(Trash22, { className: "h-3 w-3" })
|
|
558
|
-
}
|
|
559
|
-
)
|
|
560
|
-
] }, cidr)) }) : /* @__PURE__ */ jsx6("p", { className: "text-xs text-muted-foreground mb-3", children: "No CIDR rules configured. All traffic is allowed." }),
|
|
561
|
-
/* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-3", children: [
|
|
562
|
-
/* @__PURE__ */ jsx6(
|
|
563
|
-
"input",
|
|
564
|
-
{
|
|
565
|
-
type: "text",
|
|
566
|
-
placeholder: "CIDR (e.g. 10.0.0.0/8)",
|
|
567
|
-
value: newCidr,
|
|
568
|
-
onChange: (e) => setNewCidr(e.target.value),
|
|
569
|
-
onKeyDown: (e) => e.key === "Enter" && handleAddCidr(),
|
|
570
|
-
className: "flex-1 rounded-lg border border-border bg-background px-3 py-2 text-sm font-mono text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring"
|
|
571
|
-
}
|
|
572
|
-
),
|
|
573
|
-
/* @__PURE__ */ jsxs6(
|
|
574
|
-
"button",
|
|
575
|
-
{
|
|
576
|
-
type: "button",
|
|
577
|
-
onClick: handleAddCidr,
|
|
578
|
-
disabled: !newCidr.trim(),
|
|
579
|
-
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",
|
|
580
|
-
children: [
|
|
581
|
-
/* @__PURE__ */ jsx6(Plus3, { className: "h-4 w-4" }),
|
|
582
|
-
"Add"
|
|
583
|
-
]
|
|
584
|
-
}
|
|
585
|
-
)
|
|
586
|
-
] })
|
|
587
|
-
] })
|
|
588
|
-
] });
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
// src/dashboard/backend-config.tsx
|
|
592
|
-
import * as React5 from "react";
|
|
593
|
-
import { Bot, Plus as Plus4, RefreshCw, Trash2 as Trash23, Server, Wrench } from "lucide-react";
|
|
594
|
-
import { Fragment, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
595
|
-
function BackendConfig({
|
|
596
|
-
status,
|
|
597
|
-
mcpServers,
|
|
598
|
-
onAddMcp,
|
|
599
|
-
onRemoveMcp,
|
|
600
|
-
onRestart,
|
|
601
|
-
loading = false,
|
|
602
|
-
className
|
|
603
|
-
}) {
|
|
604
|
-
const [showAddMcp, setShowAddMcp] = React5.useState(false);
|
|
605
|
-
const [mcpName, setMcpName] = React5.useState("");
|
|
606
|
-
const [mcpCommand, setMcpCommand] = React5.useState("");
|
|
607
|
-
const [mcpArgs, setMcpArgs] = React5.useState("");
|
|
608
|
-
const handleAddMcp = () => {
|
|
609
|
-
const name = mcpName.trim();
|
|
610
|
-
const command = mcpCommand.trim();
|
|
611
|
-
if (name && command) {
|
|
612
|
-
onAddMcp({
|
|
613
|
-
name,
|
|
614
|
-
command,
|
|
615
|
-
args: mcpArgs.trim() ? mcpArgs.trim().split(/\s+/) : void 0
|
|
616
|
-
});
|
|
617
|
-
setMcpName("");
|
|
618
|
-
setMcpCommand("");
|
|
619
|
-
setMcpArgs("");
|
|
620
|
-
setShowAddMcp(false);
|
|
621
|
-
}
|
|
622
|
-
};
|
|
623
|
-
if (loading || !status) {
|
|
624
|
-
return /* @__PURE__ */ jsxs7("div", { className: cn("rounded-lg border border-border bg-muted/20 p-6 text-center", className), children: [
|
|
625
|
-
/* @__PURE__ */ jsx7(Bot, { className: "mx-auto h-6 w-6 text-muted-foreground animate-pulse mb-2" }),
|
|
626
|
-
/* @__PURE__ */ jsx7("p", { className: "text-sm text-muted-foreground", children: "Loading backend status..." })
|
|
627
|
-
] });
|
|
628
|
-
}
|
|
629
|
-
return /* @__PURE__ */ jsxs7("div", { className: cn("space-y-4", className), children: [
|
|
630
|
-
/* @__PURE__ */ jsxs7("div", { className: "rounded-lg border border-border bg-card overflow-hidden", children: [
|
|
631
|
-
/* @__PURE__ */ jsxs7("div", { className: "px-4 py-3 border-b border-border bg-muted/30 flex items-center justify-between", children: [
|
|
632
|
-
/* @__PURE__ */ jsx7("h4", { className: "text-xs font-medium text-muted-foreground uppercase tracking-wider", children: "Agent Status" }),
|
|
633
|
-
/* @__PURE__ */ jsxs7(
|
|
634
|
-
"button",
|
|
635
|
-
{
|
|
636
|
-
type: "button",
|
|
637
|
-
onClick: onRestart,
|
|
638
|
-
className: "inline-flex items-center gap-1.5 rounded-md bg-muted px-2.5 py-1 text-xs font-medium text-foreground hover:bg-muted/80 transition-colors border border-border",
|
|
639
|
-
children: [
|
|
640
|
-
/* @__PURE__ */ jsx7(RefreshCw, { className: "h-3 w-3" }),
|
|
641
|
-
"Restart"
|
|
642
|
-
]
|
|
643
|
-
}
|
|
644
|
-
)
|
|
645
|
-
] }),
|
|
646
|
-
/* @__PURE__ */ jsx7("div", { className: "p-4", children: /* @__PURE__ */ jsxs7("dl", { className: "grid grid-cols-[100px_1fr] gap-y-3 text-sm", children: [
|
|
647
|
-
/* @__PURE__ */ jsx7("dt", { className: "text-muted-foreground", children: "Status" }),
|
|
648
|
-
/* @__PURE__ */ jsx7("dd", { children: /* @__PURE__ */ jsx7("span", { className: cn(
|
|
649
|
-
"inline-flex items-center gap-1.5 rounded-full px-2 py-0.5 text-[10px] font-bold uppercase tracking-wider",
|
|
650
|
-
status.running ? "bg-[var(--surface-success-bg)] text-[var(--surface-success-text)]" : "bg-destructive/10 text-destructive"
|
|
651
|
-
), children: status.running ? "Running" : "Stopped" }) }),
|
|
652
|
-
/* @__PURE__ */ jsx7("dt", { className: "text-muted-foreground", children: "Model" }),
|
|
653
|
-
/* @__PURE__ */ jsx7("dd", { className: "font-mono text-xs", children: status.model ?? "Default" }),
|
|
654
|
-
status.provider && /* @__PURE__ */ jsxs7(Fragment, { children: [
|
|
655
|
-
/* @__PURE__ */ jsx7("dt", { className: "text-muted-foreground", children: "Provider" }),
|
|
656
|
-
/* @__PURE__ */ jsx7("dd", { className: "font-mono text-xs", children: status.provider })
|
|
657
|
-
] })
|
|
658
|
-
] }) })
|
|
659
|
-
] }),
|
|
660
|
-
/* @__PURE__ */ jsxs7("div", { className: "rounded-lg border border-border bg-card overflow-hidden", children: [
|
|
661
|
-
/* @__PURE__ */ jsxs7("div", { className: "px-4 py-3 border-b border-border bg-muted/30 flex items-center justify-between", children: [
|
|
662
|
-
/* @__PURE__ */ jsxs7("h4", { className: "text-xs font-medium text-muted-foreground uppercase tracking-wider flex items-center gap-2", children: [
|
|
663
|
-
/* @__PURE__ */ jsx7(Wrench, { className: "h-3.5 w-3.5" }),
|
|
664
|
-
"MCP Servers"
|
|
665
|
-
] }),
|
|
666
|
-
/* @__PURE__ */ jsxs7(
|
|
667
|
-
"button",
|
|
668
|
-
{
|
|
669
|
-
type: "button",
|
|
670
|
-
onClick: () => setShowAddMcp(!showAddMcp),
|
|
671
|
-
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",
|
|
672
|
-
children: [
|
|
673
|
-
/* @__PURE__ */ jsx7(Plus4, { className: "h-3 w-3" }),
|
|
674
|
-
"Add"
|
|
675
|
-
]
|
|
676
|
-
}
|
|
677
|
-
)
|
|
678
|
-
] }),
|
|
679
|
-
mcpServers.length > 0 ? /* @__PURE__ */ jsx7("div", { className: "divide-y divide-border", children: mcpServers.map((s) => /* @__PURE__ */ jsxs7("div", { className: "flex items-center justify-between px-4 py-3", children: [
|
|
680
|
-
/* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-3 min-w-0", children: [
|
|
681
|
-
/* @__PURE__ */ jsx7(Server, { className: "h-3.5 w-3.5 text-muted-foreground shrink-0" }),
|
|
682
|
-
/* @__PURE__ */ jsxs7("div", { className: "min-w-0", children: [
|
|
683
|
-
/* @__PURE__ */ jsx7("p", { className: "text-sm font-medium text-foreground truncate", children: s.name }),
|
|
684
|
-
/* @__PURE__ */ jsxs7("p", { className: "text-xs font-mono text-muted-foreground truncate", children: [
|
|
685
|
-
s.command,
|
|
686
|
-
" ",
|
|
687
|
-
s.args?.join(" ") ?? ""
|
|
688
|
-
] })
|
|
689
|
-
] })
|
|
690
|
-
] }),
|
|
691
|
-
/* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-2 shrink-0", children: [
|
|
692
|
-
s.status && /* @__PURE__ */ jsx7("span", { className: cn(
|
|
693
|
-
"inline-flex items-center rounded-full px-1.5 py-0.5 text-[9px] font-bold uppercase",
|
|
694
|
-
s.status === "running" ? "bg-[var(--surface-success-bg)] text-[var(--surface-success-text)]" : s.status === "error" ? "bg-destructive/10 text-destructive" : "bg-muted text-muted-foreground"
|
|
695
|
-
), children: s.status }),
|
|
696
|
-
/* @__PURE__ */ jsx7(
|
|
697
|
-
"button",
|
|
698
|
-
{
|
|
699
|
-
type: "button",
|
|
700
|
-
onClick: () => onRemoveMcp(s.name),
|
|
701
|
-
className: "p-1 text-muted-foreground hover:text-destructive transition-colors rounded",
|
|
702
|
-
children: /* @__PURE__ */ jsx7(Trash23, { className: "h-3.5 w-3.5" })
|
|
703
|
-
}
|
|
704
|
-
)
|
|
705
|
-
] })
|
|
706
|
-
] }, s.name)) }) : /* @__PURE__ */ jsx7("div", { className: "p-4 text-center", children: /* @__PURE__ */ jsx7("p", { className: "text-xs text-muted-foreground", children: "No MCP servers configured" }) }),
|
|
707
|
-
showAddMcp && /* @__PURE__ */ jsxs7("div", { className: "p-4 border-t border-border bg-muted/10 space-y-2", children: [
|
|
708
|
-
/* @__PURE__ */ jsx7(
|
|
709
|
-
"input",
|
|
710
|
-
{
|
|
711
|
-
type: "text",
|
|
712
|
-
placeholder: "Server name",
|
|
713
|
-
value: mcpName,
|
|
714
|
-
onChange: (e) => setMcpName(e.target.value),
|
|
715
|
-
className: "w-full rounded-lg border border-border bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring"
|
|
716
|
-
}
|
|
717
|
-
),
|
|
718
|
-
/* @__PURE__ */ jsx7(
|
|
719
|
-
"input",
|
|
720
|
-
{
|
|
721
|
-
type: "text",
|
|
722
|
-
placeholder: "Command (e.g. npx @server/mcp)",
|
|
723
|
-
value: mcpCommand,
|
|
724
|
-
onChange: (e) => setMcpCommand(e.target.value),
|
|
725
|
-
className: "w-full rounded-lg border border-border bg-background px-3 py-2 text-sm font-mono text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring"
|
|
726
|
-
}
|
|
727
|
-
),
|
|
728
|
-
/* @__PURE__ */ jsx7(
|
|
729
|
-
"input",
|
|
730
|
-
{
|
|
731
|
-
type: "text",
|
|
732
|
-
placeholder: "Arguments (space-separated, optional)",
|
|
733
|
-
value: mcpArgs,
|
|
734
|
-
onChange: (e) => setMcpArgs(e.target.value),
|
|
735
|
-
className: "w-full rounded-lg border border-border bg-background px-3 py-2 text-sm font-mono text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring"
|
|
736
|
-
}
|
|
737
|
-
),
|
|
738
|
-
/* @__PURE__ */ jsxs7("div", { className: "flex justify-end gap-2 pt-1", children: [
|
|
739
|
-
/* @__PURE__ */ jsx7(
|
|
740
|
-
"button",
|
|
741
|
-
{
|
|
742
|
-
type: "button",
|
|
743
|
-
onClick: () => setShowAddMcp(false),
|
|
744
|
-
className: "rounded-md px-3 py-1.5 text-xs font-medium text-muted-foreground hover:text-foreground transition-colors",
|
|
745
|
-
children: "Cancel"
|
|
746
|
-
}
|
|
747
|
-
),
|
|
748
|
-
/* @__PURE__ */ jsx7(
|
|
749
|
-
"button",
|
|
750
|
-
{
|
|
751
|
-
type: "button",
|
|
752
|
-
onClick: handleAddMcp,
|
|
753
|
-
disabled: !mcpName.trim() || !mcpCommand.trim(),
|
|
754
|
-
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",
|
|
755
|
-
children: "Add Server"
|
|
756
|
-
}
|
|
757
|
-
)
|
|
758
|
-
] })
|
|
759
|
-
] })
|
|
760
|
-
] })
|
|
761
|
-
] });
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
// src/dashboard/snapshot-list.tsx
|
|
765
|
-
import * as React6 from "react";
|
|
766
|
-
import { Camera, Clock as Clock2, HardDrive, Plus as Plus5, RotateCcw } from "lucide-react";
|
|
767
|
-
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
768
|
-
function formatBytes(bytes) {
|
|
769
|
-
if (bytes == null || bytes < 0) return "-";
|
|
770
|
-
if (bytes === 0) return "0 B";
|
|
771
|
-
const k = 1024;
|
|
772
|
-
const sizes = ["B", "KB", "MB", "GB", "TB"];
|
|
773
|
-
const i = Math.min(Math.floor(Math.log(bytes) / Math.log(k)), sizes.length - 1);
|
|
774
|
-
return `${Number.parseFloat((bytes / k ** i).toFixed(1))} ${sizes[i]}`;
|
|
775
|
-
}
|
|
776
|
-
function formatDate(dateStr) {
|
|
777
|
-
const d = new Date(dateStr);
|
|
778
|
-
if (Number.isNaN(d.getTime())) return dateStr;
|
|
779
|
-
return d.toLocaleDateString(void 0, { month: "short", day: "numeric", hour: "2-digit", minute: "2-digit" });
|
|
780
|
-
}
|
|
781
|
-
function SnapshotList({ snapshots, onCreate, onRestore, onSaveAsTemplate, loading = false, className }) {
|
|
782
|
-
const [showCreate, setShowCreate] = React6.useState(false);
|
|
783
|
-
const [tags, setTags] = React6.useState("");
|
|
784
|
-
const handleCreate = () => {
|
|
785
|
-
const tagList = tags.trim() ? tags.trim().split(",").map((t) => t.trim()).filter(Boolean) : void 0;
|
|
786
|
-
onCreate(tagList);
|
|
787
|
-
setTags("");
|
|
788
|
-
setShowCreate(false);
|
|
789
|
-
};
|
|
790
|
-
return /* @__PURE__ */ jsxs8("div", { className: cn("space-y-4", className), children: [
|
|
791
|
-
/* @__PURE__ */ jsxs8("div", { className: "flex items-center justify-between", children: [
|
|
792
|
-
/* @__PURE__ */ jsx8("h3", { className: "text-sm font-bold text-foreground", children: "Snapshots" }),
|
|
793
|
-
/* @__PURE__ */ jsxs8(
|
|
794
|
-
"button",
|
|
795
|
-
{
|
|
796
|
-
type: "button",
|
|
797
|
-
onClick: () => setShowCreate(!showCreate),
|
|
798
|
-
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",
|
|
799
|
-
children: [
|
|
800
|
-
/* @__PURE__ */ jsx8(Plus5, { className: "h-3.5 w-3.5" }),
|
|
801
|
-
"Create Snapshot"
|
|
802
|
-
]
|
|
803
|
-
}
|
|
804
|
-
)
|
|
805
|
-
] }),
|
|
806
|
-
showCreate && /* @__PURE__ */ jsxs8("div", { className: "rounded-lg border border-primary/20 bg-primary/5 p-4 space-y-3", children: [
|
|
807
|
-
/* @__PURE__ */ jsx8("p", { className: "text-sm text-foreground font-medium", children: "Create a new snapshot of the current sandbox state." }),
|
|
808
|
-
/* @__PURE__ */ jsx8(
|
|
809
|
-
"input",
|
|
810
|
-
{
|
|
811
|
-
type: "text",
|
|
812
|
-
placeholder: "Tags (comma-separated, optional)",
|
|
813
|
-
value: tags,
|
|
814
|
-
onChange: (e) => setTags(e.target.value),
|
|
815
|
-
onKeyDown: (e) => e.key === "Enter" && handleCreate(),
|
|
816
|
-
className: "w-full rounded-lg border border-border bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring"
|
|
817
|
-
}
|
|
818
|
-
),
|
|
819
|
-
/* @__PURE__ */ jsxs8("div", { className: "flex justify-end gap-2", children: [
|
|
820
|
-
/* @__PURE__ */ jsx8(
|
|
821
|
-
"button",
|
|
822
|
-
{
|
|
823
|
-
type: "button",
|
|
824
|
-
onClick: () => setShowCreate(false),
|
|
825
|
-
className: "rounded-md px-3 py-1.5 text-xs font-medium text-muted-foreground hover:text-foreground transition-colors",
|
|
826
|
-
children: "Cancel"
|
|
827
|
-
}
|
|
828
|
-
),
|
|
829
|
-
/* @__PURE__ */ jsxs8(
|
|
830
|
-
"button",
|
|
831
|
-
{
|
|
832
|
-
type: "button",
|
|
833
|
-
onClick: handleCreate,
|
|
834
|
-
className: "rounded-md bg-primary px-3 py-1.5 text-xs font-medium text-primary-foreground hover:bg-primary/90 transition-colors",
|
|
835
|
-
children: [
|
|
836
|
-
/* @__PURE__ */ jsx8(Camera, { className: "h-3 w-3 mr-1.5 inline" }),
|
|
837
|
-
"Create"
|
|
838
|
-
]
|
|
839
|
-
}
|
|
840
|
-
)
|
|
841
|
-
] })
|
|
842
|
-
] }),
|
|
843
|
-
loading ? /* @__PURE__ */ jsxs8("div", { className: "rounded-lg border border-border bg-muted/20 p-6 text-center", children: [
|
|
844
|
-
/* @__PURE__ */ jsx8(Camera, { className: "mx-auto h-6 w-6 text-muted-foreground animate-pulse mb-2" }),
|
|
845
|
-
/* @__PURE__ */ jsx8("p", { className: "text-sm text-muted-foreground", children: "Loading snapshots..." })
|
|
846
|
-
] }) : snapshots.length > 0 ? /* @__PURE__ */ jsx8("div", { className: "rounded-lg border border-border overflow-hidden", children: /* @__PURE__ */ jsxs8("table", { className: "w-full text-sm", children: [
|
|
847
|
-
/* @__PURE__ */ jsx8("thead", { className: "bg-muted/30 border-b border-border", children: /* @__PURE__ */ jsxs8("tr", { children: [
|
|
848
|
-
/* @__PURE__ */ jsx8("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "ID" }),
|
|
849
|
-
/* @__PURE__ */ jsx8("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Created" }),
|
|
850
|
-
/* @__PURE__ */ jsx8("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Size" }),
|
|
851
|
-
/* @__PURE__ */ jsx8("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Tags" }),
|
|
852
|
-
/* @__PURE__ */ jsx8("th", { className: "px-4 py-2.5 text-right text-xs font-medium text-muted-foreground w-24" })
|
|
853
|
-
] }) }),
|
|
854
|
-
/* @__PURE__ */ jsx8("tbody", { className: "divide-y divide-border", children: snapshots.map((s) => /* @__PURE__ */ jsxs8("tr", { children: [
|
|
855
|
-
/* @__PURE__ */ jsx8("td", { className: "px-4 py-3 font-mono text-xs text-foreground", children: s.id.slice(0, 12) }),
|
|
856
|
-
/* @__PURE__ */ jsx8("td", { className: "px-4 py-3 text-xs text-muted-foreground", children: /* @__PURE__ */ jsxs8("span", { className: "inline-flex items-center gap-1.5", children: [
|
|
857
|
-
/* @__PURE__ */ jsx8(Clock2, { className: "h-3 w-3" }),
|
|
858
|
-
formatDate(s.createdAt)
|
|
859
|
-
] }) }),
|
|
860
|
-
/* @__PURE__ */ jsx8("td", { className: "px-4 py-3 text-xs text-muted-foreground", children: /* @__PURE__ */ jsxs8("span", { className: "inline-flex items-center gap-1.5", children: [
|
|
861
|
-
/* @__PURE__ */ jsx8(HardDrive, { className: "h-3 w-3" }),
|
|
862
|
-
formatBytes(s.sizeBytes)
|
|
863
|
-
] }) }),
|
|
864
|
-
/* @__PURE__ */ jsx8("td", { className: "px-4 py-3", children: s.tags?.length ? /* @__PURE__ */ jsx8("div", { className: "flex items-center gap-1 flex-wrap", children: s.tags.map((tag) => /* @__PURE__ */ jsx8("span", { className: "rounded-full bg-muted px-2 py-0.5 text-[10px] font-medium text-muted-foreground border border-border", children: tag }, tag)) }) : /* @__PURE__ */ jsx8("span", { className: "text-xs text-muted-foreground", children: "-" }) }),
|
|
865
|
-
/* @__PURE__ */ jsx8("td", { className: "px-4 py-3 text-right", children: /* @__PURE__ */ jsxs8("div", { className: "flex items-center justify-end gap-2", children: [
|
|
866
|
-
/* @__PURE__ */ jsxs8(
|
|
867
|
-
"button",
|
|
868
|
-
{
|
|
869
|
-
type: "button",
|
|
870
|
-
onClick: () => onRestore(s.id),
|
|
871
|
-
className: "inline-flex items-center gap-1.5 rounded-md bg-muted px-2.5 py-1 text-xs font-medium text-foreground hover:bg-muted/80 transition-colors border border-border",
|
|
872
|
-
title: "Restore to new sandbox",
|
|
873
|
-
children: [
|
|
874
|
-
/* @__PURE__ */ jsx8(RotateCcw, { className: "h-3 w-3" }),
|
|
875
|
-
"Restore"
|
|
876
|
-
]
|
|
877
|
-
}
|
|
878
|
-
),
|
|
879
|
-
onSaveAsTemplate && /* @__PURE__ */ jsx8(
|
|
880
|
-
"button",
|
|
881
|
-
{
|
|
882
|
-
type: "button",
|
|
883
|
-
onClick: () => onSaveAsTemplate(s.id),
|
|
884
|
-
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",
|
|
885
|
-
title: "Save as reusable template",
|
|
886
|
-
children: "Save as Template"
|
|
887
|
-
}
|
|
888
|
-
)
|
|
889
|
-
] }) })
|
|
890
|
-
] }, s.id)) })
|
|
891
|
-
] }) }) : /* @__PURE__ */ jsxs8("div", { className: "rounded-lg border border-border bg-muted/20 p-6 text-center", children: [
|
|
892
|
-
/* @__PURE__ */ jsx8(Camera, { className: "mx-auto h-8 w-8 text-muted-foreground mb-2" }),
|
|
893
|
-
/* @__PURE__ */ jsx8("p", { className: "text-sm text-muted-foreground", children: "No snapshots yet" }),
|
|
894
|
-
/* @__PURE__ */ jsx8("p", { className: "text-xs text-muted-foreground mt-1", children: "Create a snapshot to save the current state of your sandbox." })
|
|
895
|
-
] })
|
|
896
|
-
] });
|
|
897
|
-
}
|
|
898
|
-
|
|
899
|
-
// src/dashboard/promo-banner.tsx
|
|
900
|
-
import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
901
|
-
function PromoBanner({
|
|
902
|
-
title,
|
|
903
|
-
description,
|
|
904
|
-
buttonLabel,
|
|
905
|
-
href,
|
|
906
|
-
onClick,
|
|
907
|
-
icon,
|
|
908
|
-
disabled = false,
|
|
909
|
-
className
|
|
910
|
-
}) {
|
|
911
|
-
const buttonClasses = cn(
|
|
912
|
-
"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",
|
|
913
|
-
disabled ? "opacity-50 cursor-not-allowed" : "hover:bg-[var(--btn-primary-hover)]"
|
|
914
|
-
);
|
|
915
|
-
const buttonContent = /* @__PURE__ */ jsxs9(Fragment2, { children: [
|
|
916
|
-
buttonLabel,
|
|
917
|
-
/* @__PURE__ */ jsxs9("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: [
|
|
918
|
-
/* @__PURE__ */ jsx9("path", { d: "M5 12h14" }),
|
|
919
|
-
/* @__PURE__ */ jsx9("path", { d: "m12 5 7 7-7 7" })
|
|
920
|
-
] })
|
|
921
|
-
] });
|
|
922
|
-
return /* @__PURE__ */ jsxs9("div", { className: cn("relative overflow-hidden rounded-xl bg-[var(--brand-strong)] p-8 md:flex md:items-center md:justify-between", className), children: [
|
|
923
|
-
/* @__PURE__ */ jsxs9("div", { className: "relative z-10", children: [
|
|
924
|
-
/* @__PURE__ */ jsx9("h3", { className: "text-xl font-bold text-[var(--brand-strong-text)]", children: title }),
|
|
925
|
-
/* @__PURE__ */ jsx9("p", { className: "mt-2 max-w-md text-sm text-[var(--brand-strong-text-muted)]", children: description }),
|
|
926
|
-
href && !disabled ? /* @__PURE__ */ jsx9("a", { href, target: "_blank", rel: "noopener noreferrer", onClick, className: buttonClasses, children: buttonContent }) : /* @__PURE__ */ jsx9("button", { type: "button", onClick, disabled, className: buttonClasses, children: buttonContent })
|
|
927
|
-
] }),
|
|
928
|
-
icon && /* @__PURE__ */ jsx9("div", { className: "relative z-10 mt-6 flex items-center md:mt-0", children: /* @__PURE__ */ jsx9("div", { className: "flex h-16 w-16 items-center justify-center rounded-2xl border border-white/10 bg-white/10", children: icon }) }),
|
|
929
|
-
/* @__PURE__ */ jsx9("div", { className: "pointer-events-none absolute inset-y-0 right-0 w-1/3 bg-gradient-to-l from-white/5 to-transparent" })
|
|
930
|
-
] });
|
|
931
|
-
}
|
|
58
|
+
} from "./chunk-DNZ4DTNA.js";
|
|
59
|
+
import "./chunk-7ZA5SEK3.js";
|
|
60
|
+
import "./chunk-EI44GEQ5.js";
|
|
932
61
|
export {
|
|
933
62
|
BackendConfig,
|
|
934
63
|
BackendSelector,
|