@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/index.js CHANGED
@@ -472,6 +472,59 @@ var init_list_components = __esm({
472
472
  }
473
473
  });
474
474
 
475
+ // src/screen/key-bindings.js
476
+ function bindingIdentity(b) {
477
+ return [
478
+ String(b?.key ?? ""),
479
+ b?.meta ? "m" : "",
480
+ b?.ctrl ? "c" : "",
481
+ b?.shift ? "s" : ""
482
+ ].join("|");
483
+ }
484
+ function bindingMatchesInput(binding, input, key) {
485
+ if (!binding?.key) return false;
486
+ let keyMatches = false;
487
+ if (key?.[binding.key]) {
488
+ keyMatches = true;
489
+ } else if (input === binding.key) {
490
+ keyMatches = true;
491
+ } else if (key?.name === binding.key) {
492
+ keyMatches = true;
493
+ }
494
+ if (!keyMatches) return false;
495
+ const modOk = (flag, pressed) => {
496
+ if (flag === true) return !!pressed;
497
+ if (flag === false) return !pressed;
498
+ return !pressed;
499
+ };
500
+ if (!modOk(binding.meta, key?.meta)) return false;
501
+ if (!modOk(binding.ctrl, key?.ctrl)) return false;
502
+ if (binding.shift !== void 0 && !!key?.shift !== !!binding.shift) return false;
503
+ return true;
504
+ }
505
+ function formatBindingKey(binding) {
506
+ let label = KEY_LABELS[binding.key] || binding.key;
507
+ if (binding.shift) label = `\u21E7${label}`;
508
+ if (binding.ctrl) label = `^${label}`;
509
+ if (binding.meta) label = `\u2325${label}`;
510
+ return label;
511
+ }
512
+ var KEY_LABELS;
513
+ var init_key_bindings = __esm({
514
+ "src/screen/key-bindings.js"() {
515
+ KEY_LABELS = {
516
+ escape: "esc",
517
+ leftArrow: "\u2190",
518
+ rightArrow: "\u2192",
519
+ upArrow: "\u2191",
520
+ downArrow: "\u2193",
521
+ return: "enter",
522
+ pageUp: "PgUp",
523
+ pageDown: "PgDn"
524
+ };
525
+ }
526
+ });
527
+
475
528
  // src/screen/screens.js
476
529
  import { useState as useState2, createElement as h3 } from "react";
477
530
  import { render, useInput, Text as Text3 } from "ink";
@@ -487,7 +540,7 @@ function groupKeyBindings(bindings) {
487
540
  order: binding.order || 999
488
541
  };
489
542
  }
490
- groups[caption].keys.push(binding.key);
543
+ groups[caption].keys.push(formatBindingKey(binding));
491
544
  });
492
545
  return Object.values(groups);
493
546
  }
@@ -527,12 +580,14 @@ function formatKeyBindings(bindings, mode = "long") {
527
580
  }
528
581
  function formatKeys(keys) {
529
582
  const keyMap = {
530
- "escape": "esc",
531
- "leftArrow": "\u2190",
532
- "rightArrow": "\u2192",
533
- "upArrow": "\u2191",
534
- "downArrow": "\u2193",
535
- "return": "enter"
583
+ escape: "esc",
584
+ leftArrow: "\u2190",
585
+ rightArrow: "\u2192",
586
+ upArrow: "\u2191",
587
+ downArrow: "\u2193",
588
+ return: "enter",
589
+ pageUp: "PgUp",
590
+ pageDown: "PgDn"
536
591
  };
537
592
  return keys.map((k) => keyMap[k] || k).join("/");
538
593
  }
