@lobehub/editor 4.24.1 → 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 (143) 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/types/kernel.d.ts +4 -0
  140. package/es/utils/updatePosition.js +5 -2
  141. package/package.json +3 -1
  142. package/patches/@lexical__yjs@0.42.0.patch +42 -0
  143. package/scripts/postinstall-lexical-patch.cjs +62 -36
@@ -0,0 +1,161 @@
1
+ import { getKernelFromEditor, init_utils } from "../../../editor-kernel/utils.js";
2
+ import { HOVER_LINK_COMMAND, HOVER_OUT_LINK_COMMAND } from "./LinkNode.js";
3
+ import { $applyNodeReplacement, DecoratorNode, createCommand } from "lexical";
4
+ import { addClassNamesToElement } from "@lexical/utils";
5
+ //#region src/plugins/link/node/LinkCardNode.ts
6
+ init_utils();
7
+ const EDIT_LINK_CARD_COMMAND = createCommand();
8
+ var LinkCardNode = class LinkCardNode extends DecoratorNode {
9
+ static getType() {
10
+ return "link-card";
11
+ }
12
+ static clone(node) {
13
+ return new LinkCardNode(node.__url, node.__title, node.__icon, node.__description, node.__openTarget, node.__key);
14
+ }
15
+ static importJSON(serializedNode) {
16
+ return $createLinkCardNode({
17
+ description: serializedNode.description,
18
+ icon: serializedNode.icon,
19
+ openTarget: serializedNode.openTarget,
20
+ title: serializedNode.title,
21
+ url: serializedNode.url
22
+ }).updateFromJSON(serializedNode);
23
+ }
24
+ static importDOM() {
25
+ return { a: (node) => {
26
+ if (node instanceof HTMLAnchorElement && node.dataset.linkCard === "true" && node.dataset.linkCardLayout !== "block") return {
27
+ conversion: $convertLinkCardElement,
28
+ priority: 2
29
+ };
30
+ return null;
31
+ } };
32
+ }
33
+ constructor(url, title, icon, description, openTarget, key) {
34
+ super(key);
35
+ this.__url = url;
36
+ this.__title = title || url;
37
+ this.__icon = icon || "";
38
+ this.__description = description || "";
39
+ this.__openTarget = openTarget ?? "_blank";
40
+ }
41
+ createDOM(config, editor) {
42
+ const element = document.createElement("span");
43
+ element.dataset.linkCard = "true";
44
+ addClassNamesToElement(element, config.theme.linkCard);
45
+ element.addEventListener("mouseenter", (event) => {
46
+ if (event.target instanceof HTMLElement) {
47
+ event.target.classList.add("hover");
48
+ editor.dispatchCommand(HOVER_LINK_COMMAND, {
49
+ event,
50
+ node: this
51
+ });
52
+ }
53
+ });
54
+ element.addEventListener("mouseleave", (event) => {
55
+ if (event.target instanceof HTMLElement) {
56
+ event.target.classList.remove("hover");
57
+ editor.dispatchCommand(HOVER_OUT_LINK_COMMAND, { event });
58
+ }
59
+ });
60
+ return element;
61
+ }
62
+ updateDOM() {
63
+ return false;
64
+ }
65
+ isInline() {
66
+ return true;
67
+ }
68
+ isKeyboardSelectable() {
69
+ return true;
70
+ }
71
+ exportDOM() {
72
+ const element = document.createElement("a");
73
+ element.dataset.linkCard = "true";
74
+ element.dataset.icon = this.getIcon();
75
+ element.dataset.description = this.getDescription();
76
+ if (this.getOpenTarget()) element.target = this.getOpenTarget();
77
+ element.href = this.getURL();
78
+ element.textContent = this.getTitle();
79
+ return { element };
80
+ }
81
+ exportJSON() {
82
+ return {
83
+ ...super.exportJSON(),
84
+ description: this.getDescription(),
85
+ icon: this.getIcon(),
86
+ openTarget: this.getOpenTarget(),
87
+ title: this.getTitle(),
88
+ url: this.getURL()
89
+ };
90
+ }
91
+ updateFromJSON(serializedNode) {
92
+ return super.updateFromJSON(serializedNode).setURL(serializedNode.url).setTitle(serializedNode.title).setIcon(serializedNode.icon).setDescription(serializedNode.description).setOpenTarget(serializedNode.openTarget ?? "_blank");
93
+ }
94
+ getTextContent() {
95
+ return this.getTitle();
96
+ }
97
+ getURL() {
98
+ return this.getLatest().__url;
99
+ }
100
+ setURL(url) {
101
+ const writable = this.getWritable();
102
+ writable.__url = url;
103
+ return writable;
104
+ }
105
+ getTitle() {
106
+ return this.getLatest().__title || this.getURL();
107
+ }
108
+ setTitle(title) {
109
+ const writable = this.getWritable();
110
+ writable.__title = title || this.getURL();
111
+ return writable;
112
+ }
113
+ getIcon() {
114
+ return this.getLatest().__icon;
115
+ }
116
+ setIcon(icon) {
117
+ const writable = this.getWritable();
118
+ writable.__icon = icon || "";
119
+ return writable;
120
+ }
121
+ getDescription() {
122
+ return this.getLatest().__description;
123
+ }
124
+ setDescription(description) {
125
+ const writable = this.getWritable();
126
+ writable.__description = description || "";
127
+ return writable;
128
+ }
129
+ getOpenTarget() {
130
+ return this.getLatest().__openTarget;
131
+ }
132
+ setOpenTarget(openTarget) {
133
+ const writable = this.getWritable();
134
+ writable.__openTarget = openTarget ?? "_blank";
135
+ return writable;
136
+ }
137
+ decorate(editor) {
138
+ const decorator = getKernelFromEditor(editor)?.getDecorator(LinkCardNode.getType());
139
+ if (!decorator) return null;
140
+ if (typeof decorator === "function") return decorator(this, editor);
141
+ return decorator.render(this, editor);
142
+ }
143
+ };
144
+ function $createLinkCardNode(payload) {
145
+ return $applyNodeReplacement(new LinkCardNode(payload.url, payload.title, payload.icon, payload.description, payload.openTarget));
146
+ }
147
+ function $convertLinkCardElement(domNode) {
148
+ const element = domNode;
149
+ return { node: $createLinkCardNode({
150
+ description: element.dataset.description,
151
+ icon: element.dataset.icon,
152
+ openTarget: element.getAttribute("target"),
153
+ title: element.textContent || element.href,
154
+ url: element.getAttribute("href") || ""
155
+ }) };
156
+ }
157
+ function $isLinkCardNode(node) {
158
+ return node instanceof LinkCardNode || node?.getType?.() === LinkCardNode.getType();
159
+ }
160
+ //#endregion
161
+ export { $createLinkCardNode, $isLinkCardNode, EDIT_LINK_CARD_COMMAND, LinkCardNode };
@@ -0,0 +1,33 @@
1
+ import { DOMConversionMap, DOMExportOutput, DecoratorNode, EditorConfig, LexicalEditor, LexicalNode, LexicalUpdateJSON, NodeKey, SerializedLexicalNode, Spread } from "lexical";
2
+ //#region src/plugins/link/node/LinkIframeNode.d.ts
3
+ type SerializedLinkIframeNode = Spread<{
4
+ src: string;
5
+ title: string;
6
+ url: string;
7
+ }, SerializedLexicalNode>;
8
+ declare class LinkIframeNode extends DecoratorNode<unknown> {
9
+ __url: string;
10
+ __src: string;
11
+ __title: string;
12
+ static getType(): string;
13
+ static clone(node: LinkIframeNode): LinkIframeNode;
14
+ static importJSON(serializedNode: SerializedLinkIframeNode): LinkIframeNode;
15
+ static importDOM(): DOMConversionMap | null;
16
+ constructor(url: string, src?: string, title?: string, key?: NodeKey);
17
+ createDOM(config: EditorConfig, editor: LexicalEditor): HTMLElement;
18
+ updateDOM(): false;
19
+ isInline(): false;
20
+ exportDOM(): DOMExportOutput;
21
+ exportJSON(): SerializedLinkIframeNode;
22
+ updateFromJSON(serializedNode: LexicalUpdateJSON<SerializedLinkIframeNode>): this;
23
+ getTextContent(): string;
24
+ getURL(): string;
25
+ setURL(url: string): this;
26
+ getSrc(): string;
27
+ setSrc(src?: string): this;
28
+ getTitle(): string;
29
+ setTitle(title?: string): this;
30
+ decorate(editor: LexicalEditor): unknown;
31
+ }
32
+ //#endregion
33
+ export { LinkIframeNode, SerializedLinkIframeNode };
@@ -0,0 +1,131 @@
1
+ import { getKernelFromEditor, init_utils } from "../../../editor-kernel/utils.js";
2
+ import { HOVER_LINK_COMMAND, HOVER_OUT_LINK_COMMAND } from "./LinkNode.js";
3
+ import { $applyNodeReplacement, DecoratorNode } from "lexical";
4
+ import { addClassNamesToElement } from "@lexical/utils";
5
+ //#region src/plugins/link/node/LinkIframeNode.ts
6
+ init_utils();
7
+ var LinkIframeNode = class LinkIframeNode extends DecoratorNode {
8
+ static getType() {
9
+ return "link-iframe";
10
+ }
11
+ static clone(node) {
12
+ return new LinkIframeNode(node.__url, node.__src, node.__title, node.__key);
13
+ }
14
+ static importJSON(serializedNode) {
15
+ return $createLinkIframeNode({
16
+ src: serializedNode.src,
17
+ title: serializedNode.title,
18
+ url: serializedNode.url
19
+ }).updateFromJSON(serializedNode);
20
+ }
21
+ static importDOM() {
22
+ return { a: (node) => {
23
+ if (node instanceof HTMLAnchorElement && node.dataset.linkIframe === "true") return {
24
+ conversion: $convertLinkIframeElement,
25
+ priority: 2
26
+ };
27
+ return null;
28
+ } };
29
+ }
30
+ constructor(url, src, title, key) {
31
+ super(key);
32
+ this.__url = url;
33
+ this.__src = src || url;
34
+ this.__title = title || url;
35
+ }
36
+ createDOM(config, editor) {
37
+ const element = document.createElement("div");
38
+ element.dataset.linkIframe = "true";
39
+ addClassNamesToElement(element, config.theme.linkIframe);
40
+ element.addEventListener("mouseenter", (event) => {
41
+ if (event.target instanceof HTMLElement) {
42
+ event.target.classList.add("hover");
43
+ editor.dispatchCommand(HOVER_LINK_COMMAND, {
44
+ event,
45
+ node: this
46
+ });
47
+ }
48
+ });
49
+ element.addEventListener("mouseleave", (event) => {
50
+ if (event.target instanceof HTMLElement) {
51
+ event.target.classList.remove("hover");
52
+ editor.dispatchCommand(HOVER_OUT_LINK_COMMAND, { event });
53
+ }
54
+ });
55
+ return element;
56
+ }
57
+ updateDOM() {
58
+ return false;
59
+ }
60
+ isInline() {
61
+ return false;
62
+ }
63
+ exportDOM() {
64
+ const element = document.createElement("a");
65
+ element.dataset.linkIframe = "true";
66
+ element.dataset.src = this.getSrc();
67
+ element.href = this.getURL();
68
+ element.textContent = this.getTitle();
69
+ return { element };
70
+ }
71
+ exportJSON() {
72
+ return {
73
+ ...super.exportJSON(),
74
+ src: this.getSrc(),
75
+ title: this.getTitle(),
76
+ url: this.getURL()
77
+ };
78
+ }
79
+ updateFromJSON(serializedNode) {
80
+ return super.updateFromJSON(serializedNode).setURL(serializedNode.url).setSrc(serializedNode.src).setTitle(serializedNode.title);
81
+ }
82
+ getTextContent() {
83
+ return this.getTitle();
84
+ }
85
+ getURL() {
86
+ return this.getLatest().__url;
87
+ }
88
+ setURL(url) {
89
+ const writable = this.getWritable();
90
+ writable.__url = url;
91
+ return writable;
92
+ }
93
+ getSrc() {
94
+ return this.getLatest().__src || this.getURL();
95
+ }
96
+ setSrc(src) {
97
+ const writable = this.getWritable();
98
+ writable.__src = src || this.getURL();
99
+ return writable;
100
+ }
101
+ getTitle() {
102
+ return this.getLatest().__title || this.getURL();
103
+ }
104
+ setTitle(title) {
105
+ const writable = this.getWritable();
106
+ writable.__title = title || this.getURL();
107
+ return writable;
108
+ }
109
+ decorate(editor) {
110
+ const decorator = getKernelFromEditor(editor)?.getDecorator(LinkIframeNode.getType());
111
+ if (!decorator) return null;
112
+ if (typeof decorator === "function") return decorator(this, editor);
113
+ return decorator.render(this, editor);
114
+ }
115
+ };
116
+ function $createLinkIframeNode(payload) {
117
+ return $applyNodeReplacement(new LinkIframeNode(payload.url, payload.src, payload.title));
118
+ }
119
+ function $convertLinkIframeElement(domNode) {
120
+ const element = domNode;
121
+ return { node: $createLinkIframeNode({
122
+ src: element.dataset.src,
123
+ title: element.textContent || element.href,
124
+ url: element.getAttribute("href") || ""
125
+ }) };
126
+ }
127
+ function $isLinkIframeNode(node) {
128
+ return node instanceof LinkIframeNode || node?.getType?.() === LinkIframeNode.getType();
129
+ }
130
+ //#endregion
131
+ export { $createLinkIframeNode, $isLinkIframeNode, LinkIframeNode };
@@ -1,9 +1,55 @@
1
- import { BaseSelection, DOMConversionMap, EditorConfig, ElementNode, LexicalCommand, LexicalEditor, LexicalNode, LexicalUpdateJSON, NodeKey, RangeSelection, SerializedElementNode, Spread } from "lexical";
1
+ import { BaseSelection, DOMConversionMap, EditorConfig, ElementDOMSlot, ElementNode, LexicalCommand, LexicalEditor, LexicalNode, LexicalUpdateJSON, NodeKey, RangeSelection, SerializedElementNode, Spread } from "lexical";
2
2
  //#region src/plugins/link/node/LinkNode.d.ts
