@ng-cn/core 1.0.14 → 1.0.15

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ng-cn/core",
3
- "version": "1.0.14",
3
+ "version": "1.0.15",
4
4
  "description": "Beautifully designed Angular components built with Tailwind CSS v4 - The official Angular port of shadcn/ui",
5
5
  "keywords": [
6
6
  "angular",
@@ -17,6 +17,65 @@ const ALL_COMPONENTS = [
17
17
  'table', 'tabs', 'textarea', 'toast', 'toggle', 'toggle-group', 'tooltip',
18
18
  'typography'
19
19
  ];
20
+ const COMPONENT_REGISTRY = {
21
+ accordion: { files: ['accordion.component.ts', 'accordion-content.component.ts', 'accordion-context.ts', 'accordion-item.component.ts', 'accordion-trigger.component.ts', 'index.ts'] },
22
+ alert: { files: ['alert.component.ts', 'alert-title.component.ts', 'alert-description.component.ts', 'alert-variants.ts', 'index.ts'] },
23
+ 'alert-dialog': { files: ['alert-dialog.component.ts', 'alert-dialog-action.component.ts', 'alert-dialog-cancel.component.ts', 'alert-dialog-content.component.ts', 'alert-dialog-context.ts', 'alert-dialog-description.component.ts', 'alert-dialog-footer.component.ts', 'alert-dialog-header.component.ts', 'alert-dialog-title.component.ts', 'alert-dialog-trigger.component.ts', 'index.ts'] },
24
+ 'aspect-ratio': { files: ['aspect-ratio.component.ts', 'index.ts'] },
25
+ avatar: { files: ['avatar.component.ts', 'avatar-fallback.component.ts', 'avatar-image.component.ts', 'ui-avatar.component.ts', 'index.ts'] },
26
+ badge: { files: ['badge.component.ts', 'badge-variants.ts', 'index.ts'] },
27
+ breadcrumb: { files: ['breadcrumb.component.ts', 'breadcrumb-ellipsis.component.ts', 'breadcrumb-item.component.ts', 'breadcrumb-link.component.ts', 'breadcrumb-list.component.ts', 'breadcrumb-page.component.ts', 'breadcrumb-separator.component.ts', 'index.ts'] },
28
+ button: { files: ['button.component.ts', 'button-variants.ts', 'index.ts'] },
29
+ 'button-group': { files: ['button-group.component.ts', 'button-group-variants.ts', 'index.ts'] },
30
+ calendar: { files: ['calendar.component.ts', 'index.ts'] },
31
+ card: { files: ['card.component.ts', 'card-action.component.ts', 'card-content.component.ts', 'card-description.component.ts', 'card-footer.component.ts', 'card-header.component.ts', 'card-title.component.ts', 'index.ts'] },
32
+ carousel: { files: ['carousel.component.ts', 'carousel-content.component.ts', 'carousel-context.ts', 'carousel-item.component.ts', 'carousel-next.component.ts', 'carousel-previous.component.ts', 'index.ts'] },
33
+ chart: { files: ['chart.component.ts', 'chart-container.component.ts', 'chart-context.ts', 'chart-legend.component.ts', 'chart-legend-content.component.ts', 'chart-tooltip.component.ts', 'chart-tooltip-content.component.ts', 'index.ts'] },
34
+ checkbox: { files: ['checkbox.component.ts', 'index.ts'] },
35
+ collapsible: { files: ['collapsible.component.ts', 'collapsible-content.component.ts', 'collapsible-context.ts', 'collapsible-trigger.component.ts', 'index.ts'] },
36
+ combobox: { files: ['combobox.component.ts', 'combobox-content.component.ts', 'combobox-context.ts', 'combobox-empty.component.ts', 'combobox-group.component.ts', 'combobox-input.component.ts', 'combobox-item.component.ts', 'combobox-list.component.ts', 'combobox-trigger.component.ts', 'combobox-value.component.ts', 'index.ts'] },
37
+ command: { files: ['command.component.ts', 'command-context.ts', 'command-dialog.component.ts', 'command-empty.component.ts', 'command-group.component.ts', 'command-input.component.ts', 'command-item.component.ts', 'command-list.component.ts', 'command-separator.component.ts', 'command-shortcut.component.ts', 'index.ts'] },
38
+ 'context-menu': { files: ['context-menu.component.ts', 'context-menu-checkbox-item.component.ts', 'context-menu-content.component.ts', 'context-menu-context.ts', 'context-menu-item.component.ts', 'context-menu-label.component.ts', 'context-menu-radio-group.component.ts', 'context-menu-radio-item.component.ts', 'context-menu-separator.component.ts', 'context-menu-shortcut.component.ts', 'context-menu-sub.component.ts', 'context-menu-sub-content.component.ts', 'context-menu-sub-trigger.component.ts', 'context-menu-trigger.component.ts', 'index.ts'] },
39
+ 'data-table': { files: ['data-table.component.ts', 'data-table-content.component.ts', 'data-table-context.ts', 'data-table-pagination.component.ts', 'data-table-search.component.ts', 'data-table-toolbar.component.ts', 'data-table-view-options.component.ts', 'index.ts'] },
40
+ 'date-picker': { files: ['date-picker.component.ts', 'index.ts'] },
41
+ dialog: { files: ['dialog.component.ts', 'dialog-close.component.ts', 'dialog-content.component.ts', 'dialog-context.ts', 'dialog-description.component.ts', 'dialog-footer.component.ts', 'dialog-header.component.ts', 'dialog-title.component.ts', 'dialog-trigger.component.ts', 'index.ts'] },
42
+ drawer: { files: ['drawer.component.ts', 'drawer-close.component.ts', 'drawer-content.component.ts', 'drawer-context.ts', 'drawer-description.component.ts', 'drawer-footer.component.ts', 'drawer-header.component.ts', 'drawer-title.component.ts', 'drawer-trigger.component.ts', 'index.ts'] },
43
+ 'dropdown-menu': { files: ['dropdown-menu.component.ts', 'dropdown-menu-checkbox-item.component.ts', 'dropdown-menu-content.component.ts', 'dropdown-menu-context.ts', 'dropdown-menu-group.component.ts', 'dropdown-menu-item.component.ts', 'dropdown-menu-label.component.ts', 'dropdown-menu-radio-group.component.ts', 'dropdown-menu-radio-item.component.ts', 'dropdown-menu-separator.component.ts', 'dropdown-menu-shortcut.component.ts', 'dropdown-menu-sub.component.ts', 'dropdown-menu-sub-content.component.ts', 'dropdown-menu-sub-trigger.component.ts', 'dropdown-menu-trigger.component.ts', 'index.ts'] },
44
+ empty: { files: ['empty.component.ts', 'empty-action.component.ts', 'empty-description.component.ts', 'empty-icon.component.ts', 'empty-title.component.ts', 'index.ts'] },
45
+ form: { files: ['form.component.ts', 'form-context.ts', 'form-control.component.ts', 'form-description.component.ts', 'form-field.component.ts', 'form-item.component.ts', 'form-label.component.ts', 'form-message.component.ts', 'index.ts'] },
46
+ 'hover-card': { files: ['hover-card.component.ts', 'hover-card-content.component.ts', 'hover-card-context.ts', 'hover-card-trigger.component.ts', 'index.ts'] },
47
+ input: { files: ['input.component.ts', 'index.ts'] },
48
+ 'input-group': { files: ['input-group.component.ts', 'input-group-addon.component.ts', 'input-group-input.component.ts', 'index.ts'] },
49
+ 'input-otp': { files: ['input-otp.component.ts', 'input-otp-context.ts', 'input-otp-group.component.ts', 'input-otp-separator.component.ts', 'input-otp-slot.component.ts', 'index.ts'] },
50
+ kbd: { files: ['kbd.component.ts', 'kbd-variants.ts', 'index.ts'] },
51
+ label: { files: ['label.component.ts', 'index.ts'] },
52
+ menubar: { files: ['menubar.component.ts', 'menubar-checkbox-item.component.ts', 'menubar-content.component.ts', 'menubar-context.ts', 'menubar-item.component.ts', 'menubar-label.component.ts', 'menubar-menu.component.ts', 'menubar-radio-group.component.ts', 'menubar-radio-item.component.ts', 'menubar-separator.component.ts', 'menubar-shortcut.component.ts', 'menubar-sub.component.ts', 'menubar-sub-content.component.ts', 'menubar-sub-trigger.component.ts', 'menubar-trigger.component.ts', 'index.ts'] },
53
+ 'native-select': { files: ['native-select.component.ts', 'native-select-variants.ts', 'index.ts'] },
54
+ 'navigation-menu': { files: ['navigation-menu.component.ts', 'navigation-menu-content.component.ts', 'navigation-menu-context.ts', 'navigation-menu-indicator.component.ts', 'navigation-menu-item.component.ts', 'navigation-menu-link.component.ts', 'navigation-menu-list.component.ts', 'navigation-menu-trigger.component.ts', 'navigation-menu-trigger-style.ts', 'navigation-menu-viewport.component.ts', 'index.ts'] },
55
+ pagination: { files: ['pagination.component.ts', 'pagination-content.component.ts', 'pagination-ellipsis.component.ts', 'pagination-item.component.ts', 'pagination-link.component.ts', 'pagination-next.component.ts', 'pagination-previous.component.ts', 'index.ts'] },
56
+ popover: { files: ['popover.component.ts', 'popover-anchor.component.ts', 'popover-content.component.ts', 'popover-context.ts', 'popover-trigger.component.ts', 'index.ts'] },
57
+ progress: { files: ['progress.component.ts', 'index.ts'] },
58
+ 'radio-group': { files: ['radio-group.component.ts', 'radio-group-context.ts', 'radio-group-item.component.ts', 'index.ts'] },
59
+ resizable: { files: ['resizable-panel-group.component.ts', 'resizable-context.ts', 'resizable-handle.component.ts', 'resizable-panel.component.ts', 'index.ts'] },
60
+ 'scroll-area': { files: ['scroll-area.component.ts', 'scroll-bar.component.ts', 'index.ts'] },
61
+ segmented: { files: ['segmented.component.ts', 'segmented-context.ts', 'segmented-item.component.ts', 'segmented-variants.ts', 'index.ts'] },
62
+ select: { files: ['select.component.ts', 'select-content.component.ts', 'select-context.ts', 'select-group.component.ts', 'select-item.component.ts', 'select-label.component.ts', 'select-separator.component.ts', 'select-trigger.component.ts', 'select-value.component.ts', 'index.ts'] },
63
+ separator: { files: ['separator.component.ts', 'index.ts'] },
64
+ sheet: { files: ['sheet.component.ts', 'sheet-close.component.ts', 'sheet-content.component.ts', 'sheet-context.ts', 'sheet-description.component.ts', 'sheet-footer.component.ts', 'sheet-header.component.ts', 'sheet-title.component.ts', 'sheet-trigger.component.ts', 'sheet-variants.ts', 'index.ts'] },
65
+ sidebar: { files: ['sidebar.component.ts', 'sidebar-content.component.ts', 'sidebar-context.ts', 'sidebar-footer.component.ts', 'sidebar-group.component.ts', 'sidebar-group-action.component.ts', 'sidebar-group-content.component.ts', 'sidebar-group-label.component.ts', 'sidebar-header.component.ts', 'sidebar-input.component.ts', 'sidebar-inset.component.ts', 'sidebar-menu.component.ts', 'sidebar-menu-action.component.ts', 'sidebar-menu-badge.component.ts', 'sidebar-menu-button.component.ts', 'sidebar-menu-item.component.ts', 'sidebar-menu-skeleton.component.ts', 'sidebar-menu-sub.component.ts', 'sidebar-menu-sub-button.component.ts', 'sidebar-menu-sub-item.component.ts', 'sidebar-provider.component.ts', 'sidebar-rail.component.ts', 'sidebar-route-active.service.ts', 'sidebar-separator.component.ts', 'sidebar-trigger.component.ts', 'index.ts'] },
66
+ skeleton: { files: ['skeleton.component.ts', 'index.ts'] },
67
+ slider: { files: ['slider.component.ts', 'index.ts'] },
68
+ spinner: { files: ['spinner.component.ts', 'spinner-variants.ts', 'index.ts'] },
69
+ switch: { files: ['switch.component.ts', 'index.ts'] },
70
+ table: { files: ['table.component.ts', 'table-body.component.ts', 'table-caption.component.ts', 'table-cell.component.ts', 'table-footer.component.ts', 'table-head.component.ts', 'table-header.component.ts', 'table-row.component.ts', 'index.ts'] },
71
+ tabs: { files: ['tabs.component.ts', 'tabs-content.component.ts', 'tabs-context.ts', 'tabs-list.component.ts', 'tabs-trigger.component.ts', 'index.ts'] },
72
+ textarea: { files: ['textarea.component.ts', 'index.ts'] },
73
+ toast: { files: ['toast.component.ts', 'toast-action.component.ts', 'toast-description.component.ts', 'toast-title.component.ts', 'toast-variants.ts', 'toast.service.ts', 'toaster.component.ts', 'index.ts'] },
74
+ toggle: { files: ['toggle.component.ts', 'toggle-variants.ts', 'index.ts'] },
75
+ 'toggle-group': { files: ['toggle-group.component.ts', 'toggle-group-context.ts', 'toggle-group-item.component.ts', 'index.ts'] },
76
+ tooltip: { files: ['tooltip.component.ts', 'tooltip-content.component.ts', 'tooltip-context.ts', 'tooltip-provider.component.ts', 'tooltip-trigger.component.ts', 'index.ts'] },
77
+ typography: { files: ['typography-blockquote.component.ts', 'typography-h1.component.ts', 'typography-h2.component.ts', 'typography-h3.component.ts', 'typography-h4.component.ts', 'typography-inline-code.component.ts', 'typography-large.component.ts', 'typography-lead.component.ts', 'typography-list.component.ts', 'typography-muted.component.ts', 'typography-p.component.ts', 'typography-small.component.ts', 'index.ts'] },
78
+ };
20
79
  // Registry of all utility files that need to be copied
21
80
  const UTILS_FILES_REGISTRY = [
22
81
  // Core utils
@@ -717,15 +776,55 @@ function ngAdd(options) {
717
776
  else {
718
777
  context.logger.info('📦 Selected Components');
719
778
  }
720
- // For now, just log the components that would be copied
721
- // Components will be available via 'ng g @ng-cn/core:component <name>'
722
- for (const component of componentsToInstall) {
723
- context.logger.info(` + ${component} (available via: ng g @ng-cn/core:component ${component})`);
779
+ // Actually copy the component files
780
+ const componentBasePath = 'node_modules/@ng-cn/core/src/app/lib/components/ui';
781
+ const fallbackBasePath = 'src/app/lib/components/ui';
782
+ let totalFilesCopied = 0;
783
+ for (const componentName of componentsToInstall) {
784
+ const componentInfo = COMPONENT_REGISTRY[componentName];
785
+ if (!componentInfo) {
786
+ context.logger.warn(` ⚠ Unknown component: ${componentName}`);
787
+ continue;
788
+ }
789
+ const targetComponentPath = `/src/app/lib/components/ui/${componentName}`;
790
+ let filesCopied = 0;
791
+ for (const file of componentInfo.files) {
792
+ // Try package path first, then fallback to local dev path
793
+ let sourcePath = `${componentBasePath}/${componentName}/${file}`;
794
+ const targetPath = `${targetComponentPath}/${file}`;
795
+ let content = tree.read(sourcePath);
796
+ if (!content) {
797
+ // Fallback to local development path
798
+ sourcePath = `${fallbackBasePath}/${componentName}/${file}`;
799
+ content = tree.read(sourcePath);
800
+ }
801
+ if (content) {
802
+ if (tree.exists(targetPath)) {
803
+ tree.overwrite(targetPath, content);
804
+ }
805
+ else {
806
+ tree.create(targetPath, content);
807
+ }
808
+ filesCopied++;
809
+ }
810
+ }
811
+ if (filesCopied > 0) {
812
+ context.logger.info(` ✓ ${componentName} (${filesCopied} files)`);
813
+ totalFilesCopied += filesCopied;
814
+ }
815
+ else {
816
+ context.logger.warn(` ⚠ ${componentName} - no files found (use: ng g @ng-cn/core:c ${componentName})`);
817
+ }
724
818
  }
725
819
  context.logger.info('');
726
- context.logger.info('💡 To copy individual components, use:');
727
- context.logger.info(` ng g @ng-cn/core:component button`);
728
- context.logger.info(` ng g @ng-cn/core:component card`);
820
+ if (totalFilesCopied > 0) {
821
+ context.logger.info(` 📦 ${totalFilesCopied} total files copied`);
822
+ }
823
+ else {
824
+ context.logger.info('💡 To copy components after install, use:');
825
+ context.logger.info(` ng g @ng-cn/core:component button`);
826
+ context.logger.info(` ng g @ng-cn/core:component card`);
827
+ }
729
828
  }
730
829
  // Success message with ASCII art banner
731
830
  context.logger.info('');
@@ -27,6 +27,71 @@ const ALL_COMPONENTS = [
27
27
  'typography'
28
28
  ];
29
29
 
30
+ // Component registry - maps component names to their file structure (for --components option)
31
+ interface ComponentInfo {
32
+ files: string[];
33
+ }
34
+
35
+ const COMPONENT_REGISTRY: Record<string, ComponentInfo> = {
36
+ accordion: { files: ['accordion.component.ts', 'accordion-content.component.ts', 'accordion-context.ts', 'accordion-item.component.ts', 'accordion-trigger.component.ts', 'index.ts'] },
37
+ alert: { files: ['alert.component.ts', 'alert-title.component.ts', 'alert-description.component.ts', 'alert-variants.ts', 'index.ts'] },
38
+ 'alert-dialog': { files: ['alert-dialog.component.ts', 'alert-dialog-action.component.ts', 'alert-dialog-cancel.component.ts', 'alert-dialog-content.component.ts', 'alert-dialog-context.ts', 'alert-dialog-description.component.ts', 'alert-dialog-footer.component.ts', 'alert-dialog-header.component.ts', 'alert-dialog-title.component.ts', 'alert-dialog-trigger.component.ts', 'index.ts'] },
39
+ 'aspect-ratio': { files: ['aspect-ratio.component.ts', 'index.ts'] },
40
+ avatar: { files: ['avatar.component.ts', 'avatar-fallback.component.ts', 'avatar-image.component.ts', 'ui-avatar.component.ts', 'index.ts'] },
41
+ badge: { files: ['badge.component.ts', 'badge-variants.ts', 'index.ts'] },
42
+ breadcrumb: { files: ['breadcrumb.component.ts', 'breadcrumb-ellipsis.component.ts', 'breadcrumb-item.component.ts', 'breadcrumb-link.component.ts', 'breadcrumb-list.component.ts', 'breadcrumb-page.component.ts', 'breadcrumb-separator.component.ts', 'index.ts'] },
43
+ button: { files: ['button.component.ts', 'button-variants.ts', 'index.ts'] },
44
+ 'button-group': { files: ['button-group.component.ts', 'button-group-variants.ts', 'index.ts'] },
45
+ calendar: { files: ['calendar.component.ts', 'index.ts'] },
46
+ card: { files: ['card.component.ts', 'card-action.component.ts', 'card-content.component.ts', 'card-description.component.ts', 'card-footer.component.ts', 'card-header.component.ts', 'card-title.component.ts', 'index.ts'] },
47
+ carousel: { files: ['carousel.component.ts', 'carousel-content.component.ts', 'carousel-context.ts', 'carousel-item.component.ts', 'carousel-next.component.ts', 'carousel-previous.component.ts', 'index.ts'] },
48
+ chart: { files: ['chart.component.ts', 'chart-container.component.ts', 'chart-context.ts', 'chart-legend.component.ts', 'chart-legend-content.component.ts', 'chart-tooltip.component.ts', 'chart-tooltip-content.component.ts', 'index.ts'] },
49
+ checkbox: { files: ['checkbox.component.ts', 'index.ts'] },
50
+ collapsible: { files: ['collapsible.component.ts', 'collapsible-content.component.ts', 'collapsible-context.ts', 'collapsible-trigger.component.ts', 'index.ts'] },
51
+ combobox: { files: ['combobox.component.ts', 'combobox-content.component.ts', 'combobox-context.ts', 'combobox-empty.component.ts', 'combobox-group.component.ts', 'combobox-input.component.ts', 'combobox-item.component.ts', 'combobox-list.component.ts', 'combobox-trigger.component.ts', 'combobox-value.component.ts', 'index.ts'] },
52
+ command: { files: ['command.component.ts', 'command-context.ts', 'command-dialog.component.ts', 'command-empty.component.ts', 'command-group.component.ts', 'command-input.component.ts', 'command-item.component.ts', 'command-list.component.ts', 'command-separator.component.ts', 'command-shortcut.component.ts', 'index.ts'] },
53
+ 'context-menu': { files: ['context-menu.component.ts', 'context-menu-checkbox-item.component.ts', 'context-menu-content.component.ts', 'context-menu-context.ts', 'context-menu-item.component.ts', 'context-menu-label.component.ts', 'context-menu-radio-group.component.ts', 'context-menu-radio-item.component.ts', 'context-menu-separator.component.ts', 'context-menu-shortcut.component.ts', 'context-menu-sub.component.ts', 'context-menu-sub-content.component.ts', 'context-menu-sub-trigger.component.ts', 'context-menu-trigger.component.ts', 'index.ts'] },
54
+ 'data-table': { files: ['data-table.component.ts', 'data-table-content.component.ts', 'data-table-context.ts', 'data-table-pagination.component.ts', 'data-table-search.component.ts', 'data-table-toolbar.component.ts', 'data-table-view-options.component.ts', 'index.ts'] },
55
+ 'date-picker': { files: ['date-picker.component.ts', 'index.ts'] },
56
+ dialog: { files: ['dialog.component.ts', 'dialog-close.component.ts', 'dialog-content.component.ts', 'dialog-context.ts', 'dialog-description.component.ts', 'dialog-footer.component.ts', 'dialog-header.component.ts', 'dialog-title.component.ts', 'dialog-trigger.component.ts', 'index.ts'] },
57
+ drawer: { files: ['drawer.component.ts', 'drawer-close.component.ts', 'drawer-content.component.ts', 'drawer-context.ts', 'drawer-description.component.ts', 'drawer-footer.component.ts', 'drawer-header.component.ts', 'drawer-title.component.ts', 'drawer-trigger.component.ts', 'index.ts'] },
58
+ 'dropdown-menu': { files: ['dropdown-menu.component.ts', 'dropdown-menu-checkbox-item.component.ts', 'dropdown-menu-content.component.ts', 'dropdown-menu-context.ts', 'dropdown-menu-group.component.ts', 'dropdown-menu-item.component.ts', 'dropdown-menu-label.component.ts', 'dropdown-menu-radio-group.component.ts', 'dropdown-menu-radio-item.component.ts', 'dropdown-menu-separator.component.ts', 'dropdown-menu-shortcut.component.ts', 'dropdown-menu-sub.component.ts', 'dropdown-menu-sub-content.component.ts', 'dropdown-menu-sub-trigger.component.ts', 'dropdown-menu-trigger.component.ts', 'index.ts'] },
59
+ empty: { files: ['empty.component.ts', 'empty-action.component.ts', 'empty-description.component.ts', 'empty-icon.component.ts', 'empty-title.component.ts', 'index.ts'] },
60
+ form: { files: ['form.component.ts', 'form-context.ts', 'form-control.component.ts', 'form-description.component.ts', 'form-field.component.ts', 'form-item.component.ts', 'form-label.component.ts', 'form-message.component.ts', 'index.ts'] },
61
+ 'hover-card': { files: ['hover-card.component.ts', 'hover-card-content.component.ts', 'hover-card-context.ts', 'hover-card-trigger.component.ts', 'index.ts'] },
62
+ input: { files: ['input.component.ts', 'index.ts'] },
63
+ 'input-group': { files: ['input-group.component.ts', 'input-group-addon.component.ts', 'input-group-input.component.ts', 'index.ts'] },
64
+ 'input-otp': { files: ['input-otp.component.ts', 'input-otp-context.ts', 'input-otp-group.component.ts', 'input-otp-separator.component.ts', 'input-otp-slot.component.ts', 'index.ts'] },
65
+ kbd: { files: ['kbd.component.ts', 'kbd-variants.ts', 'index.ts'] },
66
+ label: { files: ['label.component.ts', 'index.ts'] },
67
+ menubar: { files: ['menubar.component.ts', 'menubar-checkbox-item.component.ts', 'menubar-content.component.ts', 'menubar-context.ts', 'menubar-item.component.ts', 'menubar-label.component.ts', 'menubar-menu.component.ts', 'menubar-radio-group.component.ts', 'menubar-radio-item.component.ts', 'menubar-separator.component.ts', 'menubar-shortcut.component.ts', 'menubar-sub.component.ts', 'menubar-sub-content.component.ts', 'menubar-sub-trigger.component.ts', 'menubar-trigger.component.ts', 'index.ts'] },
68
+ 'native-select': { files: ['native-select.component.ts', 'native-select-variants.ts', 'index.ts'] },
69
+ 'navigation-menu': { files: ['navigation-menu.component.ts', 'navigation-menu-content.component.ts', 'navigation-menu-context.ts', 'navigation-menu-indicator.component.ts', 'navigation-menu-item.component.ts', 'navigation-menu-link.component.ts', 'navigation-menu-list.component.ts', 'navigation-menu-trigger.component.ts', 'navigation-menu-trigger-style.ts', 'navigation-menu-viewport.component.ts', 'index.ts'] },
70
+ pagination: { files: ['pagination.component.ts', 'pagination-content.component.ts', 'pagination-ellipsis.component.ts', 'pagination-item.component.ts', 'pagination-link.component.ts', 'pagination-next.component.ts', 'pagination-previous.component.ts', 'index.ts'] },
71
+ popover: { files: ['popover.component.ts', 'popover-anchor.component.ts', 'popover-content.component.ts', 'popover-context.ts', 'popover-trigger.component.ts', 'index.ts'] },
72
+ progress: { files: ['progress.component.ts', 'index.ts'] },
73
+ 'radio-group': { files: ['radio-group.component.ts', 'radio-group-context.ts', 'radio-group-item.component.ts', 'index.ts'] },
74
+ resizable: { files: ['resizable-panel-group.component.ts', 'resizable-context.ts', 'resizable-handle.component.ts', 'resizable-panel.component.ts', 'index.ts'] },
75
+ 'scroll-area': { files: ['scroll-area.component.ts', 'scroll-bar.component.ts', 'index.ts'] },
76
+ segmented: { files: ['segmented.component.ts', 'segmented-context.ts', 'segmented-item.component.ts', 'segmented-variants.ts', 'index.ts'] },
77
+ select: { files: ['select.component.ts', 'select-content.component.ts', 'select-context.ts', 'select-group.component.ts', 'select-item.component.ts', 'select-label.component.ts', 'select-separator.component.ts', 'select-trigger.component.ts', 'select-value.component.ts', 'index.ts'] },
78
+ separator: { files: ['separator.component.ts', 'index.ts'] },
79
+ sheet: { files: ['sheet.component.ts', 'sheet-close.component.ts', 'sheet-content.component.ts', 'sheet-context.ts', 'sheet-description.component.ts', 'sheet-footer.component.ts', 'sheet-header.component.ts', 'sheet-title.component.ts', 'sheet-trigger.component.ts', 'sheet-variants.ts', 'index.ts'] },
80
+ sidebar: { files: ['sidebar.component.ts', 'sidebar-content.component.ts', 'sidebar-context.ts', 'sidebar-footer.component.ts', 'sidebar-group.component.ts', 'sidebar-group-action.component.ts', 'sidebar-group-content.component.ts', 'sidebar-group-label.component.ts', 'sidebar-header.component.ts', 'sidebar-input.component.ts', 'sidebar-inset.component.ts', 'sidebar-menu.component.ts', 'sidebar-menu-action.component.ts', 'sidebar-menu-badge.component.ts', 'sidebar-menu-button.component.ts', 'sidebar-menu-item.component.ts', 'sidebar-menu-skeleton.component.ts', 'sidebar-menu-sub.component.ts', 'sidebar-menu-sub-button.component.ts', 'sidebar-menu-sub-item.component.ts', 'sidebar-provider.component.ts', 'sidebar-rail.component.ts', 'sidebar-route-active.service.ts', 'sidebar-separator.component.ts', 'sidebar-trigger.component.ts', 'index.ts'] },
81
+ skeleton: { files: ['skeleton.component.ts', 'index.ts'] },
82
+ slider: { files: ['slider.component.ts', 'index.ts'] },
83
+ spinner: { files: ['spinner.component.ts', 'spinner-variants.ts', 'index.ts'] },
84
+ switch: { files: ['switch.component.ts', 'index.ts'] },
85
+ table: { files: ['table.component.ts', 'table-body.component.ts', 'table-caption.component.ts', 'table-cell.component.ts', 'table-footer.component.ts', 'table-head.component.ts', 'table-header.component.ts', 'table-row.component.ts', 'index.ts'] },
86
+ tabs: { files: ['tabs.component.ts', 'tabs-content.component.ts', 'tabs-context.ts', 'tabs-list.component.ts', 'tabs-trigger.component.ts', 'index.ts'] },
87
+ textarea: { files: ['textarea.component.ts', 'index.ts'] },
88
+ toast: { files: ['toast.component.ts', 'toast-action.component.ts', 'toast-description.component.ts', 'toast-title.component.ts', 'toast-variants.ts', 'toast.service.ts', 'toaster.component.ts', 'index.ts'] },
89
+ toggle: { files: ['toggle.component.ts', 'toggle-variants.ts', 'index.ts'] },
90
+ 'toggle-group': { files: ['toggle-group.component.ts', 'toggle-group-context.ts', 'toggle-group-item.component.ts', 'index.ts'] },
91
+ tooltip: { files: ['tooltip.component.ts', 'tooltip-content.component.ts', 'tooltip-context.ts', 'tooltip-provider.component.ts', 'tooltip-trigger.component.ts', 'index.ts'] },
92
+ typography: { files: ['typography-blockquote.component.ts', 'typography-h1.component.ts', 'typography-h2.component.ts', 'typography-h3.component.ts', 'typography-h4.component.ts', 'typography-inline-code.component.ts', 'typography-large.component.ts', 'typography-lead.component.ts', 'typography-list.component.ts', 'typography-muted.component.ts', 'typography-p.component.ts', 'typography-small.component.ts', 'index.ts'] },
93
+ };
94
+
30
95
  // Registry of all utility files that need to be copied
31
96
  const UTILS_FILES_REGISTRY = [
32
97
  // Core utils
@@ -786,16 +851,59 @@ export function ngAdd(options: NgAddOptions): Rule {
786
851
  context.logger.info('📦 Selected Components');
787
852
  }
788
853
 
789
- // For now, just log the components that would be copied
790
- // Components will be available via 'ng g @ng-cn/core:component <name>'
791
- for (const component of componentsToInstall) {
792
- context.logger.info(` + ${component} (available via: ng g @ng-cn/core:component ${component})`);
854
+ // Actually copy the component files
855
+ const componentBasePath = 'node_modules/@ng-cn/core/src/app/lib/components/ui';
856
+ const fallbackBasePath = 'src/app/lib/components/ui';
857
+ let totalFilesCopied = 0;
858
+
859
+ for (const componentName of componentsToInstall) {
860
+ const componentInfo = COMPONENT_REGISTRY[componentName];
861
+ if (!componentInfo) {
862
+ context.logger.warn(` ⚠ Unknown component: ${componentName}`);
863
+ continue;
864
+ }
865
+
866
+ const targetComponentPath = `/src/app/lib/components/ui/${componentName}`;
867
+ let filesCopied = 0;
868
+
869
+ for (const file of componentInfo.files) {
870
+ // Try package path first, then fallback to local dev path
871
+ let sourcePath = `${componentBasePath}/${componentName}/${file}`;
872
+ const targetPath = `${targetComponentPath}/${file}`;
873
+
874
+ let content = tree.read(sourcePath);
875
+ if (!content) {
876
+ // Fallback to local development path
877
+ sourcePath = `${fallbackBasePath}/${componentName}/${file}`;
878
+ content = tree.read(sourcePath);
879
+ }
880
+
881
+ if (content) {
882
+ if (tree.exists(targetPath)) {
883
+ tree.overwrite(targetPath, content);
884
+ } else {
885
+ tree.create(targetPath, content);
886
+ }
887
+ filesCopied++;
888
+ }
889
+ }
890
+
891
+ if (filesCopied > 0) {
892
+ context.logger.info(` ✓ ${componentName} (${filesCopied} files)`);
893
+ totalFilesCopied += filesCopied;
894
+ } else {
895
+ context.logger.warn(` ⚠ ${componentName} - no files found (use: ng g @ng-cn/core:c ${componentName})`);
896
+ }
793
897
  }
794
898
 
795
899
  context.logger.info('');
796
- context.logger.info('💡 To copy individual components, use:');
797
- context.logger.info(` ng g @ng-cn/core:component button`);
798
- context.logger.info(` ng g @ng-cn/core:component card`);
900
+ if (totalFilesCopied > 0) {
901
+ context.logger.info(` 📦 ${totalFilesCopied} total files copied`);
902
+ } else {
903
+ context.logger.info('💡 To copy components after install, use:');
904
+ context.logger.info(` ng g @ng-cn/core:component button`);
905
+ context.logger.info(` ng g @ng-cn/core:component card`);
906
+ }
799
907
  }
800
908
 
801
909
  // Success message with ASCII art banner