@srcroot/ui 0.0.1

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 (44) hide show
  1. package/README.md +151 -0
  2. package/dist/index.d.ts +2 -0
  3. package/dist/index.js +640 -0
  4. package/package.json +43 -0
  5. package/registry/accordion.tsx +158 -0
  6. package/registry/alert-dialog.tsx +206 -0
  7. package/registry/alert.tsx +73 -0
  8. package/registry/aspect-ratio.tsx +44 -0
  9. package/registry/avatar.tsx +94 -0
  10. package/registry/badge.tsx +68 -0
  11. package/registry/breadcrumb.tsx +151 -0
  12. package/registry/button-group.tsx +84 -0
  13. package/registry/button.tsx +102 -0
  14. package/registry/calendar.tsx +238 -0
  15. package/registry/card.tsx +114 -0
  16. package/registry/carousel.tsx +169 -0
  17. package/registry/checkbox.tsx +79 -0
  18. package/registry/collapsible.tsx +110 -0
  19. package/registry/container.tsx +60 -0
  20. package/registry/dialog.tsx +264 -0
  21. package/registry/dropdown-menu.tsx +387 -0
  22. package/registry/image.tsx +144 -0
  23. package/registry/input.tsx +44 -0
  24. package/registry/label.tsx +34 -0
  25. package/registry/loading-spinner.tsx +108 -0
  26. package/registry/otp-input.tsx +152 -0
  27. package/registry/pagination.tsx +146 -0
  28. package/registry/popover.tsx +135 -0
  29. package/registry/progress.tsx +49 -0
  30. package/registry/radio.tsx +99 -0
  31. package/registry/search.tsx +146 -0
  32. package/registry/select.tsx +190 -0
  33. package/registry/separator.tsx +44 -0
  34. package/registry/sheet.tsx +180 -0
  35. package/registry/skeleton.tsx +26 -0
  36. package/registry/slider.tsx +115 -0
  37. package/registry/star-rating.tsx +131 -0
  38. package/registry/switch.tsx +70 -0
  39. package/registry/table.tsx +136 -0
  40. package/registry/tabs.tsx +122 -0
  41. package/registry/text.tsx +70 -0
  42. package/registry/textarea.tsx +39 -0
  43. package/registry/toast.tsx +95 -0
  44. package/registry/tooltip.tsx +122 -0
