@ds-mo/ui 14.17.1 → 14.18.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 (251) hide show
  1. package/dist/.package-ready.json +1 -1
  2. package/dist/agent-patterns.json +6 -6
  3. package/dist/agent.json +60 -32
  4. package/dist/angular/ds-button-filled.d.ts +1 -1
  5. package/dist/angular/ds-button-filled.js +3 -3
  6. package/dist/angular/ds-button-unfilled.d.ts +1 -1
  7. package/dist/angular/ds-button-unfilled.js +3 -3
  8. package/dist/angular/ds-mobile-bar-nav.d.ts +1 -1
  9. package/dist/angular/ds-mobile-bar-nav.js +3 -3
  10. package/dist/components/ds-agent-activity.js +1 -1
  11. package/dist/components/ds-agent-questionnaire.js +1 -1
  12. package/dist/components/ds-agent-response.js +1 -1
  13. package/dist/components/ds-agent-source-list.js +1 -1
  14. package/dist/components/ds-agent-tool-call.js +1 -1
  15. package/dist/components/ds-attachment-list.js +1 -1
  16. package/dist/components/ds-badge.js +1 -1
  17. package/dist/components/ds-banner.js +1 -1
  18. package/dist/components/ds-bar-action.js +1 -1
  19. package/dist/components/ds-bar-nav.js +1 -1
  20. package/dist/components/ds-bar-title.js +1 -1
  21. package/dist/components/ds-bar-workflow.js +1 -1
  22. package/dist/components/ds-bar-workflow.js.map +1 -1
  23. package/dist/components/ds-breadcrumb.js +1 -1
  24. package/dist/components/ds-button-filled.js +1 -1
  25. package/dist/components/ds-button-unfilled.js +1 -1
  26. package/dist/components/ds-card-chart.js +1 -1
  27. package/dist/components/ds-card-chart.js.map +1 -1
  28. package/dist/components/ds-card-overview.js +1 -1
  29. package/dist/components/ds-card-setting.js +1 -1
  30. package/dist/components/ds-card-setting.js.map +1 -1
  31. package/dist/components/ds-chart-legend.js +1 -1
  32. package/dist/components/ds-chart.js +1 -1
  33. package/dist/components/ds-checkbox.js +1 -1
  34. package/dist/components/ds-chip.js +1 -1
  35. package/dist/components/ds-code-block.js +1 -1
  36. package/dist/components/ds-conversation-list-item.js +1 -1
  37. package/dist/components/ds-conversation-list-section.js +1 -1
  38. package/dist/components/ds-conversation-list.js +1 -1
  39. package/dist/components/ds-divider.js +1 -1
  40. package/dist/components/ds-empty-state.js +1 -1
  41. package/dist/components/ds-field.js +1 -1
  42. package/dist/components/ds-filter-menu.js +1 -1
  43. package/dist/components/ds-input.js +1 -1
  44. package/dist/components/ds-markdown.js +1 -1
  45. package/dist/components/ds-menu.js +1 -1
  46. package/dist/components/ds-message-actions.js +1 -1
  47. package/dist/components/ds-message-actions.js.map +1 -1
  48. package/dist/components/ds-message-bubble.js +1 -1
  49. package/dist/components/ds-message-composer.js +1 -1
  50. package/dist/components/ds-message-scroller.js +1 -1
  51. package/dist/components/ds-message.js +1 -1
  52. package/dist/components/ds-mobile-bar-nav.js +1 -1
  53. package/dist/components/ds-mobile-header.js +1 -1
  54. package/dist/components/ds-mobile-section-switcher.js +1 -1
  55. package/dist/components/ds-mobile-sheet-nav.js +1 -1
  56. package/dist/components/ds-modal.js +1 -1
  57. package/dist/components/ds-pagination.js +1 -1
  58. package/dist/components/ds-panel-nav.js +1 -1
  59. package/dist/components/ds-panel-sub-nav.js +1 -1
  60. package/dist/components/ds-panel-tool-header.js +1 -1
  61. package/dist/components/ds-panel-tool-search.js +1 -1
  62. package/dist/components/ds-panel-tools.js +1 -1
  63. package/dist/components/ds-paper-texture.js +1 -1
  64. package/dist/components/ds-radio.js +1 -1
  65. package/dist/components/ds-scroll-overlay.js +1 -1
  66. package/dist/components/ds-select.js +1 -1
  67. package/dist/components/ds-shell-app.js +1 -1
  68. package/dist/components/ds-shell-app.js.map +1 -1
  69. package/dist/components/ds-shell-page.js +1 -1
  70. package/dist/components/ds-shell-tools.js +1 -1
  71. package/dist/components/ds-skeleton.js +1 -1
  72. package/dist/components/ds-slider.js +1 -1
  73. package/dist/components/ds-swatch-picker.js +1 -1
  74. package/dist/components/ds-switch.js +1 -1
  75. package/dist/components/ds-tab-group.js +1 -1
  76. package/dist/components/ds-table-filter.js +1 -1
  77. package/dist/components/ds-table-filter.js.map +1 -1
  78. package/dist/components/ds-table-saved-views.js +1 -1
  79. package/dist/components/ds-table-sort.js +1 -1
  80. package/dist/components/ds-table-sort.js.map +1 -1
  81. package/dist/components/ds-table-toolbar.js +1 -1
  82. package/dist/components/ds-table-toolbar.js.map +1 -1
  83. package/dist/components/ds-table.js +1 -1
  84. package/dist/components/ds-table.js.map +1 -1
  85. package/dist/components/ds-tag.js +1 -1
  86. package/dist/components/ds-text.js +1 -1
  87. package/dist/components/ds-toast.js +1 -1
  88. package/dist/components/ds-toast.js.map +1 -1
  89. package/dist/components/ds-tooltip-chart.js +1 -1
  90. package/dist/components/ds-tooltip.js +1 -1
  91. package/dist/components/ds-typing-indicator.js +1 -1
  92. package/dist/components/{p-y4jhIkRc.js → p-3WlVHGUe.js} +2 -2
  93. package/dist/components/{p-DAqSoyd4.js → p-9EQ3xLI1.js} +2 -2
  94. package/dist/components/p-9GuNIXuz.js +2 -0
  95. package/dist/components/{p-B0PEg5RF.js.map → p-9GuNIXuz.js.map} +1 -1
  96. package/dist/components/p-B0Byw6OX.js +2 -0
  97. package/dist/components/p-B0Byw6OX.js.map +1 -0
  98. package/dist/components/{p-DSIOINBK.js → p-B145kX-i.js} +2 -2
  99. package/dist/components/{p-DZ55e3j9.js → p-B9ZfoprK.js} +2 -2
  100. package/dist/components/{p-DNJTN_s0.js → p-BBmRD_S0.js} +2 -2
  101. package/dist/components/{p-C4TgWJi9.js → p-BKx2ihnG.js} +2 -2
  102. package/dist/components/{p-Be1sY58w.js → p-BTTxl3ro.js} +2 -2
  103. package/dist/components/p-Ba7DaFkb.js +2 -0
  104. package/dist/components/{p-DEjh3klB.js.map → p-Ba7DaFkb.js.map} +1 -1
  105. package/dist/components/{p-CKmfuK7k.js → p-BcNYvLMi.js} +2 -2
  106. package/dist/components/p-BcNYvLMi.js.map +1 -0
  107. package/dist/components/{p-rrFMAZkj.js → p-BdgoB2ef.js} +2 -2
  108. package/dist/components/{p-U8JIZzI2.js → p-Be-vZpWx.js} +2 -2
  109. package/dist/components/p-Be-vZpWx.js.map +1 -0
  110. package/dist/components/{p-DQqOzDEq.js → p-BiBXCjjo.js} +2 -2
  111. package/dist/components/{p-D1YhYulL.js → p-C6yLERx6.js} +2 -2
  112. package/dist/components/{p-Dg9qiMDa.js → p-C9StMCWg.js} +2 -2
  113. package/dist/components/{p-7tZx4ukz.js → p-CAJIARNW.js} +2 -2
  114. package/dist/components/{p-BVlfQ8xZ.js → p-CBa_6lqj.js} +2 -2
  115. package/dist/components/p-CBa_6lqj.js.map +1 -0
  116. package/dist/components/{p-D2z2DgtX.js → p-CHEt4mW8.js} +2 -2
  117. package/dist/components/p-CI5Ce8VF.js +2 -0
  118. package/dist/components/p-CI5Ce8VF.js.map +1 -0
  119. package/dist/components/{p-BHz78C8h.js → p-CbZ0RU2c.js} +2 -2
  120. package/dist/components/{p-BFSLT0yc.js → p-Cgm7YMLw.js} +2 -2
  121. package/dist/components/{p-BFSLT0yc.js.map → p-Cgm7YMLw.js.map} +1 -1
  122. package/dist/components/{p-Bn8NryHK.js → p-CihNoe1M.js} +2 -2
  123. package/dist/components/{p-D4K9CE8a.js → p-Cj5Hkt6Y.js} +2 -2
  124. package/dist/components/{p-ruPTIV6t.js → p-D6P_1YrL.js} +2 -2
  125. package/dist/components/p-DC_8dK55.js +2 -0
  126. package/dist/components/p-DC_8dK55.js.map +1 -0
  127. package/dist/components/p-DHzn7INS.js +2 -0
  128. package/dist/components/{p-DIEK54fV.js.map → p-DHzn7INS.js.map} +1 -1
  129. package/dist/components/{p-BNkU9dcc.js → p-DJ0GCCFa.js} +2 -2
  130. package/dist/components/p-DKY3IJqt.js +2 -0
  131. package/dist/components/p-DKY3IJqt.js.map +1 -0
  132. package/dist/components/{p-DsJW5W3W.js → p-DMaQkEYP.js} +2 -2
  133. package/dist/components/p-DNpLDbWY.js +2 -0
  134. package/dist/components/p-DNpLDbWY.js.map +1 -0
  135. package/dist/components/{p-PD1Vfg0t.js → p-DPR0TGGJ.js} +2 -2
  136. package/dist/components/{p-CMFvaY05.js → p-DPpHSIzS.js} +2 -2
  137. package/dist/components/p-DQyvAN44.js +2 -0
  138. package/dist/components/p-DQyvAN44.js.map +1 -0
  139. package/dist/components/p-DaxKVtZG.js +2 -0
  140. package/dist/components/p-DaxKVtZG.js.map +1 -0
  141. package/dist/components/{p-a6qP60zb.js → p-DbJahF86.js} +2 -2
  142. package/dist/components/{p-D8Qd6gkf.js → p-Dd8VKdAO.js} +2 -2
  143. package/dist/components/{p-C0X2zKbB.js → p-Dginv8PT.js} +2 -2
  144. package/dist/components/{p-BHDkJmrC.js → p-Dl3gciej.js} +2 -2
  145. package/dist/components/p-Drcl4zS9.js +2 -0
  146. package/dist/components/p-DzPw6aDK.js +2 -0
  147. package/dist/components/p-DzPw6aDK.js.map +1 -0
  148. package/dist/components/{p-BhYWdbEp.js → p-HZz5aJKz.js} +2 -2
  149. package/dist/components/{p-BGXqFq2C.js → p-LHzBS95u.js} +2 -2
  150. package/dist/components/{p-DHuyj-9J.js → p-hp_2W2Qy.js} +2 -2
  151. package/dist/components/p-jk2-y5dH.js +2 -0
  152. package/dist/components/p-jk2-y5dH.js.map +1 -0
  153. package/dist/components/{p-CzcWZiH-.js → p-oCc3mz_c.js} +2 -2
  154. package/dist/docs/components.json +234 -19
  155. package/dist/lib/components/ButtonFilled/ButtonFilled.d.ts +2 -0
  156. package/dist/lib/components/PanelTools/panel-tools-types.d.ts +2 -2
  157. package/dist/lib/shell/index.js +17 -25
  158. package/dist/lib/shell/index.js.map +2 -2
  159. package/dist/lib/shell/shell-responsive.d.ts +6 -1
  160. package/dist/lib/utils/anchored-position.d.ts +2 -0
  161. package/dist/lib/utils/button-render.d.ts +1 -0
  162. package/dist/mcp-data/registry/button-filled.json +20 -1
  163. package/dist/mcp-data/registry/button-unfilled.json +23 -3
  164. package/dist/mcp-data/registry/filter-menu.json +2 -2
  165. package/dist/mcp-data/registry/input.json +2 -2
  166. package/dist/mcp-data/registry/menu.json +2 -1
  167. package/dist/mcp-data/registry/mobile-bar-nav.json +57 -6
  168. package/dist/mcp-data/registry/pagination.json +5 -4
  169. package/dist/mcp-data/registry/panel-tools.json +4 -4
  170. package/dist/mcp-data/registry/registry.json +59 -31
  171. package/dist/mcp-data/registry/select.json +2 -2
  172. package/dist/mcp-data/registry/shell-app.json +5 -5
  173. package/dist/mcp-data/registry/shell-tools.json +4 -4
  174. package/dist/mcp-data/registry/table-filter.json +3 -3
  175. package/dist/mcp-data/registry/table-sort.json +5 -4
  176. package/dist/mcp-data/registry/table-toolbar.json +10 -9
  177. package/dist/mcp-data/registry/table.json +4 -4
  178. package/dist/styles/table.css +30 -10
  179. package/dist/types/components/ButtonFilled/ButtonFilled.d.ts +2 -0
  180. package/dist/types/components/ButtonUnfilled/ButtonUnfilled.d.ts +2 -0
  181. package/dist/types/components/Menu/Menu.d.ts +1 -0
  182. package/dist/types/components/MobileBarNav/MobileBarNav.d.ts +4 -0
  183. package/dist/types/components/Pagination/Pagination.d.ts +8 -0
  184. package/dist/types/components/PanelTools/panel-tools-types.d.ts +2 -2
  185. package/dist/types/components/Table/Table.d.ts +1 -0
  186. package/dist/types/components/TableToolbar/TableToolbar.d.ts +5 -0
  187. package/dist/types/components.d.ts +51 -0
  188. package/dist/types/shell/shell-responsive.d.ts +6 -1
  189. package/dist/types/utils/anchored-position.d.ts +2 -0
  190. package/dist/types/utils/button-render.d.ts +1 -0
  191. package/dist/vue/ds-button-filled.js +1 -0
  192. package/dist/vue/ds-button-unfilled.js +1 -0
  193. package/dist/vue/ds-mobile-bar-nav.js +3 -0
  194. package/package.json +1 -1
  195. package/dist/components/p-23u3lIr1.js +0 -2
  196. package/dist/components/p-Asi9h3pa.js +0 -2
  197. package/dist/components/p-Asi9h3pa.js.map +0 -1
  198. package/dist/components/p-B0PEg5RF.js +0 -2
  199. package/dist/components/p-BBxcMVqk.js +0 -2
  200. package/dist/components/p-BBxcMVqk.js.map +0 -1
  201. package/dist/components/p-BEvBJ65v.js +0 -2
  202. package/dist/components/p-BEvBJ65v.js.map +0 -1
  203. package/dist/components/p-BVlfQ8xZ.js.map +0 -1
  204. package/dist/components/p-BX0BMhgz.js +0 -2
  205. package/dist/components/p-BX0BMhgz.js.map +0 -1
  206. package/dist/components/p-Bh67XbfB.js +0 -2
  207. package/dist/components/p-Bh67XbfB.js.map +0 -1
  208. package/dist/components/p-CKmfuK7k.js.map +0 -1
  209. package/dist/components/p-DEjh3klB.js +0 -2
  210. package/dist/components/p-DIEK54fV.js +0 -2
  211. package/dist/components/p-DOqnJVos.js +0 -2
  212. package/dist/components/p-DOqnJVos.js.map +0 -1
  213. package/dist/components/p-Dr4AgJbV.js +0 -2
  214. package/dist/components/p-Dr4AgJbV.js.map +0 -1
  215. package/dist/components/p-NzGRAPcF.js +0 -2
  216. package/dist/components/p-NzGRAPcF.js.map +0 -1
  217. package/dist/components/p-U8JIZzI2.js.map +0 -1
  218. package/dist/components/p-ZZzC52Ea.js +0 -2
  219. package/dist/components/p-ZZzC52Ea.js.map +0 -1
  220. package/dist/components/p-__lNIImO.js +0 -2
  221. package/dist/components/p-__lNIImO.js.map +0 -1
  222. /package/dist/components/{p-y4jhIkRc.js.map → p-3WlVHGUe.js.map} +0 -0
  223. /package/dist/components/{p-DAqSoyd4.js.map → p-9EQ3xLI1.js.map} +0 -0
  224. /package/dist/components/{p-DSIOINBK.js.map → p-B145kX-i.js.map} +0 -0
  225. /package/dist/components/{p-DZ55e3j9.js.map → p-B9ZfoprK.js.map} +0 -0
  226. /package/dist/components/{p-DNJTN_s0.js.map → p-BBmRD_S0.js.map} +0 -0
  227. /package/dist/components/{p-C4TgWJi9.js.map → p-BKx2ihnG.js.map} +0 -0
  228. /package/dist/components/{p-Be1sY58w.js.map → p-BTTxl3ro.js.map} +0 -0
  229. /package/dist/components/{p-rrFMAZkj.js.map → p-BdgoB2ef.js.map} +0 -0
  230. /package/dist/components/{p-DQqOzDEq.js.map → p-BiBXCjjo.js.map} +0 -0
  231. /package/dist/components/{p-D1YhYulL.js.map → p-C6yLERx6.js.map} +0 -0
  232. /package/dist/components/{p-Dg9qiMDa.js.map → p-C9StMCWg.js.map} +0 -0
  233. /package/dist/components/{p-7tZx4ukz.js.map → p-CAJIARNW.js.map} +0 -0
  234. /package/dist/components/{p-D2z2DgtX.js.map → p-CHEt4mW8.js.map} +0 -0
  235. /package/dist/components/{p-BHz78C8h.js.map → p-CbZ0RU2c.js.map} +0 -0
  236. /package/dist/components/{p-Bn8NryHK.js.map → p-CihNoe1M.js.map} +0 -0
  237. /package/dist/components/{p-D4K9CE8a.js.map → p-Cj5Hkt6Y.js.map} +0 -0
  238. /package/dist/components/{p-ruPTIV6t.js.map → p-D6P_1YrL.js.map} +0 -0
  239. /package/dist/components/{p-BNkU9dcc.js.map → p-DJ0GCCFa.js.map} +0 -0
  240. /package/dist/components/{p-DsJW5W3W.js.map → p-DMaQkEYP.js.map} +0 -0
  241. /package/dist/components/{p-PD1Vfg0t.js.map → p-DPR0TGGJ.js.map} +0 -0
  242. /package/dist/components/{p-CMFvaY05.js.map → p-DPpHSIzS.js.map} +0 -0
  243. /package/dist/components/{p-a6qP60zb.js.map → p-DbJahF86.js.map} +0 -0
  244. /package/dist/components/{p-D8Qd6gkf.js.map → p-Dd8VKdAO.js.map} +0 -0
  245. /package/dist/components/{p-C0X2zKbB.js.map → p-Dginv8PT.js.map} +0 -0
  246. /package/dist/components/{p-BHDkJmrC.js.map → p-Dl3gciej.js.map} +0 -0
  247. /package/dist/components/{p-23u3lIr1.js.map → p-Drcl4zS9.js.map} +0 -0
  248. /package/dist/components/{p-BhYWdbEp.js.map → p-HZz5aJKz.js.map} +0 -0
  249. /package/dist/components/{p-BGXqFq2C.js.map → p-LHzBS95u.js.map} +0 -0
  250. /package/dist/components/{p-DHuyj-9J.js.map → p-hp_2W2Qy.js.map} +0 -0
  251. /package/dist/components/{p-CzcWZiH-.js.map → p-oCc3mz_c.js.map} +0 -0
