@docfuse/plugins 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.
Files changed (47) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +52 -0
  3. package/README.zh-CN.md +52 -0
  4. package/dist/client/kroki.js +221 -0
  5. package/dist/client/mermaid.js +366 -0
  6. package/dist/client/plantuml.js +221 -0
  7. package/dist/diagram.css +195 -0
  8. package/dist/external-links.d.ts +10 -0
  9. package/dist/external-links.js +49 -0
  10. package/dist/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  11. package/dist/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  12. package/dist/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  13. package/dist/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  14. package/dist/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  15. package/dist/fonts/KaTeX_Main-Bold.woff2 +0 -0
  16. package/dist/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  17. package/dist/fonts/KaTeX_Main-Italic.woff2 +0 -0
  18. package/dist/fonts/KaTeX_Main-Regular.woff2 +0 -0
  19. package/dist/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  20. package/dist/fonts/KaTeX_Math-Italic.woff2 +0 -0
  21. package/dist/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  22. package/dist/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  23. package/dist/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  24. package/dist/fonts/KaTeX_Script-Regular.woff2 +0 -0
  25. package/dist/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  26. package/dist/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  27. package/dist/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  28. package/dist/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  29. package/dist/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  30. package/dist/index.d.ts +9 -0
  31. package/dist/index.js +757 -0
  32. package/dist/kroki.d.ts +12 -0
  33. package/dist/kroki.js +293 -0
  34. package/dist/link-card.d.ts +9 -0
  35. package/dist/link-card.js +87 -0
  36. package/dist/math.css +3 -0
  37. package/dist/math.d.ts +19 -0
  38. package/dist/math.js +140 -0
  39. package/dist/mermaid.d.ts +10 -0
  40. package/dist/mermaid.js +290 -0
  41. package/dist/pagefind.d.ts +66 -0
  42. package/dist/pagefind.js +84 -0
  43. package/dist/plantuml.d.ts +10 -0
  44. package/dist/plantuml.js +297 -0
  45. package/dist/reading-time.d.ts +20 -0
  46. package/dist/reading-time.js +90 -0
  47. package/package.json +122 -0
