@tangle-network/sandbox-ui 0.2.1 → 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 (68) 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.d.ts +8 -1
  45. package/dist/primitives.js +25 -19
  46. package/dist/run.d.ts +2 -2
  47. package/dist/run.js +8 -7
  48. package/dist/{use-sidecar-auth-Bb0-w3lX.d.ts → sdk-hooks.d.ts} +61 -72
  49. package/dist/sdk-hooks.js +29 -0
  50. package/dist/styles.css +179 -0
  51. package/dist/tokens.css +165 -0
  52. package/dist/{tool-display-BvsVW_Ur.d.ts → tool-display-Ct9nFAzJ.d.ts} +1 -1
  53. package/dist/types.d.ts +1 -1
  54. package/dist/{usage-chart-DINgSVL5.d.ts → usage-chart-CY9xo3KX.d.ts} +8 -3
  55. package/dist/use-pty-session-DeZSxOCN.d.ts +69 -0
  56. package/dist/utils.d.ts +1 -1
  57. package/dist/utils.js +1 -1
  58. package/dist/workspace.d.ts +171 -33
  59. package/dist/workspace.js +25 -1
  60. package/package.json +10 -3
  61. package/dist/chunk-2UHPE5T7.js +0 -201
  62. package/dist/chunk-6MQIDUPA.js +0 -502
  63. package/dist/chunk-KYY2X6LY.js +0 -318
  64. package/dist/chunk-L6ZDH5F4.js +0 -334
  65. package/dist/chunk-M34OA6PQ.js +0 -233
  66. package/dist/chunk-M6VLC32S.js +0 -219
  67. package/dist/chunk-U62G5TS7.js +0 -472
  68. package/src/styles/tokens.css +0 -73
