@astryxdesign/core 0.1.8 → 0.1.9

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 (487) hide show
  1. package/CHANGELOG.md +100 -0
  2. package/dist/AppShell/AppShell.d.ts.map +1 -1
  3. package/dist/AppShell/AppShell.js +6 -2
  4. package/dist/AppShell/AppShellMobileContext.d.ts +8 -0
  5. package/dist/AppShell/AppShellMobileContext.d.ts.map +1 -1
  6. package/dist/Avatar/Avatar.d.ts +18 -2
  7. package/dist/Avatar/Avatar.d.ts.map +1 -1
  8. package/dist/Avatar/Avatar.js +63 -7
  9. package/dist/Avatar/AvatarStatusDot.d.ts +33 -5
  10. package/dist/Avatar/AvatarStatusDot.d.ts.map +1 -1
  11. package/dist/Avatar/AvatarStatusDot.js +144 -19
  12. package/dist/Banner/Banner.d.ts +9 -1
  13. package/dist/Banner/Banner.d.ts.map +1 -1
  14. package/dist/Banner/Banner.js +31 -2
  15. package/dist/Breadcrumbs/BreadcrumbItem.d.ts +31 -2
  16. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  17. package/dist/Breadcrumbs/BreadcrumbItem.js +208 -6
  18. package/dist/Breadcrumbs/index.d.ts +5 -0
  19. package/dist/Breadcrumbs/index.d.ts.map +1 -1
  20. package/dist/Breadcrumbs/index.js +9 -1
  21. package/dist/Button/Button.d.ts +8 -2
  22. package/dist/Button/Button.d.ts.map +1 -1
  23. package/dist/Button/Button.js +31 -2
  24. package/dist/ButtonGroup/ButtonGroup.d.ts +9 -1
  25. package/dist/ButtonGroup/ButtonGroup.d.ts.map +1 -1
  26. package/dist/ButtonGroup/ButtonGroup.js +30 -1
  27. package/dist/Calendar/Calendar.d.ts.map +1 -1
  28. package/dist/Calendar/Calendar.js +30 -2
  29. package/dist/Calendar/dayCellUtils.js +1 -1
  30. package/dist/Card/Card.d.ts +8 -2
  31. package/dist/Card/Card.d.ts.map +1 -1
  32. package/dist/Card/Card.js +28 -2
  33. package/dist/Carousel/Carousel.d.ts +4 -1
  34. package/dist/Carousel/Carousel.d.ts.map +1 -1
  35. package/dist/Carousel/Carousel.js +24 -4
  36. package/dist/Chat/ChatComposer.d.ts +8 -0
  37. package/dist/Chat/ChatComposer.d.ts.map +1 -1
  38. package/dist/Chat/ChatComposer.js +46 -7
  39. package/dist/Chat/ChatComposerDrawer.d.ts +2 -0
  40. package/dist/Chat/ChatComposerDrawer.d.ts.map +1 -1
  41. package/dist/Chat/ChatComposerDrawer.js +11 -1
  42. package/dist/Chat/ChatComposerInput.d.ts +20 -3
  43. package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
  44. package/dist/Chat/ChatComposerInput.js +35 -3
  45. package/dist/Chat/ChatContext.d.ts +33 -0
  46. package/dist/Chat/ChatContext.d.ts.map +1 -1
  47. package/dist/Chat/ChatContext.js +16 -2
  48. package/dist/Chat/ChatDictationButton.d.ts.map +1 -1
  49. package/dist/Chat/ChatDictationButton.js +1 -1
  50. package/dist/Chat/ChatLayout.d.ts.map +1 -1
  51. package/dist/Chat/ChatLayoutScrollButton.d.ts.map +1 -1
  52. package/dist/Chat/ChatLayoutScrollButton.js +4 -4
  53. package/dist/Chat/ChatMessageMetadata.d.ts.map +1 -1
  54. package/dist/Chat/ChatSendButton.d.ts.map +1 -1
  55. package/dist/Chat/ChatSendButton.js +7 -1
  56. package/dist/Chat/ChatToolCalls.d.ts +5 -1
  57. package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
  58. package/dist/Chat/ChatToolCalls.js +18 -9
  59. package/dist/Chat/index.d.ts +2 -1
  60. package/dist/Chat/index.d.ts.map +1 -1
  61. package/dist/Chat/index.js +1 -1
  62. package/dist/Chat/useChatStreamScroll.d.ts +2 -1
  63. package/dist/Chat/useChatStreamScroll.d.ts.map +1 -1
  64. package/dist/Chat/useChatStreamScroll.js +22 -8
  65. package/dist/Chat/useSpeechRecognition.d.ts +0 -1
  66. package/dist/Chat/useSpeechRecognition.d.ts.map +1 -1
  67. package/dist/Chat/useSpeechRecognition.js +1 -1
  68. package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
  69. package/dist/CheckboxInput/CheckboxInput.js +1 -1
  70. package/dist/CheckboxList/CheckboxListItem.d.ts +20 -1
  71. package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
  72. package/dist/CheckboxList/CheckboxListItem.js +11 -1
  73. package/dist/ClickableCard/ClickableCard.d.ts +9 -2
  74. package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
  75. package/dist/ClickableCard/ClickableCard.js +5 -2
  76. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  77. package/dist/CodeBlock/CodeBlock.js +8 -1
  78. package/dist/CommandPalette/CommandPalette.d.ts +1 -1
  79. package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
  80. package/dist/CommandPalette/CommandPalette.js +3 -1
  81. package/dist/CommandPalette/CommandPaletteGroup.d.ts.map +1 -1
  82. package/dist/CommandPalette/CommandPaletteGroup.js +9 -2
  83. package/dist/CommandPalette/CommandPaletteInput.d.ts +7 -1
  84. package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
  85. package/dist/CommandPalette/CommandPaletteInput.js +6 -1
  86. package/dist/DateInput/DateInput.d.ts +47 -1
  87. package/dist/DateInput/DateInput.d.ts.map +1 -1
  88. package/dist/DateInput/DateInput.js +26 -2
  89. package/dist/DateInput/index.d.ts +1 -1
  90. package/dist/DateInput/index.d.ts.map +1 -1
  91. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  92. package/dist/DateTimeInput/DateTimeInput.js +10 -1
  93. package/dist/Dialog/Dialog.d.ts +3 -0
  94. package/dist/Dialog/Dialog.d.ts.map +1 -1
  95. package/dist/Dialog/Dialog.js +57 -6
  96. package/dist/Dialog/DialogContext.d.ts +7 -0
  97. package/dist/Dialog/DialogContext.d.ts.map +1 -1
  98. package/dist/Dialog/DialogContext.js +2 -2
  99. package/dist/Dialog/DialogHeader.d.ts +5 -2
  100. package/dist/Dialog/DialogHeader.d.ts.map +1 -1
  101. package/dist/Dialog/DialogHeader.js +6 -1
  102. package/dist/Divider/Divider.js +1 -1
  103. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts +1 -2
  104. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -1
  105. package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +1 -2
  106. package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts +1 -2
  107. package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts.map +1 -1
  108. package/dist/DropdownMenu/DropdownMenuRadioGroup.js +1 -2
  109. package/dist/Field/Field.d.ts.map +1 -1
  110. package/dist/Field/Field.js +2 -3
  111. package/dist/Field/FieldLabel.d.ts +1 -1
  112. package/dist/Field/FieldLabel.d.ts.map +1 -1
  113. package/dist/Field/FieldLabel.js +42 -15
  114. package/dist/FieldStatus/FieldStatus.d.ts +8 -0
  115. package/dist/FieldStatus/FieldStatus.d.ts.map +1 -1
  116. package/dist/FieldStatus/FieldStatus.js +22 -4
  117. package/dist/FileInput/FileInput.d.ts.map +1 -1
  118. package/dist/FileInput/FileInput.js +10 -2
  119. package/dist/HoverCard/HoverCard.d.ts +9 -1
  120. package/dist/HoverCard/HoverCard.d.ts.map +1 -1
  121. package/dist/HoverCard/HoverCard.js +2 -0
  122. package/dist/HoverCard/useHoverCard.d.ts +8 -0
  123. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  124. package/dist/HoverCard/useHoverCard.js +7 -2
  125. package/dist/Icon/Icon.d.ts +7 -2
  126. package/dist/Icon/Icon.d.ts.map +1 -1
  127. package/dist/Item/Item.d.ts +6 -1
  128. package/dist/Item/Item.d.ts.map +1 -1
  129. package/dist/Item/Item.js +22 -1
  130. package/dist/Layer/useLayer.d.ts +6 -0
  131. package/dist/Layer/useLayer.d.ts.map +1 -1
  132. package/dist/Layer/useLayer.js +2 -0
  133. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  134. package/dist/MobileNav/MobileNav.js +6 -1
  135. package/dist/MobileNav/MobileNavToggle.d.ts.map +1 -1
  136. package/dist/MobileNav/MobileNavToggle.js +4 -0
  137. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  138. package/dist/MultiSelector/MultiSelector.js +39 -12
  139. package/dist/MultiSelector/hooks.d.ts.map +1 -1
  140. package/dist/MultiSelector/hooks.js +16 -0
  141. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  142. package/dist/Outline/Outline.d.ts +0 -1
  143. package/dist/Outline/Outline.d.ts.map +1 -1
  144. package/dist/Outline/Outline.js +0 -1
  145. package/dist/OverflowList/OverflowList.d.ts +20 -3
  146. package/dist/OverflowList/OverflowList.d.ts.map +1 -1
  147. package/dist/OverflowList/OverflowList.js +29 -4
  148. package/dist/Popover/Popover.d.ts.map +1 -1
  149. package/dist/Popover/Popover.js +3 -2
  150. package/dist/Popover/usePopover.d.ts.map +1 -1
  151. package/dist/Popover/usePopover.js +3 -9
  152. package/dist/PowerSearch/PowerSearchEditPopover.js +1 -1
  153. package/dist/PowerSearch/usePowerSearchSource.d.ts.map +1 -1
  154. package/dist/SelectableCard/SelectableCard.d.ts +9 -2
  155. package/dist/SelectableCard/SelectableCard.d.ts.map +1 -1
  156. package/dist/SelectableCard/SelectableCard.js +16 -13
  157. package/dist/Selector/Selector.d.ts.map +1 -1
  158. package/dist/Selector/Selector.js +4 -2
  159. package/dist/Selector/hooks.d.ts.map +1 -1
  160. package/dist/Selector/hooks.js +16 -0
  161. package/dist/SideNav/SideNavCollapseButton.d.ts.map +1 -1
  162. package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
  163. package/dist/SideNav/SideNavHeading.js +21 -8
  164. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  165. package/dist/Slider/Slider.d.ts.map +1 -1
  166. package/dist/Slider/Slider.js +19 -2
  167. package/dist/Stack/stack.stylex.d.ts +9 -9
  168. package/dist/Table/BaseTable.d.ts.map +1 -1
  169. package/dist/Table/BaseTable.js +3 -2
  170. package/dist/Table/index.d.ts +2 -0
  171. package/dist/Table/index.d.ts.map +1 -1
  172. package/dist/Table/index.js +1 -0
  173. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  174. package/dist/Table/plugins/rowStatus/index.d.ts +3 -0
  175. package/dist/Table/plugins/rowStatus/index.d.ts.map +1 -0
  176. package/dist/Table/plugins/rowStatus/index.js +3 -0
  177. package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts +60 -0
  178. package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts.map +1 -0
  179. package/dist/Table/plugins/rowStatus/useTableRowStatus.js +145 -0
  180. package/dist/Table/plugins/selection/useTableSelection.d.ts +13 -0
  181. package/dist/Table/plugins/selection/useTableSelection.d.ts.map +1 -1
  182. package/dist/Table/plugins/selection/useTableSelection.js +4 -1
  183. package/dist/Table/plugins/tree/useTableTreeData.d.ts +16 -0
  184. package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -1
  185. package/dist/Table/plugins/tree/useTableTreeData.js +87 -0
  186. package/dist/Table/plugins/tree/useTableTreeState.d.ts +6 -0
  187. package/dist/Table/plugins/tree/useTableTreeState.d.ts.map +1 -1
  188. package/dist/Table/plugins/tree/useTableTreeState.js +22 -1
  189. package/dist/Table/useBaseTablePlugins.d.ts.map +1 -1
  190. package/dist/Table/useBaseTablePlugins.js +5 -3
  191. package/dist/Thumbnail/Thumbnail.d.ts +26 -5
  192. package/dist/Thumbnail/Thumbnail.d.ts.map +1 -1
  193. package/dist/Thumbnail/Thumbnail.js +97 -54
  194. package/dist/Thumbnail/thumbnail.markers.stylex.d.ts +9 -0
  195. package/dist/Thumbnail/thumbnail.markers.stylex.d.ts.map +1 -0
  196. package/dist/Thumbnail/thumbnail.markers.stylex.js +14 -0
  197. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  198. package/dist/TimeInput/TimeInput.js +15 -4
  199. package/dist/Timestamp/Timestamp.d.ts +3 -1
  200. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  201. package/dist/Timestamp/Timestamp.js +8 -6
  202. package/dist/Toast/useToast.d.ts.map +1 -1
  203. package/dist/Toast/useToast.js +2 -5
  204. package/dist/ToggleButton/ToggleButtonGroup.d.ts +0 -1
  205. package/dist/ToggleButton/ToggleButtonGroup.d.ts.map +1 -1
  206. package/dist/ToggleButton/ToggleButtonGroup.js +1 -1
  207. package/dist/Tokenizer/Tokenizer.d.ts +1 -1
  208. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  209. package/dist/Tokenizer/Tokenizer.js +16 -4
  210. package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
  211. package/dist/TopNav/TopNavHeading.js +18 -8
  212. package/dist/TopNav/TopNavMegaMenu.js +10 -4
  213. package/dist/TopNav/TopNavMegaMenuFeaturedCard.d.ts +9 -1
  214. package/dist/TopNav/TopNavMegaMenuFeaturedCard.d.ts.map +1 -1
  215. package/dist/TopNav/TopNavMegaMenuFeaturedCard.js +8 -1
  216. package/dist/TopNav/TopNavMenu.d.ts +2 -1
  217. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  218. package/dist/TopNav/TopNavMenu.js +68 -6
  219. package/dist/TreeList/TreeList.d.ts +12 -3
  220. package/dist/TreeList/TreeList.d.ts.map +1 -1
  221. package/dist/TreeList/TreeList.js +2 -0
  222. package/dist/TreeList/TreeListBranches.d.ts +5 -0
  223. package/dist/TreeList/TreeListBranches.d.ts.map +1 -1
  224. package/dist/TreeList/TreeListBranches.js +10 -4
  225. package/dist/TreeList/TreeListItem.d.ts +8 -2
  226. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  227. package/dist/TreeList/TreeListItem.js +27 -7
  228. package/dist/TreeList/TreeListTypes.d.ts +25 -1
  229. package/dist/TreeList/TreeListTypes.d.ts.map +1 -1
  230. package/dist/TreeList/index.d.ts +2 -2
  231. package/dist/TreeList/index.d.ts.map +1 -1
  232. package/dist/astryx.css +28 -18
  233. package/dist/astryx.umd.js +51 -51
  234. package/dist/astryx.umd.js.map +4 -4
  235. package/dist/authoring/doc.d.ts.map +1 -1
  236. package/dist/docs-types.d.ts +8 -0
  237. package/dist/docs-types.d.ts.map +1 -1
  238. package/dist/hooks/computeOverflow.d.ts +47 -0
  239. package/dist/hooks/computeOverflow.d.ts.map +1 -0
  240. package/dist/hooks/computeOverflow.js +213 -0
  241. package/dist/hooks/index.d.ts +1 -0
  242. package/dist/hooks/index.d.ts.map +1 -1
  243. package/dist/hooks/index.js +2 -1
  244. package/dist/hooks/useDevWarning.d.ts +18 -0
  245. package/dist/hooks/useDevWarning.d.ts.map +1 -0
  246. package/dist/hooks/useDevWarning.js +49 -0
  247. package/dist/hooks/useOverflow.d.ts +19 -0
  248. package/dist/hooks/useOverflow.d.ts.map +1 -1
  249. package/dist/hooks/useOverflow.js +36 -25
  250. package/dist/i18n/resolve.d.ts.map +1 -1
  251. package/dist/i18n/resolve.js +7 -18
  252. package/dist/theme/Theme.d.ts.map +1 -1
  253. package/dist/theme/Theme.js +2 -7
  254. package/dist/theme/generateThemeRules.d.ts.map +1 -1
  255. package/dist/theme/generateThemeRules.js +51 -0
  256. package/dist/theme/hct.d.ts +0 -1
  257. package/dist/theme/hct.d.ts.map +1 -1
  258. package/dist/theme/hct.js +1 -1
  259. package/dist/theme/syntax/defineSyntaxTheme.d.ts.map +1 -1
  260. package/dist/theme/syntax/defineSyntaxTheme.js +2 -1
  261. package/dist/utils/devWarning.d.ts +42 -0
  262. package/dist/utils/devWarning.d.ts.map +1 -0
  263. package/dist/utils/devWarning.js +88 -0
  264. package/dist/utils/index.d.ts +3 -1
  265. package/dist/utils/index.d.ts.map +1 -1
  266. package/dist/utils/index.js +3 -2
  267. package/dist/utils/plainDate.d.ts +30 -0
  268. package/dist/utils/plainDate.d.ts.map +1 -1
  269. package/dist/utils/plainDate.js +53 -0
  270. package/dist/utils/types.d.ts +13 -0
  271. package/dist/utils/types.d.ts.map +1 -1
  272. package/locales/en.json +24 -0
  273. package/locales/fr-FR.json +14 -0
  274. package/locales/pseudo.json +18 -0
  275. package/package.json +1 -1
  276. package/src/AppShell/AppShell.tsx +6 -0
  277. package/src/AppShell/AppShellMobileContext.tsx +8 -0
  278. package/src/AppShell/useAppShellMobile.doc.mjs +8 -0
  279. package/src/Avatar/Avatar.doc.mjs +15 -3
  280. package/src/Avatar/Avatar.test.tsx +156 -1
  281. package/src/Avatar/Avatar.tsx +96 -4
  282. package/src/Avatar/AvatarStatusDot.doc.mjs +23 -12
  283. package/src/Avatar/AvatarStatusDot.test.tsx +284 -0
  284. package/src/Avatar/AvatarStatusDot.tsx +178 -21
  285. package/src/Banner/Banner.doc.mjs +9 -0
  286. package/src/Banner/Banner.test.tsx +30 -0
  287. package/src/Banner/Banner.tsx +47 -3
  288. package/src/Breadcrumbs/BreadcrumbItem.doc.mjs +26 -0
  289. package/src/Breadcrumbs/BreadcrumbItem.tsx +333 -14
  290. package/src/Breadcrumbs/Breadcrumbs.doc.mjs +2 -0
  291. package/src/Breadcrumbs/Breadcrumbs.test.tsx +284 -2
  292. package/src/Breadcrumbs/index.ts +27 -0
  293. package/src/Button/Button.doc.mjs +9 -0
  294. package/src/Button/Button.test.tsx +49 -0
  295. package/src/Button/Button.tsx +25 -1
  296. package/src/ButtonGroup/ButtonGroup.doc.mjs +3 -0
  297. package/src/ButtonGroup/ButtonGroup.test.tsx +37 -0
  298. package/src/ButtonGroup/ButtonGroup.tsx +32 -0
  299. package/src/Calendar/Calendar.doc.mjs +12 -1
  300. package/src/Calendar/Calendar.test.tsx +193 -0
  301. package/src/Calendar/Calendar.tsx +38 -16
  302. package/src/Calendar/dayCellUtils.test.ts +5 -0
  303. package/src/Calendar/dayCellUtils.ts +1 -1
  304. package/src/Card/Card.doc.mjs +9 -0
  305. package/src/Card/Card.test.tsx +21 -3
  306. package/src/Card/Card.tsx +35 -2
  307. package/src/Carousel/Carousel.doc.mjs +1 -0
  308. package/src/Carousel/Carousel.test.tsx +53 -0
  309. package/src/Carousel/Carousel.tsx +34 -4
  310. package/src/Chat/ChatComposer.doc.mjs +12 -3
  311. package/src/Chat/ChatComposer.test.tsx +115 -0
  312. package/src/Chat/ChatComposer.tsx +73 -9
  313. package/src/Chat/ChatComposerDrawer.test.tsx +61 -0
  314. package/src/Chat/ChatComposerDrawer.tsx +11 -1
  315. package/src/Chat/ChatComposerInput.doc.mjs +7 -0
  316. package/src/Chat/ChatComposerInput.test.tsx +103 -0
  317. package/src/Chat/ChatComposerInput.tsx +49 -3
  318. package/src/Chat/ChatContext.tsx +37 -10
  319. package/src/Chat/ChatDictationButton.tsx +4 -1
  320. package/src/Chat/ChatLayout.tsx +3 -1
  321. package/src/Chat/ChatLayoutScrollButton.tsx +7 -2
  322. package/src/Chat/ChatMessageMetadata.tsx +1 -2
  323. package/src/Chat/ChatSendButton.tsx +11 -1
  324. package/src/Chat/ChatToolCalls.test.tsx +64 -0
  325. package/src/Chat/ChatToolCalls.tsx +25 -4
  326. package/src/Chat/index.ts +5 -1
  327. package/src/Chat/useChatStreamScroll.test.tsx +49 -0
  328. package/src/Chat/useChatStreamScroll.ts +27 -10
  329. package/src/Chat/useSpeechRecognition.ts +1 -1
  330. package/src/Chat/useTriggerMenu.tsx +3 -1
  331. package/src/CheckboxInput/CheckboxInput.test.tsx +32 -1
  332. package/src/CheckboxInput/CheckboxInput.tsx +1 -1
  333. package/src/CheckboxList/CheckboxList.test.tsx +81 -3
  334. package/src/CheckboxList/CheckboxListItem.doc.mjs +33 -5
  335. package/src/CheckboxList/CheckboxListItem.tsx +41 -5
  336. package/src/ClickableCard/ClickableCard.doc.mjs +2 -0
  337. package/src/ClickableCard/ClickableCard.test.tsx +34 -0
  338. package/src/ClickableCard/ClickableCard.tsx +13 -3
  339. package/src/CodeBlock/CodeBlock.test.tsx +56 -0
  340. package/src/CodeBlock/CodeBlock.tsx +9 -1
  341. package/src/CommandPalette/CommandPalette.doc.mjs +1 -0
  342. package/src/CommandPalette/CommandPalette.tsx +3 -1
  343. package/src/CommandPalette/CommandPaletteGroup.test.tsx +71 -0
  344. package/src/CommandPalette/CommandPaletteGroup.tsx +12 -1
  345. package/src/CommandPalette/CommandPaletteInput.doc.mjs +26 -9
  346. package/src/CommandPalette/CommandPaletteInput.test.tsx +34 -0
  347. package/src/CommandPalette/CommandPaletteInput.tsx +11 -0
  348. package/src/ContextMenu/ContextMenu.test.tsx +193 -0
  349. package/src/DateInput/DateInput.doc.mjs +16 -0
  350. package/src/DateInput/DateInput.test.tsx +108 -0
  351. package/src/DateInput/DateInput.tsx +68 -3
  352. package/src/DateInput/index.ts +1 -0
  353. package/src/DateTimeInput/DateTimeInput.test.tsx +35 -0
  354. package/src/DateTimeInput/DateTimeInput.tsx +14 -2
  355. package/src/Dialog/Dialog.doc.mjs +2 -2
  356. package/src/Dialog/Dialog.test.tsx +77 -4
  357. package/src/Dialog/Dialog.tsx +89 -5
  358. package/src/Dialog/DialogContext.ts +9 -2
  359. package/src/Dialog/DialogHeader.doc.mjs +2 -2
  360. package/src/Dialog/DialogHeader.tsx +9 -2
  361. package/src/Divider/Divider.tsx +2 -2
  362. package/src/DropdownMenu/DropdownMenu.test.tsx +169 -0
  363. package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +1 -2
  364. package/src/DropdownMenu/DropdownMenuRadioGroup.tsx +1 -2
  365. package/src/Field/Field.test.tsx +57 -12
  366. package/src/Field/Field.tsx +6 -5
  367. package/src/Field/FieldLabel.tsx +8 -0
  368. package/src/FieldStatus/FieldStatus.doc.mjs +17 -5
  369. package/src/FieldStatus/FieldStatus.test.tsx +78 -40
  370. package/src/FieldStatus/FieldStatus.tsx +22 -4
  371. package/src/FileInput/FileInput.test.tsx +52 -3
  372. package/src/FileInput/FileInput.tsx +13 -2
  373. package/src/HoverCard/HoverCard.doc.mjs +13 -0
  374. package/src/HoverCard/HoverCard.test.tsx +21 -2
  375. package/src/HoverCard/HoverCard.tsx +11 -0
  376. package/src/HoverCard/useHoverCard.doc.mjs +2 -0
  377. package/src/HoverCard/useHoverCard.tsx +24 -2
  378. package/src/Icon/Icon.doc.mjs +3 -2
  379. package/src/Icon/Icon.tsx +7 -2
  380. package/src/IconButton/IconButton.doc.mjs +8 -0
  381. package/src/IconButton/IconButton.test.tsx +15 -18
  382. package/src/Item/Item.test.tsx +40 -18
  383. package/src/Item/Item.tsx +38 -2
  384. package/src/Layer/useLayer.tsx +8 -0
  385. package/src/List/List.test.tsx +9 -8
  386. package/src/MobileNav/MobileNav.tsx +12 -1
  387. package/src/MobileNav/MobileNavToggle.test.tsx +116 -0
  388. package/src/MobileNav/MobileNavToggle.tsx +9 -1
  389. package/src/MultiSelector/MultiSelector.doc.mjs +4 -2
  390. package/src/MultiSelector/MultiSelector.test.tsx +172 -3
  391. package/src/MultiSelector/MultiSelector.tsx +55 -12
  392. package/src/MultiSelector/hooks.ts +17 -0
  393. package/src/NumberInput/NumberInput.test.tsx +11 -7
  394. package/src/NumberInput/NumberInput.tsx +1 -2
  395. package/src/Outline/Outline.tsx +0 -1
  396. package/src/OverflowList/OverflowList.doc.mjs +39 -6
  397. package/src/OverflowList/OverflowList.test.tsx +111 -0
  398. package/src/OverflowList/OverflowList.tsx +48 -8
  399. package/src/Popover/Popover.tsx +7 -4
  400. package/src/Popover/usePopover.tsx +9 -13
  401. package/src/PowerSearch/PowerSearch.doc.mjs +14 -0
  402. package/src/PowerSearch/PowerSearch.test.tsx +20 -0
  403. package/src/PowerSearch/PowerSearchEditPopover.test.tsx +67 -0
  404. package/src/PowerSearch/PowerSearchEditPopover.tsx +1 -1
  405. package/src/PowerSearch/usePowerSearchSource.ts +1 -2
  406. package/src/SelectableCard/SelectableCard.doc.mjs +2 -0
  407. package/src/SelectableCard/SelectableCard.test.tsx +46 -4
  408. package/src/SelectableCard/SelectableCard.tsx +29 -16
  409. package/src/Selector/Selector.test.tsx +69 -0
  410. package/src/Selector/Selector.tsx +5 -1
  411. package/src/Selector/hooks.ts +17 -0
  412. package/src/SideNav/SideNav.test.tsx +119 -3
  413. package/src/SideNav/SideNavCollapseButton.tsx +6 -1
  414. package/src/SideNav/SideNavHeading.tsx +24 -8
  415. package/src/SideNav/SideNavItem.tsx +5 -1
  416. package/src/Slider/Slider.test.tsx +75 -2
  417. package/src/Slider/Slider.tsx +21 -5
  418. package/src/Switch/Switch.test.tsx +32 -1
  419. package/src/Table/BaseTable.tsx +3 -5
  420. package/src/Table/index.ts +5 -0
  421. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +6 -2
  422. package/src/Table/plugins/rowStatus/index.ts +7 -0
  423. package/src/Table/plugins/rowStatus/useTableRowStatus.test.tsx +138 -0
  424. package/src/Table/plugins/rowStatus/useTableRowStatus.tsx +189 -0
  425. package/src/Table/plugins/selection/useTableSelection.test.tsx +16 -0
  426. package/src/Table/plugins/selection/useTableSelection.tsx +19 -1
  427. package/src/Table/plugins/tree/useTableTreeData.test.tsx +135 -3
  428. package/src/Table/plugins/tree/useTableTreeData.tsx +128 -1
  429. package/src/Table/plugins/tree/useTableTreeState.test.tsx +104 -0
  430. package/src/Table/plugins/tree/useTableTreeState.tsx +46 -2
  431. package/src/Table/useBaseTablePlugins.ts +10 -6
  432. package/src/Table/useTableRowStatus.doc.mjs +30 -0
  433. package/src/Table/useTableSelection.doc.mjs +65 -14
  434. package/src/Table/useTableTreeData.doc.mjs +29 -0
  435. package/src/Table/useTableTreeState.doc.mjs +2 -2
  436. package/src/TextArea/TextArea.test.tsx +9 -1
  437. package/src/Thumbnail/Thumbnail.doc.mjs +19 -10
  438. package/src/Thumbnail/Thumbnail.test.tsx +98 -7
  439. package/src/Thumbnail/Thumbnail.tsx +141 -41
  440. package/src/Thumbnail/thumbnail.markers.stylex.ts +12 -0
  441. package/src/TimeInput/TimeInput.test.tsx +76 -1
  442. package/src/TimeInput/TimeInput.tsx +19 -6
  443. package/src/Timestamp/Timestamp.doc.mjs +4 -4
  444. package/src/Timestamp/Timestamp.test.tsx +32 -0
  445. package/src/Timestamp/Timestamp.tsx +28 -8
  446. package/src/Toast/useToast.tsx +7 -8
  447. package/src/ToggleButton/ToggleButtonGroup.tsx +1 -1
  448. package/src/Tokenizer/Tokenizer.doc.mjs +15 -1
  449. package/src/Tokenizer/Tokenizer.test.tsx +183 -2
  450. package/src/Tokenizer/Tokenizer.tsx +16 -4
  451. package/src/TopNav/TopNav.test.tsx +125 -1
  452. package/src/TopNav/TopNavHeading.tsx +24 -8
  453. package/src/TopNav/TopNavMegaMenu.test.tsx +149 -1
  454. package/src/TopNav/TopNavMegaMenu.tsx +10 -4
  455. package/src/TopNav/TopNavMegaMenuFeaturedCard.doc.mjs +3 -3
  456. package/src/TopNav/TopNavMegaMenuFeaturedCard.tsx +19 -2
  457. package/src/TopNav/TopNavMenu.test.tsx +118 -4
  458. package/src/TopNav/TopNavMenu.tsx +83 -5
  459. package/src/TreeList/TreeList.doc.mjs +22 -0
  460. package/src/TreeList/TreeList.test.tsx +232 -0
  461. package/src/TreeList/TreeList.tsx +18 -2
  462. package/src/TreeList/TreeListBranches.tsx +16 -2
  463. package/src/TreeList/TreeListItem.tsx +51 -11
  464. package/src/TreeList/TreeListTypes.ts +27 -1
  465. package/src/TreeList/index.ts +2 -2
  466. package/src/authoring/doc.ts +1 -3
  467. package/src/docs-types.ts +5 -0
  468. package/src/hooks/computeOverflow.test.ts +422 -0
  469. package/src/hooks/computeOverflow.ts +290 -0
  470. package/src/hooks/index.ts +2 -0
  471. package/src/hooks/useDevWarning.test.tsx +45 -0
  472. package/src/hooks/useDevWarning.ts +54 -0
  473. package/src/hooks/useOverflow.test.ts +96 -4
  474. package/src/hooks/useOverflow.ts +74 -33
  475. package/src/i18n/InternationalizationProvider.doc.mjs +99 -2
  476. package/src/i18n/resolve.ts +9 -21
  477. package/src/theme/Theme.tsx +11 -14
  478. package/src/theme/generateThemeRules.test.ts +65 -0
  479. package/src/theme/generateThemeRules.ts +56 -0
  480. package/src/theme/hct.ts +1 -1
  481. package/src/theme/syntax/defineSyntaxTheme.ts +5 -6
  482. package/src/utils/devWarning.test.ts +56 -0
  483. package/src/utils/devWarning.ts +102 -0
  484. package/src/utils/index.ts +6 -0
  485. package/src/utils/plainDate.test.ts +22 -0
  486. package/src/utils/plainDate.ts +61 -0
  487. package/src/utils/types.ts +14 -0
