@tangle-network/sandbox-ui 0.14.0 → 0.15.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/dist/auth.d.ts +1 -74
  2. package/dist/auth.js +1 -4
  3. package/dist/chat.d.ts +1 -136
  4. package/dist/chat.js +2 -15
  5. package/dist/chunk-2BUPSB7O.js +0 -0
  6. package/dist/chunk-3J6FG3FJ.js +18 -0
  7. package/dist/chunk-76IQLPW2.js +206 -0
  8. package/dist/chunk-7ZA5SEK3.js +239 -0
  9. package/dist/chunk-AZ3AWMTM.js +8 -0
  10. package/dist/chunk-CMY7W45U.js +380 -0
  11. package/dist/chunk-EI44GEQ5.js +6 -0
  12. package/dist/chunk-ENMWGVDL.js +858 -0
  13. package/dist/{chunk-5OQ27N57.js → chunk-GPT7VKK6.js} +34 -38
  14. package/dist/chunk-HLZTKSGT.js +2652 -0
  15. package/dist/chunk-JBGKGLD7.js +16 -0
  16. package/dist/chunk-NJNME4J4.js +14 -0
  17. package/dist/chunk-QPAJR74X.js +20 -0
  18. package/dist/chunk-TK46XFLM.js +28 -0
  19. package/dist/chunk-WID73FPH.js +89 -0
  20. package/dist/chunk-YVXK4XRO.js +30 -0
  21. package/dist/dashboard.d.ts +450 -4
  22. package/dist/dashboard.js +20 -891
  23. package/dist/editor.d.ts +1 -120
  24. package/dist/editor.js +1 -5
  25. package/dist/files.d.ts +1 -129
  26. package/dist/files.js +2 -7
  27. package/dist/globals.css +2 -1265
  28. package/dist/hooks.d.ts +114 -11
  29. package/dist/hooks.js +17 -88
  30. package/dist/index.d.ts +24 -99
  31. package/dist/index.js +251 -256
  32. package/dist/markdown.d.ts +1 -29
  33. package/dist/markdown.js +2 -2
  34. package/dist/openui.d.ts +8 -115
  35. package/dist/openui.js +1 -6
  36. package/dist/pages.d.ts +13 -12
  37. package/dist/pages.js +91 -115
  38. package/dist/primitives.d.ts +14 -49
  39. package/dist/primitives.js +69 -77
  40. package/dist/run.d.ts +1 -14
  41. package/dist/run.js +2 -22
  42. package/dist/sdk-hooks.d.ts +3 -283
  43. package/dist/sdk-hooks.js +10 -14
  44. package/dist/stores.d.ts +2 -14
  45. package/dist/stores.js +11 -39
  46. package/dist/styles.css +2 -1265
  47. package/dist/template-card-DStb8boW.d.ts +183 -0
  48. package/dist/types.d.ts +11 -8
  49. package/dist/types.js +1 -0
  50. package/dist/utils.d.ts +1 -44
  51. package/dist/utils.js +6 -12
  52. package/dist/workspace.d.ts +5 -10
  53. package/dist/workspace.js +3 -19
  54. package/package.json +19 -54
  55. package/dist/active-sessions-store-CeOmXgv5.d.ts +0 -85
  56. package/dist/artifact-pane-Bh45Ssco.d.ts +0 -24
  57. package/dist/branding-DCi5VEik.d.ts +0 -13
  58. package/dist/button-CMQuQEW_.d.ts +0 -17
  59. package/dist/chat-container-f4yEs6KN.d.ts +0 -106
  60. package/dist/chunk-34A66VBG.js +0 -214
  61. package/dist/chunk-34I7UFSX.js +0 -92
  62. package/dist/chunk-36QY2W5G.js +0 -802
  63. package/dist/chunk-4CLN43XT.js +0 -45
  64. package/dist/chunk-54SQQMMM.js +0 -156
  65. package/dist/chunk-66EZOYZR.js +0 -102
  66. package/dist/chunk-BX6AQMUS.js +0 -183
  67. package/dist/chunk-DI3NZ5ZX.js +0 -192
  68. package/dist/chunk-DPGIXDAI.js +0 -220
  69. package/dist/chunk-DXMIEK4K.js +0 -1426
  70. package/dist/chunk-GSZA3TSY.js +0 -79
  71. package/dist/chunk-HB5Y37YU.js +0 -54
  72. package/dist/chunk-LQNEZDRM.js +0 -109
  73. package/dist/chunk-MA7YKRUP.js +0 -131
  74. package/dist/chunk-MKTSMWVD.js +0 -109
  75. package/dist/chunk-MQXABZTB.js +0 -1348
  76. package/dist/chunk-MT5FJ3ZT.js +0 -186
  77. package/dist/chunk-NKUPJC34.js +0 -2070
  78. package/dist/chunk-OEX7NZE3.js +0 -321
  79. package/dist/chunk-OKLQVY3Y.js +0 -139
  80. package/dist/chunk-Q56BYXQF.js +0 -61
  81. package/dist/chunk-QD4QE5P5.js +0 -40
  82. package/dist/chunk-QDH5GEGY.js +0 -630
  83. package/dist/chunk-QID2OOMG.js +0 -133
  84. package/dist/chunk-QMU2PWOU.js +0 -493
  85. package/dist/chunk-RQHJBTEU.js +0 -10
  86. package/dist/chunk-T7HMZEVO.js +0 -216
  87. package/dist/chunk-U6QTHMY6.js +0 -1290
  88. package/dist/chunk-US6JKJKH.js +0 -124
  89. package/dist/chunk-VX3XOUEB.js +0 -63
  90. package/dist/chunk-XLG757B6.js +0 -933
  91. package/dist/chunk-ZMNSRDMH.js +0 -127
  92. package/dist/chunk-ZNCEM5CD.js +0 -316
  93. package/dist/document-editor-pane-A70-EhdQ.d.ts +0 -124
  94. package/dist/document-editor-pane-TLPVRBBU.js +0 -11
  95. package/dist/expanded-tool-detail-Dh99mcbY.d.ts +0 -63
  96. package/dist/file-tabs-BLfxfmAH.d.ts +0 -51
  97. package/dist/parts-CyGkM6Fp.d.ts +0 -50
  98. package/dist/run-CtFZ6s-D.d.ts +0 -41
  99. package/dist/sidebar-drop-zone-tDBsuOH5.d.ts +0 -301
  100. package/dist/sidecar-CFU2W9j1.d.ts +0 -8
  101. package/dist/template-card-BAtvcAkU.d.ts +0 -18
  102. package/dist/tool-call-feed-Bs3MyQMT.d.ts +0 -68
  103. package/dist/tool-display-Ct9nFAzJ.d.ts +0 -32
  104. package/dist/usage-chart-CPTcNlGs.d.ts +0 -73
  105. package/dist/use-sandbox-metrics-DWc0k9Xm.d.ts +0 -153
  106. package/dist/variant-list-BrHYcBCk.d.ts +0 -540
