@particle-academy/fancy-code 0.14.0 → 0.15.1
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 +28 -0
- package/dist/index.cjs +78 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +51 -3
- package/dist/index.d.ts +51 -3
- package/dist/index.js +77 -3
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import { RefObject, ReactNode, CSSProperties } from 'react';
|
|
2
|
+
import { RefObject, ReactNode, CSSProperties, ComponentType } from 'react';
|
|
3
3
|
import { ThemeColors, DiffAnnotations, LineChangeType, Token, Tokenizer } from '@particle-academy/fancy-file-commons';
|
|
4
4
|
export { ThemeColors, Token, TokenType, Tokenizer, languageFromFilename } from '@particle-academy/fancy-file-commons';
|
|
5
5
|
import { MediaKind, MediaViewerProps, FauxClientProps, InputTagProps, InputTagTriggers, InputTagAdapter } from '@particle-academy/react-fancy';
|
|
@@ -379,6 +379,12 @@ interface FileViewerProps {
|
|
|
379
379
|
* and full screen, revealed on hover): `variant`, a controlled `zoom`, …
|
|
380
380
|
*/
|
|
381
381
|
frameProps?: Partial<FauxClientProps>;
|
|
382
|
+
/**
|
|
383
|
+
* An Office file's contents, already read on the server (dark-slide /
|
|
384
|
+
* holy-sheet / last-word), for the viewer registered with
|
|
385
|
+
* `registerOfficeViewer`. Preferred over reading `src` in the browser.
|
|
386
|
+
*/
|
|
387
|
+
document?: unknown;
|
|
382
388
|
className?: string;
|
|
383
389
|
style?: CSSProperties;
|
|
384
390
|
}
|
|
@@ -399,11 +405,53 @@ interface FileViewerProps {
|
|
|
399
405
|
* <FileViewer filename="logo.png" src={url} style={{ height: 360 }} />
|
|
400
406
|
* ```
|
|
401
407
|
*/
|
|
402
|
-
declare function FileViewer({ filename, mime, src, value, onChange, readOnly, theme, fileKind, lineNumbers, wordWrap, line, column, highlightLines, minHeight, maxHeight, mediaProps, view, frameProps, className, style, }: FileViewerProps): react.JSX.Element;
|
|
408
|
+
declare function FileViewer({ filename, mime, src, value, onChange, readOnly, theme, fileKind, lineNumbers, wordWrap, line, column, highlightLines, minHeight, maxHeight, mediaProps, view, frameProps, document, className, style, }: FileViewerProps): react.JSX.Element;
|
|
403
409
|
declare namespace FileViewer {
|
|
404
410
|
var displayName: string;
|
|
405
411
|
}
|
|
406
412
|
|
|
413
|
+
/** What a registered Office viewer is given. */
|
|
414
|
+
interface OfficeViewerProps {
|
|
415
|
+
/** The file's contents as our reader returns them (a dark-slide deck, a holy-sheet workbook, a last-word doc). */
|
|
416
|
+
document: unknown;
|
|
417
|
+
filename?: string;
|
|
418
|
+
/** Passed through from `FileViewer`, for viewers that sit in the FauxClient frame. */
|
|
419
|
+
frameProps?: Partial<FauxClientProps>;
|
|
420
|
+
}
|
|
421
|
+
interface OfficeViewerOptions {
|
|
422
|
+
/**
|
|
423
|
+
* Read the raw file in the browser, for when the app has only a URL. Give it
|
|
424
|
+
* our JS reader, e.g. `(bytes) => new PptxReader().read(bytes)`. Without it,
|
|
425
|
+
* a file with no server-read `document` gets the download card.
|
|
426
|
+
*/
|
|
427
|
+
read?: (bytes: Uint8Array) => unknown;
|
|
428
|
+
}
|
|
429
|
+
interface Registration {
|
|
430
|
+
View: ComponentType<OfficeViewerProps>;
|
|
431
|
+
read?: (bytes: Uint8Array) => unknown;
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Show one Office format in `FileViewer` with a kit viewer. Call once at app
|
|
435
|
+
* start, per format you want:
|
|
436
|
+
*
|
|
437
|
+
* ```ts
|
|
438
|
+
* import { registerOfficeViewer } from "@particle-academy/fancy-code";
|
|
439
|
+
* import { DeckFileView } from "@particle-academy/fancy-slides";
|
|
440
|
+
* import { PptxReader } from "@particle-academy/dark-slide";
|
|
441
|
+
*
|
|
442
|
+
* registerOfficeViewer("pptx", DeckFileView, { read: (b) => new PptxReader().read(b) });
|
|
443
|
+
* ```
|
|
444
|
+
*
|
|
445
|
+
* A registry rather than an import because fancy-code must not depend on the
|
|
446
|
+
* slide or sheet packages: an app that has neither (and does not want them)
|
|
447
|
+
* would otherwise fail to build on a file type it never opens.
|
|
448
|
+
*/
|
|
449
|
+
declare function registerOfficeViewer(kind: OfficeKind, View: ComponentType<OfficeViewerProps>, options?: OfficeViewerOptions): void;
|
|
450
|
+
/** The viewer registered for a format, if any. */
|
|
451
|
+
declare function officeViewerFor(kind: OfficeKind): Registration | undefined;
|
|
452
|
+
/** Forget every registration (tests, hot reload). */
|
|
453
|
+
declare function clearOfficeViewers(): void;
|
|
454
|
+
|
|
407
455
|
/**
|
|
408
456
|
* Diff-gutter model — the pure mapping between fancy-file-commons line
|
|
409
457
|
* annotations and what the CodeEditor gutter renders per line-number row.
|
|
@@ -503,4 +551,4 @@ interface CodeEditorInputTagProps extends Omit<InputTagProps, "adapter"> {
|
|
|
503
551
|
*/
|
|
504
552
|
declare function CodeEditorInputTag(props: CodeEditorInputTagProps): react.JSX.Element;
|
|
505
553
|
|
|
506
|
-
export { CodeEditor, type CodeEditorContextValue, CodeEditorInputTag, type CodeEditorInputTagProps, type CodeEditorPanelProps, type CodeEditorProps, type CodeEditorStatusBarProps, type CodeEditorToolbarProps, type FileKind, FileViewer, type FileViewerProps, type GutterDiffMark, type LanguageDefinition, MarkdownEditor, type MarkdownEditorMode, type MarkdownEditorProps, type ResolveFileKindInput, type ThemeDefinition, diffMarkColor, diffRemovedColor, getLanguage, getRegisteredLanguages, getRegisteredThemes, getTheme, gutterDiffMark, hasDeletedAtEnd, registerLanguage, registerTheme, renderMarkdown, resolveFileKind, tokenizeMarkdown, useCodeEditor, useCodeEditorInputTagAdapter };
|
|
554
|
+
export { CodeEditor, type CodeEditorContextValue, CodeEditorInputTag, type CodeEditorInputTagProps, type CodeEditorPanelProps, type CodeEditorProps, type CodeEditorStatusBarProps, type CodeEditorToolbarProps, type DocumentFormat, type FileKind, FileViewer, type FileViewerProps, type GutterDiffMark, type LanguageDefinition, MarkdownEditor, type MarkdownEditorMode, type MarkdownEditorProps, type OfficeKind, type OfficeViewerOptions, type OfficeViewerProps, type ResolveFileKindInput, type ThemeDefinition, clearOfficeViewers, diffMarkColor, diffRemovedColor, getLanguage, getRegisteredLanguages, getRegisteredThemes, getTheme, gutterDiffMark, hasDeletedAtEnd, officeViewerFor, registerLanguage, registerOfficeViewer, registerTheme, renderMarkdown, resolveFileKind, tokenizeMarkdown, useCodeEditor, useCodeEditorInputTagAdapter };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import { RefObject, ReactNode, CSSProperties } from 'react';
|
|
2
|
+
import { RefObject, ReactNode, CSSProperties, ComponentType } from 'react';
|
|
3
3
|
import { ThemeColors, DiffAnnotations, LineChangeType, Token, Tokenizer } from '@particle-academy/fancy-file-commons';
|
|
4
4
|
export { ThemeColors, Token, TokenType, Tokenizer, languageFromFilename } from '@particle-academy/fancy-file-commons';
|
|
5
5
|
import { MediaKind, MediaViewerProps, FauxClientProps, InputTagProps, InputTagTriggers, InputTagAdapter } from '@particle-academy/react-fancy';
|
|
@@ -379,6 +379,12 @@ interface FileViewerProps {
|
|
|
379
379
|
* and full screen, revealed on hover): `variant`, a controlled `zoom`, …
|
|
380
380
|
*/
|
|
381
381
|
frameProps?: Partial<FauxClientProps>;
|
|
382
|
+
/**
|
|
383
|
+
* An Office file's contents, already read on the server (dark-slide /
|
|
384
|
+
* holy-sheet / last-word), for the viewer registered with
|
|
385
|
+
* `registerOfficeViewer`. Preferred over reading `src` in the browser.
|
|
386
|
+
*/
|
|
387
|
+
document?: unknown;
|
|
382
388
|
className?: string;
|
|
383
389
|
style?: CSSProperties;
|
|
384
390
|
}
|
|
@@ -399,11 +405,53 @@ interface FileViewerProps {
|
|
|
399
405
|
* <FileViewer filename="logo.png" src={url} style={{ height: 360 }} />
|
|
400
406
|
* ```
|
|
401
407
|
*/
|
|
402
|
-
declare function FileViewer({ filename, mime, src, value, onChange, readOnly, theme, fileKind, lineNumbers, wordWrap, line, column, highlightLines, minHeight, maxHeight, mediaProps, view, frameProps, className, style, }: FileViewerProps): react.JSX.Element;
|
|
408
|
+
declare function FileViewer({ filename, mime, src, value, onChange, readOnly, theme, fileKind, lineNumbers, wordWrap, line, column, highlightLines, minHeight, maxHeight, mediaProps, view, frameProps, document, className, style, }: FileViewerProps): react.JSX.Element;
|
|
403
409
|
declare namespace FileViewer {
|
|
404
410
|
var displayName: string;
|
|
405
411
|
}
|
|
406
412
|
|
|
413
|
+
/** What a registered Office viewer is given. */
|
|
414
|
+
interface OfficeViewerProps {
|
|
415
|
+
/** The file's contents as our reader returns them (a dark-slide deck, a holy-sheet workbook, a last-word doc). */
|
|
416
|
+
document: unknown;
|
|
417
|
+
filename?: string;
|
|
418
|
+
/** Passed through from `FileViewer`, for viewers that sit in the FauxClient frame. */
|
|
419
|
+
frameProps?: Partial<FauxClientProps>;
|
|
420
|
+
}
|
|
421
|
+
interface OfficeViewerOptions {
|
|
422
|
+
/**
|
|
423
|
+
* Read the raw file in the browser, for when the app has only a URL. Give it
|
|
424
|
+
* our JS reader, e.g. `(bytes) => new PptxReader().read(bytes)`. Without it,
|
|
425
|
+
* a file with no server-read `document` gets the download card.
|
|
426
|
+
*/
|
|
427
|
+
read?: (bytes: Uint8Array) => unknown;
|
|
428
|
+
}
|
|
429
|
+
interface Registration {
|
|
430
|
+
View: ComponentType<OfficeViewerProps>;
|
|
431
|
+
read?: (bytes: Uint8Array) => unknown;
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Show one Office format in `FileViewer` with a kit viewer. Call once at app
|
|
435
|
+
* start, per format you want:
|
|
436
|
+
*
|
|
437
|
+
* ```ts
|
|
438
|
+
* import { registerOfficeViewer } from "@particle-academy/fancy-code";
|
|
439
|
+
* import { DeckFileView } from "@particle-academy/fancy-slides";
|
|
440
|
+
* import { PptxReader } from "@particle-academy/dark-slide";
|
|
441
|
+
*
|
|
442
|
+
* registerOfficeViewer("pptx", DeckFileView, { read: (b) => new PptxReader().read(b) });
|
|
443
|
+
* ```
|
|
444
|
+
*
|
|
445
|
+
* A registry rather than an import because fancy-code must not depend on the
|
|
446
|
+
* slide or sheet packages: an app that has neither (and does not want them)
|
|
447
|
+
* would otherwise fail to build on a file type it never opens.
|
|
448
|
+
*/
|
|
449
|
+
declare function registerOfficeViewer(kind: OfficeKind, View: ComponentType<OfficeViewerProps>, options?: OfficeViewerOptions): void;
|
|
450
|
+
/** The viewer registered for a format, if any. */
|
|
451
|
+
declare function officeViewerFor(kind: OfficeKind): Registration | undefined;
|
|
452
|
+
/** Forget every registration (tests, hot reload). */
|
|
453
|
+
declare function clearOfficeViewers(): void;
|
|
454
|
+
|
|
407
455
|
/**
|
|
408
456
|
* Diff-gutter model — the pure mapping between fancy-file-commons line
|
|
409
457
|
* annotations and what the CodeEditor gutter renders per line-number row.
|
|
@@ -503,4 +551,4 @@ interface CodeEditorInputTagProps extends Omit<InputTagProps, "adapter"> {
|
|
|
503
551
|
*/
|
|
504
552
|
declare function CodeEditorInputTag(props: CodeEditorInputTagProps): react.JSX.Element;
|
|
505
553
|
|
|
506
|
-
export { CodeEditor, type CodeEditorContextValue, CodeEditorInputTag, type CodeEditorInputTagProps, type CodeEditorPanelProps, type CodeEditorProps, type CodeEditorStatusBarProps, type CodeEditorToolbarProps, type FileKind, FileViewer, type FileViewerProps, type GutterDiffMark, type LanguageDefinition, MarkdownEditor, type MarkdownEditorMode, type MarkdownEditorProps, type ResolveFileKindInput, type ThemeDefinition, diffMarkColor, diffRemovedColor, getLanguage, getRegisteredLanguages, getRegisteredThemes, getTheme, gutterDiffMark, hasDeletedAtEnd, registerLanguage, registerTheme, renderMarkdown, resolveFileKind, tokenizeMarkdown, useCodeEditor, useCodeEditorInputTagAdapter };
|
|
554
|
+
export { CodeEditor, type CodeEditorContextValue, CodeEditorInputTag, type CodeEditorInputTagProps, type CodeEditorPanelProps, type CodeEditorProps, type CodeEditorStatusBarProps, type CodeEditorToolbarProps, type DocumentFormat, type FileKind, FileViewer, type FileViewerProps, type GutterDiffMark, type LanguageDefinition, MarkdownEditor, type MarkdownEditorMode, type MarkdownEditorProps, type OfficeKind, type OfficeViewerOptions, type OfficeViewerProps, type ResolveFileKindInput, type ThemeDefinition, clearOfficeViewers, diffMarkColor, diffRemovedColor, getLanguage, getRegisteredLanguages, getRegisteredThemes, getTheme, gutterDiffMark, hasDeletedAtEnd, officeViewerFor, registerLanguage, registerOfficeViewer, registerTheme, renderMarkdown, resolveFileKind, tokenizeMarkdown, useCodeEditor, useCodeEditorInputTagAdapter };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createContext, useMemo, useState, useCallback, useRef, useEffect, useSyncExternalStore, useContext } from 'react';
|
|
2
|
-
import { cn, useControllableState, ContentRenderer, resolveMediaType, FauxClient, MediaViewer, textareaAdapter, InputTag } from '@particle-academy/react-fancy';
|
|
2
|
+
import { cn, useControllableState, ContentRenderer, resolveMediaType, FauxClient, MediaViewer, Callout, textareaAdapter, InputTag } from '@particle-academy/react-fancy';
|
|
3
3
|
import { annotateLines, highlightCode, tok, tokenizeHtml, LIGHT_COLORS, DARK_COLORS, languageFromFilename } from '@particle-academy/fancy-file-commons';
|
|
4
4
|
export { languageFromFilename } from '@particle-academy/fancy-file-commons';
|
|
5
5
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
@@ -1720,6 +1720,18 @@ function MarkdownEditor({
|
|
|
1720
1720
|
);
|
|
1721
1721
|
}
|
|
1722
1722
|
MarkdownEditor.displayName = "MarkdownEditor";
|
|
1723
|
+
|
|
1724
|
+
// src/office-viewers.ts
|
|
1725
|
+
var registry = /* @__PURE__ */ new Map();
|
|
1726
|
+
function registerOfficeViewer(kind, View, options = {}) {
|
|
1727
|
+
registry.set(kind, { View, read: options.read });
|
|
1728
|
+
}
|
|
1729
|
+
function officeViewerFor(kind) {
|
|
1730
|
+
return registry.get(kind);
|
|
1731
|
+
}
|
|
1732
|
+
function clearOfficeViewers() {
|
|
1733
|
+
registry.clear();
|
|
1734
|
+
}
|
|
1723
1735
|
var OFFICE_EXT = { docx: "docx", xlsx: "xlsx", xlsm: "xlsx", pptx: "pptx" };
|
|
1724
1736
|
var OFFICE_MIME = {
|
|
1725
1737
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.document": "docx",
|
|
@@ -1779,6 +1791,7 @@ function FileViewer({
|
|
|
1779
1791
|
mediaProps,
|
|
1780
1792
|
view = "rendered",
|
|
1781
1793
|
frameProps,
|
|
1794
|
+
document,
|
|
1782
1795
|
className,
|
|
1783
1796
|
style
|
|
1784
1797
|
}) {
|
|
@@ -1811,7 +1824,17 @@ function FileViewer({
|
|
|
1811
1824
|
"data-office-kind": resolved.officeKind,
|
|
1812
1825
|
className: cn("overflow-hidden", className),
|
|
1813
1826
|
style,
|
|
1814
|
-
children: /* @__PURE__ */ jsx(
|
|
1827
|
+
children: /* @__PURE__ */ jsx(
|
|
1828
|
+
OfficeFile,
|
|
1829
|
+
{
|
|
1830
|
+
kind: resolved.officeKind,
|
|
1831
|
+
document,
|
|
1832
|
+
src,
|
|
1833
|
+
mime,
|
|
1834
|
+
filename,
|
|
1835
|
+
frameProps
|
|
1836
|
+
}
|
|
1837
|
+
)
|
|
1815
1838
|
}
|
|
1816
1839
|
);
|
|
1817
1840
|
}
|
|
@@ -1871,6 +1894,57 @@ function FileViewer({
|
|
|
1871
1894
|
);
|
|
1872
1895
|
}
|
|
1873
1896
|
FileViewer.displayName = "FileViewer";
|
|
1897
|
+
function OfficeFile({
|
|
1898
|
+
kind,
|
|
1899
|
+
document,
|
|
1900
|
+
src,
|
|
1901
|
+
mime,
|
|
1902
|
+
filename,
|
|
1903
|
+
frameProps
|
|
1904
|
+
}) {
|
|
1905
|
+
const registration = officeViewerFor(kind);
|
|
1906
|
+
const read = registration?.read;
|
|
1907
|
+
const [state, setState] = useState({ status: "idle" });
|
|
1908
|
+
useEffect(() => {
|
|
1909
|
+
if (!registration || document !== void 0 || !src || !read) return;
|
|
1910
|
+
let live = true;
|
|
1911
|
+
setState({ status: "reading" });
|
|
1912
|
+
fetch(src).then(async (response) => {
|
|
1913
|
+
if (!response.ok) throw new Error(`The file could not be fetched (${response.status}).`);
|
|
1914
|
+
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
1915
|
+
const doc = read(bytes);
|
|
1916
|
+
if (live) setState({ status: "read", document: doc });
|
|
1917
|
+
}).catch((error) => {
|
|
1918
|
+
if (live) setState({ status: "failed", message: error instanceof Error ? error.message : String(error) });
|
|
1919
|
+
});
|
|
1920
|
+
return () => {
|
|
1921
|
+
live = false;
|
|
1922
|
+
};
|
|
1923
|
+
}, [registration, document, src, read]);
|
|
1924
|
+
const fallback = /* @__PURE__ */ jsx(MediaViewer, { src: src ?? "", mime, alt: filename, className: "h-full w-full" });
|
|
1925
|
+
if (!registration) return fallback;
|
|
1926
|
+
const View = registration.View;
|
|
1927
|
+
if (document !== void 0) return /* @__PURE__ */ jsx(View, { document, filename, frameProps });
|
|
1928
|
+
if (state.status === "read") return /* @__PURE__ */ jsx(View, { document: state.document, filename, frameProps });
|
|
1929
|
+
if (state.status === "reading") {
|
|
1930
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex h-full items-center justify-center p-6 text-sm text-zinc-500", children: [
|
|
1931
|
+
"Opening ",
|
|
1932
|
+
filename ?? "the file",
|
|
1933
|
+
"\u2026"
|
|
1934
|
+
] });
|
|
1935
|
+
}
|
|
1936
|
+
if (state.status === "failed") {
|
|
1937
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex h-full flex-col gap-2 p-2", children: [
|
|
1938
|
+
/* @__PURE__ */ jsxs(Callout, { color: "red", children: [
|
|
1939
|
+
filename ?? "This file",
|
|
1940
|
+
" could not be opened: ",
|
|
1941
|
+
state.message
|
|
1942
|
+
] }),
|
|
1943
|
+
fallback
|
|
1944
|
+
] });
|
|
1945
|
+
}
|
|
1946
|
+
return fallback;
|
|
1947
|
+
}
|
|
1874
1948
|
var ESCAPES = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" };
|
|
1875
1949
|
function textToHtml(text) {
|
|
1876
1950
|
return text.replace(/\r\n?/g, "\n").split(/\n{2,}/).filter((block) => block.trim() !== "").map((block) => `<p>${block.replace(/[&<>"']/g, (c) => ESCAPES[c]).replace(/\n/g, "<br>")}</p>`).join("");
|
|
@@ -2002,6 +2076,6 @@ function CodeEditorInputTag(props) {
|
|
|
2002
2076
|
return /* @__PURE__ */ jsx(InputTag, { adapter, ...props });
|
|
2003
2077
|
}
|
|
2004
2078
|
|
|
2005
|
-
export { CodeEditor, CodeEditorInputTag, FileViewer, MarkdownEditor, diffMarkColor, diffRemovedColor, getLanguage, getRegisteredLanguages, getRegisteredThemes, getTheme, gutterDiffMark, hasDeletedAtEnd, registerLanguage, registerTheme, renderMarkdown, resolveFileKind, tokenizeMarkdown, useCodeEditor, useCodeEditorInputTagAdapter };
|
|
2079
|
+
export { CodeEditor, CodeEditorInputTag, FileViewer, MarkdownEditor, clearOfficeViewers, diffMarkColor, diffRemovedColor, getLanguage, getRegisteredLanguages, getRegisteredThemes, getTheme, gutterDiffMark, hasDeletedAtEnd, officeViewerFor, registerLanguage, registerOfficeViewer, registerTheme, renderMarkdown, resolveFileKind, tokenizeMarkdown, useCodeEditor, useCodeEditorInputTagAdapter };
|
|
2006
2080
|
//# sourceMappingURL=index.js.map
|
|
2007
2081
|
//# sourceMappingURL=index.js.map
|