@astryxdesign/core 0.5.2-canary.501e975 → 0.5.2-canary.51676e7

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 (284) hide show
  1. package/dist/AppShell/AppShell.d.ts.map +1 -1
  2. package/dist/AppShell/AppShell.js +2 -3
  3. package/dist/Chat/ChatComposer.d.ts +2 -2
  4. package/dist/Chat/ChatComposer.d.ts.map +1 -1
  5. package/dist/Chat/ChatComposer.js +21 -2
  6. package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
  7. package/dist/Chat/ChatComposerInput.js +20 -3
  8. package/dist/Chat/chatComposerSelection.d.ts +25 -0
  9. package/dist/Chat/chatComposerSelection.d.ts.map +1 -1
  10. package/dist/Chat/chatComposerSelection.js +78 -0
  11. package/dist/Chat/useChatNewMessages.js +3 -3
  12. package/dist/Chat/useChatStreamScroll.d.ts.map +1 -1
  13. package/dist/Chat/useChatStreamScroll.js +64 -72
  14. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  15. package/dist/CheckboxInput/CheckboxInput.js +8 -7
  16. package/dist/CheckboxList/CheckboxListItem.d.ts +5 -3
  17. package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
  18. package/dist/Collapsible/Collapsible.d.ts.map +1 -1
  19. package/dist/Collapsible/Collapsible.js +1 -1
  20. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  21. package/dist/DropdownMenu/DropdownMenu.js +2 -4
  22. package/dist/Field/Field.d.ts.map +1 -1
  23. package/dist/Field/Field.js +10 -4
  24. package/dist/Field/PanelSearchInput.d.ts.map +1 -1
  25. package/dist/Field/PanelSearchInput.js +3 -5
  26. package/dist/FieldStatus/FieldStatus.d.ts.map +1 -1
  27. package/dist/FieldStatus/FieldStatus.js +4 -2
  28. package/dist/Layer/useTouchTrigger.d.ts.map +1 -1
  29. package/dist/Layer/useTouchTrigger.js +2 -4
  30. package/dist/Layout/LayoutHeader.d.ts +10 -1
  31. package/dist/Layout/LayoutHeader.d.ts.map +1 -1
  32. package/dist/Layout/LayoutHeader.js +8 -2
  33. package/dist/Markdown/parser.d.ts +26 -0
  34. package/dist/Markdown/parser.d.ts.map +1 -1
  35. package/dist/Markdown/parser.js +210 -56
  36. package/dist/RadioList/RadioListItem.d.ts +30 -5
  37. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  38. package/dist/RadioList/RadioListItem.js +25 -11
  39. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  40. package/dist/Resizable/ResizeHandle.js +23 -2
  41. package/dist/Resizable/index.d.ts +2 -0
  42. package/dist/Resizable/index.d.ts.map +1 -1
  43. package/dist/Resizable/index.js +1 -0
  44. package/dist/Resizable/useResizable.d.ts +89 -8
  45. package/dist/Resizable/useResizable.d.ts.map +1 -1
  46. package/dist/Resizable/useResizable.js +521 -38
  47. package/dist/Resizable/utils.d.ts +35 -0
  48. package/dist/Resizable/utils.d.ts.map +1 -0
  49. package/dist/Resizable/utils.js +27 -0
  50. package/dist/Slider/Slider.d.ts.map +1 -1
  51. package/dist/Slider/Slider.js +3 -5
  52. package/dist/Stepper/Step.d.ts +2 -1
  53. package/dist/Stepper/Step.d.ts.map +1 -1
  54. package/dist/Stepper/Step.js +75 -11
  55. package/dist/Stepper/Stepper.d.ts.map +1 -1
  56. package/dist/Stepper/Stepper.js +1 -0
  57. package/dist/Switch/Switch.d.ts.map +1 -1
  58. package/dist/Switch/Switch.js +8 -7
  59. package/dist/TabList/TabList.d.ts +19 -1
  60. package/dist/TabList/TabList.d.ts.map +1 -1
  61. package/dist/TabList/TabList.js +28 -5
  62. package/dist/Table/index.d.ts +16 -1
  63. package/dist/Table/index.d.ts.map +1 -1
  64. package/dist/Table/index.js +4 -0
  65. package/dist/Table/plugins/columnResize/useTableColumnResize.d.ts.map +1 -1
  66. package/dist/Table/plugins/columnResize/useTableColumnResize.js +8 -9
  67. package/dist/Table/plugins/rowStatus/index.d.ts +1 -1
  68. package/dist/Table/plugins/rowStatus/index.d.ts.map +1 -1
  69. package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts +11 -33
  70. package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts.map +1 -1
  71. package/dist/Table/plugins/rowStatus/useTableRowStatus.js +108 -75
  72. package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
  73. package/dist/Table/plugins/sortable/useTableSortable.js +9 -1
  74. package/dist/Text/useTruncation.d.ts +1 -1
  75. package/dist/Text/useTruncation.d.ts.map +1 -1
  76. package/dist/Text/useTruncation.js +8 -7
  77. package/dist/Timestamp/Timestamp.d.ts +2 -2
  78. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  79. package/dist/Timestamp/Timestamp.js +4 -145
  80. package/dist/Timestamp/formatRelativeTime.d.ts +20 -0
  81. package/dist/Timestamp/formatRelativeTime.d.ts.map +1 -0
  82. package/dist/Timestamp/formatRelativeTime.js +81 -0
  83. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  84. package/dist/Toast/ToastViewport.js +124 -77
  85. package/dist/Tokenizer/Tokenizer.d.ts +0 -38
  86. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  87. package/dist/Tokenizer/Tokenizer.js +94 -26
  88. package/dist/Tokenizer/useEndLaneReserve.d.ts +31 -0
  89. package/dist/Tokenizer/useEndLaneReserve.d.ts.map +1 -0
  90. package/dist/Tokenizer/useEndLaneReserve.js +136 -0
  91. package/dist/Tooltip/useTooltip.d.ts +1 -1
  92. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  93. package/dist/Tooltip/useTooltip.js +53 -42
  94. package/dist/TreeList/TreeList.d.ts +1 -1
  95. package/dist/TreeList/TreeList.d.ts.map +1 -1
  96. package/dist/TreeList/TreeList.js +11 -1
  97. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  98. package/dist/Typeahead/BaseTypeahead.js +85 -30
  99. package/dist/Typeahead/Typeahead.d.ts +0 -21
  100. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  101. package/dist/Typeahead/Typeahead.js +108 -67
  102. package/dist/Typeahead/busyIndicatorLane.d.ts +62 -0
  103. package/dist/Typeahead/busyIndicatorLane.d.ts.map +1 -0
  104. package/dist/Typeahead/busyIndicatorLane.js +98 -0
  105. package/dist/astryx.css +21 -9
  106. package/dist/hooks/useFocusReturnVisibility.d.ts.map +1 -1
  107. package/dist/hooks/useFocusReturnVisibility.js +3 -5
  108. package/dist/hooks/useFocusTrap.d.ts +3 -1
  109. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  110. package/dist/hooks/useFocusTrap.js +31 -1
  111. package/dist/hooks/useIsomorphicLayoutEffect.d.ts +2 -2
  112. package/dist/hooks/useIsomorphicLayoutEffect.d.ts.map +1 -1
  113. package/dist/hooks/useOverflow.js +11 -11
  114. package/dist/hooks/useScrollOverflow.d.ts.map +1 -1
  115. package/dist/hooks/useScrollOverflow.js +7 -4
  116. package/dist/theme/defineTheme.d.ts +13 -0
  117. package/dist/theme/defineTheme.d.ts.map +1 -1
  118. package/dist/theme/defineTheme.js +7 -0
  119. package/dist/theme/generateThemeRules.d.ts.map +1 -1
  120. package/dist/theme/generateThemeRules.js +1 -1
  121. package/dist/theme/localTokens.d.ts +18 -0
  122. package/dist/theme/localTokens.d.ts.map +1 -0
  123. package/dist/theme/localTokens.js +162 -0
  124. package/dist/utils/interactionModality.d.ts +6 -28
  125. package/dist/utils/interactionModality.d.ts.map +1 -1
  126. package/dist/utils/interactionModality.js +72 -37
  127. package/dist/utils/sharedResizeObserver.d.ts +17 -9
  128. package/dist/utils/sharedResizeObserver.d.ts.map +1 -1
  129. package/dist/utils/sharedResizeObserver.js +54 -12
  130. package/locales/fr-FR.json +12 -0
  131. package/package.json +8 -2
  132. package/scripts/agent-doc-state.mjs +137 -0
  133. package/scripts/postinstall.mjs +28 -60
  134. package/src/AlertDialog/useImperativeAlertDialog.test.tsx +163 -0
  135. package/src/AppShell/AppShell.tsx +2 -3
  136. package/src/Calendar/hooks/useCalendarConstraints.test.ts +153 -0
  137. package/src/Calendar/hooks/useCalendarDays.test.ts +265 -0
  138. package/src/Calendar/hooks/useCalendarNavigation.test.ts +252 -0
  139. package/src/Center/Center.doc.mjs +7 -0
  140. package/src/Chat/ChatComposer.doc.mjs +7 -4
  141. package/src/Chat/ChatComposer.test.tsx +84 -2
  142. package/src/Chat/ChatComposer.tsx +45 -3
  143. package/src/Chat/ChatComposerInput.test.tsx +188 -0
  144. package/src/Chat/ChatComposerInput.tsx +26 -3
  145. package/src/Chat/ChatComposerTokenElement.doc.mjs +5 -0
  146. package/src/Chat/ChatMessage.doc.mjs +17 -0
  147. package/src/Chat/ChatMessageList.doc.mjs +33 -0
  148. package/src/Chat/ChatSendButton.test.tsx +189 -0
  149. package/src/Chat/ChatToolCalls.doc.mjs +9 -0
  150. package/src/Chat/chatComposerSelection.ts +109 -0
  151. package/src/Chat/useChatNewMessages.ts +3 -3
  152. package/src/Chat/useChatPasteAsToken.test.ts +191 -0
  153. package/src/Chat/useChatStreamScroll.test.tsx +168 -5
  154. package/src/Chat/useChatStreamScroll.ts +71 -67
  155. package/src/CheckboxInput/CheckboxInput.test.tsx +53 -0
  156. package/src/CheckboxInput/CheckboxInput.tsx +9 -20
  157. package/src/CheckboxList/CheckboxList.test.tsx +45 -0
  158. package/src/CheckboxList/CheckboxListItem.doc.mjs +11 -9
  159. package/src/CheckboxList/CheckboxListItem.tsx +5 -3
  160. package/src/Collapsible/Collapsible.tsx +10 -0
  161. package/src/Collapsible/useCollapsible.test.tsx +236 -0
  162. package/src/CommandPalette/CommandPaletteEmpty.test.tsx +101 -0
  163. package/src/ComplexSelector/ComplexSelector.spec.md +1 -1
  164. package/src/DropdownMenu/DropdownMenu.tsx +2 -4
  165. package/src/DropdownMenu/DropdownMenuRadioItem.doc.mjs +13 -0
  166. package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +31 -0
  167. package/src/Field/Field.doc.mjs +1 -0
  168. package/src/Field/Field.test.tsx +39 -0
  169. package/src/Field/Field.tsx +18 -1
  170. package/src/Field/InputClearButton.test.tsx +68 -6
  171. package/src/Field/PanelSearchInput.tsx +3 -5
  172. package/src/FieldStatus/FieldStatus.test.tsx +17 -0
  173. package/src/FieldStatus/FieldStatus.tsx +7 -2
  174. package/src/Grid/Grid.doc.mjs +11 -0
  175. package/src/Grid/GridSpan.doc.mjs +7 -0
  176. package/src/Indicator/Indicator.doc.mjs +3 -0
  177. package/src/Layer/useTouchTrigger.ts +2 -4
  178. package/src/Layout/Layout.test.tsx +64 -0
  179. package/src/Layout/LayoutHeader.doc.mjs +23 -0
  180. package/src/Layout/LayoutHeader.tsx +18 -0
  181. package/src/Layout/LayoutPanel.doc.mjs +4 -3
  182. package/src/Lightbox/useLightbox.test.tsx +263 -0
  183. package/src/Markdown/incremental.test.ts +83 -0
  184. package/src/Markdown/parser.perf.test.ts +93 -0
  185. package/src/Markdown/parser.ts +256 -55
  186. package/src/MultiSelector/MultiSelector.doc.mjs +11 -0
  187. package/src/MultiSelector/MultiSelector.spec.md +1 -1
  188. package/src/PowerSearch/PowerSearch.doc.mjs +45 -0
  189. package/src/PowerSearch/PowerSearchFilterEditor.test.tsx +253 -0
  190. package/src/PowerSearch/PowerSearchToken.test.tsx +228 -0
  191. package/src/RadioList/RadioList.test.tsx +185 -2
  192. package/src/RadioList/RadioListItem.doc.mjs +30 -11
  193. package/src/RadioList/RadioListItem.tsx +53 -28
  194. package/src/Resizable/Resizable.doc.mjs +71 -8
  195. package/src/Resizable/Resizable.spec.md +85 -34
  196. package/src/Resizable/ResizeHandle.test.tsx +78 -2
  197. package/src/Resizable/ResizeHandle.tsx +32 -2
  198. package/src/Resizable/index.ts +2 -0
  199. package/src/Resizable/shippedDirections.test.ts +462 -0
  200. package/src/Resizable/useResizable.doc.mjs +124 -29
  201. package/src/Resizable/useResizable.test.ts +1277 -3
  202. package/src/Resizable/useResizable.ts +760 -48
  203. package/src/Resizable/utils.test.ts +74 -0
  204. package/src/Resizable/utils.ts +35 -0
  205. package/src/Selector/Selector.doc.mjs +10 -0
  206. package/src/Selector/Selector.spec.md +8 -6
  207. package/src/SideNav/SideNavCollapseButton.doc.mjs +8 -0
  208. package/src/SideNav/SideNavHeading.doc.mjs +3 -0
  209. package/src/SideNav/SideNavItem.doc.mjs +3 -0
  210. package/src/SideNav/SideNavSection.doc.mjs +10 -0
  211. package/src/Slider/Slider.tsx +2 -5
  212. package/src/Stack/Stack.doc.mjs +38 -0
  213. package/src/Stepper/Step.doc.mjs +1 -1
  214. package/src/Stepper/Step.tsx +118 -5
  215. package/src/Stepper/Stepper.doc.mjs +50 -0
  216. package/src/Stepper/Stepper.spec.md +245 -0
  217. package/src/Stepper/Stepper.test.tsx +315 -0
  218. package/src/Stepper/Stepper.tsx +8 -0
  219. package/src/Switch/Switch.test.tsx +53 -0
  220. package/src/Switch/Switch.tsx +9 -20
  221. package/src/TabList/TabList.doc.mjs +10 -1
  222. package/src/TabList/TabList.tsx +53 -3
  223. package/src/TabList/TabMenu.doc.mjs +14 -0
  224. package/src/Table/Table.perf.test.tsx +51 -1
  225. package/src/Table/TableHeaderCell.test.tsx +291 -0
  226. package/src/Table/index.ts +19 -4
  227. package/src/Table/plugins/columnResize/useTableColumnResize.tsx +8 -12
  228. package/src/Table/plugins/filtering/useTableFilterState.test.tsx +143 -0
  229. package/src/Table/plugins/groupedRows/useTableGroupedRows.test.tsx +2 -1
  230. package/src/Table/plugins/rowStatus/index.ts +1 -4
  231. package/src/Table/plugins/rowStatus/useTableRowStatus.public.test.ts +92 -0
  232. package/src/Table/plugins/rowStatus/useTableRowStatus.test.tsx +413 -56
  233. package/src/Table/plugins/rowStatus/useTableRowStatus.tsx +155 -102
  234. package/src/Table/plugins/sortable/useTableSortable.test.tsx +72 -0
  235. package/src/Table/plugins/sortable/useTableSortable.tsx +18 -1
  236. package/src/Table/useTableRowStatus.doc.mjs +5 -5
  237. package/src/Text/useTruncation.ts +8 -7
  238. package/src/TextArea/TextArea.spec.md +1 -1
  239. package/src/Timestamp/Timestamp.doc.mjs +2 -2
  240. package/src/Timestamp/Timestamp.test.tsx +75 -3
  241. package/src/Timestamp/Timestamp.tsx +7 -153
  242. package/src/Timestamp/formatRelativeTime.ts +96 -0
  243. package/src/Toast/Toast.doc.mjs +4 -0
  244. package/src/Toast/ToastViewport.test.tsx +177 -0
  245. package/src/Toast/ToastViewport.tsx +126 -58
  246. package/src/Tokenizer/Tokenizer.doc.mjs +16 -0
  247. package/src/Tokenizer/Tokenizer.spec.md +5 -5
  248. package/src/Tokenizer/Tokenizer.test.tsx +335 -1
  249. package/src/Tokenizer/Tokenizer.tsx +129 -46
  250. package/src/Tokenizer/useEndLaneReserve.ts +146 -0
  251. package/src/Tooltip/Tooltip.test.tsx +21 -1
  252. package/src/Tooltip/useTooltip.test.tsx +318 -0
  253. package/src/Tooltip/useTooltip.tsx +60 -39
  254. package/src/TopNav/TopNavHeading.doc.mjs +3 -0
  255. package/src/TopNav/TopNavItem.doc.mjs +3 -0
  256. package/src/TopNav/TopNavMenu.doc.mjs +10 -0
  257. package/src/TreeList/TreeList.test.tsx +94 -0
  258. package/src/TreeList/TreeList.tsx +19 -1
  259. package/src/Typeahead/BaseTypeahead.tsx +96 -25
  260. package/src/Typeahead/Typeahead.doc.mjs +6 -0
  261. package/src/Typeahead/Typeahead.spec.md +8 -2
  262. package/src/Typeahead/Typeahead.test.tsx +502 -1
  263. package/src/Typeahead/Typeahead.tsx +187 -69
  264. package/src/Typeahead/TypeaheadItem.test.tsx +93 -2
  265. package/src/Typeahead/busyIndicatorLane.tsx +109 -0
  266. package/src/__tests__/stylexDeclarations.ts +51 -0
  267. package/src/hooks/useFocusReturnVisibility.ts +3 -5
  268. package/src/hooks/useFocusTrap.doc.mjs +2 -2
  269. package/src/hooks/useFocusTrap.test.tsx +48 -0
  270. package/src/hooks/useFocusTrap.ts +35 -1
  271. package/src/hooks/useOverflow.ts +11 -11
  272. package/src/hooks/useScrollLock.test.ts +103 -0
  273. package/src/hooks/useScrollOverflow.ts +7 -4
  274. package/src/i18n/useTranslator.test.tsx +197 -0
  275. package/src/postinstall.test.mjs +71 -5
  276. package/src/theme/defineTheme.test.ts +227 -0
  277. package/src/theme/defineTheme.ts +30 -0
  278. package/src/theme/derivedVarRegistry.test.ts +6 -0
  279. package/src/theme/generateThemeRules.ts +4 -1
  280. package/src/theme/localTokens.ts +272 -0
  281. package/src/utils/interactionModality.test.ts +83 -0
  282. package/src/utils/interactionModality.ts +88 -36
  283. package/src/utils/sharedResizeObserver.test.ts +124 -5
  284. package/src/utils/sharedResizeObserver.ts +60 -14