@@ -5,11 +5,14 @@
5
5
  /**
6
6
  * @file ChatContext.tsx
7
7
  * @input Uses React createContext
8
- * @output Exports ChatMessageContext for sharing sender/density between Chat components
9
- * @position Internal context; consumed by ChatMessage and ChatMessageBubble
8
+ * @output Exports Chat message/list/composer/layout contexts. The composer
9
+ * context (value + input-control registration) is public composition API,
10
+ * re-exported from Chat/index.ts for custom inputs.
11
+ * @position Shared contexts; consumed by ChatMessage, ChatComposer,
12
+ * ChatComposerInput, ChatSendButton, and custom inputs.
10
13
  */
11
14
 
12
- import {createContext, use} from 'react';
15
+ import {createContext, use, type RefObject} from 'react';
13
16
 
14
17
  export type ChatMessageSender = 'user' | 'assistant' | 'system';
15
18
  export type ChatDensity = 'compact' | 'balanced' | 'spacious';
@@ -19,8 +22,9 @@ export interface ChatMessageContextValue {
19
22
  density: ChatDensity;
20
23
  }
21
24
 
22
- export const ChatMessageContext =
23
- createContext<ChatMessageContextValue | null>(null);
25
+ export const ChatMessageContext = createContext<ChatMessageContextValue | null>(
26
+ null,
27
+ );
24
28
  ChatMessageContext.displayName = 'ChatMessageContext';
