@once-ui-system/core 1.7.12 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +53 -0
- package/README.md +27 -0
- package/agent/once-ui-codegen.mdc +27 -0
- package/ai/catalog.json +195 -634
- package/ai/components/Banner.json +2 -2
- package/ai/components/BlobFx.json +2 -2
- package/ai/components/Carousel.json +1 -0
- package/ai/components/ChartHeader.json +2 -2
- package/ai/components/Checkbox.json +2 -1
- package/ai/components/CompareImage.json +2 -1
- package/ai/components/ContextMenu.json +2 -2
- package/ai/components/Dialog.json +2 -0
- package/ai/components/HoverCard.json +2 -2
- package/ai/components/InfiniteScroll.json +2 -2
- package/ai/components/OgCard.json +3 -2
- package/ai/components/RadioButton.json +2 -1
- package/ai/components/ScrollContainer.json +2 -2
- package/ai/components/SegmentedControl.json +2 -2
- package/ai/components/Select.json +1 -1
- package/ai/components/SplitView.json +2 -2
- package/ai/components/Swiper.json +1 -0
- package/ai/components/Table.json +17 -2
- package/ai/components/ThemeSwitcher.json +2 -2
- package/ai/components/Tooltip.json +2 -2
- package/ai/examples/blocks/content/chat1content.ts +2 -0
- package/ai/examples/dashboard.tsx +1 -1
- package/ai/examples/settings.tsx +32 -28
- package/ai/manifest.json +2 -2
- package/ai/spec.json +64 -68
- package/dist/AGENTS.md +53 -0
- package/dist/README.md +27 -0
- package/dist/agent/once-ui-codegen.mdc +27 -0
- package/dist/ai/catalog.json +195 -634
- package/dist/ai/components/Banner.json +2 -2
- package/dist/ai/components/BlobFx.json +2 -2
- package/dist/ai/components/Carousel.json +1 -0
- package/dist/ai/components/ChartHeader.json +2 -2
- package/dist/ai/components/Checkbox.json +2 -1
- package/dist/ai/components/CompareImage.json +2 -1
- package/dist/ai/components/ContextMenu.json +2 -2
- package/dist/ai/components/Dialog.json +2 -0
- package/dist/ai/components/HoverCard.json +2 -2
- package/dist/ai/components/InfiniteScroll.json +2 -2
- package/dist/ai/components/OgCard.json +3 -2
- package/dist/ai/components/RadioButton.json +2 -1
- package/dist/ai/components/ScrollContainer.json +2 -2
- package/dist/ai/components/SegmentedControl.json +2 -2
- package/dist/ai/components/Select.json +1 -1
- package/dist/ai/components/SplitView.json +2 -2
- package/dist/ai/components/Swiper.json +1 -0
- package/dist/ai/components/Table.json +17 -2
- package/dist/ai/components/ThemeSwitcher.json +2 -2
- package/dist/ai/components/Tooltip.json +2 -2
- package/dist/ai/manifest.json +2 -2
- package/dist/ai/spec.json +64 -68
- package/dist/components/Accordion.js +1 -1
- package/dist/components/Accordion.js.map +1 -1
- package/dist/components/AccordionGroup.d.ts +1 -1
- package/dist/components/AccordionGroup.d.ts.map +1 -1
- package/dist/components/AccordionGroup.js +3 -3
- package/dist/components/AccordionGroup.js.map +1 -1
- package/dist/components/Animation.d.ts +1 -1
- package/dist/components/Animation.d.ts.map +1 -1
- package/dist/components/Arrow.d.ts +1 -1
- package/dist/components/Arrow.d.ts.map +1 -1
- package/dist/components/Arrow.js +4 -4
- package/dist/components/Arrow.js.map +1 -1
- package/dist/components/ArrowNavigationContext.d.ts +2 -1
- package/dist/components/ArrowNavigationContext.d.ts.map +1 -1
- package/dist/components/ArrowNavigationContext.js +5 -3
- package/dist/components/ArrowNavigationContext.js.map +1 -1
- package/dist/components/Avatar.d.ts +2 -2
- package/dist/components/Avatar.d.ts.map +1 -1
- package/dist/components/AvatarGroup.js +1 -1
- package/dist/components/AvatarGroup.js.map +1 -1
- package/dist/components/Background.js +1 -1
- package/dist/components/Background.js.map +1 -1
- package/dist/components/Badge.js +1 -1
- package/dist/components/Badge.js.map +1 -1
- package/dist/components/Banner.d.ts.map +1 -1
- package/dist/components/Banner.js +1 -0
- package/dist/components/Banner.js.map +1 -1
- package/dist/components/BlobFx.d.ts +1 -1
- package/dist/components/BlobFx.d.ts.map +1 -1
- package/dist/components/BlobFx.js +3 -2
- package/dist/components/BlobFx.js.map +1 -1
- package/dist/components/Button.js +1 -1
- package/dist/components/Button.js.map +1 -1
- package/dist/components/Card.js +1 -1
- package/dist/components/Card.js.map +1 -1
- package/dist/components/Carousel.d.ts +3 -2
- package/dist/components/Carousel.d.ts.map +1 -1
- package/dist/components/Carousel.js +5 -5
- package/dist/components/Carousel.js.map +1 -1
- package/dist/components/Checkbox.d.ts +1 -0
- package/dist/components/Checkbox.d.ts.map +1 -1
- package/dist/components/Checkbox.js +5 -5
- package/dist/components/Checkbox.js.map +1 -1
- package/dist/components/Chip.js +1 -1
- package/dist/components/Chip.js.map +1 -1
- package/dist/components/ClientFlex.d.ts.map +1 -1
- package/dist/components/ClientFlex.js +124 -5
- package/dist/components/ClientFlex.js.map +1 -1
- package/dist/components/CompareImage.d.ts +2 -1
- package/dist/components/CompareImage.d.ts.map +1 -1
- package/dist/components/CompareImage.js +4 -4
- package/dist/components/CompareImage.js.map +1 -1
- package/dist/components/ContextMenu.d.ts +2 -2
- package/dist/components/ContextMenu.d.ts.map +1 -1
- package/dist/components/ContextMenu.js +3 -3
- package/dist/components/ContextMenu.js.map +1 -1
- package/dist/components/CountFx.d.ts +1 -1
- package/dist/components/CountFx.d.ts.map +1 -1
- package/dist/components/CountFx.js +6 -6
- package/dist/components/CountFx.js.map +1 -1
- package/dist/components/CountdownFx.d.ts +1 -1
- package/dist/components/CountdownFx.d.ts.map +1 -1
- package/dist/components/CountdownFx.js +4 -4
- package/dist/components/CountdownFx.js.map +1 -1
- package/dist/components/Cursor.d.ts +1 -1
- package/dist/components/Cursor.d.ts.map +1 -1
- package/dist/components/Cursor.js +6 -6
- package/dist/components/Cursor.js.map +1 -1
- package/dist/components/DateInput.d.ts +1 -1
- package/dist/components/DateInput.d.ts.map +1 -1
- package/dist/components/DateInput.js +6 -5
- package/dist/components/DateInput.js.map +1 -1
- package/dist/components/DatePicker.d.ts.map +1 -1
- package/dist/components/DatePicker.js +5 -5
- package/dist/components/DatePicker.js.map +1 -1
- package/dist/components/DateRangeInput.d.ts +2 -2
- package/dist/components/DateRangeInput.d.ts.map +1 -1
- package/dist/components/DateRangeInput.js +6 -4
- package/dist/components/DateRangeInput.js.map +1 -1
- package/dist/components/DateRangePicker.d.ts +1 -1
- package/dist/components/DateRangePicker.d.ts.map +1 -1
- package/dist/components/DateRangePicker.js +3 -3
- package/dist/components/DateRangePicker.js.map +1 -1
- package/dist/components/Dialog.d.ts +2 -0
- package/dist/components/Dialog.d.ts.map +1 -1
- package/dist/components/Dialog.js +32 -15
- package/dist/components/Dialog.js.map +1 -1
- package/dist/components/Dropdown.d.ts.map +1 -1
- package/dist/components/Dropdown.js +1 -1
- package/dist/components/Dropdown.js.map +1 -1
- package/dist/components/DropdownWrapper.d.ts.map +1 -1
- package/dist/components/DropdownWrapper.js +14 -11
- package/dist/components/DropdownWrapper.js.map +1 -1
- package/dist/components/ElementType.d.ts +1 -1
- package/dist/components/ElementType.d.ts.map +1 -1
- package/dist/components/ElementType.js +6 -4
- package/dist/components/ElementType.js.map +1 -1
- package/dist/components/EmojiPickerDropdown.d.ts +1 -1
- package/dist/components/EmojiPickerDropdown.d.ts.map +1 -1
- package/dist/components/EmojiPickerDropdown.js +4 -2
- package/dist/components/EmojiPickerDropdown.js.map +1 -1
- package/dist/components/Fade.js +1 -1
- package/dist/components/Fade.js.map +1 -1
- package/dist/components/FlipFx.d.ts.map +1 -1
- package/dist/components/FlipFx.js +2 -4
- package/dist/components/FlipFx.js.map +1 -1
- package/dist/components/FocusTrap.d.ts +1 -1
- package/dist/components/FocusTrap.d.ts.map +1 -1
- package/dist/components/FocusTrap.js +3 -3
- package/dist/components/FocusTrap.js.map +1 -1
- package/dist/components/GlitchFx.js +1 -1
- package/dist/components/GlitchFx.js.map +1 -1
- package/dist/components/Heading.js +1 -1
- package/dist/components/Heading.js.map +1 -1
- package/dist/components/HoloFx.d.ts +1 -1
- package/dist/components/HoloFx.d.ts.map +1 -1
- package/dist/components/HoloFx.js +17 -8
- package/dist/components/HoloFx.js.map +1 -1
- package/dist/components/Icon.js +1 -1
- package/dist/components/Icon.js.map +1 -1
- package/dist/components/IconButton.js +1 -1
- package/dist/components/IconButton.js.map +1 -1
- package/dist/components/InlineCode.js +1 -1
- package/dist/components/InlineCode.js.map +1 -1
- package/dist/components/Input.js +1 -1
- package/dist/components/Input.js.map +1 -1
- package/dist/components/LetterFx.js +1 -1
- package/dist/components/LetterFx.js.map +1 -1
- package/dist/components/ListItem.js +1 -1
- package/dist/components/ListItem.js.map +1 -1
- package/dist/components/Logo.d.ts +1 -1
- package/dist/components/Logo.d.ts.map +1 -1
- package/dist/components/Logo.js +6 -10
- package/dist/components/Logo.js.map +1 -1
- package/dist/components/LogoCloud.js +1 -1
- package/dist/components/LogoCloud.js.map +1 -1
- package/dist/components/Mask.js +1 -1
- package/dist/components/Mask.js.map +1 -1
- package/dist/components/MasonryGrid.js +1 -1
- package/dist/components/MasonryGrid.js.map +1 -1
- package/dist/components/Media.d.ts +1 -1
- package/dist/components/Media.d.ts.map +1 -1
- package/dist/components/Media.js +10 -7
- package/dist/components/Media.js.map +1 -1
- package/dist/components/MediaVideoPlayer.d.ts +12 -0
- package/dist/components/MediaVideoPlayer.d.ts.map +1 -0
- package/dist/components/MediaVideoPlayer.js +246 -0
- package/dist/components/MediaVideoPlayer.js.map +1 -0
- package/dist/components/MediaVideoPlayer.module.scss +90 -0
- package/dist/components/Modal.d.ts +1 -1
- package/dist/components/Modal.d.ts.map +1 -1
- package/dist/components/Modal.js +3 -3
- package/dist/components/Modal.js.map +1 -1
- package/dist/components/NavIcon.js +1 -1
- package/dist/components/NavIcon.js.map +1 -1
- package/dist/components/NumberInput.js +1 -1
- package/dist/components/NumberInput.js.map +1 -1
- package/dist/components/OgCard.d.ts +3 -2
- package/dist/components/OgCard.d.ts.map +1 -1
- package/dist/components/OgCard.js +2 -2
- package/dist/components/OgCard.js.map +1 -1
- package/dist/components/Option.js +1 -1
- package/dist/components/Option.js.map +1 -1
- package/dist/components/ProgressBar.d.ts +1 -1
- package/dist/components/ProgressBar.d.ts.map +1 -1
- package/dist/components/ProgressBar.js +1 -1
- package/dist/components/ProgressBar.js.map +1 -1
- package/dist/components/RadioButton.d.ts +1 -0
- package/dist/components/RadioButton.d.ts.map +1 -1
- package/dist/components/RadioButton.js +6 -6
- package/dist/components/RadioButton.js.map +1 -1
- package/dist/components/ScrollContainer.d.ts +1 -1
- package/dist/components/ScrollContainer.d.ts.map +1 -1
- package/dist/components/ScrollContainer.js +3 -3
- package/dist/components/ScrollContainer.js.map +1 -1
- package/dist/components/ScrollLock.d.ts +6 -1
- package/dist/components/ScrollLock.d.ts.map +1 -1
- package/dist/components/ScrollLock.js +39 -13
- package/dist/components/ScrollLock.js.map +1 -1
- package/dist/components/ScrollToTop.d.ts +4 -1
- package/dist/components/ScrollToTop.d.ts.map +1 -1
- package/dist/components/ScrollToTop.js +2 -1
- package/dist/components/ScrollToTop.js.map +1 -1
- package/dist/components/Scroller.d.ts +2 -2
- package/dist/components/Scroller.d.ts.map +1 -1
- package/dist/components/Scroller.js +4 -4
- package/dist/components/Scroller.js.map +1 -1
- package/dist/components/SegmentedControl.d.ts +1 -1
- package/dist/components/SegmentedControl.d.ts.map +1 -1
- package/dist/components/SegmentedControl.js +3 -3
- package/dist/components/SegmentedControl.js.map +1 -1
- package/dist/components/Select.d.ts +1 -1
- package/dist/components/Select.d.ts.map +1 -1
- package/dist/components/Select.js +1 -1
- package/dist/components/Select.js.map +1 -1
- package/dist/components/ServerFlex.d.ts.map +1 -1
- package/dist/components/ServerFlex.js +4 -2
- package/dist/components/ServerFlex.js.map +1 -1
- package/dist/components/ServerGrid.js +1 -1
- package/dist/components/ServerGrid.js.map +1 -1
- package/dist/components/SharedInteractiveStyles.module.scss +4 -0
- package/dist/components/ShineFx.d.ts +1 -1
- package/dist/components/ShineFx.d.ts.map +1 -1
- package/dist/components/ShineFx.js +4 -3
- package/dist/components/ShineFx.js.map +1 -1
- package/dist/components/Skeleton.js +1 -1
- package/dist/components/Skeleton.js.map +1 -1
- package/dist/components/Slider.js +1 -1
- package/dist/components/Slider.js.map +1 -1
- package/dist/components/Slider.module.scss +2 -2
- package/dist/components/SmartLink.js +1 -1
- package/dist/components/SmartLink.js.map +1 -1
- package/dist/components/Spinner.js +1 -1
- package/dist/components/Spinner.js.map +1 -1
- package/dist/components/StatusIndicator.js +1 -1
- package/dist/components/StatusIndicator.js.map +1 -1
- package/dist/components/StylePanel.js +1 -1
- package/dist/components/StylePanel.js.map +1 -1
- package/dist/components/Swiper.d.ts +2 -1
- package/dist/components/Swiper.d.ts.map +1 -1
- package/dist/components/Swiper.js +5 -5
- package/dist/components/Swiper.js.map +1 -1
- package/dist/components/Switch.js +1 -1
- package/dist/components/Switch.js.map +1 -1
- package/dist/components/Table.d.ts +37 -6
- package/dist/components/Table.d.ts.map +1 -1
- package/dist/components/Table.js +115 -30
- package/dist/components/Table.js.map +1 -1
- package/dist/components/Table.module.scss +174 -3
- package/dist/components/Tag.d.ts +1 -1
- package/dist/components/Tag.d.ts.map +1 -1
- package/dist/components/Tag.js +1 -1
- package/dist/components/Tag.js.map +1 -1
- package/dist/components/Text.js +1 -1
- package/dist/components/Text.js.map +1 -1
- package/dist/components/Textarea.d.ts.map +1 -1
- package/dist/components/Textarea.js +1 -2
- package/dist/components/Textarea.js.map +1 -1
- package/dist/components/ThemeInit.d.ts.map +1 -1
- package/dist/components/ThemeInit.js +6 -2
- package/dist/components/ThemeInit.js.map +1 -1
- package/dist/components/TiltFx.d.ts +1 -1
- package/dist/components/TiltFx.d.ts.map +1 -1
- package/dist/components/TiltFx.js +16 -7
- package/dist/components/TiltFx.js.map +1 -1
- package/dist/components/Timeline.d.ts +3 -3
- package/dist/components/Timeline.d.ts.map +1 -1
- package/dist/components/Timeline.js +19 -54
- package/dist/components/Timeline.js.map +1 -1
- package/dist/components/Toast.js +1 -1
- package/dist/components/Toast.js.map +1 -1
- package/dist/components/Toaster.d.ts +1 -1
- package/dist/components/Toaster.d.ts.map +1 -1
- package/dist/components/Toaster.js +4 -4
- package/dist/components/Toaster.js.map +1 -1
- package/dist/components/Toaster.module.scss +0 -2
- package/dist/components/ToggleButton.js +1 -1
- package/dist/components/ToggleButton.js.map +1 -1
- package/dist/components/Tooltip.js +1 -1
- package/dist/components/Tooltip.js.map +1 -1
- package/dist/components/TypeFx.d.ts +1 -1
- package/dist/components/TypeFx.d.ts.map +1 -1
- package/dist/components/TypeFx.js +3 -3
- package/dist/components/TypeFx.js.map +1 -1
- package/dist/components/User.js +1 -1
- package/dist/components/User.js.map +1 -1
- package/dist/components/UserMenu.d.ts +1 -1
- package/dist/components/UserMenu.d.ts.map +1 -1
- package/dist/components/UserMenu.js +4 -3
- package/dist/components/UserMenu.js.map +1 -1
- package/dist/css/styles.css +1 -1
- package/dist/css/styles.css.map +1 -1
- package/dist/css/tokens.css +1 -1
- package/dist/css/tokens.css.map +1 -1
- package/dist/data/emoji-data.json +8 -0
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +2 -0
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/useArrowNavigation.d.ts.map +1 -1
- package/dist/hooks/useArrowNavigation.js +1 -0
- package/dist/hooks/useArrowNavigation.js.map +1 -1
- package/dist/hooks/useDebounce.d.ts +1 -1
- package/dist/hooks/useDebounce.d.ts.map +1 -1
- package/dist/hooks/useDebounce.js.map +1 -1
- package/dist/hooks/useResponsiveClasses.d.ts.map +1 -1
- package/dist/hooks/useResponsiveClasses.js +45 -2
- package/dist/hooks/useResponsiveClasses.js.map +1 -1
- package/dist/icons.d.ts.map +1 -1
- package/dist/icons.js +7 -2
- package/dist/icons.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/index.js.map +1 -1
- package/dist/interfaces.d.ts +21 -36
- package/dist/interfaces.d.ts.map +1 -1
- package/dist/internal/scrollLockState.d.ts +2 -0
- package/dist/internal/scrollLockState.d.ts.map +1 -0
- package/dist/internal/scrollLockState.js +12 -0
- package/dist/internal/scrollLockState.js.map +1 -0
- package/dist/modules/code/CodeBlock.impl.d.ts +2 -0
- package/dist/modules/code/CodeBlock.impl.d.ts.map +1 -1
- package/dist/modules/code/CodeBlock.impl.js +57 -17
- package/dist/modules/code/CodeBlock.impl.js.map +1 -1
- package/dist/modules/data/BarChart.impl.js +1 -1
- package/dist/modules/data/BarChart.impl.js.map +1 -1
- package/dist/modules/data/LineBarChart.impl.js +1 -1
- package/dist/modules/data/LineBarChart.impl.js.map +1 -1
- package/dist/modules/data/LineChart.impl.js +1 -1
- package/dist/modules/data/LineChart.impl.js.map +1 -1
- package/dist/modules/data/PieChart.impl.js +1 -1
- package/dist/modules/data/PieChart.impl.js.map +1 -1
- package/dist/modules/data/utils/formatDate.js +1 -1
- package/dist/modules/data/utils/formatDate.js.map +1 -1
- package/dist/modules/index.d.ts +2 -1
- package/dist/modules/index.d.ts.map +1 -1
- package/dist/modules/index.js +2 -1
- package/dist/modules/index.js.map +1 -1
- package/dist/modules/seo/Schema.d.ts.map +1 -1
- package/dist/modules/seo/Schema.js +3 -4
- package/dist/modules/seo/Schema.js.map +1 -1
- package/dist/package.json +38 -8
- package/dist/server/og-url-validation.d.ts +10 -0
- package/dist/server/og-url-validation.d.ts.map +1 -0
- package/dist/server/og-url-validation.js +234 -0
- package/dist/server/og-url-validation.js.map +1 -0
- package/dist/server/og-utils.d.ts +5 -2
- package/dist/server/og-utils.d.ts.map +1 -1
- package/dist/server/og-utils.js +39 -14
- package/dist/server/og-utils.js.map +1 -1
- package/dist/styles/typography.scss +8 -0
- package/dist/tokens/typography.scss +2 -0
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/safe-html.d.ts +19 -0
- package/dist/utils/safe-html.d.ts.map +1 -0
- package/dist/utils/safe-html.js +43 -0
- package/dist/utils/safe-html.js.map +1 -0
- package/next-env.d.ts +5 -0
- package/package.json +55 -21
- package/scripts/copy-files.js +103 -0
- package/scripts/generate-ai-spec.js +464 -0
- package/scripts/generate-emoji-data.js +128 -0
- package/scripts/init-agent.js +156 -0
- package/scripts/validate-ai-code.js +149 -0
- package/.eslintrc.json +0 -3
- package/ai/catalog.seed.json +0 -411
- package/dist/ai/catalog.seed.json +0 -411
- package/next.config.mjs +0 -14
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates ai/spec.json — a compressed, machine-readable description of every
|
|
3
|
+
* exported component: own props (with types and defaults), shared prop mixins
|
|
4
|
+
* (encoded once), and token aliases.
|
|
5
|
+
*
|
|
6
|
+
* Compression strategy:
|
|
7
|
+
* - Props declared in src/interfaces.ts are grouped into "mixins" and emitted
|
|
8
|
+
* once; components only reference mixin names.
|
|
9
|
+
* - Props inherited from React/DOM typings (node_modules) are omitted.
|
|
10
|
+
* - Wrapper components (e.g. Row extends ComponentProps<typeof Flex>) get an
|
|
11
|
+
* "extends" reference instead of repeating the base component's props.
|
|
12
|
+
* - Each prop is a single string: "type", "type = default", or "!type" (required).
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const fs = require("fs");
|
|
16
|
+
const path = require("path");
|
|
17
|
+
|
|
18
|
+
const ROOT = path.join(__dirname, "..");
|
|
19
|
+
const SRC = path.join(ROOT, "src");
|
|
20
|
+
const OUT_DIR = path.join(ROOT, "ai");
|
|
21
|
+
const OUT_FILE = path.join(OUT_DIR, "spec.json");
|
|
22
|
+
|
|
23
|
+
const BARRELS = [
|
|
24
|
+
{ file: path.join(SRC, "components", "index.ts"), group: "components" },
|
|
25
|
+
{ file: path.join(SRC, "modules", "index.ts"), group: "modules" },
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
// Internal/low-level exports that AI should not use directly
|
|
29
|
+
const EXCLUDE = new Set([
|
|
30
|
+
"ServerFlex",
|
|
31
|
+
"ClientFlex",
|
|
32
|
+
"ServerGrid",
|
|
33
|
+
"ClientGrid",
|
|
34
|
+
"ElementType",
|
|
35
|
+
"FocusTrap",
|
|
36
|
+
"ScrollLock",
|
|
37
|
+
"ThemeInit",
|
|
38
|
+
"ArrowNavigation",
|
|
39
|
+
"Meta",
|
|
40
|
+
]);
|
|
41
|
+
|
|
42
|
+
// Token aliases worth defining once in the spec (read from types.ts source)
|
|
43
|
+
const TOKEN_ALIASES = [
|
|
44
|
+
"SpacingToken",
|
|
45
|
+
"StaticSpacingToken",
|
|
46
|
+
"ResponsiveSpacingToken",
|
|
47
|
+
"TShirtSizes",
|
|
48
|
+
"ColorScheme",
|
|
49
|
+
"ColorWeight",
|
|
50
|
+
"Colors",
|
|
51
|
+
"RadiusSize",
|
|
52
|
+
"ShadowSize",
|
|
53
|
+
"TextVariant",
|
|
54
|
+
"TextType",
|
|
55
|
+
"TextWeight",
|
|
56
|
+
"TextSize",
|
|
57
|
+
"gridSize",
|
|
58
|
+
"opacity",
|
|
59
|
+
"flex",
|
|
60
|
+
"CSSUnit",
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
function loadTsConfig(ts) {
|
|
64
|
+
const configPath = path.join(ROOT, "tsconfig.json");
|
|
65
|
+
const configFile = ts.readConfigFile(configPath, ts.sys.readFile);
|
|
66
|
+
const parsed = ts.parseJsonConfigFileContent(configFile.config, ts.sys, ROOT);
|
|
67
|
+
return parsed.options;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function isInNodeModules(decl) {
|
|
71
|
+
return decl.getSourceFile().fileName.includes("node_modules");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function normalize(p) {
|
|
75
|
+
return p.replace(/\\/g, "/");
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Strip quotes from string literal default values */
|
|
79
|
+
function cleanDefault(text) {
|
|
80
|
+
const t = text.trim();
|
|
81
|
+
if (/^["'].*["']$/.test(t)) return t.slice(1, -1);
|
|
82
|
+
return t;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Compact a type-node source text: collapse whitespace, strip leading pipe */
|
|
86
|
+
function typeText(node) {
|
|
87
|
+
if (!node) return "unknown";
|
|
88
|
+
return node
|
|
89
|
+
.getText()
|
|
90
|
+
.replace(/\s*\n\s*/g, " ")
|
|
91
|
+
.replace(/\s{2,}/g, " ")
|
|
92
|
+
.replace(/^\|\s*/, "");
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async function main() {
|
|
96
|
+
const ts = await import("typescript");
|
|
97
|
+
const options = loadTsConfig(ts);
|
|
98
|
+
const rootNames = BARRELS.map((b) => b.file).filter(fs.existsSync);
|
|
99
|
+
const program = ts.createProgram(rootNames, options);
|
|
100
|
+
const checker = program.getTypeChecker();
|
|
101
|
+
|
|
102
|
+
const interfacesFile = normalize(path.join(SRC, "interfaces.ts"));
|
|
103
|
+
|
|
104
|
+
const mixinsUsed = new Map(); // mixinName -> Map<propName, propString>
|
|
105
|
+
const components = {};
|
|
106
|
+
const warnings = [];
|
|
107
|
+
|
|
108
|
+
/** Resolve the function node (arrow/function expr) implementing a component */
|
|
109
|
+
function getComponentFunction(decl) {
|
|
110
|
+
if (ts.isFunctionDeclaration(decl)) return decl;
|
|
111
|
+
if (ts.isVariableDeclaration(decl) && decl.initializer) {
|
|
112
|
+
let init = decl.initializer;
|
|
113
|
+
// unwrap forwardRef(...) / memo(forwardRef(...))
|
|
114
|
+
while (ts.isCallExpression(init)) {
|
|
115
|
+
const arg = init.arguments[0];
|
|
116
|
+
if (!arg) return undefined;
|
|
117
|
+
init = arg;
|
|
118
|
+
}
|
|
119
|
+
if (ts.isArrowFunction(init) || ts.isFunctionExpression(init)) return init;
|
|
120
|
+
}
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Extract destructuring defaults from a component function's first param */
|
|
125
|
+
function getDefaults(fn) {
|
|
126
|
+
const defaults = {};
|
|
127
|
+
if (!fn || !fn.parameters.length) return defaults;
|
|
128
|
+
const param = fn.parameters[0];
|
|
129
|
+
if (!ts.isObjectBindingPattern(param.name)) return defaults;
|
|
130
|
+
for (const el of param.name.elements) {
|
|
131
|
+
if (el.initializer && ts.isIdentifier(el.propertyName ?? el.name)) {
|
|
132
|
+
const key = (el.propertyName ?? el.name).getText();
|
|
133
|
+
defaults[key] = cleanDefault(el.initializer.getText());
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return defaults;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** Get the props type for a component declaration */
|
|
140
|
+
function getPropsType(decl) {
|
|
141
|
+
// forwardRef<Ref, Props>(...)
|
|
142
|
+
if (ts.isVariableDeclaration(decl) && decl.initializer && ts.isCallExpression(decl.initializer)) {
|
|
143
|
+
const call = decl.initializer;
|
|
144
|
+
if (call.typeArguments && call.typeArguments.length >= 2) {
|
|
145
|
+
return checker.getTypeFromTypeNode(call.typeArguments[1]);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// const X: React.FC<Props> = ...
|
|
149
|
+
if (ts.isVariableDeclaration(decl) && decl.type && ts.isTypeReferenceNode(decl.type)) {
|
|
150
|
+
const typeName = decl.type.typeName.getText();
|
|
151
|
+
if (
|
|
152
|
+
/(^|\.)(FC|FunctionComponent)$/.test(typeName) &&
|
|
153
|
+
decl.type.typeArguments &&
|
|
154
|
+
decl.type.typeArguments.length >= 1
|
|
155
|
+
) {
|
|
156
|
+
return checker.getTypeFromTypeNode(decl.type.typeArguments[0]);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
const fn = getComponentFunction(decl);
|
|
160
|
+
if (fn && fn.parameters.length && fn.parameters[0].type) {
|
|
161
|
+
return checker.getTypeFromTypeNode(fn.parameters[0].type);
|
|
162
|
+
}
|
|
163
|
+
return undefined;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** Build the prop string: "type", "type = default", "!type" (required) */
|
|
167
|
+
function propString(propSymbol, decl, defaults) {
|
|
168
|
+
const name = propSymbol.getName();
|
|
169
|
+
let type = "unknown";
|
|
170
|
+
if (
|
|
171
|
+
(ts.isPropertySignature(decl) || ts.isPropertyDeclaration(decl)) &&
|
|
172
|
+
decl.type
|
|
173
|
+
) {
|
|
174
|
+
type = typeText(decl.type);
|
|
175
|
+
} else {
|
|
176
|
+
type = checker.typeToString(checker.getTypeOfSymbol(propSymbol));
|
|
177
|
+
}
|
|
178
|
+
const optional = (propSymbol.flags & ts.SymbolFlags.Optional) !== 0;
|
|
179
|
+
let out = optional ? type : `!${type}`;
|
|
180
|
+
if (defaults && defaults[name] !== undefined) {
|
|
181
|
+
out += ` = ${defaults[name]}`;
|
|
182
|
+
}
|
|
183
|
+
return out;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function processExport(name, symbol, group) {
|
|
187
|
+
if (EXCLUDE.has(name)) return;
|
|
188
|
+
if (!/^[A-Z]/.test(name)) return; // components only
|
|
189
|
+
|
|
190
|
+
let target = symbol;
|
|
191
|
+
if (target.flags & ts.SymbolFlags.Alias) {
|
|
192
|
+
target = checker.getAliasedSymbol(target);
|
|
193
|
+
}
|
|
194
|
+
const decl = target.valueDeclaration ?? (target.declarations && target.declarations[0]);
|
|
195
|
+
if (!decl) return;
|
|
196
|
+
if (!ts.isVariableDeclaration(decl) && !ts.isFunctionDeclaration(decl)) return;
|
|
197
|
+
|
|
198
|
+
const propsType = getPropsType(decl);
|
|
199
|
+
if (!propsType) {
|
|
200
|
+
warnings.push(`No props type resolved for ${name}; skipped`);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// For union props (e.g. ButtonProps | AnchorProps) merge member properties
|
|
205
|
+
const typesToScan = propsType.isUnion() ? propsType.types : [propsType];
|
|
206
|
+
const seen = new Map(); // propName -> symbol
|
|
207
|
+
for (const t of typesToScan) {
|
|
208
|
+
for (const p of checker.getPropertiesOfType(t)) {
|
|
209
|
+
if (!seen.has(p.getName())) seen.set(p.getName(), p);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const fn = getComponentFunction(decl);
|
|
214
|
+
const defaults = getDefaults(fn);
|
|
215
|
+
|
|
216
|
+
const ownFile = normalize(decl.getSourceFile().fileName);
|
|
217
|
+
const mixins = new Set();
|
|
218
|
+
const extendsComponents = new Set();
|
|
219
|
+
const props = {};
|
|
220
|
+
|
|
221
|
+
for (const [propName, propSym] of seen) {
|
|
222
|
+
const pDecl = propSym.valueDeclaration ?? (propSym.declarations && propSym.declarations[0]);
|
|
223
|
+
if (!pDecl) continue;
|
|
224
|
+
if (isInNodeModules(pDecl)) continue; // React/DOM inherited noise
|
|
225
|
+
|
|
226
|
+
const declFile = normalize(pDecl.getSourceFile().fileName);
|
|
227
|
+
|
|
228
|
+
if (declFile === interfacesFile) {
|
|
229
|
+
// shared mixin prop: attribute to parent interface, emit once globally
|
|
230
|
+
const parent = pDecl.parent && ts.isInterfaceDeclaration(pDecl.parent) ? pDecl.parent.name.getText() : null;
|
|
231
|
+
if (parent) {
|
|
232
|
+
mixins.add(parent);
|
|
233
|
+
if (!mixinsUsed.has(parent)) mixinsUsed.set(parent, new Map());
|
|
234
|
+
if (!mixinsUsed.get(parent).has(propName)) {
|
|
235
|
+
mixinsUsed.get(parent).set(propName, propString(propSym, pDecl, {}));
|
|
236
|
+
}
|
|
237
|
+
// keep component-level default override visible
|
|
238
|
+
if (defaults[propName] !== undefined) {
|
|
239
|
+
props[propName] = propString(propSym, pDecl, defaults);
|
|
240
|
+
}
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (declFile !== ownFile && declFile.includes("/src/")) {
|
|
246
|
+
// declared in another component's file → wrapper relationship
|
|
247
|
+
const base = path.basename(declFile).replace(/\.(tsx|ts)$/, "");
|
|
248
|
+
extendsComponents.add(base);
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
props[propName] = propString(propSym, pDecl, defaults);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const entry = { group };
|
|
256
|
+
if (extendsComponents.size) entry.extends = [...extendsComponents];
|
|
257
|
+
if (mixins.size) entry.mixins = [...mixins].sort();
|
|
258
|
+
if (Object.keys(props).length) entry.props = props;
|
|
259
|
+
components[name] = entry;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
for (const barrel of BARRELS) {
|
|
263
|
+
if (!fs.existsSync(barrel.file)) continue;
|
|
264
|
+
const sf = program.getSourceFile(barrel.file);
|
|
265
|
+
if (!sf) continue;
|
|
266
|
+
const moduleSymbol = checker.getSymbolAtLocation(sf);
|
|
267
|
+
if (!moduleSymbol) continue;
|
|
268
|
+
for (const exp of checker.getExportsOfModule(moduleSymbol)) {
|
|
269
|
+
processExport(exp.getName(), exp, barrel.group);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Emit interfaces.ts types referenced by name in prop strings (e.g. FlexBreakpointProps)
|
|
274
|
+
const interfacesSf = program.getSourceFile(path.join(SRC, "interfaces.ts"));
|
|
275
|
+
if (interfacesSf) {
|
|
276
|
+
const declared = new Map();
|
|
277
|
+
interfacesSf.forEachChild((node) => {
|
|
278
|
+
if (ts.isInterfaceDeclaration(node)) declared.set(node.name.getText(), node);
|
|
279
|
+
});
|
|
280
|
+
const allPropStrings = [];
|
|
281
|
+
for (const c of Object.values(components)) {
|
|
282
|
+
if (c.props) allPropStrings.push(...Object.values(c.props));
|
|
283
|
+
}
|
|
284
|
+
for (const m of mixinsUsed.values()) {
|
|
285
|
+
allPropStrings.push(...m.values());
|
|
286
|
+
}
|
|
287
|
+
const blob = allPropStrings.join("\n");
|
|
288
|
+
for (const [ifaceName, node] of declared) {
|
|
289
|
+
if (mixinsUsed.has(ifaceName)) continue;
|
|
290
|
+
if (!new RegExp(`\\b${ifaceName}\\b`).test(blob)) continue;
|
|
291
|
+
const propsMap = new Map();
|
|
292
|
+
for (const member of node.members) {
|
|
293
|
+
if (!ts.isPropertySignature(member) || !member.name) continue;
|
|
294
|
+
const pName = member.name.getText();
|
|
295
|
+
const optional = !!member.questionToken;
|
|
296
|
+
const t = typeText(member.type);
|
|
297
|
+
propsMap.set(pName, optional ? t : `!${t}`);
|
|
298
|
+
}
|
|
299
|
+
mixinsUsed.set(ifaceName, propsMap);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Token aliases from types.ts (compact source text)
|
|
304
|
+
const tokens = {};
|
|
305
|
+
const typesSf = program.getSourceFile(path.join(SRC, "types.ts"));
|
|
306
|
+
if (typesSf) {
|
|
307
|
+
typesSf.forEachChild((node) => {
|
|
308
|
+
if (ts.isTypeAliasDeclaration(node) && TOKEN_ALIASES.includes(node.name.getText())) {
|
|
309
|
+
tokens[node.name.getText()] = typeText(node.type);
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// IconName erases to string (Record<string, IconType>) — emit actual keys from icons.ts
|
|
315
|
+
const iconsSf = program.getSourceFile(path.join(SRC, "icons.ts"));
|
|
316
|
+
if (iconsSf) {
|
|
317
|
+
iconsSf.forEachChild((node) => {
|
|
318
|
+
if (!ts.isVariableStatement(node)) return;
|
|
319
|
+
for (const decl of node.declarationList.declarations) {
|
|
320
|
+
if (decl.name.getText() !== "iconLibrary" || !decl.initializer) continue;
|
|
321
|
+
if (!ts.isObjectLiteralExpression(decl.initializer)) continue;
|
|
322
|
+
const names = decl.initializer.properties
|
|
323
|
+
.map((p) => (p.name ? p.name.getText() : null))
|
|
324
|
+
.filter(Boolean);
|
|
325
|
+
tokens.IconName = names.map((n) => `"${n}"`).join(" | ");
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const mixins = {};
|
|
331
|
+
for (const [name, propsMap] of [...mixinsUsed.entries()].sort()) {
|
|
332
|
+
mixins[name] = Object.fromEntries(propsMap);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(ROOT, "package.json"), "utf8"));
|
|
336
|
+
const spec = {
|
|
337
|
+
name: pkg.name,
|
|
338
|
+
version: pkg.version,
|
|
339
|
+
generated: new Date().toISOString().split("T")[0],
|
|
340
|
+
format:
|
|
341
|
+
"props are 'type', 'type = default', or '!type' (required). Components with 'mixins' also accept all props of those mixins. 'extends' means all props of that component are accepted.",
|
|
342
|
+
tokens,
|
|
343
|
+
mixins,
|
|
344
|
+
components,
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
fs.mkdirSync(OUT_DIR, { recursive: true });
|
|
348
|
+
fs.writeFileSync(OUT_FILE, JSON.stringify(spec, null, 1));
|
|
349
|
+
|
|
350
|
+
emitArtifacts(spec, pkg.version);
|
|
351
|
+
|
|
352
|
+
const count = Object.keys(components).length;
|
|
353
|
+
console.log(`AI spec generated: ${count} components -> ${path.relative(ROOT, OUT_FILE)}`);
|
|
354
|
+
if (warnings.length) {
|
|
355
|
+
console.log(`Warnings (${warnings.length}):`);
|
|
356
|
+
for (const w of warnings) console.log(` - ${w}`);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/** Post-process Row/Column and emit sliced agent artifacts */
|
|
361
|
+
function emitArtifacts(spec, version) {
|
|
362
|
+
const shorthands = {
|
|
363
|
+
fill: { replaces: "fillWidth fillHeight", on: ["Row", "Column", "Flex", "Grid"] },
|
|
364
|
+
fit: { replaces: "fitWidth fitHeight", on: ["Row", "Column", "Flex", "Grid"] },
|
|
365
|
+
center: { replaces: 'horizontal="center" vertical="center"', on: ["Row", "Column", "Flex"] },
|
|
366
|
+
layout: {
|
|
367
|
+
Column: { preferOver: 'Flex direction="column"' },
|
|
368
|
+
Row: { preferOver: 'Flex direction="row"' },
|
|
369
|
+
Flex: { useWhen: "direction changes at breakpoints only" },
|
|
370
|
+
},
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
if (spec.components.Row) {
|
|
374
|
+
spec.components.Row.preset = { direction: "row" };
|
|
375
|
+
spec.components.Row.preferOver = 'Flex with direction="row"';
|
|
376
|
+
}
|
|
377
|
+
if (spec.components.Column) {
|
|
378
|
+
spec.components.Column.preset = { direction: "column" };
|
|
379
|
+
spec.components.Column.preferOver = 'Flex with direction="column"';
|
|
380
|
+
}
|
|
381
|
+
spec.shorthands = shorthands;
|
|
382
|
+
|
|
383
|
+
fs.writeFileSync(OUT_FILE, JSON.stringify(spec, null, 1));
|
|
384
|
+
|
|
385
|
+
const seedPath = path.join(OUT_DIR, "catalog.seed.json");
|
|
386
|
+
const seed = fs.existsSync(seedPath)
|
|
387
|
+
? JSON.parse(fs.readFileSync(seedPath, "utf8"))
|
|
388
|
+
: {};
|
|
389
|
+
|
|
390
|
+
const catalog = { version, generated: spec.generated, components: {} };
|
|
391
|
+
for (const [name, entry] of Object.entries(spec.components)) {
|
|
392
|
+
const meta = seed[name] || {};
|
|
393
|
+
catalog.components[name] = {
|
|
394
|
+
group: entry.group,
|
|
395
|
+
extends: entry.extends,
|
|
396
|
+
purpose: meta.purpose || `${name} component`,
|
|
397
|
+
tags: meta.tags || [entry.group],
|
|
398
|
+
...(meta.avoid ? { avoid: meta.avoid } : {}),
|
|
399
|
+
...(meta.preferOver ? { preferOver: meta.preferOver } : {}),
|
|
400
|
+
...(meta.pairsWith ? { pairsWith: meta.pairsWith } : {}),
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
fs.writeFileSync(path.join(OUT_DIR, "catalog.json"), JSON.stringify(catalog, null, 1));
|
|
404
|
+
|
|
405
|
+
const componentsDir = path.join(OUT_DIR, "components");
|
|
406
|
+
fs.mkdirSync(componentsDir, { recursive: true });
|
|
407
|
+
for (const [name, entry] of Object.entries(spec.components)) {
|
|
408
|
+
const slice = {
|
|
409
|
+
name,
|
|
410
|
+
...entry,
|
|
411
|
+
mixinsRef: entry.mixins || [],
|
|
412
|
+
tokensRef: Object.keys(entry.mixins || []).length ? "see spec.json mixins" : undefined,
|
|
413
|
+
};
|
|
414
|
+
if (slice.mixins) {
|
|
415
|
+
slice.resolvedMixins = {};
|
|
416
|
+
for (const mixin of slice.mixins) {
|
|
417
|
+
if (spec.mixins[mixin]) slice.resolvedMixins[mixin] = spec.mixins[mixin];
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
fs.writeFileSync(path.join(componentsDir, `${name}.json`), JSON.stringify(slice, null, 1));
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
const manifest = {
|
|
424
|
+
version,
|
|
425
|
+
generated: spec.generated,
|
|
426
|
+
files: {
|
|
427
|
+
spec: "spec.json",
|
|
428
|
+
catalog: "catalog.json",
|
|
429
|
+
rules: "rules.compact.md",
|
|
430
|
+
rulesFull: "rules.md",
|
|
431
|
+
recipes: "recipes.md",
|
|
432
|
+
gotchas: "gotchas.json",
|
|
433
|
+
tasks: "tasks/index.json",
|
|
434
|
+
examples: "examples/",
|
|
435
|
+
blocks: "examples/blocks/manifest.json",
|
|
436
|
+
components: "components/",
|
|
437
|
+
},
|
|
438
|
+
bootstrap: ["rules.compact.md", "catalog.json"],
|
|
439
|
+
resolve: "tasks/index.json",
|
|
440
|
+
};
|
|
441
|
+
fs.writeFileSync(path.join(OUT_DIR, "manifest.json"), JSON.stringify(manifest, null, 1));
|
|
442
|
+
|
|
443
|
+
syncDocsPublic();
|
|
444
|
+
console.log(`AI artifacts: catalog + ${Object.keys(spec.components).length} component slices`);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
function syncDocsPublic() {
|
|
448
|
+
const docsAi = path.join(ROOT, "../../apps/docs/public/ai");
|
|
449
|
+
if (!fs.existsSync(path.dirname(docsAi))) return;
|
|
450
|
+
copyDir(OUT_DIR, docsAi);
|
|
451
|
+
console.log(`Synced AI artifacts -> ${path.relative(ROOT, docsAi)}`);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
function copyDir(src, dest) {
|
|
455
|
+
if (!fs.existsSync(dest)) fs.mkdirSync(dest, { recursive: true });
|
|
456
|
+
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
457
|
+
const srcPath = path.join(src, entry.name);
|
|
458
|
+
const destPath = path.join(dest, entry.name);
|
|
459
|
+
if (entry.isDirectory()) copyDir(srcPath, destPath);
|
|
460
|
+
else fs.copyFileSync(srcPath, destPath);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
main().catch((e) => { console.error(e); process.exit(1); });
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const https = require("https");
|
|
4
|
+
|
|
5
|
+
const categories = {
|
|
6
|
+
smileys: ["face", "emotion", "person", "smile", "laugh", "sad", "angry", "love", "heart"],
|
|
7
|
+
animals: ["animal", "creature", "pet", "bug", "insect", "bird", "fish", "reptile", "mammal"],
|
|
8
|
+
food: ["food", "drink", "fruit", "vegetable", "meal", "breakfast", "lunch", "dinner"],
|
|
9
|
+
activities: ["sport", "game", "hobby", "activity", "exercise", "play"],
|
|
10
|
+
travel: [
|
|
11
|
+
"travel",
|
|
12
|
+
"place",
|
|
13
|
+
"building",
|
|
14
|
+
"location",
|
|
15
|
+
"transport",
|
|
16
|
+
"vehicle",
|
|
17
|
+
"car",
|
|
18
|
+
"plane",
|
|
19
|
+
"ship",
|
|
20
|
+
],
|
|
21
|
+
objects: ["object", "tool", "device", "book", "money", "office", "clothing"],
|
|
22
|
+
symbols: ["symbol", "sign", "arrow", "shape", "math", "punctuation", "currency", "religion"],
|
|
23
|
+
flags: ["flag", "country", "nation"],
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
function fetchEmojiData() {
|
|
27
|
+
return new Promise((resolve, reject) => {
|
|
28
|
+
https
|
|
29
|
+
.get("https://raw.githubusercontent.com/github/gemoji/master/db/emoji.json", (res) => {
|
|
30
|
+
let data = "";
|
|
31
|
+
res.on("data", (chunk) => {
|
|
32
|
+
data += chunk;
|
|
33
|
+
});
|
|
34
|
+
res.on("end", () => {
|
|
35
|
+
try {
|
|
36
|
+
const emojiData = JSON.parse(data);
|
|
37
|
+
resolve(emojiData);
|
|
38
|
+
} catch (e) {
|
|
39
|
+
reject(e);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
})
|
|
43
|
+
.on("error", (err) => {
|
|
44
|
+
reject(err);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function categorizeEmoji(emoji) {
|
|
50
|
+
let bestCategory = "objects";
|
|
51
|
+
let bestScore = 0;
|
|
52
|
+
|
|
53
|
+
const textToCheck = [emoji.description || "", ...(emoji.aliases || []), ...(emoji.tags || [])]
|
|
54
|
+
.join(" ")
|
|
55
|
+
.toLowerCase();
|
|
56
|
+
|
|
57
|
+
for (const [category, keywords] of Object.entries(categories)) {
|
|
58
|
+
const score = keywords.reduce((count, keyword) => {
|
|
59
|
+
return count + (textToCheck.includes(keyword) ? 1 : 0);
|
|
60
|
+
}, 0);
|
|
61
|
+
|
|
62
|
+
if (score > bestScore) {
|
|
63
|
+
bestScore = score;
|
|
64
|
+
bestCategory = category;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return bestCategory;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function generateEmojiData() {
|
|
72
|
+
try {
|
|
73
|
+
console.log("Fetching emoji data...");
|
|
74
|
+
const rawEmojiData = await fetchEmojiData();
|
|
75
|
+
|
|
76
|
+
const categorizedEmojis = {};
|
|
77
|
+
|
|
78
|
+
Object.keys(categories).forEach((category) => {
|
|
79
|
+
categorizedEmojis[category] = [];
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Helper function to check if a string contains valid emoji characters
|
|
83
|
+
function isValidEmoji(str) {
|
|
84
|
+
// Check if the string exists and isn't just whitespace
|
|
85
|
+
if (!str || str.trim() === "") return false;
|
|
86
|
+
|
|
87
|
+
// Check for replacement characters or known broken sequences
|
|
88
|
+
if (str.includes("�")) return false;
|
|
89
|
+
|
|
90
|
+
// Simple length check - emoji shouldn't be too long
|
|
91
|
+
if (str.length > 10) return false;
|
|
92
|
+
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
rawEmojiData.forEach((emoji) => {
|
|
97
|
+
if (emoji.emoji && isValidEmoji(emoji.emoji)) {
|
|
98
|
+
const category = categorizeEmoji(emoji);
|
|
99
|
+
categorizedEmojis[category].push({
|
|
100
|
+
char: emoji.emoji,
|
|
101
|
+
description: emoji.description || "",
|
|
102
|
+
aliases: emoji.aliases || [],
|
|
103
|
+
tags: emoji.tags || [],
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
const outputPath = path.join(__dirname, "../src/data/emoji-data.json");
|
|
109
|
+
|
|
110
|
+
const dir = path.dirname(outputPath);
|
|
111
|
+
if (!fs.existsSync(dir)) {
|
|
112
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
fs.writeFileSync(outputPath, JSON.stringify(categorizedEmojis, null, 2), { encoding: "utf8" });
|
|
116
|
+
|
|
117
|
+
console.log(`Emoji data generated successfully at ${outputPath}`);
|
|
118
|
+
console.log(`Total emojis: ${rawEmojiData.length}`);
|
|
119
|
+
Object.keys(categorizedEmojis).forEach((category) => {
|
|
120
|
+
console.log(`${category}: ${categorizedEmojis[category].length} emojis`);
|
|
121
|
+
});
|
|
122
|
+
} catch (error) {
|
|
123
|
+
console.error("Error generating emoji data:", error);
|
|
124
|
+
process.exit(1);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
generateEmojiData();
|