@tangle-network/sandbox-ui 0.14.0 → 0.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) 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-AHBZCBDO.js +2960 -0
  10. package/dist/chunk-AZ3AWMTM.js +8 -0
  11. package/dist/chunk-CMY7W45U.js +380 -0
  12. package/dist/{chunk-QMU2PWOU.js → chunk-DNZ4DTNA.js} +71 -17
  13. package/dist/chunk-EI44GEQ5.js +6 -0
  14. package/dist/{chunk-5OQ27N57.js → chunk-GPT7VKK6.js} +34 -38
  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 +538 -4
  22. package/dist/dashboard.js +15 -886
  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 +247 -252
  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 +1 -2
  37. package/dist/pages.js +68 -66
  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/{usage-chart-CPTcNlGs.d.ts → template-card-UhV3pmRC.d.ts} +16 -1
  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-RQHJBTEU.js +0 -10
  85. package/dist/chunk-T7HMZEVO.js +0 -216
  86. package/dist/chunk-U6QTHMY6.js +0 -1290
  87. package/dist/chunk-US6JKJKH.js +0 -124
  88. package/dist/chunk-VX3XOUEB.js +0 -63
  89. package/dist/chunk-XLG757B6.js +0 -933
  90. package/dist/chunk-ZMNSRDMH.js +0 -127
  91. package/dist/chunk-ZNCEM5CD.js +0 -316
  92. package/dist/document-editor-pane-A70-EhdQ.d.ts +0 -124
  93. package/dist/document-editor-pane-TLPVRBBU.js +0 -11
  94. package/dist/expanded-tool-detail-Dh99mcbY.d.ts +0 -63
  95. package/dist/file-tabs-BLfxfmAH.d.ts +0 -51
  96. package/dist/parts-CyGkM6Fp.d.ts +0 -50
  97. package/dist/run-CtFZ6s-D.d.ts +0 -41
  98. package/dist/sidebar-drop-zone-tDBsuOH5.d.ts +0 -301
  99. package/dist/sidecar-CFU2W9j1.d.ts +0 -8
  100. package/dist/template-card-BAtvcAkU.d.ts +0 -18
  101. package/dist/tool-call-feed-Bs3MyQMT.d.ts +0 -68
  102. package/dist/tool-display-Ct9nFAzJ.d.ts +0 -32
  103. package/dist/use-sandbox-metrics-DWc0k9Xm.d.ts +0 -153
  104. package/dist/variant-list-BrHYcBCk.d.ts +0 -540
