@tangle-network/ui 1.0.0

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 (220) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/LICENSE +21 -0
  3. package/README.md +33 -0
  4. package/dist/active-sessions-store-CeOmXgv5.d.ts +85 -0
  5. package/dist/artifact-pane-DvJyPWV4.d.ts +24 -0
  6. package/dist/auth.d.ts +74 -0
  7. package/dist/auth.js +15 -0
  8. package/dist/button-CMQuQEW_.d.ts +17 -0
  9. package/dist/chat.d.ts +232 -0
  10. package/dist/chat.js +30 -0
  11. package/dist/chunk-2NFQRQOD.js +1009 -0
  12. package/dist/chunk-2VH6PUXD.js +186 -0
  13. package/dist/chunk-34A66VBG.js +214 -0
  14. package/dist/chunk-3OI2QKFD.js +0 -0
  15. package/dist/chunk-4CLN43XT.js +45 -0
  16. package/dist/chunk-54SQQMMM.js +156 -0
  17. package/dist/chunk-5Z5ZYMOJ.js +0 -0
  18. package/dist/chunk-66BNMOVT.js +167 -0
  19. package/dist/chunk-6BGQA4BQ.js +0 -0
  20. package/dist/chunk-7UO2ZMRQ.js +133 -0
  21. package/dist/chunk-BX6AQMUS.js +183 -0
  22. package/dist/chunk-CD53GZOM.js +59 -0
  23. package/dist/chunk-CSAIKY36.js +54 -0
  24. package/dist/chunk-EEE55AVS.js +1201 -0
  25. package/dist/chunk-GYPQXTJU.js +230 -0
  26. package/dist/chunk-HFL6R6IF.js +37 -0
  27. package/dist/chunk-HJKCSXCH.js +737 -0
  28. package/dist/chunk-LISXUB4D.js +1222 -0
  29. package/dist/chunk-LQS34IGP.js +0 -0
  30. package/dist/chunk-MKTSMWVD.js +109 -0
  31. package/dist/chunk-NKDZ7GZE.js +192 -0
  32. package/dist/chunk-OEX7NZE3.js +321 -0
  33. package/dist/chunk-Q56BYXQF.js +61 -0
  34. package/dist/chunk-Q7EIIWTC.js +0 -0
  35. package/dist/chunk-REJESC5U.js +117 -0
  36. package/dist/chunk-RQGKSCEZ.js +0 -0
  37. package/dist/chunk-RQHJBTEU.js +10 -0
  38. package/dist/chunk-TMFOPHHN.js +299 -0
  39. package/dist/chunk-XGKULLYE.js +40 -0
  40. package/dist/chunk-XIHMJ7ZQ.js +614 -0
  41. package/dist/chunk-YJ2G3XO5.js +1048 -0
  42. package/dist/chunk-YNN4O57I.js +754 -0
  43. package/dist/code-block-DjXf8eOG.d.ts +19 -0
  44. package/dist/document-editor-pane-A5LT5H4N.js +12 -0
  45. package/dist/document-editor-pane-DyDEX_Zm.d.ts +124 -0
  46. package/dist/editor.d.ts +120 -0
  47. package/dist/editor.js +34 -0
  48. package/dist/files.d.ts +175 -0
  49. package/dist/files.js +20 -0
  50. package/dist/hooks.d.ts +56 -0
  51. package/dist/hooks.js +41 -0
  52. package/dist/index.d.ts +43 -0
  53. package/dist/index.js +446 -0
  54. package/dist/markdown.d.ts +15 -0
  55. package/dist/markdown.js +14 -0
  56. package/dist/message-BHWbxBtT.d.ts +15 -0
  57. package/dist/openui.d.ts +115 -0
  58. package/dist/openui.js +12 -0
  59. package/dist/parts-dj7AcUg0.d.ts +36 -0
  60. package/dist/primitives.d.ts +332 -0
  61. package/dist/primitives.js +191 -0
  62. package/dist/run-PfLmDAox.d.ts +41 -0
  63. package/dist/run.d.ts +69 -0
  64. package/dist/run.js +36 -0
  65. package/dist/sdk-hooks.d.ts +285 -0
  66. package/dist/sdk-hooks.js +31 -0
  67. package/dist/stores.d.ts +17 -0
  68. package/dist/stores.js +76 -0
  69. package/dist/tool-call-feed-Bs3MyQMT.d.ts +68 -0
  70. package/dist/tool-display-z4JcDmMQ.d.ts +32 -0
  71. package/dist/tool-previews.d.ts +48 -0
  72. package/dist/tool-previews.js +21 -0
  73. package/dist/types.d.ts +19 -0
  74. package/dist/types.js +1 -0
  75. package/dist/utils.d.ts +45 -0
  76. package/dist/utils.js +32 -0
  77. package/package.json +193 -0
  78. package/src/auth/auth.tsx +228 -0
  79. package/src/auth/index.ts +13 -0
  80. package/src/auth/login-layout.tsx +46 -0
  81. package/src/chat/agent-timeline.stories.tsx +429 -0
  82. package/src/chat/agent-timeline.tsx +360 -0
  83. package/src/chat/chat-container.tsx +486 -0
  84. package/src/chat/chat-input.stories.tsx +142 -0
  85. package/src/chat/chat-input.tsx +389 -0
  86. package/src/chat/chat-message.stories.tsx +237 -0
  87. package/src/chat/chat-message.tsx +129 -0
  88. package/src/chat/index.ts +18 -0
  89. package/src/chat/message-list.stories.tsx +336 -0
  90. package/src/chat/message-list.tsx +79 -0
  91. package/src/chat/thinking-indicator.stories.tsx +56 -0
  92. package/src/chat/thinking-indicator.tsx +30 -0
  93. package/src/chat/user-message.stories.tsx +92 -0
  94. package/src/chat/user-message.tsx +43 -0
  95. package/src/editor/document-editor-pane.tsx +351 -0
  96. package/src/editor/editor-provider.tsx +428 -0
  97. package/src/editor/editor-toolbar.tsx +130 -0
  98. package/src/editor/index.ts +31 -0
  99. package/src/editor/markdown-conversion.ts +21 -0
  100. package/src/editor/markdown-document-editor.tsx +137 -0
  101. package/src/editor/tiptap-editor.tsx +331 -0
  102. package/src/editor/use-editor.ts +221 -0
  103. package/src/files/file-artifact-pane.tsx +183 -0
  104. package/src/files/file-preview.tsx +342 -0
  105. package/src/files/file-tabs.tsx +71 -0
  106. package/src/files/file-tree.tsx +258 -0
  107. package/src/files/index.ts +17 -0
  108. package/src/files/rich-file-tree.stories.tsx +104 -0
  109. package/src/files/rich-file-tree.test.tsx +42 -0
  110. package/src/files/rich-file-tree.tsx +232 -0
  111. package/src/hooks/index.ts +10 -0
  112. package/src/hooks/use-auth.ts +153 -0
  113. package/src/hooks/use-auto-scroll.ts +59 -0
  114. package/src/hooks/use-dropdown-menu.ts +40 -0
  115. package/src/hooks/use-live-time.test.tsx +40 -0
  116. package/src/hooks/use-live-time.ts +27 -0
  117. package/src/hooks/use-realtime-session.ts +319 -0
  118. package/src/hooks/use-run-collapse-state.ts +25 -0
  119. package/src/hooks/use-run-groups.ts +111 -0
  120. package/src/hooks/use-sdk-session.ts +575 -0
  121. package/src/hooks/use-sse-stream.ts +475 -0
  122. package/src/hooks/use-tool-call-stream.ts +96 -0
  123. package/src/index.ts +14 -0
  124. package/src/lib/utils.ts +6 -0
  125. package/src/markdown/code-block.tsx +198 -0
  126. package/src/markdown/index.ts +2 -0
  127. package/src/markdown/markdown.stories.tsx +190 -0
  128. package/src/markdown/markdown.tsx +62 -0
  129. package/src/openui/index.ts +20 -0
  130. package/src/openui/openui-artifact-renderer.tsx +542 -0
  131. package/src/primitives/artifact-pane.tsx +91 -0
  132. package/src/primitives/avatar.stories.tsx +95 -0
  133. package/src/primitives/avatar.tsx +47 -0
  134. package/src/primitives/badge.stories.tsx +57 -0
  135. package/src/primitives/badge.tsx +97 -0
  136. package/src/primitives/button.stories.tsx +48 -0
  137. package/src/primitives/button.tsx +115 -0
  138. package/src/primitives/card.stories.tsx +53 -0
  139. package/src/primitives/card.tsx +98 -0
  140. package/src/primitives/code-block.stories.tsx +115 -0
  141. package/src/primitives/code-block.tsx +22 -0
  142. package/src/primitives/design-tokens.stories.tsx +162 -0
  143. package/src/primitives/dialog.stories.tsx +176 -0
  144. package/src/primitives/dialog.tsx +137 -0
  145. package/src/primitives/drop-zone.stories.tsx +123 -0
  146. package/src/primitives/drop-zone.tsx +131 -0
  147. package/src/primitives/dropdown-menu.stories.tsx +122 -0
  148. package/src/primitives/dropdown-menu.tsx +214 -0
  149. package/src/primitives/empty-state.stories.tsx +81 -0
  150. package/src/primitives/empty-state.tsx +40 -0
  151. package/src/primitives/index.ts +118 -0
  152. package/src/primitives/input.stories.tsx +113 -0
  153. package/src/primitives/input.tsx +136 -0
  154. package/src/primitives/label.stories.tsx +84 -0
  155. package/src/primitives/label.tsx +24 -0
  156. package/src/primitives/progress.stories.tsx +93 -0
  157. package/src/primitives/progress.tsx +50 -0
  158. package/src/primitives/segmented-control.test.tsx +328 -0
  159. package/src/primitives/segmented-control.tsx +154 -0
  160. package/src/primitives/select.stories.tsx +164 -0
  161. package/src/primitives/select.tsx +158 -0
  162. package/src/primitives/sidebar-drop-zone.stories.tsx +100 -0
  163. package/src/primitives/sidebar-drop-zone.tsx +149 -0
  164. package/src/primitives/skeleton.stories.tsx +79 -0
  165. package/src/primitives/skeleton.tsx +55 -0
  166. package/src/primitives/stat-card.stories.tsx +137 -0
  167. package/src/primitives/stat-card.tsx +97 -0
  168. package/src/primitives/switch.stories.tsx +85 -0
  169. package/src/primitives/switch.tsx +28 -0
  170. package/src/primitives/table.stories.tsx +170 -0
  171. package/src/primitives/table.tsx +116 -0
  172. package/src/primitives/tabs.stories.tsx +180 -0
  173. package/src/primitives/tabs.tsx +71 -0
  174. package/src/primitives/terminal-display.stories.tsx +191 -0
  175. package/src/primitives/terminal-display.tsx +189 -0
  176. package/src/primitives/theme-toggle.stories.tsx +32 -0
  177. package/src/primitives/theme-toggle.tsx +96 -0
  178. package/src/primitives/toast.stories.tsx +155 -0
  179. package/src/primitives/toast.tsx +190 -0
  180. package/src/primitives/upload-progress.stories.tsx +120 -0
  181. package/src/primitives/upload-progress.tsx +110 -0
  182. package/src/run/expanded-tool-detail.stories.tsx +182 -0
  183. package/src/run/expanded-tool-detail.tsx +186 -0
  184. package/src/run/index.ts +13 -0
  185. package/src/run/inline-thinking-item.stories.tsx +136 -0
  186. package/src/run/inline-thinking-item.tsx +120 -0
  187. package/src/run/inline-tool-item.stories.tsx +222 -0
  188. package/src/run/inline-tool-item.tsx +190 -0
  189. package/src/run/run-group.stories.tsx +322 -0
  190. package/src/run/run-group.tsx +569 -0
  191. package/src/run/run-item-primitives.tsx +17 -0
  192. package/src/run/tool-call-feed.stories.tsx +294 -0
  193. package/src/run/tool-call-feed.tsx +192 -0
  194. package/src/run/tool-call-step.stories.tsx +198 -0
  195. package/src/run/tool-call-step.tsx +240 -0
  196. package/src/sdk-hooks.ts +38 -0
  197. package/src/stores/active-sessions-store.ts +455 -0
  198. package/src/stores/chat-store.ts +43 -0
  199. package/src/stores/index.ts +2 -0
  200. package/src/tool-previews/command-preview.tsx +116 -0
  201. package/src/tool-previews/diff-preview.tsx +85 -0
  202. package/src/tool-previews/glob-results-preview.tsx +98 -0
  203. package/src/tool-previews/grep-results-preview.tsx +157 -0
  204. package/src/tool-previews/index.ts +22 -0
  205. package/src/tool-previews/preview-primitives.tsx +84 -0
  206. package/src/tool-previews/question-preview.tsx +101 -0
  207. package/src/tool-previews/web-search-preview.tsx +117 -0
  208. package/src/tool-previews/write-file-preview.tsx +80 -0
  209. package/src/types/branding.ts +11 -0
  210. package/src/types/index.ts +5 -0
  211. package/src/types/message.ts +13 -0
  212. package/src/types/parts.ts +51 -0
  213. package/src/types/run.ts +56 -0
  214. package/src/types/tool-display.ts +41 -0
  215. package/src/utils/copy-text.ts +30 -0
  216. package/src/utils/format.test.ts +43 -0
  217. package/src/utils/format.ts +56 -0
  218. package/src/utils/index.ts +10 -0
  219. package/src/utils/time-ago.ts +9 -0
  220. package/src/utils/tool-display.ts +238 -0
