@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/screen.js CHANGED
@@ -591,23 +591,24 @@ function formatKeyBindings(bindings, mode = "long") {
591
591
  })));
592
592
  groups.sort((a, b) => a.order - b.order);
593
593
  const items = [];
594
- groups.forEach((group) => {
594
+ groups.forEach((group, groupIndex) => {
595
595
  const bindingWithCustom = resolvedBindings.find(
596
- (b) => group.keys.includes(b.key) && typeof b.resolvedCaption !== "string"
596
+ (b) => group.keys.includes(formatBindingKey(b)) && typeof b.resolvedCaption !== "string"
597
597
  );
598
598
  if (bindingWithCustom && bindingWithCustom.resolvedCaption) {
599
599
  items.push(bindingWithCustom.resolvedCaption);
600
600
  } else {
601
- const keyStr = formatKeys(group.keys);
602
- if (mode === "long") {
603
- items.push(`${keyStr} to ${group.caption}`);
604
- } else {
605
- items.push(keyStr);
606
- }
601
+ items.push(formatFooterHotkey(formatKeys(group.keys), group.caption, mode, `kb-${groupIndex}`));
607
602
  }
608
603
  });
609
604
  return items;
610
605
  }
606
+ var FOOTER_HOTKEY_STYLE = { bold: true, color: "white", dimColor: false };
607
+ function formatFooterHotkey(keyStr, caption = "", mode = "long", reactKey = "hotkey") {
608
+ const keyNode = h3(Text3, { key: `${reactKey}-key`, ...FOOTER_HOTKEY_STYLE }, keyStr);
609
+ if (mode !== "long" || !caption) return keyNode;
610
+ return h3(Text3, { key: reactKey, dimColor: true, color: "white" }, keyNode, ` to ${caption}`);
611
+ }
611
612
  function formatKeys(keys) {
612
613
  const keyMap = {
613
614
  escape: "esc",
@@ -984,8 +985,45 @@ function nextScrollAfterContentChange({ following, scrollTop, maxScroll }) {
984
985
  return { scrollTop: next, following: isScrolledToBottom(next, max) };
985
986
  }
986
987
 
987
- // src/screen/scrollable-text.js
988
- var h5 = createElement2;
988
+ // src/screen/visible-text.js
989
+ var ANSI_RE = /\u001b(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g;
990
+ function stripAnsi(text) {
991
+ return String(text ?? "").replace(ANSI_RE, "");
992
+ }
993
+ function visibleWidth(text) {
994
+ return [...stripAnsi(text)].length;
995
+ }
996
+ function sliceVisible(text, cols) {
997
+ const w = Math.max(0, Math.floor(Number(cols) || 0));
998
+ const s = String(text ?? "");
999
+ if (w === 0) return "";
1000
+ let vis = 0;
1001
+ let out = "";
1002
+ let i = 0;
1003
+ while (i < s.length && vis < w) {
1004
+ ANSI_RE.lastIndex = i;
1005
+ const m = ANSI_RE.exec(s);
1006
+ if (m && m.index === i) {
1007
+ out += m[0];
1008
+ i += m[0].length;
1009
+ continue;
1010
+ }
1011
+ const cp = s.codePointAt(i);
1012
+ const ch = String.fromCodePoint(cp);
1013
+ out += ch;
1014
+ i += ch.length;
1015
+ vis += 1;
1016
+ }
1017
+ return out;
1018
+ }
1019
+ function padEndVisible(text, cols) {
1020
+ const w = Math.max(0, Math.floor(Number(cols) || 0));
1021
+ const s = String(text ?? "");
1022
+ const vis = visibleWidth(s);
1023
+ if (vis === w) return s;
1024
+ if (vis > w) return sliceVisible(s, w);
1025
+ return s + " ".repeat(w - vis);
1026
+ }
989
1027
  function wrapTextLines(text, cols) {
990
1028
  const w = Math.max(1, Math.floor(Number(cols) || 1));
991
1029
  const out = [];
@@ -994,20 +1032,23 @@ function wrapTextLines(text, cols) {
994
1032
  out.push("");
995
1033
  continue;
996
1034
  }
1035
+ if (visibleWidth(raw) <= w) {
1036
+ out.push(raw);
1037
+ continue;
1038
+ }
997
1039
  let rest = raw;
998
- while (rest.length > w) {
999
- out.push(rest.slice(0, w));
1000
- rest = rest.slice(w);
1040
+ while (visibleWidth(rest) > w) {
1041
+ const head = sliceVisible(rest, w);
1042
+ out.push(head);
1043
+ rest = rest.slice(head.length);
1001
1044
  }
1002
1045
  if (rest.length > 0) out.push(rest);
1003
1046
  }
1004
1047
  return out;
1005
1048
  }
1006
- function padEndVisible(s, w) {
1007
- const t = String(s ?? "");
1008
- if (t.length >= w) return t.slice(0, w);
1009
- return t + " ".repeat(w - t.length);
1010
- }
1049
+
1050
+ // src/screen/scrollable-text.js
1051
+ var h5 = createElement2;
1011
1052
  var SCROLL_KEYS = [
1012
1053
  { key: "upArrow", caption: "scroll", action: "scrollUp", order: 0 },
1013
1054
  { key: "downArrow", caption: "scroll", action: "scrollDown", order: 0 },
@@ -1080,14 +1121,17 @@ function ScrollableText({
1080
1121
  }, [ctx, bindKeys, followBottom]);
1081
1122
  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" : "");
1082
1123
  const rowNodes = visible.map((line, i) => {
1083
- const body = padEndVisible(line, textWidth);
1084
1124
  const glyph = bar ? bar[i] ?? BAR_TRACK : showScrollbar ? " " : "";
1085
1125
  const isThumb = glyph === BAR_THUMB;
1086
1126
  return h5(
1087
- Text5,
1088
- { key: `L${clamped + i}` },
1089
- body,
1090
- glyph ? h5(Text5, { color: isThumb ? "cyan" : "gray" }, glyph) : null
1127
+ Box4,
1128
+ { key: `L${clamped + i}`, flexDirection: "row", width: contentCols, flexShrink: 0 },
1129
+ h5(
1130
+ Box4,
1131
+ { width: textWidth, flexShrink: 0 },
1132
+ h5(Text5, { wrap: "truncate" }, padEndVisible(line, textWidth))
1133
+ ),
1134
+ showScrollbar ? h5(Box4, { width: 1, flexShrink: 0 }, h5(Text5, { color: isThumb ? "cyan" : "gray" }, glyph)) : null
1091
1135
  );
1092
1136
  });
1093
1137
  if (bar && visible.length < viewportRows) {
@@ -1095,10 +1139,10 @@ function ScrollableText({
1095
1139
  const glyph = bar[i] ?? BAR_TRACK;
1096
1140
  rowNodes.push(
1097
1141
  h5(
1098
- Text5,
1099
- { key: `pad${i}` },
1100
- padEndVisible("", textWidth),
1101
- h5(Text5, { color: glyph === BAR_THUMB ? "cyan" : "gray" }, glyph)
1142
+ Box4,
1143
+ { key: `pad${i}`, flexDirection: "row", width: contentCols, flexShrink: 0 },
1144
+ h5(Box4, { width: textWidth, flexShrink: 0 }, h5(Text5, {}, padEndVisible("", textWidth))),
1145
+ h5(Box4, { width: 1, flexShrink: 0 }, h5(Text5, { color: glyph === BAR_THUMB ? "cyan" : "gray" }, glyph))
1102
1146
  )
1103
1147
  );
1104
1148
  }
@@ -1251,6 +1295,7 @@ export {
1251
1295
  BAR_TRACK,
1252
1296
  Box5 as Box,
1253
1297
  Divider,
1298
+ FOOTER_HOTKEY_STYLE,
1254
1299
  FooterPresets,
1255
1300
  GridCell,
1256
1301
  InputField,
@@ -1276,6 +1321,7 @@ export {
1276
1321
  buildFooter,
1277
1322
  clampScroll,
1278
1323
  formatBindingKey,
1324
+ formatFooterHotkey,
1279
1325
  createElement3 as h,
1280
1326
  isScrolledToBottom,
1281
1327
  load,
@@ -1283,6 +1329,7 @@ export {
1283
1329
  nextScrollAfterContentChange,
1284
1330
  nextScrollAfterUserMove,
1285
1331
  organizeFooterMessages,
1332
+ padEndVisible,
1286
1333
  scrollbarGlyphs,
1287
1334
  showListScreen,
1288
1335
  showMenuScreen,
@@ -1290,6 +1337,8 @@ export {
1290
1337
  showMultiColumnListWithPreviewScreen,
1291
1338
  showScreen,
1292
1339
  showWordGridScreen,
1340
+ sliceVisible,
1341
+ stripAnsi,
1293
1342
  useCallback,
1294
1343
  useEffect3 as useEffect,
1295
1344
  useInput2 as useInput,
@@ -1297,6 +1346,7 @@ export {
1297
1346
  useMemo2 as useMemo,
1298
1347
  useRef3 as useRef,
1299
1348
  useState4 as useState,
1349
+ visibleWidth,
1300
1350
  wrapTextLines
1301
1351
  };
1302
1352
  //# sourceMappingURL=screen.js.map