@@ -0,0 +1,858 @@
1
+ import {
2
+ cn
3
+ } from "./chunk-EI44GEQ5.js";
4
+
5
+ // src/dashboard/model-picker.tsx
6
+ import * as React from "react";
7
+ import { ChevronDown, Search, Sparkles, Zap, Brain, Star, Loader2 } from "lucide-react";
8
+ import * as Popover from "@radix-ui/react-dropdown-menu";
9
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
10
+ function canonicalModelId(model) {
11
+ const id = model.id;
12
+ if (id.includes("/")) return id;
13
+ const provider = model._provider ?? model.provider;
14
+ return provider ? `${provider}/${id}` : id;
15
+ }
16
+ function formatPricing(pricing) {
17
+ const prompt = Number(pricing?.prompt ?? 0);
18
+ const completion = Number(pricing?.completion ?? 0);
19
+ if (!prompt && !completion) return null;
20
+ const fmt = (n) => {
21
+ const perM = n * 1e6;
22
+ if (perM === 0) return "0";
23
+ if (perM >= 1) return `$${perM.toFixed(2)}`;
24
+ return `$${perM.toFixed(2)}`;
25
+ };
26
+ return `${fmt(prompt)} / ${fmt(completion)} per 1M`;
27
+ }
28
+ function formatContext(ctx) {
29
+ if (!ctx) return null;
30
+ if (ctx >= 1e6) return `${(ctx / 1e6).toFixed(1)}M ctx`;
31
+ if (ctx >= 1e3) return `${Math.round(ctx / 1e3)}k ctx`;
32
+ return `${ctx} ctx`;
33
+ }
34
+ var DEFAULT_PRESETS = [
35
+ {
36
+ id: "fast",
37
+ label: "Fast",
38
+ hint: "Cheapest, lowest latency",
39
+ icon: Zap,
40
+ match: (models) => {
41
+ const ids = models.map(canonicalModelId);
42
+ return ids.find((m) => /gpt-5\.\d+-mini$/.test(m)) ?? ids.find((m) => /gpt-5-mini$/.test(m)) ?? ids.find((m) => m.endsWith("/claude-haiku-4.5")) ?? ids.find((m) => /haiku/.test(m));
43
+ }
44
+ },
45
+ {
46
+ id: "balanced",
47
+ label: "Balanced",
48
+ hint: "Best value for most chat",
49
+ icon: Sparkles,
50
+ match: (models) => {
51
+ const ids = models.map(canonicalModelId);
52
+ return ids.find((m) => /^openai\/gpt-5\.\d+$/.test(m)) ?? ids.find((m) => /^openai\/gpt-5$/.test(m)) ?? ids.find((m) => m.endsWith("/claude-sonnet-4-6")) ?? ids.find((m) => /sonnet/.test(m));
53
+ }
54
+ },
55
+ {
56
+ id: "best",
57
+ label: "Best",
58
+ hint: "Hardest reasoning, highest quality",
59
+ icon: Brain,
60
+ match: (models) => {
61
+ const ids = models.map(canonicalModelId);
62
+ return ids.find((m) => /^openai\/gpt-5\.\d+-pro$/.test(m)) ?? ids.find((m) => /^openai\/o3$/.test(m)) ?? ids.find((m) => m.endsWith("/claude-opus-4-7")) ?? ids.find((m) => /opus/.test(m));
63
+ }
64
+ }
65
+ ];
66
+ function ModelPicker({
67
+ value,
68
+ onChange,
69
+ models,
70
+ loading = false,
71
+ recents,
72
+ presets = DEFAULT_PRESETS,
73
+ excludeProviders,
74
+ modalities,
75
+ variant = "field",
76
+ label = "Model",
77
+ placeholder = "Choose a model",
78
+ className,
79
+ triggerClassName,
80
+ disabled
81
+ }) {
82
+ const [query, setQuery] = React.useState("");
83
+ const [open, setOpen] = React.useState(false);
84
+ const filtered = React.useMemo(() => {
85
+ const excluded = new Set((excludeProviders ?? []).map((p) => p.toLowerCase()));
86
+ const allowedModalities = modalities ? new Set(modalities) : null;
87
+ const q = query.trim().toLowerCase();
88
+ return models.filter((m) => {
89
+ const provider = (m._provider ?? m.provider ?? "").toLowerCase();
90
+ if (excluded.has(provider)) return false;
91
+ if (allowedModalities && m.architecture?.modality && !allowedModalities.has(m.architecture.modality)) return false;
92
+ if (!q) return true;
93
+ const id = canonicalModelId(m).toLowerCase();
94
+ const name = (m.name ?? "").toLowerCase();
95
+ return id.includes(q) || name.includes(q) || provider.includes(q);
96
+ });
97
+ }, [models, query, modalities, excludeProviders]);
98
+ const grouped = React.useMemo(() => {
99
+ const groups = /* @__PURE__ */ new Map();
100
+ for (const m of filtered) {
101
+ const provider = m._provider ?? m.provider ?? "other";
102
+ const list = groups.get(provider);
103
+ if (list) list.push(m);
104
+ else groups.set(provider, [m]);
105
+ }
106
+ return Array.from(groups.entries()).sort(([a], [b]) => a.localeCompare(b));
107
+ }, [filtered]);
108
+ const current = React.useMemo(
109
+ () => models.find((m) => canonicalModelId(m) === value),
110
+ [models, value]
111
+ );
112
+ const currentLabel = current?.name ?? current?.id ?? value;
113
+ const recentIds = React.useMemo(() => {
114
+ if (!recents?.length) return [];
115
+ const lookup = new Map(models.map((m) => [canonicalModelId(m), m]));
116
+ return recents.map((id) => lookup.get(id)).filter((m) => Boolean(m)).slice(0, 4);
117
+ }, [recents, models]);
118
+ const handleSelect = (id) => {
119
+ onChange(id);
120
+ setOpen(false);
121
+ setQuery("");
122
+ };
123
+ const trigger = variant === "pill" ? /* @__PURE__ */ jsxs(
124
+ "button",
125
+ {
126
+ type: "button",
127
+ disabled,
128
+ className: cn(
129
+ "inline-flex items-center gap-1.5 rounded-full border border-border bg-card",
130
+ "px-2.5 py-1 text-xs font-medium text-foreground",
131
+ "transition-colors duration-[var(--transition-fast)]",
132
+ "hover:border-primary/30 hover:bg-accent/30",
133
+ "focus:outline-none focus:border-primary/40",
134
+ "data-[state=open]:border-primary/40 data-[state=open]:bg-accent/30",
135
+ "disabled:opacity-50 disabled:cursor-not-allowed",
136
+ className,
137
+ triggerClassName
138
+ ),
139
+ children: [
140
+ /* @__PURE__ */ jsx(Sparkles, { className: "h-3 w-3 text-muted-foreground" }),
141
+ /* @__PURE__ */ jsx("span", { className: "truncate max-w-[160px]", children: currentLabel || placeholder }),
142
+ /* @__PURE__ */ jsx(ChevronDown, { className: "h-3 w-3 text-muted-foreground transition-transform data-[state=open]:rotate-180" })
143
+ ]
144
+ }
145
+ ) : /* @__PURE__ */ jsxs(
146
+ "button",
147
+ {
148
+ type: "button",
149
+ disabled,
150
+ className: cn(
151
+ "flex w-full items-center justify-between gap-2 rounded-[var(--radius-md)]",
152
+ "border border-border bg-card px-3 py-2.5 text-sm text-left",
153
+ "transition-colors duration-[var(--transition-fast)]",
154
+ "hover:border-primary/20 hover:bg-accent/30",
155
+ "focus:outline-none focus:border-primary/30",
156
+ "data-[state=open]:border-primary/30 data-[state=open]:bg-accent/30",
157
+ "disabled:opacity-50 disabled:cursor-not-allowed",
158
+ className,
159
+ triggerClassName
160
+ ),
161
+ children: [
162
+ /* @__PURE__ */ jsx("span", { className: cn("truncate", current ? "text-foreground font-medium" : "text-muted-foreground"), children: currentLabel || placeholder }),
163
+ /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4 shrink-0 text-muted-foreground transition-transform data-[state=open]:rotate-180" })
164
+ ]
165
+ }
166
+ );
167
+ return /* @__PURE__ */ jsxs("div", { className: cn(variant === "field" ? "space-y-1.5" : "inline-flex", variant === "field" ? className : void 0), children: [
168
+ variant === "field" && label && /* @__PURE__ */ jsx("label", { className: "block text-xs font-medium text-muted-foreground uppercase tracking-[0.06em]", children: label }),
169
+ /* @__PURE__ */ jsxs(Popover.Root, { open, onOpenChange: setOpen, children: [
170
+ /* @__PURE__ */ jsx(Popover.Trigger, { asChild: true, children: trigger }),
171
+ /* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsxs(
172
+ Popover.Content,
173
+ {
174
+ sideOffset: 4,
175
+ align: variant === "pill" ? "start" : "start",
176
+ className: cn(
177
+ "z-50 w-[var(--radix-dropdown-menu-trigger-width)] min-w-[320px] max-w-[460px]",
178
+ "max-h-[440px] overflow-hidden flex flex-col",
179
+ "rounded-[var(--radius-md)] border border-border bg-card shadow-[var(--shadow-dropdown)]",
180
+ "data-[state=open]:animate-in data-[state=closed]:animate-out",
181
+ "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
182
+ "data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95"
183
+ ),
184
+ children: [
185
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 border-b border-border px-3 py-2", children: [
186
+ /* @__PURE__ */ jsx(Search, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }),
187
+ /* @__PURE__ */ jsx(
188
+ "input",
189
+ {
190
+ type: "text",
191
+ value: query,
192
+ onChange: (e) => setQuery(e.target.value),
193
+ placeholder: "Search models...",
194
+ autoFocus: true,
195
+ className: "flex-1 bg-transparent text-sm outline-none placeholder:text-muted-foreground"
196
+ }
197
+ ),
198
+ loading && /* @__PURE__ */ jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin text-muted-foreground" })
199
+ ] }),
200
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto", children: [
201
+ !query && presets.length > 0 && /* @__PURE__ */ jsx(Section, { label: "Presets", children: presets.map((preset) => {
202
+ const Icon = preset.icon ?? Star;
203
+ const matchedId = preset.match(models);
204
+ if (!matchedId) return null;
205
+ const matched = models.find((m) => canonicalModelId(m) === matchedId);
206
+ const isCurrent = matchedId === value;
207
+ return /* @__PURE__ */ jsxs(
208
+ PickerItem,
209
+ {
210
+ onSelect: () => handleSelect(matchedId),
211
+ active: isCurrent,
212
+ children: [
213
+ /* @__PURE__ */ jsx(Icon, { className: "h-3.5 w-3.5 shrink-0 text-[var(--accent-text)]" }),
214
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
215
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
216
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: preset.label }),
217
+ /* @__PURE__ */ jsxs("span", { className: "text-[10px] text-muted-foreground truncate", children: [
218
+ "\u2192 ",
219
+ matched?.name ?? matchedId
220
+ ] })
221
+ ] }),
222
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: preset.hint })
223
+ ] })
224
+ ]
225
+ },
226
+ preset.id
227
+ );
228
+ }) }),
229
+ !query && recentIds.length > 0 && /* @__PURE__ */ jsx(Section, { label: "Recent", children: recentIds.map((m) => /* @__PURE__ */ jsx(
230
+ ModelRow,
231
+ {
232
+ model: m,
233
+ active: canonicalModelId(m) === value,
234
+ onSelect: handleSelect
235
+ },
236
+ `recent-${canonicalModelId(m)}`
237
+ )) }),
238
+ grouped.length === 0 ? /* @__PURE__ */ jsx("div", { className: "px-3 py-8 text-center text-xs text-muted-foreground", children: loading ? "Loading models..." : query ? "No models match." : "No models available." }) : grouped.map(([provider, list]) => /* @__PURE__ */ jsx(Section, { label: provider, children: list.map((m) => /* @__PURE__ */ jsx(
239
+ ModelRow,
240
+ {
241
+ model: m,
242
+ active: canonicalModelId(m) === value,
243
+ onSelect: handleSelect
244
+ },
245
+ canonicalModelId(m)
246
+ )) }, provider))
247
+ ] }),
248
+ /* @__PURE__ */ jsxs("div", { className: "border-t border-border px-3 py-1.5 text-[10px] text-muted-foreground", children: [
249
+ filtered.length,
250
+ " of ",
251
+ models.length,
252
+ " model",
253
+ models.length === 1 ? "" : "s"
254
+ ] })
255
+ ]
256
+ }
257
+ ) })
258
+ ] })
259
+ ] });
260
+ }
261
+ function Section({ label, children }) {
262
+ return /* @__PURE__ */ jsxs("div", { className: "py-1", children: [
263
+ /* @__PURE__ */ jsx("div", { className: "px-3 pt-1.5 pb-0.5 text-[10px] font-mono uppercase tracking-widest text-muted-foreground", children: label }),
264
+ /* @__PURE__ */ jsx("div", { children })
265
+ ] });
266
+ }
267
+ function PickerItem({
268
+ onSelect,
269
+ active,
270
+ children
271
+ }) {
272
+ return /* @__PURE__ */ jsx(
273
+ Popover.Item,
274
+ {
275
+ onSelect: (e) => {
276
+ e.preventDefault();
277
+ onSelect();
278
+ },
279
+ className: cn(
280
+ "flex cursor-pointer items-start gap-2 px-3 py-2 outline-none",
281
+ "transition-colors duration-[var(--transition-fast)]",
282
+ "hover:bg-accent/40 focus:bg-accent/40",
283
+ active && "bg-[var(--accent-surface-soft)] text-[var(--accent-text)]"
284
+ ),
285
+ children
286
+ }
287
+ );
288
+ }
289
+ function ModelRow({
290
+ model,
291
+ active,
292
+ onSelect
293
+ }) {
294
+ const id = canonicalModelId(model);
295
+ const pricing = formatPricing(model.pricing);
296
+ const ctx = formatContext(model.context_length);
297
+ return /* @__PURE__ */ jsx(PickerItem, { onSelect: () => onSelect(id), active, children: /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
298
+ /* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between gap-2", children: [
299
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium truncate", children: model.name ?? model.id }),
300
+ ctx && /* @__PURE__ */ jsx("span", { className: "shrink-0 text-[10px] text-muted-foreground", children: ctx })
301
+ ] }),
302
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-[11px] text-muted-foreground", children: [
303
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: id }),
304
+ pricing && /* @__PURE__ */ jsxs(Fragment, { children: [
305
+ /* @__PURE__ */ jsx("span", { className: "shrink-0", children: "\xB7" }),
306
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 font-mono", children: pricing })
307
+ ] })
308
+ ] })
309
+ ] }) });
310
+ }
311
+
312
+ // src/dashboard/billing-dashboard.tsx
313
+ import { Button } from "@tangle-network/ui/primitives";
314
+ import { Badge } from "@tangle-network/ui/primitives";
315
+ import {
316
+ Card,
317
+ CardContent,
318
+ CardDescription,
319
+ CardHeader,
320
+ CardTitle
321
+ } from "@tangle-network/ui/primitives";
322
+ import { Progress } from "@tangle-network/ui/primitives";
323
+ import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
324
+ var variantColors = {
325
+ sandbox: {
326
+ accent: "text-[var(--accent-text)]",
327
+ accentBg: "bg-[var(--accent-surface-soft)]",
328
+ border: "border-[var(--border-accent)]",
329
+ progress: "bg-[image:var(--accent-gradient-strong)]"
330
+ }
331
+ };
332
+ function formatCredits(value) {
333
+ if (value >= 1e6) {
334
+ return `${(value / 1e6).toFixed(1)}M`;
335
+ }
336
+ if (value >= 1e3) {
337
+ return `${(value / 1e3).toFixed(1)}K`;
338
+ }
339
+ return value.toLocaleString();
340
+ }
341
+ function formatDate(dateStr) {
342
+ const date = new Date(dateStr);
343
+ return date.toLocaleDateString("en-US", {
344
+ month: "long",
345
+ day: "numeric",
346
+ year: "numeric"
347
+ });
348
+ }
349
+ function getStatusBadgeVariant(status) {
350
+ switch (status.toLowerCase()) {
351
+ case "active":
352
+ return "success";
353
+ case "trialing":
354
+ case "past_due":
355
+ return "warning";
356
+ case "canceled":
357
+ case "unpaid":
358
+ return "error";
359
+ default:
360
+ return "secondary";
361
+ }
362
+ }
363
+ function CreditCardIcon({ className }) {
364
+ return /* @__PURE__ */ jsxs2(
365
+ "svg",
366
+ {
367
+ xmlns: "http://www.w3.org/2000/svg",
368
+ viewBox: "0 0 24 24",
369
+ fill: "none",
370
+ stroke: "currentColor",
371
+ strokeWidth: "2",
372
+ strokeLinecap: "round",
373
+ strokeLinejoin: "round",
374
+ className: cn("h-5 w-5", className),
375
+ children: [
376
+ /* @__PURE__ */ jsx2("title", { children: "Credit card icon" }),
377
+ /* @__PURE__ */ jsx2("rect", { width: "20", height: "14", x: "2", y: "5", rx: "2" }),
378
+ /* @__PURE__ */ jsx2("line", { x1: "2", x2: "22", y1: "10", y2: "10" })
379
+ ]
380
+ }
381
+ );
382
+ }
383
+ function CoinsIcon({ className }) {
384
+ return /* @__PURE__ */ jsxs2(
385
+ "svg",
386
+ {
387
+ xmlns: "http://www.w3.org/2000/svg",
388
+ viewBox: "0 0 24 24",
389
+ fill: "none",
390
+ stroke: "currentColor",
391
+ strokeWidth: "2",
392
+ strokeLinecap: "round",
393
+ strokeLinejoin: "round",
394
+ className: cn("h-5 w-5", className),
395
+ children: [
396
+ /* @__PURE__ */ jsx2("title", { children: "Coins icon" }),
397
+ /* @__PURE__ */ jsx2("circle", { cx: "8", cy: "8", r: "6" }),
398
+ /* @__PURE__ */ jsx2("path", { d: "M18.09 10.37A6 6 0 1 1 10.34 18" }),
399
+ /* @__PURE__ */ jsx2("path", { d: "M7 6h1v4" }),
400
+ /* @__PURE__ */ jsx2("path", { d: "m16.71 13.88.7.71-2.82 2.82" })
401
+ ]
402
+ }
403
+ );
404
+ }
405
+ function ChartIcon({ className }) {
406
+ return /* @__PURE__ */ jsxs2(
407
+ "svg",
408
+ {
409
+ xmlns: "http://www.w3.org/2000/svg",
410
+ viewBox: "0 0 24 24",
411
+ fill: "none",
412
+ stroke: "currentColor",
413
+ strokeWidth: "2",
414
+ strokeLinecap: "round",
415
+ strokeLinejoin: "round",
416
+ className: cn("h-5 w-5", className),
417
+ children: [
418
+ /* @__PURE__ */ jsx2("title", { children: "Chart icon" }),
419
+ /* @__PURE__ */ jsx2("path", { d: "M3 3v18h18" }),
420
+ /* @__PURE__ */ jsx2("path", { d: "m19 9-5 5-4-4-3 3" })
421
+ ]
422
+ }
423
+ );
424
+ }
425
+ function BillingDashboard({
426
+ subscription,
427
+ balance,
428
+ usage,
429
+ onManageSubscription,
430
+ onAddCredits,
431
+ variant = "sandbox",
432
+ className,
433
+ cardClassName
434
+ }) {
435
+ const colors2 = variantColors[variant];
436
+ const totalCredits = balance.available + balance.used;
437
+ const usagePercent = totalCredits > 0 ? balance.used / totalCredits * 100 : 0;
438
+ const sortedModels = Object.entries(usage.byModel).sort(
439
+ ([, a], [, b]) => b - a
440
+ );
441
+ return /* @__PURE__ */ jsxs2("div", { className: cn("grid gap-6 lg:grid-cols-3", className), children: [
442
+ /* @__PURE__ */ jsxs2(Card, { variant, className: cn("lg:col-span-1", cardClassName), children: [
443
+ /* @__PURE__ */ jsx2(CardHeader, { children: /* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between", children: [
444
+ /* @__PURE__ */ jsxs2(CardTitle, { className: "flex items-center gap-2 text-base", children: [
445
+ /* @__PURE__ */ jsx2(CreditCardIcon, { className: colors2.accent }),
446
+ "Current Plan"
447
+ ] }),
448
+ subscription && /* @__PURE__ */ jsx2(Badge, { variant: getStatusBadgeVariant(subscription.status), children: subscription.status })
449
+ ] }) }),
450
+ /* @__PURE__ */ jsx2(CardContent, { className: "space-y-4", children: subscription ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
451
+ /* @__PURE__ */ jsxs2("div", { children: [
452
+ /* @__PURE__ */ jsx2("p", { className: cn("font-bold text-2xl", colors2.accent), children: subscription.tierName }),
453
+ /* @__PURE__ */ jsxs2("p", { className: "text-muted-foreground text-sm", children: [
454
+ "Renews ",
455
+ formatDate(subscription.renewsAt)
456
+ ] })
457
+ ] }),
458
+ /* @__PURE__ */ jsx2(
459
+ Button,
460
+ {
461
+ variant: "outline",
462
+ className: "w-full",
463
+ onClick: onManageSubscription,
464
+ children: "Manage Subscription"
465
+ }
466
+ )
467
+ ] }) : /* @__PURE__ */ jsxs2(Fragment2, { children: [
468
+ /* @__PURE__ */ jsxs2("div", { children: [
469
+ /* @__PURE__ */ jsx2("p", { className: "font-bold text-2xl text-muted-foreground", children: "No Active Plan" }),
470
+ /* @__PURE__ */ jsx2("p", { className: "text-muted-foreground text-sm", children: "Subscribe to get started" })
471
+ ] }),
472
+ /* @__PURE__ */ jsx2(
473
+ Button,
474
+ {
475
+ variant,
476
+ className: "w-full",
477
+ onClick: onManageSubscription,
478
+ children: "View Plans"
479
+ }
480
+ )
481
+ ] }) })
482
+ ] }),
483
+ /* @__PURE__ */ jsxs2(Card, { variant, className: cn("lg:col-span-1", cardClassName), children: [
484
+ /* @__PURE__ */ jsxs2(CardHeader, { children: [
485
+ /* @__PURE__ */ jsxs2(CardTitle, { className: "flex items-center gap-2 text-base", children: [
486
+ /* @__PURE__ */ jsx2(CoinsIcon, { className: colors2.accent }),
487
+ "Credit Balance"
488
+ ] }),
489
+ /* @__PURE__ */ jsxs2(CardDescription, { children: [
490
+ formatCredits(balance.available),
491
+ " credits remaining"
492
+ ] })
493
+ ] }),
494
+ /* @__PURE__ */ jsxs2(CardContent, { className: "space-y-4", children: [
495
+ /* @__PURE__ */ jsxs2("div", { className: "space-y-2", children: [
496
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between text-sm", children: [
497
+ /* @__PURE__ */ jsx2("span", { className: "text-muted-foreground", children: "Used" }),
498
+ /* @__PURE__ */ jsx2("span", { className: "font-medium", children: formatCredits(balance.used) })
499
+ ] }),
500
+ /* @__PURE__ */ jsx2(Progress, { value: usagePercent, variant, className: "h-3" }),
501
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between text-sm", children: [
502
+ /* @__PURE__ */ jsx2("span", { className: "text-muted-foreground", children: "Available" }),
503
+ /* @__PURE__ */ jsx2("span", { className: cn("font-medium", colors2.accent), children: formatCredits(balance.available) })
504
+ ] })
505
+ ] }),
506
+ /* @__PURE__ */ jsx2(Button, { variant, className: "w-full", onClick: onAddCredits, children: "Add Credits" })
507
+ ] })
508
+ ] }),
509
+ /* @__PURE__ */ jsxs2(Card, { variant, className: cn("lg:col-span-1", cardClassName), children: [
510
+ /* @__PURE__ */ jsxs2(CardHeader, { children: [
511
+ /* @__PURE__ */ jsxs2(CardTitle, { className: "flex items-center gap-2 text-base", children: [
512
+ /* @__PURE__ */ jsx2(ChartIcon, { className: colors2.accent }),
513
+ "Usage Breakdown"
514
+ ] }),
515
+ /* @__PURE__ */ jsx2(CardDescription, { children: usage.period })
516
+ ] }),
517
+ /* @__PURE__ */ jsxs2(CardContent, { className: "space-y-4", children: [
518
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-baseline justify-between", children: [
519
+ /* @__PURE__ */ jsx2("span", { className: "text-muted-foreground text-sm", children: "Total Usage" }),
520
+ /* @__PURE__ */ jsx2("span", { className: cn("font-bold text-2xl", colors2.accent), children: formatCredits(usage.total) })
521
+ ] }),
522
+ /* @__PURE__ */ jsxs2("div", { className: "space-y-3", children: [
523
+ /* @__PURE__ */ jsx2("p", { className: "font-medium text-muted-foreground text-xs uppercase tracking-wider", children: "By Model" }),
524
+ /* @__PURE__ */ jsxs2("div", { className: "space-y-2", children: [
525
+ sortedModels.slice(0, 5).map(([model, credits]) => {
526
+ const modelPercent = usage.total > 0 ? credits / usage.total * 100 : 0;
527
+ return /* @__PURE__ */ jsxs2("div", { className: "space-y-1", children: [
528
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between text-sm", children: [
529
+ /* @__PURE__ */ jsx2("span", { className: "truncate text-muted-foreground", children: model }),
530
+ /* @__PURE__ */ jsx2("span", { className: "ml-2 font-medium", children: formatCredits(credits) })
531
+ ] }),
532
+ /* @__PURE__ */ jsx2("div", { className: "h-1.5 w-full overflow-hidden rounded-full bg-muted", children: /* @__PURE__ */ jsx2(
533
+ "div",
534
+ {
535
+ className: cn(
536
+ "h-full rounded-full transition-all",
537
+ variant === "sandbox" && colors2.progress
538
+ ),
539
+ style: { width: `${modelPercent}%` }
540
+ }
541
+ ) })
542
+ ] }, model);
543
+ }),
544
+ sortedModels.length > 5 && /* @__PURE__ */ jsxs2("p", { className: "text-muted-foreground text-xs", children: [
545
+ "+",
546
+ sortedModels.length - 5,
547
+ " more models"
548
+ ] }),
549
+ sortedModels.length === 0 && /* @__PURE__ */ jsx2("p", { className: "text-muted-foreground text-sm", children: "No usage this period" })
550
+ ] })
551
+ ] })
552
+ ] })
553
+ ] })
554
+ ] });
555
+ }
556
+
557
+ // src/dashboard/pricing-page.tsx
558
+ import { Check, Zap as Zap2 } from "lucide-react";
559
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
560
+ function formatPrice(cents) {
561
+ if (!Number.isFinite(cents) || cents < 0) return "$0";
562
+ return cents % 100 === 0 ? `$${cents / 100}` : `$${(cents / 100).toFixed(2)}`;
563
+ }
564
+ function PricingPage({
565
+ tiers,
566
+ currentTierId,
567
+ billingPeriod,
568
+ onBillingPeriodChange,
569
+ onSelectTier,
570
+ loading = false,
571
+ className
572
+ }) {
573
+ const tiersWithRecommended = tiers.map((tier, i) => ({
574
+ ...tier,
575
+ recommended: tier.recommended ?? (tiers.length === 3 && i === 1)
576
+ }));
577
+ return /* @__PURE__ */ jsxs3("div", { className: cn("w-full space-y-10", className), children: [
578
+ /* @__PURE__ */ jsx3("div", { className: "flex justify-center", children: /* @__PURE__ */ jsxs3("div", { className: "inline-flex items-center rounded-full border border-border bg-muted/50 p-1", children: [
579
+ /* @__PURE__ */ jsx3(
580
+ "button",
581
+ {
582
+ type: "button",
583
+ onClick: () => onBillingPeriodChange("monthly"),
584
+ className: cn(
585
+ "rounded-full px-5 py-2 text-sm font-medium transition-all",
586
+ billingPeriod === "monthly" ? "bg-card text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"
587
+ ),
588
+ children: "Monthly"
589
+ }
590
+ ),
591
+ /* @__PURE__ */ jsxs3(
592
+ "button",
593
+ {
594
+ type: "button",
595
+ onClick: () => onBillingPeriodChange("yearly"),
596
+ className: cn(
597
+ "rounded-full px-5 py-2 text-sm font-medium transition-all",
598
+ billingPeriod === "yearly" ? "bg-card text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"
599
+ ),
600
+ children: [
601
+ "Yearly",
602
+ /* @__PURE__ */ jsx3("span", { className: "ml-2 rounded-full bg-[var(--surface-success-bg)] px-2 py-0.5 text-[10px] font-bold text-[var(--surface-success-text)]", children: "Save 20%" })
603
+ ]
604
+ }
605
+ )
606
+ ] }) }),
607
+ /* @__PURE__ */ jsx3("div", { className: "grid grid-cols-1 gap-6 lg:grid-cols-3 items-start", children: tiersWithRecommended.map((tier) => {
608
+ const isCurrentTier = tier.id === currentTierId;
609
+ const isRecommended = tier.recommended;
610
+ const isFree = tier.monthlyPriceCents === 0;
611
+ const price = billingPeriod === "yearly" && tier.yearlyPriceCents !== void 0 ? tier.yearlyPriceCents : tier.monthlyPriceCents;
612
+ const displayPrice = billingPeriod === "yearly" ? Math.round(price / 12) : price;
613
+ return /* @__PURE__ */ jsxs3(
614
+ "div",
615
+ {
616
+ className: cn(
617
+ "relative flex flex-col rounded-2xl border transition-all",
618
+ isRecommended ? "border-primary shadow-[var(--shadow-glow)] scale-[1.02] z-10" : "border-border hover:border-primary/30"
619
+ ),
620
+ children: [
621
+ isRecommended && /* @__PURE__ */ jsx3("div", { className: "absolute -top-4 left-1/2 -translate-x-1/2", children: /* @__PURE__ */ jsxs3("span", { className: "inline-flex items-center gap-1.5 rounded-full bg-primary px-4 py-1.5 text-xs font-bold text-primary-foreground shadow-sm", children: [
622
+ /* @__PURE__ */ jsx3(Zap2, { className: "h-3 w-3" }),
623
+ "Most Popular"
624
+ ] }) }),
625
+ isCurrentTier && !isRecommended && /* @__PURE__ */ jsx3("div", { className: "absolute -top-4 left-1/2 -translate-x-1/2", children: /* @__PURE__ */ jsx3("span", { className: "inline-flex items-center rounded-full bg-muted border border-border px-4 py-1.5 text-xs font-bold text-foreground", children: "Current Plan" }) }),
626
+ /* @__PURE__ */ jsxs3("div", { className: cn("flex flex-col p-8", isRecommended && "pt-10"), children: [
627
+ /* @__PURE__ */ jsxs3("div", { className: "mb-6", children: [
628
+ /* @__PURE__ */ jsx3("h3", { className: "text-lg font-bold text-foreground", children: tier.name }),
629
+ /* @__PURE__ */ jsx3("p", { className: "mt-1 text-sm text-muted-foreground", children: tier.description })
630
+ ] }),
631
+ /* @__PURE__ */ jsxs3("div", { className: "mb-8", children: [
632
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-baseline gap-1", children: [
633
+ /* @__PURE__ */ jsx3("span", { className: cn("text-5xl font-extrabold tracking-tight", isRecommended ? "text-primary" : "text-foreground"), children: isFree ? "Free" : formatPrice(displayPrice) }),
634
+ !isFree && /* @__PURE__ */ jsx3("span", { className: "text-muted-foreground text-sm font-medium", children: "/mo" })
635
+ ] }),
636
+ billingPeriod === "yearly" && tier.yearlyPriceCents !== void 0 && !isFree && /* @__PURE__ */ jsxs3("p", { className: "mt-1 text-xs text-muted-foreground", children: [
637
+ formatPrice(tier.yearlyPriceCents),
638
+ " billed annually"
639
+ ] })
640
+ ] }),
641
+ /* @__PURE__ */ jsx3(
642
+ "button",
643
+ {
644
+ type: "button",
645
+ onClick: () => onSelectTier(tier.id),
646
+ disabled: isCurrentTier || loading,
647
+ className: cn(
648
+ "w-full rounded-xl py-3 text-sm font-bold transition-all active:scale-[0.98] disabled:opacity-50",
649
+ isRecommended ? "bg-primary text-primary-foreground hover:bg-primary/90 shadow-sm" : isCurrentTier ? "bg-muted text-muted-foreground border border-border cursor-default" : "bg-card text-foreground border border-border hover:border-primary/50 hover:bg-muted"
650
+ ),
651
+ children: isCurrentTier ? "Current Plan" : isFree ? "Get Started" : "Subscribe"
652
+ }
653
+ ),
654
+ /* @__PURE__ */ jsx3("div", { className: "my-8 border-t border-border" }),
655
+ /* @__PURE__ */ jsx3("ul", { className: "space-y-3.5 flex-1", children: tier.features.map((feature) => /* @__PURE__ */ jsxs3("li", { className: "flex items-start gap-3", children: [
656
+ /* @__PURE__ */ jsx3("div", { className: cn(
657
+ "mt-0.5 flex h-5 w-5 shrink-0 items-center justify-center rounded-full",
658
+ isRecommended ? "bg-primary/10 text-primary" : "bg-muted text-muted-foreground"
659
+ ), children: /* @__PURE__ */ jsx3(Check, { className: "h-3 w-3" }) }),
660
+ /* @__PURE__ */ jsx3("span", { className: "text-sm text-foreground/80", children: feature })
661
+ ] }, feature)) })
662
+ ] })
663
+ ]
664
+ },
665
+ tier.id
666
+ );
667
+ }) })
668
+ ] });
669
+ }
670
+
671
+ // src/dashboard/usage-chart.tsx
672
+ import * as React2 from "react";
673
+ import { Card as Card2, CardContent as CardContent2, CardHeader as CardHeader2, CardTitle as CardTitle2 } from "@tangle-network/ui/primitives";
674
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
675
+ var colors = {
676
+ bar: "bg-[image:var(--accent-gradient-strong)]",
677
+ barHover: "hover:brightness-110",
678
+ text: "text-[var(--accent-text)]"
679
+ };
680
+ function formatDate2(dateStr) {
681
+ const date = new Date(dateStr);
682
+ return date.toLocaleDateString("en-US", { month: "short", day: "numeric" });
683
+ }
684
+ function formatValue(value) {
685
+ if (value >= 1e6) {
686
+ return `${(value / 1e6).toFixed(1)}M`;
687
+ }
688
+ if (value >= 1e3) {
689
+ return `${(value / 1e3).toFixed(1)}K`;
690
+ }
691
+ return value.toLocaleString();
692
+ }
693
+ function UsageChart({ data, title, unit, className }) {
694
+ const maxValue = Math.max(...data.map((d) => d.value), 1);
695
+ const total = data.reduce((sum, d) => sum + d.value, 0);
696
+ const [hoveredIndex, setHoveredIndex] = React2.useState(null);
697
+ return /* @__PURE__ */ jsxs4(Card2, { className, children: [
698
+ /* @__PURE__ */ jsxs4(CardHeader2, { className: "flex flex-row items-center justify-between pb-2", children: [
699
+ /* @__PURE__ */ jsx4(CardTitle2, { className: "font-medium text-base", children: title }),
700
+ /* @__PURE__ */ jsxs4("div", { className: "text-right", children: [
701
+ /* @__PURE__ */ jsx4("span", { className: cn("font-bold text-2xl", colors.text), children: formatValue(total) }),
702
+ /* @__PURE__ */ jsx4("span", { className: "ml-1 text-muted-foreground text-sm", children: unit })
703
+ ] })
704
+ ] }),
705
+ /* @__PURE__ */ jsxs4(CardContent2, { children: [
706
+ /* @__PURE__ */ jsxs4("div", { className: "relative", children: [
707
+ /* @__PURE__ */ jsxs4("div", { className: "absolute top-0 left-0 flex h-48 flex-col justify-between text-muted-foreground text-xs", children: [
708
+ /* @__PURE__ */ jsx4("span", { children: formatValue(maxValue) }),
709
+ /* @__PURE__ */ jsx4("span", { children: formatValue(Math.round(maxValue / 2)) }),
710
+ /* @__PURE__ */ jsx4("span", { children: "0" })
711
+ ] }),
712
+ /* @__PURE__ */ jsx4("div", { className: "ml-10 flex h-48 items-end gap-1", children: data.map((point, index) => {
713
+ const heightPercent = point.value / maxValue * 100;
714
+ const isHovered = hoveredIndex === index;
715
+ return (
716
+ // biome-ignore lint/a11y/noStaticElementInteractions: chart bar uses hover for tooltip display
717
+ /* @__PURE__ */ jsxs4(
718
+ "div",
719
+ {
720
+ className: "group relative flex flex-1 flex-col items-center",
721
+ onMouseEnter: () => setHoveredIndex(index),
722
+ onMouseLeave: () => setHoveredIndex(null),
723
+ children: [
724
+ isHovered && /* @__PURE__ */ jsxs4("div", { className: "absolute -top-12 z-10 rounded-lg bg-popover px-3 py-1.5 text-sm shadow-lg", children: [
725
+ /* @__PURE__ */ jsxs4("p", { className: "font-medium", children: [
726
+ formatValue(point.value),
727
+ " ",
728
+ unit
729
+ ] }),
730
+ /* @__PURE__ */ jsx4("p", { className: "text-muted-foreground text-xs", children: formatDate2(point.date) })
731
+ ] }),
732
+ /* @__PURE__ */ jsx4(
733
+ "div",
734
+ {
735
+ className: cn(
736
+ "min-h-[2px] w-full rounded-t-sm transition-all duration-200",
737
+ colors.bar,
738
+ colors.barHover,
739
+ isHovered && "opacity-80"
740
+ ),
741
+ style: { height: `${Math.max(heightPercent, 1)}%` }
742
+ }
743
+ )
744
+ ]
745
+ },
746
+ point.date
747
+ )
748
+ );
749
+ }) }),
750
+ /* @__PURE__ */ jsx4("div", { className: "mt-2 ml-10 flex gap-1", children: data.map((point, index) => /* @__PURE__ */ jsx4(
751
+ "div",
752
+ {
753
+ className: cn(
754
+ "flex-1 truncate text-center text-muted-foreground text-xs",
755
+ hoveredIndex === index && colors.text
756
+ ),
757
+ children: data.length <= 7 || index % Math.ceil(data.length / 7) === 0 ? formatDate2(point.date) : ""
758
+ },
759
+ point.date
760
+ )) })
761
+ ] }),
762
+ /* @__PURE__ */ jsxs4("div", { className: "mt-4 grid grid-cols-3 gap-4 border-border border-t pt-4", children: [
763
+ /* @__PURE__ */ jsxs4("div", { className: "text-center", children: [
764
+ /* @__PURE__ */ jsx4("p", { className: "text-muted-foreground text-xs", children: "Average" }),
765
+ /* @__PURE__ */ jsxs4("p", { className: "font-medium", children: [
766
+ formatValue(Math.round(total / data.length)),
767
+ " ",
768
+ unit
769
+ ] })
770
+ ] }),
771
+ /* @__PURE__ */ jsxs4("div", { className: "text-center", children: [
772
+ /* @__PURE__ */ jsx4("p", { className: "text-muted-foreground text-xs", children: "Peak" }),
773
+ /* @__PURE__ */ jsxs4("p", { className: "font-medium", children: [
774
+ formatValue(maxValue),
775
+ " ",
776
+ unit
777
+ ] })
778
+ ] }),
779
+ /* @__PURE__ */ jsxs4("div", { className: "text-center", children: [
780
+ /* @__PURE__ */ jsx4("p", { className: "text-muted-foreground text-xs", children: "Total" }),
781
+ /* @__PURE__ */ jsxs4("p", { className: cn("font-medium", colors.text), children: [
782
+ formatValue(total),
783
+ " ",
784
+ unit
785
+ ] })
786
+ ] })
787
+ ] })
788
+ ] })
789
+ ] });
790
+ }
791
+
792
+ // src/dashboard/template-card.tsx
793
+ import { ArrowRight } from "lucide-react";
794
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
795
+ function TemplateCard({ template, onUseTemplate, className }) {
796
+ return /* @__PURE__ */ jsxs5(
797
+ "div",
798
+ {
799
+ className: cn(
800
+ "group relative flex flex-col justify-between rounded-2xl border border-border bg-card p-6 transition-all hover:border-primary/30 hover:shadow-md",
801
+ className
802
+ ),
803
+ children: [
804
+ /* @__PURE__ */ jsxs5("div", { children: [
805
+ /* @__PURE__ */ jsx5("div", { className: "mb-4 flex h-12 w-12 items-center justify-center rounded-xl bg-muted border border-border", children: template.icon ?? /* @__PURE__ */ jsx5("span", { className: "text-lg font-bold text-primary", children: template.name.charAt(0).toUpperCase() }) }),
806
+ /* @__PURE__ */ jsx5("h3", { className: "mb-1 text-base font-bold text-foreground", children: template.name }),
807
+ /* @__PURE__ */ jsx5("p", { className: "text-sm text-muted-foreground leading-relaxed line-clamp-2", children: template.description }),
808
+ template.tags && template.tags.length > 0 && /* @__PURE__ */ jsx5("div", { className: "mt-3 flex flex-wrap gap-1.5", children: template.tags.map((tag, i) => /* @__PURE__ */ jsx5(
809
+ "span",
810
+ {
811
+ className: "rounded-full bg-muted px-2 py-0.5 text-[10px] font-medium text-muted-foreground",
812
+ children: tag
813
+ },
814
+ `${tag}-${i}`
815
+ )) })
816
+ ] }),
817
+ /* @__PURE__ */ jsxs5(
818
+ "button",
819
+ {
820
+ type: "button",
821
+ onClick: () => onUseTemplate(template.id),
822
+ className: "mt-5 inline-flex w-full items-center justify-center gap-2 rounded-xl bg-primary/10 border border-primary/20 px-4 py-2.5 text-sm font-bold text-primary transition-colors hover:bg-primary hover:text-primary-foreground active:scale-[0.98]",
823
+ children: [
824
+ "Use Template",
825
+ /* @__PURE__ */ jsx5(ArrowRight, { className: "h-4 w-4 transition-transform group-hover:translate-x-0.5" })
826
+ ]
827
+ }
828
+ )
829
+ ]
830
+ }
831
+ );
832
+ }
833
+
834
+ // src/dashboard/info-panel.tsx
835
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
836
+ function InfoPanel({ label, title, description, className }) {
837
+ return /* @__PURE__ */ jsxs6("div", { className: cn("rounded-lg bg-[var(--brand-strong)] p-5 text-[var(--brand-strong-text)] relative overflow-hidden", className), children: [
838
+ /* @__PURE__ */ jsxs6("div", { className: "relative z-10", children: [
839
+ /* @__PURE__ */ jsx6("p", { className: "text-[10px] font-bold uppercase tracking-widest text-[var(--brand-strong-text-dim)]", children: label }),
840
+ /* @__PURE__ */ jsx6("h3", { className: "mt-1 text-lg font-bold", children: title }),
841
+ /* @__PURE__ */ jsx6("p", { className: "mt-1 text-sm text-[var(--brand-strong-text-muted)]", children: description })
842
+ ] }),
843
+ /* @__PURE__ */ jsx6("div", { className: "absolute right-0 top-0 h-full w-1/3 bg-white/5 -skew-x-12 translate-x-12 pointer-events-none" })
844
+ ] });
845
+ }
846
+
847
+ export {
848
+ canonicalModelId,
849
+ formatPricing,
850
+ formatContext,
851
+ ModelPicker,
852
+ BillingDashboard,
853
+ formatPrice,
854
+ PricingPage,
855
+ UsageChart,
856
+ TemplateCard,
857
+ InfoPanel
858
+ };