@astryxdesign/core 0.6.2 → 0.6.3-canary.01fa439

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 (384) 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/Chat/ChatComposer.d.ts.map +1 -1
  9. package/dist/Chat/ChatComposer.js +10 -5
  10. package/dist/Chat/ChatComposerDrawer.d.ts +19 -5
  11. package/dist/Chat/ChatComposerDrawer.d.ts.map +1 -1
  12. package/dist/Chat/ChatComposerDrawer.js +48 -23
  13. package/dist/Chat/ChatComposerInput.d.ts +11 -3
  14. package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
  15. package/dist/Chat/ChatComposerInput.js +63 -16
  16. package/dist/Chat/ChatDictationButton.d.ts +3 -2
  17. package/dist/Chat/ChatDictationButton.d.ts.map +1 -1
  18. package/dist/Chat/ChatDictationButton.js +6 -4
  19. package/dist/Chat/ChatSendButton.js +1 -1
  20. package/dist/Chat/index.d.ts +1 -1
  21. package/dist/Chat/index.d.ts.map +1 -1
  22. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  23. package/dist/CheckboxInput/CheckboxInput.js +12 -2
  24. package/dist/Collapsible/Collapsible.d.ts.map +1 -1
  25. package/dist/Collapsible/Collapsible.js +6 -1
  26. package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
  27. package/dist/CommandPalette/CommandPaletteInput.js +1 -1
  28. package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
  29. package/dist/ComplexSelector/ComplexSelector.js +1 -1
  30. package/dist/DateInput/DateInput.d.ts.map +1 -1
  31. package/dist/DateInput/DateInput.js +4 -4
  32. package/dist/DateInput/NativeDateField.d.ts.map +1 -1
  33. package/dist/DateInput/NativeDateField.js +16 -16
  34. package/dist/DateInput/TouchDateField.d.ts.map +1 -1
  35. package/dist/DateInput/TouchDateField.js +2 -2
  36. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  37. package/dist/DateRangeInput/DateRangeInput.js +4 -4
  38. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  39. package/dist/DateTimeInput/DateTimeInput.js +8 -8
  40. package/dist/DateTimeInput/TouchDateTimeField.d.ts.map +1 -1
  41. package/dist/DateTimeInput/TouchDateTimeField.js +4 -4
  42. package/dist/DateTimeInput/nativePickerSegmentStyles.d.ts +9 -3
  43. package/dist/DateTimeInput/nativePickerSegmentStyles.d.ts.map +1 -1
  44. package/dist/DateTimeInput/nativePickerSegmentStyles.js +3 -3
  45. package/dist/Dialog/DialogHeader.d.ts +8 -1
  46. package/dist/Dialog/DialogHeader.d.ts.map +1 -1
  47. package/dist/Dialog/DialogHeader.js +10 -1
  48. package/dist/Field/InputClearButton.d.ts +4 -2
  49. package/dist/Field/InputClearButton.d.ts.map +1 -1
  50. package/dist/Field/InputClearButton.js +4 -2
  51. package/dist/Field/PanelSearchInput.d.ts.map +1 -1
  52. package/dist/Field/PanelSearchInput.js +1 -1
  53. package/dist/FileInput/FileInput.d.ts.map +1 -1
  54. package/dist/FileInput/FileInput.js +7 -7
  55. package/dist/Kbd/Kbd.d.ts +5 -3
  56. package/dist/Kbd/Kbd.d.ts.map +1 -1
  57. package/dist/Kbd/Kbd.js +36 -42
  58. package/dist/Link/Link.d.ts.map +1 -1
  59. package/dist/Link/Link.js +6 -2
  60. package/dist/Markdown/Markdown.d.ts +8 -2
  61. package/dist/Markdown/Markdown.d.ts.map +1 -1
  62. package/dist/Markdown/Markdown.fr23.bench.d.ts +2 -0
  63. package/dist/Markdown/Markdown.fr23.bench.d.ts.map +1 -0
  64. package/dist/Markdown/Markdown.fr23.bench.js +150 -0
  65. package/dist/Markdown/Markdown.fr23.sampling.d.ts +2 -0
  66. package/dist/Markdown/Markdown.fr23.sampling.d.ts.map +1 -0
  67. package/dist/Markdown/Markdown.fr23.sampling.js +43 -0
  68. package/dist/Markdown/Markdown.js +262 -109
  69. package/dist/Markdown/ast.d.ts +162 -0
  70. package/dist/Markdown/ast.d.ts.map +1 -0
  71. package/dist/Markdown/ast.js +74 -0
  72. package/dist/Markdown/index.d.ts +5 -3
  73. package/dist/Markdown/index.d.ts.map +1 -1
  74. package/dist/Markdown/index.js +3 -2
  75. package/dist/Markdown/parser/index.d.ts +10 -0
  76. package/dist/Markdown/parser/index.d.ts.map +1 -0
  77. package/dist/Markdown/parser/index.js +10 -0
  78. package/dist/Markdown/parser.d.ts +126 -58
  79. package/dist/Markdown/parser.d.ts.map +1 -1
  80. package/dist/Markdown/parser.js +716 -167
  81. package/dist/Markdown/plugins/frontmatter.d.ts +32 -0
  82. package/dist/Markdown/plugins/frontmatter.d.ts.map +1 -0
  83. package/dist/Markdown/plugins/frontmatter.js +132 -0
  84. package/dist/Markdown/plugins/index.d.ts +16 -0
  85. package/dist/Markdown/plugins/index.d.ts.map +1 -0
  86. package/dist/Markdown/plugins/index.js +13 -0
  87. package/dist/Markdown/plugins/protocol.d.ts +191 -0
  88. package/dist/Markdown/plugins/protocol.d.ts.map +1 -0
  89. package/dist/Markdown/plugins/protocol.js +957 -0
  90. package/dist/Markdown/plugins/semanticFence.d.ts +36 -0
  91. package/dist/Markdown/plugins/semanticFence.d.ts.map +1 -0
  92. package/dist/Markdown/plugins/semanticFence.js +213 -0
  93. package/dist/Markdown/plugins/sourceDecoration.d.ts +76 -0
  94. package/dist/Markdown/plugins/sourceDecoration.d.ts.map +1 -0
  95. package/dist/Markdown/plugins/sourceDecoration.js +393 -0
  96. package/dist/Markdown/plugins/textTransform.d.ts +30 -0
  97. package/dist/Markdown/plugins/textTransform.d.ts.map +1 -0
  98. package/dist/Markdown/plugins/textTransform.js +374 -0
  99. package/dist/Markdown/remark.d.ts +196 -0
  100. package/dist/Markdown/remark.d.ts.map +1 -0
  101. package/dist/Markdown/remark.js +1062 -0
  102. package/dist/Markdown/url.d.ts +4 -0
  103. package/dist/Markdown/url.d.ts.map +1 -0
  104. package/dist/Markdown/url.js +25 -0
  105. package/dist/Markdown/utils.d.ts +4 -2
  106. package/dist/Markdown/utils.d.ts.map +1 -1
  107. package/dist/Markdown/utils.js +2 -1
  108. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  109. package/dist/NumberInput/NumberInput.js +4 -4
  110. package/dist/Outline/index.d.ts +1 -0
  111. package/dist/Outline/index.d.ts.map +1 -1
  112. package/dist/Outline/parseOutlineFromMarkdown.d.ts +7 -1
  113. package/dist/Outline/parseOutlineFromMarkdown.d.ts.map +1 -1
  114. package/dist/Outline/parseOutlineFromMarkdown.js +14 -8
  115. package/dist/Outline/useOutlineFromMarkdown.d.ts +3 -1
  116. package/dist/Outline/useOutlineFromMarkdown.d.ts.map +1 -1
  117. package/dist/Outline/useOutlineFromMarkdown.js +8 -3
  118. package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
  119. package/dist/PowerSearch/PowerSearchEditPopover.js +46 -30
  120. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  121. package/dist/ProgressBar/ProgressBar.js +36 -31
  122. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  123. package/dist/RadioList/RadioListItem.js +13 -1
  124. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  125. package/dist/SegmentedControl/SegmentedControlItem.js +5 -5
  126. package/dist/Selector/Selector.d.ts +9 -7
  127. package/dist/Selector/Selector.d.ts.map +1 -1
  128. package/dist/Selector/Selector.js +7 -5
  129. package/dist/SideNav/SideNav.d.ts +2 -1
  130. package/dist/SideNav/SideNav.d.ts.map +1 -1
  131. package/dist/SideNav/SideNav.js +9 -3
  132. package/dist/Slider/Slider.d.ts.map +1 -1
  133. package/dist/Slider/Slider.js +28 -12
  134. package/dist/Spinner/Spinner.d.ts.map +1 -1
  135. package/dist/Spinner/Spinner.js +23 -7
  136. package/dist/Switch/Switch.d.ts.map +1 -1
  137. package/dist/Switch/Switch.js +11 -0
  138. package/dist/TabList/Tab.d.ts +1 -1
  139. package/dist/TabList/Tab.d.ts.map +1 -1
  140. package/dist/TabList/Tab.js +20 -7
  141. package/dist/Table/Table.d.ts +1 -0
  142. package/dist/Table/Table.d.ts.map +1 -1
  143. package/dist/Table/Table.js +29 -13
  144. package/dist/TextArea/TextArea.d.ts.map +1 -1
  145. package/dist/TextArea/TextArea.js +1 -1
  146. package/dist/TextInput/TextInput.d.ts.map +1 -1
  147. package/dist/TextInput/TextInput.js +2 -2
  148. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  149. package/dist/TimeInput/TimeInput.js +4 -4
  150. package/dist/ToggleButton/ToggleButton.d.ts +9 -6
  151. package/dist/ToggleButton/ToggleButton.d.ts.map +1 -1
  152. package/dist/ToggleButton/ToggleButton.js +15 -27
  153. package/dist/TreeList/TreeList.d.ts.map +1 -1
  154. package/dist/TreeList/TreeList.js +3 -0
  155. package/dist/TreeList/TreeListItem.d.ts +6 -1
  156. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  157. package/dist/TreeList/TreeListItem.js +12 -2
  158. package/dist/TreeList/TreeListTypes.d.ts +8 -1
  159. package/dist/TreeList/TreeListTypes.d.ts.map +1 -1
  160. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  161. package/dist/Typeahead/BaseTypeahead.js +16 -7
  162. package/dist/astryx.css +18 -7
  163. package/dist/hooks/scrollKeyboardDelegation.d.ts +3 -0
  164. package/dist/hooks/scrollKeyboardDelegation.d.ts.map +1 -0
  165. package/dist/hooks/scrollKeyboardDelegation.js +146 -0
  166. package/dist/hooks/useScrollableArea.d.ts +6 -2
  167. package/dist/hooks/useScrollableArea.d.ts.map +1 -1
  168. package/dist/hooks/useScrollableArea.js +17 -5
  169. package/dist/theme/generateThemeRules.d.ts.map +1 -1
  170. package/dist/theme/generateThemeRules.js +10 -0
  171. package/dist/utils/interactionOverlay.stylex.d.ts +8 -0
  172. package/dist/utils/interactionOverlay.stylex.d.ts.map +1 -1
  173. package/dist/utils/interactionOverlay.stylex.js +9 -0
  174. package/locales/af-ZA.json +530 -1
  175. package/locales/ar-SA.json +490 -1
  176. package/locales/ca-ES.json +514 -1
  177. package/locales/cs-CZ.json +498 -1
  178. package/locales/da-DK.json +522 -1
  179. package/locales/de-DE.json +522 -1
  180. package/locales/el-GR.json +498 -1
  181. package/locales/es-ES.json +510 -1
  182. package/locales/fi-FI.json +498 -1
  183. package/locales/fr-FR.json +534 -1
  184. package/locales/he-IL.json +498 -1
  185. package/locales/hu-HU.json +498 -1
  186. package/locales/it-IT.json +510 -1
  187. package/locales/ja-JP.json +490 -1
  188. package/locales/ko-KR.json +498 -1
  189. package/locales/nl-NL.json +534 -1
  190. package/locales/no-NO.json +510 -1
  191. package/locales/pl-PL.json +506 -1
  192. package/locales/pt-BR.json +506 -1
  193. package/locales/pt-PT.json +506 -1
  194. package/locales/ro-RO.json +506 -1
  195. package/locales/ru-RU.json +498 -1
  196. package/locales/sr-SP.json +498 -1
  197. package/locales/sv-SE.json +502 -1
  198. package/locales/tr-TR.json +498 -1
  199. package/locales/uk-UA.json +498 -1
  200. package/locales/vi-VN.json +502 -1
  201. package/locales/zh-CN.json +490 -1
  202. package/locales/zh-TW.json +490 -1
  203. package/package.json +19 -4
  204. package/src/BottomSheet/BottomSheet.doc.mjs +8 -1
  205. package/src/BottomSheet/BottomSheet.spec.md +46 -20
  206. package/src/BottomSheet/BottomSheet.test.tsx +6 -3
  207. package/src/BottomSheet/BottomSheet.tsx +3 -1
  208. package/src/BottomSheet/BottomSheetKeyboard.test.tsx +195 -0
  209. package/src/BottomSheet/BottomSheetPanel.test.tsx +11 -1
  210. package/src/BottomSheet/BottomSheetPanel.tsx +49 -16
  211. package/src/BottomSheet/__tests__/BottomSheetKeyboard.a11y.browser.spec.ts +344 -0
  212. package/src/Button/__tests__/Button.a11y.chromium.spec.ts +17 -1
  213. package/src/Button/__tests__/Button.a11y.known-failures.ts +0 -29
  214. package/src/Button/__tests__/Button.a11y.renders.tsx +9 -2
  215. package/src/Button/__tests__/Button.a11y.states.ts +9 -0
  216. package/src/Chat/ChatComposer.doc.mjs +14 -0
  217. package/src/Chat/ChatComposer.spec.md +230 -0
  218. package/src/Chat/ChatComposer.test.tsx +57 -0
  219. package/src/Chat/ChatComposer.tsx +21 -9
  220. package/src/Chat/ChatComposerDrawer.doc.mjs +63 -5
  221. package/src/Chat/ChatComposerDrawer.spec.md +267 -0
  222. package/src/Chat/ChatComposerDrawer.test.tsx +81 -12
  223. package/src/Chat/ChatComposerDrawer.tsx +38 -9
  224. package/src/Chat/ChatComposerInput.doc.mjs +114 -26
  225. package/src/Chat/ChatComposerInput.spec.md +185 -0
  226. package/src/Chat/ChatComposerInput.test.tsx +113 -3
  227. package/src/Chat/ChatComposerInput.tsx +78 -13
  228. package/src/Chat/ChatComposerTokenElement.spec.md +152 -0
  229. package/src/Chat/ChatComposerTokenElement.test.tsx +85 -0
  230. package/src/Chat/ChatDictationButton.doc.mjs +3 -3
  231. package/src/Chat/ChatDictationButton.spec.md +160 -0
  232. package/src/Chat/ChatDictationButton.test.tsx +92 -27
  233. package/src/Chat/ChatDictationButton.tsx +8 -6
  234. package/src/Chat/ChatSendButton.test.tsx +1 -4
  235. package/src/Chat/ChatSendButton.tsx +1 -1
  236. package/src/Chat/index.ts +1 -0
  237. package/src/CheckboxInput/CheckboxInput.doc.mjs +11 -0
  238. package/src/CheckboxInput/CheckboxInput.test.tsx +34 -0
  239. package/src/CheckboxInput/CheckboxInput.tsx +21 -1
  240. package/src/ClickableCard/ClickableCard.test.tsx +102 -5
  241. package/src/Collapsible/Collapsible.doc.mjs +11 -0
  242. package/src/Collapsible/Collapsible.test.tsx +21 -0
  243. package/src/Collapsible/Collapsible.tsx +5 -0
  244. package/src/CommandPalette/CommandPaletteInput.tsx +6 -1
  245. package/src/ComplexSelector/ComplexSelector.tsx +6 -1
  246. package/src/DateInput/DateInput.tsx +6 -1
  247. package/src/DateInput/NativeDateField.tsx +24 -8
  248. package/src/DateInput/TouchDateField.tsx +6 -2
  249. package/src/DateRangeInput/DateRangeInput.tsx +6 -1
  250. package/src/DateTimeInput/DateTimeInput.tsx +6 -1
  251. package/src/DateTimeInput/TouchDateTimeField.tsx +6 -1
  252. package/src/DateTimeInput/nativePickerSegmentStyles.ts +24 -8
  253. package/src/Dialog/DialogHeader.doc.mjs +12 -0
  254. package/src/Dialog/DialogHeader.test.tsx +97 -0
  255. package/src/Dialog/DialogHeader.tsx +25 -3
  256. package/src/Dialog/modules/DialogHeader.spec.md +75 -38
  257. package/src/Field/InputClearButton.test.tsx +17 -3
  258. package/src/Field/InputClearButton.tsx +5 -2
  259. package/src/Field/PanelSearchInput.tsx +5 -2
  260. package/src/FieldStatus/__tests__/StatusMessage.a11y.states.ts +1 -1
  261. package/src/FileInput/FileInput.tsx +11 -2
  262. package/src/Kbd/Kbd.doc.mjs +3 -3
  263. package/src/Kbd/Kbd.test.tsx +57 -1
  264. package/src/Kbd/Kbd.tsx +57 -37
  265. package/src/Layout/LayoutFooter.doc.mjs +17 -0
  266. package/src/Link/Link.doc.mjs +11 -0
  267. package/src/Link/Link.test.tsx +24 -0
  268. package/src/Link/Link.tsx +5 -0
  269. package/src/List/List.spec.md +200 -0
  270. package/src/Markdown/Markdown.doc.mjs +272 -0
  271. package/src/Markdown/Markdown.fr23.bench.ts +174 -0
  272. package/src/Markdown/Markdown.fr23.perf.test.ts +249 -0
  273. package/src/Markdown/Markdown.fr23.sampling.ts +49 -0
  274. package/src/Markdown/Markdown.helpers.perf.test.ts +489 -0
  275. package/src/Markdown/Markdown.public.test.ts +126 -3
  276. package/src/Markdown/Markdown.spec.md +197 -44
  277. package/src/Markdown/Markdown.test.tsx +11 -0
  278. package/src/Markdown/Markdown.tsx +414 -135
  279. package/src/Markdown/ast.test.ts +307 -0
  280. package/src/Markdown/ast.ts +337 -0
  281. package/src/Markdown/incremental.test.ts +12 -0
  282. package/src/Markdown/index.ts +37 -1
  283. package/src/Markdown/modules/remark.spec.md +269 -0
  284. package/src/Markdown/parser/index.ts +64 -0
  285. package/src/Markdown/parser.public.test.ts +103 -0
  286. package/src/Markdown/parser.ts +1180 -235
  287. package/src/Markdown/plugins/frontmatter.test.tsx +125 -0
  288. package/src/Markdown/plugins/frontmatter.ts +198 -0
  289. package/src/Markdown/plugins/index.ts +53 -0
  290. package/src/Markdown/plugins/protocol.test.tsx +1022 -0
  291. package/src/Markdown/plugins/protocol.ts +1618 -0
  292. package/src/Markdown/plugins/semanticFence.test.tsx +546 -0
  293. package/src/Markdown/plugins/semanticFence.ts +424 -0
  294. package/src/Markdown/plugins/sourceDecoration.test.tsx +799 -0
  295. package/src/Markdown/plugins/sourceDecoration.ts +539 -0
  296. package/src/Markdown/plugins/textTransform.test.ts +467 -0
  297. package/src/Markdown/plugins/textTransform.ts +554 -0
  298. package/src/Markdown/remark.test.tsx +1877 -0
  299. package/src/Markdown/remark.ts +1574 -0
  300. package/src/Markdown/url.ts +30 -0
  301. package/src/Markdown/utils.ts +13 -1
  302. package/src/MultiSelector/MultiSelector.test.tsx +1 -35
  303. package/src/MultiSelector/__tests__/MultiSelector.listbox.a11y.test.tsx +128 -0
  304. package/src/NumberInput/NumberInput.tsx +6 -1
  305. package/src/Outline/Outline.doc.mjs +7 -5
  306. package/src/Outline/Outline.spec.md +1 -1
  307. package/src/Outline/index.ts +1 -0
  308. package/src/Outline/modules/parseOutlineFromMarkdown.spec.md +142 -0
  309. package/src/Outline/parseOutlineFromMarkdown.ts +40 -10
  310. package/src/Outline/useOutlineFromMarkdown.ts +15 -3
  311. package/src/PowerSearch/PowerSearch.doc.mjs +2 -2
  312. package/src/PowerSearch/PowerSearchEditPopover.test.tsx +150 -1
  313. package/src/PowerSearch/PowerSearchEditPopover.tsx +51 -28
  314. package/src/ProgressBar/ProgressBar.test.tsx +29 -11
  315. package/src/ProgressBar/ProgressBar.tsx +59 -72
  316. package/src/RadioList/RadioList.doc.mjs +11 -0
  317. package/src/RadioList/RadioList.test.tsx +32 -0
  318. package/src/RadioList/RadioListItem.tsx +25 -1
  319. package/src/ScrollableArea/modules/useScrollableArea.spec.md +50 -22
  320. package/src/SegmentedControl/SegmentedControl.doc.mjs +2 -2
  321. package/src/SegmentedControl/SegmentedControl.test.tsx +31 -0
  322. package/src/SegmentedControl/SegmentedControlItem.tsx +6 -9
  323. package/src/Selector/Selector.doc.mjs +2 -2
  324. package/src/Selector/Selector.spec.md +51 -49
  325. package/src/Selector/Selector.test.tsx +61 -55
  326. package/src/Selector/Selector.tsx +23 -14
  327. package/src/Selector/__tests__/Listbox.a11y.chromium.spec.ts +130 -0
  328. package/src/Selector/__tests__/Listbox.a11y.dom.ts +38 -0
  329. package/src/Selector/__tests__/Listbox.a11y.inventory.test.ts +63 -0
  330. package/src/Selector/__tests__/Listbox.a11y.known-failures.ts +37 -0
  331. package/src/Selector/__tests__/Listbox.a11y.states.ts +232 -0
  332. package/src/Selector/__tests__/Selector.listbox.a11y.test.tsx +121 -0
  333. package/src/SideNav/SideNav.doc.mjs +1 -1
  334. package/src/SideNav/SideNav.test.tsx +10 -0
  335. package/src/SideNav/SideNav.tsx +14 -2
  336. package/src/Slider/Slider.doc.mjs +17 -3
  337. package/src/Slider/Slider.test.tsx +221 -0
  338. package/src/Slider/Slider.tsx +40 -9
  339. package/src/Spinner/Spinner.test.tsx +4 -0
  340. package/src/Spinner/Spinner.tsx +31 -18
  341. package/src/Switch/Switch.doc.mjs +11 -0
  342. package/src/Switch/Switch.test.tsx +16 -0
  343. package/src/Switch/Switch.tsx +28 -0
  344. package/src/TabList/Tab.tsx +35 -7
  345. package/src/TabList/TabList.doc.mjs +11 -0
  346. package/src/TabList/TabList.spec.md +204 -0
  347. package/src/TabList/TabList.test.tsx +66 -0
  348. package/src/TabList/__tests__/Tabs.a11y.known-failures.ts +1 -34
  349. package/src/Table/Table.doc.mjs +1 -1
  350. package/src/Table/Table.spec.md +54 -40
  351. package/src/Table/Table.test.tsx +83 -10
  352. package/src/Table/Table.tsx +30 -22
  353. package/src/Table/__tests__/TableScroll.a11y.chromium.spec.ts +137 -0
  354. package/src/TextArea/TextArea.tsx +6 -1
  355. package/src/TextInput/TextInput.test.tsx +20 -1
  356. package/src/TextInput/TextInput.tsx +6 -1
  357. package/src/TimeInput/TimeInput.tsx +6 -1
  358. package/src/ToggleButton/ToggleButton.doc.mjs +3 -3
  359. package/src/ToggleButton/ToggleButton.test.tsx +299 -4
  360. package/src/ToggleButton/ToggleButton.tsx +26 -33
  361. package/src/ToggleButton/ToggleButtonGroup.doc.mjs +3 -3
  362. package/src/ToggleButton/__tests__/ToggleButton.a11y.chromium.spec.ts +209 -0
  363. package/src/Tokenizer/Tokenizer.spec.md +142 -75
  364. package/src/TreeList/TreeList.doc.mjs +3 -3
  365. package/src/TreeList/TreeList.spec.md +123 -75
  366. package/src/TreeList/TreeList.test.tsx +29 -0
  367. package/src/TreeList/TreeList.tsx +3 -0
  368. package/src/TreeList/TreeListItem.tsx +15 -1
  369. package/src/TreeList/TreeListTypes.ts +11 -1
  370. package/src/Typeahead/BaseTypeahead.spec.md +4 -3
  371. package/src/Typeahead/BaseTypeahead.tsx +21 -7
  372. package/src/Typeahead/Typeahead.test.tsx +53 -0
  373. package/src/__tests__/PressedState.a11y.chromium.spec.ts +813 -0
  374. package/src/__tests__/pressState.ts +93 -0
  375. package/src/hooks/scrollKeyboardDelegation.test.ts +155 -0
  376. package/src/hooks/scrollKeyboardDelegation.ts +233 -0
  377. package/src/hooks/useScrollableArea.doc.mjs +15 -3
  378. package/src/hooks/useScrollableArea.test.tsx +59 -1
  379. package/src/hooks/useScrollableArea.ts +34 -10
  380. package/src/inputFontFloor.test.ts +106 -0
  381. package/src/tailwind-theme.css +0 -6
  382. package/src/theme/generateThemeRules.test.ts +30 -0
  383. package/src/theme/generateThemeRules.ts +10 -0
  384. package/src/utils/interactionOverlay.stylex.ts +20 -0
