@tangle-network/ui 11.7.0 → 11.9.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 (41) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/chat.js +10 -9
  3. package/dist/{chunk-3M7BPSSW.js → chunk-3TI5DEHV.js} +1 -1
  4. package/dist/chunk-4CLN43XT.js +45 -0
  5. package/dist/{chunk-UYTRNR7Z.js → chunk-565EATZZ.js} +1 -1
  6. package/dist/{chunk-VDCZHOOO.js → chunk-7YOCS6IF.js} +6 -6
  7. package/dist/{chunk-P5F43A65.js → chunk-EOAX2ENE.js} +2 -1
  8. package/dist/{chunk-XT4JMYVB.js → chunk-KNYSKNZX.js} +5 -3
  9. package/dist/{chunk-EIUE5GV2.js → chunk-ND3ZFXQK.js} +1 -44
  10. package/dist/{chunk-AAUNOHVL.js → chunk-NWECTSOH.js} +1 -1
  11. package/dist/{chunk-CVX53NCC.js → chunk-TN4FUEWY.js} +7 -7
  12. package/dist/{chunk-4ENFLPWQ.js → chunk-VPWY4I5O.js} +1 -1
  13. package/dist/{chunk-ZRVH3WCA.js → chunk-XU3HB5QL.js} +21 -1
  14. package/dist/{chunk-6RIX7M27.js → chunk-XYAIX4SU.js} +2 -2
  15. package/dist/{chunk-PLWRAS5Z.js → chunk-YJ6RE5WV.js} +272 -108
  16. package/dist/{chunk-LFVF2HFB.js → chunk-YTO5V6QS.js} +1 -1
  17. package/dist/{chunk-QGTIBEPX.js → chunk-ZKCEAIIZ.js} +1 -1
  18. package/dist/{document-editor-pane-A3JURPW5.js → document-editor-pane-EX25CEYW.js} +2 -2
  19. package/dist/editor.js +2 -2
  20. package/dist/files.d.ts +41 -12
  21. package/dist/files.js +14 -6
  22. package/dist/hooks.js +9 -8
  23. package/dist/index.d.ts +2 -1
  24. package/dist/index.js +32 -24
  25. package/dist/markdown.d.ts +4 -1
  26. package/dist/markdown.js +1 -1
  27. package/dist/openui.js +2 -2
  28. package/dist/run.js +10 -9
  29. package/dist/sdk-hooks.js +9 -8
  30. package/dist/tool-previews.js +2 -2
  31. package/dist/utils.js +6 -4
  32. package/package.json +3 -3
  33. package/src/files/file-artifact-pane.stories.tsx +149 -0
  34. package/src/files/file-artifact-pane.test.tsx +192 -0
  35. package/src/files/file-artifact-pane.tsx +6 -1
  36. package/src/files/file-format.ts +42 -4
  37. package/src/files/file-preview-kind.test.ts +109 -0
  38. package/src/files/file-preview.tsx +315 -132
  39. package/src/files/index.ts +8 -1
  40. package/src/markdown/markdown.test.tsx +26 -2
  41. package/src/markdown/markdown.tsx +5 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @tangle-network/ui
2
2
 
3
+ ## 11.9.0
4
+
5
+ ### Minor Changes
6
+
7
+ - b047084: Expose react-markdown's typed `urlTransform` hook through the shared Markdown component.
8
+
9
+ ## 11.8.0
10
+
11
+ ### Minor Changes
12
+
13
+ - 3416c80: `FileArtifactPane` (and `FilePreview`, its body) now previews every file kind a sandbox produces, so a product no longer builds its own image, PDF, CSV, video, audio, or spreadsheet viewers. Pass `blobUrl` (an object URL or remote URL) for binary content and the pane renders an image fit to the pane on a checker ground (click for natural size), a PDF at full pane height, or a video/audio player; pass `content` for CSV and the pane parses quoted commas and line breaks (RFC 4180) into a sticky-header table capped at `CSV_PREVIEW_ROW_LIMIT` (500) rows with a "Showing N of M rows" line; an `.xlsx`/`.xls` file renders a download card with the filename, its size when the new optional `size` prop (bytes) is set, and a Download button wired to `onDownload`. A file with no `blobUrl` and no text content renders one sentence, "This file needs a download link to preview.", with the same Download button. Text, code, JSON, YAML, and markdown render as before. `@tangle-network/ui/files` also exports the pure `resolveFilePreviewKind(filename, mimeType)` helper (`FilePreviewKind`: MIME type wins over extension; anything unmatched is `binary`), `parseCsv`, and `CSV_PREVIEW_ROW_LIMIT`; `detectFileFormat` gains the `video` and `audio` formats and the `avif` image extension.
14
+
3
15
  ## 11.7.0
