@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
@@ -1,318 +0,0 @@
1
- import {
2
- cn
3
- } from "./chunk-RQHJBTEU.js";
4
-
5
- // src/workspace/workspace-layout.tsx
6
- import { useState } from "react";
7
- import { PanelLeftClose, PanelLeftOpen, PanelRightClose, PanelRightOpen } from "lucide-react";
8
- import { jsx, jsxs } from "react/jsx-runtime";
9
- function WorkspaceLayout({
10
- left,
11
- leftHeader,
12
- center,
13
- centerHeader,
14
- centerFooter,
15
- right,
16
- rightHeader,
17
- bottom,
18
- defaultLeftOpen = true,
19
- defaultRightOpen = false,
20
- defaultBottomOpen = false,
21
- className
22
- }) {
23
- const [leftOpen, setLeftOpen] = useState(defaultLeftOpen);
24
- const [rightOpen, setRightOpen] = useState(defaultRightOpen);
25
- const [bottomOpen, setBottomOpen] = useState(defaultBottomOpen);
26
- return /* @__PURE__ */ jsx("div", { className: cn("flex flex-col h-screen bg-[var(--bg-root)] text-[var(--text-primary)] font-[var(--font-sans)]", className), children: /* @__PURE__ */ jsxs("div", { className: "flex flex-1 min-h-0", children: [
27
- left && leftOpen && /* @__PURE__ */ jsxs("aside", { className: "w-64 shrink-0 border-r border-[var(--border-subtle)] flex flex-col bg-[var(--bg-dark)]", children: [
28
- leftHeader && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-3 py-2 border-b border-[var(--border-subtle)] shrink-0", children: [
29
- leftHeader,
30
- /* @__PURE__ */ jsx(
31
- "button",
32
- {
33
- onClick: () => setLeftOpen(false),
34
- className: "p-1 rounded-[var(--radius-sm)] hover:bg-[var(--bg-hover)] text-[var(--text-muted)]",
35
- children: /* @__PURE__ */ jsx(PanelLeftClose, { className: "h-4 w-4" })
36
- }
37
- )
38
- ] }),
39
- /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-auto py-1", children: left })
40
- ] }),
41
- /* @__PURE__ */ jsxs("main", { className: "flex-1 flex flex-col min-w-0", children: [
42
- (centerHeader || left) && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-3 py-2 border-b border-[var(--border-subtle)] shrink-0 bg-[var(--bg-dark)]", children: [
43
- left && !leftOpen && /* @__PURE__ */ jsx(
44
- "button",
45
- {
46
- onClick: () => setLeftOpen(true),
47
- className: "p-1 rounded-[var(--radius-sm)] hover:bg-[var(--bg-hover)] text-[var(--text-muted)]",
48
- children: /* @__PURE__ */ jsx(PanelLeftOpen, { className: "h-4 w-4" })
49
- }
50
- ),
51
- /* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children: centerHeader }),
52
- right && !rightOpen && /* @__PURE__ */ jsx(
53
- "button",
54
- {
55
- onClick: () => setRightOpen(true),
56
- className: "p-1 rounded-[var(--radius-sm)] hover:bg-[var(--bg-hover)] text-[var(--text-muted)]",
57
- children: /* @__PURE__ */ jsx(PanelRightOpen, { className: "h-4 w-4" })
58
- }
59
- )
60
- ] }),
61
- /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-auto", children: center }),
62
- bottom && bottomOpen && /* @__PURE__ */ jsx("div", { className: "border-t border-[var(--border-subtle)] bg-[var(--bg-card)] max-h-48 overflow-auto", children: bottom }),
63
- centerFooter && /* @__PURE__ */ jsx("div", { className: "border-t border-[var(--border-subtle)] shrink-0 bg-[var(--bg-dark)]", children: centerFooter })
64
- ] }),
65
- right && rightOpen && /* @__PURE__ */ jsxs("aside", { className: "w-[480px] shrink-0 border-l border-[var(--border-subtle)] flex flex-col bg-[var(--bg-dark)]", children: [
66
- rightHeader !== void 0 ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-3 py-2 border-b border-[var(--border-subtle)] shrink-0", children: [
67
- rightHeader,
68
- /* @__PURE__ */ jsx(
69
- "button",
70
- {
71
- onClick: () => setRightOpen(false),
72
- className: "p-1 rounded-[var(--radius-sm)] hover:bg-[var(--bg-hover)] text-[var(--text-muted)]",
73
- children: /* @__PURE__ */ jsx(PanelRightClose, { className: "h-4 w-4" })
74
- }
75
- )
76
- ] }) : /* @__PURE__ */ jsx("div", { className: "flex justify-end px-3 py-2 border-b border-[var(--border-subtle)] shrink-0", children: /* @__PURE__ */ jsx(
77
- "button",
78
- {
79
- onClick: () => setRightOpen(false),
80
- className: "p-1 rounded-[var(--radius-sm)] hover:bg-[var(--bg-hover)] text-[var(--text-muted)]",
81
- children: /* @__PURE__ */ jsx(PanelRightClose, { className: "h-4 w-4" })
82
- }
83
- ) }),
84
- /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-auto", children: right })
85
- ] })
86
- ] }) });
87
- }
88
-
89
- // src/workspace/status-bar.tsx
90
- import { Zap, FileText, X } from "lucide-react";
91
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
92
- var STATUS_LABELS = {
93
- connected: { label: "", color: "bg-[var(--code-success)]" },
94
- connecting: { label: "Connecting...", color: "bg-[var(--code-number)]" },
95
- disconnected: { label: "Disconnected", color: "bg-[var(--code-error)]" },
96
- provisioning: { label: "Provisioning...", color: "bg-[var(--code-number)]" }
97
- };
98
- function StatusBar({
99
- modelLabel,
100
- onModelClick,
101
- credits,
102
- contextBadges = [],
103
- onRemoveBadge,
104
- status = "connected",
105
- className
106
- }) {
107
- const statusInfo = STATUS_LABELS[status];
108
- return /* @__PURE__ */ jsxs2(
109
- "div",
110
- {
111
- className: cn(
112
- "flex items-center gap-3 px-4 py-1.5 border-t border-[var(--border-subtle)] bg-[var(--bg-dark)] text-xs",
113
- className
114
- ),
115
- children: [
116
- modelLabel && /* @__PURE__ */ jsxs2(
117
- "button",
118
- {
119
- onClick: onModelClick,
120
- 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",
121
- children: [
122
- /* @__PURE__ */ jsx2("span", { className: cn("w-1.5 h-1.5 rounded-full", statusInfo.color) }),
123
- modelLabel
124
- ]
125
- }
126
- ),
127
- statusInfo.label && /* @__PURE__ */ jsx2("span", { className: "text-[var(--text-muted)]", children: statusInfo.label }),
128
- contextBadges.map((badge) => /* @__PURE__ */ jsxs2(
129
- "span",
130
- {
131
- 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",
132
- children: [
133
- /* @__PURE__ */ jsx2(FileText, { className: "h-3 w-3" }),
134
- badge.label,
135
- badge.count !== void 0 && /* @__PURE__ */ jsx2("span", { className: "text-[var(--text-muted)]", children: badge.count }),
136
- onRemoveBadge && /* @__PURE__ */ jsx2(
137
- "button",
138
- {
139
- onClick: () => onRemoveBadge(badge.id),
140
- className: "hover:text-[var(--text-primary)] transition-colors",
141
- children: /* @__PURE__ */ jsx2(X, { className: "h-2.5 w-2.5" })
142
- }
143
- )
144
- ]
145
- },
146
- badge.id
147
- )),
148
- /* @__PURE__ */ jsx2("div", { className: "flex-1" }),
149
- credits !== void 0 && /* @__PURE__ */ jsxs2("span", { className: "inline-flex items-center gap-1 text-[var(--text-muted)]", children: [
150
- /* @__PURE__ */ jsx2(Zap, { className: "h-3 w-3" }),
151
- credits.toLocaleString(),
152
- " credits"
153
- ] })
154
- ]
155
- }
156
- );
157
- }
158
-
159
- // src/workspace/status-banner.tsx
160
- import { Loader2, AlertCircle, CheckCircle, Wifi } from "lucide-react";
161
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
162
- var BANNER_STYLES = {
163
- provisioning: { bg: "bg-[var(--brand-cool)]/5", border: "border-[var(--brand-cool)]/20", icon: Loader2 },
164
- connecting: { bg: "bg-[var(--code-number)]/5", border: "border-[var(--code-number)]/20", icon: Wifi },
165
- error: { bg: "bg-[var(--code-error)]/5", border: "border-[var(--code-error)]/20", icon: AlertCircle },
166
- success: { bg: "bg-[var(--code-success)]/5", border: "border-[var(--code-success)]/20", icon: CheckCircle },
167
- info: { bg: "bg-[var(--bg-elevated)]", border: "border-[var(--border-default)]", icon: AlertCircle }
168
- };
169
- function StatusBanner({ type, message, detail, onDismiss, className }) {
170
- const style = BANNER_STYLES[type];
171
- const Icon = style.icon;
172
- const isAnimated = type === "provisioning" || type === "connecting";
173
- 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: [
174
- /* @__PURE__ */ jsx3(Icon, { className: cn("h-4 w-4 shrink-0", isAnimated && "animate-spin") }),
175
- /* @__PURE__ */ jsx3("span", { className: "text-[var(--text-secondary)]", children: message }),
176
- detail && /* @__PURE__ */ jsx3("span", { className: "text-[var(--text-muted)] text-xs", children: detail }),
177
- onDismiss && /* @__PURE__ */ jsx3("button", { onClick: onDismiss, className: "ml-auto text-[var(--text-muted)] hover:text-[var(--text-secondary)] text-xs", children: "Dismiss" })
178
- ] });
179
- }
180
-
181
- // src/workspace/audit-results.tsx
182
- import { useState as useState2 } from "react";
183
- import { CheckCircle as CheckCircle2, XCircle, ChevronRight, Shield } from "lucide-react";
184
- import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
185
- function AuditResults({ forms, crossFormChecks = [], overallScore, className }) {
186
- const totalPassed = forms.reduce((s, f) => s + f.passed, 0);
187
- const totalChecks = forms.reduce((s, f) => s + f.passed + f.failed, 0);
188
- return /* @__PURE__ */ jsxs4("div", { className: cn("space-y-3 p-3", className), children: [
189
- /* @__PURE__ */ jsxs4("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: [
190
- /* @__PURE__ */ jsx4(Shield, { className: cn("h-5 w-5", totalChecks === totalPassed ? "text-[var(--code-success)]" : "text-[var(--code-number)]") }),
191
- /* @__PURE__ */ jsxs4("div", { children: [
192
- /* @__PURE__ */ jsxs4("div", { className: "text-sm font-semibold text-[var(--text-primary)]", children: [
193
- totalPassed,
194
- "/",
195
- totalChecks,
196
- " checks passed"
197
- ] }),
198
- overallScore !== void 0 && /* @__PURE__ */ jsxs4("div", { className: "text-xs text-[var(--text-muted)]", children: [
199
- "Score: ",
200
- overallScore,
201
- "/100"
202
- ] })
203
- ] })
204
- ] }),
205
- forms.map((form) => /* @__PURE__ */ jsx4(FormAuditCard, { form }, form.formId)),
206
- crossFormChecks.length > 0 && /* @__PURE__ */ jsxs4("div", { children: [
207
- /* @__PURE__ */ jsx4("div", { className: "text-xs font-semibold text-[var(--text-muted)] uppercase tracking-wider mb-1", children: "Cross-Form Checks" }),
208
- crossFormChecks.map((check, i) => /* @__PURE__ */ jsx4(CheckRow, { check }, i))
209
- ] })
210
- ] });
211
- }
212
- function FormAuditCard({ form }) {
213
- const [expanded, setExpanded] = useState2(form.failed > 0);
214
- const allPassed = form.failed === 0 && form.found;
215
- return /* @__PURE__ */ jsxs4("div", { className: "rounded-[var(--radius-md)] border border-[var(--border-subtle)] overflow-hidden", children: [
216
- /* @__PURE__ */ jsxs4(
217
- "button",
218
- {
219
- onClick: () => setExpanded(!expanded),
220
- className: "flex items-center gap-2 w-full px-3 py-2 text-left hover:bg-[var(--bg-hover)] transition-colors",
221
- children: [
222
- allPassed ? /* @__PURE__ */ jsx4(CheckCircle2, { className: "h-4 w-4 text-[var(--code-success)] shrink-0" }) : !form.found ? /* @__PURE__ */ jsx4(XCircle, { className: "h-4 w-4 text-[var(--code-error)] shrink-0" }) : /* @__PURE__ */ jsx4(XCircle, { className: "h-4 w-4 text-[var(--code-number)] shrink-0" }),
223
- /* @__PURE__ */ jsx4("span", { className: "text-sm font-medium text-[var(--text-primary)] flex-1", children: form.formName || form.formId }),
224
- /* @__PURE__ */ jsxs4("span", { className: cn("text-xs tabular-nums", allPassed ? "text-[var(--code-success)]" : "text-[var(--text-muted)]"), children: [
225
- form.passed,
226
- "/",
227
- form.passed + form.failed
228
- ] }),
229
- /* @__PURE__ */ jsx4(ChevronRight, { className: cn("h-3 w-3 text-[var(--text-muted)] transition-transform", expanded && "rotate-90") })
230
- ]
231
- }
232
- ),
233
- expanded && /* @__PURE__ */ jsx4("div", { className: "border-t border-[var(--border-subtle)] px-3 py-1.5 space-y-0.5", children: form.checks.map((check, i) => /* @__PURE__ */ jsx4(CheckRow, { check }, i)) })
234
- ] });
235
- }
236
- function CheckRow({ check }) {
237
- return /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2 py-1 text-xs", children: [
238
- check.passed ? /* @__PURE__ */ jsx4(CheckCircle2, { className: "h-3 w-3 text-[var(--code-success)] shrink-0" }) : /* @__PURE__ */ jsx4(XCircle, { className: "h-3 w-3 text-[var(--code-error)] shrink-0" }),
239
- /* @__PURE__ */ jsx4("span", { className: "text-[var(--text-secondary)] flex-1 truncate", children: check.label }),
240
- /* @__PURE__ */ jsx4("span", { className: "text-[var(--text-muted)] tabular-nums shrink-0", children: check.passed ? String(check.actual ?? check.expected) : `${check.actual ?? "missing"} \u2260 ${check.expected}` })
241
- ] });
242
- }
243
-
244
- // src/workspace/terminal-panel.tsx
245
- import { useRef, useEffect } from "react";
246
- import { Terminal as TerminalIcon, ChevronDown, ChevronUp, X as X2 } from "lucide-react";
247
- import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
248
- var LINE_COLORS = {
249
- command: "text-[var(--code-function)]",
250
- stdout: "text-[var(--text-secondary)]",
251
- stderr: "text-[var(--code-error)]",
252
- system: "text-[var(--text-muted)]"
253
- };
254
- var LINE_PREFIXES = {
255
- command: "$ ",
256
- stdout: "",
257
- stderr: "",
258
- system: "# "
259
- };
260
- function TerminalPanel({
261
- lines,
262
- title = "Terminal",
263
- isCollapsed = false,
264
- onToggle,
265
- onClose,
266
- maxHeight = 200,
267
- className
268
- }) {
269
- const scrollRef = useRef(null);
270
- useEffect(() => {
271
- if (!isCollapsed && scrollRef.current) {
272
- scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
273
- }
274
- }, [lines, isCollapsed]);
275
- return /* @__PURE__ */ jsxs5("div", { className: cn("border-t border-[var(--border-subtle)] bg-[var(--bg-card)]", className), children: [
276
- /* @__PURE__ */ jsxs5(
277
- "button",
278
- {
279
- onClick: onToggle,
280
- 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",
281
- children: [
282
- /* @__PURE__ */ jsx5(TerminalIcon, { className: "h-3.5 w-3.5" }),
283
- /* @__PURE__ */ jsx5("span", { className: "font-medium", children: title }),
284
- 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 }),
285
- /* @__PURE__ */ jsx5("div", { className: "flex-1" }),
286
- onClose && /* @__PURE__ */ jsx5(X2, { className: "h-3 w-3 hover:text-[var(--text-primary)]", onClick: (e) => {
287
- e.stopPropagation();
288
- onClose();
289
- } }),
290
- isCollapsed ? /* @__PURE__ */ jsx5(ChevronUp, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx5(ChevronDown, { className: "h-3 w-3" })
291
- ]
292
- }
293
- ),
294
- !isCollapsed && /* @__PURE__ */ jsxs5(
295
- "div",
296
- {
297
- ref: scrollRef,
298
- className: "overflow-auto px-3 pb-2 font-[var(--font-mono)] text-xs leading-[1.6]",
299
- style: { maxHeight },
300
- children: [
301
- lines.map((line) => /* @__PURE__ */ jsxs5("div", { className: cn("whitespace-pre-wrap", LINE_COLORS[line.type]), children: [
302
- /* @__PURE__ */ jsx5("span", { className: "text-[var(--text-muted)] select-none", children: LINE_PREFIXES[line.type] }),
303
- line.text
304
- ] }, line.id)),
305
- lines.length === 0 && /* @__PURE__ */ jsx5("div", { className: "text-[var(--text-muted)] py-2", children: "No output yet" })
306
- ]
307
- }
308
- )
309
- ] });
310
- }
311
-
312
- export {
313
- WorkspaceLayout,
314
- StatusBar,
315
- StatusBanner,
316
- AuditResults,
317
- TerminalPanel
318
- };
@@ -1,334 +0,0 @@
1
- import {
2
- cn
3
- } from "./chunk-RQHJBTEU.js";
4
-
5
- // src/files/file-tree.tsx
6
- import { useState } from "react";
7
- import {
8
- File,
9
- FileText,
10
- FileCode,
11
- FileSpreadsheet,
12
- FileImage,
13
- Folder,
14
- FolderOpen,
15
- ChevronRight,
16
- FileJson
17
- } from "lucide-react";
18
- import { jsx, jsxs } from "react/jsx-runtime";
19
- var FILE_ICONS = {
20
- pdf: FileText,
21
- csv: FileSpreadsheet,
22
- xlsx: FileSpreadsheet,
23
- xls: FileSpreadsheet,
24
- py: FileCode,
25
- ts: FileCode,
26
- js: FileCode,
27
- json: FileJson,
28
- yaml: FileCode,
29
- yml: FileCode,
30
- md: FileText,
31
- txt: FileText,
32
- png: FileImage,
33
- jpg: FileImage,
34
- jpeg: FileImage,
35
- gif: FileImage,
36
- svg: FileImage
37
- };
38
- function getFileIcon(name) {
39
- const ext = name.split(".").pop()?.toLowerCase() || "";
40
- return FILE_ICONS[ext] || File;
41
- }
42
- function getFileColor(name) {
43
- const ext = name.split(".").pop()?.toLowerCase() || "";
44
- switch (ext) {
45
- case "pdf":
46
- return "text-red-400";
47
- case "py":
48
- return "text-yellow-400";
49
- case "ts":
50
- case "js":
51
- return "text-blue-400";
52
- case "json":
53
- return "text-green-400";
54
- case "yaml":
55
- case "yml":
56
- return "text-purple-400";
57
- case "csv":
58
- case "xlsx":
59
- return "text-emerald-400";
60
- case "md":
61
- return "text-[var(--text-secondary)]";
62
- default:
63
- return "text-[var(--text-muted)]";
64
- }
65
- }
66
- function TreeNode({ node, depth, selectedPath, onSelect, defaultExpanded }) {
67
- const [expanded, setExpanded] = useState(defaultExpanded);
68
- const isSelected = node.path === selectedPath;
69
- const isDir = node.type === "directory";
70
- const handleClick = () => {
71
- if (isDir) {
72
- setExpanded(!expanded);
73
- }
74
- onSelect?.(node.path, node);
75
- };
76
- const Icon = isDir ? expanded ? FolderOpen : Folder : getFileIcon(node.name);
77
- const iconColor = isDir ? "text-[var(--brand-cool)]" : getFileColor(node.name);
78
- return /* @__PURE__ */ jsxs("div", { children: [
79
- /* @__PURE__ */ jsxs(
80
- "button",
81
- {
82
- onClick: handleClick,
83
- className: cn(
84
- "flex items-center gap-1.5 w-full text-left px-2 py-1 rounded-[var(--radius-sm)] text-sm transition-colors",
85
- "hover:bg-[var(--bg-hover)]",
86
- isSelected && "bg-[var(--brand-cool)]/10 text-[var(--text-primary)]",
87
- !isSelected && "text-[var(--text-secondary)]"
88
- ),
89
- style: { paddingLeft: `${depth * 16 + 8}px` },
90
- children: [
91
- isDir && /* @__PURE__ */ jsx(
92
- ChevronRight,
93
- {
94
- className: cn(
95
- "h-3 w-3 shrink-0 text-[var(--text-muted)] transition-transform",
96
- expanded && "rotate-90"
97
- )
98
- }
99
- ),
100
- !isDir && /* @__PURE__ */ jsx("span", { className: "w-3" }),
101
- /* @__PURE__ */ jsx(Icon, { className: cn("h-4 w-4 shrink-0", iconColor) }),
102
- /* @__PURE__ */ jsx("span", { className: "truncate", children: node.name }),
103
- node.size !== void 0 && !isDir && /* @__PURE__ */ jsx("span", { className: "text-[var(--text-muted)] text-xs ml-auto tabular-nums", children: formatSize(node.size) })
104
- ]
105
- }
106
- ),
107
- isDir && expanded && node.children && /* @__PURE__ */ jsx("div", { children: node.children.sort((a, b) => {
108
- if (a.type !== b.type) return a.type === "directory" ? -1 : 1;
109
- return a.name.localeCompare(b.name);
110
- }).map((child) => /* @__PURE__ */ jsx(
111
- TreeNode,
112
- {
113
- node: child,
114
- depth: depth + 1,
115
- selectedPath,
116
- onSelect,
117
- defaultExpanded
118
- },
119
- child.path
120
- )) })
121
- ] });
122
- }
123
- function formatSize(bytes) {
124
- if (bytes < 1024) return `${bytes}B`;
125
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(0)}K`;
126
- return `${(bytes / (1024 * 1024)).toFixed(1)}M`;
127
- }
128
- function FileTree({ root, selectedPath, onSelect, className, defaultExpanded = true }) {
129
- return /* @__PURE__ */ jsx("div", { className: cn("text-sm font-[var(--font-sans)]", className), children: root.children ? root.children.sort((a, b) => {
130
- if (a.type !== b.type) return a.type === "directory" ? -1 : 1;
131
- return a.name.localeCompare(b.name);
132
- }).map((child) => /* @__PURE__ */ jsx(
133
- TreeNode,
134
- {
135
- node: child,
136
- depth: 0,
137
- selectedPath,
138
- onSelect,
139
- defaultExpanded
140
- },
141
- child.path
142
- )) : /* @__PURE__ */ jsx(
143
- TreeNode,
144
- {
145
- node: root,
146
- depth: 0,
147
- selectedPath,
148
- onSelect,
149
- defaultExpanded
150
- }
151
- ) });
152
- }
153
-
154
- // src/files/file-preview.tsx
155
- import {
156
- Download,
157
- X,
158
- FileText as FileText2
159
- } from "lucide-react";
160
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
161
- function getPreviewType(filename, mimeType) {
162
- const ext = filename.split(".").pop()?.toLowerCase() || "";
163
- if (mimeType?.startsWith("application/pdf") || ext === "pdf") return "pdf";
164
- if (mimeType?.startsWith("image/") || ["png", "jpg", "jpeg", "gif", "svg", "webp"].includes(ext)) return "image";
165
- if (["csv"].includes(ext)) return "csv";
166
- if (["xlsx", "xls"].includes(ext)) return "spreadsheet";
167
- if (["py", "ts", "js", "tsx", "jsx", "sh", "bash"].includes(ext)) return "code";
168
- if (["json"].includes(ext)) return "json";
169
- if (["yaml", "yml"].includes(ext)) return "yaml";
170
- if (["md", "markdown"].includes(ext)) return "markdown";
171
- if (["txt", "log", "text"].includes(ext)) return "text";
172
- return "unknown";
173
- }
174
- function CodePreview({ content, filename }) {
175
- const lines = content.split("\n");
176
- return /* @__PURE__ */ jsxs2("div", { className: "relative bg-[var(--bg-input)] rounded-[var(--radius-md)] border border-[var(--border-subtle)] overflow-hidden", children: [
177
- /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2 px-4 py-2.5 border-b border-[var(--border-subtle)]", children: [
178
- /* @__PURE__ */ jsxs2("div", { className: "flex gap-1.5", children: [
179
- /* @__PURE__ */ jsx2("div", { className: "w-3 h-3 rounded-full bg-[#FF5F57]" }),
180
- /* @__PURE__ */ jsx2("div", { className: "w-3 h-3 rounded-full bg-[#FEBC2E]" }),
181
- /* @__PURE__ */ jsx2("div", { className: "w-3 h-3 rounded-full bg-[#8E59FF]" })
182
- ] }),
183
- /* @__PURE__ */ jsx2("span", { className: "text-xs text-[var(--text-muted)] font-[var(--font-mono)] ml-2", children: filename })
184
- ] }),
185
- /* @__PURE__ */ jsx2("div", { className: "overflow-auto max-h-[70vh]", children: /* @__PURE__ */ jsx2("table", { className: "w-full", children: /* @__PURE__ */ jsx2("tbody", { children: lines.map((line, i) => /* @__PURE__ */ jsxs2("tr", { className: "hover:bg-[var(--bg-hover)]/50", children: [
186
- /* @__PURE__ */ jsx2("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 }),
187
- /* @__PURE__ */ jsx2("td", { className: "pr-4 py-0 font-[var(--font-mono)] text-[13px] text-[var(--text-secondary)] leading-[1.55] whitespace-pre", children: line || " " })
188
- ] }, i)) }) }) })
189
- ] });
190
- }
191
- function CsvPreview({ content }) {
192
- const lines = content.trim().split("\n");
193
- if (lines.length === 0) return null;
194
- const headers = lines[0].split(",").map((h) => h.trim().replace(/^"|"$/g, ""));
195
- const rows = lines.slice(1).map(
196
- (line) => line.split(",").map((cell) => cell.trim().replace(/^"|"$/g, ""))
197
- );
198
- return /* @__PURE__ */ jsx2("div", { className: "overflow-auto max-h-[70vh] rounded-[var(--radius-md)] border border-[var(--border-subtle)]", children: /* @__PURE__ */ jsxs2("table", { className: "w-full text-sm", children: [
199
- /* @__PURE__ */ jsx2("thead", { children: /* @__PURE__ */ jsx2("tr", { className: "bg-[var(--bg-elevated)] sticky top-0", children: headers.map((h, i) => /* @__PURE__ */ jsx2(
200
- "th",
201
- {
202
- className: "px-3 py-2 text-left text-xs font-semibold text-[var(--text-secondary)] border-b border-[var(--border-subtle)] whitespace-nowrap",
203
- children: h
204
- },
205
- i
206
- )) }) }),
207
- /* @__PURE__ */ jsx2("tbody", { children: rows.map((row, i) => /* @__PURE__ */ jsx2("tr", { className: "border-b border-[var(--border-subtle)] hover:bg-[var(--bg-hover)]/50", children: row.map((cell, j) => /* @__PURE__ */ jsx2(
208
- "td",
209
- {
210
- className: "px-3 py-1.5 text-[var(--text-secondary)] font-[var(--font-mono)] text-xs whitespace-nowrap",
211
- children: cell
212
- },
213
- j
214
- )) }, i)) })
215
- ] }) });
216
- }
217
- function ImagePreview({ src, filename }) {
218
- return /* @__PURE__ */ jsx2("div", { className: "flex items-center justify-center p-4 bg-[var(--bg-input)] rounded-[var(--radius-md)] border border-[var(--border-subtle)]", children: /* @__PURE__ */ jsx2("img", { src, alt: filename, className: "max-w-full max-h-[70vh] object-contain rounded" }) });
219
- }
220
- function PdfPreview({ blobUrl, filename }) {
221
- return /* @__PURE__ */ jsx2("div", { className: "rounded-[var(--radius-md)] border border-[var(--border-subtle)] overflow-hidden bg-[var(--bg-input)]", children: /* @__PURE__ */ jsx2(
222
- "iframe",
223
- {
224
- src: blobUrl,
225
- title: filename,
226
- className: "w-full h-[70vh] border-0"
227
- }
228
- ) });
229
- }
230
- function TextPreview({ content }) {
231
- return /* @__PURE__ */ jsx2("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 });
232
- }
233
- function EmptyPreview({ filename }) {
234
- return /* @__PURE__ */ jsxs2("div", { className: "flex flex-col items-center justify-center py-16 text-[var(--text-muted)]", children: [
235
- /* @__PURE__ */ jsx2(FileText2, { className: "h-12 w-12 mb-3 opacity-30" }),
236
- /* @__PURE__ */ jsxs2("p", { className: "text-sm", children: [
237
- "Cannot preview ",
238
- filename
239
- ] }),
240
- /* @__PURE__ */ jsx2("p", { className: "text-xs mt-1", children: "Download to view this file" })
241
- ] });
242
- }
243
- function FilePreview({
244
- filename,
245
- content,
246
- blobUrl,
247
- mimeType,
248
- onClose,
249
- onDownload,
250
- className
251
- }) {
252
- const previewType = getPreviewType(filename, mimeType);
253
- return /* @__PURE__ */ jsxs2("div", { className: cn("flex flex-col h-full", className), children: [
254
- /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2 px-3 py-2 border-b border-[var(--border-subtle)] shrink-0", children: [
255
- /* @__PURE__ */ jsx2("span", { className: "text-sm font-medium text-[var(--text-primary)] truncate flex-1", children: filename }),
256
- onDownload && /* @__PURE__ */ jsx2(
257
- "button",
258
- {
259
- onClick: onDownload,
260
- className: "p-1.5 rounded-[var(--radius-sm)] hover:bg-[var(--bg-hover)] text-[var(--text-muted)] hover:text-[var(--text-secondary)] transition-colors",
261
- children: /* @__PURE__ */ jsx2(Download, { className: "h-4 w-4" })
262
- }
263
- ),
264
- onClose && /* @__PURE__ */ jsx2(
265
- "button",
266
- {
267
- onClick: onClose,
268
- className: "p-1.5 rounded-[var(--radius-sm)] hover:bg-[var(--bg-hover)] text-[var(--text-muted)] hover:text-[var(--text-secondary)] transition-colors",
269
- children: /* @__PURE__ */ jsx2(X, { className: "h-4 w-4" })
270
- }
271
- )
272
- ] }),
273
- /* @__PURE__ */ jsxs2("div", { className: "flex-1 overflow-auto p-3", children: [
274
- previewType === "pdf" && blobUrl && /* @__PURE__ */ jsx2(PdfPreview, { blobUrl, filename }),
275
- previewType === "image" && (blobUrl || content) && /* @__PURE__ */ jsx2(ImagePreview, { src: blobUrl || `data:image/*;base64,${content}`, filename }),
276
- previewType === "csv" && content && /* @__PURE__ */ jsx2(CsvPreview, { content }),
277
- (previewType === "code" || previewType === "json" || previewType === "yaml") && content && /* @__PURE__ */ jsx2(CodePreview, { content, filename }),
278
- previewType === "text" && content && /* @__PURE__ */ jsx2(TextPreview, { content }),
279
- previewType === "markdown" && content && /* @__PURE__ */ jsx2(TextPreview, { content }),
280
- previewType === "unknown" && /* @__PURE__ */ jsx2(EmptyPreview, { filename }),
281
- !content && !blobUrl && /* @__PURE__ */ jsx2(EmptyPreview, { filename })
282
- ] })
283
- ] });
284
- }
285
-
286
- // src/files/file-tabs.tsx
287
- import { X as X2, FileText as FileText3, FileCode as FileCode2, FileSpreadsheet as FileSpreadsheet2 } from "lucide-react";
288
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
289
- function getTabIcon(name) {
290
- const ext = name.split(".").pop()?.toLowerCase() || "";
291
- if (["pdf"].includes(ext)) return FileText3;
292
- if (["csv", "xlsx"].includes(ext)) return FileSpreadsheet2;
293
- return FileCode2;
294
- }
295
- function FileTabs({ tabs, activeId, onSelect, onClose, className }) {
296
- if (tabs.length === 0) return null;
297
- return /* @__PURE__ */ jsx3("div", { className: cn("flex items-center border-b border-[var(--border-subtle)] bg-[var(--bg-dark)] overflow-x-auto", className), children: tabs.map((tab) => {
298
- const isActive = tab.id === activeId;
299
- const Icon = getTabIcon(tab.name);
300
- return /* @__PURE__ */ jsxs3(
301
- "button",
302
- {
303
- onClick: () => onSelect(tab.id),
304
- className: cn(
305
- "group flex items-center gap-1.5 px-3 py-1.5 text-xs border-r border-[var(--border-subtle)] shrink-0 transition-colors",
306
- isActive ? "bg-[var(--bg-card)] text-[var(--text-primary)] border-b-2 border-b-[var(--brand-cool)]" : "text-[var(--text-muted)] hover:text-[var(--text-secondary)] hover:bg-[var(--bg-elevated)]"
307
- ),
308
- children: [
309
- /* @__PURE__ */ jsx3(Icon, { className: "h-3 w-3 shrink-0" }),
310
- /* @__PURE__ */ jsx3("span", { className: "truncate max-w-[120px]", children: tab.name }),
311
- tab.dirty && /* @__PURE__ */ jsx3("span", { className: "w-1.5 h-1.5 rounded-full bg-[var(--brand-cool)]" }),
312
- /* @__PURE__ */ jsx3(
313
- "span",
314
- {
315
- onClick: (e) => {
316
- e.stopPropagation();
317
- onClose(tab.id);
318
- },
319
- className: "p-0.5 rounded hover:bg-[var(--bg-hover)] opacity-0 group-hover:opacity-100 transition-opacity",
320
- children: /* @__PURE__ */ jsx3(X2, { className: "h-2.5 w-2.5" })
321
- }
322
- )
323
- ]
324
- },
325
- tab.id
326
- );
327
- }) });
328
- }
329
-
330
- export {
331
- FileTree,
332
- FilePreview,
333
- FileTabs
334
- };