@@ -7,7 +7,7 @@ authority: current
7
7
  archive_reason: null
8
8
  superseded_by: null
9
9
  approved_by: cixzhang
10
- approved_at: 2026-09-13
10
+ approved_at: 2026-09-19
11
11
  owners: [cixzhang]
12
12
  review_triggers: [api, theming]
13
13
  verified_by:
@@ -16,10 +16,12 @@ verified_by:
16
16
  packages/core/src/Markdown/Markdown.public.test.ts,
17
17
  packages/core/src/Markdown/parser.test.ts,
18
18
  packages/core/src/Markdown/incremental.test.ts,
19
+ packages/core/src/Markdown/remark.test.tsx,
20
+ packages/core/src/Outline/parseOutlineFromMarkdown.test.ts,
19
21
  packages/core/src/theme/themingTargets.test.ts,
20
22
  scripts/check-knowledge.mjs,
21
23
  ]
22
- modules: []
24
+ modules: [module:Markdown/remark]
23
25
  families: [family:navigation-destinations]
24
26
  design_specs: []
25
27
  architecture:
@@ -31,6 +33,10 @@ system_specs:
31
33
  spec:AST-002/DEC-5,
32
34
  spec:AST-005/DEC-1,
33
35
  spec:AST-005/DEC-2,
