@astryxdesign/core 0.7.1-canary.ff40b78 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (348) hide show
  1. package/CHANGELOG.md +116 -0
  2. package/dist/Button/Button.d.ts +1 -1
  3. package/dist/Button/Button.d.ts.map +1 -1
  4. package/dist/Button/Button.js +5 -2
  5. package/dist/Calendar/Calendar.d.ts.map +1 -1
  6. package/dist/Calendar/Calendar.js +191 -32
  7. package/dist/Chat/ChatComposerInput.d.ts +9 -0
  8. package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
  9. package/dist/Chat/ChatComposerInput.js +5 -0
  10. package/dist/Chat/ChatMessage.d.ts +11 -0
  11. package/dist/Chat/ChatMessage.d.ts.map +1 -1
  12. package/dist/Chat/ChatMessage.js +18 -9
  13. package/dist/Chat/useTriggerMenu.d.ts +9 -2
  14. package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
  15. package/dist/Chat/useTriggerMenu.js +222 -50
  16. package/dist/CodeBlock/CodeBlock.d.ts +10 -1
  17. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  18. package/dist/CodeBlock/CodeBlock.js +158 -38
  19. package/dist/CodeBlock/tokenizer.d.ts +39 -0
  20. package/dist/CodeBlock/tokenizer.d.ts.map +1 -1
  21. package/dist/CodeBlock/tokenizer.js +108 -0
  22. package/dist/CommandPalette/CommandPalette.d.ts +9 -5
  23. package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
  24. package/dist/CommandPalette/CommandPalette.js +38 -20
  25. package/dist/CommandPalette/CommandPaletteEmpty.d.ts +4 -3
  26. package/dist/CommandPalette/CommandPaletteEmpty.d.ts.map +1 -1
  27. package/dist/CommandPalette/CommandPaletteEmpty.js +4 -3
  28. package/dist/CommandPalette/CommandPaletteInput.d.ts +5 -4
  29. package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
  30. package/dist/CommandPalette/CommandPaletteInput.js +35 -18
  31. package/dist/CommandPalette/CommandPaletteItem.d.ts +11 -6
  32. package/dist/CommandPalette/CommandPaletteItem.d.ts.map +1 -1
  33. package/dist/CommandPalette/CommandPaletteItem.js +8 -9
  34. package/dist/CommandPalette/CommandPaletteList.d.ts +4 -2
  35. package/dist/CommandPalette/CommandPaletteList.d.ts.map +1 -1
  36. package/dist/CommandPalette/CommandPaletteList.js +10 -5
  37. package/dist/ComplexSelector/ComplexSelector.d.ts +8 -5
  38. package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
  39. package/dist/ComplexSelector/ComplexSelector.js +12 -3
  40. package/dist/ContextMenu/ContextMenu.d.ts +3 -3
  41. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  42. package/dist/ContextMenu/ContextMenu.js +62 -48
  43. package/dist/DateInput/DateInput.js +1 -1
  44. package/dist/DateInput/MonthScroller.d.ts.map +1 -1
  45. package/dist/DateInput/MonthScroller.js +12 -4
  46. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  47. package/dist/DateRangeInput/DateRangeInput.js +12 -2
  48. package/dist/Dialog/Dialog.d.ts +1 -1
  49. package/dist/Dialog/Dialog.d.ts.map +1 -1
  50. package/dist/Dialog/Dialog.js +16 -5
  51. package/dist/Dialog/DialogHeader.d.ts +1 -1
  52. package/dist/Dialog/DialogHeader.d.ts.map +1 -1
  53. package/dist/Dialog/DialogHeader.js +7 -7
  54. package/dist/Divider/Divider.d.ts +1 -0
  55. package/dist/Divider/Divider.d.ts.map +1 -1
  56. package/dist/Divider/Divider.js +5 -4
  57. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  58. package/dist/DropdownMenu/DropdownMenu.js +161 -65
  59. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts +7 -5
  60. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -1
  61. package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +12 -5
  62. package/dist/Field/InputClearButton.d.ts +8 -4
  63. package/dist/Field/InputClearButton.d.ts.map +1 -1
  64. package/dist/Field/InputClearButton.js +23 -7
  65. package/dist/FormLayout/FormLayout.d.ts.map +1 -1
  66. package/dist/FormLayout/FormLayout.js +1 -0
  67. package/dist/Grid/Grid.d.ts +13 -4
  68. package/dist/Grid/Grid.d.ts.map +1 -1
  69. package/dist/Grid/Grid.js +7 -1
  70. package/dist/HoverCard/useHoverCard.d.ts +1 -1
  71. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  72. package/dist/HoverCard/useHoverCard.js +67 -49
  73. package/dist/InputGroup/InputGroup.d.ts +2 -4
  74. package/dist/InputGroup/InputGroup.d.ts.map +1 -1
  75. package/dist/InputGroup/InputGroup.js +2 -4
  76. package/dist/InputGroup/groupStyles.d.ts +10 -2
  77. package/dist/InputGroup/groupStyles.d.ts.map +1 -1
  78. package/dist/InputGroup/groupStyles.js +19 -2
  79. package/dist/Layer/useLayerDismissal.d.ts +9 -9
  80. package/dist/Layer/useLayerDismissal.js +9 -9
  81. package/dist/Markdown/Markdown.d.ts.map +1 -1
  82. package/dist/Markdown/Markdown.js +25 -4
  83. package/dist/MobileNav/MobileNav.d.ts +7 -0
  84. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  85. package/dist/MobileNav/MobileNav.js +45 -2
  86. package/dist/MultiSelector/MultiSelector.d.ts +7 -4
  87. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  88. package/dist/MultiSelector/MultiSelector.js +24 -3
  89. package/dist/NumberInput/NumberInput.js +1 -1
  90. package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
  91. package/dist/PowerSearch/PowerSearchEditPopover.js +6 -0
  92. package/dist/Resizable/ResizeHandle.d.ts +1 -1
  93. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  94. package/dist/Resizable/ResizeHandle.js +83 -15
  95. package/dist/Resizable/useResizable.d.ts +23 -0
  96. package/dist/Resizable/useResizable.d.ts.map +1 -1
  97. package/dist/Resizable/useResizable.js +78 -0
  98. package/dist/Selector/Selector.d.ts +6 -3
  99. package/dist/Selector/Selector.d.ts.map +1 -1
  100. package/dist/Selector/Selector.js +28 -6
  101. package/dist/SideNav/SideNav.d.ts.map +1 -1
  102. package/dist/SideNav/SideNav.js +10 -2
  103. package/dist/Slider/Slider.d.ts.map +1 -1
  104. package/dist/Slider/Slider.js +18 -7
  105. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  106. package/dist/Tokenizer/Tokenizer.js +16 -4
  107. package/dist/Toolbar/Toolbar.d.ts +2 -1
  108. package/dist/Toolbar/Toolbar.d.ts.map +1 -1
  109. package/dist/Toolbar/Toolbar.js +3 -1
  110. package/dist/TreeList/TreeList.d.ts.map +1 -1
  111. package/dist/TreeList/TreeList.js +6 -3
  112. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  113. package/dist/TreeList/TreeListItem.js +7 -1
  114. package/dist/astryx.css +22 -0
  115. package/dist/hooks/useScrollOverflow.d.ts.map +1 -1
  116. package/dist/hooks/useScrollOverflow.js +55 -12
  117. package/dist/hooks/useTreeFocus.d.ts +21 -0
  118. package/dist/hooks/useTreeFocus.d.ts.map +1 -1
  119. package/dist/hooks/useTreeFocus.js +40 -2
  120. package/dist/i18n/generated-locales/en.generated.d.ts.map +1 -1
  121. package/dist/i18n/generated-locales/en.generated.js +3 -0
  122. package/dist/i18n/generated-locales/pseudo.generated.d.ts.map +1 -1
  123. package/dist/i18n/generated-locales/pseudo.generated.js +3 -0
  124. package/dist/reset.a11y.chromium.spec.js +6 -1
  125. package/dist/reset.until-found.a11y.browser.spec.d.ts +2 -0
  126. package/dist/reset.until-found.a11y.browser.spec.d.ts.map +1 -0
  127. package/dist/reset.until-found.a11y.browser.spec.js +71 -0
  128. package/dist/theme/syntax/SyntaxTheme.d.ts.map +1 -1
  129. package/dist/theme/syntax/SyntaxTheme.js +43 -2
  130. package/dist/theme/syntax/defineSyntaxTheme.d.ts +18 -5
  131. package/dist/theme/syntax/defineSyntaxTheme.d.ts.map +1 -1
  132. package/dist/theme/syntax/defineSyntaxTheme.js +38 -9
  133. package/dist/theme/syntax/index.d.ts +1 -1
  134. package/dist/theme/syntax/index.d.ts.map +1 -1
  135. package/dist/theme/syntax/tokens.d.ts +12 -2
  136. package/dist/theme/syntax/tokens.d.ts.map +1 -1
  137. package/dist/theme/syntax/tokens.js +25 -4
  138. package/dist/utils/plainDate.d.ts +12 -0
  139. package/dist/utils/plainDate.d.ts.map +1 -1
  140. package/dist/utils/plainDate.js +63 -10
  141. package/locales/en.json +13 -1
  142. package/locales/pseudo.json +9 -0
  143. package/package.json +4 -4
  144. package/src/AlertDialog/AlertDialog.doc.mjs +6 -0
  145. package/src/AlertDialog/AlertDialog.spec.md +205 -0
  146. package/src/AlertDialog/AlertDialog.test.tsx +3 -14
  147. package/src/AlertDialog/__tests__/AlertDialog.a11y.chromium.spec.ts +115 -0
  148. package/src/AlertDialog/__tests__/AlertDialog.a11y.renders.tsx +52 -0
  149. package/src/AlertDialog/__tests__/AlertDialog.a11y.states.ts +68 -0
  150. package/src/AlertDialog/__tests__/AlertDialog.a11y.test.tsx +144 -0
  151. package/src/Button/Button.test.tsx +37 -0
  152. package/src/Button/Button.tsx +5 -1
  153. package/src/Calendar/Calendar.doc.mjs +234 -60
  154. package/src/Calendar/Calendar.spec.md +209 -0
  155. package/src/Calendar/Calendar.test.tsx +559 -20
  156. package/src/Calendar/Calendar.tsx +258 -41
  157. package/src/Calendar/__tests__/Calendar.a11y.chromium.spec.ts +126 -0
  158. package/src/Calendar/__tests__/Calendar.a11y.known-failures.ts +12 -0
  159. package/src/Calendar/__tests__/Calendar.a11y.renders.tsx +57 -0
  160. package/src/Calendar/__tests__/Calendar.a11y.states.ts +139 -0
  161. package/src/Calendar/__tests__/Calendar.a11y.test.tsx +137 -0
  162. package/src/Carousel/Carousel.test.tsx +54 -71
  163. package/src/Carousel/__tests__/Carousel.a11y.chromium.spec.ts +192 -0
  164. package/src/Carousel/__tests__/Carousel.a11y.known-failures.ts +27 -0
  165. package/src/Carousel/__tests__/Carousel.a11y.renders.tsx +39 -0
  166. package/src/Carousel/__tests__/Carousel.a11y.states.ts +139 -0
  167. package/src/Carousel/__tests__/Carousel.a11y.test.tsx +127 -0
  168. package/src/Chat/ChatComposerInput.doc.mjs +1 -1
  169. package/src/Chat/ChatComposerInput.spec.md +94 -55
  170. package/src/Chat/ChatComposerInput.test.tsx +202 -1
  171. package/src/Chat/ChatComposerInput.tsx +13 -0
  172. package/src/Chat/ChatMessage.doc.mjs +31 -16
  173. package/src/Chat/ChatMessage.spec.md +265 -0
  174. package/src/Chat/ChatMessage.test.tsx +246 -0
  175. package/src/Chat/ChatMessage.tsx +30 -7
  176. package/src/Chat/ChatSystemMessage.spec.md +36 -37
  177. package/src/Chat/ChatSystemMessage.test.tsx +4 -4
  178. package/src/Chat/useTriggerMenu.test.tsx +994 -0
  179. package/src/Chat/useTriggerMenu.tsx +278 -46
  180. package/src/CodeBlock/CodeBlock.doc.mjs +7 -0
  181. package/src/CodeBlock/CodeBlock.spec.md +40 -41
  182. package/src/CodeBlock/CodeBlock.test.tsx +89 -9
  183. package/src/CodeBlock/CodeBlock.tsx +205 -25
  184. package/src/CodeBlock/__tests__/CodeBlockOverflowStop.a11y.chromium.spec.ts +125 -0
  185. package/src/CodeBlock/__tests__/DiffRowPaint.a11y.chromium.spec.ts +93 -0
  186. package/src/CodeBlock/tokenizer.test.ts +112 -0
  187. package/src/CodeBlock/tokenizer.ts +106 -0
  188. package/src/Collapsible/CollapsibleGroup.spec.md +17 -8
  189. package/src/Collapsible/__tests__/CollapsibleGroup.a11y.renders.tsx +45 -0
  190. package/src/Collapsible/__tests__/CollapsibleGroup.a11y.states.ts +139 -0
  191. package/src/Collapsible/__tests__/CollapsibleGroup.a11y.test.tsx +135 -0
  192. package/src/Collapsible/__tests__/CollapsibleGroupAccordion.a11y.chromium.spec.ts +128 -0
  193. package/src/CommandPalette/CommandPalette.doc.mjs +126 -23
  194. package/src/CommandPalette/CommandPalette.spec.md +100 -49
  195. package/src/CommandPalette/CommandPalette.test.tsx +241 -2
  196. package/src/CommandPalette/CommandPalette.tsx +47 -21
  197. package/src/CommandPalette/CommandPaletteEmpty.tsx +4 -3
  198. package/src/CommandPalette/CommandPaletteInput.doc.mjs +61 -4
  199. package/src/CommandPalette/CommandPaletteInput.spec.md +215 -0
  200. package/src/CommandPalette/CommandPaletteInput.test.tsx +375 -51
  201. package/src/CommandPalette/CommandPaletteInput.tsx +74 -21
  202. package/src/CommandPalette/CommandPaletteItem.doc.mjs +73 -22
  203. package/src/CommandPalette/CommandPaletteItem.spec.md +223 -0
  204. package/src/CommandPalette/CommandPaletteItem.test.tsx +200 -5
  205. package/src/CommandPalette/CommandPaletteItem.tsx +16 -11
  206. package/src/CommandPalette/CommandPaletteList.doc.mjs +3 -3
  207. package/src/CommandPalette/CommandPaletteList.spec.md +41 -36
  208. package/src/CommandPalette/CommandPaletteList.test.tsx +80 -0
  209. package/src/CommandPalette/CommandPaletteList.tsx +10 -5
  210. package/src/CommandPalette/__tests__/CommandPalette.a11y.chromium.spec.ts +599 -0
  211. package/src/ComplexSelector/ComplexSelector.doc.mjs +10 -8
  212. package/src/ComplexSelector/ComplexSelector.spec.md +14 -9
  213. package/src/ComplexSelector/ComplexSelector.test.tsx +64 -16
  214. package/src/ComplexSelector/ComplexSelector.tsx +37 -8
  215. package/src/ContextMenu/ContextMenu.doc.mjs +6 -0
  216. package/src/ContextMenu/ContextMenu.spec.md +51 -53
  217. package/src/ContextMenu/ContextMenu.test.tsx +132 -1
  218. package/src/ContextMenu/ContextMenu.tsx +78 -48
  219. package/src/DateInput/DateInput.doc.mjs +52 -0
  220. package/src/DateInput/DateInput.spec.md +27 -15
  221. package/src/DateInput/DateInput.test.tsx +38 -0
  222. package/src/DateInput/DateInput.tsx +1 -1
  223. package/src/DateInput/DateInputTouch.test.tsx +22 -0
  224. package/src/DateInput/MonthScroller.tsx +12 -4
  225. package/src/DateRangeInput/DateRangeInput.test.tsx +49 -0
  226. package/src/DateRangeInput/DateRangeInput.tsx +20 -2
  227. package/src/Dialog/Dialog.test.tsx +27 -1
  228. package/src/Dialog/Dialog.tsx +16 -5
  229. package/src/Dialog/DialogHeader.test.tsx +27 -2
  230. package/src/Dialog/DialogHeader.tsx +7 -8
  231. package/src/Dialog/__tests__/Dialog.a11y.chromium.spec.ts +21 -14
  232. package/src/Divider/Divider.doc.mjs +5 -3
  233. package/src/Divider/Divider.spec.md +140 -54
  234. package/src/Divider/Divider.test.tsx +15 -0
  235. package/src/Divider/Divider.tsx +6 -4
  236. package/src/DropdownMenu/DropdownMenu.doc.mjs +2 -2
  237. package/src/DropdownMenu/DropdownMenu.spec.md +121 -45
  238. package/src/DropdownMenu/DropdownMenu.test.tsx +288 -23
  239. package/src/DropdownMenu/DropdownMenu.tsx +212 -81
  240. package/src/DropdownMenu/DropdownMenuCheckboxItem.doc.mjs +38 -2
  241. package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +32 -18
  242. package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +146 -1
  243. package/src/DropdownMenu/__tests__/MenuButton.a11y.chromium.spec.ts +136 -0
  244. package/src/DropdownMenu/__tests__/MenuButton.a11y.known-failures.ts +12 -0
  245. package/src/DropdownMenu/__tests__/MenuButton.a11y.renders.tsx +67 -0
  246. package/src/DropdownMenu/__tests__/MenuButton.a11y.states.ts +141 -0
  247. package/src/DropdownMenu/__tests__/MenuButton.a11y.test.tsx +138 -0
  248. package/src/DropdownMenu/__tests__/MenuHeaderFooter.a11y.chromium.spec.ts +40 -5
  249. package/src/Field/InputClearButton.test.tsx +29 -4
  250. package/src/Field/InputClearButton.tsx +26 -8
  251. package/src/FieldStatus/__tests__/StatusMessage.a11y.chromium.spec.ts +21 -14
  252. package/src/FormLayout/FormLayout.tsx +1 -0
  253. package/src/FormLayout/__snapshots__/FormLayout.test.tsx.snap +1 -1
  254. package/src/Grid/Grid.doc.mjs +14 -11
  255. package/src/Grid/Grid.spec.md +50 -32
  256. package/src/Grid/Grid.tsx +13 -4
  257. package/src/HoverCard/HoverCard.test.tsx +46 -1
  258. package/src/HoverCard/useHoverCard.tsx +68 -48
  259. package/src/InputGroup/InputGroup.doc.mjs +11 -1
  260. package/src/InputGroup/InputGroup.tsx +2 -4
  261. package/src/InputGroup/groupStyles.ts +40 -2
  262. package/src/Layer/useLayerDismissal.ts +9 -9
  263. package/src/Link/__tests__/Link.a11y.chromium.spec.ts +142 -0
  264. package/src/Link/__tests__/Link.a11y.renders.tsx +97 -0
  265. package/src/Link/__tests__/Link.a11y.states.ts +104 -0
  266. package/src/Link/__tests__/Link.a11y.test.tsx +127 -0
  267. package/src/Markdown/Markdown.doc.mjs +1 -1
  268. package/src/Markdown/Markdown.fr23.perf.test.ts +161 -40
  269. package/src/Markdown/Markdown.spec.md +45 -23
  270. package/src/Markdown/Markdown.test.tsx +38 -0
  271. package/src/Markdown/Markdown.tsx +24 -2
  272. package/src/Markdown/plugins/semanticFence.test.tsx +3 -2
  273. package/src/MobileNav/MobileNav.tsx +50 -2
  274. package/src/MobileNav/MobileNavFocus.test.tsx +553 -0
  275. package/src/MoreMenu/MoreMenu.spec.md +18 -7
  276. package/src/MoreMenu/MoreMenu.test.tsx +38 -0
  277. package/src/MultiSelector/MultiSelector.doc.mjs +8 -8
  278. package/src/MultiSelector/MultiSelector.spec.md +1 -1
  279. package/src/MultiSelector/MultiSelector.test.tsx +188 -21
  280. package/src/MultiSelector/MultiSelector.tsx +50 -11
  281. package/src/NumberInput/NumberInput.test.tsx +24 -0
  282. package/src/NumberInput/NumberInput.tsx +1 -1
  283. package/src/NumberInput/__tests__/NumberInput.a11y.chromium.spec.ts +22 -15
  284. package/src/PowerSearch/PowerSearchEditPopover.test.tsx +78 -0
  285. package/src/PowerSearch/PowerSearchEditPopover.tsx +6 -0
  286. package/src/Resizable/Resizable.spec.md +41 -9
  287. package/src/Resizable/ResizeHandle.test.tsx +565 -2
  288. package/src/Resizable/ResizeHandle.tsx +126 -21
  289. package/src/Resizable/__tests__/ResizeHandle.a11y.chromium.spec.ts +119 -0
  290. package/src/Resizable/__tests__/ResizeHandle.a11y.known-failures.ts +39 -0
  291. package/src/Resizable/__tests__/ResizeHandle.a11y.renders.tsx +80 -0
  292. package/src/Resizable/__tests__/ResizeHandle.a11y.states.ts +197 -0
  293. package/src/Resizable/__tests__/ResizeHandle.a11y.test.tsx +123 -0
  294. package/src/Resizable/useResizable.ts +95 -0
  295. package/src/Selector/Selector.doc.mjs +7 -6
  296. package/src/Selector/Selector.spec.md +37 -24
  297. package/src/Selector/Selector.test.tsx +176 -10
  298. package/src/Selector/Selector.tsx +59 -14
  299. package/src/Selector/__tests__/Combobox.a11y.known-failures.ts +27 -0
  300. package/src/Selector/__tests__/Combobox.a11y.states.ts +145 -0
  301. package/src/Selector/__tests__/Selector.combobox.a11y.chromium.spec.ts +108 -0
  302. package/src/Selector/__tests__/Selector.combobox.a11y.test.tsx +126 -0
  303. package/src/SideNav/SideNav.test.tsx +35 -0
  304. package/src/SideNav/SideNav.tsx +16 -2
  305. package/src/Slider/Slider.spec.md +43 -17
  306. package/src/Slider/Slider.test.tsx +134 -0
  307. package/src/Slider/Slider.tsx +29 -6
  308. package/src/Slider/__tests__/Slider.a11y.chromium.spec.ts +112 -0
  309. package/src/Slider/__tests__/Slider.a11y.known-failures.ts +12 -0
  310. package/src/Slider/__tests__/Slider.a11y.renders.tsx +42 -0
  311. package/src/Slider/__tests__/Slider.a11y.states.ts +106 -0
  312. package/src/Slider/__tests__/Slider.a11y.test.tsx +105 -0
  313. package/src/Switch/__tests__/Switch.a11y.chromium.spec.ts +25 -18
  314. package/src/TabList/__tests__/Tabs.a11y.chromium.spec.ts +19 -12
  315. package/src/TextInput/__tests__/TextInput.a11y.chromium.spec.ts +21 -14
  316. package/src/ToggleButton/__tests__/ToggleButton.a11y.chromium.spec.ts +18 -11
  317. package/src/Tokenizer/Tokenizer.test.tsx +87 -0
  318. package/src/Tokenizer/Tokenizer.tsx +31 -3
  319. package/src/Toolbar/Toolbar.test.tsx +131 -4
  320. package/src/Toolbar/Toolbar.tsx +3 -1
  321. package/src/TreeList/TreeList.spec.md +33 -8
  322. package/src/TreeList/TreeList.test.tsx +72 -0
  323. package/src/TreeList/TreeList.tsx +10 -6
  324. package/src/TreeList/TreeListItem.tsx +5 -0
  325. package/src/TreeList/__tests__/TreeList.a11y.chromium.spec.ts +125 -0
  326. package/src/TreeList/__tests__/TreeList.a11y.renders.tsx +44 -0
  327. package/src/TreeList/__tests__/TreeList.a11y.states.ts +164 -0
  328. package/src/TreeList/__tests__/TreeList.a11y.test.tsx +138 -0
  329. package/src/TreeList/__tests__/TreeListRowNames.a11y.chromium.spec.ts +82 -0
  330. package/src/hooks/useScrollOverflow.test.tsx +222 -0
  331. package/src/hooks/useScrollOverflow.ts +55 -13
  332. package/src/hooks/useTreeFocus.test.tsx +37 -0
  333. package/src/hooks/useTreeFocus.ts +55 -2
  334. package/src/i18n/generated-locales/en.generated.ts +3 -0
  335. package/src/i18n/generated-locales/pseudo.generated.ts +3 -0
  336. package/src/reset.a11y.chromium.spec.ts +9 -1
  337. package/src/reset.css +8 -3
  338. package/src/reset.until-found.a11y.browser.spec.ts +75 -0
  339. package/src/reset.until-found.test.ts +32 -0
  340. package/src/stylexValidation.source-build.test.mjs +42 -0
  341. package/src/theme/syntax/SyntaxTheme.tsx +46 -5
  342. package/src/theme/syntax/defineSyntaxTheme.diff.test.ts +91 -0
  343. package/src/theme/syntax/defineSyntaxTheme.ts +75 -15
  344. package/src/theme/syntax/index.ts +5 -1
  345. package/src/theme/syntax/tokens.ts +32 -2
  346. package/src/theme/syntax/useSyntaxTheme.diff.test.tsx +40 -0
  347. package/src/utils/plainDate.test.ts +147 -0
  348. package/src/utils/plainDate.ts +75 -10
