@ds-mo/ui 13.3.0 → 13.5.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 (391) hide show
  1. package/dist/.package-ready.json +1 -1
  2. package/dist/agent-patterns.json +14 -1
  3. package/dist/agent.json +126 -28
  4. package/dist/angular/boolean-value-accessor.js +3 -3
  5. package/dist/angular/ds-agent-activity.js +3 -3
  6. package/dist/angular/ds-agent-response.d.ts +1 -1
  7. package/dist/angular/ds-agent-response.js +5 -5
  8. package/dist/angular/ds-agent-source-list.js +3 -3
  9. package/dist/angular/ds-agent-tool-call.js +3 -3
  10. package/dist/angular/ds-attachment-list.js +3 -3
  11. package/dist/angular/ds-avatar.js +3 -3
  12. package/dist/angular/ds-badge.js +3 -3
  13. package/dist/angular/ds-bar-nav.js +3 -3
  14. package/dist/angular/ds-bar-title.js +3 -3
  15. package/dist/angular/ds-bar-workflow.js +3 -3
  16. package/dist/angular/ds-breadcrumb.js +3 -3
  17. package/dist/angular/ds-button-filled.js +3 -3
  18. package/dist/angular/ds-button-unfilled.js +3 -3
  19. package/dist/angular/ds-card-data-viz.js +3 -3
  20. package/dist/angular/ds-card-overview.js +3 -3
  21. package/dist/angular/ds-card-setting.js +3 -3
  22. package/dist/angular/ds-chart-bar.js +3 -3
  23. package/dist/angular/ds-chart-donut.js +3 -3
  24. package/dist/angular/ds-chart-legend.js +3 -3
  25. package/dist/angular/ds-chart-line.js +3 -3
  26. package/dist/angular/ds-checkbox.js +3 -3
  27. package/dist/angular/ds-chip.js +3 -3
  28. package/dist/angular/ds-code-block.js +3 -3
  29. package/dist/angular/ds-conversation-list-item.js +3 -3
  30. package/dist/angular/ds-conversation-list-section.js +3 -3
  31. package/dist/angular/ds-conversation-list.js +3 -3
  32. package/dist/angular/ds-divider.js +3 -3
  33. package/dist/angular/ds-empty-state.js +3 -3
  34. package/dist/angular/ds-field.js +3 -3
  35. package/dist/angular/ds-icon.js +3 -3
  36. package/dist/angular/ds-input.js +3 -3
  37. package/dist/angular/ds-loader.js +3 -3
  38. package/dist/angular/ds-markdown.js +3 -3
  39. package/dist/angular/ds-menu.js +3 -3
  40. package/dist/angular/ds-message-actions.d.ts +19 -0
  41. package/dist/angular/ds-message-actions.js +45 -0
  42. package/dist/angular/ds-message-bubble.js +3 -3
  43. package/dist/angular/ds-message-composer.js +3 -3
  44. package/dist/angular/ds-message-scroller.js +3 -3
  45. package/dist/angular/ds-message.d.ts +1 -1
  46. package/dist/angular/ds-message.js +5 -5
  47. package/dist/angular/ds-mobile-bar-nav.js +3 -3
  48. package/dist/angular/ds-mobile-header.js +3 -3
  49. package/dist/angular/ds-mobile-section-switcher.js +3 -3
  50. package/dist/angular/ds-mobile-sheet-nav.js +3 -3
  51. package/dist/angular/ds-modal.js +3 -3
  52. package/dist/angular/ds-panel-nav.js +3 -3
  53. package/dist/angular/ds-panel-sub-nav.js +3 -3
  54. package/dist/angular/ds-panel-tool-header.js +3 -3
  55. package/dist/angular/ds-panel-tool-search.js +3 -3
  56. package/dist/angular/ds-panel-tools.js +3 -3
  57. package/dist/angular/ds-radio.js +3 -3
  58. package/dist/angular/ds-scroll-overlay.js +3 -3
  59. package/dist/angular/ds-select.js +3 -3
  60. package/dist/angular/ds-shell-app.js +3 -3
  61. package/dist/angular/ds-shell-page.js +3 -3
  62. package/dist/angular/ds-shell-tools.js +3 -3
  63. package/dist/angular/ds-skeleton.js +3 -3
  64. package/dist/angular/ds-slider.js +3 -3
  65. package/dist/angular/ds-swatch-picker.js +3 -3
  66. package/dist/angular/ds-switch.js +3 -3
  67. package/dist/angular/ds-tab-group.js +3 -3
  68. package/dist/angular/ds-tag.js +3 -3
  69. package/dist/angular/ds-text.js +3 -3
  70. package/dist/angular/ds-toast.js +3 -3
  71. package/dist/angular/ds-tooltip-data-viz.js +3 -3
  72. package/dist/angular/ds-tooltip.js +3 -3
  73. package/dist/angular/ds-typing-indicator.js +3 -3
  74. package/dist/angular/index.d.ts +1 -1
  75. package/dist/angular/index.js +1 -0
  76. package/dist/angular/proxies.d.ts +1 -0
  77. package/dist/angular/proxies.js +1 -0
  78. package/dist/angular/radio-value-accessor.js +3 -3
  79. package/dist/angular/select-value-accessor.js +3 -3
  80. package/dist/angular/text-value-accessor.js +3 -3
  81. package/dist/angular/value-accessor.js +3 -3
  82. package/dist/components/ds-agent-response.js +1 -1
  83. package/dist/components/ds-agent-response.js.map +1 -1
  84. package/dist/components/ds-agent-tool-call.js +1 -1
  85. package/dist/components/ds-attachment-list.js +1 -1
  86. package/dist/components/ds-bar-nav.js +1 -1
  87. package/dist/components/ds-bar-title.js +1 -1
  88. package/dist/components/ds-bar-workflow.js +1 -1
  89. package/dist/components/ds-card-overview.js +1 -1
  90. package/dist/components/ds-card-overview.js.map +1 -1
  91. package/dist/components/ds-chart-donut.js +1 -1
  92. package/dist/components/ds-chart-line.js +1 -1
  93. package/dist/components/ds-code-block.js +1 -1
  94. package/dist/components/ds-conversation-list-item.js +1 -1
  95. package/dist/components/ds-conversation-list-section.js +1 -1
  96. package/dist/components/ds-empty-state.js +1 -1
  97. package/dist/components/ds-field.js +1 -1
  98. package/dist/components/ds-input.js +1 -1
  99. package/dist/components/ds-input.js.map +1 -1
  100. package/dist/components/ds-markdown.js +1 -1
  101. package/dist/components/ds-menu.js +1 -1
  102. package/dist/components/ds-message-actions.d.ts +11 -0
  103. package/dist/components/ds-message-actions.js +2 -0
  104. package/dist/components/ds-message-actions.js.map +1 -0
  105. package/dist/components/ds-message-composer.js +1 -1
  106. package/dist/components/ds-message-scroller.js +1 -1
  107. package/dist/components/ds-message.js +1 -1
  108. package/dist/components/ds-mobile-bar-nav.js +1 -1
  109. package/dist/components/ds-mobile-header.js +1 -1
  110. package/dist/components/ds-mobile-section-switcher.js +1 -1
  111. package/dist/components/ds-mobile-sheet-nav.js +1 -1
  112. package/dist/components/ds-panel-nav.js +1 -1
  113. package/dist/components/ds-panel-tool-header.js +1 -1
  114. package/dist/components/ds-panel-tool-search.js +1 -1
  115. package/dist/components/ds-panel-tools.js +1 -1
  116. package/dist/components/ds-select.js +1 -1
  117. package/dist/components/ds-select.js.map +1 -1
  118. package/dist/components/ds-shell-app.js +1 -1
  119. package/dist/components/ds-shell-page.js +1 -1
  120. package/dist/components/ds-shell-tools.js +1 -1
  121. package/dist/components/ds-swatch-picker.js +1 -1
  122. package/dist/components/ds-toast.js +1 -1
  123. package/dist/components/ds-tooltip-data-viz.js +1 -1
  124. package/dist/components/ds-typing-indicator.js +1 -1
  125. package/dist/components/index.d.ts +3 -0
  126. package/dist/components/{p-DEaszb15.js → p-1moOgMI-.js} +2 -2
  127. package/dist/components/p-4scN0Mgn.js +2 -0
  128. package/dist/components/{p-dVlKpt8U.js → p-B3PdrMP0.js} +2 -2
  129. package/dist/components/{p-Beejrd-R.js → p-BN9FZESH.js} +2 -2
  130. package/dist/components/{p-CAAWaTYD.js → p-BQTJ7ePr.js} +2 -2
  131. package/dist/components/{p-POvz6PiQ.js → p-BgjtMMye.js} +2 -2
  132. package/dist/components/p-BnhGV__o.js +2 -0
  133. package/dist/components/p-BnhGV__o.js.map +1 -0
  134. package/dist/components/p-BxEOlO5k.js +2 -0
  135. package/dist/components/p-BxEOlO5k.js.map +1 -0
  136. package/dist/components/{p-CUWFHTM6.js → p-CIgag7g2.js} +2 -2
  137. package/dist/components/{p-E7Ds1fQa.js → p-CQ4vAZ9j.js} +2 -2
  138. package/dist/components/{p-L-JEw-kX.js → p-CUSz0JFa.js} +2 -2
  139. package/dist/components/{p-xNfMEOpU.js → p-CWItD0gL.js} +2 -2
  140. package/dist/components/p-CWKrq35a.js +2 -0
  141. package/dist/components/p-CWKrq35a.js.map +1 -0
  142. package/dist/components/{p-CyjC57Dk.js → p-CmUGA_q4.js} +2 -2
  143. package/dist/components/p-CqOswTy8.js +2 -0
  144. package/dist/components/{p-Bi-21Kvd.js.map → p-CqOswTy8.js.map} +1 -1
  145. package/dist/components/{p-DKaXlmoQ.js → p-D4bVE7e4.js} +2 -2
  146. package/dist/components/{p-DAbOfJgz.js → p-D5PN6zdD.js} +2 -2
  147. package/dist/components/{p-BvvoC1lV.js → p-DXGKfXTh.js} +2 -2
  148. package/dist/components/{p-krhJI5_Y.js → p-DtV263qO.js} +2 -2
  149. package/dist/components/{p-vY5ia2gz.js → p-RT1mts9U.js} +2 -2
  150. package/dist/components/{p-z6c6TMlA.js → p-a6cnCKWk.js} +2 -2
  151. package/dist/docs/components.json +291 -16
  152. package/dist/mcp-data/registry/agent-activity.json +1 -1
  153. package/dist/mcp-data/registry/agent-response.json +25 -4
  154. package/dist/mcp-data/registry/agent-source-list.json +1 -1
  155. package/dist/mcp-data/registry/agent-tool-call.json +1 -1
  156. package/dist/mcp-data/registry/attachment-list.json +1 -1
  157. package/dist/mcp-data/registry/avatar.json +2 -2
  158. package/dist/mcp-data/registry/badge.json +1 -1
  159. package/dist/mcp-data/registry/bar-nav.json +1 -1
  160. package/dist/mcp-data/registry/bar-title.json +1 -1
  161. package/dist/mcp-data/registry/bar-workflow.json +1 -1
  162. package/dist/mcp-data/registry/breadcrumb.json +1 -1
  163. package/dist/mcp-data/registry/button-filled.json +1 -1
  164. package/dist/mcp-data/registry/button-unfilled.json +1 -1
  165. package/dist/mcp-data/registry/card-data-viz.json +1 -1
  166. package/dist/mcp-data/registry/card-overview.json +33 -35
  167. package/dist/mcp-data/registry/card-setting.json +1 -1
  168. package/dist/mcp-data/registry/chart-bar.json +1 -1
  169. package/dist/mcp-data/registry/chart-donut.json +1 -1
  170. package/dist/mcp-data/registry/chart-legend.json +1 -1
  171. package/dist/mcp-data/registry/chart-line.json +1 -1
  172. package/dist/mcp-data/registry/checkbox.json +1 -1
  173. package/dist/mcp-data/registry/chip.json +1 -1
  174. package/dist/mcp-data/registry/code-block.json +2 -2
  175. package/dist/mcp-data/registry/conversation-list-item.json +1 -1
  176. package/dist/mcp-data/registry/conversation-list-section.json +1 -1
  177. package/dist/mcp-data/registry/conversation-list.json +1 -1
  178. package/dist/mcp-data/registry/divider.json +1 -1
  179. package/dist/mcp-data/registry/empty-state.json +1 -1
  180. package/dist/mcp-data/registry/field.json +1 -1
  181. package/dist/mcp-data/registry/icon.json +1 -1
  182. package/dist/mcp-data/registry/input.json +6 -6
  183. package/dist/mcp-data/registry/loader.json +1 -1
  184. package/dist/mcp-data/registry/markdown.json +1 -1
  185. package/dist/mcp-data/registry/menu.json +1 -1
  186. package/dist/mcp-data/registry/message-actions.json +186 -0
  187. package/dist/mcp-data/registry/message-bubble.json +1 -1
  188. package/dist/mcp-data/registry/message-composer.json +1 -1
  189. package/dist/mcp-data/registry/message-scroller.json +1 -1
  190. package/dist/mcp-data/registry/message.json +28 -7
  191. package/dist/mcp-data/registry/mobile-bar-nav.json +1 -1
  192. package/dist/mcp-data/registry/mobile-header.json +1 -1
  193. package/dist/mcp-data/registry/mobile-section-switcher.json +1 -1
  194. package/dist/mcp-data/registry/mobile-sheet-nav.json +1 -1
  195. package/dist/mcp-data/registry/modal.json +1 -1
  196. package/dist/mcp-data/registry/panel-nav.json +1 -1
  197. package/dist/mcp-data/registry/panel-sub-nav.json +1 -1
  198. package/dist/mcp-data/registry/panel-tool-header.json +1 -1
  199. package/dist/mcp-data/registry/panel-tool-search.json +1 -1
  200. package/dist/mcp-data/registry/panel-tools.json +1 -1
  201. package/dist/mcp-data/registry/radio.json +1 -1
  202. package/dist/mcp-data/registry/registry.json +129 -28
  203. package/dist/mcp-data/registry/scroll-overlay.json +1 -1
  204. package/dist/mcp-data/registry/select.json +5 -5
  205. package/dist/mcp-data/registry/shell-app.json +1 -1
  206. package/dist/mcp-data/registry/shell-page.json +1 -1
  207. package/dist/mcp-data/registry/shell-tools.json +1 -1
  208. package/dist/mcp-data/registry/skeleton.json +1 -1
  209. package/dist/mcp-data/registry/slider.json +1 -1
  210. package/dist/mcp-data/registry/swatch-picker.json +1 -1
  211. package/dist/mcp-data/registry/switch.json +1 -1
  212. package/dist/mcp-data/registry/tab-group.json +1 -1
  213. package/dist/mcp-data/registry/tag.json +1 -1
  214. package/dist/mcp-data/registry/text.json +1 -1
  215. package/dist/mcp-data/registry/toast.json +1 -1
  216. package/dist/mcp-data/registry/tooltip-data-viz.json +1 -1
  217. package/dist/mcp-data/registry/tooltip.json +1 -1
  218. package/dist/mcp-data/registry/typing-indicator.json +1 -1
  219. package/dist/react/components.d.ts +1 -0
  220. package/dist/react/components.js +1 -0
  221. package/dist/react/ds-agent-activity.d.ts +1 -1
  222. package/dist/react/ds-agent-activity.js +1 -1
  223. package/dist/react/ds-agent-response.d.ts +1 -1
  224. package/dist/react/ds-agent-response.js +1 -1
  225. package/dist/react/ds-agent-source-list.d.ts +1 -1
  226. package/dist/react/ds-agent-source-list.js +1 -1
  227. package/dist/react/ds-agent-tool-call.d.ts +1 -1
  228. package/dist/react/ds-agent-tool-call.js +1 -1
  229. package/dist/react/ds-attachment-list.d.ts +1 -1
  230. package/dist/react/ds-attachment-list.js +1 -1
  231. package/dist/react/ds-avatar.d.ts +1 -1
  232. package/dist/react/ds-avatar.js +1 -1
  233. package/dist/react/ds-badge.d.ts +1 -1
  234. package/dist/react/ds-badge.js +1 -1
  235. package/dist/react/ds-bar-nav.d.ts +1 -1
  236. package/dist/react/ds-bar-nav.js +1 -1
  237. package/dist/react/ds-bar-title.d.ts +1 -1
  238. package/dist/react/ds-bar-title.js +1 -1
  239. package/dist/react/ds-bar-workflow.d.ts +1 -1
  240. package/dist/react/ds-bar-workflow.js +1 -1
  241. package/dist/react/ds-breadcrumb.d.ts +1 -1
  242. package/dist/react/ds-breadcrumb.js +1 -1
  243. package/dist/react/ds-button-filled.d.ts +1 -1
  244. package/dist/react/ds-button-filled.js +1 -1
  245. package/dist/react/ds-button-unfilled.d.ts +1 -1
  246. package/dist/react/ds-button-unfilled.js +1 -1
  247. package/dist/react/ds-card-data-viz.d.ts +1 -1
  248. package/dist/react/ds-card-data-viz.js +1 -1
  249. package/dist/react/ds-card-overview.d.ts +1 -1
  250. package/dist/react/ds-card-overview.js +1 -1
  251. package/dist/react/ds-card-setting.d.ts +1 -1
  252. package/dist/react/ds-card-setting.js +1 -1
  253. package/dist/react/ds-chart-bar.d.ts +1 -1
  254. package/dist/react/ds-chart-bar.js +1 -1
  255. package/dist/react/ds-chart-donut.d.ts +1 -1
  256. package/dist/react/ds-chart-donut.js +1 -1
  257. package/dist/react/ds-chart-legend.d.ts +1 -1
  258. package/dist/react/ds-chart-legend.js +1 -1
  259. package/dist/react/ds-chart-line.d.ts +1 -1
  260. package/dist/react/ds-chart-line.js +1 -1
  261. package/dist/react/ds-checkbox.d.ts +1 -1
  262. package/dist/react/ds-checkbox.js +1 -1
  263. package/dist/react/ds-chip.d.ts +1 -1
  264. package/dist/react/ds-chip.js +1 -1
  265. package/dist/react/ds-code-block.d.ts +1 -1
  266. package/dist/react/ds-code-block.js +1 -1
  267. package/dist/react/ds-conversation-list-item.d.ts +1 -1
  268. package/dist/react/ds-conversation-list-item.js +1 -1
  269. package/dist/react/ds-conversation-list-section.d.ts +1 -1
  270. package/dist/react/ds-conversation-list-section.js +1 -1
  271. package/dist/react/ds-conversation-list.d.ts +1 -1
  272. package/dist/react/ds-conversation-list.js +1 -1
  273. package/dist/react/ds-divider.d.ts +1 -1
  274. package/dist/react/ds-divider.js +1 -1
  275. package/dist/react/ds-empty-state.d.ts +1 -1
  276. package/dist/react/ds-empty-state.js +1 -1
  277. package/dist/react/ds-field.d.ts +1 -1
  278. package/dist/react/ds-field.js +1 -1
  279. package/dist/react/ds-icon.d.ts +1 -1
  280. package/dist/react/ds-icon.js +1 -1
  281. package/dist/react/ds-input.d.ts +1 -1
  282. package/dist/react/ds-input.js +1 -1
  283. package/dist/react/ds-loader.d.ts +1 -1
  284. package/dist/react/ds-loader.js +1 -1
  285. package/dist/react/ds-markdown.d.ts +1 -1
  286. package/dist/react/ds-markdown.js +1 -1
  287. package/dist/react/ds-menu.d.ts +1 -1
  288. package/dist/react/ds-menu.js +1 -1
  289. package/dist/react/ds-message-actions.d.ts +13 -0
  290. package/dist/react/ds-message-actions.js +15 -0
  291. package/dist/react/ds-message-bubble.d.ts +1 -1
  292. package/dist/react/ds-message-bubble.js +1 -1
  293. package/dist/react/ds-message-composer.d.ts +1 -1
  294. package/dist/react/ds-message-composer.js +1 -1
  295. package/dist/react/ds-message-scroller.d.ts +1 -1
  296. package/dist/react/ds-message-scroller.js +1 -1
  297. package/dist/react/ds-message.d.ts +1 -1
  298. package/dist/react/ds-message.js +1 -1
  299. package/dist/react/ds-mobile-bar-nav.d.ts +1 -1
  300. package/dist/react/ds-mobile-bar-nav.js +1 -1
  301. package/dist/react/ds-mobile-header.d.ts +1 -1
  302. package/dist/react/ds-mobile-header.js +1 -1
  303. package/dist/react/ds-mobile-section-switcher.d.ts +1 -1
  304. package/dist/react/ds-mobile-section-switcher.js +1 -1
  305. package/dist/react/ds-mobile-sheet-nav.d.ts +1 -1
  306. package/dist/react/ds-mobile-sheet-nav.js +1 -1
  307. package/dist/react/ds-modal.d.ts +1 -1
  308. package/dist/react/ds-modal.js +1 -1
  309. package/dist/react/ds-panel-nav.d.ts +1 -1
  310. package/dist/react/ds-panel-nav.js +1 -1
  311. package/dist/react/ds-panel-sub-nav.d.ts +1 -1
  312. package/dist/react/ds-panel-sub-nav.js +1 -1
  313. package/dist/react/ds-panel-tool-header.d.ts +1 -1
  314. package/dist/react/ds-panel-tool-header.js +1 -1
  315. package/dist/react/ds-panel-tool-search.d.ts +1 -1
  316. package/dist/react/ds-panel-tool-search.js +1 -1
  317. package/dist/react/ds-panel-tools.d.ts +1 -1
  318. package/dist/react/ds-panel-tools.js +1 -1
  319. package/dist/react/ds-radio.d.ts +1 -1
  320. package/dist/react/ds-radio.js +1 -1
  321. package/dist/react/ds-scroll-overlay.d.ts +1 -1
  322. package/dist/react/ds-scroll-overlay.js +1 -1
  323. package/dist/react/ds-select.d.ts +1 -1
  324. package/dist/react/ds-select.js +1 -1
  325. package/dist/react/ds-shell-app.d.ts +1 -1
  326. package/dist/react/ds-shell-app.js +1 -1
  327. package/dist/react/ds-shell-page.d.ts +1 -1
  328. package/dist/react/ds-shell-page.js +1 -1
  329. package/dist/react/ds-shell-tools.d.ts +1 -1
  330. package/dist/react/ds-shell-tools.js +1 -1
  331. package/dist/react/ds-skeleton.d.ts +1 -1
  332. package/dist/react/ds-skeleton.js +1 -1
  333. package/dist/react/ds-slider.d.ts +1 -1
  334. package/dist/react/ds-slider.js +1 -1
  335. package/dist/react/ds-swatch-picker.d.ts +1 -1
  336. package/dist/react/ds-swatch-picker.js +1 -1
  337. package/dist/react/ds-switch.d.ts +1 -1
  338. package/dist/react/ds-switch.js +1 -1
  339. package/dist/react/ds-tab-group.d.ts +1 -1
  340. package/dist/react/ds-tab-group.js +1 -1
  341. package/dist/react/ds-tag.d.ts +1 -1
  342. package/dist/react/ds-tag.js +1 -1
  343. package/dist/react/ds-text.d.ts +1 -1
  344. package/dist/react/ds-text.js +1 -1
  345. package/dist/react/ds-toast.d.ts +1 -1
  346. package/dist/react/ds-toast.js +1 -1
  347. package/dist/react/ds-tooltip-data-viz.d.ts +1 -1
  348. package/dist/react/ds-tooltip-data-viz.js +1 -1
  349. package/dist/react/ds-tooltip.d.ts +1 -1
  350. package/dist/react/ds-tooltip.js +1 -1
  351. package/dist/react/ds-typing-indicator.d.ts +1 -1
  352. package/dist/react/ds-typing-indicator.js +1 -1
  353. package/dist/react/react-runtime.d.ts +17 -0
  354. package/dist/react/react-runtime.js +13 -0
  355. package/dist/types/components/AgentResponse/AgentResponse.d.ts +3 -1
  356. package/dist/types/components/CardOverview/CardOverview.d.ts +11 -10
  357. package/dist/types/components/CardOverview/card-overview-controller.d.ts +13 -1
  358. package/dist/types/components/CardOverview/card-overview-types.d.ts +8 -2
  359. package/dist/types/components/CardOverview/index.d.ts +1 -1
  360. package/dist/types/components/CodeBlock/CodeBlock.d.ts +3 -1
  361. package/dist/types/components/Input/Input.d.ts +1 -1
  362. package/dist/types/components/Message/Message.d.ts +4 -1
  363. package/dist/types/components/MessageActions/MessageActions.d.ts +18 -0
  364. package/dist/types/components/Select/Select.d.ts +1 -1
  365. package/dist/types/components/conversation-types.d.ts +5 -0
  366. package/dist/types/components.d.ts +88 -18
  367. package/dist/types/utils/clipboard.d.ts +31 -0
  368. package/package.json +5 -8
  369. package/dist/components/p-B_A4g4Xe.js +0 -2
  370. package/dist/components/p-B_A4g4Xe.js.map +0 -1
  371. package/dist/components/p-Bi-21Kvd.js +0 -2
  372. package/dist/components/p-DV-tK5TR.js +0 -2
  373. package/dist/components/p-awaBUdlz.js +0 -2
  374. package/dist/components/p-awaBUdlz.js.map +0 -1
  375. /package/dist/components/{p-DEaszb15.js.map → p-1moOgMI-.js.map} +0 -0
  376. /package/dist/components/{p-DV-tK5TR.js.map → p-4scN0Mgn.js.map} +0 -0
  377. /package/dist/components/{p-dVlKpt8U.js.map → p-B3PdrMP0.js.map} +0 -0
  378. /package/dist/components/{p-Beejrd-R.js.map → p-BN9FZESH.js.map} +0 -0
  379. /package/dist/components/{p-CAAWaTYD.js.map → p-BQTJ7ePr.js.map} +0 -0
  380. /package/dist/components/{p-POvz6PiQ.js.map → p-BgjtMMye.js.map} +0 -0
  381. /package/dist/components/{p-CUWFHTM6.js.map → p-CIgag7g2.js.map} +0 -0
  382. /package/dist/components/{p-E7Ds1fQa.js.map → p-CQ4vAZ9j.js.map} +0 -0
  383. /package/dist/components/{p-L-JEw-kX.js.map → p-CUSz0JFa.js.map} +0 -0
  384. /package/dist/components/{p-xNfMEOpU.js.map → p-CWItD0gL.js.map} +0 -0
  385. /package/dist/components/{p-CyjC57Dk.js.map → p-CmUGA_q4.js.map} +0 -0
  386. /package/dist/components/{p-DKaXlmoQ.js.map → p-D4bVE7e4.js.map} +0 -0
  387. /package/dist/components/{p-DAbOfJgz.js.map → p-D5PN6zdD.js.map} +0 -0
  388. /package/dist/components/{p-BvvoC1lV.js.map → p-DXGKfXTh.js.map} +0 -0
  389. /package/dist/components/{p-krhJI5_Y.js.map → p-DtV263qO.js.map} +0 -0
  390. /package/dist/components/{p-vY5ia2gz.js.map → p-RT1mts9U.js.map} +0 -0
  391. /package/dist/components/{p-z6c6TMlA.js.map → p-a6cnCKWk.js.map} +0 -0