36
+ spec:AST-036/DEC-1,
37
+ spec:AST-036/DEC-2,
38
+ spec:AST-036/DEC-3,
39
+ spec:AST-036/DEC-4,
34
40
  ]
35
41
  ---
36
42
 
@@ -39,18 +45,20 @@ system_specs:
39
45
  ## Intent
40
46
 
41
47
  Markdown renders parsed content in a Document with stable default block parts and
42
- constrained renderer seams. In addition to the existing element overrides and
43
- prose-only inline plugins, a caller may opt a document into dollar-delimited math
44
- by supplying one typed renderer for both inline and display expressions. The
45
- parser exposes the same syntax only through an explicit option. Existing parsing,
46
- rendering, styling, and streaming behavior remain unchanged when math is absent.
48
+ constrained renderer seams. Callers may opt into the canonical plugin protocol for
49
+ bounded source syntax, immutable document transformation, typed extension
50
+ rendering, and native typed document-start frontmatter. They may separately opt into dollar-delimited math by supplying one typed
51
+ renderer for both inline and display expressions. The parser accepts matching
52
+ explicit options. Existing parsing, rendering, styling, and streaming behavior
53
+ remain unchanged when plugins and math are absent.
47
54
 
48
55
  ## Compatibility and migration
49
56
 