@@ -0,0 +1,297 @@
1
+ // src/plantuml/index.ts
2
+ import { defineMarkdownPlugin } from "@docfuse/markdown";
3
+ import { deflateSync, strToU8 } from "fflate";
4
+
5
+ // src/shared/diagram.ts
6
+ import { visit } from "unist-util-visit";
7
+ function displayKind(kind) {
8
+ if (kind === "plantuml") return "PlantUML";
9
+ return kind.charAt(0).toUpperCase() + kind.slice(1);
10
+ }
11
+ var DIAGRAM_LABELS = {
12
+ en: {
13
+ copy: "Copy source",
14
+ source: "Show source",
15
+ preview: "Show preview",
16
+ expand: "Expand diagram",
17
+ close: "Close expanded diagram",
18
+ controls: "Diagram zoom controls",
19
+ zoomOut: "Zoom out",
20
+ zoomReset: "Reset zoom",
21
+ zoomIn: "Zoom in",
22
+ imageAlt: (kind) => `${displayKind(kind)} diagram`,
23
+ loadingPreview: "Diagram preview loads in the browser."
24
+ },
25
+ zh: {
26
+ copy: "\u590D\u5236\u56FE\u8868\u6E90\u7801",
27
+ source: "\u663E\u793A\u56FE\u8868\u6E90\u7801",
28
+ preview: "\u663E\u793A\u56FE\u8868\u9884\u89C8",
29
+ expand: "\u653E\u5927\u56FE\u8868",
30
+ close: "\u5173\u95ED\u56FE\u8868\u9884\u89C8",
31
+ controls: "\u56FE\u8868\u7F29\u653E\u63A7\u4EF6",
32
+ zoomOut: "\u7F29\u5C0F\u56FE\u8868",
33
+ zoomReset: "\u91CD\u7F6E\u56FE\u8868\u7F29\u653E",
34
+ zoomIn: "\u653E\u5927\u56FE\u8868",
35
+ imageAlt: (kind) => `${displayKind(kind)} \u56FE\u8868`,
36
+ loadingPreview: "\u56FE\u8868\u9884\u89C8\u5C06\u5728\u6D4F\u89C8\u5668\u4E2D\u52A0\u8F7D\u3002"
37
+ }
38
+ };
39
+ function labelsFromFile(file) {
40
+ if (!file || typeof file !== "object" || !("data" in file)) return DIAGRAM_LABELS.en;
41
+ const data = file.data;
42
+ if (!data || typeof data !== "object" || !("docfuseLocale" in data)) return DIAGRAM_LABELS.en;
43
+ const locale = typeof data.docfuseLocale === "string" ? data.docfuseLocale.toLowerCase() : "";
44
+ return locale === "zh" || locale.startsWith("zh-") ? DIAGRAM_LABELS.zh : DIAGRAM_LABELS.en;
45
+ }
46
+ function languageOf(node) {
47
+ const code = node.children.find(
48
+ (child) => child.type === "element" && child.tagName === "code"
49
+ );
50
+ if (!code) return void 0;
51
+ const classes = Array.isArray(code.properties.className) ? code.properties.className : [];
52
+ const language = classes.map(String).find((className) => className.startsWith("language-"))?.slice("language-".length).toLowerCase();
53
+ const source = code.children.filter((child) => child.type === "text").map((child) => child.value).join("");
54
+ return language ? { language, source } : void 0;
55
+ }
56
+ function action(name, label, properties = {}) {
57
+ return {
58
+ type: "element",
59
+ tagName: "button",
60
+ properties: { type: "button", dataDfDiagramAction: name, ariaLabel: label, ...properties },
61
+ children: [{ type: "text", value: label }]
62
+ };
63
+ }
64
+ function zoomControl(name, label) {
65
+ return {
66
+ type: "element",
67
+ tagName: "button",
68
+ properties: { type: "button", dataDfDiagramAction: name, ariaLabel: label, title: label },
69
+ children: [{ type: "text", value: label }]
70
+ };
71
+ }
72
+ function diagramFence(options) {
73
+ return () => (tree, file) => {
74
+ const labels = labelsFromFile(file);
75
+ visit(tree, "element", (node, index, parent) => {
76
+ if (!parent || index === void 0 || node.tagName !== "pre") return;
77
+ const fence = languageOf(node);
78
+ if (!fence || !options.languages.has(fence.language)) return;
79
+ const imageUrl = options.imageUrl?.(fence.source, fence.language);
80
+ const hasVisualPreview = Boolean(imageUrl || options.kind === "mermaid");
81
+ const preview = imageUrl ? {
82
+ type: "element",
83
+ tagName: "img",
84
+ properties: {
85
+ className: ["df-diagram-img"],
86
+ src: imageUrl,
87
+ alt: labels.imageAlt(options.kind),
88
+ loading: "lazy"
89
+ },
90
+ children: []
91
+ } : {
92
+ type: "element",
93
+ tagName: "div",
94
+ properties: { className: ["df-diagram-placeholder"] },
95
+ children: [{ type: "text", value: labels.loadingPreview }]
96
+ };
97
+ parent.children[index] = {
98
+ type: "element",
99
+ tagName: "figure",
100
+ properties: {
101
+ className: ["df-diagram-window"],
102
+ dataDfPluginDiagram: options.kind,
103
+ dataDfSource: fence.source,
104
+ ...options.moduleUrl ? { dataDfModuleUrl: options.moduleUrl } : {}
105
+ },
106
+ children: [
107
+ {
108
+ type: "element",
109
+ tagName: "figcaption",
110
+ properties: { className: ["df-diagram-toolbar"] },
111
+ children: [
112
+ {
113
+ type: "element",
114
+ tagName: "span",
115
+ properties: { className: ["df-diagram-title"] },
116
+ children: [{ type: "text", value: options.filename(fence.language) }]
117
+ },
118
+ {
119
+ type: "element",
120
+ tagName: "span",
121
+ properties: { className: ["df-diagram-actions"] },
122
+ children: [
123
+ action("copy", labels.copy),
124
+ action("source", labels.source, { dataDfDiagramPreviewLabel: labels.preview }),
125
+ action("expand", labels.expand, { dataDfDiagramCloseLabel: labels.close })
126
+ ]
127
+ }
128
+ ]
129
+ },
130
+ {
131
+ type: "element",
132
+ tagName: "div",
133
+ properties: { className: ["df-diagram-stage"] },
134
+ children: [
135
+ {
136
+ type: "element",
137
+ tagName: "div",
138
+ properties: { className: ["df-diagram-preview"] },
139
+ children: [preview]
140
+ },
141
+ {
142
+ type: "element",
143
+ tagName: "pre",
144
+ properties: { className: ["df-diagram-source"], hidden: true },
145
+ children: [
146
+ {
147
+ type: "element",
148
+ tagName: "code",
149
+ properties: {},
150
+ children: [{ type: "text", value: fence.source }]
151
+ }
152
+ ]
153
+ },
154
+ ...hasVisualPreview ? [
155
+ {
156
+ type: "element",
157
+ tagName: "div",
158
+ properties: {
159
+ className: ["df-diagram-zoom-controls"],
160
+ role: "group",
161
+ ariaLabel: labels.controls
162
+ },
163
+ children: [
164
+ zoomControl("zoom-out", labels.zoomOut),
165
+ zoomControl("zoom-reset", labels.zoomReset),
166
+ zoomControl("zoom-in", labels.zoomIn)
167
+ ]
168
+ }
169
+ ] : []
170
+ ]
171
+ }
172
+ ]
173
+ };
174
+ });
175
+ };
176
+ }
177
+
178
+ // src/shared/markdownSource.ts
179
+ function stripColumns(value, columns) {
180
+ let index = 0;
181
+ let consumed = 0;
182
+ while (index < value.length && consumed < columns) {
183
+ if (value[index] === " ") consumed += 1;
184
+ else if (value[index] === " ") consumed += 4 - consumed % 4;
185
+ else break;
186
+ index += 1;
187
+ }
188
+ return consumed >= columns ? value.slice(index) : void 0;
189
+ }
190
+ function containerContent(line, state) {
191
+ let value = line;
192
+ while (true) {
193
+ const quote = value.match(/^ {0,3}>[ \t]?/);
194
+ if (!quote) break;
195
+ value = value.slice(quote[0].length);
196
+ }
197
+ const continued = state.listIndent > 0 ? stripColumns(value, state.listIndent) : void 0;
198
+ const content = continued ?? value;
199
+ const item = content.match(/^ {0,3}(?:[-+*]|\d{1,9}[.)])([ \t]+)(.*)$/);
200
+ if (item) {
201
+ const body = item[2] ?? "";
202
+ state.listIndent = (continued === void 0 ? 0 : state.listIndent) + content.length - body.length;
203
+ return body;
204
+ }
205
+ if (continued !== void 0 || value.trim() === "") return content;
206
+ state.listIndent = 0;
207
+ return value;
208
+ }
209
+ function openingFence(line) {
210
+ const match = line.match(/^ {0,3}(`{3,}|~{3,})(.*)$/);
211
+ if (!match?.[1]) return void 0;
212
+ const marker = match[1][0];
213
+ const info = match[2]?.trim() ?? "";
214
+ if (marker === "`" && info.includes("`")) return void 0;
215
+ return {
216
+ marker,
217
+ size: match[1].length,
218
+ language: info.split(/\s+/, 1)[0]?.toLowerCase() ?? ""
219
+ };
220
+ }
221
+ function closesFence(line, fence) {
222
+ const match = line.match(/^ {0,3}(`+|~+)[ \t]*$/);
223
+ return Boolean(match?.[1]?.[0] === fence.marker && match[1].length >= fence.size);
224
+ }
225
+ function hasMarkdownFenceLanguage(source, languages) {
226
+ let open;
227
+ const container = { listIndent: 0 };
228
+ for (const line of source.split(/\r?\n/)) {
229
+ const content = containerContent(line, container);
230
+ if (open) {
231
+ if (closesFence(content, open)) open = void 0;
232
+ continue;
233
+ }
234
+ const fence = openingFence(content);
235
+ if (!fence) continue;
236
+ if (languages.has(fence.language)) return true;
237
+ open = fence;
238
+ }
239
+ return false;
240
+ }
241
+
242
+ // src/plantuml/index.ts
243
+ var PLUGIN_VERSION = "3";
244
+ function encode6Bit(value) {
245
+ const normalized = value & 63;
246
+ if (normalized < 10) return String.fromCharCode(48 + normalized);
247
+ if (normalized < 36) return String.fromCharCode(65 + normalized - 10);
248
+ if (normalized < 62) return String.fromCharCode(97 + normalized - 36);
249
+ return normalized === 62 ? "-" : "_";
250
+ }
251
+ function encodePlantUml(source) {
252
+ const normalized = /@start\w+/i.test(source) ? source : `@startuml
253
+ ${source}
254
+ @enduml`;
255
+ const bytes = deflateSync(strToU8(normalized), { level: 9 });
256
+ let encoded = "";
257
+ for (let index = 0; index < bytes.length; index += 3) {
258
+ const first = bytes[index] ?? 0;
259
+ const second = bytes[index + 1] ?? 0;
260
+ const third = bytes[index + 2] ?? 0;
261
+ encoded += encode6Bit(first >> 2);
262
+ encoded += encode6Bit((first & 3) << 4 | second >> 4);
263
+ encoded += encode6Bit((second & 15) << 2 | third >> 6);
264
+ encoded += encode6Bit(third & 63);
265
+ }
266
+ return encoded;
267
+ }
268
+ function plantUml(options = {}) {
269
+ const server = options.server === false ? "" : options.server?.trim().replace(/\/+$/, "") ?? "";
270
+ return defineMarkdownPlugin({
271
+ name: "plantuml",
272
+ version: PLUGIN_VERSION,
273
+ cacheKey: { server },
274
+ browserCompiler: {
275
+ module: "@docfuse/plugins/plantuml",
276
+ exportName: "plantUml",
277
+ options: { server: server || false }
278
+ },
279
+ fenceLanguages: ["plantuml", "puml"],
280
+ appliesTo: ({ source }) => hasMarkdownFenceLanguage(source, /* @__PURE__ */ new Set(["plantuml", "puml"])),
281
+ assets: {
282
+ clients: [{ id: "plantuml", module: "@docfuse/plugins/client/plantuml" }],
283
+ styles: [{ id: "diagrams", module: "@docfuse/plugins/diagram.css" }]
284
+ },
285
+ rehypePlugins: [
286
+ diagramFence({
287
+ languages: /* @__PURE__ */ new Set(["plantuml", "puml"]),
288
+ kind: "plantuml",
289
+ filename: () => "diagram.puml",
290
+ ...server ? { imageUrl: (source) => `${server}/${encodePlantUml(source)}` } : {}
291
+ })
292
+ ]
293
+ });
294
+ }
295
+ export {
296
+ plantUml
297
+ };
@@ -0,0 +1,20 @@
1
+ import { MarkdownPlugin } from '@docfuse/markdown';
2
+
3
+ interface ReadingTimeOptions {
4
+ wordsPerMinute?: number;
5
+ cjkWordsPerMinute?: number;
6
+ includeCode?: boolean;
7
+ /** Fallback label template. English is built in. `{minutes}` is replaced with the calculated value. */
8
+ label?: string;
9
+ /** Locale-specific templates, merged over the built-in Chinese label. */
10
+ labels?: Readonly<Record<string, string>>;
11
+ }
12
+ interface ReadingTimeCounts {
13
+ latinWords: number;
14
+ cjkCharacters: number;
15
+ minutes: number;
16
+ }
17
+ declare function countReadingTime(text: string, wordsPerMinute: number, cjkWordsPerMinute: number): ReadingTimeCounts;
18
+ declare function readingTime(options?: ReadingTimeOptions): MarkdownPlugin;
19
+
20
+ export { type ReadingTimeCounts, type ReadingTimeOptions, countReadingTime, readingTime };
@@ -0,0 +1,90 @@
1
+ // src/reading-time/index.ts
2
+ import { defineMarkdownPlugin } from "@docfuse/markdown";
3
+ import { SKIP, visit } from "unist-util-visit";
4
+
5
+ // src/shared/hast.ts
6
+ function element(tagName, properties = {}, children = []) {
7
+ return { type: "element", tagName, properties, children };
8
+ }
9
+
10
+ // src/reading-time/index.ts
11
+ var PLUGIN_VERSION = "3";
12
+ var CJK = /[\u4e00-\u9fff\u3400-\u4dbf\u3040-\u30ff\uac00-\ud7af]/g;
13
+ var SKIP_TAGS = /* @__PURE__ */ new Set(["pre", "code", "script", "style", "svg"]);
14
+ var DEFAULT_LABEL = "{minutes} min read";
15
+ var DEFAULT_LABELS = { zh: "\u7EA6 {minutes} \u5206\u949F\u9605\u8BFB" };
16
+ function positiveRate(value, name) {
17
+ if (!Number.isFinite(value) || value <= 0) throw new TypeError(`${name} must be a positive finite number`);
18
+ return value;
19
+ }
20
+ function countReadingTime(text, wordsPerMinute, cjkWordsPerMinute) {
21
+ positiveRate(wordsPerMinute, "wordsPerMinute");
22
+ positiveRate(cjkWordsPerMinute, "cjkWordsPerMinute");
23
+ const cjkCharacters = text.match(CJK)?.length ?? 0;
24
+ const latinWords = text.replace(CJK, " ").match(/[A-Za-z0-9]+(?:['’][A-Za-z0-9]+)*/g)?.length ?? 0;
25
+ const minutes = Math.max(1, Math.round(latinWords / wordsPerMinute + cjkCharacters / cjkWordsPerMinute));
26
+ return { latinWords, cjkCharacters, minutes };
27
+ }
28
+ function collectText(tree, includeCode) {
29
+ const chunks = [];
30
+ visit(tree, (node) => {
31
+ if (!includeCode && node.type === "element" && SKIP_TAGS.has(node.tagName)) return SKIP;
32
+ if (node.type === "text") chunks.push(node.value);
33
+ });
34
+ return chunks.join(" ");
35
+ }
36
+ function localeFromFile(file) {
37
+ if (!file || typeof file !== "object" || !("data" in file)) return void 0;
38
+ const data = file.data;
39
+ if (!data || typeof data !== "object" || !("docfuseLocale" in data)) return void 0;
40
+ const locale = data.docfuseLocale;
41
+ return typeof locale === "string" ? locale.trim().toLowerCase() || void 0 : void 0;
42
+ }
43
+ function normalizeLabels(labels) {
44
+ return Object.fromEntries(
45
+ Object.entries(labels ?? {}).map(([locale, value]) => [locale.trim().toLowerCase(), value]).filter(([locale]) => locale.length > 0)
46
+ );
47
+ }
48
+ function labelForLocale(fallback, labels, locale) {
49
+ if (!locale) return fallback;
50
+ return labels[locale] ?? labels[locale.split("-")[0] ?? ""] ?? fallback;
51
+ }
52
+ function readingTime(options = {}) {
53
+ const wordsPerMinute = positiveRate(options.wordsPerMinute ?? 220, "wordsPerMinute");
54
+ const cjkWordsPerMinute = positiveRate(options.cjkWordsPerMinute ?? 300, "cjkWordsPerMinute");
55
+ const includeCode = options.includeCode === true;
56
+ const label = options.label ?? DEFAULT_LABEL;
57
+ const labels = { ...DEFAULT_LABELS, ...normalizeLabels(options.labels) };
58
+ return defineMarkdownPlugin({
59
+ name: "reading-time",
60
+ version: PLUGIN_VERSION,
61
+ cacheKey: { wordsPerMinute, cjkWordsPerMinute, includeCode, label, labels },
62
+ browserCompiler: {
63
+ module: "@docfuse/plugins/reading-time",
64
+ exportName: "readingTime",
65
+ options: { wordsPerMinute, cjkWordsPerMinute, includeCode, label, labels }
66
+ },
67
+ rehypePlugins: [
68
+ () => (tree, file) => {
69
+ const counts = countReadingTime(collectText(tree, includeCode), wordsPerMinute, cjkWordsPerMinute);
70
+ if (counts.latinWords + counts.cjkCharacters === 0) return;
71
+ const resolvedLabel = labelForLocale(label, labels, localeFromFile(file));
72
+ const readingTimeNode = element(
73
+ "p",
74
+ {
75
+ className: ["df-reading-time"],
76
+ dataDfReadingMinutes: String(counts.minutes),
77
+ dataDfWordCount: String(counts.latinWords + counts.cjkCharacters)
78
+ },
79
+ [{ type: "text", value: resolvedLabel.replaceAll("{minutes}", String(counts.minutes)) }]
80
+ );
81
+ const titleIndex = tree.children.findIndex((node) => node.type === "element" && node.tagName === "h1");
82
+ tree.children.splice(titleIndex >= 0 ? titleIndex + 1 : 0, 0, readingTimeNode);
83
+ }
84
+ ]
85
+ });
86
+ }
87
+ export {
88
+ countReadingTime,
89
+ readingTime
90
+ };
package/package.json ADDED
@@ -0,0 +1,122 @@
1
+ {
2
+ "name": "@docfuse/plugins",
3
+ "version": "0.1.0",
4
+ "description": "Official plugins and providers for Docfuse and @docfuse/markdown.",
5
+ "license": "MIT",
6
+ "author": "Docfuse Contributors",
7
+ "homepage": "https://docfuse.dev",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/jiangxinlei/docfuse.git",
11
+ "directory": "packages/plugins"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/jiangxinlei/docfuse/issues"
15
+ },
16
+ "keywords": [
17
+ "docfuse",
18
+ "markdown",
19
+ "plugin",
20
+ "remark",
21
+ "rehype",
22
+ "mermaid",
23
+ "plantuml"
24
+ ],
25
+ "type": "module",
26
+ "types": "./dist/index.d.ts",
27
+ "exports": {
28
+ ".": {
29
+ "types": "./dist/index.d.ts",
30
+ "import": "./dist/index.js"
31
+ },
32
+ "./external-links": {
33
+ "types": "./dist/external-links.d.ts",
34
+ "import": "./dist/external-links.js"
35
+ },
36
+ "./reading-time": {
37
+ "types": "./dist/reading-time.d.ts",
38
+ "import": "./dist/reading-time.js"
39
+ },
40
+ "./link-card": {
41
+ "types": "./dist/link-card.d.ts",
42
+ "import": "./dist/link-card.js"
43
+ },
44
+ "./kroki": {
45
+ "types": "./dist/kroki.d.ts",
46
+ "import": "./dist/kroki.js"
47
+ },
48
+ "./mermaid": {
49
+ "types": "./dist/mermaid.d.ts",
50
+ "import": "./dist/mermaid.js"
51
+ },
52
+ "./plantuml": {
53
+ "types": "./dist/plantuml.d.ts",
54
+ "import": "./dist/plantuml.js"
55
+ },
56
+ "./math": {
57
+ "types": "./dist/math.d.ts",
58
+ "import": "./dist/math.js"
59
+ },
60
+ "./pagefind": {
61
+ "types": "./dist/pagefind.d.ts",
62
+ "import": "./dist/pagefind.js"
63
+ },
64
+ "./client/kroki": "./dist/client/kroki.js",
65
+ "./client/mermaid": "./dist/client/mermaid.js",
66
+ "./client/plantuml": "./dist/client/plantuml.js",
67
+ "./diagram.css": "./dist/diagram.css",
68
+ "./math.css": "./dist/math.css"
69
+ },
70
+ "files": [
71
+ "dist",
72
+ "README.zh-CN.md"
73
+ ],
74
+ "engines": {
75
+ "node": ">=22"
76
+ },
77
+ "publishConfig": {
78
+ "access": "public",
79
+ "provenance": true
80
+ },
81
+ "dependencies": {
82
+ "fflate": "^0.8.3",
83
+ "katex": "^0.17.0",
84
+ "rehype-katex": "^7.0.1",
85
+ "remark-math": "^6.0.0",
86
+ "unist-util-visit": "^5.1.0",
87
+ "@docfuse/markdown": "0.1.0"
88
+ },
89
+ "peerDependencies": {
90
+ "mermaid": "^11.12.0",
91
+ "pagefind": "^1.5.2"
92
+ },
93
+ "peerDependenciesMeta": {
94
+ "mermaid": {
95
+ "optional": true
96
+ },
97
+ "pagefind": {
98
+ "optional": true
99
+ }
100
+ },
101
+ "devDependencies": {
102
+ "@types/hast": "^3.0.4",
103
+ "@types/mdast": "^4.0.4",
104
+ "@types/node": "^22.10.0",
105
+ "@types/react": "^19.0.0",
106
+ "@types/react-dom": "^19.0.0",
107
+ "esbuild": "^0.28.1",
108
+ "mermaid": "^11.12.0",
109
+ "pagefind": "^1.5.2",
110
+ "react": "^19.0.0",
111
+ "react-dom": "^19.0.0",
112
+ "tsup": "^8.3.5",
113
+ "typescript": "^6.0.3",
114
+ "docfuse": "0.1.0"
115
+ },
116
+ "scripts": {
117
+ "build": "tsup && node scripts/verify-client-assets.mjs && node scripts/build-math-css.mjs",
118
+ "test:browser": "node scripts/test-browser-build.mjs",
119
+ "typecheck": "tsc --noEmit",
120
+ "test": "vitest run --root ../.. packages/plugins"
121
+ }
122
+ }