4
16
 
5
17
  ### Minor Changes
package/dist/chat.js CHANGED
@@ -5,18 +5,19 @@ import {
5
5
  MessageList,
6
6
  ThinkingIndicator,
7
7
  UserMessage
8
- } from "./chunk-VDCZHOOO.js";
9
- import "./chunk-UYTRNR7Z.js";
10
- import "./chunk-CVX53NCC.js";
11
- import "./chunk-XT4JMYVB.js";
12
- import "./chunk-EIUE5GV2.js";
13
- import "./chunk-AAUNOHVL.js";
8
+ } from "./chunk-7YOCS6IF.js";
9
+ import "./chunk-565EATZZ.js";
10
+ import "./chunk-TN4FUEWY.js";
11
+ import "./chunk-KNYSKNZX.js";
12
+ import "./chunk-ND3ZFXQK.js";
13
+ import "./chunk-NWECTSOH.js";
14
14
  import "./chunk-L57WRIND.js";
15
- import "./chunk-3M7BPSSW.js";
15
+ import "./chunk-3TI5DEHV.js";
16
16
  import "./chunk-GYPQXTJU.js";
17
+ import "./chunk-4CLN43XT.js";
18
+ import "./chunk-XU3HB5QL.js";
17
19
  import "./chunk-MKTSMWVD.js";
18
- import "./chunk-ZRVH3WCA.js";
19
- import "./chunk-P5F43A65.js";
20
+ import "./chunk-EOAX2ENE.js";
20
21
  import "./chunk-WUQDUBJG.js";
21
22
  import "./chunk-RQHJBTEU.js";
22
23
  export {
@@ -17,7 +17,7 @@ import {
17
17
  } from "./chunk-MKTSMWVD.js";
18
18
  import {
19
19
  Markdown
20
- } from "./chunk-P5F43A65.js";
20
+ } from "./chunk-EOAX2ENE.js";
21
21
  import {
22
22
  CodeBlock
23
23
  } from "./chunk-WUQDUBJG.js";
@@ -0,0 +1,45 @@
1
+ // src/utils/format.ts
2
+ function formatDuration(ms) {
3
+ if (ms < 1e3) return "<1s";
4
+ const seconds = Math.floor(ms / 1e3);
5
+ if (seconds < 60) return `${seconds}s`;
6
+ const minutes = Math.floor(seconds / 60);
7
+ const remaining = seconds % 60;
8
+ return remaining > 0 ? `${minutes}m ${remaining}s` : `${minutes}m`;
9
+ }
10
+ function truncateText(text, max) {
11
+ const cleaned = text.replace(/\s+/g, " ").trim();
12
+ if (cleaned.length <= max) return cleaned;
13
+ return cleaned.slice(0, max).trim() + "...";
14
+ }
15
+ function formatUptime(ms) {
16
+ if (!Number.isFinite(ms) || ms < 0) return "\u2014";
17
+ const totalSeconds = Math.floor(ms / 1e3);
18
+ if (totalSeconds < 60) return `${totalSeconds}s`;
19
+ const minutes = Math.floor(totalSeconds / 60);
20
+ const seconds = totalSeconds % 60;
21
+ if (minutes < 60) return `${minutes}m ${seconds}s`;
22
+ const hours = Math.floor(minutes / 60);
23
+ const remMinutes = minutes % 60;
24
+ if (hours < 24) return `${hours}h ${remMinutes}m`;
25
+ const days = Math.floor(hours / 24);
26
+ const remHours = hours % 24;
27
+ return `${days}d ${remHours}h`;
28
+ }
29
+ function formatBytes(bytes) {
30
+ if (!Number.isFinite(bytes) || bytes < 0) return "\u2014";
31
+ if (bytes < 1024) return `${Math.round(bytes)} B`;
32
+ const kb = bytes / 1024;
33
+ if (kb < 1024) return `${kb < 10 ? kb.toFixed(1) : Math.round(kb)} KB`;
34
+ const mb = kb / 1024;
35
+ if (mb < 1024) return `${mb < 10 ? mb.toFixed(1) : Math.round(mb)} MB`;
36
+ const gb = mb / 1024;
37
+ return `${gb < 10 ? gb.toFixed(2) : gb.toFixed(1)} GB`;
38
+ }
39
+
40
+ export {
41
+ formatDuration,
42
+ truncateText,
43
+ formatUptime,
44
+ formatBytes
45
+ };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getToolCategory
3
- } from "./chunk-EIUE5GV2.js";
3
+ } from "./chunk-ND3ZFXQK.js";
4
4
 