50
57
  - Released default preserved: `yes`
51
58
  - Compatibility class: additive, opt-in public API; existing parser nodes, DOM,
52
- styling, targets, and dollar-delimited text remain unchanged unless the caller
53
- supplies `components.math` or passes `{math: true}` to a parser.
59
+ styling, targets, dollar-delimited text, `components`, and `inlinePlugins` remain
60
+ unchanged unless the caller supplies `plugins`, supplies `components.math`, or
61
+ passes the matching explicit parser option.
54
62
  - Controlled/uncontrolled behavior: not applicable
55
63
  - Migration decision: none
56
64
 
@@ -69,6 +77,16 @@ Consumer migration instructions belong in consumer docs and release notes.
69
77
  delimiter boundaries, escape behavior, parser nodes, and streaming parity.
70
78
  - Passing each recognized expression as inert text to the caller's one math
71
79
  renderer with an `inline` or `block` display value.
80
+ - Applying the canonical `plugins` protocol in the fixed syntax → immutable
81
+ transform → render order while preserving built-in lexical shields, Core-owned
82
+ semantics, and local readable fallback.
83
+ - Validating and freezing replacement document roots before later transforms or
84
+ rendering observe them.
85
+ - Sharing plugin-enabled parse configuration, transformed heading projection, and
86
+ collision-safe heading IDs with Markdown-derived Outline utilities.
87
+ - Decoding an optional document-start frontmatter block into caller-defined typed
88
+ metadata, withholding unfinished frontmatter while streaming, and excluding
89
+ completed frontmatter syntax from rendered content.
72
90
 
73
91
  **Does not own / non-goals**
74
92
 
@@ -78,11 +96,43 @@ Consumer migration instructions belong in consumer docs and release notes.
78
96
  additional default block anatomy.
79
97
  - Nested anatomy or targets owned by CodeBlock, Blockquote, List, CheckboxList,
