themelia-react-ui 2.0.4 → 2.0.5

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 (588) hide show
  1. package/CHANGELOG.md +135 -0
  2. package/README.md +113 -18
  3. package/dist/{accordion-96o0pN62.js → accordion-B9MHG0cT.js} +11 -11
  4. package/dist/accordion-B9MHG0cT.js.map +1 -0
  5. package/dist/{action-menu-CHgs8vmj.js → action-menu-Dr8K-ghE.js} +48 -45
  6. package/dist/action-menu-Dr8K-ghE.js.map +1 -0
  7. package/dist/{affordance-geometry.generated-B8yGOr8-.js → affordance-geometry.generated-P-aVJisu.js} +36 -36
  8. package/dist/affordance-geometry.generated-P-aVJisu.js.map +1 -0
  9. package/dist/{ai-artifact-BZUGwBq6.js → ai-artifact-C58tQcBy.js} +210 -174
  10. package/dist/ai-artifact-C58tQcBy.js.map +1 -0
  11. package/dist/app-shell-B_IuNWOo.js +321 -0
  12. package/dist/app-shell-B_IuNWOo.js.map +1 -0
  13. package/dist/{attachment-DkoqHPPl.js → attachment-Dg_69lPS.js} +9 -9
  14. package/dist/{attachment-DkoqHPPl.js.map → attachment-Dg_69lPS.js.map} +1 -1
  15. package/dist/{auth-D2FVZ1MH.js → auth-DTkkshS8.js} +43 -46
  16. package/dist/auth-DTkkshS8.js.map +1 -0
  17. package/dist/{avatar-DWO_HHhB.js → avatar-CeZaFmNH.js} +8 -8
  18. package/dist/avatar-CeZaFmNH.js.map +1 -0
  19. package/dist/{badge-DErMA7ul.js → badge-xIiV_E7f.js} +5 -5
  20. package/dist/{badge-DErMA7ul.js.map → badge-xIiV_E7f.js.map} +1 -1
  21. package/dist/base/accordion.js +1 -1
  22. package/dist/base/action-menu.js +1 -1
  23. package/dist/base/attachment.js +1 -1
  24. package/dist/base/avatar.js +1 -1
  25. package/dist/base/badge.js +1 -1
  26. package/dist/base/batch-action-bar.js +1 -1
  27. package/dist/base/buttons.js +1 -1
  28. package/dist/base/cards.js +2 -2
  29. package/dist/base/carousel.js +3 -3
  30. package/dist/base/cascader.js +2 -372
  31. package/dist/base/chart.js +1 -1
  32. package/dist/base/choice-inputs.js +2 -2
  33. package/dist/base/code-block.js +1 -1
  34. package/dist/base/combobox.js +2 -2
  35. package/dist/base/command.js +1 -1
  36. package/dist/base/copyable.js +1 -1
  37. package/dist/base/date-pickers.js +2 -2
  38. package/dist/base/display.js +6 -6
  39. package/dist/base/feedback.js +3 -3
  40. package/dist/base/forms-numeric.js +1 -1
  41. package/dist/base/forms.js +2 -2
  42. package/dist/base/inline-edit.js +10 -10
  43. package/dist/base/item.js +1 -1
  44. package/dist/base/media-gallery.js +1 -1
  45. package/dist/base/metadata-list.js +1 -1
  46. package/dist/base/navigation.js +7 -7
  47. package/dist/base/popover-menu.js +1 -1
  48. package/dist/base/popover.js +1 -1
  49. package/dist/base/qr-code.js +1 -1
  50. package/dist/base/repeaters.js +12 -12
  51. package/dist/base/repeaters.js.map +1 -1
  52. package/dist/base/sheet.js +1 -1
  53. package/dist/base/sidebar.js +2 -2
  54. package/dist/base/signature-pad.js +7 -7
  55. package/dist/base/signature-pad.js.map +1 -1
  56. package/dist/base/skeleton.js +1 -1
  57. package/dist/base/spinner.js +4 -4
  58. package/dist/base/structure.js +1 -1
  59. package/dist/base/table.js +1 -1
  60. package/dist/base/text-inputs.js +2 -2
  61. package/dist/base/toaster.js +36 -36
  62. package/dist/base/toaster.js.map +1 -1
  63. package/dist/base/tree.js +1 -1
  64. package/dist/base/upload-queue.js +1 -1
  65. package/dist/base/upload.js +3 -3
  66. package/dist/base/value-inputs.js +1 -1
  67. package/dist/{batch-action-bar-CdfIkCsV.js → batch-action-bar-Cb8cuvyy.js} +6 -6
  68. package/dist/batch-action-bar-Cb8cuvyy.js.map +1 -0
  69. package/dist/blocks/admin.js +21 -21
  70. package/dist/blocks/admin.js.map +1 -1
  71. package/dist/blocks/ai.js +230 -216
  72. package/dist/blocks/ai.js.map +1 -1
  73. package/dist/blocks/analytics/usage-card.js +1 -1
  74. package/dist/blocks/analytics.js +325 -306
  75. package/dist/blocks/analytics.js.map +1 -1
  76. package/dist/blocks/collaboration.js +1 -1
  77. package/dist/blocks/commerce/receipt.js +1 -1
  78. package/dist/blocks/commerce.js +1259 -1184
  79. package/dist/blocks/commerce.js.map +1 -1
  80. package/dist/blocks/data-display.js +2 -2
  81. package/dist/blocks/onboarding.js +6 -6
  82. package/dist/blocks/onboarding.js.map +1 -1
  83. package/dist/blocks/planning.js +11 -11
  84. package/dist/blocks/timelines.js +1 -1
  85. package/dist/{breadcrumbs-B-WFKecD.js → breadcrumbs-J-K2kl4c.js} +6 -6
  86. package/dist/{breadcrumbs-B-WFKecD.js.map → breadcrumbs-J-K2kl4c.js.map} +1 -1
  87. package/dist/{button-DkmK-z8E.js → button-S2Cubiiz.js} +3 -3
  88. package/dist/button-S2Cubiiz.js.map +1 -0
  89. package/dist/{buttons-BbfzgczK.js → buttons-BedGfygC.js} +16 -16
  90. package/dist/buttons-BedGfygC.js.map +1 -0
  91. package/dist/cards-rNX1M4Xn.js +69 -0
  92. package/dist/cards-rNX1M4Xn.js.map +1 -0
  93. package/dist/cascader-B1MiqkBq.js +439 -0
  94. package/dist/cascader-B1MiqkBq.js.map +1 -0
  95. package/dist/chart-Darg_0Kv.js +162 -0
  96. package/dist/chart-Darg_0Kv.js.map +1 -0
  97. package/dist/{checkbox-CzYUTPeJ.js → checkbox-fflqmODR.js} +2 -2
  98. package/dist/{checkbox-CzYUTPeJ.js.map → checkbox-fflqmODR.js.map} +1 -1
  99. package/dist/{choice-inputs-JLtij2y4.js → choice-inputs-zUgBZfE4.js} +258 -257
  100. package/dist/choice-inputs-zUgBZfE4.js.map +1 -0
  101. package/dist/{code-block-CGNaUtAE.js → code-block-BzmM9Eig.js} +8 -8
  102. package/dist/code-block-BzmM9Eig.js.map +1 -0
  103. package/dist/{collapsible-Do4G0zcC.js → collapsible-BkAI_XD4.js} +5 -5
  104. package/dist/{collapsible-Do4G0zcC.js.map → collapsible-BkAI_XD4.js.map} +1 -1
  105. package/dist/{collection-content-C0e8SsTm.js → collection-content-gDh1v6O4.js} +3 -3
  106. package/dist/{collection-content-C0e8SsTm.js.map → collection-content-gDh1v6O4.js.map} +1 -1
  107. package/dist/combobox-BEx0vJgF.js +362 -0
  108. package/dist/combobox-BEx0vJgF.js.map +1 -0
  109. package/dist/{combobox-CBJDs6w_.js → combobox-BwzoXFSG.js} +254 -247
  110. package/dist/combobox-BwzoXFSG.js.map +1 -0
  111. package/dist/{command-D8NrBj-b.js → command-CrlPIL6m.js} +11 -11
  112. package/dist/command-CrlPIL6m.js.map +1 -0
  113. package/dist/{comments-Di4w0H_S.js → comments-Dmpp0YIc.js} +29 -29
  114. package/dist/{comments-Di4w0H_S.js.map → comments-Dmpp0YIc.js.map} +1 -1
  115. package/dist/components/base/action-menu/action-menu.d.ts +7 -1
  116. package/dist/components/base/buttons/button-group.recipes.d.ts +2 -2
  117. package/dist/components/base/cards/card.recipes.d.ts +6 -4
  118. package/dist/components/base/cards/partials/card-primitives.d.ts +9 -2
  119. package/dist/components/base/cards/partials/smart-card-header-row.d.ts +3 -1
  120. package/dist/components/base/cards/smart-card.types.d.ts +3 -4
  121. package/dist/components/base/cascader/cascader-panel.d.ts +76 -0
  122. package/dist/components/base/chart/chart.d.ts +6 -1
  123. package/dist/components/base/choice-inputs/choice-types.d.ts +1 -1
  124. package/dist/components/base/combobox/index.d.ts +4 -2
  125. package/dist/components/base/feedback/alert.d.ts +1 -1
  126. package/dist/components/base/forms/form-sizing.d.ts +7 -7
  127. package/dist/components/base/forms/workflow.d.ts +7 -1
  128. package/dist/components/base/forms-numeric/coordinates-input.d.ts +4 -0
  129. package/dist/components/base/metadata-list/metadata-list.d.ts +1 -1
  130. package/dist/components/base/navigation/dropdown-menu.d.ts +1 -1
  131. package/dist/components/base/navigation/page-header/page-header-commands.d.ts +14 -7
  132. package/dist/components/base/navigation/page-header/page-header.d.ts +10 -3
  133. package/dist/components/base/popover/popover.d.ts +3 -3
  134. package/dist/components/base/popover/popover.recipes.d.ts +10 -6
  135. package/dist/components/base/sheet/sheet.d.ts +2 -0
  136. package/dist/components/base/sidebar/index.d.ts +1 -1
  137. package/dist/components/base/sidebar/sidebar.d.ts +27 -5
  138. package/dist/components/base/toaster/index.d.ts +1 -0
  139. package/dist/components/blocks/ai/ai-file-tree/types.d.ts +1 -1
  140. package/dist/components/blocks/ai/ai-tool-call/types.d.ts +3 -1
  141. package/dist/components/blocks/ai/ai.strings.d.ts +2 -0
  142. package/dist/components/blocks/analytics/metric-comparison/metric-comparison.d.ts +2 -0
  143. package/dist/components/blocks/analytics/usage-card/usage-card.strings.d.ts +2 -0
  144. package/dist/components/blocks/commerce/cart-summary/cart-summary.d.ts +1 -1
  145. package/dist/components/blocks/commerce/cart-summary/types.d.ts +3 -1
  146. package/dist/components/blocks/commerce/discount-stack/discount-stack.d.ts +1 -1
  147. package/dist/components/blocks/commerce/discount-stack/types.d.ts +3 -1
  148. package/dist/components/blocks/commerce/fulfillment-group/fulfillment-group.d.ts +1 -1
  149. package/dist/components/blocks/commerce/fulfillment-group/types.d.ts +11 -2
  150. package/dist/components/blocks/commerce/index.d.ts +7 -6
  151. package/dist/components/blocks/commerce/inventory-section/inventory-section.d.ts +1 -1
  152. package/dist/components/blocks/commerce/inventory-section/types.d.ts +7 -2
  153. package/dist/components/blocks/commerce/invoice-header/index.d.ts +2 -1
  154. package/dist/components/blocks/commerce/invoice-header/invoice-header.d.ts +1 -1
  155. package/dist/components/blocks/commerce/invoice-header/types.d.ts +20 -6
  156. package/dist/components/blocks/commerce/invoice-line-items/invoice-line-items.d.ts +1 -1
  157. package/dist/components/blocks/commerce/invoice-line-items/types.d.ts +24 -7
  158. package/dist/components/blocks/commerce/invoice-mini/index.d.ts +2 -1
  159. package/dist/components/blocks/commerce/invoice-mini/types.d.ts +7 -1
  160. package/dist/components/blocks/commerce/order-customer/order-customer.d.ts +1 -1
  161. package/dist/components/blocks/commerce/order-customer/types.d.ts +9 -1
  162. package/dist/components/blocks/commerce/refund-status/refund-status.d.ts +1 -1
  163. package/dist/components/blocks/commerce/refund-status/types.d.ts +3 -1
  164. package/dist/components/blocks/commerce/shipment-tracking/shipment-tracking.d.ts +1 -1
  165. package/dist/components/blocks/commerce/shipment-tracking/types.d.ts +3 -1
  166. package/dist/components/blocks/commerce/tax-breakdown/tax-breakdown.d.ts +1 -1
  167. package/dist/components/blocks/commerce/tax-breakdown/types.d.ts +3 -1
  168. package/dist/components/features/activities/activities.types.d.ts +8 -2
  169. package/dist/components/features/activities/partials/activity-marker.d.ts +7 -3
  170. package/dist/components/features/activities/partials/activity-row.d.ts +24 -0
  171. package/dist/components/features/event-calendar/colors.d.ts +2 -2
  172. package/dist/components/features/event-calendar/event-calendar.types.d.ts +4 -0
  173. package/dist/components/features/filters/facets/select-facet.d.ts +2 -1
  174. package/dist/components/features/filters/filters.strings.d.ts +4 -0
  175. package/dist/components/features/filters/filters.types.d.ts +3 -0
  176. package/dist/components/features/filters/partials/filter-editor-header.d.ts +16 -0
  177. package/dist/components/features/filters/partials/filter-popover-content.d.ts +1 -1
  178. package/dist/components/features/filters/partials/nested-filter-options.d.ts +21 -0
  179. package/dist/components/features/filters/partials/regular-filter-content.d.ts +2 -1
  180. package/dist/components/features/filters/utils/filter-utils.d.ts +21 -3
  181. package/dist/components/features/global-search/global-search.types.d.ts +2 -0
  182. package/dist/components/features/global-search/partials/global-search-input.d.ts +10 -0
  183. package/dist/components/features/global-search/partials/global-search-result-row.d.ts +7 -1
  184. package/dist/components/features/media-library/media-library.strings.d.ts +2 -0
  185. package/dist/components/features/media-library/media-library.types.d.ts +14 -4
  186. package/dist/components/features/media-library/partials/media-library-partials.d.ts +1 -1
  187. package/dist/components/features/products/components/product-options-matrix.d.ts +1 -1
  188. package/dist/components/features/products/components/product-variant-editor.d.ts +1 -1
  189. package/dist/components/features/products/components/product-variants-manager.d.ts +1 -1
  190. package/dist/components/features/products/products.strings.d.ts +9 -1
  191. package/dist/components/features/products/products.types.d.ts +43 -6
  192. package/dist/components/features/resource/resource.d.ts +5 -1
  193. package/dist/components/features/resource/resource.types.d.ts +12 -1
  194. package/dist/components/features/rich-text-editor/partials/image-uploads.d.ts +1 -1
  195. package/dist/components/features/rich-text-editor/rich-text-editor.recipes.d.ts +12 -2
  196. package/dist/components/features/table/partials/resource-cell.d.ts +22 -5
  197. package/dist/components/features/table/table.types.d.ts +1 -1
  198. package/dist/components/layout/app-shell/app-shell.types.d.ts +35 -6
  199. package/dist/components/layout/app-shell/index.d.ts +1 -1
  200. package/dist/components/layout/app-shell/shell-regions.d.ts +27 -0
  201. package/dist/components/layout/app-shell/sidebar-inset-layout.d.ts +1 -1
  202. package/dist/components/layout/app-shell/stacked-layout.d.ts +1 -1
  203. package/dist/components/layout/app-shell/topbar-sidebar-layout.d.ts +1 -1
  204. package/dist/components/layout/containers/two-column-layout.d.ts +2 -0
  205. package/dist/components/layout/header/header.recipes.d.ts +6 -0
  206. package/dist/components/layout/header/header.types.d.ts +6 -6
  207. package/dist/components/layout/navigation/section-nav/section-nav.d.ts +5 -0
  208. package/dist/components/layout/page/index.d.ts +2 -1
  209. package/dist/components/layout/page/page.d.ts +1 -0
  210. package/dist/components/layout/page/page.strings.d.ts +11 -0
  211. package/dist/components/layout/page/page.types.d.ts +69 -17
  212. package/dist/components/layout/page/partials/page-header.d.ts +1 -1
  213. package/dist/components/layout/settings/settings.d.ts +9 -2
  214. package/dist/components/layout/sidebar/app-sidebar.d.ts +1 -1
  215. package/dist/components/layout/sidebar/components/sidebar-grouped-navigation.d.ts +1 -1
  216. package/dist/components/layout/sidebar/components/sidebar-navigation.d.ts +1 -1
  217. package/dist/components/layout/sidebar/index.d.ts +2 -1
  218. package/dist/components/layout/sidebar/sidebar-navigation-rail.d.ts +36 -0
  219. package/dist/components/layout/sidebar/sidebar.context.d.ts +5 -7
  220. package/dist/components/layout/sidebar/sidebar.types.d.ts +48 -10
  221. package/dist/components/layout/sidebar/sidebar.utils.d.ts +9 -1
  222. package/dist/components/layout/workspace/workspace.types.d.ts +8 -1
  223. package/dist/components/ui/alert-dialog.d.ts +2 -2
  224. package/dist/components/ui/combobox.d.ts +1 -1
  225. package/dist/components/ui/dialog.d.ts +2 -2
  226. package/dist/components/ui/overlay-layer.d.ts +18 -0
  227. package/dist/components/ui/sheet.d.ts +5 -1
  228. package/dist/components/ui/sidebar-state.d.ts +82 -0
  229. package/dist/components/ui/sidebar.d.ts +5 -17
  230. package/dist/{containers-CRmHf7GP.js → containers-CIxwbZt5.js} +26 -18
  231. package/dist/containers-CIxwbZt5.js.map +1 -0
  232. package/dist/{control-group-recipes-DBXmfh2E.js → control-group-recipes-fMuFo0H7.js} +6 -6
  233. package/dist/control-group-recipes-fMuFo0H7.js.map +1 -0
  234. package/dist/{copyable-CnGCZdX4.js → copyable-DIznBqiT.js} +7 -7
  235. package/dist/{copyable-CnGCZdX4.js.map → copyable-DIznBqiT.js.map} +1 -1
  236. package/dist/{data-display-D4oujtx0.js → data-display-BdgCAw8T.js} +268 -277
  237. package/dist/data-display-BdgCAw8T.js.map +1 -0
  238. package/dist/{date-Q7jnXxpk.js → date-Cq8ioE4o.js} +5 -5
  239. package/dist/{date-Q7jnXxpk.js.map → date-Cq8ioE4o.js.map} +1 -1
  240. package/dist/{date-picker-zOt1oNss.js → date-picker-RlIzKom_.js} +446 -437
  241. package/dist/date-picker-RlIzKom_.js.map +1 -0
  242. package/dist/{date-pickers-cj8IXbDC.js → date-pickers-DY-CpXS1.js} +12 -12
  243. package/dist/{date-pickers-cj8IXbDC.js.map → date-pickers-DY-CpXS1.js.map} +1 -1
  244. package/dist/{display-BB4BL_-K.js → display-KsZLw9Ov.js} +9 -9
  245. package/dist/{display-BB4BL_-K.js.map → display-KsZLw9Ov.js.map} +1 -1
  246. package/dist/dropdown-menu-CLs8Lihj.js +194 -0
  247. package/dist/dropdown-menu-CLs8Lihj.js.map +1 -0
  248. package/dist/features/activities.js +302 -253
  249. package/dist/features/activities.js.map +1 -1
  250. package/dist/features/ai-chat.js +15 -14
  251. package/dist/features/ai-chat.js.map +1 -1
  252. package/dist/features/appointment-calendar.js +22 -22
  253. package/dist/features/appointment-calendar.js.map +1 -1
  254. package/dist/features/async-preview.js +11 -10
  255. package/dist/features/async-preview.js.map +1 -1
  256. package/dist/features/combobox.js +1 -1
  257. package/dist/features/comments.js +1 -1
  258. package/dist/features/data-view.js +13 -13
  259. package/dist/features/data-view.js.map +1 -1
  260. package/dist/features/email-composer.js +237 -238
  261. package/dist/features/email-composer.js.map +1 -1
  262. package/dist/features/event-calendar.js +140 -146
  263. package/dist/features/event-calendar.js.map +1 -1
  264. package/dist/features/filters.js +1 -1
  265. package/dist/features/global-search.js +167 -138
  266. package/dist/features/global-search.js.map +1 -1
  267. package/dist/features/kanban.js +6 -7
  268. package/dist/features/kanban.js.map +1 -1
  269. package/dist/features/map.js +269 -260
  270. package/dist/features/map.js.map +1 -1
  271. package/dist/features/media-library.js +375 -326
  272. package/dist/features/media-library.js.map +1 -1
  273. package/dist/features/mentions.js +1 -1
  274. package/dist/features/overlays.js +1 -1
  275. package/dist/features/products.js +1266 -1193
  276. package/dist/features/products.js.map +1 -1
  277. package/dist/features/questionnaire.js +13 -13
  278. package/dist/features/questionnaire.js.map +1 -1
  279. package/dist/features/resource-assignment.js +8 -8
  280. package/dist/features/resource.js +69 -66
  281. package/dist/features/resource.js.map +1 -1
  282. package/dist/features/rich-text-editor.js +2 -2
  283. package/dist/features/schema-form.js +13 -13
  284. package/dist/features/suggestions.js +3 -3
  285. package/dist/features/sync.js +3 -3
  286. package/dist/features/table.js +1 -1
  287. package/dist/features/theme-tweaker.js +94 -95
  288. package/dist/features/theme-tweaker.js.map +1 -1
  289. package/dist/{feedback-DZNL5Lg1.js → feedback-Dm4izqFn.js} +80 -79
  290. package/dist/feedback-Dm4izqFn.js.map +1 -0
  291. package/dist/{field-affordance-recipes-DjwGx8eM.js → field-affordance-recipes-DWcNcOjh.js} +4 -4
  292. package/dist/field-affordance-recipes-DWcNcOjh.js.map +1 -0
  293. package/dist/{filters-BcT-uoFy.js → filters-DzgSJpMK.js} +693 -645
  294. package/dist/filters-DzgSJpMK.js.map +1 -0
  295. package/dist/{form-field-CsgRmdit.js → form-field-BriWAXyg.js} +6 -6
  296. package/dist/{form-field-CsgRmdit.js.map → form-field-BriWAXyg.js.map} +1 -1
  297. package/dist/{form-sizing-NXgjkmBG.js → form-sizing-Bs2qFUeE.js} +4 -4
  298. package/dist/form-sizing-Bs2qFUeE.js.map +1 -0
  299. package/dist/{forms-BV5m5aQF.js → forms-C0RGnJTe.js} +17 -13
  300. package/dist/forms-C0RGnJTe.js.map +1 -0
  301. package/dist/{forms-numeric-Cd33AKbD.js → forms-numeric-DIhghX1Q.js} +86 -82
  302. package/dist/forms-numeric-DIhghX1Q.js.map +1 -0
  303. package/dist/{header-BA697l4a.js → header-DMMxG__r.js} +67 -63
  304. package/dist/header-DMMxG__r.js.map +1 -0
  305. package/dist/header.recipes-BigdJjw1.js +6 -0
  306. package/dist/header.recipes-BigdJjw1.js.map +1 -0
  307. package/dist/{hooks-BQ-L7Uu2.js → hooks-DB6CJ4MR.js} +26 -14
  308. package/dist/hooks-DB6CJ4MR.js.map +1 -0
  309. package/dist/{icon-badge-COWbCCWu.js → icon-badge-DZF-YjQp.js} +4 -4
  310. package/dist/{icon-badge-COWbCCWu.js.map → icon-badge-DZF-YjQp.js.map} +1 -1
  311. package/dist/index.js +4 -5
  312. package/dist/{input-Cdw4k0fb.js → input-CvyDQlY7.js} +8 -8
  313. package/dist/{input-Cdw4k0fb.js.map → input-CvyDQlY7.js.map} +1 -1
  314. package/dist/input-DMBwAZmr.js +21 -0
  315. package/dist/input-DMBwAZmr.js.map +1 -0
  316. package/dist/{item-BvZ2DR_x.js → item-DjCcM18E.js} +16 -16
  317. package/dist/item-DjCcM18E.js.map +1 -0
  318. package/dist/layout/app-shell.js +1 -1
  319. package/dist/layout/auth.js +1 -1
  320. package/dist/layout/containers.js +1 -1
  321. package/dist/layout/header.js +1 -1
  322. package/dist/layout/hooks.js +1 -1
  323. package/dist/layout/navigation.js +1 -1
  324. package/dist/layout/page.js +2 -2
  325. package/dist/layout/settings.js +1 -1
  326. package/dist/layout/sidebar.js +3 -3
  327. package/dist/layout/workspace.js +1 -1
  328. package/dist/layout.js +11 -11
  329. package/dist/lib/affordance-geometry.generated.d.ts +35 -35
  330. package/dist/lib/control-group-recipes.d.ts +5 -5
  331. package/dist/lib/field-affordance-recipes.d.ts +2 -2
  332. package/dist/lib/motion-recipes.d.ts +3 -0
  333. package/dist/lib/navigation-row-recipes.d.ts +7 -7
  334. package/dist/lib/overlay-anatomy-recipes.d.ts +3 -3
  335. package/dist/lib/portal-recipes.d.ts +3 -0
  336. package/dist/lib/token-merge.generated.d.ts +13 -10
  337. package/dist/lib/utils.js +1 -1
  338. package/dist/map-demo-tile.svg +1 -1
  339. package/dist/{media-gallery-CSl5W1Fy.js → media-gallery-C-vChPmE.js} +6 -6
  340. package/dist/{media-gallery-CSl5W1Fy.js.map → media-gallery-C-vChPmE.js.map} +1 -1
  341. package/dist/{mentions-RKOQaaIB.js → mentions-0_4nWuI-.js} +10 -10
  342. package/dist/{mentions-RKOQaaIB.js.map → mentions-0_4nWuI-.js.map} +1 -1
  343. package/dist/{message-INU2xsxu.js → message-g8p7eVtN.js} +7 -7
  344. package/dist/message-g8p7eVtN.js.map +1 -0
  345. package/dist/{metadata-list-qVV7AVxa.js → metadata-list-C3MkU-Om.js} +64 -64
  346. package/dist/metadata-list-C3MkU-Om.js.map +1 -0
  347. package/dist/{motion-recipes-ChBg4HBq.js → motion-recipes-DitYv6P2.js} +10 -2
  348. package/dist/motion-recipes-DitYv6P2.js.map +1 -0
  349. package/dist/navigation-C90-oQFq.js +255 -0
  350. package/dist/navigation-C90-oQFq.js.map +1 -0
  351. package/dist/{navigation-BQNcg1jT.js → navigation-DaRjthVk.js} +101 -82
  352. package/dist/navigation-DaRjthVk.js.map +1 -0
  353. package/dist/{navigation-row-recipes-WZEs-hYo.js → navigation-row-recipes-BGVLvU_O.js} +9 -9
  354. package/dist/navigation-row-recipes-BGVLvU_O.js.map +1 -0
  355. package/dist/{number-E_OiIfJz.js → number-CRU3kGuk.js} +5 -5
  356. package/dist/{number-E_OiIfJz.js.map → number-CRU3kGuk.js.map} +1 -1
  357. package/dist/overflow-tab-bar-IVFVQFV6.js +172 -0
  358. package/dist/overflow-tab-bar-IVFVQFV6.js.map +1 -0
  359. package/dist/overlays-BSgCtM2v.js +1013 -0
  360. package/dist/overlays-BSgCtM2v.js.map +1 -0
  361. package/dist/page-DUUKDAjo.js +331 -0
  362. package/dist/page-DUUKDAjo.js.map +1 -0
  363. package/dist/page-header-commands-7P3sfE9u.js +193 -0
  364. package/dist/page-header-commands-7P3sfE9u.js.map +1 -0
  365. package/dist/{pagination-Ch8X_47u.js → pagination-CgCjElfu.js} +7 -7
  366. package/dist/{pagination-Ch8X_47u.js.map → pagination-CgCjElfu.js.map} +1 -1
  367. package/dist/popover-B85RenIl.js +201 -0
  368. package/dist/popover-B85RenIl.js.map +1 -0
  369. package/dist/{popover-menu-BYaCdFhf.js → popover-menu-DlnQ2zlR.js} +7 -7
  370. package/dist/{popover-menu-BYaCdFhf.js.map → popover-menu-DlnQ2zlR.js.map} +1 -1
  371. package/dist/{popover.recipes-D3j8R8Yp.js → popover.recipes-DdiEp0rd.js} +18 -11
  372. package/dist/popover.recipes-DdiEp0rd.js.map +1 -0
  373. package/dist/portal-recipes-BpsV_iRw.js +9 -0
  374. package/dist/portal-recipes-BpsV_iRw.js.map +1 -0
  375. package/dist/{primitives-Du64iDbb.js → primitives-ByWgGUgt.js} +5 -5
  376. package/dist/{primitives-Du64iDbb.js.map → primitives-ByWgGUgt.js.map} +1 -1
  377. package/dist/primitives.js +4 -4
  378. package/dist/{progress-BvF2oCdV.js → progress-BJhQP9WX.js} +9 -9
  379. package/dist/{progress-BvF2oCdV.js.map → progress-BJhQP9WX.js.map} +1 -1
  380. package/dist/{receipt-D789Jwyy.js → receipt-tOc7B3hw.js} +7 -7
  381. package/dist/receipt-tOc7B3hw.js.map +1 -0
  382. package/dist/{rich-text-CLJEf76s.js → rich-text-Dth5E882.js} +3 -3
  383. package/dist/{rich-text-CLJEf76s.js.map → rich-text-Dth5E882.js.map} +1 -1
  384. package/dist/{rich-text-editor-Cm1dpUpQ.js → rich-text-editor-C1fOjBTd.js} +429 -430
  385. package/dist/rich-text-editor-C1fOjBTd.js.map +1 -0
  386. package/dist/{rich-text-editor-toolbar-L__e4x47.js → rich-text-editor-toolbar-laT3tpBT.js} +32 -28
  387. package/dist/rich-text-editor-toolbar-laT3tpBT.js.map +1 -0
  388. package/dist/{scroll-area-La5RXsEk.js → scroll-area-B0dB2cBi.js} +2 -2
  389. package/dist/{scroll-area-La5RXsEk.js.map → scroll-area-B0dB2cBi.js.map} +1 -1
  390. package/dist/{separator-DAEV0w65.js → separator-Bb17I4pu.js} +2 -2
  391. package/dist/{separator-DAEV0w65.js.map → separator-Bb17I4pu.js.map} +1 -1
  392. package/dist/{separator-GNfPG-90.js → separator-Ct_WSWTJ.js} +3 -3
  393. package/dist/{separator-GNfPG-90.js.map → separator-Ct_WSWTJ.js.map} +1 -1
  394. package/dist/services/actions.js +1 -1
  395. package/dist/{settings-BA2iboic.js → settings-Bar5wiSo.js} +31 -38
  396. package/dist/settings-Bar5wiSo.js.map +1 -0
  397. package/dist/sheet-BGPERY55.js +250 -0
  398. package/dist/sheet-BGPERY55.js.map +1 -0
  399. package/dist/sidebar-D-JYMMbY.js +686 -0
  400. package/dist/sidebar-D-JYMMbY.js.map +1 -0
  401. package/dist/sidebar-f9gRWXIR.js +1001 -0
  402. package/dist/sidebar-f9gRWXIR.js.map +1 -0
  403. package/dist/skeleton-BapcJd9C.js +265 -0
  404. package/dist/skeleton-BapcJd9C.js.map +1 -0
  405. package/dist/smart-card-CW0hjetE.js +617 -0
  406. package/dist/smart-card-CW0hjetE.js.map +1 -0
  407. package/dist/{structure-C0V0_Fyf.js → structure-DuxKl954.js} +2 -2
  408. package/dist/{structure-C0V0_Fyf.js.map → structure-DuxKl954.js.map} +1 -1
  409. package/dist/style.css +1 -1
  410. package/dist/{table-CpvnzJn9.js → table-B4TE7iOV.js} +3 -3
  411. package/dist/{table-CpvnzJn9.js.map → table-B4TE7iOV.js.map} +1 -1
  412. package/dist/{table-DH7rozUv.js → table-CFOWY9pQ.js} +394 -388
  413. package/dist/table-CFOWY9pQ.js.map +1 -0
  414. package/dist/{tabs-eqVsJZWO.js → tabs-e7ko9S2o.js} +11 -11
  415. package/dist/tabs-e7ko9S2o.js.map +1 -0
  416. package/dist/{text-inputs-DXItiR0O.js → text-inputs-B1culiSK.js} +17 -17
  417. package/dist/text-inputs-B1culiSK.js.map +1 -0
  418. package/dist/theme.css +40 -37
  419. package/dist/{timelines-DmtztAY_.js → timelines-D3Sl9ueq.js} +13 -12
  420. package/dist/timelines-D3Sl9ueq.js.map +1 -0
  421. package/dist/tooltip-8wz5S_Zr.js +113 -0
  422. package/dist/tooltip-8wz5S_Zr.js.map +1 -0
  423. package/dist/{tree-CV11PURP.js → tree-CzUJHnFs.js} +8 -8
  424. package/dist/{tree-CV11PURP.js.map → tree-CzUJHnFs.js.map} +1 -1
  425. package/dist/typography/rich-text.js +1 -1
  426. package/dist/{typography-CSyJrh28.js → typography-BQnine1k.js} +12 -12
  427. package/dist/{typography-CSyJrh28.js.map → typography-BQnine1k.js.map} +1 -1
  428. package/dist/typography.js +1 -1
  429. package/dist/ui-provider-C-74NVLc.js +178 -0
  430. package/dist/ui-provider-C-74NVLc.js.map +1 -0
  431. package/dist/ui-provider.js +2 -3
  432. package/dist/{upload-C9SA0X-8.js → upload-BOW6U2tI.js} +25 -24
  433. package/dist/upload-BOW6U2tI.js.map +1 -0
  434. package/dist/{upload-queue-DhQJiSRc.js → upload-queue-qUXn1ykN.js} +16 -14
  435. package/dist/upload-queue-qUXn1ykN.js.map +1 -0
  436. package/dist/{usage-card-DVTRDmGz.js → usage-card-BeSLY0i0.js} +24 -17
  437. package/dist/usage-card-BeSLY0i0.js.map +1 -0
  438. package/dist/{utils-CF3c95Io.js → utils-B6kKb8jB.js} +237 -141
  439. package/dist/utils-B6kKb8jB.js.map +1 -0
  440. package/dist/value-inputs-skdVB2zh.js +980 -0
  441. package/dist/value-inputs-skdVB2zh.js.map +1 -0
  442. package/dist/{value-typography-ClupF0Gz.js → value-typography-uAmWUAL1.js} +2 -2
  443. package/dist/{value-typography-ClupF0Gz.js.map → value-typography-uAmWUAL1.js.map} +1 -1
  444. package/dist/{workspace-9EFVXUyk.js → workspace-DHeHv0qw.js} +50 -49
  445. package/dist/workspace-DHeHv0qw.js.map +1 -0
  446. package/docs/consumer-skill/SKILL.md +4 -3
  447. package/docs/consumer-skill/agents/themelia-ui-designer.md +96 -0
  448. package/docs/consumer-skill/examples/app-layout.tsx +52 -1
  449. package/docs/consumer-skill/examples/commerce-lines.tsx +271 -0
  450. package/docs/consumer-skill/examples/custom-composition.tsx +21 -0
  451. package/docs/consumer-skill/examples/detail.tsx +5 -4
  452. package/docs/consumer-skill/examples/editable-sections.tsx +38 -0
  453. package/docs/consumer-skill/examples/index-table.tsx +4 -4
  454. package/docs/consumer-skill/examples/manifest.json +38 -1
  455. package/docs/consumer-skill/examples/product-media.tsx +41 -0
  456. package/docs/consumer-skill/examples/product-parts.tsx +81 -0
  457. package/docs/consumer-skill/examples/rhf-edit-page.tsx +1 -1
  458. package/docs/consumer-skill/examples/rhf-overlay-form.tsx +24 -11
  459. package/docs/consumer-skill/examples/token-domain-structure.tsx +68 -0
  460. package/docs/consumer-skill/references/agents-md.md +19 -17
  461. package/docs/consumer-skill/references/analytics.md +18 -1
  462. package/docs/consumer-skill/references/commerce-composition.md +97 -0
  463. package/docs/consumer-skill/references/component-contracts.json +7131 -1754
  464. package/docs/consumer-skill/references/composition-mapping.md +17 -3
  465. package/docs/consumer-skill/references/composition.md +159 -31
  466. package/docs/consumer-skill/references/critique-rubric.md +98 -0
  467. package/docs/consumer-skill/references/custom-composition.md +261 -0
  468. package/docs/consumer-skill/references/data-reading.md +79 -0
  469. package/docs/consumer-skill/references/design-contract.md +11 -9
  470. package/docs/consumer-skill/references/discovery.md +27 -3
  471. package/docs/consumer-skill/references/domain-structures.md +124 -0
  472. package/docs/consumer-skill/references/installation.md +15 -4
  473. package/docs/consumer-skill/references/integration.md +35 -0
  474. package/docs/consumer-skill/references/product-composition.md +94 -0
  475. package/docs/consumer-skill/references/public-api.json +2881 -420
  476. package/docs/consumer-skill/references/review.md +39 -2
  477. package/docs/consumer-skill/references/rhf-workflows.md +7 -0
  478. package/docs/consumer-skill/references/screen-archetypes.md +19 -7
  479. package/docs/consumer-skill/references/screen-decisions.md +126 -0
  480. package/docs/consumer-skill/references/theming.md +17 -18
  481. package/docs/consumer-skill/references/tokens.json +9 -9
  482. package/docs/consumer-skill/references/view-craft.md +153 -0
  483. package/docs/consumer-skill/references/view-spec.md +73 -0
  484. package/docs/consumer-skill/references/workflows.md +7 -1
  485. package/docs/consumer-skill/scripts/composition-policy.mjs +39 -3
  486. package/docs/consumer-skill/scripts/composition-schema.mjs +1 -1
  487. package/docs/consumer-skill/scripts/composition.mjs +61 -5
  488. package/docs/consumer-skill/scripts/discovery.mjs +54 -0
  489. package/docs/consumer-skill/scripts/inspect-component.mjs +1 -1
  490. package/docs/consumer-skill/scripts/inspection.mjs +73 -18
  491. package/docs/consumer-skill/scripts/setup-skills.mjs +41 -6
  492. package/docs/consumer-skill/scripts/surface-policy.mjs +5 -5
  493. package/docs/consumer-skills/themelia-components/SKILL.md +14 -13
  494. package/docs/consumer-skills/themelia-composition/SKILL.md +16 -17
  495. package/docs/consumer-skills/themelia-design/SKILL.md +55 -0
  496. package/docs/consumer-skills/themelia-foundation/SKILL.md +2 -1
  497. package/docs/consumer-skills/themelia-tokenization/SKILL.md +24 -17
  498. package/package.json +1 -1
  499. package/dist/accordion-96o0pN62.js.map +0 -1
  500. package/dist/action-menu-CHgs8vmj.js.map +0 -1
  501. package/dist/affordance-geometry.generated-B8yGOr8-.js.map +0 -1
  502. package/dist/ai-artifact-BZUGwBq6.js.map +0 -1
  503. package/dist/app-shell-D63ATkIK.js +0 -237
  504. package/dist/app-shell-D63ATkIK.js.map +0 -1
  505. package/dist/auth-D2FVZ1MH.js.map +0 -1
  506. package/dist/avatar-DWO_HHhB.js.map +0 -1
  507. package/dist/base/cascader.js.map +0 -1
  508. package/dist/batch-action-bar-CdfIkCsV.js.map +0 -1
  509. package/dist/button-DkmK-z8E.js.map +0 -1
  510. package/dist/buttons-BbfzgczK.js.map +0 -1
  511. package/dist/cards-4JTWqvVe.js +0 -68
  512. package/dist/cards-4JTWqvVe.js.map +0 -1
  513. package/dist/chart-C19flDQb.js +0 -155
  514. package/dist/chart-C19flDQb.js.map +0 -1
  515. package/dist/choice-inputs-JLtij2y4.js.map +0 -1
  516. package/dist/code-block-CGNaUtAE.js.map +0 -1
  517. package/dist/combobox-CBJDs6w_.js.map +0 -1
  518. package/dist/combobox-hbhSS18O.js +0 -355
  519. package/dist/combobox-hbhSS18O.js.map +0 -1
  520. package/dist/command-D8NrBj-b.js.map +0 -1
  521. package/dist/containers-CRmHf7GP.js.map +0 -1
  522. package/dist/control-group-recipes-DBXmfh2E.js.map +0 -1
  523. package/dist/data-display-D4oujtx0.js.map +0 -1
  524. package/dist/date-picker-zOt1oNss.js.map +0 -1
  525. package/dist/dropdown-menu-CjY8Vt7W.js +0 -193
  526. package/dist/dropdown-menu-CjY8Vt7W.js.map +0 -1
  527. package/dist/feedback-DZNL5Lg1.js.map +0 -1
  528. package/dist/field-affordance-recipes-DjwGx8eM.js.map +0 -1
  529. package/dist/filters-BcT-uoFy.js.map +0 -1
  530. package/dist/form-sizing-NXgjkmBG.js.map +0 -1
  531. package/dist/forms-BV5m5aQF.js.map +0 -1
  532. package/dist/forms-numeric-Cd33AKbD.js.map +0 -1
  533. package/dist/header-BA697l4a.js.map +0 -1
  534. package/dist/hooks-BQ-L7Uu2.js.map +0 -1
  535. package/dist/input-DOPL5_QE.js +0 -21
  536. package/dist/input-DOPL5_QE.js.map +0 -1
  537. package/dist/item-BvZ2DR_x.js.map +0 -1
  538. package/dist/message-INU2xsxu.js.map +0 -1
  539. package/dist/metadata-list-qVV7AVxa.js.map +0 -1
  540. package/dist/motion-recipes-ChBg4HBq.js.map +0 -1
  541. package/dist/navigation-BQNcg1jT.js.map +0 -1
  542. package/dist/navigation-DLF3E1IC.js +0 -145
  543. package/dist/navigation-DLF3E1IC.js.map +0 -1
  544. package/dist/navigation-row-recipes-WZEs-hYo.js.map +0 -1
  545. package/dist/overflow-tab-bar-CrbqMteP.js +0 -284
  546. package/dist/overflow-tab-bar-CrbqMteP.js.map +0 -1
  547. package/dist/overlays-BlKynmde.js +0 -1004
  548. package/dist/overlays-BlKynmde.js.map +0 -1
  549. package/dist/page-CIwNUtGr.js +0 -227
  550. package/dist/page-CIwNUtGr.js.map +0 -1
  551. package/dist/page-header-commands-DFMlNYzN.js +0 -128
  552. package/dist/page-header-commands-DFMlNYzN.js.map +0 -1
  553. package/dist/popover-uwzX2vo7.js +0 -198
  554. package/dist/popover-uwzX2vo7.js.map +0 -1
  555. package/dist/popover.recipes-D3j8R8Yp.js.map +0 -1
  556. package/dist/receipt-D789Jwyy.js.map +0 -1
  557. package/dist/rich-text-editor-Cm1dpUpQ.js.map +0 -1
  558. package/dist/rich-text-editor-toolbar-L__e4x47.js.map +0 -1
  559. package/dist/settings-BA2iboic.js.map +0 -1
  560. package/dist/sheet-DoAxTb2J.js +0 -213
  561. package/dist/sheet-DoAxTb2J.js.map +0 -1
  562. package/dist/sidebar-ByqKR_W5.js +0 -512
  563. package/dist/sidebar-ByqKR_W5.js.map +0 -1
  564. package/dist/sidebar-D2iSnGJ7.js +0 -619
  565. package/dist/sidebar-D2iSnGJ7.js.map +0 -1
  566. package/dist/skeleton-CUj8O5Bd.js +0 -264
  567. package/dist/skeleton-CUj8O5Bd.js.map +0 -1
  568. package/dist/smart-card-CqkzQsK-.js +0 -614
  569. package/dist/smart-card-CqkzQsK-.js.map +0 -1
  570. package/dist/table-DH7rozUv.js.map +0 -1
  571. package/dist/tabs-eqVsJZWO.js.map +0 -1
  572. package/dist/text-inputs-DXItiR0O.js.map +0 -1
  573. package/dist/theme-scope-BJ6eE2xt.js +0 -50
  574. package/dist/theme-scope-BJ6eE2xt.js.map +0 -1
  575. package/dist/timelines-DmtztAY_.js.map +0 -1
  576. package/dist/tooltip-B4hjKtoV.js +0 -111
  577. package/dist/tooltip-B4hjKtoV.js.map +0 -1
  578. package/dist/ui-provider-CQCuCBcy.js +0 -136
  579. package/dist/ui-provider-CQCuCBcy.js.map +0 -1
  580. package/dist/upload-C9SA0X-8.js.map +0 -1
  581. package/dist/upload-queue-DhQJiSRc.js.map +0 -1
  582. package/dist/usage-card-DVTRDmGz.js.map +0 -1
  583. package/dist/use-mobile-vaLKwgyf.js +0 -15
  584. package/dist/use-mobile-vaLKwgyf.js.map +0 -1
  585. package/dist/utils-CF3c95Io.js.map +0 -1
  586. package/dist/value-inputs-C0o70p-U.js +0 -980
  587. package/dist/value-inputs-C0o70p-U.js.map +0 -1
  588. package/dist/workspace-9EFVXUyk.js.map +0 -1