@@ -245,7 +245,7 @@
245
245
  },
246
246
  "peerDependencies": {
247
247
  "required": [
248
- "@ds-mo/tokens ^5.1.0",
248
+ "@ds-mo/tokens ^5.1.1",
249
249
  "@ds-mo/icons ^6.4.0"
250
250
  ],
251
251
  "frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
@@ -120,7 +120,7 @@
120
120
  },
121
121
  "peerDependencies": {
122
122
  "required": [
123
- "@ds-mo/tokens ^5.1.0",
123
+ "@ds-mo/tokens ^5.1.1",
124
124
  "@ds-mo/icons ^6.4.0"
125
125
  ],
126
126
  "frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
@@ -144,7 +144,7 @@
144
144
  },
145
145
  {
146
146
  "path": "src/wc/components/CodeBlock/CodeBlock.tsx",
147
- "content": "import { Component, h, Host, Prop, State } from '@stencil/core';\n\n@Component({\n tag: 'ds-code-block',\n styleUrl: 'CodeBlock.css',\n scoped: true,\n})\nexport class CodeBlock {\n @Prop() code: string = '';\n @Prop() language: string = '';\n @Prop() filename: string = '';\n\n @State() private copied: boolean = false;\n private copiedTimer?: ReturnType<typeof setTimeout>;\n\n disconnectedCallback() {\n if (this.copiedTimer) clearTimeout(this.copiedTimer);\n }\n\n private copy = async () => {\n if (!navigator.clipboard || !this.code) return;\n await navigator.clipboard.writeText(this.code);\n this.copied = true;\n if (this.copiedTimer) clearTimeout(this.copiedTimer);\n this.copiedTimer = setTimeout(() => {\n this.copied = false;\n }, 2000);\n };\n\n render() {\n const label = this.filename || this.language || 'Code';\n return (\n <Host>\n <figure class=\"code-block\">\n <figcaption class=\"code-block__header\">\n <ds-text as=\"span\" variant=\"text-caption\" emphasis color=\"on-strong\">\n {label}\n </ds-text>\n <ds-tooltip label={this.copied ? 'Copied' : 'Copy code'} side=\"bottom\" size=\"sm\">\n <ds-button-unfilled\n variant=\"icon\"\n icon={this.copied ? 'Check' : 'Copy'}\n size=\"xs\"\n aria-label={this.copied ? 'Copied' : 'Copy code'}\n hasBorder={false}\n onDsClick={this.copy}\n />\n </ds-tooltip>\n </figcaption>\n <pre>\n <code>{this.code}</code>\n </pre>\n </figure>\n </Host>\n );\n }\n}\n",
147
+ "content": "import { Component, h, Host, Prop, State, Watch } from '@stencil/core';\nimport { ClipboardFeedbackController } from '../../utils/clipboard';\n\n@Component({\n tag: 'ds-code-block',\n styleUrl: 'CodeBlock.css',\n scoped: true,\n})\nexport class CodeBlock {\n @Prop() code: string = '';\n @Prop() language: string = '';\n @Prop() filename: string = '';\n\n @State() private copied: boolean = false;\n private readonly copyFeedback = new ClipboardFeedbackController(copied => {\n this.copied = copied;\n });\n\n connectedCallback() {\n this.copyFeedback.connect();\n }\n\n @Watch('code')\n handleCodeChange() {\n this.copyFeedback.reset();\n }\n\n disconnectedCallback() {\n this.copyFeedback.disconnect();\n }\n\n private copy = async () => {\n await this.copyFeedback.copy(this.code);\n };\n\n render() {\n const label = this.filename || this.language || 'Code';\n return (\n <Host>\n <figure class=\"code-block\">\n <figcaption class=\"code-block__header\">\n <ds-text as=\"span\" variant=\"text-caption\" emphasis color=\"on-strong\">\n {label}\n </ds-text>\n <ds-tooltip label={this.copied ? 'Copied' : 'Copy code'} side=\"bottom\" size=\"sm\">\n <ds-button-unfilled\n variant=\"icon\"\n icon={this.copied ? 'Check' : 'Copy'}\n size=\"xs\"\n aria-label={this.copied ? 'Copied' : 'Copy code'}\n hasBorder={false}\n onDsClick={this.copy}\n />\n </ds-tooltip>\n </figcaption>\n <pre>\n <code>{this.code}</code>\n </pre>\n </figure>\n </Host>\n );\n }\n}\n",
148
148
  "type": "registry:ui"
149
149
  }
