@nmakarov/cli-toolkit 0.79.0 → 0.81.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 +194 -57
- package/dist/cli-runner.cjs.map +1 -1
- package/dist/cli-runner.js +194 -57
- package/dist/cli-runner.js.map +1 -1
- package/dist/db.cjs +105 -25
- package/dist/db.cjs.map +1 -1
- package/dist/db.js +105 -25
- package/dist/db.js.map +1 -1
- package/dist/index.cjs +179 -56
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +175 -56
- package/dist/index.js.map +1 -1
- package/dist/init.cjs +89 -32
- package/dist/init.cjs.map +1 -1
- package/dist/init.js +89 -32
- package/dist/init.js.map +1 -1
- package/dist/logger.cjs +6 -5
- package/dist/logger.cjs.map +1 -1
- package/dist/logger.js +6 -5
- package/dist/logger.js.map +1 -1
- package/dist/screen.cjs +59 -26
- package/dist/screen.cjs.map +1 -1
- package/dist/screen.js +55 -26
- package/dist/screen.js.map +1 -1
- package/package.json +1 -1
package/dist/init.js
CHANGED
|
@@ -1001,6 +1001,30 @@ var init_ui_elements = __esm({
|
|
|
1001
1001
|
}
|
|
1002
1002
|
});
|
|
1003
1003
|
|
|
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
|
+
|
|
1004
1028
|
// src/screen/scrollable-text.js
|
|
1005
1029
|
import { useState as useState3, useEffect as useEffect2, useMemo, useRef as useRef2, createElement as createElement2 } from "react";
|
|
1006
1030
|
import { Box as Box4, Text as Text5 } from "ink";
|
|
@@ -1035,9 +1059,11 @@ function ScrollableText({
|
|
|
1035
1059
|
showScrollbar = true,
|
|
1036
1060
|
showStatus = true,
|
|
1037
1061
|
bindKeys = true,
|
|
1038
|
-
header = null
|
|
1062
|
+
header = null,
|
|
1063
|
+
followBottom = false
|
|
1039
1064
|
}) {
|
|
1040
1065
|
const [scrollTop, setScrollTop] = useState3(0);
|
|
1066
|
+
const [following, setFollowing] = useState3(() => !!followBottom);
|
|
1041
1067
|
const [, bump] = useState3(0);
|
|
1042
1068
|
const termRows = process.stdout.rows || 24;
|
|
1043
1069
|
const viewportRows = Math.max(
|
|
@@ -1056,39 +1082,40 @@ function ScrollableText({
|
|
|
1056
1082
|
const clamped = Math.min(Math.max(0, scrollTop), maxScroll);
|
|
1057
1083
|
const visible = allLines.slice(clamped, clamped + viewportRows);
|
|
1058
1084
|
const bar = needsBar ? scrollbarGlyphs(viewportRows, allLines.length, clamped) : null;
|
|
1059
|
-
useEffect2(() => {
|
|
1060
|
-
setScrollTop((s) => Math.min(s, maxScroll));
|
|
1061
|
-
}, [maxScroll]);
|
|
1062
1085
|
const maxScrollRef = useRef2(maxScroll);
|
|
1063
1086
|
const pageSizeRef = useRef2(viewportRows);
|
|
1087
|
+
const scrollTopRef = useRef2(scrollTop);
|
|
1088
|
+
const followingRef = useRef2(following);
|
|
1064
1089
|
maxScrollRef.current = maxScroll;
|
|
1065
1090
|
pageSizeRef.current = viewportRows;
|
|
1091
|
+
scrollTopRef.current = scrollTop;
|
|
1092
|
+
followingRef.current = following;
|
|
1093
|
+
useEffect2(() => {
|
|
1094
|
+
const next = nextScrollAfterContentChange({
|
|
1095
|
+
following: followBottom && followingRef.current,
|
|
1096
|
+
scrollTop: scrollTopRef.current,
|
|
1097
|
+
maxScroll
|
|
1098
|
+
});
|
|
1099
|
+
if (next.scrollTop !== scrollTopRef.current) setScrollTop(next.scrollTop);
|
|
1100
|
+
if (followBottom && next.following !== followingRef.current) setFollowing(next.following);
|
|
1101
|
+
}, [maxScroll, followBottom]);
|
|
1102
|
+
const applyUserScroll = (delta) => {
|
|
1103
|
+
const next = nextScrollAfterUserMove(scrollTopRef.current, maxScrollRef.current, delta);
|
|
1104
|
+
setScrollTop(next.scrollTop);
|
|
1105
|
+
if (followBottom) setFollowing(next.following);
|
|
1106
|
+
bump((n) => n + 1);
|
|
1107
|
+
ctx?.update?.();
|
|
1108
|
+
};
|
|
1066
1109
|
useEffect2(() => {
|
|
1067
1110
|
if (!ctx || !bindKeys) return void 0;
|
|
1068
1111
|
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
|
-
});
|
|
1112
|
+
ctx.setAction("scrollUp", () => applyUserScroll(-1));
|
|
1113
|
+
ctx.setAction("scrollDown", () => applyUserScroll(1));
|
|
1114
|
+
ctx.setAction("pageUp", () => applyUserScroll(-pageSizeRef.current));
|
|
1115
|
+
ctx.setAction("pageDown", () => applyUserScroll(pageSizeRef.current));
|
|
1089
1116
|
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" : "");
|
|
1117
|
+
}, [ctx, bindKeys, followBottom]);
|
|
1118
|
+
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
1119
|
const rowNodes = visible.map((line, i) => {
|
|
1093
1120
|
const body = padEndVisible(line, textWidth);
|
|
1094
1121
|
const glyph = bar ? bar[i] ?? BAR_TRACK : showScrollbar ? " " : "";
|
|
@@ -1126,6 +1153,7 @@ var init_scrollable_text = __esm({
|
|
|
1126
1153
|
"src/screen/scrollable-text.js"() {
|
|
1127
1154
|
init_components();
|
|
1128
1155
|
init_scrollbar();
|
|
1156
|
+
init_follow_scroll();
|
|
1129
1157
|
init_scrollbar();
|
|
1130
1158
|
h5 = createElement2;
|
|
1131
1159
|
SCROLL_KEYS = [
|
|
@@ -1294,10 +1322,14 @@ __export(screen_exports, {
|
|
|
1294
1322
|
buildBreadcrumb: () => buildBreadcrumb,
|
|
1295
1323
|
buildDetailBreadcrumb: () => buildDetailBreadcrumb,
|
|
1296
1324
|
buildFooter: () => buildFooter,
|
|
1325
|
+
clampScroll: () => clampScroll,
|
|
1297
1326
|
formatBindingKey: () => formatBindingKey,
|
|
1298
1327
|
h: () => createElement3,
|
|
1328
|
+
isScrolledToBottom: () => isScrolledToBottom,
|
|
1299
1329
|
load: () => load,
|
|
1300
1330
|
memo: () => memo,
|
|
1331
|
+
nextScrollAfterContentChange: () => nextScrollAfterContentChange,
|
|
1332
|
+
nextScrollAfterUserMove: () => nextScrollAfterUserMove,
|
|
1301
1333
|
organizeFooterMessages: () => organizeFooterMessages,
|
|
1302
1334
|
scrollbarGlyphs: () => scrollbarGlyphs,
|
|
1303
1335
|
showListScreen: () => showListScreen,
|
|
@@ -1334,6 +1366,7 @@ var init_screen = __esm({
|
|
|
1334
1366
|
init_components();
|
|
1335
1367
|
init_ui_elements();
|
|
1336
1368
|
init_scrollable_text();
|
|
1369
|
+
init_follow_scroll();
|
|
1337
1370
|
init_scrollbar();
|
|
1338
1371
|
init_key_bindings();
|
|
1339
1372
|
init_utils();
|
|
@@ -2643,13 +2676,14 @@ var Logger = class _Logger {
|
|
|
2643
2676
|
*/
|
|
2644
2677
|
progress(message, opts) {
|
|
2645
2678
|
const { prefix, count, total } = opts;
|
|
2646
|
-
const
|
|
2679
|
+
const displayTotal = Math.max(Number(total) || 0, Number(count) || 0);
|
|
2680
|
+
const paddedTotal = String(displayTotal).length;
|
|
2647
2681
|
const paddedCount = String(count).padStart(paddedTotal, " ");
|
|
2648
2682
|
const payload = {
|
|
2649
2683
|
level: "progress",
|
|
2650
2684
|
message,
|
|
2651
2685
|
count: paddedCount,
|
|
2652
|
-
total,
|
|
2686
|
+
total: displayTotal,
|
|
2653
2687
|
prefix
|
|
2654
2688
|
};
|
|
2655
2689
|
const key = prefix ?? "";
|
|
@@ -2666,7 +2700,7 @@ var Logger = class _Logger {
|
|
|
2666
2700
|
if (wantTimes) {
|
|
2667
2701
|
let remaining = -1;
|
|
2668
2702
|
if (itemsPerSec > 0) {
|
|
2669
|
-
remaining = (
|
|
2703
|
+
remaining = Math.max(0, (displayTotal - count) / itemsPerSec);
|
|
2670
2704
|
}
|
|
2671
2705
|
payload.elapsed = this.round(elapsedSeconds, 2);
|
|
2672
2706
|
payload.remaining = remaining >= 0 ? this.round(remaining, 2) : remaining;
|
|
@@ -2675,12 +2709,12 @@ var Logger = class _Logger {
|
|
|
2675
2709
|
payload.rate = itemsPerSec >= 0 ? this.round(itemsPerSec, 2) : itemsPerSec;
|
|
2676
2710
|
}
|
|
2677
2711
|
}
|
|
2678
|
-
if (count
|
|
2712
|
+
if (count === total) {
|
|
2679
2713
|
delete this.startTimes[key];
|
|
2680
2714
|
delete this.startCounts[key];
|
|
2681
2715
|
delete this.lastProgressTimes[key];
|
|
2682
2716
|
}
|
|
2683
|
-
if (this.shouldOutputProgress(prefix ?? "", count,
|
|
2717
|
+
if (this.shouldOutputProgress(prefix ?? "", count, displayTotal)) {
|
|
2684
2718
|
this.out(payload);
|
|
2685
2719
|
if (this.options.progressThrottle && prefix) {
|
|
2686
2720
|
this.lastProgressTimes[prefix] = Date.now();
|
|
@@ -2839,6 +2873,8 @@ function setup(opts = {}) {
|
|
|
2839
2873
|
// a quick "show me the figured params and quit" that skips the flow's
|
|
2840
2874
|
// actual work. Like --stopAfter=init, this is a hard exit(0) (registered
|
|
2841
2875
|
// cleanups are skipped); call it once components/params are resolved.
|
|
2876
|
+
_requestExitCode: null,
|
|
2877
|
+
requestExit: null,
|
|
2842
2878
|
showUsedParamsIfNeeded: () => {
|
|
2843
2879
|
const mode = params.getShowUsedParamsMode?.();
|
|
2844
2880
|
if (mode !== "top" && mode !== "stop") return;
|
|
@@ -2849,6 +2885,9 @@ function setup(opts = {}) {
|
|
|
2849
2885
|
}
|
|
2850
2886
|
}
|
|
2851
2887
|
};
|
|
2888
|
+
context.requestExit = (code = 0) => {
|
|
2889
|
+
context._requestExitCode = code;
|
|
2890
|
+
};
|
|
2852
2891
|
logger.debug("[setup] completed successfully");
|
|
2853
2892
|
return context;
|
|
2854
2893
|
}
|
|
@@ -2884,9 +2923,25 @@ async function init(flow, opts = {}) {
|
|
|
2884
2923
|
if (cleanupRan) return;
|
|
2885
2924
|
cleanupRan = true;
|
|
2886
2925
|
const fns = [...ctx.cleanupFunctions].reverse();
|
|
2926
|
+
const budgetMs = 5e3;
|
|
2927
|
+
const started = Date.now();
|
|
2887
2928
|
for (const fn of fns) {
|
|
2929
|
+
const left = budgetMs - (Date.now() - started);
|
|
2930
|
+
if (left <= 0) {
|
|
2931
|
+
ctx.logger.warn("[cleanup] budget exhausted \u2014 skipping remaining cleanup");
|
|
2932
|
+
break;
|
|
2933
|
+
}
|
|
2888
2934
|
try {
|
|
2889
|
-
await
|
|
2935
|
+
await Promise.race([
|
|
2936
|
+
Promise.resolve(fn(ctx)),
|
|
2937
|
+
new Promise((_, reject) => {
|
|
2938
|
+
const t = setTimeout(
|
|
2939
|
+
() => reject(new Error(`cleanup timed out after ${left}ms`)),
|
|
2940
|
+
left
|
|
2941
|
+
);
|
|
2942
|
+
t.unref?.();
|
|
2943
|
+
})
|
|
2944
|
+
]);
|
|
2890
2945
|
} catch (error) {
|
|
2891
2946
|
ctx.logger.warn("[cleanup] error in cleanup function:", error);
|
|
2892
2947
|
}
|
|
@@ -2982,6 +3037,8 @@ async function init(flow, opts = {}) {
|
|
|
2982
3037
|
process.exit(process.exitCode);
|
|
2983
3038
|
} else if (stop || kill) {
|
|
2984
3039
|
process.exit(0);
|
|
3040
|
+
} else if (context._requestExitCode != null) {
|
|
3041
|
+
process.exit(context._requestExitCode);
|
|
2985
3042
|
}
|
|
2986
3043
|
}
|
|
2987
3044
|
}
|