@electroplix/components 0.4.1 → 0.5.0-alpha.11

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.
Files changed (185) hide show
  1. package/CHANGELOG.md +154 -0
  2. package/LICENSE +21 -0
  3. package/README.md +88 -474
  4. package/SECURITY.md +19 -0
  5. package/cli.cjs +544 -485
  6. package/dist/config.d.ts +73 -0
  7. package/dist/config.esm.js +229 -0
  8. package/dist/index.esm.js +10195 -8528
  9. package/dist/package.json +100 -0
  10. package/dist/src/components/blog/index.d.ts +21 -21
  11. package/dist/src/components/blog/index.d.ts.map +1 -1
  12. package/dist/src/components/buttons/index.d.ts +41 -16
  13. package/dist/src/components/buttons/index.d.ts.map +1 -1
  14. package/dist/src/components/content/BlockquoteTestimonial.d.ts +3 -5
  15. package/dist/src/components/content/BlockquoteTestimonial.d.ts.map +1 -1
  16. package/dist/src/components/content/CalloutBox.d.ts +4 -6
  17. package/dist/src/components/content/CalloutBox.d.ts.map +1 -1
  18. package/dist/src/components/content/HeadingSection.d.ts +4 -6
  19. package/dist/src/components/content/HeadingSection.d.ts.map +1 -1
  20. package/dist/src/components/content/InlineCodeText.d.ts +5 -6
  21. package/dist/src/components/content/InlineCodeText.d.ts.map +1 -1
  22. package/dist/src/components/content/ParagraphBlock.d.ts +5 -7
  23. package/dist/src/components/content/ParagraphBlock.d.ts.map +1 -1
  24. package/dist/src/components/content/RichMarkdown.d.ts +5 -6
  25. package/dist/src/components/content/RichMarkdown.d.ts.map +1 -1
  26. package/dist/src/components/content/TeamGrid.d.ts +3 -2
  27. package/dist/src/components/content/TeamGrid.d.ts.map +1 -1
  28. package/dist/src/components/content/index.d.ts +7 -7
  29. package/dist/src/components/content/index.d.ts.map +1 -1
  30. package/dist/src/components/data-display/Badge.d.ts +6 -5
  31. package/dist/src/components/data-display/Badge.d.ts.map +1 -1
  32. package/dist/src/components/data-display/BadgeGroup.d.ts +6 -4
  33. package/dist/src/components/data-display/BadgeGroup.d.ts.map +1 -1
  34. package/dist/src/components/data-display/BarChart.d.ts +3 -2
  35. package/dist/src/components/data-display/BarChart.d.ts.map +1 -1
  36. package/dist/src/components/data-display/CalendarGrid.d.ts +4 -3
  37. package/dist/src/components/data-display/CalendarGrid.d.ts.map +1 -1
  38. package/dist/src/components/data-display/DataTable.d.ts +5 -5
  39. package/dist/src/components/data-display/DataTable.d.ts.map +1 -1
  40. package/dist/src/components/data-display/LineChart.d.ts +3 -2
  41. package/dist/src/components/data-display/LineChart.d.ts.map +1 -1
  42. package/dist/src/components/data-display/PieChart.d.ts +3 -2
  43. package/dist/src/components/data-display/PieChart.d.ts.map +1 -1
  44. package/dist/src/components/data-display/ProgressBar.d.ts +3 -2
  45. package/dist/src/components/data-display/ProgressBar.d.ts.map +1 -1
  46. package/dist/src/components/data-display/RatingStars.d.ts +3 -2
  47. package/dist/src/components/data-display/RatingStars.d.ts.map +1 -1
  48. package/dist/src/components/data-display/Sparkline.d.ts +3 -2
  49. package/dist/src/components/data-display/Sparkline.d.ts.map +1 -1
  50. package/dist/src/components/data-display/Timeline.d.ts +4 -3
  51. package/dist/src/components/data-display/Timeline.d.ts.map +1 -1
  52. package/dist/src/components/data-display/index.d.ts +11 -11
  53. package/dist/src/components/data-display/index.d.ts.map +1 -1
  54. package/dist/src/components/ecommerce/index.d.ts +24 -21
  55. package/dist/src/components/ecommerce/index.d.ts.map +1 -1
  56. package/dist/src/components/forms/AddressAutocomplete.d.ts +2 -4
  57. package/dist/src/components/forms/AddressAutocomplete.d.ts.map +1 -1
  58. package/dist/src/components/forms/Captcha.d.ts +3 -5
  59. package/dist/src/components/forms/Captcha.d.ts.map +1 -1
  60. package/dist/src/components/forms/ContactForm.d.ts +2 -4
  61. package/dist/src/components/forms/ContactForm.d.ts.map +1 -1
  62. package/dist/src/components/forms/DateTimePicker.d.ts +3 -5
  63. package/dist/src/components/forms/DateTimePicker.d.ts.map +1 -1
  64. package/dist/src/components/forms/FileUploader.d.ts +2 -4
  65. package/dist/src/components/forms/FileUploader.d.ts.map +1 -1
  66. package/dist/src/components/forms/FormShell.d.ts +2 -5
  67. package/dist/src/components/forms/FormShell.d.ts.map +1 -1
  68. package/dist/src/components/forms/InputField.d.ts +2 -4
  69. package/dist/src/components/forms/InputField.d.ts.map +1 -1
  70. package/dist/src/components/forms/MultiStepWizard.d.ts +2 -4
  71. package/dist/src/components/forms/MultiStepWizard.d.ts.map +1 -1
  72. package/dist/src/components/forms/NewsletterSignup.d.ts +2 -4
  73. package/dist/src/components/forms/NewsletterSignup.d.ts.map +1 -1
  74. package/dist/src/components/forms/RadioGroup.d.ts +2 -4
  75. package/dist/src/components/forms/RadioGroup.d.ts.map +1 -1
  76. package/dist/src/components/forms/SelectDropdown.d.ts +2 -4
  77. package/dist/src/components/forms/SelectDropdown.d.ts.map +1 -1
  78. package/dist/src/components/forms/TextAreaField.d.ts +2 -4
  79. package/dist/src/components/forms/TextAreaField.d.ts.map +1 -1
  80. package/dist/src/components/forms/ToggleSwitch.d.ts +2 -4
  81. package/dist/src/components/forms/ToggleSwitch.d.ts.map +1 -1
  82. package/dist/src/components/forms/ValidationWrapper.d.ts +2 -4
  83. package/dist/src/components/forms/ValidationWrapper.d.ts.map +1 -1
  84. package/dist/src/components/forms/index.d.ts +28 -28
  85. package/dist/src/components/forms/index.d.ts.map +1 -1
  86. package/dist/src/components/hero/CTAOverlayHero.d.ts +2 -4
  87. package/dist/src/components/hero/CTAOverlayHero.d.ts.map +1 -1
  88. package/dist/src/components/hero/CarouselHero.d.ts +2 -4
  89. package/dist/src/components/hero/CarouselHero.d.ts.map +1 -1
  90. package/dist/src/components/hero/HeroShell.d.ts +2 -5
  91. package/dist/src/components/hero/HeroShell.d.ts.map +1 -1
  92. package/dist/src/components/hero/ImageHero.d.ts +35 -0
  93. package/dist/src/components/hero/ImageHero.d.ts.map +1 -0
  94. package/dist/src/components/hero/PatternedHero.d.ts +3 -5
  95. package/dist/src/components/hero/PatternedHero.d.ts.map +1 -1
  96. package/dist/src/components/hero/SplitHero.d.ts +2 -4
  97. package/dist/src/components/hero/SplitHero.d.ts.map +1 -1
  98. package/dist/src/components/hero/StaticHero.d.ts +3 -5
  99. package/dist/src/components/hero/StaticHero.d.ts.map +1 -1
  100. package/dist/src/components/hero/VideoHeaderHero.d.ts +2 -4
  101. package/dist/src/components/hero/VideoHeaderHero.d.ts.map +1 -1
  102. package/dist/src/components/hero/index.d.ts +16 -14
  103. package/dist/src/components/hero/index.d.ts.map +1 -1
  104. package/dist/src/components/lists-cards/index.d.ts +17 -20
  105. package/dist/src/components/lists-cards/index.d.ts.map +1 -1
  106. package/dist/src/components/marketing/index.d.ts +22 -22
  107. package/dist/src/components/marketing/index.d.ts.map +1 -1
  108. package/dist/src/components/media/index.d.ts +27 -29
  109. package/dist/src/components/media/index.d.ts.map +1 -1
  110. package/dist/src/components/miscellaneous/index.d.ts +18 -17
  111. package/dist/src/components/miscellaneous/index.d.ts.map +1 -1
  112. package/dist/src/components/modals/index.d.ts +23 -34
  113. package/dist/src/components/modals/index.d.ts.map +1 -1
  114. package/dist/src/components/navigation/AnchorLinks.d.ts +4 -6
  115. package/dist/src/components/navigation/AnchorLinks.d.ts.map +1 -1
  116. package/dist/src/components/navigation/Breadcrumbs.d.ts +3 -5
  117. package/dist/src/components/navigation/Breadcrumbs.d.ts.map +1 -1
  118. package/dist/src/components/navigation/Footer.d.ts +3 -4
  119. package/dist/src/components/navigation/Footer.d.ts.map +1 -1
  120. package/dist/src/components/navigation/LanguageSelector.d.ts +3 -5
  121. package/dist/src/components/navigation/LanguageSelector.d.ts.map +1 -1
  122. package/dist/src/components/navigation/MegaMenu.d.ts +3 -5
  123. package/dist/src/components/navigation/MegaMenu.d.ts.map +1 -1
  124. package/dist/src/components/navigation/Pagination.d.ts +3 -2
  125. package/dist/src/components/navigation/Pagination.d.ts.map +1 -1
  126. package/dist/src/components/navigation/PrimaryNav.d.ts +3 -2
  127. package/dist/src/components/navigation/PrimaryNav.d.ts.map +1 -1
  128. package/dist/src/components/navigation/SideDrawerNav.d.ts +5 -4
  129. package/dist/src/components/navigation/SideDrawerNav.d.ts.map +1 -1
  130. package/dist/src/components/navigation/SidebarMenu.d.ts +4 -3
  131. package/dist/src/components/navigation/SidebarMenu.d.ts.map +1 -1
  132. package/dist/src/components/navigation/Stepper.d.ts +4 -3
  133. package/dist/src/components/navigation/Stepper.d.ts.map +1 -1
  134. package/dist/src/components/navigation/Tabs.d.ts +5 -4
  135. package/dist/src/components/navigation/Tabs.d.ts.map +1 -1
  136. package/dist/src/components/navigation/index.d.ts +22 -22
  137. package/dist/src/components/navigation/index.d.ts.map +1 -1
  138. package/dist/src/components/onboarding/index.d.ts +17 -17
  139. package/dist/src/components/onboarding/index.d.ts.map +1 -1
  140. package/dist/src/components/search/index.d.ts +13 -12
  141. package/dist/src/components/search/index.d.ts.map +1 -1
  142. package/dist/src/components/site-identity/index.d.ts +13 -14
  143. package/dist/src/components/site-identity/index.d.ts.map +1 -1
  144. package/dist/src/components/social/index.d.ts +19 -18
  145. package/dist/src/components/social/index.d.ts.map +1 -1
  146. package/dist/src/components/user-accounts/index.d.ts +17 -17
  147. package/dist/src/components/user-accounts/index.d.ts.map +1 -1
  148. package/dist/src/core/config.d.ts +1 -1
  149. package/dist/src/core/config.d.ts.map +1 -1
  150. package/dist/src/core/icons.d.ts +1 -1
  151. package/dist/src/core/icons.d.ts.map +1 -1
  152. package/dist/src/core/index.d.ts +7 -7
  153. package/dist/src/core/index.d.ts.map +1 -1
  154. package/dist/src/core/provider.d.ts +20 -20
  155. package/dist/src/core/provider.d.ts.map +1 -1
  156. package/dist/src/core/styles.d.ts +2 -0
  157. package/dist/src/core/styles.d.ts.map +1 -0
  158. package/dist/src/core/types.d.ts +4 -4
  159. package/dist/src/core/types.d.ts.map +1 -1
  160. package/dist/src/core/utils.d.ts +1 -1
  161. package/dist/src/core/utils.d.ts.map +1 -1
  162. package/dist/src/index.d.ts +19 -19
  163. package/dist/src/index.d.ts.map +1 -1
  164. package/metadata/blog.json +807 -0
  165. package/metadata/buttons.json +2186 -0
  166. package/metadata/content.json +1152 -0
  167. package/metadata/data-display.json +822 -0
  168. package/metadata/ecommerce.json +1059 -0
  169. package/metadata/forms.json +2637 -0
  170. package/metadata/hero.json +1401 -0
  171. package/metadata/lists-cards.json +848 -0
  172. package/metadata/marketing.json +1235 -0
  173. package/metadata/media.json +800 -0
  174. package/metadata/miscellaneous.json +778 -0
  175. package/metadata/modals.json +954 -0
  176. package/metadata/navigation.json +1348 -0
  177. package/metadata/onboarding.json +615 -0
  178. package/metadata/search.json +559 -0
  179. package/metadata/site-identity.json +555 -0
  180. package/metadata/social.json +654 -0
  181. package/metadata/user-accounts.json +727 -0
  182. package/package.json +22 -7
  183. package/dist/README.md +0 -35
  184. package/dist/src/__tests__/test-utils.d.ts +0 -8
  185. package/dist/src/__tests__/test-utils.d.ts.map +0 -1