5
5
  // src/hooks/use-run-groups.ts
6
6
  import { useMemo } from "react";
@@ -2,24 +2,24 @@ import {
2
2
  useAutoScroll,
3
3
  useRunCollapseState,
4
4
  useRunGroups
5
- } from "./chunk-UYTRNR7Z.js";
5
+ } from "./chunk-565EATZZ.js";
6
6
  import {
7
7
  InlineThinkingItem,
8
8
  RunGroup
9
- } from "./chunk-CVX53NCC.js";
9
+ } from "./chunk-TN4FUEWY.js";
10
10
  import {
11
11
  ToolCallGroup,
12
12
  ToolCallStep
13
- } from "./chunk-XT4JMYVB.js";
13
+ } from "./chunk-KNYSKNZX.js";
14
14
  import {
15
15
  getToolDisplayMetadata
16
- } from "./chunk-EIUE5GV2.js";
16
+ } from "./chunk-ND3ZFXQK.js";
17
17
  import {
18
18
  OpenUIArtifactRenderer
19
- } from "./chunk-3M7BPSSW.js";
19
+ } from "./chunk-3TI5DEHV.js";
20
20
  import {
21
21
  Markdown
22
- } from "./chunk-P5F43A65.js";
22
+ } from "./chunk-EOAX2ENE.js";
23
23
  import {
24
24
  cn
25
25
  } from "./chunk-RQHJBTEU.js";
@@ -12,7 +12,7 @@ import ReactMarkdown from "react-markdown";
12
12
  import remarkGfm from "remark-gfm";
13
13
  import rehypeSanitize from "rehype-sanitize";
14
14
  import { Fragment, jsx } from "react/jsx-runtime";