@@ -0,0 +1,104 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /** AST-021 state inventory for the Link binding of the shared Link contract. */
4
+
5
+ import type {LinkStateFacts} from '@astryxdesign/a11y-spec';
6
+
7
+ export interface LinkA11yState {
8
+ readonly id:
9
+ | 'text-native'
10
+ | 'icon-labelled'
11
+ | 'current-page'
12
+ | 'external-new-tab'
13
+ | 'provider-router'
14
+ | 'as-router';
15
+ readonly summary: string;
16
+ readonly facts: LinkStateFacts;
17
+ readonly storyId: string;
18
+ }
19
+
20
+ export type LinkA11yRow = (typeof LINK_A11Y_STATES)[number];
21
+
22
+ const story = (id: string) => `a11y-link-pattern--${id}`;
23
+
24
+ /**
25
+ * Every destination is a same-document fragment, so following a link keeps the
26
+ * story mounted. The router component records its own navigations; the browser
27
+ * records native ones.
28
+ */
29
+ export const LINK_A11Y_DESTINATION = '#link-destination';
30
+
31
+ export const LINK_A11Y_STATES = [
32
+ {
33
+ id: 'text-native',
34
+ summary: 'a native anchor named by its visible text',
35
+ facts: {name: 'Documentation', current: null},
36
+ storyId: story('text-native'),
37
+ },
38
+ {
39
+ id: 'icon-labelled',
40
+ summary: 'an icon-only link named by its label prop',
41
+ facts: {name: 'Close dialog', current: null},
42
+ storyId: story('icon-labelled'),
43
+ },
44
+ {
45
+ id: 'current-page',
46
+ summary: 'a link the caller marks as the current page',
47
+ facts: {name: 'Overview', current: 'page'},
48
+ storyId: story('current-page'),
49
+ },
50
+ {
51
+ id: 'external-new-tab',
52
+ summary:
53
+ 'an external link whose name carries the new-tab notice and which opens a new tab',
54
+ facts: {name: 'GitHub (opens in new tab)', current: null},
55
+ storyId: story('external-new-tab'),
56
+ },
57
+ {
58
+ id: 'provider-router',
59
+ summary:
60
+ 'a link rendered by a router component supplied through LinkProvider',
61
+ facts: {name: 'Projects', current: null},
62
+ storyId: story('provider-router'),
63
+ },
64
+ {
65
+ id: 'as-router',
66
+ summary: 'a link rendered by a router component supplied through `as`',
67
+ facts: {name: 'Settings', current: null},
68
+ storyId: story('as-router'),
69
+ },
70
+ ] as const satisfies ReadonlyArray<LinkA11yState>;
71
+
72
+ export const LINK_A11Y_EXCLUSIONS = [
73
+ {
74
+ owner: 'Link component tests and family:navigation-destinations FR7',
75
+ reason:
76
+ 'A disabled Link renders an href-less anchor that is neither a link nor focusable. That disabled semantics is member-owned, so disabled states are not bound to this available-link contract.',
77
+ },
78
+ {
79
+ owner: 'Link component tests (button rendering)',
80
+ reason:
81
+ 'A Link without href renders a button for an action; the button contract, not this one, owns that role.',
82
+ },
83
+ {
84
+ owner: 'family:navigation-destinations and its verification map',
85
+ reason:
86
+ 'Which destinations are accepted or blocked, and how rejected destinations render, is navigation safety rather than an accessibility outcome.',
87
+ },
88
+ {
89
+ owner:
90
+ 'BreadcrumbItem, SideNavItem, TopNavItem, Citation, and ClickableCard',
91
+ reason:
92
+ 'Links inside navigation aggregates and enlarged surfaces bind separately; this first binding covers the Link component and both custom-router seams.',
93
+ },
94
+ {
95
+ owner: 'Link visual and Tooltip evidence',
96
+ reason:
97
+ 'Underline, hover color, focus ring paint, truncation, and tooltip presentation require rendered and component checks.',
98
+ },
99
+ {
100
+ owner: 'AST-009',
101
+ reason:
102
+ 'This binding claims DOM, accessibility-tree, and real-browser behavior, not spoken wording, virtual-cursor, or assistive-technology activation.',
103
+ },
104
+ ] as const;
@@ -0,0 +1,127 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ /** @vitest-environment jsdom */
3
+
4
+ /** Fast DOM binding for Link against the shared Link contract. */
5
+
6
+ import {cleanup, render} from '@testing-library/react';
7
+ import {describe, expect, it} from 'vitest';
8
+ import {
9
+ LINK_PATTERN,
10
+ checkAccessibilitySpec,
11
+ createJsdomHarness,
12
+ expectAccessibilitySpec,
13
+ summarize,
14
+ type BindingResult,
15
+ } from '@astryxdesign/a11y-spec';
16
+ import {LINK_A11Y_RENDERS} from './Link.a11y.renders';
17
+ import {
18
+ LINK_A11Y_EXCLUSIONS,
19
+ LINK_A11Y_STATES,
20
+ type LinkA11yRow,
21
+ } from './Link.a11y.states';
22
+
23
+ const SUBJECT_SELECTOR = '[data-a11y-link]';
24
+
25
+ function subjectFor(): HTMLElement {
26
+ const subjects = document.querySelectorAll(SUBJECT_SELECTOR);
27
+ if (subjects.length !== 1 || !(subjects[0] instanceof HTMLElement)) {
28
+ throw new Error(
29
+ `binding rendered ${subjects.length} link subjects instead of one`,
30
+ );
31
+ }
32
+ return subjects[0];
33
+ }
34
+
35
+ async function checkState(state: LinkA11yRow): Promise<BindingResult> {
36
+ return checkAccessibilitySpec({
37
+ spec: LINK_PATTERN,
38
+ binding: 'Link',
39
+ state: state.id,
40
+ facts: state.facts,
41
+ mount: async () => {
42
+ render(LINK_A11Y_RENDERS[state.id]());
43
+ return createJsdomHarness({subject: subjectFor()});
44
+ },
45
+ unmount: cleanup,
46
+ });
47
+ }
48
+
49
+ describe('the shared Link pattern, jsdom lane', () => {
50
+ it.each(
51
+ LINK_A11Y_STATES.map(
52
+ state => [`Link [${state.id}]`, state.summary, state] as const,
53
+ ),
54
+ )('%s — %s', async (_id, _summary, state) => {
55
+ await expectAccessibilitySpec({
56
+ spec: LINK_PATTERN,
57
+ binding: 'Link',
58
+ state: state.id,
59
+ facts: state.facts,
60
+ render: () => {
61
+ render(LINK_A11Y_RENDERS[state.id]());
62
+ },
63
+ subject: subjectFor,
64
+ cleanup,
65
+ });
66
+ });
67
+
68
+ it('runs DOM expectations and reports tree and browser outcomes as unrun', async () => {
69
+ const results: BindingResult[] = [];
70
+ for (const state of LINK_A11Y_STATES) {
71
+ results.push(await checkState(state));
72
+ }
73
+ const report = summarize(LINK_PATTERN, results);
74
+ expect(report.counts.pass).toBeGreaterThan(0);
75
+ expect([...report.unrunLayers].sort()).toEqual([
76
+ 'accessibility-tree',
77
+ 'real-browser',
78
+ ]);
79
+ expect(report.counts.unexpectedPass).toBe(0);
80
+ });
81
+ });
82
+
83
+ describe('the Link binding inventory', () => {
84
+ it('names a distinct checked-in story for every state', () => {
85
+ const ids = LINK_A11Y_STATES.map(state => state.storyId);
86
+ expect(new Set(ids).size).toBe(ids.length);
87
+ });
88
+
89
+ it('records every adjacent exclusion with an owner and reason', () => {
90
+ expect(LINK_A11Y_EXCLUSIONS.every(row => row.owner && row.reason)).toBe(
91
+ true,
92
+ );
93
+ });
94
+
95
+ it('renders an anchor with a destination for every state', () => {
96
+ for (const state of LINK_A11Y_STATES) {
97
+ render(LINK_A11Y_RENDERS[state.id]());
98
+ const subject = subjectFor();
99
+ expect(subject.tagName, state.id).toBe('A');
100
+ expect(subject.getAttribute('href'), state.id).not.toBeNull();
101
+ cleanup();
102
+ }
103
+ });
104
+
105
+ it('renders both custom-router seams through the router component', () => {
106
+ for (const id of ['provider-router', 'as-router'] as const) {
107
+ render(LINK_A11Y_RENDERS[id]());
108
+ expect(subjectFor().hasAttribute('data-a11y-link-router'), id).toBe(true);
109
+ cleanup();
110
+ }
111
+ for (const state of LINK_A11Y_STATES.filter(
112
+ row => row.id !== 'provider-router' && row.id !== 'as-router',
113
+ )) {
114
+ render(LINK_A11Y_RENDERS[state.id]());
115
+ expect(subjectFor().hasAttribute('data-a11y-link-router'), state.id).toBe(
116
+ false,
117
+ );
118
+ cleanup();
119
+ }
120
+ });
121
+
122
+ it('opens the external state in a new tab', () => {
123
+ render(LINK_A11Y_RENDERS['external-new-tab']());
124
+ expect(subjectFor().getAttribute('target')).toBe('_blank');
125
+ cleanup();
126
+ });
127
+ });
@@ -29,7 +29,7 @@ const anatomy = [
29
29
  name: 'Code block',
30
30
  required: false,
31
31
  description:
32
- 'Fenced code block; a custom code renderer replaces the default part.',
32
+ 'Fenced code block; long lines wrap rather than scroll sideways. A custom code renderer replaces the default part.',
33
33
  },
