@nmakarov/cli-toolkit 0.80.0 → 0.82.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 +132 -44
- package/dist/cli-runner.cjs.map +1 -1
- package/dist/cli-runner.js +134 -46
- package/dist/cli-runner.js.map +1 -1
- package/dist/index.cjs +140 -44
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +134 -46
- package/dist/index.js.map +1 -1
- package/dist/init.cjs +132 -44
- package/dist/init.cjs.map +1 -1
- package/dist/init.js +134 -46
- package/dist/init.js.map +1 -1
- package/dist/screen.cjs +128 -44
- package/dist/screen.cjs.map +1 -1
- package/dist/screen.js +120 -44
- package/dist/screen.js.map +1 -1
- package/package.json +1 -1
package/dist/init.js
CHANGED
|
@@ -1001,9 +1001,68 @@ var init_ui_elements = __esm({
|
|
|
1001
1001
|
}
|
|
1002
1002
|
});
|
|
1003
1003
|
|
|
1004
|
-
// src/screen/
|
|
1005
|
-
|
|
1006
|
-
|
|
1004
|
+
// src/screen/follow-scroll.js
|
|
1005
|
+
function clampScroll(scrollTop, maxScroll) {
|
|
1006
|
+
const max = Math.max(0, Number(maxScroll) || 0);
|
|
1007
|
+
const top = Number(scrollTop) || 0;
|
|
1008
|
+
return Math.min(Math.max(0, top), max);
|
|
1009
|
+
}
|
|
1010
|
+
function isScrolledToBottom(scrollTop, maxScroll) {
|
|
1011
|
+
return clampScroll(scrollTop, maxScroll) >= Math.max(0, Number(maxScroll) || 0);
|
|
1012
|
+
}
|
|
1013
|
+
function nextScrollAfterUserMove(scrollTop, maxScroll, delta) {
|
|
1014
|
+
const next = clampScroll((Number(scrollTop) || 0) + (Number(delta) || 0), maxScroll);
|
|
1015
|
+
return { scrollTop: next, following: isScrolledToBottom(next, maxScroll) };
|
|
1016
|
+
}
|
|
1017
|
+
function nextScrollAfterContentChange({ following, scrollTop, maxScroll }) {
|
|
1018
|
+
const max = Math.max(0, Number(maxScroll) || 0);
|
|
1019
|
+
if (following) return { scrollTop: max, following: true };
|
|
1020
|
+
const next = clampScroll(scrollTop, max);
|
|
1021
|
+
return { scrollTop: next, following: isScrolledToBottom(next, max) };
|
|
1022
|
+
}
|
|
1023
|
+
var init_follow_scroll = __esm({
|
|
1024
|
+
"src/screen/follow-scroll.js"() {
|
|
1025
|
+
}
|
|
1026
|
+
});
|
|
1027
|
+
|
|
1028
|
+
// src/screen/visible-text.js
|
|
1029
|
+
function stripAnsi(text) {
|
|
1030
|
+
return String(text ?? "").replace(ANSI_RE, "");
|
|
1031
|
+
}
|
|
1032
|
+
function visibleWidth(text) {
|
|
1033
|
+
return [...stripAnsi(text)].length;
|
|
1034
|
+
}
|
|
1035
|
+
function sliceVisible(text, cols) {
|
|
1036
|
+
const w = Math.max(0, Math.floor(Number(cols) || 0));
|
|
1037
|
+
const s = String(text ?? "");
|
|
1038
|
+
if (w === 0) return "";
|
|
1039
|
+
let vis = 0;
|
|
1040
|
+
let out = "";
|
|
1041
|
+
let i = 0;
|
|
1042
|
+
while (i < s.length && vis < w) {
|
|
1043
|
+
ANSI_RE.lastIndex = i;
|
|
1044
|
+
const m = ANSI_RE.exec(s);
|
|
1045
|
+
if (m && m.index === i) {
|
|
1046
|
+
out += m[0];
|
|
1047
|
+
i += m[0].length;
|
|
1048
|
+
continue;
|
|
1049
|
+
}
|
|
1050
|
+
const cp = s.codePointAt(i);
|
|
1051
|
+
const ch = String.fromCodePoint(cp);
|
|
1052
|
+
out += ch;
|
|
1053
|
+
i += ch.length;
|
|
1054
|
+
vis += 1;
|
|
1055
|
+
}
|
|
1056
|
+
return out;
|
|
1057
|
+
}
|
|
1058
|
+
function padEndVisible(text, cols) {
|
|
1059
|
+
const w = Math.max(0, Math.floor(Number(cols) || 0));
|
|
1060
|
+
const s = String(text ?? "");
|
|
1061
|
+
const vis = visibleWidth(s);
|
|
1062
|
+
if (vis === w) return s;
|
|
1063
|
+
if (vis > w) return sliceVisible(s, w);
|
|
1064
|
+
return s + " ".repeat(w - vis);
|
|
1065
|
+
}
|
|
1007
1066
|
function wrapTextLines(text, cols) {
|
|
1008
1067
|
const w = Math.max(1, Math.floor(Number(cols) || 1));
|
|
1009
1068
|
const out = [];
|
|
@@ -1012,20 +1071,30 @@ function wrapTextLines(text, cols) {
|
|
|
1012
1071
|
out.push("");
|
|
1013
1072
|
continue;
|
|
1014
1073
|
}
|
|
1074
|
+
if (visibleWidth(raw) <= w) {
|
|
1075
|
+
out.push(raw);
|
|
1076
|
+
continue;
|
|
1077
|
+
}
|
|
1015
1078
|
let rest = raw;
|
|
1016
|
-
while (rest
|
|
1017
|
-
|
|
1018
|
-
|
|
1079
|
+
while (visibleWidth(rest) > w) {
|
|
1080
|
+
const head = sliceVisible(rest, w);
|
|
1081
|
+
out.push(head);
|
|
1082
|
+
rest = rest.slice(head.length);
|
|
1019
1083
|
}
|
|
1020
1084
|
if (rest.length > 0) out.push(rest);
|
|
1021
1085
|
}
|
|
1022
1086
|
return out;
|
|
1023
1087
|
}
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
}
|
|
1088
|
+
var ANSI_RE;
|
|
1089
|
+
var init_visible_text = __esm({
|
|
1090
|
+
"src/screen/visible-text.js"() {
|
|
1091
|
+
ANSI_RE = /\u001b(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g;
|
|
1092
|
+
}
|
|
1093
|
+
});
|
|
1094
|
+
|
|
1095
|
+
// src/screen/scrollable-text.js
|
|
1096
|
+
import { useState as useState3, useEffect as useEffect2, useMemo, useRef as useRef2, createElement as createElement2 } from "react";
|
|
1097
|
+
import { Box as Box4, Text as Text5 } from "ink";
|
|
1029
1098
|
function ScrollableText({
|
|
1030
1099
|
ctx,
|
|
1031
1100
|
text = "",
|
|
@@ -1035,9 +1104,11 @@ function ScrollableText({
|
|
|
1035
1104
|
showScrollbar = true,
|
|
1036
1105
|
showStatus = true,
|
|
1037
1106
|
bindKeys = true,
|
|
1038
|
-
header = null
|
|
1107
|
+
header = null,
|
|
1108
|
+
followBottom = false
|
|
1039
1109
|
}) {
|
|
1040
1110
|
const [scrollTop, setScrollTop] = useState3(0);
|
|
1111
|
+
const [following, setFollowing] = useState3(() => !!followBottom);
|
|
1041
1112
|
const [, bump] = useState3(0);
|
|
1042
1113
|
const termRows = process.stdout.rows || 24;
|
|
1043
1114
|
const viewportRows = Math.max(
|
|
@@ -1056,48 +1127,52 @@ function ScrollableText({
|
|
|
1056
1127
|
const clamped = Math.min(Math.max(0, scrollTop), maxScroll);
|
|
1057
1128
|
const visible = allLines.slice(clamped, clamped + viewportRows);
|
|
1058
1129
|
const bar = needsBar ? scrollbarGlyphs(viewportRows, allLines.length, clamped) : null;
|
|
1059
|
-
useEffect2(() => {
|
|
1060
|
-
setScrollTop((s) => Math.min(s, maxScroll));
|
|
1061
|
-
}, [maxScroll]);
|
|
1062
1130
|
const maxScrollRef = useRef2(maxScroll);
|
|
1063
1131
|
const pageSizeRef = useRef2(viewportRows);
|
|
1132
|
+
const scrollTopRef = useRef2(scrollTop);
|
|
1133
|
+
const followingRef = useRef2(following);
|
|
1064
1134
|
maxScrollRef.current = maxScroll;
|
|
1065
1135
|
pageSizeRef.current = viewportRows;
|
|
1136
|
+
scrollTopRef.current = scrollTop;
|
|
1137
|
+
followingRef.current = following;
|
|
1138
|
+
useEffect2(() => {
|
|
1139
|
+
const next = nextScrollAfterContentChange({
|
|
1140
|
+
following: followBottom && followingRef.current,
|
|
1141
|
+
scrollTop: scrollTopRef.current,
|
|
1142
|
+
maxScroll
|
|
1143
|
+
});
|
|
1144
|
+
if (next.scrollTop !== scrollTopRef.current) setScrollTop(next.scrollTop);
|
|
1145
|
+
if (followBottom && next.following !== followingRef.current) setFollowing(next.following);
|
|
1146
|
+
}, [maxScroll, followBottom]);
|
|
1147
|
+
const applyUserScroll = (delta) => {
|
|
1148
|
+
const next = nextScrollAfterUserMove(scrollTopRef.current, maxScrollRef.current, delta);
|
|
1149
|
+
setScrollTop(next.scrollTop);
|
|
1150
|
+
if (followBottom) setFollowing(next.following);
|
|
1151
|
+
bump((n) => n + 1);
|
|
1152
|
+
ctx?.update?.();
|
|
1153
|
+
};
|
|
1066
1154
|
useEffect2(() => {
|
|
1067
1155
|
if (!ctx || !bindKeys) return void 0;
|
|
1068
1156
|
ctx.setKeyBinding(SCROLL_KEYS);
|
|
1069
|
-
ctx.setAction("scrollUp", () =>
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
});
|
|
1074
|
-
ctx.setAction("scrollDown", () => {
|
|
1075
|
-
setScrollTop((s) => Math.min(maxScrollRef.current, s + 1));
|
|
1076
|
-
bump((n) => n + 1);
|
|
1077
|
-
ctx.update?.();
|
|
1078
|
-
});
|
|
1079
|
-
ctx.setAction("pageUp", () => {
|
|
1080
|
-
setScrollTop((s) => Math.max(0, s - pageSizeRef.current));
|
|
1081
|
-
bump((n) => n + 1);
|
|
1082
|
-
ctx.update?.();
|
|
1083
|
-
});
|
|
1084
|
-
ctx.setAction("pageDown", () => {
|
|
1085
|
-
setScrollTop((s) => Math.min(maxScrollRef.current, s + pageSizeRef.current));
|
|
1086
|
-
bump((n) => n + 1);
|
|
1087
|
-
ctx.update?.();
|
|
1088
|
-
});
|
|
1157
|
+
ctx.setAction("scrollUp", () => applyUserScroll(-1));
|
|
1158
|
+
ctx.setAction("scrollDown", () => applyUserScroll(1));
|
|
1159
|
+
ctx.setAction("pageUp", () => applyUserScroll(-pageSizeRef.current));
|
|
1160
|
+
ctx.setAction("pageDown", () => applyUserScroll(pageSizeRef.current));
|
|
1089
1161
|
return void 0;
|
|
1090
|
-
}, [ctx, bindKeys]);
|
|
1091
|
-
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" : "");
|
|
1162
|
+
}, [ctx, bindKeys, followBottom]);
|
|
1163
|
+
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" : "") + (followBottom && following ? " \xB7 follow" : followBottom ? " \xB7 follow off" : "");
|
|
1092
1164
|
const rowNodes = visible.map((line, i) => {
|
|
1093
|
-
const body = padEndVisible(line, textWidth);
|
|
1094
1165
|
const glyph = bar ? bar[i] ?? BAR_TRACK : showScrollbar ? " " : "";
|
|
1095
1166
|
const isThumb = glyph === BAR_THUMB;
|
|
1096
1167
|
return h5(
|
|
1097
|
-
|
|
1098
|
-
{ key: `L${clamped + i}
|
|
1099
|
-
|
|
1100
|
-
|
|
1168
|
+
Box4,
|
|
1169
|
+
{ key: `L${clamped + i}`, flexDirection: "row", width: contentCols, flexShrink: 0 },
|
|
1170
|
+
h5(
|
|
1171
|
+
Box4,
|
|
1172
|
+
{ width: textWidth, flexShrink: 0 },
|
|
1173
|
+
h5(Text5, { wrap: "truncate" }, padEndVisible(line, textWidth))
|
|
1174
|
+
),
|
|
1175
|
+
showScrollbar ? h5(Box4, { width: 1, flexShrink: 0 }, h5(Text5, { color: isThumb ? "cyan" : "gray" }, glyph)) : null
|
|
1101
1176
|
);
|
|
1102
1177
|
});
|
|
1103
1178
|
if (bar && visible.length < viewportRows) {
|
|
@@ -1105,10 +1180,10 @@ function ScrollableText({
|
|
|
1105
1180
|
const glyph = bar[i] ?? BAR_TRACK;
|
|
1106
1181
|
rowNodes.push(
|
|
1107
1182
|
h5(
|
|
1108
|
-
|
|
1109
|
-
{ key: `pad${i}
|
|
1110
|
-
padEndVisible("", textWidth),
|
|
1111
|
-
h5(Text5, { color: glyph === BAR_THUMB ? "cyan" : "gray" }, glyph)
|
|
1183
|
+
Box4,
|
|
1184
|
+
{ key: `pad${i}`, flexDirection: "row", width: contentCols, flexShrink: 0 },
|
|
1185
|
+
h5(Box4, { width: textWidth, flexShrink: 0 }, h5(Text5, {}, padEndVisible("", textWidth))),
|
|
1186
|
+
h5(Box4, { width: 1, flexShrink: 0 }, h5(Text5, { color: glyph === BAR_THUMB ? "cyan" : "gray" }, glyph))
|
|
1112
1187
|
)
|
|
1113
1188
|
);
|
|
1114
1189
|
}
|
|
@@ -1126,7 +1201,10 @@ var init_scrollable_text = __esm({
|
|
|
1126
1201
|
"src/screen/scrollable-text.js"() {
|
|
1127
1202
|
init_components();
|
|
1128
1203
|
init_scrollbar();
|
|
1204
|
+
init_follow_scroll();
|
|
1205
|
+
init_visible_text();
|
|
1129
1206
|
init_scrollbar();
|
|
1207
|
+
init_visible_text();
|
|
1130
1208
|
h5 = createElement2;
|
|
1131
1209
|
SCROLL_KEYS = [
|
|
1132
1210
|
{ key: "upArrow", caption: "scroll", action: "scrollUp", order: 0 },
|
|
@@ -1294,11 +1372,16 @@ __export(screen_exports, {
|
|
|
1294
1372
|
buildBreadcrumb: () => buildBreadcrumb,
|
|
1295
1373
|
buildDetailBreadcrumb: () => buildDetailBreadcrumb,
|
|
1296
1374
|
buildFooter: () => buildFooter,
|
|
1375
|
+
clampScroll: () => clampScroll,
|
|
1297
1376
|
formatBindingKey: () => formatBindingKey,
|
|
1298
1377
|
h: () => createElement3,
|
|
1378
|
+
isScrolledToBottom: () => isScrolledToBottom,
|
|
1299
1379
|
load: () => load,
|
|
1300
1380
|
memo: () => memo,
|
|
1381
|
+
nextScrollAfterContentChange: () => nextScrollAfterContentChange,
|
|
1382
|
+
nextScrollAfterUserMove: () => nextScrollAfterUserMove,
|
|
1301
1383
|
organizeFooterMessages: () => organizeFooterMessages,
|
|
1384
|
+
padEndVisible: () => padEndVisible,
|
|
1302
1385
|
scrollbarGlyphs: () => scrollbarGlyphs,
|
|
1303
1386
|
showListScreen: () => showListScreen,
|
|
1304
1387
|
showMenuScreen: () => showMenuScreen,
|
|
@@ -1306,6 +1389,8 @@ __export(screen_exports, {
|
|
|
1306
1389
|
showMultiColumnListWithPreviewScreen: () => showMultiColumnListWithPreviewScreen,
|
|
1307
1390
|
showScreen: () => showScreen,
|
|
1308
1391
|
showWordGridScreen: () => showWordGridScreen,
|
|
1392
|
+
sliceVisible: () => sliceVisible,
|
|
1393
|
+
stripAnsi: () => stripAnsi,
|
|
1309
1394
|
useCallback: () => useCallback,
|
|
1310
1395
|
useEffect: () => useEffect3,
|
|
1311
1396
|
useInput: () => useInput2,
|
|
@@ -1313,6 +1398,7 @@ __export(screen_exports, {
|
|
|
1313
1398
|
useMemo: () => useMemo2,
|
|
1314
1399
|
useRef: () => useRef3,
|
|
1315
1400
|
useState: () => useState4,
|
|
1401
|
+
visibleWidth: () => visibleWidth,
|
|
1316
1402
|
wrapTextLines: () => wrapTextLines
|
|
1317
1403
|
});
|
|
1318
1404
|
import React2, { useState as useState4, useEffect as useEffect3, useLayoutEffect, useRef as useRef3, useMemo as useMemo2, useCallback, memo, createElement as createElement3 } from "react";
|
|
@@ -1334,6 +1420,8 @@ var init_screen = __esm({
|
|
|
1334
1420
|
init_components();
|
|
1335
1421
|
init_ui_elements();
|
|
1336
1422
|
init_scrollable_text();
|
|
1423
|
+
init_visible_text();
|
|
1424
|
+
init_follow_scroll();
|
|
1337
1425
|
init_scrollbar();
|
|
1338
1426
|
init_key_bindings();
|
|
1339
1427
|
init_utils();
|