@ds-mo/ui 9.1.0 → 9.2.0

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 (318) hide show
  1. package/dist/.build-stamp +1 -1
  2. package/dist/agent-patterns.json +165 -1
  3. package/dist/agent.json +2110 -634
  4. package/dist/angular/ds-agent-activity.d.ts +12 -0
  5. package/dist/angular/ds-agent-activity.js +38 -0
  6. package/dist/angular/ds-agent-response.d.ts +12 -0
  7. package/dist/angular/ds-agent-response.js +38 -0
  8. package/dist/angular/ds-agent-source-list.d.ts +12 -0
  9. package/dist/angular/ds-agent-source-list.js +38 -0
  10. package/dist/angular/ds-agent-tool-call.d.ts +12 -0
  11. package/dist/angular/ds-agent-tool-call.js +38 -0
  12. package/dist/angular/ds-attachment-list.d.ts +12 -0
  13. package/dist/angular/ds-attachment-list.js +38 -0
  14. package/dist/angular/ds-avatar.d.ts +12 -0
  15. package/dist/angular/ds-avatar.js +38 -0
  16. package/dist/angular/ds-code-block.d.ts +12 -0
  17. package/dist/angular/ds-code-block.js +38 -0
  18. package/dist/angular/ds-conversation-list-item.d.ts +19 -0
  19. package/dist/angular/ds-conversation-list-item.js +42 -0
  20. package/dist/angular/ds-conversation-list-section.d.ts +12 -0
  21. package/dist/angular/ds-conversation-list-section.js +38 -0
  22. package/dist/angular/ds-conversation-list.d.ts +12 -0
  23. package/dist/angular/ds-conversation-list.js +37 -0
  24. package/dist/angular/ds-markdown.d.ts +12 -0
  25. package/dist/angular/ds-markdown.js +38 -0
  26. package/dist/angular/ds-menu.d.ts +1 -1
  27. package/dist/angular/ds-menu.js +3 -3
  28. package/dist/angular/ds-message-bubble.d.ts +12 -0
  29. package/dist/angular/ds-message-bubble.js +38 -0
  30. package/dist/angular/ds-message-composer.d.ts +23 -0
  31. package/dist/angular/ds-message-composer.js +49 -0
  32. package/dist/angular/ds-message-scroller.d.ts +15 -0
  33. package/dist/angular/ds-message-scroller.js +43 -0
  34. package/dist/angular/ds-message.d.ts +12 -0
  35. package/dist/angular/ds-message.js +38 -0
  36. package/dist/angular/ds-panel-tool-header.d.ts +25 -0
  37. package/dist/angular/ds-panel-tool-header.js +49 -0
  38. package/dist/angular/ds-panel-tool-search.d.ts +19 -0
  39. package/dist/angular/ds-panel-tool-search.js +49 -0
  40. package/dist/angular/ds-panel-tools.d.ts +30 -1
  41. package/dist/angular/ds-panel-tools.js +14 -5
  42. package/dist/angular/index.d.ts +1 -1
  43. package/dist/angular/index.js +17 -0
  44. package/dist/angular/proxies.d.ts +17 -0
  45. package/dist/angular/proxies.js +17 -0
  46. package/dist/components/ds-agent-activity.d.ts +11 -0
  47. package/dist/components/ds-agent-activity.js +2 -0
  48. package/dist/components/ds-agent-activity.js.map +1 -0
  49. package/dist/components/ds-agent-response.d.ts +11 -0
  50. package/dist/components/ds-agent-response.js +2 -0
  51. package/dist/components/ds-agent-response.js.map +1 -0
  52. package/dist/components/ds-agent-source-list.d.ts +11 -0
  53. package/dist/components/ds-agent-source-list.js +2 -0
  54. package/dist/components/ds-agent-source-list.js.map +1 -0
  55. package/dist/components/ds-agent-tool-call.d.ts +11 -0
  56. package/dist/components/ds-agent-tool-call.js +2 -0
  57. package/dist/components/ds-agent-tool-call.js.map +1 -0
  58. package/dist/components/ds-app-shell.js +1 -1
  59. package/dist/components/ds-app-shell.js.map +1 -1
  60. package/dist/components/ds-attachment-list.d.ts +11 -0
  61. package/dist/components/ds-attachment-list.js +2 -0
  62. package/dist/components/ds-attachment-list.js.map +1 -0
  63. package/dist/components/ds-avatar.d.ts +11 -0
  64. package/dist/components/ds-avatar.js +2 -0
  65. package/dist/components/ds-avatar.js.map +1 -0
  66. package/dist/components/ds-badge.js +1 -1
  67. package/dist/components/ds-banner.js +1 -1
  68. package/dist/components/ds-bar-nav.js +1 -1
  69. package/dist/components/ds-bar-nav.js.map +1 -1
  70. package/dist/components/ds-button-filled.js +1 -1
  71. package/dist/components/ds-button-unfilled.js +1 -1
  72. package/dist/components/ds-card-data-viz-donut.js +1 -1
  73. package/dist/components/ds-card-setting.js +1 -1
  74. package/dist/components/ds-card-shell-data-viz.js +1 -1
  75. package/dist/components/ds-card.js +1 -1
  76. package/dist/components/ds-chart-bar.js +1 -1
  77. package/dist/components/ds-chart-bar.js.map +1 -1
  78. package/dist/components/ds-chart-donut.js +1 -1
  79. package/dist/components/ds-chart-legend.js +1 -1
  80. package/dist/components/ds-chart-line.js +1 -1
  81. package/dist/components/ds-checkbox.js +1 -1
  82. package/dist/components/ds-chip.js +1 -1
  83. package/dist/components/ds-code-block.d.ts +11 -0
  84. package/dist/components/ds-code-block.js +2 -0
  85. package/dist/components/ds-code-block.js.map +1 -0
  86. package/dist/components/ds-conversation-list-item.d.ts +11 -0
  87. package/dist/components/ds-conversation-list-item.js +2 -0
  88. package/dist/components/ds-conversation-list-item.js.map +1 -0
  89. package/dist/components/ds-conversation-list-section.d.ts +11 -0
  90. package/dist/components/ds-conversation-list-section.js +2 -0
  91. package/dist/components/ds-conversation-list-section.js.map +1 -0
  92. package/dist/components/ds-conversation-list.d.ts +11 -0
  93. package/dist/components/ds-conversation-list.js +2 -0
  94. package/dist/components/ds-conversation-list.js.map +1 -0
  95. package/dist/components/ds-divider.js +1 -1
  96. package/dist/components/ds-divider.js.map +1 -1
  97. package/dist/components/ds-empty-state.js +1 -1
  98. package/dist/components/ds-field.js +1 -1
  99. package/dist/components/ds-icon.js +1 -1
  100. package/dist/components/ds-input.js +1 -1
  101. package/dist/components/ds-markdown.d.ts +11 -0
  102. package/dist/components/ds-markdown.js +2 -0
  103. package/dist/components/ds-markdown.js.map +1 -0
  104. package/dist/components/ds-menu.js +1 -1
  105. package/dist/components/ds-message-bubble.d.ts +11 -0
  106. package/dist/components/ds-message-bubble.js +2 -0
  107. package/dist/components/ds-message-bubble.js.map +1 -0
  108. package/dist/components/ds-message-composer.d.ts +11 -0
  109. package/dist/components/ds-message-composer.js +2 -0
  110. package/dist/components/ds-message-composer.js.map +1 -0
  111. package/dist/components/ds-message-scroller.d.ts +11 -0
  112. package/dist/components/ds-message-scroller.js +2 -0
  113. package/dist/components/ds-message-scroller.js.map +1 -0
  114. package/dist/components/ds-message.d.ts +11 -0
  115. package/dist/components/ds-message.js +2 -0
  116. package/dist/components/ds-message.js.map +1 -0
  117. package/dist/components/ds-modal.js +1 -1
  118. package/dist/components/ds-panel-nav.js +1 -1
  119. package/dist/components/ds-panel-nav.js.map +1 -1
  120. package/dist/components/ds-panel-sub-nav.js +1 -1
  121. package/dist/components/ds-panel-tool-header.d.ts +11 -0
  122. package/dist/components/ds-panel-tool-header.js +2 -0
  123. package/dist/components/ds-panel-tool-header.js.map +1 -0
  124. package/dist/components/ds-panel-tool-search.d.ts +11 -0
  125. package/dist/components/ds-panel-tool-search.js +2 -0
  126. package/dist/components/ds-panel-tool-search.js.map +1 -0
  127. package/dist/components/ds-panel-tools.js +1 -1
  128. package/dist/components/ds-panel-tools.js.map +1 -1
  129. package/dist/components/ds-radio.js +1 -1
  130. package/dist/components/ds-select-multi.js +1 -1
  131. package/dist/components/ds-select-multi.js.map +1 -1
  132. package/dist/components/ds-select.js +1 -1
  133. package/dist/components/ds-select.js.map +1 -1
  134. package/dist/components/ds-shell-gradient-picker.js +1 -1
  135. package/dist/components/ds-shell-gradient-picker.js.map +1 -1
  136. package/dist/components/ds-shell-gradient-swatch.js +1 -1
  137. package/dist/components/ds-skeleton.js +1 -1
  138. package/dist/components/ds-slider.js +1 -1
  139. package/dist/components/ds-slider.js.map +1 -1
  140. package/dist/components/ds-swatch-picker.js +1 -1
  141. package/dist/components/ds-switch.js +1 -1
  142. package/dist/components/ds-tab-group.js +1 -1
  143. package/dist/components/ds-tab-group.js.map +1 -1
  144. package/dist/components/ds-tag.js +1 -1
  145. package/dist/components/ds-text.js +1 -1
  146. package/dist/components/ds-tooltip-data-viz.js +1 -1
  147. package/dist/components/ds-tooltip.js +1 -1
  148. package/dist/components/index.d.ts +14 -0
  149. package/dist/components/p--KbMQblZ.js +2 -0
  150. package/dist/components/p--KbMQblZ.js.map +1 -0
  151. package/dist/components/p-2br9YDbG.js +2 -0
  152. package/dist/components/p-2br9YDbG.js.map +1 -0
  153. package/dist/components/p-41UAx2eq.js +2 -0
  154. package/dist/components/p-41UAx2eq.js.map +1 -0
  155. package/dist/components/{p-HBIxvS5C.js → p-B1pWGQn1.js} +2 -2
  156. package/dist/components/p-B7NbIYjS.js +2 -0
  157. package/dist/components/p-B7NbIYjS.js.map +1 -0
  158. package/dist/components/p-BBu_Svz7.js +2 -0
  159. package/dist/components/p-BBu_Svz7.js.map +1 -0
  160. package/dist/components/p-BEQB8EnB.js +2 -0
  161. package/dist/components/p-BEQB8EnB.js.map +1 -0
  162. package/dist/components/{p-CZ9wemOA.js → p-BFWC17em.js} +2 -2
  163. package/dist/components/{p-KRGBGTu5.js → p-BO6C8abO.js} +2 -2
  164. package/dist/components/{p-CbS_WWs2.js → p-BSOZXagj.js} +2 -2
  165. package/dist/components/p-BSOZXagj.js.map +1 -0
  166. package/dist/components/{p-DT2MK8Kt.js → p-BZAEZENv.js} +2 -2
  167. package/dist/components/p-BhC1YyId.js +2 -0
  168. package/dist/components/p-BhC1YyId.js.map +1 -0
  169. package/dist/components/p-BjtXYFhw.js +2 -0
  170. package/dist/components/p-BjtXYFhw.js.map +1 -0
  171. package/dist/components/{p-B-zhgAvL.js → p-Bo5wikia.js} +2 -2
  172. package/dist/components/p-Bx1G7P6W.js +2 -0
  173. package/dist/components/p-Bx1G7P6W.js.map +1 -0
  174. package/dist/components/p-C0sq948W.js +2 -0
  175. package/dist/components/p-C0sq948W.js.map +1 -0
  176. package/dist/components/p-CC1C8s6m.js +2 -0
  177. package/dist/components/p-CC1C8s6m.js.map +1 -0
  178. package/dist/components/p-CKaJx0IX.js +2 -0
  179. package/dist/components/p-CKaJx0IX.js.map +1 -0
  180. package/dist/components/{p-DyZj3k9S.js → p-CZMEhdlA.js} +2 -2
  181. package/dist/components/{p-BbV68F23.js → p-CiwdnKKp.js} +2 -2
  182. package/dist/components/{p-BLzivrE3.js → p-CnM9SW_8.js} +2 -2
  183. package/dist/components/{p-C7UFYvsO.js → p-CuKn9I5f.js} +2 -2
  184. package/dist/components/p-DFMM60My.js +2 -0
  185. package/dist/components/p-DFMM60My.js.map +1 -0
  186. package/dist/components/p-DIsDiGcp.js +2 -0
  187. package/dist/components/p-DIsDiGcp.js.map +1 -0
  188. package/dist/components/p-DVsje4AY.js +2 -0
  189. package/dist/components/p-DVsje4AY.js.map +1 -0
  190. package/dist/components/p-Di-ThR90.js +2 -0
  191. package/dist/components/p-Di-ThR90.js.map +1 -0
  192. package/dist/components/{p-C7RObioO.js → p-Di4HBckb.js} +2 -2
  193. package/dist/components/p-a9nGf1s6.js +2 -0
  194. package/dist/components/p-a9nGf1s6.js.map +1 -0
  195. package/dist/components/p-gidVv1FX.js +2 -0
  196. package/dist/components/p-gidVv1FX.js.map +1 -0
  197. package/dist/components/p-rEe-3KR_.js +2 -0
  198. package/dist/components/p-rEe-3KR_.js.map +1 -0
  199. package/dist/components/p-t9Ei1kHP.js +2 -0
  200. package/dist/components/p-t9Ei1kHP.js.map +1 -0
  201. package/dist/docs/components.json +7834 -3899
  202. package/dist/lib/components/Menu/menu-placement.d.ts +13 -0
  203. package/dist/lib/shell/index.d.ts +1 -1
  204. package/dist/lib/shell/index.js +8 -0
  205. package/dist/lib/shell/index.js.map +2 -2
  206. package/dist/mcp-data/registry/agent-activity.json +158 -0
  207. package/dist/mcp-data/registry/agent-response.json +211 -0
  208. package/dist/mcp-data/registry/agent-source-list.json +153 -0
  209. package/dist/mcp-data/registry/agent-tool-call.json +213 -0
  210. package/dist/mcp-data/registry/app-shell.json +2 -2
  211. package/dist/mcp-data/registry/attachment-list.json +137 -0
  212. package/dist/mcp-data/registry/avatar.json +136 -0
  213. package/dist/mcp-data/registry/bar-nav.json +2 -2
  214. package/dist/mcp-data/registry/code-block.json +150 -0
  215. package/dist/mcp-data/registry/conversation-list-item.json +265 -0
  216. package/dist/mcp-data/registry/conversation-list-section.json +139 -0
  217. package/dist/mcp-data/registry/conversation-list.json +101 -0
  218. package/dist/mcp-data/registry/markdown.json +141 -0
  219. package/dist/mcp-data/registry/menu.json +27 -7
  220. package/dist/mcp-data/registry/message-bubble.json +119 -0
  221. package/dist/mcp-data/registry/message-composer.json +265 -0
  222. package/dist/mcp-data/registry/message-scroller.json +217 -0
  223. package/dist/mcp-data/registry/message.json +255 -0
  224. package/dist/mcp-data/registry/panel-nav.json +2 -2
  225. package/dist/mcp-data/registry/panel-tool-header.json +342 -0
  226. package/dist/mcp-data/registry/panel-tool-search.json +387 -0
  227. package/dist/mcp-data/registry/panel-tools.json +150 -10
  228. package/dist/mcp-data/registry/registry.json +2146 -619
  229. package/dist/mcp-data/registry/select.json +1 -1
  230. package/dist/mcp-data/registry/slider.json +4 -4
  231. package/dist/mcp-data/registry/tab-group.json +2 -2
  232. package/dist/mcp-data/registry/text.json +1 -1
  233. package/dist/react/components.d.ts +17 -0
  234. package/dist/react/components.js +17 -0
  235. package/dist/react/ds-agent-activity.d.ts +9 -0
  236. package/dist/react/ds-agent-activity.js +12 -0
  237. package/dist/react/ds-agent-response.d.ts +9 -0
  238. package/dist/react/ds-agent-response.js +12 -0
  239. package/dist/react/ds-agent-source-list.d.ts +9 -0
  240. package/dist/react/ds-agent-source-list.js +12 -0
  241. package/dist/react/ds-agent-tool-call.d.ts +9 -0
  242. package/dist/react/ds-agent-tool-call.js +12 -0
  243. package/dist/react/ds-attachment-list.d.ts +9 -0
  244. package/dist/react/ds-attachment-list.js +12 -0
  245. package/dist/react/ds-avatar.d.ts +9 -0
  246. package/dist/react/ds-avatar.js +12 -0
  247. package/dist/react/ds-code-block.d.ts +9 -0
  248. package/dist/react/ds-code-block.js +12 -0
  249. package/dist/react/ds-conversation-list-item.d.ts +14 -0
  250. package/dist/react/ds-conversation-list-item.js +12 -0
  251. package/dist/react/ds-conversation-list-section.d.ts +9 -0
  252. package/dist/react/ds-conversation-list-section.js +12 -0
  253. package/dist/react/ds-conversation-list.d.ts +9 -0
  254. package/dist/react/ds-conversation-list.js +12 -0
  255. package/dist/react/ds-markdown.d.ts +9 -0
  256. package/dist/react/ds-markdown.js +12 -0
  257. package/dist/react/ds-message-bubble.d.ts +9 -0
  258. package/dist/react/ds-message-bubble.js +12 -0
  259. package/dist/react/ds-message-composer.d.ts +16 -0
  260. package/dist/react/ds-message-composer.js +16 -0
  261. package/dist/react/ds-message-scroller.d.ts +12 -0
  262. package/dist/react/ds-message-scroller.js +12 -0
  263. package/dist/react/ds-message.d.ts +9 -0
  264. package/dist/react/ds-message.js +12 -0
  265. package/dist/react/ds-panel-tool-header.d.ts +17 -0
  266. package/dist/react/ds-panel-tool-header.js +16 -0
  267. package/dist/react/ds-panel-tool-search.d.ts +14 -0
  268. package/dist/react/ds-panel-tool-search.js +16 -0
  269. package/dist/react/ds-panel-tools.d.ts +10 -0
  270. package/dist/react/ds-panel-tools.js +3 -0
  271. package/dist/types/components/AgentActivity/AgentActivity.d.ts +14 -0
  272. package/dist/types/components/AgentResponse/AgentResponse.d.ts +12 -0
  273. package/dist/types/components/AgentSourceList/AgentSourceList.d.ts +9 -0
  274. package/dist/types/components/AgentToolCall/AgentToolCall.d.ts +13 -0
  275. package/dist/types/components/AppShell/AppShell.d.ts +4 -0
  276. package/dist/types/components/AttachmentList/AttachmentList.d.ts +8 -0
  277. package/dist/types/components/Avatar/Avatar.d.ts +8 -0
  278. package/dist/types/components/CodeBlock/CodeBlock.d.ts +11 -0
  279. package/dist/types/components/ConversationList/ConversationList.d.ts +4 -0
  280. package/dist/types/components/ConversationListItem/ConversationListItem.d.ts +22 -0
  281. package/dist/types/components/ConversationListSection/ConversationListSection.d.ts +9 -0
  282. package/dist/types/components/Markdown/Markdown.d.ts +15 -0
  283. package/dist/types/components/Menu/Menu.d.ts +3 -0
  284. package/dist/types/components/Menu/menu-placement.d.ts +13 -0
  285. package/dist/types/components/Menu/menu-types.d.ts +1 -1
  286. package/dist/types/components/Message/Message.d.ts +17 -0
  287. package/dist/types/components/MessageBubble/MessageBubble.d.ts +6 -0
  288. package/dist/types/components/MessageComposer/MessageComposer.d.ts +29 -0
  289. package/dist/types/components/MessageScroller/MessageScroller.d.ts +37 -0
  290. package/dist/types/components/PanelNav/PanelNav.d.ts +3 -0
  291. package/dist/types/components/PanelToolHeader/PanelToolHeader.d.ts +34 -0
  292. package/dist/types/components/PanelToolSearch/PanelToolSearch.d.ts +35 -0
  293. package/dist/types/components/PanelTools/PanelTools.d.ts +40 -1
  294. package/dist/types/components/PanelTools/panel-tools-types.d.ts +20 -0
  295. package/dist/types/components/PanelTools/panel-tools-utils.d.ts +2 -0
  296. package/dist/types/components/Slider/Slider.d.ts +1 -1
  297. package/dist/types/components/conversation-types.d.ts +55 -0
  298. package/dist/types/components.d.ts +1185 -8
  299. package/dist/types/shell/index.d.ts +1 -1
  300. package/dist/types/utils/choice-list-parts.d.ts +4 -1
  301. package/package.json +4 -1
  302. package/dist/components/p-CXF8yd-g.js +0 -2
  303. package/dist/components/p-CXF8yd-g.js.map +0 -1
  304. package/dist/components/p-CbS_WWs2.js.map +0 -1
  305. package/dist/components/p-VKIsOVza.js +0 -2
  306. package/dist/components/p-VKIsOVza.js.map +0 -1
  307. package/dist/components/p-tQ1QHeOE.js +0 -2
  308. package/dist/components/p-tQ1QHeOE.js.map +0 -1
  309. /package/dist/components/{p-HBIxvS5C.js.map → p-B1pWGQn1.js.map} +0 -0
  310. /package/dist/components/{p-CZ9wemOA.js.map → p-BFWC17em.js.map} +0 -0
  311. /package/dist/components/{p-KRGBGTu5.js.map → p-BO6C8abO.js.map} +0 -0
  312. /package/dist/components/{p-DT2MK8Kt.js.map → p-BZAEZENv.js.map} +0 -0
  313. /package/dist/components/{p-B-zhgAvL.js.map → p-Bo5wikia.js.map} +0 -0
  314. /package/dist/components/{p-DyZj3k9S.js.map → p-CZMEhdlA.js.map} +0 -0
  315. /package/dist/components/{p-BbV68F23.js.map → p-CiwdnKKp.js.map} +0 -0
  316. /package/dist/components/{p-BLzivrE3.js.map → p-CnM9SW_8.js.map} +0 -0
  317. /package/dist/components/{p-C7UFYvsO.js.map → p-CuKn9I5f.js.map} +0 -0
  318. /package/dist/components/{p-C7RObioO.js.map → p-Di4HBckb.js.map} +0 -0
