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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (558) hide show
  1. package/CHANGELOG.md +81 -0
  2. package/dist/BottomSheet/BottomSheet.d.ts +1 -1
  3. package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
  4. package/dist/BottomSheet/BottomSheet.js +3 -1
  5. package/dist/BottomSheet/BottomSheetPanel.d.ts +7 -5
  6. package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
  7. package/dist/BottomSheet/BottomSheetPanel.js +56 -19
  8. package/dist/Button/Button.d.ts.map +1 -1
  9. package/dist/Button/Button.js +13 -28
  10. package/dist/Chat/ChatComposer.d.ts.map +1 -1
  11. package/dist/Chat/ChatComposer.js +10 -5
  12. package/dist/Chat/ChatComposerDrawer.d.ts +19 -5
  13. package/dist/Chat/ChatComposerDrawer.d.ts.map +1 -1
  14. package/dist/Chat/ChatComposerDrawer.js +48 -23
  15. package/dist/Chat/ChatComposerInput.d.ts +11 -3
  16. package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
  17. package/dist/Chat/ChatComposerInput.js +63 -16
  18. package/dist/Chat/ChatDictationButton.d.ts +3 -2
  19. package/dist/Chat/ChatDictationButton.d.ts.map +1 -1
  20. package/dist/Chat/ChatDictationButton.js +6 -4
  21. package/dist/Chat/ChatLayout.d.ts +19 -0
  22. package/dist/Chat/ChatLayout.d.ts.map +1 -1
  23. package/dist/Chat/ChatLayout.js +19 -0
  24. package/dist/Chat/ChatLayoutScrollButton.d.ts.map +1 -1
  25. package/dist/Chat/ChatLayoutScrollButton.js +44 -34
  26. package/dist/Chat/ChatMessageBubble.d.ts +2 -0
  27. package/dist/Chat/ChatMessageBubble.d.ts.map +1 -1
  28. package/dist/Chat/ChatMessageBubble.js +5 -3
  29. package/dist/Chat/ChatMessageList.d.ts +2 -1
  30. package/dist/Chat/ChatMessageList.d.ts.map +1 -1
  31. package/dist/Chat/ChatMessageList.js +4 -3
  32. package/dist/Chat/ChatMessageMetadata.d.ts +5 -4
  33. package/dist/Chat/ChatMessageMetadata.d.ts.map +1 -1
  34. package/dist/Chat/ChatMessageMetadata.js +12 -9
  35. package/dist/Chat/ChatSendButton.d.ts +2 -1
  36. package/dist/Chat/ChatSendButton.d.ts.map +1 -1
  37. package/dist/Chat/ChatSendButton.js +6 -4
  38. package/dist/Chat/index.d.ts +1 -1
  39. package/dist/Chat/index.d.ts.map +1 -1
  40. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  41. package/dist/CheckboxInput/CheckboxInput.js +12 -2
  42. package/dist/Citation/Citation.d.ts +1 -1
  43. package/dist/Citation/Citation.d.ts.map +1 -1
  44. package/dist/Citation/Citation.js +3 -2
  45. package/dist/CodeBlock/highlightRanges.d.ts.map +1 -1
  46. package/dist/CodeBlock/highlightRanges.js +14 -3
  47. package/dist/Collapsible/Collapsible.d.ts.map +1 -1
  48. package/dist/Collapsible/Collapsible.js +6 -1
  49. package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
  50. package/dist/CommandPalette/CommandPaletteInput.js +1 -1
  51. package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
  52. package/dist/ComplexSelector/ComplexSelector.js +1 -1
  53. package/dist/DateInput/DateInput.d.ts.map +1 -1
  54. package/dist/DateInput/DateInput.js +4 -4
  55. package/dist/DateInput/NativeDateField.d.ts.map +1 -1
  56. package/dist/DateInput/NativeDateField.js +16 -16
  57. package/dist/DateInput/TouchDateField.d.ts.map +1 -1
  58. package/dist/DateInput/TouchDateField.js +2 -2
  59. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  60. package/dist/DateRangeInput/DateRangeInput.js +4 -4
  61. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  62. package/dist/DateTimeInput/DateTimeInput.js +8 -8
  63. package/dist/DateTimeInput/TouchDateTimeField.d.ts.map +1 -1
  64. package/dist/DateTimeInput/TouchDateTimeField.js +4 -4
  65. package/dist/DateTimeInput/nativePickerSegmentStyles.d.ts +9 -3
  66. package/dist/DateTimeInput/nativePickerSegmentStyles.d.ts.map +1 -1
  67. package/dist/DateTimeInput/nativePickerSegmentStyles.js +3 -3
  68. package/dist/Dialog/DialogHeader.d.ts +8 -1
  69. package/dist/Dialog/DialogHeader.d.ts.map +1 -1
  70. package/dist/Dialog/DialogHeader.js +13 -4
  71. package/dist/Field/InputClearButton.d.ts +4 -2
  72. package/dist/Field/InputClearButton.d.ts.map +1 -1
  73. package/dist/Field/InputClearButton.js +4 -2
  74. package/dist/Field/PanelSearchInput.d.ts.map +1 -1
  75. package/dist/Field/PanelSearchInput.js +1 -1
  76. package/dist/FileInput/FileInput.d.ts.map +1 -1
  77. package/dist/FileInput/FileInput.js +7 -7
  78. package/dist/Icon/Icon.d.ts +6 -31
  79. package/dist/Icon/Icon.d.ts.map +1 -1
  80. package/dist/Icon/Icon.js +8 -71
  81. package/dist/Icon/IconDefaultSizeContext.d.ts +4 -0
  82. package/dist/Icon/IconDefaultSizeContext.d.ts.map +1 -0
  83. package/dist/Icon/IconDefaultSizeContext.js +18 -0
  84. package/dist/Icon/IconSize.stylex.d.ts +65 -0
  85. package/dist/Icon/IconSize.stylex.d.ts.map +1 -0
  86. package/dist/Icon/IconSize.stylex.js +75 -0
  87. package/dist/Item/Item.d.ts +1 -1
  88. package/dist/Item/Item.d.ts.map +1 -1
  89. package/dist/Item/Item.js +4 -3
  90. package/dist/Kbd/Kbd.d.ts +5 -3
  91. package/dist/Kbd/Kbd.d.ts.map +1 -1
  92. package/dist/Kbd/Kbd.js +36 -42
  93. package/dist/Link/Link.d.ts.map +1 -1
  94. package/dist/Link/Link.js +6 -2
  95. package/dist/Link/useLinkComponent.d.ts +5 -15
  96. package/dist/Link/useLinkComponent.d.ts.map +1 -1
  97. package/dist/Link/useLinkComponent.js +34 -51
  98. package/dist/List/List.d.ts +19 -1
  99. package/dist/List/List.d.ts.map +1 -1
  100. package/dist/List/List.js +9 -2
  101. package/dist/List/ListContext.d.ts +1 -0
  102. package/dist/List/ListContext.d.ts.map +1 -1
  103. package/dist/List/ListContext.js +2 -1
  104. package/dist/List/ListItem.d.ts +1 -1
  105. package/dist/List/ListItem.d.ts.map +1 -1
  106. package/dist/List/ListItem.js +9 -2
  107. package/dist/Markdown/Markdown.d.ts +8 -2
  108. package/dist/Markdown/Markdown.d.ts.map +1 -1
  109. package/dist/Markdown/Markdown.fr23.bench.d.ts +2 -0
  110. package/dist/Markdown/Markdown.fr23.bench.d.ts.map +1 -0
  111. package/dist/Markdown/Markdown.fr23.bench.js +150 -0
  112. package/dist/Markdown/Markdown.fr23.sampling.d.ts +2 -0
  113. package/dist/Markdown/Markdown.fr23.sampling.d.ts.map +1 -0
  114. package/dist/Markdown/Markdown.fr23.sampling.js +43 -0
  115. package/dist/Markdown/Markdown.js +262 -109
  116. package/dist/Markdown/ast.d.ts +162 -0
  117. package/dist/Markdown/ast.d.ts.map +1 -0
  118. package/dist/Markdown/ast.js +74 -0
  119. package/dist/Markdown/index.d.ts +5 -3
  120. package/dist/Markdown/index.d.ts.map +1 -1
  121. package/dist/Markdown/index.js +3 -2
  122. package/dist/Markdown/parser/index.d.ts +10 -0
  123. package/dist/Markdown/parser/index.d.ts.map +1 -0
  124. package/dist/Markdown/parser/index.js +10 -0
  125. package/dist/Markdown/parser.d.ts +126 -58
  126. package/dist/Markdown/parser.d.ts.map +1 -1
  127. package/dist/Markdown/parser.js +726 -177
  128. package/dist/Markdown/plugins/frontmatter.d.ts +32 -0
  129. package/dist/Markdown/plugins/frontmatter.d.ts.map +1 -0
  130. package/dist/Markdown/plugins/frontmatter.js +132 -0
  131. package/dist/Markdown/plugins/index.d.ts +17 -0
  132. package/dist/Markdown/plugins/index.d.ts.map +1 -0
  133. package/dist/Markdown/plugins/index.js +14 -0
  134. package/dist/Markdown/plugins/protocol.d.ts +191 -0
  135. package/dist/Markdown/plugins/protocol.d.ts.map +1 -0
  136. package/dist/Markdown/plugins/protocol.js +957 -0
  137. package/dist/Markdown/plugins/semanticFence.d.ts +36 -0
  138. package/dist/Markdown/plugins/semanticFence.d.ts.map +1 -0
  139. package/dist/Markdown/plugins/semanticFence.js +213 -0
  140. package/dist/Markdown/plugins/softBreaks.d.ts +3 -0
  141. package/dist/Markdown/plugins/softBreaks.d.ts.map +1 -0
  142. package/dist/Markdown/plugins/softBreaks.js +172 -0
  143. package/dist/Markdown/plugins/sourceDecoration.d.ts +76 -0
  144. package/dist/Markdown/plugins/sourceDecoration.d.ts.map +1 -0
  145. package/dist/Markdown/plugins/sourceDecoration.js +393 -0
  146. package/dist/Markdown/plugins/textTransform.d.ts +30 -0
  147. package/dist/Markdown/plugins/textTransform.d.ts.map +1 -0
  148. package/dist/Markdown/plugins/textTransform.js +374 -0
  149. package/dist/Markdown/remark.d.ts +196 -0
  150. package/dist/Markdown/remark.d.ts.map +1 -0
  151. package/dist/Markdown/remark.js +1062 -0
  152. package/dist/Markdown/url.d.ts +6 -0
  153. package/dist/Markdown/url.d.ts.map +1 -0
  154. package/dist/Markdown/url.js +31 -0
  155. package/dist/Markdown/utils.d.ts +4 -2
  156. package/dist/Markdown/utils.d.ts.map +1 -1
  157. package/dist/Markdown/utils.js +2 -1
  158. package/dist/MetadataList/MetadataList.js +3 -3
  159. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  160. package/dist/NumberInput/NumberInput.js +4 -4
  161. package/dist/Outline/index.d.ts +1 -0
  162. package/dist/Outline/index.d.ts.map +1 -1
  163. package/dist/Outline/parseOutlineFromMarkdown.d.ts +7 -1
  164. package/dist/Outline/parseOutlineFromMarkdown.d.ts.map +1 -1
  165. package/dist/Outline/parseOutlineFromMarkdown.js +14 -8
  166. package/dist/Outline/useOutlineFromMarkdown.d.ts +3 -1
  167. package/dist/Outline/useOutlineFromMarkdown.d.ts.map +1 -1
  168. package/dist/Outline/useOutlineFromMarkdown.js +8 -3
  169. package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
  170. package/dist/PowerSearch/PowerSearchEditPopover.js +46 -30
  171. package/dist/PowerSearch/PowerSearchValueEditor.d.ts.map +1 -1
  172. package/dist/PowerSearch/PowerSearchValueEditor.js +5 -0
  173. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  174. package/dist/ProgressBar/ProgressBar.js +36 -31
  175. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  176. package/dist/RadioList/RadioListItem.js +13 -1
  177. package/dist/ScrollableArea/ScrollableArea.d.ts +1 -0
  178. package/dist/ScrollableArea/ScrollableArea.d.ts.map +1 -1
  179. package/dist/ScrollableArea/ScrollableArea.js +1 -0
  180. package/dist/SegmentedControl/SegmentedControl.d.ts +1 -1
  181. package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
  182. package/dist/SegmentedControl/SegmentedControl.js +1 -0
  183. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  184. package/dist/SegmentedControl/SegmentedControlItem.js +5 -5
  185. package/dist/Selector/Selector.d.ts +9 -7
  186. package/dist/Selector/Selector.d.ts.map +1 -1
  187. package/dist/Selector/Selector.js +16 -7
  188. package/dist/SideNav/SideNav.d.ts +2 -1
  189. package/dist/SideNav/SideNav.d.ts.map +1 -1
  190. package/dist/SideNav/SideNav.js +9 -3
  191. package/dist/Slider/Slider.d.ts.map +1 -1
  192. package/dist/Slider/Slider.js +28 -12
  193. package/dist/Spinner/Spinner.d.ts.map +1 -1
  194. package/dist/Spinner/Spinner.js +23 -7
  195. package/dist/Switch/Switch.d.ts.map +1 -1
  196. package/dist/Switch/Switch.js +11 -0
  197. package/dist/TabList/Tab.d.ts +1 -1
  198. package/dist/TabList/Tab.d.ts.map +1 -1
  199. package/dist/TabList/Tab.js +20 -7
  200. package/dist/Table/Table.d.ts +1 -0
  201. package/dist/Table/Table.d.ts.map +1 -1
  202. package/dist/Table/Table.js +29 -13
  203. package/dist/TextArea/TextArea.d.ts.map +1 -1
  204. package/dist/TextArea/TextArea.js +1 -1
  205. package/dist/TextInput/TextInput.d.ts.map +1 -1
  206. package/dist/TextInput/TextInput.js +2 -2
  207. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  208. package/dist/TimeInput/TimeInput.js +4 -4
  209. package/dist/Timer/Timer.d.ts +49 -0
  210. package/dist/Timer/Timer.d.ts.map +1 -0
  211. package/dist/Timer/Timer.js +158 -0
  212. package/dist/Timer/index.d.ts +9 -0
  213. package/dist/Timer/index.d.ts.map +1 -0
  214. package/dist/Timer/index.js +10 -0
  215. package/dist/ToggleButton/ToggleButton.d.ts +9 -6
  216. package/dist/ToggleButton/ToggleButton.d.ts.map +1 -1
  217. package/dist/ToggleButton/ToggleButton.js +15 -27
  218. package/dist/TopNav/TopNavMenu.d.ts +1 -1
  219. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  220. package/dist/TopNav/TopNavMenu.js +3 -2
  221. package/dist/TreeList/TreeList.d.ts.map +1 -1
  222. package/dist/TreeList/TreeList.js +3 -0
  223. package/dist/TreeList/TreeListItem.d.ts +6 -1
  224. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  225. package/dist/TreeList/TreeListItem.js +12 -2
  226. package/dist/TreeList/TreeListTypes.d.ts +8 -1
  227. package/dist/TreeList/TreeListTypes.d.ts.map +1 -1
  228. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  229. package/dist/Typeahead/BaseTypeahead.js +16 -7
  230. package/dist/astryx.css +27 -11
  231. package/dist/hooks/scrollKeyboardDelegation.d.ts +3 -0
  232. package/dist/hooks/scrollKeyboardDelegation.d.ts.map +1 -0
  233. package/dist/hooks/scrollKeyboardDelegation.js +146 -0
  234. package/dist/hooks/useClickableContainer.d.ts +10 -1
  235. package/dist/hooks/useClickableContainer.d.ts.map +1 -1
  236. package/dist/hooks/useClickableContainer.js +15 -4
  237. package/dist/hooks/useScrollableArea.d.ts +6 -2
  238. package/dist/hooks/useScrollableArea.d.ts.map +1 -1
  239. package/dist/hooks/useScrollableArea.js +17 -5
  240. package/dist/index.d.ts +1 -0
  241. package/dist/index.d.ts.map +1 -1
  242. package/dist/index.js +3 -0
  243. package/dist/theme/declarationBoundary.d.ts +17 -0
  244. package/dist/theme/declarationBoundary.d.ts.map +1 -0
  245. package/dist/theme/declarationBoundary.js +343 -0
  246. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  247. package/dist/theme/derivedVarRegistry.js +4 -0
  248. package/dist/theme/generateThemeRules.d.ts +13 -7
  249. package/dist/theme/generateThemeRules.d.ts.map +1 -1
  250. package/dist/theme/generateThemeRules.js +140 -34
  251. package/dist/utils/interactionOverlay.stylex.d.ts +8 -0
  252. package/dist/utils/interactionOverlay.stylex.d.ts.map +1 -1
  253. package/dist/utils/interactionOverlay.stylex.js +9 -0
  254. package/dist/utils/safeUrl.d.ts +11 -0
  255. package/dist/utils/safeUrl.d.ts.map +1 -0
  256. package/dist/utils/safeUrl.js +78 -0
  257. package/locales/af-ZA.json +530 -1
  258. package/locales/ar-SA.json +490 -1
  259. package/locales/ca-ES.json +514 -1
  260. package/locales/cs-CZ.json +498 -1
  261. package/locales/da-DK.json +522 -1
  262. package/locales/de-DE.json +522 -1
  263. package/locales/el-GR.json +498 -1
  264. package/locales/es-ES.json +510 -1
  265. package/locales/fi-FI.json +498 -1
  266. package/locales/fr-FR.json +534 -1
  267. package/locales/he-IL.json +498 -1
  268. package/locales/hu-HU.json +498 -1
  269. package/locales/it-IT.json +510 -1
  270. package/locales/ja-JP.json +490 -1
  271. package/locales/ko-KR.json +498 -1
  272. package/locales/nl-NL.json +534 -1
  273. package/locales/no-NO.json +510 -1
  274. package/locales/pl-PL.json +506 -1
  275. package/locales/pt-BR.json +506 -1
  276. package/locales/pt-PT.json +506 -1
  277. package/locales/ro-RO.json +506 -1
  278. package/locales/ru-RU.json +498 -1
  279. package/locales/sr-SP.json +498 -1
  280. package/locales/sv-SE.json +502 -1
  281. package/locales/tr-TR.json +498 -1
  282. package/locales/uk-UA.json +498 -1
  283. package/locales/vi-VN.json +502 -1
  284. package/locales/zh-CN.json +490 -1
  285. package/locales/zh-TW.json +490 -1
  286. package/package.json +24 -4
  287. package/src/BottomSheet/BottomSheet.doc.mjs +8 -1
  288. package/src/BottomSheet/BottomSheet.spec.md +46 -20
  289. package/src/BottomSheet/BottomSheet.test.tsx +6 -3
  290. package/src/BottomSheet/BottomSheet.tsx +3 -1
  291. package/src/BottomSheet/BottomSheetKeyboard.test.tsx +195 -0
  292. package/src/BottomSheet/BottomSheetPanel.test.tsx +11 -1
  293. package/src/BottomSheet/BottomSheetPanel.tsx +49 -16
  294. package/src/BottomSheet/__tests__/BottomSheetKeyboard.a11y.browser.spec.ts +344 -0
  295. package/src/Button/Button.doc.mjs +5 -5
  296. package/src/Button/Button.tsx +15 -13
  297. package/src/Button/__tests__/Button.a11y.chromium.spec.ts +17 -1
  298. package/src/Button/__tests__/Button.a11y.known-failures.ts +0 -29
  299. package/src/Button/__tests__/Button.a11y.renders.tsx +9 -2
  300. package/src/Button/__tests__/Button.a11y.states.ts +9 -0
  301. package/src/Chat/ChatComposer.doc.mjs +14 -0
  302. package/src/Chat/ChatComposer.spec.md +230 -0
  303. package/src/Chat/ChatComposer.test.tsx +57 -0
  304. package/src/Chat/ChatComposer.tsx +21 -9
  305. package/src/Chat/ChatComposerDrawer.doc.mjs +63 -5
  306. package/src/Chat/ChatComposerDrawer.spec.md +267 -0
  307. package/src/Chat/ChatComposerDrawer.test.tsx +81 -12
  308. package/src/Chat/ChatComposerDrawer.tsx +38 -9
  309. package/src/Chat/ChatComposerInput.doc.mjs +114 -26
  310. package/src/Chat/ChatComposerInput.spec.md +185 -0
  311. package/src/Chat/ChatComposerInput.test.tsx +113 -3
  312. package/src/Chat/ChatComposerInput.tsx +78 -13
  313. package/src/Chat/ChatComposerTokenElement.spec.md +152 -0
  314. package/src/Chat/ChatComposerTokenElement.test.tsx +85 -0
  315. package/src/Chat/ChatDictationButton.doc.mjs +3 -3
  316. package/src/Chat/ChatDictationButton.spec.md +160 -0
  317. package/src/Chat/ChatDictationButton.test.tsx +92 -27
  318. package/src/Chat/ChatDictationButton.tsx +8 -6
  319. package/src/Chat/ChatLayout.doc.mjs +12 -4
  320. package/src/Chat/ChatLayout.spec.md +271 -0
  321. package/src/Chat/ChatLayout.test.tsx +28 -0
  322. package/src/Chat/ChatLayout.tsx +19 -0
  323. package/src/Chat/ChatLayoutScrollButton.spec.md +338 -0
  324. package/src/Chat/ChatLayoutScrollButton.test.tsx +31 -0
  325. package/src/Chat/ChatLayoutScrollButton.tsx +31 -14
  326. package/src/Chat/ChatMessageBubble.doc.mjs +28 -15
  327. package/src/Chat/ChatMessageBubble.spec.md +263 -0
  328. package/src/Chat/ChatMessageBubble.test.tsx +93 -0
  329. package/src/Chat/ChatMessageBubble.tsx +8 -3
  330. package/src/Chat/ChatMessageList.spec.md +222 -0
  331. package/src/Chat/ChatMessageList.test.tsx +64 -2
  332. package/src/Chat/ChatMessageList.tsx +4 -3
  333. package/src/Chat/ChatMessageMetadata.doc.mjs +57 -3
  334. package/src/Chat/ChatMessageMetadata.spec.md +220 -0
  335. package/src/Chat/ChatMessageMetadata.test.tsx +34 -0
  336. package/src/Chat/ChatMessageMetadata.tsx +13 -13
  337. package/src/Chat/ChatSendButton.doc.mjs +32 -0
  338. package/src/Chat/ChatSendButton.spec.md +227 -0
  339. package/src/Chat/ChatSendButton.test.tsx +128 -8
  340. package/src/Chat/ChatSendButton.tsx +6 -4
  341. package/src/Chat/__tests__/ChatLayoutScrollButton.a11y.chromium.spec.ts +514 -0
  342. package/src/Chat/__tests__/ChatLayoutScrollButtonSurface.a11y.chromium.spec.ts +740 -0
  343. package/src/Chat/__tests__/ChatMessageBubble.a11y.chromium.spec.ts +457 -0
  344. package/src/Chat/__tests__/ChatMessageBubble.auditProvenance.test.ts +91 -0
  345. package/src/Chat/__tests__/ChatMessageBubble.auditProvenance.ts +91 -0
  346. package/src/Chat/__tests__/ChatMessageList.a11y.chromium.spec.ts +456 -0
  347. package/src/Chat/__tests__/ChatMessageMetadata.a11y.chromium.spec.ts +413 -0
  348. package/src/Chat/__tests__/ChatSendButton.a11y.chromium.spec.ts +1522 -0
  349. package/src/Chat/index.ts +1 -0
  350. package/src/CheckboxInput/CheckboxInput.doc.mjs +11 -0
  351. package/src/CheckboxInput/CheckboxInput.test.tsx +34 -0
  352. package/src/CheckboxInput/CheckboxInput.tsx +21 -1
  353. package/src/Citation/Citation.doc.mjs +2 -2
  354. package/src/Citation/Citation.test.tsx +17 -0
  355. package/src/Citation/Citation.tsx +4 -2
  356. package/src/ClickableCard/ClickableCard.doc.mjs +2 -2
  357. package/src/ClickableCard/ClickableCard.test.tsx +102 -5
  358. package/src/CodeBlock/highlightRanges.test.ts +143 -1
  359. package/src/CodeBlock/highlightRanges.ts +15 -5
  360. package/src/Collapsible/Collapsible.doc.mjs +11 -0
  361. package/src/Collapsible/Collapsible.test.tsx +21 -0
  362. package/src/Collapsible/Collapsible.tsx +5 -0
  363. package/src/CommandPalette/CommandPaletteInput.tsx +6 -1
  364. package/src/ComplexSelector/ComplexSelector.tsx +6 -1
  365. package/src/DateInput/DateInput.tsx +6 -1
  366. package/src/DateInput/NativeDateField.tsx +24 -8
  367. package/src/DateInput/TouchDateField.tsx +6 -2
  368. package/src/DateRangeInput/DateRangeInput.tsx +6 -1
  369. package/src/DateTimeInput/DateTimeInput.tsx +6 -1
  370. package/src/DateTimeInput/TouchDateTimeField.tsx +6 -1
  371. package/src/DateTimeInput/nativePickerSegmentStyles.ts +24 -8
  372. package/src/Dialog/Dialog.doc.mjs +2 -0
  373. package/src/Dialog/DialogHeader.doc.mjs +22 -0
  374. package/src/Dialog/DialogHeader.test.tsx +167 -3
  375. package/src/Dialog/DialogHeader.tsx +37 -6
  376. package/src/Dialog/modules/DialogHeader.spec.md +101 -42
  377. package/src/DropdownMenu/DropdownMenu.test.tsx +5 -2
  378. package/src/Field/InputClearButton.test.tsx +17 -3
  379. package/src/Field/InputClearButton.tsx +5 -2
  380. package/src/Field/PanelSearchInput.tsx +5 -2
  381. package/src/FieldStatus/__tests__/StatusMessage.a11y.states.ts +1 -1
  382. package/src/FileInput/FileInput.tsx +11 -2
  383. package/src/Icon/Icon.doc.mjs +7 -5
  384. package/src/Icon/Icon.spec.md +81 -40
  385. package/src/Icon/Icon.test.tsx +6 -3
  386. package/src/Icon/Icon.tsx +20 -68
  387. package/src/Icon/IconDefaultSizeContext.ts +23 -0
  388. package/src/Icon/IconSize.stylex.ts +70 -0
  389. package/src/IconButton/IconButton.doc.mjs +3 -3
  390. package/src/IconButton/IconButton.test.tsx +40 -0
  391. package/src/Item/Item.doc.mjs +6 -2
  392. package/src/Item/Item.tsx +9 -3
  393. package/src/Kbd/Kbd.doc.mjs +3 -3
  394. package/src/Kbd/Kbd.test.tsx +57 -1
  395. package/src/Kbd/Kbd.tsx +57 -37
  396. package/src/Layout/LayoutFooter.doc.mjs +17 -0
  397. package/src/Link/Link.doc.mjs +17 -4
  398. package/src/Link/Link.test.tsx +24 -0
  399. package/src/Link/Link.tsx +5 -0
  400. package/src/Link/LinkProvider.doc.mjs +2 -2
  401. package/src/Link/__tests__/Link.navigation.a11y.chromium.spec.ts +261 -0
  402. package/src/Link/useLinkComponent.test.tsx +333 -0
  403. package/src/Link/useLinkComponent.ts +29 -57
  404. package/src/List/List.doc.mjs +6 -0
  405. package/src/List/List.spec.md +200 -0
  406. package/src/List/List.test.tsx +117 -1
  407. package/src/List/List.tsx +22 -2
  408. package/src/List/ListContext.tsx +3 -1
  409. package/src/List/ListItem.doc.mjs +2 -2
  410. package/src/List/ListItem.source-build.test.mjs +41 -0
  411. package/src/List/ListItem.tsx +26 -2
  412. package/src/Markdown/Markdown.doc.mjs +300 -2
  413. package/src/Markdown/Markdown.fr23.bench.ts +174 -0
  414. package/src/Markdown/Markdown.fr23.perf.test.ts +249 -0
  415. package/src/Markdown/Markdown.fr23.sampling.ts +49 -0
  416. package/src/Markdown/Markdown.helpers.perf.test.ts +489 -0
  417. package/src/Markdown/Markdown.public.test.ts +130 -3
  418. package/src/Markdown/Markdown.renderBoundary.test.tsx +72 -0
  419. package/src/Markdown/Markdown.spec.md +210 -45
  420. package/src/Markdown/Markdown.test.tsx +87 -3
  421. package/src/Markdown/Markdown.tsx +414 -135
  422. package/src/Markdown/ast.test.ts +307 -0
  423. package/src/Markdown/ast.ts +337 -0
  424. package/src/Markdown/incremental.test.ts +43 -0
  425. package/src/Markdown/index.ts +37 -1
  426. package/src/Markdown/modules/remark.spec.md +269 -0
  427. package/src/Markdown/modules/softBreaks.spec.md +137 -0
  428. package/src/Markdown/parser/index.ts +64 -0
  429. package/src/Markdown/parser.public.test.ts +103 -0
  430. package/src/Markdown/parser.test.ts +38 -6
  431. package/src/Markdown/parser.ts +1221 -242
  432. package/src/Markdown/plugins/frontmatter.test.tsx +125 -0
  433. package/src/Markdown/plugins/frontmatter.ts +198 -0
  434. package/src/Markdown/plugins/index.ts +54 -0
  435. package/src/Markdown/plugins/protocol.test.tsx +1022 -0
  436. package/src/Markdown/plugins/protocol.ts +1618 -0
  437. package/src/Markdown/plugins/semanticFence.test.tsx +546 -0
  438. package/src/Markdown/plugins/semanticFence.ts +424 -0
  439. package/src/Markdown/plugins/softBreaks.test.tsx +172 -0
  440. package/src/Markdown/plugins/softBreaks.ts +169 -0
  441. package/src/Markdown/plugins/sourceDecoration.test.tsx +799 -0
  442. package/src/Markdown/plugins/sourceDecoration.ts +539 -0
  443. package/src/Markdown/plugins/textTransform.test.ts +467 -0
  444. package/src/Markdown/plugins/textTransform.ts +554 -0
  445. package/src/Markdown/remark.test.tsx +1877 -0
  446. package/src/Markdown/remark.ts +1574 -0
  447. package/src/Markdown/url.ts +36 -0
  448. package/src/Markdown/utils.ts +13 -1
  449. package/src/MetadataList/MetadataList.test.tsx +4 -2
  450. package/src/MetadataList/MetadataList.tsx +3 -3
  451. package/src/MultiSelector/MultiSelector.test.tsx +1 -35
  452. package/src/MultiSelector/__tests__/MultiSelector.listbox.a11y.test.tsx +128 -0
  453. package/src/NumberInput/NumberInput.tsx +6 -1
  454. package/src/Outline/Outline.doc.mjs +7 -5
  455. package/src/Outline/Outline.spec.md +1 -1
  456. package/src/Outline/index.ts +1 -0
  457. package/src/Outline/modules/parseOutlineFromMarkdown.spec.md +142 -0
  458. package/src/Outline/parseOutlineFromMarkdown.ts +40 -10
  459. package/src/Outline/useOutlineFromMarkdown.ts +15 -3
  460. package/src/PowerSearch/PowerSearch.doc.mjs +2 -2
  461. package/src/PowerSearch/PowerSearchEditPopover.test.tsx +150 -1
  462. package/src/PowerSearch/PowerSearchEditPopover.tsx +51 -28
  463. package/src/PowerSearch/PowerSearchValueEditor.test.tsx +51 -0
  464. package/src/PowerSearch/PowerSearchValueEditor.tsx +4 -0
  465. package/src/ProgressBar/ProgressBar.test.tsx +29 -11
  466. package/src/ProgressBar/ProgressBar.tsx +59 -72
  467. package/src/RadioList/RadioList.doc.mjs +11 -0
  468. package/src/RadioList/RadioList.test.tsx +32 -0
  469. package/src/RadioList/RadioListItem.tsx +25 -1
  470. package/src/ScrollableArea/ScrollableArea.tsx +1 -0
  471. package/src/ScrollableArea/modules/useScrollableArea.spec.md +50 -22
  472. package/src/SegmentedControl/SegmentedControl.doc.mjs +2 -2
  473. package/src/SegmentedControl/SegmentedControl.test.tsx +61 -0
  474. package/src/SegmentedControl/SegmentedControl.tsx +2 -1
  475. package/src/SegmentedControl/SegmentedControlItem.tsx +6 -9
  476. package/src/Selector/Selector.doc.mjs +3 -3
  477. package/src/Selector/Selector.spec.md +51 -49
  478. package/src/Selector/Selector.test.tsx +61 -55
  479. package/src/Selector/Selector.tsx +34 -15
  480. package/src/Selector/__tests__/Listbox.a11y.chromium.spec.ts +130 -0
  481. package/src/Selector/__tests__/Listbox.a11y.dom.ts +38 -0
  482. package/src/Selector/__tests__/Listbox.a11y.inventory.test.ts +63 -0
  483. package/src/Selector/__tests__/Listbox.a11y.known-failures.ts +37 -0
  484. package/src/Selector/__tests__/Listbox.a11y.states.ts +232 -0
  485. package/src/Selector/__tests__/Selector.a11y.chromium.spec.ts +45 -0
  486. package/src/Selector/__tests__/Selector.listbox.a11y.test.tsx +121 -0
  487. package/src/SideNav/SideNav.doc.mjs +1 -1
  488. package/src/SideNav/SideNav.test.tsx +10 -0
  489. package/src/SideNav/SideNav.tsx +14 -2
  490. package/src/Slider/Slider.doc.mjs +17 -3
  491. package/src/Slider/Slider.test.tsx +221 -0
  492. package/src/Slider/Slider.tsx +40 -9
  493. package/src/Spinner/Spinner.test.tsx +4 -0
  494. package/src/Spinner/Spinner.tsx +31 -18
  495. package/src/Switch/Switch.doc.mjs +11 -0
  496. package/src/Switch/Switch.test.tsx +16 -0
  497. package/src/Switch/Switch.tsx +28 -0
  498. package/src/TabList/Tab.tsx +35 -7
  499. package/src/TabList/TabList.doc.mjs +11 -0
  500. package/src/TabList/TabList.spec.md +204 -0
  501. package/src/TabList/TabList.test.tsx +66 -0
  502. package/src/TabList/__tests__/Tabs.a11y.known-failures.ts +1 -34
  503. package/src/Table/Table.doc.mjs +1 -1
  504. package/src/Table/Table.spec.md +54 -40
  505. package/src/Table/Table.test.tsx +83 -10
  506. package/src/Table/Table.tsx +30 -22
  507. package/src/Table/__tests__/TableScroll.a11y.chromium.spec.ts +137 -0
  508. package/src/TextArea/TextArea.tsx +6 -1
  509. package/src/TextInput/TextInput.test.tsx +20 -1
  510. package/src/TextInput/TextInput.tsx +6 -1
  511. package/src/TimeInput/TimeInput.tsx +6 -1
  512. package/src/Timer/Timer.doc.mjs +183 -0
  513. package/src/Timer/Timer.spec.md +215 -0
  514. package/src/Timer/Timer.test.tsx +302 -0
  515. package/src/Timer/Timer.tsx +258 -0
  516. package/src/Timer/index.ts +11 -0
  517. package/src/ToggleButton/ToggleButton.doc.mjs +3 -3
  518. package/src/ToggleButton/ToggleButton.test.tsx +299 -4
  519. package/src/ToggleButton/ToggleButton.tsx +26 -33
  520. package/src/ToggleButton/ToggleButtonGroup.doc.mjs +3 -3
  521. package/src/ToggleButton/__tests__/ToggleButton.a11y.chromium.spec.ts +209 -0
  522. package/src/Token/Token.doc.mjs +2 -2
  523. package/src/Tokenizer/Tokenizer.spec.md +142 -75
  524. package/src/TopNav/TopNavMenu.doc.mjs +2 -2
  525. package/src/TopNav/TopNavMenu.test.tsx +30 -0
  526. package/src/TopNav/TopNavMenu.tsx +7 -2
  527. package/src/TreeList/TreeList.doc.mjs +3 -3
  528. package/src/TreeList/TreeList.spec.md +123 -75
  529. package/src/TreeList/TreeList.test.tsx +29 -0
  530. package/src/TreeList/TreeList.tsx +3 -0
  531. package/src/TreeList/TreeListItem.tsx +15 -1
  532. package/src/TreeList/TreeListTypes.ts +11 -1
  533. package/src/Typeahead/BaseTypeahead.spec.md +4 -3
  534. package/src/Typeahead/BaseTypeahead.tsx +21 -7
  535. package/src/Typeahead/Typeahead.test.tsx +53 -0
  536. package/src/__tests__/PressedState.a11y.chromium.spec.ts +813 -0
  537. package/src/__tests__/pressState.ts +93 -0
  538. package/src/hooks/scrollKeyboardDelegation.test.ts +155 -0
  539. package/src/hooks/scrollKeyboardDelegation.ts +233 -0
  540. package/src/hooks/useClickableContainer.test.tsx +236 -0
  541. package/src/hooks/useClickableContainer.ts +20 -4
  542. package/src/hooks/useScrollableArea.doc.mjs +15 -3
  543. package/src/hooks/useScrollableArea.test.tsx +59 -1
  544. package/src/hooks/useScrollableArea.ts +34 -10
  545. package/src/index.ts +3 -0
  546. package/src/inputFontFloor.test.ts +106 -0
  547. package/src/tailwind-theme.css +0 -6
  548. package/src/theme/Theme.test.tsx +32 -0
  549. package/src/theme/declarationBoundary.test.ts +126 -0
  550. package/src/theme/declarationBoundary.ts +347 -0
  551. package/src/theme/derivedVarRegistry.test.ts +7 -0
  552. package/src/theme/derivedVarRegistry.ts +1 -0
  553. package/src/theme/generateThemeRules.test.ts +526 -1
  554. package/src/theme/generateThemeRules.ts +228 -50
  555. package/src/theme/tokenValueCompat.test.ts +4 -1
  556. package/src/utils/interactionOverlay.stylex.ts +20 -0
  557. package/src/utils/safeUrl.test.ts +88 -0
  558. package/src/utils/safeUrl.ts +85 -0
