@astryxdesign/core 0.6.2 → 0.6.3-canary.0788c49

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 (558) hide show
  1. package/CHANGELOG.md +81 -0
  2. package/dist/BottomSheet/BottomSheet.d.ts +1 -1
  3. package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
  4. package/dist/BottomSheet/BottomSheet.js +3 -1
  5. package/dist/BottomSheet/BottomSheetPanel.d.ts +7 -5
  6. package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
  7. package/dist/BottomSheet/BottomSheetPanel.js +56 -19
  8. package/dist/Button/Button.d.ts.map +1 -1
  9. package/dist/Button/Button.js +13 -28
  10. package/dist/Chat/ChatComposer.d.ts.map +1 -1
  11. package/dist/Chat/ChatComposer.js +10 -5
  12. package/dist/Chat/ChatComposerDrawer.d.ts +19 -5
  13. package/dist/Chat/ChatComposerDrawer.d.ts.map +1 -1
  14. package/dist/Chat/ChatComposerDrawer.js +48 -23
  15. package/dist/Chat/ChatComposerInput.d.ts +11 -3
  16. package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
  17. package/dist/Chat/ChatComposerInput.js +63 -16
  18. package/dist/Chat/ChatDictationButton.d.ts +3 -2
  19. package/dist/Chat/ChatDictationButton.d.ts.map +1 -1
  20. package/dist/Chat/ChatDictationButton.js +6 -4
  21. package/dist/Chat/ChatLayout.d.ts +19 -0
  22. package/dist/Chat/ChatLayout.d.ts.map +1 -1
  23. package/dist/Chat/ChatLayout.js +19 -0
  24. package/dist/Chat/ChatLayoutScrollButton.d.ts.map +1 -1
  25. package/dist/Chat/ChatLayoutScrollButton.js +44 -34
  26. package/dist/Chat/ChatMessageBubble.d.ts +2 -0
  27. package/dist/Chat/ChatMessageBubble.d.ts.map +1 -1
  28. package/dist/Chat/ChatMessageBubble.js +5 -3
  29. package/dist/Chat/ChatMessageList.d.ts +2 -1
  30. package/dist/Chat/ChatMessageList.d.ts.map +1 -1
  31. package/dist/Chat/ChatMessageList.js +4 -3
  32. package/dist/Chat/ChatMessageMetadata.d.ts +5 -4
  33. package/dist/Chat/ChatMessageMetadata.d.ts.map +1 -1
  34. package/dist/Chat/ChatMessageMetadata.js +12 -9
  35. package/dist/Chat/ChatSendButton.d.ts +2 -1
  36. package/dist/Chat/ChatSendButton.d.ts.map +1 -1
  37. package/dist/Chat/ChatSendButton.js +6 -4
  38. package/dist/Chat/index.d.ts +1 -1
  39. package/dist/Chat/index.d.ts.map +1 -1
  40. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  41. package/dist/CheckboxInput/CheckboxInput.js +12 -2
  42. package/dist/Citation/Citation.d.ts +1 -1
  43. package/dist/Citation/Citation.d.ts.map +1 -1
  44. package/dist/Citation/Citation.js +3 -2
  45. package/dist/CodeBlock/highlightRanges.d.ts.map +1 -1
  46. package/dist/CodeBlock/highlightRanges.js +14 -3
  47. package/dist/Collapsible/Collapsible.d.ts.map +1 -1
  48. package/dist/Collapsible/Collapsible.js +6 -1
  49. package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
  50. package/dist/CommandPalette/CommandPaletteInput.js +1 -1
  51. package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
  52. package/dist/ComplexSelector/ComplexSelector.js +1 -1
  53. package/dist/DateInput/DateInput.d.ts.map +1 -1
  54. package/dist/DateInput/DateInput.js +4 -4
  55. package/dist/DateInput/NativeDateField.d.ts.map +1 -1
  56. package/dist/DateInput/NativeDateField.js +16 -16
  57. package/dist/DateInput/TouchDateField.d.ts.map +1 -1
  58. package/dist/DateInput/TouchDateField.js +2 -2
  59. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  60. package/dist/DateRangeInput/DateRangeInput.js +4 -4
  61. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  62. package/dist/DateTimeInput/DateTimeInput.js +8 -8
  63. package/dist/DateTimeInput/TouchDateTimeField.d.ts.map +1 -1
  64. package/dist/DateTimeInput/TouchDateTimeField.js +4 -4
  65. package/dist/DateTimeInput/nativePickerSegmentStyles.d.ts +9 -3
  66. package/dist/DateTimeInput/nativePickerSegmentStyles.d.ts.map +1 -1
  67. package/dist/DateTimeInput/nativePickerSegmentStyles.js +3 -3
  68. package/dist/Dialog/DialogHeader.d.ts +8 -1
  69. package/dist/Dialog/DialogHeader.d.ts.map +1 -1
  70. package/dist/Dialog/DialogHeader.js +13 -4
  71. package/dist/Field/InputClearButton.d.ts +4 -2
  72. package/dist/Field/InputClearButton.d.ts.map +1 -1
  73. package/dist/Field/InputClearButton.js +4 -2
  74. package/dist/Field/PanelSearchInput.d.ts.map +1 -1
  75. package/dist/Field/PanelSearchInput.js +1 -1
  76. package/dist/FileInput/FileInput.d.ts.map +1 -1
  77. package/dist/FileInput/FileInput.js +7 -7
  78. package/dist/Icon/Icon.d.ts +6 -31
  79. package/dist/Icon/Icon.d.ts.map +1 -1
  80. package/dist/Icon/Icon.js +8 -71
  81. package/dist/Icon/IconDefaultSizeContext.d.ts +4 -0
  82. package/dist/Icon/IconDefaultSizeContext.d.ts.map +1 -0
  83. package/dist/Icon/IconDefaultSizeContext.js +18 -0
  84. package/dist/Icon/IconSize.stylex.d.ts +65 -0
  85. package/dist/Icon/IconSize.stylex.d.ts.map +1 -0
  86. package/dist/Icon/IconSize.stylex.js +75 -0
  87. package/dist/Item/Item.d.ts +1 -1
  88. package/dist/Item/Item.d.ts.map +1 -1
  89. package/dist/Item/Item.js +4 -3
  90. package/dist/Kbd/Kbd.d.ts +5 -3
  91. package/dist/Kbd/Kbd.d.ts.map +1 -1
  92. package/dist/Kbd/Kbd.js +36 -42
  93. package/dist/Link/Link.d.ts.map +1 -1
  94. package/dist/Link/Link.js +6 -2
  95. package/dist/Link/useLinkComponent.d.ts +5 -15
  96. package/dist/Link/useLinkComponent.d.ts.map +1 -1
  97. package/dist/Link/useLinkComponent.js +34 -51
  98. package/dist/List/List.d.ts +19 -1
  99. package/dist/List/List.d.ts.map +1 -1
  100. package/dist/List/List.js +9 -2
  101. package/dist/List/ListContext.d.ts +1 -0
  102. package/dist/List/ListContext.d.ts.map +1 -1
  103. package/dist/List/ListContext.js +2 -1
  104. package/dist/List/ListItem.d.ts +1 -1
  105. package/dist/List/ListItem.d.ts.map +1 -1
  106. package/dist/List/ListItem.js +9 -2
  107. package/dist/Markdown/Markdown.d.ts +8 -2
  108. package/dist/Markdown/Markdown.d.ts.map +1 -1
  109. package/dist/Markdown/Markdown.fr23.bench.d.ts +2 -0
  110. package/dist/Markdown/Markdown.fr23.bench.d.ts.map +1 -0
  111. package/dist/Markdown/Markdown.fr23.bench.js +150 -0
  112. package/dist/Markdown/Markdown.fr23.sampling.d.ts +2 -0
  113. package/dist/Markdown/Markdown.fr23.sampling.d.ts.map +1 -0
  114. package/dist/Markdown/Markdown.fr23.sampling.js +43 -0
  115. package/dist/Markdown/Markdown.js +262 -109
  116. package/dist/Markdown/ast.d.ts +162 -0
  117. package/dist/Markdown/ast.d.ts.map +1 -0
  118. package/dist/Markdown/ast.js +74 -0
  119. package/dist/Markdown/index.d.ts +5 -3
  120. package/dist/Markdown/index.d.ts.map +1 -1
  121. package/dist/Markdown/index.js +3 -2
  122. package/dist/Markdown/parser/index.d.ts +10 -0
  123. package/dist/Markdown/parser/index.d.ts.map +1 -0
  124. package/dist/Markdown/parser/index.js +10 -0
  125. package/dist/Markdown/parser.d.ts +126 -58
  126. package/dist/Markdown/parser.d.ts.map +1 -1
  127. package/dist/Markdown/parser.js +726 -177
  128. package/dist/Markdown/plugins/frontmatter.d.ts +32 -0
  129. package/dist/Markdown/plugins/frontmatter.d.ts.map +1 -0
  130. package/dist/Markdown/plugins/frontmatter.js +132 -0
  131. package/dist/Markdown/plugins/index.d.ts +17 -0
  132. package/dist/Markdown/plugins/index.d.ts.map +1 -0
  133. package/dist/Markdown/plugins/index.js +14 -0
  134. package/dist/Markdown/plugins/protocol.d.ts +191 -0
  135. package/dist/Markdown/plugins/protocol.d.ts.map +1 -0
  136. package/dist/Markdown/plugins/protocol.js +957 -0
  137. package/dist/Markdown/plugins/semanticFence.d.ts +36 -0
  138. package/dist/Markdown/plugins/semanticFence.d.ts.map +1 -0
  139. package/dist/Markdown/plugins/semanticFence.js +213 -0
  140. package/dist/Markdown/plugins/softBreaks.d.ts +3 -0
  141. package/dist/Markdown/plugins/softBreaks.d.ts.map +1 -0
  142. package/dist/Markdown/plugins/softBreaks.js +172 -0
  143. package/dist/Markdown/plugins/sourceDecoration.d.ts +76 -0
  144. package/dist/Markdown/plugins/sourceDecoration.d.ts.map +1 -0
  145. package/dist/Markdown/plugins/sourceDecoration.js +393 -0
  146. package/dist/Markdown/plugins/textTransform.d.ts +30 -0
  147. package/dist/Markdown/plugins/textTransform.d.ts.map +1 -0
  148. package/dist/Markdown/plugins/textTransform.js +374 -0
  149. package/dist/Markdown/remark.d.ts +196 -0
  150. package/dist/Markdown/remark.d.ts.map +1 -0
  151. package/dist/Markdown/remark.js +1062 -0
  152. package/dist/Markdown/url.d.ts +6 -0
  153. package/dist/Markdown/url.d.ts.map +1 -0
  154. package/dist/Markdown/url.js +31 -0
  155. package/dist/Markdown/utils.d.ts +4 -2
  156. package/dist/Markdown/utils.d.ts.map +1 -1
  157. package/dist/Markdown/utils.js +2 -1
  158. package/dist/MetadataList/MetadataList.js +3 -3
  159. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  160. package/dist/NumberInput/NumberInput.js +4 -4
  161. package/dist/Outline/index.d.ts +1 -0
  162. package/dist/Outline/index.d.ts.map +1 -1
  163. package/dist/Outline/parseOutlineFromMarkdown.d.ts +7 -1
  164. package/dist/Outline/parseOutlineFromMarkdown.d.ts.map +1 -1
  165. package/dist/Outline/parseOutlineFromMarkdown.js +14 -8
  166. package/dist/Outline/useOutlineFromMarkdown.d.ts +3 -1
  167. package/dist/Outline/useOutlineFromMarkdown.d.ts.map +1 -1
  168. package/dist/Outline/useOutlineFromMarkdown.js +8 -3
  169. package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
  170. package/dist/PowerSearch/PowerSearchEditPopover.js +46 -30
  171. package/dist/PowerSearch/PowerSearchValueEditor.d.ts.map +1 -1
  172. package/dist/PowerSearch/PowerSearchValueEditor.js +5 -0
  173. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  174. package/dist/ProgressBar/ProgressBar.js +36 -31
  175. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  176. package/dist/RadioList/RadioListItem.js +13 -1
  177. package/dist/ScrollableArea/ScrollableArea.d.ts +1 -0
  178. package/dist/ScrollableArea/ScrollableArea.d.ts.map +1 -1
  179. package/dist/ScrollableArea/ScrollableArea.js +1 -0
  180. package/dist/SegmentedControl/SegmentedControl.d.ts +1 -1
  181. package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
  182. package/dist/SegmentedControl/SegmentedControl.js +1 -0
  183. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  184. package/dist/SegmentedControl/SegmentedControlItem.js +5 -5
  185. package/dist/Selector/Selector.d.ts +9 -7
  186. package/dist/Selector/Selector.d.ts.map +1 -1
  187. package/dist/Selector/Selector.js +16 -7
  188. package/dist/SideNav/SideNav.d.ts +2 -1
  189. package/dist/SideNav/SideNav.d.ts.map +1 -1
  190. package/dist/SideNav/SideNav.js +9 -3
  191. package/dist/Slider/Slider.d.ts.map +1 -1
  192. package/dist/Slider/Slider.js +28 -12
  193. package/dist/Spinner/Spinner.d.ts.map +1 -1
  194. package/dist/Spinner/Spinner.js +23 -7
  195. package/dist/Switch/Switch.d.ts.map +1 -1
  196. package/dist/Switch/Switch.js +11 -0
  197. package/dist/TabList/Tab.d.ts +1 -1
  198. package/dist/TabList/Tab.d.ts.map +1 -1
  199. package/dist/TabList/Tab.js +20 -7
  200. package/dist/Table/Table.d.ts +1 -0
  201. package/dist/Table/Table.d.ts.map +1 -1
  202. package/dist/Table/Table.js +29 -13
  203. package/dist/TextArea/TextArea.d.ts.map +1 -1
  204. package/dist/TextArea/TextArea.js +1 -1
  205. package/dist/TextInput/TextInput.d.ts.map +1 -1
  206. package/dist/TextInput/TextInput.js +2 -2
  207. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  208. package/dist/TimeInput/TimeInput.js +4 -4
  209. package/dist/Timer/Timer.d.ts +49 -0
  210. package/dist/Timer/Timer.d.ts.map +1 -0
  211. package/dist/Timer/Timer.js +158 -0
  212. package/dist/Timer/index.d.ts +9 -0
  213. package/dist/Timer/index.d.ts.map +1 -0
  214. package/dist/Timer/index.js +10 -0
  215. package/dist/ToggleButton/ToggleButton.d.ts +9 -6
  216. package/dist/ToggleButton/ToggleButton.d.ts.map +1 -1
  217. package/dist/ToggleButton/ToggleButton.js +15 -27
  218. package/dist/TopNav/TopNavMenu.d.ts +1 -1
  219. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  220. package/dist/TopNav/TopNavMenu.js +3 -2
  221. package/dist/TreeList/TreeList.d.ts.map +1 -1
  222. package/dist/TreeList/TreeList.js +3 -0
  223. package/dist/TreeList/TreeListItem.d.ts +6 -1
  224. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  225. package/dist/TreeList/TreeListItem.js +12 -2
  226. package/dist/TreeList/TreeListTypes.d.ts +8 -1
  227. package/dist/TreeList/TreeListTypes.d.ts.map +1 -1
  228. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  229. package/dist/Typeahead/BaseTypeahead.js +16 -7
  230. package/dist/astryx.css +27 -11
  231. package/dist/hooks/scrollKeyboardDelegation.d.ts +3 -0
  232. package/dist/hooks/scrollKeyboardDelegation.d.ts.map +1 -0
  233. package/dist/hooks/scrollKeyboardDelegation.js +146 -0
  234. package/dist/hooks/useClickableContainer.d.ts +10 -1
  235. package/dist/hooks/useClickableContainer.d.ts.map +1 -1
  236. package/dist/hooks/useClickableContainer.js +15 -4
  237. package/dist/hooks/useScrollableArea.d.ts +6 -2
  238. package/dist/hooks/useScrollableArea.d.ts.map +1 -1
  239. package/dist/hooks/useScrollableArea.js +17 -5
  240. package/dist/index.d.ts +1 -0
  241. package/dist/index.d.ts.map +1 -1
  242. package/dist/index.js +3 -0
  243. package/dist/theme/declarationBoundary.d.ts +17 -0
  244. package/dist/theme/declarationBoundary.d.ts.map +1 -0
  245. package/dist/theme/declarationBoundary.js +343 -0
  246. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  247. package/dist/theme/derivedVarRegistry.js +4 -0
  248. package/dist/theme/generateThemeRules.d.ts +13 -7
  249. package/dist/theme/generateThemeRules.d.ts.map +1 -1
  250. package/dist/theme/generateThemeRules.js +140 -34
  251. package/dist/utils/interactionOverlay.stylex.d.ts +8 -0
  252. package/dist/utils/interactionOverlay.stylex.d.ts.map +1 -1
  253. package/dist/utils/interactionOverlay.stylex.js +9 -0
  254. package/dist/utils/safeUrl.d.ts +11 -0
  255. package/dist/utils/safeUrl.d.ts.map +1 -0
  256. package/dist/utils/safeUrl.js +78 -0
  257. package/locales/af-ZA.json +530 -1
  258. package/locales/ar-SA.json +490 -1
  259. package/locales/ca-ES.json +514 -1
  260. package/locales/cs-CZ.json +498 -1
  261. package/locales/da-DK.json +522 -1
  262. package/locales/de-DE.json +522 -1
  263. package/locales/el-GR.json +498 -1
  264. package/locales/es-ES.json +510 -1
  265. package/locales/fi-FI.json +498 -1
  266. package/locales/fr-FR.json +534 -1
  267. package/locales/he-IL.json +498 -1
  268. package/locales/hu-HU.json +498 -1
  269. package/locales/it-IT.json +510 -1
  270. package/locales/ja-JP.json +490 -1
  271. package/locales/ko-KR.json +498 -1
  272. package/locales/nl-NL.json +534 -1
  273. package/locales/no-NO.json +510 -1
  274. package/locales/pl-PL.json +506 -1
  275. package/locales/pt-BR.json +506 -1
  276. package/locales/pt-PT.json +506 -1
  277. package/locales/ro-RO.json +506 -1
  278. package/locales/ru-RU.json +498 -1
  279. package/locales/sr-SP.json +498 -1
  280. package/locales/sv-SE.json +502 -1
  281. package/locales/tr-TR.json +498 -1
  282. package/locales/uk-UA.json +498 -1
  283. package/locales/vi-VN.json +502 -1
  284. package/locales/zh-CN.json +490 -1
  285. package/locales/zh-TW.json +490 -1
  286. package/package.json +24 -4
  287. package/src/BottomSheet/BottomSheet.doc.mjs +8 -1
  288. package/src/BottomSheet/BottomSheet.spec.md +46 -20
  289. package/src/BottomSheet/BottomSheet.test.tsx +6 -3
  290. package/src/BottomSheet/BottomSheet.tsx +3 -1
  291. package/src/BottomSheet/BottomSheetKeyboard.test.tsx +195 -0
  292. package/src/BottomSheet/BottomSheetPanel.test.tsx +11 -1
  293. package/src/BottomSheet/BottomSheetPanel.tsx +49 -16
  294. package/src/BottomSheet/__tests__/BottomSheetKeyboard.a11y.browser.spec.ts +344 -0
  295. package/src/Button/Button.doc.mjs +5 -5
  296. package/src/Button/Button.tsx +15 -13
  297. package/src/Button/__tests__/Button.a11y.chromium.spec.ts +17 -1
  298. package/src/Button/__tests__/Button.a11y.known-failures.ts +0 -29
  299. package/src/Button/__tests__/Button.a11y.renders.tsx +9 -2
  300. package/src/Button/__tests__/Button.a11y.states.ts +9 -0
  301. package/src/Chat/ChatComposer.doc.mjs +14 -0
  302. package/src/Chat/ChatComposer.spec.md +230 -0
  303. package/src/Chat/ChatComposer.test.tsx +57 -0
  304. package/src/Chat/ChatComposer.tsx +21 -9
  305. package/src/Chat/ChatComposerDrawer.doc.mjs +63 -5
  306. package/src/Chat/ChatComposerDrawer.spec.md +267 -0
  307. package/src/Chat/ChatComposerDrawer.test.tsx +81 -12
  308. package/src/Chat/ChatComposerDrawer.tsx +38 -9
  309. package/src/Chat/ChatComposerInput.doc.mjs +114 -26
  310. package/src/Chat/ChatComposerInput.spec.md +185 -0
  311. package/src/Chat/ChatComposerInput.test.tsx +113 -3
  312. package/src/Chat/ChatComposerInput.tsx +78 -13
  313. package/src/Chat/ChatComposerTokenElement.spec.md +152 -0
  314. package/src/Chat/ChatComposerTokenElement.test.tsx +85 -0
  315. package/src/Chat/ChatDictationButton.doc.mjs +3 -3
  316. package/src/Chat/ChatDictationButton.spec.md +160 -0
  317. package/src/Chat/ChatDictationButton.test.tsx +92 -27
  318. package/src/Chat/ChatDictationButton.tsx +8 -6
  319. package/src/Chat/ChatLayout.doc.mjs +12 -4
  320. package/src/Chat/ChatLayout.spec.md +271 -0
  321. package/src/Chat/ChatLayout.test.tsx +28 -0
  322. package/src/Chat/ChatLayout.tsx +19 -0
  323. package/src/Chat/ChatLayoutScrollButton.spec.md +338 -0
  324. package/src/Chat/ChatLayoutScrollButton.test.tsx +31 -0
  325. package/src/Chat/ChatLayoutScrollButton.tsx +31 -14
  326. package/src/Chat/ChatMessageBubble.doc.mjs +28 -15
  327. package/src/Chat/ChatMessageBubble.spec.md +263 -0
  328. package/src/Chat/ChatMessageBubble.test.tsx +93 -0
  329. package/src/Chat/ChatMessageBubble.tsx +8 -3
  330. package/src/Chat/ChatMessageList.spec.md +222 -0
  331. package/src/Chat/ChatMessageList.test.tsx +64 -2
  332. package/src/Chat/ChatMessageList.tsx +4 -3
  333. package/src/Chat/ChatMessageMetadata.doc.mjs +57 -3
  334. package/src/Chat/ChatMessageMetadata.spec.md +220 -0
  335. package/src/Chat/ChatMessageMetadata.test.tsx +34 -0
  336. package/src/Chat/ChatMessageMetadata.tsx +13 -13
  337. package/src/Chat/ChatSendButton.doc.mjs +32 -0
  338. package/src/Chat/ChatSendButton.spec.md +227 -0
  339. package/src/Chat/ChatSendButton.test.tsx +128 -8
  340. package/src/Chat/ChatSendButton.tsx +6 -4
  341. package/src/Chat/__tests__/ChatLayoutScrollButton.a11y.chromium.spec.ts +514 -0
  342. package/src/Chat/__tests__/ChatLayoutScrollButtonSurface.a11y.chromium.spec.ts +740 -0
  343. package/src/Chat/__tests__/ChatMessageBubble.a11y.chromium.spec.ts +457 -0
  344. package/src/Chat/__tests__/ChatMessageBubble.auditProvenance.test.ts +91 -0
  345. package/src/Chat/__tests__/ChatMessageBubble.auditProvenance.ts +91 -0
  346. package/src/Chat/__tests__/ChatMessageList.a11y.chromium.spec.ts +456 -0
  347. package/src/Chat/__tests__/ChatMessageMetadata.a11y.chromium.spec.ts +413 -0
  348. package/src/Chat/__tests__/ChatSendButton.a11y.chromium.spec.ts +1522 -0
  349. package/src/Chat/index.ts +1 -0
  350. package/src/CheckboxInput/CheckboxInput.doc.mjs +11 -0
  351. package/src/CheckboxInput/CheckboxInput.test.tsx +34 -0
  352. package/src/CheckboxInput/CheckboxInput.tsx +21 -1
  353. package/src/Citation/Citation.doc.mjs +2 -2
  354. package/src/Citation/Citation.test.tsx +17 -0
  355. package/src/Citation/Citation.tsx +4 -2
  356. package/src/ClickableCard/ClickableCard.doc.mjs +2 -2
  357. package/src/ClickableCard/ClickableCard.test.tsx +102 -5
  358. package/src/CodeBlock/highlightRanges.test.ts +143 -1
  359. package/src/CodeBlock/highlightRanges.ts +15 -5
  360. package/src/Collapsible/Collapsible.doc.mjs +11 -0
  361. package/src/Collapsible/Collapsible.test.tsx +21 -0
  362. package/src/Collapsible/Collapsible.tsx +5 -0
  363. package/src/CommandPalette/CommandPaletteInput.tsx +6 -1
  364. package/src/ComplexSelector/ComplexSelector.tsx +6 -1
  365. package/src/DateInput/DateInput.tsx +6 -1
  366. package/src/DateInput/NativeDateField.tsx +24 -8
  367. package/src/DateInput/TouchDateField.tsx +6 -2
  368. package/src/DateRangeInput/DateRangeInput.tsx +6 -1
  369. package/src/DateTimeInput/DateTimeInput.tsx +6 -1
  370. package/src/DateTimeInput/TouchDateTimeField.tsx +6 -1
  371. package/src/DateTimeInput/nativePickerSegmentStyles.ts +24 -8
  372. package/src/Dialog/Dialog.doc.mjs +2 -0
  373. package/src/Dialog/DialogHeader.doc.mjs +22 -0
  374. package/src/Dialog/DialogHeader.test.tsx +167 -3
  375. package/src/Dialog/DialogHeader.tsx +37 -6
  376. package/src/Dialog/modules/DialogHeader.spec.md +101 -42
  377. package/src/DropdownMenu/DropdownMenu.test.tsx +5 -2
  378. package/src/Field/InputClearButton.test.tsx +17 -3
  379. package/src/Field/InputClearButton.tsx +5 -2
  380. package/src/Field/PanelSearchInput.tsx +5 -2
  381. package/src/FieldStatus/__tests__/StatusMessage.a11y.states.ts +1 -1
  382. package/src/FileInput/FileInput.tsx +11 -2
  383. package/src/Icon/Icon.doc.mjs +7 -5
  384. package/src/Icon/Icon.spec.md +81 -40
  385. package/src/Icon/Icon.test.tsx +6 -3
  386. package/src/Icon/Icon.tsx +20 -68
  387. package/src/Icon/IconDefaultSizeContext.ts +23 -0
  388. package/src/Icon/IconSize.stylex.ts +70 -0
  389. package/src/IconButton/IconButton.doc.mjs +3 -3
  390. package/src/IconButton/IconButton.test.tsx +40 -0
  391. package/src/Item/Item.doc.mjs +6 -2
  392. package/src/Item/Item.tsx +9 -3
  393. package/src/Kbd/Kbd.doc.mjs +3 -3
  394. package/src/Kbd/Kbd.test.tsx +57 -1
  395. package/src/Kbd/Kbd.tsx +57 -37
  396. package/src/Layout/LayoutFooter.doc.mjs +17 -0
  397. package/src/Link/Link.doc.mjs +17 -4
  398. package/src/Link/Link.test.tsx +24 -0
  399. package/src/Link/Link.tsx +5 -0
  400. package/src/Link/LinkProvider.doc.mjs +2 -2
  401. package/src/Link/__tests__/Link.navigation.a11y.chromium.spec.ts +261 -0
  402. package/src/Link/useLinkComponent.test.tsx +333 -0
  403. package/src/Link/useLinkComponent.ts +29 -57
  404. package/src/List/List.doc.mjs +6 -0
  405. package/src/List/List.spec.md +200 -0
  406. package/src/List/List.test.tsx +117 -1
  407. package/src/List/List.tsx +22 -2
  408. package/src/List/ListContext.tsx +3 -1
  409. package/src/List/ListItem.doc.mjs +2 -2
  410. package/src/List/ListItem.source-build.test.mjs +41 -0
  411. package/src/List/ListItem.tsx +26 -2
  412. package/src/Markdown/Markdown.doc.mjs +300 -2
  413. package/src/Markdown/Markdown.fr23.bench.ts +174 -0
  414. package/src/Markdown/Markdown.fr23.perf.test.ts +249 -0
  415. package/src/Markdown/Markdown.fr23.sampling.ts +49 -0
  416. package/src/Markdown/Markdown.helpers.perf.test.ts +489 -0
  417. package/src/Markdown/Markdown.public.test.ts +130 -3
  418. package/src/Markdown/Markdown.renderBoundary.test.tsx +72 -0
  419. package/src/Markdown/Markdown.spec.md +210 -45
  420. package/src/Markdown/Markdown.test.tsx +87 -3
  421. package/src/Markdown/Markdown.tsx +414 -135
  422. package/src/Markdown/ast.test.ts +307 -0
  423. package/src/Markdown/ast.ts +337 -0
  424. package/src/Markdown/incremental.test.ts +43 -0
  425. package/src/Markdown/index.ts +37 -1
  426. package/src/Markdown/modules/remark.spec.md +269 -0
  427. package/src/Markdown/modules/softBreaks.spec.md +137 -0
  428. package/src/Markdown/parser/index.ts +64 -0
  429. package/src/Markdown/parser.public.test.ts +103 -0
  430. package/src/Markdown/parser.test.ts +38 -6
  431. package/src/Markdown/parser.ts +1221 -242
  432. package/src/Markdown/plugins/frontmatter.test.tsx +125 -0
  433. package/src/Markdown/plugins/frontmatter.ts +198 -0
  434. package/src/Markdown/plugins/index.ts +54 -0
  435. package/src/Markdown/plugins/protocol.test.tsx +1022 -0
  436. package/src/Markdown/plugins/protocol.ts +1618 -0
  437. package/src/Markdown/plugins/semanticFence.test.tsx +546 -0
  438. package/src/Markdown/plugins/semanticFence.ts +424 -0
  439. package/src/Markdown/plugins/softBreaks.test.tsx +172 -0
  440. package/src/Markdown/plugins/softBreaks.ts +169 -0
  441. package/src/Markdown/plugins/sourceDecoration.test.tsx +799 -0
  442. package/src/Markdown/plugins/sourceDecoration.ts +539 -0
  443. package/src/Markdown/plugins/textTransform.test.ts +467 -0
  444. package/src/Markdown/plugins/textTransform.ts +554 -0
  445. package/src/Markdown/remark.test.tsx +1877 -0
  446. package/src/Markdown/remark.ts +1574 -0
  447. package/src/Markdown/url.ts +36 -0
  448. package/src/Markdown/utils.ts +13 -1
  449. package/src/MetadataList/MetadataList.test.tsx +4 -2
  450. package/src/MetadataList/MetadataList.tsx +3 -3
  451. package/src/MultiSelector/MultiSelector.test.tsx +1 -35
  452. package/src/MultiSelector/__tests__/MultiSelector.listbox.a11y.test.tsx +128 -0
  453. package/src/NumberInput/NumberInput.tsx +6 -1
  454. package/src/Outline/Outline.doc.mjs +7 -5
  455. package/src/Outline/Outline.spec.md +1 -1
  456. package/src/Outline/index.ts +1 -0
  457. package/src/Outline/modules/parseOutlineFromMarkdown.spec.md +142 -0
  458. package/src/Outline/parseOutlineFromMarkdown.ts +40 -10
  459. package/src/Outline/useOutlineFromMarkdown.ts +15 -3
  460. package/src/PowerSearch/PowerSearch.doc.mjs +2 -2
  461. package/src/PowerSearch/PowerSearchEditPopover.test.tsx +150 -1
  462. package/src/PowerSearch/PowerSearchEditPopover.tsx +51 -28
  463. package/src/PowerSearch/PowerSearchValueEditor.test.tsx +51 -0
  464. package/src/PowerSearch/PowerSearchValueEditor.tsx +4 -0
  465. package/src/ProgressBar/ProgressBar.test.tsx +29 -11
  466. package/src/ProgressBar/ProgressBar.tsx +59 -72
  467. package/src/RadioList/RadioList.doc.mjs +11 -0
  468. package/src/RadioList/RadioList.test.tsx +32 -0
  469. package/src/RadioList/RadioListItem.tsx +25 -1
  470. package/src/ScrollableArea/ScrollableArea.tsx +1 -0
  471. package/src/ScrollableArea/modules/useScrollableArea.spec.md +50 -22
  472. package/src/SegmentedControl/SegmentedControl.doc.mjs +2 -2
  473. package/src/SegmentedControl/SegmentedControl.test.tsx +61 -0
  474. package/src/SegmentedControl/SegmentedControl.tsx +2 -1
  475. package/src/SegmentedControl/SegmentedControlItem.tsx +6 -9
  476. package/src/Selector/Selector.doc.mjs +3 -3
  477. package/src/Selector/Selector.spec.md +51 -49
  478. package/src/Selector/Selector.test.tsx +61 -55
  479. package/src/Selector/Selector.tsx +34 -15
  480. package/src/Selector/__tests__/Listbox.a11y.chromium.spec.ts +130 -0
  481. package/src/Selector/__tests__/Listbox.a11y.dom.ts +38 -0
  482. package/src/Selector/__tests__/Listbox.a11y.inventory.test.ts +63 -0
  483. package/src/Selector/__tests__/Listbox.a11y.known-failures.ts +37 -0
  484. package/src/Selector/__tests__/Listbox.a11y.states.ts +232 -0
  485. package/src/Selector/__tests__/Selector.a11y.chromium.spec.ts +45 -0
  486. package/src/Selector/__tests__/Selector.listbox.a11y.test.tsx +121 -0
  487. package/src/SideNav/SideNav.doc.mjs +1 -1
  488. package/src/SideNav/SideNav.test.tsx +10 -0
  489. package/src/SideNav/SideNav.tsx +14 -2
  490. package/src/Slider/Slider.doc.mjs +17 -3
  491. package/src/Slider/Slider.test.tsx +221 -0
  492. package/src/Slider/Slider.tsx +40 -9
  493. package/src/Spinner/Spinner.test.tsx +4 -0
  494. package/src/Spinner/Spinner.tsx +31 -18
  495. package/src/Switch/Switch.doc.mjs +11 -0
  496. package/src/Switch/Switch.test.tsx +16 -0
  497. package/src/Switch/Switch.tsx +28 -0
  498. package/src/TabList/Tab.tsx +35 -7
  499. package/src/TabList/TabList.doc.mjs +11 -0
  500. package/src/TabList/TabList.spec.md +204 -0
  501. package/src/TabList/TabList.test.tsx +66 -0
  502. package/src/TabList/__tests__/Tabs.a11y.known-failures.ts +1 -34
  503. package/src/Table/Table.doc.mjs +1 -1
  504. package/src/Table/Table.spec.md +54 -40
  505. package/src/Table/Table.test.tsx +83 -10
  506. package/src/Table/Table.tsx +30 -22
  507. package/src/Table/__tests__/TableScroll.a11y.chromium.spec.ts +137 -0
  508. package/src/TextArea/TextArea.tsx +6 -1
  509. package/src/TextInput/TextInput.test.tsx +20 -1
  510. package/src/TextInput/TextInput.tsx +6 -1
  511. package/src/TimeInput/TimeInput.tsx +6 -1
  512. package/src/Timer/Timer.doc.mjs +183 -0
  513. package/src/Timer/Timer.spec.md +215 -0
  514. package/src/Timer/Timer.test.tsx +302 -0
  515. package/src/Timer/Timer.tsx +258 -0
  516. package/src/Timer/index.ts +11 -0
  517. package/src/ToggleButton/ToggleButton.doc.mjs +3 -3
  518. package/src/ToggleButton/ToggleButton.test.tsx +299 -4
  519. package/src/ToggleButton/ToggleButton.tsx +26 -33
  520. package/src/ToggleButton/ToggleButtonGroup.doc.mjs +3 -3
  521. package/src/ToggleButton/__tests__/ToggleButton.a11y.chromium.spec.ts +209 -0
  522. package/src/Token/Token.doc.mjs +2 -2
  523. package/src/Tokenizer/Tokenizer.spec.md +142 -75
  524. package/src/TopNav/TopNavMenu.doc.mjs +2 -2
  525. package/src/TopNav/TopNavMenu.test.tsx +30 -0
  526. package/src/TopNav/TopNavMenu.tsx +7 -2
  527. package/src/TreeList/TreeList.doc.mjs +3 -3
  528. package/src/TreeList/TreeList.spec.md +123 -75
  529. package/src/TreeList/TreeList.test.tsx +29 -0
  530. package/src/TreeList/TreeList.tsx +3 -0
  531. package/src/TreeList/TreeListItem.tsx +15 -1
  532. package/src/TreeList/TreeListTypes.ts +11 -1
  533. package/src/Typeahead/BaseTypeahead.spec.md +4 -3
  534. package/src/Typeahead/BaseTypeahead.tsx +21 -7
  535. package/src/Typeahead/Typeahead.test.tsx +53 -0
  536. package/src/__tests__/PressedState.a11y.chromium.spec.ts +813 -0
  537. package/src/__tests__/pressState.ts +93 -0
  538. package/src/hooks/scrollKeyboardDelegation.test.ts +155 -0
  539. package/src/hooks/scrollKeyboardDelegation.ts +233 -0
  540. package/src/hooks/useClickableContainer.test.tsx +236 -0
  541. package/src/hooks/useClickableContainer.ts +20 -4
  542. package/src/hooks/useScrollableArea.doc.mjs +15 -3
  543. package/src/hooks/useScrollableArea.test.tsx +59 -1
  544. package/src/hooks/useScrollableArea.ts +34 -10
  545. package/src/index.ts +3 -0
  546. package/src/inputFontFloor.test.ts +106 -0
  547. package/src/tailwind-theme.css +0 -6
  548. package/src/theme/Theme.test.tsx +32 -0
  549. package/src/theme/declarationBoundary.test.ts +126 -0
  550. package/src/theme/declarationBoundary.ts +347 -0
  551. package/src/theme/derivedVarRegistry.test.ts +7 -0
  552. package/src/theme/derivedVarRegistry.ts +1 -0
  553. package/src/theme/generateThemeRules.test.ts +526 -1
  554. package/src/theme/generateThemeRules.ts +228 -50
  555. package/src/theme/tokenValueCompat.test.ts +4 -1
  556. package/src/utils/interactionOverlay.stylex.ts +20 -0
  557. package/src/utils/safeUrl.test.ts +88 -0
  558. package/src/utils/safeUrl.ts +85 -0
