@haklex/rich-editor 0.0.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.
Files changed (204) hide show
  1. package/LICENSE +28 -0
  2. package/README.md +164 -0
  3. package/dist/RichEditor-DeRWrU51.js +1125 -0
  4. package/dist/RichRenderer-BmM4j0fv.js +95 -0
  5. package/dist/components/ContentEditable.d.ts +7 -0
  6. package/dist/components/ContentEditable.d.ts.map +1 -0
  7. package/dist/components/RendererWrapper.d.ts +31 -0
  8. package/dist/components/RendererWrapper.d.ts.map +1 -0
  9. package/dist/components/RichEditor.d.ts +3 -0
  10. package/dist/components/RichEditor.d.ts.map +1 -0
  11. package/dist/components/RichRenderer.d.ts +3 -0
  12. package/dist/components/RichRenderer.d.ts.map +1 -0
  13. package/dist/components/decorators/AlertEditDecorator.d.ts +10 -0
  14. package/dist/components/decorators/AlertEditDecorator.d.ts.map +1 -0
  15. package/dist/components/decorators/BannerEditDecorator.d.ts +10 -0
  16. package/dist/components/decorators/BannerEditDecorator.d.ts.map +1 -0
  17. package/dist/components/decorators/CodeBlockEditDecorator.d.ts +8 -0
  18. package/dist/components/decorators/CodeBlockEditDecorator.d.ts.map +1 -0
  19. package/dist/components/decorators/GridEditDecorator.d.ts +10 -0
  20. package/dist/components/decorators/GridEditDecorator.d.ts.map +1 -0
  21. package/dist/components/renderers/AlertReadOnlyDecorator.d.ts +9 -0
  22. package/dist/components/renderers/AlertReadOnlyDecorator.d.ts.map +1 -0
  23. package/dist/components/renderers/AlertRenderer.d.ts +9 -0
  24. package/dist/components/renderers/AlertRenderer.d.ts.map +1 -0
  25. package/dist/components/renderers/BannerReadOnlyDecorator.d.ts +9 -0
  26. package/dist/components/renderers/BannerReadOnlyDecorator.d.ts.map +1 -0
  27. package/dist/components/renderers/BannerRenderer.d.ts +9 -0
  28. package/dist/components/renderers/BannerRenderer.d.ts.map +1 -0
  29. package/dist/components/renderers/CodeBlockRenderer.d.ts +9 -0
  30. package/dist/components/renderers/CodeBlockRenderer.d.ts.map +1 -0
  31. package/dist/components/renderers/FootnoteRenderer.d.ts +5 -0
  32. package/dist/components/renderers/FootnoteRenderer.d.ts.map +1 -0
  33. package/dist/components/renderers/FootnoteSectionEditRenderer.d.ts +6 -0
  34. package/dist/components/renderers/FootnoteSectionEditRenderer.d.ts.map +1 -0
  35. package/dist/components/renderers/FootnoteSectionRenderer.d.ts +6 -0
  36. package/dist/components/renderers/FootnoteSectionRenderer.d.ts.map +1 -0
  37. package/dist/components/renderers/GridReadOnlyDecorator.d.ts +9 -0
  38. package/dist/components/renderers/GridReadOnlyDecorator.d.ts.map +1 -0
  39. package/dist/components/renderers/ImageRenderer.d.ts +11 -0
  40. package/dist/components/renderers/ImageRenderer.d.ts.map +1 -0
  41. package/dist/components/renderers/KaTeXRenderer.d.ts +6 -0
  42. package/dist/components/renderers/KaTeXRenderer.d.ts.map +1 -0
  43. package/dist/components/renderers/LinkCardRenderer.d.ts +11 -0
  44. package/dist/components/renderers/LinkCardRenderer.d.ts.map +1 -0
  45. package/dist/components/renderers/MentionRenderer.d.ts +7 -0
  46. package/dist/components/renderers/MentionRenderer.d.ts.map +1 -0
  47. package/dist/components/renderers/MermaidRenderer.d.ts +6 -0
  48. package/dist/components/renderers/MermaidRenderer.d.ts.map +1 -0
  49. package/dist/components/renderers/VideoRenderer.d.ts +8 -0
  50. package/dist/components/renderers/VideoRenderer.d.ts.map +1 -0
  51. package/dist/components/utils.d.ts +5 -0
  52. package/dist/components/utils.d.ts.map +1 -0
  53. package/dist/config-edit.d.ts +4 -0
  54. package/dist/config-edit.d.ts.map +1 -0
  55. package/dist/config.d.ts +5 -0
  56. package/dist/config.d.ts.map +1 -0
  57. package/dist/context/ColorSchemeContext.d.ts +8 -0
  58. package/dist/context/ColorSchemeContext.d.ts.map +1 -0
  59. package/dist/context/FootnoteDefinitionsContext.d.ts +12 -0
  60. package/dist/context/FootnoteDefinitionsContext.d.ts.map +1 -0
  61. package/dist/context/RendererConfigContext.d.ts +12 -0
  62. package/dist/context/RendererConfigContext.d.ts.map +1 -0
  63. package/dist/editor.d.ts +6 -0
  64. package/dist/editor.d.ts.map +1 -0
  65. package/dist/editor.mjs +12 -0
  66. package/dist/index.d.ts +59 -0
  67. package/dist/index.d.ts.map +1 -0
  68. package/dist/index.mjs +79 -0
  69. package/dist/node-registry.d.ts +4 -0
  70. package/dist/node-registry.d.ts.map +1 -0
  71. package/dist/nodes/AlertQuoteEditNode.d.ts +9 -0
  72. package/dist/nodes/AlertQuoteEditNode.d.ts.map +1 -0
  73. package/dist/nodes/AlertQuoteNode.d.ts +31 -0
  74. package/dist/nodes/AlertQuoteNode.d.ts.map +1 -0
  75. package/dist/nodes/BannerEditNode.d.ts +9 -0
  76. package/dist/nodes/BannerEditNode.d.ts.map +1 -0
  77. package/dist/nodes/BannerNode.d.ts +32 -0
  78. package/dist/nodes/BannerNode.d.ts.map +1 -0
  79. package/dist/nodes/CodeBlockEditNode.d.ts +9 -0
  80. package/dist/nodes/CodeBlockEditNode.d.ts.map +1 -0
  81. package/dist/nodes/CodeBlockNode.d.ts +28 -0
  82. package/dist/nodes/CodeBlockNode.d.ts.map +1 -0
  83. package/dist/nodes/DetailsNode.d.ts +28 -0
  84. package/dist/nodes/DetailsNode.d.ts.map +1 -0
  85. package/dist/nodes/FootnoteNode.d.ts +22 -0
  86. package/dist/nodes/FootnoteNode.d.ts.map +1 -0
  87. package/dist/nodes/FootnoteSectionEditNode.d.ts +10 -0
  88. package/dist/nodes/FootnoteSectionEditNode.d.ts.map +1 -0
  89. package/dist/nodes/FootnoteSectionNode.d.ts +25 -0
  90. package/dist/nodes/FootnoteSectionNode.d.ts.map +1 -0
  91. package/dist/nodes/GridContainerNode.d.ts +34 -0
  92. package/dist/nodes/GridContainerNode.d.ts.map +1 -0
  93. package/dist/nodes/GridEditNode.d.ts +9 -0
  94. package/dist/nodes/GridEditNode.d.ts.map +1 -0
  95. package/dist/nodes/HorizontalRuleNode.d.ts +19 -0
  96. package/dist/nodes/HorizontalRuleNode.d.ts.map +1 -0
  97. package/dist/nodes/ImageNode.d.ts +43 -0
  98. package/dist/nodes/ImageNode.d.ts.map +1 -0
  99. package/dist/nodes/KaTeXBlockNode.d.ts +24 -0
  100. package/dist/nodes/KaTeXBlockNode.d.ts.map +1 -0
  101. package/dist/nodes/KaTeXInlineNode.d.ts +22 -0
  102. package/dist/nodes/KaTeXInlineNode.d.ts.map +1 -0
  103. package/dist/nodes/LinkCardNode.d.ts +49 -0
  104. package/dist/nodes/LinkCardNode.d.ts.map +1 -0
  105. package/dist/nodes/MentionNode.d.ts +27 -0
  106. package/dist/nodes/MentionNode.d.ts.map +1 -0
  107. package/dist/nodes/MermaidNode.d.ts +24 -0
  108. package/dist/nodes/MermaidNode.d.ts.map +1 -0
  109. package/dist/nodes/SpoilerNode.d.ts +16 -0
  110. package/dist/nodes/SpoilerNode.d.ts.map +1 -0
  111. package/dist/nodes/TaskListItemNode.d.ts +21 -0
  112. package/dist/nodes/TaskListItemNode.d.ts.map +1 -0
  113. package/dist/nodes/VideoNode.d.ts +36 -0
  114. package/dist/nodes/VideoNode.d.ts.map +1 -0
  115. package/dist/nodes/index.d.ts +30 -0
  116. package/dist/nodes/index.d.ts.map +1 -0
  117. package/dist/nodes/shared.d.ts +3 -0
  118. package/dist/nodes/shared.d.ts.map +1 -0
  119. package/dist/plugins/AlertPlugin.d.ts +4 -0
  120. package/dist/plugins/AlertPlugin.d.ts.map +1 -0
  121. package/dist/plugins/AutoFocusPlugin.d.ts +2 -0
  122. package/dist/plugins/AutoFocusPlugin.d.ts.map +1 -0
  123. package/dist/plugins/AutoLinkPlugin.d.ts +7 -0
  124. package/dist/plugins/AutoLinkPlugin.d.ts.map +1 -0
  125. package/dist/plugins/DragDropPlugin.d.ts +2 -0
  126. package/dist/plugins/DragDropPlugin.d.ts.map +1 -0
  127. package/dist/plugins/EditorRefPlugin.d.ts +7 -0
  128. package/dist/plugins/EditorRefPlugin.d.ts.map +1 -0
  129. package/dist/plugins/FootnotePlugin.d.ts +4 -0
  130. package/dist/plugins/FootnotePlugin.d.ts.map +1 -0
  131. package/dist/plugins/HeadingAnchorPlugin.d.ts +2 -0
  132. package/dist/plugins/HeadingAnchorPlugin.d.ts.map +1 -0
  133. package/dist/plugins/ImagePlugin.d.ts +5 -0
  134. package/dist/plugins/ImagePlugin.d.ts.map +1 -0
  135. package/dist/plugins/ImageUploadPlugin.d.ts +14 -0
  136. package/dist/plugins/ImageUploadPlugin.d.ts.map +1 -0
  137. package/dist/plugins/KaTeXPlugin.d.ts +4 -0
  138. package/dist/plugins/KaTeXPlugin.d.ts.map +1 -0
  139. package/dist/plugins/MarkdownShortcutsPlugin.d.ts +2 -0
  140. package/dist/plugins/MarkdownShortcutsPlugin.d.ts.map +1 -0
  141. package/dist/plugins/MermaidPlugin.d.ts +3 -0
  142. package/dist/plugins/MermaidPlugin.d.ts.map +1 -0
  143. package/dist/plugins/OnChangePlugin.d.ts +8 -0
  144. package/dist/plugins/OnChangePlugin.d.ts.map +1 -0
  145. package/dist/plugins/SubmitShortcutPlugin.d.ts +6 -0
  146. package/dist/plugins/SubmitShortcutPlugin.d.ts.map +1 -0
  147. package/dist/plugins/index.d.ts +16 -0
  148. package/dist/plugins/index.d.ts.map +1 -0
  149. package/dist/renderer.d.ts +3 -0
  150. package/dist/renderer.d.ts.map +1 -0
  151. package/dist/renderer.mjs +4 -0
  152. package/dist/rich-editor.css +1328 -0
  153. package/dist/styles/article.css.d.ts +3 -0
  154. package/dist/styles/article.css.d.ts.map +1 -0
  155. package/dist/styles/comment.css.d.ts +3 -0
  156. package/dist/styles/comment.css.d.ts.map +1 -0
  157. package/dist/styles/details.css.d.ts +2 -0
  158. package/dist/styles/details.css.d.ts.map +1 -0
  159. package/dist/styles/grid.css.d.ts +2 -0
  160. package/dist/styles/grid.css.d.ts.map +1 -0
  161. package/dist/styles/index.d.ts +7 -0
  162. package/dist/styles/index.d.ts.map +1 -0
  163. package/dist/styles/katex.css.d.ts +2 -0
  164. package/dist/styles/katex.css.d.ts.map +1 -0
  165. package/dist/styles/note.css.d.ts +3 -0
  166. package/dist/styles/note.css.d.ts.map +1 -0
  167. package/dist/styles/shared.css.d.ts +2 -0
  168. package/dist/styles/shared.css.d.ts.map +1 -0
  169. package/dist/styles/theme.d.ts +3 -0
  170. package/dist/styles/theme.d.ts.map +1 -0
  171. package/dist/styles/vars.css.d.ts +8 -0
  172. package/dist/styles/vars.css.d.ts.map +1 -0
  173. package/dist/transformers/alert.d.ts +3 -0
  174. package/dist/transformers/alert.d.ts.map +1 -0
  175. package/dist/transformers/container.d.ts +3 -0
  176. package/dist/transformers/container.d.ts.map +1 -0
  177. package/dist/transformers/footnote.d.ts +10 -0
  178. package/dist/transformers/footnote.d.ts.map +1 -0
  179. package/dist/transformers/index.d.ts +10 -0
  180. package/dist/transformers/index.d.ts.map +1 -0
  181. package/dist/transformers/insert.d.ts +6 -0
  182. package/dist/transformers/insert.d.ts.map +1 -0
  183. package/dist/transformers/katex.d.ts +4 -0
  184. package/dist/transformers/katex.d.ts.map +1 -0
  185. package/dist/transformers/mention.d.ts +3 -0
  186. package/dist/transformers/mention.d.ts.map +1 -0
  187. package/dist/transformers/spoiler.d.ts +3 -0
  188. package/dist/transformers/spoiler.d.ts.map +1 -0
  189. package/dist/transformers/tasklist.d.ts +11 -0
  190. package/dist/transformers/tasklist.d.ts.map +1 -0
  191. package/dist/types/renderer-config.d.ts +67 -0
  192. package/dist/types/renderer-config.d.ts.map +1 -0
  193. package/dist/types/slash-menu.d.ts +11 -0
  194. package/dist/types/slash-menu.d.ts.map +1 -0
  195. package/dist/types.d.ts +32 -0
  196. package/dist/types.d.ts.map +1 -0
  197. package/dist/utils/lucide-dom.d.ts +4 -0
  198. package/dist/utils/lucide-dom.d.ts.map +1 -0
  199. package/dist/utils/shiki.d.ts +6 -0
  200. package/dist/utils/shiki.d.ts.map +1 -0
  201. package/dist/utils/thumbhash.d.ts +7 -0
  202. package/dist/utils/thumbhash.d.ts.map +1 -0
  203. package/dist/utils-CctLX9nj.js +2702 -0
  204. package/package.json +88 -0