150
150
  ]
@@ -253,7 +253,7 @@
253
253
  },
254
254
  "peerDependencies": {
255
255
  "required": [
256
- "@ds-mo/tokens ^5.1.0",
256
+ "@ds-mo/tokens ^5.1.1",
257
257
  "@ds-mo/icons ^6.4.0"
258
258
  ],
259
259
  "frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
@@ -110,7 +110,7 @@
110
110
  },
111
111
  "peerDependencies": {
112
112
  "required": [
113
- "@ds-mo/tokens ^5.1.0",
113
+ "@ds-mo/tokens ^5.1.1",
114
114
  "@ds-mo/icons ^6.4.0"
115
115
  ],
116
116
  "frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
@@ -102,7 +102,7 @@
102
102
  },
103
103
  "peerDependencies": {
104
104
  "required": [
105
- "@ds-mo/tokens ^5.1.0",
105
+ "@ds-mo/tokens ^5.1.1",
106
106
  "@ds-mo/icons ^6.4.0"
107
107
  ],
108
108
  "frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
@@ -198,7 +198,7 @@
198
198
  },
199
199
  "peerDependencies": {
200
200
  "required": [
201
- "@ds-mo/tokens ^5.1.0",
201
+ "@ds-mo/tokens ^5.1.1",
202
202
  "@ds-mo/icons ^6.4.0"
203
203
  ],
204
204
  "frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
@@ -170,7 +170,7 @@
170
170
  },
171
171
  "peerDependencies": {
172
172
  "required": [
173
- "@ds-mo/tokens ^5.1.0",
173
+ "@ds-mo/tokens ^5.1.1",
174
174
  "@ds-mo/icons ^6.4.0"
175
175
  ],
176
176
  "frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
@@ -193,7 +193,7 @@
193
193
  },
194
194
  "peerDependencies": {
195
195
  "required": [
196
- "@ds-mo/tokens ^5.1.0",
196
+ "@ds-mo/tokens ^5.1.1",
197
197
  "@ds-mo/icons ^6.4.0"
198
198
  ],
199
199
  "frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
@@ -194,7 +194,7 @@
194
194
  },
195
195
  "peerDependencies": {
196
196
  "required": [
197
- "@ds-mo/tokens ^5.1.0",
197
+ "@ds-mo/tokens ^5.1.1",
198
198
  "@ds-mo/icons ^6.4.0"
199
199
  ],
200
200
  "frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
@@ -55,7 +55,7 @@
55
55
  ],