package/dist/index.js ADDED
@@ -0,0 +1,640 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/cli/index.ts
4
+ import { Command } from "commander";
5
+
6
+ // src/cli/commands/init.ts
7
+ import fs from "fs-extra";
8
+ import path from "path";
9
+ import chalk from "chalk";
10
+ import ora from "ora";
11
+ import prompts from "prompts";
12
+ import { fileURLToPath } from "url";
13
+ var __dirname = path.dirname(fileURLToPath(import.meta.url));
14
+ var UTILS_CONTENT = `import { type ClassValue, clsx } from "clsx"
15
+ import { twMerge } from "tailwind-merge"
16
+
17
+ export function cn(...inputs: ClassValue[]) {
18
+ return twMerge(clsx(inputs))
19
+ }
20
+ `;
21
+ var CSS_VARIABLES = `@tailwind base;
22
+ @tailwind components;
23
+ @tailwind utilities;
24
+
25
+ @layer base {
26
+ :root {
27
+ --background: 0 0% 100%;
28
+ --foreground: 222.2 84% 4.9%;
29
+ --card: 0 0% 100%;
30
+ --card-foreground: 222.2 84% 4.9%;
31
+ --popover: 0 0% 100%;
32
+ --popover-foreground: 222.2 84% 4.9%;
33
+ --primary: 222.2 47.4% 11.2%;
34
+ --primary-foreground: 210 40% 98%;
35
+ --secondary: 210 40% 96.1%;
36
+ --secondary-foreground: 222.2 47.4% 11.2%;
37
+ --muted: 210 40% 96.1%;
38
+ --muted-foreground: 215.4 16.3% 46.9%;
39
+ --accent: 210 40% 96.1%;
40
+ --accent-foreground: 222.2 47.4% 11.2%;
41
+ --destructive: 0 84.2% 60.2%;
42
+ --destructive-foreground: 210 40% 98%;
43
+ --border: 214.3 31.8% 91.4%;
44
+ --input: 214.3 31.8% 91.4%;
45
+ --ring: 222.2 84% 4.9%;
46
+ --radius: 0.5rem;
47
+ }
48
+
49
+ .dark {
50
+ --background: 222.2 84% 4.9%;
51
+ --foreground: 210 40% 98%;
52
+ --card: 222.2 84% 4.9%;
53
+ --card-foreground: 210 40% 98%;
54
+ --popover: 222.2 84% 4.9%;
55
+ --popover-foreground: 210 40% 98%;
56
+ --primary: 210 40% 98%;
57
+ --primary-foreground: 222.2 47.4% 11.2%;
58
+ --secondary: 217.2 32.6% 17.5%;
59
+ --secondary-foreground: 210 40% 98%;
60
+ --muted: 217.2 32.6% 17.5%;
61
+ --muted-foreground: 215 20.2% 65.1%;
62
+ --accent: 217.2 32.6% 17.5%;
63
+ --accent-foreground: 210 40% 98%;
64
+ --destructive: 0 62.8% 30.6%;
65
+ --destructive-foreground: 210 40% 98%;
66
+ --border: 217.2 32.6% 17.5%;
67
+ --input: 217.2 32.6% 17.5%;
68
+ --ring: 212.7 26.8% 83.9%;
69
+ }
70
+ }
71
+
72
+ @layer base {
73
+ * {
74
+ @apply border-border;
75
+ }
76
+ body {
77
+ @apply bg-background text-foreground;
78
+ }
79
+ }
80
+ `;
81
+ var TAILWIND_CONFIG = `import type { Config } from "tailwindcss"
82
+
83
+ const config: Config = {
84
+ darkMode: ["class"],
85
+ content: [
86
+ "./src/**/*.{js,ts,jsx,tsx,mdx}",
87
+ "./app/**/*.{js,ts,jsx,tsx,mdx}",
88
+ "./components/**/*.{js,ts,jsx,tsx,mdx}",
89
+ ],
90
+ theme: {
91
+ extend: {
92
+ colors: {
93
+ border: "hsl(var(--border))",
94
+ input: "hsl(var(--input))",
95
+ ring: "hsl(var(--ring))",
96
+ background: "hsl(var(--background))",
97
+ foreground: "hsl(var(--foreground))",
98
+ primary: {
99
+ DEFAULT: "hsl(var(--primary))",
100
+ foreground: "hsl(var(--primary-foreground))",
101
+ },
102
+ secondary: {
103
+ DEFAULT: "hsl(var(--secondary))",
104
+ foreground: "hsl(var(--secondary-foreground))",
105
+ },
106
+ destructive: {
107
+ DEFAULT: "hsl(var(--destructive))",
108
+ foreground: "hsl(var(--destructive-foreground))",
109
+ },
110
+ muted: {
111
+ DEFAULT: "hsl(var(--muted))",
112
+ foreground: "hsl(var(--muted-foreground))",
113
+ },
114
+ accent: {
115
+ DEFAULT: "hsl(var(--accent))",
116
+ foreground: "hsl(var(--accent-foreground))",
117
+ },
118
+ popover: {
119
+ DEFAULT: "hsl(var(--popover))",
120
+ foreground: "hsl(var(--popover-foreground))",
121
+ },
122
+ card: {
123
+ DEFAULT: "hsl(var(--card))",
124
+ foreground: "hsl(var(--card-foreground))",
125
+ },
126
+ },
127
+ borderRadius: {
128
+ lg: "var(--radius)",
129
+ md: "calc(var(--radius) - 2px)",
130
+ sm: "calc(var(--radius) - 4px)",
131
+ },
132
+ },
133
+ },
134
+ plugins: [],
135
+ }
136
+
137
+ export default config
138
+ `;
139
+ async function init(options) {
140
+ const cwd = path.resolve(options.cwd);
141
+ console.log(chalk.cyan("\n\u{1F680} Initializing srcroot-ui...\n"));
142
+ const packageJsonPath = path.join(cwd, "package.json");
143
+ if (!fs.existsSync(packageJsonPath)) {
144
+ console.log(chalk.red("Error: No package.json found. Please run this in a project directory."));
145
+ process.exit(1);
146
+ }
147
+ const srcDir = path.join(cwd, "src");
148
+ const appDir = path.join(cwd, "app");
149
+ const isAppRouter = fs.existsSync(appDir);
150
+ const libDir = path.join(srcDir, "lib");
151
+ const componentsDir = path.join(srcDir, "components", "ui");
152
+ const globalsPath = isAppRouter ? path.join(appDir, "globals.css") : path.join(srcDir, "styles", "globals.css");
153
+ if (!options.yes) {
154
+ const response = await prompts([
155
+ {
156
+ type: "confirm",
157
+ name: "proceed",
158
+ message: `This will create files in ${chalk.cyan(cwd)}. Continue?`,
159
+ initial: true
160
+ }
161
+ ]);
162
+ if (!response.proceed) {
163
+ console.log(chalk.yellow("Cancelled."));
164
+ process.exit(0);
165
+ }
166
+ }
167
+ const spinner = ora("Creating project structure...").start();
168
+ try {
169
+ await fs.ensureDir(libDir);
170
+ await fs.ensureDir(componentsDir);
171
+ const utilsPath = path.join(libDir, "utils.ts");
172
+ await fs.writeFile(utilsPath, UTILS_CONTENT);
173
+ spinner.succeed(`Created ${chalk.cyan("src/lib/utils.ts")}`);
174
+ spinner.start("Setting up CSS variables...");
175
+ const stylesDir = path.dirname(globalsPath);
176
+ await fs.ensureDir(stylesDir);
177
+ if (fs.existsSync(globalsPath)) {
178
+ const existingCss = await fs.readFile(globalsPath, "utf-8");
179
+ if (!existingCss.includes("--background:")) {
180
+ await fs.writeFile(globalsPath, CSS_VARIABLES + "\n" + existingCss);
181
+ spinner.succeed(`Updated ${chalk.cyan(path.relative(cwd, globalsPath))}`);
182
+ } else {
183
+ spinner.info(`CSS variables already exist in ${chalk.cyan(path.relative(cwd, globalsPath))}`);
184
+ }
185
+ } else {
186
+ await fs.writeFile(globalsPath, CSS_VARIABLES);
187
+ spinner.succeed(`Created ${chalk.cyan(path.relative(cwd, globalsPath))}`);
188
+ }
189
+ spinner.start("Setting up Tailwind config...");
190
+ const tailwindConfigPath = path.join(cwd, "tailwind.config.ts");
191
+ if (!fs.existsSync(tailwindConfigPath)) {
192
+ await fs.writeFile(tailwindConfigPath, TAILWIND_CONFIG);
193
+ spinner.succeed(`Created ${chalk.cyan("tailwind.config.ts")}`);
194
+ } else {
195
+ spinner.info(`${chalk.cyan("tailwind.config.ts")} already exists, skipping`);
196
+ }
197
+ spinner.start("Checking dependencies...");
198
+ const pkg = await fs.readJson(packageJsonPath);
199
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
200
+ const missing = [];
201
+ const requiredDeps = ["clsx", "tailwind-merge", "class-variance-authority", "lucide-react"];
202
+ for (const dep of requiredDeps) {
203
+ if (!deps[dep]) {
204
+ missing.push(dep);
205
+ }
206
+ }
207
+ if (missing.length > 0) {
208
+ spinner.stop();
209
+ const { install } = await prompts({
210
+ type: "confirm",
211
+ name: "install",
212
+ message: `Missing dependencies: ${missing.join(", ")}. Install now?`,
213
+ initial: true
214
+ });
215
+ if (install) {
216
+ spinner.start(`Installing dependencies...`);
217
+ import("child_process").then(({ execSync }) => {
218
+ execSync(`npm install ${missing.join(" ")}`, { stdio: "ignore", cwd });
219
+ spinner.succeed("Dependencies installed");
220
+ }).catch(() => {
221
+ spinner.fail("Failed to install dependencies automatically.");
222
+ console.log(chalk.dim(`
223
+ Please manually run: npm install ${missing.join(" ")}
224
+ `));
225
+ });
226
+ } else {
227
+ console.log(chalk.dim(`
228
+ Please manually run: npm install ${missing.join(" ")}
229
+ `));
230
+ }
231
+ } else {
232
+ spinner.succeed("All dependencies installed");
233
+ }
234
+ console.log(chalk.green("\n\u2705 Project initialized successfully!\n"));
235
+ console.log("Next steps:");
236
+ console.log(chalk.dim(" 1. Install missing dependencies (if any)"));
237
+ console.log(chalk.dim(" 2. Run: npx srcroot-ui add button"));
238
+ console.log();
239
+ } catch (error) {
240
+ spinner.fail("Failed to initialize project");
241
+ console.error(error);
242
+ process.exit(1);
243
+ }
244
+ }
245
+
246
+ // src/cli/commands/add.ts
247
+ import fs2 from "fs-extra";
248
+ import path2 from "path";
249
+ import chalk2 from "chalk";
250
+ import ora2 from "ora";
251
+ import prompts2 from "prompts";
252
+ import { fileURLToPath as fileURLToPath2 } from "url";
253
+
254
+ // src/cli/registry.ts
255
+ var REGISTRY = {
256
+ // Core
257
+ button: {
258
+ file: "button.tsx",
259
+ description: "Polymorphic button with variants",
260
+ category: "Core",
261
+ dependencies: []
262
+ },
263
+ badge: {
264
+ file: "badge.tsx",
265
+ description: "Status indicator",
266
+ category: "Core",
267
+ dependencies: []
268
+ },
269
+ avatar: {
270
+ file: "avatar.tsx",
271
+ description: "User avatar with fallback",
272
+ category: "Core",
273
+ dependencies: []
274
+ },
275
+ separator: {
276
+ file: "separator.tsx",
277
+ description: "Visual divider",
278
+ category: "Core",
279
+ dependencies: []
280
+ },
281
+ // Typography
282
+ text: {
283
+ file: "text.tsx",
284
+ description: "Polymorphic typography",
285
+ category: "Typography",
286
+ dependencies: []
287
+ },
288
+ label: {
289
+ file: "label.tsx",
290
+ description: "Form label",
291
+ category: "Typography",
292
+ dependencies: []
293
+ },
294
+ // Forms
295
+ input: {
296
+ file: "input.tsx",
297
+ description: "Text input field",
298
+ category: "Forms",
299
+ dependencies: []
300
+ },
301
+ textarea: {
302
+ file: "textarea.tsx",
303
+ description: "Multi-line text input",
304
+ category: "Forms",
305
+ dependencies: []
306
+ },
307
+ checkbox: {
308
+ file: "checkbox.tsx",
309
+ description: "Checkbox input",
310
+ category: "Forms",
311
+ dependencies: []
312
+ },
313
+ radio: {
314
+ file: "radio.tsx",
315
+ description: "Radio button group",
316
+ category: "Forms",
317
+ dependencies: []
318
+ },
319
+ switch: {
320
+ file: "switch.tsx",
321
+ description: "Toggle switch",
322
+ category: "Forms",
323
+ dependencies: []
324
+ },
325
+ slider: {
326
+ file: "slider.tsx",
327
+ description: "Range slider",
328
+ category: "Forms",
329
+ dependencies: []
330
+ },
331
+ select: {
332
+ file: "select.tsx",
333
+ description: "Custom select dropdown",
334
+ category: "Forms",
335
+ dependencies: []
336
+ },
337
+ // Layout
338
+ card: {
339
+ file: "card.tsx",
340
+ description: "Card container",
341
+ category: "Layout",
342
+ dependencies: []
343
+ },
344
+ container: {
345
+ file: "container.tsx",
346
+ description: "Max-width container",
347
+ category: "Layout",
348
+ dependencies: []
349
+ },
350
+ "aspect-ratio": {
351
+ file: "aspect-ratio.tsx",
352
+ description: "Maintain aspect ratio",
353
+ category: "Layout",
354
+ dependencies: []
355
+ },
356
+ // Data Display
357
+ accordion: {
358
+ file: "accordion.tsx",
359
+ description: "Expandable sections",
360
+ category: "Data Display",
361
+ dependencies: []
362
+ },
363
+ tabs: {
364
+ file: "tabs.tsx",
365
+ description: "Tab navigation",
366
+ category: "Data Display",
367
+ dependencies: []
368
+ },
369
+ table: {
370
+ file: "table.tsx",
371
+ description: "Data table",
372
+ category: "Data Display",
373
+ dependencies: []
374
+ },
375
+ progress: {
376
+ file: "progress.tsx",
377
+ description: "Progress indicator",
378
+ category: "Data Display",
379
+ dependencies: []
380
+ },
381
+ skeleton: {
382
+ file: "skeleton.tsx",
383
+ description: "Loading placeholder",
384
+ category: "Data Display",
385
+ dependencies: []
386
+ },
387
+ // Overlay / Feedback
388
+ dialog: {
389
+ file: "dialog.tsx",
390
+ description: "Modal dialog",
391
+ category: "Overlay / Feedback",
392
+ dependencies: []
393
+ },
394
+ "alert-dialog": {
395
+ file: "alert-dialog.tsx",
396
+ description: "Confirmation dialog",
397
+ category: "Overlay / Feedback",
398
+ dependencies: ["dialog"]
399
+ },
400
+ sheet: {
401
+ file: "sheet.tsx",
402
+ description: "Slide-in panel",
403
+ category: "Overlay / Feedback",
404
+ dependencies: []
405
+ },
406
+ popover: {
407
+ file: "popover.tsx",
408
+ description: "Floating content",
409
+ category: "Overlay / Feedback",
410
+ dependencies: []
411
+ },
412
+ tooltip: {
413
+ file: "tooltip.tsx",
414
+ description: "Hover tooltip",
415
+ category: "Overlay / Feedback",
416
+ dependencies: []
417
+ },
418
+ "dropdown-menu": {
419
+ file: "dropdown-menu.tsx",
420
+ description: "Action dropdown",
421
+ category: "Overlay / Feedback",
422
+ dependencies: []
423
+ },
424
+ toast: {
425
+ file: "toast.tsx",
426
+ description: "Notification toast",
427
+ category: "Overlay / Feedback",
428
+ dependencies: []
429
+ },
430
+ alert: {
431
+ file: "alert.tsx",
432
+ description: "Inline alert",
433
+ category: "Overlay / Feedback",
434
+ dependencies: []
435
+ },
436
+ // Navigation
437
+ breadcrumb: {
438
+ file: "breadcrumb.tsx",
439
+ description: "Breadcrumb navigation",
440
+ category: "Navigation",
441
+ dependencies: []
442
+ },
443
+ pagination: {
444
+ file: "pagination.tsx",
445
+ description: "Page navigation",
446
+ category: "Navigation",
447
+ dependencies: ["button"]
448
+ },
449
+ // New Components
450
+ "loading-spinner": {
451
+ file: "loading-spinner.tsx",
452
+ description: "Loading spinner with variants",
453
+ category: "Feedback",
454
+ dependencies: []
455
+ },
456
+ image: {
457
+ file: "image.tsx",
458
+ description: "Enhanced image with loading",
459
+ category: "Data Display",
460
+ dependencies: []
461
+ },
462
+ "button-group": {
463
+ file: "button-group.tsx",
464
+ description: "Group buttons together",
465
+ category: "Core",
466
+ dependencies: ["button"]
467
+ },
468
+ "otp-input": {
469
+ file: "otp-input.tsx",
470
+ description: "OTP verification input",
471
+ category: "Forms",
472
+ dependencies: []
473
+ },
474
+ search: {
475
+ file: "search.tsx",
476
+ description: "Search input with debounce",
477
+ category: "Forms",
478
+ dependencies: []
479
+ },
480
+ "star-rating": {
481
+ file: "star-rating.tsx",
482
+ description: "Star rating input",
483
+ category: "Feedback",
484
+ dependencies: []
485
+ },
486
+ collapsible: {
487
+ file: "collapsible.tsx",
488
+ description: "Expandable section",
489
+ category: "Data Display",
490
+ dependencies: []
491
+ },
492
+ carousel: {
493
+ file: "carousel.tsx",
494
+ description: "Image/content slider",
495
+ category: "Data Display",
496
+ dependencies: []
497
+ },
498
+ calendar: {
499
+ file: "calendar.tsx",
500
+ description: "Date picker",
501
+ category: "Forms",
502
+ dependencies: []
503
+ }
504
+ };
505
+
506
+ // src/cli/commands/add.ts
507
+ var __dirname3 = path2.dirname(fileURLToPath2(import.meta.url));
508
+ async function add(components, options) {
509
+ const cwd = path2.resolve(options.cwd);
510
+ if (options.all) {
511
+ components = Object.keys(REGISTRY);
512
+ }
513
+ if (components.length === 0) {
514
+ const { items } = await prompts2({
515
+ type: "multiselect",
516
+ name: "items",
517
+ message: "Which components would you like to add?",
518
+ hint: "Space to select. A to toggle all. Enter to submit.",
519
+ choices: Object.keys(REGISTRY).map((name) => ({
520
+ title: name,
521
+ value: name
522
+ }))
523
+ });
524
+ if (!items || items.length === 0) {
525
+ console.log(chalk2.yellow("No components selected."));
526
+ process.exit(0);
527
+ }
528
+ components = items;
529
+ }
530
+ const validComponents = [];
531
+ const invalidComponents = [];
532
+ for (const comp of components) {
533
+ if (comp in REGISTRY) {
534
+ validComponents.push(comp);
535
+ } else {
536
+ invalidComponents.push(comp);
537
+ }
538
+ }
539
+ if (invalidComponents.length > 0) {
540
+ console.log(chalk2.red(`Unknown components: ${invalidComponents.join(", ")}`));
541
+ console.log(chalk2.dim("\nRun 'srcroot-ui list' to see available components."));
542
+ process.exit(1);
543
+ }
544
+ const toInstall = /* @__PURE__ */ new Set();
545
+ function resolveDeps(name) {
546
+ if (toInstall.has(name)) return;
547
+ toInstall.add(name);
548
+ const comp = REGISTRY[name];
549
+ if (comp.dependencies) {
550
+ for (const dep of comp.dependencies) {
551
+ resolveDeps(dep);
552
+ }
553
+ }
554
+ }
555
+ for (const comp of validComponents) {
556
+ resolveDeps(comp);
557
+ }
558
+ const componentsToAdd = Array.from(toInstall);
559
+ console.log(chalk2.cyan("\n\u{1F4E6} Adding components:\n"));
560
+ componentsToAdd.forEach((name) => {
561
+ console.log(chalk2.dim(` - ${name}`));
562
+ });
563
+ console.log();
564
+ if (!options.yes) {
565
+ const response = await prompts2({
566
+ type: "confirm",
567
+ name: "proceed",
568
+ message: "Continue?",
569
+ initial: true
570
+ });
571
+ if (!response.proceed) {
572
+ console.log(chalk2.yellow("Cancelled."));
573
+ process.exit(0);
574
+ }
575
+ }
576
+ const spinner = ora2("Adding components...").start();
577
+ const componentsDir = path2.join(cwd, "src", "components", "ui");
578
+ try {
579
+ await fs2.ensureDir(componentsDir);
580
+ for (const name of componentsToAdd) {
581
+ const comp = REGISTRY[name];
582
+ const targetPath = path2.join(componentsDir, comp.file);
583
+ if (fs2.existsSync(targetPath) && !options.overwrite) {
584
+ spinner.info(`${chalk2.cyan(comp.file)} already exists, skipping (use --overwrite to replace)`);
585
+ continue;
586
+ }
587
+ const registryPath = path2.resolve(__dirname3, "..", "..", "..", "registry", comp.file);
588
+ if (!fs2.existsSync(registryPath)) {
589
+ spinner.warn(`Registry file not found for ${name}: ${registryPath}`);
590
+ continue;
591
+ }
592
+ const content = await fs2.readFile(registryPath, "utf-8");
593
+ await fs2.writeFile(targetPath, content);
594
+ spinner.succeed(`Added ${chalk2.cyan(comp.file)}`);
595
+ }
596
+ console.log(chalk2.green("\n\u2705 Components added successfully!\n"));
597
+ } catch (error) {
598
+ spinner.fail("Failed to add components");
599
+ console.error(error);
600
+ process.exit(1);
601
+ }
602
+ }
603
+
604
+ // src/cli/commands/list.ts
605
+ import chalk3 from "chalk";
606
+ async function list() {
607
+ console.log(chalk3.cyan("\n\u{1F4E6} Available components:\n"));
608
+ const categories = {
609
+ "Core": [],
610
+ "Typography": [],
611
+ "Forms": [],
612
+ "Layout": [],
613
+ "Data Display": [],
614
+ "Overlay / Feedback": [],
615
+ "Navigation": []
616
+ };
617
+ for (const [name, comp] of Object.entries(REGISTRY)) {
618
+ if (categories[comp.category]) {
619
+ categories[comp.category].push(name);
620
+ }
621
+ }
622
+ for (const [category, components] of Object.entries(categories)) {
623
+ if (components.length === 0) continue;
624
+ console.log(chalk3.bold.white(` ${category}`));
625
+ components.forEach((name) => {
626
+ const comp = REGISTRY[name];
627
+ console.log(chalk3.dim(` - ${name}`) + chalk3.gray(` (${comp.description})`));
628
+ });
629
+ console.log();
630
+ }
631
+ console.log(chalk3.dim("Usage: npx srcroot-ui add <component>\n"));
632
+ }
633
+
634
+ // src/cli/index.ts
635
+ var program = new Command();
636
+ program.name("@srcroot/ui").description("Add polymorphic, accessible UI components to your project").version("0.0.1");
637
+ program.command("init").description("Initialize your project with srcroot-ui").option("-y, --yes", "Skip confirmation prompts", false).option("--cwd <path>", "Working directory", process.cwd()).action(init);
638
+ program.command("add").description("Add components to your project").argument("[components...]", "Components to add").option("-y, --yes", "Skip confirmation prompts", false).option("-o, --overwrite", "Overwrite existing files", false).option("-a, --all", "Add all available components", false).option("--cwd <path>", "Working directory", process.cwd()).action(add);
639
+ program.command("list").description("List all available components").action(list);
640
+ program.parse();
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@srcroot/ui",
3
+ "version": "0.0.1",
4
+ "description": "A shadcn-style CLI UI library with polymorphic, accessible React components",
5
+ "type": "module",
6
+ "bin": {
7
+ "srcroot-ui": "./dist/cli/index.js"
8
+ },
9
+ "scripts": {
10
+ "build": "tsup",
11
+ "dev": "tsup --watch"
12
+ },
13
+ "files": [
14
+ "dist",
15
+ "registry"
16
+ ],
17
+ "keywords": [
18
+ "react",
19
+ "ui",
20
+ "components",
21
+ "tailwind",
22
+ "polymorphic",
23
+ "accessible"
24
+ ],
25
+ "dependencies": {
26
+ "commander": "^12.1.0",
27
+ "chalk": "^5.3.0",
28
+ "fs-extra": "^11.2.0",
29
+ "ora": "^8.1.1",
30
+ "prompts": "^2.4.2"
31
+ },
32
+ "devDependencies": {
33
+ "@types/fs-extra": "^11.0.4",
34
+ "@types/node": "^22.10.1",
35
+ "@types/prompts": "^2.4.9",
36
+ "tsup": "^8.3.5",
37
+ "typescript": "^5.7.2"
38
+ },
39
+ "peerDependencies": {
40
+ "react": "^18.0.0 || ^19.0.0",
41
+ "react-dom": "^18.0.0 || ^19.0.0"
42
+ }
43
+ }