@haklex/rich-editor 0.0.31 → 0.0.32

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 (59) hide show
  1. package/dist/{RichEditor-D1twFNL-.js → RichEditor-CXvIPtDS.js} +423 -73
  2. package/dist/components/decorators/GridEditDecorator.d.ts.map +1 -1
  3. package/dist/components/renderers/AlertStaticDecorator.d.ts +9 -0
  4. package/dist/components/renderers/AlertStaticDecorator.d.ts.map +1 -0
  5. package/dist/components/renderers/BannerStaticDecorator.d.ts +9 -0
  6. package/dist/components/renderers/BannerStaticDecorator.d.ts.map +1 -0
  7. package/dist/components/renderers/FootnoteStaticRenderer.d.ts +5 -0
  8. package/dist/components/renderers/FootnoteStaticRenderer.d.ts.map +1 -0
  9. package/dist/components/renderers/GridStaticDecorator.d.ts +9 -0
  10. package/dist/components/renderers/GridStaticDecorator.d.ts.map +1 -0
  11. package/dist/context/NestedContentRendererContext.d.ts +6 -0
  12. package/dist/context/NestedContentRendererContext.d.ts.map +1 -0
  13. package/dist/editor.mjs +2 -2
  14. package/dist/index.d.ts +7 -2
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.mjs +95 -52
  17. package/dist/nodes/AlertQuoteEditNode.d.ts +9 -2
  18. package/dist/nodes/AlertQuoteEditNode.d.ts.map +1 -1
  19. package/dist/nodes/AlertQuoteNode.d.ts +5 -6
  20. package/dist/nodes/AlertQuoteNode.d.ts.map +1 -1
  21. package/dist/nodes/BannerEditNode.d.ts +9 -2
  22. package/dist/nodes/BannerEditNode.d.ts.map +1 -1
  23. package/dist/nodes/BannerNode.d.ts +5 -6
  24. package/dist/nodes/BannerNode.d.ts.map +1 -1
  25. package/dist/nodes/GridContainerNode.d.ts +3 -5
  26. package/dist/nodes/GridContainerNode.d.ts.map +1 -1
  27. package/dist/nodes/GridEditNode.d.ts +12 -1
  28. package/dist/nodes/GridEditNode.d.ts.map +1 -1
  29. package/dist/plugins/AlertPlugin.d.ts.map +1 -1
  30. package/dist/plugins/FootnotePlugin.d.ts.map +1 -1
  31. package/dist/rich-editor.css +1 -1
  32. package/dist/static-entry.d.ts +22 -0
  33. package/dist/static-entry.d.ts.map +1 -0
  34. package/dist/static-entry.mjs +39 -0
  35. package/dist/styles/shared.css.d.ts.map +1 -1
  36. package/dist/styles/theme.d.ts.map +1 -1
  37. package/dist/styles-entry.d.ts +7 -0
  38. package/dist/styles-entry.d.ts.map +1 -0
  39. package/dist/styles-entry.mjs +26 -0
  40. package/dist/{utils-BuZqZkYK.js → theme-C_ic3l9g.js} +665 -804
  41. package/dist/transformers/alert.d.ts.map +1 -1
  42. package/dist/transformers/container.d.ts.map +1 -1
  43. package/dist/transformers/grid-container.d.ts.map +1 -1
  44. package/dist/transformers/index.d.ts +1 -0
  45. package/dist/transformers/index.d.ts.map +1 -1
  46. package/dist/transformers/superscript-subscript.d.ts +2 -0
  47. package/dist/transformers/superscript-subscript.d.ts.map +1 -0
  48. package/dist/types.d.ts +0 -10
  49. package/dist/types.d.ts.map +1 -1
  50. package/dist/utils/extractTextContent.d.ts +3 -0
  51. package/dist/utils/extractTextContent.d.ts.map +1 -0
  52. package/dist/utils-C9cpKf8S.js +28 -0
  53. package/package.json +11 -7
  54. package/dist/RichRenderer-hHQ4JQs0.js +0 -113
  55. package/dist/components/RichRenderer.d.ts +0 -3
  56. package/dist/components/RichRenderer.d.ts.map +0 -1
  57. package/dist/renderer.d.ts +0 -3
  58. package/dist/renderer.d.ts.map +0 -1
  59. package/dist/renderer.mjs +0 -4
@@ -7,17 +7,24 @@ import { LinkNode, AutoLinkNode } from "@lexical/link";
7
7
  import { ListNode, ListItemNode } from "@lexical/list";
8
8
  import { HeadingNode, QuoteNode } from "@lexical/rich-text";
9
9
  import { TableNode, TableCellNode, TableRowNode } from "@lexical/table";
10
- import { DecoratorNode, ElementNode, $insertNodes, $getRoot, createEditor, $getNodeByKey, $nodesOfType } from "lexical";
11
- import { OctagonAlert, TriangleAlert, MessageSquareWarning, Lightbulb, Info, Flag, Code, ChevronRight, LayoutGrid, ImageIcon, Sigma, Link, Workflow, Video } from "lucide-react";
12
- import { createContext, use, useMemo, createElement, useCallback, useState, useEffect, useRef } from "react";
10
+ import { DecoratorNode, $insertNodes, ElementNode, $getNodeByKey } from "lexical";
11
+ import { createContext, use, useMemo, createElement, useState, useRef, useEffect, useCallback } from "react";
13
12
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
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";
13
+ import { OctagonAlert, TriangleAlert, MessageSquareWarning, Lightbulb, Info, Code, ChevronRight, ImageIcon, Sigma, Link, Workflow, Video } from "lucide-react";
18
14
  import { thumbHashToDataURL, rgbaToThumbHash } from "thumbhash";
19
- import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
20
- import { TooltipRoot, TooltipTrigger, TooltipContent } from "@haklex/rich-editor-ui";
15
+ const NestedContentRendererContext = createContext(
16
+ null
17
+ );
18
+ const NestedContentRendererProvider = NestedContentRendererContext.Provider;
19
+ function useNestedContentRenderer() {
20
+ const fn = use(NestedContentRendererContext);
21
+ if (!fn) {
22
+ throw new Error(
23
+ "useNestedContentRenderer must be used within a NestedContentRendererProvider"
24
+ );
25
+ }
26
+ return fn;
27
+ }
21
28
  const RendererConfigContext = createContext({
22
29
  config: void 0,
23
30
  mode: "renderer",
@@ -79,10 +86,11 @@ const AlertRenderer = ({ type }) => {
79
86
  /* @__PURE__ */ jsx("span", { className: "rich-alert-label", children: ALERT_LABELS[type] })
80
87
  ] });
81
88
  };
