@astryxdesign/core 0.6.2-canary.bfee8a4 → 0.6.2-canary.d4cc232

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 (103) hide show
  1. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  2. package/dist/CheckboxInput/CheckboxInput.js +12 -2
  3. package/dist/Collapsible/Collapsible.d.ts.map +1 -1
  4. package/dist/Collapsible/Collapsible.js +6 -1
  5. package/dist/Kbd/Kbd.d.ts +5 -3
  6. package/dist/Kbd/Kbd.d.ts.map +1 -1
  7. package/dist/Kbd/Kbd.js +36 -42
  8. package/dist/Link/Link.d.ts.map +1 -1
  9. package/dist/Link/Link.js +6 -2
  10. package/dist/Markdown/Markdown.d.ts.map +1 -1
  11. package/dist/Markdown/Markdown.js +63 -59
  12. package/dist/Markdown/ast.d.ts +124 -0
  13. package/dist/Markdown/ast.d.ts.map +1 -0
  14. package/dist/Markdown/ast.js +38 -0
  15. package/dist/Markdown/parser.d.ts +14 -8
  16. package/dist/Markdown/parser.d.ts.map +1 -1
  17. package/dist/Markdown/parser.js +352 -97
  18. package/dist/Outline/parseOutlineFromMarkdown.d.ts.map +1 -1
  19. package/dist/Outline/parseOutlineFromMarkdown.js +7 -7
  20. package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
  21. package/dist/PowerSearch/PowerSearchEditPopover.js +46 -30
  22. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  23. package/dist/RadioList/RadioListItem.js +13 -1
  24. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  25. package/dist/SegmentedControl/SegmentedControlItem.js +5 -5
  26. package/dist/SideNav/SideNav.d.ts +2 -1
  27. package/dist/SideNav/SideNav.d.ts.map +1 -1
  28. package/dist/SideNav/SideNav.js +9 -3
  29. package/dist/Slider/Slider.d.ts.map +1 -1
  30. package/dist/Slider/Slider.js +14 -8
  31. package/dist/Switch/Switch.d.ts.map +1 -1
  32. package/dist/Switch/Switch.js +11 -0
  33. package/dist/TabList/Tab.d.ts +1 -1
  34. package/dist/TabList/Tab.d.ts.map +1 -1
  35. package/dist/TabList/Tab.js +20 -7
  36. package/dist/ToggleButton/ToggleButton.d.ts +2 -1
  37. package/dist/ToggleButton/ToggleButton.d.ts.map +1 -1
  38. package/dist/ToggleButton/ToggleButton.js +7 -1
  39. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  40. package/dist/Typeahead/BaseTypeahead.js +15 -6
  41. package/dist/astryx.css +8 -0
  42. package/dist/utils/interactionOverlay.stylex.d.ts +8 -0
  43. package/dist/utils/interactionOverlay.stylex.d.ts.map +1 -1
  44. package/dist/utils/interactionOverlay.stylex.js +9 -0
  45. package/package.json +3 -3
  46. package/src/Button/__tests__/Button.a11y.chromium.spec.ts +17 -1
  47. package/src/Button/__tests__/Button.a11y.known-failures.ts +0 -29
  48. package/src/Button/__tests__/Button.a11y.renders.tsx +9 -2
  49. package/src/Button/__tests__/Button.a11y.states.ts +9 -0
  50. package/src/CheckboxInput/CheckboxInput.doc.mjs +11 -0
  51. package/src/CheckboxInput/CheckboxInput.test.tsx +34 -0
  52. package/src/CheckboxInput/CheckboxInput.tsx +21 -1
  53. package/src/ClickableCard/ClickableCard.test.tsx +102 -5
  54. package/src/Collapsible/Collapsible.doc.mjs +11 -0
  55. package/src/Collapsible/Collapsible.test.tsx +21 -0
  56. package/src/Collapsible/Collapsible.tsx +5 -0
  57. package/src/Kbd/Kbd.doc.mjs +3 -3
  58. package/src/Kbd/Kbd.test.tsx +57 -1
  59. package/src/Kbd/Kbd.tsx +57 -37
  60. package/src/Link/Link.doc.mjs +11 -0
  61. package/src/Link/Link.test.tsx +24 -0
  62. package/src/Link/Link.tsx +5 -0
  63. package/src/Markdown/Markdown.spec.md +159 -43
  64. package/src/Markdown/Markdown.test.tsx +11 -0
  65. package/src/Markdown/Markdown.tsx +106 -96
  66. package/src/Markdown/ast.test.ts +169 -0
  67. package/src/Markdown/ast.ts +231 -0
  68. package/src/Markdown/incremental.test.ts +12 -0
  69. package/src/Markdown/parser.ts +427 -118
  70. package/src/Outline/Outline.spec.md +1 -1
  71. package/src/Outline/modules/parseOutlineFromMarkdown.spec.md +142 -0
  72. package/src/Outline/parseOutlineFromMarkdown.ts +8 -13
  73. package/src/PowerSearch/PowerSearchEditPopover.test.tsx +150 -1
  74. package/src/PowerSearch/PowerSearchEditPopover.tsx +51 -28
  75. package/src/RadioList/RadioList.doc.mjs +11 -0
  76. package/src/RadioList/RadioList.test.tsx +32 -0
  77. package/src/RadioList/RadioListItem.tsx +25 -1
  78. package/src/SegmentedControl/SegmentedControl.doc.mjs +2 -2
  79. package/src/SegmentedControl/SegmentedControl.test.tsx +31 -0
  80. package/src/SegmentedControl/SegmentedControlItem.tsx +6 -9
  81. package/src/SideNav/SideNav.doc.mjs +1 -1
  82. package/src/SideNav/SideNav.test.tsx +10 -0
  83. package/src/SideNav/SideNav.tsx +14 -2
  84. package/src/Slider/Slider.doc.mjs +11 -0
  85. package/src/Slider/Slider.test.tsx +128 -0
  86. package/src/Slider/Slider.tsx +25 -7
  87. package/src/Switch/Switch.doc.mjs +11 -0
  88. package/src/Switch/Switch.test.tsx +16 -0
  89. package/src/Switch/Switch.tsx +28 -0
  90. package/src/TabList/Tab.tsx +35 -7
  91. package/src/TabList/TabList.doc.mjs +11 -0
  92. package/src/TabList/TabList.test.tsx +66 -0
  93. package/src/TabList/__tests__/Tabs.a11y.known-failures.ts +1 -34
  94. package/src/ToggleButton/ToggleButton.test.tsx +133 -0
  95. package/src/ToggleButton/ToggleButton.tsx +9 -2
  96. package/src/ToggleButton/__tests__/ToggleButton.a11y.chromium.spec.ts +209 -0
  97. package/src/Tokenizer/Tokenizer.spec.md +142 -75
  98. package/src/Typeahead/BaseTypeahead.spec.md +4 -3
  99. package/src/Typeahead/BaseTypeahead.tsx +15 -6
  100. package/src/Typeahead/Typeahead.test.tsx +53 -0
  101. package/src/__tests__/PressedState.a11y.chromium.spec.ts +813 -0
  102. package/src/__tests__/pressState.ts +93 -0
  103. package/src/utils/interactionOverlay.stylex.ts +20 -0
