@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/dist/files.js
CHANGED
|
@@ -5,10 +5,17 @@ import {
|
|
|
5
5
|
FileTree,
|
|
6
6
|
RichFileTree,
|
|
7
7
|
filterFileTree
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-LELGOLFV.js";
|
|
9
|
+
import {
|
|
10
|
+
detectFileFormat,
|
|
11
|
+
fileExtension,
|
|
12
|
+
getCodeLanguage,
|
|
13
|
+
getFormatLabel,
|
|
14
|
+
getSyntaxLanguage
|
|
15
|
+
} from "./chunk-ZRVH3WCA.js";
|
|
9
16
|
import "./chunk-CSAIKY36.js";
|
|
10
|
-
import "./chunk-
|
|
11
|
-
import "./chunk-
|
|
17
|
+
import "./chunk-FJBTCTZM.js";
|
|
18
|
+
import "./chunk-WUQDUBJG.js";
|
|
12
19
|
import "./chunk-RQHJBTEU.js";
|
|
13
20
|
export {
|
|
14
21
|
FileArtifactPane,
|
|
@@ -16,5 +23,10 @@ export {
|
|
|
16
23
|
FileTabs,
|
|
17
24
|
FileTree,
|
|
18
25
|
RichFileTree,
|
|
19
|
-
|
|
26
|
+
detectFileFormat,
|
|
27
|
+
fileExtension,
|
|
28
|
+
filterFileTree,
|
|
29
|
+
getCodeLanguage,
|
|
30
|
+
getFormatLabel,
|
|
31
|
+
getSyntaxLanguage
|
|
20
32
|
};
|
package/dist/hooks.js
CHANGED
|
@@ -11,18 +11,18 @@ import {
|
|
|
11
11
|
useSSEStream,
|
|
12
12
|
useSdkSession,
|
|
13
13
|
useToolCallStream
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-CMX2I43A.js";
|
|
15
15
|
import "./chunk-OEX7NZE3.js";
|
|
16
16
|
import {
|
|
17
17
|
useAutoScroll,
|
|
18
18
|
useRunCollapseState,
|
|
19
19
|
useRunGroups
|
|
20
20
|
} from "./chunk-54SQQMMM.js";
|
|
21
|
-
import "./chunk-
|
|
22
|
-
import "./chunk-
|
|
21
|
+
import "./chunk-5VPTNXX7.js";
|
|
22
|
+
import "./chunk-DGW77LD7.js";
|
|
23
23
|
import "./chunk-BX6AQMUS.js";
|
|
24
|
-
import "./chunk-
|
|
25
|
-
import "./chunk-
|
|
24
|
+
import "./chunk-FJBTCTZM.js";
|
|
25
|
+
import "./chunk-WUQDUBJG.js";
|
|
26
26
|
import "./chunk-RQHJBTEU.js";
|
|
27
27
|
export {
|
|
28
28
|
RealtimeSessionRegistry,
|
package/dist/index.d.ts
CHANGED
|
@@ -6,9 +6,9 @@ export { AgentTimeline, AgentTimelineArtifactItem, AgentTimelineCustomItem, Agen
|
|
|
6
6
|
export { ExpandedToolDetail, ExpandedToolDetailProps, InlineThinkingItem, InlineThinkingItemProps, InlineToolItem, InlineToolItemProps, LiveDuration, RunGroup, RunGroupProps } from './run.js';
|
|
7
7
|
export { F as FeedSegment, T as ToolCallData, a as ToolCallFeed, b as ToolCallFeedProps, c as ToolCallGroup, d as ToolCallGroupProps, e as ToolCallStatus, f as ToolCallStep, g as ToolCallStepProps, h as ToolCallType, p as parseToolEvent } from './tool-call-feed-Bs3MyQMT.js';
|
|
8
8
|
export { OpenUIAction, OpenUIActionsNode, OpenUIArtifactRenderer, OpenUIArtifactRendererProps, OpenUIBadgeNode, OpenUICardNode, OpenUICodeNode, OpenUIComponentNode, OpenUIGridNode, OpenUIHeadingNode, OpenUIKeyValueNode, OpenUIMarkdownNode, OpenUIPrimitive, OpenUISeparatorNode, OpenUIStackNode, OpenUIStatNode, OpenUITableNode, OpenUITextNode } from './openui.js';
|
|
9
|
-
export { FileArtifactPane, FileArtifactPaneProps, FileNode, FilePreview, FilePreviewProps, FileTabData, FileTabs, FileTabsProps, FileTree, FileTreeProps, FileTreeVisibilityOptions, RichFileTree, RichFileTreeGitEntry, RichFileTreeGitStatus, RichFileTreeProps, RichFileTreeThemeVars, filterFileTree } from './files.js';
|
|
9
|
+
export { FileArtifactPane, FileArtifactPaneProps, FileFormat, FileNode, FilePreview, FilePreviewProps, FileTabData, FileTabs, FileTabsProps, FileTree, FileTreeProps, FileTreeVisibilityOptions, RichFileTree, RichFileTreeGitEntry, RichFileTreeGitStatus, RichFileTreeProps, RichFileTreeThemeVars, detectFileFormat, fileExtension, filterFileTree, getCodeLanguage, getFormatLabel, getSyntaxLanguage } from './files.js';
|
|
10
10
|
export { Markdown, MarkdownProps } from './markdown.js';
|
|
11
|
-
export { C as CodeBlock, a as CodeBlockProps, b as CopyButton } from './code-block-
|
|
11
|
+
export { C as CodeBlock, a as CodeBlockProps, b as CopyButton } from './code-block-0kSpWMnf.js';
|
|
12
12
|
export { AuthHeader, AuthHeaderProps, GitHubLoginButton, GitHubLoginButtonProps, LoginLayout, LoginLayoutProps, SessionUser, UserMenu, UserMenuProps } from './auth.js';
|
|
13
13
|
export { AgentStreamEvent, AppendUserMessageOptions, ApplySdkEventOptions, AutomationStreamEvent, BeginAssistantMessageOptions, BotStreamEvent, CompleteAssistantMessageOptions, ConnectionState, RealtimeSessionOptions, RealtimeSessionRegistry, RealtimeSessionRegistryProps, RealtimeSessionState, RealtimeSessionTarget, SSEEvent, SdkSessionAttachment, SdkSessionEvent, SdkSessionSeed, TaskStreamEvent, TerminalStreamEvent, UseRunGroupsOptions, UseSSEStreamOptions, UseSSEStreamResult, UseSdkSessionOptions, UseSdkSessionReturn, UseToolCallStreamReturn, useAutoScroll, useDropdownMenu, useRealtimeSession, useRunCollapseState, useRunGroups, useSSEStream, useSdkSession, useToolCallStream } from './sdk-hooks.js';
|
|
14
14
|
export { AuthUser, UseAuthOptions, UseAuthResult, createAuthFetcher, useApiKey, useAuth, useLiveTime } from './hooks.js';
|
package/dist/index.js
CHANGED
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
useSSEStream,
|
|
18
18
|
useSdkSession,
|
|
19
19
|
useToolCallStream
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-CMX2I43A.js";
|
|
21
21
|
import {
|
|
22
22
|
addMessage,
|
|
23
23
|
addParts,
|
|
@@ -138,7 +138,7 @@ import {
|
|
|
138
138
|
MessageList,
|
|
139
139
|
ThinkingIndicator,
|
|
140
140
|
UserMessage
|
|
141
|
-
} from "./chunk-
|
|
141
|
+
} from "./chunk-KT5RNO7N.js";
|
|
142
142
|
import {
|
|
143
143
|
useAutoScroll,
|
|
144
144
|
useRunCollapseState,
|
|
@@ -148,18 +148,18 @@ import "./chunk-LQS34IGP.js";
|
|
|
148
148
|
import {
|
|
149
149
|
ToolCallFeed,
|
|
150
150
|
parseToolEvent
|
|
151
|
-
} from "./chunk-
|
|
151
|
+
} from "./chunk-5VPTNXX7.js";
|
|
152
152
|
import {
|
|
153
153
|
ExpandedToolDetail,
|
|
154
154
|
InlineThinkingItem,
|
|
155
155
|
InlineToolItem,
|
|
156
156
|
LiveDuration,
|
|
157
157
|
RunGroup
|
|
158
|
-
} from "./chunk-
|
|
158
|
+
} from "./chunk-JBPWIYTQ.js";
|
|
159
159
|
import {
|
|
160
160
|
ToolCallGroup,
|
|
161
161
|
ToolCallStep
|
|
162
|
-
} from "./chunk-
|
|
162
|
+
} from "./chunk-DGW77LD7.js";
|
|
163
163
|
import {
|
|
164
164
|
formatBytes,
|
|
165
165
|
formatDuration,
|
|
@@ -180,11 +180,11 @@ import {
|
|
|
180
180
|
QuestionPreview,
|
|
181
181
|
WebSearchPreview,
|
|
182
182
|
WriteFilePreview
|
|
183
|
-
} from "./chunk-
|
|
183
|
+
} from "./chunk-AAUNOHVL.js";
|
|
184
184
|
import "./chunk-RQGKSCEZ.js";
|
|
185
185
|
import {
|
|
186
186
|
OpenUIArtifactRenderer
|
|
187
|
-
} from "./chunk-
|
|
187
|
+
} from "./chunk-52Y3FMFI.js";
|
|
188
188
|
import {
|
|
189
189
|
Badge,
|
|
190
190
|
Card,
|
|
@@ -215,7 +215,14 @@ import {
|
|
|
215
215
|
FileTree,
|
|
216
216
|
RichFileTree,
|
|
217
217
|
filterFileTree
|
|
218
|
-
} from "./chunk-
|
|
218
|
+
} from "./chunk-LELGOLFV.js";
|
|
219
|
+
import {
|
|
220
|
+
detectFileFormat,
|
|
221
|
+
fileExtension,
|
|
222
|
+
getCodeLanguage,
|
|
223
|
+
getFormatLabel,
|
|
224
|
+
getSyntaxLanguage
|
|
225
|
+
} from "./chunk-ZRVH3WCA.js";
|
|
219
226
|
import {
|
|
220
227
|
Tabs,
|
|
221
228
|
TabsContent,
|
|
@@ -228,11 +235,11 @@ import {
|
|
|
228
235
|
import "./chunk-5Z5ZYMOJ.js";
|
|
229
236
|
import {
|
|
230
237
|
Markdown
|
|
231
|
-
} from "./chunk-
|
|
238
|
+
} from "./chunk-FJBTCTZM.js";
|
|
232
239
|
import {
|
|
233
240
|
CodeBlock,
|
|
234
241
|
CopyButton
|
|
235
|
-
} from "./chunk-
|
|
242
|
+
} from "./chunk-WUQDUBJG.js";
|
|
236
243
|
import {
|
|
237
244
|
cn
|
|
238
245
|
} from "./chunk-RQHJBTEU.js";
|
|
@@ -465,6 +472,8 @@ export {
|
|
|
465
472
|
cn,
|
|
466
473
|
copyText,
|
|
467
474
|
createAuthFetcher,
|
|
475
|
+
detectFileFormat,
|
|
476
|
+
fileExtension,
|
|
468
477
|
filterFileTree,
|
|
469
478
|
formatBytes,
|
|
470
479
|
formatDuration,
|
|
@@ -472,9 +481,12 @@ export {
|
|
|
472
481
|
getActiveSession,
|
|
473
482
|
getAllActiveSessions,
|
|
474
483
|
getAllProjectActivity,
|
|
484
|
+
getCodeLanguage,
|
|
485
|
+
getFormatLabel,
|
|
475
486
|
getSessionsByActivity,
|
|
476
487
|
getSessionsForNavbar,
|
|
477
488
|
getSessionsForProject,
|
|
489
|
+
getSyntaxLanguage,
|
|
478
490
|
getToolCategory,
|
|
479
491
|
getToolDisplayMetadata,
|
|
480
492
|
getToolErrorText,
|
package/dist/markdown.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
3
|
-
export { C as CodeBlock, a as CodeBlockProps, b as CopyButton } from './code-block-
|
|
3
|
+
export { C as CodeBlock, a as CodeBlockProps, b as CopyButton } from './code-block-0kSpWMnf.js';
|
|
4
4
|
|
|
5
5
|
interface MarkdownProps {
|
|
6
6
|
children: string;
|
package/dist/markdown.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import "./chunk-5Z5ZYMOJ.js";
|
|
2
2
|
import {
|
|
3
3
|
Markdown
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-FJBTCTZM.js";
|
|
5
5
|
import {
|
|
6
6
|
CodeBlock,
|
|
7
7
|
CopyButton
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-WUQDUBJG.js";
|
|
9
9
|
import "./chunk-RQHJBTEU.js";
|
|
10
10
|
export {
|
|
11
11
|
CodeBlock,
|
package/dist/openui.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import "./chunk-RQGKSCEZ.js";
|
|
2
2
|
import {
|
|
3
3
|
OpenUIArtifactRenderer
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-52Y3FMFI.js";
|
|
5
5
|
import "./chunk-GYPQXTJU.js";
|
|
6
6
|
import "./chunk-MKTSMWVD.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
|
OpenUIArtifactRenderer
|
package/dist/primitives.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ import * as SwitchPrimitives from '@radix-ui/react-switch';
|
|
|
14
14
|
import * as LabelPrimitive from '@radix-ui/react-label';
|
|
15
15
|
export { Logo, LogoProps, TangleKnot } from '@tangle-network/brand';
|
|
16
16
|
export { A as ArtifactPane, a as ArtifactPaneProps } from './artifact-pane-DvJyPWV4.js';
|
|
17
|
-
export { C as CodeBlock, a as CodeBlockProps, b as CopyButton } from './code-block-
|
|
17
|
+
export { C as CodeBlock, a as CodeBlockProps, b as CopyButton } from './code-block-0kSpWMnf.js';
|
|
18
18
|
|
|
19
19
|
declare const Card: React$1.ForwardRefExoticComponent<React$1.HTMLAttributes<HTMLDivElement> & {
|
|
20
20
|
variant?: "default" | "glass" | "sandbox" | "elevated";
|
package/dist/primitives.js
CHANGED
package/dist/run.js
CHANGED
|
@@ -2,26 +2,27 @@ import "./chunk-LQS34IGP.js";
|
|
|
2
2
|
import {
|
|
3
3
|
ToolCallFeed,
|
|
4
4
|
parseToolEvent
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-5VPTNXX7.js";
|
|
6
6
|
import {
|
|
7
7
|
ExpandedToolDetail,
|
|
8
8
|
InlineThinkingItem,
|
|
9
9
|
InlineToolItem,
|
|
10
10
|
LiveDuration,
|
|
11
11
|
RunGroup
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-JBPWIYTQ.js";
|
|
13
13
|
import {
|
|
14
14
|
ToolCallGroup,
|
|
15
15
|
ToolCallStep
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-DGW77LD7.js";
|
|
17
17
|
import "./chunk-4CLN43XT.js";
|
|
18
18
|
import "./chunk-BX6AQMUS.js";
|
|
19
|
-
import "./chunk-
|
|
20
|
-
import "./chunk-
|
|
19
|
+
import "./chunk-AAUNOHVL.js";
|
|
20
|
+
import "./chunk-52Y3FMFI.js";
|
|
21
21
|
import "./chunk-GYPQXTJU.js";
|
|
22
22
|
import "./chunk-MKTSMWVD.js";
|
|
23
|
-
import "./chunk-
|
|
24
|
-
import "./chunk-
|
|
23
|
+
import "./chunk-ZRVH3WCA.js";
|
|
24
|
+
import "./chunk-FJBTCTZM.js";
|
|
25
|
+
import "./chunk-WUQDUBJG.js";
|
|
25
26
|
import "./chunk-RQHJBTEU.js";
|
|
26
27
|
export {
|
|
27
28
|
ExpandedToolDetail,
|
package/dist/sdk-hooks.js
CHANGED
|
@@ -5,18 +5,18 @@ import {
|
|
|
5
5
|
useSSEStream,
|
|
6
6
|
useSdkSession,
|
|
7
7
|
useToolCallStream
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-CMX2I43A.js";
|
|
9
9
|
import "./chunk-OEX7NZE3.js";
|
|
10
10
|
import {
|
|
11
11
|
useAutoScroll,
|
|
12
12
|
useRunCollapseState,
|
|
13
13
|
useRunGroups
|
|
14
14
|
} from "./chunk-54SQQMMM.js";
|
|
15
|
-
import "./chunk-
|
|
16
|
-
import "./chunk-
|
|
15
|
+
import "./chunk-5VPTNXX7.js";
|
|
16
|
+
import "./chunk-DGW77LD7.js";
|
|
17
17
|
import "./chunk-BX6AQMUS.js";
|
|
18
|
-
import "./chunk-
|
|
19
|
-
import "./chunk-
|
|
18
|
+
import "./chunk-FJBTCTZM.js";
|
|
19
|
+
import "./chunk-WUQDUBJG.js";
|
|
20
20
|
import "./chunk-RQHJBTEU.js";
|
|
21
21
|
export {
|
|
22
22
|
RealtimeSessionRegistry,
|
package/dist/tool-previews.js
CHANGED
|
@@ -7,8 +7,9 @@ import {
|
|
|
7
7
|
QuestionPreview,
|
|
8
8
|
WebSearchPreview,
|
|
9
9
|
WriteFilePreview
|
|
10
|
-
} from "./chunk-
|
|
11
|
-
import "./chunk-
|
|
10
|
+
} from "./chunk-AAUNOHVL.js";
|
|
11
|
+
import "./chunk-ZRVH3WCA.js";
|
|
12
|
+
import "./chunk-WUQDUBJG.js";
|
|
12
13
|
import "./chunk-RQHJBTEU.js";
|
|
13
14
|
export {
|
|
14
15
|
CommandPreview,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/ui",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"description": "Generic React UI components for Tangle products — primitives, chat, run, files, editor, markdown.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -132,7 +132,7 @@
|
|
|
132
132
|
"react": "^18 || ^19",
|
|
133
133
|
"react-dom": "^18 || ^19",
|
|
134
134
|
"react-router": "^7",
|
|
135
|
-
"@tangle-network/brand": "^0.
|
|
135
|
+
"@tangle-network/brand": "^0.5.0"
|
|
136
136
|
},
|
|
137
137
|
"peerDependenciesMeta": {
|
|
138
138
|
"@nanostores/react": {
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
type DocumentEditorPaneCollaborationConfig,
|
|
7
7
|
} from "../editor/document-editor-pane";
|
|
8
8
|
import { ArtifactPane, type ArtifactPaneProps } from "../primitives/artifact-pane";
|
|
9
|
+
import { detectFileFormat } from "./file-format";
|
|
9
10
|
import { FilePreview, type FilePreviewProps } from "./file-preview";
|
|
10
11
|
import { FileTabs, type FileTabData } from "./file-tabs";
|
|
11
12
|
|
|
@@ -79,9 +80,8 @@ export function FileArtifactPane({
|
|
|
79
80
|
/>
|
|
80
81
|
) : undefined;
|
|
81
82
|
const isMarkdown =
|
|
82
|
-
mimeType === "
|
|
83
|
-
|
|
84
|
-
path?.toLowerCase().endsWith(".md");
|
|
83
|
+
detectFileFormat(filename, mimeType) === "markdown" ||
|
|
84
|
+
(path ? detectFileFormat(path, mimeType) === "markdown" : false);
|
|
85
85
|
const isEditableMarkdown = isMarkdown && editor?.enabled;
|
|
86
86
|
const headerActions = (
|
|
87
87
|
<>
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
detectFileFormat,
|
|
4
|
+
fileExtension,
|
|
5
|
+
getCodeLanguage,
|
|
6
|
+
getFormatLabel,
|
|
7
|
+
getSyntaxLanguage,
|
|
8
|
+
} from "./file-format";
|
|
9
|
+
|
|
10
|
+
describe("detectFileFormat", () => {
|
|
11
|
+
it("detects by extension", () => {
|
|
12
|
+
expect(detectFileFormat("report.pdf")).toBe("pdf");
|
|
13
|
+
expect(detectFileFormat("photo.PNG")).toBe("image");
|
|
14
|
+
expect(detectFileFormat("data.csv")).toBe("csv");
|
|
15
|
+
expect(detectFileFormat("book.xlsx")).toBe("spreadsheet");
|
|
16
|
+
expect(detectFileFormat("legacy.xls")).toBe("spreadsheet");
|
|
17
|
+
expect(detectFileFormat("main.py")).toBe("code");
|
|
18
|
+
expect(detectFileFormat("app.tsx")).toBe("code");
|
|
19
|
+
expect(detectFileFormat("config.json")).toBe("json");
|
|
20
|
+
expect(detectFileFormat("compose.yaml")).toBe("yaml");
|
|
21
|
+
expect(detectFileFormat("compose.yml")).toBe("yaml");
|
|
22
|
+
expect(detectFileFormat("README.md")).toBe("markdown");
|
|
23
|
+
expect(detectFileFormat("notes.markdown")).toBe("markdown");
|
|
24
|
+
expect(detectFileFormat("output.log")).toBe("text");
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("treats shell dotfiles as code", () => {
|
|
28
|
+
expect(detectFileFormat(".bashrc")).toBe("code");
|
|
29
|
+
expect(detectFileFormat(".profile")).toBe("code");
|
|
30
|
+
expect(detectFileFormat(".gitignore")).toBe("code");
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("routes every highlightable extension to the code viewer", () => {
|
|
34
|
+
// detectFileFormat must stay in sync with getSyntaxLanguage — anything we
|
|
35
|
+
// can highlight (and that has no dedicated format) renders as code, not text.
|
|
36
|
+
for (const file of [
|
|
37
|
+
"main.rs",
|
|
38
|
+
"server.go",
|
|
39
|
+
"app.rb",
|
|
40
|
+
"styles.css",
|
|
41
|
+
"theme.scss",
|
|
42
|
+
"index.html",
|
|
43
|
+
"Cargo.toml",
|
|
44
|
+
"query.sql",
|
|
45
|
+
"Token.sol",
|
|
46
|
+
"schema.proto",
|
|
47
|
+
"module.mjs",
|
|
48
|
+
"legacy.cjs",
|
|
49
|
+
"deploy.zsh",
|
|
50
|
+
]) {
|
|
51
|
+
expect(detectFileFormat(file)).toBe("code");
|
|
52
|
+
expect(getSyntaxLanguage(file)).toBeDefined();
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("keeps formats with a dedicated renderer out of the code viewer", () => {
|
|
57
|
+
expect(detectFileFormat("config.json")).toBe("json");
|
|
58
|
+
expect(detectFileFormat("compose.yml")).toBe("yaml");
|
|
59
|
+
expect(detectFileFormat("README.md")).toBe("markdown");
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("prefers MIME type when it is more specific", () => {
|
|
63
|
+
expect(detectFileFormat("file", "application/pdf")).toBe("pdf");
|
|
64
|
+
expect(detectFileFormat("blob", "image/png")).toBe("image");
|
|
65
|
+
expect(detectFileFormat("CHANGELOG", "text/markdown")).toBe("markdown");
|
|
66
|
+
expect(detectFileFormat("noext", "text/plain")).toBe("text");
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("falls back to unknown when nothing matches", () => {
|
|
70
|
+
expect(detectFileFormat("mystery.bin")).toBe("unknown");
|
|
71
|
+
expect(detectFileFormat("noextension")).toBe("unknown");
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("does not classify a dotless basename that happens to spell an extension", () => {
|
|
75
|
+
// A file literally named "pdf"/"json" has no extension — it must not be
|
|
76
|
+
// treated as that format (e.g. a pdf with no blobUrl renders empty).
|
|
77
|
+
expect(detectFileFormat("pdf")).toBe("unknown");
|
|
78
|
+
expect(detectFileFormat("json")).toBe("unknown");
|
|
79
|
+
expect(detectFileFormat("csv")).toBe("unknown");
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("lets a concrete extension win over a generic text/plain MIME", () => {
|
|
83
|
+
expect(detectFileFormat("config.json", "text/plain")).toBe("json");
|
|
84
|
+
expect(detectFileFormat("main.py", "text/plain")).toBe("code");
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("detects structured-data MIME types on generically-named files", () => {
|
|
88
|
+
expect(detectFileFormat("data", "application/json")).toBe("json");
|
|
89
|
+
expect(detectFileFormat("records", "text/csv")).toBe("csv");
|
|
90
|
+
expect(detectFileFormat("export", "application/csv")).toBe("csv");
|
|
91
|
+
expect(detectFileFormat("config", "application/yaml")).toBe("yaml");
|
|
92
|
+
expect(detectFileFormat("feed", "application/x-yaml")).toBe("yaml");
|
|
93
|
+
expect(detectFileFormat("feed", "text/yaml")).toBe("yaml");
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("ignores MIME charset parameters", () => {
|
|
97
|
+
expect(detectFileFormat("data", "application/json; charset=utf-8")).toBe("json");
|
|
98
|
+
expect(detectFileFormat("blob", "IMAGE/PNG")).toBe("image");
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("treats an authoritative MIME type as outranking a conflicting extension", () => {
|
|
102
|
+
expect(detectFileFormat("notes.json", "text/markdown")).toBe("markdown");
|
|
103
|
+
expect(detectFileFormat("page.txt", "application/json")).toBe("json");
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe("getFormatLabel", () => {
|
|
108
|
+
it("maps every format to a human label", () => {
|
|
109
|
+
expect(getFormatLabel("pdf")).toBe("PDF");
|
|
110
|
+
expect(getFormatLabel("json")).toBe("JSON");
|
|
111
|
+
expect(getFormatLabel("yaml")).toBe("YAML");
|
|
112
|
+
expect(getFormatLabel("markdown")).toBe("Markdown");
|
|
113
|
+
expect(getFormatLabel("spreadsheet")).toBe("Spreadsheet");
|
|
114
|
+
expect(getFormatLabel("unknown")).toBe("File");
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
describe("getSyntaxLanguage", () => {
|
|
119
|
+
it("maps known extensions to highlight.js language ids", () => {
|
|
120
|
+
expect(getSyntaxLanguage("main.py")).toBe("python");
|
|
121
|
+
expect(getSyntaxLanguage("server.ts")).toBe("typescript");
|
|
122
|
+
expect(getSyntaxLanguage("index.mjs")).toBe("javascript");
|
|
123
|
+
expect(getSyntaxLanguage("config.json")).toBe("json");
|
|
124
|
+
expect(getSyntaxLanguage("compose.yml")).toBe("yaml");
|
|
125
|
+
expect(getSyntaxLanguage(".bashrc")).toBe("bash");
|
|
126
|
+
expect(getSyntaxLanguage("lib.rs")).toBe("rust");
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("resolves from a full path, not just a basename", () => {
|
|
130
|
+
expect(getSyntaxLanguage("src/server/index.ts")).toBe("typescript");
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("returns undefined for unmapped extensions", () => {
|
|
134
|
+
expect(getSyntaxLanguage("mystery.bin")).toBeUndefined();
|
|
135
|
+
expect(getSyntaxLanguage("noextension")).toBeUndefined();
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
describe("getCodeLanguage", () => {
|
|
140
|
+
it("uses the detected format for json/yaml, covering extensionless MIME-only files", () => {
|
|
141
|
+
expect(getCodeLanguage("config", "json")).toBe("json");
|
|
142
|
+
expect(getCodeLanguage("config", "yaml")).toBe("yaml");
|
|
143
|
+
// A real extension resolves to the same answer.
|
|
144
|
+
expect(getCodeLanguage("config.json", "json")).toBe("json");
|
|
145
|
+
expect(getCodeLanguage("compose.yml", "yaml")).toBe("yaml");
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it("keys off the extension for other code formats", () => {
|
|
149
|
+
expect(getCodeLanguage("main.rs", "code")).toBe("rust");
|
|
150
|
+
expect(getCodeLanguage("server.ts", "code")).toBe("typescript");
|
|
151
|
+
expect(getCodeLanguage("notes", "code")).toBeUndefined();
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
describe("fileExtension", () => {
|
|
156
|
+
it("lowercases the trailing extension", () => {
|
|
157
|
+
expect(fileExtension("Photo.JPEG")).toBe("jpeg");
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it("returns the dotfile name for files that are all extension", () => {
|
|
161
|
+
expect(fileExtension(".gitignore")).toBe("gitignore");
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it("ignores dots in directory components", () => {
|
|
165
|
+
expect(fileExtension("my.config.dir/file")).toBe("");
|
|
166
|
+
expect(fileExtension("v1.2.0/Makefile")).toBe("");
|
|
167
|
+
expect(fileExtension("a.b.c/server.ts")).toBe("ts");
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it("returns no extension for a dotless basename, even one that looks like an extension", () => {
|
|
171
|
+
expect(fileExtension("json")).toBe("");
|
|
172
|
+
expect(fileExtension("pdf")).toBe("");
|
|
173
|
+
expect(fileExtension("README")).toBe("");
|
|
174
|
+
expect(fileExtension("Makefile")).toBe("");
|
|
175
|
+
});
|
|
176
|
+
});
|