@webjsdev/ui 0.3.16 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +71 -10
- package/THIRD_PARTY_NOTICES.md +349 -0
- package/package.json +4 -2
- package/packages/registry/blocks/_shared/placeholder.svg +1 -0
- package/packages/registry/blocks/charts/chart-area-axes.ts +72 -0
- package/packages/registry/blocks/charts/chart-area-default.ts +64 -0
- package/packages/registry/blocks/charts/chart-area-gradient.ts +82 -0
- package/packages/registry/blocks/charts/chart-area-icons.ts +77 -0
- package/packages/registry/blocks/charts/chart-area-interactive.ts +204 -0
- package/packages/registry/blocks/charts/chart-area-legend.ts +72 -0
- package/packages/registry/blocks/charts/chart-area-linear.ts +64 -0
- package/packages/registry/blocks/charts/chart-area-stacked-expand.ts +77 -0
- package/packages/registry/blocks/charts/chart-area-stacked.ts +71 -0
- package/packages/registry/blocks/charts/chart-area-step.ts +68 -0
- package/packages/registry/blocks/charts/chart-bar-active.ts +93 -0
- package/packages/registry/blocks/charts/chart-bar-default.ts +60 -0
- package/packages/registry/blocks/charts/chart-bar-horizontal.ts +62 -0
- package/packages/registry/blocks/charts/chart-bar-interactive.ts +185 -0
- package/packages/registry/blocks/charts/chart-bar-label-custom.ts +78 -0
- package/packages/registry/blocks/charts/chart-bar-label.ts +66 -0
- package/packages/registry/blocks/charts/chart-bar-mixed.ts +87 -0
- package/packages/registry/blocks/charts/chart-bar-multiple.ts +67 -0
- package/packages/registry/blocks/charts/chart-bar-negative.ts +61 -0
- package/packages/registry/blocks/charts/chart-bar-stacked.ts +68 -0
- package/packages/registry/blocks/charts/chart-line-default.ts +61 -0
- package/packages/registry/blocks/charts/chart-line-dots-colors.ts +86 -0
- package/packages/registry/blocks/charts/chart-line-dots-custom.ts +75 -0
- package/packages/registry/blocks/charts/chart-line-dots.ts +65 -0
- package/packages/registry/blocks/charts/chart-line-interactive.ts +185 -0
- package/packages/registry/blocks/charts/chart-line-label-custom.ts +94 -0
- package/packages/registry/blocks/charts/chart-line-label.ts +73 -0
- package/packages/registry/blocks/charts/chart-line-linear.ts +61 -0
- package/packages/registry/blocks/charts/chart-line-multiple.ts +71 -0
- package/packages/registry/blocks/charts/chart-line-step.ts +61 -0
- package/packages/registry/blocks/charts/chart-pie-donut-active.ts +86 -0
- package/packages/registry/blocks/charts/chart-pie-donut-text.ts +89 -0
- package/packages/registry/blocks/charts/chart-pie-donut.ts +77 -0
- package/packages/registry/blocks/charts/chart-pie-interactive.ts +120 -0
- package/packages/registry/blocks/charts/chart-pie-label-custom.ts +82 -0
- package/packages/registry/blocks/charts/chart-pie-label-list.ts +86 -0
- package/packages/registry/blocks/charts/chart-pie-label.ts +77 -0
- package/packages/registry/blocks/charts/chart-pie-legend.ts +79 -0
- package/packages/registry/blocks/charts/chart-pie-separator-none.ts +77 -0
- package/packages/registry/blocks/charts/chart-pie-simple.ts +77 -0
- package/packages/registry/blocks/charts/chart-pie-stacked.ts +99 -0
- package/packages/registry/blocks/charts/chart-radar-default.ts +61 -0
- package/packages/registry/blocks/charts/chart-radar-dots.ts +61 -0
- package/packages/registry/blocks/charts/chart-radar-grid-circle-fill.ts +61 -0
- package/packages/registry/blocks/charts/chart-radar-grid-circle-no-lines.ts +61 -0
- package/packages/registry/blocks/charts/chart-radar-grid-circle.ts +61 -0
- package/packages/registry/blocks/charts/chart-radar-grid-custom.ts +61 -0
- package/packages/registry/blocks/charts/chart-radar-grid-fill.ts +61 -0
- package/packages/registry/blocks/charts/chart-radar-grid-none.ts +60 -0
- package/packages/registry/blocks/charts/chart-radar-icons.ts +76 -0
- package/packages/registry/blocks/charts/chart-radar-label-custom.ts +77 -0
- package/packages/registry/blocks/charts/chart-radar-legend.ts +70 -0
- package/packages/registry/blocks/charts/chart-radar-lines-only.ts +68 -0
- package/packages/registry/blocks/charts/chart-radar-multiple.ts +68 -0
- package/packages/registry/blocks/charts/chart-radar-radius.ts +68 -0
- package/packages/registry/blocks/charts/chart-radial-grid.ts +80 -0
- package/packages/registry/blocks/charts/chart-radial-label.ts +85 -0
- package/packages/registry/blocks/charts/chart-radial-shape.ts +81 -0
- package/packages/registry/blocks/charts/chart-radial-simple.ts +79 -0
- package/packages/registry/blocks/charts/chart-radial-stacked.ts +69 -0
- package/packages/registry/blocks/charts/chart-radial-text.ts +82 -0
- package/packages/registry/blocks/charts/chart-tooltip-advanced.ts +84 -0
- package/packages/registry/blocks/charts/chart-tooltip-default.ts +62 -0
- package/packages/registry/blocks/charts/chart-tooltip-formatter.ts +75 -0
- package/packages/registry/blocks/charts/chart-tooltip-icons.ts +68 -0
- package/packages/registry/blocks/charts/chart-tooltip-indicator-line.ts +62 -0
- package/packages/registry/blocks/charts/chart-tooltip-indicator-none.ts +62 -0
- package/packages/registry/blocks/charts/chart-tooltip-label-custom.ts +65 -0
- package/packages/registry/blocks/charts/chart-tooltip-label-formatter.ts +66 -0
- package/packages/registry/blocks/charts/chart-tooltip-label-none.ts +62 -0
- package/packages/registry/blocks/dashboard-01/components/app-sidebar.ts +108 -0
- package/packages/registry/blocks/dashboard-01/components/chart-area-interactive.ts +276 -0
- package/packages/registry/blocks/dashboard-01/components/dashboard-data.ts +89 -0
- package/packages/registry/blocks/dashboard-01/components/data-table.ts +637 -0
- package/packages/registry/blocks/dashboard-01/components/nav-documents.ts +65 -0
- package/packages/registry/blocks/dashboard-01/components/nav-main.ts +61 -0
- package/packages/registry/blocks/dashboard-01/components/nav-secondary.ts +40 -0
- package/packages/registry/blocks/dashboard-01/components/nav-user.ts +66 -0
- package/packages/registry/blocks/dashboard-01/components/section-cards.ts +59 -0
- package/packages/registry/blocks/dashboard-01/components/site-header.ts +52 -0
- package/packages/registry/blocks/dashboard-01/page.ts +39 -0
- package/packages/registry/blocks/login-01/components/login-form.ts +70 -0
- package/packages/registry/blocks/login-01/page.ts +19 -0
- package/packages/registry/blocks/login-02/components/login-form.ts +68 -0
- package/packages/registry/blocks/login-02/page.ts +36 -0
- package/packages/registry/blocks/login-03/components/login-form.ts +89 -0
- package/packages/registry/blocks/login-03/page.ts +28 -0
- package/packages/registry/blocks/login-04/components/login-form.ts +91 -0
- package/packages/registry/blocks/login-04/page.ts +20 -0
- package/packages/registry/blocks/login-05/components/login-form.ts +75 -0
- package/packages/registry/blocks/login-05/page.ts +19 -0
- package/packages/registry/blocks/preview/cards/activate-agent-dialog.ts +77 -0
- package/packages/registry/blocks/preview/cards/analytics-card.ts +64 -0
- package/packages/registry/blocks/preview/cards/anomaly-alert.ts +35 -0
- package/packages/registry/blocks/preview/cards/bar-chart-card.ts +91 -0
- package/packages/registry/blocks/preview/cards/book-appointment.ts +51 -0
- package/packages/registry/blocks/preview/cards/codespaces-card.ts +172 -0
- package/packages/registry/blocks/preview/cards/contributions-activity.ts +63 -0
- package/packages/registry/blocks/preview/cards/contributors.ts +51 -0
- package/packages/registry/blocks/preview/cards/environment-variables.ts +47 -0
- package/packages/registry/blocks/preview/cards/feedback-form.ts +61 -0
- package/packages/registry/blocks/preview/cards/file-upload.ts +51 -0
- package/packages/registry/blocks/preview/cards/github-profile.ts +66 -0
- package/packages/registry/blocks/preview/cards/icon-preview-grid.ts +47 -0
- package/packages/registry/blocks/preview/cards/invite-team.ts +78 -0
- package/packages/registry/blocks/preview/cards/invoice.ts +91 -0
- package/packages/registry/blocks/preview/cards/live-waveform.ts +330 -0
- package/packages/registry/blocks/preview/cards/no-team-members.ts +61 -0
- package/packages/registry/blocks/preview/cards/not-found.ts +46 -0
- package/packages/registry/blocks/preview/cards/observability-card.ts +44 -0
- package/packages/registry/blocks/preview/cards/pie-chart-card.ts +84 -0
- package/packages/registry/blocks/preview/cards/report-bug.ts +71 -0
- package/packages/registry/blocks/preview/cards/shipping-address.ts +79 -0
- package/packages/registry/blocks/preview/cards/shortcuts.ts +57 -0
- package/packages/registry/blocks/preview/cards/skeleton-loading.ts +37 -0
- package/packages/registry/blocks/preview/cards/sleep-report.ts +85 -0
- package/packages/registry/blocks/preview/cards/style-overview.ts +64 -0
- package/packages/registry/blocks/preview/cards/typography-specimen.ts +107 -0
- package/packages/registry/blocks/preview/cards/ui-elements.ts +164 -0
- package/packages/registry/blocks/preview/cards/usage-card.ts +63 -0
- package/packages/registry/blocks/preview/cards/visitors.ts +71 -0
- package/packages/registry/blocks/preview/cards/weekly-fitness-summary.ts +54 -0
- package/packages/registry/blocks/preview/page.ts +103 -0
- package/packages/registry/blocks/preview-02/cards/account-access.ts +70 -0
- package/packages/registry/blocks/preview-02/cards/card-overview.ts +75 -0
- package/packages/registry/blocks/preview-02/cards/claimable-balance.ts +54 -0
- package/packages/registry/blocks/preview-02/cards/contribution-history.ts +80 -0
- package/packages/registry/blocks/preview-02/cards/cover-art.ts +41 -0
- package/packages/registry/blocks/preview-02/cards/dividend-income.ts +76 -0
- package/packages/registry/blocks/preview-02/cards/empty-connect-bank.ts +38 -0
- package/packages/registry/blocks/preview-02/cards/empty-distribute-track.ts +38 -0
- package/packages/registry/blocks/preview-02/cards/empty-explore-catalog.ts +39 -0
- package/packages/registry/blocks/preview-02/cards/faq.ts +79 -0
- package/packages/registry/blocks/preview-02/cards/front-door.ts +40 -0
- package/packages/registry/blocks/preview-02/cards/index-investing.ts +31 -0
- package/packages/registry/blocks/preview-02/cards/kitchen-island.ts +90 -0
- package/packages/registry/blocks/preview-02/cards/loading-card.ts +34 -0
- package/packages/registry/blocks/preview-02/cards/new-milestone.ts +53 -0
- package/packages/registry/blocks/preview-02/cards/notification-settings.ts +126 -0
- package/packages/registry/blocks/preview-02/cards/payments.ts +88 -0
- package/packages/registry/blocks/preview-02/cards/payout-threshold.ts +92 -0
- package/packages/registry/blocks/preview-02/cards/power-usage.ts +84 -0
- package/packages/registry/blocks/preview-02/cards/preferences.ts +89 -0
- package/packages/registry/blocks/preview-02/cards/qr-connect.ts +54 -0
- package/packages/registry/blocks/preview-02/cards/receiving-method.ts +89 -0
- package/packages/registry/blocks/preview-02/cards/recent-transactions.ts +101 -0
- package/packages/registry/blocks/preview-02/cards/release-catalog.ts +73 -0
- package/packages/registry/blocks/preview-02/cards/roller-shades.ts +110 -0
- package/packages/registry/blocks/preview-02/cards/savings-progress.ts +68 -0
- package/packages/registry/blocks/preview-02/cards/savings-targets.ts +132 -0
- package/packages/registry/blocks/preview-02/cards/sidebar-nav.ts +88 -0
- package/packages/registry/blocks/preview-02/cards/social-links.ts +57 -0
- package/packages/registry/blocks/preview-02/cards/stock-performance.ts +106 -0
- package/packages/registry/blocks/preview-02/cards/syncing-state.ts +37 -0
- package/packages/registry/blocks/preview-02/cards/transfer-funds.ts +111 -0
- package/packages/registry/blocks/preview-02/cards/upcoming-payments.ts +69 -0
- package/packages/registry/blocks/preview-02/page.ts +111 -0
- package/packages/registry/blocks/preview-03/cards/files-chat.ts +75 -0
- package/packages/registry/blocks/preview-03/cards/group-chat.ts +114 -0
- package/packages/registry/blocks/preview-03/cards/reasoning-chat.ts +88 -0
- package/packages/registry/blocks/preview-03/cards/simple-chat.ts +53 -0
- package/packages/registry/blocks/preview-03/components/chat-card.ts +126 -0
- package/packages/registry/blocks/preview-03/components/chat-player.ts +174 -0
- package/packages/registry/blocks/preview-03/components/part-file.ts +77 -0
- package/packages/registry/blocks/preview-03/components/part-reasoning.ts +25 -0
- package/packages/registry/blocks/preview-03/components/part-source.ts +48 -0
- package/packages/registry/blocks/preview-03/components/part-text.ts +69 -0
- package/packages/registry/blocks/preview-03/components/part-tool.ts +71 -0
- package/packages/registry/blocks/preview-03/components/prompt-input.ts +86 -0
- package/packages/registry/blocks/preview-03/page.ts +39 -0
- package/packages/registry/blocks/sidebar-01/components/app-sidebar.ts +113 -0
- package/packages/registry/blocks/sidebar-01/components/search-form.ts +33 -0
- package/packages/registry/blocks/sidebar-01/components/version-switcher.ts +50 -0
- package/packages/registry/blocks/sidebar-01/page.ts +58 -0
- package/packages/registry/blocks/sidebar-02/components/app-sidebar.ts +131 -0
- package/packages/registry/blocks/sidebar-02/components/search-form.ts +33 -0
- package/packages/registry/blocks/sidebar-02/components/version-switcher.ts +50 -0
- package/packages/registry/blocks/sidebar-02/page.ts +53 -0
- package/packages/registry/blocks/sidebar-03/components/app-sidebar.ts +134 -0
- package/packages/registry/blocks/sidebar-03/page.ts +60 -0
- package/packages/registry/blocks/sidebar-04/components/app-sidebar.ts +136 -0
- package/packages/registry/blocks/sidebar-04/page.ts +58 -0
- package/packages/registry/blocks/sidebar-05/components/app-sidebar.ts +147 -0
- package/packages/registry/blocks/sidebar-05/components/search-form.ts +33 -0
- package/packages/registry/blocks/sidebar-05/page.ts +58 -0
- package/packages/registry/blocks/sidebar-06/components/app-sidebar.ts +105 -0
- package/packages/registry/blocks/sidebar-06/components/nav-main.ts +56 -0
- package/packages/registry/blocks/sidebar-06/components/sidebar-opt-in-form.ts +44 -0
- package/packages/registry/blocks/sidebar-06/page.ts +57 -0
- package/packages/registry/blocks/sidebar-07/components/app-sidebar.ts +108 -0
- package/packages/registry/blocks/sidebar-07/components/nav-main.ts +61 -0
- package/packages/registry/blocks/sidebar-07/components/nav-projects.ts +65 -0
- package/packages/registry/blocks/sidebar-07/components/nav-user.ts +70 -0
- package/packages/registry/blocks/sidebar-07/components/team-switcher.ts +66 -0
- package/packages/registry/blocks/sidebar-07/page.ts +59 -0
- package/packages/registry/blocks/sidebar-08/components/app-sidebar.ts +122 -0
- package/packages/registry/blocks/sidebar-08/components/nav-main.ts +68 -0
- package/packages/registry/blocks/sidebar-08/components/nav-projects.ts +65 -0
- package/packages/registry/blocks/sidebar-08/components/nav-secondary.ts +40 -0
- package/packages/registry/blocks/sidebar-08/components/nav-user.ts +70 -0
- package/packages/registry/blocks/sidebar-08/page.ts +59 -0
- package/packages/registry/blocks/sidebar-09/components/app-sidebar.ts +213 -0
- package/packages/registry/blocks/sidebar-09/components/nav-user.ts +70 -0
- package/packages/registry/blocks/sidebar-09/page.ts +52 -0
- package/packages/registry/blocks/sidebar-10/components/app-sidebar.ts +125 -0
- package/packages/registry/blocks/sidebar-10/components/nav-actions.ts +84 -0
- package/packages/registry/blocks/sidebar-10/components/nav-favorites.ts +66 -0
- package/packages/registry/blocks/sidebar-10/components/nav-main.ts +37 -0
- package/packages/registry/blocks/sidebar-10/components/nav-secondary.ts +43 -0
- package/packages/registry/blocks/sidebar-10/components/nav-workspaces.ts +78 -0
- package/packages/registry/blocks/sidebar-10/components/team-switcher.ts +63 -0
- package/packages/registry/blocks/sidebar-10/page.ts +46 -0
- package/packages/registry/blocks/sidebar-11/components/app-sidebar.ts +114 -0
- package/packages/registry/blocks/sidebar-11/page.ts +63 -0
- package/packages/registry/blocks/sidebar-12/components/app-sidebar.ts +63 -0
- package/packages/registry/blocks/sidebar-12/components/calendars.ts +53 -0
- package/packages/registry/blocks/sidebar-12/components/date-picker.ts +26 -0
- package/packages/registry/blocks/sidebar-12/components/nav-user.ts +70 -0
- package/packages/registry/blocks/sidebar-12/page.ts +43 -0
- package/packages/registry/blocks/sidebar-13/components/settings-dialog.ts +114 -0
- package/packages/registry/blocks/sidebar-13/page.ts +21 -0
- package/packages/registry/blocks/sidebar-14/components/app-sidebar.ts +120 -0
- package/packages/registry/blocks/sidebar-14/page.ts +55 -0
- package/packages/registry/blocks/sidebar-15/components/calendars.ts +53 -0
- package/packages/registry/blocks/sidebar-15/components/date-picker.ts +26 -0
- package/packages/registry/blocks/sidebar-15/components/nav-favorites.ts +66 -0
- package/packages/registry/blocks/sidebar-15/components/nav-main.ts +35 -0
- package/packages/registry/blocks/sidebar-15/components/nav-secondary.ts +43 -0
- package/packages/registry/blocks/sidebar-15/components/nav-user.ts +70 -0
- package/packages/registry/blocks/sidebar-15/components/nav-workspaces.ts +75 -0
- package/packages/registry/blocks/sidebar-15/components/sidebar-left.ts +126 -0
- package/packages/registry/blocks/sidebar-15/components/sidebar-right.ts +62 -0
- package/packages/registry/blocks/sidebar-15/components/team-switcher.ts +63 -0
- package/packages/registry/blocks/sidebar-15/page.ts +47 -0
- package/packages/registry/blocks/sidebar-16/components/app-sidebar.ts +124 -0
- package/packages/registry/blocks/sidebar-16/components/nav-main.ts +67 -0
- package/packages/registry/blocks/sidebar-16/components/nav-projects.ts +65 -0
- package/packages/registry/blocks/sidebar-16/components/nav-secondary.ts +40 -0
- package/packages/registry/blocks/sidebar-16/components/nav-user.ts +70 -0
- package/packages/registry/blocks/sidebar-16/components/search-form.ts +27 -0
- package/packages/registry/blocks/sidebar-16/components/site-header.ts +42 -0
- package/packages/registry/blocks/sidebar-16/page.ts +38 -0
- package/packages/registry/blocks/signup-01/components/signup-form.ts +78 -0
- package/packages/registry/blocks/signup-01/page.ts +19 -0
- package/packages/registry/blocks/signup-02/components/signup-form.ts +78 -0
- package/packages/registry/blocks/signup-02/page.ts +36 -0
- package/packages/registry/blocks/signup-03/components/signup-form.ts +79 -0
- package/packages/registry/blocks/signup-03/page.ts +28 -0
- package/packages/registry/blocks/signup-04/components/signup-form.ts +100 -0
- package/packages/registry/blocks/signup-04/page.ts +20 -0
- package/packages/registry/blocks/signup-05/components/signup-form.ts +75 -0
- package/packages/registry/blocks/signup-05/page.ts +20 -0
- package/packages/registry/components/accordion.ts +20 -7
- package/packages/registry/components/alert-dialog.ts +58 -13
- package/packages/registry/components/alert.ts +11 -6
- package/packages/registry/components/attachment.ts +119 -0
- package/packages/registry/components/avatar.ts +17 -8
- package/packages/registry/components/badge.ts +16 -9
- package/packages/registry/components/breadcrumb.ts +6 -6
- package/packages/registry/components/bubble.ts +95 -0
- package/packages/registry/components/button-group.ts +73 -0
- package/packages/registry/components/button.ts +35 -25
- package/packages/registry/components/calendar.ts +566 -0
- package/packages/registry/components/card.ts +8 -8
- package/packages/registry/components/carousel.ts +352 -0
- package/packages/registry/components/chart-element.ts +547 -0
- package/packages/registry/components/chart-geometry.ts +457 -0
- package/packages/registry/components/chart.ts +932 -0
- package/packages/registry/components/checkbox.ts +22 -84
- package/packages/registry/components/code-block.ts +191 -0
- package/packages/registry/components/collapsible.ts +5 -2
- package/packages/registry/components/combobox.ts +802 -0
- package/packages/registry/components/command.ts +618 -0
- package/packages/registry/components/context-menu.ts +302 -0
- package/packages/registry/components/dialog.ts +156 -45
- package/packages/registry/components/direction.ts +43 -0
- package/packages/registry/components/drawer.ts +226 -0
- package/packages/registry/components/dropdown-menu.ts +262 -83
- package/packages/registry/components/empty.ts +88 -0
- package/packages/registry/components/field.ts +165 -0
- package/packages/registry/components/floating.ts +124 -0
- package/packages/registry/components/hover-card.ts +19 -8
- package/packages/registry/components/input-group.ts +118 -0
- package/packages/registry/components/input-otp.ts +365 -0
- package/packages/registry/components/input.ts +3 -3
- package/packages/registry/components/item.ts +134 -0
- package/packages/registry/components/kbd.ts +2 -2
- package/packages/registry/components/label.ts +1 -1
- package/packages/registry/components/marker.ts +63 -0
- package/packages/registry/components/menubar.ts +388 -0
- package/packages/registry/components/message-scroller.ts +470 -0
- package/packages/registry/components/message.ts +69 -0
- package/packages/registry/components/native-select.ts +5 -5
- package/packages/registry/components/navigation-menu.ts +564 -0
- package/packages/registry/components/pagination.ts +5 -5
- package/packages/registry/components/popover.ts +30 -84
- package/packages/registry/components/progress.ts +7 -3
- package/packages/registry/components/questionnaire.ts +840 -0
- package/packages/registry/components/radio-group.ts +18 -73
- package/packages/registry/components/resizable.ts +330 -0
- package/packages/registry/components/scroll-area.ts +46 -0
- package/packages/registry/components/select.ts +650 -0
- package/packages/registry/components/separator.ts +1 -1
- package/packages/registry/components/sheet.ts +128 -0
- package/packages/registry/components/sidebar.ts +457 -0
- package/packages/registry/components/skeleton.ts +1 -1
- package/packages/registry/components/slider.ts +333 -0
- package/packages/registry/components/sonner.ts +74 -7
- package/packages/registry/components/spinner.ts +66 -0
- package/packages/registry/components/switch.ts +117 -9
- package/packages/registry/components/table.ts +11 -9
- package/packages/registry/components/tabs.ts +76 -25
- package/packages/registry/components/textarea.ts +1 -1
- package/packages/registry/components/toggle-group.ts +30 -11
- package/packages/registry/components/toggle.ts +20 -13
- package/packages/registry/components/tooltip.ts +68 -7
- package/packages/registry/components/trace.ts +75 -0
- package/packages/registry/lib/icons-hugeicons.ts +955 -0
- package/packages/registry/lib/icons-phosphor.ts +955 -0
- package/packages/registry/lib/icons-remixicon.ts +955 -0
- package/packages/registry/lib/icons-tabler.ts +955 -0
- package/packages/registry/lib/icons.ts +768 -0
- package/packages/registry/lib/utils.ts +49 -4
- package/packages/registry/registry.json +4092 -60
- package/packages/registry/styles/shadcn-tailwind.css +654 -0
- package/packages/registry/styles/style-luma.css +1987 -0
- package/packages/registry/styles/style-lyra.css +1965 -0
- package/packages/registry/styles/style-maia.css +1987 -0
- package/packages/registry/styles/style-mira.css +1992 -0
- package/packages/registry/styles/style-new-york.css +1597 -0
- package/packages/registry/styles/style-nova.css +1990 -0
- package/packages/registry/styles/style-rhea.css +1987 -0
- package/packages/registry/styles/style-sera.css +1977 -0
- package/packages/registry/styles/style-vega.css +1990 -0
- package/packages/registry/styles/supplement.json +54 -0
- package/packages/registry/styles/tw-animate.css +6 -0
- package/packages/registry/themes/base-colors.js +8 -2
- package/packages/registry/themes/fonts.js +120 -0
- package/packages/registry/themes/index.css +2 -0
- package/packages/registry/themes/items.js +61 -0
- package/packages/registry/themes/preset.js +302 -0
- package/packages/registry/themes/themes.js +1193 -0
- package/src/commands/add.js +236 -12
- package/src/commands/diff.js +34 -20
- package/src/commands/info.js +7 -0
- package/src/commands/init.js +125 -8
- package/src/commands/list.js +43 -11
- package/src/commands/view.js +33 -5
- package/src/registry/extract.js +192 -2
- package/src/registry/icons.js +31 -0
- package/src/registry/local.js +15 -1
- package/src/registry/schema.js +13 -0
- package/src/registry/style-bake.js +506 -0
- package/src/utils/theme.js +110 -21
package/src/commands/add.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
+
import { bakeForStyle } from '../registry/style-bake.js';
|
|
2
3
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
|
-
import { dirname, join, basename, relative as relPath } from 'node:path';
|
|
4
|
+
import { dirname, join, basename, posix, relative as relPath } from 'node:path';
|
|
4
5
|
import prompts from 'prompts';
|
|
5
6
|
import { execSync } from 'node:child_process';
|
|
6
7
|
import { getConfig } from '../utils/get-config.js';
|
|
7
8
|
import { logger } from '../utils/logger.js';
|
|
8
9
|
import { resolveTree, collectNpmDeps } from '../registry/resolver.js';
|
|
9
|
-
import {
|
|
10
|
+
import { withIconLibrary } from '../registry/icons.js';
|
|
11
|
+
import { DEFAULT_REGISTRY_URL, getRegistryIndex, getRegistryItem } from '../registry/fetcher.js';
|
|
10
12
|
import { isCustomElementSource } from '../registry/local.js';
|
|
11
13
|
import { stripExample } from '../registry/example.js';
|
|
12
14
|
import { ensureTheme } from '../utils/theme.js';
|
|
@@ -36,7 +38,8 @@ export const add = new Command()
|
|
|
36
38
|
process.exit(1);
|
|
37
39
|
}
|
|
38
40
|
|
|
39
|
-
|
|
41
|
+
// The icon library is the one components.json names, not always Lucide.
|
|
42
|
+
const tree = await withIconLibrary(await resolveTree(components, opts.registry), config.iconLibrary, opts.registry);
|
|
40
43
|
logger.info(`Installing ${logger.bold(components.join(', '))}…`);
|
|
41
44
|
|
|
42
45
|
// Self-heal missing theme tokens (#983): the helpers render against CSS
|
|
@@ -53,10 +56,42 @@ export const add = new Command()
|
|
|
53
56
|
}
|
|
54
57
|
}
|
|
55
58
|
|
|
59
|
+
const written = [];
|
|
60
|
+
// Block files another block already wrote (two families sharing a
|
|
61
|
+
// target, sidebar-07 then dashboard-01), collected for one summary.
|
|
62
|
+
/** @type {{ file: string, item: string, by: string[] }[]} */
|
|
63
|
+
const conflicts = [];
|
|
64
|
+
/** @type {{ blocks?: any[] }} */
|
|
65
|
+
const blockCache = {};
|
|
56
66
|
for (const item of tree) {
|
|
57
67
|
for (const file of item.files || []) {
|
|
58
|
-
await writeRegistryFile(cwd, config, item, file, opts);
|
|
68
|
+
const out = await writeRegistryFile(cwd, config, item, file, { ...opts, conflicts, blockCache });
|
|
69
|
+
if (out) written.push(out);
|
|
59
70
|
}
|
|
71
|
+
// A block or a component that needs tokens an existing app may lack
|
|
72
|
+
// (sidebar's --sidebar-*, chart's --chart-*) carries them as cssVars,
|
|
73
|
+
// and the missing ones are added to the app's stylesheet (D18).
|
|
74
|
+
if (item.cssVars && tw.css) {
|
|
75
|
+
const added = mergeCssVars(cwd, tw.css, item.cssVars);
|
|
76
|
+
if (added > 0) logger.success(`Added ${added} theme token${added === 1 ? '' : 's'} for ${item.name} to ${tw.css}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (written.length) {
|
|
80
|
+
logger.info('Files written:');
|
|
81
|
+
for (const f of written) logger.info(` ${f}`);
|
|
82
|
+
}
|
|
83
|
+
if (conflicts.length) {
|
|
84
|
+
const names = [...new Set(conflicts.map((c) => c.item))];
|
|
85
|
+
const others = [...new Set(conflicts.flatMap((c) => c.by))];
|
|
86
|
+
logger.warn(
|
|
87
|
+
`${conflicts.length} file${conflicts.length === 1 ? '' : 's'} of ${names.join(', ')} ` +
|
|
88
|
+
`${conflicts.length === 1 ? 'was' : 'were'} kept from ${others.join(', ')}, so the app mixes the blocks:`,
|
|
89
|
+
);
|
|
90
|
+
for (const c of conflicts) logger.warn(` ${c.file} (from ${c.by.join(', ')})`);
|
|
91
|
+
logger.warn(
|
|
92
|
+
`Run ${logger.cyan(`npx @webjsdev/ui add ${names.join(' ')} --overwrite`)} to replace them with ${names.join(', ')}'s version ` +
|
|
93
|
+
'(it also replaces every kit component and helper the install writes, so commit your edits first).',
|
|
94
|
+
);
|
|
60
95
|
}
|
|
61
96
|
|
|
62
97
|
if (opts.deps !== false) {
|
|
@@ -82,7 +117,31 @@ async function writeRegistryFile(cwd, config, item, file, opts) {
|
|
|
82
117
|
// replace edited source on every `add`. Only an explicit `--overwrite` may.
|
|
83
118
|
if (existsSync(target) && !opts.overwrite && helperTarget(config, item, file)) {
|
|
84
119
|
logger.info(`${relative(cwd, target)} already exists: keeping it.`);
|
|
85
|
-
return;
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// A block's files are app code the user owns (a page, a module), so `--yes`
|
|
124
|
+
// does not license replacing one: only `--overwrite` does (D18). Keeping it
|
|
125
|
+
// silently is wrong when ANOTHER block wrote it (two block families share
|
|
126
|
+
// targets such as components/app-sidebar.ts), since the app then mixes the
|
|
127
|
+
// two, so the existing file is compared against what this block and every
|
|
128
|
+
// other block would write there, and a match with another block is a
|
|
129
|
+
// warning that names it.
|
|
130
|
+
if (existsSync(target) && !opts.overwrite && isBlock(item, file)) {
|
|
131
|
+
const rel = relative(cwd, target);
|
|
132
|
+
const local = readFileSync(target, 'utf8');
|
|
133
|
+
if (local === transformForProject(file.content || '', target, config, item, { file, cwd })) {
|
|
134
|
+
logger.info(`${rel} is already ${item.name}'s version: keeping it.`);
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
const by = await otherBlockWriters(cwd, config, item, target, local, opts);
|
|
138
|
+
if (by.length) {
|
|
139
|
+
logger.warn(`${rel} was written by ${by.join(', ')}, not ${item.name}: keeping it (pass --overwrite to replace it).`);
|
|
140
|
+
opts.conflicts?.push({ file: rel, item: item.name, by });
|
|
141
|
+
} else {
|
|
142
|
+
logger.info(`${rel} already exists and differs from ${item.name}'s version: keeping it (pass --overwrite to replace it).`);
|
|
143
|
+
}
|
|
144
|
+
return null;
|
|
86
145
|
}
|
|
87
146
|
|
|
88
147
|
if (existsSync(target) && !opts.overwrite && !opts.yes) {
|
|
@@ -94,20 +153,67 @@ async function writeRegistryFile(cwd, config, item, file, opts) {
|
|
|
94
153
|
});
|
|
95
154
|
if (!r.overwrite) {
|
|
96
155
|
logger.info(`Skipped ${basename(target)}`);
|
|
97
|
-
return;
|
|
156
|
+
return null;
|
|
98
157
|
}
|
|
99
158
|
}
|
|
100
159
|
|
|
101
|
-
const content = transformForProject(file.content || '', target, config, item);
|
|
160
|
+
const content = transformForProject(file.content || '', target, config, item, { file, cwd });
|
|
102
161
|
writeFileSync(target, content, 'utf8');
|
|
103
162
|
logger.success(`Wrote ${relative(cwd, target)}`);
|
|
163
|
+
return relative(cwd, target);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* The OTHER blocks whose file at `target` is exactly `local`, by name: what
|
|
168
|
+
* `add` would have written for each of them there (the same transform, so a
|
|
169
|
+
* baked, import-rewritten file still matches). The registry's blocks are
|
|
170
|
+
* loaded once per `add` run, and only when a block file already exists.
|
|
171
|
+
*
|
|
172
|
+
* @param {string} cwd
|
|
173
|
+
* @param {any} config
|
|
174
|
+
* @param {{ name: string }} item the block being installed
|
|
175
|
+
* @param {string} target absolute path of the existing file
|
|
176
|
+
* @param {string} local its content
|
|
177
|
+
* @param {{ registry?: string, blockCache?: { blocks?: any[] } }} opts
|
|
178
|
+
* @returns {Promise<string[]>}
|
|
179
|
+
*/
|
|
180
|
+
async function otherBlockWriters(cwd, config, item, target, local, opts) {
|
|
181
|
+
const cache = opts.blockCache ?? {};
|
|
182
|
+
if (!cache.blocks) {
|
|
183
|
+
cache.blocks = [];
|
|
184
|
+
try {
|
|
185
|
+
const index = await getRegistryIndex(opts.registry);
|
|
186
|
+
for (const entry of index.filter((i) => i.type === 'registry:block')) {
|
|
187
|
+
cache.blocks.push(await getRegistryItem(entry.name, opts.registry));
|
|
188
|
+
}
|
|
189
|
+
} catch {
|
|
190
|
+
// An index that cannot be read only costs the warning its block name.
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
const by = [];
|
|
194
|
+
for (const other of cache.blocks) {
|
|
195
|
+
if (other.name === item.name) continue;
|
|
196
|
+
const match = (other.files || []).some((f) =>
|
|
197
|
+
resolveTarget(cwd, config, other, f) === target &&
|
|
198
|
+
transformForProject(f.content || '', target, config, other, { file: f, cwd }) === local);
|
|
199
|
+
if (match) by.push(other.name);
|
|
200
|
+
}
|
|
201
|
+
return by;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function isBlock(item, file) {
|
|
205
|
+
return item?.type === 'registry:block' || file?.type === 'registry:block';
|
|
104
206
|
}
|
|
105
207
|
|
|
106
208
|
/**
|
|
107
209
|
* The single transform that turns a registry file's raw content into what
|
|
108
210
|
* lands in the user's project. `add` writes this; `diff` compares against it,
|
|
109
|
-
* so the two cannot disagree (#983).
|
|
211
|
+
* so the two cannot disagree (#983). Three steps:
|
|
110
212
|
*
|
|
213
|
+
* 0. Bake the style (#1666): a registry file in base form carries semantic
|
|
214
|
+
* `cn-*` names, which are replaced by the Tailwind classes of the style
|
|
215
|
+
* `components.json` names (`new-york` when it names none), so the file an
|
|
216
|
+
* app owns is plain Tailwind. A file with no `cn-*` name is untouched.
|
|
111
217
|
* 1. Retarget the registry-relative `../lib/utils.ts` / `../lib/dom.ts` imports
|
|
112
218
|
* to the project's resolved helper paths (see {@link rewriteUtilsImport}).
|
|
113
219
|
* 2. For a Tier-1 helper component (a `registry:ui` file that is NOT a custom
|
|
@@ -118,12 +224,15 @@ async function writeRegistryFile(cwd, config, item, file, opts) {
|
|
|
118
224
|
*
|
|
119
225
|
* @param {string} content raw registry file content
|
|
120
226
|
* @param {string} target absolute path where the file will be written
|
|
121
|
-
* @param {{ resolvedPaths: { utils: string } }} config parsed components.json
|
|
227
|
+
* @param {{ resolvedPaths: { utils: string }, style?: string }} config parsed components.json
|
|
122
228
|
* @param {{ name: string, type?: string }} [item] the registry item
|
|
123
229
|
* @returns {string}
|
|
124
230
|
*/
|
|
125
|
-
export function transformForProject(content, target, config, item) {
|
|
126
|
-
let out =
|
|
231
|
+
export function transformForProject(content, target, config, item, where = {}) {
|
|
232
|
+
let out = bakeForStyle(content, config?.style);
|
|
233
|
+
out = where.file?.path && where.cwd
|
|
234
|
+
? rewriteRegistryImports(out, where.file.path, target, config, where.cwd, item)
|
|
235
|
+
: rewriteUtilsImport(out, target, config);
|
|
127
236
|
if (item && item.type === 'registry:ui' && !isCustomElementSource(content)) {
|
|
128
237
|
out = stripExample(out, item.name);
|
|
129
238
|
}
|
|
@@ -160,6 +269,100 @@ export function rewriteUtilsImport(content, target, config) {
|
|
|
160
269
|
return out;
|
|
161
270
|
}
|
|
162
271
|
|
|
272
|
+
/**
|
|
273
|
+
* Rewrite every registry-relative import in a file to the path that resolves
|
|
274
|
+
* from where the file lands in the app (D18). Each relative specifier is
|
|
275
|
+
* resolved against the file's own registry path, then mapped to where THAT
|
|
276
|
+
* registry file lands:
|
|
277
|
+
*
|
|
278
|
+
* - another file of the same item (a block's page importing its own
|
|
279
|
+
* component) lands at its own target;
|
|
280
|
+
* - `lib/utils.ts` and `lib/dom.ts` land at the utils alias and beside it,
|
|
281
|
+
* as {@link rewriteUtilsImport} has always done (and `lib/icons.ts` beside
|
|
282
|
+
* them too);
|
|
283
|
+
* - a component (`components/<name>.ts`) lands in the ui alias directory.
|
|
284
|
+
*
|
|
285
|
+
* Anything else is left as written. This is the general form of the two
|
|
286
|
+
* fixed literals {@link rewriteUtilsImport} rewrites, which a block, whose
|
|
287
|
+
* files sit at other depths and import components and each other, needs.
|
|
288
|
+
*
|
|
289
|
+
* @param {string} content
|
|
290
|
+
* @param {string} fromPath the file's path inside the registry
|
|
291
|
+
* @param {string} target absolute path where the file is written
|
|
292
|
+
* @param {object} config parsed components.json
|
|
293
|
+
* @param {string} cwd the project root
|
|
294
|
+
* @param {{ files?: { path: string }[] }} [item] the item the file belongs to
|
|
295
|
+
*/
|
|
296
|
+
export function rewriteRegistryImports(content, fromPath, target, config, cwd, item) {
|
|
297
|
+
const utilsAbs = config?.resolvedPaths?.utils;
|
|
298
|
+
const uiDir = config?.resolvedPaths?.ui ?? join(cwd, (config?.aliases?.ui || 'components/ui').replace(/^@\//, ''));
|
|
299
|
+
const own = new Map((item?.files || []).map((f) => [posix.normalize(f.path), f]));
|
|
300
|
+
const destOf = (regPath) => {
|
|
301
|
+
if (own.has(regPath) && regPath !== posix.normalize(fromPath)) return resolveTarget(cwd, config, item, own.get(regPath));
|
|
302
|
+
if (utilsAbs && regPath === 'lib/utils.ts') return utilsAbs;
|
|
303
|
+
if (utilsAbs && (regPath === 'lib/dom.ts' || regPath === 'lib/icons.ts')) return join(dirname(utilsAbs), posix.basename(regPath));
|
|
304
|
+
if (regPath.startsWith('components/') && !regPath.slice('components/'.length).includes('/')) return join(uiDir, posix.basename(regPath));
|
|
305
|
+
return null;
|
|
306
|
+
};
|
|
307
|
+
return content.replace(/((?:from|import)\s*\(?\s*)(['"])(\.{1,2}\/[^'"]+)\2/g, (whole, lead, q, spec) => {
|
|
308
|
+
const regPath = posix.normalize(posix.join(posix.dirname(fromPath), spec));
|
|
309
|
+
const dest = destOf(regPath);
|
|
310
|
+
if (!dest) return whole;
|
|
311
|
+
let rel = relPath(dirname(target), dest).split(/[\\/]/).join('/');
|
|
312
|
+
if (!rel.startsWith('.')) rel = './' + rel;
|
|
313
|
+
return `${lead}${q}${rel}${q}`;
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Add the CSS variables an item carries to the app's stylesheet, leaving
|
|
319
|
+
* every one the app already defines as it is. `light` goes in the `:root`
|
|
320
|
+
* block, `dark` in `.dark`, and `theme` in `@theme inline`, which is where
|
|
321
|
+
* the kit's theme puts each. A missing block is appended. Returns how many
|
|
322
|
+
* variables were added.
|
|
323
|
+
*
|
|
324
|
+
* @param {string} cwd
|
|
325
|
+
* @param {string} cssPath the app's stylesheet, relative to cwd
|
|
326
|
+
* @param {{ theme?: Record<string, string>, light?: Record<string, string>, dark?: Record<string, string> }} cssVars
|
|
327
|
+
*/
|
|
328
|
+
export function mergeCssVars(cwd, cssPath, cssVars) {
|
|
329
|
+
const file = join(cwd, cssPath);
|
|
330
|
+
let css = existsSync(file) ? readFileSync(file, 'utf8') : '';
|
|
331
|
+
let added = 0;
|
|
332
|
+
const groups = [
|
|
333
|
+
['@theme inline', cssVars.theme],
|
|
334
|
+
[':root', cssVars.light],
|
|
335
|
+
['.dark', cssVars.dark],
|
|
336
|
+
];
|
|
337
|
+
for (const [selector, vars] of groups) {
|
|
338
|
+
const entries = Object.entries(vars || {});
|
|
339
|
+
if (!entries.length) continue;
|
|
340
|
+
// A token counts as present only inside ITS block: a light value in
|
|
341
|
+
// :root says nothing about the dark one .dark still needs.
|
|
342
|
+
const open = css.indexOf(`${selector} {`);
|
|
343
|
+
const body = open >= 0 ? css.slice(open, css.indexOf('}', open)) : '';
|
|
344
|
+
const missing = entries.filter(([name]) => !new RegExp(`--${escapeRe(name)}\\s*:`).test(body));
|
|
345
|
+
if (!missing.length) continue;
|
|
346
|
+
const lines = missing.map(([name, value]) => ` --${name}: ${value};`).join('\n');
|
|
347
|
+
if (open >= 0) {
|
|
348
|
+
const close = css.indexOf('}', open);
|
|
349
|
+
css = `${css.slice(0, close).replace(/\s*$/, '')}\n${lines}\n${css.slice(close)}`;
|
|
350
|
+
} else {
|
|
351
|
+
css = `${css.replace(/\s*$/, '')}\n\n${selector} {\n${lines}\n}\n`;
|
|
352
|
+
}
|
|
353
|
+
added += missing.length;
|
|
354
|
+
}
|
|
355
|
+
if (added) {
|
|
356
|
+
ensureDir(dirname(file));
|
|
357
|
+
writeFileSync(file, css, 'utf8');
|
|
358
|
+
}
|
|
359
|
+
return added;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function escapeRe(s) {
|
|
363
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
364
|
+
}
|
|
365
|
+
|
|
163
366
|
/**
|
|
164
367
|
* Replace a single registry-relative import specifier with the relative path
|
|
165
368
|
* from `target`'s directory to `destAbs`. No-op when `spec` is absent.
|
|
@@ -178,7 +381,19 @@ function replaceImportSpecifier(content, spec, destAbs, target) {
|
|
|
178
381
|
.replaceAll(`"${spec}"`, `"${rel}"`);
|
|
179
382
|
}
|
|
180
383
|
|
|
181
|
-
|
|
384
|
+
/**
|
|
385
|
+
* The absolute path a registry file is written to: the helper alias for the
|
|
386
|
+
* shared helpers, else the file's own `target`, else a folder picked by the
|
|
387
|
+
* file's type. `diff` reads the same answer, so it compares the file `add`
|
|
388
|
+
* would have written.
|
|
389
|
+
*
|
|
390
|
+
* @param {string} cwd
|
|
391
|
+
* @param {any} config parsed components.json
|
|
392
|
+
* @param {any} item the registry item
|
|
393
|
+
* @param {{ path: string, type?: string, target?: string }} file
|
|
394
|
+
* @returns {string}
|
|
395
|
+
*/
|
|
396
|
+
export function resolveTarget(cwd, config, item, file) {
|
|
182
397
|
// The two shared helpers are pinned to `lib/utils.ts` / `lib/dom.ts` by the
|
|
183
398
|
// registry manifest, but where they actually BELONG is wherever the project's
|
|
184
399
|
// `utils` alias points, because that is the path `rewriteUtilsImport` writes
|
|
@@ -205,6 +420,11 @@ function resolveTarget(cwd, config, item, file) {
|
|
|
205
420
|
return join(cwd, (aliases.lib || 'lib').replace(/^@\//, ''), fileName);
|
|
206
421
|
case 'registry:hook':
|
|
207
422
|
return join(cwd, 'hooks', fileName);
|
|
423
|
+
case 'registry:block':
|
|
424
|
+
// Every shipped block file names its target (a registry test enforces
|
|
425
|
+
// it). A custom registry's block that does not keeps its path, so two
|
|
426
|
+
// files called page.ts cannot land on one another.
|
|
427
|
+
return join(cwd, file.path);
|
|
208
428
|
default:
|
|
209
429
|
return join(cwd, fileName);
|
|
210
430
|
}
|
|
@@ -236,6 +456,10 @@ function helperTarget(config, item, file) {
|
|
|
236
456
|
const name = basename(file.path || '');
|
|
237
457
|
if (item.name === 'lib-utils' && name === 'utils.ts') return utilsAbs;
|
|
238
458
|
if (item.name === 'lib-dom' && name === 'dom.ts') return join(dirname(utilsAbs), 'dom.ts');
|
|
459
|
+
// Every icon library (lib-icons, lib-icons-tabler, ...) is the SAME file in
|
|
460
|
+
// an app, the utils file's sibling `icons.ts`, which is where a block's
|
|
461
|
+
// `../lib/icons.ts` import is rewritten to (#1666).
|
|
462
|
+
if (/^lib-icons(-|$)/.test(item.name) && /^icons(-[a-z]+)?\.ts$/.test(name)) return join(dirname(utilsAbs), 'icons.ts');
|
|
239
463
|
return null;
|
|
240
464
|
}
|
|
241
465
|
|
package/src/commands/diff.js
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import { existsSync, readFileSync } from 'node:fs';
|
|
3
|
-
import { join, basename } from 'node:path';
|
|
4
3
|
import { getConfig } from '../utils/get-config.js';
|
|
5
4
|
import { fetchRegistryItem, fetchRegistryIndex, DEFAULT_REGISTRY_URL } from '../registry/fetcher.js';
|
|
6
|
-
import { transformForProject } from './add.js';
|
|
5
|
+
import { transformForProject, resolveTarget } from './add.js';
|
|
7
6
|
import { logger } from '../utils/logger.js';
|
|
8
7
|
|
|
9
8
|
export const diff = new Command()
|
|
10
9
|
.name('diff')
|
|
11
|
-
.description('Show differences between local components and the registry')
|
|
12
|
-
.argument('[name]', 'component to diff (omit to show all out-of-date)')
|
|
10
|
+
.description('Show differences between local components or blocks and the registry')
|
|
11
|
+
.argument('[name]', 'component or block to diff (omit to show all out-of-date)')
|
|
13
12
|
.option('-c, --cwd <cwd>', 'the working directory', process.cwd())
|
|
14
13
|
.option('--registry <url>', 'registry base URL', DEFAULT_REGISTRY_URL)
|
|
15
14
|
.action(async (name, opts) => {
|
|
@@ -22,35 +21,50 @@ export const diff = new Command()
|
|
|
22
21
|
// diff compares local files against the LIVE upstream, so it stays on the
|
|
23
22
|
// network path (NOT local-first): local-first would compare the package
|
|
24
23
|
// against itself. Resolve the names to compare, then fetch each item's full
|
|
25
|
-
// content (the index is metadata-only).
|
|
24
|
+
// content (the index is metadata-only). Installed blocks are compared too
|
|
25
|
+
// (D18).
|
|
26
26
|
let names;
|
|
27
27
|
if (name) {
|
|
28
28
|
names = [name];
|
|
29
29
|
} else {
|
|
30
30
|
const index = await fetchRegistryIndex(opts.registry);
|
|
31
|
-
names = index.filter((i) => i.type === 'registry:ui').map((i) => i.name);
|
|
31
|
+
names = index.filter((i) => i.type === 'registry:ui' || i.type === 'registry:block').map((i) => i.name);
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
// Grouped by the file on disk, not by item: several blocks can write the
|
|
35
|
+
// same target (every login block writes app/login/page.ts), so a file is
|
|
36
|
+
// up to date when it matches ANY item that writes it, and is reported
|
|
37
|
+
// once otherwise.
|
|
38
|
+
/** @type {Map<string, { items: string[], expected: string[] }>} */
|
|
39
|
+
const byTarget = new Map();
|
|
37
40
|
for (const n of names) {
|
|
38
41
|
const item = await fetchRegistryItem(n, opts.registry);
|
|
39
42
|
for (const file of item.files || []) {
|
|
40
|
-
|
|
43
|
+
// The path `add` writes, so the two cannot disagree (#983).
|
|
44
|
+
const target = resolveTarget(opts.cwd, config, item, file);
|
|
41
45
|
if (!existsSync(target)) continue;
|
|
42
|
-
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
46
|
+
// Compare against what `add` WOULD write (bake, import rewrite and
|
|
47
|
+
// example strip), not the raw registry content: otherwise a pristine
|
|
48
|
+
// install reports every import-rewritten file as differing (#983).
|
|
49
|
+
const expected = transformForProject(file.content || '', target, config, item, { file, cwd: opts.cwd });
|
|
50
|
+
const entry = byTarget.get(target) ?? { items: [], expected: [] };
|
|
51
|
+
entry.items.push(item.name);
|
|
52
|
+
entry.expected.push(expected);
|
|
53
|
+
byTarget.set(target, entry);
|
|
51
54
|
}
|
|
52
55
|
}
|
|
53
56
|
|
|
54
|
-
|
|
57
|
+
let changed = 0;
|
|
58
|
+
for (const [target, { items, expected }] of byTarget) {
|
|
59
|
+
const local = readFileSync(target, 'utf8');
|
|
60
|
+
if (expected.includes(local)) continue;
|
|
61
|
+
const rel = target.startsWith(opts.cwd) ? target.slice(opts.cwd.length + 1) : target;
|
|
62
|
+
logger.info(items.length === 1
|
|
63
|
+
? `${logger.bold(items[0])}: ${rel} differs from registry`
|
|
64
|
+
: `${rel} differs from every item that writes it (${items.join(', ')})`);
|
|
65
|
+
changed++;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (changed === 0) logger.success('All local components and blocks match the registry.');
|
|
55
69
|
else logger.info(`\n${changed} file${changed === 1 ? '' : 's'} differ. Re-add with ${logger.cyan('npx @webjsdev/ui add <name> -o')} to overwrite.`);
|
|
56
70
|
});
|
package/src/commands/info.js
CHANGED
|
@@ -3,6 +3,8 @@ import { existsSync } from 'node:fs';
|
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import { getConfig } from '../utils/get-config.js';
|
|
5
5
|
import { DEFAULT_REGISTRY_URL } from '../registry/fetcher.js';
|
|
6
|
+
import { loadRegistryIndex } from '../registry/local.js';
|
|
7
|
+
import { itemKind } from '../registry/extract.js';
|
|
6
8
|
import { logger } from '../utils/logger.js';
|
|
7
9
|
|
|
8
10
|
export const info = new Command()
|
|
@@ -15,6 +17,11 @@ export const info = new Command()
|
|
|
15
17
|
|
|
16
18
|
logger.info(`${logger.bold('cwd')} ${cwd}`);
|
|
17
19
|
logger.info(`${logger.bold('Registry')} ${DEFAULT_REGISTRY_URL}`);
|
|
20
|
+
// What the installed kit ships (the packaged registry `add` reads), with
|
|
21
|
+
// the blocks and charts counted beside the components (D18).
|
|
22
|
+
const kinds = loadRegistryIndex().map(itemKind);
|
|
23
|
+
const count = (k) => kinds.filter((x) => x === k).length;
|
|
24
|
+
logger.info(`${logger.bold('Kit')} ${count('ui')} components, ${count('block')} blocks, ${count('chart')} charts`);
|
|
18
25
|
logger.info(`${logger.bold('Config')} ${config ? 'components.json ✔' : 'components.json ✖ (run `npx @webjsdev/ui init`)'}`);
|
|
19
26
|
if (config) {
|
|
20
27
|
logger.info(`${logger.bold('Base color')} ${config.tailwind.baseColor}`);
|
package/src/commands/init.js
CHANGED
|
@@ -5,7 +5,16 @@ import prompts from 'prompts';
|
|
|
5
5
|
import { writeConfig, CONFIG_FILE } from '../utils/get-config.js';
|
|
6
6
|
import { logger } from '../utils/logger.js';
|
|
7
7
|
import { getRegistryItem, DEFAULT_REGISTRY_URL } from '../registry/fetcher.js';
|
|
8
|
-
import { ensureTheme } from '../utils/theme.js';
|
|
8
|
+
import { ensureTheme, presetThemeBlock } from '../utils/theme.js';
|
|
9
|
+
import { STYLE_NAMES } from '../registry/style-bake.js';
|
|
10
|
+
import {
|
|
11
|
+
ICON_LIBRARIES,
|
|
12
|
+
decodePreset,
|
|
13
|
+
getIconLibrary,
|
|
14
|
+
isPresetCode,
|
|
15
|
+
presetFontUrls,
|
|
16
|
+
resolvePresetConfig,
|
|
17
|
+
} from '../../packages/registry/themes/preset.js';
|
|
9
18
|
|
|
10
19
|
const BASE_COLORS = ['neutral', 'stone', 'zinc', 'mauve', 'olive', 'mist', 'taupe'];
|
|
11
20
|
|
|
@@ -50,11 +59,46 @@ export const init = new Command()
|
|
|
50
59
|
.option('-y, --yes', 'skip confirmation prompts', false)
|
|
51
60
|
.option('--base-color <color>', `base color (${BASE_COLORS.join('|')})`)
|
|
52
61
|
.option('--css <path>', 'path to the project Tailwind CSS file')
|
|
53
|
-
.option('-o, --overwrite', 'replace an existing alias map
|
|
62
|
+
.option('-o, --overwrite', 'replace an existing alias map, existing helper files and the theme block', false)
|
|
63
|
+
.option('--preset <code>', 'apply a design-system preset code from webjs.dev/ui/create (style, colors, font, radius, icons)')
|
|
64
|
+
.option('--style <name>', `component style (${STYLE_NAMES.join('|')})`)
|
|
65
|
+
.option('--icons <library>', `icon library (${ICON_LIBRARIES.map((l) => l.name).join('|')})`)
|
|
54
66
|
.option('--registry <url>', 'registry base URL', DEFAULT_REGISTRY_URL)
|
|
55
67
|
.action(async (opts) => {
|
|
56
68
|
const cwd = opts.cwd;
|
|
57
69
|
|
|
70
|
+
// A preset is the configurator's whole design system in one code (#1666).
|
|
71
|
+
// An explicit flag beside it wins over the field it names, so a code can
|
|
72
|
+
// be reused with one choice changed.
|
|
73
|
+
/** @type {import('../../packages/registry/themes/preset.js').PresetConfig | null} */
|
|
74
|
+
let preset = null;
|
|
75
|
+
if (opts.preset) {
|
|
76
|
+
const decoded = isPresetCode(opts.preset) ? decodePreset(opts.preset) : null;
|
|
77
|
+
if (!decoded) {
|
|
78
|
+
logger.error(`"${opts.preset}" is not a preset code. Copy one from ${logger.cyan('https://webjs.dev/ui/create')}.`);
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
const resolved = resolvePresetConfig({
|
|
82
|
+
...decoded,
|
|
83
|
+
...(opts.style ? { style: opts.style } : null),
|
|
84
|
+
...(opts.icons ? { iconLibrary: opts.icons } : null),
|
|
85
|
+
...(opts.baseColor ? { baseColor: opts.baseColor } : null),
|
|
86
|
+
});
|
|
87
|
+
if (resolved.error) {
|
|
88
|
+
logger.error(`Preset ${opts.preset}: ${resolved.error}.`);
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
preset = resolved.config;
|
|
92
|
+
}
|
|
93
|
+
if (opts.style && !STYLE_NAMES.includes(opts.style)) {
|
|
94
|
+
logger.error(`Unknown style "${opts.style}". Use one of: ${STYLE_NAMES.join(', ')}.`);
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
if (opts.icons && !getIconLibrary(opts.icons)) {
|
|
98
|
+
logger.error(`Unknown icon library "${opts.icons}". Use one of: ${ICON_LIBRARIES.map((l) => l.name).join(', ')}.`);
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
|
|
58
102
|
// Re-running `init` on a project that already has one must not RELOCATE it.
|
|
59
103
|
// The alias map decides where cn() lives and what every already-added
|
|
60
104
|
// component imports, and `tailwind.css` decides which stylesheet the app
|
|
@@ -97,7 +141,7 @@ export const init = new Command()
|
|
|
97
141
|
|
|
98
142
|
/** @type {{ baseColor: string, css: string }} */
|
|
99
143
|
let answers = {
|
|
100
|
-
baseColor: opts.baseColor || initialBaseColor,
|
|
144
|
+
baseColor: (preset && preset.baseColor) || opts.baseColor || initialBaseColor,
|
|
101
145
|
css: opts.css || initialCss,
|
|
102
146
|
};
|
|
103
147
|
|
|
@@ -105,7 +149,7 @@ export const init = new Command()
|
|
|
105
149
|
const r = await prompts(
|
|
106
150
|
[
|
|
107
151
|
{
|
|
108
|
-
type: opts.baseColor ? null : 'select',
|
|
152
|
+
type: opts.baseColor || preset ? null : 'select',
|
|
109
153
|
name: 'baseColor',
|
|
110
154
|
message: 'Base color?',
|
|
111
155
|
choices: BASE_COLORS.map((c) => ({ title: c, value: c })),
|
|
@@ -127,9 +171,12 @@ export const init = new Command()
|
|
|
127
171
|
// unrecognised key through here would write a file that `getConfig` (and
|
|
128
172
|
// therefore `add` / `diff` / `info`) throws on. Preserving a project's
|
|
129
173
|
// settings means its VALUES, not arbitrary extra fields.
|
|
174
|
+
// A style or icon library the command names replaces the stored one,
|
|
175
|
+
// since naming it is the request; otherwise a re-run keeps what is there.
|
|
176
|
+
const iconLibrary = (preset && preset.iconLibrary) || opts.icons || (keep && existing.iconLibrary) || 'lucide';
|
|
130
177
|
const config = {
|
|
131
178
|
$schema: 'https://ui.webjs.dev/schema.json',
|
|
132
|
-
style: (keep && existing.style) || 'default',
|
|
179
|
+
style: (preset && preset.style) || opts.style || (keep && existing.style) || 'default',
|
|
133
180
|
tailwind: {
|
|
134
181
|
css: answers.css,
|
|
135
182
|
baseColor: answers.baseColor,
|
|
@@ -138,7 +185,7 @@ export const init = new Command()
|
|
|
138
185
|
: true,
|
|
139
186
|
},
|
|
140
187
|
aliases,
|
|
141
|
-
iconLibrary
|
|
188
|
+
iconLibrary,
|
|
142
189
|
};
|
|
143
190
|
|
|
144
191
|
writeConfig(cwd, config);
|
|
@@ -147,17 +194,49 @@ export const init = new Command()
|
|
|
147
194
|
// Pull lib/utils + the chosen theme from the registry and write them in.
|
|
148
195
|
await writeLibUtils(cwd, aliases.utils, opts.registry, opts.overwrite);
|
|
149
196
|
|
|
197
|
+
// The icon library is written only when the command names one, since it
|
|
198
|
+
// is a large file an app that draws no kit icons does not need.
|
|
199
|
+
if (preset || opts.icons) await writeIcons(cwd, aliases.utils, iconLibrary, opts.registry, opts.overwrite);
|
|
200
|
+
|
|
201
|
+
// A preset's tokens are its own palette over the kit's theme file.
|
|
202
|
+
let block;
|
|
203
|
+
if (preset) {
|
|
204
|
+
try {
|
|
205
|
+
const neutral = await getRegistryItem('theme-neutral', opts.registry);
|
|
206
|
+
block = presetThemeBlock(neutral.files[0].content || '', preset);
|
|
207
|
+
} catch (e) {
|
|
208
|
+
logger.error(`Could not build the theme for preset ${opts.preset}: ${e.message}`);
|
|
209
|
+
process.exit(1);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
150
213
|
// The theme tokens are what the class helpers render against. A silent
|
|
151
214
|
// failure here (the old behaviour) left an unstyled install with a clean
|
|
152
215
|
// exit code, the exact trap for an autonomous agent, so hard-fail (#983).
|
|
153
|
-
|
|
216
|
+
// A preset, or --overwrite, replaces an existing block in place (#1666).
|
|
217
|
+
const theme = await ensureTheme(cwd, answers.baseColor, answers.css, opts.registry, {
|
|
218
|
+
replace: Boolean(preset || opts.overwrite),
|
|
219
|
+
block,
|
|
220
|
+
});
|
|
154
221
|
if (theme.status === 'failed') {
|
|
155
222
|
logger.error(`Could not write theme tokens into ${answers.css}: ${theme.error}`);
|
|
156
223
|
logger.info('The class helpers render against these tokens, so this must succeed.');
|
|
157
224
|
process.exit(1);
|
|
158
225
|
}
|
|
159
226
|
if (theme.status === 'written') logger.success(`Wrote theme into ${answers.css}`);
|
|
160
|
-
else logger.
|
|
227
|
+
else if (theme.status === 'replaced') logger.success(`Replaced the theme in ${answers.css}`);
|
|
228
|
+
else if (theme.status === 'legacy') {
|
|
229
|
+
logger.warn(`The theme block in ${answers.css} predates the end marker, so it was left as it is.`);
|
|
230
|
+
logger.info(`To replace it, delete the block (from ${logger.cyan('/* @webjsdev/ui theme */')} to the end of its rules) and re-run init.`);
|
|
231
|
+
} else logger.info(`Theme already present in ${answers.css}: skipping.`);
|
|
232
|
+
|
|
233
|
+
if (preset) {
|
|
234
|
+
const urls = presetFontUrls(preset);
|
|
235
|
+
if (urls.length) {
|
|
236
|
+
logger.info('The preset names its fonts in the theme. Load the faces, either self-hosted under public/ or from:');
|
|
237
|
+
for (const url of urls) logger.info(` ${url}`);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
161
240
|
|
|
162
241
|
logger.break();
|
|
163
242
|
logger.success('Done.');
|
|
@@ -224,6 +303,44 @@ async function writeLibUtils(cwd, utilsAlias, registryUrl, overwrite) {
|
|
|
224
303
|
}
|
|
225
304
|
}
|
|
226
305
|
|
|
306
|
+
/**
|
|
307
|
+
* Write the chosen icon library as the utils file's sibling `icons.ts`, the
|
|
308
|
+
* path a block's icon import is rewritten to (#1666). Naming a library is a
|
|
309
|
+
* request to switch, so a file that is still one of the five libraries as
|
|
310
|
+
* shipped is replaced. A file the app has edited is kept unless `--overwrite`
|
|
311
|
+
* says otherwise, the same rule as the other helpers.
|
|
312
|
+
*/
|
|
313
|
+
async function writeIcons(cwd, utilsAlias, library, registryUrl, overwrite) {
|
|
314
|
+
const lib = getIconLibrary(library);
|
|
315
|
+
if (!lib) return;
|
|
316
|
+
const target = join(dirname(join(cwd, utilsAlias.replace(/^@\//, '') + '.ts')), 'icons.ts');
|
|
317
|
+
const rel = relative(cwd, target);
|
|
318
|
+
try {
|
|
319
|
+
const item = await getRegistryItem(lib.item, registryUrl);
|
|
320
|
+
const content = item.files && item.files[0] ? item.files[0].content || '' : '';
|
|
321
|
+
if (!content) throw new Error(`${lib.item} has no content`);
|
|
322
|
+
if (existsSync(target) && !overwrite) {
|
|
323
|
+
const current = readFileSync(target, 'utf8');
|
|
324
|
+
if (current === content) {
|
|
325
|
+
logger.info(`${rel} is already ${lib.title}: keeping it.`);
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
const pristine = await Promise.all(
|
|
329
|
+
ICON_LIBRARIES.map((l) => getRegistryItem(l.item, registryUrl).then((it) => it.files[0].content, () => null)),
|
|
330
|
+
);
|
|
331
|
+
if (!pristine.includes(current)) {
|
|
332
|
+
logger.warn(`${rel} has been edited, so it was kept. Re-run with --overwrite to replace it with ${lib.title}.`);
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
ensureDir(dirname(target));
|
|
337
|
+
writeFileSync(target, content, 'utf8');
|
|
338
|
+
logger.success(`Wrote ${rel} (${lib.title})`);
|
|
339
|
+
} catch (e) {
|
|
340
|
+
logger.warn(`Could not fetch ${lib.item} from the registry (${e.message}).`);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
227
344
|
function relative(cwd, p) {
|
|
228
345
|
return p.startsWith(cwd) ? p.slice(cwd.length + 1) : p;
|
|
229
346
|
}
|