@@ -4,12 +4,12 @@
4
4
 
5
5
  /**
6
6
  * @file Markdown.tsx
7
- * @input Markdown string, parser AST types, optional custom renderers
7
+ * @input Markdown string, canonical AST, optional plugins and custom renderers
8
8
  * @output Exports Markdown component, MarkdownProps, and renderer contracts
9
9
  * @position Core implementation; renders markdown as Astryx components
10
10
  */
11
11
 
12
- import {useMemo, useRef} from 'react';
12
+ import {Component, Suspense, useMemo, useRef} from 'react';
13
13
  import type React from 'react';
14
14
  import {Fragment} from 'react';
15
15
  import * as stylex from '@stylexjs/stylex';
@@ -48,26 +48,43 @@ import type {CitationSource} from '../Citation/Citation';
48
48
  import {useLinkComponent} from '../Link/useLinkComponent';
49
49
  import type {LinkComponentType} from '../Link/types';
50
50
  import {
51
- parseInline,
52
- parseMarkdown,
53
- parseMarkdownIncremental,
51
+ parseInlineAst,
52
+ parseMarkdownAst,
53
+ parseMarkdownAstIncremental,
54
54
  createIncrementalState,
55
55
  trimStreamingArtifacts,
56
- inlineText,
57
56
  slugify,
58
57
  uniqueSlug,
59
58
  } from './parser';
