@blue-patterns/richtext 0.0.0-stage → 0.7.18

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 (201) hide show
  1. package/CHANGELOG.md +275 -0
  2. package/LICENSE +22 -0
  3. package/README.md +88 -2
  4. package/astryx.integration.mjs +11 -0
  5. package/blocks/RichTextEditorShowcase.template.mjs +13 -0
  6. package/blocks/RichTextEditorShowcase.tsx +19 -0
  7. package/dist/RichTextEditor.d.ts +282 -0
  8. package/dist/RichTextEditor.d.ts.map +1 -0
  9. package/dist/RichTextEditor.js +657 -0
  10. package/dist/RichTextEditorAutoLinkPlugin.d.ts +83 -0
  11. package/dist/RichTextEditorAutoLinkPlugin.d.ts.map +1 -0
  12. package/dist/RichTextEditorAutoLinkPlugin.js +99 -0
  13. package/dist/RichTextEditorToolbar.d.ts +186 -0
  14. package/dist/RichTextEditorToolbar.d.ts.map +1 -0
  15. package/dist/RichTextEditorToolbar.js +688 -0
  16. package/dist/RichTextView.d.ts +92 -0
  17. package/dist/RichTextView.d.ts.map +1 -0
  18. package/dist/RichTextView.js +267 -0
  19. package/dist/codeLanguages.d.ts +14 -0
  20. package/dist/codeLanguages.d.ts.map +1 -0
  21. package/dist/codeLanguages.js +34 -0
  22. package/dist/codeLineTokens.d.ts +53 -0
  23. package/dist/codeLineTokens.d.ts.map +1 -0
  24. package/dist/codeLineTokens.js +188 -0
  25. package/dist/editorNodes.d.ts +8 -0
  26. package/dist/editorNodes.d.ts.map +1 -0
  27. package/dist/editorNodes.js +30 -0
  28. package/dist/editorTheme.d.ts +7 -0
  29. package/dist/editorTheme.d.ts.map +1 -0
  30. package/dist/editorTheme.js +328 -0
  31. package/dist/index.d.ts +31 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +28 -0
  34. package/dist/internal/CodeBackspacePlugin.d.ts +2 -0
  35. package/dist/internal/CodeBackspacePlugin.d.ts.map +1 -0
  36. package/dist/internal/CodeBackspacePlugin.js +70 -0
  37. package/dist/internal/CodeBlockHeaderPlugin.d.ts +11 -0
  38. package/dist/internal/CodeBlockHeaderPlugin.d.ts.map +1 -0
  39. package/dist/internal/CodeBlockHeaderPlugin.js +204 -0
  40. package/dist/internal/CodePastePlugin.d.ts +5 -0
  41. package/dist/internal/CodePastePlugin.d.ts.map +1 -0
  42. package/dist/internal/CodePastePlugin.js +104 -0
  43. package/dist/internal/CodeSyntaxPlugin.d.ts +8 -0
  44. package/dist/internal/CodeSyntaxPlugin.d.ts.map +1 -0
  45. package/dist/internal/CodeSyntaxPlugin.js +397 -0
  46. package/dist/internal/CodeTabPlugin.d.ts +2 -0
  47. package/dist/internal/CodeTabPlugin.d.ts.map +1 -0
  48. package/dist/internal/CodeTabPlugin.js +256 -0
  49. package/dist/internal/DisabledLinkGuardPlugin.d.ts +4 -0
  50. package/dist/internal/DisabledLinkGuardPlugin.d.ts.map +1 -0
  51. package/dist/internal/DisabledLinkGuardPlugin.js +93 -0
  52. package/dist/internal/ImageSourceCaretPlugin.d.ts +3 -0
  53. package/dist/internal/ImageSourceCaretPlugin.d.ts.map +1 -0
  54. package/dist/internal/ImageSourceCaretPlugin.js +105 -0
  55. package/dist/internal/ListItemShortcutsPlugin.d.ts +6 -0
  56. package/dist/internal/ListItemShortcutsPlugin.d.ts.map +1 -0
  57. package/dist/internal/ListItemShortcutsPlugin.js +314 -0
  58. package/dist/internal/MarkdownExtensionsPlugin.d.ts +17 -0
  59. package/dist/internal/MarkdownExtensionsPlugin.d.ts.map +1 -0
  60. package/dist/internal/MarkdownExtensionsPlugin.js +185 -0
  61. package/dist/internal/SelectAllScopePlugin.d.ts +2 -0
  62. package/dist/internal/SelectAllScopePlugin.d.ts.map +1 -0
  63. package/dist/internal/SelectAllScopePlugin.js +67 -0
  64. package/dist/internal/TableColumnFloorPlugin.d.ts +12 -0
  65. package/dist/internal/TableColumnFloorPlugin.d.ts.map +1 -0
  66. package/dist/internal/TableColumnFloorPlugin.js +84 -0
  67. package/dist/internal/TablePastePlugin.d.ts +9 -0
  68. package/dist/internal/TablePastePlugin.d.ts.map +1 -0
  69. package/dist/internal/TablePastePlugin.js +183 -0
  70. package/dist/internal/TableScrollRegionPlugin.d.ts +9 -0
  71. package/dist/internal/TableScrollRegionPlugin.d.ts.map +1 -0
  72. package/dist/internal/TableScrollRegionPlugin.js +95 -0
  73. package/dist/internal/TaskCheckboxPlugin.d.ts +7 -0
  74. package/dist/internal/TaskCheckboxPlugin.d.ts.map +1 -0
  75. package/dist/internal/TaskCheckboxPlugin.js +193 -0
  76. package/dist/internal/ToolbarLinkPopover.d.ts +74 -0
  77. package/dist/internal/ToolbarLinkPopover.d.ts.map +1 -0
  78. package/dist/internal/ToolbarLinkPopover.js +292 -0
  79. package/dist/linkEditing.d.ts +6 -0
  80. package/dist/linkEditing.d.ts.map +1 -0
  81. package/dist/linkEditing.js +20 -0
  82. package/dist/linkForm.d.ts +28 -0
  83. package/dist/linkForm.d.ts.map +1 -0
  84. package/dist/linkForm.js +137 -0
  85. package/dist/linkUtils.d.ts +27 -0
  86. package/dist/linkUtils.d.ts.map +1 -0
  87. package/dist/linkUtils.js +101 -0
  88. package/dist/markdown.d.ts +17 -0
  89. package/dist/markdown.d.ts.map +1 -0
  90. package/dist/markdown.js +17 -0
  91. package/dist/markdownCharacterReferences.d.ts +135 -0
  92. package/dist/markdownCharacterReferences.d.ts.map +1 -0
  93. package/dist/markdownCharacterReferences.js +1127 -0
  94. package/dist/markdownCodeFence.d.ts +4 -0
  95. package/dist/markdownCodeFence.d.ts.map +1 -0
  96. package/dist/markdownCodeFence.js +167 -0
  97. package/dist/markdownExtensionNode.d.ts +88 -0
  98. package/dist/markdownExtensionNode.d.ts.map +1 -0
  99. package/dist/markdownExtensionNode.js +248 -0
  100. package/dist/markdownExtensions.d.ts +115 -0
  101. package/dist/markdownExtensions.d.ts.map +1 -0
  102. package/dist/markdownExtensions.js +392 -0
  103. package/dist/markdownHardLineBreak.d.ts +25 -0
  104. package/dist/markdownHardLineBreak.d.ts.map +1 -0
  105. package/dist/markdownHardLineBreak.js +59 -0
  106. package/dist/markdownHeading.d.ts +20 -0
  107. package/dist/markdownHeading.d.ts.map +1 -0
  108. package/dist/markdownHeading.js +30 -0
  109. package/dist/markdownLink.d.ts +5 -0
  110. package/dist/markdownLink.d.ts.map +1 -0
  111. package/dist/markdownLink.js +80 -0
  112. package/dist/markdownListExport.d.ts +3 -0
  113. package/dist/markdownListExport.d.ts.map +1 -0
  114. package/dist/markdownListExport.js +68 -0
  115. package/dist/markdownListIndentation.d.ts +8 -0
  116. package/dist/markdownListIndentation.d.ts.map +1 -0
  117. package/dist/markdownListIndentation.js +101 -0
  118. package/dist/markdownOrderedList.d.ts +3 -0
  119. package/dist/markdownOrderedList.d.ts.map +1 -0
  120. package/dist/markdownOrderedList.js +51 -0
  121. package/dist/markdownQuote.d.ts +22 -0
  122. package/dist/markdownQuote.d.ts.map +1 -0
  123. package/dist/markdownQuote.js +32 -0
  124. package/dist/markdownSerializers.d.ts +49 -0
  125. package/dist/markdownSerializers.d.ts.map +1 -0
  126. package/dist/markdownSerializers.js +83 -0
  127. package/dist/markdownSource.d.ts +71 -0
  128. package/dist/markdownSource.d.ts.map +1 -0
  129. package/dist/markdownSource.js +804 -0
  130. package/dist/markdownSpaceRuns.d.ts +24 -0
  131. package/dist/markdownSpaceRuns.d.ts.map +1 -0
  132. package/dist/markdownSpaceRuns.js +147 -0
  133. package/dist/markdownTable.d.ts +35 -0
  134. package/dist/markdownTable.d.ts.map +1 -0
  135. package/dist/markdownTable.js +227 -0
  136. package/dist/markdownTaskList.d.ts +48 -0
  137. package/dist/markdownTaskList.d.ts.map +1 -0
  138. package/dist/markdownTaskList.js +166 -0
  139. package/dist/markdownThematicBreak.d.ts +3 -0
  140. package/dist/markdownThematicBreak.d.ts.map +1 -0
  141. package/dist/markdownThematicBreak.js +55 -0
  142. package/dist/privateAttributes/layerPortalBoundary.stylex.d.ts +12 -0
  143. package/dist/privateAttributes/layerPortalBoundary.stylex.d.ts.map +1 -0
  144. package/dist/privateAttributes/layerPortalBoundary.stylex.js +16 -0
  145. package/dist/richtext.css +213 -0
  146. package/dist/textSemantics.d.ts +3 -0
  147. package/dist/textSemantics.d.ts.map +1 -0
  148. package/dist/textSemantics.js +87 -0
  149. package/package.json +158 -3
  150. package/src/RichTextEditor.doc.mjs +308 -0
  151. package/src/RichTextEditor.tsx +1109 -0
  152. package/src/RichTextEditorAutoLinkPlugin.doc.mjs +43 -0
  153. package/src/RichTextEditorAutoLinkPlugin.tsx +126 -0
  154. package/src/RichTextEditorToolbar.doc.mjs +92 -0
  155. package/src/RichTextEditorToolbar.tsx +871 -0
  156. package/src/RichTextView.doc.mjs +101 -0
  157. package/src/RichTextView.tsx +351 -0
  158. package/src/codeLanguages.ts +42 -0
  159. package/src/codeLineTokens.ts +241 -0
  160. package/src/doc-mjs.d.ts +23 -0
  161. package/src/editorNodes.ts +44 -0
  162. package/src/editorTheme.ts +550 -0
  163. package/src/index.ts +59 -0
  164. package/src/internal/CodeBackspacePlugin.tsx +96 -0
  165. package/src/internal/CodeBlockHeaderPlugin.tsx +282 -0
  166. package/src/internal/CodePastePlugin.tsx +144 -0
  167. package/src/internal/CodeSyntaxPlugin.tsx +493 -0
  168. package/src/internal/CodeTabPlugin.tsx +298 -0
  169. package/src/internal/DisabledLinkGuardPlugin.tsx +93 -0
  170. package/src/internal/ImageSourceCaretPlugin.tsx +159 -0
  171. package/src/internal/ListItemShortcutsPlugin.tsx +404 -0
  172. package/src/internal/MarkdownExtensionsPlugin.tsx +289 -0
  173. package/src/internal/SelectAllScopePlugin.tsx +86 -0
  174. package/src/internal/TableColumnFloorPlugin.tsx +99 -0
  175. package/src/internal/TablePastePlugin.tsx +223 -0
  176. package/src/internal/TableScrollRegionPlugin.tsx +107 -0
  177. package/src/internal/TaskCheckboxPlugin.tsx +246 -0
  178. package/src/internal/ToolbarLinkPopover.tsx +392 -0
  179. package/src/linkEditing.ts +20 -0
  180. package/src/linkForm.tsx +156 -0
  181. package/src/linkUtils.ts +106 -0
  182. package/src/markdown.ts +25 -0
  183. package/src/markdownCharacterReferences.ts +1137 -0
  184. package/src/markdownCodeFence.ts +216 -0
  185. package/src/markdownExtensionNode.ts +339 -0
  186. package/src/markdownExtensions.ts +496 -0
  187. package/src/markdownHardLineBreak.ts +73 -0
  188. package/src/markdownHeading.ts +33 -0
  189. package/src/markdownLink.ts +86 -0
  190. package/src/markdownListExport.ts +85 -0
  191. package/src/markdownListIndentation.ts +105 -0
  192. package/src/markdownOrderedList.ts +60 -0
  193. package/src/markdownQuote.ts +35 -0
  194. package/src/markdownSerializers.ts +108 -0
  195. package/src/markdownSource.ts +1045 -0
  196. package/src/markdownSpaceRuns.ts +164 -0
  197. package/src/markdownTable.ts +309 -0
  198. package/src/markdownTaskList.ts +222 -0
  199. package/src/markdownThematicBreak.ts +66 -0
  200. package/src/privateAttributes/layerPortalBoundary.stylex.ts +18 -0
  201. package/src/textSemantics.ts +104 -0
