@astryxdesign/core 0.5.2-canary.237d82a → 0.5.2-canary.28ce0ea

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 (316) hide show
  1. package/dist/AppShell/AppShell.d.ts.map +1 -1
  2. package/dist/AppShell/AppShell.js +8 -5
  3. package/dist/Banner/Banner.d.ts +5 -0
  4. package/dist/Banner/Banner.d.ts.map +1 -1
  5. package/dist/Banner/Banner.js +7 -2
  6. package/dist/Chat/ChatComposer.d.ts +2 -2
  7. package/dist/Chat/ChatComposer.d.ts.map +1 -1
  8. package/dist/Chat/ChatComposer.js +21 -2
  9. package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
  10. package/dist/Chat/ChatComposerInput.js +84 -6
  11. package/dist/Chat/chatComposerSelection.d.ts +71 -9
  12. package/dist/Chat/chatComposerSelection.d.ts.map +1 -1
  13. package/dist/Chat/chatComposerSelection.js +155 -9
  14. package/dist/Chat/useChatNewMessages.js +3 -3
  15. package/dist/Chat/useChatStreamScroll.d.ts.map +1 -1
  16. package/dist/Chat/useChatStreamScroll.js +64 -72
  17. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  18. package/dist/CheckboxInput/CheckboxInput.js +9 -8
  19. package/dist/CheckboxList/CheckboxListItem.d.ts +5 -3
  20. package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
  21. package/dist/Collapsible/Collapsible.d.ts.map +1 -1
  22. package/dist/Collapsible/Collapsible.js +1 -1
  23. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  24. package/dist/DropdownMenu/DropdownMenu.js +2 -4
  25. package/dist/Field/Field.d.ts.map +1 -1
  26. package/dist/Field/Field.js +10 -4
  27. package/dist/Field/FieldLabel.d.ts.map +1 -1
  28. package/dist/Field/FieldLabel.js +35 -20
  29. package/dist/Field/PanelSearchInput.d.ts.map +1 -1
  30. package/dist/Field/PanelSearchInput.js +3 -5
  31. package/dist/FieldStatus/FieldStatus.d.ts.map +1 -1
  32. package/dist/FieldStatus/FieldStatus.js +4 -2
  33. package/dist/Layer/useTouchTrigger.d.ts.map +1 -1
  34. package/dist/Layer/useTouchTrigger.js +2 -4
  35. package/dist/Layout/LayoutHeader.d.ts +10 -1
  36. package/dist/Layout/LayoutHeader.d.ts.map +1 -1
  37. package/dist/Layout/LayoutHeader.js +8 -2
  38. package/dist/Markdown/parser.d.ts +26 -0
  39. package/dist/Markdown/parser.d.ts.map +1 -1
  40. package/dist/Markdown/parser.js +241 -61
  41. package/dist/PowerSearch/PowerSearchValueEditor.d.ts.map +1 -1
  42. package/dist/PowerSearch/PowerSearchValueEditor.js +38 -49
  43. package/dist/PowerSearch/resolveDateTimeRangePart.d.ts +10 -0
  44. package/dist/PowerSearch/resolveDateTimeRangePart.d.ts.map +1 -0
  45. package/dist/PowerSearch/resolveDateTimeRangePart.js +30 -0
  46. package/dist/PowerSearch/usePowerSearchConfig.d.ts.map +1 -1
  47. package/dist/PowerSearch/usePowerSearchConfig.js +4 -24
  48. package/dist/RadioList/RadioListItem.d.ts +30 -5
  49. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  50. package/dist/RadioList/RadioListItem.js +25 -11
  51. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  52. package/dist/Resizable/ResizeHandle.js +102 -44
  53. package/dist/Resizable/index.d.ts +2 -0
  54. package/dist/Resizable/index.d.ts.map +1 -1
  55. package/dist/Resizable/index.js +1 -0
  56. package/dist/Resizable/useResizable.d.ts +89 -8
  57. package/dist/Resizable/useResizable.d.ts.map +1 -1
  58. package/dist/Resizable/useResizable.js +521 -38
  59. package/dist/Resizable/utils.d.ts +35 -0
  60. package/dist/Resizable/utils.d.ts.map +1 -0
  61. package/dist/Resizable/utils.js +27 -0
  62. package/dist/Slider/Slider.d.ts.map +1 -1
  63. package/dist/Slider/Slider.js +3 -5
  64. package/dist/Spinner/Spinner.d.ts.map +1 -1
  65. package/dist/Spinner/Spinner.js +8 -9
  66. package/dist/Stepper/Step.d.ts +2 -1
  67. package/dist/Stepper/Step.d.ts.map +1 -1
  68. package/dist/Stepper/Step.js +75 -11
  69. package/dist/Stepper/Stepper.d.ts.map +1 -1
  70. package/dist/Stepper/Stepper.js +1 -0
  71. package/dist/Switch/Switch.d.ts.map +1 -1
  72. package/dist/Switch/Switch.js +8 -8
  73. package/dist/TabList/TabList.d.ts +19 -1
  74. package/dist/TabList/TabList.d.ts.map +1 -1
  75. package/dist/TabList/TabList.js +28 -5
  76. package/dist/Table/index.d.ts +16 -1
  77. package/dist/Table/index.d.ts.map +1 -1
  78. package/dist/Table/index.js +4 -0
  79. package/dist/Table/plugins/columnResize/useTableColumnResize.d.ts.map +1 -1
  80. package/dist/Table/plugins/columnResize/useTableColumnResize.js +8 -9
  81. package/dist/Table/plugins/rowStatus/index.d.ts +1 -1
  82. package/dist/Table/plugins/rowStatus/index.d.ts.map +1 -1
  83. package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts +11 -33
  84. package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts.map +1 -1
  85. package/dist/Table/plugins/rowStatus/useTableRowStatus.js +108 -75
  86. package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
  87. package/dist/Table/plugins/sortable/useTableSortable.js +9 -1
  88. package/dist/Text/useTruncation.d.ts +1 -1
  89. package/dist/Text/useTruncation.d.ts.map +1 -1
  90. package/dist/Text/useTruncation.js +8 -7
  91. package/dist/Timestamp/Timestamp.d.ts +2 -2
  92. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  93. package/dist/Timestamp/Timestamp.js +4 -145
  94. package/dist/Timestamp/formatRelativeTime.d.ts +20 -0
  95. package/dist/Timestamp/formatRelativeTime.d.ts.map +1 -0
  96. package/dist/Timestamp/formatRelativeTime.js +81 -0
  97. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  98. package/dist/Toast/ToastViewport.js +125 -77
  99. package/dist/Tokenizer/Tokenizer.d.ts +0 -38
  100. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  101. package/dist/Tokenizer/Tokenizer.js +94 -26
  102. package/dist/Tokenizer/useEndLaneReserve.d.ts +31 -0
  103. package/dist/Tokenizer/useEndLaneReserve.d.ts.map +1 -0
  104. package/dist/Tokenizer/useEndLaneReserve.js +136 -0
  105. package/dist/Tooltip/useTooltip.d.ts +1 -1
  106. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  107. package/dist/Tooltip/useTooltip.js +53 -42
  108. package/dist/TreeList/TreeList.d.ts +1 -1
  109. package/dist/TreeList/TreeList.d.ts.map +1 -1
  110. package/dist/TreeList/TreeList.js +11 -1
  111. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  112. package/dist/Typeahead/BaseTypeahead.js +85 -30
  113. package/dist/Typeahead/Typeahead.d.ts +0 -21
  114. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  115. package/dist/Typeahead/Typeahead.js +108 -67
  116. package/dist/Typeahead/busyIndicatorLane.d.ts +62 -0
  117. package/dist/Typeahead/busyIndicatorLane.d.ts.map +1 -0
  118. package/dist/Typeahead/busyIndicatorLane.js +98 -0
  119. package/dist/astryx.css +25 -9
  120. package/dist/hooks/useFocusReturnVisibility.d.ts.map +1 -1
  121. package/dist/hooks/useFocusReturnVisibility.js +3 -5
  122. package/dist/hooks/useFocusTrap.d.ts +3 -1
  123. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  124. package/dist/hooks/useFocusTrap.js +31 -1
  125. package/dist/hooks/useIsomorphicLayoutEffect.d.ts +2 -2
  126. package/dist/hooks/useIsomorphicLayoutEffect.d.ts.map +1 -1
  127. package/dist/hooks/useOverflow.js +11 -11
  128. package/dist/hooks/useScrollOverflow.d.ts.map +1 -1
  129. package/dist/hooks/useScrollOverflow.js +7 -4
  130. package/dist/theme/defineTheme.d.ts +13 -0
  131. package/dist/theme/defineTheme.d.ts.map +1 -1
  132. package/dist/theme/defineTheme.js +7 -0
  133. package/dist/theme/generateThemeRules.d.ts.map +1 -1
  134. package/dist/theme/generateThemeRules.js +1 -1
  135. package/dist/theme/localTokens.d.ts +18 -0
  136. package/dist/theme/localTokens.d.ts.map +1 -0
  137. package/dist/theme/localTokens.js +162 -0
  138. package/dist/utils/interactionModality.d.ts +6 -28
  139. package/dist/utils/interactionModality.d.ts.map +1 -1
  140. package/dist/utils/interactionModality.js +72 -37
  141. package/dist/utils/sharedResizeObserver.d.ts +17 -9
  142. package/dist/utils/sharedResizeObserver.d.ts.map +1 -1
  143. package/dist/utils/sharedResizeObserver.js +54 -12
  144. package/locales/fr-FR.json +12 -0
  145. package/package.json +8 -2
  146. package/scripts/agent-doc-state.mjs +137 -0
  147. package/scripts/postinstall.mjs +28 -60
  148. package/src/AlertDialog/useImperativeAlertDialog.test.tsx +163 -0
  149. package/src/AppShell/AppShell.test.tsx +21 -0
  150. package/src/AppShell/AppShell.tsx +10 -5
  151. package/src/Banner/Banner.doc.mjs +4 -0
  152. package/src/Banner/Banner.tsx +12 -1
  153. package/src/Calendar/hooks/useCalendarConstraints.test.ts +153 -0
  154. package/src/Calendar/hooks/useCalendarDays.test.ts +265 -0
  155. package/src/Calendar/hooks/useCalendarNavigation.test.ts +252 -0
  156. package/src/Center/Center.doc.mjs +7 -0
  157. package/src/Chat/ChatComposer.doc.mjs +7 -4
  158. package/src/Chat/ChatComposer.test.tsx +84 -2
  159. package/src/Chat/ChatComposer.tsx +45 -3
  160. package/src/Chat/ChatComposerInput.test.tsx +504 -0
  161. package/src/Chat/ChatComposerInput.tsx +94 -6
  162. package/src/Chat/ChatComposerTokenElement.doc.mjs +5 -0
  163. package/src/Chat/ChatMessage.doc.mjs +17 -0
  164. package/src/Chat/ChatMessageList.doc.mjs +33 -0
  165. package/src/Chat/ChatSendButton.test.tsx +189 -0
  166. package/src/Chat/ChatToolCalls.doc.mjs +9 -0
  167. package/src/Chat/chatComposerSelection.ts +189 -9
  168. package/src/Chat/useChatNewMessages.ts +3 -3
  169. package/src/Chat/useChatPasteAsToken.test.ts +191 -0
  170. package/src/Chat/useChatStreamScroll.test.tsx +168 -5
  171. package/src/Chat/useChatStreamScroll.ts +71 -67
  172. package/src/CheckboxInput/CheckboxInput.test.tsx +53 -0
  173. package/src/CheckboxInput/CheckboxInput.tsx +9 -21
  174. package/src/CheckboxList/CheckboxList.test.tsx +45 -0
  175. package/src/CheckboxList/CheckboxListItem.doc.mjs +11 -9
  176. package/src/CheckboxList/CheckboxListItem.tsx +5 -3
  177. package/src/Collapsible/Collapsible.tsx +10 -0
  178. package/src/Collapsible/useCollapsible.test.tsx +236 -0
  179. package/src/CommandPalette/CommandPaletteEmpty.test.tsx +101 -0
  180. package/src/ComplexSelector/ComplexSelector.spec.md +1 -1
  181. package/src/DropdownMenu/DropdownMenu.tsx +2 -4
  182. package/src/DropdownMenu/DropdownMenuRadioItem.doc.mjs +13 -0
  183. package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +31 -0
  184. package/src/Field/Field.doc.mjs +1 -0
  185. package/src/Field/Field.test.tsx +39 -0
  186. package/src/Field/Field.tsx +18 -1
  187. package/src/Field/FieldLabel.test.tsx +15 -0
  188. package/src/Field/FieldLabel.tsx +69 -41
  189. package/src/Field/InputClearButton.test.tsx +68 -6
  190. package/src/Field/PanelSearchInput.tsx +3 -5
  191. package/src/FieldStatus/FieldStatus.test.tsx +17 -0
  192. package/src/FieldStatus/FieldStatus.tsx +7 -2
  193. package/src/Grid/Grid.doc.mjs +11 -0
  194. package/src/Grid/GridSpan.doc.mjs +7 -0
  195. package/src/Indicator/Indicator.doc.mjs +3 -0
  196. package/src/Layer/useTouchTrigger.ts +2 -4
  197. package/src/Layout/Layout.test.tsx +64 -0
  198. package/src/Layout/LayoutHeader.doc.mjs +23 -0
  199. package/src/Layout/LayoutHeader.tsx +18 -0
  200. package/src/Layout/LayoutPanel.doc.mjs +4 -3
  201. package/src/Lightbox/useLightbox.test.tsx +263 -0
  202. package/src/Markdown/Markdown.test.tsx +86 -1
  203. package/src/Markdown/incremental.test.ts +242 -0
  204. package/src/Markdown/parser.perf.test.ts +93 -0
  205. package/src/Markdown/parser.ts +287 -60
  206. package/src/MultiSelector/MultiSelector.doc.mjs +11 -0
  207. package/src/MultiSelector/MultiSelector.spec.md +1 -1
  208. package/src/PowerSearch/PowerSearch.doc.mjs +45 -0
  209. package/src/PowerSearch/PowerSearchFilterEditor.test.tsx +376 -0
  210. package/src/PowerSearch/PowerSearchToken.test.tsx +228 -0
  211. package/src/PowerSearch/PowerSearchValueEditor.test.tsx +120 -4
  212. package/src/PowerSearch/PowerSearchValueEditor.tsx +48 -63
  213. package/src/PowerSearch/resolveDateTimeRangePart.test.ts +40 -0
  214. package/src/PowerSearch/resolveDateTimeRangePart.ts +37 -0
  215. package/src/PowerSearch/usePowerSearchConfig.test.ts +26 -1
  216. package/src/PowerSearch/usePowerSearchConfig.ts +7 -25
  217. package/src/RadioList/RadioList.test.tsx +185 -2
  218. package/src/RadioList/RadioListItem.doc.mjs +30 -11
  219. package/src/RadioList/RadioListItem.tsx +53 -28
  220. package/src/Resizable/Resizable.doc.mjs +71 -8
  221. package/src/Resizable/Resizable.spec.md +85 -34
  222. package/src/Resizable/ResizeHandle.test.tsx +177 -56
  223. package/src/Resizable/ResizeHandle.tsx +128 -45
  224. package/src/Resizable/index.ts +2 -0
  225. package/src/Resizable/shippedDirections.test.ts +462 -0
  226. package/src/Resizable/useResizable.doc.mjs +124 -29
  227. package/src/Resizable/useResizable.test.ts +1329 -3
  228. package/src/Resizable/useResizable.ts +760 -48
  229. package/src/Resizable/utils.test.ts +74 -0
  230. package/src/Resizable/utils.ts +35 -0
  231. package/src/Selector/Selector.doc.mjs +10 -0
  232. package/src/Selector/Selector.spec.md +8 -6
  233. package/src/SideNav/SideNav.test.tsx +3 -3
  234. package/src/SideNav/SideNavCollapseButton.doc.mjs +8 -0
  235. package/src/SideNav/SideNavHeading.doc.mjs +3 -0
  236. package/src/SideNav/SideNavItem.doc.mjs +3 -0
  237. package/src/SideNav/SideNavSection.doc.mjs +10 -0
  238. package/src/Slider/Slider.tsx +2 -5
  239. package/src/Spinner/Spinner.test.tsx +32 -7
  240. package/src/Spinner/Spinner.tsx +25 -13
  241. package/src/Stack/Stack.doc.mjs +38 -0
  242. package/src/Stepper/Step.doc.mjs +1 -1
  243. package/src/Stepper/Step.tsx +118 -5
  244. package/src/Stepper/Stepper.doc.mjs +51 -1
  245. package/src/Stepper/Stepper.spec.md +245 -0
  246. package/src/Stepper/Stepper.test.tsx +315 -0
  247. package/src/Stepper/Stepper.tsx +8 -0
  248. package/src/Switch/Switch.test.tsx +53 -0
  249. package/src/Switch/Switch.tsx +9 -21
  250. package/src/TabList/TabList.doc.mjs +10 -1
  251. package/src/TabList/TabList.tsx +53 -3
  252. package/src/TabList/TabMenu.doc.mjs +14 -0
  253. package/src/Table/Table.perf.test.tsx +51 -1
  254. package/src/Table/Table.spec.md +92 -53
  255. package/src/Table/TableHeaderCell.test.tsx +291 -0
  256. package/src/Table/index.ts +19 -4
  257. package/src/Table/plugins/columnResize/useTableColumnResize.tsx +8 -12
  258. package/src/Table/plugins/filtering/useTableFilterState.test.tsx +143 -0
  259. package/src/Table/plugins/groupedRows/useTableGroupedRows.test.tsx +2 -1
  260. package/src/Table/plugins/rowStatus/index.ts +1 -4
  261. package/src/Table/plugins/rowStatus/useTableRowStatus.public.test.ts +92 -0
  262. package/src/Table/plugins/rowStatus/useTableRowStatus.spec.md +354 -0
  263. package/src/Table/plugins/rowStatus/useTableRowStatus.test.tsx +413 -56
  264. package/src/Table/plugins/rowStatus/useTableRowStatus.tsx +155 -102
  265. package/src/Table/plugins/sortable/useTableSortable.test.tsx +72 -0
  266. package/src/Table/plugins/sortable/useTableSortable.tsx +18 -1
  267. package/src/Table/useTableRowStatus.doc.mjs +5 -5
  268. package/src/Text/useTruncation.ts +8 -7
  269. package/src/TextArea/TextArea.spec.md +1 -1
  270. package/src/Timestamp/Timestamp.doc.mjs +2 -2
  271. package/src/Timestamp/Timestamp.test.tsx +75 -3
  272. package/src/Timestamp/Timestamp.tsx +7 -153
  273. package/src/Timestamp/formatRelativeTime.ts +96 -0
  274. package/src/Toast/Toast.doc.mjs +4 -0
  275. package/src/Toast/ToastViewport.test.tsx +194 -0
  276. package/src/Toast/ToastViewport.tsx +136 -58
  277. package/src/Tokenizer/Tokenizer.doc.mjs +16 -0
  278. package/src/Tokenizer/Tokenizer.spec.md +5 -5
  279. package/src/Tokenizer/Tokenizer.test.tsx +335 -1
  280. package/src/Tokenizer/Tokenizer.tsx +129 -46
  281. package/src/Tokenizer/useEndLaneReserve.ts +146 -0
  282. package/src/Tooltip/Tooltip.test.tsx +21 -1
  283. package/src/Tooltip/useTooltip.test.tsx +318 -0
  284. package/src/Tooltip/useTooltip.tsx +60 -39
  285. package/src/TopNav/TopNavHeading.doc.mjs +3 -0
  286. package/src/TopNav/TopNavItem.doc.mjs +3 -0
  287. package/src/TopNav/TopNavMenu.doc.mjs +10 -0
  288. package/src/TreeList/TreeList.test.tsx +94 -0
  289. package/src/TreeList/TreeList.tsx +19 -1
  290. package/src/Typeahead/BaseTypeahead.tsx +96 -25
  291. package/src/Typeahead/Typeahead.doc.mjs +6 -0
  292. package/src/Typeahead/Typeahead.spec.md +8 -2
  293. package/src/Typeahead/Typeahead.test.tsx +502 -1
  294. package/src/Typeahead/Typeahead.tsx +187 -69
  295. package/src/Typeahead/TypeaheadItem.test.tsx +93 -2
  296. package/src/Typeahead/busyIndicatorLane.tsx +109 -0
  297. package/src/__tests__/stylexDeclarations.ts +51 -0
  298. package/src/hooks/useFocusReturnVisibility.ts +3 -5
  299. package/src/hooks/useFocusTrap.doc.mjs +2 -2
  300. package/src/hooks/useFocusTrap.test.tsx +48 -0
  301. package/src/hooks/useFocusTrap.ts +35 -1
  302. package/src/hooks/useOverflow.ts +11 -11
  303. package/src/hooks/useScrollLock.test.ts +103 -0
  304. package/src/hooks/useScrollOverflow.ts +7 -4
  305. package/src/i18n/useTranslator.test.tsx +197 -0
  306. package/src/postinstall.test.mjs +71 -5
  307. package/src/theme/defineTheme.test.ts +227 -0
  308. package/src/theme/defineTheme.ts +30 -0
  309. package/src/theme/derivedVarRegistry.test.ts +6 -0
  310. package/src/theme/generateThemeRules.ts +4 -1
  311. package/src/theme/localTokens.ts +272 -0
  312. package/src/theme/themingTargets.test.ts +39 -7
  313. package/src/utils/interactionModality.test.ts +83 -0
  314. package/src/utils/interactionModality.ts +88 -36
  315. package/src/utils/sharedResizeObserver.test.ts +124 -5
  316. 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;AA2KD;;;;;;;;;;;;;;;;;;;;;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,qBA6Yf;yBA5Ze,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";