3
3
  type LinkAttributes = {
4
4
  rel?: null | string;
5
5
  target?: null | string;
6
6
  title?: null | string;
7
7
  };
8
+ type SerializedLinkNode = Spread<{
9
+ url: string;
10
+ }, Spread<LinkAttributes, SerializedElementNode>>;
11
+ type LinkHTMLElementType = HTMLAnchorElement | HTMLSpanElement;
12
+ /** @noInheritDoc */
13
+ declare class LinkNode extends ElementNode {
14
+ /** @internal */
15
+ __url: string;
16
+ /** @internal */
17
+ __target: null | string;
18
+ /** @internal */
19
+ __rel: null | string;
20
+ /** @internal */
21
+ __title: null | string;
22
+ static getType(): string;
23
+ static clone(node: LinkNode): LinkNode;
24
+ constructor(url?: string, attributes?: LinkAttributes, key?: NodeKey);
25
+ createDOM(config: EditorConfig, editor: LexicalEditor): LinkHTMLElementType;
26
+ updateLinkDOM(prevNode: this | null, anchor: LinkHTMLElementType, _config: EditorConfig): void;
27
+ updateDOM(prevNode: this, anchor: LinkHTMLElementType, config: EditorConfig): boolean;
28
+ static importDOM(): DOMConversionMap | null;
29
+ static importJSON(serializedNode: SerializedLinkNode): LinkNode;
30
+ updateFromJSON(serializedNode: LexicalUpdateJSON<SerializedLinkNode>): this;
31
+ sanitizeUrl(url: string, allowedProtocols?: Set<string>): string;
32
+ exportJSON(): SerializedLinkNode | SerializedAutoLinkNode;
33
+ getURL(): string;
34
+ setURL(url: string): this;
35
+ getTarget(): null | string;
36
+ setTarget(target: null | string): this;
37
+ getRel(): null | string;
38
+ setRel(rel: null | string): this;
39
+ getTitle(): null | string;
40
+ setTitle(title: null | string): this;
41
+ insertNewAfter(_: RangeSelection, restoreSelection?: boolean): null | ElementNode;
42
+ canInsertTextBefore(): false;
43
+ canInsertTextAfter(): false;
44
+ canBeEmpty(): false;
45
+ isInline(): true;
46
+ extractWithChild(child: LexicalNode, selection: BaseSelection): boolean;
47
+ isEmailURI(): boolean;
48
+ isWebSiteURI(): boolean;
49
+ getDOMSlot(element: HTMLElement): ElementDOMSlot<HTMLElement>;
50
+ }
51
+ type SerializedAutoLinkNode = Spread<{
52
+ isUnlinked: boolean;
53
+ }, SerializedLinkNode>;
8
54
  //#endregion
