@lobehub/editor 4.24.1 → 4.25.1
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/es/codemirror/style.js +42 -0
- package/es/editor-kernel/kernel.d.ts +9 -0
- package/es/editor-kernel/kernel.js +74 -11
- package/es/editor-kernel/react/useDecorators.js +2 -4
- package/es/headless/collapsible-plugin.js +74 -0
- package/es/headless/index.js +2 -0
- package/es/headless.d.ts +30 -0
- package/es/headless.js +2618 -139
- package/es/index.d.ts +26 -3
- package/es/index.js +17 -2
- package/es/locale/index.d.ts +14 -0
- package/es/locale/index.js +14 -0
- package/es/plugins/block/command/index.js +24 -0
- package/es/plugins/block/plugin/index.js +7 -5
- package/es/plugins/block/react/ReactBlockPlugin.js +43 -1
- package/es/plugins/block/react/core/menu-position.js +20 -0
- package/es/plugins/block/react/drag/drag-session.js +3 -2
- package/es/plugins/block/react/drag/drag-utils.js +13 -1
- package/es/plugins/codemirror-block/index.d.ts +2 -1
- package/es/plugins/codemirror-block/plugin/index.js +2 -0
- package/es/plugins/codemirror-block/react/CodemirrorNode.js +95 -5
- package/es/plugins/codemirror-block/react/useCodemirrorEditLock.js +33 -0
- package/es/plugins/codemirror-block/service/index.d.ts +40 -0
- package/es/plugins/codemirror-block/service/index.js +59 -0
- package/es/plugins/collapsible/command/index.d.ts +17 -0
- package/es/plugins/collapsible/command/index.js +72 -0
- package/es/plugins/collapsible/index.d.ts +7 -0
- package/es/plugins/collapsible/node/CollapsibleNode.d.ts +30 -0
- package/es/plugins/collapsible/node/CollapsibleNode.js +138 -0
- package/es/plugins/collapsible/plugin/index.d.ts +11 -0
- package/es/plugins/collapsible/plugin/index.js +535 -0
- package/es/plugins/collapsible/react/ReactCollapsiblePlugin.d.ts +6 -0
- package/es/plugins/collapsible/react/ReactCollapsiblePlugin.js +19 -0
- package/es/plugins/collapsible/react/index.d.ts +3 -0
- package/es/plugins/collapsible/react/style.js +67 -0
- package/es/plugins/collapsible/react/type.d.ts +6 -0
- package/es/plugins/common/plugin/index.js +14 -4
- package/es/plugins/common/plugin/progressive-select-all.js +81 -0
- package/es/plugins/common/plugin/register.js +73 -4
- package/es/plugins/common/react/ReactPlainText.js +27 -9
- package/es/plugins/link/command/index.js +26 -3
- package/es/plugins/link/conversion/index.js +237 -0
- package/es/plugins/link/index.d.ts +5 -3
- package/es/plugins/link/node/LinkBlockCardNode.d.ts +16 -0
- package/es/plugins/link/node/LinkBlockCardNode.js +66 -0
- package/es/plugins/link/node/LinkCardNode.d.ts +42 -0
- package/es/plugins/link/node/LinkCardNode.js +161 -0
- package/es/plugins/link/node/LinkIframeNode.d.ts +33 -0
- package/es/plugins/link/node/LinkIframeNode.js +131 -0
- package/es/plugins/link/node/LinkNode.d.ts +48 -2
- package/es/plugins/link/node/LinkNode.js +14 -6
- package/es/plugins/link/node/SchemaNode.d.ts +36 -0
- package/es/plugins/link/node/SchemaNode.js +149 -0
- package/es/plugins/link/normalization/index.d.ts +7 -0
- package/es/plugins/link/normalization/index.js +38 -0
- package/es/plugins/link/plugin/index.d.ts +26 -1
- package/es/plugins/link/plugin/index.js +124 -4
- package/es/plugins/link/react/ReactLinkPlugin.js +165 -7
- package/es/plugins/link/react/components/LinkCard.js +235 -0
- package/es/plugins/link/react/components/LinkEdit.js +93 -14
- package/es/plugins/link/react/components/LinkIframe.js +128 -0
- package/es/plugins/link/react/components/LinkToolbar.js +440 -111
- package/es/plugins/link/react/components/SchemaLink.js +49 -0
- package/es/plugins/link/react/index.d.ts +2 -2
- package/es/plugins/link/react/renderer-registry.d.ts +81 -0
- package/es/plugins/link/react/renderer-registry.js +80 -0
- package/es/plugins/link/react/style.js +63 -1
- package/es/plugins/link/react/type.d.ts +24 -1
- package/es/plugins/link/service/i-link-service.d.ts +156 -1
- package/es/plugins/link/service/i-link-service.js +258 -5
- package/es/plugins/list/plugin/index.js +9 -2
- package/es/plugins/litexml/command/diffCommand.js +94 -14
- package/es/plugins/litexml/command/index.js +187 -6
- package/es/plugins/litexml/diff-validation.js +237 -0
- package/es/plugins/litexml/node/DiffContentNode.js +67 -0
- package/es/plugins/litexml/node/DiffNode.d.ts +1 -1
- package/es/plugins/litexml/node/DiffNode.js +5 -2
- package/es/plugins/litexml/node/TableCellDiffNode.js +77 -0
- package/es/plugins/litexml/node/TableRowDiffNode.js +73 -0
- package/es/plugins/litexml/plugin/index.d.ts +2 -0
- package/es/plugins/litexml/plugin/index.js +62 -9
- package/es/plugins/litexml/react/DiffNodeToolbar/index.js +10 -12
- package/es/plugins/litexml/react/TableRowDiffToolbar.js +123 -0
- package/es/plugins/litexml/react/hooks/useHasDiffNode.js +5 -2
- package/es/plugins/litexml/react/index.js +6 -2
- package/es/plugins/litexml/react/style.js +18 -5
- package/es/plugins/litexml/table-cell-diff.js +153 -0
- package/es/plugins/litexml/table-row-diff.js +82 -0
- package/es/plugins/markdown/data-source/markdown/parse.js +25 -1
- package/es/plugins/markdown/plugin/index.js +5 -3
- package/es/plugins/slash/react/ReactSlashPlugin.js +22 -43
- package/es/plugins/slash/react/components/DefaultSlashMenu.js +76 -16
- package/es/plugins/slash/react/menu-utils.js +84 -0
- package/es/plugins/slash/service/i-slash-service.d.ts +3 -0
- package/es/plugins/table/plugin/index.js +4 -1
- package/es/plugins/table/plugin/single-cell-paste.js +79 -0
- package/es/plugins/table/react/TableController/hooks.js +7 -4
- package/es/plugins/table/react/hooks.js +2 -2
- package/es/plugins/table/react/index.js +2 -0
- package/es/plugins/table/react/style.js +15 -5
- package/es/plugins/table/react/useScrollFocusedTableCellIntoView.js +46 -0
- package/es/plugins/toc/index.d.ts +10 -0
- package/es/plugins/toc/plugin/index.d.ts +8 -0
- package/es/plugins/toc/plugin/index.js +56 -0
- package/es/plugins/toc/react/ReactTocPlugin.d.ts +6 -0
- package/es/plugins/toc/react/ReactTocPlugin.js +36 -0
- package/es/plugins/toc/react/getNearestScrollContainer.d.ts +6 -0
- package/es/plugins/toc/react/getNearestScrollContainer.js +18 -0
- package/es/plugins/toc/react/hooks/useActiveHeading.js +59 -0
- package/es/plugins/toc/react/hooks/useTocItems.js +50 -0
- package/es/plugins/toc/react/index.d.ts +5 -0
- package/es/plugins/toc/react/type.d.ts +30 -0
- package/es/plugins/toc/react/useToc.d.ts +5 -0
- package/es/plugins/toc/react/useToc.js +41 -0
- package/es/plugins/toc/service/i-toc-service.d.ts +39 -0
- package/es/plugins/toc/service/i-toc-service.js +125 -0
- package/es/plugins/toc/service/index.d.ts +3 -0
- package/es/plugins/toc/types.d.ts +23 -0
- package/es/plugins/toc/utils/index.d.ts +6 -0
- package/es/plugins/toc/utils/index.js +30 -0
- package/es/plugins/toolbar/react/index.js +26 -12
- package/es/plugins/toolbar/react/style.js +3 -2
- package/es/plugins/virtual-block/plugin/register.js +34 -12
- package/es/plugins/yjs/index.d.ts +6 -0
- package/es/plugins/yjs/plugin/index.d.ts +7 -0
- package/es/plugins/yjs/plugin/index.js +191 -0
- package/es/plugins/yjs/plugin/types.d.ts +16 -0
- package/es/plugins/yjs/plugin/utils/awareness.js +9 -0
- package/es/plugins/yjs/plugin/utils/editor-state.js +47 -0
- package/es/plugins/yjs/plugin/utils/history.js +31 -0
- package/es/plugins/yjs/plugin/utils/node-properties.js +39 -0
- package/es/plugins/yjs/plugin/utils/sync.js +24 -0
- package/es/plugins/yjs/react/codemirrorEditLockProvider.js +59 -0
- package/es/plugins/yjs/react/index.d.ts +23 -0
- package/es/plugins/yjs/react/index.js +200 -0
- package/es/plugins/yjs/service/index.d.ts +47 -0
- package/es/plugins/yjs/service/index.js +84 -0
- package/es/plugins/yjs/websocket-provider.d.ts +78 -0
- package/es/plugins/yjs/websocket-provider.js +263 -0
- package/es/react/hooks/useEditor.d.ts +24 -2
- package/es/react/hooks/useEditor.js +13 -3
- package/es/react.d.ts +2 -2
- package/es/renderer/LexicalRenderer.js +9 -2
- package/es/types/kernel.d.ts +16 -0
- package/es/utils/updatePosition.js +5 -2
- package/package.json +3 -1
- package/patches/@lexical__yjs@0.42.0.patch +42 -0
- package/scripts/postinstall-lexical-patch.cjs +62 -36
package/es/codemirror/style.js
CHANGED
|
@@ -50,6 +50,48 @@ const styles = createStaticStyles(({ css, cssVar }) => css`
|
|
|
50
50
|
transition: all 0.3s;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
&.collab-locked {
|
|
54
|
+
cursor: not-allowed;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
&.collab-locked::after {
|
|
58
|
+
pointer-events: none;
|
|
59
|
+
content: '';
|
|
60
|
+
|
|
61
|
+
position: absolute;
|
|
62
|
+
z-index: 11;
|
|
63
|
+
inset: 0;
|
|
64
|
+
|
|
65
|
+
background: ${cssVar.colorFillSecondary};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
&.collab-locked .cm-container {
|
|
69
|
+
opacity: 0.72;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.cm-collab-lock {
|
|
73
|
+
position: absolute;
|
|
74
|
+
z-index: 12;
|
|
75
|
+
inset-block-start: 8px;
|
|
76
|
+
inset-inline-end: 8px;
|
|
77
|
+
|
|
78
|
+
overflow: hidden;
|
|
79
|
+
|
|
80
|
+
max-width: min(240px, calc(100% - 16px));
|
|
81
|
+
padding-block: 2px;
|
|
82
|
+
padding-inline: 6px;
|
|
83
|
+
border-radius: 6px;
|
|
84
|
+
|
|
85
|
+
font-size: 12px;
|
|
86
|
+
line-height: 18px;
|
|
87
|
+
color: ${cssVar.colorTextSecondary};
|
|
88
|
+
text-overflow: ellipsis;
|
|
89
|
+
white-space: nowrap;
|
|
90
|
+
|
|
91
|
+
background: ${cssVar.colorBgElevated};
|
|
92
|
+
box-shadow: ${cssVar.boxShadowTertiary};
|
|
93
|
+
}
|
|
94
|
+
|
|
53
95
|
.cm-container {
|
|
54
96
|
position: relative;
|
|
55
97
|
width: 100%;
|
|
@@ -24,6 +24,11 @@ declare class Kernel extends EventEmitter implements IEditorKernel {
|
|
|
24
24
|
private hotReloadMode;
|
|
25
25
|
private logger;
|
|
26
26
|
private historyState;
|
|
27
|
+
/**
|
|
28
|
+
* Root-scoped resources are attached to the Lexical editor's root listener
|
|
29
|
+
* so direct Lexical setRootElement(null) calls are safe as well.
|
|
30
|
+
*/
|
|
31
|
+
private unregisterRootLifecycle?;
|
|
27
32
|
private editor?;
|
|
28
33
|
private headlessEditor;
|
|
29
34
|
constructor();
|
|
@@ -44,6 +49,7 @@ declare class Kernel extends EventEmitter implements IEditorKernel {
|
|
|
44
49
|
destroy(): void;
|
|
45
50
|
getRootElement(): HTMLElement | null;
|
|
46
51
|
setRootElement(dom: HTMLElement, editable?: boolean): LexicalEditor;
|
|
52
|
+
setRootElement(dom: null, editable?: boolean): LexicalEditor | null;
|
|
47
53
|
setEditable(editable: boolean): void;
|
|
48
54
|
initNodeEditor(): LexicalEditor;
|
|
49
55
|
initHeadlessEditor(): LexicalEditor;
|
|
@@ -80,6 +86,9 @@ declare class Kernel extends EventEmitter implements IEditorKernel {
|
|
|
80
86
|
registerNodeTransform(transform: INodeRegistrationTransform): () => void;
|
|
81
87
|
registerBeforeEditorInit(hook: IBeforeEditorInitLifecycle): () => void;
|
|
82
88
|
registerRootClassName(className: string): () => void;
|
|
89
|
+
registerRootListener(listener: (rootElement: HTMLElement | null) => void): () => void;
|
|
90
|
+
private getEditorId;
|
|
91
|
+
private registerRootLifecycle;
|
|
83
92
|
private runBeforeEditorInitLifecycle;
|
|
84
93
|
private applyRootClassNames;
|
|
85
94
|
private resolveNodesForInitialization;
|
|
@@ -95,26 +95,46 @@ var init_kernel = __esmMin((() => {
|
|
|
95
95
|
return this.editor || null;
|
|
96
96
|
}
|
|
97
97
|
destroy() {
|
|
98
|
-
|
|
98
|
+
const editor = this.editor;
|
|
99
|
+
const editorId = this.getEditorId();
|
|
99
100
|
this.logger.info(`🗑️ Destroying editor with ${this.pluginsInstances.length} plugins`);
|
|
101
|
+
if (editor) try {
|
|
102
|
+
editor.setRootElement(null);
|
|
103
|
+
} catch (error) {
|
|
104
|
+
this.logger.warn("Failed to detach editor root during destroy:", error);
|
|
105
|
+
}
|
|
106
|
+
unregisterEditorKernel(editorId);
|
|
100
107
|
try {
|
|
101
|
-
|
|
108
|
+
editor?.setEditorState(createEmptyEditorState());
|
|
102
109
|
} catch (error) {
|
|
103
110
|
this.logger.warn("Failed to reset editor state during destroy:", error);
|
|
104
111
|
}
|
|
105
|
-
this.
|
|
112
|
+
this.unregisterRootLifecycle?.();
|
|
113
|
+
this.unregisterRootLifecycle = void 0;
|
|
114
|
+
let destroyError;
|
|
106
115
|
this.pluginsInstances.forEach((plugin) => {
|
|
107
|
-
|
|
116
|
+
try {
|
|
117
|
+
plugin.destroy?.();
|
|
118
|
+
} catch (error) {
|
|
119
|
+
destroyError ??= error;
|
|
120
|
+
}
|
|
108
121
|
});
|
|
109
122
|
this.pluginsInstances = [];
|
|
110
|
-
this.
|
|
123
|
+
this.dataTypeMap.clear();
|
|
124
|
+
this.decorators = {};
|
|
111
125
|
this.nodeTransforms = [];
|
|
126
|
+
this.nodes = [];
|
|
127
|
+
this.beforeEditorInitHooks = [];
|
|
112
128
|
this.rootClassNames.clear();
|
|
113
129
|
this.serviceMap.clear();
|
|
114
|
-
this.
|
|
115
|
-
this.
|
|
130
|
+
this.themes = { [EDITOR_THEME_KEY]: generateEditorId() };
|
|
131
|
+
this.historyState = createEmptyHistoryState();
|
|
132
|
+
this._commands.clear();
|
|
133
|
+
this._commandsClean.clear();
|
|
134
|
+
this.editor = void 0;
|
|
116
135
|
this.headlessEditor = false;
|
|
117
136
|
this.logger.info("✅ Editor destroyed");
|
|
137
|
+
if (destroyError) throw destroyError;
|
|
118
138
|
}
|
|
119
139
|
getRootElement() {
|
|
120
140
|
if (this.headlessEditor) return null;
|
|
@@ -122,12 +142,23 @@ var init_kernel = __esmMin((() => {
|
|
|
122
142
|
}
|
|
123
143
|
setRootElement(dom, editable = true) {
|
|
124
144
|
if (this.editor) {
|
|
125
|
-
if (this.headlessEditor)
|
|
145
|
+
if (this.headlessEditor) {
|
|
146
|
+
if (dom === null) return this.editor;
|
|
147
|
+
throw new Error("Headless editor cannot be attached to a root element.");
|
|
148
|
+
}
|
|
126
149
|
this.logger.warn("[Editor] Editor is already initialized, updating root element only");
|
|
150
|
+
if (dom) registerEditorKernel(this.getEditorId(), this);
|
|
127
151
|
this.editor.setRootElement(dom);
|
|
128
|
-
|
|
152
|
+
if (dom) {
|
|
153
|
+
this.editor.setEditable(editable);
|
|
154
|
+
this.applyRootClassNames(dom);
|
|
155
|
+
}
|
|
129
156
|
return this.editor;
|
|
130
157
|
}
|
|
158
|
+
if (dom === null) {
|
|
159
|
+
unregisterEditorKernel(this.getEditorId());
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
131
162
|
if (this.pluginsInstances.length === 0) {
|
|
132
163
|
this.logger.info(`🔌 Initializing ${this.plugins.length} plugins`);
|
|
133
164
|
for (const plugin of this.plugins) {
|
|
@@ -138,7 +169,7 @@ var init_kernel = __esmMin((() => {
|
|
|
138
169
|
this.runBeforeEditorInitLifecycle();
|
|
139
170
|
const resolvedNodes = this.resolveNodesForInitialization();
|
|
140
171
|
this.logger.info(`📝 Creating editor with ${resolvedNodes.length} nodes`);
|
|
141
|
-
registerEditorKernel(this.
|
|
172
|
+
registerEditorKernel(this.getEditorId(), this);
|
|
142
173
|
const editor = this.editor = createEditor({
|
|
143
174
|
__kernel: this,
|
|
144
175
|
editable,
|
|
@@ -151,9 +182,9 @@ var init_kernel = __esmMin((() => {
|
|
|
151
182
|
theme: this.themes
|
|
152
183
|
});
|
|
153
184
|
this.headlessEditor = false;
|
|
185
|
+
this.registerRootLifecycle(editor);
|
|
154
186
|
this.editor.setRootElement(dom);
|
|
155
187
|
this.applyRootClassNames(dom);
|
|
156
|
-
registerEvent(editor, dom);
|
|
157
188
|
this.pluginsInstances.forEach((plugin) => {
|
|
158
189
|
plugin.onInit?.(editor);
|
|
159
190
|
});
|
|
@@ -193,6 +224,7 @@ var init_kernel = __esmMin((() => {
|
|
|
193
224
|
theme: this.themes
|
|
194
225
|
});
|
|
195
226
|
this.headlessEditor = false;
|
|
227
|
+
this.registerRootLifecycle(editor);
|
|
196
228
|
this.pluginsInstances.forEach((plugin) => {
|
|
197
229
|
plugin.onInit?.(editor);
|
|
198
230
|
});
|
|
@@ -246,6 +278,9 @@ var init_kernel = __esmMin((() => {
|
|
|
246
278
|
this.historyState.current = null;
|
|
247
279
|
}
|
|
248
280
|
datasource.read(this.editor, content, options);
|
|
281
|
+
this.pluginsInstances.forEach((plugin) => {
|
|
282
|
+
plugin.onDocumentChange?.(type, content, options);
|
|
283
|
+
});
|
|
249
284
|
this.emit("documentChange", type, content);
|
|
250
285
|
this.logger.debug(`📥 Set ${type} document`);
|
|
251
286
|
}
|
|
@@ -510,6 +545,34 @@ var init_kernel = __esmMin((() => {
|
|
|
510
545
|
if (currentRootElement) currentRootElement.classList.remove(...classNames);
|
|
511
546
|
};
|
|
512
547
|
}
|
|
548
|
+
registerRootListener(listener) {
|
|
549
|
+
if (!this.editor || this.headlessEditor) return noop;
|
|
550
|
+
return this.editor.registerRootListener(listener);
|
|
551
|
+
}
|
|
552
|
+
getEditorId() {
|
|
553
|
+
return this.themes[EDITOR_THEME_KEY];
|
|
554
|
+
}
|
|
555
|
+
registerRootLifecycle(editor) {
|
|
556
|
+
this.unregisterRootLifecycle?.();
|
|
557
|
+
let rootEventCleanup;
|
|
558
|
+
const clearRootEvent = () => {
|
|
559
|
+
rootEventCleanup?.();
|
|
560
|
+
rootEventCleanup = void 0;
|
|
561
|
+
};
|
|
562
|
+
const unregisterRootListener = editor.registerRootListener((rootElement) => {
|
|
563
|
+
clearRootEvent();
|
|
564
|
+
if (!rootElement) {
|
|
565
|
+
unregisterEditorKernel(this.getEditorId());
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
registerEditorKernel(this.getEditorId(), this);
|
|
569
|
+
rootEventCleanup = registerEvent(editor, rootElement);
|
|
570
|
+
});
|
|
571
|
+
this.unregisterRootLifecycle = () => {
|
|
572
|
+
clearRootEvent();
|
|
573
|
+
unregisterRootListener();
|
|
574
|
+
};
|
|
575
|
+
}
|
|
513
576
|
runBeforeEditorInitLifecycle() {
|
|
514
577
|
for (const hook of this.beforeEditorInitHooks) hook();
|
|
515
578
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CAN_USE_DOM } from "../../common/canUseDOM.js";
|
|
2
2
|
import { Suspense, useEffect, useLayoutEffect, useMemo, useState } from "react";
|
|
3
3
|
import { jsx } from "react/jsx-runtime";
|
|
4
|
-
import { createPortal
|
|
4
|
+
import { createPortal } from "react-dom";
|
|
5
5
|
//#region src/editor-kernel/react/useDecorators.tsx
|
|
6
6
|
const useLayoutEffectImpl = CAN_USE_DOM ? useLayoutEffect : useEffect;
|
|
7
7
|
function useDecorators(editor, ErrorBoundary) {
|
|
@@ -12,9 +12,7 @@ function useDecorators(editor, ErrorBoundary) {
|
|
|
12
12
|
const initialDecorators = editor.getDecorators();
|
|
13
13
|
setDecorators(initialDecorators);
|
|
14
14
|
clears.push(editor.registerDecoratorListener((nextDecorators) => {
|
|
15
|
-
|
|
16
|
-
setDecorators(nextDecorators);
|
|
17
|
-
});
|
|
15
|
+
setDecorators(nextDecorators);
|
|
18
16
|
}));
|
|
19
17
|
};
|
|
20
18
|
const lexicalEditor = editor.getLexicalEditor();
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { INodeHelper, init_helper } from "../editor-kernel/inode/helper.js";
|
|
2
|
+
import { KernelPlugin, init_plugin } from "../editor-kernel/plugin.js";
|
|
3
|
+
import { ILitexmlService } from "../plugins/litexml/service/litexml-service.js";
|
|
4
|
+
import { IMarkdownShortCutService } from "../plugins/markdown/service/shortcut.js";
|
|
5
|
+
import { $isCollapsibleNode, CollapsibleNode } from "../plugins/collapsible/node/CollapsibleNode.js";
|
|
6
|
+
//#region src/headless/collapsible-plugin.ts
|
|
7
|
+
init_helper();
|
|
8
|
+
init_plugin();
|
|
9
|
+
const HeadlessCollapsiblePlugin = class extends KernelPlugin {
|
|
10
|
+
static {
|
|
11
|
+
this.pluginName = "HeadlessCollapsiblePlugin";
|
|
12
|
+
}
|
|
13
|
+
constructor(kernel) {
|
|
14
|
+
super();
|
|
15
|
+
this.kernel = kernel;
|
|
16
|
+
kernel.registerNodes([CollapsibleNode]);
|
|
17
|
+
}
|
|
18
|
+
onInit(_editor) {
|
|
19
|
+
const markdownService = this.kernel.requireService(IMarkdownShortCutService);
|
|
20
|
+
markdownService?.registerMarkdownWriter(CollapsibleNode.getType(), (ctx, node) => {
|
|
21
|
+
if (!$isCollapsibleNode(node)) return false;
|
|
22
|
+
ctx.appendLine(`<details${node.isCollapsed() ? "" : " open"}>\n`);
|
|
23
|
+
ctx.appendLine(`<summary>${escapeHtml(node.getTitle())}</summary>\n\n`);
|
|
24
|
+
node.getChildren().slice(1).forEach((child) => ctx.processChild(ctx, child));
|
|
25
|
+
ctx.appendLine("\n</details>\n");
|
|
26
|
+
return true;
|
|
27
|
+
});
|
|
28
|
+
markdownService?.registerMarkdownReader("html", (node, children) => {
|
|
29
|
+
const tag = getHtmlTagName(node.value);
|
|
30
|
+
if (tag === "summary") return INodeHelper.createTypeNode("__collapsible_summary", { text: getNodeText(children) });
|
|
31
|
+
if (tag !== "details") return false;
|
|
32
|
+
const summaryNode = children.find((child) => child.type === "__collapsible_summary");
|
|
33
|
+
const contentChildren = children.filter((child) => child.type !== "__collapsible_summary");
|
|
34
|
+
const title = summaryNode && "text" in summaryNode && typeof summaryNode.text === "string" ? summaryNode.text : extractSummaryTitle(node.value) || "Details";
|
|
35
|
+
return INodeHelper.createElementNode(CollapsibleNode.getType(), {
|
|
36
|
+
children: ensureTitleChildren(contentChildren, title),
|
|
37
|
+
collapsed: !/\sopen(?:\s|>|$)/i.test(node.value),
|
|
38
|
+
title
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
const litexmlService = this.kernel.requireService(ILitexmlService);
|
|
42
|
+
litexmlService?.registerXMLWriter(CollapsibleNode.getType(), (node, ctx) => {
|
|
43
|
+
if (!$isCollapsibleNode(node)) return false;
|
|
44
|
+
return ctx.createXmlNode("collapsible", {
|
|
45
|
+
collapsed: String(node.isCollapsed()),
|
|
46
|
+
title: node.getTitle()
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
litexmlService?.registerXMLReader("collapsible", (xmlNode, children) => {
|
|
50
|
+
const title = xmlNode.getAttribute("title") || "Details";
|
|
51
|
+
return INodeHelper.createElementNode(CollapsibleNode.getType(), {
|
|
52
|
+
children: ensureTitleChildren(children, title),
|
|
53
|
+
collapsed: xmlNode.getAttribute("collapsed") === "true",
|
|
54
|
+
title
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
const getHtmlTagName = (value) => value.match(/^<\/?\s*([\da-z-]+)/i)?.[1]?.toLowerCase() || "";
|
|
60
|
+
const escapeHtml = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("\"", """);
|
|
61
|
+
const extractSummaryTitle = (value) => value.match(/<summary\b[^>]*>([\S\s]*?)<\/summary>/i)?.[1]?.trim() || "";
|
|
62
|
+
const getNodeText = (children) => children.map((child) => {
|
|
63
|
+
if (!child || typeof child !== "object") return "";
|
|
64
|
+
if ("text" in child && typeof child.text === "string") return child.text;
|
|
65
|
+
if ("children" in child && Array.isArray(child.children)) return getNodeText(child.children);
|
|
66
|
+
return "";
|
|
67
|
+
}).join("");
|
|
68
|
+
const ensureTitleChildren = (children, title) => {
|
|
69
|
+
const trimmedTitle = title.trim();
|
|
70
|
+
if (!trimmedTitle || getNodeText(children.slice(0, 1)).trim() === trimmedTitle) return children;
|
|
71
|
+
return [INodeHelper.createParagraph({ children: [INodeHelper.createTextNode(title)] }), ...children];
|
|
72
|
+
};
|
|
73
|
+
//#endregion
|
|
74
|
+
export { HeadlessCollapsiblePlugin };
|
package/es/headless/index.js
CHANGED
|
@@ -15,6 +15,7 @@ import { ListPlugin } from "../plugins/list/plugin/index.js";
|
|
|
15
15
|
import { MathPlugin } from "../plugins/math/plugin/index.js";
|
|
16
16
|
import { MentionPlugin } from "../plugins/mention/plugin/index.js";
|
|
17
17
|
import { TablePlugin } from "../plugins/table/plugin/index.js";
|
|
18
|
+
import { HeadlessCollapsiblePlugin } from "./collapsible-plugin.js";
|
|
18
19
|
import { extractMediaFromEditorState } from "./extract-media-from-editor-state.js";
|
|
19
20
|
//#region src/headless/index.ts
|
|
20
21
|
init_utils();
|
|
@@ -118,6 +119,7 @@ const DEFAULT_HEADLESS_EDITOR_PLUGINS = [
|
|
|
118
119
|
HRPlugin,
|
|
119
120
|
ListPlugin,
|
|
120
121
|
TablePlugin,
|
|
122
|
+
HeadlessCollapsiblePlugin,
|
|
121
123
|
LitexmlPlugin
|
|
122
124
|
];
|
|
123
125
|
var HeadlessEditor = class {
|
package/es/headless.d.ts
CHANGED
|
@@ -79,6 +79,12 @@ declare const _default: {
|
|
|
79
79
|
replace: string;
|
|
80
80
|
};
|
|
81
81
|
link: {
|
|
82
|
+
copy: string;
|
|
83
|
+
copySuccess: string;
|
|
84
|
+
convertToCard: string;
|
|
85
|
+
convertToIframe: string;
|
|
86
|
+
convertToLink: string;
|
|
87
|
+
convertToSchema: string;
|
|
82
88
|
edit: string;
|
|
83
89
|
editLinkTitle: string;
|
|
84
90
|
editTextTitle: string;
|
|
@@ -112,6 +118,14 @@ declare const _default: {
|
|
|
112
118
|
insertRowAbove: string;
|
|
113
119
|
insertRowBelow: string;
|
|
114
120
|
};
|
|
121
|
+
toc: {
|
|
122
|
+
ariaLabel: string;
|
|
123
|
+
empty: string;
|
|
124
|
+
expand: string;
|
|
125
|
+
pin: string;
|
|
126
|
+
title: string;
|
|
127
|
+
unpin: string;
|
|
128
|
+
};
|
|
115
129
|
};
|
|
116
130
|
//#endregion
|
|
117
131
|
//#region src/types/locale.d.ts
|
|
@@ -343,6 +357,7 @@ interface IEditor {
|
|
|
343
357
|
* @param dom
|
|
344
358
|
*/
|
|
345
359
|
setRootElement(dom: HTMLElement, editable?: boolean): LexicalEditor;
|
|
360
|
+
setRootElement(dom: null, editable?: boolean): LexicalEditor | null;
|
|
346
361
|
/**
|
|
347
362
|
* set editor selection
|
|
348
363
|
* @param selection
|
|
@@ -421,6 +436,17 @@ interface IEditorKernel extends IEditor {
|
|
|
421
436
|
* @returns unregister function
|
|
422
437
|
*/
|
|
423
438
|
registerRootClassName(className: string): () => void;
|
|
439
|
+
/**
|
|
440
|
+
* Register a callback for root attach/detach events.
|
|
441
|
+
*
|
|
442
|
+
* Call this after the kernel's Lexical editor has been initialized (usually
|
|
443
|
+
* from plugin onInit). Lexical immediately invokes a newly registered root
|
|
444
|
+
* listener with the current root, so listeners registered after the initial
|
|
445
|
+
* setRootElement still receive the first root attach. Headless kernels
|
|
446
|
+
* return a no-op cleanup because headless Lexical editors do not support
|
|
447
|
+
* root listeners.
|
|
448
|
+
*/
|
|
449
|
+
registerRootListener(listener: (rootElement: HTMLElement | null) => void): () => void;
|
|
424
450
|
/**
|
|
425
451
|
* Register service
|
|
426
452
|
* @param serviceId
|
|
@@ -458,6 +484,10 @@ interface IEditorPlugin<IConfig> {
|
|
|
458
484
|
* Editor destruction
|
|
459
485
|
*/
|
|
460
486
|
destroy(): void;
|
|
487
|
+
/**
|
|
488
|
+
* After editor document content is written by setDocument
|
|
489
|
+
*/
|
|
490
|
+
onDocumentChange?(type: string, content: any, options?: IDocumentOptions): void;
|
|
461
491
|
/**
|
|
462
492
|
* After Lexical editor instantiation
|
|
463
493
|
* @param editor Lexical editor instance
|