@@ -105,6 +105,7 @@ const styles = {
105
105
  k1xSpc: "x78zum5",
106
106
  kXwgrk: "xdt5ytf",
107
107
  kVAEAm: "x1n2onr6",
108
+ kVQacm: "x7giv3",
108
109
  $$css: true
109
110
  },
110
111
  variantWash: {
@@ -294,6 +295,9 @@ export function AppShell({
294
295
  const navHasDividers = variant === 'section';
295
296
  const isElevated = variant === 'elevated';
296
297
  const navAreaStyle = variant === 'wash' || variant === 'elevated' ? styles.navAreaWash : variant === 'surface' ? styles.navAreaSurface : undefined;
298
+ // Section normally inherits the shell's surface background. Its auto-height
299
+ // header needs to paint that surface itself while content scrolls beneath it.
300
+ const headerAreaStyle = navAreaStyle ?? (isAuto && variant === 'section' ? styles.navAreaSurface : undefined);
297
301
  const contentAreaStyle = variant === 'wash' ? styles.contentBgWash : variant === 'elevated' && hasTopNav && hasSideNav && !isBelowBreakpoint ? styles.contentBgTransparent : variant === 'surface' || variant === 'elevated' ? styles.contentBgSurface : undefined;
298
302
 
299
303
  // Background for sticky elements in auto mode — must be opaque so content
@@ -316,8 +320,7 @@ export function AppShell({
316
320
  const height = headerEl.getBoundingClientRect().height;
317
321
  shellEl.style.setProperty('--_app-shell-header-height', `${height}px`);
318
322
  };
319
- observeResize(headerEl, () => updateHeight());
320
- return () => unobserveResize(headerEl);
323
+ return observeResize(headerEl, () => updateHeight());
321
324
  }, [isAuto]);
322
325
 
323
326
  // =========================================================================
@@ -395,7 +398,7 @@ export function AppShell({
395
398
  role: "banner",
396
399
  ...mergeProps(themeProps('app-shell-header', {
397
400
  variant
398
- }), stylex.props(navAreaStyle, isAuto && styles.headerSticky)),
401
+ }), stylex.props(headerAreaStyle, isAuto && styles.headerSticky)),
399
402
  children: headerInner
400
403
  }) : undefined;