59
+ import type {IncrementalState, MathParseOptions, ParseOptions} from './parser';
60
+ import {getMarkdownAstLegacyCodeLanguage, markdownAstText} from './ast';
60
61
  import type {
61
- BlockNodeWithMath,
62
- InlineNodeWithMath,
63
- IncrementalState,
64
- MathParseOptions,
65
- ParseOptions,
66
- } from './parser';
62
+ MarkdownAstBlockContent,
63
+ MarkdownAstPhrasingContent,
64
+ MarkdownAstTable,
65
+ } from './ast';
66
+ import {
67
+ applyMarkdownTransforms,
68
+ getMarkdownExtensionRenderer,
69
+ markdownExtensionText,
70
+ prepareMarkdownPlugins,
71
+ reportMarkdownPluginFailure,
72
+ } from './plugins/protocol';
73
+ import {getMarkdownFenceProposal} from './plugins/semanticFence';
74
+ import type {
75
+ MarkdownExtensionNode,
76
+ MarkdownPluginEntry,
77
+ PreparedMarkdownPlugins,
78
+ } from './plugins/protocol';
79
+ import {sanitizeMarkdownLinkUrl, sanitizeMarkdownUrl} from './url';
67
80
  import {themeProps} from '../utils/themeProps';
68
81
  import {useTranslator, type TranslatorFn} from '../i18n';