56
56
  "states": [
57
57
  "Every size applies one complete control-density recipe across frame height, inline padding, icon zones, text-container inset, gap, and radius; supporting error content follows the shared 4px field flow.",
58
- "The normal field boundary uses the shared secondary inset border. Focus promotes it to the thicker bold-brand inset stroke, while error uses the thicker bold-negative inset stroke without changing geometry.",
58
+ "The normal field boundary uses the shared secondary inset border. Focus promotes it to the thicker bold-brand inset stroke, while error uses the thicker bold-negative inset stroke without changing geometry. Set hasBorder to false only in chrome that supplies enough surrounding structure; the control then remains borderless at rest, while focused, and while invalid, with semantics and supporting error copy unchanged.",
59
59
  "Placeholder text is secondary at rest and quaternary while the editable field is focused because it becomes decorative once typing begins.",
60
60
  "Entered values use the parent surface's primary foreground; prefix icons, suffix content, and resting placeholder text use its secondary foreground.",
61
61
  "Search fields show a standard borderless CrossCircle icon action only while they contain a value. Clearing emits the empty value and restores focus to the native field.",
@@ -186,7 +186,7 @@
186
186
  "default": "true",
187
187
  "required": false,
188
188
  "mutable": false,
189
- "description": "Show the standard inset border."
189
+ "description": "Show the standard inset border, including focused and invalid strokes."
190
190
  },
191
191
  "icon": {
192
192
  "type": "string | undefined",
@@ -412,7 +412,7 @@
412
412
  "default": "true",
413
413
  "required": false,
414
414
  "mutable": false,
415
- "description": "Show the standard inset border."
415
+ "description": "Show the standard inset border, including focused and invalid strokes."
416
416
  },
417
417
  "icon": {
418
418
  "type": "string | undefined",
@@ -566,7 +566,7 @@
566
566
  },
