@nmakarov/cli-toolkit 0.67.0 → 0.68.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 +255 -45
- package/dist/cli-runner.cjs.map +1 -1
- package/dist/cli-runner.js +249 -39
- package/dist/cli-runner.js.map +1 -1
- package/dist/index.cjs +261 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +249 -39
- package/dist/index.js.map +1 -1
- package/dist/init.cjs +255 -45
- package/dist/init.cjs.map +1 -1
- package/dist/init.js +249 -39
- package/dist/init.js.map +1 -1
- package/dist/screen.cjs +252 -47
- package/dist/screen.cjs.map +1 -1
- package/dist/screen.js +236 -39
- package/dist/screen.js.map +1 -1
- package/package.json +1 -1
package/dist/screen.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/screen/index.js
|
|
2
|
-
import React2, { useState as
|
|
3
|
-
import { Box as
|
|
2
|
+
import React2, { useState as useState4, useEffect as useEffect3, useLayoutEffect, useRef as useRef3, useMemo as useMemo2, useCallback, memo, createElement as createElement3 } from "react";
|
|
3
|
+
import { Box as Box5, Text as Text6, useInput as useInput2 } from "ink";
|
|
4
4
|
|
|
5
5
|
// src/screen/screens.js
|
|
6
6
|
import { useState as useState2, createElement as h3 } from "react";
|
|
@@ -459,6 +459,54 @@ function ListComponent({ items, ctx, selectedIndexRef, renderItem, getTitle, sor
|
|
|
459
459
|
);
|
|
460
460
|
}
|
|
461
461
|
|
|
462
|
+
// src/screen/key-bindings.js
|
|
463
|
+
function bindingIdentity(b) {
|
|
464
|
+
return [
|
|
465
|
+
String(b?.key ?? ""),
|
|
466
|
+
b?.meta ? "m" : "",
|
|
467
|
+
b?.ctrl ? "c" : "",
|
|
468
|
+
b?.shift ? "s" : ""
|
|
469
|
+
].join("|");
|
|
470
|
+
}
|
|
471
|
+
function bindingMatchesInput(binding, input, key) {
|
|
472
|
+
if (!binding?.key) return false;
|
|
473
|
+
let keyMatches = false;
|
|
474
|
+
if (key?.[binding.key]) {
|
|
475
|
+
keyMatches = true;
|
|
476
|
+
} else if (input === binding.key) {
|
|
477
|
+
keyMatches = true;
|
|
478
|
+
} else if (key?.name === binding.key) {
|
|
479
|
+
keyMatches = true;
|
|
480
|
+
}
|
|
481
|
+
if (!keyMatches) return false;
|
|
482
|
+
const modOk = (flag, pressed) => {
|
|
483
|
+
if (flag === true) return !!pressed;
|
|
484
|
+
if (flag === false) return !pressed;
|
|
485
|
+
return !pressed;
|
|
486
|
+
};
|
|
487
|
+
if (!modOk(binding.meta, key?.meta)) return false;
|
|
488
|
+
if (!modOk(binding.ctrl, key?.ctrl)) return false;
|
|
489
|
+
if (binding.shift !== void 0 && !!key?.shift !== !!binding.shift) return false;
|
|
490
|
+
return true;
|
|
491
|
+
}
|
|
492
|
+
var KEY_LABELS = {
|
|
493
|
+
escape: "esc",
|
|
494
|
+
leftArrow: "\u2190",
|
|
495
|
+
rightArrow: "\u2192",
|
|
496
|
+
upArrow: "\u2191",
|
|
497
|
+
downArrow: "\u2193",
|
|
498
|
+
return: "enter",
|
|
499
|
+
pageUp: "PgUp",
|
|
500
|
+
pageDown: "PgDn"
|
|
501
|
+
};
|
|
502
|
+
function formatBindingKey(binding) {
|
|
503
|
+
let label = KEY_LABELS[binding.key] || binding.key;
|
|
504
|
+
if (binding.shift) label = `\u21E7${label}`;
|
|
505
|
+
if (binding.ctrl) label = `^${label}`;
|
|
506
|
+
if (binding.meta) label = `\u2325${label}`;
|
|
507
|
+
return label;
|
|
508
|
+
}
|
|
509
|
+
|
|
462
510
|
// src/screen/screens.js
|
|
463
511
|
function groupKeyBindings(bindings) {
|
|
464
512
|
const groups = {};
|
|
@@ -472,7 +520,7 @@ function groupKeyBindings(bindings) {
|
|
|
472
520
|
order: binding.order || 999
|
|
473
521
|
};
|
|
474
522
|
}
|
|
475
|
-
groups[caption].keys.push(binding
|
|
523
|
+
groups[caption].keys.push(formatBindingKey(binding));
|
|
476
524
|
});
|
|
477
525
|
return Object.values(groups);
|
|
478
526
|
}
|
|
@@ -512,12 +560,14 @@ function formatKeyBindings(bindings, mode = "long") {
|
|
|
512
560
|
}
|
|
513
561
|
function formatKeys(keys) {
|
|
514
562
|
const keyMap = {
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
563
|
+
escape: "esc",
|
|
564
|
+
leftArrow: "\u2190",
|
|
565
|
+
rightArrow: "\u2192",
|
|
566
|
+
upArrow: "\u2191",
|
|
567
|
+
downArrow: "\u2193",
|
|
568
|
+
return: "enter",
|
|
569
|
+
pageUp: "PgUp",
|
|
570
|
+
pageDown: "PgDn"
|
|
521
571
|
};
|
|
522
572
|
return keys.map((k) => keyMap[k] || k).join("/");
|
|
523
573
|
}
|
|
@@ -557,7 +607,10 @@ async function showScreen(config) {
|
|
|
557
607
|
setKeyBinding: (bindingOrBindings) => {
|
|
558
608
|
const bindingsToSet = Array.isArray(bindingOrBindings) ? bindingOrBindings : [bindingOrBindings];
|
|
559
609
|
bindingsToSet.forEach((binding) => {
|
|
560
|
-
const
|
|
610
|
+
const id = bindingIdentity(binding);
|
|
611
|
+
const existingIndex = keyBindings.findIndex(
|
|
612
|
+
(b) => bindingIdentity(b) === id
|
|
613
|
+
);
|
|
561
614
|
if (existingIndex >= 0) {
|
|
562
615
|
const existing = keyBindings[existingIndex];
|
|
563
616
|
if (existing.protected) {
|
|
@@ -581,7 +634,10 @@ async function showScreen(config) {
|
|
|
581
634
|
});
|
|
582
635
|
},
|
|
583
636
|
updateKeyBinding: (keyName, updates) => {
|
|
584
|
-
const
|
|
637
|
+
const id = updates && (updates.meta != null || updates.ctrl != null || updates.shift != null) ? bindingIdentity({ key: keyName, ...updates }) : null;
|
|
638
|
+
const index = keyBindings.findIndex(
|
|
639
|
+
(b) => id ? bindingIdentity(b) === id : b.key === keyName
|
|
640
|
+
);
|
|
585
641
|
if (index >= 0) {
|
|
586
642
|
keyBindings[index] = {
|
|
587
643
|
...keyBindings[index],
|
|
@@ -589,11 +645,13 @@ async function showScreen(config) {
|
|
|
589
645
|
};
|
|
590
646
|
}
|
|
591
647
|
},
|
|
592
|
-
removeKeyBinding: (
|
|
593
|
-
const index = keyBindings.findIndex(
|
|
648
|
+
removeKeyBinding: (keyNameOrBinding) => {
|
|
649
|
+
const index = typeof keyNameOrBinding === "object" && keyNameOrBinding ? keyBindings.findIndex(
|
|
650
|
+
(b) => bindingIdentity(b) === bindingIdentity(keyNameOrBinding)
|
|
651
|
+
) : keyBindings.findIndex((b) => b.key === keyNameOrBinding);
|
|
594
652
|
if (index >= 0) {
|
|
595
653
|
if (keyBindings[index].protected) {
|
|
596
|
-
console.warn(`Cannot remove protected key: ${
|
|
654
|
+
console.warn(`Cannot remove protected key: ${keyNameOrBinding}`);
|
|
597
655
|
return;
|
|
598
656
|
}
|
|
599
657
|
keyBindings.splice(index, 1);
|
|
@@ -634,24 +692,11 @@ async function showScreen(config) {
|
|
|
634
692
|
}
|
|
635
693
|
let matchedBinding = null;
|
|
636
694
|
for (const binding of keyBindings) {
|
|
637
|
-
|
|
638
|
-
if (
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
} else if (key?.name === binding.key) {
|
|
643
|
-
keyMatches = true;
|
|
644
|
-
}
|
|
645
|
-
if (keyMatches) {
|
|
646
|
-
if (binding.enabled === false) {
|
|
647
|
-
continue;
|
|
648
|
-
}
|
|
649
|
-
if (binding.condition && !binding.condition(context)) {
|
|
650
|
-
continue;
|
|
651
|
-
}
|
|
652
|
-
matchedBinding = binding;
|
|
653
|
-
break;
|
|
654
|
-
}
|
|
695
|
+
if (binding.enabled === false) continue;
|
|
696
|
+
if (!bindingMatchesInput(binding, input, key)) continue;
|
|
697
|
+
if (binding.condition && !binding.condition(context)) continue;
|
|
698
|
+
matchedBinding = binding;
|
|
699
|
+
break;
|
|
655
700
|
}
|
|
656
701
|
if (matchedBinding && actions[matchedBinding.action]) {
|
|
657
702
|
actions[matchedBinding.action]({
|
|
@@ -865,6 +910,152 @@ function InputField({ prompt, value, onChange: _onChange, onSubmit: _onSubmit })
|
|
|
865
910
|
);
|
|
866
911
|
}
|
|
867
912
|
|
|
913
|
+
// src/screen/scrollable-text.js
|
|
914
|
+
import { useState as useState3, useEffect as useEffect2, useMemo, useRef as useRef2, createElement as createElement2 } from "react";
|
|
915
|
+
import { Box as Box4, Text as Text5 } from "ink";
|
|
916
|
+
var h5 = createElement2;
|
|
917
|
+
function wrapTextLines(text, cols) {
|
|
918
|
+
const w = Math.max(1, Math.floor(Number(cols) || 1));
|
|
919
|
+
const out = [];
|
|
920
|
+
for (const raw of String(text ?? "").split("\n")) {
|
|
921
|
+
if (raw.length === 0) {
|
|
922
|
+
out.push("");
|
|
923
|
+
continue;
|
|
924
|
+
}
|
|
925
|
+
let rest = raw;
|
|
926
|
+
while (rest.length > w) {
|
|
927
|
+
out.push(rest.slice(0, w));
|
|
928
|
+
rest = rest.slice(w);
|
|
929
|
+
}
|
|
930
|
+
if (rest.length > 0) out.push(rest);
|
|
931
|
+
}
|
|
932
|
+
return out;
|
|
933
|
+
}
|
|
934
|
+
function scrollbarGlyphs(viewportRows, totalLines, scrollTop) {
|
|
935
|
+
const view = Math.max(1, Math.floor(viewportRows));
|
|
936
|
+
const total = Math.max(0, Math.floor(totalLines));
|
|
937
|
+
if (total <= view) return null;
|
|
938
|
+
const maxScroll = total - view;
|
|
939
|
+
const thumbSize = Math.max(1, Math.round(view / total * view));
|
|
940
|
+
const travel = Math.max(0, view - thumbSize);
|
|
941
|
+
const s = Math.min(Math.max(0, Math.floor(scrollTop)), maxScroll);
|
|
942
|
+
const thumbStart = maxScroll === 0 ? 0 : Math.round(s / maxScroll * travel);
|
|
943
|
+
const glyphs = [];
|
|
944
|
+
for (let i = 0; i < view; i++) {
|
|
945
|
+
glyphs.push(i >= thumbStart && i < thumbStart + thumbSize ? "\u2588" : "\u2502");
|
|
946
|
+
}
|
|
947
|
+
return glyphs;
|
|
948
|
+
}
|
|
949
|
+
function padEndVisible(s, w) {
|
|
950
|
+
const t = String(s ?? "");
|
|
951
|
+
if (t.length >= w) return t.slice(0, w);
|
|
952
|
+
return t + " ".repeat(w - t.length);
|
|
953
|
+
}
|
|
954
|
+
var SCROLL_KEYS = [
|
|
955
|
+
{ key: "upArrow", caption: "scroll", action: "scrollUp", order: 0 },
|
|
956
|
+
{ key: "downArrow", caption: "scroll", action: "scrollDown", order: 0 },
|
|
957
|
+
{ key: "upArrow", meta: true, caption: "page", action: "pageUp", order: 0 },
|
|
958
|
+
{ key: "downArrow", meta: true, caption: "page", action: "pageDown", order: 0 },
|
|
959
|
+
{ key: "upArrow", ctrl: true, caption: "page", action: "pageUp", order: 0 },
|
|
960
|
+
{ key: "downArrow", ctrl: true, caption: "page", action: "pageDown", order: 0 },
|
|
961
|
+
{ key: "pageUp", caption: "page", action: "pageUp", order: 0 },
|
|
962
|
+
{ key: "pageDown", caption: "page", action: "pageDown", order: 0 }
|
|
963
|
+
];
|
|
964
|
+
function ScrollableText({
|
|
965
|
+
ctx,
|
|
966
|
+
text = "",
|
|
967
|
+
lines: linesProp,
|
|
968
|
+
maxHeight,
|
|
969
|
+
wrap = true,
|
|
970
|
+
showScrollbar = true,
|
|
971
|
+
showStatus = true,
|
|
972
|
+
bindKeys = true,
|
|
973
|
+
header = null
|
|
974
|
+
}) {
|
|
975
|
+
const [scrollTop, setScrollTop] = useState3(0);
|
|
976
|
+
const [, bump] = useState3(0);
|
|
977
|
+
const termCols = process.stdout.columns || 80;
|
|
978
|
+
const termRows = process.stdout.rows || 24;
|
|
979
|
+
const viewportRows = Math.max(
|
|
980
|
+
4,
|
|
981
|
+
maxHeight != null ? Math.floor(maxHeight) : Math.max(8, termRows - 8)
|
|
982
|
+
);
|
|
983
|
+
const provisionalBar = showScrollbar ? 1 : 0;
|
|
984
|
+
const wrapCols = Math.max(8, termCols - provisionalBar);
|
|
985
|
+
const allLines = useMemo(() => {
|
|
986
|
+
if (Array.isArray(linesProp)) return linesProp.map((l) => String(l ?? ""));
|
|
987
|
+
return wrap ? wrapTextLines(text, wrapCols) : String(text ?? "").split("\n");
|
|
988
|
+
}, [linesProp, text, wrap, wrapCols]);
|
|
989
|
+
const needsBar = showScrollbar && allLines.length > viewportRows;
|
|
990
|
+
const textWidth = Math.max(8, termCols - (needsBar ? 1 : 0));
|
|
991
|
+
const maxScroll = Math.max(0, allLines.length - viewportRows);
|
|
992
|
+
const clamped = Math.min(Math.max(0, scrollTop), maxScroll);
|
|
993
|
+
const visible = allLines.slice(clamped, clamped + viewportRows);
|
|
994
|
+
const bar = needsBar ? scrollbarGlyphs(viewportRows, allLines.length, clamped) : null;
|
|
995
|
+
useEffect2(() => {
|
|
996
|
+
setScrollTop((s) => Math.min(s, maxScroll));
|
|
997
|
+
}, [maxScroll]);
|
|
998
|
+
const maxScrollRef = useRef2(maxScroll);
|
|
999
|
+
const pageSizeRef = useRef2(viewportRows);
|
|
1000
|
+
maxScrollRef.current = maxScroll;
|
|
1001
|
+
pageSizeRef.current = viewportRows;
|
|
1002
|
+
useEffect2(() => {
|
|
1003
|
+
if (!ctx || !bindKeys) return void 0;
|
|
1004
|
+
ctx.setKeyBinding(SCROLL_KEYS);
|
|
1005
|
+
ctx.setAction("scrollUp", () => {
|
|
1006
|
+
setScrollTop((s) => Math.max(0, s - 1));
|
|
1007
|
+
bump((n) => n + 1);
|
|
1008
|
+
ctx.update?.();
|
|
1009
|
+
});
|
|
1010
|
+
ctx.setAction("scrollDown", () => {
|
|
1011
|
+
setScrollTop((s) => Math.min(maxScrollRef.current, s + 1));
|
|
1012
|
+
bump((n) => n + 1);
|
|
1013
|
+
ctx.update?.();
|
|
1014
|
+
});
|
|
1015
|
+
ctx.setAction("pageUp", () => {
|
|
1016
|
+
setScrollTop((s) => Math.max(0, s - pageSizeRef.current));
|
|
1017
|
+
bump((n) => n + 1);
|
|
1018
|
+
ctx.update?.();
|
|
1019
|
+
});
|
|
1020
|
+
ctx.setAction("pageDown", () => {
|
|
1021
|
+
setScrollTop((s) => Math.min(maxScrollRef.current, s + pageSizeRef.current));
|
|
1022
|
+
bump((n) => n + 1);
|
|
1023
|
+
ctx.update?.();
|
|
1024
|
+
});
|
|
1025
|
+
return void 0;
|
|
1026
|
+
}, [ctx, bindKeys]);
|
|
1027
|
+
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" : "");
|
|
1028
|
+
const rowNodes = visible.map((line, i) => {
|
|
1029
|
+
const body = padEndVisible(line, textWidth);
|
|
1030
|
+
const glyph = bar ? bar[i] ?? "\u2502" : "";
|
|
1031
|
+
return h5(
|
|
1032
|
+
Box4,
|
|
1033
|
+
{ key: `L${clamped + i}`, flexDirection: "row" },
|
|
1034
|
+
h5(Text5, {}, body),
|
|
1035
|
+
glyph ? h5(Text5, { color: bar[i] === "\u2588" ? "cyan" : "gray" }, glyph) : null
|
|
1036
|
+
);
|
|
1037
|
+
});
|
|
1038
|
+
if (bar && visible.length < viewportRows) {
|
|
1039
|
+
for (let i = visible.length; i < viewportRows; i++) {
|
|
1040
|
+
rowNodes.push(
|
|
1041
|
+
h5(
|
|
1042
|
+
Box4,
|
|
1043
|
+
{ key: `pad${i}`, flexDirection: "row" },
|
|
1044
|
+
h5(Text5, {}, padEndVisible("", textWidth)),
|
|
1045
|
+
h5(Text5, { color: bar[i] === "\u2588" ? "cyan" : "gray" }, bar[i] ?? "\u2502")
|
|
1046
|
+
)
|
|
1047
|
+
);
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
return h5(
|
|
1051
|
+
Box4,
|
|
1052
|
+
{ flexDirection: "column" },
|
|
1053
|
+
header == null ? null : typeof header === "string" ? h5(Text5, { color: "gray" }, header) : header,
|
|
1054
|
+
showStatus ? h5(Text5, { color: "gray" }, status) : null,
|
|
1055
|
+
...rowNodes
|
|
1056
|
+
);
|
|
1057
|
+
}
|
|
1058
|
+
|
|
868
1059
|
// src/screen/utils.js
|
|
869
1060
|
function buildBreadcrumb(parts) {
|
|
870
1061
|
if (parts.length === 0) return "";
|
|
@@ -1000,7 +1191,7 @@ if (typeof window === "undefined") {
|
|
|
1000
1191
|
});
|
|
1001
1192
|
}
|
|
1002
1193
|
export {
|
|
1003
|
-
|
|
1194
|
+
Box5 as Box,
|
|
1004
1195
|
Divider,
|
|
1005
1196
|
FooterPresets,
|
|
1006
1197
|
GridCell,
|
|
@@ -1016,15 +1207,20 @@ export {
|
|
|
1016
1207
|
ScreenFooter,
|
|
1017
1208
|
ScreenRow,
|
|
1018
1209
|
ScreenTitle,
|
|
1019
|
-
|
|
1210
|
+
ScrollableText,
|
|
1211
|
+
Text6 as Text,
|
|
1020
1212
|
TextBlock,
|
|
1213
|
+
bindingIdentity,
|
|
1214
|
+
bindingMatchesInput,
|
|
1021
1215
|
buildBreadcrumb,
|
|
1022
1216
|
buildDetailBreadcrumb,
|
|
1023
1217
|
buildFooter,
|
|
1024
|
-
|
|
1218
|
+
formatBindingKey,
|
|
1219
|
+
createElement3 as h,
|
|
1025
1220
|
load,
|
|
1026
1221
|
memo,
|
|
1027
1222
|
organizeFooterMessages,
|
|
1223
|
+
scrollbarGlyphs,
|
|
1028
1224
|
showListScreen,
|
|
1029
1225
|
showMenuScreen,
|
|
1030
1226
|
showMultiColumnListScreen,
|
|
@@ -1032,11 +1228,12 @@ export {
|
|
|
1032
1228
|
showScreen,
|
|
1033
1229
|
showWordGridScreen,
|
|
1034
1230
|
useCallback,
|
|
1035
|
-
|
|
1231
|
+
useEffect3 as useEffect,
|
|
1036
1232
|
useInput2 as useInput,
|
|
1037
1233
|
useLayoutEffect,
|
|
1038
|
-
useMemo,
|
|
1039
|
-
|
|
1040
|
-
|
|
1234
|
+
useMemo2 as useMemo,
|
|
1235
|
+
useRef3 as useRef,
|
|
1236
|
+
useState4 as useState,
|
|
1237
|
+
wrapTextLines
|
|
1041
1238
|
};
|
|
1042
1239
|
//# sourceMappingURL=screen.js.map
|