ht-skills 0.2.0 → 0.2.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.
- package/lib/cli.js +109 -35
- package/package.json +1 -1
package/lib/cli.js
CHANGED
|
@@ -431,16 +431,89 @@ function normalizeInlineText(value) {
|
|
|
431
431
|
return String(value || "").replace(/\s+/g, " ").trim();
|
|
432
432
|
}
|
|
433
433
|
|
|
434
|
+
const ANSI_PATTERN = /\u001B\[[0-?]*[ -/]*[@-~]/g;
|
|
435
|
+
|
|
436
|
+
function stripAnsi(value) {
|
|
437
|
+
return String(value || "").replace(ANSI_PATTERN, "");
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
function isFullwidthCodePoint(codePoint) {
|
|
441
|
+
if (!Number.isFinite(codePoint)) {
|
|
442
|
+
return false;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
return codePoint >= 0x1100 && (
|
|
446
|
+
codePoint <= 0x115f ||
|
|
447
|
+
codePoint === 0x2329 ||
|
|
448
|
+
codePoint === 0x232a ||
|
|
449
|
+
(codePoint >= 0x2e80 && codePoint <= 0xa4cf && codePoint !== 0x303f) ||
|
|
450
|
+
(codePoint >= 0xac00 && codePoint <= 0xd7a3) ||
|
|
451
|
+
(codePoint >= 0xf900 && codePoint <= 0xfaff) ||
|
|
452
|
+
(codePoint >= 0xfe10 && codePoint <= 0xfe19) ||
|
|
453
|
+
(codePoint >= 0xfe30 && codePoint <= 0xfe6f) ||
|
|
454
|
+
(codePoint >= 0xff00 && codePoint <= 0xff60) ||
|
|
455
|
+
(codePoint >= 0xffe0 && codePoint <= 0xffe6) ||
|
|
456
|
+
(codePoint >= 0x1f300 && codePoint <= 0x1f64f) ||
|
|
457
|
+
(codePoint >= 0x1f900 && codePoint <= 0x1f9ff) ||
|
|
458
|
+
(codePoint >= 0x20000 && codePoint <= 0x3fffd)
|
|
459
|
+
);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
function getCharacterWidth(character) {
|
|
463
|
+
const codePoint = character.codePointAt(0);
|
|
464
|
+
if (!Number.isFinite(codePoint)) {
|
|
465
|
+
return 0;
|
|
466
|
+
}
|
|
467
|
+
if (
|
|
468
|
+
codePoint === 0 ||
|
|
469
|
+
(codePoint >= 0x0000 && codePoint <= 0x001f) ||
|
|
470
|
+
(codePoint >= 0x007f && codePoint <= 0x009f)
|
|
471
|
+
) {
|
|
472
|
+
return 0;
|
|
473
|
+
}
|
|
474
|
+
return isFullwidthCodePoint(codePoint) ? 2 : 1;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
function getDisplayWidth(value) {
|
|
478
|
+
let width = 0;
|
|
479
|
+
for (const character of stripAnsi(value)) {
|
|
480
|
+
width += getCharacterWidth(character);
|
|
481
|
+
}
|
|
482
|
+
return width;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
function sliceTextByDisplayWidth(value, maxWidth) {
|
|
486
|
+
const safeWidth = Math.max(1, Number(maxWidth || 0));
|
|
487
|
+
const characters = [...String(value || "")];
|
|
488
|
+
let width = 0;
|
|
489
|
+
let index = 0;
|
|
490
|
+
|
|
491
|
+
while (index < characters.length) {
|
|
492
|
+
const characterWidth = getCharacterWidth(characters[index]);
|
|
493
|
+
if (width + characterWidth > safeWidth) {
|
|
494
|
+
break;
|
|
495
|
+
}
|
|
496
|
+
width += characterWidth;
|
|
497
|
+
index += 1;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
return {
|
|
501
|
+
slice: characters.slice(0, index).join(""),
|
|
502
|
+
rest: characters.slice(index).join(""),
|
|
503
|
+
width,
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
|
|
434
507
|
function truncateText(value, maxLength) {
|
|
435
508
|
const text = normalizeInlineText(value);
|
|
436
509
|
const safeMaxLength = Math.max(1, Number(maxLength || 0));
|
|
437
|
-
if (text
|
|
510
|
+
if (getDisplayWidth(text) <= safeMaxLength) {
|
|
438
511
|
return text;
|
|
439
512
|
}
|
|
440
513
|
if (safeMaxLength <= 1) {
|
|
441
514
|
return "…";
|
|
442
515
|
}
|
|
443
|
-
return `${text
|
|
516
|
+
return `${sliceTextByDisplayWidth(text, safeMaxLength - 1).slice.trimEnd()}…`;
|
|
444
517
|
}
|
|
445
518
|
|
|
446
519
|
function wrapText(value, maxWidth) {
|
|
@@ -448,37 +521,37 @@ function wrapText(value, maxWidth) {
|
|
|
448
521
|
const safeWidth = Math.max(1, Number(maxWidth || 0));
|
|
449
522
|
if (!text) return [""];
|
|
450
523
|
|
|
451
|
-
const words = text.split(" ");
|
|
452
524
|
const lines = [];
|
|
453
|
-
let
|
|
525
|
+
let remaining = text;
|
|
454
526
|
|
|
455
|
-
|
|
456
|
-
if (
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
continue;
|
|
460
|
-
}
|
|
461
|
-
lines.push(truncateText(word, safeWidth));
|
|
462
|
-
continue;
|
|
527
|
+
while (remaining) {
|
|
528
|
+
if (getDisplayWidth(remaining) <= safeWidth) {
|
|
529
|
+
lines.push(remaining);
|
|
530
|
+
break;
|
|
463
531
|
}
|
|
464
532
|
|
|
465
|
-
const
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
}
|
|
533
|
+
const characters = [...remaining];
|
|
534
|
+
let width = 0;
|
|
535
|
+
let index = 0;
|
|
536
|
+
let lastBreakIndex = -1;
|
|
470
537
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
538
|
+
while (index < characters.length) {
|
|
539
|
+
const character = characters[index];
|
|
540
|
+
const characterWidth = getCharacterWidth(character);
|
|
541
|
+
if (width + characterWidth > safeWidth) {
|
|
542
|
+
break;
|
|
543
|
+
}
|
|
544
|
+
width += characterWidth;
|
|
545
|
+
index += 1;
|
|
546
|
+
if (character === " ") {
|
|
547
|
+
lastBreakIndex = index;
|
|
548
|
+
}
|
|
477
549
|
}
|
|
478
|
-
}
|
|
479
550
|
|
|
480
|
-
|
|
481
|
-
|
|
551
|
+
const breakIndex = lastBreakIndex > 0 ? lastBreakIndex : Math.max(1, index);
|
|
552
|
+
const line = characters.slice(0, breakIndex).join("").trimEnd();
|
|
553
|
+
lines.push(line);
|
|
554
|
+
remaining = characters.slice(breakIndex).join("").trimStart();
|
|
482
555
|
}
|
|
483
556
|
|
|
484
557
|
return lines.length ? lines : [""];
|
|
@@ -563,7 +636,7 @@ function formatSearchResult(item, { index = 0, colorize = (text) => text, width
|
|
|
563
636
|
}
|
|
564
637
|
|
|
565
638
|
const separator = " ";
|
|
566
|
-
const availableDescriptionWidth = width - plainPrefix
|
|
639
|
+
const availableDescriptionWidth = width - getDisplayWidth(plainPrefix) - getDisplayWidth(separator);
|
|
567
640
|
if (availableDescriptionWidth < minDescriptionWidth) {
|
|
568
641
|
return truncateText(`${plainPrefix}${separator}${descriptionPart}`, width);
|
|
569
642
|
}
|
|
@@ -578,25 +651,26 @@ function formatSearchCard(item, { index = 0, colorize = (text) => text, width =
|
|
|
578
651
|
const slugPart = `${item.slug}@${item.latestVersion}`;
|
|
579
652
|
const namePart = normalizeInlineText(item.name || "");
|
|
580
653
|
const descriptionPart = normalizeInlineText(item.description || "");
|
|
581
|
-
const maxInnerWidth = Math.max(
|
|
654
|
+
const maxInnerWidth = Math.max(16, width - 6);
|
|
582
655
|
const titleText = `${slugPart}${namePart ? ` (${namePart})` : ""}`;
|
|
583
656
|
const detailLines = wrapText(descriptionPart || "-", maxInnerWidth);
|
|
584
657
|
const contentWidth = Math.max(
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
658
|
+
16,
|
|
659
|
+
getDisplayWidth(ordinal) + 1,
|
|
660
|
+
Math.min(maxInnerWidth, getDisplayWidth(titleText)),
|
|
661
|
+
...detailLines.map((line) => Math.min(maxInnerWidth, getDisplayWidth(line))),
|
|
588
662
|
);
|
|
589
|
-
const titleSuffix = "─".repeat(Math.max(0, contentWidth - ordinal
|
|
663
|
+
const titleSuffix = "─".repeat(Math.max(0, contentWidth - getDisplayWidth(ordinal) - 1));
|
|
590
664
|
const title = `${colorize("◇", "success")} ${colorize(ordinal, "accent")} ${colorize(titleSuffix, "muted")}╮`;
|
|
591
665
|
const blank = `│ ${" ".repeat(contentWidth)} │`;
|
|
592
666
|
const firstLineText = truncateText(titleText, contentWidth);
|
|
593
|
-
const firstLine = `│ ${colorize(firstLineText, "accent")}${" ".repeat(Math.max(0, contentWidth - firstLineText
|
|
667
|
+
const firstLine = `│ ${colorize(firstLineText, "accent")}${" ".repeat(Math.max(0, contentWidth - getDisplayWidth(firstLineText)))} │`;
|
|
594
668
|
const detailRendered = detailLines.map((line) => {
|
|
595
669
|
const text = truncateText(line, contentWidth);
|
|
596
|
-
return `│ ${text}${" ".repeat(Math.max(0, contentWidth - text
|
|
670
|
+
return `│ ${text}${" ".repeat(Math.max(0, contentWidth - getDisplayWidth(text)))} │`;
|
|
597
671
|
});
|
|
598
672
|
const bottom = `╰${"─".repeat(contentWidth + 2)}╯`;
|
|
599
|
-
return [title, blank, firstLine, ...detailRendered, bottom].join("\n");
|
|
673
|
+
return [title, blank, firstLine, ...detailRendered, blank, bottom].join("\n");
|
|
600
674
|
}
|
|
601
675
|
|
|
602
676
|
function printFallbackIntro({ registry, slug, version, skillName, skillDescription, installTargets }, log = console.log) {
|