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.
- package/dist/index.js +146 -34
- 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.
|
|
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
|
|
362
|
-
.argument("[
|
|
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 (
|
|
425
|
+
.action(async (components, opts) => {
|
|
367
426
|
const dir = getComponentsDir(opts.dir);
|
|
368
|
-
// ── interactive picker when no
|
|
369
|
-
if (!
|
|
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 ${
|
|
496
|
+
console.log(chalk.bold(` ${chalk.hex("#E84E2D")("Praxys UI")} — add ${label}`));
|
|
437
497
|
console.log("");
|
|
438
498
|
// ── add all ──────────────────────────────────────────
|
|
439
|
-
if (
|
|
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
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
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
|
-
// ──
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
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
|
-
|
|
479
|
-
if (
|
|
480
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
1061
|
+
program.parseAsync().then(() => checkForUpdates());
|