themelia-react-ui 1.0.0 → 1.0.1

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 (257) hide show
  1. package/CHANGELOG.md +53 -1
  2. package/README.md +39 -6
  3. package/dist/{accordion-BRWh3BM1.js → accordion-B-5Focs8.js} +2 -2
  4. package/dist/{accordion-BRWh3BM1.js.map → accordion-B-5Focs8.js.map} +1 -1
  5. package/dist/{affordance-geometry.generated-DXl-Zb3Z.js → affordance-geometry.generated-D8Jcfb0G.js} +10 -1
  6. package/dist/affordance-geometry.generated-D8Jcfb0G.js.map +1 -0
  7. package/dist/{ai-artifact-DkOArd3-.js → ai-artifact-B5tPpXnS.js} +5 -5
  8. package/dist/{ai-artifact-DkOArd3-.js.map → ai-artifact-B5tPpXnS.js.map} +1 -1
  9. package/dist/{app-shell-BH_bGcE2.js → app-shell-at8Kt4l_.js} +3 -3
  10. package/dist/{app-shell-BH_bGcE2.js.map → app-shell-at8Kt4l_.js.map} +1 -1
  11. package/dist/{attachment-DsLgNHXt.js → attachment-BO2R47wn.js} +2 -2
  12. package/dist/{attachment-DsLgNHXt.js.map → attachment-BO2R47wn.js.map} +1 -1
  13. package/dist/{auth-BDB1V_2q.js → auth-OFwWSAap.js} +4 -4
  14. package/dist/{auth-BDB1V_2q.js.map → auth-OFwWSAap.js.map} +1 -1
  15. package/dist/{avatar-CsnWaYmt.js → avatar-Bpzty83c.js} +73 -68
  16. package/dist/avatar-Bpzty83c.js.map +1 -0
  17. package/dist/base/accordion.js +1 -1
  18. package/dist/base/attachment.js +1 -1
  19. package/dist/base/avatar.js +1 -1
  20. package/dist/base/cards.js +2 -2
  21. package/dist/base/cascader.js +6 -6
  22. package/dist/base/choice-inputs.js +3 -2
  23. package/dist/base/code-block.js +1 -1
  24. package/dist/base/combobox.js +1 -1
  25. package/dist/base/command.js +1 -1
  26. package/dist/base/date-pickers.js +2 -2
  27. package/dist/base/display.js +1 -1
  28. package/dist/base/feedback.js +4 -3
  29. package/dist/base/forms-numeric.js +1 -1
  30. package/dist/base/forms.js +2 -2
  31. package/dist/base/inline-edit.js +236 -0
  32. package/dist/base/inline-edit.js.map +1 -0
  33. package/dist/base/media-gallery.js +1 -1
  34. package/dist/base/navigation.js +3 -3
  35. package/dist/base/popover-menu.js +1 -1
  36. package/dist/base/popover.js +1 -1
  37. package/dist/base/repeaters.js +4 -4
  38. package/dist/base/sheet.js +1 -1
  39. package/dist/base/sidebar.js +1 -1
  40. package/dist/base/signature-pad.js +3 -3
  41. package/dist/base/skeleton.js +1 -1
  42. package/dist/base/spinner.js +1 -1
  43. package/dist/base/structure.js +1 -1
  44. package/dist/base/text-inputs.js +2 -2
  45. package/dist/base/tree.js +1 -1
  46. package/dist/base/upload-queue.js +1 -1
  47. package/dist/base/upload.js +3 -3
  48. package/dist/base/value-inputs.js +1 -1
  49. package/dist/blocks/admin.js +6 -6
  50. package/dist/blocks/ai.js +6 -6
  51. package/dist/blocks/analytics/usage-card.js +3 -0
  52. package/dist/blocks/analytics.js +361 -360
  53. package/dist/blocks/analytics.js.map +1 -1
  54. package/dist/blocks/collaboration.js +1 -1
  55. package/dist/blocks/commerce/receipt.js +1 -1
  56. package/dist/blocks/commerce.js +15 -15
  57. package/dist/blocks/data-display.js +1 -1
  58. package/dist/blocks/onboarding.js +3 -3
  59. package/dist/blocks/planning.js +311 -0
  60. package/dist/blocks/planning.js.map +1 -0
  61. package/dist/blocks/timelines.js +1 -1
  62. package/dist/{cards-BMB0laLG.js → cards-Cum56XOF.js} +3 -3
  63. package/dist/{cards-BMB0laLG.js.map → cards-Cum56XOF.js.map} +1 -1
  64. package/dist/checkbox-BavDRjF7.js +34 -0
  65. package/dist/checkbox-BavDRjF7.js.map +1 -0
  66. package/dist/{choice-inputs-CavA-SGV.js → choice-inputs-CC7XqPQO.js} +166 -191
  67. package/dist/choice-inputs-CC7XqPQO.js.map +1 -0
  68. package/dist/{code-block-DIlqzoiD.js → code-block-xHKbAJDj.js} +2 -2
  69. package/dist/{code-block-DIlqzoiD.js.map → code-block-xHKbAJDj.js.map} +1 -1
  70. package/dist/{combobox-HDftpzxt.js → combobox-DEbdr-3u.js} +2 -2
  71. package/dist/{combobox-HDftpzxt.js.map → combobox-DEbdr-3u.js.map} +1 -1
  72. package/dist/{combobox-CHD750do.js → combobox-xv3yJbzl.js} +7 -7
  73. package/dist/{combobox-CHD750do.js.map → combobox-xv3yJbzl.js.map} +1 -1
  74. package/dist/{command-D0kWBZfd.js → command-C0xe7u9B.js} +2 -2
  75. package/dist/{command-D0kWBZfd.js.map → command-C0xe7u9B.js.map} +1 -1
  76. package/dist/{comments-CvYeglMS.js → comments-DviPLnwy.js} +8 -8
  77. package/dist/{comments-CvYeglMS.js.map → comments-DviPLnwy.js.map} +1 -1
  78. package/dist/components/base/cards/smart-card.types.d.ts +2 -0
  79. package/dist/components/base/inline-edit/index.d.ts +2 -0
  80. package/dist/components/base/inline-edit/inline-edit.d.ts +81 -0
  81. package/dist/components/base/inline-edit/inline-edit.recipes.d.ts +6 -0
  82. package/dist/components/base/structure/structure.recipes.d.ts +6 -0
  83. package/dist/components/blocks/analytics/index.d.ts +1 -0
  84. package/dist/components/blocks/analytics/usage-card/index.d.ts +3 -0
  85. package/dist/components/blocks/analytics/usage-card/types.d.ts +52 -0
  86. package/dist/components/blocks/analytics/usage-card/usage-card.d.ts +6 -0
  87. package/dist/components/blocks/analytics/usage-card/usage-card.data.d.ts +19 -0
  88. package/dist/components/blocks/analytics/usage-card/usage-card.strings.d.ts +22 -0
  89. package/dist/components/blocks/planning/index.d.ts +3 -0
  90. package/dist/components/blocks/planning/task-card.d.ts +3 -0
  91. package/dist/components/blocks/planning/task-card.strings.d.ts +15 -0
  92. package/dist/components/blocks/planning/task-card.types.d.ts +66 -0
  93. package/dist/{containers-CPNyNo1_.js → containers-6s-ucRQQ.js} +3 -2
  94. package/dist/containers-6s-ucRQQ.js.map +1 -0
  95. package/dist/{data-display-b3d1TfL4.js → data-display-cWIRdeaf.js} +8 -8
  96. package/dist/{data-display-b3d1TfL4.js.map → data-display-cWIRdeaf.js.map} +1 -1
  97. package/dist/{date-picker-DI6kPNof.js → date-picker-CA6ogMQB.js} +5 -5
  98. package/dist/{date-picker-DI6kPNof.js.map → date-picker-CA6ogMQB.js.map} +1 -1
  99. package/dist/{date-pickers-ByP6Cv2z.js → date-pickers-BPIKLdsD.js} +4 -4
  100. package/dist/{date-pickers-ByP6Cv2z.js.map → date-pickers-BPIKLdsD.js.map} +1 -1
  101. package/dist/{display-ByI5tkqy.js → display-CFM5myut.js} +3 -3
  102. package/dist/{display-ByI5tkqy.js.map → display-CFM5myut.js.map} +1 -1
  103. package/dist/features/activities.js +4 -4
  104. package/dist/features/ai-chat.js +4 -4
  105. package/dist/features/async-preview.js +4 -4
  106. package/dist/features/combobox.js +1 -1
  107. package/dist/features/comments.js +1 -1
  108. package/dist/features/data-view.js +5 -5
  109. package/dist/features/email-composer.js +11 -11
  110. package/dist/features/event-calendar.js +2 -2
  111. package/dist/features/event-calendar.js.map +1 -1
  112. package/dist/features/filters.js +1 -1
  113. package/dist/features/global-search.js +6 -6
  114. package/dist/features/map.js +3 -3
  115. package/dist/features/media-library.js +8 -8
  116. package/dist/features/mentions.js +1 -1
  117. package/dist/features/overlays.js +1 -1
  118. package/dist/features/products.js +43 -42
  119. package/dist/features/products.js.map +1 -1
  120. package/dist/features/questionnaire.js +107 -106
  121. package/dist/features/questionnaire.js.map +1 -1
  122. package/dist/features/resource-assignment.js +5 -5
  123. package/dist/features/resource.js +7 -7
  124. package/dist/features/rich-text-editor.js +1 -1
  125. package/dist/features/schema-form.js +11 -11
  126. package/dist/features/schema-form.js.map +1 -1
  127. package/dist/features/suggestions.js +2 -2
  128. package/dist/features/sync.js +2 -2
  129. package/dist/features/table.js +1 -1
  130. package/dist/features/theme-tweaker.js +11 -11
  131. package/dist/feedback-DWUkgEjR.js +233 -0
  132. package/dist/feedback-DWUkgEjR.js.map +1 -0
  133. package/dist/{field-affordance-recipes-CHdUtn2Z.js → field-affordance-recipes-IjzPrCTw.js} +2 -2
  134. package/dist/{field-affordance-recipes-CHdUtn2Z.js.map → field-affordance-recipes-IjzPrCTw.js.map} +1 -1
  135. package/dist/{filters-qtrcv1hf.js → filters-BhsUtsxW.js} +9 -9
  136. package/dist/{filters-qtrcv1hf.js.map → filters-BhsUtsxW.js.map} +1 -1
  137. package/dist/{form-field-C6W-y2hR.js → form-field-Bn7ZH1rA.js} +2 -2
  138. package/dist/{form-field-C6W-y2hR.js.map → form-field-Bn7ZH1rA.js.map} +1 -1
  139. package/dist/{form-sizing-39NohSqn.js → form-sizing-BWs70_a2.js} +4 -3
  140. package/dist/{form-sizing-39NohSqn.js.map → form-sizing-BWs70_a2.js.map} +1 -1
  141. package/dist/{forms-BF-HSbff.js → forms-B7pzS25o.js} +5 -5
  142. package/dist/{forms-BF-HSbff.js.map → forms-B7pzS25o.js.map} +1 -1
  143. package/dist/{forms-numeric-Y15Bg5Nr.js → forms-numeric-BUBxELBv.js} +18 -18
  144. package/dist/{forms-numeric-Y15Bg5Nr.js.map → forms-numeric-BUBxELBv.js.map} +1 -1
  145. package/dist/{header-B8ex54cY.js → header-C59QZyxV.js} +6 -6
  146. package/dist/{header-B8ex54cY.js.map → header-C59QZyxV.js.map} +1 -1
  147. package/dist/{hooks-D6UBVzf2.js → hooks-CM1Vp9Pe.js} +2 -2
  148. package/dist/{hooks-D6UBVzf2.js.map → hooks-CM1Vp9Pe.js.map} +1 -1
  149. package/dist/{input-DMorJiTI.js → input-WwYQZJzG.js} +3 -3
  150. package/dist/{input-DMorJiTI.js.map → input-WwYQZJzG.js.map} +1 -1
  151. package/dist/layout/app-shell.js +1 -1
  152. package/dist/layout/auth.js +1 -1
  153. package/dist/layout/containers.js +1 -1
  154. package/dist/layout/header.js +1 -1
  155. package/dist/layout/hooks.js +1 -1
  156. package/dist/layout/navigation.js +1 -1
  157. package/dist/layout/page.js +1 -1
  158. package/dist/layout/settings.js +1 -1
  159. package/dist/layout/sidebar.js +2 -2
  160. package/dist/layout/workspace.js +1 -1
  161. package/dist/layout.js +10 -10
  162. package/dist/lib/affordance-geometry.generated.d.ts +9 -0
  163. package/dist/{media-gallery-Ca4YmvBi.js → media-gallery-DhDRgnVZ.js} +2 -2
  164. package/dist/{media-gallery-Ca4YmvBi.js.map → media-gallery-DhDRgnVZ.js.map} +1 -1
  165. package/dist/{mentions-CRio8mTm.js → mentions-B8Ko-fhS.js} +4 -4
  166. package/dist/{mentions-CRio8mTm.js.map → mentions-B8Ko-fhS.js.map} +1 -1
  167. package/dist/{message-B5-6LeYl.js → message-COsdEvXT.js} +2 -2
  168. package/dist/{message-B5-6LeYl.js.map → message-COsdEvXT.js.map} +1 -1
  169. package/dist/{navigation-DqEITuD2.js → navigation-BUIn2Qxx.js} +2 -2
  170. package/dist/{navigation-DqEITuD2.js.map → navigation-BUIn2Qxx.js.map} +1 -1
  171. package/dist/{navigation-BTrEb5u4.js → navigation-DBEu4POU.js} +3 -3
  172. package/dist/{navigation-BTrEb5u4.js.map → navigation-DBEu4POU.js.map} +1 -1
  173. package/dist/{navigation-row-recipes-BFpvGPGE.js → navigation-row-recipes-GVh11KiT.js} +2 -2
  174. package/dist/{navigation-row-recipes-BFpvGPGE.js.map → navigation-row-recipes-GVh11KiT.js.map} +1 -1
  175. package/dist/{overlays-DBqV0J_V.js → overlays-B11wZka6.js} +4 -4
  176. package/dist/{overlays-DBqV0J_V.js.map → overlays-B11wZka6.js.map} +1 -1
  177. package/dist/{page-BMBGjJY8.js → page-BV6ioQg3.js} +5 -5
  178. package/dist/{page-BMBGjJY8.js.map → page-BV6ioQg3.js.map} +1 -1
  179. package/dist/{page-header-BH41wCyY.js → page-header-DQRa0jug.js} +2 -2
  180. package/dist/{page-header-BH41wCyY.js.map → page-header-DQRa0jug.js.map} +1 -1
  181. package/dist/{popover-TInGXsjg.js → popover-CoLDEg19.js} +2 -2
  182. package/dist/{popover-TInGXsjg.js.map → popover-CoLDEg19.js.map} +1 -1
  183. package/dist/{popover-menu-BzqXqE72.js → popover-menu-GLIQTJTx.js} +4 -4
  184. package/dist/{popover-menu-BzqXqE72.js.map → popover-menu-GLIQTJTx.js.map} +1 -1
  185. package/dist/progress-BOe8l_FZ.js +168 -0
  186. package/dist/progress-BOe8l_FZ.js.map +1 -0
  187. package/dist/{receipt-DYWTQGEc.js → receipt-BM8xsGFy.js} +3 -3
  188. package/dist/{receipt-DYWTQGEc.js.map → receipt-BM8xsGFy.js.map} +1 -1
  189. package/dist/{rich-text-editor-BZSJchAF.js → rich-text-editor-CoVrTiSv.js} +2 -2
  190. package/dist/{rich-text-editor-BZSJchAF.js.map → rich-text-editor-CoVrTiSv.js.map} +1 -1
  191. package/dist/services/actions.js +1 -1
  192. package/dist/{settings-D9f_wdIC.js → settings-BnCyyPOJ.js} +3 -3
  193. package/dist/{settings-D9f_wdIC.js.map → settings-BnCyyPOJ.js.map} +1 -1
  194. package/dist/{sheet-vOUloJ4O.js → sheet-B-q914Ad.js} +2 -2
  195. package/dist/{sheet-vOUloJ4O.js.map → sheet-B-q914Ad.js.map} +1 -1
  196. package/dist/{sidebar-C3UKkbC5.js → sidebar-C_2p3Tfj.js} +6 -6
  197. package/dist/{sidebar-C3UKkbC5.js.map → sidebar-C_2p3Tfj.js.map} +1 -1
  198. package/dist/{sidebar-CNBKFDRW.js → sidebar-DPR7fmqN.js} +4 -4
  199. package/dist/{sidebar-CNBKFDRW.js.map → sidebar-DPR7fmqN.js.map} +1 -1
  200. package/dist/{skeleton-BvRhK8y-.js → skeleton-CmT1ppqb.js} +2 -2
  201. package/dist/{skeleton-BvRhK8y-.js.map → skeleton-CmT1ppqb.js.map} +1 -1
  202. package/dist/{smart-card-B5FcKODh.js → smart-card-CYLn46e6.js} +119 -118
  203. package/dist/smart-card-CYLn46e6.js.map +1 -0
  204. package/dist/{spinner-JtlFJ7Nc.js → spinner-BY9HbUEh.js} +2 -2
  205. package/dist/{spinner-JtlFJ7Nc.js.map → spinner-BY9HbUEh.js.map} +1 -1
  206. package/dist/{structure-BvW4vG4H.js → structure-MvKVq-oL.js} +13 -13
  207. package/dist/{structure-BvW4vG4H.js.map → structure-MvKVq-oL.js.map} +1 -1
  208. package/dist/{structure.recipes-DjqnBpwz.js → structure.recipes-Bqhf9oIh.js} +2 -2
  209. package/dist/structure.recipes-Bqhf9oIh.js.map +1 -0
  210. package/dist/style.css +1 -1
  211. package/dist/{table-ghtAQ-JJ.js → table-BS39xnlq.js} +6 -6
  212. package/dist/{table-ghtAQ-JJ.js.map → table-BS39xnlq.js.map} +1 -1
  213. package/dist/{tabs-DY09eoZg.js → tabs-B5cNMWiP.js} +2 -2
  214. package/dist/{tabs-DY09eoZg.js.map → tabs-B5cNMWiP.js.map} +1 -1
  215. package/dist/{text-inputs-BhOcedTo.js → text-inputs-Dh9XGyiS.js} +4 -4
  216. package/dist/{text-inputs-BhOcedTo.js.map → text-inputs-Dh9XGyiS.js.map} +1 -1
  217. package/dist/{timelines-CnZ2S_Gr.js → timelines-B9ICkQcV.js} +2 -2
  218. package/dist/{timelines-CnZ2S_Gr.js.map → timelines-B9ICkQcV.js.map} +1 -1
  219. package/dist/{tree-COD9BGdU.js → tree-BdWSaoFT.js} +2 -2
  220. package/dist/{tree-COD9BGdU.js.map → tree-BdWSaoFT.js.map} +1 -1
  221. package/dist/{upload-DIzw1dx1.js → upload-D8Za5x_c.js} +4 -4
  222. package/dist/{upload-DIzw1dx1.js.map → upload-D8Za5x_c.js.map} +1 -1
  223. package/dist/{upload-queue-xRJ3qDmd.js → upload-queue-hIIYBLS7.js} +3 -3
  224. package/dist/{upload-queue-xRJ3qDmd.js.map → upload-queue-hIIYBLS7.js.map} +1 -1
  225. package/dist/usage-card-DnmnJ58D.js +458 -0
  226. package/dist/usage-card-DnmnJ58D.js.map +1 -0
  227. package/dist/{value-inputs-Co1fu-xv.js → value-inputs-DX2dbxjj.js} +7 -7
  228. package/dist/{value-inputs-Co1fu-xv.js.map → value-inputs-DX2dbxjj.js.map} +1 -1
  229. package/dist/{workspace-Cbqz4FzN.js → workspace-C1PZmk8L.js} +4 -4
  230. package/dist/{workspace-Cbqz4FzN.js.map → workspace-C1PZmk8L.js.map} +1 -1
  231. package/docs/consumer-skill/SKILL.md +120 -17
  232. package/docs/consumer-skill/examples/detail.tsx +7 -8
  233. package/docs/consumer-skill/examples/manifest.json +14 -0
  234. package/docs/consumer-skill/examples/planning-usage-edit.tsx +29 -0
  235. package/docs/consumer-skill/examples/scoped-directory.tsx +77 -0
  236. package/docs/consumer-skill/references/composition.md +103 -0
  237. package/docs/consumer-skill/references/design-contract.md +4 -3
  238. package/docs/consumer-skill/references/discovery.md +17 -0
  239. package/docs/consumer-skill/references/installation.md +20 -3
  240. package/docs/consumer-skill/references/integration.md +12 -0
  241. package/docs/consumer-skill/references/public-api.json +452 -229
  242. package/docs/consumer-skill/references/review.md +74 -0
  243. package/docs/consumer-skill/references/tokens.json +1 -1
  244. package/docs/consumer-skill/references/workflows.md +97 -1
  245. package/docs/consumer-skill/scripts/check-composition.mjs +31 -0
  246. package/docs/consumer-skill/scripts/composition-policy.mjs +155 -0
  247. package/docs/consumer-skill/scripts/discovery.mjs +6 -1
  248. package/docs/consumer-skill/scripts/surface-policy.mjs +22 -0
  249. package/package.json +24 -3
  250. package/dist/affordance-geometry.generated-DXl-Zb3Z.js.map +0 -1
  251. package/dist/avatar-CsnWaYmt.js.map +0 -1
  252. package/dist/choice-inputs-CavA-SGV.js.map +0 -1
  253. package/dist/containers-CPNyNo1_.js.map +0 -1
  254. package/dist/feedback-a9j9J-9A.js +0 -391
  255. package/dist/feedback-a9j9J-9A.js.map +0 -1
  256. package/dist/smart-card-B5FcKODh.js.map +0 -1
  257. package/dist/structure.recipes-DjqnBpwz.js.map +0 -1