34
34
  {
35
35
  name: 'Blockquote',
@@ -19,8 +19,10 @@
19
19
  * The timed region inside each process is the spec's protocol, unsmoothed:
20
20
  * ten untimed warmups, then one median of nine alternating paired rounds,
21
21
  * for a stable plugin list and a recreated equivalent one, against the
22
- * omitted baseline. One over-budget A/B triggers two fresh-process attempts;
23
- * the unchanged budget fails only when at least two of the three exceed it.
22
+ * omitted baseline. A first attempt within budget on both paths certifies
23
+ * them. Otherwise up to nine fresh-process attempts decide each path on its
24
+ * own: a path fails the unchanged budget when a majority of the nine (five)
25
+ * exceed it, and collection stops as soon as both paths are decided.
24
26
  */
25
27
 
26
28
  import {execFileSync} from 'node:child_process';
@@ -166,18 +168,64 @@ function logBenchmarkResult(result: BenchmarkResult, attempt: number): void {
166
168
  );
167
169
  }
168
170
 
171
+ /** The two paths FR23 measures; each is held to the budget on its own. */
172
+ const FR23_PATHS = ['stable', 'recreated'] as const;
173
+ type FR23Path = (typeof FR23_PATHS)[number];
174
+
175
+ /**
176
+ * Fresh-process attempts that decide a path after a noisy first one. On CI a
177
+ * clean representative set measures about 1.19 at 500 sections with about
178
+ * ±0.04 of sample noise, so a single attempt lands over the budget roughly
179
+ * one time in ten; a majority of nine does so well under one time in a
180
+ * thousand, while a slowdown of 0.1 or more still exceeds it nearly always.
181
+ */
182
+ const FR23_ATTEMPTS = 9;
183
+ const FR23_MAJORITY = Math.floor(FR23_ATTEMPTS / 2) + 1;
184
+
185
+ function attemptsOverBudget(
186
+ attempts: ReadonlyArray<BenchmarkResult>,
187
+ fr23Path: FR23Path,
188
+ ): number {
189
+ return attempts.filter(result => result[fr23Path] > FR23_BUDGET).length;
190
+ }
191
+
192
+ /** Whether the remaining attempts can no longer change this path's verdict. */
193
+ function pathDecided(
194
+ attempts: ReadonlyArray<BenchmarkResult>,
195
+ fr23Path: FR23Path,
196
+ ): boolean {
197
+ const over = attemptsOverBudget(attempts, fr23Path);
198
+ return over >= FR23_MAJORITY || attempts.length - over >= FR23_MAJORITY;
199
+ }
200
+
169
201
  function collectFR23Attempts(
170
202
  sections: number,
171
203
  runAttempt: (sections: number) => BenchmarkResult = runSettledBenchmark,
172
204
  ): ReadonlyArray<BenchmarkResult> {
173
205
  const attempts = [runAttempt(sections)];
174
- if (exceedsFR23Budget(attempts[0])) {
175
- attempts.push(runAttempt(sections));
206
+ if (!exceedsFR23Budget(attempts[0])) {
207
+ return attempts;
208
+ }
209
+ while (
210
+ attempts.length < FR23_ATTEMPTS &&
211
+ !FR23_PATHS.every(fr23Path => pathDecided(attempts, fr23Path))
212
+ ) {
176
213
  attempts.push(runAttempt(sections));
177
214
  }
178
215
  return attempts;
179
216
  }
180
217
 
218
+ /** The paths whose attempts exceed the budget in a majority of nine. */
219
+ function pathsOverBudget(
220
+ attempts: ReadonlyArray<BenchmarkResult>,
221
+ ): ReadonlyArray<FR23Path> {
222
+ return FR23_PATHS.filter(fr23Path =>
223
+ attempts.length > 1
224
+ ? attemptsOverBudget(attempts, fr23Path) >= FR23_MAJORITY
225
+ : attemptsOverBudget(attempts, fr23Path) > 0,
226
+ );
227
+ }
228
+
181
229
  describe('Markdown FR23 helper overhead', () => {
182
230
  it('reports one nine-pair median without selecting the cheapest baseline', () => {
183
231
  // Paired ratios: four 1.1s, one 1.2, four 10s. The median is 1.2,
@@ -218,35 +266,104 @@ describe('Markdown FR23 helper overhead', () => {
218
266
  }
219
267
  });
220
268
 
221
- it('retries one over-budget A/B twice and uses the three-attempt majority', () => {
269
+ describe('deciding each path from fresh-process attempts', () => {
222
270
  const result = (stable: number, recreated: number): BenchmarkResult => ({
223
- sections: 200,
271
+ sections: 500,
224
272
  stable,
225
273
  recreated,
226
274
  control: 1,
227
275
  });
228
- const passFirst = [result(1.2, 1.22)];
229
- expect(collectFR23Attempts(200, () => passFirst.shift()!)).toHaveLength(1);
230
-
231
- const retryResults = [
232
- result(1.27, 1.1),
233
- result(1.2, 1.22),
234
- result(1.18, 1.19),
235
- ];
236
- const attempts = collectFR23Attempts(200, () => retryResults.shift()!);
237
- expect(attempts).toHaveLength(3);
238
- expect(attempts.filter(exceedsFR23Budget)).toHaveLength(1);
239
-
240
- const majorityResults = [
241
- result(1.27, 1.1),
242
- result(1.2, 1.26),
243
- result(1.18, 1.19),
244
- ];
245
- expect(
246
- collectFR23Attempts(200, () => majorityResults.shift()!).filter(
247
- exceedsFR23Budget,
248
- ),
249
- ).toHaveLength(2);
276
+ const run = (results: BenchmarkResult[]) => {
277
+ const queue = [...results];
278
+ let calls = 0;
279
+ const attempts = collectFR23Attempts(500, () => {
280
+ calls++;
281
+ const next = queue.shift();
282
+ if (next == null) {
283
+ throw new Error('asked for an unplanned attempt');
284
+ }
285
+ return next;
286
+ });
287
+ return {attempts, calls, failing: pathsOverBudget(attempts)};
288
+ };
289
+
290
+ it('certifies both paths from one attempt within budget', () => {
291
+ const {calls, failing} = run([result(1.2, 1.22)]);
292
+ expect(calls).toBe(1);
293
+ expect(failing).toEqual([]);
294
+ });
295
+
296
+ it('fails a single attempt over budget only if no retry ran', () => {
297
+ expect(pathsOverBudget([result(1.26, 1.2)])).toEqual(['stable']);
298
+ });
299
+
300
+ it('judges each path by its own majority, not either path per attempt', () => {
301
+ // Main run 38393555595: stable was over in attempt 1 and recreated in
302
+ // attempt 2, which the old either-path-per-attempt count read as a
303
+ // two-of-three failure although neither path was over in a majority.
304
+ const {attempts, failing} = run([
305
+ result(1.256, 1.201),
306
+ result(1.145, 1.275),
307
+ result(1.183, 1.187),
308
+ result(1.18, 1.2),
309
+ result(1.19, 1.21),
310
+ result(1.2, 1.22),
311
+ result(1.17, 1.19),
312
+ ]);
313
+ // Each path needs five attempts within budget; each had one over.
314
+ expect(attempts).toHaveLength(6);
315
+ expect(failing).toEqual([]);
316
+ });
317
+
318
+ it('stops as soon as both paths are decided', () => {
319
+ // Recreated is decided after five passes, stable after one more.
320
+ const {calls} = run([
321
+ result(1.3, 1.2),
322
+ result(1.2, 1.2),
323
+ result(1.2, 1.2),
324
+ result(1.2, 1.2),
325
+ result(1.2, 1.2),
326
+ result(1.2, 1.2),
327
+ ]);
328
+ expect(calls).toBe(6);
329
+ });
330
+
331
+ it('fails a path that is over budget in a majority of nine', () => {
332
+ const {attempts, failing} = run([
333
+ result(1.3, 1.2),
334
+ result(1.27, 1.2),
335
+ result(1.2, 1.2),
336
+ result(1.26, 1.21),
337
+ result(1.22, 1.2),
338
+ result(1.28, 1.19),
339
+ result(1.26, 1.2),
340
+ ]);
341
+ expect(attempts).toHaveLength(7);
342
+ expect(failing).toEqual(['stable']);
343
+ });
344
+
345
+ it('fails a consistent slowdown on both paths after five attempts', () => {
346
+ const {calls, failing} = run(
347
+ Array.from({length: 9}, () => result(1.4, 1.42)),
348
+ );
349
+ expect(calls).toBe(5);
350
+ expect(failing).toEqual(['stable', 'recreated']);
351
+ });
352
+
353
+ it('never runs more than nine attempts', () => {
354
+ const {calls} = run([
355
+ result(1.3, 1.3),
356
+ result(1.2, 1.2),
357
+ result(1.3, 1.3),
358
+ result(1.2, 1.2),
359
+ result(1.3, 1.3),
360
+ result(1.2, 1.2),
361
+ result(1.3, 1.3),
362
+ result(1.2, 1.2),
363
+ result(1.3, 1.3),
364
+ ]);
365
+ expect(calls).toBe(9);
366
+ });
250
367
  });
251
368
 
252
369
  it.each([200, 500])(
@@ -257,28 +374,32 @@ describe('Markdown FR23 helper overhead', () => {
257
374
  // attempt counts: a value over budget is machine noise, so that process
258
375
  // is replaced before its A/B result can certify or fail the helpers.
259
376
  //
260
- // A single over-budget A/B is also noisy at this threshold. Retry that
261
- // result twice in fresh processes and fail only when a majority (at
262
- // least two of the three independent attempts) exceeds the unchanged
263
- // 1.25 budget.
377
+ // A single over-budget A/B is also noisy at this threshold. Each path
378
+ // is then decided by a majority of up to nine fresh-process attempts
379
+ // against the unchanged 1.25 budget.
264
380
  const attempts = collectFR23Attempts(sections);
265
- if (attempts.length === 3) {
381
+ if (attempts.length > 1) {
266
382
  console.log(
267
383
  ` ${sections} sections: first A/B exceeded ${FR23_BUDGET};` +
268
- ' retried twice in fresh processes',
384
+ ` decided each path from ${attempts.length} fresh processes`,
269
385
  );
270
386
  }
271
387
  attempts.forEach((result, index) =>
272
388
  logBenchmarkResult(result, index + 1),
273
389
  );
274
- const overBudget = attempts.filter(exceedsFR23Budget).length;
390
+ const failing = pathsOverBudget(attempts);
275
391
  expect(
276
- overBudget,
277
- `${overBudget} of ${attempts.length} fresh FR23 attempts exceeded ` +
278
- `the ${FR23_BUDGET} budget`,
279
- ).toBeLessThan(2);
392
+ failing,
393
+ failing
394
+ .map(
395
+ fr23Path =>
396
+ `${fr23Path}: ${attemptsOverBudget(attempts, fr23Path)} of ` +
397
+ `${attempts.length} fresh FR23 attempts exceeded the ${FR23_BUDGET} budget`,
398
+ )
399
+ .join('; '),
400
+ ).toEqual([]);
280
401
  },
281
- 300_000,
402
+ 600_000,
282
403
  );
283
404
 
284
405
  it('fails loudly when the benchmark process cannot produce a result', () => {
@@ -50,6 +50,7 @@ system_specs:
50
50
  spec:AST-036/DEC-4,
51
51
  spec:AST-061/DEC-5,
52
52
  spec:AST-061/DEC-6,
53
+ spec:AST-061/DEC-9,
53
54
  spec:AST-064/DEC-6,
54
55
  spec:AST-036/DEC-12,
55
56
  ]
@@ -64,18 +65,37 @@ constrained renderer seams. Callers may opt into the canonical plugin protocol f
64
65
  bounded source syntax, immutable document transformation, typed extension
65
66
  rendering, and native typed document-start frontmatter. They may separately opt into dollar-delimited math by supplying one typed
66
67
  renderer for both inline and display expressions. The parser accepts matching
67
- explicit options. Existing parsing, rendering, styling, and streaming behavior
68
- remain unchanged when plugins and math are absent.
68
+ explicit options. Plugins and math change nothing when absent; the default Code
69
+ block path renders `diff` and `diff-<language>` fences as CodeBlock's diff layer
70
+ (FR29) and wraps every fence's long lines (`spec:AST-061` FR5, DEC-9), and every
71
+ other parsing, rendering, styling, and streaming behavior is the released one.
69
72
 
70
73
  ## Compatibility and migration
71
74
 
72
- - Released default preserved: `yes`
73
- - Compatibility class: additive, opt-in public API; existing parser nodes, DOM,
74
- styling, targets, dollar-delimited text, `components`, and `inlinePlugins` remain
75
- unchanged unless the caller supplies `plugins`, supplies `components.math`, or
76
- passes the matching explicit parser option.
75
+ - Released default preserved: `yes`, except the default Code block path for `diff` fences (FR29, below) and its long-line wrap (below)
76
+ - Compatibility class: additive, opt-in public API, with two visible
77
+ default-path rendering changes (the FR29 diff layer and the fenced-code wrap,
78
+ below); existing parser nodes, DOM, styling, targets,
79
+ dollar-delimited text, `components`, and `inlinePlugins` remain unchanged unless
80
+ the caller supplies `plugins`, supplies `components.math`, or passes the matching
81
+ explicit parser option.
77
82
  - Controlled/uncontrolled behavior: not applicable
78
83
  - Migration decision: none
84
+ - Default-path rendering change for diff fences (FR29), a visible default change
85
+ and not a breaking one: a `diff` fence that rendered as plain text renders as CodeBlock's diff
86
+ layer over plaintext with a `diff` label, and a `diff-<language>` fence —
87
+ which rendered as plain text with a `diff` label and viewport name, its
88
+ leading word being all Markdown read — as the layer over that language with
89
+ a `<language> diff` label; the header, copy, disclosure, and viewport name
90
+ stay, and a supplied `code` renderer's contract is unchanged. The way back to
91
+ plain text is a `text` fence or a custom `code` renderer. Every other fence
92
+ is unchanged apart from the wrap below.
93
+ - Default-path wrap change (`spec:AST-061` FR5, DEC-9), visual and not breaking:
94
+ the default Code block wraps long lines instead of scrolling them sideways, so
95
+ a fence whose lines no longer overflow also no longer takes the conditional
96
+ scroll-region tab stop. Text, line numbers, syntax colours, copy output, and a
97
+ supplied `code` renderer's contract are unchanged. The way back to sideways
98
+ scrolling is a custom `code` renderer.
79
99
  - Default-path layout change (FR26–FR28): a default table's columns may become
80
100
  wider than the released fixed buckets, never narrower; header labels wrap
81
101
  instead of truncating; a table that squashed its columns may now scroll.
@@ -228,6 +248,7 @@ unions. Enabled calls return the explicit `InlineNodeWithMath` and
228
248
  | FR26 | On the default table path each column has a content-derived width floor: the larger of its own longest unbreakable token and a readable minimum derived from that column's content, bounded so a table of short columns still fits its container. The floor applies to the cell's text, not to its padded box, so it means the same amount of content at every density. A header label contributes its own readable minimum to that comparison. A table whose floors exceed the container grows and scrolls rather than dividing the container equally. |
229
249
  | FR27 | Default table header cells never truncate: no ellipsis and no width clamp. A header reads in full, on one line up to a bounded width and wrapping past it. Inline code inside a default table cell keeps its token whole rather than breaking mid-token; a supplied `inlineCode` renderer owns its own wrapping. |
230
250
  | FR28 | Markdown's Table block owns spacing, sizing, and alignment. The nested Table's own Scroll region remains the sole owner of the table's horizontal overflow, its accessible name, and its conditional keyboard focusability; Markdown adds no second scroll container, name, or tab stop of its own. |
251
+ | FR29 | On the default Code block path the fence's full first word selects CodeBlock's diff layer (`component:CodeBlock` FR9): `diff` renders the layer over plaintext, `diff-<language>` the layer over `<language>` (the language read by the same leading-word rule as any other fence); every other fence takes no diff layer. A supplied `code` renderer's contract is unchanged: it keeps receiving the leading word characters as `language` — `diff` for both `diff` and `diff-<language>` fences — and nothing new. |
231
252
 
232
253
  FR23 includes CommonMark-compatible lazy paragraph continuation inside blockquotes
233
254
  and ordered, unordered, or task-list items. Omitting a repeated quote marker or
@@ -421,22 +442,23 @@ and this change preserves the existing spelling exactly.
421
442
 
422
443
  ## Verification map
423
444
 
424
- | Contract | Verification | Representative states | Failure signal |
425
- | ---------------------- | --------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
426
- | 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. |
427
- | 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. |
428
- | 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. |
429
- | 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. |
430
- | 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. |
431
- | 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. |
432
- | 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. |
433
- | FR23 | parser, incremental, renderer, nesting, and public option tests | mixed lists; escaped prose/code table cells, nested inline code, full/streaming parity; autolink omitted/enabled | A mixed list splits or loses state, an escaped table pipe changes cells or exposes its backslash, or autolink changes other syntax. |
434
- | 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. |
435
- | 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. |
436
- | FR26–FR28 | `Markdown.test.tsx` and a real-browser layer over checked-in narrow-width stories | short, prose, and token-heavy tables; header-only rows and empty cells; long headers over short bodies; reading columns from phone to document width | A column collapses below its content floor, cell padding eats the floor, a header truncates, an inline-code token breaks mid-token, or a second scroll viewport, name, or tab stop appears around a table. |
437
- | 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. |
438
- | Navigation contract | `parser.test.ts`, `Markdown.test.tsx`, and `Markdown.renderBoundary.test.tsx` | Parsed and rendered links, including transformed built-in links; accepted ordinary schemes; rejected destinations; links versus images | A blocked destination reaches navigation or a custom link renderer, or resource policy narrows accepted navigation. |
439
- | 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. |
445
+ | Contract | Verification | Representative states | Failure signal |
446
+ | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
447
+ | 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. |
448
+ | 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. |
449
+ | 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. |
450
+ | 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. |
451
+ | 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. |
452
+ | 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. |
453
+ | 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. |
454
+ | FR23 | parser, incremental, renderer, nesting, and public option tests | mixed lists; escaped prose/code table cells, nested inline code, full/streaming parity; autolink omitted/enabled | A mixed list splits or loses state, an escaped table pipe changes cells or exposes its backslash, or autolink changes other syntax. |
455
+ | 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. |
456
+ | 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. |
457
+ | FR26–FR28 | `Markdown.test.tsx` and a real-browser layer over checked-in narrow-width stories | short, prose, and token-heavy tables; header-only rows and empty cells; long headers over short bodies; reading columns from phone to document width | A column collapses below its content floor, cell padding eats the floor, a header truncates, an inline-code token breaks mid-token, or a second scroll viewport, name, or tab stop appears around a table. |
458
+ | FR29 | `Markdown.test.tsx` (a `diff` fence → plaintext + layer, a `diff-tsx` fence → tsx + layer, a plain fence unchanged, a custom `code` renderer receives `language="diff"` for both diff fences and nothing new) | default path and custom renderer, `diff` and `diff-<language>` fences | a fence word other than `diff`/`diff-<language>` reaching the layer, or a custom renderer receiving a new prop or a different `language` |
459
+ | 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. |
460
+ | Navigation contract | `parser.test.ts`, `Markdown.test.tsx`, and `Markdown.renderBoundary.test.tsx` | Parsed and rendered links, including transformed built-in links; accepted ordinary schemes; rejected destinations; links versus images | A blocked destination reaches navigation or a custom link renderer, or resource policy narrows accepted navigation. |
461
+ | 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. |
440
462
 
441
463
  Focused tests continue to pin all nine current target names and default block
442
464
  placement. Math intentionally adds no target and no default anatomy.