From 50c20a49c26ed4d2f710454705e7c59940125ef3 Mon Sep 17 00:00:00 2001 From: Pavlo Shylo Date: Tue, 10 Mar 2026 19:58:04 +0000 Subject: [PATCH 1/5] added biome --- openframe-frontend-core/.eslintrc.json | 13 +- openframe-frontend-core/.storybook/main.ts | 20 +- openframe-frontend-core/.storybook/preview.ts | 7 +- openframe-frontend-core/biome.json | 135 ++ openframe-frontend-core/package.json | 7 +- .../scripts/generate-icons.mjs | 33 +- openframe-frontend-core/src/assets/index.ts | 2 +- .../src/components/about-icon.tsx | 8 +- .../src/components/accordion.tsx | 32 +- .../src/components/alert-dialog.tsx | 109 +- .../src/components/alert.tsx | 66 +- .../src/components/announcement-bar.tsx | 55 +- .../src/components/aspect-ratio.tsx | 8 +- .../src/components/auth-stub.tsx | 8 +- .../src/components/avatar.tsx | 28 +- .../src/components/badge.tsx | 26 +- .../src/components/breadcrumb.tsx | 131 +- .../src/components/calendar.tsx | 87 +- .../src/components/carousel.tsx | 332 +++-- .../src/components/categories-cart.tsx | 78 +- .../src/components/category-card.tsx | 10 +- .../src/components/chart.tsx | 469 +++---- .../chat/approval-request-message.tsx | 96 +- .../src/components/chat/chat-container.tsx | 145 ++- .../src/components/chat/chat-input.tsx | 157 ++- .../components/chat/chat-message-enhanced.tsx | 175 +-- .../src/components/chat/chat-message-list.tsx | 364 +++--- .../components/chat/chat-message-skeleton.tsx | 89 +- .../src/components/chat/chat-quick-action.tsx | 69 +- .../components/chat/chat-sidebar-skeleton.tsx | 53 +- .../src/components/chat/chat-sidebar.tsx | 171 +-- .../components/chat/chat-typing-indicator.tsx | 78 +- .../components/chat/error-message-display.tsx | 41 +- .../src/components/chat/hooks/index.ts | 8 +- .../chat/hooks/use-chunk-catchup.ts | 426 +++---- .../hooks/use-nats-dialog-subscription.ts | 501 ++++---- .../hooks/use-realtime-chunk-processor.ts | 165 ++- .../src/components/chat/index.ts | 34 +- .../src/components/chat/model-display.tsx | 36 +- .../chat/tool-execution-display.tsx | 110 +- .../src/components/chat/types/api.types.ts | 198 +-- .../src/components/chat/types/chat.types.ts | 32 +- .../components/chat/types/component.types.ts | 206 ++-- .../src/components/chat/types/index.ts | 12 +- .../components/chat/types/message.types.ts | 176 +-- .../components/chat/types/network.types.ts | 114 +- .../components/chat/types/processing.types.ts | 129 +- .../src/components/chat/utils/chunk-parser.ts | 96 +- .../utils/extract-incomplete-message-state.ts | 58 +- .../src/components/chat/utils/index.ts | 25 +- .../chat/utils/message-segment-accumulator.ts | 192 +-- .../chat/utils/process-historical-messages.ts | 268 ++-- .../src/components/check-circle-icon.tsx | 26 +- .../src/components/checkbox.tsx | 24 +- .../src/components/chevron-button.tsx | 42 +- .../src/components/claude-icon.tsx | 13 +- .../src/components/coins-icon.tsx | 26 +- .../src/components/collapsible.tsx | 12 +- .../src/components/command.tsx | 95 +- .../src/components/comment-card.tsx | 214 ++-- .../src/components/community-hub-icon.tsx | 28 +- .../src/components/community-icon.tsx | 28 +- .../src/components/compare-icon.tsx | 28 +- .../components/content-loading-container.tsx | 79 +- .../src/components/context-menu.tsx | 124 +- .../components/custom-external-link-icon.tsx | 36 +- .../src/components/custom-fork-icon.tsx | 36 +- .../src/components/custom-icons.tsx | 38 +- .../src/components/custom-license-icon.tsx | 36 +- .../src/components/custom-star-icon.tsx | 30 +- .../src/components/custom-time-icon.tsx | 36 +- .../src/components/date-time-picker.tsx | 39 +- .../src/components/dialog.tsx | 162 ++- .../src/components/donut-icon.tsx | 2 +- .../src/components/dropdown-menu.tsx | 121 +- .../src/components/dynamic-skeleton.tsx | 72 +- .../src/components/edit-profile-icon.tsx | 17 +- .../src/components/elestio-logo.tsx | 72 +- .../src/components/empty-state.tsx | 165 +-- .../src/components/empty-vendor-icon.tsx | 40 +- .../src/components/faq-accordion.tsx | 153 +-- .../features/ai-enrich/AIEnrichButton.tsx | 34 +- .../features/ai-enrich/AIEnrichSection.tsx | 138 +-- .../features/ai-enrich/AIRequiredBadge.tsx | 23 +- .../features/ai-enrich/AIStatusIndicator.tsx | 59 +- .../features/ai-enrich/AIWarningsSection.tsx | 40 +- .../features/ai-enrich/ConfidenceBadge.tsx | 48 +- .../components/features/ai-enrich/index.ts | 31 +- .../features/array-entry-manager.tsx | 40 +- .../features/auth-providers-list.tsx | 14 +- .../components/features/changelog-manager.tsx | 24 +- .../features/changelog-sections-manager.tsx | 13 +- .../features/clickup-tasks-manager.tsx | 6 +- .../src/components/features/command-box.tsx | 50 +- .../components/features/error-boundary.tsx | 22 +- .../features/figma-prototype-viewer.tsx | 885 +++++++------- .../components/features/filters-dropdown.tsx | 429 ++++--- .../features/github-releases-manager.tsx | 10 +- .../features/github-repo-button.tsx | 10 +- .../src/components/features/index.ts | 83 +- .../features/knowledge-base-links-manager.tsx | 10 +- .../components/features/loading-provider.tsx | 58 +- .../features/media-gallery-manager.tsx | 283 ++--- .../components/features/more-about-button.tsx | 27 +- .../components/features/og-editor-preview.tsx | 40 +- .../components/features/organization-icon.tsx | 69 +- .../features/organization-selector.tsx | 211 ++-- .../features/os-platform-selector.tsx | 78 +- .../features/os-type-badge-group.tsx | 46 +- .../src/components/features/os-type-badge.tsx | 105 +- .../features/parallax-image-showcase.tsx | 260 ++-- .../src/components/features/paths-display.tsx | 72 +- .../components/features/platform-badge.tsx | 68 +- .../components/features/platform-filter.tsx | 16 +- .../features/policy-configuration-panel.tsx | 177 ++- .../components/features/provider-button.tsx | 8 +- .../features/push-button-selector.tsx | 80 +- .../features/release-media-manager.tsx | 51 +- .../components/features/section-selector.tsx | 150 +-- .../src/components/features/select-button.tsx | 97 +- .../features/seo-editor-preview.tsx | 73 +- .../features/social-links-manager.tsx | 56 +- .../features/start-with-openframe-button.tsx | 159 +-- .../features/status-filter-component.tsx | 24 +- .../src/components/features/tags-selector.tsx | 61 +- .../src/components/features/video-player.tsx | 88 +- .../src/components/features/view-toggle.tsx | 79 +- .../src/components/features/youtube-embed.tsx | 100 +- .../src/components/filter-chip.tsx | 118 +- .../src/components/flamingo-logo.tsx | 27 +- .../src/components/footer-waitlist-button.tsx | 8 +- .../src/components/footer.tsx | 163 +-- .../src/components/form.tsx | 98 +- .../src/components/github-icon.tsx | 23 +- .../src/components/google-logo.tsx | 2 +- .../src/components/hamburger-icon.tsx | 20 +- .../src/components/hero-image-uploader.tsx | 41 +- .../src/components/hover-card.tsx | 24 +- .../src/components/hubspot-icon.tsx | 4 +- .../src/components/icon-utils.tsx | 35 +- .../src/components/icons-block.tsx | 37 +- .../src/components/icons-stub.tsx | 4 +- .../alphabet/alphabet-a-circle-icon.tsx | 9 +- .../alphabet/alphabet-a-icon.tsx | 12 +- .../alphabet/alphabet-a-square-icon.tsx | 9 +- .../alphabet/alphabet-b-circle-icon.tsx | 9 +- .../alphabet/alphabet-b-icon.tsx | 12 +- .../alphabet/alphabet-b-square-icon.tsx | 9 +- .../alphabet/alphabet-c-circle-icon.tsx | 9 +- .../alphabet/alphabet-c-icon.tsx | 12 +- .../alphabet/alphabet-c-square-icon.tsx | 9 +- .../alphabet/alphabet-d-circle-icon.tsx | 9 +- .../alphabet/alphabet-d-icon.tsx | 12 +- .../alphabet/alphabet-d-square-icon.tsx | 9 +- .../alphabet/alphabet-e-circle-icon.tsx | 9 +- .../alphabet/alphabet-e-icon.tsx | 12 +- .../alphabet/alphabet-e-square-icon.tsx | 9 +- .../alphabet/alphabet-f-circle-icon.tsx | 9 +- .../alphabet/alphabet-f-icon.tsx | 12 +- .../alphabet/alphabet-f-square-icon.tsx | 9 +- .../alphabet/alphabet-g-circle-icon.tsx | 9 +- .../alphabet/alphabet-g-icon.tsx | 12 +- .../alphabet/alphabet-g-square-icon.tsx | 9 +- .../alphabet/alphabet-h-circle-icon.tsx | 9 +- .../alphabet/alphabet-h-icon.tsx | 12 +- .../alphabet/alphabet-h-square-icon.tsx | 9 +- .../alphabet/alphabet-i-circle-icon.tsx | 9 +- .../alphabet/alphabet-i-icon.tsx | 12 +- .../alphabet/alphabet-i-square-icon.tsx | 9 +- .../alphabet/alphabet-j-circle-icon.tsx | 9 +- .../alphabet/alphabet-j-icon.tsx | 12 +- .../alphabet/alphabet-j-square-icon.tsx | 9 +- .../alphabet/alphabet-k-circle-icon.tsx | 9 +- .../alphabet/alphabet-k-icon.tsx | 12 +- .../alphabet/alphabet-k-square-icon.tsx | 9 +- .../alphabet/alphabet-l-circle-icon.tsx | 9 +- .../alphabet/alphabet-l-icon.tsx | 12 +- .../alphabet/alphabet-l-square-icon.tsx | 9 +- .../alphabet/alphabet-m-circle-icon.tsx | 9 +- .../alphabet/alphabet-m-icon.tsx | 12 +- .../alphabet/alphabet-m-square-icon.tsx | 9 +- .../alphabet/alphabet-n-circle-icon.tsx | 9 +- .../alphabet/alphabet-n-icon.tsx | 12 +- .../alphabet/alphabet-n-square-icon.tsx | 9 +- .../alphabet/alphabet-o-circle-icon.tsx | 9 +- .../alphabet/alphabet-o-icon.tsx | 12 +- .../alphabet/alphabet-o-square-icon.tsx | 9 +- .../alphabet/alphabet-p-circle-icon.tsx | 9 +- .../alphabet/alphabet-p-icon.tsx | 12 +- .../alphabet/alphabet-p-square-icon.tsx | 9 +- .../alphabet/alphabet-q-circle-icon.tsx | 9 +- .../alphabet/alphabet-q-icon.tsx | 12 +- .../alphabet/alphabet-q-square-icon.tsx | 9 +- .../alphabet/alphabet-r-circle-icon.tsx | 9 +- .../alphabet/alphabet-r-icon.tsx | 12 +- .../alphabet/alphabet-r-square-icon.tsx | 9 +- .../alphabet/alphabet-s-circle-icon.tsx | 9 +- .../alphabet/alphabet-s-icon.tsx | 12 +- .../alphabet/alphabet-s-square-icon.tsx | 9 +- .../alphabet/alphabet-t-circle-icon.tsx | 9 +- .../alphabet/alphabet-t-icon.tsx | 12 +- .../alphabet/alphabet-t-square-icon.tsx | 9 +- .../alphabet/alphabet-u-circle-icon.tsx | 9 +- .../alphabet/alphabet-u-icon.tsx | 12 +- .../alphabet/alphabet-u-square-icon.tsx | 9 +- .../alphabet/alphabet-v-circle-icon.tsx | 9 +- .../alphabet/alphabet-v-icon.tsx | 12 +- .../alphabet/alphabet-v-square-icon.tsx | 9 +- .../alphabet/alphabet-w-circle-icon.tsx | 9 +- .../alphabet/alphabet-w-icon.tsx | 12 +- .../alphabet/alphabet-w-square-icon.tsx | 9 +- .../alphabet/alphabet-x-circle-icon.tsx | 9 +- .../alphabet/alphabet-x-icon.tsx | 12 +- .../alphabet/alphabet-x-square-icon.tsx | 9 +- .../alphabet/alphabet-y-circle-icon.tsx | 9 +- .../alphabet/alphabet-y-icon.tsx | 12 +- .../alphabet/alphabet-y-square-icon.tsx | 9 +- .../alphabet/alphabet-z-circle-icon.tsx | 9 +- .../alphabet/alphabet-z-icon.tsx | 12 +- .../alphabet/alphabet-z-square-icon.tsx | 9 +- .../arrows/arrow-01-down-icon.tsx | 17 +- .../arrows/arrow-01-left-icon.tsx | 17 +- .../arrows/arrow-01-right-icon.tsx | 14 +- .../arrows/arrow-01-up-icon.tsx | 17 +- .../arrows/arrow-02-down-icon.tsx | 12 +- .../arrows/arrow-02-left-icon.tsx | 12 +- .../arrows/arrow-02-right-icon.tsx | 9 +- .../arrows/arrow-02-up-icon.tsx | 12 +- .../arrows/arrow-03-down-icon.tsx | 12 +- .../arrows/arrow-03-left-icon.tsx | 12 +- .../arrows/arrow-03-right-icon.tsx | 9 +- .../arrows/arrow-03-up-icon.tsx | 12 +- .../arrows/arrow-04-down-icon.tsx | 12 +- .../arrows/arrow-04-left-icon.tsx | 12 +- .../arrows/arrow-04-right-icon.tsx | 9 +- .../arrows/arrow-04-up-icon.tsx | 12 +- .../arrows/arrow-circle-down-icon.tsx | 9 +- .../arrows/arrow-circle-left-icon.tsx | 9 +- .../arrows/arrow-circle-right-icon.tsx | 9 +- .../arrows/arrow-circle-up-icon.tsx | 9 +- .../arrows/arrow-corner-bl-icon.tsx | 9 +- .../arrows/arrow-corner-br-icon.tsx | 9 +- .../arrows/arrow-corner-tl-icon.tsx | 9 +- .../arrows/arrow-corner-tr-icon.tsx | 9 +- .../arrows/arrow-square-down-icon.tsx | 9 +- .../arrows/arrow-square-left-icon.tsx | 9 +- .../arrows/arrow-square-right-icon.tsx | 9 +- .../arrows/arrow-square-up-icon.tsx | 9 +- .../arrows/arrow-turn-down-icon.tsx | 9 +- .../arrows/arrow-turn-left-icon.tsx | 9 +- .../arrows/arrow-turn-right-icon.tsx | 9 +- .../arrows/arrow-turn-up-icon.tsx | 12 +- .../arrows/arrows-spin-icon.tsx | 12 +- .../arrows/chevron-01-down-icon.tsx | 9 +- .../arrows/chevron-01-left-icon.tsx | 9 +- .../arrows/chevron-01-right-icon.tsx | 9 +- .../arrows/chevron-01-up-icon.tsx | 12 +- .../arrows/chevron-02-down-icon.tsx | 9 +- .../arrows/chevron-02-left-icon.tsx | 9 +- .../arrows/chevron-02-right-icon.tsx | 9 +- .../arrows/chevron-02-up-icon.tsx | 12 +- .../arrows/chevron-circle-down-icon.tsx | 9 +- .../arrows/chevron-circle-left-icon.tsx | 9 +- .../arrows/chevron-circle-right-icon.tsx | 9 +- .../arrows/chevron-circle-up-icon.tsx | 9 +- .../arrows/chevron-square-down-icon.tsx | 9 +- .../arrows/chevron-square-left-icon.tsx | 9 +- .../arrows/chevron-square-right-icon.tsx | 9 +- .../arrows/chevron-square-up-icon.tsx | 9 +- .../arrows/chevrons-03-down-icon.tsx | 9 +- .../arrows/chevrons-03-left-icon.tsx | 9 +- .../arrows/chevrons-03-right-icon.tsx | 9 +- .../arrows/chevrons-03-up-icon.tsx | 9 +- .../arrows/chevrons-04-hr-icon.tsx | 9 +- .../arrows/chevrons-04-vr-icon.tsx | 9 +- .../arrows/chevrons-05-hr-icon.tsx | 9 +- .../arrows/chevrons-05-vr-icon.tsx | 9 +- .../arrows/expand-arrow-hr-icon.tsx | 14 +- .../arrows/expand-arrow-vr-icon.tsx | 14 +- .../icons-v2-generated/arrows/index.ts | 10 +- .../arrows/switch-hr-icon.tsx | 12 +- .../arrows/switch-vr-icon.tsx | 12 +- .../arrows/transfer-alt-icon.tsx | 12 +- .../arrows/transfer-alt-square-icon.tsx | 9 +- .../arrows/transfer-hr-icon.tsx | 12 +- .../arrows/transfer-hr-square-icon.tsx | 9 +- .../arrows/transfer-vr-icon.tsx | 12 +- .../arrows/transfer-vr-square-icon.tsx | 9 +- .../audio-and-visual/aperture-icon.tsx | 12 +- .../audio-and-visual/book-play-icon.tsx | 12 +- .../audio-and-visual/camera-icon.tsx | 12 +- .../audio-and-visual/camera-off-icon.tsx | 12 +- .../audio-and-visual/camera-retro-01-icon.tsx | 9 +- .../audio-and-visual/camera-retro-02-icon.tsx | 9 +- .../audio-and-visual/carousel-hr-icon.tsx | 12 +- .../audio-and-visual/carousel-vr-icon.tsx | 12 +- .../audio-and-visual/cassette-tape-icon.tsx | 9 +- .../audio-and-visual/clapperboard-icon.tsx | 9 +- .../clapperboard-play-icon.tsx | 9 +- .../collection-album-icon.tsx | 9 +- .../collection-audio-icon.tsx | 9 +- .../collection-image-icon.tsx | 9 +- .../collection-music-icon.tsx | 9 +- .../collection-video-icon.tsx | 9 +- .../audio-and-visual/color-palette-icon.tsx | 9 +- .../audio-and-visual/colors-icon.tsx | 12 +- .../audio-and-visual/contrast-01-icon.tsx | 17 +- .../audio-and-visual/contrast-02-icon.tsx | 12 +- .../audio-and-visual/disc-vinyl-icon.tsx | 12 +- .../audio-and-visual/film-hr-icon.tsx | 12 +- .../audio-and-visual/film-vr-icon.tsx | 12 +- .../audio-and-visual/flash-auto-icon.tsx | 12 +- .../audio-and-visual/flash-icon.tsx | 12 +- .../audio-and-visual/flash-off-icon.tsx | 12 +- .../audio-and-visual/gramophone-icon.tsx | 12 +- .../audio-and-visual/hand-1-finger-icon.tsx | 12 +- .../audio-and-visual/hand-2-finger-icon.tsx | 12 +- .../audio-and-visual/head-headphone-icon.tsx | 9 +- .../audio-and-visual/headphone-icon.tsx | 12 +- .../audio-and-visual/headset-icon.tsx | 12 +- .../audio-and-visual/image-ai-icon.tsx | 12 +- .../image-arrow-down-icon.tsx | 9 +- .../audio-and-visual/image-arrow-up-icon.tsx | 9 +- .../audio-and-visual/image-check-icon.tsx | 12 +- .../audio-and-visual/image-icon.tsx | 12 +- .../audio-and-visual/image-off-icon.tsx | 12 +- .../audio-and-visual/image-plus-icon.tsx | 12 +- .../audio-and-visual/image-xmark-icon.tsx | 12 +- .../audio-and-visual/lightning-fast-icon.tsx | 9 +- .../audio-and-visual/lightning-icon.tsx | 12 +- .../audio-and-visual/lightning-off-icon.tsx | 9 +- .../microphone-stand-icon.tsx | 9 +- .../audio-and-visual/movie-icon.tsx | 12 +- .../audio-and-visual/music-note-01-icon.tsx | 12 +- .../audio-and-visual/music-note-02-icon.tsx | 12 +- .../audio-and-visual/photo-filter-icon.tsx | 12 +- .../audio-and-visual/playlist-icon.tsx | 12 +- .../audio-and-visual/playlist-music-icon.tsx | 9 +- .../audio-and-visual/podcast-icon.tsx | 12 +- .../audio-and-visual/radio-icon.tsx | 12 +- .../sliders-square-hr-icon.tsx | 9 +- .../sliders-square-vr-icon.tsx | 9 +- .../audio-and-visual/standard-3d-icon.tsx | 12 +- .../audio-and-visual/standard-4k-icon.tsx | 12 +- .../audio-and-visual/standard-ad-icon.tsx | 12 +- .../audio-and-visual/standard-ar-icon.tsx | 12 +- .../audio-and-visual/standard-cc-icon.tsx | 12 +- .../audio-and-visual/standard-hd-icon.tsx | 12 +- .../audio-and-visual/standard-sd-icon.tsx | 12 +- .../audio-and-visual/standard-vr-icon.tsx | 12 +- .../video-recorder-alt-icon.tsx | 9 +- .../audio-and-visual/video-recorder-icon.tsx | 9 +- .../video-recorder-off-icon.tsx | 9 +- .../video-recorder-play-icon.tsx | 9 +- .../audio-and-visual/volume-down-icon.tsx | 12 +- .../audio-and-visual/volume-minus-icon.tsx | 12 +- .../audio-and-visual/volume-off-icon.tsx | 12 +- .../audio-and-visual/volume-plus-icon.tsx | 12 +- .../audio-and-visual/volume-up-icon.tsx | 12 +- .../audio-and-visual/volume-xmark-icon.tsx | 12 +- .../brand-logos/adobe-ae-icon.tsx | 12 +- .../brand-logos/adobe-ai-icon.tsx | 12 +- .../brand-logos/adobe-id-icon.tsx | 12 +- .../brand-logos/adobe-pr-icon.tsx | 12 +- .../brand-logos/adobe-ps-icon.tsx | 12 +- .../brand-logos/adobe-xd-icon.tsx | 12 +- .../brand-logos/android-icon.tsx | 12 +- .../brand-logos/anthropic-logo-icon.tsx | 9 +- .../brand-logos/app-store-icon.tsx | 12 +- .../brand-logos/apple-inc-icon.tsx | 12 +- .../brand-logos/apple-logo-icon.tsx | 12 +- .../brand-logos/authentik-logo-icon.tsx | 14 +- .../brand-logos/azure-ad-logo-icon.tsx | 12 +- .../brand-logos/behance-icon.tsx | 12 +- .../brand-logos/behance-square-icon.tsx | 9 +- .../brand-logos/chrome-icon.tsx | 12 +- .../brand-logos/discord-icon.tsx | 12 +- .../brand-logos/djinni-logo-icon.tsx | 12 +- .../brand-logos/dribbble-icon.tsx | 12 +- .../brand-logos/elestio-logo-icon.tsx | 14 +- .../brand-logos/facebook-icon.tsx | 12 +- .../brand-logos/facebook-square-icon.tsx | 9 +- .../brand-logos/figma-icon.tsx | 12 +- .../brand-logos/fleet-mdm-logo-icon.tsx | 9 +- .../brand-logos/framer-icon.tsx | 12 +- .../brand-logos/github-circle-icon.tsx | 9 +- .../brand-logos/github-icon.tsx | 12 +- .../brand-logos/github-logo-icon.tsx | 12 +- .../brand-logos/google-drive-icon.tsx | 12 +- .../brand-logos/google-gemini-logo-icon.tsx | 66 +- .../brand-logos/google-icon.tsx | 12 +- .../brand-logos/google-logo-icon.tsx | 12 +- .../brand-logos/google-play-icon.tsx | 12 +- .../icons-v2-generated/brand-logos/index.ts | 2 +- .../brand-logos/instagram-icon.tsx | 12 +- .../brand-logos/linkedin-icon.tsx | 12 +- .../brand-logos/linkedin-logo-icon.tsx | 9 +- .../brand-logos/linkedin-square-icon.tsx | 9 +- .../brand-logos/linux-logo-icon.tsx | 12 +- .../brand-logos/meshcentral-logo-icon.tsx | 23 +- .../brand-logos/messenger-icon.tsx | 12 +- .../brand-logos/microsoft-logo-icon.tsx | 9 +- .../brand-logos/office-365-logo-icon.tsx | 9 +- .../brand-logos/openai-logo-icon.tsx | 12 +- .../brand-logos/osquery-logo-icon.tsx | 12 +- .../brand-logos/paypal-icon.tsx | 12 +- .../brand-logos/pinterest-icon.tsx | 12 +- .../brand-logos/powershell-logo-icon.tsx | 9 +- .../brand-logos/python-logo-icon.tsx | 12 +- .../brand-logos/reddit-logo-icon.tsx | 12 +- .../brand-logos/slack-logo-icon.tsx | 12 +- .../brand-logos/soundcloud-icon.tsx | 12 +- .../brand-logos/spotify-icon.tsx | 12 +- .../brand-logos/tactical-rmm-logo-icon.tsx | 9 +- .../brand-logos/tiktok-icon.tsx | 12 +- .../brand-logos/tiktok-square-icon.tsx | 9 +- .../brand-logos/trello-icon.tsx | 12 +- .../brand-logos/twitter-icon.tsx | 12 +- .../brand-logos/twitter-square-icon.tsx | 9 +- .../brand-logos/twitter-x-icon.tsx | 12 +- .../brand-logos/twitter-x-square-icon.tsx | 9 +- .../brand-logos/windows-icon.tsx | 12 +- .../brand-logos/windows-logo-icon.tsx | 12 +- .../brand-logos/youtube-icon.tsx | 12 +- .../buildings/archway-icon.tsx | 12 +- .../buildings/bridge-01-icon.tsx | 12 +- .../buildings/bridge-02-icon.tsx | 12 +- .../buildings/building-icon.tsx | 12 +- .../buildings/building-tree-icon.tsx | 9 +- .../buildings/buildings-icon.tsx | 12 +- .../buildings/campground-icon.tsx | 12 +- .../buildings/castle-icon.tsx | 12 +- .../buildings/church-icon.tsx | 12 +- .../buildings/fence-icon.tsx | 12 +- .../buildings/fort-icon.tsx | 12 +- .../buildings/garage-icon.tsx | 12 +- .../buildings/hospital-icon.tsx | 12 +- .../buildings/hotel-alt-icon.tsx | 12 +- .../buildings/hotel-icon.tsx | 12 +- .../icons-v2-generated/buildings/index.ts | 2 +- .../buildings/lighthouse-icon.tsx | 12 +- .../buildings/park-icon.tsx | 12 +- .../buildings/school-icon.tsx | 12 +- .../buildings/terrace-icon.tsx | 12 +- .../buildings/warehouse-icon.tsx | 12 +- .../charts/activity-circle-icon.tsx | 9 +- .../charts/activity-icon.tsx | 12 +- .../charts/activity-square-icon.tsx | 9 +- .../charts/analysis-icon.tsx | 12 +- .../charts/chart-bar-01-hr-icon.tsx | 9 +- .../charts/chart-bar-01-ver-icon.tsx | 9 +- .../charts/chart-bar-02-hr-icon.tsx | 9 +- .../charts/chart-bar-02-vr-icon.tsx | 9 +- .../charts/chart-bar-03-hr-icon.tsx | 9 +- .../charts/chart-bar-03-ver-icon.tsx | 9 +- .../charts/chart-bar-square-hr-icon.tsx | 9 +- .../charts/chart-bar-square-vr-icon.tsx | 9 +- .../charts/chart-donut-icon.tsx | 12 +- .../charts/chart-line-down-icon.tsx | 9 +- .../charts/chart-line-heart-icon.tsx | 9 +- .../charts/chart-line-star-icon.tsx | 9 +- .../charts/chart-line-up-icon.tsx | 12 +- .../charts/chart-pie-icon.tsx | 12 +- .../charts/chart-pyramid-icon.tsx | 9 +- .../charts/chart-radar-icon.tsx | 12 +- .../charts/chart-scatter-icon.tsx | 9 +- .../charts/graph-bar-icon.tsx | 12 +- .../charts/graph-curve-arrow-icon.tsx | 9 +- .../charts/graph-line-down-icon.tsx | 9 +- .../charts/graph-line-up-icon.tsx | 12 +- .../charts/graph-mix-square-icon.tsx | 9 +- .../charts/graph-mixed-icon.tsx | 12 +- .../charts/hierarchy-01-icon.tsx | 12 +- .../charts/hierarchy-02-icon.tsx | 12 +- .../icons-v2-generated/charts/index.ts | 2 +- .../charts/network-icon.tsx | 12 +- .../charts/presentation-bar-icon.tsx | 9 +- .../charts/presentation-icon.tsx | 9 +- .../charts/presentation-line-icon.tsx | 9 +- .../charts/progress-icon.tsx | 12 +- .../charts/trend-down-icon.tsx | 12 +- .../charts/trend-up-icon.tsx | 12 +- .../charts/venn-diagram-icon.tsx | 12 +- .../icons-v2-generated/clothes/apron-icon.tsx | 12 +- .../clothes/bow-tie-icon.tsx | 12 +- .../clothes/chef-hat-icon.tsx | 12 +- .../clothes/footprints-icon.tsx | 12 +- .../clothes/glasses-01-icon.tsx | 12 +- .../clothes/glasses-02-icon.tsx | 12 +- .../clothes/glove-smitten-icon.tsx | 9 +- .../clothes/hanger-icon.tsx | 12 +- .../icons-v2-generated/clothes/hat-icon.tsx | 12 +- .../clothes/heels-chunky-icon.tsx | 12 +- .../clothes/heels-high-icon.tsx | 12 +- .../icons-v2-generated/clothes/index.ts | 2 +- .../clothes/life-vest-icon.tsx | 12 +- .../clothes/mustache-icon.tsx | 12 +- .../icons-v2-generated/clothes/pants-icon.tsx | 12 +- .../icons-v2-generated/clothes/shoes-icon.tsx | 12 +- .../clothes/shoes-run-icon.tsx | 12 +- .../clothes/skate-boots-icon.tsx | 12 +- .../icons-v2-generated/clothes/socks-icon.tsx | 12 +- .../clothes/stockings-icon.tsx | 12 +- .../clothes/t-shirt-icon.tsx | 12 +- .../clothes/tank-top-icon.tsx | 12 +- .../clothes/tank-top-number-icon.tsx | 9 +- .../icons-v2-generated/clothes/tie-icon.tsx | 12 +- .../clothes/underpants-icon.tsx | 12 +- .../coding/anonymous-icon.tsx | 12 +- .../icons-v2-generated/coding/binary-icon.tsx | 12 +- .../coding/blockchain-icon.tsx | 12 +- .../coding/bracket-curly-check-icon.tsx | 9 +- .../coding/bracket-curly-ellipsis-hr-icon.tsx | 9 +- .../coding/bracket-curly-ellipsis-vr-icon.tsx | 9 +- .../coding/bracket-curly-icon.tsx | 9 +- .../coding/bracket-curly-minus-icon.tsx | 14 +- .../coding/bracket-curly-plus-icon.tsx | 9 +- .../coding/bracket-curly-xmark-icon.tsx | 9 +- .../coding/bracket-round-check-icon.tsx | 9 +- .../coding/bracket-round-ellipsis-hr-icon.tsx | 9 +- .../coding/bracket-round-ellipsis-vr-icon.tsx | 9 +- .../coding/bracket-round-icon.tsx | 9 +- .../coding/bracket-round-minus-icon.tsx | 14 +- .../coding/bracket-round-plus-icon.tsx | 9 +- .../coding/bracket-round-xmark-icon.tsx | 9 +- .../coding/bracket-square-check-icon.tsx | 9 +- .../bracket-square-ellipsis-hr-icon.tsx | 9 +- .../bracket-square-ellipsis-vr-icon.tsx | 9 +- .../coding/bracket-square-icon.tsx | 9 +- .../coding/bracket-square-minus-icon.tsx | 14 +- .../coding/bracket-square-plus-icon.tsx | 9 +- .../coding/bracket-square-xmark-icon.tsx | 9 +- .../coding/browser-icon.tsx | 12 +- .../coding/browser-window-icon.tsx | 14 +- .../icons-v2-generated/coding/bug-icon.tsx | 12 +- .../coding/bug-off-icon.tsx | 12 +- .../coding/code-ai-icon.tsx | 12 +- .../coding/code-browser-icon.tsx | 12 +- .../coding/code-circle-icon.tsx | 12 +- .../icons-v2-generated/coding/code-icon.tsx | 12 +- .../coding/code-laptop-icon.tsx | 12 +- .../coding/code-monitor-icon.tsx | 12 +- .../coding/code-square-icon.tsx | 12 +- .../coding/coding-branch-icon.tsx | 9 +- .../coding/coding-commit-icon.tsx | 9 +- .../coding/coding-fork-icon.tsx | 12 +- .../coding/coding-merge-icon.tsx | 12 +- .../coding-pull-request-closed-icon.tsx | 9 +- .../coding/coding-pull-request-icon.tsx | 9 +- .../coding/database-icon.tsx | 12 +- .../coding/database-lock-icon.tsx | 9 +- .../coding/package-alt-icon.tsx | 12 +- .../coding/package-check-icon.tsx | 9 +- .../coding/package-icon.tsx | 12 +- .../coding/package-lock-icon.tsx | 12 +- .../coding/package-minus-icon.tsx | 14 +- .../coding/package-plus-icon.tsx | 12 +- .../coding/package-search-icon.tsx | 9 +- .../coding/package-xmark-icon.tsx | 9 +- .../coding/puzzle-01-icon.tsx | 12 +- .../coding/puzzle-02-icon.tsx | 12 +- .../coding/sidebar-flip-icon.tsx | 12 +- .../coding/sidebar-icon.tsx | 12 +- .../coding/terminal-browser-icon.tsx | 9 +- .../coding/terminal-icon.tsx | 12 +- .../coding/terminal-laptop-icon.tsx | 9 +- .../coding/terminal-monitor-icon.tsx | 9 +- .../coding/terminal-square-icon.tsx | 9 +- .../coding/web-design-icon.tsx | 12 +- .../communication/at-sign-icon.tsx | 12 +- .../communication/call-hangup-icon.tsx | 12 +- .../communication/call-icon.tsx | 12 +- .../communication/call-inbound-icon.tsx | 12 +- .../communication/call-lock-icon.tsx | 12 +- .../communication/call-minus-icon.tsx | 12 +- .../communication/call-missed-01-icon.tsx | 9 +- .../communication/call-missed-02-icon.tsx | 9 +- .../communication/call-off-icon.tsx | 12 +- .../communication/call-outbound-icon.tsx | 9 +- .../communication/call-pause-icon.tsx | 12 +- .../communication/call-plus-icon.tsx | 12 +- .../communication/call-ringing-icon.tsx | 12 +- .../communication/call-signal-icon.tsx | 12 +- .../communication/call-unlock-icon.tsx | 12 +- .../communication/call-xmark-icon.tsx | 12 +- .../communication/chat-blank-icon.tsx | 12 +- .../communication/chat-check-icon.tsx | 12 +- .../communication/chat-ellipsis-icon.tsx | 9 +- .../communication/chat-exclamation-icon.tsx | 9 +- .../communication/chat-face-icon.tsx | 12 +- .../communication/chat-heart-icon.tsx | 12 +- .../communication/chat-indicator-icon.tsx | 9 +- .../communication/chat-language-icon.tsx | 9 +- .../communication/chat-minus-icon.tsx | 17 +- .../communication/chat-off-icon.tsx | 12 +- .../communication/chat-plus-icon.tsx | 12 +- .../communication/chat-question-icon.tsx | 9 +- .../communication/chat-quote-icon.tsx | 12 +- .../communication/chat-star-icon.tsx | 12 +- .../communication/chat-text-icon.tsx | 12 +- .../communication/chat-voicemail-icon.tsx | 9 +- .../communication/chat-xmark-icon.tsx | 12 +- .../communication/chats-icon.tsx | 12 +- .../communication/delete-icon.tsx | 12 +- .../communication/dialpad-icon.tsx | 12 +- .../communication/email-arrow-down-icon.tsx | 9 +- .../communication/email-arrow-up-icon.tsx | 9 +- .../communication/email-banned-icon.tsx | 12 +- .../communication/email-check-icon.tsx | 12 +- .../communication/email-heart-icon.tsx | 12 +- .../communication/email-icon.tsx | 12 +- .../communication/email-lock-icon.tsx | 12 +- .../communication/email-open-icon.tsx | 12 +- .../email-reading-heart-icon.tsx | 9 +- .../communication/email-reading-icon.tsx | 9 +- .../communication/email-search-icon.tsx | 12 +- .../communication/email-shield-icon.tsx | 12 +- .../communication/emails-icon.tsx | 12 +- .../communication/inbox-arrow-down-icon.tsx | 9 +- .../communication/inbox-arrow-up-icon.tsx | 9 +- .../communication/inbox-content-icon.tsx | 9 +- .../communication/inbox-icon.tsx | 12 +- .../icons-v2-generated/communication/index.ts | 4 +- .../communication/mailbox-icon.tsx | 12 +- .../communication/message-blank-icon.tsx | 9 +- .../communication/message-check-icon.tsx | 9 +- .../communication/message-ellipsis-icon.tsx | 9 +- .../message-exclamation-icon.tsx | 9 +- .../communication/message-face-icon.tsx | 12 +- .../communication/message-heart-icon.tsx | 9 +- .../communication/message-indicator-icon.tsx | 9 +- .../communication/message-language-icon.tsx | 9 +- .../communication/message-minus-icon.tsx | 14 +- .../communication/message-off-icon.tsx | 12 +- .../communication/message-plus-icon.tsx | 12 +- .../communication/message-question-icon.tsx | 9 +- .../communication/message-quote-icon.tsx | 9 +- .../communication/message-robot-icon.tsx | 9 +- .../communication/message-star-icon.tsx | 12 +- .../communication/message-text-icon.tsx | 12 +- .../communication/message-voicemail-icon.tsx | 9 +- .../communication/message-xmark-icon.tsx | 9 +- .../communication/messages-icon.tsx | 12 +- .../communication/send-01-icon.tsx | 12 +- .../communication/send-02-icon.tsx | 17 +- .../communication/send-03-icon.tsx | 12 +- .../communication/story-add-icon.tsx | 12 +- .../communication/telephone-retro-icon.tsx | 9 +- .../communication/voicemail-icon.tsx | 17 +- .../date-and-time/alarm-blank-icon.tsx | 12 +- .../date-and-time/alarm-check-icon.tsx | 12 +- .../date-and-time/alarm-exclamation-icon.tsx | 9 +- .../date-and-time/alarm-icon.tsx | 12 +- .../date-and-time/alarm-minus-icon.tsx | 12 +- .../date-and-time/alarm-plus-icon.tsx | 12 +- .../date-and-time/alarm-snooze-icon.tsx | 12 +- .../date-and-time/alarm-xmark-icon.tsx | 12 +- .../calendar-arrow-left-icon.tsx | 14 +- .../calendar-arrow-right-icon.tsx | 14 +- .../date-and-time/calendar-bookmark-icon.tsx | 14 +- .../date-and-time/calendar-check-icon.tsx | 14 +- .../date-and-time/calendar-day-icon.tsx | 17 +- .../date-and-time/calendar-days-icon.tsx | 14 +- .../date-and-time/calendar-heart-icon.tsx | 14 +- .../date-and-time/calendar-icon.tsx | 17 +- .../date-and-time/calendar-lock-icon.tsx | 14 +- .../date-and-time/calendar-minus-icon.tsx | 14 +- .../date-and-time/calendar-off-icon.tsx | 12 +- .../date-and-time/calendar-plus-icon.tsx | 14 +- .../date-and-time/calendar-star-icon.tsx | 14 +- .../date-and-time/calendar-xmark-icon.tsx | 14 +- .../date-and-time/clock-check-icon.tsx | 12 +- .../date-and-time/clock-history-icon.tsx | 9 +- .../date-and-time/clock-icon.tsx | 12 +- .../date-and-time/clock-plus-icon.tsx | 12 +- .../date-and-time/hourglass-clock-icon.tsx | 9 +- .../date-and-time/hourglass-icon.tsx | 12 +- .../date-and-time/stopwatch-icon.tsx | 12 +- .../date-and-time/timer-10s-icon.tsx | 12 +- .../date-and-time/timer-icon.tsx | 12 +- .../design/align-bottom-icon.tsx | 12 +- .../design/align-left-icon.tsx | 12 +- .../design/align-right-icon.tsx | 12 +- .../design/align-top-icon.tsx | 12 +- .../design/angle-90-icon.tsx | 12 +- .../icons-v2-generated/design/angle-icon.tsx | 12 +- .../design/arrange-icon.tsx | 12 +- .../design/book-font-icon.tsx | 12 +- .../icons-v2-generated/design/brush-icon.tsx | 12 +- .../icons-v2-generated/design/crop-icon.tsx | 12 +- .../design/distribute-horizontal-icon.tsx | 9 +- .../design/distribute-vertical-icon.tsx | 9 +- .../design/divider-icon.tsx | 12 +- .../icons-v2-generated/design/dots-icon.tsx | 12 +- .../design/dropper-icon.tsx | 12 +- .../icons-v2-generated/design/eraser-icon.tsx | 12 +- .../design/fill-pour-icon.tsx | 12 +- .../design/grid-alt-icon.tsx | 12 +- .../design/grid-check-icon.tsx | 12 +- .../icons-v2-generated/design/grid-icon.tsx | 12 +- .../design/grid-layout-icon.tsx | 12 +- .../design/grid-minus-icon.tsx | 12 +- .../design/grid-plus-icon.tsx | 12 +- .../design/grip-hr-icon.tsx | 12 +- .../design/grip-vr-icon.tsx | 12 +- .../icons-v2-generated/design/index.ts | 2 +- .../icons-v2-generated/design/layers-icon.tsx | 12 +- .../design/layers-minus-icon.tsx | 12 +- .../design/layers-off-icon.tsx | 12 +- .../design/layers-plus-icon.tsx | 12 +- .../icons-v2-generated/design/layout-icon.tsx | 12 +- .../design/magic-wand-icon.tsx | 12 +- .../icons-v2-generated/design/marker-icon.tsx | 12 +- .../icons-v2-generated/design/move-icon.tsx | 12 +- .../design/paint-roller-icon.tsx | 12 +- .../icons-v2-generated/design/pen-01-icon.tsx | 12 +- .../icons-v2-generated/design/pen-02-icon.tsx | 12 +- .../design/pen-brush-icon.tsx | 12 +- .../design/pen-edit-icon.tsx | 12 +- .../design/pen-line-icon.tsx | 12 +- .../design/pen-nib-alt-icon.tsx | 12 +- .../design/pen-nib-circle-icon.tsx | 9 +- .../design/pen-nib-icon.tsx | 12 +- .../design/pen-nib-minus-icon.tsx | 12 +- .../design/pen-nib-plus-icon.tsx | 12 +- .../design/pen-point-icon.tsx | 17 +- .../icons-v2-generated/design/pencil-icon.tsx | 12 +- .../design/reflect-hr-icon.tsx | 12 +- .../design/reflect-vr-icon.tsx | 12 +- .../design/scaling-up-icon.tsx | 12 +- .../design/scissors-icon.tsx | 12 +- .../design/scribble-icon.tsx | 12 +- .../design/shape-circle-dash-icon.tsx | 9 +- .../design/shape-square-dash-icon.tsx | 9 +- .../icons-v2-generated/design/stamp-icon.tsx | 12 +- .../design/table-cell-icon.tsx | 12 +- .../design/table-col-icon.tsx | 17 +- .../icons-v2-generated/design/table-icon.tsx | 12 +- .../design/table-row-icon.tsx | 17 +- .../design/text-tool-icon.tsx | 12 +- .../design/vector-bezier-curve-icon.tsx | 9 +- .../design/vector-circle-icon.tsx | 9 +- .../design/vector-square-icon.tsx | 9 +- .../design/vector-triangle-icon.tsx | 9 +- .../devices/airdrop-icon.tsx | 12 +- .../devices/airplay-icon.tsx | 12 +- .../devices/battery-charging-icon.tsx | 9 +- .../devices/battery-empty-icon.tsx | 9 +- .../devices/battery-full-icon.tsx | 12 +- .../devices/battery-low-icon.tsx | 12 +- .../devices/battery-medium-icon.tsx | 9 +- .../devices/battery-off-icon.tsx | 12 +- .../devices/bluetooth-banned-icon.tsx | 9 +- .../devices/bluetooth-connecting-icon.tsx | 9 +- .../devices/bluetooth-icon.tsx | 12 +- .../devices/bluetooth-off-icon.tsx | 9 +- .../devices/bluetooth-search-icon.tsx | 9 +- .../devices/brightness-down-icon.tsx | 9 +- .../devices/brightness-up-icon.tsx | 9 +- .../icons-v2-generated/devices/cctv-icon.tsx | 12 +- .../devices/cctv-off-icon.tsx | 12 +- .../devices/chromecast-icon.tsx | 12 +- .../devices/cloud-arrow-down-icon.tsx | 9 +- .../devices/cloud-arrow-up-icon.tsx | 9 +- .../devices/cloud-check-icon.tsx | 12 +- .../devices/cloud-exclamation-icon.tsx | 9 +- .../icons-v2-generated/devices/cloud-icon.tsx | 12 +- .../devices/cloud-music-icon.tsx | 12 +- .../devices/cloud-xmark-icon.tsx | 12 +- .../devices/computer-mouse-icon.tsx | 14 +- .../devices/connection-icon.tsx | 12 +- .../devices/earphone-icon.tsx | 12 +- .../icons-v2-generated/devices/eject-icon.tsx | 12 +- .../devices/floppy-disc-icon.tsx | 12 +- .../devices/game-controller-icon.tsx | 9 +- .../icons-v2-generated/devices/game-icon.tsx | 12 +- .../devices/hard-drive-icon.tsx | 12 +- .../devices/hard-drives-icon.tsx | 12 +- .../devices/keyboard-down-icon.tsx | 9 +- .../devices/keyboard-icon.tsx | 12 +- .../devices/keyboard-up-icon.tsx | 12 +- .../devices/laptop-file-icon.tsx | 12 +- .../devices/laptop-icon.tsx | 12 +- .../devices/laptop-mobile-icon.tsx | 9 +- .../devices/memory-card-icon.tsx | 12 +- .../devices/microchip-ai-icon.tsx | 12 +- .../devices/microchip-icon.tsx | 12 +- .../devices/mobile-phone-banned-icon.tsx | 9 +- .../devices/mobile-phone-icon.tsx | 17 +- .../devices/mobile-phone-lock-icon.tsx | 9 +- .../devices/mobile-phone-off-icon.tsx | 9 +- .../devices/mobile-phone-shield-icon.tsx | 9 +- .../devices/mobile-phone-signal-in-icon.tsx | 9 +- .../devices/mobile-phone-signal-out-icon.tsx | 9 +- .../devices/mobile-phone-vibration-icon.tsx | 9 +- .../icons-v2-generated/devices/modem-icon.tsx | 12 +- .../devices/monitor-alt-icon.tsx | 12 +- .../devices/monitor-icon.tsx | 12 +- .../devices/monitor-off-icon.tsx | 12 +- .../devices/office-phone-icon.tsx | 12 +- .../icons-v2-generated/devices/pc-icon.tsx | 12 +- .../devices/power-circle-icon.tsx | 12 +- .../icons-v2-generated/devices/power-icon.tsx | 17 +- .../devices/printer-icon.tsx | 12 +- .../devices/printer-off-icon.tsx | 12 +- .../devices/projector-icon.tsx | 12 +- .../icons-v2-generated/devices/rss-icon.tsx | 12 +- .../devices/scanner-icon.tsx | 12 +- .../devices/signal-broadcast-01-icon.tsx | 9 +- .../devices/signal-broadcast-02-icon.tsx | 9 +- .../devices/signal-broadcast-03-icon.tsx | 9 +- .../devices/signal-level-icon.tsx | 12 +- .../devices/sim-card-icon.tsx | 12 +- .../devices/speaker-icon.tsx | 12 +- .../devices/tablet-icon.tsx | 17 +- .../icons-v2-generated/devices/tv-icon.tsx | 12 +- .../devices/tv-music-icon.tsx | 12 +- .../icons-v2-generated/devices/usb-icon.tsx | 12 +- .../devices/vr-glasses-icon.tsx | 12 +- .../devices/watch-01-icon.tsx | 12 +- .../devices/watch-02-icon.tsx | 12 +- .../devices/watch-03-icon.tsx | 12 +- .../devices/webcam-circle-icon.tsx | 9 +- .../devices/webcam-square-icon.tsx | 9 +- .../devices/wifi-exclamation-icon.tsx | 9 +- .../icons-v2-generated/devices/wifi-icon.tsx | 12 +- .../devices/wifi-lock-icon.tsx | 12 +- .../devices/wifi-off-icon.tsx | 12 +- .../devices/wifi-shield-icon.tsx | 12 +- .../devices/wifi-xmark-icon.tsx | 12 +- .../documents/box-archive-icon.tsx | 12 +- .../documents/clipboard-check-icon.tsx | 9 +- .../documents/clipboard-exclamation-icon.tsx | 9 +- .../documents/clipboard-icon.tsx | 12 +- .../documents/clipboard-info-icon.tsx | 9 +- .../documents/clipboard-list-icon.tsx | 9 +- .../documents/clipboard-minus-icon.tsx | 9 +- .../documents/clipboard-plus-icon.tsx | 9 +- .../documents/clipboard-question-icon.tsx | 9 +- .../documents/clipboard-xmark-icon.tsx | 9 +- .../documents/copy-01-icon.tsx | 12 +- .../documents/copy-02-icon.tsx | 12 +- .../documents/copy-03-icon.tsx | 12 +- .../documents/file-activity-icon.tsx | 9 +- .../documents/file-attachment-icon.tsx | 9 +- .../documents/file-audio-icon.tsx | 12 +- .../documents/file-banned-icon.tsx | 12 +- .../documents/file-bookmark-icon.tsx | 9 +- .../documents/file-chart-bar-icon.tsx | 9 +- .../documents/file-chart-line-icon.tsx | 9 +- .../documents/file-check-icon.tsx | 12 +- .../documents/file-code-icon.tsx | 12 +- .../documents/file-content-icon.tsx | 12 +- .../documents/file-dollar-icon.tsx | 12 +- .../documents/file-edit-icon.tsx | 12 +- .../documents/file-exclamation-icon.tsx | 9 +- .../documents/file-heart-icon.tsx | 12 +- .../documents/file-icon.tsx | 12 +- .../documents/file-image-icon.tsx | 17 +- .../documents/file-info-icon.tsx | 12 +- .../documents/file-lock-icon.tsx | 12 +- .../documents/file-minus-icon.tsx | 17 +- .../documents/file-music-icon.tsx | 12 +- .../documents/file-off-icon.tsx | 12 +- .../documents/file-play-icon.tsx | 12 +- .../documents/file-plus-icon.tsx | 12 +- .../documents/file-question-icon.tsx | 9 +- .../documents/file-search-icon.tsx | 12 +- .../documents/file-shield-icon.tsx | 12 +- .../documents/file-user-icon.tsx | 12 +- .../documents/file-xmark-icon.tsx | 12 +- .../documents/files-icon.tsx | 12 +- .../documents/folder-banned-icon.tsx | 9 +- .../documents/folder-bookmark-icon.tsx | 9 +- .../documents/folder-check-icon.tsx | 12 +- .../documents/folder-edit-icon.tsx | 12 +- .../documents/folder-icon.tsx | 12 +- .../documents/folder-image-icon.tsx | 17 +- .../documents/folder-info-icon.tsx | 12 +- .../documents/folder-lock-icon.tsx | 12 +- .../documents/folder-minus-icon.tsx | 17 +- .../documents/folder-off-icon.tsx | 12 +- .../documents/folder-open-icon.tsx | 12 +- .../documents/folder-plus-icon.tsx | 12 +- .../documents/folder-search-icon.tsx | 9 +- .../documents/folder-shield-icon.tsx | 9 +- .../documents/folder-user-icon.tsx | 12 +- .../documents/folder-xmark-icon.tsx | 12 +- .../documents/folders-icon.tsx | 12 +- .../icons-v2-generated/documents/index.ts | 4 +- .../documents/memo-icon.tsx | 12 +- .../documents/newspaper-icon.tsx | 12 +- .../documents/page-blank-icon.tsx | 12 +- .../documents/paperclip-icon.tsx | 12 +- .../documents/paperclip-vr-icon.tsx | 12 +- .../documents/paste-01-icon.tsx | 12 +- .../documents/paste-02-icon.tsx | 12 +- .../documents/paste-03-icon.tsx | 12 +- .../icons-v2-generated/documents/pin-icon.tsx | 17 +- .../documents/pin-vr-icon.tsx | 17 +- .../documents/sticker-dots-icon.tsx | 12 +- .../documents/sticker-emoji-icon.tsx | 9 +- .../documents/sticker-icon.tsx | 12 +- .../documents/stickers-icon.tsx | 12 +- .../finance/atm-card-icon.tsx | 12 +- .../finance/atm-money-icon.tsx | 12 +- .../icons-v2-generated/finance/bank-icon.tsx | 12 +- .../finance/bank-interest-icon.tsx | 9 +- .../icons-v2-generated/finance/coin-icon.tsx | 12 +- .../finance/coin-insert-icon.tsx | 12 +- .../finance/coins-exchange-currency-icon.tsx | 9 +- .../finance/coins-exchange-icon.tsx | 9 +- .../icons-v2-generated/finance/coins-icon.tsx | 12 +- .../finance/credit-card-alt-icon.tsx | 9 +- .../finance/credit-card-arrow-down-icon.tsx | 9 +- .../finance/credit-card-arrow-up-icon.tsx | 9 +- .../finance/credit-card-edit-icon.tsx | 9 +- .../finance/credit-card-icon.tsx | 12 +- .../finance/credit-card-lock-icon.tsx | 9 +- .../finance/credit-card-minus-icon.tsx | 9 +- .../finance/credit-card-plus-icon.tsx | 9 +- .../finance/credit-card-search-icon.tsx | 9 +- .../finance/credit-card-shield-icon.tsx | 9 +- .../finance/credit-card-xmark-icon.tsx | 9 +- .../finance/credit-cards-exchange-icon.tsx | 9 +- .../finance/hand-coin-icon.tsx | 12 +- .../finance/hand-dollar-icon.tsx | 12 +- .../icons-v2-generated/finance/index.ts | 2 +- .../finance/money-bill-dollar-icon.tsx | 9 +- .../finance/money-bill-icon.tsx | 12 +- .../finance/money-bill-wavy-dollar-icon.tsx | 9 +- .../finance/money-bill-wavy-icon.tsx | 9 +- .../finance/money-cheque-dollar-icon.tsx | 9 +- .../finance/money-cheque-edit-icon.tsx | 9 +- .../finance/piggy-bank-icon.tsx | 12 +- .../finance/pos-machine-icon.tsx | 12 +- .../icons-v2-generated/finance/sack-icon.tsx | 12 +- .../finance/wallet-01-icon.tsx | 12 +- .../finance/wallet-02-icon.tsx | 12 +- .../food-and-drinks/acorn-icon.tsx | 12 +- .../food-and-drinks/apple-icon.tsx | 12 +- .../food-and-drinks/avocado-icon.tsx | 12 +- .../food-and-drinks/bagel-icon.tsx | 12 +- .../food-and-drinks/banana-icon.tsx | 12 +- .../food-and-drinks/beer-icon.tsx | 12 +- .../food-and-drinks/bowl-hot-icon.tsx | 12 +- .../food-and-drinks/bowl-noodle-icon.tsx | 12 +- .../food-and-drinks/bread-long-icon.tsx | 12 +- .../food-and-drinks/bread-short-icon.tsx | 12 +- .../food-and-drinks/bread-slice-icon.tsx | 12 +- .../food-and-drinks/cake-birthday-icon.tsx | 9 +- .../food-and-drinks/cake-slice-icon.tsx | 17 +- .../food-and-drinks/candy-cane-icon.tsx | 12 +- .../food-and-drinks/candy-icon.tsx | 12 +- .../food-and-drinks/carrot-icon.tsx | 12 +- .../food-and-drinks/cater-icon.tsx | 12 +- .../food-and-drinks/cheese-icon.tsx | 12 +- .../food-and-drinks/cherries-icon.tsx | 12 +- .../food-and-drinks/coffee-bean-icon.tsx | 12 +- .../food-and-drinks/coffee-beans-icon.tsx | 12 +- .../food-and-drinks/cookie-icon.tsx | 12 +- .../food-and-drinks/cup-bubble-tea-icon.tsx | 9 +- .../food-and-drinks/cup-to-go-icon.tsx | 12 +- .../food-and-drinks/cup-water-icon.tsx | 12 +- .../food-and-drinks/cupcake-icon.tsx | 12 +- .../food-and-drinks/egg-fried-icon.tsx | 12 +- .../food-and-drinks/egg-icon.tsx | 12 +- .../food-and-drinks/eggplant-icon.tsx | 12 +- .../food-and-drinks/fish-bone-icon.tsx | 12 +- .../food-and-drinks/fish-icon.tsx | 12 +- .../food-and-drinks/garlic-icon.tsx | 12 +- .../glass-cocktail-01-icon.tsx | 9 +- .../glass-cocktail-02-icon.tsx | 9 +- .../food-and-drinks/glass-wine-icon.tsx | 17 +- .../food-and-drinks/glasses-wine-icon.tsx | 12 +- .../food-and-drinks/grapes-icon.tsx | 12 +- .../food-and-drinks/hamburger-cheese-icon.tsx | 9 +- .../food-and-drinks/hamburger-veggie-icon.tsx | 9 +- .../food-and-drinks/honey-pot-icon.tsx | 12 +- .../food-and-drinks/hotdog-icon.tsx | 12 +- .../food-and-drinks/ice-cream-icon.tsx | 12 +- .../food-and-drinks/index.ts | 4 +- .../food-and-drinks/jar-icon.tsx | 12 +- .../food-and-drinks/kiwi-icon.tsx | 12 +- .../food-and-drinks/leafy-green-icon.tsx | 12 +- .../food-and-drinks/lemon-icon.tsx | 12 +- .../food-and-drinks/lemon-slice-icon.tsx | 12 +- .../food-and-drinks/meat-icon.tsx | 12 +- .../food-and-drinks/milk-bottle-icon.tsx | 12 +- .../food-and-drinks/milk-carton-icon.tsx | 12 +- .../food-and-drinks/mug-hot-icon.tsx | 12 +- .../food-and-drinks/mug-icon.tsx | 12 +- .../food-and-drinks/mug-tea-icon.tsx | 12 +- .../food-and-drinks/mushroom-icon.tsx | 12 +- .../food-and-drinks/peach-icon.tsx | 12 +- .../food-and-drinks/pear-icon.tsx | 12 +- .../food-and-drinks/pie-icon.tsx | 12 +- .../food-and-drinks/pizza-slice-icon.tsx | 12 +- .../food-and-drinks/sandwich-icon.tsx | 12 +- .../food-and-drinks/sausage-icon.tsx | 12 +- .../food-and-drinks/shrimp-icon.tsx | 12 +- .../food-and-drinks/soda-can-icon.tsx | 12 +- .../food-and-drinks/steak-icon.tsx | 12 +- .../food-and-drinks/strawberry-icon.tsx | 12 +- .../food-and-drinks/water-bottle-icon.tsx | 12 +- .../food-and-drinks/watermelon-icon.tsx | 12 +- .../health/bandage-icon.tsx | 12 +- .../health/bandages-icon.tsx | 12 +- .../health/blood-pressure-icon.tsx | 9 +- .../health/bone-broken-icon.tsx | 12 +- .../icons-v2-generated/health/bone-icon.tsx | 12 +- .../health/book-medical-icon.tsx | 12 +- .../health/brain-ai-icon.tsx | 12 +- .../icons-v2-generated/health/brain-icon.tsx | 12 +- .../health/briefcase-medical-icon.tsx | 9 +- .../health/capsule-icon.tsx | 12 +- .../health/capsule-tablet-icon.tsx | 9 +- .../health/clipboard-medical-icon.tsx | 9 +- .../health/dispenser-medical-icon.tsx | 9 +- .../icons-v2-generated/health/dna-icon.tsx | 12 +- .../icons-v2-generated/health/ear-icon.tsx | 12 +- .../health/face-mask-icon.tsx | 12 +- .../health/hand-droplet-icon.tsx | 12 +- .../health/hand-heart-icon.tsx | 12 +- .../icons-v2-generated/health/hand-icon.tsx | 12 +- .../health/hands-heart-icon.tsx | 12 +- .../icons-v2-generated/health/hands-icon.tsx | 12 +- .../icons-v2-generated/health/head-icon.tsx | 12 +- .../health/head-medical-icon.tsx | 12 +- .../health/heart-broken-icon.tsx | 12 +- .../icons-v2-generated/health/heart-icon.tsx | 12 +- .../health/heart-pulse-icon.tsx | 12 +- .../health/medical-circle-icon.tsx | 9 +- .../health/medical-icon.tsx | 12 +- .../health/medical-square-icon.tsx | 9 +- .../icons-v2-generated/health/mouth-icon.tsx | 17 +- .../icons-v2-generated/health/nose-icon.tsx | 12 +- .../icons-v2-generated/health/pulse-icon.tsx | 12 +- .../health/pulse-square-icon.tsx | 12 +- .../icons-v2-generated/health/skull-icon.tsx | 12 +- .../health/stethoscope-icon.tsx | 12 +- .../health/stomach-icon.tsx | 12 +- .../health/syringe-icon.tsx | 12 +- .../health/thermometer-icon.tsx | 12 +- .../health/tooth-broken-icon.tsx | 12 +- .../icons-v2-generated/health/tooth-icon.tsx | 12 +- .../health/tooth-shield-check-icon.tsx | 9 +- .../health/virus-01-icon.tsx | 12 +- .../health/virus-02-icon.tsx | 12 +- .../health/watch-heart-icon.tsx | 12 +- .../health/watch-pulse-icon.tsx | 12 +- .../health/wheelchair-icon.tsx | 12 +- .../household/air-conditioner-icon.tsx | 9 +- .../icons-v2-generated/household/axe-icon.tsx | 12 +- .../household/baby-milk-icon.tsx | 12 +- .../household/bath-icon.tsx | 12 +- .../household/bbq-grill-icon.tsx | 12 +- .../household/bed-bunk-icon.tsx | 12 +- .../household/bed-front-icon.tsx | 12 +- .../icons-v2-generated/household/bed-icon.tsx | 12 +- .../household/bed-person-icon.tsx | 12 +- .../household/blender-icon.tsx | 12 +- .../household/chair-icon.tsx | 12 +- .../household/chair-swivel-icon.tsx | 12 +- .../household/cleaver-icon.tsx | 12 +- .../household/couch-icon.tsx | 12 +- .../household/curtain-icon.tsx | 12 +- .../household/dispenser-01-icon.tsx | 12 +- .../household/dispenser-02-icon.tsx | 12 +- .../household/door-icon.tsx | 12 +- .../household/door-open-icon.tsx | 12 +- .../household/dryer-icon.tsx | 12 +- .../household/electrical-plug-icon.tsx | 9 +- .../icons-v2-generated/household/fan-icon.tsx | 12 +- .../household/fire-extinguisher-icon.tsx | 9 +- .../household/flashlight-icon.tsx | 12 +- .../household/fork-knife-icon.tsx | 12 +- .../household/fork-plate-icon.tsx | 12 +- .../household/fork-spoon-icon.tsx | 12 +- .../household/hairdryer-icon.tsx | 12 +- .../household/hammer-auction-icon.tsx | 9 +- .../household/hammer-icon.tsx | 12 +- .../household/hot-water-icon.tsx | 12 +- .../household/house-door-icon.tsx | 12 +- .../household/house-icon.tsx | 12 +- .../household/house-medical-icon.tsx | 9 +- .../household/house-window-icon.tsx | 12 +- .../household/kettle-icon.tsx | 12 +- .../household/lamp-desk-icon.tsx | 12 +- .../household/lamp-icon.tsx | 12 +- .../household/lightbulb-icon.tsx | 12 +- .../household/lightbulb-on-icon.tsx | 12 +- .../household/lipstick-icon.tsx | 12 +- .../household/magnet-icon.tsx | 12 +- .../household/microphone-icon.tsx | 12 +- .../household/microwave-icon.tsx | 12 +- .../household/mirror-icon.tsx | 12 +- .../household/mixer-icon.tsx | 12 +- .../household/oven-icon.tsx | 12 +- .../household/pan-hot-icon.tsx | 12 +- .../icons-v2-generated/household/pot-icon.tsx | 12 +- .../household/refrigerator-icon.tsx | 9 +- .../household/scale-01-icon.tsx | 12 +- .../household/scale-02-icon.tsx | 12 +- .../household/shovel-icon.tsx | 12 +- .../household/siren-icon.tsx | 12 +- .../household/siren-on-icon.tsx | 12 +- .../household/socket-icon.tsx | 12 +- .../household/solar-panel-icon.tsx | 12 +- .../household/spatula-icon.tsx | 12 +- .../household/stairs-icon.tsx | 12 +- .../household/storage-cabin-icon.tsx | 9 +- .../household/storage-clothing-icon.tsx | 9 +- .../household/toothbrush-icon.tsx | 12 +- .../household/tube-icon.tsx | 12 +- .../household/tv-retro-icon.tsx | 12 +- .../household/umbrella-icon.tsx | 12 +- .../household/vacuum-icon.tsx | 12 +- .../household/washer-icon.tsx | 12 +- .../household/window-icon.tsx | 12 +- .../household/wrench-icon.tsx | 12 +- .../household/wrench-screwdiver-icon.tsx | 9 +- .../interface/alert-alt-icon.tsx | 12 +- .../interface/alert-circle-icon.tsx | 12 +- .../interface/alert-diamond-icon.tsx | 9 +- .../interface/alert-hexagon-icon.tsx | 9 +- .../interface/alert-octagon-icon.tsx | 9 +- .../interface/alert-square-icon.tsx | 12 +- .../interface/alert-triangle-icon.tsx | 9 +- .../interface/alien-icon.tsx | 12 +- .../interface/bell-check-icon.tsx | 12 +- .../interface/bell-exclamation-icon.tsx | 9 +- .../interface/bell-icon.tsx | 12 +- .../interface/bell-indicator-icon.tsx | 9 +- .../interface/bell-minus-icon.tsx | 12 +- .../interface/bell-off-icon.tsx | 12 +- .../interface/bell-plus-icon.tsx | 12 +- .../interface/bell-ringing-icon.tsx | 12 +- .../interface/bell-ringing-off-icon.tsx | 9 +- .../interface/bell-ringing-snooze-icon.tsx | 9 +- .../interface/bell-snooze-icon.tsx | 12 +- .../interface/bell-xmark-icon.tsx | 12 +- .../icons-v2-generated/interface/dot-icon.tsx | 12 +- .../interface/download-01-icon.tsx | 12 +- .../interface/download-02-icon.tsx | 12 +- .../interface/ellipsis-01-circle-icon.tsx | 9 +- .../interface/ellipsis-01-icon.tsx | 12 +- .../interface/ellipsis-01-square-icon.tsx | 9 +- .../interface/ellipsis-01-stroke-icon.tsx | 9 +- .../interface/ellipsis-02-circle-icon.tsx | 9 +- .../interface/ellipsis-02-icon.tsx | 12 +- .../interface/ellipsis-02-square-icon.tsx | 9 +- .../interface/ellipsis-02-stroke-icon.tsx | 9 +- .../interface/external-link-icon.tsx | 9 +- .../interface/eye-alt-icon.tsx | 12 +- .../interface/eye-alt-off-icon.tsx | 12 +- .../interface/eye-close-icon.tsx | 12 +- .../icons-v2-generated/interface/eye-icon.tsx | 12 +- .../interface/eye-off-icon.tsx | 12 +- .../interface/home-alt-icon.tsx | 12 +- .../interface/home-circle-icon.tsx | 12 +- .../interface/home-door-icon.tsx | 12 +- .../interface/home-heart-icon.tsx | 12 +- .../interface/home-icon.tsx | 12 +- .../interface/home-line-icon.tsx | 17 +- .../interface/home-smile-icon.tsx | 12 +- .../interface/home-star-icon.tsx | 12 +- .../interface/life-buoy-icon.tsx | 12 +- .../interface/link-01-break-icon.tsx | 12 +- .../interface/link-01-horizontal-icon.tsx | 14 +- .../interface/link-01-icon.tsx | 17 +- .../interface/link-02-horizontal-icon.tsx | 9 +- .../interface/link-02-icon.tsx | 12 +- .../interface/loading-01-icon.tsx | 12 +- .../interface/loading-02-icon.tsx | 12 +- .../interface/loading-03-icon.tsx | 12 +- .../interface/login-01-icon.tsx | 12 +- .../interface/login-02-icon.tsx | 12 +- .../interface/logout-01-icon.tsx | 12 +- .../interface/logout-02-icon.tsx | 12 +- .../interface/menu-01-circle-icon.tsx | 9 +- .../interface/menu-01-icon.tsx | 12 +- .../interface/menu-01-square-icon.tsx | 9 +- .../interface/menu-02-circle-icon.tsx | 9 +- .../interface/menu-02-icon.tsx | 12 +- .../interface/menu-02-square-icon.tsx | 9 +- .../interface/party-icon.tsx | 12 +- .../interface/plus-multiple-icon.tsx | 9 +- .../interface/radio-waves-icon.tsx | 12 +- .../interface/retweet-icon.tsx | 12 +- .../interface/robot-icon.tsx | 12 +- .../interface/search-icon.tsx | 17 +- .../interface/search-xmark-icon.tsx | 12 +- .../interface/settings-01-icon.tsx | 12 +- .../interface/settings-02-icon.tsx | 12 +- .../interface/settings-02-line-icon.tsx | 14 +- .../interface/share-icon.tsx | 12 +- .../interface/share-square-icon.tsx | 12 +- .../interface/translate-icon.tsx | 12 +- .../interface/translate-square-icon.tsx | 9 +- .../interface/trash-blank-icon.tsx | 12 +- .../interface/trash-check-icon.tsx | 12 +- .../interface/trash-clock-icon.tsx | 12 +- .../interface/trash-exclamation-icon.tsx | 9 +- .../interface/trash-icon.tsx | 12 +- .../interface/trash-xmark-icon.tsx | 12 +- .../interface/upload-01-icon.tsx | 12 +- .../interface/upload-02-icon.tsx | 12 +- .../interface/zoom-in-icon.tsx | 12 +- .../interface/zoom-out-icon.tsx | 12 +- .../map-and-travel/arrow-location-hr-icon.tsx | 9 +- .../map-and-travel/arrow-location-icon.tsx | 9 +- .../arrow-location-off-icon.tsx | 9 +- .../map-and-travel/beach-icon.tsx | 12 +- .../map-and-travel/briefcase-icon.tsx | 12 +- .../map-and-travel/briefcase-work-icon.tsx | 9 +- .../map-and-travel/compass-icon.tsx | 12 +- .../map-and-travel/crosshair-01-icon.tsx | 12 +- .../map-and-travel/crosshair-02-icon.tsx | 12 +- .../map-and-travel/crosshair-off-icon.tsx | 9 +- .../map-and-travel/flag-01-icon.tsx | 17 +- .../map-and-travel/flag-02-icon.tsx | 17 +- .../map-and-travel/flag-03-icon.tsx | 17 +- .../map-and-travel/helicopter-symbol-icon.tsx | 9 +- .../map-and-travel/island-icon.tsx | 12 +- .../map-and-travel/location-check-icon.tsx | 9 +- .../location-exclamation-icon.tsx | 9 +- .../map-and-travel/location-heart-icon.tsx | 9 +- .../map-and-travel/location-icon.tsx | 12 +- .../map-and-travel/location-minus-icon.tsx | 14 +- .../map-and-travel/location-off-icon.tsx | 12 +- .../map-and-travel/location-plus-icon.tsx | 9 +- .../map-and-travel/location-star-icon.tsx | 9 +- .../map-and-travel/location-xmark-icon.tsx | 9 +- .../map-and-travel/map-icon.tsx | 12 +- .../map-and-travel/map-location-icon.tsx | 12 +- .../map-and-travel/map-search-icon.tsx | 12 +- .../map-and-travel/map-square-icon.tsx | 12 +- .../map-and-travel/passport-icon.tsx | 12 +- .../map-and-travel/pin-location-01-icon.tsx | 9 +- .../map-and-travel/pin-location-02-icon.tsx | 9 +- .../map-and-travel/plane-arrival-icon.tsx | 9 +- .../map-and-travel/plane-departure-icon.tsx | 9 +- .../map-and-travel/pool-icon.tsx | 12 +- .../map-and-travel/post-sign-icon.tsx | 12 +- .../map-and-travel/post-signs-icon.tsx | 12 +- .../map-and-travel/radar-icon.tsx | 12 +- .../map-and-travel/route-arrow-icon.tsx | 12 +- .../map-and-travel/route-icon.tsx | 12 +- .../map-and-travel/route-location-icon.tsx | 9 +- .../map-and-travel/route-xmark-icon.tsx | 12 +- .../map-and-travel/spa-icon.tsx | 12 +- .../map-and-travel/street-view-icon.tsx | 12 +- .../map-and-travel/suitcase-icon.tsx | 12 +- .../map-and-travel/ticket-airline-icon.tsx | 9 +- .../map-and-travel/trolley-case-icon.tsx | 12 +- .../media-playback/backward-icon.tsx | 12 +- .../media-playback/collapse-01-icon.tsx | 12 +- .../media-playback/collapse-02-icon.tsx | 12 +- .../media-playback/collapse-03-icon.tsx | 12 +- .../media-playback/collapse-04-dots-icon.tsx | 9 +- .../media-playback/collapse-04-icon.tsx | 12 +- .../media-playback/expand-01-icon.tsx | 12 +- .../media-playback/expand-02-icon.tsx | 12 +- .../media-playback/expand-03-icon.tsx | 12 +- .../media-playback/expand-04-dots-icon.tsx | 9 +- .../media-playback/expand-04-icon.tsx | 12 +- .../media-playback/expand-square-icon.tsx | 9 +- .../media-playback/fast-backward-icon.tsx | 9 +- .../media-playback/fast-forward-icon.tsx | 12 +- .../media-playback/flip-01-left-icon.tsx | 12 +- .../media-playback/flip-01-right-icon.tsx | 12 +- .../media-playback/flip-02-left-icon.tsx | 12 +- .../media-playback/flip-02-right-icon.tsx | 12 +- .../media-playback/forward-icon.tsx | 12 +- .../media-playback/mic-alt-icon.tsx | 12 +- .../media-playback/mic-icon.tsx | 12 +- .../media-playback/mic-off-icon.tsx | 12 +- .../media-playback/next-icon.tsx | 12 +- .../media-playback/pause-circle-icon.tsx | 12 +- .../media-playback/pause-icon.tsx | 12 +- .../media-playback/play-circle-icon.tsx | 12 +- .../media-playback/play-icon.tsx | 12 +- .../media-playback/previous-icon.tsx | 12 +- .../media-playback/recording-circle-icon.tsx | 9 +- .../media-playback/recording-icon.tsx | 12 +- .../media-playback/refresh-01-left-icon.tsx | 9 +- .../media-playback/refresh-01-right-icon.tsx | 9 +- .../refresh-02-exclamation-icon.tsx | 9 +- .../media-playback/refresh-02-hr-icon.tsx | 12 +- .../media-playback/refresh-02-vr-icon.tsx | 12 +- .../media-playback/repeat-icon.tsx | 12 +- .../media-playback/repeat-one-icon.tsx | 12 +- .../media-playback/rotate-360-icon.tsx | 12 +- .../media-playback/shuffle-icon.tsx | 12 +- .../media-playback/stop-circle-icon.tsx | 12 +- .../media-playback/stop-icon.tsx | 12 +- .../media-playback/subtitle-icon.tsx | 12 +- .../media-playback/waveform-icon.tsx | 12 +- .../number/age-limit-0-icon.tsx | 12 +- .../number/age-limit-10-icon.tsx | 12 +- .../number/age-limit-12-icon.tsx | 12 +- .../number/age-limit-13-icon.tsx | 12 +- .../number/age-limit-16-icon.tsx | 12 +- .../number/age-limit-17-icon.tsx | 12 +- .../number/age-limit-18-icon.tsx | 12 +- .../number/age-limit-21-icon.tsx | 12 +- .../number/age-limit-3-icon.tsx | 12 +- .../number/age-limit-6-icon.tsx | 12 +- .../icons-v2-generated/number/dice-1-icon.tsx | 12 +- .../icons-v2-generated/number/dice-2-icon.tsx | 12 +- .../icons-v2-generated/number/dice-3-icon.tsx | 12 +- .../icons-v2-generated/number/dice-4-icon.tsx | 12 +- .../icons-v2-generated/number/dice-5-icon.tsx | 12 +- .../icons-v2-generated/number/dice-6-icon.tsx | 12 +- .../icons-v2-generated/number/index.ts | 4 +- .../number/number-0-icon.tsx | 12 +- .../number/number-1-icon.tsx | 12 +- .../number/number-2-icon.tsx | 12 +- .../number/number-3-icon.tsx | 12 +- .../number/number-4-icon.tsx | 12 +- .../number/number-5-icon.tsx | 12 +- .../number/number-6-icon.tsx | 12 +- .../number/number-7-icon.tsx | 12 +- .../number/number-8-icon.tsx | 12 +- .../number/number-9-icon.tsx | 12 +- .../number/numer-0-circle-icon.tsx | 9 +- .../number/numer-0-square-icon.tsx | 9 +- .../number/numer-1-circle-icon.tsx | 9 +- .../number/numer-1-square-icon.tsx | 9 +- .../number/numer-2-circle-icon.tsx | 9 +- .../number/numer-2-square-icon.tsx | 9 +- .../number/numer-3-circle-icon.tsx | 9 +- .../number/numer-3-square-icon.tsx | 9 +- .../number/numer-4-circle-icon.tsx | 9 +- .../number/numer-4-square-icon.tsx | 9 +- .../number/numer-5-circle-icon.tsx | 9 +- .../number/numer-5-square-icon.tsx | 9 +- .../number/numer-6-circle-icon.tsx | 9 +- .../number/numer-6-square-icon.tsx | 9 +- .../number/numer-7-circle-icon.tsx | 9 +- .../number/numer-7-square-icon.tsx | 9 +- .../number/numer-8-circle-icon.tsx | 9 +- .../number/numer-8-square-icon.tsx | 9 +- .../number/numer-9-circle-icon.tsx | 9 +- .../number/numer-9-square-icon.tsx | 9 +- .../icons-v2-generated/school/atom-icon.tsx | 12 +- .../school/backpack-icon.tsx | 12 +- .../school/book-alt-icon.tsx | 12 +- .../school/book-bookmark-icon.tsx | 9 +- .../school/book-heart-icon.tsx | 12 +- .../icons-v2-generated/school/book-icon.tsx | 12 +- .../school/book-open-icon.tsx | 12 +- .../school/book-text-icon.tsx | 12 +- .../school/bookmark-check-icon.tsx | 9 +- .../school/bookmark-icon.tsx | 12 +- .../school/bookmark-minus-icon.tsx | 14 +- .../school/bookmark-off-icon.tsx | 12 +- .../school/bookmark-plus-icon.tsx | 9 +- .../school/bookmark-xmark-icon.tsx | 9 +- .../school/bookmarks-icon.tsx | 12 +- .../icons-v2-generated/school/books-icon.tsx | 12 +- .../school/calculator-01-icon.tsx | 9 +- .../school/calculator-02-icon.tsx | 9 +- .../school/drawing-compass-icon.tsx | 9 +- .../icons-v2-generated/school/flask-icon.tsx | 12 +- .../school/flask-vial-icon.tsx | 12 +- .../school/globe-01-icon.tsx | 12 +- .../school/globe-02-icon.tsx | 12 +- .../school/globe-stand-icon.tsx | 12 +- .../school/graduation-cap-icon.tsx | 9 +- .../icons-v2-generated/school/honor-icon.tsx | 12 +- .../school/honor-star-icon.tsx | 12 +- .../icons-v2-generated/school/index.ts | 8 +- .../school/microscope-icon.tsx | 12 +- .../school/notebook-alt-icon.tsx | 12 +- .../school/notebook-icon.tsx | 12 +- .../school/piano-key-icon.tsx | 12 +- .../icons-v2-generated/school/planet-icon.tsx | 12 +- .../school/point-100-icon.tsx | 12 +- .../school/quote-left-icon.tsx | 12 +- .../school/quote-right-icon.tsx | 12 +- .../school/quote-square-icon.tsx | 12 +- .../icons-v2-generated/school/ruler-icon.tsx | 12 +- .../school/ruler-triangle-icon.tsx | 9 +- .../school/shape-circle-icon.tsx | 12 +- .../school/shape-diamond-square-icon.tsx | 9 +- .../school/shape-hexagon-icon.tsx | 9 +- .../school/shape-octagon-icon.tsx | 9 +- .../school/shape-square-icon.tsx | 12 +- .../school/shape-triangle-icon.tsx | 9 +- .../icons-v2-generated/school/shapes-icon.tsx | 12 +- .../school/stationery-icon.tsx | 12 +- .../school/telescope-icon.tsx | 12 +- .../icons-v2-generated/school/vial-icon.tsx | 12 +- .../security/audit-icon.tsx | 12 +- .../security/banned-icon.tsx | 12 +- .../security/fingerprint-icon.tsx | 12 +- .../icons-v2-generated/security/index.ts | 4 +- .../icons-v2-generated/security/key-icon.tsx | 12 +- .../security/key-skeleton-icon.tsx | 12 +- .../security/keyhole-circle-icon.tsx | 9 +- .../security/keyhole-square-icon.tsx | 9 +- .../security/lock-alt-icon.tsx | 12 +- .../security/lock-check-icon.tsx | 12 +- .../security/lock-circle-icon.tsx | 12 +- .../icons-v2-generated/security/lock-icon.tsx | 12 +- .../security/lock-keyhole-icon.tsx | 12 +- .../security/lock-keyhole-off-icon.tsx | 9 +- .../security/lock-open-icon.tsx | 12 +- .../security/lock-open-keyhole-icon.tsx | 9 +- .../security/lock-square-icon.tsx | 12 +- .../security/passcode-icon.tsx | 12 +- .../icons-v2-generated/security/scan-icon.tsx | 12 +- .../security/shield-alt-icon.tsx | 17 +- .../security/shield-check-icon.tsx | 12 +- .../security/shield-exclamation-icon.tsx | 9 +- .../security/shield-icon.tsx | 12 +- .../security/shield-keyhole-icon.tsx | 9 +- .../security/shield-lightning-icon.tsx | 9 +- .../security/shield-lock-icon.tsx | 12 +- .../security/shield-minus-icon.tsx | 17 +- .../security/shield-off-icon.tsx | 12 +- .../security/shield-plus-icon.tsx | 12 +- .../security/shield-user-icon.tsx | 12 +- .../security/shield-wifi-icon.tsx | 12 +- .../security/shield-xmark-icon.tsx | 12 +- .../security/signature-document-icon.tsx | 9 +- .../security/signature-icon.tsx | 17 +- .../security/unlock-icon.tsx | 12 +- .../security/unlock-keyhole-icon.tsx | 9 +- .../shopping/badge-dollar-icon.tsx | 12 +- .../shopping/badge-percent-icon.tsx | 9 +- .../shopping/bag-alt-icon.tsx | 12 +- .../shopping/bag-arrow-down-icon.tsx | 9 +- .../shopping/bag-arrow-up-icon.tsx | 12 +- .../shopping/bag-check-icon.tsx | 12 +- .../shopping/bag-exclamation-icon.tsx | 9 +- .../icons-v2-generated/shopping/bag-icon.tsx | 12 +- .../shopping/bag-minus-icon.tsx | 12 +- .../shopping/bag-plus-icon.tsx | 12 +- .../shopping/bag-xmark-icon.tsx | 12 +- .../shopping/basket-arrow-down-icon.tsx | 9 +- .../shopping/basket-arrow-up-icon.tsx | 9 +- .../shopping/basket-blank-icon.tsx | 12 +- .../shopping/basket-check-icon.tsx | 12 +- .../shopping/basket-exclamation-icon.tsx | 9 +- .../shopping/basket-icon.tsx | 12 +- .../shopping/basket-minus-icon.tsx | 12 +- .../shopping/basket-plus-icon.tsx | 12 +- .../shopping/basket-xmark-icon.tsx | 12 +- .../shopping/cart-arrow-down-icon.tsx | 9 +- .../shopping/cart-arrow-up-icon.tsx | 12 +- .../shopping/cart-check-icon.tsx | 12 +- .../shopping/cart-exclamation-icon.tsx | 9 +- .../icons-v2-generated/shopping/cart-icon.tsx | 12 +- .../shopping/cart-minus-icon.tsx | 12 +- .../shopping/cart-plus-icon.tsx | 12 +- .../shopping/cart-xmark-icon.tsx | 12 +- .../shopping/certified-01-icon.tsx | 12 +- .../shopping/certified-02-icon.tsx | 12 +- .../shopping/code-bar-icon.tsx | 12 +- .../shopping/code-bar-scan-icon.tsx | 12 +- .../shopping/code-qr-icon.tsx | 12 +- .../shopping/code-qr-scan-icon.tsx | 12 +- .../shopping/code-scan-icon.tsx | 12 +- .../shopping/crown-icon.tsx | 17 +- .../shopping/diamond-gem-icon.tsx | 12 +- .../shopping/feedback-icon.tsx | 12 +- .../icons-v2-generated/shopping/gift-icon.tsx | 12 +- .../shopping/giftcard-icon.tsx | 12 +- .../icons-v2-generated/shopping/index.ts | 2 +- .../shopping/megaphone-01-icon.tsx | 12 +- .../shopping/megaphone-02-icon.tsx | 12 +- .../shopping/monitor-chart-icon.tsx | 9 +- .../shopping/receipt-01-icon.tsx | 12 +- .../shopping/receipt-02-icon.tsx | 12 +- .../shopping/scale-balanced-icon.tsx | 9 +- .../shopping/scale-unbalanced-icon.tsx | 9 +- .../icons-v2-generated/shopping/shop-icon.tsx | 12 +- .../shopping/shop-location-icon.tsx | 9 +- .../icons-v2-generated/shopping/star-icon.tsx | 12 +- .../shopping/tag-dollar-icon.tsx | 12 +- .../icons-v2-generated/shopping/tag-icon.tsx | 12 +- .../shopping/tag-percent-icon.tsx | 12 +- .../icons-v2-generated/shopping/tags-icon.tsx | 12 +- .../shopping/thumbs-down-icon.tsx | 17 +- .../shopping/thumbs-up-icon.tsx | 17 +- .../shopping/ticket-icon.tsx | 12 +- .../shopping/voucher-dollar-icon.tsx | 9 +- .../shopping/voucher-percent-icon.tsx | 9 +- .../signs-and-symbols/asterisk-icon.tsx | 12 +- .../signs-and-symbols/bitcoin-circle-icon.tsx | 9 +- .../signs-and-symbols/bitcoin-icon.tsx | 12 +- .../signs-and-symbols/check-alt-icon.tsx | 12 +- .../signs-and-symbols/check-circle-icon.tsx | 12 +- .../signs-and-symbols/check-fill-icon.tsx | 12 +- .../signs-and-symbols/check-icon.tsx | 12 +- .../signs-and-symbols/check-square-icon.tsx | 12 +- .../signs-and-symbols/checks-icon.tsx | 12 +- .../signs-and-symbols/degree-celsius-icon.tsx | 9 +- .../degree-fahrenheit-icon.tsx | 9 +- .../signs-and-symbols/dollar-circle-icon.tsx | 9 +- .../signs-and-symbols/dollar-icon.tsx | 12 +- .../signs-and-symbols/euro-circle-icon.tsx | 12 +- .../signs-and-symbols/euro-icon.tsx | 12 +- .../signs-and-symbols/exclamation-icon.tsx | 12 +- .../signs-and-symbols/gender-female-icon.tsx | 9 +- .../signs-and-symbols/gender-male-icon.tsx | 12 +- .../signs-and-symbols/hash-icon.tsx | 12 +- .../signs-and-symbols/index.ts | 2 +- .../signs-and-symbols/info-circle-icon.tsx | 12 +- .../signs-and-symbols/info-icon.tsx | 12 +- .../signs-and-symbols/key-command-icon.tsx | 12 +- .../signs-and-symbols/key-option-icon.tsx | 12 +- .../signs-and-symbols/minus-alt-icon.tsx | 12 +- .../signs-and-symbols/minus-circle-icon.tsx | 17 +- .../signs-and-symbols/minus-icon.tsx | 17 +- .../signs-and-symbols/minus-square-icon.tsx | 17 +- .../signs-and-symbols/percent-circle-icon.tsx | 9 +- .../signs-and-symbols/percent-icon.tsx | 12 +- .../signs-and-symbols/percent-square-icon.tsx | 9 +- .../signs-and-symbols/percent-stroke-icon.tsx | 9 +- .../signs-and-symbols/plus-alt-icon.tsx | 12 +- .../signs-and-symbols/plus-circle-icon.tsx | 12 +- .../signs-and-symbols/plus-icon.tsx | 12 +- .../signs-and-symbols/plus-square-icon.tsx | 12 +- .../signs-and-symbols/pound-circle-icon.tsx | 12 +- .../signs-and-symbols/pound-icon.tsx | 17 +- .../signs-and-symbols/question-alt-icon.tsx | 12 +- .../question-circle-icon.tsx | 9 +- .../signs-and-symbols/question-icon.tsx | 12 +- .../question-square-icon.tsx | 9 +- .../signs-and-symbols/recycle-icon.tsx | 12 +- .../signs-and-symbols/sign-female-icon.tsx | 12 +- .../signs-and-symbols/sign-male-icon.tsx | 12 +- .../signs-and-symbols/smoking-icon.tsx | 12 +- .../smoking-off-circle-icon.tsx | 9 +- .../signs-and-symbols/smoking-off-icon.tsx | 12 +- .../signs-and-symbols/turn-left-sign-icon.tsx | 9 +- .../turn-right-sign-icon.tsx | 9 +- .../signs-and-symbols/xmark-alt-icon.tsx | 12 +- .../signs-and-symbols/xmark-circle-icon.tsx | 12 +- .../signs-and-symbols/xmark-icon.tsx | 12 +- .../signs-and-symbols/xmark-square-icon.tsx | 12 +- .../signs-and-symbols/yin-yang-icon.tsx | 12 +- .../sort-and-filter/filter-01-check-icon.tsx | 9 +- .../sort-and-filter/filter-01-clock-icon.tsx | 9 +- .../sort-and-filter/filter-01-icon.tsx | 12 +- .../sort-and-filter/filter-01-list-icon.tsx | 9 +- .../sort-and-filter/filter-02-icon.tsx | 12 +- .../sort-and-filter/filter-03-hr-icon.tsx | 12 +- .../sort-and-filter/filter-03-vr-icon.tsx | 12 +- .../sort-and-filter/filter-04-hr-icon.tsx | 12 +- .../sort-and-filter/filter-04-vr-icon.tsx | 12 +- .../sort-and-filter/slider-right-icon.tsx | 12 +- .../sort-and-filter/sliders-icon.tsx | 12 +- .../sort-and-filter/sort-19-down-icon.tsx | 12 +- .../sort-and-filter/sort-19-up-icon.tsx | 12 +- .../sort-and-filter/sort-91-down-icon.tsx | 12 +- .../sort-and-filter/sort-91-up-icon.tsx | 12 +- .../sort-and-filter/sort-az-down-icon.tsx | 12 +- .../sort-and-filter/sort-az-up-icon.tsx | 12 +- .../sort-and-filter/sort-item-down-icon.tsx | 9 +- .../sort-and-filter/sort-item-up-icon.tsx | 12 +- .../sort-and-filter/sort-za-down-icon.tsx | 12 +- .../sort-and-filter/sort-za-up-icon.tsx | 12 +- .../sort-and-filter/toggle-alt-right-icon.tsx | 9 +- .../sort-and-filter/toggle-right-icon.tsx | 12 +- .../sort-and-filter/toggles-icon.tsx | 12 +- .../sport/american-football-icon.tsx | 9 +- .../sport/archery-arrow-icon.tsx | 9 +- .../sport/archery-bow-icon.tsx | 12 +- .../sport/archery-target-icon.tsx | 9 +- .../sport/baseball-icon.tsx | 12 +- .../sport/basketball-basket-icon.tsx | 9 +- .../sport/basketball-icon.tsx | 12 +- .../icons-v2-generated/sport/biking-icon.tsx | 12 +- .../icons-v2-generated/sport/bowling-icon.tsx | 12 +- .../icons-v2-generated/sport/boxing-icon.tsx | 12 +- .../icons-v2-generated/sport/chess-icon.tsx | 17 +- .../icons-v2-generated/sport/court-icon.tsx | 12 +- .../icons-v2-generated/sport/dumbell-icon.tsx | 12 +- .../sport/football-icon.tsx | 12 +- .../sport/medal-01-icon.tsx | 12 +- .../sport/medal-02-icon.tsx | 12 +- .../sport/pingpong-icon.tsx | 12 +- .../icons-v2-generated/sport/ranking-icon.tsx | 12 +- .../sport/reward-badge-check-icon.tsx | 9 +- .../sport/reward-badge-icon.tsx | 12 +- .../sport/reward-badge-star-icon.tsx | 9 +- .../icons-v2-generated/sport/running-icon.tsx | 12 +- .../sport/skipping-rope-icon.tsx | 9 +- .../icons-v2-generated/sport/snorkel-icon.tsx | 12 +- .../sport/swimming-icon.tsx | 12 +- .../icons-v2-generated/sport/trophy-icon.tsx | 12 +- .../sport/volleyball-icon.tsx | 12 +- .../icons-v2-generated/sport/whistle-icon.tsx | 12 +- .../text-editor/block-quote-icon.tsx | 12 +- .../text-editor/bold-alt-icon.tsx | 12 +- .../text-editor/bold-icon.tsx | 12 +- .../text-editor/font-alt-icon.tsx | 12 +- .../text-editor/font-icon.tsx | 12 +- .../text-editor/grid-dots-blank-icon.tsx | 9 +- .../text-editor/grid-dots-bottom-icon.tsx | 9 +- .../text-editor/grid-dots-center-hr-icon.tsx | 9 +- .../text-editor/grid-dots-center-icon.tsx | 9 +- .../text-editor/grid-dots-center-vr-icon.tsx | 9 +- .../text-editor/grid-dots-left-icon.tsx | 9 +- .../text-editor/grid-dots-outer-icon.tsx | 9 +- .../text-editor/grid-dots-right-icon.tsx | 9 +- .../text-editor/grid-dots-top-icon.tsx | 12 +- .../text-editor/heading-alt-icon.tsx | 12 +- .../text-editor/heading-h1-icon.tsx | 12 +- .../text-editor/heading-h2-icon.tsx | 12 +- .../text-editor/heading-h3-icon.tsx | 12 +- .../text-editor/heading-h4-icon.tsx | 12 +- .../text-editor/heading-h5-icon.tsx | 12 +- .../text-editor/heading-h6-icon.tsx | 12 +- .../text-editor/heading-icon.tsx | 12 +- .../text-editor/i-cursor-icon.tsx | 17 +- .../text-editor/indent-icon.tsx | 12 +- .../text-editor/italic-icon.tsx | 12 +- .../text-editor/line-height-icon.tsx | 12 +- .../text-editor/list-bullet-icon.tsx | 12 +- .../text-editor/list-check-icon.tsx | 12 +- .../text-editor/list-number-icon.tsx | 12 +- .../text-editor/list-tree-icon.tsx | 12 +- .../text-editor/paragraph-icon.tsx | 12 +- .../text-editor/spacing-hr-icon.tsx | 12 +- .../text-editor/spacing-vr-icon.tsx | 12 +- .../text-editor/strike-through-icon.tsx | 14 +- .../text-editor/text-align-center-icon.tsx | 9 +- .../text-editor/text-align-justify-icon.tsx | 9 +- .../text-editor/text-align-left-icon.tsx | 9 +- .../text-editor/text-align-right-icon.tsx | 9 +- .../text-editor/text-area-dragger-icon.tsx | 9 +- .../text-editor/underline-icon.tsx | 12 +- .../users/address-book-icon.tsx | 12 +- .../users/avatar-circle-icon.tsx | 9 +- .../users/avatar-square-icon.tsx | 9 +- .../users/book-user-icon.tsx | 12 +- .../users/face-angry-icon.tsx | 12 +- .../users/face-baby-icon.tsx | 12 +- .../users/face-laugh-icon.tsx | 12 +- .../users/face-meh-01-icon.tsx | 12 +- .../users/face-meh-02-icon.tsx | 12 +- .../users/face-sad-01-icon.tsx | 12 +- .../users/face-sad-02-icon.tsx | 12 +- .../users/face-sleeping-icon.tsx | 9 +- .../users/face-smile-01-icon.tsx | 12 +- .../users/face-smile-02-icon.tsx | 12 +- .../users/face-surprise-icon.tsx | 9 +- .../users/face-wink-icon.tsx | 12 +- .../users/face-worry-icon.tsx | 12 +- .../users/head-heart-icon.tsx | 12 +- .../users/head-settings-icon.tsx | 9 +- .../users/house-user-icon.tsx | 12 +- .../icons-v2-generated/users/id-card-icon.tsx | 12 +- .../icons-v2-generated/users/index.ts | 2 +- .../users/user-banned-icon.tsx | 12 +- .../users/user-check-icon.tsx | 12 +- .../users/user-edit-icon.tsx | 12 +- .../icons-v2-generated/users/user-icon.tsx | 12 +- .../users/user-lock-icon.tsx | 12 +- .../users/user-minus-icon.tsx | 12 +- .../users/user-plus-icon.tsx | 12 +- .../users/user-search-icon.tsx | 12 +- .../users/user-settings-icon.tsx | 9 +- .../users/user-xmark-icon.tsx | 12 +- .../users/users-group-icon.tsx | 12 +- .../icons-v2-generated/users/users-icon.tsx | 12 +- .../users/users-plus-icon.tsx | 12 +- .../vehicles-and-delivery/anchor-icon.tsx | 12 +- .../baby-carriage-icon.tsx | 9 +- .../vehicles-and-delivery/bike-icon.tsx | 12 +- .../vehicles-and-delivery/bus-icon.tsx | 12 +- .../vehicles-and-delivery/cable-car-icon.tsx | 12 +- .../car-battery-01-icon.tsx | 9 +- .../car-battery-02-icon.tsx | 9 +- .../vehicles-and-delivery/car-icon.tsx | 12 +- .../car-side-electric-icon.tsx | 9 +- .../vehicles-and-delivery/car-side-icon.tsx | 12 +- .../vehicles-and-delivery/car-taxi-icon.tsx | 12 +- .../vehicles-and-delivery/ferry-icon.tsx | 12 +- .../vehicles-and-delivery/gauge-01-icon.tsx | 12 +- .../vehicles-and-delivery/gauge-02-icon.tsx | 12 +- .../hand-parcel-icon.tsx | 12 +- .../vehicles-and-delivery/helicopter-icon.tsx | 12 +- .../vehicles-and-delivery/helmet-icon.tsx | 12 +- .../vehicles-and-delivery/motorbike-icon.tsx | 12 +- .../parcel-01-check-icon.tsx | 9 +- .../parcel-01-delay-icon.tsx | 9 +- .../parcel-01-fast-icon.tsx | 9 +- .../parcel-01-heart-icon.tsx | 9 +- .../vehicles-and-delivery/parcel-01-icon.tsx | 12 +- .../parcel-01-label-icon.tsx | 9 +- .../parcel-01-lock-icon.tsx | 9 +- .../parcel-01-search-icon.tsx | 9 +- .../parcel-01-star-icon.tsx | 9 +- .../parcel-02-check-icon.tsx | 9 +- .../parcel-02-fast-icon.tsx | 9 +- .../vehicles-and-delivery/parcel-02-icon.tsx | 12 +- .../parcel-02-lock-icon.tsx | 9 +- .../parcel-02-search-icon.tsx | 9 +- .../vehicles-and-delivery/parcel-03-icon.tsx | 12 +- .../vehicles-and-delivery/parcels-icon.tsx | 12 +- .../vehicles-and-delivery/plane-alt-icon.tsx | 12 +- .../vehicles-and-delivery/plane-icon.tsx | 12 +- .../vehicles-and-delivery/plane-off-icon.tsx | 12 +- .../plane-vertical-icon.tsx | 9 +- .../plane-vertical-off-icon.tsx | 9 +- .../pump-electric-icon.tsx | 9 +- .../vehicles-and-delivery/pump-gas-icon.tsx | 12 +- .../road-barrier-icon.tsx | 12 +- .../vehicles-and-delivery/road-icon.tsx | 12 +- .../vehicles-and-delivery/rocket-01-icon.tsx | 12 +- .../vehicles-and-delivery/rocket-02-icon.tsx | 12 +- .../vehicles-and-delivery/rv-icon.tsx | 12 +- .../vehicles-and-delivery/sailboat-icon.tsx | 12 +- .../speed-train-icon.tsx | 12 +- .../steering-wheel-icon.tsx | 9 +- .../traffic-cone-icon.tsx | 12 +- .../traffic-signal-icon.tsx | 9 +- .../vehicles-and-delivery/train-icon.tsx | 12 +- .../train-tunnel-icon.tsx | 12 +- .../vehicles-and-delivery/tram-icon.tsx | 12 +- .../vehicles-and-delivery/trolley-icon.tsx | 12 +- .../truck-delay-icon.tsx | 12 +- .../vehicles-and-delivery/truck-fast-icon.tsx | 12 +- .../vehicles-and-delivery/truck-icon.tsx | 12 +- .../truck-medical-icon.tsx | 9 +- .../weather-and-nature/aquarium-icon.tsx | 12 +- .../cloud-weather-bolt-icon.tsx | 9 +- .../cloud-weather-drizzle-icon.tsx | 9 +- .../cloud-weather-fog-icon.tsx | 9 +- .../weather-and-nature/cloud-weather-icon.tsx | 9 +- .../cloud-weather-moon-icon.tsx | 9 +- .../cloud-weather-rain-01-icon.tsx | 9 +- .../cloud-weather-rain-02-icon.tsx | 9 +- .../cloud-weather-snow-01-icon.tsx | 9 +- .../cloud-weather-snow-02-icon.tsx | 9 +- .../cloud-weather-sun-bright-icon.tsx | 9 +- .../cloud-weather-sun-icon.tsx | 9 +- .../weather-and-nature/dog-icon.tsx | 12 +- .../weather-and-nature/dog-leash-icon.tsx | 12 +- .../weather-and-nature/drowning-icon.tsx | 12 +- .../weather-and-nature/explosion-icon.tsx | 12 +- .../weather-and-nature/fire-icon.tsx | 12 +- .../weather-and-nature/flood-icon.tsx | 12 +- .../weather-and-nature/flower-01-icon.tsx | 12 +- .../weather-and-nature/flower-02-icon.tsx | 12 +- .../weather-and-nature/flower-plant-icon.tsx | 12 +- .../weather-and-nature/flower-sakura-icon.tsx | 9 +- .../weather-and-nature/flower-tulip-icon.tsx | 12 +- .../weather-and-nature/hand-seedling-icon.tsx | 9 +- .../weather-and-nature/heat-icon.tsx | 12 +- .../hill-avalanche-icon.tsx | 9 +- .../hill-rockslide-icon.tsx | 9 +- .../weather-and-nature/hurricane-icon.tsx | 12 +- .../weather-and-nature/leaf-icon.tsx | 12 +- .../weather-and-nature/leaves-icon.tsx | 12 +- .../weather-and-nature/moon-icon.tsx | 12 +- .../weather-and-nature/moon-star-icon.tsx | 12 +- .../weather-and-nature/paw-icon.tsx | 12 +- .../radiation-circle-icon.tsx | 9 +- .../weather-and-nature/radiation-icon.tsx | 12 +- .../weather-and-nature/seedling-icon.tsx | 12 +- .../weather-and-nature/seedling-pot-icon.tsx | 12 +- .../weather-and-nature/snowflake-icon.tsx | 12 +- .../sparkle-01-blink-icon.tsx | 9 +- .../weather-and-nature/sparkle-01-icon.tsx | 12 +- .../sparkle-02-blink-icon.tsx | 9 +- .../weather-and-nature/sparkle-02-icon.tsx | 12 +- .../weather-and-nature/stars-icon.tsx | 12 +- .../weather-and-nature/sun-01-icon.tsx | 12 +- .../weather-and-nature/sun-02-icon.tsx | 12 +- .../weather-and-nature/sunrise-icon.tsx | 12 +- .../weather-and-nature/sunrise-sea-icon.tsx | 12 +- .../weather-and-nature/sunset-icon.tsx | 12 +- .../temperature-cold-icon.tsx | 9 +- .../temperature-hot-icon.tsx | 9 +- .../weather-and-nature/temperature-icon.tsx | 12 +- .../weather-and-nature/tornado-icon.tsx | 12 +- .../weather-and-nature/tree-icon.tsx | 17 +- .../weather-and-nature/trees-icon.tsx | 12 +- .../weather-and-nature/ufo-icon.tsx | 12 +- .../weather-and-nature/water-droplet-icon.tsx | 9 +- .../water-droplet-off-icon.tsx | 9 +- .../weather-and-nature/water-wave-icon.tsx | 12 +- .../weather-and-nature/wind-icon.tsx | 12 +- .../src/components/icons/about-icon.tsx | 9 +- .../src/components/icons/ai-robot-icon.tsx | 33 +- .../components/icons/alert-triangle-icon.tsx | 26 +- .../src/components/icons/archive-icon.tsx | 8 +- .../icons/automate-everything-icon.tsx | 17 +- .../src/components/icons/bash-icon.tsx | 29 +- .../src/components/icons/bot-icon.tsx | 8 +- .../src/components/icons/buildings-icon.tsx | 24 +- .../src/components/icons/capterra-icon.tsx | 9 +- .../components/icons/check-circle-icon.tsx | 26 +- .../src/components/icons/claude-icon.tsx | 13 +- .../src/components/icons/cmd-icon.tsx | 26 +- .../src/components/icons/coins-icon.tsx | 27 +- .../components/icons/community-hub-icon.tsx | 26 +- .../src/components/icons/community-icon.tsx | 26 +- .../src/components/icons/compare-icon.tsx | 26 +- .../src/components/icons/copy-icon.tsx | 17 +- .../src/components/icons/cpu-icon.tsx | 82 +- .../icons/custom-external-link-icon.tsx | 36 +- .../src/components/icons/custom-fork-icon.tsx | 36 +- .../components/icons/custom-license-icon.tsx | 36 +- .../src/components/icons/custom-star-icon.tsx | 30 +- .../src/components/icons/custom-time-icon.tsx | 36 +- .../icons/cut-vendor-costs-icon.tsx | 12 +- .../src/components/icons/dashboard-icon.tsx | 22 +- .../src/components/icons/deno-icon.tsx | 23 +- .../icons/device-type-icons/desktop-icon.tsx | 14 +- .../get-device-type-icon.tsx | 53 +- .../icons/device-type-icons/index.ts | 4 +- .../icons/device-type-icons/laptop-icon.tsx | 15 +- .../icons/device-type-icons/server-icon.tsx | 14 +- .../src/components/icons/devices-icon.tsx | 10 +- .../src/components/icons/document-icon.tsx | 14 +- .../src/components/icons/donut-icon.tsx | 2 +- .../components/icons/double-chevron-icon.tsx | 56 +- .../components/icons/edit-profile-icon.tsx | 17 +- .../src/components/icons/elestio-logo.tsx | 72 +- .../components/icons/empty-vendor-icon.tsx | 40 +- .../src/components/icons/error-icon.tsx | 2 +- .../icons/explore-categories-icon.tsx | 23 +- .../src/components/icons/eye-icon.tsx | 38 +- .../src/components/icons/facebook-icon.tsx | 6 +- .../src/components/icons/figma-icon.tsx | 15 +- .../src/components/icons/file-check-icon.tsx | 26 +- .../src/components/icons/file-text-icon.tsx | 42 +- .../src/components/icons/filter-icon.tsx | 18 +- .../src/components/icons/flamingo-logo.tsx | 22 +- .../components/icons/folder-shield-icon.tsx | 22 +- .../src/components/icons/g2-icon.tsx | 16 +- .../src/components/icons/getapp-icon.tsx | 47 +- .../src/components/icons/github-icon.tsx | 24 +- .../src/components/icons/globe-icon.tsx | 6 +- .../components/icons/google-gemini-icon.tsx | 208 +++- .../src/components/icons/google-logo.tsx | 2 +- .../src/components/icons/grid-view-icon.tsx | 19 +- .../src/components/icons/hamburger-icon.tsx | 20 +- .../src/components/icons/hand-dollar-icon.tsx | 10 +- .../src/components/icons/hotel-icon.tsx | 26 +- .../src/components/icons/hubspot-icon.tsx | 2 +- .../src/components/icons/icon-utils.tsx | 48 +- .../src/components/icons/image-icon.tsx | 17 +- .../src/components/icons/index.ts | 296 +++-- .../src/components/icons/info-circle-icon.tsx | 33 +- .../src/components/icons/instagram-icon.tsx | 6 +- .../src/components/icons/jump-in-icon.tsx | 14 +- .../src/components/icons/linkedin-icon.tsx | 16 +- .../src/components/icons/linux-icon.tsx | 21 +- .../src/components/icons/log-out-icon.tsx | 25 +- .../src/components/icons/logs-icon.tsx | 34 +- .../src/components/icons/luma-icon.tsx | 5 +- .../src/components/icons/macos-icon.tsx | 16 +- .../components/icons/margin-crisis-icon.tsx | 32 +- .../src/components/icons/menu-icon.tsx | 18 +- .../components/icons/message-circle-icon.tsx | 34 +- .../icons/miami-cyber-gang-logo-face-only.tsx | 34 +- .../icons/miami-cyber-gang-logo.tsx | 150 ++- .../src/components/icons/mingo-icon.tsx | 18 +- .../components/icons/minus-circle-icon.tsx | 26 +- .../src/components/icons/mlg-logo.tsx | 19 +- .../src/components/icons/moon-icon.tsx | 24 +- .../src/components/icons/ms-icon.tsx | 28 +- .../src/components/icons/network-icon.tsx | 8 +- .../src/components/icons/nushell-icon.tsx | 23 +- .../src/components/icons/open-source-icon.tsx | 26 +- .../src/components/icons/openai-icon.tsx | 23 +- .../components/icons/openframe-ai-icons.tsx | 87 +- .../src/components/icons/openframe-logo.tsx | 19 +- .../src/components/icons/openframe-text.tsx | 63 +- .../src/components/icons/openmsp-logo.tsx | 21 +- .../components/icons/organizations-icon.tsx | 14 +- .../src/components/icons/package-icon.tsx | 10 +- .../src/components/icons/plus-circle-icon.tsx | 32 +- .../src/components/icons/policies-icon.tsx | 14 +- .../src/components/icons/powershell-icon.tsx | 24 +- .../src/components/icons/python-icon.tsx | 28 +- .../src/components/icons/queries-icon.tsx | 14 +- .../components/icons/reclaim-profits-icon.tsx | 12 +- .../src/components/icons/reddit-icon.tsx | 32 +- .../components/icons/remote-control-icon.tsx | 17 +- .../src/components/icons/script-icon.tsx | 19 +- .../src/components/icons/search-icon.tsx | 7 +- .../src/components/icons/send-icon.tsx | 22 +- .../src/components/icons/settings-icon.tsx | 22 +- .../icons/shape-circle-dash-icon.tsx | 16 +- .../src/components/icons/shell-icon.tsx | 27 +- .../components/icons/shield-check-icon.tsx | 16 +- .../src/components/icons/shield-icon.tsx | 18 +- .../src/components/icons/shield-key-icon.tsx | 22 +- .../src/components/icons/shield-lock-icon.tsx | 21 +- .../src/components/icons/slack-icon.tsx | 64 +- .../src/components/icons/sliders-icon.tsx | 18 +- .../src/components/icons/sparkles-icon.tsx | 26 +- .../icons/sso-configuration-icon.tsx | 24 +- .../src/components/icons/sun-icon.tsx | 29 +- .../src/components/icons/table-view-icon.tsx | 19 +- .../src/components/icons/telegram-icon.tsx | 6 +- .../src/components/icons/thumbs-down-icon.tsx | 12 +- .../src/components/icons/thumbs-up-icon.tsx | 12 +- .../components/icons/tool-directory-icons.tsx | 32 +- .../src/components/icons/trustpilot-icon.tsx | 13 +- .../src/components/icons/user-icon.tsx | 38 +- .../src/components/icons/users-group-icon.tsx | 8 +- .../src/components/icons/users-icon.tsx | 8 +- .../icons/vendor-directory-icon.tsx | 26 +- .../icons/vendor-showcase-icons.tsx | 52 +- .../src/components/icons/vendors-icon.tsx | 26 +- .../src/components/icons/whatsapp-icon.tsx | 8 +- .../src/components/icons/windows-icon.tsx | 21 +- .../src/components/icons/x-icon.tsx | 24 +- .../src/components/icons/x-logo.tsx | 12 +- .../src/components/icons/youtube-icon.tsx | 6 +- .../src/components/image-cropper.tsx | 192 ++- .../src/components/index.ts | 120 +- .../src/components/input.tsx | 30 +- .../src/components/interactive-wrapper.tsx | 312 +++-- .../components/join-waitlist-button-stub.tsx | 6 +- .../src/components/label.tsx | 20 +- .../components/layout/list-page-layout.tsx | 80 +- .../src/components/layout/page-container.tsx | 220 ++-- .../src/components/loading/card-skeleton.tsx | 109 +- .../loading/category-card-skeleton.tsx | 19 +- .../category-vendor-selector-skeleton.tsx | 14 +- .../components/loading/content-skeleton.tsx | 185 ++- .../loading/device-card-skeleton.tsx | 30 +- .../src/components/loading/index.ts | 114 +- .../loading/margin-report-skeleton.tsx | 18 +- .../loading/msp-profile-form-skeleton.tsx | 28 +- .../components/loading/openmsp-heartbeat.tsx | 39 +- .../loading/organization-card-skeleton.tsx | 45 +- .../loading/organization-icon-skeleton.tsx | 34 +- .../loading/page-layout-skeleton.tsx | 193 ++- .../components/loading/unified-skeleton.tsx | 204 ++-- .../loading/users-grid-skeleton.tsx | 21 +- .../loading/wizard-layout-skeleton.tsx | 14 +- .../src/components/log-severity-dot.tsx | 66 +- .../src/components/logs-list.tsx | 219 ++-- .../src/components/made-with-love.tsx | 37 +- .../src/components/media-carousel.tsx | 251 ++-- .../src/components/menu-icon.tsx | 18 +- .../src/components/menubar.tsx | 167 ++- .../src/components/metric-value.tsx | 17 +- .../src/components/minus-circle-icon.tsx | 26 +- .../src/components/moon-icon.tsx | 23 +- .../src/components/ms-icon.tsx | 28 +- .../src/components/msp-display.tsx | 14 +- .../src/components/navigation-menu.tsx | 74 +- .../src/components/navigation/app-header.tsx | 139 ++- .../src/components/navigation/app-layout.tsx | 54 +- .../navigation/client-only-header.tsx | 8 +- .../components/navigation/header-button.tsx | 35 +- .../navigation/header-global-search.tsx | 60 +- .../navigation/header-organization-filter.tsx | 55 +- .../components/navigation/header-skeleton.tsx | 50 +- .../src/components/navigation/header.tsx | 319 +++-- .../src/components/navigation/index.ts | 80 +- .../navigation/mobile-burger-menu.tsx | 187 ++- .../navigation/mobile-nav-panel.tsx | 101 +- .../navigation/navigation-sidebar.tsx | 430 +++---- .../components/navigation/sliding-sidebar.tsx | 241 ++-- .../navigation/sticky-section-nav.tsx | 152 +-- .../src/components/open-source-features.tsx | 48 +- .../src/components/open-source-icon.tsx | 28 +- .../src/components/openframe-logo.tsx | 19 +- .../src/components/openmsp-logo.tsx | 21 +- .../src/components/pagination.tsx | 117 +- .../components/persistent-filter-controls.tsx | 113 +- .../src/components/persistent-pagination.tsx | 143 +-- .../src/components/platform/CveLink.tsx | 25 +- .../components/platform/ScriptArguments.tsx | 99 +- .../components/platform/ScriptInfoSection.tsx | 86 +- .../components/platform/ShellTypeBadge.tsx | 70 +- .../src/components/platform/SoftwareInfo.tsx | 33 +- .../platform/SoftwareSourceBadge.tsx | 44 +- .../src/components/platform/ToolBadge.tsx | 31 +- .../src/components/platform/index.ts | 41 +- .../src/components/plus-circle-icon.tsx | 32 +- .../src/components/popover.tsx | 24 +- .../src/components/pricing-display.tsx | 198 +-- .../profile/ProfileLoadingSkeleton.tsx | 12 +- .../src/components/progress.tsx | 20 +- .../src/components/provider-button.tsx | 29 +- .../providers/dynamic-theme-provider.tsx | 12 +- .../src/components/radio-group.tsx | 34 +- .../src/components/reddit-icon.tsx | 32 +- .../src/components/resizable.tsx | 32 +- .../src/components/results-count.tsx | 40 +- .../src/components/scroll-area.tsx | 49 +- .../src/components/select.tsx | 93 +- .../src/components/selection-source-badge.tsx | 8 +- .../src/components/send-icon.tsx | 22 +- .../src/components/separator.tsx | 18 +- .../shared/detail-page-skeleton.tsx | 29 +- .../src/components/shared/onboarding/index.ts | 12 +- .../onboarding/onboarding-step-card.tsx | 92 +- .../onboarding/onboarding-walkthrough.tsx | 187 ++- .../shared/product-release/index.ts | 16 +- .../product-release-card-skeleton.tsx | 12 +- .../product-release/product-release-card.tsx | 42 +- .../product-release/release-detail-page.tsx | 146 +-- .../release-detail-skeleton.tsx | 2 +- .../src/components/sidebar.tsx | 1073 ++++++++-------- .../src/components/skeleton.tsx | 8 +- .../src/components/slack-icon.tsx | 44 +- .../src/components/slider.tsx | 19 +- .../src/components/smooth-accordion.tsx | 68 +- .../src/components/social-icon-row.tsx | 62 +- .../src/components/sonner.tsx | 26 +- .../src/components/square-avatar.tsx | 13 +- .../src/components/steps.tsx | 56 +- .../src/components/sun-icon.tsx | 28 +- .../src/components/switch.tsx | 18 +- .../src/components/table.tsx | 54 +- .../src/components/tabs.tsx | 36 +- .../src/components/test/ods-current-test.tsx | 53 +- .../src/components/textarea.tsx | 18 +- .../src/components/toast/index.ts | 5 +- .../src/components/toggle-group.tsx | 51 +- .../src/components/toggle.tsx | 46 +- .../src/components/tool-icon.tsx | 86 +- .../src/components/ui/accordion.tsx | 32 +- .../src/components/ui/actions-menu.tsx | 294 +++-- .../src/components/ui/alert-dialog.tsx | 109 +- .../src/components/ui/alert.tsx | 68 +- .../components/ui/allowed-domains-input.tsx | 122 +- .../src/components/ui/aspect-ratio.tsx | 8 +- .../src/components/ui/autocomplete.tsx | 447 ++++--- .../src/components/ui/badge.tsx | 28 +- .../src/components/ui/benefit-card.tsx | 145 +-- .../components/ui/brand-association-card.tsx | 24 +- .../components/ui/brand-association-grid.tsx | 22 +- .../src/components/ui/breadcrumb.tsx | 133 +- .../src/components/ui/bullet-list.tsx | 14 +- .../src/components/ui/button.tsx | 296 +++-- .../src/components/ui/card.tsx | 114 +- .../src/components/ui/checkbox-block.tsx | 140 +-- .../ui/checkbox-with-description.tsx | 100 +- .../src/components/ui/checkbox.tsx | 24 +- .../src/components/ui/chevron-button.tsx | 30 +- .../src/components/ui/circular-progress.tsx | 51 +- .../src/components/ui/content-loader.tsx | 70 +- .../src/components/ui/cursor-pagination.tsx | 131 +- .../src/components/ui/custom-icons.tsx | 64 +- .../src/components/ui/dashboard-info-card.tsx | 74 +- .../src/components/ui/date-picker.tsx | 413 +++---- .../src/components/ui/device-card-compact.tsx | 31 +- .../src/components/ui/device-card.tsx | 150 +-- .../src/components/ui/dialog.tsx | 56 +- .../src/components/ui/drawer.tsx | 176 ++- .../src/components/ui/dropdown-menu.tsx | 121 +- .../src/components/ui/error-state.tsx | 113 +- .../src/components/ui/feature-card.tsx | 28 +- .../src/components/ui/field-wrapper.tsx | 28 +- .../components/ui/file-manager/file-icon.tsx | 67 +- .../file-manager/file-manager-action-bar.tsx | 20 +- .../file-manager/file-manager-breadcrumb.tsx | 31 +- .../file-manager-context-menu.tsx | 56 +- .../ui/file-manager/file-manager-empty.tsx | 45 +- .../ui/file-manager/file-manager-skeleton.tsx | 63 +- .../file-manager/file-manager-table-row.tsx | 108 +- .../ui/file-manager/file-manager-table.tsx | 170 ++- .../ui/file-manager/file-manager.tsx | 153 ++- .../src/components/ui/file-manager/index.ts | 34 +- .../src/components/ui/file-manager/types.ts | 199 ++- .../src/components/ui/floating-tooltip.tsx | 173 ++- .../src/components/ui/highlight-card.tsx | 39 +- .../src/components/ui/icons-block.tsx | 35 +- .../src/components/ui/image-gallery-modal.tsx | 24 +- .../src/components/ui/index.ts | 232 ++-- .../src/components/ui/info-card.tsx | 61 +- .../src/components/ui/info-row.tsx | 16 +- .../src/components/ui/input.tsx | 61 +- .../src/components/ui/interactive-card.tsx | 100 +- .../src/components/ui/label.tsx | 63 +- .../src/components/ui/media-type-selector.tsx | 58 +- .../src/components/ui/menubar.tsx | 167 ++- .../src/components/ui/mobile-filter-sheet.tsx | 385 +++--- .../src/components/ui/modal.tsx | 195 ++- .../src/components/ui/more-actions-menu.tsx | 76 +- .../src/components/ui/navigation-menu.tsx | 76 +- .../src/components/ui/organization-card.tsx | 130 +- .../src/components/ui/page-actions.tsx | 179 +-- .../src/components/ui/page-loader.tsx | 67 +- .../src/components/ui/pagination-stub.tsx | 46 +- .../src/components/ui/progress-bar.tsx | 16 +- .../src/components/ui/progress.tsx | 20 +- .../src/components/ui/radio-group.tsx | 34 +- .../ui/release-changelog-section.tsx | 18 +- .../ui/responsive-icons-block-stub.tsx | 8 +- .../src/components/ui/select.tsx | 163 ++- .../src/components/ui/separator.tsx | 18 +- .../src/components/ui/service-card.tsx | 110 +- .../src/components/ui/sheet.tsx | 135 +- .../ui/simple-markdown-renderer.tsx | 225 ++-- .../src/components/ui/skeleton.tsx | 228 ++-- .../src/components/ui/slider.tsx | 24 +- .../src/components/ui/square-avatar.tsx | 32 +- .../src/components/ui/status-badge.tsx | 57 +- .../src/components/ui/status-indicator.tsx | 15 +- .../src/components/ui/switch.tsx | 22 +- .../src/components/ui/tab-content.tsx | 40 +- .../src/components/ui/tab-navigation.tsx | 201 +-- .../src/components/ui/table/index.ts | 48 +- .../ui/table/query-report-table/index.ts | 17 +- .../query-report-table-header.tsx | 47 +- .../query-report-table-row.tsx | 50 +- .../query-report-table-skeleton.tsx | 38 +- .../query-report-table/query-report-table.tsx | 67 +- .../ui/table/query-report-table/types.ts | 64 +- .../ui/table/query-report-table/utils.ts | 71 +- .../src/components/ui/table/table-cell.tsx | 40 +- .../ui/table/table-description-cell.tsx | 34 +- .../components/ui/table/table-empty-state.tsx | 37 +- .../src/components/ui/table/table-header.tsx | 307 +++-- .../src/components/ui/table/table-row.tsx | 64 +- .../components/ui/table/table-skeleton.tsx | 48 +- .../ui/table/table-timestamp-cell.tsx | 40 +- .../src/components/ui/table/table.tsx | 138 +-- .../src/components/ui/table/types.ts | 262 ++-- .../src/components/ui/table/utils.ts | 36 +- .../src/components/ui/tabs.tsx | 36 +- .../src/components/ui/tag.tsx | 92 +- .../src/components/ui/tags-input.tsx | 107 +- .../src/components/ui/textarea.tsx | 43 +- .../src/components/ui/title-content-block.tsx | 20 +- .../src/components/ui/toaster.tsx | 10 +- .../src/components/ui/toggle.tsx | 46 +- .../src/components/ui/tooltip.tsx | 28 +- .../src/components/unified-filter-logic.tsx | 342 +++--- .../src/components/unified-pagination.tsx | 68 +- .../src/components/user-display.tsx | 14 +- .../src/components/user-icon.tsx | 36 +- .../src/components/user-summary-stub.tsx | 108 +- .../src/components/vendor-compact-card.tsx | 132 +- .../src/components/vendor-directory-icon.tsx | 30 +- .../src/components/vendor-display-button.tsx | 56 +- .../src/components/vendor-icon.tsx | 81 +- .../src/components/vendor-page-skeleton.tsx | 4 +- .../src/components/vendor-tag.tsx | 188 +-- .../src/components/vendors-icon.tsx | 28 +- .../src/components/why-it-matters.tsx | 45 +- .../src/components/x-button.tsx | 40 +- .../src/components/x-icon.tsx | 20 +- .../src/components/x-logo.tsx | 12 +- .../src/components/yes-no-display.tsx | 84 +- openframe-frontend-core/src/fonts.ts | 22 +- .../src/hooks/api-hooks.ts | 20 +- openframe-frontend-core/src/hooks/index.ts | 23 +- .../src/hooks/nats/use-nats-client.ts | 60 +- .../src/hooks/platform/index.ts | 2 +- .../src/hooks/platform/use-platform-config.ts | 20 +- .../state/__tests__/use-api-params.test.ts | 1082 ++++++++--------- .../src/hooks/state/flatten-schema.ts | 77 +- .../src/hooks/state/graphql-parser.ts | 107 +- .../src/hooks/state/index.ts | 82 +- .../src/hooks/state/introspection.ts | 162 ++- .../src/hooks/state/url-converter.ts | 116 +- .../src/hooks/state/use-api-params.ts | 382 +++--- .../state/use-cursor-pagination-state.ts | 131 +- .../src/hooks/state/use-query-params.ts | 284 +++-- openframe-frontend-core/src/hooks/ui/index.ts | 18 +- .../src/hooks/ui/use-debounce.ts | 18 +- .../src/hooks/ui/use-horizontal-scrollbar.ts | 298 ++--- .../src/hooks/ui/use-local-storage.ts | 90 +- .../src/hooks/ui/use-media-query.ts | 45 +- .../src/hooks/ui/use-memoized-callback.ts | 16 +- .../src/hooks/ui/use-onboarding-state.ts | 146 ++- .../src/hooks/ui/use-table-pagination.ts | 62 +- .../src/hooks/ui/use-throttle.ts | 30 +- .../src/hooks/ui/use-window-size.ts | 26 +- .../src/hooks/use-authenticated-image.ts | 203 ++-- .../src/hooks/use-batch-images.ts | 126 +- .../src/hooks/use-contact-submission.ts | 89 +- .../src/hooks/use-dynamic-theme-context.ts | 4 +- .../src/hooks/use-dynamic-theming.ts | 4 +- .../src/hooks/use-interactive-state.ts | 4 +- .../src/hooks/use-quick-action-hint.ts | 94 +- .../src/hooks/use-subcategory-count.ts | 4 +- .../src/hooks/use-theme-aware.ts | 4 +- .../src/hooks/use-toast.ts | 44 +- openframe-frontend-core/src/index.ts | 33 +- openframe-frontend-core/src/nats/index.ts | 2 +- openframe-frontend-core/src/nats/nats.ts | 294 +++-- .../src/stories/AppLayout.stories.tsx | 94 +- .../src/stories/Autocomplete.stories.tsx | 41 +- .../src/stories/Button.stories.tsx | 119 +- .../src/stories/Checkbox.stories.tsx | 38 +- .../src/stories/CheckboxBlock.stories.tsx | 15 +- .../CheckboxWithDescription.stories.tsx | 12 +- .../src/stories/DatePicker.stories.tsx | 457 +++---- .../src/stories/DeviceCard.stories.tsx | 55 +- .../src/stories/DeviceCardCompact.stories.tsx | 42 +- .../src/stories/Drawer.stories.tsx | 69 +- .../src/stories/FileManager.stories.tsx | 56 +- .../stories/FileManagerActionBar.stories.tsx | 2 +- .../src/stories/Input.stories.tsx | 11 +- .../src/stories/ListPageLayout.stories.tsx | 322 ++--- .../src/stories/MobileFilterModal.stories.tsx | 94 +- .../src/stories/MobileFilterSheet.stories.tsx | 102 +- .../src/stories/Modal.stories.tsx | 53 +- .../src/stories/MoreActionsMenu.stories.tsx | 12 +- .../src/stories/NavigationSidebar.stories.tsx | 18 +- .../stories/OSPlatformSelector.stories.tsx | 79 +- .../src/stories/OSTypeBadge.stories.tsx | 3 +- .../src/stories/OSTypeBadgeGroup.stories.tsx | 33 +- .../stories/OrganizationSelector.stories.tsx | 34 +- .../src/stories/PageActions.stories.tsx | 58 +- .../src/stories/PageContainer.stories.tsx | 18 +- .../src/stories/Pagination.stories.tsx | 24 +- .../src/stories/QueryReportTable.stories.tsx | 42 +- .../src/stories/ScriptArguments.stories.tsx | 125 +- .../src/stories/ScriptInfoSection.stories.tsx | 135 +- .../src/stories/Select.stories.tsx | 76 +- .../src/stories/SelectButton.stories.tsx | 58 +- .../src/stories/ShellTypeBadge.stories.tsx | 13 +- .../src/stories/TabNavigation.stories.tsx | 73 +- .../src/stories/Table.stories.tsx | 167 +-- .../src/stories/TagsInput.stories.tsx | 30 +- .../src/stories/Textarea.stories.tsx | 39 +- .../src/stories/ToolBadge.stories.tsx | 6 +- .../src/styles/app-globals.css | 50 +- openframe-frontend-core/src/styles/index.css | 2 +- .../src/styles/ods-colors.css | 19 +- .../src/styles/ods-design-tokens.css | 6 +- .../src/styles/ods-interaction-states.css | 9 +- .../src/styles/ods-responsive-tokens.css | 156 +-- .../src/styles/storybook-fonts.css | 2 +- .../src/styles/vendor-react-easy-crop.css | 6 +- .../src/styles/vendor-react-scroll.css | 2 +- .../src/types/access-code-cohorts.ts | 2 +- .../src/types/announcement.ts | 20 +- openframe-frontend-core/src/types/auth.ts | 48 +- openframe-frontend-core/src/types/blog.ts | 82 +- .../src/types/case-study.ts | 158 +-- .../src/types/categories.ts | 4 +- openframe-frontend-core/src/types/category.ts | 2 +- .../src/types/customer-interview-ai.types.ts | 234 ++-- .../src/types/customer-interview.ts | 226 ++-- openframe-frontend-core/src/types/employee.ts | 2 +- openframe-frontend-core/src/types/faq.ts | 2 +- openframe-frontend-core/src/types/icons.ts | 7 +- openframe-frontend-core/src/types/index.ts | 97 +- .../src/types/logs.types.ts | 52 +- openframe-frontend-core/src/types/luma.ts | 2 +- .../src/types/marketing.ts | 57 +- openframe-frontend-core/src/types/media.ts | 2 +- .../src/types/navigation.ts | 134 +- openframe-frontend-core/src/types/os.types.ts | 42 +- .../src/types/permissions.ts | 4 +- openframe-frontend-core/src/types/platform.ts | 22 +- .../src/types/product-release.ts | 274 +++-- openframe-frontend-core/src/types/profile.ts | 2 +- openframe-frontend-core/src/types/report.ts | 4 +- .../src/types/shell.types.ts | 35 +- openframe-frontend-core/src/types/slack.ts | 2 +- openframe-frontend-core/src/types/stack.ts | 16 +- openframe-frontend-core/src/types/supabase.ts | 582 +++++---- openframe-frontend-core/src/types/team.ts | 2 +- openframe-frontend-core/src/types/tmcg.ts | 94 +- .../src/types/tool.types.ts | 10 +- openframe-frontend-core/src/types/user.ts | 75 +- .../src/types/vendor-links.ts | 8 +- openframe-frontend-core/src/types/vendor.ts | 226 ++-- openframe-frontend-core/src/types/waitlist.ts | 2 +- .../src/utils/access-code-client.ts | 19 +- .../src/utils/announcement-storage.ts | 2 +- .../src/utils/app-config.ts | 8 +- openframe-frontend-core/src/utils/cn.ts | 112 +- openframe-frontend-core/src/utils/common.ts | 33 +- .../src/utils/compare-utils-stub.ts | 4 +- .../src/utils/date-formatters.ts | 14 +- .../src/utils/date-utils.ts | 78 +- .../src/utils/dynamic-icons.tsx | 58 +- .../src/utils/format-text-stub.ts | 2 +- openframe-frontend-core/src/utils/format.ts | 66 +- .../src/utils/image-proxy-stub.ts | 15 +- .../src/utils/image-proxy.ts | 6 +- openframe-frontend-core/src/utils/index.ts | 36 +- .../src/utils/lite-youtube-embed-stub.tsx | 2 +- .../src/utils/lite-youtube-embed.tsx | 9 +- .../src/utils/media-carousel-utils-stub.ts | 2 +- .../src/utils/media-carousel-utils.ts | 4 +- .../src/utils/ods-color-tokens-stub.ts | 6 +- .../src/utils/ods-color-utils.ts | 152 ++- .../src/utils/onboarding-storage.ts | 89 +- .../src/utils/os-platforms.ts | 6 +- openframe-frontend-core/src/utils/os-utils.ts | 68 +- .../src/utils/platform-config.tsx | 47 +- .../src/utils/shell-utils.ts | 18 +- .../src/utils/tool-utils.ts | 106 +- openframe-frontend-core/src/utils/url-fix.ts | 17 +- .../src/utils/validation-utils.ts | 76 +- .../src/utils/vendor-media-stub.ts | 18 +- openframe-frontend-core/tailwind.config.ts | 252 ++-- openframe-frontend-core/tsup.config.ts | 8 +- openframe-frontend-core/vitest.config.ts | 15 +- openframe-frontend-core/vitest.setup.ts | 25 +- 2307 files changed, 28424 insertions(+), 37452 deletions(-) create mode 100644 openframe-frontend-core/biome.json diff --git a/openframe-frontend-core/.eslintrc.json b/openframe-frontend-core/.eslintrc.json index 5a53b1ed7..d7e754fa4 100644 --- a/openframe-frontend-core/.eslintrc.json +++ b/openframe-frontend-core/.eslintrc.json @@ -1,9 +1,5 @@ { - "extends": [ - "next/core-web-vitals", - "next/typescript", - "plugin:storybook/recommended" - ], + "extends": ["next/core-web-vitals", "next/typescript", "plugin:storybook/recommended"], "rules": { "@typescript-eslint/no-unused-vars": "warn", "@typescript-eslint/no-explicit-any": "warn", @@ -13,10 +9,7 @@ }, "overrides": [ { - "files": [ - "**/*.ts", - "**/*.tsx" - ], + "files": ["**/*.ts", "**/*.tsx"], "parser": "@typescript-eslint/parser", "parserOptions": { "ecmaVersion": "latest", @@ -27,4 +20,4 @@ } } ] -} \ No newline at end of file +} diff --git a/openframe-frontend-core/.storybook/main.ts b/openframe-frontend-core/.storybook/main.ts index 546a44a40..0ac95a889 100644 --- a/openframe-frontend-core/.storybook/main.ts +++ b/openframe-frontend-core/.storybook/main.ts @@ -1,16 +1,14 @@ import type { StorybookConfig } from '@storybook/nextjs-vite'; const config: StorybookConfig = { - "stories": [ - "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)" - ], - "addons": [], - "framework": { - "name": "@storybook/nextjs-vite", - "options": {} + stories: ['../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], + addons: [], + framework: { + name: '@storybook/nextjs-vite', + options: {}, + }, + core: { + disableTelemetry: true, }, - "core": { - "disableTelemetry": true - } }; -export default config; \ No newline at end of file +export default config; diff --git a/openframe-frontend-core/.storybook/preview.ts b/openframe-frontend-core/.storybook/preview.ts index 17654aba2..39b7835f1 100644 --- a/openframe-frontend-core/.storybook/preview.ts +++ b/openframe-frontend-core/.storybook/preview.ts @@ -3,16 +3,15 @@ import type { Preview } from '@storybook/nextjs-vite'; import '../src/styles/storybook-fonts.css'; import '../src/styles/index.css'; - const preview: Preview = { parameters: { controls: { matchers: { - color: /(background|color)$/i, - date: /Date$/i, + color: /(background|color)$/i, + date: /Date$/i, }, }, }, }; -export default preview; \ No newline at end of file +export default preview; diff --git a/openframe-frontend-core/biome.json b/openframe-frontend-core/biome.json new file mode 100644 index 000000000..2d963b15b --- /dev/null +++ b/openframe-frontend-core/biome.json @@ -0,0 +1,135 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.4.4/schema.json", + "vcs": { "enabled": false, "clientKind": "git", "useIgnoreFile": false }, + "files": { + "ignoreUnknown": false, + "includes": [ + "**", + "!**/node_modules/", + "!**/dist", + "!**/.tsbuildinfo", + "!**/.next", + "!**/.yalc", + "!**/next-env.d.ts", + "!**/*.css" + ] + }, + "assist": { "actions": { "source": { "organizeImports": "on" } } }, + "json": { + "formatter": { "enabled": true, "indentWidth": 2, "indentStyle": "space" } + }, + "formatter": { + "enabled": true, + "formatWithErrors": false, + "indentStyle": "space", + "indentWidth": 2, + "lineEnding": "lf", + "lineWidth": 120, + "attributePosition": "auto", + "bracketSameLine": false, + "bracketSpacing": true + }, + "linter": { + "enabled": true, + "rules": { + "recommended": false, + "complexity": { + "noUselessConstructor": "error", + "useLiteralKeys": "error" + }, + "correctness": { + "noUnusedVariables": "error", + "noUndeclaredDependencies": "error", + "useHookAtTopLevel": "error", + "useExhaustiveDependencies": "warn" + }, + "style": { + "noParameterAssign": "error", + "useArrayLiterals": "error", + "useConst": "error", + "useDefaultParameterLast": "error", + "useNamingConvention": { + "level": "error", + "options": { + "strictCase": false, + "conventions": [ + { + "selector": { "kind": "variable" }, + "formats": ["camelCase", "CONSTANT_CASE", "PascalCase"] + }, + { + "selector": { "kind": "function" }, + "formats": ["camelCase", "PascalCase"] + }, + { "selector": { "kind": "class" }, "formats": ["PascalCase"] }, + { + "selector": { "kind": "interface" }, + "formats": ["PascalCase"] + }, + { + "selector": { "kind": "typeAlias" }, + "formats": ["PascalCase"] + }, + { "selector": { "kind": "enum" }, "formats": ["PascalCase"] }, + { + "selector": { "kind": "enumMember" }, + "formats": ["PascalCase"] + }, + { + "selector": { "kind": "objectLiteralMember" }, + "formats": ["camelCase", "PascalCase", "snake_case", "CONSTANT_CASE"] + }, + { + "selector": { "kind": "typeMember" }, + "formats": ["camelCase", "PascalCase", "snake_case", "CONSTANT_CASE"] + }, + { + "selector": { "kind": "importAlias" }, + "formats": ["camelCase", "PascalCase", "CONSTANT_CASE"] + }, + { + "selector": { "kind": "exportAlias" }, + "formats": ["camelCase", "PascalCase", "CONSTANT_CASE"] + } + ] + } + } + }, + "suspicious": { + "noDuplicateClassMembers": "error", + "noRedeclare": "error" + } + } + }, + "javascript": { + "formatter": { + "jsxQuoteStyle": "double", + "quoteProperties": "asNeeded", + "trailingCommas": "all", + "semicolons": "always", + "arrowParentheses": "asNeeded", + "bracketSameLine": false, + "quoteStyle": "single", + "attributePosition": "auto", + "bracketSpacing": true + } + }, + "css": { + "parser": { "cssModules": true }, + "linter": { "enabled": true }, + "formatter": { "enabled": true } + }, + "overrides": [ + { + "includes": ["**/*.test.ts", "**/*.test.tsx"], + "linter": { + "rules": { "correctness": { "noUndeclaredDependencies": "off" } } + } + }, + { + "includes": ["**/*.css"], + "linter": { "enabled": false }, + "formatter": { "enabled": false } + } + ] +} diff --git a/openframe-frontend-core/package.json b/openframe-frontend-core/package.json index 5c761e897..ded8cd06c 100644 --- a/openframe-frontend-core/package.json +++ b/openframe-frontend-core/package.json @@ -141,7 +141,11 @@ "yalc:publish": "yalc publish", "generate:icons": "node scripts/generate-icons.mjs", "storybook": "storybook dev -p 6006", - "build-storybook": "storybook build" + "build-storybook": "storybook build", + "lint:biome": "biome check .", + "lint:biome:fix": "biome check --fix .", + "format": "biome format .", + "format:fix": "biome format --fix ." }, "peerDependencies": { "@tanstack/react-query": "^5.0.0", @@ -211,6 +215,7 @@ "vaul": "^1.1.2" }, "devDependencies": { + "@biomejs/biome": "^2.4.4", "@storybook/nextjs-vite": "^10.1.11", "@svgr/cli": "^8.1.0", "@testing-library/jest-dom": "^6.9.1", diff --git a/openframe-frontend-core/scripts/generate-icons.mjs b/openframe-frontend-core/scripts/generate-icons.mjs index 9c1843f36..99f1dd717 100644 --- a/openframe-frontend-core/scripts/generate-icons.mjs +++ b/openframe-frontend-core/scripts/generate-icons.mjs @@ -5,9 +5,9 @@ import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, unlinkSync, import { basename, dirname, join } from 'path'; import { fileURLToPath } from 'url'; -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); -const rootDir = join(__dirname, '..'); +const Filename = fileURLToPath(import.meta.url); +const Dirname = dirname(Filename); +const rootDir = join(Dirname, '..'); const ICONS_V2_DIR = join(rootDir, 'src/components/icons-v2'); const OUTPUT_DIR = join(rootDir, 'src/components/icons-v2-generated'); @@ -16,7 +16,7 @@ const OUTPUT_DIR = join(rootDir, 'src/components/icons-v2-generated'); function toPascalCase(str) { return str .split(/[-\s]+/) - .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .map(word => word.charAt(0).toUpperCase() + word.slice(1)) .join(''); } @@ -27,7 +27,7 @@ function toKebabCase(str) { // Get all category directories function getCategories() { - return readdirSync(ICONS_V2_DIR).filter((item) => { + return readdirSync(ICONS_V2_DIR).filter(item => { const itemPath = join(ICONS_V2_DIR, item); return statSync(itemPath).isDirectory() && !item.startsWith('.'); }); @@ -44,8 +44,8 @@ function getOriginalSvgNames(categoryPath) { if (/^\d/.test(name)) { throw new Error( `Invalid icon name: "${name}" starts with a number. ` + - `Icon names cannot start with numbers as they would generate invalid JavaScript/TypeScript identifiers. ` + - `Please rename the file to start with a letter (e.g., "point-100" instead of "100-point").` + `Icon names cannot start with numbers as they would generate invalid JavaScript/TypeScript identifiers. ` + + `Please rename the file to start with a letter (e.g., "point-100" instead of "100-point").`, ); } } @@ -111,9 +111,10 @@ function processGeneratedCategory(categoryPath, originalNames) { exports.sort((a, b) => a.componentName.localeCompare(b.componentName)); // Generate index.ts - const indexContent = exports - .map(({ componentName, kebabName }) => `export { ${componentName} } from './${kebabName}-icon';`) - .join('\n') + '\n'; + const indexContent = + exports + .map(({ componentName, kebabName }) => `export { ${componentName} } from './${kebabName}-icon';`) + .join('\n') + '\n'; writeFileSync(join(categoryPath, 'index.ts'), indexContent); @@ -149,10 +150,10 @@ for (const category of categories) { // Run SVGR for entire category at once try { - execSync( - `npx @svgr/cli --config-file svgr.config.cjs --out-dir "${outputPath}" -- "${inputPath}"`, - { cwd: rootDir, stdio: 'pipe' } - ); + execSync(`npx @svgr/cli --config-file svgr.config.cjs --out-dir "${outputPath}" -- "${inputPath}"`, { + cwd: rootDir, + stdio: 'pipe', + }); // Post-process generated files const count = processGeneratedCategory(outputPath, originalNames); @@ -165,8 +166,8 @@ for (const category of categories) { // Generate root index.ts const categoryExports = categories - .filter((cat) => existsSync(join(OUTPUT_DIR, cat, 'index.ts'))) - .map((cat) => `export * from './${cat}';`) + .filter(cat => existsSync(join(OUTPUT_DIR, cat, 'index.ts'))) + .map(cat => `export * from './${cat}';`) .join('\n'); writeFileSync(join(OUTPUT_DIR, 'index.ts'), categoryExports + '\n'); diff --git a/openframe-frontend-core/src/assets/index.ts b/openframe-frontend-core/src/assets/index.ts index f0b6328c7..eaa60bb29 100644 --- a/openframe-frontend-core/src/assets/index.ts +++ b/openframe-frontend-core/src/assets/index.ts @@ -2,4 +2,4 @@ // Font files and other static assets // Platform utilities will be available when needed -export const assets = {} as const; \ No newline at end of file +export const assets = {} as const; diff --git a/openframe-frontend-core/src/components/about-icon.tsx b/openframe-frontend-core/src/components/about-icon.tsx index c20237cf8..6505ef561 100644 --- a/openframe-frontend-core/src/components/about-icon.tsx +++ b/openframe-frontend-core/src/components/about-icon.tsx @@ -6,11 +6,7 @@ interface AboutIconProps { className?: string; } -export function AboutIcon({ - width = 24, - height = 24, - className = "" -}: AboutIconProps) { +export function AboutIcon({ width = 24, height = 24, className = '' }: AboutIconProps) { return ( ); -} \ No newline at end of file +} diff --git a/openframe-frontend-core/src/components/accordion.tsx b/openframe-frontend-core/src/components/accordion.tsx index 0bf4dd33b..1045ac307 100644 --- a/openframe-frontend-core/src/components/accordion.tsx +++ b/openframe-frontend-core/src/components/accordion.tsx @@ -1,20 +1,20 @@ -"use client" +'use client'; -import * as React from "react" -import * as AccordionPrimitive from "@radix-ui/react-accordion" -import { ChevronDown } from "lucide-react" +import * as AccordionPrimitive from '@radix-ui/react-accordion'; +import { ChevronDown } from 'lucide-react'; +import * as React from 'react'; -import { cn } from "../utils/cn" +import { cn } from '../utils/cn'; -const Accordion = AccordionPrimitive.Root +const Accordion = AccordionPrimitive.Root; const AccordionItem = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - -)) -AccordionItem.displayName = "AccordionItem" + +)); +AccordionItem.displayName = 'AccordionItem'; const AccordionTrigger = React.forwardRef< React.ElementRef, @@ -24,7 +24,7 @@ const AccordionTrigger = React.forwardRef< svg]:rotate-180", + 'flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180', className, )} {...props} @@ -33,8 +33,8 @@ const AccordionTrigger = React.forwardRef< -)) -AccordionTrigger.displayName = "AccordionTrigger" +)); +AccordionTrigger.displayName = 'AccordionTrigger'; const AccordionContent = React.forwardRef< React.ElementRef, @@ -43,14 +43,14 @@ const AccordionContent = React.forwardRef<
{children}
-)) -AccordionContent.displayName = "AccordionContent" +)); +AccordionContent.displayName = 'AccordionContent'; -export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } +export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }; diff --git a/openframe-frontend-core/src/components/alert-dialog.tsx b/openframe-frontend-core/src/components/alert-dialog.tsx index 42d8eafc2..4da63b4fe 100644 --- a/openframe-frontend-core/src/components/alert-dialog.tsx +++ b/openframe-frontend-core/src/components/alert-dialog.tsx @@ -1,16 +1,16 @@ -"use client" +'use client'; -import * as React from "react" -import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog" +import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog'; +import * as React from 'react'; -import { cn } from "../utils/cn" -import { buttonVariants } from "./ui/button" +import { cn } from '../utils/cn'; +import { buttonVariants } from './ui/button'; -const AlertDialog = AlertDialogPrimitive.Root +const AlertDialog = AlertDialogPrimitive.Root; -const AlertDialogTrigger = AlertDialogPrimitive.Trigger +const AlertDialogTrigger = AlertDialogPrimitive.Trigger; -const AlertDialogPortal = AlertDialogPrimitive.Portal +const AlertDialogPortal = AlertDialogPrimitive.Portal; const AlertDialogOverlay = React.forwardRef< React.ElementRef, @@ -18,14 +18,14 @@ const AlertDialogOverlay = React.forwardRef< >(({ className, ...props }, ref) => ( -)) -AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName +)); +AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName; const AlertDialogContent = React.forwardRef< React.ElementRef, @@ -36,79 +36,48 @@ const AlertDialogContent = React.forwardRef< -)) -AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName +)); +AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName; -const AlertDialogHeader = ({ - className, - ...props -}: React.HTMLAttributes) => ( -
-) -AlertDialogHeader.displayName = "AlertDialogHeader" +const AlertDialogHeader = ({ className, ...props }: React.HTMLAttributes) => ( +
+); +AlertDialogHeader.displayName = 'AlertDialogHeader'; -const AlertDialogFooter = ({ - className, - ...props -}: React.HTMLAttributes) => ( -
-) -AlertDialogFooter.displayName = "AlertDialogFooter" +const AlertDialogFooter = ({ className, ...props }: React.HTMLAttributes) => ( +
+); +AlertDialogFooter.displayName = 'AlertDialogFooter'; const AlertDialogTitle = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - -)) -AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName + +)); +AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName; const AlertDialogDescription = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - -)) -AlertDialogDescription.displayName = - AlertDialogPrimitive.Description.displayName + +)); +AlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName; const AlertDialogAction = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - -)) -AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName + +)); +AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName; const AlertDialogCancel = React.forwardRef< React.ElementRef, @@ -116,15 +85,11 @@ const AlertDialogCancel = React.forwardRef< >(({ className, ...props }, ref) => ( -)) -AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName +)); +AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName; export { AlertDialog, @@ -138,4 +103,4 @@ export { AlertDialogDescription, AlertDialogAction, AlertDialogCancel, -} +}; diff --git a/openframe-frontend-core/src/components/alert.tsx b/openframe-frontend-core/src/components/alert.tsx index 23b4ae5bb..18fb9f780 100644 --- a/openframe-frontend-core/src/components/alert.tsx +++ b/openframe-frontend-core/src/components/alert.tsx @@ -1,59 +1,43 @@ -import * as React from "react" -import { cva, type VariantProps } from "class-variance-authority" +import { cva, type VariantProps } from 'class-variance-authority'; +import * as React from 'react'; -import { cn } from "../utils/cn" +import { cn } from '../utils/cn'; const alertVariants = cva( - "relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground", + 'relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground', { variants: { variant: { - default: "bg-background text-foreground", - destructive: - "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive", + default: 'bg-background text-foreground', + destructive: 'border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive', }, }, defaultVariants: { - variant: "default", + variant: 'default', }, - } -) + }, +); const Alert = React.forwardRef< HTMLDivElement, React.HTMLAttributes & VariantProps >(({ className, variant, ...props }, ref) => ( -
-)) -Alert.displayName = "Alert" +
+)); +Alert.displayName = 'Alert'; -const AlertTitle = React.forwardRef< - HTMLParagraphElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
-)) -AlertTitle.displayName = "AlertTitle" +const AlertTitle = React.forwardRef>( + ({ className, ...props }, ref) => ( +
+ ), +); +AlertTitle.displayName = 'AlertTitle'; -const AlertDescription = React.forwardRef< - HTMLParagraphElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
-)) -AlertDescription.displayName = "AlertDescription" +const AlertDescription = React.forwardRef>( + ({ className, ...props }, ref) => ( +
+ ), +); +AlertDescription.displayName = 'AlertDescription'; -export { Alert, AlertTitle, AlertDescription } +export { Alert, AlertTitle, AlertDescription }; diff --git a/openframe-frontend-core/src/components/announcement-bar.tsx b/openframe-frontend-core/src/components/announcement-bar.tsx index 8aff828bf..b2201a71f 100644 --- a/openframe-frontend-core/src/components/announcement-bar.tsx +++ b/openframe-frontend-core/src/components/announcement-bar.tsx @@ -1,27 +1,16 @@ -"use client"; +'use client'; -import { useState, useEffect } from 'react'; import { X } from 'lucide-react'; -import { Button } from './ui/button'; -import { renderSvgIcon } from './icon-utils'; -import { - setStoredAnnouncement, - getStoredAnnouncement, - clearStoredAnnouncement, -} from '../utils/announcement-storage'; +import { useEffect, useState } from 'react'; import { Announcement } from '../types/announcement'; +import { clearStoredAnnouncement, getStoredAnnouncement, setStoredAnnouncement } from '../utils/announcement-storage'; import { getAppType } from '../utils/app-config'; +import { renderSvgIcon } from './icon-utils'; +import { Button } from './ui/button'; // Helper that defers to renderSvgIcon so we don't need local icon imports -const getSvgIcon = ( - name: string, - size: 'main' | 'cta' = 'main', - extra: Record = {} -) => { - const cls = - size === 'cta' - ? 'relative shrink-0 w-3 h-3 md:w-4 md:h-4' - : 'relative shrink-0 w-6 h-6 md:w-8 md:h-8'; +const getSvgIcon = (name: string, size: 'main' | 'cta' = 'main', extra: Record = {}) => { + const cls = size === 'cta' ? 'relative shrink-0 w-3 h-3 md:w-4 md:h-4' : 'relative shrink-0 w-6 h-6 md:w-8 md:h-8'; return renderSvgIcon(name, { className: cls, ...extra }); }; @@ -34,7 +23,7 @@ export function AnnouncementBar() { // Helper to determine dismissal key for localStorage const getDismissKey = (id: string) => `${platform}-announcement-${id}-dismissed`; - + // Helper to get platform-specific cache key const getCacheKey = () => `${platform}-announcement-cache`; @@ -43,7 +32,7 @@ export function AnnouncementBar() { try { // Server-side platform injection - no URL parameter needed const response = await fetch(`/api/announcements/active`); - + if (response.ok) { const data = await response.json(); if (data.announcement) { @@ -59,7 +48,7 @@ export function AnnouncementBar() { // No announcement available - clean up localStorage and hide bar setAnnouncement(null); setIsVisible(false); - + // Use utility function to properly clear platform-specific announcement data clearStoredAnnouncement(getCacheKey()); } @@ -68,7 +57,7 @@ export function AnnouncementBar() { console.error(`❌ [${platform.toUpperCase()}] Error fetching announcement: ${response.status}`); setAnnouncement(null); setIsVisible(false); - + // Clear stale data on network errors too clearStoredAnnouncement(getCacheKey()); } @@ -76,7 +65,7 @@ export function AnnouncementBar() { console.error('Error fetching active announcement:', error); setAnnouncement(null); setIsVisible(false); - + // Clear stale data on exceptions too clearStoredAnnouncement(getCacheKey()); } @@ -97,7 +86,7 @@ export function AnnouncementBar() { // Schedule refresh every 5 minutes const interval = setInterval(fetchActiveAnnouncement, 300_000); return () => clearInterval(interval); - }, []); + }, [fetchActiveAnnouncement, getCacheKey, getDismissKey]); // helpers const handleDismiss = () => { @@ -127,11 +116,7 @@ export function AnnouncementBar() { ); } - return getSvgIcon( - announcement.icon_svg_name || 'openframe-logo', - 'main', - announcement.icon_svg_props ?? {} - ); + return getSvgIcon(announcement.icon_svg_name || 'openframe-logo', 'main', announcement.icon_svg_props ?? {}); }; // If no announcement or dismissed => render nothing @@ -145,11 +130,11 @@ export function AnnouncementBar() { >
{/* Mobile: Clickable content area, Desktop: Regular content */} -
{ + onClick={e => { // Only handle click on mobile (< 768px) and if CTA is enabled if (window.innerWidth < 768 && announcement.cta_enabled && announcement.cta_url) { e.preventDefault(); @@ -177,11 +162,7 @@ export function AnnouncementBar() { size="sm" leftIcon={ announcement.cta_show_icon && announcement.cta_icon - ? getSvgIcon( - announcement.cta_icon, - 'cta', - announcement.cta_icon_props ?? {} - ) + ? getSvgIcon(announcement.cta_icon, 'cta', announcement.cta_icon_props ?? {}) : undefined } className="transition-opacity hover:opacity-90 text-xs md:text-sm whitespace-nowrap" @@ -199,7 +180,7 @@ export function AnnouncementBar() { {/* Dismiss button - always visible */} - ) -}) -CarouselPrevious.displayName = "CarouselPrevious" - -const CarouselNext = React.forwardRef< - HTMLButtonElement, - React.ComponentProps ->(({ className, variant = "outline", size = "icon", ...props }, ref) => { - const { orientation, scrollNext, canScrollNext } = useCarousel() - - return ( - - ) -}) -CarouselNext.displayName = "CarouselNext" + return ( + + ); + }, +); +CarouselPrevious.displayName = 'CarouselPrevious'; + +const CarouselNext = React.forwardRef>( + ({ className, variant = 'outline', size = 'icon', ...props }, ref) => { + const { orientation, scrollNext, canScrollNext } = useCarousel(); -export { - type CarouselApi, - Carousel, - CarouselContent, - CarouselItem, - CarouselPrevious, - CarouselNext, -} + return ( + + ); + }, +); +CarouselNext.displayName = 'CarouselNext'; + +export { type CarouselApi, Carousel, CarouselContent, CarouselItem, CarouselPrevious, CarouselNext }; diff --git a/openframe-frontend-core/src/components/categories-cart.tsx b/openframe-frontend-core/src/components/categories-cart.tsx index e4af5ed49..3818a77bf 100644 --- a/openframe-frontend-core/src/components/categories-cart.tsx +++ b/openframe-frontend-core/src/components/categories-cart.tsx @@ -1,20 +1,19 @@ -"use client" +'use client'; -import React from 'react'; -import Link from 'next/link'; import { ChevronRight } from 'lucide-react'; -import { VendorIcon } from './vendor-icon'; +import Link from 'next/link'; +import React from 'react'; import type { CategoryCardProps, RealCategoryCardProps } from '../types/category'; - +import { VendorIcon } from './vendor-icon'; // Component that receives vendor and subcategory data as props -export function CategoriesCart({ - category, - vendors = [], - vendorCount = 0, - subcategoryCount = 0, +export function CategoriesCart({ + category, + vendors = [], + vendorCount = 0, + subcategoryCount = 0, isLoading = false, - className = '' + className = '', }: RealCategoryCardProps) { return (
- {isLoading ? ( - // Skeleton loading for vendor icons - Array.from({ length: 20 }).map((_, index) => ( -
- )) - ) : vendors && vendors.length > 0 ? ( - vendors.map((vendor: any) => ( - - )) - ) : ( - // No vendors found - show placeholder icons - Array.from({ length: 6 }).map((_, index) => ( -
-
-
- )) - )} + {isLoading + ? // Skeleton loading for vendor icons + Array.from({ length: 20 }).map((_, index) => ( +
+ )) + : vendors && vendors.length > 0 + ? vendors.map((vendor: any) => ( + + )) + : // No vendors found - show placeholder icons + Array.from({ length: 6 }).map((_, index) => ( +
+
+
+ ))}
{/* Gradient overlays for fade effect */} @@ -80,7 +77,7 @@ export function CategoriesCart({
+ >
@@ -89,4 +86,3 @@ export function CategoriesCart({ ); } - diff --git a/openframe-frontend-core/src/components/category-card.tsx b/openframe-frontend-core/src/components/category-card.tsx index e8387952a..e1d8e37f9 100644 --- a/openframe-frontend-core/src/components/category-card.tsx +++ b/openframe-frontend-core/src/components/category-card.tsx @@ -1,6 +1,6 @@ -"use client" +'use client'; -import React from "react"; +import React from 'react'; interface CategoryCardProps { name: string; @@ -32,10 +32,12 @@ export function CategoryCard({ name, description, categoryCount, productCount, i style={{ minWidth: 48, minHeight: 48 }} aria-label={`View ${name}`} > - + + +
); -} \ No newline at end of file +} diff --git a/openframe-frontend-core/src/components/chart.tsx b/openframe-frontend-core/src/components/chart.tsx index 5734d7f2a..2e02b8f5f 100644 --- a/openframe-frontend-core/src/components/chart.tsx +++ b/openframe-frontend-core/src/components/chart.tsx @@ -1,50 +1,45 @@ -"use client" +'use client'; -import * as React from "react" -import * as RechartsPrimitive from "recharts" +import * as React from 'react'; +import * as RechartsPrimitive from 'recharts'; -import { cn } from "../utils/cn" +import { cn } from '../utils/cn'; // Format: { THEME_NAME: CSS_SELECTOR } -const THEMES = { light: "", dark: ".dark" } as const +const THEMES = { light: '', dark: '.dark' } as const; export type ChartConfig = { [k in string]: { - label?: React.ReactNode - icon?: React.ComponentType - } & ( - | { color?: string; theme?: never } - | { color?: never; theme: Record } - ) -} + label?: React.ReactNode; + icon?: React.ComponentType; + } & ({ color?: string; theme?: never } | { color?: never; theme: Record }); +}; type ChartContextProps = { - config: ChartConfig -} + config: ChartConfig; +}; -const ChartContext = React.createContext(null) +const ChartContext = React.createContext(null); function useChart() { - const context = React.useContext(ChartContext) + const context = React.useContext(ChartContext); if (!context) { - throw new Error("useChart must be used within a ") + throw new Error('useChart must be used within a '); } - return context + return context; } const ChartContainer = React.forwardRef< HTMLDivElement, - React.ComponentProps<"div"> & { - config: ChartConfig - children: React.ComponentProps< - typeof RechartsPrimitive.ResponsiveContainer - >["children"] + React.ComponentProps<'div'> & { + config: ChartConfig; + children: React.ComponentProps['children']; } >(({ id, className, children, config, ...props }, ref) => { - const uniqueId = React.useId() - const chartId = `chart-${id || uniqueId.replace(/:/g, "")}` + const uniqueId = React.useId(); + const chartId = `chart-${id || uniqueId.replace(/:/g, '')}`; return ( @@ -53,312 +48,250 @@ const ChartContainer = React.forwardRef< ref={ref} className={cn( "flex aspect-video justify-center text-xs [&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-none [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-sector]:outline-none [&_.recharts-surface]:outline-none", - className + className, )} {...props} > - - {children} - + {children}
- ) -}) -ChartContainer.displayName = "Chart" + ); +}); +ChartContainer.displayName = 'Chart'; const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => { - const colorConfig = Object.entries(config).filter( - ([_, config]) => config.theme || config.color - ) + const colorConfig = Object.entries(config).filter(([_, config]) => config.theme || config.color); if (!colorConfig.length) { - return null + return null; } return (
-
+
- {/* Progress text */} + {/* Progress text */}

- {progress != undefined && progress > 0 ? Math.floor(progress) : 0}% /100% + {progress != undefined && progress > 0 ? Math.floor(progress) : 0}%{' '} + /100%

{/* Progress text */} -

- {label ?? `Processing…`} -

+

{label ?? `Processing…`}

You can safely navigate away from this page, report generation will continue in the background.

@@ -49,4 +68,4 @@ export function OpenmspHeartbeatLoader({ className = '', progress = 0, label, ba
); -} \ No newline at end of file +} diff --git a/openframe-frontend-core/src/components/loading/organization-card-skeleton.tsx b/openframe-frontend-core/src/components/loading/organization-card-skeleton.tsx index ea249502c..9892be1bd 100644 --- a/openframe-frontend-core/src/components/loading/organization-card-skeleton.tsx +++ b/openframe-frontend-core/src/components/loading/organization-card-skeleton.tsx @@ -1,26 +1,26 @@ -import type React from "react" -import { cn } from "../../utils/cn" -import { OrganizationIconSkeleton } from "./organization-icon-skeleton" -import { TextSkeleton, MediaSkeleton, InteractiveSkeleton } from "./unified-skeleton" +import type React from 'react'; +import { cn } from '../../utils/cn'; +import { OrganizationIconSkeleton } from './organization-icon-skeleton'; +import { InteractiveSkeleton, MediaSkeleton, TextSkeleton } from './unified-skeleton'; export interface OrganizationCardSkeletonProps { /** * Additional CSS classes */ - className?: string + className?: string; /** * Show footer stats area */ - showFooter?: boolean + showFooter?: boolean; /** * Show description area */ - showDescription?: boolean + showDescription?: boolean; /** Optional tailwind classes to override the card container background & border */ - containerClassName?: string + containerClassName?: string; } /** @@ -39,14 +39,14 @@ export function OrganizationCardSkeleton({ className, containerClassName, showFooter = true, - showDescription = true + showDescription = true, }: OrganizationCardSkeletonProps) { return (
- ) + ); } /** @@ -128,20 +128,17 @@ export function OrganizationCardSkeletonGrid({ className, containerClassName, showFooter = true, - showDescription = true + showDescription = true, }: { - count?: number - className?: string - containerClassName?: string - showFooter?: boolean - showDescription?: boolean + count?: number; + className?: string; + containerClassName?: string; + showFooter?: boolean; + showDescription?: boolean; }) { return (
@@ -154,5 +151,5 @@ export function OrganizationCardSkeletonGrid({ /> ))}
- ) + ); } diff --git a/openframe-frontend-core/src/components/loading/organization-icon-skeleton.tsx b/openframe-frontend-core/src/components/loading/organization-icon-skeleton.tsx index 9763da992..ada13c80e 100644 --- a/openframe-frontend-core/src/components/loading/organization-icon-skeleton.tsx +++ b/openframe-frontend-core/src/components/loading/organization-icon-skeleton.tsx @@ -1,5 +1,5 @@ -import type React from "react" -import { cn } from "../../utils/cn" +import type React from 'react'; +import { cn } from '../../utils/cn'; export interface OrganizationIconSkeletonProps { /** @@ -11,22 +11,22 @@ export interface OrganizationIconSkeletonProps { * - l: 56px (w-14 h-14) * - xl: 64px (w-16 h-16) - for detail views */ - size?: 'xs' | 'sm' | 'md' | 'lg' | 'l' | 'xl' + size?: 'xs' | 'sm' | 'md' | 'lg' | 'l' | 'xl'; /** * Additional CSS classes */ - className?: string + className?: string; /** * Show background container (default: true) */ - showBackground?: boolean + showBackground?: boolean; /** * Background style variant (default: 'dark') */ - backgroundStyle?: 'dark' | 'light' | 'white' + backgroundStyle?: 'dark' | 'light' | 'white'; } /** @@ -38,8 +38,8 @@ const sizeClasses = { md: 'w-10 h-10', lg: 'w-12 h-12', l: 'w-14 h-14', - xl: 'w-16 h-16' -} + xl: 'w-16 h-16', +}; /** * Background style classes matching VendorIcon/OrganizationIcon exactly @@ -47,8 +47,8 @@ const sizeClasses = { const backgroundClasses = { dark: 'bg-[#161616] border border-ods-border', light: 'bg-ods-card border border-ods-border', - white: 'bg-white border border-[#E5E5E5]' -} + white: 'bg-white border border-[#E5E5E5]', +}; /** * OrganizationIconSkeleton - Loading skeleton for OrganizationIcon @@ -77,23 +77,19 @@ export function OrganizationIconSkeleton({ size = 'md', className = '', showBackground = true, - backgroundStyle = 'dark' + backgroundStyle = 'dark', }: OrganizationIconSkeletonProps) { const containerClasses = cn( sizeClasses[size], 'rounded-lg flex items-center justify-center flex-shrink-0', showBackground && backgroundClasses[backgroundStyle], !showBackground && 'overflow-hidden', - className - ) + className, + ); return ( -
+
- ) + ); } diff --git a/openframe-frontend-core/src/components/loading/page-layout-skeleton.tsx b/openframe-frontend-core/src/components/loading/page-layout-skeleton.tsx index b2d031970..48bc6fde2 100644 --- a/openframe-frontend-core/src/components/loading/page-layout-skeleton.tsx +++ b/openframe-frontend-core/src/components/loading/page-layout-skeleton.tsx @@ -1,10 +1,10 @@ -import type React from "react" -import { cn } from "../../utils/cn" -import { UnifiedSkeleton, TextSkeleton, MediaSkeleton, InteractiveSkeleton } from "./unified-skeleton" -import { CardSkeletonGrid } from "./card-skeleton" +import type React from 'react'; +import { cn } from '../../utils/cn'; +import { CardSkeletonGrid } from './card-skeleton'; +import { InteractiveSkeleton, MediaSkeleton, TextSkeleton, UnifiedSkeleton } from './unified-skeleton'; interface PageLayoutSkeletonProps { - className?: string + className?: string; } /** @@ -17,19 +17,19 @@ export function AnnouncementBarSkeleton() {
{/* Logo skeleton */}
- + {/* Text content skeleton */}
- + {/* Close button skeleton */}
- ) + ); } /** @@ -58,7 +58,7 @@ export function HeaderSkeleton() {
- + {/* Desktop: Show action buttons skeletons */}
@@ -67,7 +67,7 @@ export function HeaderSkeleton() {
- ) + ); } /** @@ -78,33 +78,33 @@ export function HeroSkeleton() {
{/* Title skeleton */} - + {/* Subtitle skeleton */}
- + {/* CTA Button skeleton */}
- ) + ); } /** * Search container skeleton with filters */ -export function SearchContainerSkeleton({ +export function SearchContainerSkeleton({ className, - showFilters = true + showFilters = true, }: PageLayoutSkeletonProps & { showFilters?: boolean }) { return ( -
+
{/* Search input and button */}
@@ -121,7 +121,7 @@ export function SearchContainerSkeleton({
)}
- ) + ); } /** @@ -129,11 +129,11 @@ export function SearchContainerSkeleton({ */ export function CategorySidebarSkeleton({ className }: PageLayoutSkeletonProps) { return ( -
+
{/* Sidebar header */} - + {/* Category list */}
{Array.from({ length: 8 }).map((_, index) => ( @@ -158,7 +158,7 @@ export function CategorySidebarSkeleton({ className }: PageLayoutSkeletonProps)
- ) + ); } /** @@ -166,7 +166,7 @@ export function CategorySidebarSkeleton({ className }: PageLayoutSkeletonProps) */ export function BreadcrumbSkeleton({ className }: PageLayoutSkeletonProps) { return ( -
+
@@ -175,7 +175,7 @@ export function BreadcrumbSkeleton({ className }: PageLayoutSkeletonProps) {
- ) + ); } /** @@ -183,13 +183,13 @@ export function BreadcrumbSkeleton({ className }: PageLayoutSkeletonProps) { */ export function ResultsHeaderSkeleton({ className }: PageLayoutSkeletonProps) { return ( -
+
{/* */}
- ) + ); } /** @@ -199,21 +199,23 @@ export function TwoColumnLayoutSkeleton({ className, sidebarContent, mainContent, - sidebarPosition = 'left' + sidebarPosition = 'left', }: PageLayoutSkeletonProps & { - sidebarContent?: React.ReactNode - mainContent?: React.ReactNode - sidebarPosition?: 'left' | 'right' + sidebarContent?: React.ReactNode; + mainContent?: React.ReactNode; + sidebarPosition?: 'left' | 'right'; }) { - const sidebar = sidebarContent || - const main = mainContent || + const sidebar = sidebarContent || ; + const main = mainContent || ; return ( -
+
{sidebarPosition === 'left' ? ( <> @@ -226,7 +228,7 @@ export function TwoColumnLayoutSkeleton({ )}
- ) + ); } /** @@ -234,7 +236,7 @@ export function TwoColumnLayoutSkeleton({ */ export function ArticleLayoutSkeleton({ className }: PageLayoutSkeletonProps) { return ( -
+
{/* Article header */}
{/* Category/tags */} @@ -242,13 +244,13 @@ export function ArticleLayoutSkeleton({ className }: PageLayoutSkeletonProps) {
- + {/* Title */}
- + {/* Metadata */}
@@ -276,7 +278,7 @@ export function ArticleLayoutSkeleton({ className }: PageLayoutSkeletonProps) {
))} - + {/* Subheading in content */}
@@ -287,7 +289,7 @@ export function ArticleLayoutSkeleton({ className }: PageLayoutSkeletonProps) {
- ) + ); } /** @@ -296,7 +298,7 @@ export function ArticleLayoutSkeleton({ className }: PageLayoutSkeletonProps) { */ export function VendorDetailLayoutSkeleton({ className }: PageLayoutSkeletonProps) { return ( -
+
{/* Breadcrumb */} @@ -305,13 +307,12 @@ export function VendorDetailLayoutSkeleton({ className }: PageLayoutSkeletonProp
{/* Left Content Area */}
- {/* Vendor Hero Section */}
{/* Header - Logo and Title Side by Side */}
- +
{/* Large title skeleton */} @@ -319,7 +320,7 @@ export function VendorDetailLayoutSkeleton({ className }: PageLayoutSkeletonProp {/* Category text */}
- + {/* Pricing tags */}
@@ -340,13 +341,13 @@ export function VendorDetailLayoutSkeleton({ className }: PageLayoutSkeletonProp
{/* Deploy Button */}
- + {/* Voting Buttons */}
- + {/* GitHub Score Section */}
{/* Header */} @@ -357,7 +358,7 @@ export function VendorDetailLayoutSkeleton({ className }: PageLayoutSkeletonProp
- + {/* Stats */}
{Array.from({ length: 4 }).map((_, i) => ( @@ -371,7 +372,7 @@ export function VendorDetailLayoutSkeleton({ className }: PageLayoutSkeletonProp ))}
- + {/* Action Buttons */}
@@ -384,13 +385,16 @@ export function VendorDetailLayoutSkeleton({ className }: PageLayoutSkeletonProp {/* Alternatives Container */}
- + {/* Open Source Alternatives */}
{Array.from({ length: 6 }).map((_, i) => ( -
+
))}
@@ -400,7 +404,10 @@ export function VendorDetailLayoutSkeleton({ className }: PageLayoutSkeletonProp
{Array.from({ length: 4 }).map((_, i) => ( -
+
))}
@@ -409,7 +416,7 @@ export function VendorDetailLayoutSkeleton({ className }: PageLayoutSkeletonProp {/* About Section */}
- +
@@ -484,14 +491,14 @@ export function VendorDetailLayoutSkeleton({ className }: PageLayoutSkeletonProp
- + {/* Description */}
- + {/* Footer */}
@@ -507,7 +514,7 @@ export function VendorDetailLayoutSkeleton({ className }: PageLayoutSkeletonProp {/* Comments Section */}
- + {/* Comment Form Skeleton */}
@@ -517,7 +524,7 @@ export function VendorDetailLayoutSkeleton({ className }: PageLayoutSkeletonProp
- + {/* Description Section */}
@@ -528,14 +535,14 @@ export function VendorDetailLayoutSkeleton({ className }: PageLayoutSkeletonProp
- + {/* Send Button */}
- + {/* Sample Comment Cards */}
{Array.from({ length: 3 }).map((_, i) => ( @@ -548,12 +555,12 @@ export function VendorDetailLayoutSkeleton({ className }: PageLayoutSkeletonProp
- + {/* Timestamp */}
- + {/* Comment Content */}
@@ -574,13 +581,13 @@ export function VendorDetailLayoutSkeleton({ className }: PageLayoutSkeletonProp
{/* Deploy Button */}
- + {/* Voting Buttons */}
- + {/* GitHub Score Section */}
{/* Header */} @@ -591,7 +598,7 @@ export function VendorDetailLayoutSkeleton({ className }: PageLayoutSkeletonProp
- + {/* Stats */}
{Array.from({ length: 4 }).map((_, i) => ( @@ -605,7 +612,7 @@ export function VendorDetailLayoutSkeleton({ className }: PageLayoutSkeletonProp ))}
- + {/* Action Buttons */}
@@ -617,23 +624,22 @@ export function VendorDetailLayoutSkeleton({ className }: PageLayoutSkeletonProp
- ) + ); } /** * Stats/features section skeleton for homepage */ -export function StatsSectionSkeleton({ - className, - columns = 3 -}: PageLayoutSkeletonProps & { columns?: number }) { +export function StatsSectionSkeleton({ className, columns = 3 }: PageLayoutSkeletonProps & { columns?: number }) { return ( -
+
{Array.from({ length: columns }).map((_, i) => (
@@ -647,24 +653,19 @@ export function StatsSectionSkeleton({
))}
- ) + ); } - /** * Blog Card Grid Skeleton - Always displays exactly 4 blog card skeletons * Used for consistent blog page layout with predictable height */ export function BlogCardGridSkeleton({ className }: PageLayoutSkeletonProps) { return ( -
- +
+
- ) + ); } /** @@ -673,14 +674,10 @@ export function BlogCardGridSkeleton({ className }: PageLayoutSkeletonProps) { */ export function VendorGridSkeleton({ className }: PageLayoutSkeletonProps) { return ( -
- +
+
- ) + ); } /** @@ -689,13 +686,9 @@ export function VendorGridSkeleton({ className }: PageLayoutSkeletonProps) { */ export function SlackCommunitySkeleton() { return ( -
+
{/* Frame 651 Container */}
- {/* Title Skeleton */}
@@ -703,7 +696,6 @@ export function SlackCommunitySkeleton() { {/* Content Area - Channel List + Chat Interface */}
- {/* Channel List Skeleton */}
@@ -748,7 +740,6 @@ export function SlackCommunitySkeleton() { {/* Chat Interface Skeleton */}
- {/* Header */}
@@ -785,4 +776,4 @@ export function SlackCommunitySkeleton() {
); -} \ No newline at end of file +} diff --git a/openframe-frontend-core/src/components/loading/unified-skeleton.tsx b/openframe-frontend-core/src/components/loading/unified-skeleton.tsx index 1f5d45b11..34ecfa24f 100644 --- a/openframe-frontend-core/src/components/loading/unified-skeleton.tsx +++ b/openframe-frontend-core/src/components/loading/unified-skeleton.tsx @@ -1,32 +1,32 @@ -import type React from "react" -import { cn } from "../../utils/cn" +import type React from 'react'; +import { cn } from '../../utils/cn'; interface UnifiedSkeletonProps extends React.HTMLAttributes { - className?: string - variant?: 'default' | 'text' | 'circular' | 'rectangular' - width?: string | number - height?: string | number + className?: string; + variant?: 'default' | 'text' | 'circular' | 'rectangular'; + width?: string | number; + height?: string | number; /** * Disable animation for users who prefer reduced motion */ - animate?: boolean + animate?: boolean; /** * Accessibility label for screen readers */ - 'aria-label'?: string + 'aria-label'?: string; } /** * UnifiedSkeleton Component - * + * * Base skeleton component with consistent styling across the application. - * + * * Design Specifications: * - Color: #2A2A2A base with #3A3A3A pulse highlight * - Animation: 1.5s pulse duration, ease-in-out timing * - Responsive: Mobile-first approach with proper scaling * - Accessibility: ARIA labels and reduced motion support - * + * * @param variant - Shape variant: default (rounded), text (rounded-sm), circular, rectangular * @param width - Custom width (defaults to full width) * @param height - Custom height (defaults to auto based on variant) @@ -42,47 +42,45 @@ export function UnifiedSkeleton({ 'aria-label': ariaLabel, ...props }: UnifiedSkeletonProps) { - const baseClasses = "bg-[#2A2A2A]" - + const baseClasses = 'bg-[#2A2A2A]'; + const variantClasses = { - default: "rounded-md", - text: "rounded-sm", - circular: "rounded-full", - rectangular: "rounded-none" - } - + default: 'rounded-md', + text: 'rounded-sm', + circular: 'rounded-full', + rectangular: 'rounded-none', + }; + const defaultHeights = { - default: "h-4", - text: "h-4", - circular: "h-12 w-12", - rectangular: "h-4" - } - - const animationClasses = animate - ? "animate-pulse motion-reduce:animate-none" - : "" - + default: 'h-4', + text: 'h-4', + circular: 'h-12 w-12', + rectangular: 'h-4', + }; + + const animationClasses = animate ? 'animate-pulse motion-reduce:animate-none' : ''; + const style: React.CSSProperties = { width: width, height: height, - } - + }; + return (
- ) + ); } /** @@ -93,50 +91,40 @@ export const TextSkeleton = { * Large heading skeleton (h1, h2) */ Heading: ({ className, ...props }: Omit) => ( - ), - + /** * Medium heading skeleton (h3, h4) */ Subheading: ({ className, ...props }: Omit) => ( - ), - + /** * Body text skeleton */ Body: ({ className, ...props }: Omit) => ( - + ), - + /** * Small text skeleton (captions, metadata) */ Caption: ({ className, ...props }: Omit) => ( - + ), -} +}; /** * Interactive element skeletons @@ -146,38 +134,38 @@ export const InteractiveSkeleton = { * Button skeleton with proper touch targets */ Button: ({ className, ...props }: Omit) => ( - ), - + /** * Input field skeleton */ Input: ({ className, ...props }: Omit) => ( - ), - + /** * Filter chip skeleton */ Chip: ({ className, ...props }: Omit) => ( - ), -} +}; /** * Media skeletons for images and icons @@ -186,52 +174,60 @@ export const MediaSkeleton = { /** * Avatar/profile image skeleton */ - Avatar: ({ size = 'md', className, ...props }: Omit & { size?: 'sm' | 'md' | 'lg' }) => { + Avatar: ({ + size = 'md', + className, + ...props + }: Omit & { size?: 'sm' | 'md' | 'lg' }) => { const sizeClasses = { - sm: "h-8 w-8", - md: "h-12 w-12", - lg: "h-16 w-16" - } - + sm: 'h-8 w-8', + md: 'h-12 w-12', + lg: 'h-16 w-16', + }; + return ( - - ) + ); }, - + /** * Card image skeleton (16:9 aspect ratio) */ CardImage: ({ className, ...props }: Omit) => ( - ), - + /** * Icon skeleton */ - Icon: ({ size = 'md', className, ...props }: Omit & { size?: 'sm' | 'md' | 'lg' }) => { + Icon: ({ + size = 'md', + className, + ...props + }: Omit & { size?: 'sm' | 'md' | 'lg' }) => { const sizeClasses = { - sm: "h-4 w-4", - md: "h-6 w-6", - lg: "h-8 w-8" - } - + sm: 'h-4 w-4', + md: 'h-6 w-6', + lg: 'h-8 w-8', + }; + return ( - - ) + {...props} + /> + ); }, -} \ No newline at end of file +}; diff --git a/openframe-frontend-core/src/components/loading/users-grid-skeleton.tsx b/openframe-frontend-core/src/components/loading/users-grid-skeleton.tsx index 9715877d8..3abf14913 100644 --- a/openframe-frontend-core/src/components/loading/users-grid-skeleton.tsx +++ b/openframe-frontend-core/src/components/loading/users-grid-skeleton.tsx @@ -1,19 +1,24 @@ -import { cn } from "../../utils/cn" +import { cn } from '../../utils/cn'; interface Props { - rows?: number - className?: string + rows?: number; + className?: string; } export function UsersGridSkeleton({ rows = 10, className }: Props) { - const skeletonRows = Array.from({ length: rows }) + const skeletonRows = Array.from({ length: rows }); return (
- {['Name', 'Email', 'Role', 'Created', 'Last Sign-In'].map((h) => ( - + {['Name', 'Email', 'Role', 'Created', 'Last Sign-In'].map(h => ( + ))} @@ -30,5 +35,5 @@ export function UsersGridSkeleton({ rows = 10, className }: Props) {
{h} + {h} +
- ) -} \ No newline at end of file + ); +} diff --git a/openframe-frontend-core/src/components/loading/wizard-layout-skeleton.tsx b/openframe-frontend-core/src/components/loading/wizard-layout-skeleton.tsx index 403a4271e..5439091d7 100644 --- a/openframe-frontend-core/src/components/loading/wizard-layout-skeleton.tsx +++ b/openframe-frontend-core/src/components/loading/wizard-layout-skeleton.tsx @@ -1,10 +1,10 @@ -import React from 'react' -import { cn } from "../../utils/cn" -import { UnifiedSkeleton, TextSkeleton } from './unified-skeleton' +import React from 'react'; +import { cn } from '../../utils/cn'; +import { TextSkeleton, UnifiedSkeleton } from './unified-skeleton'; interface WizardLayoutSkeletonProps { - steps?: number - className?: string + steps?: number; + className?: string; } export function WizardLayoutSkeleton({ steps = 6, className }: WizardLayoutSkeletonProps) { @@ -29,5 +29,5 @@ export function WizardLayoutSkeleton({ steps = 6, className }: WizardLayoutSkele
- ) -} \ No newline at end of file + ); +} diff --git a/openframe-frontend-core/src/components/log-severity-dot.tsx b/openframe-frontend-core/src/components/log-severity-dot.tsx index 5fc41d4ba..108bdee00 100644 --- a/openframe-frontend-core/src/components/log-severity-dot.tsx +++ b/openframe-frontend-core/src/components/log-severity-dot.tsx @@ -1,42 +1,34 @@ -import * as React from 'react' -import { cn } from '../utils/cn' -import { LogSeverityDotProps, LOG_SEVERITY_COLORS, LOG_SEVERITY_LABELS } from '../types/logs.types' +import * as React from 'react'; +import { LOG_SEVERITY_COLORS, LOG_SEVERITY_LABELS, LogSeverityDotProps } from '../types/logs.types'; +import { cn } from '../utils/cn'; -export const LogSeverityDot = React.forwardRef< - HTMLDivElement, - LogSeverityDotProps ->(({ severity, size = 'md', className }, ref) => { - const sizeClasses = { - sm: 'w-4 h-4', - md: 'w-6 h-6', - lg: 'w-8 h-8' - } +export const LogSeverityDot = React.forwardRef( + ({ severity, size = 'md', className }, ref) => { + const sizeClasses = { + sm: 'w-4 h-4', + md: 'w-6 h-6', + lg: 'w-8 h-8', + }; - const dotSizeClasses = { - sm: 'w-2 h-2', - md: 'w-3 h-3', - lg: 'w-4 h-4' - } + const dotSizeClasses = { + sm: 'w-2 h-2', + md: 'w-3 h-3', + lg: 'w-4 h-4', + }; - return ( -
+ return (
-
- ) -}) + ref={ref} + className={cn('relative flex items-center justify-center shrink-0', sizeClasses[size], className)} + aria-label={`${LOG_SEVERITY_LABELS[severity]} severity`} + > +
+
+ ); + }, +); -LogSeverityDot.displayName = 'LogSeverityDot' \ No newline at end of file +LogSeverityDot.displayName = 'LogSeverityDot'; diff --git a/openframe-frontend-core/src/components/logs-list.tsx b/openframe-frontend-core/src/components/logs-list.tsx index 4a09196f4..03430c903 100644 --- a/openframe-frontend-core/src/components/logs-list.tsx +++ b/openframe-frontend-core/src/components/logs-list.tsx @@ -1,162 +1,157 @@ -import * as React from 'react' -import { cn } from '../utils/cn' -import { LogsListProps, LogEntry } from '../types/logs.types' -import { LogSeverityDot } from './log-severity-dot' -import { ToolIcon } from './tool-icon' +import * as React from 'react'; +import { LogEntry, LogsListProps } from '../types/logs.types'; +import { cn } from '../utils/cn'; +import { LogSeverityDot } from './log-severity-dot'; +import { ToolIcon } from './tool-icon'; const formatTimestamp = (timestamp: string | Date): string => { - const date = timestamp instanceof Date ? timestamp : new Date(timestamp) - - const year = date.getFullYear() - const month = String(date.getMonth() + 1).padStart(2, '0') - const day = String(date.getDate()).padStart(2, '0') - const hours = String(date.getHours()).padStart(2, '0') - const minutes = String(date.getMinutes()).padStart(2, '0') - - return `${year}/${month}/${day},${hours}:${minutes}` -} + const date = timestamp instanceof Date ? timestamp : new Date(timestamp); + + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); + const day = String(date.getDate()).padStart(2, '0'); + const hours = String(date.getHours()).padStart(2, '0'); + const minutes = String(date.getMinutes()).padStart(2, '0'); + + return `${year}/${month}/${day},${hours}:${minutes}`; +}; const LogCard: React.FC<{ - log: LogEntry - isLast: boolean - showConnector: boolean - onClick?: () => void + log: LogEntry; + isLast: boolean; + showConnector: boolean; + onClick?: () => void; }> = ({ log, isLast, showConnector, onClick }) => { return (
{ + onKeyDown={e => { if (e.key === 'Enter' || e.key === ' ') { - e.preventDefault() - onClick?.() + e.preventDefault(); + onClick?.(); } }} > - +
-

- {log.title} -

+

{log.title}

{formatTimestamp(log.timestamp)}

- {log.toolType && ( - - )} + {log.toolType && }
- + {showConnector && !isLast && ( - - ) -} + ); +}; -export const LogsList = React.forwardRef< - HTMLDivElement, - LogsListProps ->(({ - logs, - maxHeight = '400px', - showConnector = true, - onLogClick, - loading = false, - emptyMessage = 'No logs to display', - className -}, ref) => { - const containerRef = React.useRef(null) +export const LogsList = React.forwardRef( + ( + { + logs, + maxHeight = '400px', + showConnector = true, + onLogClick, + loading = false, + emptyMessage = 'No logs to display', + className, + }, + ref, + ) => { + const containerRef = React.useRef(null); - const isFullHeight = maxHeight === '100%' + const isFullHeight = maxHeight === '100%'; - const getContainerStyles = () => { - if (isFullHeight) return undefined - return { maxHeight, minHeight: '200px' } - } + const getContainerStyles = () => { + if (isFullHeight) return undefined; + return { maxHeight, minHeight: '200px' }; + }; - const getContainerClasses = () => { - if (isFullHeight) return 'h-full' - return '' - } + const getContainerClasses = () => { + if (isFullHeight) return 'h-full'; + return ''; + }; - if (loading) { - return ( -
-
Loading logs...
-
- ) - } + if (loading) { + return ( +
+
Loading logs...
+
+ ); + } + + if (logs.length === 0) { + return ( +
+
{emptyMessage}
+
+ ); + } - if (logs.length === 0) { return ( -
-
{emptyMessage}
-
- ) - } - - return ( -
-
- {logs.map((log, index) => ( - onLogClick?.(log)} - /> - ))} +
+ {logs.map((log, index) => ( + onLogClick?.(log)} + /> + ))} +
-
- ) -}) + ); + }, +); -LogsList.displayName = 'LogsList' \ No newline at end of file +LogsList.displayName = 'LogsList'; diff --git a/openframe-frontend-core/src/components/made-with-love.tsx b/openframe-frontend-core/src/components/made-with-love.tsx index 15b434666..bdc3f6c65 100644 --- a/openframe-frontend-core/src/components/made-with-love.tsx +++ b/openframe-frontend-core/src/components/made-with-love.tsx @@ -1,8 +1,8 @@ -'use client' +'use client'; -import React, { useState, useEffect } from 'react'; -import { FlamingoLogo } from './flamingo-logo'; +import React, { useEffect, useState } from 'react'; import { getBaseUrl } from '../utils'; +import { FlamingoLogo } from './flamingo-logo'; interface MadeWithLoveProps { /** Custom class name for the container */ @@ -13,11 +13,7 @@ interface MadeWithLoveProps { showOnMobile?: boolean; } -export function MadeWithLove({ - className = '', - size = 'md', - showOnMobile = true -}: MadeWithLoveProps) { +export function MadeWithLove({ className = '', size = 'md', showOnMobile = true }: MadeWithLoveProps) { const [isMobile, setIsMobile] = useState(false); const [isHovered, setIsHovered] = useState(false); @@ -38,20 +34,20 @@ export function MadeWithLove({ logoSize: 14, fontSizeMobile: '12px', fontSizeDesktop: '14px', - gap: '4px' + gap: '4px', }, md: { logoSize: 16, fontSizeMobile: '14px', fontSizeDesktop: '16px', - gap: '4px' + gap: '4px', }, lg: { logoSize: 20, fontSizeMobile: '16px', fontSizeDesktop: '18px', - gap: '6px' - } + gap: '6px', + }, }; const config = sizeConfig[size]; @@ -59,13 +55,13 @@ export function MadeWithLove({ // Container styles using primitive CSS const containerStyle: React.CSSProperties = { - display: (!showOnMobile && isMobile) ? 'none' : 'inline-flex', + display: !showOnMobile && isMobile ? 'none' : 'inline-flex', alignItems: 'center', gap: config.gap, fontSize: isMobile ? config.fontSizeMobile : config.fontSizeDesktop, lineHeight: 1.5, color: '#ffffff', - fontFamily: 'inherit' + fontFamily: 'inherit', }; // Button/link styles using primitive CSS @@ -83,7 +79,7 @@ export function MadeWithLove({ fontSize: '14px', fontFamily: '"Azeret Mono", monospace', transition: 'text-decoration 0.2s ease', - outline: 'none' + outline: 'none', }; // Logo container styles @@ -91,21 +87,18 @@ export function MadeWithLove({ marginLeft: '2px', flexShrink: 0, display: 'flex', - alignItems: 'center' + alignItems: 'center', }; // Text span styles const textStyle: React.CSSProperties = { color: '#ffffff', marginLeft: '4px', - textDecoration: 'none' + textDecoration: 'none', }; return ( -
+
Made with love by
); -} \ No newline at end of file +} diff --git a/openframe-frontend-core/src/components/media-carousel.tsx b/openframe-frontend-core/src/components/media-carousel.tsx index 0f6535d19..17582167a 100644 --- a/openframe-frontend-core/src/components/media-carousel.tsx +++ b/openframe-frontend-core/src/components/media-carousel.tsx @@ -1,26 +1,26 @@ -"use client"; +'use client'; -import { useState, useRef, useEffect, memo, useCallback } from 'react'; -import { cn } from "../utils/cn"; -import { MediaItem } from '../utils/media-carousel-utils-stub'; +import { memo, useCallback, useEffect, useRef, useState } from 'react'; +import { cn } from '../utils/cn'; import { LiteYouTubeEmbed } from '../utils/lite-youtube-embed-stub'; +import { MediaItem } from '../utils/media-carousel-utils-stub'; // Navigation icons const ChevronLeftIcon = () => ( - + ); const ChevronRightIcon = () => ( - + ); const PlayIcon = () => ( - + ); @@ -105,63 +105,52 @@ const CarouselYouTubeEmbed = ({ videoId, title }: { videoId: string; title: stri ); }; -export const MediaCarousel = memo(function MediaCarousel({ - media, +export const MediaCarousel = memo(function MediaCarousel({ + media, className, - aspectRatio = "16/9", + aspectRatio = '16/9', showThumbnails = true, autoPlay = false, - objectFit = 'contain' + objectFit = 'contain', }: MediaCarouselProps) { const [currentIndex, setCurrentIndex] = useState(0); const [touchStart, setTouchStart] = useState(null); const [touchEnd, setTouchEnd] = useState(null); - const [playingVideos, setPlayingVideos] = useState>(new Set()); + const [_playingVideos, setPlayingVideos] = useState>(new Set()); const carouselRef = useRef(null); const thumbnailsRef = useRef(null); - // Early return if no media provided - if (!media || media.length === 0) { - return null; - } - - const currentItem = media[currentIndex] || media[0]; - - // Additional safety check - if (!currentItem) { - return null; - } - // Handle video play/pause - const handleVideoClick = useCallback((index: number) => { - const item = media[index]; - if (item.type !== 'video') return; - - // Find the video element - const videoElements = document.querySelectorAll(`video[data-video-index="${index}"]`); - const video = videoElements[0] as HTMLVideoElement; - - if (!video) { - console.log('❌ Video element not found for index:', index); - return; - } - - if (video.paused) { - const playPromise = video.play(); - - if (playPromise !== undefined) { - playPromise - .then(() => { - const playButton = video.parentElement?.querySelector('.video-play-button'); - if (playButton) { - (playButton as HTMLElement).style.display = 'none'; - } - }) - .catch((error) => { - if (error.name === 'NotSupportedError' || error.name === 'MediaElementError') { - const fallbackDiv = document.createElement('div'); - fallbackDiv.className = 'absolute inset-0 flex items-center justify-center bg-ods-card text-center p-4'; - fallbackDiv.innerHTML = ` + const handleVideoClick = useCallback( + (index: number) => { + const item = media[index]; + if (item.type !== 'video') return; + + // Find the video element + const videoElements = document.querySelectorAll(`video[data-video-index="${index}"]`); + const video = videoElements[0] as HTMLVideoElement; + + if (!video) { + console.log('❌ Video element not found for index:', index); + return; + } + + if (video.paused) { + const playPromise = video.play(); + + if (playPromise !== undefined) { + playPromise + .then(() => { + const playButton = video.parentElement?.querySelector('.video-play-button'); + if (playButton) { + (playButton as HTMLElement).style.display = 'none'; + } + }) + .catch(error => { + if (error.name === 'NotSupportedError' || error.name === 'MediaElementError') { + const fallbackDiv = document.createElement('div'); + fallbackDiv.className = 'absolute inset-0 flex items-center justify-center bg-ods-card text-center p-4'; + fallbackDiv.innerHTML = ` `; - video.parentElement?.appendChild(fallbackDiv); - } - }); - } - } else { - video.pause(); - const playButton = video.parentElement?.querySelector('.video-play-button'); - if (playButton) { - (playButton as HTMLElement).style.display = 'flex'; + video.parentElement?.appendChild(fallbackDiv); + } + }); + } + } else { + video.pause(); + const playButton = video.parentElement?.querySelector('.video-play-button'); + if (playButton) { + (playButton as HTMLElement).style.display = 'flex'; + } } - } - }, [media]); + }, + [media], + ); // Navigation functions const nextSlide = useCallback(() => { @@ -190,7 +181,7 @@ export const MediaCarousel = memo(function MediaCarousel({ currentVideo.pause(); } setPlayingVideos(new Set()); - setCurrentIndex((prev) => (prev + 1) % media.length); + setCurrentIndex(prev => (prev + 1) % media.length); }, [currentIndex, media.length]); const prevSlide = useCallback(() => { @@ -199,32 +190,50 @@ export const MediaCarousel = memo(function MediaCarousel({ currentVideo.pause(); } setPlayingVideos(new Set()); - setCurrentIndex((prev) => (prev - 1 + media.length) % media.length); + setCurrentIndex(prev => (prev - 1 + media.length) % media.length); }, [currentIndex, media.length]); - const selectSlide = useCallback((index: number) => { - if (index === currentIndex) return; - - const currentVideo = document.querySelector(`[data-video-index="${currentIndex}"]`) as HTMLVideoElement; - if (currentVideo && !currentVideo.paused) { - currentVideo.pause(); - } - setPlayingVideos(new Set()); - setCurrentIndex(index); - }, [currentIndex]); + const selectSlide = useCallback( + (index: number) => { + if (index === currentIndex) return; + + const currentVideo = document.querySelector(`[data-video-index="${currentIndex}"]`) as HTMLVideoElement; + if (currentVideo && !currentVideo.paused) { + currentVideo.pause(); + } + setPlayingVideos(new Set()); + setCurrentIndex(index); + }, + [currentIndex], + ); // Keyboard navigation - only when carousel is focused - const handleKeyDown = useCallback((e: React.KeyboardEvent) => { - if (media.length <= 1) return; - - if (e.key === 'ArrowLeft') { - e.preventDefault(); - prevSlide(); - } else if (e.key === 'ArrowRight') { - e.preventDefault(); - nextSlide(); - } - }, [nextSlide, prevSlide, media.length]); + const handleKeyDown = useCallback( + (e: React.KeyboardEvent) => { + if (media.length <= 1) return; + + if (e.key === 'ArrowLeft') { + e.preventDefault(); + prevSlide(); + } else if (e.key === 'ArrowRight') { + e.preventDefault(); + nextSlide(); + } + }, + [nextSlide, prevSlide, media.length], + ); + + // Early return if no media provided + if (!media || media.length === 0) { + return null; + } + + const currentItem = media[currentIndex] || media[0]; + + // Additional safety check + if (!currentItem) { + return null; + } // Touch/swipe handling const minSwipeDistance = 50; @@ -240,7 +249,7 @@ export const MediaCarousel = memo(function MediaCarousel({ const onTouchEnd = () => { if (!touchStart || !touchEnd) return; - + const distance = touchStart - touchEnd; const isLeftSwipe = distance > minSwipeDistance; const isRightSwipe = distance < -minSwipeDistance; @@ -275,7 +284,7 @@ export const MediaCarousel = memo(function MediaCarousel({ // Render video const renderVideo = (item: MediaItem, index: number) => (
- - + {/* Play Button Overlay */} -
handleVideoClick(index)} > @@ -344,12 +357,12 @@ export const MediaCarousel = memo(function MediaCarousel({ // Render image const renderImage = (item: MediaItem, index: number) => (
- {item.alt { + onError={e => { console.log('❌ Image failed to load:', item.src); const target = e.target as HTMLImageElement; target.style.display = 'none'; @@ -361,7 +374,7 @@ export const MediaCarousel = memo(function MediaCarousel({ // Render main media item const renderMainMedia = (item: MediaItem, index: number) => { console.log('🎬 Rendering media item:', { type: item.type, src: item.src.substring(0, 100) + '...' }); - + switch (item.type) { case 'youtube': return renderYouTubeEmbed(item, index); @@ -376,7 +389,7 @@ export const MediaCarousel = memo(function MediaCarousel({ // Render thumbnail const renderThumbnail = (item: MediaItem, index: number) => { const isActive = index === currentIndex; - + let thumbnailSrc = item.src; if (item.type === 'youtube') { const videoId = getYouTubeVideoId(item.src); @@ -390,11 +403,9 @@ export const MediaCarousel = memo(function MediaCarousel({ key={index} onClick={() => selectSlide(index)} className={cn( - "relative flex-shrink-0 overflow-hidden transition-all duration-200", - "w-20 h-20 md:w-24 md:h-24 rounded-lg border-2", - isActive - ? "border-[#FFC008] ring-2 ring-[#FFC008]/20" - : "border-ods-border hover:border-[#888888]" + 'relative flex-shrink-0 overflow-hidden transition-all duration-200', + 'w-20 h-20 md:w-24 md:h-24 rounded-lg border-2', + isActive ? 'border-[#FFC008] ring-2 ring-[#FFC008]/20' : 'border-ods-border hover:border-[#888888]', )} > - + {/* Play icon overlay for videos */} {(item.type === 'video' || item.type === 'youtube') && (
- +
)} - + {/* Active indicator */} - {isActive && ( -
- )} + {isActive &&
} ); }; @@ -439,21 +448,21 @@ export const MediaCarousel = memo(function MediaCarousel({ }; return ( -
+
{/* Main Display Area with Fixed Aspect Ratio */} -
1 ? handleKeyDown : undefined} tabIndex={media.length > 1 ? 0 : undefined} - role={media.length > 1 ? "region" : undefined} - aria-label={media.length > 1 ? "Media carousel, use arrow keys to navigate" : undefined} + role={media.length > 1 ? 'region' : undefined} + aria-label={media.length > 1 ? 'Media carousel, use arrow keys to navigate' : undefined} > {/* Media content */} {renderMainMedia(currentItem, currentIndex)} @@ -488,7 +497,7 @@ export const MediaCarousel = memo(function MediaCarousel({ {/* Thumbnail Navigation - only show if multiple items and showThumbnails is true */} {media.length > 1 && showThumbnails && (
-
); -}); \ No newline at end of file +}); diff --git a/openframe-frontend-core/src/components/menu-icon.tsx b/openframe-frontend-core/src/components/menu-icon.tsx index 14811996b..089ac67e0 100644 --- a/openframe-frontend-core/src/components/menu-icon.tsx +++ b/openframe-frontend-core/src/components/menu-icon.tsx @@ -1,5 +1,5 @@ -import React from 'react'; import { Menu } from 'lucide-react'; +import React from 'react'; interface MenuIconProps { className?: string; @@ -7,16 +7,6 @@ interface MenuIconProps { height?: number; } -export const MenuIcon: React.FC = ({ - className = "", - width = 24, - height = 24 -}) => { - return ( - - ); -}; \ No newline at end of file +export const MenuIcon: React.FC = ({ className = '', width = 24, height = 24 }) => { + return ; +}; diff --git a/openframe-frontend-core/src/components/menubar.tsx b/openframe-frontend-core/src/components/menubar.tsx index 64fcaa3c0..7a1d3b23c 100644 --- a/openframe-frontend-core/src/components/menubar.tsx +++ b/openframe-frontend-core/src/components/menubar.tsx @@ -1,20 +1,20 @@ -"use client" +'use client'; -import * as React from "react" -import * as MenubarPrimitive from "@radix-ui/react-menubar" -import { Check, ChevronRight, Circle } from "lucide-react" +import * as MenubarPrimitive from '@radix-ui/react-menubar'; +import { Check, ChevronRight, Circle } from 'lucide-react'; +import * as React from 'react'; -import { cn } from "../utils/cn" +import { cn } from '../utils/cn'; -const MenubarMenu = MenubarPrimitive.Menu +const MenubarMenu = MenubarPrimitive.Menu; -const MenubarGroup = MenubarPrimitive.Group +const MenubarGroup = MenubarPrimitive.Group; -const MenubarPortal = MenubarPrimitive.Portal +const MenubarPortal = MenubarPrimitive.Portal; -const MenubarSub = MenubarPrimitive.Sub +const MenubarSub = MenubarPrimitive.Sub; -const MenubarRadioGroup = MenubarPrimitive.RadioGroup +const MenubarRadioGroup = MenubarPrimitive.RadioGroup; const Menubar = React.forwardRef< React.ElementRef, @@ -22,14 +22,11 @@ const Menubar = React.forwardRef< >(({ className, ...props }, ref) => ( -)) -Menubar.displayName = MenubarPrimitive.Root.displayName +)); +Menubar.displayName = MenubarPrimitive.Root.displayName; const MenubarTrigger = React.forwardRef< React.ElementRef, @@ -38,34 +35,34 @@ const MenubarTrigger = React.forwardRef< -)) -MenubarTrigger.displayName = MenubarPrimitive.Trigger.displayName +)); +MenubarTrigger.displayName = MenubarPrimitive.Trigger.displayName; const MenubarSubTrigger = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef & { - inset?: boolean + inset?: boolean; } >(({ className, inset, children, ...props }, ref) => ( {children} -)) -MenubarSubTrigger.displayName = MenubarPrimitive.SubTrigger.displayName +)); +MenubarSubTrigger.displayName = MenubarPrimitive.SubTrigger.displayName; const MenubarSubContent = React.forwardRef< React.ElementRef, @@ -74,56 +71,51 @@ const MenubarSubContent = React.forwardRef< -)) -MenubarSubContent.displayName = MenubarPrimitive.SubContent.displayName +)); +MenubarSubContent.displayName = MenubarPrimitive.SubContent.displayName; const MenubarContent = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef ->( - ( - { className, align = "start", alignOffset = -4, sideOffset = 8, ...props }, - ref - ) => ( - - - - ) -) -MenubarContent.displayName = MenubarPrimitive.Content.displayName +>(({ className, align = 'start', alignOffset = -4, sideOffset = 8, ...props }, ref) => ( + + + +)); +MenubarContent.displayName = MenubarPrimitive.Content.displayName; const MenubarItem = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef & { - inset?: boolean + inset?: boolean; } >(({ className, inset, ...props }, ref) => ( -)) -MenubarItem.displayName = MenubarPrimitive.Item.displayName +)); +MenubarItem.displayName = MenubarPrimitive.Item.displayName; const MenubarCheckboxItem = React.forwardRef< React.ElementRef, @@ -132,8 +124,8 @@ const MenubarCheckboxItem = React.forwardRef< {children} -)) -MenubarCheckboxItem.displayName = MenubarPrimitive.CheckboxItem.displayName +)); +MenubarCheckboxItem.displayName = MenubarPrimitive.CheckboxItem.displayName; const MenubarRadioItem = React.forwardRef< React.ElementRef, @@ -155,8 +147,8 @@ const MenubarRadioItem = React.forwardRef< @@ -167,54 +159,35 @@ const MenubarRadioItem = React.forwardRef< {children} -)) -MenubarRadioItem.displayName = MenubarPrimitive.RadioItem.displayName +)); +MenubarRadioItem.displayName = MenubarPrimitive.RadioItem.displayName; const MenubarLabel = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef & { - inset?: boolean + inset?: boolean; } >(({ className, inset, ...props }, ref) => ( -)) -MenubarLabel.displayName = MenubarPrimitive.Label.displayName +)); +MenubarLabel.displayName = MenubarPrimitive.Label.displayName; const MenubarSeparator = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - -)) -MenubarSeparator.displayName = MenubarPrimitive.Separator.displayName - -const MenubarShortcut = ({ - className, - ...props -}: React.HTMLAttributes) => { - return ( - - ) -} -MenubarShortcut.displayname = "MenubarShortcut" + +)); +MenubarSeparator.displayName = MenubarPrimitive.Separator.displayName; + +const MenubarShortcut = ({ className, ...props }: React.HTMLAttributes) => { + return ; +}; +MenubarShortcut.displayname = 'MenubarShortcut'; export { Menubar, @@ -233,4 +206,4 @@ export { MenubarGroup, MenubarSub, MenubarShortcut, -} +}; diff --git a/openframe-frontend-core/src/components/metric-value.tsx b/openframe-frontend-core/src/components/metric-value.tsx index 7f694fee6..0c966859d 100644 --- a/openframe-frontend-core/src/components/metric-value.tsx +++ b/openframe-frontend-core/src/components/metric-value.tsx @@ -1,7 +1,7 @@ -"use client" +'use client'; import React from 'react'; -import { cn } from "../utils/cn"; +import { cn } from '../utils/cn'; interface MetricValueProps { value: string | number; @@ -15,11 +15,14 @@ interface MetricValueProps { */ export function MetricValue({ value, label, className }: MetricValueProps) { return ( -
+
{value} - - {label} - + {label}
); -} \ No newline at end of file +} diff --git a/openframe-frontend-core/src/components/minus-circle-icon.tsx b/openframe-frontend-core/src/components/minus-circle-icon.tsx index 1e28a9f2d..78d7dcdd7 100644 --- a/openframe-frontend-core/src/components/minus-circle-icon.tsx +++ b/openframe-frontend-core/src/components/minus-circle-icon.tsx @@ -1,28 +1,28 @@ -import React from 'react' +import React from 'react'; interface MinusCircleIconProps extends React.SVGProps { - className?: string + className?: string; } export function MinusCircleIcon({ className, ...props }: MinusCircleIconProps) { return ( - - - - ) -} \ No newline at end of file + ); +} diff --git a/openframe-frontend-core/src/components/moon-icon.tsx b/openframe-frontend-core/src/components/moon-icon.tsx index c88b99f02..4efb6c70b 100644 --- a/openframe-frontend-core/src/components/moon-icon.tsx +++ b/openframe-frontend-core/src/components/moon-icon.tsx @@ -6,21 +6,20 @@ interface MoonIconProps { height?: number; } -export const MoonIcon: React.FC = ({ - className = "", - width = 23, - height = 24 -}) => { +export const MoonIcon: React.FC = ({ className = '', width = 23, height = 24 }) => { return ( - - + ); -}; \ No newline at end of file +}; diff --git a/openframe-frontend-core/src/components/ms-icon.tsx b/openframe-frontend-core/src/components/ms-icon.tsx index 46800d303..b8addfc29 100644 --- a/openframe-frontend-core/src/components/ms-icon.tsx +++ b/openframe-frontend-core/src/components/ms-icon.tsx @@ -1,18 +1,14 @@ -import React from 'react' +import React from 'react'; interface MsIconProps { - width?: number | string - height?: number | string - className?: string + width?: number | string; + height?: number | string; + className?: string; } -export function MicrosoftIcon({ - width = 24, - height = 24, - className = "text-current" - }: MsIconProps) { +export function MicrosoftIcon({ width = 24, height = 24, className = 'text-current' }: MsIconProps) { return ( - - - - - + + + + - ) -} \ No newline at end of file + ); +} diff --git a/openframe-frontend-core/src/components/msp-display.tsx b/openframe-frontend-core/src/components/msp-display.tsx index d6ec63e77..7cde7ab35 100644 --- a/openframe-frontend-core/src/components/msp-display.tsx +++ b/openframe-frontend-core/src/components/msp-display.tsx @@ -1,23 +1,21 @@ -"use client" +'use client'; import React from 'react'; -import { cn } from "../utils/cn"; +import { cn } from '../utils/cn'; import { SquareAvatar } from './square-avatar'; -interface MSPDisplayProps { +interface MspDisplayProps { name: string; logoUrl?: string | null; size?: number; // avatar size in px (square) className?: string; } -export function MSPDisplay({ name, logoUrl, size = 40, className }: MSPDisplayProps) { +export function MSPDisplay({ name, logoUrl, size = 40, className }: MspDisplayProps) { return (
-

- {name} -

+

{name}

); -} \ No newline at end of file +} diff --git a/openframe-frontend-core/src/components/navigation-menu.tsx b/openframe-frontend-core/src/components/navigation-menu.tsx index 8dea5fb51..a1d67d53f 100644 --- a/openframe-frontend-core/src/components/navigation-menu.tsx +++ b/openframe-frontend-core/src/components/navigation-menu.tsx @@ -1,9 +1,9 @@ -import * as React from "react" -import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu" -import { cva } from "class-variance-authority" -import { ChevronDown } from "lucide-react" +import * as NavigationMenuPrimitive from '@radix-ui/react-navigation-menu'; +import { cva } from 'class-variance-authority'; +import { ChevronDown } from 'lucide-react'; +import * as React from 'react'; -import { cn } from "../utils/cn" +import { cn } from '../utils/cn'; const NavigationMenu = React.forwardRef< React.ElementRef, @@ -11,17 +11,14 @@ const NavigationMenu = React.forwardRef< >(({ className, children, ...props }, ref) => ( {children} -)) -NavigationMenu.displayName = NavigationMenuPrimitive.Root.displayName +)); +NavigationMenu.displayName = NavigationMenuPrimitive.Root.displayName; const NavigationMenuList = React.forwardRef< React.ElementRef, @@ -29,20 +26,17 @@ const NavigationMenuList = React.forwardRef< >(({ className, ...props }, ref) => ( -)) -NavigationMenuList.displayName = NavigationMenuPrimitive.List.displayName +)); +NavigationMenuList.displayName = NavigationMenuPrimitive.List.displayName; -const NavigationMenuItem = NavigationMenuPrimitive.Item +const NavigationMenuItem = NavigationMenuPrimitive.Item; const navigationMenuTriggerStyle = cva( - "group inline-flex h-10 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-accent/50 data-[state=open]:bg-accent/50" -) + 'group inline-flex h-10 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-accent/50 data-[state=open]:bg-accent/50', +); const NavigationMenuTrigger = React.forwardRef< React.ElementRef, @@ -50,17 +44,17 @@ const NavigationMenuTrigger = React.forwardRef< >(({ className, children, ...props }, ref) => ( - {children}{" "} + {children}{' '} -)) -NavigationMenuTrigger.displayName = NavigationMenuPrimitive.Trigger.displayName +)); +NavigationMenuTrigger.displayName = NavigationMenuPrimitive.Trigger.displayName; const NavigationMenuContent = React.forwardRef< React.ElementRef, @@ -69,33 +63,32 @@ const NavigationMenuContent = React.forwardRef< -)) -NavigationMenuContent.displayName = NavigationMenuPrimitive.Content.displayName +)); +NavigationMenuContent.displayName = NavigationMenuPrimitive.Content.displayName; -const NavigationMenuLink = NavigationMenuPrimitive.Link +const NavigationMenuLink = NavigationMenuPrimitive.Link; const NavigationMenuViewport = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( -
+
-)) -NavigationMenuViewport.displayName = - NavigationMenuPrimitive.Viewport.displayName +)); +NavigationMenuViewport.displayName = NavigationMenuPrimitive.Viewport.displayName; const NavigationMenuIndicator = React.forwardRef< React.ElementRef, @@ -104,16 +97,15 @@ const NavigationMenuIndicator = React.forwardRef<
-)) -NavigationMenuIndicator.displayName = - NavigationMenuPrimitive.Indicator.displayName +)); +NavigationMenuIndicator.displayName = NavigationMenuPrimitive.Indicator.displayName; export { navigationMenuTriggerStyle, @@ -125,4 +117,4 @@ export { NavigationMenuLink, NavigationMenuIndicator, NavigationMenuViewport, -} +}; diff --git a/openframe-frontend-core/src/components/navigation/app-header.tsx b/openframe-frontend-core/src/components/navigation/app-header.tsx index a2986a2ea..31c4cca6b 100644 --- a/openframe-frontend-core/src/components/navigation/app-header.tsx +++ b/openframe-frontend-core/src/components/navigation/app-header.tsx @@ -1,36 +1,36 @@ -'use client' +'use client'; -import { Bell } from 'lucide-react' -import { useMdUp } from '../../hooks/ui/use-media-query' -import { cn } from '../../utils/cn' -import { LogOutIcon, OpenFrameLogo, OpenFrameText, UserIcon } from '../icons' -import { Menu01Icon, SearchIcon, XmarkIcon } from '../icons-v2-generated' -import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, SquareAvatar } from '../ui' -import { HeaderButton } from './header-button' -import { HeaderGlobalSearch } from './header-global-search' -import { HeaderOrganizationFilter } from './header-organization-filter' +import { Bell } from 'lucide-react'; +import { useMdUp } from '../../hooks/ui/use-media-query'; +import { cn } from '../../utils/cn'; +import { LogOutIcon, OpenFrameLogo, OpenFrameText, UserIcon } from '../icons'; +import { Menu01Icon, SearchIcon, XmarkIcon } from '../icons-v2-generated'; +import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, SquareAvatar } from '../ui'; +import { HeaderButton } from './header-button'; +import { HeaderGlobalSearch } from './header-global-search'; +import { HeaderOrganizationFilter } from './header-organization-filter'; export interface AppHeaderProps { - showSearch?: boolean - onSearch?: (query: string) => void - showOrganizations?: boolean - organizations?: { id: string; name: string }[] - selectedOrgId?: string - onOrgChange?: (id: string) => void - showNotifications?: boolean - unreadCount?: number + showSearch?: boolean; + onSearch?: (query: string) => void; + showOrganizations?: boolean; + organizations?: { id: string; name: string }[]; + selectedOrgId?: string; + onOrgChange?: (id: string) => void; + showNotifications?: boolean; + unreadCount?: number; // User block - showUser?: boolean - userName?: string - userEmail?: string - userAvatarUrl?: string | null - onProfile?: () => void - onLogout?: () => void - className?: string + showUser?: boolean; + userName?: string; + userEmail?: string; + userAvatarUrl?: string | null; + onProfile?: () => void; + onLogout?: () => void; + className?: string; /** Whether the mobile menu is open */ - isMobileMenuOpen: boolean + isMobileMenuOpen: boolean; /** Callback to toggle mobile menu */ - onToggleMobileMenu?: () => void + onToggleMobileMenu?: () => void; } export function AppHeader({ @@ -50,43 +50,46 @@ export function AppHeader({ onLogout, className, isMobileMenuOpen, - onToggleMobileMenu -}: AppHeaderProps) { - const isMdUp = useMdUp() ?? false - + onToggleMobileMenu, +}: AppHeaderProps) { + const isMdUp = useMdUp() ?? false; + return (
{/* Mobile: Burger Menu Button */} - {!isMdUp && : } - aria-label={isMobileMenuOpen ? "Close menu" : "Open menu"} - aria-expanded={isMobileMenuOpen} - />} + {!isMdUp && ( + : } + aria-label={isMobileMenuOpen ? 'Close menu' : 'Open menu'} + aria-expanded={isMobileMenuOpen} + /> + )} {/* Mobile: Logo section */} - {!isMdUp &&
- - -
} + {!isMdUp && ( +
+ + +
+ )} {/* Desktop: Global Search */} {showSearch ? ( - - ) :
} + + ) : ( +
+ )} {/* Mobile: Search button */} {showSearch && ( @@ -110,14 +113,16 @@ export function AppHeader({ {isMdUp && showUser && ( - } + + } aria-label="User" /> @@ -136,15 +141,9 @@ export function AppHeader({ />
{userName && ( -
- {userName} -
- )} - {userEmail && ( -
- {userEmail} -
+
{userName}
)} + {userEmail &&
{userEmail}
}
@@ -189,7 +188,7 @@ export function AppHeader({ /> )}
- ) + ); } -export default AppHeader +export default AppHeader; diff --git a/openframe-frontend-core/src/components/navigation/app-layout.tsx b/openframe-frontend-core/src/components/navigation/app-layout.tsx index 717503843..f6e196d62 100644 --- a/openframe-frontend-core/src/components/navigation/app-layout.tsx +++ b/openframe-frontend-core/src/components/navigation/app-layout.tsx @@ -1,20 +1,20 @@ -'use client' +'use client'; -import { Suspense, useCallback, useState } from 'react' -import { NavigationSidebarConfig } from '../../types/navigation' -import { cn } from '../../utils' -import { AppHeader, AppHeaderProps } from './app-header' -import { MobileBurgerMenu, MobileBurgerMenuProps } from './mobile-burger-menu' -import { NavigationSidebar } from './navigation-sidebar' +import { Suspense, useCallback, useState } from 'react'; +import { NavigationSidebarConfig } from '../../types/navigation'; +import { cn } from '../../utils'; +import { AppHeader, AppHeaderProps } from './app-header'; +import { MobileBurgerMenu, MobileBurgerMenuProps } from './mobile-burger-menu'; +import { NavigationSidebar } from './navigation-sidebar'; export interface AppLayoutProps { - children: React.ReactNode - sidebarConfig: NavigationSidebarConfig - headerProps: Omit - loadingFallback?: React.ReactNode - mainClassName?: string - className?: string - mobileBurgerMenuProps: Omit + children: React.ReactNode; + sidebarConfig: NavigationSidebarConfig; + headerProps: Omit; + loadingFallback?: React.ReactNode; + mainClassName?: string; + className?: string; + mobileBurgerMenuProps: Omit; } export function AppLayout({ @@ -26,18 +26,18 @@ export function AppLayout({ className, mobileBurgerMenuProps, }: AppLayoutProps) { - const [mobileMenuOpen, setMobileMenuOpen] = useState(false) + const [mobileMenuOpen, setMobileMenuOpen] = useState(false); const handleToggleMobileMenu = useCallback(() => { - setMobileMenuOpen(prev => !prev) - }, []) + setMobileMenuOpen(prev => !prev); + }, []); const handleCloseMobileMenu = useCallback(() => { - setMobileMenuOpen(false) - }, []) + setMobileMenuOpen(false); + }, []); return ( -
+
{/* Mobile Burger Menu - opens below header */} - + {/* Main Content */} -
- - {children} - +
+ {children}
- ) + ); } diff --git a/openframe-frontend-core/src/components/navigation/client-only-header.tsx b/openframe-frontend-core/src/components/navigation/client-only-header.tsx index 83db8af8d..21c1699f9 100644 --- a/openframe-frontend-core/src/components/navigation/client-only-header.tsx +++ b/openframe-frontend-core/src/components/navigation/client-only-header.tsx @@ -1,6 +1,6 @@ -"use client"; +'use client'; -import { useState, useEffect } from 'react'; +import { useEffect, useState } from 'react'; import { Header, HeaderConfig } from './header'; import { HeaderSkeleton } from './header-skeleton'; @@ -15,11 +15,11 @@ export function ClientOnlyHeader({ config, skeleton }: ClientOnlyHeaderProps) { useEffect(() => { setIsClient(true); }, []); - + if (!isClient) { // Return custom skeleton or default skeleton while client-side JavaScript loads return skeleton || ; } return
; -} \ No newline at end of file +} diff --git a/openframe-frontend-core/src/components/navigation/header-button.tsx b/openframe-frontend-core/src/components/navigation/header-button.tsx index 89da386d0..6c8bd75ca 100644 --- a/openframe-frontend-core/src/components/navigation/header-button.tsx +++ b/openframe-frontend-core/src/components/navigation/header-button.tsx @@ -1,39 +1,34 @@ -'use client' +'use client'; -import React from 'react' -import { cn } from '../../utils/cn' +import React from 'react'; +import { cn } from '../../utils/cn'; export interface HeaderButtonProps extends React.ButtonHTMLAttributes { /** Whether the button is in active/pressed state */ - isActive?: boolean + isActive?: boolean; /** Icon to display in the button */ - icon: React.ReactNode + icon: React.ReactNode; /** Additional class names */ - className?: string + className?: string; } -export function HeaderButton({ - isActive = false, - icon, - className, - ...props -}: HeaderButtonProps) { +export function HeaderButton({ isActive = false, icon, className, ...props }: HeaderButtonProps) { return ( - ) + ); } -export default HeaderButton +export default HeaderButton; diff --git a/openframe-frontend-core/src/components/navigation/header-global-search.tsx b/openframe-frontend-core/src/components/navigation/header-global-search.tsx index f3ea0df4d..8c8d03289 100644 --- a/openframe-frontend-core/src/components/navigation/header-global-search.tsx +++ b/openframe-frontend-core/src/components/navigation/header-global-search.tsx @@ -1,20 +1,20 @@ -'use client' +'use client'; -import React from 'react' -import { cn } from '../../utils/cn' -import { SearchIcon } from '../icons-v2-generated' +import React from 'react'; +import { cn } from '../../utils/cn'; +import { SearchIcon } from '../icons-v2-generated'; export interface HeaderGlobalSearchProps { /** Current search value */ - value?: string + value?: string; /** Callback when search value changes */ - onChange?: (value: string) => void + onChange?: (value: string) => void; /** Callback when search is submitted */ - onSubmit?: (value: string) => void + onSubmit?: (value: string) => void; /** Placeholder text */ - placeholder?: string + placeholder?: string; /** Additional class names */ - className?: string + className?: string; } export function HeaderGlobalSearch({ @@ -22,40 +22,36 @@ export function HeaderGlobalSearch({ onChange, onSubmit, placeholder = 'Global Search', - className + className, }: HeaderGlobalSearchProps) { - const [internalValue, setInternalValue] = React.useState(value) + const [internalValue, setInternalValue] = React.useState(value); - const currentValue = onChange ? value : internalValue + const currentValue = onChange ? value : internalValue; const handleChange = (e: React.ChangeEvent) => { - const newValue = e.target.value + const newValue = e.target.value; if (onChange) { - onChange(newValue) + onChange(newValue); } else { - setInternalValue(newValue) + setInternalValue(newValue); } - } + }; const handleSubmit = (e: React.FormEvent) => { - e.preventDefault() - onSubmit?.(currentValue) - } + e.preventDefault(); + onSubmit?.(currentValue); + }; const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter') { - onSubmit?.(currentValue) + onSubmit?.(currentValue); } - } + }; return (
- ) + ); } -export default HeaderGlobalSearch +export default HeaderGlobalSearch; diff --git a/openframe-frontend-core/src/components/navigation/header-organization-filter.tsx b/openframe-frontend-core/src/components/navigation/header-organization-filter.tsx index 46b8459a9..80a347906 100644 --- a/openframe-frontend-core/src/components/navigation/header-organization-filter.tsx +++ b/openframe-frontend-core/src/components/navigation/header-organization-filter.tsx @@ -1,26 +1,26 @@ -'use client' +'use client'; -import { cn } from '../../utils/cn' -import { Filter02Icon } from '../icons-v2-generated' -import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '../ui' +import { cn } from '../../utils/cn'; +import { Filter02Icon } from '../icons-v2-generated'; +import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '../ui'; export interface HeaderOrganizationFilterOrganization { - id: string - name: string - deviceCount?: number + id: string; + name: string; + deviceCount?: number; } export interface HeaderOrganizationFilterProps { /** List of organizations */ - organizations?: HeaderOrganizationFilterOrganization[] + organizations?: HeaderOrganizationFilterOrganization[]; /** Currently selected organization ID */ - selectedOrgId?: string + selectedOrgId?: string; /** Callback when organization changes */ - onOrgChange?: (id: string) => void + onOrgChange?: (id: string) => void; /** Total device count (shown when "All Organizations" is selected) */ - totalDeviceCount?: number + totalDeviceCount?: number; /** Additional class names */ - className?: string + className?: string; } export function HeaderOrganizationFilter({ @@ -28,22 +28,22 @@ export function HeaderOrganizationFilter({ selectedOrgId, onOrgChange, totalDeviceCount, - className + className, }: HeaderOrganizationFilterProps) { - const selectedOrg = organizations.find(o => o.id === selectedOrgId) - const displayName = selectedOrg?.name || 'All Organizations' - const deviceCount = selectedOrg?.deviceCount ?? totalDeviceCount + const selectedOrg = organizations.find(o => o.id === selectedOrgId); + const displayName = selectedOrg?.name || 'All Organizations'; + const deviceCount = selectedOrg?.deviceCount ?? totalDeviceCount; return ( - onOrgChange?.('')}> - All Organizations - + onOrgChange?.('')}>All Organizations {organizations.map(org => ( - onOrgChange?.(org.id)} - > + onOrgChange?.(org.id)}> {org.name} ))} - ) + ); } -export default HeaderOrganizationFilter +export default HeaderOrganizationFilter; diff --git a/openframe-frontend-core/src/components/navigation/header-skeleton.tsx b/openframe-frontend-core/src/components/navigation/header-skeleton.tsx index 64829fda4..564d40551 100644 --- a/openframe-frontend-core/src/components/navigation/header-skeleton.tsx +++ b/openframe-frontend-core/src/components/navigation/header-skeleton.tsx @@ -1,25 +1,25 @@ -import { cn } from '../../utils' -import { HeaderConfig } from '../../types/navigation' +import { HeaderConfig } from '../../types/navigation'; +import { cn } from '../../utils'; export interface HeaderSkeletonProps { - config?: HeaderConfig + config?: HeaderConfig; } export function HeaderSkeleton({ config }: HeaderSkeletonProps) { - const showNavigation = config?.navigation && config.navigation.items.length > 0 - const showActions = config?.actions?.right && config.actions.right.length > 0 - const showMobileMenu = config?.mobile?.enabled - const isAdminHeader = config?.className?.includes('admin') - + const showNavigation = config?.navigation && config.navigation.items.length > 0; + const showActions = config?.actions?.right && config.actions.right.length > 0; + const showMobileMenu = config?.mobile?.enabled; + const isAdminHeader = config?.className?.includes('admin'); + return (
-
{/* Left: Logo */} @@ -29,7 +29,7 @@ export function HeaderSkeleton({ config }: HeaderSkeletonProps) {
)} - +
@@ -38,11 +38,13 @@ export function HeaderSkeleton({ config }: HeaderSkeletonProps) { {/* Center: Navigation */} {showNavigation && ( -
- ) -} \ No newline at end of file + ); +} diff --git a/openframe-frontend-core/src/components/navigation/header.tsx b/openframe-frontend-core/src/components/navigation/header.tsx index 7e9326498..3522777f7 100644 --- a/openframe-frontend-core/src/components/navigation/header.tsx +++ b/openframe-frontend-core/src/components/navigation/header.tsx @@ -1,180 +1,183 @@ -"use client" +'use client'; -import Link from 'next/link' -import React, { useEffect, useRef, useState } from 'react' -import { HeaderConfig, NavigationItem } from '../../types/navigation' -import { cn } from '../../utils' -import { Button } from '../ui/button' +import Link from 'next/link'; +import React, { useEffect, useRef, useState } from 'react'; +import { HeaderConfig, NavigationItem } from '../../types/navigation'; +import { cn } from '../../utils'; +import { Button } from '../ui/button'; export interface HeaderProps { - config: HeaderConfig - platform?: string + config: HeaderConfig; + platform?: string; } // Re-export from types for convenience -export type { HeaderConfig } from '../../types/navigation' +export type { HeaderConfig } from '../../types/navigation'; export function Header({ config, platform }: HeaderProps) { - const [show, setShow] = useState(true) - const [lastScrollY, setLastScrollY] = useState(0) - const [openDropdowns, setOpenDropdowns] = useState>({}) - const dropdownRefs = useRef>({}) - const triggerRefs = useRef>({}) - + const [show, setShow] = useState(true); + const [_lastScrollY, setLastScrollY] = useState(0); + const [openDropdowns, setOpenDropdowns] = useState>({}); + const dropdownRefs = useRef>({}); + const triggerRefs = useRef>({}); + // Handle click outside and escape key for custom dropdown useEffect(() => { const handleClickOutside = (event: MouseEvent) => { - const target = event.target as HTMLElement - if (!target) return + const target = event.target as HTMLElement; + if (!target) return; // Check if click is outside all dropdowns const isOutsideAllDropdowns = Object.keys(openDropdowns).every(id => { - const dropdown = dropdownRefs.current[id] - const trigger = triggerRefs.current[id] - - if (!dropdown || !trigger) return true - - return !dropdown.contains(target) && !trigger.contains(target) - }) + const dropdown = dropdownRefs.current[id]; + const trigger = triggerRefs.current[id]; + + if (!dropdown || !trigger) return true; + + return !dropdown.contains(target) && !trigger.contains(target); + }); if (isOutsideAllDropdowns) { - setOpenDropdowns({}) + setOpenDropdowns({}); } - } + }; const handleEscapeKey = (event: KeyboardEvent) => { if (event.key === 'Escape') { - setOpenDropdowns({}) + setOpenDropdowns({}); } - } + }; // Only add listeners if any dropdown is open - const hasOpenDropdowns = Object.values(openDropdowns).some(Boolean) + const hasOpenDropdowns = Object.values(openDropdowns).some(Boolean); if (hasOpenDropdowns) { - document.addEventListener('mousedown', handleClickOutside) - document.addEventListener('keydown', handleEscapeKey) + document.addEventListener('mousedown', handleClickOutside); + document.addEventListener('keydown', handleEscapeKey); } return () => { - document.removeEventListener('mousedown', handleClickOutside) - document.removeEventListener('keydown', handleEscapeKey) - } - }, [openDropdowns]) + document.removeEventListener('mousedown', handleClickOutside); + document.removeEventListener('keydown', handleEscapeKey); + }; + }, [openDropdowns]); // Force close all dropdowns and cleanup on unmount useEffect(() => { return () => { // Close all dropdowns before unmounting to prevent focus errors - setOpenDropdowns({}) + setOpenDropdowns({}); // Clear any stored refs - dropdownRefs.current = {} - triggerRefs.current = {} - } - }, []) - + dropdownRefs.current = {}; + triggerRefs.current = {}; + }; + }, []); + useEffect(() => { // Only add scroll listener if autoHide is enabled if (!config.autoHide) { - setShow(true) // Always show header when autoHide is disabled - return + setShow(true); // Always show header when autoHide is disabled + return; } - + const handleScroll = () => { - const currentScrollY = window.scrollY - + const currentScrollY = window.scrollY; + setLastScrollY(prevScrollY => { // Determine if we should show or hide the header - const shouldHide = currentScrollY > prevScrollY && currentScrollY > 50 - const shouldShow = currentScrollY < prevScrollY || currentScrollY <= 10 - + const shouldHide = currentScrollY > prevScrollY && currentScrollY > 50; + const shouldShow = currentScrollY < prevScrollY || currentScrollY <= 10; + if (shouldHide) { - setShow(false) + setShow(false); } else if (shouldShow) { - setShow(true) + setShow(true); } - - return currentScrollY - }) - } - window.addEventListener('scroll', handleScroll, { passive: true }) - - return () => { - window.removeEventListener('scroll', handleScroll) - } - }, [config.autoHide]) + return currentScrollY; + }); + }; + window.addEventListener('scroll', handleScroll, { passive: true }); + + return () => { + window.removeEventListener('scroll', handleScroll); + }; + }, [config.autoHide]); const renderNavigationItem = (item: NavigationItem) => { // If custom element provided, use it if (item.element) { - return {item.element} + return {item.element}; } // If it has children, render as custom dropdown if (item.children && item.children.length > 0) { - const isOpen = openDropdowns[item.id] || false - + const isOpen = openDropdowns[item.id] || false; + return (
- + {isOpen && (
{ dropdownRefs.current[item.id] = el }} + ref={el => { + dropdownRefs.current[item.id] = el; + }} className={cn( - "absolute top-full left-0 mt-1", - item.dropdownClassName ? "" : "bg-ods-card border border-ods-border", - "rounded-lg shadow-xl z-[9999]", - item.id === 'community' ? "min-w-[240px]" : "min-w-[220px]", - item.dropdownClassName || '' + 'absolute top-full left-0 mt-1', + item.dropdownClassName ? '' : 'bg-ods-card border border-ods-border', + 'rounded-lg shadow-xl z-[9999]', + item.id === 'community' ? 'min-w-[240px]' : 'min-w-[220px]', + item.dropdownClassName || '', )} >
{item.children.map((child, index) => ( -
- ) + ); } // Regular navigation item @@ -207,19 +208,19 @@ export function Header({ config, platform }: HeaderProps) { leftIcon={item.icon} rightIcon={item.badge} className={cn( - "h-10 px-3 py-2", + 'h-10 px-3 py-2', "font-['DM_Sans'] font-bold text-[16px] leading-none tracking-[-0.32px]", - "hover:bg-ods-bg-hover focus:bg-ods-bg-hover", - "whitespace-nowrap", - "text-ods-text-primary", // All items use primary text color + 'hover:bg-ods-bg-hover focus:bg-ods-bg-hover', + 'whitespace-nowrap', + 'text-ods-text-primary', // All items use primary text color item.isActive && 'bg-ods-bg-hover', // Active items get subtle gray background - item.className + item.className, )} {...(item.isExternal && { isExternal: true })} > {item.label} - ) + ); } // Button with onClick @@ -231,94 +232,82 @@ export function Header({ config, platform }: HeaderProps) { leftIcon={item.icon} rightIcon={item.badge} className={cn( - "h-10 px-3 py-2", + 'h-10 px-3 py-2', "font-['DM_Sans'] font-bold text-[16px] leading-none tracking-[-0.32px]", - "hover:bg-ods-bg-hover focus:bg-ods-bg-hover", - "whitespace-nowrap", - "text-ods-text-primary", // All items use primary text color + 'hover:bg-ods-bg-hover focus:bg-ods-bg-hover', + 'whitespace-nowrap', + 'text-ods-text-primary', // All items use primary text color item.isActive && 'bg-ods-bg-hover', // Active items get gray background - item.className + item.className, )} > {item.label} - ) - } - - + ); + }; + return ( -
- {/* Left: Logo */} -
- {config.actions?.left && ( -
- {config.actions.left} -
- )} - - - {config.logo.element} - -
+ {/* Left: Logo */} +
+ {config.actions?.left &&
{config.actions.left}
} - {/* Center: Navigation */} - {config.navigation && config.navigation.items.length > 0 && ( - - )} + + {config.logo.element} + +
- {/* Right: Actions */} -
- {/* Desktop Actions */} - {config.actions?.right && ( -
- {config.actions.right} -
+ {/* Center: Navigation */} + {config.navigation && config.navigation.items.length > 0 && ( + )} - {/* Mobile Menu Toggle */} - {config.mobile && config.mobile.enabled && ( - - )} -
-
+ > + )} +
+
- ) -} \ No newline at end of file + ); +} diff --git a/openframe-frontend-core/src/components/navigation/index.ts b/openframe-frontend-core/src/components/navigation/index.ts index f4295ffe0..c96d97e0f 100644 --- a/openframe-frontend-core/src/components/navigation/index.ts +++ b/openframe-frontend-core/src/components/navigation/index.ts @@ -1,47 +1,39 @@ -"use client" - -// Navigation component exports -export { Header } from './header' -export type { HeaderConfig, HeaderProps } from './header' - -export { ClientOnlyHeader } from './client-only-header' -export type { ClientOnlyHeaderProps } from './client-only-header' - -export { HeaderSkeleton } from './header-skeleton' -export type { HeaderSkeletonProps } from './header-skeleton' - -export { MobileNavPanel } from './mobile-nav-panel' -export type { MobileNavPanelProps } from './mobile-nav-panel' - -export { SlidingSidebar } from './sliding-sidebar' -export type { SlidingSidebarProps } from './sliding-sidebar' - -export { StickySectionNav, useSectionNavigation } from './sticky-section-nav' -export type { StickyNavSection } from './sticky-section-nav' - -export { NavigationSidebar } from './navigation-sidebar' -export type { NavigationSidebarProps } from './navigation-sidebar' - -export { AppHeader } from './app-header' -export type { AppHeaderProps } from './app-header' - -export { AppLayout } from './app-layout' -export type { AppLayoutProps } from './app-layout' - -export { MobileBurgerMenu } from './mobile-burger-menu' -export type { MobileBurgerMenuProps } from './mobile-burger-menu' - -export { HeaderButton } from './header-button' -export type { HeaderButtonProps } from './header-button' - -export { HeaderGlobalSearch } from './header-global-search' -export type { HeaderGlobalSearchProps } from './header-global-search' - -export { HeaderOrganizationFilter } from './header-organization-filter' -export type { HeaderOrganizationFilterOrganization, HeaderOrganizationFilterProps } from './header-organization-filter' +'use client'; // Re-export types from navigation types export type { - MobileNavConfig, NavigationItem, NavigationSidebarConfig, - NavigationSidebarItem, SlidingSidebarConfig, UnifiedSidebarUser -} from '../../types/navigation' + MobileNavConfig, + NavigationItem, + NavigationSidebarConfig, + NavigationSidebarItem, + SlidingSidebarConfig, + UnifiedSidebarUser, +} from '../../types/navigation'; +export type { AppHeaderProps } from './app-header'; +export { AppHeader } from './app-header'; +export type { AppLayoutProps } from './app-layout'; +export { AppLayout } from './app-layout'; +export type { ClientOnlyHeaderProps } from './client-only-header'; +export { ClientOnlyHeader } from './client-only-header'; +export type { HeaderConfig, HeaderProps } from './header'; +// Navigation component exports +export { Header } from './header'; +export type { HeaderButtonProps } from './header-button'; +export { HeaderButton } from './header-button'; +export type { HeaderGlobalSearchProps } from './header-global-search'; +export { HeaderGlobalSearch } from './header-global-search'; +export type { HeaderOrganizationFilterOrganization, HeaderOrganizationFilterProps } from './header-organization-filter'; +export { HeaderOrganizationFilter } from './header-organization-filter'; +export type { HeaderSkeletonProps } from './header-skeleton'; +export { HeaderSkeleton } from './header-skeleton'; +export type { MobileBurgerMenuProps } from './mobile-burger-menu'; + +export { MobileBurgerMenu } from './mobile-burger-menu'; +export type { MobileNavPanelProps } from './mobile-nav-panel'; +export { MobileNavPanel } from './mobile-nav-panel'; +export type { NavigationSidebarProps } from './navigation-sidebar'; +export { NavigationSidebar } from './navigation-sidebar'; +export type { SlidingSidebarProps } from './sliding-sidebar'; +export { SlidingSidebar } from './sliding-sidebar'; +export type { StickyNavSection } from './sticky-section-nav'; +export { StickySectionNav, useSectionNavigation } from './sticky-section-nav'; diff --git a/openframe-frontend-core/src/components/navigation/mobile-burger-menu.tsx b/openframe-frontend-core/src/components/navigation/mobile-burger-menu.tsx index a5231803b..3c2229b0c 100644 --- a/openframe-frontend-core/src/components/navigation/mobile-burger-menu.tsx +++ b/openframe-frontend-core/src/components/navigation/mobile-burger-menu.tsx @@ -1,34 +1,34 @@ -"use client" +'use client'; -import React, { useCallback, useEffect } from 'react' -import { NavigationSidebarConfig, NavigationSidebarItem } from '../../types/navigation' -import { cn } from '../../utils' -import { Logout02Icon, PenEditIcon, UserSearchIcon } from '../icons-v2-generated' -import { Button, SquareAvatar } from '../ui' +import React, { useCallback, useEffect } from 'react'; +import { NavigationSidebarConfig, NavigationSidebarItem } from '../../types/navigation'; +import { cn } from '../../utils'; +import { Logout02Icon, PenEditIcon, UserSearchIcon } from '../icons-v2-generated'; +import { Button, SquareAvatar } from '../ui'; // Header height constant (h-12 = 48px) -const HEADER_HEIGHT = 48 +const HEADER_HEIGHT = 48; export interface MobileBurgerMenuProps { /** Whether the menu is open */ - isOpen: boolean + isOpen: boolean; /** Callback to close the menu */ - onClose: () => void + onClose: () => void; /** Sidebar configuration */ - config: NavigationSidebarConfig + config: NavigationSidebarConfig; /** User info for the header card */ user?: { - userName?: string - userEmail?: string - userAvatarUrl?: string | null - userRole?: string - } + userName?: string; + userEmail?: string; + userAvatarUrl?: string | null; + userRole?: string; + }; /** Callback when search user button is clicked */ - onSearchUser?: () => void + onSearchUser?: () => void; /** Callback when edit profile button is clicked */ - onEditProfile?: () => void + onEditProfile?: () => void; /** Callback when logout button is clicked */ - onLogout?: () => void + onLogout?: () => void; } export function MobileBurgerMenu({ @@ -38,63 +38,66 @@ export function MobileBurgerMenu({ user, onSearchUser, onEditProfile, - onLogout + onLogout, }: MobileBurgerMenuProps) { // Prevent body scroll when menu is open useEffect(() => { if (isOpen) { - document.body.style.overflow = 'hidden' + document.body.style.overflow = 'hidden'; } else { - document.body.style.overflow = 'unset' + document.body.style.overflow = 'unset'; } return () => { - document.body.style.overflow = 'unset' - } - }, [isOpen]) + document.body.style.overflow = 'unset'; + }; + }, [isOpen]); // Handle escape key useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if (e.key === 'Escape' && isOpen) { - onClose() + onClose(); } - } + }; if (isOpen) { - document.addEventListener('keydown', handleKeyDown) - return () => document.removeEventListener('keydown', handleKeyDown) + document.addEventListener('keydown', handleKeyDown); + return () => document.removeEventListener('keydown', handleKeyDown); } - }, [isOpen, onClose]) + }, [isOpen, onClose]); - const handleItemClick = useCallback((item: NavigationSidebarItem) => { - if (item.onClick) { - item.onClick() - } else if (item.path) { - config.onNavigate?.(item.path) - } - onClose() - }, [config, onClose]) + const handleItemClick = useCallback( + (item: NavigationSidebarItem) => { + if (item.onClick) { + item.onClick(); + } else if (item.path) { + config.onNavigate?.(item.path); + } + onClose(); + }, + [config, onClose], + ); // Separate primary and secondary items - const primaryItems = config.items.filter(item => item.section !== 'secondary') - const secondaryItems = config.items.filter(item => item.section === 'secondary') + const primaryItems = config.items.filter(item => item.section !== 'secondary'); + const secondaryItems = config.items.filter(item => item.section === 'secondary'); const renderNavigationItem = (item: NavigationSidebarItem, isGridItem = false) => { - const isActive = item.isActive ?? false + const isActive = item.isActive ?? false; return ( - ) - } + ); + }; // Render grid of navigation items (2 columns) const renderNavigationGrid = (items: NavigationSidebarItem[]) => { - const rows: NavigationSidebarItem[][] = [] + const rows: NavigationSidebarItem[][] = []; for (let i = 0; i < items.length; i += 2) { - rows.push(items.slice(i, i + 2)) + rows.push(items.slice(i, i + 2)); } return (
{rows.map((row, rowIndex) => (
- {row.map((item) => ( - - {renderNavigationItem(item, true)} - + {row.map(item => ( + {renderNavigationItem(item, true)} ))} {/* Fill empty space if odd number of items in last row */} {row.length === 1 &&
}
))}
- ) - } + ); + }; return ( <> {/* Blur backdrop - positioned below header */}
- - {user.userName || 'User'} - + {user.userName || 'User'} {user.userRole && ( {user.userRole} @@ -205,31 +204,19 @@ export function MobileBurgerMenu({ )}
{user.userEmail && ( - - {user.userEmail} - + {user.userEmail} )}
{/* Action Buttons */}
{onSearchUser && ( - )} {onEditProfile && ( - )} @@ -238,17 +225,13 @@ export function MobileBurgerMenu({ )} {/* Primary Navigation Items - Grid Layout */} - + {/* Secondary Navigation Items - Full Width */} {secondaryItems.length > 0 && ( )} @@ -268,12 +251,8 @@ export function MobileBurgerMenu({
{/* Footer */} - {config.footer && ( -
- {config.footer} -
- )} + {config.footer &&
{config.footer}
}
- ) + ); } diff --git a/openframe-frontend-core/src/components/navigation/mobile-nav-panel.tsx b/openframe-frontend-core/src/components/navigation/mobile-nav-panel.tsx index da69ed47b..16049dcef 100644 --- a/openframe-frontend-core/src/components/navigation/mobile-nav-panel.tsx +++ b/openframe-frontend-core/src/components/navigation/mobile-nav-panel.tsx @@ -1,59 +1,59 @@ -"use client" +'use client'; -import React, { useEffect, useRef } from 'react' -import { cn } from '../../utils' -import { MobileNavConfig, NavigationItem } from '../../types/navigation' -import { Button } from '../ui/button' -import { X } from 'lucide-react' +import { X } from 'lucide-react'; +import React, { useEffect, useRef } from 'react'; +import { MobileNavConfig, NavigationItem } from '../../types/navigation'; +import { cn } from '../../utils'; +import { Button } from '../ui/button'; export interface MobileNavPanelProps { - isOpen: boolean - config: MobileNavConfig + isOpen: boolean; + config: MobileNavConfig; } export function MobileNavPanel({ isOpen, config }: MobileNavPanelProps) { - const panelRef = useRef(null) + const panelRef = useRef(null); // Prevent body scroll when menu is open - using the original working approach useEffect(() => { if (isOpen) { - document.body.style.overflow = 'hidden' + document.body.style.overflow = 'hidden'; } else { - document.body.style.overflow = 'unset' + document.body.style.overflow = 'unset'; } return () => { - document.body.style.overflow = 'unset' - } - }, [isOpen]) + document.body.style.overflow = 'unset'; + }; + }, [isOpen]); // Handle escape key useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if (e.key === 'Escape' && isOpen) { - config.onClose?.() + config.onClose?.(); } - } + }; if (isOpen) { - document.addEventListener('keydown', handleKeyDown) - return () => document.removeEventListener('keydown', handleKeyDown) + document.addEventListener('keydown', handleKeyDown); + return () => document.removeEventListener('keydown', handleKeyDown); } - }, [isOpen, config]) + }, [isOpen, config]); - if (!isOpen) return null + if (!isOpen) return null; const renderNavigationItem = (item: NavigationItem) => { if (item.element) { - return
{item.element}
+ return
{item.element}
; } const handleClick = () => { if (item.onClick) { - item.onClick() + item.onClick(); } - config.onClose?.() - } + config.onClose?.(); + }; if (item.href) { return ( @@ -66,13 +66,13 @@ export function MobileNavPanel({ isOpen, config }: MobileNavPanelProps) { leftIcon={item.icon} rightIcon={item.badge} className={cn( - "justify-start h-12 px-4 bg-transparent border-none text-ods-text-primary hover:bg-ods-bg-hover gap-3 rounded-md transition-colors", - item.isActive ? "bg-ods-bg-hover" : "" + 'justify-start h-12 px-4 bg-transparent border-none text-ods-text-primary hover:bg-ods-bg-hover gap-3 rounded-md transition-colors', + item.isActive ? 'bg-ods-bg-hover' : '', )} > {item.label} - ) + ); } return ( @@ -83,41 +83,36 @@ export function MobileNavPanel({ isOpen, config }: MobileNavPanelProps) { onClick={handleClick} leftIcon={item.icon} className={cn( - "justify-start h-12 px-4 bg-transparent border-none text-ods-text-primary hover:bg-ods-bg-hover gap-3 rounded-md transition-colors", - item.isActive ? "bg-ods-bg-hover" : "" + 'justify-start h-12 px-4 bg-transparent border-none text-ods-text-primary hover:bg-ods-bg-hover gap-3 rounded-md transition-colors', + item.isActive ? 'bg-ods-bg-hover' : '', )} > {item.label} - {item.badge !== undefined && ( - {item.badge} - )} + {item.badge !== undefined && {item.badge}} - ) - } + ); + }; return ( <> {/* Backdrop - closes nav when clicked outside */} -
+
{/* Navigation Panel - modal style like the original working version */}
e.stopPropagation()} // Prevent closing when clicking inside nav + onClick={e => e.stopPropagation()} // Prevent closing when clicking inside nav > {/* Header with close button - fixed at top */}
@@ -141,21 +136,15 @@ export function MobileNavPanel({ isOpen, config }: MobileNavPanelProps) { {section.title}
)} -
- {section.items.map(renderNavigationItem)} -
+
{section.items.map(renderNavigationItem)}
))}
{/* Footer with action button - fixed at bottom */} - {config.footer && ( -
- {config.footer} -
- )} + {config.footer &&
{config.footer}
}
- ) -} \ No newline at end of file + ); +} diff --git a/openframe-frontend-core/src/components/navigation/navigation-sidebar.tsx b/openframe-frontend-core/src/components/navigation/navigation-sidebar.tsx index 30d03adcd..49090fb34 100644 --- a/openframe-frontend-core/src/components/navigation/navigation-sidebar.tsx +++ b/openframe-frontend-core/src/components/navigation/navigation-sidebar.tsx @@ -1,293 +1,253 @@ -"use client" +'use client'; -import { cloneElement, useCallback, useLayoutEffect, useMemo, useState } from 'react' -import { useLocalStorage } from '../../hooks/ui/use-local-storage' -import { useLgUp } from '../../hooks/ui/use-media-query' -import { NavigationSidebarConfig, NavigationSidebarItem } from '../../types/navigation' -import { cn } from '../../utils' -import { DoubleChevronIcon, OpenFrameLogo, OpenFrameText } from '../icons' +import { cloneElement, useCallback, useLayoutEffect, useMemo, useState } from 'react'; +import { useLocalStorage } from '../../hooks/ui/use-local-storage'; +import { useLgUp } from '../../hooks/ui/use-media-query'; +import { NavigationSidebarConfig, NavigationSidebarItem } from '../../types/navigation'; +import { cn } from '../../utils'; +import { DoubleChevronIcon, OpenFrameLogo, OpenFrameText } from '../icons'; // Constants -const MINIMIZED_WIDTH = 56 // 3.5rem = 56px -const EXPANDED_WIDTH = 224 // 14rem = 224px -const STORAGE_KEY = 'of.navigationSidebar.minimized' +const MINIMIZED_WIDTH = 56; // 3.5rem = 56px +const EXPANDED_WIDTH = 224; // 14rem = 224px +const STORAGE_KEY = 'of.navigationSidebar.minimized'; export interface NavigationSidebarProps { - config: NavigationSidebarConfig + config: NavigationSidebarConfig; } export function NavigationSidebar({ config }: NavigationSidebarProps) { - - const isLgUp = useLgUp() ?? false + const isLgUp = useLgUp() ?? false; // Initialize minimized state based on tablet mode or config // useLocalStorage will read from localStorage first, then fall back to this value - const [minimized, setMinimized] = useLocalStorage( - STORAGE_KEY, - !isLgUp || (config.minimized ?? false) - ) + const [minimized, setMinimized] = useLocalStorage(STORAGE_KEY, !isLgUp || (config.minimized ?? false)); // Enable transitions only after the correct width is painted - const [transitionsEnabled, setTransitionsEnabled] = useState(false) + const [transitionsEnabled, setTransitionsEnabled] = useState(false); - const [mobileMenuOpen, setMobileMenuOpen] = useState(false) + const [mobileMenuOpen, setMobileMenuOpen] = useState(false); const handleToggleMinimized = useCallback(() => { if (!isLgUp) { - setMobileMenuOpen(prev => !prev) + setMobileMenuOpen(prev => !prev); } else { - setMinimized(prev => !prev) - config.onToggleMinimized?.() - } - }, [isLgUp, setMinimized, config]) - - const handleItemClick = useCallback((item: NavigationSidebarItem, event?: React.MouseEvent) => { - event?.stopPropagation() - - // Close mobile menu on navigation - if (!isLgUp && mobileMenuOpen) { - setMobileMenuOpen(false) - } - - if (item.onClick) { - item.onClick() - } else if (item.path) { - config.onNavigate?.(item.path) + setMinimized(prev => !prev); + config.onToggleMinimized?.(); } - }, [config, isLgUp, mobileMenuOpen]) - - const renderNavigationItem = useCallback(( - item: NavigationSidebarItem, - inOverlay: boolean - ) => { - const isActive = item.isActive ?? false - const isMinimized = !isLgUp && !inOverlay ? true : minimized - const shouldShowLabel = !isLgUp ? inOverlay : (inOverlay || !minimized) - - return ( -
- - {/* Label - only render when needed */} - {shouldShowLabel && ( - - {item.label} - - )} + {/* Active indicator */} + {isActive &&
- + {/* Features Grid */}
{features.map((feature, index) => ( - + ))}
@@ -87,4 +77,4 @@ const OpenSourceFeatures: React.FC = () => { ); }; -export default OpenSourceFeatures; \ No newline at end of file +export default OpenSourceFeatures; diff --git a/openframe-frontend-core/src/components/open-source-icon.tsx b/openframe-frontend-core/src/components/open-source-icon.tsx index 8d0be8ab1..e0d9e318c 100644 --- a/openframe-frontend-core/src/components/open-source-icon.tsx +++ b/openframe-frontend-core/src/components/open-source-icon.tsx @@ -6,22 +6,24 @@ interface OpenSourceIconProps { height?: number; } -export const OpenSourceIcon: React.FC = ({ - className = "", - width = 19, - height = 18 -}) => { +export const OpenSourceIcon: React.FC = ({ className = '', width = 19, height = 18 }) => { return ( - - - + + ); -}; \ No newline at end of file +}; diff --git a/openframe-frontend-core/src/components/openframe-logo.tsx b/openframe-frontend-core/src/components/openframe-logo.tsx index 87bdc6eee..351be56bb 100644 --- a/openframe-frontend-core/src/components/openframe-logo.tsx +++ b/openframe-frontend-core/src/components/openframe-logo.tsx @@ -1,6 +1,11 @@ import React from 'react'; -export const OpenFrameLogo = ({ className, lowerPathColor, upperPathColor, ...props }: { className?: string, lowerPathColor?: string, upperPathColor?: string } & React.SVGProps) => { +export const OpenFrameLogo = ({ + className, + lowerPathColor, + upperPathColor, + ...props +}: { className?: string; lowerPathColor?: string; upperPathColor?: string } & React.SVGProps) => { return ( @@ -43,4 +50,4 @@ export const OpenFrameLogo = ({ className, lowerPathColor, upperPathColor, ...p ); -}; \ No newline at end of file +}; diff --git a/openframe-frontend-core/src/components/openmsp-logo.tsx b/openframe-frontend-core/src/components/openmsp-logo.tsx index 336757d5a..1e13abe12 100644 --- a/openframe-frontend-core/src/components/openmsp-logo.tsx +++ b/openframe-frontend-core/src/components/openmsp-logo.tsx @@ -8,7 +8,13 @@ interface OpenmspLogoProps extends React.SVGProps { color?: string; } -export function OpenmspLogo({ className = '', frontBubbleColor = '#000000', innerFrontBubbleColor = '#ffffff', backBubbleColor = '#ffffff', ...props }: OpenmspLogoProps) { +export function OpenmspLogo({ + className = '', + frontBubbleColor = '#000000', + innerFrontBubbleColor = '#ffffff', + backBubbleColor = '#ffffff', + ...props +}: OpenmspLogoProps) { return ( + fill={backBubbleColor} + /> + fill={innerFrontBubbleColor} + /> - + fill={frontBubbleColor} + /> + ); } diff --git a/openframe-frontend-core/src/components/pagination.tsx b/openframe-frontend-core/src/components/pagination.tsx index ddb03410f..9f8fe09b5 100644 --- a/openframe-frontend-core/src/components/pagination.tsx +++ b/openframe-frontend-core/src/components/pagination.tsx @@ -1,71 +1,71 @@ -"use client" +'use client'; -import { ChevronLeft, ChevronRight, MoreHorizontal } from "lucide-react" -import * as React from "react" -import { cn } from "../utils/cn" -import { Button } from "./ui/button" +import { ChevronLeft, ChevronRight, MoreHorizontal } from 'lucide-react'; +import * as React from 'react'; +import { cn } from '../utils/cn'; +import { Button } from './ui/button'; interface PaginationProps { - currentPage: number - totalPages: number - onPageChange: (page: number) => void - className?: string + currentPage: number; + totalPages: number; + onPageChange: (page: number) => void; + className?: string; } const Pagination = ({ currentPage, totalPages, onPageChange, className }: PaginationProps) => { // Generate page numbers to display const getPageNumbers = () => { - const pages = [] - const maxPagesToShow = 5 + const pages = []; + const maxPagesToShow = 5; if (totalPages <= maxPagesToShow) { // Show all pages if there are fewer than maxPagesToShow for (let i = 1; i <= totalPages; i++) { - pages.push(i) + pages.push(i); } } else { // Always show first page - pages.push(1) + pages.push(1); // Calculate start and end of page range - let start = Math.max(2, currentPage - 1) - let end = Math.min(totalPages - 1, currentPage + 1) + let start = Math.max(2, currentPage - 1); + let end = Math.min(totalPages - 1, currentPage + 1); // Adjust if at the beginning or end if (currentPage <= 2) { - end = Math.min(totalPages - 1, 4) + end = Math.min(totalPages - 1, 4); } else if (currentPage >= totalPages - 1) { - start = Math.max(2, totalPages - 3) + start = Math.max(2, totalPages - 3); } // Add ellipsis if needed if (start > 2) { - pages.push(-1) // -1 represents ellipsis + pages.push(-1); // -1 represents ellipsis } // Add middle pages for (let i = start; i <= end; i++) { - pages.push(i) + pages.push(i); } // Add ellipsis if needed if (end < totalPages - 1) { - pages.push(-2) // -2 represents ellipsis + pages.push(-2); // -2 represents ellipsis } // Always show last page if (totalPages > 1) { - pages.push(totalPages) + pages.push(totalPages); } } - return pages - } + return pages; + }; - const pageNumbers = getPageNumbers() + const pageNumbers = getPageNumbers(); return ( -