@@ -124,7 +124,13 @@ back navigation, content bounds, commands and section tabs. Pass the record's
124
124
  main command as `header.primaryAction`, supporting commands as
125
125
  `header.secondaryActions` and separately selected/routed sections as `header.tabs`;
126
126
  continuous sections can stay in the body. One status badge goes in
127
- `header.titleBadges`. `PageHeader` serves a custom shell; `PageActions` is
127
+ `header.slots.titleSuffix` as a composed Badge. Place supporting facts in
128
+ `header.slots.afterDescription` as a MetadataList when useful. The four header
129
+ slots (`beforeTitle`, `titleSuffix`, `afterDescription`, `actions`) accept content
130
+ or a callback receiving `{ renderLink, defaultContent }`; an actions callback
131
+ can wrap or return `defaultContent` to retain responsive commands. A replacement
132
+ owns its behavior. Below 40rem of actual header width, all commands, including
133
+ primary, share one More actions trigger. `PageHeader` serves a custom shell; `PageActions` is
128
134
  deprecated. The [layout recipe](../examples/app-layout.tsx) demonstrates that
129
135
  route boundary; the [detail recipe](../examples/detail.tsx) demonstrates the
130
136
  record route and an independently framed subject with MetadataList and value
@@ -184,6 +184,25 @@ export function analyzeComposition(text, { filename, index, ts }) {
184
184
  if (ts.isJsxElement(element.parent)) element.parent.children.forEach(collect);
185
185
  return found;
186
186
  };
