@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/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,155 @@ 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
|
+
var BAR_THUMB = "#";
|
|
918
|
+
var BAR_TRACK = "|";
|
|
919
|
+
function wrapTextLines(text, cols) {
|
|
920
|
+
const w = Math.max(1, Math.floor(Number(cols) || 1));
|
|
921
|
+
const out = [];
|
|
922
|
+
for (const raw of String(text ?? "").split("\n")) {
|
|
923
|
+
if (raw.length === 0) {
|
|
924
|
+
out.push("");
|
|
925
|
+
continue;
|
|
926
|
+
}
|
|
927
|
+
let rest = raw;
|
|
928
|
+
while (rest.length > w) {
|
|
929
|
+
out.push(rest.slice(0, w));
|
|
930
|
+
rest = rest.slice(w);
|
|
931
|
+
}
|
|
932
|
+
if (rest.length > 0) out.push(rest);
|
|
933
|
+
}
|
|
934
|
+
return out;
|
|
935
|
+
}
|
|
936
|
+
function scrollbarGlyphs(viewportRows, totalLines, scrollTop) {
|
|
937
|
+
const view = Math.max(1, Math.floor(viewportRows));
|
|
938
|
+
const total = Math.max(0, Math.floor(totalLines));
|
|
939
|
+
if (total <= view) return null;
|
|
940
|
+
const maxScroll = total - view;
|
|
941
|
+
const thumbSize = Math.max(1, Math.round(view / total * view));
|
|
942
|
+
const travel = Math.max(0, view - thumbSize);
|
|
943
|
+
const s = Math.min(Math.max(0, Math.floor(scrollTop)), maxScroll);
|
|
944
|
+
const thumbStart = maxScroll === 0 ? 0 : Math.round(s / maxScroll * travel);
|
|
945
|
+
const glyphs = [];
|
|
946
|
+
for (let i = 0; i < view; i++) {
|
|
947
|
+
glyphs.push(i >= thumbStart && i < thumbStart + thumbSize ? BAR_THUMB : BAR_TRACK);
|
|
948
|
+
}
|
|
949
|
+
return glyphs;
|
|
950
|
+
}
|
|
951
|
+
function padEndVisible(s, w) {
|
|
952
|
+
const t = String(s ?? "");
|
|
953
|
+
if (t.length >= w) return t.slice(0, w);
|
|
954
|
+
return t + " ".repeat(w - t.length);
|
|
955
|
+
}
|
|
956
|
+
var SCROLL_KEYS = [
|
|
957
|
+
{ key: "upArrow", caption: "scroll", action: "scrollUp", order: 0 },
|
|
958
|
+
{ key: "downArrow", caption: "scroll", action: "scrollDown", order: 0 },
|
|
959
|
+
{ key: "upArrow", meta: true, caption: "page", action: "pageUp", order: 0 },
|
|
960
|
+
{ key: "downArrow", meta: true, caption: "page", action: "pageDown", order: 0 },
|
|
961
|
+
{ key: "upArrow", ctrl: true, caption: "page", action: "pageUp", order: 0 },
|
|
962
|
+
{ key: "downArrow", ctrl: true, caption: "page", action: "pageDown", order: 0 },
|
|
963
|
+
{ key: "pageUp", caption: "page", action: "pageUp", order: 0 },
|
|
964
|
+
{ key: "pageDown", caption: "page", action: "pageDown", order: 0 }
|
|
965
|
+
];
|
|
966
|
+
function ScrollableText({
|
|
967
|
+
ctx,
|
|
968
|
+
text = "",
|
|
969
|
+
lines: linesProp,
|
|
970
|
+
maxHeight,
|
|
971
|
+
wrap = true,
|
|
972
|
+
showScrollbar = true,
|
|
973
|
+
showStatus = true,
|
|
974
|
+
bindKeys = true,
|
|
975
|
+
header = null
|
|
976
|
+
}) {
|
|
977
|
+
const [scrollTop, setScrollTop] = useState3(0);
|
|
978
|
+
const [, bump] = useState3(0);
|
|
979
|
+
const termRows = process.stdout.rows || 24;
|
|
980
|
+
const viewportRows = Math.max(
|
|
981
|
+
4,
|
|
982
|
+
maxHeight != null ? Math.floor(maxHeight) : Math.max(8, termRows - 8)
|
|
983
|
+
);
|
|
984
|
+
const contentCols = Math.max(20, getScreenWidth() - 4);
|
|
985
|
+
const barCols = showScrollbar ? 1 : 0;
|
|
986
|
+
const textWidth = Math.max(8, contentCols - barCols);
|
|
987
|
+
const allLines = useMemo(() => {
|
|
988
|
+
if (Array.isArray(linesProp)) return linesProp.map((l) => String(l ?? ""));
|
|
989
|
+
return wrap ? wrapTextLines(text, textWidth) : String(text ?? "").split("\n");
|
|
990
|
+
}, [linesProp, text, wrap, textWidth]);
|
|
991
|
+
const needsBar = showScrollbar && allLines.length > viewportRows;
|
|
992
|
+
const maxScroll = Math.max(0, allLines.length - viewportRows);
|
|
993
|
+
const clamped = Math.min(Math.max(0, scrollTop), maxScroll);
|
|
994
|
+
const visible = allLines.slice(clamped, clamped + viewportRows);
|
|
995
|
+
const bar = needsBar ? scrollbarGlyphs(viewportRows, allLines.length, clamped) : null;
|
|
996
|
+
useEffect2(() => {
|
|
997
|
+
setScrollTop((s) => Math.min(s, maxScroll));
|
|
998
|
+
}, [maxScroll]);
|
|
999
|
+
const maxScrollRef = useRef2(maxScroll);
|
|
1000
|
+
const pageSizeRef = useRef2(viewportRows);
|
|
1001
|
+
maxScrollRef.current = maxScroll;
|
|
1002
|
+
pageSizeRef.current = viewportRows;
|
|
1003
|
+
useEffect2(() => {
|
|
1004
|
+
if (!ctx || !bindKeys) return void 0;
|
|
1005
|
+
ctx.setKeyBinding(SCROLL_KEYS);
|
|
1006
|
+
ctx.setAction("scrollUp", () => {
|
|
1007
|
+
setScrollTop((s) => Math.max(0, s - 1));
|
|
1008
|
+
bump((n) => n + 1);
|
|
1009
|
+
ctx.update?.();
|
|
1010
|
+
});
|
|
1011
|
+
ctx.setAction("scrollDown", () => {
|
|
1012
|
+
setScrollTop((s) => Math.min(maxScrollRef.current, s + 1));
|
|
1013
|
+
bump((n) => n + 1);
|
|
1014
|
+
ctx.update?.();
|
|
1015
|
+
});
|
|
1016
|
+
ctx.setAction("pageUp", () => {
|
|
1017
|
+
setScrollTop((s) => Math.max(0, s - pageSizeRef.current));
|
|
1018
|
+
bump((n) => n + 1);
|
|
1019
|
+
ctx.update?.();
|
|
1020
|
+
});
|
|
1021
|
+
ctx.setAction("pageDown", () => {
|
|
1022
|
+
setScrollTop((s) => Math.min(maxScrollRef.current, s + pageSizeRef.current));
|
|
1023
|
+
bump((n) => n + 1);
|
|
1024
|
+
ctx.update?.();
|
|
1025
|
+
});
|
|
1026
|
+
return void 0;
|
|
1027
|
+
}, [ctx, bindKeys]);
|
|
1028
|
+
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" : "");
|
|
1029
|
+
const rowNodes = visible.map((line, i) => {
|
|
1030
|
+
const body = padEndVisible(line, textWidth);
|
|
1031
|
+
const glyph = bar ? bar[i] ?? BAR_TRACK : showScrollbar ? " " : "";
|
|
1032
|
+
const isThumb = glyph === BAR_THUMB;
|
|
1033
|
+
return h5(
|
|
1034
|
+
Text5,
|
|
1035
|
+
{ key: `L${clamped + i}` },
|
|
1036
|
+
body,
|
|
1037
|
+
glyph ? h5(Text5, { color: isThumb ? "cyan" : "gray" }, glyph) : null
|
|
1038
|
+
);
|
|
1039
|
+
});
|
|
1040
|
+
if (bar && visible.length < viewportRows) {
|
|
1041
|
+
for (let i = visible.length; i < viewportRows; i++) {
|
|
1042
|
+
const glyph = bar[i] ?? BAR_TRACK;
|
|
1043
|
+
rowNodes.push(
|
|
1044
|
+
h5(
|
|
1045
|
+
Text5,
|
|
1046
|
+
{ key: `pad${i}` },
|
|
1047
|
+
padEndVisible("", textWidth),
|
|
1048
|
+
h5(Text5, { color: glyph === BAR_THUMB ? "cyan" : "gray" }, glyph)
|
|
1049
|
+
)
|
|
1050
|
+
);
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
return h5(
|
|
1054
|
+
Box4,
|
|
1055
|
+
{ flexDirection: "column" },
|
|
1056
|
+
header == null ? null : typeof header === "string" ? h5(Text5, { color: "gray" }, header) : header,
|
|
1057
|
+
showStatus ? h5(Text5, { color: "gray" }, status) : null,
|
|
1058
|
+
...rowNodes
|
|
1059
|
+
);
|
|
1060
|
+
}
|
|
1061
|
+
|
|
868
1062
|
// src/screen/utils.js
|
|
869
1063
|
function buildBreadcrumb(parts) {
|
|
870
1064
|
if (parts.length === 0) return "";
|
|
@@ -1000,7 +1194,7 @@ if (typeof window === "undefined") {
|
|
|
1000
1194
|
});
|
|
1001
1195
|
}
|
|
1002
1196
|
export {
|
|
1003
|
-
|
|
1197
|
+
Box5 as Box,
|
|
1004
1198
|
Divider,
|
|
1005
1199
|
FooterPresets,
|
|
1006
1200
|
GridCell,
|
|
@@ -1016,15 +1210,20 @@ export {
|
|
|
1016
1210
|
ScreenFooter,
|
|
1017
1211
|
ScreenRow,
|
|
1018
1212
|
ScreenTitle,
|
|
1019
|
-
|
|
1213
|
+
ScrollableText,
|
|
1214
|
+
Text6 as Text,
|
|
1020
1215
|
TextBlock,
|
|
1216
|
+
bindingIdentity,
|
|
1217
|
+
bindingMatchesInput,
|
|
1021
1218
|
buildBreadcrumb,
|
|
1022
1219
|
buildDetailBreadcrumb,
|
|
1023
1220
|
buildFooter,
|
|
1024
|
-
|
|
1221
|
+
formatBindingKey,
|
|
1222
|
+
createElement3 as h,
|
|
1025
1223
|
load,
|
|
1026
1224
|
memo,
|
|
1027
1225
|
organizeFooterMessages,
|
|
1226
|
+
scrollbarGlyphs,
|
|
1028
1227
|
showListScreen,
|
|
1029
1228
|
showMenuScreen,
|
|
1030
1229
|
showMultiColumnListScreen,
|
|
@@ -1032,11 +1231,12 @@ export {
|
|
|
1032
1231
|
showScreen,
|
|
1033
1232
|
showWordGridScreen,
|
|
1034
1233
|
useCallback,
|
|
1035
|
-
|
|
1234
|
+
useEffect3 as useEffect,
|
|
1036
1235
|
useInput2 as useInput,
|
|
1037
1236
|
useLayoutEffect,
|
|
1038
|
-
useMemo,
|
|
1039
|
-
|
|
1040
|
-
|
|
1237
|
+
useMemo2 as useMemo,
|
|
1238
|
+
useRef3 as useRef,
|
|
1239
|
+
useState4 as useState,
|
|
1240
|
+
wrapTextLines
|
|
1041
1241
|
};
|
|
1042
1242
|
//# sourceMappingURL=screen.js.map
|