@@ -1 +1 @@
1
- {"version":3,"file":"AppShell.d.ts","sourceRoot":"","sources":["../../src/AppShell/AppShell.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAQL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAmBf,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAO5C,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,SAAS,CAAC;AA+BhD;;;;;;GAMG;AACH,MAAM,MAAM,kBAAkB,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,MAAM,CAAC;AAE7D;;;;;;;GAOG;AAEH;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,kBAAkB,CAAC;AAEvD;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;OAEG;IACH,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IAEzC;;;;OAIG;IACH,OAAO,CAAC,EAAE,SAAS,CAAC;IAEpB;;;OAGG;IACH,UAAU,CAAC,EAAE,kBAAkB,CAAC;IAEhC;;;;;;;;;OASG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,aAAc,SAAQ,SAAS,CAAC,cAAc,CAAC;IAC9D,wCAAwC;IACxC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,eAAe,CAAC;IAE1B;;;OAGG;IACH,MAAM,CAAC,EAAE,SAAS,CAAC;IAEnB;;OAEG;IACH,QAAQ,EAAE,SAAS,CAAC;IAEpB;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,WAAW,CAAC;IAE7B;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAEzB;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,SAAS,CAAC,EAAE,KAAK,GAAG,eAAe,GAAG,SAAS,CAAC;IAEhD;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACH,OAAO,CAAC,EAAE,SAAS,CAAC;IAEpB;;;OAGG;IACH,MAAM,CAAC,EAAE,SAAS,CAAC;CACpB;AA4KD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,QAAQ,CAAC,EACvB,OAAoB,EACpB,MAAM,EACN,QAAQ,EACR,cAAc,EACd,aAAa,EAAE,UAAU,EACzB,MAAe,EACf,SAAS,EACT,OAAO,EACP,MAAM,EACN,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,EACH,GAAG,IAAI,EACR,EAAE,aAAa,qBAkZf;yBAjae,QAAQ"}
1
+ {"version":3,"file":"AppShell.d.ts","sourceRoot":"","sources":["../../src/AppShell/AppShell.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAQL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAmBf,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAO5C,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,SAAS,CAAC;AA+BhD;;;;;;GAMG;AACH,MAAM,MAAM,kBAAkB,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,MAAM,CAAC;AAE7D;;;;;;;GAOG;AAEH;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,kBAAkB,CAAC;AAEvD;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;OAEG;IACH,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IAEzC;;;;OAIG;IACH,OAAO,CAAC,EAAE,SAAS,CAAC;IAEpB;;;OAGG;IACH,UAAU,CAAC,EAAE,kBAAkB,CAAC;IAEhC;;;;;;;;;OASG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,aAAc,SAAQ,SAAS,CAAC,cAAc,CAAC;IAC9D,wCAAwC;IACxC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,eAAe,CAAC;IAE1B;;;OAGG;IACH,MAAM,CAAC,EAAE,SAAS,CAAC;IAEnB;;OAEG;IACH,QAAQ,EAAE,SAAS,CAAC;IAEpB;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,WAAW,CAAC;IAE7B;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAEzB;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,SAAS,CAAC,EAAE,KAAK,GAAG,eAAe,GAAG,SAAS,CAAC;IAEhD;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACH,OAAO,CAAC,EAAE,SAAS,CAAC;IAEpB;;;OAGG;IACH,MAAM,CAAC,EAAE,SAAS,CAAC;CACpB;AA4KD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,QAAQ,CAAC,EACvB,OAAoB,EACpB,MAAM,EACN,QAAQ,EACR,cAAc,EACd,aAAa,EAAE,UAAU,EACzB,MAAe,EACf,SAAS,EACT,OAAO,EACP,MAAM,EACN,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,EACH,GAAG,IAAI,EACR,EAAE,aAAa,qBAiZf;yBAhae,QAAQ"}
@@ -36,7 +36,7 @@ import { AppShellMobileContext } from "./AppShellMobileContext.js";
36
36
  import { mergeProps, isRenderable } from "../utils/index.js";
37
37
  import { focusOutlineProps } from "../utils/focusOutline.stylex.js";
38
38
  import { useMediaQuery } from "../hooks/useMediaQuery.js";
39
- import { observeResize, unobserveResize } from "../utils/sharedResizeObserver.js";
39
+ import { observeResize } from "../utils/sharedResizeObserver.js";
40
40
  import { themeProps } from "../utils/themeProps.js";
41
41
  import { useTranslator } from "../i18n/index.js";
42
42
  import { useMergedRefs } from "../hooks/useMergedRefs.js";
@@ -320,8 +320,7 @@ export function AppShell({
320
320
  const height = headerEl.getBoundingClientRect().height;
321
321
  shellEl.style.setProperty('--_app-shell-header-height', `${height}px`);
322
322
  };
323
- observeResize(headerEl, () => updateHeight());
324
- return () => unobserveResize(headerEl);
323
+ return observeResize(headerEl, () => updateHeight());
325
324
  }, [isAuto]);
326
325
 
327
326
  // =========================================================================
@@ -46,8 +46,8 @@ export interface ChatComposerProps extends Omit<BaseProps<HTMLDivElement>, 'onCh
46
46
  /**
47
47
  * Resting elevation of the composer body. `low` (the default) keeps today's
48
48
  * raised look — low at rest, bumping to med on hover / focus. `none` flattens
49
- * it and draws a border with the same rest / hover / focus treatment as a
50
- * text input (emphasized border → accent on focus, with matching inset rings).
49
+ * it and draws a border. Keyboard focus in the editor adds the shared focus
50
+ * ring in either presentation; pointer focus does not add that ring.
51
51
  * @default 'low'
52
52
  */
53
53
  elevation?: 'none' | 'low';
@@ -1 +1 @@
1
- {"version":3,"file":"ChatComposer.d.ts","sourceRoot":"","sources":["../../src/Chat/ChatComposer.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,EAKZ,KAAK,SAAS,EAEf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AA0B5C,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,OAAO,GAAG,SAAS,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,CAAC;AAEtE,MAAM,WAAW,iBAAkB,SAAQ,IAAI,CAC7C,SAAS,CAAC,cAAc,CAAC,EACzB,UAAU,GAAG,UAAU,CACxB;IACC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC,+CAA+C;IAC/C,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,kDAAkD;IAClD,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,kFAAkF;IAClF,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,oCAAoC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0CAA0C;IAC1C,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,qCAAqC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uCAAuC;IACvC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,sBAAsB;IACtB,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAI3B,6GAA6G;IAC7G,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,yHAAyH;IACzH,aAAa,CAAC,EAAE,SAAS,CAAC;IAC1B,yGAAyG;IACzG,aAAa,CAAC,EAAE,SAAS,CAAC;IAC1B,2DAA2D;IAC3D,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,gHAAgH;IAChH,aAAa,CAAC,EAAE,SAAS,CAAC;IAC1B,gHAAgH;IAChH,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,gDAAgD;IAChD,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB,iEAAiE;IACjE,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B,oDAAoD;IACpD,cAAc,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;CACnC;AAoMD;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,qBAiLpD;yBAjLe,YAAY"}
1
+ {"version":3,"file":"ChatComposer.d.ts","sourceRoot":"","sources":["../../src/Chat/ChatComposer.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,EAKZ,KAAK,SAAS,EAGf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AA+B5C,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,OAAO,GAAG,SAAS,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,CAAC;AAEtE,MAAM,WAAW,iBAAkB,SAAQ,IAAI,CAC7C,SAAS,CAAC,cAAc,CAAC,EACzB,UAAU,GAAG,UAAU,CACxB;IACC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC,+CAA+C;IAC/C,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,kDAAkD;IAClD,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,kFAAkF;IAClF,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,oCAAoC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0CAA0C;IAC1C,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,qCAAqC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uCAAuC;IACvC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,sBAAsB;IACtB,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAI3B,6GAA6G;IAC7G,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,yHAAyH;IACzH,aAAa,CAAC,EAAE,SAAS,CAAC;IAC1B,yGAAyG;IACzG,aAAa,CAAC,EAAE,SAAS,CAAC;IAC1B,2DAA2D;IAC3D,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,gHAAgH;IAChH,aAAa,CAAC,EAAE,SAAS,CAAC;IAC1B,gHAAgH;IAChH,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,gDAAgD;IAChD,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB,iEAAiE;IACjE,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B,oDAAoD;IACpD,cAAc,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;CACnC;AA8MD;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,qBA2MpD;yBA3Me,YAAY"}
@@ -33,6 +33,8 @@ import { ChatComposerContext } from "./ChatContext.js";
33
33
  import { ChatSendButton } from "./ChatSendButton.js";
34
34
  import { themeProps } from "../utils/themeProps.js";
35
35
  import { useTranslator } from "../i18n/index.js";
36
+ import { focusOutlineStyles } from "../utils/focusOutline.stylex.js";
37
+ import { getInteractionModality, useInteractionModalityTracking } from "../utils/interactionModality.js";
36
38
 
37
39
  // =============================================================================
38
40
  // Types
@@ -65,7 +67,8 @@ const styles = {
65
67
 
66
68
  // Resting elevation for the composer body, narrowed to two steps.
67
69
  // - `low` (default) preserves today's look: low at rest, bumping to med on
68
- // hover / focus-within (a CSS-only interaction state the component owns).
70
+ // hover / focus-within. The WCAG focus indicator is a separate shared ring,
71
+ // gated to keyboard focus in the editor; the shadow change remains depth.
69
72
  // - `none` is flat; depth comes from a border + inset rings instead. The
70
73
  // border is drawn *inside* the padding — its width is subtracted from every
71
74
  // side (like Card) so total inset (border + padding) equals the elevated
@@ -88,6 +91,9 @@ const elevationStyles = {
88
91
  $$css: true
89
92
  }
90
93
  };
94
+ function isComposerEditor(target) {
95
+ return target instanceof HTMLElement && target.matches('textarea, [contenteditable="true"], [role="textbox"], input:not([type]), input[type="text"], input[type="search"], input[type="email"], input[type="url"], input[type="tel"], input[type="password"]');
96
+ }
91
97
 
92
98
  // =============================================================================
93
99
  // Component
@@ -134,6 +140,8 @@ export function ChatComposer(props) {
134
140
  } = props;
135
141
  const placeholder = placeholderFromProps ?? t('@astryx.chat.composer.placeholder');
136
142
  const [internalValue, setInternalValue] = useState('');
143
+ const [hasKeyboardEditorFocus, setHasKeyboardEditorFocus] = useState(false);
144
+ useInteractionModalityTracking();
137
145
  const isControlled = controlledValue !== undefined;
138
146
  const currentValue = isControlled ? controlledValue : internalValue;
139
147
  const updateValue = useCallback(newValue => {
@@ -175,6 +183,14 @@ export function ChatComposer(props) {
175
183
  const editable = bodyRef.current?.querySelector('[contenteditable="true"], textarea');
176
184
  editable?.focus();
177
185
  }, []);
186
+ const handleBodyFocusCapture = useCallback(event => {
187
+ setHasKeyboardEditorFocus(isComposerEditor(event.target) && getInteractionModality() === 'keyboard');
188
+ }, []);
189
+ const handleBodyBlurCapture = useCallback(event => {
190
+ if (!event.currentTarget.contains(event.relatedTarget)) {
191
+ setHasKeyboardEditorFocus(false);
192
+ }
193
+ }, []);
178
194
  const statusEl = status ? /*#__PURE__*/_jsxs("div", {
179
195
  role: status.type === 'error' ? 'alert' : 'status',
180
196
  ...{
@@ -238,7 +254,10 @@ export function ChatComposer(props) {
238
254
  children: [statusPosition === 'top' && statusEl, drawer, /*#__PURE__*/_jsxs("div", {
239
255
  ref: bodyRef,
240
256
  onClick: handleBodyClick,
241
- ...stylex.props(styles.body, elevationStyles[elevation], density === 'compact' && styles.compact, xstyle),
257
+ onPointerDownCapture: () => setHasKeyboardEditorFocus(false),
258
+ onFocusCapture: handleBodyFocusCapture,
259
+ onBlurCapture: handleBodyBlurCapture,
260
+ ...stylex.props(styles.body, elevationStyles[elevation], hasKeyboardEditorFocus && focusOutlineStyles.focusWithin, density === 'compact' && styles.compact, xstyle),
242
261
  children: [(headerActions || headerContext) && /*#__PURE__*/_jsxs("div", {
243
262
  ...{
244
263
  className: "x78zum5 x6s0dn4 x1qughib x1txdalj xe0p6wg"
@@ -1 +1 @@
1
- {"version":3,"file":"ChatComposerInput.d.ts","sourceRoot":"","sources":["../../src/Chat/ChatComposerInput.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ChatComposerInput.d.ts","sourceRoot":"","sources":["../../src/Chat/ChatComposerInput.tsx"],"names":[],"mappings":"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"}
@@ -30,7 +30,7 @@ import { colorVars, spacingVars, typeScaleVars, typographyVars } from "../theme/
30
30
  import { mergeProps, isImeKeyEvent } from "../utils/index.js";
31
31
  import { useTriggerMenu } from "./useTriggerMenu.js";
32
32
  import { useChatComposerTokens, isCustomToken } from "./useChatComposerTokens.js";
33
- import { ensureCaretInside, insertTextAtCursor } from "./chatComposerSelection.js";
33
+ import { ensureCaretInside, insertTextAtCursor, isSelectionAtStart, isSelectionAtEnd } from "./chatComposerSelection.js";
34
34
  import { ChatPastedTextToken } from "./ChatPastedTextToken.js";
35
35
  import { useChatPasteAsToken } from "./useChatPasteAsToken.js";
36
36
  import { Badge } from "../Badge/index.js";
@@ -373,12 +373,29 @@ export function ChatComposerInput(props) {
373
373
  return;
374
374
  }
375
375
 
376
- // History navigation (only when trigger menu is not active)
376
+ // History navigation (only when trigger menu is not active).
377
+ // Recall only at the text boundaries so the caret can still move
378
+ // between lines in a multi-line draft: ArrowUp recalls the
379
+ // previous message when the caret is at the very start, ArrowDown
380
+ // steps forward when it's at the very end. A recalled message is
381
+ // shown fully selected (see `selectAll` below); that spans both
382
+ // boundaries at once, so repeated presses keep stepping through
383
+ // history. Mid-text, we bail before `preventDefault` and let the
384
+ // browser move the caret up/down a line.
377
385
  if (hasHistory && (e.key === 'ArrowUp' || e.key === 'ArrowDown')) {
378
386
  if (!editableRef.current) {
379
387
  return;
380
388
  }
381
- const text = serialize(editableRef.current);
389
+ const editable = editableRef.current;
390
+ const isCollapsed = window.getSelection()?.isCollapsed ?? true;
391
+ const atStart = isSelectionAtStart(editable);
392
+ const atEnd = isSelectionAtEnd(editable);
393
+ const canRecallPrev = atStart && (isCollapsed || atEnd);
394
+ const canRecallNext = atEnd && (isCollapsed || atStart);
395
+ if (e.key === 'ArrowUp' ? !canRecallPrev : !canRecallNext) {
396
+ return;
397
+ }
398
+ const text = serialize(editable);
382
399
  const history = historyRef.current;
383
400
  if (history.length === 0) {
384
401
  return;
@@ -32,6 +32,31 @@
32
32
  * support).
33
33
  */
34
34
  export declare function ensureCaretInside(editable: HTMLElement): Selection | null;
35
+ /**
36
+ * Whether the current selection's start boundary sits at the very
37
+ * beginning of `editable`'s content.
38
+ *
39
+ * Used by the composer to decide when ArrowUp should recall message
40
+ * history versus move the caret up a line: history is only recalled
41
+ * when the caret is at the very start of the draft. This handles both
42
+ * the editable root and offset 0 of its leading text node without
43
+ * cloning the draft's contents.
44
+ *
45
+ * Returns `false` when there is no selection, the range is not inside
46
+ * `editable`, or the boundary APIs are unavailable — callers treat
47
+ * that as "not at the boundary" and let the browser move the caret.
48
+ */
49
+ export declare function isSelectionAtStart(editable: HTMLElement): boolean;
50
+ /**
51
+ * Whether the current selection's end boundary sits at the very end of
52
+ * `editable`'s content.
53
+ *
54
+ * The ArrowDown counterpart to {@link isSelectionAtStart}: history is
55
+ * only stepped forward when the caret is at the very end of the draft.
56
+ *
57
+ * Returns `false` under the same conditions as {@link isSelectionAtStart}.
58
+ */
59
+ export declare function isSelectionAtEnd(editable: HTMLElement): boolean;
35
60
  /**
36
61
  * Insert plain text at the current selection, scoped to `editable`.
37
62
  *
@@ -1 +1 @@
1
- {"version":3,"file":"chatComposerSelection.d.ts","sourceRoot":"","sources":["../../src/Chat/chatComposerSelection.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,WAAW,GAAG,SAAS,GAAG,IAAI,CAmBzE;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,WAAW,EACrB,IAAI,EAAE,MAAM,GACX,OAAO,CAiBT"}
1
+ {"version":3,"file":"chatComposerSelection.d.ts","sourceRoot":"","sources":["../../src/Chat/chatComposerSelection.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,WAAW,GAAG,SAAS,GAAG,IAAI,CAmBzE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,WAAW,GAAG,OAAO,CAYjE;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,WAAW,GAAG,OAAO,CAO/D;AAiED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,WAAW,EACrB,IAAI,EAAE,MAAM,GACX,OAAO,CAiBT"}
@@ -55,6 +55,84 @@ export function ensureCaretInside(editable) {
55
55
  return selection;
56
56
  }
57
57
 
58
+ /**
59
+ * Whether the current selection's start boundary sits at the very
60
+ * beginning of `editable`'s content.
61
+ *
62
+ * Used by the composer to decide when ArrowUp should recall message
63
+ * history versus move the caret up a line: history is only recalled
64
+ * when the caret is at the very start of the draft. This handles both
65
+ * the editable root and offset 0 of its leading text node without
66
+ * cloning the draft's contents.
67
+ *
68
+ * Returns `false` when there is no selection, the range is not inside
69
+ * `editable`, or the boundary APIs are unavailable — callers treat
70
+ * that as "not at the boundary" and let the browser move the caret.
71
+ */
72
+ export function isSelectionAtStart(editable) {
73
+ const selection = window.getSelection();
74
+ if (!selection || selection.rangeCount === 0) {
75
+ return false;
76
+ }
77
+ const range = selection.getRangeAt(0);
78
+ return isBoundaryAtEdge(editable, range.startContainer, range.startOffset, 'start');
79
+ }
80
+
81
+ /**
82
+ * Whether the current selection's end boundary sits at the very end of
83
+ * `editable`'s content.
84
+ *
85
+ * The ArrowDown counterpart to {@link isSelectionAtStart}: history is
86
+ * only stepped forward when the caret is at the very end of the draft.
87
+ *
88
+ * Returns `false` under the same conditions as {@link isSelectionAtStart}.
89
+ */
90
+ export function isSelectionAtEnd(editable) {
91
+ const selection = window.getSelection();
92
+ if (!selection || selection.rangeCount === 0) {
93
+ return false;
94
+ }
95
+ const range = selection.getRangeAt(0);
96
+ return isBoundaryAtEdge(editable, range.endContainer, range.endOffset, 'end');
97
+ }
98
+
99
+ /**
100
+ * Check whether a Range boundary is at an edge of `editable` without
101
+ * materializing the content before or after it. Chromium represents a caret
102
+ * at the start of non-empty contenteditables as the first text node at offset
103
+ * zero (and likewise uses the last text node's length at the end). Those are
104
+ * distinct DOM boundary points from the editable's child offsets, so
105
+ * `compareBoundaryPoints` cannot be used here.
106
+ */
107
+ function isBoundaryAtEdge(editable, container, offset, edge) {
108
+ if (!editable.contains(container) || !isAtNodeEdge(container, offset, edge)) {
109
+ return false;
110
+ }
111
+ for (let node = container; node && node !== editable;) {
112
+ if (hasContentSibling(node, edge)) {
113
+ return false;
114
+ }
115
+ node = node.parentNode;
116
+ }
117
+ return true;
118
+ }
119
+ function isAtNodeEdge(node, offset, edge) {
120
+ if (node.nodeType === Node.TEXT_NODE || node.nodeType === Node.CDATA_SECTION_NODE) {
121
+ return edge === 'start' ? offset === 0 : offset === (node.nodeValue?.length ?? 0);
122
+ }
123
+ return edge === 'start' ? offset === 0 : offset === node.childNodes.length;
124
+ }
125
+ function hasContentSibling(node, edge) {
126
+ for (let sibling = edge === 'start' ? node.previousSibling : node.nextSibling; sibling; sibling = edge === 'start' ? sibling.previousSibling : sibling.nextSibling) {
127
+ // Elements (including <br> and tokens) represent a content boundary even
128
+ // when they have no text. Empty text nodes and comments do not.
129
+ if (sibling.nodeType === Node.ELEMENT_NODE || sibling.nodeType === Node.TEXT_NODE && sibling.nodeValue !== '') {
130
+ return true;
131
+ }
132
+ }
133
+ return false;
134
+ }
135
+
58
136
  /**
59
137
  * Insert plain text at the current selection, scoped to `editable`.
60
138
  *
@@ -23,7 +23,7 @@
23
23
  * - /packages/core/src/Chat/index.ts (exports)
24
24
  */
25
25
  import { useCallback, useEffect, useRef, useState } from 'react';
26
- import { observeResize, unobserveResize } from "../utils/sharedResizeObserver.js";
26
+ import { observeResize } from "../utils/sharedResizeObserver.js";
27
27
 
28
28
  // =============================================================================
29
29
  // Types
@@ -49,7 +49,7 @@ export function useChatNewMessages({
49
49
  const elementRef = useRef(null);
50
50
  const cleanupRef = useRef(null);
51
51
  const attach = useCallback(el => {
52
- observeResize(el, () => {
52
+ const unobserve = observeResize(el, () => {
53
53
  onResizeRef.current?.();
54
54
  const messages = el.getElementsByClassName('astryx-chat-message');
55
55
  const last = messages.length > 0 ? messages[messages.length - 1] : null;
@@ -60,7 +60,7 @@ export function useChatNewMessages({
60
60
  }
61
61
  }
62
62
  });
63
- cleanupRef.current = () => unobserveResize(el);
63
+ cleanupRef.current = unobserve;
64
64
  }, []);
65
65
  const detach = useCallback(() => {
66
66
  cleanupRef.current?.();
@@ -1 +1 @@
1
- {"version":3,"file":"useChatStreamScroll.d.ts","sourceRoot":"","sources":["../../src/Chat/useChatStreamScroll.ts"],"names":[],"mappings":"AAoCA,MAAM,WAAW,yBAAyB;IACxC;;;;;;;;OAQG;IACH,QAAQ,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;CACjC;AAED,MAAM,WAAW,0BAA0B;IACzC;;OAEG;IACH,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAE/C;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,yBAAyB;IACxC,6DAA6D;IAC7D,YAAY,EAAE,OAAO,CAAC;IAEtB,yDAAyD;IACzD,QAAQ,EAAE,OAAO,CAAC;IAElB,yDAAyD;IACzD,cAAc,EAAE,CAAC,OAAO,CAAC,EAAE,yBAAyB,KAAK,IAAI,CAAC;IAE9D,sFAAsF;IACtF,eAAe,EAAE,CAAC,EAAE,EAAE,WAAW,KAAK,IAAI,CAAC;IAE3C,6CAA6C;IAC7C,IAAI,EAAE,MAAM,IAAI,CAAC;IAEjB,0BAA0B;IAC1B,MAAM,EAAE,MAAM,IAAI,CAAC;IAEnB,oEAAoE;IACpE,cAAc,EAAE,MAAM,IAAI,CAAC;IAE3B,mDAAmD;IACnD,mBAAmB,EAAE,MAAM,IAAI,CAAC;CACjC;AAQD,wBAAgB,mBAAmB,CAAC,EAClC,SAAS,EACT,OAAc,EACd,aAAkB,EAClB,eAAqB,EACrB,OAAa,EACb,SAAgB,EAChB,IAAW,GACZ,EAAE,0BAA0B,GAAG,yBAAyB,CAyRxD"}
1
+ {"version":3,"file":"useChatStreamScroll.d.ts","sourceRoot":"","sources":["../../src/Chat/useChatStreamScroll.ts"],"names":[],"mappings":"AAsCA,MAAM,WAAW,yBAAyB;IACxC;;;;;;;;OAQG;IACH,QAAQ,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;CACjC;AAED,MAAM,WAAW,0BAA0B;IACzC;;OAEG;IACH,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAE/C;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,yBAAyB;IACxC,6DAA6D;IAC7D,YAAY,EAAE,OAAO,CAAC;IAEtB,yDAAyD;IACzD,QAAQ,EAAE,OAAO,CAAC;IAElB,yDAAyD;IACzD,cAAc,EAAE,CAAC,OAAO,CAAC,EAAE,yBAAyB,KAAK,IAAI,CAAC;IAE9D,sFAAsF;IACtF,eAAe,EAAE,CAAC,EAAE,EAAE,WAAW,KAAK,IAAI,CAAC;IAE3C,6CAA6C;IAC7C,IAAI,EAAE,MAAM,IAAI,CAAC;IAEjB,0BAA0B;IAC1B,MAAM,EAAE,MAAM,IAAI,CAAC;IAEnB,oEAAoE;IACpE,cAAc,EAAE,MAAM,IAAI,CAAC;IAE3B,mDAAmD;IACnD,mBAAmB,EAAE,MAAM,IAAI,CAAC;CACjC;AA8BD,wBAAgB,mBAAmB,CAAC,EAClC,SAAS,EACT,OAAc,EACd,aAAkB,EAClB,eAAqB,EACrB,OAAa,EACb,SAAgB,EAChB,IAAW,GACZ,EAAE,0BAA0B,GAAG,yBAAyB,CAqQxD"}
@@ -18,10 +18,12 @@
18
18
  * - Under `prefers-reduced-motion`, every spring path falls back to the
19
19
  * same instant jump — following still works, it just doesn't animate
20
20
  *
21
- * Uses scroll direction (lastScrollTop comparison) to detect user
22
- * intent — works for wheel, touch, scrollbar drag, keyboard, everything.
23
- * Tracks scrollHeight/offsetHeight changes to ignore Chrome synthetic
24
- * scroll events caused by content resize.
21
+ * Reads user intent from position: a scroll that lands above the last
22
+ * position this hook set or saw is the reader — wheel, touch, scrollbar
23
+ * drag, keyboard, everything, including a move the spring's next frame
24
+ * would otherwise outrun. While following, the hook owns the position:
25
+ * it disables scroll anchoring on the container, so the only move the
26
+ * browser makes on its own is a resize clamp onto the bottom.
25
27
  *
26
28
  * SYNC: When modified, update:
27
29
  * - /packages/core/src/Chat/index.ts (exports)
@@ -38,6 +40,24 @@ import { useMediaQuery } from "../hooks/useMediaQuery.js";
38
40
  // =============================================================================
39
41
 
40
42
  const SIXTY_FPS_MS = 1000 / 60;
43
+
44
+ // While following, the hook owns the container's position, so the browser's
45
+ // scroll anchoring must not move it. The rule rides on an attribute rather
46
+ // than the element's inline style: a consumer's own `overflow-anchor` is never
47
+ // read, written or restored — it simply applies again once the attribute is
48
+ // gone. `!important` so an inline `auto` cannot switch anchoring back on under
49
+ // a following container; an inline `!important` still wins, deliberately.
50
+ const FOLLOWING_ATTR = 'data-astryx-chat-following';
51
+ const FOLLOWING_STYLE_ATTR = 'data-astryx-chat-following-style';
52
+ function ensureFollowingStyle() {
53
+ if (typeof document === 'undefined' || document.head.querySelector(`[${FOLLOWING_STYLE_ATTR}]`)) {
54
+ return;
55
+ }
56
+ const style = document.createElement('style');
57
+ style.setAttribute(FOLLOWING_STYLE_ATTR, '');
58
+ style.textContent = `[${FOLLOWING_ATTR}]{overflow-anchor:none !important}`;
59
+ document.head.appendChild(style);
60
+ }
41
61
  export function useChatStreamScroll({
42
62
  scrollRef,
43
63
  enabled = true,
@@ -56,12 +76,24 @@ export function useChatStreamScroll({
56
76
  const velocityRef = useRef(0);
57
77
  const animatingRef = useRef(false);
58
78
  const lastTickRef = useRef(undefined);
59
-
60
- // For scroll direction detection
79
+ // The spring's pending frame, so a jump can cancel it for real.
80
+ const rafRef = useRef(0);
81
+ // The last position this hook set or saw. Updated by every write of ours
82
+ // (spring frame, jump, scrollToMessage) as well as by every scroll event,
83
+ // so a reader's move that lands between two spring frames still shows as
84
+ // "above where we put it" even when the next frame's write outran it.
61
85
  const lastScrollTopRef = useRef(0);
62
- // For synthetic scroll detection
63
- const lastScrollHeightRef = useRef(0);
64
- const lastOffsetHeightRef = useRef(0);
86
+
87
+ // While following, this hook is the only thing that should move the
88
+ // container: scroll anchoring is off, so the browser's one remaining move
89
+ // is the resize clamp onto the bottom, and every other upward move is the
90
+ // reader. Unlocked, the element's own anchoring applies again — a reader up
91
+ // in the history wants it when content above them changes.
92
+ const setLocked = useCallback(locked => {
93
+ lockedRef.current = locked;
94
+ setIsLocked(locked);
95
+ scrollRef.current?.toggleAttribute(FOLLOWING_ATTR, locked);
96
+ }, [scrollRef]);
65
97
 
66
98
  // --- Spring animation ---
67
99
 
@@ -94,7 +126,8 @@ export function useChatStreamScroll({
94
126
  lastTickRef.current = now;
95
127
  velocityRef.current = (damping * velocityRef.current + stiffness * diff) / mass;
96
128
  el.scrollTop += velocityRef.current * tickDelta;
97
- requestAnimationFrame(animate);
129
+ lastScrollTopRef.current = el.scrollTop;
130
+ rafRef.current = requestAnimationFrame(animate);
98
131
  }, [scrollRef, damping, stiffness, mass]);
99
132
 
100
133
  // Jump to the bottom in a single frame — cancels any in-flight spring so
@@ -104,6 +137,7 @@ export function useChatStreamScroll({
104
137
  if (!el) {
105
138
  return;
106
139
  }
140
+ cancelAnimationFrame(rafRef.current);
107
141
  animatingRef.current = false;
108
142
  velocityRef.current = 0;
109
143
  lastTickRef.current = undefined;
@@ -125,15 +159,14 @@ export function useChatStreamScroll({
125
159
  if (!animatingRef.current) {
126
160
  animatingRef.current = true;
127
161
  lastTickRef.current = undefined;
128
- requestAnimationFrame(animate);
162
+ rafRef.current = requestAnimationFrame(animate);
129
163
  }
130
164
  }, [animate, jumpToBottom, prefersReducedMotion]);
131
165
 
132
166
  // --- Public API ---
133
167
 
134
168
  const scrollToBottom = useCallback(options => {
135
- lockedRef.current = true;
136
- setIsLocked(true);
169
+ setLocked(true);
137
170
  setIsScrolledUp(false);
138
171
  initialFillPendingRef.current = false;
139
172
  if (options?.behavior === 'instant') {
@@ -141,7 +174,7 @@ export function useChatStreamScroll({
141
174
  return;
142
175
  }
143
176
  startAnimation();
144
- }, [startAnimation, jumpToBottom]);
177
+ }, [setLocked, startAnimation, jumpToBottom]);
145
178
  const scrollToMessage = useCallback(el => {
146
179
  const container = scrollRef.current;
147
180
  if (!container) {
@@ -168,16 +201,14 @@ export function useChatStreamScroll({
168
201
  }
169
202
  }, [scrollRef, scrollToMessage]);
170
203
  const lock = useCallback(() => {
171
- lockedRef.current = true;
172
- setIsLocked(true);
204
+ setLocked(true);
173
205
  setIsScrolledUp(false);
174
206
  startAnimation();
175
- }, [startAnimation]);
207
+ }, [setLocked, startAnimation]);
176
208
  const unlock = useCallback(() => {
177
- lockedRef.current = false;
209
+ setLocked(false);
178
210
  animatingRef.current = false;
179
- setIsLocked(false);
180
- }, []);
211
+ }, [setLocked]);
181
212
  const scrollIfLocked = useCallback(() => {
182
213
  if (!enabled) {
183
214
  return;
@@ -205,11 +236,9 @@ export function useChatStreamScroll({
205
236
  if (!el || !enabled) {
206
237
  return;
207
238
  }
208
-
209
- // Initialize tracking values
210
239
  lastScrollTopRef.current = el.scrollTop;
211
- lastScrollHeightRef.current = el.scrollHeight;
212
- lastOffsetHeightRef.current = el.offsetHeight;
240
+ ensureFollowingStyle();
241
+ el.toggleAttribute(FOLLOWING_ATTR, lockedRef.current);
213
242
  const onScroll = () => {
214
243
  const {
215
244
  scrollTop,
@@ -220,65 +249,29 @@ export function useChatStreamScroll({
220
249
 
221
250
  // Button visibility
222
251
  setIsScrolledUp(dist > buttonThreshold);
223
-
224
- // Detect synthetic scroll — Chrome fires scroll events when
225
- // scrollHeight or offsetHeight changes (content resize, keyboard).
226
- const scrollHeightChanged = scrollHeight !== lastScrollHeightRef.current;
227
- const offsetHeightChanged = offsetHeight !== lastOffsetHeightRef.current;
228
- lastScrollHeightRef.current = scrollHeight;
229
- lastOffsetHeightRef.current = offsetHeight;
230
- if (scrollHeightChanged || offsetHeightChanged) {
231
- // Synthetic scroll from resize — don't change lock state
232
- lastScrollTopRef.current = scrollTop;
233
- return;
234
- }
235
-
236
- // Scroll direction — unlock on scroll up
237
252
  const isScrollingUp = scrollTop < lastScrollTopRef.current;
238
253
  lastScrollTopRef.current = scrollTop;
239
- if (isScrollingUp && lockedRef.current) {
240
- lockedRef.current = false;
254
+
255
+ // With anchoring off, the browser only ever moves a following
256
+ // container up to clamp it onto a new, smaller bottom, and a reader
257
+ // scrolling up never lands there. (clientHeight, not dist: offsetHeight
258
+ // includes borders and a horizontal scrollbar.)
259
+ const landedAtBottom = scrollHeight - scrollTop - el.clientHeight < 1;
260
+ if (isScrollingUp && !landedAtBottom && lockedRef.current) {
261
+ setLocked(false);
241
262
  animatingRef.current = false;
242
- setIsLocked(false);
243
263
  }
244
264
  };
245
265
  const onScrollEnd = () => {
246
266
  const dist = el.scrollHeight - el.scrollTop - el.offsetHeight;
247
267
  if (dist <= lockThreshold) {
248
- lockedRef.current = true;
249
- setIsLocked(true);
250
- }
251
- };
252
-
253
- // Wheel up while animating — interrupt immediately.
254
- // onScroll direction detection covers most cases, but wheel fires
255
- // before the scroll position updates so we can react faster.
256
- const onWheel = e => {
257
- if (e.deltaY < 0 && animatingRef.current) {
258
- lockedRef.current = false;
259
- animatingRef.current = false;
260
- setIsLocked(false);
261
- }
262
- };
263
-
264
- // Touch move — user is dragging, take control
265
- const onTouchMove = () => {
266
- if (animatingRef.current) {
267
- lockedRef.current = false;
268
- animatingRef.current = false;
269
- setIsLocked(false);
268
+ setLocked(true);
270
269
  }
271
270
  };
272
271
  el.addEventListener('scroll', onScroll, {
273
272
  passive: true
274
273
  });
275
274
  el.addEventListener('scrollend', onScrollEnd);
276
- el.addEventListener('wheel', onWheel, {
277
- passive: true
278
- });
279
- el.addEventListener('touchmove', onTouchMove, {
280
- passive: true
281
- });
282
275
 
283
276
  // Initial scroll to bottom — content already present at mount.
284
277
  requestAnimationFrame(() => {
@@ -291,10 +284,9 @@ export function useChatStreamScroll({
291
284
  return () => {
292
285
  el.removeEventListener('scroll', onScroll);
293
286
  el.removeEventListener('scrollend', onScrollEnd);
294
- el.removeEventListener('wheel', onWheel);
295
- el.removeEventListener('touchmove', onTouchMove);
287
+ el.removeAttribute(FOLLOWING_ATTR);
296
288
  };
297
- }, [scrollRef, enabled, lockThreshold, buttonThreshold]);
289
+ }, [scrollRef, enabled, lockThreshold, buttonThreshold, setLocked]);
298
290
  return {
299
291
  isScrolledUp,
300
292
  isLocked,
@@ -1 +1 @@
1
- {"version":3,"file":"CheckboxInput.d.ts","sourceRoot":"","sources":["../../src/CheckboxInput/CheckboxInput.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAOL,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,MAAM,MAAM,kBAAkB,CAAC;AAS3C,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,gBAAgB,CAAC;AAG9C,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,SAAS,CAAC;AACtC,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,gBAAgB,CAAC;AAmFhD,QAAA,MAAM,iBAAiB;;;;;;;;;EASrB,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG,MAAM,OAAO,iBAAiB,CAAC;AAE/D,MAAM,WAAW,kBAAmB,SAAQ,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC;IACrE,wDAAwD;IACxD,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAClC;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;OAEG;IACH,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,WAAW,CAAC,gBAAgB,CAAC,KAAK,IAAI,CAAC;IACxE;;OAEG;IACH,YAAY,CAAC,EAAE,CACb,OAAO,EAAE,OAAO,EAChB,CAAC,EAAE,WAAW,CAAC,gBAAgB,CAAC,KAC7B,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;OAEG;IACH,KAAK,EAAE,OAAO,GAAG,eAAe,CAAC;IACjC;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;;;;;;;;;;;;;;OAmBG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;OAIG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB;;;;;OAKG;IACH,IAAI,CAAC,EAAE,iBAAiB,CAAC;IACzB;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,UAAU,CAAC,gBAAgB,CAAC,KAAK,IAAI,CAAC;IACpD;;OAEG;IACH,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,UAAU,CAAC,gBAAgB,CAAC,KAAK,IAAI,CAAC;IACnD;;OAEG;IACH,SAAS,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;IACjC;;;OAGG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAOD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,aAAqB,EACrB,WAAW,EACX,QAAQ,EACR,YAAY,EACZ,SAAiB,EACjB,KAAK,EACL,UAAkB,EAClB,QAAQ,EACR,eAAe,EACf,UAAkB,EAClB,UAAkB,EAClB,UAAkB,EAClB,IAAW,EACX,OAAO,EACP,MAAM,EACN,SAAS,EACT,MAAM,EACN,KAAK,EACL,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,EACH,GAAG,IAAI,EACR,EAAE,kBAAkB,+BA6MpB;yBAtOe,aAAa"}
1
+ {"version":3,"file":"CheckboxInput.d.ts","sourceRoot":"","sources":["../../src/CheckboxInput/CheckboxInput.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAOL,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,MAAM,MAAM,kBAAkB,CAAC;AAS3C,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,gBAAgB,CAAC;AAG9C,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,SAAS,CAAC;AACtC,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,gBAAgB,CAAC;AAsEhD,QAAA,MAAM,iBAAiB;;;;;;;;;EASrB,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG,MAAM,OAAO,iBAAiB,CAAC;AAE/D,MAAM,WAAW,kBAAmB,SAAQ,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC;IACrE,wDAAwD;IACxD,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAClC;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;OAEG;IACH,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,WAAW,CAAC,gBAAgB,CAAC,KAAK,IAAI,CAAC;IACxE;;OAEG;IACH,YAAY,CAAC,EAAE,CACb,OAAO,EAAE,OAAO,EAChB,CAAC,EAAE,WAAW,CAAC,gBAAgB,CAAC,KAC7B,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;OAEG;IACH,KAAK,EAAE,OAAO,GAAG,eAAe,CAAC;IACjC;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;;;;;;;;;;;;;;OAmBG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;OAIG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB;;;;;OAKG;IACH,IAAI,CAAC,EAAE,iBAAiB,CAAC;IACzB;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,UAAU,CAAC,gBAAgB,CAAC,KAAK,IAAI,CAAC;IACpD;;OAEG;IACH,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,UAAU,CAAC,gBAAgB,CAAC,KAAK,IAAI,CAAC;IACnD;;OAEG;IACH,SAAS,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;IACjC;;;OAGG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAOD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,aAAqB,EACrB,WAAW,EACX,QAAQ,EACR,YAAY,EACZ,SAAiB,EACjB,KAAK,EACL,UAAkB,EAClB,QAAQ,EACR,eAAe,EACf,UAAkB,EAClB,UAAkB,EAClB,UAAkB,EAClB,IAAW,EACX,OAAO,EACP,MAAM,EACN,SAAS,EACT,MAAM,EACN,KAAK,EACL,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,EACH,GAAG,IAAI,EACR,EAAE,kBAAkB,+BA+MpB;yBAxOe,aAAa"}
@@ -23,7 +23,7 @@ import { FieldLabel } from "../Field/FieldLabel.js";
23
23
  import { FieldStatus } from "../FieldStatus/FieldStatus.js";
24
24
  import { Spinner } from "../Spinner/index.js";
25
25
  import { useTooltip } from "../Tooltip/index.js";
26
- import { mergeProps } from "../utils/index.js";
26
+ import { mergeProps, rtlStyles } from "../utils/index.js";
27
27
  import { indicatorScope } from "../Indicator/indicator.markers.stylex.js";
28
28
  import { useIndicatorFocusRing } from "../hooks/useIndicatorFocusRing.js";
29
29
  import { useResolvedRequired } from "../hooks/useResolvedRequired.js";
@@ -54,16 +54,17 @@ const styles = {
54
54
  },
55
55
  input: {
56
56
  kVAEAm: "x10l6tqk",
57
+ k87sOh: "xwa60dl",
57
58
  kogj98: "x1ghz6dp",
58
59
  kmVPX3: "x1717udv",
59
60
  kSiTet: "xg01cxk",
60
61
  kkrTdU: "x1ypdohk x16khyan",
61
62
  kY2c9j: "x1vjfegm",
62
- k7Eaqz: "xkagaj0",
63
- kAzted: "x80b3aj",
64
- k87sOh: "xijlfn0",
65
- kLqNvP: "x1hsn8va",
66
- k3aq6I: "x8i4i9p",
63
+ $$css: true
64
+ },
65
+ inputCoarse: {
66
+ kjFi6P: "xkagaj0",
67
+ kHzOjL: "x80b3aj",
67
68
  $$css: true
68
69
  },
69
70
  inputDisabled: {
@@ -270,7 +271,7 @@ export function CheckboxInput({
270
271
  "aria-describedby": ariaDescribedBy,
271
272
  "aria-invalid": status?.type === 'error' ? true : undefined,
272
273
  "aria-busy": isBusy || undefined,
273
- ...stylex.props(styles.input, wrapperSizeStyles[size], isDisabled && styles.inputDisabled)
274
+ ...stylex.props(styles.input, rtlStyles.centerInline('-50%'), styles.inputCoarse, wrapperSizeStyles[size], isDisabled && styles.inputDisabled)
274
275
  }), /*#__PURE__*/_jsx("span", {
275
276
  ref: indicatorRef,
276
277
  ...{
@@ -19,7 +19,9 @@ export interface CheckboxListItemProps extends BaseProps<HTMLLIElement> {
19
19
  *
20
20
  * Accepts a plain string (single-line truncation applied automatically)
21
21
  * or a ReactNode for rich content (no truncation constraints —
22
- * child components control their own text behavior).
22
+ * child components control their own text behavior). Links and buttons in
23
+ * the label keep their own behavior; only non-interactive row clicks
24
+ * delegate to the checkbox.
23
25
  */
24
26
  label: ReactNode;
25
27
  /**
@@ -47,9 +49,9 @@ export interface CheckboxListItemProps extends BaseProps<HTMLLIElement> {
47
49
  */
48
50
  value?: string;
49
51
  /**
50
- * Secondary text below the label.
52
+ * Secondary content below the label. Accepts a plain string or a ReactNode.
51
53
  */
52
- description?: string;
54
+ description?: ReactNode;
53
55
  /**
54
56
  * Content rendered after the label area.
55
57
  */