themelia-react-ui 1.0.1 → 2.0.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 (640) hide show
  1. package/CHANGELOG.md +417 -0
  2. package/README.md +67 -16
  3. package/dist/{accordion-B-5Focs8.js → accordion-CQXVcd7Q.js} +15 -15
  4. package/dist/accordion-CQXVcd7Q.js.map +1 -0
  5. package/dist/action-menu-BTpyPWJ2.js +264 -0
  6. package/dist/action-menu-BTpyPWJ2.js.map +1 -0
  7. package/dist/affordance-geometry.generated-CCIXb2A1.js +68 -0
  8. package/dist/affordance-geometry.generated-CCIXb2A1.js.map +1 -0
  9. package/dist/{ai-artifact-B5tPpXnS.js → ai-artifact-2PQ2fItc.js} +206 -215
  10. package/dist/ai-artifact-2PQ2fItc.js.map +1 -0
  11. package/dist/{app-shell-at8Kt4l_.js → app-shell-BE_j8-AT.js} +11 -10
  12. package/dist/app-shell-BE_j8-AT.js.map +1 -0
  13. package/dist/{attachment-BO2R47wn.js → attachment-q69dlt5Y.js} +14 -14
  14. package/dist/attachment-q69dlt5Y.js.map +1 -0
  15. package/dist/auth-M3vGMgDR.js +723 -0
  16. package/dist/auth-M3vGMgDR.js.map +1 -0
  17. package/dist/{avatar-Bpzty83c.js → avatar-djdaaQ_s.js} +15 -13
  18. package/dist/avatar-djdaaQ_s.js.map +1 -0
  19. package/dist/{badge-BZXZT9wd.js → badge-0tATj-tW.js} +9 -9
  20. package/dist/badge-0tATj-tW.js.map +1 -0
  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/buttons.js +3 -2
  27. package/dist/base/cards.js +3 -3
  28. package/dist/base/carousel.js +33 -35
  29. package/dist/base/carousel.js.map +1 -1
  30. package/dist/base/cascader.js +246 -222
  31. package/dist/base/cascader.js.map +1 -1
  32. package/dist/base/chart.js +1 -1
  33. package/dist/base/choice-inputs.js +2 -2
  34. package/dist/base/code-block.js +1 -1
  35. package/dist/base/combobox.js +1 -1
  36. package/dist/base/command.js +1 -1
  37. package/dist/base/copyable.js +1 -1
  38. package/dist/base/date-pickers.js +3 -3
  39. package/dist/base/display.js +7 -7
  40. package/dist/base/feedback.js +4 -4
  41. package/dist/base/forms-numeric.js +1 -1
  42. package/dist/base/forms.js +2 -2
  43. package/dist/base/inline-edit.js +94 -92
  44. package/dist/base/inline-edit.js.map +1 -1
  45. package/dist/base/item.js +1 -1
  46. package/dist/base/media-gallery.js +1 -1
  47. package/dist/base/navigation.js +6 -6
  48. package/dist/base/popover-menu.js +1 -1
  49. package/dist/base/popover.js +1 -1
  50. package/dist/base/qr-code.js +7 -4
  51. package/dist/base/qr-code.js.map +1 -1
  52. package/dist/base/repeaters.js +53 -53
  53. package/dist/base/repeaters.js.map +1 -1
  54. package/dist/base/sheet.js +1 -1
  55. package/dist/base/sidebar.js +1 -1
  56. package/dist/base/signature-pad.js +84 -82
  57. package/dist/base/signature-pad.js.map +1 -1
  58. package/dist/base/skeleton.js +1 -1
  59. package/dist/base/spinner.js +1 -1
  60. package/dist/base/structure.js +1 -1
  61. package/dist/base/table.js +1 -1
  62. package/dist/base/text-inputs.js +2 -2
  63. package/dist/base/toaster.js +7 -7
  64. package/dist/base/toaster.js.map +1 -1
  65. package/dist/base/tree.js +1 -1
  66. package/dist/base/upload-queue.js +1 -1
  67. package/dist/base/upload.js +3 -3
  68. package/dist/base/value-inputs.js +1 -1
  69. package/dist/blocks/admin.js +228 -219
  70. package/dist/blocks/admin.js.map +1 -1
  71. package/dist/blocks/ai.js +182 -212
  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 +665 -620
  75. package/dist/blocks/analytics.js.map +1 -1
  76. package/dist/blocks/collaboration.js +2 -2
  77. package/dist/blocks/commerce/receipt.js +1 -1
  78. package/dist/blocks/commerce.js +1439 -1553
  79. package/dist/blocks/commerce.js.map +1 -1
  80. package/dist/blocks/data-display.js +1 -1
  81. package/dist/blocks/onboarding.js +6 -6
  82. package/dist/blocks/onboarding.js.map +1 -1
  83. package/dist/blocks/planning.js +88 -97
  84. package/dist/blocks/planning.js.map +1 -1
  85. package/dist/blocks/timelines.js +1 -1
  86. package/dist/{breadcrumbs-DdQ_I5ra.js → breadcrumbs-BG_FfMK4.js} +10 -9
  87. package/dist/breadcrumbs-BG_FfMK4.js.map +1 -0
  88. package/dist/button-BS3KDg00.js +674 -0
  89. package/dist/button-BS3KDg00.js.map +1 -0
  90. package/dist/{button-BS6l5JUw.js → button-R9CCoH7A.js} +3 -3
  91. package/dist/button-R9CCoH7A.js.map +1 -0
  92. package/dist/buttons-DS0ZIwJ_.js +99 -0
  93. package/dist/buttons-DS0ZIwJ_.js.map +1 -0
  94. package/dist/{cards-Cum56XOF.js → cards-QoJ4syG9.js} +12 -12
  95. package/dist/{cards-Cum56XOF.js.map → cards-QoJ4syG9.js.map} +1 -1
  96. package/dist/chart-DwYRKKc-.js +152 -0
  97. package/dist/chart-DwYRKKc-.js.map +1 -0
  98. package/dist/checkbox-B7ygNTYp.js +35 -0
  99. package/dist/checkbox-B7ygNTYp.js.map +1 -0
  100. package/dist/choice-inputs-DrJ17tpL.js +752 -0
  101. package/dist/choice-inputs-DrJ17tpL.js.map +1 -0
  102. package/dist/{code-block-xHKbAJDj.js → code-block-CD0B7M93.js} +27 -27
  103. package/dist/code-block-CD0B7M93.js.map +1 -0
  104. package/dist/{collapsible-DsnktFNv.js → collapsible-oMUGM4tr.js} +3 -3
  105. package/dist/{collapsible-DsnktFNv.js.map → collapsible-oMUGM4tr.js.map} +1 -1
  106. package/dist/{collection-content-C4fthJV_.js → collection-content-Ys-OZOkY.js} +3 -3
  107. package/dist/{collection-content-C4fthJV_.js.map → collection-content-Ys-OZOkY.js.map} +1 -1
  108. package/dist/combobox-6dvVny2q.js +272 -0
  109. package/dist/combobox-6dvVny2q.js.map +1 -0
  110. package/dist/combobox-CB7039j7.js +865 -0
  111. package/dist/combobox-CB7039j7.js.map +1 -0
  112. package/dist/{command-C0xe7u9B.js → command-kPMPjIxJ.js} +35 -31
  113. package/dist/command-kPMPjIxJ.js.map +1 -0
  114. package/dist/{comments-DviPLnwy.js → comments-You0Bzbr.js} +434 -409
  115. package/dist/comments-You0Bzbr.js.map +1 -0
  116. package/dist/components/base/accordion/accordion-variants.d.ts +3 -3
  117. package/dist/components/base/action-menu/action-menu.d.ts +7 -2
  118. package/dist/components/base/avatar/stacked-avatars.d.ts +5 -4
  119. package/dist/components/base/badge/badge.d.ts +1 -1
  120. package/dist/components/base/badge/badge.recipes.d.ts +4 -4
  121. package/dist/components/base/buttons/base-button.d.ts +7 -0
  122. package/dist/components/base/buttons/button-group.recipes.d.ts +2 -2
  123. package/dist/components/base/buttons/button.recipes.d.ts +10 -19
  124. package/dist/components/base/buttons/button.variants.d.ts +1 -0
  125. package/dist/components/base/buttons/loader-button.d.ts +3 -1
  126. package/dist/components/base/buttons/text-button.d.ts +2 -2
  127. package/dist/components/base/buttons/use-pending-button-focus.d.ts +3 -0
  128. package/dist/components/base/cards/card.recipes.d.ts +13 -5
  129. package/dist/components/base/cards/partials/card-primitives.d.ts +9 -4
  130. package/dist/components/base/cards/smart-card.types.d.ts +1 -1
  131. package/dist/components/base/carousel/carousel.d.ts +3 -1
  132. package/dist/components/base/chart/chart.d.ts +7 -7
  133. package/dist/components/base/choice-inputs/checkbox.d.ts +6 -7
  134. package/dist/components/base/choice-inputs/choice-types.d.ts +10 -1
  135. package/dist/components/base/choice-inputs/pill-radio-group.d.ts +9 -0
  136. package/dist/components/base/choice-inputs/select-popup.d.ts +9 -1
  137. package/dist/components/base/choice-inputs/switch-card.d.ts +1 -1
  138. package/dist/components/base/date-pickers/date-picker-field.d.ts +23 -0
  139. package/dist/components/base/date-pickers/date-picker.recipes.d.ts +5 -18
  140. package/dist/components/base/date-pickers/datepicker.types.d.ts +16 -3
  141. package/dist/components/base/display/alert-dialog.d.ts +8 -2
  142. package/dist/components/base/display/content-block.d.ts +4 -2
  143. package/dist/components/base/display/date-block.d.ts +1 -1
  144. package/dist/components/base/display/dialog.d.ts +12 -5
  145. package/dist/components/base/display/surface-title.d.ts +6 -1
  146. package/dist/components/base/feedback/alert.d.ts +1 -1
  147. package/dist/components/base/feedback/state-display.d.ts +5 -4
  148. package/dist/components/base/forms/form-field.d.ts +1 -0
  149. package/dist/components/base/forms/form-sizing.d.ts +35 -3
  150. package/dist/components/base/forms/workflow.d.ts +2 -0
  151. package/dist/components/base/forms-numeric/coordinates-input.d.ts +3 -0
  152. package/dist/components/base/forms-numeric/rounding-mode-select.d.ts +4 -1
  153. package/dist/components/base/navigation/dropdown-menu.d.ts +2 -2
  154. package/dist/components/base/navigation/language-switcher.d.ts +4 -1
  155. package/dist/components/base/navigation/overflow-tab-bar.d.ts +7 -0
  156. package/dist/components/base/navigation/page-header/page-header.d.ts +4 -1
  157. package/dist/components/base/navigation/tabs.d.ts +3 -5
  158. package/dist/components/base/popover/popover.recipes.d.ts +3 -3
  159. package/dist/components/base/qr-code/qr-code.d.ts +2 -0
  160. package/dist/components/base/sheet/index.d.ts +29 -1
  161. package/dist/components/base/sheet/sheet.d.ts +14 -5
  162. package/dist/components/base/spinner/spinner.d.ts +3 -2
  163. package/dist/components/base/structure/structure.types.d.ts +1 -1
  164. package/dist/components/base/table/index.d.ts +1 -1
  165. package/dist/components/base/text-inputs/clear-native-field.d.ts +2 -0
  166. package/dist/components/base/text-inputs/input-group.d.ts +2 -2
  167. package/dist/components/base/text-inputs/input-trailing-action.d.ts +3 -0
  168. package/dist/components/base/upload/upload.recipes.d.ts +11 -0
  169. package/dist/components/base/value-inputs/date-time-input.d.ts +4 -2
  170. package/dist/components/base/value-inputs/tags-input.d.ts +2 -0
  171. package/dist/components/blocks/admin/api-key-list/api-key-list.types.d.ts +1 -1
  172. package/dist/components/blocks/analytics/format-metric-value.d.ts +11 -1
  173. package/dist/components/blocks/analytics/index.d.ts +1 -1
  174. package/dist/components/blocks/analytics/types.d.ts +1 -1
  175. package/dist/components/blocks/analytics/use-metric-format-options.d.ts +3 -0
  176. package/dist/components/blocks/collaboration/message/message.recipes.d.ts +12 -0
  177. package/dist/components/blocks/commerce/address-card/address-card.d.ts +1 -1
  178. package/dist/components/blocks/commerce/address-card/types.d.ts +3 -1
  179. package/dist/components/blocks/commerce/booking-calendar-event-card/booking-calendar-event-card.d.ts +4 -0
  180. package/dist/components/blocks/commerce/booking-calendar-event-card/booking-calendar-event-card.types.d.ts +2 -2
  181. package/dist/components/blocks/commerce/discount-stack/types.d.ts +1 -1
  182. package/dist/components/blocks/commerce/inventory-section/types.d.ts +4 -1
  183. package/dist/components/blocks/commerce/loyalty-points/types.d.ts +1 -1
  184. package/dist/components/blocks/commerce/order-status/types.d.ts +4 -0
  185. package/dist/components/blocks/commerce/payment-method/payment-method.d.ts +1 -1
  186. package/dist/components/blocks/commerce/payment-method/types.d.ts +3 -1
  187. package/dist/components/blocks/commerce/seo-listing/seo-listing-card.d.ts +1 -1
  188. package/dist/components/blocks/commerce/seo-listing/types.d.ts +3 -1
  189. package/dist/components/blocks/commerce/vendor-profile/types.d.ts +4 -1
  190. package/dist/components/blocks/data-display/dense-info-card/dense-info.strings.d.ts +4 -1
  191. package/dist/components/blocks/data-display/empty-state/partials/illustrations/document-stack.d.ts +1 -4
  192. package/dist/components/blocks/data-display/empty-state/partials/illustrations/inbox-clean.d.ts +1 -4
  193. package/dist/components/blocks/data-display/empty-state/partials/illustrations/index.d.ts +3 -3
  194. package/dist/components/blocks/data-display/empty-state/partials/illustrations/stacked-cards.d.ts +1 -6
  195. package/dist/components/blocks/data-display/inline-stat/inline-stat.d.ts +4 -1
  196. package/dist/components/features/combobox/components/error-message.d.ts +1 -1
  197. package/dist/components/features/comments/hooks/use-attachment-upload.d.ts +5 -0
  198. package/dist/components/features/filters/facets/search-facet.d.ts +0 -12
  199. package/dist/components/features/filters/facets/tags-facet.d.ts +1 -1
  200. package/dist/components/features/global-search/partials/global-search-idle-state.d.ts +1 -1
  201. package/dist/components/features/media-library/media-library.d.ts +1 -1
  202. package/dist/components/features/media-library/media-library.types.d.ts +9 -1
  203. package/dist/components/features/media-library/partials/media-library-partials.d.ts +1 -1
  204. package/dist/components/features/overlays/alert-dialog.d.ts +1 -1
  205. package/dist/components/features/overlays/drawer.d.ts +1 -1
  206. package/dist/components/features/overlays/hooks/use-overlay-actions.d.ts +2 -2
  207. package/dist/components/features/overlays/hooks/use-overlay-visibility.d.ts +11 -4
  208. package/dist/components/features/overlays/hooks/use-pending-overlay-focus.d.ts +2 -0
  209. package/dist/components/features/overlays/overlay-tone-icon.d.ts +37 -0
  210. package/dist/components/features/overlays/overlays.types.d.ts +46 -22
  211. package/dist/components/features/products/components/product-variants-manager.d.ts +1 -1
  212. package/dist/components/features/products/partials/index.d.ts +1 -1
  213. package/dist/components/features/products/partials/product-metric-tile.d.ts +6 -0
  214. package/dist/components/features/products/partials/product-tone.d.ts +11 -1
  215. package/dist/components/features/products/products.types.d.ts +2 -1
  216. package/dist/components/features/rich-text-editor/rich-text-editor.recipes.d.ts +2 -1
  217. package/dist/components/features/table/data-table.d.ts +1 -1
  218. package/dist/components/features/table/partials/cell-renderers.d.ts +1 -1
  219. package/dist/components/features/table/table.types.d.ts +1 -1
  220. package/dist/components/features/theme-tweaker/theme-provider-settings.d.ts +3 -1
  221. package/dist/components/layout/auth/auth.types.d.ts +1 -0
  222. package/dist/components/layout/page/index.d.ts +1 -1
  223. package/dist/components/layout/page/page.d.ts +0 -1
  224. package/dist/components/layout/page/page.types.d.ts +62 -6
  225. package/dist/components/layout/page/partials/page-header-commands.d.ts +13 -0
  226. package/dist/components/layout/page/partials/page-header.d.ts +1 -1
  227. package/dist/components/layout/sidebar/sidebar.context.d.ts +1 -1
  228. package/dist/components/layout/sidebar/sidebar.types.d.ts +4 -0
  229. package/dist/components/layout/workspace/workspace.types.d.ts +3 -0
  230. package/dist/components/primitives/date.d.ts +16 -3
  231. package/dist/components/primitives/dimensions.d.ts +6 -0
  232. package/dist/components/primitives/duration.d.ts +6 -0
  233. package/dist/components/primitives/file-size.d.ts +6 -0
  234. package/dist/components/primitives/index.d.ts +1 -1
  235. package/dist/components/primitives/initials.d.ts +11 -2
  236. package/dist/components/primitives/link.d.ts +7 -0
  237. package/dist/components/primitives/money.d.ts +29 -7
  238. package/dist/components/primitives/number.d.ts +17 -0
  239. package/dist/components/primitives/value-typography.d.ts +54 -0
  240. package/dist/components/primitives/value.d.ts +31 -6
  241. package/dist/components/typography/heading.d.ts +13 -2
  242. package/dist/components/typography/index.d.ts +1 -1
  243. package/dist/components/typography/label.d.ts +3 -0
  244. package/dist/components/typography/running-text.d.ts +12 -0
  245. package/dist/components/typography/text-link.d.ts +23 -1
  246. package/dist/components/typography/text.d.ts +12 -0
  247. package/dist/components/typography/text.styles.d.ts +37 -5
  248. package/dist/components/ui/button.d.ts +1 -1
  249. package/dist/components/ui/checkbox.d.ts +5 -0
  250. package/dist/components/ui/overlay.recipes.d.ts +15 -2
  251. package/dist/components/ui/popover.d.ts +1 -1
  252. package/dist/components/ui/select.d.ts +3 -1
  253. package/dist/components/ui/sidebar.d.ts +1 -1
  254. package/dist/components/ui/switch.d.ts +5 -0
  255. package/dist/components/ui/table.d.ts +2 -1
  256. package/dist/container.recipes-D9t8MHR3.js +27 -0
  257. package/dist/container.recipes-D9t8MHR3.js.map +1 -0
  258. package/dist/{containers-6s-ucRQQ.js → containers-D_L4mTVb.js} +34 -57
  259. package/dist/containers-D_L4mTVb.js.map +1 -0
  260. package/dist/control-group-recipes-DBXmfh2E.js +21 -0
  261. package/dist/control-group-recipes-DBXmfh2E.js.map +1 -0
  262. package/dist/{copyable-B7SBXJMr.js → copyable-DDjTeW_j.js} +6 -6
  263. package/dist/{copyable-B7SBXJMr.js.map → copyable-DDjTeW_j.js.map} +1 -1
  264. package/dist/data-display-3xWQUnMA.js +1335 -0
  265. package/dist/data-display-3xWQUnMA.js.map +1 -0
  266. package/dist/date-picker-DrDFj8Lo.js +1527 -0
  267. package/dist/date-picker-DrDFj8Lo.js.map +1 -0
  268. package/dist/date-pickers-CnFCb0n2.js +174 -0
  269. package/dist/date-pickers-CnFCb0n2.js.map +1 -0
  270. package/dist/date-uf3z37Nm.js +144 -0
  271. package/dist/date-uf3z37Nm.js.map +1 -0
  272. package/dist/{display-CFM5myut.js → display-DrdvPwF0.js} +25 -25
  273. package/dist/display-DrdvPwF0.js.map +1 -0
  274. package/dist/{dropdown-menu-BlnyHr-9.js → dropdown-menu-CURh59jb.js} +22 -15
  275. package/dist/dropdown-menu-CURh59jb.js.map +1 -0
  276. package/dist/features/activities.js +177 -163
  277. package/dist/features/activities.js.map +1 -1
  278. package/dist/features/ai-chat.js +187 -183
  279. package/dist/features/ai-chat.js.map +1 -1
  280. package/dist/features/async-preview.js +9 -9
  281. package/dist/features/async-preview.js.map +1 -1
  282. package/dist/features/combobox.js +1 -1
  283. package/dist/features/comments.js +1 -1
  284. package/dist/features/data-view.js +25 -23
  285. package/dist/features/data-view.js.map +1 -1
  286. package/dist/features/email-composer/engine.js +1 -1
  287. package/dist/features/email-composer.js +353 -355
  288. package/dist/features/email-composer.js.map +1 -1
  289. package/dist/features/event-calendar.js +346 -354
  290. package/dist/features/event-calendar.js.map +1 -1
  291. package/dist/features/filters.js +1 -1
  292. package/dist/features/global-search.js +225 -227
  293. package/dist/features/global-search.js.map +1 -1
  294. package/dist/features/kanban.js +29 -29
  295. package/dist/features/kanban.js.map +1 -1
  296. package/dist/features/map.js +59 -58
  297. package/dist/features/map.js.map +1 -1
  298. package/dist/features/media-library.js +898 -900
  299. package/dist/features/media-library.js.map +1 -1
  300. package/dist/features/mentions.js +1 -1
  301. package/dist/features/overlays.js +1 -1
  302. package/dist/features/products.js +1412 -1444
  303. package/dist/features/products.js.map +1 -1
  304. package/dist/features/questionnaire.js +51 -49
  305. package/dist/features/questionnaire.js.map +1 -1
  306. package/dist/features/resource-assignment.js +103 -97
  307. package/dist/features/resource-assignment.js.map +1 -1
  308. package/dist/features/resource.js +13 -13
  309. package/dist/features/resource.js.map +1 -1
  310. package/dist/features/rich-text-editor.js +2 -2
  311. package/dist/features/schema-form.js +187 -183
  312. package/dist/features/schema-form.js.map +1 -1
  313. package/dist/features/suggestions.js +3 -3
  314. package/dist/features/sync.js +7 -7
  315. package/dist/features/sync.js.map +1 -1
  316. package/dist/features/table.js +1 -1
  317. package/dist/features/theme-tweaker.js +508 -503
  318. package/dist/features/theme-tweaker.js.map +1 -1
  319. package/dist/{feedback-DWUkgEjR.js → feedback-C7nR0UtT.js} +30 -31
  320. package/dist/feedback-C7nR0UtT.js.map +1 -0
  321. package/dist/field-affordance-recipes-CkOCdE2y.js +46 -0
  322. package/dist/field-affordance-recipes-CkOCdE2y.js.map +1 -0
  323. package/dist/{field-state-recipes-CBTRt9Bt.js → field-state-recipes-C-MFBX_8.js} +5 -5
  324. package/dist/field-state-recipes-C-MFBX_8.js.map +1 -0
  325. package/dist/{filters-BhsUtsxW.js → filters-Da-gNPsE.js} +760 -765
  326. package/dist/filters-Da-gNPsE.js.map +1 -0
  327. package/dist/{form-field-Bn7ZH1rA.js → form-field-VZRULhUK.js} +20 -12
  328. package/dist/form-field-VZRULhUK.js.map +1 -0
  329. package/dist/form-sizing-uWP4lzVh.js +126 -0
  330. package/dist/form-sizing-uWP4lzVh.js.map +1 -0
  331. package/dist/{forms-B7pzS25o.js → forms-DDeEnXSY.js} +38 -38
  332. package/dist/forms-DDeEnXSY.js.map +1 -0
  333. package/dist/forms-numeric-D3CO60IF.js +905 -0
  334. package/dist/forms-numeric-D3CO60IF.js.map +1 -0
  335. package/dist/{header-C59QZyxV.js → header-2ycNqGOR.js} +42 -37
  336. package/dist/header-2ycNqGOR.js.map +1 -0
  337. package/dist/{hooks-CM1Vp9Pe.js → hooks-DQ5SPwZw.js} +2 -2
  338. package/dist/{hooks-CM1Vp9Pe.js.map → hooks-DQ5SPwZw.js.map} +1 -1
  339. package/dist/{http-CLTVJc5I.js → http-CdEIHyQf.js} +2 -2
  340. package/dist/http-CdEIHyQf.js.map +1 -0
  341. package/dist/{icon-badge-Dv0RYQwj.js → icon-badge-DKlaKXoo.js} +4 -4
  342. package/dist/{icon-badge-Dv0RYQwj.js.map → icon-badge-DKlaKXoo.js.map} +1 -1
  343. package/dist/index.js +2 -2
  344. package/dist/input-BoLm9Hks.js +20 -0
  345. package/dist/input-BoLm9Hks.js.map +1 -0
  346. package/dist/input-DhQdBnV3.js +255 -0
  347. package/dist/input-DhQdBnV3.js.map +1 -0
  348. package/dist/item-xzh8Df65.js +264 -0
  349. package/dist/item-xzh8Df65.js.map +1 -0
  350. package/dist/layout/app-shell.js +1 -1
  351. package/dist/layout/auth.js +1 -1
  352. package/dist/layout/containers.js +1 -1
  353. package/dist/layout/header.js +1 -1
  354. package/dist/layout/hooks.js +1 -1
  355. package/dist/layout/navigation.js +1 -1
  356. package/dist/layout/page.js +1 -1
  357. package/dist/layout/settings.js +1 -1
  358. package/dist/layout/sidebar.js +2 -2
  359. package/dist/layout/workspace.js +1 -1
  360. package/dist/layout.js +10 -10
  361. package/dist/lib/affordance-geometry.generated.d.ts +46 -10
  362. package/dist/lib/control-group-recipes.d.ts +22 -0
  363. package/dist/lib/field-affordance-recipes.d.ts +70 -2
  364. package/dist/lib/field-state-recipes.d.ts +6 -4
  365. package/dist/lib/format.d.ts +9 -0
  366. package/dist/lib/money-format.d.ts +30 -0
  367. package/dist/lib/navigation-row-recipes.d.ts +3 -2
  368. package/dist/lib/overlay-anatomy-recipes.d.ts +2 -2
  369. package/dist/lib/surface-recipes.d.ts +13 -8
  370. package/dist/lib/token-merge.generated.d.ts +144 -2
  371. package/dist/lib/track-recipes.d.ts +1 -1
  372. package/dist/lib/ui-provider/provider.d.ts +7 -2
  373. package/dist/lib/ui-provider/store.d.ts +6 -2
  374. package/dist/lib/ui-provider/types.d.ts +9 -4
  375. package/dist/lib/use-prefers-reduced-motion.d.ts +2 -0
  376. package/dist/lib/utils.js +1 -1
  377. package/dist/media-gallery-DQUQ0m3O.js +201 -0
  378. package/dist/media-gallery-DQUQ0m3O.js.map +1 -0
  379. package/dist/{mentions-B8Ko-fhS.js → mentions-DwnO1hh4.js} +40 -39
  380. package/dist/{mentions-B8Ko-fhS.js.map → mentions-DwnO1hh4.js.map} +1 -1
  381. package/dist/message-YfFmbXNP.js +127 -0
  382. package/dist/message-YfFmbXNP.js.map +1 -0
  383. package/dist/{navigation-BUIn2Qxx.js → navigation-Cbp5V8gG.js} +27 -28
  384. package/dist/navigation-Cbp5V8gG.js.map +1 -0
  385. package/dist/navigation-Cs1npsPm.js +300 -0
  386. package/dist/navigation-Cs1npsPm.js.map +1 -0
  387. package/dist/{navigation-row-recipes-GVh11KiT.js → navigation-row-recipes-DlU-4QYO.js} +3 -3
  388. package/dist/navigation-row-recipes-DlU-4QYO.js.map +1 -0
  389. package/dist/number-CKRBYmZK.js +352 -0
  390. package/dist/number-CKRBYmZK.js.map +1 -0
  391. package/dist/overlay.recipes-CNO2S9V6.js +34 -0
  392. package/dist/overlay.recipes-CNO2S9V6.js.map +1 -0
  393. package/dist/overlays-DhZO7N-I.js +994 -0
  394. package/dist/overlays-DhZO7N-I.js.map +1 -0
  395. package/dist/page-C6R_8jFt.js +325 -0
  396. package/dist/page-C6R_8jFt.js.map +1 -0
  397. package/dist/{page-header-DQRa0jug.js → page-header-hUTzcRZX.js} +12 -12
  398. package/dist/page-header-hUTzcRZX.js.map +1 -0
  399. package/dist/{pagination-C29vvt3I.js → pagination-Cm0jqa3I.js} +21 -21
  400. package/dist/pagination-Cm0jqa3I.js.map +1 -0
  401. package/dist/{popover-CoLDEg19.js → popover-Dcv_9jxr.js} +23 -22
  402. package/dist/popover-Dcv_9jxr.js.map +1 -0
  403. package/dist/{popover-menu-GLIQTJTx.js → popover-menu-DnRFKnHP.js} +6 -6
  404. package/dist/{popover-menu-GLIQTJTx.js.map → popover-menu-DnRFKnHP.js.map} +1 -1
  405. package/dist/{popover.recipes-zgBhMlUu.js → popover.recipes-wmbvL8Ah.js} +5 -5
  406. package/dist/popover.recipes-wmbvL8Ah.js.map +1 -0
  407. package/dist/primitives-BHrwQXFC.js +379 -0
  408. package/dist/primitives-BHrwQXFC.js.map +1 -0
  409. package/dist/primitives.js +5 -5
  410. package/dist/{progress-BOe8l_FZ.js → progress-Br9WTFya.js} +12 -11
  411. package/dist/progress-Br9WTFya.js.map +1 -0
  412. package/dist/{receipt-BM8xsGFy.js → receipt-CV89q1fs.js} +8 -8
  413. package/dist/{receipt-BM8xsGFy.js.map → receipt-CV89q1fs.js.map} +1 -1
  414. package/dist/rich-text-editor-CF8QYt5p.js +574 -0
  415. package/dist/rich-text-editor-CF8QYt5p.js.map +1 -0
  416. package/dist/{rich-text-editor.recipes-DxvOE0Lp.js → rich-text-editor.recipes-CnrbmTS1.js} +35 -26
  417. package/dist/rich-text-editor.recipes-CnrbmTS1.js.map +1 -0
  418. package/dist/{rich-text-B7sBAdDG.js → rich-text-j-FGAeZW.js} +4 -4
  419. package/dist/{rich-text-B7sBAdDG.js.map → rich-text-j-FGAeZW.js.map} +1 -1
  420. package/dist/{scroll-area-BheMQ270.js → scroll-area-DRdr16Pt.js} +3 -3
  421. package/dist/scroll-area-DRdr16Pt.js.map +1 -0
  422. package/dist/{separator-DO0EbCEm.js → separator-BMEvwnSX.js} +2 -2
  423. package/dist/{separator-DO0EbCEm.js.map → separator-BMEvwnSX.js.map} +1 -1
  424. package/dist/{separator-Gk_WXH-l.js → separator-Do5GWDYi.js} +4 -4
  425. package/dist/separator-Do5GWDYi.js.map +1 -0
  426. package/dist/services/actions/action-adapters.d.ts +4 -0
  427. package/dist/services/actions.js +8 -3
  428. package/dist/services/actions.js.map +1 -1
  429. package/dist/{settings-BnCyyPOJ.js → settings-DHk0L2MF.js} +20 -19
  430. package/dist/settings-DHk0L2MF.js.map +1 -0
  431. package/dist/sheet-DRFNY1_D.js +207 -0
  432. package/dist/sheet-DRFNY1_D.js.map +1 -0
  433. package/dist/sidebar-Cb8WzOvE.js +511 -0
  434. package/dist/sidebar-Cb8WzOvE.js.map +1 -0
  435. package/dist/{sidebar-DPR7fmqN.js → sidebar-Crc9M9JV.js} +220 -201
  436. package/dist/sidebar-Crc9M9JV.js.map +1 -0
  437. package/dist/{skeleton-CmT1ppqb.js → skeleton-DVMXr15h.js} +5 -5
  438. package/dist/{skeleton-CmT1ppqb.js.map → skeleton-DVMXr15h.js.map} +1 -1
  439. package/dist/smart-card-Bek-TwUp.js +599 -0
  440. package/dist/smart-card-Bek-TwUp.js.map +1 -0
  441. package/dist/{spinner-BY9HbUEh.js → spinner-CmTAbEp0.js} +8 -6
  442. package/dist/spinner-CmTAbEp0.js.map +1 -0
  443. package/dist/{structure-MvKVq-oL.js → structure-hzCW-XsV.js} +3 -3
  444. package/dist/{structure-MvKVq-oL.js.map → structure-hzCW-XsV.js.map} +1 -1
  445. package/dist/{structure.recipes-Bqhf9oIh.js → structure.recipes-EPz60cU8.js} +37 -19
  446. package/dist/structure.recipes-EPz60cU8.js.map +1 -0
  447. package/dist/style.css +1 -1
  448. package/dist/surface-recipes-DOdoBXGm.js +28 -0
  449. package/dist/surface-recipes-DOdoBXGm.js.map +1 -0
  450. package/dist/table-D6zKeYQX.js +162 -0
  451. package/dist/table-D6zKeYQX.js.map +1 -0
  452. package/dist/table-Dn4XSrSB.js +1444 -0
  453. package/dist/table-Dn4XSrSB.js.map +1 -0
  454. package/dist/tabs-Dk8E_knt.js +189 -0
  455. package/dist/tabs-Dk8E_knt.js.map +1 -0
  456. package/dist/text-inputs-BFpku0Qd.js +331 -0
  457. package/dist/text-inputs-BFpku0Qd.js.map +1 -0
  458. package/dist/{text.styles-BwB7vAF2.js → text.styles-g4He6UIW.js} +26 -15
  459. package/dist/text.styles-g4He6UIW.js.map +1 -0
  460. package/dist/theme.css +90 -124
  461. package/dist/themes/green.css +24 -0
  462. package/dist/{timelines-B9ICkQcV.js → timelines-CxuAFIc2.js} +97 -104
  463. package/dist/timelines-CxuAFIc2.js.map +1 -0
  464. package/dist/track-recipes-CDcKmndW.js +10 -0
  465. package/dist/track-recipes-CDcKmndW.js.map +1 -0
  466. package/dist/{tree-BdWSaoFT.js → tree-BotPlyiD.js} +13 -13
  467. package/dist/{tree-BdWSaoFT.js.map → tree-BotPlyiD.js.map} +1 -1
  468. package/dist/typography/rich-text.js +1 -1
  469. package/dist/typography-CAr3dQhX.js +116 -0
  470. package/dist/typography-CAr3dQhX.js.map +1 -0
  471. package/dist/typography.js +2 -2
  472. package/dist/{ui-provider-CftIB6TY.js → ui-provider-D2_TeLqw.js} +22 -22
  473. package/dist/ui-provider-D2_TeLqw.js.map +1 -0
  474. package/dist/ui-provider.js +1 -1
  475. package/dist/{upload-D8Za5x_c.js → upload-B0WHW7ak.js} +278 -274
  476. package/dist/upload-B0WHW7ak.js.map +1 -0
  477. package/dist/{upload-queue-hIIYBLS7.js → upload-queue-BHZ_wBfd.js} +72 -75
  478. package/dist/upload-queue-BHZ_wBfd.js.map +1 -0
  479. package/dist/{usage-card-DnmnJ58D.js → usage-card-U0YBLTNT.js} +31 -33
  480. package/dist/usage-card-U0YBLTNT.js.map +1 -0
  481. package/dist/utils-Cospvlj1.js +4445 -0
  482. package/dist/utils-Cospvlj1.js.map +1 -0
  483. package/dist/value-inputs-CjfzGMsN.js +854 -0
  484. package/dist/value-inputs-CjfzGMsN.js.map +1 -0
  485. package/dist/value-typography-Deb3yQZg.js +116 -0
  486. package/dist/value-typography-Deb3yQZg.js.map +1 -0
  487. package/dist/{workspace-C1PZmk8L.js → workspace-YoFjXi6i.js} +97 -105
  488. package/dist/workspace-YoFjXi6i.js.map +1 -0
  489. package/docs/consumer-skill/SKILL.md +123 -119
  490. package/docs/consumer-skill/examples/analytics.tsx +34 -0
  491. package/docs/consumer-skill/examples/app-layout.tsx +42 -14
  492. package/docs/consumer-skill/examples/detail.tsx +20 -5
  493. package/docs/consumer-skill/examples/edit.tsx +1 -1
  494. package/docs/consumer-skill/examples/layout-blocks.tsx +20 -0
  495. package/docs/consumer-skill/examples/manifest.json +37 -3
  496. package/docs/consumer-skill/examples/scoped-directory.tsx +2 -2
  497. package/docs/consumer-skill/examples/website.tsx +56 -0
  498. package/docs/consumer-skill/references/analytics.md +70 -0
  499. package/docs/consumer-skill/references/composition.md +195 -9
  500. package/docs/consumer-skill/references/design-contract.md +33 -7
  501. package/docs/consumer-skill/references/discovery.md +80 -5
  502. package/docs/consumer-skill/references/installation.md +22 -0
  503. package/docs/consumer-skill/references/integration.md +46 -1
  504. package/docs/consumer-skill/references/public-api.json +2083 -237
  505. package/docs/consumer-skill/references/review.md +74 -6
  506. package/docs/consumer-skill/references/theming.md +59 -8
  507. package/docs/consumer-skill/references/tokens.json +180 -9
  508. package/docs/consumer-skill/references/workflows.md +34 -0
  509. package/docs/consumer-skill/scripts/check-composition.mjs +1 -1
  510. package/docs/consumer-skill/scripts/composition-policy.mjs +271 -18
  511. package/docs/consumer-skill/scripts/discovery.mjs +69 -9
  512. package/docs/consumer-skill/scripts/pin-button-defaults.mjs +95 -0
  513. package/docs/consumer-skill/scripts/surface-policy.mjs +44 -0
  514. package/package.json +11 -6
  515. package/dist/accordion-B-5Focs8.js.map +0 -1
  516. package/dist/action-menu-Cx3SdfbN.js +0 -265
  517. package/dist/action-menu-Cx3SdfbN.js.map +0 -1
  518. package/dist/affordance-geometry.generated-D8Jcfb0G.js +0 -32
  519. package/dist/affordance-geometry.generated-D8Jcfb0G.js.map +0 -1
  520. package/dist/ai-artifact-B5tPpXnS.js.map +0 -1
  521. package/dist/app-shell-at8Kt4l_.js.map +0 -1
  522. package/dist/attachment-BO2R47wn.js.map +0 -1
  523. package/dist/auth-OFwWSAap.js +0 -704
  524. package/dist/auth-OFwWSAap.js.map +0 -1
  525. package/dist/avatar-Bpzty83c.js.map +0 -1
  526. package/dist/badge-BZXZT9wd.js.map +0 -1
  527. package/dist/breadcrumbs-DdQ_I5ra.js.map +0 -1
  528. package/dist/button-BS6l5JUw.js.map +0 -1
  529. package/dist/buttons-Co6GavvM.js +0 -674
  530. package/dist/buttons-Co6GavvM.js.map +0 -1
  531. package/dist/chart-DVfcwQu6.js +0 -149
  532. package/dist/chart-DVfcwQu6.js.map +0 -1
  533. package/dist/checkbox-BavDRjF7.js +0 -34
  534. package/dist/checkbox-BavDRjF7.js.map +0 -1
  535. package/dist/choice-inputs-CC7XqPQO.js +0 -619
  536. package/dist/choice-inputs-CC7XqPQO.js.map +0 -1
  537. package/dist/code-block-xHKbAJDj.js.map +0 -1
  538. package/dist/combobox-DEbdr-3u.js +0 -273
  539. package/dist/combobox-DEbdr-3u.js.map +0 -1
  540. package/dist/combobox-xv3yJbzl.js +0 -850
  541. package/dist/combobox-xv3yJbzl.js.map +0 -1
  542. package/dist/command-C0xe7u9B.js.map +0 -1
  543. package/dist/comments-DviPLnwy.js.map +0 -1
  544. package/dist/containers-6s-ucRQQ.js.map +0 -1
  545. package/dist/data-display-cWIRdeaf.js +0 -1129
  546. package/dist/data-display-cWIRdeaf.js.map +0 -1
  547. package/dist/date-BFNMnrS5.js +0 -140
  548. package/dist/date-BFNMnrS5.js.map +0 -1
  549. package/dist/date-picker-CA6ogMQB.js +0 -1521
  550. package/dist/date-picker-CA6ogMQB.js.map +0 -1
  551. package/dist/date-pickers-BPIKLdsD.js +0 -224
  552. package/dist/date-pickers-BPIKLdsD.js.map +0 -1
  553. package/dist/display-CFM5myut.js.map +0 -1
  554. package/dist/dropdown-menu-BlnyHr-9.js.map +0 -1
  555. package/dist/feedback-DWUkgEjR.js.map +0 -1
  556. package/dist/field-affordance-recipes-IjzPrCTw.js +0 -17
  557. package/dist/field-affordance-recipes-IjzPrCTw.js.map +0 -1
  558. package/dist/field-state-recipes-CBTRt9Bt.js.map +0 -1
  559. package/dist/filters-BhsUtsxW.js.map +0 -1
  560. package/dist/form-field-Bn7ZH1rA.js.map +0 -1
  561. package/dist/form-sizing-BWs70_a2.js +0 -100
  562. package/dist/form-sizing-BWs70_a2.js.map +0 -1
  563. package/dist/format-iamDSqs2.js +0 -110
  564. package/dist/format-iamDSqs2.js.map +0 -1
  565. package/dist/forms-B7pzS25o.js.map +0 -1
  566. package/dist/forms-numeric-BUBxELBv.js +0 -867
  567. package/dist/forms-numeric-BUBxELBv.js.map +0 -1
  568. package/dist/header-C59QZyxV.js.map +0 -1
  569. package/dist/http-CLTVJc5I.js.map +0 -1
  570. package/dist/input-WwYQZJzG.js +0 -202
  571. package/dist/input-WwYQZJzG.js.map +0 -1
  572. package/dist/input-ci9fIgFH.js +0 -20
  573. package/dist/input-ci9fIgFH.js.map +0 -1
  574. package/dist/item-pJ0GsOiJ.js +0 -257
  575. package/dist/item-pJ0GsOiJ.js.map +0 -1
  576. package/dist/media-gallery-DhDRgnVZ.js +0 -200
  577. package/dist/media-gallery-DhDRgnVZ.js.map +0 -1
  578. package/dist/message-COsdEvXT.js +0 -108
  579. package/dist/message-COsdEvXT.js.map +0 -1
  580. package/dist/navigation-BUIn2Qxx.js.map +0 -1
  581. package/dist/navigation-DBEu4POU.js +0 -267
  582. package/dist/navigation-DBEu4POU.js.map +0 -1
  583. package/dist/navigation-row-recipes-GVh11KiT.js.map +0 -1
  584. package/dist/number-BNT_BIzJ.js +0 -269
  585. package/dist/number-BNT_BIzJ.js.map +0 -1
  586. package/dist/overlay.recipes-BxW5FIfK.js +0 -6
  587. package/dist/overlay.recipes-BxW5FIfK.js.map +0 -1
  588. package/dist/overlays-B11wZka6.js +0 -982
  589. package/dist/overlays-B11wZka6.js.map +0 -1
  590. package/dist/page-BV6ioQg3.js +0 -202
  591. package/dist/page-BV6ioQg3.js.map +0 -1
  592. package/dist/page-header-DQRa0jug.js.map +0 -1
  593. package/dist/pagination-C29vvt3I.js.map +0 -1
  594. package/dist/popover-CoLDEg19.js.map +0 -1
  595. package/dist/popover.recipes-zgBhMlUu.js.map +0 -1
  596. package/dist/primitives-DKPOGVpj.js +0 -330
  597. package/dist/primitives-DKPOGVpj.js.map +0 -1
  598. package/dist/progress-BOe8l_FZ.js.map +0 -1
  599. package/dist/rich-text-editor-CoVrTiSv.js +0 -575
  600. package/dist/rich-text-editor-CoVrTiSv.js.map +0 -1
  601. package/dist/rich-text-editor.recipes-DxvOE0Lp.js.map +0 -1
  602. package/dist/scroll-area-BheMQ270.js.map +0 -1
  603. package/dist/select-B2mb-qN2.js +0 -108
  604. package/dist/select-B2mb-qN2.js.map +0 -1
  605. package/dist/separator-Gk_WXH-l.js.map +0 -1
  606. package/dist/settings-BnCyyPOJ.js.map +0 -1
  607. package/dist/sheet-B-q914Ad.js +0 -205
  608. package/dist/sheet-B-q914Ad.js.map +0 -1
  609. package/dist/sidebar-C_2p3Tfj.js +0 -501
  610. package/dist/sidebar-C_2p3Tfj.js.map +0 -1
  611. package/dist/sidebar-DPR7fmqN.js.map +0 -1
  612. package/dist/smart-card-CYLn46e6.js +0 -578
  613. package/dist/smart-card-CYLn46e6.js.map +0 -1
  614. package/dist/spinner-BY9HbUEh.js.map +0 -1
  615. package/dist/structure.recipes-Bqhf9oIh.js.map +0 -1
  616. package/dist/surface-recipes-CQfRr-VL.js +0 -27
  617. package/dist/surface-recipes-CQfRr-VL.js.map +0 -1
  618. package/dist/table-BS39xnlq.js +0 -1424
  619. package/dist/table-BS39xnlq.js.map +0 -1
  620. package/dist/table-DnlAJ-xR.js +0 -146
  621. package/dist/table-DnlAJ-xR.js.map +0 -1
  622. package/dist/tabs-B5cNMWiP.js +0 -187
  623. package/dist/tabs-B5cNMWiP.js.map +0 -1
  624. package/dist/text-inputs-Dh9XGyiS.js +0 -331
  625. package/dist/text-inputs-Dh9XGyiS.js.map +0 -1
  626. package/dist/text.styles-BwB7vAF2.js.map +0 -1
  627. package/dist/timelines-B9ICkQcV.js.map +0 -1
  628. package/dist/track-recipes-LLPNVwFg.js +0 -10
  629. package/dist/track-recipes-LLPNVwFg.js.map +0 -1
  630. package/dist/typography-Cv8MT-82.js +0 -96
  631. package/dist/typography-Cv8MT-82.js.map +0 -1
  632. package/dist/ui-provider-CftIB6TY.js.map +0 -1
  633. package/dist/upload-D8Za5x_c.js.map +0 -1
  634. package/dist/upload-queue-hIIYBLS7.js.map +0 -1
  635. package/dist/usage-card-DnmnJ58D.js.map +0 -1
  636. package/dist/utils-C2EKeIw3.js +0 -72
  637. package/dist/utils-C2EKeIw3.js.map +0 -1
  638. package/dist/value-inputs-DX2dbxjj.js +0 -824
  639. package/dist/value-inputs-DX2dbxjj.js.map +0 -1
  640. package/dist/workspace-C1PZmk8L.js.map +0 -1