567
567
  "peerDependencies": {
568
568
  "required": [
569
- "@ds-mo/tokens ^5.1.0",
569
+ "@ds-mo/tokens ^5.1.1",
570
570
  "@ds-mo/icons ^6.4.0"
571
571
  ],
572
572
  "frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
@@ -586,12 +586,12 @@
586
586
  "files": [
587
587
  {
588
588
  "path": "src/wc/components/Input/Input.css",
589
- "content": "@import '../../utils/interaction-fill.css';\n@import '../../utils/control-inactive.css';\n@import '../../utils/control-density.css';\n@import '../../utils/control-parts.css';\n@import '../../utils/control-width.css';\n@import '../../utils/field-stack.css';\n@import '../../utils/typography.css';\n@import '../../utils/forced-colors.css';\n\n:host(.input-host) {\n display: inline-flex;\n --ds-input-fg: var(--color-foreground-primary);\n --ds-input-placeholder: var(--color-foreground-secondary);\n --ds-input-placeholder-active: var(--color-foreground-quaternary);\n --ds-input-adornment-fg: var(--color-foreground-secondary);\n --ds-input-border: var(--color-border-secondary);\n}\n\n:host(.ds-control-width--fill),\n:host(.ds-control-width--fill) .input-control {\n width: 100%;\n}\n\n.input-control {\n overflow: hidden;\n background: transparent;\n border: none;\n border-radius: var(--ds-control-radius, var(--dimension-radius-025));\n color: var(--ds-input-fg);\n}\n\n.input-control--bordered {\n --ds-interaction-border-width: var(--dimension-stroke-width-012);\n --ds-interaction-border-color: var(--ds-input-border);\n}\n\n.input-control--error {\n --ds-interaction-border-width: var(--dimension-stroke-width-018);\n --ds-interaction-border-color: var(--color-border-bold-negative);\n}\n\n.input-control:focus-within:not(.input-control--error) {\n --ds-interaction-border-width: var(--dimension-stroke-width-018);\n --ds-interaction-border-color: var(--color-border-bold-brand);\n}\n\n:host(.ds-control-inactive) .input-control::after {\n background: transparent;\n}\n\n.native-input {\n position: relative;\n z-index: 2;\n flex: 1 1 auto;\n min-width: 0;\n height: 100%;\n background: transparent;\n border: none;\n border-radius: inherit;\n outline: none;\n color: var(--ds-input-fg);\n font-family: inherit;\n -webkit-appearance: none;\n appearance: none;\n}\n\n.native-input:disabled {\n cursor: not-allowed;\n}\n\n.native-input:read-only:not(:disabled) {\n cursor: default;\n}\n\n.native-input::placeholder {\n color: var(--ds-input-placeholder);\n}\n\n.native-input:focus::placeholder {\n color: var(--ds-input-placeholder-active);\n}\n\n.native-input[type='search']::-webkit-search-cancel-button,\n.native-input[type='search']::-webkit-search-decoration {\n display: none;\n}\n\n.input-control__prefix,\n.input-control__suffix {\n position: relative;\n z-index: 2;\n color: var(--ds-input-adornment-fg);\n}\n\n.input-control__suffix--empty {\n display: none;\n}\n\n.input-control__clear {\n position: relative;\n z-index: 2;\n flex-shrink: 0;\n align-self: center;\n}\n\n.input-control.ds-control--lg:has(.input-control__clear),\n.input-control.ds-control--md:has(.input-control__clear),\n.input-control.ds-control--sm:has(.input-control__clear) {\n padding-right: var(--dimension-space-050);\n}\n\n.input-control.ds-control--xs:has(.input-control__clear) {\n padding-right: 0;\n}\n\n.error-text {\n width: 100%;\n}\n\n@media (forced-colors: active) {\n .input-control:focus-within:not(.input-control--error)::after {\n outline-color: var(--ds-forced-color-selected);\n }\n\n .input-control--error::after {\n outline-style: dashed;\n }\n}\n",
589
+ "content": "@import '../../utils/interaction-fill.css';\n@import '../../utils/control-inactive.css';\n@import '../../utils/control-density.css';\n@import '../../utils/control-parts.css';\n@import '../../utils/control-width.css';\n@import '../../utils/field-stack.css';\n@import '../../utils/typography.css';\n@import '../../utils/forced-colors.css';\n\n:host(.input-host) {\n display: inline-flex;\n --ds-input-fg: var(--color-foreground-primary);\n --ds-input-placeholder: var(--color-foreground-secondary);\n --ds-input-placeholder-active: var(--color-foreground-quaternary);\n --ds-input-adornment-fg: var(--color-foreground-secondary);\n --ds-input-border: var(--color-border-secondary);\n}\n\n:host(.ds-control-width--fill),\n:host(.ds-control-width--fill) .input-control {\n width: 100%;\n}\n\n.input-control {\n overflow: hidden;\n background: transparent;\n border: none;\n border-radius: var(--ds-control-radius, var(--dimension-radius-025));\n color: var(--ds-input-fg);\n}\n\n.input-control--bordered {\n --ds-interaction-border-width: var(--dimension-stroke-width-012);\n --ds-interaction-border-color: var(--ds-input-border);\n}\n\n.input-control--error {\n --ds-interaction-border-width: var(--dimension-stroke-width-018);\n --ds-interaction-border-color: var(--color-border-bold-negative);\n}\n\n.input-control--bordered:focus-within:not(.input-control--error) {\n --ds-interaction-border-width: var(--dimension-stroke-width-018);\n --ds-interaction-border-color: var(--color-border-bold-brand);\n}\n\n:host(.ds-control-inactive) .input-control::after {\n background: transparent;\n}\n\n.native-input {\n position: relative;\n z-index: 2;\n flex: 1 1 auto;\n min-width: 0;\n height: 100%;\n background: transparent;\n border: none;\n border-radius: inherit;\n outline: none;\n color: var(--ds-input-fg);\n font-family: inherit;\n -webkit-appearance: none;\n appearance: none;\n}\n\n.native-input:disabled {\n cursor: not-allowed;\n}\n\n.native-input:read-only:not(:disabled) {\n cursor: default;\n}\n\n.native-input::placeholder {\n color: var(--ds-input-placeholder);\n}\n\n.native-input:focus::placeholder {\n color: var(--ds-input-placeholder-active);\n}\n\n.native-input[type='search']::-webkit-search-cancel-button,\n.native-input[type='search']::-webkit-search-decoration {\n display: none;\n}\n\n.input-control__prefix,\n.input-control__suffix {\n position: relative;\n z-index: 2;\n color: var(--ds-input-adornment-fg);\n}\n\n.input-control__suffix--empty {\n display: none;\n}\n\n.input-control__clear {\n position: relative;\n z-index: 2;\n flex-shrink: 0;\n align-self: center;\n}\n\n.input-control.ds-control--lg:has(.input-control__clear),\n.input-control.ds-control--md:has(.input-control__clear),\n.input-control.ds-control--sm:has(.input-control__clear) {\n padding-right: var(--dimension-space-050);\n}\n\n.input-control.ds-control--xs:has(.input-control__clear) {\n padding-right: 0;\n}\n\n.error-text {\n width: 100%;\n}\n\n@media (forced-colors: active) {\n .input-control--bordered:focus-within:not(.input-control--error)::after {\n outline-color: var(--ds-forced-color-selected);\n }\n\n .input-control--error::after {\n outline-style: dashed;\n }\n}\n",
590
590
  "type": "registry:ui"
591
591
  },
592
592
  {
593
593
  "path": "src/wc/components/Input/Input.tsx",
594
- "content": "import { AttachInternals, Component, Prop, State, Event, EventEmitter, Element, Method, Watch, h, Host } from '@stencil/core';\nimport {\n controlWidthClass,\n CONTROL_TEXT_VARIANT,\n DEFAULT_REQUIRED_MESSAGE,\n restoreStringFormState,\n setFormControlValue,\n setRequiredValidity,\n type ControlWidth,\n} from '../../utils';\n\nexport type InputType = 'text' | 'email' | 'tel' | 'url' | 'search' | 'password';\nexport type InputSize = 'lg' | 'md' | 'sm' | 'xs';\nexport type InputWidth = ControlWidth;\n\nconst ICON_SIZE: Record<InputSize, 'lg' | 'md' | 'sm' | 'xs'> = {\n lg: 'lg',\n md: 'md',\n sm: 'sm',\n xs: 'xs',\n};\n\nconst CLEAR_BUTTON_SIZE: Record<InputSize, 'md' | 'sm' | 'xs'> = {\n lg: 'md',\n md: 'sm',\n sm: 'xs',\n xs: 'xs',\n};\n\nlet idCounter = 0;\n\n@Component({\n tag: 'ds-input',\n styleUrl: 'Input.css',\n scoped: true,\n formAssociated: true,\n})\nexport class Input {\n @Element() el!: HTMLElement;\n @AttachInternals() internals!: ElementInternals;\n\n private generatedId = `ds-input-${++idCounter}`;\n private errorId = `${this.generatedId}-error`;\n\n @Prop({ mutable: true }) value: string = '';\n @Prop({ reflect: true }) name: string | undefined;\n @Prop({ reflect: true }) form: string | undefined;\n @Prop({ reflect: true }) disabled: boolean = false;\n /** Keeps the value focusable and submittable while preventing edits. */\n @Prop({ reflect: true }) readOnly: boolean = false;\n @Prop({ reflect: true }) required: boolean = false;\n @Prop() requiredMessage: string = DEFAULT_REQUIRED_MESSAGE;\n @Prop() clearLabel: string = 'Clear';\n @Prop() placeholder: string | undefined;\n @Prop() type: InputType = 'text';\n /** Native browser autofill hint. */\n @Prop({ attribute: 'autocomplete' }) autoComplete: string | undefined;\n /** Preferred virtual keyboard without changing the value semantics. */\n @Prop({ attribute: 'inputmode' }) inputMode: string = '';\n /** Preferred virtual-keyboard action label. */\n @Prop({ attribute: 'enterkeyhint' }) enterKeyHint: string = '';\n /** Control density. */\n @Prop() size: InputSize = 'md';\n /** Width fit — fill the parent (default) or hug the available content. */\n @Prop() width: InputWidth = 'fill';\n /** Show the standard inset border. */\n @Prop() hasBorder: boolean = true;\n /** Optional leading icon name. */\n @Prop() icon: string | undefined;\n @Prop() isInactive: boolean = false;\n @Prop() autoFocus: boolean = false;\n @Prop() error: boolean = false;\n @Prop() errorMessage: string | undefined;\n /** Associates the internal input with an external <label>. */\n @Prop() inputId: string | undefined;\n @Prop({ attribute: 'aria-label' }) ariaLabel: string | null = null;\n @Prop({ attribute: 'aria-labelledby' }) ariaLabelledby: string | undefined;\n @Prop({ attribute: 'aria-describedby' }) ariaDescribedby: string | undefined;\n\n @Event() dsChange!: EventEmitter<string>;\n @Event() dsClear!: EventEmitter<void>;\n\n private initialValue = '';\n @State() private formDisabled = false;\n @State() private hasSuffix = false;\n @State() private focused = false;\n @State() private touched = false;\n\n componentWillLoad() {\n this.initialValue = this.value;\n this.hasSuffix = Boolean(this.el.querySelector('[slot=\"suffix\"]'));\n this.syncFormValue();\n }\n\n componentDidRender() {\n const hasSuffix = Boolean(this.el.querySelector('[slot=\"suffix\"]'));\n if (hasSuffix !== this.hasSuffix) this.hasSuffix = hasSuffix;\n }\n\n @Watch('value')\n @Watch('disabled')\n @Watch('isInactive')\n @Watch('required')\n syncFormValue() {\n const inactive = this.isInactive || this.disabled || this.formDisabled;\n setFormControlValue(this.internals, this.value, { inactive });\n const missing = this.required && !inactive && this.value.length === 0;\n setRequiredValidity(this.internals, missing, this.requiredMessage);\n }\n\n formDisabledCallback(disabled: boolean) {\n this.formDisabled = disabled;\n this.syncFormValue();\n }\n\n formResetCallback() {\n this.value = this.initialValue;\n }\n\n formStateRestoreCallback(state: string | File | FormData | null) {\n this.value = restoreStringFormState(state);\n }\n\n @Method()\n async setFocus() {\n this.el.querySelector('input')?.focus();\n }\n\n private handleInput = (e: Event) => {\n this.value = (e.target as HTMLInputElement).value;\n this.dsChange.emit(this.value);\n };\n\n private handleFocus = () => {\n this.focused = true;\n };\n\n private handleBlur = () => {\n this.focused = false;\n this.touched = true;\n };\n\n private handleClear = () => {\n this.value = '';\n this.dsChange.emit('');\n this.dsClear.emit();\n this.el.querySelector('input')?.focus();\n };\n\n render() {\n const inputId = this.inputId ?? this.generatedId;\n const inactive = this.isInactive || this.disabled || this.formDisabled;\n const filled = this.value.length > 0;\n const dirty = this.value !== this.initialValue;\n const showClear = this.type === 'search' && filled && !inactive && !this.readOnly;\n const showError = this.error && Boolean(this.errorMessage);\n const textVariant = CONTROL_TEXT_VARIANT[this.size];\n const iconSize = ICON_SIZE[this.size];\n\n const describedBy = [\n this.ariaDescribedby,\n showError ? this.errorId : undefined,\n ].filter(Boolean).join(' ') || undefined;\n\n return (\n <Host\n class={{\n 'input-host': true,\n 'ds-field-stack': true,\n 'ds-control-inactive': inactive,\n [`ds-control--${this.size}`]: true,\n ...controlWidthClass(this.width),\n }}\n data-disabled={inactive ? '' : undefined}\n data-readonly={this.readOnly ? '' : undefined}\n data-required={this.required ? '' : undefined}\n data-invalid={this.error ? '' : undefined}\n data-filled={filled ? '' : undefined}\n data-focused={this.focused ? '' : undefined}\n data-dirty={dirty ? '' : undefined}\n data-touched={this.touched ? '' : undefined}\n >\n <div\n class={{\n 'input-control': true,\n 'ds-control-frame': true,\n 'input-control--bordered': this.hasBorder || this.error,\n 'input-control--error': this.error,\n 'ds-interaction-fill': true,\n [`ds-control--${this.size}`]: true,\n }}\n >\n {this.icon && (\n <span class=\"input-control__prefix ds-control-icon-box ds-interaction-fill__content\" aria-hidden=\"true\">\n <ds-icon name={this.icon} size={iconSize} color=\"inherit\" />\n </span>\n )}\n <input\n type={this.type}\n id={inputId}\n value={this.value}\n placeholder={this.placeholder}\n disabled={inactive}\n readOnly={this.readOnly}\n required={this.required}\n autoFocus={this.autoFocus}\n autoComplete={this.autoComplete}\n inputMode={this.inputMode || undefined}\n enterKeyHint={this.enterKeyHint || undefined}\n class={`native-input ds-control-label-box ds-text--${textVariant.replace('text-', '')} ds-text--regular ds-interaction-fill__content`}\n aria-label={this.ariaLabel}\n aria-labelledby={this.ariaLabelledby}\n aria-describedby={describedBy}\n aria-invalid={this.error ? 'true' : undefined}\n onInput={this.handleInput}\n onFocus={this.handleFocus}\n onBlur={this.handleBlur}\n />\n <span class={{ 'input-control__suffix': true, 'ds-control-icon-box': true, 'input-control__suffix--empty': !this.hasSuffix }}>\n <slot name=\"suffix\" />\n </span>\n {showClear && (\n <ds-button-unfilled\n class=\"input-control__clear\"\n variant=\"icon\"\n size={CLEAR_BUTTON_SIZE[this.size]}\n icon=\"CrossCircle\"\n hasBorder={false}\n rounded\n ariaLabel={this.clearLabel}\n onDsClick={this.handleClear}\n />\n )}\n </div>\n {showError && (\n <ds-text\n class=\"error-text\"\n as=\"div\"\n variant=\"text-body-small\"\n color=\"negative\"\n textId={this.errorId}\n role=\"alert\"\n >\n {this.errorMessage}\n </ds-text>\n )}\n </Host>\n );\n }\n}\n",
594
+ "content": "import { AttachInternals, Component, Prop, State, Event, EventEmitter, Element, Method, Watch, h, Host } from '@stencil/core';\nimport {\n controlWidthClass,\n CONTROL_TEXT_VARIANT,\n DEFAULT_REQUIRED_MESSAGE,\n restoreStringFormState,\n setFormControlValue,\n setRequiredValidity,\n type ControlWidth,\n} from '../../utils';\n\nexport type InputType = 'text' | 'email' | 'tel' | 'url' | 'search' | 'password';\nexport type InputSize = 'lg' | 'md' | 'sm' | 'xs';\nexport type InputWidth = ControlWidth;\n\nconst ICON_SIZE: Record<InputSize, 'lg' | 'md' | 'sm' | 'xs'> = {\n lg: 'lg',\n md: 'md',\n sm: 'sm',\n xs: 'xs',\n};\n\nconst CLEAR_BUTTON_SIZE: Record<InputSize, 'md' | 'sm' | 'xs'> = {\n lg: 'md',\n md: 'sm',\n sm: 'xs',\n xs: 'xs',\n};\n\nlet idCounter = 0;\n\n@Component({\n tag: 'ds-input',\n styleUrl: 'Input.css',\n scoped: true,\n formAssociated: true,\n})\nexport class Input {\n @Element() el!: HTMLElement;\n @AttachInternals() internals!: ElementInternals;\n\n private generatedId = `ds-input-${++idCounter}`;\n private errorId = `${this.generatedId}-error`;\n\n @Prop({ mutable: true }) value: string = '';\n @Prop({ reflect: true }) name: string | undefined;\n @Prop({ reflect: true }) form: string | undefined;\n @Prop({ reflect: true }) disabled: boolean = false;\n /** Keeps the value focusable and submittable while preventing edits. */\n @Prop({ reflect: true }) readOnly: boolean = false;\n @Prop({ reflect: true }) required: boolean = false;\n @Prop() requiredMessage: string = DEFAULT_REQUIRED_MESSAGE;\n @Prop() clearLabel: string = 'Clear';\n @Prop() placeholder: string | undefined;\n @Prop() type: InputType = 'text';\n /** Native browser autofill hint. */\n @Prop({ attribute: 'autocomplete' }) autoComplete: string | undefined;\n /** Preferred virtual keyboard without changing the value semantics. */\n @Prop({ attribute: 'inputmode' }) inputMode: string = '';\n /** Preferred virtual-keyboard action label. */\n @Prop({ attribute: 'enterkeyhint' }) enterKeyHint: string = '';\n /** Control density. */\n @Prop() size: InputSize = 'md';\n /** Width fit — fill the parent (default) or hug the available content. */\n @Prop() width: InputWidth = 'fill';\n /** Show the standard inset border, including focused and invalid strokes. */\n @Prop() hasBorder: boolean = true;\n /** Optional leading icon name. */\n @Prop() icon: string | undefined;\n @Prop() isInactive: boolean = false;\n @Prop() autoFocus: boolean = false;\n @Prop() error: boolean = false;\n @Prop() errorMessage: string | undefined;\n /** Associates the internal input with an external <label>. */\n @Prop() inputId: string | undefined;\n @Prop({ attribute: 'aria-label' }) ariaLabel: string | null = null;\n @Prop({ attribute: 'aria-labelledby' }) ariaLabelledby: string | undefined;\n @Prop({ attribute: 'aria-describedby' }) ariaDescribedby: string | undefined;\n\n @Event() dsChange!: EventEmitter<string>;\n @Event() dsClear!: EventEmitter<void>;\n\n private initialValue = '';\n @State() private formDisabled = false;\n @State() private hasSuffix = false;\n @State() private focused = false;\n @State() private touched = false;\n\n componentWillLoad() {\n this.initialValue = this.value;\n this.hasSuffix = Boolean(this.el.querySelector('[slot=\"suffix\"]'));\n this.syncFormValue();\n }\n\n componentDidRender() {\n const hasSuffix = Boolean(this.el.querySelector('[slot=\"suffix\"]'));\n if (hasSuffix !== this.hasSuffix) this.hasSuffix = hasSuffix;\n }\n\n @Watch('value')\n @Watch('disabled')\n @Watch('isInactive')\n @Watch('required')\n syncFormValue() {\n const inactive = this.isInactive || this.disabled || this.formDisabled;\n setFormControlValue(this.internals, this.value, { inactive });\n const missing = this.required && !inactive && this.value.length === 0;\n setRequiredValidity(this.internals, missing, this.requiredMessage);\n }\n\n formDisabledCallback(disabled: boolean) {\n this.formDisabled = disabled;\n this.syncFormValue();\n }\n\n formResetCallback() {\n this.value = this.initialValue;\n }\n\n formStateRestoreCallback(state: string | File | FormData | null) {\n this.value = restoreStringFormState(state);\n }\n\n @Method()\n async setFocus() {\n this.el.querySelector('input')?.focus();\n }\n\n private handleInput = (e: Event) => {\n this.value = (e.target as HTMLInputElement).value;\n this.dsChange.emit(this.value);\n };\n\n private handleFocus = () => {\n this.focused = true;\n };\n\n private handleBlur = () => {\n this.focused = false;\n this.touched = true;\n };\n\n private handleClear = () => {\n this.value = '';\n this.dsChange.emit('');\n this.dsClear.emit();\n this.el.querySelector('input')?.focus();\n };\n\n render() {\n const inputId = this.inputId ?? this.generatedId;\n const inactive = this.isInactive || this.disabled || this.formDisabled;\n const filled = this.value.length > 0;\n const dirty = this.value !== this.initialValue;\n const showClear = this.type === 'search' && filled && !inactive && !this.readOnly;\n const showError = this.error && Boolean(this.errorMessage);\n const textVariant = CONTROL_TEXT_VARIANT[this.size];\n const iconSize = ICON_SIZE[this.size];\n\n const describedBy = [\n this.ariaDescribedby,\n showError ? this.errorId : undefined,\n ].filter(Boolean).join(' ') || undefined;\n\n return (\n <Host\n class={{\n 'input-host': true,\n 'ds-field-stack': true,\n 'ds-control-inactive': inactive,\n [`ds-control--${this.size}`]: true,\n ...controlWidthClass(this.width),\n }}\n data-disabled={inactive ? '' : undefined}\n data-readonly={this.readOnly ? '' : undefined}\n data-required={this.required ? '' : undefined}\n data-invalid={this.error ? '' : undefined}\n data-filled={filled ? '' : undefined}\n data-focused={this.focused ? '' : undefined}\n data-dirty={dirty ? '' : undefined}\n data-touched={this.touched ? '' : undefined}\n >\n <div\n class={{\n 'input-control': true,\n 'ds-control-frame': true,\n 'input-control--bordered': this.hasBorder,\n 'input-control--error': this.hasBorder && this.error,\n 'ds-interaction-fill': true,\n [`ds-control--${this.size}`]: true,\n }}\n >\n {this.icon && (\n <span class=\"input-control__prefix ds-control-icon-box ds-interaction-fill__content\" aria-hidden=\"true\">\n <ds-icon name={this.icon} size={iconSize} color=\"inherit\" />\n </span>\n )}\n <input\n type={this.type}\n id={inputId}\n value={this.value}\n placeholder={this.placeholder}\n disabled={inactive}\n readOnly={this.readOnly}\n required={this.required}\n autoFocus={this.autoFocus}\n autoComplete={this.autoComplete}\n inputMode={this.inputMode || undefined}\n enterKeyHint={this.enterKeyHint || undefined}\n class={`native-input ds-control-label-box ds-text--${textVariant.replace('text-', '')} ds-text--regular ds-interaction-fill__content`}\n aria-label={this.ariaLabel}\n aria-labelledby={this.ariaLabelledby}\n aria-describedby={describedBy}\n aria-invalid={this.error ? 'true' : undefined}\n onInput={this.handleInput}\n onFocus={this.handleFocus}\n onBlur={this.handleBlur}\n />\n <span class={{ 'input-control__suffix': true, 'ds-control-icon-box': true, 'input-control__suffix--empty': !this.hasSuffix }}>\n <slot name=\"suffix\" />\n </span>\n {showClear && (\n <ds-button-unfilled\n class=\"input-control__clear\"\n variant=\"icon\"\n size={CLEAR_BUTTON_SIZE[this.size]}\n icon=\"CrossCircle\"\n hasBorder={false}\n rounded\n ariaLabel={this.clearLabel}\n onDsClick={this.handleClear}\n />\n )}\n </div>\n {showError && (\n <ds-text\n class=\"error-text\"\n as=\"div\"\n variant=\"text-body-small\"\n color=\"negative\"\n textId={this.errorId}\n role=\"alert\"\n >\n {this.errorMessage}\n </ds-text>\n )}\n </Host>\n );\n }\n}\n",
595
595
  "type": "registry:ui"
596
596
  }
597
597
  ]
@@ -165,7 +165,7 @@
165
165
  },
