@nmakarov/cli-toolkit 0.67.0 → 0.69.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli-runner.cjs +259 -45
- package/dist/cli-runner.cjs.map +1 -1
- package/dist/cli-runner.js +253 -39
- package/dist/cli-runner.js.map +1 -1
- package/dist/index.cjs +265 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +253 -39
- package/dist/index.js.map +1 -1
- package/dist/init.cjs +259 -45
- package/dist/init.cjs.map +1 -1
- package/dist/init.js +253 -39
- package/dist/init.js.map +1 -1
- package/dist/screen.cjs +255 -47
- package/dist/screen.cjs.map +1 -1
- package/dist/screen.js +239 -39
- package/dist/screen.js.map +1 -1
- package/package.json +1 -1
package/dist/cli-runner.js
CHANGED
|
@@ -478,6 +478,59 @@ var init_list_components = __esm({
|
|
|
478
478
|
}
|
|
479
479
|
});
|
|
480
480
|
|
|
481
|
+
// src/screen/key-bindings.js
|
|
482
|
+
function bindingIdentity(b) {
|
|
483
|
+
return [
|
|
484
|
+
String(b?.key ?? ""),
|
|
485
|
+
b?.meta ? "m" : "",
|
|
486
|
+
b?.ctrl ? "c" : "",
|
|
487
|
+
b?.shift ? "s" : ""
|
|
488
|
+
].join("|");
|
|
489
|
+
}
|
|
490
|
+
function bindingMatchesInput(binding, input, key) {
|
|
491
|
+
if (!binding?.key) return false;
|
|
492
|
+
let keyMatches = false;
|
|
493
|
+
if (key?.[binding.key]) {
|
|
494
|
+
keyMatches = true;
|
|
495
|
+
} else if (input === binding.key) {
|
|
496
|
+
keyMatches = true;
|
|
497
|
+
} else if (key?.name === binding.key) {
|
|
498
|
+
keyMatches = true;
|
|
499
|
+
}
|
|
500
|
+
if (!keyMatches) return false;
|
|
501
|
+
const modOk = (flag, pressed) => {
|
|
502
|
+
if (flag === true) return !!pressed;
|
|
503
|
+
if (flag === false) return !pressed;
|
|
504
|
+
return !pressed;
|
|
505
|
+
};
|
|
506
|
+
if (!modOk(binding.meta, key?.meta)) return false;
|
|
507
|
+
if (!modOk(binding.ctrl, key?.ctrl)) return false;
|
|
508
|
+
if (binding.shift !== void 0 && !!key?.shift !== !!binding.shift) return false;
|
|
509
|
+
return true;
|
|
510
|
+
}
|
|
511
|
+
function formatBindingKey(binding) {
|
|
512
|
+
let label = KEY_LABELS[binding.key] || binding.key;
|
|
513
|
+
if (binding.shift) label = `\u21E7${label}`;
|
|
514
|
+
if (binding.ctrl) label = `^${label}`;
|
|
515
|
+
if (binding.meta) label = `\u2325${label}`;
|
|
516
|
+
return label;
|
|
517
|
+
}
|
|
518
|
+
var KEY_LABELS;
|
|
519
|
+
var init_key_bindings = __esm({
|
|
520
|
+
"src/screen/key-bindings.js"() {
|
|
521
|
+
KEY_LABELS = {
|
|
522
|
+
escape: "esc",
|
|
523
|
+
leftArrow: "\u2190",
|
|
524
|
+
rightArrow: "\u2192",
|
|
525
|
+
upArrow: "\u2191",
|
|
526
|
+
downArrow: "\u2193",
|
|
527
|
+
return: "enter",
|
|
528
|
+
pageUp: "PgUp",
|
|
529
|
+
pageDown: "PgDn"
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
});
|
|
533
|
+
|
|
481
534
|
// src/screen/screens.js
|
|
482
535
|
import { useState as useState2, createElement as h3 } from "react";
|
|
483
536
|
import { render, useInput, Text as Text3 } from "ink";
|
|
@@ -493,7 +546,7 @@ function groupKeyBindings(bindings) {
|
|
|
493
546
|
order: binding.order || 999
|
|
494
547
|
};
|
|
495
548
|
}
|
|
496
|
-
groups[caption].keys.push(binding
|
|
549
|
+
groups[caption].keys.push(formatBindingKey(binding));
|
|
497
550
|
});
|
|
498
551
|
return Object.values(groups);
|
|
499
552
|
}
|
|
@@ -533,12 +586,14 @@ function formatKeyBindings(bindings, mode = "long") {
|
|
|
533
586
|
}
|
|
534
587
|
function formatKeys(keys) {
|
|
535
588
|
const keyMap = {
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
589
|
+
escape: "esc",
|
|
590
|
+
leftArrow: "\u2190",
|
|
591
|
+
rightArrow: "\u2192",
|
|
592
|
+
upArrow: "\u2191",
|
|
593
|
+
downArrow: "\u2193",
|
|
594
|
+
return: "enter",
|
|
595
|
+
pageUp: "PgUp",
|
|
596
|
+
pageDown: "PgDn"
|
|
542
597
|
};
|
|
543
598
|
return keys.map((k) => keyMap[k] || k).join("/");
|
|
544
599
|
}
|
|
@@ -578,7 +633,10 @@ async function showScreen(config2) {
|
|
|
578
633
|
setKeyBinding: (bindingOrBindings) => {
|
|
579
634
|
const bindingsToSet = Array.isArray(bindingOrBindings) ? bindingOrBindings : [bindingOrBindings];
|
|
580
635
|
bindingsToSet.forEach((binding) => {
|
|
581
|
-
const
|
|
636
|
+
const id = bindingIdentity(binding);
|
|
637
|
+
const existingIndex = keyBindings.findIndex(
|
|
638
|
+
(b) => bindingIdentity(b) === id
|
|
639
|
+
);
|
|
582
640
|
if (existingIndex >= 0) {
|
|
583
641
|
const existing = keyBindings[existingIndex];
|
|
584
642
|
if (existing.protected) {
|
|
@@ -602,7 +660,10 @@ async function showScreen(config2) {
|
|
|
602
660
|
});
|
|
603
661
|
},
|
|
604
662
|
updateKeyBinding: (keyName, updates) => {
|
|
605
|
-
const
|
|
663
|
+
const id = updates && (updates.meta != null || updates.ctrl != null || updates.shift != null) ? bindingIdentity({ key: keyName, ...updates }) : null;
|
|
664
|
+
const index = keyBindings.findIndex(
|
|
665
|
+
(b) => id ? bindingIdentity(b) === id : b.key === keyName
|
|
666
|
+
);
|
|
606
667
|
if (index >= 0) {
|
|
607
668
|
keyBindings[index] = {
|
|
608
669
|
...keyBindings[index],
|
|
@@ -610,11 +671,13 @@ async function showScreen(config2) {
|
|
|
610
671
|
};
|
|
611
672
|
}
|
|
612
673
|
},
|
|
613
|
-
removeKeyBinding: (
|
|
614
|
-
const index = keyBindings.findIndex(
|
|
674
|
+
removeKeyBinding: (keyNameOrBinding) => {
|
|
675
|
+
const index = typeof keyNameOrBinding === "object" && keyNameOrBinding ? keyBindings.findIndex(
|
|
676
|
+
(b) => bindingIdentity(b) === bindingIdentity(keyNameOrBinding)
|
|
677
|
+
) : keyBindings.findIndex((b) => b.key === keyNameOrBinding);
|
|
615
678
|
if (index >= 0) {
|
|
616
679
|
if (keyBindings[index].protected) {
|
|
617
|
-
console.warn(`Cannot remove protected key: ${
|
|
680
|
+
console.warn(`Cannot remove protected key: ${keyNameOrBinding}`);
|
|
618
681
|
return;
|
|
619
682
|
}
|
|
620
683
|
keyBindings.splice(index, 1);
|
|
@@ -655,24 +718,11 @@ async function showScreen(config2) {
|
|
|
655
718
|
}
|
|
656
719
|
let matchedBinding = null;
|
|
657
720
|
for (const binding of keyBindings) {
|
|
658
|
-
|
|
659
|
-
if (
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
} else if (key?.name === binding.key) {
|
|
664
|
-
keyMatches = true;
|
|
665
|
-
}
|
|
666
|
-
if (keyMatches) {
|
|
667
|
-
if (binding.enabled === false) {
|
|
668
|
-
continue;
|
|
669
|
-
}
|
|
670
|
-
if (binding.condition && !binding.condition(context)) {
|
|
671
|
-
continue;
|
|
672
|
-
}
|
|
673
|
-
matchedBinding = binding;
|
|
674
|
-
break;
|
|
675
|
-
}
|
|
721
|
+
if (binding.enabled === false) continue;
|
|
722
|
+
if (!bindingMatchesInput(binding, input, key)) continue;
|
|
723
|
+
if (binding.condition && !binding.condition(context)) continue;
|
|
724
|
+
matchedBinding = binding;
|
|
725
|
+
break;
|
|
676
726
|
}
|
|
677
727
|
if (matchedBinding && actions[matchedBinding.action]) {
|
|
678
728
|
actions[matchedBinding.action]({
|
|
@@ -808,6 +858,7 @@ var init_screens = __esm({
|
|
|
808
858
|
"src/screen/screens.js"() {
|
|
809
859
|
init_components();
|
|
810
860
|
init_list_components();
|
|
861
|
+
init_key_bindings();
|
|
811
862
|
showMenuScreen = showListScreen;
|
|
812
863
|
showWordGridScreen = showMultiColumnListScreen;
|
|
813
864
|
}
|
|
@@ -897,6 +948,161 @@ var init_ui_elements = __esm({
|
|
|
897
948
|
}
|
|
898
949
|
});
|
|
899
950
|
|
|
951
|
+
// src/screen/scrollable-text.js
|
|
952
|
+
import { useState as useState3, useEffect as useEffect2, useMemo, useRef as useRef2, createElement as createElement2 } from "react";
|
|
953
|
+
import { Box as Box4, Text as Text5 } from "ink";
|
|
954
|
+
function wrapTextLines(text, cols) {
|
|
955
|
+
const w = Math.max(1, Math.floor(Number(cols) || 1));
|
|
956
|
+
const out = [];
|
|
957
|
+
for (const raw of String(text ?? "").split("\n")) {
|
|
958
|
+
if (raw.length === 0) {
|
|
959
|
+
out.push("");
|
|
960
|
+
continue;
|
|
961
|
+
}
|
|
962
|
+
let rest = raw;
|
|
963
|
+
while (rest.length > w) {
|
|
964
|
+
out.push(rest.slice(0, w));
|
|
965
|
+
rest = rest.slice(w);
|
|
966
|
+
}
|
|
967
|
+
if (rest.length > 0) out.push(rest);
|
|
968
|
+
}
|
|
969
|
+
return out;
|
|
970
|
+
}
|
|
971
|
+
function scrollbarGlyphs(viewportRows, totalLines, scrollTop) {
|
|
972
|
+
const view = Math.max(1, Math.floor(viewportRows));
|
|
973
|
+
const total = Math.max(0, Math.floor(totalLines));
|
|
974
|
+
if (total <= view) return null;
|
|
975
|
+
const maxScroll = total - view;
|
|
976
|
+
const thumbSize = Math.max(1, Math.round(view / total * view));
|
|
977
|
+
const travel = Math.max(0, view - thumbSize);
|
|
978
|
+
const s = Math.min(Math.max(0, Math.floor(scrollTop)), maxScroll);
|
|
979
|
+
const thumbStart = maxScroll === 0 ? 0 : Math.round(s / maxScroll * travel);
|
|
980
|
+
const glyphs = [];
|
|
981
|
+
for (let i = 0; i < view; i++) {
|
|
982
|
+
glyphs.push(i >= thumbStart && i < thumbStart + thumbSize ? BAR_THUMB : BAR_TRACK);
|
|
983
|
+
}
|
|
984
|
+
return glyphs;
|
|
985
|
+
}
|
|
986
|
+
function padEndVisible(s, w) {
|
|
987
|
+
const t = String(s ?? "");
|
|
988
|
+
if (t.length >= w) return t.slice(0, w);
|
|
989
|
+
return t + " ".repeat(w - t.length);
|
|
990
|
+
}
|
|
991
|
+
function ScrollableText({
|
|
992
|
+
ctx,
|
|
993
|
+
text = "",
|
|
994
|
+
lines: linesProp,
|
|
995
|
+
maxHeight,
|
|
996
|
+
wrap = true,
|
|
997
|
+
showScrollbar = true,
|
|
998
|
+
showStatus = true,
|
|
999
|
+
bindKeys = true,
|
|
1000
|
+
header = null
|
|
1001
|
+
}) {
|
|
1002
|
+
const [scrollTop, setScrollTop] = useState3(0);
|
|
1003
|
+
const [, bump] = useState3(0);
|
|
1004
|
+
const termRows = process.stdout.rows || 24;
|
|
1005
|
+
const viewportRows = Math.max(
|
|
1006
|
+
4,
|
|
1007
|
+
maxHeight != null ? Math.floor(maxHeight) : Math.max(8, termRows - 8)
|
|
1008
|
+
);
|
|
1009
|
+
const contentCols = Math.max(20, getScreenWidth() - 4);
|
|
1010
|
+
const barCols = showScrollbar ? 1 : 0;
|
|
1011
|
+
const textWidth = Math.max(8, contentCols - barCols);
|
|
1012
|
+
const allLines = useMemo(() => {
|
|
1013
|
+
if (Array.isArray(linesProp)) return linesProp.map((l) => String(l ?? ""));
|
|
1014
|
+
return wrap ? wrapTextLines(text, textWidth) : String(text ?? "").split("\n");
|
|
1015
|
+
}, [linesProp, text, wrap, textWidth]);
|
|
1016
|
+
const needsBar = showScrollbar && allLines.length > viewportRows;
|
|
1017
|
+
const maxScroll = Math.max(0, allLines.length - viewportRows);
|
|
1018
|
+
const clamped = Math.min(Math.max(0, scrollTop), maxScroll);
|
|
1019
|
+
const visible = allLines.slice(clamped, clamped + viewportRows);
|
|
1020
|
+
const bar = needsBar ? scrollbarGlyphs(viewportRows, allLines.length, clamped) : null;
|
|
1021
|
+
useEffect2(() => {
|
|
1022
|
+
setScrollTop((s) => Math.min(s, maxScroll));
|
|
1023
|
+
}, [maxScroll]);
|
|
1024
|
+
const maxScrollRef = useRef2(maxScroll);
|
|
1025
|
+
const pageSizeRef = useRef2(viewportRows);
|
|
1026
|
+
maxScrollRef.current = maxScroll;
|
|
1027
|
+
pageSizeRef.current = viewportRows;
|
|
1028
|
+
useEffect2(() => {
|
|
1029
|
+
if (!ctx || !bindKeys) return void 0;
|
|
1030
|
+
ctx.setKeyBinding(SCROLL_KEYS);
|
|
1031
|
+
ctx.setAction("scrollUp", () => {
|
|
1032
|
+
setScrollTop((s) => Math.max(0, s - 1));
|
|
1033
|
+
bump((n) => n + 1);
|
|
1034
|
+
ctx.update?.();
|
|
1035
|
+
});
|
|
1036
|
+
ctx.setAction("scrollDown", () => {
|
|
1037
|
+
setScrollTop((s) => Math.min(maxScrollRef.current, s + 1));
|
|
1038
|
+
bump((n) => n + 1);
|
|
1039
|
+
ctx.update?.();
|
|
1040
|
+
});
|
|
1041
|
+
ctx.setAction("pageUp", () => {
|
|
1042
|
+
setScrollTop((s) => Math.max(0, s - pageSizeRef.current));
|
|
1043
|
+
bump((n) => n + 1);
|
|
1044
|
+
ctx.update?.();
|
|
1045
|
+
});
|
|
1046
|
+
ctx.setAction("pageDown", () => {
|
|
1047
|
+
setScrollTop((s) => Math.min(maxScrollRef.current, s + pageSizeRef.current));
|
|
1048
|
+
bump((n) => n + 1);
|
|
1049
|
+
ctx.update?.();
|
|
1050
|
+
});
|
|
1051
|
+
return void 0;
|
|
1052
|
+
}, [ctx, bindKeys]);
|
|
1053
|
+
const status = allLines.length === 0 ? "empty" : `lines ${clamped + 1}-${Math.min(clamped + visible.length, allLines.length)} of ${allLines.length}` + (needsBar ? " \xB7 \u2325\u2191/\u2193 or PgUp/Dn page" : "");
|
|
1054
|
+
const rowNodes = visible.map((line, i) => {
|
|
1055
|
+
const body = padEndVisible(line, textWidth);
|
|
1056
|
+
const glyph = bar ? bar[i] ?? BAR_TRACK : showScrollbar ? " " : "";
|
|
1057
|
+
const isThumb = glyph === BAR_THUMB;
|
|
1058
|
+
return h5(
|
|
1059
|
+
Text5,
|
|
1060
|
+
{ key: `L${clamped + i}` },
|
|
1061
|
+
body,
|
|
1062
|
+
glyph ? h5(Text5, { color: isThumb ? "cyan" : "gray" }, glyph) : null
|
|
1063
|
+
);
|
|
1064
|
+
});
|
|
1065
|
+
if (bar && visible.length < viewportRows) {
|
|
1066
|
+
for (let i = visible.length; i < viewportRows; i++) {
|
|
1067
|
+
const glyph = bar[i] ?? BAR_TRACK;
|
|
1068
|
+
rowNodes.push(
|
|
1069
|
+
h5(
|
|
1070
|
+
Text5,
|
|
1071
|
+
{ key: `pad${i}` },
|
|
1072
|
+
padEndVisible("", textWidth),
|
|
1073
|
+
h5(Text5, { color: glyph === BAR_THUMB ? "cyan" : "gray" }, glyph)
|
|
1074
|
+
)
|
|
1075
|
+
);
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
return h5(
|
|
1079
|
+
Box4,
|
|
1080
|
+
{ flexDirection: "column" },
|
|
1081
|
+
header == null ? null : typeof header === "string" ? h5(Text5, { color: "gray" }, header) : header,
|
|
1082
|
+
showStatus ? h5(Text5, { color: "gray" }, status) : null,
|
|
1083
|
+
...rowNodes
|
|
1084
|
+
);
|
|
1085
|
+
}
|
|
1086
|
+
var h5, BAR_THUMB, BAR_TRACK, SCROLL_KEYS;
|
|
1087
|
+
var init_scrollable_text = __esm({
|
|
1088
|
+
"src/screen/scrollable-text.js"() {
|
|
1089
|
+
init_components();
|
|
1090
|
+
h5 = createElement2;
|
|
1091
|
+
BAR_THUMB = "#";
|
|
1092
|
+
BAR_TRACK = "|";
|
|
1093
|
+
SCROLL_KEYS = [
|
|
1094
|
+
{ key: "upArrow", caption: "scroll", action: "scrollUp", order: 0 },
|
|
1095
|
+
{ key: "downArrow", caption: "scroll", action: "scrollDown", order: 0 },
|
|
1096
|
+
{ key: "upArrow", meta: true, caption: "page", action: "pageUp", order: 0 },
|
|
1097
|
+
{ key: "downArrow", meta: true, caption: "page", action: "pageDown", order: 0 },
|
|
1098
|
+
{ key: "upArrow", ctrl: true, caption: "page", action: "pageUp", order: 0 },
|
|
1099
|
+
{ key: "downArrow", ctrl: true, caption: "page", action: "pageDown", order: 0 },
|
|
1100
|
+
{ key: "pageUp", caption: "page", action: "pageUp", order: 0 },
|
|
1101
|
+
{ key: "pageDown", caption: "page", action: "pageDown", order: 0 }
|
|
1102
|
+
];
|
|
1103
|
+
}
|
|
1104
|
+
});
|
|
1105
|
+
|
|
900
1106
|
// src/screen/utils.js
|
|
901
1107
|
function buildBreadcrumb(parts) {
|
|
902
1108
|
if (parts.length === 0) return "";
|
|
@@ -1028,7 +1234,7 @@ var init_footer_builder = __esm({
|
|
|
1028
1234
|
// src/screen/index.js
|
|
1029
1235
|
var screen_exports = {};
|
|
1030
1236
|
__export(screen_exports, {
|
|
1031
|
-
Box: () =>
|
|
1237
|
+
Box: () => Box5,
|
|
1032
1238
|
Divider: () => Divider,
|
|
1033
1239
|
FooterPresets: () => FooterPresets,
|
|
1034
1240
|
GridCell: () => GridCell,
|
|
@@ -1044,15 +1250,20 @@ __export(screen_exports, {
|
|
|
1044
1250
|
ScreenFooter: () => ScreenFooter,
|
|
1045
1251
|
ScreenRow: () => ScreenRow,
|
|
1046
1252
|
ScreenTitle: () => ScreenTitle,
|
|
1047
|
-
|
|
1253
|
+
ScrollableText: () => ScrollableText,
|
|
1254
|
+
Text: () => Text6,
|
|
1048
1255
|
TextBlock: () => TextBlock,
|
|
1256
|
+
bindingIdentity: () => bindingIdentity,
|
|
1257
|
+
bindingMatchesInput: () => bindingMatchesInput,
|
|
1049
1258
|
buildBreadcrumb: () => buildBreadcrumb,
|
|
1050
1259
|
buildDetailBreadcrumb: () => buildDetailBreadcrumb,
|
|
1051
1260
|
buildFooter: () => buildFooter,
|
|
1052
|
-
|
|
1261
|
+
formatBindingKey: () => formatBindingKey,
|
|
1262
|
+
h: () => createElement3,
|
|
1053
1263
|
load: () => load,
|
|
1054
1264
|
memo: () => memo,
|
|
1055
1265
|
organizeFooterMessages: () => organizeFooterMessages,
|
|
1266
|
+
scrollbarGlyphs: () => scrollbarGlyphs,
|
|
1056
1267
|
showListScreen: () => showListScreen,
|
|
1057
1268
|
showMenuScreen: () => showMenuScreen,
|
|
1058
1269
|
showMultiColumnListScreen: () => showMultiColumnListScreen,
|
|
@@ -1060,15 +1271,16 @@ __export(screen_exports, {
|
|
|
1060
1271
|
showScreen: () => showScreen,
|
|
1061
1272
|
showWordGridScreen: () => showWordGridScreen,
|
|
1062
1273
|
useCallback: () => useCallback,
|
|
1063
|
-
useEffect: () =>
|
|
1274
|
+
useEffect: () => useEffect3,
|
|
1064
1275
|
useInput: () => useInput2,
|
|
1065
1276
|
useLayoutEffect: () => useLayoutEffect,
|
|
1066
|
-
useMemo: () =>
|
|
1067
|
-
useRef: () =>
|
|
1068
|
-
useState: () =>
|
|
1277
|
+
useMemo: () => useMemo2,
|
|
1278
|
+
useRef: () => useRef3,
|
|
1279
|
+
useState: () => useState4,
|
|
1280
|
+
wrapTextLines: () => wrapTextLines
|
|
1069
1281
|
});
|
|
1070
|
-
import React2, { useState as
|
|
1071
|
-
import { Box as
|
|
1282
|
+
import React2, { useState as useState4, useEffect as useEffect3, useLayoutEffect, useRef as useRef3, useMemo as useMemo2, useCallback, memo, createElement as createElement3 } from "react";
|
|
1283
|
+
import { Box as Box5, Text as Text6, useInput as useInput2 } from "ink";
|
|
1072
1284
|
async function load() {
|
|
1073
1285
|
if (loadPromise) return loadPromise;
|
|
1074
1286
|
loadPromise = Promise.all([
|
|
@@ -1085,6 +1297,8 @@ var init_screen = __esm({
|
|
|
1085
1297
|
init_list_components();
|
|
1086
1298
|
init_components();
|
|
1087
1299
|
init_ui_elements();
|
|
1300
|
+
init_scrollable_text();
|
|
1301
|
+
init_key_bindings();
|
|
1088
1302
|
init_utils();
|
|
1089
1303
|
init_footer_builder();
|
|
1090
1304
|
loadPromise = null;
|