187
+ // Static region children only: inspect known passive wrappers and providers,
188
+ // never assume an opaque app component renders its lexical children.
189
+ const regionElements = (opening, deep = false) => {
190
+ const found = [];
191
+ const collect = (child, scope = opening, subject) => {
192
+ if (ts.isJsxFragment(child)) { child.children.forEach(item => collect(item, scope, subject)); return; }
193
+ if (ts.isJsxExpression(child)) { const value = resolve(child.expression); if (value) collect(value, scope, subject); return; }
194
+ const node = ts.isJsxElement(child) ? child.openingElement : ts.isJsxSelfClosingElement(child) ? child : undefined;
195
+ if (!node) return;
196
+ const owner = ownerFor(node.tagName);
197
+ found.push({node, owner, tag: node.tagName.getText(source), scope, subject});
198
+ const provider = isOwner(owner, 'features/filters', ['FilterProvider']);
199
+ const card = isOwner(owner, 'base/cards', ['Card', 'SmartCard']);
200
+ // Cards delimit independent subjects; providers delimit query state.
201
+ if (deep && ts.isJsxElement(child) && (/^(div|section|form)$/.test(node.tagName.getText(source)) || isOwner(owner, 'base/structure', ['Stack', 'Grid', 'AdaptiveGrid', 'GridCell']) || card || provider)) child.children.forEach(item => collect(item, provider ? node : scope, card ? node : subject));
202
+ };
203
+ if (ts.isJsxElement(opening.parent)) opening.parent.children.forEach(child => collect(child));
204
+ return found;
205
+ };
187
206
  const renderedText = element => ts.isJsxElement(element) ? element.children.map(child => ts.isJsxText(child) ? child.text : ts.isJsxExpression(child) ? literal(child.expression) ?? '' : '').join(' ').trim() : '';