@@ -2,14 +2,22 @@
2
2
 
3
3
  /**
4
4
  * @file parser.ts
5
- * @input Markdown string
6
- * @output Array of MarkdownNode AST nodes, including opt-in math; heading slug
7
- * helpers
8
- * (inlineText, slugify, uniqueSlug) shared by Markdown rendering and
9
- * Outline's parseOutlineFromMarkdown
10
- * @position Core parser; consumed by Markdown.tsx and Outline
5
+ * @input Markdown string and released parse options
6
+ * @output Canonical MDAST-aligned nodes for internal consumers plus unchanged
7
+ * released parser-node projections; shared heading slug helpers
8
+ * @position Core parser and compatibility boundary; consumed by Markdown and Outline
11
9
  */
12
10
 
11
+ import type {
12
+ MarkdownAstBlockContent,
13
+ MarkdownAstListItem,
14
+ MarkdownAstPhrasingContent,
15
+ MarkdownAstPosition,
16
+ MarkdownAstRoot,
17
+ MarkdownAstTableCell,
18
+ MarkdownAstTableRow,
19
+ } from './ast';
20
+
13
21
  // ---------------------------------------------------------------------------
14
22
  // Types
15
23
  // ---------------------------------------------------------------------------
@@ -130,6 +138,199 @@ export type TableCellNode = {children: InlineNode[]};
130
138
  type TableCellNodeWithMath = {children: InlineNodeWithMath[]};
131
139
  export type TableAlignment = 'left' | 'center' | 'right' | null;
132
140
 
141
+ type LegacyProjectionCache = WeakMap<object, object>;
142
+
143
+ function projectRange(
144
+ position: MarkdownAstPosition | undefined,
145
+ ): BlockMetadata {
146
+ const start = position?.start.offset;
147
+ const end = position?.end.offset;
148
+ return start == null || end == null ? {} : {range: {start, end}};
149
+ }
150
+
151
+ function projectInlineNode(
152
+ node: MarkdownAstPhrasingContent,
153
+ cache: LegacyProjectionCache,
154
+ ): InlineNodeWithMath {
155
+ const cached = cache.get(node);
156
+ if (cached != null) {
157
+ return cached as InlineNodeWithMath;
158
+ }
159
+ let projected: InlineNodeWithMath;
160
+ switch (node.type) {
161
+ case 'text':
162
+ projected = {type: 'text', content: node.value};
163
+ break;
164
+ case 'strong':
165
+ projected = {
166
+ type: 'bold',
167
+ children: node.children.map(child => projectInlineNode(child, cache)),
168
+ };
169
+ break;
170
+ case 'emphasis':
171
+ projected = {
172
+ type: 'italic',
173
+ children: node.children.map(child => projectInlineNode(child, cache)),
174
+ };
175
+ break;
176
+ case 'delete':
177
+ projected = {
178
+ type: 'strikethrough',
179
+ children: node.children.map(child => projectInlineNode(child, cache)),
180
+ };
181
+ break;
182
+ case 'inlineCode':
183
+ projected = {type: 'code', content: node.value};
184
+ break;
185
+ case 'inlineMath':
186
+ projected = {type: 'math', value: node.value};
187
+ break;
188
+ case 'link':
189
+ projected = {
190
+ type: 'link',
191
+ href: node.url,
192
+ children: node.children.map(child => projectInlineNode(child, cache)),
193
+ };
194
+ break;
195
+ case 'image':
196
+ projected = {type: 'image', src: node.url, alt: node.alt};
197
+ break;
198
+ case 'citation':
199
+ projected = {type: 'citation', sourceId: node.sourceId};
200
+ break;
201
+ case 'break':
202
+ projected = {type: 'break'};
203
+ break;
204
+ }
205
+ cache.set(node, projected);
206
+ return projected;
207
+ }
208
+
209
+ function projectTableCell(
210
+ node: MarkdownAstTableCell,
211
+ cache: LegacyProjectionCache,
212
+ ): TableCellNodeWithMath {
213
+ const cached = cache.get(node);
214
+ if (cached != null) {
215
+ return cached as TableCellNodeWithMath;
216
+ }
217
+ const projected = {
218
+ children: node.children.map(child => projectInlineNode(child, cache)),
219
+ };
220
+ cache.set(node, projected);
221
+ return projected;
222
+ }
223
+
224
+ function projectListItem(
225
+ node: MarkdownAstListItem,
226
+ cache: LegacyProjectionCache,
227
+ ): ListItemNodeWithMath {
228
+ const cached = cache.get(node);
229
+ if (cached != null) {
230
+ return cached as ListItemNodeWithMath;
231
+ }
232
+ const projected = {
233
+ checked: node.checked,
234
+ children: node.children.map(child => projectBlockNode(child, cache)),
235
+ };
236
+ cache.set(node, projected);
237
+ return projected;
238
+ }
239
+
240
+ function projectBlockNode(
241
+ node: MarkdownAstBlockContent,
242
+ cache: LegacyProjectionCache,
243
+ ): BlockNodeWithMath {
244
+ const cached = cache.get(node);
245
+ if (cached != null) {
246
+ return cached as BlockNodeWithMath;
247
+ }
248
+ const metadata = projectRange(node.position);
249
+ let projected: BlockNodeWithMath;
250
+ switch (node.type) {
251
+ case 'heading':
252
+ projected = {
253
+ type: 'heading',
254
+ level: node.depth,
255
+ children: node.children.map(child => projectInlineNode(child, cache)),
256
+ ...metadata,
257
+ };
258
+ break;
259
+ case 'paragraph':
260
+ projected = {
261
+ type: 'paragraph',
262
+ children: node.children.map(child => projectInlineNode(child, cache)),
263
+ ...metadata,
264
+ };
265
+ break;
266
+ case 'code':
267
+ projected = {
268
+ type: 'codeblock',
269
+ language: node.lang ?? 'plaintext',
270
+ content: node.value,
271
+ ...metadata,
272
+ };
273
+ break;
274
+ case 'math':
275
+ projected = {type: 'math', value: node.value, ...metadata};
276
+ break;
277
+ case 'blockquote':
278
+ projected = {
279
+ type: 'blockquote',
280
+ children: node.children.map(child => projectBlockNode(child, cache)),
281
+ ...metadata,
282
+ };
283
+ break;
284
+ case 'list':
285
+ projected = {
286
+ type: 'list',
287
+ ordered: node.ordered,
288
+ start: node.start,
289
+ delimiter: node.delimiter,
290
+ loose: node.spread,
291
+ items: node.children.map(item => projectListItem(item, cache)),
292
+ ...metadata,
293
+ };
294
+ break;
295
+ case 'table': {
296
+ const [header = {type: 'tableRow' as const, children: []}, ...rows] =
297
+ node.children;
298
+ projected = {
299
+ type: 'table',
300
+ headers: header.children.map(cell => projectTableCell(cell, cache)),
301
+ alignments: [...node.align],
302
+ rows: rows.map(row =>
303
+ row.children.map(cell => projectTableCell(cell, cache)),
304
+ ),
305
+ ...metadata,
306
+ };
307
+ break;
308
+ }
309
+ case 'thematicBreak':
310
+ projected = {type: 'hr', ...metadata};
311
+ break;
312
+ case 'image':
313
+ projected = {type: 'image', alt: node.alt, src: node.url, ...metadata};
314
+ break;
315
+ }
316
+ cache.set(node, projected);
317
+ return projected;
318
+ }
319
+
320
+ function projectInlineNodes(
321
+ nodes: ReadonlyArray<MarkdownAstPhrasingContent>,
322
+ cache: LegacyProjectionCache = new WeakMap(),
323
+ ): InlineNodeWithMath[] {
324
+ return nodes.map(node => projectInlineNode(node, cache));
325
+ }
326
+
327
+ function projectMarkdownRoot(
328
+ root: MarkdownAstRoot,
329
+ cache: LegacyProjectionCache = new WeakMap(),
330
+ ): BlockNodeWithMath[] {
331
+ return root.children.map(node => projectBlockNode(node, cache));
332
+ }
333
+
133
334
  // ---------------------------------------------------------------------------