@@ -1,14 +1,46 @@
1
- import { surfaceParts, surfaceForwarders, paddingClass, paddingProperty, utility } from './surface-policy.mjs';
1
+ import { surfaceParts, surfaceForwarders, paddingClass, paddingProperty, utility, attenuatedFocusRing, sharedCornerParts, displayNames, nativeDisplayNames, marginProperty, radiusProperty, replacesSharedRadius, cornerRole, addsCopyMargin } from './surface-policy.mjs';
2
2
 
3
- const controlNames = /^(?:Base)?(?:Button|ButtonLink|IconButton)$|^(Input|InputGroupInput|Textarea|SelectTrigger|ComboboxTrigger)$/;
3
+ const controlNames = /^(?:Base)?(?:Button|ButtonLink|IconButton)$|^(Input|InputGroup|InputGroupInput|InputGroupTextarea|Textarea|PasswordInput|Select|SelectTrigger|ComboboxInput|ComboboxInputTrigger|ComboboxTrigger)$/;
4
4
  const controlFamilies = new Set(['buttons', 'text-inputs', 'choice-inputs', 'combobox']);
5
5
  const fixedControlClass = /^(?:h|min-h|size|p[xytrblse]?)-|^rounded-(?:xs|sm|md|lg|xl|[2-9]xl|\[)|^text-(?:xs|sm|base|lg|xl|[2-9]xl|\[(?:length:|[\d.]+(?:px|rem|em)))|^(?:font|leading|tracking)-/;
6
- const controlStyle = /^(height|minHeight|fontSize|fontWeight|lineHeight|letterSpacing|borderRadius)$/;
6
+ const controlStyle = /^(height|minHeight|fontSize|fontWeight|fontFamily|lineHeight|letterSpacing|borderRadius)$/;
7
+ const fixedTextClass = /^text-(?:xs|sm|base|lg|xl|[2-9]xl|\[(?:length:|[\d.]+(?:px|rem|em)))|^(?:font|leading|tracking)-/;
8
+ const textStyle = /^(fontSize|fontWeight|fontFamily|lineHeight|letterSpacing)$/;
9
+ const smallPeerGaps = new Set(['none', 'normal', 'dense', '2xs', 'xs', 'sm', 'md']);
10
+ const structureDefaults = { Stack: 'md', Grid: 'xl', AdaptiveGrid: 'xl' };
11
+ const breakpoints = ['base', 'sm', 'md', 'lg', 'xl', '2xl'];
12
+ const paletteHues = 'slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose|mauve|olive|mist|taupe';
13
+ const rawPaletteClass = new RegExp(`^!?(?:bg|text|border(?:-[xytrblse])?|ring|ring-offset|outline|fill|stroke|from|via|to|divide|decoration|caret|accent|placeholder)-(?:(?:${paletteHues})-(?:50|[1-9]00|950)|black|white)(?:/\\d{1,3})?$`);
14
+ const overlayOwners = ['ActionDialog', 'ConfirmDialog', 'ActionDrawer'];
15
+
16
+ /** Bind this source only; never resolve imports, read libraries or run app code. */
17
+ export function sourceBindings(source, ts) {
18
+ const options = { noResolve: true, noLib: true, allowJs: true, jsx: ts.JsxEmit.Preserve };
19
+ const host = ts.createCompilerHost(options);
20
+ host.getSourceFile = filename => filename === source.fileName ? source : undefined;
21
+ const checker = ts.createProgram([source.fileName], options, host).getTypeChecker();
22
+ return node => {
23
+ if (!node) return undefined;
24
+ if (ts.isIdentifier(node) && ts.isShorthandPropertyAssignment(node.parent) && node.parent.name === node) return checker.getShorthandAssignmentValueSymbol(node.parent);
25
+ return checker.getSymbolAtLocation(node);
26
+ };
27
+ }
7
28
 
8
29
  /** Small static review: TypeScript is supplied by the app, never a runtime peer. */
30
+ const overlayRemedy = 'Use ActionDialog, ConfirmDialog or ActionDrawer with useOverlayVisibility from themelia-react-ui/features/overlays; they own focus, dismissal, pending state and portal theming.';
31
+ const modalLibrary = /^(?:@radix-ui\/react-(?:alert-)?dialog|react-modal|vaul|@base-ui(?:-components)?\/react\/(?:alert-)?dialog)$/;
32
+ const modalExports = { '@headlessui/react': /^(?:Dialog|DialogPanel)$/, 'react-aria-components': /^(?:Dialog|Modal|ModalOverlay)$/ };
33
+ const importsModal = (node, ts) => {
34
+ const specifier = node.moduleSpecifier.text;
35
+ if (modalLibrary.test(specifier)) return true;
36
+ const names = node.importClause?.namedBindings;
37
+ return !!modalExports[specifier] && !!names && ts.isNamedImports(names) && names.elements.some(item => modalExports[specifier].test((item.propertyName ?? item.name).text));
38
+ };
39
+
9
40
  export function analyzeComposition(text, { filename, index, ts }) {
10
41
  const kind = /\.jsx?$/.test(filename) ? ts.ScriptKind.JSX : filename.endsWith('.tsx') ? ts.ScriptKind.TSX : ts.ScriptKind.TS;
11
42
  const source = ts.createSourceFile(filename, text, ts.ScriptTarget.Latest, true, kind);
43
+ const symbolFor = sourceBindings(source, ts);
12
44
  const families = new Map(index.families.map(owner => [owner.import, owner]));
13
45
  const bindings = new Map();
14
46
  const namespaces = new Map();
@@ -33,7 +65,7 @@ export function analyzeComposition(text, { filename, index, ts }) {
33
65
  const resolve = (node, visiting = new Set()) => {
34
66
  if (!node || visiting.has(node)) return undefined;
35
67
  visiting.add(node);
36
- if (ts.isIdentifier(node)) return resolve(declarations.get(node.text), visiting);
68
+ if (ts.isIdentifier(node)) return resolve(declarations.get(symbolFor(node)), visiting);
37
69
  if (ts.isParenthesizedExpression(node) || ts.isAsExpression(node) || ts.isSatisfiesExpression(node)) return resolve(node.expression, visiting);
38
70
  return node;
39
71
  };
@@ -60,6 +92,20 @@ export function analyzeComposition(text, { filename, index, ts }) {
60
92
  if (!node || visited.has(node)) { if (expression) unknown(expression); return []; }
61
93
  visited.add(node);
62
94
  if (ts.isStringLiteralLike(node)) return [node.text];
95
+ if (ts.isTemplateExpression(node)) {
96
+ // Keep complete static class tokens. A token split by a dynamic span is
97
+ // unresolved rather than an invented class from one of its fragments.
98
+ const chunks = [node.head.text];
99
+ for (const span of node.templateSpans) {
100
+ const resolved = resolve(span.expression);
101
+ if (resolved && (ts.isStringLiteralLike(resolved) || ts.isNumericLiteral(resolved))) chunks[chunks.length - 1] += resolved.text + span.literal.text;
102
+ else { unknown(span.expression); chunks.push(span.literal.text); }
103
+ }
104
+ return chunks.map((chunk, index) => {
105
+ const value = index > 0 ? chunk.replace(/^\S*/, '') : chunk;
106
+ return index < chunks.length - 1 ? value.replace(/\S*$/, '') : value;
107
+ });
108
+ }
63
109
  if (ts.isConditionalExpression(node)) return [...classStrings(node.whenTrue, visited), ...classStrings(node.whenFalse, visited)];
64
110
  if (ts.isBinaryExpression(node) && [ts.SyntaxKind.AmpersandAmpersandToken, ts.SyntaxKind.BarBarToken, ts.SyntaxKind.QuestionQuestionToken].includes(node.operatorToken.kind)) return classStrings(node.right, visited);
65
111
  if (ts.isArrayLiteralExpression(node)) return node.elements.flatMap(item => classStrings(item, visited));
@@ -83,12 +129,14 @@ export function analyzeComposition(text, { filename, index, ts }) {
83
129
  for (const item of elements) {
84
130
  const symbol = (item.propertyName ?? item.name).text;
85
131
  if (!family.symbols.includes(symbol)) add(item, 'public-symbol', 'error', `${specifier} does not export ${symbol}; inspect its declaration.`);
86
- if (ts.isImportDeclaration(node)) bindings.set(item.name.text, { symbol, import: specifier });
132
+ else if (family.deprecated?.[symbol]) add(item, 'deprecated-owner', 'warning', `${symbol} is deprecated: ${family.deprecated[symbol]}`);
133
+ if (ts.isImportDeclaration(node)) bindings.set(symbolFor(item.name), { symbol, import: specifier });
87
134
  }
88
- if (names && ts.isNamespaceImport(names)) namespaces.set(names.name.text, specifier);
135
+ if (names && ts.isNamespaceImport(names)) namespaces.set(symbolFor(names.name), specifier);
89
136
  }
90
137
  function gather(node) {
91
- if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer) declarations.set(node.name.text, node.initializer);
138
+ if (ts.isImportDeclaration(node) && ts.isStringLiteralLike(node.moduleSpecifier) && importsModal(node, ts)) add(node, 'raw-overlay-primitive', 'warning', `${node.moduleSpecifier.text} builds a second dialog system. ${overlayRemedy}`);
139
+ if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer) declarations.set(symbolFor(node.name), node.initializer);
92
140
  if ((ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) && node.moduleSpecifier && ts.isStringLiteralLike(node.moduleSpecifier)) importOwner(node, node.moduleSpecifier.text);
93
141
  if (ts.isImportTypeNode(node) && ts.isLiteralTypeNode(node.argument) && ts.isStringLiteralLike(node.argument.literal)) importOwner(node, node.argument.literal.text);
94
142
  if (ts.isCallExpression(node) && node.expression.kind === ts.SyntaxKind.ImportKeyword && ts.isStringLiteralLike(node.arguments[0])) importOwner(node, node.arguments[0].text);
@@ -97,10 +145,11 @@ export function analyzeComposition(text, { filename, index, ts }) {
97
145
  gather(source);
98
146
  for (const diagnostic of source.parseDiagnostics) add(source, 'syntax', 'error', ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'));
99
147
  const ownerFor = tag => {
100
- if (bindings.has(tag)) return bindings.get(tag);
101
- const [namespace, symbol] = tag.split('.');
102
- const specifier = namespaces.get(namespace);
103
- return specifier && symbol ? { symbol, import: specifier } : undefined;
148
+ if (ts.isIdentifier(tag)) return bindings.get(symbolFor(tag));
149
+ if (ts.isPropertyAccessExpression(tag) && ts.isIdentifier(tag.expression)) {
150
+ const specifier = namespaces.get(symbolFor(tag.expression));
151
+ return specifier ? { symbol: tag.name.text, import: specifier } : undefined;
152
+ }
104
153
  };
105
154
  const attributes = node => node.attributes.properties.flatMap(attribute => {
106
155
  if (ts.isJsxSpreadAttribute(attribute)) return properties(attribute.expression);
@@ -108,6 +157,151 @@ export function analyzeComposition(text, { filename, index, ts }) {
108
157
  const expression = ts.isJsxExpression(attribute.initializer) ? attribute.initializer.expression : attribute.initializer;
109
158
  return [[attribute.name.getText(source), expression]];
110
159
  });
160
+ const isOwner = (owner, suffix, names) => owner?.import === `${index.package}/${suffix}` && names.includes(owner.symbol);
161
+ const framedOwner = (owner, props) => {
162
+ if (literal(props.get('surface')) === 'flat') return false;
163
+ if (isOwner(owner, 'base/cards', ['Card', 'SmartCard']) || isOwner(owner, 'features/table', ['DataTable']) || isOwner(owner, 'features/data-view', ['DataView'])) return true;
164
+ if (isOwner(owner, 'blocks/analytics', ['Metric'])) return literal(props.get('variant')) === 'card';
165
+ if (isOwner(owner, 'blocks/analytics', ['MetricGrid'])) return !props.has('variant') || literal(props.get('variant')) === 'card';
166
+ return isOwner(owner, 'blocks/analytics/usage-card', ['UsageCard']) || isOwner(owner, 'blocks/analytics', ['UsageCard']) || isOwner(owner, 'blocks/planning', ['TaskCard']);
167
+ };
168
+ // Examine lexical JSX only. Names ending in Card are not evidence of framing;
169
+ // ChartCard is card-free; Receipt's flat paper has no ordinary Card frame.
170
+ const contentElements = element => {
171
+ const found = [];
172
+ function collect(child) {
173
+ if (ts.isJsxElement(child) || ts.isJsxSelfClosingElement(child)) {
174
+ const opening = ts.isJsxElement(child) ? child.openingElement : child;
175
+ const owner = ownerFor(opening.tagName);
176
+ found.push({ opening, owner, props: new Map(attributes(opening)), element: child });
177
+ if (isOwner(owner, 'base/cards', ['Card', 'SmartCard'])) return;
178
+ if (ts.isJsxElement(child)) child.children.forEach(collect);
179
+ return;
180
+ }
181
+ if (ts.isJsxFragment(child)) child.children.forEach(collect);
182
+ else if (ts.isJsxExpression(child) && child.expression) { const resolved = resolve(child.expression); if (resolved) collect(resolved); }
183
+ }
184
+ if (ts.isJsxElement(element.parent)) element.parent.children.forEach(collect);
185
+ return found;
186
+ };
187
+ const renderedText = element => ts.isJsxElement(element) ? element.children.map(child => ts.isJsxText(child) ? child.text : ts.isJsxExpression(child) ? literal(child.expression) ?? '' : '').join(' ').trim() : '';
188
+ const slotBadge = expression => {
189
+ const node = resolve(expression);
190
+ if (!node) return false;
191
+ if (ts.isJsxElement(node) || ts.isJsxSelfClosingElement(node)) {
192
+ const opening = ts.isJsxElement(node) ? node.openingElement : node;
193
+ const owner = ownerFor(opening.tagName);
194
+ if (isOwner(owner, 'base/badge', ['Badge'])) return true;
195
+ if (owner?.import === `${index.package}/base/buttons` || /^(button|a)$/.test(opening.tagName.getText(source))) return false;
196
+ if (!/^(div|span)$/.test(opening.tagName.getText(source)) && !isOwner(owner, 'base/structure', ['Stack', 'Grid'])) return false;
197
+ return ts.isJsxElement(node) && node.children.some(slotBadge);
198
+ }
199
+ if (ts.isJsxFragment(node)) return node.children.some(slotBadge);
200
+ if (ts.isJsxExpression(node)) return slotBadge(node.expression);
201
+ return false;
202
+ };
203
+ // Buttons placed directly, conditionally or inside a passive row of a page
204
+ // header's actions region. Unknown local components are custom controls.
205
+ const commandButtons = expression => {
206
+ const node = resolve(expression);
207
+ if (!node) return false;
208
+ if (ts.isJsxElement(node) || ts.isJsxSelfClosingElement(node)) {
209
+ const opening = ts.isJsxElement(node) ? node.openingElement : node;
210
+ const owner = ownerFor(opening.tagName);
211
+ if (isOwner(owner, 'base/buttons', ['Button', 'TextButton', 'BaseButton', 'BaseButtonLink']) || isOwner(owner, 'base/forms', ['SubmitStateButton']) || opening.tagName.getText(source) === 'button') return true;
212
+ if (!/^(div|span)$/.test(opening.tagName.getText(source)) && !isOwner(owner, 'base/structure', ['Stack', 'Grid'])) return false;
213
+ return ts.isJsxElement(node) && node.children.some(commandButtons);
214
+ }
215
+ if (ts.isJsxFragment(node)) return node.children.some(commandButtons);
216
+ if (ts.isJsxExpression(node)) return commandButtons(node.expression);
217
+ if (ts.isConditionalExpression(node)) return commandButtons(node.whenTrue) || commandButtons(node.whenFalse);
218
+ if (ts.isBinaryExpression(node) && [ts.SyntaxKind.AmpersandAmpersandToken, ts.SyntaxKind.BarBarToken, ts.SyntaxKind.QuestionQuestionToken].includes(node.operatorToken.kind)) return commandButtons(node.right);
219
+ return false;
220
+ };
221
+ // Static class tokens only; dynamic parts are skipped rather than reported.
222
+ const staticClasses = (expression, visited = new Set()) => {
223
+ const node = resolve(expression);
224
+ if (!node || visited.has(node)) return [];
225
+ visited.add(node);
226
+ if (ts.isStringLiteralLike(node)) return [node.text];
227
+ if (ts.isTemplateExpression(node)) return [node.head.text, ...node.templateSpans.map(span => span.literal.text)];
228
+ if (ts.isConditionalExpression(node)) return [...staticClasses(node.whenTrue, visited), ...staticClasses(node.whenFalse, visited)];
229
+ if (ts.isBinaryExpression(node) && [ts.SyntaxKind.AmpersandAmpersandToken, ts.SyntaxKind.BarBarToken, ts.SyntaxKind.QuestionQuestionToken].includes(node.operatorToken.kind)) return staticClasses(node.right, visited);
230
+ if (ts.isArrayLiteralExpression(node)) return node.elements.flatMap(item => staticClasses(item, visited));
231
+ if (ts.isCallExpression(node) && /^(cn|clsx|classNames|twMerge)$/.test(node.expression.getText(source))) return node.arguments.flatMap(item => staticClasses(item, visited));
232
+ return [];
233
+ };
234
+ const classTokens = opening => {
235
+ const attribute = opening.attributes.properties.find(property => ts.isJsxAttribute(property) && property.name.getText(source) === 'className');
236
+ if (!attribute?.initializer) return [];
237
+ const expression = ts.isJsxExpression(attribute.initializer) ? attribute.initializer.expression : attribute.initializer;
238
+ // Template chunks may split a token; keep only complete-looking words.
239
+ return staticClasses(expression).flatMap(value => value.split(/\s+/)).filter(token => token && !token.startsWith('-') && !token.endsWith('-')).map(utility);
240
+ };
241
+ // tone="primary" is solid unless a buttonStyle says otherwise; untoned Buttons are neutral.
242
+ const solidPrimary = (opening, owner) => {
243
+ if (!isOwner(owner, 'base/buttons', ['Button'])) return false;
244
+ const props = new Map(attributes(opening));
245
+ const style = props.has('buttonStyle') ? literal(props.get('buttonStyle')) : 'solid';
246
+ return literal(props.get('tone')) === 'primary' && style === 'solid';
247
+ };
248
+ // Overlays are separate surfaces with their own primary command.
249
+ const solidPrimaryCount = root => {
250
+ let count = 0;
251
+ const walk = child => {
252
+ if (ts.isJsxElement(child) || ts.isJsxSelfClosingElement(child)) {
253
+ const opening = ts.isJsxElement(child) ? child.openingElement : child;
254
+ const owner = ownerFor(opening.tagName);
255
+ if (isOwner(owner, 'features/overlays', overlayOwners)) return;
256
+ if (solidPrimary(opening, owner)) count++;
257
+ }
258
+ ts.forEachChild(child, walk);
259
+ };
260
+ ts.forEachChild(root, walk);
261
+ return count;
262
+ };
263
+ const peerCards = element => {
264
+ if (!ts.isJsxElement(element.parent)) return new Set();
265
+ const cards = [];
266
+ function collect(child) {
267
+ if (ts.isJsxFragment(child)) { child.children.forEach(collect); return; }
268
+ const opening = ts.isJsxElement(child) ? child.openingElement : ts.isJsxSelfClosingElement(child) ? child : undefined;
269
+ if (!opening) return;
270
+ const owner = ownerFor(opening.tagName);
271
+ if (framedOwner(owner, new Map(attributes(opening)))) cards.push(opening);
272
+ else if (owner?.symbol === 'GridCell' && owner.import === `${index.package}/base/structure` && ts.isJsxElement(child)) child.children.forEach(collect);
273
+ }
274
+ element.parent.children.forEach(collect);
275
+ return new Set(cards);
276
+ };
277
+ const gapEntries = (expression, fallback) => {
278
+ if (!expression) return { base: fallback };
279
+ const node = resolve(expression);
280
+ if (node && ts.isStringLiteralLike(node)) return { base: node.text };
281
+ if (node && ts.isObjectLiteralExpression(node)) return { base: fallback, ...Object.fromEntries(properties(node).filter(([key]) => breakpoints.includes(key)).map(([key, value]) => {
282
+ const gap = literal(value);
283
+ if (gap === undefined && value) unknown(value);
284
+ return [key, gap];
285
+ })) };
286
+ if (node && node.kind === ts.SyntaxKind.NullKeyword || ts.isIdentifier(expression) && expression.text === 'undefined' && !symbolFor(expression)) return { base: fallback };
287
+ unknown(expression);
288
+ return { base: undefined };
289
+ };
290
+ const smallGap = (props, symbol, unknownProps) => {
291
+ // Unknown spread props may replace a missing gap. Report them as unresolved
292
+ // instead of treating the live default as a proven effective value.
293
+ if (!props.has('gap') && unknownProps) return false;
294
+ const gaps = gapEntries(props.get('gap'), structureDefaults[symbol]);
295
+ const rows = symbol === 'Stack' ? {} : gapEntries(props.get('rowGap'));
296
+ const columns = symbol === 'Stack' ? {} : gapEntries(props.get('columnGap'));
297
+ let gap; let row; let column;
298
+ return breakpoints.some(breakpoint => {
299
+ if (Object.hasOwn(gaps, breakpoint)) gap = gaps[breakpoint];
300
+ if (Object.hasOwn(rows, breakpoint)) row = rows[breakpoint];
301
+ if (Object.hasOwn(columns, breakpoint)) column = columns[breakpoint];
302
+ return smallPeerGaps.has(row ?? gap) || smallPeerGaps.has(column ?? gap);
303
+ });
304
+ };
111
305
  function visit(node, parents = []) {
112
306
  if (ts.isJsxElement(node)) {
113
307
  const element = node.openingElement;
@@ -121,34 +315,93 @@ export function analyzeComposition(text, { filename, index, ts }) {
121
315
  ts.forEachChild(node, child => visit(child, parents));
122
316
  }
123
317
  function inspect(node, parents) {
124
- const owner = ownerFor(node.tagName.getText(source));
125
- if (!owner) return;
318
+ const owner = ownerFor(node.tagName);
319
+ if (classTokens(node).some(token => rawPaletteClass.test(token))) add(node, 'raw-palette-color', 'warning', 'Raw palette colors bypass the theme and dark mode. Use semantic classes such as bg-primary, bg-muted, text-muted-foreground, border-border or a status tone (text-destructive, bg-success/10).');
320
+ if (!owner) {
321
+ const tag = node.tagName.getText(source);
322
+ if (tag === 'button') add(node, 'raw-button', 'warning', 'Use Button or TextButton from themelia-react-ui/base/buttons: they own size, focus ring, loading and disabled states.');
323
+ if (/^h[1-6]$/.test(tag)) add(node, 'raw-heading', 'warning', 'Use Heading from themelia-react-ui/typography (tag for the level, size for emphasis): it resets native margins and keeps the shared heading scale.');
324
+ const nativeProps = new Map(attributes(node));
325
+ const modalRole = /^(?:dialog|alertdialog)$/.test(literal(nativeProps.get('role')) ?? '') || nativeProps.has('aria-modal');
326
+ const scrim = classTokens(node).includes('fixed') && classTokens(node).includes('inset-0');
327
+ 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}`);
328
+ if (/^(?:input|textarea)$/.test(node.tagName.getText(source))) {
329
+ const props = new Map(attributes(node));
330
+ 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.');
331
+ 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.');
332
+ }
333
+ if (nativeDisplayNames.test(node.tagName.getText(source))) {
334
+ const props = new Map(attributes(node));
335
+ if (props.has('className') && classStrings(props.get('className')).flatMap(value=>value.split(/\s+/)).some(addsCopyMargin)) add(node,'copy-margin','error','Display copy is margin-free; use the surrounding structure for copy gaps.');
336
+ if (props.has('style') && properties(props.get('style')).some(([name,value])=>marginProperty.test(name)&&!/^['"]?0(?:px|rem|em|%)?['"]?$/.test(resolve(value)?.getText(source)??''))) add(node,'copy-margin','error','Display copy is margin-free; use the surrounding structure for copy gaps.');
337
+ }
338
+ return;
339
+ }
126
340
  if (!families.get(owner.import)?.symbols.includes(owner.symbol)) {
127
341
  add(node, 'public-symbol', 'error', `${owner.import} does not export ${owner.symbol}.`);
128
342
  return;
129
343
  }
344
+ const unresolvedBeforeProps = unresolved.length;
130
345
  const props = new Map(attributes(node));
346
+ const unknownProps = unresolved.length !== unresolvedBeforeProps;
131
347
  const family = owner.import.split('/base/')[1];
132
348
  const anatomy = surfaceParts[family]?.test(owner.symbol);
133
349
  const forwarder = surfaceForwarders[family] === owner.symbol;
134
350
  const control = controlFamilies.has(family) && controlNames.test(owner.symbol);
135
- for (const name of ['className', 'contentClassName']) {
351
+ const sharedCorners = sharedCornerParts[family]?.test(owner.symbol);
352
+ const display = owner.import === `${index.package}/typography` && displayNames.test(owner.symbol);
353
+ const fixedText = owner.import === `${index.package}/typography` && owner.symbol === 'DisplayLabel' || family === 'badge' && owner.symbol === 'Badge';
354
+ const badge = family === 'badge' && owner.symbol === 'Badge';
355
+ for (const name of ['className', 'headerClassName', 'contentClassName', 'footerClassName', 'subHeadingClassName']) {
136
356
  if (!props.has(name)) continue;
137
357
  const classes = classStrings(props.get(name)).flatMap(value => value.split(/\s+/)).map(utility);
138
358
  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.`);
139
359
  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.`);
360
+ 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.`);
361
+ 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.`);
362
+ if ((control || family === 'buttons' && owner.symbol === 'TextButton') && name === 'className' && classes.some(attenuatedFocusRing)) add(node, 'focus-ring-contrast', 'error', `${owner.symbol}: keep the shared ring-ring/sidebar-ring focus color at full strength; numeric opacity weakens its tested contrast.`);
363
+ if (fixedText && name === 'className' && classes.some(value => fixedTextClass.test(value) || badge && /^rounded(?:-|$)/.test(value))) add(node, 'fixed-text-role', 'error', `${owner.symbol}: preserve its shared typography${badge ? ' and radius' : ''}; use its public variants and className for placement.`);
140
364
  }
141
- if (props.has('style')) for (const [name] of properties(props.get('style'))) {
365
+ if (props.has('style')) for (const [name,value] of properties(props.get('style'))) {
142
366
  if (anatomy && paddingProperty.test(name)) add(node, 'surface-padding', 'error', `${owner.symbol}: ${name} replaces the region inset; use its public surface props.`);
143
367
  if (control && (paddingProperty.test(name) || controlStyle.test(name))) add(node, 'control-anatomy', 'error', `${owner.symbol}: ${name} belongs to shared control geometry/typography.`);
368
+ if ((control || sharedCorners) && radiusProperty.test(name)) add(node,'shared-radius','error',`${owner.symbol}: corners follow the global radius theme root, not inline styles.`);
369
+ if (display && marginProperty.test(name) && !/^['"]?0(?:px|rem|em|%)?['"]?$/.test(resolve(value)?.getText(source)??'')) add(node,'copy-margin','error',`${owner.symbol}: typography is margin-free; the surrounding structure owns its copy gap.`);
370
+ if (fixedText && (textStyle.test(name) || badge && /^border.*Radius$/.test(name))) add(node, 'fixed-text-role', 'error', `${owner.symbol}: ${name} replaces its shared text/radius recipe.`);
144
371
  }
145
- if (['SmartCard', 'ContentBlock'].includes(owner.symbol)) add(node, 'deprecated-owner', 'warning', `${owner.symbol} is a migration owner; prefer Card for new compositions.`);
146
- const framed = ['Card', 'SmartCard', 'ContentBlock', 'DataTable', 'DataView'].includes(owner.symbol) && literal(props.get('surface')) !== 'flat';
372
+ if ((isOwner(owner, 'base/cards', ['Card', 'SmartCard']) || isOwner(owner, 'base/display', ['ContentBlock'])) && props.get('icon')) add(node, 'decorative-icon', 'warning', `${owner.symbol}: a title icon is decoration unless it encodes state or a brand; let the title name the subject.`);
373
+ const facts = isOwner(owner, 'blocks/data-display', ['MetadataList']) ? resolve(props.get('items')) : undefined;
374
+ if (facts && ts.isArrayLiteralExpression(facts) && facts.elements.filter(item => properties(item, false).some(([name]) => name === 'icon')).length > 1) add(node, 'decorative-icon', 'warning', 'MetadataList: an icon on every fact repeats what its label says; keep labels and drop the icons.');
375
+ const framed = framedOwner(owner, props);
376
+ if (isOwner(owner, 'base/cards', ['Card', 'SmartCard'])) {
377
+ for (const slot of ['headerAction', 'headerStart']) if (slotBadge(props.get(slot))) add(node, 'card-badge-placement', 'error', `${owner.symbol}: put status/count/version badges in titleSuffix; ${slot} owns ${slot === 'headerAction' ? 'commands' : 'pre-title context'}.`);
378
+ const contents = contentElements(node);
379
+ const role = (item, names) => isOwner(item.owner, 'typography', names);
380
+ const valueHeading = contents.some(item => role(item, ['Heading']) && /[$€£¥]|\d/.test(renderedText(item.element)));
381
+ if (valueHeading && contents.some(item => role(item, ['Text', 'DisplayLabel'])) && contents.some(item => isOwner(item.owner, 'base/badge', ['Badge']) && /[%+−-]|\d/.test(renderedText(item.element)))) add(node, 'reinvented-metric-card', 'warning', 'This label/value/change composition resembles a KPI. Compare Metric/MetricGrid from blocks/analytics or InlineStat from blocks/data-display; retain a custom subject when their metric lifecycle does not fit.');
382
+ const factRows = contents.filter(item => isOwner(item.owner, 'base/structure', ['Stack']) && literal(item.props.get('direction')) === 'horizontal' && literal(item.props.get('justify')) === 'between' && contentElements(item.opening).filter(child => role(child, ['Text', 'DisplayLabel'])).length >= 2);
383
+ if (factRows.length >= 2) add(node, 'reinvented-metadata-list', 'warning', 'Repeated label/value rows resemble record facts. Compare MetadataList or InlineStat from blocks/data-display; preserve custom rows when they own distinct interactions or content.');
384
+ if (contents.some(item => role(item, ['Heading']) && /^(?:no\b|nothing\b|empty\b)/i.test(renderedText(item.element))) && contents.some(item => isOwner(item.owner, 'base/structure', ['Stack']) && literal(item.props.get('align')) === 'center') && contents.some(item => isOwner(item.owner, 'base/buttons', ['Button', 'ButtonLink']))) add(node, 'reinvented-empty-state', 'warning', 'This centered empty region may fit EmptyState from blocks/data-display, with media, title, description and actions. Review the product state before choosing the complete owner.');
385
+ }
386
+ 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.');
387
+ 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.');
388
+ 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.');
389
+ 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.');
390
+ const peers = parents.findLast(parent => parent.structure);
391
+ if (framed && (!props.has('surface') || literal(props.get('surface')) === 'framed') && peers?.cards.size > 1 && peers.cards.has(node) && peers.smallGap) add(node, 'card-peer-gap', 'warning', 'Small peer gaps can be consumed by framed Card rings. Use lg/xl for independent surfaces and inspect visible edge clearance; tighter grouping needs an intentional surface policy.');
147
392
  if (framed && parents.some(parent => parent.framed)) add(node, 'nested-surface', 'warning', `${owner.symbol} is inside a framed owner. Review subject boundaries; use surface="flat" when the parent owns the frame.`);
148
393
  const header = properties(props.get('header'), false);
394
+ const pageOwner = owner.import === `${index.package}/layout/page` || owner.import === `${index.package}/layout`;
395
+ const headerActions = pageOwner && owner.symbol === 'Page' ? new Map(header).get('actions') : pageOwner && owner.symbol === 'PageHeader' ? props.get('actions') : undefined;
396
+ if (pageOwner && owner.symbol === 'Page') {
397
+ const primaries = solidPrimaryCount(ts.isJsxSelfClosingElement(node) ? node : node.parent) + (new Map(header).has('primaryAction') ? 1 : 0);
398
+ if (primaries > 1) add(node, 'multiple-primary-commands', 'warning', `Page has ${primaries} solid primary commands. Keep one primary per page as header.primaryAction; leave other commands untoned (the neutral button) or list them in secondaryActions.`);
399
+ }
400
+ if (headerActions && commandButtons(headerActions)) add(node, 'page-command-roles', 'warning', `${owner.symbol}: buttons in the header actions region choose their own emphasis, size and overflow. Pass commands as primaryAction (one) and ordered secondaryActions; keep actions for non-command controls.`);
149
401
  const title = literal(props.get('title')) ?? literal(new Map(header).get('title'));
150
402
  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.`);
151
- return { framed, title };
403
+ const structure = owner.import === `${index.package}/base/structure` && Object.hasOwn(structureDefaults, owner.symbol);
404
+ return { framed, title, structure, cards: structure ? peerCards(node) : new Set(), smallGap: structure ? smallGap(props, owner.symbol, unknownProps) : false };
152
405
  }
153
406
  visit(source);
154
407
  return { findings, unresolved };
@@ -3,6 +3,10 @@ const requireFormat = (condition, message) => { if (!condition) throw new Error(
3
3
  const stringList = value => Array.isArray(value) && value.every(item => typeof item === 'string');
4
4
  const text = value => typeof value === 'string' && value.trim().length > 0;
5
5
  const normalized = value => value.toLowerCase();
6
+ const words = value => value.replace(/([a-z0-9])([A-Z])/g, '$1 $2').replace(/([A-Z])([A-Z][a-z])/g, '$1 $2').toLowerCase().split(/[^a-z0-9]+/).filter(Boolean);
7
+ const symbolKey = value => words(value).join('');
8
+ const searchable = value => words(value).join(' ');
9
+ const containsTerms = (value, terms) => { const tokens = new Set(words(value)); return terms.every(term => tokens.has(term)); };
6
10
  const compare = (a, b) => a < b ? -1 : a > b ? 1 : 0;
7
11
 
8
12
  export function validateIndex(index) {
@@ -17,7 +21,18 @@ export function validateIndex(index) {
17
21
  requireFormat(text(family.declaration) && /^dist\/[\w./-]+\.d\.ts$/.test(family.declaration) && !family.declaration.split('/').includes('..'), `invalid declaration ${family.declaration}`);
18
22
  requireFormat(layers.includes(family.layer) && text(family.responsibility), `missing layer/responsibility for ${family.import}`);
19
23
  for (const field of ['symbols', 'jobs', 'tags', 'aliases', 'sections', 'optionalPeers']) requireFormat(stringList(family[field]), `invalid ${field} for ${family.import}`);
24
+ if (family.deprecated !== undefined) requireFormat(family.deprecated !== null && typeof family.deprecated === 'object' && !Array.isArray(family.deprecated) && Object.entries(family.deprecated).every(([symbol, note]) => family.symbols.includes(symbol) && text(note)), `invalid deprecated symbols for ${family.import}`);
25
+ if (family.references !== undefined) requireFormat(stringList(family.references) && family.references.every(file => /^references\/[\w/-]+\.md(?:#[\w-]+)?$/.test(file) && !file.split('/').includes('..')), `invalid focused references for ${family.import}`);
20
26
  requireFormat(family.optionalPeers.every(peer => index.peers?.some(owner => owner.name === peer && owner.optional)), `unknown optional peer for ${family.import}`);
27
+ if (family.recipes !== undefined) {
28
+ requireFormat(Array.isArray(family.recipes), `invalid recipes for ${family.import}`);
29
+ for (const recipe of family.recipes) {
30
+ requireFormat(text(recipe?.file) && /^examples\/[\w/-]+\.tsx$/.test(recipe.file) && !recipe.file.split('/').includes('..'), 'invalid recipe file');
31
+ requireFormat(stringList(recipe.exports) && recipe.exports.length > 0 && stringList(recipe.symbols) && recipe.symbols.every(symbol => family.symbols.includes(symbol)), `unknown recipe symbol for ${family.import}`);
32
+ requireFormat(stringList(recipe.references) && recipe.references.every(file => /^references\/[\w/-]+\.md(?:#[\w-]+)?$/.test(file) && !file.split('/').includes('..')), 'invalid recipe references');
33
+ if (recipe.optionalPeers !== undefined) requireFormat(stringList(recipe.optionalPeers) && recipe.optionalPeers.every(peer => index.peers?.some(owner => owner.name === peer && owner.optional)), 'invalid recipe optional peers');
34
+ }
35
+ }
21
36
  requireFormat(Array.isArray(family.guidance), `invalid guidance for ${family.import}`);
22
37
  for (const guide of family.guidance) {
23
38
  requireFormat(text(guide?.title) && typeof guide.description === 'string' && typeof guide.section === 'string', 'invalid guidance title/description/section');
@@ -56,28 +71,73 @@ export function parseArguments(args) {
56
71
  return { query, ...options };
57
72
  }
58
73
 
59
- const guideText = guide => normalized([guide.title, guide.description, ...Object.values(guide.browse), ...guide.jobs, ...guide.tags, ...guide.aliases, ...guide.chooseWhen, ...guide.appOwned, ...guide.sections.flatMap(section => [section.title, ...section.symbols])].join(' '));
74
+ const guideText = guide => searchable([guide.title, guide.description, ...Object.values(guide.browse), ...guide.jobs, ...guide.tags, ...guide.aliases, ...guide.chooseWhen, ...guide.appOwned, ...guide.sections.flatMap(section => [section.title, ...section.symbols])].join(' '));
75
+ const guideSymbols = (guide, family) => [...new Set(guide.sections.flatMap(section => section.symbols))].filter(symbol => family.symbols.includes(symbol));
76
+
77
+ function relevantGuidance(family, needle, terms, matchedSymbols, exactImport) {
78
+ const ranked = family.guidance.map(guide => {
79
+ const haystack = guideText(guide);
80
+ const count = terms.filter(term => containsTerms(haystack, [term])).length;
81
+ const symbol = matchedSymbols.some(value => guideSymbols(guide, family).includes(value));
82
+ const alias = guide.aliases.some(value => normalized(value) === needle);
83
+ const complete = count === terms.length;
84
+ const score = Number(alias) * 10000 + Number(symbol) * 1000 + Number(complete) * 100 + count;
85
+ return { guide, score, symbol, alias, complete };
86
+ }).sort((a, b) => b.score - a.score || Number(guideSymbols(b.guide, family).length > 0) - Number(guideSymbols(a.guide, family).length > 0) || compare(a.guide.title, b.guide.title));
87
+ // Exact owner and curated task matches supply their own narrow reading path.
88
+ // Generic intent can still combine terms across fields/family guidance.
89
+ const selected = ranked.filter(item => ranked.some(item => item.alias) ? item.alias : matchedSymbols.length ? item.symbol : ranked.some(item => item.complete) ? item.complete : item.score > 0);
90
+ return (selected.length || !exactImport ? selected : ranked).slice(0, 3).map(item => item.guide);
91
+ }
60
92
 
61
93
  export function findComponents(index, { query, layer = null, limit = 5 }) {
62
94
  validateIndex(index);
63
95
  const needle = normalized(query);
64
- const terms = [...new Set(needle.split(/\s+/).filter(Boolean))];
96
+ const terms = [...new Set(words(query))];
97
+ const key = symbolKey(query);
98
+ // Cased API names and explicit imports request a precise contract. Ordinary
99
+ // nouns such as table/empty remain task searches with richer alternatives.
100
+ const exactOwners = index.families.filter(family => (!layer || layer === family.layer) && (normalized(family.import) === needle || family.symbols.includes(query.trim())));
65
101
  const ranked = [];
66
102
  for (const family of index.families) {
67
103
  if (layer && layer !== family.layer) continue;
104
+ if (exactOwners.length && !exactOwners.includes(family)) continue;
68
105
  const exactImport = normalized(family.import) === needle;
69
- const matchedSymbols = family.symbols.filter(symbol => normalized(symbol) === needle);
70
- const haystack = normalized([family.import, family.responsibility, ...family.symbols, ...family.jobs, ...family.tags, ...family.aliases, ...family.sections, ...family.guidance.map(guideText)].join(' '));
106
+ const matchedSymbols = family.symbols.filter(symbol => symbolKey(symbol) === key);
107
+ const documented = new Set(family.guidance.flatMap(guide => guideSymbols(guide, family)));
108
+ const focusedSymbols = [...documented].filter(symbol => containsTerms(symbol, terms));
109
+ const haystack = searchable([family.import, family.responsibility, ...documented, ...family.jobs, ...family.tags, ...family.aliases, ...family.sections, ...family.guidance.map(guideText)].join(' '));
71
110
  // AND matching prevents a familiar term from inventing results for an
72
111
  // otherwise unknown query. Avoidance/alternatives do not recommend owners.
73
- if (!exactImport && !matchedSymbols.length && !terms.every(term => haystack.includes(term))) continue;
112
+ if (!exactImport && !matchedSymbols.length && !containsTerms(haystack, terms)) continue;
74
113
  // A dedicated, documented owner precedes an aggregate re-export of the
75
114
  // same symbol. Explicit import queries still select the requested family.
76
- const documentedSymbol = family.guidance.some(guide => guide.sections.some(section => section.symbols.some(symbol => normalized(symbol) === needle)));
115
+ const documentedSymbol = matchedSymbols.some(symbol => documented.has(symbol));
116
+ const exactFamily = searchable(family.import.split('/').at(-1)) === terms.join(' ');
77
117
  const exactIntent = family.aliases.some(alias => normalized(alias) === needle);
78
- const score = exactImport ? 10000 : matchedSymbols.length ? 9000 + Number(documentedSymbol) : exactIntent ? 8000 : terms.reduce((sum, term) => sum + (family.guidance.some(guide => normalized(guide.title).includes(term)) ? 20 : family.tags.some(tag => normalized(tag).includes(term)) ? 10 : 1), 0);
79
- const guidance = family.guidance.map(guide => ({ guide, score: terms.filter(term => guideText(guide).includes(term)).length })).sort((a, b) => b.score - a.score || compare(a.guide.title, b.guide.title)).slice(0, 3).map(({ guide }) => guide);
80
- ranked.push({ score, import: family.import, declaration: family.declaration, layer: family.layer, responsibility: family.responsibility, optionalPeers: family.optionalPeers, match: exactImport ? 'import' : matchedSymbols.length ? 'symbol' : 'intent', matchedSymbols, guidance });
118
+ const score = exactImport ? 10000 : matchedSymbols.length ? 9000 + Number(documentedSymbol) : exactFamily ? 8500 : exactIntent ? 8000 : focusedSymbols.length ? 7100 + 1 / Math.min(...focusedSymbols.map(symbol => words(symbol).length)) : terms.reduce((sum, term) => sum + (family.guidance.some(guide => containsTerms(guide.title, [term])) ? 20 : family.tags.some(tag => containsTerms(tag, [term])) ? 10 : 1), 0);
119
+ const selectedSymbols = matchedSymbols.length ? matchedSymbols : focusedSymbols;
120
+ const guidance = relevantGuidance(family, needle, terms, exactOwners.length ? selectedSymbols : [], exactImport);
121
+ const candidateSymbols = exactImport ? family.symbols : exactOwners.length || matchedSymbols.length ? selectedSymbols : [...new Set([...selectedSymbols, ...guidance.flatMap(guide => guideSymbols(guide, family))])];
122
+ // Intent searches recommend current names only. Exact queries keep the
123
+ // requested symbol; either way the result reports deprecated candidates.
124
+ const lifecycle = family.deprecated ?? {};
125
+ const relevantSymbols = exactImport || matchedSymbols.length ? candidateSymbols : candidateSymbols.filter(symbol => !Object.hasOwn(lifecycle, symbol));
126
+ const deprecated = Object.fromEntries(candidateSymbols.filter(symbol => Object.hasOwn(lifecycle, symbol)).map(symbol => [symbol, lifecycle[symbol]]));
127
+ const candidates = (family.recipes ?? []).filter(recipe => recipe.symbols.some(symbol => relevantSymbols.includes(symbol)));
128
+ const recipeText = recipe => normalized([recipe.file, ...recipe.exports, ...recipe.references].join(' ')).replace(/[-/#]/g, ' ');
129
+ const taskRecipes = matchedSymbols.length || exactImport ? [] : candidates.filter(recipe => terms.every(term => recipeText(recipe).includes(term)));
130
+ // Retain related task synonyms without routing through an unrelated alias
131
+ // on the same broad family (for example, website versus main/aside layout).
132
+ const aliases = guidance.flatMap(guide => guide.aliases.filter(alias => terms.some(term => containsTerms(alias, [term]))).map(alias => normalized(alias).split(/\s+/)));
133
+ const aliasRecipes = matchedSymbols.length || exactImport ? [] : candidates.filter(recipe => aliases.some(intent => intent.every(term => recipeText(recipe).includes(term))));
134
+ const recipes = taskRecipes.length ? taskRecipes : aliasRecipes.length ? aliasRecipes : candidates;
135
+ const nextReads = {
136
+ declaration: family.declaration,
137
+ references: [...new Set([...(family.references ?? []), ...recipes.flatMap(recipe => recipe.references)])].sort(compare),
138
+ examples: recipes.map(({ file, exports, symbols, optionalPeers = [] }) => ({ file, exports, symbols: symbols.filter(symbol => relevantSymbols.includes(symbol)), optionalPeers })),
139
+ };
140
+ ranked.push({ score, import: family.import, declaration: family.declaration, layer: family.layer, responsibility: family.responsibility, optionalPeers: family.optionalPeers, match: exactImport ? 'import' : matchedSymbols.length ? 'symbol' : 'intent', matchedSymbols, relevantSymbols, ...(Object.keys(deprecated).length ? { deprecated } : {}), nextReads, guidance });
81
141
  }
82
142
  ranked.sort((a, b) => b.score - a.score || compare(a.import, b.import));
83
143
  return { package: index.package, version: index.version, query, layer, limit, total: ranked.length, results: ranked.slice(0, limit) };
@@ -0,0 +1,95 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { createRequire } from 'node:module';
4
+ import { pathToFileURL } from 'node:url';
5
+ import { sourceBindings } from './composition-policy.mjs';
6
+
7
+ // An untoned Button now renders as the secondary outline, and a Button with a
8
+ // tone but no buttonStyle keeps that tone's solid treatment. Writing the old
9
+ // default tone onto untoned Buttons therefore keeps an existing screen's look.
10
+ // It edits only with --write; otherwise it reports what it would change.
11
+ // --solid-only pins only Buttons that also set no buttonStyle, so untoned
12
+ // outline and ghost Buttons (often brand-coloured by accident) turn neutral.
13
+ const usage = 'Usage: pin-button-defaults.mjs [--write] [--solid-only] [--tone=primary] [--source-root=<dir>] <files or directories>';
14
+ const owners = new Set(['Button', 'BaseButton', 'BaseButtonLink']);
15
+ const packageImports = new Set(['themelia-react-ui', 'themelia-react-ui/base/buttons']);
16
+
17
+ export function pinButtonDefaults(text, { filename, ts, tone = 'primary', solidOnly = false, sourceRoot }) {
18
+ const kind = /\.jsx?$/.test(filename) ? ts.ScriptKind.JSX : filename.endsWith('.tsx') ? ts.ScriptKind.TSX : ts.ScriptKind.TS;
19
+ const source = ts.createSourceFile(filename, text, ts.ScriptTarget.Latest, true, kind);
20
+ const symbolFor = sourceBindings(source, ts);
21
+ // Library maintainers pass --source-root so relative imports of the buttons family count too.
22
+ const buttonsDir = sourceRoot && path.resolve(sourceRoot, 'components/base/buttons');
23
+ const isButtonsImport = specifier => packageImports.has(specifier)
24
+ || (!!buttonsDir && specifier.startsWith('.') && `${path.resolve(path.dirname(filename), specifier)}/`.startsWith(`${buttonsDir}/`));
25
+ const named = new Set();
26
+ const namespaces = new Set();
27
+ for (const statement of source.statements) {
28
+ if (!ts.isImportDeclaration(statement) || !ts.isStringLiteral(statement.moduleSpecifier) || !isButtonsImport(statement.moduleSpecifier.text)) continue;
29
+ const bindings = statement.importClause?.namedBindings;
30
+ if (bindings && ts.isNamespaceImport(bindings)) namespaces.add(symbolFor(bindings.name));
31
+ if (bindings && ts.isNamedImports(bindings)) for (const item of bindings.elements) {
32
+ if (!item.isTypeOnly && owners.has((item.propertyName ?? item.name).text)) named.add(symbolFor(item.name));
33
+ }
34
+ }
35
+ const isOwner = tag => (ts.isIdentifier(tag) && named.has(symbolFor(tag)))
36
+ || (ts.isPropertyAccessExpression(tag) && ts.isIdentifier(tag.expression) && namespaces.has(symbolFor(tag.expression)) && owners.has(tag.name.text));
37
+ const edits = [];
38
+ const pinned = [];
39
+ const unresolved = [];
40
+ const line = node => source.getLineAndCharacterOfPosition(node.getStart(source)).line + 1;
41
+ const visit = node => {
42
+ if ((ts.isJsxOpeningElement(node) || ts.isJsxSelfClosingElement(node)) && isOwner(node.tagName)) {
43
+ const props = node.attributes.properties;
44
+ const has = name => props.some(attribute => ts.isJsxAttribute(attribute) && attribute.name.getText(source) === name);
45
+ const toned = has('tone') || (solidOnly && has('buttonStyle'));
46
+ if (!toned && props.some(ts.isJsxSpreadAttribute)) {
47
+ // A spread may already carry tone; leave it to review.
48
+ unresolved.push({ file: filename, line: line(node) });
49
+ } else if (!toned) {
50
+ edits.push({ at: (node.typeArguments ?? node.tagName).end, text: ` tone="${tone}"` });
51
+ pinned.push({ file: filename, line: line(node), tone });
52
+ }
53
+ }
54
+ ts.forEachChild(node, visit);
55
+ };
56
+ visit(source);
57
+ let output = text;
58
+ for (const edit of edits.sort((a, b) => b.at - a.at)) output = output.slice(0, edit.at) + edit.text + output.slice(edit.at);
59
+ return { output, pinned, unresolved };
60
+ }
61
+
62
+ function collect(target, files) {
63
+ const info = fs.lstatSync(target);
64
+ if (info.isSymbolicLink()) throw new Error(`Use a real source path instead of a symlink: ${target}`);
65
+ if (info.isDirectory()) {
66
+ for (const entry of fs.readdirSync(target).sort()) if (!['node_modules', '.git', 'dist', 'build'].includes(entry)) collect(path.join(target, entry), files);
67
+ } else if (/\.[jt]sx?$/.test(target) && !/\.d\.ts$/.test(target)) files.add(path.resolve(target));
68
+ }
69
+
70
+ if (process.argv[1] && import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href) {
71
+ try {
72
+ const args = process.argv.slice(2);
73
+ const option = name => args.find(arg => arg.startsWith(`--${name}=`))?.slice(name.length + 3);
74
+ const targets = args.filter(arg => !arg.startsWith('--'));
75
+ const unknown = args.filter(arg => arg.startsWith('--') && !/^--(?:write|solid-only|tone=.+|source-root=.+)$/.test(arg));
76
+ if (!targets.length || unknown.length) throw new Error(unknown.length ? `Unknown option: ${unknown.join(', ')}\n${usage}` : usage);
77
+ const ts = createRequire(path.join(process.cwd(), 'package.json'))('typescript');
78
+ const files = new Set();
79
+ targets.forEach(target => collect(target, files));
80
+ if (!files.size) throw new Error('No supported source files found in the supplied paths.');
81
+ const write = args.includes('--write');
82
+ const sourceRoot = option('source-root') && path.resolve(option('source-root'));
83
+ const results = [...files].sort().map(filename => {
84
+ const text = fs.readFileSync(filename, 'utf8');
85
+ const result = pinButtonDefaults(text, { filename, ts, tone: option('tone'), solidOnly: args.includes('--solid-only'), sourceRoot });
86
+ if (write && result.output !== text) fs.writeFileSync(filename, result.output);
87
+ return result;
88
+ });
89
+ const pinned = results.flatMap(result => result.pinned);
90
+ process.stdout.write(JSON.stringify({ mode: write ? 'write' : 'dry-run', files: files.size, pinned: pinned.length, changedFiles: new Set(pinned.map(item => item.file)).size, unresolved: results.flatMap(result => result.unresolved), details: pinned }, null, 2) + '\n');
91
+ } catch (error) {
92
+ process.stderr.write(`pin-button-defaults: ${error.message}\nRun from the consumer app with its TypeScript development dependency installed.\n`);
93
+ process.exitCode = 1;
94
+ }
95
+ }