69
82
 
70
83
  type SyncReactNode = Exclude<React.ReactNode, Promise<unknown>>;
84
+ type RenderExtensionNode = MarkdownExtensionNode;
85
+ type RenderInlineNode = MarkdownAstPhrasingContent<RenderExtensionNode>;
86
+ type RenderBlockNode = MarkdownAstBlockContent<RenderExtensionNode>;
87
+ type RenderTable = MarkdownAstTable<RenderExtensionNode>;
71
88
 
72
89
  // ---------------------------------------------------------------------------
73
90
  // Props
@@ -140,7 +157,9 @@ export interface MarkdownComponents {
140
157
  hr?: React.ComponentType<object>;
141
158
  }
142
159
 
143
- export interface MarkdownProps extends BaseProps<HTMLElement> {
160
+ export interface MarkdownProps<
161
+ Plugins extends ReadonlyArray<MarkdownPluginEntry> = readonly [],
162
+ > extends BaseProps<HTMLElement> {
144
163
  ref?: React.Ref<HTMLDivElement> | React.Ref<HTMLSpanElement>;
145
164
  children: string;
146
165
  /**
@@ -201,6 +220,11 @@ export interface MarkdownProps extends BaseProps<HTMLElement> {
201
220
  * are unaffected. Patterns are matched in order; first match wins
202
221
  * for overlapping ranges.
203
222
  */
223
+ /** Ordered syntax, immutable transforms, and typed extension renderers. */
224
+ plugins?: Plugins;
225
+ /**
226
+ * Legacy text-match renderers. Unchanged and independent from `plugins`.
227
+ */
204
228
  inlinePlugins?: MarkdownInlinePlugin[];
205
229
  /**
206
230
  * Opt-in autolinking of bare URLs and emails inside text.
@@ -487,25 +511,25 @@ interface StreamingCursor {
487
511
  * Count the total text characters in inline nodes without rendering.
488
512
  * Used to advance the cursor past a block that will be faded as a whole unit.
489
513
  */
490
- function countInlineTextLength(nodes: InlineNodeWithMath[]): number {
514
+ function countInlineTextLength(nodes: ReadonlyArray<RenderInlineNode>): number {
491
515
  let len = 0;
492
516
  for (const node of nodes) {
493
517
  switch (node.type) {
494
518
  case 'text':
495
- len += node.content.length;
519
+ len += node.value.length;
496
520
  break;
497
- case 'code':
498
- len += node.content.length;
521
+ case 'inlineCode':
522
+ len += node.value.length;
499
523
  break;
500
- case 'math':
524
+ case 'inlineMath':
501
525
  len += node.value.length;
502
526
  break;
503
527
  case 'image':
504
528
  len += node.alt.length;
505
529
  break;
506
- case 'bold':
507
- case 'italic':
508
- case 'strikethrough':
530
+ case 'strong':
531
+ case 'emphasis':
532
+ case 'delete':
509
533
  case 'link':
510
534
  len += countInlineTextLength(node.children);
511
535
  break;
@@ -515,6 +539,9 @@ function countInlineTextLength(nodes: InlineNodeWithMath[]): number {
515
539
  case 'citation':
516
540
  len += 1;
517
541
  break;
542
+ case 'extension':
543
+ len += node.source?.length ?? 0;
544
+ break;
518
545
  }
519
546
  }
520
547
  return len;
@@ -523,7 +550,7 @@ function countInlineTextLength(nodes: InlineNodeWithMath[]): number {
523
550
  /**
524
551
  * Count total text characters in a block node tree.
525
552
  */
526
- function countBlockTextLength(nodes: BlockNodeWithMath[]): number {
553
+ function countBlockTextLength(nodes: ReadonlyArray<RenderBlockNode>): number {
527
554
  let len = 0;
528
555
  for (const node of nodes) {
529
556
  switch (node.type) {
@@ -531,8 +558,8 @@ function countBlockTextLength(nodes: BlockNodeWithMath[]): number {
531
558
  case 'paragraph':
532
559
  len += countInlineTextLength(node.children);
533
560
  break;
534
- case 'codeblock':
535
- len += node.content.length;
561
+ case 'code':
562
+ len += node.value.length;
536
563
  break;
537
564
  case 'math':
538
565
  len += node.value.length;
@@ -541,21 +568,21 @@ function countBlockTextLength(nodes: BlockNodeWithMath[]): number {
541
568
  len += countBlockTextLength(node.children);
542
569
  break;
543
570
  case 'list':
544
- for (const item of node.items) {
571
+ for (const item of node.children) {
545
572
  len += countBlockTextLength(item.children);
546
573
  }
547
574
  break;
548
575
  case 'table':
549
- for (const h of node.headers) {
550
- len += countInlineTextLength(h.children);
551
- }
552
- for (const row of node.rows) {
553
- for (const cell of row) {
576
+ for (const row of node.children) {
577
+ for (const cell of row.children) {
554
578
  len += countInlineTextLength(cell.children);
555
579
  }
556
580
  }
557
581
  break;
558
- case 'hr':
582
+ case 'extension':
583
+ len += node.source?.length ?? 0;
584
+ break;
585
+ case 'thematicBreak':
559
586
  break;
560
587
  case 'image':
561
588
  len += node.alt.length;
@@ -575,27 +602,6 @@ const headingStyles = {
575
602
  } as const;
576
603
 
577
604
  // ---------------------------------------------------------------------------
578
- // URL sanitization — block dangerous protocols
579
- // ---------------------------------------------------------------------------
580
-
581
- const DANGEROUS_URL_PATTERN = /^(javascript|data|vbscript):/i;
582
-
583
- function sanitizeUrl(url: string): string | null {
584
- // Strip control characters before testing, the same normalization the
585
- // parser's isSafeUrl applies — the anchored pattern must see the URL the
586
- // way a browser will. Return the normalized value so the stripped
587
- // characters don't ride along into an attribute or component override.
588
- // eslint-disable-next-line no-control-regex -- control chars are the bypass
589
- const normalized = url.replace(/[\x00-\x1f\x7f]/g, '').trim();
590
- if (normalized.length === 0) {
591
- return null;
592
- }
593
- if (DANGEROUS_URL_PATTERN.test(normalized)) {
594
- return null;
595
- }
596
- return normalized;
597
- }
598
-
599
605
  // ---------------------------------------------------------------------------
600
606
  // Inline plugin matching
601
607
  // ---------------------------------------------------------------------------
@@ -701,6 +707,81 @@ function applyInlinePlugins(
701
707
  return segments;
702
708
  }
703
709
 
710
+ interface MarkdownPluginBoundaryProps {
711
+ children: React.ReactNode;
712
+ fallback: React.ReactNode;
713
+ pluginName: string;
714
+ resetKey: unknown;
715
+ resetRenderer: unknown;
716
+ }
717
+
718
+ interface MarkdownPluginBoundaryState {
719
+ failed: boolean;
720
+ resetKey: unknown;
721
+ resetRenderer: unknown;
722
+ }
723
+
724
+ class MarkdownPluginBoundary extends Component<
725
+ MarkdownPluginBoundaryProps,
726
+ MarkdownPluginBoundaryState
727
+ > {
728
+ state: MarkdownPluginBoundaryState = {
729
+ failed: false,
730
+ resetKey: this.props.resetKey,
731
+ resetRenderer: this.props.resetRenderer,
732
+ };
733
+
734
+ static getDerivedStateFromError(): Partial<MarkdownPluginBoundaryState> {
735
+ return {failed: true};
736
+ }
737
+
738
+ static getDerivedStateFromProps(
739
+ props: MarkdownPluginBoundaryProps,
740
+ state: MarkdownPluginBoundaryState,
741
+ ): Partial<MarkdownPluginBoundaryState> | null {
742
+ return props.resetKey === state.resetKey &&
743
+ props.resetRenderer === state.resetRenderer
744
+ ? null
745
+ : {
746
+ failed: false,
747
+ resetKey: props.resetKey,
748
+ resetRenderer: props.resetRenderer,
749
+ };
750
+ }
751
+
752
+ componentDidCatch(error: unknown): void {
753
+ reportMarkdownPluginFailure(this.props.pluginName, 'render', error);
754
+ }
755
+
756
+ render(): React.ReactNode {
757
+ return this.state.failed ? this.props.fallback : this.props.children;
758
+ }
759
+ }
760
+
761
+ function useStableMarkdownSyntaxEntries(
762
+ plugins: PreparedMarkdownPlugins | undefined,
763
+ ): ReadonlyArray<MarkdownPluginEntry> | undefined {
764
+ const identity = plugins?.syntaxIdentity ?? '';
765
+ const syntaxEntriesRef = useRef<
766
+ | {
767
+ identity: string;
768
+ entries: ReadonlyArray<MarkdownPluginEntry> | undefined;
769
+ }
770
+ | undefined
771
+ >(undefined);
772
+ if (
773
+ syntaxEntriesRef.current == null ||
774
+ syntaxEntriesRef.current.identity !== identity
775
+ ) {
776
+ syntaxEntriesRef.current = {
777
+ identity,
778
+ entries:
779
+ plugins == null || identity === '' ? undefined : plugins.syntaxEntries,
780
+ };
781
+ }
782
+ return syntaxEntriesRef.current.entries;
783
+ }
784
+
704
785
  // ---------------------------------------------------------------------------
705
786
  // Inline renderer
706
787
  // ---------------------------------------------------------------------------
@@ -768,7 +849,7 @@ function getCitationNumber(ctx: CitationContext, sourceId: string): number {
768
849
  }
769
850
 
770
851
  function renderInline(
771
- node: InlineNodeWithMath,
852
+ node: RenderInlineNode,
772
853
  index: number,
773
854
  onLinkClick: MarkdownProps['onLinkClick'] | undefined,
774
855
  cursor: StreamingCursor,
@@ -776,11 +857,12 @@ function renderInline(
776
857
  linkComponent: LinkComponentType = 'a',
777
858
  inlinePlugins?: MarkdownInlinePlugin[],
778
859
  components?: Partial<MarkdownComponents>,
860
+ preparedPlugins?: PreparedMarkdownPlugins,
779
861
  ): SyncReactNode {
780
862
  switch (node.type) {
781
863
  case 'text': {
782
864
  if (inlinePlugins && inlinePlugins.length > 0) {
783
- const segments = applyInlinePlugins(node.content, inlinePlugins);
865
+ const segments = applyInlinePlugins(node.value, inlinePlugins);
784
866
  // If no plugin matched, fall through to the normal path
785
867
  // O(1) guard: applyInlinePlugins returns a single text segment when
786
868
  // nothing matched — skip the plugin path entirely in that case.
@@ -803,9 +885,9 @@ function renderInline(
803
885
  return <Fragment key={index}>{result}</Fragment>;
804
886
  }
805
887
  }
806
- return wrapTextWithFade(node.content, cursor, index);
888
+ return wrapTextWithFade(node.value, cursor, index);
807
889
  }
808
- case 'bold':
890
+ case 'strong':
809
891
  return (
810
892
  <strong key={index} {...stylex.props(styles.bold)}>
811
893
  {node.children.map((c, i) =>
@@ -818,11 +900,12 @@ function renderInline(
818
900
  linkComponent,
819
901
  inlinePlugins,
820
902
  components,
903
+ preparedPlugins,
821
904
  ),
822
905
  )}
823
906
  </strong>
824
907
  );
825
- case 'italic':
908
+ case 'emphasis':
826
909
  return (
827
910
  <em key={index}>
828
911
  {node.children.map((c, i) =>
@@ -835,11 +918,12 @@ function renderInline(
835
918
  linkComponent,
836
919
  inlinePlugins,
837
920
  components,
921
+ preparedPlugins,
838
922
  ),
839
923
  )}
840
924
  </em>
841
925
  );
842
- case 'strikethrough':
926
+ case 'delete':
843
927
  return (
844
928
  <del key={index} {...stylex.props(styles.strikethrough)}>
845
929
  {node.children.map((c, i) =>
@@ -852,22 +936,23 @@ function renderInline(
852
936
  linkComponent,
853
937
  inlinePlugins,
854
938
  components,
939
+ preparedPlugins,
855
940
  ),
856
941
  )}
857
942
  </del>
858
943
  );
859
- case 'code': {
944
+ case 'inlineCode': {
860
945
  // Track code content length for cursor but don't split inside code
861
- cursor.offset += node.content.length;
946
+ cursor.offset += node.value.length;
862
947
  const InlineCodeComp = components?.inlineCode;
863
948
  const codeEl = InlineCodeComp ? (
864
- <InlineCodeComp key={index}>{node.content}</InlineCodeComp>
949
+ <InlineCodeComp key={index}>{node.value}</InlineCodeComp>
865
950
  ) : (
866
- <Code key={index}>{node.content}</Code>
951
+ <Code key={index}>{node.value}</Code>
867
952
  );
868
953
  return codeEl;
869
954
  }
870
- case 'math': {
955
+ case 'inlineMath': {
871
956
  const MathComp = components?.math;
872
957
  if (MathComp == null) {
873
958
  return wrapTextWithFade(`$${node.value}$`, cursor, index);
@@ -876,7 +961,7 @@ function renderInline(
876
961
  return <MathComp key={index} value={node.value} display="inline" />;
877
962
  }
878
963
  case 'link': {
879
- const safeHref = sanitizeUrl(node.href);
964
+ const safeHref = sanitizeMarkdownLinkUrl(node.url);
880
965
  if (safeHref == null) {
881
966
  // Unsafe URL — render as plain text
882
967
  return (
@@ -891,6 +976,7 @@ function renderInline(
891
976
  linkComponent,
892
977
  inlinePlugins,
893
978
  components,
979
+ preparedPlugins,
894
980
  ),
895
981
  )}
896
982
  </span>
@@ -910,6 +996,7 @@ function renderInline(
910
996
  linkComponent,
911
997
  inlinePlugins,
912
998
  components,
999
+ preparedPlugins,
913
1000
  ),
914
1001
  )}
915
1002
  </LinkComp>
@@ -948,13 +1035,14 @@ function renderInline(
948
1035
  linkComponent,
949
1036
  inlinePlugins,
950
1037
  components,
1038
+ preparedPlugins,
951
1039
  ),
952
1040
  )}
953
1041
  </LinkTag>
954
1042
  );
955
1043
  }
956
1044
  case 'image': {
957
- const safeSrc = sanitizeUrl(node.src);
1045
+ const safeSrc = sanitizeMarkdownUrl(node.url);
958
1046
  if (safeSrc == null) {
959
1047
  return <span key={index}>[{node.alt}]</span>;
960
1048
  }
@@ -971,6 +1059,34 @@ function renderInline(
971
1059
  />
972
1060
  );
973
1061
  }
1062
+ case 'extension': {
1063
+ const renderer = getMarkdownExtensionRenderer(preparedPlugins, node);
1064
+ const fallback = wrapTextWithFade(
1065
+ node.source ?? markdownExtensionText(preparedPlugins, node),
1066
+ cursor,
1067
+ index,
1068
+ );
1069
+ if (renderer == null) {
1070
+ return fallback;
1071
+ }
1072
+ let rendered: React.ReactNode;
1073
+ try {
1074
+ rendered = renderer.render({node});
1075
+ } catch (error) {
1076
+ reportMarkdownPluginFailure(node.plugin, 'render', error);
1077
+ return fallback;
1078
+ }
1079
+ return (
1080
+ <MarkdownPluginBoundary
1081
+ key={index}
1082
+ pluginName={node.plugin}
1083
+ resetKey={node}
1084
+ resetRenderer={renderer.render}
1085
+ fallback={fallback}>
1086
+ <Suspense fallback={fallback}>{rendered}</Suspense>
1087
+ </MarkdownPluginBoundary>
1088
+ );
1089
+ }
974
1090
  case 'break':
975
1091
  cursor.offset += 1;
976
1092
  return <br key={index} />;
@@ -1012,13 +1128,13 @@ function renderInline(
1012
1128
  // ---------------------------------------------------------------------------
1013
1129
 
1014
1130
  function getElementSpacing(
1015
- node: BlockNodeWithMath,
1131
+ node: RenderBlockNode,
1016
1132
  density: 'default' | 'compact',
1017
1133
  ): StyleXStyles {
1018
1134
  const compact = density === 'compact';
1019
1135
  switch (node.type) {
1020
1136
  case 'heading':
1021
- return node.level <= 3
1137
+ return node.depth <= 3
1022
1138
  ? compact
1023
1139
  ? styles.spacingHeadingMajorCompact
1024
1140
  : styles.spacingHeadingMajorDefault
@@ -1029,7 +1145,7 @@ function getElementSpacing(
1029
1145
  return compact
1030
1146
  ? styles.spacingParagraphCompact
1031
1147
  : styles.spacingParagraphDefault;
1032
- case 'codeblock':
1148
+ case 'code':
1033
1149
  case 'math':
1034
1150
  return compact
1035
1151
  ? styles.spacingCodeblockCompact
@@ -1042,7 +1158,11 @@ function getElementSpacing(
1042
1158
  return compact ? styles.spacingListCompact : styles.spacingListDefault;
1043
1159
  case 'table':
1044
1160
  return compact ? styles.spacingTableCompact : styles.spacingTableDefault;
1045
- case 'hr':
1161
+ case 'extension':
1162
+ return compact
1163
+ ? styles.spacingParagraphCompact
1164
+ : styles.spacingParagraphDefault;
1165
+ case 'thematicBreak':
1046
1166
  return compact ? styles.spacingHrCompact : styles.spacingHrDefault;
1047
1167
  case 'image':
1048
1168
  return compact ? styles.spacingImageCompact : styles.spacingImageDefault;
@@ -1057,15 +1177,17 @@ function getElementSpacing(
1057
1177
  * Compute per-column min-widths from table AST content.
1058
1178
  * Buckets: ≤6 chars → 60px, 7–15 → 80px, >15 → 120px.
1059
1179
  */
1060
- function computeTableColumnMinWidths(node: {
1061
- headers: {children: InlineNodeWithMath[]}[];
1062
- rows: {children: InlineNodeWithMath[]}[][];
1063
- }): number[] {
1064
- return node.headers.map((h, colIdx) => {
1065
- let maxLen = countInlineTextLength(h.children);
1066
- for (const row of node.rows) {
1067
- if (row[colIdx]) {
1068
- const len = countInlineTextLength(row[colIdx].children);
1180
+ function computeTableColumnMinWidths(node: RenderTable): number[] {
1181
+ const [header, ...rows] = node.children;
1182
+ if (header == null) {
1183
+ return [];
1184
+ }
1185
+ return header.children.map((cell, colIdx) => {
1186
+ let maxLen = countInlineTextLength(cell.children);
1187
+ for (const row of rows) {
1188
+ const rowCell = row.children[colIdx];
1189
+ if (rowCell != null) {
1190
+ const len = countInlineTextLength(rowCell.children);
1069
1191
  if (len > maxLen) {
1070
1192
  maxLen = len;
1071
1193
  }
@@ -1076,7 +1198,7 @@ function computeTableColumnMinWidths(node: {
1076
1198
  }
1077
1199
 
1078
1200
  function renderBlock(
1079
- node: BlockNodeWithMath,
1201
+ node: RenderBlockNode,
1080
1202
  index: number,
1081
1203
  blockCount: number,
1082
1204
  density: 'default' | 'compact',
@@ -1089,8 +1211,9 @@ function renderBlock(
1089
1211
  linkComponent: LinkComponentType = 'a',
1090
1212
  inlinePlugins: MarkdownInlinePlugin[] | undefined,
1091
1213
  components: Partial<MarkdownComponents> | undefined,
1214
+ preparedPlugins: PreparedMarkdownPlugins | undefined,
1092
1215
  t: TranslatorFn,
1093
- headingIdMap?: ReadonlyMap<BlockNodeWithMath, string>,
1216
+ headingIdMap?: ReadonlyMap<RenderBlockNode, string>,
1094
1217
  ): SyncReactNode {
1095
1218
  const blockAlignMargin = BLOCK_ALIGN_MARGIN[contentAlign];
1096
1219
  const blockAlignStyle =
@@ -1103,7 +1226,7 @@ function renderBlock(
1103
1226
 
1104
1227
  switch (node.type) {
1105
1228
  case 'heading': {
1106
- const level = Math.min(node.level + headingLevelStart - 1, 6) as
1229
+ const level = Math.min(node.depth + headingLevelStart - 1, 6) as
1107
1230
  1 | 2 | 3 | 4 | 5 | 6;
1108
1231
  const headingChildren = node.children.map((c, i) =>
1109
1232
  renderInline(
@@ -1115,6 +1238,7 @@ function renderBlock(
1115
1238
  linkComponent,
1116
1239
  inlinePlugins,
1117
1240
  components,
1241
+ preparedPlugins,
1118
1242
  ),
1119
1243
  );
1120
1244
  // Only top-level headings get an id: the map is built from the same
@@ -1166,6 +1290,7 @@ function renderBlock(
1166
1290
  linkComponent,
1167
1291
  inlinePlugins,
1168
1292
  components,
1293
+ preparedPlugins,
1169
1294
  ),
1170
1295
  );
1171
1296
  const ParagraphComp = components?.paragraph;
@@ -1203,20 +1328,17 @@ function renderBlock(
1203
1328
  </div>
1204
1329
  );
1205
1330
  }
1206
- case 'codeblock': {
1331
+ case 'code': {
1207
1332
  // Track codeblock content in cursor for accurate character counting
1208
- cursor.offset += node.content.length;
1333
+ cursor.offset += node.value.length;
1334
+ const language = getMarkdownAstLegacyCodeLanguage(node) ?? 'plaintext';
1209
1335
  const CodeBlockComp = components?.code;
1210
1336
  if (CodeBlockComp) {
1211
1337
  return (
1212
- <CodeBlockComp
1213
- key={index}
1214
- code={node.content}
1215
- language={node.language}
1216
- />
1338
+ <CodeBlockComp key={index} code={node.value} language={language} />
1217
1339
  );
1218
1340
  }
1219
- return (
1341
+ const fallback = (
1220
1342
  <div
1221
1343
  key={index}
1222
1344
  {...mergeProps(
@@ -1229,8 +1351,8 @@ function renderBlock(
1229
1351
  ),
1230
1352
  )}>
1231
1353
  <CodeBlock
1232
- code={node.content}
1233
- language={node.language}
1354
+ code={node.value}
1355
+ language={language}
1234
1356
  isCollapsible
1235
1357
  xstyle={[
1236
1358
  contentWidthValue != null
@@ -1241,6 +1363,36 @@ function renderBlock(
1241
1363
  />
1242
1364
  </div>
1243
1365
  );
1366
+ const proposal = getMarkdownFenceProposal(node);
1367
+ if (proposal == null) {
1368
+ return fallback;
1369
+ }
1370
+ const renderer = getMarkdownExtensionRenderer(
1371
+ preparedPlugins,
1372
+ proposal.node,
1373
+ );
1374
+ if (renderer == null) {
1375
+ return fallback;
1376
+ }
1377
+ try {
1378
+ const rendered = renderer.render({node: proposal.node});
1379
+ if (rendered == null || typeof rendered === 'boolean') {
1380
+ return fallback;
1381
+ }
1382
+ return (
1383
+ <MarkdownPluginBoundary
1384
+ key={index}
1385
+ pluginName={proposal.node.plugin}
1386
+ resetKey={proposal.node}
1387
+ resetRenderer={renderer.render}
1388
+ fallback={fallback}>
1389
+ <Suspense fallback={fallback}>{rendered}</Suspense>
1390
+ </MarkdownPluginBoundary>
1391
+ );
1392
+ } catch (error) {
1393
+ reportMarkdownPluginFailure(proposal.node.plugin, 'render', error);
1394
+ return fallback;
1395
+ }
1244
1396
  }
1245
1397
  case 'math': {
1246
1398
  cursor.offset += node.value.length;
@@ -1272,6 +1424,7 @@ function renderBlock(
1272
1424
  linkComponent,
1273
1425
  inlinePlugins,
1274
1426
  components,
1427
+ preparedPlugins,
1275
1428
  t,
1276
1429
  ),
1277
1430
  );
@@ -1307,6 +1460,7 @@ function renderBlock(
1307
1460
  linkComponent,
1308
1461
  inlinePlugins,
1309
1462
  components,
1463
+ preparedPlugins,
1310
1464
  t,
1311
1465
  ),
1312
1466
  )}
@@ -1316,11 +1470,12 @@ function renderBlock(
1316
1470
  case 'list': {
1317
1471
  // Detect task lists: all items have a checked state
1318
1472
  const isTaskList =
1319
- node.items.length > 0 && node.items.every(item => item.checked != null);
1473
+ node.children.length > 0 &&
1474
+ node.children.every(item => item.checked != null);
1320
1475
 
1321
1476
  if (isTaskList) {
1322
1477
  // Extract labels from task items — render as rich inline content
1323
- const checkedValues = node.items
1478
+ const checkedValues = node.children
1324
1479
  .map((item, i) => ({item, key: `task-${i}`}))
1325
1480
  .filter(({item}) => item.checked)
1326
1481
  .map(({key}) => key);
@@ -1343,7 +1498,7 @@ function renderBlock(
1343
1498
  xstyle={styles.blockIndent}
1344
1499
  isReadOnly
1345
1500
  density="compact">
1346
- {node.items.map((item, i) => {
1501
+ {node.children.map((item, i) => {
1347
1502
  const firstChild = item.children[0];
1348
1503
  const isInline =
1349
1504
  item.children.length === 1 &&
@@ -1361,6 +1516,7 @@ function renderBlock(
1361
1516
  linkComponent,
1362
1517
  inlinePlugins,
1363
1518
  components,
1519
+ preparedPlugins,
1364
1520
  ),
1365
1521
  )}
1366
1522
  </>
@@ -1381,6 +1537,7 @@ function renderBlock(
1381
1537
  linkComponent,
1382
1538
  inlinePlugins,
1383
1539
  components,
1540
+ preparedPlugins,
1384
1541
  t,
1385
1542
  ),
1386
1543
  )}
@@ -1423,7 +1580,7 @@ function renderBlock(
1423
1580
  density="compact"
1424
1581
  start={node.ordered ? node.start : undefined}
1425
1582
  xstyle={styles.blockIndent}>
1426
- {node.items.map((item, i) => {
1583
+ {node.children.map((item, i) => {
1427
1584
  const firstChild = item.children[0];
1428
1585
  const isInline =
1429
1586
  item.children.length === 1 && firstChild?.type === 'paragraph';
@@ -1443,6 +1600,7 @@ function renderBlock(
1443
1600
  linkComponent,
1444
1601
  inlinePlugins,
1445
1602
  components,
1603
+ preparedPlugins,
1446
1604
  ),
1447
1605
  )}
1448
1606
  </>
@@ -1463,6 +1621,7 @@ function renderBlock(
1463
1621
  linkComponent,
1464
1622
  inlinePlugins,
1465
1623
  components,
1624
+ preparedPlugins,
1466
1625
  t,
1467
1626
  ),
1468
1627
  )}
@@ -1483,6 +1642,7 @@ function renderBlock(
1483
1642
  }
1484
1643
  case 'table': {
1485
1644
  const colMinWidths = computeTableColumnMinWidths(node);
1645
+ const [header, ...rows] = node.children;
1486
1646
 
1487
1647
  return (
1488
1648
  <div
@@ -1512,14 +1672,14 @@ function renderBlock(
1512
1672
  <Table dividers="rows" textOverflow="wrap">
1513
1673
  <TableHeader>
1514
1674
  <TableRow>
1515
- {node.headers.map((h, i) => (
1675
+ {header?.children.map((h, i) => (
1516
1676
  <TableHeaderCell
1517
1677
  // eslint-disable-next-line @eslint-react/no-array-index-key -- markdown table columns are positional by definition
1518
1678
  key={i}
1519
1679
  xstyle={[
1520
1680
  dynamicStyles.cellMinWidth(`${colMinWidths[i]}px`),
1521
- node.alignments[i] === 'center' && cellAlignStyles.center,
1522
- node.alignments[i] === 'right' && cellAlignStyles.end,
1681
+ node.align[i] === 'center' && cellAlignStyles.center,
1682
+ node.align[i] === 'right' && cellAlignStyles.end,
1523
1683
  ]}>
1524
1684
  {h.children.map((c, j) =>
1525
1685
  renderInline(
@@ -1531,6 +1691,7 @@ function renderBlock(
1531
1691
  linkComponent,
1532
1692
  inlinePlugins,
1533
1693
  components,
1694
+ preparedPlugins,
1534
1695
  ),
1535
1696
  )}
1536
1697
  </TableHeaderCell>
@@ -1538,14 +1699,14 @@ function renderBlock(
1538
1699
  </TableRow>
1539
1700
  </TableHeader>
1540
1701
  <TableBody>
1541
- {node.rows.map((row, i) => {
1542
- const cells = row.map((cell, j) => (
1702
+ {rows.map((row, i) => {
1703
+ const cells = row.children.map((cell, j) => (
1543
1704
  <TableCell
1544
1705
  // eslint-disable-next-line @eslint-react/no-array-index-key -- markdown table cells are positional by row and column
1545
1706
  key={j}
1546
1707
  xstyle={[
1547
- node.alignments[j] === 'center' && cellAlignStyles.center,
1548
- node.alignments[j] === 'right' && cellAlignStyles.end,
1708
+ node.align[j] === 'center' && cellAlignStyles.center,
1709
+ node.align[j] === 'right' && cellAlignStyles.end,
1549
1710
  ]}>
1550
1711
  {cell.children.map((c, k) =>
1551
1712
  renderInline(
@@ -1557,6 +1718,7 @@ function renderBlock(
1557
1718
  linkComponent,
1558
1719
  inlinePlugins,
1559
1720
  components,
1721
+ preparedPlugins,
1560
1722
  ),
1561
1723
  )}
1562
1724
  </TableCell>
@@ -1574,7 +1736,47 @@ function renderBlock(
1574
1736
  </div>
1575
1737
  );
1576
1738
  }
1577
- case 'hr': {
1739
+ case 'extension': {
1740
+ const renderer = getMarkdownExtensionRenderer(preparedPlugins, node);
1741
+ const fallbackText =
1742
+ node.source ?? markdownExtensionText(preparedPlugins, node);
1743
+ const fallback = wrapTextWithFade(fallbackText, cursor, index);
1744
+ let content: React.ReactNode = fallback;
1745
+ if (renderer != null) {
1746
+ try {
1747
+ const rendered = renderer.render({node});
1748
+ content = (
1749
+ <MarkdownPluginBoundary
1750
+ pluginName={node.plugin}
1751
+ resetKey={node}
1752
+ resetRenderer={renderer.render}
1753
+ fallback={fallback}>
1754
+ <Suspense fallback={fallback}>{rendered}</Suspense>
1755
+ </MarkdownPluginBoundary>
1756
+ );
1757
+ } catch (error) {
1758
+ reportMarkdownPluginFailure(node.plugin, 'render', error);
1759
+ }
1760
+ }
1761
+ return (
1762
+ <div
1763
+ key={index}
1764
+ {...stylex.props(
1765
+ spacing,
1766
+ contentWidthValue != null
1767
+ ? dynamicStyles.proseWidth(contentWidthValue)
1768
+ : null,
1769
+ contentAlign !== 'start'
1770
+ ? dynamicStyles.proseAlign(ALIGN_MARGIN[contentAlign])
1771
+ : null,
1772
+ isFirst && styles.noMarginBlockStart,
1773
+ isLast && styles.noMarginBlockEnd,
1774
+ )}>
1775
+ {content}
1776
+ </div>
1777
+ );
1778
+ }
1779
+ case 'thematicBreak': {
1578
1780
  const HrComp = components?.hr;
1579
1781
  if (HrComp) {
1580
1782
  return <HrComp key={index} />;
@@ -1595,7 +1797,7 @@ function renderBlock(
1595
1797
  );
1596
1798
  }
1597
1799
  case 'image': {
1598
- const safeSrc = sanitizeUrl(node.src);
1800
+ const safeSrc = sanitizeMarkdownUrl(node.url);
1599
1801
  if (safeSrc == null) {
1600
1802
  return (
1601
1803
  <div
@@ -1649,7 +1851,9 @@ function renderBlock(
1649
1851
  * </Markdown>
1650
1852
  * ```
1651
1853
  */
1652
- export function Markdown({
1854
+ export function Markdown<
1855
+ const Plugins extends ReadonlyArray<MarkdownPluginEntry> = readonly [],
1856
+ >({
1653
1857
  ref,
1654
1858
  children,
1655
1859
  display = 'block',
@@ -1662,6 +1866,7 @@ export function Markdown({
1662
1866
  contentWidth = 680,
1663
1867
  contentAlign = 'start',
1664
1868
  components,
1869
+ plugins,
1665
1870
  inlinePlugins,
1666
1871
  autolink,
1667
1872
  xstyle,
@@ -1669,7 +1874,7 @@ export function Markdown({
1669
1874
  style,
1670
1875
  'data-testid': testId,
1671
1876
  ...props
1672
- }: MarkdownProps): React.ReactElement {
1877
+ }: MarkdownProps<Plugins>): React.ReactElement {
1673
1878
  const t = useTranslator();
1674
1879
  const LinkComponent = useLinkComponent();
1675
1880
  // Derive the set of source IDs for the parser (stable across renders when sources don't change)
@@ -1678,14 +1883,30 @@ export function Markdown({
1678
1883
  [sources],
1679
1884
  );
1680
1885
 
1886
+ const preparedPlugins = useMemo(() => {
1887
+ if (plugins == null) {
1888
+ return undefined;
1889
+ }
1890
+ try {
1891
+ return prepareMarkdownPlugins(plugins);
1892
+ } catch (error) {
1893
+ reportMarkdownPluginFailure('configuration', 'transform', error);
1894
+ return undefined;
1895
+ }
1896
+ }, [plugins]);
1897
+ const syntaxPlugins = useStableMarkdownSyntaxEntries(preparedPlugins);
1681
1898
  const hasMathRenderer = components?.math != null;
1682
- const legacyParseOptions = useMemo<ParseOptions>(
1683
- () => ({sourceIds, autolink}),
1684
- [sourceIds, autolink],
1899
+ const legacyParseOptions = useMemo<
1900
+ ParseOptions<ReadonlyArray<MarkdownPluginEntry>>
1901
+ >(
1902
+ () => ({sourceIds, autolink, plugins: syntaxPlugins}),
1903
+ [sourceIds, autolink, syntaxPlugins],
1685
1904
  );
1686
- const mathParseOptions = useMemo<MathParseOptions>(
1687
- () => ({sourceIds, autolink, math: true}),
1688
- [sourceIds, autolink],
1905
+ const mathParseOptions = useMemo<
1906
+ MathParseOptions<ReadonlyArray<MarkdownPluginEntry>>
1907
+ >(
1908
+ () => ({sourceIds, autolink, math: true, plugins: syntaxPlugins}),
1909
+ [sourceIds, autolink, syntaxPlugins],
1689
1910
  );
1690
1911
 
1691
1912
  // Smooth bursty streamed chunks into a steady character-by-character reveal.
@@ -1708,7 +1929,7 @@ export function Markdown({
1708
1929
  prevMathRef.current = hasMathRenderer;
1709
1930
  }
1710
1931
 
1711
- const blocks = useMemo(() => {
1932
+ const parsedBlocks = useMemo(() => {
1712
1933
  if (display === 'inline') {
1713
1934
  return [];
1714
1935
  }
@@ -1719,21 +1940,25 @@ export function Markdown({
1719
1940
  }
1720
1941
  const options = hasMathRenderer ? mathParseOptions : legacyParseOptions;
1721
1942
  const input = trimStreamingArtifacts(smoothedText, options);
1722
- return hasMathRenderer
1723
- ? parseMarkdownIncremental(
1724
- input,
1725
- incrementalStateRef.current as IncrementalState<true>,
1726
- mathParseOptions,
1727
- )
1728
- : parseMarkdownIncremental(
1729
- input,
1730
- incrementalStateRef.current as IncrementalState<false>,
1731
- legacyParseOptions,
1732
- );
1943
+ return (
1944
+ hasMathRenderer
1945
+ ? parseMarkdownAstIncremental(
1946
+ input,
1947
+ incrementalStateRef.current,
1948
+ mathParseOptions,
1949
+ )
1950
+ : parseMarkdownAstIncremental(
1951
+ input,
1952
+ incrementalStateRef.current,
1953
+ legacyParseOptions,
1954
+ )
1955
+ ).children;
1733
1956
  }
1734
- return hasMathRenderer
1735
- ? parseMarkdown(children, mathParseOptions)
1736
- : parseMarkdown(children, legacyParseOptions);
1957
+ return (
1958
+ hasMathRenderer
1959
+ ? parseMarkdownAst(children, mathParseOptions)
1960
+ : parseMarkdownAst(children, legacyParseOptions)
1961
+ ).children;
1737
1962
  }, [
1738
1963
  display,
1739
1964
  smoothedText,
@@ -1744,6 +1969,24 @@ export function Markdown({
1744
1969
  legacyParseOptions,
1745
1970
  ]);
1746
1971
 
1972
+ const transformSource = isStreaming
1973
+ ? trimStreamingArtifacts(
1974
+ smoothedText,
1975
+ hasMathRenderer ? mathParseOptions : legacyParseOptions,
1976
+ )
1977
+ : children;
1978
+ const blocks = useMemo(
1979
+ () =>
1980
+ applyMarkdownTransforms(
1981
+ {type: 'root', children: parsedBlocks},
1982
+ preparedPlugins,
1983
+ transformSource,
1984
+ !isStreaming,
1985
+ 'block',
1986
+ ).children,
1987
+ [parsedBlocks, preparedPlugins, transformSource, isStreaming],
1988
+ );
1989
+
1747
1990
  // Assign each top-level heading the slug that parseOutlineFromMarkdown
1748
1991
  // would derive for it, so Outline hash links built from the same source
1749
1992
  // always find a matching DOM id. Mirrors that function's traversal exactly:
@@ -1754,18 +1997,20 @@ export function Markdown({
1754
1997
  if (display === 'inline' || blocks.length === 0) {
1755
1998
  return undefined;
1756
1999
  }
1757
- const map = new Map<BlockNodeWithMath, string>();
2000
+ const map = new Map<RenderBlockNode, string>();
1758
2001
  const counts = new Map<string, number>();
1759
2002
  for (const block of blocks) {
1760
2003
  if (block.type === 'heading') {
1761
- const label = inlineText(block.children).trim();
2004
+ const label = markdownAstText(block.children, node =>
2005
+ markdownExtensionText(preparedPlugins, node),
2006
+ ).trim();
1762
2007
  map.set(block, uniqueSlug(slugify(label), counts));
1763
2008
  }
1764
2009
  }
1765
2010
  return map;
1766
- }, [display, blocks]);
2011
+ }, [display, blocks, preparedPlugins]);
1767
2012
 
1768
- const inlineNodes = useMemo(() => {
2013
+ const parsedInlineNodes = useMemo(() => {
1769
2014
  if (display !== 'inline') {
1770
2015
  return [];
1771
2016
  }
@@ -1774,8 +2019,8 @@ export function Markdown({
1774
2019
  ? trimStreamingArtifacts(smoothedText, options)
1775
2020
  : children;
1776
2021
  return hasMathRenderer
1777
- ? parseInline(input, mathParseOptions)
1778
- : parseInline(input, legacyParseOptions);
2022
+ ? parseInlineAst(input, mathParseOptions)
2023
+ : parseInlineAst(input, legacyParseOptions);
1779
2024
  }, [
1780
2025
  display,
1781
2026
  smoothedText,
@@ -1786,6 +2031,38 @@ export function Markdown({
1786
2031
  legacyParseOptions,
1787
2032
  ]);
1788
2033
 
2034
+ const inlineTransformSource = isStreaming
2035
+ ? trimStreamingArtifacts(
2036
+ smoothedText,
2037
+ hasMathRenderer ? mathParseOptions : legacyParseOptions,
2038
+ )
2039
+ : children;
2040
+ const inlineNodes = useMemo(() => {
2041
+ if (display !== 'inline') {
2042
+ return [];
2043
+ }
2044
+ const transformed = applyMarkdownTransforms(
2045
+ {
2046
+ type: 'root',
2047
+ children: [{type: 'paragraph', children: parsedInlineNodes}],
2048
+ },
2049
+ preparedPlugins,
2050
+ inlineTransformSource,
2051
+ !isStreaming,
2052
+ 'inline',
2053
+ );
2054
+ const paragraph = transformed.children[0];
2055
+ return paragraph?.type === 'paragraph'
2056
+ ? [...paragraph.children]
2057
+ : parsedInlineNodes;
2058
+ }, [
2059
+ display,
2060
+ parsedInlineNodes,
2061
+ preparedPlugins,
2062
+ inlineTransformSource,
2063
+ isStreaming,
2064
+ ]);
2065
+
1789
2066
  // Track recent boundaries for stacked fade-in animation.
1790
2067
  // The number of spans needed = ceil(animationDuration / tickInterval).
1791
2068
  // useStreamingText ticks every ~tickMs, and the fade runs for
@@ -1800,8 +2077,8 @@ export function Markdown({
1800
2077
  return Math.min(Math.ceil(duration / tickMs), 12);
1801
2078
  }, [token]);
1802
2079
 
1803
- const prevBlocksRef = useRef<BlockNodeWithMath[]>([]);
1804
- const prevInlineNodesRef = useRef<InlineNodeWithMath[]>([]);
2080
+ const prevBlocksRef = useRef<ReadonlyArray<RenderBlockNode>>([]);
2081
+ const prevInlineNodesRef = useRef<ReadonlyArray<RenderInlineNode>>([]);
1805
2082
  const boundariesRef = useRef<number[]>([]);
1806
2083
  const smoothedLen = smoothedText.length;
1807
2084
  const boundaries = useMemo(() => {
@@ -1850,6 +2127,7 @@ export function Markdown({
1850
2127
  LinkComponent,
1851
2128
  inlinePlugins,
1852
2129
  components,
2130
+ preparedPlugins,
1853
2131
  ),
1854
2132
  )}
1855
2133
  </span>
@@ -1895,6 +2173,7 @@ export function Markdown({
1895
2173
  LinkComponent,
1896
2174
  inlinePlugins,
1897
2175
  components,
2176
+ preparedPlugins,
1898
2177
  t,
1899
2178
  headingIdMap,
1900
2179
  ),