@nmakarov/cli-toolkit 0.68.0 → 0.70.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 +328 -193
- package/dist/cli-runner.cjs.map +1 -1
- package/dist/cli-runner.js +328 -193
- package/dist/cli-runner.js.map +1 -1
- package/dist/index.cjs +366 -209
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +360 -209
- package/dist/index.js.map +1 -1
- package/dist/init.cjs +145 -103
- package/dist/init.cjs.map +1 -1
- package/dist/init.js +145 -103
- package/dist/init.js.map +1 -1
- package/dist/screen.cjs +140 -103
- package/dist/screen.cjs.map +1 -1
- package/dist/screen.js +137 -103
- package/dist/screen.js.map +1 -1
- package/dist/tasks.cjs +218 -106
- package/dist/tasks.cjs.map +1 -1
- package/dist/tasks.js +215 -106
- package/dist/tasks.js.map +1 -1
- package/package.json +2 -2
package/dist/screen.js
CHANGED
|
@@ -84,7 +84,43 @@ function ScreenFooter({ lines, textStyle }) {
|
|
|
84
84
|
// src/screen/list-components.js
|
|
85
85
|
import React, { useState, useEffect, useRef, createElement } from "react";
|
|
86
86
|
import { Box as Box2, Text as Text2 } from "ink";
|
|
87
|
+
|
|
88
|
+
// src/screen/scrollbar.js
|
|
89
|
+
var BAR_THUMB = "#";
|
|
90
|
+
var BAR_TRACK = "|";
|
|
91
|
+
function scrollbarGlyphs(viewportRows, totalLines, scrollTop) {
|
|
92
|
+
const view = Math.max(1, Math.floor(viewportRows));
|
|
93
|
+
const total = Math.max(0, Math.floor(totalLines));
|
|
94
|
+
if (total <= view) return null;
|
|
95
|
+
const maxScroll = total - view;
|
|
96
|
+
const thumbSize = Math.max(1, Math.round(view / total * view));
|
|
97
|
+
const travel = Math.max(0, view - thumbSize);
|
|
98
|
+
const s = Math.min(Math.max(0, Math.floor(scrollTop)), maxScroll);
|
|
99
|
+
const thumbStart = maxScroll === 0 ? 0 : Math.round(s / maxScroll * travel);
|
|
100
|
+
const glyphs = [];
|
|
101
|
+
for (let i = 0; i < view; i++) {
|
|
102
|
+
glyphs.push(i >= thumbStart && i < thumbStart + thumbSize ? BAR_THUMB : BAR_TRACK);
|
|
103
|
+
}
|
|
104
|
+
return glyphs;
|
|
105
|
+
}
|
|
106
|
+
var PAGE_SCROLL_KEY_BINDINGS = [
|
|
107
|
+
{ key: "upArrow", meta: true, caption: "page", action: "pageUp", order: 0 },
|
|
108
|
+
{ key: "downArrow", meta: true, caption: "page", action: "pageDown", order: 0 },
|
|
109
|
+
{ key: "upArrow", ctrl: true, caption: "page", action: "pageUp", order: 0 },
|
|
110
|
+
{ key: "downArrow", ctrl: true, caption: "page", action: "pageDown", order: 0 },
|
|
111
|
+
{ key: "pageUp", caption: "page", action: "pageUp", order: 0 },
|
|
112
|
+
{ key: "pageDown", caption: "page", action: "pageDown", order: 0 }
|
|
113
|
+
];
|
|
114
|
+
|
|
115
|
+
// src/screen/list-components.js
|
|
87
116
|
var h2 = createElement;
|
|
117
|
+
function clipNameForScrollbar(name, needsBar) {
|
|
118
|
+
if (!needsBar || name == null) return name;
|
|
119
|
+
const s = String(name);
|
|
120
|
+
if (s.length <= 1) return s;
|
|
121
|
+
if (/\s$/.test(s)) return s.slice(0, -1);
|
|
122
|
+
return s;
|
|
123
|
+
}
|
|
88
124
|
function MultiColumnListComponent({ items, ctx, selectedIndexRef }) {
|
|
89
125
|
const [, forceUpdate] = useState({});
|
|
90
126
|
const termWidth = (process.stdout.columns || 80) - 8;
|
|
@@ -298,17 +334,42 @@ function ListComponent({ items, ctx, selectedIndexRef, renderItem, getTitle, sor
|
|
|
298
334
|
});
|
|
299
335
|
ctx.setAction("scrollUp", () => {
|
|
300
336
|
const { scrollOffset: currentScrollOffset } = scrollStateRef.current;
|
|
301
|
-
|
|
302
|
-
setScrollOffset(newScrollOffset);
|
|
337
|
+
setScrollOffset(Math.max(0, currentScrollOffset - 1));
|
|
303
338
|
forceUpdate({});
|
|
304
339
|
});
|
|
305
340
|
ctx.setAction("scrollDown", () => {
|
|
306
341
|
const { scrollOffset: currentScrollOffset, maxHeight: currentMaxHeight, totalItems } = scrollStateRef.current;
|
|
307
342
|
const currentMaxScrollOffset = Math.max(0, totalItems - currentMaxHeight);
|
|
308
|
-
|
|
309
|
-
setScrollOffset(newScrollOffset);
|
|
343
|
+
setScrollOffset(Math.min(currentMaxScrollOffset, currentScrollOffset + 1));
|
|
310
344
|
forceUpdate({});
|
|
311
345
|
});
|
|
346
|
+
ctx.setAction("pageUp", () => {
|
|
347
|
+
const { maxHeight: vh } = scrollStateRef.current;
|
|
348
|
+
const page = Math.max(1, vh || 1);
|
|
349
|
+
const newIndex = Math.max(0, selectedIndexRef.current - page);
|
|
350
|
+
selectedIndexRef.current = newIndex;
|
|
351
|
+
const list = displayItemsRef.current;
|
|
352
|
+
onSelectionChangeRef.current?.(newIndex, list[newIndex]);
|
|
353
|
+
setScrollOffset(newIndex);
|
|
354
|
+
forceUpdate({});
|
|
355
|
+
});
|
|
356
|
+
ctx.setAction("pageDown", () => {
|
|
357
|
+
const { maxHeight: vh, totalItems } = scrollStateRef.current;
|
|
358
|
+
const page = Math.max(1, vh || 1);
|
|
359
|
+
const maxIndex = Math.max(0, totalItems - 1);
|
|
360
|
+
const newIndex = Math.min(maxIndex, selectedIndexRef.current + page);
|
|
361
|
+
selectedIndexRef.current = newIndex;
|
|
362
|
+
const list = displayItemsRef.current;
|
|
363
|
+
onSelectionChangeRef.current?.(newIndex, list[newIndex]);
|
|
364
|
+
const maxScroll = Math.max(0, totalItems - page);
|
|
365
|
+
setScrollOffset(Math.min(maxScroll, Math.max(0, newIndex - page + 1)));
|
|
366
|
+
forceUpdate({});
|
|
367
|
+
});
|
|
368
|
+
const navBindings = [
|
|
369
|
+
{ key: "upArrow", caption: "navigate", action: "moveUp", order: 0 },
|
|
370
|
+
{ key: "downArrow", caption: "navigate", action: "moveDown", order: 0 },
|
|
371
|
+
...PAGE_SCROLL_KEY_BINDINGS
|
|
372
|
+
];
|
|
312
373
|
if (sortable) {
|
|
313
374
|
ctx.setAction("toggleSort", () => {
|
|
314
375
|
const nextSort = sortOrder === "none" ? "asc" : sortOrder === "asc" ? "desc" : "none";
|
|
@@ -355,8 +416,7 @@ function ListComponent({ items, ctx, selectedIndexRef, renderItem, getTitle, sor
|
|
|
355
416
|
}
|
|
356
417
|
};
|
|
357
418
|
ctx.setKeyBinding([
|
|
358
|
-
|
|
359
|
-
{ key: "downArrow", caption: "navigate", action: "moveDown", order: 0 },
|
|
419
|
+
...navBindings,
|
|
360
420
|
{
|
|
361
421
|
key: "s",
|
|
362
422
|
caption: sortCaption,
|
|
@@ -366,47 +426,47 @@ function ListComponent({ items, ctx, selectedIndexRef, renderItem, getTitle, sor
|
|
|
366
426
|
]);
|
|
367
427
|
ctx.update();
|
|
368
428
|
} else {
|
|
369
|
-
ctx.setKeyBinding(
|
|
370
|
-
{ key: "upArrow", caption: "navigate", action: "moveUp", order: 0 },
|
|
371
|
-
{ key: "downArrow", caption: "navigate", action: "moveDown", order: 0 }
|
|
372
|
-
]);
|
|
429
|
+
ctx.setKeyBinding(navBindings);
|
|
373
430
|
}
|
|
374
431
|
}, [sortOrder, sortable]);
|
|
375
432
|
const selectedIndex = selectedIndexRef.current;
|
|
433
|
+
const needsBar = displayItems.length > effectiveMaxHeight;
|
|
434
|
+
const barGlyphs = needsBar ? scrollbarGlyphs(effectiveMaxHeight, displayItems.length, clampedScrollOffset) : null;
|
|
435
|
+
const appendBar = (rowContent, displayIndex) => {
|
|
436
|
+
if (!barGlyphs) return rowContent;
|
|
437
|
+
const glyph = barGlyphs[displayIndex] ?? BAR_TRACK;
|
|
438
|
+
return h2(
|
|
439
|
+
Box2,
|
|
440
|
+
{ flexDirection: "row" },
|
|
441
|
+
rowContent,
|
|
442
|
+
h2(Text2, { color: glyph === BAR_THUMB ? "cyan" : "gray" }, glyph)
|
|
443
|
+
);
|
|
444
|
+
};
|
|
376
445
|
const defaultRenderItem = (item, isSelected, displayIndex, actualIndex) => {
|
|
377
446
|
const isFirstVisible = displayIndex === 0;
|
|
378
447
|
const isLastVisible = displayIndex === visibleItems.length - 1;
|
|
379
|
-
let arrowPrefix = "";
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
arrowPrefix = "\
|
|
383
|
-
} else if (isLastVisible && canScrollDown) {
|
|
384
|
-
arrowPrefix = "\u2193 ";
|
|
385
|
-
} else {
|
|
386
|
-
arrowPrefix = " ";
|
|
387
|
-
}
|
|
388
|
-
if (isSelected) {
|
|
389
|
-
selectionPrefix = selectionMarker;
|
|
390
|
-
} else {
|
|
391
|
-
selectionPrefix = " ".repeat(selectionMarker.length);
|
|
448
|
+
let arrowPrefix = " ";
|
|
449
|
+
if (!needsBar) {
|
|
450
|
+
if (isFirstVisible && canScrollUp) arrowPrefix = "\u2191 ";
|
|
451
|
+
else if (isLastVisible && canScrollDown) arrowPrefix = "\u2193 ";
|
|
392
452
|
}
|
|
453
|
+
const selectionPrefix = isSelected ? selectionMarker : " ".repeat(selectionMarker.length);
|
|
454
|
+
const label = clipNameForScrollbar(item.name, needsBar);
|
|
393
455
|
return h2(
|
|
394
456
|
Box2,
|
|
395
457
|
{ flexDirection: "row" },
|
|
396
|
-
|
|
397
|
-
h2(Text2, {
|
|
398
|
-
key: `arrow-${actualIndex}`,
|
|
399
|
-
color: "white"
|
|
400
|
-
}, arrowPrefix),
|
|
401
|
-
// Selection marker space (always same width, not highlighted)
|
|
458
|
+
h2(Text2, { key: `arrow-${actualIndex}`, color: "white" }, arrowPrefix),
|
|
402
459
|
h2(Text2, { key: `marker-${actualIndex}`, color: "white" }, selectionPrefix),
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
460
|
+
h2(
|
|
461
|
+
Text2,
|
|
462
|
+
{
|
|
463
|
+
key: `name-${actualIndex}`,
|
|
464
|
+
color: isSelected ? "black" : "white",
|
|
465
|
+
backgroundColor: isSelected ? "cyan" : void 0,
|
|
466
|
+
bold: isSelected
|
|
467
|
+
},
|
|
468
|
+
label
|
|
469
|
+
)
|
|
410
470
|
);
|
|
411
471
|
};
|
|
412
472
|
const itemRenderer = renderItem || defaultRenderItem;
|
|
@@ -419,42 +479,32 @@ function ListComponent({ items, ctx, selectedIndexRef, renderItem, getTitle, sor
|
|
|
419
479
|
if (renderItem) {
|
|
420
480
|
const isFirstVisible = displayIndex === 0;
|
|
421
481
|
const isLastVisible = displayIndex === visibleItems.length - 1;
|
|
422
|
-
let arrowPrefix = "";
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
arrowPrefix = "\
|
|
426
|
-
} else if (isLastVisible && canScrollDown) {
|
|
427
|
-
arrowPrefix = "\u2193 ";
|
|
428
|
-
} else {
|
|
429
|
-
arrowPrefix = " ";
|
|
430
|
-
}
|
|
431
|
-
if (isSelected) {
|
|
432
|
-
selectionPrefix = selectionMarker;
|
|
433
|
-
} else {
|
|
434
|
-
selectionPrefix = " ".repeat(selectionMarker.length);
|
|
482
|
+
let arrowPrefix = " ";
|
|
483
|
+
if (!needsBar) {
|
|
484
|
+
if (isFirstVisible && canScrollUp) arrowPrefix = "\u2191 ";
|
|
485
|
+
else if (isLastVisible && canScrollDown) arrowPrefix = "\u2193 ";
|
|
435
486
|
}
|
|
487
|
+
const selectionPrefix = isSelected ? selectionMarker : " ".repeat(selectionMarker.length);
|
|
488
|
+
const clipped = needsBar ? { ...item, name: clipNameForScrollbar(item.name, true) } : item;
|
|
489
|
+
const row = h2(
|
|
490
|
+
Box2,
|
|
491
|
+
{ flexDirection: "row" },
|
|
492
|
+
h2(Text2, { key: `arrow-${actualIndex}`, color: "white" }, arrowPrefix),
|
|
493
|
+
h2(Text2, { key: `marker-${actualIndex}`, color: "white" }, selectionPrefix),
|
|
494
|
+
renderItem(clipped, isSelected, displayIndex)
|
|
495
|
+
);
|
|
436
496
|
return h2(ScreenRow, {
|
|
437
497
|
key: `item-${actualIndex}`,
|
|
438
|
-
children:
|
|
439
|
-
Box2,
|
|
440
|
-
{ flexDirection: "row" },
|
|
441
|
-
// Arrow (clickable if functional, not highlighted)
|
|
442
|
-
h2(Text2, {
|
|
443
|
-
key: `arrow-${actualIndex}`,
|
|
444
|
-
color: "white"
|
|
445
|
-
}, arrowPrefix),
|
|
446
|
-
// Selection marker space (always same width, not highlighted)
|
|
447
|
-
h2(Text2, { key: `marker-${actualIndex}`, color: "white" }, selectionPrefix),
|
|
448
|
-
// Custom rendered content
|
|
449
|
-
renderItem(item, isSelected, displayIndex)
|
|
450
|
-
)
|
|
451
|
-
});
|
|
452
|
-
} else {
|
|
453
|
-
return h2(ScreenRow, {
|
|
454
|
-
key: `item-${actualIndex}`,
|
|
455
|
-
children: itemRenderer(item, isSelected, displayIndex, actualIndex)
|
|
498
|
+
children: appendBar(row, displayIndex)
|
|
456
499
|
});
|
|
457
500
|
}
|
|
501
|
+
return h2(ScreenRow, {
|
|
502
|
+
key: `item-${actualIndex}`,
|
|
503
|
+
children: appendBar(
|
|
504
|
+
itemRenderer(item, isSelected, displayIndex, actualIndex),
|
|
505
|
+
displayIndex
|
|
506
|
+
)
|
|
507
|
+
});
|
|
458
508
|
})
|
|
459
509
|
);
|
|
460
510
|
}
|
|
@@ -931,21 +981,6 @@ function wrapTextLines(text, cols) {
|
|
|
931
981
|
}
|
|
932
982
|
return out;
|
|
933
983
|
}
|
|
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
984
|
function padEndVisible(s, w) {
|
|
950
985
|
const t = String(s ?? "");
|
|
951
986
|
if (t.length >= w) return t.slice(0, w);
|
|
@@ -954,12 +989,7 @@ function padEndVisible(s, w) {
|
|
|
954
989
|
var SCROLL_KEYS = [
|
|
955
990
|
{ key: "upArrow", caption: "scroll", action: "scrollUp", order: 0 },
|
|
956
991
|
{ key: "downArrow", caption: "scroll", action: "scrollDown", order: 0 },
|
|
957
|
-
|
|
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 }
|
|
992
|
+
...PAGE_SCROLL_KEY_BINDINGS
|
|
963
993
|
];
|
|
964
994
|
function ScrollableText({
|
|
965
995
|
ctx,
|
|
@@ -974,20 +1004,19 @@ function ScrollableText({
|
|
|
974
1004
|
}) {
|
|
975
1005
|
const [scrollTop, setScrollTop] = useState3(0);
|
|
976
1006
|
const [, bump] = useState3(0);
|
|
977
|
-
const termCols = process.stdout.columns || 80;
|
|
978
1007
|
const termRows = process.stdout.rows || 24;
|
|
979
1008
|
const viewportRows = Math.max(
|
|
980
1009
|
4,
|
|
981
1010
|
maxHeight != null ? Math.floor(maxHeight) : Math.max(8, termRows - 8)
|
|
982
1011
|
);
|
|
983
|
-
const
|
|
984
|
-
const
|
|
1012
|
+
const contentCols = Math.max(20, getScreenWidth() - 4);
|
|
1013
|
+
const barCols = showScrollbar ? 1 : 0;
|
|
1014
|
+
const textWidth = Math.max(8, contentCols - barCols);
|
|
985
1015
|
const allLines = useMemo(() => {
|
|
986
1016
|
if (Array.isArray(linesProp)) return linesProp.map((l) => String(l ?? ""));
|
|
987
|
-
return wrap ? wrapTextLines(text,
|
|
988
|
-
}, [linesProp, text, wrap,
|
|
1017
|
+
return wrap ? wrapTextLines(text, textWidth) : String(text ?? "").split("\n");
|
|
1018
|
+
}, [linesProp, text, wrap, textWidth]);
|
|
989
1019
|
const needsBar = showScrollbar && allLines.length > viewportRows;
|
|
990
|
-
const textWidth = Math.max(8, termCols - (needsBar ? 1 : 0));
|
|
991
1020
|
const maxScroll = Math.max(0, allLines.length - viewportRows);
|
|
992
1021
|
const clamped = Math.min(Math.max(0, scrollTop), maxScroll);
|
|
993
1022
|
const visible = allLines.slice(clamped, clamped + viewportRows);
|
|
@@ -1027,22 +1056,24 @@ function ScrollableText({
|
|
|
1027
1056
|
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
1057
|
const rowNodes = visible.map((line, i) => {
|
|
1029
1058
|
const body = padEndVisible(line, textWidth);
|
|
1030
|
-
const glyph = bar ? bar[i] ?? "
|
|
1059
|
+
const glyph = bar ? bar[i] ?? BAR_TRACK : showScrollbar ? " " : "";
|
|
1060
|
+
const isThumb = glyph === BAR_THUMB;
|
|
1031
1061
|
return h5(
|
|
1032
|
-
|
|
1033
|
-
{ key: `L${clamped + i}
|
|
1034
|
-
|
|
1035
|
-
glyph ? h5(Text5, { color:
|
|
1062
|
+
Text5,
|
|
1063
|
+
{ key: `L${clamped + i}` },
|
|
1064
|
+
body,
|
|
1065
|
+
glyph ? h5(Text5, { color: isThumb ? "cyan" : "gray" }, glyph) : null
|
|
1036
1066
|
);
|
|
1037
1067
|
});
|
|
1038
1068
|
if (bar && visible.length < viewportRows) {
|
|
1039
1069
|
for (let i = visible.length; i < viewportRows; i++) {
|
|
1070
|
+
const glyph = bar[i] ?? BAR_TRACK;
|
|
1040
1071
|
rowNodes.push(
|
|
1041
1072
|
h5(
|
|
1042
|
-
|
|
1043
|
-
{ key: `pad${i}
|
|
1044
|
-
|
|
1045
|
-
h5(Text5, { color:
|
|
1073
|
+
Text5,
|
|
1074
|
+
{ key: `pad${i}` },
|
|
1075
|
+
padEndVisible("", textWidth),
|
|
1076
|
+
h5(Text5, { color: glyph === BAR_THUMB ? "cyan" : "gray" }, glyph)
|
|
1046
1077
|
)
|
|
1047
1078
|
);
|
|
1048
1079
|
}
|
|
@@ -1191,6 +1222,8 @@ if (typeof window === "undefined") {
|
|
|
1191
1222
|
});
|
|
1192
1223
|
}
|
|
1193
1224
|
export {
|
|
1225
|
+
BAR_THUMB,
|
|
1226
|
+
BAR_TRACK,
|
|
1194
1227
|
Box5 as Box,
|
|
1195
1228
|
Divider,
|
|
1196
1229
|
FooterPresets,
|
|
@@ -1200,6 +1233,7 @@ export {
|
|
|
1200
1233
|
ListItem,
|
|
1201
1234
|
MultiColumnListComponent,
|
|
1202
1235
|
MultiColumnListWithPreviewComponent,
|
|
1236
|
+
PAGE_SCROLL_KEY_BINDINGS,
|
|
1203
1237
|
React2 as React,
|
|
1204
1238
|
ScreenBody,
|
|
1205
1239
|
ScreenContainer,
|