@liminis/editor 0.1.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (219) hide show
  1. package/LICENSE +34 -0
  2. package/README.md +376 -0
  3. package/dist/annotations/anchor-align.d.ts +50 -0
  4. package/dist/annotations/anchor-align.js +74 -0
  5. package/dist/annotations/anchor-model.d.ts +81 -0
  6. package/dist/annotations/anchor-model.js +80 -0
  7. package/dist/annotations/anchor-resolver.d.ts +92 -0
  8. package/dist/annotations/anchor-resolver.js +231 -0
  9. package/dist/annotations/block-structure.d.ts +65 -0
  10. package/dist/annotations/block-structure.js +113 -0
  11. package/dist/annotations/types.d.ts +159 -0
  12. package/dist/annotations/types.js +50 -0
  13. package/dist/annotations.d.ts +25 -0
  14. package/dist/annotations.js +27 -0
  15. package/dist/app/App.d.ts +64 -0
  16. package/dist/app/App.js +180 -0
  17. package/dist/app/editor/AmbientCorrectionPlugin.d.ts +18 -0
  18. package/dist/app/editor/AmbientCorrectionPlugin.js +185 -0
  19. package/dist/app/editor/AnchorScrollPlugin.d.ts +16 -0
  20. package/dist/app/editor/AnchorScrollPlugin.js +134 -0
  21. package/dist/app/editor/AnnotationMarkerPlugin.d.ts +24 -0
  22. package/dist/app/editor/AnnotationMarkerPlugin.js +224 -0
  23. package/dist/app/editor/AnnotationPlugin.d.ts +42 -0
  24. package/dist/app/editor/AnnotationPlugin.js +135 -0
  25. package/dist/app/editor/AnnotationSurface.d.ts +34 -0
  26. package/dist/app/editor/AnnotationSurface.js +115 -0
  27. package/dist/app/editor/AssetContext.d.ts +20 -0
  28. package/dist/app/editor/AssetContext.js +81 -0
  29. package/dist/app/editor/BlockClickPlugin.d.ts +8 -0
  30. package/dist/app/editor/BlockClickPlugin.js +126 -0
  31. package/dist/app/editor/CodeBlockPlugin.d.ts +1 -0
  32. package/dist/app/editor/CodeBlockPlugin.js +219 -0
  33. package/dist/app/editor/CorrectionPanelPlugin.d.ts +14 -0
  34. package/dist/app/editor/CorrectionPanelPlugin.js +333 -0
  35. package/dist/app/editor/DragHandlePlugin.d.ts +1 -0
  36. package/dist/app/editor/DragHandlePlugin.js +300 -0
  37. package/dist/app/editor/Editor.d.ts +84 -0
  38. package/dist/app/editor/Editor.js +539 -0
  39. package/dist/app/editor/FrontmatterPlugin.d.ts +19 -0
  40. package/dist/app/editor/FrontmatterPlugin.js +301 -0
  41. package/dist/app/editor/ImageModal.d.ts +13 -0
  42. package/dist/app/editor/ImageModal.js +267 -0
  43. package/dist/app/editor/ImagePlugin.d.ts +3 -0
  44. package/dist/app/editor/ImagePlugin.js +187 -0
  45. package/dist/app/editor/LinkClickPlugin.d.ts +23 -0
  46. package/dist/app/editor/LinkClickPlugin.js +90 -0
  47. package/dist/app/editor/MarkdownShortcutsPlugin.d.ts +49 -0
  48. package/dist/app/editor/MarkdownShortcutsPlugin.js +373 -0
  49. package/dist/app/editor/OrderedTaskListPlugin.d.ts +21 -0
  50. package/dist/app/editor/OrderedTaskListPlugin.js +78 -0
  51. package/dist/app/editor/SearchPlugin.d.ts +1 -0
  52. package/dist/app/editor/SearchPlugin.js +379 -0
  53. package/dist/app/editor/SelectionContextMenuPlugin.d.ts +29 -0
  54. package/dist/app/editor/SelectionContextMenuPlugin.js +160 -0
  55. package/dist/app/editor/SlashMenu.d.ts +11 -0
  56. package/dist/app/editor/SlashMenu.js +494 -0
  57. package/dist/app/editor/SlashMenuPlugin.d.ts +1 -0
  58. package/dist/app/editor/SlashMenuPlugin.js +168 -0
  59. package/dist/app/editor/TableActionsPlugin.d.ts +1 -0
  60. package/dist/app/editor/TableActionsPlugin.js +398 -0
  61. package/dist/app/editor/TogglePlugin.d.ts +1 -0
  62. package/dist/app/editor/TogglePlugin.js +159 -0
  63. package/dist/app/editor/Toolbar.d.ts +10 -0
  64. package/dist/app/editor/Toolbar.js +263 -0
  65. package/dist/app/editor/WikiLinkExistencePlugin.d.ts +17 -0
  66. package/dist/app/editor/WikiLinkExistencePlugin.js +104 -0
  67. package/dist/app/editor/WikiLinkFormatPlugin.d.ts +17 -0
  68. package/dist/app/editor/WikiLinkFormatPlugin.js +203 -0
  69. package/dist/app/editor/annotation-marks.d.ts +177 -0
  70. package/dist/app/editor/annotation-marks.js +750 -0
  71. package/dist/app/editor/annotationCommands.d.ts +13 -0
  72. package/dist/app/editor/annotationCommands.js +11 -0
  73. package/dist/app/editor/c4/C4InteractiveRenderer.d.ts +35 -0
  74. package/dist/app/editor/c4/C4InteractiveRenderer.js +299 -0
  75. package/dist/app/editor/c4/edge-clipping.d.ts +24 -0
  76. package/dist/app/editor/c4/edge-clipping.js +139 -0
  77. package/dist/app/editor/c4/hooks/useC4DiagramDrag.d.ts +38 -0
  78. package/dist/app/editor/c4/hooks/useC4DiagramDrag.js +112 -0
  79. package/dist/app/editor/c4/layout.d.ts +25 -0
  80. package/dist/app/editor/c4/layout.js +839 -0
  81. package/dist/app/editor/c4/parser.d.ts +19 -0
  82. package/dist/app/editor/c4/parser.js +410 -0
  83. package/dist/app/editor/c4/render-to-string.d.ts +24 -0
  84. package/dist/app/editor/c4/render-to-string.js +34 -0
  85. package/dist/app/editor/c4/renderer.d.ts +64 -0
  86. package/dist/app/editor/c4/renderer.js +569 -0
  87. package/dist/app/editor/c4/types.d.ts +203 -0
  88. package/dist/app/editor/c4/types.js +43 -0
  89. package/dist/app/editor/correction-yaml.d.ts +10 -0
  90. package/dist/app/editor/correction-yaml.js +40 -0
  91. package/dist/app/editor/editorNodes.d.ts +9 -0
  92. package/dist/app/editor/editorNodes.js +36 -0
  93. package/dist/app/editor/index.d.ts +1 -0
  94. package/dist/app/editor/index.js +1 -0
  95. package/dist/app/editor/mark-overlap-resolver.d.ts +6 -0
  96. package/dist/app/editor/mark-overlap-resolver.js +45 -0
  97. package/dist/app/editor/nodes/C4Component.d.ts +14 -0
  98. package/dist/app/editor/nodes/C4Component.js +284 -0
  99. package/dist/app/editor/nodes/C4Node.d.ts +32 -0
  100. package/dist/app/editor/nodes/C4Node.js +112 -0
  101. package/dist/app/editor/nodes/CalloutNode.d.ts +23 -0
  102. package/dist/app/editor/nodes/CalloutNode.js +94 -0
  103. package/dist/app/editor/nodes/CustomLinkNode.d.ts +57 -0
  104. package/dist/app/editor/nodes/CustomLinkNode.js +209 -0
  105. package/dist/app/editor/nodes/CustomListItemNode.d.ts +46 -0
  106. package/dist/app/editor/nodes/CustomListItemNode.js +120 -0
  107. package/dist/app/editor/nodes/CustomListNode.d.ts +27 -0
  108. package/dist/app/editor/nodes/CustomListNode.js +54 -0
  109. package/dist/app/editor/nodes/DefinitionListNode.d.ts +44 -0
  110. package/dist/app/editor/nodes/DefinitionListNode.js +151 -0
  111. package/dist/app/editor/nodes/DiagramContextMenu.d.ts +45 -0
  112. package/dist/app/editor/nodes/DiagramContextMenu.js +116 -0
  113. package/dist/app/editor/nodes/EquationComponent.d.ts +17 -0
  114. package/dist/app/editor/nodes/EquationComponent.js +130 -0
  115. package/dist/app/editor/nodes/EquationNode.d.ts +43 -0
  116. package/dist/app/editor/nodes/EquationNode.js +179 -0
  117. package/dist/app/editor/nodes/FootnoteNode.d.ts +40 -0
  118. package/dist/app/editor/nodes/FootnoteNode.js +151 -0
  119. package/dist/app/editor/nodes/FrontmatterNode.d.ts +24 -0
  120. package/dist/app/editor/nodes/FrontmatterNode.js +88 -0
  121. package/dist/app/editor/nodes/HorizontalRuleNode.d.ts +17 -0
  122. package/dist/app/editor/nodes/HorizontalRuleNode.js +57 -0
  123. package/dist/app/editor/nodes/HtmlNode.d.ts +32 -0
  124. package/dist/app/editor/nodes/HtmlNode.js +121 -0
  125. package/dist/app/editor/nodes/ImageComponent.d.ts +11 -0
  126. package/dist/app/editor/nodes/ImageComponent.js +228 -0
  127. package/dist/app/editor/nodes/ImageNode.d.ts +53 -0
  128. package/dist/app/editor/nodes/ImageNode.js +183 -0
  129. package/dist/app/editor/nodes/ListItemParagraphBreakNode.d.ts +42 -0
  130. package/dist/app/editor/nodes/ListItemParagraphBreakNode.js +95 -0
  131. package/dist/app/editor/nodes/MermaidComponent.d.ts +11 -0
  132. package/dist/app/editor/nodes/MermaidComponent.js +216 -0
  133. package/dist/app/editor/nodes/MermaidNode.d.ts +27 -0
  134. package/dist/app/editor/nodes/MermaidNode.js +96 -0
  135. package/dist/app/editor/nodes/ToggleNode.d.ts +56 -0
  136. package/dist/app/editor/nodes/ToggleNode.js +237 -0
  137. package/dist/app/editor/nodes/diagram-context-menu.d.ts +11 -0
  138. package/dist/app/editor/nodes/diagram-context-menu.js +70 -0
  139. package/dist/app/editor/nodes/diagram-utils.d.ts +13 -0
  140. package/dist/app/editor/nodes/diagram-utils.js +29 -0
  141. package/dist/app/editor/nodes/index.d.ts +30 -0
  142. package/dist/app/editor/nodes/index.js +17 -0
  143. package/dist/app/editor/utils.d.ts +2 -0
  144. package/dist/app/editor/utils.js +17 -0
  145. package/dist/app/mapper/index.d.ts +3 -0
  146. package/dist/app/mapper/index.js +2 -0
  147. package/dist/app/mapper/lexicalToMdast.d.ts +62 -0
  148. package/dist/app/mapper/lexicalToMdast.js +1937 -0
  149. package/dist/app/mapper/mdastToLexical.d.ts +31 -0
  150. package/dist/app/mapper/mdastToLexical.js +1075 -0
  151. package/dist/contract.d.ts +8 -0
  152. package/dist/contract.js +8 -0
  153. package/dist/headless.d.ts +40 -0
  154. package/dist/headless.js +45 -0
  155. package/dist/host/context.d.ts +20 -0
  156. package/dist/host/context.js +24 -0
  157. package/dist/host/defaults.d.ts +21 -0
  158. package/dist/host/defaults.js +47 -0
  159. package/dist/host/messages.d.ts +26 -0
  160. package/dist/host/messages.js +47 -0
  161. package/dist/host/types.d.ts +75 -0
  162. package/dist/host/types.js +13 -0
  163. package/dist/index.d.ts +35 -0
  164. package/dist/index.js +51 -0
  165. package/dist/markdown/annotate-sentinels.d.ts +42 -0
  166. package/dist/markdown/annotate-sentinels.js +86 -0
  167. package/dist/markdown/parse.d.ts +56 -0
  168. package/dist/markdown/parse.js +516 -0
  169. package/dist/markdown/stringify.d.ts +7 -0
  170. package/dist/markdown/stringify.js +439 -0
  171. package/dist/markdown/vendor/mdast-util-wiki-link/LICENSE +21 -0
  172. package/dist/markdown/vendor/mdast-util-wiki-link/README.md +41 -0
  173. package/dist/markdown/vendor/mdast-util-wiki-link/from-markdown.d.ts +72 -0
  174. package/dist/markdown/vendor/mdast-util-wiki-link/from-markdown.js +127 -0
  175. package/dist/markdown/vendor/mdast-util-wiki-link/index.d.ts +8 -0
  176. package/dist/markdown/vendor/mdast-util-wiki-link/index.js +6 -0
  177. package/dist/markdown/vendor/mdast-util-wiki-link/to-markdown.d.ts +17 -0
  178. package/dist/markdown/vendor/mdast-util-wiki-link/to-markdown.js +33 -0
  179. package/dist/markdown.d.ts +28 -0
  180. package/dist/markdown.js +44 -0
  181. package/dist/mathjax-config.d.ts +89 -0
  182. package/dist/mathjax-config.js +133 -0
  183. package/dist/nodes.d.ts +34 -0
  184. package/dist/nodes.js +34 -0
  185. package/dist/stores/correctionStore.d.ts +15 -0
  186. package/dist/stores/correctionStore.js +12 -0
  187. package/dist/stores/editorStore.d.ts +16 -0
  188. package/dist/stores/editorStore.js +27 -0
  189. package/dist/styles.css +2477 -0
  190. package/dist/types.d.ts +296 -0
  191. package/dist/types.js +91 -0
  192. package/dist/utils/file-types.d.ts +10 -0
  193. package/dist/utils/file-types.js +46 -0
  194. package/docs/annotations.md +134 -0
  195. package/docs/decisions/README.md +72 -0
  196. package/docs/decisions/adr-002.md +27 -0
  197. package/docs/decisions/adr-007.md +27 -0
  198. package/docs/decisions/adr-010.md +26 -0
  199. package/docs/decisions/adr-024.md +124 -0
  200. package/docs/decisions/adr-024b.md +0 -0
  201. package/docs/decisions/adr-025.md +103 -0
  202. package/docs/decisions/adr-027.md +66 -0
  203. package/docs/decisions/adr-057.md +67 -0
  204. package/docs/decisions/adr-070.md +131 -0
  205. package/docs/decisions/adr-075.md +410 -0
  206. package/docs/decisions/adr-076.md +183 -0
  207. package/docs/decisions/adr-077.md +410 -0
  208. package/docs/decisions/adr-078.md +243 -0
  209. package/docs/decisions/adr-079.md +167 -0
  210. package/docs/decisions/adr-080.md +325 -0
  211. package/docs/decisions/adr-081.md +108 -0
  212. package/docs/decisions/adr-082.md +123 -0
  213. package/docs/decisions/adr-083.md +204 -0
  214. package/docs/decisions/adr-084.md +179 -0
  215. package/docs/editor-api.md +106 -0
  216. package/docs/markdown-pipeline.md +282 -0
  217. package/docs/provenance.md +106 -0
  218. package/docs/zusammen-editor-capability-map.md +232 -0
  219. package/package.json +175 -0
