@lobehub/editor 4.20.3 โ 4.22.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 +16 -15
- package/es/codemirror/components/Toolbar.js +2 -1
- package/es/codemirror/constants.js +6 -0
- package/es/codemirror/types.d.ts +4 -0
- package/es/headless.d.ts +1 -0
- package/es/headless.js +7 -0
- package/es/locale/index.d.ts +1 -0
- package/es/locale/index.js +1 -0
- package/es/plugins/codemirror-block/react/CodemirrorNode.js +45 -22
- package/es/plugins/codemirror-block/react/MermaidPreview.js +47 -0
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -70,7 +70,7 @@ A modern, extensible rich text editor built on Meta's Lexical framework with dua
|
|
|
70
70
|
- ๐จ **Customizable UI** - Antd-styled components with flexible theming
|
|
71
71
|
- ๐ **File & Media Support** - Native support for images, files, tables, and more
|
|
72
72
|
- ๐ฏ **TypeScript Native** - Built with TypeScript for excellent developer experience
|
|
73
|
-
- ๐ฑ **Modern Build System** - Optimized with Vite,
|
|
73
|
+
- ๐ฑ **Modern Build System** - Optimized with Vite, lobedocs, and comprehensive testing
|
|
74
74
|
|
|
75
75
|
## ๐ฆ Installation
|
|
76
76
|
|
|
@@ -419,21 +419,22 @@ $ pnpm install
|
|
|
419
419
|
$ pnpm run dev
|
|
420
420
|
```
|
|
421
421
|
|
|
422
|
-
This
|
|
422
|
+
This starts the lobedocs development server with the live component playground. The local URL is
|
|
423
|
+
printed in the terminal.
|
|
423
424
|
|
|
424
425
|
### Available Scripts
|
|
425
426
|
|
|
426
|
-
| Script | Description
|
|
427
|
-
| -------------------- |
|
|
428
|
-
| `pnpm dev` | Start
|
|
429
|
-
| `pnpm build` | Build library and generate type definitions
|
|
430
|
-
| `pnpm test` | Run tests with Vitest
|
|
431
|
-
| `pnpm test:coverage` | Run tests with coverage report
|
|
432
|
-
| `pnpm lint` | Lint and fix code with ESLint
|
|
433
|
-
| `pnpm type-check` | Type check with TypeScript
|
|
434
|
-
| `pnpm ci` | Run all CI checks (lint, type-check, test)
|
|
435
|
-
| `pnpm docs:build` | Build documentation for production
|
|
436
|
-
| `pnpm release` | Publish new version with semantic-release
|
|
427
|
+
| Script | Description |
|
|
428
|
+
| -------------------- | -------------------------------------------- |
|
|
429
|
+
| `pnpm dev` | Start lobedocs with the component playground |
|
|
430
|
+
| `pnpm build` | Build library and generate type definitions |
|
|
431
|
+
| `pnpm test` | Run tests with Vitest |
|
|
432
|
+
| `pnpm test:coverage` | Run tests with coverage report |
|
|
433
|
+
| `pnpm lint` | Lint and fix code with ESLint |
|
|
434
|
+
| `pnpm type-check` | Type check with TypeScript |
|
|
435
|
+
| `pnpm ci` | Run all CI checks (lint, type-check, test) |
|
|
436
|
+
| `pnpm docs:build` | Build documentation for production |
|
|
437
|
+
| `pnpm release` | Publish new version with semantic-release |
|
|
437
438
|
|
|
438
439
|
### Debug Environment Variables
|
|
439
440
|
|
|
@@ -587,9 +588,9 @@ lobe-editor/
|
|
|
587
588
|
โ โโโ index.ts # Public API exports
|
|
588
589
|
โ
|
|
589
590
|
โโโ docs/ # ๐ Documentation source
|
|
591
|
+
โโโ docs.config.ts # lobedocs site configuration
|
|
590
592
|
โโโ tests/ # ๐งช Test files
|
|
591
|
-
|
|
592
|
-
โโโ .dumi/ # Dumi doc build cache
|
|
593
|
+
โโโ vitest.config.ts # Test configuration
|
|
593
594
|
```
|
|
594
595
|
|
|
595
596
|
The architecture follows a **dual-layer design**:
|
|
@@ -6,7 +6,7 @@ import { ActionIcon, Flexbox } from "@lobehub/ui";
|
|
|
6
6
|
import { ChevronDown, ChevronRight } from "lucide-react";
|
|
7
7
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
8
|
//#region src/codemirror/components/Toolbar.tsx
|
|
9
|
-
const Toolbar = ({ selectedLang, onLanguageChange, onCopy, tabSize, onTabSizeChange, useTabs, onUseTabsChange, showLineNumbers, onShowLineNumbersChange, onClick, expand, toggleExpand, labels, languageOptions }) => {
|
|
9
|
+
const Toolbar = ({ selectedLang, onLanguageChange, onCopy, tabSize, onTabSizeChange, useTabs, onUseTabsChange, showLineNumbers, onShowLineNumbersChange, onClick, expand, toggleExpand, labels, languageOptions, extra }) => {
|
|
10
10
|
return /* @__PURE__ */ jsxs(Flexbox, {
|
|
11
11
|
align: "center",
|
|
12
12
|
className: "cm-header-toolbar",
|
|
@@ -24,6 +24,7 @@ const Toolbar = ({ selectedLang, onLanguageChange, onCopy, tabSize, onTabSizeCha
|
|
|
24
24
|
horizontal: true,
|
|
25
25
|
onClick: (e) => e.stopPropagation(),
|
|
26
26
|
children: [
|
|
27
|
+
extra,
|
|
27
28
|
/* @__PURE__ */ jsx(MoreOptions, {
|
|
28
29
|
labels,
|
|
29
30
|
onShowLineNumbersChange,
|
package/es/codemirror/types.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
|
|
1
3
|
//#region src/codemirror/types.d.ts
|
|
2
4
|
interface CodeMirrorMode {
|
|
3
5
|
ext?: string[];
|
|
@@ -7,6 +9,7 @@ interface CodeMirrorMode {
|
|
|
7
9
|
}
|
|
8
10
|
interface CodeMirrorLabels {
|
|
9
11
|
copy?: string;
|
|
12
|
+
preview?: string;
|
|
10
13
|
selectLanguage?: string;
|
|
11
14
|
showLineNumbers?: string;
|
|
12
15
|
tabSize?: string;
|
|
@@ -35,6 +38,7 @@ interface MoreOptionsProps {
|
|
|
35
38
|
}
|
|
36
39
|
interface ToolbarProps {
|
|
37
40
|
expand?: boolean;
|
|
41
|
+
extra?: ReactNode;
|
|
38
42
|
labels?: CodeMirrorLabels;
|
|
39
43
|
languageOptions?: CodeMirrorMode[];
|
|
40
44
|
onClick?: () => void;
|
package/es/headless.d.ts
CHANGED
package/es/headless.js
CHANGED
|
@@ -28,6 +28,7 @@ var locale_default = {
|
|
|
28
28
|
cancel: "Cancel",
|
|
29
29
|
codemirror: {
|
|
30
30
|
copySuccess: "Code copied to clipboard",
|
|
31
|
+
preview: "Preview",
|
|
31
32
|
selectLanguage: "Select language",
|
|
32
33
|
selectTheme: "Select theme",
|
|
33
34
|
showLineNumbers: "Show Line Numbers",
|
|
@@ -5449,6 +5450,12 @@ const LANGUAGES = [
|
|
|
5449
5450
|
syntax: "markdown",
|
|
5450
5451
|
value: "markdown"
|
|
5451
5452
|
},
|
|
5453
|
+
{
|
|
5454
|
+
ext: ["mermaid", "mmd"],
|
|
5455
|
+
name: "Mermaid",
|
|
5456
|
+
syntax: "simplemode",
|
|
5457
|
+
value: "mermaid"
|
|
5458
|
+
},
|
|
5452
5459
|
{
|
|
5453
5460
|
name: "MATLAB",
|
|
5454
5461
|
syntax: "octave",
|
package/es/locale/index.d.ts
CHANGED
package/es/locale/index.js
CHANGED
|
@@ -6,7 +6,9 @@ import { lobeTheme } from "../../../codemirror/theme.js";
|
|
|
6
6
|
import { useTranslation } from "../../../editor-kernel/react/useTranslation.js";
|
|
7
7
|
import { SELECT_AFTER_CODEMIRROR_COMMAND, SELECT_BEFORE_CODEMIRROR_COMMAND } from "../command/index.js";
|
|
8
8
|
import { useLexicalNodeSelection } from "../../../editor-kernel/react/useLexicalNodeSelection.js";
|
|
9
|
-
import
|
|
9
|
+
import MermaidPreview from "./MermaidPreview.js";
|
|
10
|
+
import { ActionIcon, Block } from "@lobehub/ui";
|
|
11
|
+
import { CodeXml, Eye } from "lucide-react";
|
|
10
12
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
11
13
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
12
14
|
import { cx } from "antd-style";
|
|
@@ -26,6 +28,8 @@ const ReactCodemirrorNode = ({ node, className, editor }) => {
|
|
|
26
28
|
const [useTabs, setUseTabs] = useState(node.options.indentWithTabs ?? false);
|
|
27
29
|
const [showLineNumbers, setShowLineNumbers] = useState(node.options.lineNumbers ?? false);
|
|
28
30
|
const [expand, setExpand] = useState(true);
|
|
31
|
+
const [preview, setPreview] = useState(false);
|
|
32
|
+
const [code, setCode] = useState(node.code);
|
|
29
33
|
const handleCopy = useCallback(async () => {
|
|
30
34
|
if (instanceRef.current) {
|
|
31
35
|
const code = instanceRef.current.getValue();
|
|
@@ -35,6 +39,7 @@ const ReactCodemirrorNode = ({ node, className, editor }) => {
|
|
|
35
39
|
}
|
|
36
40
|
}, []);
|
|
37
41
|
const labels = useMemo(() => ({
|
|
42
|
+
preview: t("codemirror.preview"),
|
|
38
43
|
selectLanguage: t("codemirror.selectLanguage"),
|
|
39
44
|
showLineNumbers: t("codemirror.showLineNumbers"),
|
|
40
45
|
tabSize: t("codemirror.tabSize"),
|
|
@@ -42,6 +47,7 @@ const ReactCodemirrorNode = ({ node, className, editor }) => {
|
|
|
42
47
|
}), [t]);
|
|
43
48
|
const handleLanguageChange = useCallback((value) => {
|
|
44
49
|
setSelectedLang(value);
|
|
50
|
+
if (value !== "mermaid") setPreview(false);
|
|
45
51
|
if (instanceRef.current) instanceRef.current.setOption("mode", value);
|
|
46
52
|
editor.update(() => {
|
|
47
53
|
node.setLang(value);
|
|
@@ -145,7 +151,9 @@ const ReactCodemirrorNode = ({ node, className, editor }) => {
|
|
|
145
151
|
});
|
|
146
152
|
});
|
|
147
153
|
instance.on("change", () => {
|
|
148
|
-
|
|
154
|
+
const currentValue = instance.getValue();
|
|
155
|
+
isEmptyRef.current = !currentValue.trim();
|
|
156
|
+
setCode(currentValue);
|
|
149
157
|
});
|
|
150
158
|
instance.on("change", debounce(() => {
|
|
151
159
|
const currentValue = instance.getValue();
|
|
@@ -191,27 +199,42 @@ const ReactCodemirrorNode = ({ node, className, editor }) => {
|
|
|
191
199
|
onMouseUp: (e) => e.stopPropagation(),
|
|
192
200
|
onSelect: (e) => e.stopPropagation(),
|
|
193
201
|
variant: "filled",
|
|
194
|
-
children: [
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
202
|
+
children: [
|
|
203
|
+
/* @__PURE__ */ jsx(Toolbar, {
|
|
204
|
+
expand,
|
|
205
|
+
extra: selectedLang === "mermaid" ? /* @__PURE__ */ jsx(ActionIcon, {
|
|
206
|
+
className: "cm-hidden-actions",
|
|
207
|
+
icon: preview ? CodeXml : Eye,
|
|
208
|
+
onClick: () => setPreview(!preview),
|
|
209
|
+
size: "small",
|
|
210
|
+
title: labels.preview
|
|
211
|
+
}) : void 0,
|
|
212
|
+
labels,
|
|
213
|
+
onClick: () => setExpand(!expand),
|
|
214
|
+
onCopy: handleCopy,
|
|
215
|
+
onLanguageChange: handleLanguageChange,
|
|
216
|
+
onShowLineNumbersChange: handleShowLineNumbersChange,
|
|
217
|
+
onTabSizeChange: handleTabSizeChange,
|
|
218
|
+
onUseTabsChange: handleUseTabsChange,
|
|
219
|
+
selectedLang,
|
|
220
|
+
showLineNumbers,
|
|
221
|
+
tabSize,
|
|
222
|
+
toggleExpand: () => setExpand(!expand),
|
|
223
|
+
useTabs
|
|
224
|
+
}),
|
|
225
|
+
/* @__PURE__ */ jsx("div", {
|
|
226
|
+
className: cx("cm-container", (!expand || preview && selectedLang === "mermaid") && "cm-container-collapsed"),
|
|
227
|
+
children: /* @__PURE__ */ jsx("textarea", {
|
|
228
|
+
className: "cm-textarea",
|
|
229
|
+
ref
|
|
230
|
+
})
|
|
231
|
+
}),
|
|
232
|
+
expand && preview && selectedLang === "mermaid" && /* @__PURE__ */ jsx("div", {
|
|
233
|
+
className: "cm-container",
|
|
234
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
235
|
+
children: /* @__PURE__ */ jsx(MermaidPreview, { code })
|
|
213
236
|
})
|
|
214
|
-
|
|
237
|
+
]
|
|
215
238
|
});
|
|
216
239
|
};
|
|
217
240
|
ReactCodemirrorNode.displayName = "ReactCodemirrorNode";
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Mermaid } from "@lobehub/ui";
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
|
+
import { jsx } from "react/jsx-runtime";
|
|
5
|
+
import { cssVar } from "antd-style";
|
|
6
|
+
//#region src/plugins/codemirror-block/react/MermaidPreview.tsx
|
|
7
|
+
const MermaidPreview = ({ code }) => {
|
|
8
|
+
const [error, setError] = useState(null);
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
if (!code.trim()) {
|
|
11
|
+
setError(null);
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
let cancelled = false;
|
|
15
|
+
import("mermaid").then(async (mod) => {
|
|
16
|
+
try {
|
|
17
|
+
await mod.default.parse(code);
|
|
18
|
+
if (!cancelled) setError(null);
|
|
19
|
+
} catch (error_) {
|
|
20
|
+
if (!cancelled) setError(error_ instanceof Error ? error_.message : String(error_));
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
return () => {
|
|
24
|
+
cancelled = true;
|
|
25
|
+
};
|
|
26
|
+
}, [code]);
|
|
27
|
+
if (!code.trim()) return null;
|
|
28
|
+
if (error) return /* @__PURE__ */ jsx("div", {
|
|
29
|
+
style: {
|
|
30
|
+
color: cssVar.colorTextDescription,
|
|
31
|
+
fontFamily: cssVar.fontFamilyCode,
|
|
32
|
+
fontSize: 12,
|
|
33
|
+
padding: 16,
|
|
34
|
+
whiteSpace: "pre-wrap"
|
|
35
|
+
},
|
|
36
|
+
children: error
|
|
37
|
+
});
|
|
38
|
+
return /* @__PURE__ */ jsx(Mermaid, {
|
|
39
|
+
animated: false,
|
|
40
|
+
theme: "lobe-theme",
|
|
41
|
+
variant: "borderless",
|
|
42
|
+
children: code
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
MermaidPreview.displayName = "MermaidPreview";
|
|
46
|
+
//#endregion
|
|
47
|
+
export { MermaidPreview as default };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/editor",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.22.0",
|
|
4
4
|
"description": "A powerful and extensible rich text editor built on Meta's Lexical framework, providing a modern editing experience with React integration.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"lobehub",
|
|
@@ -61,11 +61,11 @@
|
|
|
61
61
|
"build:clean": "tsx ./scripts/clean.ts",
|
|
62
62
|
"build:watch": "tsdown --watch",
|
|
63
63
|
"ci": "npm run lint:circular && npm run lint && npm run type-check",
|
|
64
|
-
"clean": "rm -r es lib dist coverage .
|
|
65
|
-
"dev": "
|
|
66
|
-
"docs:build": "
|
|
67
|
-
"docs:build-analyze": "ANALYZE=1
|
|
68
|
-
"docs:dev": "
|
|
64
|
+
"clean": "rm -r es lib dist coverage .react-router .eslintcache node_modules/.cache",
|
|
65
|
+
"dev": "lobedocs dev",
|
|
66
|
+
"docs:build": "lobedocs build",
|
|
67
|
+
"docs:build-analyze": "ANALYZE=1 lobedocs build",
|
|
68
|
+
"docs:dev": "lobedocs dev",
|
|
69
69
|
"postinstall": "node ./scripts/postinstall-lexical-patch.cjs",
|
|
70
70
|
"lint": "eslint \"{src,tests}/**/*.{js,jsx,ts,tsx}\" --fix",
|
|
71
71
|
"lint:circular": "dpdm src/**/*.{ts,tsx} --warning false --tree false --exit-code circular:1 -T true",
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
"prepare": "husky && npm run setup",
|
|
77
77
|
"prettier": "prettier -c --write --no-error-on-unmatched-pattern \"**/**\"",
|
|
78
78
|
"release": "semantic-release",
|
|
79
|
-
"setup": "
|
|
79
|
+
"setup": "lobedocs typegen",
|
|
80
80
|
"start": "npm run docs:dev",
|
|
81
81
|
"test": "vitest",
|
|
82
82
|
"test:coverage": "vitest run --coverage --passWithNoTests",
|