@@ -0,0 +1,74 @@
1
+ # Composition review and checks
2
+
3
+ Use after selecting owners, or when auditing a screen. Read only the relevant
4
+ workflow/declarations; this is a review aid, not a new design system.
5
+
6
+ ## Review the task and hierarchy
7
+
8
+ - Can the user identify the subject, scope and next action? A contextual fact
9
+ may be repeated for a separate purpose, but should not compete with itself.
10
+ - Do independent records, facts, metrics and editable fields use appropriate
11
+ owners? Inspect an existing feature before reconstructing its lifecycle.
12
+ - Do primary commands have explicit emphasis and page/collection/record scope?
13
+ Keep bulk actions tied to selection. Secondary/destructive importance is semantic.
14
+ - Do rows lead with identity and useful decision information? Preserve access
15
+ to hidden facts without forcing every fact into a tall identity stack.
16
+ - Does each gutter, inset and sibling/copy gap have one owner? Review the whole
17
+ parent/child chain. A token-backed utility on the wrong owner can still drift.
18
+ - Does the same composition remain readable with long copy, translations,
19
+ empty/missing values and narrow parent width? Zero and unavailable differ.
20
+
21
+ These criteria allow different product layouts. Do not impose a universal card
22
+ count, row-line limit, primary button count or one-column form rule.
23
+
24
+ ## Optional source checker
25
+
26
+ The package ships a read-only script, not an npm bin/global CLI. It needs the
27
+ consumer's TypeScript development dependency and Node matching the package engine:
28
+
29
+ ```sh
30
+ node node_modules/themelia-react-ui/docs/consumer-skill/scripts/check-composition.mjs resources/js/pages resources/js/components
31
+ ```
32
+
33
+ Pass explicit files/directories; `.tsx`, `.ts`, `.jsx` and `.js` are inspected.
34
+ It resolves TypeScript from the app working directory and uses its adjacent
35
+ versioned public index. A complete copied skill works too, after the installed
36
+ identity check. It neither edits files nor installs dependencies. Add this
37
+ command to app CI if useful; repository lint is not automatically installed.
38
+
39
+ Errors cover unknown package entrypoints/symbols and statically detected overrides
40
+ of protected popup insets or control geometry/typography. Deprecated migration
41
+ owners, nested framed subjects and repeated literal headings produce review
42
+ warnings; warnings alone do not fail CI. Messages identify the owner and remedy.
43
+ Use existing app lint/typechecking for wider language, utility and accessibility
44
+ coverage. The script is deliberately small and does not replace them.
45
+
46
+ Coverage is limited to direct public imports, aliases/namespaces, and same-file
47
+ static classes/styles/props. Dynamic expressions are reported as unresolved;
48
+ cross-file wrappers, external CSS, render functions and runtime values require
49
+ review. Do not treat an empty report as proof of complete compliance or taste.
50
+
51
+ ## Rendered and interaction evidence
52
+
53
+ Source checks establish imports and known ownership violations. Separately inspect
54
+ actual rectangles, wrapping, shared insets, title/action alignment and information
55
+ weight at supported parent widths and light/dark scopes. Open relevant menus and
56
+ overlays; a closed trigger cannot prove portal theming or popup geometry.
57
+
58
+ Execute affected callbacks, keyboard/focus, cancellation, validation, pending
59
+ and failure/retry states. Use deferred requests to observe pending behavior.
60
+ Confirm accepted data changes and draft retention; a toast is only feedback.
61
+ Compare before/after at the same conditions and record unverified limits.
62
+
63
+ ## Application policy
64
+
65
+ Keep a small app-owned reference linked from its agent instructions: name the
66
+ shell/header/gutter owners, routing/query/form adapters, scope semantics,
67
+ information priorities and relevant local tests. Link current representative
68
+ pages. Preserve permissions, transport, translations and product-specific forms.
69
+ Do not copy the package API manual or edit the packaged skill to encode local policy.
70
+
71
+ Package declarations define supported props; this skill defines shared composition
72
+ and anatomy; the app defines business meaning and page conventions. If a shared
73
+ owner fails in a valid composition, isolate a reproduction and report the owning
74
+ contract. Do not spread private selectors or copy the control to hide the issue.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "package": "themelia-react-ui",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "structural": [
5
5
  {
6
6
  "name": "--space-scale",
@@ -8,7 +8,7 @@ The [example manifest](../examples/manifest.json) owns filenames, exported compo
8
8
 
9
9
  [AccountIndex](../examples/index-table.tsx) exports `AccountIndex`, `AccountIndexProps`, `AccountRow` and `AccountIndexState`. Props are `state`, `onRetry`, `hrefFor(record)` and `onNavigate(href)`. The app drives the discriminated state: `loading`, `error` with a message, or `ready` with records. An empty ready collection renders the table's empty state. `onRetry` starts your loader; it must update app state through loading to ready/error. Stable row IDs belong to the records.
10
10
 
11
- This recipe uses `themelia-react-ui/features/table`, so install `@tanstack/react-table` as an optional peer. Card frames the page; the nested table uses `surface="flat"`. A row action calls your navigation callback with your URL; `href` alone on a table action does not navigate. Supply your router callback or ordinary browser navigation. The library neither requests records nor chooses routes.
11
+ This recipe uses `themelia-react-ui/features/table`, so install `@tanstack/react-table` as an optional peer. Card frames an independent Accounts subject; the nested table uses `surface="flat"`. For a primary route collection use its own native table surface, as in the scoped directory below. A row action calls your navigation callback with your URL; `href` alone on a table action does not navigate. Supply your router callback or ordinary browser navigation. The library neither requests records nor chooses routes.
12
12
 
13
13
  Verify loading, empty refresh, failure/retry and ready records. Open the row menu with pointer and keyboard, activate Open account, and assert the requested URL. Check narrow overflow and email-link behavior. If you need a presentational table without TanStack features, discover `themelia-react-ui/base/table` and inspect its separate declaration.
14
14
 
@@ -18,6 +18,51 @@ Verify loading, empty refresh, failure/retry and ready records. Open the row men
18
18
 
19
19
  No optional peers are needed. The app supplies the loaded record and owns navigation, locale/provider defaults and permissions. Verify missing email/date/balance, a real minor-unit amount, Edit/Back callbacks, and the one-column mobile/two-column desktop composition in light/dark themes.
20
20
 
21
+ ## Scoped directory
22
+
23
+ [ScopedDirectory](../examples/scoped-directory.tsx) demonstrates a primary route
24
+ collection. Page owns Contacts, the fixed business context and the explicit
25
+ primary New contact command. DataTable owns the surface; FilterLayout occupies
26
+ `topbarContent`, optional export occupies `topbarEnd`, and Open contact is a row
27
+ action. ResourceCell owns identity, shared small secondary Text supplies role,
28
+ and Email owns formatting. Other operational facts belong in the contact detail
29
+ reached by `onOpen`; the example does not append every fact to compact identity.
30
+
31
+ The optional `compact` prop is an app presentation decision from available parent
32
+ width. It keeps email reach in the identity cell and omits the comparison Email
33
+ column, preserving one copy and reachable row actions. This deliberately retains
34
+ one task-critical fact; other metadata stays in detail. Choose the threshold
35
+ from actual content and work-region measurements, not a universal library setting.
36
+
37
+ The app supplies `scope`, discriminated `state`, controlled `filters`, navigation
38
+ and loading callbacks. `onQueryChange({ scopeId, filters })` preserves fixed
39
+ business context even when criteria clear. It does not request records itself.
40
+ The recipe removes a blank `q` search criterion without normalizing unrelated
41
+ filter types. Native search can emit an empty string; the app owns query meaning.
42
+ The app updates filters/state, ignores stale responses and preserves scope/query
43
+ in pagination URLs. A criteria-driven business selector would be a different
44
+ product contract. The recipe's English labels are example copy: adapt labels,
45
+ criteria and component `strings` for app translation and permissions.
46
+
47
+ Strict imported declarations require the manifest's optional peers. DataTable
48
+ sorting is disabled because the app's server owns query order. Loading/error
49
+ feedback occupies the empty result region while query context remains mounted;
50
+ accepted records replace it. Use the public loading/preview owner if retaining
51
+ previous results is required by your app. Avoid stale pagination metadata during
52
+ loading/errors. Run the same query with loading, failure, retry and empty results.
53
+
54
+ Verify one contextual lead, primary command, query/clear scope retention,
55
+ row-menu navigation, server page callbacks and long-name/mobile containment.
56
+ Opening a record preserves access to supporting facts. Integrated queries and
57
+ separate query regions are both supported; choose by task and keep one owner.
58
+
59
+ Keep DataTable column/action definitions stable when unrelated parent feedback
60
+ changes. Changing a cell renderer's identity can remount its menu trigger and
61
+ lose return focus. This recipe memoizes row actions by `onOpen`; the app keeps
62
+ that callback stable when its meaning is unchanged. Stabilize these lifecycle
63
+ inputs deliberately, not every object. A real record/scope change can replace
64
+ definitions; the app then chooses appropriate destination focus.
65
+
21
66
  ## Validated editing
22
67
 
23
68
  [AccountEdit](../examples/edit.tsx) exports `AccountEdit`, `AccountEditProps` and `AccountEditValue`. Supply `initial`, `onSave(value): Promise<AccountEditValue>` and `onCancel()`. Mount with `key={initial.id}` when switching records. The draft is local to that mount; resetting the same record's draft deliberately also requires a new key or your own controlled adaptation.
@@ -37,3 +82,54 @@ Your delete callback owns persistence and record removal. Reject failures instea
37
82
  Verify open/title/description, trapped keyboard focus, Cancel/Escape without a deletion callback, pending dismissal prevention, rejected callback with an open error, retry and successful closure, exactly one backend effect, and trigger focus restoration. Check the opened error dialog in dark/mobile as well as light/desktop. Do not infer these interactions from typechecking or screenshots.
38
83
 
39
84
  The existing [AccountComposition](../examples/composition.tsx) remains a basic Card/ActionDialog composition with `save(name): Promise<void>`. Its callback must reject on failure; the app owns persistence. The four recipes above add progressive collection/detail/edit/delete ownership without changing that example's contract.
85
+
86
+ ## Task summaries, usage and inline editing
87
+
88
+ [Workspace summary](../examples/planning-usage-edit.tsx) composes three focused
89
+ owners. `TaskCard` from `themelia-react-ui/blocks/planning` owns the explicit Card
90
+ surface, completion summary, disclosure and controlled subtask proposals.
91
+ Update `items` after `onItemCompletedChange`; supply each item's `busy` or
92
+ `error` from your persistence lifecycle. Workflow status, due-date formatting,
93
+ assignment and Open navigation remain application data/callbacks.
94
+
95
+ Click passive header content or use the native caret (pointer, Enter or Space)
96
+ to request expansion. Both respect `expanded`/`onExpandedChange` and `disabled`;
97
+ header clicks focus the caret. Subtask and footer actions remain independent.
98
+ The block owns the segmented progress drawing, completion strikethrough and
99
+ compact checklist insets. Its progress value follows accepted `items`, including
100
+ undo; workflow status stays separate. Reduced motion disables the ring animation.
101
+
102
+ `UsageCard` from `themelia-react-ui/blocks/analytics/usage-card` is a standalone
103
+ quota/analytics composition with no optional chart dependency. Supply matching
104
+ `data`, `period` and `status` after `onPeriodChange`; cancel or ignore stale
105
+ application requests. `limit: null` means unlimited, omitted limits or values
106
+ mean unavailable, and zero is real data. Category percentages describe shares
107
+ of reported usage; bounded graphics never hide actual over-quota amounts.
108
+ Daily data is chronological application data with text equivalents. The block
109
+ owns no subscription or billing requests; `footerAction` is application content.
110
+
111
+ `InlineEdit` from `themelia-react-ui/base/inline-edit` uses our native Input or
112
+ Textarea. Supply `onSave(value, { signal })` for asynchronous persistence and
113
+ update a controlled saved value through `onValueChange`. Required/validation
114
+ checks apply when saving a draft. Parent forms serialize the committed value
115
+ through `name`; they own validation of their complete stored data. Enter saves
116
+ a single line, Control/Command Enter saves multiline text, and Escape cancels.
117
+ Cancellation invalidates late UI results and aborts the supplied signal; the
118
+ application owns actual transport and server effects. Read-only display stays
119
+ passive. Use Card at a composition boundary for a framed field section.
120
+
121
+ Display and editing share the Input frame and `size`, `invalid`, `loading`,
122
+ `clearable`, `onClear` and `showCharacterCount` APIs. The stable field/action
123
+ grid transitions width and honors reduced motion; multiline actions form a
124
+ centered column. Use `inputProps`/`textareaProps` for native editor attributes,
125
+ adornments, resizing and control-specific strings. State/value/ref/form ownership
126
+ stays with InlineEdit. Native constraints run on Save; uncommitted controls are
127
+ isolated from the parent form. External loading retains a draft while disabling
128
+ edits/save, and leaves cancellation available.
129
+
130
+ The field/action gap uses the existing `lg` spacing step; the action group uses
131
+ `xs`. Single-line actions form a horizontal pair and multiline actions a centered
132
+ column. Keep these library-owned controls and geometry instead of replacing
133
+ them with app-styled fields. Verify cancellation during persistence, retained
134
+ drafts after rejection, native constraints, committed FormData, controlled
135
+ expansion, checkbox/Open independence and async period retry in your app.
@@ -0,0 +1,31 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { createRequire } from 'node:module';
4
+ import { validateIndex } from './discovery.mjs';
5
+ import { analyzeComposition } from './composition-policy.mjs';
6
+
7
+ try {
8
+ const targets = process.argv.slice(2);
9
+ if (!targets.length || targets.some(target => target.startsWith('-'))) throw new Error('Pass explicit source files/directories; no flags or automatic app-wide scan.');
10
+ const requireFromApp = createRequire(path.join(process.cwd(), 'package.json'));
11
+ const ts = requireFromApp('typescript');
12
+ const index = validateIndex(JSON.parse(fs.readFileSync(new URL('../references/public-api.json', import.meta.url), 'utf8')));
13
+ const files = new Set();
14
+ function collect(target) {
15
+ const info = fs.lstatSync(target);
16
+ if (info.isSymbolicLink()) throw new Error(`Use a real source path instead of a symlink: ${target}`);
17
+ if (info.isDirectory()) {
18
+ for (const entry of fs.readdirSync(target).sort()) if (!['node_modules', '.git', 'dist', 'build'].includes(entry)) collect(path.join(target, entry));
19
+ } else if (/\.[jt]sx?$/.test(target) && !/\.d\.ts$/.test(target)) files.add(path.resolve(target));
20
+ }
21
+ targets.forEach(collect);
22
+ if (!files.size) throw new Error('No supported source files found in the supplied paths.');
23
+ const reports = [...files].sort().map(filename => analyzeComposition(fs.readFileSync(filename, 'utf8'), { filename, index, ts }));
24
+ const findings = reports.flatMap(report => report.findings);
25
+ const errors = findings.filter(finding => finding.severity === 'error').length;
26
+ process.stdout.write(JSON.stringify({ package: index.package, version: index.version, files: files.size, errors, warnings: findings.length - errors, findings, unresolved: reports.flatMap(report => report.unresolved), limits: 'Direct public imports and same-file static JSX only; external CSS, cross-file wrappers and dynamic values require review.' }, null, 2) + '\n');
27
+ process.exitCode = errors ? 1 : 0;
28
+ } catch (error) {
29
+ process.stderr.write(`check-composition: ${error.message}\nRun from the consumer app with its TypeScript development dependency installed.\n`);
30
+ process.exitCode = 1;
31
+ }
@@ -0,0 +1,155 @@
1
+ import { surfaceParts, surfaceForwarders, paddingClass, paddingProperty, utility } from './surface-policy.mjs';
2
+
3
+ const controlNames = /^(?:Base)?(?:Button|ButtonLink|IconButton)$|^(Input|InputGroupInput|Textarea|SelectTrigger|ComboboxTrigger)$/;
4
+ const controlFamilies = new Set(['buttons', 'text-inputs', 'choice-inputs', 'combobox']);
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)$/;
7
+
8
+ /** Small static review: TypeScript is supplied by the app, never a runtime peer. */
9
+ export function analyzeComposition(text, { filename, index, ts }) {
10
+ const kind = /\.jsx?$/.test(filename) ? ts.ScriptKind.JSX : filename.endsWith('.tsx') ? ts.ScriptKind.TSX : ts.ScriptKind.TS;
11
+ const source = ts.createSourceFile(filename, text, ts.ScriptTarget.Latest, true, kind);
12
+ const families = new Map(index.families.map(owner => [owner.import, owner]));
13
+ const bindings = new Map();
14
+ const namespaces = new Map();
15
+ const declarations = new Map();
16
+ const findings = [];
17
+ const unresolved = [];
18
+ const seen = new Set();
19
+ const add = (node, rule, severity, message) => {
20
+ const position = source.getLineAndCharacterOfPosition(node.getStart(source));
21
+ const key = `${node.pos}:${rule}`;
22
+ if (seen.has(key)) return;
23
+ seen.add(key);
24
+ findings.push({ file: filename, line: position.line + 1, column: position.character + 1, rule, severity, message });
25
+ };
26
+ const unknown = node => {
27
+ const key = `${node.pos}:unresolved`;
28
+ if (seen.has(key)) return;
29
+ seen.add(key);
30
+ const position = source.getLineAndCharacterOfPosition(node.getStart(source));
31
+ unresolved.push({ file: filename, line: position.line + 1, expression: node.getText(source).slice(0, 120) });
32
+ };
33
+ const resolve = (node, visiting = new Set()) => {
34
+ if (!node || visiting.has(node)) return undefined;
35
+ visiting.add(node);
36
+ if (ts.isIdentifier(node)) return resolve(declarations.get(node.text), visiting);
37
+ if (ts.isParenthesizedExpression(node) || ts.isAsExpression(node) || ts.isSatisfiesExpression(node)) return resolve(node.expression, visiting);
38
+ return node;
39
+ };
40
+ const properties = (expression, mark = true, visited = new Set()) => {
41
+ const node = resolve(expression);
42
+ if (!node || !ts.isObjectLiteralExpression(node) || visited.has(node)) {
43
+ if (mark && expression) unknown(expression);
44
+ return [];
45
+ }
46
+ visited.add(node);
47
+ return node.properties.flatMap(property => {
48
+ if (ts.isSpreadAssignment(property)) return properties(property.expression, mark, visited);
49
+ const name = property.name;
50
+ const key = ts.isComputedPropertyName(name) ? resolve(name.expression) : name;
51
+ if (!key || !(ts.isIdentifier(key) || ts.isStringLiteralLike(key))) {
52
+ if (mark) unknown(property);
53
+ return [];
54
+ }
55
+ return [[key.text, ts.isShorthandPropertyAssignment(property) ? property.name : property.initializer]];
56
+ });
57
+ };
58
+ const classStrings = (expression, visited = new Set()) => {
59
+ const node = resolve(expression);
60
+ if (!node || visited.has(node)) { if (expression) unknown(expression); return []; }
61
+ visited.add(node);
62
+ if (ts.isStringLiteralLike(node)) return [node.text];
63
+ if (ts.isConditionalExpression(node)) return [...classStrings(node.whenTrue, visited), ...classStrings(node.whenFalse, visited)];
64
+ if (ts.isBinaryExpression(node) && [ts.SyntaxKind.AmpersandAmpersandToken, ts.SyntaxKind.BarBarToken, ts.SyntaxKind.QuestionQuestionToken].includes(node.operatorToken.kind)) return classStrings(node.right, visited);
65
+ if (ts.isArrayLiteralExpression(node)) return node.elements.flatMap(item => classStrings(item, visited));
66
+ if (ts.isCallExpression(node) && /^(cn|clsx|classNames|twMerge)$/.test(node.expression.getText(source))) return node.arguments.flatMap(item => classStrings(item, visited));
67
+ if ([ts.SyntaxKind.FalseKeyword, ts.SyntaxKind.TrueKeyword, ts.SyntaxKind.NullKeyword].includes(node.kind)) return [];
68
+ unknown(expression);
69
+ return [];
70
+ };
71
+ const literal = expression => {
72
+ const node = resolve(expression);
73
+ return node && ts.isStringLiteralLike(node) ? node.text : undefined;
74
+ };
75
+ function importOwner(node, specifier) {
76
+ if (specifier !== index.package && !specifier.startsWith(index.package + '/')) return;
77
+ if (index.stylesheets?.includes(specifier)) return;
78
+ const family = families.get(specifier);
79
+ if (!family) { add(node, 'public-import', 'error', `No published owner for ${specifier}; discover the version-matched public entrypoint.`); return; }
80
+ const names = ts.isImportDeclaration(node) ? node.importClause?.namedBindings : node.exportClause;
81
+ if (ts.isImportDeclaration(node) && node.importClause?.name && !family.symbols.includes('default')) add(node.importClause.name, 'public-symbol', 'error', `${specifier} has no default export; use its named public symbols.`);
82
+ const elements = names && (ts.isNamedImports(names) || ts.isNamedExports(names)) ? names.elements : [];
83
+ for (const item of elements) {
84
+ const symbol = (item.propertyName ?? item.name).text;
85
+ 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 });
87
+ }
88
+ if (names && ts.isNamespaceImport(names)) namespaces.set(names.name.text, specifier);
89
+ }
90
+ function gather(node) {
91
+ if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer) declarations.set(node.name.text, node.initializer);
92
+ if ((ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) && node.moduleSpecifier && ts.isStringLiteralLike(node.moduleSpecifier)) importOwner(node, node.moduleSpecifier.text);
93
+ if (ts.isImportTypeNode(node) && ts.isLiteralTypeNode(node.argument) && ts.isStringLiteralLike(node.argument.literal)) importOwner(node, node.argument.literal.text);
94
+ if (ts.isCallExpression(node) && node.expression.kind === ts.SyntaxKind.ImportKeyword && ts.isStringLiteralLike(node.arguments[0])) importOwner(node, node.arguments[0].text);
95
+ ts.forEachChild(node, gather);
96
+ }
97
+ gather(source);
98
+ for (const diagnostic of source.parseDiagnostics) add(source, 'syntax', 'error', ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'));
99
+ 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;
104
+ };
105
+ const attributes = node => node.attributes.properties.flatMap(attribute => {
106
+ if (ts.isJsxSpreadAttribute(attribute)) return properties(attribute.expression);
107
+ if (!attribute.initializer) return [[attribute.name.getText(source), undefined]];
108
+ const expression = ts.isJsxExpression(attribute.initializer) ? attribute.initializer.expression : attribute.initializer;
109
+ return [[attribute.name.getText(source), expression]];
110
+ });
111
+ function visit(node, parents = []) {
112
+ if (ts.isJsxElement(node)) {
113
+ const element = node.openingElement;
114
+ const context = inspect(element, parents);
115
+ for (const child of node.children) visit(child, context ? [...parents, context] : parents);
116
+ // JSX in props is a separate region, not a child frame.
117
+ for (const attribute of element.attributes.properties) ts.forEachChild(attribute, child => visit(child, parents));
118
+ return;
119
+ }
120
+ if (ts.isJsxSelfClosingElement(node)) inspect(node, parents);
121
+ ts.forEachChild(node, child => visit(child, parents));
122
+ }
123
+ function inspect(node, parents) {
124
+ const owner = ownerFor(node.tagName.getText(source));
125
+ if (!owner) return;
126
+ if (!families.get(owner.import)?.symbols.includes(owner.symbol)) {
127
+ add(node, 'public-symbol', 'error', `${owner.import} does not export ${owner.symbol}.`);
128
+ return;
129
+ }
130
+ const props = new Map(attributes(node));
131
+ const family = owner.import.split('/base/')[1];
132
+ const anatomy = surfaceParts[family]?.test(owner.symbol);
133
+ const forwarder = surfaceForwarders[family] === owner.symbol;
134
+ const control = controlFamilies.has(family) && controlNames.test(owner.symbol);
135
+ for (const name of ['className', 'contentClassName']) {
136
+ if (!props.has(name)) continue;
137
+ const classes = classStrings(props.get(name)).flatMap(value => value.split(/\s+/)).map(utility);
138
+ 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
+ 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.`);
140
+ }
141
+ if (props.has('style')) for (const [name] of properties(props.get('style'))) {
142
+ if (anatomy && paddingProperty.test(name)) add(node, 'surface-padding', 'error', `${owner.symbol}: ${name} replaces the region inset; use its public surface props.`);
143
+ if (control && (paddingProperty.test(name) || controlStyle.test(name))) add(node, 'control-anatomy', 'error', `${owner.symbol}: ${name} belongs to shared control geometry/typography.`);
144
+ }
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';
147
+ 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
+ const header = properties(props.get('header'), false);
149
+ const title = literal(props.get('title')) ?? literal(new Map(header).get('title'));
150
+ 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 };
152
+ }
153
+ visit(source);
154
+ return { findings, unresolved };
155
+ }
@@ -8,6 +8,7 @@ const compare = (a, b) => a < b ? -1 : a > b ? 1 : 0;
8
8
  export function validateIndex(index) {
9
9
  requireFormat(index?.schemaVersion === 1 && index?.discoveryVersion === 1, 'unsupported schema/format; expected schemaVersion 1 and discoveryVersion 1');
10
10
  requireFormat(text(index.package) && text(index.version) && Array.isArray(index.families), 'package, version and families are required');
11
+ if (index.stylesheets !== undefined) requireFormat(stringList(index.stylesheets) && index.stylesheets.every(file => file.startsWith(index.package + '/') && file.endsWith('.css') && !file.includes('..')), 'invalid public stylesheets');
11
12
  const seen = new Set();
12
13
  for (const family of index.families) {
13
14
  requireFormat(text(family?.import) && (family.import === index.package || family.import.startsWith(`${index.package}/`)) && !/[\\\s]|(?:^|\/)\.\.(?:\/|$)/.test(family.import), 'invalid public import');
@@ -70,7 +71,11 @@ export function findComponents(index, { query, layer = null, limit = 5 }) {
70
71
  // AND matching prevents a familiar term from inventing results for an
71
72
  // otherwise unknown query. Avoidance/alternatives do not recommend owners.
72
73
  if (!exactImport && !matchedSymbols.length && !terms.every(term => haystack.includes(term))) continue;
73
- const score = exactImport ? 10000 : matchedSymbols.length ? 9000 : 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);
74
+ // A dedicated, documented owner precedes an aggregate re-export of the
75
+ // 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)));
77
+ 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);
74
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);
75
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 });
76
81
  }
@@ -0,0 +1,22 @@
1
+ // One public anatomy vocabulary, shared by repository and opt-in consumer checks.
2
+ export const surfaceParts = {
3
+ popover: /^(PopoverContent|PopoverHeader|PopoverFooter)$/,
4
+ command: /^Command(Input|List|Group|Item|Empty|Separator)?$/,
5
+ navigation: /^DropdownMenu(Content|SubContent|Label|Item|LinkItem|SubTrigger|CheckboxItem|RadioItem|Separator)$/,
6
+ 'choice-inputs': /^SelectPopup(Content|Group|Item|Label|Separator)?$/,
7
+ combobox: /^Combobox(Popup|Content|List|Item|GroupLabel|Separator)$/,
8
+ };
9
+ export const surfaceForwarders = { 'choice-inputs': 'Select', 'popover-menu': 'PopoverMenu', 'action-menu': 'ActionMenu' };
10
+ export const paddingClass = /^(?:p[xytrblse]?|p(?:bs|be)|padding)-|^\[padding/;
11
+ export const paddingProperty = /^padding(?:Inline|Block)?(?:Start|End)?$|^padding(?:Top|Right|Bottom|Left)$|^padding-(?:(?:inline|block)(?:-(?:start|end))?|top|right|bottom|left)$/;
12
+
13
+ export function utility(token) {
14
+ let depth = 0;
15
+ let start = 0;
16
+ for (let i = 0; i < token.length; i++) {
17
+ if (token[i] === '[' || token[i] === '(') depth++;
18
+ if (token[i] === ']' || token[i] === ')') depth--;
19
+ if (token[i] === ':' && depth === 0) start = i + 1;
20
+ }
21
+ return token.slice(start).replace(/^!|!$/g, '');
22
+ }
package/package.json CHANGED
@@ -1,12 +1,21 @@
1
1
  {
2
2
  "name": "themelia-react-ui",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "A composable React UI library for modern admin applications, built on Base UI and Tailwind with shadcn-compatible themes, shared design tokens, reusable layouts, blocks and workflows.",
5
5
  "publishConfig": {
6
6
  "access": "public",
7
7
  "registry": "https://registry.npmjs.org/"
8
8
  },
9
- "keywords": ["react", "ui", "admin", "dashboard", "base-ui", "tailwindcss", "shadcn", "design-system"],
9
+ "keywords": [
10
+ "react",
11
+ "ui",
12
+ "admin",
13
+ "dashboard",
14
+ "base-ui",
15
+ "tailwindcss",
16
+ "shadcn",
17
+ "design-system"
18
+ ],
10
19
  "repository": {
11
20
  "type": "git",
12
21
  "url": "git+https://github.com/nicolasvlachos/themelia-react-ui.git"
@@ -413,6 +422,18 @@
413
422
  "./features/questionnaire": {
414
423
  "types": "./dist/components/features/questionnaire/index.d.ts",
415
424
  "import": "./dist/features/questionnaire.js"
425
+ },
426
+ "./base/inline-edit": {
427
+ "types": "./dist/components/base/inline-edit/index.d.ts",
428
+ "import": "./dist/base/inline-edit.js"
429
+ },
430
+ "./blocks/planning": {
431
+ "types": "./dist/components/blocks/planning/index.d.ts",
432
+ "import": "./dist/blocks/planning.js"
433
+ },
434
+ "./blocks/analytics/usage-card": {
435
+ "types": "./dist/components/blocks/analytics/usage-card/index.d.ts",
436
+ "import": "./dist/blocks/analytics/usage-card.js"
416
437
  }
417
438
  },
418
439
  "scripts": {
@@ -442,7 +463,7 @@
442
463
  "check:maps": "node scripts/test-client-boundaries.mjs --built",
443
464
  "build:docs": "node scripts/build-static-docs.mjs",
444
465
  "check:static-docs": "node scripts/check-static-docs.mjs",
445
- "check:agent": "node --test scripts/test-consumer-discovery.mjs scripts/test-consumer-skill-assets.mjs scripts/test-static-docs.mjs scripts/test-fixture-install.mjs && npm run check:skill",
466
+ "check:agent": "node --test scripts/test-consumer-discovery.mjs scripts/test-consumer-composition.mjs scripts/test-consumer-skill-assets.mjs scripts/test-static-docs.mjs scripts/test-fixture-install.mjs && npm run check:skill",
446
467
  "check:release": "npm run check:all && npm run check:agent && npm run build:docs && npm run check:static-docs"
447
468
  },
448
469
  "peerDependencies": {
@@ -1 +0,0 @@
1
- {"version":3,"file":"affordance-geometry.generated-DXl-Zb3Z.js","names":["affordanceGeometry","\"navigationGuideStart\"","\"clearEnd\"","\"sm\"","\"md\"","\"lg\"","\"clearPadding\"","\"togglePadding\"","\"largeIconStart\"","\"largeIconEnd\"","\"overlayClose\"","\"overlayHeader\""],"sources":["../src/lib/affordance-geometry.generated.ts"],"sourcesContent":["/* Generated by scripts/primitive-geometry.mjs; recipes, not theme roots. */\nexport const affordanceGeometry = {\n \"navigationGuideStart\": \"ms-[calc(var(--control-x)*0.8333333*var(--space-scale)+var(--icon)*0.75*var(--space-scale)/2)]\",\n \"clearEnd\": {\n \"sm\": \"end-[calc(var(--control-x)*0.8333333*var(--space-scale))]\",\n \"md\": \"end-[calc(var(--control-x)*var(--space-scale))]\",\n \"lg\": \"end-[calc(var(--control-x)*var(--space-scale))]\"\n },\n \"clearPadding\": {\n \"sm\": \"pe-[calc(var(--control-x)*0.8333333*var(--space-scale)+1.375rem*var(--density-scale)+0.375rem*var(--space-scale))]\",\n \"md\": \"pe-[calc(var(--control-x)*var(--space-scale)+1.375rem*var(--density-scale)+0.375rem*var(--space-scale))]\",\n \"lg\": \"pe-[calc(var(--control-x)*var(--space-scale)+1.375rem*var(--density-scale)+0.375rem*var(--space-scale))]\"\n },\n \"togglePadding\": \"pe-[calc(1.375rem*var(--density-scale)+0.625rem*var(--space-scale))]\",\n \"largeIconStart\": \"ps-[calc((var(--control-x)+var(--icon)*1.25+0.5rem)*var(--space-scale))]\",\n \"largeIconEnd\": \"pe-[calc((var(--control-x)+var(--icon)*1.25+0.5rem)*var(--space-scale))]\",\n \"overlayClose\": \"absolute top-[calc(var(--surface-y)*var(--space-scale))] end-[calc(var(--surface-x)*var(--space-scale))]\",\n \"overlayHeader\": \"[[data-slot$=-content]:has(>[data-overlay-close])>&]:pe-[calc(var(--surface-x)*var(--space-scale)+var(--height-action)*0.8888889*var(--density-scale)+0.5rem*var(--space-scale))]\"\n} as const;\n"],"mappings":";AACA,IAAaA,IAAqB;CAChCC,sBAAwB;CACxBC,UAAY;EACVC,IAAM;EACNC,IAAM;EACNC,IAAM;CACR;CACAC,cAAgB;EACdH,IAAM;EACNC,IAAM;EACNC,IAAM;CACR;CACAE,eAAiB;CACjBC,gBAAkB;CAClBC,cAAgB;CAChBC,cAAgB;CAChBC,eAAiB;AACnB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"avatar-CsnWaYmt.js","names":["Avatar","AvatarPrimitive","AvatarImage","AvatarFallback","AvatarBadge","AvatarGroup","AvatarGroupCount","Avatar","React","PrimitiveAvatar","PrimitiveAvatarImage","PrimitiveAvatarFallback","PrimitiveAvatarBadge","PrimitiveAvatarGroup","PrimitiveAvatarGroupCount","Avatar","resolveComponentScale","useDensityConfig","cn","Text","formatInitials","React$1","Avatar$2","props","Avatar","size","className","AvatarImage$1","AvatarFallback$1","AvatarBadge$1","AvatarGroup$1","AvatarGroupCount$1","AVATAR_SIZE_CLASSNAMES","getPrimitiveSize","Avatar$1","sizeProp","defaultScale","AvatarImage","AvatarFallback","children","AvatarBadge","AvatarGroup","AvatarGroupCount","FALLBACK_TONES","FALLBACK_TONE_CLASSNAMES","pickFallbackTone","override","seed","s","c","StackedAvatars","visible","users","max","overflow","overflowContent","overflowFormatter","initials","user","fallbackTone","index","avatarClassName","showOverflow"],"sources":["../src/components/ui/avatar.tsx","../src/components/base/avatar/avatar.tsx","../src/components/base/avatar/stacked-avatars.tsx"],"sourcesContent":["import { Avatar as AvatarPrimitive } from \"@base-ui/react/avatar\"\nimport * as React from \"react\"\n\nimport { cn } from \"../../lib/utils\"\n\nfunction Avatar({\n className,\n size = \"default\",\n ...props\n}: AvatarPrimitive.Root.Props & {\n size?: \"default\" | \"sm\" | \"lg\"\n}) {\n return (\n <AvatarPrimitive.Root\n data-slot=\"avatar\"\n data-size={size}\n className={cn(\n \"size-8 rounded-full after:rounded-full data-[size=lg]:size-10 data-[size=sm]:size-6 after:border-border group/avatar relative flex shrink-0 select-none after:absolute after:inset-0 after:border after:mix-blend-darken dark:after:mix-blend-lighten\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction AvatarImage({ className, ...props }: AvatarPrimitive.Image.Props) {\n return (\n <AvatarPrimitive.Image\n data-slot=\"avatar-image\"\n className={cn(\n \"rounded-full aspect-square size-full object-cover\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction AvatarFallback({\n className,\n ...props\n}: AvatarPrimitive.Fallback.Props) {\n return (\n <AvatarPrimitive.Fallback\n data-slot=\"avatar-fallback\"\n className={cn(\n \"bg-muted text-muted-foreground rounded-full flex size-full items-center justify-center text-sm group-data-[size=sm]/avatar:text-xs\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction AvatarBadge({ className, ...props }: React.ComponentProps<\"span\">) {\n return (\n <span\n data-slot=\"avatar-badge\"\n className={cn(\n \"bg-primary text-primary-foreground ring-background absolute right-0 bottom-0 z-10 inline-flex items-center justify-center rounded-full bg-blend-color ring-2 select-none\",\n \"group-data-[size=sm]/avatar:size-2 group-data-[size=sm]/avatar:[&>svg]:hidden\",\n \"group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2\",\n \"group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction AvatarGroup({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"avatar-group\"\n className={cn(\n \"*:data-[slot=avatar]:ring-background group/avatar-group flex -space-x-2 *:data-[slot=avatar]:ring-2\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction AvatarGroupCount({\n className,\n ...props\n}: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"avatar-group-count\"\n className={cn(\"bg-muted text-muted-foreground size-8 rounded-full text-sm group-has-data-[size=lg]/avatar-group:size-10 group-has-data-[size=sm]/avatar-group:size-6 [&>svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3 ring-background relative flex shrink-0 items-center justify-center ring-2\", className)}\n {...props}\n />\n )\n}\n\nexport {\n Avatar,\n AvatarImage,\n AvatarFallback,\n AvatarGroup,\n AvatarGroupCount,\n AvatarBadge,\n}\n","/**\n * Avatar — tokenized base wrapper around the shadcn/Base UI avatar primitive.\n * Public sizing uses the shared component scale.\n */\nimport * as React from 'react';\n\nimport {\n\tAvatar as PrimitiveAvatar,\n\tAvatarBadge as PrimitiveAvatarBadge,\n\tAvatarFallback as PrimitiveAvatarFallback,\n\tAvatarGroup as PrimitiveAvatarGroup,\n\tAvatarGroupCount as PrimitiveAvatarGroupCount,\n\tAvatarImage as PrimitiveAvatarImage,\n} from '../../ui/avatar';\nimport { useDensityConfig, type ComponentScale } from '../../../lib/ui-provider';\nimport { resolveComponentScale } from '../../../lib/ui-provider/scale';\nimport { Text } from '../../typography';\nimport { cn } from '../../../lib/utils';\n\nexport type AvatarSize = ComponentScale;\n\nexport interface AvatarProps\n\textends Omit<React.ComponentPropsWithoutRef<typeof PrimitiveAvatar>, 'size'> {\n\t/** Size preset. */\n\tsize?: AvatarSize;\n}\n\nexport type AvatarImageProps = React.ComponentPropsWithoutRef<typeof PrimitiveAvatarImage>;\nexport type AvatarFallbackProps = React.ComponentPropsWithoutRef<typeof PrimitiveAvatarFallback>;\nexport type AvatarBadgeProps = React.ComponentPropsWithoutRef<typeof PrimitiveAvatarBadge>;\nexport type AvatarGroupProps = React.ComponentPropsWithoutRef<typeof PrimitiveAvatarGroup>;\nexport type AvatarGroupCountProps = React.ComponentPropsWithoutRef<typeof PrimitiveAvatarGroupCount>;\n\nconst AVATAR_SIZE_CLASSNAMES: Record<AvatarSize, string> = {\n\tsm: 'size-avatar-sm',\n\tmd: 'size-avatar',\n\tlg: 'size-avatar-lg',\n};\n\nfunction getPrimitiveSize(size: AvatarSize): NonNullable<React.ComponentPropsWithoutRef<typeof PrimitiveAvatar>['size']> {\n\tif (size === 'sm') return 'sm';\n\tif (size === 'lg') return 'lg';\n\treturn 'default';\n}\n\nconst Avatar = React.forwardRef<React.ElementRef<typeof PrimitiveAvatar>, AvatarProps>(\n\t({ size: sizeProp, className, ...props }, ref) => {\n\t\tconst { defaultScale } = useDensityConfig();\n\t\tconst size = resolveComponentScale(sizeProp, defaultScale);\n\t\treturn (\n\t\t\t<PrimitiveAvatar\n\t\t\t\tref={ref}\n\t\t\t\tsize={getPrimitiveSize(size)}\n\t\t\t\tdata-scale={size}\n\t\t\t\tclassName={cn('avatar--component', AVATAR_SIZE_CLASSNAMES[size], className)}\n\t\t\t\t{...props}\n\t\t\t/>\n\t\t);\n\t},\n);\nAvatar.displayName = 'Avatar';\n\nconst AvatarImage = React.forwardRef<React.ElementRef<typeof PrimitiveAvatarImage>, AvatarImageProps>(\n\t({ className, ...props }, ref) => (\n\t\t<PrimitiveAvatarImage\n\t\t\tref={ref}\n\t\t\tclassName={cn('avatar--image', className)}\n\t\t\t{...props}\n\t\t/>\n\t),\n);\nAvatarImage.displayName = 'AvatarImage';\n\nconst AvatarFallback = React.forwardRef<React.ElementRef<typeof PrimitiveAvatarFallback>, AvatarFallbackProps>(\n\t({ className, children, ...props }, ref) => (\n\t\t<PrimitiveAvatarFallback\n\t\t\tref={ref}\n\t\t\tclassName={cn('avatar--fallback text-xs font-medium leading-none', className)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{typeof children === 'string' || typeof children === 'number' || typeof children === 'bigint' ? (\n\t\t\t\t<Text tag=\"span\" size=\"inherit\" weight=\"medium\" lineHeight=\"none\" className=\"text-inherit\">\n\t\t\t\t\t{children}\n\t\t\t\t</Text>\n\t\t\t) : children}\n\t\t</PrimitiveAvatarFallback>\n\t),\n);\nAvatarFallback.displayName = 'AvatarFallback';\n\nconst AvatarBadge = React.forwardRef<React.ElementRef<typeof PrimitiveAvatarBadge>, AvatarBadgeProps>(\n\t({ className, ...props }, ref) => (\n\t\t<PrimitiveAvatarBadge\n\t\t\tref={ref}\n\t\t\tclassName={cn('avatar--badge', className)}\n\t\t\t{...props}\n\t\t/>\n\t),\n);\nAvatarBadge.displayName = 'AvatarBadge';\n\nconst AvatarGroup = React.forwardRef<React.ElementRef<typeof PrimitiveAvatarGroup>, AvatarGroupProps>(\n\t({ className, ...props }, ref) => (\n\t\t<PrimitiveAvatarGroup\n\t\t\tref={ref}\n\t\t\tclassName={cn('avatar-group--component', className)}\n\t\t\t{...props}\n\t\t/>\n\t),\n);\nAvatarGroup.displayName = 'AvatarGroup';\n\nconst AvatarGroupCount = React.forwardRef<React.ElementRef<typeof PrimitiveAvatarGroupCount>, AvatarGroupCountProps>(\n\t({ className, children, ...props }, ref) => (\n\t\t<PrimitiveAvatarGroupCount\n\t\t\tref={ref}\n\t\t\tclassName={cn(\n\t\t\t\t'avatar-group-count--component size-avatar text-xs',\n\t\t\t\t'group-has-data-[size=sm]/avatar-group:size-avatar-sm',\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{typeof children === 'string' || typeof children === 'number' || typeof children === 'bigint' ? (\n\t\t\t\t<Text tag=\"span\" size=\"inherit\" weight=\"medium\" lineHeight=\"none\" className=\"text-inherit\">\n\t\t\t\t\t{children}\n\t\t\t\t</Text>\n\t\t\t) : children}\n\t\t</PrimitiveAvatarGroupCount>\n\t),\n);\nAvatarGroupCount.displayName = 'AvatarGroupCount';\n\nexport {\n\tAvatar,\n\tAvatarImage,\n\tAvatarFallback,\n\tAvatarGroup,\n\tAvatarGroupCount,\n\tAvatarBadge,\n};\n","/**\n * StackedAvatars — overlapping avatar group with optional `+N` overflow chip,\n * configurable size, and an optional semantic `tone` for fallbacks (initials)\n * when the consumer hasn't passed `imageUrl`.\n *\n * Wraps the shadcn AvatarGroup primitive with library-default sizes and a\n * deterministic tone picker so fallbacks stay stable without raw hue utilities.\n */\nimport * as React from 'react';\nimport {\n\tAvatar,\n\tAvatarFallback,\n\tAvatarGroup,\n\tAvatarGroupCount,\n\tAvatarImage,\n} from './avatar';\nimport { formatInitials } from '../../primitives/initials-format';\nimport { Text } from '../../typography';\nimport { useDensityConfig, type ComponentScale } from '../../../lib/ui-provider';\nimport { resolveComponentScale } from '../../../lib/ui-provider/scale';\nimport { cn } from '../../../lib/utils';\nimport type { SemanticTone } from '../../../lib/component-vocabulary';\n\nexport type StackedAvatarsSize = ComponentScale;\nexport type StackedAvatarTone = SemanticTone;\n\nexport interface StackedAvatarUser {\n\t/** Stable identity for this entry. */\n\tid?: string;\n\t/** Form field name, forwarded for native submits. */\n\tname: string;\n\timageUrl?: string;\n\t/** Optional semantic tone override for this user's fallback. Numeric values map to the token set. */\n\ttone?: StackedAvatarTone | number;\n}\n\nexport interface StackedAvatarsProps {\n\tusers: StackedAvatarUser[];\n\tmax?: number;\n\t/** Size preset. */\n\tsize?: StackedAvatarsSize;\n\tclassName?: string;\n\t/** Whether to render a `+N` count when `users.length > max`. Default `true`. */\n\tshowOverflow?: boolean;\n\t/** Provide custom overflow text (e.g. \"+12 more\"). Default `+N`. */\n\toverflowFormatter?: (overflow: number) => React.ReactNode;\n\t/** Extra class names applied to each avatar root. */\n\tavatarClassName?: string;\n}\n\nconst FALLBACK_TONES: StackedAvatarTone[] = [\n\t'neutral',\n\t'primary',\n\t'secondary',\n\t'info',\n\t'success',\n];\n\nconst FALLBACK_TONE_CLASSNAMES: Record<StackedAvatarTone, string> = {\n\tneutral: 'bg-muted text-muted-foreground',\n\tprimary: 'bg-primary/10 text-primary-accent',\n\tsecondary: 'bg-secondary text-secondary-foreground',\n\tinfo: 'bg-info/10 text-info',\n\tsuccess: 'bg-success/10 text-success',\n\twarning: 'bg-warning/15 text-warning',\n\tdestructive: 'bg-destructive/10 text-destructive',\n};\n\nfunction pickFallbackTone(seed: string | number, override?: StackedAvatarTone | number) {\n\tif (typeof override === 'string') {\n\t\treturn override;\n\t}\n\tif (typeof override === 'number') {\n\t\treturn FALLBACK_TONES[Math.abs(override) % FALLBACK_TONES.length];\n\t}\n\tconst key = typeof seed === 'number' ? seed : Array.from(seed).reduce((s, c) => s + c.charCodeAt(0), 0);\n\treturn FALLBACK_TONES[key % FALLBACK_TONES.length];\n}\n\nexport function StackedAvatars({\n\tusers,\n\tmax = 4,\n\tsize: sizeProp,\n\tclassName,\n\tshowOverflow = true,\n\toverflowFormatter,\n\tavatarClassName,\n}: StackedAvatarsProps) {\n\tconst { defaultScale } = useDensityConfig();\n\tconst size = resolveComponentScale(sizeProp, defaultScale);\n\tconst visible = users.slice(0, max);\n\tconst overflow = Math.max(0, users.length - max);\n\tconst overflowContent = overflowFormatter ? overflowFormatter(overflow) : `+${overflow}`;\n\n\treturn (\n\t\t<AvatarGroup className={cn('stacked-avatars--component', className)}>\n\t\t\t{visible.map((user, index) => {\n\t\t\t\tconst initials = formatInitials(user.name, {\n\t\t\t\t\tmaxCharacters: size === 'sm' ? 1 : 2,\n\t\t\t\t\tfallback: '?',\n\t\t\t\t});\n\t\t\t\tconst fallbackTone = pickFallbackTone(user.id ?? user.name ?? index, user.tone);\n\t\t\t\treturn (\n\t\t\t\t\t<Avatar\n\t\t\t\t\t\tkey={user.id ?? `${user.name}-${index}`}\n\t\t\t\t\t\tsize={size}\n\t\t\t\t\t\tclassName={cn('ring-background', avatarClassName)}\n\t\t\t\t\t\ttitle={user.name}\n\t\t\t\t\t>\n\t\t\t\t\t\t{user.imageUrl ? (\n\t\t\t\t\t\t\t<AvatarImage src={user.imageUrl} alt={user.name} />\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t<AvatarFallback\n\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t'font-semibold',\n\t\t\t\t\t\t\t\tFALLBACK_TONE_CLASSNAMES[fallbackTone],\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{initials}\n\t\t\t\t\t\t</AvatarFallback>\n\t\t\t\t\t</Avatar>\n\t\t\t\t);\n\t\t\t})}\n\t\t\t{!!showOverflow && overflow > 0 && (\n\t\t\t\t<AvatarGroupCount>\n\t\t\t\t\t{typeof overflowContent === 'string' || typeof overflowContent === 'number' || typeof overflowContent === 'bigint' ? (\n\t\t\t\t\t\t<Text tag=\"span\" size=\"xs\" weight=\"semibold\">\n\t\t\t\t\t\t\t{overflowContent}\n\t\t\t\t\t\t</Text>\n\t\t\t\t\t) : overflowContent}\n\t\t\t\t</AvatarGroupCount>\n\t\t\t)}\n\t\t</AvatarGroup>\n\t);\n}\n\nStackedAvatars.displayName = 'StackedAvatars';\n"],"mappings":"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"}