@ng-cn/core 1.0.12 → 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.12",
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
@@ -545,12 +604,17 @@ function ngAdd(options) {
545
604
  const packageJsonPath = '/package.json';
546
605
  if (tree.exists(packageJsonPath)) {
547
606
  const packageJson = JSON.parse(tree.read(packageJsonPath).toString('utf-8'));
607
+ // Detect Angular version to determine CDK version
608
+ const angularCoreVersion = packageJson.dependencies?.['@angular/core'] || packageJson.devDependencies?.['@angular/core'] || '';
609
+ const angularMajorVersion = parseInt(angularCoreVersion.replace(/[\^~]/, '').split('.')[0], 10) || 21;
610
+ // Use compatible CDK version based on Angular version
611
+ const cdkVersion = angularMajorVersion >= 21 ? '^21.0.5' : angularMajorVersion >= 20 ? '^20.0.0' : '^19.0.0';
548
612
  const requiredDependencies = {
549
613
  'lucide-angular': '^0.562.0',
550
614
  'class-variance-authority': '^0.7.1',
551
615
  'clsx': '^2.1.1',
552
616
  'tailwind-merge': '^3.4.0',
553
- '@angular/cdk': '^21.0.5',
617
+ '@angular/cdk': cdkVersion,
554
618
  'tailwindcss': '^4.1.18',
555
619
  '@tailwindcss/postcss': '^4.1.18'
556
620
  };
@@ -712,15 +776,55 @@ function ngAdd(options) {
712
776
  else {
713
777
  context.logger.info('📦 Selected Components');
714
778
  }
715
- // For now, just log the components that would be copied
716
- // Components will be available via 'ng g @ng-cn/core:component <name>'
717
- for (const component of componentsToInstall) {
718
- 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
+ }
719
818
  }
720
819
  context.logger.info('');
721
- context.logger.info('💡 To copy individual components, use:');
722
- context.logger.info(` ng g @ng-cn/core:component button`);
723
- 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
+ }
724
828
  }
725
829
  // Success message with ASCII art banner
726
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
@@ -594,12 +659,19 @@ export function ngAdd(options: NgAddOptions): Rule {
594
659
  if (tree.exists(packageJsonPath)) {
595
660
  const packageJson = JSON.parse(tree.read(packageJsonPath)!.toString('utf-8'));
596
661
 
597
- const requiredDependencies = {
662
+ // Detect Angular version to determine CDK version
663
+ const angularCoreVersion = packageJson.dependencies?.['@angular/core'] || packageJson.devDependencies?.['@angular/core'] || '';
664
+ const angularMajorVersion = parseInt(angularCoreVersion.replace(/[\^~]/, '').split('.')[0], 10) || 21;
665
+
666
+ // Use compatible CDK version based on Angular version
667
+ const cdkVersion = angularMajorVersion >= 21 ? '^21.0.5' : angularMajorVersion >= 20 ? '^20.0.0' : '^19.0.0';
668
+
669
+ const requiredDependencies: Record<string, string> = {
598
670
  'lucide-angular': '^0.562.0',
599
671
  'class-variance-authority': '^0.7.1',
600
672
  'clsx': '^2.1.1',
601
673
  'tailwind-merge': '^3.4.0',
602
- '@angular/cdk': '^21.0.5',
674
+ '@angular/cdk': cdkVersion,
603
675
  'tailwindcss': '^4.1.18',
604
676
  '@tailwindcss/postcss': '^4.1.18'
605
677
  };
@@ -779,16 +851,59 @@ export function ngAdd(options: NgAddOptions): Rule {
779
851
  context.logger.info('📦 Selected Components');
780
852
  }
781
853
 
782
- // For now, just log the components that would be copied
783
- // Components will be available via 'ng g @ng-cn/core:component <name>'
784
- for (const component of componentsToInstall) {
785
- 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
+ }
786
897
  }
787
898
 
788
899
  context.logger.info('');
789
- context.logger.info('💡 To copy individual components, use:');
790
- context.logger.info(` ng g @ng-cn/core:component button`);
791
- 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
+ }
792
907
  }
793
908
 
794
909
  // Success message with ASCII art banner