@@ -0,0 +1,8 @@
1
+ // src/openui/index.ts
2
+ import {
3
+ OpenUIArtifactRenderer
4
+ } from "@tangle-network/ui/openui";
5
+
6
+ export {
7
+ OpenUIArtifactRenderer
8
+ };
@@ -0,0 +1,380 @@
1
+ // src/hooks/use-session-stream.ts
2
+ import { useCallback, useEffect, useRef, useState } from "react";
3
+ function mapApiMessage(msg, counterRef) {
4
+ const created = msg.info.timestamp ? new Date(msg.info.timestamp).getTime() : Date.now();
5
+ const message = {
6
+ id: msg.info.id,
7
+ role: msg.info.role,
8
+ time: { created },
9
+ _insertionIndex: counterRef.current++
10
+ };
11
+ const parts = (msg.parts ?? []).map((p, i) => {
12
+ if (p.type === "tool" && p.tool) {
13
+ return {
14
+ type: "tool",
15
+ id: p.id ?? `${msg.info.id}-tool-${i}`,
16
+ tool: p.tool,
17
+ state: {
18
+ status: p.state?.status ?? "completed",
19
+ input: p.state?.input,
20
+ output: p.state?.output,
21
+ error: p.state?.error,
22
+ metadata: p.state?.metadata,
23
+ time: p.time
24
+ }
25
+ };
26
+ }
27
+ if (p.type === "reasoning") {
28
+ return {
29
+ type: "reasoning",
30
+ text: p.text ?? "",
31
+ time: p.time
32
+ };
33
+ }
34
+ return { type: "text", text: p.text ?? "" };
35
+ });
36
+ return { message, parts };
37
+ }
38
+ async function fetchJson(url, token, init) {
39
+ const headers = { Authorization: `Bearer ${token}` };
40
+ if (init?.body) headers["Content-Type"] = "application/json";
41
+ const res = await fetch(url, { ...init, headers: { ...headers, ...init?.headers }, credentials: "include" });
42
+ if (!res.ok) throw new Error(`HTTP ${res.status}: ${res.statusText}`);
43
+ return res.json();
44
+ }
45
+ function useSessionStream({
46
+ apiUrl,
47
+ token,
48
+ sessionId,
49
+ enabled = true
50
+ }) {
51
+ const [messages, setMessages] = useState([]);
52
+ const [partMap, setPartMap] = useState({});
53
+ const [isStreaming, setIsStreaming] = useState(false);
54
+ const [error, setError] = useState(null);
55
+ const [connected, setConnected] = useState(false);
56
+ const abortRef = useRef(null);
57
+ const reconnectTimerRef = useRef(null);
58
+ const streamingMsgIdRef = useRef(null);
59
+ const insertionCounterRef = useRef(0);
60
+ const handleSSEEventRef = useRef(null);
61
+ const refetch = useCallback(async () => {
62
+ if (!token || !sessionId || !apiUrl) return;
63
+ try {
64
+ const url = `${apiUrl}/session/sessions/${encodeURIComponent(sessionId)}/messages?limit=200`;
65
+ const data = await fetchJson(url, token);
66
+ const apiMessages = Array.isArray(data) ? data : data.messages ?? [];
67
+ const newMessages = [];
68
+ const newPartMap = {};
69
+ for (const apiMsg of apiMessages) {
70
+ const { message, parts } = mapApiMessage(apiMsg, insertionCounterRef);
71
+ newMessages.push(message);
72
+ newPartMap[message.id] = parts;
73
+ }
74
+ setMessages(newMessages);
75
+ setPartMap(newPartMap);
76
+ streamingMsgIdRef.current = null;
77
+ } catch (err) {
78
+ const msg = err instanceof Error ? err.message : "Failed to fetch messages";
79
+ setError(msg);
80
+ }
81
+ }, [apiUrl, token, sessionId]);
82
+ const connectSSE = useCallback(async () => {
83
+ if (!token || !sessionId || !apiUrl || !enabled) return;
84
+ if (reconnectTimerRef.current) {
85
+ clearTimeout(reconnectTimerRef.current);
86
+ reconnectTimerRef.current = null;
87
+ }
88
+ abortRef.current?.abort();
89
+ const controller = new AbortController();
90
+ abortRef.current = controller;
91
+ try {
92
+ const url = `${apiUrl}/session/events?sessionId=${encodeURIComponent(sessionId)}`;
93
+ const res = await fetch(url, {
94
+ headers: { Authorization: `Bearer ${token}` },
95
+ signal: controller.signal,
96
+ credentials: "include"
97
+ });
98
+ if (!res.ok) throw new Error(`SSE connection failed: ${res.status}`);
99
+ setConnected(true);
100
+ setError(null);
101
+ const reader = res.body?.getReader();
102
+ if (!reader) throw new Error("No response body");
103
+ const decoder = new TextDecoder();
104
+ let buffer = "";
105
+ while (true) {
106
+ const { done, value } = await reader.read();
107
+ if (done) break;
108
+ buffer += decoder.decode(value, { stream: true });
109
+ const frames = buffer.split("\n\n");
110
+ buffer = frames.pop() ?? "";
111
+ for (const frame of frames) {
112
+ if (!frame.trim()) continue;
113
+ let eventType = "message";
114
+ const dataLines = [];
115
+ for (const line of frame.split("\n")) {
116
+ if (line.startsWith("event:")) {
117
+ eventType = line.slice(6).trim();
118
+ } else if (line.startsWith("data:")) {
119
+ dataLines.push(line.slice(5).trim());
120
+ }
121
+ }
122
+ if (dataLines.length === 0) continue;
123
+ let parsed;
124
+ try {
125
+ parsed = JSON.parse(dataLines.join("\n"));
126
+ } catch {
127
+ continue;
128
+ }
129
+ handleSSEEventRef.current?.(eventType, parsed);
130
+ }
131
+ }
132
+ } catch (err) {
133
+ if (err.name === "AbortError") return;
134
+ const msg = err instanceof Error ? err.message : "SSE connection error";
135
+ setError(msg);
136
+ setConnected(false);
137
+ if (!controller.signal.aborted) {
138
+ reconnectTimerRef.current = setTimeout(() => connectSSE(), 3e3);
139
+ }
140
+ }
141
+ }, [apiUrl, token, sessionId, enabled]);
142
+ const handleSSEEvent = useCallback((type, raw) => {
143
+ const envelope = raw?.properties;
144
+ const props = envelope?.info ?? envelope?.part ?? envelope ?? raw;
145
+ if (type === "message.updated") {
146
+ const id = props.id ?? props.messageId ?? "";
147
+ const role = props.role ?? "assistant";
148
+ if (!id) return;
149
+ setMessages((prev) => {
150
+ const exists = prev.some((m) => m.id === id);
151
+ if (exists) return prev;
152
+ return [
153
+ ...prev,
154
+ {
155
+ id,
156
+ role,
157
+ time: { created: Date.now() },
158
+ _insertionIndex: insertionCounterRef.current++
159
+ }
160
+ ];
161
+ });
162
+ if (role === "assistant") {
163
+ streamingMsgIdRef.current = id;
164
+ setIsStreaming(true);
165
+ }
166
+ } else if (type === "message.part.updated") {
167
+ const msgId = streamingMsgIdRef.current;
168
+ if (!msgId) return;
169
+ const partType = props.type ?? "text";
170
+ setIsStreaming(true);
171
+ setPartMap((prev) => {
172
+ const existing = prev[msgId] ?? [];
173
+ const updated = [...existing];
174
+ if (partType === "text") {
175
+ const text = props.text ?? props.content ?? "";
176
+ const idx = updated.findIndex((p) => p.type === "text");
177
+ const textPart = { type: "text", text };
178
+ if (idx >= 0) {
179
+ updated[idx] = textPart;
180
+ } else {
181
+ updated.push(textPart);
182
+ }
183
+ } else if (partType === "tool") {
184
+ const toolId = props.id ?? props.toolId ?? `tool-${Date.now()}`;
185
+ const toolName = props.tool ?? props.name ?? "unknown";
186
+ const state = props.state ?? { status: "running" };
187
+ const toolPart = {
188
+ type: "tool",
189
+ id: toolId,
190
+ tool: toolName,
191
+ state: {
192
+ status: state.status ?? "running",
193
+ input: state.input,
194
+ output: state.output,
195
+ error: state.error,
196
+ metadata: state.metadata,
197
+ time: state.time
198
+ }
199
+ };
200
+ const idx = updated.findIndex((p) => p.type === "tool" && p.id === toolId);
201
+ if (idx >= 0) {
202
+ updated[idx] = toolPart;
203
+ } else {
204
+ updated.push(toolPart);
205
+ }
206
+ } else if (partType === "reasoning") {
207
+ const text = props.text ?? "";
208
+ const idx = updated.findIndex((p) => p.type === "reasoning");
209
+ const reasoningPart = { type: "reasoning", text };
210
+ if (idx >= 0) {
211
+ updated[idx] = reasoningPart;
212
+ } else {
213
+ updated.push(reasoningPart);
214
+ }
215
+ }
216
+ return { ...prev, [msgId]: updated };
217
+ });
218
+ } else if (type === "session.idle") {
219
+ setIsStreaming(false);
220
+ streamingMsgIdRef.current = null;
221
+ refetch();
222
+ } else if (type === "session.error") {
223
+ setIsStreaming(false);
224
+ streamingMsgIdRef.current = null;
225
+ const errorMsg = props.error ?? props.message ?? "Agent error";
226
+ setError(errorMsg);
227
+ refetch();
228
+ }
229
+ }, [refetch]);
230
+ handleSSEEventRef.current = handleSSEEvent;
231
+ const send = useCallback(async (text) => {
232
+ if (!token || !sessionId || !apiUrl) return;
233
+ try {
234
+ const url = `${apiUrl}/session/sessions/${encodeURIComponent(sessionId)}/messages`;
235
+ await fetchJson(url, token, {
236
+ method: "POST",
237
+ body: JSON.stringify({ parts: [{ type: "text", text }] })
238
+ });
239
+ setIsStreaming(true);
240
+ } catch (err) {
241
+ const msg = err instanceof Error ? err.message : "Failed to send message";
242
+ setError(msg);
243
+ }
244
+ }, [apiUrl, token, sessionId]);
245
+ const abort = useCallback(async () => {
246
+ if (!token || !sessionId || !apiUrl) return;
247
+ try {
248
+ const url = `${apiUrl}/session/sessions/${encodeURIComponent(sessionId)}/abort`;
249
+ await fetchJson(url, token, { method: "POST" });
250
+ } catch (err) {
251
+ const msg = err instanceof Error ? err.message : "Failed to abort";
252
+ setError(msg);
253
+ }
254
+ }, [apiUrl, token, sessionId]);
255
+ useEffect(() => {
256
+ if (!enabled || !token || !sessionId) return;
257
+ refetch();
258
+ connectSSE();
259
+ return () => {
260
+ abortRef.current?.abort();
261
+ if (reconnectTimerRef.current) clearTimeout(reconnectTimerRef.current);
262
+ setConnected(false);
263
+ };
264
+ }, [enabled, token, sessionId, refetch, connectSSE]);
265
+ return { messages, partMap, isStreaming, send, abort, refetch, error, connected };
266
+ }
267
+
268
+ // src/hooks/use-sidecar-auth.ts
269
+ import { useState as useState2, useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2 } from "react";
270
+ function storageKey(resourceId, apiUrl) {
271
+ return `sidecar_session_${resourceId}__${apiUrl}`;
272
+ }
273
+ function loadSession(resourceId, apiUrl) {
274
+ if (typeof window === "undefined") return null;
275
+ try {
276
+ const raw = localStorage.getItem(storageKey(resourceId, apiUrl));
277
+ if (!raw) return null;
278
+ const data = JSON.parse(raw);
279
+ if (data.expiresAt * 1e3 - Date.now() < 6e4) {
280
+ localStorage.removeItem(storageKey(resourceId, apiUrl));
281
+ return null;
282
+ }
283
+ return data;
284
+ } catch {
285
+ return null;
286
+ }
287
+ }
288
+ function saveSession(resourceId, apiUrl, token, expiresAt) {
289
+ if (typeof window === "undefined") return;
290
+ try {
291
+ localStorage.setItem(storageKey(resourceId, apiUrl), JSON.stringify({ token, expiresAt }));
292
+ } catch {
293
+ }
294
+ }
295
+ function clearSession(resourceId, apiUrl) {
296
+ if (typeof window === "undefined") return;
297
+ localStorage.removeItem(storageKey(resourceId, apiUrl));
298
+ }
299
+ function useSidecarAuth({ resourceId, apiUrl, signMessage }) {
300
+ const cached = loadSession(resourceId, apiUrl);
301
+ const [token, setToken] = useState2(cached?.token ?? null);
302
+ const [expiresAt, setExpiresAt] = useState2(cached?.expiresAt ?? 0);
303
+ const [isAuthenticating, setIsAuthenticating] = useState2(false);
304
+ const [error, setError] = useState2(null);
305
+ const refreshTimerRef = useRef2(void 0);
306
+ const clearCachedToken = useCallback2(() => {
307
+ setToken(null);
308
+ setExpiresAt(0);
309
+ clearSession(resourceId, apiUrl);
310
+ }, [resourceId, apiUrl]);
311
+ const authenticate = useCallback2(async () => {
312
+ if (!apiUrl) return null;
313
+ setIsAuthenticating(true);
314
+ setError(null);
315
+ try {
316
+ const challengeRes = await fetch(`${apiUrl}/api/auth/challenge`, {
317
+ method: "POST"
318
+ });
319
+ if (!challengeRes.ok) {
320
+ throw new Error(`Challenge failed: ${challengeRes.status}`);
321
+ }
322
+ const { nonce, message } = await challengeRes.json();
323
+ const signature = await signMessage(message);
324
+ const sessionRes = await fetch(`${apiUrl}/api/auth/session`, {
325
+ method: "POST",
326
+ headers: { "Content-Type": "application/json" },
327
+ body: JSON.stringify({ nonce, signature })
328
+ });
329
+ if (!sessionRes.ok) {
330
+ const text = await sessionRes.text();
331
+ throw new Error(text || `Session exchange failed: ${sessionRes.status}`);
332
+ }
333
+ const { token: newToken, expires_at } = await sessionRes.json();
334
+ setToken(newToken);
335
+ setExpiresAt(expires_at);
336
+ saveSession(resourceId, apiUrl, newToken, expires_at);
337
+ return newToken;
338
+ } catch (err) {
339
+ setError(err instanceof Error ? err.message : "Authentication failed");
340
+ clearCachedToken();
341
+ return null;
342
+ } finally {
343
+ setIsAuthenticating(false);
344
+ }
345
+ }, [resourceId, apiUrl, signMessage, clearCachedToken]);
346
+ useEffect2(() => {
347
+ if (refreshTimerRef.current) {
348
+ clearTimeout(refreshTimerRef.current);
349
+ }
350
+ if (!token || !expiresAt) return;
351
+ const msUntilRefresh = (expiresAt - 300) * 1e3 - Date.now();
352
+ if (msUntilRefresh <= 0) {
353
+ clearCachedToken();
354
+ return;
355
+ }
356
+ refreshTimerRef.current = setTimeout(() => {
357
+ authenticate().catch(() => {
358
+ clearCachedToken();
359
+ });
360
+ }, msUntilRefresh);
361
+ return () => {
362
+ if (refreshTimerRef.current) {
363
+ clearTimeout(refreshTimerRef.current);
364
+ }
365
+ };
366
+ }, [token, expiresAt, authenticate, clearCachedToken]);
367
+ return {
368
+ token,
369
+ isAuthenticated: token !== null,
370
+ isAuthenticating,
371
+ authenticate,
372
+ clearCachedToken,
373
+ error
374
+ };
375
+ }
376
+
377
+ export {
378
+ useSessionStream,
379
+ useSidecarAuth
380
+ };
@@ -1,22 +1,18 @@
1
1
  import {
2
- Progress
3
- } from "./chunk-66EZOYZR.js";
2
+ cn
3
+ } from "./chunk-EI44GEQ5.js";
4
+
5
+ // src/dashboard/billing-dashboard.tsx
6
+ import { Button } from "@tangle-network/ui/primitives";
7
+ import { Badge } from "@tangle-network/ui/primitives";
4
8
  import {
5
- Badge,
6
9
  Card,
7
10
  CardContent,
8
11
  CardDescription,
9
12
  CardHeader,
10
13
  CardTitle
11
- } from "./chunk-ZMNSRDMH.js";
12
- import {
13
- Button
14
- } from "./chunk-MKTSMWVD.js";
15
- import {
16
- cn
17
- } from "./chunk-RQHJBTEU.js";
18
-
19
- // src/dashboard/billing-dashboard.tsx
14
+ } from "@tangle-network/ui/primitives";
15
+ import { Progress } from "@tangle-network/ui/primitives";
20
16
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
21
17
  var variantColors = {
22
18
  sandbox: {
@@ -367,6 +363,7 @@ function PricingPage({
367
363
 
368
364
  // src/dashboard/usage-chart.tsx
369
365
  import * as React from "react";
366
+ import { Card as Card2, CardContent as CardContent2, CardHeader as CardHeader2, CardTitle as CardTitle2 } from "@tangle-network/ui/primitives";
370
367
  import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
371
368
  var colors = {
372
369
  bar: "bg-[image:var(--accent-gradient-strong)]",
@@ -390,15 +387,15 @@ function UsageChart({ data, title, unit, className }) {
390
387
  const maxValue = Math.max(...data.map((d) => d.value), 1);
391
388
  const total = data.reduce((sum, d) => sum + d.value, 0);
392
389
  const [hoveredIndex, setHoveredIndex] = React.useState(null);
393
- return /* @__PURE__ */ jsxs3(Card, { className, children: [
394
- /* @__PURE__ */ jsxs3(CardHeader, { className: "flex flex-row items-center justify-between pb-2", children: [
395
- /* @__PURE__ */ jsx3(CardTitle, { className: "font-medium text-base", children: title }),
390
+ return /* @__PURE__ */ jsxs3(Card2, { className, children: [
391
+ /* @__PURE__ */ jsxs3(CardHeader2, { className: "flex flex-row items-center justify-between pb-2", children: [
392
+ /* @__PURE__ */ jsx3(CardTitle2, { className: "font-medium text-base", children: title }),
396
393
  /* @__PURE__ */ jsxs3("div", { className: "text-right", children: [
397
394
  /* @__PURE__ */ jsx3("span", { className: cn("font-bold text-2xl", colors.text), children: formatValue(total) }),
398
395
  /* @__PURE__ */ jsx3("span", { className: "ml-1 text-muted-foreground text-sm", children: unit })
399
396
  ] })
400
397
  ] }),
401
- /* @__PURE__ */ jsxs3(CardContent, { children: [
398
+ /* @__PURE__ */ jsxs3(CardContent2, { children: [
402
399
  /* @__PURE__ */ jsxs3("div", { className: "relative", children: [
403
400
  /* @__PURE__ */ jsxs3("div", { className: "absolute top-0 left-0 flex h-48 flex-col justify-between text-muted-foreground text-xs", children: [
404
401
  /* @__PURE__ */ jsx3("span", { children: formatValue(maxValue) }),
@@ -485,9 +482,66 @@ function UsageChart({ data, title, unit, className }) {
485
482
  ] });
486
483
  }
487
484
 
485
+ // src/dashboard/template-card.tsx
486
+ import { ArrowRight } from "lucide-react";
487
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
488
+ function TemplateCard({ template, onUseTemplate, className }) {
489
+ return /* @__PURE__ */ jsxs4(
490
+ "div",
491
+ {
492
+ className: cn(
493
+ "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",
494
+ className
495
+ ),
496
+ children: [
497
+ /* @__PURE__ */ jsxs4("div", { children: [
498
+ /* @__PURE__ */ jsx4("div", { className: "mb-4 flex h-12 w-12 items-center justify-center rounded-xl bg-muted border border-border", children: template.icon ?? /* @__PURE__ */ jsx4("span", { className: "text-lg font-bold text-primary", children: template.name.charAt(0).toUpperCase() }) }),
499
+ /* @__PURE__ */ jsx4("h3", { className: "mb-1 text-base font-bold text-foreground", children: template.name }),
500
+ /* @__PURE__ */ jsx4("p", { className: "text-sm text-muted-foreground leading-relaxed line-clamp-2", children: template.description }),
501
+ template.tags && template.tags.length > 0 && /* @__PURE__ */ jsx4("div", { className: "mt-3 flex flex-wrap gap-1.5", children: template.tags.map((tag, i) => /* @__PURE__ */ jsx4(
502
+ "span",
503
+ {
504
+ className: "rounded-full bg-muted px-2 py-0.5 text-[10px] font-medium text-muted-foreground",
505
+ children: tag
506
+ },
507
+ `${tag}-${i}`
508
+ )) })
509
+ ] }),
510
+ /* @__PURE__ */ jsxs4(
511
+ "button",
512
+ {
513
+ type: "button",
514
+ onClick: () => onUseTemplate(template.id),
515
+ 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]",
516
+ children: [
517
+ "Use Template",
518
+ /* @__PURE__ */ jsx4(ArrowRight, { className: "h-4 w-4 transition-transform group-hover:translate-x-0.5" })
519
+ ]
520
+ }
521
+ )
522
+ ]
523
+ }
524
+ );
525
+ }
526
+
527
+ // src/dashboard/info-panel.tsx
528
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
529
+ function InfoPanel({ label, title, description, className }) {
530
+ return /* @__PURE__ */ jsxs5("div", { className: cn("rounded-lg bg-[var(--brand-strong)] p-5 text-[var(--brand-strong-text)] relative overflow-hidden", className), children: [
531
+ /* @__PURE__ */ jsxs5("div", { className: "relative z-10", children: [
532
+ /* @__PURE__ */ jsx5("p", { className: "text-[10px] font-bold uppercase tracking-widest text-[var(--brand-strong-text-dim)]", children: label }),
533
+ /* @__PURE__ */ jsx5("h3", { className: "mt-1 text-lg font-bold", children: title }),
534
+ /* @__PURE__ */ jsx5("p", { className: "mt-1 text-sm text-[var(--brand-strong-text-muted)]", children: description })
535
+ ] }),
536
+ /* @__PURE__ */ jsx5("div", { className: "absolute right-0 top-0 h-full w-1/3 bg-white/5 -skew-x-12 translate-x-12 pointer-events-none" })
537
+ ] });
538
+ }
539
+
488
540
  export {
489
541
  BillingDashboard,
490
542
  formatPrice,
491
543
  PricingPage,
492
- UsageChart
544
+ UsageChart,
545
+ TemplateCard,
546
+ InfoPanel
493
547
  };
@@ -0,0 +1,6 @@
1
+ // src/lib/utils.ts
2
+ import { cn } from "@tangle-network/ui/utils";
3
+
4
+ export {
5
+ cn
6
+ };
@@ -1,36 +1,6 @@
1
- import {
2
- useActiveSessions,
3
- useNavbarSessions,
4
- useProjectActivity,
5
- useTotalRunningSessions
6
- } from "./chunk-OEX7NZE3.js";
7
- import {
8
- EmptyState,
9
- Input
10
- } from "./chunk-MA7YKRUP.js";
11
- import {
12
- ChatContainer
13
- } from "./chunk-XLG757B6.js";
14
- import {
15
- OpenUIArtifactRenderer
16
- } from "./chunk-ZNCEM5CD.js";
17
- import {
18
- FileArtifactPane,
19
- FileTree,
20
- filterFileTree
21
- } from "./chunk-QDH5GEGY.js";
22
- import {
23
- ArtifactPane
24
- } from "./chunk-HB5Y37YU.js";
25
- import {
26
- Markdown
27
- } from "./chunk-T7HMZEVO.js";
28
- import {
29
- Badge
30
- } from "./chunk-ZMNSRDMH.js";
31
1
  import {
32
2
  cn
33
- } from "./chunk-RQHJBTEU.js";
3
+ } from "./chunk-EI44GEQ5.js";
34
4
 
35
5
  // src/workspace/workspace-layout.tsx
36
6
  import {
@@ -487,9 +457,19 @@ function WorkspaceLayout({
487
457
  );
488
458
  }
489
459
 
460
+ // src/workspace/index.ts
461
+ import { ArtifactPane as ArtifactPane3 } from "@tangle-network/ui/primitives";
462
+
490
463
  // src/workspace/directory-pane.tsx
491
464
  import { useMemo as useMemo2, useState as useState2 } from "react";
492
465
  import { RefreshCw, Search, Upload } from "lucide-react";
466
+ import { ArtifactPane } from "@tangle-network/ui/primitives";
467
+ import {
468
+ FileTree,
469
+ filterFileTree
470
+ } from "@tangle-network/ui/files";
471
+ import { EmptyState } from "@tangle-network/ui/primitives";
472
+ import { Input } from "@tangle-network/ui/primitives";
493
473
  import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
494
474
  function countNodes(node) {
495
475
  if (node.type === "file") {
@@ -612,6 +592,9 @@ function DirectoryPane({
612
592
  );
613
593
  }
614
594
 
595
+ // src/workspace/runtime-pane.tsx
596
+ import { Binary, ShieldCheck, TerminalSquare } from "lucide-react";
597
+
615
598
  // src/workspace/status-banner.tsx
616
599
  import { Loader2, AlertCircle, CheckCircle, Wifi } from "lucide-react";
617
600
  import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
@@ -773,7 +756,6 @@ function TerminalPanel({
773
756
  }
774
757
 
775
758
  // src/workspace/runtime-pane.tsx
776
- import { Binary, ShieldCheck, TerminalSquare } from "lucide-react";
777
759
  import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
778
760
  function RuntimePane({
779
761
  title = "Runtime",
@@ -833,6 +815,7 @@ function RuntimePane({
833
815
  // src/workspace/session-sidebar.tsx
834
816
  import { useCallback, useMemo as useMemo3, useRef as useRef3, useState as useState3 } from "react";
835
817
  import { ArrowLeft, FolderTree, MessageSquareText, Plus, Search as Search2, Settings, Sparkles } from "lucide-react";
818
+ import { useNavbarSessions } from "@tangle-network/ui/stores";
836
819
  import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
837
820
  function statusDot(status) {
838
821
  switch (status) {
@@ -1155,6 +1138,11 @@ function SessionSidebar({
1155
1138
 
1156
1139
  // src/workspace/session-activity-monitor.tsx
1157
1140
  import { Activity, AlertCircle as AlertCircle2, LoaderCircle, MessageSquareText as MessageSquareText2 } from "lucide-react";
1141
+ import {
1142
+ useActiveSessions,
1143
+ useProjectActivity,
1144
+ useTotalRunningSessions
1145
+ } from "@tangle-network/ui/stores";
1158
1146
  import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
1159
1147
  function SessionStatusDot({ session }) {
1160
1148
  if (session.status === "error") {
@@ -1260,6 +1248,13 @@ import {
1260
1248
  LayoutPanelTop,
1261
1249
  X as X4
1262
1250
  } from "lucide-react";
1251
+ import { Badge } from "@tangle-network/ui/primitives";
1252
+ import { EmptyState as EmptyState2 } from "@tangle-network/ui/primitives";
1253
+ import { Markdown } from "@tangle-network/ui/markdown";
1254
+ import { ChatContainer } from "@tangle-network/ui/chat";
1255
+ import { FileArtifactPane } from "@tangle-network/ui/files";
1256
+ import { OpenUIArtifactRenderer } from "@tangle-network/ui/openui";
1257
+ import { ArtifactPane as ArtifactPane2 } from "@tangle-network/ui/primitives";
1263
1258
  import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
1264
1259
  function getArtifactTabIcon(kind) {
1265
1260
  switch (kind) {
@@ -1348,7 +1343,7 @@ function renderArtifact(artifact) {
1348
1343
  );
1349
1344
  case "markdown":
1350
1345
  return /* @__PURE__ */ jsx9(
1351
- ArtifactPane,
1346
+ ArtifactPane2,
1352
1347
  {
1353
1348
  eyebrow: artifact.eyebrow ?? "Document",
1354
1349
  title: artifact.title,
@@ -1362,7 +1357,7 @@ function renderArtifact(artifact) {
1362
1357
  );
1363
1358
  case "openui":
1364
1359
  return /* @__PURE__ */ jsx9(
1365
- ArtifactPane,
1360
+ ArtifactPane2,
1366
1361
  {
1367
1362
  eyebrow: artifact.eyebrow ?? "Structured Artifact",
1368
1363
  title: artifact.title,
@@ -1376,7 +1371,7 @@ function renderArtifact(artifact) {
1376
1371
  );
1377
1372
  case "custom":
1378
1373
  return /* @__PURE__ */ jsx9(
1379
- ArtifactPane,
1374
+ ArtifactPane2,
1380
1375
  {
1381
1376
  eyebrow: artifact.eyebrow ?? "Artifact",
1382
1377
  title: artifact.title,
@@ -1480,7 +1475,7 @@ function SandboxWorkbench({
1480
1475
  ] })
1481
1476
  ] });
1482
1477
  const center = /* @__PURE__ */ jsx9(
1483
- ArtifactPane,
1478
+ ArtifactPane2,
1484
1479
  {
1485
1480
  eyebrow: session.eyebrow ?? "Agent Session",
1486
1481
  title: session.title ?? "Execution timeline",
@@ -1510,7 +1505,7 @@ function SandboxWorkbench({
1510
1505
  }
1511
1506
  ),
1512
1507
  /* @__PURE__ */ jsx9("div", { className: "min-h-0 flex-1 overflow-auto bg-background", children: activeArtifact ? renderArtifact(activeArtifact) : /* @__PURE__ */ jsx9("div", { className: "flex h-full items-center justify-center p-6", children: emptyArtifactState ?? /* @__PURE__ */ jsx9(
1513
- EmptyState,
1508
+ EmptyState2,
1514
1509
  {
1515
1510
  icon: /* @__PURE__ */ jsx9(Boxes, { className: "h-8 w-8" }),
1516
1511
  title: "No artifact selected",
@@ -2200,5 +2195,6 @@ export {
2200
2195
  AuditResults,
2201
2196
  TaskBoard,
2202
2197
  CalendarView,
2203
- ApprovalQueue
2198
+ ApprovalQueue,
2199
+ ArtifactPane3 as ArtifactPane
2204
2200
  };