80
98
  or Table.
81
- - Executing or sanitizing a renderer's math library output, raw HTML parsing,
82
- arbitrary AST plugins, or new list/table/inline-style override slots.
99
+ - Executing or sanitizing a renderer's math or plugin output, raw HTML parsing,
100
+ mutable or unrestricted AST plugins, package discovery, or new
101
+ list/table/inline-style override slots.
83
102
 
84
103
  ## Public concepts
85
104
 
105
+ `plugins` is one optional ordered list of opaque entries created by
106
+ `createMarkdownPlugin()`. A plugin declares only the `syntax`, `transform`, and
107
+ `renderers` capabilities it uses. Syntax-bearing entries supply stable parse
108
+ identity. Parsing, transforms, rendering, and Outline use one stable, strictly typed,
109
+ MDAST-aligned canonical tree; `parseMarkdownAst()` and `parseInlineAst()` expose
110
+ that tree from the server-safe `@astryxdesign/core/Markdown/parser` subpath while
111
+ `@astryxdesign/core/Markdown/plugins` remains server-safe for constructing and
112
+ running plugins in server or RSC code. The rendered `Markdown` component remains a
113
+ client entry: function-bearing plugin entries are not serializable props and cannot
114
+ cross an RSC boundary. `MarkdownAstNodeMap` and `visitMarkdownNodes` provide
115
+ node-kind narrowing. Released parser functions preserve their existing result shape
116
+ through a compatibility projection. Transforms return validated replacement roots
117
+ without entering parse identity. Every extension node introduced
118
+ by syntax or transformation has complete renderer ownership and a deterministic
119
+ text projection. Text matching, semantic fences, source decoration, and native
120
+ frontmatter are helpers that compile to transforms rather than separate protocol
121
+ phases. Frontmatter is document metadata: it has no renderer, uses a bounded
122
+ first-party key/value grammar rather than Remark compatibility, and exposes typed
123
+ metadata through the helper that created it. `spec:AST-036`
124
+ owns the shared protocol and limited Remark compatibility profile,
125
+ `module:Markdown/remark` owns that profile's adapter, and this component owns
126
+ aggregate application and fallback.
127
+
128
+ ### Acceptance and implementation state
129
+
130
+ The plugin clauses below are the accepted target contract for the AST-036 rollout,
131
+ not a claim that the APIs already ship. Until every clause's implementation and
132
+ verification land, the currently released no-plugin, parser, `components`, and
133
+ `inlinePlugins` behavior remains the only available contract. Each implementation PR
134
+ must identify the clauses it completes without weakening the zero-breaking baseline.
135
+
86
136
  `MarkdownComponents.math` is one optional renderer with the signature
87
137
  `({value: string, display: 'inline' | 'block'}) => ReactNode`. Supplying it opts
88
138
  the component into math parsing because the caller owns both whether dollar
@@ -97,19 +147,33 @@ unions. Enabled calls return the explicit `InlineNodeWithMath` and
97
147
 
98
148
  ## Behavioral and layout contract
99
149
 