@@ -1,43 +1,108 @@
1
1
  // Copyright (c) Meta Platforms, Inc. and affiliates.
2
2
 
3
- import {describe, it, expect} from 'vitest';
4
- import {render, screen} from '@testing-library/react';
3
+ import {createRef} from 'react';
4
+ import {describe, it, expect, vi} from 'vitest';
5
+ import {fireEvent, render, screen} from '@testing-library/react';
6
+ import {colorVars} from '../theme/tokens.stylex';
5
7
  import {ChatDictationButton} from './ChatDictationButton';
8
+ import type {ChatDictationButtonProps} from './ChatDictationButton';
6
9
  import type {UseSpeechRecognitionReturn} from './useSpeechRecognition';
7
10
 
8
- const dictation: UseSpeechRecognitionReturn = {
9
- isSupported: true,
10
- isListening: false,
11
- isSpeaking: false,
12
- volume: 0,
13
- bands: [0, 0, 0, 0, 0],
14
- rawBands: [0, 0, 0, 0, 0],
15
- interimTranscript: '',
16
- start: () => {},
17
- stop: () => {},
18
- abort: () => {},
19
- toggle: () => {},
20
- };
11
+ function makeDictation(
12
+ overrides: Partial<UseSpeechRecognitionReturn> = {},
13
+ ): UseSpeechRecognitionReturn {
14
+ return {
15
+ isSupported: true,
16
+ isListening: false,
17
+ isSpeaking: false,
18
+ volume: 0,
19
+ bands: [0, 0, 0, 0, 0],
20
+ rawBands: [0, 0, 0, 0, 0],
21
+ interimTranscript: '',
22
+ start: vi.fn(),
23
+ stop: vi.fn(),
24
+ abort: vi.fn(),
25
+ toggle: vi.fn(),
26
+ ...overrides,
27
+ };
28
+ }
21
29
 
