@qodalis/cli 2.0.0-beta.6 → 2.0.0-beta.8
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/package.json +2 -2
- package/public-api.d.mts +100 -136
- package/public-api.d.ts +100 -136
- package/public-api.js +2904 -1354
- package/public-api.js.map +1 -1
- package/public-api.mjs +2904 -1350
- package/public-api.mjs.map +1 -1
- package/wasm/qodalis_cli_wasm_bg.wasm +0 -0
package/public-api.js
CHANGED
|
@@ -411,7 +411,7 @@ var CliBackgroundServiceRegistry_TOKEN = "cli-background-service-registry";
|
|
|
411
411
|
var CliPackageManagerService_TOKEN = "cli-package-manager-service";
|
|
412
412
|
|
|
413
413
|
// src/lib/version.ts
|
|
414
|
-
var LIBRARY_VERSION = "2.0.0-beta.
|
|
414
|
+
var LIBRARY_VERSION = "2.0.0-beta.8";
|
|
415
415
|
var API_VERSION = 2;
|
|
416
416
|
var CliAliasCommandProcessor = class {
|
|
417
417
|
constructor() {
|
|
@@ -606,105 +606,6 @@ var CliCaptureCommandProcessor = class {
|
|
|
606
606
|
setTimeout(() => URL.revokeObjectURL(url), 1e3);
|
|
607
607
|
}
|
|
608
608
|
};
|
|
609
|
-
var CliBase64CommandProcessor = class {
|
|
610
|
-
constructor() {
|
|
611
|
-
this.command = "base64";
|
|
612
|
-
this.aliases = ["b64"];
|
|
613
|
-
this.description = "Encode or decode Base64 strings";
|
|
614
|
-
this.author = cliCore.DefaultLibraryAuthor;
|
|
615
|
-
this.processors = [];
|
|
616
|
-
this.metadata = {
|
|
617
|
-
icon: "\u{1F523}",
|
|
618
|
-
module: "misc"
|
|
619
|
-
};
|
|
620
|
-
this.processors = [
|
|
621
|
-
{
|
|
622
|
-
command: "encode",
|
|
623
|
-
aliases: ["enc", "e"],
|
|
624
|
-
description: "Encode text to Base64",
|
|
625
|
-
acceptsRawInput: true,
|
|
626
|
-
valueRequired: true,
|
|
627
|
-
processCommand: async (command2, context2) => {
|
|
628
|
-
const text = command2.value || command2.data || "";
|
|
629
|
-
const encoded = btoa(
|
|
630
|
-
new TextEncoder().encode(text).reduce(
|
|
631
|
-
(data, byte) => data + String.fromCharCode(byte),
|
|
632
|
-
""
|
|
633
|
-
)
|
|
634
|
-
);
|
|
635
|
-
context2.writer.writeln(encoded);
|
|
636
|
-
context2.process.output(encoded);
|
|
637
|
-
},
|
|
638
|
-
writeDescription: (context2) => {
|
|
639
|
-
const { writer } = context2;
|
|
640
|
-
writer.writeln("Encode text to Base64 format");
|
|
641
|
-
writer.writeln();
|
|
642
|
-
writer.writeln(`\u{1F4CB} ${context2.translator.t("cli.common.usage", "Usage:")}`);
|
|
643
|
-
writer.writeln(
|
|
644
|
-
` ${writer.wrapInColor("base64 encode <text>", cliCore.CliForegroundColor.Cyan)}`
|
|
645
|
-
);
|
|
646
|
-
writer.writeln();
|
|
647
|
-
writer.writeln(`\u{1F4DD} ${context2.translator.t("cli.common.examples", "Examples:")}`);
|
|
648
|
-
writer.writeln(
|
|
649
|
-
` base64 encode Hello World ${writer.wrapInColor("# \u2192 SGVsbG8gV29ybGQ=", cliCore.CliForegroundColor.Green)}`
|
|
650
|
-
);
|
|
651
|
-
}
|
|
652
|
-
},
|
|
653
|
-
{
|
|
654
|
-
command: "decode",
|
|
655
|
-
aliases: ["dec", "d"],
|
|
656
|
-
description: "Decode a Base64 string",
|
|
657
|
-
acceptsRawInput: true,
|
|
658
|
-
valueRequired: true,
|
|
659
|
-
processCommand: async (command2, context2) => {
|
|
660
|
-
const encoded = command2.value || command2.data || "";
|
|
661
|
-
try {
|
|
662
|
-
const bytes = Uint8Array.from(
|
|
663
|
-
atob(encoded),
|
|
664
|
-
(c) => c.charCodeAt(0)
|
|
665
|
-
);
|
|
666
|
-
const decoded = new TextDecoder().decode(bytes);
|
|
667
|
-
context2.writer.writeln(decoded);
|
|
668
|
-
context2.process.output(decoded);
|
|
669
|
-
} catch {
|
|
670
|
-
context2.writer.writeError("Invalid Base64 string");
|
|
671
|
-
context2.process.exit(-1);
|
|
672
|
-
}
|
|
673
|
-
},
|
|
674
|
-
writeDescription: (context2) => {
|
|
675
|
-
const { writer } = context2;
|
|
676
|
-
writer.writeln("Decode a Base64 string back to text");
|
|
677
|
-
writer.writeln();
|
|
678
|
-
writer.writeln(`\u{1F4CB} ${context2.translator.t("cli.common.usage", "Usage:")}`);
|
|
679
|
-
writer.writeln(
|
|
680
|
-
` ${writer.wrapInColor("base64 decode <base64>", cliCore.CliForegroundColor.Cyan)}`
|
|
681
|
-
);
|
|
682
|
-
writer.writeln();
|
|
683
|
-
writer.writeln(`\u{1F4DD} ${context2.translator.t("cli.common.examples", "Examples:")}`);
|
|
684
|
-
writer.writeln(
|
|
685
|
-
` base64 decode SGVsbG8gV29ybGQ= ${writer.wrapInColor("# \u2192 Hello World", cliCore.CliForegroundColor.Green)}`
|
|
686
|
-
);
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
];
|
|
690
|
-
}
|
|
691
|
-
async processCommand(command2, context2) {
|
|
692
|
-
context2.executor.showHelp(command2, context2);
|
|
693
|
-
}
|
|
694
|
-
writeDescription(context2) {
|
|
695
|
-
const { writer, translator: t } = context2;
|
|
696
|
-
writer.writeln(t.t("cli.base64.long_description", "Encode and decode Base64 strings"));
|
|
697
|
-
writer.writeln(t.t("cli.base64.utf8_note", "Supports UTF-8 text encoding"));
|
|
698
|
-
writer.writeln();
|
|
699
|
-
writer.writeln(`\u{1F4CB} ${t.t("cli.common.usage", "Usage:")}`);
|
|
700
|
-
writer.writeln(
|
|
701
|
-
` ${writer.wrapInColor("base64 encode <text>", cliCore.CliForegroundColor.Cyan)} ${t.t("cli.base64.encode_desc", "Encode text to Base64")}`
|
|
702
|
-
);
|
|
703
|
-
writer.writeln(
|
|
704
|
-
` ${writer.wrapInColor("base64 decode <base64>", cliCore.CliForegroundColor.Cyan)} ${t.t("cli.base64.decode_desc", "Decode Base64 to text")}`
|
|
705
|
-
);
|
|
706
|
-
}
|
|
707
|
-
};
|
|
708
609
|
var CliCalCommandProcessor = class {
|
|
709
610
|
constructor() {
|
|
710
611
|
this.command = "cal";
|
|
@@ -1480,255 +1381,6 @@ var CliEvalCommandProcessor = class {
|
|
|
1480
1381
|
);
|
|
1481
1382
|
}
|
|
1482
1383
|
};
|
|
1483
|
-
var CliHashCommandProcessor = class {
|
|
1484
|
-
constructor() {
|
|
1485
|
-
this.command = "hash";
|
|
1486
|
-
this.description = "Generate hash digests of text";
|
|
1487
|
-
this.author = cliCore.DefaultLibraryAuthor;
|
|
1488
|
-
this.processors = [];
|
|
1489
|
-
this.metadata = {
|
|
1490
|
-
icon: "#\uFE0F\u20E3",
|
|
1491
|
-
module: "misc"
|
|
1492
|
-
};
|
|
1493
|
-
const algorithms = [
|
|
1494
|
-
{ command: "sha256", algo: "SHA-256", aliases: ["sha-256"] },
|
|
1495
|
-
{ command: "sha1", algo: "SHA-1", aliases: ["sha-1"] },
|
|
1496
|
-
{ command: "sha384", algo: "SHA-384", aliases: ["sha-384"] },
|
|
1497
|
-
{ command: "sha512", algo: "SHA-512", aliases: ["sha-512"] }
|
|
1498
|
-
];
|
|
1499
|
-
this.processors = algorithms.map(({ command: command2, algo, aliases }) => ({
|
|
1500
|
-
command: command2,
|
|
1501
|
-
aliases,
|
|
1502
|
-
description: `Generate ${algo} hash`,
|
|
1503
|
-
acceptsRawInput: true,
|
|
1504
|
-
valueRequired: true,
|
|
1505
|
-
processCommand: async (cmd, context2) => {
|
|
1506
|
-
const text = cmd.value || cmd.data || "";
|
|
1507
|
-
try {
|
|
1508
|
-
const encoded = new TextEncoder().encode(text);
|
|
1509
|
-
const hashBuffer = await crypto.subtle.digest(
|
|
1510
|
-
algo,
|
|
1511
|
-
encoded
|
|
1512
|
-
);
|
|
1513
|
-
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
1514
|
-
const hex = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1515
|
-
context2.writer.writeln(hex);
|
|
1516
|
-
context2.process.output(hex);
|
|
1517
|
-
} catch {
|
|
1518
|
-
context2.writer.writeError(`Failed to compute ${algo} hash`);
|
|
1519
|
-
context2.process.exit(-1);
|
|
1520
|
-
}
|
|
1521
|
-
},
|
|
1522
|
-
writeDescription: (context2) => {
|
|
1523
|
-
const { writer } = context2;
|
|
1524
|
-
writer.writeln(
|
|
1525
|
-
`Generate a ${algo} hash digest of the input text`
|
|
1526
|
-
);
|
|
1527
|
-
writer.writeln();
|
|
1528
|
-
writer.writeln(`\u{1F4CB} ${context2.translator.t("cli.common.usage", "Usage:")}`);
|
|
1529
|
-
writer.writeln(
|
|
1530
|
-
` ${writer.wrapInColor(`hash ${command2} <text>`, cliCore.CliForegroundColor.Cyan)}`
|
|
1531
|
-
);
|
|
1532
|
-
}
|
|
1533
|
-
}));
|
|
1534
|
-
}
|
|
1535
|
-
async processCommand(command2, context2) {
|
|
1536
|
-
context2.executor.showHelp(command2, context2);
|
|
1537
|
-
}
|
|
1538
|
-
writeDescription(context2) {
|
|
1539
|
-
const { writer, translator: t } = context2;
|
|
1540
|
-
writer.writeln(
|
|
1541
|
-
t.t("cli.hash.long_description", "Generate cryptographic hash digests using the Web Crypto API")
|
|
1542
|
-
);
|
|
1543
|
-
writer.writeln();
|
|
1544
|
-
writer.writeln(`\u{1F4CB} ${t.t("cli.common.usage", "Usage:")}`);
|
|
1545
|
-
writer.writeln(
|
|
1546
|
-
` ${writer.wrapInColor("hash sha256 <text>", cliCore.CliForegroundColor.Cyan)} SHA-256 hash`
|
|
1547
|
-
);
|
|
1548
|
-
writer.writeln(
|
|
1549
|
-
` ${writer.wrapInColor("hash sha1 <text>", cliCore.CliForegroundColor.Cyan)} SHA-1 hash`
|
|
1550
|
-
);
|
|
1551
|
-
writer.writeln(
|
|
1552
|
-
` ${writer.wrapInColor("hash sha384 <text>", cliCore.CliForegroundColor.Cyan)} SHA-384 hash`
|
|
1553
|
-
);
|
|
1554
|
-
writer.writeln(
|
|
1555
|
-
` ${writer.wrapInColor("hash sha512 <text>", cliCore.CliForegroundColor.Cyan)} SHA-512 hash`
|
|
1556
|
-
);
|
|
1557
|
-
writer.writeln();
|
|
1558
|
-
writer.writeln(`\u{1F4DD} ${t.t("cli.common.examples", "Examples:")}`);
|
|
1559
|
-
writer.writeln(
|
|
1560
|
-
` hash sha256 hello ${writer.wrapInColor("# \u2192 2cf24dba5fb0a30e...", cliCore.CliForegroundColor.Green)}`
|
|
1561
|
-
);
|
|
1562
|
-
}
|
|
1563
|
-
};
|
|
1564
|
-
var CliHexCommandProcessor = class {
|
|
1565
|
-
constructor() {
|
|
1566
|
-
this.command = "hex";
|
|
1567
|
-
this.description = "Hex encode/decode and number base conversions";
|
|
1568
|
-
this.author = cliCore.DefaultLibraryAuthor;
|
|
1569
|
-
this.processors = [];
|
|
1570
|
-
this.metadata = {
|
|
1571
|
-
icon: "0\uFE0F\u20E3",
|
|
1572
|
-
module: "misc"
|
|
1573
|
-
};
|
|
1574
|
-
this.processors = [
|
|
1575
|
-
{
|
|
1576
|
-
command: "encode",
|
|
1577
|
-
aliases: ["enc"],
|
|
1578
|
-
description: "Encode text to hexadecimal",
|
|
1579
|
-
acceptsRawInput: true,
|
|
1580
|
-
valueRequired: true,
|
|
1581
|
-
processCommand: async (command2, context2) => {
|
|
1582
|
-
const text = command2.value || command2.data || "";
|
|
1583
|
-
const hex = Array.from(new TextEncoder().encode(text)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1584
|
-
context2.writer.writeln(hex);
|
|
1585
|
-
context2.process.output(hex);
|
|
1586
|
-
},
|
|
1587
|
-
writeDescription: (context2) => {
|
|
1588
|
-
const { writer } = context2;
|
|
1589
|
-
writer.writeln("Encode text to hexadecimal representation");
|
|
1590
|
-
writer.writeln();
|
|
1591
|
-
writer.writeln(`\u{1F4CB} ${context2.translator.t("cli.common.usage", "Usage:")}`);
|
|
1592
|
-
writer.writeln(
|
|
1593
|
-
` ${writer.wrapInColor("hex encode <text>", cliCore.CliForegroundColor.Cyan)}`
|
|
1594
|
-
);
|
|
1595
|
-
}
|
|
1596
|
-
},
|
|
1597
|
-
{
|
|
1598
|
-
command: "decode",
|
|
1599
|
-
aliases: ["dec"],
|
|
1600
|
-
description: "Decode hexadecimal to text",
|
|
1601
|
-
acceptsRawInput: true,
|
|
1602
|
-
valueRequired: true,
|
|
1603
|
-
processCommand: async (command2, context2) => {
|
|
1604
|
-
const hexStr = command2.value || command2.data || "";
|
|
1605
|
-
try {
|
|
1606
|
-
const bytes = new Uint8Array(
|
|
1607
|
-
(hexStr.match(/.{1,2}/g) || []).map(
|
|
1608
|
-
(byte) => parseInt(byte, 16)
|
|
1609
|
-
)
|
|
1610
|
-
);
|
|
1611
|
-
const text = new TextDecoder().decode(bytes);
|
|
1612
|
-
context2.writer.writeln(text);
|
|
1613
|
-
context2.process.output(text);
|
|
1614
|
-
} catch {
|
|
1615
|
-
context2.writer.writeError("Invalid hex string");
|
|
1616
|
-
context2.process.exit(-1);
|
|
1617
|
-
}
|
|
1618
|
-
},
|
|
1619
|
-
writeDescription: (context2) => {
|
|
1620
|
-
const { writer } = context2;
|
|
1621
|
-
writer.writeln("Decode hexadecimal back to text");
|
|
1622
|
-
writer.writeln();
|
|
1623
|
-
writer.writeln(`\u{1F4CB} ${context2.translator.t("cli.common.usage", "Usage:")}`);
|
|
1624
|
-
writer.writeln(
|
|
1625
|
-
` ${writer.wrapInColor("hex decode <hex>", cliCore.CliForegroundColor.Cyan)}`
|
|
1626
|
-
);
|
|
1627
|
-
}
|
|
1628
|
-
},
|
|
1629
|
-
{
|
|
1630
|
-
command: "convert",
|
|
1631
|
-
aliases: ["conv"],
|
|
1632
|
-
description: "Convert a number between bases",
|
|
1633
|
-
acceptsRawInput: true,
|
|
1634
|
-
valueRequired: true,
|
|
1635
|
-
parameters: [
|
|
1636
|
-
{
|
|
1637
|
-
name: "from",
|
|
1638
|
-
description: "Source base (default: 10)",
|
|
1639
|
-
type: "number",
|
|
1640
|
-
required: false
|
|
1641
|
-
},
|
|
1642
|
-
{
|
|
1643
|
-
name: "to",
|
|
1644
|
-
description: "Target base (default: 16)",
|
|
1645
|
-
type: "number",
|
|
1646
|
-
required: false
|
|
1647
|
-
}
|
|
1648
|
-
],
|
|
1649
|
-
processCommand: async (command2, context2) => {
|
|
1650
|
-
const { writer } = context2;
|
|
1651
|
-
const input = command2.value || "";
|
|
1652
|
-
const fromBase = parseInt(command2.args["from"]) || 10;
|
|
1653
|
-
const toBase = parseInt(command2.args["to"]) || 16;
|
|
1654
|
-
if (fromBase < 2 || fromBase > 36 || toBase < 2 || toBase > 36) {
|
|
1655
|
-
writer.writeError("Base must be between 2 and 36");
|
|
1656
|
-
context2.process.exit(-1);
|
|
1657
|
-
return;
|
|
1658
|
-
}
|
|
1659
|
-
try {
|
|
1660
|
-
const num = parseInt(input, fromBase);
|
|
1661
|
-
if (isNaN(num)) {
|
|
1662
|
-
writer.writeError(
|
|
1663
|
-
`Invalid number for base ${fromBase}`
|
|
1664
|
-
);
|
|
1665
|
-
context2.process.exit(-1);
|
|
1666
|
-
return;
|
|
1667
|
-
}
|
|
1668
|
-
const result = num.toString(toBase);
|
|
1669
|
-
writer.writeln(
|
|
1670
|
-
` ${writer.wrapInColor(`Base ${fromBase}:`, cliCore.CliForegroundColor.Cyan)} ${input}`
|
|
1671
|
-
);
|
|
1672
|
-
writer.writeln(
|
|
1673
|
-
` ${writer.wrapInColor(`Base ${toBase}:`, cliCore.CliForegroundColor.Cyan)} ${result}`
|
|
1674
|
-
);
|
|
1675
|
-
if (fromBase !== 10 && toBase !== 10) {
|
|
1676
|
-
writer.writeln(
|
|
1677
|
-
` ${writer.wrapInColor("Base 10:", cliCore.CliForegroundColor.Cyan)} ${num}`
|
|
1678
|
-
);
|
|
1679
|
-
}
|
|
1680
|
-
context2.process.output(result);
|
|
1681
|
-
} catch {
|
|
1682
|
-
writer.writeError("Conversion failed");
|
|
1683
|
-
context2.process.exit(-1);
|
|
1684
|
-
}
|
|
1685
|
-
},
|
|
1686
|
-
writeDescription: (context2) => {
|
|
1687
|
-
const { writer } = context2;
|
|
1688
|
-
writer.writeln(
|
|
1689
|
-
"Convert a number between different bases (2-36)"
|
|
1690
|
-
);
|
|
1691
|
-
writer.writeln();
|
|
1692
|
-
writer.writeln(`\u{1F4CB} ${context2.translator.t("cli.common.usage", "Usage:")}`);
|
|
1693
|
-
writer.writeln(
|
|
1694
|
-
` ${writer.wrapInColor("hex convert <number> [--from=N] [--to=N]", cliCore.CliForegroundColor.Cyan)}`
|
|
1695
|
-
);
|
|
1696
|
-
writer.writeln();
|
|
1697
|
-
writer.writeln(`\u{1F4DD} ${context2.translator.t("cli.common.examples", "Examples:")}`);
|
|
1698
|
-
writer.writeln(
|
|
1699
|
-
` hex convert 255 ${writer.wrapInColor("# Decimal \u2192 Hex (ff)", cliCore.CliForegroundColor.Green)}`
|
|
1700
|
-
);
|
|
1701
|
-
writer.writeln(
|
|
1702
|
-
` hex convert ff --from=16 --to=2 ${writer.wrapInColor("# Hex \u2192 Binary (11111111)", cliCore.CliForegroundColor.Green)}`
|
|
1703
|
-
);
|
|
1704
|
-
writer.writeln(
|
|
1705
|
-
` hex convert 1010 --from=2 ${writer.wrapInColor("# Binary \u2192 Hex (a)", cliCore.CliForegroundColor.Green)}`
|
|
1706
|
-
);
|
|
1707
|
-
}
|
|
1708
|
-
}
|
|
1709
|
-
];
|
|
1710
|
-
}
|
|
1711
|
-
async processCommand(command2, context2) {
|
|
1712
|
-
context2.executor.showHelp(command2, context2);
|
|
1713
|
-
}
|
|
1714
|
-
writeDescription(context2) {
|
|
1715
|
-
const { writer, translator: t } = context2;
|
|
1716
|
-
writer.writeln(
|
|
1717
|
-
t.t("cli.hex.long_description", "Hex encode/decode text and convert numbers between bases")
|
|
1718
|
-
);
|
|
1719
|
-
writer.writeln();
|
|
1720
|
-
writer.writeln(`\u{1F4CB} ${t.t("cli.common.usage", "Usage:")}`);
|
|
1721
|
-
writer.writeln(
|
|
1722
|
-
` ${writer.wrapInColor("hex encode <text>", cliCore.CliForegroundColor.Cyan)} ${t.t("cli.hex.text_to_hex", "Text to hex")}`
|
|
1723
|
-
);
|
|
1724
|
-
writer.writeln(
|
|
1725
|
-
` ${writer.wrapInColor("hex decode <hex>", cliCore.CliForegroundColor.Cyan)} ${t.t("cli.hex.hex_to_text", "Hex to text")}`
|
|
1726
|
-
);
|
|
1727
|
-
writer.writeln(
|
|
1728
|
-
` ${writer.wrapInColor("hex convert <num> [--from --to]", cliCore.CliForegroundColor.Cyan)} ${t.t("cli.hex.base_conversion", "Base conversion")}`
|
|
1729
|
-
);
|
|
1730
|
-
}
|
|
1731
|
-
};
|
|
1732
1384
|
var CliJsonCommandProcessor = class {
|
|
1733
1385
|
constructor() {
|
|
1734
1386
|
this.command = "json";
|
|
@@ -1855,134 +1507,6 @@ var CliJsonCommandProcessor = class {
|
|
|
1855
1507
|
);
|
|
1856
1508
|
}
|
|
1857
1509
|
};
|
|
1858
|
-
var CliJwtCommandProcessor = class {
|
|
1859
|
-
constructor() {
|
|
1860
|
-
this.command = "jwt";
|
|
1861
|
-
this.description = "Decode and inspect JWT tokens";
|
|
1862
|
-
this.author = cliCore.DefaultLibraryAuthor;
|
|
1863
|
-
this.processors = [];
|
|
1864
|
-
this.metadata = {
|
|
1865
|
-
icon: "\u{1F511}",
|
|
1866
|
-
module: "misc"
|
|
1867
|
-
};
|
|
1868
|
-
this.processors = [
|
|
1869
|
-
{
|
|
1870
|
-
command: "decode",
|
|
1871
|
-
aliases: ["d"],
|
|
1872
|
-
description: "Decode a JWT token",
|
|
1873
|
-
acceptsRawInput: true,
|
|
1874
|
-
valueRequired: true,
|
|
1875
|
-
processCommand: async (command2, context2) => {
|
|
1876
|
-
const token = command2.value || command2.data || "";
|
|
1877
|
-
const { writer } = context2;
|
|
1878
|
-
const parts = token.split(".");
|
|
1879
|
-
if (parts.length !== 3) {
|
|
1880
|
-
writer.writeError(
|
|
1881
|
-
"Invalid JWT token (expected 3 parts separated by dots)"
|
|
1882
|
-
);
|
|
1883
|
-
context2.process.exit(-1);
|
|
1884
|
-
return;
|
|
1885
|
-
}
|
|
1886
|
-
try {
|
|
1887
|
-
const header = JSON.parse(
|
|
1888
|
-
this.base64UrlDecode(parts[0])
|
|
1889
|
-
);
|
|
1890
|
-
const payload = JSON.parse(
|
|
1891
|
-
this.base64UrlDecode(parts[1])
|
|
1892
|
-
);
|
|
1893
|
-
writer.writeln(
|
|
1894
|
-
writer.wrapInColor(
|
|
1895
|
-
"Header:",
|
|
1896
|
-
cliCore.CliForegroundColor.Yellow
|
|
1897
|
-
)
|
|
1898
|
-
);
|
|
1899
|
-
writer.writeln(JSON.stringify(header, null, 2));
|
|
1900
|
-
writer.writeln();
|
|
1901
|
-
writer.writeln(
|
|
1902
|
-
writer.wrapInColor(
|
|
1903
|
-
"Payload:",
|
|
1904
|
-
cliCore.CliForegroundColor.Yellow
|
|
1905
|
-
)
|
|
1906
|
-
);
|
|
1907
|
-
writer.writeln(JSON.stringify(payload, null, 2));
|
|
1908
|
-
writer.writeln();
|
|
1909
|
-
if (payload.exp) {
|
|
1910
|
-
const expDate = new Date(payload.exp * 1e3);
|
|
1911
|
-
const isExpired = expDate.getTime() < Date.now();
|
|
1912
|
-
writer.writeln(
|
|
1913
|
-
`${writer.wrapInColor("Expires:", cliCore.CliForegroundColor.Yellow)} ${expDate.toLocaleString()} ${isExpired ? writer.wrapInColor(
|
|
1914
|
-
"(EXPIRED)",
|
|
1915
|
-
cliCore.CliForegroundColor.Red
|
|
1916
|
-
) : writer.wrapInColor(
|
|
1917
|
-
"(valid)",
|
|
1918
|
-
cliCore.CliForegroundColor.Green
|
|
1919
|
-
)}`
|
|
1920
|
-
);
|
|
1921
|
-
}
|
|
1922
|
-
if (payload.iat) {
|
|
1923
|
-
const iatDate = new Date(payload.iat * 1e3);
|
|
1924
|
-
writer.writeln(
|
|
1925
|
-
`${writer.wrapInColor("Issued:", cliCore.CliForegroundColor.Yellow)} ${iatDate.toLocaleString()}`
|
|
1926
|
-
);
|
|
1927
|
-
}
|
|
1928
|
-
writer.writeln();
|
|
1929
|
-
writer.writeln(
|
|
1930
|
-
writer.wrapInColor(
|
|
1931
|
-
"Signature:",
|
|
1932
|
-
cliCore.CliForegroundColor.Yellow
|
|
1933
|
-
) + ` ${parts[2].substring(0, 20)}...`
|
|
1934
|
-
);
|
|
1935
|
-
context2.process.output({ header, payload });
|
|
1936
|
-
} catch {
|
|
1937
|
-
writer.writeError("Failed to decode JWT token");
|
|
1938
|
-
context2.process.exit(-1);
|
|
1939
|
-
}
|
|
1940
|
-
},
|
|
1941
|
-
writeDescription: (context2) => {
|
|
1942
|
-
const { writer } = context2;
|
|
1943
|
-
writer.writeln(
|
|
1944
|
-
"Decode a JWT token and display its header and payload"
|
|
1945
|
-
);
|
|
1946
|
-
writer.writeln(
|
|
1947
|
-
"Shows expiration status and issue date if present"
|
|
1948
|
-
);
|
|
1949
|
-
writer.writeln();
|
|
1950
|
-
writer.writeln(`\u{1F4CB} ${context2.translator.t("cli.common.usage", "Usage:")}`);
|
|
1951
|
-
writer.writeln(
|
|
1952
|
-
` ${writer.wrapInColor("jwt decode <token>", cliCore.CliForegroundColor.Cyan)}`
|
|
1953
|
-
);
|
|
1954
|
-
}
|
|
1955
|
-
}
|
|
1956
|
-
];
|
|
1957
|
-
}
|
|
1958
|
-
async processCommand(command2, context2) {
|
|
1959
|
-
context2.executor.showHelp(command2, context2);
|
|
1960
|
-
}
|
|
1961
|
-
writeDescription(context2) {
|
|
1962
|
-
const { writer, translator: t } = context2;
|
|
1963
|
-
writer.writeln(t.t("cli.jwt.long_description", "Decode and inspect JSON Web Tokens (JWT)"));
|
|
1964
|
-
writer.writeln();
|
|
1965
|
-
writer.writeln(`\u{1F4CB} ${t.t("cli.common.usage", "Usage:")}`);
|
|
1966
|
-
writer.writeln(
|
|
1967
|
-
` ${writer.wrapInColor("jwt decode <token>", cliCore.CliForegroundColor.Cyan)} ${t.t("cli.jwt.decode_display", "Decode and display JWT contents")}`
|
|
1968
|
-
);
|
|
1969
|
-
writer.writeln();
|
|
1970
|
-
writer.writeln(
|
|
1971
|
-
`\u{1F4DD} ${t.t("cli.jwt.shows", "Shows: header, payload, expiration status, issue date")}`
|
|
1972
|
-
);
|
|
1973
|
-
}
|
|
1974
|
-
base64UrlDecode(str) {
|
|
1975
|
-
str = str.replace(/-/g, "+").replace(/_/g, "/");
|
|
1976
|
-
while (str.length % 4) {
|
|
1977
|
-
str += "=";
|
|
1978
|
-
}
|
|
1979
|
-
return decodeURIComponent(
|
|
1980
|
-
atob(str).split("").map(
|
|
1981
|
-
(c) => "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2)
|
|
1982
|
-
).join("")
|
|
1983
|
-
);
|
|
1984
|
-
}
|
|
1985
|
-
};
|
|
1986
1510
|
var LOREM_WORDS = [
|
|
1987
1511
|
"lorem",
|
|
1988
1512
|
"ipsum",
|
|
@@ -3027,10 +2551,8 @@ var CliUptimeCommandProcessor = class {
|
|
|
3027
2551
|
icon: "\u23F1\uFE0F",
|
|
3028
2552
|
module: "misc"
|
|
3029
2553
|
};
|
|
3030
|
-
this.sessionStartTimes = /* @__PURE__ */ new WeakMap();
|
|
3031
2554
|
}
|
|
3032
2555
|
async initialize(context2) {
|
|
3033
|
-
this.sessionStartTimes.set(context2.terminal, Date.now());
|
|
3034
2556
|
try {
|
|
3035
2557
|
context2.backgroundServices.register({
|
|
3036
2558
|
name: "uptime-tracker",
|
|
@@ -3038,6 +2560,10 @@ var CliUptimeCommandProcessor = class {
|
|
|
3038
2560
|
type: "daemon",
|
|
3039
2561
|
onStart: async (ctx) => {
|
|
3040
2562
|
ctx.log("Uptime tracking started");
|
|
2563
|
+
await new Promise((resolve) => {
|
|
2564
|
+
ctx.signal.addEventListener("abort", () => resolve(), { once: true });
|
|
2565
|
+
});
|
|
2566
|
+
ctx.log("Uptime tracking stopped");
|
|
3041
2567
|
}
|
|
3042
2568
|
});
|
|
3043
2569
|
await context2.backgroundServices.start("uptime-tracker");
|
|
@@ -3046,9 +2572,16 @@ var CliUptimeCommandProcessor = class {
|
|
|
3046
2572
|
}
|
|
3047
2573
|
async processCommand(_, context2) {
|
|
3048
2574
|
const svcInfo = context2.backgroundServices.getStatus("uptime-tracker");
|
|
3049
|
-
const startTime = svcInfo?.status === "running" && svcInfo.startedAt ? svcInfo.startedAt.getTime() : this.sessionStartTimes.get(context2.terminal) ?? Date.now();
|
|
3050
|
-
const elapsed = Date.now() - startTime;
|
|
3051
2575
|
const { writer } = context2;
|
|
2576
|
+
if (!svcInfo || svcInfo.status !== "running") {
|
|
2577
|
+
writer.writeError("Uptime tracker is not running.");
|
|
2578
|
+
writer.writeInfo(
|
|
2579
|
+
`Use ${writer.wrapInColor("services start uptime-tracker", cliCore.CliForegroundColor.Cyan)} to restart.`
|
|
2580
|
+
);
|
|
2581
|
+
return;
|
|
2582
|
+
}
|
|
2583
|
+
const startTime = svcInfo.startedAt.getTime();
|
|
2584
|
+
const elapsed = Date.now() - startTime;
|
|
3052
2585
|
const seconds = Math.floor(elapsed / 1e3) % 60;
|
|
3053
2586
|
const minutes = Math.floor(elapsed / (1e3 * 60)) % 60;
|
|
3054
2587
|
const hours = Math.floor(elapsed / (1e3 * 60 * 60)) % 24;
|
|
@@ -3079,137 +2612,6 @@ var CliUptimeCommandProcessor = class {
|
|
|
3079
2612
|
);
|
|
3080
2613
|
}
|
|
3081
2614
|
};
|
|
3082
|
-
var CliUrlCommandProcessor = class {
|
|
3083
|
-
constructor() {
|
|
3084
|
-
this.command = "url";
|
|
3085
|
-
this.description = "Encode or decode URLs";
|
|
3086
|
-
this.author = cliCore.DefaultLibraryAuthor;
|
|
3087
|
-
this.processors = [];
|
|
3088
|
-
this.metadata = {
|
|
3089
|
-
icon: "\u{1F517}",
|
|
3090
|
-
module: "misc"
|
|
3091
|
-
};
|
|
3092
|
-
this.processors = [
|
|
3093
|
-
{
|
|
3094
|
-
command: "encode",
|
|
3095
|
-
aliases: ["enc"],
|
|
3096
|
-
description: "URL-encode a string",
|
|
3097
|
-
acceptsRawInput: true,
|
|
3098
|
-
valueRequired: true,
|
|
3099
|
-
processCommand: async (command2, context2) => {
|
|
3100
|
-
const text = command2.value || command2.data || "";
|
|
3101
|
-
const encoded = encodeURIComponent(text);
|
|
3102
|
-
context2.writer.writeln(encoded);
|
|
3103
|
-
context2.process.output(encoded);
|
|
3104
|
-
},
|
|
3105
|
-
writeDescription: (context2) => {
|
|
3106
|
-
const { writer } = context2;
|
|
3107
|
-
writer.writeln("URL-encode a string");
|
|
3108
|
-
writer.writeln();
|
|
3109
|
-
writer.writeln(`\u{1F4CB} ${context2.translator.t("cli.common.usage", "Usage:")}`);
|
|
3110
|
-
writer.writeln(
|
|
3111
|
-
` ${writer.wrapInColor("url encode <text>", cliCore.CliForegroundColor.Cyan)}`
|
|
3112
|
-
);
|
|
3113
|
-
writer.writeln();
|
|
3114
|
-
writer.writeln(`\u{1F4DD} ${context2.translator.t("cli.common.examples", "Examples:")}`);
|
|
3115
|
-
writer.writeln(
|
|
3116
|
-
` url encode hello world ${writer.wrapInColor("# \u2192 hello%20world", cliCore.CliForegroundColor.Green)}`
|
|
3117
|
-
);
|
|
3118
|
-
}
|
|
3119
|
-
},
|
|
3120
|
-
{
|
|
3121
|
-
command: "decode",
|
|
3122
|
-
aliases: ["dec"],
|
|
3123
|
-
description: "URL-decode a string",
|
|
3124
|
-
acceptsRawInput: true,
|
|
3125
|
-
valueRequired: true,
|
|
3126
|
-
processCommand: async (command2, context2) => {
|
|
3127
|
-
const text = command2.value || command2.data || "";
|
|
3128
|
-
try {
|
|
3129
|
-
const decoded = decodeURIComponent(text);
|
|
3130
|
-
context2.writer.writeln(decoded);
|
|
3131
|
-
context2.process.output(decoded);
|
|
3132
|
-
} catch {
|
|
3133
|
-
context2.writer.writeError("Invalid URL-encoded string");
|
|
3134
|
-
context2.process.exit(-1);
|
|
3135
|
-
}
|
|
3136
|
-
},
|
|
3137
|
-
writeDescription: (context2) => {
|
|
3138
|
-
const { writer } = context2;
|
|
3139
|
-
writer.writeln("URL-decode a string");
|
|
3140
|
-
writer.writeln();
|
|
3141
|
-
writer.writeln(`\u{1F4CB} ${context2.translator.t("cli.common.usage", "Usage:")}`);
|
|
3142
|
-
writer.writeln(
|
|
3143
|
-
` ${writer.wrapInColor("url decode <text>", cliCore.CliForegroundColor.Cyan)}`
|
|
3144
|
-
);
|
|
3145
|
-
writer.writeln();
|
|
3146
|
-
writer.writeln(`\u{1F4DD} ${context2.translator.t("cli.common.examples", "Examples:")}`);
|
|
3147
|
-
writer.writeln(
|
|
3148
|
-
` url decode hello%20world ${writer.wrapInColor("# \u2192 hello world", cliCore.CliForegroundColor.Green)}`
|
|
3149
|
-
);
|
|
3150
|
-
}
|
|
3151
|
-
},
|
|
3152
|
-
{
|
|
3153
|
-
command: "parse",
|
|
3154
|
-
description: "Parse a URL into its components",
|
|
3155
|
-
acceptsRawInput: true,
|
|
3156
|
-
valueRequired: true,
|
|
3157
|
-
processCommand: async (command2, context2) => {
|
|
3158
|
-
const input = command2.value || command2.data || "";
|
|
3159
|
-
try {
|
|
3160
|
-
const url = new URL(input);
|
|
3161
|
-
const parts = {
|
|
3162
|
-
protocol: url.protocol,
|
|
3163
|
-
hostname: url.hostname,
|
|
3164
|
-
port: url.port || "(default)",
|
|
3165
|
-
pathname: url.pathname,
|
|
3166
|
-
search: url.search || "(none)",
|
|
3167
|
-
hash: url.hash || "(none)",
|
|
3168
|
-
origin: url.origin
|
|
3169
|
-
};
|
|
3170
|
-
const { writer } = context2;
|
|
3171
|
-
Object.entries(parts).forEach(([key, value]) => {
|
|
3172
|
-
writer.writeln(
|
|
3173
|
-
` ${writer.wrapInColor(key.padEnd(12), cliCore.CliForegroundColor.Cyan)} ${value}`
|
|
3174
|
-
);
|
|
3175
|
-
});
|
|
3176
|
-
context2.process.output(parts);
|
|
3177
|
-
} catch {
|
|
3178
|
-
context2.writer.writeError("Invalid URL");
|
|
3179
|
-
context2.process.exit(-1);
|
|
3180
|
-
}
|
|
3181
|
-
},
|
|
3182
|
-
writeDescription: (context2) => {
|
|
3183
|
-
const { writer } = context2;
|
|
3184
|
-
writer.writeln("Parse a URL and display its components");
|
|
3185
|
-
writer.writeln();
|
|
3186
|
-
writer.writeln(`\u{1F4CB} ${context2.translator.t("cli.common.usage", "Usage:")}`);
|
|
3187
|
-
writer.writeln(
|
|
3188
|
-
` ${writer.wrapInColor("url parse <url>", cliCore.CliForegroundColor.Cyan)}`
|
|
3189
|
-
);
|
|
3190
|
-
}
|
|
3191
|
-
}
|
|
3192
|
-
];
|
|
3193
|
-
}
|
|
3194
|
-
async processCommand(command2, context2) {
|
|
3195
|
-
context2.executor.showHelp(command2, context2);
|
|
3196
|
-
}
|
|
3197
|
-
writeDescription(context2) {
|
|
3198
|
-
const { writer, translator: t } = context2;
|
|
3199
|
-
writer.writeln(t.t("cli.url.long_description", "Encode, decode, and parse URLs"));
|
|
3200
|
-
writer.writeln();
|
|
3201
|
-
writer.writeln(`\u{1F4CB} ${t.t("cli.common.usage", "Usage:")}`);
|
|
3202
|
-
writer.writeln(
|
|
3203
|
-
` ${writer.wrapInColor("url encode <text>", cliCore.CliForegroundColor.Cyan)} ${t.t("cli.url.encode_desc", "URL-encode a string")}`
|
|
3204
|
-
);
|
|
3205
|
-
writer.writeln(
|
|
3206
|
-
` ${writer.wrapInColor("url decode <text>", cliCore.CliForegroundColor.Cyan)} ${t.t("cli.url.decode_desc", "URL-decode a string")}`
|
|
3207
|
-
);
|
|
3208
|
-
writer.writeln(
|
|
3209
|
-
` ${writer.wrapInColor("url parse <url>", cliCore.CliForegroundColor.Cyan)} ${t.t("cli.url.parse_desc", "Parse URL components")}`
|
|
3210
|
-
);
|
|
3211
|
-
}
|
|
3212
|
-
};
|
|
3213
2615
|
var CliYesCommandProcessor = class {
|
|
3214
2616
|
constructor() {
|
|
3215
2617
|
this.command = "yes";
|
|
@@ -6825,6 +6227,9 @@ var CliServerManager = class {
|
|
|
6825
6227
|
|
|
6826
6228
|
// src/lib/wasm/fallback.ts
|
|
6827
6229
|
var JsFallbackAccelerator = class {
|
|
6230
|
+
constructor() {
|
|
6231
|
+
this.ruleSets = /* @__PURE__ */ new Map();
|
|
6232
|
+
}
|
|
6828
6233
|
textSearch(text, needle, startRow, startCol, caseSensitive, wrap) {
|
|
6829
6234
|
if (!needle) return [-1, -1];
|
|
6830
6235
|
const lines = text.split("\n");
|
|
@@ -6884,6 +6289,60 @@ var JsFallbackAccelerator = class {
|
|
|
6884
6289
|
}
|
|
6885
6290
|
return matches.sort();
|
|
6886
6291
|
}
|
|
6292
|
+
registerRuleSet(ruleSetId, rules) {
|
|
6293
|
+
const parsed = [];
|
|
6294
|
+
for (const line of rules.split("\n")) {
|
|
6295
|
+
if (!line) continue;
|
|
6296
|
+
const parts = line.split(" ");
|
|
6297
|
+
if (parts.length < 2) continue;
|
|
6298
|
+
try {
|
|
6299
|
+
parsed.push({
|
|
6300
|
+
pattern: new RegExp(parts[0]),
|
|
6301
|
+
tokenType: parts[1],
|
|
6302
|
+
captureGroup: parts[2] ? parseInt(parts[2], 10) : 0
|
|
6303
|
+
});
|
|
6304
|
+
} catch {
|
|
6305
|
+
}
|
|
6306
|
+
}
|
|
6307
|
+
this.ruleSets.set(ruleSetId, parsed);
|
|
6308
|
+
}
|
|
6309
|
+
tokenizeLine(line, ruleSetId) {
|
|
6310
|
+
const rules = this.ruleSets.get(ruleSetId);
|
|
6311
|
+
if (!rules || !line) return "";
|
|
6312
|
+
const tokens = [];
|
|
6313
|
+
let pos = 0;
|
|
6314
|
+
while (pos < line.length) {
|
|
6315
|
+
let matched = false;
|
|
6316
|
+
for (const rule of rules) {
|
|
6317
|
+
rule.pattern.lastIndex = 0;
|
|
6318
|
+
const sliced = line.slice(pos);
|
|
6319
|
+
const m = rule.pattern.exec(sliced);
|
|
6320
|
+
if (m) {
|
|
6321
|
+
let start;
|
|
6322
|
+
let end;
|
|
6323
|
+
if (rule.captureGroup > 0 && m[rule.captureGroup] !== void 0) {
|
|
6324
|
+
const groupText = m[rule.captureGroup];
|
|
6325
|
+
const groupOffset = m[0].indexOf(groupText);
|
|
6326
|
+
start = pos + m.index + groupOffset;
|
|
6327
|
+
end = start + groupText.length;
|
|
6328
|
+
} else {
|
|
6329
|
+
start = pos + m.index;
|
|
6330
|
+
end = pos + m.index + m[0].length;
|
|
6331
|
+
}
|
|
6332
|
+
if (pos + m.index + m[0].length > pos) {
|
|
6333
|
+
tokens.push(`${start},${end},${rule.tokenType}`);
|
|
6334
|
+
pos = pos + m.index + m[0].length;
|
|
6335
|
+
matched = true;
|
|
6336
|
+
break;
|
|
6337
|
+
}
|
|
6338
|
+
}
|
|
6339
|
+
}
|
|
6340
|
+
if (!matched) {
|
|
6341
|
+
pos++;
|
|
6342
|
+
}
|
|
6343
|
+
}
|
|
6344
|
+
return tokens.join("\n");
|
|
6345
|
+
}
|
|
6887
6346
|
commonPrefix(strings) {
|
|
6888
6347
|
if (strings.length === 0) return "";
|
|
6889
6348
|
let prefix = strings[0];
|
|
@@ -7056,6 +6515,30 @@ function wasmCommonPrefix(strings) {
|
|
|
7056
6515
|
wasm.__wbindgen_export3(d0, d1, 1);
|
|
7057
6516
|
}
|
|
7058
6517
|
}
|
|
6518
|
+
function wasmRegisterRuleSet(ruleSetId, rules) {
|
|
6519
|
+
const ptr0 = passStringToWasm0(ruleSetId, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
6520
|
+
const len0 = WASM_VECTOR_LEN;
|
|
6521
|
+
const ptr1 = passStringToWasm0(rules, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
6522
|
+
const len1 = WASM_VECTOR_LEN;
|
|
6523
|
+
wasm.register_rule_set(ptr0, len0, ptr1, len1);
|
|
6524
|
+
}
|
|
6525
|
+
function wasmTokenizeLine(line, ruleSetId) {
|
|
6526
|
+
let d0, d1;
|
|
6527
|
+
try {
|
|
6528
|
+
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
6529
|
+
const ptr0 = passStringToWasm0(line, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
6530
|
+
const len0 = WASM_VECTOR_LEN;
|
|
6531
|
+
const ptr1 = passStringToWasm0(ruleSetId, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
6532
|
+
const len1 = WASM_VECTOR_LEN;
|
|
6533
|
+
wasm.tokenize_line(retptr, ptr0, len0, ptr1, len1);
|
|
6534
|
+
d0 = getDataViewMemory0().getInt32(retptr + 0, true);
|
|
6535
|
+
d1 = getDataViewMemory0().getInt32(retptr + 4, true);
|
|
6536
|
+
return getStringFromWasm0(d0, d1);
|
|
6537
|
+
} finally {
|
|
6538
|
+
wasm.__wbindgen_add_to_stack_pointer(16);
|
|
6539
|
+
wasm.__wbindgen_export3(d0, d1, 1);
|
|
6540
|
+
}
|
|
6541
|
+
}
|
|
7059
6542
|
async function instantiateWasm(wasmBytes) {
|
|
7060
6543
|
const imports = { __proto__: null, "./qodalis_cli_wasm_bg.js": { __proto__: null } };
|
|
7061
6544
|
const { instance } = await WebAssembly.instantiate(wasmBytes, imports);
|
|
@@ -7096,6 +6579,12 @@ function createWasmAccelerator() {
|
|
|
7096
6579
|
},
|
|
7097
6580
|
commonPrefix(strings) {
|
|
7098
6581
|
return wasmCommonPrefix(strings.join("\n"));
|
|
6582
|
+
},
|
|
6583
|
+
registerRuleSet(ruleSetId, rules) {
|
|
6584
|
+
wasmRegisterRuleSet(ruleSetId, rules);
|
|
6585
|
+
},
|
|
6586
|
+
tokenizeLine(line, ruleSetId) {
|
|
6587
|
+
return wasmTokenizeLine(line, ruleSetId);
|
|
7099
6588
|
}
|
|
7100
6589
|
};
|
|
7101
6590
|
}
|
|
@@ -8022,7 +7511,11 @@ var NanoEditorRenderer = class {
|
|
|
8022
7511
|
output2 += "\x1B[2K";
|
|
8023
7512
|
if (lineIdx < buffer.lines.length) {
|
|
8024
7513
|
const line = buffer.lines[lineIdx];
|
|
8025
|
-
|
|
7514
|
+
if (this.highlightEngine) {
|
|
7515
|
+
output2 += this.highlightEngine.renderLine(lineIdx, line, cols);
|
|
7516
|
+
} else {
|
|
7517
|
+
output2 += line.length > cols ? line.slice(0, cols) : line;
|
|
7518
|
+
}
|
|
8026
7519
|
}
|
|
8027
7520
|
}
|
|
8028
7521
|
output2 += `\x1B[${rows - 1};1H`;
|
|
@@ -8101,6 +7594,135 @@ var NanoEditorRenderer = class {
|
|
|
8101
7594
|
}
|
|
8102
7595
|
};
|
|
8103
7596
|
|
|
7597
|
+
// src/lib/editor/syntax/utils.ts
|
|
7598
|
+
function truncateAnsi(str, maxCols) {
|
|
7599
|
+
let visible = 0;
|
|
7600
|
+
let i = 0;
|
|
7601
|
+
let result = "";
|
|
7602
|
+
let hasActiveColor = false;
|
|
7603
|
+
while (i < str.length && visible < maxCols) {
|
|
7604
|
+
if (str[i] === "\x1B" && i + 1 < str.length && str[i + 1] === "[") {
|
|
7605
|
+
let j = i + 2;
|
|
7606
|
+
while (j < str.length && !(str.charCodeAt(j) >= 64 && str.charCodeAt(j) <= 126)) {
|
|
7607
|
+
j++;
|
|
7608
|
+
}
|
|
7609
|
+
if (j < str.length) {
|
|
7610
|
+
const seq = str.slice(i, j + 1);
|
|
7611
|
+
result += seq;
|
|
7612
|
+
if (seq === "\x1B[0m") {
|
|
7613
|
+
hasActiveColor = false;
|
|
7614
|
+
} else {
|
|
7615
|
+
hasActiveColor = true;
|
|
7616
|
+
}
|
|
7617
|
+
i = j + 1;
|
|
7618
|
+
} else {
|
|
7619
|
+
break;
|
|
7620
|
+
}
|
|
7621
|
+
} else {
|
|
7622
|
+
result += str[i];
|
|
7623
|
+
visible++;
|
|
7624
|
+
i++;
|
|
7625
|
+
}
|
|
7626
|
+
}
|
|
7627
|
+
if (hasActiveColor) {
|
|
7628
|
+
result += "\x1B[0m";
|
|
7629
|
+
}
|
|
7630
|
+
return result;
|
|
7631
|
+
}
|
|
7632
|
+
function parseTokens(raw) {
|
|
7633
|
+
if (!raw) return [];
|
|
7634
|
+
return raw.split("\n").map((entry) => {
|
|
7635
|
+
const [s, e, ...rest] = entry.split(",");
|
|
7636
|
+
return { start: parseInt(s, 10), end: parseInt(e, 10), tokenType: rest.join(",") };
|
|
7637
|
+
});
|
|
7638
|
+
}
|
|
7639
|
+
|
|
7640
|
+
// src/lib/editor/syntax/engine.ts
|
|
7641
|
+
var SyntaxHighlightEngine = class {
|
|
7642
|
+
constructor(highlighter, theme, accelerator2) {
|
|
7643
|
+
this.highlighter = highlighter;
|
|
7644
|
+
this.theme = theme;
|
|
7645
|
+
this.accelerator = accelerator2;
|
|
7646
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
7647
|
+
this.registeredRuleSets = /* @__PURE__ */ new Set();
|
|
7648
|
+
}
|
|
7649
|
+
invalidate(fromLine) {
|
|
7650
|
+
for (const key of this.cache.keys()) {
|
|
7651
|
+
if (key >= fromLine) {
|
|
7652
|
+
this.cache.delete(key);
|
|
7653
|
+
}
|
|
7654
|
+
}
|
|
7655
|
+
}
|
|
7656
|
+
renderLine(lineIdx, line, cols) {
|
|
7657
|
+
const entryState = lineIdx === 0 ? this.highlighter.initialState : this.cache.get(lineIdx - 1)?.exitState ?? this.highlighter.initialState;
|
|
7658
|
+
const cached = this.cache.get(lineIdx);
|
|
7659
|
+
if (cached && cached.lineContent === line && cached.entryState === entryState) {
|
|
7660
|
+
return truncateAnsi(cached.rendered, cols);
|
|
7661
|
+
}
|
|
7662
|
+
try {
|
|
7663
|
+
const ruleSetId = `${this.highlighter.id}:${entryState}`;
|
|
7664
|
+
if (!this.registeredRuleSets.has(ruleSetId)) {
|
|
7665
|
+
const rules = this.highlighter.getRules(entryState);
|
|
7666
|
+
const serialized = rules.map(
|
|
7667
|
+
(r) => `${r.pattern.source} ${r.tokenType} ${r.captureGroup ?? 0}`
|
|
7668
|
+
).join("\n");
|
|
7669
|
+
this.accelerator.registerRuleSet(ruleSetId, serialized);
|
|
7670
|
+
this.registeredRuleSets.add(ruleSetId);
|
|
7671
|
+
}
|
|
7672
|
+
const raw = this.accelerator.tokenizeLine(line, ruleSetId);
|
|
7673
|
+
const tokens = parseTokens(raw);
|
|
7674
|
+
const exitState = this.highlighter.getNextLineState(line, entryState);
|
|
7675
|
+
const rendered = this.applyTheme(line, tokens);
|
|
7676
|
+
this.cache.set(lineIdx, { lineContent: line, entryState, exitState, rendered });
|
|
7677
|
+
return truncateAnsi(rendered, cols);
|
|
7678
|
+
} catch {
|
|
7679
|
+
return line.length > cols ? line.slice(0, cols) : line;
|
|
7680
|
+
}
|
|
7681
|
+
}
|
|
7682
|
+
applyTheme(line, tokens) {
|
|
7683
|
+
if (tokens.length === 0) return line;
|
|
7684
|
+
let result = "";
|
|
7685
|
+
let pos = 0;
|
|
7686
|
+
for (const token of tokens) {
|
|
7687
|
+
if (token.start > pos) {
|
|
7688
|
+
result += line.slice(pos, token.start);
|
|
7689
|
+
}
|
|
7690
|
+
const color = this.theme.colors[token.tokenType];
|
|
7691
|
+
const text = line.slice(token.start, token.end);
|
|
7692
|
+
if (color) {
|
|
7693
|
+
result += color + text + "\x1B[0m";
|
|
7694
|
+
} else {
|
|
7695
|
+
result += text;
|
|
7696
|
+
}
|
|
7697
|
+
pos = token.end;
|
|
7698
|
+
}
|
|
7699
|
+
if (pos < line.length) {
|
|
7700
|
+
result += line.slice(pos);
|
|
7701
|
+
}
|
|
7702
|
+
return result;
|
|
7703
|
+
}
|
|
7704
|
+
};
|
|
7705
|
+
|
|
7706
|
+
// src/lib/editor/syntax/theme.ts
|
|
7707
|
+
var defaultSyntaxTheme = {
|
|
7708
|
+
name: "default",
|
|
7709
|
+
colors: {
|
|
7710
|
+
keyword: "\x1B[93m",
|
|
7711
|
+
string: "\x1B[32m",
|
|
7712
|
+
number: "\x1B[36m",
|
|
7713
|
+
comment: "\x1B[90m",
|
|
7714
|
+
tag: "\x1B[91m",
|
|
7715
|
+
attribute: "\x1B[33m",
|
|
7716
|
+
value: "\x1B[36m",
|
|
7717
|
+
punctuation: "\x1B[97m",
|
|
7718
|
+
heading: "\x1B[1;93m",
|
|
7719
|
+
link: "\x1B[4;36m",
|
|
7720
|
+
bold: "\x1B[1m",
|
|
7721
|
+
italic: "\x1B[3m",
|
|
7722
|
+
code: "\x1B[90;47m"
|
|
7723
|
+
}
|
|
7724
|
+
};
|
|
7725
|
+
|
|
8104
7726
|
// src/lib/processors/system/cli-nano-command-processor.ts
|
|
8105
7727
|
var FS_TOKEN = "cli-file-system-service";
|
|
8106
7728
|
var CliNanoCommandProcessor = class {
|
|
@@ -8168,6 +7790,7 @@ var CliNanoCommandProcessor = class {
|
|
|
8168
7790
|
} else {
|
|
8169
7791
|
this.filePath = null;
|
|
8170
7792
|
}
|
|
7793
|
+
this.setupHighlighting(this.filePath);
|
|
8171
7794
|
context2.enterFullScreenMode(this);
|
|
8172
7795
|
this.renderer.render(this.buffer, this.filePath || "New Buffer");
|
|
8173
7796
|
this.resizeDisposable = context2.terminal.onResize(() => {
|
|
@@ -8296,11 +7919,13 @@ var CliNanoCommandProcessor = class {
|
|
|
8296
7919
|
}
|
|
8297
7920
|
if (data === "\v") {
|
|
8298
7921
|
this.buffer.deleteLine();
|
|
7922
|
+
this.renderer.highlightEngine?.invalidate(this.buffer.cursorRow);
|
|
8299
7923
|
this.render();
|
|
8300
7924
|
return;
|
|
8301
7925
|
}
|
|
8302
7926
|
if (data === "") {
|
|
8303
7927
|
if (this.buffer.uncutLines()) {
|
|
7928
|
+
this.renderer.highlightEngine?.invalidate(this.buffer.cursorRow);
|
|
8304
7929
|
this.render();
|
|
8305
7930
|
} else {
|
|
8306
7931
|
this.showStatus("Clipboard is empty");
|
|
@@ -8361,11 +7986,16 @@ var CliNanoCommandProcessor = class {
|
|
|
8361
7986
|
}
|
|
8362
7987
|
if (data === "\r") {
|
|
8363
7988
|
this.buffer.insertNewline();
|
|
7989
|
+
this.renderer.highlightEngine?.invalidate(this.buffer.cursorRow - 1);
|
|
8364
7990
|
this.render();
|
|
8365
7991
|
return;
|
|
8366
7992
|
}
|
|
8367
7993
|
if (data === "\x7F") {
|
|
7994
|
+
const wasMerge = this.buffer.cursorCol === 0 && this.buffer.cursorRow > 0;
|
|
8368
7995
|
this.buffer.deleteCharBefore();
|
|
7996
|
+
if (wasMerge) {
|
|
7997
|
+
this.renderer.highlightEngine?.invalidate(this.buffer.cursorRow);
|
|
7998
|
+
}
|
|
8369
7999
|
this.render();
|
|
8370
8000
|
return;
|
|
8371
8001
|
}
|
|
@@ -8458,6 +8088,31 @@ var CliNanoCommandProcessor = class {
|
|
|
8458
8088
|
` ${writer.wrapInColor("^Y", cliCore.CliForegroundColor.Yellow)} Page Up ${writer.wrapInColor("^V", cliCore.CliForegroundColor.Yellow)} Page Down`
|
|
8459
8089
|
);
|
|
8460
8090
|
}
|
|
8091
|
+
// ── Syntax highlighting ──
|
|
8092
|
+
setupHighlighting(filePath) {
|
|
8093
|
+
if (!filePath) {
|
|
8094
|
+
this.renderer.highlightEngine = void 0;
|
|
8095
|
+
return;
|
|
8096
|
+
}
|
|
8097
|
+
try {
|
|
8098
|
+
const registry = this.context.services.get(
|
|
8099
|
+
"syntax-highlighter-registry"
|
|
8100
|
+
);
|
|
8101
|
+
const ext = "." + filePath.split(".").pop()?.toLowerCase();
|
|
8102
|
+
const highlighter = registry.getByExtension(ext);
|
|
8103
|
+
if (highlighter) {
|
|
8104
|
+
this.renderer.highlightEngine = new SyntaxHighlightEngine(
|
|
8105
|
+
highlighter,
|
|
8106
|
+
defaultSyntaxTheme,
|
|
8107
|
+
getAccelerator()
|
|
8108
|
+
);
|
|
8109
|
+
} else {
|
|
8110
|
+
this.renderer.highlightEngine = void 0;
|
|
8111
|
+
}
|
|
8112
|
+
} catch {
|
|
8113
|
+
this.renderer.highlightEngine = void 0;
|
|
8114
|
+
}
|
|
8115
|
+
}
|
|
8461
8116
|
// ── Rendering helpers ──
|
|
8462
8117
|
render() {
|
|
8463
8118
|
this.renderer.render(
|
|
@@ -8995,17 +8650,23 @@ var CliProcessRegistry = class {
|
|
|
8995
8650
|
get currentPid() {
|
|
8996
8651
|
return this._currentPid;
|
|
8997
8652
|
}
|
|
8998
|
-
register(command2) {
|
|
8653
|
+
register(command2, options) {
|
|
8999
8654
|
const pid = this.nextPid++;
|
|
9000
8655
|
const abortController = new AbortController();
|
|
8656
|
+
const type = options?.type ?? "command";
|
|
9001
8657
|
this.processes.set(pid, {
|
|
9002
8658
|
pid,
|
|
8659
|
+
name: options?.name ?? command2,
|
|
8660
|
+
type,
|
|
9003
8661
|
command: command2,
|
|
9004
8662
|
startTime: Date.now(),
|
|
9005
8663
|
status: "running",
|
|
9006
|
-
abortController
|
|
8664
|
+
abortController,
|
|
8665
|
+
onKill: options?.onKill
|
|
9007
8666
|
});
|
|
9008
|
-
|
|
8667
|
+
if (type === "command") {
|
|
8668
|
+
this._currentPid = pid;
|
|
8669
|
+
}
|
|
9009
8670
|
this.pruneHistory();
|
|
9010
8671
|
return { pid, abortController };
|
|
9011
8672
|
}
|
|
@@ -9032,6 +8693,10 @@ var CliProcessRegistry = class {
|
|
|
9032
8693
|
kill(pid) {
|
|
9033
8694
|
const entry = this.processes.get(pid);
|
|
9034
8695
|
if (!entry || entry.status !== "running") return false;
|
|
8696
|
+
if (entry.onKill) {
|
|
8697
|
+
Promise.resolve(entry.onKill()).catch(() => {
|
|
8698
|
+
});
|
|
8699
|
+
}
|
|
9035
8700
|
entry.abortController.abort();
|
|
9036
8701
|
entry.status = "killed";
|
|
9037
8702
|
entry.exitCode = -9;
|
|
@@ -9041,7 +8706,9 @@ var CliProcessRegistry = class {
|
|
|
9041
8706
|
return true;
|
|
9042
8707
|
}
|
|
9043
8708
|
list() {
|
|
9044
|
-
return Array.from(this.processes.values()).map(
|
|
8709
|
+
return Array.from(this.processes.values()).map(
|
|
8710
|
+
({ abortController, onKill, ...entry }) => entry
|
|
8711
|
+
);
|
|
9045
8712
|
}
|
|
9046
8713
|
pruneHistory() {
|
|
9047
8714
|
const entries = Array.from(this.processes.entries());
|
|
@@ -9078,11 +8745,13 @@ var CliPsCommandProcessor = class {
|
|
|
9078
8745
|
context2.writer.writeInfo("No processes");
|
|
9079
8746
|
return;
|
|
9080
8747
|
}
|
|
9081
|
-
const headers = ["PID", "STATUS", "EXIT", "TIME", "COMMAND"];
|
|
8748
|
+
const headers = ["PID", "NAME", "TYPE", "STATUS", "EXIT", "TIME", "COMMAND"];
|
|
9082
8749
|
const rows = processes.map((p) => {
|
|
9083
8750
|
const elapsed = `${Math.round((Date.now() - p.startTime) / 1e3)}s`;
|
|
9084
8751
|
return [
|
|
9085
8752
|
String(p.pid),
|
|
8753
|
+
p.name,
|
|
8754
|
+
p.type,
|
|
9086
8755
|
p.status,
|
|
9087
8756
|
p.exitCode !== void 0 ? String(p.exitCode) : "-",
|
|
9088
8757
|
elapsed,
|
|
@@ -9127,37 +8796,585 @@ var CliKillCommandProcessor = class {
|
|
|
9127
8796
|
}
|
|
9128
8797
|
}
|
|
9129
8798
|
};
|
|
9130
|
-
|
|
9131
|
-
|
|
9132
|
-
var
|
|
9133
|
-
|
|
9134
|
-
|
|
9135
|
-
|
|
9136
|
-
|
|
9137
|
-
|
|
9138
|
-
|
|
9139
|
-
|
|
9140
|
-
|
|
9141
|
-
|
|
9142
|
-
|
|
9143
|
-
|
|
9144
|
-
|
|
9145
|
-
|
|
9146
|
-
|
|
8799
|
+
var ESC = "\x1B";
|
|
8800
|
+
var CSI = `${ESC}[`;
|
|
8801
|
+
var ansi = {
|
|
8802
|
+
clearScreen: `${CSI}2J`,
|
|
8803
|
+
cursorHome: `${CSI}H`,
|
|
8804
|
+
hideCursor: `${CSI}?25l`,
|
|
8805
|
+
cursorTo: (row, col) => `${CSI}${row};${col}H`,
|
|
8806
|
+
fg: {
|
|
8807
|
+
green: `${CSI}32m`,
|
|
8808
|
+
cyan: `${CSI}36m`,
|
|
8809
|
+
yellow: `${CSI}93m`,
|
|
8810
|
+
white: `${CSI}97m`,
|
|
8811
|
+
red: `${CSI}91m`,
|
|
8812
|
+
gray: `${CSI}90m`
|
|
8813
|
+
},
|
|
8814
|
+
bg: {
|
|
8815
|
+
blue: `${CSI}44m`
|
|
8816
|
+
},
|
|
8817
|
+
bold: `${CSI}1m`,
|
|
8818
|
+
dim: `${CSI}2m`,
|
|
8819
|
+
inverse: `${CSI}7m`,
|
|
8820
|
+
reset: `${CSI}0m`
|
|
9147
8821
|
};
|
|
9148
|
-
var
|
|
9149
|
-
|
|
8822
|
+
var BOX = {
|
|
8823
|
+
topLeft: "\u250C",
|
|
8824
|
+
topRight: "\u2510",
|
|
8825
|
+
bottomLeft: "\u2514",
|
|
8826
|
+
bottomRight: "\u2518",
|
|
8827
|
+
horizontal: "\u2500",
|
|
8828
|
+
vertical: "\u2502"
|
|
9150
8829
|
};
|
|
9151
|
-
|
|
9152
|
-
|
|
9153
|
-
|
|
9154
|
-
const
|
|
9155
|
-
|
|
9156
|
-
const
|
|
9157
|
-
|
|
9158
|
-
}
|
|
9159
|
-
|
|
9160
|
-
|
|
8830
|
+
function formatElapsed(ms) {
|
|
8831
|
+
const seconds = Math.floor(ms / 1e3) % 60;
|
|
8832
|
+
const minutes = Math.floor(ms / (1e3 * 60)) % 60;
|
|
8833
|
+
const hours = Math.floor(ms / (1e3 * 60 * 60)) % 24;
|
|
8834
|
+
const days = Math.floor(ms / (1e3 * 60 * 60 * 24));
|
|
8835
|
+
const parts = [];
|
|
8836
|
+
if (days > 0) parts.push(`${days}d`);
|
|
8837
|
+
if (hours > 0) parts.push(`${hours}h`);
|
|
8838
|
+
if (minutes > 0) parts.push(`${minutes}m`);
|
|
8839
|
+
parts.push(`${seconds}s`);
|
|
8840
|
+
return parts.join(" ");
|
|
8841
|
+
}
|
|
8842
|
+
function statusColor(status) {
|
|
8843
|
+
switch (status) {
|
|
8844
|
+
case "running":
|
|
8845
|
+
return ansi.fg.green;
|
|
8846
|
+
case "completed":
|
|
8847
|
+
case "done":
|
|
8848
|
+
return ansi.fg.cyan;
|
|
8849
|
+
case "pending":
|
|
8850
|
+
return ansi.fg.yellow;
|
|
8851
|
+
case "stopped":
|
|
8852
|
+
return ansi.fg.white;
|
|
8853
|
+
case "failed":
|
|
8854
|
+
case "killed":
|
|
8855
|
+
return ansi.fg.red;
|
|
8856
|
+
default:
|
|
8857
|
+
return ansi.fg.white;
|
|
8858
|
+
}
|
|
8859
|
+
}
|
|
8860
|
+
function truncate(str, maxLen) {
|
|
8861
|
+
if (str.length <= maxLen) return str;
|
|
8862
|
+
return str.substring(0, maxLen - 1) + "\u2026";
|
|
8863
|
+
}
|
|
8864
|
+
function pad(str, len) {
|
|
8865
|
+
return str.length >= len ? str.substring(0, len) : str + " ".repeat(len - str.length);
|
|
8866
|
+
}
|
|
8867
|
+
var CliHtopCommandProcessor = class {
|
|
8868
|
+
constructor() {
|
|
8869
|
+
this.command = "htop";
|
|
8870
|
+
this.description = "Interactive process and service monitor";
|
|
8871
|
+
this.author = cliCore.DefaultLibraryAuthor;
|
|
8872
|
+
this.metadata = {
|
|
8873
|
+
icon: "\u{1F4CA}",
|
|
8874
|
+
sealed: true,
|
|
8875
|
+
module: "system"
|
|
8876
|
+
};
|
|
8877
|
+
this.context = null;
|
|
8878
|
+
this.registry = null;
|
|
8879
|
+
this.refreshTimer = null;
|
|
8880
|
+
this.rows = [];
|
|
8881
|
+
this.selectedIndex = 0;
|
|
8882
|
+
this.scrollOffset = 0;
|
|
8883
|
+
this.termCols = 80;
|
|
8884
|
+
this.termRows = 24;
|
|
8885
|
+
this.showingLogs = false;
|
|
8886
|
+
this.errorMessage = null;
|
|
8887
|
+
this.errorTimeout = null;
|
|
8888
|
+
}
|
|
8889
|
+
async processCommand(_command, context2) {
|
|
8890
|
+
try {
|
|
8891
|
+
this.registry = context2.services.get(
|
|
8892
|
+
CliProcessRegistry_TOKEN
|
|
8893
|
+
);
|
|
8894
|
+
} catch {
|
|
8895
|
+
context2.writer.writeError("Process registry not available");
|
|
8896
|
+
return;
|
|
8897
|
+
}
|
|
8898
|
+
this.context = context2;
|
|
8899
|
+
this.termCols = context2.terminal.cols || 80;
|
|
8900
|
+
this.termRows = context2.terminal.rows || 24;
|
|
8901
|
+
this.selectedIndex = 0;
|
|
8902
|
+
this.scrollOffset = 0;
|
|
8903
|
+
this.showingLogs = false;
|
|
8904
|
+
this.errorMessage = null;
|
|
8905
|
+
context2.enterFullScreenMode(this);
|
|
8906
|
+
this.refreshData();
|
|
8907
|
+
this.render();
|
|
8908
|
+
this.refreshTimer = context2.createInterval(() => {
|
|
8909
|
+
if (!this.showingLogs) {
|
|
8910
|
+
this.refreshData();
|
|
8911
|
+
this.render();
|
|
8912
|
+
}
|
|
8913
|
+
}, 1e3);
|
|
8914
|
+
}
|
|
8915
|
+
async onData(data, context2) {
|
|
8916
|
+
if (this.showingLogs) {
|
|
8917
|
+
this.showingLogs = false;
|
|
8918
|
+
this.render();
|
|
8919
|
+
return;
|
|
8920
|
+
}
|
|
8921
|
+
switch (data) {
|
|
8922
|
+
case `${ESC}[A`:
|
|
8923
|
+
this.moveSelection(-1);
|
|
8924
|
+
this.render();
|
|
8925
|
+
break;
|
|
8926
|
+
case `${ESC}[B`:
|
|
8927
|
+
this.moveSelection(1);
|
|
8928
|
+
this.render();
|
|
8929
|
+
break;
|
|
8930
|
+
case "q":
|
|
8931
|
+
case ESC:
|
|
8932
|
+
this.exit(context2);
|
|
8933
|
+
break;
|
|
8934
|
+
case "k":
|
|
8935
|
+
this.actionKill();
|
|
8936
|
+
break;
|
|
8937
|
+
case "s":
|
|
8938
|
+
await this.actionStart();
|
|
8939
|
+
break;
|
|
8940
|
+
case "t":
|
|
8941
|
+
await this.actionStop();
|
|
8942
|
+
break;
|
|
8943
|
+
case "r":
|
|
8944
|
+
await this.actionRestart();
|
|
8945
|
+
break;
|
|
8946
|
+
case "l":
|
|
8947
|
+
this.actionLogs();
|
|
8948
|
+
break;
|
|
8949
|
+
}
|
|
8950
|
+
}
|
|
8951
|
+
onResize(cols, rows, _context) {
|
|
8952
|
+
this.termCols = cols;
|
|
8953
|
+
this.termRows = rows;
|
|
8954
|
+
this.clampScroll();
|
|
8955
|
+
this.render();
|
|
8956
|
+
}
|
|
8957
|
+
onDispose(_context) {
|
|
8958
|
+
this.refreshTimer = null;
|
|
8959
|
+
this.context = null;
|
|
8960
|
+
this.registry = null;
|
|
8961
|
+
this.rows = [];
|
|
8962
|
+
}
|
|
8963
|
+
writeDescription(context2) {
|
|
8964
|
+
const { writer } = context2;
|
|
8965
|
+
writer.writeln("Interactive process and service monitor");
|
|
8966
|
+
writer.writeln();
|
|
8967
|
+
writer.writeln("Usage:");
|
|
8968
|
+
writer.writeln(` ${writer.wrapInColor("htop", 36)}`);
|
|
8969
|
+
}
|
|
8970
|
+
// ── Data ─────────────────────────────────────────────────────────
|
|
8971
|
+
refreshData() {
|
|
8972
|
+
if (!this.registry || !this.context) return;
|
|
8973
|
+
const processEntries = this.registry.list();
|
|
8974
|
+
const serviceEntries = this.context.backgroundServices.list();
|
|
8975
|
+
const servicePids = /* @__PURE__ */ new Set();
|
|
8976
|
+
for (const svc of serviceEntries) {
|
|
8977
|
+
if (svc.pid !== void 0) {
|
|
8978
|
+
servicePids.add(svc.pid);
|
|
8979
|
+
}
|
|
8980
|
+
}
|
|
8981
|
+
const unified = [];
|
|
8982
|
+
for (const p of processEntries) {
|
|
8983
|
+
if (servicePids.has(p.pid)) continue;
|
|
8984
|
+
unified.push({
|
|
8985
|
+
pid: p.pid,
|
|
8986
|
+
name: p.name,
|
|
8987
|
+
type: p.type,
|
|
8988
|
+
status: p.status,
|
|
8989
|
+
time: formatElapsed(Date.now() - p.startTime),
|
|
8990
|
+
mode: "main",
|
|
8991
|
+
source: "process"
|
|
8992
|
+
});
|
|
8993
|
+
}
|
|
8994
|
+
for (const svc of serviceEntries) {
|
|
8995
|
+
let time = "-";
|
|
8996
|
+
if (svc.status === "running" && svc.startedAt) {
|
|
8997
|
+
time = formatElapsed(Date.now() - new Date(svc.startedAt).getTime());
|
|
8998
|
+
}
|
|
8999
|
+
unified.push({
|
|
9000
|
+
pid: svc.pid ?? 0,
|
|
9001
|
+
name: svc.name,
|
|
9002
|
+
type: svc.type,
|
|
9003
|
+
status: svc.status,
|
|
9004
|
+
time,
|
|
9005
|
+
mode: svc.executionMode === "worker" ? "worker" : "main",
|
|
9006
|
+
source: "service",
|
|
9007
|
+
serviceInfo: svc
|
|
9008
|
+
});
|
|
9009
|
+
}
|
|
9010
|
+
unified.sort((a, b) => {
|
|
9011
|
+
const aRunning = a.status === "running" ? 0 : 1;
|
|
9012
|
+
const bRunning = b.status === "running" ? 0 : 1;
|
|
9013
|
+
if (aRunning !== bRunning) return aRunning - bRunning;
|
|
9014
|
+
return b.pid - a.pid;
|
|
9015
|
+
});
|
|
9016
|
+
this.rows = unified;
|
|
9017
|
+
if (this.rows.length === 0) {
|
|
9018
|
+
this.selectedIndex = 0;
|
|
9019
|
+
} else {
|
|
9020
|
+
this.selectedIndex = Math.min(this.selectedIndex, this.rows.length - 1);
|
|
9021
|
+
}
|
|
9022
|
+
this.clampScroll();
|
|
9023
|
+
}
|
|
9024
|
+
// ── Actions ──────────────────────────────────────────────────────
|
|
9025
|
+
actionKill() {
|
|
9026
|
+
const row = this.rows[this.selectedIndex];
|
|
9027
|
+
if (!row || row.status !== "running") return;
|
|
9028
|
+
if (row.name === "htop" && row.source === "process") {
|
|
9029
|
+
if (this.context) this.exit(this.context);
|
|
9030
|
+
return;
|
|
9031
|
+
}
|
|
9032
|
+
try {
|
|
9033
|
+
if (this.registry) {
|
|
9034
|
+
this.registry.kill(row.pid);
|
|
9035
|
+
}
|
|
9036
|
+
} catch (e) {
|
|
9037
|
+
this.showError(e?.message || "Kill failed");
|
|
9038
|
+
}
|
|
9039
|
+
this.refreshData();
|
|
9040
|
+
this.render();
|
|
9041
|
+
}
|
|
9042
|
+
async actionStart() {
|
|
9043
|
+
const row = this.rows[this.selectedIndex];
|
|
9044
|
+
if (!row || row.source !== "service") return;
|
|
9045
|
+
if (row.status !== "stopped" && row.status !== "pending" && row.status !== "failed") return;
|
|
9046
|
+
try {
|
|
9047
|
+
await this.context.backgroundServices.start(row.name);
|
|
9048
|
+
} catch (e) {
|
|
9049
|
+
this.showError(e?.message || "Start failed");
|
|
9050
|
+
}
|
|
9051
|
+
this.refreshData();
|
|
9052
|
+
this.render();
|
|
9053
|
+
}
|
|
9054
|
+
async actionStop() {
|
|
9055
|
+
const row = this.rows[this.selectedIndex];
|
|
9056
|
+
if (!row || row.source !== "service" || row.status !== "running") return;
|
|
9057
|
+
try {
|
|
9058
|
+
await this.context.backgroundServices.stop(row.name);
|
|
9059
|
+
} catch (e) {
|
|
9060
|
+
this.showError(e?.message || "Stop failed");
|
|
9061
|
+
}
|
|
9062
|
+
this.refreshData();
|
|
9063
|
+
this.render();
|
|
9064
|
+
}
|
|
9065
|
+
async actionRestart() {
|
|
9066
|
+
const row = this.rows[this.selectedIndex];
|
|
9067
|
+
if (!row || row.source !== "service") return;
|
|
9068
|
+
try {
|
|
9069
|
+
await this.context.backgroundServices.restart(row.name);
|
|
9070
|
+
} catch (e) {
|
|
9071
|
+
this.showError(e?.message || "Restart failed");
|
|
9072
|
+
}
|
|
9073
|
+
this.refreshData();
|
|
9074
|
+
this.render();
|
|
9075
|
+
}
|
|
9076
|
+
actionLogs() {
|
|
9077
|
+
const row = this.rows[this.selectedIndex];
|
|
9078
|
+
if (!row || row.source !== "service" || !this.context) return;
|
|
9079
|
+
this.showingLogs = true;
|
|
9080
|
+
this.renderLogsOverlay(row.name);
|
|
9081
|
+
}
|
|
9082
|
+
// ── Navigation ───────────────────────────────────────────────────
|
|
9083
|
+
moveSelection(delta) {
|
|
9084
|
+
if (this.rows.length === 0) return;
|
|
9085
|
+
this.selectedIndex += delta;
|
|
9086
|
+
if (this.selectedIndex < 0) {
|
|
9087
|
+
this.selectedIndex = this.rows.length - 1;
|
|
9088
|
+
} else if (this.selectedIndex >= this.rows.length) {
|
|
9089
|
+
this.selectedIndex = 0;
|
|
9090
|
+
}
|
|
9091
|
+
this.clampScroll();
|
|
9092
|
+
}
|
|
9093
|
+
get visibleRows() {
|
|
9094
|
+
return Math.max(1, this.termRows - 4);
|
|
9095
|
+
}
|
|
9096
|
+
clampScroll() {
|
|
9097
|
+
if (this.selectedIndex < this.scrollOffset) {
|
|
9098
|
+
this.scrollOffset = this.selectedIndex;
|
|
9099
|
+
} else if (this.selectedIndex >= this.scrollOffset + this.visibleRows) {
|
|
9100
|
+
this.scrollOffset = this.selectedIndex - this.visibleRows + 1;
|
|
9101
|
+
}
|
|
9102
|
+
this.scrollOffset = Math.max(0, this.scrollOffset);
|
|
9103
|
+
}
|
|
9104
|
+
// ── Rendering ────────────────────────────────────────────────────
|
|
9105
|
+
render() {
|
|
9106
|
+
if (!this.context) return;
|
|
9107
|
+
const buf = [];
|
|
9108
|
+
const cols = this.termCols;
|
|
9109
|
+
buf.push(ansi.clearScreen, ansi.cursorHome, ansi.hideCursor);
|
|
9110
|
+
const runningCount = this.rows.filter((r) => r.status === "running").length;
|
|
9111
|
+
const totalCount = this.rows.length;
|
|
9112
|
+
let headerLeft;
|
|
9113
|
+
if (this.errorMessage) {
|
|
9114
|
+
headerLeft = `${ansi.bold}${ansi.fg.red} htop ${ansi.reset}${ansi.fg.red}${ansi.dim} ${this.errorMessage}${ansi.reset}`;
|
|
9115
|
+
} else {
|
|
9116
|
+
headerLeft = `${ansi.bold}${ansi.fg.cyan} htop${ansi.reset}${ansi.fg.gray} \u2014 ${runningCount} running / ${totalCount} total${ansi.reset}`;
|
|
9117
|
+
}
|
|
9118
|
+
const headerRight = `${ansi.fg.gray}Refresh: 1s${ansi.reset}`;
|
|
9119
|
+
buf.push(ansi.cursorTo(1, 1));
|
|
9120
|
+
buf.push(headerLeft);
|
|
9121
|
+
buf.push(ansi.cursorTo(1, Math.max(1, cols - 11)));
|
|
9122
|
+
buf.push(headerRight);
|
|
9123
|
+
buf.push(ansi.cursorTo(2, 1));
|
|
9124
|
+
buf.push(ansi.fg.gray, BOX.horizontal.repeat(cols), ansi.reset);
|
|
9125
|
+
const colPid = 6;
|
|
9126
|
+
const colType = 9;
|
|
9127
|
+
const colStatus = 11;
|
|
9128
|
+
const colMode = 8;
|
|
9129
|
+
const colTime = 9;
|
|
9130
|
+
const colName = Math.max(10, cols - colPid - colType - colStatus - colMode - colTime - 2);
|
|
9131
|
+
buf.push(ansi.cursorTo(3, 1));
|
|
9132
|
+
buf.push(
|
|
9133
|
+
ansi.bold,
|
|
9134
|
+
ansi.fg.white,
|
|
9135
|
+
" ",
|
|
9136
|
+
pad("PID", colPid),
|
|
9137
|
+
pad("NAME", colName),
|
|
9138
|
+
pad("TYPE", colType),
|
|
9139
|
+
pad("STATUS", colStatus),
|
|
9140
|
+
pad("MODE", colMode),
|
|
9141
|
+
pad("TIME", colTime),
|
|
9142
|
+
ansi.reset
|
|
9143
|
+
);
|
|
9144
|
+
if (this.rows.length === 0) {
|
|
9145
|
+
const emptyMsg = "No processes";
|
|
9146
|
+
const emptyRow = Math.floor(this.visibleRows / 2) + 4;
|
|
9147
|
+
const emptyCol = Math.max(1, Math.floor((cols - emptyMsg.length) / 2));
|
|
9148
|
+
buf.push(ansi.cursorTo(emptyRow, emptyCol));
|
|
9149
|
+
buf.push(ansi.fg.gray, emptyMsg, ansi.reset);
|
|
9150
|
+
} else {
|
|
9151
|
+
const endIdx = Math.min(
|
|
9152
|
+
this.scrollOffset + this.visibleRows,
|
|
9153
|
+
this.rows.length
|
|
9154
|
+
);
|
|
9155
|
+
for (let i = this.scrollOffset; i < endIdx; i++) {
|
|
9156
|
+
const row = this.rows[i];
|
|
9157
|
+
const screenRow = 4 + (i - this.scrollOffset);
|
|
9158
|
+
const isSelected = i === this.selectedIndex;
|
|
9159
|
+
const prefix = isSelected ? ">" : " ";
|
|
9160
|
+
const pidStr = pad(String(row.pid), colPid);
|
|
9161
|
+
const nameStr = pad(truncate(row.name, colName - 1), colName);
|
|
9162
|
+
const typeStr = pad(row.type, colType);
|
|
9163
|
+
const statusStr = pad(row.status, colStatus);
|
|
9164
|
+
const modeStr = pad(row.mode, colMode);
|
|
9165
|
+
const timeStr = pad(row.time, colTime);
|
|
9166
|
+
buf.push(ansi.cursorTo(screenRow, 1));
|
|
9167
|
+
if (isSelected) {
|
|
9168
|
+
buf.push(ansi.inverse);
|
|
9169
|
+
}
|
|
9170
|
+
buf.push(
|
|
9171
|
+
prefix,
|
|
9172
|
+
pidStr,
|
|
9173
|
+
nameStr,
|
|
9174
|
+
typeStr,
|
|
9175
|
+
isSelected ? "" : statusColor(row.status),
|
|
9176
|
+
statusStr,
|
|
9177
|
+
isSelected ? "" : ansi.reset,
|
|
9178
|
+
modeStr,
|
|
9179
|
+
timeStr
|
|
9180
|
+
);
|
|
9181
|
+
const contentLen = 1 + colPid + colName + colType + colStatus + colMode + colTime;
|
|
9182
|
+
if (contentLen < cols) {
|
|
9183
|
+
buf.push(" ".repeat(cols - contentLen));
|
|
9184
|
+
}
|
|
9185
|
+
if (isSelected) {
|
|
9186
|
+
buf.push(ansi.reset);
|
|
9187
|
+
}
|
|
9188
|
+
}
|
|
9189
|
+
if (this.scrollOffset > 0) {
|
|
9190
|
+
buf.push(ansi.cursorTo(4, cols));
|
|
9191
|
+
buf.push(ansi.fg.gray, "\u2191", ansi.reset);
|
|
9192
|
+
}
|
|
9193
|
+
if (endIdx < this.rows.length) {
|
|
9194
|
+
buf.push(ansi.cursorTo(3 + this.visibleRows, cols));
|
|
9195
|
+
buf.push(ansi.fg.gray, "\u2193", ansi.reset);
|
|
9196
|
+
}
|
|
9197
|
+
}
|
|
9198
|
+
const footerSepRow = this.termRows - 1;
|
|
9199
|
+
buf.push(ansi.cursorTo(footerSepRow, 1));
|
|
9200
|
+
buf.push(ansi.fg.gray, BOX.horizontal.repeat(cols), ansi.reset);
|
|
9201
|
+
buf.push(ansi.cursorTo(this.termRows, 1));
|
|
9202
|
+
buf.push(ansi.dim, " ", this.buildFooter(), ansi.reset);
|
|
9203
|
+
this.context.terminal.write(buf.join(""));
|
|
9204
|
+
}
|
|
9205
|
+
buildFooter() {
|
|
9206
|
+
const selected = this.rows[this.selectedIndex];
|
|
9207
|
+
const parts = [];
|
|
9208
|
+
if (selected) {
|
|
9209
|
+
if (selected.status === "running") {
|
|
9210
|
+
parts.push("k:Kill");
|
|
9211
|
+
}
|
|
9212
|
+
if (selected.source === "service" && (selected.status === "stopped" || selected.status === "pending" || selected.status === "failed")) {
|
|
9213
|
+
parts.push("s:Start");
|
|
9214
|
+
}
|
|
9215
|
+
if (selected.source === "service" && selected.status === "running") {
|
|
9216
|
+
parts.push("t:Stop");
|
|
9217
|
+
}
|
|
9218
|
+
if (selected.source === "service") {
|
|
9219
|
+
parts.push("r:Restart");
|
|
9220
|
+
parts.push("l:Logs");
|
|
9221
|
+
}
|
|
9222
|
+
}
|
|
9223
|
+
parts.push("q:Quit");
|
|
9224
|
+
parts.push("\u2191\u2193:Navigate");
|
|
9225
|
+
return parts.join(" ");
|
|
9226
|
+
}
|
|
9227
|
+
// ── Logs overlay ─────────────────────────────────────────────
|
|
9228
|
+
renderLogsOverlay(serviceName) {
|
|
9229
|
+
if (!this.context) return;
|
|
9230
|
+
const logs = this.context.backgroundServices.getLogs(serviceName, 15);
|
|
9231
|
+
const buf = [];
|
|
9232
|
+
const boxWidth = Math.min(this.termCols - 4, 70);
|
|
9233
|
+
const boxHeight = Math.min(this.termRows - 4, logs.length + 4);
|
|
9234
|
+
const startCol = Math.max(1, Math.floor((this.termCols - boxWidth) / 2));
|
|
9235
|
+
const startRow = Math.max(1, Math.floor((this.termRows - boxHeight) / 2));
|
|
9236
|
+
const innerWidth = boxWidth - 2;
|
|
9237
|
+
const title = ` Logs: ${truncate(serviceName, innerWidth - 10)} `;
|
|
9238
|
+
const topBorder = BOX.topLeft + BOX.horizontal + title + BOX.horizontal.repeat(Math.max(0, boxWidth - title.length - 3)) + BOX.topRight;
|
|
9239
|
+
buf.push(ansi.cursorTo(startRow, startCol));
|
|
9240
|
+
buf.push(ansi.fg.cyan, topBorder, ansi.reset);
|
|
9241
|
+
const contentRows = boxHeight - 3;
|
|
9242
|
+
if (logs.length === 0) {
|
|
9243
|
+
const noLogs = "No log entries";
|
|
9244
|
+
buf.push(ansi.cursorTo(startRow + 1, startCol));
|
|
9245
|
+
buf.push(
|
|
9246
|
+
ansi.fg.cyan,
|
|
9247
|
+
BOX.vertical,
|
|
9248
|
+
ansi.reset,
|
|
9249
|
+
" ",
|
|
9250
|
+
ansi.fg.gray,
|
|
9251
|
+
pad(noLogs, innerWidth - 1),
|
|
9252
|
+
ansi.reset,
|
|
9253
|
+
ansi.fg.cyan,
|
|
9254
|
+
BOX.vertical,
|
|
9255
|
+
ansi.reset
|
|
9256
|
+
);
|
|
9257
|
+
for (let r = 2; r <= contentRows; r++) {
|
|
9258
|
+
buf.push(ansi.cursorTo(startRow + r, startCol));
|
|
9259
|
+
buf.push(
|
|
9260
|
+
ansi.fg.cyan,
|
|
9261
|
+
BOX.vertical,
|
|
9262
|
+
ansi.reset,
|
|
9263
|
+
" ".repeat(innerWidth),
|
|
9264
|
+
ansi.fg.cyan,
|
|
9265
|
+
BOX.vertical,
|
|
9266
|
+
ansi.reset
|
|
9267
|
+
);
|
|
9268
|
+
}
|
|
9269
|
+
} else {
|
|
9270
|
+
const displayLogs = logs.slice(-contentRows);
|
|
9271
|
+
let lineIdx = 0;
|
|
9272
|
+
for (const entry of displayLogs) {
|
|
9273
|
+
const ts = new Date(entry.timestamp).toLocaleTimeString();
|
|
9274
|
+
const levelTag = entry.level === "error" ? `${ansi.fg.red}ERR${ansi.reset}` : entry.level === "warn" ? `${ansi.fg.yellow}WRN${ansi.reset}` : `${ansi.fg.cyan}INF${ansi.reset}`;
|
|
9275
|
+
const line = `[${ts}] ${levelTag} ${truncate(entry.message, innerWidth - 18)}`;
|
|
9276
|
+
buf.push(ansi.cursorTo(startRow + 1 + lineIdx, startCol));
|
|
9277
|
+
buf.push(
|
|
9278
|
+
ansi.fg.cyan,
|
|
9279
|
+
BOX.vertical,
|
|
9280
|
+
ansi.reset,
|
|
9281
|
+
" ",
|
|
9282
|
+
line
|
|
9283
|
+
);
|
|
9284
|
+
buf.push(ansi.cursorTo(startRow + 1 + lineIdx, startCol + boxWidth - 1));
|
|
9285
|
+
buf.push(ansi.fg.cyan, BOX.vertical, ansi.reset);
|
|
9286
|
+
lineIdx++;
|
|
9287
|
+
}
|
|
9288
|
+
for (let r = lineIdx; r < contentRows; r++) {
|
|
9289
|
+
buf.push(ansi.cursorTo(startRow + 1 + r, startCol));
|
|
9290
|
+
buf.push(
|
|
9291
|
+
ansi.fg.cyan,
|
|
9292
|
+
BOX.vertical,
|
|
9293
|
+
ansi.reset,
|
|
9294
|
+
" ".repeat(innerWidth),
|
|
9295
|
+
ansi.fg.cyan,
|
|
9296
|
+
BOX.vertical,
|
|
9297
|
+
ansi.reset
|
|
9298
|
+
);
|
|
9299
|
+
}
|
|
9300
|
+
}
|
|
9301
|
+
const pressMsg = "Press any key to close";
|
|
9302
|
+
const pressPad = Math.max(0, Math.floor((innerWidth - pressMsg.length) / 2));
|
|
9303
|
+
buf.push(ansi.cursorTo(startRow + contentRows + 1, startCol));
|
|
9304
|
+
buf.push(
|
|
9305
|
+
ansi.fg.cyan,
|
|
9306
|
+
BOX.vertical,
|
|
9307
|
+
ansi.reset,
|
|
9308
|
+
" ".repeat(pressPad),
|
|
9309
|
+
ansi.fg.gray,
|
|
9310
|
+
pressMsg,
|
|
9311
|
+
ansi.reset,
|
|
9312
|
+
" ".repeat(Math.max(0, innerWidth - pressPad - pressMsg.length)),
|
|
9313
|
+
ansi.fg.cyan,
|
|
9314
|
+
BOX.vertical,
|
|
9315
|
+
ansi.reset
|
|
9316
|
+
);
|
|
9317
|
+
buf.push(ansi.cursorTo(startRow + contentRows + 2, startCol));
|
|
9318
|
+
buf.push(
|
|
9319
|
+
ansi.fg.cyan,
|
|
9320
|
+
BOX.bottomLeft,
|
|
9321
|
+
BOX.horizontal.repeat(boxWidth - 2),
|
|
9322
|
+
BOX.bottomRight,
|
|
9323
|
+
ansi.reset
|
|
9324
|
+
);
|
|
9325
|
+
this.context.terminal.write(buf.join(""));
|
|
9326
|
+
}
|
|
9327
|
+
// ── Utilities ────────────────────────────────────────────────
|
|
9328
|
+
showError(msg) {
|
|
9329
|
+
this.errorMessage = msg;
|
|
9330
|
+
if (this.errorTimeout) {
|
|
9331
|
+
this.errorTimeout.clear();
|
|
9332
|
+
}
|
|
9333
|
+
if (this.context) {
|
|
9334
|
+
this.errorTimeout = this.context.createInterval(() => {
|
|
9335
|
+
this.errorMessage = null;
|
|
9336
|
+
this.errorTimeout?.clear();
|
|
9337
|
+
this.errorTimeout = null;
|
|
9338
|
+
}, 3e3);
|
|
9339
|
+
}
|
|
9340
|
+
}
|
|
9341
|
+
exit(context2) {
|
|
9342
|
+
context2.exitFullScreenMode();
|
|
9343
|
+
this.context = null;
|
|
9344
|
+
}
|
|
9345
|
+
};
|
|
9346
|
+
|
|
9347
|
+
// src/lib/processors/system/index.ts
|
|
9348
|
+
var systemProcessors = [
|
|
9349
|
+
new CliHelpCommandProcessor(),
|
|
9350
|
+
new CliVersionCommandProcessor(),
|
|
9351
|
+
new CliFeedbackCommandProcessor(),
|
|
9352
|
+
new CliHistoryCommandProcessor(),
|
|
9353
|
+
new CliPackagesCommandProcessor(),
|
|
9354
|
+
new CliHotKeysCommandProcessor(),
|
|
9355
|
+
new CliDebugCommandProcessor(),
|
|
9356
|
+
new CliNanoCommandProcessor(),
|
|
9357
|
+
new CliServicesCommandProcessor(),
|
|
9358
|
+
new CliPsCommandProcessor(),
|
|
9359
|
+
new CliKillCommandProcessor(),
|
|
9360
|
+
new CliHtopCommandProcessor()
|
|
9361
|
+
];
|
|
9362
|
+
var themes = {
|
|
9363
|
+
...cliCore.DefaultThemes
|
|
9364
|
+
};
|
|
9365
|
+
var themeInfos = {
|
|
9366
|
+
...cliCore.DefaultThemeInfos
|
|
9367
|
+
};
|
|
9368
|
+
|
|
9369
|
+
// src/lib/processors/theme/cli-theme-command-processor.ts
|
|
9370
|
+
function hexToRgb(hex) {
|
|
9371
|
+
const m = /^#?([0-9A-Fa-f]{6})$/.exec(hex);
|
|
9372
|
+
if (!m) return null;
|
|
9373
|
+
const n = parseInt(m[1], 16);
|
|
9374
|
+
return { r: n >> 16 & 255, g: n >> 8 & 255, b: n & 255 };
|
|
9375
|
+
}
|
|
9376
|
+
function colorSwatch(hex) {
|
|
9377
|
+
if (!hex) return " ";
|
|
9161
9378
|
const rgb = hexToRgb(hex);
|
|
9162
9379
|
if (!rgb) return " ";
|
|
9163
9380
|
return `\x1B[48;2;${rgb.r};${rgb.g};${rgb.b}m \x1B[0m`;
|
|
@@ -9336,10 +9553,12 @@ var CliThemeCommandProcessor = class {
|
|
|
9336
9553
|
const selected = await context2.reader.readSelect(
|
|
9337
9554
|
"Select a theme (live preview):",
|
|
9338
9555
|
options,
|
|
9339
|
-
|
|
9340
|
-
|
|
9341
|
-
|
|
9342
|
-
|
|
9556
|
+
{
|
|
9557
|
+
onChange: (value) => {
|
|
9558
|
+
if (themes[value]) {
|
|
9559
|
+
context2.terminal.options.theme = themes[value];
|
|
9560
|
+
this.applyStyles(context2);
|
|
9561
|
+
}
|
|
9343
9562
|
}
|
|
9344
9563
|
}
|
|
9345
9564
|
);
|
|
@@ -10204,21 +10423,16 @@ var miscProcessors = [
|
|
|
10204
10423
|
new CliSleepCommandProcessor(),
|
|
10205
10424
|
new CliUnameCommandProcessor(),
|
|
10206
10425
|
new CliTimeCommandProcessor(),
|
|
10207
|
-
new CliBase64CommandProcessor(),
|
|
10208
10426
|
new CliJsonCommandProcessor(),
|
|
10209
|
-
new CliUrlCommandProcessor(),
|
|
10210
|
-
new CliHashCommandProcessor(),
|
|
10211
10427
|
new CliRandomCommandProcessor(),
|
|
10212
10428
|
new CliUptimeCommandProcessor(),
|
|
10213
10429
|
new CliOpenCommandProcessor(),
|
|
10214
10430
|
new CliLoremCommandProcessor(),
|
|
10215
10431
|
new CliTimestampCommandProcessor(),
|
|
10216
10432
|
new CliColorCommandProcessor(),
|
|
10217
|
-
new CliJwtCommandProcessor(),
|
|
10218
10433
|
new CliCalCommandProcessor(),
|
|
10219
10434
|
new CliSeqCommandProcessor(),
|
|
10220
10435
|
new CliScreenCommandProcessor(),
|
|
10221
|
-
new CliHexCommandProcessor(),
|
|
10222
10436
|
new CliClipboardCommandProcessor(),
|
|
10223
10437
|
new CliConvertCommandProcessor(),
|
|
10224
10438
|
new CliYesCommandProcessor()
|
|
@@ -10529,6 +10743,10 @@ var CliBackgroundServiceRegistry = class {
|
|
|
10529
10743
|
this.isFullScreen = false;
|
|
10530
10744
|
this.notificationQueue = [];
|
|
10531
10745
|
}
|
|
10746
|
+
/** Attach the process registry so background services get PIDs */
|
|
10747
|
+
setProcessRegistry(registry) {
|
|
10748
|
+
this.processRegistry = registry;
|
|
10749
|
+
}
|
|
10532
10750
|
register(service) {
|
|
10533
10751
|
if (this.entries.has(service.name)) {
|
|
10534
10752
|
throw new Error(`Service "${service.name}" is already registered`);
|
|
@@ -10549,7 +10767,17 @@ var CliBackgroundServiceRegistry = class {
|
|
|
10549
10767
|
if (entry.status === "running") {
|
|
10550
10768
|
throw new Error(`Service "${name}" is already running`);
|
|
10551
10769
|
}
|
|
10770
|
+
if (this.processRegistry) {
|
|
10771
|
+
const svcType = entry.service.type === "daemon" ? "daemon" : entry.service.type === "job" ? "job" : "service";
|
|
10772
|
+
const proc = this.processRegistry.register(name, {
|
|
10773
|
+
name,
|
|
10774
|
+
type: svcType,
|
|
10775
|
+
onKill: () => this.stop(name)
|
|
10776
|
+
});
|
|
10777
|
+
entry.pid = proc.pid;
|
|
10778
|
+
}
|
|
10552
10779
|
const useWorker = entry.service.workerCompatible && entry.service.workerFactory && typeof Worker !== "undefined";
|
|
10780
|
+
const isDaemon = entry.service.type === "daemon";
|
|
10553
10781
|
if (useWorker) {
|
|
10554
10782
|
const workerRunner = new CliWorkerServiceRunner(
|
|
10555
10783
|
entry.service,
|
|
@@ -10563,15 +10791,19 @@ var CliBackgroundServiceRegistry = class {
|
|
|
10563
10791
|
entry.startedAt = /* @__PURE__ */ new Date();
|
|
10564
10792
|
entry.stoppedAt = void 0;
|
|
10565
10793
|
entry.error = void 0;
|
|
10566
|
-
|
|
10567
|
-
|
|
10568
|
-
|
|
10569
|
-
|
|
10570
|
-
|
|
10571
|
-
})
|
|
10572
|
-
|
|
10573
|
-
|
|
10794
|
+
if (isDaemon) {
|
|
10795
|
+
workerRunner.start().catch((e) => this.handleStartError(entry, name, e));
|
|
10796
|
+
} else {
|
|
10797
|
+
try {
|
|
10798
|
+
await workerRunner.start();
|
|
10799
|
+
} catch (e) {
|
|
10800
|
+
this.handleStartError(entry, name, e);
|
|
10801
|
+
}
|
|
10574
10802
|
}
|
|
10803
|
+
this.emitLifecycleEvent("service-started", name, {
|
|
10804
|
+
name,
|
|
10805
|
+
mode: "worker"
|
|
10806
|
+
});
|
|
10575
10807
|
} else {
|
|
10576
10808
|
const mainRunner = new CliMainThreadServiceRunner(
|
|
10577
10809
|
entry.service,
|
|
@@ -10585,21 +10817,25 @@ var CliBackgroundServiceRegistry = class {
|
|
|
10585
10817
|
entry.startedAt = /* @__PURE__ */ new Date();
|
|
10586
10818
|
entry.stoppedAt = void 0;
|
|
10587
10819
|
entry.error = void 0;
|
|
10588
|
-
|
|
10589
|
-
|
|
10590
|
-
|
|
10591
|
-
|
|
10592
|
-
|
|
10593
|
-
})
|
|
10594
|
-
|
|
10595
|
-
|
|
10820
|
+
if (isDaemon) {
|
|
10821
|
+
mainRunner.start().catch((e) => this.handleStartError(entry, name, e));
|
|
10822
|
+
} else {
|
|
10823
|
+
try {
|
|
10824
|
+
await mainRunner.start();
|
|
10825
|
+
} catch (e) {
|
|
10826
|
+
this.handleStartError(entry, name, e);
|
|
10827
|
+
}
|
|
10596
10828
|
}
|
|
10829
|
+
this.emitLifecycleEvent("service-started", name, {
|
|
10830
|
+
name,
|
|
10831
|
+
mode: "main-thread"
|
|
10832
|
+
});
|
|
10597
10833
|
}
|
|
10598
10834
|
}
|
|
10599
10835
|
async stop(name) {
|
|
10600
10836
|
const entry = this.getEntry(name);
|
|
10601
10837
|
if (entry.status !== "running") {
|
|
10602
|
-
|
|
10838
|
+
return;
|
|
10603
10839
|
}
|
|
10604
10840
|
if (entry.runner) {
|
|
10605
10841
|
await entry.runner.stop();
|
|
@@ -10607,6 +10843,7 @@ var CliBackgroundServiceRegistry = class {
|
|
|
10607
10843
|
entry.status = "stopped";
|
|
10608
10844
|
entry.stoppedAt = /* @__PURE__ */ new Date();
|
|
10609
10845
|
entry.runner = null;
|
|
10846
|
+
this.completeInProcessRegistry(entry, 0);
|
|
10610
10847
|
this.emitLifecycleEvent("service-stopped", name, { name });
|
|
10611
10848
|
}
|
|
10612
10849
|
async restart(name) {
|
|
@@ -10635,6 +10872,7 @@ var CliBackgroundServiceRegistry = class {
|
|
|
10635
10872
|
entry.status = "done";
|
|
10636
10873
|
entry.stoppedAt = /* @__PURE__ */ new Date();
|
|
10637
10874
|
entry.runner = null;
|
|
10875
|
+
this.completeInProcessRegistry(entry, 0);
|
|
10638
10876
|
this.emitLifecycleEvent("service-completed", name, {
|
|
10639
10877
|
name,
|
|
10640
10878
|
duration
|
|
@@ -10648,6 +10886,7 @@ var CliBackgroundServiceRegistry = class {
|
|
|
10648
10886
|
entry.error = error.message;
|
|
10649
10887
|
entry.stoppedAt = /* @__PURE__ */ new Date();
|
|
10650
10888
|
entry.runner = null;
|
|
10889
|
+
this.failInProcessRegistry(entry);
|
|
10651
10890
|
this.emitLifecycleEvent("service-failed", name, {
|
|
10652
10891
|
name,
|
|
10653
10892
|
error: error.message
|
|
@@ -10722,6 +10961,7 @@ var CliBackgroundServiceRegistry = class {
|
|
|
10722
10961
|
const executionMode = entry.runner ? entry.runner.mode : "main-thread";
|
|
10723
10962
|
const uptime = entry.status === "running" && entry.startedAt ? Date.now() - entry.startedAt.getTime() : void 0;
|
|
10724
10963
|
return {
|
|
10964
|
+
pid: entry.pid,
|
|
10725
10965
|
name: entry.service.name,
|
|
10726
10966
|
description: entry.service.description,
|
|
10727
10967
|
type: entry.service.type,
|
|
@@ -10738,9 +10978,14 @@ var CliBackgroundServiceRegistry = class {
|
|
|
10738
10978
|
if (!entry) return;
|
|
10739
10979
|
const from = entry.status;
|
|
10740
10980
|
entry.status = status;
|
|
10741
|
-
if (status === "stopped" || status === "
|
|
10981
|
+
if (status === "stopped" || status === "done") {
|
|
10982
|
+
entry.stoppedAt = /* @__PURE__ */ new Date();
|
|
10983
|
+
entry.runner = null;
|
|
10984
|
+
this.completeInProcessRegistry(entry, 0);
|
|
10985
|
+
} else if (status === "failed") {
|
|
10742
10986
|
entry.stoppedAt = /* @__PURE__ */ new Date();
|
|
10743
10987
|
entry.runner = null;
|
|
10988
|
+
this.failInProcessRegistry(entry);
|
|
10744
10989
|
}
|
|
10745
10990
|
this.emitLifecycleEvent("status-change", name, { name, from, to: status });
|
|
10746
10991
|
}
|
|
@@ -10751,6 +10996,7 @@ var CliBackgroundServiceRegistry = class {
|
|
|
10751
10996
|
entry.stoppedAt = /* @__PURE__ */ new Date();
|
|
10752
10997
|
entry.runner = null;
|
|
10753
10998
|
entry.logBuffer.add(`Failed to start: ${error.message}`, "error");
|
|
10999
|
+
this.failInProcessRegistry(entry);
|
|
10754
11000
|
if (entry.service.onError) {
|
|
10755
11001
|
entry.service.onError(error, {});
|
|
10756
11002
|
}
|
|
@@ -10803,6 +11049,16 @@ var CliBackgroundServiceRegistry = class {
|
|
|
10803
11049
|
this.writer.writeln(`\u250C\u2500 [${event.source}] ${message}`);
|
|
10804
11050
|
this.writer.writeln("\u2514" + "\u2500".repeat(40));
|
|
10805
11051
|
}
|
|
11052
|
+
completeInProcessRegistry(entry, exitCode) {
|
|
11053
|
+
if (entry.pid !== void 0 && this.processRegistry) {
|
|
11054
|
+
this.processRegistry.complete(entry.pid, exitCode);
|
|
11055
|
+
}
|
|
11056
|
+
}
|
|
11057
|
+
failInProcessRegistry(entry) {
|
|
11058
|
+
if (entry.pid !== void 0 && this.processRegistry) {
|
|
11059
|
+
this.processRegistry.fail(entry.pid);
|
|
11060
|
+
}
|
|
11061
|
+
}
|
|
10806
11062
|
};
|
|
10807
11063
|
var ANSI_RE = /\x1b\[[0-9;]*m/g;
|
|
10808
11064
|
function visibleLength(text) {
|
|
@@ -10899,8 +11155,8 @@ var CliTerminalWriter = class {
|
|
|
10899
11155
|
}
|
|
10900
11156
|
writeColumns(items, options) {
|
|
10901
11157
|
const cols = options?.columns ?? 3;
|
|
10902
|
-
const
|
|
10903
|
-
const colWidth = Math.max(...items.map((s) => s.length)) +
|
|
11158
|
+
const pad2 = options?.padding ?? 2;
|
|
11159
|
+
const colWidth = Math.max(...items.map((s) => s.length)) + pad2;
|
|
10904
11160
|
for (let i = 0; i < items.length; i += cols) {
|
|
10905
11161
|
const row = items.slice(i, i + cols);
|
|
10906
11162
|
this.writeln(row.map((item) => item.padEnd(colWidth)).join(""));
|
|
@@ -11128,51 +11384,34 @@ var CliTerminalProgressBar = class {
|
|
|
11128
11384
|
}
|
|
11129
11385
|
updateProgressBar() {
|
|
11130
11386
|
const displayProgress = Math.round(this.progress);
|
|
11131
|
-
const totalBars =
|
|
11387
|
+
const totalBars = 30;
|
|
11132
11388
|
const filledBars = Math.round(
|
|
11133
11389
|
displayProgress / this.total * totalBars
|
|
11134
11390
|
);
|
|
11135
11391
|
const emptyBars = totalBars - filledBars;
|
|
11136
11392
|
const RESET4 = "\x1B[0m";
|
|
11393
|
+
const CYAN = "\x1B[36m";
|
|
11394
|
+
const BRIGHT_CYAN = "\x1B[1;36m";
|
|
11137
11395
|
const DIM4 = "\x1B[2m";
|
|
11396
|
+
const shimmerPos = this.tickCount % (filledBars + 4);
|
|
11138
11397
|
let filledStr = "";
|
|
11139
|
-
const shimmerPos = this.tickCount % (filledBars + 6);
|
|
11140
11398
|
for (let i = 0; i < filledBars; i++) {
|
|
11141
|
-
const
|
|
11142
|
-
|
|
11143
|
-
|
|
11144
|
-
|
|
11145
|
-
|
|
11146
|
-
}
|
|
11147
|
-
const
|
|
11148
|
-
const progressBar = `[${filledStr}${emptyStr}]`;
|
|
11149
|
-
const percentage = `${displayProgress}%`.padStart(4, " ");
|
|
11150
|
-
const text = this.text.length > 0 ? ` ${this.text}` : "";
|
|
11399
|
+
const isShimmer = i >= shimmerPos - 1 && i <= shimmerPos;
|
|
11400
|
+
filledStr += `${isShimmer ? BRIGHT_CYAN : CYAN}\u2501${RESET4}`;
|
|
11401
|
+
}
|
|
11402
|
+
const emptyStr = `${DIM4}${"\u2500".repeat(emptyBars)}${RESET4}`;
|
|
11403
|
+
const progressBar = `${CYAN}[${RESET4}${filledStr}${emptyStr}${CYAN}]${RESET4}`;
|
|
11404
|
+
const percentage = `${CYAN}${displayProgress}%${RESET4}`.padStart(4, " ");
|
|
11405
|
+
const text = this.text.length > 0 ? ` ${DIM4}${this.text}${RESET4}` : "";
|
|
11151
11406
|
this.clearCurrentLine();
|
|
11152
11407
|
this.progressText = `${progressBar} ${percentage} ${text}`;
|
|
11153
|
-
this.progressTextPlainLength = 1 + totalBars + 1 + 1 + 4 + 1 + text.length;
|
|
11408
|
+
this.progressTextPlainLength = 1 + totalBars + 1 + 1 + 4 + 1 + this.text.length + 1;
|
|
11154
11409
|
this.terminal.write(this.progressText);
|
|
11155
11410
|
this.lastLineCount = this.calcLineCount(this.progressTextPlainLength);
|
|
11156
11411
|
if (this.context) {
|
|
11157
11412
|
this.context.setCurrentLine(this.progressText);
|
|
11158
11413
|
}
|
|
11159
11414
|
}
|
|
11160
|
-
/**
|
|
11161
|
-
* Returns an ANSI 256-color escape for a position in the progress bar.
|
|
11162
|
-
* Gradient: red (0%) -> yellow (50%) -> green (100%).
|
|
11163
|
-
*/
|
|
11164
|
-
progressGradientColor(ratio) {
|
|
11165
|
-
let r, g;
|
|
11166
|
-
if (ratio < 0.5) {
|
|
11167
|
-
r = 5;
|
|
11168
|
-
g = Math.round(ratio / 0.5 * 5);
|
|
11169
|
-
} else {
|
|
11170
|
-
r = Math.round((1 - ratio) / 0.5 * 5);
|
|
11171
|
-
g = 5;
|
|
11172
|
-
}
|
|
11173
|
-
const colorIndex = 16 + 36 * r + 6 * g + 0;
|
|
11174
|
-
return `\x1B[38;5;${colorIndex}m`;
|
|
11175
|
-
}
|
|
11176
11415
|
clearCurrentLine() {
|
|
11177
11416
|
const lines = Math.max(
|
|
11178
11417
|
this.lastLineCount,
|
|
@@ -11285,243 +11524,1779 @@ var CliClipboard = class {
|
|
|
11285
11524
|
}
|
|
11286
11525
|
};
|
|
11287
11526
|
|
|
11288
|
-
// src/lib/
|
|
11289
|
-
var
|
|
11290
|
-
constructor(host) {
|
|
11527
|
+
// src/lib/input/modes/input-mode-base.ts
|
|
11528
|
+
var InputModeBase = class {
|
|
11529
|
+
constructor(host, resolve) {
|
|
11291
11530
|
this.host = host;
|
|
11531
|
+
this.resolve = resolve;
|
|
11532
|
+
this._resolved = false;
|
|
11533
|
+
/** Number of extra lines rendered below the prompt (help bar, error, etc.) */
|
|
11534
|
+
this.extraLines = 0;
|
|
11292
11535
|
}
|
|
11293
|
-
|
|
11294
|
-
|
|
11536
|
+
/** Whether this mode has already resolved (prevents double-resolve from async operations) */
|
|
11537
|
+
get isResolved() {
|
|
11538
|
+
return this._resolved;
|
|
11295
11539
|
}
|
|
11296
|
-
|
|
11297
|
-
|
|
11540
|
+
/**
|
|
11541
|
+
* Default key event handler. Aborts on Ctrl+C or Escape.
|
|
11542
|
+
* Subclasses may override for custom behavior (e.g., search-enabled modes
|
|
11543
|
+
* clear the filter first, abort on second press).
|
|
11544
|
+
*/
|
|
11545
|
+
handleKeyEvent(event) {
|
|
11546
|
+
if (event.code === "KeyC" && event.ctrlKey || event.code === "Escape") {
|
|
11547
|
+
this.abort();
|
|
11548
|
+
return false;
|
|
11549
|
+
}
|
|
11550
|
+
return true;
|
|
11298
11551
|
}
|
|
11299
|
-
|
|
11300
|
-
|
|
11301
|
-
|
|
11302
|
-
|
|
11303
|
-
|
|
11304
|
-
|
|
11305
|
-
|
|
11306
|
-
|
|
11307
|
-
|
|
11308
|
-
type: "confirm",
|
|
11309
|
-
promptText: displayPrompt,
|
|
11310
|
-
resolve,
|
|
11311
|
-
buffer: "",
|
|
11312
|
-
cursorPosition: 0,
|
|
11313
|
-
defaultValue
|
|
11314
|
-
});
|
|
11315
|
-
});
|
|
11552
|
+
/**
|
|
11553
|
+
* Resolve the input promise with a value and pop this mode from the stack.
|
|
11554
|
+
*/
|
|
11555
|
+
resolveAndPop(value) {
|
|
11556
|
+
if (this._resolved) return;
|
|
11557
|
+
this._resolved = true;
|
|
11558
|
+
this.host.terminal.write("\r\n");
|
|
11559
|
+
this.host.popMode();
|
|
11560
|
+
this.resolve(value);
|
|
11316
11561
|
}
|
|
11317
|
-
|
|
11318
|
-
|
|
11319
|
-
|
|
11320
|
-
|
|
11321
|
-
|
|
11562
|
+
/**
|
|
11563
|
+
* Abort the input — resolve with null and pop.
|
|
11564
|
+
*/
|
|
11565
|
+
abort() {
|
|
11566
|
+
if (this._resolved) return;
|
|
11567
|
+
this._resolved = true;
|
|
11568
|
+
this.host.terminal.write("\r\n");
|
|
11569
|
+
this.host.popMode();
|
|
11570
|
+
this.resolve(null);
|
|
11571
|
+
}
|
|
11572
|
+
/**
|
|
11573
|
+
* Clear the current line and redraw prompt + display text, positioning
|
|
11574
|
+
* the cursor at the given position.
|
|
11575
|
+
*
|
|
11576
|
+
* All writes are batched into a single `terminal.write()` call to
|
|
11577
|
+
* eliminate visible flicker between clear and redraw.
|
|
11578
|
+
*/
|
|
11579
|
+
redrawLine(promptText, displayText, cursorPosition) {
|
|
11580
|
+
let buf = "";
|
|
11581
|
+
if (this.extraLines > 0) {
|
|
11582
|
+
buf += "\x1B[s";
|
|
11583
|
+
for (let i = 0; i < this.extraLines; i++) {
|
|
11584
|
+
buf += "\x1B[B\x1B[2K";
|
|
11585
|
+
}
|
|
11586
|
+
buf += "\x1B[u";
|
|
11587
|
+
this.extraLines = 0;
|
|
11588
|
+
}
|
|
11589
|
+
buf += "\x1B[2K\r" + promptText + displayText;
|
|
11590
|
+
const cursorOffset = displayText.length - cursorPosition;
|
|
11591
|
+
if (cursorOffset > 0) {
|
|
11592
|
+
buf += `\x1B[${cursorOffset}D`;
|
|
11322
11593
|
}
|
|
11323
|
-
|
|
11324
|
-
if (this.host.activeInputRequest) {
|
|
11325
|
-
throw new Error("Another input request is already active");
|
|
11326
|
-
}
|
|
11327
|
-
const termRows = this.host.getTerminalRows?.() ?? 24;
|
|
11328
|
-
const maxVisible = Math.max(3, termRows - 2);
|
|
11329
|
-
this.host.writeToTerminal(prompt + "\r\n");
|
|
11330
|
-
const displayLines = this.renderSelectOptions(options, 0, 0, maxVisible);
|
|
11331
|
-
onChange?.(options[0].value);
|
|
11332
|
-
this.host.setActiveInputRequest({
|
|
11333
|
-
type: "select",
|
|
11334
|
-
promptText: prompt,
|
|
11335
|
-
resolve,
|
|
11336
|
-
buffer: "",
|
|
11337
|
-
cursorPosition: 0,
|
|
11338
|
-
options,
|
|
11339
|
-
selectedIndex: 0,
|
|
11340
|
-
onChange,
|
|
11341
|
-
scrollOffset: 0,
|
|
11342
|
-
maxVisible,
|
|
11343
|
-
displayLines
|
|
11344
|
-
});
|
|
11345
|
-
});
|
|
11594
|
+
this.host.terminal.write(buf);
|
|
11346
11595
|
}
|
|
11347
|
-
|
|
11348
|
-
|
|
11349
|
-
|
|
11350
|
-
|
|
11351
|
-
|
|
11596
|
+
/**
|
|
11597
|
+
* Render a dimmed help bar below the current line.
|
|
11598
|
+
*/
|
|
11599
|
+
writeHelp(text) {
|
|
11600
|
+
this.extraLines++;
|
|
11601
|
+
this.host.terminal.write(
|
|
11602
|
+
`\r
|
|
11603
|
+
\x1B[2m${text}\x1B[0m\x1B[${this.extraLines}A`
|
|
11604
|
+
);
|
|
11605
|
+
}
|
|
11606
|
+
/**
|
|
11607
|
+
* Write a validation error below the current line.
|
|
11608
|
+
*/
|
|
11609
|
+
writeError(message) {
|
|
11610
|
+
this.extraLines++;
|
|
11611
|
+
this.host.terminal.write(
|
|
11612
|
+
`\r
|
|
11613
|
+
\x1B[31m\u2718 ${message}\x1B[0m\x1B[${this.extraLines}A`
|
|
11614
|
+
);
|
|
11615
|
+
}
|
|
11616
|
+
/**
|
|
11617
|
+
* Clear any extra lines (help bar, errors) rendered below the prompt.
|
|
11618
|
+
*/
|
|
11619
|
+
clearExtraLines() {
|
|
11620
|
+
if (this.extraLines > 0) {
|
|
11621
|
+
let buf = "\x1B[s";
|
|
11622
|
+
for (let i = 0; i < this.extraLines; i++) {
|
|
11623
|
+
buf += "\x1B[B\x1B[2K";
|
|
11624
|
+
}
|
|
11625
|
+
buf += "\x1B[u";
|
|
11626
|
+
this.host.terminal.write(buf);
|
|
11627
|
+
this.extraLines = 0;
|
|
11352
11628
|
}
|
|
11353
|
-
|
|
11354
|
-
|
|
11355
|
-
|
|
11356
|
-
|
|
11357
|
-
|
|
11358
|
-
|
|
11359
|
-
|
|
11360
|
-
|
|
11361
|
-
|
|
11362
|
-
|
|
11363
|
-
|
|
11364
|
-
|
|
11365
|
-
|
|
11366
|
-
|
|
11367
|
-
|
|
11368
|
-
|
|
11629
|
+
}
|
|
11630
|
+
};
|
|
11631
|
+
|
|
11632
|
+
// src/lib/input/modes/line-input-mode.ts
|
|
11633
|
+
var LineInputMode = class extends InputModeBase {
|
|
11634
|
+
constructor(host, resolve, promptText, options) {
|
|
11635
|
+
super(host, resolve);
|
|
11636
|
+
this.promptText = promptText;
|
|
11637
|
+
this.options = options;
|
|
11638
|
+
this.hasError = false;
|
|
11639
|
+
this.buffer = options?.default ?? "";
|
|
11640
|
+
this.cursorPosition = this.buffer.length;
|
|
11641
|
+
}
|
|
11642
|
+
activate() {
|
|
11643
|
+
const prompt = `\x1B[36m?\x1B[0m ${this.promptText}`;
|
|
11644
|
+
this.host.writeToTerminal(prompt);
|
|
11645
|
+
if (this.buffer) {
|
|
11646
|
+
this.host.writeToTerminal(this.buffer);
|
|
11647
|
+
} else if (this.options?.placeholder) {
|
|
11648
|
+
this.host.writeToTerminal(`\x1B[2m${this.options.placeholder}\x1B[0m`);
|
|
11649
|
+
this.host.terminal.write(`\x1B[${this.options.placeholder.length}D`);
|
|
11650
|
+
}
|
|
11651
|
+
}
|
|
11652
|
+
async handleInput(data) {
|
|
11653
|
+
if (this.hasError) {
|
|
11654
|
+
this.hasError = false;
|
|
11655
|
+
this.clearExtraLines();
|
|
11656
|
+
}
|
|
11657
|
+
if (data === "\r") {
|
|
11658
|
+
if (this.options?.validate) {
|
|
11659
|
+
const error = this.options.validate(this.buffer);
|
|
11660
|
+
if (error) {
|
|
11661
|
+
this.hasError = true;
|
|
11662
|
+
this.writeError(error);
|
|
11663
|
+
return;
|
|
11664
|
+
}
|
|
11665
|
+
}
|
|
11666
|
+
this.resolveAndPop(this.buffer);
|
|
11667
|
+
} else if (data === "\x7F") {
|
|
11668
|
+
if (this.cursorPosition > 0) {
|
|
11669
|
+
this.buffer = this.buffer.slice(0, this.cursorPosition - 1) + this.buffer.slice(this.cursorPosition);
|
|
11670
|
+
this.cursorPosition--;
|
|
11671
|
+
this.renderLine();
|
|
11672
|
+
}
|
|
11673
|
+
} else if (data === "\x1B[D") {
|
|
11674
|
+
if (this.cursorPosition > 0) {
|
|
11675
|
+
this.cursorPosition--;
|
|
11676
|
+
this.host.terminal.write(data);
|
|
11677
|
+
}
|
|
11678
|
+
} else if (data === "\x1B[C") {
|
|
11679
|
+
if (this.cursorPosition < this.buffer.length) {
|
|
11680
|
+
this.cursorPosition++;
|
|
11681
|
+
this.host.terminal.write(data);
|
|
11682
|
+
}
|
|
11683
|
+
} else if (data.startsWith("\x1B")) ; else {
|
|
11684
|
+
const text = data.replace(/[\r\n]+/g, "");
|
|
11685
|
+
this.buffer = this.buffer.slice(0, this.cursorPosition) + text + this.buffer.slice(this.cursorPosition);
|
|
11686
|
+
this.cursorPosition += text.length;
|
|
11687
|
+
if (this.cursorPosition === this.buffer.length && this.extraLines === 0 && !this.hasError && !(this.options?.placeholder && this.buffer.length === text.length)) {
|
|
11688
|
+
this.host.terminal.write(text);
|
|
11689
|
+
} else {
|
|
11690
|
+
this.renderLine();
|
|
11691
|
+
}
|
|
11692
|
+
}
|
|
11693
|
+
}
|
|
11694
|
+
onResize() {
|
|
11695
|
+
this.renderLine();
|
|
11696
|
+
}
|
|
11697
|
+
renderLine() {
|
|
11698
|
+
const prompt = `\x1B[36m?\x1B[0m ${this.promptText}`;
|
|
11699
|
+
if (this.buffer.length === 0 && this.options?.placeholder) {
|
|
11700
|
+
const placeholder = `\x1B[2m${this.options.placeholder}\x1B[0m`;
|
|
11701
|
+
this.redrawLine(prompt, placeholder, 0);
|
|
11702
|
+
} else {
|
|
11703
|
+
this.redrawLine(prompt, this.buffer, this.cursorPosition);
|
|
11704
|
+
}
|
|
11705
|
+
}
|
|
11706
|
+
};
|
|
11707
|
+
|
|
11708
|
+
// src/lib/input/modes/password-input-mode.ts
|
|
11709
|
+
var PasswordInputMode = class extends InputModeBase {
|
|
11710
|
+
constructor(host, resolve, promptText) {
|
|
11711
|
+
super(host, resolve);
|
|
11712
|
+
this.promptText = promptText;
|
|
11713
|
+
this.buffer = "";
|
|
11714
|
+
}
|
|
11715
|
+
activate() {
|
|
11716
|
+
const prompt = `\x1B[36m?\x1B[0m ${this.promptText}`;
|
|
11717
|
+
this.host.writeToTerminal(prompt);
|
|
11718
|
+
}
|
|
11719
|
+
async handleInput(data) {
|
|
11720
|
+
if (data === "\r") {
|
|
11721
|
+
this.resolveAndPop(this.buffer);
|
|
11722
|
+
} else if (data === "\x7F") {
|
|
11723
|
+
if (this.buffer.length > 0) {
|
|
11724
|
+
this.buffer = this.buffer.slice(0, -1);
|
|
11725
|
+
this.renderLine();
|
|
11726
|
+
}
|
|
11727
|
+
} else if (data.startsWith("\x1B")) ; else {
|
|
11728
|
+
const text = data.replace(/[\r\n]+/g, "");
|
|
11729
|
+
this.buffer += text;
|
|
11730
|
+
if (this.extraLines === 0) {
|
|
11731
|
+
this.host.terminal.write("*".repeat(text.length));
|
|
11732
|
+
} else {
|
|
11733
|
+
this.renderLine();
|
|
11734
|
+
}
|
|
11735
|
+
}
|
|
11736
|
+
}
|
|
11737
|
+
onResize() {
|
|
11738
|
+
this.renderLine();
|
|
11739
|
+
}
|
|
11740
|
+
renderLine() {
|
|
11741
|
+
const prompt = `\x1B[36m?\x1B[0m ${this.promptText}`;
|
|
11742
|
+
const masked = "*".repeat(this.buffer.length);
|
|
11743
|
+
this.redrawLine(prompt, masked, masked.length);
|
|
11744
|
+
}
|
|
11745
|
+
};
|
|
11746
|
+
|
|
11747
|
+
// src/lib/input/modes/confirm-input-mode.ts
|
|
11748
|
+
var ConfirmInputMode = class extends InputModeBase {
|
|
11749
|
+
constructor(host, resolve, promptText, defaultValue = false) {
|
|
11750
|
+
super(host, resolve);
|
|
11751
|
+
this.promptText = promptText;
|
|
11752
|
+
this.defaultValue = defaultValue;
|
|
11753
|
+
this.buffer = "";
|
|
11754
|
+
}
|
|
11755
|
+
activate() {
|
|
11756
|
+
const hint = this.defaultValue ? "(Y/n)" : "(y/N)";
|
|
11757
|
+
const prompt = `\x1B[36m?\x1B[0m ${this.promptText} ${hint}: `;
|
|
11758
|
+
this.host.writeToTerminal(prompt);
|
|
11759
|
+
}
|
|
11760
|
+
async handleInput(data) {
|
|
11761
|
+
if (data === "\r") {
|
|
11762
|
+
if (this.buffer === "") {
|
|
11763
|
+
this.resolveAndPop(this.defaultValue);
|
|
11764
|
+
} else {
|
|
11765
|
+
const lower = this.buffer.toLowerCase();
|
|
11766
|
+
if (lower === "y") {
|
|
11767
|
+
this.resolveAndPop(true);
|
|
11768
|
+
} else if (lower === "n") {
|
|
11769
|
+
this.resolveAndPop(false);
|
|
11770
|
+
}
|
|
11771
|
+
}
|
|
11772
|
+
} else if (data === "\x7F") {
|
|
11773
|
+
if (this.buffer.length > 0) {
|
|
11774
|
+
this.buffer = this.buffer.slice(0, -1);
|
|
11775
|
+
this.renderLine();
|
|
11776
|
+
}
|
|
11777
|
+
} else if (data.startsWith("\x1B")) ; else {
|
|
11778
|
+
const lower = data.toLowerCase();
|
|
11779
|
+
if (lower === "y" || lower === "n") {
|
|
11780
|
+
this.buffer = lower;
|
|
11781
|
+
this.renderLine();
|
|
11782
|
+
}
|
|
11783
|
+
}
|
|
11784
|
+
}
|
|
11785
|
+
onResize() {
|
|
11786
|
+
this.renderLine();
|
|
11787
|
+
}
|
|
11788
|
+
renderLine() {
|
|
11789
|
+
const hint = this.defaultValue ? "(Y/n)" : "(y/N)";
|
|
11790
|
+
const prompt = `\x1B[36m?\x1B[0m ${this.promptText} ${hint}: `;
|
|
11791
|
+
this.redrawLine(prompt, this.buffer, this.buffer.length);
|
|
11792
|
+
}
|
|
11793
|
+
};
|
|
11794
|
+
|
|
11795
|
+
// src/lib/input/modes/number-input-mode.ts
|
|
11796
|
+
var NumberInputMode = class extends InputModeBase {
|
|
11797
|
+
constructor(host, resolve, promptText, options) {
|
|
11798
|
+
super(host, resolve);
|
|
11799
|
+
this.promptText = promptText;
|
|
11800
|
+
this.options = options;
|
|
11801
|
+
this.hasError = false;
|
|
11802
|
+
this.buffer = options?.default != null ? String(options.default) : "";
|
|
11803
|
+
this.cursorPosition = this.buffer.length;
|
|
11804
|
+
}
|
|
11805
|
+
activate() {
|
|
11806
|
+
const prompt = this.buildPrompt();
|
|
11807
|
+
this.host.writeToTerminal(prompt);
|
|
11808
|
+
if (this.buffer) {
|
|
11809
|
+
this.host.writeToTerminal(this.buffer);
|
|
11810
|
+
}
|
|
11811
|
+
}
|
|
11812
|
+
async handleInput(data) {
|
|
11813
|
+
if (this.hasError) {
|
|
11814
|
+
this.hasError = false;
|
|
11815
|
+
this.clearExtraLines();
|
|
11816
|
+
}
|
|
11817
|
+
if (data === "\r") {
|
|
11818
|
+
if (this.buffer === "" || this.buffer === "-") {
|
|
11819
|
+
if (this.options?.default != null) {
|
|
11820
|
+
this.resolveAndPop(this.options.default);
|
|
11821
|
+
}
|
|
11822
|
+
return;
|
|
11823
|
+
}
|
|
11824
|
+
const value = parseInt(this.buffer, 10);
|
|
11825
|
+
if (isNaN(value)) {
|
|
11826
|
+
this.hasError = true;
|
|
11827
|
+
this.writeError("Please enter a valid number");
|
|
11828
|
+
return;
|
|
11829
|
+
}
|
|
11830
|
+
if (this.options?.min != null && value < this.options.min) {
|
|
11831
|
+
this.hasError = true;
|
|
11832
|
+
this.writeError(`Value must be at least ${this.options.min}`);
|
|
11833
|
+
return;
|
|
11834
|
+
}
|
|
11835
|
+
if (this.options?.max != null && value > this.options.max) {
|
|
11836
|
+
this.hasError = true;
|
|
11837
|
+
this.writeError(`Value must be at most ${this.options.max}`);
|
|
11838
|
+
return;
|
|
11839
|
+
}
|
|
11840
|
+
this.resolveAndPop(value);
|
|
11841
|
+
} else if (data === "\x7F") {
|
|
11842
|
+
if (this.cursorPosition > 0) {
|
|
11843
|
+
this.buffer = this.buffer.slice(0, this.cursorPosition - 1) + this.buffer.slice(this.cursorPosition);
|
|
11844
|
+
this.cursorPosition--;
|
|
11845
|
+
this.renderLine();
|
|
11846
|
+
}
|
|
11847
|
+
} else if (data === "\x1B[D") {
|
|
11848
|
+
if (this.cursorPosition > 0) {
|
|
11849
|
+
this.cursorPosition--;
|
|
11850
|
+
this.host.terminal.write(data);
|
|
11851
|
+
}
|
|
11852
|
+
} else if (data === "\x1B[C") {
|
|
11853
|
+
if (this.cursorPosition < this.buffer.length) {
|
|
11854
|
+
this.cursorPosition++;
|
|
11855
|
+
this.host.terminal.write(data);
|
|
11856
|
+
}
|
|
11857
|
+
} else if (data.startsWith("\x1B")) ; else {
|
|
11858
|
+
for (const ch of data) {
|
|
11859
|
+
if (ch >= "0" && ch <= "9") {
|
|
11860
|
+
this.buffer = this.buffer.slice(0, this.cursorPosition) + ch + this.buffer.slice(this.cursorPosition);
|
|
11861
|
+
this.cursorPosition++;
|
|
11862
|
+
} else if (ch === "-" && this.cursorPosition === 0 && !this.buffer.startsWith("-")) {
|
|
11863
|
+
this.buffer = "-" + this.buffer;
|
|
11864
|
+
this.cursorPosition++;
|
|
11865
|
+
}
|
|
11866
|
+
}
|
|
11867
|
+
this.renderLine();
|
|
11868
|
+
}
|
|
11869
|
+
}
|
|
11870
|
+
onResize() {
|
|
11871
|
+
this.renderLine();
|
|
11872
|
+
}
|
|
11873
|
+
buildPrompt() {
|
|
11874
|
+
const { min, max } = this.options ?? {};
|
|
11875
|
+
let hint = "";
|
|
11876
|
+
if (min != null && max != null) {
|
|
11877
|
+
hint = ` \x1B[2m(${min}\u2013${max})\x1B[0m`;
|
|
11878
|
+
} else if (min != null) {
|
|
11879
|
+
hint = ` \x1B[2m(min: ${min})\x1B[0m`;
|
|
11880
|
+
} else if (max != null) {
|
|
11881
|
+
hint = ` \x1B[2m(max: ${max})\x1B[0m`;
|
|
11882
|
+
}
|
|
11883
|
+
return `\x1B[36m?\x1B[0m ${this.promptText}${hint}`;
|
|
11884
|
+
}
|
|
11885
|
+
renderLine() {
|
|
11886
|
+
const prompt = this.buildPrompt();
|
|
11887
|
+
this.redrawLine(prompt, this.buffer, this.cursorPosition);
|
|
11888
|
+
}
|
|
11889
|
+
};
|
|
11890
|
+
|
|
11891
|
+
// src/lib/input/modes/select-input-mode.ts
|
|
11892
|
+
var SelectInputMode = class extends InputModeBase {
|
|
11893
|
+
constructor(host, resolve, promptText, options, selectOptions) {
|
|
11894
|
+
super(host, resolve);
|
|
11895
|
+
this.promptText = promptText;
|
|
11896
|
+
this.options = options;
|
|
11897
|
+
this.selectOptions = selectOptions;
|
|
11898
|
+
this.selectedIndex = 0;
|
|
11899
|
+
this.scrollOffset = 0;
|
|
11900
|
+
this.filter = "";
|
|
11901
|
+
/** Number of lines rendered for the option list + help bar (for clearing) */
|
|
11902
|
+
this.renderedLines = 0;
|
|
11903
|
+
this.filteredOptions = [...options];
|
|
11904
|
+
this.maxVisible = Math.min(options.length, host.getTerminalRows() - 4);
|
|
11905
|
+
}
|
|
11906
|
+
activate() {
|
|
11907
|
+
this.host.terminal.write("\x1B[?25l");
|
|
11908
|
+
if (this.selectOptions?.default) {
|
|
11909
|
+
const idx = this.filteredOptions.findIndex(
|
|
11910
|
+
(o) => o.value === this.selectOptions.default
|
|
11911
|
+
);
|
|
11912
|
+
if (idx >= 0) {
|
|
11913
|
+
this.selectedIndex = idx;
|
|
11914
|
+
if (this.selectedIndex >= this.maxVisible) {
|
|
11915
|
+
this.scrollOffset = this.selectedIndex - this.maxVisible + 1;
|
|
11916
|
+
}
|
|
11917
|
+
}
|
|
11918
|
+
} else {
|
|
11919
|
+
this.selectedIndex = this.findNextSelectable(-1, 1);
|
|
11920
|
+
}
|
|
11921
|
+
this.renderAll();
|
|
11922
|
+
}
|
|
11923
|
+
/**
|
|
11924
|
+
* Handle keyboard events. When searchable and filter is non-empty,
|
|
11925
|
+
* Escape clears the filter instead of aborting.
|
|
11926
|
+
*/
|
|
11927
|
+
handleKeyEvent(event) {
|
|
11928
|
+
if (this.selectOptions?.searchable && this.filter.length > 0) {
|
|
11929
|
+
if (event.code === "Escape") {
|
|
11930
|
+
this.clearFilter();
|
|
11931
|
+
return false;
|
|
11932
|
+
}
|
|
11933
|
+
}
|
|
11934
|
+
return super.handleKeyEvent(event);
|
|
11935
|
+
}
|
|
11936
|
+
/**
|
|
11937
|
+
* Clear rendered option lines and show final answer before resolving.
|
|
11938
|
+
*/
|
|
11939
|
+
resolveAndPop(value) {
|
|
11940
|
+
this.clearRenderedLines();
|
|
11941
|
+
const selectedOpt = this.options.find((o) => o.value === value);
|
|
11942
|
+
const label = selectedOpt ? selectedOpt.label : value;
|
|
11943
|
+
this.host.terminal.write("\x1B[2K\r");
|
|
11944
|
+
this.host.terminal.write(`\x1B[32m\u2714\x1B[0m ${this.promptText} \x1B[36m${label}\x1B[0m`);
|
|
11945
|
+
this.host.terminal.write("\x1B[?25h");
|
|
11946
|
+
super.resolveAndPop(value);
|
|
11947
|
+
}
|
|
11948
|
+
/**
|
|
11949
|
+
* Clear rendered option lines before aborting.
|
|
11950
|
+
*/
|
|
11951
|
+
abort() {
|
|
11952
|
+
this.clearRenderedLines();
|
|
11953
|
+
this.host.terminal.write("\x1B[2K\r");
|
|
11954
|
+
this.host.terminal.write(`\x1B[33m\u2718\x1B[0m ${this.promptText} \x1B[2mcancelled\x1B[0m`);
|
|
11955
|
+
this.host.terminal.write("\x1B[?25h");
|
|
11956
|
+
super.abort();
|
|
11957
|
+
}
|
|
11958
|
+
async handleInput(data) {
|
|
11959
|
+
if (data === "\x1B[A") {
|
|
11960
|
+
this.moveSelection(-1);
|
|
11961
|
+
} else if (data === "\x1B[B") {
|
|
11962
|
+
this.moveSelection(1);
|
|
11963
|
+
} else if (data === "\r") {
|
|
11964
|
+
const items = this.buildDisplayList();
|
|
11965
|
+
const selectableItems = items.filter(
|
|
11966
|
+
(i) => i.type === "option" && !i.option.disabled
|
|
11967
|
+
);
|
|
11968
|
+
if (selectableItems.length > 0 && this.selectedIndex < selectableItems.length) {
|
|
11969
|
+
const selected = selectableItems[this.selectedIndex];
|
|
11970
|
+
this.resolveAndPop(selected.option.value);
|
|
11971
|
+
}
|
|
11972
|
+
} else if (data === "\x7F") {
|
|
11973
|
+
if (this.selectOptions?.searchable && this.filter.length > 0) {
|
|
11974
|
+
this.filter = this.filter.slice(0, -1);
|
|
11975
|
+
this.applyFilter();
|
|
11976
|
+
this.renderAll();
|
|
11977
|
+
}
|
|
11978
|
+
} else if (data.startsWith("\x1B")) ; else if (this.selectOptions?.searchable && data.length === 1 && data >= " ") {
|
|
11979
|
+
this.filter += data;
|
|
11980
|
+
this.applyFilter();
|
|
11981
|
+
this.renderAll();
|
|
11982
|
+
}
|
|
11983
|
+
}
|
|
11984
|
+
onResize(cols, rows) {
|
|
11985
|
+
this.maxVisible = Math.min(this.filteredOptions.length, rows - 4);
|
|
11986
|
+
if (this.scrollOffset + this.maxVisible > this.getSelectableCount()) {
|
|
11987
|
+
this.scrollOffset = Math.max(0, this.getSelectableCount() - this.maxVisible);
|
|
11988
|
+
}
|
|
11989
|
+
this.renderAll();
|
|
11990
|
+
}
|
|
11991
|
+
// ── Private helpers ──────────────────────────────────────────────
|
|
11992
|
+
/**
|
|
11993
|
+
* Build flattened display list with group headers inserted.
|
|
11994
|
+
*/
|
|
11995
|
+
buildDisplayList() {
|
|
11996
|
+
const items = [];
|
|
11997
|
+
let lastGroup;
|
|
11998
|
+
for (const opt of this.filteredOptions) {
|
|
11999
|
+
if (opt.group && opt.group !== lastGroup) {
|
|
12000
|
+
items.push({ type: "group-header", groupName: opt.group });
|
|
12001
|
+
lastGroup = opt.group;
|
|
12002
|
+
}
|
|
12003
|
+
items.push({ type: "option", option: opt });
|
|
12004
|
+
}
|
|
12005
|
+
return items;
|
|
12006
|
+
}
|
|
12007
|
+
/**
|
|
12008
|
+
* Get the total count of selectable items (non-disabled options).
|
|
12009
|
+
*/
|
|
12010
|
+
getSelectableCount() {
|
|
12011
|
+
return this.filteredOptions.filter((o) => !o.disabled).length;
|
|
12012
|
+
}
|
|
12013
|
+
/**
|
|
12014
|
+
* Move selection in the given direction (+1 or -1), skipping disabled options.
|
|
12015
|
+
* Wraps around at boundaries.
|
|
12016
|
+
*/
|
|
12017
|
+
moveSelection(direction) {
|
|
12018
|
+
const selectableCount = this.getSelectableCount();
|
|
12019
|
+
if (selectableCount === 0) return;
|
|
12020
|
+
let next = this.selectedIndex + direction;
|
|
12021
|
+
if (next < 0) next = selectableCount - 1;
|
|
12022
|
+
if (next >= selectableCount) next = 0;
|
|
12023
|
+
this.selectedIndex = next;
|
|
12024
|
+
if (this.selectedIndex < this.scrollOffset) {
|
|
12025
|
+
this.scrollOffset = this.selectedIndex;
|
|
12026
|
+
} else if (this.selectedIndex >= this.scrollOffset + this.maxVisible) {
|
|
12027
|
+
this.scrollOffset = this.selectedIndex - this.maxVisible + 1;
|
|
12028
|
+
}
|
|
12029
|
+
const selectables = this.filteredOptions.filter((o) => !o.disabled);
|
|
12030
|
+
if (this.selectOptions?.onChange && selectables[this.selectedIndex]) {
|
|
12031
|
+
this.selectOptions.onChange(selectables[this.selectedIndex].value);
|
|
12032
|
+
}
|
|
12033
|
+
this.renderAll();
|
|
12034
|
+
}
|
|
12035
|
+
/**
|
|
12036
|
+
* Find the next selectable (non-disabled) option index starting from `from`.
|
|
12037
|
+
* Returns 0 if none found.
|
|
12038
|
+
*/
|
|
12039
|
+
findNextSelectable(from, direction) {
|
|
12040
|
+
const len = this.filteredOptions.length;
|
|
12041
|
+
if (len === 0) return 0;
|
|
12042
|
+
let idx = from + direction;
|
|
12043
|
+
for (let i = 0; i < len; i++) {
|
|
12044
|
+
if (idx < 0) idx = len - 1;
|
|
12045
|
+
if (idx >= len) idx = 0;
|
|
12046
|
+
if (!this.filteredOptions[idx].disabled) return idx;
|
|
12047
|
+
idx += direction;
|
|
12048
|
+
}
|
|
12049
|
+
return 0;
|
|
12050
|
+
}
|
|
12051
|
+
/**
|
|
12052
|
+
* Apply the current filter text to the options list.
|
|
12053
|
+
*/
|
|
12054
|
+
applyFilter() {
|
|
12055
|
+
if (this.filter.length === 0) {
|
|
12056
|
+
this.filteredOptions = [...this.options];
|
|
12057
|
+
} else {
|
|
12058
|
+
const lower = this.filter.toLowerCase();
|
|
12059
|
+
this.filteredOptions = this.options.filter(
|
|
12060
|
+
(o) => o.label.toLowerCase().includes(lower)
|
|
12061
|
+
);
|
|
12062
|
+
}
|
|
12063
|
+
this.selectedIndex = 0;
|
|
12064
|
+
this.scrollOffset = 0;
|
|
12065
|
+
this.maxVisible = Math.min(
|
|
12066
|
+
this.filteredOptions.length,
|
|
12067
|
+
this.host.getTerminalRows() - 4
|
|
12068
|
+
);
|
|
12069
|
+
const selectables = this.filteredOptions.filter((o) => !o.disabled);
|
|
12070
|
+
if (selectables.length > 0) {
|
|
12071
|
+
this.selectedIndex = 0;
|
|
12072
|
+
}
|
|
12073
|
+
}
|
|
12074
|
+
/**
|
|
12075
|
+
* Clear the filter and restore full option list.
|
|
12076
|
+
*/
|
|
12077
|
+
clearFilter() {
|
|
12078
|
+
this.filter = "";
|
|
12079
|
+
this.applyFilter();
|
|
12080
|
+
this.renderAll();
|
|
12081
|
+
}
|
|
12082
|
+
/**
|
|
12083
|
+
* Clear all previously rendered lines and re-render everything.
|
|
12084
|
+
*/
|
|
12085
|
+
renderAll() {
|
|
12086
|
+
this.clearRenderedLines();
|
|
12087
|
+
this.renderPrompt();
|
|
12088
|
+
this.renderOptions();
|
|
12089
|
+
this.renderHelpBar();
|
|
12090
|
+
if (this.renderedLines > 0) {
|
|
12091
|
+
this.host.terminal.write(`\x1B[${this.renderedLines}A`);
|
|
12092
|
+
}
|
|
12093
|
+
}
|
|
12094
|
+
/**
|
|
12095
|
+
* Clear all lines we previously rendered (below the cursor).
|
|
12096
|
+
*/
|
|
12097
|
+
clearRenderedLines() {
|
|
12098
|
+
if (this.renderedLines > 0) {
|
|
12099
|
+
this.host.terminal.write("\x1B[s");
|
|
12100
|
+
for (let i = 0; i < this.renderedLines; i++) {
|
|
12101
|
+
this.host.terminal.write("\x1B[B\x1B[2K");
|
|
12102
|
+
}
|
|
12103
|
+
this.host.terminal.write("\x1B[u");
|
|
12104
|
+
this.renderedLines = 0;
|
|
12105
|
+
}
|
|
12106
|
+
}
|
|
12107
|
+
/**
|
|
12108
|
+
* Render the prompt line.
|
|
12109
|
+
*/
|
|
12110
|
+
renderPrompt() {
|
|
12111
|
+
this.host.terminal.write("\x1B[2K\r");
|
|
12112
|
+
if (this.selectOptions?.searchable) {
|
|
12113
|
+
if (this.filter.length > 0) {
|
|
12114
|
+
this.host.terminal.write(
|
|
12115
|
+
`\x1B[36m?\x1B[0m ${this.promptText}: ${this.filter}`
|
|
12116
|
+
);
|
|
12117
|
+
} else {
|
|
12118
|
+
this.host.terminal.write(
|
|
12119
|
+
`\x1B[36m?\x1B[0m ${this.promptText}: \x1B[2mType to filter...\x1B[0m`
|
|
12120
|
+
);
|
|
12121
|
+
}
|
|
12122
|
+
} else {
|
|
12123
|
+
this.host.terminal.write(`\x1B[36m?\x1B[0m ${this.promptText}:`);
|
|
12124
|
+
}
|
|
12125
|
+
}
|
|
12126
|
+
/**
|
|
12127
|
+
* Render the visible slice of the option list.
|
|
12128
|
+
*/
|
|
12129
|
+
renderOptions() {
|
|
12130
|
+
const items = this.buildDisplayList();
|
|
12131
|
+
const selectables = this.filteredOptions.filter((o) => !o.disabled);
|
|
12132
|
+
const visibleStart = this.scrollOffset;
|
|
12133
|
+
const visibleEnd = Math.min(
|
|
12134
|
+
selectables.length,
|
|
12135
|
+
this.scrollOffset + this.maxVisible
|
|
12136
|
+
);
|
|
12137
|
+
if (visibleStart > 0) {
|
|
12138
|
+
this.host.terminal.write(`\r
|
|
12139
|
+
\x1B[2m \u2191 ${visibleStart} more above\x1B[0m`);
|
|
12140
|
+
this.renderedLines++;
|
|
12141
|
+
}
|
|
12142
|
+
let selectableIdx = 0;
|
|
12143
|
+
for (const item of items) {
|
|
12144
|
+
if (item.type === "group-header") {
|
|
12145
|
+
if (this.isGroupVisible(item.groupName, items, visibleStart, visibleEnd)) {
|
|
12146
|
+
this.host.terminal.write(
|
|
12147
|
+
`\r
|
|
12148
|
+
\x1B[35m\u2500\u2500 ${item.groupName} \u2500\u2500\x1B[0m`
|
|
12149
|
+
);
|
|
12150
|
+
this.renderedLines++;
|
|
12151
|
+
}
|
|
12152
|
+
} else {
|
|
12153
|
+
const opt = item.option;
|
|
12154
|
+
if (opt.disabled) {
|
|
12155
|
+
if (this.isDisabledOptionVisible(items, item, visibleStart, visibleEnd)) {
|
|
12156
|
+
this.renderOptionLine(opt, false, false);
|
|
12157
|
+
this.renderedLines++;
|
|
12158
|
+
}
|
|
12159
|
+
} else {
|
|
12160
|
+
if (selectableIdx >= visibleStart && selectableIdx < visibleEnd) {
|
|
12161
|
+
const isSelected = selectableIdx === this.selectedIndex;
|
|
12162
|
+
this.renderOptionLine(opt, isSelected, false);
|
|
12163
|
+
this.renderedLines++;
|
|
12164
|
+
}
|
|
12165
|
+
selectableIdx++;
|
|
12166
|
+
}
|
|
12167
|
+
}
|
|
12168
|
+
}
|
|
12169
|
+
const remaining = selectables.length - visibleEnd;
|
|
12170
|
+
if (remaining > 0) {
|
|
12171
|
+
this.host.terminal.write(`\r
|
|
12172
|
+
\x1B[2m \u2193 ${remaining} more below\x1B[0m`);
|
|
12173
|
+
this.renderedLines++;
|
|
12174
|
+
}
|
|
12175
|
+
}
|
|
12176
|
+
/**
|
|
12177
|
+
* Check if a group header should be visible given the current visible range.
|
|
12178
|
+
*/
|
|
12179
|
+
isGroupVisible(groupName, items, visibleStart, visibleEnd) {
|
|
12180
|
+
let selectableIdx = 0;
|
|
12181
|
+
for (const item of items) {
|
|
12182
|
+
if (item.type === "option" && !item.option.disabled) {
|
|
12183
|
+
if (item.option.group === groupName && selectableIdx >= visibleStart && selectableIdx < visibleEnd) {
|
|
12184
|
+
return true;
|
|
12185
|
+
}
|
|
12186
|
+
selectableIdx++;
|
|
12187
|
+
}
|
|
12188
|
+
}
|
|
12189
|
+
return false;
|
|
12190
|
+
}
|
|
12191
|
+
/**
|
|
12192
|
+
* Check if a disabled option should be visible (adjacent to visible selectables).
|
|
12193
|
+
*/
|
|
12194
|
+
isDisabledOptionVisible(items, disabledItem, visibleStart, visibleEnd) {
|
|
12195
|
+
const idx = items.indexOf(disabledItem);
|
|
12196
|
+
let selectableIdx = 0;
|
|
12197
|
+
for (let i = 0; i < items.length; i++) {
|
|
12198
|
+
if (items[i].type === "option" && !items[i].option.disabled) {
|
|
12199
|
+
if (i > idx) {
|
|
12200
|
+
return selectableIdx >= visibleStart && selectableIdx < visibleEnd;
|
|
12201
|
+
}
|
|
12202
|
+
selectableIdx++;
|
|
12203
|
+
}
|
|
12204
|
+
}
|
|
12205
|
+
return selectableIdx > 0 && selectableIdx - 1 >= visibleStart && selectableIdx - 1 < visibleEnd;
|
|
12206
|
+
}
|
|
12207
|
+
/**
|
|
12208
|
+
* Render a single option line.
|
|
12209
|
+
*/
|
|
12210
|
+
renderOptionLine(opt, isSelected, _isGroupHeader) {
|
|
12211
|
+
const label = this.highlightMatch(opt.label);
|
|
12212
|
+
const desc = opt.description ? ` \x1B[2;3m\u2014 ${opt.description}\x1B[0m` : "";
|
|
12213
|
+
if (opt.disabled) {
|
|
12214
|
+
this.host.terminal.write(
|
|
12215
|
+
`\r
|
|
12216
|
+
\x1B[2m${opt.label}${desc} (disabled)\x1B[0m`
|
|
12217
|
+
);
|
|
12218
|
+
} else if (isSelected) {
|
|
12219
|
+
this.host.terminal.write(
|
|
12220
|
+
`\r
|
|
12221
|
+
\x1B[48;5;24m\x1B[38;5;117m\u276F ${label}${desc}\x1B[0m`
|
|
12222
|
+
);
|
|
12223
|
+
} else {
|
|
12224
|
+
this.host.terminal.write(
|
|
12225
|
+
`\r
|
|
12226
|
+
${label}${desc}`
|
|
12227
|
+
);
|
|
12228
|
+
}
|
|
12229
|
+
}
|
|
12230
|
+
/**
|
|
12231
|
+
* Highlight the matched filter text in yellow within the label.
|
|
12232
|
+
*/
|
|
12233
|
+
highlightMatch(label) {
|
|
12234
|
+
if (!this.filter || this.filter.length === 0) return label;
|
|
12235
|
+
const lower = label.toLowerCase();
|
|
12236
|
+
const filterLower = this.filter.toLowerCase();
|
|
12237
|
+
const idx = lower.indexOf(filterLower);
|
|
12238
|
+
if (idx < 0) return label;
|
|
12239
|
+
const before = label.substring(0, idx);
|
|
12240
|
+
const match = label.substring(idx, idx + this.filter.length);
|
|
12241
|
+
const after = label.substring(idx + this.filter.length);
|
|
12242
|
+
return `${before}\x1B[33m${match}\x1B[0m${after}`;
|
|
12243
|
+
}
|
|
12244
|
+
/**
|
|
12245
|
+
* Render the help bar at the bottom.
|
|
12246
|
+
*/
|
|
12247
|
+
renderHelpBar() {
|
|
12248
|
+
let help;
|
|
12249
|
+
if (this.selectOptions?.searchable) {
|
|
12250
|
+
if (this.filter.length > 0) {
|
|
12251
|
+
const matchCount = this.filteredOptions.filter((o) => !o.disabled).length;
|
|
12252
|
+
const totalCount = this.options.filter((o) => !o.disabled).length;
|
|
12253
|
+
help = `${matchCount} of ${totalCount} matches \xB7 backspace to clear filter \xB7 enter to select \xB7 esc to cancel`;
|
|
12254
|
+
} else {
|
|
12255
|
+
help = "\u2191/\u2193 navigate \xB7 type to filter \xB7 enter to select \xB7 esc to cancel";
|
|
12256
|
+
}
|
|
12257
|
+
} else {
|
|
12258
|
+
help = "\u2191/\u2193 navigate \xB7 enter to select \xB7 esc to cancel";
|
|
12259
|
+
}
|
|
12260
|
+
this.host.terminal.write(`\r
|
|
12261
|
+
\x1B[2m${help}\x1B[0m`);
|
|
12262
|
+
this.renderedLines++;
|
|
12263
|
+
}
|
|
12264
|
+
};
|
|
12265
|
+
|
|
12266
|
+
// src/lib/input/modes/inline-select-input-mode.ts
|
|
12267
|
+
var InlineSelectInputMode = class extends InputModeBase {
|
|
12268
|
+
constructor(host, resolve, promptText, options, selectOptions) {
|
|
12269
|
+
super(host, resolve);
|
|
12270
|
+
this.promptText = promptText;
|
|
12271
|
+
this.options = options;
|
|
12272
|
+
this.selectOptions = selectOptions;
|
|
12273
|
+
/** Index into the selectable (non-disabled) options */
|
|
12274
|
+
this.selectedIndex = 0;
|
|
12275
|
+
/** Start index of the visible window (into selectable options) */
|
|
12276
|
+
this.visibleStart = 0;
|
|
12277
|
+
/** End index (exclusive) of the visible window */
|
|
12278
|
+
this.visibleEnd = 0;
|
|
12279
|
+
this.selectableOptions = options.filter((o) => !o.disabled);
|
|
12280
|
+
}
|
|
12281
|
+
activate() {
|
|
12282
|
+
this.host.terminal.write("\x1B[?25l");
|
|
12283
|
+
if (this.selectOptions?.default) {
|
|
12284
|
+
const idx = this.selectableOptions.findIndex(
|
|
12285
|
+
(o) => o.value === this.selectOptions.default
|
|
12286
|
+
);
|
|
12287
|
+
if (idx >= 0) {
|
|
12288
|
+
this.selectedIndex = idx;
|
|
12289
|
+
}
|
|
12290
|
+
}
|
|
12291
|
+
this.calculateVisibleWindow();
|
|
12292
|
+
this.render();
|
|
12293
|
+
}
|
|
12294
|
+
/**
|
|
12295
|
+
* Clear the inline options and help bar, then show final answer before resolving.
|
|
12296
|
+
*/
|
|
12297
|
+
resolveAndPop(value) {
|
|
12298
|
+
this.clearExtraLines();
|
|
12299
|
+
const selectedOpt = this.selectableOptions.find((o) => o.value === value);
|
|
12300
|
+
const label = selectedOpt ? selectedOpt.label : value;
|
|
12301
|
+
this.host.terminal.write("\x1B[2K\r");
|
|
12302
|
+
this.host.terminal.write(`\x1B[32m\u2714\x1B[0m ${this.promptText}: \x1B[36m${label}\x1B[0m`);
|
|
12303
|
+
this.host.terminal.write("\x1B[?25h");
|
|
12304
|
+
super.resolveAndPop(value);
|
|
12305
|
+
}
|
|
12306
|
+
/**
|
|
12307
|
+
* Clear the inline options and help bar before aborting.
|
|
12308
|
+
*/
|
|
12309
|
+
abort() {
|
|
12310
|
+
this.clearExtraLines();
|
|
12311
|
+
this.host.terminal.write("\x1B[2K\r");
|
|
12312
|
+
this.host.terminal.write(`\x1B[33m\u2718\x1B[0m ${this.promptText}: \x1B[2mcancelled\x1B[0m`);
|
|
12313
|
+
this.host.terminal.write("\x1B[?25h");
|
|
12314
|
+
super.abort();
|
|
12315
|
+
}
|
|
12316
|
+
async handleInput(data) {
|
|
12317
|
+
if (data === "\x1B[C") {
|
|
12318
|
+
this.moveSelection(1);
|
|
12319
|
+
} else if (data === "\x1B[D") {
|
|
12320
|
+
this.moveSelection(-1);
|
|
12321
|
+
} else if (data === "\r") {
|
|
12322
|
+
if (this.selectableOptions.length > 0) {
|
|
12323
|
+
this.resolveAndPop(this.selectableOptions[this.selectedIndex].value);
|
|
12324
|
+
}
|
|
12325
|
+
}
|
|
12326
|
+
}
|
|
12327
|
+
onResize(cols, rows) {
|
|
12328
|
+
this.calculateVisibleWindow();
|
|
12329
|
+
this.render();
|
|
12330
|
+
}
|
|
12331
|
+
// ── Private helpers ──────────────────────────────────────────────
|
|
12332
|
+
/**
|
|
12333
|
+
* Move selection in the given direction, skipping disabled options.
|
|
12334
|
+
* Wraps around at boundaries.
|
|
12335
|
+
*/
|
|
12336
|
+
moveSelection(direction) {
|
|
12337
|
+
if (this.selectableOptions.length === 0) return;
|
|
12338
|
+
let next = this.selectedIndex + direction;
|
|
12339
|
+
if (next < 0) next = this.selectableOptions.length - 1;
|
|
12340
|
+
if (next >= this.selectableOptions.length) next = 0;
|
|
12341
|
+
this.selectedIndex = next;
|
|
12342
|
+
this.adjustVisibleWindow();
|
|
12343
|
+
if (this.selectOptions?.onChange) {
|
|
12344
|
+
this.selectOptions.onChange(this.selectableOptions[this.selectedIndex].value);
|
|
12345
|
+
}
|
|
12346
|
+
this.render();
|
|
12347
|
+
}
|
|
12348
|
+
/**
|
|
12349
|
+
* Get the prompt prefix string (without ANSI escapes for width calculation).
|
|
12350
|
+
*/
|
|
12351
|
+
getPromptPrefix() {
|
|
12352
|
+
return `? ${this.promptText}: `;
|
|
12353
|
+
}
|
|
12354
|
+
/**
|
|
12355
|
+
* Get the rendered prompt prefix (with ANSI color codes).
|
|
12356
|
+
*/
|
|
12357
|
+
getRenderedPromptPrefix() {
|
|
12358
|
+
return `\x1B[36m?\x1B[0m ${this.promptText}: `;
|
|
12359
|
+
}
|
|
12360
|
+
/**
|
|
12361
|
+
* Calculate the width needed for a single option.
|
|
12362
|
+
* Selected option: `[ label ]` (label.length + 4)
|
|
12363
|
+
* Unselected option: ` label ` (label.length + 2)
|
|
12364
|
+
*/
|
|
12365
|
+
getOptionWidth(opt, isSelected) {
|
|
12366
|
+
return isSelected ? opt.label.length + 4 : opt.label.length + 2;
|
|
12367
|
+
}
|
|
12368
|
+
/**
|
|
12369
|
+
* Calculate how many options fit in the visible window starting from a given index.
|
|
12370
|
+
*/
|
|
12371
|
+
calculateVisibleWindow() {
|
|
12372
|
+
const cols = this.host.getTerminalCols();
|
|
12373
|
+
const promptWidth = this.getPromptPrefix().length;
|
|
12374
|
+
const available = cols - promptWidth;
|
|
12375
|
+
if (this.selectableOptions.length === 0) {
|
|
12376
|
+
this.visibleStart = 0;
|
|
12377
|
+
this.visibleEnd = 0;
|
|
12378
|
+
return;
|
|
12379
|
+
}
|
|
12380
|
+
let totalWidth = 0;
|
|
12381
|
+
for (let i = 0; i < this.selectableOptions.length; i++) {
|
|
12382
|
+
totalWidth += this.getOptionWidth(this.selectableOptions[i], i === this.selectedIndex);
|
|
12383
|
+
}
|
|
12384
|
+
if (totalWidth <= available) {
|
|
12385
|
+
this.visibleStart = 0;
|
|
12386
|
+
this.visibleEnd = this.selectableOptions.length;
|
|
12387
|
+
return;
|
|
12388
|
+
}
|
|
12389
|
+
this.adjustVisibleWindow();
|
|
12390
|
+
}
|
|
12391
|
+
/**
|
|
12392
|
+
* Adjust the visible window so the selected option is visible.
|
|
12393
|
+
* Accounts for scroll indicator widths.
|
|
12394
|
+
*/
|
|
12395
|
+
adjustVisibleWindow() {
|
|
12396
|
+
const cols = this.host.getTerminalCols();
|
|
12397
|
+
const promptWidth = this.getPromptPrefix().length;
|
|
12398
|
+
const available = cols - promptWidth;
|
|
12399
|
+
if (this.selectableOptions.length === 0) return;
|
|
12400
|
+
let totalWidth = 0;
|
|
12401
|
+
for (let i = 0; i < this.selectableOptions.length; i++) {
|
|
12402
|
+
totalWidth += this.getOptionWidth(this.selectableOptions[i], i === this.selectedIndex);
|
|
12403
|
+
}
|
|
12404
|
+
if (totalWidth <= available) {
|
|
12405
|
+
this.visibleStart = 0;
|
|
12406
|
+
this.visibleEnd = this.selectableOptions.length;
|
|
12407
|
+
return;
|
|
12408
|
+
}
|
|
12409
|
+
const indicatorWidth = 2;
|
|
12410
|
+
if (this.selectedIndex < this.visibleStart) {
|
|
12411
|
+
this.visibleStart = this.selectedIndex;
|
|
12412
|
+
}
|
|
12413
|
+
let usedWidth = 0;
|
|
12414
|
+
const hasLeftIndicator = this.visibleStart > 0;
|
|
12415
|
+
if (hasLeftIndicator) {
|
|
12416
|
+
usedWidth += indicatorWidth;
|
|
12417
|
+
}
|
|
12418
|
+
let end = this.visibleStart;
|
|
12419
|
+
for (let i = this.visibleStart; i < this.selectableOptions.length; i++) {
|
|
12420
|
+
const optWidth = this.getOptionWidth(this.selectableOptions[i], i === this.selectedIndex);
|
|
12421
|
+
const needsRightIndicator = i < this.selectableOptions.length - 1;
|
|
12422
|
+
const rightReserve = needsRightIndicator ? indicatorWidth : 0;
|
|
12423
|
+
if (usedWidth + optWidth + rightReserve <= available) {
|
|
12424
|
+
usedWidth += optWidth;
|
|
12425
|
+
end = i + 1;
|
|
12426
|
+
} else {
|
|
12427
|
+
break;
|
|
12428
|
+
}
|
|
12429
|
+
}
|
|
12430
|
+
if (this.selectedIndex >= end) {
|
|
12431
|
+
end = this.selectedIndex + 1;
|
|
12432
|
+
usedWidth = this.getOptionWidth(
|
|
12433
|
+
this.selectableOptions[this.selectedIndex],
|
|
12434
|
+
true
|
|
12435
|
+
);
|
|
12436
|
+
const hasRightIndicator = end < this.selectableOptions.length;
|
|
12437
|
+
if (hasRightIndicator) {
|
|
12438
|
+
usedWidth += indicatorWidth;
|
|
12439
|
+
}
|
|
12440
|
+
let start = this.selectedIndex;
|
|
12441
|
+
for (let i = this.selectedIndex - 1; i >= 0; i--) {
|
|
12442
|
+
const optWidth = this.getOptionWidth(this.selectableOptions[i], false);
|
|
12443
|
+
const needsLeftIndicator = i > 0;
|
|
12444
|
+
const leftReserve = needsLeftIndicator ? indicatorWidth : 0;
|
|
12445
|
+
const currentLeftReserve = start > 0 ? indicatorWidth : 0;
|
|
12446
|
+
if (usedWidth - currentLeftReserve + optWidth + leftReserve <= available) {
|
|
12447
|
+
usedWidth = usedWidth - currentLeftReserve + optWidth + leftReserve;
|
|
12448
|
+
start = i;
|
|
12449
|
+
} else {
|
|
12450
|
+
break;
|
|
12451
|
+
}
|
|
12452
|
+
}
|
|
12453
|
+
this.visibleStart = start;
|
|
12454
|
+
end = this.selectedIndex + 1;
|
|
12455
|
+
usedWidth = 0;
|
|
12456
|
+
if (this.visibleStart > 0) usedWidth += indicatorWidth;
|
|
12457
|
+
for (let i = this.visibleStart; i < end; i++) {
|
|
12458
|
+
usedWidth += this.getOptionWidth(this.selectableOptions[i], i === this.selectedIndex);
|
|
12459
|
+
}
|
|
12460
|
+
for (let i = end; i < this.selectableOptions.length; i++) {
|
|
12461
|
+
const optWidth = this.getOptionWidth(this.selectableOptions[i], false);
|
|
12462
|
+
const needsRightIndicator = i < this.selectableOptions.length - 1;
|
|
12463
|
+
const rightReserve = needsRightIndicator ? indicatorWidth : 0;
|
|
12464
|
+
if (usedWidth + optWidth + rightReserve <= available) {
|
|
12465
|
+
usedWidth += optWidth;
|
|
12466
|
+
end = i + 1;
|
|
12467
|
+
} else {
|
|
12468
|
+
break;
|
|
12469
|
+
}
|
|
12470
|
+
}
|
|
12471
|
+
}
|
|
12472
|
+
this.visibleEnd = end;
|
|
12473
|
+
}
|
|
12474
|
+
/**
|
|
12475
|
+
* Render the complete inline select display.
|
|
12476
|
+
*/
|
|
12477
|
+
render() {
|
|
12478
|
+
this.clearExtraLines();
|
|
12479
|
+
this.host.terminal.write("\x1B[2K\r");
|
|
12480
|
+
this.host.terminal.write(this.getRenderedPromptPrefix());
|
|
12481
|
+
if (this.selectableOptions.length === 0) return;
|
|
12482
|
+
const hasLeftOverflow = this.visibleStart > 0;
|
|
12483
|
+
const hasRightOverflow = this.visibleEnd < this.selectableOptions.length;
|
|
12484
|
+
if (hasLeftOverflow) {
|
|
12485
|
+
this.host.terminal.write("\x1B[2m\u25C2\x1B[0m ");
|
|
12486
|
+
}
|
|
12487
|
+
for (let i = this.visibleStart; i < this.visibleEnd; i++) {
|
|
12488
|
+
const opt = this.selectableOptions[i];
|
|
12489
|
+
const isSelected = i === this.selectedIndex;
|
|
12490
|
+
if (isSelected) {
|
|
12491
|
+
this.host.terminal.write(`\x1B[36m[ ${opt.label} ]\x1B[0m`);
|
|
12492
|
+
} else {
|
|
12493
|
+
this.host.terminal.write(` ${opt.label} `);
|
|
12494
|
+
}
|
|
12495
|
+
}
|
|
12496
|
+
if (hasRightOverflow) {
|
|
12497
|
+
this.host.terminal.write(" \x1B[2m\u25B8\x1B[0m");
|
|
12498
|
+
}
|
|
12499
|
+
this.writeHelp("\u2190/\u2192 navigate \xB7 enter to select \xB7 esc to cancel");
|
|
12500
|
+
}
|
|
12501
|
+
};
|
|
12502
|
+
|
|
12503
|
+
// src/lib/input/modes/multi-select-input-mode.ts
|
|
12504
|
+
var MultiSelectInputMode = class extends InputModeBase {
|
|
12505
|
+
constructor(host, resolve, promptText, options, selectOptions) {
|
|
12506
|
+
super(host, resolve);
|
|
12507
|
+
this.promptText = promptText;
|
|
12508
|
+
this.options = options;
|
|
12509
|
+
this.selectOptions = selectOptions;
|
|
12510
|
+
this.selectedIndex = 0;
|
|
12511
|
+
this.scrollOffset = 0;
|
|
12512
|
+
this.filter = "";
|
|
12513
|
+
/** Number of lines rendered for the option list + help bar (for clearing) */
|
|
12514
|
+
this.renderedLines = 0;
|
|
12515
|
+
this.filteredOptions = [...options];
|
|
12516
|
+
this.maxVisible = Math.min(options.length, host.getTerminalRows() - 4);
|
|
12517
|
+
this.checkedState = /* @__PURE__ */ new Map();
|
|
12518
|
+
for (const opt of options) {
|
|
12519
|
+
this.checkedState.set(opt.value, opt.checked === true);
|
|
12520
|
+
}
|
|
12521
|
+
}
|
|
12522
|
+
activate() {
|
|
12523
|
+
this.host.terminal.write("\x1B[?25l");
|
|
12524
|
+
this.selectedIndex = 0;
|
|
12525
|
+
const selectables = this.filteredOptions.filter((o) => !o.disabled);
|
|
12526
|
+
if (selectables.length > 0) {
|
|
12527
|
+
this.selectedIndex = 0;
|
|
12528
|
+
}
|
|
12529
|
+
this.renderAll();
|
|
12530
|
+
}
|
|
12531
|
+
/**
|
|
12532
|
+
* Handle keyboard events. When searchable and filter is non-empty,
|
|
12533
|
+
* Escape clears the filter instead of aborting.
|
|
12534
|
+
*/
|
|
12535
|
+
handleKeyEvent(event) {
|
|
12536
|
+
if (this.selectOptions?.searchable && this.filter.length > 0) {
|
|
12537
|
+
if (event.code === "Escape") {
|
|
12538
|
+
this.clearFilter();
|
|
12539
|
+
return false;
|
|
12540
|
+
}
|
|
12541
|
+
}
|
|
12542
|
+
return super.handleKeyEvent(event);
|
|
12543
|
+
}
|
|
12544
|
+
/**
|
|
12545
|
+
* Clear rendered option lines and show final answer before resolving.
|
|
12546
|
+
*/
|
|
12547
|
+
resolveAndPop(value) {
|
|
12548
|
+
this.clearRenderedLines();
|
|
12549
|
+
const labels = value.map((v) => {
|
|
12550
|
+
const opt = this.options.find((o) => o.value === v);
|
|
12551
|
+
return opt ? opt.label : v;
|
|
12552
|
+
});
|
|
12553
|
+
const summary = labels.length > 0 ? labels.join(", ") : "none";
|
|
12554
|
+
this.host.terminal.write("\x1B[2K\r");
|
|
12555
|
+
this.host.terminal.write(`\x1B[32m\u2714\x1B[0m ${this.promptText} \x1B[36m${summary}\x1B[0m`);
|
|
12556
|
+
this.host.terminal.write("\x1B[?25h");
|
|
12557
|
+
super.resolveAndPop(value);
|
|
12558
|
+
}
|
|
12559
|
+
/**
|
|
12560
|
+
* Clear rendered option lines before aborting.
|
|
12561
|
+
*/
|
|
12562
|
+
abort() {
|
|
12563
|
+
this.clearRenderedLines();
|
|
12564
|
+
this.host.terminal.write("\x1B[2K\r");
|
|
12565
|
+
this.host.terminal.write(`\x1B[33m\u2718\x1B[0m ${this.promptText} \x1B[2mcancelled\x1B[0m`);
|
|
12566
|
+
this.host.terminal.write("\x1B[?25h");
|
|
12567
|
+
super.abort();
|
|
12568
|
+
}
|
|
12569
|
+
async handleInput(data) {
|
|
12570
|
+
if (data === "\x1B[A") {
|
|
12571
|
+
this.moveSelection(-1);
|
|
12572
|
+
} else if (data === "\x1B[B") {
|
|
12573
|
+
this.moveSelection(1);
|
|
12574
|
+
} else if (data === " ") {
|
|
12575
|
+
this.toggleCurrent();
|
|
12576
|
+
} else if (data === "a" && (!this.selectOptions?.searchable || this.filter.length === 0)) {
|
|
12577
|
+
this.toggleAll();
|
|
12578
|
+
} else if (data === "\r") {
|
|
12579
|
+
this.resolveAndPop(this.getCheckedValues());
|
|
12580
|
+
} else if (data === "\x7F") {
|
|
12581
|
+
if (this.selectOptions?.searchable && this.filter.length > 0) {
|
|
12582
|
+
this.filter = this.filter.slice(0, -1);
|
|
12583
|
+
this.applyFilter();
|
|
12584
|
+
this.renderAll();
|
|
12585
|
+
}
|
|
12586
|
+
} else if (data.startsWith("\x1B")) ; else if (this.selectOptions?.searchable && data.length === 1 && data >= " ") {
|
|
12587
|
+
this.filter += data;
|
|
12588
|
+
this.applyFilter();
|
|
12589
|
+
this.renderAll();
|
|
12590
|
+
}
|
|
12591
|
+
}
|
|
12592
|
+
onResize(cols, rows) {
|
|
12593
|
+
this.maxVisible = Math.min(this.filteredOptions.length, rows - 4);
|
|
12594
|
+
if (this.scrollOffset + this.maxVisible > this.getSelectableCount()) {
|
|
12595
|
+
this.scrollOffset = Math.max(0, this.getSelectableCount() - this.maxVisible);
|
|
12596
|
+
}
|
|
12597
|
+
this.renderAll();
|
|
12598
|
+
}
|
|
12599
|
+
// ── Private helpers ──────────────────────────────────────────────
|
|
12600
|
+
/**
|
|
12601
|
+
* Build flattened display list with group headers inserted.
|
|
12602
|
+
*/
|
|
12603
|
+
buildDisplayList() {
|
|
12604
|
+
const items = [];
|
|
12605
|
+
let lastGroup;
|
|
12606
|
+
for (const opt of this.filteredOptions) {
|
|
12607
|
+
if (opt.group && opt.group !== lastGroup) {
|
|
12608
|
+
items.push({ type: "group-header", groupName: opt.group });
|
|
12609
|
+
lastGroup = opt.group;
|
|
12610
|
+
}
|
|
12611
|
+
items.push({ type: "option", option: opt });
|
|
12612
|
+
}
|
|
12613
|
+
return items;
|
|
12614
|
+
}
|
|
12615
|
+
/**
|
|
12616
|
+
* Get the total count of selectable items (non-disabled options).
|
|
12617
|
+
*/
|
|
12618
|
+
getSelectableCount() {
|
|
12619
|
+
return this.filteredOptions.filter((o) => !o.disabled).length;
|
|
12620
|
+
}
|
|
12621
|
+
/**
|
|
12622
|
+
* Get an array of currently checked values (preserving original order).
|
|
12623
|
+
*/
|
|
12624
|
+
getCheckedValues() {
|
|
12625
|
+
const result = [];
|
|
12626
|
+
for (const opt of this.options) {
|
|
12627
|
+
if (this.checkedState.get(opt.value) === true) {
|
|
12628
|
+
result.push(opt.value);
|
|
12629
|
+
}
|
|
12630
|
+
}
|
|
12631
|
+
return result;
|
|
12632
|
+
}
|
|
12633
|
+
/**
|
|
12634
|
+
* Move selection in the given direction (+1 or -1), skipping disabled options.
|
|
12635
|
+
* Wraps around at boundaries.
|
|
12636
|
+
*/
|
|
12637
|
+
moveSelection(direction) {
|
|
12638
|
+
const selectableCount = this.getSelectableCount();
|
|
12639
|
+
if (selectableCount === 0) return;
|
|
12640
|
+
let next = this.selectedIndex + direction;
|
|
12641
|
+
if (next < 0) next = selectableCount - 1;
|
|
12642
|
+
if (next >= selectableCount) next = 0;
|
|
12643
|
+
this.selectedIndex = next;
|
|
12644
|
+
if (this.selectedIndex < this.scrollOffset) {
|
|
12645
|
+
this.scrollOffset = this.selectedIndex;
|
|
12646
|
+
} else if (this.selectedIndex >= this.scrollOffset + this.maxVisible) {
|
|
12647
|
+
this.scrollOffset = this.selectedIndex - this.maxVisible + 1;
|
|
12648
|
+
}
|
|
12649
|
+
this.renderAll();
|
|
12650
|
+
}
|
|
12651
|
+
/**
|
|
12652
|
+
* Toggle the checked state of the currently selected option.
|
|
12653
|
+
*/
|
|
12654
|
+
toggleCurrent() {
|
|
12655
|
+
const selectables = this.filteredOptions.filter((o) => !o.disabled);
|
|
12656
|
+
if (selectables.length === 0 || this.selectedIndex >= selectables.length) return;
|
|
12657
|
+
const opt = selectables[this.selectedIndex];
|
|
12658
|
+
const current = this.checkedState.get(opt.value) === true;
|
|
12659
|
+
this.checkedState.set(opt.value, !current);
|
|
12660
|
+
if (this.selectOptions?.onChange) {
|
|
12661
|
+
this.selectOptions.onChange(this.getCheckedValues());
|
|
12662
|
+
}
|
|
12663
|
+
this.renderAll();
|
|
12664
|
+
}
|
|
12665
|
+
/**
|
|
12666
|
+
* Toggle all non-disabled options: if all are checked, uncheck all; otherwise check all.
|
|
12667
|
+
*/
|
|
12668
|
+
toggleAll() {
|
|
12669
|
+
const nonDisabled = this.options.filter((o) => !o.disabled);
|
|
12670
|
+
const allChecked = nonDisabled.every((o) => this.checkedState.get(o.value) === true);
|
|
12671
|
+
for (const opt of nonDisabled) {
|
|
12672
|
+
this.checkedState.set(opt.value, !allChecked);
|
|
12673
|
+
}
|
|
12674
|
+
if (this.selectOptions?.onChange) {
|
|
12675
|
+
this.selectOptions.onChange(this.getCheckedValues());
|
|
12676
|
+
}
|
|
12677
|
+
this.renderAll();
|
|
12678
|
+
}
|
|
12679
|
+
/**
|
|
12680
|
+
* Apply the current filter text to the options list.
|
|
12681
|
+
*/
|
|
12682
|
+
applyFilter() {
|
|
12683
|
+
if (this.filter.length === 0) {
|
|
12684
|
+
this.filteredOptions = [...this.options];
|
|
12685
|
+
} else {
|
|
12686
|
+
const lower = this.filter.toLowerCase();
|
|
12687
|
+
this.filteredOptions = this.options.filter(
|
|
12688
|
+
(o) => o.label.toLowerCase().includes(lower)
|
|
12689
|
+
);
|
|
12690
|
+
}
|
|
12691
|
+
this.selectedIndex = 0;
|
|
12692
|
+
this.scrollOffset = 0;
|
|
12693
|
+
this.maxVisible = Math.min(
|
|
12694
|
+
this.filteredOptions.length,
|
|
12695
|
+
this.host.getTerminalRows() - 4
|
|
12696
|
+
);
|
|
12697
|
+
}
|
|
12698
|
+
/**
|
|
12699
|
+
* Clear the filter and restore full option list.
|
|
12700
|
+
*/
|
|
12701
|
+
clearFilter() {
|
|
12702
|
+
this.filter = "";
|
|
12703
|
+
this.applyFilter();
|
|
12704
|
+
this.renderAll();
|
|
12705
|
+
}
|
|
12706
|
+
/**
|
|
12707
|
+
* Clear all previously rendered lines and re-render everything.
|
|
12708
|
+
*/
|
|
12709
|
+
renderAll() {
|
|
12710
|
+
this.clearRenderedLines();
|
|
12711
|
+
this.renderPrompt();
|
|
12712
|
+
this.renderOptions();
|
|
12713
|
+
this.renderHelpBar();
|
|
12714
|
+
if (this.renderedLines > 0) {
|
|
12715
|
+
this.host.terminal.write(`\x1B[${this.renderedLines}A`);
|
|
12716
|
+
}
|
|
12717
|
+
}
|
|
12718
|
+
/**
|
|
12719
|
+
* Clear all lines we previously rendered (below the cursor).
|
|
12720
|
+
*/
|
|
12721
|
+
clearRenderedLines() {
|
|
12722
|
+
if (this.renderedLines > 0) {
|
|
12723
|
+
this.host.terminal.write("\x1B[s");
|
|
12724
|
+
for (let i = 0; i < this.renderedLines; i++) {
|
|
12725
|
+
this.host.terminal.write("\x1B[B\x1B[2K");
|
|
12726
|
+
}
|
|
12727
|
+
this.host.terminal.write("\x1B[u");
|
|
12728
|
+
this.renderedLines = 0;
|
|
12729
|
+
}
|
|
12730
|
+
}
|
|
12731
|
+
/**
|
|
12732
|
+
* Render the prompt line.
|
|
12733
|
+
*/
|
|
12734
|
+
renderPrompt() {
|
|
12735
|
+
this.host.terminal.write("\x1B[2K\r");
|
|
12736
|
+
if (this.selectOptions?.searchable) {
|
|
12737
|
+
if (this.filter.length > 0) {
|
|
12738
|
+
this.host.terminal.write(
|
|
12739
|
+
`\x1B[36m?\x1B[0m ${this.promptText}: ${this.filter}`
|
|
12740
|
+
);
|
|
12741
|
+
} else {
|
|
12742
|
+
this.host.terminal.write(
|
|
12743
|
+
`\x1B[36m?\x1B[0m ${this.promptText}: \x1B[2mType to filter...\x1B[0m`
|
|
12744
|
+
);
|
|
12745
|
+
}
|
|
12746
|
+
} else {
|
|
12747
|
+
this.host.terminal.write(`\x1B[36m?\x1B[0m ${this.promptText}:`);
|
|
12748
|
+
}
|
|
12749
|
+
}
|
|
12750
|
+
/**
|
|
12751
|
+
* Render the visible slice of the option list.
|
|
12752
|
+
*/
|
|
12753
|
+
renderOptions() {
|
|
12754
|
+
const items = this.buildDisplayList();
|
|
12755
|
+
const selectables = this.filteredOptions.filter((o) => !o.disabled);
|
|
12756
|
+
const visibleStart = this.scrollOffset;
|
|
12757
|
+
const visibleEnd = Math.min(
|
|
12758
|
+
selectables.length,
|
|
12759
|
+
this.scrollOffset + this.maxVisible
|
|
12760
|
+
);
|
|
12761
|
+
if (visibleStart > 0) {
|
|
12762
|
+
this.host.terminal.write(`\r
|
|
12763
|
+
\x1B[2m \u2191 ${visibleStart} more above\x1B[0m`);
|
|
12764
|
+
this.renderedLines++;
|
|
12765
|
+
}
|
|
12766
|
+
let selectableIdx = 0;
|
|
12767
|
+
for (const item of items) {
|
|
12768
|
+
if (item.type === "group-header") {
|
|
12769
|
+
if (this.isGroupVisible(item.groupName, items, visibleStart, visibleEnd)) {
|
|
12770
|
+
this.host.terminal.write(
|
|
12771
|
+
`\r
|
|
12772
|
+
\x1B[35m\u2500\u2500 ${item.groupName} \u2500\u2500\x1B[0m`
|
|
12773
|
+
);
|
|
12774
|
+
this.renderedLines++;
|
|
12775
|
+
}
|
|
12776
|
+
} else {
|
|
12777
|
+
const opt = item.option;
|
|
12778
|
+
if (opt.disabled) {
|
|
12779
|
+
if (this.isDisabledOptionVisible(items, item, visibleStart, visibleEnd)) {
|
|
12780
|
+
this.renderOptionLine(opt, false);
|
|
12781
|
+
this.renderedLines++;
|
|
12782
|
+
}
|
|
12783
|
+
} else {
|
|
12784
|
+
if (selectableIdx >= visibleStart && selectableIdx < visibleEnd) {
|
|
12785
|
+
const isSelected = selectableIdx === this.selectedIndex;
|
|
12786
|
+
this.renderOptionLine(opt, isSelected);
|
|
12787
|
+
this.renderedLines++;
|
|
12788
|
+
}
|
|
12789
|
+
selectableIdx++;
|
|
12790
|
+
}
|
|
12791
|
+
}
|
|
12792
|
+
}
|
|
12793
|
+
const remaining = selectables.length - visibleEnd;
|
|
12794
|
+
if (remaining > 0) {
|
|
12795
|
+
this.host.terminal.write(`\r
|
|
12796
|
+
\x1B[2m \u2193 ${remaining} more below\x1B[0m`);
|
|
12797
|
+
this.renderedLines++;
|
|
12798
|
+
}
|
|
12799
|
+
}
|
|
12800
|
+
/**
|
|
12801
|
+
* Check if a group header should be visible given the current visible range.
|
|
12802
|
+
*/
|
|
12803
|
+
isGroupVisible(groupName, items, visibleStart, visibleEnd) {
|
|
12804
|
+
let selectableIdx = 0;
|
|
12805
|
+
for (const item of items) {
|
|
12806
|
+
if (item.type === "option" && !item.option.disabled) {
|
|
12807
|
+
if (item.option.group === groupName && selectableIdx >= visibleStart && selectableIdx < visibleEnd) {
|
|
12808
|
+
return true;
|
|
12809
|
+
}
|
|
12810
|
+
selectableIdx++;
|
|
12811
|
+
}
|
|
12812
|
+
}
|
|
12813
|
+
return false;
|
|
12814
|
+
}
|
|
12815
|
+
/**
|
|
12816
|
+
* Check if a disabled option should be visible (adjacent to visible selectables).
|
|
12817
|
+
*/
|
|
12818
|
+
isDisabledOptionVisible(items, disabledItem, visibleStart, visibleEnd) {
|
|
12819
|
+
const idx = items.indexOf(disabledItem);
|
|
12820
|
+
let selectableIdx = 0;
|
|
12821
|
+
for (let i = 0; i < items.length; i++) {
|
|
12822
|
+
if (items[i].type === "option" && !items[i].option.disabled) {
|
|
12823
|
+
if (i > idx) {
|
|
12824
|
+
return selectableIdx >= visibleStart && selectableIdx < visibleEnd;
|
|
12825
|
+
}
|
|
12826
|
+
selectableIdx++;
|
|
12827
|
+
}
|
|
12828
|
+
}
|
|
12829
|
+
return selectableIdx > 0 && selectableIdx - 1 >= visibleStart && selectableIdx - 1 < visibleEnd;
|
|
12830
|
+
}
|
|
12831
|
+
/**
|
|
12832
|
+
* Render a single option line with checkbox indicator.
|
|
12833
|
+
*/
|
|
12834
|
+
renderOptionLine(opt, isSelected) {
|
|
12835
|
+
const isChecked = this.checkedState.get(opt.value) === true;
|
|
12836
|
+
const label = this.highlightMatch(opt.label);
|
|
12837
|
+
const desc = opt.description ? ` \x1B[2;3m\u2014 ${opt.description}\x1B[0m` : "";
|
|
12838
|
+
const checkbox = isChecked ? "\x1B[32m\u25C9\x1B[0m" : "\x1B[2m\u25CB\x1B[0m";
|
|
12839
|
+
if (opt.disabled) {
|
|
12840
|
+
const disabledCheckbox = isChecked ? "\x1B[2m\u25C9\x1B[0m" : "\x1B[2m\u25CB\x1B[0m";
|
|
12841
|
+
this.host.terminal.write(
|
|
12842
|
+
`\r
|
|
12843
|
+
${disabledCheckbox} \x1B[2m${opt.label}${desc} (disabled)\x1B[0m`
|
|
12844
|
+
);
|
|
12845
|
+
} else if (isSelected) {
|
|
12846
|
+
this.host.terminal.write(
|
|
12847
|
+
`\r
|
|
12848
|
+
\x1B[48;5;24m\x1B[38;5;117m\u276F ${checkbox} ${label}${desc}\x1B[0m`
|
|
12849
|
+
);
|
|
12850
|
+
} else {
|
|
12851
|
+
this.host.terminal.write(
|
|
12852
|
+
`\r
|
|
12853
|
+
${checkbox} ${label}${desc}`
|
|
12854
|
+
);
|
|
12855
|
+
}
|
|
12856
|
+
}
|
|
12857
|
+
/**
|
|
12858
|
+
* Highlight the matched filter text in yellow within the label.
|
|
12859
|
+
*/
|
|
12860
|
+
highlightMatch(label) {
|
|
12861
|
+
if (!this.filter || this.filter.length === 0) return label;
|
|
12862
|
+
const lower = label.toLowerCase();
|
|
12863
|
+
const filterLower = this.filter.toLowerCase();
|
|
12864
|
+
const idx = lower.indexOf(filterLower);
|
|
12865
|
+
if (idx < 0) return label;
|
|
12866
|
+
const before = label.substring(0, idx);
|
|
12867
|
+
const match = label.substring(idx, idx + this.filter.length);
|
|
12868
|
+
const after = label.substring(idx + this.filter.length);
|
|
12869
|
+
return `${before}\x1B[33m${match}\x1B[0m${after}`;
|
|
12870
|
+
}
|
|
12871
|
+
/**
|
|
12872
|
+
* Render the help bar at the bottom.
|
|
12873
|
+
*/
|
|
12874
|
+
renderHelpBar() {
|
|
12875
|
+
const checkedCount = this.getCheckedValues().length;
|
|
12876
|
+
let help;
|
|
12877
|
+
if (this.selectOptions?.searchable) {
|
|
12878
|
+
if (this.filter.length > 0) {
|
|
12879
|
+
const matchCount = this.filteredOptions.filter((o) => !o.disabled).length;
|
|
12880
|
+
const totalCount = this.options.filter((o) => !o.disabled).length;
|
|
12881
|
+
help = `${checkedCount} selected \xB7 ${matchCount} of ${totalCount} matches \xB7 space to toggle \xB7 enter to confirm`;
|
|
12882
|
+
} else {
|
|
12883
|
+
help = `${checkedCount} selected \xB7 space to toggle \xB7 a to select all \xB7 type to filter \xB7 enter to confirm`;
|
|
12884
|
+
}
|
|
12885
|
+
} else {
|
|
12886
|
+
help = `${checkedCount} selected \xB7 space to toggle \xB7 a to select all \xB7 enter to confirm`;
|
|
12887
|
+
}
|
|
12888
|
+
this.host.terminal.write(`\r
|
|
12889
|
+
\x1B[2m${help}\x1B[0m`);
|
|
12890
|
+
this.renderedLines++;
|
|
12891
|
+
}
|
|
12892
|
+
};
|
|
12893
|
+
|
|
12894
|
+
// src/lib/input/modes/date-input-mode.ts
|
|
12895
|
+
var DateInputMode = class extends InputModeBase {
|
|
12896
|
+
constructor(host, resolve, promptText, options) {
|
|
12897
|
+
super(host, resolve);
|
|
12898
|
+
this.promptText = promptText;
|
|
12899
|
+
this.options = options;
|
|
12900
|
+
this.hasError = false;
|
|
12901
|
+
this.format = options?.format ?? "YYYY-MM-DD";
|
|
12902
|
+
this.separator = this.extractSeparator(this.format);
|
|
12903
|
+
this.buffer = options?.default ?? "";
|
|
12904
|
+
this.cursorPosition = this.buffer.length;
|
|
12905
|
+
}
|
|
12906
|
+
activate() {
|
|
12907
|
+
const prompt = this.buildPrompt();
|
|
12908
|
+
this.host.writeToTerminal(prompt);
|
|
12909
|
+
if (this.buffer) {
|
|
12910
|
+
this.host.writeToTerminal(this.buffer);
|
|
12911
|
+
}
|
|
12912
|
+
}
|
|
12913
|
+
async handleInput(data) {
|
|
12914
|
+
if (this.hasError) {
|
|
12915
|
+
this.hasError = false;
|
|
12916
|
+
this.clearExtraLines();
|
|
12917
|
+
}
|
|
12918
|
+
if (data === "\r") {
|
|
12919
|
+
const error = this.validateBuffer();
|
|
12920
|
+
if (error) {
|
|
12921
|
+
this.hasError = true;
|
|
12922
|
+
this.writeError(error);
|
|
12923
|
+
return;
|
|
12924
|
+
}
|
|
12925
|
+
this.resolveAndPop(this.buffer);
|
|
12926
|
+
} else if (data === "\x7F") {
|
|
12927
|
+
if (this.cursorPosition > 0) {
|
|
12928
|
+
this.buffer = this.buffer.slice(0, this.cursorPosition - 1) + this.buffer.slice(this.cursorPosition);
|
|
12929
|
+
this.cursorPosition--;
|
|
12930
|
+
this.renderLine();
|
|
12931
|
+
}
|
|
12932
|
+
} else if (data === "\x1B[D") {
|
|
12933
|
+
if (this.cursorPosition > 0) {
|
|
12934
|
+
this.cursorPosition--;
|
|
12935
|
+
this.host.terminal.write(data);
|
|
12936
|
+
}
|
|
12937
|
+
} else if (data === "\x1B[C") {
|
|
12938
|
+
if (this.cursorPosition < this.buffer.length) {
|
|
12939
|
+
this.cursorPosition++;
|
|
12940
|
+
this.host.terminal.write(data);
|
|
12941
|
+
}
|
|
12942
|
+
} else if (data.startsWith("\x1B")) ; else {
|
|
12943
|
+
for (const ch of data) {
|
|
12944
|
+
if (ch >= "0" && ch <= "9" || ch === this.separator) {
|
|
12945
|
+
this.buffer = this.buffer.slice(0, this.cursorPosition) + ch + this.buffer.slice(this.cursorPosition);
|
|
12946
|
+
this.cursorPosition++;
|
|
12947
|
+
}
|
|
12948
|
+
}
|
|
12949
|
+
this.renderLine();
|
|
12950
|
+
}
|
|
12951
|
+
}
|
|
12952
|
+
onResize() {
|
|
12953
|
+
this.renderLine();
|
|
12954
|
+
}
|
|
12955
|
+
buildPrompt() {
|
|
12956
|
+
return `\x1B[36m?\x1B[0m ${this.promptText} \x1B[2m(${this.format})\x1B[0m`;
|
|
12957
|
+
}
|
|
12958
|
+
renderLine() {
|
|
12959
|
+
const prompt = this.buildPrompt();
|
|
12960
|
+
this.redrawLine(prompt, this.buffer, this.cursorPosition);
|
|
12961
|
+
}
|
|
12962
|
+
/**
|
|
12963
|
+
* Extract the separator character from a format string.
|
|
12964
|
+
* Returns the first non-alphabetic character found (e.g., '-' from 'YYYY-MM-DD').
|
|
12965
|
+
* Defaults to '-' if none is found.
|
|
12966
|
+
*/
|
|
12967
|
+
extractSeparator(format) {
|
|
12968
|
+
for (const ch of format) {
|
|
12969
|
+
if (ch < "A" || ch > "z" || ch > "Z" && ch < "a") {
|
|
12970
|
+
return ch;
|
|
12971
|
+
}
|
|
12972
|
+
}
|
|
12973
|
+
return "-";
|
|
12974
|
+
}
|
|
12975
|
+
/**
|
|
12976
|
+
* Parse the buffer according to the current format string.
|
|
12977
|
+
* Returns { year, month, day } or null if parsing fails.
|
|
12978
|
+
*/
|
|
12979
|
+
parseBuffer() {
|
|
12980
|
+
const parts = this.buffer.split(this.separator);
|
|
12981
|
+
const formatParts = this.format.split(this.separator);
|
|
12982
|
+
if (parts.length !== formatParts.length) return null;
|
|
12983
|
+
let year = 0, month = 0, day = 0;
|
|
12984
|
+
for (let i = 0; i < formatParts.length; i++) {
|
|
12985
|
+
const token = formatParts[i].toUpperCase();
|
|
12986
|
+
const value = parseInt(parts[i], 10);
|
|
12987
|
+
if (isNaN(value)) return null;
|
|
12988
|
+
if (token === "YYYY") year = value;
|
|
12989
|
+
else if (token === "MM") month = value;
|
|
12990
|
+
else if (token === "DD") day = value;
|
|
12991
|
+
}
|
|
12992
|
+
return { year, month, day };
|
|
12993
|
+
}
|
|
12994
|
+
/**
|
|
12995
|
+
* Validate the buffer as a real calendar date.
|
|
12996
|
+
* Returns an error message string or null if valid.
|
|
12997
|
+
*/
|
|
12998
|
+
validateBuffer() {
|
|
12999
|
+
if (!this.buffer) {
|
|
13000
|
+
return `Please enter a date in ${this.format} format`;
|
|
13001
|
+
}
|
|
13002
|
+
const parsed = this.parseBuffer();
|
|
13003
|
+
if (!parsed) {
|
|
13004
|
+
return `Invalid date format, expected ${this.format}`;
|
|
13005
|
+
}
|
|
13006
|
+
const { year, month, day } = parsed;
|
|
13007
|
+
if (month < 1 || month > 12) {
|
|
13008
|
+
return "Month must be between 1 and 12";
|
|
13009
|
+
}
|
|
13010
|
+
const maxDay = this.daysInMonth(year, month);
|
|
13011
|
+
if (day < 1 || day > maxDay) {
|
|
13012
|
+
return `Day must be between 1 and ${maxDay} for the given month`;
|
|
13013
|
+
}
|
|
13014
|
+
if (this.options?.min) {
|
|
13015
|
+
const minDate = this.parseDate(this.options.min);
|
|
13016
|
+
const current = new Date(year, month - 1, day);
|
|
13017
|
+
if (minDate && current < minDate) {
|
|
13018
|
+
return `Date must not be before ${this.options.min}`;
|
|
13019
|
+
}
|
|
13020
|
+
}
|
|
13021
|
+
if (this.options?.max) {
|
|
13022
|
+
const maxDate = this.parseDate(this.options.max);
|
|
13023
|
+
const current = new Date(year, month - 1, day);
|
|
13024
|
+
if (maxDate && current > maxDate) {
|
|
13025
|
+
return `Date must not be after ${this.options.max}`;
|
|
13026
|
+
}
|
|
13027
|
+
}
|
|
13028
|
+
return null;
|
|
13029
|
+
}
|
|
13030
|
+
/**
|
|
13031
|
+
* Returns the number of days in a given month (accounting for leap years).
|
|
13032
|
+
*/
|
|
13033
|
+
daysInMonth(year, month) {
|
|
13034
|
+
return new Date(year, month, 0).getDate();
|
|
13035
|
+
}
|
|
13036
|
+
/**
|
|
13037
|
+
* Parse a date string (in the same format as this mode) into a Date object.
|
|
13038
|
+
*/
|
|
13039
|
+
parseDate(dateStr) {
|
|
13040
|
+
const parts = dateStr.split(this.separator);
|
|
13041
|
+
const formatParts = this.format.split(this.separator);
|
|
13042
|
+
if (parts.length !== formatParts.length) return null;
|
|
13043
|
+
let year = 0, month = 0, day = 0;
|
|
13044
|
+
for (let i = 0; i < formatParts.length; i++) {
|
|
13045
|
+
const token = formatParts[i].toUpperCase();
|
|
13046
|
+
const value = parseInt(parts[i], 10);
|
|
13047
|
+
if (isNaN(value)) return null;
|
|
13048
|
+
if (token === "YYYY") year = value;
|
|
13049
|
+
else if (token === "MM") month = value;
|
|
13050
|
+
else if (token === "DD") day = value;
|
|
13051
|
+
}
|
|
13052
|
+
return new Date(year, month - 1, day);
|
|
13053
|
+
}
|
|
13054
|
+
};
|
|
13055
|
+
|
|
13056
|
+
// src/lib/input/modes/file-input-mode.ts
|
|
13057
|
+
var FileInputMode = class extends InputModeBase {
|
|
13058
|
+
constructor(host, resolve, promptText, options) {
|
|
13059
|
+
super(host, resolve);
|
|
13060
|
+
this.promptText = promptText;
|
|
13061
|
+
this.options = options;
|
|
13062
|
+
this.waiting = false;
|
|
13063
|
+
}
|
|
13064
|
+
activate() {
|
|
13065
|
+
const provider = this.host.filePickerProvider;
|
|
13066
|
+
const acceptHint = this.options?.accept ? ` \x1B[2m(${this.options.accept})\x1B[0m` : "";
|
|
13067
|
+
this.host.writeToTerminal(`\x1B[36m?\x1B[0m ${this.promptText}${acceptHint}: `);
|
|
13068
|
+
if (!provider.isSupported) {
|
|
13069
|
+
this.host.writeToTerminal("\x1B[2m(file picker not available)\x1B[0m");
|
|
13070
|
+
this.abort();
|
|
13071
|
+
return;
|
|
13072
|
+
}
|
|
13073
|
+
this.host.writeToTerminal("\x1B[33mOpening file picker...\x1B[0m");
|
|
13074
|
+
this.waiting = true;
|
|
13075
|
+
this.pickFiles(provider);
|
|
13076
|
+
}
|
|
13077
|
+
async pickFiles(provider) {
|
|
13078
|
+
try {
|
|
13079
|
+
let results;
|
|
13080
|
+
if (this.options?.directory) {
|
|
13081
|
+
const dir = await provider.pickDirectory();
|
|
13082
|
+
results = dir ? [dir] : null;
|
|
13083
|
+
} else {
|
|
13084
|
+
results = await provider.pickFiles(this.options);
|
|
13085
|
+
}
|
|
13086
|
+
this.waiting = false;
|
|
13087
|
+
if (!results || this.isResolved) {
|
|
13088
|
+
this.host.terminal.write("\x1B[2K\r");
|
|
13089
|
+
this.host.writeToTerminal(`\x1B[36m?\x1B[0m ${this.promptText}: \x1B[2m(cancelled)\x1B[0m\r
|
|
13090
|
+
`);
|
|
13091
|
+
if (!this.isResolved) this.abort();
|
|
13092
|
+
return;
|
|
13093
|
+
}
|
|
13094
|
+
this.host.terminal.write("\x1B[2K\r");
|
|
13095
|
+
this.host.writeToTerminal(`\x1B[36m?\x1B[0m ${this.promptText}:\r
|
|
13096
|
+
`);
|
|
13097
|
+
let totalSize = 0;
|
|
13098
|
+
for (const file of results) {
|
|
13099
|
+
const sizeStr = this.formatSize(file.size);
|
|
13100
|
+
this.host.writeToTerminal(` \x1B[32m\u2714\x1B[0m ${file.name} \x1B[2m(${sizeStr})\x1B[0m\r
|
|
13101
|
+
`);
|
|
13102
|
+
totalSize += file.size;
|
|
13103
|
+
}
|
|
13104
|
+
if (results.length > 1) {
|
|
13105
|
+
this.host.writeToTerminal(` \x1B[2m${results.length} files selected \xB7 ${this.formatSize(totalSize)} total\x1B[0m\r
|
|
13106
|
+
`);
|
|
13107
|
+
}
|
|
13108
|
+
this.resolveAndPop(results);
|
|
13109
|
+
} catch {
|
|
13110
|
+
this.waiting = false;
|
|
13111
|
+
if (this.isResolved) return;
|
|
13112
|
+
this.host.terminal.write("\x1B[2K\r");
|
|
13113
|
+
this.host.writeToTerminal(`\x1B[36m?\x1B[0m ${this.promptText}: \x1B[31m(error)\x1B[0m\r
|
|
13114
|
+
`);
|
|
13115
|
+
this.abort();
|
|
13116
|
+
}
|
|
13117
|
+
}
|
|
13118
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
13119
|
+
async handleInput(_data) {
|
|
13120
|
+
}
|
|
13121
|
+
formatSize(bytes) {
|
|
13122
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
13123
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
13124
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
13125
|
+
}
|
|
13126
|
+
};
|
|
13127
|
+
|
|
13128
|
+
// src/lib/services/cli-input-reader.ts
|
|
13129
|
+
var CliInputReader = class {
|
|
13130
|
+
constructor(host) {
|
|
13131
|
+
this.host = host;
|
|
13132
|
+
}
|
|
13133
|
+
readLine(prompt, options) {
|
|
13134
|
+
return new Promise((resolve) => {
|
|
13135
|
+
const mode = new LineInputMode(this.host, resolve, prompt, options);
|
|
13136
|
+
this.host.pushMode(mode);
|
|
13137
|
+
});
|
|
13138
|
+
}
|
|
13139
|
+
readPassword(prompt) {
|
|
13140
|
+
return new Promise((resolve) => {
|
|
13141
|
+
const mode = new PasswordInputMode(this.host, resolve, prompt);
|
|
13142
|
+
this.host.pushMode(mode);
|
|
13143
|
+
});
|
|
13144
|
+
}
|
|
13145
|
+
readConfirm(prompt, defaultValue = false) {
|
|
13146
|
+
return new Promise((resolve) => {
|
|
13147
|
+
const mode = new ConfirmInputMode(this.host, resolve, prompt, defaultValue);
|
|
13148
|
+
this.host.pushMode(mode);
|
|
13149
|
+
});
|
|
13150
|
+
}
|
|
13151
|
+
readSelect(prompt, options, selectOptions) {
|
|
13152
|
+
if (!options || options.length === 0) {
|
|
13153
|
+
return Promise.reject(new Error("readSelect requires at least one option"));
|
|
13154
|
+
}
|
|
13155
|
+
return new Promise((resolve) => {
|
|
13156
|
+
const mode = new SelectInputMode(this.host, resolve, prompt, options, selectOptions);
|
|
13157
|
+
this.host.pushMode(mode);
|
|
13158
|
+
});
|
|
13159
|
+
}
|
|
13160
|
+
readSelectInline(prompt, options, selectOptions) {
|
|
13161
|
+
if (!options || options.length === 0) {
|
|
13162
|
+
return Promise.reject(new Error("readSelectInline requires at least one option"));
|
|
13163
|
+
}
|
|
13164
|
+
return new Promise((resolve) => {
|
|
13165
|
+
const mode = new InlineSelectInputMode(this.host, resolve, prompt, options, selectOptions);
|
|
13166
|
+
this.host.pushMode(mode);
|
|
13167
|
+
});
|
|
13168
|
+
}
|
|
13169
|
+
readMultiSelect(prompt, options, selectOptions) {
|
|
13170
|
+
if (!options || options.length === 0) {
|
|
13171
|
+
return Promise.reject(new Error("readMultiSelect requires at least one option"));
|
|
13172
|
+
}
|
|
13173
|
+
return new Promise((resolve) => {
|
|
13174
|
+
const mode = new MultiSelectInputMode(this.host, resolve, prompt, options, selectOptions);
|
|
13175
|
+
this.host.pushMode(mode);
|
|
13176
|
+
});
|
|
13177
|
+
}
|
|
13178
|
+
readNumber(prompt, options) {
|
|
13179
|
+
return new Promise((resolve) => {
|
|
13180
|
+
const mode = new NumberInputMode(this.host, resolve, prompt, options);
|
|
13181
|
+
this.host.pushMode(mode);
|
|
13182
|
+
});
|
|
13183
|
+
}
|
|
13184
|
+
readDate(prompt, options) {
|
|
13185
|
+
return new Promise((resolve) => {
|
|
13186
|
+
const mode = new DateInputMode(this.host, resolve, prompt, options);
|
|
13187
|
+
this.host.pushMode(mode);
|
|
13188
|
+
});
|
|
13189
|
+
}
|
|
13190
|
+
readFile(prompt, options) {
|
|
13191
|
+
return new Promise((resolve) => {
|
|
13192
|
+
const mode = new FileInputMode(this.host, resolve, prompt, options);
|
|
13193
|
+
this.host.pushMode(mode);
|
|
13194
|
+
});
|
|
13195
|
+
}
|
|
13196
|
+
};
|
|
13197
|
+
|
|
13198
|
+
// src/lib/services/file-picker/browser-file-picker-provider.ts
|
|
13199
|
+
var BrowserFilePickerProvider = class {
|
|
13200
|
+
constructor() {
|
|
13201
|
+
this.isSupported = typeof document !== "undefined";
|
|
13202
|
+
}
|
|
13203
|
+
async pickFiles(options) {
|
|
13204
|
+
if (!this.isSupported) return null;
|
|
13205
|
+
if (options?.directory) {
|
|
13206
|
+
const dir = await this.pickDirectory();
|
|
13207
|
+
return dir ? [dir] : null;
|
|
13208
|
+
}
|
|
13209
|
+
return new Promise((resolve) => {
|
|
13210
|
+
const input = document.createElement("input");
|
|
13211
|
+
input.type = "file";
|
|
13212
|
+
input.style.display = "none";
|
|
13213
|
+
if (options?.accept) input.accept = options.accept;
|
|
13214
|
+
if (options?.multiple) input.multiple = true;
|
|
13215
|
+
input.addEventListener("change", async () => {
|
|
13216
|
+
const files = input.files;
|
|
13217
|
+
if (!files || files.length === 0) {
|
|
13218
|
+
resolve(null);
|
|
13219
|
+
input.remove();
|
|
13220
|
+
return;
|
|
13221
|
+
}
|
|
13222
|
+
const readAs = options?.readAs ?? "text";
|
|
13223
|
+
const results = [];
|
|
13224
|
+
for (let i = 0; i < files.length; i++) {
|
|
13225
|
+
const file = files[i];
|
|
13226
|
+
const content = await this.readFile(file, readAs);
|
|
13227
|
+
results.push({
|
|
13228
|
+
name: file.name,
|
|
13229
|
+
content,
|
|
13230
|
+
size: file.size,
|
|
13231
|
+
type: file.type || "application/octet-stream"
|
|
13232
|
+
});
|
|
13233
|
+
}
|
|
13234
|
+
resolve(results);
|
|
13235
|
+
input.remove();
|
|
13236
|
+
});
|
|
13237
|
+
input.addEventListener("cancel", () => {
|
|
13238
|
+
resolve(null);
|
|
13239
|
+
input.remove();
|
|
11369
13240
|
});
|
|
13241
|
+
document.body.appendChild(input);
|
|
13242
|
+
input.click();
|
|
11370
13243
|
});
|
|
11371
13244
|
}
|
|
11372
|
-
|
|
11373
|
-
if (!
|
|
11374
|
-
return Promise.reject(
|
|
11375
|
-
new Error("readMultiSelect requires at least one option")
|
|
11376
|
-
);
|
|
11377
|
-
}
|
|
13245
|
+
async pickDirectory() {
|
|
13246
|
+
if (!this.isSupported) return null;
|
|
11378
13247
|
return new Promise((resolve) => {
|
|
11379
|
-
|
|
11380
|
-
|
|
11381
|
-
|
|
11382
|
-
|
|
11383
|
-
|
|
11384
|
-
|
|
11385
|
-
|
|
13248
|
+
const input = document.createElement("input");
|
|
13249
|
+
input.type = "file";
|
|
13250
|
+
input.style.display = "none";
|
|
13251
|
+
input.webkitdirectory = true;
|
|
13252
|
+
input.addEventListener("change", () => {
|
|
13253
|
+
const files = input.files;
|
|
13254
|
+
if (!files || files.length === 0) {
|
|
13255
|
+
resolve(null);
|
|
13256
|
+
} else {
|
|
13257
|
+
const pathParts = files[0].webkitRelativePath?.split("/") ?? [];
|
|
13258
|
+
resolve({
|
|
13259
|
+
name: pathParts[0] || "selected-directory",
|
|
13260
|
+
content: "",
|
|
13261
|
+
size: 0,
|
|
13262
|
+
type: "inode/directory"
|
|
13263
|
+
});
|
|
11386
13264
|
}
|
|
13265
|
+
input.remove();
|
|
11387
13266
|
});
|
|
11388
|
-
|
|
11389
|
-
|
|
11390
|
-
|
|
11391
|
-
const displayLines = this.renderMultiSelectOptions(options, 0, checkedIndices, 0, maxVisible);
|
|
11392
|
-
this.host.setActiveInputRequest({
|
|
11393
|
-
type: "multi-select",
|
|
11394
|
-
promptText: prompt,
|
|
11395
|
-
resolve,
|
|
11396
|
-
buffer: "",
|
|
11397
|
-
cursorPosition: 0,
|
|
11398
|
-
options,
|
|
11399
|
-
selectedIndex: 0,
|
|
11400
|
-
checkedIndices,
|
|
11401
|
-
scrollOffset: 0,
|
|
11402
|
-
maxVisible,
|
|
11403
|
-
displayLines
|
|
13267
|
+
input.addEventListener("cancel", () => {
|
|
13268
|
+
resolve(null);
|
|
13269
|
+
input.remove();
|
|
11404
13270
|
});
|
|
13271
|
+
document.body.appendChild(input);
|
|
13272
|
+
input.click();
|
|
11405
13273
|
});
|
|
11406
13274
|
}
|
|
11407
|
-
|
|
11408
|
-
return new Promise((resolve) => {
|
|
11409
|
-
|
|
11410
|
-
|
|
11411
|
-
|
|
11412
|
-
|
|
11413
|
-
|
|
11414
|
-
if (options?.min !== void 0 || options?.max !== void 0) {
|
|
11415
|
-
const minStr = options?.min !== void 0 ? String(options.min) : "";
|
|
11416
|
-
const maxStr = options?.max !== void 0 ? String(options.max) : "";
|
|
11417
|
-
if (minStr && maxStr) {
|
|
11418
|
-
hints.push(`${minStr}-${maxStr}`);
|
|
11419
|
-
} else if (minStr) {
|
|
11420
|
-
hints.push(`>=${minStr}`);
|
|
11421
|
-
} else if (maxStr) {
|
|
11422
|
-
hints.push(`<=${maxStr}`);
|
|
11423
|
-
}
|
|
11424
|
-
}
|
|
11425
|
-
if (options?.default !== void 0) {
|
|
11426
|
-
hints.push(`default: ${options.default}`);
|
|
11427
|
-
}
|
|
11428
|
-
if (hints.length > 0) {
|
|
11429
|
-
displayPrompt += ` (${hints.join(", ")}): `;
|
|
13275
|
+
readFile(file, readAs) {
|
|
13276
|
+
return new Promise((resolve, reject) => {
|
|
13277
|
+
const reader = new FileReader();
|
|
13278
|
+
reader.onload = () => resolve(reader.result);
|
|
13279
|
+
reader.onerror = () => reject(reader.error);
|
|
13280
|
+
if (readAs === "arraybuffer") {
|
|
13281
|
+
reader.readAsArrayBuffer(file);
|
|
11430
13282
|
} else {
|
|
11431
|
-
|
|
13283
|
+
reader.readAsText(file);
|
|
11432
13284
|
}
|
|
11433
|
-
this.host.writeToTerminal(displayPrompt);
|
|
11434
|
-
this.host.setActiveInputRequest({
|
|
11435
|
-
type: "number",
|
|
11436
|
-
promptText: displayPrompt,
|
|
11437
|
-
resolve,
|
|
11438
|
-
buffer: "",
|
|
11439
|
-
cursorPosition: 0,
|
|
11440
|
-
numberOptions: options
|
|
11441
|
-
});
|
|
11442
13285
|
});
|
|
11443
13286
|
}
|
|
11444
|
-
|
|
11445
|
-
|
|
11446
|
-
|
|
11447
|
-
|
|
11448
|
-
|
|
11449
|
-
|
|
11450
|
-
if (needsScroll) {
|
|
11451
|
-
const upLabel = scrollOffset > 0 ? "\u2191 more" : "";
|
|
11452
|
-
this.host.writeToTerminal(` \x1B[2m${upLabel}\x1B[0m\r
|
|
11453
|
-
`);
|
|
11454
|
-
lines++;
|
|
11455
|
-
}
|
|
11456
|
-
for (let i = scrollOffset; i < end; i++) {
|
|
11457
|
-
const prefix = i === selectedIndex ? " \x1B[36m> " : " ";
|
|
11458
|
-
const suffix = i === selectedIndex ? "\x1B[0m" : "";
|
|
11459
|
-
this.host.writeToTerminal(
|
|
11460
|
-
`${prefix}${options[i].label}${suffix}\r
|
|
11461
|
-
`
|
|
11462
|
-
);
|
|
11463
|
-
lines++;
|
|
11464
|
-
}
|
|
11465
|
-
if (needsScroll) {
|
|
11466
|
-
const downLabel = end < options.length ? "\u2193 more" : "";
|
|
11467
|
-
this.host.writeToTerminal(` \x1B[2m${downLabel}\x1B[0m\r
|
|
11468
|
-
`);
|
|
11469
|
-
lines++;
|
|
11470
|
-
}
|
|
11471
|
-
return lines;
|
|
11472
|
-
}
|
|
11473
|
-
renderInlineSelectOptions(options, selectedIndex) {
|
|
11474
|
-
return options.map((opt, i) => {
|
|
11475
|
-
if (i === selectedIndex) {
|
|
11476
|
-
return `\x1B[36m[ ${opt.label} ]\x1B[0m`;
|
|
11477
|
-
}
|
|
11478
|
-
return ` ${opt.label} `;
|
|
11479
|
-
}).join("");
|
|
11480
|
-
}
|
|
11481
|
-
renderMultiSelectOptions(options, selectedIndex, checkedIndices, scrollOffset = 0, maxVisible = options.length) {
|
|
11482
|
-
const needsScroll = options.length > maxVisible;
|
|
11483
|
-
const itemSlots = needsScroll ? maxVisible - 2 : options.length;
|
|
11484
|
-
const visibleCount = Math.max(1, Math.min(itemSlots, options.length));
|
|
11485
|
-
const end = Math.min(scrollOffset + visibleCount, options.length);
|
|
11486
|
-
let lines = 0;
|
|
11487
|
-
if (needsScroll) {
|
|
11488
|
-
const upLabel = scrollOffset > 0 ? "\u2191 more" : "";
|
|
11489
|
-
this.host.writeToTerminal(` \x1B[2m${upLabel}\x1B[0m\r
|
|
11490
|
-
`);
|
|
11491
|
-
lines++;
|
|
11492
|
-
}
|
|
11493
|
-
for (let i = scrollOffset; i < end; i++) {
|
|
11494
|
-
const checkbox = checkedIndices.has(i) ? "[x]" : "[ ]";
|
|
11495
|
-
const prefix = i === selectedIndex ? ` \x1B[36m> ${checkbox} ` : ` ${checkbox} `;
|
|
11496
|
-
const suffix = i === selectedIndex ? "\x1B[0m" : "";
|
|
11497
|
-
this.host.writeToTerminal(
|
|
11498
|
-
`${prefix}${options[i].label}${suffix}\r
|
|
11499
|
-
`
|
|
11500
|
-
);
|
|
11501
|
-
lines++;
|
|
11502
|
-
}
|
|
11503
|
-
if (needsScroll) {
|
|
11504
|
-
const downLabel = end < options.length ? "\u2193 more" : "";
|
|
11505
|
-
this.host.writeToTerminal(` \x1B[2m${downLabel}\x1B[0m\r
|
|
11506
|
-
`);
|
|
11507
|
-
lines++;
|
|
11508
|
-
}
|
|
11509
|
-
return lines;
|
|
13287
|
+
};
|
|
13288
|
+
|
|
13289
|
+
// src/lib/services/file-picker/noop-file-picker-provider.ts
|
|
13290
|
+
var NoopFilePickerProvider = class {
|
|
13291
|
+
constructor() {
|
|
13292
|
+
this.isSupported = false;
|
|
11510
13293
|
}
|
|
11511
|
-
|
|
11512
|
-
|
|
11513
|
-
|
|
11514
|
-
|
|
11515
|
-
|
|
11516
|
-
|
|
11517
|
-
this.host.setActiveInputRequest({
|
|
11518
|
-
type,
|
|
11519
|
-
promptText: prompt,
|
|
11520
|
-
resolve,
|
|
11521
|
-
buffer: "",
|
|
11522
|
-
cursorPosition: 0
|
|
11523
|
-
});
|
|
11524
|
-
});
|
|
13294
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
13295
|
+
async pickFiles(_options) {
|
|
13296
|
+
return null;
|
|
13297
|
+
}
|
|
13298
|
+
async pickDirectory() {
|
|
13299
|
+
return null;
|
|
11525
13300
|
}
|
|
11526
13301
|
};
|
|
11527
13302
|
|
|
@@ -12046,400 +13821,6 @@ var CommandLineMode = class {
|
|
|
12046
13821
|
}
|
|
12047
13822
|
};
|
|
12048
13823
|
|
|
12049
|
-
// src/lib/input/reader-mode.ts
|
|
12050
|
-
var ReaderMode = class {
|
|
12051
|
-
constructor(host) {
|
|
12052
|
-
this.host = host;
|
|
12053
|
-
}
|
|
12054
|
-
async handleInput(data) {
|
|
12055
|
-
const request = this.host.getActiveInputRequest();
|
|
12056
|
-
if (!request) {
|
|
12057
|
-
return;
|
|
12058
|
-
}
|
|
12059
|
-
switch (request.type) {
|
|
12060
|
-
case "line":
|
|
12061
|
-
this.handleLineInput(request, data);
|
|
12062
|
-
break;
|
|
12063
|
-
case "password":
|
|
12064
|
-
this.handlePasswordInput(request, data);
|
|
12065
|
-
break;
|
|
12066
|
-
case "confirm":
|
|
12067
|
-
this.handleConfirmInput(request, data);
|
|
12068
|
-
break;
|
|
12069
|
-
case "select":
|
|
12070
|
-
this.handleSelectInput(request, data);
|
|
12071
|
-
break;
|
|
12072
|
-
case "select-inline":
|
|
12073
|
-
this.handleSelectInlineInput(request, data);
|
|
12074
|
-
break;
|
|
12075
|
-
case "multi-select":
|
|
12076
|
-
this.handleMultiSelectInput(request, data);
|
|
12077
|
-
break;
|
|
12078
|
-
case "number":
|
|
12079
|
-
this.handleNumberInput(request, data);
|
|
12080
|
-
break;
|
|
12081
|
-
}
|
|
12082
|
-
}
|
|
12083
|
-
handleKeyEvent(event) {
|
|
12084
|
-
if (event.code === "KeyC" && event.ctrlKey) {
|
|
12085
|
-
const request = this.host.getActiveInputRequest();
|
|
12086
|
-
if (request) {
|
|
12087
|
-
request.resolve(null);
|
|
12088
|
-
this.host.setActiveInputRequest(null);
|
|
12089
|
-
this.host.terminal.writeln("");
|
|
12090
|
-
this.host.popMode();
|
|
12091
|
-
}
|
|
12092
|
-
return false;
|
|
12093
|
-
}
|
|
12094
|
-
if (event.code === "Escape") {
|
|
12095
|
-
const request = this.host.getActiveInputRequest();
|
|
12096
|
-
if (request) {
|
|
12097
|
-
request.resolve(null);
|
|
12098
|
-
this.host.setActiveInputRequest(null);
|
|
12099
|
-
this.host.terminal.writeln("");
|
|
12100
|
-
this.host.popMode();
|
|
12101
|
-
}
|
|
12102
|
-
return false;
|
|
12103
|
-
}
|
|
12104
|
-
return true;
|
|
12105
|
-
}
|
|
12106
|
-
handleLineInput(request, data) {
|
|
12107
|
-
if (data === "\r") {
|
|
12108
|
-
this.host.terminal.write("\r\n");
|
|
12109
|
-
const value = request.buffer;
|
|
12110
|
-
this.host.setActiveInputRequest(null);
|
|
12111
|
-
this.host.popMode();
|
|
12112
|
-
request.resolve(value);
|
|
12113
|
-
} else if (data === "\x7F") {
|
|
12114
|
-
if (request.cursorPosition > 0) {
|
|
12115
|
-
request.buffer = request.buffer.slice(0, request.cursorPosition - 1) + request.buffer.slice(request.cursorPosition);
|
|
12116
|
-
request.cursorPosition--;
|
|
12117
|
-
this.redrawReaderLine(request, request.buffer);
|
|
12118
|
-
}
|
|
12119
|
-
} else if (data === "\x1B[D") {
|
|
12120
|
-
if (request.cursorPosition > 0) {
|
|
12121
|
-
request.cursorPosition--;
|
|
12122
|
-
this.host.terminal.write(data);
|
|
12123
|
-
}
|
|
12124
|
-
} else if (data === "\x1B[C") {
|
|
12125
|
-
if (request.cursorPosition < request.buffer.length) {
|
|
12126
|
-
request.cursorPosition++;
|
|
12127
|
-
this.host.terminal.write(data);
|
|
12128
|
-
}
|
|
12129
|
-
} else if (data.startsWith("\x1B")) ; else {
|
|
12130
|
-
const text = data.replace(/[\r\n]+/g, "");
|
|
12131
|
-
request.buffer = request.buffer.slice(0, request.cursorPosition) + text + request.buffer.slice(request.cursorPosition);
|
|
12132
|
-
request.cursorPosition += text.length;
|
|
12133
|
-
this.redrawReaderLine(request, request.buffer);
|
|
12134
|
-
}
|
|
12135
|
-
}
|
|
12136
|
-
handlePasswordInput(request, data) {
|
|
12137
|
-
if (data === "\r") {
|
|
12138
|
-
this.host.terminal.write("\r\n");
|
|
12139
|
-
const value = request.buffer;
|
|
12140
|
-
this.host.setActiveInputRequest(null);
|
|
12141
|
-
this.host.popMode();
|
|
12142
|
-
request.resolve(value);
|
|
12143
|
-
} else if (data === "\x7F") {
|
|
12144
|
-
if (request.cursorPosition > 0) {
|
|
12145
|
-
request.buffer = request.buffer.slice(0, request.cursorPosition - 1) + request.buffer.slice(request.cursorPosition);
|
|
12146
|
-
request.cursorPosition--;
|
|
12147
|
-
this.redrawReaderLine(
|
|
12148
|
-
request,
|
|
12149
|
-
"*".repeat(request.buffer.length)
|
|
12150
|
-
);
|
|
12151
|
-
}
|
|
12152
|
-
} else if (data.startsWith("\x1B")) ; else {
|
|
12153
|
-
const text = data.replace(/[\r\n]+/g, "");
|
|
12154
|
-
request.buffer = request.buffer.slice(0, request.cursorPosition) + text + request.buffer.slice(request.cursorPosition);
|
|
12155
|
-
request.cursorPosition += text.length;
|
|
12156
|
-
this.redrawReaderLine(request, "*".repeat(request.buffer.length));
|
|
12157
|
-
}
|
|
12158
|
-
}
|
|
12159
|
-
handleConfirmInput(request, data) {
|
|
12160
|
-
if (data === "\r") {
|
|
12161
|
-
this.host.terminal.write("\r\n");
|
|
12162
|
-
this.host.setActiveInputRequest(null);
|
|
12163
|
-
this.host.popMode();
|
|
12164
|
-
const buf = request.buffer.toLowerCase();
|
|
12165
|
-
if (buf === "y") {
|
|
12166
|
-
request.resolve(true);
|
|
12167
|
-
} else if (buf === "n") {
|
|
12168
|
-
request.resolve(false);
|
|
12169
|
-
} else {
|
|
12170
|
-
request.resolve(request.defaultValue ?? false);
|
|
12171
|
-
}
|
|
12172
|
-
} else if (data === "\x7F") {
|
|
12173
|
-
if (request.cursorPosition > 0) {
|
|
12174
|
-
request.buffer = request.buffer.slice(0, -1);
|
|
12175
|
-
request.cursorPosition--;
|
|
12176
|
-
this.redrawReaderLine(request, request.buffer);
|
|
12177
|
-
}
|
|
12178
|
-
} else if (data.startsWith("\x1B")) ; else {
|
|
12179
|
-
const char = data.toLowerCase();
|
|
12180
|
-
if (char === "y" || char === "n") {
|
|
12181
|
-
request.buffer = data;
|
|
12182
|
-
request.cursorPosition = 1;
|
|
12183
|
-
this.redrawReaderLine(request, request.buffer);
|
|
12184
|
-
}
|
|
12185
|
-
}
|
|
12186
|
-
}
|
|
12187
|
-
handleSelectInput(request, data) {
|
|
12188
|
-
const options = request.options;
|
|
12189
|
-
const selectedIndex = request.selectedIndex;
|
|
12190
|
-
if (data === "\r") {
|
|
12191
|
-
this.host.terminal.write("\r\n");
|
|
12192
|
-
this.host.setActiveInputRequest(null);
|
|
12193
|
-
this.host.popMode();
|
|
12194
|
-
request.resolve(options[selectedIndex].value);
|
|
12195
|
-
} else if (data === "\x1B[A") {
|
|
12196
|
-
if (selectedIndex > 0) {
|
|
12197
|
-
request.selectedIndex = selectedIndex - 1;
|
|
12198
|
-
this.adjustScrollOffset(request);
|
|
12199
|
-
this.redrawSelectOptions(request);
|
|
12200
|
-
request.onChange?.(options[request.selectedIndex].value);
|
|
12201
|
-
}
|
|
12202
|
-
} else if (data === "\x1B[B") {
|
|
12203
|
-
if (selectedIndex < options.length - 1) {
|
|
12204
|
-
request.selectedIndex = selectedIndex + 1;
|
|
12205
|
-
this.adjustScrollOffset(request);
|
|
12206
|
-
this.redrawSelectOptions(request);
|
|
12207
|
-
request.onChange?.(options[request.selectedIndex].value);
|
|
12208
|
-
}
|
|
12209
|
-
}
|
|
12210
|
-
}
|
|
12211
|
-
adjustScrollOffset(request) {
|
|
12212
|
-
const options = request.options;
|
|
12213
|
-
const maxVisible = request.maxVisible ?? options.length;
|
|
12214
|
-
const needsScroll = options.length > maxVisible;
|
|
12215
|
-
const itemSlots = needsScroll ? maxVisible - 2 : options.length;
|
|
12216
|
-
const visibleCount = Math.max(1, Math.min(itemSlots, options.length));
|
|
12217
|
-
const scrollOffset = request.scrollOffset ?? 0;
|
|
12218
|
-
const selectedIndex = request.selectedIndex;
|
|
12219
|
-
if (selectedIndex < scrollOffset) {
|
|
12220
|
-
request.scrollOffset = selectedIndex;
|
|
12221
|
-
} else if (selectedIndex >= scrollOffset + visibleCount) {
|
|
12222
|
-
request.scrollOffset = selectedIndex - visibleCount + 1;
|
|
12223
|
-
}
|
|
12224
|
-
}
|
|
12225
|
-
redrawReaderLine(request, displayText) {
|
|
12226
|
-
this.host.terminal.write("\x1B[2K\r");
|
|
12227
|
-
this.host.terminal.write(request.promptText + displayText);
|
|
12228
|
-
const cursorOffset = request.buffer.length - request.cursorPosition;
|
|
12229
|
-
if (cursorOffset > 0) {
|
|
12230
|
-
this.host.terminal.write(`\x1B[${cursorOffset}D`);
|
|
12231
|
-
}
|
|
12232
|
-
}
|
|
12233
|
-
redrawSelectOptions(request) {
|
|
12234
|
-
const options = request.options;
|
|
12235
|
-
const selectedIndex = request.selectedIndex;
|
|
12236
|
-
const maxVisible = request.maxVisible ?? options.length;
|
|
12237
|
-
const scrollOffset = request.scrollOffset ?? 0;
|
|
12238
|
-
const displayLines = request.displayLines ?? options.length;
|
|
12239
|
-
const needsScroll = options.length > maxVisible;
|
|
12240
|
-
const itemSlots = needsScroll ? maxVisible - 2 : options.length;
|
|
12241
|
-
const visibleCount = Math.max(1, Math.min(itemSlots, options.length));
|
|
12242
|
-
const end = Math.min(scrollOffset + visibleCount, options.length);
|
|
12243
|
-
if (displayLines > 0) {
|
|
12244
|
-
this.host.terminal.write(`\x1B[${displayLines}A`);
|
|
12245
|
-
}
|
|
12246
|
-
if (needsScroll) {
|
|
12247
|
-
this.host.terminal.write("\x1B[2K\r");
|
|
12248
|
-
const upLabel = scrollOffset > 0 ? "\u2191 more" : "";
|
|
12249
|
-
this.host.terminal.write(` \x1B[2m${upLabel}\x1B[0m\r
|
|
12250
|
-
`);
|
|
12251
|
-
}
|
|
12252
|
-
for (let i = scrollOffset; i < end; i++) {
|
|
12253
|
-
this.host.terminal.write("\x1B[2K\r");
|
|
12254
|
-
const prefix = i === selectedIndex ? " \x1B[36m> " : " ";
|
|
12255
|
-
const suffix = i === selectedIndex ? "\x1B[0m" : "";
|
|
12256
|
-
this.host.terminal.write(
|
|
12257
|
-
`${prefix}${options[i].label}${suffix}\r
|
|
12258
|
-
`
|
|
12259
|
-
);
|
|
12260
|
-
}
|
|
12261
|
-
if (needsScroll) {
|
|
12262
|
-
this.host.terminal.write("\x1B[2K\r");
|
|
12263
|
-
const downLabel = end < options.length ? "\u2193 more" : "";
|
|
12264
|
-
this.host.terminal.write(` \x1B[2m${downLabel}\x1B[0m\r
|
|
12265
|
-
`);
|
|
12266
|
-
}
|
|
12267
|
-
}
|
|
12268
|
-
handleSelectInlineInput(request, data) {
|
|
12269
|
-
const options = request.options;
|
|
12270
|
-
const selectedIndex = request.selectedIndex;
|
|
12271
|
-
if (data === "\r") {
|
|
12272
|
-
this.host.terminal.write("\r\n");
|
|
12273
|
-
this.host.setActiveInputRequest(null);
|
|
12274
|
-
this.host.popMode();
|
|
12275
|
-
request.resolve(options[selectedIndex].value);
|
|
12276
|
-
} else if (data === "\x1B[D") {
|
|
12277
|
-
if (selectedIndex > 0) {
|
|
12278
|
-
request.selectedIndex = selectedIndex - 1;
|
|
12279
|
-
this.redrawInlineSelectOptions(request);
|
|
12280
|
-
request.onChange?.(options[request.selectedIndex].value);
|
|
12281
|
-
}
|
|
12282
|
-
} else if (data === "\x1B[C") {
|
|
12283
|
-
if (selectedIndex < options.length - 1) {
|
|
12284
|
-
request.selectedIndex = selectedIndex + 1;
|
|
12285
|
-
this.redrawInlineSelectOptions(request);
|
|
12286
|
-
request.onChange?.(options[request.selectedIndex].value);
|
|
12287
|
-
}
|
|
12288
|
-
}
|
|
12289
|
-
}
|
|
12290
|
-
redrawInlineSelectOptions(request) {
|
|
12291
|
-
const options = request.options;
|
|
12292
|
-
const selectedIndex = request.selectedIndex;
|
|
12293
|
-
this.host.terminal.write("\x1B[2K\r");
|
|
12294
|
-
const inlineText = options.map((opt, i) => {
|
|
12295
|
-
if (i === selectedIndex) {
|
|
12296
|
-
return `\x1B[36m[ ${opt.label} ]\x1B[0m`;
|
|
12297
|
-
}
|
|
12298
|
-
return ` ${opt.label} `;
|
|
12299
|
-
}).join("");
|
|
12300
|
-
this.host.terminal.write(`${request.promptText} ${inlineText}`);
|
|
12301
|
-
}
|
|
12302
|
-
handleMultiSelectInput(request, data) {
|
|
12303
|
-
const options = request.options;
|
|
12304
|
-
const selectedIndex = request.selectedIndex;
|
|
12305
|
-
const checkedIndices = request.checkedIndices;
|
|
12306
|
-
if (data === "\r") {
|
|
12307
|
-
this.host.terminal.write("\r\n");
|
|
12308
|
-
this.host.setActiveInputRequest(null);
|
|
12309
|
-
this.host.popMode();
|
|
12310
|
-
const selected = options.filter((_, i) => checkedIndices.has(i)).map((opt) => opt.value);
|
|
12311
|
-
request.resolve(selected);
|
|
12312
|
-
} else if (data === " ") {
|
|
12313
|
-
if (checkedIndices.has(selectedIndex)) {
|
|
12314
|
-
checkedIndices.delete(selectedIndex);
|
|
12315
|
-
} else {
|
|
12316
|
-
checkedIndices.add(selectedIndex);
|
|
12317
|
-
}
|
|
12318
|
-
this.redrawMultiSelectOptions(request);
|
|
12319
|
-
} else if (data === "\x1B[A") {
|
|
12320
|
-
if (selectedIndex > 0) {
|
|
12321
|
-
request.selectedIndex = selectedIndex - 1;
|
|
12322
|
-
this.adjustScrollOffset(request);
|
|
12323
|
-
this.redrawMultiSelectOptions(request);
|
|
12324
|
-
}
|
|
12325
|
-
} else if (data === "\x1B[B") {
|
|
12326
|
-
if (selectedIndex < options.length - 1) {
|
|
12327
|
-
request.selectedIndex = selectedIndex + 1;
|
|
12328
|
-
this.adjustScrollOffset(request);
|
|
12329
|
-
this.redrawMultiSelectOptions(request);
|
|
12330
|
-
}
|
|
12331
|
-
}
|
|
12332
|
-
}
|
|
12333
|
-
redrawMultiSelectOptions(request) {
|
|
12334
|
-
const options = request.options;
|
|
12335
|
-
const selectedIndex = request.selectedIndex;
|
|
12336
|
-
const checkedIndices = request.checkedIndices;
|
|
12337
|
-
const maxVisible = request.maxVisible ?? options.length;
|
|
12338
|
-
const scrollOffset = request.scrollOffset ?? 0;
|
|
12339
|
-
const displayLines = request.displayLines ?? options.length;
|
|
12340
|
-
const needsScroll = options.length > maxVisible;
|
|
12341
|
-
const itemSlots = needsScroll ? maxVisible - 2 : options.length;
|
|
12342
|
-
const visibleCount = Math.max(1, Math.min(itemSlots, options.length));
|
|
12343
|
-
const end = Math.min(scrollOffset + visibleCount, options.length);
|
|
12344
|
-
if (displayLines > 0) {
|
|
12345
|
-
this.host.terminal.write(`\x1B[${displayLines}A`);
|
|
12346
|
-
}
|
|
12347
|
-
if (needsScroll) {
|
|
12348
|
-
this.host.terminal.write("\x1B[2K\r");
|
|
12349
|
-
const upLabel = scrollOffset > 0 ? "\u2191 more" : "";
|
|
12350
|
-
this.host.terminal.write(` \x1B[2m${upLabel}\x1B[0m\r
|
|
12351
|
-
`);
|
|
12352
|
-
}
|
|
12353
|
-
for (let i = scrollOffset; i < end; i++) {
|
|
12354
|
-
this.host.terminal.write("\x1B[2K\r");
|
|
12355
|
-
const checkbox = checkedIndices.has(i) ? "[x]" : "[ ]";
|
|
12356
|
-
const prefix = i === selectedIndex ? ` \x1B[36m> ${checkbox} ` : ` ${checkbox} `;
|
|
12357
|
-
const suffix = i === selectedIndex ? "\x1B[0m" : "";
|
|
12358
|
-
this.host.terminal.write(
|
|
12359
|
-
`${prefix}${options[i].label}${suffix}\r
|
|
12360
|
-
`
|
|
12361
|
-
);
|
|
12362
|
-
}
|
|
12363
|
-
if (needsScroll) {
|
|
12364
|
-
this.host.terminal.write("\x1B[2K\r");
|
|
12365
|
-
const downLabel = end < options.length ? "\u2193 more" : "";
|
|
12366
|
-
this.host.terminal.write(` \x1B[2m${downLabel}\x1B[0m\r
|
|
12367
|
-
`);
|
|
12368
|
-
}
|
|
12369
|
-
}
|
|
12370
|
-
handleNumberInput(request, data) {
|
|
12371
|
-
if (data === "\r") {
|
|
12372
|
-
const numOptions = request.numberOptions;
|
|
12373
|
-
if (request.buffer === "") {
|
|
12374
|
-
if (numOptions?.default !== void 0) {
|
|
12375
|
-
this.host.terminal.write("\r\n");
|
|
12376
|
-
this.host.setActiveInputRequest(null);
|
|
12377
|
-
this.host.popMode();
|
|
12378
|
-
request.resolve(numOptions.default);
|
|
12379
|
-
} else {
|
|
12380
|
-
this.host.terminal.write("\r\n");
|
|
12381
|
-
this.host.terminal.write(
|
|
12382
|
-
"\x1B[31mPlease enter a number.\x1B[0m\r\n"
|
|
12383
|
-
);
|
|
12384
|
-
this.host.terminal.write(request.promptText);
|
|
12385
|
-
}
|
|
12386
|
-
return;
|
|
12387
|
-
}
|
|
12388
|
-
const value = Number(request.buffer);
|
|
12389
|
-
if (isNaN(value)) {
|
|
12390
|
-
this.host.terminal.write("\r\n");
|
|
12391
|
-
this.host.terminal.write("\x1B[31mInvalid number.\x1B[0m\r\n");
|
|
12392
|
-
request.buffer = "";
|
|
12393
|
-
request.cursorPosition = 0;
|
|
12394
|
-
this.host.terminal.write(request.promptText);
|
|
12395
|
-
return;
|
|
12396
|
-
}
|
|
12397
|
-
if (numOptions?.min !== void 0 && value < numOptions.min) {
|
|
12398
|
-
this.host.terminal.write("\r\n");
|
|
12399
|
-
this.host.terminal.write(
|
|
12400
|
-
`\x1B[31mValue must be at least ${numOptions.min}.\x1B[0m\r
|
|
12401
|
-
`
|
|
12402
|
-
);
|
|
12403
|
-
request.buffer = "";
|
|
12404
|
-
request.cursorPosition = 0;
|
|
12405
|
-
this.host.terminal.write(request.promptText);
|
|
12406
|
-
return;
|
|
12407
|
-
}
|
|
12408
|
-
if (numOptions?.max !== void 0 && value > numOptions.max) {
|
|
12409
|
-
this.host.terminal.write("\r\n");
|
|
12410
|
-
this.host.terminal.write(
|
|
12411
|
-
`\x1B[31mValue must be at most ${numOptions.max}.\x1B[0m\r
|
|
12412
|
-
`
|
|
12413
|
-
);
|
|
12414
|
-
request.buffer = "";
|
|
12415
|
-
request.cursorPosition = 0;
|
|
12416
|
-
this.host.terminal.write(request.promptText);
|
|
12417
|
-
return;
|
|
12418
|
-
}
|
|
12419
|
-
this.host.terminal.write("\r\n");
|
|
12420
|
-
this.host.setActiveInputRequest(null);
|
|
12421
|
-
this.host.popMode();
|
|
12422
|
-
request.resolve(value);
|
|
12423
|
-
} else if (data === "\x7F") {
|
|
12424
|
-
if (request.cursorPosition > 0) {
|
|
12425
|
-
request.buffer = request.buffer.slice(0, request.cursorPosition - 1) + request.buffer.slice(request.cursorPosition);
|
|
12426
|
-
request.cursorPosition--;
|
|
12427
|
-
this.redrawReaderLine(request, request.buffer);
|
|
12428
|
-
}
|
|
12429
|
-
} else if (data === "-") {
|
|
12430
|
-
if (request.cursorPosition === 0 && !request.buffer.includes("-")) {
|
|
12431
|
-
request.buffer = "-" + request.buffer;
|
|
12432
|
-
request.cursorPosition = 1;
|
|
12433
|
-
this.redrawReaderLine(request, request.buffer);
|
|
12434
|
-
}
|
|
12435
|
-
} else if (/^\d$/.test(data)) {
|
|
12436
|
-
request.buffer = request.buffer.slice(0, request.cursorPosition) + data + request.buffer.slice(request.cursorPosition);
|
|
12437
|
-
request.cursorPosition += 1;
|
|
12438
|
-
this.redrawReaderLine(request, request.buffer);
|
|
12439
|
-
}
|
|
12440
|
-
}
|
|
12441
|
-
};
|
|
12442
|
-
|
|
12443
13824
|
// src/lib/input/raw-mode.ts
|
|
12444
13825
|
var RawMode = class {
|
|
12445
13826
|
constructor(processor, context2, fullScreen = false) {
|
|
@@ -12532,7 +13913,6 @@ var CliExecutionContext = class {
|
|
|
12532
13913
|
this.completionEngine = new CliCompletionEngine();
|
|
12533
13914
|
this.promptLength = 0;
|
|
12534
13915
|
this.lineBuffer = new CliLineBuffer();
|
|
12535
|
-
this._activeInputRequest = null;
|
|
12536
13916
|
this.modeStack = [];
|
|
12537
13917
|
this.managedTimers = /* @__PURE__ */ new Set();
|
|
12538
13918
|
this.resizeDisposable = null;
|
|
@@ -12571,6 +13951,7 @@ var CliExecutionContext = class {
|
|
|
12571
13951
|
this.textAnimator = textAnimator;
|
|
12572
13952
|
this.clipboard = new CliClipboard(this);
|
|
12573
13953
|
this.process = new CliExecutionProcess(this);
|
|
13954
|
+
this.filePickerProvider = typeof document !== "undefined" ? new BrowserFilePickerProvider() : new NoopFilePickerProvider();
|
|
12574
13955
|
this.reader = new CliInputReader(this);
|
|
12575
13956
|
this.logger = deps.logger;
|
|
12576
13957
|
this.logger.setCliLogLevel(cliOptions?.logLevel || cliCore.CliLogLevel.ERROR);
|
|
@@ -12583,26 +13964,15 @@ var CliExecutionContext = class {
|
|
|
12583
13964
|
);
|
|
12584
13965
|
}
|
|
12585
13966
|
// -- Public API (ICliExecutionContext) --
|
|
12586
|
-
get activeInputRequest() {
|
|
12587
|
-
return this._activeInputRequest;
|
|
12588
|
-
}
|
|
12589
|
-
/**
|
|
12590
|
-
* Sets the active input request. When a non-null request is provided,
|
|
12591
|
-
* pushes ReaderMode onto the mode stack. ReaderMode pops itself on
|
|
12592
|
-
* completion, so passing null here does NOT pop the mode.
|
|
12593
|
-
*/
|
|
12594
|
-
setActiveInputRequest(request) {
|
|
12595
|
-
this._activeInputRequest = request;
|
|
12596
|
-
if (request !== null) {
|
|
12597
|
-
this.pushMode(new ReaderMode(this));
|
|
12598
|
-
}
|
|
12599
|
-
}
|
|
12600
13967
|
writeToTerminal(text) {
|
|
12601
13968
|
this.terminal.write(text);
|
|
12602
13969
|
}
|
|
12603
13970
|
getTerminalRows() {
|
|
12604
13971
|
return this.terminal.rows;
|
|
12605
13972
|
}
|
|
13973
|
+
getTerminalCols() {
|
|
13974
|
+
return this.terminal.cols;
|
|
13975
|
+
}
|
|
12606
13976
|
get currentLine() {
|
|
12607
13977
|
return this.lineBuffer.text;
|
|
12608
13978
|
}
|
|
@@ -12707,7 +14077,12 @@ var CliExecutionContext = class {
|
|
|
12707
14077
|
);
|
|
12708
14078
|
return;
|
|
12709
14079
|
}
|
|
12710
|
-
if (this.isProgressRunning() || this.isRawModeActive()
|
|
14080
|
+
if (this.isProgressRunning() || this.isRawModeActive()) {
|
|
14081
|
+
return;
|
|
14082
|
+
}
|
|
14083
|
+
const mode = this.currentMode;
|
|
14084
|
+
if (mode?.onResize) {
|
|
14085
|
+
mode.onResize(this.terminal.cols, this.terminal.rows);
|
|
12711
14086
|
return;
|
|
12712
14087
|
}
|
|
12713
14088
|
const buf = this.terminal.buffer.active;
|
|
@@ -12821,10 +14196,6 @@ var CliExecutionContext = class {
|
|
|
12821
14196
|
getExecutionContext() {
|
|
12822
14197
|
return this;
|
|
12823
14198
|
}
|
|
12824
|
-
// -- ReaderModeHost interface --
|
|
12825
|
-
getActiveInputRequest() {
|
|
12826
|
-
return this._activeInputRequest;
|
|
12827
|
-
}
|
|
12828
14199
|
// -- Managed timer cleanup --
|
|
12829
14200
|
clearAllManagedTimers() {
|
|
12830
14201
|
for (const timer of this.managedTimers) {
|
|
@@ -13267,6 +14638,7 @@ var CliCommandExecutor = class {
|
|
|
13267
14638
|
const aliasProcessor = this.registry.findProcessor("alias", []);
|
|
13268
14639
|
const aliases = aliasProcessor?.userAliases ?? {};
|
|
13269
14640
|
if (aliases[mainCommand]) {
|
|
14641
|
+
this.completeProcess(processEntry, context2, 0);
|
|
13270
14642
|
return await this.executeSingleCommand(
|
|
13271
14643
|
aliases[mainCommand],
|
|
13272
14644
|
data,
|
|
@@ -13279,6 +14651,7 @@ var CliCommandExecutor = class {
|
|
|
13279
14651
|
context2
|
|
13280
14652
|
);
|
|
13281
14653
|
if (executed) {
|
|
14654
|
+
this.completeProcess(processEntry, context2, 0);
|
|
13282
14655
|
process.end();
|
|
13283
14656
|
return;
|
|
13284
14657
|
}
|
|
@@ -13293,6 +14666,7 @@ var CliCommandExecutor = class {
|
|
|
13293
14666
|
context2.writer.writeInfo(
|
|
13294
14667
|
`\u{1F4E6} Use ${context2.writer.wrapInColor("pkg add <name>", cliCore.CliForegroundColor.Cyan)} to install additional commands`
|
|
13295
14668
|
);
|
|
14669
|
+
this.failProcess(processEntry, context2);
|
|
13296
14670
|
context2.process.exit(-1, {
|
|
13297
14671
|
silent: true
|
|
13298
14672
|
});
|
|
@@ -13311,6 +14685,7 @@ var CliCommandExecutor = class {
|
|
|
13311
14685
|
continue;
|
|
13312
14686
|
}
|
|
13313
14687
|
if (await handler.handle(args, processor, commandToProcess, context2)) {
|
|
14688
|
+
this.completeProcess(processEntry, context2, 0);
|
|
13314
14689
|
process.end();
|
|
13315
14690
|
return;
|
|
13316
14691
|
}
|
|
@@ -13379,26 +14754,18 @@ var CliCommandExecutor = class {
|
|
|
13379
14754
|
if (!process.outputCalled && capturingWriter.hasOutput()) {
|
|
13380
14755
|
process.data = capturingWriter.getCapturedData();
|
|
13381
14756
|
}
|
|
13382
|
-
|
|
13383
|
-
try {
|
|
13384
|
-
const procRegistry = context2.services.get(CliProcessRegistry_TOKEN);
|
|
13385
|
-
procRegistry.complete(processEntry.pid, context2.process.exitCode ?? 0);
|
|
13386
|
-
} catch {
|
|
13387
|
-
}
|
|
13388
|
-
}
|
|
14757
|
+
this.completeProcess(processEntry, context2, context2.process.exitCode ?? 0);
|
|
13389
14758
|
if (!process.exited) {
|
|
13390
14759
|
process.end();
|
|
13391
14760
|
}
|
|
13392
14761
|
} catch (e) {
|
|
13393
14762
|
context2.spinner?.hide();
|
|
13394
|
-
if (processEntry) {
|
|
13395
|
-
try {
|
|
13396
|
-
const procRegistry = context2.services.get(CliProcessRegistry_TOKEN);
|
|
13397
|
-
procRegistry.fail(processEntry.pid);
|
|
13398
|
-
} catch {
|
|
13399
|
-
}
|
|
13400
|
-
}
|
|
13401
14763
|
if (e instanceof ProcessExitedError) {
|
|
14764
|
+
if (e.code === 0) {
|
|
14765
|
+
this.completeProcess(processEntry, context2, 0);
|
|
14766
|
+
} else {
|
|
14767
|
+
this.failProcess(processEntry, context2);
|
|
14768
|
+
}
|
|
13402
14769
|
cancellable?.cancel();
|
|
13403
14770
|
context2.abort?.();
|
|
13404
14771
|
if (e.code !== 0) {
|
|
@@ -13411,6 +14778,7 @@ var CliCommandExecutor = class {
|
|
|
13411
14778
|
);
|
|
13412
14779
|
}
|
|
13413
14780
|
} else {
|
|
14781
|
+
this.failProcess(processEntry, context2);
|
|
13414
14782
|
context2.writer.writeError(`Error executing command: ${e}`);
|
|
13415
14783
|
context2.process.exit(-1);
|
|
13416
14784
|
}
|
|
@@ -13418,6 +14786,22 @@ var CliCommandExecutor = class {
|
|
|
13418
14786
|
abortSub.unsubscribe();
|
|
13419
14787
|
}
|
|
13420
14788
|
}
|
|
14789
|
+
completeProcess(processEntry, context2, exitCode) {
|
|
14790
|
+
if (!processEntry) return;
|
|
14791
|
+
try {
|
|
14792
|
+
const procRegistry = context2.services.get(CliProcessRegistry_TOKEN);
|
|
14793
|
+
procRegistry.complete(processEntry.pid, exitCode);
|
|
14794
|
+
} catch {
|
|
14795
|
+
}
|
|
14796
|
+
}
|
|
14797
|
+
failProcess(processEntry, context2) {
|
|
14798
|
+
if (!processEntry) return;
|
|
14799
|
+
try {
|
|
14800
|
+
const procRegistry = context2.services.get(CliProcessRegistry_TOKEN);
|
|
14801
|
+
procRegistry.fail(processEntry.pid);
|
|
14802
|
+
} catch {
|
|
14803
|
+
}
|
|
14804
|
+
}
|
|
13421
14805
|
async writeStderrToFile(filePath, context2) {
|
|
13422
14806
|
const stderr = this.lastCapturingWriter?.getCapturedStderr();
|
|
13423
14807
|
if (!stderr) return;
|
|
@@ -14498,6 +15882,167 @@ var CliStateStoreManager = class {
|
|
|
14498
15882
|
}
|
|
14499
15883
|
};
|
|
14500
15884
|
|
|
15885
|
+
// src/lib/editor/syntax/registry.ts
|
|
15886
|
+
var SyntaxHighlighterRegistry = class {
|
|
15887
|
+
constructor() {
|
|
15888
|
+
this.highlighters = /* @__PURE__ */ new Map();
|
|
15889
|
+
this.extensionMap = /* @__PURE__ */ new Map();
|
|
15890
|
+
}
|
|
15891
|
+
register(highlighter) {
|
|
15892
|
+
this.highlighters.set(highlighter.id, highlighter);
|
|
15893
|
+
for (const ext of highlighter.extensions) {
|
|
15894
|
+
this.extensionMap.set(ext, highlighter.id);
|
|
15895
|
+
}
|
|
15896
|
+
}
|
|
15897
|
+
unregister(id) {
|
|
15898
|
+
const h = this.highlighters.get(id);
|
|
15899
|
+
if (h) {
|
|
15900
|
+
for (const ext of h.extensions) {
|
|
15901
|
+
this.extensionMap.delete(ext);
|
|
15902
|
+
}
|
|
15903
|
+
this.highlighters.delete(id);
|
|
15904
|
+
}
|
|
15905
|
+
}
|
|
15906
|
+
getByExtension(ext) {
|
|
15907
|
+
const id = this.extensionMap.get(ext);
|
|
15908
|
+
return id ? this.highlighters.get(id) ?? null : null;
|
|
15909
|
+
}
|
|
15910
|
+
getRegisteredLanguages() {
|
|
15911
|
+
return Array.from(this.highlighters.keys());
|
|
15912
|
+
}
|
|
15913
|
+
};
|
|
15914
|
+
|
|
15915
|
+
// src/lib/editor/syntax/highlighters/json-highlighter.ts
|
|
15916
|
+
var JsonHighlighter = class {
|
|
15917
|
+
constructor() {
|
|
15918
|
+
this.id = "json";
|
|
15919
|
+
this.extensions = [".json", ".jsonc"];
|
|
15920
|
+
this.initialState = 0;
|
|
15921
|
+
}
|
|
15922
|
+
getRules(state) {
|
|
15923
|
+
if (state === 1) {
|
|
15924
|
+
return [
|
|
15925
|
+
{ pattern: /.*?\*\//, tokenType: "comment" },
|
|
15926
|
+
{ pattern: /.*/, tokenType: "comment" }
|
|
15927
|
+
];
|
|
15928
|
+
}
|
|
15929
|
+
return [
|
|
15930
|
+
{ pattern: /\/\/.*$/, tokenType: "comment" },
|
|
15931
|
+
{ pattern: /\/\*.*?\*\//, tokenType: "comment" },
|
|
15932
|
+
{ pattern: /\/\*.*$/, tokenType: "comment" },
|
|
15933
|
+
{ pattern: /"(?:[^"\\]|\\.)*"/, tokenType: "string" },
|
|
15934
|
+
{ pattern: /-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?/, tokenType: "number" },
|
|
15935
|
+
{ pattern: /\b(?:true|false|null)\b/, tokenType: "keyword" },
|
|
15936
|
+
{ pattern: /[{}\[\]:,]/, tokenType: "punctuation" }
|
|
15937
|
+
];
|
|
15938
|
+
}
|
|
15939
|
+
getNextLineState(line, currentState) {
|
|
15940
|
+
if (currentState === 1) {
|
|
15941
|
+
return line.includes("*/") ? 0 : 1;
|
|
15942
|
+
}
|
|
15943
|
+
const stripped = line.replace(/"(?:[^"\\]|\\.)*"/g, "").replace(/\/\/.*$/, "");
|
|
15944
|
+
const openIdx = stripped.lastIndexOf("/*");
|
|
15945
|
+
if (openIdx === -1) return 0;
|
|
15946
|
+
const closeIdx = stripped.indexOf("*/", openIdx);
|
|
15947
|
+
return closeIdx === -1 ? 1 : 0;
|
|
15948
|
+
}
|
|
15949
|
+
};
|
|
15950
|
+
|
|
15951
|
+
// src/lib/editor/syntax/highlighters/html-highlighter.ts
|
|
15952
|
+
var HtmlHighlighter = class {
|
|
15953
|
+
constructor() {
|
|
15954
|
+
this.id = "html";
|
|
15955
|
+
this.extensions = [".html", ".htm", ".xml", ".svg"];
|
|
15956
|
+
this.initialState = 0;
|
|
15957
|
+
}
|
|
15958
|
+
getRules(state) {
|
|
15959
|
+
if (state === 1) {
|
|
15960
|
+
return [
|
|
15961
|
+
{ pattern: /.*?-->/, tokenType: "comment" },
|
|
15962
|
+
{ pattern: /.*/, tokenType: "comment" }
|
|
15963
|
+
];
|
|
15964
|
+
}
|
|
15965
|
+
return [
|
|
15966
|
+
{ pattern: /<!--.*?-->/, tokenType: "comment" },
|
|
15967
|
+
{ pattern: /<!--.*$/, tokenType: "comment" },
|
|
15968
|
+
{ pattern: /(<\/?)([\w-]+)/, tokenType: "tag", captureGroup: 2 },
|
|
15969
|
+
{ pattern: /"[^"]*"/, tokenType: "value" },
|
|
15970
|
+
{ pattern: /'[^']*'/, tokenType: "value" },
|
|
15971
|
+
{ pattern: /([\w-]+)(\s*=)/, tokenType: "attribute", captureGroup: 1 },
|
|
15972
|
+
{ pattern: /\/?>/, tokenType: "punctuation" },
|
|
15973
|
+
{ pattern: /<\/?/, tokenType: "punctuation" },
|
|
15974
|
+
{ pattern: /=/, tokenType: "punctuation" }
|
|
15975
|
+
];
|
|
15976
|
+
}
|
|
15977
|
+
getNextLineState(line, currentState) {
|
|
15978
|
+
if (currentState === 1) {
|
|
15979
|
+
return line.includes("-->") ? 0 : 1;
|
|
15980
|
+
}
|
|
15981
|
+
const openIdx = line.lastIndexOf("<!--");
|
|
15982
|
+
if (openIdx === -1) return 0;
|
|
15983
|
+
const closeIdx = line.indexOf("-->", openIdx);
|
|
15984
|
+
return closeIdx === -1 ? 1 : 0;
|
|
15985
|
+
}
|
|
15986
|
+
};
|
|
15987
|
+
|
|
15988
|
+
// src/lib/editor/syntax/highlighters/markdown-highlighter.ts
|
|
15989
|
+
var MarkdownHighlighter = class {
|
|
15990
|
+
constructor() {
|
|
15991
|
+
this.id = "markdown";
|
|
15992
|
+
this.extensions = [".md", ".mdx", ".markdown"];
|
|
15993
|
+
this.initialState = 0;
|
|
15994
|
+
}
|
|
15995
|
+
getRules(state) {
|
|
15996
|
+
if (state === 1) {
|
|
15997
|
+
return [
|
|
15998
|
+
{ pattern: /^```/, tokenType: "string" },
|
|
15999
|
+
{ pattern: /.*/, tokenType: "code" }
|
|
16000
|
+
];
|
|
16001
|
+
}
|
|
16002
|
+
return [
|
|
16003
|
+
{ pattern: /^#{1,6}\s+.*$/, tokenType: "heading" },
|
|
16004
|
+
{ pattern: /^```/, tokenType: "string" },
|
|
16005
|
+
{ pattern: /`[^`]+`/, tokenType: "code" },
|
|
16006
|
+
{ pattern: /\[[^\]]+\]\([^)]+\)/, tokenType: "link" },
|
|
16007
|
+
{ pattern: /\*\*[^*]+\*\*/, tokenType: "bold" },
|
|
16008
|
+
{ pattern: /__[^_]+__/, tokenType: "bold" },
|
|
16009
|
+
{ pattern: /\*[^*]+\*/, tokenType: "italic" },
|
|
16010
|
+
{ pattern: /_[^_]+_/, tokenType: "italic" },
|
|
16011
|
+
{ pattern: /^[-*>]\s/, tokenType: "punctuation" }
|
|
16012
|
+
];
|
|
16013
|
+
}
|
|
16014
|
+
getNextLineState(line, currentState) {
|
|
16015
|
+
const trimmed = line.trimStart();
|
|
16016
|
+
if (trimmed.startsWith("```")) {
|
|
16017
|
+
return currentState === 0 ? 1 : 0;
|
|
16018
|
+
}
|
|
16019
|
+
return currentState;
|
|
16020
|
+
}
|
|
16021
|
+
};
|
|
16022
|
+
|
|
16023
|
+
// src/lib/editor/syntax/highlighters/yaml-highlighter.ts
|
|
16024
|
+
var YamlHighlighter = class {
|
|
16025
|
+
constructor() {
|
|
16026
|
+
this.id = "yaml";
|
|
16027
|
+
this.extensions = [".yaml", ".yml"];
|
|
16028
|
+
this.initialState = 0;
|
|
16029
|
+
}
|
|
16030
|
+
getRules() {
|
|
16031
|
+
return [
|
|
16032
|
+
{ pattern: /#.*$/, tokenType: "comment" },
|
|
16033
|
+
{ pattern: /"(?:[^"\\]|\\.)*"/, tokenType: "string" },
|
|
16034
|
+
{ pattern: /'[^']*'/, tokenType: "string" },
|
|
16035
|
+
{ pattern: /\b(?:true|false|null|~)\b/, tokenType: "keyword" },
|
|
16036
|
+
{ pattern: /-?\d+(?:\.\d+)?/, tokenType: "number" },
|
|
16037
|
+
{ pattern: /([\w][\w.\-]*)(\s*:)/, tokenType: "tag", captureGroup: 1 },
|
|
16038
|
+
{ pattern: /[-:\[\]{}]/, tokenType: "punctuation" }
|
|
16039
|
+
];
|
|
16040
|
+
}
|
|
16041
|
+
getNextLineState() {
|
|
16042
|
+
return 0;
|
|
16043
|
+
}
|
|
16044
|
+
};
|
|
16045
|
+
|
|
14501
16046
|
// src/lib/services/cli-translation-service.ts
|
|
14502
16047
|
var CliTranslationService = class {
|
|
14503
16048
|
constructor() {
|
|
@@ -15476,6 +17021,12 @@ var CliEngine = class {
|
|
|
15476
17021
|
}
|
|
15477
17022
|
]);
|
|
15478
17023
|
}
|
|
17024
|
+
const syntaxRegistry = new SyntaxHighlighterRegistry();
|
|
17025
|
+
syntaxRegistry.register(new JsonHighlighter());
|
|
17026
|
+
syntaxRegistry.register(new HtmlHighlighter());
|
|
17027
|
+
syntaxRegistry.register(new MarkdownHighlighter());
|
|
17028
|
+
syntaxRegistry.register(new YamlHighlighter());
|
|
17029
|
+
services.set([{ provide: "syntax-highlighter-registry", useValue: syntaxRegistry }]);
|
|
15479
17030
|
if (!pendingTokens.has(ICliEnvironment_TOKEN)) {
|
|
15480
17031
|
services.set([
|
|
15481
17032
|
{
|
|
@@ -15508,6 +17059,7 @@ var CliEngine = class {
|
|
|
15508
17059
|
{ ...this.options ?? {}, terminalOptions }
|
|
15509
17060
|
);
|
|
15510
17061
|
this.executionContext.initializeTerminalListeners();
|
|
17062
|
+
this.executionContext.backgroundServices.setProcessRegistry(processRegistry);
|
|
15511
17063
|
services.set([{
|
|
15512
17064
|
provide: CliBackgroundServiceRegistry_TOKEN,
|
|
15513
17065
|
useValue: this.executionContext.backgroundServices
|
|
@@ -16064,7 +17616,9 @@ function createQueuedReader(queue) {
|
|
|
16064
17616
|
readSelect: async () => next() ?? "",
|
|
16065
17617
|
readSelectInline: async () => next() ?? "",
|
|
16066
17618
|
readMultiSelect: async () => next() ?? [],
|
|
16067
|
-
readNumber: async () => next() ?? 0
|
|
17619
|
+
readNumber: async () => next() ?? 0,
|
|
17620
|
+
readDate: async () => next() ?? null,
|
|
17621
|
+
readFile: async () => next() ?? null
|
|
16068
17622
|
};
|
|
16069
17623
|
}
|
|
16070
17624
|
function createNoopBackgroundServices() {
|
|
@@ -16109,7 +17663,6 @@ exports.CapturingTerminalWriter = CapturingTerminalWriter;
|
|
|
16109
17663
|
exports.CliAliasCommandProcessor = CliAliasCommandProcessor;
|
|
16110
17664
|
exports.CliArgsParser = CliArgsParser;
|
|
16111
17665
|
exports.CliBackgroundServiceRegistry_TOKEN = CliBackgroundServiceRegistry_TOKEN;
|
|
16112
|
-
exports.CliBase64CommandProcessor = CliBase64CommandProcessor;
|
|
16113
17666
|
exports.CliBoot = CliBoot;
|
|
16114
17667
|
exports.CliCalCommandProcessor = CliCalCommandProcessor;
|
|
16115
17668
|
exports.CliCaptureCommandProcessor = CliCaptureCommandProcessor;
|
|
@@ -16138,14 +17691,12 @@ exports.CliExecutionProcess = CliExecutionProcess;
|
|
|
16138
17691
|
exports.CliExportCommandProcessor = CliExportCommandProcessor;
|
|
16139
17692
|
exports.CliFeedbackCommandProcessor = CliFeedbackCommandProcessor;
|
|
16140
17693
|
exports.CliFontSizeCommandProcessor = CliFontSizeCommandProcessor;
|
|
16141
|
-
exports.CliHashCommandProcessor = CliHashCommandProcessor;
|
|
16142
17694
|
exports.CliHelpCommandProcessor = CliHelpCommandProcessor;
|
|
16143
|
-
exports.CliHexCommandProcessor = CliHexCommandProcessor;
|
|
16144
17695
|
exports.CliHistoryCommandProcessor = CliHistoryCommandProcessor;
|
|
16145
17696
|
exports.CliHotKeysCommandProcessor = CliHotKeysCommandProcessor;
|
|
17697
|
+
exports.CliHtopCommandProcessor = CliHtopCommandProcessor;
|
|
16146
17698
|
exports.CliInputReader = CliInputReader;
|
|
16147
17699
|
exports.CliJsonCommandProcessor = CliJsonCommandProcessor;
|
|
16148
|
-
exports.CliJwtCommandProcessor = CliJwtCommandProcessor;
|
|
16149
17700
|
exports.CliKeyValueStore = CliKeyValueStore;
|
|
16150
17701
|
exports.CliKillCommandProcessor = CliKillCommandProcessor;
|
|
16151
17702
|
exports.CliLogger = CliLogger;
|
|
@@ -16190,7 +17741,6 @@ exports.CliUnAliasCommandProcessor = CliUnAliasCommandProcessor;
|
|
|
16190
17741
|
exports.CliUnameCommandProcessor = CliUnameCommandProcessor;
|
|
16191
17742
|
exports.CliUnsetCommandProcessor = CliUnsetCommandProcessor;
|
|
16192
17743
|
exports.CliUptimeCommandProcessor = CliUptimeCommandProcessor;
|
|
16193
|
-
exports.CliUrlCommandProcessor = CliUrlCommandProcessor;
|
|
16194
17744
|
exports.CliVersionCommandProcessor = CliVersionCommandProcessor;
|
|
16195
17745
|
exports.CliYesCommandProcessor = CliYesCommandProcessor;
|
|
16196
17746
|
exports.CommandParser = CommandParser;
|