@tangle-network/sandbox-ui 0.2.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.
- package/README.md +68 -0
- package/dist/auth.d.ts +57 -0
- package/dist/auth.js +14 -0
- package/dist/branding-DCi5VEik.d.ts +13 -0
- package/dist/button-BidTtuRS.d.ts +15 -0
- package/dist/chat.d.ts +121 -0
- package/dist/chat.js +25 -0
- package/dist/chunk-2UHPE5T7.js +201 -0
- package/dist/chunk-4EIWPJMJ.js +545 -0
- package/dist/chunk-6MQIDUPA.js +502 -0
- package/dist/chunk-B26TQ7SA.js +47 -0
- package/dist/chunk-E6FS7R4X.js +109 -0
- package/dist/chunk-GRYHFH5O.js +110 -0
- package/dist/chunk-HMND7JPA.js +868 -0
- package/dist/chunk-HRMUF35V.js +19 -0
- package/dist/chunk-HYEAX3DC.js +822 -0
- package/dist/chunk-KMXV7DDX.js +174 -0
- package/dist/chunk-KYY2X6LY.js +318 -0
- package/dist/chunk-L6ZDH5F4.js +334 -0
- package/dist/chunk-LTFK464G.js +103 -0
- package/dist/chunk-M34OA6PQ.js +233 -0
- package/dist/chunk-M6VLC32S.js +219 -0
- package/dist/chunk-MCGKDCOR.js +173 -0
- package/dist/chunk-NI2EI43H.js +294 -0
- package/dist/chunk-OU4TRNQZ.js +173 -0
- package/dist/chunk-QD4QE5P5.js +40 -0
- package/dist/chunk-QSQBDR3N.js +180 -0
- package/dist/chunk-RQHJBTEU.js +10 -0
- package/dist/chunk-U62G5TS7.js +472 -0
- package/dist/chunk-ZOL2TR5M.js +475 -0
- package/dist/dashboard.d.ts +111 -0
- package/dist/dashboard.js +26 -0
- package/dist/editor.d.ts +196 -0
- package/dist/editor.js +713 -0
- package/dist/expanded-tool-detail-OkXGqTHe.d.ts +52 -0
- package/dist/files.d.ts +66 -0
- package/dist/files.js +11 -0
- package/dist/hooks.d.ts +22 -0
- package/dist/hooks.js +107 -0
- package/dist/index.d.ts +107 -0
- package/dist/index.js +551 -0
- package/dist/markdown.d.ts +55 -0
- package/dist/markdown.js +17 -0
- package/dist/pages.d.ts +89 -0
- package/dist/pages.js +1181 -0
- package/dist/parts-CyGkM6Fp.d.ts +50 -0
- package/dist/primitives.d.ts +189 -0
- package/dist/primitives.js +161 -0
- package/dist/run-CtFZ6s-D.d.ts +41 -0
- package/dist/run.d.ts +14 -0
- package/dist/run.js +29 -0
- package/dist/sidecar-CFU2W9j1.d.ts +8 -0
- package/dist/stores.d.ts +28 -0
- package/dist/stores.js +49 -0
- package/dist/terminal.d.ts +44 -0
- package/dist/terminal.js +160 -0
- package/dist/tool-call-feed-D5Ume-Pt.d.ts +66 -0
- package/dist/tool-display-BvsVW_Ur.d.ts +32 -0
- package/dist/types.d.ts +6 -0
- package/dist/types.js +0 -0
- package/dist/usage-chart-DINgSVL5.d.ts +60 -0
- package/dist/use-sidecar-auth-Bb0-w3lX.d.ts +339 -0
- package/dist/utils.d.ts +28 -0
- package/dist/utils.js +28 -0
- package/dist/workspace.d.ts +113 -0
- package/dist/workspace.js +15 -0
- package/package.json +174 -0
- package/src/styles/globals.css +230 -0
- package/src/styles/tokens.css +73 -0
- package/tailwind.config.cjs +99 -0
package/dist/pages.js
ADDED
|
@@ -0,0 +1,1181 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Dialog,
|
|
3
|
+
DialogContent,
|
|
4
|
+
DialogDescription,
|
|
5
|
+
DialogFooter,
|
|
6
|
+
DialogHeader,
|
|
7
|
+
DialogTitle,
|
|
8
|
+
EmptyState,
|
|
9
|
+
Input,
|
|
10
|
+
Tabs,
|
|
11
|
+
TabsContent,
|
|
12
|
+
TabsList,
|
|
13
|
+
TabsTrigger
|
|
14
|
+
} from "./chunk-NI2EI43H.js";
|
|
15
|
+
import {
|
|
16
|
+
BillingDashboard,
|
|
17
|
+
PricingPage,
|
|
18
|
+
UsageChart
|
|
19
|
+
} from "./chunk-4EIWPJMJ.js";
|
|
20
|
+
import {
|
|
21
|
+
Badge,
|
|
22
|
+
Card,
|
|
23
|
+
Skeleton,
|
|
24
|
+
SkeletonCard
|
|
25
|
+
} from "./chunk-2UHPE5T7.js";
|
|
26
|
+
import {
|
|
27
|
+
Button
|
|
28
|
+
} from "./chunk-E6FS7R4X.js";
|
|
29
|
+
import {
|
|
30
|
+
cn
|
|
31
|
+
} from "./chunk-RQHJBTEU.js";
|
|
32
|
+
|
|
33
|
+
// src/pages/billing-page.tsx
|
|
34
|
+
import * as React from "react";
|
|
35
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
36
|
+
var defaultBillingData = {
|
|
37
|
+
subscription: null,
|
|
38
|
+
balance: { available: 0, used: 0 },
|
|
39
|
+
usage: { period: "This Month", total: 0, byModel: {} },
|
|
40
|
+
usageHistory: [],
|
|
41
|
+
tiers: []
|
|
42
|
+
};
|
|
43
|
+
async function fetchBillingDataFromApi(apiBasePath) {
|
|
44
|
+
const [subscriptionRes, balanceRes, usageRes, tiersRes] = await Promise.all([
|
|
45
|
+
fetch(`${apiBasePath}/v1/billing/subscription`),
|
|
46
|
+
fetch(`${apiBasePath}/v1/billing/balance`),
|
|
47
|
+
fetch(`${apiBasePath}/v1/billing/usage`),
|
|
48
|
+
fetch(`${apiBasePath}/v1/billing/tiers`)
|
|
49
|
+
]);
|
|
50
|
+
if (!subscriptionRes.ok && subscriptionRes.status !== 404) {
|
|
51
|
+
throw new Error("Failed to fetch subscription data");
|
|
52
|
+
}
|
|
53
|
+
if (!balanceRes.ok) {
|
|
54
|
+
throw new Error("Failed to fetch balance data");
|
|
55
|
+
}
|
|
56
|
+
if (!usageRes.ok) {
|
|
57
|
+
throw new Error("Failed to fetch usage data");
|
|
58
|
+
}
|
|
59
|
+
if (!tiersRes.ok) {
|
|
60
|
+
throw new Error("Failed to fetch pricing tiers");
|
|
61
|
+
}
|
|
62
|
+
const subscription = subscriptionRes.status === 404 ? null : await subscriptionRes.json();
|
|
63
|
+
const balance = await balanceRes.json();
|
|
64
|
+
const usageData = await usageRes.json();
|
|
65
|
+
const tiers = await tiersRes.json();
|
|
66
|
+
return {
|
|
67
|
+
subscription,
|
|
68
|
+
balance,
|
|
69
|
+
usage: {
|
|
70
|
+
period: usageData.period || "This Month",
|
|
71
|
+
total: usageData.total || 0,
|
|
72
|
+
byModel: usageData.byModel || {}
|
|
73
|
+
},
|
|
74
|
+
usageHistory: usageData.history || [],
|
|
75
|
+
tiers: tiers.tiers || tiers
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function BillingPageSkeleton({
|
|
79
|
+
variant: _variant = "sandbox"
|
|
80
|
+
}) {
|
|
81
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-8", children: [
|
|
82
|
+
/* @__PURE__ */ jsxs("div", { className: "grid gap-6 lg:grid-cols-3", children: [
|
|
83
|
+
/* @__PURE__ */ jsx(SkeletonCard, {}),
|
|
84
|
+
/* @__PURE__ */ jsx(SkeletonCard, {}),
|
|
85
|
+
/* @__PURE__ */ jsx(SkeletonCard, {})
|
|
86
|
+
] }),
|
|
87
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-4 rounded-xl border border-border bg-card p-6", children: [
|
|
88
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-6 w-48" }),
|
|
89
|
+
/* @__PURE__ */ jsx("div", { className: "h-48", children: /* @__PURE__ */ jsx(Skeleton, { className: "h-full w-full" }) })
|
|
90
|
+
] })
|
|
91
|
+
] });
|
|
92
|
+
}
|
|
93
|
+
function BillingPageError({
|
|
94
|
+
message,
|
|
95
|
+
onRetry
|
|
96
|
+
}) {
|
|
97
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center space-y-4 rounded-xl border border-destructive/20 bg-destructive/5 p-8 text-center", children: [
|
|
98
|
+
/* @__PURE__ */ jsx("div", { className: "rounded-full bg-destructive/10 p-3", children: /* @__PURE__ */ jsxs(
|
|
99
|
+
"svg",
|
|
100
|
+
{
|
|
101
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
102
|
+
viewBox: "0 0 24 24",
|
|
103
|
+
fill: "none",
|
|
104
|
+
stroke: "currentColor",
|
|
105
|
+
strokeWidth: "2",
|
|
106
|
+
strokeLinecap: "round",
|
|
107
|
+
strokeLinejoin: "round",
|
|
108
|
+
className: "h-6 w-6 text-destructive",
|
|
109
|
+
children: [
|
|
110
|
+
/* @__PURE__ */ jsx("title", { children: "Error icon" }),
|
|
111
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
|
|
112
|
+
/* @__PURE__ */ jsx("line", { x1: "12", x2: "12", y1: "8", y2: "12" }),
|
|
113
|
+
/* @__PURE__ */ jsx("line", { x1: "12", x2: "12.01", y1: "16", y2: "16" })
|
|
114
|
+
]
|
|
115
|
+
}
|
|
116
|
+
) }),
|
|
117
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
118
|
+
/* @__PURE__ */ jsx("h3", { className: "font-semibold text-lg", children: "Failed to load billing data" }),
|
|
119
|
+
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm", children: message })
|
|
120
|
+
] }),
|
|
121
|
+
/* @__PURE__ */ jsx(
|
|
122
|
+
"button",
|
|
123
|
+
{
|
|
124
|
+
type: "button",
|
|
125
|
+
onClick: onRetry,
|
|
126
|
+
className: "rounded-lg bg-primary px-4 py-2 font-medium text-primary-foreground text-sm transition-colors hover:bg-primary/90",
|
|
127
|
+
children: "Try Again"
|
|
128
|
+
}
|
|
129
|
+
)
|
|
130
|
+
] });
|
|
131
|
+
}
|
|
132
|
+
function BillingPage({
|
|
133
|
+
variant = "sandbox",
|
|
134
|
+
initialData,
|
|
135
|
+
apiBasePath = "",
|
|
136
|
+
onManageSubscription,
|
|
137
|
+
onAddCredits,
|
|
138
|
+
onSelectTier,
|
|
139
|
+
fetchBillingData
|
|
140
|
+
}) {
|
|
141
|
+
const [state, setState] = React.useState({
|
|
142
|
+
data: initialData || null,
|
|
143
|
+
loading: !initialData,
|
|
144
|
+
error: null,
|
|
145
|
+
billingPeriod: "monthly",
|
|
146
|
+
selectingTier: false
|
|
147
|
+
});
|
|
148
|
+
const loadData = React.useCallback(async () => {
|
|
149
|
+
setState((prev) => ({ ...prev, loading: true, error: null }));
|
|
150
|
+
try {
|
|
151
|
+
const data2 = fetchBillingData ? await fetchBillingData() : await fetchBillingDataFromApi(apiBasePath);
|
|
152
|
+
setState((prev) => ({ ...prev, data: data2, loading: false }));
|
|
153
|
+
} catch (err) {
|
|
154
|
+
setState((prev) => ({
|
|
155
|
+
...prev,
|
|
156
|
+
loading: false,
|
|
157
|
+
error: err instanceof Error ? err.message : "An unknown error occurred"
|
|
158
|
+
}));
|
|
159
|
+
}
|
|
160
|
+
}, [apiBasePath, fetchBillingData]);
|
|
161
|
+
React.useEffect(() => {
|
|
162
|
+
if (!initialData) {
|
|
163
|
+
loadData();
|
|
164
|
+
}
|
|
165
|
+
}, [initialData, loadData]);
|
|
166
|
+
const handleManageSubscription = React.useCallback(() => {
|
|
167
|
+
if (onManageSubscription) {
|
|
168
|
+
onManageSubscription();
|
|
169
|
+
} else {
|
|
170
|
+
window.location.href = `${apiBasePath}/v1/billing/portal`;
|
|
171
|
+
}
|
|
172
|
+
}, [apiBasePath, onManageSubscription]);
|
|
173
|
+
const handleAddCredits = React.useCallback(() => {
|
|
174
|
+
if (onAddCredits) {
|
|
175
|
+
onAddCredits();
|
|
176
|
+
} else {
|
|
177
|
+
window.location.href = `${apiBasePath}/v1/billing/credits`;
|
|
178
|
+
}
|
|
179
|
+
}, [apiBasePath, onAddCredits]);
|
|
180
|
+
const handleSelectTier = React.useCallback(
|
|
181
|
+
async (tierId) => {
|
|
182
|
+
if (onSelectTier) {
|
|
183
|
+
onSelectTier(tierId);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
setState((prev) => ({ ...prev, selectingTier: true }));
|
|
187
|
+
try {
|
|
188
|
+
const res = await fetch(`${apiBasePath}/v1/billing/subscribe`, {
|
|
189
|
+
method: "POST",
|
|
190
|
+
headers: { "Content-Type": "application/json" },
|
|
191
|
+
body: JSON.stringify({
|
|
192
|
+
tierId,
|
|
193
|
+
billingPeriod: state.billingPeriod
|
|
194
|
+
})
|
|
195
|
+
});
|
|
196
|
+
if (!res.ok) {
|
|
197
|
+
throw new Error("Failed to initiate subscription");
|
|
198
|
+
}
|
|
199
|
+
const { checkoutUrl } = await res.json();
|
|
200
|
+
if (checkoutUrl) {
|
|
201
|
+
window.location.href = checkoutUrl;
|
|
202
|
+
} else {
|
|
203
|
+
loadData();
|
|
204
|
+
}
|
|
205
|
+
} catch (err) {
|
|
206
|
+
setState((prev) => ({
|
|
207
|
+
...prev,
|
|
208
|
+
selectingTier: false,
|
|
209
|
+
error: err instanceof Error ? err.message : "Failed to select tier"
|
|
210
|
+
}));
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
[apiBasePath, state.billingPeriod, onSelectTier, loadData]
|
|
214
|
+
);
|
|
215
|
+
const handleBillingPeriodChange = React.useCallback(
|
|
216
|
+
(period) => {
|
|
217
|
+
setState((prev) => ({ ...prev, billingPeriod: period }));
|
|
218
|
+
},
|
|
219
|
+
[]
|
|
220
|
+
);
|
|
221
|
+
if (state.loading) {
|
|
222
|
+
return /* @__PURE__ */ jsx(BillingPageSkeleton, { variant });
|
|
223
|
+
}
|
|
224
|
+
if (state.error) {
|
|
225
|
+
return /* @__PURE__ */ jsx(BillingPageError, { message: state.error, onRetry: loadData });
|
|
226
|
+
}
|
|
227
|
+
const data = state.data || defaultBillingData;
|
|
228
|
+
return /* @__PURE__ */ jsx("div", { className: "space-y-8", children: /* @__PURE__ */ jsxs(Tabs, { defaultValue: "overview", className: "w-full", children: [
|
|
229
|
+
/* @__PURE__ */ jsxs(TabsList, { className: "mb-6", children: [
|
|
230
|
+
/* @__PURE__ */ jsx(TabsTrigger, { value: "overview", children: "Overview" }),
|
|
231
|
+
/* @__PURE__ */ jsx(TabsTrigger, { value: "plans", children: "Plans" }),
|
|
232
|
+
/* @__PURE__ */ jsx(TabsTrigger, { value: "usage", children: "Usage History" })
|
|
233
|
+
] }),
|
|
234
|
+
/* @__PURE__ */ jsx(TabsContent, { value: "overview", className: "space-y-6", children: /* @__PURE__ */ jsx(
|
|
235
|
+
BillingDashboard,
|
|
236
|
+
{
|
|
237
|
+
subscription: data.subscription,
|
|
238
|
+
balance: data.balance,
|
|
239
|
+
usage: data.usage,
|
|
240
|
+
onManageSubscription: handleManageSubscription,
|
|
241
|
+
onAddCredits: handleAddCredits,
|
|
242
|
+
variant
|
|
243
|
+
}
|
|
244
|
+
) }),
|
|
245
|
+
/* @__PURE__ */ jsx(TabsContent, { value: "plans", children: /* @__PURE__ */ jsx(
|
|
246
|
+
PricingPage,
|
|
247
|
+
{
|
|
248
|
+
tiers: data.tiers,
|
|
249
|
+
currentTierId: data.subscription?.tierName,
|
|
250
|
+
billingPeriod: state.billingPeriod,
|
|
251
|
+
onBillingPeriodChange: handleBillingPeriodChange,
|
|
252
|
+
onSelectTier: handleSelectTier,
|
|
253
|
+
variant,
|
|
254
|
+
loading: state.selectingTier
|
|
255
|
+
}
|
|
256
|
+
) }),
|
|
257
|
+
/* @__PURE__ */ jsx(TabsContent, { value: "usage", children: data.usageHistory.length > 0 ? /* @__PURE__ */ jsx(
|
|
258
|
+
UsageChart,
|
|
259
|
+
{
|
|
260
|
+
data: data.usageHistory,
|
|
261
|
+
title: "Credit Usage History",
|
|
262
|
+
unit: "credits",
|
|
263
|
+
variant
|
|
264
|
+
}
|
|
265
|
+
) : /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center rounded-xl border border-border bg-card p-12 text-center", children: [
|
|
266
|
+
/* @__PURE__ */ jsxs(
|
|
267
|
+
"svg",
|
|
268
|
+
{
|
|
269
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
270
|
+
viewBox: "0 0 24 24",
|
|
271
|
+
fill: "none",
|
|
272
|
+
stroke: "currentColor",
|
|
273
|
+
strokeWidth: "2",
|
|
274
|
+
strokeLinecap: "round",
|
|
275
|
+
strokeLinejoin: "round",
|
|
276
|
+
className: "mb-4 h-12 w-12 text-muted-foreground",
|
|
277
|
+
children: [
|
|
278
|
+
/* @__PURE__ */ jsx("title", { children: "No usage data icon" }),
|
|
279
|
+
/* @__PURE__ */ jsx("path", { d: "M3 3v18h18" }),
|
|
280
|
+
/* @__PURE__ */ jsx("path", { d: "m19 9-5 5-4-4-3 3" })
|
|
281
|
+
]
|
|
282
|
+
}
|
|
283
|
+
),
|
|
284
|
+
/* @__PURE__ */ jsx("h3", { className: "font-semibold text-lg", children: "No usage data yet" }),
|
|
285
|
+
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm", children: "Start using credits to see your usage history here." })
|
|
286
|
+
] }) })
|
|
287
|
+
] }) });
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// src/pages/pricing-page.tsx
|
|
291
|
+
import * as React2 from "react";
|
|
292
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
293
|
+
var variantColors = {
|
|
294
|
+
sandbox: {
|
|
295
|
+
accent: "text-purple-400",
|
|
296
|
+
gradientFrom: "from-purple-600",
|
|
297
|
+
gradientTo: "to-purple-400"
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
async function fetchTiersFromApi(apiBasePath) {
|
|
301
|
+
const res = await fetch(`${apiBasePath}/v1/billing/tiers`);
|
|
302
|
+
if (!res.ok) {
|
|
303
|
+
throw new Error("Failed to fetch pricing tiers");
|
|
304
|
+
}
|
|
305
|
+
const data = await res.json();
|
|
306
|
+
return data.tiers || data;
|
|
307
|
+
}
|
|
308
|
+
function PricingPageSkeleton() {
|
|
309
|
+
return /* @__PURE__ */ jsxs2("div", { className: "space-y-8", children: [
|
|
310
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-center gap-4", children: [
|
|
311
|
+
/* @__PURE__ */ jsx2(Skeleton, { className: "h-10 w-24" }),
|
|
312
|
+
/* @__PURE__ */ jsx2(Skeleton, { className: "h-10 w-24" })
|
|
313
|
+
] }),
|
|
314
|
+
/* @__PURE__ */ jsxs2("div", { className: "grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3", children: [
|
|
315
|
+
/* @__PURE__ */ jsx2(SkeletonCard, { className: "h-96" }),
|
|
316
|
+
/* @__PURE__ */ jsx2(SkeletonCard, { className: "h-96" }),
|
|
317
|
+
/* @__PURE__ */ jsx2(SkeletonCard, { className: "h-96" })
|
|
318
|
+
] })
|
|
319
|
+
] });
|
|
320
|
+
}
|
|
321
|
+
function PricingPageError({
|
|
322
|
+
message,
|
|
323
|
+
onRetry
|
|
324
|
+
}) {
|
|
325
|
+
return /* @__PURE__ */ jsxs2("div", { className: "flex flex-col items-center justify-center space-y-4 rounded-xl border border-destructive/20 bg-destructive/5 p-8 text-center", children: [
|
|
326
|
+
/* @__PURE__ */ jsx2("div", { className: "rounded-full bg-destructive/10 p-3", children: /* @__PURE__ */ jsxs2(
|
|
327
|
+
"svg",
|
|
328
|
+
{
|
|
329
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
330
|
+
viewBox: "0 0 24 24",
|
|
331
|
+
fill: "none",
|
|
332
|
+
stroke: "currentColor",
|
|
333
|
+
strokeWidth: "2",
|
|
334
|
+
strokeLinecap: "round",
|
|
335
|
+
strokeLinejoin: "round",
|
|
336
|
+
className: "h-6 w-6 text-destructive",
|
|
337
|
+
children: [
|
|
338
|
+
/* @__PURE__ */ jsx2("title", { children: "Error icon" }),
|
|
339
|
+
/* @__PURE__ */ jsx2("circle", { cx: "12", cy: "12", r: "10" }),
|
|
340
|
+
/* @__PURE__ */ jsx2("line", { x1: "12", x2: "12", y1: "8", y2: "12" }),
|
|
341
|
+
/* @__PURE__ */ jsx2("line", { x1: "12", x2: "12.01", y1: "16", y2: "16" })
|
|
342
|
+
]
|
|
343
|
+
}
|
|
344
|
+
) }),
|
|
345
|
+
/* @__PURE__ */ jsxs2("div", { className: "space-y-2", children: [
|
|
346
|
+
/* @__PURE__ */ jsx2("h3", { className: "font-semibold text-lg", children: "Failed to load pricing" }),
|
|
347
|
+
/* @__PURE__ */ jsx2("p", { className: "text-muted-foreground text-sm", children: message })
|
|
348
|
+
] }),
|
|
349
|
+
/* @__PURE__ */ jsx2(
|
|
350
|
+
"button",
|
|
351
|
+
{
|
|
352
|
+
type: "button",
|
|
353
|
+
onClick: onRetry,
|
|
354
|
+
className: "rounded-lg bg-primary px-4 py-2 font-medium text-primary-foreground text-sm transition-colors hover:bg-primary/90",
|
|
355
|
+
children: "Try Again"
|
|
356
|
+
}
|
|
357
|
+
)
|
|
358
|
+
] });
|
|
359
|
+
}
|
|
360
|
+
function StandalonePricingPage({
|
|
361
|
+
variant = "sandbox",
|
|
362
|
+
initialTiers,
|
|
363
|
+
apiBasePath = "",
|
|
364
|
+
onSelectTier,
|
|
365
|
+
fetchTiers,
|
|
366
|
+
title = "Simple, transparent pricing",
|
|
367
|
+
subtitle = "Choose the plan that fits your needs. Upgrade or downgrade at any time."
|
|
368
|
+
}) {
|
|
369
|
+
const colors = variantColors[variant];
|
|
370
|
+
const [state, setState] = React2.useState({
|
|
371
|
+
tiers: initialTiers || [],
|
|
372
|
+
loading: !initialTiers,
|
|
373
|
+
error: null,
|
|
374
|
+
billingPeriod: "monthly",
|
|
375
|
+
selectingTier: false
|
|
376
|
+
});
|
|
377
|
+
const loadTiers = React2.useCallback(async () => {
|
|
378
|
+
setState((prev) => ({ ...prev, loading: true, error: null }));
|
|
379
|
+
try {
|
|
380
|
+
const tiers = fetchTiers ? await fetchTiers() : await fetchTiersFromApi(apiBasePath);
|
|
381
|
+
setState((prev) => ({ ...prev, tiers, loading: false }));
|
|
382
|
+
} catch (err) {
|
|
383
|
+
setState((prev) => ({
|
|
384
|
+
...prev,
|
|
385
|
+
loading: false,
|
|
386
|
+
error: err instanceof Error ? err.message : "An unknown error occurred"
|
|
387
|
+
}));
|
|
388
|
+
}
|
|
389
|
+
}, [apiBasePath, fetchTiers]);
|
|
390
|
+
React2.useEffect(() => {
|
|
391
|
+
if (!initialTiers) {
|
|
392
|
+
loadTiers();
|
|
393
|
+
}
|
|
394
|
+
}, [initialTiers, loadTiers]);
|
|
395
|
+
const handleSelectTier = React2.useCallback(
|
|
396
|
+
async (tierId) => {
|
|
397
|
+
if (onSelectTier) {
|
|
398
|
+
onSelectTier(tierId, state.billingPeriod);
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
setState((prev) => ({ ...prev, selectingTier: true }));
|
|
402
|
+
try {
|
|
403
|
+
const res = await fetch(`${apiBasePath}/v1/billing/subscribe`, {
|
|
404
|
+
method: "POST",
|
|
405
|
+
headers: { "Content-Type": "application/json" },
|
|
406
|
+
body: JSON.stringify({
|
|
407
|
+
tierId,
|
|
408
|
+
billingPeriod: state.billingPeriod
|
|
409
|
+
})
|
|
410
|
+
});
|
|
411
|
+
if (!res.ok) {
|
|
412
|
+
throw new Error("Failed to initiate subscription");
|
|
413
|
+
}
|
|
414
|
+
const { checkoutUrl, loginUrl } = await res.json();
|
|
415
|
+
if (loginUrl) {
|
|
416
|
+
window.location.href = loginUrl;
|
|
417
|
+
} else if (checkoutUrl) {
|
|
418
|
+
window.location.href = checkoutUrl;
|
|
419
|
+
}
|
|
420
|
+
} catch (err) {
|
|
421
|
+
setState((prev) => ({
|
|
422
|
+
...prev,
|
|
423
|
+
selectingTier: false,
|
|
424
|
+
error: err instanceof Error ? err.message : "Failed to select tier"
|
|
425
|
+
}));
|
|
426
|
+
}
|
|
427
|
+
},
|
|
428
|
+
[apiBasePath, state.billingPeriod, onSelectTier]
|
|
429
|
+
);
|
|
430
|
+
const handleBillingPeriodChange = React2.useCallback(
|
|
431
|
+
(period) => {
|
|
432
|
+
setState((prev) => ({ ...prev, billingPeriod: period }));
|
|
433
|
+
},
|
|
434
|
+
[]
|
|
435
|
+
);
|
|
436
|
+
return /* @__PURE__ */ jsxs2("div", { className: "w-full space-y-12", children: [
|
|
437
|
+
/* @__PURE__ */ jsxs2("div", { className: "space-y-4 text-center", children: [
|
|
438
|
+
/* @__PURE__ */ jsx2(
|
|
439
|
+
"h1",
|
|
440
|
+
{
|
|
441
|
+
className: cn(
|
|
442
|
+
"font-bold text-4xl tracking-tight sm:text-5xl",
|
|
443
|
+
"bg-gradient-to-r bg-clip-text text-transparent",
|
|
444
|
+
colors.gradientFrom,
|
|
445
|
+
colors.gradientTo
|
|
446
|
+
),
|
|
447
|
+
children: title
|
|
448
|
+
}
|
|
449
|
+
),
|
|
450
|
+
/* @__PURE__ */ jsx2("p", { className: "mx-auto max-w-2xl text-lg text-muted-foreground", children: subtitle })
|
|
451
|
+
] }),
|
|
452
|
+
state.loading ? /* @__PURE__ */ jsx2(PricingPageSkeleton, {}) : state.error ? /* @__PURE__ */ jsx2(PricingPageError, { message: state.error, onRetry: loadTiers }) : /* @__PURE__ */ jsx2(
|
|
453
|
+
PricingPage,
|
|
454
|
+
{
|
|
455
|
+
tiers: state.tiers,
|
|
456
|
+
billingPeriod: state.billingPeriod,
|
|
457
|
+
onBillingPeriodChange: handleBillingPeriodChange,
|
|
458
|
+
onSelectTier: handleSelectTier,
|
|
459
|
+
variant,
|
|
460
|
+
loading: state.selectingTier
|
|
461
|
+
}
|
|
462
|
+
),
|
|
463
|
+
/* @__PURE__ */ jsxs2("div", { className: "mx-auto max-w-3xl space-y-6 border-border border-t pt-8", children: [
|
|
464
|
+
/* @__PURE__ */ jsx2("h2", { className: "text-center font-semibold text-xl", children: "Frequently Asked Questions" }),
|
|
465
|
+
/* @__PURE__ */ jsxs2("div", { className: "space-y-4", children: [
|
|
466
|
+
/* @__PURE__ */ jsxs2("details", { className: "group rounded-lg border border-border bg-card p-4", children: [
|
|
467
|
+
/* @__PURE__ */ jsxs2("summary", { className: "flex cursor-pointer list-none items-center justify-between font-medium", children: [
|
|
468
|
+
"What are credits?",
|
|
469
|
+
/* @__PURE__ */ jsxs2(
|
|
470
|
+
"svg",
|
|
471
|
+
{
|
|
472
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
473
|
+
viewBox: "0 0 24 24",
|
|
474
|
+
fill: "none",
|
|
475
|
+
stroke: "currentColor",
|
|
476
|
+
strokeWidth: "2",
|
|
477
|
+
strokeLinecap: "round",
|
|
478
|
+
strokeLinejoin: "round",
|
|
479
|
+
className: "h-4 w-4 transition-transform group-open:rotate-180",
|
|
480
|
+
children: [
|
|
481
|
+
/* @__PURE__ */ jsx2("title", { children: "Toggle icon" }),
|
|
482
|
+
/* @__PURE__ */ jsx2("path", { d: "m6 9 6 6 6-6" })
|
|
483
|
+
]
|
|
484
|
+
}
|
|
485
|
+
)
|
|
486
|
+
] }),
|
|
487
|
+
/* @__PURE__ */ jsx2("p", { className: "mt-3 text-muted-foreground text-sm", children: "Credits are used to pay for AI model usage. Different models consume different amounts of credits based on their complexity and the length of your requests." })
|
|
488
|
+
] }),
|
|
489
|
+
/* @__PURE__ */ jsxs2("details", { className: "group rounded-lg border border-border bg-card p-4", children: [
|
|
490
|
+
/* @__PURE__ */ jsxs2("summary", { className: "flex cursor-pointer list-none items-center justify-between font-medium", children: [
|
|
491
|
+
"Can I change plans later?",
|
|
492
|
+
/* @__PURE__ */ jsxs2(
|
|
493
|
+
"svg",
|
|
494
|
+
{
|
|
495
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
496
|
+
viewBox: "0 0 24 24",
|
|
497
|
+
fill: "none",
|
|
498
|
+
stroke: "currentColor",
|
|
499
|
+
strokeWidth: "2",
|
|
500
|
+
strokeLinecap: "round",
|
|
501
|
+
strokeLinejoin: "round",
|
|
502
|
+
className: "h-4 w-4 transition-transform group-open:rotate-180",
|
|
503
|
+
children: [
|
|
504
|
+
/* @__PURE__ */ jsx2("title", { children: "Toggle icon" }),
|
|
505
|
+
/* @__PURE__ */ jsx2("path", { d: "m6 9 6 6 6-6" })
|
|
506
|
+
]
|
|
507
|
+
}
|
|
508
|
+
)
|
|
509
|
+
] }),
|
|
510
|
+
/* @__PURE__ */ jsx2("p", { className: "mt-3 text-muted-foreground text-sm", children: "Yes! You can upgrade or downgrade your plan at any time. When you upgrade, you will be charged the prorated difference. When you downgrade, the change takes effect at the end of your billing cycle." })
|
|
511
|
+
] }),
|
|
512
|
+
/* @__PURE__ */ jsxs2("details", { className: "group rounded-lg border border-border bg-card p-4", children: [
|
|
513
|
+
/* @__PURE__ */ jsxs2("summary", { className: "flex cursor-pointer list-none items-center justify-between font-medium", children: [
|
|
514
|
+
"Do unused credits roll over?",
|
|
515
|
+
/* @__PURE__ */ jsxs2(
|
|
516
|
+
"svg",
|
|
517
|
+
{
|
|
518
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
519
|
+
viewBox: "0 0 24 24",
|
|
520
|
+
fill: "none",
|
|
521
|
+
stroke: "currentColor",
|
|
522
|
+
strokeWidth: "2",
|
|
523
|
+
strokeLinecap: "round",
|
|
524
|
+
strokeLinejoin: "round",
|
|
525
|
+
className: "h-4 w-4 transition-transform group-open:rotate-180",
|
|
526
|
+
children: [
|
|
527
|
+
/* @__PURE__ */ jsx2("title", { children: "Toggle icon" }),
|
|
528
|
+
/* @__PURE__ */ jsx2("path", { d: "m6 9 6 6 6-6" })
|
|
529
|
+
]
|
|
530
|
+
}
|
|
531
|
+
)
|
|
532
|
+
] }),
|
|
533
|
+
/* @__PURE__ */ jsx2("p", { className: "mt-3 text-muted-foreground text-sm", children: "Monthly credits do not roll over to the next month. However, purchased credit packs never expire and can be used at any time." })
|
|
534
|
+
] })
|
|
535
|
+
] })
|
|
536
|
+
] })
|
|
537
|
+
] });
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// src/pages/profiles-page.tsx
|
|
541
|
+
import {
|
|
542
|
+
AlertCircle,
|
|
543
|
+
ChevronRight,
|
|
544
|
+
Copy,
|
|
545
|
+
Edit2,
|
|
546
|
+
Loader2,
|
|
547
|
+
Plus,
|
|
548
|
+
Search,
|
|
549
|
+
Settings2,
|
|
550
|
+
Trash2
|
|
551
|
+
} from "lucide-react";
|
|
552
|
+
import * as React3 from "react";
|
|
553
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
554
|
+
var TIER_LIMITS = {
|
|
555
|
+
free: 3,
|
|
556
|
+
starter: 10,
|
|
557
|
+
pro: 50,
|
|
558
|
+
enterprise: Number.POSITIVE_INFINITY
|
|
559
|
+
};
|
|
560
|
+
function ProfilesPage({
|
|
561
|
+
apiClient,
|
|
562
|
+
tier = "free",
|
|
563
|
+
maxProfiles,
|
|
564
|
+
onCompareClick,
|
|
565
|
+
title = "Profiles"
|
|
566
|
+
}) {
|
|
567
|
+
const [builtinProfiles, setBuiltinProfiles] = React3.useState([]);
|
|
568
|
+
const [customProfiles, setCustomProfiles] = React3.useState([]);
|
|
569
|
+
const [loading, setLoading] = React3.useState(true);
|
|
570
|
+
const [error, setError] = React3.useState(null);
|
|
571
|
+
const [searchQuery, setSearchQuery] = React3.useState("");
|
|
572
|
+
const [createDialogOpen, setCreateDialogOpen] = React3.useState(false);
|
|
573
|
+
const [editingProfile, setEditingProfile] = React3.useState(
|
|
574
|
+
null
|
|
575
|
+
);
|
|
576
|
+
const [deletingProfile, setDeletingProfile] = React3.useState(
|
|
577
|
+
null
|
|
578
|
+
);
|
|
579
|
+
const [detailProfile, setDetailProfile] = React3.useState(
|
|
580
|
+
null
|
|
581
|
+
);
|
|
582
|
+
const profileLimit = maxProfiles ?? TIER_LIMITS[tier] ?? 3;
|
|
583
|
+
const canCreateMore = customProfiles.length < profileLimit;
|
|
584
|
+
const loadProfiles = React3.useCallback(async () => {
|
|
585
|
+
try {
|
|
586
|
+
setLoading(true);
|
|
587
|
+
setError(null);
|
|
588
|
+
const data = await apiClient.listProfiles();
|
|
589
|
+
setBuiltinProfiles(data.builtin);
|
|
590
|
+
setCustomProfiles(data.custom);
|
|
591
|
+
} catch (err) {
|
|
592
|
+
setError(err instanceof Error ? err.message : "Failed to load profiles");
|
|
593
|
+
} finally {
|
|
594
|
+
setLoading(false);
|
|
595
|
+
}
|
|
596
|
+
}, [apiClient]);
|
|
597
|
+
React3.useEffect(() => {
|
|
598
|
+
loadProfiles();
|
|
599
|
+
}, [loadProfiles]);
|
|
600
|
+
const filteredBuiltin = builtinProfiles.filter(
|
|
601
|
+
(p) => p.name.toLowerCase().includes(searchQuery.toLowerCase()) || p.description?.toLowerCase().includes(searchQuery.toLowerCase())
|
|
602
|
+
);
|
|
603
|
+
const filteredCustom = customProfiles.filter(
|
|
604
|
+
(p) => p.name.toLowerCase().includes(searchQuery.toLowerCase()) || p.description?.toLowerCase().includes(searchQuery.toLowerCase())
|
|
605
|
+
);
|
|
606
|
+
return /* @__PURE__ */ jsxs3("div", { className: "space-y-6", children: [
|
|
607
|
+
/* @__PURE__ */ jsxs3("div", { className: "flex items-center justify-between", children: [
|
|
608
|
+
/* @__PURE__ */ jsxs3("div", { children: [
|
|
609
|
+
/* @__PURE__ */ jsx3("h1", { className: "font-semibold text-2xl", children: title }),
|
|
610
|
+
/* @__PURE__ */ jsx3("p", { className: "text-muted-foreground", children: "Customize agent behavior with system prompts, models, and instructions" })
|
|
611
|
+
] }),
|
|
612
|
+
/* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-3", children: [
|
|
613
|
+
onCompareClick && customProfiles.length >= 2 && /* @__PURE__ */ jsx3(
|
|
614
|
+
Button,
|
|
615
|
+
{
|
|
616
|
+
variant: "outline",
|
|
617
|
+
onClick: () => onCompareClick(customProfiles),
|
|
618
|
+
children: "Compare Profiles"
|
|
619
|
+
}
|
|
620
|
+
),
|
|
621
|
+
/* @__PURE__ */ jsxs3(
|
|
622
|
+
Button,
|
|
623
|
+
{
|
|
624
|
+
onClick: () => setCreateDialogOpen(true),
|
|
625
|
+
disabled: !canCreateMore,
|
|
626
|
+
children: [
|
|
627
|
+
/* @__PURE__ */ jsx3(Plus, { className: "mr-2 h-4 w-4" }),
|
|
628
|
+
"Create Profile"
|
|
629
|
+
]
|
|
630
|
+
}
|
|
631
|
+
)
|
|
632
|
+
] })
|
|
633
|
+
] }),
|
|
634
|
+
!canCreateMore && /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2 rounded-lg border border-yellow-500/30 bg-yellow-500/10 p-3 text-sm text-yellow-400", children: [
|
|
635
|
+
/* @__PURE__ */ jsx3(AlertCircle, { className: "h-4 w-4" }),
|
|
636
|
+
/* @__PURE__ */ jsxs3("span", { children: [
|
|
637
|
+
"You've reached your profile limit (",
|
|
638
|
+
profileLimit,
|
|
639
|
+
" profiles). Upgrade your plan to create more."
|
|
640
|
+
] })
|
|
641
|
+
] }),
|
|
642
|
+
/* @__PURE__ */ jsxs3("div", { className: "relative max-w-md", children: [
|
|
643
|
+
/* @__PURE__ */ jsx3(Search, { className: "absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-muted-foreground" }),
|
|
644
|
+
/* @__PURE__ */ jsx3(
|
|
645
|
+
Input,
|
|
646
|
+
{
|
|
647
|
+
placeholder: "Search profiles...",
|
|
648
|
+
value: searchQuery,
|
|
649
|
+
onChange: (e) => setSearchQuery(e.target.value),
|
|
650
|
+
className: "pl-9"
|
|
651
|
+
}
|
|
652
|
+
)
|
|
653
|
+
] }),
|
|
654
|
+
error && /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2 rounded-lg border border-red-500/30 bg-red-500/10 p-3 text-red-400 text-sm", children: [
|
|
655
|
+
/* @__PURE__ */ jsx3(AlertCircle, { className: "h-4 w-4" }),
|
|
656
|
+
/* @__PURE__ */ jsx3("span", { children: error }),
|
|
657
|
+
/* @__PURE__ */ jsx3(Button, { variant: "ghost", size: "sm", onClick: loadProfiles, children: "Retry" })
|
|
658
|
+
] }),
|
|
659
|
+
loading && /* @__PURE__ */ jsx3("div", { className: "flex items-center justify-center py-12", children: /* @__PURE__ */ jsx3(Loader2, { className: "h-8 w-8 animate-spin text-muted-foreground" }) }),
|
|
660
|
+
!loading && /* @__PURE__ */ jsxs3("div", { className: "space-y-8", children: [
|
|
661
|
+
/* @__PURE__ */ jsxs3("section", { children: [
|
|
662
|
+
/* @__PURE__ */ jsx3("div", { className: "mb-3 flex items-center justify-between", children: /* @__PURE__ */ jsxs3("h2", { className: "font-medium text-lg", children: [
|
|
663
|
+
"Your Profiles (",
|
|
664
|
+
customProfiles.length,
|
|
665
|
+
"/",
|
|
666
|
+
profileLimit === Number.POSITIVE_INFINITY ? "inf" : profileLimit,
|
|
667
|
+
")"
|
|
668
|
+
] }) }),
|
|
669
|
+
filteredCustom.length === 0 ? /* @__PURE__ */ jsx3(
|
|
670
|
+
EmptyState,
|
|
671
|
+
{
|
|
672
|
+
icon: /* @__PURE__ */ jsx3(Settings2, { className: "h-8 w-8" }),
|
|
673
|
+
title: "No custom profiles yet",
|
|
674
|
+
description: "Create a profile to customize agent behavior",
|
|
675
|
+
action: canCreateMore ? /* @__PURE__ */ jsxs3(Button, { onClick: () => setCreateDialogOpen(true), children: [
|
|
676
|
+
/* @__PURE__ */ jsx3(Plus, { className: "mr-2 h-4 w-4" }),
|
|
677
|
+
"Create Profile"
|
|
678
|
+
] }) : void 0
|
|
679
|
+
}
|
|
680
|
+
) : /* @__PURE__ */ jsx3("div", { className: "grid gap-4 md:grid-cols-2 lg:grid-cols-3", children: filteredCustom.map((profile) => /* @__PURE__ */ jsx3(
|
|
681
|
+
ProfileCard,
|
|
682
|
+
{
|
|
683
|
+
profile,
|
|
684
|
+
onView: () => setDetailProfile(profile),
|
|
685
|
+
onEdit: () => setEditingProfile(profile),
|
|
686
|
+
onDelete: () => setDeletingProfile(profile)
|
|
687
|
+
},
|
|
688
|
+
profile.id
|
|
689
|
+
)) })
|
|
690
|
+
] }),
|
|
691
|
+
/* @__PURE__ */ jsxs3("section", { children: [
|
|
692
|
+
/* @__PURE__ */ jsx3("div", { className: "mb-3 flex items-center justify-between", children: /* @__PURE__ */ jsxs3("h2", { className: "font-medium text-lg", children: [
|
|
693
|
+
"Built-in Profiles (",
|
|
694
|
+
builtinProfiles.length,
|
|
695
|
+
")"
|
|
696
|
+
] }) }),
|
|
697
|
+
filteredBuiltin.length === 0 ? /* @__PURE__ */ jsx3("p", { className: "text-muted-foreground text-sm", children: "No matching built-in profiles" }) : /* @__PURE__ */ jsx3("div", { className: "grid gap-4 md:grid-cols-2 lg:grid-cols-3", children: filteredBuiltin.map((profile) => /* @__PURE__ */ jsx3(
|
|
698
|
+
ProfileCard,
|
|
699
|
+
{
|
|
700
|
+
profile,
|
|
701
|
+
onView: () => setDetailProfile(profile),
|
|
702
|
+
isBuiltin: true
|
|
703
|
+
},
|
|
704
|
+
profile.id
|
|
705
|
+
)) })
|
|
706
|
+
] })
|
|
707
|
+
] }),
|
|
708
|
+
/* @__PURE__ */ jsx3(
|
|
709
|
+
ProfileFormDialog,
|
|
710
|
+
{
|
|
711
|
+
open: createDialogOpen || !!editingProfile,
|
|
712
|
+
onClose: () => {
|
|
713
|
+
setCreateDialogOpen(false);
|
|
714
|
+
setEditingProfile(null);
|
|
715
|
+
},
|
|
716
|
+
profile: editingProfile,
|
|
717
|
+
builtinProfiles,
|
|
718
|
+
apiClient,
|
|
719
|
+
onSuccess: () => {
|
|
720
|
+
setCreateDialogOpen(false);
|
|
721
|
+
setEditingProfile(null);
|
|
722
|
+
loadProfiles();
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
),
|
|
726
|
+
/* @__PURE__ */ jsx3(
|
|
727
|
+
DeleteProfileDialog,
|
|
728
|
+
{
|
|
729
|
+
profile: deletingProfile,
|
|
730
|
+
onClose: () => setDeletingProfile(null),
|
|
731
|
+
apiClient,
|
|
732
|
+
onSuccess: () => {
|
|
733
|
+
setDeletingProfile(null);
|
|
734
|
+
loadProfiles();
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
),
|
|
738
|
+
/* @__PURE__ */ jsx3(
|
|
739
|
+
ProfileDetailDialog,
|
|
740
|
+
{
|
|
741
|
+
profile: detailProfile,
|
|
742
|
+
onClose: () => setDetailProfile(null),
|
|
743
|
+
onEdit: detailProfile && !detailProfile.is_builtin ? () => {
|
|
744
|
+
setDetailProfile(null);
|
|
745
|
+
setEditingProfile(detailProfile);
|
|
746
|
+
} : void 0,
|
|
747
|
+
onDuplicate: canCreateMore ? () => {
|
|
748
|
+
setDetailProfile(null);
|
|
749
|
+
setEditingProfile({
|
|
750
|
+
...detailProfile,
|
|
751
|
+
id: "",
|
|
752
|
+
name: `${detailProfile?.name}-copy`,
|
|
753
|
+
is_builtin: false
|
|
754
|
+
});
|
|
755
|
+
} : void 0
|
|
756
|
+
}
|
|
757
|
+
)
|
|
758
|
+
] });
|
|
759
|
+
}
|
|
760
|
+
function ProfileCard({
|
|
761
|
+
profile,
|
|
762
|
+
onView,
|
|
763
|
+
onEdit,
|
|
764
|
+
onDelete,
|
|
765
|
+
isBuiltin
|
|
766
|
+
}) {
|
|
767
|
+
return /* @__PURE__ */ jsxs3(
|
|
768
|
+
Card,
|
|
769
|
+
{
|
|
770
|
+
className: "cursor-pointer p-4 transition-colors hover:border-border/80",
|
|
771
|
+
onClick: onView,
|
|
772
|
+
children: [
|
|
773
|
+
/* @__PURE__ */ jsxs3("div", { className: "flex items-start justify-between", children: [
|
|
774
|
+
/* @__PURE__ */ jsxs3("div", { className: "flex-1", children: [
|
|
775
|
+
/* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2", children: [
|
|
776
|
+
/* @__PURE__ */ jsx3("h3", { className: "font-medium", children: profile.name }),
|
|
777
|
+
isBuiltin && /* @__PURE__ */ jsx3(Badge, { variant: "secondary", className: "border-0 text-xs", children: "Built-in" }),
|
|
778
|
+
profile.is_public && !isBuiltin && /* @__PURE__ */ jsx3(Badge, { className: "border-0 bg-blue-500/10 text-blue-400 text-xs", children: "Public" })
|
|
779
|
+
] }),
|
|
780
|
+
profile.description && /* @__PURE__ */ jsx3("p", { className: "mt-1 line-clamp-2 text-muted-foreground text-sm", children: profile.description })
|
|
781
|
+
] }),
|
|
782
|
+
!isBuiltin && /* @__PURE__ */ jsxs3("div", { className: "flex gap-1", onClick: (e) => e.stopPropagation(), children: [
|
|
783
|
+
onEdit && /* @__PURE__ */ jsx3(
|
|
784
|
+
Button,
|
|
785
|
+
{
|
|
786
|
+
variant: "ghost",
|
|
787
|
+
size: "icon",
|
|
788
|
+
onClick: onEdit,
|
|
789
|
+
"aria-label": "Edit profile",
|
|
790
|
+
children: /* @__PURE__ */ jsx3(Edit2, { className: "h-4 w-4" })
|
|
791
|
+
}
|
|
792
|
+
),
|
|
793
|
+
onDelete && /* @__PURE__ */ jsx3(
|
|
794
|
+
Button,
|
|
795
|
+
{
|
|
796
|
+
variant: "ghost",
|
|
797
|
+
size: "icon",
|
|
798
|
+
onClick: onDelete,
|
|
799
|
+
"aria-label": "Delete profile",
|
|
800
|
+
children: /* @__PURE__ */ jsx3(Trash2, { className: "h-4 w-4 text-red-400" })
|
|
801
|
+
}
|
|
802
|
+
)
|
|
803
|
+
] })
|
|
804
|
+
] }),
|
|
805
|
+
/* @__PURE__ */ jsxs3("div", { className: "mt-3 flex flex-wrap gap-2", children: [
|
|
806
|
+
profile.extends && /* @__PURE__ */ jsxs3(Badge, { variant: "outline", className: "text-xs", children: [
|
|
807
|
+
"extends ",
|
|
808
|
+
profile.extends
|
|
809
|
+
] }),
|
|
810
|
+
profile.model && /* @__PURE__ */ jsx3(Badge, { variant: "outline", className: "text-xs", children: profile.model.split("/").pop() })
|
|
811
|
+
] }),
|
|
812
|
+
profile.metrics && profile.metrics.total_runs > 0 && /* @__PURE__ */ jsxs3("div", { className: "mt-3 flex gap-4 border-border border-t pt-3 text-muted-foreground text-xs", children: [
|
|
813
|
+
/* @__PURE__ */ jsxs3("span", { children: [
|
|
814
|
+
profile.metrics.total_runs,
|
|
815
|
+
" runs"
|
|
816
|
+
] }),
|
|
817
|
+
/* @__PURE__ */ jsxs3("span", { children: [
|
|
818
|
+
profile.metrics.success_rate.toFixed(0),
|
|
819
|
+
"% success"
|
|
820
|
+
] }),
|
|
821
|
+
/* @__PURE__ */ jsxs3("span", { children: [
|
|
822
|
+
"~",
|
|
823
|
+
(profile.metrics.avg_duration_ms / 1e3).toFixed(1),
|
|
824
|
+
"s avg"
|
|
825
|
+
] })
|
|
826
|
+
] })
|
|
827
|
+
]
|
|
828
|
+
}
|
|
829
|
+
);
|
|
830
|
+
}
|
|
831
|
+
function ProfileFormDialog({
|
|
832
|
+
open,
|
|
833
|
+
onClose,
|
|
834
|
+
profile,
|
|
835
|
+
builtinProfiles,
|
|
836
|
+
apiClient,
|
|
837
|
+
onSuccess
|
|
838
|
+
}) {
|
|
839
|
+
const isEditing = !!profile?.id;
|
|
840
|
+
const [formData, setFormData] = React3.useState({
|
|
841
|
+
name: "",
|
|
842
|
+
description: "",
|
|
843
|
+
extends: "",
|
|
844
|
+
model: "",
|
|
845
|
+
system_prompt: "",
|
|
846
|
+
instructions: [],
|
|
847
|
+
tags: [],
|
|
848
|
+
is_public: false
|
|
849
|
+
});
|
|
850
|
+
const [saving, setSaving] = React3.useState(false);
|
|
851
|
+
const [error, setError] = React3.useState(null);
|
|
852
|
+
React3.useEffect(() => {
|
|
853
|
+
if (profile) {
|
|
854
|
+
setFormData({
|
|
855
|
+
name: profile.name,
|
|
856
|
+
description: profile.description ?? "",
|
|
857
|
+
extends: profile.extends ?? "",
|
|
858
|
+
model: profile.model ?? "",
|
|
859
|
+
system_prompt: profile.system_prompt ?? "",
|
|
860
|
+
instructions: profile.instructions ?? [],
|
|
861
|
+
tags: profile.tags ?? [],
|
|
862
|
+
is_public: profile.is_public ?? false
|
|
863
|
+
});
|
|
864
|
+
} else {
|
|
865
|
+
setFormData({
|
|
866
|
+
name: "",
|
|
867
|
+
description: "",
|
|
868
|
+
extends: "",
|
|
869
|
+
model: "",
|
|
870
|
+
system_prompt: "",
|
|
871
|
+
instructions: [],
|
|
872
|
+
tags: [],
|
|
873
|
+
is_public: false
|
|
874
|
+
});
|
|
875
|
+
}
|
|
876
|
+
}, [profile]);
|
|
877
|
+
const handleSubmit = async (e) => {
|
|
878
|
+
e.preventDefault();
|
|
879
|
+
setError(null);
|
|
880
|
+
setSaving(true);
|
|
881
|
+
try {
|
|
882
|
+
const cleanData = {
|
|
883
|
+
name: formData.name,
|
|
884
|
+
...formData.description && { description: formData.description },
|
|
885
|
+
...formData.extends && { extends: formData.extends },
|
|
886
|
+
...formData.model && { model: formData.model },
|
|
887
|
+
...formData.system_prompt && {
|
|
888
|
+
system_prompt: formData.system_prompt
|
|
889
|
+
},
|
|
890
|
+
...formData.instructions?.length && {
|
|
891
|
+
instructions: formData.instructions
|
|
892
|
+
},
|
|
893
|
+
...formData.tags?.length && { tags: formData.tags },
|
|
894
|
+
is_public: formData.is_public
|
|
895
|
+
};
|
|
896
|
+
if (isEditing) {
|
|
897
|
+
await apiClient.updateProfile(profile.id, cleanData);
|
|
898
|
+
} else {
|
|
899
|
+
await apiClient.createProfile(cleanData);
|
|
900
|
+
}
|
|
901
|
+
onSuccess();
|
|
902
|
+
} catch (err) {
|
|
903
|
+
setError(err instanceof Error ? err.message : "Failed to save profile");
|
|
904
|
+
} finally {
|
|
905
|
+
setSaving(false);
|
|
906
|
+
}
|
|
907
|
+
};
|
|
908
|
+
return /* @__PURE__ */ jsx3(Dialog, { open, onOpenChange: (o) => !o && onClose(), children: /* @__PURE__ */ jsxs3(DialogContent, { className: "max-w-2xl", children: [
|
|
909
|
+
/* @__PURE__ */ jsxs3(DialogHeader, { children: [
|
|
910
|
+
/* @__PURE__ */ jsx3(DialogTitle, { children: isEditing ? "Edit Profile" : "Create Profile" }),
|
|
911
|
+
/* @__PURE__ */ jsx3(DialogDescription, { children: isEditing ? "Update your custom profile configuration" : "Create a new profile to customize agent behavior" })
|
|
912
|
+
] }),
|
|
913
|
+
/* @__PURE__ */ jsxs3("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
|
|
914
|
+
error && /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2 rounded-lg border border-red-500/30 bg-red-500/10 p-3 text-red-400 text-sm", children: [
|
|
915
|
+
/* @__PURE__ */ jsx3(AlertCircle, { className: "h-4 w-4" }),
|
|
916
|
+
/* @__PURE__ */ jsx3("span", { children: error })
|
|
917
|
+
] }),
|
|
918
|
+
/* @__PURE__ */ jsxs3("div", { children: [
|
|
919
|
+
/* @__PURE__ */ jsx3("label", { className: "mb-1 block font-medium text-sm", children: "Name *" }),
|
|
920
|
+
/* @__PURE__ */ jsx3(
|
|
921
|
+
Input,
|
|
922
|
+
{
|
|
923
|
+
value: formData.name,
|
|
924
|
+
onChange: (e) => setFormData((d) => ({ ...d, name: e.target.value })),
|
|
925
|
+
placeholder: "my-custom-profile",
|
|
926
|
+
required: true,
|
|
927
|
+
pattern: "^[a-zA-Z0-9_-]+$",
|
|
928
|
+
title: "Only letters, numbers, hyphens, and underscores"
|
|
929
|
+
}
|
|
930
|
+
)
|
|
931
|
+
] }),
|
|
932
|
+
/* @__PURE__ */ jsxs3("div", { children: [
|
|
933
|
+
/* @__PURE__ */ jsx3("label", { className: "mb-1 block font-medium text-sm", children: "Description" }),
|
|
934
|
+
/* @__PURE__ */ jsx3(
|
|
935
|
+
Input,
|
|
936
|
+
{
|
|
937
|
+
value: formData.description,
|
|
938
|
+
onChange: (e) => setFormData((d) => ({ ...d, description: e.target.value })),
|
|
939
|
+
placeholder: "A brief description of this profile"
|
|
940
|
+
}
|
|
941
|
+
)
|
|
942
|
+
] }),
|
|
943
|
+
/* @__PURE__ */ jsxs3("div", { children: [
|
|
944
|
+
/* @__PURE__ */ jsx3("label", { className: "mb-1 block font-medium text-sm", children: "Extends (base profile)" }),
|
|
945
|
+
/* @__PURE__ */ jsxs3(
|
|
946
|
+
"select",
|
|
947
|
+
{
|
|
948
|
+
value: formData.extends,
|
|
949
|
+
onChange: (e) => setFormData((d) => ({ ...d, extends: e.target.value })),
|
|
950
|
+
className: "w-full rounded-md border border-border bg-background px-3 py-2 text-sm",
|
|
951
|
+
children: [
|
|
952
|
+
/* @__PURE__ */ jsx3("option", { value: "", children: "None (start from scratch)" }),
|
|
953
|
+
builtinProfiles.map((p) => /* @__PURE__ */ jsxs3("option", { value: p.name, children: [
|
|
954
|
+
p.name,
|
|
955
|
+
" - ",
|
|
956
|
+
p.description ?? "Built-in profile"
|
|
957
|
+
] }, p.id))
|
|
958
|
+
]
|
|
959
|
+
}
|
|
960
|
+
)
|
|
961
|
+
] }),
|
|
962
|
+
/* @__PURE__ */ jsxs3("div", { children: [
|
|
963
|
+
/* @__PURE__ */ jsx3("label", { className: "mb-1 block font-medium text-sm", children: "Model" }),
|
|
964
|
+
/* @__PURE__ */ jsx3(
|
|
965
|
+
Input,
|
|
966
|
+
{
|
|
967
|
+
value: formData.model,
|
|
968
|
+
onChange: (e) => setFormData((d) => ({ ...d, model: e.target.value })),
|
|
969
|
+
placeholder: "anthropic/claude-sonnet-4"
|
|
970
|
+
}
|
|
971
|
+
),
|
|
972
|
+
/* @__PURE__ */ jsx3("p", { className: "mt-1 text-muted-foreground text-xs", children: "Format: provider/model-id (e.g., anthropic/claude-sonnet-4)" })
|
|
973
|
+
] }),
|
|
974
|
+
/* @__PURE__ */ jsxs3("div", { children: [
|
|
975
|
+
/* @__PURE__ */ jsx3("label", { className: "mb-1 block font-medium text-sm", children: "System Prompt" }),
|
|
976
|
+
/* @__PURE__ */ jsx3(
|
|
977
|
+
"textarea",
|
|
978
|
+
{
|
|
979
|
+
value: formData.system_prompt,
|
|
980
|
+
onChange: (e) => setFormData((d) => ({ ...d, system_prompt: e.target.value })),
|
|
981
|
+
placeholder: "Custom system prompt for the agent...",
|
|
982
|
+
rows: 4,
|
|
983
|
+
className: "w-full rounded-md border border-border bg-background px-3 py-2 font-mono text-sm"
|
|
984
|
+
}
|
|
985
|
+
)
|
|
986
|
+
] }),
|
|
987
|
+
/* @__PURE__ */ jsxs3("div", { children: [
|
|
988
|
+
/* @__PURE__ */ jsx3("label", { className: "mb-1 block font-medium text-sm", children: "Tags" }),
|
|
989
|
+
/* @__PURE__ */ jsx3(
|
|
990
|
+
Input,
|
|
991
|
+
{
|
|
992
|
+
value: formData.tags?.join(", "),
|
|
993
|
+
onChange: (e) => setFormData((d) => ({
|
|
994
|
+
...d,
|
|
995
|
+
tags: e.target.value.split(",").map((t) => t.trim()).filter(Boolean)
|
|
996
|
+
})),
|
|
997
|
+
placeholder: "trading, aggressive, experimental"
|
|
998
|
+
}
|
|
999
|
+
),
|
|
1000
|
+
/* @__PURE__ */ jsx3("p", { className: "mt-1 text-muted-foreground text-xs", children: "Comma-separated tags for organization" })
|
|
1001
|
+
] }),
|
|
1002
|
+
/* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2", children: [
|
|
1003
|
+
/* @__PURE__ */ jsx3(
|
|
1004
|
+
"input",
|
|
1005
|
+
{
|
|
1006
|
+
type: "checkbox",
|
|
1007
|
+
id: "is_public",
|
|
1008
|
+
checked: formData.is_public,
|
|
1009
|
+
onChange: (e) => setFormData((d) => ({ ...d, is_public: e.target.checked })),
|
|
1010
|
+
className: "rounded border-border"
|
|
1011
|
+
}
|
|
1012
|
+
),
|
|
1013
|
+
/* @__PURE__ */ jsx3("label", { htmlFor: "is_public", className: "text-sm", children: "Make this profile public (visible to other users)" })
|
|
1014
|
+
] }),
|
|
1015
|
+
/* @__PURE__ */ jsxs3(DialogFooter, { children: [
|
|
1016
|
+
/* @__PURE__ */ jsx3(Button, { type: "button", variant: "outline", onClick: onClose, children: "Cancel" }),
|
|
1017
|
+
/* @__PURE__ */ jsxs3(Button, { type: "submit", disabled: saving || !formData.name, children: [
|
|
1018
|
+
saving && /* @__PURE__ */ jsx3(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }),
|
|
1019
|
+
isEditing ? "Save Changes" : "Create Profile"
|
|
1020
|
+
] })
|
|
1021
|
+
] })
|
|
1022
|
+
] })
|
|
1023
|
+
] }) });
|
|
1024
|
+
}
|
|
1025
|
+
function DeleteProfileDialog({
|
|
1026
|
+
profile,
|
|
1027
|
+
onClose,
|
|
1028
|
+
apiClient,
|
|
1029
|
+
onSuccess
|
|
1030
|
+
}) {
|
|
1031
|
+
const [deleting, setDeleting] = React3.useState(false);
|
|
1032
|
+
const [error, setError] = React3.useState(null);
|
|
1033
|
+
const handleDelete = async () => {
|
|
1034
|
+
if (!profile) return;
|
|
1035
|
+
setError(null);
|
|
1036
|
+
setDeleting(true);
|
|
1037
|
+
try {
|
|
1038
|
+
await apiClient.deleteProfile(profile.id);
|
|
1039
|
+
onSuccess();
|
|
1040
|
+
} catch (err) {
|
|
1041
|
+
setError(err instanceof Error ? err.message : "Failed to delete profile");
|
|
1042
|
+
} finally {
|
|
1043
|
+
setDeleting(false);
|
|
1044
|
+
}
|
|
1045
|
+
};
|
|
1046
|
+
return /* @__PURE__ */ jsx3(Dialog, { open: !!profile, onOpenChange: (o) => !o && onClose(), children: /* @__PURE__ */ jsxs3(DialogContent, { children: [
|
|
1047
|
+
/* @__PURE__ */ jsxs3(DialogHeader, { children: [
|
|
1048
|
+
/* @__PURE__ */ jsx3(DialogTitle, { children: "Delete Profile" }),
|
|
1049
|
+
/* @__PURE__ */ jsxs3(DialogDescription, { children: [
|
|
1050
|
+
'Are you sure you want to delete "',
|
|
1051
|
+
profile?.name,
|
|
1052
|
+
'"? This action cannot be undone.'
|
|
1053
|
+
] })
|
|
1054
|
+
] }),
|
|
1055
|
+
error && /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2 rounded-lg border border-red-500/30 bg-red-500/10 p-3 text-red-400 text-sm", children: [
|
|
1056
|
+
/* @__PURE__ */ jsx3(AlertCircle, { className: "h-4 w-4" }),
|
|
1057
|
+
/* @__PURE__ */ jsx3("span", { children: error })
|
|
1058
|
+
] }),
|
|
1059
|
+
profile?.metrics && profile.metrics.total_runs > 0 && /* @__PURE__ */ jsxs3("div", { className: "rounded-lg border border-yellow-500/30 bg-yellow-500/10 p-3 text-sm text-yellow-400", children: [
|
|
1060
|
+
"This profile has ",
|
|
1061
|
+
profile.metrics.total_runs,
|
|
1062
|
+
" recorded runs. Deleting it will lose all performance metrics."
|
|
1063
|
+
] }),
|
|
1064
|
+
/* @__PURE__ */ jsxs3(DialogFooter, { children: [
|
|
1065
|
+
/* @__PURE__ */ jsx3(Button, { variant: "outline", onClick: onClose, children: "Cancel" }),
|
|
1066
|
+
/* @__PURE__ */ jsxs3(
|
|
1067
|
+
Button,
|
|
1068
|
+
{
|
|
1069
|
+
variant: "destructive",
|
|
1070
|
+
onClick: handleDelete,
|
|
1071
|
+
disabled: deleting,
|
|
1072
|
+
children: [
|
|
1073
|
+
deleting && /* @__PURE__ */ jsx3(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }),
|
|
1074
|
+
"Delete Profile"
|
|
1075
|
+
]
|
|
1076
|
+
}
|
|
1077
|
+
)
|
|
1078
|
+
] })
|
|
1079
|
+
] }) });
|
|
1080
|
+
}
|
|
1081
|
+
function ProfileDetailDialog({
|
|
1082
|
+
profile,
|
|
1083
|
+
onClose,
|
|
1084
|
+
onEdit,
|
|
1085
|
+
onDuplicate
|
|
1086
|
+
}) {
|
|
1087
|
+
if (!profile) return null;
|
|
1088
|
+
return /* @__PURE__ */ jsx3(Dialog, { open: !!profile, onOpenChange: (o) => !o && onClose(), children: /* @__PURE__ */ jsxs3(DialogContent, { className: "max-w-2xl", children: [
|
|
1089
|
+
/* @__PURE__ */ jsxs3(DialogHeader, { children: [
|
|
1090
|
+
/* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2", children: [
|
|
1091
|
+
/* @__PURE__ */ jsx3(DialogTitle, { children: profile.name }),
|
|
1092
|
+
profile.is_builtin && /* @__PURE__ */ jsx3(Badge, { variant: "secondary", className: "border-0", children: "Built-in" }),
|
|
1093
|
+
profile.is_public && !profile.is_builtin && /* @__PURE__ */ jsx3(Badge, { className: "border-0 bg-blue-500/10 text-blue-400", children: "Public" })
|
|
1094
|
+
] }),
|
|
1095
|
+
profile.description && /* @__PURE__ */ jsx3(DialogDescription, { children: profile.description })
|
|
1096
|
+
] }),
|
|
1097
|
+
/* @__PURE__ */ jsxs3("div", { className: "space-y-4", children: [
|
|
1098
|
+
/* @__PURE__ */ jsxs3("div", { className: "grid gap-4 sm:grid-cols-2", children: [
|
|
1099
|
+
profile.extends && /* @__PURE__ */ jsxs3("div", { children: [
|
|
1100
|
+
/* @__PURE__ */ jsx3("label", { className: "font-medium text-muted-foreground text-xs", children: "Extends" }),
|
|
1101
|
+
/* @__PURE__ */ jsx3("p", { className: "text-sm", children: profile.extends })
|
|
1102
|
+
] }),
|
|
1103
|
+
profile.model && /* @__PURE__ */ jsxs3("div", { children: [
|
|
1104
|
+
/* @__PURE__ */ jsx3("label", { className: "font-medium text-muted-foreground text-xs", children: "Model" }),
|
|
1105
|
+
/* @__PURE__ */ jsx3("p", { className: "text-sm", children: profile.model })
|
|
1106
|
+
] })
|
|
1107
|
+
] }),
|
|
1108
|
+
profile.tags && profile.tags.length > 0 && /* @__PURE__ */ jsxs3("div", { children: [
|
|
1109
|
+
/* @__PURE__ */ jsx3("label", { className: "font-medium text-muted-foreground text-xs", children: "Tags" }),
|
|
1110
|
+
/* @__PURE__ */ jsx3("div", { className: "mt-1 flex flex-wrap gap-1", children: profile.tags.map((tag) => /* @__PURE__ */ jsx3(Badge, { variant: "outline", className: "text-xs", children: tag }, tag)) })
|
|
1111
|
+
] }),
|
|
1112
|
+
profile.system_prompt && /* @__PURE__ */ jsxs3("div", { children: [
|
|
1113
|
+
/* @__PURE__ */ jsx3("label", { className: "font-medium text-muted-foreground text-xs", children: "System Prompt" }),
|
|
1114
|
+
/* @__PURE__ */ jsxs3("div", { className: "relative mt-1", children: [
|
|
1115
|
+
/* @__PURE__ */ jsx3("pre", { className: "max-h-48 overflow-auto rounded-lg bg-muted p-3 font-mono text-sm", children: profile.system_prompt }),
|
|
1116
|
+
/* @__PURE__ */ jsx3(
|
|
1117
|
+
Button,
|
|
1118
|
+
{
|
|
1119
|
+
variant: "ghost",
|
|
1120
|
+
size: "icon",
|
|
1121
|
+
className: "absolute top-2 right-2",
|
|
1122
|
+
onClick: () => navigator.clipboard.writeText(profile.system_prompt),
|
|
1123
|
+
"aria-label": "Copy system prompt",
|
|
1124
|
+
children: /* @__PURE__ */ jsx3(Copy, { className: "h-4 w-4" })
|
|
1125
|
+
}
|
|
1126
|
+
)
|
|
1127
|
+
] })
|
|
1128
|
+
] }),
|
|
1129
|
+
profile.instructions && profile.instructions.length > 0 && /* @__PURE__ */ jsxs3("div", { children: [
|
|
1130
|
+
/* @__PURE__ */ jsx3("label", { className: "font-medium text-muted-foreground text-xs", children: "Instructions" }),
|
|
1131
|
+
/* @__PURE__ */ jsx3("ul", { className: "mt-1 space-y-1", children: profile.instructions.map((inst, i) => /* @__PURE__ */ jsxs3("li", { className: "flex items-start gap-2 text-sm", children: [
|
|
1132
|
+
/* @__PURE__ */ jsx3(ChevronRight, { className: "mt-0.5 h-4 w-4 text-muted-foreground" }),
|
|
1133
|
+
inst
|
|
1134
|
+
] }, i)) })
|
|
1135
|
+
] }),
|
|
1136
|
+
profile.metrics && profile.metrics.total_runs > 0 && /* @__PURE__ */ jsxs3("div", { className: "rounded-lg border border-border p-4", children: [
|
|
1137
|
+
/* @__PURE__ */ jsx3("label", { className: "font-medium text-muted-foreground text-xs", children: "Performance Metrics" }),
|
|
1138
|
+
/* @__PURE__ */ jsxs3("div", { className: "mt-2 grid grid-cols-2 gap-4 sm:grid-cols-4", children: [
|
|
1139
|
+
/* @__PURE__ */ jsxs3("div", { children: [
|
|
1140
|
+
/* @__PURE__ */ jsx3("p", { className: "font-medium text-2xl", children: profile.metrics.total_runs }),
|
|
1141
|
+
/* @__PURE__ */ jsx3("p", { className: "text-muted-foreground text-xs", children: "Total Runs" })
|
|
1142
|
+
] }),
|
|
1143
|
+
/* @__PURE__ */ jsxs3("div", { children: [
|
|
1144
|
+
/* @__PURE__ */ jsxs3("p", { className: "font-medium text-2xl", children: [
|
|
1145
|
+
profile.metrics.success_rate.toFixed(0),
|
|
1146
|
+
"%"
|
|
1147
|
+
] }),
|
|
1148
|
+
/* @__PURE__ */ jsx3("p", { className: "text-muted-foreground text-xs", children: "Success Rate" })
|
|
1149
|
+
] }),
|
|
1150
|
+
/* @__PURE__ */ jsxs3("div", { children: [
|
|
1151
|
+
/* @__PURE__ */ jsxs3("p", { className: "font-medium text-2xl", children: [
|
|
1152
|
+
(profile.metrics.avg_duration_ms / 1e3).toFixed(1),
|
|
1153
|
+
"s"
|
|
1154
|
+
] }),
|
|
1155
|
+
/* @__PURE__ */ jsx3("p", { className: "text-muted-foreground text-xs", children: "Avg Duration" })
|
|
1156
|
+
] }),
|
|
1157
|
+
profile.metrics.avg_tokens_used && /* @__PURE__ */ jsxs3("div", { children: [
|
|
1158
|
+
/* @__PURE__ */ jsx3("p", { className: "font-medium text-2xl", children: profile.metrics.avg_tokens_used.toLocaleString() }),
|
|
1159
|
+
/* @__PURE__ */ jsx3("p", { className: "text-muted-foreground text-xs", children: "Avg Tokens" })
|
|
1160
|
+
] })
|
|
1161
|
+
] })
|
|
1162
|
+
] })
|
|
1163
|
+
] }),
|
|
1164
|
+
/* @__PURE__ */ jsxs3(DialogFooter, { children: [
|
|
1165
|
+
/* @__PURE__ */ jsx3(Button, { variant: "outline", onClick: onClose, children: "Close" }),
|
|
1166
|
+
onDuplicate && /* @__PURE__ */ jsxs3(Button, { variant: "outline", onClick: onDuplicate, children: [
|
|
1167
|
+
/* @__PURE__ */ jsx3(Copy, { className: "mr-2 h-4 w-4" }),
|
|
1168
|
+
"Duplicate"
|
|
1169
|
+
] }),
|
|
1170
|
+
onEdit && /* @__PURE__ */ jsxs3(Button, { onClick: onEdit, children: [
|
|
1171
|
+
/* @__PURE__ */ jsx3(Edit2, { className: "mr-2 h-4 w-4" }),
|
|
1172
|
+
"Edit"
|
|
1173
|
+
] })
|
|
1174
|
+
] })
|
|
1175
|
+
] }) });
|
|
1176
|
+
}
|
|
1177
|
+
export {
|
|
1178
|
+
BillingPage,
|
|
1179
|
+
ProfilesPage,
|
|
1180
|
+
StandalonePricingPage
|
|
1181
|
+
};
|