@heyramzi/ui 0.0.0-stage → 1.53.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/COMPONENTS.md +133 -0
- package/README.md +136 -2
- package/package.json +217 -3
- package/scripts/lib/sync-core.mjs +113 -0
- package/scripts/relative-imports.mjs +39 -0
- package/scripts/sf-symbol.mjs +93 -0
- package/scripts/sync-shadcn.mjs +354 -0
- package/scripts/sync-transitions.mjs +146 -0
- package/scripts/validate-apple-symbol-exports.mjs +45 -0
- package/src/agents/agent-avatar.svelte +592 -0
- package/src/agents/agent-cast.svelte +197 -0
- package/src/agents/demo.svelte +125 -0
- package/src/agents/helpers.ts +58 -0
- package/src/agents/index.ts +11 -0
- package/src/agents/page-state.ts +70 -0
- package/src/agents/types.ts +74 -0
- package/src/agents/vendor/LICENSE +21 -0
- package/src/agents/vendor/color.ts +155 -0
- package/src/agents/vendor/draw.ts +840 -0
- package/src/agents/vendor/engine.ts +845 -0
- package/src/agents/vendor/mesh.ts +838 -0
- package/src/agents/vendor/plastic.ts +4958 -0
- package/src/agents/vendor/presets.ts +103 -0
- package/src/agents/vendor/shapes.ts +41 -0
- package/src/agents/vendor/ticker.ts +67 -0
- package/src/agents/vendor/types.ts +261 -0
- package/src/agents/vendor/wear.ts +1699 -0
- package/src/ai/ai-button/AiButton.svelte +86 -0
- package/src/ai/ai-button/index.ts +4 -0
- package/src/ai/confirmation/Confirmation.svelte +54 -0
- package/src/ai/confirmation/ConfirmationAccepted.svelte +23 -0
- package/src/ai/confirmation/ConfirmationAction.svelte +53 -0
- package/src/ai/confirmation/ConfirmationActions.svelte +24 -0
- package/src/ai/confirmation/ConfirmationFailed.svelte +23 -0
- package/src/ai/confirmation/ConfirmationRejected.svelte +23 -0
- package/src/ai/confirmation/ConfirmationRequest.svelte +25 -0
- package/src/ai/confirmation/ConfirmationTitle.svelte +25 -0
- package/src/ai/confirmation/confirmation-context.svelte.ts +22 -0
- package/src/ai/confirmation/index.ts +50 -0
- package/src/ai/generation-progress/GenerationProgress.svelte +354 -0
- package/src/ai/generation-progress/index.ts +7 -0
- package/src/ai/index.ts +111 -0
- package/src/ai/loading-state/PixelLoader.svelte +138 -0
- package/src/ai/loading-state/index.ts +5 -0
- package/src/ai/prompt-input/PromptInput.svelte +56 -0
- package/src/ai/prompt-input/PromptInputBody.svelte +19 -0
- package/src/ai/prompt-input/PromptInputSubmit.svelte +79 -0
- package/src/ai/prompt-input/PromptInputTextarea.svelte +74 -0
- package/src/ai/prompt-input/PromptInputToolbar.svelte +19 -0
- package/src/ai/prompt-input/index.ts +34 -0
- package/src/ai/reasoning/Reasoning.svelte +50 -0
- package/src/ai/reasoning/ReasoningContent.svelte +27 -0
- package/src/ai/reasoning/ReasoningTrigger.svelte +48 -0
- package/src/ai/reasoning/index.ts +22 -0
- package/src/ai/reasoning/reasoning-context.svelte.ts +20 -0
- package/src/ai/response/Response.css +32 -0
- package/src/ai/response/Response.svelte +130 -0
- package/src/ai/response/index.ts +5 -0
- package/src/ai/suggestion/Suggestion.svelte +43 -0
- package/src/ai/suggestion/Suggestions.svelte +25 -0
- package/src/ai/suggestion/index.ts +14 -0
- package/src/ai/thinking-dots/ThinkingDots.svelte +42 -0
- package/src/ai/thinking-dots/index.ts +5 -0
- package/src/ai/typing-dots/TypingDots.css +39 -0
- package/src/ai/typing-dots/TypingDots.svelte +27 -0
- package/src/ai/typing-dots/index.ts +10 -0
- package/src/ai/voice-waveform/VoiceWaveform.css +40 -0
- package/src/ai/voice-waveform/VoiceWaveform.svelte +46 -0
- package/src/ai/voice-waveform/index.ts +5 -0
- package/src/ambient.d.ts +4 -0
- package/src/animations/README.md +84 -0
- package/src/animations/avatar-group-hover.css +47 -0
- package/src/animations/card-tilt.css +74 -0
- package/src/animations/digit-pop-in.css +78 -0
- package/src/animations/dropdown.css +121 -0
- package/src/animations/index.css +23 -0
- package/src/animations/input-clear-dissolve.css +86 -0
- package/src/animations/input-shake.css +114 -0
- package/src/animations/learn-more-hover.css +48 -0
- package/src/animations/like-button.css +93 -0
- package/src/animations/modal.css +75 -0
- package/src/animations/page-side-by-side.css +63 -0
- package/src/animations/plus-menu-morph.css +97 -0
- package/src/animations/resize.css +20 -0
- package/src/animations/shimmer-text.css +72 -0
- package/src/animations/spinning-counter.css +52 -0
- package/src/animations/success-check.css +120 -0
- package/src/animations/text-swap.css +40 -0
- package/src/animations/text-swap.ts +40 -0
- package/src/animations/thinking-states.css +65 -0
- package/src/animations/upstream-coverage.test.ts +65 -0
- package/src/components/copy/clipboard.test.ts +206 -0
- package/src/components/copy/clipboard.ts +49 -0
- package/src/components/copy/copied.svelte.ts +39 -0
- package/src/components/copy/copy-button.svelte +175 -0
- package/src/components/copy/copy-button.test.ts +110 -0
- package/src/components/copy/index.ts +3 -0
- package/src/components/data-table/data-table.test.ts +134 -0
- package/src/components/data-table/data-table.ts +77 -0
- package/src/components/data-table/index.ts +20 -0
- package/src/components/data-table/infinite-scroll.svelte +69 -0
- package/src/components/data-table/pagination.svelte +62 -0
- package/src/components/data-table/row-arrow.svelte +28 -0
- package/src/components/data-table/row-context-menu.svelte +106 -0
- package/src/components/data-table/row-context-menu.test.ts +18 -0
- package/src/components/data-table/sortable-head.svelte +56 -0
- package/src/components/data-table/table-skeleton.svelte +83 -0
- package/src/components/hold-to-confirm/hold-to-confirm.svelte +232 -0
- package/src/components/hold-to-confirm/index.ts +1 -0
- package/src/components/icon/icon.svelte +23 -0
- package/src/components/icon/index.ts +1 -0
- package/src/components/language-flag/index.ts +1 -0
- package/src/components/language-flag/language-flag.svelte +60 -0
- package/src/components/list-page/detail-empty.svelte +50 -0
- package/src/components/list-page/detail-header.svelte +107 -0
- package/src/components/list-page/detail-panel.svelte +90 -0
- package/src/components/list-page/detail-row.svelte +36 -0
- package/src/components/list-page/detail-stack.svelte +33 -0
- package/src/components/list-page/empty-state.svelte +91 -0
- package/src/components/list-page/filter-item.svelte +49 -0
- package/src/components/list-page/filter-popover.svelte +88 -0
- package/src/components/list-page/filter-section.ts +12 -0
- package/src/components/list-page/grouped-list.svelte +116 -0
- package/src/components/list-page/index.ts +28 -0
- package/src/components/list-page/list-page-shell.svelte +195 -0
- package/src/components/list-page/list-table.ts +19 -0
- package/src/components/list-page/master-detail.svelte +206 -0
- package/src/components/list-page/master-detail.test.ts +37 -0
- package/src/components/list-page/notice-bar.svelte +68 -0
- package/src/components/list-page/prev-next.svelte +104 -0
- package/src/components/list-page/queue-rail.ts +25 -0
- package/src/components/list-page/rail-body.svelte +29 -0
- package/src/components/list-page/rail-empty.svelte +51 -0
- package/src/components/list-page/rail-head.svelte +49 -0
- package/src/components/list-page/rail-row.svelte +163 -0
- package/src/components/list-page/section-host.ts +24 -0
- package/src/components/list-page/section-label.svelte +39 -0
- package/src/components/list-page/step.test.ts +21 -0
- package/src/components/list-page/step.ts +10 -0
- package/src/components/list-page/toolbar-search.svelte +63 -0
- package/src/components/list-page/toolbar-trigger.ts +23 -0
- package/src/components/list-page/types.ts +1 -0
- package/src/components/locale-switch/index.ts +1 -0
- package/src/components/locale-switch/locale-switch.svelte +59 -0
- package/src/components/mark/index.ts +1 -0
- package/src/components/mark/mark.svelte +70 -0
- package/src/components/open-in/index.ts +6 -0
- package/src/components/open-in/open-in.svelte +106 -0
- package/src/components/picker/index.ts +4 -0
- package/src/components/picker/mark-picker.svelte +84 -0
- package/src/components/picker/picker.svelte +143 -0
- package/src/components/picker/trigger.ts +42 -0
- package/src/components/picker/types.ts +26 -0
- package/src/components/segmented/animated-tabs.svelte +175 -0
- package/src/components/segmented/collapse.test.ts +49 -0
- package/src/components/segmented/collapse.ts +39 -0
- package/src/components/segmented/index.ts +41 -0
- package/src/components/segmented/section-runs.svelte +79 -0
- package/src/components/segmented/segment.svelte +106 -0
- package/src/components/segmented/segmented-filter.svelte +125 -0
- package/src/components/segmented/segmented-nav.svelte +82 -0
- package/src/components/segmented/segmented-overflow.svelte +85 -0
- package/src/components/segmented/segmented-track.svelte +181 -0
- package/src/components/segmented/segmented.test.ts +20 -0
- package/src/components/segmented/segmented.ts +80 -0
- package/src/components/star-rating-input/index.ts +9 -0
- package/src/components/star-rating-input/star-rating-input.svelte +256 -0
- package/src/components/svg/index.ts +1 -0
- package/src/components/svg/svg.svelte +9 -0
- package/src/design/index.css +270 -0
- package/src/design/polish.css +159 -0
- package/src/effects/animated-beam/AnimatedBeam.svelte +132 -0
- package/src/effects/animated-beam/diagrams/BeamSvg.svelte +91 -0
- package/src/effects/animated-beam/index.ts +2 -0
- package/src/effects/animated-beam/path-calculator.svelte.ts +70 -0
- package/src/effects/animated-beam/types.ts +23 -0
- package/src/effects/border-beam/BorderBeam.svelte +223 -0
- package/src/effects/border-beam/css.test.ts +46 -0
- package/src/effects/border-beam/css.ts +574 -0
- package/src/effects/border-beam/presets.ts +699 -0
- package/src/effects/border-beam/register-properties.test.ts +74 -0
- package/src/effects/border-beam/register-properties.ts +73 -0
- package/src/effects/border-beam/types.ts +38 -0
- package/src/effects/image-comparison/ImageComparison.css +100 -0
- package/src/effects/image-comparison/ImageComparison.svelte +209 -0
- package/src/effects/image-comparison/compare.test.ts +17 -0
- package/src/effects/image-comparison/compare.ts +5 -0
- package/src/effects/image-reveal/ImageReveal.css +10 -0
- package/src/effects/image-reveal/ImageReveal.svelte +357 -0
- package/src/effects/image-reveal/index.ts +2 -0
- package/src/effects/image-reveal/types.ts +35 -0
- package/src/effects/interactive-hover-button/InteractiveHoverButton.svelte +338 -0
- package/src/effects/magic-card/MagicCard.css +47 -0
- package/src/effects/magic-card/MagicCard.svelte +57 -0
- package/src/effects/magnetic/magnetic.ts +81 -0
- package/src/effects/marquee/Marquee.css +78 -0
- package/src/effects/marquee/Marquee.svelte +160 -0
- package/src/effects/progressive-blur/ProgressiveBlur.css +9 -0
- package/src/effects/progressive-blur/ProgressiveBlur.svelte +63 -0
- package/src/effects/reveal/AnimatedGroup.svelte +101 -0
- package/src/effects/reveal/InView.svelte +77 -0
- package/src/effects/reveal/TextEffect.svelte +142 -0
- package/src/effects/reveal/index.ts +11 -0
- package/src/effects/reveal/observe.test.ts +60 -0
- package/src/effects/reveal/observe.ts +65 -0
- package/src/effects/reveal/reveal.css +141 -0
- package/src/effects/reveal/types.ts +32 -0
- package/src/effects/shader-gradient/ShaderGradient.svelte +227 -0
- package/src/effects/shader-gradient/color.ts +44 -0
- package/src/effects/shader-gradient/index.ts +5 -0
- package/src/effects/shader-gradient/renderer.ts +456 -0
- package/src/effects/shader-gradient/shaders.ts +324 -0
- package/src/effects/shader-gradient/types.ts +68 -0
- package/src/effects/spotlight/Spotlight.css +14 -0
- package/src/effects/spotlight/Spotlight.svelte +61 -0
- package/src/effects/theme-toggle/ThemeToggle.css +214 -0
- package/src/effects/theme-toggle/ThemeToggle.svelte +126 -0
- package/src/gallery.test.ts +52 -0
- package/src/icons/TradeIcon.svelte +16 -0
- package/src/icons/apple-symbol-exports.ts +135 -0
- package/src/icons/sf-symbols.ts +527 -0
- package/src/icons/trades.ts +89 -0
- package/src/index.ts +53 -0
- package/src/lib/arrive.ts +26 -0
- package/src/lib/icon-types.ts +17 -0
- package/src/lib/layout.ts +8 -0
- package/src/lib/panel.ts +21 -0
- package/src/lib/sliding-pill.ts +68 -0
- package/src/lib/theme-transition.ts +79 -0
- package/src/lib/tone.ts +48 -0
- package/src/shadcn/accordion/accordion-content.svelte +22 -0
- package/src/shadcn/accordion/accordion-item.svelte +17 -0
- package/src/shadcn/accordion/accordion-trigger.svelte +44 -0
- package/src/shadcn/accordion/accordion.svelte +19 -0
- package/src/shadcn/accordion/index.ts +16 -0
- package/src/shadcn/alert/alert-description.svelte +23 -0
- package/src/shadcn/alert/alert-title.svelte +20 -0
- package/src/shadcn/alert/alert.svelte +46 -0
- package/src/shadcn/alert/index.ts +14 -0
- package/src/shadcn/alert-dialog/alert-dialog-action.svelte +18 -0
- package/src/shadcn/alert-dialog/alert-dialog-cancel.svelte +18 -0
- package/src/shadcn/alert-dialog/alert-dialog-content.svelte +29 -0
- package/src/shadcn/alert-dialog/alert-dialog-description.svelte +17 -0
- package/src/shadcn/alert-dialog/alert-dialog-footer.svelte +20 -0
- package/src/shadcn/alert-dialog/alert-dialog-header.svelte +20 -0
- package/src/shadcn/alert-dialog/alert-dialog-overlay.svelte +17 -0
- package/src/shadcn/alert-dialog/alert-dialog-portal.svelte +7 -0
- package/src/shadcn/alert-dialog/alert-dialog-title.svelte +17 -0
- package/src/shadcn/alert-dialog/alert-dialog-trigger.svelte +7 -0
- package/src/shadcn/alert-dialog/alert-dialog.svelte +7 -0
- package/src/shadcn/alert-dialog/index.ts +37 -0
- package/src/shadcn/attachment/attachment-action.svelte +23 -0
- package/src/shadcn/attachment/attachment-actions.svelte +23 -0
- package/src/shadcn/attachment/attachment-content.svelte +23 -0
- package/src/shadcn/attachment/attachment-description.svelte +23 -0
- package/src/shadcn/attachment/attachment-group.svelte +23 -0
- package/src/shadcn/attachment/attachment-media.svelte +44 -0
- package/src/shadcn/attachment/attachment-title.svelte +23 -0
- package/src/shadcn/attachment/attachment-trigger.svelte +29 -0
- package/src/shadcn/attachment/attachment.svelte +55 -0
- package/src/shadcn/attachment/index.ts +40 -0
- package/src/shadcn/avatar/avatar-badge.svelte +26 -0
- package/src/shadcn/avatar/avatar-fallback.svelte +20 -0
- package/src/shadcn/avatar/avatar-group-count.svelte +23 -0
- package/src/shadcn/avatar/avatar-group.svelte +23 -0
- package/src/shadcn/avatar/avatar-image.svelte +17 -0
- package/src/shadcn/avatar/avatar.svelte +26 -0
- package/src/shadcn/avatar/index.ts +22 -0
- package/src/shadcn/badge/badge.svelte +54 -0
- package/src/shadcn/badge/index.ts +2 -0
- package/src/shadcn/breadcrumb/breadcrumb-ellipsis.svelte +23 -0
- package/src/shadcn/breadcrumb/breadcrumb-item.svelte +20 -0
- package/src/shadcn/breadcrumb/breadcrumb-link.svelte +31 -0
- package/src/shadcn/breadcrumb/breadcrumb-list.svelte +23 -0
- package/src/shadcn/breadcrumb/breadcrumb-page.svelte +23 -0
- package/src/shadcn/breadcrumb/breadcrumb-separator.svelte +27 -0
- package/src/shadcn/breadcrumb/breadcrumb.svelte +21 -0
- package/src/shadcn/breadcrumb/index.ts +25 -0
- package/src/shadcn/bubble/bubble-content.svelte +31 -0
- package/src/shadcn/bubble/bubble-group.svelte +20 -0
- package/src/shadcn/bubble/bubble-reactions.svelte +52 -0
- package/src/shadcn/bubble/bubble.svelte +58 -0
- package/src/shadcn/bubble/index.ts +25 -0
- package/src/shadcn/button/button.svelte +121 -0
- package/src/shadcn/button/index.ts +17 -0
- package/src/shadcn/calendar/calendar-caption.svelte +76 -0
- package/src/shadcn/calendar/calendar-cell.svelte +19 -0
- package/src/shadcn/calendar/calendar-day.svelte +35 -0
- package/src/shadcn/calendar/calendar-grid-body.svelte +12 -0
- package/src/shadcn/calendar/calendar-grid-head.svelte +12 -0
- package/src/shadcn/calendar/calendar-grid-row.svelte +12 -0
- package/src/shadcn/calendar/calendar-grid.svelte +16 -0
- package/src/shadcn/calendar/calendar-head-cell.svelte +19 -0
- package/src/shadcn/calendar/calendar-header.svelte +19 -0
- package/src/shadcn/calendar/calendar-heading.svelte +16 -0
- package/src/shadcn/calendar/calendar-month-select.svelte +44 -0
- package/src/shadcn/calendar/calendar-month.svelte +15 -0
- package/src/shadcn/calendar/calendar-months.svelte +19 -0
- package/src/shadcn/calendar/calendar-nav.svelte +19 -0
- package/src/shadcn/calendar/calendar-next-button.svelte +31 -0
- package/src/shadcn/calendar/calendar-prev-button.svelte +31 -0
- package/src/shadcn/calendar/calendar-year-select.svelte +43 -0
- package/src/shadcn/calendar/calendar.svelte +124 -0
- package/src/shadcn/calendar/index.ts +40 -0
- package/src/shadcn/card/card-action.svelte +23 -0
- package/src/shadcn/card/card-content.svelte +20 -0
- package/src/shadcn/card/card-description.svelte +20 -0
- package/src/shadcn/card/card-footer.svelte +23 -0
- package/src/shadcn/card/card-header.svelte +23 -0
- package/src/shadcn/card/card-title.svelte +29 -0
- package/src/shadcn/card/card.svelte +25 -0
- package/src/shadcn/card/index.ts +25 -0
- package/src/shadcn/checkbox/checkbox.svelte +38 -0
- package/src/shadcn/checkbox/index.ts +6 -0
- package/src/shadcn/code-block/code-block-actions.svelte +23 -0
- package/src/shadcn/code-block/code-block-content.svelte +55 -0
- package/src/shadcn/code-block/code-block-copy-button.svelte +10 -0
- package/src/shadcn/code-block/code-block-footer.svelte +20 -0
- package/src/shadcn/code-block/code-block-header.svelte +23 -0
- package/src/shadcn/code-block/code-block-line.svelte +71 -0
- package/src/shadcn/code-block/code-block-title.svelte +23 -0
- package/src/shadcn/code-block/code-block.svelte +47 -0
- package/src/shadcn/code-block/index.ts +42 -0
- package/src/shadcn/collapsible/collapsible-content.svelte +7 -0
- package/src/shadcn/collapsible/collapsible-trigger.svelte +7 -0
- package/src/shadcn/collapsible/collapsible.svelte +11 -0
- package/src/shadcn/collapsible/index.ts +13 -0
- package/src/shadcn/command/command-dialog.svelte +66 -0
- package/src/shadcn/command/command-empty.svelte +17 -0
- package/src/shadcn/command/command-group.svelte +40 -0
- package/src/shadcn/command/command-input.svelte +64 -0
- package/src/shadcn/command/command-item.svelte +37 -0
- package/src/shadcn/command/command-link-item.svelte +20 -0
- package/src/shadcn/command/command-list.svelte +20 -0
- package/src/shadcn/command/command-loading.svelte +7 -0
- package/src/shadcn/command/command-separator.svelte +17 -0
- package/src/shadcn/command/command-shortcut.svelte +23 -0
- package/src/shadcn/command/command.svelte +28 -0
- package/src/shadcn/command/index.ts +40 -0
- package/src/shadcn/context-menu/context-menu-checkbox-item.svelte +41 -0
- package/src/shadcn/context-menu/context-menu-content.svelte +28 -0
- package/src/shadcn/context-menu/context-menu-group-heading.svelte +21 -0
- package/src/shadcn/context-menu/context-menu-group.svelte +7 -0
- package/src/shadcn/context-menu/context-menu-item.svelte +27 -0
- package/src/shadcn/context-menu/context-menu-label.svelte +27 -0
- package/src/shadcn/context-menu/context-menu-portal.svelte +7 -0
- package/src/shadcn/context-menu/context-menu-radio-group.svelte +16 -0
- package/src/shadcn/context-menu/context-menu-radio-item.svelte +35 -0
- package/src/shadcn/context-menu/context-menu-separator.svelte +17 -0
- package/src/shadcn/context-menu/context-menu-shortcut.svelte +23 -0
- package/src/shadcn/context-menu/context-menu-sub-content.svelte +25 -0
- package/src/shadcn/context-menu/context-menu-sub-trigger.svelte +29 -0
- package/src/shadcn/context-menu/context-menu-sub.svelte +7 -0
- package/src/shadcn/context-menu/context-menu-trigger.svelte +17 -0
- package/src/shadcn/context-menu/context-menu.svelte +7 -0
- package/src/shadcn/context-menu/index.ts +52 -0
- package/src/shadcn/dialog/dialog-close.svelte +11 -0
- package/src/shadcn/dialog/dialog-content.svelte +57 -0
- package/src/shadcn/dialog/dialog-description.svelte +20 -0
- package/src/shadcn/dialog/dialog-footer.svelte +32 -0
- package/src/shadcn/dialog/dialog-header.svelte +20 -0
- package/src/shadcn/dialog/dialog-overlay.svelte +20 -0
- package/src/shadcn/dialog/dialog-portal.svelte +7 -0
- package/src/shadcn/dialog/dialog-title.svelte +17 -0
- package/src/shadcn/dialog/dialog-trigger.svelte +11 -0
- package/src/shadcn/dialog/dialog.svelte +7 -0
- package/src/shadcn/dialog/index.ts +34 -0
- package/src/shadcn/dropdown-menu/dropdown-menu-checkbox-group.svelte +16 -0
- package/src/shadcn/dropdown-menu/dropdown-menu-checkbox-item.svelte +43 -0
- package/src/shadcn/dropdown-menu/dropdown-menu-content.svelte +31 -0
- package/src/shadcn/dropdown-menu/dropdown-menu-group-heading.svelte +22 -0
- package/src/shadcn/dropdown-menu/dropdown-menu-group.svelte +7 -0
- package/src/shadcn/dropdown-menu/dropdown-menu-item.svelte +29 -0
- package/src/shadcn/dropdown-menu/dropdown-menu-label.svelte +27 -0
- package/src/shadcn/dropdown-menu/dropdown-menu-portal.svelte +7 -0
- package/src/shadcn/dropdown-menu/dropdown-menu-radio-group.svelte +16 -0
- package/src/shadcn/dropdown-menu/dropdown-menu-radio-item.svelte +34 -0
- package/src/shadcn/dropdown-menu/dropdown-menu-separator.svelte +17 -0
- package/src/shadcn/dropdown-menu/dropdown-menu-shortcut.svelte +23 -0
- package/src/shadcn/dropdown-menu/dropdown-menu-sub-content.svelte +20 -0
- package/src/shadcn/dropdown-menu/dropdown-menu-sub-trigger.svelte +29 -0
- package/src/shadcn/dropdown-menu/dropdown-menu-sub.svelte +7 -0
- package/src/shadcn/dropdown-menu/dropdown-menu-trigger.svelte +7 -0
- package/src/shadcn/dropdown-menu/dropdown-menu.svelte +7 -0
- package/src/shadcn/dropdown-menu/index.ts +56 -0
- package/src/shadcn/dropdown-menu/menu-item.ts +43 -0
- package/src/shadcn/empty/empty-content.svelte +23 -0
- package/src/shadcn/empty/empty-description.svelte +23 -0
- package/src/shadcn/empty/empty-header.svelte +20 -0
- package/src/shadcn/empty/empty-media.svelte +41 -0
- package/src/shadcn/empty/empty-title.svelte +20 -0
- package/src/shadcn/empty/empty.svelte +23 -0
- package/src/shadcn/empty/index.ts +22 -0
- package/src/shadcn/field/field-content.svelte +20 -0
- package/src/shadcn/field/field-description.svelte +25 -0
- package/src/shadcn/field/field-error.svelte +58 -0
- package/src/shadcn/field/field-group.svelte +23 -0
- package/src/shadcn/field/field-label.svelte +26 -0
- package/src/shadcn/field/field-legend.svelte +29 -0
- package/src/shadcn/field/field-separator.svelte +35 -0
- package/src/shadcn/field/field-set.svelte +24 -0
- package/src/shadcn/field/field-title.svelte +23 -0
- package/src/shadcn/field/field.svelte +53 -0
- package/src/shadcn/field/index.ts +36 -0
- package/src/shadcn/field/simple-field.svelte +71 -0
- package/src/shadcn/field/simple-field.test.ts +47 -0
- package/src/shadcn/hooks/highlighter.ts +132 -0
- package/src/shadcn/hooks/is-mobile.svelte.ts +9 -0
- package/src/shadcn/hooks/use-highlighted-code.svelte.ts +98 -0
- package/src/shadcn/hover-card/hover-card-content.svelte +31 -0
- package/src/shadcn/hover-card/hover-card-portal.svelte +7 -0
- package/src/shadcn/hover-card/hover-card-trigger.svelte +7 -0
- package/src/shadcn/hover-card/hover-card.svelte +7 -0
- package/src/shadcn/hover-card/index.ts +15 -0
- package/src/shadcn/input/index.ts +9 -0
- package/src/shadcn/input/input.svelte +48 -0
- package/src/shadcn/input/secret-input.svelte +75 -0
- package/src/shadcn/input-otp/index.ts +15 -0
- package/src/shadcn/input-otp/input-otp-group.svelte +20 -0
- package/src/shadcn/input-otp/input-otp-separator.svelte +25 -0
- package/src/shadcn/input-otp/input-otp-slot.svelte +26 -0
- package/src/shadcn/input-otp/input-otp.svelte +22 -0
- package/src/shadcn/kbd/index.ts +10 -0
- package/src/shadcn/kbd/kbd-group.svelte +20 -0
- package/src/shadcn/kbd/kbd.svelte +23 -0
- package/src/shadcn/label/index.ts +7 -0
- package/src/shadcn/label/label.svelte +20 -0
- package/src/shadcn/marker/index.ts +15 -0
- package/src/shadcn/marker/marker-content.svelte +23 -0
- package/src/shadcn/marker/marker-icon.svelte +21 -0
- package/src/shadcn/marker/marker.svelte +52 -0
- package/src/shadcn/message/index.ts +22 -0
- package/src/shadcn/message/message-avatar.svelte +23 -0
- package/src/shadcn/message/message-content.svelte +23 -0
- package/src/shadcn/message/message-footer.svelte +23 -0
- package/src/shadcn/message/message-group.svelte +20 -0
- package/src/shadcn/message/message-header.svelte +23 -0
- package/src/shadcn/message/message.svelte +27 -0
- package/src/shadcn/message-scroller/index.ts +28 -0
- package/src/shadcn/message-scroller/message-scroller-button.svelte +52 -0
- package/src/shadcn/message-scroller/message-scroller-content.svelte +17 -0
- package/src/shadcn/message-scroller/message-scroller-item.svelte +22 -0
- package/src/shadcn/message-scroller/message-scroller-provider.svelte +7 -0
- package/src/shadcn/message-scroller/message-scroller-viewport.svelte +20 -0
- package/src/shadcn/message-scroller/message-scroller.svelte +20 -0
- package/src/shadcn/popover/index.ts +28 -0
- package/src/shadcn/popover/popover-close.svelte +7 -0
- package/src/shadcn/popover/popover-content.svelte +38 -0
- package/src/shadcn/popover/popover-description.svelte +20 -0
- package/src/shadcn/popover/popover-header.svelte +20 -0
- package/src/shadcn/popover/popover-portal.svelte +7 -0
- package/src/shadcn/popover/popover-title.svelte +15 -0
- package/src/shadcn/popover/popover-trigger.svelte +17 -0
- package/src/shadcn/popover/popover.svelte +7 -0
- package/src/shadcn/progress/index.ts +7 -0
- package/src/shadcn/progress/progress.svelte +30 -0
- package/src/shadcn/radio-group/index.ts +10 -0
- package/src/shadcn/radio-group/radio-group-item.svelte +32 -0
- package/src/shadcn/radio-group/radio-group.svelte +19 -0
- package/src/shadcn/scroll-area/index.ts +10 -0
- package/src/shadcn/scroll-area/scroll-area-scrollbar.svelte +30 -0
- package/src/shadcn/scroll-area/scroll-area.svelte +43 -0
- package/src/shadcn/select/index.ts +37 -0
- package/src/shadcn/select/select-content.svelte +45 -0
- package/src/shadcn/select/select-group-heading.svelte +21 -0
- package/src/shadcn/select/select-group.svelte +17 -0
- package/src/shadcn/select/select-item.svelte +38 -0
- package/src/shadcn/select/select-label.svelte +20 -0
- package/src/shadcn/select/select-portal.svelte +7 -0
- package/src/shadcn/select/select-scroll-down-button.svelte +23 -0
- package/src/shadcn/select/select-scroll-up-button.svelte +23 -0
- package/src/shadcn/select/select-separator.svelte +18 -0
- package/src/shadcn/select/select-trigger.svelte +33 -0
- package/src/shadcn/select/select.svelte +11 -0
- package/src/shadcn/separator/index.ts +7 -0
- package/src/shadcn/separator/separator.svelte +23 -0
- package/src/shadcn/sheet/index.ts +34 -0
- package/src/shadcn/sheet/sheet-close.svelte +7 -0
- package/src/shadcn/sheet/sheet-content.svelte +73 -0
- package/src/shadcn/sheet/sheet-description.svelte +17 -0
- package/src/shadcn/sheet/sheet-footer.svelte +20 -0
- package/src/shadcn/sheet/sheet-header.svelte +20 -0
- package/src/shadcn/sheet/sheet-overlay.svelte +20 -0
- package/src/shadcn/sheet/sheet-portal.svelte +7 -0
- package/src/shadcn/sheet/sheet-title.svelte +17 -0
- package/src/shadcn/sheet/sheet-trigger.svelte +7 -0
- package/src/shadcn/sheet/sheet.css +48 -0
- package/src/shadcn/sheet/sheet.svelte +7 -0
- package/src/shadcn/sidebar/constants.ts +6 -0
- package/src/shadcn/sidebar/context.svelte.ts +81 -0
- package/src/shadcn/sidebar/index.ts +75 -0
- package/src/shadcn/sidebar/sidebar-content.svelte +24 -0
- package/src/shadcn/sidebar/sidebar-footer.svelte +21 -0
- package/src/shadcn/sidebar/sidebar-group-action.svelte +33 -0
- package/src/shadcn/sidebar/sidebar-group-content.svelte +21 -0
- package/src/shadcn/sidebar/sidebar-group-label.svelte +33 -0
- package/src/shadcn/sidebar/sidebar-group.svelte +21 -0
- package/src/shadcn/sidebar/sidebar-header.svelte +21 -0
- package/src/shadcn/sidebar/sidebar-input.svelte +21 -0
- package/src/shadcn/sidebar/sidebar-inset.svelte +23 -0
- package/src/shadcn/sidebar/sidebar-menu-action.svelte +37 -0
- package/src/shadcn/sidebar/sidebar-menu-badge.svelte +24 -0
- package/src/shadcn/sidebar/sidebar-menu-button.svelte +103 -0
- package/src/shadcn/sidebar/sidebar-menu-item.svelte +21 -0
- package/src/shadcn/sidebar/sidebar-menu-skeleton.svelte +36 -0
- package/src/shadcn/sidebar/sidebar-menu-sub-button.svelte +39 -0
- package/src/shadcn/sidebar/sidebar-menu-sub-item.svelte +21 -0
- package/src/shadcn/sidebar/sidebar-menu-sub.svelte +24 -0
- package/src/shadcn/sidebar/sidebar-menu.svelte +21 -0
- package/src/shadcn/sidebar/sidebar-provider.svelte +62 -0
- package/src/shadcn/sidebar/sidebar-rail.svelte +36 -0
- package/src/shadcn/sidebar/sidebar-separator.svelte +19 -0
- package/src/shadcn/sidebar/sidebar-trigger.svelte +37 -0
- package/src/shadcn/sidebar/sidebar.svelte +109 -0
- package/src/shadcn/skeleton/index.ts +7 -0
- package/src/shadcn/skeleton/skeleton.svelte +17 -0
- package/src/shadcn/slider/index.ts +7 -0
- package/src/shadcn/slider/slider.svelte +52 -0
- package/src/shadcn/sonner/index.ts +1 -0
- package/src/shadcn/sonner/sonner.svelte +31 -0
- package/src/shadcn/spinner/index.ts +1 -0
- package/src/shadcn/spinner/spinner.svelte +26 -0
- package/src/shadcn/switch/index.ts +7 -0
- package/src/shadcn/switch/switch.svelte +31 -0
- package/src/shadcn/table/index.ts +28 -0
- package/src/shadcn/table/table-body.svelte +20 -0
- package/src/shadcn/table/table-caption.svelte +20 -0
- package/src/shadcn/table/table-cell.svelte +20 -0
- package/src/shadcn/table/table-footer.svelte +20 -0
- package/src/shadcn/table/table-head.svelte +23 -0
- package/src/shadcn/table/table-header.svelte +20 -0
- package/src/shadcn/table/table-row.svelte +23 -0
- package/src/shadcn/table/table.svelte +25 -0
- package/src/shadcn/tabs/index.ts +16 -0
- package/src/shadcn/tabs/tabs-content.svelte +17 -0
- package/src/shadcn/tabs/tabs-list.svelte +36 -0
- package/src/shadcn/tabs/tabs-trigger.svelte +20 -0
- package/src/shadcn/tabs/tabs.svelte +19 -0
- package/src/shadcn/textarea/index.ts +7 -0
- package/src/shadcn/textarea/textarea.svelte +23 -0
- package/src/shadcn/toggle/index.ts +13 -0
- package/src/shadcn/toggle/toggle.svelte +53 -0
- package/src/shadcn/toggle-group/index.ts +10 -0
- package/src/shadcn/toggle-group/toggle-group-item.svelte +35 -0
- package/src/shadcn/toggle-group/toggle-group.svelte +66 -0
- package/src/shadcn/tooltip/context.ts +16 -0
- package/src/shadcn/tooltip/index.ts +19 -0
- package/src/shadcn/tooltip/tooltip-content.svelte +38 -0
- package/src/shadcn/tooltip/tooltip-portal.svelte +7 -0
- package/src/shadcn/tooltip/tooltip-provider.svelte +16 -0
- package/src/shadcn/tooltip/tooltip-trigger.svelte +7 -0
- package/src/shadcn/tooltip/tooltip.svelte +19 -0
- package/src/shadcn/trace/index.ts +29 -0
- package/src/shadcn/trace/trace-collapsible-content.svelte +29 -0
- package/src/shadcn/trace/trace-collapsible-trigger.svelte +29 -0
- package/src/shadcn/trace/trace-collapsible.svelte +20 -0
- package/src/shadcn/trace/trace-description.svelte +20 -0
- package/src/shadcn/trace/trace-icon.svelte +24 -0
- package/src/shadcn/trace/trace-line.svelte +31 -0
- package/src/shadcn/trace/trace-title.svelte +20 -0
- package/src/shadcn/trace/trace.svelte +23 -0
- package/src/shadcn/utilities.css +549 -0
- package/src/test-setup.ts +34 -0
- package/src/tokens.css +993 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Print the `sf-symbols.ts` entry for an SF Symbol, read off this Mac. node packages/ui/scripts/sf-symbol.mjs macwindow
|
|
3
|
+
|
|
4
|
+
// The SF Symbols app is the better source when somebody has it open, because its export is the artwork Apple ships for that symbol at that weight.
|
|
5
|
+
|
|
6
|
+
// This is for the rest of the time: it asks macOS for the symbol it would draw, takes the outline, and writes it in the same unit space the app's
|
|
7
|
+
|
|
8
|
+
// exports use. How it gets a vector out of an image: `NSImage(systemSymbolName:)` hands back an `NSSymbolImageRep`, which answers `outlinePath` with the CGPath it fills.
|
|
9
|
+
|
|
10
|
+
// Drawing that image into a PDF context does not work, because AppKit rasterises it into an image mask.
|
|
11
|
+
|
|
12
|
+
// `outlinePath` is not API, which is why this is a script somebody runs on their own machine rather than anything the package ships.
|
|
13
|
+
|
|
14
|
+
// The space: at 512pt Regular Medium the outline measures exactly twice the app's export on both axes, checked against folder, doc.on.doc and magnifyingglass,
|
|
15
|
+
|
|
16
|
+
// so SCALE is 0.5 and the result drops straight into the shared 768 canvas. trash is the one symbol where the two disagree beyond that,
|
|
17
|
+
|
|
18
|
+
// macOS 26 having redrawn its lid a little taller, which is a reason to prefer a fresh export when there is one.
|
|
19
|
+
|
|
20
|
+
import { execFileSync } from "node:child_process";
|
|
21
|
+
import { mkdtempSync, writeFileSync } from "node:fs";
|
|
22
|
+
import { tmpdir } from "node:os";
|
|
23
|
+
import { join } from "node:path";
|
|
24
|
+
|
|
25
|
+
const SCALE = 0.5;
|
|
26
|
+
|
|
27
|
+
const SWIFT = `
|
|
28
|
+
import AppKit
|
|
29
|
+
let cfg = NSImage.SymbolConfiguration(pointSize: 512, weight: .regular, scale: .medium)
|
|
30
|
+
guard let rep = NSImage(systemSymbolName: CommandLine.arguments[1], accessibilityDescription: nil)?
|
|
31
|
+
.withSymbolConfiguration(cfg)?.representations.first,
|
|
32
|
+
let raw = rep.value(forKey: "outlinePath") else {
|
|
33
|
+
FileHandle.standardError.write("no such symbol\\n".data(using: .utf8)!)
|
|
34
|
+
exit(2)
|
|
35
|
+
}
|
|
36
|
+
print(CFCopyDescription(raw as CFTypeRef)! as String)
|
|
37
|
+
`;
|
|
38
|
+
|
|
39
|
+
const name = process.argv[2];
|
|
40
|
+
if (!name) {
|
|
41
|
+
console.error("usage: sf-symbol.mjs <symbol-name>");
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const swiftFile = join(mkdtempSync(join(tmpdir(), "sf-symbol-")), "outline.swift");
|
|
46
|
+
writeFileSync(swiftFile, SWIFT);
|
|
47
|
+
const dump = execFileSync("swift", [swiftFile, name], {
|
|
48
|
+
encoding: "utf8",
|
|
49
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
/** CGPath's own description, which lists one segment per line in PostScript order. */
|
|
53
|
+
const commands = [];
|
|
54
|
+
for (const line of dump.split("\n")) {
|
|
55
|
+
const parts = line.trim().split(/\s+/);
|
|
56
|
+
const numbers = parts.slice(0, -1).map(Number);
|
|
57
|
+
if (numbers.some(Number.isNaN)) continue;
|
|
58
|
+
const letter = { moveto: "M", lineto: "L", curveto: "C", closepath: "Z" }[parts.at(-1)];
|
|
59
|
+
if (letter) commands.push([letter, letter === "Z" ? [] : numbers]);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (!commands.length) {
|
|
63
|
+
console.error(`no outline for ${name}`);
|
|
64
|
+
process.exit(2);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const xs = [];
|
|
68
|
+
const ys = [];
|
|
69
|
+
for (const [, numbers] of commands) {
|
|
70
|
+
for (const [i, value] of numbers.entries()) (i % 2 ? ys : xs).push(value);
|
|
71
|
+
}
|
|
72
|
+
const minX = Math.min(...xs);
|
|
73
|
+
const minY = Math.min(...ys);
|
|
74
|
+
|
|
75
|
+
/** Three decimals, which is what the app's own exports carry. */
|
|
76
|
+
const round = (value) => Math.round(value * SCALE * 1000) / 1000;
|
|
77
|
+
|
|
78
|
+
const d = commands
|
|
79
|
+
.map(([letter, numbers]) =>
|
|
80
|
+
letter === "Z"
|
|
81
|
+
? "Z"
|
|
82
|
+
: letter + numbers.map((v, i) => round(v - (i % 2 ? minY : minX))).join(" "),
|
|
83
|
+
)
|
|
84
|
+
.join("");
|
|
85
|
+
|
|
86
|
+
const key = /^[a-z][\w]*$/i.test(name) ? name : `"${name}"`;
|
|
87
|
+
console.log(` ${key}: {
|
|
88
|
+
w: ${round(Math.max(...xs) - minX)},
|
|
89
|
+
h: ${round(Math.max(...ys) - minY)},
|
|
90
|
+
paths: [
|
|
91
|
+
"${d}",
|
|
92
|
+
],
|
|
93
|
+
},`);
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/* Sync shadcn-svelte into src/shadcn and its demos into the site, three-way
|
|
3
|
+
* per file against `.upstream/`. The rules and why: ../docs/decisions.md. */
|
|
4
|
+
|
|
5
|
+
// pnpm sync:shadcn fetch, vendor, apply, report
|
|
6
|
+
// pnpm sync:shadcn --check fail if upstream moved (CI / pre-release)
|
|
7
|
+
|
|
8
|
+
import { execFileSync } from "node:child_process";
|
|
9
|
+
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
11
|
+
import { basename, dirname, relative, resolve } from "node:path";
|
|
12
|
+
import {
|
|
13
|
+
CHECK,
|
|
14
|
+
classify,
|
|
15
|
+
fetchJson,
|
|
16
|
+
fetchTextIf,
|
|
17
|
+
mapLimit,
|
|
18
|
+
readLock,
|
|
19
|
+
removeDeep,
|
|
20
|
+
report,
|
|
21
|
+
sha256,
|
|
22
|
+
writeFileDeep,
|
|
23
|
+
} from "./lib/sync-core.mjs";
|
|
24
|
+
|
|
25
|
+
/* The vega style's registry. The bare /registry lacks the chat parts (bubble,
|
|
26
|
+
* message, marker, attachment); same item and file shape otherwise. */
|
|
27
|
+
const REGISTRY = "https://shadcn-svelte.com/registry/styles/vega";
|
|
28
|
+
const WANTED = new Set(["registry:ui", "registry:hook", "registry:lib"]);
|
|
29
|
+
|
|
30
|
+
const PKG = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
31
|
+
const ROOT = resolve(PKG, "../..");
|
|
32
|
+
const VENDOR = `${PKG}/.upstream/shadcn`;
|
|
33
|
+
const LOCK = `${PKG}/.upstream/shadcn.lock.json`;
|
|
34
|
+
const LOCAL = `${PKG}/src/shadcn`;
|
|
35
|
+
|
|
36
|
+
/** Ours by choice, not upstream's: don't report them as drift. */
|
|
37
|
+
const LOCAL_ONLY = new Set(["hooks"]);
|
|
38
|
+
|
|
39
|
+
/* The registry's aliases, resolved to what this package imports. Absolute
|
|
40
|
+
* `#src/` form, so a nested target resolves without counting `../`. */
|
|
41
|
+
const ALIASES = [
|
|
42
|
+
["$UTILS$.js", "@heyramzi/utils"],
|
|
43
|
+
["$UI$/", "../src/shadcn"],
|
|
44
|
+
["$HOOKS$/", "../src/shadcn/hooks"],
|
|
45
|
+
["$LIB$/", "../src/shadcn/hooks"],
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
/* Upstream target path -> ours, where the two differ. Everything else lands at
|
|
49
|
+
* src/shadcn/<target>. */
|
|
50
|
+
const REROUTE = {
|
|
51
|
+
"is-mobile/is-mobile.svelte.ts": "src/shadcn/hooks/is-mobile.svelte.ts",
|
|
52
|
+
"highlighter/highlighter.ts": "src/shadcn/hooks/highlighter.ts",
|
|
53
|
+
"use-highlighted-code/use-highlighted-code.svelte.ts":
|
|
54
|
+
"src/shadcn/hooks/use-highlighted-code.svelte.ts",
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const DOCS = "https://raw.githubusercontent.com/huntabyte/shadcn-svelte/main/docs";
|
|
58
|
+
const DEMO_VENDOR = `${PKG}/.upstream/shadcn-demos`;
|
|
59
|
+
const DEMO_LOCAL = `${ROOT}/site/src/lib/shadcn-demos`;
|
|
60
|
+
const DEMO_MANIFEST = `${DEMO_LOCAL}/index.ts`;
|
|
61
|
+
|
|
62
|
+
/* The docs site's aliases. A demo is a docs file, not a registry payload, so it
|
|
63
|
+
* carries `$lib/...` rather than the registry's `$UI$`. */
|
|
64
|
+
const DEMO_ALIASES = [
|
|
65
|
+
["$lib/registry/ui/", "@heyramzi/ui/shadcn/"],
|
|
66
|
+
// A handful reach their neighbour by relative path instead of the alias.
|
|
67
|
+
["../ui/", "@heyramzi/ui/shadcn/"],
|
|
68
|
+
["$lib/registry/hooks/", "@heyramzi/ui/shadcn/hooks/"],
|
|
69
|
+
["$lib/utils.js", "@heyramzi/utils"],
|
|
70
|
+
// The peer here is the runes build, published under its own name.
|
|
71
|
+
["@tabler/icons-svelte/", "@tabler/icons-svelte-runes/"],
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
/* Registry primitives this package does not ship: no product imports them. Skipped, so a sync does not bring them back. */
|
|
75
|
+
const NOT_SHIPPED = new Set([
|
|
76
|
+
"aspect-ratio",
|
|
77
|
+
"button-group",
|
|
78
|
+
"carousel",
|
|
79
|
+
"chart",
|
|
80
|
+
"data-table",
|
|
81
|
+
"drawer",
|
|
82
|
+
"form",
|
|
83
|
+
"input-group",
|
|
84
|
+
"item",
|
|
85
|
+
"kbd",
|
|
86
|
+
"menubar",
|
|
87
|
+
"native-select",
|
|
88
|
+
"navigation-menu",
|
|
89
|
+
"pagination",
|
|
90
|
+
"range-calendar",
|
|
91
|
+
"resizable",
|
|
92
|
+
// Its `pascalToKebab` has no importer, in the registry or here.
|
|
93
|
+
"casing",
|
|
94
|
+
// `cn` ships from @heyramzi/utils in vibe-kit, so a copy here would be a stray.
|
|
95
|
+
"utils",
|
|
96
|
+
"questionnaire",
|
|
97
|
+
]);
|
|
98
|
+
|
|
99
|
+
/* No demo to take: `hooks`, `is-mobile` and `utils` are not components, and `sidebar` demos as a page block. */
|
|
100
|
+
const NO_DEMO = new Set([
|
|
101
|
+
"hooks",
|
|
102
|
+
"is-mobile",
|
|
103
|
+
"highlighter",
|
|
104
|
+
"use-highlighted-code",
|
|
105
|
+
"sidebar",
|
|
106
|
+
"utils",
|
|
107
|
+
]);
|
|
108
|
+
|
|
109
|
+
const adapt = (text) => ALIASES.reduce((acc, [from, to]) => acc.replaceAll(from, to), text);
|
|
110
|
+
/* The site imports the kit the way a consumer does, so the exports map names a
|
|
111
|
+
* primitive without its `/index.js` and a hook without its `.js`. */
|
|
112
|
+
const adaptDemo = (text) =>
|
|
113
|
+
DEMO_ALIASES.reduce((acc, [from, to]) => acc.replaceAll(from, to), text)
|
|
114
|
+
.replace(/(@heyramzi\/ui\/shadcn\/[a-z-]+)\/index\.js/g, "$1")
|
|
115
|
+
.replace(/(@heyramzi\/ui\/shadcn\/hooks\/[a-z-]+(?:\.svelte)?)\.js/g, "$1");
|
|
116
|
+
const readIf = (path) => (existsSync(path) ? readFileSync(path, "utf8") : null);
|
|
117
|
+
const route = (name, target) => `${PKG}/${REROUTE[`${name}/${target}`] ?? `src/shadcn/${target}`}`;
|
|
118
|
+
/** Report a file by where it lives here, not by its upstream target. */
|
|
119
|
+
const label = (path) => (path.startsWith(`${PKG}/`) ? path.slice(PKG.length + 1) : relative(ROOT, path));
|
|
120
|
+
|
|
121
|
+
const pascal = (name) => name.replace(/(^|-)([a-z])/g, (_, __, c) => c.toUpperCase());
|
|
122
|
+
|
|
123
|
+
/** Title and one-line description from the component's docs page. */
|
|
124
|
+
const meta = (doc, name) => {
|
|
125
|
+
const head = doc?.match(/^---\n([\s\S]*?)\n---/)?.[1] ?? "";
|
|
126
|
+
const field = (key) =>
|
|
127
|
+
head
|
|
128
|
+
.match(new RegExp(`^${key}:\\s*(.+)$`, "m"))?.[1]
|
|
129
|
+
.trim()
|
|
130
|
+
.replace(/^["']|["']$/g, "");
|
|
131
|
+
return { title: field("title") ?? pascal(name), note: field("description") ?? "" };
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
/** The demo's own shadcn imports, as it spells them for a consumer. */
|
|
135
|
+
const publicImports = (demo) =>
|
|
136
|
+
[...demo.matchAll(/import[^;]*?from\s+"@heyramzi\/ui\/shadcn\/[^"]+";/g)]
|
|
137
|
+
.map((m) => m[0])
|
|
138
|
+
.join("\n");
|
|
139
|
+
|
|
140
|
+
/* The section's list, generated: a primitive reaches the page by being synced,
|
|
141
|
+
* never by being typed into an array. */
|
|
142
|
+
async function writeManifest(demos) {
|
|
143
|
+
const rows = demos.map(
|
|
144
|
+
(d) =>
|
|
145
|
+
` { name: ${JSON.stringify(d.name)}, title: ${JSON.stringify(d.title)}, note: ${JSON.stringify(d.note)}, path: ${JSON.stringify(d.imports)}, Comp: ${pascal(d.name)}Demo },`,
|
|
146
|
+
);
|
|
147
|
+
const text = [
|
|
148
|
+
"/* Generated by scripts/sync-shadcn.mjs. Do not edit: the demos are upstream's",
|
|
149
|
+
" * own, and the sync rewrites this list. */",
|
|
150
|
+
'import type { Component } from "svelte";',
|
|
151
|
+
...demos.map(
|
|
152
|
+
(d) => `import ${pascal(d.name)}Demo from "./${d.name}.svelte";`,
|
|
153
|
+
),
|
|
154
|
+
"",
|
|
155
|
+
"export type ShadcnDemo = {",
|
|
156
|
+
" name: string;",
|
|
157
|
+
" title: string;",
|
|
158
|
+
" note: string;",
|
|
159
|
+
" path: string;",
|
|
160
|
+
" Comp: Component;",
|
|
161
|
+
"};",
|
|
162
|
+
"",
|
|
163
|
+
"export const SHADCN_DEMOS: ShadcnDemo[] = [",
|
|
164
|
+
...rows,
|
|
165
|
+
"];",
|
|
166
|
+
"",
|
|
167
|
+
].join("\n");
|
|
168
|
+
if (readIf(DEMO_MANIFEST) === text) return;
|
|
169
|
+
await writeFileDeep(DEMO_MANIFEST, text);
|
|
170
|
+
written.push(DEMO_MANIFEST);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const walk = (dir, base = "") =>
|
|
174
|
+
existsSync(dir)
|
|
175
|
+
? readdirSync(dir, { withFileTypes: true }).flatMap((e) =>
|
|
176
|
+
e.isDirectory() ? walk(`${dir}/${e.name}`, `${base}${e.name}/`) : [`${base}${e.name}`],
|
|
177
|
+
)
|
|
178
|
+
: [];
|
|
179
|
+
|
|
180
|
+
const index = await fetchJson(`${REGISTRY}/index.json`);
|
|
181
|
+
const items = (Array.isArray(index) ? index : (index.items ?? [])).filter(
|
|
182
|
+
(i) => WANTED.has(i.type) && !NOT_SHIPPED.has(i.name),
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
const fetched = await mapLimit(items, 8, async (item) => ({
|
|
186
|
+
item,
|
|
187
|
+
payload: await fetchJson(`${REGISTRY}/${item.relativeUrl ?? `${item.name}.json`}`),
|
|
188
|
+
}));
|
|
189
|
+
|
|
190
|
+
const previous = (await readLock(LOCK))?.entries ?? {};
|
|
191
|
+
const entries = {};
|
|
192
|
+
const buckets = { synced: [], added: [], port: [], removed: [], unported: [] };
|
|
193
|
+
const written = [];
|
|
194
|
+
let unchanged = 0;
|
|
195
|
+
|
|
196
|
+
for (const { item, payload } of fetched) {
|
|
197
|
+
const files = (payload.files ?? []).map((f) => ({
|
|
198
|
+
target: f.target ?? f.path,
|
|
199
|
+
content: f.content ?? "",
|
|
200
|
+
}));
|
|
201
|
+
// One hash per component, so the lock diff reads as a list of what moved.
|
|
202
|
+
const hash = sha256(files.map((f) => `${f.target}:${f.content}`).join("|"));
|
|
203
|
+
|
|
204
|
+
const states = files.map((f) => {
|
|
205
|
+
const base = readIf(`${VENDOR}/${item.name}/${f.target}`);
|
|
206
|
+
const next = adapt(f.content);
|
|
207
|
+
const path = route(item.name, f.target);
|
|
208
|
+
const state = classify(readIf(path), base === null ? null : adapt(base), next);
|
|
209
|
+
return { ...f, path, next, state, moved: base !== f.content };
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
/* Upstream deleted these since the last sync. Ours goes only when it was
|
|
213
|
+
* still the pristine copy of what upstream deleted. */
|
|
214
|
+
const gone = walk(`${VENDOR}/${item.name}`)
|
|
215
|
+
.filter((target) => !files.some((f) => f.target === target))
|
|
216
|
+
.map((target) => {
|
|
217
|
+
const path = route(item.name, target);
|
|
218
|
+
const base = adapt(readIf(`${VENDOR}/${item.name}/${target}`));
|
|
219
|
+
// Ours the moment it is not the pristine copy of what upstream deleted.
|
|
220
|
+
return { target, path, ours: classify(readIf(path), base, base) === "diverged" };
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
const moved = states.filter((s) => s.moved);
|
|
224
|
+
const fresh = !existsSync(`${LOCAL}/${item.name}`) && states.every((s) => s.state === "new");
|
|
225
|
+
const blocked =
|
|
226
|
+
states.some((s) => s.state === "dropped") ||
|
|
227
|
+
moved.some((s) => s.state === "diverged") ||
|
|
228
|
+
gone.some((g) => g.ours);
|
|
229
|
+
const apply = blocked ? [] : moved.filter((s) => s.state !== "current");
|
|
230
|
+
const drop = blocked ? [] : gone;
|
|
231
|
+
|
|
232
|
+
const adapted = states.filter((s) => s.state === "diverged" || s.state === "dropped");
|
|
233
|
+
entries[item.name] = {
|
|
234
|
+
type: item.type,
|
|
235
|
+
files: files.length,
|
|
236
|
+
sha256: hash,
|
|
237
|
+
...(adapted.length ? { adapted: adapted.map((s) => s.target) } : {}),
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
if (!moved.length && !gone.length) unchanged++;
|
|
241
|
+
else if (fresh) buckets.added.push(`${item.name} — ${files.length} files, new primitive`);
|
|
242
|
+
else if (blocked)
|
|
243
|
+
for (const s of moved) buckets.port.push(`${label(s.path)} — upstream moved, ${s.state} here`);
|
|
244
|
+
else if (apply.length)
|
|
245
|
+
buckets.synced.push(`${item.name} — ${apply.map((s) => basename(s.path)).join(", ")}`);
|
|
246
|
+
|
|
247
|
+
for (const g of gone)
|
|
248
|
+
buckets.removed.push(`${label(g.path)} — gone upstream${g.ours ? ", kept (ours)" : ""}`);
|
|
249
|
+
|
|
250
|
+
if (CHECK) continue;
|
|
251
|
+
|
|
252
|
+
for (const s of apply) {
|
|
253
|
+
await writeFileDeep(s.path, s.next);
|
|
254
|
+
written.push(s.path);
|
|
255
|
+
}
|
|
256
|
+
for (const g of drop) await removeDeep(g.path);
|
|
257
|
+
|
|
258
|
+
await removeDeep(`${VENDOR}/${item.name}`);
|
|
259
|
+
for (const f of files) await writeFileDeep(`${VENDOR}/${item.name}/${f.target}`, f.content);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
for (const name of Object.keys(previous)) {
|
|
263
|
+
if (entries[name]) continue;
|
|
264
|
+
buckets.removed.push(`${name} — gone from the registry`);
|
|
265
|
+
if (!CHECK) await removeDeep(`${VENDOR}/${name}`);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const ours = readdirSync(LOCAL, { withFileTypes: true })
|
|
269
|
+
.filter((e) => e.isDirectory() && !entries[e.name] && !LOCAL_ONLY.has(e.name))
|
|
270
|
+
.map((e) => e.name);
|
|
271
|
+
for (const name of ours) buckets.unported.push(`${name} — in src/shadcn, not in the registry`);
|
|
272
|
+
|
|
273
|
+
/* The docs site's own demo for each primitive, vendored on the same three-way
|
|
274
|
+
* rule. Upstream writes them; the site renders them. */
|
|
275
|
+
const demoNames = [...Object.keys(entries), ...LOCAL_ONLY]
|
|
276
|
+
.filter((name) => !NO_DEMO.has(name))
|
|
277
|
+
.toSorted();
|
|
278
|
+
|
|
279
|
+
const manifest = [];
|
|
280
|
+
|
|
281
|
+
for (const name of demoNames) {
|
|
282
|
+
const [raw, doc] = await Promise.all([
|
|
283
|
+
fetchTextIf(`${DOCS}/src/lib/registry/examples/${name}-demo.svelte`),
|
|
284
|
+
fetchTextIf(`${DOCS}/content/components/${name}.md`),
|
|
285
|
+
]);
|
|
286
|
+
if (raw === null) {
|
|
287
|
+
buckets.unported.push(`${name} — no demo upstream`);
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const path = `${DEMO_LOCAL}/${name}.svelte`;
|
|
292
|
+
const base = readIf(`${DEMO_VENDOR}/${name}.svelte`);
|
|
293
|
+
const next = adaptDemo(raw);
|
|
294
|
+
const local = readIf(path);
|
|
295
|
+
const state = classify(local, base === null ? null : adaptDemo(base), next);
|
|
296
|
+
// The imports of the demo that renders: ours when kept, upstream's when written.
|
|
297
|
+
const shown = state === "diverged" ? local : next;
|
|
298
|
+
|
|
299
|
+
if (state === "dropped") buckets.removed.push(`${label(path)} — dropped here, kept out`);
|
|
300
|
+
else manifest.push({ name, ...meta(doc, name), imports: publicImports(shown) });
|
|
301
|
+
|
|
302
|
+
if (base !== raw) {
|
|
303
|
+
if (state === "diverged") buckets.port.push(`${label(path)} — upstream moved, ours here`);
|
|
304
|
+
else if (state !== "current") buckets.synced.push(`${name} — demo`);
|
|
305
|
+
}
|
|
306
|
+
if (CHECK) continue;
|
|
307
|
+
|
|
308
|
+
if (state === "stale" || state === "new") {
|
|
309
|
+
await writeFileDeep(path, next);
|
|
310
|
+
written.push(path);
|
|
311
|
+
}
|
|
312
|
+
await writeFileDeep(`${DEMO_VENDOR}/${name}.svelte`, raw);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (!CHECK) await writeManifest(manifest);
|
|
316
|
+
|
|
317
|
+
/* Upstream ships prettier with tabs everywhere; .ts here is spaces. Root cwd,
|
|
318
|
+
* because the fmt options live in the workspace vite.config.ts. */
|
|
319
|
+
if (written.length) {
|
|
320
|
+
try {
|
|
321
|
+
execFileSync("pnpm", ["exec", "vp", "fmt", ...written], { cwd: ROOT, stdio: "pipe" });
|
|
322
|
+
} catch (error) {
|
|
323
|
+
console.error(`\n vp fmt failed, ${written.length} written files are unformatted`);
|
|
324
|
+
console.error(` ${error.message}`);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
if (!CHECK) {
|
|
329
|
+
await writeFileDeep(
|
|
330
|
+
LOCK,
|
|
331
|
+
`${JSON.stringify(
|
|
332
|
+
{
|
|
333
|
+
source: "https://github.com/huntabyte/shadcn-svelte",
|
|
334
|
+
registry: `${REGISTRY}/index.json`,
|
|
335
|
+
fetchedAt: new Date().toISOString().slice(0, 10),
|
|
336
|
+
components: items.length,
|
|
337
|
+
localOnly: [...LOCAL_ONLY],
|
|
338
|
+
entries,
|
|
339
|
+
},
|
|
340
|
+
null,
|
|
341
|
+
"\t",
|
|
342
|
+
)}\n`,
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
process.exit(
|
|
347
|
+
report({
|
|
348
|
+
title: `shadcn-svelte — ${items.length} primitives`,
|
|
349
|
+
buckets,
|
|
350
|
+
unchanged,
|
|
351
|
+
footer: `${Object.values(entries).filter((e) => e.adapted).length} adapted here, ${LOCAL_ONLY.size} ours`,
|
|
352
|
+
trailer: `Wrote ${written.length} file${written.length === 1 ? "" : "s"}. Review \`git diff src/\`; the ! lines are yours to port.`,
|
|
353
|
+
}),
|
|
354
|
+
);
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/*
|
|
3
|
+
* Sync the free transitions.dev catalogue.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/*
|
|
7
|
+
* pnpm sync:transitions fetch, vendor, report pnpm sync:transitions --check fail if upstream moved (CI / pre-release)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/*
|
|
11
|
+
* Upstream ships every transition in one JSON with a `pro` flag.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/*
|
|
15
|
+
* Paid ones are dropped here and never vendored — Ramzi, 14 Sep 2026: "the pro effects we don't need them".
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/*
|
|
19
|
+
* Everything free lands in `.upstream/transitions/` as the pristine copy; our adapted, token-fed version lives in `src/animations/`. PORTED maps one to the other. See ../src/animations/README.md.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { existsSync } from "node:fs";
|
|
23
|
+
import { fileURLToPath } from "node:url";
|
|
24
|
+
import { dirname, resolve } from "node:path";
|
|
25
|
+
import {
|
|
26
|
+
CHECK,
|
|
27
|
+
fetchJson,
|
|
28
|
+
githubHeadSha,
|
|
29
|
+
readLock,
|
|
30
|
+
removeDeep,
|
|
31
|
+
report,
|
|
32
|
+
sha256,
|
|
33
|
+
writeFileDeep,
|
|
34
|
+
} from "./lib/sync-core.mjs";
|
|
35
|
+
|
|
36
|
+
const REPO = "Jakubantalik/transitions.dev";
|
|
37
|
+
const DATA = `https://raw.githubusercontent.com/${REPO}/main/scripts/transitions-data.json`;
|
|
38
|
+
|
|
39
|
+
const PKG = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
40
|
+
const VENDOR = `${PKG}/.upstream/transitions`;
|
|
41
|
+
const LOCK = `${PKG}/.upstream/transitions.lock.json`;
|
|
42
|
+
const SRC = `${PKG}/src`;
|
|
43
|
+
|
|
44
|
+
/*
|
|
45
|
+
* upstream slug -> the file under src/ that owns it.
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
/*
|
|
49
|
+
* Fourteen of these name the component that owns the motion instead of a file in src/animations.
|
|
50
|
+
*/
|
|
51
|
+
|
|
52
|
+
/*
|
|
53
|
+
* A slug absent from this map is reported as unported. The reasoning is in ../src/animations/README.md.
|
|
54
|
+
*/
|
|
55
|
+
const PORTED = {
|
|
56
|
+
"card-resize": "animations/resize.css",
|
|
57
|
+
"number-pop-in": "animations/digit-pop-in.css",
|
|
58
|
+
"notification-badge": "shadcn/badge/badge.svelte",
|
|
59
|
+
"text-states-swap": "animations/text-swap.css",
|
|
60
|
+
"menu-dropdown": "animations/dropdown.css",
|
|
61
|
+
"modal-open-close": "animations/modal.css",
|
|
62
|
+
"panel-reveal": "shadcn/sheet/sheet-content.svelte",
|
|
63
|
+
"page-side-by-side": "animations/page-side-by-side.css",
|
|
64
|
+
"icon-swap": "effects/theme-toggle/ThemeToggle.svelte",
|
|
65
|
+
"success-check": "animations/success-check.css",
|
|
66
|
+
"avatar-group-hover": "animations/avatar-group-hover.css",
|
|
67
|
+
"error-state-shake": "animations/input-shake.css",
|
|
68
|
+
"input-clear-with-dissolve": "animations/input-clear-dissolve.css",
|
|
69
|
+
"skeleton-loader-and-reveal": "shadcn/skeleton/skeleton.svelte",
|
|
70
|
+
"texts-reveal": "effects/reveal/TextEffect.svelte",
|
|
71
|
+
"tabs-sliding": "shadcn/tabs/tabs-list.svelte",
|
|
72
|
+
"shimmer-text": "animations/shimmer-text.css",
|
|
73
|
+
"tooltip-open-close": "shadcn/tooltip/tooltip-content.svelte",
|
|
74
|
+
"3d-tilt": "animations/card-tilt.css",
|
|
75
|
+
"dropdown-menu-morph": "animations/plus-menu-morph.css",
|
|
76
|
+
accordion: "shadcn/accordion/accordion-content.svelte",
|
|
77
|
+
"like-button": "animations/like-button.css",
|
|
78
|
+
"learn-more-hover": "animations/learn-more-hover.css",
|
|
79
|
+
"spinning-counter": "animations/spinning-counter.css",
|
|
80
|
+
"thinking-states": "animations/thinking-states.css",
|
|
81
|
+
"reasoning-stream": "ai/reasoning/ReasoningContent.svelte",
|
|
82
|
+
"streaming-text": "effects/reveal/TextEffect.svelte",
|
|
83
|
+
toggle: "shadcn/switch/switch.svelte",
|
|
84
|
+
"checkbox-check": "shadcn/checkbox/checkbox.svelte",
|
|
85
|
+
"toast-open-close": "shadcn/sonner/sonner.svelte",
|
|
86
|
+
"banner-stacking": "shadcn/sonner/sonner.svelte",
|
|
87
|
+
"matrix-dot-loader": "ai/loading-state/PixelLoader.svelte",
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const catalogue = await fetchJson(DATA);
|
|
91
|
+
const free = catalogue.filter((t) => !t.pro);
|
|
92
|
+
const paid = catalogue.length - free.length;
|
|
93
|
+
|
|
94
|
+
const previous = (await readLock(LOCK))?.entries ?? {};
|
|
95
|
+
const entries = {};
|
|
96
|
+
const buckets = { added: [], changed: [], removed: [], unported: [] };
|
|
97
|
+
let unchanged = 0;
|
|
98
|
+
|
|
99
|
+
for (const { slug, name, css } of free) {
|
|
100
|
+
const hash = sha256(css);
|
|
101
|
+
entries[slug] = { name, sha256: hash, local: PORTED[slug] ?? null };
|
|
102
|
+
|
|
103
|
+
if (!previous[slug]) buckets.added.push(`${slug} — ${name}`);
|
|
104
|
+
else if (previous[slug].sha256 !== hash) buckets.changed.push(`${slug} — ${name}`);
|
|
105
|
+
else unchanged++;
|
|
106
|
+
|
|
107
|
+
if (!PORTED[slug]) buckets.unported.push(`${slug} — nothing under src/ owns it`);
|
|
108
|
+
else if (!existsSync(`${SRC}/${PORTED[slug]}`))
|
|
109
|
+
buckets.unported.push(`${slug} — PORTED says ${PORTED[slug]}, which is missing`);
|
|
110
|
+
|
|
111
|
+
if (!CHECK) await writeFileDeep(`${VENDOR}/${slug}.css`, css);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
for (const slug of Object.keys(previous)) {
|
|
115
|
+
if (entries[slug]) continue;
|
|
116
|
+
buckets.removed.push(`${slug} — gone upstream or moved to pro`);
|
|
117
|
+
if (!CHECK) await removeDeep(`${VENDOR}/${slug}.css`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (!CHECK) {
|
|
121
|
+
await writeFileDeep(
|
|
122
|
+
LOCK,
|
|
123
|
+
`${JSON.stringify(
|
|
124
|
+
{
|
|
125
|
+
source: `https://github.com/${REPO}`,
|
|
126
|
+
data: DATA,
|
|
127
|
+
ref: await githubHeadSha(REPO),
|
|
128
|
+
fetchedAt: new Date().toISOString().slice(0, 10),
|
|
129
|
+
free: free.length,
|
|
130
|
+
skippedPro: paid,
|
|
131
|
+
entries,
|
|
132
|
+
},
|
|
133
|
+
null,
|
|
134
|
+
"\t",
|
|
135
|
+
)}\n`,
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
process.exit(
|
|
140
|
+
report({
|
|
141
|
+
title: `transitions.dev — ${free.length} free`,
|
|
142
|
+
buckets,
|
|
143
|
+
unchanged,
|
|
144
|
+
footer: `${paid} pro skipped`,
|
|
145
|
+
}),
|
|
146
|
+
);
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { sfSymbolExports } from "../src/icons/apple-symbol-exports.ts";
|
|
3
|
+
|
|
4
|
+
const token = /[MLCZ]|[-+]?(?:\d*\.\d+|\d+\.?\d*)(?:e[-+]?\d+)?/gi;
|
|
5
|
+
const arity = { M: 2, L: 2, C: 6, Z: 0 };
|
|
6
|
+
|
|
7
|
+
for (const [name, symbol] of Object.entries(sfSymbolExports)) {
|
|
8
|
+
for (const dimension of [symbol.w, symbol.h, symbol.units]) {
|
|
9
|
+
assert(Number.isFinite(dimension) && dimension > 0, `${name}: invalid dimension`);
|
|
10
|
+
}
|
|
11
|
+
assert(symbol.opacity >= 0 && symbol.opacity <= 1, `${name}: invalid opacity`);
|
|
12
|
+
assert(symbol.paths.length > 0, `${name}: no paths`);
|
|
13
|
+
for (const d of symbol.paths) {
|
|
14
|
+
assert(
|
|
15
|
+
d.startsWith("M") && d.endsWith("Z"),
|
|
16
|
+
`${name}: path must start with M and close with Z`,
|
|
17
|
+
);
|
|
18
|
+
assert(d.replace(token, "").trim() === "", `${name}: unexpected path text`);
|
|
19
|
+
const tokens = d.match(token);
|
|
20
|
+
let command;
|
|
21
|
+
let count = 0;
|
|
22
|
+
const complete = () => {
|
|
23
|
+
if (!command) return;
|
|
24
|
+
assert(
|
|
25
|
+
arity[command] === 0 ? count === 0 : count > 0 && count % arity[command] === 0,
|
|
26
|
+
`${name}: invalid ${command} coordinates`,
|
|
27
|
+
);
|
|
28
|
+
};
|
|
29
|
+
for (const value of tokens) {
|
|
30
|
+
if (value in arity) {
|
|
31
|
+
complete();
|
|
32
|
+
command = value;
|
|
33
|
+
count = 0;
|
|
34
|
+
} else {
|
|
35
|
+
assert(command && Number.isFinite(Number(value)), `${name}: invalid coordinate`);
|
|
36
|
+
count++;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
complete();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
console.log(
|
|
44
|
+
`Validated ${Object.keys(sfSymbolExports).length} supplied Apple exports: dimensions, opacity and complete SVG path commands.`,
|
|
45
|
+
);
|