cooterlabs 1.0.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +77 -14
- package/bin/index.js +510 -75
- package/package.json +12 -4
- package/templates/action-sheet/action-sheet.tsx +113 -0
- package/templates/alert/alert.tsx +96 -0
- package/templates/avatar/avatar.tsx +21 -4
- package/templates/badge/badge.tsx +106 -15
- package/templates/breadcrumb/breadcrumb.tsx +113 -0
- package/templates/button/button.tsx +40 -2
- package/templates/button-group/button-group.tsx +93 -0
- package/templates/card/card.tsx +31 -19
- package/templates/checkbox/checkbox.tsx +4 -3
- package/templates/context-menu/context-menu.tsx +177 -0
- package/templates/dialog/dialog.tsx +127 -0
- package/templates/dropdown/dropdown.tsx +180 -0
- package/templates/input/input.tsx +30 -3
- package/templates/label/label.tsx +2 -2
- package/templates/list/list.tsx +96 -0
- package/templates/loader/loader.tsx +38 -0
- package/templates/navbar-bottom/navbar-bottom.tsx +76 -0
- package/templates/navbar-top/navbar-top.tsx +84 -0
- package/templates/page-control/page-control.tsx +55 -0
- package/templates/pagination/pagination.tsx +109 -0
- package/templates/progress/progress.tsx +69 -0
- package/templates/radio/radio.tsx +41 -0
- package/templates/stepper/stepper.tsx +93 -0
- package/templates/tabs/tabs.tsx +57 -0
- package/templates/textarea/textarea.tsx +1 -1
- package/templates/toggle/toggle.tsx +27 -0
- package/templates/tooltip/tooltip.tsx +37 -0
package/bin/index.js
CHANGED
|
@@ -14,6 +14,116 @@ const __dirname = path.dirname(__filename);
|
|
|
14
14
|
const cwd = process.cwd();
|
|
15
15
|
|
|
16
16
|
const REGISTRY = {
|
|
17
|
+
alert: {
|
|
18
|
+
name: "Alert",
|
|
19
|
+
dependencies: ["lucide-react", "clsx", "tailwind-merge"],
|
|
20
|
+
dev_dependencies: [],
|
|
21
|
+
},
|
|
22
|
+
"action-sheet": {
|
|
23
|
+
name: "ActionSheet",
|
|
24
|
+
dependencies: ["@radix-ui/react-dialog", "clsx", "tailwind-merge"],
|
|
25
|
+
dev_dependencies: [],
|
|
26
|
+
},
|
|
27
|
+
breadcrumb: {
|
|
28
|
+
name: "Breadcrumb",
|
|
29
|
+
dependencies: ["lucide-react", "clsx", "tailwind-merge"],
|
|
30
|
+
dev_dependencies: [],
|
|
31
|
+
},
|
|
32
|
+
"button-group": {
|
|
33
|
+
name: "ButtonGroup",
|
|
34
|
+
dependencies: ["clsx", "tailwind-merge"],
|
|
35
|
+
dev_dependencies: [],
|
|
36
|
+
},
|
|
37
|
+
"context-menu": {
|
|
38
|
+
name: "ContextMenu",
|
|
39
|
+
dependencies: [
|
|
40
|
+
"@radix-ui/react-context-menu",
|
|
41
|
+
"lucide-react",
|
|
42
|
+
"clsx",
|
|
43
|
+
"tailwind-merge",
|
|
44
|
+
],
|
|
45
|
+
dev_dependencies: [],
|
|
46
|
+
},
|
|
47
|
+
dialog: {
|
|
48
|
+
name: "Dialog",
|
|
49
|
+
dependencies: [
|
|
50
|
+
"@radix-ui/react-dialog",
|
|
51
|
+
"lucide-react",
|
|
52
|
+
"clsx",
|
|
53
|
+
"tailwind-merge",
|
|
54
|
+
],
|
|
55
|
+
dev_dependencies: [],
|
|
56
|
+
},
|
|
57
|
+
dropdown: {
|
|
58
|
+
name: "Dropdown",
|
|
59
|
+
dependencies: [
|
|
60
|
+
"@radix-ui/react-dropdown-menu",
|
|
61
|
+
"lucide-react",
|
|
62
|
+
"clsx",
|
|
63
|
+
"tailwind-merge",
|
|
64
|
+
],
|
|
65
|
+
dev_dependencies: [],
|
|
66
|
+
},
|
|
67
|
+
list: {
|
|
68
|
+
name: "List",
|
|
69
|
+
dependencies: ["lucide-react", "clsx", "tailwind-merge"],
|
|
70
|
+
dev_dependencies: [],
|
|
71
|
+
},
|
|
72
|
+
loader: {
|
|
73
|
+
name: "Loader",
|
|
74
|
+
dependencies: ["clsx", "tailwind-merge"],
|
|
75
|
+
dev_dependencies: [],
|
|
76
|
+
},
|
|
77
|
+
"navbar-bottom": {
|
|
78
|
+
name: "NavbarBottom",
|
|
79
|
+
dependencies: ["lucide-react", "clsx", "tailwind-merge"],
|
|
80
|
+
dev_dependencies: [],
|
|
81
|
+
},
|
|
82
|
+
"navbar-top": {
|
|
83
|
+
name: "NavbarTop",
|
|
84
|
+
dependencies: ["lucide-react", "clsx", "tailwind-merge"],
|
|
85
|
+
dev_dependencies: [],
|
|
86
|
+
},
|
|
87
|
+
"page-control": {
|
|
88
|
+
name: "PageControl",
|
|
89
|
+
dependencies: ["clsx", "tailwind-merge"],
|
|
90
|
+
dev_dependencies: [],
|
|
91
|
+
},
|
|
92
|
+
pagination: {
|
|
93
|
+
name: "Pagination",
|
|
94
|
+
dependencies: ["lucide-react", "clsx", "tailwind-merge"],
|
|
95
|
+
dev_dependencies: [],
|
|
96
|
+
},
|
|
97
|
+
progress: {
|
|
98
|
+
name: "Progress",
|
|
99
|
+
dependencies: ["@radix-ui/react-progress", "clsx", "tailwind-merge"],
|
|
100
|
+
dev_dependencies: [],
|
|
101
|
+
},
|
|
102
|
+
radio: {
|
|
103
|
+
name: "Radio",
|
|
104
|
+
dependencies: ["@radix-ui/react-radio-group", "clsx", "tailwind-merge"],
|
|
105
|
+
dev_dependencies: [],
|
|
106
|
+
},
|
|
107
|
+
stepper: {
|
|
108
|
+
name: "Stepper",
|
|
109
|
+
dependencies: ["lucide-react", "clsx", "tailwind-merge"],
|
|
110
|
+
dev_dependencies: [],
|
|
111
|
+
},
|
|
112
|
+
tabs: {
|
|
113
|
+
name: "Tabs",
|
|
114
|
+
dependencies: ["@radix-ui/react-tabs", "clsx", "tailwind-merge"],
|
|
115
|
+
dev_dependencies: [],
|
|
116
|
+
},
|
|
117
|
+
toggle: {
|
|
118
|
+
name: "Toggle",
|
|
119
|
+
dependencies: ["@radix-ui/react-switch", "clsx", "tailwind-merge"],
|
|
120
|
+
dev_dependencies: [],
|
|
121
|
+
},
|
|
122
|
+
tooltip: {
|
|
123
|
+
name: "Tooltip",
|
|
124
|
+
dependencies: ["@radix-ui/react-tooltip", "clsx", "tailwind-merge"],
|
|
125
|
+
dev_dependencies: [],
|
|
126
|
+
},
|
|
17
127
|
button: {
|
|
18
128
|
name: "Button",
|
|
19
129
|
dependencies: [
|
|
@@ -57,15 +167,237 @@ const REGISTRY = {
|
|
|
57
167
|
checkbox: {
|
|
58
168
|
name: "Checkbox",
|
|
59
169
|
dependencies: [
|
|
170
|
+
"@radix-ui/react-checkbox",
|
|
60
171
|
"lucide-react",
|
|
61
172
|
"clsx",
|
|
62
173
|
"tailwind-merge",
|
|
63
|
-
"@radix-ui/react-checkbox",
|
|
64
174
|
],
|
|
65
175
|
dev_dependencies: [],
|
|
66
176
|
},
|
|
67
177
|
};
|
|
68
178
|
|
|
179
|
+
const THEMES = {
|
|
180
|
+
aeon: `@theme static {
|
|
181
|
+
/* ——— Aeon Product Design System ——— */
|
|
182
|
+
--font-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
|
|
183
|
+
|
|
184
|
+
/* Semantic colors */
|
|
185
|
+
--color-background: #ffffff;
|
|
186
|
+
--color-foreground: #1c222a;
|
|
187
|
+
--color-card: #ffffff;
|
|
188
|
+
--color-card-foreground: #1c222a;
|
|
189
|
+
--color-popover: #ffffff;
|
|
190
|
+
--color-popover-foreground: #1c222a;
|
|
191
|
+
--color-primary: #0d3435;
|
|
192
|
+
--color-primary-foreground: #ffffff;
|
|
193
|
+
--color-secondary: #f4f5f6;
|
|
194
|
+
--color-secondary-foreground: #1c222a;
|
|
195
|
+
--color-muted: #e7e9ec;
|
|
196
|
+
--color-muted-foreground: #475569;
|
|
197
|
+
--color-accent: #f1f3f3;
|
|
198
|
+
--color-accent-foreground: #0a2b2b;
|
|
199
|
+
--color-destructive: #821717;
|
|
200
|
+
--color-destructive-foreground: #ffffff;
|
|
201
|
+
--color-border: #cbcfd5;
|
|
202
|
+
--color-input: #cbcfd5;
|
|
203
|
+
--color-ring: #0d3435;
|
|
204
|
+
--color-success: #14532d;
|
|
205
|
+
--color-success-foreground: #ffffff;
|
|
206
|
+
--color-info: #075985;
|
|
207
|
+
--color-info-foreground: #ffffff;
|
|
208
|
+
--color-warning: #713f12;
|
|
209
|
+
--color-warning-foreground: #ffffff;
|
|
210
|
+
--color-error: #991b1b;
|
|
211
|
+
--color-error-foreground: #ffffff;
|
|
212
|
+
|
|
213
|
+
/* Primitive scales */
|
|
214
|
+
--color-primary-50: #f1f3f3;
|
|
215
|
+
--color-primary-100: #e0e6e6;
|
|
216
|
+
--color-primary-200: #bcc9c9;
|
|
217
|
+
--color-primary-300: #93a8a8;
|
|
218
|
+
--color-primary-400: #577778;
|
|
219
|
+
--color-primary-500: #0f3d3e;
|
|
220
|
+
--color-primary-600: #0d3435;
|
|
221
|
+
--color-primary-700: #0a2b2b;
|
|
222
|
+
--color-primary-800: #082222;
|
|
223
|
+
--color-primary-900: #061819;
|
|
224
|
+
--color-grey-50: #f4f5f6;
|
|
225
|
+
--color-grey-100: #e7e9ec;
|
|
226
|
+
--color-grey-200: #cbcfd5;
|
|
227
|
+
--color-grey-300: #acb2bc;
|
|
228
|
+
--color-grey-400: #7e8896;
|
|
229
|
+
--color-grey-500: #475569;
|
|
230
|
+
--color-grey-600: #3c4859;
|
|
231
|
+
--color-grey-700: #323c4a;
|
|
232
|
+
--color-grey-800: #272f3a;
|
|
233
|
+
--color-grey-900: #1c222a;
|
|
234
|
+
--color-success-50: #f1f5f2;
|
|
235
|
+
--color-success-100: #e0e9e4;
|
|
236
|
+
--color-success-200: #bdcfc4;
|
|
237
|
+
--color-success-300: #95b2a0;
|
|
238
|
+
--color-success-400: #5a876c;
|
|
239
|
+
--color-success-500: #14532d;
|
|
240
|
+
--color-success-600: #114726;
|
|
241
|
+
--color-success-700: #0e3a20;
|
|
242
|
+
--color-success-800: #0b2e19;
|
|
243
|
+
--color-success-900: #082112;
|
|
244
|
+
--color-error-50: #f9f1f1;
|
|
245
|
+
--color-error-100: #f2e1e1;
|
|
246
|
+
--color-error-200: #e2bfbf;
|
|
247
|
+
--color-error-300: #d19898;
|
|
248
|
+
--color-error-400: #b85f5f;
|
|
249
|
+
--color-error-500: #991b1b;
|
|
250
|
+
--color-error-600: #821717;
|
|
251
|
+
--color-error-700: #6b1313;
|
|
252
|
+
--color-error-800: #540f0f;
|
|
253
|
+
--color-error-900: #3d0b0b;
|
|
254
|
+
--color-warning-50: #f6f3f1;
|
|
255
|
+
--color-warning-100: #ede6e0;
|
|
256
|
+
--color-warning-200: #d7c9bd;
|
|
257
|
+
--color-warning-300: #bfa994;
|
|
258
|
+
--color-warning-400: #9c7959;
|
|
259
|
+
--color-warning-500: #713f12;
|
|
260
|
+
--color-warning-600: #60360f;
|
|
261
|
+
--color-warning-700: #4f2c0d;
|
|
262
|
+
--color-warning-800: #3e230a;
|
|
263
|
+
--color-warning-900: #2d1907;
|
|
264
|
+
--color-info-50: #f0f5f8;
|
|
265
|
+
--color-info-100: #dfe9ef;
|
|
266
|
+
--color-info-200: #bad1dd;
|
|
267
|
+
--color-info-300: #8fb4c8;
|
|
268
|
+
--color-info-400: #518baa;
|
|
269
|
+
--color-info-500: #075985;
|
|
270
|
+
--color-info-600: #064c71;
|
|
271
|
+
--color-info-700: #053e5d;
|
|
272
|
+
--color-info-800: #04314a;
|
|
273
|
+
--color-info-900: #032435;
|
|
274
|
+
|
|
275
|
+
/* Typography — Aeon text ramp (text-h1 … text-c3, text-label, text-btn-*) */
|
|
276
|
+
--text-h1: 3rem;
|
|
277
|
+
--text-h1--line-height: 3.625rem;
|
|
278
|
+
--text-h1--font-weight: 600;
|
|
279
|
+
--text-h2: 2.5rem;
|
|
280
|
+
--text-h2--line-height: 3rem;
|
|
281
|
+
--text-h2--font-weight: 600;
|
|
282
|
+
--text-h3: 2rem;
|
|
283
|
+
--text-h3--line-height: 2.375rem;
|
|
284
|
+
--text-h3--font-weight: 600;
|
|
285
|
+
--text-h4: 1.75rem;
|
|
286
|
+
--text-h4--line-height: 2.125rem;
|
|
287
|
+
--text-h4--font-weight: 600;
|
|
288
|
+
--text-h5: 1.5rem;
|
|
289
|
+
--text-h5--line-height: 1.75rem;
|
|
290
|
+
--text-h5--font-weight: 600;
|
|
291
|
+
--text-s1: 1.125rem;
|
|
292
|
+
--text-s1--line-height: 1.75rem;
|
|
293
|
+
--text-s1--font-weight: 600;
|
|
294
|
+
--text-s2: 1rem;
|
|
295
|
+
--text-s2--line-height: 1.5rem;
|
|
296
|
+
--text-s2--font-weight: 600;
|
|
297
|
+
--text-b1: 1rem;
|
|
298
|
+
--text-b1--line-height: 1.5rem;
|
|
299
|
+
--text-b1--font-weight: 400;
|
|
300
|
+
--text-b2: 1rem;
|
|
301
|
+
--text-b2--line-height: 1.5rem;
|
|
302
|
+
--text-b2--font-weight: 500;
|
|
303
|
+
--text-b3: 0.875rem;
|
|
304
|
+
--text-b3--line-height: 1.25rem;
|
|
305
|
+
--text-b3--font-weight: 400;
|
|
306
|
+
--text-b4: 0.875rem;
|
|
307
|
+
--text-b4--line-height: 1.25rem;
|
|
308
|
+
--text-b4--font-weight: 500;
|
|
309
|
+
--text-c1: 0.75rem;
|
|
310
|
+
--text-c1--line-height: 1rem;
|
|
311
|
+
--text-c1--font-weight: 400;
|
|
312
|
+
--text-c2: 0.75rem;
|
|
313
|
+
--text-c2--line-height: 1rem;
|
|
314
|
+
--text-c2--font-weight: 500;
|
|
315
|
+
--text-c3: 0.625rem;
|
|
316
|
+
--text-c3--line-height: 0.875rem;
|
|
317
|
+
--text-c3--font-weight: 500;
|
|
318
|
+
--text-label: 0.75rem;
|
|
319
|
+
--text-label--line-height: 1rem;
|
|
320
|
+
--text-label--font-weight: 500;
|
|
321
|
+
--text-btn-giant: 1.125rem;
|
|
322
|
+
--text-btn-giant--line-height: 1.5rem;
|
|
323
|
+
--text-btn-giant--font-weight: 600;
|
|
324
|
+
--text-btn-large: 1rem;
|
|
325
|
+
--text-btn-large--line-height: 1.25rem;
|
|
326
|
+
--text-btn-large--font-weight: 600;
|
|
327
|
+
--text-btn-medium: 0.875rem;
|
|
328
|
+
--text-btn-medium--line-height: 1rem;
|
|
329
|
+
--text-btn-medium--font-weight: 600;
|
|
330
|
+
--text-btn-small: 0.75rem;
|
|
331
|
+
--text-btn-small--line-height: 1rem;
|
|
332
|
+
--text-btn-small--font-weight: 600;
|
|
333
|
+
--text-btn-tiny: 0.625rem;
|
|
334
|
+
--text-btn-tiny--line-height: 0.75rem;
|
|
335
|
+
--text-btn-tiny--font-weight: 600;
|
|
336
|
+
|
|
337
|
+
/* Radius scale — rounded-xxs … rounded-xl */
|
|
338
|
+
--radius-xxs: 0.25rem;
|
|
339
|
+
--radius-xs: 0.5rem;
|
|
340
|
+
--radius-sm: 0.75rem;
|
|
341
|
+
--radius-md: 1rem;
|
|
342
|
+
--radius-lg: 1.25rem;
|
|
343
|
+
--radius-xl: 1.5rem;
|
|
344
|
+
|
|
345
|
+
/* Elevation — shadow-100 … shadow-800 */
|
|
346
|
+
--shadow-100: 0 4px 4px -2px #13192714, 0 2px 4px -2px #1319271f;
|
|
347
|
+
--shadow-200: 0 8px 8px -4px #13192714, 0 4px 6px -4px #1319271f;
|
|
348
|
+
--shadow-300: 0 8px 16px -6px #13192714, 0 6px 8px -6px #1319271f;
|
|
349
|
+
--shadow-400: 0 8px 24px -4px #13192714, 0 6px 12px -6px #1319271f;
|
|
350
|
+
--shadow-500: 0 10px 32px -4px #1319271a, 0 6px 14px -6px #1319271f;
|
|
351
|
+
--shadow-600: 0 12px 42px -4px #1319271f, 0 8px 18px -6px #1319271f;
|
|
352
|
+
--shadow-700: 0 14px 64px -4px #1319271f, 0 8px 22px -6px #1319271f;
|
|
353
|
+
--shadow-800: 0 18px 88px -4px #13192724, 0 8px 28px -6px #1319271f;
|
|
354
|
+
}`,
|
|
355
|
+
zinc: `:root {
|
|
356
|
+
--background: 0 0% 100%;
|
|
357
|
+
--foreground: 240 10% 3.9%;
|
|
358
|
+
--card: 0 0% 100%;
|
|
359
|
+
--card-foreground: 240 10% 3.9%;
|
|
360
|
+
--popover: 0 0% 100%;
|
|
361
|
+
--popover-foreground: 240 10% 3.9%;
|
|
362
|
+
--primary: 240 5.9% 10%;
|
|
363
|
+
--primary-foreground: 0 0% 98%;
|
|
364
|
+
--secondary: 240 4.8% 95.9%;
|
|
365
|
+
--secondary-foreground: 240 5.9% 10%;
|
|
366
|
+
--muted: 240 4.8% 95.9%;
|
|
367
|
+
--muted-foreground: 240 3.8% 46.1%;
|
|
368
|
+
--accent: 240 4.8% 95.9%;
|
|
369
|
+
--accent-foreground: 240 5.9% 10%;
|
|
370
|
+
--destructive: 0 84.2% 60.2%;
|
|
371
|
+
--destructive-foreground: 0 0% 98%;
|
|
372
|
+
--border: 240 5.9% 90%;
|
|
373
|
+
--input: 240 5.9% 90%;
|
|
374
|
+
--ring: 240 10% 3.9%;
|
|
375
|
+
--radius: 0.5rem;
|
|
376
|
+
}`,
|
|
377
|
+
slate: `:root {
|
|
378
|
+
--background: 0 0% 100%;
|
|
379
|
+
--foreground: 222.2 84% 4.9%;
|
|
380
|
+
--card: 0 0% 100%;
|
|
381
|
+
--card-foreground: 222.2 84% 4.9%;
|
|
382
|
+
--popover: 0 0% 100%;
|
|
383
|
+
--popover-foreground: 222.2 84% 4.9%;
|
|
384
|
+
--primary: 222.2 47.4% 11.2%;
|
|
385
|
+
--primary-foreground: 210 40% 98%;
|
|
386
|
+
--secondary: 210 40% 96.1%;
|
|
387
|
+
--secondary-foreground: 222.2 47.4% 11.2%;
|
|
388
|
+
--muted: 210 40% 96.1%;
|
|
389
|
+
--muted-foreground: 215.4 16.3% 46.9%;
|
|
390
|
+
--accent: 210 40% 96.1%;
|
|
391
|
+
--accent-foreground: 222.2 47.4% 11.2%;
|
|
392
|
+
--destructive: 0 84.2% 60.2%;
|
|
393
|
+
--destructive-foreground: 210 40% 98%;
|
|
394
|
+
--border: 214.3 31.8% 91.4%;
|
|
395
|
+
--input: 214.3 31.8% 91.4%;
|
|
396
|
+
--ring: 222.2 84% 4.9%;
|
|
397
|
+
--radius: 0.5rem;
|
|
398
|
+
}`,
|
|
399
|
+
};
|
|
400
|
+
|
|
69
401
|
const templatesDir = path.join(__dirname, "../templates");
|
|
70
402
|
|
|
71
403
|
const detectEnvironment = () => {
|
|
@@ -164,13 +496,20 @@ const main = async () => {
|
|
|
164
496
|
}
|
|
165
497
|
|
|
166
498
|
const args = process.argv.slice(2);
|
|
167
|
-
|
|
168
|
-
let
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
499
|
+
const isAll = args.includes("--all");
|
|
500
|
+
let command = "add";
|
|
501
|
+
let componentName = "";
|
|
502
|
+
|
|
503
|
+
// Parse command & componentName from args (ignoring flags)
|
|
504
|
+
const positionalArgs = args.filter((a) => !a.startsWith("--"));
|
|
505
|
+
if (positionalArgs.length > 0) {
|
|
506
|
+
if (positionalArgs[0] === "init" || positionalArgs[0] === "add") {
|
|
507
|
+
command = positionalArgs[0];
|
|
508
|
+
componentName = positionalArgs[1] || "";
|
|
509
|
+
} else {
|
|
510
|
+
command = "add";
|
|
511
|
+
componentName = positionalArgs[0];
|
|
512
|
+
}
|
|
174
513
|
}
|
|
175
514
|
|
|
176
515
|
let availableComponents = Object.keys(REGISTRY);
|
|
@@ -181,102 +520,198 @@ const main = async () => {
|
|
|
181
520
|
if (dirs.length > 0) availableComponents = dirs;
|
|
182
521
|
}
|
|
183
522
|
|
|
184
|
-
if (command === "
|
|
185
|
-
|
|
523
|
+
if (command === "init") {
|
|
524
|
+
let theme = componentName; // support `cooterlabs init aeon`
|
|
525
|
+
if (!THEMES[theme]) {
|
|
186
526
|
const response = await prompts({
|
|
187
527
|
type: "select",
|
|
188
|
-
name: "
|
|
189
|
-
message: "Which
|
|
528
|
+
name: "theme",
|
|
529
|
+
message: "Which theme would you like to use?",
|
|
530
|
+
choices: Object.keys(THEMES).map((t) => ({
|
|
531
|
+
title: t === "aeon" ? "aeon (default)" : t,
|
|
532
|
+
value: t,
|
|
533
|
+
})),
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
if (!response.theme) {
|
|
537
|
+
console.log(chalk.red("❌ Theme selection cancelled."));
|
|
538
|
+
process.exit(1);
|
|
539
|
+
}
|
|
540
|
+
theme = response.theme;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// aeon is a Tailwind v4 @theme block appended at top level;
|
|
544
|
+
// legacy themes are :root vars wrapped in @layer base.
|
|
545
|
+
const isV4Theme = theme === "aeon";
|
|
546
|
+
const themeCSS = THEMES[theme];
|
|
547
|
+
let cssPath = path.join(cwd, "src", "index.css");
|
|
548
|
+
|
|
549
|
+
if (env.framework === "next") {
|
|
550
|
+
if (env.isAppDir) {
|
|
551
|
+
cssPath = fs.existsSync(path.join(cwd, "src", "app"))
|
|
552
|
+
? path.join(cwd, "src", "app", "globals.css")
|
|
553
|
+
: path.join(cwd, "app", "globals.css");
|
|
554
|
+
} else {
|
|
555
|
+
cssPath = fs.existsSync(path.join(cwd, "src", "styles", "globals.css"))
|
|
556
|
+
? path.join(cwd, "src", "styles", "globals.css")
|
|
557
|
+
: path.join(cwd, "styles", "globals.css");
|
|
558
|
+
}
|
|
559
|
+
} else {
|
|
560
|
+
cssPath = fs.existsSync(path.join(cwd, "src", "index.css"))
|
|
561
|
+
? path.join(cwd, "src", "index.css")
|
|
562
|
+
: path.join(cwd, "src", "globals.css");
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// Ensure dir exists
|
|
566
|
+
const cssDir = path.dirname(cssPath);
|
|
567
|
+
if (!fs.existsSync(cssDir)) {
|
|
568
|
+
fs.mkdirSync(cssDir, { recursive: true });
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
let cssContent = "";
|
|
572
|
+
if (fs.existsSync(cssPath)) {
|
|
573
|
+
cssContent = fs.readFileSync(cssPath, "utf-8");
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
const hasTailwindDirectives =
|
|
577
|
+
cssContent.includes("@tailwind base") ||
|
|
578
|
+
cssContent.includes('@import "tailwindcss');
|
|
579
|
+
if (!hasTailwindDirectives) {
|
|
580
|
+
cssContent = isV4Theme
|
|
581
|
+
? `@import "tailwindcss";\n\n${cssContent}`
|
|
582
|
+
: `@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\n${cssContent}`;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
const themeBlock = isV4Theme
|
|
586
|
+
? `\n${themeCSS}\n`
|
|
587
|
+
: `\n@layer base {\n${themeCSS}\n}\n`;
|
|
588
|
+
|
|
589
|
+
const hasExistingVars = isV4Theme
|
|
590
|
+
? cssContent.includes("@theme")
|
|
591
|
+
: cssContent.includes(":root {") || cssContent.includes("--background:");
|
|
592
|
+
|
|
593
|
+
if (hasExistingVars) {
|
|
594
|
+
console.log(
|
|
595
|
+
chalk.yellow(
|
|
596
|
+
`⚠️ Theme variables may already exist in ${cssPath}. Please append them manually if needed:\n${themeBlock}`,
|
|
597
|
+
),
|
|
598
|
+
);
|
|
599
|
+
} else {
|
|
600
|
+
cssContent = `${cssContent}${themeBlock}`;
|
|
601
|
+
fs.writeFileSync(cssPath, cssContent);
|
|
602
|
+
console.log(chalk.green(`✅ Theme '${theme}' initialized in ${cssPath}`));
|
|
603
|
+
}
|
|
604
|
+
} else if (command === "add") {
|
|
605
|
+
let componentsToAdd = [];
|
|
606
|
+
|
|
607
|
+
if (isAll) {
|
|
608
|
+
componentsToAdd = availableComponents;
|
|
609
|
+
} else if (componentName) {
|
|
610
|
+
componentsToAdd = [componentName];
|
|
611
|
+
} else {
|
|
612
|
+
const response = await prompts({
|
|
613
|
+
type: "autocompleteMultiselect",
|
|
614
|
+
name: "components",
|
|
615
|
+
message:
|
|
616
|
+
"Which components would you like to add? (Space to select, Enter to submit, or use --all flag)",
|
|
190
617
|
choices: availableComponents.map((c) => ({ title: c, value: c })),
|
|
191
618
|
});
|
|
192
|
-
|
|
619
|
+
componentsToAdd = response.components || [];
|
|
193
620
|
}
|
|
194
621
|
|
|
195
|
-
if (
|
|
196
|
-
console.log(chalk.red("❌ Operation cancelled."));
|
|
622
|
+
if (componentsToAdd.length === 0) {
|
|
623
|
+
console.log(chalk.red("❌ No components selected. Operation cancelled."));
|
|
197
624
|
process.exit(1);
|
|
198
625
|
}
|
|
199
626
|
|
|
200
|
-
|
|
201
|
-
|
|
627
|
+
// Check availability
|
|
628
|
+
const notFound = componentsToAdd.filter(
|
|
629
|
+
(c) => !availableComponents.includes(c),
|
|
630
|
+
);
|
|
631
|
+
if (notFound.length > 0) {
|
|
632
|
+
console.log(chalk.red(`❌ Components not found: ${notFound.join(", ")}`));
|
|
202
633
|
process.exit(1);
|
|
203
634
|
}
|
|
204
635
|
|
|
205
|
-
const templatePath = path.join(templatesDir, componentName);
|
|
206
|
-
|
|
207
636
|
// Create target dir
|
|
208
637
|
const targetDir = path.join(cwd, env.componentsDir);
|
|
209
638
|
if (!fs.existsSync(targetDir)) {
|
|
210
639
|
fs.mkdirSync(targetDir, { recursive: true });
|
|
211
640
|
}
|
|
212
641
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
path.join(templatePath, srcFile),
|
|
230
|
-
"utf-8",
|
|
642
|
+
let allDependencies = new Set();
|
|
643
|
+
|
|
644
|
+
for (const comp of componentsToAdd) {
|
|
645
|
+
const templatePath = path.join(templatesDir, comp);
|
|
646
|
+
const extension = env.typescript ? ".tsx" : ".jsx";
|
|
647
|
+
const componentFile = `${comp}${extension}`;
|
|
648
|
+
const targetPath = path.join(targetDir, componentFile);
|
|
649
|
+
|
|
650
|
+
const spinner = ora(`Installing ${comp}...`).start();
|
|
651
|
+
|
|
652
|
+
try {
|
|
653
|
+
let sourceContent = "";
|
|
654
|
+
if (fs.existsSync(templatePath)) {
|
|
655
|
+
const files = fs.readdirSync(templatePath);
|
|
656
|
+
const srcFile = files.find(
|
|
657
|
+
(f) => f.endsWith(".tsx") || f.endsWith(".jsx"),
|
|
231
658
|
);
|
|
659
|
+
if (srcFile) {
|
|
660
|
+
sourceContent = fs.readFileSync(
|
|
661
|
+
path.join(templatePath, srcFile),
|
|
662
|
+
"utf-8",
|
|
663
|
+
);
|
|
664
|
+
} else if (files.length > 0) {
|
|
665
|
+
sourceContent = fs.readFileSync(
|
|
666
|
+
path.join(templatePath, files[0]),
|
|
667
|
+
"utf-8",
|
|
668
|
+
);
|
|
669
|
+
}
|
|
232
670
|
} else {
|
|
233
|
-
sourceContent =
|
|
234
|
-
|
|
235
|
-
|
|
671
|
+
sourceContent = `import React from 'react';\n\nexport const ${REGISTRY[comp]?.name || "Component"} = () => { return <div>${comp}</div>; };\n`;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
// Format code
|
|
675
|
+
let formattedContent = sourceContent;
|
|
676
|
+
try {
|
|
677
|
+
formattedContent = await formatFile(sourceContent, targetPath);
|
|
678
|
+
} catch (err) {
|
|
679
|
+
console.log(
|
|
680
|
+
chalk.yellow(
|
|
681
|
+
`\n⚠️ Formatting failed for ${comp}. Using original formatting.`,
|
|
682
|
+
),
|
|
236
683
|
);
|
|
237
684
|
}
|
|
238
|
-
} else {
|
|
239
|
-
// Fallback or generic content simply to show it works
|
|
240
|
-
sourceContent = `import React from 'react';\n\nexport const ${REGISTRY[componentName]?.name || "Component"} = () => { return <div>${componentName}</div>; };\n`;
|
|
241
|
-
}
|
|
242
685
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
// Simple type stripping if needed, but omitted for stability.
|
|
246
|
-
// It's recommended to publish compiled JS if targeting non-TS,
|
|
247
|
-
// or let the user fix up the .jsx file.
|
|
248
|
-
}
|
|
686
|
+
fs.writeFileSync(targetPath, formattedContent, "utf-8");
|
|
687
|
+
spinner.succeed(`Created ${componentFile} in ${env.componentsDir}`);
|
|
249
688
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
689
|
+
// Track dependencies
|
|
690
|
+
const registryEntry = REGISTRY[comp];
|
|
691
|
+
if (registryEntry && registryEntry.dependencies) {
|
|
692
|
+
registryEntry.dependencies.forEach((d) => allDependencies.add(d));
|
|
693
|
+
}
|
|
254
694
|
} catch (err) {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
);
|
|
695
|
+
spinner.fail(`Failed to add ${comp}`);
|
|
696
|
+
console.error(chalk.red(err.message));
|
|
258
697
|
}
|
|
698
|
+
}
|
|
259
699
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
installDependencies(registryEntry.dependencies, env.pkgCommand);
|
|
272
|
-
depSpinner.succeed(`Installed dependencies.`);
|
|
700
|
+
if (allDependencies.size > 0) {
|
|
701
|
+
const depsArray = Array.from(allDependencies);
|
|
702
|
+
const depSpinner = ora(
|
|
703
|
+
`Installing collective dependencies: ${depsArray.join(", ")}...`,
|
|
704
|
+
).start();
|
|
705
|
+
try {
|
|
706
|
+
installDependencies(depsArray, env.pkgCommand);
|
|
707
|
+
depSpinner.succeed(`Dependencies installed successfully.`);
|
|
708
|
+
} catch (err) {
|
|
709
|
+
depSpinner.fail(`Failed to install dependencies.`);
|
|
710
|
+
console.error(chalk.red(err.message));
|
|
273
711
|
}
|
|
274
|
-
|
|
275
|
-
console.log(chalk.green(`\n✅ ${componentName} added successfully!`));
|
|
276
|
-
} catch (err) {
|
|
277
|
-
spinner.fail(`Failed to add ${componentName}`);
|
|
278
|
-
console.error(chalk.red(err.message));
|
|
279
712
|
}
|
|
713
|
+
|
|
714
|
+
console.log(chalk.green(`\n✅ Finished adding components!`));
|
|
280
715
|
} else {
|
|
281
716
|
console.log(chalk.red(`❌ Unknown command: ${command}`));
|
|
282
717
|
}
|
package/package.json
CHANGED
|
@@ -1,19 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cooterlabs",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Component library CLI for CooterLabs apps",
|
|
5
5
|
"files": [
|
|
6
6
|
"bin",
|
|
7
7
|
"templates"
|
|
8
8
|
],
|
|
9
|
-
"main": "index.js",
|
|
10
9
|
"scripts": {
|
|
11
10
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
12
11
|
},
|
|
13
12
|
"bin": {
|
|
14
|
-
"cooterlabs": "
|
|
13
|
+
"cooterlabs": "bin/index.js"
|
|
15
14
|
},
|
|
16
|
-
"keywords": [
|
|
15
|
+
"keywords": [
|
|
16
|
+
"aeon",
|
|
17
|
+
"react",
|
|
18
|
+
"typescript",
|
|
19
|
+
"cli",
|
|
20
|
+
"component-library"
|
|
21
|
+
],
|
|
17
22
|
"author": "",
|
|
18
23
|
"license": "ISC",
|
|
19
24
|
"type": "module",
|
|
@@ -25,5 +30,8 @@
|
|
|
25
30
|
"prettier": "^3.8.1",
|
|
26
31
|
"prompts": "^2.4.2",
|
|
27
32
|
"tailwind-merge": "^3.5.0"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@types/react": "^19.2.14"
|
|
28
36
|
}
|
|
29
37
|
}
|