package/cli.cjs CHANGED
@@ -1,485 +1,544 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * @electroplix/components CLI
5
- *
6
- * Usage:
7
- * npx @electroplix/components init – scaffold config + providers
8
- * npx @electroplix/components add <name|category> – show install/import instructions
9
- * npx @electroplix/components list – list all available components
10
- */
11
-
12
- const fs = require("fs");
13
- const path = require("path");
14
-
15
- /* ══════════════════════════════════════════════════════════════════ */
16
- /* ANSI Styling */
17
- /* ══════════════════════════════════════════════════════════════════ */
18
-
19
- const ESC = "\x1b[";
20
- const R = `${ESC}0m`;
21
-
22
- const s = {
23
- bold: (t) => `${ESC}1m${t}${R}`,
24
- dim: (t) => `${ESC}2m${t}${R}`,
25
- italic: (t) => `${ESC}3m${t}${R}`,
26
- purple: (t) => `${ESC}38;2;139;92;246m${t}${R}`,
27
- violet: (t) => `${ESC}38;2;167;139;250m${t}${R}`,
28
- cyan: (t) => `${ESC}38;2;34;211;238m${t}${R}`,
29
- green: (t) => `${ESC}38;2;52;211;153m${t}${R}`,
30
- yellow: (t) => `${ESC}38;2;251;191;36m${t}${R}`,
31
- red: (t) => `${ESC}38;2;251;113;133m${t}${R}`,
32
- white: (t) => `${ESC}38;2;243;244;246m${t}${R}`,
33
- gray: (t) => `${ESC}38;2;107;114;128m${t}${R}`,
34
- };
35
-
36
- /* ══════════════════════════════════════════════════════════════════ */
37
- /* Box-drawing helpers */
38
- /* ══════════════════════════════════════════════════════════════════ */
39
-
40
- const B = {
41
- tl: "\u256D", tr: "\u256E", bl: "\u2570", br: "\u256F",
42
- h: "\u2500", v: "\u2502",
43
- tee: "\u251C", end: "\u2570",
44
- branch: "\u251C\u2500\u2500", last: "\u2570\u2500\u2500",
45
- pipe: "\u2502 ",
46
- dot: "\u25CF", arrow: "\u2192", check: "\u2713", star: "\u2605",
47
- diamond: "\u25C6", spark: "\u26A1",
48
- };
49
-
50
- function hr(w) { return B.h.repeat(w); }
51
-
52
- function stripAnsi(t) { return t.replace(/\x1b\[[0-9;]*m/g, ""); }
53
-
54
- function boxLine(l, content, r, w) {
55
- const vis = stripAnsi(content);
56
- const pad = Math.max(0, w - vis.length - 2);
57
- return ` ${l}${content}${" ".repeat(pad)}${r}`;
58
- }
59
-
60
- /* ══════════════════════════════════════════════════════════════════ */
61
- /* Brand banner */
62
- /* ══════════════════════════════════════════════════════════════════ */
63
-
64
- function printBanner() {
65
- const W = 58;
66
- const lines = [
67
- "",
68
- ` ${B.tl}${hr(W)}${B.tr}`,
69
- boxLine(B.v, ` ${s.bold(s.purple(`${B.spark} ELECTROPLIX`))} ${s.dim(s.violet("COMPONENTS"))}`, B.v, W),
70
- boxLine(B.v, ` ${s.gray("Parametric \u2022 Config-Driven \u2022 Zero-Dependency")}`, B.v, W),
71
- ` ${B.bl}${hr(W)}${B.br}`,
72
- "",
73
- ];
74
- console.log(lines.join("\n"));
75
- }
76
-
77
- /* ══════════════════════════════════════════════════════════════════ */
78
- /* Component registry */
79
- /* ══════════════════════════════════════════════════════════════════ */
80
-
81
- const CATEGORIES = {
82
- navigation: [
83
- "AnchorLinks", "Breadcrumbs", "LanguageSelector", "MegaMenu",
84
- "Pagination", "PrimaryNav", "SidebarMenu", "SideDrawerNav", "Stepper", "Tabs",
85
- ],
86
- hero: [
87
- "HeroShell", "StaticHero", "CarouselHero", "CTAOverlayHero",
88
- "PatternedHero", "SplitHero", "VideoHeaderHero",
89
- ],
90
- buttons: [
91
- "PrimaryButton", "SecondaryButton", "TertiaryButton", "IconButton",
92
- "FloatingActionButton", "ButtonGroup", "LoadingButton", "ShareButton",
93
- "DownloadButton", "PrintButton",
94
- ],
95
- forms: [
96
- "FormShell", "InputField", "TextAreaField", "SelectDropdown", "RadioGroup",
97
- "ToggleSwitch", "DateTimePicker", "FileUploader", "ContactForm",
98
- "NewsletterSignup", "MultiStepWizard", "Captcha", "AddressAutocomplete",
99
- "ValidationWrapper",
100
- ],
101
- content: [
102
- "BlockquoteTestimonial", "CalloutBox", "HeadingSection",
103
- "InlineCodeText", "ParagraphBlock", "RichMarkdown",
104
- ],
105
- "data-display": [
106
- "Badge", "BadgeGroup", "BarChart", "LineChart", "PieChart", "Sparkline",
107
- "ProgressBar", "RatingStars", "CalendarGrid", "DataTable", "Timeline",
108
- ],
109
- ecommerce: [
110
- "CartDrawer", "MiniCartPanel", "OrderSummary", "ProductCard",
111
- "ProductGrid", "ProductDetail", "VariantSelector", "QuickAddButton",
112
- "WishlistButton", "PaymentButtons",
113
- ],
114
- "lists-cards": [
115
- "BlockShell", "Accordion", "GenericList", "FeatureGrid",
116
- "ItemCardGrid", "PricingTable", "SortableTable", "LCTimeline",
117
- ],
118
- marketing: [
119
- "ComparisonTable", "CountdownTimer", "FeatureHighlights", "LeadMagnetGate",
120
- "MarketingHeroBlock", "PromoPopup", "TestimonialsCarousel", "TrustBadges",
121
- ],
122
- media: [
123
- "MediaShell", "ResponsiveVideo", "AudioEmbed", "AvatarProfile", "IconGrid",
124
- "ImageGallery", "LightboxGallery", "MasonryGrid", "PolaroidImage",
125
- "LottieOrSVG", "ImageCropperUploader",
126
- ],
127
- miscellaneous: [
128
- "CookieConsent", "ScrollProgressBar", "ThemeToggle", "EmptyState",
129
- "AppInstallBanner", "DownloadBlock", "InlineCode", "RSSFeed",
130
- ],
131
- modals: [
132
- "OverlayBase", "GenericModal", "ConfirmDialog", "FormDialog",
133
- "LoadingOverlay", "Tooltip", "ToastBanners", "CookieNotice", "WelcomePopup",
134
- ],
135
- onboarding: [
136
- "FAQAccordion", "OnboardingWizard", "ProductTour", "TooltipHelp",
137
- "SupportChat", "ContactSupportBlock",
138
- ],
139
- search: [
140
- "SiteSearchBar", "AutoSuggest", "FacetFilters", "SearchResultCard",
141
- "SearchResults", "SearchEmptyState",
142
- ],
143
- "site-identity": [
144
- "LogoDisplay", "AnimatedBrandMark", "Taglines", "BrandingShell",
145
- "BrandIconGrid", "FaviconUploader",
146
- ],
147
- social: [
148
- "SocialShareBar", "SocialLoginButtons", "SocialEmbed", "FollowLike",
149
- "ReactionsBar", "CommentsBox", "ReviewsForm",
150
- ],
151
- "user-accounts": [
152
- "AuthForm", "PasswordReset", "MultiFactorAuthInput", "ProfileOverview",
153
- "ProfileSettings", "AccountSettings", "RoleBadge",
154
- ],
155
- blog: [
156
- "BlogCard", "AuthorByline", "TagList", "BlogBadge", "ReadingBar",
157
- "ArticleRenderer", "RelatedPosts", "ArchiveList", "CommentsSection",
158
- ],
159
- };
160
-
161
- /* build lookup maps */
162
- const ALL_COMPONENTS = new Map();
163
- for (const [cat, names] of Object.entries(CATEGORIES)) {
164
- for (const n of names) ALL_COMPONENTS.set(n.toLowerCase(), { name: n, category: cat });
165
- }
166
-
167
- const CATEGORY_ALIASES = new Map();
168
- for (const cat of Object.keys(CATEGORIES)) {
169
- CATEGORY_ALIASES.set(cat.toLowerCase(), cat);
170
- CATEGORY_ALIASES.set(cat.replace(/-/g, "").toLowerCase(), cat);
171
- CATEGORY_ALIASES.set(cat.replace(/-/g, " ").toLowerCase(), cat);
172
- }
173
-
174
- const totalCount = Object.values(CATEGORIES).reduce((sum, a) => sum + a.length, 0);
175
-
176
- /* ══════════════════════════════════════════════════════════════════ */
177
- /* Package manager detection */
178
- /* ══════════════════════════════════════════════════════════════════ */
179
-
180
- function detectPackageManager() {
181
- const cwd = process.cwd();
182
-
183
- // Check for lockfiles in order of preference
184
- if (fs.existsSync(path.join(cwd, "bun.lock"))) {
185
- return { manager: "bun", command: "bun install", displayName: "Bun" };
186
- }
187
- if (fs.existsSync(path.join(cwd, "pnpm-lock.yaml"))) {
188
- return { manager: "pnpm", command: "pnpm install", displayName: "pnpm" };
189
- }
190
- if (fs.existsSync(path.join(cwd, "yarn.lock"))) {
191
- return { manager: "yarn", command: "yarn install", displayName: "Yarn" };
192
- }
193
- if (fs.existsSync(path.join(cwd, "package-lock.json"))) {
194
- return { manager: "npm", command: "npm install", displayName: "npm" };
195
- }
196
-
197
- // Default to npm if no lockfile found
198
- return { manager: "npm", command: "npm install", displayName: "npm" };
199
- }
200
-
201
- /* ══════════════════════════════════════════════════════════════════ */
202
- /* INIT command */
203
- /* ══════════════════════════════════════════════════════════════════ */
204
-
205
- function init() {
206
- printBanner();
207
-
208
- const configName = "electroplix.config.ts";
209
- const providerRel = path.join("components", "providers.tsx");
210
- const configPath = path.join(process.cwd(), configName);
211
- const providerDir = path.join(process.cwd(), "components");
212
- const providerPath = path.join(providerDir, "providers.tsx");
213
-
214
- const created = [];
215
- const skipped = [];
216
-
217
- /* ── config file ─────────────────────────────────────── */
218
- if (fs.existsSync(configPath)) {
219
- skipped.push(configName);
220
- } else {
221
- const configTemplate = `import { defineConfig } from "@electroplix/components";
222
-
223
- const config = defineConfig({
224
- // \u2500\u2500 Global overrides (applied to ALL categories) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
225
- accentColor: "#7C3AED",
226
- textColor: "#E5E7EB",
227
- bgColor: "#0b0b0c",
228
- borderColor: "rgba(255,255,255,0.14)",
229
- radius: 14,
230
- fontFamily: "Inter, system-ui, sans-serif",
231
-
232
- // \u2500\u2500 Per-category overrides \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
233
- // navigation: { sticky: true, height: 64 },
234
- // hero: { minH: 600 },
235
- // buttons: { bgColor: "#e94560", radius: 12 },
236
- // forms: { inputBg: "rgba(255,255,255,0.05)" },
237
- // ecommerce: { accentColor: "#10B981" },
238
- });
239
-
240
- export default config;
241
- `;
242
- fs.writeFileSync(configPath, configTemplate, "utf-8");
243
- created.push(configName);
244
- }
245
-
246
- /* ── providers wrapper ───────────────────────────────── */
247
- if (fs.existsSync(providerPath)) {
248
- skipped.push(providerRel);
249
- } else {
250
- if (!fs.existsSync(providerDir)) {
251
- fs.mkdirSync(providerDir, { recursive: true });
252
- }
253
- const providerTemplate = `"use client";
254
-
255
- import { ElectroplixProvider } from "@electroplix/components";
256
- import config from "../electroplix.config";
257
-
258
- /**
259
- * Client-side providers wrapper.
260
- * Import this into your root layout (which stays as a Server Component).
261
- */
262
- export function Providers({ children }: { children: React.ReactNode }) {
263
- return (
264
- <ElectroplixProvider config={config}>
265
- {children}
266
- </ElectroplixProvider>
267
- );
268
- }
269
- `;
270
- fs.writeFileSync(providerPath, providerTemplate, "utf-8");
271
- created.push(providerRel);
272
- }
273
-
274
- /* ── output ──────────────────────────────────────────── */
275
- if (created.length) {
276
- console.log(` ${s.green(B.check)} ${s.bold("Created:")}`);
277
- for (const f of created) {
278
- console.log(` ${s.green(B.arrow)} ${s.cyan(f)}`);
279
- }
280
- console.log();
281
- }
282
- if (skipped.length) {
283
- console.log(` ${s.yellow("!")} ${s.bold("Already exists")} ${s.dim("(skipped):")}`);
284
- for (const f of skipped) {
285
- console.log(` ${s.gray(B.arrow)} ${s.gray(f)}`);
286
- }
287
- console.log();
288
- }
289
-
290
- console.log(` ${s.bold("Next steps:")}`);
291
- console.log();
292
- console.log(` ${s.white("1.")} Update your ${s.cyan("app/layout.tsx")}:`);
293
- console.log();
294
- console.log(` ${s.gray("import { Providers } from \"../components/providers\";")}`);
295
- console.log();
296
- console.log(` ${s.gray("export default function RootLayout({ children }) {")}`);
297
- console.log(` ${s.gray(" return (")}`);
298
- console.log(` ${s.gray(" <html><body>")}`);
299
- console.log(` ${s.gray(" ")}${s.cyan("<Providers>")}${s.gray("{children}")}${s.cyan("</Providers>")}`);
300
- console.log(` ${s.gray(" </body></html>")}`);
301
- console.log(` ${s.gray(" );")}`);
302
- console.log(` ${s.gray("}")}`);
303
- console.log();
304
- console.log(` ${s.white("2.")} Import components in ${s.cyan("\"use client\"")} pages:`);
305
- console.log();
306
- console.log(` ${s.gray("import { PrimaryNav, StaticHero } from \"@electroplix/components\";")}`);
307
- console.log();
308
- console.log();
309
-
310
- /* ── install package ── */
311
- const { execSync } = require("child_process");
312
- const pmInfo = detectPackageManager();
313
-
314
- console.log(` ${s.white("3.")} Installing ${s.cyan("@electroplix/components")} with ${s.cyan(pmInfo.displayName)}...`);
315
- console.log();
316
-
317
- try {
318
- execSync(`${pmInfo.command} @electroplix/components`, { stdio: "inherit" });
319
- console.log();
320
- console.log(` ${s.green(B.check)} ${s.bold("Setup complete!")} You're ready to use Electroplix components.`);
321
- console.log();
322
- } catch (err) {
323
- console.log();
324
- console.log(` ${s.yellow("!")} ${s.bold("Manual install required:")}`);
325
- console.log(` ${s.cyan(`${pmInfo.command} @electroplix/components`)}`);
326
- console.log();
327
- }
328
- }
329
-
330
- /* ══════════════════════════════════════════════════════════════════ */
331
- /* ADD command */
332
- /* ══════════════════════════════════════════════════════════════════ */
333
-
334
- function add(name) {
335
- printBanner();
336
-
337
- if (!name) {
338
- console.log(` ${s.red("\u2717")} ${s.bold("Missing argument")}\n`);
339
- console.log(` ${s.white("Usage:")}`);
340
- console.log(` ${s.cyan("npx @electroplix/components add")} ${s.violet("<ComponentName>")}`);
341
- console.log(` ${s.cyan("npx @electroplix/components add")} ${s.violet("<category>")}`);
342
- console.log();
343
- console.log(` ${s.dim("Examples:")}`);
344
- console.log(` ${s.gray("npx @electroplix/components add PrimaryNav")}`);
345
- console.log(` ${s.gray("npx @electroplix/components add navigation")}`);
346
- console.log();
347
- process.exit(1);
348
- }
349
-
350
- const key = name.toLowerCase().replace(/\s+/g, "-");
351
-
352
- /* ── try category match first ──────────────────────── */
353
- const catMatch = CATEGORY_ALIASES.get(key);
354
- if (catMatch) {
355
- const comps = CATEGORIES[catMatch];
356
- console.log(` ${s.purple(B.star)} ${s.bold(catMatch)} ${s.dim(`\u2014 ${comps.length} components`)}`);
357
- console.log();
358
- console.log(` ${s.white("Import all:")}`);
359
- console.log();
360
- console.log(` ${s.cyan("import {")}`);
361
- for (let i = 0; i < comps.length; i++) {
362
- const comma = i < comps.length - 1 ? "," : "";
363
- console.log(` ${s.cyan(" " + comps[i] + comma)}`);
364
- }
365
- console.log(` ${s.cyan("} from \"@electroplix/components\";")}`);
366
- console.log();
367
-
368
- /* tree display */
369
- console.log(` ${s.bold("Components:")}`);
370
- console.log();
371
- for (let i = 0; i < comps.length; i++) {
372
- const isLast = i === comps.length - 1;
373
- const prefix = isLast ? B.last : B.branch;
374
- console.log(` ${s.gray(prefix)} ${s.violet(comps[i])}`);
375
- }
376
- console.log();
377
- return;
378
- }
379
-
380
- /* ── try individual component match ────────────────── */
381
- const entry = ALL_COMPONENTS.get(key);
382
- if (!entry) {
383
- console.log(` ${s.red("\u2717")} Component or category ${s.bold("\"" + name + "\"")} not found.\n`);
384
- console.log(` ${s.dim("Run")} ${s.cyan("npx @electroplix/components list")} ${s.dim("to see all options.")}\n`);
385
- process.exit(1);
386
- }
387
-
388
- const W = 52;
389
- console.log(` ${B.tl}${hr(W)}${B.tr}`);
390
- console.log(boxLine(B.v, ` ${s.bold(s.purple(entry.name))} ${s.dim("from " + s.violet(entry.category))}`, B.v, W));
391
- console.log(` ${B.bl}${hr(W)}${B.br}`);
392
- console.log();
393
- console.log(` ${s.green("1.")} ${s.white("Install")} ${s.dim("(if not already):")}`);
394
- console.log(` ${s.cyan("npm install @electroplix/components")}`);
395
- console.log();
396
- console.log(` ${s.green("2.")} ${s.white("Import:")}`);
397
- console.log(` ${s.cyan("import { " + entry.name + " } from \"@electroplix/components\";")}`);
398
- console.log();
399
- console.log(` ${s.green("3.")} ${s.white("Use in JSX:")}`);
400
- console.log(` ${s.cyan("<" + entry.name + " />")}`);
401
- console.log();
402
- console.log(` ${s.dim("All components are config-driven. Wrap your app with")}`);
403
- console.log(` ${s.dim("<Providers> (from")} ${s.cyan("npx @electroplix/components init")}${s.dim(") to theme globally.")}`);
404
- console.log();
405
- }
406
-
407
- /* ══════════════════════════════════════════════════════════════════ */
408
- /* LIST command */
409
- /* ══════════════════════════════════════════════════════════════════ */
410
-
411
- function list() {
412
- printBanner();
413
-
414
- console.log(` ${s.bold(s.white(totalCount + " components"))} ${s.dim("across")} ${s.bold(s.white(Object.keys(CATEGORIES).length + " categories"))}`);
415
- console.log(` ${s.gray(hr(54))}`);
416
- console.log();
417
-
418
- const catEntries = Object.entries(CATEGORIES);
419
-
420
- for (let ci = 0; ci < catEntries.length; ci++) {
421
- const [cat, names] = catEntries[ci];
422
- const isLastCat = ci === catEntries.length - 1;
423
- const catPrefix = isLastCat ? B.end : B.tee;
424
- const childPipe = isLastCat ? " " : B.pipe;
425
-
426
- console.log(` ${s.gray(catPrefix)} ${s.bold(s.purple(cat))} ${s.dim("(" + names.length + ")")}`);
427
-
428
- for (let ni = 0; ni < names.length; ni++) {
429
- const isLastName = ni === names.length - 1;
430
- const namePrefix = isLastName ? B.last : B.branch;
431
- console.log(` ${s.gray(childPipe)} ${s.gray(namePrefix)} ${s.cyan(names[ni])}`);
432
- }
433
-
434
- if (!isLastCat) console.log(` ${s.gray(B.pipe)}`);
435
- }
436
-
437
- console.log();
438
- console.log(` ${s.dim("Add a component:")} ${s.cyan("npx @electroplix/components add <name>")}`);
439
- console.log(` ${s.dim("Add a category:")} ${s.cyan("npx @electroplix/components add <category>")}`);
440
- console.log(` ${s.dim("Scaffold project:")} ${s.cyan("npx @electroplix/components init")}`);
441
- console.log();
442
- }
443
-
444
- /* ══════════════════════════════════════════════════════════════════ */
445
- /* HELP / default */
446
- /* ══════════════════════════════════════════════════════════════════ */
447
-
448
- function help() {
449
- printBanner();
450
-
451
- console.log(` ${s.bold("Commands:")}`);
452
- console.log();
453
- console.log(` ${s.cyan("init")} ${s.white("Scaffold config + providers for your project")}`);
454
- console.log(` ${s.cyan("add")} ${s.violet("<name>")} ${s.white("Show import instructions for a component")}`);
455
- console.log(` ${s.cyan("add")} ${s.violet("<category>")} ${s.white("Show all components in a category")}`);
456
- console.log(` ${s.cyan("list")} ${s.white("Browse all")} ${s.bold(totalCount + "")} ${s.white("components")}`);
457
- console.log();
458
- console.log(` ${s.bold("Examples:")}`);
459
- console.log();
460
- console.log(` ${s.gray("$")} ${s.white("npx @electroplix/components init")}`);
461
- console.log(` ${s.gray("$")} ${s.white("npx @electroplix/components add PrimaryNav")}`);
462
- console.log(` ${s.gray("$")} ${s.white("npx @electroplix/components add navigation")}`);
463
- console.log(` ${s.gray("$")} ${s.white("npx @electroplix/components list")}`);
464
- console.log();
465
- }
466
-
467
- /* ══════════════════════════════════════════════════════════════════ */
468
- /* Main */
469
- /* ══════════════════════════════════════════════════════════════════ */
470
-
471
- const [, , cmd, ...args] = process.argv;
472
-
473
- switch (cmd) {
474
- case "init":
475
- init();
476
- break;
477
- case "add":
478
- add(args.join(" "));
479
- break;
480
- case "list":
481
- list();
482
- break;
483
- default:
484
- help();
485
- }
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * @electroplix/components CLI
5
+ *
6
+ * Usage:
7
+ * npx @electroplix/components init – scaffold config + providers
8
+ * npx @electroplix/components add <name|category> – show install/import instructions
9
+ * npx @electroplix/components list – list all available components
10
+ */
11
+
12
+ const fs = require('node:fs');
13
+ const path = require('node:path');
14
+
15
+ /* ══════════════════════════════════════════════════════════════════ */
16
+ /* ANSI Styling */
17
+ /* ══════════════════════════════════════════════════════════════════ */
18
+
19
+ const ESC = '\x1b[';
20
+ const R = `${ESC}0m`;
21
+
22
+ const s = {
23
+ bold: (t) => `${ESC}1m${t}${R}`,
24
+ dim: (t) => `${ESC}2m${t}${R}`,
25
+ italic: (t) => `${ESC}3m${t}${R}`,
26
+ purple: (t) => `${ESC}38;2;139;92;246m${t}${R}`,
27
+ violet: (t) => `${ESC}38;2;167;139;250m${t}${R}`,
28
+ cyan: (t) => `${ESC}38;2;34;211;238m${t}${R}`,
29
+ green: (t) => `${ESC}38;2;52;211;153m${t}${R}`,
30
+ yellow: (t) => `${ESC}38;2;251;191;36m${t}${R}`,
31
+ red: (t) => `${ESC}38;2;251;113;133m${t}${R}`,
32
+ white: (t) => `${ESC}38;2;243;244;246m${t}${R}`,
33
+ gray: (t) => `${ESC}38;2;107;114;128m${t}${R}`,
34
+ };
35
+
36
+ /* ══════════════════════════════════════════════════════════════════ */
37
+ /* Box-drawing helpers */
38
+ /* ══════════════════════════════════════════════════════════════════ */
39
+
40
+ const B = {
41
+ tl: '\u256D',
42
+ tr: '\u256E',
43
+ bl: '\u2570',
44
+ br: '\u256F',
45
+ h: '\u2500',
46
+ v: '\u2502',
47
+ tee: '\u251C',
48
+ end: '\u2570',
49
+ branch: '\u251C\u2500\u2500',
50
+ last: '\u2570\u2500\u2500',
51
+ pipe: '\u2502 ',
52
+ dot: '\u25CF',
53
+ arrow: '\u2192',
54
+ check: '\u2713',
55
+ star: '\u2605',
56
+ diamond: '\u25C6',
57
+ spark: '\u26A1',
58
+ };
59
+
60
+ function hr(w) {
61
+ return B.h.repeat(w);
62
+ }
63
+
64
+ function stripAnsi(t) {
65
+ return t.replace(/\x1b\[[0-9;]*m/g, '');
66
+ }
67
+
68
+ function boxLine(l, content, r, w) {
69
+ const vis = stripAnsi(content);
70
+ const pad = Math.max(0, w - vis.length - 2);
71
+ return ` ${l}${content}${' '.repeat(pad)}${r}`;
72
+ }
73
+
74
+ /* ══════════════════════════════════════════════════════════════════ */
75
+ /* Brand banner */
76
+ /* ══════════════════════════════════════════════════════════════════ */
77
+
78
+ function printBanner() {
79
+ const W = 58;
80
+ const lines = [
81
+ '',
82
+ ` ${B.tl}${hr(W)}${B.tr}`,
83
+ boxLine(
84
+ B.v,
85
+ ` ${s.bold(s.purple(`${B.spark} ELECTROPLIX`))} ${s.dim(s.violet('COMPONENTS'))}`,
86
+ B.v,
87
+ W,
88
+ ),
89
+ boxLine(B.v, ` ${s.gray('Parametric \u2022 Config-Driven \u2022 Zero-Dependency')}`, B.v, W),
90
+ ` ${B.bl}${hr(W)}${B.br}`,
91
+ '',
92
+ ];
93
+ for (const line of lines) {
94
+ console.log(line);
95
+ }
96
+ }
97
+
98
+ /* ══════════════════════════════════════════════════════════════════ */
99
+ /* Component registry */
100
+ /* ══════════════════════════════════════════════════════════════════ */
101
+
102
+ const CATEGORIES = {
103
+ navigation: [
104
+ 'AnchorLinks',
105
+ 'Breadcrumbs',
106
+ 'Footer',
107
+ 'LanguageSelector',
108
+ 'MegaMenu',
109
+ 'Pagination',
110
+ 'PrimaryNav',
111
+ 'SidebarMenu',
112
+ 'SideDrawerNav',
113
+ 'Stepper',
114
+ 'Tabs',
115
+ ],
116
+ hero: [
117
+ 'HeroShell',
118
+ 'StaticHero',
119
+ 'CarouselHero',
120
+ 'CTAOverlayHero',
121
+ 'PatternedHero',
122
+ 'SplitHero',
123
+ 'VideoHeaderHero',
124
+ ],
125
+ buttons: [
126
+ 'Button',
127
+ 'PrimaryButton',
128
+ 'SecondaryButton',
129
+ 'TertiaryButton',
130
+ 'IconButton',
131
+ 'FloatingActionButton',
132
+ 'ButtonGroup',
133
+ 'LoadingButton',
134
+ 'ShareButton',
135
+ 'DownloadButton',
136
+ 'PrintButton',
137
+ ],
138
+ forms: [
139
+ 'FormShell',
140
+ 'InputField',
141
+ 'TextAreaField',
142
+ 'SelectDropdown',
143
+ 'RadioGroup',
144
+ 'ToggleSwitch',
145
+ 'DateTimePicker',
146
+ 'FileUploader',
147
+ 'ContactForm',
148
+ 'NewsletterSignup',
149
+ 'MultiStepWizard',
150
+ 'Captcha',
151
+ 'AddressAutocomplete',
152
+ 'ValidationWrapper',
153
+ ],
154
+ content: [
155
+ 'BlockquoteTestimonial',
156
+ 'CalloutBox',
157
+ 'HeadingSection',
158
+ 'InlineCodeText',
159
+ 'ParagraphBlock',
160
+ 'RichMarkdown',
161
+ 'TeamGrid',
162
+ ],
163
+ 'data-display': [
164
+ 'Badge',
165
+ 'BadgeGroup',
166
+ 'BarChart',
167
+ 'LineChart',
168
+ 'PieChart',
169
+ 'Sparkline',
170
+ 'ProgressBar',
171
+ 'RatingStars',
172
+ 'CalendarGrid',
173
+ 'DataTable',
174
+ 'Timeline',
175
+ ],
176
+ ecommerce: [
177
+ 'CartDrawer',
178
+ 'MiniCartPanel',
179
+ 'OrderSummary',
180
+ 'ProductCard',
181
+ 'ProductGrid',
182
+ 'ProductDetail',
183
+ 'VariantSelector',
184
+ 'QuickAddButton',
185
+ 'WishlistButton',
186
+ 'PaymentButtons',
187
+ ],
188
+ 'lists-cards': [
189
+ 'BlockShell',
190
+ 'Accordion',
191
+ 'GenericList',
192
+ 'FeatureGrid',
193
+ 'ItemCardGrid',
194
+ 'PricingTable',
195
+ 'SortableTable',
196
+ 'LCTimeline',
197
+ ],
198
+ marketing: [
199
+ 'ComparisonTable',
200
+ 'CountdownTimer',
201
+ 'FeatureHighlights',
202
+ 'HowItWorks',
203
+ 'LeadMagnetGate',
204
+ 'MarketingHeroBlock',
205
+ 'PromoPopup',
206
+ 'StatsCounter',
207
+ 'TestimonialsCarousel',
208
+ 'TrustBadges',
209
+ ],
210
+ media: [
211
+ 'AudioEmbed',
212
+ 'AvatarProfile',
213
+ 'IconGrid',
214
+ 'ImageCropperUploader',
215
+ 'ImageGallery',
216
+ 'LightboxGallery',
217
+ 'LottieOrSVG',
218
+ 'MasonryGrid',
219
+ 'MapEmbed',
220
+ 'MediaShell',
221
+ 'PolaroidImage',
222
+ 'ResponsiveVideo',
223
+ ],
224
+ miscellaneous: [
225
+ 'CookieConsent',
226
+ 'ScrollProgressBar',
227
+ 'ThemeToggle',
228
+ 'EmptyState',
229
+ 'AppInstallBanner',
230
+ 'DownloadBlock',
231
+ 'InlineCode',
232
+ 'RSSFeed',
233
+ ],
234
+ modals: [
235
+ 'OverlayBase',
236
+ 'GenericModal',
237
+ 'ConfirmDialog',
238
+ 'FormDialog',
239
+ 'LoadingOverlay',
240
+ 'Tooltip',
241
+ 'ToastBanners',
242
+ 'CookieNotice',
243
+ 'WelcomePopup',
244
+ ],
245
+ onboarding: [
246
+ 'FAQAccordion',
247
+ 'OnboardingWizard',
248
+ 'ProductTour',
249
+ 'TooltipHelp',
250
+ 'SupportChat',
251
+ 'ContactSupportBlock',
252
+ ],
253
+ search: [
254
+ 'SiteSearchBar',
255
+ 'AutoSuggest',
256
+ 'FacetFilters',
257
+ 'SearchResultCard',
258
+ 'SearchResults',
259
+ 'SearchEmptyState',
260
+ ],
261
+ 'site-identity': [
262
+ 'LogoDisplay',
263
+ 'AnimatedBrandMark',
264
+ 'Taglines',
265
+ 'BrandingShell',
266
+ 'BrandIconGrid',
267
+ 'FaviconUploader',
268
+ ],
269
+ social: [
270
+ 'SocialShareBar',
271
+ 'SocialLoginButtons',
272
+ 'SocialEmbed',
273
+ 'FollowLike',
274
+ 'ReactionsBar',
275
+ 'CommentsBox',
276
+ 'ReviewsForm',
277
+ ],
278
+ 'user-accounts': [
279
+ 'AuthForm',
280
+ 'PasswordReset',
281
+ 'MultiFactorAuthInput',
282
+ 'ProfileOverview',
283
+ 'ProfileSettings',
284
+ 'AccountSettings',
285
+ 'RoleBadge',
286
+ ],
287
+ blog: [
288
+ 'BlogCard',
289
+ 'AuthorByline',
290
+ 'TagList',
291
+ 'BlogBadge',
292
+ 'ReadingBar',
293
+ 'ArticleRenderer',
294
+ 'RelatedPosts',
295
+ 'ArchiveList',
296
+ 'CommentsSection',
297
+ ],
298
+ };
299
+
300
+ /* build lookup maps */
301
+ const ALL_COMPONENTS = new Map();
302
+ for (const [cat, names] of Object.entries(CATEGORIES)) {
303
+ for (const n of names) ALL_COMPONENTS.set(n.toLowerCase(), { name: n, category: cat });
304
+ }
305
+
306
+ const CATEGORY_ALIASES = new Map();
307
+ for (const cat of Object.keys(CATEGORIES)) {
308
+ CATEGORY_ALIASES.set(cat.toLowerCase(), cat);
309
+ CATEGORY_ALIASES.set(cat.replace(/-/g, '').toLowerCase(), cat);
310
+ CATEGORY_ALIASES.set(cat.replace(/-/g, ' ').toLowerCase(), cat);
311
+ }
312
+
313
+ const _totalCount = Object.values(CATEGORIES).reduce((sum, a) => sum + a.length, 0);
314
+
315
+ /* ══════════════════════════════════════════════════════════════════ */
316
+ /* Package manager detection */
317
+ /* ══════════════════════════════════════════════════════════════════ */
318
+
319
+ function detectPackageManager() {
320
+ const cwd = process.cwd();
321
+
322
+ // Check for lockfiles in order of preference
323
+ if (fs.existsSync(path.join(cwd, 'bun.lock'))) {
324
+ return { manager: 'bun', command: 'bun install', displayName: 'Bun' };
325
+ }
326
+ if (fs.existsSync(path.join(cwd, 'pnpm-lock.yaml'))) {
327
+ return { manager: 'pnpm', command: 'pnpm install', displayName: 'pnpm' };
328
+ }
329
+ if (fs.existsSync(path.join(cwd, 'yarn.lock'))) {
330
+ return { manager: 'yarn', command: 'yarn install', displayName: 'Yarn' };
331
+ }
332
+ if (fs.existsSync(path.join(cwd, 'package-lock.json'))) {
333
+ return { manager: 'npm', command: 'npm install', displayName: 'npm' };
334
+ }
335
+
336
+ // Default to npm if no lockfile found
337
+ return { manager: 'npm', command: 'npm install', displayName: 'npm' };
338
+ }
339
+
340
+ /* ══════════════════════════════════════════════════════════════════ */
341
+ /* INIT command */
342
+ /* ══════════════════════════════════════════════════════════════════ */
343
+
344
+ function init() {
345
+ printBanner();
346
+
347
+ const configName = 'electroplix.config.ts';
348
+ const providerRel = path.join('components', 'providers.tsx');
349
+ const configPath = path.join(process.cwd(), configName);
350
+ const providerDir = path.join(process.cwd(), 'components');
351
+ const providerPath = path.join(providerDir, 'providers.tsx');
352
+
353
+ const created = [];
354
+ const skipped = [];
355
+
356
+ /* ── config file ─────────────────────────────────────── */
357
+ if (fs.existsSync(configPath)) {
358
+ skipped.push(configName);
359
+ } else {
360
+ const configTemplate = `import { defineConfig } from "@electroplix/components";
361
+
362
+ const config = defineConfig({
363
+ // \u2500\u2500 Global overrides (applied to ALL categories) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
364
+ accentColor: "#7C3AED",
365
+ textColor: "#E5E7EB",
366
+ bgColor: "#0b0b0c",
367
+ borderColor: "rgba(255,255,255,0.14)",
368
+ radius: 14,
369
+ fontFamily: "Inter, system-ui, sans-serif",
370
+
371
+ // \u2500\u2500 Per-category overrides \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
372
+ // navigation: { sticky: true, height: 64 },
373
+ // hero: { minH: 600 },
374
+ // buttons: { bgColor: "#e94560", radius: 12 },
375
+ // forms: { inputBg: "rgba(255,255,255,0.05)" },
376
+ // ecommerce: { accentColor: "#10B981" },
377
+ });
378
+
379
+ export default config;
380
+ `;
381
+ fs.writeFileSync(configPath, configTemplate, 'utf-8');
382
+ created.push(configName);
383
+ }
384
+
385
+ /* ── providers wrapper ───────────────────────────────── */
386
+ if (fs.existsSync(providerPath)) {
387
+ skipped.push(providerRel);
388
+ } else {
389
+ if (!fs.existsSync(providerDir)) {
390
+ fs.mkdirSync(providerDir, { recursive: true });
391
+ }
392
+ const providerTemplate = `"use client";
393
+
394
+ import { ElectroplixProvider } from "@electroplix/components";
395
+ import config from "../electroplix.config";
396
+
397
+ /**
398
+ * Client-side providers wrapper.
399
+ * Import this into your root layout (which stays as a Server Component).
400
+ */
401
+ export function Providers({ children }: { children: React.ReactNode }) {
402
+ return (
403
+ <ElectroplixProvider config={config}>
404
+ {children}
405
+ </ElectroplixProvider>
406
+ );
407
+ }
408
+ `;
409
+ fs.writeFileSync(providerPath, providerTemplate, 'utf-8');
410
+ created.push(providerRel);
411
+ }
412
+
413
+ /* ── output ──────────────────────────────────────────── */
414
+ if (created.length) {
415
+ for (const f of created) {
416
+ console.log(` ${s.green(B.check)} Created ${s.bold(f)}`);
417
+ }
418
+ }
419
+ if (skipped.length) {
420
+ for (const f of skipped) {
421
+ console.log(` ${s.yellow(B.diamond)} Skipped ${s.bold(f)} ${s.gray('(already exists)')}`);
422
+ }
423
+ }
424
+
425
+ /* ── install package ── */
426
+ const { execSync } = require('node:child_process');
427
+ const pmInfo = detectPackageManager();
428
+
429
+ try {
430
+ execSync(`${pmInfo.command} @electroplix/components`, { stdio: 'inherit' });
431
+ } catch (_err) {
432
+ }
433
+ }
434
+
435
+ /* ══════════════════════════════════════════════════════════════════ */
436
+ /* ADD command */
437
+ /* ══════════════════════════════════════════════════════════════════ */
438
+
439
+ function add(name) {
440
+ printBanner();
441
+
442
+ if (!name) {
443
+ console.log(` ${s.red('Error:')} Please provide a component or category name.\n`);
444
+ console.log(` ${s.gray('Usage:')} npx @electroplix/components add <component|category>\n`);
445
+ process.exit(1);
446
+ }
447
+
448
+ const key = name.toLowerCase().replace(/\s+/g, '-');
449
+
450
+ /* ── try category match first ──────────────────────── */
451
+ const catMatch = CATEGORY_ALIASES.get(key);
452
+ if (catMatch) {
453
+ const comps = CATEGORIES[catMatch];
454
+ console.log(` ${s.bold(s.purple(catMatch))} ${s.gray(`(${comps.length} components)`)}\n`);
455
+
456
+ for (let i = 0; i < comps.length; i++) {
457
+ const isLast = i === comps.length - 1;
458
+ const prefix = isLast ? B.last : B.branch;
459
+ console.log(` ${s.gray(prefix)} ${comps[i]}`);
460
+ }
461
+
462
+ console.log(`\n ${s.gray('Import all from category:')}`);
463
+ console.log(` ${s.cyan(`import { ${comps.join(', ')} } from '@electroplix/components';`)}\n`);
464
+ return;
465
+ }
466
+
467
+ /* ── try individual component match ────────────────── */
468
+ const entry = ALL_COMPONENTS.get(key);
469
+ if (!entry) {
470
+ console.log(` ${s.red('Error:')} Component "${name}" not found.\n`);
471
+ console.log(` ${s.gray('Run')} npx @electroplix/components list ${s.gray('to see all available components.')}\n`);
472
+ process.exit(1);
473
+ }
474
+
475
+ console.log(` ${s.bold(s.green(entry.name))} ${s.gray(`(${entry.category})`)}\n`);
476
+ console.log(` ${s.gray('Import:')}`);
477
+ console.log(` ${s.cyan(`import { ${entry.name} } from '@electroplix/components';`)}\n`);
478
+ console.log(` ${s.gray('Install:')}`);
479
+ console.log(` ${s.cyan('npm install @electroplix/components')}\n`);
480
+ }
481
+
482
+ /* ══════════════════════════════════════════════════════════════════ */
483
+ /* LIST command */
484
+ /* ══════════════════════════════════════════════════════════════════ */
485
+
486
+ function list() {
487
+ printBanner();
488
+
489
+ const catEntries = Object.entries(CATEGORIES);
490
+ const totalCount = Object.values(CATEGORIES).reduce((sum, a) => sum + a.length, 0);
491
+
492
+ console.log(` ${s.bold(s.cyan(`${totalCount} components`))} across ${s.bold(`${catEntries.length} categories`)}\n`);
493
+
494
+ for (let ci = 0; ci < catEntries.length; ci++) {
495
+ const [cat, names] = catEntries[ci];
496
+ const isLastCat = ci === catEntries.length - 1;
497
+ const catPrefix = isLastCat ? B.end : B.tee;
498
+ const childPipe = isLastCat ? ' ' : B.pipe;
499
+
500
+ console.log(` ${s.bold(`${catPrefix}${B.h}${B.h} ${s.purple(cat)}`)} ${s.gray(`(${names.length})`)}`);
501
+
502
+ for (let ni = 0; ni < names.length; ni++) {
503
+ const isLastName = ni === names.length - 1;
504
+ const namePrefix = isLastName ? B.last : B.branch;
505
+ console.log(` ${childPipe}${namePrefix}${B.h}${B.h} ${names[ni]}`);
506
+ }
507
+
508
+ if (!isLastCat) console.log('');
509
+ }
510
+
511
+ console.log('');
512
+ }
513
+
514
+ /* ══════════════════════════════════════════════════════════════════ */
515
+ /* HELP / default */
516
+ /* ══════════════════════════════════════════════════════════════════ */
517
+
518
+ function help() {
519
+ printBanner();
520
+ console.log(` ${s.bold('Usage:')}\n`);
521
+ console.log(` ${s.cyan('npx @electroplix/components init')} ${s.gray('Scaffold config + provider setup')}`);
522
+ console.log(` ${s.cyan('npx @electroplix/components add')} <name> ${s.gray('Show install/import instructions')}`);
523
+ console.log(` ${s.cyan('npx @electroplix/components list')} ${s.gray('List all available components')}\n`);
524
+ }
525
+
526
+ /* ══════════════════════════════════════════════════════════════════ */
527
+ /* Main */
528
+ /* ══════════════════════════════════════════════════════════════════ */
529
+
530
+ const [, , cmd, ...args] = process.argv;
531
+
532
+ switch (cmd) {
533
+ case 'init':
534
+ init();
535
+ break;
536
+ case 'add':
537
+ add(args.join(' '));
538
+ break;
539
+ case 'list':
540
+ list();
541
+ break;
542
+ default:
543
+ help();
544
+ }