100
- | ID | Invariant |
101
- | ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
102
- | FR1 | Block and inline displays render one Document root carrying the current `markdown` target. Inline display renders no block anatomy. |
103
- | FR2 | On the default block render path, Heading, Paragraph, List, Code block, Blockquote, Table, Divider, and Image carry the eight current local block targets documented below. |
104
- | FR3 | A supplied `heading`, `paragraph`, `code`, `blockquote`, `hr`, or safe-URL `image` renderer replaces the corresponding default part, so Markdown does not impose that part's local target on the replacement. |
105
- | FR4 | The released Code block target remains `markdown-codeblock`; this compatibility anomaly is not renamed or aliased. |
106
- | FR5 | Density and Heading level remain reflected capabilities on their owning targets. Display mode, streaming state, and renderer selection do not become separate anatomy entries. |
107
- | FR6 | Without `components.math`, Markdown does not recognize math syntax. Default, legacy-set, `math: false`, and `ParseOptions`-annotated parser calls retain the released `InlineNode` / `BlockNode` result unions; only `MathParseOptions` returns the explicit math-enabled unions. |
108
- | FR7 | With math enabled, `$…$` produces an inline `math` node and `$$…$$` produces a block `math` node. The renderer receives the delimiter-free source as `value` and its placement as `display`. |
109
- | FR8 | Inline math stays on one line, cannot have whitespace touching either delimiter, and cannot open immediately after a digit or close immediately before one. `$$` is reserved for display math. These boundaries keep paired currency amounts literal. |
110
- | FR9 | A backslash-escaped dollar is literal outside math and does not close math inside it. An unmatched inline or display delimiter remains literal in non-streaming output. |
111
- | FR10 | Code spans and fenced code blocks are opaque to math parsing. Link destinations are opaque; link labels may contain inline math. Inline plugins run only on prose text and never inside math. |
112
- | FR11 | Streaming converges to the same nodes as a full parse at every chunk boundary, including display math nested in ordinary lists, task lists, blockquotes, and their supported combinations, with LF or CRLF and with or without source ranges. Incomplete math is withheld only while its exact owning container remains open; a list/quote exit or quote-depth change restores literal parsing. Math-enabled incremental calls require `IncrementalParseState<true>`, so the cache and returned union share one contract. |
150
+ | ID | Invariant |
151
+ | ---- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
152
+ | FR1 | Block and inline displays render one Document root carrying the current `markdown` target. Inline display renders no block anatomy. |
153
+ | FR2 | On the default block render path, Heading, Paragraph, List, Code block, Blockquote, Table, Divider, and Image carry the eight current local block targets documented below. |
154
+ | FR3 | A supplied `heading`, `paragraph`, `code`, `blockquote`, `hr`, or safe-URL `image` renderer replaces the corresponding default part, so Markdown does not impose that part's local target on the replacement. |
155
+ | FR4 | The released Code block target remains `markdown-codeblock`; this compatibility anomaly is not renamed or aliased. |
156
+ | FR5 | Density and Heading level remain reflected capabilities on their owning targets. Display mode, streaming state, and renderer selection do not become separate anatomy entries. |
157
+ | FR6 | Without `components.math`, Markdown does not recognize math syntax. Default, legacy-set, `math: false`, and `ParseOptions`-annotated parser calls retain the released `InlineNode` / `BlockNode` result unions; only `MathParseOptions` returns the explicit math-enabled unions. |
158
+ | FR7 | With math enabled, `$…$` produces an inline `math` node and `$$…$$` produces a block `math` node. The renderer receives the delimiter-free source as `value` and its placement as `display`. |
159
+ | FR8 | Inline math stays on one line, cannot have whitespace touching either delimiter, and cannot open immediately after a digit or close immediately before one. `$$` is reserved for display math. These boundaries keep paired currency amounts literal. |
160
+ | FR9 | A backslash-escaped dollar is literal outside math and does not close math inside it. An unmatched inline or display delimiter remains literal in non-streaming output. |
161
+ | FR10 | Code spans and fenced code blocks are opaque to math parsing. Link destinations are opaque; link labels may contain inline math. Inline plugins run only on prose text and never inside math. |
162
+ | FR11 | Streaming converges to the same nodes as a full parse at every chunk boundary, including display math nested in ordinary lists, task lists, blockquotes, and their supported combinations, with LF or CRLF and with or without source ranges. Incomplete math is withheld only while its exact owning container remains open; a list/quote exit or quote-depth change restores literal parsing. Math-enabled incremental calls require `IncrementalParseState<true>`, so the cache and returned union share one contract. |
163
+ | FR12 | Omitting `plugins` and passing an empty list are one semantic empty pipeline with identical parser unions, AST, DOM, styling, targets, IDs, and streaming behavior. Core may skip empty preparation and allocation without creating a separate behavior model. `components`, `inlinePlugins`, citations, autolinking, sources, and math retain their released meaning. |
164
+ | FR13 | Plugin-enabled parsing follows built-in lexical shields and ordered syntax claims, then uses one stable, strictly typed, MDAST-aligned canonical tree for transforms, rendering, and Outline. `MarkdownAstNodeMap` and `visitMarkdownNodes` narrow callbacks by node kind. Released parser functions preserve their existing result shape through a compatibility projection. Every returned root is finite, acyclic, representable, validated, and frozen before later plugins or rendering observe it. |
165
+ | FR14 | Plugin failures preserve the last valid document and readable authored source. Core retains heading, navigation, image, list, table, and document semantics and exposes no raw-markup parser channel, registry, package discovery, mutable shared AST, or unrestricted DOM hook. URL-like plugin data remains untrusted; Astryx-owned sinks follow `family:navigation-destinations`. |
166
+ | FR15 | Incremental parse identity contains every parse-affecting Markdown option and only ordered syntax-bearing plugin name, protocol version, and `parseKey`. Transform or renderer changes reuse settled parse output, rerun transformation, and do not remount unaffected extension output. |
167
+ | FR16 | Plugin-enabled Markdown and Markdown-derived Outline use the same parse options, ordered transforms, extension text projection, slugger, and collision allocator so every visible heading, Outline label, heading ID, and target agree. A limited Remark adapter may run only synchronous transform plugins whose input and output round-trip through the documented supported MDAST subset. |
168
+ | FR17 | An extension node declares `content` as `'none'`, `'phrasing'`, `'flow'`, or an explicit `{allow, min?, max?}` allowlist that narrows the category its `display` implies. Markdown parses every container's inner source span under the same grammar and shields, validates children at each transform boundary, renders children through the same built-in renderers and `components` seams, counts nesting toward the built-in depth bound, leaves FR16 heading traversal unchanged, and renders children in place when a container renderer fails. |
169
+ | FR18 | A transform may read and remove another plugin's extension nodes, including a subtree containing them, and may insert or remove headings; it may not create, edit, internally reorder, or duplicate another plugin's nodes, change a source heading's depth, or forge or duplicate heading identity. `dependsOn` is validated at preparation; an unmet or misordered dependency skips only that plugin's transform. Every rejection names the rule and owning plugin. |
170
+ | FR19 | `onPluginDiagnostic` is available on the component and parser options and receives one source-free event — plugin, phase, stable code, severity — per failure, advisory, or silent degradation, in development and production, rate-limited with a suppression code. Admission, duplicate-name, and protocol-version failures behave identically through the component and every parser entrypoint: the call succeeds with the last valid configuration and never throws into the caller. |
171
+ | FR20 | `parseMarkdownAst()` and `parseInlineAst()` return the canonical tree and accept the same options and plugin list as the component; `@astryxdesign/core/Markdown/parser` exposes parsing, canonical AST types, and plugin admission with no client boundary. `createMarkdownPlugin()` infers the extension-node union, so no callsite needs explicit type arguments, and a declaration that yields no usable extension type is a type error rather than a silent `never`. |
172
+ | FR21 | A transform runs again for every streamed update and must be idempotent and convergent; transforms whose effect requires complete input use the final-input signal. Semantically equal plugin lists reuse prepared work whether or not the array reference is stable, and development reports one diagnostic when a recreated list prevents reuse. |
173
+ | FR22 | An extension renderer may opt into the Markdown-owned extension theme target so themes reach plugin output. Opting out leaves output untargeted. The target adds no default styling or anatomy beyond the block spacing and content width Core already applies. |
174
+ | FR23 | Markdown owns an explicit supported dialect rather than claiming full CommonMark or GFM conformance. Adjacent compatible ordered or unordered items remain one list regardless of task-marker presence; each item independently preserves its checked state or ordinary list-item semantics, including at nested levels. The default grammar keeps its released task-list and table support, while `autolink: 'gfm'` adds only the documented autolink behavior and does not toggle any other syntax. |
175
+ | FR24 | `createMarkdownFrontmatter()` recognizes only a leading `---` block of unique `key: value` lines, decodes it through the caller's typed parser, stores finite JSON-like metadata on the canonical document, and removes the syntax from rendered content. An unfinished leading block yields no visible Markdown while streaming; malformed or unfinished final input remains ordinary Markdown. Frontmatter has no renderer and requires no Remark compatibility. |
176
+ | FR25 | The canonical parser and plugin-construction subpaths remain server-safe and can run function-bearing plugins entirely within server or RSC code. The client-owned `Markdown` component supports traditional and streaming SSR, but plugin entries containing functions cannot be serialized from a Server Component into that client boundary; direct RSC rendering requires a future additive server renderer rather than weakening the plugin protocol. |
113
177
 
114
178
  ### Allowed variation
115
179
 
@@ -125,21 +189,28 @@ unions. Enabled calls return the explicit `InlineNodeWithMath` and
125
189
  - **AV5 — Math renderer.** The caller may use any renderer that accepts the raw
126
190
  expression and display value. Its DOM, styles, typesetting engine, error UI,
127
191
  and accessibility representation are outside Markdown's ownership.
192
+ - **AV6 — Installed plugins.** A host may supply any ordered set of compatible
193
+ opaque plugin entries. Syntax, immutable transform behavior, renderer-owned
194
+ output, and helper implementation may vary while validation, readable fallback,
195
+ Core semantics, and heading identity stay fixed.
128
196
 
129
197
  ### Representative states
130
198
 
131
- | State | Required invariant | Allowed variation |
132
- | ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
133
- | Default block content | Every parsed block uses its corresponding current Markdown target. | Block count, order, density, content width, and alignment. |
134
- | Custom block renderers | The replaced Heading, Paragraph, Code block, Blockquote, Divider, or Image lacks the corresponding Markdown target. | Replacement structure and styling. |
135
- | Ordered/unordered list | List carries `markdown-list`. | Marker kind, start value, item count, and nested content. |
136
- | Task list | The outer List part carries `markdown-list`. | Checked values and item content. |
137
- | Safe block image | Default Image carries `markdown-image`, or a custom image renderer replaces it. | Source and alternative text. |
138
- | Unsafe block image URL | Markdown renders its fallback Image part with `markdown-image`; no custom image renderer receives the rejected URL. | Alternative text shown by the fallback. |
139
- | Inline display | Document carries `markdown`; no block target renders. | Inline text, links, code, citations, plugins, and opt-in inline math. |
140
- | Math renderer absent | Dollar-delimited source follows the released Markdown grammar and no `math` node or renderer output exists. | Currency, unmatched delimiters, and ordinary prose. |
141
- | Math renderer present | Complete supported delimiters are opaque to Markdown formatting and are passed to the renderer as inert text. | Inline or block display and any renderer-owned output. |
142
- | Streaming math | Incomplete math is withheld; once complete, the streamed nodes equal the full-parse nodes at top level and inside list/blockquote containers. | Delimiters and expression text may arrive in separate chunks; source ranges remain optional. |
199
+ | State | Required invariant | Allowed variation |
200
+ | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
201
+ | Default block content | Every parsed block uses its corresponding current Markdown target. | Block count, order, density, content width, and alignment. |
202
+ | Custom block renderers | The replaced Heading, Paragraph, Code block, Blockquote, Divider, or Image lacks the corresponding Markdown target. | Replacement structure and styling. |
203
+ | Ordered/unordered list | List carries `markdown-list`. | Marker kind, start value, item count, and nested content. |
204
+ | Task list | Each task-marked item carries its own checked state; mixed task/plain items stay in one compatible list and preserve document order and nesting. | Checked values, item content, and adjacent plain items. |
205
+ | Safe block image | Default Image carries `markdown-image`, or a custom image renderer replaces it. | Source and alternative text. |
206
+ | Unsafe block image URL | Markdown renders its fallback Image part with `markdown-image`; no custom image renderer receives the rejected URL. | Alternative text shown by the fallback. |
207
+ | Inline display | Document carries `markdown`; no block target renders. | Inline text, links, code, citations, plugins, and opt-in inline math. |
208
+ | Math renderer absent | Dollar-delimited source follows the released Markdown grammar and no `math` node or renderer output exists. | Currency, unmatched delimiters, and ordinary prose. |
209
+ | Math renderer present | Complete supported delimiters are opaque to Markdown formatting and are passed to the renderer as inert text. | Inline or block display and any renderer-owned output. |
210
+ | Streaming math | Incomplete math is withheld; once complete, the streamed nodes equal the full-parse nodes at top level and inside list/blockquote containers. | Delimiters and expression text may arrive in separate chunks; source ranges remain optional. |
211
+ | Plugins omitted | Released parser unions, AST, DOM, targets, heading IDs, and performance remain unchanged. | Omitted or empty list; both are one empty transform pipeline. |
212
+ | Plugins enabled | Fixed syntax → immutable transform → render order, validated roots, readable fallback, and matching Markdown/Outline heading identity remain invariant. | Syntax, transforms, renderers, helper execution plans, plugin order, and live post-parse state. |
213
+ | Native frontmatter | A complete leading block is absent from rendered content and yields typed metadata; unfinished streaming input is withheld. | Metadata schema and values are caller-defined finite data. |
143
214
 