@@ -0,0 +1,610 @@
1
+ import {
2
+ Markdown
3
+ } from "./chunk-LTFK464G.js";
4
+ import {
5
+ cn
6
+ } from "./chunk-RQHJBTEU.js";
7
+
8
+ // src/workspace/artifact-pane.tsx
9
+ import { jsx, jsxs } from "react/jsx-runtime";
10
+ function ArtifactPane({
11
+ eyebrow,
12
+ title,
13
+ subtitle,
14
+ meta,
15
+ headerActions,
16
+ tabs,
17
+ toolbar,
18
+ footer,
19
+ emptyState,
20
+ children,
21
+ className,
22
+ contentClassName
23
+ }) {
24
+ const hasContent = children !== void 0 && children !== null;
25
+ return /* @__PURE__ */ jsxs(
26
+ "section",
27
+ {
28
+ className: cn(
29
+ "flex h-full min-h-0 flex-col overflow-hidden rounded-[var(--radius-xl)] bg-[linear-gradient(180deg,rgba(255,255,255,0.02),transparent_18%),var(--bg-dark)] text-[var(--text-primary)]",
30
+ className
31
+ ),
32
+ children: [
33
+ /* @__PURE__ */ jsxs("header", { className: "border-b border-[var(--border-subtle)] bg-[radial-gradient(circle_at_top_left,rgba(98,114,243,0.12),transparent_34%),linear-gradient(180deg,rgba(255,255,255,0.04),transparent)]", children: [
34
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3 px-4 py-3", children: [
35
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
36
+ eyebrow && /* @__PURE__ */ jsx("div", { className: "mb-1 inline-flex max-w-full items-center rounded-full border border-[var(--border-accent)] bg-[var(--brand-cool)]/10 px-2.5 py-1 text-[10px] font-semibold uppercase tracking-[0.16em] text-[var(--brand-cool)]", children: eyebrow }),
37
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 text-[15px] font-semibold text-[var(--text-primary)]", children: title }),
38
+ (subtitle || meta) && /* @__PURE__ */ jsxs("div", { className: "mt-1 flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1 text-xs leading-relaxed text-[var(--text-muted)]", children: [
39
+ subtitle && /* @__PURE__ */ jsx("span", { className: "truncate", children: subtitle }),
40
+ meta && /* @__PURE__ */ jsx("span", { className: "flex items-center gap-2", children: meta })
41
+ ] })
42
+ ] }),
43
+ headerActions && /* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center gap-1.5", children: headerActions })
44
+ ] }),
45
+ tabs,
46
+ toolbar && /* @__PURE__ */ jsx("div", { className: "border-t border-[var(--border-subtle)] px-3 py-2", children: toolbar })
47
+ ] }),
48
+ /* @__PURE__ */ jsx("div", { className: cn("min-h-0 flex-1 overflow-auto", contentClassName), children: hasContent ? children : emptyState ? /* @__PURE__ */ jsx("div", { className: "flex h-full items-center justify-center p-6", children: emptyState }) : null }),
49
+ footer && /* @__PURE__ */ jsx("footer", { className: "shrink-0 border-t border-[var(--border-subtle)] bg-[var(--bg-card)] px-3 py-2", children: footer })
50
+ ]
51
+ }
52
+ );
53
+ }
54
+
55
+ // src/files/file-tree.tsx
56
+ import { useState } from "react";
57
+ import {
58
+ File,
59
+ FileText,
60
+ FileCode,
61
+ FileSpreadsheet,
62
+ FileImage,
63
+ Folder,
64
+ FolderOpen,
65
+ ChevronRight,
66
+ FileJson
67
+ } from "lucide-react";
68
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
69
+ function isNodeVisible(node, visibility) {
70
+ if (!visibility) return true;
71
+ if (visibility.hiddenPaths?.includes(node.path)) {
72
+ return false;
73
+ }
74
+ if (visibility.hiddenPathPrefixes?.some(
75
+ (prefix) => node.path === prefix || node.path.startsWith(`${prefix}/`)
76
+ )) {
77
+ return false;
78
+ }
79
+ return visibility.isVisible ? visibility.isVisible(node) : true;
80
+ }
81
+ function filterFileTree(root, visibility) {
82
+ if (!isNodeVisible(root, visibility)) {
83
+ return null;
84
+ }
85
+ if (root.type === "file") {
86
+ return root;
87
+ }
88
+ const children = (root.children ?? []).map((child) => filterFileTree(child, visibility)).filter((child) => child !== null);
89
+ if (root.children && root.children.length > 0 && children.length === 0) {
90
+ return null;
91
+ }
92
+ return {
93
+ ...root,
94
+ children
95
+ };
96
+ }
97
+ var FILE_ICONS = {
98
+ pdf: FileText,
99
+ csv: FileSpreadsheet,
100
+ xlsx: FileSpreadsheet,
101
+ xls: FileSpreadsheet,
102
+ py: FileCode,
103
+ ts: FileCode,
104
+ js: FileCode,
105
+ json: FileJson,
106
+ yaml: FileCode,
107
+ yml: FileCode,
108
+ md: FileText,
109
+ txt: FileText,
110
+ png: FileImage,
111
+ jpg: FileImage,
112
+ jpeg: FileImage,
113
+ gif: FileImage,
114
+ svg: FileImage
115
+ };
116
+ function getFileIcon(name) {
117
+ const ext = name.split(".").pop()?.toLowerCase() || "";
118
+ return FILE_ICONS[ext] || File;
119
+ }
120
+ function getFileColor(name) {
121
+ const ext = name.split(".").pop()?.toLowerCase() || "";
122
+ switch (ext) {
123
+ case "pdf":
124
+ return "text-red-400";
125
+ case "py":
126
+ return "text-yellow-400";
127
+ case "ts":
128
+ case "js":
129
+ return "text-blue-400";
130
+ case "json":
131
+ return "text-green-400";
132
+ case "yaml":
133
+ case "yml":
134
+ return "text-[var(--accent-text)]";
135
+ case "csv":
136
+ case "xlsx":
137
+ return "text-emerald-400";
138
+ case "md":
139
+ return "text-[var(--text-secondary)]";
140
+ default:
141
+ return "text-[var(--text-muted)]";
142
+ }
143
+ }
144
+ function TreeNode({ node, depth, selectedPath, onSelect, defaultExpanded }) {
145
+ const [expanded, setExpanded] = useState(defaultExpanded);
146
+ const isSelected = node.path === selectedPath;
147
+ const isDir = node.type === "directory";
148
+ const handleClick = () => {
149
+ if (isDir) {
150
+ setExpanded(!expanded);
151
+ }
152
+ onSelect?.(node.path, node);
153
+ };
154
+ const Icon = isDir ? expanded ? FolderOpen : Folder : getFileIcon(node.name);
155
+ const iconColor = isDir ? "text-[var(--brand-cool)]" : getFileColor(node.name);
156
+ return /* @__PURE__ */ jsxs2("div", { children: [
157
+ /* @__PURE__ */ jsxs2(
158
+ "button",
159
+ {
160
+ onClick: handleClick,
161
+ className: cn(
162
+ "flex items-center gap-1.5 w-full text-left px-2 py-1 rounded-[var(--radius-sm)] text-sm transition-colors",
163
+ "hover:bg-[var(--bg-hover)]",
164
+ isSelected && "bg-[var(--brand-cool)]/10 text-[var(--text-primary)]",
165
+ !isSelected && "text-[var(--text-secondary)]"
166
+ ),
167
+ style: { paddingLeft: `${depth * 16 + 8}px` },
168
+ children: [
169
+ isDir && /* @__PURE__ */ jsx2(
170
+ ChevronRight,
171
+ {
172
+ className: cn(
173
+ "h-3 w-3 shrink-0 text-[var(--text-muted)] transition-transform",
174
+ expanded && "rotate-90"
175
+ )
176
+ }
177
+ ),
178
+ !isDir && /* @__PURE__ */ jsx2("span", { className: "w-3" }),
179
+ /* @__PURE__ */ jsx2(Icon, { className: cn("h-4 w-4 shrink-0", iconColor) }),
180
+ /* @__PURE__ */ jsx2("span", { className: "truncate", children: node.name }),
181
+ node.size !== void 0 && !isDir && /* @__PURE__ */ jsx2("span", { className: "text-[var(--text-muted)] text-xs ml-auto tabular-nums", children: formatSize(node.size) })
182
+ ]
183
+ }
184
+ ),
185
+ isDir && expanded && node.children && /* @__PURE__ */ jsx2("div", { children: node.children.sort((a, b) => {
186
+ if (a.type !== b.type) return a.type === "directory" ? -1 : 1;
187
+ return a.name.localeCompare(b.name);
188
+ }).map((child) => /* @__PURE__ */ jsx2(
189
+ TreeNode,
190
+ {
191
+ node: child,
192
+ depth: depth + 1,
193
+ selectedPath,
194
+ onSelect,
195
+ defaultExpanded
196
+ },
197
+ child.path
198
+ )) })
199
+ ] });
200
+ }
201
+ function formatSize(bytes) {
202
+ if (bytes < 1024) return `${bytes}B`;
203
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(0)}K`;
204
+ return `${(bytes / (1024 * 1024)).toFixed(1)}M`;
205
+ }
206
+ function FileTree({
207
+ root,
208
+ selectedPath,
209
+ onSelect,
210
+ className,
211
+ defaultExpanded = true,
212
+ visibility
213
+ }) {
214
+ const visibleRoot = filterFileTree(root, visibility);
215
+ if (!visibleRoot) {
216
+ return null;
217
+ }
218
+ return /* @__PURE__ */ jsx2("div", { className: cn("text-sm font-[var(--font-sans)]", className), children: visibleRoot.children ? visibleRoot.children.sort((a, b) => {
219
+ if (a.type !== b.type) return a.type === "directory" ? -1 : 1;
220
+ return a.name.localeCompare(b.name);
221
+ }).map((child) => /* @__PURE__ */ jsx2(
222
+ TreeNode,
223
+ {
224
+ node: child,
225
+ depth: 0,
226
+ selectedPath,
227
+ onSelect,
228
+ defaultExpanded
229
+ },
230
+ child.path
231
+ )) : /* @__PURE__ */ jsx2(
232
+ TreeNode,
233
+ {
234
+ node: visibleRoot,
235
+ depth: 0,
236
+ selectedPath,
237
+ onSelect,
238
+ defaultExpanded
239
+ }
240
+ ) });
241
+ }
242
+
243
+ // src/files/file-preview.tsx
244
+ import {
245
+ Download,
246
+ X,
247
+ FileText as FileText2
248
+ } from "lucide-react";
249
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
250
+ function getPreviewType(filename, mimeType) {
251
+ const ext = filename.split(".").pop()?.toLowerCase() || "";
252
+ if (mimeType?.startsWith("application/pdf") || ext === "pdf") return "pdf";
253
+ if (mimeType?.startsWith("image/") || ["png", "jpg", "jpeg", "gif", "svg", "webp"].includes(ext)) return "image";
254
+ if (["csv"].includes(ext)) return "csv";
255
+ if (["xlsx", "xls"].includes(ext)) return "spreadsheet";
256
+ if (["py", "ts", "js", "tsx", "jsx", "sh", "bash"].includes(ext)) return "code";
257
+ if (["json"].includes(ext)) return "json";
258
+ if (["yaml", "yml"].includes(ext)) return "yaml";
259
+ if (["md", "markdown"].includes(ext)) return "markdown";
260
+ if (["txt", "log", "text"].includes(ext)) return "text";
261
+ return "unknown";
262
+ }
263
+ function getPreviewLabel(previewType) {
264
+ switch (previewType) {
265
+ case "pdf":
266
+ return "PDF";
267
+ case "image":
268
+ return "Image";
269
+ case "csv":
270
+ return "CSV";
271
+ case "spreadsheet":
272
+ return "Spreadsheet";
273
+ case "code":
274
+ return "Code";
275
+ case "json":
276
+ return "JSON";
277
+ case "yaml":
278
+ return "YAML";
279
+ case "markdown":
280
+ return "Markdown";
281
+ case "text":
282
+ return "Text";
283
+ default:
284
+ return "File";
285
+ }
286
+ }
287
+ function CodePreview({ content, filename }) {
288
+ const lines = content.split("\n");
289
+ const language = filename.split(".").pop()?.toUpperCase() || "TXT";
290
+ return /* @__PURE__ */ jsxs3("div", { className: "relative bg-[var(--bg-input)] rounded-[var(--radius-md)] border border-[var(--border-subtle)] overflow-hidden", children: [
291
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center justify-between gap-3 border-b border-[var(--border-subtle)] px-4 py-2.5", children: [
292
+ /* @__PURE__ */ jsxs3("div", { className: "flex gap-1.5", children: [
293
+ /* @__PURE__ */ jsx3("div", { className: "w-3 h-3 rounded-full bg-[#FF5F57]" }),
294
+ /* @__PURE__ */ jsx3("div", { className: "w-3 h-3 rounded-full bg-[#FEBC2E]" }),
295
+ /* @__PURE__ */ jsx3("div", { className: "w-3 h-3 rounded-full bg-[#8E59FF]" })
296
+ ] }),
297
+ /* @__PURE__ */ jsx3("div", { className: "ml-2 min-w-0 flex-1 truncate text-xs font-[var(--font-mono)] text-[var(--text-muted)]", children: filename }),
298
+ /* @__PURE__ */ jsxs3("div", { className: "inline-flex items-center gap-2 rounded-[var(--radius-full)] border border-[var(--border-subtle)] bg-[var(--bg-card)] px-2.5 py-1 text-[10px] font-semibold uppercase tracking-[0.12em] text-[var(--text-muted)]", children: [
299
+ /* @__PURE__ */ jsx3("span", { children: language }),
300
+ /* @__PURE__ */ jsx3("span", { className: "h-1 w-1 rounded-full bg-[var(--border-hover)]" }),
301
+ /* @__PURE__ */ jsxs3("span", { children: [
302
+ lines.length,
303
+ " lines"
304
+ ] })
305
+ ] })
306
+ ] }),
307
+ /* @__PURE__ */ jsx3("div", { className: "overflow-auto max-h-[70vh]", children: /* @__PURE__ */ jsx3("table", { className: "w-full", children: /* @__PURE__ */ jsx3("tbody", { children: lines.map((line, i) => /* @__PURE__ */ jsxs3("tr", { className: "hover:bg-[var(--bg-hover)]/50", children: [
308
+ /* @__PURE__ */ jsx3("td", { className: "text-right pr-4 pl-4 py-0 select-none text-[var(--text-muted)] text-xs font-[var(--font-mono)] w-10 align-top leading-[1.55]", children: i + 1 }),
309
+ /* @__PURE__ */ jsx3("td", { className: "pr-4 py-0 font-[var(--font-mono)] text-[13px] text-[var(--text-secondary)] leading-[1.55] whitespace-pre", children: line || " " })
310
+ ] }, i)) }) }) })
311
+ ] });
312
+ }
313
+ function parseCsvRow(line) {
314
+ const cells = [];
315
+ let current = "";
316
+ let inQuotes = false;
317
+ for (let index = 0; index < line.length; index += 1) {
318
+ const char = line[index];
319
+ const next = line[index + 1];
320
+ if (char === '"') {
321
+ if (inQuotes && next === '"') {
322
+ current += '"';
323
+ index += 1;
324
+ continue;
325
+ }
326
+ inQuotes = !inQuotes;
327
+ continue;
328
+ }
329
+ if (char === "," && !inQuotes) {
330
+ cells.push(current.trim());
331
+ current = "";
332
+ continue;
333
+ }
334
+ current += char;
335
+ }
336
+ cells.push(current.trim());
337
+ return cells;
338
+ }
339
+ function CsvPreview({ content }) {
340
+ const lines = content.trim().split(/\r?\n/).filter(Boolean);
341
+ if (lines.length === 0) return null;
342
+ const headers = parseCsvRow(lines[0]).map((header) => header.replace(/^"|"$/g, ""));
343
+ const rows = lines.slice(1).map(
344
+ (line) => parseCsvRow(line).map((cell) => cell.replace(/^"|"$/g, ""))
345
+ );
346
+ return /* @__PURE__ */ jsx3("div", { className: "overflow-auto max-h-[70vh] rounded-[var(--radius-md)] border border-[var(--border-subtle)]", children: /* @__PURE__ */ jsxs3("table", { className: "w-full text-sm", children: [
347
+ /* @__PURE__ */ jsx3("thead", { children: /* @__PURE__ */ jsx3("tr", { className: "bg-[var(--bg-elevated)] sticky top-0", children: headers.map((h, i) => /* @__PURE__ */ jsx3(
348
+ "th",
349
+ {
350
+ className: "px-3 py-2 text-left text-xs font-semibold text-[var(--text-secondary)] border-b border-[var(--border-subtle)] whitespace-nowrap",
351
+ children: h
352
+ },
353
+ i
354
+ )) }) }),
355
+ /* @__PURE__ */ jsx3("tbody", { children: rows.map((row, i) => /* @__PURE__ */ jsx3("tr", { className: "border-b border-[var(--border-subtle)] hover:bg-[var(--bg-hover)]/50", children: row.map((cell, j) => /* @__PURE__ */ jsx3(
356
+ "td",
357
+ {
358
+ className: "px-3 py-1.5 text-[var(--text-secondary)] font-[var(--font-mono)] text-xs whitespace-nowrap",
359
+ children: cell
360
+ },
361
+ j
362
+ )) }, i)) })
363
+ ] }) });
364
+ }
365
+ function ImagePreview({ src, filename }) {
366
+ return /* @__PURE__ */ jsx3("div", { className: "flex items-center justify-center p-4 bg-[var(--bg-input)] rounded-[var(--radius-md)] border border-[var(--border-subtle)]", children: /* @__PURE__ */ jsx3("img", { src, alt: filename, className: "max-w-full max-h-[70vh] object-contain rounded" }) });
367
+ }
368
+ function PdfPreview({ blobUrl, filename }) {
369
+ return /* @__PURE__ */ jsx3("div", { className: "rounded-[var(--radius-md)] border border-[var(--border-subtle)] overflow-hidden bg-[var(--bg-input)]", children: /* @__PURE__ */ jsx3(
370
+ "iframe",
371
+ {
372
+ src: blobUrl,
373
+ title: filename,
374
+ className: "w-full h-[70vh] border-0"
375
+ }
376
+ ) });
377
+ }
378
+ function TextPreview({ content }) {
379
+ return /* @__PURE__ */ jsx3("pre", { className: "bg-[var(--bg-input)] rounded-[var(--radius-md)] border border-[var(--border-subtle)] p-4 overflow-auto max-h-[70vh] text-sm text-[var(--text-secondary)] font-[var(--font-mono)] leading-[1.55]", children: content });
380
+ }
381
+ function UnsupportedPreview({
382
+ filename,
383
+ title,
384
+ description
385
+ }) {
386
+ return /* @__PURE__ */ jsxs3("div", { className: "flex flex-col items-center justify-center rounded-[var(--radius-md)] border border-dashed border-[var(--border-subtle)] bg-[var(--bg-input)] px-6 py-16 text-center text-[var(--text-muted)]", children: [
387
+ /* @__PURE__ */ jsx3(FileText2, { className: "mb-3 h-12 w-12 opacity-30" }),
388
+ /* @__PURE__ */ jsx3("p", { className: "text-sm text-[var(--text-secondary)]", children: title }),
389
+ /* @__PURE__ */ jsx3("p", { className: "mt-1 max-w-md text-xs", children: description }),
390
+ /* @__PURE__ */ jsx3("p", { className: "mt-4 text-[11px] uppercase tracking-[0.12em]", children: filename })
391
+ ] });
392
+ }
393
+ function MarkdownPreview({ content }) {
394
+ return /* @__PURE__ */ jsx3("div", { className: "rounded-[var(--radius-md)] border border-[var(--border-subtle)] bg-[var(--bg-input)] p-5", children: /* @__PURE__ */ jsx3(Markdown, { className: "prose-sm max-w-none", children: content }) });
395
+ }
396
+ function EmptyPreview({ filename }) {
397
+ return /* @__PURE__ */ jsxs3("div", { className: "flex flex-col items-center justify-center py-16 text-[var(--text-muted)]", children: [
398
+ /* @__PURE__ */ jsx3(FileText2, { className: "h-12 w-12 mb-3 opacity-30" }),
399
+ /* @__PURE__ */ jsxs3("p", { className: "text-sm", children: [
400
+ "Cannot preview ",
401
+ filename
402
+ ] }),
403
+ /* @__PURE__ */ jsx3("p", { className: "text-xs mt-1", children: "Download to view this file" })
404
+ ] });
405
+ }
406
+ function FilePreview({
407
+ filename,
408
+ content,
409
+ blobUrl,
410
+ mimeType,
411
+ onClose,
412
+ onDownload,
413
+ hideHeader = false,
414
+ className
415
+ }) {
416
+ const previewType = getPreviewType(filename, mimeType);
417
+ const previewLabel = getPreviewLabel(previewType);
418
+ const hasRenderableSource = Boolean(content) || Boolean(blobUrl) || previewType === "unknown" || previewType === "spreadsheet";
419
+ return /* @__PURE__ */ jsxs3("div", { className: cn("flex flex-col h-full", className), children: [
420
+ !hideHeader && /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2 px-3 py-2 border-b border-[var(--border-subtle)] shrink-0", children: [
421
+ /* @__PURE__ */ jsxs3("div", { className: "min-w-0 flex-1", children: [
422
+ /* @__PURE__ */ jsx3("div", { className: "truncate text-sm font-medium text-[var(--text-primary)]", children: filename }),
423
+ /* @__PURE__ */ jsx3("div", { className: "mt-0.5 text-[11px] uppercase tracking-[0.12em] text-[var(--text-muted)]", children: previewLabel })
424
+ ] }),
425
+ onDownload && /* @__PURE__ */ jsx3(
426
+ "button",
427
+ {
428
+ type: "button",
429
+ onClick: onDownload,
430
+ "aria-label": `Download ${filename}`,
431
+ className: "p-1.5 rounded-[var(--radius-sm)] hover:bg-[var(--bg-hover)] text-[var(--text-muted)] hover:text-[var(--text-secondary)] transition-colors",
432
+ children: /* @__PURE__ */ jsx3(Download, { className: "h-4 w-4" })
433
+ }
434
+ ),
435
+ onClose && /* @__PURE__ */ jsx3(
436
+ "button",
437
+ {
438
+ type: "button",
439
+ onClick: onClose,
440
+ "aria-label": `Close ${filename}`,
441
+ className: "p-1.5 rounded-[var(--radius-sm)] hover:bg-[var(--bg-hover)] text-[var(--text-muted)] hover:text-[var(--text-secondary)] transition-colors",
442
+ children: /* @__PURE__ */ jsx3(X, { className: "h-4 w-4" })
443
+ }
444
+ )
445
+ ] }),
446
+ /* @__PURE__ */ jsxs3("div", { className: "flex-1 overflow-auto p-3", children: [
447
+ previewType === "pdf" && blobUrl && /* @__PURE__ */ jsx3(PdfPreview, { blobUrl, filename }),
448
+ previewType === "image" && blobUrl && /* @__PURE__ */ jsx3(ImagePreview, { src: blobUrl, filename }),
449
+ previewType === "csv" && content && /* @__PURE__ */ jsx3(CsvPreview, { content }),
450
+ (previewType === "code" || previewType === "json" || previewType === "yaml") && content && /* @__PURE__ */ jsx3(CodePreview, { content, filename }),
451
+ previewType === "text" && content && /* @__PURE__ */ jsx3(TextPreview, { content }),
452
+ previewType === "markdown" && content && /* @__PURE__ */ jsx3(MarkdownPreview, { content }),
453
+ previewType === "spreadsheet" && /* @__PURE__ */ jsx3(
454
+ UnsupportedPreview,
455
+ {
456
+ filename,
457
+ title: "Spreadsheet preview is not available in this surface",
458
+ description: "Download the workbook or convert it to CSV when you need an inline preview."
459
+ }
460
+ ),
461
+ previewType === "unknown" && /* @__PURE__ */ jsx3(EmptyPreview, { filename }),
462
+ !hasRenderableSource && /* @__PURE__ */ jsx3(
463
+ UnsupportedPreview,
464
+ {
465
+ filename,
466
+ title: "Preview data is not available yet",
467
+ description: "This artifact can be shown once the app provides inline content or a downloadable blob."
468
+ }
469
+ )
470
+ ] })
471
+ ] });
472
+ }
473
+
474
+ // src/files/file-tabs.tsx
475
+ import { X as X2, FileText as FileText3, FileCode as FileCode2, FileSpreadsheet as FileSpreadsheet2 } from "lucide-react";
476
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
477
+ function getTabIcon(name) {
478
+ const ext = name.split(".").pop()?.toLowerCase() || "";
479
+ if (["pdf"].includes(ext)) return FileText3;
480
+ if (["csv", "xlsx"].includes(ext)) return FileSpreadsheet2;
481
+ return FileCode2;
482
+ }
483
+ function FileTabs({ tabs, activeId, onSelect, onClose, className }) {
484
+ if (tabs.length === 0) return null;
485
+ return /* @__PURE__ */ jsx4("div", { className: cn("flex items-center border-b border-[var(--border-subtle)] bg-[var(--bg-dark)] overflow-x-auto", className), children: tabs.map((tab) => {
486
+ const isActive = tab.id === activeId;
487
+ const Icon = getTabIcon(tab.name);
488
+ return /* @__PURE__ */ jsxs4(
489
+ "div",
490
+ {
491
+ className: cn(
492
+ "group flex items-center border-r border-[var(--border-subtle)] shrink-0",
493
+ isActive ? "bg-[var(--bg-card)] text-[var(--text-primary)] border-b-2 border-b-[var(--brand-cool)]" : "text-[var(--text-muted)] hover:bg-[var(--bg-elevated)]"
494
+ ),
495
+ children: [
496
+ /* @__PURE__ */ jsxs4(
497
+ "button",
498
+ {
499
+ type: "button",
500
+ onClick: () => onSelect(tab.id),
501
+ className: "flex min-w-0 items-center gap-1.5 px-3 py-1.5 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",
502
+ children: [
503
+ /* @__PURE__ */ jsx4(Icon, { className: "h-3 w-3 shrink-0" }),
504
+ /* @__PURE__ */ jsx4("span", { className: "max-w-[120px] truncate", children: tab.name }),
505
+ tab.dirty && /* @__PURE__ */ jsx4("span", { className: "h-1.5 w-1.5 rounded-full bg-[var(--brand-cool)]" })
506
+ ]
507
+ }
508
+ ),
509
+ /* @__PURE__ */ jsx4(
510
+ "button",
511
+ {
512
+ type: "button",
513
+ "aria-label": `Close ${tab.name}`,
514
+ onClick: () => onClose(tab.id),
515
+ className: "mr-1 rounded p-0.5 opacity-0 transition-opacity hover:bg-[var(--bg-hover)] focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--brand-cool)]/60 group-hover:opacity-100",
516
+ children: /* @__PURE__ */ jsx4(X2, { className: "h-2.5 w-2.5" })
517
+ }
518
+ )
519
+ ]
520
+ },
521
+ tab.id
522
+ );
523
+ }) });
524
+ }
525
+
526
+ // src/files/file-artifact-pane.tsx
527
+ import { Download as Download2, X as X3 } from "lucide-react";
528
+ import { Fragment, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
529
+ function FileArtifactPane({
530
+ filename,
531
+ content,
532
+ blobUrl,
533
+ mimeType,
534
+ onClose,
535
+ onDownload,
536
+ path,
537
+ tabs = [],
538
+ activeTabId,
539
+ onTabSelect,
540
+ onTabClose,
541
+ eyebrow = "Artifact",
542
+ meta,
543
+ toolbar,
544
+ footer,
545
+ className
546
+ }) {
547
+ const showTabs = tabs.length > 0 && onTabSelect && onTabClose;
548
+ return /* @__PURE__ */ jsx5(
549
+ ArtifactPane,
550
+ {
551
+ eyebrow,
552
+ title: filename,
553
+ subtitle: path,
554
+ meta,
555
+ toolbar,
556
+ footer,
557
+ className,
558
+ tabs: showTabs ? /* @__PURE__ */ jsx5(
559
+ FileTabs,
560
+ {
561
+ tabs,
562
+ activeId: activeTabId,
563
+ onSelect: onTabSelect,
564
+ onClose: onTabClose
565
+ }
566
+ ) : void 0,
567
+ headerActions: /* @__PURE__ */ jsxs5(Fragment, { children: [
568
+ onDownload && /* @__PURE__ */ jsx5(
569
+ "button",
570
+ {
571
+ type: "button",
572
+ "aria-label": `Download ${filename}`,
573
+ onClick: onDownload,
574
+ 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",
575
+ children: /* @__PURE__ */ jsx5(Download2, { className: "h-4 w-4" })
576
+ }
577
+ ),
578
+ onClose && /* @__PURE__ */ jsx5(
579
+ "button",
580
+ {
581
+ type: "button",
582
+ "aria-label": `Close ${filename}`,
583
+ onClick: onClose,
584
+ 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",
585
+ children: /* @__PURE__ */ jsx5(X3, { className: "h-4 w-4" })
586
+ }
587
+ )
588
+ ] }),
589
+ children: /* @__PURE__ */ jsx5(
590
+ FilePreview,
591
+ {
592
+ filename,
593
+ content,
594
+ blobUrl,
595
+ mimeType,
596
+ hideHeader: true
597
+ }
598
+ )
599
+ }
600
+ );
601
+ }
602
+
603
+ export {
604
+ ArtifactPane,
605
+ filterFileTree,
606
+ FileTree,
607
+ FilePreview,
608
+ FileTabs,
609
+ FileArtifactPane
610
+ };