@@ -29,7 +29,7 @@
29
29
  ],
30
30
  "accessibility": [
31
31
  "The component renders a named navigation region, gives its compact page-size noun a complete accessible label, and gives every icon-only boundary control an explicit page action name.",
32
- "The visible page-size cluster states the localized noun, selected size, and total, for example Rows: 25 of 500. The page position uses compact ‘of’ phrasing, for example 1 of 20. A polite atomic status update retains the complete current range and ‘Page’ phrasing with the localized item noun.",
32
+ "The visible page-size cluster states the localized noun, selected size, and current-page count of total, for example Rows: [25] 25 of 500. The page position uses compact ‘of’ phrasing, for example 1 of 20. A polite atomic status update retains the complete current range and ‘Page’ phrasing with the localized item noun.",
33
33
  "Left and Right Arrow request the adjacent page while focus is within pagination navigation controls. The page-size combobox retains its own arrow-key behavior.",
34
34
  "Inactive boundary controls and every control during loading remain non-interactive while retaining their accessible names."
35
35
  ],
@@ -41,7 +41,8 @@
41
41
  "fitToPage appends a Fit to page choice. fitPageSize supplies its effective whole-item capacity; the closed trigger says Fit while the range continues to expose the resulting count. fitToPageInactive keeps that choice visible but unavailable when fitting is not meaningful for the current data shape. Pagination does not measure layouts itself."
42
42
  ],
43
43
  "responsiveBehavior": [
44
- "The compact controls remain on one line with noun, selected size, total, a vertical divider, and page position. When inline space is still insufficient, the pagination surface scrolls horizontally rather than wrapping labels away from their controls."
44
+ "The full-width controls remain on one line with noun, selected size, current-page count of total, a vertical divider, page position, and first, previous, next, and last navigation.",
45
+ "Inside a ds-table container narrower than 900px, pagination hides the visible current-page count and first/last boundary controls while retaining the page-size noun and selector plus previous, page position, and next. The complete current range remains available to assistive technology."
45
46
  ],
