@haklex/rich-editor 0.0.37 → 0.0.39

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 (31) hide show
  1. package/README.md +4 -4
  2. package/dist/{RichEditor-B9KIgSwn.js → RichEditor-BcbNmzGB.js} +531 -12
  3. package/dist/components/RichEditor.d.ts.map +1 -1
  4. package/dist/components/decorators/AlertEditDecorator.d.ts.map +1 -1
  5. package/dist/components/decorators/CodeBlockEditDecorator.d.ts.map +1 -1
  6. package/dist/components/renderers/CodeBlockRenderer.d.ts +5 -0
  7. package/dist/components/renderers/CodeBlockRenderer.d.ts.map +1 -1
  8. package/dist/editor.mjs +2 -2
  9. package/dist/index.d.ts +2 -0
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.mjs +31 -28
  12. package/dist/nodes/CodeBlockEditNode.d.ts +9 -0
  13. package/dist/nodes/CodeBlockEditNode.d.ts.map +1 -1
  14. package/dist/nodes/CodeBlockNode.d.ts +1 -0
  15. package/dist/nodes/CodeBlockNode.d.ts.map +1 -1
  16. package/dist/nodes/MentionNode.d.ts +2 -0
  17. package/dist/nodes/MentionNode.d.ts.map +1 -1
  18. package/dist/plugins/BlockExitPlugin.d.ts +2 -0
  19. package/dist/plugins/BlockExitPlugin.d.ts.map +1 -0
  20. package/dist/rich-editor.css +1 -1
  21. package/dist/static-entry.mjs +1 -1
  22. package/dist/{theme-D1COY7pa.js → theme-gVNBI_ET.js} +79 -50
  23. package/dist/transformers/code-block.d.ts +3 -0
  24. package/dist/transformers/code-block.d.ts.map +1 -0
  25. package/dist/transformers/index.d.ts +1 -0
  26. package/dist/transformers/index.d.ts.map +1 -1
  27. package/dist/transformers/quote.d.ts +3 -0
  28. package/dist/transformers/quote.d.ts.map +1 -0
  29. package/dist/utils/codeBlockSelectionIntent.d.ts +4 -0
  30. package/dist/utils/codeBlockSelectionIntent.d.ts.map +1 -0
  31. package/package.json +4 -4
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
  ## 包定位
7
7
 
8
8
  - `@haklex/rich-editor`:编辑器核心(`RichEditor`)
9
- - `@haklex/rich-renderer`:只读渲染引擎(`RichRenderer`)
9
+ - `@haklex/rich-static-renderer`:只读渲染引擎(`RichRenderer`)
10
10
  - `@haklex/rich-renderers`:静态增强渲染器聚合(codeblock、image、video、mermaid...)
11
11
  - `@haklex/rich-renderers-edit`:编辑增强渲染器与扩展插件聚合
12
12
  - `@haklex/rich-kit-shiro`:一站式生产组合(编辑 + 渲染 + 常用扩展)