22
30
  describe('ChatDictationButton', () => {
23
- it('renders a dictation button', () => {
24
- render(
25
- <ChatDictationButton dictation={dictation} data-testid="dictation" />,
31
+ it('labels and toggles the idle button', () => {
32
+ const dictation = makeDictation();
33
+ render(<ChatDictationButton dictation={dictation} />);
34
+
35
+ fireEvent.click(screen.getByRole('button', {name: 'Start dictation'}));
36
+ expect(dictation.toggle).toHaveBeenCalledTimes(1);
37
+ });
38
+
39
+ it('shows five hidden visualizer bars and the stop label while listening', () => {
40
+ const dictation = makeDictation({
41
+ isListening: true,
42
+ bands: [0.02, 0.04, 0.08, 0.12, 0.18],
43
+ });
44
+ const {container} = render(<ChatDictationButton dictation={dictation} />);
45
+
46
+ expect(screen.getByRole('button', {name: 'Stop dictation'})).toBeEnabled();
47
+ const visualizer = container.querySelector('[aria-hidden="true"]');
48
+ expect(visualizer).not.toBeNull();
49
+ expect(visualizer?.children).toHaveLength(5);
50
+ });
51
+
52
+ it('uses theme-owned colors for clipping feedback', () => {
53
+ const dictation = makeDictation({
54
+ isListening: true,
55
+ volume: 0.3,
56
+ bands: [0.2, 0.2, 0.2, 0.2, 0.2],
57
+ });
58
+ const {container} = render(<ChatDictationButton dictation={dictation} />);
59
+
60
+ const barStyle = container
61
+ .querySelector('[aria-hidden="true"] > span')
62
+ ?.getAttribute('style');
63
+ expect(barStyle).toContain('color-mix(in srgb');
64
+ expect(barStyle).toContain(colorVars['--color-accent']);
65
+ expect(barStyle).toContain(colorVars['--color-error']);
66
+ expect(barStyle).not.toContain('hsl(');
67
+ });
68
+
69
+ it('hides unsupported dictation by default', () => {
70
+ const {container} = render(
71
+ <ChatDictationButton dictation={makeDictation({isSupported: false})} />,
26
72
  );
27
- expect(screen.getByTestId('dictation')).toBeTruthy();
73
+
74
+ expect(container).toBeEmptyDOMElement();
28
75
  });
29
76
 
30
- it('forwards rest props (data-*, aria-*, id) to the root element', () => {
77
+ it('disables an unsupported button when explicitly kept visible', () => {
31
78
  render(
32
79
  <ChatDictationButton
33
- dictation={dictation}
34
- data-testid="dictation"
35
- data-custom="x"
36
- id="dictate-1"
80
+ dictation={makeDictation({isSupported: false})}
81
+ isHiddenWhenUnsupported={false}
37
82
  />,
38
83
  );
39
- const root = screen.getByTestId('dictation');
40
- expect(root).toHaveAttribute('data-custom', 'x');
41
- expect(root).toHaveAttribute('id', 'dictate-1');
84
+
85
+ expect(
86
+ screen.getByRole('button', {name: 'Start dictation'}),
87
+ ).toBeDisabled();
88
+ });
89
+
90
+ it('forwards the root ref and accepted span props', () => {
91
+ const ref = createRef<HTMLSpanElement>();
92
+ const props = {
93
+ dictation: makeDictation(),
94
+ 'data-testid': 'dictation',
95
+ 'data-custom': 'x',
96
+ id: 'dictate-1',
97
+ label: 'Use voice input',
98
+ ref,
99
+ } satisfies ChatDictationButtonProps;
100
+
101
+ render(<ChatDictationButton {...props} />);
102
+
103
+ expect(ref.current).toBe(screen.getByTestId('dictation'));
104
+ expect(ref.current).toHaveAttribute('data-custom', 'x');
105
+ expect(ref.current).toHaveAttribute('id', 'dictate-1');
106
+ expect(screen.getByRole('button', {name: 'Use voice input'})).toBeEnabled();
42
107
  });
43
108
  });
@@ -11,7 +11,8 @@
11
11
  * A toggle button that connects to useChatDictation. Shows a microphone
12
12
  * icon when idle. When listening, replaces the icon with volume-reactive
13
13
  * frequency bars (equalizer style) that respond to real mic input.
14
- * Bars use the accent color and hue-shift when volume clips past 10%.
14
+ * Bars use the accent color and blend toward the semantic error color when
15
+ * volume clips past 20%.
15
16
  *
16
17
  * SYNC: When modified, update:
17
18
  * - /packages/core/src/Chat/index.ts (exports)
@@ -39,7 +40,7 @@ export interface ChatDictationButtonProps extends BaseProps<HTMLSpanElement> {
39
40
  dictation: UseSpeechRecognitionReturn;
40
41
  /** Button size. @default "md" */
41
42
  size?: 'sm' | 'md';
42
- /** Hide the button when SpeechRecognition is not supported. @default true */
43
+ /** Hide the button when SpeechRecognition is unsupported. When false, the unsupported button remains visible but disabled. @default true */
43
44
  isHiddenWhenUnsupported?: boolean;
44
45
  /** Accessible label override. */
45
46
  label?: string;
@@ -128,13 +129,13 @@ export function ChatDictationButton({
128
129
  // Boost each band for visibility — quiet speech (0-10%) maps to full visual range
129
130
  const boostedBands = bands.map(b => Math.min(Math.pow(b / 0.2, 0.5), 1));
130
131
 
131
- // Hue shift from accent color when volume clips past 10%
132
+ // Blend toward the theme's semantic error color as clipping increases.
132
133
  const isClipping = rawVolume >= 0.2;
133
- const hueShift = isClipping ? Math.min((rawVolume - 0.2) / 0.1, 1) * 60 : 0;
134
+ const clippingStrength = Math.min((rawVolume - 0.2) / 0.1, 1) * 100;
134
135
 
135
136
  const barColor = isClipping
136
- ? `hsl(calc(var(--accent-hue, 210) + ${hueShift}), 80%, 50%)`
137
- : `var(--color-accent, ${colorVars['--color-accent']})`;
137
+ ? `color-mix(in srgb, ${colorVars['--color-accent']}, ${colorVars['--color-error']} ${clippingStrength}%)`
138
+ : colorVars['--color-accent'];
138
139
 
139
140
  const {barWidth, barGap, barMaxHeight} = SIZE_CONFIG[size];
140
141
 
@@ -180,6 +181,7 @@ export function ChatDictationButton({
180
181
  size={size}
181
182
  icon={isListening ? undefined : <Icon icon="microphone" size={size} />}
182
183
  isIconOnly
184
+ isDisabled={!dictation.isSupported}
183
185
  onClick={dictation.toggle}
184
186
  />
185
187
  </span>
@@ -6,7 +6,7 @@ export const docs = {
6
6
  name: 'ChatLayout',
7
7
  subComponentOf: 'Chat',
8
8
  displayName: 'Chat Layout',
9
- description: 'Layout shell for full chat interfaces. Messages flow in normal page flow, composer is fixed to the bottom with a frosted glass dock. Adapts density (compact/balanced/spacious) automatically via container width observation. Includes built-in auto-scroll, a "New messages" scroll-to-bottom button, and a frosted glass blur layer behind the composer. By default the layout root is the scroll container; pass scrollRef to delegate scrolling to a parent element or the document body.',
9
+ description: 'Layout shell for full chat interfaces. Messages flow in normal page flow, composer is fixed to the bottom with a frosted glass dock. Set density (compact/balanced/spacious) to control spacing, message max-width, and blur layer sizing. Includes built-in auto-scroll, a "New messages" scroll-to-bottom button, and a frosted glass blur layer behind the composer. By default the layout root is the scroll container; pass scrollRef to delegate scrolling to a parent element or the document body.',
10
10
  props: [
11
11
  {
12
12
  name: 'children',
@@ -53,9 +53,15 @@ export const docs = {
53
53
  type: 'React.RefObject<HTMLElement | null>',
54
54
  description: 'External scroll container ref. When provided, auto-scroll and scroll-to-bottom target this element instead of the layout root. Use when the chat is embedded in a page where a parent element or the document body scrolls.',
55
55
  },
56
+ {
57
+ name: 'density',
58
+ type: "'compact' | 'balanced' | 'spacious'",
59
+ description: 'Visual density: controls dock padding, message-area max-width and inline padding, and the height of the frosted glass blur layer.',
60
+ default: "'balanced'",
61
+ },
56
62
  ],
57
63
  usage: {
58
- description: 'ChatLayout is the layout shell for full-page chat interfaces. It renders messages in normal page flow and docks the composer to the bottom with a frosted glass blur layer. Density adapts automatically via container width observation. Use it to wrap ChatMessageList and ChatComposer for a complete chat experience with built-in auto-scroll and a scroll-to-bottom button.',
64
+ description: 'ChatLayout is the layout shell for full-page chat interfaces. It renders messages in normal page flow and docks the composer to the bottom with a frosted glass blur layer. Set the density prop to control spacing; it defaults to balanced. Use it to wrap ChatMessageList and ChatComposer for a complete chat experience with built-in auto-scroll and a scroll-to-bottom button.',
59
65
  bestPractices: [
60
66
  {
61
67
  guidance: true,
@@ -111,20 +117,21 @@ export const docs = {
111
117
  export const docsZh = {
112
118
  name: 'ChatLayout',
113
119
  displayName: 'Chat Layout',
114
- description: '完整聊天界面的布局外壳。消息在页面中自然流动,编写器固定在底部,带有毛玻璃效果。通过容器宽度自动适配密度。',
120
+ description: '完整聊天界面的布局外壳。消息在页面中自然流动,编写器固定在底部,带有毛玻璃效果。通过 density 属性控制密度。',
115
121
  propDescriptions: {
116
122
  children: '消息内容,通常是 ChatMessageList。在页面中自然流动。',
117
123
  composer: '编写器元素,通常是 ChatComposer。固定在底部,带有毛玻璃底座。',
118
124
  emptyState: '子元素为空时显示的内容。',
119
125
  scrollButton: '编写器上方的滚动到底部按钮。默认使用 ChatLayoutScrollButton。传入 null 隐藏。',
120
126
  scrollRef: '外部滚动容器引用。提供时,自动滚动和滚动到底部将目标指向此元素。',
127
+ density: '视觉密度:控制底座内边距、消息区最大宽度和毛玻璃层高度。',
121
128
  },
122
129
  };
123
130
 
124
131
  export const docsDense = {
125
132
  name: 'ChatLayout',
126
133
  displayName: 'Chat Layout',
127
- description: 'layout shell for full chat; msgs in page flow, composer fixed bottom w/ frosted glass dock; auto density via container width; scrollRef delegates to parent/body',
134
+ description: 'layout shell for full chat; msgs in page flow, composer fixed bottom w/ frosted glass dock; density prop (default balanced); scrollRef delegates to parent/body',
128
135
  usage: {
129
136
  bestPractices: [
130
137
  {guidance: true, description: 'Pass ChatMessageList as children and ChatComposer as composer prop for complete chat.'},
@@ -140,5 +147,6 @@ export const docsDense = {
140
147
  emptyState: 'content when children empty',
141
148
  scrollButton: 'scroll-to-bottom btn; defaults to ChatLayoutScrollButton; pass null to hide',
142
149
  scrollRef: 'external scroll container ref; targets parent/body instead of layout root',
150
+ density: 'visual density; dock padding, message max-width, blur height',
143
151
  },
144
152
  };
@@ -0,0 +1,271 @@
1
+ ---
2
+ schema_version: 3
3
+ template_version: 5
4
+ kind: component
5
+ id: component:ChatLayout
6
+ authority: draft
7
+ archive_reason: null
8
+ superseded_by: null
9
+ approved_by: null
10
+ approved_at: null
11
+ owners: [cixzhang]
12
+ review_triggers:
13
+ [public-api, behavior, layout, scrolling, accessibility, theming, testing]
14
+ verified_by:
15
+ [
16
+ packages/core/src/Chat/ChatLayout.test.tsx,
17
+ packages/core/src/Chat/ChatLayoutScrollButton.test.tsx,
18
+ packages/core/src/Chat/useChatStreamScroll.test.tsx,
19
+ packages/core/src/Chat/useChatNewMessages.test.tsx,
20
+ apps/storybook/stories/ChatLayout.stories.tsx,
21
+ ]
22
+ modules: []
23
+ families: []
24
+ design_specs: []
25
+ architecture:
26
+ [
27
+ architecture:public-component-api,
28
+ architecture:component-style-authoring,
29
+ architecture:component-theming-surface,
30
+ architecture:theme-tokens,
31
+ architecture:react-component-runtime,
32
+ architecture:component-test-sufficiency,
33
+ architecture:knowledge-contracts,
34
+ ]
35
+ contributing: []
36
+ system_specs: [spec:AST-025, spec:AST-029]
37
+ ---
38
+
39
+ # ChatLayout component contract
40
+
41
+ This record is an **observational backfill** written during the 2026-09-23
42
+ component audit under `spec:AST-029/FR3–FR4`. Every row below describes behavior
43
+ already shipped on `main` and verified against source, focused tests, and
44
+ consumer docs. It adds, improves, removes and reinterprets nothing. Rows whose
45
+ basis is only current behavior are marked `verify`; unresolved questions stay in
46
+ Open questions rather than being settled here.
47
+
48
+ ## Contract at a glance
49
+
50
+ | Area | Contract |
51
+ | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
52
+ | Public contract | `children`, `composer`, `emptyState`, `scrollButton`, `scrollRef`, `density`, plus `BaseProps<HTMLDivElement>` and `ref` on the layout root. |
53
+ | Behavior | The root is a flex column; the message area grows and never shrinks; the dock is sticky when the root scrolls and fixed when `scrollRef` names an outer scroller. |
54
+ | End-user impact | A chat transcript scrolls under a composer that stays reachable, auto-follows streaming output, and offers a scroll-to-bottom affordance when the reader has moved away from the bottom. |
55
+ | Builder impact | None from this record. It documents the shipped surface; `density` becomes discoverable in consumer docs, which previously described an adaptation the component never performed. |
56
+ | Compatibility/readiness | Observational draft. No public API, default, or compatibility promise changes. Authority is `draft` until exact-head owner approval. |
57
+ | Review checks | Reject any row that states unshipped behavior, promises a default this component does not apply, or resolves OQ1 or OQ2 without an owner decision. |
58
+ | Governing rules | `architecture:public-component-api/INV1, INV5–INV6, INV8`; `architecture:component-style-authoring/INV1–INV3, INV5`; `architecture:theme-tokens/INV3`; `spec:AST-025/FR12` (not adopted). |
59
+
60
+ This table is a review projection; the body below is authoritative.
61
+
62
+ ## Intent
63
+
64
+ ChatLayout is the structural shell for a chat surface. It owns one job: place a
65
+ scrolling transcript above a docked composer so that short conversations do not
66
+ overflow, long ones scroll, and the composer stays at the bottom edge of
67
+ whichever element actually scrolls. Scroll mechanics live in
68
+ `useChatStreamScroll` and `useChatNewMessages`; this component wires them to the
69
+ DOM it owns and publishes the resulting refs through `ChatLayoutContext`.
70
+
71
+ Consumer usage, prop syntax, and examples belong in `ChatLayout.doc.mjs`.
72
+
73
+ ## Compatibility and migration
74
+
75
+ - Released default preserved: yes — `@astryxdesign/core@0.6.2` ships `./Chat`
76
+ with `ChatLayout`, `ChatLayoutProps`, and `ChatLayoutScrollButton`.
77
+ - Compatibility class: no change. This record is descriptive.
78
+ - Controlled/uncontrolled behavior: not applicable; the component holds no
79
+ controllable value.
80
+ - Migration decision: none.
81
+
82
+ ## Ownership boundary
83
+
84
+ **Owns**
85
+
86
+ - The layout root: flex column, relative positioning, `container-type:
87
+ inline-size`, and the `chat-layout` theming target with its `density` axis.
88
+ - Which element scrolls: the root itself, or the element named by `scrollRef`.
89
+ - Dock positioning — `sticky` in self-scroll mode, `fixed` in external-scroll
90
+ mode — and the frosted glass layer painted behind it.
91
+ - Density geometry: dock padding, message-column max-width and inline padding,
92
+ and the blur layer's height and mask.
93
+ - Empty-state substitution and its centering box.
94
+ - The default scroll-to-bottom affordance and the visibility signal it receives.
95
+ - Publication of `scrollContainerRef` and `contentRef` through `ChatLayoutContext`.
96
+
97
+ **Does not own / non-goals**
98
+
99
+ - Scroll measurement, spring animation, lock behavior, or new-message detection —
100
+ owned by `useChatStreamScroll` and `useChatNewMessages`.
101
+ - Message presentation, grouping, or sender semantics — owned by
102
+ `component:ChatMessageList` and `component:ChatMessage`.
103
+ - Composer behavior, sizing, or status — owned by `component:ChatComposer`.
104
+ - The scroll-to-bottom control's own presentation — owned by
105
+ `component:ChatLayoutScrollButton`.
106
+ - Page shell, navigation, and landmark assignment — the caller's.
107
+ - The shared effective-axis scroll capability defined by `spec:AST-025`. This
108
+ component predates it and has not adopted it; see OQ1.
109
+
110
+ ## Public concepts
111
+
112
+ | Concept | Closed values or states | Meaning | Default | Owner | Stability | Invalid-value behavior |
113
+ | -------------- | -------------------------------------------------------- | ---------------------------------------------------------------------------------- | ----------- | -------------------- | --------- | ------------------------------------------ |
114
+ | `children` | any `ReactNode` | Transcript content placed in the message area. | required | caller | stable | n/a — required |
115
+ | `composer` | any `ReactNode` | Content placed inside the dock. | required | caller | stable | n/a — required |
116
+ | `emptyState` | `ReactNode`; omitted | Rendered centred in place of `children` when `children` is empty. | omitted | caller | stable | omitted renders `children` unchanged |
117
+ | `scrollButton` | `undefined`; `null`; any `ReactNode` | `undefined` selects the wired default, `null` renders nothing, a node replaces it. | `undefined` | component/caller | stable | any node renders as given |
118
+ | `scrollRef` | `RefObject<HTMLElement \| null>`; omitted | Names the scrolling element; omitted makes the root scroll. | omitted | caller | stable | a null `current` leaves scrolling inactive |
119
+ | `density` | `compact`; `balanced`; `spacious` | Selects dock padding, message-column width, and blur-layer size. | `balanced` | component | stable | closed union; rejected by types |
120
+ | root surface | `ref`; DOM/data/ARIA props; `className`/`style`/`xstyle` | Extends the layout root. | omitted | public component API | stable | n/a |
121
+
122
+ `density` is a closed union. It is a documented `visualProps` axis of the
123
+ `chat-layout` target, and it is not theme-extensible: no unavailable custom value
124
+ has a safe theme-independent baseline, so it stays closed under
125
+ `architecture:component-theming-surface/INV14`.
126
+
127
+ ## Behavioral and layout contract
128
+
129
+ | ID | Candidate invariant | Basis | Draft review state |
130
+ | --- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | ------------------ |
131
+ | FR1 | The root MUST be a flex column, so an in-flow sticky dock's height is part of the root's height rather than added on top of it. | regression fix for the phantom-scrollbar defect; pinned tests | settled |
132
+ | FR2 | The message area MUST grow into leftover space and MUST NOT shrink below its content height, so short transcripts fill the root exactly and long ones overflow it. | same regression fix; pinned tests | settled |
133
+ | FR3 | With no `scrollRef`, the root MUST be the scroll container and the dock MUST be `position: sticky`. | shipped implementation; pinned test | verify |
134
+ | FR4 | With a `scrollRef`, the root MUST NOT become a scroll container and the dock MUST be `position: fixed`. | shipped implementation; pinned test | verify |
135
+ | FR5 | `emptyState` MUST replace `children` exactly when `children` is `null`, `undefined`, `false`, or an empty array, and MUST NOT replace any other children. | shipped `hasVisibleContent`; pinned tests | verify |
136
+ | FR6 | `scrollButton` MUST distinguish three cases: omitted selects the wired default, `null` renders nothing, and a supplied node replaces the default. | shipped implementation; pinned tests | verify |
137
+ | FR7 | `density` MUST reflect on the root as the `chat-layout` target's `data-density`, and MUST select the dock, message-area, dock-inner, and blur-layer geometry together. | shipped implementation; pinned tests; family doc `visualProps` | verify |
138
+ | FR8 | The layout MUST publish its scroll container and message content refs through `ChatLayoutContext` so the message list can register the observed content box. | shipped implementation | verify |
139
+ | FR9 | Accepted `ref`, DOM/data/ARIA props, `className`, `style`, and `xstyle` MUST reach and compose on the layout root. | `architecture:public-component-api/INV5–INV6, INV8` | settled |
140
+
141
+ ### Allowed variation
142
+
143
+ - **AV1 — Scroll ownership.** The caller chooses between root-owned and
144
+ externally-owned scrolling; the dock's positioning strategy follows that choice.
145
+ - **AV2 — Scroll affordance.** The caller may keep the wired default, remove it,
146
+ or substitute any node.
147
+ - **AV3 — Transcript content.** Any content is allowed in the message area;
148
+ ChatMessageList is a convention, not a requirement.
149
+ - **AV4 — Density geometry.** Resolved spacing and widths follow the active
150
+ theme's spacing scale; only the relationships between the three steps are fixed.
151
+
152
+ ### Representative states
153
+
154
+ | State | Required invariant | Allowed variation |
155
+ | ----------------------------------- | --------------------------------------------------------------- | --------------------------------------------- |
156
+ | Self-scroll, short transcript | Root fills without overflowing; dock sticky at the block end | Empty state may occupy the message area |
157
+ | Self-scroll, overflowing transcript | Root scrolls; message area keeps content height | Scroll affordance becomes visible |
158
+ | External `scrollRef` | Root does not scroll; dock is fixed to the viewport's block end | The named element may be the document element |
159
+ | Empty children with `emptyState` | Centred empty state replaces the transcript | Any node may be the empty state |
160
+ | `scrollButton={null}` | No scroll affordance renders | Auto-scroll behavior is unchanged |
161
+
162
+ ### Performance and resources
163
+
164
+ - **PR1 — No layout measurement in the component.** Density is a prop. ChatLayout
165
+ MUST NOT add a `ResizeObserver`, media query, or animation frame of its own for
166
+ presentation the CSS container/flex model already resolves
167
+ (`architecture:component-style-authoring/INV2`).
168
+ - **PR2 — Shared observation only.** Content-size observation reaches the browser
169
+ through the pooled `observeResize` utility used by `useChatNewMessages`, never a
170
+ per-instance observer.
171
+
172
+ ## Accessibility contract
173
+
174
+ - **AR1 — The wired default scroll affordance MUST NOT be focusable while it is
175
+ visually hidden.** The hidden pill paints nothing, so focus landing on it would
176
+ have no visible indicator (WCAG 2.2 SC 2.4.7, Level AA). `opacity` and
177
+ `pointer-events` do not remove an element from sequential focus navigation.
178
+ - **AR2 — The scroll affordance MUST remain keyboard reachable and operable while
179
+ visible**, with its accessible name supplied by the catalog and replaced by the
180
+ new-messages label when new messages have arrived.
181
+ - **AR3 — All user-visible and AT-facing strings MUST come from the catalog**:
182
+ `@astryx.chatLayout.newMessages` and
183
+ `@astryx.chatLayoutScrollButton.scrollToBottom`.
184
+ - **AR4 — The layout MUST NOT assign a landmark role or accessible name to the
185
+ caller's regions.** Region semantics stay caller-owned.
186
+ - The self-scroll root carries no `tabIndex`, role, or accessible name of its
187
+ own; the keyboard path into scrolled content comes from whatever `children`
188
+ supplies. Which layer should own that guarantee is unresolved; see OQ1.
189
+
190
+ ## Design relationships
191
+
192
+ - The dock paints a `backdrop-filter` blur with a block-start mask so the
193
+ transcript fades out behind the composer rather than ending at a hard edge.
194
+ Blur height and mask distance step with `density`.
195
+ - The scroll affordance is a pill whose height tracks `--size-element-md`, the
196
+ same token that sizes the `md` Button it wraps, so the pill's `overflow: hidden`
197
+ cannot clip that Button under a theme that retunes the element scale
198
+ (`architecture:theme-tokens/INV3`).
199
+ - Spacing, radius, shadow, duration, and easing all resolve from portable
200
+ semantic tokens. Component-local geometry that has no semantic role — the
201
+ message-column and dock-inner caps, the blur heights, and the empty-state
202
+ minimum height — remains a named component design decision.
203
+ - The component declares no portable token and no public custom property.
204
+
205
+ ### Theming reachability (observed)
206
+
207
+ Observed dispositions for ChatLayout's five anatomy parts. This record carries no
208
+ `anatomy-theming:v1` block; the block is optional during migration, and see OQ2.
209
+
210
+ | Anatomy part | Disposition |
211
+ | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
212
+ | Message area | `none` — reachability-gap: an internal element with no `themeProps` call. `chat-layout` sits on the scrolling root, so a theme cannot reach the transcript column's own padding or width cap. |
213
+ | Frosted glass dock | `none` — reachability-gap: the dock, its inner column, and the backdrop-filter blur layer paint the component's most distinctive surface and carry no target. |
214
+ | Scroll-to-bottom button | `delegatesTo` the `chat-layout-scroll-button` target, whose canonical owner in the current inventory is `component:Chat`. |
215
+ | Composer | `delegatesTo` the `chat-composer` target, whose canonical owner in the current inventory is `component:Chat`. |
216
+ | Empty state | `none` — reachability-gap: the empty-state box only centres caller content and carries no target. |
217
+
218
+ The `chat-layout` target itself is declared on the Chat family document
219
+ (`Chat.doc.mjs`) rather than on `ChatLayout.doc.mjs`.
220
+
221
+ ## Family and system relationships
222
+
223
+ - There is no `family:chat` record. The Chat components share the `Chat.doc.mjs`
224
+ family document and its theming target inventory, which is not a knowledge-record
225
+ family contract.
226
+ - `component:ChatLayoutScrollButton` owns the scroll affordance's presentation,
227
+ label, and focus behavior. ChatLayout owns only when it is visible.
228
+ - `component:ChatMessageList` consumes `ChatLayoutContext.contentRef`.
229
+ - `spec:AST-025` owns the shared effective-axis scroll capability. ChatLayout
230
+ does not adopt it today: it declares `overflow-y: auto` unconditionally in
231
+ self-scroll mode rather than resolving an effective axis, and it assigns no
232
+ keyboard access to the viewport itself. FR12 accepts an existing focusable
233
+ descendant as the keyboard path; see OQ1 for the open ownership question.
234
+
235
+ ## Verification map
236
+
237
+ | Contract | Verification | Representative states | Mutation or failure expectation | Audit section |
238
+ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------- | ------------------------------------------------------------------------------------------------- | --------------------- |
239
+ | FR1, FR2 | `ChatLayout.test.tsx` self-scroll layout contract | self-scroll, short and long transcript | Restoring `min-height: 100%` on the message area re-adds the dock's height and the root overflows | `audit:ChatLayout/§4` |
240
+ | FR3, FR4 | `ChatLayout.test.tsx` dock positioning | no `scrollRef`; external `scrollRef` | Swapping sticky and fixed detaches the dock from the element that actually scrolls | `audit:ChatLayout/§4` |
241
+ | FR5 | `ChatLayout.test.tsx` empty-state cases | empty array; populated children | Treating populated children as empty hides the transcript | `audit:ChatLayout/§4` |
242
+ | FR6 | `ChatLayout.test.tsx` scroll-button cases | default; custom node; `null` | Collapsing `null` into the default renders an affordance the caller removed | `audit:ChatLayout/§4` |
243
+ | FR7 | `ChatLayout.test.tsx` density cases; `themingTargets.test.ts` | compact; balanced; spacious | Dropping the reflected attribute makes the theming axis unselectable | `audit:ChatLayout/§2` |
244
+ | FR8 | `useChatNewMessages.test.tsx`, `useChatStreamScroll.test.tsx` | first fill; streaming growth | Losing the published refs stops auto-scroll from observing content growth | `audit:ChatLayout/§7` |
245
+ | AR1, AR2 | `ChatLayoutScrollButton.test.tsx` and `ChatLayout.test.tsx` tab-order cases; `ChatLayoutScrollButton.a11y.chromium.spec.ts` in real Chromium | hidden at rest; visible; re-hidden after activation | Reverting to `opacity`-only hiding puts an invisible control back in the tab order | `audit:ChatLayout/§1` |
246
+ | PR2 | Source review of `useChatNewMessages` | mounted list | A per-instance `ResizeObserver` replaces the pooled one | `audit:ChatLayout/§7` |
247
+
248
+ ## Decision log
249
+
250
+ None. This record settles no decision; it describes shipped behavior.
251
+
252
+ ## Open questions
253
+
254
+ - **OQ1 — Which layer owns the keyboard path to scrolled content, and should
255
+ ChatLayout guarantee it?** (`human-api`) In self-scroll mode the root is the
256
+ scroll container and carries no `tabIndex`, role, or accessible name of its
257
+ own. `component:ChatMessageList`, the documented child, carries `role="log"`
258
+ and `tabIndex={0}`, and `spec:AST-025/FR12` accepts such an existing
259
+ focusable descendant instead of a named viewport. `children` is typed
260
+ `ReactNode`, so whether a composition supplying no focusable descendant is
261
+ also covered is unresolved, as is which layer should own the guarantee. This
262
+ record states the arrangement and asserts no defect.
263
+ - **OQ2 — Should the frosted dock be themeable?** (`human-api`) The dock, its
264
+ inner column, and the blur layer paint the component's signature surface, and
265
+ no current target reaches them.
266
+
267
+ ## Content boundary
268
+
269
+ This file does not duplicate `ChatLayout.doc.mjs` prop tables or examples, the
270
+ audit's scores and evidence, or the scroll, message, and composer contracts owned
271
+ by their own components and hooks.
@@ -2,6 +2,7 @@
2
2
 
3
3
  import {describe, it, expect, vi, beforeEach, afterEach} from 'vitest';
4
4
  import {act, render, screen} from '@testing-library/react';
5
+ import userEvent from '@testing-library/user-event';
5
6
  import {ChatLayout} from './ChatLayout';
6
7
  import {ChatMessageList} from './ChatMessageList';
7
8
  import {ChatMessage} from './ChatMessage';
@@ -103,6 +104,33 @@ describe('ChatLayout', () => {
103
104
  });
104
105
  });
105
106
 
107
+ // ---------------------------------------------------------------------------
108
+ // Keyboard reachability of the default scroll-to-bottom button
109
+ //
110
+ // At rest the layout is neither scrolled up nor holding new messages, so the
111
+ // default ChatLayoutScrollButton renders in its hidden state. It is still in
112
+ // the DOM, and it is the first control in the dock — ahead of the composer.
113
+ // A control nobody can see must not be a tab stop: WCAG 2.2 SC 2.4.7 requires
114
+ // a visible focus indicator wherever focus can land, and the hidden pill paints
115
+ // nothing. `opacity: 0` and `pointer-events: none` do not remove an element
116
+ // from sequential focus navigation; only visibility/display/inert do.
117
+ // ---------------------------------------------------------------------------
118
+
119
+ describe('ChatLayout — default scroll button keyboard reachability', () => {
120
+ it('does not make the resting scroll-to-bottom button the first tab stop', async () => {
121
+ const user = userEvent.setup();
122
+ render(
123
+ <ChatLayout composer={<button type="button">Send</button>}>
124
+ <div>msg</div>
125
+ </ChatLayout>,
126
+ );
127
+
128
+ await user.tab();
129
+
130
+ expect(screen.getByRole('button', {name: 'Send'})).toHaveFocus();
131
+ });
132
+ });
133
+
106
134
  // ---------------------------------------------------------------------------
107
135
  // Self-scroll layout contract (#2573)
108
136
  //
@@ -268,6 +268,25 @@ function hasVisibleContent(children: ReactNode): boolean {
268
268
  // Component
269
269
  // =============================================================================
270
270
 
271
+ /**
272
+ * Layout shell for a full chat interface: messages in page flow, composer
273
+ * docked to the bottom behind a frosted glass layer, with auto-scroll and a
274
+ * scroll-to-bottom button wired in.
275
+ *
276
+ * @example
277
+ * ```
278
+ * <ChatLayout
279
+ * density="spacious"
280
+ * composer={<ChatComposer onSubmit={send} />}
281
+ * emptyState={<EmptyState title="No messages yet" />}>
282
+ * <ChatMessageList>
283
+ * <ChatMessage sender="assistant">
284
+ * <ChatMessageBubble>How can I help?</ChatMessageBubble>
285
+ * </ChatMessage>
286
+ * </ChatMessageList>
287
+ * </ChatLayout>
288
+ * ```
289
+ */
271
290
  export function ChatLayout({
272
291
  children,
273
292
  composer,