@tangle-network/ui 3.0.0 → 4.1.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.
- package/CHANGELOG.md +13 -0
- package/dist/chat.js +8 -7
- package/dist/{chunk-TMFOPHHN.js → chunk-52Y3FMFI.js} +2 -2
- package/dist/{chunk-7UO2ZMRQ.js → chunk-5VPTNXX7.js} +2 -2
- package/dist/{chunk-XIHMJ7ZQ.js → chunk-AAUNOHVL.js} +5 -30
- package/dist/{chunk-YJ2G3XO5.js → chunk-CMX2I43A.js} +1 -1
- package/dist/{chunk-2VH6PUXD.js → chunk-DGW77LD7.js} +1 -1
- package/dist/{chunk-CD53GZOM.js → chunk-FJBTCTZM.js} +1 -1
- package/dist/{chunk-YNN4O57I.js → chunk-JBPWIYTQ.js} +4 -4
- package/dist/{chunk-2NFQRQOD.js → chunk-KT5RNO7N.js} +4 -4
- package/dist/{chunk-HJKCSXCH.js → chunk-LELGOLFV.js} +44 -78
- package/dist/{chunk-EEE55AVS.js → chunk-SZ44QDA6.js} +1 -1
- package/dist/{chunk-66BNMOVT.js → chunk-WUQDUBJG.js} +5 -4
- package/dist/chunk-ZRVH3WCA.js +107 -0
- package/dist/{code-block-DjXf8eOG.d.ts → code-block-0kSpWMnf.d.ts} +7 -1
- package/dist/{document-editor-pane-A5LT5H4N.js → document-editor-pane-WCTA3ZOE.js} +3 -3
- package/dist/editor.js +3 -3
- package/dist/files.d.ts +39 -2
- package/dist/files.js +16 -4
- package/dist/hooks.js +5 -5
- package/dist/index.d.ts +2 -2
- package/dist/index.js +22 -10
- package/dist/markdown.d.ts +1 -1
- package/dist/markdown.js +2 -2
- package/dist/openui.js +3 -3
- package/dist/primitives.d.ts +1 -1
- package/dist/primitives.js +1 -1
- package/dist/run.js +8 -7
- package/dist/sdk-hooks.js +5 -5
- package/dist/tool-previews.js +3 -2
- package/package.json +2 -2
- package/src/files/file-artifact-pane.tsx +3 -3
- package/src/files/file-format.test.ts +176 -0
- package/src/files/file-format.ts +167 -0
- package/src/files/file-preview.stories.tsx +87 -0
- package/src/files/file-preview.test.tsx +52 -0
- package/src/files/file-preview.tsx +48 -94
- package/src/files/index.ts +8 -0
- package/src/markdown/code-block.test.tsx +62 -0
- package/src/markdown/code-block.tsx +11 -4
- package/src/tool-previews/write-file-preview.tsx +2 -30
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @tangle-network/ui
|
|
2
2
|
|
|
3
|
+
## 4.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- d7a442d: Share theme-aware file-format rendering across the preview and artifact surfaces. `FilePreview` now routes `code`/`json`/`yaml` through the same theme-aware `CodeBlock` the chat markdown renderer uses, so code is syntax-highlighted and theme-consistent in the artifact pane instead of monochrome. A new `files/file-format` module (`detectFileFormat`, `getFormatLabel`, `getSyntaxLanguage`, `fileExtension`) is the single source of truth for extension/MIME detection, consumed by `FilePreview`, `FileArtifactPane`, and `WriteFilePreview`. `CodeBlock` gains an optional `label` prop to display a header name independent of the highlight language.
|
|
8
|
+
|
|
9
|
+
## 4.0.0
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- Updated dependencies [184c8bb]
|
|
14
|
+
- @tangle-network/brand@0.5.0
|
|
15
|
+
|
|
3
16
|
## 3.0.0
|
|
4
17
|
|
|
5
18
|
### Patch Changes
|
package/dist/chat.js
CHANGED
|
@@ -6,18 +6,19 @@ import {
|
|
|
6
6
|
MessageList,
|
|
7
7
|
ThinkingIndicator,
|
|
8
8
|
UserMessage
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-KT5RNO7N.js";
|
|
10
10
|
import "./chunk-54SQQMMM.js";
|
|
11
|
-
import "./chunk-
|
|
12
|
-
import "./chunk-
|
|
11
|
+
import "./chunk-JBPWIYTQ.js";
|
|
12
|
+
import "./chunk-DGW77LD7.js";
|
|
13
13
|
import "./chunk-4CLN43XT.js";
|
|
14
14
|
import "./chunk-BX6AQMUS.js";
|
|
15
|
-
import "./chunk-
|
|
16
|
-
import "./chunk-
|
|
15
|
+
import "./chunk-AAUNOHVL.js";
|
|
16
|
+
import "./chunk-52Y3FMFI.js";
|
|
17
17
|
import "./chunk-GYPQXTJU.js";
|
|
18
18
|
import "./chunk-MKTSMWVD.js";
|
|
19
|
-
import "./chunk-
|
|
20
|
-
import "./chunk-
|
|
19
|
+
import "./chunk-ZRVH3WCA.js";
|
|
20
|
+
import "./chunk-FJBTCTZM.js";
|
|
21
|
+
import "./chunk-WUQDUBJG.js";
|
|
21
22
|
import "./chunk-RQHJBTEU.js";
|
|
22
23
|
export {
|
|
23
24
|
AgentTimeline,
|
|
@@ -17,10 +17,10 @@ import {
|
|
|
17
17
|
} from "./chunk-MKTSMWVD.js";
|
|
18
18
|
import {
|
|
19
19
|
Markdown
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-FJBTCTZM.js";
|
|
21
21
|
import {
|
|
22
22
|
CodeBlock
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-WUQDUBJG.js";
|
|
24
24
|
import {
|
|
25
25
|
cn
|
|
26
26
|
} from "./chunk-RQHJBTEU.js";
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getSyntaxLanguage
|
|
3
|
+
} from "./chunk-ZRVH3WCA.js";
|
|
1
4
|
import {
|
|
2
5
|
CodeBlock,
|
|
3
6
|
CopyButton
|
|
4
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-WUQDUBJG.js";
|
|
5
8
|
import {
|
|
6
9
|
cn
|
|
7
10
|
} from "./chunk-RQHJBTEU.js";
|
|
@@ -153,39 +156,11 @@ function extractWriteContent(input) {
|
|
|
153
156
|
const content = String(obj.content ?? obj.contents ?? obj.data ?? "");
|
|
154
157
|
return { path, content };
|
|
155
158
|
}
|
|
156
|
-
function getLanguageFromPath(path) {
|
|
157
|
-
const ext = path.split(".").pop()?.toLowerCase();
|
|
158
|
-
const map = {
|
|
159
|
-
ts: "typescript",
|
|
160
|
-
tsx: "tsx",
|
|
161
|
-
js: "javascript",
|
|
162
|
-
jsx: "jsx",
|
|
163
|
-
rs: "rust",
|
|
164
|
-
py: "python",
|
|
165
|
-
go: "go",
|
|
166
|
-
rb: "ruby",
|
|
167
|
-
json: "json",
|
|
168
|
-
yaml: "yaml",
|
|
169
|
-
yml: "yaml",
|
|
170
|
-
toml: "toml",
|
|
171
|
-
md: "markdown",
|
|
172
|
-
css: "css",
|
|
173
|
-
scss: "scss",
|
|
174
|
-
html: "html",
|
|
175
|
-
sh: "bash",
|
|
176
|
-
bash: "bash",
|
|
177
|
-
zsh: "bash",
|
|
178
|
-
sql: "sql",
|
|
179
|
-
sol: "solidity",
|
|
180
|
-
proto: "protobuf"
|
|
181
|
-
};
|
|
182
|
-
return ext ? map[ext] : void 0;
|
|
183
|
-
}
|
|
184
159
|
var WriteFilePreview = memo2(({ part }) => {
|
|
185
160
|
const write = extractWriteContent(part.state.input);
|
|
186
161
|
if (!write) return null;
|
|
187
162
|
const lineCount = write.content.split("\n").length;
|
|
188
|
-
const language =
|
|
163
|
+
const language = getSyntaxLanguage(write.path);
|
|
189
164
|
return /* @__PURE__ */ jsxs3(
|
|
190
165
|
PreviewCard,
|
|
191
166
|
{
|
|
@@ -15,16 +15,16 @@ import {
|
|
|
15
15
|
QuestionPreview,
|
|
16
16
|
WebSearchPreview,
|
|
17
17
|
WriteFilePreview
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-AAUNOHVL.js";
|
|
19
19
|
import {
|
|
20
20
|
OpenUIArtifactRenderer
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-52Y3FMFI.js";
|
|
22
22
|
import {
|
|
23
23
|
Markdown
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-FJBTCTZM.js";
|
|
25
25
|
import {
|
|
26
26
|
CodeBlock
|
|
27
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-WUQDUBJG.js";
|
|
28
28
|
import {
|
|
29
29
|
cn
|
|
30
30
|
} from "./chunk-RQHJBTEU.js";
|
|
@@ -6,20 +6,20 @@ import {
|
|
|
6
6
|
import {
|
|
7
7
|
InlineThinkingItem,
|
|
8
8
|
RunGroup
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-JBPWIYTQ.js";
|
|
10
10
|
import {
|
|
11
11
|
ToolCallGroup,
|
|
12
12
|
ToolCallStep
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-DGW77LD7.js";
|
|
14
14
|
import {
|
|
15
15
|
getToolDisplayMetadata
|
|
16
16
|
} from "./chunk-BX6AQMUS.js";
|
|
17
17
|
import {
|
|
18
18
|
OpenUIArtifactRenderer
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-52Y3FMFI.js";
|
|
20
20
|
import {
|
|
21
21
|
Markdown
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-FJBTCTZM.js";
|
|
23
23
|
import {
|
|
24
24
|
cn
|
|
25
25
|
} from "./chunk-RQHJBTEU.js";
|
|
@@ -1,9 +1,19 @@
|
|
|
1
|
+
import {
|
|
2
|
+
detectFileFormat,
|
|
3
|
+
fileExtension,
|
|
4
|
+
getCodeLanguage,
|
|
5
|
+
getFormatLabel
|
|
6
|
+
} from "./chunk-ZRVH3WCA.js";
|
|
1
7
|
import {
|
|
2
8
|
ArtifactPane
|
|
3
9
|
} from "./chunk-CSAIKY36.js";
|
|
4
10
|
import {
|
|
5
11
|
Markdown
|
|
6
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-FJBTCTZM.js";
|
|
13
|
+
import {
|
|
14
|
+
CodeBlock,
|
|
15
|
+
CopyButton
|
|
16
|
+
} from "./chunk-WUQDUBJG.js";
|
|
7
17
|
import {
|
|
8
18
|
cn
|
|
9
19
|
} from "./chunk-RQHJBTEU.js";
|
|
@@ -311,69 +321,25 @@ import {
|
|
|
311
321
|
FileText as FileText2
|
|
312
322
|
} from "lucide-react";
|
|
313
323
|
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
314
|
-
function
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
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
|
-
] });
|
|
324
|
+
function CodePreview({
|
|
325
|
+
content,
|
|
326
|
+
filename,
|
|
327
|
+
format
|
|
328
|
+
}) {
|
|
329
|
+
const lineCount = content.split("\n").length;
|
|
330
|
+
const language = getCodeLanguage(filename, format);
|
|
331
|
+
const labelToken = fileExtension(filename) || language || "txt";
|
|
332
|
+
return /* @__PURE__ */ jsx3(
|
|
333
|
+
CodeBlock,
|
|
334
|
+
{
|
|
335
|
+
code: content,
|
|
336
|
+
language,
|
|
337
|
+
label: `${labelToken} \xB7 ${lineCount} lines`,
|
|
338
|
+
showLineNumbers: true,
|
|
339
|
+
className: "max-h-[70vh] overflow-auto",
|
|
340
|
+
children: /* @__PURE__ */ jsx3(CopyButton, { text: content })
|
|
341
|
+
}
|
|
342
|
+
);
|
|
377
343
|
}
|
|
378
344
|
function parseCsvRow(line) {
|
|
379
345
|
const cells = [];
|
|
@@ -478,9 +444,9 @@ function FilePreview({
|
|
|
478
444
|
hideHeader = false,
|
|
479
445
|
className
|
|
480
446
|
}) {
|
|
481
|
-
const
|
|
482
|
-
const previewLabel =
|
|
483
|
-
const hasRenderableSource = Boolean(content) || Boolean(blobUrl) ||
|
|
447
|
+
const format = detectFileFormat(filename, mimeType);
|
|
448
|
+
const previewLabel = getFormatLabel(format);
|
|
449
|
+
const hasRenderableSource = Boolean(content) || Boolean(blobUrl) || format === "unknown" || format === "spreadsheet";
|
|
484
450
|
return /* @__PURE__ */ jsxs2("div", { className: cn("flex flex-col h-full", className), children: [
|
|
485
451
|
!hideHeader && /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2 px-3 py-2 border-b border-border shrink-0", children: [
|
|
486
452
|
/* @__PURE__ */ jsxs2("div", { className: "min-w-0 flex-1", children: [
|
|
@@ -509,13 +475,13 @@ function FilePreview({
|
|
|
509
475
|
)
|
|
510
476
|
] }),
|
|
511
477
|
/* @__PURE__ */ jsxs2("div", { className: "flex-1 overflow-auto p-3", children: [
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
(
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
478
|
+
format === "pdf" && blobUrl && /* @__PURE__ */ jsx3(PdfPreview, { blobUrl, filename }),
|
|
479
|
+
format === "image" && blobUrl && /* @__PURE__ */ jsx3(ImagePreview, { src: blobUrl, filename }),
|
|
480
|
+
format === "csv" && typeof content === "string" && /* @__PURE__ */ jsx3(CsvPreview, { content }),
|
|
481
|
+
(format === "code" || format === "json" || format === "yaml") && typeof content === "string" && /* @__PURE__ */ jsx3(CodePreview, { content, filename, format }),
|
|
482
|
+
format === "text" && typeof content === "string" && /* @__PURE__ */ jsx3(TextPreview, { content }),
|
|
483
|
+
format === "markdown" && typeof content === "string" && /* @__PURE__ */ jsx3(MarkdownPreview, { content }),
|
|
484
|
+
format === "spreadsheet" && /* @__PURE__ */ jsx3(
|
|
519
485
|
UnsupportedPreview,
|
|
520
486
|
{
|
|
521
487
|
filename,
|
|
@@ -523,8 +489,8 @@ function FilePreview({
|
|
|
523
489
|
description: "Download the workbook or convert it to CSV when you need an inline preview."
|
|
524
490
|
}
|
|
525
491
|
),
|
|
526
|
-
|
|
527
|
-
|
|
492
|
+
format === "unknown" && typeof content !== "string" && /* @__PURE__ */ jsx3(EmptyPreview, { filename }),
|
|
493
|
+
format === "unknown" && typeof content === "string" && /* @__PURE__ */ jsx3(TextPreview, { content }),
|
|
528
494
|
!hasRenderableSource && typeof content !== "string" && /* @__PURE__ */ jsx3(
|
|
529
495
|
UnsupportedPreview,
|
|
530
496
|
{
|
|
@@ -594,7 +560,7 @@ import { lazy, Suspense } from "react";
|
|
|
594
560
|
import { Download as Download2, X as X3 } from "lucide-react";
|
|
595
561
|
import { Fragment, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
596
562
|
var LazyDocumentEditorPane = lazy(async () => {
|
|
597
|
-
const module = await import("./document-editor-pane-
|
|
563
|
+
const module = await import("./document-editor-pane-WCTA3ZOE.js");
|
|
598
564
|
return { default: module.DocumentEditorPane };
|
|
599
565
|
});
|
|
600
566
|
function FileArtifactPane({
|
|
@@ -626,7 +592,7 @@ function FileArtifactPane({
|
|
|
626
592
|
onClose: onTabClose
|
|
627
593
|
}
|
|
628
594
|
) : void 0;
|
|
629
|
-
const isMarkdown = mimeType === "
|
|
595
|
+
const isMarkdown = detectFileFormat(filename, mimeType) === "markdown" || (path ? detectFileFormat(path, mimeType) === "markdown" : false);
|
|
630
596
|
const isEditableMarkdown = isMarkdown && editor?.enabled;
|
|
631
597
|
const headerActions = /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
632
598
|
onDownload && /* @__PURE__ */ jsx5(
|
|
@@ -83,24 +83,25 @@ function useIsLightTheme() {
|
|
|
83
83
|
return isLight;
|
|
84
84
|
}
|
|
85
85
|
var CodeBlock = memo(
|
|
86
|
-
({ code, language, showLineNumbers = false, light: lightProp, className, children, ...props }) => {
|
|
86
|
+
({ code, language, label, showLineNumbers = false, light: lightProp, className, children, ...props }) => {
|
|
87
87
|
const isLight = useIsLightTheme();
|
|
88
88
|
const light = lightProp ?? isLight;
|
|
89
89
|
const theme = getSyntaxTheme();
|
|
90
90
|
const bg = "bg-card border-border";
|
|
91
91
|
const headerBg = light ? "bg-muted/50 border-border" : "bg-background border-border";
|
|
92
92
|
const langColor = "text-muted-foreground";
|
|
93
|
+
const headerLabel = label ?? language;
|
|
93
94
|
return /* @__PURE__ */ jsxs(
|
|
94
95
|
"div",
|
|
95
96
|
{
|
|
96
97
|
className: cn("group relative overflow-hidden rounded-lg border font-mono", bg, className),
|
|
97
98
|
...props,
|
|
98
99
|
children: [
|
|
99
|
-
|
|
100
|
-
/* @__PURE__ */ jsx("span", { className: cn("text-[calc(var(--font-size-xs)-1px)] font-mono font-medium uppercase tracking-widest", langColor), children:
|
|
100
|
+
headerLabel && /* @__PURE__ */ jsxs("div", { className: cn("flex items-center justify-between border-b px-3 py-1", headerBg), children: [
|
|
101
|
+
/* @__PURE__ */ jsx("span", { className: cn("text-[calc(var(--font-size-xs)-1px)] font-mono font-medium uppercase tracking-widest", langColor), children: headerLabel }),
|
|
101
102
|
children
|
|
102
103
|
] }),
|
|
103
|
-
!
|
|
104
|
+
!headerLabel && children && /* @__PURE__ */ jsx("div", { className: "absolute right-2 top-2 z-10 flex items-center gap-2 opacity-0 transition-opacity group-hover:opacity-100", children }),
|
|
104
105
|
/* @__PURE__ */ jsx(
|
|
105
106
|
SyntaxHighlighter,
|
|
106
107
|
{
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// src/files/file-format.ts
|
|
2
|
+
var IMAGE_EXTENSIONS = ["png", "jpg", "jpeg", "gif", "svg", "webp"];
|
|
3
|
+
var EXTENSION_TO_SYNTAX_LANGUAGE = {
|
|
4
|
+
ts: "typescript",
|
|
5
|
+
tsx: "tsx",
|
|
6
|
+
js: "javascript",
|
|
7
|
+
jsx: "jsx",
|
|
8
|
+
mjs: "javascript",
|
|
9
|
+
cjs: "javascript",
|
|
10
|
+
rs: "rust",
|
|
11
|
+
py: "python",
|
|
12
|
+
go: "go",
|
|
13
|
+
rb: "ruby",
|
|
14
|
+
json: "json",
|
|
15
|
+
yaml: "yaml",
|
|
16
|
+
yml: "yaml",
|
|
17
|
+
toml: "toml",
|
|
18
|
+
md: "markdown",
|
|
19
|
+
markdown: "markdown",
|
|
20
|
+
css: "css",
|
|
21
|
+
scss: "scss",
|
|
22
|
+
html: "html",
|
|
23
|
+
sh: "bash",
|
|
24
|
+
bash: "bash",
|
|
25
|
+
zsh: "bash",
|
|
26
|
+
bashrc: "bash",
|
|
27
|
+
bash_logout: "bash",
|
|
28
|
+
profile: "bash",
|
|
29
|
+
sql: "sql",
|
|
30
|
+
sol: "solidity",
|
|
31
|
+
proto: "protobuf"
|
|
32
|
+
};
|
|
33
|
+
var NON_CODE_SYNTAX_EXTENSIONS = /* @__PURE__ */ new Set(["json", "yaml", "yml", "md", "markdown"]);
|
|
34
|
+
var PLAIN_CODE_EXTENSIONS = ["env", "gitignore"];
|
|
35
|
+
var CODE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
36
|
+
...Object.keys(EXTENSION_TO_SYNTAX_LANGUAGE).filter((ext) => !NON_CODE_SYNTAX_EXTENSIONS.has(ext)),
|
|
37
|
+
...PLAIN_CODE_EXTENSIONS
|
|
38
|
+
]);
|
|
39
|
+
function fileExtension(filename) {
|
|
40
|
+
const base = filename.slice(filename.lastIndexOf("/") + 1);
|
|
41
|
+
const dot = base.lastIndexOf(".");
|
|
42
|
+
if (dot < 0) return "";
|
|
43
|
+
return base.slice(dot + 1).toLowerCase();
|
|
44
|
+
}
|
|
45
|
+
function mimeEssence(mimeType) {
|
|
46
|
+
return mimeType?.split(";")[0]?.trim().toLowerCase() ?? "";
|
|
47
|
+
}
|
|
48
|
+
function detectFileFormat(filename, mimeType) {
|
|
49
|
+
const ext = fileExtension(filename);
|
|
50
|
+
const mime = mimeEssence(mimeType);
|
|
51
|
+
if (mime === "application/pdf") return "pdf";
|
|
52
|
+
if (mime.startsWith("image/")) return "image";
|
|
53
|
+
if (mime === "text/markdown") return "markdown";
|
|
54
|
+
if (mime === "application/json") return "json";
|
|
55
|
+
if (mime === "text/csv" || mime === "application/csv") return "csv";
|
|
56
|
+
if (mime === "application/yaml" || mime === "application/x-yaml" || mime === "text/yaml") return "yaml";
|
|
57
|
+
if (ext === "pdf") return "pdf";
|
|
58
|
+
if (IMAGE_EXTENSIONS.includes(ext)) return "image";
|
|
59
|
+
if (ext === "csv") return "csv";
|
|
60
|
+
if (ext === "xlsx" || ext === "xls") return "spreadsheet";
|
|
61
|
+
if (CODE_EXTENSIONS.has(ext)) return "code";
|
|
62
|
+
if (ext === "json") return "json";
|
|
63
|
+
if (ext === "yaml" || ext === "yml") return "yaml";
|
|
64
|
+
if (ext === "md" || ext === "markdown") return "markdown";
|
|
65
|
+
if (["txt", "log", "text"].includes(ext)) return "text";
|
|
66
|
+
if (mime === "text/plain") return "text";
|
|
67
|
+
return "unknown";
|
|
68
|
+
}
|
|
69
|
+
function getFormatLabel(format) {
|
|
70
|
+
switch (format) {
|
|
71
|
+
case "pdf":
|
|
72
|
+
return "PDF";
|
|
73
|
+
case "image":
|
|
74
|
+
return "Image";
|
|
75
|
+
case "csv":
|
|
76
|
+
return "CSV";
|
|
77
|
+
case "spreadsheet":
|
|
78
|
+
return "Spreadsheet";
|
|
79
|
+
case "code":
|
|
80
|
+
return "Code";
|
|
81
|
+
case "json":
|
|
82
|
+
return "JSON";
|
|
83
|
+
case "yaml":
|
|
84
|
+
return "YAML";
|
|
85
|
+
case "markdown":
|
|
86
|
+
return "Markdown";
|
|
87
|
+
case "text":
|
|
88
|
+
return "Text";
|
|
89
|
+
default:
|
|
90
|
+
return "File";
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function getSyntaxLanguage(filename) {
|
|
94
|
+
return EXTENSION_TO_SYNTAX_LANGUAGE[fileExtension(filename)];
|
|
95
|
+
}
|
|
96
|
+
function getCodeLanguage(filename, format) {
|
|
97
|
+
if (format === "json" || format === "yaml") return format;
|
|
98
|
+
return getSyntaxLanguage(filename);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export {
|
|
102
|
+
fileExtension,
|
|
103
|
+
detectFileFormat,
|
|
104
|
+
getFormatLabel,
|
|
105
|
+
getSyntaxLanguage,
|
|
106
|
+
getCodeLanguage
|
|
107
|
+
};
|
|
@@ -5,12 +5,18 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
|
5
5
|
interface CodeBlockProps extends HTMLAttributes<HTMLDivElement> {
|
|
6
6
|
code: string;
|
|
7
7
|
language?: string;
|
|
8
|
+
/**
|
|
9
|
+
* Header text. Defaults to `language`. Set this when the display name differs
|
|
10
|
+
* from the highlight.js language id — e.g. a file extension ("BASHRC") whose
|
|
11
|
+
* content highlights as a known language, or none.
|
|
12
|
+
*/
|
|
13
|
+
label?: string;
|
|
8
14
|
showLineNumbers?: boolean;
|
|
9
15
|
/** Force light theme; defaults to dark */
|
|
10
16
|
light?: boolean;
|
|
11
17
|
children?: ReactNode;
|
|
12
18
|
}
|
|
13
|
-
declare const CodeBlock: React.MemoExoticComponent<({ code, language, showLineNumbers, light: lightProp, className, children, ...props }: CodeBlockProps) => react_jsx_runtime.JSX.Element>;
|
|
19
|
+
declare const CodeBlock: React.MemoExoticComponent<({ code, language, label, showLineNumbers, light: lightProp, className, children, ...props }: CodeBlockProps) => react_jsx_runtime.JSX.Element>;
|
|
14
20
|
/** Copy-to-clipboard button for use inside CodeBlock. */
|
|
15
21
|
declare const CopyButton: React.MemoExoticComponent<({ text }: {
|
|
16
22
|
text: string;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import {
|
|
3
3
|
DocumentEditorPane
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-SZ44QDA6.js";
|
|
5
5
|
import "./chunk-Q56BYXQF.js";
|
|
6
6
|
import "./chunk-CSAIKY36.js";
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-
|
|
7
|
+
import "./chunk-FJBTCTZM.js";
|
|
8
|
+
import "./chunk-WUQDUBJG.js";
|
|
9
9
|
import "./chunk-RQHJBTEU.js";
|
|
10
10
|
export {
|
|
11
11
|
DocumentEditorPane
|
package/dist/editor.js
CHANGED
|
@@ -11,11 +11,11 @@ import {
|
|
|
11
11
|
useEditorConnection,
|
|
12
12
|
useEditorContext,
|
|
13
13
|
useYjsState
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-SZ44QDA6.js";
|
|
15
15
|
import "./chunk-Q56BYXQF.js";
|
|
16
16
|
import "./chunk-CSAIKY36.js";
|
|
17
|
-
import "./chunk-
|
|
18
|
-
import "./chunk-
|
|
17
|
+
import "./chunk-FJBTCTZM.js";
|
|
18
|
+
import "./chunk-WUQDUBJG.js";
|
|
19
19
|
import "./chunk-RQHJBTEU.js";
|
|
20
20
|
export {
|
|
21
21
|
CollaboratorsList,
|
package/dist/files.d.ts
CHANGED
|
@@ -101,7 +101,7 @@ declare function RichFileTree({ root, paths, selectedPath, onSelect, search, ini
|
|
|
101
101
|
* Renders any file type beautifully:
|
|
102
102
|
* - PDF: embedded viewer
|
|
103
103
|
* - CSV/XLSX: tabular preview
|
|
104
|
-
* - Code (py/json/yaml/ts/js): line-numbered viewer
|
|
104
|
+
* - Code (py/json/yaml/ts/js): syntax-highlighted, line-numbered viewer
|
|
105
105
|
* - Markdown: rendered prose
|
|
106
106
|
* - Images: inline display
|
|
107
107
|
* - Text: monospace preview
|
|
@@ -172,4 +172,41 @@ interface FileArtifactPaneProps extends Omit<FilePreviewProps, "className"> {
|
|
|
172
172
|
*/
|
|
173
173
|
declare function FileArtifactPane({ filename, content, blobUrl, mimeType, onClose, onDownload, path, tabs, activeTabId, onTabSelect, onTabClose, eyebrow, meta, toolbar, footer, className, editor, }: FileArtifactPaneProps): react_jsx_runtime.JSX.Element;
|
|
174
174
|
|
|
175
|
-
|
|
175
|
+
/**
|
|
176
|
+
* Shared file-format detection for every file surface — the preview pane, the
|
|
177
|
+
* artifact pane, and code rendering. Centralising extension/MIME → format logic
|
|
178
|
+
* keeps chat and artifact views consistent and avoids the same mapping drifting
|
|
179
|
+
* across components.
|
|
180
|
+
*/
|
|
181
|
+
type FileFormat = "pdf" | "image" | "csv" | "spreadsheet" | "code" | "json" | "yaml" | "markdown" | "text" | "unknown";
|
|
182
|
+
/**
|
|
183
|
+
* Lowercased trailing extension. Returns "" for a name with no extension
|
|
184
|
+
* ("README", "json" → ""), and the post-dot name for a dotfile (".bashrc" →
|
|
185
|
+
* "bashrc"). Directory components are ignored so dots in a directory name don't
|
|
186
|
+
* leak in ("my.config/file" → "").
|
|
187
|
+
*/
|
|
188
|
+
declare function fileExtension(filename: string): string;
|
|
189
|
+
/**
|
|
190
|
+
* Resolve a filename + optional MIME type to the renderer format. A specific,
|
|
191
|
+
* authoritative MIME type wins over the extension; otherwise the extension
|
|
192
|
+
* decides; a generic text/plain payload is the final fallback.
|
|
193
|
+
*/
|
|
194
|
+
declare function detectFileFormat(filename: string, mimeType?: string): FileFormat;
|
|
195
|
+
/** Human-facing label for a detected format. */
|
|
196
|
+
declare function getFormatLabel(format: FileFormat): string;
|
|
197
|
+
/**
|
|
198
|
+
* Map a filename (or path) to a highlight.js language id for CodeBlock. Returns
|
|
199
|
+
* undefined when there is no confident mapping; CodeBlock then renders themed
|
|
200
|
+
* monospace and lets the highlighter auto-detect, so callers never need a
|
|
201
|
+
* bespoke language table.
|
|
202
|
+
*/
|
|
203
|
+
declare function getSyntaxLanguage(filename: string): string | undefined;
|
|
204
|
+
/**
|
|
205
|
+
* Highlight language for a file already classified as a code-like format.
|
|
206
|
+
* `json`/`yaml` are their own highlight language even when detected purely from
|
|
207
|
+
* a MIME type on an extensionless file (where the extension can't reveal it);
|
|
208
|
+
* any other code format keys off the extension.
|
|
209
|
+
*/
|
|
210
|
+
declare function getCodeLanguage(filename: string, format: FileFormat): string | undefined;
|
|
211
|
+
|
|
212
|
+
export { FileArtifactPane, type FileArtifactPaneProps, type FileFormat, type FileNode, FilePreview, type FilePreviewProps, type FileTabData, FileTabs, type FileTabsProps, FileTree, type FileTreeProps, type FileTreeVisibilityOptions, RichFileTree, type RichFileTreeGitEntry, type RichFileTreeGitStatus, type RichFileTreeProps, type RichFileTreeThemeVars, detectFileFormat, fileExtension, filterFileTree, getCodeLanguage, getFormatLabel, getSyntaxLanguage };
|