@tangle-network/sandbox-ui 0.2.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 (70) hide show
  1. package/README.md +68 -0
  2. package/dist/auth.d.ts +57 -0
  3. package/dist/auth.js +14 -0
  4. package/dist/branding-DCi5VEik.d.ts +13 -0
  5. package/dist/button-BidTtuRS.d.ts +15 -0
  6. package/dist/chat.d.ts +121 -0
  7. package/dist/chat.js +25 -0
  8. package/dist/chunk-2UHPE5T7.js +201 -0
  9. package/dist/chunk-4EIWPJMJ.js +545 -0
  10. package/dist/chunk-6MQIDUPA.js +502 -0
  11. package/dist/chunk-B26TQ7SA.js +47 -0
  12. package/dist/chunk-E6FS7R4X.js +109 -0
  13. package/dist/chunk-GRYHFH5O.js +110 -0
  14. package/dist/chunk-HMND7JPA.js +868 -0
  15. package/dist/chunk-HRMUF35V.js +19 -0
  16. package/dist/chunk-HYEAX3DC.js +822 -0
  17. package/dist/chunk-KMXV7DDX.js +174 -0
  18. package/dist/chunk-KYY2X6LY.js +318 -0
  19. package/dist/chunk-L6ZDH5F4.js +334 -0
  20. package/dist/chunk-LTFK464G.js +103 -0
  21. package/dist/chunk-M34OA6PQ.js +233 -0
  22. package/dist/chunk-M6VLC32S.js +219 -0
  23. package/dist/chunk-MCGKDCOR.js +173 -0
  24. package/dist/chunk-NI2EI43H.js +294 -0
  25. package/dist/chunk-OU4TRNQZ.js +173 -0
  26. package/dist/chunk-QD4QE5P5.js +40 -0
  27. package/dist/chunk-QSQBDR3N.js +180 -0
  28. package/dist/chunk-RQHJBTEU.js +10 -0
  29. package/dist/chunk-U62G5TS7.js +472 -0
  30. package/dist/chunk-ZOL2TR5M.js +475 -0
  31. package/dist/dashboard.d.ts +111 -0
  32. package/dist/dashboard.js +26 -0
  33. package/dist/editor.d.ts +196 -0
  34. package/dist/editor.js +713 -0
  35. package/dist/expanded-tool-detail-OkXGqTHe.d.ts +52 -0
  36. package/dist/files.d.ts +66 -0
  37. package/dist/files.js +11 -0
  38. package/dist/hooks.d.ts +22 -0
  39. package/dist/hooks.js +107 -0
  40. package/dist/index.d.ts +107 -0
  41. package/dist/index.js +551 -0
  42. package/dist/markdown.d.ts +55 -0
  43. package/dist/markdown.js +17 -0
  44. package/dist/pages.d.ts +89 -0
  45. package/dist/pages.js +1181 -0
  46. package/dist/parts-CyGkM6Fp.d.ts +50 -0
  47. package/dist/primitives.d.ts +189 -0
  48. package/dist/primitives.js +161 -0
  49. package/dist/run-CtFZ6s-D.d.ts +41 -0
  50. package/dist/run.d.ts +14 -0
  51. package/dist/run.js +29 -0
  52. package/dist/sidecar-CFU2W9j1.d.ts +8 -0
  53. package/dist/stores.d.ts +28 -0
  54. package/dist/stores.js +49 -0
  55. package/dist/terminal.d.ts +44 -0
  56. package/dist/terminal.js +160 -0
  57. package/dist/tool-call-feed-D5Ume-Pt.d.ts +66 -0
  58. package/dist/tool-display-BvsVW_Ur.d.ts +32 -0
  59. package/dist/types.d.ts +6 -0
  60. package/dist/types.js +0 -0
  61. package/dist/usage-chart-DINgSVL5.d.ts +60 -0
  62. package/dist/use-sidecar-auth-Bb0-w3lX.d.ts +339 -0
  63. package/dist/utils.d.ts +28 -0
  64. package/dist/utils.js +28 -0
  65. package/dist/workspace.d.ts +113 -0
  66. package/dist/workspace.js +15 -0
  67. package/package.json +174 -0
  68. package/src/styles/globals.css +230 -0
  69. package/src/styles/tokens.css +73 -0
  70. package/tailwind.config.cjs +99 -0