166
166
  "peerDependencies": {
167
167
  "required": [
168
- "@ds-mo/tokens ^5.1.0",
168
+ "@ds-mo/tokens ^5.1.1",
169
169
  "@ds-mo/icons ^6.4.0"
170
170
  ],
171
171
  "frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
@@ -117,7 +117,7 @@
117
117
  },
118
118
  "peerDependencies": {
119
119
  "required": [
120
- "@ds-mo/tokens ^5.1.0",
120
+ "@ds-mo/tokens ^5.1.1",
121
121
  "@ds-mo/icons ^6.4.0"
122
122
  ],
123
123
  "frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
@@ -453,7 +453,7 @@
453
453
  "complexPropertyNote": "Assign these non-primitive values as JavaScript properties: anchor, items, sections.",
454
454
  "peerDependencies": {
455
455
  "required": [
456
- "@ds-mo/tokens ^5.1.0",
456
+ "@ds-mo/tokens ^5.1.1",
457
457
  "@ds-mo/icons ^6.4.0"
458
458
  ],
459
459
  "frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
@@ -0,0 +1,186 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "message-actions",
4
+ "title": "MessageActions",
5
+ "description": "Reusable message-level Copy and controlled response-feedback actions with consistent compact presentation and browser clipboard feedback.",
6
+ "type": "registry:ui",
7
+ "meta": {
8
+ "library": "@ds-mo/ui",
9
+ "distribution": "npm",
10
+ "storybook": "https://zainadeel.github.io/compomo/?path=/story/message-actions",
11
+ "source": "src/wc/components/MessageActions/MessageActions.tsx",
12
+ "intentStatus": "complete",
13
+ "intent": {
14
+ "audience": "specialized",
15
+ "status": "experimental",
16
+ "summary": "Reusable message-level Copy and controlled response-feedback actions with consistent compact presentation and browser clipboard feedback.",
17
+ "useWhen": [
18
+ "A message metadata row needs Copy alone or Copy with mutually exclusive positive and negative response feedback."
19
+ ],
20
+ "avoidWhen": [
21
+ "The action executes, retries, regenerates, approves, edits, or otherwise changes message content."
22
+ ],
23
+ "commonlyComposedWith": [
24
+ "component:ds-message",
25
+ "component:ds-agent-response",
26
+ "component:ds-button-unfilled"
27
+ ],
28
+ "patterns": [
29
+ "pattern:conversation",
30
+ "pattern:agent-chat"
31
+ ],
32
+ "accessibility": [
33
+ "Every icon action has a matching tooltip and accessible name.",
34
+ "Feedback uses genuine pressed toggle semantics and Copy announces its temporary Copied state through the control name."
35
+ ],
36
+ "states": [
37
+ "The component owns clipboard execution and temporary copied feedback while the application supplies the exact copyable text.",
38
+ "Feedback is controlled: the component emits the requested positive, negative, or cleared value while the application owns persistence and consequences.",
39
+ "Copy, positive feedback, and negative feedback retain one stable order."
40
+ ],
41
+ "responsiveBehavior": [
42
+ "The compact action group follows the visibility and metadata placement policy of its owning Message."
43
+ ],
44
+ "references": [
45
+ {
46
+ "label": "Message metadata composition",
47
+ "path": "src/wc/components/Message/Message.stories.ts"
48
+ }
49
+ ]
50
+ },
51
+ "api": {
52
+ "props": {
53
+ "copyText": {
54
+ "type": "string | undefined",
55
+ "resolvedType": "string | undefined",
56
+ "attribute": "copy-text",
57
+ "required": false,
58
+ "mutable": false
59
+ },
60
+ "feedback": {
61
+ "type": "MessageFeedback | undefined",
62
+ "resolvedType": "\"negative\" | \"positive\" | undefined",
63
+ "attribute": "feedback",
64
+ "required": false,
65
+ "mutable": false
66
+ },
67
+ "feedbackEnabled": {
68
+ "type": "boolean",
69
+ "resolvedType": "boolean",
70
+ "attribute": "feedback-enabled",
71
+ "default": "false",
72
+ "required": false,
73
+ "mutable": false
74
+ }
75
+ },
76
+ "events": [
77
+ {
78
+ "name": "dsCopyResult",
79
+ "detail": "MessageCopyResultEventDetail",
80
+ "bubbles": true,
81
+ "cancelable": true,
82
+ "composed": true
83
+ },
84
+ {
85
+ "name": "dsFeedbackChange",
86
+ "detail": "MessageFeedback | undefined",
87
+ "bubbles": true,
88
+ "cancelable": true,
89
+ "composed": true
90
+ }
91
+ ],
92
+ "methods": [],
93
+ "slots": []
94
+ },
95
+ "props": {
96
+ "copyText": {
97
+ "type": "string | undefined",
98
+ "resolvedType": "string | undefined",
99
+ "attribute": "copy-text",
100
+ "required": false,
101
+ "mutable": false
102
+ },
103
+ "feedback": {
104
+ "type": "MessageFeedback | undefined",
105
+ "resolvedType": "\"negative\" | \"positive\" | undefined",
106
+ "attribute": "feedback",
107
+ "required": false,
108
+ "mutable": false
109
+ },
110
+ "feedbackEnabled": {
111
+ "type": "boolean",
112
+ "resolvedType": "boolean",
113
+ "attribute": "feedback-enabled",
114
+ "default": "false",
115
+ "required": false,
116
+ "mutable": false
117
+ }
118
+ },
119
+ "events": [
120
+ {
121
+ "name": "dsCopyResult",
122
+ "detail": "MessageCopyResultEventDetail",
123
+ "bubbles": true,
124
+ "cancelable": true,
125
+ "composed": true
126
+ },
127
+ {
128
+ "name": "dsFeedbackChange",
129
+ "detail": "MessageFeedback | undefined",
130
+ "bubbles": true,
131
+ "cancelable": true,
132
+ "composed": true
133
+ }
134
+ ],
135
+ "methods": [],
136
+ "slots": [],
137
+ "exports": {
138
+ "customElement": "ds-message-actions",
139
+ "react": "DsMessageActions",
140
+ "angular": "DsMessageActions"
141
+ },
142
+ "consumption": {
143
+ "install": "npm install @ds-mo/ui @ds-mo/tokens @ds-mo/icons",
144
+ "cssSetup": "import '@ds-mo/tokens';\nimport '@ds-mo/tokens/reset';\nimport '@ds-mo/tokens/globals';",
145
+ "customElements": {
146
+ "import": "import '@ds-mo/ui/dist/components/ds-message-actions.js';",
147
+ "example": "<ds-message-actions></ds-message-actions>"
148
+ },
149
+ "react": {
150
+ "import": "import { DsMessageActions } from '@ds-mo/ui/react';",
151
+ "example": "<DsMessageActions />"
152
+ },
153
+ "angular": {
154
+ "import": "import { DsMessageActions } from '@ds-mo/ui/angular/ds-message-actions';",
155
+ "example": "<ds-message-actions></ds-message-actions>"
156
+ },
157
+ "peerDependencies": {
158
+ "required": [
159
+ "@ds-mo/tokens ^5.1.1",
160
+ "@ds-mo/icons ^6.4.0"
161
+ ],
162
+ "frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
163
+ }
164
+ }
165
+ },
166
+ "dependencies": [
167
+ "@ds-mo/ui",
168
+ "@ds-mo/tokens"
169
+ ],
170
+ "registryDependencies": [
171
+ "button-unfilled",
172
+ "tooltip"
173
+ ],
174
+ "files": [
175
+ {
176
+ "path": "src/wc/components/MessageActions/MessageActions.css",
177
+ "content": ":host {\n display: inline-flex;\n box-sizing: border-box;\n}\n\n.message-actions {\n display: inline-flex;\n align-items: center;\n gap: var(--dimension-space-050);\n}\n",
178
+ "type": "registry:ui"
179
+ },
180
+ {
181
+ "path": "src/wc/components/MessageActions/MessageActions.tsx",
182
+ "content": "import { Component, Event, EventEmitter, h, Host, Prop, State, Watch } from '@stencil/core';\nimport { ClipboardFeedbackController } from '../../utils/clipboard';\nimport type { MessageCopyResultEventDetail, MessageFeedback } from '../conversation-types';\n\n@Component({\n tag: 'ds-message-actions',\n styleUrl: 'MessageActions.css',\n scoped: true,\n})\nexport class MessageActions {\n @Prop() copyText: string | undefined;\n @Prop() feedbackEnabled: boolean = false;\n @Prop() feedback: MessageFeedback | undefined;\n\n @State() private copied: boolean = false;\n\n @Event() dsCopyResult!: EventEmitter<MessageCopyResultEventDetail>;\n @Event() dsFeedbackChange!: EventEmitter<MessageFeedback | undefined>;\n\n private readonly copyFeedback = new ClipboardFeedbackController(copied => {\n this.copied = copied;\n });\n\n connectedCallback() {\n this.copyFeedback.connect();\n }\n\n @Watch('copyText')\n handleCopyTextChange() {\n this.copyFeedback.reset();\n }\n\n disconnectedCallback() {\n this.copyFeedback.disconnect();\n }\n\n private copy = async () => {\n const success = await this.copyFeedback.copy(this.copyText ?? '');\n if (success === undefined) return;\n\n this.dsCopyResult.emit({ status: success ? 'success' : 'error' });\n };\n\n private changeFeedback(feedback: MessageFeedback, selected: boolean) {\n this.dsFeedbackChange.emit(selected ? feedback : undefined);\n }\n\n render() {\n const copyLabel = this.copied ? 'Copied' : 'Copy message';\n\n return (\n <Host>\n <div class=\"message-actions\" role=\"group\" aria-label=\"Message actions\">\n {this.copyText ? (\n <ds-tooltip label={copyLabel} side=\"top\" size=\"sm\">\n <ds-button-unfilled\n variant=\"icon\"\n icon={this.copied ? 'Check' : 'Copy'}\n size=\"xs\"\n aria-label={copyLabel}\n hasBorder={false}\n onDsClick={this.copy}\n />\n </ds-tooltip>\n ) : null}\n {this.feedbackEnabled ? (\n <ds-tooltip label=\"Good response\" side=\"top\" size=\"sm\">\n <ds-button-unfilled\n variant=\"icon\"\n icon={this.feedback === 'positive' ? 'ThumbsUpFilled' : 'ThumbsUp'}\n size=\"xs\"\n aria-label=\"Good response\"\n hasBorder={false}\n pressed={this.feedback === 'positive'}\n onDsChange={(event: CustomEvent<boolean>) =>\n this.changeFeedback('positive', event.detail)\n }\n />\n </ds-tooltip>\n ) : null}\n {this.feedbackEnabled ? (\n <ds-tooltip label=\"Bad response\" side=\"top\" size=\"sm\">\n <ds-button-unfilled\n variant=\"icon\"\n icon={this.feedback === 'negative' ? 'ThumbsDownFilled' : 'ThumbsDown'}\n size=\"xs\"\n aria-label=\"Bad response\"\n hasBorder={false}\n pressed={this.feedback === 'negative'}\n onDsChange={(event: CustomEvent<boolean>) =>\n this.changeFeedback('negative', event.detail)\n }\n />\n </ds-tooltip>\n ) : null}\n </div>\n </Host>\n );\n }\n}\n",
183
+ "type": "registry:ui"
184
+ }
185
+ ]
186
+ }
@@ -97,7 +97,7 @@
97
97
  },
