@macrowing/filepilot-parser-mdoc 0.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/dist/index.cjs ADDED
@@ -0,0 +1,247 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.tsx
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ default: () => MdocParser
34
+ });
35
+ module.exports = __toCommonJS(index_exports);
36
+ var import_react = require("react");
37
+ var import_react_dom = __toESM(require("react-dom"), 1);
38
+ var import_client = require("react-dom/client");
39
+ var import_bundles = require("@docflow/editor/bundles/index.css");
40
+ var import_bundles2 = require("@docflow/mobile/bundles/index.css");
41
+ var import_jsx_runtime = require("react/jsx-runtime");
42
+ var reactRoots = /* @__PURE__ */ new WeakMap();
43
+ var ensureReactDomLegacyRender = () => {
44
+ const legacyReactDOM = import_react_dom.default;
45
+ legacyReactDOM.render ?? (legacyReactDOM.render = (node, container) => {
46
+ const root = reactRoots.get(container) ?? (0, import_client.createRoot)(container);
47
+ reactRoots.set(container, root);
48
+ root.render(node);
49
+ return void 0;
50
+ });
51
+ legacyReactDOM.unmountComponentAtNode ?? (legacyReactDOM.unmountComponentAtNode = (container) => {
52
+ const root = reactRoots.get(container);
53
+ if (!root) return false;
54
+ root.unmount();
55
+ reactRoots.delete(container);
56
+ return true;
57
+ });
58
+ };
59
+ var HIGHLIGHT_ATTRIBUTE = "data-filepilot-block-range-highlight";
60
+ var PREVIOUS_BACKGROUND_ATTRIBUTE = "data-filepilot-previous-background-color";
61
+ var PREVIOUS_BACKGROUND_PRIORITY_ATTRIBUTE = "data-filepilot-previous-background-priority";
62
+ var getDocflowBlocks = (container) => {
63
+ if (!container) return [];
64
+ const blocks = Array.from(container.querySelectorAll(".doc-block"));
65
+ return blocks.length ? blocks : Array.from(container.querySelectorAll("[component-name]"));
66
+ };
67
+ var clearBlockRangeHighlight = (container) => {
68
+ getDocflowBlocks(container).forEach((element) => {
69
+ if (!element.hasAttribute(HIGHLIGHT_ATTRIBUTE)) return;
70
+ element.style.setProperty(
71
+ "background-color",
72
+ element.getAttribute(PREVIOUS_BACKGROUND_ATTRIBUTE) ?? "",
73
+ element.getAttribute(PREVIOUS_BACKGROUND_PRIORITY_ATTRIBUTE) ?? ""
74
+ );
75
+ element.removeAttribute(HIGHLIGHT_ATTRIBUTE);
76
+ element.removeAttribute(PREVIOUS_BACKGROUND_ATTRIBUTE);
77
+ element.removeAttribute(PREVIOUS_BACKGROUND_PRIORITY_ATTRIBUTE);
78
+ });
79
+ };
80
+ var markBlockRangeElement = (element, color) => {
81
+ if (!element.hasAttribute(HIGHLIGHT_ATTRIBUTE)) {
82
+ element.setAttribute(PREVIOUS_BACKGROUND_ATTRIBUTE, element.style.backgroundColor);
83
+ element.setAttribute(PREVIOUS_BACKGROUND_PRIORITY_ATTRIBUTE, element.style.getPropertyPriority("background-color"));
84
+ }
85
+ element.setAttribute(HIGHLIGHT_ATTRIBUTE, "true");
86
+ element.style.setProperty("background-color", color, "important");
87
+ };
88
+ var applyBlockRangeHighlight = (container, range, color) => {
89
+ clearBlockRangeHighlight(container);
90
+ if (!range || !color) return;
91
+ const from = Math.min(range.from, range.to);
92
+ const to = Math.max(range.from, range.to);
93
+ if (!Number.isFinite(from) || !Number.isFinite(to) || from < 1) return;
94
+ getDocflowBlocks(container).slice(from - 1, to).forEach((element) => markBlockRangeElement(element, color));
95
+ };
96
+ var isSourceUrl = (source) => /^(?:https?:|data:|blob:|\/|\.\/|\.\.\/)/i.test(source);
97
+ var readSourceBlob = async (source, fetchOptions) => {
98
+ if (typeof source === "string") {
99
+ if (isSourceUrl(source)) {
100
+ const response = await fetch(source, fetchOptions);
101
+ if (!response.ok) throw new Error(`Failed to load mdoc file: ${response.status} ${response.statusText}`);
102
+ return response.blob();
103
+ }
104
+ return new Blob([source], { type: "application/json" });
105
+ }
106
+ if (source instanceof Blob) return source;
107
+ const buffer = source instanceof Uint8Array ? source.slice().buffer : source;
108
+ return new Blob([buffer], { type: "application/octet-stream" });
109
+ };
110
+ var isJsonBuffer = (buffer) => {
111
+ const data = new Uint8Array(buffer);
112
+ for (let index = 0; index < data.length; index += 1) {
113
+ const byte = data[index];
114
+ if (byte === 239 && data[index + 1] === 187 && data[index + 2] === 191) {
115
+ index += 2;
116
+ continue;
117
+ }
118
+ if (byte === 32 || byte === 10 || byte === 13 || byte === 9) continue;
119
+ return byte === 123 || byte === 91;
120
+ }
121
+ return false;
122
+ };
123
+ var parseJsonContent = (content) => {
124
+ const trimmed = content.trimStart();
125
+ return trimmed.startsWith("{") || trimmed.startsWith("[") ? JSON.parse(content) : content;
126
+ };
127
+ var readMdocContent = async (source, core, fetchOptions) => {
128
+ if (typeof source === "string" && !isSourceUrl(source)) return parseJsonContent(source);
129
+ const blob = await readSourceBlob(source, fetchOptions);
130
+ const buffer = await blob.arrayBuffer();
131
+ if (isJsonBuffer(buffer)) return JSON.parse(new TextDecoder().decode(buffer));
132
+ return core.parseDocflowHistoryContent(buffer);
133
+ };
134
+ var useShallowStableValue = (value) => {
135
+ const ref = (0, import_react.useRef)(value);
136
+ const previous = ref.current;
137
+ const previousKeys = Object.keys(previous);
138
+ const nextKeys = Object.keys(value);
139
+ const isEqual = previousKeys.length === nextKeys.length && nextKeys.every((key) => previous[key] === value[key]);
140
+ if (!isEqual) ref.current = value;
141
+ return ref.current;
142
+ };
143
+ var toDocflowLocale = (locale) => locale?.startsWith("en") ? "en_US" : "zh_CN";
144
+ var createEditor = async (engine, config) => {
145
+ ensureReactDomLegacyRender();
146
+ if (engine === "mobile") {
147
+ const mobile = await import("@docflow/mobile");
148
+ return new mobile.Editor(config);
149
+ }
150
+ const desktop = await import("@docflow/editor");
151
+ return new desktop.PreviewEditor(config);
152
+ };
153
+ var syncEditorContent = (editor, content) => {
154
+ if (typeof content === "string") {
155
+ editor.replaceContent?.(content);
156
+ return;
157
+ }
158
+ if (editor.replaceContentByJSON) {
159
+ editor.replaceContentByJSON(JSON.stringify(content));
160
+ return;
161
+ }
162
+ editor.replaceContent?.(content);
163
+ };
164
+ function MdocParser({ file, className, style, options, locale }) {
165
+ const containerRef = (0, import_react.useRef)(null);
166
+ const [error, setError] = (0, import_react.useState)();
167
+ const typedOptions = options ?? {};
168
+ const {
169
+ line,
170
+ lineRange,
171
+ lineRangeHighlightColor,
172
+ onEditorReady,
173
+ minHeight,
174
+ ...editorOptions
175
+ } = typedOptions;
176
+ const stableEditorOptions = useShallowStableValue(editorOptions);
177
+ const navigationRef = (0, import_react.useRef)({});
178
+ const scrolledLineRef = (0, import_react.useRef)(void 0);
179
+ const highlightedRangeRef = (0, import_react.useRef)(void 0);
180
+ const onEditorReadyRef = (0, import_react.useRef)(onEditorReady);
181
+ const engine = stableEditorOptions.engine ?? "desktop";
182
+ navigationRef.current = { line, lineRange, lineRangeHighlightColor };
183
+ onEditorReadyRef.current = onEditorReady;
184
+ const applyNavigation = (0, import_react.useCallback)((force = false) => {
185
+ const { line: line2, lineRange: lineRange2, lineRangeHighlightColor: lineRangeHighlightColor2 } = navigationRef.current;
186
+ const container = containerRef.current;
187
+ const rangeKey = lineRange2 ? `${lineRange2.from}:${lineRange2.to}:${lineRangeHighlightColor2 ?? ""}` : "";
188
+ if (line2 && scrolledLineRef.current !== line2 && container) {
189
+ getDocflowBlocks(container)[line2 - 1]?.scrollIntoView({ behavior: "smooth", block: "center" });
190
+ scrolledLineRef.current = line2;
191
+ }
192
+ if (!force && highlightedRangeRef.current === rangeKey) return;
193
+ highlightedRangeRef.current = rangeKey;
194
+ applyBlockRangeHighlight(container, lineRange2, lineRangeHighlightColor2);
195
+ }, []);
196
+ (0, import_react.useEffect)(() => {
197
+ let active = true;
198
+ let editor;
199
+ const container = containerRef.current;
200
+ if (!container) return;
201
+ setError(void 0);
202
+ container.innerHTML = "";
203
+ scrolledLineRef.current = void 0;
204
+ highlightedRangeRef.current = void 0;
205
+ import("@docflow/core").then(async (core) => {
206
+ const content = await readMdocContent(file.source, core, stableEditorOptions.fetchOptions);
207
+ if (!active) return;
208
+ const transformedContent = await stableEditorOptions.transformContent?.(content) ?? content;
209
+ if (!active) return;
210
+ const baseConfig = {
211
+ title: file.name ?? "",
212
+ readonly: true,
213
+ preview: true,
214
+ lang: toDocflowLocale(locale),
215
+ ...stableEditorOptions.config,
216
+ ...engine === "mobile" ? stableEditorOptions.mobileConfig : stableEditorOptions.desktopConfig,
217
+ content: transformedContent
218
+ };
219
+ editor = await createEditor(engine, baseConfig);
220
+ if (!active) {
221
+ editor.destroy();
222
+ return;
223
+ }
224
+ await editor.mount(container);
225
+ if (!active) return;
226
+ syncEditorContent(editor, transformedContent);
227
+ onEditorReadyRef.current?.(editor);
228
+ requestAnimationFrame(() => {
229
+ if (active) applyNavigation(true);
230
+ });
231
+ }).catch((error2) => {
232
+ if (active) setError(error2);
233
+ });
234
+ return () => {
235
+ active = false;
236
+ editor?.destroy();
237
+ container.innerHTML = "";
238
+ };
239
+ }, [file.source, file.name, stableEditorOptions, locale, engine, applyNavigation]);
240
+ (0, import_react.useEffect)(() => {
241
+ applyNavigation();
242
+ }, [line, lineRange?.from, lineRange?.to, lineRangeHighlightColor, applyNavigation]);
243
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className, style: { height: "100%", minHeight: minHeight ?? 480, ...style }, children: [
244
+ error ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: error instanceof Error ? error.message : "Failed to render mdoc file." }) : null,
245
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref: containerRef, style: { height: "100%", minHeight: minHeight ?? 480 } })
246
+ ] });
247
+ }
@@ -0,0 +1,36 @@
1
+ import * as react from 'react';
2
+ import { CSSProperties } from 'react';
3
+ import { EditorConfig } from '@docflow/editor';
4
+ import { EditorConfig as EditorConfig$1 } from '@docflow/mobile';
5
+ import { FilePilotRenderProps } from '@macrowing/filepilot-types';
6
+
7
+ type ComponentLiteral = object;
8
+ type DocflowEditorInstance = {
9
+ mount(host: HTMLElement): Promise<unknown>;
10
+ destroy(): void;
11
+ applyUpdate?(data: Uint8Array | ArrayBuffer): void;
12
+ replaceContent?(content: string | ComponentLiteral): void;
13
+ replaceContentByJSON?(json: string): void;
14
+ };
15
+ type DocflowContent = string | ComponentLiteral;
16
+ type BaseDocflowConfig = Omit<EditorConfig, 'content' | 'title' | 'readonly' | 'preview' | 'lang'> & Omit<EditorConfig$1, 'content' | 'title' | 'readonly' | 'preview' | 'lang'>;
17
+ type MdocBlockRange = {
18
+ from: number;
19
+ to: number;
20
+ };
21
+ type MdocParserOptions = {
22
+ line?: number;
23
+ lineRange?: MdocBlockRange;
24
+ lineRangeHighlightColor?: string;
25
+ config?: Partial<BaseDocflowConfig>;
26
+ mobileConfig?: Partial<EditorConfig$1>;
27
+ desktopConfig?: Partial<EditorConfig>;
28
+ engine?: 'desktop' | 'mobile';
29
+ fetchOptions?: RequestInit;
30
+ transformContent?: (content: DocflowContent) => DocflowContent | Promise<DocflowContent>;
31
+ onEditorReady?: (editor: DocflowEditorInstance) => void;
32
+ minHeight?: CSSProperties['minHeight'];
33
+ };
34
+ declare function MdocParser({ file, className, style, options, locale }: FilePilotRenderProps<MdocParserOptions>): react.JSX.Element;
35
+
36
+ export { type MdocBlockRange, type MdocParserOptions, MdocParser as default };
@@ -0,0 +1,36 @@
1
+ import * as react from 'react';
2
+ import { CSSProperties } from 'react';
3
+ import { EditorConfig } from '@docflow/editor';
4
+ import { EditorConfig as EditorConfig$1 } from '@docflow/mobile';
5
+ import { FilePilotRenderProps } from '@macrowing/filepilot-types';
6
+
7
+ type ComponentLiteral = object;
8
+ type DocflowEditorInstance = {
9
+ mount(host: HTMLElement): Promise<unknown>;
10
+ destroy(): void;
11
+ applyUpdate?(data: Uint8Array | ArrayBuffer): void;
12
+ replaceContent?(content: string | ComponentLiteral): void;
13
+ replaceContentByJSON?(json: string): void;
14
+ };
15
+ type DocflowContent = string | ComponentLiteral;
16
+ type BaseDocflowConfig = Omit<EditorConfig, 'content' | 'title' | 'readonly' | 'preview' | 'lang'> & Omit<EditorConfig$1, 'content' | 'title' | 'readonly' | 'preview' | 'lang'>;
17
+ type MdocBlockRange = {
18
+ from: number;
19
+ to: number;
20
+ };
21
+ type MdocParserOptions = {
22
+ line?: number;
23
+ lineRange?: MdocBlockRange;
24
+ lineRangeHighlightColor?: string;
25
+ config?: Partial<BaseDocflowConfig>;
26
+ mobileConfig?: Partial<EditorConfig$1>;
27
+ desktopConfig?: Partial<EditorConfig>;
28
+ engine?: 'desktop' | 'mobile';
29
+ fetchOptions?: RequestInit;
30
+ transformContent?: (content: DocflowContent) => DocflowContent | Promise<DocflowContent>;
31
+ onEditorReady?: (editor: DocflowEditorInstance) => void;
32
+ minHeight?: CSSProperties['minHeight'];
33
+ };
34
+ declare function MdocParser({ file, className, style, options, locale }: FilePilotRenderProps<MdocParserOptions>): react.JSX.Element;
35
+
36
+ export { type MdocBlockRange, type MdocParserOptions, MdocParser as default };
package/dist/index.js ADDED
@@ -0,0 +1,216 @@
1
+ // src/index.tsx
2
+ import { useCallback, useEffect, useRef, useState } from "react";
3
+ import ReactDOM from "react-dom";
4
+ import { createRoot } from "react-dom/client";
5
+ import "@docflow/editor/bundles/index.css";
6
+ import "@docflow/mobile/bundles/index.css";
7
+ import { jsx, jsxs } from "react/jsx-runtime";
8
+ var reactRoots = /* @__PURE__ */ new WeakMap();
9
+ var ensureReactDomLegacyRender = () => {
10
+ const legacyReactDOM = ReactDOM;
11
+ legacyReactDOM.render ?? (legacyReactDOM.render = (node, container) => {
12
+ const root = reactRoots.get(container) ?? createRoot(container);
13
+ reactRoots.set(container, root);
14
+ root.render(node);
15
+ return void 0;
16
+ });
17
+ legacyReactDOM.unmountComponentAtNode ?? (legacyReactDOM.unmountComponentAtNode = (container) => {
18
+ const root = reactRoots.get(container);
19
+ if (!root) return false;
20
+ root.unmount();
21
+ reactRoots.delete(container);
22
+ return true;
23
+ });
24
+ };
25
+ var HIGHLIGHT_ATTRIBUTE = "data-filepilot-block-range-highlight";
26
+ var PREVIOUS_BACKGROUND_ATTRIBUTE = "data-filepilot-previous-background-color";
27
+ var PREVIOUS_BACKGROUND_PRIORITY_ATTRIBUTE = "data-filepilot-previous-background-priority";
28
+ var getDocflowBlocks = (container) => {
29
+ if (!container) return [];
30
+ const blocks = Array.from(container.querySelectorAll(".doc-block"));
31
+ return blocks.length ? blocks : Array.from(container.querySelectorAll("[component-name]"));
32
+ };
33
+ var clearBlockRangeHighlight = (container) => {
34
+ getDocflowBlocks(container).forEach((element) => {
35
+ if (!element.hasAttribute(HIGHLIGHT_ATTRIBUTE)) return;
36
+ element.style.setProperty(
37
+ "background-color",
38
+ element.getAttribute(PREVIOUS_BACKGROUND_ATTRIBUTE) ?? "",
39
+ element.getAttribute(PREVIOUS_BACKGROUND_PRIORITY_ATTRIBUTE) ?? ""
40
+ );
41
+ element.removeAttribute(HIGHLIGHT_ATTRIBUTE);
42
+ element.removeAttribute(PREVIOUS_BACKGROUND_ATTRIBUTE);
43
+ element.removeAttribute(PREVIOUS_BACKGROUND_PRIORITY_ATTRIBUTE);
44
+ });
45
+ };
46
+ var markBlockRangeElement = (element, color) => {
47
+ if (!element.hasAttribute(HIGHLIGHT_ATTRIBUTE)) {
48
+ element.setAttribute(PREVIOUS_BACKGROUND_ATTRIBUTE, element.style.backgroundColor);
49
+ element.setAttribute(PREVIOUS_BACKGROUND_PRIORITY_ATTRIBUTE, element.style.getPropertyPriority("background-color"));
50
+ }
51
+ element.setAttribute(HIGHLIGHT_ATTRIBUTE, "true");
52
+ element.style.setProperty("background-color", color, "important");
53
+ };
54
+ var applyBlockRangeHighlight = (container, range, color) => {
55
+ clearBlockRangeHighlight(container);
56
+ if (!range || !color) return;
57
+ const from = Math.min(range.from, range.to);
58
+ const to = Math.max(range.from, range.to);
59
+ if (!Number.isFinite(from) || !Number.isFinite(to) || from < 1) return;
60
+ getDocflowBlocks(container).slice(from - 1, to).forEach((element) => markBlockRangeElement(element, color));
61
+ };
62
+ var isSourceUrl = (source) => /^(?:https?:|data:|blob:|\/|\.\/|\.\.\/)/i.test(source);
63
+ var readSourceBlob = async (source, fetchOptions) => {
64
+ if (typeof source === "string") {
65
+ if (isSourceUrl(source)) {
66
+ const response = await fetch(source, fetchOptions);
67
+ if (!response.ok) throw new Error(`Failed to load mdoc file: ${response.status} ${response.statusText}`);
68
+ return response.blob();
69
+ }
70
+ return new Blob([source], { type: "application/json" });
71
+ }
72
+ if (source instanceof Blob) return source;
73
+ const buffer = source instanceof Uint8Array ? source.slice().buffer : source;
74
+ return new Blob([buffer], { type: "application/octet-stream" });
75
+ };
76
+ var isJsonBuffer = (buffer) => {
77
+ const data = new Uint8Array(buffer);
78
+ for (let index = 0; index < data.length; index += 1) {
79
+ const byte = data[index];
80
+ if (byte === 239 && data[index + 1] === 187 && data[index + 2] === 191) {
81
+ index += 2;
82
+ continue;
83
+ }
84
+ if (byte === 32 || byte === 10 || byte === 13 || byte === 9) continue;
85
+ return byte === 123 || byte === 91;
86
+ }
87
+ return false;
88
+ };
89
+ var parseJsonContent = (content) => {
90
+ const trimmed = content.trimStart();
91
+ return trimmed.startsWith("{") || trimmed.startsWith("[") ? JSON.parse(content) : content;
92
+ };
93
+ var readMdocContent = async (source, core, fetchOptions) => {
94
+ if (typeof source === "string" && !isSourceUrl(source)) return parseJsonContent(source);
95
+ const blob = await readSourceBlob(source, fetchOptions);
96
+ const buffer = await blob.arrayBuffer();
97
+ if (isJsonBuffer(buffer)) return JSON.parse(new TextDecoder().decode(buffer));
98
+ return core.parseDocflowHistoryContent(buffer);
99
+ };
100
+ var useShallowStableValue = (value) => {
101
+ const ref = useRef(value);
102
+ const previous = ref.current;
103
+ const previousKeys = Object.keys(previous);
104
+ const nextKeys = Object.keys(value);
105
+ const isEqual = previousKeys.length === nextKeys.length && nextKeys.every((key) => previous[key] === value[key]);
106
+ if (!isEqual) ref.current = value;
107
+ return ref.current;
108
+ };
109
+ var toDocflowLocale = (locale) => locale?.startsWith("en") ? "en_US" : "zh_CN";
110
+ var createEditor = async (engine, config) => {
111
+ ensureReactDomLegacyRender();
112
+ if (engine === "mobile") {
113
+ const mobile = await import("@docflow/mobile");
114
+ return new mobile.Editor(config);
115
+ }
116
+ const desktop = await import("@docflow/editor");
117
+ return new desktop.PreviewEditor(config);
118
+ };
119
+ var syncEditorContent = (editor, content) => {
120
+ if (typeof content === "string") {
121
+ editor.replaceContent?.(content);
122
+ return;
123
+ }
124
+ if (editor.replaceContentByJSON) {
125
+ editor.replaceContentByJSON(JSON.stringify(content));
126
+ return;
127
+ }
128
+ editor.replaceContent?.(content);
129
+ };
130
+ function MdocParser({ file, className, style, options, locale }) {
131
+ const containerRef = useRef(null);
132
+ const [error, setError] = useState();
133
+ const typedOptions = options ?? {};
134
+ const {
135
+ line,
136
+ lineRange,
137
+ lineRangeHighlightColor,
138
+ onEditorReady,
139
+ minHeight,
140
+ ...editorOptions
141
+ } = typedOptions;
142
+ const stableEditorOptions = useShallowStableValue(editorOptions);
143
+ const navigationRef = useRef({});
144
+ const scrolledLineRef = useRef(void 0);
145
+ const highlightedRangeRef = useRef(void 0);
146
+ const onEditorReadyRef = useRef(onEditorReady);
147
+ const engine = stableEditorOptions.engine ?? "desktop";
148
+ navigationRef.current = { line, lineRange, lineRangeHighlightColor };
149
+ onEditorReadyRef.current = onEditorReady;
150
+ const applyNavigation = useCallback((force = false) => {
151
+ const { line: line2, lineRange: lineRange2, lineRangeHighlightColor: lineRangeHighlightColor2 } = navigationRef.current;
152
+ const container = containerRef.current;
153
+ const rangeKey = lineRange2 ? `${lineRange2.from}:${lineRange2.to}:${lineRangeHighlightColor2 ?? ""}` : "";
154
+ if (line2 && scrolledLineRef.current !== line2 && container) {
155
+ getDocflowBlocks(container)[line2 - 1]?.scrollIntoView({ behavior: "smooth", block: "center" });
156
+ scrolledLineRef.current = line2;
157
+ }
158
+ if (!force && highlightedRangeRef.current === rangeKey) return;
159
+ highlightedRangeRef.current = rangeKey;
160
+ applyBlockRangeHighlight(container, lineRange2, lineRangeHighlightColor2);
161
+ }, []);
162
+ useEffect(() => {
163
+ let active = true;
164
+ let editor;
165
+ const container = containerRef.current;
166
+ if (!container) return;
167
+ setError(void 0);
168
+ container.innerHTML = "";
169
+ scrolledLineRef.current = void 0;
170
+ highlightedRangeRef.current = void 0;
171
+ import("@docflow/core").then(async (core) => {
172
+ const content = await readMdocContent(file.source, core, stableEditorOptions.fetchOptions);
173
+ if (!active) return;
174
+ const transformedContent = await stableEditorOptions.transformContent?.(content) ?? content;
175
+ if (!active) return;
176
+ const baseConfig = {
177
+ title: file.name ?? "",
178
+ readonly: true,
179
+ preview: true,
180
+ lang: toDocflowLocale(locale),
181
+ ...stableEditorOptions.config,
182
+ ...engine === "mobile" ? stableEditorOptions.mobileConfig : stableEditorOptions.desktopConfig,
183
+ content: transformedContent
184
+ };
185
+ editor = await createEditor(engine, baseConfig);
186
+ if (!active) {
187
+ editor.destroy();
188
+ return;
189
+ }
190
+ await editor.mount(container);
191
+ if (!active) return;
192
+ syncEditorContent(editor, transformedContent);
193
+ onEditorReadyRef.current?.(editor);
194
+ requestAnimationFrame(() => {
195
+ if (active) applyNavigation(true);
196
+ });
197
+ }).catch((error2) => {
198
+ if (active) setError(error2);
199
+ });
200
+ return () => {
201
+ active = false;
202
+ editor?.destroy();
203
+ container.innerHTML = "";
204
+ };
205
+ }, [file.source, file.name, stableEditorOptions, locale, engine, applyNavigation]);
206
+ useEffect(() => {
207
+ applyNavigation();
208
+ }, [line, lineRange?.from, lineRange?.to, lineRangeHighlightColor, applyNavigation]);
209
+ return /* @__PURE__ */ jsxs("div", { className, style: { height: "100%", minHeight: minHeight ?? 480, ...style }, children: [
210
+ error ? /* @__PURE__ */ jsx("div", { children: error instanceof Error ? error.message : "Failed to render mdoc file." }) : null,
211
+ /* @__PURE__ */ jsx("div", { ref: containerRef, style: { height: "100%", minHeight: minHeight ?? 480 } })
212
+ ] });
213
+ }
214
+ export {
215
+ MdocParser as default
216
+ };
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@macrowing/filepilot-parser-mdoc",
3
+ "version": "0.1.0",
4
+ "description": "Docflow mdoc parser powered by @docflow/editor for FilePilot.",
5
+ "type": "module",
6
+ "sideEffects": [
7
+ "*.css"
8
+ ],
9
+ "main": "./dist/index.cjs",
10
+ "module": "./dist/index.js",
11
+ "types": "./dist/index.d.ts",
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "import": "./dist/index.js",
16
+ "require": "./dist/index.cjs"
17
+ }
18
+ },
19
+ "files": [
20
+ "dist"
21
+ ],
22
+ "peerDependencies": {
23
+ "react": ">=18.2.0 || >=19.0.0",
24
+ "react-dom": ">=18.2.0 || >=19.0.0"
25
+ },
26
+ "dependencies": {
27
+ "@docflow/common": "2.17.0",
28
+ "@docflow/core": "2.17.0",
29
+ "@docflow/editor": "2.17.0",
30
+ "@docflow/mobile": "2.17.0",
31
+ "@docflow/mobile-core": "2.17.0",
32
+ "@docflow/ui": "2.17.0",
33
+ "@macrowing/filepilot-types": "0.1.0"
34
+ },
35
+ "devDependencies": {
36
+ "@types/react": "^19.0.12",
37
+ "rimraf": "^6.0.1",
38
+ "tsup": "^8.4.0"
39
+ },
40
+ "publishConfig": {
41
+ "access": "public"
42
+ },
43
+ "scripts": {
44
+ "build": "tsup src/index.tsx --format esm,cjs --dts --clean --external react --external react-dom --external react-dom/client --external @docflow/common --external @docflow/core --external @docflow/editor --external @docflow/mobile --external @docflow/mobile-core --external @docflow/ui",
45
+ "clean": "rimraf dist",
46
+ "typecheck": "tsc -p tsconfig.json --noEmit"
47
+ }
48
+ }