@@ -0,0 +1,68 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file markdownListExport.ts
5
+ * @input Uses @lexical/list (ListNode, ListItemNode) and @lexical/markdown
6
+ * (ElementTransformer).
7
+ * @output Exports LIST_EXPORT, which writes lists the way Lexical's list
8
+ * transformers do except for nesting: a nested list is indented to its
9
+ * parent item's content column instead of four spaces per level, and each
10
+ * task item writes `[ ]` or `[x]` (markdownTaskList.ts).
11
+ * @position Part of DEFAULT_TRANSFORMERS (markdownTable.ts), ahead of
12
+ * Lexical's list transformers, which still import. Import reads nesting the
13
+ * CommonMark way (markdownListIndentation.ts), so a child written four
14
+ * spaces under `100. parent` (content column five) would read back at the
15
+ * parent's level; indenting to the content column keeps an edited list's
16
+ * structure when it is read again (spec:AST-062 FR3).
17
+ */
18
+
19
+ import { $isListItemNode, $isListNode, ListItemNode, ListNode } from '@lexical/list';
20
+ import { $getTaskState } from "./markdownTaskList.js";
21
+
22
+ /** The bullet Lexical recorded for a list on import (`-`, `*`, or `+`). */
23
+ function bulletOf(list) {
24
+ // The export may hand over a view of the list (an object inheriting from
25
+ // the node, see markdownSource.ts); Lexical validates the fields of the
26
+ // node it serializes, so read the recorded state from the node itself.
27
+ const serialized = list.getLatest().exportJSON();
28
+ const marker = serialized.$?.mdListMarker;
29
+ return marker === '*' || marker === '+' ? marker : '-';
30
+ }
31
+ function exportList(list, exportChildren, indent) {
32
+ const lines = [];
33
+ const type = list.getListType();
34
+ let index = 0;
35
+ // Where a nested list goes: the content column of the item before it.
36
+ let childIndent = `${indent} `;
37
+ for (const item of list.getChildren()) {
38
+ if (!$isListItemNode(item)) {
39
+ continue;
40
+ }
41
+ const first = item.getFirstChild();
42
+ if (item.getChildrenSize() === 1 && $isListNode(first)) {
43
+ lines.push(exportList(first, exportChildren, childIndent));
44
+ continue;
45
+ }
46
+ const marker = type === 'number' ? `${list.getStart() + index}. ` : `${bulletOf(list)} `;
47
+ // A task item, in any bulleted list, writes its box.
48
+ const task = type === 'number' ? null : $getTaskState(item);
49
+ const checkbox = task == null ? '' : `[${task === 'checked' ? 'x' : ' '}] `;
50
+ let text = exportChildren(item);
51
+ if (type !== 'number') {
52
+ // As Lexical does: keep `1. ` at the start of a bullet item literal.
53
+ text = text.replace(/^(\s{0,3}\d+)(\.\s)/, '$1\\$2');
54
+ }
55
+ lines.push(indent + marker + checkbox + text);
56
+ childIndent = indent + ' '.repeat(marker.length);
57
+ index++;
58
+ }
59
+ return lines.join('\n');
60
+ }
61
+ export const LIST_EXPORT = {
62
+ dependencies: [ListNode, ListItemNode],
63
+ export: (node, exportChildren) => $isListNode(node) ? exportList(node, exportChildren, '') : null,
64
+ // Export only: Lexical's list transformers import.
65
+ regExp: /(?!)/,
66
+ replace: () => false,
67
+ type: 'element'
68
+ };
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Rewrites each list item line's indentation as four spaces per CommonMark
3
+ * nesting level, leaving every other line, and every line inside fenced code,
4
+ * as it is. The line count and every line's content after its indentation
5
+ * are unchanged.
6
+ */
7
+ export declare function normalizeListIndentation(markdown: string): string;
8
+ //# sourceMappingURL=markdownListIndentation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdownListIndentation.d.ts","sourceRoot":"","sources":["../src/markdownListIndentation.ts"],"names":[],"mappings":"AA+BA;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAmEjE"}
@@ -0,0 +1,101 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file markdownListIndentation.ts
5
+ * @input Markdown source text.
6
+ * @output Exports normalizeListIndentation, which rewrites the indentation of
7
+ * every list item line so its nesting reads the same to Lexical as it does
8
+ * to CommonMark.
9
+ * @position Used by markdownSource.ts on the text it imports (never on the
10
+ * bytes it records). Lexical nests a list item one level per four spaces or
11
+ * one tab; CommonMark nests an item under the item whose content it is
12
+ * indented to, so `- a` / ` - b` (two spaces) and `1. a` / ` 1. b`
13
+ * (three) are nested lists that Lexical would flatten (spec:AST-061 FR5).
14
+ */
15
+
16
+ /** A list item line: indentation, marker, and the spaces after it. */
17
+ const LIST_ITEM = /^([ \t]*)([-+*]|\d{1,9}[.)])( {1,4}|\t|$)/;
18
+ const FENCE_OPEN = /^[ \t]*(`{3,}|~{3,})/;
19
+ const BLANK_LINE = /^[ \t]*\r?$/;
20
+ /** A thematic break after its indentation: three or more of one marker. */
21
+ const THEMATIC_BREAK_CONTENT = /^([-*_])(?:[ \t]*\1){2,}[ \t]*\r?$/;
22
+
23
+ /** Columns of leading whitespace, with tabs stopping every four columns. */
24
+ function columnsOf(whitespace) {
25
+ let columns = 0;
26
+ for (const character of whitespace) {
27
+ columns = character === '\t' ? columns + 4 - columns % 4 : columns + 1;
28
+ }
29
+ return columns;
30
+ }
31
+
32
+ /**
33
+ * Rewrites each list item line's indentation as four spaces per CommonMark
34
+ * nesting level, leaving every other line, and every line inside fenced code,
35
+ * as it is. The line count and every line's content after its indentation
36
+ * are unchanged.
37
+ */
38
+ export function normalizeListIndentation(markdown) {
39
+ const lines = markdown.split('\n');
40
+ // The content column of each open list item, outermost first.
41
+ const open = [];
42
+ let fence = null;
43
+ let afterBlankLine = false;
44
+ for (let index = 0; index < lines.length; index++) {
45
+ const line = lines[index];
46
+ if (fence != null) {
47
+ if (new RegExp(`^[ \\t]*${fence[0]}{${fence.length},}[ \\t]*\\r?$`).test(line)) {
48
+ fence = null;
49
+ }
50
+ continue;
51
+ }
52
+ if (BLANK_LINE.test(line)) {
53
+ afterBlankLine = true;
54
+ continue;
55
+ }
56
+ const leading = /^[ \t]*/.exec(line)?.[0] ?? '';
57
+ const indent = columnsOf(leading);
58
+ if (THEMATIC_BREAK_CONTENT.test(line.slice(leading.length))) {
59
+ // A thematic break, never an item, even where an item could start
60
+ // (CommonMark 0.31 §4.1). Lexical shows it only as a block of its own,
61
+ // so a break inside an item loses its indentation and reads as one.
62
+ while (open.length > 0 && indent < open[open.length - 1]) {
63
+ open.pop();
64
+ }
65
+ if (open.length > 0) {
66
+ lines[index] = line.slice(leading.length);
67
+ }
68
+ afterBlankLine = false;
69
+ continue;
70
+ }
71
+ const item = LIST_ITEM.exec(line);
72
+ if (item == null) {
73
+ const opening = FENCE_OPEN.exec(line);
74
+ if (opening != null) {
75
+ fence = opening[1];
76
+ }
77
+ // A line after a blank line belongs to the items it is indented into;
78
+ // without a blank line it continues the last item's paragraph lazily.
79
+ if (afterBlankLine) {
80
+ while (open.length > 0 && indent < open[open.length - 1]) {
81
+ open.pop();
82
+ }
83
+ }
84
+ afterBlankLine = false;
85
+ continue;
86
+ }
87
+ afterBlankLine = false;
88
+ // An item nests under every open item whose content it reaches.
89
+ while (open.length > 0 && indent < open[open.length - 1]) {
90
+ open.pop();
91
+ }
92
+ const depth = open.length;
93
+ // The content column, counted across the whole prefix so a tab after
94
+ // the marker stops at the right column; a marker that ends the line has
95
+ // its content one column after it.
96
+ const markerEnd = columnsOf(item[0]) + (item[3] === '' ? 1 : 0);
97
+ open.push(markerEnd);
98
+ lines[index] = ' '.repeat(depth * 4) + line.slice(item[1].length);
99
+ }
100
+ return lines.join('\n');
101
+ }
@@ -0,0 +1,3 @@
1
+ import { type ElementTransformer } from '@lexical/markdown';
2
+ export declare const ORDERED_LIST_KEEPING_START: ElementTransformer;
3
+ //# sourceMappingURL=markdownOrderedList.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdownOrderedList.d.ts","sourceRoot":"","sources":["../src/markdownOrderedList.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAe,KAAK,kBAAkB,EAAC,MAAM,mBAAmB,CAAC;AAsBxE,eAAO,MAAM,0BAA0B,EAAE,kBAkBxC,CAAC"}
@@ -0,0 +1,51 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file markdownOrderedList.ts
5
+ * @input Uses @lexical/list (ListNode, ListItemNode) and @lexical/markdown
6
+ * (ORDERED_LIST).
7
+ * @output Exports ORDERED_LIST_KEEPING_START, Lexical's ordered list
8
+ * transformer with one change: a nested numbered list starts at the number
9
+ * its first item was written with.
10
+ * @position Replaces Lexical's ORDERED_LIST in DEFAULT_TRANSFORMERS
11
+ * (markdownTable.ts). Lexical nests an indented item under a numbered list
12
+ * by indenting it into a new list that starts at 1, so `27.` written as the
13
+ * first item of a nested list read as `1.` (or `a.` under spec:AST-061
14
+ * DEC-6). Markdown keeps every list's start, nested or not (DEC-6), so this
15
+ * sets the new nested list's start from the marker. A later item in the same
16
+ * nested list continues its sequence, as Lexical's does.
17
+ */
18
+
19
+ import { $isListItemNode, $isListNode } from '@lexical/list';
20
+ import { ORDERED_LIST } from '@lexical/markdown';
21
+
22
+ /**
23
+ * The list `item` was indented into: the last item under `list`, followed
24
+ * down through the wrapper items Lexical nests lists in.
25
+ */
26
+ function nestedListOfLastItem(list) {
27
+ let current = list;
28
+ for (;;) {
29
+ const last = current.getLastChild();
30
+ const nested = $isListItemNode(last) ? last.getFirstChild() : null;
31
+ if (!$isListItemNode(last) || last.getChildrenSize() !== 1 || !$isListNode(nested)) {
32
+ return current;
33
+ }
34
+ current = nested;
35
+ }
36
+ }
37
+ export const ORDERED_LIST_KEEPING_START = {
38
+ ...ORDERED_LIST,
39
+ replace: (parentNode, children, match, isImport) => {
40
+ const previous = parentNode.getPreviousSibling();
41
+ const result = ORDERED_LIST.replace(parentNode, children, match, isImport);
42
+ if ($isListNode(previous) && previous.getListType() === 'number') {
43
+ const nested = nestedListOfLastItem(previous);
44
+ // The new item opened this nested list: start it at its own number.
45
+ if (nested !== previous && nested.getListType() === 'number' && nested.getChildrenSize() === 1) {
46
+ nested.setStart(Number(match[2]));
47
+ }
48
+ }
49
+ return result;
50
+ }
51
+ };
@@ -0,0 +1,22 @@
1
+ /**
2
+ * @file markdownQuote.ts
3
+ * @input Uses @lexical/markdown (Lexical's QUOTE element transformer).
4
+ * @output Exports QUOTE_MARKERS, Lexical's quote transformer reading a block
5
+ * quote marker as CommonMark does.
6
+ * @position Part of DEFAULT_TRANSFORMERS (markdownTable.ts) in place of
7
+ * Lexical's QUOTE, whose pattern needs a space after `>` and no indentation.
8
+ * Matches core Markdown's block quote marker (CommonMark 0.31 §5.1). Typing
9
+ * shortcuts keep Lexical's own trigger: `> ` makes a quote. Text that starts
10
+ * like a marker is escaped on export by markdownSource.ts.
11
+ */
12
+ import { type ElementTransformer } from '@lexical/markdown';
13
+ /**
14
+ * A block quote marker: up to three spaces of indentation, then `>` and the
15
+ * one space or tab that may follow it, so `>a` and ` > a` import as quotes,
16
+ * as in core Markdown. A typed line becomes a quote only at Lexical's own
17
+ * `> `, so typing `>` and Enter, or ` > `, changes nothing new. RichText
18
+ * quotes do not nest: the marker of a quote inside a quote stays in the
19
+ * outer quote's text.
20
+ */
21
+ export declare const QUOTE_MARKERS: ElementTransformer;
22
+ //# sourceMappingURL=markdownQuote.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdownQuote.d.ts","sourceRoot":"","sources":["../src/markdownQuote.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AAEH,OAAO,EAAQ,KAAK,kBAAkB,EAAC,MAAM,mBAAmB,CAAC;AAKjE;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,EAAE,kBAO3B,CAAC"}
@@ -0,0 +1,32 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file markdownQuote.ts
5
+ * @input Uses @lexical/markdown (Lexical's QUOTE element transformer).
6
+ * @output Exports QUOTE_MARKERS, Lexical's quote transformer reading a block
7
+ * quote marker as CommonMark does.
8
+ * @position Part of DEFAULT_TRANSFORMERS (markdownTable.ts) in place of
9
+ * Lexical's QUOTE, whose pattern needs a space after `>` and no indentation.
10
+ * Matches core Markdown's block quote marker (CommonMark 0.31 §5.1). Typing
11
+ * shortcuts keep Lexical's own trigger: `> ` makes a quote. Text that starts
12
+ * like a marker is escaped on export by markdownSource.ts.
13
+ */
14
+
15
+ import { QUOTE } from '@lexical/markdown';
16
+
17
+ /** Lexical's own quote marker, which the typing shortcut keeps using. */
18
+ const SHORTCUT_MARKER = /^>\s/;
19
+
20
+ /**
21
+ * A block quote marker: up to three spaces of indentation, then `>` and the
22
+ * one space or tab that may follow it, so `>a` and ` > a` import as quotes,
23
+ * as in core Markdown. A typed line becomes a quote only at Lexical's own
24
+ * `> `, so typing `>` and Enter, or ` > `, changes nothing new. RichText
25
+ * quotes do not nest: the marker of a quote inside a quote stays in the
26
+ * outer quote's text.
27
+ */
28
+ export const QUOTE_MARKERS = {
29
+ ...QUOTE,
30
+ regExp: /^ {0,3}>[ \t]?/,
31
+ replace: (parentNode, children, match, isImport) => isImport || SHORTCUT_MARKER.test(match[0]) ? QUOTE.replace(parentNode, children, match, isImport) : false
32
+ };
@@ -0,0 +1,49 @@
1
+ import { type Transformer } from '@lexical/markdown';
2
+ import { type RichTextMarkdownExtension } from './markdownExtensions.js';
3
+ import type { Klass, LexicalNode } from 'lexical';
4
+ /** Options shared by the Markdown serializer helpers. */
5
+ export interface MarkdownSerializerOptions {
6
+ /**
7
+ * Markdown transformers to use. Defaults to the editor's default set: the
8
+ * standard `@lexical/markdown` `TRANSFORMERS` plus GFM tables. Pass the same
9
+ * array you give `RichTextEditor`'s `transformers` prop so content
10
+ * round-trips consistently.
11
+ */
12
+ transformers?: ReadonlyArray<Transformer>;
13
+ /**
14
+ * Extra Lexical nodes to register beyond the default OSS set, mirroring the
15
+ * editor's `nodes` prop. Required for custom node types to serialize.
16
+ */
17
+ nodes?: ReadonlyArray<Klass<LexicalNode>>;
18
+ /**
19
+ * Markdown plugins to recognize, each adopted with `createRichTextExtension`
20
+ * (spec:AST-064). Their syntax becomes extension nodes holding its exact
21
+ * source; without them, plugin syntax stays text. Pass the same extensions
22
+ * the surfaces that show the document use.
23
+ */
24
+ extensions?: ReadonlyArray<RichTextMarkdownExtension>;
25
+ }
26
+ /**
27
+ * Convert a Markdown string to a serialized Lexical `EditorState` JSON string
28
+ * — the same shape `RichTextEditor`'s `defaultValue` expects. Runs headless
29
+ * (no DOM), so it is safe in Node / SSR.
30
+ *
31
+ * @example
32
+ * ```
33
+ * const value = markdownToEditorStateJSON('# Title\n\nHello');
34
+ * <RichTextEditor label="Notes" defaultValue={value} />
35
+ * ```
36
+ */
37
+ export declare function markdownToEditorStateJSON(markdown: string, options?: MarkdownSerializerOptions): string;
38
+ /**
39
+ * Convert a serialized Lexical `EditorState` JSON string (e.g. the value
40
+ * produced by `RichTextEditor`'s `onChange` or `defaultValue`) to a Markdown
41
+ * string. Runs headless (no DOM), so it is safe in Node / SSR.
42
+ *
43
+ * @example
44
+ * ```
45
+ * const md = editorStateJSONToMarkdown(savedJson);
46
+ * ```
47
+ */
48
+ export declare function editorStateJSONToMarkdown(editorStateJSON: string, options?: MarkdownSerializerOptions): string;
49
+ //# sourceMappingURL=markdownSerializers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdownSerializers.d.ts","sourceRoot":"","sources":["../src/markdownSerializers.ts"],"names":[],"mappings":"AAwBA,OAAO,EAAC,KAAK,WAAW,EAAC,MAAM,mBAAmB,CAAC;AAOnD,OAAO,EAAY,KAAK,yBAAyB,EAAC,MAAM,sBAAsB,CAAC;AAC/E,OAAO,KAAK,EAAC,KAAK,EAAE,WAAW,EAAC,MAAM,SAAS,CAAC;AAEhD,yDAAyD;AACzD,MAAM,WAAW,yBAAyB;IACxC;;;;;OAKG;IACH,YAAY,CAAC,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC;IAC1C;;;OAGG;IACH,KAAK,CAAC,EAAE,aAAa,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC;IAC1C;;;;;OAKG;IACH,UAAU,CAAC,EAAE,aAAa,CAAC,yBAAyB,CAAC,CAAC;CACvD;AAYD;;;;;;;;;;GAUG;AACH,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,yBAA8B,GACtC,MAAM,CAMR;AAED;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CACvC,eAAe,EAAE,MAAM,EACvB,OAAO,GAAE,yBAA8B,GACtC,MAAM,CAKR"}
@@ -0,0 +1,83 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file markdownSerializers.ts
5
+ * @input Uses @lexical/headless (createHeadlessEditor), @lexical/markdown
6
+ * (transformer types), the shared DEFAULT_NODES set, DEFAULT_TRANSFORMERS,
7
+ * and the source-preserving import and export in markdownSource.ts.
8
+ * @output Standalone Markdown <-> serialized EditorState helpers:
9
+ * markdownToEditorStateJSON, editorStateJSONToMarkdown.
10
+ * @position Exported from the server-safe `@blue-patterns/richtext/markdown`
11
+ * entry and re-exported from the @blue-patterns/richtext barrel. Complements the ref's getMarkdown() by working WITHOUT a mounted
12
+ * editor (e.g. to produce a `defaultValue` from Markdown on the server).
13
+ *
14
+ * SYNC: When modified, update:
15
+ * - /packages/richtext/src/index.ts (exports)
16
+ * - /packages/richtext/src/RichTextEditor.doc.mjs (usage notes)
17
+ * - /packages/richtext/src/RichTextEditor.test.tsx (tests)
18
+ *
19
+ * NOTE: `@lexical/headless`, `@lexical/markdown`, and `lexical` are OPTIONAL
20
+ * peer dependencies. Install them to use these helpers. `@lexical/headless`
21
+ * runs Lexical without a DOM, so these work in Node / SSR contexts.
22
+ */
23
+
24
+ import { createHeadlessEditor } from '@lexical/headless';
25
+ import { DEFAULT_NODES } from "./editorNodes.js";
26
+ import { $exportMarkdownKeepingSource, importMarkdownKeepingSource } from "./markdownSource.js";
27
+ import { DEFAULT_TRANSFORMERS } from "./markdownTable.js";
28
+ import { pluginsOf } from "./markdownExtensions.js";
29
+
30
+ /** Options shared by the Markdown serializer helpers. */
31
+
32
+ function createSerializerEditor(nodes) {
33
+ return createHeadlessEditor({
34
+ namespace: 'astryx-editor-serializer',
35
+ nodes: nodes ? [...DEFAULT_NODES, ...nodes] : [...DEFAULT_NODES],
36
+ onError(error) {
37
+ throw error;
38
+ }
39
+ });
40
+ }
41
+
42
+ /**
43
+ * Convert a Markdown string to a serialized Lexical `EditorState` JSON string
44
+ * — the same shape `RichTextEditor`'s `defaultValue` expects. Runs headless
45
+ * (no DOM), so it is safe in Node / SSR.
46
+ *
47
+ * @example
48
+ * ```
49
+ * const value = markdownToEditorStateJSON('# Title\n\nHello');
50
+ * <RichTextEditor label="Notes" defaultValue={value} />
51
+ * ```
52
+ */
53
+ export function markdownToEditorStateJSON(markdown, options = {}) {
54
+ const {
55
+ transformers = DEFAULT_TRANSFORMERS,
56
+ nodes,
57
+ extensions = []
58
+ } = options;
59
+ const plugins = pluginsOf(extensions, transformers);
60
+ const editor = createSerializerEditor(nodes);
61
+ importMarkdownKeepingSource(editor, markdown, [...transformers], plugins);
62
+ return JSON.stringify(editor.getEditorState().toJSON());
63
+ }
64
+
65
+ /**
66
+ * Convert a serialized Lexical `EditorState` JSON string (e.g. the value
67
+ * produced by `RichTextEditor`'s `onChange` or `defaultValue`) to a Markdown
68
+ * string. Runs headless (no DOM), so it is safe in Node / SSR.
69
+ *
70
+ * @example
71
+ * ```
72
+ * const md = editorStateJSONToMarkdown(savedJson);
73
+ * ```
74
+ */
75
+ export function editorStateJSONToMarkdown(editorStateJSON, options = {}) {
76
+ const {
77
+ transformers = DEFAULT_TRANSFORMERS,
78
+ nodes
79
+ } = options;
80
+ const editor = createSerializerEditor(nodes);
81
+ const state = editor.parseEditorState(editorStateJSON);
82
+ return state.read(() => $exportMarkdownKeepingSource([...transformers]));
83
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * @file markdownSource.ts
3
+ * @input Uses lexical (node state, root, node classes), @lexical/markdown
4
+ * ($convertFromMarkdownString / $convertToMarkdownString), @lexical/code,
5
+ * and the plugin node shielding and export in markdownExtensions.ts.
6
+ * @output Exports importMarkdownKeepingSource and $exportMarkdownKeepingSource,
7
+ * the source-preserving Markdown import and export behind
8
+ * markdownToEditorStateJSON, editorStateJSONToMarkdown, and getMarkdown().
9
+ * @position Internal to @blue-patterns/richtext. Implements spec:AST-062: an
10
+ * untouched top-level block exports exactly as authored, and an edit
11
+ * regenerates only the blocks it changed.
12
+ *
13
+ * Import splits the source into chunks (runs of non-blank lines; a fenced code
14
+ * block is one chunk even across blank lines) and imports each chunk on its
15
+ * own. Once node transforms settle the tree, the top-level nodes from one or
16
+ * more consecutive chunks form a group, and the group's first node records the
17
+ * exact bytes it came from and its canonical Markdown at import. Facts about
18
+ * the whole document — its byte order mark, the bytes before the first block
19
+ * and after the last, its line ending — live on the root, so moving, copying,
20
+ * or deleting blocks never moves or duplicates them.
21
+ *
22
+ * Export reads the tree without changing it. Each group's canonical Markdown
23
+ * is regenerated (and cached per node, so an unchanged tree costs a lookup per
24
+ * group); when it still matches the recorded one, the recorded bytes are
25
+ * written instead.
26
+ */
27
+ import { type Transformer } from '@lexical/markdown';
28
+ import { type ElementNode, type LexicalEditor } from 'lexical';
29
+ import type { MarkdownPluginEntry } from '@blue-patterns/core/Markdown/plugins';
30
+ /** The whitespace and content one chunk of Markdown source was split into. */
31
+ export interface MarkdownChunk {
32
+ /** Blank lines before the chunk; only the first chunk has any. */
33
+ readonly leading: string;
34
+ /** The chunk's lines, without the line ending that ends the last one. */
35
+ readonly content: string;
36
+ /** Everything after the content up to the next chunk, or to the end. */
37
+ readonly trailing: string;
38
+ }
39
+ export declare function splitMarkdownChunks(markdown: string): Array<MarkdownChunk>;
40
+ /**
41
+ * Imports Markdown into the editor's root and records its authored bytes. Runs
42
+ * two discrete updates: the first imports every chunk and stamps its nodes
43
+ * with the chunk's group id; the second runs after node transforms have
44
+ * settled the tree (adjacent lists merge, for example), so each group covers
45
+ * exactly the chunks that ended up in its nodes.
46
+ */
47
+ export declare function importMarkdownKeepingSource(editor: LexicalEditor, markdown: string, transformers: Array<Transformer>, plugins?: ReadonlyArray<MarkdownPluginEntry>): void;
48
+ /**
49
+ * Joins the lines of a paragraph, list item, or block quote across soft line
50
+ * breaks: a line ending without two trailing spaces or a backslash continues
51
+ * the block, as one space (CommonMark, soft line breaks; spec:AST-061 FR5).
52
+ * Lexical imports every line ending as a line break and marks the hard ones,
53
+ * so the unmarked ones outside code are the soft breaks.
54
+ */
55
+ export declare function $joinSoftLineBreaks(element: ElementNode): void;
56
+ /**
57
+ * Moves a list that begins nested right after another list into that list's
58
+ * last item. Lexical starts a new top-level list for an item of a different
59
+ * type (a bullet under `1. item`) and nests it there, while CommonMark nests
60
+ * it inside the item above; without this, writing it back would lose the
61
+ * nesting (spec:AST-061 FR5, spec:AST-062 FR3).
62
+ */
63
+ export declare function $nestFollowingLists(root: ElementNode): void;
64
+ /**
65
+ * A token that appears nowhere in `text`. There are 137,468 private-use code
66
+ * points, so a text shorter than that always misses one of them; a longer
67
+ * text cannot contain every one of the 137,468² ordered pairs.
68
+ */
69
+ export declare function absentToken(text: string): string;
70
+ export declare function $exportMarkdownKeepingSource(transformers: ReadonlyArray<Transformer>): string;
71
+ //# sourceMappingURL=markdownSource.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdownSource.d.ts","sourceRoot":"","sources":["../src/markdownSource.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAA6B,KAAK,WAAW,EAAC,MAAM,mBAAmB,CAAC;AAI/E,OAAO,EAUL,KAAK,WAAW,EAChB,KAAK,aAAa,EAGnB,MAAM,SAAS,CAAC;AAyBjB,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,sCAAsC,CAAC;AAE9E,8EAA8E;AAC9E,MAAM,WAAW,aAAa;IAC5B,kEAAkE;IAClE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,yEAAyE;IACzE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,wEAAwE;IACxE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AA0JD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,KAAK,CAAC,aAAa,CAAC,CA4E1E;AA8CD;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,aAAa,EACrB,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,KAAK,CAAC,WAAW,CAAC,EAChC,OAAO,GAAE,aAAa,CAAC,mBAAmB,CAAM,GAC/C,IAAI,CAgGN;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,CA6B9D;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,CAoB3D;AAsLD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAqBhD;AAmOD,wBAAgB,4BAA4B,CAC1C,YAAY,EAAE,aAAa,CAAC,WAAW,CAAC,GACvC,MAAM,CA0ER"}