@electroplix/components 0.4.1 → 0.5.0-alpha.2

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