@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 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, Dumi docs, and comprehensive testing
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 will start the Dumi documentation server with live playground at `http://localhost:8000`.
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 Dumi development server with playground |
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
- โ”œโ”€โ”€ vitest.config.ts # Test configuration
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,
@@ -184,6 +184,12 @@ const LANGUAGES = [
184
184
  syntax: "markdown",
185
185
  value: "markdown"
186
186
  },
187
+ {
188
+ ext: ["mermaid", "mmd"],
189
+ name: "Mermaid",
190
+ syntax: "simplemode",
191
+ value: "mermaid"
192
+ },
187
193
  {
188
194
  name: "MATLAB",
189
195
  syntax: "octave",
@@ -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
@@ -62,6 +62,7 @@ declare const _default: {
62
62
  cancel: string;
63
63
  codemirror: {
64
64
  copySuccess: string;
65
+ preview: string;
65
66
  selectLanguage: string;
66
67
  selectTheme: string;
67
68
  showLineNumbers: string;
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",
@@ -8,6 +8,7 @@ declare const _default: {
8
8
  cancel: string;
9
9
  codemirror: {
10
10
  copySuccess: string;
11
+ preview: string;
11
12
  selectLanguage: string;
12
13
  selectTheme: string;
13
14
  showLineNumbers: string;
@@ -11,6 +11,7 @@ var init_locale = __esmMin((() => {
11
11
  cancel: "Cancel",
12
12
  codemirror: {
13
13
  copySuccess: "Code copied to clipboard",
14
+ preview: "Preview",
14
15
  selectLanguage: "Select language",
15
16
  selectTheme: "Select theme",
16
17
  showLineNumbers: "Show Line Numbers",
@@ -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 { Block } from "@lobehub/ui";
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
- isEmptyRef.current = !instance.getValue().trim();
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: [/* @__PURE__ */ jsx(Toolbar, {
195
- expand,
196
- labels,
197
- onClick: () => setExpand(!expand),
198
- onCopy: handleCopy,
199
- onLanguageChange: handleLanguageChange,
200
- onShowLineNumbersChange: handleShowLineNumbersChange,
201
- onTabSizeChange: handleTabSizeChange,
202
- onUseTabsChange: handleUseTabsChange,
203
- selectedLang,
204
- showLineNumbers,
205
- tabSize,
206
- toggleExpand: () => setExpand(!expand),
207
- useTabs
208
- }), /* @__PURE__ */ jsx("div", {
209
- className: cx("cm-container", !expand && "cm-container-collapsed"),
210
- children: /* @__PURE__ */ jsx("textarea", {
211
- className: "cm-textarea",
212
- ref
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.20.3",
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 .dumi/tmp .eslintcache node_modules/.cache",
65
- "dev": "dumi dev",
66
- "docs:build": "dumi build",
67
- "docs:build-analyze": "ANALYZE=1 dumi build",
68
- "docs:dev": "dumi 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": "dumi setup",
79
+ "setup": "lobedocs typegen",
80
80
  "start": "npm run docs:dev",
81
81
  "test": "vitest",
82
82
  "test:coverage": "vitest run --coverage --passWithNoTests",