@@ -0,0 +1,265 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "message-composer",
4
+ "title": "MessageComposer",
5
+ "description": "Two-line auto-growing conversation textarea with submit, stop, error, and extensible leading-tool or trailing-action regions.",
6
+ "type": "registry:ui",
7
+ "meta": {
8
+ "library": "@ds-mo/ui",
9
+ "distribution": "npm",
10
+ "storybook": "https://zainadeel.github.io/compomo/?path=/story/message-composer",
11
+ "source": "src/wc/components/MessageComposer/MessageComposer.tsx",
12
+ "intentStatus": "complete",
13
+ "intent": {
14
+ "audience": "general",
15
+ "status": "experimental",
16
+ "summary": "Two-line auto-growing conversation textarea with submit, stop, error, and extensible leading-tool or trailing-action regions.",
17
+ "useWhen": [
18
+ "A conversation accepts multi-line text and needs Enter-to-submit with a streaming stop action."
19
+ ],
20
+ "avoidWhen": [
21
+ "The text is a conventional form field submitted with unrelated fields."
22
+ ],
23
+ "commonlyComposedWith": [
24
+ "component:ds-message-scroller",
25
+ "component:ds-button-filled",
26
+ "component:ds-button-unfilled"
27
+ ],
28
+ "patterns": [
29
+ "pattern:conversation"
30
+ ],
31
+ "accessibility": [
32
+ "Always provide a localized textarea label and submit or stop action names.",
33
+ "Shift plus Enter inserts a newline while Enter submits non-empty content.",
34
+ "Streaming and error states remain perceivable without relying on icon shape alone."
35
+ ],
36
+ "states": [
37
+ "The owner controls draft value and transport state; the composer emits intent without performing transport.",
38
+ "Use the tools slot for leading actions such as add and the actions slot for trailing actions such as dictation; the built-in submit or stop action remains last.",
39
+ "The submit action defaults to brand for both agent and person-to-person conversations; change submitIntent only when the action consequence has a different semantic intent."
40
+ ],
41
+ "responsiveBehavior": [
42
+ "The textarea begins at two body-medium lines, grows one line at a time through six lines, and then scrolls internally."
43
+ ],
44
+ "references": [
45
+ {
46
+ "label": "Storybook examples",
47
+ "path": "src/wc/components/MessageComposer/MessageComposer.stories.ts"
48
+ },
49
+ {
50
+ "label": "Scroller overlay composition",
51
+ "path": "src/wc/components/MessageScroller/MessageScroller.stories.ts"
52
+ }
53
+ ]
54
+ },
55
+ "api": {
56
+ "props": {
57
+ "isInactive": {
58
+ "type": "boolean",
59
+ "resolvedType": "boolean",
60
+ "attribute": "is-inactive",
61
+ "default": "false",
62
+ "required": false,
63
+ "mutable": false
64
+ },
65
+ "label": {
66
+ "type": "string",
67
+ "resolvedType": "string",
68
+ "attribute": "label",
69
+ "default": "'Message'",
70
+ "required": false,
71
+ "mutable": false
72
+ },
73
+ "placeholder": {
74
+ "type": "string",
75
+ "resolvedType": "string",
76
+ "attribute": "placeholder",
77
+ "default": "'Write a message'",
78
+ "required": false,
79
+ "mutable": false
80
+ },
81
+ "status": {
82
+ "type": "MessageComposerStatus",
83
+ "resolvedType": "\"error\" | \"ready\" | \"streaming\" | \"submitted\"",
84
+ "attribute": "status",
85
+ "default": "'ready'",
86
+ "required": false,
87
+ "mutable": false
88
+ },
89
+ "submitIntent": {
90
+ "type": "ButtonFilledIntent",
91
+ "resolvedType": "\"ai\" | \"brand\" | \"caution\" | \"guide\" | \"negative\" | \"neutral\" | \"positive\" | \"walkthrough\" | \"warning\"",
92
+ "attribute": "submit-intent",
93
+ "default": "'brand'",
94
+ "required": false,
95
+ "mutable": false
96
+ },
97
+ "value": {
98
+ "type": "string",
99
+ "resolvedType": "string",
100
+ "attribute": "value",
101
+ "default": "''",
102
+ "required": false,
103
+ "mutable": true
104
+ }
105
+ },
106
+ "events": [
107
+ {
108
+ "name": "dsInput",
109
+ "detail": "string",
110
+ "bubbles": true,
111
+ "cancelable": true,
112
+ "composed": true
113
+ },
114
+ {
115
+ "name": "dsStop",
116
+ "detail": "void",
117
+ "bubbles": true,
118
+ "cancelable": true,
119
+ "composed": true
120
+ },
121
+ {
122
+ "name": "dsSubmit",
123
+ "detail": "{ text: string }",
124
+ "bubbles": true,
125
+ "cancelable": true,
126
+ "composed": true
127
+ }
128
+ ],
129
+ "methods": [
130
+ {
131
+ "name": "setFocus",
132
+ "signature": "setFocus() => Promise<void>"
133
+ }
134
+ ],
135
+ "slots": []
136
+ },
137
+ "props": {
138
+ "isInactive": {
139
+ "type": "boolean",
140
+ "resolvedType": "boolean",
141
+ "attribute": "is-inactive",
142
+ "default": "false",
143
+ "required": false,
144
+ "mutable": false
145
+ },
146
+ "label": {
147
+ "type": "string",
148
+ "resolvedType": "string",
149
+ "attribute": "label",
150
+ "default": "'Message'",
151
+ "required": false,
152
+ "mutable": false
153
+ },
154
+ "placeholder": {
155
+ "type": "string",
156
+ "resolvedType": "string",
157
+ "attribute": "placeholder",
158
+ "default": "'Write a message'",
159
+ "required": false,
160
+ "mutable": false
161
+ },
162
+ "status": {
163
+ "type": "MessageComposerStatus",
164
+ "resolvedType": "\"error\" | \"ready\" | \"streaming\" | \"submitted\"",
165
+ "attribute": "status",
166
+ "default": "'ready'",
167
+ "required": false,
168
+ "mutable": false
169
+ },
170
+ "submitIntent": {
171
+ "type": "ButtonFilledIntent",
172
+ "resolvedType": "\"ai\" | \"brand\" | \"caution\" | \"guide\" | \"negative\" | \"neutral\" | \"positive\" | \"walkthrough\" | \"warning\"",
173
+ "attribute": "submit-intent",
174
+ "default": "'brand'",
175
+ "required": false,
176
+ "mutable": false
177
+ },
178
+ "value": {
179
+ "type": "string",
180
+ "resolvedType": "string",
181
+ "attribute": "value",
182
+ "default": "''",
183
+ "required": false,
184
+ "mutable": true
185
+ }
186
+ },
187
+ "events": [
188
+ {
189
+ "name": "dsInput",
190
+ "detail": "string",
191
+ "bubbles": true,
192
+ "cancelable": true,
193
+ "composed": true
194
+ },
195
+ {
196
+ "name": "dsStop",
197
+ "detail": "void",
198
+ "bubbles": true,
199
+ "cancelable": true,
200
+ "composed": true
201
+ },
202
+ {
203
+ "name": "dsSubmit",
204
+ "detail": "{ text: string }",
205
+ "bubbles": true,
206
+ "cancelable": true,
207
+ "composed": true
208
+ }
209
+ ],
210
+ "methods": [
211
+ {
212
+ "name": "setFocus",
213
+ "signature": "setFocus() => Promise<void>"
214
+ }
215
+ ],
216
+ "slots": [],
217
+ "exports": {
218
+ "customElement": "ds-message-composer",
219
+ "react": "DsMessageComposer",
220
+ "angular": "DsMessageComposer"
221
+ },
222
+ "consumption": {
223
+ "install": "npm install @ds-mo/ui @ds-mo/tokens @ds-mo/icons",
224
+ "cssSetup": "import '@ds-mo/tokens';\nimport '@ds-mo/tokens/reset';\nimport '@ds-mo/tokens/globals';",
225
+ "customElements": {
226
+ "import": "import '@ds-mo/ui/dist/components/ds-message-composer.js';",
227
+ "example": "<ds-message-composer></ds-message-composer>"
228
+ },
229
+ "react": {
230
+ "import": "import { DsMessageComposer } from '@ds-mo/ui/react';",
231
+ "example": "<DsMessageComposer />"
232
+ },
233
+ "angular": {
234
+ "import": "import { DsMessageComposer } from '@ds-mo/ui/angular/ds-message-composer';",
235
+ "example": "<ds-message-composer></ds-message-composer>"
236
+ },
237
+ "peerDependencies": {
238
+ "required": [
239
+ "@ds-mo/tokens ^3.1.1",
240
+ "@ds-mo/icons ^6.2.0"
241
+ ],
242
+ "frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
243
+ }
244
+ }
245
+ },
246
+ "dependencies": [
247
+ "@ds-mo/ui",
248
+ "@ds-mo/tokens"
249
+ ],
250
+ "registryDependencies": [
251
+ "button-filled"
252
+ ],
253
+ "files": [
254
+ {
255
+ "path": "src/wc/components/MessageComposer/MessageComposer.css",
256
+ "content": ":host {\n display: block;\n width: 100%;\n box-sizing: border-box;\n}\n\n.message-composer {\n display: flex;\n flex-direction: column;\n gap: var(--dimension-space-050);\n width: 100%;\n margin: 0;\n}\n\n.message-composer__attachments:empty,\n.message-composer__tools:empty {\n display: none;\n}\n\n.message-composer__field {\n display: flex;\n flex-direction: column;\n gap: var(--dimension-space-050);\n padding: var(--dimension-space-100);\n border-radius: var(--dimension-radius-300);\n background: var(--color-background-primary);\n box-shadow: var(--effect-elevation-elevated-sm);\n overflow: hidden;\n}\n\n.message-composer--error .message-composer__field {\n box-shadow: var(--effect-elevation-elevated-sm),\n inset 0 0 0 var(--dimension-stroke-width-025) var(--color-border-bold-negative);\n}\n\ntextarea {\n width: 100%;\n min-height: calc(var(--typography-lineheight-md) * 2 + var(--dimension-space-150));\n max-height: calc(var(--typography-lineheight-md) * 6 + var(--dimension-space-150));\n padding: var(--dimension-space-075);\n border: none;\n outline: none;\n resize: none;\n overflow-y: auto;\n box-sizing: border-box;\n background: transparent;\n color: var(--color-foreground-primary);\n font-family: var(--typography-font-family);\n font-size: var(--typography-fontsize-md);\n line-height: var(--typography-lineheight-md);\n font-weight: var(--typography-weight-regular);\n letter-spacing: var(--typography-letterspacing-negative-half);\n}\n\ntextarea::placeholder {\n color: var(--color-foreground-secondary);\n}\n\ntextarea:focus::placeholder {\n color: var(--color-foreground-quaternary);\n}\n\n.message-composer__tools {\n display: flex;\n align-items: center;\n gap: var(--dimension-space-050);\n min-height: var(--dimension-size-400);\n}\n\n.message-composer__controls {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--dimension-space-050);\n width: 100%;\n min-width: 0;\n}\n\n.message-composer__actions {\n display: flex;\n align-items: center;\n gap: var(--dimension-space-050);\n flex: 0 0 auto;\n margin-inline-start: auto;\n}\n",
257
+ "type": "registry:ui"
258
+ },
259
+ {
260
+ "path": "src/wc/components/MessageComposer/MessageComposer.tsx",
261
+ "content": "import {\n Component,\n Element,\n Event,\n EventEmitter,\n h,\n Host,\n Method,\n Prop,\n Watch,\n} from '@stencil/core';\nimport type { ButtonFilledIntent } from '../ButtonFilled/ButtonFilled';\nimport type { MessageComposerStatus } from '../conversation-types';\n\n@Component({\n tag: 'ds-message-composer',\n styleUrl: 'MessageComposer.css',\n scoped: true,\n})\nexport class MessageComposer {\n @Element() el!: HTMLElement;\n\n @Prop({ mutable: true }) value: string = '';\n @Prop() placeholder: string = 'Write a message';\n @Prop() label: string = 'Message';\n @Prop() status: MessageComposerStatus = 'ready';\n @Prop() isInactive: boolean = false;\n @Prop() submitIntent: ButtonFilledIntent = 'brand';\n\n @Event() dsInput!: EventEmitter<string>;\n @Event() dsSubmit!: EventEmitter<{ text: string }>;\n @Event() dsStop!: EventEmitter<void>;\n\n private textarea?: HTMLTextAreaElement;\n\n componentDidLoad() {\n this.resize();\n }\n\n @Watch('value')\n onValueChange() {\n requestAnimationFrame(() => this.resize());\n }\n\n @Method()\n async setFocus() {\n this.textarea?.focus();\n }\n\n private get streaming(): boolean {\n return this.status === 'submitted' || this.status === 'streaming';\n }\n\n private resize() {\n if (!this.textarea) return;\n this.textarea.style.height = 'auto';\n this.textarea.style.height = `${this.textarea.scrollHeight}px`;\n }\n\n private handleInput = (event: Event) => {\n this.value = (event.target as HTMLTextAreaElement).value;\n this.resize();\n this.dsInput.emit(this.value);\n };\n\n private submit() {\n const text = this.value.trim();\n if (!text || this.isInactive || this.streaming) return;\n this.dsSubmit.emit({ text });\n }\n\n private handleKeyDown = (event: KeyboardEvent) => {\n if (event.key !== 'Enter' || event.shiftKey || event.isComposing) return;\n event.preventDefault();\n this.submit();\n };\n\n private handleAction = () => {\n if (this.streaming) this.dsStop.emit();\n else this.submit();\n };\n\n render() {\n const error = this.status === 'error';\n return (\n <Host>\n <form\n class={{ 'message-composer': true, 'message-composer--error': error }}\n aria-busy={this.streaming ? 'true' : undefined}\n onSubmit={(event: Event) => {\n event.preventDefault();\n this.submit();\n }}\n >\n <div class=\"message-composer__attachments\">\n <slot name=\"attachments\" />\n </div>\n <div class=\"message-composer__field\">\n <textarea\n ref={element => {\n this.textarea = element;\n }}\n value={this.value}\n placeholder={this.placeholder}\n aria-label={this.label}\n aria-invalid={error ? 'true' : undefined}\n disabled={this.isInactive}\n rows={2}\n onInput={this.handleInput}\n onKeyDown={this.handleKeyDown}\n />\n <div class=\"message-composer__controls\">\n <div class=\"message-composer__tools\">\n <slot name=\"tools\" />\n </div>\n <div class=\"message-composer__actions\">\n <slot name=\"actions\" />\n <ds-button-filled\n class=\"message-composer__action\"\n variant=\"icon\"\n icon={this.streaming ? 'Pause' : 'PaperplaneSend'}\n intent={this.streaming ? 'neutral' : this.submitIntent}\n size=\"md\"\n rounded\n aria-label={this.streaming ? 'Stop response' : 'Send message'}\n isInactive={this.isInactive || (!this.streaming && !this.value.trim())}\n onDsClick={this.handleAction}\n />\n </div>\n </div>\n </div>\n </form>\n </Host>\n );\n }\n}\n",
262
+ "type": "registry:ui"
263
+ }
264
+ ]
265
+ }
@@ -0,0 +1,217 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "message-scroller",
4
+ "title": "MessageScroller",
5
+ "description": "Focusable transcript scroller that follows the live edge only while the reader remains there.",
6
+ "type": "registry:ui",
7
+ "meta": {
8
+ "library": "@ds-mo/ui",
9
+ "distribution": "npm",
10
+ "storybook": "https://zainadeel.github.io/compomo/?path=/story/message-scroller",
11
+ "source": "src/wc/components/MessageScroller/MessageScroller.tsx",
12
+ "intentStatus": "complete",
13
+ "intent": {
14
+ "audience": "general",
15
+ "status": "experimental",
16
+ "summary": "Focusable transcript scroller that follows the live edge only while the reader remains there.",
17
+ "useWhen": [
18
+ "Messages can append or stream and transcript position must remain respectful of reader intent."
19
+ ],
20
+ "avoidWhen": [
21
+ "Content is static prose without message navigation or live updates."
22
+ ],
23
+ "commonlyComposedWith": [
24
+ "component:ds-message",
25
+ "component:ds-message-composer"
26
+ ],
27
+ "patterns": [
28
+ "pattern:conversation"
29
+ ],
30
+ "accessibility": [
31
+ "Give each transcript a concise localized label.",
32
+ "New content is exposed through log semantics without stealing focus.",
33
+ "The scroll-to-latest control remains keyboard accessible when follow behavior is released."
34
+ ],
35
+ "states": [
36
+ "Selection and transcript interaction release automatic following.",
37
+ "Opening saved history, message jumps, and prepended history preserve deliberate position.",
38
+ "The scroll-to-latest action is absent at the live edge, appears only after the reader moves away, and disappears again after returning to the newest message.",
39
+ "Place a composer container in the overlay slot so messages scroll behind it; the scroller measures its live height, reserves matching transcript clearance, and keeps scroll-to-latest above it."
40
+ ],
41
+ "responsiveBehavior": [
42
+ "Transcript width is readable while the scroll region fills available height."
43
+ ],
44
+ "references": [
45
+ {
46
+ "label": "Storybook examples",
47
+ "path": "src/wc/components/MessageScroller/MessageScroller.stories.ts"
48
+ }
49
+ ]
50
+ },
51
+ "api": {
52
+ "props": {
53
+ "autoFollow": {
54
+ "type": "boolean",
55
+ "resolvedType": "boolean",
56
+ "attribute": "auto-follow",
57
+ "default": "true",
58
+ "required": false,
59
+ "mutable": false
60
+ },
61
+ "busy": {
62
+ "type": "boolean",
63
+ "resolvedType": "boolean",
64
+ "attribute": "busy",
65
+ "default": "false",
66
+ "required": false,
67
+ "mutable": false
68
+ },
69
+ "defaultPosition": {
70
+ "type": "MessageScrollerPosition",
71
+ "resolvedType": "\"end\" | \"last-anchor\" | \"start\"",
72
+ "attribute": "default-position",
73
+ "default": "'last-anchor'",
74
+ "required": false,
75
+ "mutable": false
76
+ },
77
+ "messagesLabel": {
78
+ "type": "string",
79
+ "resolvedType": "string",
80
+ "attribute": "messages-label",
81
+ "default": "'Messages'",
82
+ "required": false,
83
+ "mutable": false
84
+ }
85
+ },
86
+ "events": [
87
+ {
88
+ "name": "dsReachStart",
89
+ "detail": "void",
90
+ "bubbles": true,
91
+ "cancelable": true,
92
+ "composed": true
93
+ }
94
+ ],
95
+ "methods": [
96
+ {
97
+ "name": "scrollToEnd",
98
+ "signature": "scrollToEnd() => Promise<void>"
99
+ },
100
+ {
101
+ "name": "scrollToMessage",
102
+ "signature": "scrollToMessage(id: string) => Promise<boolean>"
103
+ },
104
+ {
105
+ "name": "scrollToStart",
106
+ "signature": "scrollToStart() => Promise<void>"
107
+ }
108
+ ],
109
+ "slots": []
110
+ },
111
+ "props": {
112
+ "autoFollow": {
113
+ "type": "boolean",
114
+ "resolvedType": "boolean",
115
+ "attribute": "auto-follow",
116
+ "default": "true",
117
+ "required": false,
118
+ "mutable": false
119
+ },
120
+ "busy": {
121
+ "type": "boolean",
122
+ "resolvedType": "boolean",
123
+ "attribute": "busy",
124
+ "default": "false",
125
+ "required": false,
126
+ "mutable": false
127
+ },
128
+ "defaultPosition": {
129
+ "type": "MessageScrollerPosition",
130
+ "resolvedType": "\"end\" | \"last-anchor\" | \"start\"",
131
+ "attribute": "default-position",
132
+ "default": "'last-anchor'",
133
+ "required": false,
134
+ "mutable": false
135
+ },
136
+ "messagesLabel": {
137
+ "type": "string",
138
+ "resolvedType": "string",
139
+ "attribute": "messages-label",
140
+ "default": "'Messages'",
141
+ "required": false,
142
+ "mutable": false
143
+ }
144
+ },
145
+ "events": [
146
+ {
147
+ "name": "dsReachStart",
148
+ "detail": "void",
149
+ "bubbles": true,
150
+ "cancelable": true,
151
+ "composed": true
152
+ }
153
+ ],
154
+ "methods": [
155
+ {
156
+ "name": "scrollToEnd",
157
+ "signature": "scrollToEnd() => Promise<void>"
158
+ },
159
+ {
160
+ "name": "scrollToMessage",
161
+ "signature": "scrollToMessage(id: string) => Promise<boolean>"
162
+ },
163
+ {
164
+ "name": "scrollToStart",
165
+ "signature": "scrollToStart() => Promise<void>"
166
+ }
167
+ ],
168
+ "slots": [],
169
+ "exports": {
170
+ "customElement": "ds-message-scroller",
171
+ "react": "DsMessageScroller",
172
+ "angular": "DsMessageScroller"
173
+ },
174
+ "consumption": {
175
+ "install": "npm install @ds-mo/ui @ds-mo/tokens @ds-mo/icons",
176
+ "cssSetup": "import '@ds-mo/tokens';\nimport '@ds-mo/tokens/reset';\nimport '@ds-mo/tokens/globals';",
177
+ "customElements": {
178
+ "import": "import '@ds-mo/ui/dist/components/ds-message-scroller.js';",
179
+ "example": "<ds-message-scroller></ds-message-scroller>"
180
+ },
181
+ "react": {
182
+ "import": "import { DsMessageScroller } from '@ds-mo/ui/react';",
183
+ "example": "<DsMessageScroller />"
184
+ },
185
+ "angular": {
186
+ "import": "import { DsMessageScroller } from '@ds-mo/ui/angular/ds-message-scroller';",
187
+ "example": "<ds-message-scroller></ds-message-scroller>"
188
+ },
189
+ "peerDependencies": {
190
+ "required": [
191
+ "@ds-mo/tokens ^3.1.1",
192
+ "@ds-mo/icons ^6.2.0"
193
+ ],
194
+ "frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
195
+ }
196
+ }
197
+ },
198
+ "dependencies": [
199
+ "@ds-mo/ui",
200
+ "@ds-mo/tokens"
201
+ ],
202
+ "registryDependencies": [
203
+ "button-unfilled"
204
+ ],
205
+ "files": [
206
+ {
207
+ "path": "src/wc/components/MessageScroller/MessageScroller.css",
208
+ "content": ":host {\n display: block;\n width: 100%;\n height: 100%;\n min-height: 0;\n box-sizing: border-box;\n}\n\n.message-scroller {\n position: relative;\n width: 100%;\n height: 100%;\n min-height: 0;\n}\n\n.message-scroller__viewport {\n width: 100%;\n height: 100%;\n min-height: 0;\n overflow: auto;\n overscroll-behavior: contain;\n box-sizing: border-box;\n}\n\n.message-scroller__viewport:focus-visible {\n outline: var(--dimension-stroke-width-025) solid var(--color-border-bold-brand);\n outline-offset: calc(-1 * var(--dimension-stroke-width-025));\n}\n\n.message-scroller__content {\n display: flex;\n flex-direction: column;\n gap: var(--dimension-space-150);\n width: 100%;\n max-width: var(--dimension-panel-width-2xl);\n min-height: 100%;\n margin-inline: auto;\n padding-block-start: var(--dimension-space-200);\n padding-inline: var(--dimension-space-200);\n padding-block-end: calc(\n var(--dimension-space-200) + var(--ds-message-scroller-overlay-block-size, 0px)\n );\n box-sizing: border-box;\n}\n\n.message-scroller__overlay {\n position: absolute;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: var(--dimension-z-index-floating);\n}\n\n.message-scroller__control {\n position: absolute;\n right: var(--dimension-space-100);\n bottom: calc(var(--ds-message-scroller-overlay-block-size, 0px) + var(--dimension-space-100));\n z-index: var(--dimension-z-index-floating);\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: var(--dimension-radius-half);\n background: var(--color-background-transparent);\n box-shadow: var(--effect-elevation-elevated-md);\n -webkit-backdrop-filter: blur(var(--effect-blur-sm));\n backdrop-filter: blur(var(--effect-blur-sm));\n}\n",
209
+ "type": "registry:ui"
210
+ },
211
+ {
212
+ "path": "src/wc/components/MessageScroller/MessageScroller.tsx",
213
+ "content": "import {\n Component,\n Element,\n Event,\n EventEmitter,\n h,\n Host,\n Method,\n Prop,\n State,\n} from '@stencil/core';\nimport type { MessageScrollerPosition } from '../conversation-types';\n\nconst LIVE_EDGE_THRESHOLD = 24;\n\n@Component({\n tag: 'ds-message-scroller',\n styleUrl: 'MessageScroller.css',\n scoped: true,\n})\nexport class MessageScroller {\n @Element() el!: HTMLElement;\n\n @Prop() busy: boolean = false;\n @Prop() autoFollow: boolean = true;\n @Prop() defaultPosition: MessageScrollerPosition = 'last-anchor';\n @Prop() messagesLabel: string = 'Messages';\n\n @Event() dsReachStart!: EventEmitter<void>;\n\n @State() private showScrollToLatest = false;\n\n private viewport?: HTMLElement;\n private content?: HTMLElement;\n private overlay?: HTMLElement;\n private resizeObserver?: ResizeObserver;\n private overlayResizeObserver?: ResizeObserver;\n private mutationObserver?: MutationObserver;\n private following = true;\n private atStart = false;\n private programmatic = false;\n private previousScrollHeight = 0;\n\n componentDidLoad() {\n this.connectObservers();\n requestAnimationFrame(() => this.applyDefaultPosition());\n document.addEventListener('selectionchange', this.handleSelectionChange);\n }\n\n disconnectedCallback() {\n this.resizeObserver?.disconnect();\n this.overlayResizeObserver?.disconnect();\n this.mutationObserver?.disconnect();\n document.removeEventListener('selectionchange', this.handleSelectionChange);\n }\n\n @Method()\n async scrollToMessage(id: string): Promise<boolean> {\n const message = Array.from(\n this.el.querySelectorAll<HTMLElement & { messageId?: string }>('ds-message')\n ).find(item => item.messageId === id || item.getAttribute('message-id') === id);\n if (!message) return false;\n this.following = false;\n this.showScrollToLatest = true;\n message.scrollIntoView({ block: 'start', behavior: this.motionBehavior() });\n return true;\n }\n\n @Method()\n async scrollToStart() {\n this.scrollTo(0);\n this.following = false;\n this.showScrollToLatest = Boolean(\n this.viewport && this.viewport.scrollHeight > this.viewport.clientHeight\n );\n }\n\n @Method()\n async scrollToEnd() {\n this.following = true;\n this.showScrollToLatest = false;\n this.scrollTo(this.viewport?.scrollHeight ?? 0);\n }\n\n private motionBehavior(): ScrollBehavior {\n return matchMedia('(prefers-reduced-motion: reduce)').matches ? 'auto' : 'smooth';\n }\n\n private scrollTo(top: number) {\n if (!this.viewport) return;\n this.programmatic = true;\n this.viewport.scrollTo({ top, behavior: this.motionBehavior() });\n requestAnimationFrame(() => {\n this.programmatic = false;\n });\n }\n\n private connectObservers() {\n if (!this.viewport || !this.content) return;\n this.previousScrollHeight = this.viewport.scrollHeight;\n this.resizeObserver = new ResizeObserver(() => this.handleContentGrowth());\n this.resizeObserver.observe(this.content);\n if (this.overlay) {\n this.overlayResizeObserver = new ResizeObserver(entries => {\n const blockSize = Math.ceil(entries[0]?.contentRect.height ?? 0);\n this.el.style.setProperty('--ds-message-scroller-overlay-block-size', `${blockSize}px`);\n this.handleContentGrowth();\n });\n this.overlayResizeObserver.observe(this.overlay);\n }\n this.mutationObserver = new MutationObserver(() => this.handleContentGrowth());\n this.mutationObserver.observe(this.content, {\n childList: true,\n subtree: true,\n characterData: true,\n });\n }\n\n private handleContentGrowth() {\n if (!this.viewport) return;\n const nextHeight = this.viewport.scrollHeight;\n const delta = nextHeight - this.previousScrollHeight;\n if (delta > 0 && this.viewport.scrollTop <= LIVE_EDGE_THRESHOLD && !this.following) {\n this.viewport.scrollTop += delta;\n } else if (this.autoFollow && this.following) {\n this.viewport.scrollTop = nextHeight;\n }\n this.previousScrollHeight = nextHeight;\n this.showScrollToLatest = !this.following && !this.isAtLiveEdge();\n }\n\n private applyDefaultPosition() {\n if (!this.viewport) return;\n if (this.defaultPosition === 'start') {\n this.viewport.scrollTop = 0;\n this.following = false;\n this.showScrollToLatest = this.viewport.scrollHeight > this.viewport.clientHeight;\n return;\n }\n if (this.defaultPosition === 'last-anchor') {\n const anchors = this.el.querySelectorAll<HTMLElement>('ds-message[scroll-anchor]');\n const last = anchors.item(anchors.length - 1);\n if (last) {\n last.scrollIntoView({ block: 'start' });\n this.following = this.isAtLiveEdge();\n this.showScrollToLatest = !this.following;\n return;\n }\n }\n this.viewport.scrollTop = this.viewport.scrollHeight;\n this.following = true;\n this.showScrollToLatest = false;\n }\n\n private isAtLiveEdge(): boolean {\n if (!this.viewport) return true;\n return (\n this.viewport.scrollHeight - this.viewport.clientHeight - this.viewport.scrollTop <=\n LIVE_EDGE_THRESHOLD\n );\n }\n\n private handleScroll = () => {\n if (!this.viewport || this.programmatic) return;\n this.following = this.isAtLiveEdge();\n this.showScrollToLatest = !this.following;\n const nextAtStart = this.viewport.scrollTop <= LIVE_EDGE_THRESHOLD;\n if (nextAtStart && !this.atStart) this.dsReachStart.emit();\n this.atStart = nextAtStart;\n };\n\n private releaseFollow = () => {\n this.following = false;\n this.showScrollToLatest = !this.isAtLiveEdge();\n };\n\n private handleSelectionChange = () => {\n const selection = document.getSelection();\n if (\n selection &&\n !selection.isCollapsed &&\n selection.anchorNode &&\n this.el.contains(selection.anchorNode)\n ) {\n this.following = false;\n this.showScrollToLatest = !this.isAtLiveEdge();\n }\n };\n\n render() {\n return (\n <Host>\n <div class=\"message-scroller\">\n <div\n class=\"message-scroller__viewport\"\n ref={element => {\n this.viewport = element;\n }}\n role=\"region\"\n aria-label={this.messagesLabel}\n tabIndex={0}\n onScroll={this.handleScroll}\n onPointerDown={this.releaseFollow}\n onKeyDown={(event: KeyboardEvent) => {\n if (['ArrowUp', 'PageUp', 'Home'].includes(event.key)) this.releaseFollow();\n }}\n >\n <div\n class=\"message-scroller__content\"\n ref={element => {\n this.content = element;\n }}\n role=\"log\"\n aria-live=\"polite\"\n aria-relevant=\"additions\"\n aria-busy={this.busy ? 'true' : undefined}\n >\n <slot />\n </div>\n </div>\n <div\n class=\"message-scroller__overlay\"\n ref={element => {\n this.overlay = element;\n }}\n >\n <slot name=\"overlay\" />\n </div>\n {this.showScrollToLatest ? (\n <div class=\"message-scroller__control\">\n <ds-button-unfilled\n variant=\"icon\"\n icon=\"ChevronDown\"\n size=\"md\"\n rounded\n hasBorder={false}\n aria-label=\"Scroll to latest message\"\n onDsClick={() => void this.scrollToEnd()}\n />\n </div>\n ) : null}\n </div>\n </Host>\n );\n }\n}\n",
214
+ "type": "registry:ui"
215
+ }
216
+ ]
217
+ }