@@ -0,0 +1,334 @@
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
+ };
@@ -0,0 +1,103 @@
1
+ import {
2
+ cn
3
+ } from "./chunk-RQHJBTEU.js";
4
+
5
+ // src/markdown/code-block.tsx
6
+ import {
7
+ memo,
8
+ useCallback,
9
+ useState
10
+ } from "react";
11
+ import { Check, Copy } from "lucide-react";
12
+ import { jsx, jsxs } from "react/jsx-runtime";
13
+ var CodeBlock = memo(
14
+ ({ code, language, className, children, ...props }) => {
15
+ return /* @__PURE__ */ jsx(
16
+ "div",
17
+ {
18
+ className: cn("relative overflow-hidden rounded-lg", className),
19
+ ...props,
20
+ children: /* @__PURE__ */ jsxs("div", { className: "relative", children: [
21
+ /* @__PURE__ */ jsx("pre", { className: "m-0 p-4 overflow-x-auto text-sm font-mono bg-neutral-100 text-neutral-800 dark:bg-neutral-900 dark:text-neutral-100", children: /* @__PURE__ */ jsx("code", { className: language ? `language-${language}` : void 0, children: code }) }),
22
+ children && /* @__PURE__ */ jsx("div", { className: "absolute top-2 right-2 flex items-center gap-2", children })
23
+ ] })
24
+ }
25
+ );
26
+ }
27
+ );
28
+ CodeBlock.displayName = "CodeBlock";
29
+ var CopyButton = memo(({ text }) => {
30
+ const [copied, setCopied] = useState(false);
31
+ const handleCopy = useCallback(async () => {
32
+ try {
33
+ await navigator.clipboard.writeText(text);
34
+ setCopied(true);
35
+ setTimeout(() => setCopied(false), 2e3);
36
+ } catch {
37
+ }
38
+ }, [text]);
39
+ return /* @__PURE__ */ jsx(
40
+ "button",
41
+ {
42
+ onClick: handleCopy,
43
+ className: "flex items-center justify-center w-7 h-7 rounded-md bg-black/10 dark:bg-white/10 hover:bg-black/20 dark:hover:bg-white/20 transition-colors",
44
+ title: "Copy to clipboard",
45
+ children: copied ? /* @__PURE__ */ jsx(Check, { className: "w-3.5 h-3.5 text-green-500 dark:text-green-400" }) : /* @__PURE__ */ jsx(Copy, { className: "w-3.5 h-3.5 text-neutral-400" })
46
+ }
47
+ );
48
+ });
49
+ CopyButton.displayName = "CopyButton";
50
+
51
+ // src/markdown/markdown.tsx
52
+ import { memo as memo2 } from "react";
53
+ import ReactMarkdown from "react-markdown";
54
+ import remarkGfm from "remark-gfm";
55
+ import rehypeSanitize from "rehype-sanitize";
56
+ import { Fragment, jsx as jsx2 } from "react/jsx-runtime";
57
+ var Markdown = memo2(({ children, className }) => {
58
+ return /* @__PURE__ */ jsx2(
59
+ "div",
60
+ {
61
+ className: cn("prose prose-sm dark:prose-invert max-w-none", className),
62
+ children: /* @__PURE__ */ jsx2(
63
+ ReactMarkdown,
64
+ {
65
+ remarkPlugins: [remarkGfm],
66
+ rehypePlugins: [rehypeSanitize],
67
+ components: {
68
+ pre({ children: preChildren }) {
69
+ return /* @__PURE__ */ jsx2(Fragment, { children: preChildren });
70
+ },
71
+ code({ className: codeClass, children: codeChildren, ...rest }) {
72
+ const match = /language-(\w+)/.exec(codeClass || "");
73
+ const language = match?.[1];
74
+ const code = String(codeChildren).replace(/\n$/, "");
75
+ if (!language && !code.includes("\n")) {
76
+ return /* @__PURE__ */ jsx2(
77
+ "code",
78
+ {
79
+ className: cn(
80
+ "px-1.5 py-0.5 rounded bg-neutral-200/50 dark:bg-neutral-800/50 text-sm font-mono",
81
+ codeClass
82
+ ),
83
+ ...rest,
84
+ children: codeChildren
85
+ }
86
+ );
87
+ }
88
+ return /* @__PURE__ */ jsx2(CodeBlock, { code, language, children: /* @__PURE__ */ jsx2(CopyButton, { text: code }) });
89
+ }
90
+ },
91
+ children
92
+ }
93
+ )
94
+ }
95
+ );
96
+ });
97
+ Markdown.displayName = "Markdown";
98
+
99
+ export {
100
+ CodeBlock,
101
+ CopyButton,
102
+ Markdown
103
+ };
@@ -0,0 +1,233 @@
1
+ import {
2
+ cn
3
+ } from "./chunk-RQHJBTEU.js";
4
+
5
+ // src/markdown/simple-markdown.tsx
6
+ import { jsx } from "react/jsx-runtime";
7
+ function SimpleMarkdown({ content, className }) {
8
+ const html = renderMarkdown(content);
9
+ return /* @__PURE__ */ jsx(
10
+ "div",
11
+ {
12
+ className: cn("tangle-prose", className),
13
+ dangerouslySetInnerHTML: { __html: html }
14
+ }
15
+ );
16
+ }
17
+ function escapeHtml(text) {
18
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
19
+ }
20
+ function renderInline(text) {
21
+ let result = escapeHtml(text);
22
+ result = result.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>");
23
+ result = result.replace(/__(.+?)__/g, "<strong>$1</strong>");
24
+ result = result.replace(/\*(.+?)\*/g, "<em>$1</em>");
25
+ result = result.replace(/_(.+?)_/g, "<em>$1</em>");
26
+ result = result.replace(/`([^`]+)`/g, '<code class="tangle-inline-code">$1</code>');
27
+ result = result.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank" rel="noopener" class="tangle-link">$1</a>');
28
+ return result;
29
+ }
30
+ function renderMarkdown(md) {
31
+ const lines = md.split("\n");
32
+ const output = [];
33
+ let i = 0;
34
+ let inList = false;
35
+ let listType = "ul";
36
+ const closeList = () => {
37
+ if (inList) {
38
+ output.push(`</${listType}>`);
39
+ inList = false;
40
+ }
41
+ };
42
+ while (i < lines.length) {
43
+ const line = lines[i];
44
+ if (line.startsWith("```")) {
45
+ closeList();
46
+ const lang = line.slice(3).trim();
47
+ const codeLines = [];
48
+ i++;
49
+ while (i < lines.length && !lines[i].startsWith("```")) {
50
+ codeLines.push(lines[i]);
51
+ i++;
52
+ }
53
+ i++;
54
+ const code = escapeHtml(codeLines.join("\n"));
55
+ output.push(`<div class="tangle-code-block">`);
56
+ if (lang) output.push(`<div class="tangle-code-lang">${escapeHtml(lang)}</div>`);
57
+ output.push(`<pre><code>${code}</code></pre></div>`);
58
+ continue;
59
+ }
60
+ if (line.includes("|") && i + 1 < lines.length && /^\s*\|?[\s-:|]+\|/.test(lines[i + 1])) {
61
+ closeList();
62
+ const headers = line.split("|").map((h) => h.trim()).filter(Boolean);
63
+ i += 2;
64
+ output.push('<div class="tangle-table-wrap"><table class="tangle-table">');
65
+ output.push("<thead><tr>");
66
+ for (const h of headers) output.push(`<th>${renderInline(h)}</th>`);
67
+ output.push("</tr></thead><tbody>");
68
+ while (i < lines.length && lines[i].includes("|")) {
69
+ const cells = lines[i].split("|").map((c) => c.trim()).filter(Boolean);
70
+ output.push("<tr>");
71
+ for (const c of cells) output.push(`<td>${renderInline(c)}</td>`);
72
+ output.push("</tr>");
73
+ i++;
74
+ }
75
+ output.push("</tbody></table></div>");
76
+ continue;
77
+ }
78
+ const headingMatch = line.match(/^(#{1,6})\s+(.+)/);
79
+ if (headingMatch) {
80
+ closeList();
81
+ const level = headingMatch[1].length;
82
+ output.push(`<h${level} class="tangle-h${level}">${renderInline(headingMatch[2])}</h${level}>`);
83
+ i++;
84
+ continue;
85
+ }
86
+ if (line.startsWith("> ")) {
87
+ closeList();
88
+ const quoteLines = [];
89
+ while (i < lines.length && lines[i].startsWith("> ")) {
90
+ quoteLines.push(lines[i].slice(2));
91
+ i++;
92
+ }
93
+ output.push(`<blockquote class="tangle-blockquote">${quoteLines.map(renderInline).join("<br>")}</blockquote>`);
94
+ continue;
95
+ }
96
+ if (/^[\s]*[-*+]\s/.test(line)) {
97
+ if (!inList || listType !== "ul") {
98
+ closeList();
99
+ output.push('<ul class="tangle-list">');
100
+ inList = true;
101
+ listType = "ul";
102
+ }
103
+ output.push(`<li>${renderInline(line.replace(/^[\s]*[-*+]\s/, ""))}</li>`);
104
+ i++;
105
+ continue;
106
+ }
107
+ if (/^[\s]*\d+\.\s/.test(line)) {
108
+ if (!inList || listType !== "ol") {
109
+ closeList();
110
+ output.push('<ol class="tangle-list tangle-list-ordered">');
111
+ inList = true;
112
+ listType = "ol";
113
+ }
114
+ output.push(`<li>${renderInline(line.replace(/^[\s]*\d+\.\s/, ""))}</li>`);
115
+ i++;
116
+ continue;
117
+ }
118
+ if (/^[-*_]{3,}\s*$/.test(line)) {
119
+ closeList();
120
+ output.push('<hr class="tangle-hr">');
121
+ i++;
122
+ continue;
123
+ }
124
+ if (!line.trim()) {
125
+ closeList();
126
+ i++;
127
+ continue;
128
+ }
129
+ closeList();
130
+ output.push(`<p>${renderInline(line)}</p>`);
131
+ i++;
132
+ }
133
+ closeList();
134
+ return output.join("\n");
135
+ }
136
+ var simpleMarkdownStyles = `
137
+ .tangle-prose {
138
+ font-family: var(--font-sans);
139
+ color: var(--text-primary);
140
+ font-size: 0.9375rem;
141
+ line-height: 1.6;
142
+ }
143
+ .tangle-prose p { margin: 0.5em 0; }
144
+ .tangle-prose p:last-child { margin-bottom: 0; }
145
+ .tangle-h1 { font-size: 1.5rem; font-weight: 700; margin: 1em 0 0.5em; }
146
+ .tangle-h2 { font-size: 1.25rem; font-weight: 700; margin: 1em 0 0.5em; }
147
+ .tangle-h3 { font-size: 1.1rem; font-weight: 600; margin: 0.75em 0 0.4em; }
148
+ .tangle-h4, .tangle-h5, .tangle-h6 { font-size: 1rem; font-weight: 600; margin: 0.5em 0 0.3em; }
149
+ .tangle-inline-code {
150
+ font-family: var(--font-mono);
151
+ font-size: 0.85em;
152
+ background: var(--bg-input);
153
+ border: 1px solid var(--border-subtle);
154
+ border-radius: 4px;
155
+ padding: 0.15em 0.4em;
156
+ }
157
+ .tangle-code-block {
158
+ position: relative;
159
+ margin: 0.75em 0;
160
+ background: var(--bg-input);
161
+ border: 1px solid var(--border-subtle);
162
+ border-radius: var(--radius-md);
163
+ overflow: hidden;
164
+ }
165
+ .tangle-code-lang {
166
+ position: absolute;
167
+ top: 8px;
168
+ right: 12px;
169
+ font-family: var(--font-mono);
170
+ font-size: 11px;
171
+ color: var(--text-muted);
172
+ text-transform: uppercase;
173
+ letter-spacing: 0.05em;
174
+ }
175
+ .tangle-code-block pre {
176
+ margin: 0;
177
+ padding: 1rem;
178
+ overflow-x: auto;
179
+ font-family: var(--font-mono);
180
+ font-size: 13px;
181
+ line-height: 1.55;
182
+ color: var(--text-secondary);
183
+ }
184
+ .tangle-link {
185
+ color: var(--brand-cool);
186
+ text-decoration: none;
187
+ }
188
+ .tangle-link:hover { text-decoration: underline; }
189
+ .tangle-blockquote {
190
+ border-left: 3px solid var(--border-accent);
191
+ padding: 0.5em 1em;
192
+ margin: 0.5em 0;
193
+ color: var(--text-secondary);
194
+ }
195
+ .tangle-list {
196
+ margin: 0.5em 0;
197
+ padding-left: 1.5em;
198
+ }
199
+ .tangle-list li { margin: 0.2em 0; }
200
+ .tangle-list-ordered { list-style-type: decimal; }
201
+ .tangle-table-wrap { overflow-x: auto; margin: 0.75em 0; }
202
+ .tangle-table {
203
+ width: 100%;
204
+ border-collapse: collapse;
205
+ font-size: 0.875rem;
206
+ }
207
+ .tangle-table th {
208
+ text-align: left;
209
+ padding: 0.5em 0.75em;
210
+ border-bottom: 2px solid var(--border-default);
211
+ font-weight: 600;
212
+ color: var(--text-secondary);
213
+ white-space: nowrap;
214
+ }
215
+ .tangle-table td {
216
+ padding: 0.4em 0.75em;
217
+ border-bottom: 1px solid var(--border-subtle);
218
+ color: var(--text-secondary);
219
+ }
220
+ .tangle-table tr:hover td { background: var(--bg-hover); }
221
+ .tangle-hr {
222
+ border: none;
223
+ border-top: 1px solid var(--border-subtle);
224
+ margin: 1em 0;
225
+ }
226
+ strong { font-weight: 700; }
227
+ em { font-style: italic; }
228
+ `;
229
+
230
+ export {
231
+ SimpleMarkdown,
232
+ simpleMarkdownStyles
233
+ };