@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
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message scroller: the scroll container for a chat transcript. Tier-2. A
|
|
3
|
+
* port of shadcn's MessageScroller (the headless primitive in
|
|
4
|
+
* `@shadcn/react`, styled): it opens at the newest message, follows new ones
|
|
5
|
+
* while the reader is at the end, leaves them where they are once they have
|
|
6
|
+
* scrolled up, and offers a button back to the end.
|
|
7
|
+
*
|
|
8
|
+
* shadcn parity:
|
|
9
|
+
* MessageScroller.Provider + MessageScroller
|
|
10
|
+
* → <ui-message-scroller auto-scroll default-scroll-position
|
|
11
|
+
* scroll-edge-threshold scroll-previous-item-peek>
|
|
12
|
+
* MessageScrollerViewport → <ui-message-scroller-viewport>
|
|
13
|
+
* MessageScrollerContent → <ui-message-scroller-content> (a role="log")
|
|
14
|
+
* MessageScrollerItem (messageId, scrollAnchor)
|
|
15
|
+
* → <ui-message-scroller-item message-id scroll-anchor>
|
|
16
|
+
* MessageScrollerButton direction, children
|
|
17
|
+
* → <ui-message-scroller-button direction="end" | "start" label>,
|
|
18
|
+
* its children in place of the arrow
|
|
19
|
+
* useMessageScroller() → the element: scrollToEnd(), scrollToStart(), scrollToMessage(id)
|
|
20
|
+
* useMessageScrollerScrollable()
|
|
21
|
+
* → `scrollable` ({ start, end }) and `ui-scrollable-change`
|
|
22
|
+
* useMessageScrollerVisibility()
|
|
23
|
+
* → `visibleMessageIds` and `ui-visibility-change`
|
|
24
|
+
*
|
|
25
|
+
* `default-scroll-position` is where it opens: "end" (the default), "start",
|
|
26
|
+
* or "last-anchor", the last item marked `scroll-anchor` at the top (a new
|
|
27
|
+
* question with the answer streaming in under it). The transcript is always
|
|
28
|
+
* served visible, so it reads with JavaScript off (the opening position is
|
|
29
|
+
* then the start, the browser's own). A scroller the browser builds on the
|
|
30
|
+
* client, which has never painted, is held invisible until that position
|
|
31
|
+
* applies, so the reader never sees a jump; one the server rendered moves
|
|
32
|
+
* to it once its script runs.
|
|
33
|
+
* After that, an item that ARRIVES marked `scroll-anchor` (the question a
|
|
34
|
+
* reader just sent) settles near the top with `scroll-previous-item-peek`
|
|
35
|
+
* pixels (64 by default, shadcn's) of the previous turn still in view, and stays there while the reply streams in below it: a
|
|
36
|
+
* tail spacer at the end of the list makes the room, and shrinks as the
|
|
37
|
+
* reply grows. Once the reply fills the viewport, `auto-scroll` follows it.
|
|
38
|
+
* Messages added at the top keep the reader's place through the browser's
|
|
39
|
+
* own scroll anchoring. `scroll-edge-threshold` (8 pixels by default,
|
|
40
|
+
* shadcn's) is how near an edge counts as being at it: what keeps the
|
|
41
|
+
* scroller following, and what shows the button.
|
|
42
|
+
*
|
|
43
|
+
* A11y (handled by the element, plus your part): the content is a
|
|
44
|
+
* `role="log"` that announces additions, and the button is named "Scroll to
|
|
45
|
+
* end" / "Scroll to start" (or its `label`) and is inert while there is
|
|
46
|
+
* nowhere to go. Your
|
|
47
|
+
* part: label the transcript (`aria-label` on `<ui-message-scroller-content>`).
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```html
|
|
51
|
+
* <ui-message-scroller auto-scroll class="h-96">
|
|
52
|
+
* <ui-message-scroller-viewport>
|
|
53
|
+
* <ui-message-scroller-content aria-label="Conversation">
|
|
54
|
+
* <ui-message-scroller-item message-id="m1">Hello</ui-message-scroller-item>
|
|
55
|
+
* <ui-message-scroller-item message-id="m2" scroll-anchor>What is WebJs?</ui-message-scroller-item>
|
|
56
|
+
* </ui-message-scroller-content>
|
|
57
|
+
* </ui-message-scroller-viewport>
|
|
58
|
+
* <ui-message-scroller-button></ui-message-scroller-button>
|
|
59
|
+
* </ui-message-scroller>
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
import { WebComponent, html, prop, signal, nothing } from '@webjsdev/core';
|
|
63
|
+
import { cn, takeAuthoredClass } from '../lib/utils.ts';
|
|
64
|
+
import { buttonClass, type ButtonVariant, type ButtonSize } from './button.ts';
|
|
65
|
+
|
|
66
|
+
/** The frame's classes (the element applies them). */
|
|
67
|
+
export const messageScrollerClass = (): string =>
|
|
68
|
+
'cn-message-scroller group/message-scroller relative flex size-full min-h-0 flex-col overflow-hidden';
|
|
69
|
+
/** The scrolling viewport (the element applies them). */
|
|
70
|
+
export const messageScrollerViewportClass = (): string =>
|
|
71
|
+
'cn-message-scroller-viewport size-full min-h-0 min-w-0 scroll-fade-b scrollbar-thin scrollbar-gutter-stable overflow-y-auto overscroll-contain contain-content data-autoscrolling:scrollbar-thumb-transparent data-autoscrolling:scrollbar-track-transparent data-[pending-scroll]:invisible';
|
|
72
|
+
/** The message list (the element applies them). */
|
|
73
|
+
export const messageScrollerContentClass = (): string =>
|
|
74
|
+
'cn-message-scroller-content flex h-max min-h-full flex-col';
|
|
75
|
+
/** One message (the element puts them on the item itself). */
|
|
76
|
+
export const messageScrollerItemClass = (): string =>
|
|
77
|
+
'cn-message-scroller-item min-w-0 shrink-0 [contain-intrinsic-size:auto_10rem] [content-visibility:auto]';
|
|
78
|
+
/** The scroll button (the element applies them). */
|
|
79
|
+
export const messageScrollerButtonClass = (): string =>
|
|
80
|
+
'cn-message-scroller-button absolute inset-s-1/2 -translate-x-1/2 border-border bg-background text-foreground transition-[translate,scale,opacity] duration-200 hover:bg-muted hover:text-foreground data-[active=false]:pointer-events-none data-[active=false]:scale-95 data-[active=false]:opacity-0 data-[active=false]:duration-400 data-[active=false]:ease-[cubic-bezier(0.7,0,0.84,0)] data-[active=true]:translate-y-0 data-[active=true]:scale-100 data-[active=true]:opacity-100 data-[active=true]:ease-[cubic-bezier(0.23,1,0.32,1)] data-[direction=end]:bottom-4 data-[direction=end]:data-[active=false]:translate-y-full data-[direction=start]:top-4 data-[direction=start]:data-[active=false]:-translate-y-full rtl:translate-x-1/2 data-[direction=start]:[&_svg]:rotate-180';
|
|
81
|
+
|
|
82
|
+
const ROOT = 'ui-message-scroller';
|
|
83
|
+
// How near an edge counts as being at it, in pixels, and how much of the
|
|
84
|
+
// previous turn stays in view above a newly anchored one: shadcn's defaults
|
|
85
|
+
// (DEFAULT_SCROLL_EDGE_THRESHOLD and DEFAULT_SCROLL_PREVIOUS_ITEM_PEEK).
|
|
86
|
+
const EDGE = 8;
|
|
87
|
+
const PEEK = 64;
|
|
88
|
+
|
|
89
|
+
// shadcn's className lands on the element each part renders (the frame, the
|
|
90
|
+
// viewport, the list, the button), so each host hands its authored class to
|
|
91
|
+
// that element and becomes display: contents. A block host would otherwise
|
|
92
|
+
// sit between the frame's flex column and the viewport, the viewport would
|
|
93
|
+
// grow to its content, and nothing would scroll.
|
|
94
|
+
function takeAuthored(part: HTMLElement): string | null {
|
|
95
|
+
return takeAuthoredClass(part, 'contents');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function rootOf(el: Element): UiMessageScroller | null {
|
|
99
|
+
return (typeof el.closest === 'function' ? el.closest(ROOT) : null) as UiMessageScroller | null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// --------------------------------------------------------------------------
|
|
103
|
+
// <ui-message-scroller>
|
|
104
|
+
// --------------------------------------------------------------------------
|
|
105
|
+
|
|
106
|
+
export class UiMessageScroller extends WebComponent({
|
|
107
|
+
autoScroll: prop(Boolean, { attribute: 'auto-scroll' }),
|
|
108
|
+
defaultScrollPosition: prop<'end' | 'start' | 'last-anchor'>(String, { attribute: 'default-scroll-position' }),
|
|
109
|
+
scrollEdgeThreshold: prop(Number, { attribute: 'scroll-edge-threshold' }),
|
|
110
|
+
scrollPreviousItemPeek: prop(Number, { attribute: 'scroll-previous-item-peek' }),
|
|
111
|
+
}) {
|
|
112
|
+
// Served visible: a transcript must read with JavaScript off.
|
|
113
|
+
readonly pending = signal(false);
|
|
114
|
+
/** The edges the viewport can still scroll toward. */
|
|
115
|
+
scrollable = { start: false, end: false };
|
|
116
|
+
/** The messages on screen, by `message-id`. */
|
|
117
|
+
visibleMessageIds: string[] = [];
|
|
118
|
+
_following = true;
|
|
119
|
+
_opened = false;
|
|
120
|
+
_resize: ResizeObserver | null = null;
|
|
121
|
+
_visible: IntersectionObserver | null = null;
|
|
122
|
+
_shown = new Set<string>();
|
|
123
|
+
// Turn anchoring: the anchored items already placed, the one being held,
|
|
124
|
+
// the scroll position that holds it, and the tail spacer's height.
|
|
125
|
+
_anchored = new WeakSet<Element>();
|
|
126
|
+
_held: Element | null = null;
|
|
127
|
+
_heldTop = 0;
|
|
128
|
+
_spacerHeight = 0;
|
|
129
|
+
_growFrame = 0;
|
|
130
|
+
|
|
131
|
+
constructor() {
|
|
132
|
+
super();
|
|
133
|
+
this.autoScroll = false;
|
|
134
|
+
this.defaultScrollPosition = 'end';
|
|
135
|
+
this.scrollEdgeThreshold = EDGE;
|
|
136
|
+
this.scrollPreviousItemPeek = PEEK;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
connectedCallback(): void {
|
|
140
|
+
// A scroller built in the browser has not painted yet, so it is held
|
|
141
|
+
// invisible until its opening position applies. One the server rendered
|
|
142
|
+
// has painted already (its frame is in the markup), and hiding it now
|
|
143
|
+
// would blank text the reader can see, so it moves in place instead.
|
|
144
|
+
if (!this._opened && typeof this.querySelector === 'function' && !this.querySelector('[data-slot="message-scroller"]')) this.pending.set(true);
|
|
145
|
+
super.connectedCallback?.();
|
|
146
|
+
queueMicrotask(() => this._setup());
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
_edge(): number {
|
|
150
|
+
const n = Number(this.scrollEdgeThreshold);
|
|
151
|
+
return Number.isFinite(n) && n >= 0 ? n : EDGE;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
_peek(): number {
|
|
155
|
+
const n = Number(this.scrollPreviousItemPeek);
|
|
156
|
+
return Number.isFinite(n) && n >= 0 ? n : PEEK;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
disconnectedCallback(): void {
|
|
160
|
+
cancelAnimationFrame(this._growFrame);
|
|
161
|
+
this._resize?.disconnect();
|
|
162
|
+
this._visible?.disconnect();
|
|
163
|
+
this._resize = null;
|
|
164
|
+
this._visible = null;
|
|
165
|
+
super.disconnectedCallback?.();
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
render() {
|
|
169
|
+
return html`<div data-slot="message-scroller" ?data-pending-scroll=${this.pending.get()} class=${cn(messageScrollerClass(), takeAuthored(this))}><slot></slot></div>`;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
viewport(): HTMLElement | null {
|
|
173
|
+
return this.querySelector<HTMLElement>('[data-slot="message-scroller-viewport"]');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
_content(): HTMLElement | null {
|
|
177
|
+
return this.querySelector<HTMLElement>('[data-slot="message-scroller-content"]');
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
_items(): UiMessageScrollerItem[] {
|
|
181
|
+
if (typeof this.querySelectorAll !== 'function') return [];
|
|
182
|
+
return Array.from(this.querySelectorAll<UiMessageScrollerItem>('ui-message-scroller-item')).filter((i) => rootOf(i) === this);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
_setup(): void {
|
|
186
|
+
const viewport = this.viewport();
|
|
187
|
+
const content = this._content();
|
|
188
|
+
if (!viewport || !content) {
|
|
189
|
+
requestAnimationFrame(() => this._setup());
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
viewport.addEventListener('scroll', this._onScroll, { passive: true });
|
|
193
|
+
// New or growing messages: follow them if the reader is at the end.
|
|
194
|
+
if (typeof ResizeObserver === 'function') {
|
|
195
|
+
// Following the end is decided at once, before the next frame's scroll
|
|
196
|
+
// event can read the grown list as "scrolled away". The spacer is fitted
|
|
197
|
+
// a frame later, since resizing it inside the observed content would
|
|
198
|
+
// otherwise loop the observer; its own resize then comes back here.
|
|
199
|
+
this._resize = new ResizeObserver(() => {
|
|
200
|
+
this._onGrow();
|
|
201
|
+
cancelAnimationFrame(this._growFrame);
|
|
202
|
+
this._growFrame = requestAnimationFrame(() => this._fitSpacer());
|
|
203
|
+
});
|
|
204
|
+
this._resize.observe(content);
|
|
205
|
+
}
|
|
206
|
+
if (typeof IntersectionObserver === 'function') {
|
|
207
|
+
this._visible = new IntersectionObserver(this._onIntersect, { root: viewport });
|
|
208
|
+
for (const item of this._items()) this._visible.observe(item);
|
|
209
|
+
}
|
|
210
|
+
requestAnimationFrame(() => this._open());
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// The opening position, applied once the messages have laid out.
|
|
214
|
+
_open(): void {
|
|
215
|
+
const viewport = this.viewport();
|
|
216
|
+
if (!viewport) return;
|
|
217
|
+
const pos = this.defaultScrollPosition;
|
|
218
|
+
if (pos === 'start') viewport.scrollTop = 0;
|
|
219
|
+
else if (pos === 'last-anchor') {
|
|
220
|
+
const anchors = this._items().filter((i) => i.hasAttribute('scroll-anchor'));
|
|
221
|
+
const last = anchors[anchors.length - 1];
|
|
222
|
+
if (last) this._scrollToItem(last, 'start', 'auto');
|
|
223
|
+
else viewport.scrollTop = viewport.scrollHeight;
|
|
224
|
+
} else viewport.scrollTop = viewport.scrollHeight;
|
|
225
|
+
this._opened = true;
|
|
226
|
+
// The turns present at open are placed by the opening position.
|
|
227
|
+
for (const item of this._items()) if (item.hasAttribute('scroll-anchor')) this._anchored.add(item);
|
|
228
|
+
this._following = this._atEnd();
|
|
229
|
+
this.pending.set(false);
|
|
230
|
+
// The viewport draws its own pending flag, so it redraws with it.
|
|
231
|
+
this.querySelector<UiMessageScrollerViewport>('ui-message-scroller-viewport')?.requestUpdate?.();
|
|
232
|
+
this._measure();
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
_atEnd(): boolean {
|
|
236
|
+
const v = this.viewport();
|
|
237
|
+
return !!v && v.scrollHeight - v.scrollTop - v.clientHeight <= this._edge();
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
_onScroll = (): void => {
|
|
241
|
+
// A scroll the reader makes away from the end stops the following; one
|
|
242
|
+
// back to the end resumes it.
|
|
243
|
+
if (this._opened) this._following = this._atEnd();
|
|
244
|
+
this._measure();
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
_onGrow = (): void => {
|
|
248
|
+
if (!this._opened) return;
|
|
249
|
+
if (this.autoScroll && this._following) {
|
|
250
|
+
const v = this.viewport();
|
|
251
|
+
if (v) {
|
|
252
|
+
v.setAttribute('data-autoscrolling', '');
|
|
253
|
+
v.scrollTop = v.scrollHeight;
|
|
254
|
+
requestAnimationFrame(() => v.removeAttribute('data-autoscrolling'));
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
this._measure();
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
_measure(): void {
|
|
261
|
+
const v = this.viewport();
|
|
262
|
+
if (!v) return;
|
|
263
|
+
const edge = this._edge();
|
|
264
|
+
const next = { start: v.scrollTop > edge, end: v.scrollHeight - v.scrollTop - v.clientHeight > edge };
|
|
265
|
+
if (next.start !== this.scrollable.start || next.end !== this.scrollable.end) {
|
|
266
|
+
this.scrollable = next;
|
|
267
|
+
this.dispatchEvent(new CustomEvent('ui-scrollable-change', { bubbles: true, detail: { ...next } }));
|
|
268
|
+
}
|
|
269
|
+
for (const b of Array.from(this.querySelectorAll<UiMessageScrollerButton>('ui-message-scroller-button'))) b.requestUpdate?.();
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
_onIntersect = (entries: IntersectionObserverEntry[]): void => {
|
|
273
|
+
for (const e of entries) {
|
|
274
|
+
const id = (e.target as Element).getAttribute('message-id');
|
|
275
|
+
if (!id) continue;
|
|
276
|
+
if (e.isIntersecting) this._shown.add(id);
|
|
277
|
+
else this._shown.delete(id);
|
|
278
|
+
}
|
|
279
|
+
const ids = this._items().map((i) => i.getAttribute('message-id')).filter((id): id is string => !!id && this._shown.has(id));
|
|
280
|
+
this.visibleMessageIds = ids;
|
|
281
|
+
this.dispatchEvent(new CustomEvent('ui-visibility-change', { bubbles: true, detail: { visibleMessageIds: ids } }));
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
// An item that arrives after setup is tracked too.
|
|
285
|
+
_itemConnected(item: UiMessageScrollerItem): void {
|
|
286
|
+
this._visible?.observe(item);
|
|
287
|
+
// A new turn marked scroll-anchor settles near the top, a peek of the
|
|
288
|
+
// previous turn above it, as shadcn's does. Placed once per item, since
|
|
289
|
+
// an item can connect again when the list re-renders around it.
|
|
290
|
+
if (this._opened && item.hasAttribute('scroll-anchor') && !this._anchored.has(item)) {
|
|
291
|
+
this._anchored.add(item);
|
|
292
|
+
requestAnimationFrame(() => this._anchorTurn(item));
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
_spacer(): HTMLElement | null {
|
|
297
|
+
return this.querySelector<HTMLElement>('[data-message-scroller-spacer]');
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Scroll a new anchored turn to the top, keeping the peek of the turn
|
|
301
|
+
// before it, with room made below by the tail spacer when the conversation
|
|
302
|
+
// is still too short to scroll that far.
|
|
303
|
+
_anchorTurn(item: Element): void {
|
|
304
|
+
const v = this.viewport();
|
|
305
|
+
const content = this._content();
|
|
306
|
+
if (!v || !content || !item.isConnected) return;
|
|
307
|
+
const pad = parseFloat(getComputedStyle(content).paddingTop) || 0;
|
|
308
|
+
// Where the turn sits in the layout, read through offsetTop so a
|
|
309
|
+
// transform on it (an entrance sliding it up) does not move the target.
|
|
310
|
+
const listTop = v.scrollTop + content.getBoundingClientRect().top - v.getBoundingClientRect().top;
|
|
311
|
+
const top = listTop + (item as HTMLElement).offsetTop - content.offsetTop;
|
|
312
|
+
this._held = item;
|
|
313
|
+
this._heldTop = Math.max(0, top - pad - this._peek());
|
|
314
|
+
this._fitSpacer();
|
|
315
|
+
v.scrollTo({ top: this._heldTop, behavior: this._behavior() });
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// The spacer fills exactly the room the held turn needs below it, so it
|
|
319
|
+
// shrinks as the reply streams in and the turn stays where it is. Once the
|
|
320
|
+
// reply fills the viewport the spacer is gone, and a following viewport
|
|
321
|
+
// (auto-scroll) takes over from the end.
|
|
322
|
+
_fitSpacer(): void {
|
|
323
|
+
const v = this.viewport();
|
|
324
|
+
const spacer = this._spacer();
|
|
325
|
+
if (!v || !spacer) return;
|
|
326
|
+
if (this._held && !this._held.isConnected) this._held = null;
|
|
327
|
+
const natural = v.scrollHeight - this._spacerHeight;
|
|
328
|
+
const next = this._held ? Math.max(0, Math.ceil(this._heldTop + v.clientHeight - natural)) : 0;
|
|
329
|
+
if (next === this._spacerHeight) return;
|
|
330
|
+
this._spacerHeight = next;
|
|
331
|
+
if (next === 0) this._held = null;
|
|
332
|
+
const content = this._content();
|
|
333
|
+
const gap = content ? parseFloat(getComputedStyle(content).rowGap) || 0 : 0;
|
|
334
|
+
spacer.hidden = next === 0;
|
|
335
|
+
spacer.style.height = `${next}px`;
|
|
336
|
+
// The list's flex gap would add itself above the spacer, so it is taken
|
|
337
|
+
// back. The spacer is never the browser's scroll anchor (overflow-anchor:
|
|
338
|
+
// none): it is the last thing in view, so the browser would keep IT in
|
|
339
|
+
// place as the reply grows above it, pushing the held turn up a line at
|
|
340
|
+
// a time.
|
|
341
|
+
spacer.style.marginTop = next > 0 ? `${-gap}px` : '';
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
_scrollToItem(item: Element, align: 'start' | 'center' | 'end' | 'nearest', behavior: ScrollBehavior): void {
|
|
345
|
+
const v = this.viewport();
|
|
346
|
+
if (!v) return;
|
|
347
|
+
const vr = v.getBoundingClientRect();
|
|
348
|
+
const ir = item.getBoundingClientRect();
|
|
349
|
+
let top = v.scrollTop + (ir.top - vr.top);
|
|
350
|
+
if (align === 'end') top -= vr.height - ir.height;
|
|
351
|
+
else if (align === 'center') top -= (vr.height - ir.height) / 2;
|
|
352
|
+
else if (align === 'nearest') {
|
|
353
|
+
if (ir.top >= vr.top && ir.bottom <= vr.bottom) return;
|
|
354
|
+
if (ir.bottom > vr.bottom) top -= vr.height - ir.height;
|
|
355
|
+
}
|
|
356
|
+
v.scrollTo({ top, behavior });
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
_behavior(): ScrollBehavior {
|
|
360
|
+
return typeof matchMedia === 'function' && matchMedia('(prefers-reduced-motion: reduce)').matches ? 'auto' : 'smooth';
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/** Scroll to the newest message, and follow new ones again. */
|
|
364
|
+
scrollToEnd(): void {
|
|
365
|
+
const v = this.viewport();
|
|
366
|
+
if (!v) return;
|
|
367
|
+
this._following = true;
|
|
368
|
+
v.scrollTo({ top: v.scrollHeight, behavior: this._behavior() });
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/** Scroll to the oldest message. */
|
|
372
|
+
scrollToStart(): void {
|
|
373
|
+
this.viewport()?.scrollTo({ top: 0, behavior: this._behavior() });
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/** Scroll the message with `id` into view. */
|
|
377
|
+
scrollToMessage(id: string, opts: { align?: 'start' | 'center' | 'end' | 'nearest'; behavior?: ScrollBehavior } = {}): void {
|
|
378
|
+
const item = this._items().find((i) => i.getAttribute('message-id') === id);
|
|
379
|
+
if (item) this._scrollToItem(item, opts.align ?? 'start', opts.behavior ?? this._behavior());
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
UiMessageScroller.register('ui-message-scroller');
|
|
383
|
+
|
|
384
|
+
// --------------------------------------------------------------------------
|
|
385
|
+
// <ui-message-scroller-viewport> and <ui-message-scroller-content>
|
|
386
|
+
// --------------------------------------------------------------------------
|
|
387
|
+
|
|
388
|
+
export class UiMessageScrollerViewport extends WebComponent {
|
|
389
|
+
render() {
|
|
390
|
+
const pending = rootOf(this)?.pending?.get() ?? false;
|
|
391
|
+
return html`<div data-slot="message-scroller-viewport" ?data-pending-scroll=${!!pending} class=${cn(messageScrollerViewportClass(), takeAuthored(this))}><slot></slot></div>`;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
UiMessageScrollerViewport.register('ui-message-scroller-viewport');
|
|
395
|
+
|
|
396
|
+
export class UiMessageScrollerContent extends WebComponent {
|
|
397
|
+
render() {
|
|
398
|
+
return html`<div
|
|
399
|
+
data-slot="message-scroller-content"
|
|
400
|
+
role="log"
|
|
401
|
+
aria-relevant="additions"
|
|
402
|
+
aria-label=${this.getAttribute?.('aria-label') || nothing}
|
|
403
|
+
class=${cn(messageScrollerContentClass(), takeAuthored(this))}
|
|
404
|
+
><slot></slot><div data-message-scroller-spacer aria-hidden="true" hidden style="overflow-anchor: none"></div></div>`;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
UiMessageScrollerContent.register('ui-message-scroller-content');
|
|
408
|
+
|
|
409
|
+
// --------------------------------------------------------------------------
|
|
410
|
+
// <ui-message-scroller-item>
|
|
411
|
+
// --------------------------------------------------------------------------
|
|
412
|
+
|
|
413
|
+
export class UiMessageScrollerItem extends WebComponent {
|
|
414
|
+
_authored: string | null = null;
|
|
415
|
+
|
|
416
|
+
connectedCallback(): void {
|
|
417
|
+
super.connectedCallback?.();
|
|
418
|
+
rootOf(this)?._itemConnected(this);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// An item is a flex child of the column, so it carries its own classes.
|
|
422
|
+
render() {
|
|
423
|
+
this._authored = takeAuthoredClass(this);
|
|
424
|
+
if (typeof this.setAttribute === 'function') {
|
|
425
|
+
this.setAttribute('class', cn(messageScrollerItemClass(), this._authored));
|
|
426
|
+
this.setAttribute('data-slot', 'message-scroller-item');
|
|
427
|
+
}
|
|
428
|
+
return html`<slot></slot>`;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
UiMessageScrollerItem.register('ui-message-scroller-item');
|
|
432
|
+
|
|
433
|
+
// --------------------------------------------------------------------------
|
|
434
|
+
// <ui-message-scroller-button>
|
|
435
|
+
// --------------------------------------------------------------------------
|
|
436
|
+
|
|
437
|
+
export class UiMessageScrollerButton extends WebComponent({
|
|
438
|
+
direction: prop<'end' | 'start'>(String),
|
|
439
|
+
// The button's accessible name, for a transcript in another language.
|
|
440
|
+
label: String,
|
|
441
|
+
variant: String,
|
|
442
|
+
size: String,
|
|
443
|
+
}) {
|
|
444
|
+
constructor() {
|
|
445
|
+
super();
|
|
446
|
+
this.direction = 'end';
|
|
447
|
+
this.variant = 'secondary';
|
|
448
|
+
this.size = 'icon-sm';
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
render() {
|
|
452
|
+
const root = rootOf(this);
|
|
453
|
+
const toStart = this.direction === 'start';
|
|
454
|
+
const active = !!root && (toStart ? root.scrollable.start : root.scrollable.end);
|
|
455
|
+
return html`<button
|
|
456
|
+
type="button"
|
|
457
|
+
data-slot="message-scroller-button"
|
|
458
|
+
data-direction=${toStart ? 'start' : 'end'}
|
|
459
|
+
data-variant=${this.variant}
|
|
460
|
+
data-size=${this.size}
|
|
461
|
+
data-active=${active ? 'true' : 'false'}
|
|
462
|
+
tabindex=${active ? nothing : '-1'}
|
|
463
|
+
aria-hidden=${active ? nothing : 'true'}
|
|
464
|
+
class=${cn(buttonClass({ variant: this.variant as ButtonVariant, size: this.size as ButtonSize }), messageScrollerButtonClass(), takeAuthored(this))}
|
|
465
|
+
@click=${() => (toStart ? root?.scrollToStart() : root?.scrollToEnd())}
|
|
466
|
+
aria-label=${this.label || nothing}
|
|
467
|
+
><slot><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 5v14"></path><path d="m19 12-7 7-7-7"></path></svg>${this.label ? '' : html`<span class="sr-only">${toStart ? 'Scroll to start' : 'Scroll to end'}</span>`}</slot></button>`;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
UiMessageScrollerButton.register('ui-message-scroller-button');
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message: one entry in a conversation (a chat bubble with its avatar,
|
|
3
|
+
* header and footer), and the group that stacks several. Tier-1 class
|
|
4
|
+
* helper; compose with plain elements, and pair with the bubble helpers for
|
|
5
|
+
* the speech bubble itself.
|
|
6
|
+
*
|
|
7
|
+
* shadcn parity:
|
|
8
|
+
* MessageGroup → messageGroupClass()
|
|
9
|
+
* Message → messageClass(), with data-align="start" | "end"
|
|
10
|
+
* MessageAvatar → messageAvatarClass()
|
|
11
|
+
* MessageContent → messageContentClass()
|
|
12
|
+
* MessageHeader → messageHeaderClass()
|
|
13
|
+
* MessageFooter → messageFooterClass()
|
|
14
|
+
*
|
|
15
|
+
* `data-align="end"` on the message mirrors it to the inline end (the user's
|
|
16
|
+
* own messages), and the footer follows. Alignment is logical, so it flips
|
|
17
|
+
* under `dir="rtl"`.
|
|
18
|
+
*
|
|
19
|
+
* A11y (required for accessible output): render the conversation as a list
|
|
20
|
+
* (`<ol>` of messages, or `role="log"` on the container when new messages
|
|
21
|
+
* arrive live, which also makes them announced). Each message should say who
|
|
22
|
+
* sent it in text a screen reader reaches, the header or a visually hidden
|
|
23
|
+
* name, and an avatar image needs `alt` (or `alt=""` when the name is
|
|
24
|
+
* already in the header).
|
|
25
|
+
*
|
|
26
|
+
* Design tokens used: --muted, --muted-foreground.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```html
|
|
30
|
+
* <ol class=${messageGroupClass()} role="log" aria-label="Conversation">
|
|
31
|
+
* <li class=${messageClass()} data-align="end">
|
|
32
|
+
* <div class=${messageContentClass()}>
|
|
33
|
+
* <div class=${messageHeaderClass()}>You</div>
|
|
34
|
+
* <p>Can you ship it today?</p>
|
|
35
|
+
* </div>
|
|
36
|
+
* </li>
|
|
37
|
+
* </ol>
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
/** The stack of messages. */
|
|
42
|
+
export function messageGroupClass(): string {
|
|
43
|
+
return 'cn-message-group flex min-w-0 flex-col';
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** One message row. `data-align="end"` mirrors it to the inline end. */
|
|
47
|
+
export function messageClass(): string {
|
|
48
|
+
return 'cn-message group/message relative flex w-full min-w-0 data-[align=end]:flex-row-reverse';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** The sender's avatar beside the message. */
|
|
52
|
+
export function messageAvatarClass(): string {
|
|
53
|
+
return 'cn-message-avatar flex w-fit shrink-0 items-center justify-center self-end overflow-hidden rounded-full bg-muted';
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** The column holding the header, the bubble and the footer. */
|
|
57
|
+
export function messageContentClass(): string {
|
|
58
|
+
return 'cn-message-content flex w-full min-w-0 flex-col wrap-break-word';
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** The sender name and time above the bubble. */
|
|
62
|
+
export function messageHeaderClass(): string {
|
|
63
|
+
return 'cn-message-header flex max-w-full min-w-0 items-center';
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Actions or status below the bubble. */
|
|
67
|
+
export function messageFooterClass(): string {
|
|
68
|
+
return 'cn-message-footer flex max-w-full min-w-0 items-center group-data-[align=end]/message:justify-end';
|
|
69
|
+
}
|
|
@@ -73,18 +73,18 @@ export type NativeSelectSize = 'default' | 'sm';
|
|
|
73
73
|
// convention that every part has a class helper.
|
|
74
74
|
|
|
75
75
|
export const nativeSelectWrapperClass = (): string =>
|
|
76
|
-
'group/native-select relative w-fit has-[select:disabled]:opacity-50';
|
|
76
|
+
'cn-native-select-wrapper group/native-select relative w-fit has-[select:disabled]:opacity-50';
|
|
77
77
|
|
|
78
78
|
export function nativeSelectClass(): string {
|
|
79
79
|
return cn(
|
|
80
|
-
'
|
|
81
|
-
'
|
|
82
|
-
'
|
|
80
|
+
'cn-native-select outline-none disabled:pointer-events-none disabled:cursor-not-allowed',
|
|
81
|
+
'cn-native-select-part-2',
|
|
82
|
+
'cn-native-select-part-3',
|
|
83
83
|
);
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
export const nativeSelectIconClass = (): string =>
|
|
87
|
-
'pointer-events-none absolute
|
|
87
|
+
'cn-native-select-icon pointer-events-none absolute select-none';
|
|
88
88
|
|
|
89
89
|
/** Option / optgroup styling: forces themed background even in dark mode. */
|
|
90
90
|
export const nativeSelectOptionClass = (): string => 'bg-[Canvas] text-[CanvasText]';
|