@lobehub/editor 4.24.0 → 4.25.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 (144) hide show
  1. package/es/codemirror/style.js +42 -0
  2. package/es/editor-kernel/kernel.js +3 -0
  3. package/es/editor-kernel/react/useDecorators.js +2 -4
  4. package/es/headless/collapsible-plugin.js +74 -0
  5. package/es/headless/index.js +2 -0
  6. package/es/headless.d.ts +18 -0
  7. package/es/headless.js +2479 -71
  8. package/es/index.d.ts +26 -3
  9. package/es/index.js +17 -2
  10. package/es/locale/index.d.ts +14 -0
  11. package/es/locale/index.js +14 -0
  12. package/es/plugins/block/command/index.js +24 -0
  13. package/es/plugins/block/plugin/index.js +7 -5
  14. package/es/plugins/block/react/ReactBlockPlugin.js +43 -1
  15. package/es/plugins/block/react/core/menu-position.js +20 -0
  16. package/es/plugins/block/react/drag/drag-session.js +3 -2
  17. package/es/plugins/block/react/drag/drag-utils.js +13 -1
  18. package/es/plugins/codemirror-block/index.d.ts +2 -1
  19. package/es/plugins/codemirror-block/plugin/index.js +2 -0
  20. package/es/plugins/codemirror-block/react/CodemirrorNode.js +95 -5
  21. package/es/plugins/codemirror-block/react/useCodemirrorEditLock.js +33 -0
  22. package/es/plugins/codemirror-block/service/index.d.ts +40 -0
  23. package/es/plugins/codemirror-block/service/index.js +59 -0
  24. package/es/plugins/collapsible/command/index.d.ts +17 -0
  25. package/es/plugins/collapsible/command/index.js +72 -0
  26. package/es/plugins/collapsible/index.d.ts +7 -0
  27. package/es/plugins/collapsible/node/CollapsibleNode.d.ts +30 -0
  28. package/es/plugins/collapsible/node/CollapsibleNode.js +138 -0
  29. package/es/plugins/collapsible/plugin/index.d.ts +11 -0
  30. package/es/plugins/collapsible/plugin/index.js +535 -0
  31. package/es/plugins/collapsible/react/ReactCollapsiblePlugin.d.ts +6 -0
  32. package/es/plugins/collapsible/react/ReactCollapsiblePlugin.js +19 -0
  33. package/es/plugins/collapsible/react/index.d.ts +3 -0
  34. package/es/plugins/collapsible/react/style.js +67 -0
  35. package/es/plugins/collapsible/react/type.d.ts +6 -0
  36. package/es/plugins/common/plugin/index.js +3 -2
  37. package/es/plugins/common/plugin/progressive-select-all.js +81 -0
  38. package/es/plugins/common/plugin/register.js +73 -4
  39. package/es/plugins/link/command/index.js +26 -3
  40. package/es/plugins/link/conversion/index.js +237 -0
  41. package/es/plugins/link/index.d.ts +5 -3
  42. package/es/plugins/link/node/LinkBlockCardNode.d.ts +16 -0
  43. package/es/plugins/link/node/LinkBlockCardNode.js +66 -0
  44. package/es/plugins/link/node/LinkCardNode.d.ts +42 -0
  45. package/es/plugins/link/node/LinkCardNode.js +161 -0
  46. package/es/plugins/link/node/LinkIframeNode.d.ts +33 -0
  47. package/es/plugins/link/node/LinkIframeNode.js +131 -0
  48. package/es/plugins/link/node/LinkNode.d.ts +48 -2
  49. package/es/plugins/link/node/LinkNode.js +14 -6
  50. package/es/plugins/link/node/SchemaNode.d.ts +36 -0
  51. package/es/plugins/link/node/SchemaNode.js +149 -0
  52. package/es/plugins/link/normalization/index.d.ts +7 -0
  53. package/es/plugins/link/normalization/index.js +38 -0
  54. package/es/plugins/link/plugin/index.d.ts +26 -1
  55. package/es/plugins/link/plugin/index.js +124 -4
  56. package/es/plugins/link/react/ReactLinkPlugin.js +165 -7
  57. package/es/plugins/link/react/components/LinkCard.js +235 -0
  58. package/es/plugins/link/react/components/LinkEdit.js +93 -14
  59. package/es/plugins/link/react/components/LinkIframe.js +128 -0
  60. package/es/plugins/link/react/components/LinkToolbar.js +440 -111
  61. package/es/plugins/link/react/components/SchemaLink.js +49 -0
  62. package/es/plugins/link/react/index.d.ts +2 -2
  63. package/es/plugins/link/react/renderer-registry.d.ts +81 -0
  64. package/es/plugins/link/react/renderer-registry.js +80 -0
  65. package/es/plugins/link/react/style.js +63 -1
  66. package/es/plugins/link/react/type.d.ts +24 -1
  67. package/es/plugins/link/service/i-link-service.d.ts +156 -1
  68. package/es/plugins/link/service/i-link-service.js +258 -5
  69. package/es/plugins/list/plugin/index.js +9 -2
  70. package/es/plugins/litexml/command/diffCommand.js +94 -14
  71. package/es/plugins/litexml/command/index.js +187 -6
  72. package/es/plugins/litexml/diff-validation.js +237 -0
  73. package/es/plugins/litexml/node/DiffContentNode.js +67 -0
  74. package/es/plugins/litexml/node/DiffNode.d.ts +1 -1
  75. package/es/plugins/litexml/node/DiffNode.js +5 -2
  76. package/es/plugins/litexml/node/TableCellDiffNode.js +77 -0
  77. package/es/plugins/litexml/node/TableRowDiffNode.js +73 -0
  78. package/es/plugins/litexml/plugin/index.d.ts +2 -0
  79. package/es/plugins/litexml/plugin/index.js +62 -9
  80. package/es/plugins/litexml/react/DiffNodeToolbar/index.js +10 -12
  81. package/es/plugins/litexml/react/TableRowDiffToolbar.js +123 -0
  82. package/es/plugins/litexml/react/hooks/useHasDiffNode.js +5 -2
  83. package/es/plugins/litexml/react/index.js +6 -2
  84. package/es/plugins/litexml/react/style.js +18 -5
  85. package/es/plugins/litexml/table-cell-diff.js +153 -0
  86. package/es/plugins/litexml/table-row-diff.js +82 -0
  87. package/es/plugins/markdown/data-source/markdown/parse.js +25 -1
  88. package/es/plugins/markdown/plugin/index.js +5 -3
  89. package/es/plugins/slash/react/ReactSlashPlugin.js +22 -43
  90. package/es/plugins/slash/react/components/DefaultSlashMenu.js +76 -16
  91. package/es/plugins/slash/react/menu-utils.js +84 -0
  92. package/es/plugins/slash/service/i-slash-service.d.ts +3 -0
  93. package/es/plugins/table/plugin/index.js +4 -1
  94. package/es/plugins/table/plugin/single-cell-paste.js +79 -0
  95. package/es/plugins/table/react/TableController/hooks.js +7 -4
  96. package/es/plugins/table/react/hooks.js +2 -2
  97. package/es/plugins/table/react/index.js +2 -0
  98. package/es/plugins/table/react/style.js +15 -5
  99. package/es/plugins/table/react/useScrollFocusedTableCellIntoView.js +46 -0
  100. package/es/plugins/toc/index.d.ts +10 -0
  101. package/es/plugins/toc/plugin/index.d.ts +8 -0
  102. package/es/plugins/toc/plugin/index.js +56 -0
  103. package/es/plugins/toc/react/ReactTocPlugin.d.ts +6 -0
  104. package/es/plugins/toc/react/ReactTocPlugin.js +36 -0
  105. package/es/plugins/toc/react/getNearestScrollContainer.d.ts +6 -0
  106. package/es/plugins/toc/react/getNearestScrollContainer.js +18 -0
  107. package/es/plugins/toc/react/hooks/useActiveHeading.js +59 -0
  108. package/es/plugins/toc/react/hooks/useTocItems.js +50 -0
  109. package/es/plugins/toc/react/index.d.ts +5 -0
  110. package/es/plugins/toc/react/type.d.ts +30 -0
  111. package/es/plugins/toc/react/useToc.d.ts +5 -0
  112. package/es/plugins/toc/react/useToc.js +41 -0
  113. package/es/plugins/toc/service/i-toc-service.d.ts +39 -0
  114. package/es/plugins/toc/service/i-toc-service.js +125 -0
  115. package/es/plugins/toc/service/index.d.ts +3 -0
  116. package/es/plugins/toc/types.d.ts +23 -0
  117. package/es/plugins/toc/utils/index.d.ts +6 -0
  118. package/es/plugins/toc/utils/index.js +30 -0
  119. package/es/plugins/toolbar/react/index.js +26 -12
  120. package/es/plugins/toolbar/react/style.js +3 -2
  121. package/es/plugins/virtual-block/plugin/register.js +34 -12
  122. package/es/plugins/yjs/index.d.ts +6 -0
  123. package/es/plugins/yjs/plugin/index.d.ts +7 -0
  124. package/es/plugins/yjs/plugin/index.js +191 -0
  125. package/es/plugins/yjs/plugin/types.d.ts +16 -0
  126. package/es/plugins/yjs/plugin/utils/awareness.js +9 -0
  127. package/es/plugins/yjs/plugin/utils/editor-state.js +47 -0
  128. package/es/plugins/yjs/plugin/utils/history.js +31 -0
  129. package/es/plugins/yjs/plugin/utils/node-properties.js +39 -0
  130. package/es/plugins/yjs/plugin/utils/sync.js +24 -0
  131. package/es/plugins/yjs/react/codemirrorEditLockProvider.js +59 -0
  132. package/es/plugins/yjs/react/index.d.ts +23 -0
  133. package/es/plugins/yjs/react/index.js +200 -0
  134. package/es/plugins/yjs/service/index.d.ts +47 -0
  135. package/es/plugins/yjs/service/index.js +84 -0
  136. package/es/plugins/yjs/websocket-provider.d.ts +78 -0
  137. package/es/plugins/yjs/websocket-provider.js +263 -0
  138. package/es/renderer/LexicalRenderer.js +9 -2
  139. package/es/renderer/engine/render-builtin-node.js +17 -8
  140. package/es/types/kernel.d.ts +4 -0
  141. package/es/utils/updatePosition.js +5 -2
  142. package/package.json +3 -1
  143. package/patches/@lexical__yjs@0.42.0.patch +42 -0
  144. package/scripts/postinstall-lexical-patch.cjs +62 -36