82
- function AlertReadOnlyDecorator({
89
+ function AlertStaticDecorator({
83
90
  alertType,
84
- contentEditor
91
+ contentState
85
92
  }) {
93
+ const renderContent = useNestedContentRenderer();
86
94
  return /* @__PURE__ */ jsxs(Fragment, { children: [
87
95
  /* @__PURE__ */ jsx(
88
96
  RendererWrapper,
@@ -92,435 +100,18 @@ function AlertReadOnlyDecorator({
92
100
  props: { type: alertType }
93
101
  }
94
102
  ),
95
- /* @__PURE__ */ jsx("div", { className: "rich-alert-content", children: /* @__PURE__ */ jsx(LexicalNestedComposer, { initialEditor: contentEditor, children: /* @__PURE__ */ jsx(
96
- RichTextPlugin,
97
- {
98
- contentEditable: /* @__PURE__ */ jsx(
99
- ContentEditable,
100
- {
101
- className: "rich-alert-content-editable",
102
- style: { outline: "none" },
103
- "aria-placeholder": "",
104
- placeholder: /* @__PURE__ */ jsx("span", { style: { display: "none" } })
105
- }
106
- ),
107
- ErrorBoundary: LexicalErrorBoundary
108
- }
109
- ) }) })
103
+ /* @__PURE__ */ jsx("div", { className: "rich-alert-content", children: renderContent(contentState) })
110
104
  ] });
111
105
  }
112
- const editorTheme = {
113
- text: {
114
- bold: "rich-text-bold",
115
- italic: "rich-text-italic",
116
- underline: "rich-text-underline",
117
- strikethrough: "rich-text-strikethrough",
118
- code: "rich-text-code",
119
- highlight: "rich-text-highlight"
120
- },
121
- heading: {
122
- h1: "rich-heading-h1",
123
- h2: "rich-heading-h2",
124
- h3: "rich-heading-h3",
125
- h4: "rich-heading-h4",
126
- h5: "rich-heading-h5",
127
- h6: "rich-heading-h6"
128
- },
129
- list: {
130
- ol: "rich-list-ol",
131
- ul: "rich-list-ul",
132
- listitem: "rich-list-item",
133
- listitemChecked: "rich-list-item-checked",
134
- listitemUnchecked: "rich-list-item-unchecked",
135
- checklist: "rich-checklist",
136
- nested: {
137
- listitem: "rich-list-nested-item"
138
- }
139
- },
140
- quote: "rich-quote",
141
- link: "rich-link",
142
- paragraph: "rich-paragraph",
143
- code: "rich-code-block",
144
- table: "rich-table",
145
- tableCell: "rich-table-cell",
146
- tableCellHeader: "rich-table-cell-header",
147
- tableScrollableWrapper: "rich-table-scrollable-wrapper",
148
- /** Used by @lexical/extension HorizontalRuleNode */
149
- hr: "rich-hr"
150
- };
151
- const FootnoteDefinitionsContext = createContext({
152
- definitions: {},
153
- displayNumberMap: {}
154
- });
155
- function FootnoteDefinitionsProvider({
156
- definitions,
157
- displayNumberMap,
158
- children
159
- }) {
160
- const value = useMemo(
161
- () => ({ definitions, displayNumberMap }),
162
- [definitions, displayNumberMap]
163
- );
164
- return /* @__PURE__ */ jsx(FootnoteDefinitionsContext, { value, children });
165
- }
166
- function useFootnoteDefinitions() {
167
- return use(FootnoteDefinitionsContext);
168
- }
169
- function useFootnoteContent(identifier) {
170
- const { definitions } = use(FootnoteDefinitionsContext);
171
- return definitions[identifier];
172
- }
173
- function useFootnoteDisplayNumber(identifier) {
174
- const { displayNumberMap } = use(FootnoteDefinitionsContext);
175
- return displayNumberMap[identifier];
176
- }
177
- function FootnoteRenderer({ identifier }) {
178
- const content = useFootnoteContent(identifier);
179
- const displayNumber = useFootnoteDisplayNumber(identifier);
180
- const referenceId = `footnote-ref-${identifier}`;
181
- const targetId = `footnote-${identifier}`;
182
- const handleClick = useCallback(
183
- (e) => {
184
- const target = document.getElementById(targetId) || document.getElementById(`fn-${identifier}`);
185
- if (!target) return;
186
- e.preventDefault();
187
- target.scrollIntoView({ behavior: "smooth", block: "center" });
188
- target.classList.add("rich-footnote-highlight");
189
- window.setTimeout(() => {
190
- target.classList.remove("rich-footnote-highlight");
191
- }, 1200);
192
- },
193
- [identifier, targetId]
194
- );
195
- const label = displayNumber ?? identifier;
196
- return /* @__PURE__ */ jsx("span", { className: "rich-footnote-ref-wrapper", children: /* @__PURE__ */ jsxs(TooltipRoot, { children: [
197
- /* @__PURE__ */ jsx(
198
- TooltipTrigger,
199
- {
200
- render: (props) => /* @__PURE__ */ jsx(
201
- "a",
202
- {
203
- ...props,
204
- className: "rich-footnote-ref",
205
- href: `#${targetId}`,
206
- id: referenceId,
207
- role: "doc-noteref",
208
- "aria-label": content ? `Footnote ${label}: ${content}` : `Footnote ${label}`,
209
- onClick: handleClick,
210
- "data-footnote-ref": identifier,
211
- children: label
212
- }
213
- )
214
- }
215
- ),
216
- content ? /* @__PURE__ */ jsx(TooltipContent, { children: content }) : null
217
- ] }) });
218
- }
219
- class FootnoteNode extends DecoratorNode {
220
- constructor(identifier, key) {
221
- super(key);
222
- __publicField(this, "__identifier");
223
- this.__identifier = identifier;
224
- }
225
- static getType() {
226
- return "footnote";
227
- }
228
- static clone(node) {
229
- return new FootnoteNode(node.__identifier, node.__key);
230
- }
231
- createDOM(_config) {
232
- const sup = document.createElement("sup");
233
- sup.className = "rich-footnote";
234
- return sup;
235
- }
236
- updateDOM() {
237
- return false;
238
- }
239
- isInline() {
240
- return true;
241
- }
242
- static importJSON(serializedNode) {
243
- return $createFootnoteNode(serializedNode.identifier);
244
- }
245
- exportJSON() {
246
- return {
247
- ...super.exportJSON(),
248
- type: "footnote",
249
- identifier: this.__identifier,
250
- version: 1
251
- };
252
- }
253
- getIdentifier() {
254
- return this.getLatest().__identifier;
255
- }
256
- setIdentifier(identifier) {
257
- const writable = this.getWritable();
258
- writable.__identifier = identifier;
259
- }
260
- decorate(_editor, _config) {
261
- return createRendererDecoration("Footnote", FootnoteRenderer, {
262
- identifier: this.__identifier
263
- });
264
- }
265
- }
266
- function $createFootnoteNode(identifier) {
267
- return new FootnoteNode(identifier);
268
- }
269
- let katexModule = null;
270
- let katexLoadPromise = null;
271
- function loadKaTeX() {
272
- if (katexModule) return Promise.resolve(katexModule);
273
- if (!katexLoadPromise) {
274
- katexLoadPromise = import("katex").then((mod) => {
275
- katexModule = mod;
276
- return katexModule;
277
- });
278
- }
279
- return katexLoadPromise;
280
- }
281
- function KaTeXRenderer({ equation, displayMode }) {
282
- const [html, setHtml] = useState(null);
283
- const [error, setError] = useState(null);
284
- useEffect(() => {
285
- let cancelled = false;
286
- loadKaTeX().then((katex) => {
287
- if (cancelled) return;
288
- const rendered = katex.default.renderToString(equation, {
289
- displayMode,
290
- throwOnError: false
291
- });
292
- setHtml(rendered);
293
- setError(null);
294
- }).catch(() => {
295
- if (cancelled) return;
296
- setHtml(null);
297
- setError("KaTeX is not available");
298
- });
299
- return () => {
300
- cancelled = true;
301
- };
302
- }, [equation, displayMode]);
303
- if (error) {
304
- return /* @__PURE__ */ jsx("code", { className: "rich-katex-fallback", children: equation });
305
- }
306
- if (html) {
307
- return /* @__PURE__ */ jsx(
308
- "span",
309
- {
310
- className: displayMode ? "rich-katex-block" : "rich-katex-inline",
311
- dangerouslySetInnerHTML: { __html: html }
312
- }
313
- );
314
- }
315
- return /* @__PURE__ */ jsx("code", { className: "rich-katex-fallback", children: equation });
316
- }
317
- class KaTeXInlineNode extends DecoratorNode {
318
- constructor(equation, key) {
319
- super(key);
320
- __publicField(this, "__equation");
321
- this.__equation = equation;
322
- }
323
- static getType() {
324
- return "katex-inline";
325
- }
326
- static clone(node) {
327
- return new KaTeXInlineNode(node.__equation, node.__key);
328
- }
329
- createDOM(_config) {
330
- return document.createElement("span");
331
- }
332
- updateDOM() {
333
- return false;
334
- }
335
- isInline() {
336
- return true;
337
- }
338
- static importJSON(serializedNode) {
339
- return $createKaTeXInlineNode(serializedNode.equation);
340
- }
341
- exportJSON() {
342
- return {
343
- ...super.exportJSON(),
344
- type: "katex-inline",
345
- equation: this.__equation,
346
- version: 1
347
- };
348
- }
349
- getEquation() {
350
- return this.__equation;
351
- }
352
- setEquation(equation) {
353
- const writable = this.getWritable();
354
- writable.__equation = equation;
355
- }
356
- decorate(_editor, _config) {
357
- return createRendererDecoration("KaTeX", KaTeXRenderer, {
358
- equation: this.__equation,
359
- displayMode: false
360
- });
361
- }
362
- }
363
- function $createKaTeXInlineNode(equation) {
364
- return new KaTeXInlineNode(equation);
365
- }
366
- function $isKaTeXInlineNode(node) {
367
- return node instanceof KaTeXInlineNode;
368
- }
369
- function MentionRenderer({ handle, displayName }) {
370
- const normalizedHandle = handle.replace(/^@+/, "");
371
- const label = displayName || normalizedHandle;
372
- return /* @__PURE__ */ jsx("span", { className: "rich-mention rich-mention-plain", children: /* @__PURE__ */ jsxs("span", { className: "rich-mention-handle", children: [
373
- "@",
374
- label
375
- ] }) });
376
- }
377
- class MentionNode extends DecoratorNode {
378
- constructor(platform, handle, displayName, key) {
379
- super(key);
380
- __publicField(this, "__platform");
381
- __publicField(this, "__handle");
382
- __publicField(this, "__displayName");
383
- this.__platform = platform;
384
- this.__handle = handle;
385
- this.__displayName = displayName;
386
- }
387
- static getType() {
388
- return "mention";
389
- }
390
- static clone(node) {
391
- return new MentionNode(
392
- node.__platform,
393
- node.__handle,
394
- node.__displayName,
395
- node.__key
396
- );
397
- }
398
- createDOM(_config) {
399
- const el = document.createElement("span");
400
- el.style.display = "inline-flex";
401
- el.style.alignItems = "center";
402
- el.style.height = "1lh";
403
- return el;
404
- }
405
- updateDOM() {
406
- return false;
407
- }
408
- isInline() {
409
- return true;
410
- }
411
- getPlatform() {
412
- return this.getLatest().__platform;
413
- }
414
- getHandle() {
415
- return this.getLatest().__handle;
416
- }
417
- getDisplayName() {
418
- return this.getLatest().__displayName;
419
- }
420
- static importJSON(serializedNode) {
421
- return $createMentionNode(
422
- serializedNode.platform,
423
- serializedNode.handle,
424
- serializedNode.displayName
425
- );
426
- }
427
- exportJSON() {
428
- return {
429
- ...super.exportJSON(),
430
- type: "mention",
431
- platform: this.__platform,
432
- handle: this.__handle,
433
- ...this.__displayName ? { displayName: this.__displayName } : {},
434
- version: 1
435
- };
436
- }
437
- decorate(_editor, _config) {
438
- return createRendererDecoration("Mention", MentionRenderer, {
439
- platform: this.__platform,
440
- handle: this.__handle,
441
- displayName: this.__displayName
442
- });
443
- }
444
- }
445
- function $createMentionNode(platform, handle, displayName) {
446
- return new MentionNode(platform, handle, displayName);
447
- }
448
- class SpoilerNode extends ElementNode {
449
- static getType() {
450
- return "spoiler";
451
- }
452
- static clone(node) {
453
- return new SpoilerNode(node.__key);
454
- }
455
- constructor(key) {
456
- super(key);
457
- }
458
- createDOM(_config) {
459
- const span = document.createElement("span");
460
- span.className = "rich-spoiler";
461
- span.setAttribute("role", "button");
462
- span.setAttribute("tabindex", "0");
463
- span.setAttribute("aria-label", "Spoiler (click to reveal)");
464
- const toggle = () => {
465
- if (span.isContentEditable) return;
466
- const revealed = span.classList.toggle("rich-spoiler-revealed");
467
- span.setAttribute(
468
- "aria-label",
469
- revealed ? "Spoiler (revealed)" : "Spoiler (click to reveal)"
470
- );
471
- };
472
- span.addEventListener("click", toggle);
473
- span.addEventListener("keydown", (e) => {
474
- if (e.key === "Enter" || e.key === " ") {
475
- e.preventDefault();
476
- toggle();
477
- }
478
- });
479
- return span;
480
- }
481
- updateDOM() {
482
- return false;
483
- }
484
- static importJSON(_serializedNode) {
485
- return $createSpoilerNode();
486
- }
487
- exportJSON() {
488
- return {
489
- ...super.exportJSON(),
490
- type: "spoiler",
491
- version: 1
492
- };
493
- }
494
- canInsertTextBefore() {
495
- return true;
496
- }
497
- canInsertTextAfter() {
498
- return true;
499
- }
500
- isInline() {
501
- return true;
106
+ function extractTextContent(state) {
107
+ function walk(node) {
108
+ if (node.text) return node.text;
109
+ if (node.children) return node.children.map(walk).join("");
110
+ if (node.root) return walk(node.root);
111
+ return "";
502
112
  }
113
+ return walk(state);
503
114
  }
504
- function $createSpoilerNode() {
505
- return new SpoilerNode();
506
- }
507
- const NESTED_EDITOR_NODES = [
508
- HeadingNode,
509
- QuoteNode,
510
- ListNode,
511
- ListItemNode,
512
- LinkNode,
513
- AutoLinkNode,
514
- HorizontalRuleNode,
515
- CodeNode,
516
- TableNode,
517
- TableCellNode,
518
- TableRowNode,
519
- SpoilerNode,
520
- MentionNode,
521
- FootnoteNode,
522
- KaTeXInlineNode
523
- ];
524
115
  const ALERT_TYPES = [
525
116
  "note",
526
117
  "tip",
@@ -535,33 +126,39 @@ const ALERT_LABELS = {
535
126
  warning: "Warning",
536
127
  caution: "Caution"
537
128
  };
538
- function createContentEditor$1() {
539
- return createEditor({
540
- namespace: "AlertContent",
541
- nodes: NESTED_EDITOR_NODES,
542
- theme: editorTheme,
543
- onError: (error) => {
544
- console.error("[AlertContent]", error);
545
- }
546
- });
547
- }
548
- const _AlertQuoteNode = class _AlertQuoteNode extends DecoratorNode {
549
- constructor(alertType, contentEditor, key) {
129
+ class AlertQuoteNode extends DecoratorNode {
130
+ constructor(alertType, contentState, key) {
550
131
  super(key);
551
132
  __publicField(this, "__alertType");
552
- __publicField(this, "__contentEditor");
133
+ __publicField(this, "__contentState");
553
134
  this.__alertType = alertType;
554
- this.__contentEditor = contentEditor || createContentEditor$1();
135
+ this.__contentState = contentState || {
136
+ root: {
137
+ children: [
138
+ {
139
+ type: "paragraph",
140
+ children: [],
141
+ direction: null,
142
+ format: "",
143
+ indent: 0,
144
+ textFormat: 0,
145
+ textStyle: "",
146
+ version: 1
147
+ }
148
+ ],
149
+ direction: null,
150
+ format: "",
151
+ indent: 0,
152
+ type: "root",
153
+ version: 1
154
+ }
155
+ };
555
156
  }
556
157
  static getType() {
557
158
  return "alert-quote";
558
159
  }
559
160
  static clone(node) {
560
- return new _AlertQuoteNode(
561
- node.__alertType,
562
- node.__contentEditor,
563
- node.__key
564
- );
161
+ return new AlertQuoteNode(node.__alertType, node.__contentState, node.__key);
565
162
  }
566
163
  createDOM(_config) {
567
164
  const div = document.createElement("div");
@@ -584,81 +181,34 @@ const _AlertQuoteNode = class _AlertQuoteNode extends DecoratorNode {
584
181
  const writable = this.getWritable();
585
182
  writable.__alertType = alertType;
586
183
  }
587
- getContentEditor() {
588
- return this.__contentEditor;
184
+ getContentState() {
185
+ return this.getLatest().__contentState;
186
+ }
187
+ setContentState(state) {
188
+ const writable = this.getWritable();
189
+ writable.__contentState = state;
589
190
  }
590
191
  getTextContent() {
591
- return this.__contentEditor.getEditorState().read(() => {
592
- return $getRoot().getTextContent();
593
- });
192
+ return extractTextContent(this.__contentState);
594
193
  }
595
194
  static importJSON(serializedNode) {
596
- const node = new _AlertQuoteNode(serializedNode.alertType);
597
- if (serializedNode.content) {
598
- const editorState = node.__contentEditor.parseEditorState(
599
- serializedNode.content
600
- );
601
- node.__contentEditor.setEditorState(editorState);
602
- }
603
- return node;
195
+ return new AlertQuoteNode(serializedNode.alertType, serializedNode.content);
604
196
  }
605
197
  exportJSON() {
606
198
  return {
607
199
  ...super.exportJSON(),
608
200
  type: "alert-quote",
609
201
  alertType: this.__alertType,
610
- content: this.__contentEditor.getEditorState().toJSON(),
202
+ content: this.__contentState,
611
203
  version: 1
612
204
  };
613
205
  }
614
206
  decorate(_editor, _config) {
615
- return createElement(AlertReadOnlyDecorator, {
207
+ return createElement(AlertStaticDecorator, {
616
208
  alertType: this.__alertType,
617
- contentEditor: this.__contentEditor
209
+ contentState: this.__contentState
618
210
  });
619
211
  }
620
- };
621
- __publicField(_AlertQuoteNode, "slashMenuItems", [
622
- {
623
- title: "Callout",
624
- icon: createElement(Info, { size: 20 }),
625
- description: "Info callout block",
626
- keywords: ["alert", "note", "info", "callout"],
627
- section: "ADVANCED",
628
- onSelect: (editor) => {
629
- editor.update(() => {
630
- $insertNodes([$createAlertQuoteNode("note")]);
631
- });
632
- }
633
- },
634
- {
635
- title: "Tip",
636
- icon: createElement(Lightbulb, { size: 20 }),
637
- description: "Highlight a useful tip",
638
- keywords: ["alert", "tip", "hint"],
639
- section: "ADVANCED",
640
- onSelect: (editor) => {
641
- editor.update(() => {
642
- $insertNodes([$createAlertQuoteNode("tip")]);
643
- });
644
- }
645
- },
646
- {
647
- title: "Warning",
648
- icon: createElement(TriangleAlert, { size: 20 }),
649
- description: "Warn about something",
650
- keywords: ["alert", "warning", "caution"],
651
- section: "ADVANCED",
652
- onSelect: (editor) => {
653
- editor.update(() => {
654
- $insertNodes([$createAlertQuoteNode("warning")]);
655
- });
656
- }
657
- }
658
- ]);
659
- let AlertQuoteNode = _AlertQuoteNode;
660
- function $createAlertQuoteNode(alertType) {
661
- return new AlertQuoteNode(alertType);
662
212
  }
663
213
  function $isAlertQuoteNode(node) {
664
214
  return node instanceof AlertQuoteNode;
@@ -666,10 +216,11 @@ function $isAlertQuoteNode(node) {
666
216
  const BannerRenderer = ({ type }) => {
667
217
  return /* @__PURE__ */ jsx("span", { className: `rich-banner-icon rich-banner-icon-${type}` });
668
218
  };
669
- function BannerReadOnlyDecorator({
219
+ function BannerStaticDecorator({
670
220
  bannerType,
671
- contentEditor
221
+ contentState
672
222
  }) {
223
+ const renderContent = useNestedContentRenderer();
673
224
  return /* @__PURE__ */ jsxs("div", { className: "rich-banner-inner", children: [
674
225
  /* @__PURE__ */ jsx(
675
226
  RendererWrapper,
@@ -679,21 +230,7 @@ function BannerReadOnlyDecorator({
679
230
  props: { type: bannerType }
680
231
  }
681
232
  ),
682
- /* @__PURE__ */ jsx("div", { className: "rich-banner-content", children: /* @__PURE__ */ jsx(LexicalNestedComposer, { initialEditor: contentEditor, children: /* @__PURE__ */ jsx(
683
- RichTextPlugin,
684
- {
685
- contentEditable: /* @__PURE__ */ jsx(
686
- ContentEditable,
687
- {
688
- className: "rich-banner-content-editable",
689
- style: { outline: "none" },
690
- "aria-placeholder": "",
691
- placeholder: /* @__PURE__ */ jsx("span", { style: { display: "none" } })
692
- }
693
- ),
694
- ErrorBoundary: LexicalErrorBoundary
695
- }
696
- ) }) })
233
+ /* @__PURE__ */ jsx("div", { className: "rich-banner-content", children: renderContent(contentState) })
697
234
  ] });
698
235
  }
699
236
  const LEGACY_TYPE_MAP = {
@@ -719,29 +256,39 @@ const BANNER_LABELS = {
719
256
  warning: "Warning",
720
257
  caution: "Caution"
721
258
  };
722
- function createContentEditor() {
723
- return createEditor({
724
- namespace: "BannerContent",
725
- nodes: NESTED_EDITOR_NODES,
726
- theme: editorTheme,
727
- onError: (error) => {
728
- console.error("[BannerContent]", error);
729
- }
730
- });
731
- }
732
- const _BannerNode = class _BannerNode extends DecoratorNode {
733
- constructor(bannerType, contentEditor, key) {
259
+ class BannerNode extends DecoratorNode {
260
+ constructor(bannerType, contentState, key) {
734
261
  super(key);
735
262
  __publicField(this, "__bannerType");
736
- __publicField(this, "__contentEditor");
263
+ __publicField(this, "__contentState");
737
264
  this.__bannerType = bannerType;
738
- this.__contentEditor = contentEditor || createContentEditor();
265
+ this.__contentState = contentState || {
266
+ root: {
267
+ children: [
268
+ {
269
+ type: "paragraph",
270
+ children: [],
271
+ direction: null,
272
+ format: "",
273
+ indent: 0,
274
+ textFormat: 0,
275
+ textStyle: "",
276
+ version: 1
277
+ }
278
+ ],
279
+ direction: null,
280
+ format: "",
281
+ indent: 0,
282
+ type: "root",
283
+ version: 1
284
+ }
285
+ };
739
286
  }
740
287
  static getType() {
741
288
  return "banner";
742
289
  }
743
290
  static clone(node) {
744
- return new _BannerNode(node.__bannerType, node.__contentEditor, node.__key);
291
+ return new BannerNode(node.__bannerType, node.__contentState, node.__key);
745
292
  }
746
293
  createDOM(_config) {
747
294
  const div = document.createElement("div");
@@ -764,24 +311,23 @@ const _BannerNode = class _BannerNode extends DecoratorNode {
764
311
  const writable = this.getWritable();
765
312
  writable.__bannerType = bannerType;
766
313
  }
767
- getContentEditor() {
768
- return this.__contentEditor;
314
+ getContentState() {
315
+ return this.getLatest().__contentState;
316
+ }
317
+ setContentState(state) {
318
+ const writable = this.getWritable();
319
+ writable.__contentState = state;
769
320
  }
770
321
  getTextContent() {
771
- return this.__contentEditor.getEditorState().read(() => {
772
- return $getRoot().getTextContent();
773
- });
322
+ return extractTextContent(this.__contentState);
774
323
  }
775
324
  static importJSON(serializedNode) {
776
325
  const legacy = serializedNode;
777
326
  const bannerType = normalizeBannerType(serializedNode.bannerType);
778
- const node = new _BannerNode(bannerType);
779
327
  if (serializedNode.content) {
780
- const editorState = node.__contentEditor.parseEditorState(
781
- serializedNode.content
782
- );
783
- node.__contentEditor.setEditorState(editorState);
784
- } else if (legacy.children) {
328
+ return new BannerNode(bannerType, serializedNode.content);
329
+ }
330
+ if (legacy.children) {
785
331
  const content = {
786
332
  root: {
787
333
  children: legacy.children,
@@ -792,44 +338,25 @@ const _BannerNode = class _BannerNode extends DecoratorNode {
792
338
  version: 1
793
339
  }
794
340
  };
795
- const editorState = node.__contentEditor.parseEditorState(content);
796
- node.__contentEditor.setEditorState(editorState);
341
+ return new BannerNode(bannerType, content);
797
342
  }
798
- return node;
343
+ return new BannerNode(bannerType);
799
344
  }
800
345
  exportJSON() {
801
346
  return {
802
347
  ...super.exportJSON(),
803
348
  type: "banner",
804
349
  bannerType: this.__bannerType,
805
- content: this.__contentEditor.getEditorState().toJSON(),
350
+ content: this.__contentState,
806
351
  version: 1
807
352
  };
808
353
  }
809
354
  decorate(_editor, _config) {
810
- return createElement(BannerReadOnlyDecorator, {
355
+ return createElement(BannerStaticDecorator, {
811
356
  bannerType: this.__bannerType,
812
- contentEditor: this.__contentEditor
357
+ contentState: this.__contentState
813
358
  });
814
359
  }
815
- };
816
- __publicField(_BannerNode, "slashMenuItems", [
817
- {
818
- title: "Banner",
819
- icon: createElement(Flag, { size: 20 }),
820
- description: "Highlighted banner block",
821
- keywords: ["banner", "notice", "announcement"],
822
- section: "ADVANCED",
823
- onSelect: (editor) => {
824
- editor.update(() => {
825
- $insertNodes([$createBannerNode("note")]);
826
- });
827
- }
828
- }
829
- ]);
830
- let BannerNode = _BannerNode;
831
- function $createBannerNode(bannerType) {
832
- return new BannerNode(bannerType);
833
360
  }
834
361
  function $isBannerNode(node) {
835
362
  return node instanceof BannerNode;
@@ -1152,6 +679,117 @@ let DetailsNode = _DetailsNode;
1152
679
  function $createDetailsNode(summary, open = false) {
1153
680
  return new DetailsNode(summary, open);
1154
681
  }
682
+ const FootnoteDefinitionsContext = createContext({
683
+ definitions: {},
684
+ displayNumberMap: {}
685
+ });
686
+ function FootnoteDefinitionsProvider({
687
+ definitions,
688
+ displayNumberMap,
689
+ children
690
+ }) {
691
+ const value = useMemo(
692
+ () => ({ definitions, displayNumberMap }),
693
+ [definitions, displayNumberMap]
694
+ );
695
+ return /* @__PURE__ */ jsx(FootnoteDefinitionsContext, { value, children });
696
+ }
697
+ function useFootnoteDefinitions() {
698
+ return use(FootnoteDefinitionsContext);
699
+ }
700
+ function useFootnoteContent(identifier) {
701
+ const { definitions } = use(FootnoteDefinitionsContext);
702
+ return definitions[identifier];
703
+ }
704
+ function useFootnoteDisplayNumber(identifier) {
705
+ const { displayNumberMap } = use(FootnoteDefinitionsContext);
706
+ return displayNumberMap[identifier];
707
+ }
708
+ function FootnoteStaticRenderer({
709
+ identifier
710
+ }) {
711
+ const content = useFootnoteContent(identifier);
712
+ const displayNumber = useFootnoteDisplayNumber(identifier);
713
+ const referenceId = `footnote-ref-${identifier}`;
714
+ const targetId = `footnote-${identifier}`;
715
+ const handleClick = useCallback(
716
+ (e) => {
717
+ const target = document.getElementById(targetId) || document.getElementById(`fn-${identifier}`);
718
+ if (!target) return;
719
+ e.preventDefault();
720
+ target.scrollIntoView({ behavior: "smooth", block: "center" });
721
+ target.classList.add("rich-footnote-highlight");
722
+ window.setTimeout(() => {
723
+ target.classList.remove("rich-footnote-highlight");
724
+ }, 1200);
725
+ },
726
+ [identifier, targetId]
727
+ );
728
+ const label = displayNumber ?? identifier;
729
+ return /* @__PURE__ */ jsx("span", { className: "rich-footnote-ref-wrapper", children: /* @__PURE__ */ jsx(
730
+ "a",
731
+ {
732
+ className: "rich-footnote-ref",
733
+ href: `#${targetId}`,
734
+ id: referenceId,
735
+ role: "doc-noteref",
736
+ "aria-label": content ? `Footnote ${label}: ${content}` : `Footnote ${label}`,
737
+ onClick: handleClick,
738
+ "data-footnote-ref": identifier,
739
+ children: label
740
+ }
741
+ ) });
742
+ }
743
+ class FootnoteNode extends DecoratorNode {
744
+ constructor(identifier, key) {
745
+ super(key);
746
+ __publicField(this, "__identifier");
747
+ this.__identifier = identifier;
748
+ }
749
+ static getType() {
750
+ return "footnote";
751
+ }
752
+ static clone(node) {
753
+ return new FootnoteNode(node.__identifier, node.__key);
754
+ }
755
+ createDOM(_config) {
756
+ const sup = document.createElement("sup");
757
+ sup.className = "rich-footnote";
758
+ return sup;
759
+ }
760
+ updateDOM() {
761
+ return false;
762
+ }
763
+ isInline() {
764
+ return true;
765
+ }
766
+ static importJSON(serializedNode) {
767
+ return $createFootnoteNode(serializedNode.identifier);
768
+ }
769
+ exportJSON() {
770
+ return {
771
+ ...super.exportJSON(),
772
+ type: "footnote",
773
+ identifier: this.__identifier,
774
+ version: 1
775
+ };
776
+ }
777
+ getIdentifier() {
778
+ return this.getLatest().__identifier;
779
+ }
780
+ setIdentifier(identifier) {
781
+ const writable = this.getWritable();
782
+ writable.__identifier = identifier;
783
+ }
784
+ decorate(_editor, _config) {
785
+ return createRendererDecoration("Footnote", FootnoteStaticRenderer, {
786
+ identifier: this.__identifier
787
+ });
788
+ }
789
+ }
790
+ function $createFootnoteNode(identifier) {
791
+ return new FootnoteNode(identifier);
792
+ }
1155
793
  function FootnoteSectionRenderer({
1156
794
  definitions
1157
795
  }) {
@@ -1292,11 +930,12 @@ function $createFootnoteSectionNode(definitions = {}) {
1292
930
  function $isFootnoteSectionNode(node) {
1293
931
  return node instanceof FootnoteSectionNode;
1294
932
  }
1295
- function GridReadOnlyDecorator({
933
+ function GridStaticDecorator({
1296
934
  cols,
1297
935
  gap,
1298
- cellEditors
936
+ cellStates
1299
937
  }) {
938
+ const renderContent = useNestedContentRenderer();
1300
939
  return /* @__PURE__ */ jsx(
1301
940
  "div",
1302
941
  {
@@ -1306,59 +945,53 @@ function GridReadOnlyDecorator({
1306
945
  gridTemplateColumns: `repeat(${cols}, 1fr)`,
1307
946
  gap
1308
947
  },
1309
- children: cellEditors.map((editor, i) => /* @__PURE__ */ jsx("div", { className: "rich-grid-cell", children: /* @__PURE__ */ jsx(LexicalNestedComposer, { initialEditor: editor, children: /* @__PURE__ */ jsx(
1310
- RichTextPlugin,
1311
- {
1312
- contentEditable: /* @__PURE__ */ jsx(
1313
- ContentEditable,
1314
- {
1315
- className: "rich-grid-cell-editable",
1316
- style: { outline: "none" },
1317
- "aria-placeholder": "",
1318
- placeholder: /* @__PURE__ */ jsx("span", { style: { display: "none" } })
1319
- }
1320
- ),
1321
- ErrorBoundary: LexicalErrorBoundary
1322
- }
1323
- ) }) }, i))
948
+ children: cellStates.map((state, i) => /* @__PURE__ */ jsx("div", { className: "rich-grid-cell", children: renderContent(state) }, i))
1324
949
  }
1325
950
  );
1326
951
  }
1327
- function createCellEditor() {
1328
- return createEditor({
1329
- namespace: "GridCell",
1330
- nodes: NESTED_EDITOR_NODES,
1331
- theme: editorTheme,
1332
- onError: (error) => {
1333
- console.error("[GridCell]", error);
1334
- }
1335
- });
1336
- }
1337
- const _GridContainerNode = class _GridContainerNode extends DecoratorNode {
1338
- constructor(cols = 2, gap, cellEditors, key) {
952
+ class GridContainerNode extends DecoratorNode {
953
+ constructor(cols = 2, gap, cellStates, key) {
1339
954
  super(key);
1340
955
  __publicField(this, "__cols");
1341
956
  __publicField(this, "__gap");
1342
- __publicField(this, "__cellEditors");
957
+ __publicField(this, "__cellStates");
1343
958
  this.__cols = cols;
1344
959
  this.__gap = gap || "16px";
1345
- if (cellEditors) {
1346
- this.__cellEditors = cellEditors;
960
+ if (cellStates) {
961
+ this.__cellStates = cellStates;
1347
962
  } else {
1348
- this.__cellEditors = Array.from(
1349
- { length: cols },
1350
- () => createCellEditor()
1351
- );
963
+ const emptyState = {
964
+ root: {
965
+ children: [
966
+ {
967
+ type: "paragraph",
968
+ children: [],
969
+ direction: null,
970
+ format: "",
971
+ indent: 0,
972
+ textFormat: 0,
973
+ textStyle: "",
974
+ version: 1
975
+ }
976
+ ],
977
+ direction: null,
978
+ format: "",
979
+ indent: 0,
980
+ type: "root",
981
+ version: 1
982
+ }
983
+ };
984
+ this.__cellStates = Array.from({ length: cols }, () => emptyState);
1352
985
  }
1353
986
  }
1354
987
  static getType() {
1355
988
  return "grid-container";
1356
989
  }
1357
990
  static clone(node) {
1358
- return new _GridContainerNode(
991
+ return new GridContainerNode(
1359
992
  node.__cols,
1360
993
  node.__gap,
1361
- [...node.__cellEditors],
994
+ [...node.__cellStates],
1362
995
  node.__key
1363
996
  );
1364
997
  }
@@ -1378,11 +1011,32 @@ const _GridContainerNode = class _GridContainerNode extends DecoratorNode {
1378
1011
  }
1379
1012
  setCols(cols) {
1380
1013
  const writable = this.getWritable();
1381
- const prev = writable.__cellEditors.length;
1014
+ const prev = writable.__cellStates.length;
1382
1015
  writable.__cols = cols;
1383
1016
  if (cols > prev) {
1017
+ const emptyState = {
1018
+ root: {
1019
+ children: [
1020
+ {
1021
+ type: "paragraph",
1022
+ children: [],
1023
+ direction: null,
1024
+ format: "",
1025
+ indent: 0,
1026
+ textFormat: 0,
1027
+ textStyle: "",
1028
+ version: 1
1029
+ }
1030
+ ],
1031
+ direction: null,
1032
+ format: "",
1033
+ indent: 0,
1034
+ type: "root",
1035
+ version: 1
1036
+ }
1037
+ };
1384
1038
  for (let i = prev; i < cols; i++) {
1385
- writable.__cellEditors.push(createCellEditor());
1039
+ writable.__cellStates.push(emptyState);
1386
1040
  }
1387
1041
  }
1388
1042
  }
@@ -1393,69 +1047,77 @@ const _GridContainerNode = class _GridContainerNode extends DecoratorNode {
1393
1047
  const writable = this.getWritable();
1394
1048
  writable.__gap = gap;
1395
1049
  }
1396
- getCellEditors() {
1397
- return this.getLatest().__cellEditors;
1050
+ getCellStates() {
1051
+ return this.getLatest().__cellStates;
1398
1052
  }
1399
1053
  addCells(count) {
1400
1054
  const writable = this.getWritable();
1055
+ const emptyState = {
1056
+ root: {
1057
+ children: [
1058
+ {
1059
+ type: "paragraph",
1060
+ children: [],
1061
+ direction: null,
1062
+ format: "",
1063
+ indent: 0,
1064
+ textFormat: 0,
1065
+ textStyle: "",
1066
+ version: 1
1067
+ }
1068
+ ],
1069
+ direction: null,
1070
+ format: "",
1071
+ indent: 0,
1072
+ type: "root",
1073
+ version: 1
1074
+ }
1075
+ };
1401
1076
  for (let i = 0; i < count; i++) {
1402
- writable.__cellEditors.push(createCellEditor());
1077
+ writable.__cellStates.push(emptyState);
1403
1078
  }
1404
1079
  }
1405
1080
  removeCells(count) {
1406
1081
  const writable = this.getWritable();
1407
- const editors = writable.__cellEditors;
1408
- const toRemove = Math.min(count, editors.length);
1082
+ const states = writable.__cellStates;
1083
+ const toRemove = Math.min(count, states.length);
1409
1084
  for (let i = 0; i < toRemove; i++) {
1410
- const editor = editors.at(-1);
1411
- if (!editor) break;
1412
- const isEmpty = editor.getEditorState().read(() => {
1413
- return $getRoot().getTextContentSize() === 0;
1414
- });
1085
+ const state = states.at(-1);
1086
+ if (!state) break;
1087
+ const isEmpty = extractTextContent(state).trim() === "";
1415
1088
  if (!isEmpty) break;
1416
- editors.pop();
1089
+ states.pop();
1417
1090
  }
1418
1091
  }
1419
1092
  getTextContent() {
1420
- return this.__cellEditors.map(
1421
- (editor) => editor.getEditorState().read(() => $getRoot().getTextContent())
1422
- ).join("\n");
1093
+ return this.__cellStates.map((s) => extractTextContent(s)).join("\n");
1423
1094
  }
1424
1095
  static importJSON(serializedNode) {
1425
1096
  const legacy = serializedNode;
1426
1097
  const cols = legacy.cols || 2;
1427
1098
  const rawGap = legacy.gap;
1428
1099
  const gap = typeof rawGap === "number" ? `${rawGap}px` : rawGap;
1429
- const node = new _GridContainerNode(cols, gap);
1430
1100
  if (legacy.cells && legacy.cells.length > 0) {
1431
- const editors = legacy.cells.map((cellState) => {
1432
- const editor = createCellEditor();
1433
- const editorState = editor.parseEditorState(cellState);
1434
- editor.setEditorState(editorState);
1435
- return editor;
1436
- });
1437
- node.__cellEditors = editors;
1438
- } else if (legacy.children) {
1439
- const { children } = legacy;
1440
- const editors = children.map((child) => {
1441
- const editor = createCellEditor();
1442
- const content = {
1443
- root: {
1444
- children: [child],
1445
- direction: null,
1446
- format: "",
1447
- indent: 0,
1448
- type: "root",
1449
- version: 1
1450
- }
1451
- };
1452
- const editorState = editor.parseEditorState(content);
1453
- editor.setEditorState(editorState);
1454
- return editor;
1455
- });
1456
- node.__cellEditors = editors;
1101
+ return new GridContainerNode(cols, gap, legacy.cells);
1102
+ }
1103
+ if (legacy.children) {
1104
+ const cellStates = legacy.children.map(
1105
+ (child) => {
1106
+ return {
1107
+ root: {
1108
+ children: [child],
1109
+ direction: null,
1110
+ format: "",
1111
+ indent: 0,
1112
+ type: "root",
1113
+ version: 1
1114
+ }
1115
+ };
1116
+ }
1117
+ );
1118
+ return new GridContainerNode(cols, gap, cellStates);
1457
1119
  }
1458
- return node;
1120
+ return new GridContainerNode(cols, gap);
1459
1121
  }
1460
1122
  exportJSON() {
1461
1123
  return {
@@ -1463,35 +1125,18 @@ const _GridContainerNode = class _GridContainerNode extends DecoratorNode {
1463
1125
  type: "grid-container",
1464
1126
  cols: this.__cols,
1465
1127
  gap: this.__gap,
1466
- cells: this.__cellEditors.map(
1467
- (editor) => editor.getEditorState().toJSON()
1468
- ),
1128
+ cells: this.__cellStates,
1469
1129
  version: 1
1470
1130
  };
1471
1131
  }
1472
1132
  decorate(_editor, _config) {
1473
- return createElement(GridReadOnlyDecorator, {
1133
+ return createElement(GridStaticDecorator, {
1474
1134
  cols: this.__cols,
1475
1135
  gap: this.__gap,
1476
- cellEditors: this.__cellEditors
1136
+ cellStates: this.__cellStates
1477
1137
  });
1478
1138
  }
1479
- };
1480
- __publicField(_GridContainerNode, "slashMenuItems", [
1481
- {
1482
- title: "Grid",
1483
- icon: createElement(LayoutGrid, { size: 20 }),
1484
- description: "Grid layout container",
1485
- keywords: ["grid", "columns", "layout"],
1486
- section: "LAYOUT",
1487
- onSelect: (editor) => {
1488
- editor.update(() => {
1489
- $insertNodes([$createGridContainerNode(2)]);
1490
- });
1491
- }
1492
- }
1493
- ]);
1494
- let GridContainerNode = _GridContainerNode;
1139
+ }
1495
1140
  function $createGridContainerNode(cols = 2, gap) {
1496
1141
  return new GridContainerNode(cols, gap);
1497
1142
  }
@@ -1752,10 +1397,58 @@ __publicField(_ImageNode, "slashMenuItems", [
1752
1397
  });
1753
1398
  }
1754
1399
  }
1755
- ]);
1756
- let ImageNode = _ImageNode;
1757
- function $createImageNode(payload) {
1758
- return new ImageNode(payload);
1400
+ ]);
1401
+ let ImageNode = _ImageNode;
1402
+ function $createImageNode(payload) {
1403
+ return new ImageNode(payload);
1404
+ }
1405
+ let katexModule = null;
1406
+ let katexLoadPromise = null;
1407
+ function loadKaTeX() {
1408
+ if (katexModule) return Promise.resolve(katexModule);
1409
+ if (!katexLoadPromise) {
1410
+ katexLoadPromise = import("katex").then((mod) => {
1411
+ katexModule = mod;
1412
+ return katexModule;
1413
+ });
1414
+ }
1415
+ return katexLoadPromise;
1416
+ }
1417
+ function KaTeXRenderer({ equation, displayMode }) {
1418
+ const [html, setHtml] = useState(null);
1419
+ const [error, setError] = useState(null);
1420
+ useEffect(() => {
1421
+ let cancelled = false;
1422
+ loadKaTeX().then((katex) => {
1423
+ if (cancelled) return;
1424
+ const rendered = katex.default.renderToString(equation, {
1425
+ displayMode,
1426
+ throwOnError: false
1427
+ });
1428
+ setHtml(rendered);
1429
+ setError(null);
1430
+ }).catch(() => {
1431
+ if (cancelled) return;
1432
+ setHtml(null);
1433
+ setError("KaTeX is not available");
1434
+ });
1435
+ return () => {
1436
+ cancelled = true;
1437
+ };
1438
+ }, [equation, displayMode]);
1439
+ if (error) {
1440
+ return /* @__PURE__ */ jsx("code", { className: "rich-katex-fallback", children: equation });
1441
+ }
1442
+ if (html) {
1443
+ return /* @__PURE__ */ jsx(
1444
+ "span",
1445
+ {
1446
+ className: displayMode ? "rich-katex-block" : "rich-katex-inline",
1447
+ dangerouslySetInnerHTML: { __html: html }
1448
+ }
1449
+ );
1450
+ }
1451
+ return /* @__PURE__ */ jsx("code", { className: "rich-katex-fallback", children: equation });
1759
1452
  }
1760
1453
  const _KaTeXBlockNode = class _KaTeXBlockNode extends DecoratorNode {
1761
1454
  constructor(equation, key) {
@@ -1826,6 +1519,58 @@ function $createKaTeXBlockNode(equation) {
1826
1519
  function $isKaTeXBlockNode(node) {
1827
1520
  return node instanceof KaTeXBlockNode;
1828
1521
  }
1522
+ class KaTeXInlineNode extends DecoratorNode {
1523
+ constructor(equation, key) {
1524
+ super(key);
1525
+ __publicField(this, "__equation");
1526
+ this.__equation = equation;
1527
+ }
1528
+ static getType() {
1529
+ return "katex-inline";
1530
+ }
1531
+ static clone(node) {
1532
+ return new KaTeXInlineNode(node.__equation, node.__key);
1533
+ }
1534
+ createDOM(_config) {
1535
+ return document.createElement("span");
1536
+ }
1537
+ updateDOM() {
1538
+ return false;
1539
+ }
1540
+ isInline() {
1541
+ return true;
1542
+ }
1543
+ static importJSON(serializedNode) {
1544
+ return $createKaTeXInlineNode(serializedNode.equation);
1545
+ }
1546
+ exportJSON() {
1547
+ return {
1548
+ ...super.exportJSON(),
1549
+ type: "katex-inline",
1550
+ equation: this.__equation,
1551
+ version: 1
1552
+ };
1553
+ }
1554
+ getEquation() {
1555
+ return this.__equation;
1556
+ }
1557
+ setEquation(equation) {
1558
+ const writable = this.getWritable();
1559
+ writable.__equation = equation;
1560
+ }
1561
+ decorate(_editor, _config) {
1562
+ return createRendererDecoration("KaTeX", KaTeXRenderer, {
1563
+ equation: this.__equation,
1564
+ displayMode: false
1565
+ });
1566
+ }
1567
+ }
1568
+ function $createKaTeXInlineNode(equation) {
1569
+ return new KaTeXInlineNode(equation);
1570
+ }
1571
+ function $isKaTeXInlineNode(node) {
1572
+ return node instanceof KaTeXInlineNode;
1573
+ }
1829
1574
  function LinkCardRenderer({
1830
1575
  url,
1831
1576
  title,
@@ -1992,6 +1737,85 @@ function $createLinkCardNode(payload) {
1992
1737
  function $isLinkCardNode(node) {
1993
1738
  return node instanceof LinkCardNode;
1994
1739
  }
1740
+ function MentionRenderer({ handle, displayName }) {
1741
+ const normalizedHandle = handle.replace(/^@+/, "");
1742
+ const label = displayName || normalizedHandle;
1743
+ return /* @__PURE__ */ jsx("span", { className: "rich-mention rich-mention-plain", children: /* @__PURE__ */ jsxs("span", { className: "rich-mention-handle", children: [
1744
+ "@",
1745
+ label
1746
+ ] }) });
1747
+ }
1748
+ class MentionNode extends DecoratorNode {
1749
+ constructor(platform, handle, displayName, key) {
1750
+ super(key);
1751
+ __publicField(this, "__platform");
1752
+ __publicField(this, "__handle");
1753
+ __publicField(this, "__displayName");
1754
+ this.__platform = platform;
1755
+ this.__handle = handle;
1756
+ this.__displayName = displayName;
1757
+ }
1758
+ static getType() {
1759
+ return "mention";
1760
+ }
1761
+ static clone(node) {
1762
+ return new MentionNode(
1763
+ node.__platform,
1764
+ node.__handle,
1765
+ node.__displayName,
1766
+ node.__key
1767
+ );
1768
+ }
1769
+ createDOM(_config) {
1770
+ const el = document.createElement("span");
1771
+ el.style.display = "inline-flex";
1772
+ el.style.alignItems = "center";
1773
+ el.style.height = "1lh";
1774
+ return el;
1775
+ }
1776
+ updateDOM() {
1777
+ return false;
1778
+ }
1779
+ isInline() {
1780
+ return true;
1781
+ }
1782
+ getPlatform() {
1783
+ return this.getLatest().__platform;
1784
+ }
1785
+ getHandle() {
1786
+ return this.getLatest().__handle;
1787
+ }
1788
+ getDisplayName() {
1789
+ return this.getLatest().__displayName;
1790
+ }
1791
+ static importJSON(serializedNode) {
1792
+ return $createMentionNode(
1793
+ serializedNode.platform,
1794
+ serializedNode.handle,
1795
+ serializedNode.displayName
1796
+ );
1797
+ }
1798
+ exportJSON() {
1799
+ return {
1800
+ ...super.exportJSON(),
1801
+ type: "mention",
1802
+ platform: this.__platform,
1803
+ handle: this.__handle,
1804
+ ...this.__displayName ? { displayName: this.__displayName } : {},
1805
+ version: 1
1806
+ };
1807
+ }
1808
+ decorate(_editor, _config) {
1809
+ return createRendererDecoration("Mention", MentionRenderer, {
1810
+ platform: this.__platform,
1811
+ handle: this.__handle,
1812
+ displayName: this.__displayName
1813
+ });
1814
+ }
1815
+ }
1816
+ function $createMentionNode(platform, handle, displayName) {
1817
+ return new MentionNode(platform, handle, displayName);
1818
+ }
1995
1819
  function MermaidRenderer({ content }) {
1996
1820
  return /* @__PURE__ */ jsx("div", { className: "rich-mermaid-block", children: /* @__PURE__ */ jsx("pre", { children: /* @__PURE__ */ jsx("code", { children: content }) }) });
1997
1821
  }
@@ -2074,6 +1898,65 @@ function $createMermaidNode(diagram) {
2074
1898
  function $isMermaidNode(node) {
2075
1899
  return node instanceof MermaidNode;
2076
1900
  }
1901
+ class SpoilerNode extends ElementNode {
1902
+ static getType() {
1903
+ return "spoiler";
1904
+ }
1905
+ static clone(node) {
1906
+ return new SpoilerNode(node.__key);
1907
+ }
1908
+ constructor(key) {
1909
+ super(key);
1910
+ }
1911
+ createDOM(_config) {
1912
+ const span = document.createElement("span");
1913
+ span.className = "rich-spoiler";
1914
+ span.setAttribute("role", "button");
1915
+ span.setAttribute("tabindex", "0");
1916
+ span.setAttribute("aria-label", "Spoiler (click to reveal)");
1917
+ const toggle = () => {
1918
+ if (span.isContentEditable) return;
1919
+ const revealed = span.classList.toggle("rich-spoiler-revealed");
1920
+ span.setAttribute(
1921
+ "aria-label",
1922
+ revealed ? "Spoiler (revealed)" : "Spoiler (click to reveal)"
1923
+ );
1924
+ };
1925
+ span.addEventListener("click", toggle);
1926
+ span.addEventListener("keydown", (e) => {
1927
+ if (e.key === "Enter" || e.key === " ") {
1928
+ e.preventDefault();
1929
+ toggle();
1930
+ }
1931
+ });
1932
+ return span;
1933
+ }
1934
+ updateDOM() {
1935
+ return false;
1936
+ }
1937
+ static importJSON(_serializedNode) {
1938
+ return $createSpoilerNode();
1939
+ }
1940
+ exportJSON() {
1941
+ return {
1942
+ ...super.exportJSON(),
1943
+ type: "spoiler",
1944
+ version: 1
1945
+ };
1946
+ }
1947
+ canInsertTextBefore() {
1948
+ return true;
1949
+ }
1950
+ canInsertTextAfter() {
1951
+ return true;
1952
+ }
1953
+ isInline() {
1954
+ return true;
1955
+ }
1956
+ }
1957
+ function $createSpoilerNode() {
1958
+ return new SpoilerNode();
1959
+ }
2077
1960
  function VideoRenderer({
2078
1961
  src,
2079
1962
  poster,
@@ -2217,135 +2100,113 @@ const allNodes = [
2217
2100
  ...builtinNodes,
2218
2101
  ...customNodes
2219
2102
  ];
2220
- function FootnotePlugin({ children }) {
2221
- const [editor] = useLexicalComposerContext();
2222
- const [definitions, setDefinitions] = useState({});
2223
- const [displayNumberMap, setDisplayNumberMap] = useState({});
2224
- useEffect(() => {
2225
- return editor.registerUpdateListener(({ editorState }) => {
2226
- editorState.read(() => {
2227
- const footnoteNodes = $nodesOfType(FootnoteNode);
2228
- const seen = /* @__PURE__ */ new Set();
2229
- const numberMap = {};
2230
- let counter = 1;
2231
- for (const node of footnoteNodes) {
2232
- const id = node.getIdentifier();
2233
- if (!seen.has(id)) {
2234
- seen.add(id);
2235
- numberMap[id] = counter++;
2236
- }
2237
- }
2238
- setDisplayNumberMap(numberMap);
2239
- const sectionNodes = $nodesOfType(FootnoteSectionNode);
2240
- if (sectionNodes.length > 0) {
2241
- setDefinitions(sectionNodes[0].getDefinitions());
2242
- } else {
2243
- setDefinitions({});
2244
- }
2245
- });
2246
- });
2247
- }, [editor]);
2248
- return /* @__PURE__ */ jsx(
2249
- FootnoteDefinitionsProvider,
2250
- {
2251
- definitions,
2252
- displayNumberMap,
2253
- children
2103
+ const editorTheme = {
2104
+ text: {
2105
+ bold: "rich-text-bold",
2106
+ italic: "rich-text-italic",
2107
+ underline: "rich-text-underline",
2108
+ strikethrough: "rich-text-strikethrough",
2109
+ superscript: "rich-text-superscript",
2110
+ subscript: "rich-text-subscript",
2111
+ code: "rich-text-code",
2112
+ highlight: "rich-text-highlight"
2113
+ },
2114
+ heading: {
2115
+ h1: "rich-heading-h1",
2116
+ h2: "rich-heading-h2",
2117
+ h3: "rich-heading-h3",
2118
+ h4: "rich-heading-h4",
2119
+ h5: "rich-heading-h5",
2120
+ h6: "rich-heading-h6"
2121
+ },
2122
+ list: {
2123
+ ol: "rich-list-ol",
2124
+ ul: "rich-list-ul",
2125
+ listitem: "rich-list-item",
2126
+ listitemChecked: "rich-list-item-checked",
2127
+ listitemUnchecked: "rich-list-item-unchecked",
2128
+ checklist: "rich-checklist",
2129
+ nested: {
2130
+ listitem: "rich-list-nested-item"
2254
2131
  }
2255
- );
2256
- }
2257
- var articleVariant = "v10dui1 r8uj4t0 _17pm0gw0 v10dui0";
2258
- var darkArticleVariant = "v10dui2 r8uj4t0 _17pm0gw2 v10dui0";
2259
- var commentVariant = "_14ko36c1 r8uj4t0 _17pm0gw1 _14ko36c0";
2260
- var darkCommentVariant = "_14ko36c2 r8uj4t0 _17pm0gw3 _14ko36c0";
2261
- var noteVariant = "ys4fw91 r8uj4t0 _17pm0gw4 ys4fw90";
2262
- var darkNoteVariant = "ys4fw92 r8uj4t0 _17pm0gw5 ys4fw90";
2263
- function clsx(...args) {
2264
- return args.filter(Boolean).join(" ");
2265
- }
2266
- function getVariantClass(variant, colorScheme) {
2267
- if (variant === "comment") {
2268
- return colorScheme === "dark" ? darkCommentVariant : commentVariant;
2269
- }
2270
- if (variant === "note") {
2271
- return colorScheme === "dark" ? darkNoteVariant : noteVariant;
2272
- }
2273
- return colorScheme === "dark" ? darkArticleVariant : articleVariant;
2274
- }
2132
+ },
2133
+ quote: "rich-quote",
2134
+ link: "rich-link",
2135
+ paragraph: "rich-paragraph",
2136
+ code: "rich-code-block",
2137
+ table: "rich-table",
2138
+ tableCell: "rich-table-cell",
2139
+ tableCellHeader: "rich-table-cell-header",
2140
+ tableScrollableWrapper: "rich-table-scrollable-wrapper",
2141
+ /** Used by @lexical/extension HorizontalRuleNode */
2142
+ hr: "rich-hr"
2143
+ };
2275
2144
  export {
2276
2145
  $createFootnoteSectionNode as $,
2277
2146
  ALERT_LABELS as A,
2278
2147
  BANNER_LABELS as B,
2279
2148
  ColorSchemeProvider as C,
2280
- darkArticleVariant as D,
2281
- darkCommentVariant as E,
2282
- FootnotePlugin as F,
2149
+ FootnoteSectionNode as D,
2150
+ FootnoteStaticRenderer as E,
2151
+ FootnoteDefinitionsProvider as F,
2283
2152
  GridContainerNode as G,
2284
- darkNoteVariant as H,
2285
- decodeThumbHash as I,
2286
- noteVariant as J,
2153
+ KaTeXInlineNode as H,
2154
+ KaTeXRenderer as I,
2155
+ LinkCardRenderer as J,
2287
2156
  KaTeXBlockNode as K,
2288
2157
  LinkCardNode as L,
2289
2158
  MermaidNode as M,
2290
- NESTED_EDITOR_NODES as N,
2291
- useColorScheme as O,
2292
- useFootnoteContent as P,
2293
- useFootnoteDefinitions as Q,
2159
+ NestedContentRendererProvider as N,
2160
+ computeImageMeta as O,
2161
+ decodeThumbHash as P,
2162
+ $isAlertQuoteNode as Q,
2294
2163
  RendererConfigProvider as R,
2295
- useFootnoteDisplayNumber as S,
2296
- useRendererConfig as T,
2297
- useRendererMode as U,
2298
- useVariant as V,
2299
- $isAlertQuoteNode as W,
2300
- RendererWrapper as X,
2301
- AlertRenderer as Y,
2302
- AlertQuoteNode as Z,
2303
- $isBannerNode as _,
2164
+ AlertRenderer as S,
2165
+ SpoilerNode as T,
2166
+ MentionNode as U,
2167
+ FootnoteNode as V,
2168
+ AlertQuoteNode as W,
2169
+ $isBannerNode as X,
2170
+ BannerRenderer as Y,
2171
+ BannerNode as Z,
2172
+ normalizeBannerType as _,
2304
2173
  allNodes as a,
2305
- BannerRenderer as a0,
2306
- BannerNode as a1,
2307
- normalizeBannerType as a2,
2308
- $isCodeBlockNode as a3,
2309
- CodeBlockRenderer as a4,
2310
- CodeBlockNode as a5,
2311
- SpoilerNode as a6,
2312
- MentionNode as a7,
2313
- ImageNode as a8,
2314
- FootnoteNode as a9,
2315
- VideoNode as aa,
2316
- DetailsNode as ab,
2317
- $createAlertQuoteNode as ac,
2318
- $createImageNode as ad,
2319
- $createKaTeXInlineNode as ae,
2320
- $createKaTeXBlockNode as af,
2321
- $createBannerNode as ag,
2322
- $createDetailsNode as ah,
2323
- $createFootnoteNode as ai,
2324
- $createMentionNode as aj,
2325
- $createSpoilerNode as ak,
2174
+ $isCodeBlockNode as a0,
2175
+ CodeBlockRenderer as a1,
2176
+ CodeBlockNode as a2,
2177
+ ImageNode as a3,
2178
+ VideoNode as a4,
2179
+ DetailsNode as a5,
2180
+ $createImageNode as a6,
2181
+ $createKaTeXInlineNode as a7,
2182
+ $createKaTeXBlockNode as a8,
2183
+ $createDetailsNode as a9,
2184
+ $createFootnoteNode as aa,
2185
+ $createMentionNode as ab,
2186
+ $createSpoilerNode as ac,
2326
2187
  builtinNodes as b,
2327
2188
  customNodes as c,
2328
- clsx as d,
2329
- editorTheme as e,
2330
- $createGridContainerNode as f,
2331
- getVariantClass as g,
2332
- $createLinkCardNode as h,
2333
- $createMermaidNode as i,
2334
- $isFootnoteSectionNode as j,
2335
- $isGridContainerNode as k,
2336
- $isKaTeXBlockNode as l,
2337
- $isKaTeXInlineNode as m,
2338
- $isLinkCardNode as n,
2339
- $isMermaidNode as o,
2340
- ALERT_TYPES as p,
2341
- BANNER_TYPES as q,
2342
- FootnoteDefinitionsProvider as r,
2343
- FootnoteSectionNode as s,
2344
- KaTeXInlineNode as t,
2345
- KaTeXRenderer as u,
2346
- LinkCardRenderer as v,
2347
- articleVariant as w,
2348
- commentVariant as x,
2349
- computeImageMeta as y,
2350
- createRendererDecoration as z
2189
+ RendererWrapper as d,
2190
+ createRendererDecoration as e,
2191
+ editorTheme as f,
2192
+ extractTextContent as g,
2193
+ useFootnoteContent as h,
2194
+ useFootnoteDefinitions as i,
2195
+ useFootnoteDisplayNumber as j,
2196
+ useNestedContentRenderer as k,
2197
+ useRendererConfig as l,
2198
+ useRendererMode as m,
2199
+ useVariant as n,
2200
+ $createGridContainerNode as o,
2201
+ $createLinkCardNode as p,
2202
+ $createMermaidNode as q,
2203
+ $isFootnoteSectionNode as r,
2204
+ $isGridContainerNode as s,
2205
+ $isKaTeXBlockNode as t,
2206
+ useColorScheme as u,
2207
+ $isKaTeXInlineNode as v,
2208
+ $isLinkCardNode as w,
2209
+ $isMermaidNode as x,
2210
+ ALERT_TYPES as y,
2211
+ BANNER_TYPES as z
2351
2212
  };