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