@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/screen.cjs
CHANGED
|
@@ -56,11 +56,16 @@ __export(screen_exports, {
|
|
|
56
56
|
buildBreadcrumb: () => buildBreadcrumb,
|
|
57
57
|
buildDetailBreadcrumb: () => buildDetailBreadcrumb,
|
|
58
58
|
buildFooter: () => buildFooter,
|
|
59
|
+
clampScroll: () => clampScroll,
|
|
59
60
|
formatBindingKey: () => formatBindingKey,
|
|
60
61
|
h: () => import_react6.createElement,
|
|
62
|
+
isScrolledToBottom: () => isScrolledToBottom,
|
|
61
63
|
load: () => load,
|
|
62
64
|
memo: () => import_react6.memo,
|
|
65
|
+
nextScrollAfterContentChange: () => nextScrollAfterContentChange,
|
|
66
|
+
nextScrollAfterUserMove: () => nextScrollAfterUserMove,
|
|
63
67
|
organizeFooterMessages: () => organizeFooterMessages,
|
|
68
|
+
padEndVisible: () => padEndVisible,
|
|
64
69
|
scrollbarGlyphs: () => scrollbarGlyphs,
|
|
65
70
|
showListScreen: () => showListScreen,
|
|
66
71
|
showMenuScreen: () => showMenuScreen,
|
|
@@ -68,6 +73,8 @@ __export(screen_exports, {
|
|
|
68
73
|
showMultiColumnListWithPreviewScreen: () => showMultiColumnListWithPreviewScreen,
|
|
69
74
|
showScreen: () => showScreen,
|
|
70
75
|
showWordGridScreen: () => showWordGridScreen,
|
|
76
|
+
sliceVisible: () => sliceVisible,
|
|
77
|
+
stripAnsi: () => stripAnsi,
|
|
71
78
|
useCallback: () => import_react6.useCallback,
|
|
72
79
|
useEffect: () => import_react6.useEffect,
|
|
73
80
|
useInput: () => import_ink6.useInput,
|
|
@@ -75,6 +82,7 @@ __export(screen_exports, {
|
|
|
75
82
|
useMemo: () => import_react6.useMemo,
|
|
76
83
|
useRef: () => import_react6.useRef,
|
|
77
84
|
useState: () => import_react6.useState,
|
|
85
|
+
visibleWidth: () => visibleWidth,
|
|
78
86
|
wrapTextLines: () => wrapTextLines
|
|
79
87
|
});
|
|
80
88
|
module.exports = __toCommonJS(screen_exports);
|
|
@@ -1042,7 +1050,66 @@ function InputField({ prompt, value, onChange: _onChange, onSubmit: _onSubmit })
|
|
|
1042
1050
|
// src/screen/scrollable-text.js
|
|
1043
1051
|
var import_react5 = null; // Will be set by load();
|
|
1044
1052
|
var import_ink5 = null; // Will be set by load();
|
|
1045
|
-
|
|
1053
|
+
|
|
1054
|
+
// src/screen/follow-scroll.js
|
|
1055
|
+
function clampScroll(scrollTop, maxScroll) {
|
|
1056
|
+
const max = Math.max(0, Number(maxScroll) || 0);
|
|
1057
|
+
const top = Number(scrollTop) || 0;
|
|
1058
|
+
return Math.min(Math.max(0, top), max);
|
|
1059
|
+
}
|
|
1060
|
+
function isScrolledToBottom(scrollTop, maxScroll) {
|
|
1061
|
+
return clampScroll(scrollTop, maxScroll) >= Math.max(0, Number(maxScroll) || 0);
|
|
1062
|
+
}
|
|
1063
|
+
function nextScrollAfterUserMove(scrollTop, maxScroll, delta) {
|
|
1064
|
+
const next = clampScroll((Number(scrollTop) || 0) + (Number(delta) || 0), maxScroll);
|
|
1065
|
+
return { scrollTop: next, following: isScrolledToBottom(next, maxScroll) };
|
|
1066
|
+
}
|
|
1067
|
+
function nextScrollAfterContentChange({ following, scrollTop, maxScroll }) {
|
|
1068
|
+
const max = Math.max(0, Number(maxScroll) || 0);
|
|
1069
|
+
if (following) return { scrollTop: max, following: true };
|
|
1070
|
+
const next = clampScroll(scrollTop, max);
|
|
1071
|
+
return { scrollTop: next, following: isScrolledToBottom(next, max) };
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
// src/screen/visible-text.js
|
|
1075
|
+
var ANSI_RE = /\u001b(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g;
|
|
1076
|
+
function stripAnsi(text) {
|
|
1077
|
+
return String(text ?? "").replace(ANSI_RE, "");
|
|
1078
|
+
}
|
|
1079
|
+
function visibleWidth(text) {
|
|
1080
|
+
return [...stripAnsi(text)].length;
|
|
1081
|
+
}
|
|
1082
|
+
function sliceVisible(text, cols) {
|
|
1083
|
+
const w = Math.max(0, Math.floor(Number(cols) || 0));
|
|
1084
|
+
const s = String(text ?? "");
|
|
1085
|
+
if (w === 0) return "";
|
|
1086
|
+
let vis = 0;
|
|
1087
|
+
let out = "";
|
|
1088
|
+
let i = 0;
|
|
1089
|
+
while (i < s.length && vis < w) {
|
|
1090
|
+
ANSI_RE.lastIndex = i;
|
|
1091
|
+
const m = ANSI_RE.exec(s);
|
|
1092
|
+
if (m && m.index === i) {
|
|
1093
|
+
out += m[0];
|
|
1094
|
+
i += m[0].length;
|
|
1095
|
+
continue;
|
|
1096
|
+
}
|
|
1097
|
+
const cp = s.codePointAt(i);
|
|
1098
|
+
const ch = String.fromCodePoint(cp);
|
|
1099
|
+
out += ch;
|
|
1100
|
+
i += ch.length;
|
|
1101
|
+
vis += 1;
|
|
1102
|
+
}
|
|
1103
|
+
return out;
|
|
1104
|
+
}
|
|
1105
|
+
function padEndVisible(text, cols) {
|
|
1106
|
+
const w = Math.max(0, Math.floor(Number(cols) || 0));
|
|
1107
|
+
const s = String(text ?? "");
|
|
1108
|
+
const vis = visibleWidth(s);
|
|
1109
|
+
if (vis === w) return s;
|
|
1110
|
+
if (vis > w) return sliceVisible(s, w);
|
|
1111
|
+
return s + " ".repeat(w - vis);
|
|
1112
|
+
}
|
|
1046
1113
|
function wrapTextLines(text, cols) {
|
|
1047
1114
|
const w = Math.max(1, Math.floor(Number(cols) || 1));
|
|
1048
1115
|
const out = [];
|
|
@@ -1051,20 +1118,23 @@ function wrapTextLines(text, cols) {
|
|
|
1051
1118
|
out.push("");
|
|
1052
1119
|
continue;
|
|
1053
1120
|
}
|
|
1121
|
+
if (visibleWidth(raw) <= w) {
|
|
1122
|
+
out.push(raw);
|
|
1123
|
+
continue;
|
|
1124
|
+
}
|
|
1054
1125
|
let rest = raw;
|
|
1055
|
-
while (rest
|
|
1056
|
-
|
|
1057
|
-
|
|
1126
|
+
while (visibleWidth(rest) > w) {
|
|
1127
|
+
const head = sliceVisible(rest, w);
|
|
1128
|
+
out.push(head);
|
|
1129
|
+
rest = rest.slice(head.length);
|
|
1058
1130
|
}
|
|
1059
1131
|
if (rest.length > 0) out.push(rest);
|
|
1060
1132
|
}
|
|
1061
1133
|
return out;
|
|
1062
1134
|
}
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
return t + " ".repeat(w - t.length);
|
|
1067
|
-
}
|
|
1135
|
+
|
|
1136
|
+
// src/screen/scrollable-text.js
|
|
1137
|
+
// Removed: intermediate assignment - code will access import variable directly
|
|
1068
1138
|
var SCROLL_KEYS = [
|
|
1069
1139
|
{ key: "upArrow", caption: "scroll", action: "scrollUp", order: 0 },
|
|
1070
1140
|
{ key: "downArrow", caption: "scroll", action: "scrollDown", order: 0 },
|
|
@@ -1079,9 +1149,11 @@ function ScrollableText({
|
|
|
1079
1149
|
showScrollbar = true,
|
|
1080
1150
|
showStatus = true,
|
|
1081
1151
|
bindKeys = true,
|
|
1082
|
-
header = null
|
|
1152
|
+
header = null,
|
|
1153
|
+
followBottom = false
|
|
1083
1154
|
}) {
|
|
1084
1155
|
const [scrollTop, setScrollTop] = (0, import_react5.useState)(0);
|
|
1156
|
+
const [following, setFollowing] = (0, import_react5.useState)(() => !!followBottom);
|
|
1085
1157
|
const [, bump] = (0, import_react5.useState)(0);
|
|
1086
1158
|
const termRows = process.stdout.rows || 24;
|
|
1087
1159
|
const viewportRows = Math.max(
|
|
@@ -1100,48 +1172,52 @@ function ScrollableText({
|
|
|
1100
1172
|
const clamped = Math.min(Math.max(0, scrollTop), maxScroll);
|
|
1101
1173
|
const visible = allLines.slice(clamped, clamped + viewportRows);
|
|
1102
1174
|
const bar = needsBar ? scrollbarGlyphs(viewportRows, allLines.length, clamped) : null;
|
|
1103
|
-
(0, import_react5.useEffect)(() => {
|
|
1104
|
-
setScrollTop((s) => Math.min(s, maxScroll));
|
|
1105
|
-
}, [maxScroll]);
|
|
1106
1175
|
const maxScrollRef = (0, import_react5.useRef)(maxScroll);
|
|
1107
1176
|
const pageSizeRef = (0, import_react5.useRef)(viewportRows);
|
|
1177
|
+
const scrollTopRef = (0, import_react5.useRef)(scrollTop);
|
|
1178
|
+
const followingRef = (0, import_react5.useRef)(following);
|
|
1108
1179
|
maxScrollRef.current = maxScroll;
|
|
1109
1180
|
pageSizeRef.current = viewportRows;
|
|
1181
|
+
scrollTopRef.current = scrollTop;
|
|
1182
|
+
followingRef.current = following;
|
|
1183
|
+
(0, import_react5.useEffect)(() => {
|
|
1184
|
+
const next = nextScrollAfterContentChange({
|
|
1185
|
+
following: followBottom && followingRef.current,
|
|
1186
|
+
scrollTop: scrollTopRef.current,
|
|
1187
|
+
maxScroll
|
|
1188
|
+
});
|
|
1189
|
+
if (next.scrollTop !== scrollTopRef.current) setScrollTop(next.scrollTop);
|
|
1190
|
+
if (followBottom && next.following !== followingRef.current) setFollowing(next.following);
|
|
1191
|
+
}, [maxScroll, followBottom]);
|
|
1192
|
+
const applyUserScroll = (delta) => {
|
|
1193
|
+
const next = nextScrollAfterUserMove(scrollTopRef.current, maxScrollRef.current, delta);
|
|
1194
|
+
setScrollTop(next.scrollTop);
|
|
1195
|
+
if (followBottom) setFollowing(next.following);
|
|
1196
|
+
bump((n) => n + 1);
|
|
1197
|
+
ctx?.update?.();
|
|
1198
|
+
};
|
|
1110
1199
|
(0, import_react5.useEffect)(() => {
|
|
1111
1200
|
if (!ctx || !bindKeys) return void 0;
|
|
1112
1201
|
ctx.setKeyBinding(SCROLL_KEYS);
|
|
1113
|
-
ctx.setAction("scrollUp", () =>
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
});
|
|
1118
|
-
ctx.setAction("scrollDown", () => {
|
|
1119
|
-
setScrollTop((s) => Math.min(maxScrollRef.current, s + 1));
|
|
1120
|
-
bump((n) => n + 1);
|
|
1121
|
-
ctx.update?.();
|
|
1122
|
-
});
|
|
1123
|
-
ctx.setAction("pageUp", () => {
|
|
1124
|
-
setScrollTop((s) => Math.max(0, s - pageSizeRef.current));
|
|
1125
|
-
bump((n) => n + 1);
|
|
1126
|
-
ctx.update?.();
|
|
1127
|
-
});
|
|
1128
|
-
ctx.setAction("pageDown", () => {
|
|
1129
|
-
setScrollTop((s) => Math.min(maxScrollRef.current, s + pageSizeRef.current));
|
|
1130
|
-
bump((n) => n + 1);
|
|
1131
|
-
ctx.update?.();
|
|
1132
|
-
});
|
|
1202
|
+
ctx.setAction("scrollUp", () => applyUserScroll(-1));
|
|
1203
|
+
ctx.setAction("scrollDown", () => applyUserScroll(1));
|
|
1204
|
+
ctx.setAction("pageUp", () => applyUserScroll(-pageSizeRef.current));
|
|
1205
|
+
ctx.setAction("pageDown", () => applyUserScroll(pageSizeRef.current));
|
|
1133
1206
|
return void 0;
|
|
1134
|
-
}, [ctx, bindKeys]);
|
|
1135
|
-
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" : "");
|
|
1207
|
+
}, [ctx, bindKeys, followBottom]);
|
|
1208
|
+
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" : "");
|
|
1136
1209
|
const rowNodes = visible.map((line, i) => {
|
|
1137
|
-
const body = padEndVisible(line, textWidth);
|
|
1138
1210
|
const glyph = bar ? bar[i] ?? BAR_TRACK : showScrollbar ? " " : "";
|
|
1139
1211
|
const isThumb = glyph === BAR_THUMB;
|
|
1140
1212
|
return h5(
|
|
1141
|
-
import_ink5.
|
|
1142
|
-
{ key: `L${clamped + i}
|
|
1143
|
-
|
|
1144
|
-
|
|
1213
|
+
import_ink5.Box,
|
|
1214
|
+
{ key: `L${clamped + i}`, flexDirection: "row", width: contentCols, flexShrink: 0 },
|
|
1215
|
+
h5(
|
|
1216
|
+
import_ink5.Box,
|
|
1217
|
+
{ width: textWidth, flexShrink: 0 },
|
|
1218
|
+
h5(import_ink5.Text, { wrap: "truncate" }, padEndVisible(line, textWidth))
|
|
1219
|
+
),
|
|
1220
|
+
showScrollbar ? h5(import_ink5.Box, { width: 1, flexShrink: 0 }, h5(import_ink5.Text, { color: isThumb ? "cyan" : "gray" }, glyph)) : null
|
|
1145
1221
|
);
|
|
1146
1222
|
});
|
|
1147
1223
|
if (bar && visible.length < viewportRows) {
|
|
@@ -1149,10 +1225,10 @@ function ScrollableText({
|
|
|
1149
1225
|
const glyph = bar[i] ?? BAR_TRACK;
|
|
1150
1226
|
rowNodes.push(
|
|
1151
1227
|
h5(
|
|
1152
|
-
import_ink5.
|
|
1153
|
-
{ key: `pad${i}
|
|
1154
|
-
padEndVisible("", textWidth),
|
|
1155
|
-
h5(import_ink5.Text, { color: glyph === BAR_THUMB ? "cyan" : "gray" }, glyph)
|
|
1228
|
+
import_ink5.Box,
|
|
1229
|
+
{ key: `pad${i}`, flexDirection: "row", width: contentCols, flexShrink: 0 },
|
|
1230
|
+
h5(import_ink5.Box, { width: textWidth, flexShrink: 0 }, h5(import_ink5.Text, {}, padEndVisible("", textWidth))),
|
|
1231
|
+
h5(import_ink5.Box, { width: 1, flexShrink: 0 }, h5(import_ink5.Text, { color: glyph === BAR_THUMB ? "cyan" : "gray" }, glyph))
|
|
1156
1232
|
)
|
|
1157
1233
|
);
|
|
1158
1234
|
}
|
|
@@ -1341,11 +1417,16 @@ if (typeof window === "undefined") {
|
|
|
1341
1417
|
buildBreadcrumb,
|
|
1342
1418
|
buildDetailBreadcrumb,
|
|
1343
1419
|
buildFooter,
|
|
1420
|
+
clampScroll,
|
|
1344
1421
|
formatBindingKey,
|
|
1345
1422
|
h,
|
|
1423
|
+
isScrolledToBottom,
|
|
1346
1424
|
load,
|
|
1347
1425
|
memo,
|
|
1426
|
+
nextScrollAfterContentChange,
|
|
1427
|
+
nextScrollAfterUserMove,
|
|
1348
1428
|
organizeFooterMessages,
|
|
1429
|
+
padEndVisible,
|
|
1349
1430
|
scrollbarGlyphs,
|
|
1350
1431
|
showListScreen,
|
|
1351
1432
|
showMenuScreen,
|
|
@@ -1353,6 +1434,8 @@ if (typeof window === "undefined") {
|
|
|
1353
1434
|
showMultiColumnListWithPreviewScreen,
|
|
1354
1435
|
showScreen,
|
|
1355
1436
|
showWordGridScreen,
|
|
1437
|
+
sliceVisible,
|
|
1438
|
+
stripAnsi,
|
|
1356
1439
|
useCallback,
|
|
1357
1440
|
useEffect,
|
|
1358
1441
|
useInput,
|
|
@@ -1360,6 +1443,7 @@ if (typeof window === "undefined") {
|
|
|
1360
1443
|
useMemo,
|
|
1361
1444
|
useRef,
|
|
1362
1445
|
useState,
|
|
1446
|
+
visibleWidth,
|
|
1363
1447
|
wrapTextLines
|
|
1364
1448
|
});
|
|
1365
1449
|
//# sourceMappingURL=screen.cjs.map
|