134
335
  // Parse options
135
336
  // ---------------------------------------------------------------------------
@@ -201,7 +402,10 @@ type ResolvedOptions = {
201
402
  readonly linkDefs?: ReadonlyMap<string, string>;
202
403
  };
203
404
 
204
- const EMPTY_OPTS: ResolvedOptions = {sourceIds: undefined, autolink: undefined};
405
+ const EMPTY_OPTS: ResolvedOptions = {
406
+ sourceIds: undefined,
407
+ autolink: undefined,
408
+ };
205
409
 
206
410
  function resolveOptions(
207
411
  arg: ReadonlySet<string> | RuntimeParseOptions | undefined,
@@ -542,7 +746,7 @@ function matchReferenceLink(
542
746
  start: number,
543
747
  linkDefs: ReadonlyMap<string, string>,
544
748
  opts: ResolvedOptions,
545
- ): {node: InlineNodeWithMath; end: number} | null {
749
+ ): {node: MarkdownAstPhrasingContent; end: number} | null {
546
750
  const textClose = text.indexOf(']', start + 1);
547
751
  if (textClose === -1) {
548
752
  return null;
@@ -560,7 +764,11 @@ function matchReferenceLink(
560
764
  const href = linkDefs.get(normalizeLinkLabel(label));
561
765
  if (href != null && isSafeUrl(href)) {
562
766
  return {
563
- node: {type: 'link', href, children: parseInlineImpl(linkText, opts)},
767
+ node: {
768
+ type: 'link',
769
+ url: href,
770
+ children: parseInlineImpl(linkText, opts),
771
+ },
564
772
  end: labelClose + 1,
565
773
  };
566
774
  }
@@ -577,7 +785,7 @@ function matchReferenceLink(
577
785
  return null;
578
786
  }
579
787
  return {
580
- node: {type: 'link', href, children: parseInlineImpl(linkText, opts)},
788
+ node: {type: 'link', url: href, children: parseInlineImpl(linkText, opts)},
581
789
  end: textClose + 1,
582
790
  };
583
791
  }
@@ -587,7 +795,7 @@ function matchReferenceImage(
587
795
  text: string,
588
796
  start: number,
589
797
  linkDefs: ReadonlyMap<string, string>,
590
- ): {node: InlineNodeWithMath; end: number} | null {
798
+ ): {node: MarkdownAstPhrasingContent; end: number} | null {
591
799
  const altClose = text.indexOf(']', start + 2);
592
800
  if (altClose === -1) {
593
801
  return null;
@@ -600,7 +808,7 @@ function matchReferenceImage(
600
808
  const label = rawLabel === '' ? alt : rawLabel;
601
809
  const src = linkDefs.get(normalizeLinkLabel(label));
602
810
  if (src != null && isSafeUrl(src)) {
603
- return {node: {type: 'image', src, alt}, end: labelClose + 1};
811
+ return {node: {type: 'image', url: src, alt}, end: labelClose + 1};
604
812
  }
605
813
  // No match — fall back to a shortcut `![alt]`.
606
814
  }
@@ -612,7 +820,7 @@ function matchReferenceImage(
612
820
  if (src == null || !isSafeUrl(src)) {
613
821
  return null;
614
822
  }
615
- return {node: {type: 'image', src, alt}, end: altClose + 1};
823
+ return {node: {type: 'image', url: src, alt}, end: altClose + 1};
616
824
  }
617
825
 
618
826
  // ---------------------------------------------------------------------------
@@ -791,6 +999,14 @@ export function parseInline(
791
999
  text: string,
792
1000
  arg?: ReadonlySet<string> | RuntimeParseOptions,
793
1001
  ): InlineNodeWithMath[] {
1002
+ return projectInlineNodes(parseInlineAst(text, arg));
1003
+ }
1004
+
1005
+ /** @internal Canonical inline parse used by Markdown rendering. */
1006
+ export function parseInlineAst(
1007
+ text: string,
1008
+ arg?: ReadonlySet<string> | RuntimeParseOptions,
1009
+ ): MarkdownAstPhrasingContent[] {
794
1010
  return parseInlineEntry(text, resolveOptions(arg));
795
1011
  }
796
1012
 
@@ -806,7 +1022,7 @@ export function parseInline(
806
1022
  function parseInlineEntry(
807
1023
  text: string,
808
1024
  opts: ResolvedOptions,
809
- ): InlineNodeWithMath[] {
1025
+ ): MarkdownAstPhrasingContent[] {
810
1026
  const nodes = parseInlineImpl(text, opts);
811
1027
  return opts.autolink === 'gfm' ? transformAutolinks(nodes) : nodes;
812
1028
  }
@@ -814,14 +1030,14 @@ function parseInlineEntry(
814
1030
  function parseInlineImpl(
815
1031
  text: string,
816
1032
  opts: ResolvedOptions,
817
- ): InlineNodeWithMath[] {
818
- const nodes: InlineNodeWithMath[] = [];
1033
+ ): MarkdownAstPhrasingContent[] {
1034
+ const nodes: MarkdownAstPhrasingContent[] = [];
819
1035
  let i = 0;
820
1036
 
821
1037
  while (i < text.length) {
822
1038
  // --- Escape ---
823
1039
  if (text[i] === '\\' && i + 1 < text.length) {
824
- nodes.push({type: 'text', content: text[i + 1]});
1040
+ nodes.push({type: 'text', value: text[i + 1]});
825
1041
  i += 2;
826
1042
  continue;
827
1043
  }
@@ -832,7 +1048,10 @@ function parseInlineImpl(
832
1048
  const openIndex = i + tickCount;
833
1049
  const closeIndex = text.indexOf('`'.repeat(tickCount), openIndex);
834
1050
  if (closeIndex !== -1) {
835
- nodes.push({type: 'code', content: text.slice(openIndex, closeIndex)});
1051
+ nodes.push({
1052
+ type: 'inlineCode',
1053
+ value: text.slice(openIndex, closeIndex),
1054
+ });
836
1055
  i = closeIndex + tickCount;
837
1056
  continue;
838
1057
  }
@@ -842,7 +1061,10 @@ function parseInlineImpl(
842
1061
  if (opts.math && text[i] === '$') {
843
1062
  const closeIndex = findInlineMathEnd(text, i);
844
1063
  if (closeIndex !== -1) {
845
- nodes.push({type: 'math', value: text.slice(i + 1, closeIndex)});
1064
+ nodes.push({
1065
+ type: 'inlineMath',
1066
+ value: text.slice(i + 1, closeIndex),
1067
+ });
846
1068
  i = closeIndex + 1;
847
1069
  continue;
848
1070
  }
@@ -867,11 +1089,11 @@ function parseInlineImpl(
867
1089
  const src = text.slice(altClose + 2, srcClose);
868
1090
  if (!isSafeUrl(src)) {
869
1091
  // Dangerous scheme — emit as plain text.
870
- nodes.push({type: 'text', content: text.slice(i, srcClose + 1)});
1092
+ nodes.push({type: 'text', value: text.slice(i, srcClose + 1)});
871
1093
  } else {
872
1094
  nodes.push({
873
1095
  type: 'image',
874
- src,
1096
+ url: src,
875
1097
  alt: text.slice(i + 2, altClose),
876
1098
  });
877
1099
  }
@@ -910,11 +1132,11 @@ function parseInlineImpl(
910
1132
  const href = text.slice(textClose + 2, urlClose);
911
1133
  if (!isSafeUrl(href)) {
912
1134
  // Dangerous scheme — emit as plain text instead of a link.
913
- nodes.push({type: 'text', content: text.slice(i, urlClose + 1)});
1135
+ nodes.push({type: 'text', value: text.slice(i, urlClose + 1)});
914
1136
  } else {
915
1137
  nodes.push({
916
1138
  type: 'link',
917
- href,
1139
+ url: href,
918
1140
  children: parseInlineImpl(text.slice(i + 1, textClose), opts),
919
1141
  });
920
1142
  }
@@ -950,10 +1172,10 @@ function parseInlineImpl(
950
1172
  (!isUnderscore || !isWordChar(text[closeIndex + 3]))
951
1173
  ) {
952
1174
  nodes.push({
953
- type: 'bold',
1175
+ type: 'strong',
954
1176
  children: [
955
1177
  {
956
- type: 'italic',
1178
+ type: 'emphasis',
957
1179
  children: parseInlineImpl(text.slice(i + 3, closeIndex), opts),
958
1180
  },
959
1181
  ],
@@ -980,7 +1202,7 @@ function parseInlineImpl(
980
1202
  (!isUnderscore || !isWordChar(text[closeIndex + 2]))
981
1203
  ) {
982
1204
  nodes.push({
983
- type: 'bold',
1205
+ type: 'strong',
984
1206
  children: parseInlineImpl(text.slice(i + 2, closeIndex), opts),
985
1207
  });
986
1208
  i = closeIndex + 2;
@@ -994,7 +1216,7 @@ function parseInlineImpl(
994
1216
  const closeIndex = text.indexOf('~~', i + 2);
995
1217
  if (closeIndex !== -1) {
996
1218
  nodes.push({
997
- type: 'strikethrough',
1219
+ type: 'delete',
998
1220
  children: parseInlineImpl(text.slice(i + 2, closeIndex), opts),
999
1221
  });
1000
1222
  i = closeIndex + 2;
@@ -1015,7 +1237,7 @@ function parseInlineImpl(
1015
1237
  (!isUnderscore || !isWordChar(text[closeIndex + 1]))
1016
1238
  ) {
1017
1239
  nodes.push({
1018
- type: 'italic',
1240
+ type: 'emphasis',
1019
1241
  children: parseInlineImpl(text.slice(i + 1, closeIndex), opts),
1020
1242
  });
1021
1243
  i = closeIndex + 1;
@@ -1043,9 +1265,12 @@ function parseInlineImpl(
1043
1265
  if (trimmed.length > 0) {
1044
1266
  const last = nodes[nodes.length - 1];
1045
1267
  if (last?.type === 'text') {
1046
- last.content += trimmed;
1268
+ nodes[nodes.length - 1] = {
1269
+ ...last,
1270
+ value: last.value + trimmed,
1271
+ };
1047
1272
  } else {
1048
- nodes.push({type: 'text', content: trimmed});
1273
+ nodes.push({type: 'text', value: trimmed});
1049
1274
  }
1050
1275
  }
1051
1276
  nodes.push({type: 'break'});
@@ -1056,9 +1281,9 @@ function parseInlineImpl(
1056
1281
 
1057
1282
  const last = nodes[nodes.length - 1];
1058
1283
  if (last?.type === 'text') {
1059
- last.content += content;
1284
+ nodes[nodes.length - 1] = {...last, value: last.value + content};
1060
1285
  } else {
1061
- nodes.push({type: 'text', content});
1286
+ nodes.push({type: 'text', value: content});
1062
1287
  }
1063
1288
  i = end;
1064
1289
  }
@@ -1303,26 +1528,26 @@ function scanAutolinksInText(text: string): AutolinkMatch[] {
1303
1528
  * Split a text-node `content` string into a sequence of text + link nodes
1304
1529
  * based on autolink matches.
1305
1530
  */
1306
- function splitTextOnAutolinks(content: string): InlineNodeWithMath[] {
1531
+ function splitTextOnAutolinks(content: string): MarkdownAstPhrasingContent[] {
1307
1532
  const matches = scanAutolinksInText(content);
1308
1533
  if (matches.length === 0) {
1309
- return [{type: 'text', content}];
1534
+ return [{type: 'text', value: content}];
1310
1535
  }
1311
- const out: InlineNodeWithMath[] = [];
1536
+ const out: MarkdownAstPhrasingContent[] = [];
1312
1537
  let cursor = 0;
1313
1538
  for (const m of matches) {
1314
1539
  if (m.start > cursor) {
1315
- out.push({type: 'text', content: content.slice(cursor, m.start)});
1540
+ out.push({type: 'text', value: content.slice(cursor, m.start)});
1316
1541
  }
1317
1542
  out.push({
1318
1543
  type: 'link',
1319
- href: m.href,
1320
- children: [{type: 'text', content: m.display}],
1544
+ url: m.href,
1545
+ children: [{type: 'text', value: m.display}],
1321
1546
  });
1322
1547
  cursor = m.end;
1323
1548
  }
1324
1549
  if (cursor < content.length) {
1325
- out.push({type: 'text', content: content.slice(cursor)});
1550
+ out.push({type: 'text', value: content.slice(cursor)});
1326
1551
  }
1327
1552
  return out;
1328
1553
  }
@@ -1335,23 +1560,25 @@ function splitTextOnAutolinks(content: string): InlineNodeWithMath[] {
1335
1560
  * `image` alt text, `citation`, or `break`. Runs only on the outermost
1336
1561
  * block's inline tree (see `parseInlineEntry`).
1337
1562
  */
1338
- function transformAutolinks(nodes: InlineNodeWithMath[]): InlineNodeWithMath[] {
1339
- const out: InlineNodeWithMath[] = [];
1563
+ function transformAutolinks(
1564
+ nodes: ReadonlyArray<MarkdownAstPhrasingContent>,
1565
+ ): MarkdownAstPhrasingContent[] {
1566
+ const out: MarkdownAstPhrasingContent[] = [];
1340
1567
  for (const node of nodes) {
1341
1568
  if (node.type === 'text') {
1342
- const split = splitTextOnAutolinks(node.content);
1569
+ const split = splitTextOnAutolinks(node.value);
1343
1570
  for (const seg of split) {
1344
1571
  const last = out[out.length - 1];
1345
1572
  if (seg.type === 'text' && last?.type === 'text') {
1346
- last.content += seg.content;
1573
+ out[out.length - 1] = {...last, value: last.value + seg.value};
1347
1574
  } else {
1348
1575
  out.push(seg);
1349
1576
  }
1350
1577
  }
1351
1578
  } else if (
1352
- node.type === 'bold' ||
1353
- node.type === 'italic' ||
1354
- node.type === 'strikethrough'
1579
+ node.type === 'strong' ||
1580
+ node.type === 'emphasis' ||
1581
+ node.type === 'delete'
1355
1582
  ) {
1356
1583
  out.push({...node, children: transformAutolinks(node.children)});
1357
1584
  } else {
@@ -1510,9 +1737,9 @@ function parseTable(
1510
1737
  lines: string[],
1511
1738
  lineIndex: number,
1512
1739
  opts: ResolvedOptions,
1513
- ): {node: BlockNodeWithMath; nextIndex: number} {
1514
- const headers: TableCellNodeWithMath[] = splitTableRow(lines[lineIndex]).map(
1515
- cell => ({children: parseInlineEntry(cell, opts)}),
1740
+ ): {node: MarkdownAstBlockContent; nextIndex: number} {
1741
+ const headers: MarkdownAstTableCell[] = splitTableRow(lines[lineIndex]).map(
1742
+ cell => ({type: 'tableCell', children: parseInlineEntry(cell, opts)}),
1516
1743
  );
1517
1744
  const alignments: TableAlignment[] = splitTableRow(lines[lineIndex + 1]).map(
1518
1745
  cell => {
@@ -1528,22 +1755,28 @@ function parseTable(
1528
1755
  : null;
1529
1756
  },
1530
1757
  );
1531
- const rows: TableCellNodeWithMath[][] = [];
1758
+ const rows: MarkdownAstTableRow[] = [];
1532
1759
  let rowIndex = lineIndex + 2;
1533
1760
  while (
1534
1761
  rowIndex < lines.length &&
1535
1762
  lines[rowIndex].includes('|') &&
1536
1763
  lines[rowIndex].trim() !== ''
1537
1764
  ) {
1538
- rows.push(
1539
- splitTableRow(lines[rowIndex]).map(cell => ({
1765
+ rows.push({
1766
+ type: 'tableRow',
1767
+ children: splitTableRow(lines[rowIndex]).map(cell => ({
1768
+ type: 'tableCell',
1540
1769
  children: parseInlineEntry(cell, opts),
1541
1770
  })),
1542
- );
1771
+ });
1543
1772
  rowIndex++;
1544
1773
  }
1545
1774
  return {
1546
- node: {type: 'table', headers, alignments, rows},
1775
+ node: {
1776
+ type: 'table',
1777
+ align: alignments,
1778
+ children: [{type: 'tableRow', children: headers}, ...rows],
1779
+ },
1547
1780
  nextIndex: rowIndex,
1548
1781
  };
1549
1782
  }
@@ -1553,8 +1786,8 @@ function parseList(
1553
1786
  startIndex: number,
1554
1787
  ordered: boolean,
1555
1788
  opts: ResolvedOptions,
1556
- ): {node: BlockNodeWithMath; nextIndex: number} {
1557
- const items: ListItemNodeWithMath[] = [];
1789
+ ): {node: MarkdownAstBlockContent; nextIndex: number} {
1790
+ const items: MarkdownAstListItem[] = [];
1558
1791
  const baseIndent = getIndent(lines[startIndex]);
1559
1792
  // Ordered lists may use either '.' or ')' as the marker delimiter
1560
1793
  // (CommonMark 5.2). Capture which one this list starts with so its items
@@ -1608,7 +1841,11 @@ function parseList(
1608
1841
  itemText += '\n' + deindented.join('\n');
1609
1842
  }
1610
1843
 
1611
- items.push({checked, children: parseMarkdownImpl(itemText, nested(opts))});
1844
+ items.push({
1845
+ type: 'listItem',
1846
+ checked,
1847
+ children: parseMarkdownImpl(itemText, nested(opts)),
1848
+ });
1612
1849
 
1613
1850
  // CommonMark loose list: blank line(s) between items of the same style
1614
1851
  // and indent still form one list. Skip the blanks and continue if the
@@ -1632,8 +1869,8 @@ function parseList(
1632
1869
  ordered,
1633
1870
  start,
1634
1871
  delimiter: ordered ? (delim as '.' | ')') : undefined,
1635
- loose: loose || undefined,
1636
- items,
1872
+ spread: loose || undefined,
1873
+ children: items,
1637
1874
  },
1638
1875
  nextIndex: index,
1639
1876
  };
@@ -1659,13 +1896,24 @@ export function parseMarkdown(
1659
1896
  input: string,
1660
1897
  arg?: ReadonlySet<string> | RuntimeParseOptions,
1661
1898
  ): BlockNodeWithMath[] {
1662
- return parseMarkdownImpl(input, resolveOptions(arg));
1899
+ return projectMarkdownRoot(parseMarkdownAst(input, arg));
1900
+ }
1901
+
1902
+ /** @internal Canonical block parse used by Markdown and Outline rendering. */
1903
+ export function parseMarkdownAst(
1904
+ input: string,
1905
+ arg?: ReadonlySet<string> | RuntimeParseOptions,
1906
+ ): MarkdownAstRoot {
1907
+ return {
1908
+ type: 'root',
1909
+ children: parseMarkdownImpl(input, resolveOptions(arg)),
1910
+ };
1663
1911
  }
1664
1912
 
1665
1913
  function parseMarkdownImpl(
1666
1914
  input: string,
1667
1915
  baseOpts: ResolvedOptions,
1668
- ): BlockNodeWithMath[] {
1916
+ ): MarkdownAstBlockContent[] {
1669
1917
  // Collect this input's link reference definitions and strip their lines,
1670
1918
  // then merge them with any definitions inherited from an enclosing parse
1671
1919
  // (the incremental parser passes the whole document's definitions in; a
@@ -1686,7 +1934,7 @@ function parseMarkdownImpl(
1686
1934
  const opts: ResolvedOptions =
1687
1935
  linkDefs != null ? {...baseOpts, linkDefs} : baseOpts;
1688
1936
  const lines = cleaned.split('\n');
1689
- const blocks: BlockNodeWithMath[] = [];
1937
+ const blocks: MarkdownAstBlockContent[] = [];
1690
1938
  // The line each block started on, parallel to `blocks`. Only collected when
1691
1939
  // ranges were asked for; a block's end is resolved after the loop, since the
1692
1940
  // branch that produced it has already moved `index` past whatever it read.
@@ -1697,7 +1945,7 @@ function parseMarkdownImpl(
1697
1945
  ? []
1698
1946
  : null;
1699
1947
  let blockStartLine = 0;
1700
- const pushBlock = (node: BlockNodeWithMath, endLine?: number) => {
1948
+ const pushBlock = (node: MarkdownAstBlockContent, endLine?: number) => {
1701
1949
  blocks.push(node);
1702
1950
  blockStartLines?.push(blockStartLine);
1703
1951
  blockEndLines?.push(endLine);
@@ -1716,7 +1964,7 @@ function parseMarkdownImpl(
1716
1964
  const fenceMatch = line.match(/^(`{3,}|~{3,})(\w*)/);
1717
1965
  if (fenceMatch) {
1718
1966
  const fence = fenceMatch[1];
1719
- const language = fenceMatch[2] || 'plaintext';
1967
+ const language = fenceMatch[2] || null;
1720
1968
  const codeLines: string[] = [];
1721
1969
  index++;
1722
1970
  while (index < lines.length && !lines[index].startsWith(fence)) {
@@ -1728,7 +1976,7 @@ function parseMarkdownImpl(
1728
1976
  // can end on them, so it states its own end rather than letting the
1729
1977
  // positional derivation trim them off.
1730
1978
  pushBlock(
1731
- {type: 'codeblock', language, content: codeLines.join('\n')},
1979
+ {type: 'code', lang: language, value: codeLines.join('\n')},
1732
1980
  Math.min(index, lines.length) - 1,
1733
1981
  );
1734
1982
  continue;
@@ -1752,7 +2000,7 @@ function parseMarkdownImpl(
1752
2000
  if (headingMatch) {
1753
2001
  pushBlock({
1754
2002
  type: 'heading',
1755
- level: headingMatch[1].length as 1 | 2 | 3 | 4 | 5 | 6,
2003
+ depth: headingMatch[1].length as 1 | 2 | 3 | 4 | 5 | 6,
1756
2004
  children: parseInlineEntry(headingMatch[2], opts),
1757
2005
  });
1758
2006
  index++;
@@ -1761,7 +2009,7 @@ function parseMarkdownImpl(
1761
2009
 
1762
2010
  // --- HR (must precede list check to handle `- - -`, `* * *`, `_ _ _`) ---
1763
2011
  if (isHorizontalRule(line)) {
1764
- pushBlock({type: 'hr'});
2012
+ pushBlock({type: 'thematicBreak'});
1765
2013
  index++;
1766
2014
  continue;
1767
2015
  }
@@ -1775,7 +2023,7 @@ function parseMarkdownImpl(
1775
2023
  line.trim() === imageMatch[0] &&
1776
2024
  isSafeUrl(imageMatch[2])
1777
2025
  ) {
1778
- pushBlock({type: 'image', alt: imageMatch[1], src: imageMatch[2]});
2026
+ pushBlock({type: 'image', alt: imageMatch[1], url: imageMatch[2]});
1779
2027
  index++;
1780
2028
  continue;
1781
2029
  }
@@ -1866,7 +2114,7 @@ function parseMarkdownImpl(
1866
2114
  * `lineMap` says which input line each surviving line came from.
1867
2115
  */
1868
2116
  function stampSourceRanges(
1869
- blocks: BlockNodeWithMath[],
2117
+ blocks: MarkdownAstBlockContent[],
1870
2118
  blockStartLines: number[],
1871
2119
  blockEndLines: (number | undefined)[],
1872
2120
  lines: string[],
@@ -1903,7 +2151,13 @@ function stampSourceRanges(
1903
2151
  // range that dropped it would slice to something that re-parses
1904
2152
  // differently.
1905
2153
  const end = lineStart(endLine) + lines[endLine].length;
1906
- blocks[i] = {...blocks[i], range: {start: lineStart(startLine), end}};
2154
+ blocks[i] = {
2155
+ ...blocks[i],
2156
+ position: {
2157
+ start: {offset: lineStart(startLine)},
2158
+ end: {offset: end},
2159
+ },
2160
+ };
1907
2161
  }
1908
2162
  }
1909
2163
 
@@ -1968,6 +2222,13 @@ type IncrementalCache = {
1968
2222
  /** The effective document-global definitions used by slice parses. */
1969
2223
  linkDefs: ReadonlyMap<string, string>;
1970
2224
  linkDefsKey: string;
2225
+ /** Canonical settled blocks shared by rendering and compatibility projection. */
2226
+ settledAstBlocks: MarkdownAstBlockContent[];
2227
+ settledRevision: number;
2228
+ projectedRevision: number;
2229
+ projectedSettledBlocks: BlockNodeWithMath[];
2230
+ /** Preserves released settled-node identity across projected snapshots. */
2231
+ projectionCache: LegacyProjectionCache;
1971
2232
  work: IncrementalWork;
1972
2233
  };
1973
2234
 
@@ -1986,6 +2247,11 @@ function makeIncrementalCache(
1986
2247
  tailLinkDefs: new Map(),
1987
2248
  linkDefs: defs,
1988
2249
  linkDefsKey: linkDefsSignature(defs),
2250
+ settledAstBlocks: [],
2251
+ settledRevision: 0,
2252
+ projectedRevision: -1,
2253
+ projectedSettledBlocks: [],
2254
+ projectionCache: new WeakMap(),
1989
2255
  work: {
1990
2256
  splitCharacters: 0,
1991
2257
  boundaryLines: 0,
@@ -2481,9 +2747,9 @@ function atOffset(opts: ResolvedOptions, offset: number): ResolvedOptions {
2481
2747
  * lists even though the full-text parser joins them per CommonMark §5.3.
2482
2748
  */
2483
2749
  function mergeSettledBlocks(
2484
- prev: BlockNodeWithMath[],
2485
- delta: BlockNodeWithMath[],
2486
- ): BlockNodeWithMath[] {
2750
+ prev: MarkdownAstBlockContent[],
2751
+ delta: MarkdownAstBlockContent[],
2752
+ ): MarkdownAstBlockContent[] {
2487
2753
  if (prev.length === 0 || delta.length === 0) {
2488
2754
  return [...prev, ...delta];
2489
2755
  }
@@ -2495,17 +2761,21 @@ function mergeSettledBlocks(
2495
2761
  prevLast.ordered === deltaFirst.ordered &&
2496
2762
  prevLast.delimiter === deltaFirst.delimiter
2497
2763
  ) {
2498
- const merged: BlockNodeWithMath = {
2764
+ const merged: MarkdownAstBlockContent = {
2499
2765
  type: 'list',
2500
2766
  ordered: prevLast.ordered,
2501
2767
  start: prevLast.start,
2502
2768
  delimiter: prevLast.delimiter,
2503
- loose: true,
2504
- items: [...prevLast.items, ...deltaFirst.items],
2505
- // One list now, so one range: from where the first half started to
2506
- // where the second half ended.
2507
- ...(prevLast.range != null && deltaFirst.range != null
2508
- ? {range: {start: prevLast.range.start, end: deltaFirst.range.end}}
2769
+ spread: true,
2770
+ children: [...prevLast.children, ...deltaFirst.children],
2771
+ // One list now, so one position spans both halves.
2772
+ ...(prevLast.position != null && deltaFirst.position != null
2773
+ ? {
2774
+ position: {
2775
+ start: prevLast.position.start,
2776
+ end: deltaFirst.position.end,
2777
+ },
2778
+ }
2509
2779
  : null),
2510
2780
  };
2511
2781
  return [...prev.slice(0, -1), merged, ...delta.slice(1)];
@@ -2515,15 +2785,15 @@ function mergeSettledBlocks(
2515
2785
 
2516
2786
  /** Append a newly-settled slice without copying the already-settled prefix. */
2517
2787
  function appendSettledBlocks(
2518
- prev: BlockNodeWithMath[],
2519
- delta: BlockNodeWithMath[],
2520
- ): void {
2788
+ prev: MarkdownAstBlockContent[],
2789
+ delta: MarkdownAstBlockContent[],
2790
+ ): boolean {
2521
2791
  if (delta.length === 0) {
2522
- return;
2792
+ return false;
2523
2793
  }
2524
2794
  if (prev.length === 0) {
2525
2795
  prev.push(...delta);
2526
- return;
2796
+ return false;
2527
2797
  }
2528
2798
  const prevLast = prev[prev.length - 1];
2529
2799
  const deltaFirst = delta[0];
@@ -2538,16 +2808,22 @@ function appendSettledBlocks(
2538
2808
  ordered: prevLast.ordered,
2539
2809
  start: prevLast.start,
2540
2810
  delimiter: prevLast.delimiter,
2541
- loose: true,
2542
- items: [...prevLast.items, ...deltaFirst.items],
2543
- ...(prevLast.range != null && deltaFirst.range != null
2544
- ? {range: {start: prevLast.range.start, end: deltaFirst.range.end}}
2811
+ spread: true,
2812
+ children: [...prevLast.children, ...deltaFirst.children],
2813
+ ...(prevLast.position != null && deltaFirst.position != null
2814
+ ? {
2815
+ position: {
2816
+ start: prevLast.position.start,
2817
+ end: deltaFirst.position.end,
2818
+ },
2819
+ }
2545
2820
  : null),
2546
2821
  };
2547
2822
  prev.push(...delta.slice(1));
2548
- return;
2823
+ return true;
2549
2824
  }
2550
2825
  prev.push(...delta);
2826
+ return false;
2551
2827
  }
2552
2828
 
2553
2829
  function sameUnsettledDefinitions(
@@ -2583,6 +2859,11 @@ function resetIncrementalCache(
2583
2859
  cache.tailLinkDefs = new Map();
2584
2860
  cache.linkDefs = new Map();
2585
2861
  cache.linkDefsKey = '';
2862
+ cache.settledAstBlocks = [];
2863
+ cache.settledRevision++;
2864
+ cache.projectedRevision = -1;
2865
+ cache.projectedSettledBlocks = [];
2866
+ cache.projectionCache = new WeakMap();
2586
2867
  cache.work = {
2587
2868
  splitCharacters: 0,
2588
2869
  boundaryLines: 0,
@@ -2622,6 +2903,36 @@ export function parseMarkdownIncremental(
2622
2903
  state: IncrementalState<boolean>,
2623
2904
  arg?: ReadonlySet<string> | RuntimeParseOptions,
2624
2905
  ): BlockNodeWithMath[] {
2906
+ const root = parseMarkdownAstIncremental(input, state, arg);
2907
+ const cache = incrementalCaches.get(state) ?? makeIncrementalCache(state);
2908
+ const projected = projectMarkdownRoot(root, cache.projectionCache);
2909
+ if (cache.projectedRevision !== cache.settledRevision) {
2910
+ cache.projectedSettledBlocks = cache.settledAstBlocks.map(block =>
2911
+ projectBlockNode(block, cache.projectionCache),
2912
+ );
2913
+ cache.projectedRevision = cache.settledRevision;
2914
+ }
2915
+ state.settledBlocks = cache.projectedSettledBlocks;
2916
+ return projected;
2917
+ }
2918
+
2919
+ /** @internal Canonical incremental parse used by Markdown rendering. */
2920
+ export function parseMarkdownAstIncremental(
2921
+ input: string,
2922
+ state: IncrementalState<boolean>,
2923
+ arg?: ReadonlySet<string> | RuntimeParseOptions,
2924
+ ): MarkdownAstRoot {
2925
+ return {
2926
+ type: 'root',
2927
+ children: parseMarkdownIncrementalAstBlocks(input, state, arg),
2928
+ };
2929
+ }
2930
+
2931
+ function parseMarkdownIncrementalAstBlocks(
2932
+ input: string,
2933
+ state: IncrementalState<boolean>,
2934
+ arg?: ReadonlySet<string> | RuntimeParseOptions,
2935
+ ): MarkdownAstBlockContent[] {
2625
2936
  const opts = resolveOptions(arg);
2626
2937
  const cache = incrementalCaches.get(state) ?? makeIncrementalCache(state);
2627
2938
  let reparseSettled = false;
@@ -2746,17 +3057,39 @@ export function parseMarkdownIncremental(
2746
3057
 
2747
3058
  let parsedSettledBlocks = 0;
2748
3059
  if (reparseSettled) {
2749
- state.settledBlocks = state.settledText
3060
+ cache.settledAstBlocks = state.settledText
2750
3061
  ? parseMarkdownImpl(state.settledText, parseOpts)
2751
3062
  : [];
2752
- parsedSettledBlocks = state.settledBlocks.length;
3063
+ parsedSettledBlocks = cache.settledAstBlocks.length;
3064
+ cache.settledRevision++;
3065
+ cache.projectedSettledBlocks = cache.settledAstBlocks.map(block =>
3066
+ projectBlockNode(block, cache.projectionCache),
3067
+ );
3068
+ cache.projectedRevision = cache.settledRevision;
2753
3069
  } else if (settledDelta !== '') {
2754
3070
  const deltaBlocks = parseMarkdownImpl(
2755
3071
  settledDelta,
2756
3072
  atOffset(parseOpts, oldSettledEnd),
2757
3073
  );
2758
- appendSettledBlocks(state.settledBlocks, deltaBlocks);
3074
+ const mergeIndex = cache.settledAstBlocks.length - 1;
3075
+ const mergedList = appendSettledBlocks(cache.settledAstBlocks, deltaBlocks);
2759
3076
  parsedSettledBlocks = deltaBlocks.length;
3077
+ cache.settledRevision++;
3078
+ const projectedDelta = deltaBlocks.map(block =>
3079
+ projectBlockNode(block, cache.projectionCache),
3080
+ );
3081
+ if (mergedList) {
3082
+ const canonicalMerged = cache.settledAstBlocks[mergeIndex];
3083
+ const projectedMerged = projectBlockNode(
3084
+ canonicalMerged,
3085
+ cache.projectionCache,
3086
+ );
3087
+ cache.projectedSettledBlocks[mergeIndex] = projectedMerged;
3088
+ cache.projectedSettledBlocks.push(...projectedDelta.slice(1));
3089
+ } else {
3090
+ cache.projectedSettledBlocks.push(...projectedDelta);
3091
+ }
3092
+ cache.projectedRevision = cache.settledRevision;
2760
3093
  }
2761
3094
 
2762
3095
  // The unsettled tail is trimmed before parsing, so its offset in the
@@ -2790,7 +3123,7 @@ export function parseMarkdownIncremental(
2790
3123
  definitionCharacters: settledDelta.length + unsettledInput.length,
2791
3124
  renderedBlocks: parsedSettledBlocks + unsettledBlocks.length,
2792
3125
  };
2793
- return mergeSettledBlocks(state.settledBlocks, unsettledBlocks);
3126
+ return mergeSettledBlocks(cache.settledAstBlocks, unsettledBlocks);
2794
3127
  }
2795
3128
 
2796
3129
  // ---------------------------------------------------------------------------
@@ -2801,30 +3134,6 @@ export function parseMarkdownIncremental(
2801
3134
  // derives its item ids from the same functions, so outline hash links always
2802
3135
  // resolve to a rendered heading by construction.
2803
3136
 
2804
- /** Flatten inline nodes into their plain text content. */
2805
- export function inlineText(nodes: ReadonlyArray<InlineNodeWithMath>): string {
2806
- return nodes
2807
- .map(node => {
2808
- switch (node.type) {
2809
- case 'text':
2810
- case 'code':
2811
- case 'math':
2812
- return node.type === 'math' ? node.value : node.content;
2813
- case 'bold':
2814
- case 'italic':
2815
- case 'strikethrough':
2816
- case 'link':
2817
- return inlineText(node.children);
2818
- case 'image':
2819
- return node.alt;
2820
- case 'citation':
2821
- case 'break':
2822
- return '';
2823
- }
2824
- })
2825
- .join('');
2826
- }
2827
-
2828
3137
  /** Turn heading text into a URL-safe slug (lowercase, hyphen-separated). */
2829
3138
  export function slugify(value: string): string {
2830
3139
  return value