@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.cjs CHANGED
@@ -493,6 +493,59 @@ var init_list_components = __esm({
493
493
  }
494
494
  });
495
495
 
496
+ // src/screen/key-bindings.js
497
+ function bindingIdentity(b) {
498
+ return [
499
+ String(b?.key ?? ""),
500
+ b?.meta ? "m" : "",
501
+ b?.ctrl ? "c" : "",
502
+ b?.shift ? "s" : ""
503
+ ].join("|");
504
+ }
505
+ function bindingMatchesInput(binding, input, key) {
506
+ if (!binding?.key) return false;
507
+ let keyMatches = false;
508
+ if (key?.[binding.key]) {
509
+ keyMatches = true;
510
+ } else if (input === binding.key) {
511
+ keyMatches = true;
512
+ } else if (key?.name === binding.key) {
513
+ keyMatches = true;
514
+ }
515
+ if (!keyMatches) return false;
516
+ const modOk = (flag, pressed) => {
517
+ if (flag === true) return !!pressed;
518
+ if (flag === false) return !pressed;
519
+ return !pressed;
520
+ };
521
+ if (!modOk(binding.meta, key?.meta)) return false;
522
+ if (!modOk(binding.ctrl, key?.ctrl)) return false;
523
+ if (binding.shift !== void 0 && !!key?.shift !== !!binding.shift) return false;
524
+ return true;
525
+ }
526
+ function formatBindingKey(binding) {
527
+ let label = KEY_LABELS[binding.key] || binding.key;
528
+ if (binding.shift) label = `\u21E7${label}`;
529
+ if (binding.ctrl) label = `^${label}`;
530
+ if (binding.meta) label = `\u2325${label}`;
531
+ return label;
532
+ }
533
+ var KEY_LABELS;
534
+ var init_key_bindings = __esm({
535
+ "src/screen/key-bindings.js"() {
536
+ KEY_LABELS = {
537
+ escape: "esc",
538
+ leftArrow: "\u2190",
539
+ rightArrow: "\u2192",
540
+ upArrow: "\u2191",
541
+ downArrow: "\u2193",
542
+ return: "enter",
543
+ pageUp: "PgUp",
544
+ pageDown: "PgDn"
545
+ };
546
+ }
547
+ });
548
+
496
549
  // src/screen/screens.js
497
550
  function groupKeyBindings(bindings) {
498
551
  const groups = {};
@@ -506,7 +559,7 @@ function groupKeyBindings(bindings) {
506
559
  order: binding.order || 999
507
560
  };
508
561
  }
509
- groups[caption].keys.push(binding.key);
562
+ groups[caption].keys.push(formatBindingKey(binding));
510
563
  });
511
564
  return Object.values(groups);
512
565
  }
@@ -546,12 +599,14 @@ function formatKeyBindings(bindings, mode = "long") {
546
599
  }
547
600
  function formatKeys(keys) {
548
601
  const keyMap = {
549
- "escape": "esc",
550
- "leftArrow": "\u2190",
551
- "rightArrow": "\u2192",
552
- "upArrow": "\u2191",
553
- "downArrow": "\u2193",
554
- "return": "enter"
602
+ escape: "esc",
603
+ leftArrow: "\u2190",
604
+ rightArrow: "\u2192",
605
+ upArrow: "\u2191",
606
+ downArrow: "\u2193",
607
+ return: "enter",
608
+ pageUp: "PgUp",
609
+ pageDown: "PgDn"
555
610
  };
556
611
  return keys.map((k) => keyMap[k] || k).join("/");
557
612
  }