401
404
 
@@ -473,7 +476,7 @@ export function AppShell({
473
476
  role: headerContent == null ? 'banner' : undefined,
474
477
  ...mergeProps(themeProps('app-shell-header', {
475
478
  variant
476
- }), stylex.props(navAreaStyle, isAuto && styles.headerSticky)),
479
+ }), stylex.props(headerAreaStyle, isAuto && styles.headerSticky)),
477
480
  children: /*#__PURE__*/_jsx(LayoutHeader, {
478
481
  padding: 0,
479
482
  hasDivider: navHasDividers,
@@ -11,6 +11,11 @@
11
11
  * - Status icon (themeProps 'banner-icon'): the target rides on the default
12
12
  * <Icon> itself — the element that paints — so 'status:X' overrides reach
13
13
  * the glyph (#4166); for a custom `icon` node it stays on the layout wrapper
14
+ * - Description (themeProps 'banner-description'): the supporting line owns its
15
+ * own colour and type, and the space between it and the title
16
+ * - The end area carries no target: it is a layout row (flex, wrap, edge
17
+ * compensation) rather than a painted surface, and a theme that wants the
18
+ * header to grow around its buttons sets `padding-block` on 'banner'
14
19
  * - No left border accent — color is expressed through the full header background
15
20
  * - Each visual area owns its own border-radius (no overflow:clip on the container)
16
21
  * - Children are collapsible by default: a toggle appears in the header end
@@ -1 +1 @@
1
- {"version":3,"file":"Banner.d.ts","sourceRoot":"","sources":["../../src/Banner/Banner.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH,OAAO,EAA0B,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAE9D,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAgB5C,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,+BAA+B,CAAC;AAErE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,gBAAgB,CAAC;AAI9C,OAAO,KAAK,EAAC,eAAe,EAAE,kBAAkB,EAAC,MAAM,SAAS,CAAC;AAMjE;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,eAAe,CAAC;AAEjD;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,kBAAkB,CAAC;AAEvD,MAAM,WAAW,WAAY,SAAQ,SAAS,CAAC,cAAc,CAAC;IAC5D,wCAAwC;IACxC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC;;OAEG;IACH,MAAM,EAAE,YAAY,CAAC;IACrB;;OAEG;IACH,KAAK,EAAE,SAAS,CAAC;IACjB;;OAEG;IACH,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB;;OAEG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;;;;;;;OAWG;IACH,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B;;;;;OAKG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB;;;;;;;;;;;;;;;;;;OAkBG;IACH,WAAW,CAAC,EAAE,OAAO,GAAG,iBAAiB,CAAC;IAC1C;;;;OAIG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAmND;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AACH,wBAAgB,MAAM,CAAC,EACrB,MAAM,EACN,KAAK,EACL,WAAW,EACX,IAAI,EACJ,aAAqB,EACrB,SAAS,EACT,YAAY,EACZ,UAAU,EACV,SAAkB,EAClB,SAAkB,EAClB,WAAkB,EAClB,QAAQ,EACR,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,EACH,cAAc,EACd,oBAAoB,EACpB,GAAG,IAAI,EACR,EAAE,WAAW,sCA6Ob;yBAjQe,MAAM"}
1
+ {"version":3,"file":"Banner.d.ts","sourceRoot":"","sources":["../../src/Banner/Banner.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAEH,OAAO,EAA0B,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAE9D,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAgB5C,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,+BAA+B,CAAC;AAErE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,gBAAgB,CAAC;AAI9C,OAAO,KAAK,EAAC,eAAe,EAAE,kBAAkB,EAAC,MAAM,SAAS,CAAC;AAMjE;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,eAAe,CAAC;AAEjD;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,kBAAkB,CAAC;AAEvD,MAAM,WAAW,WAAY,SAAQ,SAAS,CAAC,cAAc,CAAC;IAC5D,wCAAwC;IACxC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC;;OAEG;IACH,MAAM,EAAE,YAAY,CAAC;IACrB;;OAEG;IACH,KAAK,EAAE,SAAS,CAAC;IACjB;;OAEG;IACH,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB;;OAEG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;;;;;;;OAWG;IACH,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B;;;;;OAKG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB;;;;;;;;;;;;;;;;;;OAkBG;IACH,WAAW,CAAC,EAAE,OAAO,GAAG,iBAAiB,CAAC;IAC1C;;;;OAIG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAmND;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AACH,wBAAgB,MAAM,CAAC,EACrB,MAAM,EACN,KAAK,EACL,WAAW,EACX,IAAI,EACJ,aAAqB,EACrB,SAAS,EACT,YAAY,EACZ,UAAU,EACV,SAAkB,EAClB,SAAkB,EAClB,WAAkB,EAClB,QAAQ,EACR,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,EACH,cAAc,EACd,oBAAoB,EACpB,GAAG,IAAI,EACR,EAAE,WAAW,sCAmPb;yBAvQe,MAAM"}
@@ -15,6 +15,11 @@
15
15
  * - Status icon (themeProps 'banner-icon'): the target rides on the default
16
16
  * <Icon> itself — the element that paints — so 'status:X' overrides reach
17
17
  * the glyph (#4166); for a custom `icon` node it stays on the layout wrapper
18
+ * - Description (themeProps 'banner-description'): the supporting line owns its
19
+ * own colour and type, and the space between it and the title
20
+ * - The end area carries no target: it is a layout row (flex, wrap, edge
21
+ * compensation) rather than a painted surface, and a theme that wants the
22
+ * header to grow around its buttons sets `padding-block` on 'banner'
18
23
  * - No left border accent — color is expressed through the full header background
19
24
  * - Each visual area owns its own border-radius (no overflow:clip on the container)
20
25
  * - Children are collapsible by default: a toggle appears in the header end
@@ -429,9 +434,9 @@ export function Banner({
429
434
  },
430
435
  children: title
431
436
  }), isRenderable(description) && /*#__PURE__*/_jsx("div", {
432
- ...{
437
+ ...mergeProps(themeProps('banner-description'), {
433
438
  className: "x1ghz6dp xjb2p0i x141an7d x1sodnla x1ltkj2j xv1l7n4 xj0a0fe"
434
- },
439
+ }),
435
440
  children: description
436
441
  })]
437
442
  }), showEndArea && /*#__PURE__*/_jsxs("div", {
@@ -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, placeCaretAtEnd, getSelectionRangeInside, restoreSelectionRange } 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";
@@ -182,11 +182,65 @@ export function ChatComposerInput(props) {
182
182
  // when a parent attaches a ref — without this, paste-as-token would
183
183
  // silently no-op whenever `ChatComposerInput` is rendered without
184
184
  // a forwarded ref (e.g. inside `ChatComposer`).
185
+ /**
186
+ * Focus the editable and put the caret after the draft.
187
+ *
188
+ * A bare `focus()` is not enough: Chromium collapses the caret to the
189
+ * start of the content, which is the one position where ArrowUp means
190
+ * "recall history" — so focusing a composer that already holds a draft
191
+ * would arm the next ArrowUp to replace it. Landing after the text is
192
+ * also what a click on the composer's trailing space means.
193
+ */
194
+ /**
195
+ * Focus the editable, keeping a caret or selection the user already has
196
+ * inside it.
197
+ *
198
+ * A bare `focus()` is not enough on its own: Chromium collapses the caret
199
+ * to the start of the content, which is the one position where ArrowUp
200
+ * means "recall history" — so focusing a composer that already holds a
201
+ * draft would arm the next ArrowUp to replace it. But a consumer calling
202
+ * `focus()` to return the user to where they were must not have their
203
+ * caret moved either, so an existing in-editor selection is captured
204
+ * before focusing and restored after. Only when there is none does the
205
+ * caret land after the draft.
206
+ */
207
+ const focusEditable = useCallback(() => {
208
+ const editable = editableRef.current;
209
+ if (!editable) {
210
+ return;
211
+ }
212
+ // Read before focusing: `focus()` itself creates the offset-0 caret, so
213
+ // asking afterwards cannot tell the user's own caret from the engine's.
214
+ const existing = getSelectionRangeInside(editable);
215
+ editable.focus();
216
+ if (existing) {
217
+ restoreSelectionRange(existing);
218
+ return;
219
+ }
220
+ placeCaretAtEnd(editable);
221
+ }, []);
222
+
223
+ /**
224
+ * Focus the editable and put the caret after the draft, whatever the
225
+ * selection was.
226
+ *
227
+ * This is the composer shell's click-to-focus path: clicking the empty
228
+ * space after a draft means "put me after the text", so it overrides a
229
+ * stale caret rather than restoring one.
230
+ */
231
+ const focusEditableAtEnd = useCallback(() => {
232
+ const editable = editableRef.current;
233
+ if (!editable) {
234
+ return;
235
+ }
236
+ editable.focus();
237
+ placeCaretAtEnd(editable);
238
+ }, []);
185
239
  const handle = {
186
240
  insertToken: token => insertTokenRef.current(token),
187
241
  expandToken: id => tokens.expandToken(id),
188
242
  insertText: text => insertTextRef.current(text),
189
- focus: () => editableRef.current?.focus(),
243
+ focus: focusEditable,
190
244
  getValue: () => serialize(editableRef.current ?? document.createElement('div'))
191
245
  };
192
246
  selfRef.current = handle;
@@ -201,12 +255,12 @@ export function ChatComposerInput(props) {
201
255
  return;
202
256
  }
203
257
  inputControlRef.current = {
204
- focus: () => editableRef.current?.focus()
258
+ focus: focusEditableAtEnd
205
259
  };
206
260
  return () => {
207
261
  inputControlRef.current = null;
208
262
  };
209
- }, [inputControlRef]);
263
+ }, [inputControlRef, focusEditableAtEnd]);
210
264
  useEffect(() => {
211
265
  if (controlledValue === undefined || !editableRef.current) {
212
266
  return;
@@ -373,12 +427,36 @@ export function ChatComposerInput(props) {
373
427
  return;
374
428
  }
375
429
 
376
- // History navigation (only when trigger menu is not active)
430
+ // History navigation (only when trigger menu is not active).
431
+ // Recall only at the text boundaries so the caret can still move
432
+ // between lines in a multi-line draft: ArrowUp recalls the
433
+ // previous message when the caret is at the very start, ArrowDown
434
+ // steps forward when it's at the very end. A recalled message is
435
+ // shown fully selected (see `selectAll` below); that spans both
436
+ // boundaries at once, so repeated presses keep stepping through
437
+ // history. Mid-text, we bail before `preventDefault` and let the
438
+ // browser move the caret up/down a line.
377
439
  if (hasHistory && (e.key === 'ArrowUp' || e.key === 'ArrowDown')) {
378
440
  if (!editableRef.current) {
379
441
  return;
380
442
  }
381
- const text = serialize(editableRef.current);
443
+ const editable = editableRef.current;
444
+ // Last-resort fallback for a caret we never placed: an engine that
445
+ // leaves no Range inside the editable on focus, or a consumer that
446
+ // focused the DOM node directly instead of through our focus
447
+ // control. Place it where the focus control would have, so a
448
+ // pending draft is never mistaken for a caret at the start. A no-op
449
+ // whenever a real caret exists — including one the user moved.
450
+ ensureCaretInside(editable);
451
+ const isCollapsed = window.getSelection()?.isCollapsed ?? true;
452
+ const atStart = isSelectionAtStart(editable);
453
+ const atEnd = isSelectionAtEnd(editable);
454
+ const canRecallPrev = atStart && (isCollapsed || atEnd);
455
+ const canRecallNext = atEnd && (isCollapsed || atStart);
456
+ if (e.key === 'ArrowUp' ? !canRecallPrev : !canRecallNext) {
457
+ return;
458
+ }
459
+ const text = serialize(editable);
382
460
  const history = historyRef.current;
383
461
  if (history.length === 0) {
384
462
  return;
@@ -7,31 +7,93 @@
7
7
  * Selection helpers for the contentEditable chat input. Both the
8
8
  * imperative `insertToken` / `insertText` APIs and the paste pipeline
9
9
  * need a valid Range inside the editable before they can mutate the
10
- * DOM. Browsers do NOT create a Selection range when an element is
11
- * programmatically focused — a Range only exists once the user has
12
- * clicked inside the editable or we've created one explicitly.
10
+ * DOM, and history recall needs to know where the caret sits.
13
11
  *
14
- * `ensureCaretInside` centralizes the fallback: if the current
15
- * Selection has no Range inside the given editable, place a collapsed
16
- * caret at the end of the editable. Callers can then read
17
- * `selection.getRangeAt(0)` safely.
12
+ * What a programmatic `focus()` does to the Selection is browser- and
13
+ * state-dependent, and it is never what a click on the composer's padding
14
+ * means. Measured in Chromium: focusing a contentEditable collapses the
15
+ * caret to offset 0 — the *start* of the draft — whether or not a
16
+ * selection existed before, and whether the editable is empty or not.
17
+ * Other engines may instead leave no Range inside the editable at all.
18
+ *
19
+ * So the composer never infers the caret from a bare `focus()`:
20
+ * - `placeCaretAtEnd` states the caret outright, and is what the composer
21
+ * shell's click-to-focus uses, because clicking the space after a draft
22
+ * means "put me after the text" in every chat composer.
23
+ * - `getSelectionRangeInside` + `restoreSelectionRange` let the imperative
24
+ * `focus()` keep a caret or selection the user already had: it is read
25
+ * before focusing, since afterwards the engine's own caret is
26
+ * indistinguishable from theirs.
27
+ * - `ensureCaretInside` is the weaker fallback for callers that only
28
+ * need *a* valid Range (paste, imperative insertion): it respects a
29
+ * caret that already exists and creates one at the end otherwise.
18
30
  *
19
31
  * SYNC: When modified, update:
20
32
  * - /packages/core/src/Chat/ChatComposerInput.tsx (consumer)
21
33
  * - /packages/core/src/Chat/useChatComposerTokens.ts (consumer)
22
34
  */
35
+ /**
36
+ * The current Selection's range when it lies inside `editable`, or `null`.
37
+ *
38
+ * Callers use this to read the caret *before* focusing, because `focus()`
39
+ * creates a caret of its own and afterwards the two are indistinguishable.
40
+ * The range is cloned, so later DOM work cannot mutate the saved position.
41
+ */
42
+ export declare function getSelectionRangeInside(editable: HTMLElement): Range | null;
43
+ /** Make `range` the current Selection again. */
44
+ export declare function restoreSelectionRange(range: Range): void;
45
+ /**
46
+ * Collapse the Selection to the very end of `editable`'s content,
47
+ * replacing whatever the Selection held before.
48
+ *
49
+ * Unlike {@link ensureCaretInside} this is unconditional: a caret the
50
+ * browser placed on `focus()` is overwritten, which is the point — that
51
+ * caret lands at the start of the draft, where ArrowUp means "recall
52
+ * history" and would discard what the user had typed.
53
+ *
54
+ * Returns `true` when the caret was placed, `false` if the Selection API
55
+ * is unavailable (e.g. a detached document).
56
+ */
57
+ export declare function placeCaretAtEnd(editable: HTMLElement): boolean;
23
58
  /**
24
59
  * Ensure the current Selection has a Range inside `editable`.
25
60
  *
26
61
  * If `window.getSelection()` already has a Range whose `startContainer`
27
- * is inside `editable`, this is a no-op. Otherwise a collapsed caret
28
- * is placed at the end of `editable` and the Selection is updated.
62
+ * is inside `editable`, this is a no-op — including the start-of-draft
63
+ * caret a `focus()` leaves behind, which is why callers that care where
64
+ * the caret lands want {@link placeCaretAtEnd} instead. Otherwise a
65
+ * collapsed caret is placed at the end of `editable`.
29
66
  *
30
67
  * Returns the live `Selection` on success, or `null` if no Selection
31
68
  * is available at all (e.g. detached document, JSDOM without selection
32
69
  * support).
33
70
  */
34
71
  export declare function ensureCaretInside(editable: HTMLElement): Selection | null;
72
+ /**
73
+ * Whether the current selection's start boundary sits at the very
74
+ * beginning of `editable`'s content.
75
+ *
76
+ * Used by the composer to decide when ArrowUp should recall message
77
+ * history versus move the caret up a line: history is only recalled
78
+ * when the caret is at the very start of the draft. This handles both
79
+ * the editable root and offset 0 of its leading text node without
80
+ * cloning the draft's contents.
81
+ *
82
+ * Returns `false` when there is no selection, the range is not inside
83
+ * `editable`, or the boundary APIs are unavailable — callers treat
84
+ * that as "not at the boundary" and let the browser move the caret.
85
+ */
86
+ export declare function isSelectionAtStart(editable: HTMLElement): boolean;
87
+ /**
88
+ * Whether the current selection's end boundary sits at the very end of
89
+ * `editable`'s content.
90
+ *
91
+ * The ArrowDown counterpart to {@link isSelectionAtStart}: history is
92
+ * only stepped forward when the caret is at the very end of the draft.
93
+ *
94
+ * Returns `false` under the same conditions as {@link isSelectionAtStart}.
95
+ */
96
+ export declare function isSelectionAtEnd(editable: HTMLElement): boolean;
35
97
  /**
36
98
  * Insert plain text at the current selection, scoped to `editable`.
37
99
  *
@@ -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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,WAAW,GAAG,KAAK,GAAG,IAAI,CAa3E;AAED,gDAAgD;AAChD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAOxD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,WAAW,GAAG,OAAO,CAW9D;AAED;;;;;;;;;;;;GAYG;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"}
@@ -11,26 +11,94 @@
11
11
  * Selection helpers for the contentEditable chat input. Both the
12
12
  * imperative `insertToken` / `insertText` APIs and the paste pipeline
13
13
  * need a valid Range inside the editable before they can mutate the
14
- * DOM. Browsers do NOT create a Selection range when an element is
15
- * programmatically focused — a Range only exists once the user has
16
- * clicked inside the editable or we've created one explicitly.
14
+ * DOM, and history recall needs to know where the caret sits.
17
15
  *
18
- * `ensureCaretInside` centralizes the fallback: if the current
19
- * Selection has no Range inside the given editable, place a collapsed
20
- * caret at the end of the editable. Callers can then read
21
- * `selection.getRangeAt(0)` safely.
16
+ * What a programmatic `focus()` does to the Selection is browser- and
17
+ * state-dependent, and it is never what a click on the composer's padding
18
+ * means. Measured in Chromium: focusing a contentEditable collapses the
19
+ * caret to offset 0 — the *start* of the draft — whether or not a
20
+ * selection existed before, and whether the editable is empty or not.
21
+ * Other engines may instead leave no Range inside the editable at all.
22
+ *
23
+ * So the composer never infers the caret from a bare `focus()`:
24
+ * - `placeCaretAtEnd` states the caret outright, and is what the composer
25
+ * shell's click-to-focus uses, because clicking the space after a draft
26
+ * means "put me after the text" in every chat composer.
27
+ * - `getSelectionRangeInside` + `restoreSelectionRange` let the imperative
28
+ * `focus()` keep a caret or selection the user already had: it is read
29
+ * before focusing, since afterwards the engine's own caret is
30
+ * indistinguishable from theirs.
31
+ * - `ensureCaretInside` is the weaker fallback for callers that only
32
+ * need *a* valid Range (paste, imperative insertion): it respects a
33
+ * caret that already exists and creates one at the end otherwise.
22
34
  *
23
35
  * SYNC: When modified, update:
24
36
  * - /packages/core/src/Chat/ChatComposerInput.tsx (consumer)
25
37
  * - /packages/core/src/Chat/useChatComposerTokens.ts (consumer)
26
38
  */
27
39
 
40
+ /**
41
+ * The current Selection's range when it lies inside `editable`, or `null`.
42
+ *
43
+ * Callers use this to read the caret *before* focusing, because `focus()`
44
+ * creates a caret of its own and afterwards the two are indistinguishable.
45
+ * The range is cloned, so later DOM work cannot mutate the saved position.
46
+ */
47
+ export function getSelectionRangeInside(editable) {
48
+ const selection = window.getSelection();
49
+ if (!selection || selection.rangeCount === 0) {
50
+ return null;
51
+ }
52
+ const range = selection.getRangeAt(0);
53
+ if (!editable.contains(range.startContainer) || !editable.contains(range.endContainer)) {
54
+ return null;
55
+ }
56
+ return range.cloneRange();
57
+ }
58
+
59
+ /** Make `range` the current Selection again. */
60
+ export function restoreSelectionRange(range) {
61
+ const selection = window.getSelection();
62
+ if (!selection) {
63
+ return;
64
+ }
65
+ selection.removeAllRanges();
66
+ selection.addRange(range);
67
+ }
68
+
69
+ /**
70
+ * Collapse the Selection to the very end of `editable`'s content,
71
+ * replacing whatever the Selection held before.
72
+ *
73
+ * Unlike {@link ensureCaretInside} this is unconditional: a caret the
74
+ * browser placed on `focus()` is overwritten, which is the point — that
75
+ * caret lands at the start of the draft, where ArrowUp means "recall
76
+ * history" and would discard what the user had typed.
77
+ *
78
+ * Returns `true` when the caret was placed, `false` if the Selection API
79
+ * is unavailable (e.g. a detached document).
80
+ */
81
+ export function placeCaretAtEnd(editable) {
82
+ const selection = window.getSelection();
83
+ if (!selection) {
84
+ return false;
85
+ }
86
+ const range = document.createRange();
87
+ range.selectNodeContents(editable);
88
+ range.collapse(false); // collapse to end
89
+ selection.removeAllRanges();
90
+ selection.addRange(range);
91
+ return true;
92
+ }
93
+
28
94
  /**
29
95
  * Ensure the current Selection has a Range inside `editable`.
30
96
  *
31
97
  * If `window.getSelection()` already has a Range whose `startContainer`
32
- * is inside `editable`, this is a no-op. Otherwise a collapsed caret
33
- * is placed at the end of `editable` and the Selection is updated.
98
+ * is inside `editable`, this is a no-op — including the start-of-draft
99
+ * caret a `focus()` leaves behind, which is why callers that care where
100
+ * the caret lands want {@link placeCaretAtEnd} instead. Otherwise a
101
+ * collapsed caret is placed at the end of `editable`.
34
102
  *
35
103
  * Returns the live `Selection` on success, or `null` if no Selection
36
104
  * is available at all (e.g. detached document, JSDOM without selection
@@ -55,6 +123,84 @@ export function ensureCaretInside(editable) {
55
123
  return selection;
56
124
  }
57
125
 
126
+ /**
127
+ * Whether the current selection's start boundary sits at the very
128
+ * beginning of `editable`'s content.
129
+ *
130
+ * Used by the composer to decide when ArrowUp should recall message
131
+ * history versus move the caret up a line: history is only recalled
132
+ * when the caret is at the very start of the draft. This handles both
133
+ * the editable root and offset 0 of its leading text node without
134
+ * cloning the draft's contents.
135
+ *
136
+ * Returns `false` when there is no selection, the range is not inside
137
+ * `editable`, or the boundary APIs are unavailable — callers treat
138
+ * that as "not at the boundary" and let the browser move the caret.
139
+ */
140
+ export function isSelectionAtStart(editable) {
141
+ const selection = window.getSelection();
142
+ if (!selection || selection.rangeCount === 0) {
143
+ return false;
144
+ }
145
+ const range = selection.getRangeAt(0);
146
+ return isBoundaryAtEdge(editable, range.startContainer, range.startOffset, 'start');
147
+ }
148
+
149
+ /**
150
+ * Whether the current selection's end boundary sits at the very end of
151
+ * `editable`'s content.
152
+ *
153
+ * The ArrowDown counterpart to {@link isSelectionAtStart}: history is
154
+ * only stepped forward when the caret is at the very end of the draft.
155
+ *
156
+ * Returns `false` under the same conditions as {@link isSelectionAtStart}.
157
+ */
158
+ export function isSelectionAtEnd(editable) {
159
+ const selection = window.getSelection();
160
+ if (!selection || selection.rangeCount === 0) {
161
+ return false;
162
+ }
163
+ const range = selection.getRangeAt(0);
164
+ return isBoundaryAtEdge(editable, range.endContainer, range.endOffset, 'end');
165
+ }
166
+
167
+ /**
168
+ * Check whether a Range boundary is at an edge of `editable` without
169
+ * materializing the content before or after it. Chromium represents a caret
170
+ * at the start of non-empty contenteditables as the first text node at offset
171
+ * zero (and likewise uses the last text node's length at the end). Those are
172
+ * distinct DOM boundary points from the editable's child offsets, so
173
+ * `compareBoundaryPoints` cannot be used here.
174
+ */
175
+ function isBoundaryAtEdge(editable, container, offset, edge) {
176
+ if (!editable.contains(container) || !isAtNodeEdge(container, offset, edge)) {
177
+ return false;
178
+ }
179
+ for (let node = container; node && node !== editable;) {
180
+ if (hasContentSibling(node, edge)) {
181
+ return false;
182
+ }
183
+ node = node.parentNode;
184
+ }
185
+ return true;
186
+ }
187
+ function isAtNodeEdge(node, offset, edge) {
188
+ if (node.nodeType === Node.TEXT_NODE || node.nodeType === Node.CDATA_SECTION_NODE) {
189
+ return edge === 'start' ? offset === 0 : offset === (node.nodeValue?.length ?? 0);
190
+ }
191
+ return edge === 'start' ? offset === 0 : offset === node.childNodes.length;
192
+ }
193
+ function hasContentSibling(node, edge) {
194
+ for (let sibling = edge === 'start' ? node.previousSibling : node.nextSibling; sibling; sibling = edge === 'start' ? sibling.previousSibling : sibling.nextSibling) {
195
+ // Elements (including <br> and tokens) represent a content boundary even
196
+ // when they have no text. Empty text nodes and comments do not.
197
+ if (sibling.nodeType === Node.ELEMENT_NODE || sibling.nodeType === Node.TEXT_NODE && sibling.nodeValue !== '') {
198
+ return true;
199
+ }
200
+ }
201
+ return false;
202
+ }
203
+
58
204
  /**
59
205
  * Insert plain text at the current selection, scoped to `editable`.
60
206
  *