@@ -0,0 +1,6 @@
1
+ import { IEditor } from "../../../types/kernel.js";
2
+ import "../../../types/index.js";
3
+ //#region src/plugins/toc/react/getNearestScrollContainer.d.ts
4
+ type GetTocScrollContainer = (editor: IEditor) => HTMLElement | Window | null;
5
+ //#endregion
6
+ export { GetTocScrollContainer };
@@ -0,0 +1,18 @@
1
+ //#region src/plugins/toc/react/getNearestScrollContainer.ts
2
+ function getNearestScrollContainer(element) {
3
+ if (!element || typeof window === "undefined") return window;
4
+ let current = element.parentElement;
5
+ while (current) {
6
+ if (current === document.body || current === document.documentElement) return window;
7
+ const style = window.getComputedStyle(current);
8
+ const overflow = `${style.overflow} ${style.overflowY} ${style.overflowX}`;
9
+ if (/(auto|scroll|overlay)/.test(overflow) && current.scrollHeight > current.clientHeight) return current;
10
+ current = current.parentElement;
11
+ }
12
+ return window;
13
+ }
14
+ function resolveTocScrollContainer(editor, getScrollContainer) {
15
+ return getScrollContainer?.(editor) ?? getNearestScrollContainer(editor.getRootElement());
16
+ }
17
+ //#endregion
18
+ export { getNearestScrollContainer, resolveTocScrollContainer };
@@ -0,0 +1,59 @@
1
+ import { resolveTocScrollContainer } from "../getNearestScrollContainer.js";
2
+ import { useEffect } from "react";
3
+ //#region src/plugins/toc/react/hooks/useActiveHeading.ts
4
+ function useActiveHeading({ editor, getScrollContainer, offsetTop, service }) {
5
+ useEffect(() => {
6
+ const lexicalEditor = editor.getLexicalEditor();
7
+ if (!lexicalEditor || !service) {
8
+ service?.setActiveKey(null);
9
+ return;
10
+ }
11
+ const scrollContainer = resolveTocScrollContainer(editor, getScrollContainer);
12
+ let frameId = null;
13
+ const measure = () => {
14
+ const flatItems = service.getFlatItems();
15
+ if (flatItems.length === 0) {
16
+ service.setActiveKey(null);
17
+ return;
18
+ }
19
+ let nextActiveKey = flatItems[0]?.key ?? null;
20
+ const containerTop = scrollContainer instanceof Window ? 0 : scrollContainer.getBoundingClientRect().top;
21
+ for (const item of flatItems) {
22
+ const element = lexicalEditor.getElementByKey(item.key);
23
+ if (!element) continue;
24
+ if (element.getBoundingClientRect().top - containerTop <= offsetTop + 1) nextActiveKey = item.key;
25
+ else break;
26
+ }
27
+ service.setActiveKey(nextActiveKey);
28
+ };
29
+ const scheduleMeasure = () => {
30
+ if (frameId !== null) return;
31
+ frameId = window.requestAnimationFrame(() => {
32
+ frameId = null;
33
+ measure();
34
+ });
35
+ };
36
+ measure();
37
+ const scrollOptions = { passive: true };
38
+ const documentScrollOptions = {
39
+ capture: true,
40
+ passive: true
41
+ };
42
+ scrollContainer.addEventListener("scroll", scheduleMeasure, scrollOptions);
43
+ document.addEventListener("scroll", scheduleMeasure, documentScrollOptions);
44
+ window.addEventListener("resize", scheduleMeasure);
45
+ return () => {
46
+ if (frameId !== null) window.cancelAnimationFrame(frameId);
47
+ scrollContainer.removeEventListener("scroll", scheduleMeasure, false);
48
+ document.removeEventListener("scroll", scheduleMeasure, true);
49
+ window.removeEventListener("resize", scheduleMeasure);
50
+ };
51
+ }, [
52
+ editor,
53
+ getScrollContainer,
54
+ offsetTop,
55
+ service
56
+ ]);
57
+ }
58
+ //#endregion
59
+ export { useActiveHeading };
@@ -0,0 +1,50 @@
1
+ import { ITocService } from "../../service/i-toc-service.js";
2
+ import { useEffect, useRef, useState } from "react";
3
+ //#region src/plugins/toc/react/hooks/useTocItems.ts
4
+ function useTocItems({ editor, maxDepth, minDepth, onItemsChange }) {
5
+ const onItemsChangeRef = useRef(onItemsChange);
6
+ const [service, setService] = useState(() => editor.requireService(ITocService));
7
+ const [activeKey, setActiveKey] = useState(null);
8
+ const [items, setItems] = useState([]);
9
+ onItemsChangeRef.current = onItemsChange;
10
+ useEffect(() => {
11
+ let unsubscribeService;
12
+ const attachService = () => {
13
+ const tocService = editor.requireService(ITocService);
14
+ if (!tocService) return;
15
+ unsubscribeService?.();
16
+ unsubscribeService = void 0;
17
+ tocService.setDepthRange({
18
+ maxDepth,
19
+ minDepth
20
+ });
21
+ tocService.refresh();
22
+ setService(tocService);
23
+ const sync = () => {
24
+ const nextItems = tocService.getItems();
25
+ setItems(nextItems);
26
+ setActiveKey(tocService.getActiveKey());
27
+ onItemsChangeRef.current?.(nextItems);
28
+ };
29
+ sync();
30
+ unsubscribeService = tocService.subscribe(sync);
31
+ };
32
+ attachService();
33
+ editor.on("initialized", attachService);
34
+ return () => {
35
+ editor.off("initialized", attachService);
36
+ unsubscribeService?.();
37
+ };
38
+ }, [
39
+ editor,
40
+ maxDepth,
41
+ minDepth
42
+ ]);
43
+ return {
44
+ activeKey,
45
+ items,
46
+ service
47
+ };
48
+ }
49
+ //#endregion
50
+ export { useTocItems };
@@ -0,0 +1,5 @@
1
+ import { TocHeadingTag, TocItem } from "../types.js";
2
+ import { ReactTocPluginProps, UseTocOptions, UseTocResult } from "./type.js";
3
+ import ReactTocPlugin from "./ReactTocPlugin.js";
4
+ import { useToc } from "./useToc.js";
5
+ export { ReactTocPlugin, type ReactTocPluginProps, type TocHeadingTag, type TocItem, type UseTocOptions, type UseTocResult, useToc };
@@ -0,0 +1,30 @@
1
+ import { IEditor } from "../../../types/kernel.js";
2
+ import "../../../types/index.js";
3
+ import { TocHeadingTag, TocItem, TocScrollBehavior } from "../types.js";
4
+ import { ITocService } from "../service/i-toc-service.js";
5
+ import "../service/index.js";
6
+ import { GetTocScrollContainer } from "./getNearestScrollContainer.js";
7
+ //#region src/plugins/toc/react/type.d.ts
8
+ interface ReactTocPluginProps {
9
+ editor?: IEditor;
10
+ maxDepth?: number;
11
+ minDepth?: number;
12
+ }
13
+ interface UseTocOptions {
14
+ /** @default 'smooth' */
15
+ behavior?: TocScrollBehavior;
16
+ editor: IEditor;
17
+ getScrollContainer?: GetTocScrollContainer;
18
+ maxDepth?: number;
19
+ minDepth?: number;
20
+ offsetTop?: number;
21
+ onItemsChange?: (items: TocItem[]) => void;
22
+ }
23
+ interface UseTocResult {
24
+ activeKey: null | string;
25
+ items: TocItem[];
26
+ jumpTo: (key: string) => void;
27
+ service: ITocService | null;
28
+ }
29
+ //#endregion
30
+ export { ReactTocPluginProps, type TocHeadingTag, type TocItem, UseTocOptions, UseTocResult };
@@ -0,0 +1,5 @@
1
+ import { UseTocOptions, UseTocResult } from "./type.js";
2
+ //#region src/plugins/toc/react/useToc.d.ts
3
+ declare function useToc({ behavior, editor, getScrollContainer, maxDepth, minDepth, offsetTop, onItemsChange }: UseTocOptions): UseTocResult;
4
+ //#endregion
5
+ export { useToc };
@@ -0,0 +1,41 @@
1
+ "use client";
2
+ import { resolveTocScrollContainer } from "./getNearestScrollContainer.js";
3
+ import { useActiveHeading } from "./hooks/useActiveHeading.js";
4
+ import { useTocItems } from "./hooks/useTocItems.js";
5
+ import { useCallback } from "react";
6
+ //#region src/plugins/toc/react/useToc.ts
7
+ function useToc({ behavior = "smooth", editor, getScrollContainer, maxDepth = 6, minDepth = 1, offsetTop = 0, onItemsChange }) {
8
+ const { activeKey, items, service } = useTocItems({
9
+ editor,
10
+ maxDepth,
11
+ minDepth,
12
+ onItemsChange
13
+ });
14
+ useActiveHeading({
15
+ editor,
16
+ getScrollContainer,
17
+ offsetTop,
18
+ service
19
+ });
20
+ return {
21
+ activeKey,
22
+ items,
23
+ jumpTo: useCallback((key) => {
24
+ const container = resolveTocScrollContainer(editor, getScrollContainer);
25
+ service?.jumpTo(key, {
26
+ behavior,
27
+ container,
28
+ offsetTop
29
+ });
30
+ }, [
31
+ behavior,
32
+ editor,
33
+ getScrollContainer,
34
+ offsetTop,
35
+ service
36
+ ]),
37
+ service
38
+ };
39
+ }
40
+ //#endregion
41
+ export { useToc };
@@ -0,0 +1,39 @@
1
+ import { IServiceID } from "../../../types/kernel.js";
2
+ import "../../../types/index.js";
3
+ import { TocHeadingTag, TocItem, TocJumpOptions, TocPluginOptions } from "../types.js";
4
+ import EventEmitter from "eventemitter3";
5
+ import { LexicalEditor } from "lexical";
6
+ //#region src/plugins/toc/service/i-toc-service.d.ts
7
+ interface ITocService {
8
+ getActiveKey(): null | string;
9
+ getFlatItems(): TocItem[];
10
+ getItems(): TocItem[];
11
+ jumpTo(key: string, options?: TocJumpOptions): boolean;
12
+ refresh(): TocItem[];
13
+ setActiveKey(key: null | string): void;
14
+ setDepthRange(options: TocPluginOptions): void;
15
+ subscribe(listener: () => void): () => void;
16
+ }
17
+ declare const ITocService: IServiceID<ITocService>;
18
+ type TocServiceEvents = {
19
+ change: () => void;
20
+ };
21
+ declare class TocService extends EventEmitter<TocServiceEvents> implements ITocService {
22
+ private activeKey;
23
+ private editor;
24
+ private items;
25
+ private maxDepth;
26
+ private minDepth;
27
+ bindEditor(editor: LexicalEditor): void;
28
+ getActiveKey(): null | string;
29
+ getFlatItems(): TocItem[];
30
+ getItems(): TocItem[];
31
+ jumpTo(key: string, options?: TocJumpOptions): boolean;
32
+ refresh(): TocItem[];
33
+ setActiveKey(key: null | string): void;
34
+ setDepthRange(options: TocPluginOptions): void;
35
+ subscribe(listener: () => void): () => void;
36
+ private updateItems;
37
+ }
38
+ //#endregion
39
+ export { ITocService, type TocJumpOptions, type TocPluginOptions, TocService };
@@ -0,0 +1,125 @@
1
+ import { genServiceId, init_utils } from "../../../editor-kernel/utils.js";
2
+ import { buildTocTree, flattenTocTree } from "../utils/index.js";
3
+ import EventEmitter from "eventemitter3";
4
+ import { $getRoot, $isElementNode } from "lexical";
5
+ import { $isHeadingNode } from "@lexical/rich-text";
6
+ //#region src/plugins/toc/service/i-toc-service.ts
7
+ init_utils();
8
+ const ITocService = genServiceId("TocService");
9
+ function walkHeadings(node, headings, minDepth, maxDepth) {
10
+ if ($isHeadingNode(node)) {
11
+ const tag = node.getTag();
12
+ const depth = Number(tag.slice(1));
13
+ const title = node.getTextContent().trim();
14
+ if (title && depth >= minDepth && depth <= maxDepth) headings.push({
15
+ depth,
16
+ key: node.getKey(),
17
+ tag,
18
+ title
19
+ });
20
+ }
21
+ if ($isElementNode(node)) for (const child of node.getChildren()) walkHeadings(child, headings, minDepth, maxDepth);
22
+ }
23
+ function isSameTocItems(a, b) {
24
+ const flatA = flattenTocTree(a);
25
+ const flatB = flattenTocTree(b);
26
+ if (flatA.length !== flatB.length) return false;
27
+ return flatA.every((item, index) => {
28
+ const next = flatB[index];
29
+ return item.key === next.key && item.depth === next.depth && item.tag === next.tag && item.title === next.title;
30
+ });
31
+ }
32
+ var TocService = class extends EventEmitter {
33
+ constructor(..._args) {
34
+ super(..._args);
35
+ this.activeKey = null;
36
+ this.editor = null;
37
+ this.items = [];
38
+ this.maxDepth = 6;
39
+ this.minDepth = 1;
40
+ }
41
+ bindEditor(editor) {
42
+ this.editor = editor;
43
+ }
44
+ getActiveKey() {
45
+ return this.activeKey;
46
+ }
47
+ getFlatItems() {
48
+ return flattenTocTree(this.items);
49
+ }
50
+ getItems() {
51
+ return this.items;
52
+ }
53
+ jumpTo(key, options) {
54
+ const element = this.editor?.getElementByKey(key);
55
+ if (!element) return false;
56
+ const behavior = options?.behavior ?? "smooth";
57
+ const offsetTop = options?.offsetTop ?? 0;
58
+ const scrollContainer = options?.container;
59
+ if (typeof window === "undefined") {
60
+ element.scrollIntoView({
61
+ behavior,
62
+ block: options?.block ?? "start"
63
+ });
64
+ this.setActiveKey(key);
65
+ return true;
66
+ }
67
+ if (!scrollContainer || scrollContainer instanceof Window) {
68
+ const top = element.getBoundingClientRect().top + window.scrollY - offsetTop;
69
+ window.scrollTo({
70
+ behavior,
71
+ top
72
+ });
73
+ } else {
74
+ const containerTop = scrollContainer.getBoundingClientRect().top;
75
+ const top = scrollContainer.scrollTop + element.getBoundingClientRect().top - containerTop - offsetTop;
76
+ scrollContainer.scrollTo({
77
+ behavior,
78
+ top
79
+ });
80
+ }
81
+ this.setActiveKey(key);
82
+ return true;
83
+ }
84
+ refresh() {
85
+ if (!this.editor) {
86
+ this.updateItems([]);
87
+ return [];
88
+ }
89
+ const items = this.editor.getEditorState().read(() => {
90
+ const headings = [];
91
+ walkHeadings($getRoot(), headings, this.minDepth, this.maxDepth);
92
+ return buildTocTree(headings);
93
+ });
94
+ this.updateItems(items);
95
+ return items;
96
+ }
97
+ setActiveKey(key) {
98
+ if (this.activeKey === key) return;
99
+ this.activeKey = key;
100
+ this.emit("change");
101
+ }
102
+ setDepthRange(options) {
103
+ const minDepth = options.minDepth ?? 1;
104
+ const maxDepth = options.maxDepth ?? 6;
105
+ if (this.minDepth === minDepth && this.maxDepth === maxDepth) return;
106
+ this.minDepth = minDepth;
107
+ this.maxDepth = maxDepth;
108
+ this.refresh();
109
+ }
110
+ subscribe(listener) {
111
+ this.on("change", listener);
112
+ return () => {
113
+ this.off("change", listener);
114
+ };
115
+ }
116
+ updateItems(items) {
117
+ if (isSameTocItems(this.items, items)) return;
118
+ this.items = items;
119
+ const flatItems = this.getFlatItems();
120
+ if (this.activeKey && !flatItems.some((item) => item.key === this.activeKey)) this.activeKey = flatItems[0]?.key ?? null;
121
+ this.emit("change");
122
+ }
123
+ };
124
+ //#endregion
125
+ export { ITocService, TocService };
@@ -0,0 +1,3 @@
1
+ import { TocHeadingTag, TocItem, TocJumpOptions, TocPluginOptions } from "../types.js";
2
+ import { ITocService, TocService } from "./i-toc-service.js";
3
+ export { ITocService, type TocJumpOptions, type TocPluginOptions, TocService };
@@ -0,0 +1,23 @@
1
+ //#region src/plugins/toc/types.d.ts
2
+ type TocHeadingTag = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
3
+ interface TocItem {
4
+ children: TocItem[];
5
+ depth: number;
6
+ key: string;
7
+ tag: TocHeadingTag;
8
+ title: string;
9
+ }
10
+ interface TocPluginOptions {
11
+ maxDepth?: number;
12
+ minDepth?: number;
13
+ }
14
+ type TocScrollBehavior = 'auto' | 'instant' | 'smooth';
15
+ type TocScrollLogicalPosition = 'center' | 'end' | 'nearest' | 'start';
16
+ interface TocJumpOptions {
17
+ behavior?: TocScrollBehavior;
18
+ block?: TocScrollLogicalPosition;
19
+ container?: HTMLElement | Window | null;
20
+ offsetTop?: number;
21
+ }
22
+ //#endregion
23
+ export { TocHeadingTag, TocItem, TocJumpOptions, TocPluginOptions, TocScrollBehavior, TocScrollLogicalPosition };
@@ -0,0 +1,6 @@
1
+ import { TocItem } from "../types.js";
2
+ //#region src/plugins/toc/utils/index.d.ts
3
+ declare function buildTocTree(items: Omit<TocItem, 'children'>[]): TocItem[];
4
+ declare function flattenTocTree(items: TocItem[]): TocItem[];
5
+ //#endregion
6
+ export { buildTocTree, flattenTocTree };
@@ -0,0 +1,30 @@
1
+ //#region src/plugins/toc/utils/index.ts
2
+ function buildTocTree(items) {
3
+ const roots = [];
4
+ const stack = [];
5
+ for (const item of items) {
6
+ const current = {
7
+ ...item,
8
+ children: []
9
+ };
10
+ while (stack.length > 0 && stack.at(-1).depth >= current.depth) stack.pop();
11
+ const parent = stack.at(-1);
12
+ if (parent) parent.children.push(current);
13
+ else roots.push(current);
14
+ stack.push(current);
15
+ }
16
+ return roots;
17
+ }
18
+ function flattenTocTree(items) {
19
+ const result = [];
20
+ const walk = (tocItems) => {
21
+ for (const item of tocItems) {
22
+ result.push(item);
23
+ walk(item.children);
24
+ }
25
+ };
26
+ walk(items);
27
+ return result;
28
+ }
29
+ //#endregion
30
+ export { buildTocTree, flattenTocTree };
@@ -26,6 +26,7 @@ const ReactToolbarPlugin = ({ className, children, getPopupContainer, usePortal
26
26
  const [kernelEditor] = useLexicalComposerContext();
27
27
  const { isDarkMode } = useThemeMode();
28
28
  const isMouseDownRef = useRef(false);
29
+ const linkToolbarSuppressionTokenRef = useRef(null);
29
30
  const logger = createDebugLogger("plugin", "toolbar");
30
31
  const resolvePortalContainer = useCallback(() => {
31
32
  if (!usePortal) return null;
@@ -36,6 +37,20 @@ const ReactToolbarPlugin = ({ className, children, getPopupContainer, usePortal
36
37
  const nextContainer = resolvePortalContainer();
37
38
  setPortalContainer((current) => current === nextContainer ? current : nextContainer);
38
39
  }, [resolvePortalContainer]);
40
+ const getLinkService = useCallback(() => {
41
+ return kernelEditor.requireService(ILinkService);
42
+ }, [kernelEditor]);
43
+ const suppressLinkToolbar = useCallback(() => {
44
+ const linkService = getLinkService();
45
+ if (!linkService || linkToolbarSuppressionTokenRef.current) return;
46
+ linkToolbarSuppressionTokenRef.current = linkService.suppressLinkToolbar("text-format-toolbar");
47
+ }, [getLinkService]);
48
+ const restoreLinkToolbar = useCallback(() => {
49
+ const token = linkToolbarSuppressionTokenRef.current;
50
+ if (!token) return;
51
+ linkToolbarSuppressionTokenRef.current = null;
52
+ getLinkService()?.restoreLinkToolbar(token);
53
+ }, [getLinkService]);
39
54
  const $updateTextFormatFloatingToolbar = useCallback((editor) => {
40
55
  if (!anchorElemRef.current) return;
41
56
  const selection = $getSelection();
@@ -47,18 +62,25 @@ const ReactToolbarPlugin = ({ className, children, getPopupContainer, usePortal
47
62
  const rangeRect = getDOMRangeRect(nativeSelection, rootElement);
48
63
  logger.debug("🔍 rangeRect", rangeRect);
49
64
  setFloatingElemPosition(rangeRect, popupCharStylesEditorElem, anchorElemRef.current, false);
65
+ suppressLinkToolbar();
50
66
  } else {
51
67
  popupCharStylesEditorElem.style.opacity = "0";
52
68
  popupCharStylesEditorElem.style.transform = "translate(-10000px, -10000px)";
69
+ restoreLinkToolbar();
53
70
  }
54
- }, [anchorElemRef]);
71
+ }, [
72
+ anchorElemRef,
73
+ restoreLinkToolbar,
74
+ suppressLinkToolbar
75
+ ]);
55
76
  const $hideFloatingToolbar = useCallback(() => {
56
77
  if (!anchorElemRef.current) return;
57
78
  const popupCharStylesEditorElem = popupCharStylesEditorRef.current;
58
79
  if (popupCharStylesEditorElem === null) return;
59
80
  popupCharStylesEditorElem.style.opacity = "0";
60
81
  popupCharStylesEditorElem.style.transform = "translate(-10000px, -10000px)";
61
- }, [anchorElemRef]);
82
+ restoreLinkToolbar();
83
+ }, [anchorElemRef, restoreLinkToolbar]);
62
84
  const handleMouseDownFactory = useCallback((updateToolbar) => (e) => {
63
85
  if (e.button === 0) {
64
86
  isMouseDownRef.current = true;
@@ -71,15 +93,6 @@ const ReactToolbarPlugin = ({ className, children, getPopupContainer, usePortal
71
93
  updateToolbar();
72
94
  }
73
95
  }, []);
74
- useLexicalEditor(() => {
75
- const service = kernelEditor.requireService(ILinkService);
76
- if (service) {
77
- service.setLinkToolbar(false);
78
- return () => {
79
- service.setLinkToolbar(true);
80
- };
81
- }
82
- }, []);
83
96
  useLexicalEditor((editor) => {
84
97
  if (!usePortal) {
85
98
  setPortalContainer(null);
@@ -132,6 +145,7 @@ const ReactToolbarPlugin = ({ className, children, getPopupContainer, usePortal
132
145
  if (!isMouseDownRef.current) $updateTextFormatFloatingToolbar(editor);
133
146
  return false;
134
147
  }, COMMAND_PRIORITY_LOW), () => {
148
+ restoreLinkToolbar();
135
149
  if (rootElement) {
136
150
  rootElement.removeEventListener("mousedown", handleMouseDown);
137
151
  document.removeEventListener("mouseup", handleMouseUp);
@@ -143,7 +157,7 @@ const ReactToolbarPlugin = ({ className, children, getPopupContainer, usePortal
143
157
  if (animationFrameId !== null) editorWindow.cancelAnimationFrame(animationFrameId);
144
158
  }
145
159
  });
146
- });
160
+ }, [restoreLinkToolbar]);
147
161
  const toolbarNode = /* @__PURE__ */ jsx("div", {
148
162
  className: usePortal ? styles.portalAnchor : styles.anchor,
149
163
  ref: anchorElemRef,
@@ -1,5 +1,6 @@
1
1
  import { createStaticStyles } from "antd-style";
2
2
  //#region src/plugins/toolbar/react/style.ts
3
+ const FLOATING_TOOLBAR_Z_INDEX = 1001;
3
4
  const styles = createStaticStyles(({ css, cssVar }) => ({
4
5
  anchor: css`
5
6
  position: relative;
@@ -15,7 +16,7 @@ const styles = createStaticStyles(({ css, cssVar }) => ({
15
16
  will-change: transform;
16
17
 
17
18
  position: absolute;
18
- z-index: 10;
19
+ z-index: ${FLOATING_TOOLBAR_Z_INDEX};
19
20
  inset-block-start: 0;
20
21
  inset-inline-start: 0;
21
22
  transform: translate(-10000px, -10000px);
@@ -39,7 +40,7 @@ const styles = createStaticStyles(({ css, cssVar }) => ({
39
40
  will-change: transform;
40
41
 
41
42
  position: absolute;
42
- z-index: 10;
43
+ z-index: ${FLOATING_TOOLBAR_Z_INDEX};
43
44
  inset-block-start: 0;
44
45
  inset-inline-start: 0;
45
46
  transform: translate(-10000px, -10000px);
@@ -1,9 +1,9 @@
1
1
  import { createDebugLogger, init_debug } from "../../../utils/debug.js";
2
2
  import { $closest, init_utils } from "../../../editor-kernel/utils.js";
3
3
  import { SELECT_AFTER_CODEMIRROR_COMMAND, SELECT_BEFORE_CODEMIRROR_COMMAND } from "../../codemirror-block/command/index.js";
4
- import { $createParagraphNode, $getNodeByKey, $getRoot, $getSelection, $isDecoratorNode, $isNodeSelection, $isRangeSelection, $isRootOrShadowRoot, $isTextNode, COMMAND_PRIORITY_NORMAL, KEY_ARROW_DOWN_COMMAND, KEY_ARROW_UP_COMMAND, ParagraphNode } from "lexical";
4
+ import { $createParagraphNode, $getNodeByKey, $getRoot, $getSelection, $isDecoratorNode, $isElementNode, $isNodeSelection, $isRangeSelection, $isRootOrShadowRoot, $isTextNode, COMMAND_PRIORITY_NORMAL, KEY_ARROW_DOWN_COMMAND, KEY_ARROW_UP_COMMAND, ParagraphNode } from "lexical";
5
5
  import { mergeRegister } from "@lexical/utils";
6
- import "@lexical/code-core";
6
+ import { $isListItemNode, $isListNode } from "@lexical/list";
7
7
  //#region src/plugins/virtual-block/plugin/register.ts
8
8
  init_utils();
9
9
  init_debug();
@@ -21,9 +21,36 @@ function $isEmptyNode(node) {
21
21
  if (node.getChildrenSize() === 0) return true;
22
22
  for (const child of node.getChildren()) if ($isTextNode(child)) {
23
23
  if (child.getTextContent().trim().length > 0) return false;
24
- } else if (!$isEmptyNode(child)) return false;
24
+ } else if ($isElementNode(child)) {
25
+ if (!$isEmptyNode(child)) return false;
26
+ } else return false;
25
27
  return true;
26
28
  }
29
+ function $getTopLevelListItem(selection) {
30
+ const focusNode = selection.focus.getNode();
31
+ const listNode = toRootElement(focusNode);
32
+ if (!$isListNode(listNode)) return null;
33
+ let current = focusNode;
34
+ while (current.getParent() !== listNode) {
35
+ const parent = current.getParent();
36
+ if (!parent || $isRootOrShadowRoot(parent)) return null;
37
+ current = parent;
38
+ }
39
+ if (!$isListItemNode(current)) return null;
40
+ return {
41
+ listItem: current,
42
+ listNode
43
+ };
44
+ }
45
+ function $isListBoundarySelection(selection, isBackward) {
46
+ if (!selection.isCollapsed()) return false;
47
+ const focusNode = selection.focus.getNode();
48
+ if ($isListNode(focusNode)) return isBackward ? selection.focus.offset === 0 : selection.focus.offset === focusNode.getChildrenSize();
49
+ const listContext = $getTopLevelListItem(selection);
50
+ if (!listContext) return false;
51
+ const { listItem } = listContext;
52
+ return isBackward ? !listItem.getPreviousSibling() : !listItem.getNextSibling();
53
+ }
27
54
  function registerRichKeydown(editor, kernel) {
28
55
  let needRemoveOnFocusNode = null;
29
56
  return mergeRegister(editor.registerUpdateListener((payload) => {
@@ -76,14 +103,8 @@ function registerRichKeydown(editor, kernel) {
76
103
  } else if ($isRangeSelection(selection)) {
77
104
  const possibleNode = toRootElement(selection.focus.getNode());
78
105
  if (possibleNode === needRemoveOnFocusNode || $isEmptyNode(possibleNode)) return false;
79
- if ($getRoot().getFirstChild() === possibleNode) {
80
- event.preventDefault();
81
- const p = $createParagraphNode();
82
- possibleNode.insertBefore(p);
83
- needRemoveOnFocusNode = p;
84
- p.selectStart();
85
- return true;
86
- } else if ($isDecoratorNode(possibleNode.getPreviousSibling())) {
106
+ if ($isListNode(possibleNode) && !$isListBoundarySelection(selection, true)) return false;
107
+ if ($getRoot().getFirstChild() === possibleNode || $isDecoratorNode(possibleNode.getPreviousSibling())) {
87
108
  event.preventDefault();
88
109
  const p = $createParagraphNode();
89
110
  possibleNode.insertBefore(p);
@@ -111,14 +132,15 @@ function registerRichKeydown(editor, kernel) {
111
132
  if (!selection.isCollapsed()) return false;
112
133
  const possibleNode = toRootElement(selection.focus.getNode());
113
134
  if (possibleNode === needRemoveOnFocusNode || $isEmptyNode(possibleNode)) return false;
135
+ if ($isListNode(possibleNode) && !$isListBoundarySelection(selection, false)) return false;
114
136
  if ($getRoot().getLastChild() === possibleNode) {
115
137
  event.preventDefault();
116
138
  const p = $createParagraphNode();
117
139
  possibleNode.insertAfter(p);
118
140
  needRemoveOnFocusNode = p;
119
141
  p.selectStart();
142
+ return true;
120
143
  }
121
- return true;
122
144
  }
123
145
  return false;
124
146
  }, COMMAND_PRIORITY_NORMAL), editor.registerCommand(SELECT_BEFORE_CODEMIRROR_COMMAND, (payload) => {
@@ -0,0 +1,6 @@
1
+ import { YjsInitialEditorState, YjsPluginOptions, YjsProviderFactory } from "./plugin/types.js";
2
+ import { YjsPlugin } from "./plugin/index.js";
3
+ import { Doc, ReactYjsPlugin, ReactYjsPluginProps } from "./react/index.js";
4
+ import { IYjsService, YjsAwarenessUser, YjsPluginState, YjsService } from "./service/index.js";
5
+ import { CreateWebSocketYjsProviderOptions, WebSocketYjsProvider, WebSocketYjsProviderStatus, createWebSocketYjsProvider, fetchWebSocketDemoDocument, saveWebSocketDemoDocument, snapshotWebSocketDemoDocument } from "./websocket-provider.js";
6
+ export { CreateWebSocketYjsProviderOptions, type Doc, IYjsService, ReactYjsPlugin, ReactYjsPluginProps, WebSocketYjsProvider, WebSocketYjsProviderStatus, YjsAwarenessUser, type YjsInitialEditorState, YjsPlugin, type YjsPluginOptions, YjsPluginState, type YjsProviderFactory, YjsService, createWebSocketYjsProvider, fetchWebSocketDemoDocument, saveWebSocketDemoDocument, snapshotWebSocketDemoDocument };
@@ -0,0 +1,7 @@
1
+ import { IEditorPluginConstructor } from "../../../types/kernel.js";
2
+ import "../../../types/index.js";
3
+ import { YjsInitialEditorState, YjsPluginOptions, YjsProviderFactory } from "./types.js";
4
+ //#region src/plugins/yjs/plugin/index.d.ts
5
+ declare const YjsPlugin: IEditorPluginConstructor<YjsPluginOptions>;
6
+ //#endregion
7
+ export { type YjsInitialEditorState, YjsPlugin, type YjsPluginOptions, type YjsProviderFactory };