@@ -591,7 +646,10 @@ async function showScreen(config2) {
591
646
  setKeyBinding: (bindingOrBindings) => {
592
647
  const bindingsToSet = Array.isArray(bindingOrBindings) ? bindingOrBindings : [bindingOrBindings];
593
648
  bindingsToSet.forEach((binding) => {
594
- const existingIndex = keyBindings.findIndex((b) => b.key === binding.key);
649
+ const id = bindingIdentity(binding);
650
+ const existingIndex = keyBindings.findIndex(
651
+ (b) => bindingIdentity(b) === id
652
+ );
595
653
  if (existingIndex >= 0) {
596
654
  const existing = keyBindings[existingIndex];
597
655
  if (existing.protected) {
@@ -615,7 +673,10 @@ async function showScreen(config2) {
615
673
  });
616
674
  },
617
675
  updateKeyBinding: (keyName, updates) => {
618
- const index = keyBindings.findIndex((b) => b.key === keyName);
676
+ const id = updates && (updates.meta != null || updates.ctrl != null || updates.shift != null) ? bindingIdentity({ key: keyName, ...updates }) : null;
677
+ const index = keyBindings.findIndex(
678
+ (b) => id ? bindingIdentity(b) === id : b.key === keyName
679
+ );
619
680
  if (index >= 0) {
620
681
  keyBindings[index] = {
621
682
  ...keyBindings[index],
@@ -623,11 +684,13 @@ async function showScreen(config2) {
623
684
  };
624
685
  }
625
686
  },
626
- removeKeyBinding: (keyName) => {
627
- const index = keyBindings.findIndex((b) => b.key === keyName);
687
+ removeKeyBinding: (keyNameOrBinding) => {
688
+ const index = typeof keyNameOrBinding === "object" && keyNameOrBinding ? keyBindings.findIndex(
689
+ (b) => bindingIdentity(b) === bindingIdentity(keyNameOrBinding)
690
+ ) : keyBindings.findIndex((b) => b.key === keyNameOrBinding);
628
691
  if (index >= 0) {
629
692
  if (keyBindings[index].protected) {
630
- console.warn(`Cannot remove protected key: ${keyName}`);
693
+ console.warn(`Cannot remove protected key: ${keyNameOrBinding}`);
631
694
  return;
632
695
  }
633
696
  keyBindings.splice(index, 1);
@@ -668,24 +731,11 @@ async function showScreen(config2) {
668
731
  }
669
732
  let matchedBinding = null;
670
733
  for (const binding of keyBindings) {
671
- let keyMatches = false;
672
- if (key[binding.key]) {
673
- keyMatches = true;
674
- } else if (input === binding.key) {
675
- keyMatches = true;
676
- } else if (key?.name === binding.key) {
677
- keyMatches = true;
678
- }
679
- if (keyMatches) {
680
- if (binding.enabled === false) {
681
- continue;
682
- }
683
- if (binding.condition && !binding.condition(context)) {
684
- continue;
685
- }
686
- matchedBinding = binding;
687
- break;
688
- }
734
+ if (binding.enabled === false) continue;
735
+ if (!bindingMatchesInput(binding, input, key)) continue;
736
+ if (binding.condition && !binding.condition(context)) continue;
737
+ matchedBinding = binding;
738
+ break;
689
739
  }
690
740
  if (matchedBinding && actions[matchedBinding.action]) {
691
741
  actions[matchedBinding.action]({
@@ -823,6 +873,7 @@ var init_screens = __esm({
823
873
  import_ink3 = require("ink");
824
874
  init_components();
825
875
  init_list_components();
876
+ init_key_bindings();
826
877
  showMenuScreen = showListScreen;
827
878
  showWordGridScreen = showMultiColumnListScreen;
828
879
  }
@@ -913,6 +964,161 @@ var init_ui_elements = __esm({
913
964
  }
914
965
  });
915
966
 
967
+ // src/screen/scrollable-text.js
968
+ function wrapTextLines(text, cols) {
969
+ const w = Math.max(1, Math.floor(Number(cols) || 1));
970
+ const out = [];
971
+ for (const raw of String(text ?? "").split("\n")) {
972
+ if (raw.length === 0) {
973
+ out.push("");
974
+ continue;
975
+ }
976
+ let rest = raw;
977
+ while (rest.length > w) {
978
+ out.push(rest.slice(0, w));
979
+ rest = rest.slice(w);
980
+ }
981
+ if (rest.length > 0) out.push(rest);
982
+ }
983
+ return out;
984
+ }
985
+ function scrollbarGlyphs(viewportRows, totalLines, scrollTop) {
986
+ const view = Math.max(1, Math.floor(viewportRows));
987
+ const total = Math.max(0, Math.floor(totalLines));
988
+ if (total <= view) return null;
989
+ const maxScroll = total - view;
990
+ const thumbSize = Math.max(1, Math.round(view / total * view));
991
+ const travel = Math.max(0, view - thumbSize);
992
+ const s = Math.min(Math.max(0, Math.floor(scrollTop)), maxScroll);
993
+ const thumbStart = maxScroll === 0 ? 0 : Math.round(s / maxScroll * travel);
994
+ const glyphs = [];
995
+ for (let i = 0; i < view; i++) {
996
+ glyphs.push(i >= thumbStart && i < thumbStart + thumbSize ? BAR_THUMB : BAR_TRACK);
997
+ }
998
+ return glyphs;
999
+ }
1000
+ function padEndVisible(s, w) {
1001
+ const t = String(s ?? "");
1002
+ if (t.length >= w) return t.slice(0, w);
1003
+ return t + " ".repeat(w - t.length);
1004
+ }
1005
+ function ScrollableText({
1006
+ ctx,
1007
+ text = "",
1008
+ lines: linesProp,
1009
+ maxHeight,
1010
+ wrap = true,
1011
+ showScrollbar = true,
1012
+ showStatus = true,
1013
+ bindKeys = true,
1014
+ header = null
1015
+ }) {
1016
+ const [scrollTop, setScrollTop] = (0, import_react5.useState)(0);
1017
+ const [, bump] = (0, import_react5.useState)(0);
1018
+ const termRows = process.stdout.rows || 24;
1019
+ const viewportRows = Math.max(
1020
+ 4,
1021
+ maxHeight != null ? Math.floor(maxHeight) : Math.max(8, termRows - 8)
1022
+ );
1023
+ const contentCols = Math.max(20, getScreenWidth() - 4);
1024
+ const barCols = showScrollbar ? 1 : 0;
1025
+ const textWidth = Math.max(8, contentCols - barCols);
1026
+ const allLines = (0, import_react5.useMemo)(() => {
1027
+ if (Array.isArray(linesProp)) return linesProp.map((l) => String(l ?? ""));
1028
+ return wrap ? wrapTextLines(text, textWidth) : String(text ?? "").split("\n");
1029
+ }, [linesProp, text, wrap, textWidth]);
1030
+ const needsBar = showScrollbar && allLines.length > viewportRows;
1031
+ const maxScroll = Math.max(0, allLines.length - viewportRows);
1032
+ const clamped = Math.min(Math.max(0, scrollTop), maxScroll);
1033
+ const visible = allLines.slice(clamped, clamped + viewportRows);
1034
+ const bar = needsBar ? scrollbarGlyphs(viewportRows, allLines.length, clamped) : null;
1035
+ (0, import_react5.useEffect)(() => {
1036
+ setScrollTop((s) => Math.min(s, maxScroll));
1037
+ }, [maxScroll]);
1038
+ const maxScrollRef = (0, import_react5.useRef)(maxScroll);
1039
+ const pageSizeRef = (0, import_react5.useRef)(viewportRows);
1040
+ maxScrollRef.current = maxScroll;
1041
+ pageSizeRef.current = viewportRows;
1042
+ (0, import_react5.useEffect)(() => {
1043
+ if (!ctx || !bindKeys) return void 0;
1044
+ ctx.setKeyBinding(SCROLL_KEYS);
1045
+ ctx.setAction("scrollUp", () => {
1046
+ setScrollTop((s) => Math.max(0, s - 1));
1047
+ bump((n) => n + 1);
1048
+ ctx.update?.();
1049
+ });
1050
+ ctx.setAction("scrollDown", () => {
1051
+ setScrollTop((s) => Math.min(maxScrollRef.current, s + 1));
1052
+ bump((n) => n + 1);
1053
+ ctx.update?.();
1054
+ });
1055
+ ctx.setAction("pageUp", () => {
1056
+ setScrollTop((s) => Math.max(0, s - pageSizeRef.current));
1057
+ bump((n) => n + 1);
1058
+ ctx.update?.();
1059
+ });
1060
+ ctx.setAction("pageDown", () => {
1061
+ setScrollTop((s) => Math.min(maxScrollRef.current, s + pageSizeRef.current));
1062
+ bump((n) => n + 1);
1063
+ ctx.update?.();
1064
+ });
1065
+ return void 0;
1066
+ }, [ctx, bindKeys]);
1067
+ 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" : "");
1068
+ const rowNodes = visible.map((line, i) => {
1069
+ const body = padEndVisible(line, textWidth);
1070
+ const glyph = bar ? bar[i] ?? BAR_TRACK : showScrollbar ? " " : "";
1071
+ const isThumb = glyph === BAR_THUMB;
1072
+ return h5(
1073
+ import_ink5.Text,
1074
+ { key: `L${clamped + i}` },
1075
+ body,
1076
+ glyph ? h5(import_ink5.Text, { color: isThumb ? "cyan" : "gray" }, glyph) : null
1077
+ );
1078
+ });
1079
+ if (bar && visible.length < viewportRows) {
1080
+ for (let i = visible.length; i < viewportRows; i++) {
1081
+ const glyph = bar[i] ?? BAR_TRACK;
1082
+ rowNodes.push(
1083
+ h5(
1084
+ import_ink5.Text,
1085
+ { key: `pad${i}` },
1086
+ padEndVisible("", textWidth),
1087
+ h5(import_ink5.Text, { color: glyph === BAR_THUMB ? "cyan" : "gray" }, glyph)
1088
+ )
1089
+ );
1090
+ }
1091
+ }
1092
+ return h5(
1093
+ import_ink5.Box,
1094
+ { flexDirection: "column" },
1095
+ header == null ? null : typeof header === "string" ? h5(import_ink5.Text, { color: "gray" }, header) : header,
1096
+ showStatus ? h5(import_ink5.Text, { color: "gray" }, status) : null,
1097
+ ...rowNodes
1098
+ );
1099
+ }
1100
+ var import_react5, import_ink5, h5, BAR_THUMB, BAR_TRACK, SCROLL_KEYS;
1101
+ var init_scrollable_text = __esm({
1102
+ "src/screen/scrollable-text.js"() {
1103
+ import_react5 = require("react");
1104
+ import_ink5 = require("ink");
1105
+ init_components();
1106
+ h5 = import_react5.createElement;
1107
+ BAR_THUMB = "#";
1108
+ BAR_TRACK = "|";
1109
+ SCROLL_KEYS = [
1110
+ { key: "upArrow", caption: "scroll", action: "scrollUp", order: 0 },
1111
+ { key: "downArrow", caption: "scroll", action: "scrollDown", order: 0 },
1112
+ { key: "upArrow", meta: true, caption: "page", action: "pageUp", order: 0 },
1113
+ { key: "downArrow", meta: true, caption: "page", action: "pageDown", order: 0 },
1114
+ { key: "upArrow", ctrl: true, caption: "page", action: "pageUp", order: 0 },
1115
+ { key: "downArrow", ctrl: true, caption: "page", action: "pageDown", order: 0 },
1116
+ { key: "pageUp", caption: "page", action: "pageUp", order: 0 },
1117
+ { key: "pageDown", caption: "page", action: "pageDown", order: 0 }
1118
+ ];
1119
+ }
1120
+ });
1121
+
916
1122
  // src/screen/utils.js
917
1123
  function buildBreadcrumb(parts) {
918
1124
  if (parts.length === 0) return "";
@@ -1048,15 +1254,17 @@ async function load() {
1048
1254
  loadPromise = Promise.resolve();
1049
1255
  return loadPromise;
1050
1256
  }
1051
- var import_react5, import_ink5, loadPromise;
1257
+ var import_react6, import_ink6, loadPromise;
1052
1258
  var init_screen = __esm({
1053
1259
  "src/screen/index.js"() {
1054
- import_react5 = __toESM(require("react"), 1);
1055
- import_ink5 = require("ink");
1260
+ import_react6 = __toESM(require("react"), 1);
1261
+ import_ink6 = require("ink");
1056
1262
  init_screens();
1057
1263
  init_list_components();
1058
1264
  init_components();
1059
1265
  init_ui_elements();
1266
+ init_scrollable_text();
1267
+ init_key_bindings();
1060
1268
  init_utils();
1061
1269
  init_footer_builder();
1062
1270
  loadPromise = null;
@@ -1073,7 +1281,7 @@ __export(src_exports, {
1073
1281
  AbstractTask: () => AbstractTask,
1074
1282
  Args: () => Args,
1075
1283
  Aws: () => Aws,
1076
- Box: () => import_ink5.Box,
1284
+ Box: () => import_ink6.Box,
1077
1285
  Db: () => Db,
1078
1286
  Divider: () => Divider,
1079
1287
  FileDatabase: () => FileDatabase,
@@ -1089,7 +1297,7 @@ __export(src_exports, {
1089
1297
  MultiColumnListWithPreviewComponent: () => MultiColumnListWithPreviewComponent,
1090
1298
  Params: () => Params,
1091
1299
  REMOTE_CLI_REL: () => REMOTE_CLI_REL,
1092
- React: () => import_react5.default,
1300
+ React: () => import_react6.default,
1093
1301
  S3: () => S3,
1094
1302
  SERVICE_TASK_NAMES: () => SERVICE_TASK_NAMES,
1095
1303
  ScreenBody: () => ScreenBody,
@@ -1098,6 +1306,7 @@ __export(src_exports, {
1098
1306
  ScreenFooter: () => ScreenFooter,
1099
1307
  ScreenRow: () => ScreenRow,
1100
1308
  ScreenTitle: () => ScreenTitle,
1309
+ ScrollableText: () => ScrollableText,
1101
1310
  TaskGetLogs: () => TaskGetLogs,
1102
1311
  TaskPing: () => TaskPing,
1103
1312
  TaskSampleProcess: () => TaskSampleProcess,
@@ -1108,13 +1317,15 @@ __export(src_exports, {
1108
1317
  TaskSystemInfo: () => TaskSystemInfo,
1109
1318
  TasksManager: () => TasksManager,
1110
1319
  TasksRegistry: () => TasksRegistry,
1111
- Text: () => import_ink5.Text,
1320
+ Text: () => import_ink6.Text,
1112
1321
  TextBlock: () => TextBlock,
1113
1322
  activateRelease: () => activateRelease,
1114
1323
  appendDeployLog: () => appendDeployLog,
1115
1324
  appendTaskIpcLog: () => appendTaskIpcLog,
1116
1325
  applyRuntimeParam: () => applyRuntimeParam,
1117
1326
  applyRuntimePatch: () => applyRuntimePatch,
1327
+ bindingIdentity: () => bindingIdentity,
1328
+ bindingMatchesInput: () => bindingMatchesInput,
1118
1329
  bootstrapHost: () => bootstrapHost,
1119
1330
  buildBreadcrumb: () => buildBreadcrumb,
1120
1331
  buildDetailBreadcrumb: () => buildDetailBreadcrumb,
@@ -1147,9 +1358,10 @@ __export(src_exports, {
1147
1358
  ensureTaskTables: () => ensureTaskTables,
1148
1359
  ensureTasksRuntime: () => ensureTasksRuntime,
1149
1360
  flushTaskIpcLogs: () => flushTaskIpcLogs,
1361
+ formatBindingKey: () => formatBindingKey,
1150
1362
  getArgsInstance: () => getArgsInstance,
1151
1363
  getPm2Process: () => getPm2Process,
1152
- h: () => import_react5.createElement,
1364
+ h: () => import_react6.createElement,
1153
1365
  initServiceStructure: () => initServiceStructure,
1154
1366
  installDeps: () => installDeps,
1155
1367
  installOperatorShell: () => installOperatorShell,
@@ -1166,7 +1378,7 @@ __export(src_exports, {
1166
1378
  load: () => load,
1167
1379
  loadServices: () => loadServices,
1168
1380
  matchesParsedPattern: () => matchesParsedPattern,
1169
- memo: () => import_react5.memo,
1381
+ memo: () => import_react6.memo,
1170
1382
  mergeAllowedTasksWithServiceTasks: () => mergeAllowedTasksWithServiceTasks,
1171
1383
  nextTimeMatch: () => nextTimeMatch,
1172
1384
  normalizeAllowedTasks: () => normalizeAllowedTasks,
@@ -1204,6 +1416,7 @@ __export(src_exports, {
1204
1416
  runRemoteStatus: () => runRemoteStatus,
1205
1417
  runShell: () => runShell,
1206
1418
  runTasksLoop: () => runTasksLoop,
1419
+ scrollbarGlyphs: () => scrollbarGlyphs,
1207
1420
  scrubEnvContent: () => scrubEnvContent,
1208
1421
  servicePaths: () => servicePaths,
1209
1422
  setupContext: () => setupContext,
@@ -1226,15 +1439,16 @@ __export(src_exports, {
1226
1439
  unregisterServicesRegistry: () => unregisterServicesRegistry,
1227
1440
  updateServicesRegistryMetadata: () => updateServicesRegistryMetadata,
1228
1441
  updateTaskProgress: () => updateTaskProgress,
1229
- useCallback: () => import_react5.useCallback,
1230
- useEffect: () => import_react5.useEffect,
1231
- useInput: () => import_ink5.useInput,
1232
- useLayoutEffect: () => import_react5.useLayoutEffect,
1233
- useMemo: () => import_react5.useMemo,
1234
- useRef: () => import_react5.useRef,
1235
- useState: () => import_react5.useState,
1442
+ useCallback: () => import_react6.useCallback,
1443
+ useEffect: () => import_react6.useEffect,
1444
+ useInput: () => import_ink6.useInput,
1445
+ useLayoutEffect: () => import_react6.useLayoutEffect,
1446
+ useMemo: () => import_react6.useMemo,
1447
+ useRef: () => import_react6.useRef,
1448
+ useState: () => import_react6.useState,
1236
1449
  waitForTaskResult: () => waitForTaskResult,
1237
1450
  waitPm2: () => waitPm2,
1451
+ wrapTextLines: () => wrapTextLines,
1238
1452
  writeReleaseBuildInfo: () => writeReleaseBuildInfo
1239
1453
  });
1240
1454
  module.exports = __toCommonJS(src_exports);
@@ -9508,6 +9722,7 @@ var TasksManager = class _TasksManager {
9508
9722
  ScreenFooter,
9509
9723
  ScreenRow,
9510
9724
  ScreenTitle,
9725
+ ScrollableText,
9511
9726
  TaskGetLogs,
9512
9727
  TaskPing,
9513
9728
  TaskSampleProcess,
@@ -9525,6 +9740,8 @@ var TasksManager = class _TasksManager {
9525
9740
  appendTaskIpcLog,
9526
9741
  applyRuntimeParam,
9527
9742
  applyRuntimePatch,
9743
+ bindingIdentity,
9744
+ bindingMatchesInput,
9528
9745
  bootstrapHost,
9529
9746
  buildBreadcrumb,
9530
9747
  buildDetailBreadcrumb,
@@ -9557,6 +9774,7 @@ var TasksManager = class _TasksManager {
9557
9774
  ensureTaskTables,
9558
9775
  ensureTasksRuntime,
9559
9776
  flushTaskIpcLogs,
9777
+ formatBindingKey,
9560
9778
  getArgsInstance,
9561
9779
  getPm2Process,
9562
9780
  h,
@@ -9614,6 +9832,7 @@ var TasksManager = class _TasksManager {
9614
9832
  runRemoteStatus,
9615
9833
  runShell,
9616
9834
  runTasksLoop,
9835
+ scrollbarGlyphs,
9617
9836
  scrubEnvContent,
9618
9837
  servicePaths,
9619
9838
  setupContext,
@@ -9645,6 +9864,7 @@ var TasksManager = class _TasksManager {
9645
9864
  useState,
9646
9865
  waitForTaskResult,
9647
9866
  waitPm2,
9867
+ wrapTextLines,
9648
9868
  writeReleaseBuildInfo
9649
9869
  });
9650
9870
  //# sourceMappingURL=index.cjs.map