@@ -51,7 +51,7 @@ export function DemoEditor() {
51
51
  如果要渲染只读内容:
52
52
 
53
53
  ```tsx
54
- import { RichRenderer } from '@haklex/rich-renderer'
54
+ import { RichRenderer } from '@haklex/rich-static-renderer'
55
55
 
56
56
  <RichRenderer value={value} />
57
57
  ```
@@ -67,7 +67,7 @@ import { RichRenderer } from '@haklex/rich-renderer'
67
67
  | --- | --- |
68
68
  | `@haklex/rich-editor` | 完整导出(组件、节点、插件命令、上下文、类型) |
69
69
  | `@haklex/rich-editor/editor` | 轻入口:`RichEditor` + 节点配置 |
70
- | `@haklex/rich-editor/static` | 渲染/SSR 相关静态工具(供 `@haklex/rich-renderer` 与扩展包使用) |
70
+ | `@haklex/rich-editor/static` | 渲染/SSR 相关静态工具(供 `@haklex/rich-static-renderer` 与扩展包使用) |
71
71
  | `@haklex/rich-editor/styles` | 仅样式变量/variant class 导出(不含 Lexical theme 对象) |
72
72
  | `@haklex/rich-editor/style.css` | 打包后的编辑器样式 |
73
73
 
@@ -307,7 +307,7 @@ static slashMenuItems = [
307
307
 
308
308
  ```tsx
309
309
  import { RichEditor } from '@haklex/rich-editor'
310
- import { RichRenderer } from '@haklex/rich-renderer'
310
+ import { RichRenderer } from '@haklex/rich-static-renderer'
311
311
 
312
312
  <RichEditor extraNodes={[PollEditNode]}>{/* <PollPlugin /> */}</RichEditor>
313
313
 
@@ -12,10 +12,10 @@ import { ListPlugin } from "@lexical/react/LexicalListPlugin";
12
12
  import { RichTextPlugin } from "@lexical/react/LexicalRichTextPlugin";
13
13
  import { TabIndentationPlugin } from "@lexical/react/LexicalTabIndentationPlugin";
14
14
  import { TablePlugin } from "@lexical/react/LexicalTablePlugin";
15
- import { V as $isAlertQuoteNode, d as RendererWrapper, W as AlertRenderer, X as SpoilerNode, Y as MentionNode, Z as FootnoteNode, L as KaTeXInlineNode, _ as AlertQuoteNode, f as editorTheme, a0 as $isBannerNode, a1 as BannerRenderer, a2 as BannerNode, a3 as normalizeBannerType, a4 as $isCodeBlockNode, a5 as CodeBlockRenderer, a6 as CodeBlockNode, i as useFootnoteDefinitions, G as FootnoteSectionNode, t as $isGridContainerNode, I as GridContainerNode, b as builtinNodes, K as KaTeXBlockNode, J as ImageNode, a7 as VideoNode, O as LinkCardNode, a8 as DetailsNode, Q as MermaidNode, F as FootnoteDefinitionsProvider, p as $createImageNode, T as computeImageMeta, S as OPEN_IMAGE_UPLOAD_DIALOG_COMMAND, a9 as $createKaTeXInlineNode, aa as $createKaTeXBlockNode, ab as $createDetailsNode, ac as $createFootnoteNode, s as $isFootnoteSectionNode, $ as $createFootnoteSectionNode, ad as $createMentionNode, g as extractTextContent, ae as $createSpoilerNode, r as $createMermaidNode, C as ColorSchemeProvider, R as RendererConfigProvider } from "./theme-D1COY7pa.js";
16
- import { $getNodeByKey, $insertNodes, createEditor, $nodesOfType, $getRoot, createCommand, COMMAND_PRIORITY_EDITOR, $parseSerializedNode, $getSelection, $isRangeSelection, $createParagraphNode, COMMAND_PRIORITY_HIGH, PASTE_COMMAND, $createTextNode, KEY_ENTER_COMMAND } from "lexical";
15
+ import { Y as $isAlertQuoteNode, d as RendererWrapper, Z as AlertRenderer, _ as SpoilerNode, T as MentionNode, a0 as FootnoteNode, O as KaTeXInlineNode, a1 as AlertQuoteNode, f as editorTheme, a2 as $isBannerNode, a3 as BannerRenderer, a4 as BannerNode, a5 as normalizeBannerType, a6 as $isCodeBlockNode, a7 as CodeBlockRenderer, a8 as CodeBlockNode, i as useFootnoteDefinitions, I as FootnoteSectionNode, v as $isGridContainerNode, K as GridContainerNode, b as builtinNodes, M as KaTeXBlockNode, L as ImageNode, a9 as VideoNode, Q as LinkCardNode, aa as DetailsNode, U as MermaidNode, F as FootnoteDefinitionsProvider, p as $createImageNode, W as computeImageMeta, V as OPEN_IMAGE_UPLOAD_DIALOG_COMMAND, ab as $createKaTeXInlineNode, ac as $createKaTeXBlockNode, ad as $createDetailsNode, ae as $createFootnoteNode, t as $isFootnoteSectionNode, $ as $createFootnoteSectionNode, r as $createMentionNode, g as extractTextContent, af as $createSpoilerNode, s as $createMermaidNode, C as ColorSchemeProvider, R as RendererConfigProvider } from "./theme-gVNBI_ET.js";
16
+ import { $getNodeByKey, KEY_ENTER_COMMAND, COMMAND_PRIORITY_CRITICAL, KEY_ARROW_DOWN_COMMAND, COMMAND_PRIORITY_HIGH, $getRoot, $createParagraphNode, $isParagraphNode, $getSelection, $isRangeSelection, $insertNodes, createEditor, $isElementNode, $isDecoratorNode, $createNodeSelection, $setSelection, $nodesOfType, createCommand, COMMAND_PRIORITY_EDITOR, $isRootNode, $isNodeSelection, KEY_BACKSPACE_COMMAND, KEY_DELETE_COMMAND, KEY_ARROW_UP_COMMAND, $isTextNode, $parseSerializedNode, PASTE_COMMAND, $createTextNode, $createLineBreakNode } from "lexical";
17
17
  import { Info, Lightbulb, TriangleAlert, Flag, LayoutGrid, Plus, Minus, Check, Upload, Link2 } from "lucide-react";
18
- import { useCallback, createElement, useState, useEffect, createContext, use, useRef, useMemo } from "react";
18
+ import { useCallback, useEffect, createElement, useState, createContext, use, useRef, useMemo } from "react";
19
19
  import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
20
20
  import { ContentEditable as ContentEditable$1 } from "@lexical/react/LexicalContentEditable";
21
21
  import { LexicalNestedComposer } from "@lexical/react/LexicalNestedComposer";
@@ -23,13 +23,110 @@ import { CodeNode } from "@lexical/code";
23
23
  import { HorizontalRuleNode, INSERT_HORIZONTAL_RULE_COMMAND, $createHorizontalRuleNode } from "@lexical/extension";
24
24
  import { LinkNode, AutoLinkNode, createLinkMatcherWithRegExp, registerAutoLink } from "@lexical/link";
25
25
  import { ListNode, ListItemNode } from "@lexical/list";
26
- import { HeadingNode, QuoteNode, DRAG_DROP_PASTE } from "@lexical/rich-text";
26
+ import { HeadingNode, QuoteNode, $isQuoteNode, DRAG_DROP_PASTE, $createQuoteNode } from "@lexical/rich-text";
27
27
  import { TableNode, TableCellNode, TableRowNode } from "@lexical/table";
28
+ import { useLexicalNodeSelection } from "@lexical/react/useLexicalNodeSelection";
28
29
  import { Dialog, DialogPopup, DialogTitle, SegmentedControl } from "@haklex/rich-editor-ui";
29
30
  import { b as clsx, g as getVariantClass } from "./utils-fpeaZV1R.js";
30
31
  import { MarkdownShortcutPlugin } from "@lexical/react/LexicalMarkdownShortcutPlugin";
31
- import { CHECK_LIST, TRANSFORMERS } from "@lexical/markdown";
32
+ import { CHECK_LIST, TRANSFORMERS, QUOTE, CODE } from "@lexical/markdown";
32
33
  import { GIT_ALERT_TRANSFORMER as GIT_ALERT_TRANSFORMER$1, CONTAINER_TRANSFORMER as CONTAINER_TRANSFORMER$1, FOOTNOTE_TRANSFORMER as FOOTNOTE_TRANSFORMER$1, FOOTNOTE_SECTION_TRANSFORMER as FOOTNOTE_SECTION_TRANSFORMER$1, KATEX_INLINE_TRANSFORMER as KATEX_INLINE_TRANSFORMER$1, KATEX_BLOCK_TRANSFORMER as KATEX_BLOCK_TRANSFORMER$1, MENTION_TRANSFORMER as MENTION_TRANSFORMER$1, SPOILER_TRANSFORMER as SPOILER_TRANSFORMER$1, INSERT_TRANSFORMER, SUPERSCRIPT_TRANSFORMER, SUBSCRIPT_TRANSFORMER, IMAGE_BLOCK_TRANSFORMER, VIDEO_BLOCK_TRANSFORMER, CODE_BLOCK_NODE_TRANSFORMER, LINK_CARD_BLOCK_TRANSFORMER, MERMAID_BLOCK_TRANSFORMER, HORIZONTAL_RULE_BLOCK_TRANSFORMER, TABLE_BLOCK_TRANSFORMER } from "@haklex/rich-headless/transformers";
34
+ function ExitBlockPlugin({
35
+ parentEditor,
36
+ nodeKey
37
+ }) {
38
+ const [editor] = useLexicalComposerContext();
39
+ useEffect(() => {
40
+ const focusParent = () => {
41
+ parentEditor.focus(() => {
42
+ parentEditor.update(() => {
43
+ const alertNode = $getNodeByKey(nodeKey);
44
+ if (alertNode) {
45
+ const next = alertNode.getNextSibling();
46
+ if (next) {
47
+ next.selectStart();
48
+ }
49
+ }
50
+ });
51
+ });
52
+ };
53
+ const exitAlert = (removeEmpty) => {
54
+ const root = $getRoot();
55
+ const lastChild = root.getLastChild();
56
+ const shouldRemoveAlert = removeEmpty && root.getChildrenSize() <= 1;
57
+ if (removeEmpty && lastChild) {
58
+ lastChild.remove();
59
+ }
60
+ if (shouldRemoveAlert) {
61
+ parentEditor.update(
62
+ () => {
63
+ const alertNode = $getNodeByKey(nodeKey);
64
+ if (alertNode) {
65
+ const paragraph = $createParagraphNode();
66
+ alertNode.insertAfter(paragraph);
67
+ alertNode.remove();
68
+ paragraph.selectStart();
69
+ }
70
+ },
71
+ { onUpdate: focusParent }
72
+ );
73
+ } else {
74
+ parentEditor.update(
75
+ () => {
76
+ const alertNode = $getNodeByKey(nodeKey);
77
+ if (alertNode) {
78
+ let next = alertNode.getNextSibling();
79
+ if (!next || !$isParagraphNode(next)) {
80
+ next = $createParagraphNode();
81
+ alertNode.insertAfter(next);
82
+ }
83
+ next.selectStart();
84
+ }
85
+ },
86
+ { onUpdate: focusParent }
87
+ );
88
+ }
89
+ };
90
+ const isAtLastEmptyParagraph = () => {
91
+ const selection = $getSelection();
92
+ if (!$isRangeSelection(selection) || !selection.isCollapsed())
93
+ return false;
94
+ const anchorNode = selection.anchor.getNode();
95
+ const topLevelElement = anchorNode.getTopLevelElement();
96
+ return topLevelElement && $isParagraphNode(topLevelElement) && topLevelElement.getTextContent() === "" && topLevelElement.getNextSibling() === null;
97
+ };
98
+ const unregisterEnter = editor.registerCommand(
99
+ KEY_ENTER_COMMAND,
100
+ (event) => {
101
+ if (event?.metaKey || event?.ctrlKey) {
102
+ event.preventDefault();
103
+ exitAlert(false);
104
+ return true;
105
+ }
106
+ if (!isAtLastEmptyParagraph()) return false;
107
+ event?.preventDefault();
108
+ exitAlert(true);
109
+ return true;
110
+ },
111
+ COMMAND_PRIORITY_CRITICAL
112
+ );
113
+ const unregisterArrowDown = editor.registerCommand(
114
+ KEY_ARROW_DOWN_COMMAND,
115
+ (event) => {
116
+ if (!isAtLastEmptyParagraph()) return false;
117
+ event?.preventDefault();
118
+ exitAlert(false);
119
+ return true;
120
+ },
121
+ COMMAND_PRIORITY_HIGH
122
+ );
123
+ return () => {
124
+ unregisterEnter();
125
+ unregisterArrowDown();
126
+ };
127
+ }, [editor, parentEditor, nodeKey]);
128
+ return null;
129
+ }
33
130
  function AlertEditDecorator({
34
131
  nodeKey,
35
132
  alertType,
@@ -78,7 +175,8 @@ function AlertEditDecorator({
78
175
  }
79
176
  ),
80
177
  /* @__PURE__ */ jsx(ListPlugin, {}),
81
- /* @__PURE__ */ jsx(LinkPlugin, {})
178
+ /* @__PURE__ */ jsx(LinkPlugin, {}),
179
+ /* @__PURE__ */ jsx(ExitBlockPlugin, { parentEditor: editor, nodeKey })
82
180
  ] }) })
83
181
  ] });
84
182
  }
@@ -337,13 +435,31 @@ let BannerEditNode = _BannerEditNode;
337
435
  function $createBannerEditNode(bannerType, contentState) {
338
436
  return new BannerEditNode(bannerType, contentState);
339
437
  }
438
+ const codeBlockCursorIntentMap = /* @__PURE__ */ new Map();
439
+ function setCodeBlockCursorIntent(nodeKey, placement) {
440
+ codeBlockCursorIntentMap.set(nodeKey, placement);
441
+ }
442
+ function consumeCodeBlockCursorIntent(nodeKey) {
443
+ const placement = codeBlockCursorIntentMap.get(nodeKey);
444
+ if (!placement) return null;
445
+ codeBlockCursorIntentMap.delete(nodeKey);
446
+ return placement;
447
+ }
340
448
  function CodeBlockEditDecorator({
341
449
  nodeKey,
342
450
  code,
343
451
  language
344
452
  }) {
345
453
  const [editor] = useLexicalComposerContext();
454
+ const [isSelected] = useLexicalNodeSelection(nodeKey);
455
+ const [cursorPlacement, setCursorPlacement] = useState(
456
+ "start"
457
+ );
346
458
  const editable = editor.isEditable();
459
+ useEffect(() => {
460
+ if (!editable || !isSelected) return;
461
+ setCursorPlacement(consumeCodeBlockCursorIntent(nodeKey) ?? "start");
462
+ }, [editable, isSelected, nodeKey]);
347
463
  const handleCodeChange = useCallback(
348
464
  (newCode) => {
349
465
  editor.update(() => {
@@ -355,6 +471,100 @@ function CodeBlockEditDecorator({
355
471
  },
356
472
  [editor, nodeKey]
357
473
  );
474
+ const handleLanguageChange = useCallback(
475
+ (newLanguage) => {
476
+ editor.update(() => {
477
+ const node = $getNodeByKey(nodeKey);
478
+ if ($isCodeBlockNode(node)) {
479
+ node.setLanguage(newLanguage);
480
+ }
481
+ });
482
+ },
483
+ [editor, nodeKey]
484
+ );
485
+ const handleDelete = useCallback(() => {
486
+ editor.getRootElement()?.focus({ preventScroll: true });
487
+ editor.update(() => {
488
+ const node = $getNodeByKey(nodeKey);
489
+ if (!node) return;
490
+ const prev = node.getPreviousSibling();
491
+ const next = node.getNextSibling();
492
+ const parent = node.getParent();
493
+ node.remove();
494
+ if (prev) {
495
+ if ($isElementNode(prev)) {
496
+ prev.selectEnd();
497
+ } else {
498
+ if ($isDecoratorNode(prev) && prev.getType() === "code-block") {
499
+ setCodeBlockCursorIntent(prev.getKey(), "end");
500
+ }
501
+ const selection = $createNodeSelection();
502
+ selection.add(prev.getKey());
503
+ $setSelection(selection);
504
+ }
505
+ return;
506
+ }
507
+ if (next) {
508
+ if ($isElementNode(next)) {
509
+ next.selectStart();
510
+ } else {
511
+ if ($isDecoratorNode(next) && next.getType() === "code-block") {
512
+ setCodeBlockCursorIntent(next.getKey(), "start");
513
+ }
514
+ const selection = $createNodeSelection();
515
+ selection.add(next.getKey());
516
+ $setSelection(selection);
517
+ }
518
+ return;
519
+ }
520
+ if (parent) {
521
+ const p = $createParagraphNode();
522
+ parent.append(p);
523
+ p.selectStart();
524
+ }
525
+ });
526
+ }, [editor, nodeKey]);
527
+ const handleExitBlock = useCallback(
528
+ (direction) => {
529
+ editor.getRootElement()?.focus({ preventScroll: true });
530
+ editor.update(() => {
531
+ const node = $getNodeByKey(nodeKey);
532
+ if (!node) return;
533
+ if (direction === "before") {
534
+ const prev = node.getPreviousSibling();
535
+ if (!prev) return;
536
+ if ($isElementNode(prev)) {
537
+ prev.selectEnd();
538
+ } else {
539
+ if ($isDecoratorNode(prev) && prev.getType() === "code-block") {
540
+ setCodeBlockCursorIntent(prev.getKey(), "end");
541
+ }
542
+ const selection = $createNodeSelection();
543
+ selection.add(prev.getKey());
544
+ $setSelection(selection);
545
+ }
546
+ return;
547
+ }
548
+ let next = node.getNextSibling();
549
+ if (!next) {
550
+ const p = $createParagraphNode();
551
+ node.insertAfter(p);
552
+ next = p;
553
+ }
554
+ if ($isElementNode(next)) {
555
+ next.selectStart();
556
+ } else {
557
+ if ($isDecoratorNode(next) && next.getType() === "code-block") {
558
+ setCodeBlockCursorIntent(next.getKey(), "start");
559
+ }
560
+ const selection = $createNodeSelection();
561
+ selection.add(next.getKey());
562
+ $setSelection(selection);
563
+ }
564
+ });
565
+ },
566
+ [editor, nodeKey]
567
+ );
358
568
  return /* @__PURE__ */ jsx(
359
569
  RendererWrapper,
360
570
  {
@@ -364,17 +574,22 @@ function CodeBlockEditDecorator({
364
574
  code,
365
575
  language,
366
576
  editable,
367
- onCodeChange: editable ? handleCodeChange : void 0
577
+ onCodeChange: editable ? handleCodeChange : void 0,
578
+ onLanguageChange: editable ? handleLanguageChange : void 0,
579
+ onDelete: editable ? handleDelete : void 0,
580
+ onExitBlock: editable ? handleExitBlock : void 0,
581
+ selected: editable ? isSelected : false,
582
+ cursorPlacement: editable ? cursorPlacement : "start"
368
583
  }
369
584
  }
370
585
  );
371
586
  }
372
- class CodeBlockEditNode extends CodeBlockNode {
587
+ const _CodeBlockEditNode = class _CodeBlockEditNode extends CodeBlockNode {
373
588
  static clone(node) {
374
- return new CodeBlockEditNode(node.__code, node.__language, node.__key);
589
+ return new _CodeBlockEditNode(node.__code, node.__language, node.__key);
375
590
  }
376
591
  static importJSON(serializedNode) {
377
- return new CodeBlockEditNode(serializedNode.code, serializedNode.language);
592
+ return new _CodeBlockEditNode(serializedNode.code, serializedNode.language);
378
593
  }
379
594
  decorate(_editor, _config) {
380
595
  return createElement(CodeBlockEditDecorator, {
@@ -383,7 +598,16 @@ class CodeBlockEditNode extends CodeBlockNode {
383
598
  language: this.__language
384
599
  });
385
600
  }
386
- }
601
+ };
602
+ __publicField(_CodeBlockEditNode, "slashMenuItems", CodeBlockNode.slashMenuItems.map((item) => ({
603
+ ...item,
604
+ onSelect: (editor) => {
605
+ editor.update(() => {
606
+ $insertNodes([new _CodeBlockEditNode("", "text")]);
607
+ });
608
+ }
609
+ })));
610
+ let CodeBlockEditNode = _CodeBlockEditNode;
387
611
  function FootnoteSectionEditRenderer({
388
612
  definitions
389
613
  }) {
@@ -871,6 +1095,237 @@ function AutoLinkPlugin({ matchers }) {
871
1095
  }, [editor, matchers]);
872
1096
  return null;
873
1097
  }
1098
+ function selectDecoratorNode(node, cursorPlacement = "start") {
1099
+ if (node.getType() === "code-block") {
1100
+ setCodeBlockCursorIntent(node.getKey(), cursorPlacement);
1101
+ }
1102
+ const selection = $createNodeSelection();
1103
+ selection.add(node.getKey());
1104
+ $setSelection(selection);
1105
+ }
1106
+ function isAtTopLevelBoundary(selection, direction) {
1107
+ const point = selection.anchor;
1108
+ const topLevel = point.getNode().getTopLevelElementOrThrow();
1109
+ const pointNode = point.getNode();
1110
+ if (point.type === "text") {
1111
+ if (!$isTextNode(pointNode)) return false;
1112
+ const expectedOffset = direction === "start" ? 0 : pointNode.getTextContentSize();
1113
+ if (point.offset !== expectedOffset) return false;
1114
+ } else {
1115
+ if (!$isElementNode(pointNode)) return false;
1116
+ const expectedOffset = direction === "start" ? 0 : pointNode.getChildrenSize();
1117
+ if (point.offset !== expectedOffset) return false;
1118
+ }
1119
+ let current = pointNode;
1120
+ while (current && current !== topLevel) {
1121
+ const sibling = direction === "start" ? current.getPreviousSibling() : current.getNextSibling();
1122
+ if (sibling !== null) return false;
1123
+ current = current.getParent();
1124
+ }
1125
+ return current === topLevel;
1126
+ }
1127
+ function isSingleLineParagraph(node) {
1128
+ return $isParagraphNode(node) && !node.getTextContent().includes("\n");
1129
+ }
1130
+ function BlockExitPlugin() {
1131
+ const [editor] = useLexicalComposerContext();
1132
+ useEffect(() => {
1133
+ const unregisterArrowDown = editor.registerCommand(
1134
+ KEY_ARROW_DOWN_COMMAND,
1135
+ (event) => {
1136
+ const selection = $getSelection();
1137
+ if (!$isRangeSelection(selection) || !selection.isCollapsed()) {
1138
+ return false;
1139
+ }
1140
+ const anchorNode = selection.anchor.getNode();
1141
+ const topLevelElement = anchorNode.getTopLevelElementOrThrow();
1142
+ const shouldSelectNextDecorator = isAtTopLevelBoundary(selection, "end") || isSingleLineParagraph(topLevelElement);
1143
+ if (!$isQuoteNode(topLevelElement) && shouldSelectNextDecorator) {
1144
+ const next2 = topLevelElement.getNextSibling();
1145
+ if (next2 && $isDecoratorNode(next2) && next2.isKeyboardSelectable()) {
1146
+ event?.preventDefault();
1147
+ selectDecoratorNode(next2, "start");
1148
+ return true;
1149
+ }
1150
+ }
1151
+ const element = $isElementNode(anchorNode) ? anchorNode : anchorNode.getParentOrThrow();
1152
+ let quoteChild = element;
1153
+ let quoteNode = null;
1154
+ let current = element;
1155
+ while (current) {
1156
+ const parent = current.getParent();
1157
+ if (!parent || $isRootNode(parent)) break;
1158
+ if ($isQuoteNode(parent)) {
1159
+ quoteNode = parent;
1160
+ quoteChild = current;
1161
+ break;
1162
+ }
1163
+ current = parent;
1164
+ }
1165
+ if (!quoteNode) return false;
1166
+ if (quoteChild.getNextSibling() !== null) return false;
1167
+ if (!$isParagraphNode(quoteChild) || quoteChild.getTextContent() !== "")
1168
+ return false;
1169
+ event?.preventDefault();
1170
+ let next = quoteNode.getNextSibling();
1171
+ if (!next) {
1172
+ next = $createParagraphNode();
1173
+ quoteNode.insertAfter(next);
1174
+ }
1175
+ next.selectStart();
1176
+ return true;
1177
+ },
1178
+ COMMAND_PRIORITY_CRITICAL
1179
+ );
1180
+ const unregisterCmdEnter = editor.registerCommand(
1181
+ KEY_ENTER_COMMAND,
1182
+ (event) => {
1183
+ if (!event?.metaKey && !event?.ctrlKey) return false;
1184
+ const selection = $getSelection();
1185
+ if ($isNodeSelection(selection)) {
1186
+ const nodes = selection.getNodes();
1187
+ if (nodes.length !== 1) return false;
1188
+ const node = nodes[0];
1189
+ if (!$isDecoratorNode(node)) return false;
1190
+ event.preventDefault();
1191
+ let next2 = node.getNextSibling();
1192
+ if (!next2) {
1193
+ next2 = $createParagraphNode();
1194
+ node.insertAfter(next2);
1195
+ }
1196
+ if ($isElementNode(next2)) {
1197
+ next2.selectStart();
1198
+ } else if ($isDecoratorNode(next2)) {
1199
+ selectDecoratorNode(next2, "start");
1200
+ }
1201
+ return true;
1202
+ }
1203
+ if (!$isRangeSelection(selection)) return false;
1204
+ const anchorNode = selection.anchor.getNode();
1205
+ const topLevelElement = anchorNode.getTopLevelElementOrThrow();
1206
+ if ($isParagraphNode(topLevelElement)) return false;
1207
+ event.preventDefault();
1208
+ let next = topLevelElement.getNextSibling();
1209
+ if (!next || !$isParagraphNode(next)) {
1210
+ next = $createParagraphNode();
1211
+ topLevelElement.insertAfter(next);
1212
+ }
1213
+ next.selectStart();
1214
+ return true;
1215
+ },
1216
+ COMMAND_PRIORITY_CRITICAL
1217
+ );
1218
+ function handleDeleteDecorator(event) {
1219
+ const selection = $getSelection();
1220
+ if (!$isNodeSelection(selection)) return false;
1221
+ const nodes = selection.getNodes();
1222
+ if (nodes.length !== 1) return false;
1223
+ const node = nodes[0];
1224
+ if (!$isDecoratorNode(node)) return false;
1225
+ event?.preventDefault();
1226
+ const prev = node.getPreviousSibling();
1227
+ const next = node.getNextSibling();
1228
+ node.remove();
1229
+ if (prev && $isElementNode(prev)) {
1230
+ prev.selectEnd();
1231
+ } else if (prev && $isDecoratorNode(prev)) {
1232
+ selectDecoratorNode(prev, "end");
1233
+ } else if (next && $isElementNode(next)) {
1234
+ next.selectStart();
1235
+ } else if (next && $isDecoratorNode(next)) {
1236
+ selectDecoratorNode(next, "start");
1237
+ } else {
1238
+ const root = $getRoot();
1239
+ if (root && $isElementNode(root)) {
1240
+ const p = $createParagraphNode();
1241
+ root.append(p);
1242
+ p.selectStart();
1243
+ }
1244
+ }
1245
+ return true;
1246
+ }
1247
+ const unregisterBackspace = editor.registerCommand(
1248
+ KEY_BACKSPACE_COMMAND,
1249
+ handleDeleteDecorator,
1250
+ COMMAND_PRIORITY_HIGH
1251
+ );
1252
+ const unregisterDelete = editor.registerCommand(
1253
+ KEY_DELETE_COMMAND,
1254
+ handleDeleteDecorator,
1255
+ COMMAND_PRIORITY_HIGH
1256
+ );
1257
+ const unregisterArrowUpDecorator = editor.registerCommand(
1258
+ KEY_ARROW_UP_COMMAND,
1259
+ (event) => {
1260
+ const selection = $getSelection();
1261
+ if ($isRangeSelection(selection) && selection.isCollapsed()) {
1262
+ const anchorNode = selection.anchor.getNode();
1263
+ const topLevelElement = anchorNode.getTopLevelElementOrThrow();
1264
+ const shouldSelectPreviousDecorator = isAtTopLevelBoundary(selection, "start") || isSingleLineParagraph(topLevelElement);
1265
+ if (!$isQuoteNode(topLevelElement) && shouldSelectPreviousDecorator) {
1266
+ const prev2 = topLevelElement.getPreviousSibling();
1267
+ if (prev2 && $isDecoratorNode(prev2) && prev2.isKeyboardSelectable()) {
1268
+ event?.preventDefault();
1269
+ selectDecoratorNode(prev2, "end");
1270
+ return true;
1271
+ }
1272
+ }
1273
+ }
1274
+ if (!$isNodeSelection(selection)) return false;
1275
+ const nodes = selection.getNodes();
1276
+ if (nodes.length !== 1) return false;
1277
+ const node = nodes[0];
1278
+ if (!$isDecoratorNode(node)) return false;
1279
+ const prev = node.getPreviousSibling();
1280
+ if (prev && $isElementNode(prev)) {
1281
+ event?.preventDefault();
1282
+ prev.selectEnd();
1283
+ return true;
1284
+ }
1285
+ if (prev && $isDecoratorNode(prev)) {
1286
+ event?.preventDefault();
1287
+ selectDecoratorNode(prev, "end");
1288
+ return true;
1289
+ }
1290
+ return false;
1291
+ },
1292
+ COMMAND_PRIORITY_CRITICAL
1293
+ );
1294
+ const unregisterArrowDownDecorator = editor.registerCommand(
1295
+ KEY_ARROW_DOWN_COMMAND,
1296
+ (event) => {
1297
+ const selection = $getSelection();
1298
+ if (!$isNodeSelection(selection)) return false;
1299
+ const nodes = selection.getNodes();
1300
+ if (nodes.length !== 1) return false;
1301
+ const node = nodes[0];
1302
+ if (!$isDecoratorNode(node)) return false;
1303
+ const next = node.getNextSibling();
1304
+ if (next && $isElementNode(next)) {
1305
+ event?.preventDefault();
1306
+ next.selectStart();
1307
+ return true;
1308
+ }
1309
+ if (next && $isDecoratorNode(next)) {
1310
+ event?.preventDefault();
1311
+ selectDecoratorNode(next, "start");
1312
+ return true;
1313
+ }
1314
+ return false;
1315
+ },
1316
+ COMMAND_PRIORITY_CRITICAL
1317
+ );
1318
+ return () => {
1319
+ unregisterArrowDown();
1320
+ unregisterCmdEnter();
1321
+ unregisterBackspace();
1322
+ unregisterDelete();
1323
+ unregisterArrowUpDecorator();
1324
+ unregisterArrowDownDecorator();
1325
+ };
1326
+ }, [editor]);
1327
+ return null;
1328
+ }
874
1329
  function EditorRefPlugin({ onEditorReady }) {
875
1330
  const [editor] = useLexicalComposerContext();
876
1331
  const callbackRef = useRef(onEditorReady);
@@ -1561,6 +2016,34 @@ const GIT_ALERT_TRANSFORMER = {
1561
2016
  parentNode.replace(alertNode);
1562
2017
  }
1563
2018
  };
2019
+ function findCodeBlockKlass(nodes) {
2020
+ return nodes.find((n) => n.getType?.() === "code-block") || CodeBlockNode;
2021
+ }
2022
+ const CODE_BLOCK_MULTILINE_TRANSFORMER = {
2023
+ dependencies: [],
2024
+ regExpEnd: {
2025
+ optional: true,
2026
+ regExp: /[ \t]*```$/
2027
+ },
2028
+ regExpStart: /^[ \t]*```([\w-]+)?/,
2029
+ replace: (rootNode, _children, startMatch, _endMatch, linesInBetween) => {
2030
+ const lang = startMatch[1] || "";
2031
+ let code = "";
2032
+ if (linesInBetween) {
2033
+ const lines = [...linesInBetween];
2034
+ while (lines.length > 0 && !lines[0].length) lines.shift();
2035
+ while (lines.length > 0 && !lines.at(-1).length) lines.pop();
2036
+ code = lines.join("\n");
2037
+ }
2038
+ const Klass = findCodeBlockKlass(getResolvedEditNodes());
2039
+ const node = new Klass(code, lang);
2040
+ rootNode.replace(node);
2041
+ const selection = $createNodeSelection();
2042
+ selection.add(node.getKey());
2043
+ $setSelection(selection);
2044
+ },
2045
+ type: "multiline-element"
2046
+ };
1564
2047
  const BANNER_TYPE_MAP = {
1565
2048
  note: "note",
1566
2049
  info: "note",
@@ -1683,6 +2166,39 @@ const MENTION_TRANSFORMER = {
1683
2166
  textNode.replace(mentionNode);
1684
2167
  }
1685
2168
  };
2169
+ const QUOTE_TRANSFORMER = {
2170
+ dependencies: [QuoteNode],
2171
+ export: (node, exportChildren) => {
2172
+ if (!$isQuoteNode(node)) {
2173
+ return null;
2174
+ }
2175
+ const lines = exportChildren(node).split("\n");
2176
+ return lines.map((line) => `> ${line}`).join("\n");
2177
+ },
2178
+ regExp: /^>\s/,
2179
+ replace: (parentNode, children, _match, isImport) => {
2180
+ if (isImport) {
2181
+ const previousNode = parentNode.getPreviousSibling();
2182
+ if ($isQuoteNode(previousNode)) {
2183
+ previousNode.splice(previousNode.getChildrenSize(), 0, [
2184
+ $createLineBreakNode(),
2185
+ ...children
2186
+ ]);
2187
+ parentNode.remove();
2188
+ return;
2189
+ }
2190
+ }
2191
+ const node = $createQuoteNode();
2192
+ const paragraph = $createParagraphNode();
2193
+ paragraph.append(...children);
2194
+ node.append(paragraph);
2195
+ parentNode.replace(node);
2196
+ if (!isImport) {
2197
+ paragraph.select(0, 0);
2198
+ }
2199
+ },
2200
+ type: "element"
2201
+ };
1686
2202
  function quoteAttr(value) {
1687
2203
  return value.replaceAll('"', '\\"');
1688
2204
  }
@@ -1730,12 +2246,14 @@ const ALL_TRANSFORMERS = [
1730
2246
  IMAGE_BLOCK_TRANSFORMER,
1731
2247
  VIDEO_BLOCK_TRANSFORMER,
1732
2248
  CODE_BLOCK_NODE_TRANSFORMER,
2249
+ CODE_BLOCK_MULTILINE_TRANSFORMER,
1733
2250
  LINK_CARD_BLOCK_TRANSFORMER,
1734
2251
  MERMAID_BLOCK_TRANSFORMER,
1735
2252
  GRID_CONTAINER_BLOCK_TRANSFORMER,
1736
2253
  HORIZONTAL_RULE_BLOCK_TRANSFORMER,
1737
2254
  TABLE_BLOCK_TRANSFORMER,
1738
- ...TRANSFORMERS
2255
+ QUOTE_TRANSFORMER,
2256
+ ...TRANSFORMERS.filter((t) => t !== QUOTE && t !== CODE)
1739
2257
  ];
1740
2258
  function MarkdownShortcutsPlugin() {
1741
2259
  return /* @__PURE__ */ jsx(MarkdownShortcutPlugin, { transformers: ALL_TRANSFORMERS });
@@ -1914,6 +2432,7 @@ function RichEditor({
1914
2432
  /* @__PURE__ */ jsx(MermaidPlugin, {}),
1915
2433
  /* @__PURE__ */ jsx(HorizontalRulePlugin, {}),
1916
2434
  /* @__PURE__ */ jsx(CheckListPlugin, {}),
2435
+ /* @__PURE__ */ jsx(BlockExitPlugin, {}),
1917
2436
  /* @__PURE__ */ jsx(AutoLinkPlugin, {}),
1918
2437
  /* @__PURE__ */ jsx(EditorRefPlugin, { onEditorReady }),
1919
2438
  autoFocus && /* @__PURE__ */ jsx(AutoFocusPlugin, {}),
@@ -1 +1 @@
1
- {"version":3,"file":"RichEditor.d.ts","sourceRoot":"","sources":["../../src/components/RichEditor.tsx"],"names":[],"mappings":"AAiCA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAI/C,wBAAgB,UAAU,CAAC,EACzB,YAAY,EACZ,QAAQ,EACR,OAAmB,EACnB,KAAe,EACf,WAAkC,EAClC,QAAQ,EACR,SAAiB,EACjB,SAAS,EACT,gBAAgB,EAChB,KAAK,EACL,OAAO,EACP,aAAa,EACb,UAAU,EACV,cAAc,EACd,WAAW,EACX,UAAU,EACV,QAAQ,GACT,EAAE,eAAe,+BAwEjB"}
1
+ {"version":3,"file":"RichEditor.d.ts","sourceRoot":"","sources":["../../src/components/RichEditor.tsx"],"names":[],"mappings":"AAkCA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAI/C,wBAAgB,UAAU,CAAC,EACzB,YAAY,EACZ,QAAQ,EACR,OAAmB,EACnB,KAAe,EACf,WAAkC,EAClC,QAAQ,EACR,SAAiB,EACjB,SAAS,EACT,gBAAgB,EAChB,KAAK,EACL,OAAO,EACP,aAAa,EACb,UAAU,EACV,cAAc,EACd,WAAW,EACX,UAAU,EACV,QAAQ,GACT,EAAE,eAAe,+BAyEjB"}