46
47
  "frameworkCaveats": {
47
48
  "customElements": [
@@ -403,12 +404,12 @@
403
404
  "files": [
404
405
  {
405
406
  "path": "src/wc/components/Pagination/Pagination.css",
406
- "content": "@import '../../utils/visually-hidden.css';\n\n:host {\n display: block;\n box-sizing: border-box;\n min-width: 0;\n max-width: 100%;\n overflow-x: auto;\n scrollbar-width: none;\n}\n\n:host::-webkit-scrollbar {\n display: none;\n}\n\n.pagination,\n.pagination__page-size,\n.pagination__navigation {\n display: flex;\n align-items: center;\n box-sizing: border-box;\n}\n\n.pagination {\n width: max-content;\n min-width: 100%;\n gap: var(--dimension-space-200);\n}\n\n.pagination__page-size,\n.pagination__navigation {\n flex: 0 0 auto;\n}\n\n.pagination__page-size {\n gap: var(--dimension-space-100);\n padding-inline-end: var(--dimension-space-100);\n}\n\n.pagination__navigation {\n gap: var(--dimension-space-050);\n}\n\n.pagination__total,\n.pagination__page,\n.pagination__label {\n display: inline-flex;\n align-items: center;\n flex: 0 0 auto;\n white-space: nowrap;\n}\n\n.pagination__page {\n justify-content: center;\n min-inline-size: 0;\n padding-inline: var(--dimension-space-100);\n}\n",
407
+ "content": "@import '../../utils/visually-hidden.css';\n\n:host {\n display: block;\n box-sizing: border-box;\n min-width: 0;\n max-width: 100%;\n overflow-x: auto;\n scrollbar-width: none;\n}\n\n:host::-webkit-scrollbar {\n display: none;\n}\n\n.pagination,\n.pagination__page-size,\n.pagination__navigation {\n display: flex;\n align-items: center;\n box-sizing: border-box;\n}\n\n.pagination {\n width: max-content;\n min-width: 100%;\n gap: var(--dimension-space-200);\n}\n\n.pagination__page-size,\n.pagination__navigation {\n flex: 0 0 auto;\n}\n\n.pagination__page-size {\n gap: 0;\n padding-inline-end: var(--dimension-space-100);\n}\n\n.pagination__navigation {\n gap: var(--dimension-space-050);\n}\n\n.pagination__total,\n.pagination__page,\n.pagination__label {\n display: inline-flex;\n align-items: center;\n flex: 0 0 auto;\n white-space: nowrap;\n}\n\n.pagination__total {\n margin-inline-start: var(--dimension-space-100);\n}\n\n.pagination .pagination__page-size-select {\n --ds-select-fg-active: var(--color-foreground-secondary);\n}\n\n.pagination__page {\n justify-content: center;\n min-inline-size: 0;\n padding-inline: var(--dimension-space-100);\n}\n\n.pagination--table-compact .pagination__total,\n.pagination--table-compact .pagination__boundary {\n display: none;\n}\n\n.pagination--table-compact {\n gap: var(--dimension-space-100);\n}\n\n.pagination--table-compact .pagination__page-size {\n padding-inline-end: 0;\n}\n",
407
408
  "type": "registry:ui"
408
409
  },
409
410
  {
410
411
  "path": "src/wc/components/Pagination/Pagination.tsx",
411
- "content": "import { Component, Event, EventEmitter, h, Host, Prop } from '@stencil/core';\nimport { resolvePaginationState } from './pagination-model';\nimport type {\n PaginationChangeDetail,\n PaginationPageSizeMode,\n} from './pagination-types';\n\nlet paginationId = 0;\n\n@Component({\n tag: 'ds-pagination',\n styleUrl: 'Pagination.css',\n scoped: true,\n})\nexport class Pagination {\n /** Controlled zero-based page index. */\n @Prop() pageIndex: number = 0;\n /** Controlled number of top-level items on a full page. */\n @Prop() pageSize: number = 25;\n /** Whether pageSize is a fixed choice or a measured Fit snapshot. */\n @Prop() pageSizeMode: PaginationPageSizeMode = 'fixed';\n /** Controlled total number of top-level items across every page. */\n @Prop() totalItems: number = 0;\n /** Available page sizes. Assign arrays through JavaScript. */\n @Prop() pageSizeOptions: number[] = [25, 50, 100, 200];\n /** Include the Fit to page choice. */\n @Prop() fitToPage: boolean = false;\n /** Keep the Fit to page choice visible but unavailable. */\n @Prop() fitToPageInactive: boolean = false;\n /** Effective whole-item capacity to request when Fit is selected. */\n @Prop() fitPageSize: number | undefined;\n /** Full choice-list label for Fit. */\n @Prop() fitPageSizeLabel: string = 'Fit to page';\n /** Compact closed-trigger label while Fit is selected. */\n @Prop() fitPageSizeTriggerLabel: string = 'Fit';\n /** Localized plural noun used by assistive range announcements. */\n @Prop() itemLabel: string = 'items';\n /** Visible page-size control label. */\n @Prop() pageSizeLabel: string = 'Items';\n /** Accessible page-size control label. Defaults to “{pageSizeLabel} per page”. */\n @Prop() pageSizeAriaLabel: string | undefined;\n /** Accessible name for the pagination navigation region. */\n @Prop() label: string = 'Pagination';\n /** Prevent interaction while the owner replaces the current data page. */\n @Prop() loading: boolean = false;\n\n /** Emits the complete next controlled state after a page or page-size request. */\n @Event() dsChange!: EventEmitter<PaginationChangeDetail>;\n\n private readonly pageSizeControlId = `ds-pagination-${++paginationId}-page-size`;\n\n private requestPage(pageIndex: number): void {\n const state = this.resolvedState;\n if (this.loading || pageIndex === state.pageIndex || pageIndex < 0 || pageIndex >= state.totalPages) {\n return;\n }\n this.dsChange.emit({\n pageIndex,\n pageSize: state.pageSize,\n pageSizeMode: state.pageSizeMode,\n totalItems: state.totalItems,\n pageSizeOptions: state.pageSizeOptions,\n fitToPage: this.fitToPage,\n fitToPageInactive: this.fitToPageInactive,\n fitPageSize: this.resolvedFitPageSize,\n fitPageSizeLabel: this.fitPageSizeLabel,\n fitPageSizeTriggerLabel: this.fitPageSizeTriggerLabel,\n itemLabel: this.itemLabel,\n pageSizeLabel: this.pageSizeLabel,\n ariaLabel: this.label,\n previousPageIndex: state.pageIndex,\n previousPageSize: state.pageSize,\n previousPageSizeMode: state.pageSizeMode,\n reason: 'page',\n });\n }\n\n private requestPageSize(value: string | string[]): void {\n if (\n this.loading ||\n typeof value !== 'string' ||\n (value === 'fit' && this.fitToPageInactive)\n ) return;\n const state = this.resolvedState;\n const nextMode: PaginationPageSizeMode = value === 'fit' ? 'fit' : 'fixed';\n const pageSize = nextMode === 'fit' ? this.resolvedFitPageSize : Number(value);\n if (!pageSize || (pageSize === state.pageSize && nextMode === state.pageSizeMode)) return;\n this.dsChange.emit({\n pageIndex: 0,\n pageSize: Math.trunc(pageSize),\n pageSizeMode: nextMode,\n totalItems: state.totalItems,\n pageSizeOptions: state.pageSizeOptions,\n fitToPage: this.fitToPage,\n fitToPageInactive: this.fitToPageInactive,\n fitPageSize: this.resolvedFitPageSize,\n fitPageSizeLabel: this.fitPageSizeLabel,\n fitPageSizeTriggerLabel: this.fitPageSizeTriggerLabel,\n itemLabel: this.itemLabel,\n pageSizeLabel: this.pageSizeLabel,\n ariaLabel: this.label,\n previousPageIndex: state.pageIndex,\n previousPageSize: state.pageSize,\n previousPageSizeMode: state.pageSizeMode,\n reason: 'page-size',\n });\n }\n\n private handleKeyDown(event: KeyboardEvent): void {\n if (event.defaultPrevented || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) {\n return;\n }\n const fromChoiceControl = event.composedPath().some(node =>\n node instanceof HTMLElement &&\n (node.tagName === 'DS-SELECT' || ['INPUT', 'SELECT', 'TEXTAREA'].includes(node.tagName)),\n );\n if (fromChoiceControl || (event.key !== 'ArrowLeft' && event.key !== 'ArrowRight')) return;\n const state = this.resolvedState;\n const pageIndex = event.key === 'ArrowLeft' ? state.pageIndex - 1 : state.pageIndex + 1;\n if (this.loading || pageIndex < 0 || pageIndex >= state.totalPages) return;\n event.preventDefault();\n this.requestPage(pageIndex);\n }\n\n private get resolvedFitPageSize(): number | undefined {\n return Number.isFinite(this.fitPageSize) && Number(this.fitPageSize) > 0\n ? Math.trunc(Number(this.fitPageSize))\n : undefined;\n }\n\n private get resolvedState() {\n return resolvePaginationState({\n pageIndex: this.pageIndex,\n pageSize: this.pageSize,\n pageSizeMode: this.pageSizeMode,\n totalItems: this.totalItems,\n pageSizeOptions: this.pageSizeOptions,\n });\n }\n\n render() {\n const state = this.resolvedState;\n const atStart = state.pageIndex === 0;\n const atEnd = state.pageIndex === state.totalPages - 1;\n const total = `of ${state.totalItems}`;\n const page = `${state.pageIndex + 1} of ${state.totalPages}`;\n const announcedRange = `${state.firstItem}–${state.lastItem} of ${state.totalItems}`;\n const announcedPage = `Page ${state.pageIndex + 1} of ${state.totalPages}`;\n const pageSizeAriaLabel = this.pageSizeAriaLabel ?? `${this.pageSizeLabel} per page`;\n const options: Array<{ label: string; value: string; isInactive?: boolean }> =\n state.pageSizeOptions.map(value => ({ label: String(value), value: String(value) }));\n if (this.fitToPage) {\n options.push({\n label: this.fitPageSizeLabel,\n value: 'fit',\n isInactive: this.fitToPageInactive || this.resolvedFitPageSize === undefined,\n });\n }\n\n return (\n <Host>\n <nav\n class=\"pagination\"\n aria-label={this.label}\n aria-busy={this.loading ? 'true' : undefined}\n onKeyDown={event => this.handleKeyDown(event)}\n >\n <div class=\"pagination__page-size\">\n <ds-text\n class=\"pagination__label\"\n as=\"span\"\n variant=\"text-body-medium\"\n color=\"secondary\"\n aria-hidden=\"true\"\n >\n {this.pageSizeLabel}:\n </ds-text>\n <span id={`${this.pageSizeControlId}-label`} class=\"ds-visually-hidden\">\n {pageSizeAriaLabel}\n </span>\n <ds-select\n size=\"md\"\n inputId={this.pageSizeControlId}\n ariaLabelledby={`${this.pageSizeControlId}-label`}\n options={options}\n value={state.pageSizeMode === 'fit' ? 'fit' : String(state.pageSize)}\n triggerLabel={state.pageSizeMode === 'fit' ? this.fitPageSizeTriggerLabel : undefined}\n indicator=\"up-down\"\n allowClear={false}\n activeFill={false}\n isInactive={this.loading}\n onDsChange={event => {\n event.stopPropagation();\n this.requestPageSize(event.detail);\n }}\n />\n <ds-text\n class=\"pagination__total\"\n as=\"span\"\n variant=\"text-body-medium\"\n color=\"secondary\"\n fontFeature=\"tabular-nums\"\n >\n {total}\n </ds-text>\n </div>\n <ds-divider class=\"pagination__divider\" orientation=\"vertical\" length=\"24px\" />\n <div class=\"pagination__navigation\">\n <ds-button-unfilled\n variant=\"icon\"\n size=\"md\"\n icon=\"ChevronLeftDouble\"\n ariaLabel=\"First page\"\n hasBorder={false}\n isInactive={this.loading || atStart}\n onDsClick={() => this.requestPage(0)}\n />\n <ds-button-unfilled\n variant=\"icon\"\n size=\"md\"\n icon=\"ChevronLeft\"\n ariaLabel=\"Previous page\"\n hasBorder={false}\n isInactive={this.loading || atStart}\n onDsClick={() => this.requestPage(state.pageIndex - 1)}\n />\n <ds-text\n class=\"pagination__page\"\n as=\"span\"\n variant=\"text-body-medium\"\n color=\"secondary\"\n fontFeature=\"tabular-nums\"\n >\n {page}\n </ds-text>\n <ds-button-unfilled\n variant=\"icon\"\n size=\"md\"\n icon=\"ChevronRight\"\n ariaLabel=\"Next page\"\n hasBorder={false}\n isInactive={this.loading || atEnd}\n onDsClick={() => this.requestPage(state.pageIndex + 1)}\n />\n <ds-button-unfilled\n variant=\"icon\"\n size=\"md\"\n icon=\"ChevronRightDouble\"\n ariaLabel=\"Last page\"\n hasBorder={false}\n isInactive={this.loading || atEnd}\n onDsClick={() => this.requestPage(state.totalPages - 1)}\n />\n </div>\n <span class=\"ds-visually-hidden\" role=\"status\" aria-live=\"polite\" aria-atomic=\"true\">\n {announcedRange} {this.itemLabel}. {announcedPage}.\n </span>\n </nav>\n </Host>\n );\n }\n}\n",
412
+ "content": "import { Component, Element, Event, EventEmitter, h, Host, Prop, State } from '@stencil/core';\nimport { observeTableCaptionCompact } from '../../utils/table-caption-compact';\nimport { resolvePaginationState } from './pagination-model';\nimport type {\n PaginationChangeDetail,\n PaginationPageSizeMode,\n} from './pagination-types';\n\nlet paginationId = 0;\n\n@Component({\n tag: 'ds-pagination',\n styleUrl: 'Pagination.css',\n scoped: true,\n})\nexport class Pagination {\n @Element() el!: HTMLElement;\n\n /** Controlled zero-based page index. */\n @Prop() pageIndex: number = 0;\n /** Controlled number of top-level items on a full page. */\n @Prop() pageSize: number = 25;\n /** Whether pageSize is a fixed choice or a measured Fit snapshot. */\n @Prop() pageSizeMode: PaginationPageSizeMode = 'fixed';\n /** Controlled total number of top-level items across every page. */\n @Prop() totalItems: number = 0;\n /** Available page sizes. Assign arrays through JavaScript. */\n @Prop() pageSizeOptions: number[] = [25, 50, 100, 200];\n /** Include the Fit to page choice. */\n @Prop() fitToPage: boolean = false;\n /** Keep the Fit to page choice visible but unavailable. */\n @Prop() fitToPageInactive: boolean = false;\n /** Effective whole-item capacity to request when Fit is selected. */\n @Prop() fitPageSize: number | undefined;\n /** Full choice-list label for Fit. */\n @Prop() fitPageSizeLabel: string = 'Fit to page';\n /** Compact closed-trigger label while Fit is selected. */\n @Prop() fitPageSizeTriggerLabel: string = 'Fit';\n /** Localized plural noun used by assistive range announcements. */\n @Prop() itemLabel: string = 'items';\n /** Visible page-size control label. */\n @Prop() pageSizeLabel: string = 'Items';\n /** Accessible page-size control label. Defaults to “{pageSizeLabel} per page”. */\n @Prop() pageSizeAriaLabel: string | undefined;\n /** Accessible name for the pagination navigation region. */\n @Prop() label: string = 'Pagination';\n /** Prevent interaction while the owner replaces the current data page. */\n @Prop() loading: boolean = false;\n\n /** Emits the complete next controlled state after a page or page-size request. */\n @Event() dsChange!: EventEmitter<PaginationChangeDetail>;\n\n @State() private tableCompact = false;\n\n private readonly pageSizeControlId = `ds-pagination-${++paginationId}-page-size`;\n private tableCompactDisconnect: (() => void) | undefined;\n private hasLoaded = false;\n\n componentDidLoad(): void {\n this.hasLoaded = true;\n this.syncTableCompactObserver();\n }\n\n connectedCallback(): void {\n if (this.hasLoaded) this.syncTableCompactObserver();\n }\n\n disconnectedCallback(): void {\n this.tableCompactDisconnect?.();\n this.tableCompactDisconnect = undefined;\n }\n\n private syncTableCompactObserver(): void {\n this.tableCompactDisconnect?.();\n this.tableCompactDisconnect = observeTableCaptionCompact(this.el, compact => {\n if (this.tableCompact !== compact) this.tableCompact = compact;\n });\n }\n\n private requestPage(pageIndex: number): void {\n const state = this.resolvedState;\n if (this.loading || pageIndex === state.pageIndex || pageIndex < 0 || pageIndex >= state.totalPages) {\n return;\n }\n this.dsChange.emit({\n pageIndex,\n pageSize: state.pageSize,\n pageSizeMode: state.pageSizeMode,\n totalItems: state.totalItems,\n pageSizeOptions: state.pageSizeOptions,\n fitToPage: this.fitToPage,\n fitToPageInactive: this.fitToPageInactive,\n fitPageSize: this.resolvedFitPageSize,\n fitPageSizeLabel: this.fitPageSizeLabel,\n fitPageSizeTriggerLabel: this.fitPageSizeTriggerLabel,\n itemLabel: this.itemLabel,\n pageSizeLabel: this.pageSizeLabel,\n ariaLabel: this.label,\n previousPageIndex: state.pageIndex,\n previousPageSize: state.pageSize,\n previousPageSizeMode: state.pageSizeMode,\n reason: 'page',\n });\n }\n\n private requestPageSize(value: string | string[]): void {\n if (\n this.loading ||\n typeof value !== 'string' ||\n (value === 'fit' && this.fitToPageInactive)\n ) return;\n const state = this.resolvedState;\n const nextMode: PaginationPageSizeMode = value === 'fit' ? 'fit' : 'fixed';\n const pageSize = nextMode === 'fit' ? this.resolvedFitPageSize : Number(value);\n if (!pageSize || (pageSize === state.pageSize && nextMode === state.pageSizeMode)) return;\n this.dsChange.emit({\n pageIndex: 0,\n pageSize: Math.trunc(pageSize),\n pageSizeMode: nextMode,\n totalItems: state.totalItems,\n pageSizeOptions: state.pageSizeOptions,\n fitToPage: this.fitToPage,\n fitToPageInactive: this.fitToPageInactive,\n fitPageSize: this.resolvedFitPageSize,\n fitPageSizeLabel: this.fitPageSizeLabel,\n fitPageSizeTriggerLabel: this.fitPageSizeTriggerLabel,\n itemLabel: this.itemLabel,\n pageSizeLabel: this.pageSizeLabel,\n ariaLabel: this.label,\n previousPageIndex: state.pageIndex,\n previousPageSize: state.pageSize,\n previousPageSizeMode: state.pageSizeMode,\n reason: 'page-size',\n });\n }\n\n private handleKeyDown(event: KeyboardEvent): void {\n if (event.defaultPrevented || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) {\n return;\n }\n const fromChoiceControl = event.composedPath().some(node =>\n node instanceof HTMLElement &&\n (node.tagName === 'DS-SELECT' || ['INPUT', 'SELECT', 'TEXTAREA'].includes(node.tagName)),\n );\n if (fromChoiceControl || (event.key !== 'ArrowLeft' && event.key !== 'ArrowRight')) return;\n const state = this.resolvedState;\n const pageIndex = event.key === 'ArrowLeft' ? state.pageIndex - 1 : state.pageIndex + 1;\n if (this.loading || pageIndex < 0 || pageIndex >= state.totalPages) return;\n event.preventDefault();\n this.requestPage(pageIndex);\n }\n\n private get resolvedFitPageSize(): number | undefined {\n return Number.isFinite(this.fitPageSize) && Number(this.fitPageSize) > 0\n ? Math.trunc(Number(this.fitPageSize))\n : undefined;\n }\n\n private get resolvedState() {\n return resolvePaginationState({\n pageIndex: this.pageIndex,\n pageSize: this.pageSize,\n pageSizeMode: this.pageSizeMode,\n totalItems: this.totalItems,\n pageSizeOptions: this.pageSizeOptions,\n });\n }\n\n render() {\n const state = this.resolvedState;\n const atStart = state.pageIndex === 0;\n const atEnd = state.pageIndex === state.totalPages - 1;\n const visibleItems = state.totalItems === 0 ? 0 : state.lastItem - state.firstItem + 1;\n const total = `${visibleItems} of ${state.totalItems}`;\n const page = `${state.pageIndex + 1} of ${state.totalPages}`;\n const announcedRange = `${state.firstItem}–${state.lastItem} of ${state.totalItems}`;\n const announcedPage = `Page ${state.pageIndex + 1} of ${state.totalPages}`;\n const pageSizeAriaLabel = this.pageSizeAriaLabel ?? `${this.pageSizeLabel} per page`;\n const options: Array<{ label: string; value: string; isInactive?: boolean }> =\n state.pageSizeOptions.map(value => ({ label: String(value), value: String(value) }));\n if (this.fitToPage) {\n options.push({\n label: this.fitPageSizeLabel,\n value: 'fit',\n isInactive: this.fitToPageInactive || this.resolvedFitPageSize === undefined,\n });\n }\n\n return (\n <Host>\n <nav\n class={{\n pagination: true,\n 'pagination--table-compact': this.tableCompact,\n }}\n aria-label={this.label}\n aria-busy={this.loading ? 'true' : undefined}\n onKeyDown={event => this.handleKeyDown(event)}\n >\n <div class=\"pagination__page-size\">\n <ds-text\n class=\"pagination__label\"\n as=\"span\"\n variant=\"text-body-medium\"\n color=\"secondary\"\n aria-hidden=\"true\"\n >\n {this.pageSizeLabel}:\n </ds-text>\n <span id={`${this.pageSizeControlId}-label`} class=\"ds-visually-hidden\">\n {pageSizeAriaLabel}\n </span>\n <ds-select\n class=\"pagination__page-size-select\"\n size=\"md\"\n inputId={this.pageSizeControlId}\n ariaLabelledby={`${this.pageSizeControlId}-label`}\n options={options}\n value={state.pageSizeMode === 'fit' ? 'fit' : String(state.pageSize)}\n triggerLabel={state.pageSizeMode === 'fit' ? this.fitPageSizeTriggerLabel : undefined}\n indicator=\"up-down\"\n allowClear={false}\n activeFill={false}\n hasBorder={false}\n isInactive={this.loading}\n onDsChange={event => {\n event.stopPropagation();\n this.requestPageSize(event.detail);\n }}\n />\n <ds-text\n class=\"pagination__total\"\n as=\"span\"\n variant=\"text-body-medium\"\n color=\"secondary\"\n fontFeature=\"tabular-nums\"\n >\n {total}\n </ds-text>\n </div>\n <ds-divider\n class=\"pagination__divider\"\n orientation=\"vertical\"\n length=\"var(--dimension-size-250)\"\n />\n <div class=\"pagination__navigation\">\n <ds-button-unfilled\n class=\"pagination__boundary\"\n variant=\"icon\"\n size=\"md\"\n icon=\"ChevronLeftDouble\"\n ariaLabel=\"First page\"\n hasBorder={false}\n isInactive={this.loading || atStart}\n onDsClick={() => this.requestPage(0)}\n />\n <ds-button-unfilled\n variant=\"icon\"\n size=\"md\"\n icon=\"ChevronLeft\"\n ariaLabel=\"Previous page\"\n hasBorder={false}\n isInactive={this.loading || atStart}\n onDsClick={() => this.requestPage(state.pageIndex - 1)}\n />\n <ds-text\n class=\"pagination__page\"\n as=\"span\"\n variant=\"text-body-medium\"\n color=\"secondary\"\n fontFeature=\"tabular-nums\"\n >\n {page}\n </ds-text>\n <ds-button-unfilled\n variant=\"icon\"\n size=\"md\"\n icon=\"ChevronRight\"\n ariaLabel=\"Next page\"\n hasBorder={false}\n isInactive={this.loading || atEnd}\n onDsClick={() => this.requestPage(state.pageIndex + 1)}\n />\n <ds-button-unfilled\n class=\"pagination__boundary\"\n variant=\"icon\"\n size=\"md\"\n icon=\"ChevronRightDouble\"\n ariaLabel=\"Last page\"\n hasBorder={false}\n isInactive={this.loading || atEnd}\n onDsClick={() => this.requestPage(state.totalPages - 1)}\n />\n </div>\n <span class=\"ds-visually-hidden\" role=\"status\" aria-live=\"polite\" aria-atomic=\"true\">\n {announcedRange} {this.itemLabel}. {announcedPage}.\n </span>\n </nav>\n </Host>\n );\n }\n}\n",
412
413
  "type": "registry:ui"
413
414
  },
414
415
  {
@@ -53,7 +53,7 @@
53
53
  ],
54
54
  "accessibility": [
55
55
  "Provide localized accessible labels for the rail, shortcut group, and every rendered tool.",
56
- "Use the canonical default recipe when the product needs Search first and Help last; custom recipes must preserve a deliberate keyboard and visual order.",
56
+ "Use the canonical default recipe when the product needs Agents pinned first and Help pinned last; custom recipes must preserve a deliberate keyboard and visual order.",
57
57
  "Keep focus on the selected rail action when opening a drawer and allow normal Tab navigation into its content.",
58
58
  "When the shortcut rail overflows, its labeled body becomes a keyboard-focusable scroll region without changing the rail action order.",
59
59
  "Do not trigger global shortcuts while focus is in an editable control.",
@@ -63,7 +63,7 @@
63
63
  "states": [
64
64
  "Every tool has a stable explicit id and accessible label. The application filters definitions for authorization and entitlement.",
65
65
  "Tool order, header or footer rail placement, shortcut, and mobile grouping come from explicit item metadata rather than id-based layout assumptions.",
66
- "PANEL_TOOLS_DEFAULT_ITEMS is the canonical Lab recipe: Search pinned first, Messages before Agents, Help pinned last, current shortcuts, and only Stacks and Activity grouped under mobile Inbox.",
66
+ "PANEL_TOOLS_DEFAULT_ITEMS is the canonical Lab recipe: Agents pinned first; Messages, Activity, then Search in the body; Help pinned last; Activity directly reachable on mobile; and Stacks omitted.",
67
67
  "Only one drawer is open at a time; selecting another tool switches directly and selecting the active tool closes to rail-only mode.",
68
68
  "The drawer starts closed, remains open across ordinary route changes, and retains every mounted tool's independent conversation, query, scroll, and work state.",
69
69
  "When storageKey is set, persist only the last active tool in this browser and still restore the drawer closed after reload.",
@@ -473,12 +473,12 @@
473
473
  },
474
474
  {
475
475
  "path": "src/wc/components/PanelTools/panel-tools-types.ts",
476
- "content": "/** Application-owned tool id for the shell rail and responsive tool views. */\nexport type PanelToolsToolId = string;\n\n/** Canonical Motive shell recipe used when item metadata is omitted. */\nexport type CanonicalPanelToolsToolId =\n | 'search'\n | 'messages'\n | 'agents'\n | 'stacks'\n | 'activity'\n | 'help';\n\nexport const PANEL_TOOLS_TOOL_IDS: CanonicalPanelToolsToolId[] = [\n 'search',\n 'messages',\n 'agents',\n 'stacks',\n 'activity',\n 'help',\n];\n\n/** Rail header slot — mirrors panel-nav M logo row. */\nexport const PANEL_TOOLS_PRIMARY_TOOL_ID: CanonicalPanelToolsToolId = 'search';\n\n/** Rail footer slot — flush to the bottom of the tools column. */\nexport const PANEL_TOOLS_FOOTER_TOOL_ID: CanonicalPanelToolsToolId = 'help';\n\nexport const PANEL_TOOLS_LABELS: Record<CanonicalPanelToolsToolId, string> = {\n search: 'Search',\n messages: 'Messages',\n stacks: 'Stacks',\n activity: 'Activity',\n agents: 'Agents',\n help: 'Help & Support',\n};\n\nexport const PANEL_TOOLS_SHORTCUTS: Partial<Record<CanonicalPanelToolsToolId, string>> = {\n search: 'K',\n agents: 'A',\n messages: 'M',\n stacks: 'S',\n activity: 'N',\n help: '/',\n};\n\nexport interface PanelToolsItem {\n id: PanelToolsToolId;\n /** Icon name for <ds-icon>. */\n icon: string;\n /** Visible and accessible fallback label for custom tool ids. */\n label?: string;\n /** Explicit rail region; canonical Search/Help placement is the fallback. */\n railPlacement?: 'header' | 'body' | 'footer';\n /** Stable ordering within the selected rail region. */\n order?: number;\n /** Optional shortcut label displayed by the tooltip. */\n shortcutKey?: string;\n /** Mobile destination recipe; canonical ids retain their explicit defaults. */\n mobileDestination?: 'search' | 'inbox' | 'messages' | 'agents' | 'help';\n /** Whether this rail button is currently pressed/active. */\n selected?: boolean;\n /** Show a notification dot. */\n dot?: boolean;\n isInactive?: boolean;\n ariaLabel?: string;\n}\n\n/** Canonical shell recipe used by Lab and by managed ShellApp examples. */\nexport const PANEL_TOOLS_DEFAULT_ITEMS: PanelToolsItem[] = [\n {\n id: 'search',\n icon: 'MagnifyingGlass',\n label: PANEL_TOOLS_LABELS.search,\n railPlacement: 'header',\n order: 0,\n shortcutKey: PANEL_TOOLS_SHORTCUTS.search,\n mobileDestination: 'search',\n },\n {\n id: 'messages',\n icon: 'MessageBubbleStack',\n label: PANEL_TOOLS_LABELS.messages,\n order: 1,\n shortcutKey: PANEL_TOOLS_SHORTCUTS.messages,\n mobileDestination: 'messages',\n },\n {\n id: 'agents',\n icon: 'AI',\n label: PANEL_TOOLS_LABELS.agents,\n order: 2,\n shortcutKey: PANEL_TOOLS_SHORTCUTS.agents,\n mobileDestination: 'agents',\n },\n {\n id: 'stacks',\n icon: 'ViewMenu',\n label: PANEL_TOOLS_LABELS.stacks,\n order: 3,\n shortcutKey: PANEL_TOOLS_SHORTCUTS.stacks,\n mobileDestination: 'inbox',\n },\n {\n id: 'activity',\n icon: 'Bell',\n label: PANEL_TOOLS_LABELS.activity,\n order: 4,\n shortcutKey: PANEL_TOOLS_SHORTCUTS.activity,\n mobileDestination: 'inbox',\n },\n {\n id: 'help',\n icon: 'CircleQuestion',\n label: PANEL_TOOLS_LABELS.help,\n railPlacement: 'footer',\n order: 5,\n shortcutKey: PANEL_TOOLS_SHORTCUTS.help,\n mobileDestination: 'help',\n },\n];\n\nexport interface PanelToolsHeaderAction {\n id: string;\n icon: string;\n ariaLabel: string;\n triggerId?: string;\n controls?: string;\n expanded?: boolean;\n haspopup?: 'menu' | 'dialog' | 'listbox' | 'tree' | 'grid';\n pressed?: boolean;\n isInactive?: boolean;\n}\n\nexport interface PanelToolsHeaderConfig {\n title?: string;\n showBack?: boolean;\n backIcon?: string;\n backAriaLabel?: string;\n actions?: PanelToolsHeaderAction[];\n}\n\n/** Per-tool header state. The application replaces the object when a tool changes depth or actions. */\nexport type PanelToolsHeaders = Partial<Record<PanelToolsToolId, PanelToolsHeaderConfig>>;\n",
476
+ "content": "/** Application-owned tool id for the shell rail and responsive tool views. */\nexport type PanelToolsToolId = string;\n\n/** Canonical Motive shell recipe used when item metadata is omitted. */\nexport type CanonicalPanelToolsToolId =\n | 'search'\n | 'messages'\n | 'agents'\n | 'stacks'\n | 'activity'\n | 'help';\n\nexport const PANEL_TOOLS_TOOL_IDS: CanonicalPanelToolsToolId[] = [\n 'agents',\n 'messages',\n 'activity',\n 'search',\n 'stacks',\n 'help',\n];\n\n/** Rail header slot — mirrors panel-nav M logo row. */\nexport const PANEL_TOOLS_PRIMARY_TOOL_ID: CanonicalPanelToolsToolId = 'agents';\n\n/** Rail footer slot — flush to the bottom of the tools column. */\nexport const PANEL_TOOLS_FOOTER_TOOL_ID: CanonicalPanelToolsToolId = 'help';\n\nexport const PANEL_TOOLS_LABELS: Record<CanonicalPanelToolsToolId, string> = {\n search: 'Search',\n messages: 'Messages',\n stacks: 'Stacks',\n activity: 'Activity',\n agents: 'Agents',\n help: 'Help & Support',\n};\n\nexport const PANEL_TOOLS_SHORTCUTS: Partial<Record<CanonicalPanelToolsToolId, string>> = {\n search: 'K',\n agents: 'A',\n messages: 'M',\n stacks: 'S',\n activity: 'N',\n help: '/',\n};\n\nexport interface PanelToolsItem {\n id: PanelToolsToolId;\n /** Icon name for <ds-icon>. */\n icon: string;\n /** Visible and accessible fallback label for custom tool ids. */\n label?: string;\n /** Explicit rail region; canonical Agents/Help placement is the fallback. */\n railPlacement?: 'header' | 'body' | 'footer';\n /** Stable ordering within the selected rail region. */\n order?: number;\n /** Optional shortcut label displayed by the tooltip. */\n shortcutKey?: string;\n /** Mobile destination recipe; canonical ids retain their explicit defaults. */\n mobileDestination?: 'search' | 'inbox' | 'activity' | 'messages' | 'agents' | 'help';\n /** Whether this rail button is currently pressed/active. */\n selected?: boolean;\n /** Show a notification dot. */\n dot?: boolean;\n isInactive?: boolean;\n ariaLabel?: string;\n}\n\n/** Canonical shell recipe used by Lab and by managed ShellApp examples. */\nexport const PANEL_TOOLS_DEFAULT_ITEMS: PanelToolsItem[] = [\n {\n id: 'agents',\n icon: 'AI',\n label: PANEL_TOOLS_LABELS.agents,\n railPlacement: 'header',\n order: 0,\n shortcutKey: PANEL_TOOLS_SHORTCUTS.agents,\n mobileDestination: 'agents',\n },\n {\n id: 'messages',\n icon: 'MessageBubbleStack',\n label: PANEL_TOOLS_LABELS.messages,\n order: 0,\n shortcutKey: PANEL_TOOLS_SHORTCUTS.messages,\n mobileDestination: 'messages',\n },\n {\n id: 'activity',\n icon: 'Bell',\n label: PANEL_TOOLS_LABELS.activity,\n order: 1,\n shortcutKey: PANEL_TOOLS_SHORTCUTS.activity,\n mobileDestination: 'activity',\n },\n {\n id: 'search',\n icon: 'MagnifyingGlass',\n label: PANEL_TOOLS_LABELS.search,\n order: 2,\n shortcutKey: PANEL_TOOLS_SHORTCUTS.search,\n mobileDestination: 'search',\n },\n {\n id: 'help',\n icon: 'CircleQuestion',\n label: PANEL_TOOLS_LABELS.help,\n railPlacement: 'footer',\n order: 0,\n shortcutKey: PANEL_TOOLS_SHORTCUTS.help,\n mobileDestination: 'help',\n },\n];\n\nexport interface PanelToolsHeaderAction {\n id: string;\n icon: string;\n ariaLabel: string;\n triggerId?: string;\n controls?: string;\n expanded?: boolean;\n haspopup?: 'menu' | 'dialog' | 'listbox' | 'tree' | 'grid';\n pressed?: boolean;\n isInactive?: boolean;\n}\n\nexport interface PanelToolsHeaderConfig {\n title?: string;\n showBack?: boolean;\n backIcon?: string;\n backAriaLabel?: string;\n actions?: PanelToolsHeaderAction[];\n}\n\n/** Per-tool header state. The application replaces the object when a tool changes depth or actions. */\nexport type PanelToolsHeaders = Partial<Record<PanelToolsToolId, PanelToolsHeaderConfig>>;\n",
477
477
  "type": "registry:ui"
478
478
  },
479
479
  {
480
480
  "path": "src/wc/components/PanelTools/panel-tools-utils.ts",
481
- "content": "import { parseCssTimeMs } from '../../utils/resolve-css-time-ms';\nimport {\n PANEL_TOOLS_TOOL_IDS,\n type PanelToolsItem,\n type PanelToolsToolId,\n} from './panel-tools-types';\n\nexport function orderPanelToolsItems(items: PanelToolsItem[]): PanelToolsItem[] {\n const firstById = new Map<PanelToolsToolId, PanelToolsItem>();\n for (const item of items) {\n if (item.id.trim() && !firstById.has(item.id)) firstById.set(item.id, item);\n }\n const regionOrder = { header: 0, body: 1, footer: 2 } as const;\n return Array.from(firstById.values()).sort((left, right) => {\n const leftRegion = panelToolsRailPlacement(left);\n const rightRegion = panelToolsRailPlacement(right);\n const regionDelta = regionOrder[leftRegion] - regionOrder[rightRegion];\n if (regionDelta !== 0) return regionDelta;\n\n const leftCanonical = PANEL_TOOLS_TOOL_IDS.findIndex(id => id === left.id);\n const rightCanonical = PANEL_TOOLS_TOOL_IDS.findIndex(id => id === right.id);\n const leftOrder = left.order ?? (leftCanonical < 0 ? Number.MAX_SAFE_INTEGER : leftCanonical);\n const rightOrder =\n right.order ?? (rightCanonical < 0 ? Number.MAX_SAFE_INTEGER : rightCanonical);\n return leftOrder - rightOrder;\n });\n}\n\nexport function isPanelToolsToolId(value: string | null): value is PanelToolsToolId {\n return typeof value === 'string' && value.trim().length > 0;\n}\n\nexport function panelToolsRailPlacement(\n item: PanelToolsItem\n): 'header' | 'body' | 'footer' {\n if (item.railPlacement) return item.railPlacement;\n if (item.id === 'search') return 'header';\n if (item.id === 'help') return 'footer';\n return 'body';\n}\n\nexport function reconcilePanelToolsAvailability(\n items: PanelToolsItem[],\n open: boolean,\n activeTool: PanelToolsToolId | ''\n): { open: boolean; activeTool: PanelToolsToolId | ''; removedTool: PanelToolsToolId | '' } {\n if (!activeTool || items.some(item => item.id === activeTool)) {\n return { open, activeTool, removedTool: '' };\n }\n return { open: false, activeTool: '', removedTool: activeTool };\n}\n\nexport function shouldResyncPanelToolsItems(\n prev: PanelToolsItem[],\n next: PanelToolsItem[]\n): boolean {\n if (prev.length !== next.length) return true;\n return next.some((item, index) => {\n const prior = prev[index];\n if (!prior) return true;\n return (\n prior.id !== item.id ||\n prior.icon !== item.icon ||\n prior.label !== item.label ||\n prior.railPlacement !== item.railPlacement ||\n prior.order !== item.order ||\n prior.shortcutKey !== item.shortcutKey ||\n prior.mobileDestination !== item.mobileDestination ||\n prior.selected !== item.selected ||\n prior.dot !== item.dot ||\n prior.isInactive !== item.isInactive ||\n prior.ariaLabel !== item.ariaLabel\n );\n });\n}\n\nexport type PanelToolsMotion = 'opening' | 'closing' | 'idle';\n\nexport interface PanelToolsTransitionStyle {\n transitionProperty: string;\n transitionDuration: string;\n transitionDelay: string;\n}\n\n/** Maximum computed time for the drawer's max-width transition, including delay. */\nexport function panelToolsDrawerTransitionMs(style: PanelToolsTransitionStyle): number {\n const properties = style.transitionProperty.split(',').map(item => item.trim());\n const durations = style.transitionDuration.split(',').map(item => parseCssTimeMs(item.trim(), 0));\n const delays = style.transitionDelay.split(',').map(item => parseCssTimeMs(item.trim(), 0));\n\n return properties.reduce((max, property, index) => {\n if (property !== 'all' && property !== 'max-width') return max;\n const duration = durations[index % durations.length] ?? 0;\n const delay = delays[index % delays.length] ?? 0;\n return Math.max(max, duration + delay, 0);\n }, 0);\n}\n\n/** True when the drawer clip is fully closed — safe to skip painting slot content. */\nexport function panelToolsDrawerResting(open: boolean, motion: PanelToolsMotion): boolean {\n return !open && motion === 'idle';\n}\n\n/** True only when the animated clip has reached the terminal width for its motion phase. */\nexport function panelToolsDrawerAtTerminal(\n width: number,\n targetWidth: number,\n motion: PanelToolsMotion\n): boolean {\n if (motion === 'closing') return width <= 0.5;\n if (motion === 'opening') return targetWidth > 0 && width >= targetWidth - 0.5;\n return true;\n}\n\n/** Toggle or switch rail tool selection — repeat activation closes the active tool. */\nexport function resolvePanelToolActivation(\n open: boolean,\n activeTool: PanelToolsToolId | '',\n id: PanelToolsToolId\n): { open: boolean; activeTool: PanelToolsToolId; selected: boolean } {\n if (open && activeTool === id) {\n return { open: false, activeTool: id, selected: false };\n }\n return { open: true, activeTool: id, selected: true };\n}\n",
481
+ "content": "import { parseCssTimeMs } from '../../utils/resolve-css-time-ms';\nimport {\n PANEL_TOOLS_PRIMARY_TOOL_ID,\n PANEL_TOOLS_TOOL_IDS,\n type PanelToolsItem,\n type PanelToolsToolId,\n} from './panel-tools-types';\n\nexport function orderPanelToolsItems(items: PanelToolsItem[]): PanelToolsItem[] {\n const firstById = new Map<PanelToolsToolId, PanelToolsItem>();\n for (const item of items) {\n if (item.id.trim() && !firstById.has(item.id)) firstById.set(item.id, item);\n }\n const regionOrder = { header: 0, body: 1, footer: 2 } as const;\n return Array.from(firstById.values()).sort((left, right) => {\n const leftRegion = panelToolsRailPlacement(left);\n const rightRegion = panelToolsRailPlacement(right);\n const regionDelta = regionOrder[leftRegion] - regionOrder[rightRegion];\n if (regionDelta !== 0) return regionDelta;\n\n const leftCanonical = PANEL_TOOLS_TOOL_IDS.findIndex(id => id === left.id);\n const rightCanonical = PANEL_TOOLS_TOOL_IDS.findIndex(id => id === right.id);\n const leftOrder = left.order ?? (leftCanonical < 0 ? Number.MAX_SAFE_INTEGER : leftCanonical);\n const rightOrder =\n right.order ?? (rightCanonical < 0 ? Number.MAX_SAFE_INTEGER : rightCanonical);\n return leftOrder - rightOrder;\n });\n}\n\nexport function isPanelToolsToolId(value: string | null): value is PanelToolsToolId {\n return typeof value === 'string' && value.trim().length > 0;\n}\n\nexport function panelToolsRailPlacement(\n item: PanelToolsItem\n): 'header' | 'body' | 'footer' {\n if (item.railPlacement) return item.railPlacement;\n if (item.id === PANEL_TOOLS_PRIMARY_TOOL_ID) return 'header';\n if (item.id === 'help') return 'footer';\n return 'body';\n}\n\nexport function reconcilePanelToolsAvailability(\n items: PanelToolsItem[],\n open: boolean,\n activeTool: PanelToolsToolId | ''\n): { open: boolean; activeTool: PanelToolsToolId | ''; removedTool: PanelToolsToolId | '' } {\n if (!activeTool || items.some(item => item.id === activeTool)) {\n return { open, activeTool, removedTool: '' };\n }\n return { open: false, activeTool: '', removedTool: activeTool };\n}\n\nexport function shouldResyncPanelToolsItems(\n prev: PanelToolsItem[],\n next: PanelToolsItem[]\n): boolean {\n if (prev.length !== next.length) return true;\n return next.some((item, index) => {\n const prior = prev[index];\n if (!prior) return true;\n return (\n prior.id !== item.id ||\n prior.icon !== item.icon ||\n prior.label !== item.label ||\n prior.railPlacement !== item.railPlacement ||\n prior.order !== item.order ||\n prior.shortcutKey !== item.shortcutKey ||\n prior.mobileDestination !== item.mobileDestination ||\n prior.selected !== item.selected ||\n prior.dot !== item.dot ||\n prior.isInactive !== item.isInactive ||\n prior.ariaLabel !== item.ariaLabel\n );\n });\n}\n\nexport type PanelToolsMotion = 'opening' | 'closing' | 'idle';\n\nexport interface PanelToolsTransitionStyle {\n transitionProperty: string;\n transitionDuration: string;\n transitionDelay: string;\n}\n\n/** Maximum computed time for the drawer's max-width transition, including delay. */\nexport function panelToolsDrawerTransitionMs(style: PanelToolsTransitionStyle): number {\n const properties = style.transitionProperty.split(',').map(item => item.trim());\n const durations = style.transitionDuration.split(',').map(item => parseCssTimeMs(item.trim(), 0));\n const delays = style.transitionDelay.split(',').map(item => parseCssTimeMs(item.trim(), 0));\n\n return properties.reduce((max, property, index) => {\n if (property !== 'all' && property !== 'max-width') return max;\n const duration = durations[index % durations.length] ?? 0;\n const delay = delays[index % delays.length] ?? 0;\n return Math.max(max, duration + delay, 0);\n }, 0);\n}\n\n/** True when the drawer clip is fully closed — safe to skip painting slot content. */\nexport function panelToolsDrawerResting(open: boolean, motion: PanelToolsMotion): boolean {\n return !open && motion === 'idle';\n}\n\n/** True only when the animated clip has reached the terminal width for its motion phase. */\nexport function panelToolsDrawerAtTerminal(\n width: number,\n targetWidth: number,\n motion: PanelToolsMotion\n): boolean {\n if (motion === 'closing') return width <= 0.5;\n if (motion === 'opening') return targetWidth > 0 && width >= targetWidth - 0.5;\n return true;\n}\n\n/** Toggle or switch rail tool selection — repeat activation closes the active tool. */\nexport function resolvePanelToolActivation(\n open: boolean,\n activeTool: PanelToolsToolId | '',\n id: PanelToolsToolId\n): { open: boolean; activeTool: PanelToolsToolId; selected: boolean } {\n if (open && activeTool === id) {\n return { open: false, activeTool: id, selected: false };\n }\n return { open: true, activeTool: id, selected: true };\n}\n",
482
482
  "type": "registry:ui"
483
483
  }
484
484
  ]
@@ -1838,6 +1838,7 @@
1838
1838
  "Loading prevents duplicate activation without applying inactive opacity or removing the current keyboard focus.",
1839
1839
  "Icon and icon-label loading replace the icon in place. Label-only loading centers the loader while preserving the button's measured width.",
1840
1840
  "Physical press scaling is on by default. Disable it only when an owning composite must keep child or background geometry fixed; hover and pressed interaction washes remain available.",
1841
+ "Visible labels are emphasized by default. Set labelEmphasis to false when a composed surface deliberately needs the button label to match adjacent regular-weight control labels; this does not change the action hierarchy, foreground, icon, or interaction states.",
1841
1842
  "ButtonFilled has no active, pressed, or selected state; a filled action is a command, not a toggle or a persistent selection.",
1842
1843
  "An open popup is a transient pressed state instead. While expanded is true the pressed wash is held for the popup's complete rendered lifecycle and survives hover, so the trigger reads as held open without introducing a selected treatment.",
1843
1844
  "hasMenu supports only the label and icon-label variants. The chevron is what communicates that a menu will open, so an icon-only filled menu button would leave the affordance unstated."
@@ -1924,6 +1925,10 @@
1924
1925
  "type": "string",
1925
1926
  "required": false
1926
1927
  },
1928
+ "labelEmphasis": {
1929
+ "type": "boolean",
1930
+ "required": false
1931
+ },
1927
1932
  "pressScale": {
1928
1933
  "type": "boolean",
1929
1934
  "required": false
@@ -2049,6 +2054,7 @@
2049
2054
  "Loading prevents duplicate activation without applying inactive opacity or removing the current keyboard focus.",
2050
2055
  "Rounded changes only the control radius to the half-radius treatment; it does not change hierarchy, size, variant, or interaction semantics.",
2051
2056
  "Physical press scaling is on by default. Disable it only when an owning composite must keep child or background geometry fixed; hover and pressed interaction washes remain available.",
2057
+ "Visible labels are emphasized by default. Set labelEmphasis to false when a composed surface deliberately needs the button label to match adjacent regular-weight control labels; this does not change active state, foreground, icon, chevron, or interaction fills.",
2052
2058
  "Icon and icon-label loading replace the icon in place. Label-only loading centers the loader while preserving the button's measured width.",
2053
2059
  "hasMenu covers two distinct menu-button shapes and the variant selects between them. With label or icon-label the action has a menu, and a trailing chevron carries the affordance. With icon the button is a menu and no chevron is added.",
2054
2060
  "Because the icon-only shape has no chevron, its glyph must convey the menu on its own. Use Ellipses for a generic more-options or overflow control. Use a specific icon when the menu has a named purpose, such as Table for Customize table.",
@@ -2056,7 +2062,7 @@
2056
2062
  ],
2057
2063
  "responsiveBehavior": [
2058
2064
  "The consuming layout decides when an icon-label action becomes icon-only and must provide an explicit accessible name after changing variants. Table caption Customize is one such layout: below 900px it uses the icon shape while keeping the same neutral resting foreground regardless of the controlled column configuration.",
2059
- "When collapseLabel is set inside a named ds-table container narrower than 900px, an icon-label caption action omits its label and chevron and becomes icon-only while keeping its accessible name. An active control then promotes the icon to primary.",
2065
+ "When collapseLabel is set inside a named ds-table container narrower than 900px, an icon-label caption action omits its label and chevron and becomes icon-only while keeping its accessible name and exposing its label through a hover and keyboard-focus tooltip. An active control then promotes the icon to primary.",
2060
2066
  "Use fill width only when the parent layout requires a full-width secondary action."
2061
2067
  ],
2062
2068
  "frameworkCaveats": {
@@ -2151,6 +2157,10 @@
2151
2157
  "type": "string",
2152
2158
  "required": false
2153
2159
  },
2160
+ "labelEmphasis": {
2161
+ "type": "boolean",
2162
+ "required": false
2163
+ },
2154
2164
  "pressScale": {
2155
2165
  "type": "boolean",
2156
2166
  "required": false
@@ -2207,7 +2217,8 @@
2207
2217
  "badge",
2208
2218
  "icon",
2209
2219
  "loader",
2210
- "text"
2220
+ "text",
2221
+ "tooltip"
2211
2222
  ]
2212
2223
  },
2213
2224
  {
@@ -3946,7 +3957,7 @@
3946
3957
  ],
3947
3958
  "responsiveBehavior": [
3948
3959
  "The popup defaults to the large menu-width token and clamps to the viewport.",
3949
- "When collapseLabel is set inside a named ds-table container narrower than 900px, the trigger omits its label and chevron and becomes icon-only. Active criteria promote the prefix icon to primary.",
3960
+ "When collapseLabel is set inside a named ds-table container narrower than 900px, the trigger omits its label and chevron, becomes icon-only, and exposes its label through a hover and keyboard-focus tooltip. Active criteria promote the prefix icon to primary.",
3950
3961
  "Both panes remain visible at narrow widths, with a smaller category minimum and reduced option-pane padding.",
3951
3962
  "Treat side as preferred placement; the popup flips and clamps when viewport collision requires it."
3952
3963
  ],
@@ -4296,7 +4307,7 @@
4296
4307
  "states": [
4297
4308
  "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.",
4298
4309
  "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.",
4299
- "Placeholder text is secondary at rest and quaternary while the editable field is focused because it becomes decorative once typing begins.",
4310
+ "Placeholder text is secondary at rest and quaternary while the editable field is focused because it becomes decorative once typing begins. Single-line placeholder and entered value text truncate with an ellipsis when the field is narrower than its content.",
4300
4311
  "Entered values use the parent surface's primary foreground; prefix icons, suffix content, and resting placeholder text use its secondary foreground.",
4301
4312
  "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.",
4302
4313
  "The suffix slot is for compact units or decorations. The optional leading icon is decorative and does not replace the field label.",
@@ -4745,6 +4756,7 @@
4745
4756
  "responsiveBehavior": [
4746
4757
  "Keep the default choice-cell anchor alignment so the first or last interactive row edge aligns with its trigger, matching Select; use popup-frame only for deliberate custom frame geometry.",
4747
4758
  "Treat side as the preferred placement. When the popup cannot fit there and the opposite side offers more room, Menu flips on that main axis before clamping the final position to the viewport.",
4759
+ "Cap the popup to the vertical room available on its resolved side. When content is taller, the choice list scrolls internally and keyboard focus scrolls the active row into view.",
4748
4760
  "Avoid fixed widths that exceed narrow viewports."
4749
4761
  ],
4750
4762
  "frameworkCaveats": {
@@ -5373,7 +5385,7 @@
5373
5385
  {
5374
5386
  "name": "mobile-bar-nav",
5375
5387
  "title": "MobileBarNav",
5376
- "description": "Persistent mobile shell navigation for Menu, the router-derived current area, Search, Inbox, Messages, and Agents.",
5388
+ "description": "Persistent mobile shell navigation for Menu, the router-derived current area, Search, Activity, Messages, and Agents, with optional Inbox grouping.",
5377
5389
  "type": "registry:ui",
5378
5390
  "meta": {
5379
5391
  "library": "@ds-mo/ui",
@@ -5384,7 +5396,7 @@
5384
5396
  "intent": {
5385
5397
  "audience": "specialized",
5386
5398
  "status": "experimental",
5387
- "summary": "Persistent mobile shell navigation for Menu, the router-derived current area, Search, Inbox, Messages, and Agents.",
5399
+ "summary": "Persistent mobile shell navigation for Menu, the router-derived current area, Search, Activity, Messages, and Agents, with optional Inbox grouping.",
5388
5400
  "useWhen": [
5389
5401
  "ShellApp is rendering its dedicated below-768px application composition."
5390
5402
  ],
@@ -5403,7 +5415,8 @@
5403
5415
  "Only the active destination exposes aria-current; Menu exposes its controlled pane through aria-expanded."
5404
5416
  ],
5405
5417
  "states": [
5406
- "The fixed groups are Menu plus current area, followed by Search, Inbox, Messages, and Agents.",
5418
+ "The fixed groups are Menu plus current area, followed by Search, Activity, Messages, and Agents.",
5419
+ "Consumers that intentionally group multiple review tools can replace the Activity destination with Inbox.",
5407
5420
  "Selection promotes foreground to primary without painting a selected fill; unselected destinations use tertiary foreground.",
5408
5421
  "Opening Mobile Sheet Nav replaces the Menu glyph with Cross while preserving its accessible name.",
5409
5422
  "Reselecting an active destination does not reset its nested state.",
@@ -5426,6 +5439,18 @@
5426
5439
  "type": "MobileDestination",
5427
5440
  "required": false
5428
5441
  },
5442
+ "activityDot": {
5443
+ "type": "boolean",
5444
+ "required": false
5445
+ },
5446
+ "activityLabel": {
5447
+ "type": "string",
5448
+ "required": false
5449
+ },
5450
+ "activityMode": {
5451
+ "type": "'direct' | 'inbox'",
5452
+ "required": false
5453
+ },
5429
5454
  "agentsDot": {
5430
5455
  "type": "boolean",
5431
5456
  "required": false
@@ -6049,7 +6074,7 @@
6049
6074
  ],
6050
6075
  "accessibility": [
6051
6076
  "The component renders a named navigation region, gives its compact page-size noun a complete accessible label, and gives every icon-only boundary control an explicit page action name.",
6052
- "The visible page-size cluster states the localized noun, selected size, and total, for example Rows: 25 of 500. The page position uses compact ‘of’ phrasing, for example 1 of 20. A polite atomic status update retains the complete current range and ‘Page’ phrasing with the localized item noun.",
6077
+ "The visible page-size cluster states the localized noun, selected size, and current-page count of total, for example Rows: [25] 25 of 500. The page position uses compact ‘of’ phrasing, for example 1 of 20. A polite atomic status update retains the complete current range and ‘Page’ phrasing with the localized item noun.",
6053
6078
  "Left and Right Arrow request the adjacent page while focus is within pagination navigation controls. The page-size combobox retains its own arrow-key behavior.",
6054
6079
  "Inactive boundary controls and every control during loading remain non-interactive while retaining their accessible names."
6055
6080
  ],
@@ -6061,7 +6086,8 @@
6061
6086
  "fitToPage appends a Fit to page choice. fitPageSize supplies its effective whole-item capacity; the closed trigger says Fit while the range continues to expose the resulting count. fitToPageInactive keeps that choice visible but unavailable when fitting is not meaningful for the current data shape. Pagination does not measure layouts itself."
6062
6087
  ],
6063
6088
  "responsiveBehavior": [
6064
- "The compact controls remain on one line with noun, selected size, total, a vertical divider, and page position. When inline space is still insufficient, the pagination surface scrolls horizontally rather than wrapping labels away from their controls."
6089
+ "The full-width controls remain on one line with noun, selected size, current-page count of total, a vertical divider, page position, and first, previous, next, and last navigation.",
6090
+ "Inside a ds-table container narrower than 900px, pagination hides the visible current-page count and first/last boundary controls while retaining the page-size noun and selector plus previous, page position, and next. The complete current range remains available to assistive technology."
6065
6091
  ],
6066
6092
  "frameworkCaveats": {
6067
6093
  "customElements": [
@@ -6859,7 +6885,7 @@
6859
6885
  ],
6860
6886
  "accessibility": [
6861
6887
  "Provide localized accessible labels for the rail, shortcut group, and every rendered tool.",
6862
- "Use the canonical default recipe when the product needs Search first and Help last; custom recipes must preserve a deliberate keyboard and visual order.",
6888
+ "Use the canonical default recipe when the product needs Agents pinned first and Help pinned last; custom recipes must preserve a deliberate keyboard and visual order.",
6863
6889
  "Keep focus on the selected rail action when opening a drawer and allow normal Tab navigation into its content.",
6864
6890
  "When the shortcut rail overflows, its labeled body becomes a keyboard-focusable scroll region without changing the rail action order.",
6865
6891
  "Do not trigger global shortcuts while focus is in an editable control.",
@@ -6869,7 +6895,7 @@
6869
6895
  "states": [
6870
6896
  "Every tool has a stable explicit id and accessible label. The application filters definitions for authorization and entitlement.",
6871
6897
  "Tool order, header or footer rail placement, shortcut, and mobile grouping come from explicit item metadata rather than id-based layout assumptions.",
6872
- "PANEL_TOOLS_DEFAULT_ITEMS is the canonical Lab recipe: Search pinned first, Messages before Agents, Help pinned last, current shortcuts, and only Stacks and Activity grouped under mobile Inbox.",
6898
+ "PANEL_TOOLS_DEFAULT_ITEMS is the canonical Lab recipe: Agents pinned first; Messages, Activity, then Search in the body; Help pinned last; Activity directly reachable on mobile; and Stacks omitted.",
6873
6899
  "Only one drawer is open at a time; selecting another tool switches directly and selecting the active tool closes to rail-only mode.",
6874
6900
  "The drawer starts closed, remains open across ordinary route changes, and retains every mounted tool's independent conversation, query, scroll, and work state.",
6875
6901
  "When storageKey is set, persist only the last active tool in this browser and still restore the drawer closed after reload.",
@@ -7422,7 +7448,7 @@
7422
7448
  ],
7423
7449
  "responsiveBehavior": [
7424
7450
  "Use width fill for form fields and width hug for compact filter bars. Override those defaults only when the owning layout has a clear constraint.",
7425
- "When collapseLabel is set inside a named ds-table container narrower than 900px, the trigger omits its label and chevron and becomes icon-only. A selected value promotes the prefix icon to primary.",
7451
+ "When collapseLabel is set inside a named ds-table container narrower than 900px, the trigger omits its label and chevron, becomes icon-only, and exposes its label through a hover and keyboard-focus tooltip. A selected value promotes the prefix icon to primary.",
7426
7452
  "The popup matches the trigger minimum width, may grow for content, and clamps to the viewport.",
7427
7453
  "Keep popupAlign at start for ordinary fields and filters. Use end when a control sits against a right layout edge and the popup should grow inward.",
7428
7454
  "Long option collections scroll inside the popup; virtualization and remote search are not part of this component."
@@ -7704,14 +7730,14 @@
7704
7730
  "Keep the shell-owned routed-content scroller keyboard-focusable while its page landmark retains content semantics. Do not paint a control focus ring around that scroller; it is a layout region, and a page-column outline looks like a selected card.",
7705
7731
  "Keep the managed desktop/tablet and mobile presentations available without conditionally recreating routed or tool content.",
7706
7732
  "Do not override browser keyboard chords when enabling shell shortcuts.",
7707
- "Require Search and Help as the minimum PanelTools actions and provide accessible labels for every tool.",
7733
+ "Require Agents and Help as the minimum pinned PanelTools actions and provide accessible labels for every tool.",
7708
7734
  "Preserve focus and accessible names across navigation, collapse, overflow, and responsive presentation changes."
7709
7735
  ],
7710
7736
  "states": [
7711
7737
  "Render exactly one ShellApp as the workspace root and never nest it.",
7712
7738
  "Managed composition is the standard. Supply navigation, pageChrome, and tools objects plus named tool-view and default page-content slots.",
7713
7739
  "The application router owns the current route, authorization filtering, Dashboard or Settings context, destination collections, section collections, and localized content labels.",
7714
- "ShellApp renders the lower-level desktop, tablet, and mobile chrome, coordinates layout, gradient alignment, transitions, shortcuts, global surfaces, Inbox state, focus restoration, and mobile-stage visibility.",
7740
+ "ShellApp renders the lower-level desktop, tablet, and mobile chrome, coordinates layout, gradient alignment, transitions, shortcuts, global surfaces, optional Inbox state, focus restoration, and mobile-stage visibility.",
7715
7741
  "The application router owns authorization, current URLs, history, and route execution; ShellApp forwards navigation and page intent events without routing.",
7716
7742
  "The application supplies each routed content owner and tool view once. ShellApp changes visibility, inert state, and presentation without recreating those nodes.",
7717
7743
  "The optional banner slot accepts one application-owned ds-banner above the complete shell row. The application owns its open state, orientation for the current shell capacity, action consequences, dismissal persistence, and resolution when several messages compete.",
@@ -8113,17 +8139,17 @@
8113
8139
  "accessibility": [
8114
8140
  "Only the active mobile tool view participates in focus or the accessibility tree.",
8115
8141
  "Header Back and application-owned header actions preserve the PanelTools event contract.",
8116
- "Mobile Inbox uses a named label-only TabGroup for Stacks and Activity; Messages remains a direct mobile destination."
8142
+ "A direct Activity destination renders its own named tool view; an explicitly grouped Inbox uses a named label-only TabGroup."
8117
8143
  ],
8118
8144
  "states": [
8119
8145
  "The application owns open and activeTool and replaces headers when tool depth changes.",
8120
- "Items with mobileDestination inbox map to one mobile Inbox destination while retaining the actual active tool id; the canonical recipe groups Stacks and Activity, while Messages remains a separate destination.",
8146
+ "Items with mobileDestination inbox map to one mobile Inbox destination while retaining the actual active tool id; the canonical recipe exposes Activity directly and keeps Inbox grouping opt-in.",
8121
8147
  "Stable tool ids own named slots; label, order, rail placement, shortcut, and mobile destination are explicit item data.",
8122
8148
  "Switching responsive mode must not recreate application-owned slotted product elements."
8123
8149
  ],
8124
8150
  "responsiveBehavior": [
8125
8151
  "Desktop and tablet compose PanelTools-compatible rail and drawer chrome.",
8126
- "Mobile renders one centered MobileHeader for every tool; Stacks and Activity use the large segmented Inbox control, while Messages remains a direct tool destination."
8152
+ "Mobile renders one centered MobileHeader for every direct tool; explicitly grouped Inbox tools use the large segmented section control."
8127
8153
  ],
8128
8154
  "frameworkCaveats": {
8129
8155
  "customElements": [
@@ -9079,7 +9105,7 @@
9079
9105
  "pattern:menu-trigger"
9080
9106
  ],
9081
9107
  "accessibility": [
9082
- "The rendered structure uses a native table, caption, column headers, row groups, and data cells; keep the caption non-empty. The native caption remains visually hidden as the table's accessible name. When caption visibility is enabled, a 48px header bar renders as component chrome outside the horizontal scrollport. It defaults to a presentational caption title that does not duplicate the accessible name. Application-owned labeled controls supplied through the single named header slot replace that fallback and occupy the leading content width inside the component-owned 8px inset. When columnCustomizer is enabled, the table adds a trailing Customize control named Customize table. Its label and resting foreground remain neutral when hidden or reordered columns differ from the catalog default. The control is icon-label at typical widths and the icon-only Table menu button below 900px. Otherwise the header slot receives the complete content width.",
9108
+ "The rendered structure uses a native table, caption, column headers, row groups, and data cells; keep the caption non-empty. The native caption remains visually hidden as the table's accessible name. When caption visibility is enabled, a 48px header bar renders as component chrome outside the data scrollport. It defaults to a presentational caption title that does not duplicate the accessible name. Application-owned labeled controls supplied through the single named header slot replace that fallback and share one horizontal caption overflow row with table-owned actions inside the component-owned 8px inset. Table caption buttons keep pressed interaction paint without physically scaling. When columnCustomizer is enabled, the table adds a trailing Customize control named Customize table. Its label uses regular weight and its resting foreground remains neutral when hidden or reordered columns differ from the catalog default. The control is icon-label at typical widths and the icon-only Table menu button below 900px, where a tooltip preserves the Customize label. The always-icon-only table-variant control also exposes its label through a tooltip. Otherwise the header slot receives the complete content width.",
9083
9109
  "The data-mode switcher is the shared Menu of the table's supported infinite, pagination, and virtual modes. It uses an Ellipses trigger. The switcher is the final caption-bar action; when the column customizer is also present, a decorative vertical divider separates the two controls. dataMode remains controlled, and the application responds to change intent by supplying the matching records, pagination state, loading behavior, and query consequences. Escape, dismissal, and selection return focus to the trigger.",
9084
9110
  "The column customizer is the shared Menu of switch rows, not a table-owned dialog. Data columns can be shown, hidden, and reordered. Action columns and the selection checkbox never appear in the menu. The last remaining visible data column cannot be hidden, but it stays focusable so it can still be reordered. Escape and outside click restore focus to the trigger. Persistence stays in the application.",
9085
9111
  "Sorting controls remain buttons inside column headers and expose the table's one controlled member-row sort through aria-sort. ds-table-sort in the toolbar trailing cluster is the matching menu of Data and Direction sections; it emits the same dsSortChange shape so header clicks and menu choices stay in sync. Grouping never adds a second sort indicator or header control; the application supplies groups in its fixed product order. Optional column help underlines the header label with the shared dotted decoration and shows ds-tooltip from that label; it does not add a second header control or replace the column name.",
@@ -9105,7 +9131,7 @@
9105
9131
  ],
9106
9132
  "responsiveBehavior": [
9107
9133
  "The table keeps native semantics at narrow widths and scrolls horizontally instead of transforming into cards.",
9108
- "Inside the named ds-table container, caption Filter, Group, and Sort controls that opt into collapseLabel become icon-only below 900px by omitting their labels and chevrons. Active filtering or grouping promotes the visible icon to primary. Sort keeps resting secondary chrome because its selection lives in the menu and headers. Customize switches to the icon-only Table menu button below 900px and keeps its neutral resting foreground regardless of the controlled column configuration.",
9134
+ "Inside the named ds-table container, caption Search retains its standard maximum width and flexes smaller only when the complete row needs space. Filter, Group, and Sort controls that opt into collapseLabel become icon-only below 900px by omitting their labels and chevrons. Active filtering or grouping promotes the visible icon to primary. Sort keeps resting secondary chrome because its selection lives in the menu and headers. Customize switches to the icon-only Table menu button below 900px and keeps its neutral resting foreground regardless of the controlled column configuration. The caption, frame, and footer remain within the table host at every width. The complete caption row scrolls together only when those compact controls no longer fit, while data columns scroll inside the viewport; table-owned trailing actions never stay pinned over the scrolling toolbar.",
9109
9135
  "stickyHeader follows the active vertical scroll owner: a maxHeight, height, or fitViewport table keeps its native header and group sections inside one bounded table scrollport, while a document-flow table retains a semantic native header and renders a sticky control header whose columns follow the body's native inline scroll timeline. Consumers set --ds-table-sticky-header-offset when page chrome occupies an unbounded document scrollport edge. When the 48px table header bar is visible, it sticks at that external offset and the column header stacks immediately below it.",
9110
9136
  "fitViewport uses viewportInsetBlockStart and viewportInsetBlockEnd to describe compact surrounding chrome and page gutters relative to the nearest vertical scroll owner. Before the table reaches that compact position, its host reserves the eventual height while the visible composition grows and vertical input remains owned by the outer page. Once settled, the internal table viewport becomes the vertical scroll owner. Bounded grouped tables use browser-native sticky section rows inside real block row-group boundaries, so scroll and resize do not run component JavaScript or trigger reactive section swaps. Document-flow tables retain the synchronized visual lane required by their separate horizontal scrollport.",
9111
9137
  "Without maxHeight the table grows the document and automatic lazy loading observes the browser viewport. Horizontal scrolling chains vertical wheel and trackpad movement to the page instead of trapping it.",
@@ -9494,7 +9520,7 @@
9494
9520
  ],
9495
9521
  "accessibility": [
9496
9522
  "The trigger is an icon-label combobox named Filter table unless a product aria-label supplies the table context. Compact caption widths preserve that accessible name after the visible label and chevron are omitted.",
9497
- "The popup retains ds-filter-menu category navigation, option semantics, focus entry, dismissal, and focus return.",
9523
+ "The popup retains ds-filter-menu category navigation, option semantics, focus entry, dismissal, and focus return, and aligns its start edge with the trigger.",
9498
9524
  "Localize label, menuLabel, categoriesLabel, and clearLabel together so visible and accessible wording remains consistent."
9499
9525
  ],
9500
9526
  "states": [
@@ -9504,7 +9530,7 @@
9504
9530
  ],
9505
9531
  "responsiveBehavior": [
9506
9532
  "Place the control in the trailing region of ds-table-toolbar.",
9507
- "Inside a named ds-table container narrower than 900px, the trigger becomes icon-only and active criteria promote the Filters icon to primary."
9533
+ "Inside a named ds-table container narrower than 900px, the trigger becomes icon-only, exposes its visible label through a hover and keyboard-focus tooltip, and active criteria promote the Filters icon to primary."
9508
9534
  ],
9509
9535
  "frameworkCaveats": {
9510
9536
  "customElements": [
@@ -9803,18 +9829,19 @@
9803
9829
  ],
9804
9830
  "accessibility": [
9805
9831
  "The trigger is an icon-label menu button named Sort table unless a product aria-label is supplied. Compact caption widths keep that accessible name after the visible label is omitted.",
9806
- "The menu has two sections, Data and Direction. Each section presents one selected row through isSelected; the menu is not a single radio group because field and direction are independent.",
9832
+ "The start-aligned menu has two sections, Data and Direction. Each section presents one selected row through isSelected; the menu is not a single radio group because field and direction are independent.",
9807
9833
  "Choosing a row applies immediately and keeps the menu open so the other section can be changed. Escape and outside activation close the menu and restore trigger focus."
9808
9834
  ],
9809
9835
  "states": [
9810
9836
  "columns and sort are controlled. dsSortChange reports the next TableSortState and never mutates those inputs. The same sort object is what column headers emit through the table.",
9811
9837
  "Selecting a different data field keeps the current direction, or starts ascending when no sort exists. Selecting a direction applies it to the current field, or to the first sortable field when no sort exists. Selecting the already-active field or direction is a no-op and does not toggle the way a header click does.",
9812
- "The trigger stays Sort in resting secondary chrome. Field and direction selection live in the menu and on column headers, so the trigger never uses active primary emphasis.",
9838
+ "The trigger stays Sort in regular-weight resting secondary chrome. Field and direction selection live in the menu and on column headers, so the trigger never uses active primary emphasis. It keeps pressed interaction paint without physically scaling inside the stable table caption row.",
9813
9839
  "Compound sortable headers contribute one Data row per segment. Action columns never appear."
9814
9840
  ],
9815
9841
  "responsiveBehavior": [
9816
9842
  "Place the control in the trailing region of ds-table-toolbar with Filter and Group.",
9817
- "Inside a named ds-table container narrower than 900px, the trigger omits its label and chevron and becomes icon-only. The icon keeps the resting secondary foreground."
9843
+ "Inside a named ds-table container narrower than 900px, the trigger omits its label and chevron and becomes icon-only. A hover and keyboard-focus tooltip preserves the label, and the icon keeps the resting secondary foreground.",
9844
+ "Long sortable-field catalogs inherit Menu's collision-aware height cap and internal choice scrolling instead of extending beyond the available viewport space."
9818
9845
  ],
9819
9846
  "frameworkCaveats": {
9820
9847
  "customElements": [
@@ -9878,7 +9905,7 @@
9878
9905
  {
9879
9906
  "name": "table-toolbar",
9880
9907
  "title": "TableToolbar",
9881
- "description": "Data-agnostic companion layout for application-owned table caption controls, with a start cluster, optional search, trailing Filter/Group/Sort, and narrow-width horizontal overflow.",
9908
+ "description": "Data-agnostic companion layout for application-owned table caption controls, with a start cluster, optional search, trailing Filter/Sort/Group, and narrow-width horizontal overflow.",
9882
9909
  "type": "registry:ui",
9883
9910
  "meta": {
9884
9911
  "library": "@ds-mo/ui",
@@ -9889,7 +9916,7 @@
9889
9916
  "intent": {
9890
9917
  "audience": "general",
9891
9918
  "status": "stable",
9892
- "summary": "Data-agnostic companion layout for application-owned table caption controls, with a start cluster, optional search, trailing Filter/Group/Sort, and narrow-width horizontal overflow.",
9919
+ "summary": "Data-agnostic companion layout for application-owned table caption controls, with a start cluster, optional search, trailing Filter/Sort/Group, and narrow-width horizontal overflow.",
9893
9920
  "useWhen": [
9894
9921
  "Several table pages repeat the same saved-view cluster, middle discovery and result-shaping controls, and table-owned caption actions.",
9895
9922
  "A ds-table header slot needs consistent control spacing, alignment, grouping, and narrow-width overflow without moving product behavior into the table."
@@ -9912,17 +9939,18 @@
9912
9939
  "patterns": [],
9913
9940
  "accessibility": [
9914
9941
  "The component exposes a named toolbar while each slotted control retains its own accessible name and native keyboard behavior.",
9915
- "Use the start slot for saved views. Use search for the optional discovery field, leading for any additional left-side middle controls, and trailing for Filter, Group, and Sort. Table-owned column customizer and data-mode actions remain in the table caption trailing cluster.",
9942
+ "Use the start slot for saved views. Use search for the optional discovery field, leading for any additional left-side middle controls, and trailing for Filter, Sort, and Group. Table-owned column customizer and data-mode actions remain in the table caption trailing cluster and participate in the same caption overflow row.",
9916
9943
  "Tab order follows authored DOM order. The toolbar does not add roving focus because it may contain heterogeneous form and popup controls."
9917
9944
  ],
9918
9945
  "states": [
9919
9946
  "The component owns layout only. It never mutates slotted control props, values, menus, loading, persistence, or consequences. Use ds-table-saved-views and ds-table-filter when those interactions need the shared CompoMo behavior while application data and persistence remain external.",
9920
- "The start cluster is followed by a decorative vertical divider, then the spanning middle region. The start cluster and divider hide when nothing is projected. Search is a dedicated slot that hides when nothing is projected. Empty leading or trailing clusters add no placeholder chrome. The middle region occupies remaining width so trailing controls stay at its inline end.",
9921
- "Column customizer and data-mode switching stay table-owned caption actions. Do not recreate those menus in the toolbar."
9947
+ "Buttons in the toolbar keep their hover and pressed interaction paint without physically scaling, so pointer feedback does not disturb the stable caption geometry.",
9948
+ "The start cluster is followed by a decorative vertical divider, then Search, optional leading controls, and one trailing action cluster. The start cluster and divider hide when nothing is projected. Search is a dedicated slot that hides when nothing is projected. Empty leading or trailing clusters add no placeholder chrome. Search is the only toolbar region that shrinks; all action controls retain their control geometry and align directly before the table-owned actions.",
9949
+ "Column customizer and data-mode switching stay table-owned caption actions. Do not recreate those menus in the toolbar. When slotted into a table header, the table caption owns overflow for the complete toolbar and table-owned action row."
9922
9950
  ],
9923
9951
  "responsiveBehavior": [
9924
- "The start cluster, search, trailing Filter/Group/Sort, and table caption trailing actions stay on one line. Saved views hug their current trigger content up to the compact menu-width token rather than reserving that full width; long names truncate while the dirty dot stays visible. Filter, Group, and Sort triggers cap at the same token, and Group and Sort popups keep that compact menu-width floor. The toolbar horizontally scrolls when its complete controls no longer fit.",
9925
- "Inside a named ds-table container narrower than 900px, ds-table-filter and ds-table-sort omit their labels and chevrons and become icon-only. Group controls explicitly opt into the same treatment with collapseLabel. Active filtering or grouping promotes the prefix icon to primary. Sort keeps resting secondary chrome. Table-owned Customize also becomes the icon-only Table menu button at that width.",
9952
+ "The start cluster, search, trailing Filter/Sort/Group, and table caption trailing actions stay on one line. Saved views hug their current trigger content up to the compact menu-width token rather than reserving that full width; long names truncate while the dirty dot stays visible. Filter, Sort, and Group triggers cap at the same token, and Sort and Group popups keep that compact menu-width floor. A standalone toolbar owns its horizontal scrolling; inside a table, the complete caption row is the single overflow owner so table-owned actions never remain pinned over scrolling toolbar controls.",
9953
+ "Inside a named ds-table container narrower than 900px, Search retains its standard maximum width and flexes smaller only when the complete row needs space. ds-table-filter and ds-table-sort omit their labels and chevrons, and Group controls explicitly opted into collapseLabel become icon-only; each exposes its label through a hover and keyboard-focus tooltip. Empty leading and trailing slot groups do not add spacing. Active filtering or grouping promotes the prefix icon to primary. Sort keeps resting secondary chrome. Table-owned Customize also becomes the icon-only Table menu button at that width with the same tooltip behavior.",
9926
9954
  "The scrollbar is visually suppressed without removing touch, trackpad, mouse-wheel, or keyboard scrolling from the overflow container.",
9927
9955
  "Trailing controls remain grouped after leading controls; the toolbar does not collapse controls into an application-specific overflow menu."
9928
9956
  ],