188
207
  const slotBadge = expression => {
189
208
  const node = resolve(expression);
@@ -342,7 +361,7 @@ export function analyzeComposition(text, { filename, index, ts }) {
342
361
  if (modalRole || scrim) add(node, 'raw-overlay-primitive', 'warning', `A hand-rolled ${modalRole ? 'dialog' : 'full-screen layer'} must own focus, dismissal, pending state and theming itself. ${overlayRemedy}`);
343
362
  if (/^(?:input|textarea)$/.test(node.tagName.getText(source))) {
344
363
  const props = new Map(attributes(node));
345
- if (props.has('className') && classStrings(props.get('className')).flatMap(value=>value.split(/\s+/)).some(value=>replacesSharedRadius(value,'inner'))) add(node,'shared-radius','error','Native editable controls follow bounded rounded-inner; change the global theme root, not per-field corners.');
364
+ if (props.has('className') && classStrings(props.get('className')).flatMap(value=>value.split(/\s+/)).some(value=>replacesSharedRadius(value,'radius'))) add(node,'shared-radius','error','Native editable controls follow bounded rounded-radius; change the global theme root, not per-field corners.');
346
365
  if (props.has('style') && properties(props.get('style')).some(([name])=>radiusProperty.test(name))) add(node,'shared-radius','error','Native editable corners belong to the global radius root, not inline styles.');
347
366
  }
348
367
  if (nativeDisplayNames.test(node.tagName.getText(source))) {
@@ -375,7 +394,7 @@ export function analyzeComposition(text, { filename, index, ts }) {
375
394
  const focusDrift = focusRingDrift(tokens);
376
395
  if ((anatomy || forwarder && name === 'contentClassName') && classes.some(value => paddingClass.test(value))) add(node, 'surface-padding', 'error', `${owner.symbol}: use inset/type/density and public regions; padding overrides replace owned anatomy.`);
377
396
  if (control && name === 'className' && classes.some(value => fixedControlClass.test(value))) add(node, 'control-anatomy', 'error', `${owner.symbol}: use its size/variant props; preserve shared control height, inset, corners and typography.`);
378
- if ((name === 'className' && (control || sharedCorners) || name === 'contentClassName' && forwarder) && classes.some(value=>replacesSharedRadius(value, control && name === 'className' ? 'inner' : cornerRole(family,owner.symbol)))) add(node,'shared-radius','error',`${owner.symbol}: outer shells use rounded-radius; controls and collection rows use bounded rounded-inner. Preserve the owning role and joined-edge resets.`);
397
+ if ((name === 'className' && (control || sharedCorners) || name === 'contentClassName' && forwarder) && classes.some(value=>replacesSharedRadius(value, control && name === 'className' ? 'radius' : cornerRole(family,owner.symbol)))) add(node,'shared-radius','error',`${owner.symbol}: outer shells use rounded-surface; controls and collection rows use bounded rounded-radius. Preserve the owning role and joined-edge resets.`);
379
398
  if (display && classes.some(addsCopyMargin)) add(node,'copy-margin','error',`${owner.symbol}: typography is margin-free; use textGap on the headed owner or gap on Stack/Grid.`);
380
399
  if (textContent && name === 'className' && tokens.some(token=>fadedText(token)||textOpacityDrift(token))) add(node,'faded-text','error',`${owner.symbol}: use its semantic type instead of adding an opacity tier to the text. Presence endpoints and shared disabled state remain valid.`);
381
400
  if ((control || family === 'buttons' && owner.symbol === 'TextButton') && name === 'className' && focusDrift.length) add(node, 'focus-ring-recipe', 'error', `${owner.symbol}: keep the shared focus indicator (a ring-3 halo of ring-ring/50 around a full-strength ring edge); ${[...new Set(focusDrift)].join(', ')} redraws it.`);
@@ -404,6 +423,23 @@ export function analyzeComposition(text, { filename, index, ts }) {
404
423
  }
405
424
  if (isOwner(owner, 'base/feedback', ['Empty'])) add(node, 'reinvented-empty-state', 'warning', 'Empty exposes lower-level anatomy. Compare EmptyState from blocks/data-display for a routine no-data/no-results region; keep Empty when custom anatomy is required.');
406
425
  if (isOwner(owner, 'base/sheet', ['Sheet', 'SheetContent']) || isOwner(owner, 'base/display', ['Dialog', 'DialogContent', 'AlertDialog', 'AlertDialogContent'])) add(node, 'raw-overlay-primitive', 'warning', 'This lower-level modal needs application lifecycle coordination. Prefer ActionDialog, ConfirmDialog or ActionDrawer from features/overlays when their workflow fits.');
426
+ if (isOwner(owner, 'features/table', ['DataTable'])) {
427
+ const parent = parents.at(-1);
428
+ let ancestor = ts.isJsxOpeningElement(node) ? node.parent.parent : node.parent;
429
+ while (ancestor && !ts.isJsxElement(ancestor)) ancestor = ancestor.parent;
430
+ const directOwner = ancestor && ownerFor(ancestor.openingElement.tagName);
431
+ if (isOwner(directOwner, 'base/cards', ['Card', 'SmartCard']) && isOwner(parent?.owner, 'base/cards', ['Card', 'SmartCard']) && literal(parent.props.get('padding')) !== 'none' && !parent.unknownProps) add(node, 'uncontained-table-in-card', 'warning', 'DataTable owns its surface, topbar and edge-to-edge header band. Render it directly in Page or a layout region; a deliberate Card host must use padding="none".');
432
+ }
433
+ const routeOwner = isOwner(owner, 'layout/page', ['Page']) || isOwner(owner, 'layout', ['Page']);
434
+ if (routeOwner) {
435
+ const children = regionElements(node);
436
+ const contents = regionElements(node, true);
437
+ const tables = contents.filter(child => isOwner(child.owner, 'features/table', ['DataTable']));
438
+ for (const child of contents) if (isOwner(child.owner, 'features/filters', ['FilterLayout']) && tables.some(table => table.scope === child.scope && (!child.subject || table.subject === child.subject))) add(child.node, 'detached-filter-layout', 'warning', 'Pass FilterLayout into DataTable topbarContent with variant="compact", or use DataView so filters and table chrome share one surface.');
439
+ const hasAsideSlot = properties(props.get('slots'), false).some(([name]) => name === 'aside');
440
+ const onlyForms = children.length > 0 && children.every(child => child.tag === 'form' || isOwner(child.owner, 'base/forms', ['FormSection']));
441
+ if (onlyForms && !unknownProps && !['narrow', 'maxWidth', 'aside'].some(name => props.has(name)) && !hasAsideSlot) add(node, 'wide-single-column-form', 'warning', 'A single-column form should use Page narrow (48rem centered), an explicit maxWidth, or a supporting aside so fields do not stretch across the default route measure.');
442
+ }
407
443
  if (owner.symbol === 'Page' && literal(props.get('gutter')) === 'none' && !parents.some(parent => parent.framed)) add(node, 'page-gutter', 'warning', 'Page gutter="none" delegates inline padding to a real parent. PageViewport owns scrolling, not padding; verify the parent inset.');
408
444
  if (owner.symbol === 'Card' && framed && literal(props.get('padding')) === 'sm' && !parents.some(parent => parent.framed)) add(node, 'compact-card', 'warning', 'Standalone Card padding="sm" halves the normal inset. Omit padding for ordinary subjects; retain sm only for an intentional compact composition.');
409
445
  const peers = parents.findLast(parent => parent.structure);
@@ -432,7 +468,7 @@ export function analyzeComposition(text, { filename, index, ts }) {
432
468
  const title = literal(props.get('title')) ?? literal(new Map(header).get('title'));
433
469
  if (title && parents.some(parent => parent.title === title)) add(node, 'duplicate-heading', 'warning', `The heading "${title}" repeats its ancestor. Keep one route/subject title unless a distinct task warrants repetition.`);
434
470
  const structure = owner.import === `${index.package}/base/structure` && Object.hasOwn(structureDefaults, owner.symbol);
435
- return { framed, title, structure, cards: structure ? peerCards(node) : new Set(), smallGap: structure ? smallGap(props, owner.symbol, unknownProps) : false };
471
+ return { owner, props, unknownProps, framed, title, structure, cards: structure ? peerCards(node) : new Set(), smallGap: structure ? smallGap(props, owner.symbol, unknownProps) : false };
436
472
  }
437
473
  visit(source);
438
474
  return { findings, unresolved };
@@ -4,7 +4,7 @@ const name = value => typeof value === 'string' && /^[A-Za-z][A-Za-z0-9]*$/.test
4
4
  const uniqueList = (value, valid, maximum) => Array.isArray(value) && value.length <= maximum && value.every(valid) && new Set(value).size === value.length;
5
5
  const scalar = value => value === null || typeof value === 'boolean' || (typeof value === 'string' && value.length <= 500) || (typeof value === 'number' && Number.isFinite(value));
6
6
  const kinds = ['shell', 'page', 'layout', 'surface', 'content', 'workflow'];
7
- const placements = ['region', 'cell', 'overlay'];
7
+ const placements = ['region', 'toolbar', 'cell', 'overlay'];
8
8
  const surfaces = ['none', 'parent', 'self', 'variant'];
9
9
 
10
10
  /** One literal metadata shape shared by authored catalogue and copied tools. */
@@ -8,6 +8,19 @@ const unique = values => [...new Set(values)].sort(compare);
8
8
  const patterns = ['record-detail', 'collection', 'edit', 'analytics', 'embedded'];
9
9
  const roleKey = value => value.trim().toLowerCase().replace(/\s+/g, '-');
10
10
  const identity = mapping => `${mapping.import}#${mapping.symbol}`;
11
+ const stopWords = new Set(['and', 'for', 'the', 'with', 'from', 'into', 'show', 'list', 'page']);
12
+ const roleTokens = value => value.toLowerCase().split(/[^a-z0-9]+/).filter(token => token.length > 2 && !stopWords.has(token)).map(token => token.length > 3 && token.endsWith('s') ? token.slice(0, -1) : token);
13
+
14
+ /** Nearby mapped roles for a phrase that matched none. Hints only: the need stays unresolved. */
15
+ function suggestRoles(mappings, request, limit = 5) {
16
+ if (/^[A-Z][A-Za-z0-9]*$/.test(request) || /[\/#]/.test(request)) return [];
17
+ const wanted = new Set(roleTokens(request));
18
+ return unique(mappings.flatMap(mapping => mapping.roles))
19
+ .map(role => ({role, shared: roleTokens(role).filter(token => wanted.has(token)).length}))
20
+ .filter(candidate => candidate.shared > 0)
21
+ .sort((a, b) => b.shared - a.shared || a.role.length - b.role.length || compare(a.role, b.role))
22
+ .slice(0, limit).map(candidate => candidate.role);
23
+ }
11
24
 
12
25
  function publicProps(contract, contracts) {
13
26
  const inherited = contracts.nativeProfiles[contract.inheritedProfile] ?? {};
@@ -107,8 +120,10 @@ export function mapComposition(index, contracts, rawOptions) {
107
120
  }
108
121
  if (options.role !== undefined) {
109
122
  const found = matches(options.role);
123
+ const suggestions = found.length ? [] : suggestRoles(mappings, options.role);
110
124
  return {...base, mode: 'lookup', status: found.length ? 'mapped' : 'unresolved', query: options.role, total: found.length,
111
- results: found.slice(0, options.limit).map(candidate), omittedResults: Math.max(0, found.length - options.limit)};
125
+ results: found.slice(0, options.limit).map(candidate), omittedResults: Math.max(0, found.length - options.limit),
126
+ ...(suggestions.length ? {suggestedRoles: suggestions} : {})};
112
127
  }
113
128
 
114
129
  const unresolved = [], warnings = [], selected = [];
@@ -117,9 +132,14 @@ export function mapComposition(index, contracts, rawOptions) {
117
132
  return {import: mapping.import, symbol: mapping.symbol, kind: mapping.kind, surface: mapping.surface,
118
133
  surfaceDecision: decision, props: {...mapping.props}, slots: {}};
119
134
  };
120
- const issue = (request, region, reason, found = []) => unresolved.push({request, region, reason, totalCandidates: found.length, candidates: found.slice(0, options.limit).map(candidate)});
135
+ const issue = (request, region, reason, found = [], suggest = false) => {
136
+ // Only a phrase that matched no role gets hints; structural failures of a
137
+ // mapped role already name their candidates.
138
+ const suggestions = suggest ? suggestRoles(mappings, request) : [];
139
+ unresolved.push({request, region, reason, totalCandidates: found.length, candidates: found.slice(0, options.limit).map(candidate), ...(suggestions.length ? {suggestedRoles: suggestions} : {})});
140
+ };
121
141
  const resolve = (request, region, found = matches(request)) => {
122
- if (found.length !== 1) { issue(request, region, found.length ? 'Role is ambiguous; select an exact import#symbol or a more specific mapped role.' : 'No exact mapping covers this requirement. Use --roles or the component finder; do not infer an API.', found); return null; }
142
+ if (found.length !== 1) { issue(request, region, found.length ? 'Role is ambiguous; select an exact import#symbol or a more specific mapped role.' : 'No exact mapping covers this requirement. Retry with one of suggestedRoles when it names the same job, or use --roles or the component finder; do not infer an API.', found, !found.length); return null; }
123
143
  return found[0];
124
144
  };
125
145
  const insertion = (mapping, slot, request, region) => {
@@ -148,9 +168,15 @@ export function mapComposition(index, contracts, rawOptions) {
148
168
  } else warnings.push({code: 'host-ownership-unverified', message: 'Embedded requirements retain the caller\'s host. Confirm its insertion slot, framing, padding and gaps; no Page or new surface was added.'});
149
169
  } else {
150
170
  rootMapping = structure('route', 'page', ['children']);
151
- if (rootMapping) root = host = node(rootMapping);
171
+ if (rootMapping) {
172
+ root = host = node(rootMapping);
173
+ if (!options.aside.length && ['edit', 'record-detail'].includes(options.pattern)) {
174
+ const contract = contracts.contracts.find(contract => identity(contract) === identity(rootMapping));
175
+ if (publicProps(contract, contracts).narrow) root.props.narrow = true;
176
+ }
177
+ }
152
178
  mainSlot = 'children';
153
- if (rootMapping && options.aside.length) {
179
+ if (rootMapping && options.aside.length && !(rootMapping.slots ?? []).includes('aside')) {
154
180
  const layout = structure('main-support-layout', 'layout', ['main', 'aside']);
155
181
  if (layout && insertion(rootMapping, 'children', 'main-support-layout', 'structure')) {
156
182
  host = node(layout); root.slots.children = [host]; rootMapping = layout; mainSlot = 'main';
@@ -160,9 +186,11 @@ export function mapComposition(index, contracts, rawOptions) {
160
186
 
161
187
  let resolved = 0;
162
188
  const regions = {main: [], aside: []};
189
+ const toolbars = [], placements = [], contextualized = new Set();
163
190
  for (const region of ['main', 'aside']) for (const request of options[region]) {
164
191
  const mapping = resolve(request, region);
165
192
  if (!mapping) continue;
193
+ if (mapping.placement === 'toolbar') { toolbars.push({mapping, request, region}); continue; }
166
194
  if (mapping.placement !== 'region') { issue(request, region, mapping.placement === 'cell' ? 'This owner belongs in a table/collection renderer, not directly in a subject region.' : 'This owner belongs at the app/feature overlay boundary; open it through callbacks.', [mapping]); continue; }
167
195
  const slot = region === 'main' ? mainSlot : asideSlot;
168
196
  if (['page', 'shell'].includes(mapping.kind) && !(options.pattern === 'embedded' && rootMapping?.routeSlot === slot && mapping.kind === 'page')) { issue(request, region, 'A page or shell is not an independent content subject. Only an explicit route-host slot may receive a Page; keep one route title/gutter owner.', [mapping]); continue; }
@@ -182,10 +210,38 @@ export function mapComposition(index, contracts, rawOptions) {
182
210
  if (mapping.surface === 'parent' && options.pattern === 'embedded' && host && !insideSurface) warnings.push({code: 'host-surface-unverified', request, region, message: 'The explicit host does not establish a known surface boundary. Confirm its ancestors before adding framing; no extra Card was added.'});
183
211
  if (insideSurface && mapping.surface === 'self') warnings.push({code: 'nested-surface-review', request, region, message: `${mapping.symbol} already owns a surface. Review its supported embedded variant or use an unframed host; no props were guessed.`});
184
212
  else if (rootMapping?.surface === 'variant' && mapping.surface === 'self') warnings.push({code: 'nested-surface-review', request, region, message: `${mapping.symbol} owns a surface and its host may also frame it. Inspect both variants before keeping nested surfaces; no embedded props were guessed.`});
213
+ placements.push({mapping, child, region, slot});
185
214
  regions[region].push(child);
186
215
  if (host) (host.slots[slot] ??= []).push(child);
187
216
  resolved++;
188
217
  }
218
+ // Toolbars are placed after subjects so request order cannot affect the tree.
219
+ // FilterLayout's context and the table seam are both authored, validated APIs.
220
+ for (const {mapping, request, region} of toolbars) {
221
+ const targets = placements.filter(item => item.region === region && item.mapping.roles.includes('comparison-table') && (item.mapping.slots ?? []).includes('topbarContent'));
222
+ if (targets.length !== 1) { issue(request, region, 'A toolbar needs exactly one mapped comparison table with a topbarContent slot in this region.', targets.map(item => item.mapping)); continue; }
223
+ const target = targets[0];
224
+ let provider;
225
+ if (mapping.symbol === 'FilterLayout') {
226
+ const contexts = mappings.filter(row => row.import === mapping.import && row.symbol === 'FilterProvider' && row.roles.includes('filter-context'));
227
+ if (contexts.length !== 1 || contexts[0].kind !== 'layout' || contexts[0].placement !== 'region' || !(contexts[0].slots ?? []).includes('children')) {
228
+ issue(request, region, 'FilterLayout needs its mapped FilterProvider context with a children insertion contract.', contexts); continue;
229
+ }
230
+ provider = contexts[0];
231
+ }
232
+ const toolbar = node(mapping, 'uses-parent');
233
+ (target.child.slots.topbarContent ??= []).push(toolbar);
234
+ if (provider && !contextualized.has(target.child)) {
235
+ contextualized.add(target.child);
236
+ const wrapped = node(provider, 'context-provider');
237
+ wrapped.slots.children = [target.child];
238
+ const replace = children => { const position = children.indexOf(target.child); if (position >= 0) children[position] = wrapped; };
239
+ replace(regions[region]);
240
+ if (host) replace(host.slots[target.slot] ?? []);
241
+ // Keep subsequent toolbar insertion addressed to the table, not its context.
242
+ }
243
+ resolved++;
244
+ }
189
245
  const requested = options.main.length + options.aside.length;
190
246
  const selectedMappings = [...new Map(selected.map(mapping => [identity(mapping), mapping])).values()];
191
247
  if (options.pattern === 'edit') warnings.push({code: 'native-form-required', message: 'Provide the application-owned native form, validation and awaited submit/save/discard lifecycle. This structural plan does not create form state or transport.'});
@@ -160,8 +160,62 @@ function rankComponents(index, { query, layer = null, limit = 5 }, partial = fal
160
160
  return { package: index.package, version: index.version, query, layer, limit, total: ranked.length, results: ranked.slice(0, limit) };
161
161
  }
162
162
 
163
+ const singularPlural = segment => segment.endsWith('ies') ? [segment.slice(0, -3) + 'y'] : segment.endsWith('s') ? [segment.slice(0, -1)] : [segment + 's', ...(segment.endsWith('y') ? [segment.slice(0, -1) + 'ies'] : [])];
164
+
165
+ // An unpublished import path resolves only through deterministic evidence: the
166
+ // missing package prefix, a catalogue path alias, the family's singular/plural
167
+ // spelling, or a trailing segment that names one exported component. Anything
168
+ // else keeps the precise empty result instead of a guessed owner.
169
+ export function resolveImport(index, query) {
170
+ const requested = query.trim();
171
+ if (!/^[\w@.-]+(?:\/[\w.-]+)+$/.test(requested) || index.families.some(family => family.import === requested)) return null;
172
+ const path = requested.startsWith(`${index.package}/`) ? requested.slice(index.package.length + 1) : requested;
173
+ const byPath = new Map(index.families.map(family => [family.import === index.package ? '' : family.import.slice(index.package.length + 1), family]));
174
+ const segments = path.split('/');
175
+ const key = symbolKey(segments.at(-1));
176
+ const named = family => family.symbols.find(symbol => /^[A-Z]/.test(symbol) && symbolKey(symbol) === key && !Object.hasOwn(family.deprecated ?? {}, symbol));
177
+ const only = (families, reason, symbol) => families.length === 1 ? { requested, import: families[0].import, reason, ...(symbol ? { symbol } : {}) } : null;
178
+ if (byPath.has(path)) return only([byPath.get(path)], 'Public imports include the package name.');
179
+ const aliased = index.families.filter(family => family.aliases.includes(path));
180
+ if (aliased.length) {
181
+ // A route alias ("blocks/commerce/invoice-table") names its documented owner.
182
+ const routeSymbols = aliased.length === 1 ? [...new Set(aliased[0].guidance.filter(guide => guide.aliases.includes(path)).flatMap(guide => guideSymbols(guide, aliased[0])))] : [];
183
+ return only(aliased, 'Catalogue path alias for this family.', aliased.length === 1 ? named(aliased[0]) ?? (routeSymbols.length === 1 ? routeSymbols[0] : undefined) : undefined);
184
+ }
185
+ const parent = segments.slice(0, -1).join('/');
186
+ const spelled = singularPlural(segments.at(-1)).map(segment => byPath.get([...segments.slice(0, -1), segment].join('/'))).filter(Boolean);
187
+ if (spelled.length) return only(spelled, 'Family names keep their published singular or plural spelling.', spelled.length === 1 ? named(spelled[0]) : undefined);
188
+ // "blocks/commerce/order-header" names OrderHeader in blocks/commerce;
189
+ // "features/data-table" names DataTable, exported by one features family.
190
+ // An aggregate such as `layout` re-exports its sub-families, so the most
191
+ // specific exporting family answers, then the one that documents the
192
+ // component. Several equal owners keep ordinary search instead of a guess.
193
+ const within = prefix => index.families.filter(family => family.import === `${index.package}/${prefix}` || family.import.startsWith(`${index.package}/${prefix}/`)).filter(named);
194
+ const exporting = byPath.has(parent) && within(parent).length ? within(parent) : within(segments[0]);
195
+ const owners = exporting.filter(family => !exporting.some(other => other !== family && other.import.startsWith(`${family.import}/`)));
196
+ const documented = owners.filter(family => family.guidance.some(guide => guideSymbols(guide, family).includes(named(family))));
197
+ const chosen = owners.length === 1 ? owners : documented.length === 1 ? documented : [];
198
+ if (chosen.length) return only(chosen, 'The trailing path segment names a component exported by this family.', named(chosen[0]));
199
+ // A deprecated re-export ("blocks/data-display/metadata-value") points to
200
+ // the one family that owns the current export, whatever its layer.
201
+ const parentFamily = byPath.get(parent);
202
+ const retired = parentFamily?.symbols.find(symbol => symbolKey(symbol) === key && Object.hasOwn(parentFamily.deprecated ?? {}, symbol));
203
+ if (!retired) return null;
204
+ const current = index.families.filter(family => family.import !== index.package && named(family) === retired);
205
+ const currentOwners = current.filter(family => !current.some(other => other !== family && other.import.startsWith(`${family.import}/`)));
206
+ return only(currentOwners, `${retired} is deprecated in ${parentFamily.import}; this family owns the current export.`, retired);
207
+ }
208
+
163
209
  export function findComponents(index, options) {
164
210
  validateIndex(index);
211
+ const resolution = resolveImport(index, options.query);
212
+ if (resolution) {
213
+ // Answer with the canonical owner, keeping the request visible.
214
+ // Rank every owner before bounding the output, so `limit` cannot drop it.
215
+ const canonical = rankComponents(index, { ...options, query: resolution.symbol ?? resolution.import, limit: 100 });
216
+ const results = canonical.results.filter(result => result.import === resolution.import);
217
+ if (results.length) return { ...canonical, query: options.query, limit: options.limit ?? 5, total: results.length, results: results.slice(0, options.limit ?? 5), importResolution: resolution };
218
+ }
165
219
  const result = rankComponents(index, options);
166
220
  // Only natural-language requests recover automatically. API-like names and
167
221
  // explicit paths keep precise failure semantics rather than guessed owners.
@@ -6,6 +6,6 @@ try {
6
6
  const result = inspectComponent(read('component-contracts.json'), parseComponentArguments(process.argv.slice(2)), read('public-api.json'));
7
7
  process.stdout.write(JSON.stringify(result, null, 2) + '\n');
8
8
  } catch (error) {
9
- process.stderr.write(`inspect-component: ${error.message}\nUsage: node inspect-component.mjs <ExactSymbol> [--import exact-public-import] [--prop exact-name] [--limit 1..100]\n`);
9
+ process.stderr.write(`inspect-component: ${error.message}\nUsage: node inspect-component.mjs <ExactSymbol> [--import exact-public-import] [--prop name[,name]]... [--limit 1..100]\n`);
10
10
  process.exitCode = 1;
11
11
  }
@@ -5,7 +5,7 @@ const compare = (a, b) => a < b ? -1 : a > b ? 1 : 0;
5
5
  const boundedText = (value, limit) => ({text: value.slice(0, limit), complete: value.length <= limit, omittedCharacters: Math.max(0, value.length - limit)});
6
6
 
7
7
  function parse(args, token = false) {
8
- const options = token ? {query: '', limit: 12} : {symbol: '', publicImport: null, prop: null, limit: 12};
8
+ const options = token ? {query: '', limit: 12} : {symbol: '', publicImport: null, props: [], limit: 12};
9
9
  const seen = new Set();
10
10
  const allowed = token ? ['--limit'] : ['--import', '--prop', '--limit'];
11
11
  for (let index = 0; index < args.length; index++) {
@@ -17,15 +17,21 @@ function parse(args, token = false) {
17
17
  options[key] = arg;
18
18
  continue;
19
19
  }
20
- requireFormat(!seen.has(arg), `Duplicate argument: ${arg}`); seen.add(arg);
20
+ // Several props may be read at once: repeat --prop or separate names with commas.
21
+ requireFormat(arg === '--prop' || !seen.has(arg), `Duplicate argument: ${arg}`); seen.add(arg);
21
22
  const value = args[++index];
22
23
  requireFormat(text(value) && !value.startsWith('--'), `Missing value for ${arg}`);
23
24
  if (arg === '--limit') {
24
25
  requireFormat(/^[1-9]\d*$/.test(value) && Number(value) <= 100, 'Invalid limit: use 1..100'); options.limit = Number(value);
25
- } else options[arg === '--import' ? 'publicImport' : 'prop'] = value;
26
+ } else if (arg === '--prop') {
27
+ const names = value.split(',').map(name => name.trim());
28
+ requireFormat(names.every(text), `Missing value for ${arg}`);
29
+ for (const name of names) if (!options.props.includes(name)) options.props.push(name);
30
+ } else options.publicImport = value;
26
31
  }
27
32
  requireFormat(text(options[token ? 'query' : 'symbol']), `A nonempty ${token ? 'token query' : 'exact symbol'} is required`);
28
- return options;
33
+ // `prop` keeps the single-prop field earlier programmatic callers read.
34
+ return token ? options : {symbol: options.symbol, publicImport: options.publicImport, prop: options.props[0] ?? null, props: options.props, limit: options.limit};
29
35
  }
30
36
  export const parseComponentArguments = args => parse(args);
31
37
  export const parseTokenArguments = args => parse(args, true);
@@ -82,29 +88,78 @@ function displayGuidance(guide) {
82
88
  const list = value => (Array.isArray(value) ? value : []).slice(0, 3).map(value => boundedText(value, 600));
83
89
  const composition = guide.composition;
84
90
  return {title: boundedText(guide.title ?? '', 150), chooseWhen: list(guide.chooseWhen), avoidWhen: list(guide.avoidWhen),
91
+ alternatives: list(guide.alternatives), omittedAlternatives: Math.max(0, (guide.alternatives?.length ?? 0) - 3),
85
92
  ...(composition ? {composition: {owns: list(composition.owns), parentOwns: list(composition.parentOwns),
86
93
  ...(composition.page ? {page: boundedText(composition.page, 600)} : {}),
87
94
  ...(composition.embedded ? {embedded: boundedText(composition.embedded, 600)} : {})}} : {})};
88
95
  }
89
96
 
90
97
  /** Exact API inspection is deliberately separate from task/intent discovery. */
91
- export function inspectComponent(index, {symbol, publicImport = null, prop = null, limit = 12}, publicIndex) {
98
+ export function inspectComponent(index, {symbol, publicImport = null, prop = null, props = [], limit = 12}, publicIndex) {
92
99
  validateComponentContracts(index, publicIndex);
93
100
  requireFormat(text(symbol) && Number.isInteger(limit) && limit >= 1 && limit <= 100, 'Use an exact symbol and limit 1..100');
94
- const matches = index.contracts.filter(contract => contract.symbol === symbol && (!publicImport || contract.import === publicImport));
95
- requireFormat(matches.length > 0, `No contract for ${symbol}${publicImport ? ` from ${publicImport}` : ''}; use the finder and read the matching published declaration. Coverage: ${index.coverage ?? 'published uppercase callable exports'}`);
96
- requireFormat(matches.length === 1, `Ambiguous symbol ${symbol}; qualify with --import: ${matches.map(contract => contract.import).join(', ')}`);
101
+ const requestedProps = [...new Set([...(prop ? [prop] : []), ...props])];
102
+ const owners = index.contracts.filter(contract => contract.symbol === symbol);
103
+ // Hooks, helpers and types are public too, but have no component contract:
104
+ // name the declaration that holds their signature instead of a generic miss.
105
+ const exporters = owners.length ? [] : (publicIndex?.families ?? []).filter(family => family.symbols.includes(symbol) && (!publicImport || family.import === publicImport));
106
+ requireFormat(!exporters.length, `${symbol} is a public ${/^use[A-Z]/.test(symbol) ? 'hook' : 'export'} without a component contract. Read its signature in ${exporters.slice(0, 3).map(family => `${family.declaration} (import from ${family.import})`).join('; ')}${exporters.length > 3 ? `; ${exporters.length - 3} more` : ''}.`);
107
+ const matches = owners.filter(contract => !publicImport || contract.import === publicImport);
108
+ // An exact symbol can recover from a guessed path without fuzzy matching or
109
+ // silently selecting another owner. Bound both recovery and ambiguity output.
110
+ const imports = owners.map(contract => contract.import).sort(compare);
111
+ const ownerChoices = `${imports.slice(0, 3).join(', ')}. Omitted imports: ${Math.max(0, imports.length - 3)}.`;
112
+ requireFormat(matches.length > 0, `No contract for ${symbol}${publicImport ? ` from ${publicImport}` : ''}; ${owners.length ? `Available imports (--import): ${ownerChoices} ` : ''}Use the finder and read the matching published declaration. Coverage: ${index.coverage ?? 'published uppercase callable exports'}`);
113
+ requireFormat(matches.length === 1, `Ambiguous symbol ${symbol}; qualify with --import: ${ownerChoices}`);
97
114
  const contract = matches[0];
98
115
  const local = Object.values(contract.props);
99
116
  const inherited = contract.inheritedProfile ? Object.fromEntries(Object.entries(index.nativeProfiles[contract.inheritedProfile]).map(([name, reference]) => [name, typeof reference === 'string' ? index.nativeProperties[reference] : reference])) : {};
100
- const priority = value => Number(value.required) * 100 + Number(/^(?:value|defaultValue|checked|defaultChecked|open|defaultOpen|on[A-Z])/.test(value.name)) * 10;
101
- local.sort((a, b) => priority(b) - priority(a) || compare(a.name, b.name));
117
+ // Anatomy is the first decision when composing; callbacks remain exactly
118
+ // inspectable without displacing every insertion seam in a large API.
119
+ const anatomy = ['title', 'description', 'header', 'aside', 'toolbar', 'tabs', 'items', 'fields', 'filters', 'activeFilters', 'primaryAction', 'secondaryActions', 'topbarContent', 'topbarEnd', 'rowActions', 'pagination', 'footer', 'footerSlot', 'titleSuffix', 'headerAction', 'surface', 'padding', 'narrow', 'maxWidth', 'loading', 'error', 'empty', 'slots'];
120
+ const ownerAnatomy = {
121
+ Page: ['header', 'aside', 'toolbar', 'narrow', 'maxWidth', 'loading', 'error', 'empty', 'onRetry', 'slots', 'beforeHeader', 'afterHeader'],
122
+ Card: ['title', 'description', 'titleSuffix', 'headerAction', 'footerSlot', 'surface', 'padding', 'children', 'contentGap', 'headerPlacement', 'actions', 'slots'],
123
+ DataTable: ['columns', 'data', 'topbarContent', 'topbarEnd', 'rowActions', 'pagination', 'surface', 'emptyStateMessage', 'emptyStateAction', 'bulkActions', 'loading', 'getRowId'],
124
+ ActionDialog: ['title', 'description', 'children', 'formId', 'loading', 'strings', 'open', 'onOpenChange', 'onAsyncConfirm', 'alertMessage', 'initialFocusRef', 'footer'],
125
+ ActionDrawer: ['title', 'description', 'children', 'formId', 'loading', 'strings', 'open', 'onOpenChange', 'onAsyncConfirm', 'alertMessage', 'initialFocusRef', 'footer'],
126
+ ProductVariantsManager: ['optionGroups', 'variants', 'editingOptionId', 'defaultEditingOptionId', 'onEditingOptionIdChange', 'onSaveEditingOption', 'onCancelEditingOption', 'onError', 'renderValue', 'visibleColumns', 'selectionMode', 'onSelectVariant'],
127
+ MediaResourceGallery: ['items', 'accessors', 'primaryId', 'onPrimaryChange', 'onReorder', 'onRemove', 'onAdd', 'readOnly', 'renderItem', 'layout', 'density', 'maxItems'],
128
+ ProductOptionsMatrix: ['optionGroups', 'editingOptionId', 'defaultEditingOptionId', 'onEditingOptionIdChange', 'onSaveEditingOption', 'onCancelEditingOption', 'onError', 'renderValue', 'renderOptionActions', 'collapsedOptionIds', 'onCollapsedOptionIdsChange', 'onAddValue'],
129
+ ProductVariantEditor: ['value', 'defaultValue', 'onValueChange', 'optionFields', 'statusOptions', 'visibleFields', 'children', 'onSubmit', 'submitting', 'disabled', 'onError', 'onCancel', 'footerSlot', 'strings'],
130
+ ProductVariantDetails: ['variant', 'metadata', 'media', 'optionItems', 'renderMetadata', 'renderOption', 'renderActions', 'onEditVariant', 'onSelectVariant', 'onBack', 'footerSlot', 'empty'],
131
+ }[symbol] ?? [];
132
+ // Upstream control contracts share inherited profiles with HTML attributes.
133
+ // Keep cross-family state, mounting and render seams discoverable without
134
+ // treating every native DOM event as component anatomy.
135
+ const behavior = new Set(['children', 'render', 'value', 'defaultValue', 'checked', 'defaultChecked', 'open', 'defaultOpen', 'expanded', 'defaultExpanded', 'pressed', 'defaultPressed', 'onChange', 'onValueChange', 'onCheckedChange', 'onOpenChange', 'onExpandedChange', 'onPressedChange', 'multiple', 'keepMounted', 'hiddenUntilFound', 'disabled', 'orientation', 'loopFocus', 'modal']);
136
+ const hasProp = name => Object.hasOwn(contract.props, name) || Object.hasOwn(inherited, name);
137
+ const inheritedBehavior = value => behavior.has(value.name)
138
+ // React's HTMLAttributes includes these defaults even on div-like surfaces.
139
+ && (value.name !== 'defaultValue' || hasProp('value'))
140
+ && (value.name !== 'defaultChecked' || hasProp('checked'))
141
+ && (value.name !== 'onChange' || hasProp('value') || hasProp('checked'));
142
+ const priority = value => {
143
+ if (/(?:^className$|ClassName$|^style$|^(?:BottomSlot|TopSlot)$)/.test(value.name) || value.deprecated || /@deprecated|^Deprecated[.: ]/i.test(value.description)) return -100;
144
+ const position = ownerAnatomy.indexOf(value.name);
145
+ return Number(value.required) * 1000 + (position >= 0 ? 300 - position : anatomy.includes(value.name) ? 100 : /^(?:value|defaultValue|checked|defaultChecked|open|defaultOpen)$/.test(value.name) ? 50 : behavior.has(value.name) ? 40 : /^on[A-Z]/.test(value.name) ? 20 : 0);
146
+ };
147
+ const compareProps = (a, b) => priority(b) - priority(a) || compare(a.name, b.name);
148
+ local.sort(compareProps);
149
+ const inheritedValues = Object.values(inherited).sort((a, b) => Number(inheritedBehavior(b)) - Number(inheritedBehavior(a)) || compareProps(a, b));
102
150
  let selected;
103
- if (prop) {
104
- const value = contract.props[prop] ?? inherited[prop];
105
- requireFormat(value, `Unknown prop ${prop} on ${symbol} from ${contract.import}; inspect its declaration or omit --prop for the bounded prop index`);
106
- selected = [displayProp(value, Object.hasOwn(contract.props, prop) ? 'local' : 'inherited')];
107
- } else selected = local.slice(0, limit).map(value => displayProp(value, 'local'));
151
+ if (requestedProps.length) {
152
+ const unknown = requestedProps.filter(name => !(contract.props[name] ?? inherited[name]));
153
+ requireFormat(!unknown.length, `Unknown prop ${unknown.join(', ')} on ${symbol} from ${contract.import}; inspect its declaration or omit --prop for the bounded prop index`);
154
+ selected = requestedProps.map(name => displayProp(contract.props[name] ?? inherited[name], Object.hasOwn(contract.props, name) ? 'local' : 'inherited'));
155
+ } else {
156
+ const candidates = [
157
+ ...local.map(value => ({value, origin: 'local'})),
158
+ ...inheritedValues.filter(value => inheritedBehavior(value) && priority(value) >= 0 && !Object.hasOwn(contract.props, value.name)).map(value => ({value, origin: 'inherited'})),
159
+ ];
160
+ candidates.sort((a, b) => compareProps(a.value, b.value));
161
+ selected = candidates.slice(0, limit).map(({value, origin}) => displayProp(value, origin));
162
+ }
108
163
  const description = boundedText(contract.description ?? '', 1000);
109
164
  return {package: index.package, version: index.version, import: contract.import, symbol: contract.symbol, declaration: contract.declaration,
110
165
  ...(contract.optionalPeers ? {optionalPeers: contract.optionalPeers} : {}),
@@ -114,9 +169,9 @@ export function inspectComponent(index, {symbol, publicImport = null, prop = nul
114
169
  omissions: {localProps: local.length - selected.filter(value => value.origin === 'local').length,
115
170
  inheritedProps: Object.keys(inherited).length - selected.filter(value => value.origin === 'inherited').length,
116
171
  callSignatures: Math.max(0, contract.callSignatures.length - 4)},
117
- propNames: {local: local.map(value => value.name).slice(0, 100), omittedLocalNames: Math.max(0, local.length - 100), inherited: Object.keys(inherited).slice(0, 20), omittedInheritedNames: Math.max(0, Object.keys(inherited).length - 20)},
172
+ propNames: {local: local.map(value => value.name).slice(0, 100), omittedLocalNames: Math.max(0, local.length - 100), inherited: inheritedValues.slice(0, 20).map(value => value.name), omittedInheritedNames: Math.max(0, inheritedValues.length - 20)},
118
173
  ...(contract.guidance?.length ? {guidance: contract.guidance.slice(0, 1).map(displayGuidance), omittedGuidance: Math.max(0, contract.guidance.length - 1)} : {}),
119
- next: 'Use --prop <exact-name> for one local or inherited contract. Read declaration for full types, omitted overloads or truncated fields. Defaults marked unknown are not inferred.'};
174
+ next: 'Use --prop <exact-name> (repeat it, or separate names with commas) for local or inherited contracts. Read declaration for full types, omitted overloads or truncated fields. Defaults marked unknown are not inferred.'};
120
175
  }
121
176
 
122
177
  export function inspectTokens(index, {query, limit = 12}, publicIndex) {
@@ -129,7 +184,7 @@ export function inspectTokens(index, {query, limit = 12}, publicIndex) {
129
184
  ...index.structural.map(root => ({...root, default: {status: 'recorded', value: root.default}, kind: 'structural-root', overrideRoot: true})),
130
185
  {...index.radius, default: {status: 'recorded', value: index.radius.default}, kind: 'radius-root', overrideRoot: true},
131
186
  ...index.advanced.map(name => ({name, kind: 'advanced-root', overrideRoot: true, default: {status: 'not-recorded'}})),
132
- ...[...new Set(Object.values(index.utilities).flat())].map(name => ({name, kind: 'derived-utility', overrideRoot: false, default: {status: 'not-applicable'}, ...(name === index.radius.innerUtility ? {formula: index.radius.innerFormula} : {})})),
187
+ ...[...new Set(Object.values(index.utilities).flat())].map(name => ({name, kind: 'derived-utility', overrideRoot: false, default: {status: 'not-applicable'}, ...(name === index.radius.surfaceUtility ? {formula: index.radius.surfaceFormula} : {})})),
133
188
  ];
134
189
  const matches = values.filter(value => terms.every(term => words([value.name, value.purpose ?? ''].join(' ')).some(word => word === term))).sort((a, b) => Number(b.name === query) - Number(a.name === query) || Number(b.overrideRoot) - Number(a.overrideRoot) || compare(a.name, b.name));
135
190
  return {package: index.package, version: index.version, query, total: matches.length, results: matches.slice(0, limit), omitted: Math.max(0, matches.length - limit),
@@ -3,10 +3,11 @@ import fs from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import { fileURLToPath } from 'node:url';
5
5
 
6
- export const focusedSkills = ['themelia-components', 'themelia-tokenization', 'themelia-composition'];
6
+ export const focusedSkills = ['themelia-components', 'themelia-tokenization', 'themelia-composition', 'themelia-design'];
7
7
  export const sharedSkillName = 'themelia-react-ui-consumer';
8
+ export const agentFiles = ['themelia-ui-designer.md'];
8
9
 
9
- /** One metadata/example copy, three small discoverable entrypoints, and real templates. */
10
+ /** One metadata/example copy, four small discoverable entrypoints, and real templates. */
10
11
  export function consumerBundleFiles(packageRoot, destination) {
11
12
  const mappings = [
12
13
  ['docs/consumer-skill', sharedSkillName],
@@ -65,12 +66,46 @@ export function copyConsumerBundle({ packageRoot, destination }) {
65
66
  return checkCopiedConsumerBundle({ packageRoot, destination });
66
67
  }
67
68
 
69
+ /** Agent definitions are single files copied into an agents directory, such as `.claude/agents`. */
70
+ function agentBundleFiles(packageRoot, destination) {
71
+ return agentFiles.map(name => {
72
+ const source = path.resolve(packageRoot, 'docs/consumer-skill/agents', name);
73
+ return { source, target: path.resolve(destination, name), content: fs.readFileSync(source) };
74
+ });
75
+ }
76
+
77
+ export function checkCopiedAgents({ packageRoot, destination }) {
78
+ const files = agentBundleFiles(packageRoot, destination);
79
+ for (const { target, content } of files) {
80
+ assert(fs.existsSync(target) && !fs.lstatSync(target).isSymbolicLink(), `Copied agent is stale: missing or linked ${target}`);
81
+ assert(content.equals(fs.readFileSync(target)), `Copied agent is stale: copy it again (${target})`);
82
+ }
83
+ return { agents: files.map(file => path.basename(file.target, '.md')) };
84
+ }
85
+
86
+ export function copyAgents({ packageRoot, destination }) {
87
+ const files = agentBundleFiles(packageRoot, destination);
88
+ for (const { target } of files) assert(!fs.existsSync(target), `Destination exists: ${path.basename(target)}. Preserve or move the previous agent before copying.`);
89
+ fs.mkdirSync(destination, { recursive: true });
90
+ for (const { target, content } of files) fs.writeFileSync(target, content);
91
+ return checkCopiedAgents({ packageRoot, destination });
92
+ }
93
+
68
94
  if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
69
95
  try {
70
- const args = process.argv.slice(2);
71
- assert(args.length === 3 && ['--copy', '--check'].includes(args[0]) && args[1] === '--destination' && args[2], 'Usage: node setup-skills.mjs --copy|--check --destination <agent-skills-directory>');
96
+ const usage = 'Usage: node setup-skills.mjs --copy|--check [--destination <agent-skills-directory>] [--agents <agent-definitions-directory>]';
97
+ const [mode, ...rest] = process.argv.slice(2);
98
+ const options = {};
99
+ for (let index = 0; index < rest.length; index += 2) {
100
+ assert(['--destination', '--agents'].includes(rest[index]) && rest[index + 1] && !(rest[index] in options), usage);
101
+ options[rest[index]] = path.resolve(rest[index + 1]);
102
+ }
103
+ assert(['--copy', '--check'].includes(mode) && Object.keys(options).length, usage);
72
104
  const packageRoot = fileURLToPath(new URL('../../..', import.meta.url));
73
- const result = (args[0] === '--copy' ? copyConsumerBundle : checkCopiedConsumerBundle)({ packageRoot, destination: path.resolve(args[2]) });
74
- console.log(JSON.stringify({ ...result, destination: path.resolve(args[2]) }, null, 2));
105
+ const copy = mode === '--copy';
106
+ const result = {};
107
+ if (options['--destination']) Object.assign(result, (copy ? copyConsumerBundle : checkCopiedConsumerBundle)({ packageRoot, destination: options['--destination'] }), { destination: options['--destination'] });
108
+ if (options['--agents']) Object.assign(result, (copy ? copyAgents : checkCopiedAgents)({ packageRoot, destination: options['--agents'] }), { agentsDestination: options['--agents'] });
109
+ console.log(JSON.stringify(result, null, 2));
75
110
  } catch (error) { console.error(error.message); process.exitCode = 1; }
76
111
  }
@@ -31,11 +31,11 @@ export const marginProperty = /^margin(?:Inline|Block)?(?:Start|End)?$|^margin(?
31
31
  export const radiusProperty = /^border(?:(?:Top|Bottom)(?:Left|Right)|(?:Start|End)(?:Start|End))?Radius$|^border-(?:(?:top|bottom)-(?:left|right)-|(?:start|end)-(?:start|end)-)?radius$/;
32
32
 
33
33
  export function cornerRole(family, symbol) {
34
- if (family === 'item' && symbol === 'Item' || family === 'navigation' && /^(?:TabsList|TabsTrigger)$/.test(symbol)) return 'inner';
35
- return sharedCornerParts[family]?.test(symbol) && /(?:Item|SubTrigger)$/.test(symbol) ? 'inner' : 'radius';
34
+ if (family === 'item' && symbol === 'Item' || family === 'navigation' && /^(?:TabsList|TabsTrigger)$/.test(symbol)) return 'radius';
35
+ return sharedCornerParts[family]?.test(symbol) && /(?:Item|SubTrigger)$/.test(symbol) ? 'radius' : 'surface';
36
36
  }
37
37
 
38
- export function replacesSharedRadius(token, role = 'radius') {
38
+ export function replacesSharedRadius(token, role = 'surface') {
39
39
  const value = utility(token);
40
40
  if (new RegExp(`^rounded(?:-[trblse]{1,2})?-(?:${role}|none)$`).test(value)) return false;
41
41
  return /^rounded(?:-|$)/.test(value) || /^\[border-(?:(?:top|bottom)-(?:left|right)-|(?:start|end)-(?:start|end)-)?radius:/.test(value);
@@ -159,8 +159,8 @@ export function stateMotionDrift(classes) {
159
159
  return resting.filter(token => /^duration-(?!150$|0$)/.test(utility(token)) || /^ease-/.test(utility(token)));
160
160
  }
161
161
 
162
- // Corners take a role derived from --radius: rounded-radius (outer shells) and
163
- // rounded-inner (controls, rows, nested bodies), plus rounded-full, rounded-none
162
+ // Corners take a role derived from --radius: rounded-radius (controls, badges,
163
+ // rows, nested bodies) and rounded-surface (outer shells), plus rounded-full, rounded-none
164
164
  // and the root-derived rounded-radius-* steps. Bare rounded is a fixed 4px that
165
165
  // ignores the theme root, and the t-shirt steps are a second scale.
166
166
  export function radiusRoleDrift(token) {