@nmakarov/cli-toolkit 0.81.0 → 0.84.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
@@ -634,23 +634,23 @@ function formatKeyBindings(bindings, mode = "long") {
634
634
  })));
635
635
  groups.sort((a, b) => a.order - b.order);
636
636
  const items = [];
637
- groups.forEach((group) => {
637
+ groups.forEach((group, groupIndex) => {
638
638
  const bindingWithCustom = resolvedBindings.find(
639
- (b) => group.keys.includes(b.key) && typeof b.resolvedCaption !== "string"
639
+ (b) => group.keys.includes(formatBindingKey(b)) && typeof b.resolvedCaption !== "string"
640
640
  );
641
641
  if (bindingWithCustom && bindingWithCustom.resolvedCaption) {
642
642
  items.push(bindingWithCustom.resolvedCaption);
643
643
  } else {
644
- const keyStr = formatKeys(group.keys);
645
- if (mode === "long") {
646
- items.push(`${keyStr} to ${group.caption}`);
647
- } else {
648
- items.push(keyStr);
649
- }
644
+ items.push(formatFooterHotkey(formatKeys(group.keys), group.caption, mode, `kb-${groupIndex}`));
650
645
  }
651
646
  });
652
647
  return items;
653
648
  }
649
+ function formatFooterHotkey(keyStr, caption = "", mode = "long", reactKey = "hotkey") {
650
+ const keyNode = (0, import_react3.createElement)(import_ink3.Text, { key: `${reactKey}-key`, ...FOOTER_HOTKEY_STYLE }, keyStr);
651
+ if (mode !== "long" || !caption) return keyNode;
652
+ return (0, import_react3.createElement)(import_ink3.Text, { key: reactKey, dimColor: true, color: "white" }, keyNode, ` to ${caption}`);
653
+ }
654
654
  function formatKeys(keys) {
655
655
  const keyMap = {
656
656
  escape: "esc",
@@ -920,7 +920,7 @@ async function showMultiColumnListWithPreviewScreen(config2) {
920
920
  }
921
921
  });
922
922
  }