25
29
 
26
30
  export function useChatMessageContext(): ChatMessageContextValue | null {
@@ -31,9 +35,7 @@ export interface ChatListContextValue {
31
35
  density: ChatDensity;
32
36
  }
33
37
 
34
- export const ChatListContext = createContext<ChatListContextValue | null>(
35
- null,
36
- );
38
+ export const ChatListContext = createContext<ChatListContextValue | null>(null);
37
39
  ChatListContext.displayName = 'ChatListContext';
38
40
 
39
41
  export function useChatListContext(): ChatListContextValue | null {
@@ -44,6 +46,21 @@ export function useChatListContext(): ChatListContextValue | null {
44
46
  // Composer context — shared state between ChatComposer and ChatComposerInput
45
47
  // =============================================================================
46
48
 
49
+ /**
50
+ * Imperative surface the composer shell can invoke on its input slot.
51
+ *
52
+ * The input is one slot inside the composer body — it does not span the whole
53
+ * body — so shell-level interactions like "click empty space to focus the
54
+ * input" must flow shell → input. A custom input registers this control (via
55
+ * `ChatComposerContextValue.inputControlRef`) so the shell can drive it
56
+ * without knowing its DOM shape. Optional methods can be added over time;
57
+ * inputs implement only what they support.
58
+ */
59
+ export interface ChatComposerInputControl {
60
+ /** Move keyboard focus into the input. */
61
+ focus: () => void;
62
+ }
63
+
47
64
  export interface ChatComposerContextValue {
48
65
  value: string;
49
66
  onChange: (value: string) => void;
@@ -53,6 +70,15 @@ export interface ChatComposerContextValue {
53
70
  isStopShown: boolean;
54
71
  canSend: boolean;
55
72
  onStop?: () => void;
73
+ /**
74
+ * Mutable handle the input slot populates with its {@link
75
+ * ChatComposerInputControl}. The shell reads `.current` to drive the input
76
+ * (e.g. focus on body click). A custom input assigns
77
+ * `inputControlRef.current = { focus }` on mount and clears it on unmount.
78
+ * When unset, the shell falls back to focusing a `contenteditable`/`textarea`
79
+ * it finds in the body.
80
+ */
81
+ inputControlRef?: RefObject<ChatComposerInputControl | null>;
56
82
  }
57
83
 
58
84
  export const ChatComposerContext =
@@ -74,8 +100,9 @@ export interface ChatLayoutContextValue {
74
100
  contentRef: (el: HTMLElement | null) => void;
75
101
  }
76
102
 
77
- export const ChatLayoutContext =
78
- createContext<ChatLayoutContextValue | null>(null);
103
+ export const ChatLayoutContext = createContext<ChatLayoutContextValue | null>(
104
+ null,
105
+ );
79
106
  ChatLayoutContext.displayName = 'ChatLayoutContext';
80
107
 
81
108
  export function useChatLayoutContext(): ChatLayoutContextValue | null {
@@ -66,7 +66,10 @@ const styles = stylex.create({
66
66
  borderRadius: radiusVars['--radius-full'],
67
67
  transformOrigin: 'center',
68
68
  transitionProperty: 'transform, background-color',
69
- transitionDuration: '0.06s',
69
+ transitionDuration: {
70
+ default: '0.06s',
71
+ '@media (prefers-reduced-motion: reduce)': '0s',
72
+ },
70
73
  transitionTimingFunction: 'ease-out',
71
74
  },
72
75
  });
@@ -276,7 +276,9 @@ export function ChatLayout({
276
276
  const defaultScrollButton = (
277
277
  <ChatLayoutScrollButton
278
278
  isVisible={scroll.isScrolledUp || newMsgs.hasNewMessages}
279
- label={newMsgs.hasNewMessages ? t('@astryx.chatLayout.newMessages') : undefined}
279
+ label={
280
+ newMsgs.hasNewMessages ? t('@astryx.chatLayout.newMessages') : undefined
281
+ }
280
282
  onClick={() => {
281
283
  newMsgs.dismiss();
282
284
  scroll.scrollToBottom();
@@ -69,7 +69,10 @@ const styles = stylex.create({
69
69
  height: '32px',
70
70
  transitionProperty: 'opacity, transform, max-width',
71
71
  transitionTimingFunction: easeVars['--ease-standard'],
72
- transitionDuration: durationVars['--duration-fast-max'],
72
+ transitionDuration: {
73
+ default: durationVars['--duration-fast-max'],
74
+ '@media (prefers-reduced-motion: reduce)': '0s',
75
+ },
73
76
  },
74
77
  hidden: {
75
78
  opacity: 0,
@@ -135,7 +138,9 @@ export function ChatLayoutScrollButton({
135
138
  )}>
136
139
  <Button
137
140
  label={label ?? t('@astryx.chatLayoutScrollButton.scrollToBottom')}
138
- aria-label={label ?? t('@astryx.chatLayoutScrollButton.scrollToBottom')}
141
+ aria-label={
142
+ label ?? t('@astryx.chatLayoutScrollButton.scrollToBottom')
143
+ }
139
144
  icon={<Icon icon="chevronDown" size="md" />}
140
145
  variant="ghost"
141
146
  size="md"
@@ -122,8 +122,7 @@ export function ChatMessageMetadata({
122
122
  const sender = msgContext?.sender ?? 'assistant';
123
123
 
124
124
  const statusConfig = status != null ? STATUS_CONFIG[status] : null;
125
- const statusLabel =
126
- statusConfig != null ? t(statusConfig.i18nKey) : '';
125
+ const statusLabel = statusConfig != null ? t(statusConfig.i18nKey) : '';
127
126
 
128
127
  const hasContent =
129
128
  timestamp != null || footer != null || statusConfig != null;
@@ -88,7 +88,10 @@ export function ChatSendButton(props: ChatSendButtonProps): ReactNode {
88
88
  stopIcon,
89
89
  size = 'md',
90
90
  xstyle,
91
+ className,
92
+ style,
91
93
  ref,
94
+ ...rest
92
95
  } = props;
93
96
 
94
97
  const handleSend = onSend ?? (() => context?.onSubmit(''));
@@ -96,7 +99,11 @@ export function ChatSendButton(props: ChatSendButtonProps): ReactNode {
96
99
  return (
97
100
  <Button
98
101
  ref={ref}
99
- label={isStopShown ? t('@astryx.chatSendButton.stop') : t('@astryx.chatSendButton.send')}
102
+ label={
103
+ isStopShown
104
+ ? t('@astryx.chatSendButton.stop')
105
+ : t('@astryx.chatSendButton.send')
106
+ }
100
107
  variant={isStopShown ? 'secondary' : 'primary'}
101
108
  size={size}
102
109
  icon={
@@ -107,7 +114,10 @@ export function ChatSendButton(props: ChatSendButtonProps): ReactNode {
107
114
  isIconOnly
108
115
  isDisabled={!isStopShown && isDisabled}
109
116
  onClick={isStopShown ? onStop : handleSend}
117
+ {...rest}
110
118
  {...themeProps('chat-send-button')}
119
+ className={className}
120
+ style={style}
111
121
  xstyle={[styles.root, xstyle]}
112
122
  />
113
123
  );
@@ -133,6 +133,70 @@ describe('ChatToolCalls', () => {
133
133
  expect(panel).toHaveTextContent('file contents here');
134
134
  });
135
135
 
136
+ it('exposes the error message as text without requiring hover', () => {
137
+ render(
138
+ <ChatToolCalls
139
+ calls={[
140
+ {
141
+ name: 'bash',
142
+ status: 'error',
143
+ errorMessage: 'Command exited with code 1',
144
+ },
145
+ ]}
146
+ />,
147
+ );
148
+ // The message must exist as real (screen-reader-visible) text content,
149
+ // not only inside a hover-only title attribute.
150
+ expect(screen.getByText(/Command exited with code 1/)).toBeInTheDocument();
151
+ });
152
+
153
+ it('includes the error message in the accessible name of an expandable error row', () => {
154
+ render(
155
+ <ChatToolCalls
156
+ calls={[
157
+ {
158
+ name: 'bash',
159
+ status: 'error',
160
+ errorMessage: 'Command exited with code 1',
161
+ resultDetail: <div>stderr output</div>,
162
+ },
163
+ ]}
164
+ />,
165
+ );
166
+ expect(
167
+ screen.getByRole('button', {name: /Command exited with code 1/}),
168
+ ).toBeInTheDocument();
169
+ });
170
+
171
+ it('keeps the hover tooltip on the error status icon', () => {
172
+ const {container} = render(
173
+ <ChatToolCalls
174
+ calls={[
175
+ {
176
+ name: 'bash',
177
+ status: 'error',
178
+ errorMessage: 'Command exited with code 1',
179
+ },
180
+ ]}
181
+ />,
182
+ );
183
+ expect(
184
+ container.querySelector('[title="Command exited with code 1"]'),
185
+ ).not.toBeNull();
186
+ });
187
+
188
+ it('renders no error text for non-error calls', () => {
189
+ render(
190
+ <ChatToolCalls
191
+ calls={[
192
+ {name: 'bash', status: 'complete', errorMessage: 'stale message'},
193
+ ]}
194
+ />,
195
+ );
196
+ expect(screen.queryByText(/stale message/)).not.toBeInTheDocument();
197
+ expect(screen.queryByText(/Error:/)).not.toBeInTheDocument();
198
+ });
199
+
136
200
  it('points the group header aria-controls at the content region', () => {
137
201
  render(
138
202
  <ChatToolCalls
@@ -35,6 +35,7 @@ import {getKey, mergeProps} from '../utils';
35
35
  import {Badge} from '../Badge';
36
36
  import {Icon, type IconName} from '../Icon';
37
37
  import {Spinner} from '../Spinner';
38
+ import {VisuallyHidden} from '../VisuallyHidden';
38
39
  import {themeProps} from '../utils/themeProps';
39
40
 
40
41
  // =============================================================================
@@ -60,7 +61,11 @@ export interface ChatToolCallItem {
60
61
  deletions?: number;
61
62
  /** Additional info rendered after the label. Free-form ReactNode. */
62
63
  stats?: ReactNode;
63
- /** Error message when status is 'error'. Shown in a tooltip on the status icon. */
64
+ /**
65
+ * Error message when status is 'error'. Rendered as visually hidden text in
66
+ * the row (so screen readers and keyboard users perceive it) and echoed in a
67
+ * hover tooltip on the status icon.
68
+ */
64
69
  errorMessage?: string;
65
70
  /** Unique key for React list rendering. Derived from stable metadata if omitted. */
66
71
  key?: string;
@@ -128,7 +133,10 @@ const styles = stylex.create({
128
133
  width: '14px',
129
134
  height: '14px',
130
135
  color: colorVars['--color-text-disabled'],
131
- transition: `transform ${durationVars['--duration-fast']} ${easeVars['--ease-standard']}`,
136
+ transition: {
137
+ default: `transform ${durationVars['--duration-fast']} ${easeVars['--ease-standard']}`,
138
+ '@media (prefers-reduced-motion: reduce)': 'none',
139
+ },
132
140
  },
133
141
  chevronExpanded: {
134
142
  transform: 'rotate(180deg)',
@@ -136,7 +144,10 @@ const styles = stylex.create({
136
144
  groupContent: {
137
145
  display: 'grid',
138
146
  gridTemplateRows: '0fr',
139
- transition: `grid-template-rows ${durationVars['--duration-medium']} ${easeVars['--ease-standard']}`,
147
+ transition: {
148
+ default: `grid-template-rows ${durationVars['--duration-medium']} ${easeVars['--ease-standard']}`,
149
+ '@media (prefers-reduced-motion: reduce)': 'none',
150
+ },
140
151
  },
141
152
  groupContentExpanded: {
142
153
  gridTemplateRows: '1fr',
@@ -261,7 +272,10 @@ const styles = stylex.create({
261
272
  width: '14px',
262
273
  height: '14px',
263
274
  color: colorVars['--color-text-disabled'],
264
- transition: `transform ${durationVars['--duration-fast']} ${easeVars['--ease-standard']}`,
275
+ transition: {
276
+ default: `transform ${durationVars['--duration-fast']} ${easeVars['--ease-standard']}`,
277
+ '@media (prefers-reduced-motion: reduce)': 'none',
278
+ },
265
279
  marginInlineStart: 'auto',
266
280
  },
267
281
  callDetailContent: {
@@ -395,6 +409,13 @@ function CallRow({call}: {call: ChatToolCallItem}) {
395
409
  </span>
396
410
  </>
397
411
  )}
412
+ {status === 'error' && call.errorMessage != null && (
413
+ // The title attribute above is hover-only; expose the error detail
414
+ // as real text so it reaches screen readers, keyboard, and touch
415
+ // users. Rendering it inside the row also folds it into the
416
+ // accessible name of expandable (role="button") rows.
417
+ <VisuallyHidden>{`Error: ${call.errorMessage}`}</VisuallyHidden>
418
+ )}
398
419
  </span>
399
420
  <span {...stylex.props(styles.callName)}>{call.name}</span>
400
421
  {call.node != null && (
package/src/Chat/index.ts CHANGED
@@ -81,7 +81,11 @@ export type {
81
81
  TokenPortal,
82
82
  } from './useChatComposerTokens';
83
83
  export type {ChatMessageSender, ChatDensity} from './ChatContext';
84
- export {useChatLayoutContext} from './ChatContext';
84
+ export {useChatLayoutContext, useChatComposerContext} from './ChatContext';
85
+ export type {
86
+ ChatComposerContextValue,
87
+ ChatComposerInputControl,
88
+ } from './ChatContext';
85
89
 
86
90
  export {ChatToolCalls} from './ChatToolCalls';
87
91
  export type {
@@ -45,6 +45,23 @@ afterEach(() => {
45
45
  vi.unstubAllGlobals();
46
46
  });
47
47
 
48
+ // The global test setup polyfills matchMedia with a never-matching stub;
49
+ // this override makes only the reduced-motion query match, mirroring the
50
+ // pattern in useStreamingText.test.ts. Undone by afterEach's
51
+ // unstubAllGlobals.
52
+ function stubReducedMotion() {
53
+ vi.stubGlobal('matchMedia', (query: string) => ({
54
+ matches: query.includes('prefers-reduced-motion'),
55
+ media: query,
56
+ onchange: null,
57
+ addListener: () => {},
58
+ removeListener: () => {},
59
+ addEventListener: () => {},
60
+ removeEventListener: () => {},
61
+ dispatchEvent: () => false,
62
+ }));
63
+ }
64
+
48
65
  function setGeometry(
49
66
  el: HTMLElement,
50
67
  {scrollHeight, clientHeight}: {scrollHeight: number; clientHeight: number},
@@ -166,3 +183,35 @@ describe("useChatStreamScroll — scrollToBottom({behavior: 'instant'})", () =>
166
183
  expect(rafQueue.length).toBeGreaterThan(0);
167
184
  });
168
185
  });
186
+
187
+ describe('useChatStreamScroll — prefers-reduced-motion', () => {
188
+ it('streaming growth jumps synchronously instead of springing', () => {
189
+ stubReducedMotion();
190
+ const {api, el} = renderHook();
191
+ setGeometry(el, {scrollHeight: 1000, clientHeight: 400});
192
+ flushRaf(); // consume the mount jump
193
+ expect(el.scrollTop).toBe(600);
194
+
195
+ // Post-first-fill growth would normally take the spring path; under
196
+ // reduced motion it must land in the same synchronous step.
197
+ setGeometry(el, {scrollHeight: 1400, clientHeight: 400});
198
+ rafQueue = [];
199
+ act(() => api.current!.scrollIfLocked());
200
+ expect(el.scrollTop).toBe(1000);
201
+ expect(rafQueue).toHaveLength(0);
202
+ });
203
+
204
+ it('default scrollToBottom falls back to an instant jump', () => {
205
+ stubReducedMotion();
206
+ const {api, el} = renderHook();
207
+ setGeometry(el, {scrollHeight: 1000, clientHeight: 400});
208
+ flushRaf();
209
+ el.scrollTop = 100; // user scrolled up
210
+
211
+ rafQueue = [];
212
+ act(() => api.current!.scrollToBottom());
213
+ expect(el.scrollTop).toBe(600);
214
+ expect(rafQueue).toHaveLength(0);
215
+ expect(api.current!.isLocked).toBe(true);
216
+ });
217
+ });
@@ -4,7 +4,7 @@
4
4
 
5
5
  /**
6
6
  * @file useChatStreamScroll.ts
7
- * @input Uses React refs, state, callbacks
7
+ * @input Uses React refs, state, callbacks, useMediaQuery
8
8
  * @output Exports useChatStreamScroll hook for AI chat scroll behavior
9
9
  * @position Utility hook — used by ChatLayout, also usable standalone
10
10
  *
@@ -15,6 +15,8 @@
15
15
  * - The first fill positions instantly (whether content is present at
16
16
  * mount or arrives async); only subsequent growth springs
17
17
  * - `scrollToBottom({behavior: 'instant'})` jumps in one frame, no animation
18
+ * - Under `prefers-reduced-motion`, every spring path falls back to the
19
+ * same instant jump — following still works, it just doesn't animate
18
20
  *
19
21
  * Uses scroll direction (lastScrollTop comparison) to detect user
20
22
  * intent — works for wheel, touch, scrollbar drag, keyboard, everything.
@@ -26,6 +28,7 @@
26
28
  */
27
29
 
28
30
  import {useCallback, useEffect, useRef, useState} from 'react';
31
+ import {useMediaQuery} from '../hooks/useMediaQuery';
29
32
 
30
33
  // =============================================================================
31
34
  // Types
@@ -37,7 +40,8 @@ export interface ChatScrollToBottomOptions {
37
40
  * the spring animation. Use for programmatic positioning (opening a
38
41
  * conversation, restoring a session) — keep the default `'spring'` for
39
42
  * user-initiated scrolls like the scroll-to-bottom button. Mirrors the
40
- * DOM's `scrollTo({behavior})`.
43
+ * DOM's `scrollTo({behavior})`. When the user prefers reduced motion,
44
+ * `'spring'` also jumps instantly.
41
45
  * @default 'spring'
42
46
  */
43
47
  behavior?: 'instant' | 'spring';
@@ -189,14 +193,6 @@ export function useChatStreamScroll({
189
193
  requestAnimationFrame(animate);
190
194
  }, [scrollRef, damping, stiffness, mass]);
191
195
 
192
- const startAnimation = useCallback(() => {
193
- if (!animatingRef.current && lockedRef.current) {
194
- animatingRef.current = true;
195
- lastTickRef.current = undefined;
196
- requestAnimationFrame(animate);
197
- }
198
- }, [animate]);
199
-
200
196
  // Jump to the bottom in a single frame — cancels any in-flight spring so
201
197
  // a later animation tick can't fight the assignment.
202
198
  const jumpToBottom = useCallback(() => {
@@ -211,6 +207,27 @@ export function useChatStreamScroll({
211
207
  lastScrollTopRef.current = el.scrollTop;
212
208
  }, [scrollRef]);
213
209
 
210
+ const prefersReducedMotion = useMediaQuery(
211
+ '(prefers-reduced-motion: reduce)',
212
+ );
213
+
214
+ // Every spring entry point (scrollToBottom, lock, scrollIfLocked growth
215
+ // follow) funnels through here, so this one branch covers them all.
216
+ const startAnimation = useCallback(() => {
217
+ if (!lockedRef.current) {
218
+ return;
219
+ }
220
+ if (prefersReducedMotion) {
221
+ jumpToBottom();
222
+ return;
223
+ }
224
+ if (!animatingRef.current) {
225
+ animatingRef.current = true;
226
+ lastTickRef.current = undefined;
227
+ requestAnimationFrame(animate);
228
+ }
229
+ }, [animate, jumpToBottom, prefersReducedMotion]);
230
+
214
231
  // --- Public API ---
215
232
 
216
233
  const scrollToBottom = useCallback(
@@ -242,7 +242,7 @@ async function createVolumeAnalyser(
242
242
 
243
243
  let _sharedAudioCtx: AudioContext | null = null;
244
244
 
245
- export function getDefaultAudioContext(): AudioContext {
245
+ function getDefaultAudioContext(): AudioContext {
246
246
  if (!_sharedAudioCtx || _sharedAudioCtx.state === 'closed') {
247
247
  _sharedAudioCtx = new AudioContext();
248
248
  }
@@ -699,7 +699,9 @@ export function useTriggerMenu(
699
699
  <div
700
700
  id={listboxId}
701
701
  role="listbox"
702
- aria-label={trigger?.menuLabel ?? t('@astryx.chatTriggerMenu.suggestions')}
702
+ aria-label={
703
+ trigger?.menuLabel ?? t('@astryx.chatTriggerMenu.suggestions')
704
+ }
703
705
  {...mergeProps(
704
706
  themeProps('trigger-menu'),
705
707
  stylex.props(styles.dropdown),
@@ -9,10 +9,15 @@
9
9
  * SYNC: When CheckboxInput.tsx changes, update tests to match new behavior
10
10
  */
11
11
 
12
- import {describe, it, expect, vi, beforeEach} from 'vitest';
12
+ import {describe, it, expect, vi, beforeEach, afterEach} from 'vitest';
13
13
  import {render, screen, fireEvent, waitFor} from '@testing-library/react';
14
14
  import userEvent from '@testing-library/user-event';
15
15
  import {CheckboxInput} from './CheckboxInput';
16
+ import {__resetLiveRegionsForTest} from '../hooks/useAnnounce';
17
+
18
+ afterEach(() => {
19
+ __resetLiveRegionsForTest();
20
+ });
16
21
 
17
22
  // Mock showPopover/hidePopover (not implemented in jsdom) so the tooltip layer
18
23
  // reflects its open state via a `popover-open` attribute the tests can assert.
@@ -333,6 +338,32 @@ describe('CheckboxInput', () => {
333
338
  );
334
339
  });
335
340
 
341
+ // Regression: the status is conditionally mounted, so it must be announced
342
+ // through the persistent useAnnounce live region — a live region born
343
+ // together with its content is not reliably announced.
344
+ it('announces a status message that appears after mount', async () => {
345
+ const {rerender} = render(
346
+ <CheckboxInput label="Accept terms" value={false} onChange={() => {}} />,
347
+ );
348
+ expect(
349
+ document.querySelector('[data-astryx-live-region="assertive"]'),
350
+ ).toBeNull();
351
+
352
+ rerender(
353
+ <CheckboxInput
354
+ label="Accept terms"
355
+ value={false}
356
+ onChange={() => {}}
357
+ status={{type: 'error', message: 'Required field'}}
358
+ />,
359
+ );
360
+ await waitFor(() => {
361
+ expect(
362
+ document.querySelector('[data-astryx-live-region="assertive"]'),
363
+ ).toHaveTextContent('Required field');
364
+ });
365
+ });
366
+
336
367
  describe('disabledMessage', () => {
337
368
  const h = {hidden: true} as const;
338
369
 
@@ -166,7 +166,7 @@ const styles = stylex.create({
166
166
  indeterminateMark: {
167
167
  display: 'none',
168
168
  backgroundColor: colorVars['--color-on-accent'],
169
- borderRadius: 1,
169
+ borderRadius: radiusVars['--radius-full'],
170
170
  },
171
171
  indeterminateMarkVisible: {
172
172
  display: 'block',
@@ -9,7 +9,7 @@
9
9
  * SYNC: When CheckboxList.tsx or CheckboxListItem.tsx changes, update tests to match new behavior
10
10
  */
11
11
 
12
- import {describe, it, expect, vi, beforeEach} from 'vitest';
12
+ import {describe, it, expect, vi, beforeEach, afterEach} from 'vitest';
13
13
  import {
14
14
  render,
15
15
  screen,
@@ -20,8 +20,16 @@ import {
20
20
  import userEvent from '@testing-library/user-event';
21
21
  import {CheckboxList} from './CheckboxList';
22
22
  import {CheckboxListItem} from './CheckboxListItem';
23
+ import {__resetLiveRegionsForTest} from '../hooks/useAnnounce';
23
24
  import {List} from '../List/List';
24
25
 
26
+ // FieldStatus announces status messages through the persistent useAnnounce
27
+ // singletons; remove them between tests so role/aria-live queries in this
28
+ // file never match a leftover region.
29
+ afterEach(() => {
30
+ __resetLiveRegionsForTest();
31
+ });
32
+
25
33
  // Mock showPopover/hidePopover (not implemented in jsdom) so the tooltip layer
26
34
  // reflects its open state via a `popover-open` attribute the tests can assert.
27
35
  beforeEach(() => {
@@ -437,6 +445,71 @@ describe('CheckboxListItem standalone mode', () => {
437
445
  });
438
446
  });
439
447
 
448
+ describe('CheckboxListItem accessible name', () => {
449
+ it('names the checkbox from a string label', () => {
450
+ render(
451
+ <CheckboxList label="Preferences" value={[]} onChange={() => {}}>
452
+ <CheckboxListItem label="Option A" value="a" />
453
+ </CheckboxList>,
454
+ );
455
+ expect(
456
+ screen.getByRole('checkbox', {name: 'Option A'}),
457
+ ).toBeInTheDocument();
458
+ });
459
+
460
+ it('names the checkbox from aria-label when the label is a ReactNode', () => {
461
+ const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
462
+ render(
463
+ <CheckboxList label="Plans" value={[]} onChange={() => {}}>
464
+ <CheckboxListItem
465
+ label={
466
+ <span>
467
+ Pro plan <em>(recommended)</em>
468
+ </span>
469
+ }
470
+ aria-label="Pro plan"
471
+ value="pro"
472
+ />
473
+ </CheckboxList>,
474
+ );
475
+ expect(
476
+ screen.getByRole('checkbox', {name: 'Pro plan'}),
477
+ ).toBeInTheDocument();
478
+ // A named checkbox needs no dev guidance.
479
+ expect(warnSpy).not.toHaveBeenCalled();
480
+ warnSpy.mockRestore();
481
+ });
482
+
483
+ it('warns once when a ReactNode label has no aria-label', () => {
484
+ const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
485
+ const richLabel = (
486
+ <span>
487
+ Pro plan <em>(recommended)</em>
488
+ </span>
489
+ );
490
+ const {rerender} = render(
491
+ <CheckboxList label="Plans" value={[]} onChange={() => {}}>
492
+ <CheckboxListItem label={richLabel} value="pro" />
493
+ </CheckboxList>,
494
+ );
495
+ // Falls back to the generic name, and tells the developer how to fix it.
496
+ expect(
497
+ screen.getByRole('checkbox', {name: 'Checkbox'}),
498
+ ).toBeInTheDocument();
499
+ expect(warnSpy).toHaveBeenCalledTimes(1);
500
+ expect(String(warnSpy.mock.calls[0]?.[0])).toContain('aria-label');
501
+
502
+ // Warn once per item instance — re-renders don't repeat it.
503
+ rerender(
504
+ <CheckboxList label="Plans" value={['pro']} onChange={() => {}}>
505
+ <CheckboxListItem label={richLabel} value="pro" />
506
+ </CheckboxList>,
507
+ );
508
+ expect(warnSpy).toHaveBeenCalledTimes(1);
509
+ warnSpy.mockRestore();
510
+ });
511
+ });
512
+
440
513
  describe('CheckboxListItem ARIA props', () => {
441
514
  it('conveys checked state via the inner checkbox, not aria-checked on the listitem', () => {
442
515
  render(
@@ -567,7 +640,7 @@ describe('CheckboxListItem ARIA props', () => {
567
640
  expect(changeAction).toHaveBeenCalledWith(['a']);
568
641
  });
569
642
 
570
- it('forwards arbitrary aria attributes to the list item DOM element', () => {
643
+ it('forwards arbitrary aria attributes to the list item, but aria-label names the checkbox', () => {
571
644
  render(
572
645
  <List>
573
646
  <CheckboxListItem
@@ -578,8 +651,13 @@ describe('CheckboxListItem ARIA props', () => {
578
651
  </List>,
579
652
  );
580
653
  const item = screen.getByRole('listitem');
654
+ // Arbitrary aria-* still forward to the row...
581
655
  expect(item).toHaveAttribute('aria-describedby', 'help-text');
582
- expect(item).toHaveAttribute('aria-label', 'custom label');
656
+ // ...but aria-label names the checkbox control, not the row.
657
+ expect(item).not.toHaveAttribute('aria-label');
658
+ expect(
659
+ screen.getByRole('checkbox', {name: 'custom label'}),
660
+ ).toBeInTheDocument();
583
661
  });
584
662
 
585
663
  describe('disabledMessage', () => {