@tangle-network/sandbox-ui 0.2.2 → 0.3.3

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 (67) hide show
  1. package/README.md +201 -10
  2. package/dist/auth.js +2 -2
  3. package/dist/chat-container-C8eHLw8z.d.ts +67 -0
  4. package/dist/chat.d.ts +70 -78
  5. package/dist/chat.js +8 -8
  6. package/dist/chunk-4F2GJRGU.js +756 -0
  7. package/dist/{chunk-HYEAX3DC.js → chunk-5LV6DZZF.js} +445 -114
  8. package/dist/chunk-67C53XVV.js +1106 -0
  9. package/dist/{chunk-QSQBDR3N.js → chunk-BX6AQMUS.js} +5 -2
  10. package/dist/chunk-CCKNIAS7.js +124 -0
  11. package/dist/chunk-CJ2RYVZH.js +128 -0
  12. package/dist/{chunk-KMXV7DDX.js → chunk-CNWVHQFY.js} +1 -1
  13. package/dist/{chunk-OU4TRNQZ.js → chunk-COCSO7FG.js} +3 -3
  14. package/dist/chunk-FJSVPBKY.js +85 -0
  15. package/dist/chunk-FRGMMANX.js +102 -0
  16. package/dist/{chunk-E6FS7R4X.js → chunk-HWLX5NME.js} +1 -1
  17. package/dist/chunk-JF6E2DS5.js +610 -0
  18. package/dist/chunk-MUOL44AE.js +121 -0
  19. package/dist/chunk-MXCSSOGH.js +105 -0
  20. package/dist/{chunk-J4OADEUK.js → chunk-OM6ON27W.js} +24 -9
  21. package/dist/{chunk-NI2EI43H.js → chunk-PDV7W4NY.js} +9 -124
  22. package/dist/chunk-TQN3VR4F.js +92 -0
  23. package/dist/{chunk-SOT2V7TX.js → chunk-TXI4MZAZ.js} +62 -144
  24. package/dist/chunk-WUR652Y3.js +1140 -0
  25. package/dist/chunk-YDBXQQLC.js +336 -0
  26. package/dist/{chunk-4EIWPJMJ.js → chunk-ZP6GSX4D.js} +36 -27
  27. package/dist/dashboard.d.ts +5 -2
  28. package/dist/dashboard.js +5 -4
  29. package/dist/{expanded-tool-detail-OkXGqTHe.d.ts → expanded-tool-detail-BDi_h_dZ.d.ts} +11 -4
  30. package/dist/file-tabs-CmaoDVBI.d.ts +72 -0
  31. package/dist/files.d.ts +25 -44
  32. package/dist/files.js +8 -3
  33. package/{src/styles → dist}/globals.css +16 -67
  34. package/dist/hooks.d.ts +5 -4
  35. package/dist/hooks.js +14 -9
  36. package/dist/index.d.ts +38 -9
  37. package/dist/index.js +100 -126
  38. package/dist/markdown.d.ts +1 -24
  39. package/dist/markdown.js +1 -7
  40. package/dist/openui.d.ts +115 -0
  41. package/dist/openui.js +11 -0
  42. package/dist/pages.d.ts +3 -2
  43. package/dist/pages.js +19 -16
  44. package/dist/primitives.js +25 -19
  45. package/dist/run.d.ts +2 -2
  46. package/dist/run.js +8 -7
  47. package/dist/{use-sidecar-auth-Bb0-w3lX.d.ts → sdk-hooks.d.ts} +61 -72
  48. package/dist/sdk-hooks.js +29 -0
  49. package/dist/styles.css +179 -0
  50. package/dist/tokens.css +165 -0
  51. package/dist/{tool-display-BvsVW_Ur.d.ts → tool-display-Ct9nFAzJ.d.ts} +1 -1
  52. package/dist/types.d.ts +1 -1
  53. package/dist/{usage-chart-DINgSVL5.d.ts → usage-chart-CY9xo3KX.d.ts} +8 -3
  54. package/dist/use-pty-session-DeZSxOCN.d.ts +69 -0
  55. package/dist/utils.d.ts +1 -1
  56. package/dist/utils.js +1 -1
  57. package/dist/workspace.d.ts +171 -33
  58. package/dist/workspace.js +25 -1
  59. package/package.json +10 -3
  60. package/dist/chunk-2UHPE5T7.js +0 -201
  61. package/dist/chunk-6MQIDUPA.js +0 -502
  62. package/dist/chunk-KYY2X6LY.js +0 -318
  63. package/dist/chunk-L6ZDH5F4.js +0 -334
  64. package/dist/chunk-M34OA6PQ.js +0 -233
  65. package/dist/chunk-M6VLC32S.js +0 -219
  66. package/dist/chunk-U62G5TS7.js +0 -472
  67. package/src/styles/tokens.css +0 -73
