@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,2702 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
+ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
5
+ import { createContext, useMemo, use, createElement, useCallback, useState, useEffect, useRef } from "react";
6
+ import { CodeNode } from "@lexical/code";
7
+ import { HorizontalRuleNode } from "@lexical/extension";
8
+ import { LinkNode, AutoLinkNode } from "@lexical/link";
9
+ import { ListNode, ListItemNode } from "@lexical/list";
10
+ import { HeadingNode, QuoteNode } from "@lexical/rich-text";
11
+ import { TableNode, TableCellNode, TableRowNode } from "@lexical/table";
12
+ import { DecoratorNode, ElementNode, $insertNodes, $getRoot, createEditor, $getNodeByKey, $nodesOfType } from "lexical";
13
+ import { OctagonAlert, TriangleAlert, MessageSquareWarning, Lightbulb, Info, Flag, Code, ChevronDown, ChevronRight, LayoutGrid, ImageIcon, Sigma, Link, Workflow, Video } from "lucide-react";
14
+ import { ContentEditable } from "@lexical/react/LexicalContentEditable";
15
+ import { LexicalErrorBoundary } from "@lexical/react/LexicalErrorBoundary";
16
+ import { LexicalNestedComposer } from "@lexical/react/LexicalNestedComposer";
17
+ import { RichTextPlugin } from "@lexical/react/LexicalRichTextPlugin";
18
+ import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
19
+ import { TooltipRoot, TooltipTrigger, TooltipContent } from "@haklex/rich-editor-ui";
20
+ const PortalThemeContext = createContext({
21
+ className: ""
22
+ });
23
+ function PortalThemeProvider({
24
+ className,
25
+ children
26
+ }) {
27
+ return /* @__PURE__ */ jsx(
28
+ PortalThemeContext.Provider,
29
+ {
30
+ value: useMemo(() => ({ className }), [className]),
31
+ children
32
+ }
33
+ );
34
+ }
35
+ const RendererConfigContext = createContext({
36
+ config: void 0,
37
+ mode: "renderer"
38
+ });
39
+ function RendererConfigProvider({
40
+ config,
41
+ mode,
42
+ children
43
+ }) {
44
+ const value = useMemo(() => ({ config, mode }), [config, mode]);
45
+ return /* @__PURE__ */ jsx(RendererConfigContext.Provider, { value, children });
46
+ }
47
+ function useRendererConfig() {
48
+ return use(RendererConfigContext).config;
49
+ }
50
+ function useRendererMode() {
51
+ return use(RendererConfigContext).mode;
52
+ }
53
+ function RendererWrapper({
54
+ rendererKey,
55
+ defaultRenderer: DefaultRenderer,
56
+ props
57
+ }) {
58
+ const config = useRendererConfig();
59
+ const Renderer = config?.[rendererKey] ?? DefaultRenderer;
60
+ return /* @__PURE__ */ jsx(Renderer, { ...props });
61
+ }
62
+ function createRendererDecoration(rendererKey, defaultRenderer, props) {
63
+ return createElement(RendererWrapper, {
64
+ rendererKey,
65
+ defaultRenderer,
66
+ props
67
+ });
68
+ }
69
+ const InfoIcon = () => /* @__PURE__ */ jsx(Info, { size: 16 });
70
+ const LightbulbIcon = () => /* @__PURE__ */ jsx(Lightbulb, { size: 16 });
71
+ const MessageWarningIcon = () => /* @__PURE__ */ jsx(MessageSquareWarning, { size: 16 });
72
+ const TriangleAlertIcon = () => /* @__PURE__ */ jsx(TriangleAlert, { size: 16 });
73
+ const OctagonAlertIcon = () => /* @__PURE__ */ jsx(OctagonAlert, { size: 16 });
74
+ const ALERT_ICONS = {
75
+ note: InfoIcon,
76
+ tip: LightbulbIcon,
77
+ important: MessageWarningIcon,
78
+ warning: TriangleAlertIcon,
79
+ caution: OctagonAlertIcon
80
+ };
81
+ const AlertRenderer = ({ type }) => {
82
+ const Icon = ALERT_ICONS[type];
83
+ return /* @__PURE__ */ jsxs("div", { className: `rich-alert-header rich-alert-header-${type}`, children: [
84
+ /* @__PURE__ */ jsx("span", { className: "rich-alert-icon", children: /* @__PURE__ */ jsx(Icon, {}) }),
85
+ /* @__PURE__ */ jsx("span", { className: "rich-alert-label", children: ALERT_LABELS[type] })
86
+ ] });
87
+ };
88
+ function AlertReadOnlyDecorator({
89
+ alertType,
90
+ contentEditor
91
+ }) {
92
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
93
+ /* @__PURE__ */ jsx(
94
+ RendererWrapper,
95
+ {
96
+ rendererKey: "Alert",
97
+ defaultRenderer: AlertRenderer,
98
+ props: { type: alertType }
99
+ }
100
+ ),
101
+ /* @__PURE__ */ jsx("div", { className: "rich-alert-content", children: /* @__PURE__ */ jsx(LexicalNestedComposer, { initialEditor: contentEditor, children: /* @__PURE__ */ jsx(
102
+ RichTextPlugin,
103
+ {
104
+ contentEditable: /* @__PURE__ */ jsx(
105
+ ContentEditable,
106
+ {
107
+ className: "rich-alert-content-editable",
108
+ style: { outline: "none" },
109
+ "aria-placeholder": "",
110
+ placeholder: /* @__PURE__ */ jsx("span", { style: { display: "none" } })
111
+ }
112
+ ),
113
+ ErrorBoundary: LexicalErrorBoundary
114
+ }
115
+ ) }) })
116
+ ] });
117
+ }
118
+ const editorTheme = {
119
+ text: {
120
+ bold: "rich-text-bold",
121
+ italic: "rich-text-italic",
122
+ underline: "rich-text-underline",
123
+ strikethrough: "rich-text-strikethrough",
124
+ code: "rich-text-code",
125
+ highlight: "rich-text-highlight"
126
+ },
127
+ heading: {
128
+ h1: "rich-heading-h1",
129
+ h2: "rich-heading-h2",
130
+ h3: "rich-heading-h3",
131
+ h4: "rich-heading-h4",
132
+ h5: "rich-heading-h5",
133
+ h6: "rich-heading-h6"
134
+ },
135
+ list: {
136
+ ol: "rich-list-ol",
137
+ ul: "rich-list-ul",
138
+ listitem: "rich-list-item",
139
+ nested: {
140
+ listitem: "rich-list-nested-item"
141
+ }
142
+ },
143
+ quote: "rich-quote",
144
+ link: "rich-link",
145
+ paragraph: "rich-paragraph",
146
+ code: "rich-code-block",
147
+ table: "rich-table",
148
+ tableCell: "rich-table-cell",
149
+ tableCellHeader: "rich-table-cell-header",
150
+ tableScrollableWrapper: "rich-table-scrollable-wrapper",
151
+ /** Used by @lexical/extension HorizontalRuleNode */
152
+ hr: "rich-hr"
153
+ };
154
+ const FootnoteDefinitionsContext = createContext({
155
+ definitions: {},
156
+ displayNumberMap: {}
157
+ });
158
+ function FootnoteDefinitionsProvider({
159
+ definitions,
160
+ displayNumberMap,
161
+ children
162
+ }) {
163
+ const value = useMemo(
164
+ () => ({ definitions, displayNumberMap }),
165
+ [definitions, displayNumberMap]
166
+ );
167
+ return /* @__PURE__ */ jsx(FootnoteDefinitionsContext, { value, children });
168
+ }
169
+ function useFootnoteDefinitions() {
170
+ return use(FootnoteDefinitionsContext);
171
+ }
172
+ function useFootnoteContent(identifier) {
173
+ const { definitions } = use(FootnoteDefinitionsContext);
174
+ return definitions[identifier];
175
+ }
176
+ function useFootnoteDisplayNumber(identifier) {
177
+ const { displayNumberMap } = use(FootnoteDefinitionsContext);
178
+ return displayNumberMap[identifier];
179
+ }
180
+ function FootnoteRenderer({ identifier }) {
181
+ const content = useFootnoteContent(identifier);
182
+ const displayNumber = useFootnoteDisplayNumber(identifier);
183
+ const referenceId = `footnote-ref-${identifier}`;
184
+ const targetId = `footnote-${identifier}`;
185
+ const handleClick = useCallback(
186
+ (e) => {
187
+ const target = document.getElementById(targetId) || document.getElementById(`fn-${identifier}`);
188
+ if (!target) return;
189
+ e.preventDefault();
190
+ target.scrollIntoView({ behavior: "smooth", block: "center" });
191
+ target.classList.add("rich-footnote-highlight");
192
+ window.setTimeout(() => {
193
+ target.classList.remove("rich-footnote-highlight");
194
+ }, 1200);
195
+ },
196
+ [identifier, targetId]
197
+ );
198
+ const label = displayNumber ?? identifier;
199
+ return /* @__PURE__ */ jsx("span", { className: "rich-footnote-ref-wrapper", children: /* @__PURE__ */ jsxs(TooltipRoot, { children: [
200
+ /* @__PURE__ */ jsx(
201
+ TooltipTrigger,
202
+ {
203
+ render: (props) => /* @__PURE__ */ jsx(
204
+ "a",
205
+ {
206
+ ...props,
207
+ className: "rich-footnote-ref",
208
+ href: `#${targetId}`,
209
+ id: referenceId,
210
+ role: "doc-noteref",
211
+ "aria-label": content ? `Footnote ${label}: ${content}` : `Footnote ${label}`,
212
+ onClick: handleClick,
213
+ "data-footnote-ref": identifier,
214
+ children: label
215
+ }
216
+ )
217
+ }
218
+ ),
219
+ content ? /* @__PURE__ */ jsx(TooltipContent, { children: content }) : null
220
+ ] }) });
221
+ }
222
+ class FootnoteNode extends DecoratorNode {
223
+ constructor(identifier, key) {
224
+ super(key);
225
+ __publicField(this, "__identifier");
226
+ this.__identifier = identifier;
227
+ }
228
+ static getType() {
229
+ return "footnote";
230
+ }
231
+ static clone(node) {
232
+ return new FootnoteNode(node.__identifier, node.__key);
233
+ }
234
+ createDOM(_config) {
235
+ const sup = document.createElement("sup");
236
+ sup.className = "rich-footnote";
237
+ return sup;
238
+ }
239
+ updateDOM() {
240
+ return false;
241
+ }
242
+ isInline() {
243
+ return true;
244
+ }
245
+ static importJSON(serializedNode) {
246
+ return $createFootnoteNode(serializedNode.identifier);
247
+ }
248
+ exportJSON() {
249
+ return {
250
+ ...super.exportJSON(),
251
+ type: "footnote",
252
+ identifier: this.__identifier,
253
+ version: 1
254
+ };
255
+ }
256
+ getIdentifier() {
257
+ return this.getLatest().__identifier;
258
+ }
259
+ setIdentifier(identifier) {
260
+ const writable = this.getWritable();
261
+ writable.__identifier = identifier;
262
+ }
263
+ decorate(_editor, _config) {
264
+ return createRendererDecoration("Footnote", FootnoteRenderer, {
265
+ identifier: this.__identifier
266
+ });
267
+ }
268
+ }
269
+ function $createFootnoteNode(identifier) {
270
+ return new FootnoteNode(identifier);
271
+ }
272
+ function $isFootnoteNode(node) {
273
+ return node instanceof FootnoteNode;
274
+ }
275
+ let katexModule = null;
276
+ let katexLoadPromise = null;
277
+ function loadKaTeX() {
278
+ if (katexModule) return Promise.resolve(katexModule);
279
+ if (!katexLoadPromise) {
280
+ katexLoadPromise = import("katex").then((mod) => {
281
+ katexModule = mod;
282
+ return katexModule;
283
+ });
284
+ }
285
+ return katexLoadPromise;
286
+ }
287
+ function KaTeXRenderer({ equation, displayMode }) {
288
+ const [html, setHtml] = useState(null);
289
+ const [error, setError] = useState(null);
290
+ useEffect(() => {
291
+ let cancelled = false;
292
+ loadKaTeX().then((katex) => {
293
+ if (cancelled) return;
294
+ const rendered = katex.default.renderToString(equation, {
295
+ displayMode,
296
+ throwOnError: false
297
+ });
298
+ setHtml(rendered);
299
+ setError(null);
300
+ }).catch(() => {
301
+ if (cancelled) return;
302
+ setHtml(null);
303
+ setError("KaTeX is not available");
304
+ });
305
+ return () => {
306
+ cancelled = true;
307
+ };
308
+ }, [equation, displayMode]);
309
+ if (error) {
310
+ return /* @__PURE__ */ jsx("code", { className: "rich-katex-fallback", children: equation });
311
+ }
312
+ if (html) {
313
+ return /* @__PURE__ */ jsx(
314
+ "span",
315
+ {
316
+ className: displayMode ? "rich-katex-block" : "rich-katex-inline",
317
+ dangerouslySetInnerHTML: { __html: html }
318
+ }
319
+ );
320
+ }
321
+ return /* @__PURE__ */ jsx("code", { className: "rich-katex-fallback", children: equation });
322
+ }
323
+ class KaTeXInlineNode extends DecoratorNode {
324
+ constructor(equation, key) {
325
+ super(key);
326
+ __publicField(this, "__equation");
327
+ this.__equation = equation;
328
+ }
329
+ static getType() {
330
+ return "katex-inline";
331
+ }
332
+ static clone(node) {
333
+ return new KaTeXInlineNode(node.__equation, node.__key);
334
+ }
335
+ createDOM(_config) {
336
+ return document.createElement("span");
337
+ }
338
+ updateDOM() {
339
+ return false;
340
+ }
341
+ isInline() {
342
+ return true;
343
+ }
344
+ static importJSON(serializedNode) {
345
+ return $createKaTeXInlineNode(serializedNode.equation);
346
+ }
347
+ exportJSON() {
348
+ return {
349
+ ...super.exportJSON(),
350
+ type: "katex-inline",
351
+ equation: this.__equation,
352
+ version: 1
353
+ };
354
+ }
355
+ getEquation() {
356
+ return this.__equation;
357
+ }
358
+ setEquation(equation) {
359
+ const writable = this.getWritable();
360
+ writable.__equation = equation;
361
+ }
362
+ decorate(_editor, _config) {
363
+ return createRendererDecoration("KaTeX", KaTeXRenderer, {
364
+ equation: this.__equation,
365
+ displayMode: false
366
+ });
367
+ }
368
+ }
369
+ function $createKaTeXInlineNode(equation) {
370
+ return new KaTeXInlineNode(equation);
371
+ }
372
+ function $isKaTeXInlineNode(node) {
373
+ return node instanceof KaTeXInlineNode;
374
+ }
375
+ function MentionRenderer({ handle, displayName }) {
376
+ const normalizedHandle = handle.replace(/^@+/, "");
377
+ const label = displayName || normalizedHandle;
378
+ return /* @__PURE__ */ jsx("span", { className: "rich-mention rich-mention-plain", children: /* @__PURE__ */ jsxs("span", { className: "rich-mention-handle", children: [
379
+ "@",
380
+ label
381
+ ] }) });
382
+ }
383
+ class MentionNode extends DecoratorNode {
384
+ constructor(platform, handle, displayName, key) {
385
+ super(key);
386
+ __publicField(this, "__platform");
387
+ __publicField(this, "__handle");
388
+ __publicField(this, "__displayName");
389
+ this.__platform = platform;
390
+ this.__handle = handle;
391
+ this.__displayName = displayName;
392
+ }
393
+ static getType() {
394
+ return "mention";
395
+ }
396
+ static clone(node) {
397
+ return new MentionNode(
398
+ node.__platform,
399
+ node.__handle,
400
+ node.__displayName,
401
+ node.__key
402
+ );
403
+ }
404
+ createDOM(_config) {
405
+ const el = document.createElement("span");
406
+ el.style.display = "inline-flex";
407
+ el.style.alignItems = "center";
408
+ el.style.height = "1lh";
409
+ return el;
410
+ }
411
+ updateDOM() {
412
+ return false;
413
+ }
414
+ isInline() {
415
+ return true;
416
+ }
417
+ getPlatform() {
418
+ return this.getLatest().__platform;
419
+ }
420
+ getHandle() {
421
+ return this.getLatest().__handle;
422
+ }
423
+ getDisplayName() {
424
+ return this.getLatest().__displayName;
425
+ }
426
+ static importJSON(serializedNode) {
427
+ return $createMentionNode(
428
+ serializedNode.platform,
429
+ serializedNode.handle,
430
+ serializedNode.displayName
431
+ );
432
+ }
433
+ exportJSON() {
434
+ return {
435
+ ...super.exportJSON(),
436
+ type: "mention",
437
+ platform: this.__platform,
438
+ handle: this.__handle,
439
+ ...this.__displayName ? { displayName: this.__displayName } : {},
440
+ version: 1
441
+ };
442
+ }
443
+ decorate(_editor, _config) {
444
+ return createRendererDecoration("Mention", MentionRenderer, {
445
+ platform: this.__platform,
446
+ handle: this.__handle,
447
+ displayName: this.__displayName
448
+ });
449
+ }
450
+ }
451
+ function $createMentionNode(platform, handle, displayName) {
452
+ return new MentionNode(platform, handle, displayName);
453
+ }
454
+ function $isMentionNode(node) {
455
+ return node instanceof MentionNode;
456
+ }
457
+ class SpoilerNode extends ElementNode {
458
+ static getType() {
459
+ return "spoiler";
460
+ }
461
+ static clone(node) {
462
+ return new SpoilerNode(node.__key);
463
+ }
464
+ constructor(key) {
465
+ super(key);
466
+ }
467
+ createDOM(_config) {
468
+ const span = document.createElement("span");
469
+ span.className = "rich-spoiler";
470
+ span.setAttribute("role", "button");
471
+ span.setAttribute("tabindex", "0");
472
+ span.setAttribute("aria-label", "Spoiler (click to reveal)");
473
+ const toggle = () => {
474
+ if (span.isContentEditable) return;
475
+ const revealed = span.classList.toggle("rich-spoiler-revealed");
476
+ span.setAttribute(
477
+ "aria-label",
478
+ revealed ? "Spoiler (revealed)" : "Spoiler (click to reveal)"
479
+ );
480
+ };
481
+ span.addEventListener("click", toggle);
482
+ span.addEventListener("keydown", (e) => {
483
+ if (e.key === "Enter" || e.key === " ") {
484
+ e.preventDefault();
485
+ toggle();
486
+ }
487
+ });
488
+ return span;
489
+ }
490
+ updateDOM() {
491
+ return false;
492
+ }
493
+ static importJSON(_serializedNode) {
494
+ return $createSpoilerNode();
495
+ }
496
+ exportJSON() {
497
+ return {
498
+ ...super.exportJSON(),
499
+ type: "spoiler",
500
+ version: 1
501
+ };
502
+ }
503
+ canInsertTextBefore() {
504
+ return true;
505
+ }
506
+ canInsertTextAfter() {
507
+ return true;
508
+ }
509
+ isInline() {
510
+ return true;
511
+ }
512
+ }
513
+ function $createSpoilerNode() {
514
+ return new SpoilerNode();
515
+ }
516
+ function $isSpoilerNode(node) {
517
+ return node instanceof SpoilerNode;
518
+ }
519
+ class TaskListItemNode extends ElementNode {
520
+ constructor(checked, key) {
521
+ super(key);
522
+ __publicField(this, "__checked");
523
+ this.__checked = checked;
524
+ }
525
+ static getType() {
526
+ return "task-list-item";
527
+ }
528
+ static clone(node) {
529
+ return new TaskListItemNode(node.__checked, node.__key);
530
+ }
531
+ createDOM(_config) {
532
+ const li = document.createElement("li");
533
+ li.className = "rich-task-list-item";
534
+ li.setAttribute("role", "listitem");
535
+ const checkbox = document.createElement("input");
536
+ checkbox.type = "checkbox";
537
+ checkbox.checked = this.__checked;
538
+ checkbox.disabled = true;
539
+ checkbox.className = "rich-task-checkbox";
540
+ li.prepend(checkbox);
541
+ if (this.__checked) {
542
+ li.classList.add("rich-task-checked");
543
+ }
544
+ return li;
545
+ }
546
+ updateDOM(prevNode, dom) {
547
+ if (prevNode.__checked !== this.__checked) {
548
+ const checkbox = dom.querySelector(
549
+ 'input[type="checkbox"]'
550
+ );
551
+ if (checkbox) {
552
+ checkbox.checked = this.__checked;
553
+ }
554
+ dom.classList.toggle("rich-task-checked", this.__checked);
555
+ }
556
+ return false;
557
+ }
558
+ static importJSON(serializedNode) {
559
+ return $createTaskListItemNode(serializedNode.checked);
560
+ }
561
+ exportJSON() {
562
+ return {
563
+ ...super.exportJSON(),
564
+ type: "task-list-item",
565
+ checked: this.__checked,
566
+ version: 1
567
+ };
568
+ }
569
+ getChecked() {
570
+ return this.getLatest().__checked;
571
+ }
572
+ setChecked(checked) {
573
+ const writable = this.getWritable();
574
+ writable.__checked = checked;
575
+ }
576
+ toggleChecked() {
577
+ this.setChecked(!this.getChecked());
578
+ }
579
+ isInline() {
580
+ return false;
581
+ }
582
+ }
583
+ function $createTaskListItemNode(checked = false) {
584
+ return new TaskListItemNode(checked);
585
+ }
586
+ function $isTaskListItemNode(node) {
587
+ return node instanceof TaskListItemNode;
588
+ }
589
+ const NESTED_EDITOR_NODES = [
590
+ HeadingNode,
591
+ QuoteNode,
592
+ ListNode,
593
+ ListItemNode,
594
+ LinkNode,
595
+ AutoLinkNode,
596
+ HorizontalRuleNode,
597
+ CodeNode,
598
+ TableNode,
599
+ TableCellNode,
600
+ TableRowNode,
601
+ SpoilerNode,
602
+ MentionNode,
603
+ TaskListItemNode,
604
+ FootnoteNode,
605
+ KaTeXInlineNode
606
+ ];
607
+ const ALERT_TYPES = [
608
+ "note",
609
+ "tip",
610
+ "important",
611
+ "warning",
612
+ "caution"
613
+ ];
614
+ const ALERT_LABELS = {
615
+ note: "Note",
616
+ tip: "Tip",
617
+ important: "Important",
618
+ warning: "Warning",
619
+ caution: "Caution"
620
+ };
621
+ function createContentEditor$1() {
622
+ return createEditor({
623
+ namespace: "AlertContent",
624
+ nodes: NESTED_EDITOR_NODES,
625
+ theme: editorTheme,
626
+ onError: (error) => {
627
+ console.error("[AlertContent]", error);
628
+ }
629
+ });
630
+ }
631
+ const _AlertQuoteNode = class _AlertQuoteNode extends DecoratorNode {
632
+ constructor(alertType, contentEditor, key) {
633
+ super(key);
634
+ __publicField(this, "__alertType");
635
+ __publicField(this, "__contentEditor");
636
+ this.__alertType = alertType;
637
+ this.__contentEditor = contentEditor || createContentEditor$1();
638
+ }
639
+ static getType() {
640
+ return "alert-quote";
641
+ }
642
+ static clone(node) {
643
+ return new _AlertQuoteNode(
644
+ node.__alertType,
645
+ node.__contentEditor,
646
+ node.__key
647
+ );
648
+ }
649
+ createDOM(_config) {
650
+ const div = document.createElement("div");
651
+ div.className = `rich-alert rich-alert-${this.__alertType}`;
652
+ return div;
653
+ }
654
+ updateDOM(prevNode, dom) {
655
+ if (prevNode.__alertType !== this.__alertType) {
656
+ dom.className = `rich-alert rich-alert-${this.__alertType}`;
657
+ }
658
+ return false;
659
+ }
660
+ isInline() {
661
+ return false;
662
+ }
663
+ getAlertType() {
664
+ return this.__alertType;
665
+ }
666
+ setAlertType(alertType) {
667
+ const writable = this.getWritable();
668
+ writable.__alertType = alertType;
669
+ }
670
+ getContentEditor() {
671
+ return this.__contentEditor;
672
+ }
673
+ getTextContent() {
674
+ return this.__contentEditor.getEditorState().read(() => {
675
+ return $getRoot().getTextContent();
676
+ });
677
+ }
678
+ static importJSON(serializedNode) {
679
+ const node = new _AlertQuoteNode(serializedNode.alertType);
680
+ if (serializedNode.content) {
681
+ const editorState = node.__contentEditor.parseEditorState(
682
+ serializedNode.content
683
+ );
684
+ node.__contentEditor.setEditorState(editorState);
685
+ }
686
+ return node;
687
+ }
688
+ exportJSON() {
689
+ return {
690
+ ...super.exportJSON(),
691
+ type: "alert-quote",
692
+ alertType: this.__alertType,
693
+ content: this.__contentEditor.getEditorState().toJSON(),
694
+ version: 1
695
+ };
696
+ }
697
+ decorate(_editor, _config) {
698
+ return createElement(AlertReadOnlyDecorator, {
699
+ alertType: this.__alertType,
700
+ contentEditor: this.__contentEditor
701
+ });
702
+ }
703
+ };
704
+ __publicField(_AlertQuoteNode, "slashMenuItems", [
705
+ {
706
+ title: "Callout",
707
+ icon: createElement(Info, { size: 20 }),
708
+ description: "Info callout block",
709
+ keywords: ["alert", "note", "info", "callout"],
710
+ section: "ADVANCED",
711
+ onSelect: (editor) => {
712
+ editor.update(() => {
713
+ $insertNodes([$createAlertQuoteNode("note")]);
714
+ });
715
+ }
716
+ },
717
+ {
718
+ title: "Tip",
719
+ icon: createElement(Lightbulb, { size: 20 }),
720
+ description: "Highlight a useful tip",
721
+ keywords: ["alert", "tip", "hint"],
722
+ section: "ADVANCED",
723
+ onSelect: (editor) => {
724
+ editor.update(() => {
725
+ $insertNodes([$createAlertQuoteNode("tip")]);
726
+ });
727
+ }
728
+ },
729
+ {
730
+ title: "Warning",
731
+ icon: createElement(TriangleAlert, { size: 20 }),
732
+ description: "Warn about something",
733
+ keywords: ["alert", "warning", "caution"],
734
+ section: "ADVANCED",
735
+ onSelect: (editor) => {
736
+ editor.update(() => {
737
+ $insertNodes([$createAlertQuoteNode("warning")]);
738
+ });
739
+ }
740
+ }
741
+ ]);
742
+ let AlertQuoteNode = _AlertQuoteNode;
743
+ function $createAlertQuoteNode(alertType) {
744
+ return new AlertQuoteNode(alertType);
745
+ }
746
+ function $isAlertQuoteNode(node) {
747
+ return node instanceof AlertQuoteNode;
748
+ }
749
+ const BannerRenderer = ({ type }) => {
750
+ return /* @__PURE__ */ jsx("span", { className: `rich-banner-icon rich-banner-icon-${type}` });
751
+ };
752
+ function BannerReadOnlyDecorator({
753
+ bannerType,
754
+ contentEditor
755
+ }) {
756
+ return /* @__PURE__ */ jsxs("div", { className: "rich-banner-inner", children: [
757
+ /* @__PURE__ */ jsx(
758
+ RendererWrapper,
759
+ {
760
+ rendererKey: "Banner",
761
+ defaultRenderer: BannerRenderer,
762
+ props: { type: bannerType }
763
+ }
764
+ ),
765
+ /* @__PURE__ */ jsx("div", { className: "rich-banner-content", children: /* @__PURE__ */ jsx(LexicalNestedComposer, { initialEditor: contentEditor, children: /* @__PURE__ */ jsx(
766
+ RichTextPlugin,
767
+ {
768
+ contentEditable: /* @__PURE__ */ jsx(
769
+ ContentEditable,
770
+ {
771
+ className: "rich-banner-content-editable",
772
+ style: { outline: "none" },
773
+ "aria-placeholder": "",
774
+ placeholder: /* @__PURE__ */ jsx("span", { style: { display: "none" } })
775
+ }
776
+ ),
777
+ ErrorBoundary: LexicalErrorBoundary
778
+ }
779
+ ) }) })
780
+ ] });
781
+ }
782
+ const LEGACY_TYPE_MAP = {
783
+ info: "note",
784
+ success: "tip",
785
+ error: "caution"
786
+ };
787
+ function normalizeBannerType(type) {
788
+ if (type in LEGACY_TYPE_MAP) return LEGACY_TYPE_MAP[type];
789
+ return type || "note";
790
+ }
791
+ const BANNER_TYPES = [
792
+ "note",
793
+ "tip",
794
+ "important",
795
+ "warning",
796
+ "caution"
797
+ ];
798
+ const BANNER_LABELS = {
799
+ note: "Note",
800
+ tip: "Tip",
801
+ important: "Important",
802
+ warning: "Warning",
803
+ caution: "Caution"
804
+ };
805
+ function createContentEditor() {
806
+ return createEditor({
807
+ namespace: "BannerContent",
808
+ nodes: NESTED_EDITOR_NODES,
809
+ theme: editorTheme,
810
+ onError: (error) => {
811
+ console.error("[BannerContent]", error);
812
+ }
813
+ });
814
+ }
815
+ const _BannerNode = class _BannerNode extends DecoratorNode {
816
+ constructor(bannerType, contentEditor, key) {
817
+ super(key);
818
+ __publicField(this, "__bannerType");
819
+ __publicField(this, "__contentEditor");
820
+ this.__bannerType = bannerType;
821
+ this.__contentEditor = contentEditor || createContentEditor();
822
+ }
823
+ static getType() {
824
+ return "banner";
825
+ }
826
+ static clone(node) {
827
+ return new _BannerNode(node.__bannerType, node.__contentEditor, node.__key);
828
+ }
829
+ createDOM(_config) {
830
+ const div = document.createElement("div");
831
+ div.className = `rich-banner rich-banner-${this.__bannerType}`;
832
+ return div;
833
+ }
834
+ updateDOM(prevNode, dom) {
835
+ if (prevNode.__bannerType !== this.__bannerType) {
836
+ dom.className = `rich-banner rich-banner-${this.__bannerType}`;
837
+ }
838
+ return false;
839
+ }
840
+ isInline() {
841
+ return false;
842
+ }
843
+ getBannerType() {
844
+ return this.__bannerType;
845
+ }
846
+ setBannerType(bannerType) {
847
+ const writable = this.getWritable();
848
+ writable.__bannerType = bannerType;
849
+ }
850
+ getContentEditor() {
851
+ return this.__contentEditor;
852
+ }
853
+ getTextContent() {
854
+ return this.__contentEditor.getEditorState().read(() => {
855
+ return $getRoot().getTextContent();
856
+ });
857
+ }
858
+ static importJSON(serializedNode) {
859
+ const legacy = serializedNode;
860
+ const bannerType = normalizeBannerType(serializedNode.bannerType);
861
+ const node = new _BannerNode(bannerType);
862
+ if (serializedNode.content) {
863
+ const editorState = node.__contentEditor.parseEditorState(
864
+ serializedNode.content
865
+ );
866
+ node.__contentEditor.setEditorState(editorState);
867
+ } else if (legacy.children) {
868
+ const content = {
869
+ root: {
870
+ children: legacy.children,
871
+ direction: null,
872
+ format: "",
873
+ indent: 0,
874
+ type: "root",
875
+ version: 1
876
+ }
877
+ };
878
+ const editorState = node.__contentEditor.parseEditorState(content);
879
+ node.__contentEditor.setEditorState(editorState);
880
+ }
881
+ return node;
882
+ }
883
+ exportJSON() {
884
+ return {
885
+ ...super.exportJSON(),
886
+ type: "banner",
887
+ bannerType: this.__bannerType,
888
+ content: this.__contentEditor.getEditorState().toJSON(),
889
+ version: 1
890
+ };
891
+ }
892
+ decorate(_editor, _config) {
893
+ return createElement(BannerReadOnlyDecorator, {
894
+ bannerType: this.__bannerType,
895
+ contentEditor: this.__contentEditor
896
+ });
897
+ }
898
+ };
899
+ __publicField(_BannerNode, "slashMenuItems", [
900
+ {
901
+ title: "Banner",
902
+ icon: createElement(Flag, { size: 20 }),
903
+ description: "Highlighted banner block",
904
+ keywords: ["banner", "notice", "announcement"],
905
+ section: "ADVANCED",
906
+ onSelect: (editor) => {
907
+ editor.update(() => {
908
+ $insertNodes([$createBannerNode("note")]);
909
+ });
910
+ }
911
+ }
912
+ ]);
913
+ let BannerNode = _BannerNode;
914
+ function $createBannerNode(bannerType) {
915
+ return new BannerNode(bannerType);
916
+ }
917
+ function $isBannerNode(node) {
918
+ return node instanceof BannerNode;
919
+ }
920
+ const ColorSchemeContext = createContext("light");
921
+ function ColorSchemeProvider({
922
+ colorScheme,
923
+ children
924
+ }) {
925
+ return /* @__PURE__ */ jsx(ColorSchemeContext.Provider, { value: colorScheme, children });
926
+ }
927
+ function useColorScheme() {
928
+ return use(ColorSchemeContext);
929
+ }
930
+ let codeToHtmlFn = null;
931
+ let shikiLoadPromise = null;
932
+ function loadCodeToHtml() {
933
+ if (codeToHtmlFn) return Promise.resolve(codeToHtmlFn);
934
+ if (!shikiLoadPromise) {
935
+ shikiLoadPromise = import("shiki/bundle/web").then((mod) => {
936
+ codeToHtmlFn = mod.codeToHtml;
937
+ return mod.codeToHtml;
938
+ }).catch((err) => {
939
+ shikiLoadPromise = null;
940
+ throw err;
941
+ });
942
+ }
943
+ return shikiLoadPromise;
944
+ }
945
+ function CodeBlockRenderer({
946
+ code,
947
+ language,
948
+ showLineNumbers
949
+ }) {
950
+ const colorScheme = useColorScheme();
951
+ const shikiTheme = colorScheme === "dark" ? "github-dark" : "github-light";
952
+ const [highlightedHtml, setHighlightedHtml] = useState(null);
953
+ const [copied, setCopied] = useState(false);
954
+ const copyTimerRef = useRef(void 0);
955
+ useEffect(() => {
956
+ let cancelled = false;
957
+ setHighlightedHtml(null);
958
+ loadCodeToHtml().then(
959
+ (toHtml) => toHtml(code, {
960
+ lang: language,
961
+ theme: shikiTheme
962
+ })
963
+ ).then((html) => {
964
+ if (!cancelled) {
965
+ setHighlightedHtml(html);
966
+ }
967
+ }).catch(() => {
968
+ if (!cancelled) {
969
+ setHighlightedHtml(null);
970
+ }
971
+ });
972
+ return () => {
973
+ cancelled = true;
974
+ };
975
+ }, [code, language, shikiTheme]);
976
+ useEffect(() => {
977
+ return () => clearTimeout(copyTimerRef.current);
978
+ }, []);
979
+ const handleCopy = useCallback(() => {
980
+ navigator.clipboard.writeText(code).then(() => {
981
+ setCopied(true);
982
+ clearTimeout(copyTimerRef.current);
983
+ copyTimerRef.current = setTimeout(() => setCopied(false), 2e3);
984
+ }).catch(() => {
985
+ });
986
+ }, [code]);
987
+ const header = language ? /* @__PURE__ */ jsxs("div", { className: "rich-code-block-header", children: [
988
+ /* @__PURE__ */ jsx("span", { className: "rich-code-block-lang", children: language }),
989
+ /* @__PURE__ */ jsx(
990
+ "button",
991
+ {
992
+ type: "button",
993
+ className: "rich-code-block-copy",
994
+ onClick: handleCopy,
995
+ "aria-label": copied ? "Copied to clipboard" : "Copy code",
996
+ children: copied ? "Copied" : "Copy"
997
+ }
998
+ )
999
+ ] }) : null;
1000
+ const wrapperClass = showLineNumbers ? "rich-code-block rich-code-block-numbered" : "rich-code-block";
1001
+ if (highlightedHtml) {
1002
+ return /* @__PURE__ */ jsxs("div", { className: wrapperClass, children: [
1003
+ header,
1004
+ /* @__PURE__ */ jsx("div", { dangerouslySetInnerHTML: { __html: highlightedHtml } })
1005
+ ] });
1006
+ }
1007
+ const lines = code.split("\n");
1008
+ return /* @__PURE__ */ jsxs("div", { className: wrapperClass, children: [
1009
+ header,
1010
+ /* @__PURE__ */ jsx("pre", { children: /* @__PURE__ */ jsx("code", { children: lines.map((line, i) => /* @__PURE__ */ jsxs("span", { className: "line", children: [
1011
+ line,
1012
+ i < lines.length - 1 ? "\n" : ""
1013
+ ] }, i)) }) })
1014
+ ] });
1015
+ }
1016
+ const _CodeBlockNode = class _CodeBlockNode extends DecoratorNode {
1017
+ constructor(code, language, key) {
1018
+ super(key);
1019
+ __publicField(this, "__code");
1020
+ __publicField(this, "__language");
1021
+ this.__code = code;
1022
+ this.__language = language;
1023
+ }
1024
+ static getType() {
1025
+ return "code-block";
1026
+ }
1027
+ static clone(node) {
1028
+ return new _CodeBlockNode(node.__code, node.__language, node.__key);
1029
+ }
1030
+ createDOM(_config) {
1031
+ const div = document.createElement("div");
1032
+ div.className = "rich-code-block-wrapper";
1033
+ return div;
1034
+ }
1035
+ updateDOM() {
1036
+ return false;
1037
+ }
1038
+ isInline() {
1039
+ return false;
1040
+ }
1041
+ static importJSON(serializedNode) {
1042
+ return $createCodeBlockNode(serializedNode.code, serializedNode.language);
1043
+ }
1044
+ exportJSON() {
1045
+ return {
1046
+ ...super.exportJSON(),
1047
+ type: "code-block",
1048
+ code: this.__code,
1049
+ language: this.__language,
1050
+ version: 1
1051
+ };
1052
+ }
1053
+ getCode() {
1054
+ return this.__code;
1055
+ }
1056
+ setCode(code) {
1057
+ const writable = this.getWritable();
1058
+ writable.__code = code;
1059
+ }
1060
+ getLanguage() {
1061
+ return this.__language;
1062
+ }
1063
+ setLanguage(language) {
1064
+ const writable = this.getWritable();
1065
+ writable.__language = language;
1066
+ }
1067
+ decorate(_editor, _config) {
1068
+ return createRendererDecoration("CodeBlock", CodeBlockRenderer, {
1069
+ code: this.__code,
1070
+ language: this.__language
1071
+ });
1072
+ }
1073
+ };
1074
+ __publicField(_CodeBlockNode, "slashMenuItems", [
1075
+ {
1076
+ title: "Code Block",
1077
+ icon: createElement(Code, { size: 20 }),
1078
+ description: "Syntax-highlighted code",
1079
+ keywords: ["code", "snippet", "codeblock"],
1080
+ section: "MEDIA",
1081
+ onSelect: (editor) => {
1082
+ editor.update(() => {
1083
+ $insertNodes([$createCodeBlockNode("", "text")]);
1084
+ });
1085
+ }
1086
+ }
1087
+ ]);
1088
+ let CodeBlockNode = _CodeBlockNode;
1089
+ function $createCodeBlockNode(code, language) {
1090
+ return new CodeBlockNode(code, language);
1091
+ }
1092
+ function $isCodeBlockNode(node) {
1093
+ return node instanceof CodeBlockNode;
1094
+ }
1095
+ const SVG_NS = "http://www.w3.org/2000/svg";
1096
+ const DEFAULT_ATTRS = {
1097
+ xmlns: SVG_NS,
1098
+ width: "24",
1099
+ height: "24",
1100
+ viewBox: "0 0 24 24",
1101
+ fill: "none",
1102
+ stroke: "currentColor",
1103
+ "stroke-width": "2",
1104
+ "stroke-linecap": "round",
1105
+ "stroke-linejoin": "round"
1106
+ };
1107
+ function createLucideSvg(iconNode, attrs = {}) {
1108
+ const svg = document.createElementNS(SVG_NS, "svg");
1109
+ const merged = { ...DEFAULT_ATTRS, ...attrs };
1110
+ for (const [k, v] of Object.entries(merged)) {
1111
+ svg.setAttribute(k, v);
1112
+ }
1113
+ for (const [tag, elAttrs] of iconNode) {
1114
+ const el = document.createElementNS(SVG_NS, tag);
1115
+ for (const [k, v] of Object.entries(elAttrs)) {
1116
+ if (k === "key") continue;
1117
+ el.setAttribute(k, v);
1118
+ }
1119
+ svg.append(el);
1120
+ }
1121
+ return svg;
1122
+ }
1123
+ const ChevronDownIconNode = ChevronDown.iconNode ?? ChevronDown.__iconNode ?? [];
1124
+ const _DetailsNode = class _DetailsNode extends ElementNode {
1125
+ constructor(summary, open = false, key) {
1126
+ super(key);
1127
+ __publicField(this, "__summary");
1128
+ __publicField(this, "__open");
1129
+ this.__summary = summary;
1130
+ this.__open = open;
1131
+ }
1132
+ static getType() {
1133
+ return "details";
1134
+ }
1135
+ static clone(node) {
1136
+ return new _DetailsNode(node.__summary, node.__open, node.__key);
1137
+ }
1138
+ createDOM(_config) {
1139
+ const details = document.createElement("details");
1140
+ details.className = "rich-details";
1141
+ if (this.__open) {
1142
+ details.open = true;
1143
+ }
1144
+ const summary = document.createElement("summary");
1145
+ summary.className = "rich-details-summary";
1146
+ const label = document.createElement("span");
1147
+ label.className = "rich-details-summary-text";
1148
+ label.textContent = this.__summary;
1149
+ summary.append(label);
1150
+ const chevron = document.createElement("span");
1151
+ chevron.className = "rich-details-chevron";
1152
+ chevron.append(
1153
+ createLucideSvg(ChevronDownIconNode, { width: "1em", height: "1em" })
1154
+ );
1155
+ summary.append(chevron);
1156
+ const content = document.createElement("div");
1157
+ content.className = "rich-details-content";
1158
+ details.append(summary, content);
1159
+ return details;
1160
+ }
1161
+ updateDOM(prevNode, dom) {
1162
+ const details = dom;
1163
+ if (prevNode.__open !== this.__open) {
1164
+ details.open = this.__open;
1165
+ }
1166
+ if (prevNode.__summary !== this.__summary) {
1167
+ const label = dom.querySelector(".rich-details-summary-text");
1168
+ if (label) {
1169
+ label.textContent = this.__summary;
1170
+ }
1171
+ }
1172
+ return false;
1173
+ }
1174
+ static importJSON(serializedNode) {
1175
+ return $createDetailsNode(serializedNode.summary, serializedNode.open);
1176
+ }
1177
+ exportJSON() {
1178
+ return {
1179
+ ...super.exportJSON(),
1180
+ type: "details",
1181
+ summary: this.__summary,
1182
+ open: this.__open,
1183
+ version: 1
1184
+ };
1185
+ }
1186
+ getSummary() {
1187
+ return this.getLatest().__summary;
1188
+ }
1189
+ setSummary(summary) {
1190
+ const writable = this.getWritable();
1191
+ writable.__summary = summary;
1192
+ }
1193
+ getOpen() {
1194
+ return this.getLatest().__open;
1195
+ }
1196
+ setOpen(open) {
1197
+ const writable = this.getWritable();
1198
+ writable.__open = open;
1199
+ }
1200
+ toggleOpen() {
1201
+ this.setOpen(!this.getOpen());
1202
+ }
1203
+ getDOMSlot(element) {
1204
+ const content = element.querySelector(
1205
+ ".rich-details-content"
1206
+ );
1207
+ return super.getDOMSlot(element).withElement(content);
1208
+ }
1209
+ isInline() {
1210
+ return false;
1211
+ }
1212
+ };
1213
+ __publicField(_DetailsNode, "slashMenuItems", [
1214
+ {
1215
+ title: "Details",
1216
+ icon: createElement(ChevronRight, { size: 20 }),
1217
+ description: "Collapsible content block",
1218
+ keywords: ["details", "toggle", "collapse", "accordion"],
1219
+ section: "ADVANCED",
1220
+ onSelect: (editor) => {
1221
+ editor.update(() => {
1222
+ $insertNodes([$createDetailsNode("Details")]);
1223
+ });
1224
+ }
1225
+ }
1226
+ ]);
1227
+ let DetailsNode = _DetailsNode;
1228
+ function $createDetailsNode(summary, open = false) {
1229
+ return new DetailsNode(summary, open);
1230
+ }
1231
+ function $isDetailsNode(node) {
1232
+ return node instanceof DetailsNode;
1233
+ }
1234
+ function FootnoteSectionRenderer({
1235
+ definitions
1236
+ }) {
1237
+ const { displayNumberMap } = useFootnoteDefinitions();
1238
+ const sortedEntries = Object.entries(definitions).sort(
1239
+ ([a], [b]) => (displayNumberMap[a] ?? 0) - (displayNumberMap[b] ?? 0)
1240
+ );
1241
+ if (sortedEntries.length === 0) return null;
1242
+ return /* @__PURE__ */ jsxs("div", { className: "rich-footnote-section-content", role: "doc-endnotes", children: [
1243
+ /* @__PURE__ */ jsx("hr", { className: "rich-footnote-section-divider" }),
1244
+ /* @__PURE__ */ jsx("ol", { className: "rich-footnote-section-list", children: sortedEntries.map(([identifier, content]) => {
1245
+ const displayNum = displayNumberMap[identifier] ?? identifier;
1246
+ return /* @__PURE__ */ jsx(
1247
+ FootnoteSectionItem,
1248
+ {
1249
+ identifier,
1250
+ content,
1251
+ displayNum
1252
+ },
1253
+ identifier
1254
+ );
1255
+ }) })
1256
+ ] });
1257
+ }
1258
+ function FootnoteSectionItem({
1259
+ identifier,
1260
+ content,
1261
+ displayNum
1262
+ }) {
1263
+ const targetId = `footnote-${identifier}`;
1264
+ const refId = `footnote-ref-${identifier}`;
1265
+ const handleBackClick = useCallback(
1266
+ (e) => {
1267
+ e.preventDefault();
1268
+ const refElement = document.getElementById(refId);
1269
+ if (!refElement) return;
1270
+ refElement.scrollIntoView({ behavior: "smooth", block: "center" });
1271
+ refElement.classList.add("rich-footnote-highlight");
1272
+ window.setTimeout(() => {
1273
+ refElement.classList.remove("rich-footnote-highlight");
1274
+ }, 1200);
1275
+ },
1276
+ [refId]
1277
+ );
1278
+ return /* @__PURE__ */ jsxs(
1279
+ "li",
1280
+ {
1281
+ id: targetId,
1282
+ className: "rich-footnote-section-item",
1283
+ value: typeof displayNum === "number" ? displayNum : void 0,
1284
+ children: [
1285
+ /* @__PURE__ */ jsx("span", { className: "rich-footnote-section-item-content", children: content }),
1286
+ /* @__PURE__ */ jsx(
1287
+ "a",
1288
+ {
1289
+ href: `#${refId}`,
1290
+ onClick: handleBackClick,
1291
+ className: "rich-footnote-back-ref",
1292
+ role: "doc-backlink",
1293
+ "aria-label": `Back to reference ${displayNum}`,
1294
+ children: "↩"
1295
+ }
1296
+ )
1297
+ ]
1298
+ }
1299
+ );
1300
+ }
1301
+ class FootnoteSectionNode extends DecoratorNode {
1302
+ constructor(definitions, key) {
1303
+ super(key);
1304
+ __publicField(this, "__definitions");
1305
+ this.__definitions = definitions;
1306
+ }
1307
+ static getType() {
1308
+ return "footnote-section";
1309
+ }
1310
+ static clone(node) {
1311
+ return new FootnoteSectionNode({ ...node.__definitions }, node.__key);
1312
+ }
1313
+ createDOM(_config) {
1314
+ const div = document.createElement("div");
1315
+ div.className = "rich-footnote-section";
1316
+ return div;
1317
+ }
1318
+ updateDOM() {
1319
+ return false;
1320
+ }
1321
+ isInline() {
1322
+ return false;
1323
+ }
1324
+ static importJSON(serializedNode) {
1325
+ return $createFootnoteSectionNode(serializedNode.definitions);
1326
+ }
1327
+ exportJSON() {
1328
+ return {
1329
+ ...super.exportJSON(),
1330
+ type: "footnote-section",
1331
+ definitions: this.__definitions,
1332
+ version: 1
1333
+ };
1334
+ }
1335
+ getDefinitions() {
1336
+ return this.getLatest().__definitions;
1337
+ }
1338
+ setDefinitions(definitions) {
1339
+ const writable = this.getWritable();
1340
+ writable.__definitions = definitions;
1341
+ }
1342
+ getDefinition(identifier) {
1343
+ return this.getLatest().__definitions[identifier];
1344
+ }
1345
+ setDefinition(identifier, content) {
1346
+ const writable = this.getWritable();
1347
+ writable.__definitions = {
1348
+ ...writable.__definitions,
1349
+ [identifier]: content
1350
+ };
1351
+ }
1352
+ removeDefinition(identifier) {
1353
+ const writable = this.getWritable();
1354
+ const { [identifier]: _, ...rest } = writable.__definitions;
1355
+ writable.__definitions = rest;
1356
+ }
1357
+ decorate(_editor, _config) {
1358
+ return createRendererDecoration(
1359
+ "FootnoteSection",
1360
+ FootnoteSectionRenderer,
1361
+ {
1362
+ definitions: this.__definitions,
1363
+ nodeKey: this.__key
1364
+ }
1365
+ );
1366
+ }
1367
+ }
1368
+ function $createFootnoteSectionNode(definitions = {}) {
1369
+ return new FootnoteSectionNode(definitions);
1370
+ }
1371
+ function $isFootnoteSectionNode(node) {
1372
+ return node instanceof FootnoteSectionNode;
1373
+ }
1374
+ function GridReadOnlyDecorator({
1375
+ cols,
1376
+ gap,
1377
+ cellEditors
1378
+ }) {
1379
+ return /* @__PURE__ */ jsx(
1380
+ "div",
1381
+ {
1382
+ className: "rich-grid-inner",
1383
+ style: {
1384
+ display: "grid",
1385
+ gridTemplateColumns: `repeat(${cols}, 1fr)`,
1386
+ gap
1387
+ },
1388
+ children: cellEditors.map((editor, i) => /* @__PURE__ */ jsx("div", { className: "rich-grid-cell", children: /* @__PURE__ */ jsx(LexicalNestedComposer, { initialEditor: editor, children: /* @__PURE__ */ jsx(
1389
+ RichTextPlugin,
1390
+ {
1391
+ contentEditable: /* @__PURE__ */ jsx(
1392
+ ContentEditable,
1393
+ {
1394
+ className: "rich-grid-cell-editable",
1395
+ style: { outline: "none" },
1396
+ "aria-placeholder": "",
1397
+ placeholder: /* @__PURE__ */ jsx("span", { style: { display: "none" } })
1398
+ }
1399
+ ),
1400
+ ErrorBoundary: LexicalErrorBoundary
1401
+ }
1402
+ ) }) }, i))
1403
+ }
1404
+ );
1405
+ }
1406
+ function createCellEditor() {
1407
+ return createEditor({
1408
+ namespace: "GridCell",
1409
+ nodes: NESTED_EDITOR_NODES,
1410
+ theme: editorTheme,
1411
+ onError: (error) => {
1412
+ console.error("[GridCell]", error);
1413
+ }
1414
+ });
1415
+ }
1416
+ const _GridContainerNode = class _GridContainerNode extends DecoratorNode {
1417
+ constructor(cols = 2, gap, cellEditors, key) {
1418
+ super(key);
1419
+ __publicField(this, "__cols");
1420
+ __publicField(this, "__gap");
1421
+ __publicField(this, "__cellEditors");
1422
+ this.__cols = cols;
1423
+ this.__gap = gap || "16px";
1424
+ if (cellEditors) {
1425
+ this.__cellEditors = cellEditors;
1426
+ } else {
1427
+ this.__cellEditors = Array.from(
1428
+ { length: cols },
1429
+ () => createCellEditor()
1430
+ );
1431
+ }
1432
+ }
1433
+ static getType() {
1434
+ return "grid-container";
1435
+ }
1436
+ static clone(node) {
1437
+ return new _GridContainerNode(
1438
+ node.__cols,
1439
+ node.__gap,
1440
+ [...node.__cellEditors],
1441
+ node.__key
1442
+ );
1443
+ }
1444
+ createDOM(_config) {
1445
+ const div = document.createElement("div");
1446
+ div.className = "rich-grid-container";
1447
+ return div;
1448
+ }
1449
+ updateDOM() {
1450
+ return false;
1451
+ }
1452
+ isInline() {
1453
+ return false;
1454
+ }
1455
+ getCols() {
1456
+ return this.getLatest().__cols;
1457
+ }
1458
+ setCols(cols) {
1459
+ const writable = this.getWritable();
1460
+ const prev = writable.__cellEditors.length;
1461
+ writable.__cols = cols;
1462
+ if (cols > prev) {
1463
+ for (let i = prev; i < cols; i++) {
1464
+ writable.__cellEditors.push(createCellEditor());
1465
+ }
1466
+ }
1467
+ }
1468
+ getGap() {
1469
+ return this.getLatest().__gap;
1470
+ }
1471
+ setGap(gap) {
1472
+ const writable = this.getWritable();
1473
+ writable.__gap = gap;
1474
+ }
1475
+ getCellEditors() {
1476
+ return this.getLatest().__cellEditors;
1477
+ }
1478
+ addCells(count) {
1479
+ const writable = this.getWritable();
1480
+ for (let i = 0; i < count; i++) {
1481
+ writable.__cellEditors.push(createCellEditor());
1482
+ }
1483
+ }
1484
+ removeCells(count) {
1485
+ const writable = this.getWritable();
1486
+ const editors = writable.__cellEditors;
1487
+ const toRemove = Math.min(count, editors.length);
1488
+ for (let i = 0; i < toRemove; i++) {
1489
+ const editor = editors.at(-1);
1490
+ if (!editor) break;
1491
+ const isEmpty = editor.getEditorState().read(() => {
1492
+ return $getRoot().getTextContentSize() === 0;
1493
+ });
1494
+ if (!isEmpty) break;
1495
+ editors.pop();
1496
+ }
1497
+ }
1498
+ getTextContent() {
1499
+ return this.__cellEditors.map(
1500
+ (editor) => editor.getEditorState().read(() => $getRoot().getTextContent())
1501
+ ).join("\n");
1502
+ }
1503
+ static importJSON(serializedNode) {
1504
+ const legacy = serializedNode;
1505
+ const cols = legacy.cols || 2;
1506
+ const rawGap = legacy.gap;
1507
+ const gap = typeof rawGap === "number" ? `${rawGap}px` : rawGap;
1508
+ const node = new _GridContainerNode(cols, gap);
1509
+ if (legacy.cells && legacy.cells.length > 0) {
1510
+ const editors = legacy.cells.map((cellState) => {
1511
+ const editor = createCellEditor();
1512
+ const editorState = editor.parseEditorState(cellState);
1513
+ editor.setEditorState(editorState);
1514
+ return editor;
1515
+ });
1516
+ node.__cellEditors = editors;
1517
+ } else if (legacy.children) {
1518
+ const { children } = legacy;
1519
+ const editors = children.map((child) => {
1520
+ const editor = createCellEditor();
1521
+ const content = {
1522
+ root: {
1523
+ children: [child],
1524
+ direction: null,
1525
+ format: "",
1526
+ indent: 0,
1527
+ type: "root",
1528
+ version: 1
1529
+ }
1530
+ };
1531
+ const editorState = editor.parseEditorState(content);
1532
+ editor.setEditorState(editorState);
1533
+ return editor;
1534
+ });
1535
+ node.__cellEditors = editors;
1536
+ }
1537
+ return node;
1538
+ }
1539
+ exportJSON() {
1540
+ return {
1541
+ ...super.exportJSON(),
1542
+ type: "grid-container",
1543
+ cols: this.__cols,
1544
+ gap: this.__gap,
1545
+ cells: this.__cellEditors.map(
1546
+ (editor) => editor.getEditorState().toJSON()
1547
+ ),
1548
+ version: 1
1549
+ };
1550
+ }
1551
+ decorate(_editor, _config) {
1552
+ return createElement(GridReadOnlyDecorator, {
1553
+ cols: this.__cols,
1554
+ gap: this.__gap,
1555
+ cellEditors: this.__cellEditors
1556
+ });
1557
+ }
1558
+ };
1559
+ __publicField(_GridContainerNode, "slashMenuItems", [
1560
+ {
1561
+ title: "Grid",
1562
+ icon: createElement(LayoutGrid, { size: 20 }),
1563
+ description: "Grid layout container",
1564
+ keywords: ["grid", "columns", "layout"],
1565
+ section: "LAYOUT",
1566
+ onSelect: (editor) => {
1567
+ editor.update(() => {
1568
+ $insertNodes([$createGridContainerNode(2)]);
1569
+ });
1570
+ }
1571
+ }
1572
+ ]);
1573
+ let GridContainerNode = _GridContainerNode;
1574
+ function $createGridContainerNode(cols = 2, gap) {
1575
+ return new GridContainerNode(cols, gap);
1576
+ }
1577
+ function $isGridContainerNode(node) {
1578
+ return node instanceof GridContainerNode;
1579
+ }
1580
+ function rgbaToThumbHash(w, h, rgba) {
1581
+ if (w > 100 || h > 100) throw new Error(`${w}x${h} doesn't fit in 100x100`);
1582
+ let { PI, round, max, cos, abs } = Math;
1583
+ let avg_r = 0, avg_g = 0, avg_b = 0, avg_a = 0;
1584
+ for (let i = 0, j = 0; i < w * h; i++, j += 4) {
1585
+ let alpha = rgba[j + 3] / 255;
1586
+ avg_r += alpha / 255 * rgba[j];
1587
+ avg_g += alpha / 255 * rgba[j + 1];
1588
+ avg_b += alpha / 255 * rgba[j + 2];
1589
+ avg_a += alpha;
1590
+ }
1591
+ if (avg_a) {
1592
+ avg_r /= avg_a;
1593
+ avg_g /= avg_a;
1594
+ avg_b /= avg_a;
1595
+ }
1596
+ let hasAlpha = avg_a < w * h;
1597
+ let l_limit = hasAlpha ? 5 : 7;
1598
+ let lx = max(1, round(l_limit * w / max(w, h)));
1599
+ let ly = max(1, round(l_limit * h / max(w, h)));
1600
+ let l = [];
1601
+ let p = [];
1602
+ let q = [];
1603
+ let a = [];
1604
+ for (let i = 0, j = 0; i < w * h; i++, j += 4) {
1605
+ let alpha = rgba[j + 3] / 255;
1606
+ let r = avg_r * (1 - alpha) + alpha / 255 * rgba[j];
1607
+ let g = avg_g * (1 - alpha) + alpha / 255 * rgba[j + 1];
1608
+ let b = avg_b * (1 - alpha) + alpha / 255 * rgba[j + 2];
1609
+ l[i] = (r + g + b) / 3;
1610
+ p[i] = (r + g) / 2 - b;
1611
+ q[i] = r - g;
1612
+ a[i] = alpha;
1613
+ }
1614
+ let encodeChannel = (channel, nx, ny) => {
1615
+ let dc = 0, ac = [], scale = 0, fx = [];
1616
+ for (let cy = 0; cy < ny; cy++) {
1617
+ for (let cx = 0; cx * ny < nx * (ny - cy); cx++) {
1618
+ let f = 0;
1619
+ for (let x = 0; x < w; x++)
1620
+ fx[x] = cos(PI / w * cx * (x + 0.5));
1621
+ for (let y = 0; y < h; y++)
1622
+ for (let x = 0, fy = cos(PI / h * cy * (y + 0.5)); x < w; x++)
1623
+ f += channel[x + y * w] * fx[x] * fy;
1624
+ f /= w * h;
1625
+ if (cx || cy) {
1626
+ ac.push(f);
1627
+ scale = max(scale, abs(f));
1628
+ } else {
1629
+ dc = f;
1630
+ }
1631
+ }
1632
+ }
1633
+ if (scale)
1634
+ for (let i = 0; i < ac.length; i++)
1635
+ ac[i] = 0.5 + 0.5 / scale * ac[i];
1636
+ return [dc, ac, scale];
1637
+ };
1638
+ let [l_dc, l_ac, l_scale] = encodeChannel(l, max(3, lx), max(3, ly));
1639
+ let [p_dc, p_ac, p_scale] = encodeChannel(p, 3, 3);
1640
+ let [q_dc, q_ac, q_scale] = encodeChannel(q, 3, 3);
1641
+ let [a_dc, a_ac, a_scale] = hasAlpha ? encodeChannel(a, 5, 5) : [];
1642
+ let isLandscape = w > h;
1643
+ let header24 = round(63 * l_dc) | round(31.5 + 31.5 * p_dc) << 6 | round(31.5 + 31.5 * q_dc) << 12 | round(31 * l_scale) << 18 | hasAlpha << 23;
1644
+ let header16 = (isLandscape ? ly : lx) | round(63 * p_scale) << 3 | round(63 * q_scale) << 9 | isLandscape << 15;
1645
+ let hash = [header24 & 255, header24 >> 8 & 255, header24 >> 16, header16 & 255, header16 >> 8];
1646
+ let ac_start = hasAlpha ? 6 : 5;
1647
+ let ac_index = 0;
1648
+ if (hasAlpha) hash.push(round(15 * a_dc) | round(15 * a_scale) << 4);
1649
+ for (let ac of hasAlpha ? [l_ac, p_ac, q_ac, a_ac] : [l_ac, p_ac, q_ac])
1650
+ for (let f of ac)
1651
+ hash[ac_start + (ac_index >> 1)] |= round(15 * f) << ((ac_index++ & 1) << 2);
1652
+ return new Uint8Array(hash);
1653
+ }
1654
+ function thumbHashToRGBA(hash) {
1655
+ let { PI, min, max, cos, round } = Math;
1656
+ let header24 = hash[0] | hash[1] << 8 | hash[2] << 16;
1657
+ let header16 = hash[3] | hash[4] << 8;
1658
+ let l_dc = (header24 & 63) / 63;
1659
+ let p_dc = (header24 >> 6 & 63) / 31.5 - 1;
1660
+ let q_dc = (header24 >> 12 & 63) / 31.5 - 1;
1661
+ let l_scale = (header24 >> 18 & 31) / 31;
1662
+ let hasAlpha = header24 >> 23;
1663
+ let p_scale = (header16 >> 3 & 63) / 63;
1664
+ let q_scale = (header16 >> 9 & 63) / 63;
1665
+ let isLandscape = header16 >> 15;
1666
+ let lx = max(3, isLandscape ? hasAlpha ? 5 : 7 : header16 & 7);
1667
+ let ly = max(3, isLandscape ? header16 & 7 : hasAlpha ? 5 : 7);
1668
+ let a_dc = hasAlpha ? (hash[5] & 15) / 15 : 1;
1669
+ let a_scale = (hash[5] >> 4) / 15;
1670
+ let ac_start = hasAlpha ? 6 : 5;
1671
+ let ac_index = 0;
1672
+ let decodeChannel = (nx, ny, scale) => {
1673
+ let ac = [];
1674
+ for (let cy = 0; cy < ny; cy++)
1675
+ for (let cx = cy ? 0 : 1; cx * ny < nx * (ny - cy); cx++)
1676
+ ac.push(((hash[ac_start + (ac_index >> 1)] >> ((ac_index++ & 1) << 2) & 15) / 7.5 - 1) * scale);
1677
+ return ac;
1678
+ };
1679
+ let l_ac = decodeChannel(lx, ly, l_scale);
1680
+ let p_ac = decodeChannel(3, 3, p_scale * 1.25);
1681
+ let q_ac = decodeChannel(3, 3, q_scale * 1.25);
1682
+ let a_ac = hasAlpha && decodeChannel(5, 5, a_scale);
1683
+ let ratio = thumbHashToApproximateAspectRatio(hash);
1684
+ let w = round(ratio > 1 ? 32 : 32 * ratio);
1685
+ let h = round(ratio > 1 ? 32 / ratio : 32);
1686
+ let rgba = new Uint8Array(w * h * 4), fx = [], fy = [];
1687
+ for (let y = 0, i = 0; y < h; y++) {
1688
+ for (let x = 0; x < w; x++, i += 4) {
1689
+ let l = l_dc, p = p_dc, q = q_dc, a = a_dc;
1690
+ for (let cx = 0, n = max(lx, hasAlpha ? 5 : 3); cx < n; cx++)
1691
+ fx[cx] = cos(PI / w * (x + 0.5) * cx);
1692
+ for (let cy = 0, n = max(ly, hasAlpha ? 5 : 3); cy < n; cy++)
1693
+ fy[cy] = cos(PI / h * (y + 0.5) * cy);
1694
+ for (let cy = 0, j = 0; cy < ly; cy++)
1695
+ for (let cx = cy ? 0 : 1, fy2 = fy[cy] * 2; cx * ly < lx * (ly - cy); cx++, j++)
1696
+ l += l_ac[j] * fx[cx] * fy2;
1697
+ for (let cy = 0, j = 0; cy < 3; cy++) {
1698
+ for (let cx = cy ? 0 : 1, fy2 = fy[cy] * 2; cx < 3 - cy; cx++, j++) {
1699
+ let f = fx[cx] * fy2;
1700
+ p += p_ac[j] * f;
1701
+ q += q_ac[j] * f;
1702
+ }
1703
+ }
1704
+ if (hasAlpha)
1705
+ for (let cy = 0, j = 0; cy < 5; cy++)
1706
+ for (let cx = cy ? 0 : 1, fy2 = fy[cy] * 2; cx < 5 - cy; cx++, j++)
1707
+ a += a_ac[j] * fx[cx] * fy2;
1708
+ let b = l - 2 / 3 * p;
1709
+ let r = (3 * l - b + q) / 2;
1710
+ let g = r - q;
1711
+ rgba[i] = max(0, 255 * min(1, r));
1712
+ rgba[i + 1] = max(0, 255 * min(1, g));
1713
+ rgba[i + 2] = max(0, 255 * min(1, b));
1714
+ rgba[i + 3] = max(0, 255 * min(1, a));
1715
+ }
1716
+ }
1717
+ return { w, h, rgba };
1718
+ }
1719
+ function thumbHashToApproximateAspectRatio(hash) {
1720
+ let header = hash[3];
1721
+ let hasAlpha = hash[2] & 128;
1722
+ let isLandscape = hash[4] & 128;
1723
+ let lx = isLandscape ? hasAlpha ? 5 : 7 : header & 7;
1724
+ let ly = isLandscape ? header & 7 : hasAlpha ? 5 : 7;
1725
+ return lx / ly;
1726
+ }
1727
+ function rgbaToDataURL(w, h, rgba) {
1728
+ let row = w * 4 + 1;
1729
+ let idat = 6 + h * (5 + row);
1730
+ let bytes = [
1731
+ 137,
1732
+ 80,
1733
+ 78,
1734
+ 71,
1735
+ 13,
1736
+ 10,
1737
+ 26,
1738
+ 10,
1739
+ 0,
1740
+ 0,
1741
+ 0,
1742
+ 13,
1743
+ 73,
1744
+ 72,
1745
+ 68,
1746
+ 82,
1747
+ 0,
1748
+ 0,
1749
+ w >> 8,
1750
+ w & 255,
1751
+ 0,
1752
+ 0,
1753
+ h >> 8,
1754
+ h & 255,
1755
+ 8,
1756
+ 6,
1757
+ 0,
1758
+ 0,
1759
+ 0,
1760
+ 0,
1761
+ 0,
1762
+ 0,
1763
+ 0,
1764
+ idat >>> 24,
1765
+ idat >> 16 & 255,
1766
+ idat >> 8 & 255,
1767
+ idat & 255,
1768
+ 73,
1769
+ 68,
1770
+ 65,
1771
+ 84,
1772
+ 120,
1773
+ 1
1774
+ ];
1775
+ let table = [
1776
+ 0,
1777
+ 498536548,
1778
+ 997073096,
1779
+ 651767980,
1780
+ 1994146192,
1781
+ 1802195444,
1782
+ 1303535960,
1783
+ 1342533948,
1784
+ -306674912,
1785
+ -267414716,
1786
+ -690576408,
1787
+ -882789492,
1788
+ -1687895376,
1789
+ -2032938284,
1790
+ -1609899400,
1791
+ -1111625188
1792
+ ];
1793
+ let a = 1, b = 0;
1794
+ for (let y = 0, i = 0, end = row - 1; y < h; y++, end += row - 1) {
1795
+ bytes.push(y + 1 < h ? 0 : 1, row & 255, row >> 8, ~row & 255, row >> 8 ^ 255, 0);
1796
+ for (b = (b + a) % 65521; i < end; i++) {
1797
+ let u = rgba[i] & 255;
1798
+ bytes.push(u);
1799
+ a = (a + u) % 65521;
1800
+ b = (b + a) % 65521;
1801
+ }
1802
+ }
1803
+ bytes.push(
1804
+ b >> 8,
1805
+ b & 255,
1806
+ a >> 8,
1807
+ a & 255,
1808
+ 0,
1809
+ 0,
1810
+ 0,
1811
+ 0,
1812
+ 0,
1813
+ 0,
1814
+ 0,
1815
+ 0,
1816
+ 73,
1817
+ 69,
1818
+ 78,
1819
+ 68,
1820
+ 174,
1821
+ 66,
1822
+ 96,
1823
+ 130
1824
+ );
1825
+ for (let [start, end] of [[12, 29], [37, 41 + idat]]) {
1826
+ let c = -1;
1827
+ for (let i = start; i < end; i++) {
1828
+ c ^= bytes[i];
1829
+ c = c >>> 4 ^ table[c & 15];
1830
+ c = c >>> 4 ^ table[c & 15];
1831
+ }
1832
+ c = ~c;
1833
+ bytes[end++] = c >>> 24;
1834
+ bytes[end++] = c >> 16 & 255;
1835
+ bytes[end++] = c >> 8 & 255;
1836
+ bytes[end++] = c & 255;
1837
+ }
1838
+ return "data:image/png;base64," + btoa(String.fromCharCode(...bytes));
1839
+ }
1840
+ function thumbHashToDataURL(hash) {
1841
+ let image = thumbHashToRGBA(hash);
1842
+ return rgbaToDataURL(image.w, image.h, image.rgba);
1843
+ }
1844
+ const MAX_DIM = 100;
1845
+ async function computeImageMeta(file) {
1846
+ const url = URL.createObjectURL(file);
1847
+ try {
1848
+ const img = await loadImage(url);
1849
+ const { naturalWidth: w, naturalHeight: h } = img;
1850
+ const scale = Math.min(MAX_DIM / w, MAX_DIM / h, 1);
1851
+ const sw = Math.round(w * scale);
1852
+ const sh = Math.round(h * scale);
1853
+ const canvas = document.createElement("canvas");
1854
+ canvas.width = sw;
1855
+ canvas.height = sh;
1856
+ const ctx = canvas.getContext("2d");
1857
+ ctx.drawImage(img, 0, 0, sw, sh);
1858
+ const { data } = ctx.getImageData(0, 0, sw, sh);
1859
+ const hash = rgbaToThumbHash(sw, sh, data);
1860
+ const thumbhash = uint8ToBase64(hash);
1861
+ return { width: w, height: h, thumbhash };
1862
+ } finally {
1863
+ URL.revokeObjectURL(url);
1864
+ }
1865
+ }
1866
+ function decodeThumbHash(hash) {
1867
+ try {
1868
+ const bytes = base64ToUint8(hash);
1869
+ return thumbHashToDataURL(bytes);
1870
+ } catch {
1871
+ return void 0;
1872
+ }
1873
+ }
1874
+ function loadImage(src) {
1875
+ return new Promise((resolve, reject) => {
1876
+ const img = new Image();
1877
+ img.onload = () => resolve(img);
1878
+ img.onerror = reject;
1879
+ img.src = src;
1880
+ });
1881
+ }
1882
+ function uint8ToBase64(bytes) {
1883
+ let bin = "";
1884
+ for (const b of bytes) bin += String.fromCodePoint(b);
1885
+ return btoa(bin);
1886
+ }
1887
+ function base64ToUint8(str) {
1888
+ const bin = atob(str);
1889
+ const bytes = new Uint8Array(bin.length);
1890
+ for (let i = 0; i < bin.length; i++) bytes[i] = bin.codePointAt(i);
1891
+ return bytes;
1892
+ }
1893
+ function ImageRenderer({
1894
+ src,
1895
+ altText,
1896
+ width,
1897
+ height,
1898
+ caption,
1899
+ thumbhash,
1900
+ accent
1901
+ }) {
1902
+ const [loaded, setLoaded] = useState(false);
1903
+ const [zoomed, setZoomed] = useState(false);
1904
+ const handleLoad = useCallback(() => setLoaded(true), []);
1905
+ const handleZoomOpen = useCallback(() => {
1906
+ if (!loaded) return;
1907
+ setZoomed(true);
1908
+ }, [loaded]);
1909
+ const handleZoomClose = useCallback(() => setZoomed(false), []);
1910
+ useEffect(() => {
1911
+ if (!zoomed) return;
1912
+ const onKeyDown = (e) => {
1913
+ if (e.key === "Escape") setZoomed(false);
1914
+ };
1915
+ document.addEventListener("keydown", onKeyDown);
1916
+ return () => document.removeEventListener("keydown", onKeyDown);
1917
+ }, [zoomed]);
1918
+ useEffect(() => {
1919
+ if (!zoomed) return;
1920
+ const prev = document.body.style.overflow;
1921
+ document.body.style.overflow = "hidden";
1922
+ return () => {
1923
+ document.body.style.overflow = prev;
1924
+ };
1925
+ }, [zoomed]);
1926
+ const handleContainerKeyDown = useCallback(
1927
+ (e) => {
1928
+ if ((e.key === "Enter" || e.key === " ") && loaded) {
1929
+ e.preventDefault();
1930
+ setZoomed(true);
1931
+ }
1932
+ },
1933
+ [loaded]
1934
+ );
1935
+ const placeholderUrl = useMemo(
1936
+ () => thumbhash ? decodeThumbHash(thumbhash) : void 0,
1937
+ [thumbhash]
1938
+ );
1939
+ const aspectStyle = width && height ? { aspectRatio: `${width} / ${height}`, maxWidth: "100%", width } : { maxWidth: "100%" };
1940
+ return /* @__PURE__ */ jsxs("figure", { className: "rich-image", children: [
1941
+ /* @__PURE__ */ jsx(
1942
+ "div",
1943
+ {
1944
+ className: `rich-image-container${loaded ? " rich-image-loaded" : ""}`,
1945
+ style: {
1946
+ ...aspectStyle,
1947
+ backgroundColor: !loaded && !placeholderUrl ? accent : void 0,
1948
+ backgroundImage: !loaded && placeholderUrl ? `url(${placeholderUrl})` : void 0,
1949
+ backgroundSize: "cover",
1950
+ cursor: loaded ? "zoom-in" : void 0
1951
+ },
1952
+ onClick: handleZoomOpen,
1953
+ onKeyDown: handleContainerKeyDown,
1954
+ role: "button",
1955
+ tabIndex: loaded ? 0 : -1,
1956
+ "aria-label": loaded ? `Zoom image: ${altText}` : void 0,
1957
+ children: /* @__PURE__ */ jsx(
1958
+ "img",
1959
+ {
1960
+ src,
1961
+ alt: altText,
1962
+ width,
1963
+ height,
1964
+ loading: "lazy",
1965
+ onLoad: handleLoad,
1966
+ style: { maxWidth: "100%", height: "auto" },
1967
+ className: loaded ? "rich-image-visible" : "rich-image-hidden"
1968
+ }
1969
+ )
1970
+ }
1971
+ ),
1972
+ caption && /* @__PURE__ */ jsx("figcaption", { children: caption }),
1973
+ zoomed && /* @__PURE__ */ jsx(
1974
+ "div",
1975
+ {
1976
+ className: "rich-image-zoom-overlay",
1977
+ onClick: handleZoomClose,
1978
+ role: "dialog",
1979
+ "aria-modal": "true",
1980
+ "aria-label": `Zoomed image: ${altText}`,
1981
+ tabIndex: 0,
1982
+ children: /* @__PURE__ */ jsx("img", { src, alt: altText, className: "rich-image-zoom-img" })
1983
+ }
1984
+ )
1985
+ ] });
1986
+ }
1987
+ function sanitizeImageSrc(src) {
1988
+ const trimmed = src.trim();
1989
+ if (/^(javascript\s*:|vbscript\s*:|data\s*:(?!image\/))/i.test(trimmed)) {
1990
+ return "";
1991
+ }
1992
+ return trimmed;
1993
+ }
1994
+ function sanitizeColor(value) {
1995
+ if (!value) return void 0;
1996
+ const trimmed = value.trim();
1997
+ if (/^#[\da-f]{3,8}$/i.test(trimmed)) return trimmed;
1998
+ if (/^(rgb|hsl)a?\([^)]+\)$/i.test(trimmed)) return trimmed;
1999
+ if (/^[a-z]{3,20}$/i.test(trimmed)) return trimmed;
2000
+ return void 0;
2001
+ }
2002
+ const _ImageNode = class _ImageNode extends DecoratorNode {
2003
+ constructor(payload, key) {
2004
+ super(key);
2005
+ __publicField(this, "__src");
2006
+ __publicField(this, "__altText");
2007
+ __publicField(this, "__width");
2008
+ __publicField(this, "__height");
2009
+ __publicField(this, "__caption");
2010
+ __publicField(this, "__thumbhash");
2011
+ __publicField(this, "__accent");
2012
+ this.__src = sanitizeImageSrc(payload.src);
2013
+ this.__altText = payload.altText;
2014
+ this.__width = payload.width;
2015
+ this.__height = payload.height;
2016
+ this.__caption = payload.caption;
2017
+ this.__thumbhash = payload.thumbhash;
2018
+ this.__accent = sanitizeColor(payload.accent);
2019
+ }
2020
+ static getType() {
2021
+ return "image";
2022
+ }
2023
+ static clone(node) {
2024
+ return new _ImageNode(
2025
+ {
2026
+ src: node.__src,
2027
+ altText: node.__altText,
2028
+ width: node.__width,
2029
+ height: node.__height,
2030
+ caption: node.__caption,
2031
+ thumbhash: node.__thumbhash,
2032
+ accent: node.__accent
2033
+ },
2034
+ node.__key
2035
+ );
2036
+ }
2037
+ createDOM(_config) {
2038
+ const div = document.createElement("div");
2039
+ div.className = "rich-image-wrapper";
2040
+ return div;
2041
+ }
2042
+ updateDOM() {
2043
+ return false;
2044
+ }
2045
+ isInline() {
2046
+ return false;
2047
+ }
2048
+ static importJSON(serializedNode) {
2049
+ return $createImageNode({
2050
+ src: serializedNode.src,
2051
+ altText: serializedNode.altText,
2052
+ width: serializedNode.width,
2053
+ height: serializedNode.height,
2054
+ caption: serializedNode.caption,
2055
+ thumbhash: serializedNode.thumbhash,
2056
+ accent: serializedNode.accent
2057
+ });
2058
+ }
2059
+ exportJSON() {
2060
+ return {
2061
+ ...super.exportJSON(),
2062
+ type: "image",
2063
+ src: this.__src,
2064
+ altText: this.__altText,
2065
+ width: this.__width,
2066
+ height: this.__height,
2067
+ caption: this.__caption,
2068
+ thumbhash: this.__thumbhash,
2069
+ accent: this.__accent,
2070
+ version: 1
2071
+ };
2072
+ }
2073
+ decorate(_editor, _config) {
2074
+ return createRendererDecoration("Image", ImageRenderer, {
2075
+ src: this.__src,
2076
+ altText: this.__altText,
2077
+ width: this.__width,
2078
+ height: this.__height,
2079
+ caption: this.__caption,
2080
+ thumbhash: this.__thumbhash,
2081
+ accent: this.__accent
2082
+ });
2083
+ }
2084
+ };
2085
+ __publicField(_ImageNode, "slashMenuItems", [
2086
+ {
2087
+ title: "Image",
2088
+ icon: createElement(ImageIcon, { size: 20 }),
2089
+ description: "Upload or embed an image",
2090
+ keywords: ["image", "picture", "photo"],
2091
+ section: "MEDIA",
2092
+ onSelect: (editor) => {
2093
+ editor.update(() => {
2094
+ $insertNodes([$createImageNode({ src: "", altText: "" })]);
2095
+ });
2096
+ }
2097
+ }
2098
+ ]);
2099
+ let ImageNode = _ImageNode;
2100
+ function $createImageNode(payload) {
2101
+ return new ImageNode(payload);
2102
+ }
2103
+ const _KaTeXBlockNode = class _KaTeXBlockNode extends DecoratorNode {
2104
+ constructor(equation, key) {
2105
+ super(key);
2106
+ __publicField(this, "__equation");
2107
+ this.__equation = equation;
2108
+ }
2109
+ static getType() {
2110
+ return "katex-block";
2111
+ }
2112
+ static clone(node) {
2113
+ return new _KaTeXBlockNode(node.__equation, node.__key);
2114
+ }
2115
+ createDOM(_config) {
2116
+ const div = document.createElement("div");
2117
+ div.className = "rich-katex-block-wrapper";
2118
+ return div;
2119
+ }
2120
+ updateDOM() {
2121
+ return false;
2122
+ }
2123
+ isInline() {
2124
+ return false;
2125
+ }
2126
+ static importJSON(serializedNode) {
2127
+ return $createKaTeXBlockNode(serializedNode.equation);
2128
+ }
2129
+ exportJSON() {
2130
+ return {
2131
+ ...super.exportJSON(),
2132
+ type: "katex-block",
2133
+ equation: this.__equation,
2134
+ version: 1
2135
+ };
2136
+ }
2137
+ getEquation() {
2138
+ return this.__equation;
2139
+ }
2140
+ setEquation(equation) {
2141
+ const writable = this.getWritable();
2142
+ writable.__equation = equation;
2143
+ }
2144
+ decorate(_editor, _config) {
2145
+ return createRendererDecoration("KaTeX", KaTeXRenderer, {
2146
+ equation: this.__equation,
2147
+ displayMode: true
2148
+ });
2149
+ }
2150
+ };
2151
+ __publicField(_KaTeXBlockNode, "slashMenuItems", [
2152
+ {
2153
+ title: "Math Equation",
2154
+ icon: createElement(Sigma, { size: 20 }),
2155
+ description: "KaTeX block formula",
2156
+ keywords: ["math", "equation", "latex", "katex"],
2157
+ section: "ADVANCED",
2158
+ onSelect: (editor) => {
2159
+ editor.update(() => {
2160
+ $insertNodes([$createKaTeXBlockNode("")]);
2161
+ });
2162
+ }
2163
+ }
2164
+ ]);
2165
+ let KaTeXBlockNode = _KaTeXBlockNode;
2166
+ function $createKaTeXBlockNode(equation) {
2167
+ return new KaTeXBlockNode(equation);
2168
+ }
2169
+ function $isKaTeXBlockNode(node) {
2170
+ return node instanceof KaTeXBlockNode;
2171
+ }
2172
+ function LinkCardRenderer({
2173
+ url,
2174
+ title,
2175
+ description,
2176
+ favicon,
2177
+ image
2178
+ }) {
2179
+ const displayTitle = title || url;
2180
+ return /* @__PURE__ */ jsxs(
2181
+ "a",
2182
+ {
2183
+ className: "rich-link-card",
2184
+ href: url,
2185
+ target: "_blank",
2186
+ rel: "noopener noreferrer",
2187
+ children: [
2188
+ image && /* @__PURE__ */ jsx("span", { className: "rich-link-card-image", children: /* @__PURE__ */ jsx("img", { src: image, alt: "", loading: "lazy" }) }),
2189
+ /* @__PURE__ */ jsxs("span", { className: "rich-link-card-content", children: [
2190
+ /* @__PURE__ */ jsxs("span", { className: "rich-link-card-title", children: [
2191
+ favicon && /* @__PURE__ */ jsx(
2192
+ "img",
2193
+ {
2194
+ className: "rich-link-card-favicon",
2195
+ src: favicon,
2196
+ alt: "",
2197
+ width: 16,
2198
+ height: 16,
2199
+ onError: (e) => {
2200
+ e.target.style.display = "none";
2201
+ }
2202
+ }
2203
+ ),
2204
+ displayTitle
2205
+ ] }),
2206
+ description && /* @__PURE__ */ jsx("span", { className: "rich-link-card-description", children: description }),
2207
+ /* @__PURE__ */ jsx("span", { className: "rich-link-card-url", children: url })
2208
+ ] })
2209
+ ]
2210
+ }
2211
+ );
2212
+ }
2213
+ const _LinkCardNode = class _LinkCardNode extends DecoratorNode {
2214
+ constructor(payload, key) {
2215
+ super(key);
2216
+ __publicField(this, "__url");
2217
+ __publicField(this, "__source");
2218
+ __publicField(this, "__id");
2219
+ __publicField(this, "__title");
2220
+ __publicField(this, "__description");
2221
+ __publicField(this, "__favicon");
2222
+ __publicField(this, "__image");
2223
+ this.__url = payload.url;
2224
+ this.__source = payload.source;
2225
+ this.__id = payload.id;
2226
+ this.__title = payload.title;
2227
+ this.__description = payload.description;
2228
+ this.__favicon = payload.favicon;
2229
+ this.__image = payload.image;
2230
+ }
2231
+ static getType() {
2232
+ return "link-card";
2233
+ }
2234
+ static clone(node) {
2235
+ return new _LinkCardNode(
2236
+ {
2237
+ url: node.__url,
2238
+ source: node.__source,
2239
+ id: node.__id,
2240
+ title: node.__title,
2241
+ description: node.__description,
2242
+ favicon: node.__favicon,
2243
+ image: node.__image
2244
+ },
2245
+ node.__key
2246
+ );
2247
+ }
2248
+ createDOM(_config) {
2249
+ const div = document.createElement("div");
2250
+ div.className = "rich-link-card-wrapper";
2251
+ return div;
2252
+ }
2253
+ updateDOM() {
2254
+ return false;
2255
+ }
2256
+ isInline() {
2257
+ return false;
2258
+ }
2259
+ static importJSON(serializedNode) {
2260
+ return $createLinkCardNode({
2261
+ url: serializedNode.url,
2262
+ source: serializedNode.source,
2263
+ id: serializedNode.id,
2264
+ title: serializedNode.title,
2265
+ description: serializedNode.description,
2266
+ favicon: serializedNode.favicon,
2267
+ image: serializedNode.image
2268
+ });
2269
+ }
2270
+ exportJSON() {
2271
+ return {
2272
+ ...super.exportJSON(),
2273
+ type: "link-card",
2274
+ url: this.__url,
2275
+ source: this.__source,
2276
+ id: this.__id,
2277
+ title: this.__title,
2278
+ description: this.__description,
2279
+ favicon: this.__favicon,
2280
+ image: this.__image,
2281
+ version: 1
2282
+ };
2283
+ }
2284
+ getUrl() {
2285
+ return this.getLatest().__url;
2286
+ }
2287
+ setUrl(url) {
2288
+ const writable = this.getWritable();
2289
+ writable.__url = url;
2290
+ }
2291
+ getSource() {
2292
+ return this.getLatest().__source;
2293
+ }
2294
+ setSource(source) {
2295
+ const writable = this.getWritable();
2296
+ writable.__source = source;
2297
+ }
2298
+ getId() {
2299
+ return this.getLatest().__id;
2300
+ }
2301
+ setId(id) {
2302
+ const writable = this.getWritable();
2303
+ writable.__id = id;
2304
+ }
2305
+ decorate(_editor, _config) {
2306
+ return createRendererDecoration("LinkCard", LinkCardRenderer, {
2307
+ url: this.__url,
2308
+ source: this.__source,
2309
+ id: this.__id,
2310
+ title: this.__title,
2311
+ description: this.__description,
2312
+ favicon: this.__favicon,
2313
+ image: this.__image
2314
+ });
2315
+ }
2316
+ };
2317
+ __publicField(_LinkCardNode, "slashMenuItems", [
2318
+ {
2319
+ title: "Link Card",
2320
+ icon: createElement(Link, { size: 20 }),
2321
+ description: "Link preview card",
2322
+ keywords: ["link", "card", "bookmark", "embed"],
2323
+ section: "MEDIA",
2324
+ onSelect: (editor) => {
2325
+ editor.update(() => {
2326
+ $insertNodes([$createLinkCardNode({ url: "" })]);
2327
+ });
2328
+ }
2329
+ }
2330
+ ]);
2331
+ let LinkCardNode = _LinkCardNode;
2332
+ function $createLinkCardNode(payload) {
2333
+ return new LinkCardNode(payload);
2334
+ }
2335
+ function $isLinkCardNode(node) {
2336
+ return node instanceof LinkCardNode;
2337
+ }
2338
+ function MermaidRenderer({ content }) {
2339
+ return /* @__PURE__ */ jsx("div", { className: "rich-mermaid-block", children: /* @__PURE__ */ jsx("pre", { children: /* @__PURE__ */ jsx("code", { children: content }) }) });
2340
+ }
2341
+ const _MermaidNode = class _MermaidNode extends DecoratorNode {
2342
+ constructor(diagram, key) {
2343
+ super(key);
2344
+ __publicField(this, "__diagram");
2345
+ this.__diagram = diagram;
2346
+ }
2347
+ static getType() {
2348
+ return "mermaid";
2349
+ }
2350
+ static clone(node) {
2351
+ return new _MermaidNode(node.__diagram, node.__key);
2352
+ }
2353
+ createDOM(_config) {
2354
+ const div = document.createElement("div");
2355
+ div.className = "rich-mermaid-wrapper";
2356
+ return div;
2357
+ }
2358
+ updateDOM() {
2359
+ return false;
2360
+ }
2361
+ isInline() {
2362
+ return false;
2363
+ }
2364
+ static importJSON(serializedNode) {
2365
+ return $createMermaidNode(serializedNode.diagram);
2366
+ }
2367
+ exportJSON() {
2368
+ return {
2369
+ ...super.exportJSON(),
2370
+ type: "mermaid",
2371
+ diagram: this.__diagram,
2372
+ version: 1
2373
+ };
2374
+ }
2375
+ getDiagram() {
2376
+ return this.__diagram;
2377
+ }
2378
+ setDiagram(diagram) {
2379
+ const writable = this.getWritable();
2380
+ writable.__diagram = diagram;
2381
+ }
2382
+ decorate(editor, _config) {
2383
+ const nodeKey = this.__key;
2384
+ return createRendererDecoration("Mermaid", MermaidRenderer, {
2385
+ content: this.__diagram,
2386
+ onContentChange: (newDiagram) => {
2387
+ editor.update(() => {
2388
+ const node = $getNodeByKey(nodeKey);
2389
+ if (node) {
2390
+ node.setDiagram(newDiagram);
2391
+ }
2392
+ });
2393
+ }
2394
+ });
2395
+ }
2396
+ };
2397
+ __publicField(_MermaidNode, "slashMenuItems", [
2398
+ {
2399
+ title: "Mermaid Diagram",
2400
+ icon: createElement(Workflow, { size: 20 }),
2401
+ description: "Flowchart, sequence diagram",
2402
+ keywords: ["mermaid", "diagram", "chart", "flowchart"],
2403
+ section: "MEDIA",
2404
+ onSelect: (editor) => {
2405
+ editor.update(() => {
2406
+ $insertNodes([
2407
+ $createMermaidNode("graph TD\n A[Start] --> B[End]")
2408
+ ]);
2409
+ });
2410
+ }
2411
+ }
2412
+ ]);
2413
+ let MermaidNode = _MermaidNode;
2414
+ function $createMermaidNode(diagram) {
2415
+ return new MermaidNode(diagram);
2416
+ }
2417
+ function $isMermaidNode(node) {
2418
+ return node instanceof MermaidNode;
2419
+ }
2420
+ function VideoRenderer({
2421
+ src,
2422
+ poster,
2423
+ width,
2424
+ height
2425
+ }) {
2426
+ return /* @__PURE__ */ jsx("figure", { className: "rich-video", children: /* @__PURE__ */ jsx(
2427
+ "video",
2428
+ {
2429
+ src,
2430
+ poster,
2431
+ width,
2432
+ height,
2433
+ controls: true,
2434
+ preload: "metadata",
2435
+ style: { maxWidth: "100%", height: "auto" }
2436
+ }
2437
+ ) });
2438
+ }
2439
+ const _VideoNode = class _VideoNode extends DecoratorNode {
2440
+ constructor(payload, key) {
2441
+ super(key);
2442
+ __publicField(this, "__src");
2443
+ __publicField(this, "__poster");
2444
+ __publicField(this, "__width");
2445
+ __publicField(this, "__height");
2446
+ this.__src = payload.src;
2447
+ this.__poster = payload.poster;
2448
+ this.__width = payload.width;
2449
+ this.__height = payload.height;
2450
+ }
2451
+ static getType() {
2452
+ return "video";
2453
+ }
2454
+ static clone(node) {
2455
+ return new _VideoNode(
2456
+ {
2457
+ src: node.__src,
2458
+ poster: node.__poster,
2459
+ width: node.__width,
2460
+ height: node.__height
2461
+ },
2462
+ node.__key
2463
+ );
2464
+ }
2465
+ createDOM(_config) {
2466
+ const div = document.createElement("div");
2467
+ div.className = "rich-video-wrapper";
2468
+ return div;
2469
+ }
2470
+ updateDOM() {
2471
+ return false;
2472
+ }
2473
+ isInline() {
2474
+ return false;
2475
+ }
2476
+ static importJSON(serializedNode) {
2477
+ return $createVideoNode({
2478
+ src: serializedNode.src,
2479
+ poster: serializedNode.poster,
2480
+ width: serializedNode.width,
2481
+ height: serializedNode.height
2482
+ });
2483
+ }
2484
+ exportJSON() {
2485
+ return {
2486
+ ...super.exportJSON(),
2487
+ type: "video",
2488
+ src: this.__src,
2489
+ poster: this.__poster,
2490
+ width: this.__width,
2491
+ height: this.__height,
2492
+ version: 1
2493
+ };
2494
+ }
2495
+ getSrc() {
2496
+ return this.getLatest().__src;
2497
+ }
2498
+ setSrc(src) {
2499
+ const writable = this.getWritable();
2500
+ writable.__src = src;
2501
+ }
2502
+ decorate(_editor, _config) {
2503
+ return createRendererDecoration("Video", VideoRenderer, {
2504
+ src: this.__src,
2505
+ poster: this.__poster,
2506
+ width: this.__width,
2507
+ height: this.__height
2508
+ });
2509
+ }
2510
+ };
2511
+ __publicField(_VideoNode, "slashMenuItems", [
2512
+ {
2513
+ title: "Video",
2514
+ icon: createElement(Video, { size: 20 }),
2515
+ description: "Embed a video",
2516
+ keywords: ["video", "media", "mp4"],
2517
+ section: "MEDIA",
2518
+ onSelect: (editor) => {
2519
+ editor.update(() => {
2520
+ $insertNodes([$createVideoNode({ src: "" })]);
2521
+ });
2522
+ }
2523
+ }
2524
+ ]);
2525
+ let VideoNode = _VideoNode;
2526
+ function $createVideoNode(payload) {
2527
+ return new VideoNode(payload);
2528
+ }
2529
+ const builtinNodes = [
2530
+ HeadingNode,
2531
+ QuoteNode,
2532
+ ListNode,
2533
+ ListItemNode,
2534
+ LinkNode,
2535
+ AutoLinkNode,
2536
+ HorizontalRuleNode,
2537
+ TableNode,
2538
+ TableCellNode,
2539
+ TableRowNode,
2540
+ CodeNode
2541
+ ];
2542
+ const customNodes = [
2543
+ SpoilerNode,
2544
+ MentionNode,
2545
+ KaTeXInlineNode,
2546
+ KaTeXBlockNode,
2547
+ ImageNode,
2548
+ AlertQuoteNode,
2549
+ CodeBlockNode,
2550
+ FootnoteNode,
2551
+ FootnoteSectionNode,
2552
+ TaskListItemNode,
2553
+ VideoNode,
2554
+ LinkCardNode,
2555
+ DetailsNode,
2556
+ GridContainerNode,
2557
+ BannerNode,
2558
+ MermaidNode
2559
+ ];
2560
+ const allNodes = [
2561
+ ...builtinNodes,
2562
+ ...customNodes
2563
+ ];
2564
+ function FootnotePlugin({ children }) {
2565
+ const [editor] = useLexicalComposerContext();
2566
+ const [definitions, setDefinitions] = useState({});
2567
+ const [displayNumberMap, setDisplayNumberMap] = useState({});
2568
+ useEffect(() => {
2569
+ return editor.registerUpdateListener(({ editorState }) => {
2570
+ editorState.read(() => {
2571
+ const footnoteNodes = $nodesOfType(FootnoteNode);
2572
+ const seen = /* @__PURE__ */ new Set();
2573
+ const numberMap = {};
2574
+ let counter = 1;
2575
+ for (const node of footnoteNodes) {
2576
+ const id = node.getIdentifier();
2577
+ if (!seen.has(id)) {
2578
+ seen.add(id);
2579
+ numberMap[id] = counter++;
2580
+ }
2581
+ }
2582
+ setDisplayNumberMap(numberMap);
2583
+ const sectionNodes = $nodesOfType(FootnoteSectionNode);
2584
+ if (sectionNodes.length > 0) {
2585
+ setDefinitions(sectionNodes[0].getDefinitions());
2586
+ } else {
2587
+ setDefinitions({});
2588
+ }
2589
+ });
2590
+ });
2591
+ }, [editor]);
2592
+ return /* @__PURE__ */ jsx(
2593
+ FootnoteDefinitionsProvider,
2594
+ {
2595
+ definitions,
2596
+ displayNumberMap,
2597
+ children
2598
+ }
2599
+ );
2600
+ }
2601
+ var articleVariant = "v10dui1 r8uj4t0 _17pm0gw0 v10dui0";
2602
+ var darkArticleVariant = "v10dui2 r8uj4t0 _17pm0gw2 v10dui0";
2603
+ var commentVariant = "_14ko36c1 r8uj4t0 _17pm0gw1 _14ko36c0";
2604
+ var darkCommentVariant = "_14ko36c2 r8uj4t0 _17pm0gw3 _14ko36c0";
2605
+ var noteVariant = "ys4fw91 r8uj4t0 _17pm0gw4 ys4fw90";
2606
+ var darkNoteVariant = "ys4fw92 r8uj4t0 _17pm0gw5 ys4fw90";
2607
+ function clsx(...args) {
2608
+ return args.filter(Boolean).join(" ");
2609
+ }
2610
+ function getVariantClass(variant, colorScheme) {
2611
+ if (variant === "comment") {
2612
+ return colorScheme === "dark" ? darkCommentVariant : commentVariant;
2613
+ }
2614
+ if (variant === "note") {
2615
+ return colorScheme === "dark" ? darkNoteVariant : noteVariant;
2616
+ }
2617
+ return colorScheme === "dark" ? darkArticleVariant : articleVariant;
2618
+ }
2619
+ export {
2620
+ $createFootnoteSectionNode as $,
2621
+ ALERT_LABELS as A,
2622
+ BANNER_LABELS as B,
2623
+ ColorSchemeProvider as C,
2624
+ darkArticleVariant as D,
2625
+ darkCommentVariant as E,
2626
+ FootnotePlugin as F,
2627
+ GridContainerNode as G,
2628
+ darkNoteVariant as H,
2629
+ decodeThumbHash as I,
2630
+ noteVariant as J,
2631
+ KaTeXBlockNode as K,
2632
+ LinkCardNode as L,
2633
+ MermaidNode as M,
2634
+ NESTED_EDITOR_NODES as N,
2635
+ useColorScheme as O,
2636
+ PortalThemeProvider as P,
2637
+ useFootnoteContent as Q,
2638
+ RendererConfigProvider as R,
2639
+ useFootnoteDefinitions as S,
2640
+ useFootnoteDisplayNumber as T,
2641
+ useRendererConfig as U,
2642
+ useRendererMode as V,
2643
+ $isAlertQuoteNode as W,
2644
+ RendererWrapper as X,
2645
+ AlertRenderer as Y,
2646
+ AlertQuoteNode as Z,
2647
+ $isBannerNode as _,
2648
+ allNodes as a,
2649
+ BannerRenderer as a0,
2650
+ BannerNode as a1,
2651
+ normalizeBannerType as a2,
2652
+ $isCodeBlockNode as a3,
2653
+ CodeBlockRenderer as a4,
2654
+ CodeBlockNode as a5,
2655
+ SpoilerNode as a6,
2656
+ MentionNode as a7,
2657
+ ImageNode as a8,
2658
+ FootnoteNode as a9,
2659
+ TaskListItemNode as aa,
2660
+ VideoNode as ab,
2661
+ DetailsNode as ac,
2662
+ $createAlertQuoteNode as ad,
2663
+ $createImageNode as ae,
2664
+ $createKaTeXInlineNode as af,
2665
+ $createKaTeXBlockNode as ag,
2666
+ $createBannerNode as ah,
2667
+ $createDetailsNode as ai,
2668
+ $isDetailsNode as aj,
2669
+ $createFootnoteNode as ak,
2670
+ $isFootnoteNode as al,
2671
+ $createMentionNode as am,
2672
+ $isMentionNode as an,
2673
+ $createSpoilerNode as ao,
2674
+ $isSpoilerNode as ap,
2675
+ $createTaskListItemNode as aq,
2676
+ $isTaskListItemNode as ar,
2677
+ builtinNodes as b,
2678
+ customNodes as c,
2679
+ clsx as d,
2680
+ editorTheme as e,
2681
+ $createGridContainerNode as f,
2682
+ getVariantClass as g,
2683
+ $createLinkCardNode as h,
2684
+ $createMermaidNode as i,
2685
+ $isFootnoteSectionNode as j,
2686
+ $isGridContainerNode as k,
2687
+ $isKaTeXBlockNode as l,
2688
+ $isKaTeXInlineNode as m,
2689
+ $isLinkCardNode as n,
2690
+ $isMermaidNode as o,
2691
+ ALERT_TYPES as p,
2692
+ BANNER_TYPES as q,
2693
+ FootnoteDefinitionsProvider as r,
2694
+ FootnoteSectionNode as s,
2695
+ KaTeXInlineNode as t,
2696
+ KaTeXRenderer as u,
2697
+ LinkCardRenderer as v,
2698
+ articleVariant as w,
2699
+ commentVariant as x,
2700
+ computeImageMeta as y,
2701
+ createRendererDecoration as z
2702
+ };