@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.cjs CHANGED
@@ -33,6 +33,7 @@ __export(screen_exports, {
33
33
  BAR_TRACK: () => BAR_TRACK,
34
34
  Box: () => import_ink6.Box,
35
35
  Divider: () => Divider,
36
+ FOOTER_HOTKEY_STYLE: () => FOOTER_HOTKEY_STYLE,
36
37
  FooterPresets: () => FooterPresets,
37
38
  GridCell: () => GridCell,
38
39
  InputField: () => InputField,
@@ -58,6 +59,7 @@ __export(screen_exports, {
58
59
  buildFooter: () => buildFooter,
59
60
  clampScroll: () => clampScroll,
60
61
  formatBindingKey: () => formatBindingKey,
62
+ formatFooterHotkey: () => formatFooterHotkey,
61
63
  h: () => import_react6.createElement,
62
64
  isScrolledToBottom: () => isScrolledToBottom,
63
65
  load: () => load,
@@ -65,6 +67,7 @@ __export(screen_exports, {
65
67
  nextScrollAfterContentChange: () => nextScrollAfterContentChange,
66
68
  nextScrollAfterUserMove: () => nextScrollAfterUserMove,
67
69
  organizeFooterMessages: () => organizeFooterMessages,
70
+ padEndVisible: () => padEndVisible,
68
71
  scrollbarGlyphs: () => scrollbarGlyphs,
69
72
  showListScreen: () => showListScreen,
70
73
  showMenuScreen: () => showMenuScreen,
@@ -72,6 +75,8 @@ __export(screen_exports, {
72
75
  showMultiColumnListWithPreviewScreen: () => showMultiColumnListWithPreviewScreen,
73
76
  showScreen: () => showScreen,
74
77
  showWordGridScreen: () => showWordGridScreen,
78
+ sliceVisible: () => sliceVisible,
79
+ stripAnsi: () => stripAnsi,
75
80
  useCallback: () => import_react6.useCallback,
76
81
  useEffect: () => import_react6.useEffect,
77
82
  useInput: () => import_ink6.useInput,
@@ -79,6 +84,7 @@ __export(screen_exports, {
79
84
  useMemo: () => import_react6.useMemo,
80
85
  useRef: () => import_react6.useRef,
81
86
  useState: () => import_react6.useState,
87
+ visibleWidth: () => visibleWidth,
82
88
  wrapTextLines: () => wrapTextLines
83
89
  });
84
90
  module.exports = __toCommonJS(screen_exports);
@@ -674,23 +680,24 @@ function formatKeyBindings(bindings, mode = "long") {
674
680
  })));
675
681
  groups.sort((a, b) => a.order - b.order);
676
682
  const items = [];
677
- groups.forEach((group) => {
683
+ groups.forEach((group, groupIndex) => {
678
684
  const bindingWithCustom = resolvedBindings.find(
679
- (b) => group.keys.includes(b.key) && typeof b.resolvedCaption !== "string"
685
+ (b) => group.keys.includes(formatBindingKey(b)) && typeof b.resolvedCaption !== "string"
680
686
  );
681
687
  if (bindingWithCustom && bindingWithCustom.resolvedCaption) {
682
688
  items.push(bindingWithCustom.resolvedCaption);
683
689
  } else {
684
- const keyStr = formatKeys(group.keys);
685
- if (mode === "long") {
686
- items.push(`${keyStr} to ${group.caption}`);
687
- } else {
688
- items.push(keyStr);
689
- }
690
+ items.push(formatFooterHotkey(formatKeys(group.keys), group.caption, mode, `kb-${groupIndex}`));
690
691
  }
691
692
  });
692
693
  return items;
693
694
  }
695
+ var FOOTER_HOTKEY_STYLE = { bold: true, color: "white", dimColor: false };
696
+ function formatFooterHotkey(keyStr, caption = "", mode = "long", reactKey = "hotkey") {
697
+ const keyNode = (0, import_react3.createElement)(import_ink3.Text, { key: `${reactKey}-key`, ...FOOTER_HOTKEY_STYLE }, keyStr);
698
+ if (mode !== "long" || !caption) return keyNode;
699
+ return (0, import_react3.createElement)(import_ink3.Text, { key: reactKey, dimColor: true, color: "white" }, keyNode, ` to ${caption}`);
700
+ }
694
701
  function formatKeys(keys) {
695
702
  const keyMap = {
696
703
  escape: "esc",
@@ -1067,8 +1074,45 @@ function nextScrollAfterContentChange({ following, scrollTop, maxScroll }) {
1067
1074
  return { scrollTop: next, following: isScrolledToBottom(next, max) };
1068
1075
  }
1069
1076
 
1070
- // src/screen/scrollable-text.js
1071
- // Removed: intermediate assignment - code will access import variable directly
1077
+ // src/screen/visible-text.js
1078
+ var ANSI_RE = /\u001b(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g;
1079
+ function stripAnsi(text) {
1080
+ return String(text ?? "").replace(ANSI_RE, "");
1081
+ }
1082
+ function visibleWidth(text) {
1083
+ return [...stripAnsi(text)].length;
1084
+ }
1085
+ function sliceVisible(text, cols) {
1086
+ const w = Math.max(0, Math.floor(Number(cols) || 0));
1087
+ const s = String(text ?? "");
1088
+ if (w === 0) return "";
1089
+ let vis = 0;
1090
+ let out = "";
1091
+ let i = 0;
1092
+ while (i < s.length && vis < w) {
1093
+ ANSI_RE.lastIndex = i;
1094
+ const m = ANSI_RE.exec(s);
1095
+ if (m && m.index === i) {
1096
+ out += m[0];
1097
+ i += m[0].length;
1098
+ continue;
1099
+ }
1100
+ const cp = s.codePointAt(i);
1101
+ const ch = String.fromCodePoint(cp);
1102
+ out += ch;
1103
+ i += ch.length;
1104
+ vis += 1;
1105
+ }
1106
+ return out;
1107
+ }
1108
+ function padEndVisible(text, cols) {
1109
+ const w = Math.max(0, Math.floor(Number(cols) || 0));
1110
+ const s = String(text ?? "");
1111
+ const vis = visibleWidth(s);
1112
+ if (vis === w) return s;
1113
+ if (vis > w) return sliceVisible(s, w);
1114
+ return s + " ".repeat(w - vis);
1115
+ }
1072
1116
  function wrapTextLines(text, cols) {
1073
1117
  const w = Math.max(1, Math.floor(Number(cols) || 1));
1074
1118
  const out = [];
@@ -1077,20 +1121,23 @@ function wrapTextLines(text, cols) {
1077
1121
  out.push("");
1078
1122
  continue;
1079
1123
  }
1124
+ if (visibleWidth(raw) <= w) {
1125
+ out.push(raw);
1126
+ continue;
1127
+ }
1080
1128
  let rest = raw;
1081
- while (rest.length > w) {
1082
- out.push(rest.slice(0, w));
1083
- rest = rest.slice(w);
1129
+ while (visibleWidth(rest) > w) {
1130
+ const head = sliceVisible(rest, w);
1131
+ out.push(head);
1132
+ rest = rest.slice(head.length);
1084
1133
  }
1085
1134
  if (rest.length > 0) out.push(rest);
1086
1135
  }
1087
1136
  return out;
1088
1137
  }
1089
- function padEndVisible(s, w) {
1090
- const t = String(s ?? "");
1091
- if (t.length >= w) return t.slice(0, w);
1092
- return t + " ".repeat(w - t.length);
1093
- }
1138
+
1139
+ // src/screen/scrollable-text.js
1140
+ // Removed: intermediate assignment - code will access import variable directly
1094
1141
  var SCROLL_KEYS = [
1095
1142
  { key: "upArrow", caption: "scroll", action: "scrollUp", order: 0 },
1096
1143
  { key: "downArrow", caption: "scroll", action: "scrollDown", order: 0 },
@@ -1163,14 +1210,17 @@ function ScrollableText({
1163
1210
  }, [ctx, bindKeys, followBottom]);
1164
1211
  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" : "");
1165
1212
  const rowNodes = visible.map((line, i) => {
1166
- const body = padEndVisible(line, textWidth);
1167
1213
  const glyph = bar ? bar[i] ?? BAR_TRACK : showScrollbar ? " " : "";
1168
1214
  const isThumb = glyph === BAR_THUMB;
1169
1215
  return h5(
1170
- import_ink5.Text,
1171
- { key: `L${clamped + i}` },
1172
- body,
1173
- glyph ? h5(import_ink5.Text, { color: isThumb ? "cyan" : "gray" }, glyph) : null
1216
+ import_ink5.Box,
1217
+ { key: `L${clamped + i}`, flexDirection: "row", width: contentCols, flexShrink: 0 },
1218
+ h5(
1219
+ import_ink5.Box,
1220
+ { width: textWidth, flexShrink: 0 },
1221
+ h5(import_ink5.Text, { wrap: "truncate" }, padEndVisible(line, textWidth))
1222
+ ),
1223
+ showScrollbar ? h5(import_ink5.Box, { width: 1, flexShrink: 0 }, h5(import_ink5.Text, { color: isThumb ? "cyan" : "gray" }, glyph)) : null
1174
1224
  );
1175
1225
  });
1176
1226
  if (bar && visible.length < viewportRows) {
@@ -1178,10 +1228,10 @@ function ScrollableText({
1178
1228
  const glyph = bar[i] ?? BAR_TRACK;
1179
1229
  rowNodes.push(
1180
1230
  h5(
1181
- import_ink5.Text,
1182
- { key: `pad${i}` },
1183
- padEndVisible("", textWidth),
1184
- h5(import_ink5.Text, { color: glyph === BAR_THUMB ? "cyan" : "gray" }, glyph)
1231
+ import_ink5.Box,
1232
+ { key: `pad${i}`, flexDirection: "row", width: contentCols, flexShrink: 0 },
1233
+ h5(import_ink5.Box, { width: textWidth, flexShrink: 0 }, h5(import_ink5.Text, {}, padEndVisible("", textWidth))),
1234
+ h5(import_ink5.Box, { width: 1, flexShrink: 0 }, h5(import_ink5.Text, { color: glyph === BAR_THUMB ? "cyan" : "gray" }, glyph))
1185
1235
  )
1186
1236
  );
1187
1237
  }
@@ -1347,6 +1397,7 @@ if (typeof window === "undefined") {
1347
1397
  BAR_TRACK,
1348
1398
  Box,
1349
1399
  Divider,
1400
+ FOOTER_HOTKEY_STYLE,
1350
1401
  FooterPresets,
1351
1402
  GridCell,
1352
1403
  InputField,
@@ -1372,6 +1423,7 @@ if (typeof window === "undefined") {
1372
1423
  buildFooter,
1373
1424
  clampScroll,
1374
1425
  formatBindingKey,
1426
+ formatFooterHotkey,
1375
1427
  h,
1376
1428
  isScrolledToBottom,
1377
1429
  load,
@@ -1379,6 +1431,7 @@ if (typeof window === "undefined") {
1379
1431
  nextScrollAfterContentChange,
1380
1432
  nextScrollAfterUserMove,
1381
1433
  organizeFooterMessages,
1434
+ padEndVisible,
1382
1435
  scrollbarGlyphs,
1383
1436
  showListScreen,
1384
1437
  showMenuScreen,
@@ -1386,6 +1439,8 @@ if (typeof window === "undefined") {
1386
1439
  showMultiColumnListWithPreviewScreen,
1387
1440
  showScreen,
1388
1441
  showWordGridScreen,
1442
+ sliceVisible,
1443
+ stripAnsi,
1389
1444
  useCallback,
1390
1445
  useEffect,
1391
1446
  useInput,
@@ -1393,6 +1448,7 @@ if (typeof window === "undefined") {
1393
1448
  useMemo,
1394
1449
  useRef,
1395
1450
  useState,
1451
+ visibleWidth,
1396
1452
  wrapTextLines
1397
1453
  });
1398
1454
  //# sourceMappingURL=screen.cjs.map