15
- var Markdown = memo(({ children, className }) => {
15
+ var Markdown = memo(({ children, className, urlTransform }) => {
16
16
  return /* @__PURE__ */ jsx(
17
17
  "div",
18
18
  {
@@ -22,6 +22,7 @@ var Markdown = memo(({ children, className }) => {
22
22
  {
23
23
  remarkPlugins: [remarkGfm],
24
24
  rehypePlugins: [rehypeSanitize],
25
+ urlTransform,
25
26
  components: {
26
27
  pre({ children: preChildren }) {
27
28
  return /* @__PURE__ */ jsx(Fragment, { children: preChildren });
@@ -1,9 +1,8 @@
1
1
  import {
2
- formatDuration,
3
2
  getToolCategory,
4
3
  getToolDisplayMetadata,
5
4
  getToolErrorText
6
- } from "./chunk-EIUE5GV2.js";
5
+ } from "./chunk-ND3ZFXQK.js";
7
6
  import {
8
7
  CommandPreview,
9
8
  DiffPreview,
@@ -12,10 +11,13 @@ import {
12
11
  QuestionPreview,
13
12
  WebSearchPreview,
14
13
  WriteFilePreview
15
- } from "./chunk-AAUNOHVL.js";
14
+ } from "./chunk-NWECTSOH.js";
16
15
  import {
17
16
  TextShimmer
18
17
  } from "./chunk-L57WRIND.js";
18
+ import {
19
+ formatDuration
20
+ } from "./chunk-4CLN43XT.js";
19
21
  import {
20
22
  CodeBlock
21
23
  } from "./chunk-WUQDUBJG.js";
@@ -171,52 +171,9 @@ function getToolErrorText(part, fallback) {
171
171
  return part.state.error || fallback;
172
172
  }
173
173
 
174
- // src/utils/format.ts
175
- function formatDuration(ms) {
176
- if (ms < 1e3) return "<1s";
177
- const seconds = Math.floor(ms / 1e3);
178
- if (seconds < 60) return `${seconds}s`;
179
- const minutes = Math.floor(seconds / 60);
180
- const remaining = seconds % 60;
181
- return remaining > 0 ? `${minutes}m ${remaining}s` : `${minutes}m`;
182
- }
183
- function truncateText(text, max) {
184
- const cleaned = text.replace(/\s+/g, " ").trim();
185
- if (cleaned.length <= max) return cleaned;
186
- return cleaned.slice(0, max).trim() + "...";
187
- }
188
- function formatUptime(ms) {
189
- if (!Number.isFinite(ms) || ms < 0) return "\u2014";
190
- const totalSeconds = Math.floor(ms / 1e3);
191
- if (totalSeconds < 60) return `${totalSeconds}s`;
192
- const minutes = Math.floor(totalSeconds / 60);
193
- const seconds = totalSeconds % 60;
194
- if (minutes < 60) return `${minutes}m ${seconds}s`;
195
- const hours = Math.floor(minutes / 60);
196
- const remMinutes = minutes % 60;
197
- if (hours < 24) return `${hours}h ${remMinutes}m`;
198
- const days = Math.floor(hours / 24);
199
- const remHours = hours % 24;
200
- return `${days}d ${remHours}h`;
201
- }
202
- function formatBytes(bytes) {
203
- if (!Number.isFinite(bytes) || bytes < 0) return "\u2014";
204
- if (bytes < 1024) return `${Math.round(bytes)} B`;
205
- const kb = bytes / 1024;
206
- if (kb < 1024) return `${kb < 10 ? kb.toFixed(1) : Math.round(kb)} KB`;
207
- const mb = kb / 1024;
208
- if (mb < 1024) return `${mb < 10 ? mb.toFixed(1) : Math.round(mb)} MB`;
209
- const gb = mb / 1024;
210
- return `${gb < 10 ? gb.toFixed(2) : gb.toFixed(1)} GB`;
211
- }
212
-
213
174
  export {
214
175
  TOOL_CATEGORY_ICONS,
215
176
  getToolCategory,
216
177
  getToolDisplayMetadata,
217
- getToolErrorText,
218
- formatDuration,
219
- truncateText,
220
- formatUptime,
221
- formatBytes
178
+ getToolErrorText
222
179
  };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getSyntaxLanguage
3
- } from "./chunk-ZRVH3WCA.js";
3
+ } from "./chunk-XU3HB5QL.js";
4
4
  import {
5
5
  CodeBlock,
6
6
  CopyButton
@@ -2,20 +2,20 @@ import {
2
2
  ExpandableContent,
3
3
  InlineToolItem,
4
4
  RunRowShell
5
- } from "./chunk-XT4JMYVB.js";
6
- import {
7
- formatDuration,
8
- truncateText
9
- } from "./chunk-EIUE5GV2.js";
5
+ } from "./chunk-KNYSKNZX.js";
10
6
  import {
11
7
  TextShimmer
12
8
  } from "./chunk-L57WRIND.js";
13
9
  import {
14
10
  OpenUIArtifactRenderer
15
- } from "./chunk-3M7BPSSW.js";
11
+ } from "./chunk-3TI5DEHV.js";
12
+ import {
13
+ formatDuration,
14
+ truncateText
15
+ } from "./chunk-4CLN43XT.js";
16
16
  import {
17
17
  Markdown
18
- } from "./chunk-P5F43A65.js";
18
+ } from "./chunk-EOAX2ENE.js";
19
19
  import {
20
20
  cn
21
21
  } from "./chunk-RQHJBTEU.js";
@@ -11,7 +11,7 @@ import {
11
11
  } from "./chunk-OEX7NZE3.js";
12
12
  import {
13
13
  parseToolEvent
14
- } from "./chunk-6RIX7M27.js";
14
+ } from "./chunk-XYAIX4SU.js";
15
15
 
16
16
  // src/hooks/use-dropdown-menu.ts
17
17
  import { useEffect, useRef, useState } from "react";
@@ -1,5 +1,11 @@
1
1
  // src/files/file-format.ts
2
- var IMAGE_EXTENSIONS = ["png", "jpg", "jpeg", "gif", "svg", "webp"];
2
+ var IMAGE_EXTENSIONS = ["png", "jpg", "jpeg", "gif", "svg", "webp", "avif"];
3
+ var VIDEO_EXTENSIONS = ["mp4", "webm", "mov"];
4
+ var AUDIO_EXTENSIONS = ["mp3", "wav", "ogg", "m4a"];
5
+ var SPREADSHEET_MIME_TYPES = [
6
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
7
+ "application/vnd.ms-excel"
8
+ ];
3
9
  var EXTENSION_TO_SYNTAX_LANGUAGE = {
4
10
  ts: "typescript",
5
11
  tsx: "tsx",
@@ -50,12 +56,17 @@ function detectFileFormat(filename, mimeType) {
50
56
  const mime = mimeEssence(mimeType);
51
57
  if (mime === "application/pdf") return "pdf";
52
58
  if (mime.startsWith("image/")) return "image";
59
+ if (mime.startsWith("video/")) return "video";
60
+ if (mime.startsWith("audio/")) return "audio";
53
61
  if (mime === "text/markdown") return "markdown";
54
62
  if (mime === "application/json") return "json";
55
63
  if (mime === "text/csv" || mime === "application/csv") return "csv";
64
+ if (SPREADSHEET_MIME_TYPES.includes(mime)) return "spreadsheet";
56
65
  if (mime === "application/yaml" || mime === "application/x-yaml" || mime === "text/yaml") return "yaml";
57
66
  if (ext === "pdf") return "pdf";
58
67
  if (IMAGE_EXTENSIONS.includes(ext)) return "image";
68
+ if (VIDEO_EXTENSIONS.includes(ext)) return "video";
69
+ if (AUDIO_EXTENSIONS.includes(ext)) return "audio";
59
70
  if (ext === "csv") return "csv";
60
71
  if (ext === "xlsx" || ext === "xls") return "spreadsheet";
61
72
  if (CODE_EXTENSIONS.has(ext)) return "code";
@@ -66,12 +77,20 @@ function detectFileFormat(filename, mimeType) {
66
77
  if (mime === "text/plain") return "text";
67
78
  return "unknown";
68
79
  }
80
+ function resolveFilePreviewKind(filename, mimeType) {
81
+ const format = detectFileFormat(filename, mimeType);
82
+ return format === "unknown" ? "binary" : format;
83
+ }
69
84
  function getFormatLabel(format) {
70
85
  switch (format) {
71
86
  case "pdf":
72
87
  return "PDF";
73
88
  case "image":
74
89
  return "Image";
90
+ case "video":
91
+ return "Video";
92
+ case "audio":
93
+ return "Audio";
75
94
  case "csv":
76
95
  return "CSV";
77
96
  case "spreadsheet":
@@ -101,6 +120,7 @@ function getCodeLanguage(filename, format) {
101
120
  export {
102
121
  fileExtension,
103
122
  detectFileFormat,
123
+ resolveFilePreviewKind,
104
124
  getFormatLabel,
105
125
  getSyntaxLanguage,
106
126
  getCodeLanguage
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  ToolCallGroup,
3
3
  ToolCallStep
4
- } from "./chunk-XT4JMYVB.js";
4
+ } from "./chunk-KNYSKNZX.js";
5
5
  import {
6
6
  Markdown
7
- } from "./chunk-P5F43A65.js";
7
+ } from "./chunk-EOAX2ENE.js";
8
8
  import {
9
9
  cn
10
10
  } from "./chunk-RQHJBTEU.js";