9
- export { LinkAttributes };
55
+ export { LinkAttributes, LinkNode, SerializedAutoLinkNode, SerializedLinkNode };
@@ -1,5 +1,6 @@
1
1
  import { createDebugLogger, init_debug } from "../../../utils/debug.js";
2
- import { assert, init_utils } from "../../../editor-kernel/utils.js";
2
+ import { assert, getKernelFromEditorConfig, init_utils } from "../../../editor-kernel/utils.js";
3
+ import { ILinkService } from "../service/i-link-service.js";
3
4
  import { $applyNodeReplacement, $getSelection, $isElementNode, $isNodeSelection, $isRangeSelection, $normalizeSelection__EXPERIMENTAL, $setSelection, ElementNode, createCommand } from "lexical";
4
5
  import { $findMatchingParent, addClassNamesToElement, isHTMLAnchorElement } from "@lexical/utils";
5
6
  //#region src/plugins/link/node/LinkNode.ts
@@ -45,7 +46,7 @@ var LinkNode = class LinkNode extends ElementNode {
45
46
  event.target.classList.add("hover");
46
47
  editor.dispatchCommand(HOVER_LINK_COMMAND, {
47
48
  event,
48
- linkNode: this
49
+ node: this
49
50
  });
50
51
  }
51
52
  });