144
215
  ### Transformation and precedence order
145
216
 
@@ -151,6 +222,16 @@ unions. Enabled calls return the explicit `InlineNodeWithMath` and
151
222
  display value. Markdown never turns it into HTML or executes it.
152
223
  - Existing URL sanitization remains in force for links and images; math adds no
153
224
  navigation or raw-HTML sink.
225
+ - Built-in syntax and protected contexts claim first; extension syntax claims only
226
+ eligible source; ordered transforms then receive deeply readonly document roots;
227
+ Core validates each returned root before rendering.
228
+ - A configured native frontmatter helper claims only the document-start delimiter.
229
+ It withholds an unfinished block during streaming, removes a completed block
230
+ before later transforms render the document, and makes typed metadata available
231
+ to those later transforms and to callers of that helper.
232
+ - Text matching, semantic fences, and source decorations use transform helpers. Core
233
+ may compile those helpers into indexed internal plans without exposing additional
234
+ public phases.
154
235
 
155
236
  ### Performance and resources
156
237
 
@@ -162,6 +243,8 @@ unions. Enabled calls return the explicit `InlineNodeWithMath` and
162
243
  container depth so an indented closer cannot become a new opener and a depth
163
244
  transition cannot swallow literal content. The factory-created state carries
164
245
  the same legacy or math-enabled node contract as the parser call.
246
+ - Stable and semantically equal plugin lists reuse prepared syntax and transform plans. Only syntax enters parse identity; transform and renderer changes reuse parsed output. Zero-work and representative transforms remain within `spec:AST-036` FR21–FR23 budgets, including the plugin-authored and streaming paths.
247
+ - Remark compatibility adapters, the conformance kit, and optional renderers stay outside Core bundles unless explicitly imported.
165
248
 
166
249
  ## Accessibility contract
167
250
 
@@ -170,7 +253,10 @@ scrollable Table wrapper, and image alternative text remain unchanged. Math has
170
253
  no Astryx-owned default output: the caller's renderer owns an accessible
171
254
  representation appropriate to its typesetting engine (for example MathML or a
172
255
  labelled `role="math"` element). Markdown adds no wrapper, ARIA attributes, or
173
- HTML injection around renderer output.
256
+ HTML injection around renderer output. Plugin renderers likewise own their
257
+ complete documented semantic pattern, while Core preserves its own document,
258
+ heading, navigation, image, list, and table semantics. Transforms cannot erase
259
+ required accessible meaning or make meaning color-only.
174
260
 
175
261
  ## Design relationships
176
262
 
@@ -234,18 +320,32 @@ and this change preserves the existing spelling exactly.
234
320
  - `spec:AST-005/DEC-2` keeps embedded-resource policy separate. Markdown may
235
321
  reject a broader set of image/resource URLs without narrowing the shared
236
322
  navigation contract.
323
+ - `spec:AST-036` owns the opaque syntax/transform/renderer protocol, immutable AST
324
+ validation, limited Remark compatibility, performance, and resource boundaries.
325
+ This record owns aggregate Markdown behavior in FR12–FR22;
326
+ `module:Markdown/remark` owns the separately imported Remark adapter's
327
+ supported subset, rejections, and diagnostics; and
328
+ `module:Outline/parseOutlineFromMarkdown` owns the corresponding Outline
329
+ projection.
237
330
  - Nested Astryx primitives retain ownership of their own anatomy and targets;
238
331
  Markdown owns the outer block targets listed here.
239
332
 
240
333
  ## Verification map
241
334
 
242
- | Contract | Verification | Representative states | Failure signal |
243
- | ---------------------- | -------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
244
- | FR1–FR5 | `Markdown.test.tsx`, theme-target tests, and `scripts/check-knowledge.mjs` | Default block/inline output and all current targets | Existing DOM, target, spacing, or renderer behavior changes. |
245
- | FR6–FR10 | `parser.test.ts` and `Markdown.test.tsx` | Opt-out, inline/display math, escapes, currency, code, links, plugins | A delimiter is claimed without opt-in, TeX is formatted as Markdown, or opaque contexts leak. |
246
- | FR11 | `incremental.test.ts` and `Markdown.test.tsx` | Every-character top-level/list/task-list/blockquote splits, CRLF, source ranges | Streaming diverges from a full parse, shows partial syntax, mistakes a nested closer for an opener, or crosses a closed container. |
247
- | Public syntax/types | `Markdown.public.test.ts`, core typecheck, and `Markdown.doc.mjs` | Legacy exhaustive switches, component renderer, full and incremental math opt-ins | A legacy union widens, math-enabled results omit math nodes, or docs drift from declarations. |
248
- | Security/accessibility | `parser.test.ts`, `Markdown.test.tsx`, and renderer guidance | Inert expression strings and renderer-owned semantics | Astryx executes math as HTML or silently claims renderer-owned accessibility. |
335
+ | Contract | Verification | Representative states | Failure signal |
336
+ | ---------------------- | -------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
337
+ | FR1–FR5 | `Markdown.test.tsx`, theme-target tests, and `scripts/check-knowledge.mjs` | Default block/inline output and all current targets | Existing DOM, target, spacing, or renderer behavior changes. |
338
+ | FR6–FR10 | `parser.test.ts` and `Markdown.test.tsx` | Opt-out, inline/display math, escapes, currency, code, links, plugins | A delimiter is claimed without opt-in, TeX is formatted as Markdown, or opaque contexts leak. |
339
+ | FR11 | `incremental.test.ts` and `Markdown.test.tsx` | Every-character top-level/list/task-list/blockquote splits, CRLF, source ranges | Streaming diverges from a full parse, shows partial syntax, mistakes a nested closer for an opener, or crosses a closed container. |
340
+ | FR12–FR16 | plugin, transform, adapter, performance, and Outline parser tests | omitted/empty lists, immutable transforms, invalid outputs, live updates, one compatible Remark plugin, duplicate headings | Empty behavior forks, input mutates, invalid structure escapes, transforms reparse, adapter loses content, budgets fail, or heading targets diverge. |
341
+ | FR17–FR18 | container parse/validation, ownership, and dependency tests | leaf/container declarations, nested containers, foreign read/remove/mint/edit, unmet/misordered dependencies | Plugin-built parsed children, invalid content, lost fallback children, foreign mint/edit, or generic ownership codes. |
342
+ | FR19 | diagnostic-channel tests in development and production | every phase, advisory reports, rate suppression, no handler, malformed list, duplicate Core, version skew | A silent production failure, document content in a diagnostic, or one entrypoint throwing where another recovers. |
343
+ | FR20–FR22 | canonical/server imports, inference, streaming, preparation, theming tests | server imports, no explicit type args, chunk boundaries, recreated lists, themed/unthemed extensions | Client references, explicit-type workarounds, oscillation, per-render re-preparation, or unreachable opted-in output. |
344
+ | FR23 | parser, renderer, nesting, and public option tests | task-only, plain-only, and mixed ordered/unordered lists at top level and nested; autolink omitted/enabled | A mixed list splits or loses order/state, a plain item becomes a checkbox, or `autolink: 'gfm'` changes non-autolink syntax. |
345
+ | FR24 | `plugins/frontmatter.test.tsx`, Storybook, and server rendering | complete, malformed, non-leading, LF/CRLF, unfinished streaming, full plugin stack | Metadata syntax renders after completion, unfinished syntax leaks while streaming, typing is lost, or later plugins stop composing. |
346
+ | FR25 | `parser.public.test.ts`, plugin SSR tests, and package export checks | server/RSC parsing with plugins, synchronous SSR, suspending renderer streaming boundary | A server import gains `use client`, plugin execution needs serialization, SSR loses fallback, or direct RSC rendering is misrepresented as supported. |
347
+ | Public syntax/types | `Markdown.public.test.ts`, core typecheck, and `Markdown.doc.mjs` | Legacy exhaustive switches, math opt-ins, inferred extension-node unions | A released union widens, an enabled union loses nodes, or docs drift from declarations. |
348
+ | Security/accessibility | `parser.test.ts`, `Markdown.test.tsx`, and renderer guidance | Inert expression strings and renderer-owned semantics | Astryx executes math as HTML or silently claims renderer-owned accessibility. |
249
349
 
