@particle-academy/fancy-code 0.5.0 → 0.6.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/README.md +34 -1
- package/dist/index.cjs +121 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +103 -3
- package/dist/index.d.ts +103 -3
- package/dist/index.js +120 -4
- package/dist/index.js.map +1 -1
- package/docs/FileViewer.md +78 -0
- package/package.json +6 -4
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import { RefObject, ReactNode } from 'react';
|
|
3
|
-
import { InputTagProps, InputTagTriggers, InputTagAdapter } from '@particle-academy/react-fancy';
|
|
2
|
+
import { RefObject, ReactNode, CSSProperties } from 'react';
|
|
3
|
+
import { MediaKind, MediaViewerProps, InputTagProps, InputTagTriggers, InputTagAdapter } from '@particle-academy/react-fancy';
|
|
4
4
|
|
|
5
5
|
interface ThemeColors {
|
|
6
6
|
background: string;
|
|
@@ -225,6 +225,106 @@ declare namespace MarkdownEditor {
|
|
|
225
225
|
var displayName: string;
|
|
226
226
|
}
|
|
227
227
|
|
|
228
|
+
/**
|
|
229
|
+
* The decision a file viewer makes about a file: render it as editable text, or
|
|
230
|
+
* hand it to a media viewer. `media` carries the resolved {@link MediaKind};
|
|
231
|
+
* `text` carries the editor language id.
|
|
232
|
+
*/
|
|
233
|
+
type FileKind = {
|
|
234
|
+
kind: "text";
|
|
235
|
+
language: string;
|
|
236
|
+
} | {
|
|
237
|
+
kind: "media";
|
|
238
|
+
mediaKind: MediaKind;
|
|
239
|
+
};
|
|
240
|
+
interface ResolveFileKindInput {
|
|
241
|
+
/** File name or path — sniffed for an extension (and used for the media check). */
|
|
242
|
+
filename?: string;
|
|
243
|
+
/** MIME type — preferred over the filename when known. */
|
|
244
|
+
mime?: string;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Resolve the fancy-code editor language for a filename. Returns `"plaintext"`
|
|
248
|
+
* (no syntax highlighting) for unknown or extension-less files.
|
|
249
|
+
*/
|
|
250
|
+
declare function languageFromFilename(filename?: string): string;
|
|
251
|
+
/**
|
|
252
|
+
* Decide whether a file is previewable media (image / video / audio / PDF) or
|
|
253
|
+
* text, from its MIME type (preferred) and/or filename. Media wins when the
|
|
254
|
+
* type resolves to one; otherwise it's text and the editor language is derived
|
|
255
|
+
* from the extension.
|
|
256
|
+
*
|
|
257
|
+
* Exposed so consumers can branch their own chrome (tabs, save buttons) the
|
|
258
|
+
* same way `<FileViewer>` does internally.
|
|
259
|
+
*
|
|
260
|
+
* ```ts
|
|
261
|
+
* resolveFileKind({ filename: "logo.png" }); // { kind: "media", mediaKind: "image" }
|
|
262
|
+
* resolveFileKind({ filename: "app.tsx" }); // { kind: "text", language: "typescript" }
|
|
263
|
+
* resolveFileKind({ mime: "application/pdf" }); // { kind: "media", mediaKind: "pdf" }
|
|
264
|
+
* ```
|
|
265
|
+
*/
|
|
266
|
+
declare function resolveFileKind({ filename, mime }: ResolveFileKindInput): FileKind;
|
|
267
|
+
|
|
268
|
+
interface FileViewerProps {
|
|
269
|
+
/**
|
|
270
|
+
* File name or path — used to detect the file kind / editor language, and as
|
|
271
|
+
* a label / alt text. Pass `mime` too when you have it (it's preferred).
|
|
272
|
+
*/
|
|
273
|
+
filename?: string;
|
|
274
|
+
/** MIME type — preferred over `filename` for detection. */
|
|
275
|
+
mime?: string;
|
|
276
|
+
/**
|
|
277
|
+
* Source URL for **media** files (image / video / audio / PDF) —
|
|
278
|
+
* `http(s):` / `data:` / `blob:`. Required when the file resolves to media.
|
|
279
|
+
*/
|
|
280
|
+
src?: string;
|
|
281
|
+
/** Text content for **text** files. */
|
|
282
|
+
value?: string;
|
|
283
|
+
/** Called when text content changes (ignored when `readOnly`). */
|
|
284
|
+
onChange?: (value: string) => void;
|
|
285
|
+
/**
|
|
286
|
+
* Read-only. Defaults to `true` — `FileViewer` is a viewer. Media is always
|
|
287
|
+
* read-only regardless. Set `false` (with `onChange`) to edit text.
|
|
288
|
+
*/
|
|
289
|
+
readOnly?: boolean;
|
|
290
|
+
/** Editor theme for text: `"light" | "dark" | "auto"` or a registered name (default `"auto"`). */
|
|
291
|
+
theme?: string;
|
|
292
|
+
/** Force the file kind, bypassing detection. */
|
|
293
|
+
fileKind?: FileKind;
|
|
294
|
+
/** Show line numbers in the text editor (default `true`). */
|
|
295
|
+
lineNumbers?: boolean;
|
|
296
|
+
/** Wrap long lines in the text editor (default `false`). */
|
|
297
|
+
wordWrap?: boolean;
|
|
298
|
+
/** Editor height bounds (px). */
|
|
299
|
+
minHeight?: number;
|
|
300
|
+
maxHeight?: number;
|
|
301
|
+
/** Extra props forwarded to `<MediaViewer>` for media files. */
|
|
302
|
+
mediaProps?: Partial<Omit<MediaViewerProps, "src" | "mime" | "alt">>;
|
|
303
|
+
className?: string;
|
|
304
|
+
style?: CSSProperties;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* A unified file viewer: renders text files in a read-only `CodeEditor` (with
|
|
309
|
+
* syntax highlighting picked from the filename) and media files — image, video,
|
|
310
|
+
* audio, PDF — via react-fancy's `<MediaViewer>`. The text-vs-media decision is
|
|
311
|
+
* made by {@link resolveFileKind}; pass `fileKind` to override it, or call
|
|
312
|
+
* `resolveFileKind` yourself to branch your own chrome (tabs, save buttons).
|
|
313
|
+
*
|
|
314
|
+
* Give the container a height for media files (PDF/image/video fill their box).
|
|
315
|
+
*
|
|
316
|
+
* ```tsx
|
|
317
|
+
* // text
|
|
318
|
+
* <FileViewer filename="app.tsx" value={source} />
|
|
319
|
+
* // media
|
|
320
|
+
* <FileViewer filename="logo.png" src={url} style={{ height: 360 }} />
|
|
321
|
+
* ```
|
|
322
|
+
*/
|
|
323
|
+
declare function FileViewer({ filename, mime, src, value, onChange, readOnly, theme, fileKind, lineNumbers, wordWrap, minHeight, maxHeight, mediaProps, className, style, }: FileViewerProps): react.JSX.Element;
|
|
324
|
+
declare namespace FileViewer {
|
|
325
|
+
var displayName: string;
|
|
326
|
+
}
|
|
327
|
+
|
|
228
328
|
/**
|
|
229
329
|
* Tiny, dependency-free Markdown → HTML renderer for the MarkdownEditor preview
|
|
230
330
|
* pane. Covers the common subset — headings, bold/italic, inline + fenced code,
|
|
@@ -309,4 +409,4 @@ interface CodeEditorInputTagProps extends Omit<InputTagProps, "adapter"> {
|
|
|
309
409
|
*/
|
|
310
410
|
declare function CodeEditorInputTag(props: CodeEditorInputTagProps): react.JSX.Element;
|
|
311
411
|
|
|
312
|
-
export { CodeEditor, type CodeEditorContextValue, CodeEditorInputTag, type CodeEditorInputTagProps, type CodeEditorPanelProps, type CodeEditorProps, type CodeEditorStatusBarProps, type CodeEditorToolbarProps, type LanguageDefinition, MarkdownEditor, type MarkdownEditorMode, type MarkdownEditorProps, type ThemeColors, type ThemeDefinition, type Token, type TokenType, type Tokenizer, getLanguage, getRegisteredLanguages, getRegisteredThemes, getTheme, registerLanguage, registerTheme, renderMarkdown, tokenizeMarkdown, useCodeEditor, useCodeEditorInputTagAdapter };
|
|
412
|
+
export { CodeEditor, type CodeEditorContextValue, CodeEditorInputTag, type CodeEditorInputTagProps, type CodeEditorPanelProps, type CodeEditorProps, type CodeEditorStatusBarProps, type CodeEditorToolbarProps, type FileKind, FileViewer, type FileViewerProps, type LanguageDefinition, MarkdownEditor, type MarkdownEditorMode, type MarkdownEditorProps, type ResolveFileKindInput, type ThemeColors, type ThemeDefinition, type Token, type TokenType, type Tokenizer, getLanguage, getRegisteredLanguages, getRegisteredThemes, getTheme, languageFromFilename, registerLanguage, registerTheme, renderMarkdown, resolveFileKind, tokenizeMarkdown, useCodeEditor, useCodeEditorInputTagAdapter };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import { RefObject, ReactNode } from 'react';
|
|
3
|
-
import { InputTagProps, InputTagTriggers, InputTagAdapter } from '@particle-academy/react-fancy';
|
|
2
|
+
import { RefObject, ReactNode, CSSProperties } from 'react';
|
|
3
|
+
import { MediaKind, MediaViewerProps, InputTagProps, InputTagTriggers, InputTagAdapter } from '@particle-academy/react-fancy';
|
|
4
4
|
|
|
5
5
|
interface ThemeColors {
|
|
6
6
|
background: string;
|
|
@@ -225,6 +225,106 @@ declare namespace MarkdownEditor {
|
|
|
225
225
|
var displayName: string;
|
|
226
226
|
}
|
|
227
227
|
|
|
228
|
+
/**
|
|
229
|
+
* The decision a file viewer makes about a file: render it as editable text, or
|
|
230
|
+
* hand it to a media viewer. `media` carries the resolved {@link MediaKind};
|
|
231
|
+
* `text` carries the editor language id.
|
|
232
|
+
*/
|
|
233
|
+
type FileKind = {
|
|
234
|
+
kind: "text";
|
|
235
|
+
language: string;
|
|
236
|
+
} | {
|
|
237
|
+
kind: "media";
|
|
238
|
+
mediaKind: MediaKind;
|
|
239
|
+
};
|
|
240
|
+
interface ResolveFileKindInput {
|
|
241
|
+
/** File name or path — sniffed for an extension (and used for the media check). */
|
|
242
|
+
filename?: string;
|
|
243
|
+
/** MIME type — preferred over the filename when known. */
|
|
244
|
+
mime?: string;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Resolve the fancy-code editor language for a filename. Returns `"plaintext"`
|
|
248
|
+
* (no syntax highlighting) for unknown or extension-less files.
|
|
249
|
+
*/
|
|
250
|
+
declare function languageFromFilename(filename?: string): string;
|
|
251
|
+
/**
|
|
252
|
+
* Decide whether a file is previewable media (image / video / audio / PDF) or
|
|
253
|
+
* text, from its MIME type (preferred) and/or filename. Media wins when the
|
|
254
|
+
* type resolves to one; otherwise it's text and the editor language is derived
|
|
255
|
+
* from the extension.
|
|
256
|
+
*
|
|
257
|
+
* Exposed so consumers can branch their own chrome (tabs, save buttons) the
|
|
258
|
+
* same way `<FileViewer>` does internally.
|
|
259
|
+
*
|
|
260
|
+
* ```ts
|
|
261
|
+
* resolveFileKind({ filename: "logo.png" }); // { kind: "media", mediaKind: "image" }
|
|
262
|
+
* resolveFileKind({ filename: "app.tsx" }); // { kind: "text", language: "typescript" }
|
|
263
|
+
* resolveFileKind({ mime: "application/pdf" }); // { kind: "media", mediaKind: "pdf" }
|
|
264
|
+
* ```
|
|
265
|
+
*/
|
|
266
|
+
declare function resolveFileKind({ filename, mime }: ResolveFileKindInput): FileKind;
|
|
267
|
+
|
|
268
|
+
interface FileViewerProps {
|
|
269
|
+
/**
|
|
270
|
+
* File name or path — used to detect the file kind / editor language, and as
|
|
271
|
+
* a label / alt text. Pass `mime` too when you have it (it's preferred).
|
|
272
|
+
*/
|
|
273
|
+
filename?: string;
|
|
274
|
+
/** MIME type — preferred over `filename` for detection. */
|
|
275
|
+
mime?: string;
|
|
276
|
+
/**
|
|
277
|
+
* Source URL for **media** files (image / video / audio / PDF) —
|
|
278
|
+
* `http(s):` / `data:` / `blob:`. Required when the file resolves to media.
|
|
279
|
+
*/
|
|
280
|
+
src?: string;
|
|
281
|
+
/** Text content for **text** files. */
|
|
282
|
+
value?: string;
|
|
283
|
+
/** Called when text content changes (ignored when `readOnly`). */
|
|
284
|
+
onChange?: (value: string) => void;
|
|
285
|
+
/**
|
|
286
|
+
* Read-only. Defaults to `true` — `FileViewer` is a viewer. Media is always
|
|
287
|
+
* read-only regardless. Set `false` (with `onChange`) to edit text.
|
|
288
|
+
*/
|
|
289
|
+
readOnly?: boolean;
|
|
290
|
+
/** Editor theme for text: `"light" | "dark" | "auto"` or a registered name (default `"auto"`). */
|
|
291
|
+
theme?: string;
|
|
292
|
+
/** Force the file kind, bypassing detection. */
|
|
293
|
+
fileKind?: FileKind;
|
|
294
|
+
/** Show line numbers in the text editor (default `true`). */
|
|
295
|
+
lineNumbers?: boolean;
|
|
296
|
+
/** Wrap long lines in the text editor (default `false`). */
|
|
297
|
+
wordWrap?: boolean;
|
|
298
|
+
/** Editor height bounds (px). */
|
|
299
|
+
minHeight?: number;
|
|
300
|
+
maxHeight?: number;
|
|
301
|
+
/** Extra props forwarded to `<MediaViewer>` for media files. */
|
|
302
|
+
mediaProps?: Partial<Omit<MediaViewerProps, "src" | "mime" | "alt">>;
|
|
303
|
+
className?: string;
|
|
304
|
+
style?: CSSProperties;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* A unified file viewer: renders text files in a read-only `CodeEditor` (with
|
|
309
|
+
* syntax highlighting picked from the filename) and media files — image, video,
|
|
310
|
+
* audio, PDF — via react-fancy's `<MediaViewer>`. The text-vs-media decision is
|
|
311
|
+
* made by {@link resolveFileKind}; pass `fileKind` to override it, or call
|
|
312
|
+
* `resolveFileKind` yourself to branch your own chrome (tabs, save buttons).
|
|
313
|
+
*
|
|
314
|
+
* Give the container a height for media files (PDF/image/video fill their box).
|
|
315
|
+
*
|
|
316
|
+
* ```tsx
|
|
317
|
+
* // text
|
|
318
|
+
* <FileViewer filename="app.tsx" value={source} />
|
|
319
|
+
* // media
|
|
320
|
+
* <FileViewer filename="logo.png" src={url} style={{ height: 360 }} />
|
|
321
|
+
* ```
|
|
322
|
+
*/
|
|
323
|
+
declare function FileViewer({ filename, mime, src, value, onChange, readOnly, theme, fileKind, lineNumbers, wordWrap, minHeight, maxHeight, mediaProps, className, style, }: FileViewerProps): react.JSX.Element;
|
|
324
|
+
declare namespace FileViewer {
|
|
325
|
+
var displayName: string;
|
|
326
|
+
}
|
|
327
|
+
|
|
228
328
|
/**
|
|
229
329
|
* Tiny, dependency-free Markdown → HTML renderer for the MarkdownEditor preview
|
|
230
330
|
* pane. Covers the common subset — headings, bold/italic, inline + fenced code,
|
|
@@ -309,4 +409,4 @@ interface CodeEditorInputTagProps extends Omit<InputTagProps, "adapter"> {
|
|
|
309
409
|
*/
|
|
310
410
|
declare function CodeEditorInputTag(props: CodeEditorInputTagProps): react.JSX.Element;
|
|
311
411
|
|
|
312
|
-
export { CodeEditor, type CodeEditorContextValue, CodeEditorInputTag, type CodeEditorInputTagProps, type CodeEditorPanelProps, type CodeEditorProps, type CodeEditorStatusBarProps, type CodeEditorToolbarProps, type LanguageDefinition, MarkdownEditor, type MarkdownEditorMode, type MarkdownEditorProps, type ThemeColors, type ThemeDefinition, type Token, type TokenType, type Tokenizer, getLanguage, getRegisteredLanguages, getRegisteredThemes, getTheme, registerLanguage, registerTheme, renderMarkdown, tokenizeMarkdown, useCodeEditor, useCodeEditorInputTagAdapter };
|
|
412
|
+
export { CodeEditor, type CodeEditorContextValue, CodeEditorInputTag, type CodeEditorInputTagProps, type CodeEditorPanelProps, type CodeEditorProps, type CodeEditorStatusBarProps, type CodeEditorToolbarProps, type FileKind, FileViewer, type FileViewerProps, type LanguageDefinition, MarkdownEditor, type MarkdownEditorMode, type MarkdownEditorProps, type ResolveFileKindInput, type ThemeColors, type ThemeDefinition, type Token, type TokenType, type Tokenizer, getLanguage, getRegisteredLanguages, getRegisteredThemes, getTheme, languageFromFilename, registerLanguage, registerTheme, renderMarkdown, resolveFileKind, tokenizeMarkdown, useCodeEditor, useCodeEditorInputTagAdapter };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createContext, useState, useCallback, useRef, useMemo, useSyncExternalStore, useEffect, useContext } from 'react';
|
|
2
|
-
import { cn, useControllableState, textareaAdapter, InputTag } from '@particle-academy/react-fancy';
|
|
2
|
+
import { cn, useControllableState, resolveMediaType, MediaViewer, textareaAdapter, InputTag } from '@particle-academy/react-fancy';
|
|
3
3
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
4
4
|
|
|
5
5
|
// src/components/CodeEditor/CodeEditor.tsx
|
|
@@ -1077,6 +1077,7 @@ function tokenizeMarkdown(source) {
|
|
|
1077
1077
|
return tokens;
|
|
1078
1078
|
}
|
|
1079
1079
|
function pushInline(line, base, out) {
|
|
1080
|
+
if (line.length > 5e3) return;
|
|
1080
1081
|
const spans = [];
|
|
1081
1082
|
const add = (type, start, end) => {
|
|
1082
1083
|
spans.push(tok(type, base + start, base + end));
|
|
@@ -1682,8 +1683,11 @@ var CodeEditor = Object.assign(CodeEditorRoot, {
|
|
|
1682
1683
|
function escapeHtml2(s) {
|
|
1683
1684
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
1684
1685
|
}
|
|
1686
|
+
function escapeAttr(s) {
|
|
1687
|
+
return s.replace(/"/g, """).replace(/'/g, "'");
|
|
1688
|
+
}
|
|
1685
1689
|
function inline(text) {
|
|
1686
|
-
return text.replace(/!\[([^\]]*)\]\(([^)\s]+)(?:\s+"([^"]*)")?\)/g, (_m, alt, src, title) => `<img src="${src}" alt="${alt}"${title ? ` title="${title}"` : ""}>`).replace(/\[([^\]]+)\]\(([^)\s]+)(?:\s+"([^"]*)")?\)/g, (_m, t, href, title) => `<a href="${href}"${title ? ` title="${title}"` : ""} rel="noopener">${t}</a>`).replace(/`([^`]+)`/g, (_m, c) => `<code>${c}</code>`).replace(/(\*\*|__)(?=\S)([\s\S]*?\S)\1/g, "<strong>$2</strong>").replace(/(\*|_)(?=\S)([\s\S]*?\S)\1/g, "<em>$2</em>");
|
|
1690
|
+
return text.replace(/!\[([^\]]*)\]\(([^)\s]+)(?:\s+"([^"]*)")?\)/g, (_m, alt, src, title) => `<img src="${escapeAttr(src)}" alt="${escapeAttr(alt)}"${title ? ` title="${escapeAttr(title)}"` : ""}>`).replace(/\[([^\]]+)\]\(([^)\s]+)(?:\s+"([^"]*)")?\)/g, (_m, t, href, title) => `<a href="${escapeAttr(href)}"${title ? ` title="${escapeAttr(title)}"` : ""} rel="noopener">${t}</a>`).replace(/`([^`]+)`/g, (_m, c) => `<code>${c}</code>`).replace(/(\*\*|__)(?=\S)([\s\S]*?\S)\1/g, "<strong>$2</strong>").replace(/(\*|_)(?=\S)([\s\S]*?\S)\1/g, "<em>$2</em>");
|
|
1687
1691
|
}
|
|
1688
1692
|
function renderMarkdown(markdown) {
|
|
1689
1693
|
const lines = markdown.replace(/\r\n?/g, "\n").split("\n");
|
|
@@ -1717,7 +1721,7 @@ function renderMarkdown(markdown) {
|
|
|
1717
1721
|
i++;
|
|
1718
1722
|
}
|
|
1719
1723
|
i++;
|
|
1720
|
-
out.push(`<pre><code${lang ? ` class="language-${escapeHtml2(lang)}"` : ""}>${escapeHtml2(body.join("\n"))}</code></pre>`);
|
|
1724
|
+
out.push(`<pre><code${lang ? ` class="language-${escapeAttr(escapeHtml2(lang))}"` : ""}>${escapeHtml2(body.join("\n"))}</code></pre>`);
|
|
1721
1725
|
continue;
|
|
1722
1726
|
}
|
|
1723
1727
|
const esc = escapeHtml2(line);
|
|
@@ -1827,6 +1831,118 @@ function MarkdownEditor({
|
|
|
1827
1831
|
);
|
|
1828
1832
|
}
|
|
1829
1833
|
MarkdownEditor.displayName = "MarkdownEditor";
|
|
1834
|
+
var EXT_LANGUAGE = {
|
|
1835
|
+
js: "javascript",
|
|
1836
|
+
mjs: "javascript",
|
|
1837
|
+
cjs: "javascript",
|
|
1838
|
+
jsx: "javascript",
|
|
1839
|
+
json: "javascript",
|
|
1840
|
+
// the JS tokenizer highlights JSON acceptably
|
|
1841
|
+
jsonc: "javascript",
|
|
1842
|
+
ts: "typescript",
|
|
1843
|
+
mts: "typescript",
|
|
1844
|
+
cts: "typescript",
|
|
1845
|
+
tsx: "typescript",
|
|
1846
|
+
html: "html",
|
|
1847
|
+
htm: "html",
|
|
1848
|
+
xml: "html",
|
|
1849
|
+
xhtml: "html",
|
|
1850
|
+
vue: "html",
|
|
1851
|
+
php: "php",
|
|
1852
|
+
phtml: "php",
|
|
1853
|
+
py: "python",
|
|
1854
|
+
pyw: "python",
|
|
1855
|
+
go: "go",
|
|
1856
|
+
md: "markdown",
|
|
1857
|
+
markdown: "markdown",
|
|
1858
|
+
mkd: "markdown",
|
|
1859
|
+
mdx: "markdown"
|
|
1860
|
+
};
|
|
1861
|
+
function basename(filename) {
|
|
1862
|
+
return filename.split(/[?#]/, 1)[0].split(/[\\/]/).pop() ?? "";
|
|
1863
|
+
}
|
|
1864
|
+
function languageFromFilename(filename) {
|
|
1865
|
+
if (!filename) return "plaintext";
|
|
1866
|
+
const seg = basename(filename);
|
|
1867
|
+
const dot = seg.lastIndexOf(".");
|
|
1868
|
+
if (dot <= 0) return "plaintext";
|
|
1869
|
+
const ext = seg.slice(dot + 1).toLowerCase();
|
|
1870
|
+
return EXT_LANGUAGE[ext] ?? "plaintext";
|
|
1871
|
+
}
|
|
1872
|
+
function resolveFileKind({ filename, mime }) {
|
|
1873
|
+
const media = resolveMediaType({ mime, src: filename });
|
|
1874
|
+
if (media !== "unknown") return { kind: "media", mediaKind: media };
|
|
1875
|
+
return { kind: "text", language: languageFromFilename(filename) };
|
|
1876
|
+
}
|
|
1877
|
+
function FileViewer({
|
|
1878
|
+
filename,
|
|
1879
|
+
mime,
|
|
1880
|
+
src,
|
|
1881
|
+
value = "",
|
|
1882
|
+
onChange,
|
|
1883
|
+
readOnly = true,
|
|
1884
|
+
theme = "auto",
|
|
1885
|
+
fileKind,
|
|
1886
|
+
lineNumbers = true,
|
|
1887
|
+
wordWrap = false,
|
|
1888
|
+
minHeight,
|
|
1889
|
+
maxHeight,
|
|
1890
|
+
mediaProps,
|
|
1891
|
+
className,
|
|
1892
|
+
style
|
|
1893
|
+
}) {
|
|
1894
|
+
const resolved = fileKind ?? resolveFileKind({ filename, mime });
|
|
1895
|
+
if (resolved.kind === "media") {
|
|
1896
|
+
const { className: mediaClassName, ...restMediaProps } = mediaProps ?? {};
|
|
1897
|
+
const fill = resolved.mediaKind !== "audio";
|
|
1898
|
+
return /* @__PURE__ */ jsx(
|
|
1899
|
+
"div",
|
|
1900
|
+
{
|
|
1901
|
+
"data-fancy-file-viewer": "",
|
|
1902
|
+
"data-kind": "media",
|
|
1903
|
+
"data-media-kind": resolved.mediaKind,
|
|
1904
|
+
className: cn("overflow-hidden", className),
|
|
1905
|
+
style,
|
|
1906
|
+
children: /* @__PURE__ */ jsx(
|
|
1907
|
+
MediaViewer,
|
|
1908
|
+
{
|
|
1909
|
+
src: src ?? "",
|
|
1910
|
+
mime,
|
|
1911
|
+
alt: filename,
|
|
1912
|
+
kind: resolved.mediaKind,
|
|
1913
|
+
className: cn(fill && "h-full w-full", mediaClassName),
|
|
1914
|
+
...restMediaProps
|
|
1915
|
+
}
|
|
1916
|
+
)
|
|
1917
|
+
}
|
|
1918
|
+
);
|
|
1919
|
+
}
|
|
1920
|
+
return /* @__PURE__ */ jsx(
|
|
1921
|
+
"div",
|
|
1922
|
+
{
|
|
1923
|
+
"data-fancy-file-viewer": "",
|
|
1924
|
+
"data-kind": "text",
|
|
1925
|
+
className,
|
|
1926
|
+
style,
|
|
1927
|
+
children: /* @__PURE__ */ jsx(
|
|
1928
|
+
CodeEditor,
|
|
1929
|
+
{
|
|
1930
|
+
value,
|
|
1931
|
+
onChange: readOnly ? void 0 : onChange,
|
|
1932
|
+
language: resolved.language,
|
|
1933
|
+
theme,
|
|
1934
|
+
readOnly,
|
|
1935
|
+
lineNumbers,
|
|
1936
|
+
wordWrap,
|
|
1937
|
+
minHeight,
|
|
1938
|
+
maxHeight,
|
|
1939
|
+
children: /* @__PURE__ */ jsx(CodeEditor.Panel, {})
|
|
1940
|
+
}
|
|
1941
|
+
)
|
|
1942
|
+
}
|
|
1943
|
+
);
|
|
1944
|
+
}
|
|
1945
|
+
FileViewer.displayName = "FileViewer";
|
|
1830
1946
|
function useCodeEditorInputTagAdapter() {
|
|
1831
1947
|
const ctx = useCodeEditor();
|
|
1832
1948
|
return useMemo(() => textareaAdapter(ctx.textareaRef), [ctx.textareaRef]);
|
|
@@ -1836,6 +1952,6 @@ function CodeEditorInputTag(props) {
|
|
|
1836
1952
|
return /* @__PURE__ */ jsx(InputTag, { adapter, ...props });
|
|
1837
1953
|
}
|
|
1838
1954
|
|
|
1839
|
-
export { CodeEditor, CodeEditorInputTag, MarkdownEditor, getLanguage, getRegisteredLanguages, getRegisteredThemes, getTheme, registerLanguage, registerTheme, renderMarkdown, tokenizeMarkdown, useCodeEditor, useCodeEditorInputTagAdapter };
|
|
1955
|
+
export { CodeEditor, CodeEditorInputTag, FileViewer, MarkdownEditor, getLanguage, getRegisteredLanguages, getRegisteredThemes, getTheme, languageFromFilename, registerLanguage, registerTheme, renderMarkdown, resolveFileKind, tokenizeMarkdown, useCodeEditor, useCodeEditorInputTagAdapter };
|
|
1840
1956
|
//# sourceMappingURL=index.js.map
|
|
1841
1957
|
//# sourceMappingURL=index.js.map
|