@@ -572,7 +627,10 @@ async function showScreen(config2) {
572
627
  setKeyBinding: (bindingOrBindings) => {
573
628
  const bindingsToSet = Array.isArray(bindingOrBindings) ? bindingOrBindings : [bindingOrBindings];
574
629
  bindingsToSet.forEach((binding) => {
575
- const existingIndex = keyBindings.findIndex((b) => b.key === binding.key);
630
+ const id = bindingIdentity(binding);
631
+ const existingIndex = keyBindings.findIndex(
632
+ (b) => bindingIdentity(b) === id
633
+ );
576
634
  if (existingIndex >= 0) {
577
635
  const existing = keyBindings[existingIndex];
578
636
  if (existing.protected) {
@@ -596,7 +654,10 @@ async function showScreen(config2) {
596
654
  });
597
655
  },
598
656
  updateKeyBinding: (keyName, updates) => {
599
- const index = keyBindings.findIndex((b) => b.key === keyName);
657
+ const id = updates && (updates.meta != null || updates.ctrl != null || updates.shift != null) ? bindingIdentity({ key: keyName, ...updates }) : null;
658
+ const index = keyBindings.findIndex(
659
+ (b) => id ? bindingIdentity(b) === id : b.key === keyName
660
+ );
600
661
  if (index >= 0) {
601
662
  keyBindings[index] = {
602
663
  ...keyBindings[index],
@@ -604,11 +665,13 @@ async function showScreen(config2) {
604
665
  };
605
666
  }
606
667
  },
607
- removeKeyBinding: (keyName) => {
608
- const index = keyBindings.findIndex((b) => b.key === keyName);
668
+ removeKeyBinding: (keyNameOrBinding) => {
669
+ const index = typeof keyNameOrBinding === "object" && keyNameOrBinding ? keyBindings.findIndex(
670
+ (b) => bindingIdentity(b) === bindingIdentity(keyNameOrBinding)
671
+ ) : keyBindings.findIndex((b) => b.key === keyNameOrBinding);
609
672
  if (index >= 0) {
610
673
  if (keyBindings[index].protected) {
611
- console.warn(`Cannot remove protected key: ${keyName}`);
674
+ console.warn(`Cannot remove protected key: ${keyNameOrBinding}`);
612
675
  return;
613
676
  }
614
677
  keyBindings.splice(index, 1);
@@ -649,24 +712,11 @@ async function showScreen(config2) {
649
712
  }
650
713
  let matchedBinding = null;
651
714
  for (const binding of keyBindings) {
652
- let keyMatches = false;
653
- if (key[binding.key]) {
654
- keyMatches = true;
655
- } else if (input === binding.key) {
656
- keyMatches = true;
657
- } else if (key?.name === binding.key) {
658
- keyMatches = true;
659
- }
660
- if (keyMatches) {
661
- if (binding.enabled === false) {
662
- continue;
663
- }
664
- if (binding.condition && !binding.condition(context)) {
665
- continue;
666
- }
667
- matchedBinding = binding;
668
- break;
669
- }
715
+ if (binding.enabled === false) continue;
716
+ if (!bindingMatchesInput(binding, input, key)) continue;
717
+ if (binding.condition && !binding.condition(context)) continue;
718
+ matchedBinding = binding;
719
+ break;
670
720
  }
671
721
  if (matchedBinding && actions[matchedBinding.action]) {
672
722
  actions[matchedBinding.action]({
@@ -802,6 +852,7 @@ var init_screens = __esm({
802
852
  "src/screen/screens.js"() {
803
853
  init_components();
804
854
  init_list_components();
855
+ init_key_bindings();
805
856
  showMenuScreen = showListScreen;
806
857
  showWordGridScreen = showMultiColumnListScreen;
807
858
  }
@@ -891,6 +942,161 @@ var init_ui_elements = __esm({
891
942
  }
892
943
  });
893
944
 
945
+ // src/screen/scrollable-text.js
946
+ import { useState as useState3, useEffect as useEffect2, useMemo, useRef as useRef2, createElement as createElement2 } from "react";
947
+ import { Box as Box4, Text as Text5 } from "ink";
948
+ function wrapTextLines(text, cols) {
949
+ const w = Math.max(1, Math.floor(Number(cols) || 1));
950
+ const out = [];
951
+ for (const raw of String(text ?? "").split("\n")) {
952
+ if (raw.length === 0) {
953
+ out.push("");
954
+ continue;
955
+ }
956
+ let rest = raw;
957
+ while (rest.length > w) {
958
+ out.push(rest.slice(0, w));
959
+ rest = rest.slice(w);
960
+ }
961
+ if (rest.length > 0) out.push(rest);
962
+ }
963
+ return out;
964
+ }
965
+ function scrollbarGlyphs(viewportRows, totalLines, scrollTop) {
966
+ const view = Math.max(1, Math.floor(viewportRows));
967
+ const total = Math.max(0, Math.floor(totalLines));
968
+ if (total <= view) return null;
969
+ const maxScroll = total - view;
970
+ const thumbSize = Math.max(1, Math.round(view / total * view));
971
+ const travel = Math.max(0, view - thumbSize);
972
+ const s = Math.min(Math.max(0, Math.floor(scrollTop)), maxScroll);
973
+ const thumbStart = maxScroll === 0 ? 0 : Math.round(s / maxScroll * travel);
974
+ const glyphs = [];
975
+ for (let i = 0; i < view; i++) {
976
+ glyphs.push(i >= thumbStart && i < thumbStart + thumbSize ? BAR_THUMB : BAR_TRACK);
977
+ }
978
+ return glyphs;
979
+ }
980
+ function padEndVisible(s, w) {
981
+ const t = String(s ?? "");
982
+ if (t.length >= w) return t.slice(0, w);
983
+ return t + " ".repeat(w - t.length);
984
+ }
985
+ function ScrollableText({
986
+ ctx,
987
+ text = "",
988
+ lines: linesProp,
989
+ maxHeight,
990
+ wrap = true,
991
+ showScrollbar = true,
992
+ showStatus = true,
993
+ bindKeys = true,
994
+ header = null
995
+ }) {
996
+ const [scrollTop, setScrollTop] = useState3(0);
997
+ const [, bump] = useState3(0);
998
+ const termRows = process.stdout.rows || 24;
999
+ const viewportRows = Math.max(
1000
+ 4,
1001
+ maxHeight != null ? Math.floor(maxHeight) : Math.max(8, termRows - 8)
1002
+ );
1003
+ const contentCols = Math.max(20, getScreenWidth() - 4);
1004
+ const barCols = showScrollbar ? 1 : 0;
1005
+ const textWidth = Math.max(8, contentCols - barCols);
1006
+ const allLines = useMemo(() => {
1007
+ if (Array.isArray(linesProp)) return linesProp.map((l) => String(l ?? ""));
1008
+ return wrap ? wrapTextLines(text, textWidth) : String(text ?? "").split("\n");
1009
+ }, [linesProp, text, wrap, textWidth]);
1010
+ const needsBar = showScrollbar && allLines.length > viewportRows;
1011
+ const maxScroll = Math.max(0, allLines.length - viewportRows);
1012
+ const clamped = Math.min(Math.max(0, scrollTop), maxScroll);
1013
+ const visible = allLines.slice(clamped, clamped + viewportRows);
1014
+ const bar = needsBar ? scrollbarGlyphs(viewportRows, allLines.length, clamped) : null;
1015
+ useEffect2(() => {
1016
+ setScrollTop((s) => Math.min(s, maxScroll));
1017
+ }, [maxScroll]);
1018
+ const maxScrollRef = useRef2(maxScroll);
1019
+ const pageSizeRef = useRef2(viewportRows);
1020
+ maxScrollRef.current = maxScroll;
1021
+ pageSizeRef.current = viewportRows;
1022
+ useEffect2(() => {
1023
+ if (!ctx || !bindKeys) return void 0;
1024
+ ctx.setKeyBinding(SCROLL_KEYS);
1025
+ ctx.setAction("scrollUp", () => {
1026
+ setScrollTop((s) => Math.max(0, s - 1));
1027
+ bump((n) => n + 1);
1028
+ ctx.update?.();
1029
+ });
1030
+ ctx.setAction("scrollDown", () => {
1031
+ setScrollTop((s) => Math.min(maxScrollRef.current, s + 1));
1032
+ bump((n) => n + 1);
1033
+ ctx.update?.();
1034
+ });
1035
+ ctx.setAction("pageUp", () => {
1036
+ setScrollTop((s) => Math.max(0, s - pageSizeRef.current));
1037
+ bump((n) => n + 1);
1038
+ ctx.update?.();
1039
+ });
1040
+ ctx.setAction("pageDown", () => {
1041
+ setScrollTop((s) => Math.min(maxScrollRef.current, s + pageSizeRef.current));
1042
+ bump((n) => n + 1);
1043
+ ctx.update?.();
1044
+ });
1045
+ return void 0;
1046
+ }, [ctx, bindKeys]);
1047
+ 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" : "");
1048
+ const rowNodes = visible.map((line, i) => {
1049
+ const body = padEndVisible(line, textWidth);
1050
+ const glyph = bar ? bar[i] ?? BAR_TRACK : showScrollbar ? " " : "";
1051
+ const isThumb = glyph === BAR_THUMB;
1052
+ return h5(
1053
+ Text5,
1054
+ { key: `L${clamped + i}` },
1055
+ body,
1056
+ glyph ? h5(Text5, { color: isThumb ? "cyan" : "gray" }, glyph) : null
1057
+ );
1058
+ });
1059
+ if (bar && visible.length < viewportRows) {
1060
+ for (let i = visible.length; i < viewportRows; i++) {
1061
+ const glyph = bar[i] ?? BAR_TRACK;
1062
+ rowNodes.push(
1063
+ h5(
1064
+ Text5,
1065
+ { key: `pad${i}` },
1066
+ padEndVisible("", textWidth),
1067
+ h5(Text5, { color: glyph === BAR_THUMB ? "cyan" : "gray" }, glyph)
1068
+ )
1069
+ );
1070
+ }
1071
+ }
1072
+ return h5(
1073
+ Box4,
1074
+ { flexDirection: "column" },
1075
+ header == null ? null : typeof header === "string" ? h5(Text5, { color: "gray" }, header) : header,
1076
+ showStatus ? h5(Text5, { color: "gray" }, status) : null,
1077
+ ...rowNodes
1078
+ );
1079
+ }
1080
+ var h5, BAR_THUMB, BAR_TRACK, SCROLL_KEYS;
1081
+ var init_scrollable_text = __esm({
1082
+ "src/screen/scrollable-text.js"() {
1083
+ init_components();
1084
+ h5 = createElement2;
1085
+ BAR_THUMB = "#";
1086
+ BAR_TRACK = "|";
1087
+ SCROLL_KEYS = [
1088
+ { key: "upArrow", caption: "scroll", action: "scrollUp", order: 0 },
1089
+ { key: "downArrow", caption: "scroll", action: "scrollDown", order: 0 },
1090
+ { key: "upArrow", meta: true, caption: "page", action: "pageUp", order: 0 },
1091
+ { key: "downArrow", meta: true, caption: "page", action: "pageDown", order: 0 },
1092
+ { key: "upArrow", ctrl: true, caption: "page", action: "pageUp", order: 0 },
1093
+ { key: "downArrow", ctrl: true, caption: "page", action: "pageDown", order: 0 },
1094
+ { key: "pageUp", caption: "page", action: "pageUp", order: 0 },
1095
+ { key: "pageDown", caption: "page", action: "pageDown", order: 0 }
1096
+ ];
1097
+ }
1098
+ });
1099
+
894
1100
  // src/screen/utils.js
895
1101
  function buildBreadcrumb(parts) {
896
1102
  if (parts.length === 0) return "";
@@ -1020,8 +1226,8 @@ var init_footer_builder = __esm({
1020
1226
  });
1021
1227
 
1022
1228
  // src/screen/index.js
1023
- import React2, { useState as useState3, useEffect as useEffect2, useLayoutEffect, useRef as useRef2, useMemo, useCallback, memo, createElement as createElement2 } from "react";
1024
- import { Box as Box4, Text as Text5, useInput as useInput2 } from "ink";
1229
+ import React2, { useState as useState4, useEffect as useEffect3, useLayoutEffect, useRef as useRef3, useMemo as useMemo2, useCallback, memo, createElement as createElement3 } from "react";
1230
+ import { Box as Box5, Text as Text6, useInput as useInput2 } from "ink";
1025
1231
  async function load() {
1026
1232
  if (loadPromise) return loadPromise;
1027
1233
  loadPromise = Promise.all([
@@ -1038,6 +1244,8 @@ var init_screen = __esm({
1038
1244
  init_list_components();
1039
1245
  init_components();
1040
1246
  init_ui_elements();
1247
+ init_scrollable_text();
1248
+ init_key_bindings();
1041
1249
  init_utils();
1042
1250
  init_footer_builder();
1043
1251
  loadPromise = null;
@@ -9317,7 +9525,7 @@ export {
9317
9525
  AbstractTask,
9318
9526
  Args,
9319
9527
  Aws,
9320
- Box4 as Box,
9528
+ Box5 as Box,
9321
9529
  Db,
9322
9530
  Divider,
9323
9531
  FileDatabase,
@@ -9342,6 +9550,7 @@ export {
9342
9550
  ScreenFooter,
9343
9551
  ScreenRow,
9344
9552
  ScreenTitle,
9553
+ ScrollableText,
9345
9554
  TaskGetLogs,
9346
9555
  TaskPing,
9347
9556
  TaskSampleProcess,
@@ -9352,13 +9561,15 @@ export {
9352
9561
  TaskSystemInfo,
9353
9562
  TasksManager,
9354
9563
  TasksRegistry,
9355
- Text5 as Text,
9564
+ Text6 as Text,
9356
9565
  TextBlock,
9357
9566
  activateRelease,
9358
9567
  appendDeployLog,
9359
9568
  appendTaskIpcLog,
9360
9569
  applyRuntimeParam,
9361
9570
  applyRuntimePatch,
9571
+ bindingIdentity,
9572
+ bindingMatchesInput,
9362
9573
  bootstrapHost,
9363
9574
  buildBreadcrumb,
9364
9575
  buildDetailBreadcrumb,
@@ -9391,9 +9602,10 @@ export {
9391
9602
  ensureTaskTables,
9392
9603
  ensureTasksRuntime,
9393
9604
  flushTaskIpcLogs,
9605
+ formatBindingKey,
9394
9606
  getArgsInstance,
9395
9607
  getPm2Process,
9396
- createElement2 as h,
9608
+ createElement3 as h,
9397
9609
  initServiceStructure,
9398
9610
  installDeps,
9399
9611
  installOperatorShell,
@@ -9448,6 +9660,7 @@ export {
9448
9660
  runRemoteStatus,
9449
9661
  runShell,
9450
9662
  runTasksLoop,
9663
+ scrollbarGlyphs,
9451
9664
  scrubEnvContent,
9452
9665
  servicePaths,
9453
9666
  setupContext,
@@ -9471,14 +9684,15 @@ export {
9471
9684
  updateServicesRegistryMetadata,
9472
9685
  updateTaskProgress,
9473
9686
  useCallback,
9474
- useEffect2 as useEffect,
9687
+ useEffect3 as useEffect,
9475
9688
  useInput2 as useInput,
9476
9689
  useLayoutEffect,
9477
- useMemo,
9478
- useRef2 as useRef,
9479
- useState3 as useState,
9690
+ useMemo2 as useMemo,
9691
+ useRef3 as useRef,
9692
+ useState4 as useState,
9480
9693
  waitForTaskResult,
9481
9694
  waitPm2,
9695
+ wrapTextLines,
9482
9696
  writeReleaseBuildInfo
9483
9697
  };
9484
9698
  //# sourceMappingURL=index.js.map