@@ -0,0 +1,209 @@
1
+ import { LinkNode } from '@lexical/link';
2
+ /**
3
+ * CustomLinkNode - Extends Lexical's LinkNode to prevent VS Code webview link interception
4
+ *
5
+ * The standard LinkNode renders `<a href="...">` which VS Code webviews intercept and
6
+ * open automatically on click. This custom node renders `<a data-href="...">` instead,
7
+ * allowing us to control navigation via JavaScript (Cmd+click to open).
8
+ */
9
+ export class CustomLinkNode extends LinkNode {
10
+ /** @internal */
11
+ __wikiAliasState;
12
+ /**
13
+ * Whether this link was parsed from genuine `[[target]]` / `[[target|alias]]`
14
+ * wiki-link syntax, as opposed to a standard `[text](url)` link whose URL
15
+ * merely looks wiki-link-shaped. Export (`convertLinkNode` in
16
+ * `lexicalToMdast.ts`) always promotes a node with this flag set back to
17
+ * wiki-link syntax, regardless of `wikiLinkPromotionMode` — an opted-out
18
+ * host still must not corrupt a document's existing wiki-links (liminis#951).
19
+ * @internal
20
+ */
21
+ __wikiLinkOrigin;
22
+ constructor(url, attributes, key) {
23
+ super(url, attributes, key);
24
+ this.__wikiAliasState = null;
25
+ this.__wikiLinkOrigin = false;
26
+ }
27
+ static getType() {
28
+ return 'link'; // Use same type to replace the default LinkNode
29
+ }
30
+ static clone(node) {
31
+ const cloned = new CustomLinkNode(node.__url, { rel: node.__rel, target: node.__target, title: node.__title }, node.__key);
32
+ cloned.__wikiAliasState = node.__wikiAliasState;
33
+ cloned.__wikiLinkOrigin = node.__wikiLinkOrigin;
34
+ return cloned;
35
+ }
36
+ createDOM(config) {
37
+ void config;
38
+ const element = document.createElement('a');
39
+ // Store URL in data-href instead of href to prevent webview interception
40
+ element.setAttribute('data-href', this.__url);
41
+ if (this.__target !== null) {
42
+ element.target = this.__target;
43
+ }
44
+ if (this.__rel !== null) {
45
+ element.rel = this.__rel;
46
+ }
47
+ if (this.__title !== null) {
48
+ element.title = this.__title;
49
+ }
50
+ // Add visual styling class
51
+ element.className = 'editor-link';
52
+ // Mark wiki-links (internal .md links) with a data attribute
53
+ // This allows the WikiLinkExistencePlugin to check and style broken links
54
+ if (this.isWikiLink()) {
55
+ element.setAttribute('data-wiki-link', 'true');
56
+ element.setAttribute('data-wiki-target', this.__url);
57
+ }
58
+ else if (this.isExternalLink()) {
59
+ // External links get blue styling to differentiate from wiki-links
60
+ element.classList.add('editor-link-external');
61
+ }
62
+ return element;
63
+ }
64
+ /**
65
+ * Check if this link is a wiki-link (internal markdown file)
66
+ */
67
+ isWikiLink() {
68
+ const url = this.__url;
69
+ // Wiki-links are internal paths that don't start with http/https/mailto
70
+ // and typically end in .md or have no extension
71
+ return !url.startsWith('http://') &&
72
+ !url.startsWith('https://') &&
73
+ !url.startsWith('mailto:') &&
74
+ !url.startsWith('#');
75
+ }
76
+ /**
77
+ * Check if this link is an external link (http/https/mailto)
78
+ */
79
+ isExternalLink() {
80
+ const url = this.__url;
81
+ return url.startsWith('http://') || url.startsWith('https://') || url.startsWith('mailto:');
82
+ }
83
+ updateDOM(prevNode, anchor, config) {
84
+ void config;
85
+ const url = this.__url;
86
+ const target = this.__target;
87
+ const rel = this.__rel;
88
+ const title = this.__title;
89
+ if (url !== prevNode.__url) {
90
+ anchor.setAttribute('data-href', url);
91
+ // Update wiki-link attributes and external link class when URL changes
92
+ if (this.isWikiLink()) {
93
+ anchor.setAttribute('data-wiki-link', 'true');
94
+ anchor.setAttribute('data-wiki-target', url);
95
+ anchor.classList.remove('editor-link-external');
96
+ }
97
+ else {
98
+ anchor.removeAttribute('data-wiki-link');
99
+ anchor.removeAttribute('data-wiki-target');
100
+ anchor.classList.remove('editor-link-broken');
101
+ if (this.isExternalLink()) {
102
+ anchor.classList.add('editor-link-external');
103
+ }
104
+ else {
105
+ anchor.classList.remove('editor-link-external');
106
+ }
107
+ }
108
+ }
109
+ if (target !== prevNode.__target) {
110
+ if (target) {
111
+ anchor.target = target;
112
+ }
113
+ else {
114
+ anchor.removeAttribute('target');
115
+ }
116
+ }
117
+ if (rel !== prevNode.__rel) {
118
+ if (rel) {
119
+ anchor.rel = rel;
120
+ }
121
+ else {
122
+ anchor.removeAttribute('rel');
123
+ }
124
+ }
125
+ if (title !== prevNode.__title) {
126
+ if (title) {
127
+ anchor.title = title;
128
+ }
129
+ else {
130
+ anchor.removeAttribute('title');
131
+ }
132
+ }
133
+ return false;
134
+ }
135
+ static importDOM() {
136
+ return {
137
+ a: (node) => {
138
+ void node;
139
+ return {
140
+ conversion: convertAnchorElement,
141
+ priority: 1,
142
+ };
143
+ },
144
+ };
145
+ }
146
+ static importJSON(serializedNode) {
147
+ const node = $createCustomLinkNode(serializedNode.url, {
148
+ rel: serializedNode.rel,
149
+ target: serializedNode.target,
150
+ title: serializedNode.title,
151
+ });
152
+ if (serializedNode.wikiAliasState === 'empty') {
153
+ node.__wikiAliasState = 'empty';
154
+ }
155
+ if (serializedNode.wikiLinkOrigin) {
156
+ node.__wikiLinkOrigin = true;
157
+ }
158
+ node.setFormat(serializedNode.format);
159
+ node.setIndent(serializedNode.indent);
160
+ node.setDirection(serializedNode.direction);
161
+ return node;
162
+ }
163
+ exportJSON() {
164
+ return {
165
+ ...super.exportJSON(),
166
+ type: 'link',
167
+ version: 1,
168
+ wikiAliasState: this.__wikiAliasState ?? undefined,
169
+ wikiLinkOrigin: this.__wikiLinkOrigin ? true : undefined,
170
+ };
171
+ }
172
+ setWikiAliasState(state) {
173
+ const writable = this.getWritable();
174
+ writable.__wikiAliasState = state;
175
+ }
176
+ getWikiAliasState() {
177
+ return this.__wikiAliasState ?? null;
178
+ }
179
+ setWikiLinkOrigin(origin) {
180
+ const writable = this.getWritable();
181
+ writable.__wikiLinkOrigin = origin;
182
+ }
183
+ getWikiLinkOrigin() {
184
+ return this.__wikiLinkOrigin;
185
+ }
186
+ }
187
+ function convertAnchorElement(domNode) {
188
+ let node = null;
189
+ if (domNode instanceof HTMLAnchorElement) {
190
+ // Check data-href first (our custom attribute), then fall back to href
191
+ const url = domNode.getAttribute('data-href') || domNode.getAttribute('href');
192
+ if (url) {
193
+ node = $createCustomLinkNode(url, {
194
+ rel: domNode.getAttribute('rel'),
195
+ target: domNode.getAttribute('target'),
196
+ title: domNode.getAttribute('title'),
197
+ });
198
+ }
199
+ }
200
+ return { node };
201
+ }
202
+ export function $createCustomLinkNode(url, attributes) {
203
+ const node = new CustomLinkNode(url, attributes);
204
+ node.__wikiAliasState = null;
205
+ return node;
206
+ }
207
+ export function $isCustomLinkNode(node) {
208
+ return node instanceof CustomLinkNode;
209
+ }
@@ -0,0 +1,46 @@
1
+ import { ListItemNode, SerializedListItemNode } from '@lexical/list';
2
+ import { LexicalNode, NodeKey, EditorConfig } from 'lexical';
3
+ export declare const ORDERED_TASK_CHECKED_CLASS = "editor-listitem-ordered-checked";
4
+ export declare const ORDERED_TASK_UNCHECKED_CLASS = "editor-listitem-ordered-unchecked";
5
+ export type SerializedCustomListItemNode = SerializedListItemNode & {
6
+ taskChecked?: boolean | null;
7
+ };
8
+ /**
9
+ * CustomListItemNode - Extends Lexical's ListItemNode to carry each item's true
10
+ * per-item task-checkbox state through the markdown round trip.
11
+ *
12
+ * Stock ListItemNode.getChecked() is not a stored value — it's derived as
13
+ * `parentList.getListType() === 'check' ? Boolean(__checked) : undefined`. In a
14
+ * list that mixes checkbox and plain items, the list is typed 'check' (so the
15
+ * items that need checkbox UI get it), which makes every plain item's
16
+ * getChecked() coerce `Boolean(undefined)` to `false` — indistinguishable from
17
+ * a genuine unchecked task. This subclass stores the tri-state
18
+ * (checked / unchecked / not-a-task) independent of the parent list's type, so
19
+ * export can tell plain items and unchecked tasks apart.
20
+ */
21
+ export declare class CustomListItemNode extends ListItemNode {
22
+ /** @internal */
23
+ __taskChecked: boolean | null;
24
+ constructor(value?: number, checked?: boolean, key?: NodeKey);
25
+ static getType(): string;
26
+ static clone(node: CustomListItemNode): CustomListItemNode;
27
+ static importJSON(serializedNode: SerializedCustomListItemNode): CustomListItemNode;
28
+ exportJSON(): SerializedCustomListItemNode;
29
+ setChecked(checked?: boolean): this;
30
+ setTaskChecked(taskChecked: boolean | null): void;
31
+ getTaskChecked(): boolean | null;
32
+ createDOM(config: EditorConfig): HTMLElement;
33
+ updateDOM(prevNode: CustomListItemNode, dom: HTMLElement, config: EditorConfig): boolean;
34
+ /**
35
+ * Applies (or clears) the ordered-task checkbox affordance. Only fires for
36
+ * a leaf item (mirrors stock's own `!$isListNode(getFirstChild())` guard —
37
+ * a list item whose only child is a nested list isn't itself a task) whose
38
+ * parent list is `'number'`-typed and whose own `__taskChecked` (this
39
+ * item's field, not the list's) is non-null — a plain item in a mixed
40
+ * ordered list is left with no checkbox, same as a plain item in a mixed
41
+ * unordered list today.
42
+ */
43
+ private updateOrderedTaskDOM;
44
+ }
45
+ export declare function $createCustomListItemNode(checked?: boolean): CustomListItemNode;
46
+ export declare function $isCustomListItemNode(node: LexicalNode | null | undefined): node is CustomListItemNode;
@@ -0,0 +1,120 @@
1
+ import { ListItemNode, $isListNode } from '@lexical/list';
2
+ // Ordered-task checkbox rendering is owned entirely by this node rather than
3
+ // by @lexical/list's own checklist support, which only renders a checkbox
4
+ // (DOM attributes, theme classes, click/keyboard handling) when the *parent
5
+ // list's* listType is 'check' — and Lexical has no 'check'-typed list that is
6
+ // also numbered. These class names are deliberately separate from the
7
+ // editorTheme's listitemChecked/listitemUnchecked (Editor.tsx), which apply
8
+ // `list-style-type: none` and would suppress <ol> numbering.
9
+ export const ORDERED_TASK_CHECKED_CLASS = 'editor-listitem-ordered-checked';
10
+ export const ORDERED_TASK_UNCHECKED_CLASS = 'editor-listitem-ordered-unchecked';
11
+ /**
12
+ * CustomListItemNode - Extends Lexical's ListItemNode to carry each item's true
13
+ * per-item task-checkbox state through the markdown round trip.
14
+ *
15
+ * Stock ListItemNode.getChecked() is not a stored value — it's derived as
16
+ * `parentList.getListType() === 'check' ? Boolean(__checked) : undefined`. In a
17
+ * list that mixes checkbox and plain items, the list is typed 'check' (so the
18
+ * items that need checkbox UI get it), which makes every plain item's
19
+ * getChecked() coerce `Boolean(undefined)` to `false` — indistinguishable from
20
+ * a genuine unchecked task. This subclass stores the tri-state
21
+ * (checked / unchecked / not-a-task) independent of the parent list's type, so
22
+ * export can tell plain items and unchecked tasks apart.
23
+ */
24
+ export class CustomListItemNode extends ListItemNode {
25
+ /** @internal */
26
+ __taskChecked;
27
+ constructor(value, checked, key) {
28
+ super(value, checked, key);
29
+ this.__taskChecked = checked ?? null;
30
+ }
31
+ static getType() {
32
+ return 'listitem'; // Use same type to replace the default ListItemNode
33
+ }
34
+ static clone(node) {
35
+ const cloned = new CustomListItemNode(node.__value, node.__checked, node.__key);
36
+ cloned.__taskChecked = $isCustomListItemNode(node) ? node.__taskChecked : null;
37
+ return cloned;
38
+ }
39
+ static importJSON(serializedNode) {
40
+ const node = $createCustomListItemNode(serializedNode.checked);
41
+ // Only fall back to the legacy `checked` field when `taskChecked` is
42
+ // absent (older serialized documents predating this field) — an
43
+ // explicit `taskChecked: null` means "plain item" and must not be
44
+ // coerced into `checked`'s boolean via `??`, or a plain item in a
45
+ // mixed list would be rehydrated as an unchecked task.
46
+ node.__taskChecked = serializedNode.taskChecked !== undefined ? serializedNode.taskChecked : (serializedNode.checked ?? null);
47
+ node.setValue(serializedNode.value);
48
+ node.setFormat(serializedNode.format);
49
+ node.setIndent(serializedNode.indent);
50
+ node.setDirection(serializedNode.direction);
51
+ return node;
52
+ }
53
+ exportJSON() {
54
+ return {
55
+ ...super.exportJSON(),
56
+ type: 'listitem',
57
+ taskChecked: this.__taskChecked,
58
+ };
59
+ }
60
+ // Lexical's own CheckListPlugin/registerList() transforms call setChecked()
61
+ // directly during live editing, bypassing the mapper entirely. Syncing
62
+ // __taskChecked here means toggling a checkbox on a previously-plain item
63
+ // in a mixed list correctly promotes it to a real task.
64
+ setChecked(checked) {
65
+ const writable = super.setChecked(checked);
66
+ writable.__taskChecked = checked ?? null;
67
+ return writable;
68
+ }
69
+ setTaskChecked(taskChecked) {
70
+ const writable = this.getWritable();
71
+ writable.__taskChecked = taskChecked;
72
+ }
73
+ getTaskChecked() {
74
+ return this.__taskChecked;
75
+ }
76
+ createDOM(config) {
77
+ const dom = super.createDOM(config);
78
+ this.updateOrderedTaskDOM(dom);
79
+ return dom;
80
+ }
81
+ updateDOM(prevNode, dom, config) {
82
+ const result = super.updateDOM(prevNode, dom, config);
83
+ this.updateOrderedTaskDOM(dom);
84
+ return result;
85
+ }
86
+ /**
87
+ * Applies (or clears) the ordered-task checkbox affordance. Only fires for
88
+ * a leaf item (mirrors stock's own `!$isListNode(getFirstChild())` guard —
89
+ * a list item whose only child is a nested list isn't itself a task) whose
90
+ * parent list is `'number'`-typed and whose own `__taskChecked` (this
91
+ * item's field, not the list's) is non-null — a plain item in a mixed
92
+ * ordered list is left with no checkbox, same as a plain item in a mixed
93
+ * unordered list today.
94
+ */
95
+ updateOrderedTaskDOM(dom) {
96
+ const parent = this.getParent();
97
+ const isOrderedTask = $isListNode(parent) && parent.getListType() === 'number' && !$isListNode(this.getFirstChild()) && this.__taskChecked !== null;
98
+ dom.classList.remove(ORDERED_TASK_CHECKED_CLASS, ORDERED_TASK_UNCHECKED_CLASS);
99
+ if (!isOrderedTask) {
100
+ // Stock updateListItemChecked already clears these for any non-'check'
101
+ // parent list, but that runs before this item's own listType/parent may
102
+ // have changed shape (e.g. an ordered task item demoted to plain) — so
103
+ // clear explicitly here too rather than relying on ordering.
104
+ dom.removeAttribute('role');
105
+ dom.removeAttribute('tabIndex');
106
+ dom.removeAttribute('aria-checked');
107
+ return;
108
+ }
109
+ dom.classList.add(this.__taskChecked ? ORDERED_TASK_CHECKED_CLASS : ORDERED_TASK_UNCHECKED_CLASS);
110
+ dom.setAttribute('role', 'checkbox');
111
+ dom.setAttribute('tabIndex', '-1');
112
+ dom.setAttribute('aria-checked', this.__taskChecked ? 'true' : 'false');
113
+ }
114
+ }
115
+ export function $createCustomListItemNode(checked) {
116
+ return new CustomListItemNode(undefined, checked);
117
+ }
118
+ export function $isCustomListItemNode(node) {
119
+ return node instanceof CustomListItemNode;
120
+ }
@@ -0,0 +1,27 @@
1
+ import { ListNode, SerializedListNode, ListType } from '@lexical/list';
2
+ import { LexicalNode } from 'lexical';
3
+ export type SerializedCustomListNode = SerializedListNode & {
4
+ spread?: boolean;
5
+ };
6
+ /**
7
+ * CustomListNode - Extends Lexical's ListNode to carry the CommonMark "spread"
8
+ * (loose vs. tight) state through the markdown round trip.
9
+ *
10
+ * Stock ListNode has no field for this — the loose/tight distinction (blank
11
+ * lines between list items) exists on the parsed mdast List node but is
12
+ * otherwise discarded once imported into Lexical. This subclass preserves it
13
+ * so it can be written back out on export.
14
+ */
15
+ export declare class CustomListNode extends ListNode {
16
+ /** @internal */
17
+ __spread: boolean;
18
+ constructor(listType: ListType, start: number, key?: string);
19
+ static getType(): string;
20
+ static clone(node: CustomListNode): CustomListNode;
21
+ static importJSON(serializedNode: SerializedCustomListNode): CustomListNode;
22
+ exportJSON(): SerializedCustomListNode;
23
+ setSpread(spread: boolean): void;
24
+ getSpread(): boolean;
25
+ }
26
+ export declare function $createCustomListNode(listType: ListType, start?: number): CustomListNode;
27
+ export declare function $isCustomListNode(node: LexicalNode | null | undefined): node is CustomListNode;
@@ -0,0 +1,54 @@
1
+ import { ListNode } from '@lexical/list';
2
+ /**
3
+ * CustomListNode - Extends Lexical's ListNode to carry the CommonMark "spread"
4
+ * (loose vs. tight) state through the markdown round trip.
5
+ *
6
+ * Stock ListNode has no field for this — the loose/tight distinction (blank
7
+ * lines between list items) exists on the parsed mdast List node but is
8
+ * otherwise discarded once imported into Lexical. This subclass preserves it
9
+ * so it can be written back out on export.
10
+ */
11
+ export class CustomListNode extends ListNode {
12
+ /** @internal */
13
+ __spread;
14
+ constructor(listType, start, key) {
15
+ super(listType, start, key);
16
+ this.__spread = false;
17
+ }
18
+ static getType() {
19
+ return 'list'; // Use same type to replace the default ListNode
20
+ }
21
+ static clone(node) {
22
+ const cloned = new CustomListNode(node.__listType, node.__start, node.__key);
23
+ cloned.__spread = $isCustomListNode(node) ? node.__spread : false;
24
+ return cloned;
25
+ }
26
+ static importJSON(serializedNode) {
27
+ const node = $createCustomListNode(serializedNode.listType, serializedNode.start);
28
+ node.__spread = serializedNode.spread ?? false;
29
+ node.setFormat(serializedNode.format);
30
+ node.setIndent(serializedNode.indent);
31
+ node.setDirection(serializedNode.direction);
32
+ return node;
33
+ }
34
+ exportJSON() {
35
+ return {
36
+ ...super.exportJSON(),
37
+ type: 'list',
38
+ spread: this.__spread,
39
+ };
40
+ }
41
+ setSpread(spread) {
42
+ const writable = this.getWritable();
43
+ writable.__spread = spread;
44
+ }
45
+ getSpread() {
46
+ return this.__spread;
47
+ }
48
+ }
49
+ export function $createCustomListNode(listType, start = 1) {
50
+ return new CustomListNode(listType, start);
51
+ }
52
+ export function $isCustomListNode(node) {
53
+ return node instanceof CustomListNode;
54
+ }
@@ -0,0 +1,44 @@
1
+ import { DOMConversionMap, DOMExportOutput, EditorConfig, ElementNode, LexicalNode, SerializedElementNode } from 'lexical';
2
+ export type SerializedDefinitionListNode = SerializedElementNode;
3
+ export declare class DefinitionListNode extends ElementNode {
4
+ static getType(): string;
5
+ static clone(node: DefinitionListNode): DefinitionListNode;
6
+ createDOM(config: EditorConfig): HTMLElement;
7
+ updateDOM(): boolean;
8
+ static importDOM(): DOMConversionMap | null;
9
+ exportDOM(): DOMExportOutput;
10
+ static importJSON(serializedNode: SerializedDefinitionListNode): DefinitionListNode;
11
+ exportJSON(): SerializedDefinitionListNode;
12
+ isShadowRoot(): boolean;
13
+ canBeEmpty(): boolean;
14
+ }
15
+ export declare function $createDefinitionListNode(): DefinitionListNode;
16
+ export declare function $isDefinitionListNode(node: LexicalNode | null | undefined): node is DefinitionListNode;
17
+ export type SerializedDefinitionTermNode = SerializedElementNode;
18
+ export declare class DefinitionTermNode extends ElementNode {
19
+ static getType(): string;
20
+ static clone(node: DefinitionTermNode): DefinitionTermNode;
21
+ createDOM(config: EditorConfig): HTMLElement;
22
+ updateDOM(): boolean;
23
+ static importDOM(): DOMConversionMap | null;
24
+ exportDOM(): DOMExportOutput;
25
+ static importJSON(serializedNode: SerializedDefinitionTermNode): DefinitionTermNode;
26
+ exportJSON(): SerializedDefinitionTermNode;
27
+ }
28
+ export declare function $createDefinitionTermNode(): DefinitionTermNode;
29
+ export declare function $isDefinitionTermNode(node: LexicalNode | null | undefined): node is DefinitionTermNode;
30
+ export type SerializedDefinitionDescriptionNode = SerializedElementNode;
31
+ export declare class DefinitionDescriptionNode extends ElementNode {
32
+ static getType(): string;
33
+ static clone(node: DefinitionDescriptionNode): DefinitionDescriptionNode;
34
+ createDOM(config: EditorConfig): HTMLElement;
35
+ updateDOM(): boolean;
36
+ static importDOM(): DOMConversionMap | null;
37
+ exportDOM(): DOMExportOutput;
38
+ static importJSON(serializedNode: SerializedDefinitionDescriptionNode): DefinitionDescriptionNode;
39
+ exportJSON(): SerializedDefinitionDescriptionNode;
40
+ isShadowRoot(): boolean;
41
+ canBeEmpty(): boolean;
42
+ }
43
+ export declare function $createDefinitionDescriptionNode(): DefinitionDescriptionNode;
44
+ export declare function $isDefinitionDescriptionNode(node: LexicalNode | null | undefined): node is DefinitionDescriptionNode;
@@ -0,0 +1,151 @@
1
+ import { ElementNode, } from 'lexical';
2
+ export class DefinitionListNode extends ElementNode {
3
+ static getType() {
4
+ return 'definition-list';
5
+ }
6
+ static clone(node) {
7
+ return new DefinitionListNode(node.__key);
8
+ }
9
+ createDOM(config) {
10
+ void config;
11
+ return document.createElement('dl');
12
+ }
13
+ updateDOM() {
14
+ return false;
15
+ }
16
+ static importDOM() {
17
+ return {
18
+ dl: () => ({
19
+ conversion: convertDefinitionListElement,
20
+ priority: 1,
21
+ }),
22
+ };
23
+ }
24
+ exportDOM() {
25
+ return { element: document.createElement('dl') };
26
+ }
27
+ static importJSON(serializedNode) {
28
+ void serializedNode;
29
+ return $createDefinitionListNode();
30
+ }
31
+ exportJSON() {
32
+ return {
33
+ ...super.exportJSON(),
34
+ type: 'definition-list',
35
+ version: 1,
36
+ };
37
+ }
38
+ isShadowRoot() {
39
+ return true;
40
+ }
41
+ canBeEmpty() {
42
+ return false;
43
+ }
44
+ }
45
+ function convertDefinitionListElement() {
46
+ return { node: $createDefinitionListNode() };
47
+ }
48
+ export function $createDefinitionListNode() {
49
+ return new DefinitionListNode();
50
+ }
51
+ export function $isDefinitionListNode(node) {
52
+ return node instanceof DefinitionListNode;
53
+ }
54
+ export class DefinitionTermNode extends ElementNode {
55
+ static getType() {
56
+ return 'definition-term';
57
+ }
58
+ static clone(node) {
59
+ return new DefinitionTermNode(node.__key);
60
+ }
61
+ createDOM(config) {
62
+ void config;
63
+ return document.createElement('dt');
64
+ }
65
+ updateDOM() {
66
+ return false;
67
+ }
68
+ static importDOM() {
69
+ return {
70
+ dt: () => ({
71
+ conversion: convertDefinitionTermElement,
72
+ priority: 1,
73
+ }),
74
+ };
75
+ }
76
+ exportDOM() {
77
+ return { element: document.createElement('dt') };
78
+ }
79
+ static importJSON(serializedNode) {
80
+ void serializedNode;
81
+ return $createDefinitionTermNode();
82
+ }
83
+ exportJSON() {
84
+ return {
85
+ ...super.exportJSON(),
86
+ type: 'definition-term',
87
+ version: 1,
88
+ };
89
+ }
90
+ }
91
+ function convertDefinitionTermElement() {
92
+ return { node: $createDefinitionTermNode() };
93
+ }
94
+ export function $createDefinitionTermNode() {
95
+ return new DefinitionTermNode();
96
+ }
97
+ export function $isDefinitionTermNode(node) {
98
+ return node instanceof DefinitionTermNode;
99
+ }
100
+ export class DefinitionDescriptionNode extends ElementNode {
101
+ static getType() {
102
+ return 'definition-description';
103
+ }
104
+ static clone(node) {
105
+ return new DefinitionDescriptionNode(node.__key);
106
+ }
107
+ createDOM(config) {
108
+ void config;
109
+ return document.createElement('dd');
110
+ }
111
+ updateDOM() {
112
+ return false;
113
+ }
114
+ static importDOM() {
115
+ return {
116
+ dd: () => ({
117
+ conversion: convertDefinitionDescriptionElement,
118
+ priority: 1,
119
+ }),
120
+ };
121
+ }
122
+ exportDOM() {
123
+ return { element: document.createElement('dd') };
124
+ }
125
+ static importJSON(serializedNode) {
126
+ void serializedNode;
127
+ return $createDefinitionDescriptionNode();
128
+ }
129
+ exportJSON() {
130
+ return {
131
+ ...super.exportJSON(),
132
+ type: 'definition-description',
133
+ version: 1,
134
+ };
135
+ }
136
+ isShadowRoot() {
137
+ return true;
138
+ }
139
+ canBeEmpty() {
140
+ return false;
141
+ }
142
+ }
143
+ function convertDefinitionDescriptionElement() {
144
+ return { node: $createDefinitionDescriptionNode() };
145
+ }
146
+ export function $createDefinitionDescriptionNode() {
147
+ return new DefinitionDescriptionNode();
148
+ }
149
+ export function $isDefinitionDescriptionNode(node) {
150
+ return node instanceof DefinitionDescriptionNode;
151
+ }