praxys-ui 1.3.1 → 1.3.3

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 (2) hide show
  1. package/dist/index.js +146 -34
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import prompts from "prompts";
6
6
  import { existsSync, mkdirSync, writeFileSync, readFileSync, unlinkSync } from "fs";
7
7
  import { join } from "path";
8
8
  import { execSync } from "child_process";
9
- const VERSION = "1.3.1";
9
+ const VERSION = "1.3.3";
10
10
  // ─── Utility file contents ──────────────────────────────
11
11
  const UTILS_CONTENT = `import { clsx, type ClassValue } from "clsx";
12
12
  import { twMerge } from "tailwind-merge";
@@ -163,7 +163,6 @@ function getComponentsDir(optDir) {
163
163
  function fuzzyMatch(query, text) {
164
164
  const lower = text.toLowerCase();
165
165
  const q = query.toLowerCase();
166
- // simple substring match on slug, title, or description
167
166
  return lower.includes(q);
168
167
  }
169
168
  function truncate(str, max) {
@@ -171,6 +170,65 @@ function truncate(str, max) {
171
170
  return str;
172
171
  return str.slice(0, max - 1) + "…";
173
172
  }
173
+ function levenshtein(a, b) {
174
+ const m = a.length, n = b.length;
175
+ const dp = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));
176
+ for (let i = 0; i <= m; i++)
177
+ dp[i][0] = i;
178
+ for (let j = 0; j <= n; j++)
179
+ dp[0][j] = j;
180
+ for (let i = 1; i <= m; i++) {
181
+ for (let j = 1; j <= n; j++) {
182
+ dp[i][j] = a[i - 1] === b[j - 1]
183
+ ? dp[i - 1][j - 1]
184
+ : 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
185
+ }
186
+ }
187
+ return dp[m][n];
188
+ }
189
+ function didYouMean(input) {
190
+ let best = "";
191
+ let bestDist = Infinity;
192
+ for (const slug of COMPONENT_LIST) {
193
+ const dist = levenshtein(input.toLowerCase(), slug.toLowerCase());
194
+ if (dist < bestDist) {
195
+ bestDist = dist;
196
+ best = slug;
197
+ }
198
+ }
199
+ // Only suggest if distance is reasonable (less than ~40% of input length)
200
+ if (bestDist <= Math.max(2, Math.floor(input.length * 0.4))) {
201
+ return best;
202
+ }
203
+ return null;
204
+ }
205
+ function printNotFound(slug) {
206
+ const suggestion = didYouMean(slug);
207
+ if (suggestion) {
208
+ console.log(chalk.red(` Component "${slug}" not found.`) + chalk.dim(` Did you mean ${chalk.bold(suggestion)}?`));
209
+ }
210
+ else {
211
+ console.log(chalk.red(` Component "${slug}" not found.`));
212
+ }
213
+ }
214
+ async function checkForUpdates() {
215
+ try {
216
+ const res = await fetch("https://registry.npmjs.org/praxys-ui/latest", {
217
+ signal: AbortSignal.timeout(3000),
218
+ });
219
+ if (!res.ok)
220
+ return;
221
+ const data = await res.json();
222
+ const latest = data.version;
223
+ if (latest && latest !== VERSION) {
224
+ console.log(chalk.dim(` Update available: ${VERSION} → ${chalk.bold(latest)} Run ${chalk.cyan("npm i -g praxys-ui")} to update.`));
225
+ console.log("");
226
+ }
227
+ }
228
+ catch {
229
+ // Silently ignore — no network, timeout, etc.
230
+ }
231
+ }
174
232
  function colorizeSource(source) {
175
233
  const lines = source.split("\n");
176
234
  return lines
@@ -222,6 +280,7 @@ program
222
280
  // ── init ─────────────────────────────────────────────────
223
281
  program
224
282
  .command("init")
283
+ .alias("i")
225
284
  .description("Initialize Praxys UI in your project")
226
285
  .action(async () => {
227
286
  console.log("");
@@ -358,15 +417,15 @@ async function installDepsForComponents(slugs) {
358
417
  }
359
418
  program
360
419
  .command("add")
361
- .description("Add a component (or all components) to your project")
362
- .argument("[component]", 'Component slug (e.g. animated-button) or "all". Omit for interactive picker.')
420
+ .description("Add one or more components to your project")
421
+ .argument("[components...]", 'Component slugs (e.g. animated-button alert) or "all". Omit for interactive picker.')
363
422
  .option("-d, --dir <directory>", "Component directory")
364
423
  .option("-y, --yes", "Skip overwrite prompts (skip existing files)", false)
365
424
  .option("--install-deps", "Install component dependencies after adding", false)
366
- .action(async (component, opts) => {
425
+ .action(async (components, opts) => {
367
426
  const dir = getComponentsDir(opts.dir);
368
- // ── interactive picker when no arg ───────────────────
369
- if (!component) {
427
+ // ── interactive picker when no args ──────────────────
428
+ if (!components || components.length === 0) {
370
429
  console.log("");
371
430
  console.log(chalk.bold(` ${chalk.hex("#E84E2D")("Praxys UI")} — add components`));
372
431
  console.log("");
@@ -432,11 +491,12 @@ program
432
491
  console.log("");
433
492
  return;
434
493
  }
494
+ const label = components.length === 1 ? chalk.cyan(components[0]) : chalk.cyan(`${components.length} components`);
435
495
  console.log("");
436
- console.log(chalk.bold(` ${chalk.hex("#E84E2D")("Praxys UI")} — add ${chalk.cyan(component)}`));
496
+ console.log(chalk.bold(` ${chalk.hex("#E84E2D")("Praxys UI")} — add ${label}`));
437
497
  console.log("");
438
498
  // ── add all ──────────────────────────────────────────
439
- if (component === "all") {
499
+ if (components.includes("all")) {
440
500
  const { confirm } = await prompts({
441
501
  type: "confirm",
442
502
  name: "confirm",
@@ -447,14 +507,19 @@ program
447
507
  console.log(chalk.yellow(" Cancelled."));
448
508
  return;
449
509
  }
510
+ // Parallel fetch in batches of 6
450
511
  let added = 0;
451
512
  let failed = 0;
452
- for (const slug of COMPONENT_LIST) {
453
- const ok = await addSingleComponent(slug, dir, opts.yes);
454
- if (ok)
455
- added++;
456
- else
457
- failed++;
513
+ const batchSize = 6;
514
+ for (let i = 0; i < COMPONENT_LIST.length; i += batchSize) {
515
+ const batch = COMPONENT_LIST.slice(i, i + batchSize);
516
+ const results = await Promise.allSettled(batch.map((slug) => addSingleComponent(slug, dir, opts.yes)));
517
+ for (const r of results) {
518
+ if (r.status === "fulfilled" && r.value)
519
+ added++;
520
+ else
521
+ failed++;
522
+ }
458
523
  }
459
524
  console.log("");
460
525
  console.log(chalk.green(` ✓ ${added} components added`) +
@@ -465,31 +530,67 @@ program
465
530
  console.log("");
466
531
  return;
467
532
  }
468
- // ── add single ───────────────────────────────────────
469
- if (!COMPONENT_LIST.includes(component)) {
470
- console.log(chalk.red(` Component "${component}" not found.`));
471
- console.log("");
472
- console.log(chalk.dim(" Available components:"));
473
- COMPONENT_LIST.forEach((c) => console.log(chalk.dim(` - ${c}`)));
474
- console.log(chalk.dim(` - ${chalk.bold("all")} (adds every component)`));
533
+ // ── validate all slugs first ─────────────────────────
534
+ const invalidSlugs = components.filter((c) => !COMPONENT_LIST.includes(c));
535
+ if (invalidSlugs.length > 0) {
536
+ for (const bad of invalidSlugs) {
537
+ const suggestion = didYouMean(bad);
538
+ if (suggestion) {
539
+ console.log(chalk.red(` Component "${bad}" not found.`) + chalk.dim(` Did you mean ${chalk.bold(suggestion)}?`));
540
+ }
541
+ else {
542
+ console.log(chalk.red(` Component "${bad}" not found.`));
543
+ }
544
+ }
545
+ console.log(chalk.dim(`\n Run ${chalk.bold("praxys-ui list")} to see available components.`));
475
546
  console.log("");
476
547
  return;
477
548
  }
478
- const ok = await addSingleComponent(component, dir, false);
479
- if (ok && opts.installDeps) {
480
- await installDepsForComponents([component]);
549
+ // ── add single or multiple ───────────────────────────
550
+ if (components.length === 1) {
551
+ const slug = components[0];
552
+ const ok = await addSingleComponent(slug, dir, false);
553
+ if (ok && opts.installDeps) {
554
+ await installDepsForComponents([slug]);
555
+ }
556
+ console.log("");
557
+ console.log(chalk.dim(` Import: ${chalk.bold(`import ${toPascalCase(slug)} from '@/${dir}/${slug}'`)}`));
558
+ console.log("");
559
+ }
560
+ else {
561
+ // Parallel fetch in batches of 6
562
+ let added = 0;
563
+ let failed = 0;
564
+ const batchSize = 6;
565
+ for (let i = 0; i < components.length; i += batchSize) {
566
+ const batch = components.slice(i, i + batchSize);
567
+ const results = await Promise.allSettled(batch.map((slug) => addSingleComponent(slug, dir, opts.yes)));
568
+ for (const r of results) {
569
+ if (r.status === "fulfilled" && r.value)
570
+ added++;
571
+ else
572
+ failed++;
573
+ }
574
+ }
575
+ console.log("");
576
+ console.log(chalk.green(` ✓ ${added} components added`) +
577
+ (failed > 0 ? chalk.red(`, ${failed} failed`) : ""));
578
+ if (opts.installDeps) {
579
+ await installDepsForComponents(components);
580
+ }
581
+ console.log("");
481
582
  }
482
- console.log("");
483
- console.log(chalk.dim(` Import: ${chalk.bold(`import ${toPascalCase(component)} from '@/${dir}/${component}'`)}`));
484
- console.log("");
485
583
  });
486
584
  // ── list ─────────────────────────────────────────────────
487
585
  program
488
586
  .command("list")
587
+ .alias("ls")
489
588
  .description("List all available components")
490
589
  .option("-c, --category <category>", "Filter by category (buttons, cards, text, navigation, visual, media)")
491
590
  .option("-n, --new", "Show only new components", false)
492
591
  .option("-s, --search <query>", "Search components by name or description")
592
+ .option("--installed", "Show only locally installed components", false)
593
+ .option("-d, --dir <directory>", "Component directory (used with --installed)")
493
594
  .action((opts) => {
494
595
  console.log("");
495
596
  console.log(chalk.bold(` ${chalk.hex("#E84E2D")("Praxys UI")} — components`));
@@ -506,6 +607,16 @@ program
506
607
  return;
507
608
  }
508
609
  }
610
+ // Filter by installed
611
+ if (opts.installed) {
612
+ const compPath = join(process.cwd(), getComponentsDir(opts.dir));
613
+ entries = entries.filter(([slug]) => existsSync(join(compPath, `${slug}.tsx`)));
614
+ if (entries.length === 0) {
615
+ console.log(chalk.yellow(` No installed components found.`));
616
+ console.log("");
617
+ return;
618
+ }
619
+ }
509
620
  // Filter by new
510
621
  if (opts.new) {
511
622
  entries = entries.filter(([, meta]) => meta.isNew);
@@ -558,7 +669,7 @@ program
558
669
  const meta = COMPONENT_REGISTRY[component];
559
670
  if (!meta) {
560
671
  console.log("");
561
- console.log(chalk.red(` Component "${component}" not found.`));
672
+ printNotFound(component);
562
673
  console.log(chalk.dim(` Run ${chalk.bold("praxys-ui list")} to see available components.`));
563
674
  console.log("");
564
675
  return;
@@ -580,7 +691,7 @@ program
580
691
  .action(async (component) => {
581
692
  if (!COMPONENT_REGISTRY[component]) {
582
693
  console.log("");
583
- console.log(chalk.red(` Component "${component}" not found.`));
694
+ printNotFound(component);
584
695
  console.log(chalk.dim(` Run ${chalk.bold("praxys-ui list")} to see available components.`));
585
696
  console.log("");
586
697
  return;
@@ -611,7 +722,7 @@ program
611
722
  const dir = getComponentsDir(opts.dir);
612
723
  if (!COMPONENT_REGISTRY[component]) {
613
724
  console.log("");
614
- console.log(chalk.red(` Component "${component}" not found.`));
725
+ printNotFound(component);
615
726
  console.log("");
616
727
  return;
617
728
  }
@@ -666,6 +777,7 @@ program
666
777
  // ── remove <component> ───────────────────────────────────
667
778
  program
668
779
  .command("remove")
780
+ .alias("rm")
669
781
  .description("Remove a component from your project")
670
782
  .argument("<component>", "Component slug")
671
783
  .option("-d, --dir <directory>", "Component directory")
@@ -674,7 +786,7 @@ program
674
786
  const dir = getComponentsDir(opts.dir);
675
787
  if (!COMPONENT_REGISTRY[component]) {
676
788
  console.log("");
677
- console.log(chalk.red(` Component "${component}" not found.`));
789
+ printNotFound(component);
678
790
  console.log("");
679
791
  return;
680
792
  }
@@ -727,7 +839,7 @@ program
727
839
  let slugsToCheck;
728
840
  if (component) {
729
841
  if (!COMPONENT_REGISTRY[component]) {
730
- console.log(chalk.red(` Component "${component}" not found.`));
842
+ printNotFound(component);
731
843
  console.log("");
732
844
  return;
733
845
  }
@@ -946,4 +1058,4 @@ program
946
1058
  console.log("");
947
1059
  });
948
1060
  // ── run ──────────────────────────────────────────────────
949
- program.parse();
1061
+ program.parseAsync().then(() => checkForUpdates());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "praxys-ui",
3
- "version": "1.3.1",
3
+ "version": "1.3.3",
4
4
  "type": "module",
5
5
  "description": "CLI for scaffolding Praxys UI components into your project",
6
6
  "bin": {