250
350
  Focused tests continue to pin all nine current target names and default block
251
351
  placement. Math intentionally adds no target and no default anatomy.
@@ -274,6 +374,59 @@ Rejected: a generic AST/plugin escape hatch, raw HTML rendering, new list/table
274
374
  slots without consumer evidence, or a separate boolean on the component that
275
375
  could enable math without a renderer.
276
376
 
377
+ ### DEC-2 — Immutable transformation is the canonical Markdown extension seam
378
+
379
+ **Reference:** `component:Markdown/DEC-2`
380
+ **Decider:** `cixzhang`, `2026-09-15`
381
+
382
+ Markdown accepts one ordered `plugins` list whose opaque entries are created by
383
+ `createMarkdownPlugin()`. The public protocol exposes only bounded `syntax`,
384
+ immutable `transform`, and typed `renderers`. Core owns deep-readonly input,
385
+ validation and freezing of replacement roots, readable fallback, syntax-only parse
386
+ identity, preparation reuse, shared heading identity, containers, diagnostics, canonical/server parsing, and theming in FR12–FR22. Existing
387
+ `components`, `inlinePlugins`, math, citations, autolinking, and parser calls remain
388
+ compatible.
389
+
390
+ Text matching, semantic fences, and source decoration are transform helpers rather
391
+ than separate protocol phases. A tree-shakeable adapter may run only synchronous
392
+ transform-only Remark plugins over the documented MDAST subset; unsupported behavior
393
+ fails closed rather than being approximated.
394
+
395
+ This projects `spec:AST-036/DEC-1` through `DEC-4` into the component owner. It rejects a registry, package discovery, mutable shared AST, raw markup, a second plugin prop, or an unrestricted Unified runtime.
396
+
397
+ ### DEC-3 — Containers, diagnostics, and canonical APIs are Markdown-owned
398
+
399
+ **Reference:** `component:Markdown/DEC-3`
400
+ **Decider:** `cixzhang`, `2026-09-16`
401
+
402
+ Markdown parses every extension container's inner span itself and validates children against the plugin's declared content shape, so a callout holds real Markdown while heading identity, protected contexts, navigation policy, and Outline scope stay Core-owned. Containers change what a document can express, not which headings have identity: the released top-level traversal shared by heading IDs and Outline is untouched. A failed container renderer shows its children rather than literal source. Ownership rejections name the rule and owner; removal of another plugin's nodes is permitted and only minting, editing, internal reordering, duplication, and identity forgery are not.
403
+
404
+ Markdown also owns the protocol's observability and entry surface: `onPluginDiagnostic` makes every failure visible in production without carrying document content, admission failures degrade instead of throwing at any entrypoint, canonical parse entrypoints and a server-safe parser entry exist beside the released projection, extension types are inferred, and extension output may opt into one theme target without becoming default anatomy.
405
+
406
+ This projects `spec:AST-036/DEC-5` through `DEC-11` into the component owner in FR17–FR22. It rejects leaf-only extensions, plugin-authored parsed children, independent document shells, development-only or free-text diagnostics, parsers reachable only through a client barrel, required hand-written extension aliases, and default anatomy for plugin output.
407
+
408
+ ### DEC-4 — Own an explicit Markdown dialect, not a profile switch
409
+
410
+ **Reference:** `component:Markdown/DEC-4`
411
+ **Decider:** `cixzhang`, `2026-09-19`
412
+
413
+ Markdown's released grammar is an explicit Astryx-owned subset. Task-list markers are item semantics inside the ordinary ordered or unordered list structure, so mixed task and plain items stay in one compatible list and each item retains its own state at every nesting level. Released table and task-list syntax remains enabled by default. The optional `autolink: 'gfm'` value adds only the documented autolink behavior; it neither enables another syntax feature nor changes list structure.
414
+
415
+ This keeps documents stable as Astryx adds or declines individual ecosystem features. It rejects a blanket CommonMark or GFM conformance claim, aggregate all-task/all-plain classification, an implicit whole-grammar mode switch, and silently enabling future GFM features under the existing autolink option.
416
+
417
+ ### DEC-5 — Frontmatter is typed document metadata
418
+
419
+ **Reference:** `component:Markdown/DEC-5`
420
+ **Decider:** `cixzhang`, `2026-09-19`
421
+
422
+ Native frontmatter is a first-party helper in the ordered plugin pipeline. It
423
+ recognizes only a leading delimited block, parses a deliberately small key/value
424
+ grammar through a caller-provided typed decoder, and removes the syntax from the
425
+ rendered document. It does not create a visual extension node or require Remark's
426
+ frontmatter format. During streaming, incomplete frontmatter is withheld so raw
427
+ metadata never flashes as content; once closed, later plugins can consume the
428
+ metadata and the remaining document normally.
429
+
277
430
  ## Open questions
278
431
 
279
432
  - **OQ1 — Which focused tests should pin target absence for the five remaining
@@ -80,6 +80,17 @@ describe('Markdown', () => {
80
80
  }
81
81
  });
82
82
 
83
+ it('keeps citation markers out of released heading ids', () => {
84
+ render(
85
+ <Markdown sources={{cite: {title: 'Citation'}}}>
86
+ {'# Before [cite] after'}
87
+ </Markdown>,
88
+ );
89
+ expect(
90
+ screen.getByRole('heading', {name: /Before.*after/}),
91
+ ).toHaveAttribute('id', 'before-after');
92
+ });
93
+
83
94
  it('passes the generated id to a custom heading component', () => {
84
95
  const received: (string | undefined)[] = [];
85
96
  render(