923
- var import_react3, import_ink3, showMenuScreen, showWordGridScreen;
923
+ var import_react3, import_ink3, FOOTER_HOTKEY_STYLE, showMenuScreen, showWordGridScreen;
924
924
  var init_screens = __esm({
925
925
  "src/screen/screens.js"() {
926
926
  import_react3 = require("react");
@@ -928,6 +928,7 @@ var init_screens = __esm({
928
928
  init_components();
929
929
  init_list_components();
930
930
  init_key_bindings();
931
+ FOOTER_HOTKEY_STYLE = { bold: true, color: "white", dimColor: false };
931
932
  showMenuScreen = showListScreen;
932
933
  showWordGridScreen = showMultiColumnListScreen;
933
934
  }
@@ -1042,7 +1043,44 @@ var init_follow_scroll = __esm({
1042
1043
  }
1043
1044
  });
1044
1045
 
1045
- // src/screen/scrollable-text.js
1046
+ // src/screen/visible-text.js
1047
+ function stripAnsi(text) {
1048
+ return String(text ?? "").replace(ANSI_RE, "");
1049
+ }
1050
+ function visibleWidth(text) {
1051
+ return [...stripAnsi(text)].length;
1052
+ }
1053
+ function sliceVisible(text, cols) {
1054
+ const w = Math.max(0, Math.floor(Number(cols) || 0));
1055
+ const s = String(text ?? "");
1056
+ if (w === 0) return "";
1057
+ let vis = 0;
1058
+ let out = "";
1059
+ let i = 0;
1060
+ while (i < s.length && vis < w) {
1061
+ ANSI_RE.lastIndex = i;
1062
+ const m = ANSI_RE.exec(s);
1063
+ if (m && m.index === i) {
1064
+ out += m[0];
1065
+ i += m[0].length;
1066
+ continue;
1067
+ }
1068
+ const cp3 = s.codePointAt(i);
1069
+ const ch = String.fromCodePoint(cp3);
1070
+ out += ch;
1071
+ i += ch.length;
1072
+ vis += 1;
1073
+ }
1074
+ return out;
1075
+ }
1076
+ function padEndVisible(text, cols) {
1077
+ const w = Math.max(0, Math.floor(Number(cols) || 0));
1078
+ const s = String(text ?? "");
1079
+ const vis = visibleWidth(s);
1080
+ if (vis === w) return s;
1081
+ if (vis > w) return sliceVisible(s, w);
1082
+ return s + " ".repeat(w - vis);
1083
+ }
1046
1084
  function wrapTextLines(text, cols) {
1047
1085
  const w = Math.max(1, Math.floor(Number(cols) || 1));
1048
1086
  const out = [];
@@ -1051,20 +1089,28 @@ function wrapTextLines(text, cols) {
1051
1089
  out.push("");
1052
1090
  continue;
1053
1091
  }
1092
+ if (visibleWidth(raw) <= w) {
1093
+ out.push(raw);
1094
+ continue;
1095
+ }
1054
1096
  let rest = raw;
1055
- while (rest.length > w) {
1056
- out.push(rest.slice(0, w));
1057
- rest = rest.slice(w);
1097
+ while (visibleWidth(rest) > w) {
1098
+ const head = sliceVisible(rest, w);
1099
+ out.push(head);
1100
+ rest = rest.slice(head.length);
1058
1101
  }
1059
1102
  if (rest.length > 0) out.push(rest);
1060
1103
  }
1061
1104
  return out;
1062
1105
  }
1063
- function padEndVisible(s, w) {
1064
- const t = String(s ?? "");
1065
- if (t.length >= w) return t.slice(0, w);
1066
- return t + " ".repeat(w - t.length);
1067
- }
1106
+ var ANSI_RE;
1107
+ var init_visible_text = __esm({
1108
+ "src/screen/visible-text.js"() {
1109
+ ANSI_RE = /\u001b(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g;
1110
+ }
1111
+ });
1112
+
1113
+ // src/screen/scrollable-text.js
1068
1114
  function ScrollableText({
1069
1115
  ctx,
1070
1116
  text = "",
@@ -1132,14 +1178,17 @@ function ScrollableText({
1132
1178
  }, [ctx, bindKeys, followBottom]);
1133
1179
  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" : "");
1134
1180
  const rowNodes = visible.map((line, i) => {
1135
- const body = padEndVisible(line, textWidth);
1136
1181
  const glyph = bar ? bar[i] ?? BAR_TRACK : showScrollbar ? " " : "";
1137
1182
  const isThumb = glyph === BAR_THUMB;
1138
1183
  return h5(
1139
- import_ink5.Text,
1140
- { key: `L${clamped + i}` },
1141
- body,
1142
- glyph ? h5(import_ink5.Text, { color: isThumb ? "cyan" : "gray" }, glyph) : null
1184
+ import_ink5.Box,
1185
+ { key: `L${clamped + i}`, flexDirection: "row", width: contentCols, flexShrink: 0 },
1186
+ h5(
1187
+ import_ink5.Box,
1188
+ { width: textWidth, flexShrink: 0 },
1189
+ h5(import_ink5.Text, { wrap: "truncate" }, padEndVisible(line, textWidth))
1190
+ ),
1191
+ showScrollbar ? h5(import_ink5.Box, { width: 1, flexShrink: 0 }, h5(import_ink5.Text, { color: isThumb ? "cyan" : "gray" }, glyph)) : null
1143
1192
  );
1144
1193
  });
1145
1194
  if (bar && visible.length < viewportRows) {
@@ -1147,10 +1196,10 @@ function ScrollableText({
1147
1196
  const glyph = bar[i] ?? BAR_TRACK;
1148
1197
  rowNodes.push(
1149
1198
  h5(
1150
- import_ink5.Text,
1151
- { key: `pad${i}` },
1152
- padEndVisible("", textWidth),
1153
- h5(import_ink5.Text, { color: glyph === BAR_THUMB ? "cyan" : "gray" }, glyph)
1199
+ import_ink5.Box,
1200
+ { key: `pad${i}`, flexDirection: "row", width: contentCols, flexShrink: 0 },
1201
+ h5(import_ink5.Box, { width: textWidth, flexShrink: 0 }, h5(import_ink5.Text, {}, padEndVisible("", textWidth))),
1202
+ h5(import_ink5.Box, { width: 1, flexShrink: 0 }, h5(import_ink5.Text, { color: glyph === BAR_THUMB ? "cyan" : "gray" }, glyph))
1154
1203
  )
1155
1204
  );
1156
1205
  }
@@ -1171,7 +1220,9 @@ var init_scrollable_text = __esm({
1171
1220
  init_components();
1172
1221
  init_scrollbar();
1173
1222
  init_follow_scroll();
1223
+ init_visible_text();
1174
1224
  init_scrollbar();
1225
+ init_visible_text();
1175
1226
  h5 = import_react5.createElement;
1176
1227
  SCROLL_KEYS = [
1177
1228
  { key: "upArrow", caption: "scroll", action: "scrollUp", order: 0 },
@@ -1326,6 +1377,7 @@ var init_screen = __esm({
1326
1377
  init_components();
1327
1378
  init_ui_elements();
1328
1379
  init_scrollable_text();
1380
+ init_visible_text();
1329
1381
  init_follow_scroll();
1330
1382
  init_scrollbar();
1331
1383
  init_key_bindings();
@@ -1351,6 +1403,7 @@ __export(src_exports, {
1351
1403
  DEFAULT_RUNTIME_PARAM_SPECS: () => DEFAULT_RUNTIME_PARAM_SPECS,
1352
1404
  Db: () => Db,
1353
1405
  Divider: () => Divider,
1406
+ FOOTER_HOTKEY_STYLE: () => FOOTER_HOTKEY_STYLE,
1354
1407
  FileDatabase: () => FileDatabase,
1355
1408
  FileDatabaseError: () => FileDatabaseError,
1356
1409
  FooterPresets: () => FooterPresets,
@@ -1435,6 +1488,7 @@ __export(src_exports, {
1435
1488
  ensureTasksRuntime: () => ensureTasksRuntime,
1436
1489
  flushTaskIpcLogs: () => flushTaskIpcLogs,
1437
1490
  formatBindingKey: () => formatBindingKey,
1491
+ formatFooterHotkey: () => formatFooterHotkey,
1438
1492
  getArgsInstance: () => getArgsInstance,
1439
1493
  getPm2Process: () => getPm2Process,
1440
1494
  h: () => import_react6.createElement,
@@ -1465,6 +1519,7 @@ __export(src_exports, {
1465
1519
  npmEnv: () => npmEnv,
1466
1520
  npmInstallEnv: () => npmInstallEnv,
1467
1521
  organizeFooterMessages: () => organizeFooterMessages,
1522
+ padEndVisible: () => padEndVisible,
1468
1523
  parseGitHost: () => parseGitHost,
1469
1524
  parsePm2Jlist: () => parsePm2Jlist,
1470
1525
  prepareGitHost: () => prepareGitHost,
@@ -1510,9 +1565,11 @@ __export(src_exports, {
1510
1565
  showMultiColumnListWithPreviewScreen: () => showMultiColumnListWithPreviewScreen,
1511
1566
  showScreen: () => showScreen,
1512
1567
  showWordGridScreen: () => showWordGridScreen,
1568
+ sliceVisible: () => sliceVisible,
1513
1569
  sshRun: () => sshRun,
1514
1570
  startPm2: () => startPm2,
1515
1571
  stopPm2: () => stopPm2,
1572
+ stripAnsi: () => stripAnsi,
1516
1573
  syncEnv: () => syncEnv,
1517
1574
  taskHistoryInsertFromQueueRow: () => taskHistoryInsertFromQueueRow,
1518
1575
  timeMatcher: () => timeMatcher,
@@ -1529,6 +1586,7 @@ __export(src_exports, {
1529
1586
  useMemo: () => import_react6.useMemo,
1530
1587
  useRef: () => import_react6.useRef,
1531
1588
  useState: () => import_react6.useState,
1589
+ visibleWidth: () => visibleWidth,
1532
1590
  waitForTaskResult: () => waitForTaskResult,
1533
1591
  waitPm2: () => waitPm2,
1534
1592
  wrapTextLines: () => wrapTextLines,
@@ -10329,6 +10387,7 @@ var TasksManager = class _TasksManager {
10329
10387
  DEFAULT_RUNTIME_PARAM_SPECS,
10330
10388
  Db,
10331
10389
  Divider,
10390
+ FOOTER_HOTKEY_STYLE,
10332
10391
  FileDatabase,
10333
10392
  FileDatabaseError,
10334
10393
  FooterPresets,
@@ -10413,6 +10472,7 @@ var TasksManager = class _TasksManager {
10413
10472
  ensureTasksRuntime,
10414
10473
  flushTaskIpcLogs,
10415
10474
  formatBindingKey,
10475
+ formatFooterHotkey,
10416
10476
  getArgsInstance,
10417
10477
  getPm2Process,
10418
10478
  h,
@@ -10443,6 +10503,7 @@ var TasksManager = class _TasksManager {
10443
10503
  npmEnv,
10444
10504
  npmInstallEnv,
10445
10505
  organizeFooterMessages,
10506
+ padEndVisible,
10446
10507
  parseGitHost,
10447
10508
  parsePm2Jlist,
10448
10509
  prepareGitHost,
@@ -10488,9 +10549,11 @@ var TasksManager = class _TasksManager {
10488
10549
  showMultiColumnListWithPreviewScreen,
10489
10550
  showScreen,
10490
10551
  showWordGridScreen,
10552
+ sliceVisible,
10491
10553
  sshRun,
10492
10554
  startPm2,
10493
10555
  stopPm2,
10556
+ stripAnsi,
10494
10557
  syncEnv,
10495
10558
  taskHistoryInsertFromQueueRow,
10496
10559
  timeMatcher,
@@ -10507,6 +10570,7 @@ var TasksManager = class _TasksManager {
10507
10570
  useMemo,
10508
10571
  useRef,
10509
10572
  useState,
10573
+ visibleWidth,
10510
10574
  waitForTaskResult,
10511
10575
  waitPm2,
10512
10576
  wrapTextLines,