@@ -59,7 +60,8 @@ var LinkNode = class LinkNode extends ElementNode {
59
60
  }
60
61
  updateLinkDOM(prevNode, anchor, _config) {
61
62
  if (isHTMLAnchorElement(anchor)) {
62
- if (!prevNode || prevNode.__url !== this.__url) anchor.href = this.sanitizeUrl(this.__url);
63
+ const linkService = getLinkServiceFromConfig(_config);
64
+ if (!prevNode || prevNode.__url !== this.__url) anchor.href = this.sanitizeUrl(this.__url, linkService?.getAllowedProtocols());
63
65
  for (const attr of [
64
66
  "target",
65
67
  "rel",
@@ -90,11 +92,11 @@ var LinkNode = class LinkNode extends ElementNode {
90
92
  updateFromJSON(serializedNode) {
91
93
  return super.updateFromJSON(serializedNode).setURL(serializedNode.url).setRel(serializedNode.rel || null).setTarget(serializedNode.target || null).setTitle(serializedNode.title || null);
92
94
  }
93
- sanitizeUrl(url) {
95
+ sanitizeUrl(url, allowedProtocols = SUPPORTED_URL_PROTOCOLS) {
94
96
  url = formatUrl(url);
95
97
  try {
96
98
  const parsedUrl = new URL(formatUrl(url));
97
- if (!SUPPORTED_URL_PROTOCOLS.has(parsedUrl.protocol)) return "about:blank";
99
+ if (!allowedProtocols.has(parsedUrl.protocol)) return "about:blank";
98
100
  } catch {
99
101
  return url;
100
102
  }
@@ -174,7 +176,13 @@ var LinkNode = class LinkNode extends ElementNode {
174
176
  isWebSiteURI() {
175
177
  return this.__url.startsWith("https://") || this.__url.startsWith("http://");
176
178
  }
179
+ getDOMSlot(element) {
180
+ return super.getDOMSlot(element);
181
+ }
177
182
  };
183
+ function getLinkServiceFromConfig(config) {
184
+ return getKernelFromEditorConfig(config)?.requireService(ILinkService) || null;
185
+ }
178
186
  function $convertAnchorElement(domNode) {
179
187
  let node = null;
180
188
  if (isHTMLAnchorElement(domNode)) {
@@ -202,7 +210,7 @@ function $createLinkNode(url = "", attributes) {
202
210
  * @returns true if node is a LinkNode, false otherwise.
203
211
  */
204
212
  function $isLinkNode(node) {
205
- return node instanceof LinkNode;
213
+ return node instanceof LinkNode || node?.getType?.() === "link" || node?.getType?.() === "autolink";
206
214
  }
207
215
  var AutoLinkNode = class AutoLinkNode extends LinkNode {
208
216
  constructor(url = "", attributes = {}, key) {
@@ -0,0 +1,36 @@
1
+ import { DOMConversionMap, DOMExportOutput, DecoratorNode, EditorConfig, LexicalEditor, LexicalNode, LexicalUpdateJSON, NodeKey, SerializedLexicalNode, Spread } from "lexical";
2
+ //#region src/plugins/link/node/SchemaNode.d.ts
3
+ type SerializedSchemaNode = Spread<{
4
+ payload?: unknown;
5
+ schemaType: string;
6
+ title: string;
7
+ url: string;
8
+ }, SerializedLexicalNode>;
9
+ declare class SchemaNode extends DecoratorNode<unknown> {
10
+ __url: string;
11
+ __schemaType: string;
12
+ __payload: unknown;
13
+ __title: string;
14
+ static getType(): string;
15
+ static clone(node: SchemaNode): SchemaNode;
16
+ static importJSON(serializedNode: SerializedSchemaNode): SchemaNode;
17
+ static importDOM(): DOMConversionMap | null;
18
+ constructor(url: string, schemaType?: string, payload?: unknown, title?: string, key?: NodeKey);
19
+ createDOM(config: EditorConfig, editor: LexicalEditor): HTMLElement;
20
+ updateDOM(): false;
21
+ exportDOM(): DOMExportOutput;
22
+ exportJSON(): SerializedSchemaNode;
23
+ updateFromJSON(serializedNode: LexicalUpdateJSON<SerializedSchemaNode>): this;
24
+ getTextContent(): string;
25
+ getURL(): string;
26
+ setURL(url: string): this;
27
+ getSchemaType(): string;
28
+ setSchemaType(schemaType?: string): this;
29
+ getPayload(): unknown;
30
+ setPayload(payload: unknown): this;
31
+ getTitle(): string;
32
+ setTitle(title?: string): this;
33
+ decorate(editor: LexicalEditor): unknown;
34
+ }
35
+ //#endregion
36
+ export { SchemaNode, SerializedSchemaNode };
@@ -0,0 +1,149 @@
1
+ import { getKernelFromEditor, init_utils } from "../../../editor-kernel/utils.js";
2
+ import { HOVER_LINK_COMMAND, HOVER_OUT_LINK_COMMAND } from "./LinkNode.js";
3
+ import { $applyNodeReplacement, DecoratorNode } from "lexical";
4
+ import { addClassNamesToElement } from "@lexical/utils";
5
+ //#region src/plugins/link/node/SchemaNode.ts
6
+ init_utils();
7
+ var SchemaNode = class SchemaNode extends DecoratorNode {
8
+ static getType() {
9
+ return "schema-link";
10
+ }
11
+ static clone(node) {
12
+ return new SchemaNode(node.__url, node.__schemaType, node.__payload, node.__title, node.__key);
13
+ }
14
+ static importJSON(serializedNode) {
15
+ return $createSchemaNode({
16
+ payload: serializedNode.payload,
17
+ schemaType: serializedNode.schemaType,
18
+ title: serializedNode.title,
19
+ url: serializedNode.url
20
+ }).updateFromJSON(serializedNode);
21
+ }
22
+ static importDOM() {
23
+ return { a: (node) => {
24
+ if (node instanceof HTMLAnchorElement && node.dataset.schemaLink === "true") return {
25
+ conversion: $convertSchemaElement,
26
+ priority: 2
27
+ };
28
+ return null;
29
+ } };
30
+ }
31
+ constructor(url, schemaType, payload, title, key) {
32
+ super(key);
33
+ this.__url = url;
34
+ this.__schemaType = schemaType || "";
35
+ this.__payload = payload;
36
+ this.__title = title || url;
37
+ }
38
+ createDOM(config, editor) {
39
+ const element = document.createElement("div");
40
+ element.dataset.schemaLink = "true";
41
+ addClassNamesToElement(element, config.theme.schemaLink);
42
+ element.addEventListener("mouseenter", (event) => {
43
+ if (event.target instanceof HTMLElement) {
44
+ event.target.classList.add("hover");
45
+ editor.dispatchCommand(HOVER_LINK_COMMAND, {
46
+ event,
47
+ node: this
48
+ });
49
+ }
50
+ });
51
+ element.addEventListener("mouseleave", (event) => {
52
+ if (event.target instanceof HTMLElement) {
53
+ event.target.classList.remove("hover");
54
+ editor.dispatchCommand(HOVER_OUT_LINK_COMMAND, { event });
55
+ }
56
+ });
57
+ return element;
58
+ }
59
+ updateDOM() {
60
+ return false;
61
+ }
62
+ exportDOM() {
63
+ const element = document.createElement("a");
64
+ element.dataset.schemaLink = "true";
65
+ element.dataset.schemaType = this.getSchemaType();
66
+ element.dataset.payload = JSON.stringify(this.getPayload() ?? null);
67
+ element.href = this.getURL();
68
+ element.textContent = this.getTitle();
69
+ return { element };
70
+ }
71
+ exportJSON() {
72
+ return {
73
+ ...super.exportJSON(),
74
+ payload: this.getPayload(),
75
+ schemaType: this.getSchemaType(),
76
+ title: this.getTitle(),
77
+ url: this.getURL()
78
+ };
79
+ }
80
+ updateFromJSON(serializedNode) {
81
+ return super.updateFromJSON(serializedNode).setURL(serializedNode.url).setSchemaType(serializedNode.schemaType).setPayload(serializedNode.payload).setTitle(serializedNode.title);
82
+ }
83
+ getTextContent() {
84
+ return this.getTitle();
85
+ }
86
+ getURL() {
87
+ return this.getLatest().__url;
88
+ }
89
+ setURL(url) {
90
+ const writable = this.getWritable();
91
+ writable.__url = url;
92
+ return writable;
93
+ }
94
+ getSchemaType() {
95
+ return this.getLatest().__schemaType;
96
+ }
97
+ setSchemaType(schemaType) {
98
+ const writable = this.getWritable();
99
+ writable.__schemaType = schemaType || "";
100
+ return writable;
101
+ }
102
+ getPayload() {
103
+ return this.getLatest().__payload;
104
+ }
105
+ setPayload(payload) {
106
+ const writable = this.getWritable();
107
+ writable.__payload = payload;
108
+ return writable;
109
+ }
110
+ getTitle() {
111
+ return this.getLatest().__title || this.getURL();
112
+ }
113
+ setTitle(title) {
114
+ const writable = this.getWritable();
115
+ writable.__title = title || this.getURL();
116
+ return writable;
117
+ }
118
+ decorate(editor) {
119
+ const decorator = getKernelFromEditor(editor)?.getDecorator(SchemaNode.getType());
120
+ if (!decorator) return null;
121
+ if (typeof decorator === "function") return decorator(this, editor);
122
+ return decorator.render(this, editor);
123
+ }
124
+ };
125
+ function $createSchemaNode(payload) {
126
+ return $applyNodeReplacement(new SchemaNode(payload.url, payload.schemaType, payload.payload, payload.title));
127
+ }
128
+ function $convertSchemaElement(domNode) {
129
+ const element = domNode;
130
+ return { node: $createSchemaNode({
131
+ payload: parsePayload(element.dataset.payload),
132
+ schemaType: element.dataset.schemaType,
133
+ title: element.textContent || element.href,
134
+ url: element.getAttribute("href") || ""
135
+ }) };
136
+ }
137
+ function parsePayload(value) {
138
+ if (!value) return void 0;
139
+ try {
140
+ return JSON.parse(value);
141
+ } catch {
142
+ return value;
143
+ }
144
+ }
145
+ function $isSchemaNode(node) {
146
+ return node instanceof SchemaNode || node?.getType?.() === SchemaNode.getType();
147
+ }
148
+ //#endregion
149
+ export { $createSchemaNode, $isSchemaNode, SchemaNode };
@@ -0,0 +1,7 @@
1
+ import { LinkNode } from "../node/LinkNode.js";
2
+ import { LinkService } from "../service/i-link-service.js";
3
+ import { LexicalEditor } from "lexical";
4
+ //#region src/plugins/link/normalization/index.d.ts
5
+ declare function normalizeSchemaLinkNode(node: LinkNode, editor: LexicalEditor, linkService: LinkService): boolean;
6
+ //#endregion
7
+ export { normalizeSchemaLinkNode };
@@ -0,0 +1,38 @@
1
+ import { $createSchemaNode } from "../node/SchemaNode.js";
2
+ //#region src/plugins/link/normalization/index.ts
3
+ function normalizeSchemaLinkNode(node, editor, linkService) {
4
+ const url = node.getURL();
5
+ const title = node.getTitle() || node.getTextContent() || url;
6
+ const schema = linkService.parseSchemaUrl(url);
7
+ const rule = linkService.getSchemaRule(url, {
8
+ editor,
9
+ schema,
10
+ text: node.getTextContent(),
11
+ title
12
+ });
13
+ if (!rule) return false;
14
+ const parsed = rule.parse?.(url, schema);
15
+ const payload = parsed && typeof parsed === "object" && !Array.isArray(parsed) ? normalizeSchemaPayload(parsed) : { payload: parsed };
16
+ if (linkService.hasSchemaLinkRenderer(url)) payload.payload = {
17
+ ...payload.payload && typeof payload.payload === "object" ? payload.payload : {},
18
+ __legacyLink: {
19
+ rel: node.getRel(),
20
+ target: node.getTarget(),
21
+ text: node.getTextContent(),
22
+ title: node.getTitle()
23
+ }
24
+ };
25
+ node.replace($createSchemaNode({
26
+ payload: payload.payload,
27
+ schemaType: payload.schemaType || rule.id,
28
+ title: payload.title || title,
29
+ url: payload.url || url
30
+ }));
31
+ return true;
32
+ }
33
+ function normalizeSchemaPayload(payload) {
34
+ if ("payload" in payload || "schemaType" in payload || "title" in payload || "url" in payload) return payload;
35
+ return { payload };
36
+ }
37
+ //#endregion
38
+ export { normalizeSchemaLinkNode };