@@ -0,0 +1,1125 @@
1
+ import { jsxs, Fragment, jsx } from "react/jsx-runtime";
2
+ import { W as $isAlertQuoteNode, X as RendererWrapper, Y as AlertRenderer, Z as AlertQuoteNode, _ as $isBannerNode, a0 as BannerRenderer, a1 as BannerNode, a2 as normalizeBannerType, a3 as $isCodeBlockNode, a4 as CodeBlockRenderer, a5 as CodeBlockNode, S as useFootnoteDefinitions, s as FootnoteSectionNode, k as $isGridContainerNode, G as GridContainerNode, e as editorTheme, N as NESTED_EDITOR_NODES, b as builtinNodes, a6 as SpoilerNode, a7 as MentionNode, t as KaTeXInlineNode, K as KaTeXBlockNode, a8 as ImageNode, a9 as FootnoteNode, aa as TaskListItemNode, ab as VideoNode, L as LinkCardNode, ac as DetailsNode, M as MermaidNode, ad as $createAlertQuoteNode, ae as $createImageNode, af as $createKaTeXInlineNode, ag as $createKaTeXBlockNode, ah as $createBannerNode, ai as $createDetailsNode, aj as $isDetailsNode, ak as $createFootnoteNode, al as $isFootnoteNode, j as $isFootnoteSectionNode, $ as $createFootnoteSectionNode, m as $isKaTeXInlineNode, l as $isKaTeXBlockNode, am as $createMentionNode, an as $isMentionNode, ao as $createSpoilerNode, ap as $isSpoilerNode, aq as $createTaskListItemNode, ar as $isTaskListItemNode, i as $createMermaidNode, d as clsx, P as PortalThemeProvider, C as ColorSchemeProvider, R as RendererConfigProvider, F as FootnotePlugin, g as getVariantClass } from "./utils-CctLX9nj.js";
3
+ import { LexicalComposer } from "@lexical/react/LexicalComposer";
4
+ import { LexicalErrorBoundary } from "@lexical/react/LexicalErrorBoundary";
5
+ import { HistoryPlugin } from "@lexical/react/LexicalHistoryPlugin";
6
+ import { LinkPlugin } from "@lexical/react/LexicalLinkPlugin";
7
+ import { ListPlugin } from "@lexical/react/LexicalListPlugin";
8
+ import { RichTextPlugin } from "@lexical/react/LexicalRichTextPlugin";
9
+ import { TabIndentationPlugin } from "@lexical/react/LexicalTabIndentationPlugin";
10
+ import { TablePlugin } from "@lexical/react/LexicalTablePlugin";
11
+ import { useCallback, createElement, useState, useEffect, useRef } from "react";
12
+ import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
13
+ import { ContentEditable as ContentEditable$1 } from "@lexical/react/LexicalContentEditable";
14
+ import { LexicalNestedComposer } from "@lexical/react/LexicalNestedComposer";
15
+ import { $getNodeByKey, $nodesOfType, $getRoot, createEditor, createCommand, $insertNodes, COMMAND_PRIORITY_EDITOR, $createParagraphNode, $createTextNode, KEY_ENTER_COMMAND, COMMAND_PRIORITY_HIGH } from "lexical";
16
+ import { LayoutGrid, Plus, Minus } from "lucide-react";
17
+ import { createLinkMatcherWithRegExp, registerAutoLink } from "@lexical/link";
18
+ import { MarkdownShortcutPlugin } from "@lexical/react/LexicalMarkdownShortcutPlugin";
19
+ import { TRANSFORMERS } from "@lexical/markdown";
20
+ import { $isQuoteNode } from "@lexical/rich-text";
21
+ function AlertEditDecorator({
22
+ nodeKey,
23
+ alertType,
24
+ contentEditor
25
+ }) {
26
+ const [editor] = useLexicalComposerContext();
27
+ const editable = editor.isEditable();
28
+ const handleTypeChange = useCallback(
29
+ (newType) => {
30
+ editor.update(() => {
31
+ const node = $getNodeByKey(nodeKey);
32
+ if ($isAlertQuoteNode(node)) {
33
+ node.setAlertType(newType);
34
+ }
35
+ });
36
+ },
37
+ [editor, nodeKey]
38
+ );
39
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
40
+ /* @__PURE__ */ jsx(
41
+ RendererWrapper,
42
+ {
43
+ rendererKey: "Alert",
44
+ defaultRenderer: AlertRenderer,
45
+ props: {
46
+ type: alertType,
47
+ editable,
48
+ onTypeChange: editable ? handleTypeChange : void 0
49
+ }
50
+ }
51
+ ),
52
+ /* @__PURE__ */ jsx("div", { className: "rich-alert-content", children: /* @__PURE__ */ jsxs(LexicalNestedComposer, { initialEditor: contentEditor, children: [
53
+ /* @__PURE__ */ jsx(
54
+ RichTextPlugin,
55
+ {
56
+ contentEditable: /* @__PURE__ */ jsx(
57
+ ContentEditable$1,
58
+ {
59
+ className: "rich-alert-content-editable",
60
+ style: { outline: "none" },
61
+ "aria-placeholder": "",
62
+ placeholder: /* @__PURE__ */ jsx("span", { style: { display: "none" } })
63
+ }
64
+ ),
65
+ ErrorBoundary: LexicalErrorBoundary
66
+ }
67
+ ),
68
+ /* @__PURE__ */ jsx(ListPlugin, {}),
69
+ /* @__PURE__ */ jsx(LinkPlugin, {})
70
+ ] }) })
71
+ ] });
72
+ }
73
+ class AlertQuoteEditNode extends AlertQuoteNode {
74
+ static clone(node) {
75
+ return new AlertQuoteEditNode(
76
+ node.__alertType,
77
+ node.__contentEditor,
78
+ node.__key
79
+ );
80
+ }
81
+ static importJSON(serializedNode) {
82
+ const node = new AlertQuoteEditNode(serializedNode.alertType);
83
+ if (serializedNode.content) {
84
+ const editorState = node.__contentEditor.parseEditorState(
85
+ serializedNode.content
86
+ );
87
+ node.__contentEditor.setEditorState(editorState);
88
+ }
89
+ return node;
90
+ }
91
+ decorate(_editor, _config) {
92
+ return createElement(AlertEditDecorator, {
93
+ nodeKey: this.__key,
94
+ alertType: this.__alertType,
95
+ contentEditor: this.__contentEditor
96
+ });
97
+ }
98
+ }
99
+ function BannerEditDecorator({
100
+ nodeKey,
101
+ bannerType,
102
+ contentEditor
103
+ }) {
104
+ const [editor] = useLexicalComposerContext();
105
+ const editable = editor.isEditable();
106
+ const handleTypeChange = useCallback(
107
+ (newType) => {
108
+ editor.update(() => {
109
+ const node = $getNodeByKey(nodeKey);
110
+ if ($isBannerNode(node)) {
111
+ node.setBannerType(newType);
112
+ }
113
+ });
114
+ },
115
+ [editor, nodeKey]
116
+ );
117
+ return /* @__PURE__ */ jsxs("div", { className: "rich-banner-inner", children: [
118
+ /* @__PURE__ */ jsx(
119
+ RendererWrapper,
120
+ {
121
+ rendererKey: "Banner",
122
+ defaultRenderer: BannerRenderer,
123
+ props: {
124
+ type: bannerType,
125
+ editable,
126
+ onTypeChange: editable ? handleTypeChange : void 0
127
+ }
128
+ }
129
+ ),
130
+ /* @__PURE__ */ jsx("div", { className: "rich-banner-content", children: /* @__PURE__ */ jsxs(LexicalNestedComposer, { initialEditor: contentEditor, children: [
131
+ /* @__PURE__ */ jsx(
132
+ RichTextPlugin,
133
+ {
134
+ contentEditable: /* @__PURE__ */ jsx(
135
+ ContentEditable$1,
136
+ {
137
+ className: "rich-banner-content-editable",
138
+ style: { outline: "none" },
139
+ "aria-placeholder": "",
140
+ placeholder: /* @__PURE__ */ jsx("span", { style: { display: "none" } })
141
+ }
142
+ ),
143
+ ErrorBoundary: LexicalErrorBoundary
144
+ }
145
+ ),
146
+ /* @__PURE__ */ jsx(ListPlugin, {}),
147
+ /* @__PURE__ */ jsx(LinkPlugin, {})
148
+ ] }) })
149
+ ] });
150
+ }
151
+ class BannerEditNode extends BannerNode {
152
+ static clone(node) {
153
+ return new BannerEditNode(
154
+ node.__bannerType,
155
+ node.__contentEditor,
156
+ node.__key
157
+ );
158
+ }
159
+ static importJSON(serializedNode) {
160
+ const legacy = serializedNode;
161
+ const bannerType = normalizeBannerType(serializedNode.bannerType);
162
+ const node = new BannerEditNode(bannerType);
163
+ if (serializedNode.content) {
164
+ const editorState = node.__contentEditor.parseEditorState(
165
+ serializedNode.content
166
+ );
167
+ node.__contentEditor.setEditorState(editorState);
168
+ } else if (legacy.children) {
169
+ const content = {
170
+ root: {
171
+ children: legacy.children,
172
+ direction: null,
173
+ format: "",
174
+ indent: 0,
175
+ type: "root",
176
+ version: 1
177
+ }
178
+ };
179
+ const editorState = node.__contentEditor.parseEditorState(content);
180
+ node.__contentEditor.setEditorState(editorState);
181
+ }
182
+ return node;
183
+ }
184
+ decorate(_editor, _config) {
185
+ return createElement(BannerEditDecorator, {
186
+ nodeKey: this.__key,
187
+ bannerType: this.__bannerType,
188
+ contentEditor: this.__contentEditor
189
+ });
190
+ }
191
+ }
192
+ function CodeBlockEditDecorator({
193
+ nodeKey,
194
+ code,
195
+ language
196
+ }) {
197
+ const [editor] = useLexicalComposerContext();
198
+ const editable = editor.isEditable();
199
+ const handleCodeChange = useCallback(
200
+ (newCode) => {
201
+ editor.update(() => {
202
+ const node = $getNodeByKey(nodeKey);
203
+ if ($isCodeBlockNode(node)) {
204
+ node.setCode(newCode);
205
+ }
206
+ });
207
+ },
208
+ [editor, nodeKey]
209
+ );
210
+ return /* @__PURE__ */ jsx(
211
+ RendererWrapper,
212
+ {
213
+ rendererKey: "CodeBlock",
214
+ defaultRenderer: CodeBlockRenderer,
215
+ props: {
216
+ code,
217
+ language,
218
+ editable,
219
+ onCodeChange: editable ? handleCodeChange : void 0
220
+ }
221
+ }
222
+ );
223
+ }
224
+ class CodeBlockEditNode extends CodeBlockNode {
225
+ static clone(node) {
226
+ return new CodeBlockEditNode(node.__code, node.__language, node.__key);
227
+ }
228
+ static importJSON(serializedNode) {
229
+ return new CodeBlockEditNode(serializedNode.code, serializedNode.language);
230
+ }
231
+ decorate(_editor, _config) {
232
+ return createElement(CodeBlockEditDecorator, {
233
+ nodeKey: this.__key,
234
+ code: this.__code,
235
+ language: this.__language
236
+ });
237
+ }
238
+ }
239
+ function FootnoteSectionEditRenderer({
240
+ definitions
241
+ }) {
242
+ const [editor] = useLexicalComposerContext();
243
+ const { displayNumberMap } = useFootnoteDefinitions();
244
+ const sortedEntries = Object.entries(definitions).sort(
245
+ ([a], [b]) => (displayNumberMap[a] ?? 0) - (displayNumberMap[b] ?? 0)
246
+ );
247
+ const handleContentChange = useCallback(
248
+ (identifier, newContent) => {
249
+ editor.update(() => {
250
+ const sectionNodes = $nodesOfType(FootnoteSectionNode);
251
+ if (sectionNodes.length > 0) {
252
+ sectionNodes[0].setDefinition(identifier, newContent);
253
+ }
254
+ });
255
+ },
256
+ [editor]
257
+ );
258
+ const handleRemove = useCallback(
259
+ (identifier) => {
260
+ editor.update(() => {
261
+ const sectionNodes = $nodesOfType(FootnoteSectionNode);
262
+ if (sectionNodes.length > 0) {
263
+ sectionNodes[0].removeDefinition(identifier);
264
+ }
265
+ });
266
+ },
267
+ [editor]
268
+ );
269
+ if (sortedEntries.length === 0) return null;
270
+ return /* @__PURE__ */ jsxs(
271
+ "div",
272
+ {
273
+ className: "rich-footnote-section-content rich-footnote-section-edit",
274
+ role: "doc-endnotes",
275
+ children: [
276
+ /* @__PURE__ */ jsx("hr", { className: "rich-footnote-section-divider" }),
277
+ /* @__PURE__ */ jsx("ol", { className: "rich-footnote-section-list", children: sortedEntries.map(([identifier, content]) => {
278
+ const displayNum = displayNumberMap[identifier] ?? identifier;
279
+ return /* @__PURE__ */ jsxs(
280
+ "li",
281
+ {
282
+ id: `footnote-${identifier}`,
283
+ className: "rich-footnote-section-item rich-footnote-section-item-edit",
284
+ children: [
285
+ /* @__PURE__ */ jsxs("span", { className: "rich-footnote-section-item-num", children: [
286
+ displayNum,
287
+ "."
288
+ ] }),
289
+ /* @__PURE__ */ jsx(
290
+ "input",
291
+ {
292
+ type: "text",
293
+ className: "rich-footnote-section-item-input",
294
+ value: content,
295
+ onChange: (e) => handleContentChange(identifier, e.target.value),
296
+ placeholder: `Footnote content for [^${identifier}]`
297
+ }
298
+ ),
299
+ /* @__PURE__ */ jsx(
300
+ "button",
301
+ {
302
+ type: "button",
303
+ className: "rich-footnote-section-item-remove",
304
+ onClick: () => handleRemove(identifier),
305
+ "aria-label": `Remove footnote ${identifier}`,
306
+ children: "×"
307
+ }
308
+ )
309
+ ]
310
+ },
311
+ identifier
312
+ );
313
+ }) })
314
+ ]
315
+ }
316
+ );
317
+ }
318
+ class FootnoteSectionEditNode extends FootnoteSectionNode {
319
+ static getType() {
320
+ return "footnote-section";
321
+ }
322
+ static clone(node) {
323
+ return new FootnoteSectionEditNode({ ...node.__definitions }, node.__key);
324
+ }
325
+ static importJSON(serializedNode) {
326
+ return new FootnoteSectionEditNode(serializedNode.definitions);
327
+ }
328
+ decorate(_editor, _config) {
329
+ return createElement(FootnoteSectionEditRenderer, {
330
+ definitions: this.__definitions,
331
+ nodeKey: this.__key
332
+ });
333
+ }
334
+ }
335
+ const COL_OPTIONS = [1, 2, 3, 4];
336
+ function GridEditDecorator({
337
+ nodeKey,
338
+ cols: initialCols,
339
+ gap,
340
+ cellEditors
341
+ }) {
342
+ const [editor] = useLexicalComposerContext();
343
+ const [currentCols, setCurrentCols] = useState(initialCols);
344
+ useEffect(() => {
345
+ return editor.registerUpdateListener(({ editorState }) => {
346
+ editorState.read(() => {
347
+ const node = $getNodeByKey(nodeKey);
348
+ if ($isGridContainerNode(node)) {
349
+ setCurrentCols(node.getCols());
350
+ }
351
+ });
352
+ });
353
+ }, [editor, nodeKey]);
354
+ const handleSetCols = useCallback(
355
+ (cols) => {
356
+ editor.update(() => {
357
+ const node = $getNodeByKey(nodeKey);
358
+ if ($isGridContainerNode(node)) {
359
+ node.setCols(cols);
360
+ }
361
+ });
362
+ },
363
+ [editor, nodeKey]
364
+ );
365
+ const handleAddRow = useCallback(() => {
366
+ editor.update(() => {
367
+ const node = $getNodeByKey(nodeKey);
368
+ if ($isGridContainerNode(node)) {
369
+ node.addCells(node.getCols());
370
+ }
371
+ });
372
+ }, [editor, nodeKey]);
373
+ const handleRemoveRow = useCallback(() => {
374
+ editor.update(() => {
375
+ const node = $getNodeByKey(nodeKey);
376
+ if (!$isGridContainerNode(node)) return;
377
+ const cols = node.getCols();
378
+ const editors = node.getCellEditors();
379
+ if (editors.length <= cols) return;
380
+ const lastRow = editors.slice(-cols);
381
+ const allEmpty = lastRow.every(
382
+ (cellEditor) => cellEditor.getEditorState().read(() => $getRoot().getTextContentSize() === 0)
383
+ );
384
+ if (!allEmpty) return;
385
+ node.removeCells(cols);
386
+ });
387
+ }, [editor, nodeKey]);
388
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
389
+ /* @__PURE__ */ jsxs(
390
+ "div",
391
+ {
392
+ className: "rich-grid-toolbar",
393
+ onMouseDown: (e) => e.preventDefault(),
394
+ children: [
395
+ /* @__PURE__ */ jsx("span", { className: "rich-grid-toolbar-icon", children: /* @__PURE__ */ jsx(LayoutGrid, { size: 14 }) }),
396
+ COL_OPTIONS.map((n) => /* @__PURE__ */ jsx(
397
+ "button",
398
+ {
399
+ type: "button",
400
+ className: `rich-grid-col-btn${n === currentCols ? " rich-grid-col-btn-active" : ""}`,
401
+ onClick: () => handleSetCols(n),
402
+ "aria-label": `${n} columns`,
403
+ children: n
404
+ },
405
+ n
406
+ )),
407
+ /* @__PURE__ */ jsx("div", { className: "rich-grid-toolbar-divider" }),
408
+ /* @__PURE__ */ jsx(
409
+ "button",
410
+ {
411
+ type: "button",
412
+ className: "rich-grid-action-btn",
413
+ onClick: handleAddRow,
414
+ "aria-label": "Add row",
415
+ children: /* @__PURE__ */ jsx(Plus, { size: 14 })
416
+ }
417
+ ),
418
+ /* @__PURE__ */ jsx(
419
+ "button",
420
+ {
421
+ type: "button",
422
+ className: "rich-grid-action-btn",
423
+ onClick: handleRemoveRow,
424
+ "aria-label": "Remove row",
425
+ children: /* @__PURE__ */ jsx(Minus, { size: 14 })
426
+ }
427
+ )
428
+ ]
429
+ }
430
+ ),
431
+ /* @__PURE__ */ jsx(
432
+ "div",
433
+ {
434
+ className: "rich-grid-inner",
435
+ style: {
436
+ gridTemplateColumns: `repeat(${currentCols}, 1fr)`,
437
+ gap
438
+ },
439
+ children: cellEditors.map((cellEditor, i) => /* @__PURE__ */ jsx("div", { className: "rich-grid-cell", children: /* @__PURE__ */ jsxs(LexicalNestedComposer, { initialEditor: cellEditor, children: [
440
+ /* @__PURE__ */ jsx(
441
+ RichTextPlugin,
442
+ {
443
+ contentEditable: /* @__PURE__ */ jsx(
444
+ ContentEditable$1,
445
+ {
446
+ className: "rich-grid-cell-editable",
447
+ "aria-placeholder": "",
448
+ placeholder: /* @__PURE__ */ jsx("span", { style: { display: "none" } })
449
+ }
450
+ ),
451
+ ErrorBoundary: LexicalErrorBoundary
452
+ }
453
+ ),
454
+ /* @__PURE__ */ jsx(ListPlugin, {}),
455
+ /* @__PURE__ */ jsx(LinkPlugin, {})
456
+ ] }) }, i))
457
+ }
458
+ )
459
+ ] });
460
+ }
461
+ function createCellEditor() {
462
+ return createEditor({
463
+ namespace: "GridCell",
464
+ nodes: NESTED_EDITOR_NODES,
465
+ theme: editorTheme,
466
+ onError: (error) => {
467
+ console.error("[GridCell]", error);
468
+ }
469
+ });
470
+ }
471
+ class GridEditNode extends GridContainerNode {
472
+ static clone(node) {
473
+ return new GridEditNode(
474
+ node.__cols,
475
+ node.__gap,
476
+ [...node.__cellEditors],
477
+ node.__key
478
+ );
479
+ }
480
+ static importJSON(serializedNode) {
481
+ const legacy = serializedNode;
482
+ const cols = legacy.cols || 2;
483
+ const rawGap = legacy.gap;
484
+ const gap = typeof rawGap === "number" ? `${rawGap}px` : rawGap;
485
+ const node = new GridEditNode(cols, gap);
486
+ if (legacy.cells && legacy.cells.length > 0) {
487
+ const editors = legacy.cells.map((cellState) => {
488
+ const editor = createCellEditor();
489
+ const editorState = editor.parseEditorState(cellState);
490
+ editor.setEditorState(editorState);
491
+ return editor;
492
+ });
493
+ node.__cellEditors = editors;
494
+ } else if (legacy.children) {
495
+ const { children } = legacy;
496
+ const editors = children.map((child) => {
497
+ const editor = createCellEditor();
498
+ const content = {
499
+ root: {
500
+ children: [child],
501
+ direction: null,
502
+ format: "",
503
+ indent: 0,
504
+ type: "root",
505
+ version: 1
506
+ }
507
+ };
508
+ const editorState = editor.parseEditorState(content);
509
+ editor.setEditorState(editorState);
510
+ return editor;
511
+ });
512
+ node.__cellEditors = editors;
513
+ }
514
+ return node;
515
+ }
516
+ decorate(_editor, _config) {
517
+ return createElement(GridEditDecorator, {
518
+ nodeKey: this.__key,
519
+ cols: this.__cols,
520
+ gap: this.__gap,
521
+ cellEditors: this.__cellEditors
522
+ });
523
+ }
524
+ }
525
+ const customEditNodes = [
526
+ SpoilerNode,
527
+ MentionNode,
528
+ KaTeXInlineNode,
529
+ KaTeXBlockNode,
530
+ ImageNode,
531
+ AlertQuoteEditNode,
532
+ CodeBlockEditNode,
533
+ FootnoteNode,
534
+ FootnoteSectionEditNode,
535
+ TaskListItemNode,
536
+ VideoNode,
537
+ LinkCardNode,
538
+ DetailsNode,
539
+ GridEditNode,
540
+ BannerEditNode,
541
+ MermaidNode
542
+ ];
543
+ const allEditNodes = [
544
+ ...builtinNodes,
545
+ ...customEditNodes
546
+ ];
547
+ let _resolvedEditNodes = null;
548
+ function setResolvedEditNodes(nodes) {
549
+ _resolvedEditNodes = nodes;
550
+ }
551
+ function getResolvedEditNodes() {
552
+ return _resolvedEditNodes ?? allEditNodes;
553
+ }
554
+ const INSERT_ALERT_COMMAND = createCommand("INSERT_ALERT");
555
+ function AlertPlugin() {
556
+ const [editor] = useLexicalComposerContext();
557
+ useEffect(() => {
558
+ return editor.registerCommand(
559
+ INSERT_ALERT_COMMAND,
560
+ (alertType) => {
561
+ $insertNodes([$createAlertQuoteNode(alertType)]);
562
+ return true;
563
+ },
564
+ COMMAND_PRIORITY_EDITOR
565
+ );
566
+ }, [editor]);
567
+ return null;
568
+ }
569
+ function AutoFocusPlugin() {
570
+ const [editor] = useLexicalComposerContext();
571
+ useEffect(() => {
572
+ editor.focus();
573
+ }, [editor]);
574
+ return null;
575
+ }
576
+ const URL_REGEX = /https?:\/\/(?:www\.)?[-\w@:%.+~#=]{1,256}\.[a-zA-Z]{2}[-\w()@:%+.~#?&/=]*/;
577
+ const EMAIL_REGEX = /(?:[\w.%+-]+@[a-z0-9.-]+\.[a-z]{2,})/i;
578
+ const URL_MATCHER = createLinkMatcherWithRegExp(URL_REGEX);
579
+ const EMAIL_MATCHER = createLinkMatcherWithRegExp(
580
+ EMAIL_REGEX,
581
+ (text) => `mailto:${text}`
582
+ );
583
+ const DEFAULT_MATCHERS = [URL_MATCHER, EMAIL_MATCHER];
584
+ function AutoLinkPlugin({ matchers }) {
585
+ const [editor] = useLexicalComposerContext();
586
+ useEffect(() => {
587
+ return registerAutoLink(editor, {
588
+ matchers: matchers ?? DEFAULT_MATCHERS,
589
+ changeHandlers: []
590
+ });
591
+ }, [editor, matchers]);
592
+ return null;
593
+ }
594
+ function EditorRefPlugin({ onEditorReady }) {
595
+ const [editor] = useLexicalComposerContext();
596
+ const callbackRef = useRef(onEditorReady);
597
+ callbackRef.current = onEditorReady;
598
+ useEffect(() => {
599
+ callbackRef.current?.(editor);
600
+ return () => callbackRef.current?.(null);
601
+ }, [editor]);
602
+ return null;
603
+ }
604
+ const INSERT_IMAGE_COMMAND = createCommand(
605
+ "INSERT_IMAGE_COMMAND"
606
+ );
607
+ function ImagePlugin() {
608
+ const [editor] = useLexicalComposerContext();
609
+ useEffect(() => {
610
+ return editor.registerCommand(
611
+ INSERT_IMAGE_COMMAND,
612
+ (payload) => {
613
+ const imageNode = $createImageNode(payload);
614
+ $insertNodes([imageNode]);
615
+ return true;
616
+ },
617
+ COMMAND_PRIORITY_EDITOR
618
+ );
619
+ }, [editor]);
620
+ return null;
621
+ }
622
+ const INSERT_KATEX_INLINE_COMMAND = createCommand(
623
+ "INSERT_KATEX_INLINE"
624
+ );
625
+ const INSERT_KATEX_BLOCK_COMMAND = createCommand("INSERT_KATEX_BLOCK");
626
+ function KaTeXPlugin() {
627
+ const [editor] = useLexicalComposerContext();
628
+ useEffect(() => {
629
+ const unregisterInline = editor.registerCommand(
630
+ INSERT_KATEX_INLINE_COMMAND,
631
+ (equation) => {
632
+ const node = $createKaTeXInlineNode(equation);
633
+ $insertNodes([node]);
634
+ return true;
635
+ },
636
+ COMMAND_PRIORITY_EDITOR
637
+ );
638
+ const unregisterBlock = editor.registerCommand(
639
+ INSERT_KATEX_BLOCK_COMMAND,
640
+ (equation) => {
641
+ const node = $createKaTeXBlockNode(equation);
642
+ $insertNodes([node]);
643
+ return true;
644
+ },
645
+ COMMAND_PRIORITY_EDITOR
646
+ );
647
+ return () => {
648
+ unregisterInline();
649
+ unregisterBlock();
650
+ };
651
+ }, [editor]);
652
+ return null;
653
+ }
654
+ const ALERT_TYPE_MAP = {
655
+ NOTE: "note",
656
+ TIP: "tip",
657
+ IMPORTANT: "important",
658
+ WARNING: "warning",
659
+ CAUTION: "caution"
660
+ };
661
+ const GIT_ALERT_TRANSFORMER = {
662
+ dependencies: [AlertQuoteNode],
663
+ export: (node) => {
664
+ if (!$isAlertQuoteNode(node)) {
665
+ if ($isQuoteNode(node)) {
666
+ const lines2 = node.getTextContent().split("\n");
667
+ return lines2.map((line) => `> ${line}`).join("\n");
668
+ }
669
+ return null;
670
+ }
671
+ const type = node.getAlertType();
672
+ const typeKey = Object.keys(ALERT_TYPE_MAP).find(
673
+ (key) => ALERT_TYPE_MAP[key] === type
674
+ );
675
+ const textContent = node.getTextContent();
676
+ const lines = textContent.split("\n");
677
+ const firstLine = `> [!${typeKey || "NOTE"}]`;
678
+ const restLines = lines.map((line) => `> ${line}`);
679
+ return [firstLine, ...restLines].join("\n");
680
+ },
681
+ regExp: /^>\s*\[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION)\]\s*$/,
682
+ replace: (parentNode, children, match) => {
683
+ const typeKey = match[1];
684
+ const alertType = ALERT_TYPE_MAP[typeKey] || "note";
685
+ const alertNode = $createAlertQuoteNode(alertType);
686
+ const serializedChildren = children.map((child) => child.exportJSON());
687
+ const content = {
688
+ root: {
689
+ children: [
690
+ {
691
+ type: "paragraph",
692
+ children: serializedChildren,
693
+ direction: null,
694
+ format: "",
695
+ indent: 0,
696
+ textFormat: 0,
697
+ textStyle: "",
698
+ version: 1
699
+ }
700
+ ],
701
+ direction: null,
702
+ format: "",
703
+ indent: 0,
704
+ type: "root",
705
+ version: 1
706
+ }
707
+ };
708
+ const editorState = alertNode.getContentEditor().parseEditorState(content);
709
+ alertNode.getContentEditor().setEditorState(editorState);
710
+ parentNode.replace(alertNode);
711
+ },
712
+ type: "element"
713
+ };
714
+ const BANNER_TYPE_MAP = {
715
+ note: "note",
716
+ info: "note",
717
+ tip: "tip",
718
+ success: "tip",
719
+ important: "important",
720
+ warning: "warning",
721
+ warn: "warning",
722
+ error: "caution",
723
+ danger: "caution",
724
+ caution: "caution"
725
+ };
726
+ const CONTAINER_TRANSFORMER = {
727
+ dependencies: [BannerNode, DetailsNode],
728
+ export: (node) => {
729
+ if ($isBannerNode(node)) {
730
+ const type = node.getBannerType();
731
+ const content = node.getTextContent();
732
+ return `::: ${type}
733
+ ${content}
734
+ :::`;
735
+ }
736
+ if ($isDetailsNode(node)) {
737
+ const summary = node.getSummary();
738
+ const content = node.getTextContent();
739
+ return `::: details{summary="${summary}"}
740
+ ${content}
741
+ :::`;
742
+ }
743
+ return null;
744
+ },
745
+ regExp: /^:::\s*(\w+)(?:\{([^}]*)\})?\s*$/,
746
+ replace: (parentNode, children, match) => {
747
+ const type = match[1];
748
+ const params = match[2];
749
+ if (type in BANNER_TYPE_MAP) {
750
+ const bannerType = BANNER_TYPE_MAP[type];
751
+ const banner = $createBannerNode(bannerType);
752
+ const serializedChildren = children.map(
753
+ (child) => child.exportJSON()
754
+ );
755
+ const content = {
756
+ root: {
757
+ children: [
758
+ {
759
+ type: "paragraph",
760
+ children: serializedChildren,
761
+ direction: null,
762
+ format: "",
763
+ indent: 0,
764
+ textFormat: 0,
765
+ textStyle: "",
766
+ version: 1
767
+ }
768
+ ],
769
+ direction: null,
770
+ format: "",
771
+ indent: 0,
772
+ type: "root",
773
+ version: 1
774
+ }
775
+ };
776
+ const editorState = banner.getContentEditor().parseEditorState(content);
777
+ banner.getContentEditor().setEditorState(editorState);
778
+ parentNode.replace(banner);
779
+ return;
780
+ }
781
+ if (type === "details") {
782
+ const summaryMatch = params?.match(/summary="([^"]*)"/);
783
+ const summary = summaryMatch ? summaryMatch[1] : "Details";
784
+ const details = $createDetailsNode(summary);
785
+ children.forEach((child) => {
786
+ details.append(child);
787
+ });
788
+ parentNode.replace(details);
789
+ return;
790
+ }
791
+ const paragraph = $createParagraphNode();
792
+ paragraph.append($createTextNode(`::: ${type}`));
793
+ children.forEach((child) => {
794
+ paragraph.append(child);
795
+ });
796
+ parentNode.replace(paragraph);
797
+ },
798
+ type: "element"
799
+ };
800
+ const FOOTNOTE_TRANSFORMER = {
801
+ dependencies: [FootnoteNode],
802
+ export: (node) => {
803
+ if (!$isFootnoteNode(node)) return null;
804
+ return `[^${node.getIdentifier()}]`;
805
+ },
806
+ importRegExp: /\[\^(\w+)\]/,
807
+ regExp: /\[\^(\w+)\]$/,
808
+ replace: (textNode, match) => {
809
+ const footnoteNode = $createFootnoteNode(match[1]);
810
+ textNode.replace(footnoteNode);
811
+ },
812
+ trigger: "]",
813
+ type: "text-match"
814
+ };
815
+ const FOOTNOTE_SECTION_TRANSFORMER = {
816
+ dependencies: [FootnoteSectionNode],
817
+ export: (node) => {
818
+ if (!$isFootnoteSectionNode(node)) return null;
819
+ const definitions = node.getDefinitions();
820
+ return Object.entries(definitions).map(([id, content]) => `[^${id}]: ${content}`).join("\n");
821
+ },
822
+ regExp: /^\[\^(\w+)\]:\s+(.+)$/,
823
+ replace: (parentNode, _children, match) => {
824
+ const identifier = match[1];
825
+ const content = match[2];
826
+ const root = parentNode.getParent();
827
+ if (root) {
828
+ const children = root.getChildren();
829
+ for (const child of children) {
830
+ if ($isFootnoteSectionNode(child)) {
831
+ child.setDefinition(identifier, content);
832
+ parentNode.remove();
833
+ return;
834
+ }
835
+ }
836
+ }
837
+ const sectionNode = $createFootnoteSectionNode({ [identifier]: content });
838
+ parentNode.replace(sectionNode);
839
+ },
840
+ type: "element"
841
+ };
842
+ const INSERT_TRANSFORMER = {
843
+ format: ["underline"],
844
+ tag: "++",
845
+ type: "text-format"
846
+ };
847
+ const KATEX_INLINE_TRANSFORMER = {
848
+ dependencies: [KaTeXInlineNode],
849
+ export: (node) => {
850
+ if (!$isKaTeXInlineNode(node)) return null;
851
+ return `$${node.getEquation()}$`;
852
+ },
853
+ importRegExp: /\$([^$\n]+)\$/,
854
+ regExp: /\$([^$\n]+)\$$/,
855
+ replace: (textNode, match) => {
856
+ const katexNode = $createKaTeXInlineNode(match[1]);
857
+ textNode.replace(katexNode);
858
+ },
859
+ trigger: "$",
860
+ type: "text-match"
861
+ };
862
+ const KATEX_BLOCK_TRANSFORMER = {
863
+ dependencies: [KaTeXBlockNode],
864
+ export: (node) => {
865
+ if (!$isKaTeXBlockNode(node)) return null;
866
+ return `$$${node.getEquation()}$$`;
867
+ },
868
+ importRegExp: /\$\$([^$]+)\$\$/,
869
+ regExp: /\$\$([^$]+)\$\$$/,
870
+ replace: (textNode, match) => {
871
+ const katexNode = $createKaTeXBlockNode(match[1]);
872
+ textNode.replace(katexNode);
873
+ },
874
+ trigger: "$",
875
+ type: "text-match"
876
+ };
877
+ const MENTION_TRANSFORMER = {
878
+ dependencies: [MentionNode],
879
+ export: (node) => {
880
+ if (!$isMentionNode(node)) return null;
881
+ const displayName = node.getDisplayName();
882
+ const base = `{${node.getPlatform()}@${node.getHandle()}}`;
883
+ return displayName ? `[${displayName}]${base}` : base;
884
+ },
885
+ importRegExp: /(?:\[([^\]]+)\])?\{(\w+)@(\w[\w.-]*)\}/,
886
+ regExp: /(?:\[([^\]]+)\])?\{(\w+)@(\w[\w.-]*)\}$/,
887
+ replace: (textNode, match) => {
888
+ const displayName = match[1] || void 0;
889
+ const mentionNode = $createMentionNode(match[2], match[3], displayName);
890
+ textNode.replace(mentionNode);
891
+ },
892
+ trigger: "}",
893
+ type: "text-match"
894
+ };
895
+ const SPOILER_TRANSFORMER = {
896
+ dependencies: [SpoilerNode],
897
+ export: (node) => {
898
+ if (!$isSpoilerNode(node)) return null;
899
+ const textContent = node.getTextContent();
900
+ return `||${textContent}||`;
901
+ },
902
+ importRegExp: /\|\|(.+?)\|\|/,
903
+ regExp: /\|\|(.+?)\|\|$/,
904
+ replace: (textNode, match) => {
905
+ const spoilerNode = $createSpoilerNode();
906
+ spoilerNode.append($createTextNode(match[1]));
907
+ textNode.replace(spoilerNode);
908
+ },
909
+ trigger: "|",
910
+ type: "text-match"
911
+ };
912
+ const TASK_LIST_ITEM_TRANSFORMER = {
913
+ dependencies: [TaskListItemNode],
914
+ export: (node) => {
915
+ if (!$isTaskListItemNode(node)) return null;
916
+ const checked = node.getChecked();
917
+ const checkbox = checked ? "[x]" : "[ ]";
918
+ const textContent = node.getTextContent();
919
+ return `- ${checkbox} ${textContent}`;
920
+ },
921
+ regExp: /^(\s*)- \[([ x])\]\s/i,
922
+ replace: (parentNode, children, match) => {
923
+ const isChecked = match[2].toLowerCase() === "x";
924
+ const taskItem = $createTaskListItemNode(isChecked);
925
+ children.forEach((child) => {
926
+ taskItem.append(child);
927
+ });
928
+ parentNode.replace(taskItem);
929
+ },
930
+ type: "element"
931
+ };
932
+ const ALL_TRANSFORMERS = [
933
+ // Inline transformers
934
+ SPOILER_TRANSFORMER,
935
+ MENTION_TRANSFORMER,
936
+ FOOTNOTE_TRANSFORMER,
937
+ INSERT_TRANSFORMER,
938
+ KATEX_INLINE_TRANSFORMER,
939
+ // Block transformers (order matters - more specific first)
940
+ FOOTNOTE_SECTION_TRANSFORMER,
941
+ CONTAINER_TRANSFORMER,
942
+ GIT_ALERT_TRANSFORMER,
943
+ TASK_LIST_ITEM_TRANSFORMER,
944
+ KATEX_BLOCK_TRANSFORMER,
945
+ ...TRANSFORMERS
946
+ ];
947
+ function MarkdownShortcutsPlugin() {
948
+ return /* @__PURE__ */ jsx(MarkdownShortcutPlugin, { transformers: ALL_TRANSFORMERS });
949
+ }
950
+ const INSERT_MERMAID_COMMAND = createCommand("INSERT_MERMAID");
951
+ function MermaidPlugin() {
952
+ const [editor] = useLexicalComposerContext();
953
+ useEffect(() => {
954
+ return editor.registerCommand(
955
+ INSERT_MERMAID_COMMAND,
956
+ (diagram) => {
957
+ const node = $createMermaidNode(diagram);
958
+ $insertNodes([node]);
959
+ return true;
960
+ },
961
+ COMMAND_PRIORITY_EDITOR
962
+ );
963
+ }, [editor]);
964
+ return null;
965
+ }
966
+ function OnChangePlugin({ onChange, debounceMs }) {
967
+ const [editor] = useLexicalComposerContext();
968
+ const timerRef = useRef(void 0);
969
+ const onChangeRef = useRef(onChange);
970
+ onChangeRef.current = onChange;
971
+ useEffect(() => {
972
+ const unregister = editor.registerUpdateListener(({ editorState }) => {
973
+ const fn = onChangeRef.current;
974
+ if (!fn) return;
975
+ if (debounceMs && debounceMs > 0) {
976
+ clearTimeout(timerRef.current);
977
+ timerRef.current = setTimeout(() => {
978
+ fn(editorState.toJSON());
979
+ }, debounceMs);
980
+ } else {
981
+ fn(editorState.toJSON());
982
+ }
983
+ });
984
+ return () => {
985
+ clearTimeout(timerRef.current);
986
+ unregister();
987
+ };
988
+ }, [editor, debounceMs]);
989
+ return null;
990
+ }
991
+ function SubmitShortcutPlugin({ onSubmit }) {
992
+ const [editor] = useLexicalComposerContext();
993
+ useEffect(() => {
994
+ if (!onSubmit) return;
995
+ return editor.registerCommand(
996
+ KEY_ENTER_COMMAND,
997
+ (event) => {
998
+ if (event && (event.metaKey || event.ctrlKey)) {
999
+ event.preventDefault();
1000
+ onSubmit();
1001
+ return true;
1002
+ }
1003
+ return false;
1004
+ },
1005
+ COMMAND_PRIORITY_HIGH
1006
+ );
1007
+ }, [editor, onSubmit]);
1008
+ return null;
1009
+ }
1010
+ function ContentEditable({
1011
+ className,
1012
+ placeholder
1013
+ }) {
1014
+ return /* @__PURE__ */ jsx(
1015
+ "div",
1016
+ {
1017
+ className: "rich-editor__content-wrapper",
1018
+ style: { position: "relative" },
1019
+ children: /* @__PURE__ */ jsx(
1020
+ ContentEditable$1,
1021
+ {
1022
+ className: clsx("rich-editor__content", className),
1023
+ style: {
1024
+ outline: "none",
1025
+ minHeight: "100px",
1026
+ padding: "12px 16px"
1027
+ },
1028
+ "aria-placeholder": placeholder ?? "",
1029
+ placeholder: /* @__PURE__ */ jsx(
1030
+ "div",
1031
+ {
1032
+ className: "rich-editor__placeholder",
1033
+ style: {
1034
+ position: "absolute",
1035
+ top: "12px",
1036
+ left: "16px",
1037
+ color: "var(--rich-editor-text-secondary, #999)",
1038
+ pointerEvents: "none",
1039
+ userSelect: "none",
1040
+ display: placeholder ? void 0 : "none"
1041
+ },
1042
+ children: placeholder
1043
+ }
1044
+ )
1045
+ }
1046
+ )
1047
+ }
1048
+ );
1049
+ }
1050
+ function RichEditor({
1051
+ initialValue,
1052
+ onChange,
1053
+ variant = "article",
1054
+ theme = "light",
1055
+ placeholder = "Write something...",
1056
+ onSubmit,
1057
+ autoFocus = false,
1058
+ className,
1059
+ contentClassName,
1060
+ actions,
1061
+ onEditorReady,
1062
+ extraNodes,
1063
+ rendererConfig,
1064
+ debounceMs,
1065
+ children
1066
+ }) {
1067
+ const nodes = extraNodes ? [...allEditNodes, ...extraNodes] : allEditNodes;
1068
+ setResolvedEditNodes(nodes);
1069
+ const initialConfig = {
1070
+ namespace: "RichEditor",
1071
+ theme: editorTheme,
1072
+ nodes,
1073
+ editable: true,
1074
+ onError: (error) => {
1075
+ console.error("[RichEditor]", error);
1076
+ },
1077
+ ...initialValue ? { editorState: JSON.stringify(initialValue) } : {}
1078
+ };
1079
+ const variantClass = getVariantClass(variant, theme);
1080
+ return /* @__PURE__ */ jsx(PortalThemeProvider, { className: variantClass, children: /* @__PURE__ */ jsx(ColorSchemeProvider, { colorScheme: theme, children: /* @__PURE__ */ jsx(RendererConfigProvider, { config: rendererConfig, mode: "editor", children: /* @__PURE__ */ jsx(LexicalComposer, { initialConfig, children: /* @__PURE__ */ jsx(FootnotePlugin, { children: /* @__PURE__ */ jsxs("div", { className: clsx("rich-editor", variantClass, className), children: [
1081
+ /* @__PURE__ */ jsx(
1082
+ RichTextPlugin,
1083
+ {
1084
+ contentEditable: /* @__PURE__ */ jsx(
1085
+ ContentEditable,
1086
+ {
1087
+ className: contentClassName,
1088
+ placeholder
1089
+ }
1090
+ ),
1091
+ ErrorBoundary: LexicalErrorBoundary
1092
+ }
1093
+ ),
1094
+ /* @__PURE__ */ jsx(HistoryPlugin, {}),
1095
+ /* @__PURE__ */ jsx(ListPlugin, {}),
1096
+ /* @__PURE__ */ jsx(LinkPlugin, {}),
1097
+ /* @__PURE__ */ jsx(TabIndentationPlugin, {}),
1098
+ /* @__PURE__ */ jsx(TablePlugin, {}),
1099
+ /* @__PURE__ */ jsx(MarkdownShortcutsPlugin, {}),
1100
+ /* @__PURE__ */ jsx(OnChangePlugin, { onChange, debounceMs }),
1101
+ /* @__PURE__ */ jsx(SubmitShortcutPlugin, { onSubmit }),
1102
+ /* @__PURE__ */ jsx(ImagePlugin, {}),
1103
+ /* @__PURE__ */ jsx(KaTeXPlugin, {}),
1104
+ /* @__PURE__ */ jsx(AlertPlugin, {}),
1105
+ /* @__PURE__ */ jsx(MermaidPlugin, {}),
1106
+ /* @__PURE__ */ jsx(AutoLinkPlugin, {}),
1107
+ /* @__PURE__ */ jsx(EditorRefPlugin, { onEditorReady }),
1108
+ autoFocus && /* @__PURE__ */ jsx(AutoFocusPlugin, {}),
1109
+ children,
1110
+ actions && /* @__PURE__ */ jsx("div", { className: "rich-editor__actions", children: actions })
1111
+ ] }) }) }) }) }) });
1112
+ }
1113
+ export {
1114
+ FootnoteSectionEditNode as F,
1115
+ INSERT_ALERT_COMMAND as I,
1116
+ RichEditor as R,
1117
+ allEditNodes as a,
1118
+ INSERT_IMAGE_COMMAND as b,
1119
+ customEditNodes as c,
1120
+ INSERT_KATEX_BLOCK_COMMAND as d,
1121
+ INSERT_KATEX_INLINE_COMMAND as e,
1122
+ INSERT_MERMAID_COMMAND as f,
1123
+ getResolvedEditNodes as g,
1124
+ setResolvedEditNodes as s
1125
+ };