@@ -0,0 +1,737 @@
1
+ import {
2
+ ArtifactPane
3
+ } from "./chunk-CSAIKY36.js";
4
+ import {
5
+ Markdown
6
+ } from "./chunk-CD53GZOM.js";
7
+ import {
8
+ cn
9
+ } from "./chunk-RQHJBTEU.js";
10
+
11
+ // src/files/file-tree.tsx
12
+ import { useState } from "react";
13
+ import {
14
+ File,
15
+ FileText,
16
+ FileCode,
17
+ FileSpreadsheet,
18
+ FileImage,
19
+ Folder,
20
+ FolderOpen,
21
+ ChevronRight,
22
+ FileJson
23
+ } from "lucide-react";
24
+ import { jsx, jsxs } from "react/jsx-runtime";
25
+ function isNodeVisible(node, visibility) {
26
+ if (!visibility) return true;
27
+ if (visibility.hiddenPaths?.includes(node.path)) {
28
+ return false;
29
+ }
30
+ if (visibility.hiddenPathPrefixes?.some(
31
+ (prefix) => node.path === prefix || node.path.startsWith(`${prefix}/`)
32
+ )) {
33
+ return false;
34
+ }
35
+ return visibility.isVisible ? visibility.isVisible(node) : true;
36
+ }
37
+ function filterFileTree(root, visibility) {
38
+ if (!isNodeVisible(root, visibility)) {
39
+ return null;
40
+ }
41
+ if (root.type === "file") {
42
+ return root;
43
+ }
44
+ const children = (root.children ?? []).map((child) => filterFileTree(child, visibility)).filter((child) => child !== null);
45
+ if (root.children && root.children.length > 0 && children.length === 0) {
46
+ return null;
47
+ }
48
+ return {
49
+ ...root,
50
+ children
51
+ };
52
+ }
53
+ var FILE_ICONS = {
54
+ pdf: FileText,
55
+ csv: FileSpreadsheet,
56
+ xlsx: FileSpreadsheet,
57
+ xls: FileSpreadsheet,
58
+ py: FileCode,
59
+ ts: FileCode,
60
+ js: FileCode,
61
+ json: FileJson,
62
+ yaml: FileCode,
63
+ yml: FileCode,
64
+ md: FileText,
65
+ txt: FileText,
66
+ png: FileImage,
67
+ jpg: FileImage,
68
+ jpeg: FileImage,
69
+ gif: FileImage,
70
+ svg: FileImage
71
+ };
72
+ function getFileIcon(name) {
73
+ const ext = name.split(".").pop()?.toLowerCase() || "";
74
+ return FILE_ICONS[ext] || File;
75
+ }
76
+ function getFileColor(name) {
77
+ const ext = name.split(".").pop()?.toLowerCase() || "";
78
+ switch (ext) {
79
+ case "pdf":
80
+ return "text-red-400";
81
+ case "py":
82
+ return "text-yellow-400";
83
+ case "ts":
84
+ case "js":
85
+ return "text-blue-400";
86
+ case "json":
87
+ return "text-green-400";
88
+ case "yaml":
89
+ case "yml":
90
+ return "text-[var(--accent-text)]";
91
+ case "csv":
92
+ case "xlsx":
93
+ return "text-emerald-400";
94
+ case "md":
95
+ return "text-foreground";
96
+ default:
97
+ return "text-muted-foreground";
98
+ }
99
+ }
100
+ function TreeNode({ node, depth, selectedPath, onSelect, defaultExpanded }) {
101
+ const [expanded, setExpanded] = useState(defaultExpanded);
102
+ const isSelected = node.path === selectedPath;
103
+ const isDir = node.type === "directory";
104
+ const handleClick = () => {
105
+ if (isDir) {
106
+ setExpanded(!expanded);
107
+ }
108
+ onSelect?.(node.path, node);
109
+ };
110
+ const Icon = isDir ? expanded ? FolderOpen : Folder : getFileIcon(node.name);
111
+ const iconColor = isDir ? "text-primary" : getFileColor(node.name);
112
+ return /* @__PURE__ */ jsxs("div", { children: [
113
+ /* @__PURE__ */ jsxs(
114
+ "button",
115
+ {
116
+ type: "button",
117
+ className: cn(
118
+ "flex items-center gap-1.5 w-full text-left px-2 py-[2px] cursor-pointer text-[13px] transition-colors",
119
+ "appearance-none bg-transparent border-none",
120
+ "hover:bg-accent",
121
+ isSelected ? "bg-primary/15 text-foreground" : "text-foreground"
122
+ ),
123
+ style: { paddingLeft: `${depth * 16 + 8}px` },
124
+ onClick: handleClick,
125
+ "aria-expanded": isDir ? expanded : void 0,
126
+ children: [
127
+ isDir && /* @__PURE__ */ jsx(
128
+ ChevronRight,
129
+ {
130
+ className: cn(
131
+ "h-3 w-3 shrink-0 text-muted-foreground transition-transform",
132
+ expanded && "rotate-90"
133
+ )
134
+ }
135
+ ),
136
+ !isDir && /* @__PURE__ */ jsx("span", { className: "w-3" }),
137
+ /* @__PURE__ */ jsx(Icon, { className: cn("h-4 w-4 shrink-0", iconColor) }),
138
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: node.name }),
139
+ node.size !== void 0 && !isDir && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground text-xs ml-auto tabular-nums", children: formatSize(node.size) })
140
+ ]
141
+ }
142
+ ),
143
+ isDir && expanded && node.children && /* @__PURE__ */ jsx("div", { children: node.children.sort((a, b) => {
144
+ if (a.type !== b.type) return a.type === "directory" ? -1 : 1;
145
+ return a.name.localeCompare(b.name);
146
+ }).map((child) => /* @__PURE__ */ jsx(
147
+ TreeNode,
148
+ {
149
+ node: child,
150
+ depth: depth + 1,
151
+ selectedPath,
152
+ onSelect,
153
+ defaultExpanded
154
+ },
155
+ child.path
156
+ )) })
157
+ ] });
158
+ }
159
+ function formatSize(bytes) {
160
+ if (bytes < 1024) return `${bytes}B`;
161
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(0)}K`;
162
+ return `${(bytes / (1024 * 1024)).toFixed(1)}M`;
163
+ }
164
+ function FileTree({
165
+ root,
166
+ selectedPath,
167
+ onSelect,
168
+ className,
169
+ defaultExpanded = true,
170
+ visibility
171
+ }) {
172
+ const visibleRoot = filterFileTree(root, visibility);
173
+ if (!visibleRoot) {
174
+ return null;
175
+ }
176
+ return /* @__PURE__ */ jsx("div", { className: cn("text-sm font-sans", className), children: visibleRoot.children ? visibleRoot.children.sort((a, b) => {
177
+ if (a.type !== b.type) return a.type === "directory" ? -1 : 1;
178
+ return a.name.localeCompare(b.name);
179
+ }).map((child) => /* @__PURE__ */ jsx(
180
+ TreeNode,
181
+ {
182
+ node: child,
183
+ depth: 0,
184
+ selectedPath,
185
+ onSelect,
186
+ defaultExpanded
187
+ },
188
+ child.path
189
+ )) : /* @__PURE__ */ jsx(
190
+ TreeNode,
191
+ {
192
+ node: visibleRoot,
193
+ depth: 0,
194
+ selectedPath,
195
+ onSelect,
196
+ defaultExpanded
197
+ }
198
+ ) });
199
+ }
200
+
201
+ // src/files/rich-file-tree.tsx
202
+ import {
203
+ FileTree as PierreFileTree,
204
+ useFileTree as usePierreFileTree
205
+ } from "@pierre/trees/react";
206
+ import { useEffect, useMemo } from "react";
207
+ import { jsx as jsx2 } from "react/jsx-runtime";
208
+ function cssVarFromToken(name) {
209
+ return `var(${name})`;
210
+ }
211
+ var DEFAULT_THEME = {
212
+ selectedBg: cssVarFromToken("--accent-surface-soft"),
213
+ selectedFg: cssVarFromToken("--accent-text"),
214
+ fg: cssVarFromToken("--foreground"),
215
+ hoverBg: cssVarFromToken("--muted"),
216
+ border: cssVarFromToken("--border"),
217
+ mutedFg: cssVarFromToken("--muted-foreground")
218
+ };
219
+ function flattenFileNode(node, out = []) {
220
+ if (node.type === "file" && node.path) {
221
+ out.push(node.path);
222
+ return out;
223
+ }
224
+ if (node.type === "directory") {
225
+ if (node.children?.length) {
226
+ for (const child of node.children) {
227
+ flattenFileNode(child, out);
228
+ }
229
+ return out;
230
+ }
231
+ if (node.path) out.push(`${node.path}/`);
232
+ }
233
+ return out;
234
+ }
235
+ function RichFileTree({
236
+ root,
237
+ paths,
238
+ selectedPath,
239
+ onSelect,
240
+ search = true,
241
+ initialExpansion = "open",
242
+ gitStatus,
243
+ renderContextMenu,
244
+ header,
245
+ themeOverrides,
246
+ className,
247
+ style,
248
+ height
249
+ }) {
250
+ if (root && paths) {
251
+ throw new Error("RichFileTree: pass `root` or `paths`, not both");
252
+ }
253
+ const flatPaths = useMemo(() => {
254
+ if (paths) return Array.from(paths);
255
+ if (root) return flattenFileNode(root);
256
+ return [];
257
+ }, [paths, root]);
258
+ const { model } = usePierreFileTree({
259
+ paths: flatPaths,
260
+ search,
261
+ initialExpansion,
262
+ onSelectionChange: (selected) => {
263
+ const next = selected[0];
264
+ if (next && next !== selectedPath) onSelect?.(next);
265
+ }
266
+ });
267
+ useEffect(() => {
268
+ if (!gitStatus) return;
269
+ model.setGitStatus(gitStatus);
270
+ }, [model, gitStatus]);
271
+ useEffect(() => {
272
+ model.resetPaths(flatPaths);
273
+ }, [model, flatPaths]);
274
+ useEffect(() => {
275
+ if (!selectedPath) return;
276
+ const current = model.getSelectedPaths();
277
+ if (current.length === 1 && current[0] === selectedPath) return;
278
+ const m = model;
279
+ m.setSelectedPaths?.([selectedPath]);
280
+ }, [model, selectedPath]);
281
+ const theme = { ...DEFAULT_THEME, ...themeOverrides };
282
+ const themeStyle = useMemo(
283
+ () => ({
284
+ ["--trees-selected-bg-override"]: theme.selectedBg,
285
+ ["--trees-selected-fg-override"]: theme.selectedFg,
286
+ ["--trees-fg-override"]: theme.fg,
287
+ ["--trees-hover-bg-override"]: theme.hoverBg,
288
+ ["--trees-border-color-override"]: theme.border,
289
+ ["--trees-muted-fg-override"]: theme.mutedFg,
290
+ height: height ?? "100%",
291
+ ...style
292
+ }),
293
+ [theme, height, style]
294
+ );
295
+ return /* @__PURE__ */ jsx2(
296
+ PierreFileTree,
297
+ {
298
+ model,
299
+ header,
300
+ renderContextMenu,
301
+ className,
302
+ style: themeStyle
303
+ }
304
+ );
305
+ }
306
+
307
+ // src/files/file-preview.tsx
308
+ import {
309
+ Download,
310
+ X,
311
+ FileText as FileText2
312
+ } from "lucide-react";
313
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
314
+ function getPreviewType(filename, mimeType) {
315
+ const ext = filename.split(".").pop()?.toLowerCase() || "";
316
+ if (mimeType?.startsWith("application/pdf") || ext === "pdf") return "pdf";
317
+ if (mimeType?.startsWith("image/") || ["png", "jpg", "jpeg", "gif", "svg", "webp"].includes(ext)) return "image";
318
+ if (["csv"].includes(ext)) return "csv";
319
+ if (["xlsx", "xls"].includes(ext)) return "spreadsheet";
320
+ if (["py", "ts", "js", "tsx", "jsx", "sh", "bash"].includes(ext) || ["profile", "bashrc", "bash_logout", "env", "gitignore"].includes(ext)) return "code";
321
+ if (["json"].includes(ext)) return "json";
322
+ if (["yaml", "yml"].includes(ext)) return "yaml";
323
+ if (["md", "markdown"].includes(ext)) return "markdown";
324
+ if (["txt", "log", "text"].includes(ext)) return "text";
325
+ if (mimeType?.startsWith("text/plain")) return "text";
326
+ return "unknown";
327
+ }
328
+ function getPreviewLabel(previewType) {
329
+ switch (previewType) {
330
+ case "pdf":
331
+ return "PDF";
332
+ case "image":
333
+ return "Image";
334
+ case "csv":
335
+ return "CSV";
336
+ case "spreadsheet":
337
+ return "Spreadsheet";
338
+ case "code":
339
+ return "Code";
340
+ case "json":
341
+ return "JSON";
342
+ case "yaml":
343
+ return "YAML";
344
+ case "markdown":
345
+ return "Markdown";
346
+ case "text":
347
+ return "Text";
348
+ default:
349
+ return "File";
350
+ }
351
+ }
352
+ function CodePreview({ content, filename }) {
353
+ const lines = content.split("\n");
354
+ const language = filename.split(".").pop()?.toUpperCase() || "TXT";
355
+ return /* @__PURE__ */ jsxs2("div", { className: "relative bg-background rounded-[var(--radius-md)] border border-border overflow-hidden", children: [
356
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between gap-3 border-b border-border px-4 py-2.5", children: [
357
+ /* @__PURE__ */ jsxs2("div", { className: "flex gap-1.5", children: [
358
+ /* @__PURE__ */ jsx3("div", { className: "w-3 h-3 rounded-full bg-[#FF5F57]" }),
359
+ /* @__PURE__ */ jsx3("div", { className: "w-3 h-3 rounded-full bg-[#FEBC2E]" }),
360
+ /* @__PURE__ */ jsx3("div", { className: "w-3 h-3 rounded-full bg-[#8E59FF]" })
361
+ ] }),
362
+ /* @__PURE__ */ jsx3("div", { className: "ml-2 min-w-0 flex-1 truncate text-xs font-mono text-muted-foreground", children: filename }),
363
+ /* @__PURE__ */ jsxs2("div", { className: "inline-flex items-center gap-2 rounded-[var(--radius-full)] border border-border bg-card px-2.5 py-1 text-[10px] font-semibold uppercase tracking-[0.12em] text-muted-foreground", children: [
364
+ /* @__PURE__ */ jsx3("span", { children: language }),
365
+ /* @__PURE__ */ jsx3("span", { className: "h-1 w-1 rounded-full bg-[var(--border-hover)]" }),
366
+ /* @__PURE__ */ jsxs2("span", { children: [
367
+ lines.length,
368
+ " lines"
369
+ ] })
370
+ ] })
371
+ ] }),
372
+ /* @__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__ */ jsxs2("tr", { className: "hover:bg-accent", children: [
373
+ /* @__PURE__ */ jsx3("td", { className: "text-right pr-4 pl-4 py-0 select-none text-muted-foreground text-xs font-mono w-10 align-top leading-[1.55]", children: i + 1 }),
374
+ /* @__PURE__ */ jsx3("td", { className: "pr-4 py-0 font-mono text-[13px] text-foreground leading-[1.55] whitespace-pre", children: line || " " })
375
+ ] }, i)) }) }) })
376
+ ] });
377
+ }
378
+ function parseCsvRow(line) {
379
+ const cells = [];
380
+ let current = "";
381
+ let inQuotes = false;
382
+ for (let index = 0; index < line.length; index += 1) {
383
+ const char = line[index];
384
+ const next = line[index + 1];
385
+ if (char === '"') {
386
+ if (inQuotes && next === '"') {
387
+ current += '"';
388
+ index += 1;
389
+ continue;
390
+ }
391
+ inQuotes = !inQuotes;
392
+ continue;
393
+ }
394
+ if (char === "," && !inQuotes) {
395
+ cells.push(current.trim());
396
+ current = "";
397
+ continue;
398
+ }
399
+ current += char;
400
+ }
401
+ cells.push(current.trim());
402
+ return cells;
403
+ }
404
+ function CsvPreview({ content }) {
405
+ const lines = content.trim().split(/\r?\n/).filter(Boolean);
406
+ if (lines.length === 0) return null;
407
+ const headers = parseCsvRow(lines[0]).map((header) => header.replace(/^"|"$/g, ""));
408
+ const rows = lines.slice(1).map(
409
+ (line) => parseCsvRow(line).map((cell) => cell.replace(/^"|"$/g, ""))
410
+ );
411
+ return /* @__PURE__ */ jsx3("div", { className: "overflow-auto max-h-[70vh] rounded-[var(--radius-md)] border border-border", children: /* @__PURE__ */ jsxs2("table", { className: "w-full text-sm", children: [
412
+ /* @__PURE__ */ jsx3("thead", { children: /* @__PURE__ */ jsx3("tr", { className: "bg-muted/50 sticky top-0", children: headers.map((h, i) => /* @__PURE__ */ jsx3(
413
+ "th",
414
+ {
415
+ className: "px-3 py-2 text-left text-xs font-semibold text-foreground border-b border-border whitespace-nowrap",
416
+ children: h
417
+ },
418
+ i
419
+ )) }) }),
420
+ /* @__PURE__ */ jsx3("tbody", { children: rows.map((row, i) => /* @__PURE__ */ jsx3("tr", { className: "border-b border-border hover:bg-accent", children: row.map((cell, j) => /* @__PURE__ */ jsx3(
421
+ "td",
422
+ {
423
+ className: "px-3 py-1.5 text-foreground font-mono text-xs whitespace-nowrap",
424
+ children: cell
425
+ },
426
+ j
427
+ )) }, i)) })
428
+ ] }) });
429
+ }
430
+ function ImagePreview({ src, filename }) {
431
+ return /* @__PURE__ */ jsx3("div", { className: "flex items-center justify-center p-4 bg-background rounded-[var(--radius-md)] border border-border", children: /* @__PURE__ */ jsx3("img", { src, alt: filename, className: "max-w-full max-h-[70vh] object-contain rounded" }) });
432
+ }
433
+ function PdfPreview({ blobUrl, filename }) {
434
+ return /* @__PURE__ */ jsx3("div", { className: "rounded-[var(--radius-md)] border border-border overflow-hidden bg-background", children: /* @__PURE__ */ jsx3(
435
+ "iframe",
436
+ {
437
+ src: blobUrl,
438
+ title: filename,
439
+ className: "w-full h-[70vh] border-0"
440
+ }
441
+ ) });
442
+ }
443
+ function TextPreview({ content }) {
444
+ return /* @__PURE__ */ jsx3("pre", { className: "bg-background rounded-[var(--radius-md)] border border-border p-4 overflow-auto max-h-[70vh] text-sm text-foreground font-mono leading-[1.55]", children: content });
445
+ }
446
+ function UnsupportedPreview({
447
+ filename,
448
+ title,
449
+ description
450
+ }) {
451
+ return /* @__PURE__ */ jsxs2("div", { className: "flex flex-col items-center justify-center rounded-[var(--radius-md)] border border-dashed border-border bg-background px-6 py-16 text-center text-muted-foreground", children: [
452
+ /* @__PURE__ */ jsx3(FileText2, { className: "mb-3 h-12 w-12 opacity-30" }),
453
+ /* @__PURE__ */ jsx3("p", { className: "text-sm text-foreground", children: title }),
454
+ /* @__PURE__ */ jsx3("p", { className: "mt-1 max-w-md text-xs", children: description }),
455
+ /* @__PURE__ */ jsx3("p", { className: "mt-4 text-[11px] uppercase tracking-[0.12em]", children: filename })
456
+ ] });
457
+ }
458
+ function MarkdownPreview({ content }) {
459
+ return /* @__PURE__ */ jsx3("div", { className: "rounded-[var(--radius-md)] border border-border bg-background p-5", children: /* @__PURE__ */ jsx3(Markdown, { className: "prose-sm max-w-none", children: content }) });
460
+ }
461
+ function EmptyPreview({ filename }) {
462
+ return /* @__PURE__ */ jsxs2("div", { className: "flex flex-col items-center justify-center py-16 text-muted-foreground", children: [
463
+ /* @__PURE__ */ jsx3(FileText2, { className: "h-12 w-12 mb-3 opacity-30" }),
464
+ /* @__PURE__ */ jsxs2("p", { className: "text-sm", children: [
465
+ "Cannot preview ",
466
+ filename
467
+ ] }),
468
+ /* @__PURE__ */ jsx3("p", { className: "text-xs mt-1", children: "Download to view this file" })
469
+ ] });
470
+ }
471
+ function FilePreview({
472
+ filename,
473
+ content,
474
+ blobUrl,
475
+ mimeType,
476
+ onClose,
477
+ onDownload,
478
+ hideHeader = false,
479
+ className
480
+ }) {
481
+ const previewType = getPreviewType(filename, mimeType);
482
+ const previewLabel = getPreviewLabel(previewType);
483
+ const hasRenderableSource = Boolean(content) || Boolean(blobUrl) || previewType === "unknown" || previewType === "spreadsheet";
484
+ return /* @__PURE__ */ jsxs2("div", { className: cn("flex flex-col h-full", className), children: [
485
+ !hideHeader && /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2 px-3 py-2 border-b border-border shrink-0", children: [
486
+ /* @__PURE__ */ jsxs2("div", { className: "min-w-0 flex-1", children: [
487
+ /* @__PURE__ */ jsx3("div", { className: "truncate text-sm font-medium text-foreground", children: filename }),
488
+ /* @__PURE__ */ jsx3("div", { className: "mt-0.5 text-[11px] uppercase tracking-[0.12em] text-muted-foreground", children: previewLabel })
489
+ ] }),
490
+ onDownload && /* @__PURE__ */ jsx3(
491
+ "button",
492
+ {
493
+ type: "button",
494
+ onClick: onDownload,
495
+ "aria-label": `Download ${filename}`,
496
+ className: "p-1.5 rounded-[var(--radius-sm)] hover:bg-accent text-muted-foreground hover:text-foreground transition-colors",
497
+ children: /* @__PURE__ */ jsx3(Download, { className: "h-4 w-4" })
498
+ }
499
+ ),
500
+ onClose && /* @__PURE__ */ jsx3(
501
+ "button",
502
+ {
503
+ type: "button",
504
+ onClick: onClose,
505
+ "aria-label": `Close ${filename}`,
506
+ className: "p-1.5 rounded-[var(--radius-sm)] hover:bg-accent text-muted-foreground hover:text-foreground transition-colors",
507
+ children: /* @__PURE__ */ jsx3(X, { className: "h-4 w-4" })
508
+ }
509
+ )
510
+ ] }),
511
+ /* @__PURE__ */ jsxs2("div", { className: "flex-1 overflow-auto p-3", children: [
512
+ previewType === "pdf" && blobUrl && /* @__PURE__ */ jsx3(PdfPreview, { blobUrl, filename }),
513
+ previewType === "image" && blobUrl && /* @__PURE__ */ jsx3(ImagePreview, { src: blobUrl, filename }),
514
+ previewType === "csv" && typeof content === "string" && /* @__PURE__ */ jsx3(CsvPreview, { content }),
515
+ (previewType === "code" || previewType === "json" || previewType === "yaml") && typeof content === "string" && /* @__PURE__ */ jsx3(CodePreview, { content, filename }),
516
+ previewType === "text" && typeof content === "string" && /* @__PURE__ */ jsx3(TextPreview, { content }),
517
+ previewType === "markdown" && typeof content === "string" && /* @__PURE__ */ jsx3(MarkdownPreview, { content }),
518
+ previewType === "spreadsheet" && /* @__PURE__ */ jsx3(
519
+ UnsupportedPreview,
520
+ {
521
+ filename,
522
+ title: "Spreadsheet preview is not available in this surface",
523
+ description: "Download the workbook or convert it to CSV when you need an inline preview."
524
+ }
525
+ ),
526
+ previewType === "unknown" && typeof content !== "string" && /* @__PURE__ */ jsx3(EmptyPreview, { filename }),
527
+ previewType === "unknown" && typeof content === "string" && /* @__PURE__ */ jsx3(TextPreview, { content }),
528
+ !hasRenderableSource && typeof content !== "string" && /* @__PURE__ */ jsx3(
529
+ UnsupportedPreview,
530
+ {
531
+ filename,
532
+ title: "Preview data is not available yet",
533
+ description: "This artifact can be shown once the app provides inline content or a downloadable blob."
534
+ }
535
+ )
536
+ ] })
537
+ ] });
538
+ }
539
+
540
+ // src/files/file-tabs.tsx
541
+ import { X as X2, FileText as FileText3, FileCode as FileCode2, FileSpreadsheet as FileSpreadsheet2 } from "lucide-react";
542
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
543
+ function getTabIcon(name) {
544
+ const ext = name.split(".").pop()?.toLowerCase() || "";
545
+ if (["pdf"].includes(ext)) return FileText3;
546
+ if (["csv", "xlsx"].includes(ext)) return FileSpreadsheet2;
547
+ return FileCode2;
548
+ }
549
+ function FileTabs({ tabs, activeId, onSelect, onClose, className }) {
550
+ if (tabs.length === 0) return null;
551
+ return /* @__PURE__ */ jsx4("div", { className: cn("flex items-center border-b border-border bg-background overflow-x-auto", className), children: tabs.map((tab) => {
552
+ const isActive = tab.id === activeId;
553
+ const Icon = getTabIcon(tab.name);
554
+ return /* @__PURE__ */ jsxs3(
555
+ "div",
556
+ {
557
+ className: cn(
558
+ "group flex items-center border-r border-border shrink-0",
559
+ isActive ? "bg-card text-foreground border-b-2 border-b-primary" : "text-muted-foreground hover:bg-muted/50"
560
+ ),
561
+ children: [
562
+ /* @__PURE__ */ jsxs3(
563
+ "button",
564
+ {
565
+ type: "button",
566
+ onClick: () => onSelect(tab.id),
567
+ className: "flex min-w-0 items-center gap-1.5 px-3 py-1.5 text-xs transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary/60",
568
+ children: [
569
+ /* @__PURE__ */ jsx4(Icon, { className: "h-3 w-3 shrink-0" }),
570
+ /* @__PURE__ */ jsx4("span", { className: "max-w-[120px] truncate", children: tab.name }),
571
+ tab.dirty && /* @__PURE__ */ jsx4("span", { className: "h-1.5 w-1.5 rounded-full bg-primary" })
572
+ ]
573
+ }
574
+ ),
575
+ /* @__PURE__ */ jsx4(
576
+ "button",
577
+ {
578
+ type: "button",
579
+ "aria-label": `Close ${tab.name}`,
580
+ onClick: () => onClose(tab.id),
581
+ className: "mr-1 rounded p-0.5 opacity-0 transition-opacity hover:bg-accent focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/60 group-hover:opacity-100",
582
+ children: /* @__PURE__ */ jsx4(X2, { className: "h-2.5 w-2.5" })
583
+ }
584
+ )
585
+ ]
586
+ },
587
+ tab.id
588
+ );
589
+ }) });
590
+ }
591
+
592
+ // src/files/file-artifact-pane.tsx
593
+ import { lazy, Suspense } from "react";
594
+ import { Download as Download2, X as X3 } from "lucide-react";
595
+ import { Fragment, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
596
+ var LazyDocumentEditorPane = lazy(async () => {
597
+ const module = await import("./document-editor-pane-A5LT5H4N.js");
598
+ return { default: module.DocumentEditorPane };
599
+ });
600
+ function FileArtifactPane({
601
+ filename,
602
+ content,
603
+ blobUrl,
604
+ mimeType,
605
+ onClose,
606
+ onDownload,
607
+ path,
608
+ tabs = [],
609
+ activeTabId,
610
+ onTabSelect,
611
+ onTabClose,
612
+ eyebrow = "Artifact",
613
+ meta,
614
+ toolbar,
615
+ footer,
616
+ className,
617
+ editor
618
+ }) {
619
+ const showTabs = tabs.length > 0 && onTabSelect && onTabClose;
620
+ const paneTabs = showTabs ? /* @__PURE__ */ jsx5(
621
+ FileTabs,
622
+ {
623
+ tabs,
624
+ activeId: activeTabId,
625
+ onSelect: onTabSelect,
626
+ onClose: onTabClose
627
+ }
628
+ ) : void 0;
629
+ const isMarkdown = mimeType === "text/markdown" || filename.toLowerCase().endsWith(".md") || path?.toLowerCase().endsWith(".md");
630
+ const isEditableMarkdown = isMarkdown && editor?.enabled;
631
+ const headerActions = /* @__PURE__ */ jsxs4(Fragment, { children: [
632
+ onDownload && /* @__PURE__ */ jsx5(
633
+ "button",
634
+ {
635
+ type: "button",
636
+ "aria-label": `Download ${filename}`,
637
+ onClick: onDownload,
638
+ className: "rounded-[var(--radius-sm)] p-1.5 text-muted-foreground transition-colors hover:bg-accent hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/60",
639
+ children: /* @__PURE__ */ jsx5(Download2, { className: "h-4 w-4" })
640
+ }
641
+ ),
642
+ onClose && /* @__PURE__ */ jsx5(
643
+ "button",
644
+ {
645
+ type: "button",
646
+ "aria-label": `Close ${filename}`,
647
+ onClick: onClose,
648
+ className: "rounded-[var(--radius-sm)] p-1.5 text-muted-foreground transition-colors hover:bg-accent hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/60",
649
+ children: /* @__PURE__ */ jsx5(X3, { className: "h-4 w-4" })
650
+ }
651
+ )
652
+ ] });
653
+ if (isEditableMarkdown) {
654
+ return /* @__PURE__ */ jsx5(
655
+ Suspense,
656
+ {
657
+ fallback: /* @__PURE__ */ jsx5(
658
+ ArtifactPane,
659
+ {
660
+ eyebrow,
661
+ title: filename,
662
+ subtitle: path,
663
+ meta,
664
+ toolbar,
665
+ footer,
666
+ className,
667
+ tabs: paneTabs,
668
+ headerActions,
669
+ children: /* @__PURE__ */ jsx5("div", { className: "flex min-h-[12rem] items-center justify-center rounded-[var(--radius-lg)] border border-dashed border-border bg-muted text-sm text-muted-foreground", children: "Loading editor\u2026" })
670
+ }
671
+ ),
672
+ children: /* @__PURE__ */ jsx5(
673
+ LazyDocumentEditorPane,
674
+ {
675
+ eyebrow,
676
+ title: filename,
677
+ subtitle: path,
678
+ meta,
679
+ toolbar,
680
+ footer,
681
+ className,
682
+ tabs: paneTabs,
683
+ headerActions,
684
+ markdown: content ?? "",
685
+ mode: editor.mode,
686
+ defaultMode: editor.defaultMode,
687
+ onModeChange: editor.onModeChange,
688
+ backend: editor.backend,
689
+ collaboration: editor.collaboration,
690
+ placeholder: editor.placeholder,
691
+ autoFocus: editor.autoFocus,
692
+ readOnly: editor.readOnly,
693
+ saving: editor.saving,
694
+ saveLabel: editor.saveLabel,
695
+ onChange: editor.onChange,
696
+ onSave: editor.onSave,
697
+ previewClassName: editor.previewClassName,
698
+ editorClassName: editor.editorClassName
699
+ }
700
+ )
701
+ }
702
+ );
703
+ }
704
+ return /* @__PURE__ */ jsx5(
705
+ ArtifactPane,
706
+ {
707
+ eyebrow,
708
+ title: filename,
709
+ subtitle: path,
710
+ meta,
711
+ toolbar,
712
+ footer,
713
+ className,
714
+ tabs: paneTabs,
715
+ headerActions,
716
+ children: /* @__PURE__ */ jsx5(
717
+ FilePreview,
718
+ {
719
+ filename,
720
+ content,
721
+ blobUrl,
722
+ mimeType,
723
+ hideHeader: true
724
+ }
725
+ )
726
+ }
727
+ );
728
+ }
729
+
730
+ export {
731
+ filterFileTree,
732
+ FileTree,
733
+ RichFileTree,
734
+ FilePreview,
735
+ FileTabs,
736
+ FileArtifactPane
737
+ };