@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
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { IServiceID } from "../../../types/kernel.js";
|
|
2
|
+
import "../../../types/index.js";
|
|
3
|
+
import { Doc } from "yjs";
|
|
4
|
+
import { Binding, Provider, UserState } from "@lexical/yjs";
|
|
5
|
+
//#region src/plugins/yjs/service/index.d.ts
|
|
6
|
+
interface YjsAwarenessUser {
|
|
7
|
+
clientId: number;
|
|
8
|
+
state: UserState;
|
|
9
|
+
}
|
|
10
|
+
interface YjsPluginState {
|
|
11
|
+
binding: Binding;
|
|
12
|
+
doc: Doc | undefined;
|
|
13
|
+
docMap: Map<string, Doc>;
|
|
14
|
+
id: string;
|
|
15
|
+
provider: Provider;
|
|
16
|
+
}
|
|
17
|
+
type YjsPluginStateListener = (state: YjsPluginState | null) => void;
|
|
18
|
+
type YjsAwarenessUsersListener = (users: YjsAwarenessUser[]) => void;
|
|
19
|
+
declare class YjsService {
|
|
20
|
+
private awarenessUsers;
|
|
21
|
+
private awarenessUsersListeners;
|
|
22
|
+
private listeners;
|
|
23
|
+
private state;
|
|
24
|
+
getAwarenessUsers(): YjsAwarenessUser[];
|
|
25
|
+
getState(): YjsPluginState | null;
|
|
26
|
+
/**
|
|
27
|
+
* Apply a server-produced editor snapshot to the shared document exactly once.
|
|
28
|
+
*
|
|
29
|
+
* A normal `IEditor.setDocument` call replaces the local Lexical state without
|
|
30
|
+
* knowing about the Yjs binding. When the binding sees that state it can treat
|
|
31
|
+
* every imported node as a new local node and append a second copy to the
|
|
32
|
+
* shared tree. This path first hydrates from the shared tree, then applies the
|
|
33
|
+
* snapshot with the collaboration-skip tag and replaces the shared root in a
|
|
34
|
+
* single Yjs transaction. A second client receiving the same snapshot
|
|
35
|
+
* therefore becomes a no-op.
|
|
36
|
+
*
|
|
37
|
+
* @returns whether the shared document was changed
|
|
38
|
+
*/
|
|
39
|
+
applyExternalEditorData(editorData: Record<string, unknown>): boolean;
|
|
40
|
+
setAwarenessUsers(users: YjsAwarenessUser[]): void;
|
|
41
|
+
setState(state: YjsPluginState | null): void;
|
|
42
|
+
subscribeAwarenessUsers(listener: YjsAwarenessUsersListener): () => void;
|
|
43
|
+
subscribe(listener: YjsPluginStateListener): () => void;
|
|
44
|
+
}
|
|
45
|
+
declare const IYjsService: IServiceID<YjsService>;
|
|
46
|
+
//#endregion
|
|
47
|
+
export { IYjsService, YjsAwarenessUser, YjsPluginState, YjsService };
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { createEmptyPreviousEditorState } from "../plugin/utils/editor-state.js";
|
|
2
|
+
import { hydrateLexicalFromYjsState, syncCurrentEditorStateToYjs } from "../plugin/utils/sync.js";
|
|
3
|
+
import { SKIP_COLLAB_TAG } from "lexical";
|
|
4
|
+
//#region src/plugins/yjs/service/index.ts
|
|
5
|
+
const serializeComparableEditorState = (state) => {
|
|
6
|
+
const serialized = state.toJSON();
|
|
7
|
+
if (serialized.root?.direction === "ltr") serialized.root.direction = null;
|
|
8
|
+
return JSON.stringify(serialized);
|
|
9
|
+
};
|
|
10
|
+
var YjsService = class {
|
|
11
|
+
constructor() {
|
|
12
|
+
this.awarenessUsers = [];
|
|
13
|
+
this.awarenessUsersListeners = /* @__PURE__ */ new Set();
|
|
14
|
+
this.listeners = /* @__PURE__ */ new Set();
|
|
15
|
+
this.state = null;
|
|
16
|
+
}
|
|
17
|
+
getAwarenessUsers() {
|
|
18
|
+
return this.awarenessUsers;
|
|
19
|
+
}
|
|
20
|
+
getState() {
|
|
21
|
+
return this.state;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Apply a server-produced editor snapshot to the shared document exactly once.
|
|
25
|
+
*
|
|
26
|
+
* A normal `IEditor.setDocument` call replaces the local Lexical state without
|
|
27
|
+
* knowing about the Yjs binding. When the binding sees that state it can treat
|
|
28
|
+
* every imported node as a new local node and append a second copy to the
|
|
29
|
+
* shared tree. This path first hydrates from the shared tree, then applies the
|
|
30
|
+
* snapshot with the collaboration-skip tag and replaces the shared root in a
|
|
31
|
+
* single Yjs transaction. A second client receiving the same snapshot
|
|
32
|
+
* therefore becomes a no-op.
|
|
33
|
+
*
|
|
34
|
+
* @returns whether the shared document was changed
|
|
35
|
+
*/
|
|
36
|
+
applyExternalEditorData(editorData) {
|
|
37
|
+
const state = this.state;
|
|
38
|
+
if (!state) return false;
|
|
39
|
+
const { binding, provider } = state;
|
|
40
|
+
const hasSharedState = !binding.root.isEmpty() || binding.root._xmlText._length > 0;
|
|
41
|
+
if (hasSharedState && binding.root.isEmpty()) hydrateLexicalFromYjsState(binding, { discrete: true });
|
|
42
|
+
const previousEditorState = binding.editor.getEditorState();
|
|
43
|
+
const nextEditorState = binding.editor.parseEditorState(JSON.stringify(editorData));
|
|
44
|
+
const isSameState = serializeComparableEditorState(previousEditorState) === serializeComparableEditorState(nextEditorState);
|
|
45
|
+
if (hasSharedState && isSameState) return false;
|
|
46
|
+
binding.editor.setEditorState(nextEditorState, { tag: SKIP_COLLAB_TAG });
|
|
47
|
+
const replaceSharedState = () => {
|
|
48
|
+
const collabRoot = binding.root;
|
|
49
|
+
const sharedType = binding.root.getSharedType();
|
|
50
|
+
if (sharedType.length > 0) sharedType.delete(0, sharedType.length);
|
|
51
|
+
collabRoot._children.length = 0;
|
|
52
|
+
binding.collabNodeMap.clear();
|
|
53
|
+
syncCurrentEditorStateToYjs(binding, provider, createEmptyPreviousEditorState(binding.editor));
|
|
54
|
+
};
|
|
55
|
+
if (state.doc) state.doc.transact(replaceSharedState, binding);
|
|
56
|
+
else replaceSharedState();
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
setAwarenessUsers(users) {
|
|
60
|
+
this.awarenessUsers = users;
|
|
61
|
+
this.awarenessUsersListeners.forEach((listener) => listener(users));
|
|
62
|
+
}
|
|
63
|
+
setState(state) {
|
|
64
|
+
this.state = state;
|
|
65
|
+
this.listeners.forEach((listener) => listener(state));
|
|
66
|
+
}
|
|
67
|
+
subscribeAwarenessUsers(listener) {
|
|
68
|
+
this.awarenessUsersListeners.add(listener);
|
|
69
|
+
listener(this.awarenessUsers);
|
|
70
|
+
return () => {
|
|
71
|
+
this.awarenessUsersListeners.delete(listener);
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
subscribe(listener) {
|
|
75
|
+
this.listeners.add(listener);
|
|
76
|
+
listener(this.state);
|
|
77
|
+
return () => {
|
|
78
|
+
this.listeners.delete(listener);
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
const IYjsService = { __serviceId: "YjsService" };
|
|
83
|
+
//#endregion
|
|
84
|
+
export { IYjsService, YjsService };
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Doc } from "yjs";
|
|
2
|
+
import { Provider, ProviderAwareness, UserState } from "@lexical/yjs";
|
|
3
|
+
//#region src/plugins/yjs/websocket-provider.d.ts
|
|
4
|
+
interface CreateWebSocketYjsProviderOptions {
|
|
5
|
+
wsBaseUrl?: string;
|
|
6
|
+
}
|
|
7
|
+
type WebSocketYjsProviderStatus = 'connected' | 'connecting' | 'disconnected' | 'reconnecting';
|
|
8
|
+
type ProviderEventMap = {
|
|
9
|
+
reload: (doc: Doc) => void;
|
|
10
|
+
status: (event: {
|
|
11
|
+
status: WebSocketYjsProviderStatus;
|
|
12
|
+
}) => void;
|
|
13
|
+
sync: (isSynced: boolean) => void;
|
|
14
|
+
update: (event: unknown) => void;
|
|
15
|
+
};
|
|
16
|
+
type AwarenessMessage = {
|
|
17
|
+
sender: number;
|
|
18
|
+
state: UserState | null;
|
|
19
|
+
type: 'awareness';
|
|
20
|
+
};
|
|
21
|
+
type UpdateMessage = {
|
|
22
|
+
sender: number;
|
|
23
|
+
type: 'update';
|
|
24
|
+
update: string;
|
|
25
|
+
};
|
|
26
|
+
type ClientWebSocketMessage = AwarenessMessage | UpdateMessage | {
|
|
27
|
+
stateVector: string;
|
|
28
|
+
type: 'sync-request';
|
|
29
|
+
};
|
|
30
|
+
declare class WebSocketAwareness implements ProviderAwareness {
|
|
31
|
+
private clientId;
|
|
32
|
+
private send;
|
|
33
|
+
private listeners;
|
|
34
|
+
private localState;
|
|
35
|
+
private states;
|
|
36
|
+
constructor(clientId: number, send: (message: ClientWebSocketMessage) => void);
|
|
37
|
+
getLocalState(): UserState | null;
|
|
38
|
+
getStates(): Map<number, UserState>;
|
|
39
|
+
off(type: 'update', cb: () => void): void;
|
|
40
|
+
on(type: 'update', cb: () => void): void;
|
|
41
|
+
setLocalState(state: UserState | null): void;
|
|
42
|
+
setLocalStateField(field: string, value: unknown): void;
|
|
43
|
+
updateRemoteState(clientId: number, state: UserState | null): void;
|
|
44
|
+
private emitUpdate;
|
|
45
|
+
}
|
|
46
|
+
declare class WebSocketYjsProvider implements Provider {
|
|
47
|
+
private id;
|
|
48
|
+
private doc;
|
|
49
|
+
private wsBaseUrl;
|
|
50
|
+
awareness: WebSocketAwareness;
|
|
51
|
+
private listeners;
|
|
52
|
+
private reconnectAttempt;
|
|
53
|
+
private reconnectTimer;
|
|
54
|
+
private shouldConnect;
|
|
55
|
+
private socket;
|
|
56
|
+
private updateHandler;
|
|
57
|
+
constructor(id: string, doc: Doc, wsBaseUrl?: string);
|
|
58
|
+
connect(): void;
|
|
59
|
+
disconnect(): void;
|
|
60
|
+
off<T extends keyof ProviderEventMap>(type: T, cb: ProviderEventMap[T]): void;
|
|
61
|
+
on<T extends keyof ProviderEventMap>(type: T, cb: ProviderEventMap[T]): void;
|
|
62
|
+
private clearReconnectTimer;
|
|
63
|
+
private emit;
|
|
64
|
+
private getReconnectDelay;
|
|
65
|
+
private openSocket;
|
|
66
|
+
private publishLocalDocumentState;
|
|
67
|
+
private requestServerDocumentState;
|
|
68
|
+
private scheduleReconnect;
|
|
69
|
+
private handleClose;
|
|
70
|
+
private handleMessage;
|
|
71
|
+
private send;
|
|
72
|
+
}
|
|
73
|
+
declare function createWebSocketYjsProvider(id: string, yjsDocMap: Map<string, Doc>, options?: CreateWebSocketYjsProviderOptions): WebSocketYjsProvider;
|
|
74
|
+
declare function fetchWebSocketDemoDocument(id: string): Promise<unknown>;
|
|
75
|
+
declare function saveWebSocketDemoDocument(id: string, content: unknown): Promise<void>;
|
|
76
|
+
declare function snapshotWebSocketDemoDocument(id: string, content: unknown): void;
|
|
77
|
+
//#endregion
|
|
78
|
+
export { CreateWebSocketYjsProviderOptions, WebSocketYjsProvider, WebSocketYjsProviderStatus, createWebSocketYjsProvider, fetchWebSocketDemoDocument, saveWebSocketDemoDocument, snapshotWebSocketDemoDocument };
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import { Doc, applyUpdate, encodeStateAsUpdate, encodeStateVector } from "yjs";
|
|
2
|
+
//#region src/plugins/yjs/websocket-provider.ts
|
|
3
|
+
const DEFAULT_HTTP_BASE_URL = "http://localhost:12345";
|
|
4
|
+
const DEFAULT_WS_BASE_URL = "ws://localhost:12345";
|
|
5
|
+
const MAX_RECONNECT_DELAY_MS = 1e4;
|
|
6
|
+
const MIN_RECONNECT_DELAY_MS = 500;
|
|
7
|
+
function encodeUint8Array(update) {
|
|
8
|
+
let binary = "";
|
|
9
|
+
const chunkSize = 8192;
|
|
10
|
+
for (let index = 0; index < update.length; index += chunkSize) binary += String.fromCharCode(...update.slice(index, index + chunkSize));
|
|
11
|
+
return window.btoa(binary);
|
|
12
|
+
}
|
|
13
|
+
function decodeUint8Array(value) {
|
|
14
|
+
const binary = window.atob(value);
|
|
15
|
+
const update = new Uint8Array(binary.length);
|
|
16
|
+
for (let index = 0; index < binary.length; index += 1) update[index] = binary.charCodeAt(index);
|
|
17
|
+
return update;
|
|
18
|
+
}
|
|
19
|
+
var WebSocketAwareness = class {
|
|
20
|
+
constructor(clientId, send) {
|
|
21
|
+
this.clientId = clientId;
|
|
22
|
+
this.send = send;
|
|
23
|
+
this.listeners = /* @__PURE__ */ new Set();
|
|
24
|
+
this.localState = null;
|
|
25
|
+
this.states = /* @__PURE__ */ new Map();
|
|
26
|
+
}
|
|
27
|
+
getLocalState() {
|
|
28
|
+
return this.localState;
|
|
29
|
+
}
|
|
30
|
+
getStates() {
|
|
31
|
+
return this.states;
|
|
32
|
+
}
|
|
33
|
+
off(type, cb) {
|
|
34
|
+
if (type === "update") this.listeners.delete(cb);
|
|
35
|
+
}
|
|
36
|
+
on(type, cb) {
|
|
37
|
+
if (type === "update") this.listeners.add(cb);
|
|
38
|
+
}
|
|
39
|
+
setLocalState(state) {
|
|
40
|
+
this.localState = state;
|
|
41
|
+
if (state) this.states.set(this.clientId, state);
|
|
42
|
+
else this.states.delete(this.clientId);
|
|
43
|
+
this.emitUpdate();
|
|
44
|
+
this.send({
|
|
45
|
+
sender: this.clientId,
|
|
46
|
+
state,
|
|
47
|
+
type: "awareness"
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
setLocalStateField(field, value) {
|
|
51
|
+
const nextState = {
|
|
52
|
+
...this.localState,
|
|
53
|
+
[field]: value
|
|
54
|
+
};
|
|
55
|
+
this.setLocalState(nextState);
|
|
56
|
+
}
|
|
57
|
+
updateRemoteState(clientId, state) {
|
|
58
|
+
if (clientId === this.clientId) return;
|
|
59
|
+
if (state) this.states.set(clientId, state);
|
|
60
|
+
else this.states.delete(clientId);
|
|
61
|
+
this.emitUpdate();
|
|
62
|
+
}
|
|
63
|
+
emitUpdate() {
|
|
64
|
+
this.listeners.forEach((listener) => listener());
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
var WebSocketYjsProvider = class {
|
|
68
|
+
constructor(id, doc, wsBaseUrl = DEFAULT_WS_BASE_URL) {
|
|
69
|
+
this.id = id;
|
|
70
|
+
this.doc = doc;
|
|
71
|
+
this.wsBaseUrl = wsBaseUrl;
|
|
72
|
+
this.listeners = {
|
|
73
|
+
reload: /* @__PURE__ */ new Set(),
|
|
74
|
+
status: /* @__PURE__ */ new Set(),
|
|
75
|
+
sync: /* @__PURE__ */ new Set(),
|
|
76
|
+
update: /* @__PURE__ */ new Set()
|
|
77
|
+
};
|
|
78
|
+
this.reconnectAttempt = 0;
|
|
79
|
+
this.reconnectTimer = null;
|
|
80
|
+
this.shouldConnect = false;
|
|
81
|
+
this.socket = null;
|
|
82
|
+
this.updateHandler = (update, origin) => {
|
|
83
|
+
if (origin === this) return;
|
|
84
|
+
this.send({
|
|
85
|
+
sender: this.doc.clientID,
|
|
86
|
+
type: "update",
|
|
87
|
+
update: encodeUint8Array(update)
|
|
88
|
+
});
|
|
89
|
+
};
|
|
90
|
+
this.awareness = new WebSocketAwareness(this.doc.clientID, (message) => this.send(message));
|
|
91
|
+
}
|
|
92
|
+
connect() {
|
|
93
|
+
this.shouldConnect = true;
|
|
94
|
+
if (this.socket || this.reconnectTimer) return;
|
|
95
|
+
this.openSocket("connecting");
|
|
96
|
+
}
|
|
97
|
+
disconnect() {
|
|
98
|
+
this.shouldConnect = false;
|
|
99
|
+
this.clearReconnectTimer();
|
|
100
|
+
if (!this.socket) return;
|
|
101
|
+
this.awareness.setLocalState(null);
|
|
102
|
+
this.doc.off("update", this.updateHandler);
|
|
103
|
+
this.socket.close();
|
|
104
|
+
this.socket = null;
|
|
105
|
+
this.emit("sync", false);
|
|
106
|
+
this.emit("status", { status: "disconnected" });
|
|
107
|
+
}
|
|
108
|
+
off(type, cb) {
|
|
109
|
+
this.listeners[type].delete(cb);
|
|
110
|
+
}
|
|
111
|
+
on(type, cb) {
|
|
112
|
+
this.listeners[type].add(cb);
|
|
113
|
+
}
|
|
114
|
+
clearReconnectTimer() {
|
|
115
|
+
if (!this.reconnectTimer) return;
|
|
116
|
+
clearTimeout(this.reconnectTimer);
|
|
117
|
+
this.reconnectTimer = null;
|
|
118
|
+
}
|
|
119
|
+
emit(type, ...args) {
|
|
120
|
+
this.listeners[type].forEach((listener) => {
|
|
121
|
+
listener(...args);
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
getReconnectDelay() {
|
|
125
|
+
const exponentialDelay = MIN_RECONNECT_DELAY_MS * 2 ** this.reconnectAttempt;
|
|
126
|
+
const jitter = Math.floor(Math.random() * MIN_RECONNECT_DELAY_MS);
|
|
127
|
+
return Math.min(exponentialDelay + jitter, MAX_RECONNECT_DELAY_MS);
|
|
128
|
+
}
|
|
129
|
+
openSocket(status) {
|
|
130
|
+
const socket = new WebSocket(`${this.wsBaseUrl}/collaboration/${encodeURIComponent(this.id)}?clientId=${this.doc.clientID}`);
|
|
131
|
+
this.socket = socket;
|
|
132
|
+
socket.binaryType = "arraybuffer";
|
|
133
|
+
this.emit("status", { status });
|
|
134
|
+
socket.addEventListener("open", () => {
|
|
135
|
+
if (this.socket !== socket) {
|
|
136
|
+
socket.close();
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
this.reconnectAttempt = 0;
|
|
140
|
+
this.doc.on("update", this.updateHandler);
|
|
141
|
+
this.emit("status", { status: "connected" });
|
|
142
|
+
this.requestServerDocumentState();
|
|
143
|
+
const localState = this.awareness.getLocalState();
|
|
144
|
+
if (localState) this.send({
|
|
145
|
+
sender: this.doc.clientID,
|
|
146
|
+
state: localState,
|
|
147
|
+
type: "awareness"
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
socket.addEventListener("message", (event) => this.handleMessage(socket, event));
|
|
151
|
+
socket.addEventListener("close", () => this.handleClose(socket));
|
|
152
|
+
socket.addEventListener("error", () => {
|
|
153
|
+
this.emit("status", { status: "disconnected" });
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
publishLocalDocumentState() {
|
|
157
|
+
this.send({
|
|
158
|
+
sender: this.doc.clientID,
|
|
159
|
+
type: "update",
|
|
160
|
+
update: encodeUint8Array(encodeStateAsUpdate(this.doc))
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
requestServerDocumentState() {
|
|
164
|
+
this.send({
|
|
165
|
+
stateVector: encodeUint8Array(encodeStateVector(this.doc)),
|
|
166
|
+
type: "sync-request"
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
scheduleReconnect() {
|
|
170
|
+
if (!this.shouldConnect || this.reconnectTimer) return;
|
|
171
|
+
const delay = this.getReconnectDelay();
|
|
172
|
+
this.reconnectAttempt += 1;
|
|
173
|
+
this.emit("status", { status: "reconnecting" });
|
|
174
|
+
this.reconnectTimer = setTimeout(() => {
|
|
175
|
+
this.reconnectTimer = null;
|
|
176
|
+
this.openSocket("reconnecting");
|
|
177
|
+
}, delay);
|
|
178
|
+
}
|
|
179
|
+
handleClose(socket) {
|
|
180
|
+
if (this.socket !== socket) return;
|
|
181
|
+
this.doc.off("update", this.updateHandler);
|
|
182
|
+
this.socket = null;
|
|
183
|
+
this.emit("sync", false);
|
|
184
|
+
this.emit("status", { status: "disconnected" });
|
|
185
|
+
this.scheduleReconnect();
|
|
186
|
+
}
|
|
187
|
+
handleMessage(socket, event) {
|
|
188
|
+
if (this.socket !== socket) return;
|
|
189
|
+
let message;
|
|
190
|
+
try {
|
|
191
|
+
message = JSON.parse(event.data);
|
|
192
|
+
} catch {
|
|
193
|
+
this.emit("status", { status: "disconnected" });
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
if (message.type === "awareness") {
|
|
197
|
+
this.awareness.updateRemoteState(message.sender, message.state);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
if (message.type === "sync") {
|
|
201
|
+
try {
|
|
202
|
+
applyUpdate(this.doc, decodeUint8Array(message.update), this);
|
|
203
|
+
} catch {
|
|
204
|
+
this.emit("status", { status: "disconnected" });
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
for (const awareness of message.awareness) this.awareness.updateRemoteState(awareness.clientId, awareness.state);
|
|
208
|
+
this.emit("sync", true);
|
|
209
|
+
this.publishLocalDocumentState();
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
if (message.sender === this.doc.clientID) return;
|
|
213
|
+
try {
|
|
214
|
+
const update = decodeUint8Array(message.update);
|
|
215
|
+
applyUpdate(this.doc, update, this);
|
|
216
|
+
this.emit("update", update);
|
|
217
|
+
} catch {
|
|
218
|
+
this.emit("status", { status: "disconnected" });
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
send(message) {
|
|
222
|
+
if (this.socket?.readyState !== WebSocket.OPEN) return;
|
|
223
|
+
this.socket.send(JSON.stringify(message));
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
function createWebSocketYjsProvider(id, yjsDocMap, options = {}) {
|
|
227
|
+
const doc = yjsDocMap.get(id) || new Doc();
|
|
228
|
+
yjsDocMap.set(id, doc);
|
|
229
|
+
return new WebSocketYjsProvider(id, doc, options.wsBaseUrl);
|
|
230
|
+
}
|
|
231
|
+
async function fetchWebSocketDemoDocument(id) {
|
|
232
|
+
const response = await fetch(`${DEFAULT_HTTP_BASE_URL}/documents/${encodeURIComponent(id)}`);
|
|
233
|
+
if (!response.ok) throw new Error(`Failed to load document: ${response.status}`);
|
|
234
|
+
const { content } = await response.json();
|
|
235
|
+
return content;
|
|
236
|
+
}
|
|
237
|
+
async function postWebSocketDemoDocument(id, path, content) {
|
|
238
|
+
const response = await fetch(`${DEFAULT_HTTP_BASE_URL}/documents/${encodeURIComponent(id)}/${path}`, {
|
|
239
|
+
body: JSON.stringify({ content }),
|
|
240
|
+
headers: { "Content-Type": "application/json" },
|
|
241
|
+
method: "POST"
|
|
242
|
+
});
|
|
243
|
+
if (!response.ok) throw new Error(`Failed to ${path} document: ${response.status}`);
|
|
244
|
+
}
|
|
245
|
+
async function saveWebSocketDemoDocument(id, content) {
|
|
246
|
+
await postWebSocketDemoDocument(id, "save", content);
|
|
247
|
+
}
|
|
248
|
+
function snapshotWebSocketDemoDocument(id, content) {
|
|
249
|
+
const url = `${DEFAULT_HTTP_BASE_URL}/documents/${encodeURIComponent(id)}/snapshot`;
|
|
250
|
+
const body = JSON.stringify({ content });
|
|
251
|
+
if (typeof navigator !== "undefined" && navigator.sendBeacon) {
|
|
252
|
+
const blob = new Blob([body], { type: "text/plain;charset=UTF-8" });
|
|
253
|
+
if (navigator.sendBeacon(url, blob)) return;
|
|
254
|
+
}
|
|
255
|
+
fetch(url, {
|
|
256
|
+
body,
|
|
257
|
+
headers: { "Content-Type": "application/json" },
|
|
258
|
+
keepalive: true,
|
|
259
|
+
method: "POST"
|
|
260
|
+
}).catch(() => {});
|
|
261
|
+
}
|
|
262
|
+
//#endregion
|
|
263
|
+
export { WebSocketYjsProvider, createWebSocketYjsProvider, fetchWebSocketDemoDocument, saveWebSocketDemoDocument, snapshotWebSocketDemoDocument };
|
|
@@ -1,6 +1,28 @@
|
|
|
1
1
|
import { IEditor } from "../../types/kernel.js";
|
|
2
2
|
import "../../types/index.js";
|
|
3
3
|
//#region src/react/hooks/useEditor.d.ts
|
|
4
|
-
|
|
4
|
+
interface UseEditorOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Destroy the kernel when the owning component is cleaned up.
|
|
7
|
+
*
|
|
8
|
+
* The default is false because React Activity also runs effect cleanup while
|
|
9
|
+
* hiding a subtree. Detaching the root keeps the editor reversible and lets
|
|
10
|
+
* the same kernel be attached again when the subtree becomes visible.
|
|
11
|
+
*
|
|
12
|
+
* `autoDestroy: true` is mutually exclusive with React Activity: Activity
|
|
13
|
+
* cannot distinguish a hidden subtree from a permanently unmounted owner,
|
|
14
|
+
* so its cleanup would destroy the editor during a hide. Use the default
|
|
15
|
+
* reversible detach with Activity, and call `destroy()` (or unmount the
|
|
16
|
+
* owner with `autoDestroy`) only when the instance is no longer needed.
|
|
17
|
+
*
|
|
18
|
+
* Root detach releases the kernel from the library's global root registry
|
|
19
|
+
* and DOM/dragon listeners. It does not guarantee garbage collection when
|
|
20
|
+
* application code keeps the `IEditor` in a store, provider, or other
|
|
21
|
+
* long-lived reference. Remove that reference, unmount the owner, or use
|
|
22
|
+
* `autoDestroy: true`/`destroy()` to release the editor's runtime resources.
|
|
23
|
+
*/
|
|
24
|
+
autoDestroy?: boolean;
|
|
25
|
+
}
|
|
26
|
+
declare const useEditor: ({ autoDestroy }?: UseEditorOptions) => IEditor;
|
|
5
27
|
//#endregion
|
|
6
|
-
export { useEditor };
|
|
28
|
+
export { UseEditorOptions, useEditor };
|
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
import Editor from "../../editor-kernel/index.js";
|
|
2
|
-
import { useMemo } from "react";
|
|
2
|
+
import { useEffect, useMemo } from "react";
|
|
3
3
|
//#region src/react/hooks/useEditor.ts
|
|
4
|
-
const useEditor = () => {
|
|
5
|
-
|
|
4
|
+
const useEditor = ({ autoDestroy = false } = {}) => {
|
|
5
|
+
const editor = useMemo(() => Editor.createEditor(), []);
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
return () => {
|
|
8
|
+
if (autoDestroy) {
|
|
9
|
+
editor.destroy();
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
if (editor.getLexicalEditor()) editor.setRootElement(null);
|
|
13
|
+
};
|
|
14
|
+
}, [autoDestroy, editor]);
|
|
15
|
+
return editor;
|
|
6
16
|
};
|
|
7
17
|
//#endregion
|
|
8
18
|
export { useEditor };
|
package/es/react.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ import "./react/ChatInputActions/index.js";
|
|
|
13
13
|
import { CodeLanguageSelectProps } from "./react/CodeLanguageSelect/type.js";
|
|
14
14
|
import CodeLanguageSelect from "./react/CodeLanguageSelect/CodeLanguageSelect.js";
|
|
15
15
|
import "./react/CodeLanguageSelect/index.js";
|
|
16
|
-
import { useEditor } from "./react/hooks/useEditor.js";
|
|
16
|
+
import { UseEditorOptions, useEditor } from "./react/hooks/useEditor.js";
|
|
17
17
|
import { EditorState, useEditorState } from "./react/hooks/useEditorState/index.js";
|
|
18
18
|
import { EditorProps } from "./react/Editor/type.js";
|
|
19
19
|
import { withProps } from "./react/Editor/utils.js";
|
|
@@ -25,4 +25,4 @@ import "./react/FloatActions/index.js";
|
|
|
25
25
|
import { SendButtonProps } from "./react/SendButton/type.js";
|
|
26
26
|
import SendButton from "./react/SendButton/SendButton.js";
|
|
27
27
|
import "./react/SendButton/index.js";
|
|
28
|
-
export { ANCHOR_PADDING_CSS_VAR, ChatInput, ChatInputActionBar, type ChatInputActionBarProps, type ChatInputActionEvent, ChatInputActions, type ChatInputActionsProps, type ChatInputProps, CodeLanguageSelect, type CodeLanguageSelectProps, type CollaborationProviderFactory, DEFAULT_BLOCK_ANCHOR_PADDING, Editor, type EditorCollaborationConfig, type EditorProps, EditorProvider, type EditorProviderConfig, type EditorProviderProps, type EditorState, FloatActions, type FloatActionsProps, SendButton, type SendButtonProps, useEditor, useEditorContent, useEditorState, withProps };
|
|
28
|
+
export { ANCHOR_PADDING_CSS_VAR, ChatInput, ChatInputActionBar, type ChatInputActionBarProps, type ChatInputActionEvent, ChatInputActions, type ChatInputActionsProps, type ChatInputProps, CodeLanguageSelect, type CodeLanguageSelectProps, type CollaborationProviderFactory, DEFAULT_BLOCK_ANCHOR_PADDING, Editor, type EditorCollaborationConfig, type EditorProps, EditorProvider, type EditorProviderConfig, type EditorProviderProps, type EditorState, FloatActions, type FloatActionsProps, SendButton, type SendButtonProps, type UseEditorOptions, useEditor, useEditorContent, useEditorState, withProps };
|
|
@@ -2,11 +2,14 @@ import { getCSSVariables, getRendererClassName } from "./style.js";
|
|
|
2
2
|
import { renderNode } from "./engine/render-tree.js";
|
|
3
3
|
import { rendererNodes } from "./nodes/index.js";
|
|
4
4
|
import { createDefaultRenderers } from "./renderers/index.js";
|
|
5
|
-
import {
|
|
5
|
+
import { MotionComponent, MotionProvider } from "@lobehub/ui";
|
|
6
|
+
import { createElement, use, useMemo } from "react";
|
|
6
7
|
import { createHeadlessEditor } from "@lexical/headless";
|
|
7
8
|
import { $getRoot } from "lexical";
|
|
9
|
+
import { motion } from "motion/react";
|
|
8
10
|
//#region src/renderer/LexicalRenderer.tsx
|
|
9
11
|
function LexicalRenderer({ value, overrides, extraNodes, renderContext, as: Tag = "div", className, style, variant }) {
|
|
12
|
+
const inheritedMotion = use(MotionComponent);
|
|
10
13
|
const content = useMemo(() => {
|
|
11
14
|
const nodes = extraNodes ? [...rendererNodes, ...extraNodes] : rendererNodes;
|
|
12
15
|
const registry = createDefaultRenderers();
|
|
@@ -36,13 +39,17 @@ function LexicalRenderer({ value, overrides, extraNodes, renderContext, as: Tag
|
|
|
36
39
|
variant
|
|
37
40
|
]);
|
|
38
41
|
const cssVars = getCSSVariables(variant);
|
|
39
|
-
|
|
42
|
+
const tree = createElement(Tag, {
|
|
40
43
|
className: getRendererClassName(className),
|
|
41
44
|
style: {
|
|
42
45
|
...cssVars,
|
|
43
46
|
...style
|
|
44
47
|
}
|
|
45
48
|
}, createElement("div", null, content));
|
|
49
|
+
return inheritedMotion ? tree : createElement(MotionProvider, {
|
|
50
|
+
children: tree,
|
|
51
|
+
motion
|
|
52
|
+
});
|
|
46
53
|
}
|
|
47
54
|
//#endregion
|
|
48
55
|
export { LexicalRenderer };
|
package/es/types/kernel.d.ts
CHANGED
|
@@ -225,6 +225,7 @@ interface IEditor {
|
|
|
225
225
|
* @param dom
|
|
226
226
|
*/
|
|
227
227
|
setRootElement(dom: HTMLElement, editable?: boolean): LexicalEditor;
|
|
228
|
+
setRootElement(dom: null, editable?: boolean): LexicalEditor | null;
|
|
228
229
|
/**
|
|
229
230
|
* set editor selection
|
|
230
231
|
* @param selection
|
|
@@ -303,6 +304,17 @@ interface IEditorKernel extends IEditor {
|
|
|
303
304
|
* @returns unregister function
|
|
304
305
|
*/
|
|
305
306
|
registerRootClassName(className: string): () => void;
|
|
307
|
+
/**
|
|
308
|
+
* Register a callback for root attach/detach events.
|
|
309
|
+
*
|
|
310
|
+
* Call this after the kernel's Lexical editor has been initialized (usually
|
|
311
|
+
* from plugin onInit). Lexical immediately invokes a newly registered root
|
|
312
|
+
* listener with the current root, so listeners registered after the initial
|
|
313
|
+
* setRootElement still receive the first root attach. Headless kernels
|
|
314
|
+
* return a no-op cleanup because headless Lexical editors do not support
|
|
315
|
+
* root listeners.
|
|
316
|
+
*/
|
|
317
|
+
registerRootListener(listener: (rootElement: HTMLElement | null) => void): () => void;
|
|
306
318
|
/**
|
|
307
319
|
* Register service
|
|
308
320
|
* @param serviceId
|
|
@@ -340,6 +352,10 @@ interface IEditorPlugin<IConfig> {
|
|
|
340
352
|
* Editor destruction
|
|
341
353
|
*/
|
|
342
354
|
destroy(): void;
|
|
355
|
+
/**
|
|
356
|
+
* After editor document content is written by setDocument
|
|
357
|
+
*/
|
|
358
|
+
onDocumentChange?(type: string, content: any, options?: IDocumentOptions): void;
|
|
343
359
|
/**
|
|
344
360
|
* After Lexical editor instantiation
|
|
345
361
|
* @param editor Lexical editor instance
|
|
@@ -12,8 +12,11 @@ const updatePosition = ({ floating, placement = "bottom-start", reference, offse
|
|
|
12
12
|
}).then((props) => {
|
|
13
13
|
if (!floating) return false;
|
|
14
14
|
const { x, y } = props;
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
const viewportPadding = 8;
|
|
16
|
+
const viewportMinX = window.scrollX + viewportPadding;
|
|
17
|
+
const viewportMinY = window.scrollY + viewportPadding;
|
|
18
|
+
floating.style.left = `${Math.max(x, viewportMinX)}px`;
|
|
19
|
+
floating.style.top = `${Math.max(y, viewportMinY)}px`;
|
|
17
20
|
callback?.(props);
|
|
18
21
|
});
|
|
19
22
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/editor",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.25.1",
|
|
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",
|
|
@@ -62,6 +62,7 @@
|
|
|
62
62
|
"build:watch": "tsdown --watch",
|
|
63
63
|
"ci": "npm run lint:circular && npm run lint && npm run type-check",
|
|
64
64
|
"clean": "rm -r es lib dist coverage .react-router .eslintcache node_modules/.cache",
|
|
65
|
+
"demo:yjs-server": "node ./scripts/yjs-websocket-demo-server.cjs",
|
|
65
66
|
"dev": "lobedocs dev",
|
|
66
67
|
"docs:build": "lobedocs build",
|
|
67
68
|
"docs:build-analyze": "ANALYZE=1 lobedocs build",
|
|
@@ -138,6 +139,7 @@
|
|
|
138
139
|
"chalk": "^4.1.2"
|
|
139
140
|
},
|
|
140
141
|
"patchedDependencies": {
|
|
142
|
+
"@lexical/yjs@0.42.0": "patches/@lexical__yjs@0.42.0.patch",
|
|
141
143
|
"lexical@0.42.0": "patches/lexical@0.42.0.patch"
|
|
142
144
|
}
|
|
143
145
|
}
|