98
98
  "peerDependencies": {
99
99
  "required": [
100
- "@ds-mo/tokens ^5.1.0",
100
+ "@ds-mo/tokens ^5.1.1",
101
101
  "@ds-mo/icons ^6.4.0"
102
102
  ],
103
103
  "frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
@@ -238,7 +238,7 @@
238
238
  },
239
239
  "peerDependencies": {
240
240
  "required": [
241
- "@ds-mo/tokens ^5.1.0",
241
+ "@ds-mo/tokens ^5.1.1",
242
242
  "@ds-mo/icons ^6.4.0"
243
243
  ],
244
244
  "frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
@@ -191,7 +191,7 @@
191
191
  },
192
192
  "peerDependencies": {
193
193
  "required": [
194
- "@ds-mo/tokens ^5.1.0",
194
+ "@ds-mo/tokens ^5.1.1",
195
195
  "@ds-mo/icons ^6.4.0"
196
196
  ],
197
197
  "frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
3
  "name": "message",
4
4
  "title": "Message",
5
- "description": "Channel-neutral message row owning direction, grouping, author, time, delivery metadata, and header, footer, or action accessory regions.",
5
+ "description": "Channel-neutral message row owning direction, grouping, author, time, delivery metadata, and persistent or metadata action regions.",
6
6
  "type": "registry:ui",
7
7
  "meta": {
8
8
  "library": "@ds-mo/ui",
@@ -13,7 +13,7 @@
13
13
  "intent": {
14
14
  "audience": "general",
15
15
  "status": "experimental",
16
- "summary": "Channel-neutral message row owning direction, grouping, author, time, delivery metadata, and header, footer, or action accessory regions.",
16
+ "summary": "Channel-neutral message row owning direction, grouping, author, time, delivery metadata, and persistent or metadata action regions.",
17
17
  "useWhen": [
18
18
  "A transcript contains messages that need consistent incoming, outgoing, or system layout."
19
19
  ],
@@ -22,6 +22,7 @@
22
22
  ],
23
23
  "commonlyComposedWith": [
24
24
  "component:ds-message-bubble",
25
+ "component:ds-message-actions",
25
26
  "component:ds-message-scroller"
26
27
  ],
27
28
  "patterns": [
@@ -29,14 +30,16 @@
29
30
  ],
30
31
  "accessibility": [
31
32
  "Author and time metadata remain textual when they are necessary to interpret the transcript.",
32
- "Delivery failure uses negative Failed to send text in the footer while the message bubble keeps its normal outgoing treatment; when time is also visible, a tertiary middle dot separates the failure label and timestamp."
33
+ "Delivery failure uses negative Failed to send text in the footer while the message bubble keeps its normal outgoing treatment; when time is also visible, a tertiary middle dot separates the failure label and timestamp.",
34
+ "Hover-only metadata rows remain keyboard reachable, reveal on focus, and stay visible for touch and coarse-pointer input."
33
35
  ],
34
36
  "states": [
35
37
  "Pass an ISO timestamp. Message keeps the full value in dateTime and formats the visible value as a localized time; applications must not render a raw ISO string as visible transcript metadata.",
36
38
  "A visible incoming author uses body-small emphasis with primary foreground; timestamp and delivery metadata remain caption text.",
37
39
  "Visible author, bubble content, and timestamp or delivery footer use consistent 4px vertical gaps.",
38
40
  "Grouping joins adjacent bubble corners, shows the incoming author only at the start, and shows delivery/time metadata only at the end. Outgoing author remains in the accessible message label without repeating a visible You label.",
39
- "Set showAuthor false only when the product makes participant and assistant roles visually self-evident; continue passing author so the accessible message label remains specific."
41
+ "Set showAuthor false only when the product makes participant and assistant roles visually self-evident; continue passing author so the accessible message label remains specific.",
42
+ "Metadata actions precede incoming timestamps and follow outgoing timestamps. In hover mode the complete reserved footer—delivery, timestamp, and actions—fades as one unit without changing geometry; the persistent actions slot retains separate message-level controls."
40
43
  ],
41
44
  "responsiveBehavior": [
42
45
  "Outgoing and incoming rows adapt to available width while preserving their directional alignment."
@@ -89,6 +92,15 @@
89
92
  "required": false,
90
93
  "mutable": false
91
94
  },
95
+ "metadataVisibility": {
96
+ "type": "MessageMetadataVisibility",
97
+ "resolvedType": "\"always\" | \"hover\"",
98
+ "attribute": "metadata-visibility",
99
+ "default": "'always'",
100
+ "required": false,
101
+ "mutable": false,
102
+ "description": "Controls whether the complete metadata footer is persistent or revealed through hover/focus."
103
+ },
92
104
  "scrollAnchor": {
93
105
  "type": "boolean",
94
106
  "resolvedType": "boolean",
@@ -168,6 +180,15 @@
168
180
  "required": false,
169
181
  "mutable": false
170
182
  },
183
+ "metadataVisibility": {
184
+ "type": "MessageMetadataVisibility",
185
+ "resolvedType": "\"always\" | \"hover\"",
186
+ "attribute": "metadata-visibility",
187
+ "default": "'always'",
188
+ "required": false,
189
+ "mutable": false,
190
+ "description": "Controls whether the complete metadata footer is persistent or revealed through hover/focus."
191
+ },
171
192
  "scrollAnchor": {
172
193
  "type": "boolean",
173
194
  "resolvedType": "boolean",
@@ -228,7 +249,7 @@
228
249
  },
229
250
  "peerDependencies": {
230
251
  "required": [
231
- "@ds-mo/tokens ^5.1.0",
252
+ "@ds-mo/tokens ^5.1.1",
232
253
  "@ds-mo/icons ^6.4.0"
233
254
  ],
234
255
  "frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
@@ -245,12 +266,12 @@
245
266
  "files": [
246
267
  {
247
268
  "path": "src/wc/components/Message/Message.css",
248
- "content": ":host {\n display: block;\n box-sizing: border-box;\n content-visibility: auto;\n contain-intrinsic-size: auto var(--dimension-size-800);\n -webkit-user-select: none;\n user-select: none;\n}\n\n:host(.message-host--group-middle),\n:host(.message-host--group-last) {\n margin-block-start: calc(var(--dimension-space-050) - var(--dimension-space-300));\n}\n\n.message {\n display: grid;\n grid-template-columns: minmax(0, 1fr) auto;\n align-items: end;\n gap: 0;\n width: 100%;\n box-sizing: border-box;\n}\n\n.message--outgoing {\n grid-template-columns: minmax(0, 1fr) auto;\n}\n\n.message__header:empty,\n.message__footer:empty,\n.message__actions:empty {\n display: none;\n}\n\n.message__actions:not(:empty) {\n margin-inline-start: var(--dimension-space-075);\n}\n\n.message__body {\n display: flex;\n flex-direction: column;\n min-width: 0;\n gap: var(--dimension-space-050);\n}\n\n.message--incoming .message__body {\n width: 100%;\n}\n\n.message--outgoing .message__body {\n align-items: flex-end;\n}\n\n.message--outgoing .message__header {\n display: none;\n}\n\n.message--system .message__body {\n align-items: center;\n}\n\n.message__header,\n.message__footer,\n.message__actions {\n display: flex;\n align-items: center;\n gap: var(--dimension-space-050);\n}\n\n.message__content {\n width: 100%;\n min-width: 0;\n -webkit-user-select: text;\n user-select: text;\n}\n\n.message--outgoing .message__content {\n display: flex;\n justify-content: flex-end;\n}\n\n.message__footer time {\n display: inline-flex;\n}\n\n.message--group-middle .message__header,\n.message--group-last .message__header {\n display: none;\n}\n\n.message--group-first .message__footer,\n.message--group-middle .message__footer {\n display: none;\n}\n",
269
+ "content": ":host {\n display: block;\n box-sizing: border-box;\n content-visibility: auto;\n contain-intrinsic-size: auto var(--dimension-size-800);\n -webkit-user-select: none;\n user-select: none;\n}\n\n:host(.message-host--group-middle),\n:host(.message-host--group-last) {\n margin-block-start: calc(var(--dimension-space-050) - var(--dimension-space-300));\n}\n\n.message {\n display: grid;\n grid-template-columns: minmax(0, 1fr) auto;\n align-items: end;\n gap: 0;\n width: 100%;\n box-sizing: border-box;\n}\n\n.message--outgoing {\n grid-template-columns: minmax(0, 1fr) auto;\n}\n\n.message__header:empty,\n.message__footer:empty,\n.message__metadata-actions:empty,\n.message__actions:empty {\n display: none;\n}\n\n.message__actions:not(:empty) {\n margin-inline-start: var(--dimension-space-075);\n}\n\n.message__body {\n display: flex;\n flex-direction: column;\n min-width: 0;\n gap: var(--dimension-space-050);\n}\n\n.message--incoming .message__body {\n width: 100%;\n}\n\n.message--outgoing .message__body {\n align-items: flex-end;\n}\n\n.message--outgoing .message__header {\n display: none;\n}\n\n.message--system .message__body {\n align-items: center;\n}\n\n.message__header,\n.message__footer,\n.message__metadata-actions,\n.message__actions {\n display: flex;\n align-items: center;\n gap: var(--dimension-space-050);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .message--metadata-hover .message__footer {\n opacity: 0;\n pointer-events: none;\n transition: opacity var(--effect-motion-short-2);\n }\n\n .message--metadata-hover:hover .message__footer,\n .message--metadata-hover:focus-within .message__footer {\n opacity: 1;\n pointer-events: auto;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .message--metadata-hover .message__footer {\n transition: none;\n }\n}\n\n.message__content {\n width: 100%;\n min-width: 0;\n -webkit-user-select: text;\n user-select: text;\n}\n\n.message--outgoing .message__content {\n display: flex;\n justify-content: flex-end;\n}\n\n.message__footer time {\n display: inline-flex;\n}\n\n.message--group-middle .message__header,\n.message--group-last .message__header {\n display: none;\n}\n\n.message--group-first .message__footer,\n.message--group-middle .message__footer {\n display: none;\n}\n",
249
270
  "type": "registry:ui"
250
271
  },
251
272
  {
252
273
  "path": "src/wc/components/Message/Message.tsx",
253
- "content": "import { Component, h, Host, Prop } from '@stencil/core';\nimport type {\n MessageDeliveryState,\n MessageDirection,\n MessageGroupPosition,\n} from '../conversation-types';\n\nconst DELIVERY_LABELS: Record<MessageDeliveryState, string> = {\n sending: 'Sending',\n sent: 'Sent',\n delivered: 'Delivered',\n read: 'Read',\n failed: 'Failed to send',\n};\n\n@Component({\n tag: 'ds-message',\n styleUrl: 'Message.css',\n scoped: true,\n})\nexport class Message {\n @Prop({ reflect: true }) messageId: string = '';\n @Prop({ reflect: true }) direction: MessageDirection = 'incoming';\n @Prop() groupPosition: MessageGroupPosition = 'single';\n @Prop() author: string = '';\n /** Keep author semantics while allowing products with self-evident roles to hide the visible label. */\n @Prop() showAuthor: boolean = true;\n /** ISO timestamp. */\n @Prop() timestamp: string = '';\n @Prop() deliveryState: MessageDeliveryState | undefined;\n @Prop() streaming: boolean = false;\n @Prop({ reflect: true }) scrollAnchor: boolean = false;\n\n private formattedTimestamp(): string {\n if (!this.timestamp) return '';\n const parsed = Date.parse(this.timestamp);\n if (Number.isNaN(parsed)) return this.timestamp;\n return new Intl.DateTimeFormat(undefined, {\n hour: 'numeric',\n minute: '2-digit',\n }).format(new Date(parsed));\n }\n\n render() {\n const delivery = this.deliveryState ? DELIVERY_LABELS[this.deliveryState] : '';\n const formattedTimestamp = this.formattedTimestamp();\n return (\n <Host\n class={`message-host--group-${this.groupPosition}`}\n data-message-id={this.messageId || undefined}\n data-scroll-anchor={this.scrollAnchor ? 'true' : undefined}\n aria-busy={this.streaming ? 'true' : undefined}\n >\n <article\n class={{\n message: true,\n [`message--${this.direction}`]: true,\n [`message--group-${this.groupPosition}`]: true,\n }}\n aria-label={this.author ? `Message from ${this.author}` : 'Message'}\n >\n <div class=\"message__body\">\n <div class=\"message__header\">\n <slot name=\"header\" />\n {this.author && this.showAuthor ? (\n <ds-text as=\"span\" variant=\"text-body-small\" emphasis color=\"primary\">\n {this.author}\n </ds-text>\n ) : null}\n </div>\n <div class=\"message__content\">\n <slot />\n </div>\n <div class=\"message__footer\">\n <slot name=\"footer\" />\n {delivery ? (\n <ds-text\n as=\"span\"\n variant=\"text-caption\"\n color={this.deliveryState === 'failed' ? 'negative' : 'secondary'}\n >\n {delivery}\n </ds-text>\n ) : null}\n {this.deliveryState === 'failed' && formattedTimestamp ? (\n <ds-text as=\"span\" variant=\"text-caption\" color=\"tertiary\" aria-hidden=\"true\">\n ·\n </ds-text>\n ) : null}\n {formattedTimestamp ? (\n <time dateTime={this.timestamp}>\n <ds-text as=\"span\" variant=\"text-caption\" color=\"secondary\">\n {formattedTimestamp}\n </ds-text>\n </time>\n ) : null}\n </div>\n </div>\n <div class=\"message__actions\">\n <slot name=\"actions\" />\n </div>\n </article>\n </Host>\n );\n }\n}\n",
274
+ "content": "import { Component, h, Host, Prop } from '@stencil/core';\nimport type {\n MessageDeliveryState,\n MessageDirection,\n MessageGroupPosition,\n MessageMetadataVisibility,\n} from '../conversation-types';\n\nconst DELIVERY_LABELS: Record<MessageDeliveryState, string> = {\n sending: 'Sending',\n sent: 'Sent',\n delivered: 'Delivered',\n read: 'Read',\n failed: 'Failed to send',\n};\n\n@Component({\n tag: 'ds-message',\n styleUrl: 'Message.css',\n scoped: true,\n})\nexport class Message {\n @Prop({ reflect: true }) messageId: string = '';\n @Prop({ reflect: true }) direction: MessageDirection = 'incoming';\n @Prop() groupPosition: MessageGroupPosition = 'single';\n @Prop() author: string = '';\n /** Keep author semantics while allowing products with self-evident roles to hide the visible label. */\n @Prop() showAuthor: boolean = true;\n /** ISO timestamp. */\n @Prop() timestamp: string = '';\n @Prop() deliveryState: MessageDeliveryState | undefined;\n @Prop() streaming: boolean = false;\n /** Controls whether the complete metadata footer is persistent or revealed through hover/focus. */\n @Prop() metadataVisibility: MessageMetadataVisibility = 'always';\n @Prop({ reflect: true }) scrollAnchor: boolean = false;\n\n private formattedTimestamp(): string {\n if (!this.timestamp) return '';\n const parsed = Date.parse(this.timestamp);\n if (Number.isNaN(parsed)) return this.timestamp;\n return new Intl.DateTimeFormat(undefined, {\n hour: 'numeric',\n minute: '2-digit',\n }).format(new Date(parsed));\n }\n\n private renderMetadataActions() {\n return (\n <div class=\"message__metadata-actions\">\n <slot name=\"metadata-actions\" />\n </div>\n );\n }\n\n render() {\n const delivery = this.deliveryState ? DELIVERY_LABELS[this.deliveryState] : '';\n const formattedTimestamp = this.formattedTimestamp();\n return (\n <Host\n class={`message-host--group-${this.groupPosition}`}\n data-message-id={this.messageId || undefined}\n data-scroll-anchor={this.scrollAnchor ? 'true' : undefined}\n aria-busy={this.streaming ? 'true' : undefined}\n >\n <article\n class={{\n message: true,\n [`message--${this.direction}`]: true,\n [`message--group-${this.groupPosition}`]: true,\n [`message--metadata-${this.metadataVisibility}`]: true,\n }}\n aria-label={this.author ? `Message from ${this.author}` : 'Message'}\n >\n <div class=\"message__body\">\n <div class=\"message__header\">\n <slot name=\"header\" />\n {this.author && this.showAuthor ? (\n <ds-text as=\"span\" variant=\"text-body-small\" emphasis color=\"primary\">\n {this.author}\n </ds-text>\n ) : null}\n </div>\n <div class=\"message__content\">\n <slot />\n </div>\n <div class=\"message__footer\">\n {this.direction === 'incoming' ? this.renderMetadataActions() : null}\n <slot name=\"footer\" />\n {delivery ? (\n <ds-text\n as=\"span\"\n variant=\"text-caption\"\n color={this.deliveryState === 'failed' ? 'negative' : 'secondary'}\n >\n {delivery}\n </ds-text>\n ) : null}\n {this.deliveryState === 'failed' && formattedTimestamp ? (\n <ds-text as=\"span\" variant=\"text-caption\" color=\"tertiary\" aria-hidden=\"true\">\n ·\n </ds-text>\n ) : null}\n {formattedTimestamp ? (\n <time dateTime={this.timestamp}>\n <ds-text as=\"span\" variant=\"text-caption\" color=\"secondary\">\n {formattedTimestamp}\n </ds-text>\n </time>\n ) : null}\n {this.direction !== 'incoming' ? this.renderMetadataActions() : null}\n </div>\n </div>\n <div class=\"message__actions\">\n <slot name=\"actions\" />\n </div>\n </article>\n </Host>\n );\n }\n}\n",
254
275
  "type": "registry:ui"
255
276
  }
256
277
  ]
@@ -299,7 +299,7 @@
299
299
  "complexPropertyNote": "Assign these non-primitive values as JavaScript properties: currentArea.",
300
300
  "peerDependencies": {
301
301
  "required": [
302
- "@ds-mo/tokens ^5.1.0",
302
+ "@ds-mo/tokens ^5.1.1",
303
303
  "@ds-mo/icons ^6.4.0"
304
304
  ],
305
305
  "frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
@@ -280,7 +280,7 @@
280
280
  "complexPropertyNote": "Assign these non-primitive values as JavaScript properties: sections, subsections.",
281
281
  "peerDependencies": {
282
282
  "required": [
283
- "@ds-mo/tokens ^5.1.0",
283
+ "@ds-mo/tokens ^5.1.1",
284
284
  "@ds-mo/icons ^6.4.0"
285
285
  ],
286
286
  "frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
@@ -153,7 +153,7 @@
153
153
  "complexPropertyNote": "Assign these non-primitive values as JavaScript properties: sections.",
154
154
  "peerDependencies": {
155
155
  "required": [
156
- "@ds-mo/tokens ^5.1.0",
156
+ "@ds-mo/tokens ^5.1.1",
157
157
  "@ds-mo/icons ^6.4.0"
158
158
  ],
159
159
  "frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."