@@ -0,0 +1,1106 @@
1
+ import {
2
+ EmptyState,
3
+ Input
4
+ } from "./chunk-MUOL44AE.js";
5
+ import {
6
+ ChatContainer
7
+ } from "./chunk-4F2GJRGU.js";
8
+ import {
9
+ OpenUIArtifactRenderer
10
+ } from "./chunk-YDBXQQLC.js";
11
+ import {
12
+ ArtifactPane,
13
+ FileArtifactPane,
14
+ FileTree,
15
+ filterFileTree
16
+ } from "./chunk-JF6E2DS5.js";
17
+ import {
18
+ Markdown
19
+ } from "./chunk-LTFK464G.js";
20
+ import {
21
+ Badge
22
+ } from "./chunk-MXCSSOGH.js";
23
+ import {
24
+ cn
25
+ } from "./chunk-RQHJBTEU.js";
26
+
27
+ // src/workspace/workspace-layout.tsx
28
+ import {
29
+ useEffect,
30
+ useMemo,
31
+ useRef,
32
+ useState
33
+ } from "react";
34
+ import {
35
+ PanelBottomClose,
36
+ PanelBottomOpen,
37
+ PanelLeftClose,
38
+ PanelLeftOpen,
39
+ PanelRightClose,
40
+ PanelRightOpen,
41
+ X
42
+ } from "lucide-react";
43
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
44
+ var DESKTOP_BREAKPOINT = "(min-width: 1024px)";
45
+ function clamp(value, min, max) {
46
+ return Math.min(Math.max(value, min), max);
47
+ }
48
+ function readStoredLayout(key) {
49
+ if (typeof window === "undefined") return null;
50
+ try {
51
+ const raw = window.localStorage.getItem(key);
52
+ if (!raw) return null;
53
+ const parsed = JSON.parse(raw);
54
+ return parsed && typeof parsed === "object" ? parsed : null;
55
+ } catch {
56
+ return null;
57
+ }
58
+ }
59
+ function useDesktopMediaQuery(query) {
60
+ const [matches, setMatches] = useState(() => {
61
+ if (typeof window === "undefined") return true;
62
+ return window.matchMedia(query).matches;
63
+ });
64
+ useEffect(() => {
65
+ if (typeof window === "undefined") return;
66
+ const media = window.matchMedia(query);
67
+ const handleChange = (event) => {
68
+ setMatches(event.matches);
69
+ };
70
+ setMatches(media.matches);
71
+ media.addEventListener("change", handleChange);
72
+ return () => media.removeEventListener("change", handleChange);
73
+ }, [query]);
74
+ return matches;
75
+ }
76
+ function ResizeHandle({ label, onDragStart, onStep, className }) {
77
+ const handlePointerDown = (event) => {
78
+ event.preventDefault();
79
+ onDragStart(event.clientX);
80
+ };
81
+ const handleKeyDown = (event) => {
82
+ if (event.key === "ArrowLeft") {
83
+ event.preventDefault();
84
+ onStep(-24);
85
+ }
86
+ if (event.key === "ArrowRight") {
87
+ event.preventDefault();
88
+ onStep(24);
89
+ }
90
+ };
91
+ return /* @__PURE__ */ jsxs(
92
+ "button",
93
+ {
94
+ type: "button",
95
+ "aria-label": label,
96
+ role: "separator",
97
+ "aria-orientation": "vertical",
98
+ onPointerDown: handlePointerDown,
99
+ onKeyDown: handleKeyDown,
100
+ className: cn(
101
+ "relative hidden w-3 shrink-0 cursor-col-resize lg:flex",
102
+ "items-stretch justify-center bg-transparent touch-none",
103
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--brand-cool)]/60",
104
+ className
105
+ ),
106
+ children: [
107
+ /* @__PURE__ */ jsx("span", { className: "absolute inset-y-0 left-1/2 w-px -translate-x-1/2 bg-[var(--border-subtle)] transition-colors" }),
108
+ /* @__PURE__ */ jsx("span", { className: "absolute inset-y-0 left-1/2 w-[3px] -translate-x-1/2 rounded-full bg-transparent hover:bg-[var(--brand-cool)]/30 focus-visible:bg-[var(--brand-cool)]/40" })
109
+ ]
110
+ }
111
+ );
112
+ }
113
+ function MobileDrawer({ side, title, header, onClose, children }) {
114
+ return /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-50 flex lg:hidden", "aria-modal": "true", role: "dialog", "aria-label": title, children: [
115
+ /* @__PURE__ */ jsx(
116
+ "button",
117
+ {
118
+ type: "button",
119
+ "aria-label": `Close ${title}`,
120
+ onClick: onClose,
121
+ className: "absolute inset-0 bg-black/55 backdrop-blur-[2px]"
122
+ }
123
+ ),
124
+ /* @__PURE__ */ jsxs(
125
+ "aside",
126
+ {
127
+ className: cn(
128
+ "relative flex h-full w-[min(88vw,24rem)] flex-col border-[var(--border-default)] bg-[var(--bg-dark)] shadow-[var(--shadow-dropdown)]",
129
+ side === "left" ? "border-r" : "ml-auto border-l"
130
+ ),
131
+ children: [
132
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3 border-b border-[var(--border-subtle)] px-4 py-3", children: [
133
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: header ?? /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-[var(--text-primary)]", children: title }) }),
134
+ /* @__PURE__ */ jsx(
135
+ "button",
136
+ {
137
+ type: "button",
138
+ "aria-label": `Close ${title}`,
139
+ onClick: onClose,
140
+ className: "rounded-[var(--radius-sm)] p-1.5 text-[var(--text-muted)] transition-colors hover:bg-[var(--bg-hover)] hover:text-[var(--text-secondary)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--brand-cool)]/60",
141
+ children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" })
142
+ }
143
+ )
144
+ ] }),
145
+ /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-auto", children })
146
+ ]
147
+ }
148
+ )
149
+ ] });
150
+ }
151
+ function WorkspaceLayout({
152
+ left,
153
+ leftHeader,
154
+ center,
155
+ centerHeader,
156
+ centerFooter,
157
+ right,
158
+ rightHeader,
159
+ bottom,
160
+ defaultLeftOpen = true,
161
+ defaultRightOpen = false,
162
+ defaultBottomOpen = false,
163
+ defaultLeftWidth = 280,
164
+ defaultRightWidth = 480,
165
+ minLeftWidth = 220,
166
+ maxLeftWidth = 420,
167
+ minRightWidth = 320,
168
+ maxRightWidth = 720,
169
+ persistenceKey,
170
+ resizable = true,
171
+ theme = "operator",
172
+ density = "comfortable",
173
+ leftLabel = "Left workspace panel",
174
+ rightLabel = "Right workspace panel",
175
+ bottomLabel = "Bottom runtime panel",
176
+ className
177
+ }) {
178
+ const desktop = useDesktopMediaQuery(DESKTOP_BREAKPOINT);
179
+ const dragStateRef = useRef(null);
180
+ const storedLayout = useMemo(
181
+ () => persistenceKey ? readStoredLayout(persistenceKey) : null,
182
+ [persistenceKey]
183
+ );
184
+ const [leftOpen, setLeftOpen] = useState(storedLayout?.leftOpen ?? defaultLeftOpen);
185
+ const [rightOpen, setRightOpen] = useState(storedLayout?.rightOpen ?? defaultRightOpen);
186
+ const [bottomOpen, setBottomOpen] = useState(storedLayout?.bottomOpen ?? defaultBottomOpen);
187
+ const [leftWidth, setLeftWidth] = useState(
188
+ clamp(storedLayout?.leftWidth ?? defaultLeftWidth, minLeftWidth, maxLeftWidth)
189
+ );
190
+ const [rightWidth, setRightWidth] = useState(
191
+ clamp(storedLayout?.rightWidth ?? defaultRightWidth, minRightWidth, maxRightWidth)
192
+ );
193
+ useEffect(() => {
194
+ if (!persistenceKey || typeof window === "undefined") return;
195
+ const payload = {
196
+ leftOpen,
197
+ rightOpen,
198
+ bottomOpen,
199
+ leftWidth,
200
+ rightWidth
201
+ };
202
+ window.localStorage.setItem(persistenceKey, JSON.stringify(payload));
203
+ }, [bottomOpen, leftOpen, leftWidth, persistenceKey, rightOpen, rightWidth]);
204
+ useEffect(() => {
205
+ if (!desktop) return;
206
+ const handlePointerMove = (event) => {
207
+ const dragState = dragStateRef.current;
208
+ if (!dragState) return;
209
+ if (dragState.side === "left") {
210
+ const delta = event.clientX - dragState.pointerStartX;
211
+ setLeftWidth(clamp(dragState.widthStart + delta, minLeftWidth, maxLeftWidth));
212
+ } else {
213
+ const delta = dragState.pointerStartX - event.clientX;
214
+ setRightWidth(clamp(dragState.widthStart + delta, minRightWidth, maxRightWidth));
215
+ }
216
+ };
217
+ const handlePointerUp = () => {
218
+ dragStateRef.current = null;
219
+ };
220
+ window.addEventListener("pointermove", handlePointerMove);
221
+ window.addEventListener("pointerup", handlePointerUp);
222
+ window.addEventListener("pointercancel", handlePointerUp);
223
+ return () => {
224
+ window.removeEventListener("pointermove", handlePointerMove);
225
+ window.removeEventListener("pointerup", handlePointerUp);
226
+ window.removeEventListener("pointercancel", handlePointerUp);
227
+ };
228
+ }, [desktop, maxLeftWidth, maxRightWidth, minLeftWidth, minRightWidth]);
229
+ const leftStyle = useMemo(() => ({ width: `${leftWidth}px` }), [leftWidth]);
230
+ const rightStyle = useMemo(() => ({ width: `${rightWidth}px` }), [rightWidth]);
231
+ const startResize = (side, pointerStartX) => {
232
+ dragStateRef.current = {
233
+ side,
234
+ pointerStartX,
235
+ widthStart: side === "left" ? leftWidth : rightWidth
236
+ };
237
+ };
238
+ const stepLeftWidth = (delta) => {
239
+ setLeftWidth((current) => clamp(current + delta, minLeftWidth, maxLeftWidth));
240
+ };
241
+ const stepRightWidth = (delta) => {
242
+ setRightWidth((current) => clamp(current + delta, minRightWidth, maxRightWidth));
243
+ };
244
+ return /* @__PURE__ */ jsxs(
245
+ "div",
246
+ {
247
+ "data-sandbox-ui": "true",
248
+ "data-sandbox-theme": theme,
249
+ "data-density": density,
250
+ className: cn(
251
+ "flex h-screen flex-col overflow-hidden bg-[radial-gradient(circle_at_top,rgba(98,114,243,0.14),transparent_34%),linear-gradient(180deg,var(--bg-root),var(--bg-dark)_82%)] text-[var(--text-primary)] font-[var(--font-sans)]",
252
+ className
253
+ ),
254
+ children: [
255
+ /* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1", children: [
256
+ desktop && left && leftOpen && /* @__PURE__ */ jsxs(Fragment, { children: [
257
+ /* @__PURE__ */ jsxs(
258
+ "aside",
259
+ {
260
+ "aria-label": leftLabel,
261
+ style: leftStyle,
262
+ className: "hidden shrink-0 border-r border-[var(--border-subtle)] bg-[var(--bg-dark)] lg:flex lg:flex-col",
263
+ children: [
264
+ leftHeader && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 border-b border-[var(--border-subtle)] px-3 py-2", children: [
265
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: leftHeader }),
266
+ /* @__PURE__ */ jsx(
267
+ "button",
268
+ {
269
+ type: "button",
270
+ "aria-label": "Collapse left panel",
271
+ onClick: () => setLeftOpen(false),
272
+ className: "rounded-[var(--radius-sm)] p-1.5 text-[var(--text-muted)] transition-colors hover:bg-[var(--bg-hover)] hover:text-[var(--text-secondary)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--brand-cool)]/60",
273
+ children: /* @__PURE__ */ jsx(PanelLeftClose, { className: "h-4 w-4" })
274
+ }
275
+ )
276
+ ] }),
277
+ /* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 overflow-auto py-1", children: left })
278
+ ]
279
+ }
280
+ ),
281
+ resizable && /* @__PURE__ */ jsx(
282
+ ResizeHandle,
283
+ {
284
+ label: "Resize left panel",
285
+ onDragStart: (clientX) => startResize("left", clientX),
286
+ onStep: stepLeftWidth
287
+ }
288
+ )
289
+ ] }),
290
+ /* @__PURE__ */ jsxs("main", { className: "flex min-w-0 flex-1 flex-col", children: [
291
+ (centerHeader || left || right || bottom) && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 border-b border-[var(--border-subtle)] bg-[var(--bg-dark)] px-3 py-2", children: [
292
+ left && !leftOpen && /* @__PURE__ */ jsx(
293
+ "button",
294
+ {
295
+ type: "button",
296
+ "aria-label": "Open left panel",
297
+ onClick: () => setLeftOpen(true),
298
+ className: "rounded-[var(--radius-sm)] p-1.5 text-[var(--text-muted)] transition-colors hover:bg-[var(--bg-hover)] hover:text-[var(--text-secondary)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--brand-cool)]/60",
299
+ children: /* @__PURE__ */ jsx(PanelLeftOpen, { className: "h-4 w-4" })
300
+ }
301
+ ),
302
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: centerHeader }),
303
+ bottom && !bottomOpen && /* @__PURE__ */ jsx(
304
+ "button",
305
+ {
306
+ type: "button",
307
+ "aria-label": "Open bottom panel",
308
+ onClick: () => setBottomOpen(true),
309
+ className: "rounded-[var(--radius-sm)] p-1.5 text-[var(--text-muted)] transition-colors hover:bg-[var(--bg-hover)] hover:text-[var(--text-secondary)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--brand-cool)]/60",
310
+ children: /* @__PURE__ */ jsx(PanelBottomOpen, { className: "h-4 w-4" })
311
+ }
312
+ ),
313
+ right && !rightOpen && /* @__PURE__ */ jsx(
314
+ "button",
315
+ {
316
+ type: "button",
317
+ "aria-label": "Open right panel",
318
+ onClick: () => setRightOpen(true),
319
+ className: "rounded-[var(--radius-sm)] p-1.5 text-[var(--text-muted)] transition-colors hover:bg-[var(--bg-hover)] hover:text-[var(--text-secondary)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--brand-cool)]/60",
320
+ children: /* @__PURE__ */ jsx(PanelRightOpen, { className: "h-4 w-4" })
321
+ }
322
+ )
323
+ ] }),
324
+ /* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 overflow-auto", children: center }),
325
+ bottom && bottomOpen && /* @__PURE__ */ jsxs(
326
+ "section",
327
+ {
328
+ "aria-label": bottomLabel,
329
+ className: "border-t border-[var(--border-subtle)] bg-[var(--bg-card)]",
330
+ children: [
331
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 border-b border-[var(--border-subtle)] px-3 py-2", children: [
332
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-semibold uppercase tracking-[0.12em] text-[var(--text-muted)]", children: "Runtime" }),
333
+ /* @__PURE__ */ jsx(
334
+ "button",
335
+ {
336
+ type: "button",
337
+ "aria-label": "Collapse bottom panel",
338
+ onClick: () => setBottomOpen(false),
339
+ className: "rounded-[var(--radius-sm)] p-1.5 text-[var(--text-muted)] transition-colors hover:bg-[var(--bg-hover)] hover:text-[var(--text-secondary)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--brand-cool)]/60",
340
+ children: /* @__PURE__ */ jsx(PanelBottomClose, { className: "h-4 w-4" })
341
+ }
342
+ )
343
+ ] }),
344
+ /* @__PURE__ */ jsx("div", { className: "max-h-56 overflow-auto", children: bottom })
345
+ ]
346
+ }
347
+ ),
348
+ centerFooter && /* @__PURE__ */ jsx("div", { className: "shrink-0 border-t border-[var(--border-subtle)] bg-[var(--bg-dark)]", children: centerFooter })
349
+ ] }),
350
+ desktop && right && rightOpen && /* @__PURE__ */ jsxs(Fragment, { children: [
351
+ resizable && /* @__PURE__ */ jsx(
352
+ ResizeHandle,
353
+ {
354
+ label: "Resize right panel",
355
+ onDragStart: (clientX) => startResize("right", clientX),
356
+ onStep: stepRightWidth
357
+ }
358
+ ),
359
+ /* @__PURE__ */ jsxs(
360
+ "aside",
361
+ {
362
+ "aria-label": rightLabel,
363
+ style: rightStyle,
364
+ className: "hidden shrink-0 border-l border-[var(--border-subtle)] bg-[var(--bg-dark)] lg:flex lg:flex-col",
365
+ children: [
366
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 border-b border-[var(--border-subtle)] px-3 py-2", children: [
367
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: rightHeader ?? /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-[var(--text-primary)]", children: "Artifacts" }) }),
368
+ /* @__PURE__ */ jsx(
369
+ "button",
370
+ {
371
+ type: "button",
372
+ "aria-label": "Collapse right panel",
373
+ onClick: () => setRightOpen(false),
374
+ className: "rounded-[var(--radius-sm)] p-1.5 text-[var(--text-muted)] transition-colors hover:bg-[var(--bg-hover)] hover:text-[var(--text-secondary)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--brand-cool)]/60",
375
+ children: /* @__PURE__ */ jsx(PanelRightClose, { className: "h-4 w-4" })
376
+ }
377
+ )
378
+ ] }),
379
+ /* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 overflow-auto", children: right })
380
+ ]
381
+ }
382
+ )
383
+ ] })
384
+ ] }),
385
+ !desktop && left && leftOpen && /* @__PURE__ */ jsx(
386
+ MobileDrawer,
387
+ {
388
+ side: "left",
389
+ title: leftLabel,
390
+ header: leftHeader,
391
+ onClose: () => setLeftOpen(false),
392
+ children: left
393
+ }
394
+ ),
395
+ !desktop && right && rightOpen && /* @__PURE__ */ jsx(
396
+ MobileDrawer,
397
+ {
398
+ side: "right",
399
+ title: rightLabel,
400
+ header: rightHeader,
401
+ onClose: () => setRightOpen(false),
402
+ children: right
403
+ }
404
+ )
405
+ ]
406
+ }
407
+ );
408
+ }
409
+
410
+ // src/workspace/directory-pane.tsx
411
+ import { useMemo as useMemo2, useState as useState2 } from "react";
412
+ import { RefreshCw, Search, Upload } from "lucide-react";
413
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
414
+ function countNodes(node) {
415
+ if (node.type === "file") {
416
+ return { files: 1, directories: 0 };
417
+ }
418
+ return (node.children ?? []).reduce(
419
+ (totals, child) => {
420
+ const counts = countNodes(child);
421
+ return {
422
+ files: totals.files + counts.files,
423
+ directories: totals.directories + counts.directories
424
+ };
425
+ },
426
+ { files: 0, directories: 1 }
427
+ );
428
+ }
429
+ function filterTree(node, query) {
430
+ if (!query) return node;
431
+ const normalized = query.trim().toLowerCase();
432
+ const matches = node.name.toLowerCase().includes(normalized) || node.path.toLowerCase().includes(normalized);
433
+ if (node.type === "file") {
434
+ return matches ? node : null;
435
+ }
436
+ const filteredChildren = (node.children ?? []).map((child) => filterTree(child, normalized)).filter((child) => child !== null);
437
+ if (matches || filteredChildren.length > 0) {
438
+ return {
439
+ ...node,
440
+ children: filteredChildren
441
+ };
442
+ }
443
+ return null;
444
+ }
445
+ function DirectoryPane({
446
+ root,
447
+ selectedPath,
448
+ onSelect,
449
+ onRefresh,
450
+ onUpload,
451
+ title = "Directory",
452
+ subtitle,
453
+ defaultExpanded = true,
454
+ searchPlaceholder = "Search files and folders\u2026",
455
+ visibility,
456
+ className
457
+ }) {
458
+ const [query, setQuery] = useState2("");
459
+ const visibleRoot = useMemo2(() => filterFileTree(root, visibility), [root, visibility]);
460
+ const filteredRoot = useMemo2(
461
+ () => visibleRoot ? filterTree(visibleRoot, query) : null,
462
+ [query, visibleRoot]
463
+ );
464
+ const counts = useMemo2(() => visibleRoot ? countNodes(visibleRoot) : { files: 0, directories: 0 }, [visibleRoot]);
465
+ return /* @__PURE__ */ jsx2(
466
+ ArtifactPane,
467
+ {
468
+ eyebrow: "Workspace",
469
+ title,
470
+ subtitle: subtitle ?? root.path,
471
+ className,
472
+ toolbar: /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2", children: [
473
+ /* @__PURE__ */ jsx2("div", { className: "flex-1", children: /* @__PURE__ */ jsx2(
474
+ Input,
475
+ {
476
+ value: query,
477
+ onChange: (event) => setQuery(event.target.value),
478
+ placeholder: searchPlaceholder,
479
+ className: "h-9"
480
+ }
481
+ ) }),
482
+ onRefresh && /* @__PURE__ */ jsx2(
483
+ "button",
484
+ {
485
+ type: "button",
486
+ "aria-label": "Refresh directory",
487
+ onClick: onRefresh,
488
+ className: "rounded-[var(--radius-sm)] p-2 text-[var(--text-muted)] transition-colors hover:bg-[var(--bg-hover)] hover:text-[var(--text-secondary)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--brand-cool)]/60",
489
+ children: /* @__PURE__ */ jsx2(RefreshCw, { className: "h-4 w-4" })
490
+ }
491
+ ),
492
+ onUpload && /* @__PURE__ */ jsx2(
493
+ "button",
494
+ {
495
+ type: "button",
496
+ "aria-label": "Upload files",
497
+ onClick: onUpload,
498
+ className: "rounded-[var(--radius-sm)] p-2 text-[var(--text-muted)] transition-colors hover:bg-[var(--bg-hover)] hover:text-[var(--text-secondary)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--brand-cool)]/60",
499
+ children: /* @__PURE__ */ jsx2(Upload, { className: "h-4 w-4" })
500
+ }
501
+ )
502
+ ] }),
503
+ footer: /* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between text-xs text-[var(--text-muted)]", children: [
504
+ /* @__PURE__ */ jsxs2("span", { children: [
505
+ counts.files,
506
+ " files"
507
+ ] }),
508
+ /* @__PURE__ */ jsxs2("span", { children: [
509
+ Math.max(counts.directories - 1, 0),
510
+ " folders"
511
+ ] })
512
+ ] }),
513
+ emptyState: /* @__PURE__ */ jsx2(
514
+ EmptyState,
515
+ {
516
+ icon: /* @__PURE__ */ jsx2(Search, { className: "h-8 w-8" }),
517
+ title: "No matching files",
518
+ description: "Try a different search or clear the current filter."
519
+ }
520
+ ),
521
+ children: filteredRoot ? /* @__PURE__ */ jsx2("div", { className: "p-3", children: /* @__PURE__ */ jsx2(
522
+ FileTree,
523
+ {
524
+ root: filteredRoot,
525
+ selectedPath,
526
+ onSelect,
527
+ defaultExpanded,
528
+ visibility
529
+ }
530
+ ) }) : null
531
+ }
532
+ );
533
+ }
534
+
535
+ // src/workspace/status-banner.tsx
536
+ import { Loader2, AlertCircle, CheckCircle, Wifi } from "lucide-react";
537
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
538
+ var BANNER_STYLES = {
539
+ provisioning: { bg: "bg-[var(--brand-cool)]/5", border: "border-[var(--brand-cool)]/20", icon: Loader2 },
540
+ connecting: { bg: "bg-[var(--code-number)]/5", border: "border-[var(--code-number)]/20", icon: Wifi },
541
+ error: { bg: "bg-[var(--code-error)]/5", border: "border-[var(--code-error)]/20", icon: AlertCircle },
542
+ success: { bg: "bg-[var(--code-success)]/5", border: "border-[var(--code-success)]/20", icon: CheckCircle },
543
+ info: { bg: "bg-[var(--bg-elevated)]", border: "border-[var(--border-default)]", icon: AlertCircle }
544
+ };
545
+ function StatusBanner({ type, message, detail, onDismiss, className }) {
546
+ const style = BANNER_STYLES[type];
547
+ const Icon = style.icon;
548
+ const isAnimated = type === "provisioning" || type === "connecting";
549
+ return /* @__PURE__ */ jsxs3("div", { className: cn("flex items-center gap-3 px-4 py-2 border-b text-sm", style.bg, style.border, className), children: [
550
+ /* @__PURE__ */ jsx3(Icon, { className: cn("h-4 w-4 shrink-0", isAnimated && "animate-spin") }),
551
+ /* @__PURE__ */ jsx3("span", { className: "text-[var(--text-secondary)]", children: message }),
552
+ detail && /* @__PURE__ */ jsx3("span", { className: "text-[var(--text-muted)] text-xs", children: detail }),
553
+ onDismiss && /* @__PURE__ */ jsx3("button", { onClick: onDismiss, className: "ml-auto text-[var(--text-muted)] hover:text-[var(--text-secondary)] text-xs", children: "Dismiss" })
554
+ ] });
555
+ }
556
+
557
+ // src/workspace/status-bar.tsx
558
+ import { Zap, FileText, X as X2 } from "lucide-react";
559
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
560
+ var STATUS_LABELS = {
561
+ connected: { label: "", color: "bg-[var(--code-success)]" },
562
+ connecting: { label: "Connecting...", color: "bg-[var(--code-number)]" },
563
+ disconnected: { label: "Disconnected", color: "bg-[var(--code-error)]" },
564
+ provisioning: { label: "Provisioning...", color: "bg-[var(--code-number)]" }
565
+ };
566
+ function StatusBar({
567
+ modelLabel,
568
+ onModelClick,
569
+ credits,
570
+ contextBadges = [],
571
+ onRemoveBadge,
572
+ status = "connected",
573
+ className
574
+ }) {
575
+ const statusInfo = STATUS_LABELS[status];
576
+ return /* @__PURE__ */ jsxs4(
577
+ "div",
578
+ {
579
+ className: cn(
580
+ "flex items-center gap-3 px-4 py-1.5 border-t border-[var(--border-subtle)] bg-[var(--bg-dark)] text-xs",
581
+ className
582
+ ),
583
+ children: [
584
+ modelLabel && /* @__PURE__ */ jsxs4(
585
+ "button",
586
+ {
587
+ onClick: onModelClick,
588
+ className: "inline-flex items-center gap-1.5 px-2 py-0.5 rounded-[var(--radius-full)] border border-[var(--border-subtle)] text-[var(--text-muted)] hover:border-[var(--border-accent)] hover:text-[var(--text-secondary)] transition-colors",
589
+ children: [
590
+ /* @__PURE__ */ jsx4("span", { className: cn("w-1.5 h-1.5 rounded-full", statusInfo.color) }),
591
+ modelLabel
592
+ ]
593
+ }
594
+ ),
595
+ statusInfo.label && /* @__PURE__ */ jsx4("span", { className: "text-[var(--text-muted)]", children: statusInfo.label }),
596
+ contextBadges.map((badge) => /* @__PURE__ */ jsxs4(
597
+ "span",
598
+ {
599
+ className: "inline-flex items-center gap-1 px-2 py-0.5 rounded-[var(--radius-full)] border border-[var(--border-accent)] text-[var(--text-secondary)] bg-[var(--border-accent)]/5",
600
+ children: [
601
+ /* @__PURE__ */ jsx4(FileText, { className: "h-3 w-3" }),
602
+ badge.label,
603
+ badge.count !== void 0 && /* @__PURE__ */ jsx4("span", { className: "text-[var(--text-muted)]", children: badge.count }),
604
+ onRemoveBadge && /* @__PURE__ */ jsx4(
605
+ "button",
606
+ {
607
+ onClick: () => onRemoveBadge(badge.id),
608
+ className: "hover:text-[var(--text-primary)] transition-colors",
609
+ children: /* @__PURE__ */ jsx4(X2, { className: "h-2.5 w-2.5" })
610
+ }
611
+ )
612
+ ]
613
+ },
614
+ badge.id
615
+ )),
616
+ /* @__PURE__ */ jsx4("div", { className: "flex-1" }),
617
+ credits !== void 0 && /* @__PURE__ */ jsxs4("span", { className: "inline-flex items-center gap-1 text-[var(--text-muted)]", children: [
618
+ /* @__PURE__ */ jsx4(Zap, { className: "h-3 w-3" }),
619
+ credits.toLocaleString(),
620
+ " credits"
621
+ ] })
622
+ ]
623
+ }
624
+ );
625
+ }
626
+
627
+ // src/workspace/terminal-panel.tsx
628
+ import { useRef as useRef2, useEffect as useEffect2 } from "react";
629
+ import { Terminal as TerminalIcon, ChevronDown, ChevronUp, X as X3 } from "lucide-react";
630
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
631
+ var LINE_COLORS = {
632
+ command: "text-[var(--code-function)]",
633
+ stdout: "text-[var(--text-secondary)]",
634
+ stderr: "text-[var(--code-error)]",
635
+ system: "text-[var(--text-muted)]"
636
+ };
637
+ var LINE_PREFIXES = {
638
+ command: "$ ",
639
+ stdout: "",
640
+ stderr: "",
641
+ system: "# "
642
+ };
643
+ function TerminalPanel({
644
+ lines,
645
+ title = "Terminal",
646
+ isCollapsed = false,
647
+ onToggle,
648
+ onClose,
649
+ maxHeight = 200,
650
+ className
651
+ }) {
652
+ const scrollRef = useRef2(null);
653
+ useEffect2(() => {
654
+ if (!isCollapsed && scrollRef.current) {
655
+ scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
656
+ }
657
+ }, [lines, isCollapsed]);
658
+ return /* @__PURE__ */ jsxs5("div", { className: cn("border-t border-[var(--border-subtle)] bg-[var(--bg-card)]", className), children: [
659
+ /* @__PURE__ */ jsxs5(
660
+ "button",
661
+ {
662
+ onClick: onToggle,
663
+ className: "flex items-center gap-2 w-full px-3 py-1.5 text-xs text-[var(--text-muted)] hover:text-[var(--text-secondary)] transition-colors",
664
+ children: [
665
+ /* @__PURE__ */ jsx5(TerminalIcon, { className: "h-3.5 w-3.5" }),
666
+ /* @__PURE__ */ jsx5("span", { className: "font-medium", children: title }),
667
+ lines.length > 0 && /* @__PURE__ */ jsx5("span", { className: "px-1.5 py-0.5 rounded-[var(--radius-full)] bg-[var(--bg-input)] text-[10px] tabular-nums", children: lines.length }),
668
+ /* @__PURE__ */ jsx5("div", { className: "flex-1" }),
669
+ onClose && /* @__PURE__ */ jsx5(X3, { className: "h-3 w-3 hover:text-[var(--text-primary)]", onClick: (e) => {
670
+ e.stopPropagation();
671
+ onClose();
672
+ } }),
673
+ isCollapsed ? /* @__PURE__ */ jsx5(ChevronUp, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx5(ChevronDown, { className: "h-3 w-3" })
674
+ ]
675
+ }
676
+ ),
677
+ !isCollapsed && /* @__PURE__ */ jsxs5(
678
+ "div",
679
+ {
680
+ ref: scrollRef,
681
+ className: "overflow-auto px-3 pb-2 font-[var(--font-mono)] text-xs leading-[1.6]",
682
+ style: { maxHeight },
683
+ children: [
684
+ lines.map((line) => /* @__PURE__ */ jsxs5("div", { className: cn("whitespace-pre-wrap", LINE_COLORS[line.type]), children: [
685
+ /* @__PURE__ */ jsx5("span", { className: "text-[var(--text-muted)] select-none", children: LINE_PREFIXES[line.type] }),
686
+ line.text
687
+ ] }, line.id)),
688
+ lines.length === 0 && /* @__PURE__ */ jsx5("div", { className: "text-[var(--text-muted)] py-2", children: "No output yet" })
689
+ ]
690
+ }
691
+ )
692
+ ] });
693
+ }
694
+
695
+ // src/workspace/runtime-pane.tsx
696
+ import { Binary, ShieldCheck, TerminalSquare } from "lucide-react";
697
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
698
+ function RuntimePane({
699
+ title = "Runtime",
700
+ subtitle = "Session state, execution output, and inspection surfaces",
701
+ statusBanner,
702
+ statusBar,
703
+ terminal,
704
+ audit,
705
+ inspector,
706
+ footer,
707
+ className
708
+ }) {
709
+ return /* @__PURE__ */ jsxs6(
710
+ "section",
711
+ {
712
+ className: cn(
713
+ "flex h-full min-h-0 flex-col overflow-hidden bg-[var(--bg-dark)] text-[var(--text-primary)]",
714
+ className
715
+ ),
716
+ children: [
717
+ /* @__PURE__ */ jsxs6("header", { className: "border-b border-[var(--border-subtle)] bg-[linear-gradient(180deg,rgba(255,255,255,0.03),transparent)] px-4 py-3", children: [
718
+ /* @__PURE__ */ jsx6("div", { className: "mb-1 text-[10px] font-semibold uppercase tracking-[0.16em] text-[var(--text-muted)]", children: "Sandbox" }),
719
+ /* @__PURE__ */ jsx6("div", { className: "text-sm font-semibold text-[var(--text-primary)]", children: title }),
720
+ /* @__PURE__ */ jsx6("div", { className: "mt-1 text-xs text-[var(--text-muted)]", children: subtitle })
721
+ ] }),
722
+ statusBanner && /* @__PURE__ */ jsx6(StatusBanner, { ...statusBanner }),
723
+ statusBar && /* @__PURE__ */ jsx6(StatusBar, { ...statusBar }),
724
+ /* @__PURE__ */ jsxs6("div", { className: "grid min-h-0 flex-1 gap-px bg-[var(--border-subtle)] lg:grid-cols-[minmax(0,1.35fr)_minmax(20rem,0.9fr)]", children: [
725
+ /* @__PURE__ */ jsx6("div", { className: "min-h-0 overflow-auto bg-[var(--bg-card)]", children: terminal ? /* @__PURE__ */ jsx6(
726
+ TerminalPanel,
727
+ {
728
+ ...terminal,
729
+ className: cn("border-t-0 bg-transparent", terminal.className)
730
+ }
731
+ ) : /* @__PURE__ */ jsx6("div", { className: "flex h-full items-center justify-center p-6 text-sm text-[var(--text-muted)]", children: /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-2", children: [
732
+ /* @__PURE__ */ jsx6(TerminalSquare, { className: "h-4 w-4" }),
733
+ "No terminal activity yet"
734
+ ] }) }) }),
735
+ /* @__PURE__ */ jsxs6("div", { className: "grid min-h-0 gap-px bg-[var(--border-subtle)]", children: [
736
+ /* @__PURE__ */ jsx6("div", { className: "min-h-0 overflow-auto bg-[var(--bg-card)]", children: audit ? audit : /* @__PURE__ */ jsx6("div", { className: "flex h-full items-center justify-center p-6 text-sm text-[var(--text-muted)]", children: /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-2", children: [
737
+ /* @__PURE__ */ jsx6(ShieldCheck, { className: "h-4 w-4" }),
738
+ "No audit data available"
739
+ ] }) }) }),
740
+ /* @__PURE__ */ jsx6("div", { className: "min-h-0 overflow-auto bg-[var(--bg-card)]", children: inspector ? inspector : /* @__PURE__ */ jsx6("div", { className: "flex h-full items-center justify-center p-6 text-sm text-[var(--text-muted)]", children: /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-2", children: [
741
+ /* @__PURE__ */ jsx6(Binary, { className: "h-4 w-4" }),
742
+ "No runtime inspector attached"
743
+ ] }) }) })
744
+ ] })
745
+ ] }),
746
+ footer && /* @__PURE__ */ jsx6("footer", { className: "shrink-0 border-t border-[var(--border-subtle)] bg-[var(--bg-card)] px-3 py-2", children: footer })
747
+ ]
748
+ }
749
+ );
750
+ }
751
+
752
+ // src/workspace/sandbox-workbench.tsx
753
+ import { useEffect as useEffect3, useMemo as useMemo3, useState as useState4 } from "react";
754
+ import {
755
+ Bot,
756
+ Boxes,
757
+ FileCode2,
758
+ FileText as FileText2,
759
+ FolderTree,
760
+ LayoutPanelTop,
761
+ X as X4
762
+ } from "lucide-react";
763
+ import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
764
+ function getArtifactTabIcon(kind) {
765
+ switch (kind) {
766
+ case "file":
767
+ return FileCode2;
768
+ case "markdown":
769
+ return FileText2;
770
+ case "openui":
771
+ return LayoutPanelTop;
772
+ case "custom":
773
+ return Boxes;
774
+ }
775
+ }
776
+ function artifactTabLabel(artifact) {
777
+ if (typeof artifact.title === "string") return artifact.title;
778
+ if (artifact.kind === "file") return artifact.filename;
779
+ return "Artifact";
780
+ }
781
+ function ArtifactTabs({
782
+ artifacts,
783
+ activeArtifactId,
784
+ onSelect,
785
+ onClose
786
+ }) {
787
+ if (artifacts.length === 0) return null;
788
+ return /* @__PURE__ */ jsx7("div", { className: "flex items-center overflow-x-auto border-b border-[var(--border-subtle)] bg-[var(--bg-dark)]", children: artifacts.map((artifact) => {
789
+ const Icon = getArtifactTabIcon(artifact.kind);
790
+ const isActive = artifact.id === activeArtifactId;
791
+ return /* @__PURE__ */ jsxs7(
792
+ "div",
793
+ {
794
+ className: cn(
795
+ "group flex shrink-0 items-center border-r border-[var(--border-subtle)]",
796
+ isActive ? "border-b-2 border-b-[var(--brand-cool)] bg-[var(--bg-card)] text-[var(--text-primary)]" : "text-[var(--text-muted)] hover:bg-[var(--bg-elevated)]"
797
+ ),
798
+ children: [
799
+ /* @__PURE__ */ jsxs7(
800
+ "button",
801
+ {
802
+ type: "button",
803
+ onClick: () => onSelect(artifact.id),
804
+ className: "flex min-w-0 items-center gap-2 px-3 py-2 text-xs transition-colors hover:text-[var(--text-secondary)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-[var(--brand-cool)]/60",
805
+ children: [
806
+ /* @__PURE__ */ jsx7(Icon, { className: "h-3.5 w-3.5 shrink-0" }),
807
+ /* @__PURE__ */ jsx7("span", { className: "max-w-[14rem] truncate", children: artifactTabLabel(artifact) })
808
+ ]
809
+ }
810
+ ),
811
+ onClose && /* @__PURE__ */ jsx7(
812
+ "button",
813
+ {
814
+ type: "button",
815
+ "aria-label": `Close ${artifactTabLabel(artifact)}`,
816
+ onClick: () => onClose(artifact.id),
817
+ className: "mr-1 rounded p-1 opacity-0 transition-opacity hover:bg-[var(--bg-hover)] hover:text-[var(--text-primary)] focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--brand-cool)]/60 group-hover:opacity-100",
818
+ children: /* @__PURE__ */ jsx7(X4, { className: "h-3 w-3" })
819
+ }
820
+ )
821
+ ]
822
+ },
823
+ artifact.id
824
+ );
825
+ }) });
826
+ }
827
+ function renderArtifact(artifact) {
828
+ switch (artifact.kind) {
829
+ case "file":
830
+ return /* @__PURE__ */ jsx7(
831
+ FileArtifactPane,
832
+ {
833
+ path: artifact.path,
834
+ filename: artifact.filename,
835
+ content: artifact.content,
836
+ blobUrl: artifact.blobUrl,
837
+ mimeType: artifact.mimeType,
838
+ onDownload: artifact.onDownload,
839
+ tabs: artifact.tabs,
840
+ activeTabId: artifact.activeTabId,
841
+ onTabSelect: artifact.onTabSelect,
842
+ onTabClose: artifact.onTabClose,
843
+ eyebrow: artifact.eyebrow,
844
+ meta: artifact.meta,
845
+ toolbar: artifact.toolbar,
846
+ footer: artifact.footer
847
+ }
848
+ );
849
+ case "markdown":
850
+ return /* @__PURE__ */ jsx7(
851
+ ArtifactPane,
852
+ {
853
+ eyebrow: artifact.eyebrow ?? "Document",
854
+ title: artifact.title,
855
+ subtitle: artifact.subtitle,
856
+ meta: artifact.meta,
857
+ headerActions: artifact.headerActions,
858
+ toolbar: artifact.toolbar,
859
+ footer: artifact.footer,
860
+ children: /* @__PURE__ */ jsx7("div", { className: "p-5", children: /* @__PURE__ */ jsx7(Markdown, { className: "prose-sm max-w-none", children: artifact.content }) })
861
+ }
862
+ );
863
+ case "openui":
864
+ return /* @__PURE__ */ jsx7(
865
+ ArtifactPane,
866
+ {
867
+ eyebrow: artifact.eyebrow ?? "Structured Artifact",
868
+ title: artifact.title,
869
+ subtitle: artifact.subtitle,
870
+ meta: artifact.meta,
871
+ headerActions: artifact.headerActions,
872
+ toolbar: artifact.toolbar,
873
+ footer: artifact.footer,
874
+ children: /* @__PURE__ */ jsx7(OpenUIArtifactRenderer, { schema: artifact.schema, onAction: artifact.onAction })
875
+ }
876
+ );
877
+ case "custom":
878
+ return /* @__PURE__ */ jsx7(
879
+ ArtifactPane,
880
+ {
881
+ eyebrow: artifact.eyebrow ?? "Artifact",
882
+ title: artifact.title,
883
+ subtitle: artifact.subtitle,
884
+ meta: artifact.meta,
885
+ headerActions: artifact.headerActions,
886
+ toolbar: artifact.toolbar,
887
+ footer: artifact.footer,
888
+ children: artifact.content
889
+ }
890
+ );
891
+ }
892
+ }
893
+ function SandboxWorkbench({
894
+ title = "Sandbox session",
895
+ subtitle,
896
+ status,
897
+ directory,
898
+ session,
899
+ artifacts = [],
900
+ activeArtifactId,
901
+ onArtifactChange,
902
+ onArtifactClose,
903
+ runtime,
904
+ layout,
905
+ emptyArtifactState,
906
+ className
907
+ }) {
908
+ const [uncontrolledArtifactId, setUncontrolledArtifactId] = useState4(
909
+ activeArtifactId ?? artifacts[0]?.id
910
+ );
911
+ useEffect3(() => {
912
+ if (activeArtifactId !== void 0) return;
913
+ setUncontrolledArtifactId((current) => {
914
+ if (artifacts.length === 0) return void 0;
915
+ if (current && artifacts.some((artifact) => artifact.id === current)) return current;
916
+ return artifacts[0]?.id;
917
+ });
918
+ }, [activeArtifactId, artifacts]);
919
+ const resolvedArtifactId = activeArtifactId ?? uncontrolledArtifactId;
920
+ const activeArtifact = useMemo3(
921
+ () => artifacts.find((artifact) => artifact.id === resolvedArtifactId),
922
+ [artifacts, resolvedArtifactId]
923
+ );
924
+ const handleArtifactChange = (artifactId) => {
925
+ if (activeArtifactId === void 0) {
926
+ setUncontrolledArtifactId(artifactId);
927
+ }
928
+ onArtifactChange?.(artifactId);
929
+ };
930
+ const centerHeader = /* @__PURE__ */ jsxs7("div", { className: "flex min-w-0 items-start justify-between gap-4 rounded-[var(--radius-xl)] border border-[var(--border-subtle)] bg-[linear-gradient(135deg,rgba(98,114,243,0.12),rgba(255,255,255,0.02)_38%,transparent_72%)] px-4 py-3 shadow-[var(--shadow-card)]", children: [
931
+ /* @__PURE__ */ jsxs7("div", { className: "min-w-0", children: [
932
+ /* @__PURE__ */ jsx7("div", { className: "text-[10px] font-semibold uppercase tracking-[0.18em] text-[var(--brand-cool)]", children: "Tangle Sandbox" }),
933
+ /* @__PURE__ */ jsx7("div", { className: "truncate text-base font-semibold text-[var(--text-primary)]", children: title }),
934
+ subtitle && /* @__PURE__ */ jsx7("div", { className: "truncate text-sm text-[var(--text-muted)]", children: subtitle })
935
+ ] }),
936
+ /* @__PURE__ */ jsxs7("div", { className: "flex shrink-0 flex-wrap items-center justify-end gap-2", children: [
937
+ status,
938
+ artifacts.length > 0 && /* @__PURE__ */ jsxs7(Badge, { variant: "outline", children: [
939
+ artifacts.length,
940
+ " artifacts"
941
+ ] })
942
+ ] })
943
+ ] });
944
+ const center = /* @__PURE__ */ jsx7(
945
+ ArtifactPane,
946
+ {
947
+ eyebrow: session.eyebrow ?? "Agent Session",
948
+ title: session.title ?? "Execution timeline",
949
+ subtitle: session.subtitle,
950
+ meta: session.meta,
951
+ headerActions: session.headerActions,
952
+ className: "h-full",
953
+ contentClassName: "bg-[radial-gradient(circle_at_top,rgba(82,164,255,0.1),transparent_34%),linear-gradient(180deg,rgba(255,255,255,0.02),transparent_22%)]",
954
+ children: /* @__PURE__ */ jsx7(
955
+ ChatContainer,
956
+ {
957
+ ...session,
958
+ className: "h-full",
959
+ presentation: session.presentation ?? "timeline"
960
+ }
961
+ )
962
+ }
963
+ );
964
+ const right = artifacts.length > 0 ? /* @__PURE__ */ jsxs7("section", { className: "flex h-full min-h-0 flex-col bg-[var(--bg-dark)]", children: [
965
+ /* @__PURE__ */ jsx7(
966
+ ArtifactTabs,
967
+ {
968
+ artifacts,
969
+ activeArtifactId: resolvedArtifactId,
970
+ onSelect: handleArtifactChange,
971
+ onClose: onArtifactClose
972
+ }
973
+ ),
974
+ /* @__PURE__ */ jsx7("div", { className: "min-h-0 flex-1 overflow-auto bg-[linear-gradient(180deg,rgba(255,255,255,0.02),transparent)]", children: activeArtifact ? renderArtifact(activeArtifact) : /* @__PURE__ */ jsx7("div", { className: "flex h-full items-center justify-center p-6", children: emptyArtifactState ?? /* @__PURE__ */ jsx7(
975
+ EmptyState,
976
+ {
977
+ icon: /* @__PURE__ */ jsx7(Boxes, { className: "h-8 w-8" }),
978
+ title: "No artifact selected",
979
+ description: "Select a generated artifact, file preview, or OpenUI panel to inspect it here."
980
+ }
981
+ ) }) })
982
+ ] }) : null;
983
+ return /* @__PURE__ */ jsx7(
984
+ WorkspaceLayout,
985
+ {
986
+ left: directory ? /* @__PURE__ */ jsx7(DirectoryPane, { ...directory, className: "h-full" }) : void 0,
987
+ leftHeader: directory ? /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-2 text-xs font-semibold uppercase tracking-[0.12em] text-[var(--text-muted)]", children: [
988
+ /* @__PURE__ */ jsx7(FolderTree, { className: "h-3.5 w-3.5" }),
989
+ "Directory"
990
+ ] }) : void 0,
991
+ center,
992
+ centerHeader,
993
+ right,
994
+ rightHeader: right ? /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-2 text-xs font-semibold uppercase tracking-[0.12em] text-[var(--text-muted)]", children: [
995
+ /* @__PURE__ */ jsx7(LayoutPanelTop, { className: "h-3.5 w-3.5" }),
996
+ "Artifacts"
997
+ ] }) : void 0,
998
+ bottom: runtime ? /* @__PURE__ */ jsx7(RuntimePane, { ...runtime, className: "h-full" }) : void 0,
999
+ theme: layout?.theme ?? "operator",
1000
+ density: layout?.density ?? "comfortable",
1001
+ persistenceKey: layout?.persistenceKey,
1002
+ defaultLeftOpen: layout?.defaultLeftOpen ?? Boolean(directory),
1003
+ defaultRightOpen: layout?.defaultRightOpen ?? artifacts.length > 0,
1004
+ defaultBottomOpen: layout?.defaultBottomOpen ?? Boolean(runtime),
1005
+ defaultLeftWidth: layout?.defaultLeftWidth,
1006
+ defaultRightWidth: layout?.defaultRightWidth,
1007
+ minLeftWidth: layout?.minLeftWidth,
1008
+ maxLeftWidth: layout?.maxLeftWidth,
1009
+ minRightWidth: layout?.minRightWidth,
1010
+ maxRightWidth: layout?.maxRightWidth,
1011
+ resizable: layout?.resizable,
1012
+ className: cn("p-3 lg:p-4", className)
1013
+ }
1014
+ );
1015
+ }
1016
+ function AgentWorkbench(props) {
1017
+ return /* @__PURE__ */ jsx7(
1018
+ SandboxWorkbench,
1019
+ {
1020
+ ...props,
1021
+ session: {
1022
+ ...props.session,
1023
+ eyebrow: props.session.eyebrow ?? "Agent Session",
1024
+ title: props.session.title ?? /* @__PURE__ */ jsxs7("span", { className: "inline-flex items-center gap-2", children: [
1025
+ /* @__PURE__ */ jsx7(Bot, { className: "h-4 w-4 text-[var(--brand-cool)]" }),
1026
+ "Execution timeline"
1027
+ ] })
1028
+ }
1029
+ }
1030
+ );
1031
+ }
1032
+
1033
+ // src/workspace/audit-results.tsx
1034
+ import { useState as useState5 } from "react";
1035
+ import { CheckCircle as CheckCircle2, XCircle, ChevronRight, Shield } from "lucide-react";
1036
+ import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
1037
+ function AuditResults({ forms, crossFormChecks = [], overallScore, className }) {
1038
+ const totalPassed = forms.reduce((s, f) => s + f.passed, 0);
1039
+ const totalChecks = forms.reduce((s, f) => s + f.passed + f.failed, 0);
1040
+ return /* @__PURE__ */ jsxs8("div", { className: cn("space-y-3 p-3", className), children: [
1041
+ /* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-3 px-3 py-2 rounded-[var(--radius-md)] bg-[var(--bg-input)] border border-[var(--border-subtle)]", children: [
1042
+ /* @__PURE__ */ jsx8(Shield, { className: cn("h-5 w-5", totalChecks === totalPassed ? "text-[var(--code-success)]" : "text-[var(--code-number)]") }),
1043
+ /* @__PURE__ */ jsxs8("div", { children: [
1044
+ /* @__PURE__ */ jsxs8("div", { className: "text-sm font-semibold text-[var(--text-primary)]", children: [
1045
+ totalPassed,
1046
+ "/",
1047
+ totalChecks,
1048
+ " checks passed"
1049
+ ] }),
1050
+ overallScore !== void 0 && /* @__PURE__ */ jsxs8("div", { className: "text-xs text-[var(--text-muted)]", children: [
1051
+ "Score: ",
1052
+ overallScore,
1053
+ "/100"
1054
+ ] })
1055
+ ] })
1056
+ ] }),
1057
+ forms.map((form) => /* @__PURE__ */ jsx8(FormAuditCard, { form }, form.formId)),
1058
+ crossFormChecks.length > 0 && /* @__PURE__ */ jsxs8("div", { children: [
1059
+ /* @__PURE__ */ jsx8("div", { className: "text-xs font-semibold text-[var(--text-muted)] uppercase tracking-wider mb-1", children: "Cross-Form Checks" }),
1060
+ crossFormChecks.map((check, i) => /* @__PURE__ */ jsx8(CheckRow, { check }, i))
1061
+ ] })
1062
+ ] });
1063
+ }
1064
+ function FormAuditCard({ form }) {
1065
+ const [expanded, setExpanded] = useState5(form.failed > 0);
1066
+ const allPassed = form.failed === 0 && form.found;
1067
+ return /* @__PURE__ */ jsxs8("div", { className: "rounded-[var(--radius-md)] border border-[var(--border-subtle)] overflow-hidden", children: [
1068
+ /* @__PURE__ */ jsxs8(
1069
+ "button",
1070
+ {
1071
+ onClick: () => setExpanded(!expanded),
1072
+ className: "flex items-center gap-2 w-full px-3 py-2 text-left hover:bg-[var(--bg-hover)] transition-colors",
1073
+ children: [
1074
+ allPassed ? /* @__PURE__ */ jsx8(CheckCircle2, { className: "h-4 w-4 text-[var(--code-success)] shrink-0" }) : !form.found ? /* @__PURE__ */ jsx8(XCircle, { className: "h-4 w-4 text-[var(--code-error)] shrink-0" }) : /* @__PURE__ */ jsx8(XCircle, { className: "h-4 w-4 text-[var(--code-number)] shrink-0" }),
1075
+ /* @__PURE__ */ jsx8("span", { className: "text-sm font-medium text-[var(--text-primary)] flex-1", children: form.formName || form.formId }),
1076
+ /* @__PURE__ */ jsxs8("span", { className: cn("text-xs tabular-nums", allPassed ? "text-[var(--code-success)]" : "text-[var(--text-muted)]"), children: [
1077
+ form.passed,
1078
+ "/",
1079
+ form.passed + form.failed
1080
+ ] }),
1081
+ /* @__PURE__ */ jsx8(ChevronRight, { className: cn("h-3 w-3 text-[var(--text-muted)] transition-transform", expanded && "rotate-90") })
1082
+ ]
1083
+ }
1084
+ ),
1085
+ expanded && /* @__PURE__ */ jsx8("div", { className: "border-t border-[var(--border-subtle)] px-3 py-1.5 space-y-0.5", children: form.checks.map((check, i) => /* @__PURE__ */ jsx8(CheckRow, { check }, i)) })
1086
+ ] });
1087
+ }
1088
+ function CheckRow({ check }) {
1089
+ return /* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-2 py-1 text-xs", children: [
1090
+ check.passed ? /* @__PURE__ */ jsx8(CheckCircle2, { className: "h-3 w-3 text-[var(--code-success)] shrink-0" }) : /* @__PURE__ */ jsx8(XCircle, { className: "h-3 w-3 text-[var(--code-error)] shrink-0" }),
1091
+ /* @__PURE__ */ jsx8("span", { className: "text-[var(--text-secondary)] flex-1 truncate", children: check.label }),
1092
+ /* @__PURE__ */ jsx8("span", { className: "text-[var(--text-muted)] tabular-nums shrink-0", children: check.passed ? String(check.actual ?? check.expected) : `${check.actual ?? "missing"} \u2260 ${check.expected}` })
1093
+ ] });
1094
+ }
1095
+
1096
+ export {
1097
+ WorkspaceLayout,
1098
+ DirectoryPane,
1099
+ StatusBanner,
1100
+ StatusBar,
1101
+ TerminalPanel,
1102
+ RuntimePane,
1103
+ SandboxWorkbench,
1104
+ AgentWorkbench,
1105
+ AuditResults
1106
+ };