@open-mercato/core 0.7.1-develop.7203.1.9396450b78 → 0.8.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/.turbo/turbo-build.log +1 -1
- package/build.mjs +11 -0
- package/dist/modules/catalog/backend/catalog/products/[id]/page.js +23 -0
- package/dist/modules/catalog/backend/catalog/products/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/companies/[id]/page.js +2 -1
- package/dist/modules/customers/backend/customers/companies/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js +2 -1
- package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/people/[id]/page.js +2 -2
- package/dist/modules/customers/backend/customers/people/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/people-v2/[id]/page.js +2 -1
- package/dist/modules/customers/backend/customers/people-v2/[id]/page.js.map +2 -2
- package/dist/modules/customers/lib/detailHelpers.js +4 -0
- package/dist/modules/customers/lib/detailHelpers.js.map +2 -2
- package/dist/modules/customers/lib/todoCompatibility.js +5 -0
- package/dist/modules/customers/lib/todoCompatibility.js.map +2 -2
- package/dist/modules/design_system/backend/design-system/page.meta.js +3 -2
- package/dist/modules/design_system/backend/design-system/page.meta.js.map +2 -2
- package/dist/modules/design_system/gallery/assets/ai-prompt-image.png +0 -0
- package/dist/modules/design_system/gallery/assets/avatar-illustration.png +0 -0
- package/dist/modules/design_system/gallery/assets/avatar-memoji.png +0 -0
- package/dist/modules/design_system/gallery/assets/avatar-photo.png +0 -0
- package/dist/modules/design_system/gallery/assets/blog-photo-0.png +0 -0
- package/dist/modules/design_system/gallery/assets/contact-support-portrait.png +0 -0
- package/dist/modules/design_system/gallery/assets/crypto-ada.png +0 -0
- package/dist/modules/design_system/gallery/assets/crypto-avax.png +0 -0
- package/dist/modules/design_system/gallery/assets/crypto-bnb.png +0 -0
- package/dist/modules/design_system/gallery/assets/crypto-brand.png +0 -0
- package/dist/modules/design_system/gallery/assets/crypto-btc.png +0 -0
- package/dist/modules/design_system/gallery/assets/crypto-clock.svg +3 -0
- package/dist/modules/design_system/gallery/assets/crypto-crown.svg +3 -0
- package/dist/modules/design_system/gallery/assets/crypto-dot.png +0 -0
- package/dist/modules/design_system/gallery/assets/crypto-eth.png +0 -0
- package/dist/modules/design_system/gallery/assets/crypto-hbar.png +0 -0
- package/dist/modules/design_system/gallery/assets/crypto-history.svg +3 -0
- package/dist/modules/design_system/gallery/assets/crypto-link.svg +3 -0
- package/dist/modules/design_system/gallery/assets/crypto-menu.svg +3 -0
- package/dist/modules/design_system/gallery/assets/crypto-metamask.png +0 -0
- package/dist/modules/design_system/gallery/assets/crypto-more.svg +3 -0
- package/dist/modules/design_system/gallery/assets/crypto-phantom.png +0 -0
- package/dist/modules/design_system/gallery/assets/crypto-rewards.svg +3 -0
- package/dist/modules/design_system/gallery/assets/crypto-sad-face.png +0 -0
- package/dist/modules/design_system/gallery/assets/crypto-sol.png +0 -0
- package/dist/modules/design_system/gallery/assets/crypto-star.svg +3 -0
- package/dist/modules/design_system/gallery/assets/crypto-status-cancelled.svg +3 -0
- package/dist/modules/design_system/gallery/assets/crypto-status-pending.svg +3 -0
- package/dist/modules/design_system/gallery/assets/crypto-status-success.svg +3 -0
- package/dist/modules/design_system/gallery/assets/crypto-status-warning.svg +3 -0
- package/dist/modules/design_system/gallery/assets/crypto-trx.png +0 -0
- package/dist/modules/design_system/gallery/assets/crypto-usdc.png +0 -0
- package/dist/modules/design_system/gallery/assets/crypto-usdt.png +0 -0
- package/dist/modules/design_system/gallery/assets/crypto-walletconnect.png +0 -0
- package/dist/modules/design_system/gallery/assets/crypto-wei.png +0 -0
- package/dist/modules/design_system/gallery/assets/crypto-xrp.png +0 -0
- package/dist/modules/design_system/gallery/assets/cryptocurrency-artwork.js +67 -0
- package/dist/modules/design_system/gallery/assets/cryptocurrency-artwork.js.map +7 -0
- package/dist/modules/design_system/gallery/assets/github-logo.png +0 -0
- package/dist/modules/design_system/gallery/assets/images.d.js +1 -0
- package/dist/modules/design_system/gallery/assets/images.d.js.map +7 -0
- package/dist/modules/design_system/gallery/assets/input-european-union.svg +18 -0
- package/dist/modules/design_system/gallery/assets/landing-teams-01.png +0 -0
- package/dist/modules/design_system/gallery/assets/marketing-artwork.js +83 -0
- package/dist/modules/design_system/gallery/assets/marketing-artwork.js.map +7 -0
- package/dist/modules/design_system/gallery/assets/menu-apex.svg +18 -0
- package/dist/modules/design_system/gallery/assets/menu-mastercard.svg +7 -0
- package/dist/modules/design_system/gallery/assets/menu-spotify.svg +4 -0
- package/dist/modules/design_system/gallery/assets/menu-united-states.svg +16 -0
- package/dist/modules/design_system/gallery/assets/select-google.png +0 -0
- package/dist/modules/design_system/gallery/assets/select-poland.png +0 -0
- package/dist/modules/design_system/gallery/assets/selection-artwork.js +5 -0
- package/dist/modules/design_system/gallery/assets/selection-artwork.js.map +7 -0
- package/dist/modules/design_system/gallery/assets/social-apple-filled.svg +4 -0
- package/dist/modules/design_system/gallery/assets/social-apple-stroke.svg +4 -0
- package/dist/modules/design_system/gallery/assets/social-dropbox-filled.svg +5 -0
- package/dist/modules/design_system/gallery/assets/social-dropbox-stroke.svg +5 -0
- package/dist/modules/design_system/gallery/assets/social-facebook-filled.svg +4 -0
- package/dist/modules/design_system/gallery/assets/social-facebook-stroke.svg +10 -0
- package/dist/modules/design_system/gallery/assets/social-github-filled.svg +4 -0
- package/dist/modules/design_system/gallery/assets/social-github-stroke.svg +4 -0
- package/dist/modules/design_system/gallery/assets/social-google-filled.svg +7 -0
- package/dist/modules/design_system/gallery/assets/social-google-stroke.svg +7 -0
- package/dist/modules/design_system/gallery/assets/social-linkedin-filled.svg +4 -0
- package/dist/modules/design_system/gallery/assets/social-linkedin-stroke.svg +4 -0
- package/dist/modules/design_system/gallery/assets/social-x-filled.svg +4 -0
- package/dist/modules/design_system/gallery/assets/social-x-stroke.svg +4 -0
- package/dist/modules/design_system/gallery/assets/style-studio-product.png +0 -0
- package/dist/modules/design_system/gallery/assets/upload-images.js +8 -0
- package/dist/modules/design_system/gallery/assets/upload-images.js.map +7 -0
- package/dist/modules/design_system/gallery/components/BrandStylePreview.js +74 -0
- package/dist/modules/design_system/gallery/components/BrandStylePreview.js.map +7 -0
- package/dist/modules/design_system/gallery/components/CodeSnippet.js +31 -31
- package/dist/modules/design_system/gallery/components/CodeSnippet.js.map +2 -2
- package/dist/modules/design_system/gallery/components/ColorPlayground.js +408 -0
- package/dist/modules/design_system/gallery/components/ColorPlayground.js.map +7 -0
- package/dist/modules/design_system/gallery/components/ColorStudioCompositions.js +166 -0
- package/dist/modules/design_system/gallery/components/ColorStudioCompositions.js.map +7 -0
- package/dist/modules/design_system/gallery/components/ColorStudioSpecimen.js +182 -0
- package/dist/modules/design_system/gallery/components/ColorStudioSpecimen.js.map +7 -0
- package/dist/modules/design_system/gallery/components/CompactEntrySpecimen.js +80 -0
- package/dist/modules/design_system/gallery/components/CompactEntrySpecimen.js.map +7 -0
- package/dist/modules/design_system/gallery/components/ComponentIndex.js +42 -0
- package/dist/modules/design_system/gallery/components/ComponentIndex.js.map +7 -0
- package/dist/modules/design_system/gallery/components/DesignSystemHome.js +69 -0
- package/dist/modules/design_system/gallery/components/DesignSystemHome.js.map +7 -0
- package/dist/modules/design_system/gallery/components/EntryCard.js +47 -41
- package/dist/modules/design_system/gallery/components/EntryCard.js.map +2 -2
- package/dist/modules/design_system/gallery/components/EntryHeader.js +68 -0
- package/dist/modules/design_system/gallery/components/EntryHeader.js.map +7 -0
- package/dist/modules/design_system/gallery/components/FamilyEntryPreview.js +19 -0
- package/dist/modules/design_system/gallery/components/FamilyEntryPreview.js.map +7 -0
- package/dist/modules/design_system/gallery/components/FoundationGuide.js +46 -0
- package/dist/modules/design_system/gallery/components/FoundationGuide.js.map +7 -0
- package/dist/modules/design_system/gallery/components/GalleryLink.js +21 -0
- package/dist/modules/design_system/gallery/components/GalleryLink.js.map +7 -0
- package/dist/modules/design_system/gallery/components/GalleryNavigation.js +82 -0
- package/dist/modules/design_system/gallery/components/GalleryNavigation.js.map +7 -0
- package/dist/modules/design_system/gallery/components/GalleryShell.js +147 -67
- package/dist/modules/design_system/gallery/components/GalleryShell.js.map +3 -3
- package/dist/modules/design_system/gallery/components/IconsBrowser.js +33 -0
- package/dist/modules/design_system/gallery/components/IconsBrowser.js.map +7 -0
- package/dist/modules/design_system/gallery/components/SourceLibrary.js +93 -0
- package/dist/modules/design_system/gallery/components/SourceLibrary.js.map +7 -0
- package/dist/modules/design_system/gallery/components/StyleAgents.js +139 -0
- package/dist/modules/design_system/gallery/components/StyleAgents.js.map +7 -0
- package/dist/modules/design_system/gallery/components/ThemeTokenReference.js +101 -0
- package/dist/modules/design_system/gallery/components/ThemeTokenReference.js.map +7 -0
- package/dist/modules/design_system/gallery/components/VariantPreview.js +2 -2
- package/dist/modules/design_system/gallery/components/VariantPreview.js.map +2 -2
- package/dist/modules/design_system/gallery/components/colorPreview.js +58 -0
- package/dist/modules/design_system/gallery/components/colorPreview.js.map +7 -0
- package/dist/modules/design_system/gallery/components/colorStudio.js +218 -0
- package/dist/modules/design_system/gallery/components/colorStudio.js.map +7 -0
- package/dist/modules/design_system/gallery/components/styleAgentBranding.js +46 -0
- package/dist/modules/design_system/gallery/components/styleAgentBranding.js.map +7 -0
- package/dist/modules/design_system/gallery/components/themeTokens.js +32 -0
- package/dist/modules/design_system/gallery/components/themeTokens.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/ai-product-code.generated.js +194 -0
- package/dist/modules/design_system/gallery/demos/ai-product-code.generated.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/ai-product.js +423 -0
- package/dist/modules/design_system/gallery/demos/ai-product.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/buttons.js +289 -0
- package/dist/modules/design_system/gallery/demos/buttons.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/cryptocurrency.js +1708 -0
- package/dist/modules/design_system/gallery/demos/cryptocurrency.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/feeds-code.generated.js +92 -0
- package/dist/modules/design_system/gallery/demos/feeds-code.generated.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/feeds.js +148 -0
- package/dist/modules/design_system/gallery/demos/feeds.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/file-upload.js +72 -0
- package/dist/modules/design_system/gallery/demos/file-upload.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/foundation-reference-data.js +4875 -0
- package/dist/modules/design_system/gallery/demos/foundation-reference-data.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/foundation-references.js +188 -0
- package/dist/modules/design_system/gallery/demos/foundation-references.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/key-components-code.generated.js +6 -0
- package/dist/modules/design_system/gallery/demos/key-components-code.generated.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/key-components.js +127 -0
- package/dist/modules/design_system/gallery/demos/key-components.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/marketing-controls-code.generated.js +98 -0
- package/dist/modules/design_system/gallery/demos/marketing-controls-code.generated.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/marketing-controls.js +134 -0
- package/dist/modules/design_system/gallery/demos/marketing-controls.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/marketing-widgets-code.generated.js +195 -0
- package/dist/modules/design_system/gallery/demos/marketing-widgets-code.generated.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/marketing-widgets.js +586 -0
- package/dist/modules/design_system/gallery/demos/marketing-widgets.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/menu-fixtures.js +40 -0
- package/dist/modules/design_system/gallery/demos/menu-fixtures.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/menu-sources.js +295 -0
- package/dist/modules/design_system/gallery/demos/menu-sources.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/menus.js +150 -0
- package/dist/modules/design_system/gallery/demos/menus.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/rating-reviews.js +53 -0
- package/dist/modules/design_system/gallery/demos/rating-reviews.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/registered-icons.js +104 -0
- package/dist/modules/design_system/gallery/demos/registered-icons.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/selection-cards.js +99 -0
- package/dist/modules/design_system/gallery/demos/selection-cards.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/shell-code.generated.js +189 -0
- package/dist/modules/design_system/gallery/demos/shell-code.generated.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/shell.js +330 -0
- package/dist/modules/design_system/gallery/demos/shell.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/source-artwork.js +120 -0
- package/dist/modules/design_system/gallery/demos/source-artwork.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/source-filters-code.generated.js +125 -0
- package/dist/modules/design_system/gallery/demos/source-filters-code.generated.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/source-filters.js +185 -0
- package/dist/modules/design_system/gallery/demos/source-filters.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/source-icons.js +69 -0
- package/dist/modules/design_system/gallery/demos/source-icons.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/table-cells-code.generated.js +150 -0
- package/dist/modules/design_system/gallery/demos/table-cells-code.generated.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/table-cells.js +181 -0
- package/dist/modules/design_system/gallery/demos/table-cells.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/text-inputs.js +113 -0
- package/dist/modules/design_system/gallery/demos/text-inputs.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/time-picker-code.generated.js +41 -0
- package/dist/modules/design_system/gallery/demos/time-picker-code.generated.js.map +7 -0
- package/dist/modules/design_system/gallery/demos/time-picker.js +41 -0
- package/dist/modules/design_system/gallery/demos/time-picker.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/banners.js +123 -60
- package/dist/modules/design_system/gallery/entries/banners.js.map +2 -2
- package/dist/modules/design_system/gallery/entries/buttons-snippets.js +433 -0
- package/dist/modules/design_system/gallery/entries/buttons-snippets.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/buttons.js +127 -148
- package/dist/modules/design_system/gallery/entries/buttons.js.map +2 -2
- package/dist/modules/design_system/gallery/entries/charts.js +532 -186
- package/dist/modules/design_system/gallery/entries/charts.js.map +2 -2
- package/dist/modules/design_system/gallery/entries/dates.js +191 -3
- package/dist/modules/design_system/gallery/entries/dates.js.map +2 -2
- package/dist/modules/design_system/gallery/entries/detail.js +118 -77
- package/dist/modules/design_system/gallery/entries/detail.js.map +2 -2
- package/dist/modules/design_system/gallery/entries/display.js +1449 -240
- package/dist/modules/design_system/gallery/entries/display.js.map +2 -2
- package/dist/modules/design_system/gallery/entries/feedback.js +1058 -132
- package/dist/modules/design_system/gallery/entries/feedback.js.map +2 -2
- package/dist/modules/design_system/gallery/entries/filters.js +231 -103
- package/dist/modules/design_system/gallery/entries/filters.js.map +2 -2
- package/dist/modules/design_system/gallery/entries/foundations.js +368 -57
- package/dist/modules/design_system/gallery/entries/foundations.js.map +2 -2
- package/dist/modules/design_system/gallery/entries/icons.js +29 -91
- package/dist/modules/design_system/gallery/entries/icons.js.map +2 -2
- package/dist/modules/design_system/gallery/entries/inputs-snippets.js +154 -0
- package/dist/modules/design_system/gallery/entries/inputs-snippets.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/inputs.js +2043 -306
- package/dist/modules/design_system/gallery/entries/inputs.js.map +2 -2
- package/dist/modules/design_system/gallery/entries/library.js +20 -0
- package/dist/modules/design_system/gallery/entries/library.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/messages.js +121 -103
- package/dist/modules/design_system/gallery/entries/messages.js.map +2 -2
- package/dist/modules/design_system/gallery/entries/navigation.js +676 -103
- package/dist/modules/design_system/gallery/entries/navigation.js.map +2 -2
- package/dist/modules/design_system/gallery/entries/notifications.js +131 -78
- package/dist/modules/design_system/gallery/entries/notifications.js.map +2 -2
- package/dist/modules/design_system/gallery/entries/overlays.js +611 -203
- package/dist/modules/design_system/gallery/entries/overlays.js.map +2 -2
- package/dist/modules/design_system/gallery/entries/scaffolding.js +1175 -120
- package/dist/modules/design_system/gallery/entries/scaffolding.js.map +2 -2
- package/dist/modules/design_system/gallery/entries/schedule.js +79 -54
- package/dist/modules/design_system/gallery/entries/schedule.js.map +2 -2
- package/dist/modules/design_system/gallery/presentation.js +108 -0
- package/dist/modules/design_system/gallery/presentation.js.map +7 -0
- package/dist/modules/design_system/gallery/registry.js +8 -5
- package/dist/modules/design_system/gallery/registry.js.map +2 -2
- package/dist/modules/design_system/gallery/source-library.generated.js +5 -0
- package/dist/modules/design_system/gallery/source-library.generated.js.map +7 -0
- package/dist/modules/design_system/i18n/de.json +7167 -0
- package/dist/modules/design_system/i18n/en.json +7167 -0
- package/dist/modules/design_system/i18n/es.json +7167 -0
- package/dist/modules/design_system/i18n/ko.json +7167 -0
- package/dist/modules/design_system/i18n/pl.json +7167 -0
- package/dist/modules/push_notifications/lib/queue.js +1 -31
- package/dist/modules/push_notifications/lib/queue.js.map +2 -2
- package/jest.config.cjs +1 -0
- package/jest.mocks/image.js +1 -0
- package/package.json +8 -9
- package/src/modules/catalog/backend/catalog/products/[id]/page.tsx +50 -0
- package/src/modules/customers/backend/customers/companies/[id]/page.tsx +2 -1
- package/src/modules/customers/backend/customers/companies-v2/[id]/page.tsx +2 -1
- package/src/modules/customers/backend/customers/people/[id]/page.tsx +2 -2
- package/src/modules/customers/backend/customers/people-v2/[id]/page.tsx +2 -1
- package/src/modules/customers/lib/detailHelpers.ts +4 -0
- package/src/modules/customers/lib/todoCompatibility.ts +10 -0
- package/src/modules/design_system/backend/design-system/page.meta.ts +3 -2
- package/src/modules/design_system/gallery/assets/README.md +30 -0
- package/src/modules/design_system/gallery/assets/ai-prompt-image.png +0 -0
- package/src/modules/design_system/gallery/assets/avatar-illustration.png +0 -0
- package/src/modules/design_system/gallery/assets/avatar-memoji.png +0 -0
- package/src/modules/design_system/gallery/assets/avatar-photo.png +0 -0
- package/src/modules/design_system/gallery/assets/blog-photo-0.png +0 -0
- package/src/modules/design_system/gallery/assets/contact-support-portrait.png +0 -0
- package/src/modules/design_system/gallery/assets/crypto-ada.png +0 -0
- package/src/modules/design_system/gallery/assets/crypto-avax.png +0 -0
- package/src/modules/design_system/gallery/assets/crypto-bnb.png +0 -0
- package/src/modules/design_system/gallery/assets/crypto-brand.png +0 -0
- package/src/modules/design_system/gallery/assets/crypto-btc.png +0 -0
- package/src/modules/design_system/gallery/assets/crypto-clock.svg +3 -0
- package/src/modules/design_system/gallery/assets/crypto-crown.svg +3 -0
- package/src/modules/design_system/gallery/assets/crypto-dot.png +0 -0
- package/src/modules/design_system/gallery/assets/crypto-eth.png +0 -0
- package/src/modules/design_system/gallery/assets/crypto-hbar.png +0 -0
- package/src/modules/design_system/gallery/assets/crypto-history.svg +3 -0
- package/src/modules/design_system/gallery/assets/crypto-link.svg +3 -0
- package/src/modules/design_system/gallery/assets/crypto-menu.svg +3 -0
- package/src/modules/design_system/gallery/assets/crypto-metamask.png +0 -0
- package/src/modules/design_system/gallery/assets/crypto-more.svg +3 -0
- package/src/modules/design_system/gallery/assets/crypto-phantom.png +0 -0
- package/src/modules/design_system/gallery/assets/crypto-rewards.svg +3 -0
- package/src/modules/design_system/gallery/assets/crypto-sad-face.png +0 -0
- package/src/modules/design_system/gallery/assets/crypto-sol.png +0 -0
- package/src/modules/design_system/gallery/assets/crypto-star.svg +3 -0
- package/src/modules/design_system/gallery/assets/crypto-status-cancelled.svg +3 -0
- package/src/modules/design_system/gallery/assets/crypto-status-pending.svg +3 -0
- package/src/modules/design_system/gallery/assets/crypto-status-success.svg +3 -0
- package/src/modules/design_system/gallery/assets/crypto-status-warning.svg +3 -0
- package/src/modules/design_system/gallery/assets/crypto-trx.png +0 -0
- package/src/modules/design_system/gallery/assets/crypto-usdc.png +0 -0
- package/src/modules/design_system/gallery/assets/crypto-usdt.png +0 -0
- package/src/modules/design_system/gallery/assets/crypto-walletconnect.png +0 -0
- package/src/modules/design_system/gallery/assets/crypto-wei.png +0 -0
- package/src/modules/design_system/gallery/assets/crypto-xrp.png +0 -0
- package/src/modules/design_system/gallery/assets/cryptocurrency-artwork.ts +63 -0
- package/src/modules/design_system/gallery/assets/github-logo.png +0 -0
- package/src/modules/design_system/gallery/assets/images.d.ts +9 -0
- package/src/modules/design_system/gallery/assets/input-european-union.svg +18 -0
- package/src/modules/design_system/gallery/assets/landing-teams-01.png +0 -0
- package/src/modules/design_system/gallery/assets/marketing-artwork.ts +79 -0
- package/src/modules/design_system/gallery/assets/menu-apex.svg +18 -0
- package/src/modules/design_system/gallery/assets/menu-mastercard.svg +7 -0
- package/src/modules/design_system/gallery/assets/menu-spotify.svg +4 -0
- package/src/modules/design_system/gallery/assets/menu-united-states.svg +16 -0
- package/src/modules/design_system/gallery/assets/select-google.png +0 -0
- package/src/modules/design_system/gallery/assets/select-poland.png +0 -0
- package/src/modules/design_system/gallery/assets/selection-artwork.ts +1 -0
- package/src/modules/design_system/gallery/assets/social-apple-filled.svg +4 -0
- package/src/modules/design_system/gallery/assets/social-apple-stroke.svg +4 -0
- package/src/modules/design_system/gallery/assets/social-dropbox-filled.svg +5 -0
- package/src/modules/design_system/gallery/assets/social-dropbox-stroke.svg +5 -0
- package/src/modules/design_system/gallery/assets/social-facebook-filled.svg +4 -0
- package/src/modules/design_system/gallery/assets/social-facebook-stroke.svg +10 -0
- package/src/modules/design_system/gallery/assets/social-github-filled.svg +4 -0
- package/src/modules/design_system/gallery/assets/social-github-stroke.svg +4 -0
- package/src/modules/design_system/gallery/assets/social-google-filled.svg +7 -0
- package/src/modules/design_system/gallery/assets/social-google-stroke.svg +7 -0
- package/src/modules/design_system/gallery/assets/social-linkedin-filled.svg +4 -0
- package/src/modules/design_system/gallery/assets/social-linkedin-stroke.svg +4 -0
- package/src/modules/design_system/gallery/assets/social-x-filled.svg +4 -0
- package/src/modules/design_system/gallery/assets/social-x-stroke.svg +4 -0
- package/src/modules/design_system/gallery/assets/style-studio-product.png +0 -0
- package/src/modules/design_system/gallery/assets/upload-images.ts +5 -0
- package/src/modules/design_system/gallery/components/BrandStylePreview.tsx +94 -0
- package/src/modules/design_system/gallery/components/CodeSnippet.tsx +34 -34
- package/src/modules/design_system/gallery/components/ColorPlayground.tsx +324 -0
- package/src/modules/design_system/gallery/components/ColorStudioCompositions.tsx +96 -0
- package/src/modules/design_system/gallery/components/ColorStudioSpecimen.tsx +164 -0
- package/src/modules/design_system/gallery/components/CompactEntrySpecimen.tsx +65 -0
- package/src/modules/design_system/gallery/components/ComponentIndex.tsx +47 -0
- package/src/modules/design_system/gallery/components/DesignSystemHome.tsx +66 -0
- package/src/modules/design_system/gallery/components/EntryCard.tsx +52 -37
- package/src/modules/design_system/gallery/components/EntryHeader.tsx +80 -0
- package/src/modules/design_system/gallery/components/FamilyEntryPreview.tsx +22 -0
- package/src/modules/design_system/gallery/components/FoundationGuide.tsx +43 -0
- package/src/modules/design_system/gallery/components/GalleryLink.tsx +19 -0
- package/src/modules/design_system/gallery/components/GalleryNavigation.tsx +86 -0
- package/src/modules/design_system/gallery/components/GalleryShell.tsx +149 -63
- package/src/modules/design_system/gallery/components/IconsBrowser.tsx +40 -0
- package/src/modules/design_system/gallery/components/SourceLibrary.tsx +90 -0
- package/src/modules/design_system/gallery/components/StyleAgents.tsx +115 -0
- package/src/modules/design_system/gallery/components/ThemeTokenReference.tsx +76 -0
- package/src/modules/design_system/gallery/components/VariantPreview.tsx +2 -2
- package/src/modules/design_system/gallery/components/colorPreview.ts +63 -0
- package/src/modules/design_system/gallery/components/colorStudio.ts +260 -0
- package/src/modules/design_system/gallery/components/styleAgentBranding.ts +42 -0
- package/src/modules/design_system/gallery/components/themeTokens.ts +26 -0
- package/src/modules/design_system/gallery/demos/ai-product-code.generated.ts +3 -0
- package/src/modules/design_system/gallery/demos/ai-product.tsx +188 -0
- package/src/modules/design_system/gallery/demos/buttons.tsx +331 -0
- package/src/modules/design_system/gallery/demos/cryptocurrency.tsx +1943 -0
- package/src/modules/design_system/gallery/demos/feeds-code.generated.ts +4 -0
- package/src/modules/design_system/gallery/demos/feeds.tsx +82 -0
- package/src/modules/design_system/gallery/demos/file-upload.tsx +61 -0
- package/src/modules/design_system/gallery/demos/foundation-reference-data.ts +4872 -0
- package/src/modules/design_system/gallery/demos/foundation-references.tsx +140 -0
- package/src/modules/design_system/gallery/demos/key-components-code.generated.ts +3 -0
- package/src/modules/design_system/gallery/demos/key-components.tsx +106 -0
- package/src/modules/design_system/gallery/demos/marketing-controls-code.generated.ts +3 -0
- package/src/modules/design_system/gallery/demos/marketing-controls.tsx +92 -0
- package/src/modules/design_system/gallery/demos/marketing-widgets-code.generated.ts +3 -0
- package/src/modules/design_system/gallery/demos/marketing-widgets.tsx +189 -0
- package/src/modules/design_system/gallery/demos/menu-fixtures.tsx +48 -0
- package/src/modules/design_system/gallery/demos/menu-sources.ts +5 -0
- package/src/modules/design_system/gallery/demos/menus.tsx +122 -0
- package/src/modules/design_system/gallery/demos/rating-reviews.tsx +44 -0
- package/src/modules/design_system/gallery/demos/registered-icons.tsx +117 -0
- package/src/modules/design_system/gallery/demos/selection-cards.tsx +97 -0
- package/src/modules/design_system/gallery/demos/shell-code.generated.ts +3 -0
- package/src/modules/design_system/gallery/demos/shell.tsx +183 -0
- package/src/modules/design_system/gallery/demos/source-artwork.tsx +88 -0
- package/src/modules/design_system/gallery/demos/source-filters-code.generated.ts +3 -0
- package/src/modules/design_system/gallery/demos/source-filters.tsx +119 -0
- package/src/modules/design_system/gallery/demos/source-icons.tsx +62 -0
- package/src/modules/design_system/gallery/demos/table-cells-code.generated.ts +3 -0
- package/src/modules/design_system/gallery/demos/table-cells.tsx +144 -0
- package/src/modules/design_system/gallery/demos/text-inputs.tsx +90 -0
- package/src/modules/design_system/gallery/demos/time-picker-code.generated.ts +5 -0
- package/src/modules/design_system/gallery/demos/time-picker.tsx +31 -0
- package/src/modules/design_system/gallery/entries/banners.tsx +143 -94
- package/src/modules/design_system/gallery/entries/buttons-snippets.ts +432 -0
- package/src/modules/design_system/gallery/entries/buttons.tsx +125 -196
- package/src/modules/design_system/gallery/entries/charts.tsx +545 -233
- package/src/modules/design_system/gallery/entries/dates.tsx +171 -3
- package/src/modules/design_system/gallery/entries/detail.tsx +142 -115
- package/src/modules/design_system/gallery/entries/display.tsx +1691 -319
- package/src/modules/design_system/gallery/entries/feedback.tsx +1082 -169
- package/src/modules/design_system/gallery/entries/filters.tsx +238 -143
- package/src/modules/design_system/gallery/entries/foundations.tsx +404 -61
- package/src/modules/design_system/gallery/entries/icons.tsx +31 -117
- package/src/modules/design_system/gallery/entries/inputs-snippets.ts +156 -0
- package/src/modules/design_system/gallery/entries/inputs.tsx +2146 -503
- package/src/modules/design_system/gallery/entries/library.tsx +16 -0
- package/src/modules/design_system/gallery/entries/messages.tsx +114 -106
- package/src/modules/design_system/gallery/entries/navigation.tsx +782 -192
- package/src/modules/design_system/gallery/entries/notifications.tsx +145 -120
- package/src/modules/design_system/gallery/entries/overlays.tsx +696 -280
- package/src/modules/design_system/gallery/entries/scaffolding.tsx +1192 -158
- package/src/modules/design_system/gallery/entries/schedule.tsx +72 -58
- package/src/modules/design_system/gallery/presentation.ts +106 -0
- package/src/modules/design_system/gallery/registry.ts +8 -6
- package/src/modules/design_system/gallery/source-library.generated.ts +2 -0
- package/src/modules/design_system/i18n/de.json +7112 -3
- package/src/modules/design_system/i18n/en.json +7112 -3
- package/src/modules/design_system/i18n/es.json +7112 -3
- package/src/modules/design_system/i18n/ko.json +7112 -3
- package/src/modules/design_system/i18n/pl.json +7112 -3
- package/src/modules/push_notifications/AGENTS.md +9 -5
- package/src/modules/push_notifications/lib/queue.ts +15 -44
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
type Rgb = [number, number, number]
|
|
2
|
+
|
|
3
|
+
export function normalizeHex(input: string): string | null {
|
|
4
|
+
const value = input.trim().replace(/^#/, '')
|
|
5
|
+
if (/^[\da-f]{3}$/i.test(value)) {
|
|
6
|
+
return `#${[...value].map((digit) => digit.repeat(2)).join('').toUpperCase()}`
|
|
7
|
+
}
|
|
8
|
+
return /^[\da-f]{6}$/i.test(value) ? `#${value.toUpperCase()}` : null
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function channels(hex: string): Rgb {
|
|
12
|
+
return [
|
|
13
|
+
Number.parseInt(hex.slice(1, 3), 16),
|
|
14
|
+
Number.parseInt(hex.slice(3, 5), 16),
|
|
15
|
+
Number.parseInt(hex.slice(5, 7), 16),
|
|
16
|
+
]
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function luminance(hex: string): number {
|
|
20
|
+
const linear = channels(hex).map((channel) => {
|
|
21
|
+
const normalized = channel / 255
|
|
22
|
+
return normalized <= 0.04045 ? normalized / 12.92 : ((normalized + 0.055) / 1.055) ** 2.4
|
|
23
|
+
})
|
|
24
|
+
return linear[0] * 0.2126 + linear[1] * 0.7152 + linear[2] * 0.0722
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function contrastRatio(first: string, second: string): number {
|
|
28
|
+
const normalizedFirst = normalizeHex(first)
|
|
29
|
+
const normalizedSecond = normalizeHex(second)
|
|
30
|
+
if (!normalizedFirst || !normalizedSecond) return 1
|
|
31
|
+
const firstLuminance = luminance(normalizedFirst)
|
|
32
|
+
const secondLuminance = luminance(normalizedSecond)
|
|
33
|
+
return (Math.max(firstLuminance, secondLuminance) + 0.05) / (Math.min(firstLuminance, secondLuminance) + 0.05)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function mix(color: string, target: string, amount: number): string {
|
|
37
|
+
const targetChannels = channels(target)
|
|
38
|
+
return `#${channels(color).map((channel, index) => (
|
|
39
|
+
Math.round(channel + (targetChannels[index] - channel) * amount).toString(16).padStart(2, '0')
|
|
40
|
+
)).join('').toUpperCase()}`
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function readableForeground(background: string): string {
|
|
44
|
+
return contrastRatio(background, '#000000') >= contrastRatio(background, '#FFFFFF') ? '#000000' : '#FFFFFF'
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function readableAccent(color: string, surface: string): string {
|
|
48
|
+
const target = readableForeground(surface)
|
|
49
|
+
for (let step = 0; step <= 20; step += 1) {
|
|
50
|
+
const candidate = mix(color, target, step / 20)
|
|
51
|
+
if (contrastRatio(candidate, surface) >= 4.5) return candidate
|
|
52
|
+
}
|
|
53
|
+
return target
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function createAccentPalette(hex: string, surface: string) {
|
|
57
|
+
const color = normalizeHex(hex) ?? '#000000'
|
|
58
|
+
const normalizedSurface = normalizeHex(surface) ?? '#FFFFFF'
|
|
59
|
+
const foreground = readableForeground(color)
|
|
60
|
+
const hover = mix(color, foreground === '#FFFFFF' ? '#000000' : '#FFFFFF', 0.08)
|
|
61
|
+
const soft = mix(normalizedSurface, color, 0.1)
|
|
62
|
+
return { color, foreground, hover, soft, softForeground: readableAccent(color, soft) }
|
|
63
|
+
}
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import { contrastRatio, normalizeHex } from './colorPreview'
|
|
2
|
+
|
|
3
|
+
export type Shade = 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | 950
|
|
4
|
+
export type NeutralTone = 'neutral' | 'warm' | 'cool'
|
|
5
|
+
export type Harmony = 'analogous' | 'triadic' | 'split'
|
|
6
|
+
export type ColorStop = { step: Shade; hex: string }
|
|
7
|
+
export type StudioTheme = {
|
|
8
|
+
tokens: Record<string, string>
|
|
9
|
+
actionStep: Shade
|
|
10
|
+
pairs: Array<{
|
|
11
|
+
id: 'action' | 'hover' | 'body' | 'muted' | 'selection' | 'focus'
|
|
12
|
+
foreground: string
|
|
13
|
+
background: string
|
|
14
|
+
ratio: number
|
|
15
|
+
minimum: number
|
|
16
|
+
}>
|
|
17
|
+
}
|
|
18
|
+
export type ColorStudio = {
|
|
19
|
+
seeds: { primary: string; secondary: string; tertiary: string }
|
|
20
|
+
scale: ColorStop[]
|
|
21
|
+
secondaryScale: ColorStop[]
|
|
22
|
+
tertiaryScale: ColorStop[]
|
|
23
|
+
neutralScale: ColorStop[]
|
|
24
|
+
light: StudioTheme
|
|
25
|
+
dark: StudioTheme
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
type Rgb = [number, number, number]
|
|
29
|
+
type Oklch = { lightness: number; chroma: number; hue: number }
|
|
30
|
+
const STEPS: Shade[] = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950]
|
|
31
|
+
const LIGHTNESS = [0.98, 0.95, 0.9, 0.83, 0.74, 0.64, 0.54, 0.44, 0.35, 0.26, 0.18]
|
|
32
|
+
|
|
33
|
+
function linearChannel(channel: number): number {
|
|
34
|
+
return channel <= 0.04045 ? channel / 12.92 : ((channel + 0.055) / 1.055) ** 2.4
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function toOklch(hex: string): Oklch {
|
|
38
|
+
const [red, green, blue] = [1, 3, 5].map((offset) => linearChannel(Number.parseInt(hex.slice(offset, offset + 2), 16) / 255))
|
|
39
|
+
const long = Math.cbrt(0.4122214708 * red + 0.5363325363 * green + 0.0514459929 * blue)
|
|
40
|
+
const medium = Math.cbrt(0.2119034982 * red + 0.6806995451 * green + 0.1073969566 * blue)
|
|
41
|
+
const short = Math.cbrt(0.0883024619 * red + 0.2817188376 * green + 0.6299787005 * blue)
|
|
42
|
+
const lightness = 0.2104542553 * long + 0.793617785 * medium - 0.0040720468 * short
|
|
43
|
+
const axisA = 1.9779984951 * long - 2.428592205 * medium + 0.4505937099 * short
|
|
44
|
+
const axisB = 0.0259040371 * long + 0.7827717662 * medium - 0.808675766 * short
|
|
45
|
+
return { lightness, chroma: Math.hypot(axisA, axisB), hue: Math.atan2(axisB, axisA) }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function toLinearRgb({ lightness, chroma, hue }: Oklch): Rgb {
|
|
49
|
+
const axisA = chroma * Math.cos(hue)
|
|
50
|
+
const axisB = chroma * Math.sin(hue)
|
|
51
|
+
const long = (lightness + 0.3963377774 * axisA + 0.2158037573 * axisB) ** 3
|
|
52
|
+
const medium = (lightness - 0.1055613458 * axisA - 0.0638541728 * axisB) ** 3
|
|
53
|
+
const short = (lightness - 0.0894841775 * axisA - 1.291485548 * axisB) ** 3
|
|
54
|
+
return [
|
|
55
|
+
4.0767416621 * long - 3.3077115913 * medium + 0.2309699292 * short,
|
|
56
|
+
-1.2684380046 * long + 2.6097574011 * medium - 0.3413193965 * short,
|
|
57
|
+
-0.0041960863 * long - 0.7034186147 * medium + 1.707614701 * short,
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function inGamut(channels: Rgb): boolean {
|
|
62
|
+
return channels.every((channel) => channel >= -0.0000001 && channel <= 1.0000001)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function toHex(color: Oklch): string {
|
|
66
|
+
let channels = toLinearRgb(color)
|
|
67
|
+
if (!inGamut(channels)) {
|
|
68
|
+
let low = 0
|
|
69
|
+
let high = color.chroma
|
|
70
|
+
for (let iteration = 0; iteration < 24; iteration += 1) {
|
|
71
|
+
const chroma = (low + high) / 2
|
|
72
|
+
if (inGamut(toLinearRgb({ ...color, chroma }))) low = chroma
|
|
73
|
+
else high = chroma
|
|
74
|
+
}
|
|
75
|
+
channels = toLinearRgb({ ...color, chroma: low })
|
|
76
|
+
}
|
|
77
|
+
return `#${channels.map((channel) => {
|
|
78
|
+
const clamped = Math.max(0, Math.min(1, channel))
|
|
79
|
+
const encoded = clamped <= 0.0031308 ? clamped * 12.92 : 1.055 * clamped ** (1 / 2.4) - 0.055
|
|
80
|
+
return Math.round(encoded * 255).toString(16).padStart(2, '0')
|
|
81
|
+
}).join('').toUpperCase()}`
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function subtleSurface(surface: string, accent: string): string {
|
|
85
|
+
const base = toOklch(surface)
|
|
86
|
+
const tint = toOklch(accent)
|
|
87
|
+
const amount = 0.15
|
|
88
|
+
const axisA = base.chroma * Math.cos(base.hue) * (1 - amount) + tint.chroma * Math.cos(tint.hue) * amount
|
|
89
|
+
const axisB = base.chroma * Math.sin(base.hue) * (1 - amount) + tint.chroma * Math.sin(tint.hue) * amount
|
|
90
|
+
return toHex({
|
|
91
|
+
lightness: base.lightness * (1 - amount) + tint.lightness * amount,
|
|
92
|
+
chroma: Math.hypot(axisA, axisB),
|
|
93
|
+
hue: Math.atan2(axisB, axisA),
|
|
94
|
+
})
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function accentScale(hex: string): ColorStop[] {
|
|
98
|
+
const seed = toOklch(hex)
|
|
99
|
+
const anchor = LIGHTNESS.reduce((closest, value, index) => (
|
|
100
|
+
Math.abs(value - seed.lightness) < Math.abs(LIGHTNESS[closest] - seed.lightness) ? index : closest
|
|
101
|
+
), 0)
|
|
102
|
+
const seedEnvelope = Math.max(0.25, Math.sin(Math.PI * seed.lightness) ** 0.8)
|
|
103
|
+
return STEPS.map((step, index) => {
|
|
104
|
+
if (index === anchor) return { step, hex }
|
|
105
|
+
const lightness = LIGHTNESS[index]
|
|
106
|
+
const chroma = seed.chroma < 0.00001 ? 0 : Math.min(0.32, seed.chroma * Math.sin(Math.PI * lightness) ** 0.8 / seedEnvelope)
|
|
107
|
+
return { step, hex: toHex({ lightness, chroma, hue: seed.hue }) }
|
|
108
|
+
})
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function neutralScale(tone: NeutralTone): ColorStop[] {
|
|
112
|
+
const hue = (tone === 'warm' ? 75 : 255) * Math.PI / 180
|
|
113
|
+
return STEPS.map((step, index) => ({
|
|
114
|
+
step,
|
|
115
|
+
hex: toHex({ lightness: LIGHTNESS[index], chroma: tone === 'neutral' ? 0 : 0.014 * Math.sin(Math.PI * LIGHTNESS[index]), hue }),
|
|
116
|
+
}))
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function atShade(scale: ColorStop[], shade: Shade): string {
|
|
120
|
+
return scale.find((stop) => stop.step === shade)!.hex
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function foregroundFor(background: string): string {
|
|
124
|
+
return contrastRatio('#000000', background) >= contrastRatio('#FFFFFF', background) ? '#000000' : '#FFFFFF'
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function nearestReadable(scale: ColorStop[], target: Shade, background: string, minimum: number): string {
|
|
128
|
+
const targetIndex = STEPS.indexOf(target)
|
|
129
|
+
return scale.filter((stop) => contrastRatio(stop.hex, background) >= minimum)
|
|
130
|
+
.sort((first, second) => Math.abs(STEPS.indexOf(first.step) - targetIndex) - Math.abs(STEPS.indexOf(second.step) - targetIndex))[0]?.hex ?? foregroundFor(background)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function themeFor(scale: ColorStop[], neutrals: ColorStop[], dark: boolean, actionStep: Shade, secondary: ColorStop[], tertiary: ColorStop[]): StudioTheme {
|
|
134
|
+
const background = atShade(neutrals, dark ? 950 : 50)
|
|
135
|
+
const card = dark ? atShade(neutrals, 900) : '#FFFFFF'
|
|
136
|
+
const foreground = atShade(neutrals, dark ? 50 : 950)
|
|
137
|
+
const muted = atShade(neutrals, dark ? 800 : 100)
|
|
138
|
+
const mutedForeground = nearestReadable(neutrals, dark ? 300 : 600, muted, 4.5)
|
|
139
|
+
const primary = atShade(scale, actionStep)
|
|
140
|
+
const primaryForeground = foregroundFor(primary)
|
|
141
|
+
const actionIndex = STEPS.indexOf(actionStep)
|
|
142
|
+
const hoverIndex = Math.max(0, Math.min(STEPS.length - 1, actionIndex + (primaryForeground === '#FFFFFF' ? 1 : -1)))
|
|
143
|
+
const hoverCandidate = scale[hoverIndex].hex
|
|
144
|
+
const hover = contrastRatio(primaryForeground, hoverCandidate) >= 4.5 ? hoverCandidate : primary
|
|
145
|
+
const accent = dark ? subtleSurface(card, primary) : atShade(scale, 100)
|
|
146
|
+
const accentForeground = nearestReadable(scale, dark ? 200 : 800, accent, 4.5)
|
|
147
|
+
const focus = nearestReadable(scale, actionStep, background, 3)
|
|
148
|
+
const input = atShade(neutrals, dark ? 600 : 300)
|
|
149
|
+
const selection = scale.filter((stop) => contrastRatio(stop.hex, input) >= 3 && contrastRatio(stop.hex, background) >= 3)
|
|
150
|
+
.sort((first, second) => Math.abs(STEPS.indexOf(first.step) - actionIndex) - Math.abs(STEPS.indexOf(second.step) - actionIndex))[0]?.hex ?? foregroundFor(background)
|
|
151
|
+
const selectionForeground = foregroundFor(selection)
|
|
152
|
+
const categoryTokens = (name: string, colors: ColorStop[]) => {
|
|
153
|
+
const strong = atShade(colors, dark ? 400 : 600)
|
|
154
|
+
const soft = dark ? subtleSurface(card, strong) : atShade(colors, 100)
|
|
155
|
+
return {
|
|
156
|
+
[`--studio-${name}`]: strong,
|
|
157
|
+
[`--studio-${name}-foreground`]: foregroundFor(strong),
|
|
158
|
+
[`--studio-${name}-soft`]: soft,
|
|
159
|
+
[`--studio-${name}-soft-foreground`]: nearestReadable(colors, dark ? 200 : 800, soft, 4.5),
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
const tokens: Record<string, string> = {
|
|
163
|
+
...categoryTokens('secondary', secondary),
|
|
164
|
+
...categoryTokens('tertiary', tertiary),
|
|
165
|
+
'--background': background,
|
|
166
|
+
'--foreground': foreground,
|
|
167
|
+
'--card': card,
|
|
168
|
+
'--card-foreground': foreground,
|
|
169
|
+
'--popover': card,
|
|
170
|
+
'--popover-foreground': foreground,
|
|
171
|
+
'--muted': muted,
|
|
172
|
+
'--muted-foreground': mutedForeground,
|
|
173
|
+
'--secondary': muted,
|
|
174
|
+
'--secondary-foreground': foreground,
|
|
175
|
+
'--border': atShade(neutrals, dark ? 700 : 200),
|
|
176
|
+
'--input': input,
|
|
177
|
+
'--bg-disabled': muted,
|
|
178
|
+
'--text-disabled': atShade(neutrals, dark ? 500 : 400),
|
|
179
|
+
'--border-disabled': atShade(neutrals, dark ? 700 : 200),
|
|
180
|
+
'--primary': primary,
|
|
181
|
+
'--primary-foreground': primaryForeground,
|
|
182
|
+
'--primary-hover': hover,
|
|
183
|
+
'--accent': accent,
|
|
184
|
+
'--accent-foreground': accentForeground,
|
|
185
|
+
'--accent-indigo': selection,
|
|
186
|
+
'--accent-indigo-foreground': selectionForeground,
|
|
187
|
+
'--ring': focus,
|
|
188
|
+
'--focus-ring-inner': background,
|
|
189
|
+
'--focus-ring-outer': focus,
|
|
190
|
+
}
|
|
191
|
+
const pair = (id: StudioTheme['pairs'][number]['id'], text: string, surface: string, minimum = 4.5) => ({
|
|
192
|
+
id, foreground: text, background: surface, ratio: contrastRatio(text, surface), minimum,
|
|
193
|
+
})
|
|
194
|
+
return {
|
|
195
|
+
tokens,
|
|
196
|
+
actionStep,
|
|
197
|
+
pairs: [
|
|
198
|
+
pair('action', primaryForeground, primary),
|
|
199
|
+
pair('hover', primaryForeground, hover),
|
|
200
|
+
pair('body', foreground, background),
|
|
201
|
+
pair('muted', mutedForeground, muted),
|
|
202
|
+
pair('selection', accentForeground, accent),
|
|
203
|
+
pair('focus', focus, background, 3),
|
|
204
|
+
],
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export function createHarmonySeeds(seed: string, harmony: Harmony): { secondary: string; tertiary: string } {
|
|
209
|
+
const color = toOklch(normalizeHex(seed) ?? '#F4700D')
|
|
210
|
+
const offset = ({ analogous: 30, triadic: 120, split: 150 }[harmony]) * Math.PI / 180
|
|
211
|
+
const lightness = Math.max(0.55, Math.min(0.72, color.lightness))
|
|
212
|
+
const chroma = Math.max(0.1, Math.min(0.18, color.chroma))
|
|
213
|
+
const hue = color.chroma < 0.00001 ? 0 : color.hue
|
|
214
|
+
return {
|
|
215
|
+
secondary: toHex({ lightness, chroma, hue: hue + offset }),
|
|
216
|
+
tertiary: toHex({ lightness, chroma, hue: hue - offset }),
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export function createColorStudio(
|
|
221
|
+
seed: string,
|
|
222
|
+
neutral: NeutralTone,
|
|
223
|
+
actionShades: { light?: Shade; dark?: Shade } = {},
|
|
224
|
+
colors: { secondary?: string; tertiary?: string; harmony?: Harmony } = {},
|
|
225
|
+
): ColorStudio {
|
|
226
|
+
const primary = normalizeHex(seed) ?? '#F4700D'
|
|
227
|
+
const derived = createHarmonySeeds(primary, colors.harmony ?? 'triadic')
|
|
228
|
+
const seeds = {
|
|
229
|
+
primary,
|
|
230
|
+
secondary: normalizeHex(colors.secondary ?? '') ?? derived.secondary,
|
|
231
|
+
tertiary: normalizeHex(colors.tertiary ?? '') ?? derived.tertiary,
|
|
232
|
+
}
|
|
233
|
+
const scale = accentScale(primary)
|
|
234
|
+
const secondary = accentScale(seeds.secondary)
|
|
235
|
+
const tertiary = accentScale(seeds.tertiary)
|
|
236
|
+
const neutrals = neutralScale(neutral)
|
|
237
|
+
return {
|
|
238
|
+
seeds,
|
|
239
|
+
scale,
|
|
240
|
+
secondaryScale: secondary,
|
|
241
|
+
tertiaryScale: tertiary,
|
|
242
|
+
neutralScale: neutrals,
|
|
243
|
+
light: themeFor(scale, neutrals, false, actionShades.light ?? 600, secondary, tertiary),
|
|
244
|
+
dark: themeFor(scale, neutrals, true, actionShades.dark ?? 400, secondary, tertiary),
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export function exportColorStudio(studio: ColorStudio, format: 'css' | 'json'): string {
|
|
249
|
+
const scales = {
|
|
250
|
+
accent: Object.fromEntries(studio.scale.map((stop) => [stop.step, stop.hex])),
|
|
251
|
+
secondary: Object.fromEntries(studio.secondaryScale.map((stop) => [stop.step, stop.hex])),
|
|
252
|
+
tertiary: Object.fromEntries(studio.tertiaryScale.map((stop) => [stop.step, stop.hex])),
|
|
253
|
+
neutral: Object.fromEntries(studio.neutralScale.map((stop) => [stop.step, stop.hex])),
|
|
254
|
+
}
|
|
255
|
+
if (format === 'json') return JSON.stringify({ seeds: studio.seeds, scales, themes: { light: studio.light.tokens, dark: studio.dark.tokens } }, null, 2)
|
|
256
|
+
const scaleTokens = Object.fromEntries(Object.entries(scales).flatMap(([name, stops]) => Object.entries(stops).map(([step, hex]) => [`--palette-${name}-${step}`, hex])))
|
|
257
|
+
return (['light', 'dark'] as const).map((theme) => (
|
|
258
|
+
`.ds-color-preview-${theme} {\n${Object.entries({ ...scaleTokens, ...studio[theme].tokens }).map(([token, value]) => ` ${token}: ${value};`).join('\n')}\n}`
|
|
259
|
+
)).join('\n\n')
|
|
260
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { BrandStyle } from '@open-mercato/ui/theme/brand-style'
|
|
2
|
+
import type { ColorStudio } from './colorStudio'
|
|
3
|
+
|
|
4
|
+
export function studioBrandStyle(studio: ColorStudio, logo: string | null): BrandStyle {
|
|
5
|
+
const theme = (mode: 'light' | 'dark') => {
|
|
6
|
+
const tokens = studio[mode].tokens
|
|
7
|
+
return {
|
|
8
|
+
'--primary': tokens['--primary'],
|
|
9
|
+
'--primary-hover': tokens['--primary-hover'],
|
|
10
|
+
'--primary-foreground': tokens['--primary-foreground'],
|
|
11
|
+
'--brand-lime': studio.secondaryScale.find(stop => stop.step === 200)!.hex,
|
|
12
|
+
'--brand-yellow': studio.scale.find(stop => stop.step === 200)!.hex,
|
|
13
|
+
'--brand-violet': studio.tertiaryScale.find(stop => stop.step === 200)!.hex,
|
|
14
|
+
'--brand-violet-foreground': '#000000',
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return { version: 1, logo, seeds: studio.seeds, actionShades: { light: studio.light.actionStep, dark: studio.dark.actionStep }, light: theme('light'), dark: theme('dark') }
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function readBrandLogo(file: File): Promise<string> {
|
|
21
|
+
if (!['image/png', 'image/jpeg', 'image/webp'].includes(file.type)) return Promise.reject(new Error('fileType'))
|
|
22
|
+
if (file.size > 1024 * 1024) return Promise.reject(new Error('fileSize'))
|
|
23
|
+
return new Promise((resolve, reject) => {
|
|
24
|
+
const reader = new FileReader()
|
|
25
|
+
reader.onerror = () => reject(new Error('fileDecode'))
|
|
26
|
+
reader.onload = () => {
|
|
27
|
+
if (typeof reader.result !== 'string') { reject(new Error('fileDecode')); return }
|
|
28
|
+
const data = reader.result
|
|
29
|
+
const image = new Image()
|
|
30
|
+
image.onerror = () => reject(new Error('fileDecode'))
|
|
31
|
+
image.onload = () => {
|
|
32
|
+
if (!image.naturalWidth || !image.naturalHeight || image.naturalWidth > 4096 || image.naturalHeight > 4096) {
|
|
33
|
+
reject(new Error('fileDimensions'))
|
|
34
|
+
return
|
|
35
|
+
}
|
|
36
|
+
resolve(data)
|
|
37
|
+
}
|
|
38
|
+
image.src = data
|
|
39
|
+
}
|
|
40
|
+
reader.readAsDataURL(file)
|
|
41
|
+
})
|
|
42
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export type ThemeTokens = { light: Record<string, string>; dark: Record<string, string> }
|
|
2
|
+
|
|
3
|
+
export function readThemeTokens(sheets: Iterable<CSSStyleSheet>): ThemeTokens {
|
|
4
|
+
const light: Record<string, string> = {}
|
|
5
|
+
const dark: Record<string, string> = {}
|
|
6
|
+
function readRules(rules: CSSRuleList) {
|
|
7
|
+
for (const rule of Array.from(rules)) {
|
|
8
|
+
if ('selectorText' in rule && 'style' in rule) {
|
|
9
|
+
const styleRule = rule as CSSStyleRule
|
|
10
|
+
const selectors = styleRule.selectorText.split(',').map(selector => selector.trim())
|
|
11
|
+
const target = selectors.includes('.dark') ? dark : selectors.includes(':root') ? light : null
|
|
12
|
+
if (target) {
|
|
13
|
+
for (const property of Array.from(styleRule.style)) {
|
|
14
|
+
if (property.startsWith('--')) target[property] = styleRule.style.getPropertyValue(property).trim()
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
if ('cssRules' in rule) readRules((rule as CSSGroupingRule).cssRules)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
for (const sheet of sheets) {
|
|
22
|
+
if (sheet.ownerNode instanceof Element && sheet.ownerNode.id === 'om-brand-style') continue
|
|
23
|
+
try { readRules(sheet.cssRules) } catch { continue }
|
|
24
|
+
}
|
|
25
|
+
return { light, dark: { ...light, ...dark } }
|
|
26
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
// Generated by yarn storybook:generate from ai-product.tsx.
|
|
2
|
+
const source = "import * as React from 'react'\nimport { ArrowLeft, BookOpen, ChevronDown, ChevronRight, Folder, Mail, Menu, MoreHorizontal, MoreVertical, PanelLeft, Plus, Search, User, X, Zap } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { SearchInput } from '@open-mercato/ui/primitives/search-input'\nimport { FieldLabel } from '@open-mercato/ui/primitives/label'\nimport { HintText } from '@open-mercato/ui/primitives/hint-text'\nimport { KeyIconGlyph } from '@open-mercato/ui/primitives/key-icon'\nimport { DigitInput } from '@open-mercato/ui/primitives/digit-input'\nimport { CounterInput } from '@open-mercato/ui/primitives/counter-input'\nimport { Select, SelectTrigger, SelectContent, SelectItem, SelectValue } from '@open-mercato/ui/primitives/select'\nimport { Popover, PopoverTrigger, PopoverContent } from '@open-mercato/ui/primitives/popover'\nimport { CheckboxField } from '@open-mercato/ui/primitives/checkbox-field'\nimport { SocialButton } from '@open-mercato/ui/primitives/social-button'\nimport { Avatar } from '@open-mercato/ui/primitives/avatar'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { Sidebar } from '@open-mercato/ui/primitives/sidebar'\nimport { PromptArea } from '@open-mercato/ui/primitives/prompt-area'\nimport { FileFormatIcon } from '@open-mercato/ui/primitives/file-format-icon'\nimport { aiProductArtwork } from '@open-mercato/ui/assets/ai-product-artwork'\nimport promptImage from '../assets/ai-prompt-image.png'\nimport avatarPhoto from '../assets/avatar-photo.png'\nimport googleIcon from '../assets/select-google.png'\n\ntype ButtonProps = React.ComponentProps<typeof Button>\n\nconst prefix = 'design_system.gallery.examples.aiProduct.'\nconst source = (asset: string | { readonly src: string }) => typeof asset === 'string' ? asset : asset.src\n\nfunction AiIconButton({ size = 32, tone = 'default', appearance = 'plain', className, children, ...props }: Omit<ButtonProps, 'size'> & { size?: 20 | 24 | 28 | 32; tone?: 'default' | 'error'; appearance?: 'plain' | 'stroke' | 'lighter' }) {\n const content = React.isValidElement<{ className?: string }>(children) && children.type !== 'img'\n ? React.cloneElement(children, { className: cn(children.props.className, size === 24 ? 'size-4.5' : 'size-5') })\n : children\n return <Button type=\"button\" variant=\"ghost\" size=\"icon\" data-slot=\"ai-icon-button\" data-size={size} data-tone={tone} data-appearance={appearance} className={cn(\n 'shrink-0 p-0',\n size === 32 ? 'size-8 rounded-md' : size === 28 ? 'size-7 rounded-md' : size === 24 ? 'size-6 rounded-sm' : 'size-5 rounded-sm',\n appearance !== 'plain' && 'rounded-ai-control',\n appearance === 'stroke' ? 'bg-background shadow-xs ring-1 ring-inset ring-border' : appearance === 'lighter' ? 'bg-muted' : 'bg-background',\n tone === 'error' ? 'text-status-error-text hover:bg-status-error-bg hover:text-status-error-text aria-pressed:bg-status-error-bg aria-pressed:text-status-error-text' : 'text-muted-foreground hover:bg-muted hover:text-foreground aria-pressed:bg-muted aria-pressed:text-foreground',\n appearance !== 'plain' && 'aria-pressed:bg-primary aria-pressed:text-primary-foreground',\n className,\n )} {...props}>{content}</Button>\n}\n\nfunction AiModelSelect({ value, onChange, compact = false, className }: { value: string; onChange: (value: string) => void; compact?: boolean; className?: string }) {\n const t = useT()\n return <Select value={value} onValueChange={onChange}><SelectTrigger aria-label={t(`${prefix}model`)} data-slot=\"ai-model-select\" className={cn('h-7 w-auto min-w-19.75 gap-1 rounded-ai-control border-0 bg-muted py-1 pl-2 pr-1 text-sm leading-5 shadow-none hover:bg-accent [&>svg]:size-5', compact && 'h-5 bg-transparent p-0', className)}><SelectValue /></SelectTrigger><SelectContent>{['GPT-4', 'GPT-3.5'].map(model => <SelectItem key={model} value={model}>{model}</SelectItem>)}</SelectContent></Select>\n}\n\ntype PromptAttachment = { id: string; name: string; image?: string; objectUrl?: boolean }\n\nexport function AiPromptExample({ compact = false, attachment = 'none' }: { compact?: boolean; attachment?: 'none' | 'file' | 'image' }) {\n const t = useT()\n const [value, setValue] = React.useState('')\n const [model, setModel] = React.useState('GPT-4')\n const [lastPrompt, setLastPrompt] = React.useState('')\n const [files, setFiles] = React.useState<PromptAttachment[]>(attachment === 'none' ? [] : [{ id: 'source', name: t(`${prefix}${attachment === 'image' ? 'imageName' : 'fileName'}`), image: attachment === 'image' ? source(promptImage) : undefined }])\n const upload = React.useRef<HTMLInputElement>(null)\n const attachmentId = React.useRef(0)\n const objectUrls = React.useRef(new Set<string>())\n React.useEffect(() => () => { for (const url of objectUrls.current) URL.revokeObjectURL(url) }, [])\n const remove = (id: string) => setFiles(current => current.filter(file => {\n if (file.id !== id) return true\n if (file.objectUrl && file.image) { URL.revokeObjectURL(file.image); objectUrls.current.delete(file.image) }\n return false\n }))\n const submit = (message: string) => { setLastPrompt(`${model}: ${message}`); setValue('') }\n return <div className={cn('grid max-w-full gap-4', compact ? 'w-93.5' : 'w-175')}>\n <PromptArea compact={compact} value={value} onValueChange={setValue} onSubmit={submit} inputLabel={t(`${prefix}prompt`)} submitLabel={t(`${prefix}saveDraft`)} placeholder={t(`${prefix}placeholder`)} information={<><Zap aria-hidden=\"true\" className=\"size-4 shrink-0\" /><span>{t(`${prefix}premium`)}</span><span aria-hidden=\"true\">·</span><Popover><PopoverTrigger asChild><Button type=\"button\" variant=\"ghost\" className=\"h-4 rounded-sm p-0 text-xs font-medium leading-4\">{t(`${prefix}upgrade`)}</Button></PopoverTrigger><PopoverContent className=\"text-sm\">{t(`${prefix}localOnly`)}</PopoverContent></Popover></>}\n toolbar={<><AiIconButton size={28} appearance=\"lighter\" aria-label={t(`${prefix}addFile`)} onClick={() => upload.current?.click()}><Plus aria-hidden=\"true\" /></AiIconButton><AiModelSelect value={model} onChange={setModel} /></>}\n attachments={files.length ? files.map(file => file.image ? <div key={file.id} className=\"relative size-24 shrink-0\"><img src={file.image} alt={file.name} className=\"size-24 rounded-xl object-cover\" /><AiIconButton size={20} aria-label={`${t(`${prefix}remove`)} ${file.name}`} className=\"absolute right-1.5 top-1.5 rounded-full bg-primary text-primary-foreground hover:bg-primary-hover hover:text-primary-foreground\" onClick={() => remove(file.id)}><X aria-hidden=\"true\" /></AiIconButton></div> : <div key={file.id} className=\"flex h-15 max-w-full items-center gap-3 rounded-xl bg-muted py-2.5 pl-3 pr-4\"><FileFormatIcon format={file.name.includes('.') ? file.name.split('.').pop()?.toUpperCase().slice(0, 5) ?? 'FILE' : 'FILE'} /><div className=\"grid min-w-0 gap-1\"><span className=\"truncate text-sm font-medium\">{file.name}</span><span className=\"text-xs text-muted-foreground\">{t(`${prefix}localFile`)}</span></div><AiIconButton size={20} aria-label={`${t(`${prefix}remove`)} ${file.name}`} onClick={() => remove(file.id)}><X aria-hidden=\"true\" /></AiIconButton></div>) : undefined} />\n <input ref={upload} type=\"file\" className=\"sr-only\" tabIndex={-1} aria-label={t(`${prefix}attachmentInput`)} multiple onChange={event => { const selected = Array.from(event.target.files ?? []); setFiles(current => [...current, ...selected.map(file => { const image = file.type.startsWith('image/') ? URL.createObjectURL(file) : undefined; if (image) objectUrls.current.add(image); return { id: `attachment-${attachmentId.current++}`, name: file.name, image, objectUrl: Boolean(image) } })]); event.target.value = '' }} />\n <p className=\"text-xs leading-4 text-muted-foreground\">{t(`${prefix}localOnly`)}</p>\n {lastPrompt ? <output className=\"rounded-md border border-border p-3 text-sm\">{t(`${prefix}saved`)} {lastPrompt}</output> : null}\n </div>\n}\n\nexport function AiIconButtonExamples({ chat = false }: { chat?: boolean }) {\n const t = useT()\n const [active, setActive] = React.useState<string[]>([])\n const toggle = (key: string) => setActive(items => items.includes(key) ? items.filter(item => item !== key) : [...items, key])\n return <div className=\"grid gap-5\">{chat ? <div className=\"flex gap-5\">{(['stroke', 'lighter'] as const).map(appearance => <AiIconButton key={appearance} size={28} appearance={appearance} aria-label={`${t(`${prefix}toggleControl`)} ${appearance}`} aria-pressed={active.includes(appearance)} onClick={() => toggle(appearance)}><Plus aria-hidden=\"true\" /></AiIconButton>)}</div> : (['default', 'error'] as const).map(tone => <div key={tone} className=\"flex items-center gap-5\">{([32, 28, 24, 20] as const).map(size => <AiIconButton key={size} size={size} tone={tone} aria-label={`${t(`${prefix}${tone}`)} ${size}`} aria-pressed={active.includes(`${tone}-${size}`)} onClick={() => toggle(`${tone}-${size}`)}><ChevronDown aria-hidden=\"true\" /></AiIconButton>)}</div>)}</div>\n}\n\nfunction AiNavItem({ children, icon = true, leading, mobile = false, active = false, className, ...props }: ButtonProps & { icon?: boolean; leading?: React.ReactNode; mobile?: boolean; active?: boolean }) {\n return <Button type=\"button\" variant=\"ghost\" size=\"sm\" data-slot=\"ai-nav-item\" data-mobile={mobile} data-leading={icon} aria-current={active ? 'page' : undefined} className={cn('group h-8 w-61 max-w-full justify-start gap-2 rounded-md bg-background py-1.5 pl-1.5 pr-2 text-sm font-medium leading-5 text-muted-foreground hover:bg-muted hover:text-foreground', active && 'bg-muted text-foreground', !icon && 'hover:pl-2.5', !icon && active && 'pl-2.5', className)} {...props}>\n {icon ? leading ?? <Folder aria-hidden=\"true\" className=\"size-5\" /> : null}<span className=\"min-w-0 flex-1 truncate text-left\">{children}</span>{icon ? <ChevronRight aria-hidden=\"true\" className={cn('size-4.5 shrink-0', !mobile && !active && 'hidden group-hover:block group-focus-visible:block')} /> : <MoreVertical aria-hidden=\"true\" className={cn('size-4.5 shrink-0', !mobile && (active ? 'hidden' : 'hidden group-hover:block group-focus-visible:block'))} />}\n </Button>\n}\n\nexport function AiNavigationExamples() {\n const t = useT()\n const [active, setActive] = React.useState<string[]>([])\n return <div className=\"grid gap-4\">{[false, true].flatMap(mobile => [true, false].map(icon => { const key = `${mobile}-${icon}`; return <AiNavItem key={key} icon={icon} mobile={mobile} active={active.includes(key)} onClick={() => setActive(values => values.includes(key) ? values.filter(value => value !== key) : [...values, key])}>{t(`${prefix}projects`)}</AiNavItem> }))}</div>\n}\n\nexport function AiSearchExamples() {\n const t = useT()\n const [query, setQuery] = React.useState('')\n return <div className=\"grid w-64 max-w-full gap-4\">{(['default', 'sm'] as const).map(size => <SearchInput key={size} size={size} value={query} onChange={setQuery} clearable={false} aria-label={`${t(`${prefix}search`)} ${size}`} placeholder={t(`${prefix}searchPlaceholder`)} className={cn('rounded-lg border-0 px-2 shadow-none focus-within:bg-background [&>svg]:size-5', size === 'sm' ? 'bg-background hover:bg-muted' : 'bg-muted')} />)}<output className=\"text-xs text-muted-foreground\">{t(`${prefix}query`)} {query}</output></div>\n}\n\nexport function AiAuthExamples({ kind = 'icons' }: { kind?: 'icons' | 'social' | 'text' | 'digits' }) {\n const t = useT()\n const [value, setValue] = React.useState('')\n const [clicked, setClicked] = React.useState(false)\n const id = React.useId()\n if (kind === 'icons') return <div className=\"grid w-full max-w-lg grid-cols-2 gap-6 sm:grid-cols-4\">{(['signIn', 'signUp', 'reset', 'verification', 'signInGray', 'signUpGray', 'resetGray', 'verificationGray'] as const).map(key => <div key={key} className=\"grid justify-items-center gap-2\"><span className=\"flex size-14 items-center justify-center rounded-full border border-status-neutral-bg bg-background\"><img src={aiProductArtwork[key]} alt=\"\" className=\"size-8\" /></span><span className=\"text-xs text-muted-foreground\">{t(`${prefix}${key.replace('Gray', '')}`)}{key.endsWith('Gray') ? ` · ${t(`${prefix}gray`)}` : ''}</span></div>)}</div>\n if (kind === 'social') return <div className=\"grid gap-3\"><SocialButton type=\"button\" brand=\"google\" appearance=\"stroke\" className=\"h-auto min-h-10 w-fit max-w-full whitespace-normal py-2 text-left\" onClick={() => setClicked(true)}><img src={source(googleIcon)} alt=\"\" className=\"size-5 shrink-0\" /><span className=\"min-w-0\">{t(`${prefix}google`)}</span></SocialButton>{clicked ? <output className=\"max-w-sm text-xs text-muted-foreground\">{t(`${prefix}localOnly`)}</output> : null}</div>\n if (kind === 'digits') return <div className=\"grid gap-4\">{[false, true].map(disabled => <DigitInput key={String(disabled)} length={4} value={disabled ? '1234' : value} onChange={setValue} disabled={disabled} aria-label={`${t(`${prefix}verification`)} ${disabled ? t(`${prefix}disabled`) : t(`${prefix}editable`)}`} cellClassName=\"size-12 sm:w-12 rounded-ai-input px-2 py-2.5 text-lg leading-6\" />)}<output className=\"text-xs text-muted-foreground\">{value}</output></div>\n return <div className=\"grid w-83 max-w-full gap-5\">{[false, true].map(disabled => <div key={String(disabled)} data-slot=\"ai-text-input\" className=\"grid gap-1\"><FieldLabel htmlFor={`${id}-${disabled}`} disabled={disabled} required>{t(`${prefix}email`)}</FieldLabel><Input id={`${id}-${disabled}`} type=\"email\" value={disabled ? 'james@example.com' : value} onChange={event => setValue(event.target.value)} disabled={disabled} aria-required aria-describedby={`${id}-${disabled}-hint`} placeholder=\"hello@alignui.com\" leading={<Mail aria-hidden=\"true\" className=\"size-5 text-muted-foreground\" />} size=\"lg\" className=\"rounded-ai-input\" /><HintText id={`${id}-${disabled}-hint`} state={disabled ? 'disabled' : 'default'} leading={<KeyIconGlyph name=\"information\" />} className=\"pt-1\">{t(`${prefix}hint`)}</HintText></div>)}</div>\n}\n\nexport function AiSettingsExamples() {\n const t = useT()\n const [tab, setTab] = React.useState('people')\n const [size, setSize] = React.useState('medium')\n const [count, setCount] = React.useState<number | null>(30)\n const [model, setModel] = React.useState('GPT-4')\n return <div className=\"grid gap-6\"><div className=\"grid gap-2\">{(['people', 'profile'] as const).map(value => <Button key={value} type=\"button\" variant=\"ghost\" data-slot=\"ai-settings-item\" aria-pressed={tab === value} className=\"h-auto min-h-10 w-56 max-w-full justify-start gap-2 rounded-lg bg-background px-3 py-2 text-sm text-muted-foreground hover:bg-muted aria-pressed:bg-muted aria-pressed:text-foreground\" onClick={() => setTab(value)}>{value === 'profile' ? <span aria-hidden=\"true\"><Avatar size={20} label=\"James Brown\" src={source(avatarPhoto)} /></span> : <User aria-hidden=\"true\" className=\"size-5\" />}<span className=\"min-w-0 flex-1 whitespace-normal text-left\">{value === 'profile' ? 'James Brown' : t(`${prefix}people`)}</span>{tab === value ? <ChevronRight aria-hidden=\"true\" className=\"size-4.5\" /> : null}</Button>)}</div><output className=\"text-sm text-muted-foreground\">{t(`${prefix}${tab}`)}</output>\n <div className=\"flex flex-wrap items-center gap-5\"><Select value={size} onValueChange={setSize}><SelectTrigger data-slot=\"ai-size-select\" aria-label={t(`${prefix}responseLength`)} className=\"w-auto min-w-28 gap-2 rounded-lg [&>svg]:size-5\"><SelectValue /></SelectTrigger><SelectContent>{['small', 'medium', 'large'].map(value => <SelectItem key={value} value={value}>{t(`${prefix}${value}`)}</SelectItem>)}</SelectContent></Select><CounterInput data-slot=\"ai-counter\" value={count} min={0} max={99} onChange={setCount} size=\"default\" aria-label={t(`${prefix}historyLimit`)} decrementAriaLabel={t(`${prefix}decrease`)} incrementAriaLabel={t(`${prefix}increase`)} className=\"w-28 rounded-lg\" inputClassName=\"text-sm\" /><AiModelSelect value={model} onChange={setModel} className=\"h-9 px-3 py-2\" /></div>\n </div>\n}\n\nfunction AiNewChatButton({ onClick }: { onClick: () => void }) {\n const t = useT()\n return <Button type=\"button\" variant=\"ghost\" data-slot=\"ai-new-chat\" className=\"h-auto min-h-8 w-61 max-w-full justify-start gap-2 rounded-md p-1.5 text-sm text-status-success-text hover:bg-status-success-bg hover:text-status-success-text\" onClick={onClick}><span aria-hidden=\"true\" className=\"flex size-5 items-center justify-center rounded-full bg-status-success-solid text-status-success-solid-foreground\"><Plus className=\"size-3.5\" /></span><span className=\"min-w-0 whitespace-normal text-left\">{t(`${prefix}newChat`)}</span></Button>\n}\n\nexport function AiNewChatExample() {\n const t = useT()\n const [draft, setDraft] = React.useState(0)\n return <div className=\"grid gap-3\"><AiNewChatButton onClick={() => setDraft(value => value + 1)} /><output className=\"text-xs text-muted-foreground\">{t(`${prefix}draftNumber`, { number: draft })}</output></div>\n}\n\nfunction AiDivider() {\n return <div role=\"separator\" className=\"flex h-1 shrink-0 items-center px-1.5\"><span className=\"h-px w-full bg-border\" /></div>\n}\n\nexport function AiSidebarExample({ search = '01', initialCollapsed = false }: { search?: '01' | '02' | '03'; initialCollapsed?: boolean }) {\n const t = useT()\n const [collapsed, setCollapsed] = React.useState(initialCollapsed)\n const [query, setQuery] = React.useState('')\n const [active, setActive] = React.useState('projects')\n const [draft, setDraft] = React.useState(0)\n const newChat = () => { setDraft(value => value + 1); setActive('newChat') }\n const searchControl = <SearchInput value={query} onChange={setQuery} clearable={false} aria-label={t(`${prefix}search`)} placeholder={t(`${prefix}searchPlaceholder`)} className=\"h-9 w-61 max-w-full rounded-lg border-0 bg-muted px-2 shadow-none [&>svg]:size-5\" />\n const searchPopover = <Popover><PopoverTrigger asChild><AiIconButton aria-label={t(`${prefix}search`)}><Search aria-hidden=\"true\" /></AiIconButton></PopoverTrigger><PopoverContent className=\"w-auto p-3\">{searchControl}<p className=\"mt-2 text-xs text-muted-foreground\">{t(`${prefix}query`)} {query}</p></PopoverContent></Popover>\n const groups = [\n { label: 'pinned', items: ['launchPlan', 'researchNotes', 'contentIdeas'], icon: true },\n { label: 'recent', items: ['quarterlyReport', 'emailDraft', 'meetingNotes'], icon: false },\n { label: 'yesterday', items: ['campaignIdeas', 'productBrief', 'supportReply', 'weeklyPlan', 'projectSummary'], icon: false },\n ]\n return <div className=\"flex max-w-full flex-wrap items-start gap-5\">\n <Sidebar collapsed={collapsed} aria-label={t(`${prefix}sidebar`)} className={cn('max-w-full rounded-lg border border-border', collapsed ? 'w-18 gap-5 border-0 p-5' : 'w-68 gap-4 border-0 px-3.5 pb-3.5 pt-5')}>\n <div className=\"flex shrink-0 flex-col gap-5\"><div className=\"flex h-8 items-center justify-between\">\n {collapsed ? <AiIconButton aria-label={t(`${prefix}expand`)} onClick={() => setCollapsed(false)}><img src={aiProductArtwork.logo} alt=\"\" className=\"size-8\" /></AiIconButton> : <><img src={aiProductArtwork.logo} alt={t(`${prefix}brand`)} className=\"size-8\" /><div className=\"flex gap-1\">{search === '02' ? searchPopover : null}<AiIconButton aria-label={t(`${prefix}collapse`)} onClick={() => setCollapsed(true)}><PanelLeft aria-hidden=\"true\" /></AiIconButton></div></>}\n </div>{!collapsed && search === '01' ? searchControl : null}</div>\n {collapsed ? <><AiDivider /><div className=\"flex flex-1 flex-col gap-3\"><AiIconButton aria-label={t(`${prefix}newChat`)} onClick={newChat}><Plus aria-hidden=\"true\" /></AiIconButton><AiIconButton aria-label={t(`${prefix}projects`)} aria-pressed={active === 'projects'} onClick={() => setActive('projects')}><Folder aria-hidden=\"true\" /></AiIconButton><AiIconButton aria-label={t(`${prefix}library`)} aria-pressed={active === 'library'} onClick={() => setActive('library')}><BookOpen aria-hidden=\"true\" /></AiIconButton>{searchPopover}</div><AiDivider /></> : <>\n <div className=\"grid shrink-0 gap-1\"><AiNewChatButton onClick={newChat} /><AiNavItem active={active === 'projects'} onClick={() => setActive('projects')}>{t(`${prefix}projects`)}</AiNavItem><AiNavItem leading={<BookOpen aria-hidden=\"true\" className=\"size-5\" />} active={active === 'library'} onClick={() => setActive('library')}>{t(`${prefix}library`)}</AiNavItem>{search === '03' ? <Popover><PopoverTrigger asChild><Button type=\"button\" variant=\"ghost\" className=\"h-8 justify-start gap-2 rounded-md p-1.5 text-sm text-muted-foreground\"><Search aria-hidden=\"true\" className=\"size-5\" />{t(`${prefix}search`)}</Button></PopoverTrigger><PopoverContent className=\"w-auto p-3\">{searchControl}</PopoverContent></Popover> : null}</div>\n <AiDivider /><div className=\"flex min-h-0 flex-1 flex-col gap-4 overflow-auto\">\n {groups.map((group, groupIndex) => <React.Fragment key={group.label}>{groupIndex === 1 ? <AiDivider /> : null}<div className=\"grid shrink-0 gap-2\"><h3 className=\"px-1.5 text-xs font-medium leading-4 text-muted-foreground\">{t(`${prefix}${group.label}`)}</h3><div className=\"grid gap-1\">{group.items.filter(item => t(`${prefix}${item}`).toLocaleLowerCase().includes(query.toLocaleLowerCase())).map(item => <AiNavItem key={item} icon={group.icon} active={active === item} onClick={() => setActive(item)}>{t(`${prefix}${item}`)}</AiNavItem>)}</div></div></React.Fragment>)}\n </div><AiDivider />\n </>}\n <Popover><PopoverTrigger asChild><Button type=\"button\" variant=\"ghost\" aria-label={t(`${prefix}profile`)} data-slot=\"ai-sidebar-profile\" className={cn('shrink-0 justify-start rounded-md text-left', collapsed ? 'size-8 p-0' : 'h-13 gap-3 p-1.5')}><Avatar label=\"James Brown\" size={collapsed ? 32 : 40} />{!collapsed ? <><span className=\"flex min-w-0 flex-1 flex-col gap-1\"><span className=\"flex items-center gap-1 text-sm font-medium leading-5\">James Brown <Badge size={16} appearance=\"light\">Pro</Badge></span><span className=\"truncate text-xs font-normal leading-4 text-muted-foreground\">james@example.com</span></span><MoreVertical aria-hidden=\"true\" className=\"size-4.5\" /></> : null}</Button></PopoverTrigger><PopoverContent className=\"w-64 text-sm\"><p className=\"font-medium\">James Brown</p><p className=\"mt-2 text-muted-foreground\">{t(`${prefix}localOnly`)}</p></PopoverContent></Popover>\n </Sidebar><output aria-live=\"polite\" className=\"grid max-w-xs gap-2 text-sm text-muted-foreground\"><span>{t(`${prefix}selected`)} {t(`${prefix}${active}`)}</span><span>{t(`${prefix}draftNumber`, { number: draft })}</span>{query ? <span>{t(`${prefix}query`)} {query}</span> : null}</output>\n </div>\n}\n\nexport function AiMobileNavigationExample({ kind = 'default' }: { kind?: 'default' | 'in-projects' | 'projects' | 'project-details' }) {\n const t = useT()\n const [model, setModel] = React.useState('GPT-4')\n const [selected, setSelected] = React.useState('projects')\n const [draft, setDraft] = React.useState(0)\n const [project, setProject] = React.useState('')\n const [name, setName] = React.useState('')\n const [createOpen, setCreateOpen] = React.useState(false)\n const [pinned, setPinned] = React.useState(false)\n const id = React.useId()\n const menu = <Popover><PopoverTrigger asChild><AiIconButton aria-label={t(`${prefix}menu`)}><Menu aria-hidden=\"true\" /></AiIconButton></PopoverTrigger><PopoverContent className=\"grid w-auto gap-1 p-2\">{['projects', 'library'].map(item => <AiNavItem key={item} mobile active={selected === item} onClick={() => setSelected(item)}>{t(`${prefix}${item}`)}</AiNavItem>)}</PopoverContent></Popover>\n const more = <Popover><PopoverTrigger asChild><AiIconButton size={kind === 'project-details' ? 24 : 32} aria-label={t(`${prefix}more`)}><MoreHorizontal aria-hidden=\"true\" /></AiIconButton></PopoverTrigger><PopoverContent className=\"w-64\"><CheckboxField checked={pinned} onCheckedChange={value => setPinned(value === true)} label={t(`${prefix}pinProject`)} /></PopoverContent></Popover>\n const create = () => { if (!name.trim()) return; setProject(name.trim()); setName(''); setCreateOpen(false) }\n return <div className=\"grid w-full min-w-0 max-w-sm grid-cols-1 gap-4\"><header data-slot=\"ai-mobile-navigation\" data-kind={kind} className={cn('flex h-16 w-full min-w-0 items-center gap-2 bg-background px-2 py-3 sm:px-4', kind === 'project-details' ? 'sm:gap-2.5' : 'sm:gap-3.5')}>\n {kind === 'project-details' ? <><AiIconButton size={20} aria-label={t(`${prefix}allProjects`)} onClick={() => setSelected('projects')}><ArrowLeft aria-hidden=\"true\" /></AiIconButton><span className=\"flex-1 text-sm font-medium\">{t(`${prefix}allProjects`)}</span><div className=\"flex gap-2\"><AiIconButton size={24} aria-label={t(`${prefix}newChat`)} onClick={() => setDraft(value => value + 1)}><Plus aria-hidden=\"true\" /></AiIconButton>{more}</div></> : <>{menu}{kind === 'projects' ? <div className=\"flex flex-1 justify-end\"><Popover open={createOpen} onOpenChange={setCreateOpen}><PopoverTrigger asChild><Button type=\"button\" size=\"sm\" variant=\"outline\" className=\"w-auto shrink-0 gap-2 rounded-lg\"><Plus aria-hidden=\"true\" className=\"size-5\" />{t(`${prefix}addProject`)}</Button></PopoverTrigger><PopoverContent className=\"w-72\"><form className=\"grid gap-3\" onSubmit={event => { event.preventDefault(); create() }} onKeyDown={event => { if (event.key === 'Enter' && (event.ctrlKey || event.metaKey)) { event.preventDefault(); create() } }}><FieldLabel htmlFor={id} required>{t(`${prefix}projectName`)}</FieldLabel><Input id={id} value={name} onChange={event => setName(event.target.value)} autoFocus /><Button type=\"submit\" size=\"sm\" disabled={!name.trim()}>{t(`${prefix}addProject`)}</Button></form></PopoverContent></Popover></div> : <><div className=\"flex min-w-0 flex-1 flex-col gap-1\"><AiModelSelect value={model} onChange={setModel} compact />{kind === 'in-projects' ? <span className=\"truncate text-xs leading-4 text-muted-foreground\">{t(`${prefix}launchPlan`)}</span> : null}</div><AiIconButton aria-label={t(`${prefix}newChat`)} onClick={() => setDraft(value => value + 1)}><Plus aria-hidden=\"true\" /></AiIconButton>{more}</>}</>}\n </header><output aria-live=\"polite\" className=\"grid gap-1 text-xs text-muted-foreground\"><span>{t(`${prefix}selected`)} {t(`${prefix}${selected}`)}</span><span>{t(`${prefix}draftNumber`, { number: draft })}</span>{project ? <span>{t(`${prefix}createdProject`)} {project}</span> : null}{pinned ? <span>{t(`${prefix}pinProject`)}</span> : null}</output></div>\n}\n"
|
|
3
|
+
export const aiProductExampleCode = (expression: string) => source + "\n" + expression + "\n"
|