@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/init.js CHANGED
@@ -620,23 +620,23 @@ function formatKeyBindings(bindings, mode = "long") {
620
620
  })));
621
621
  groups.sort((a, b) => a.order - b.order);
622
622
  const items = [];
623
- groups.forEach((group) => {
623
+ groups.forEach((group, groupIndex) => {
624
624
  const bindingWithCustom = resolvedBindings.find(
625
- (b) => group.keys.includes(b.key) && typeof b.resolvedCaption !== "string"
625
+ (b) => group.keys.includes(formatBindingKey(b)) && typeof b.resolvedCaption !== "string"
626
626
  );
627
627
  if (bindingWithCustom && bindingWithCustom.resolvedCaption) {
628
628
  items.push(bindingWithCustom.resolvedCaption);
629
629
  } else {
630
- const keyStr = formatKeys(group.keys);
631
- if (mode === "long") {
632
- items.push(`${keyStr} to ${group.caption}`);
633
- } else {
634
- items.push(keyStr);
635
- }
630
+ items.push(formatFooterHotkey(formatKeys(group.keys), group.caption, mode, `kb-${groupIndex}`));
636
631
  }
637
632
  });
638
633
  return items;
639
634
  }
635
+ function formatFooterHotkey(keyStr, caption = "", mode = "long", reactKey = "hotkey") {
636
+ const keyNode = h3(Text3, { key: `${reactKey}-key`, ...FOOTER_HOTKEY_STYLE }, keyStr);
637
+ if (mode !== "long" || !caption) return keyNode;
638
+ return h3(Text3, { key: reactKey, dimColor: true, color: "white" }, keyNode, ` to ${caption}`);
639
+ }
640
640
  function formatKeys(keys) {
641
641
  const keyMap = {
642
642
  escape: "esc",
@@ -906,12 +906,13 @@ async function showMultiColumnListWithPreviewScreen(config2) {
906
906
  }
907
907
  });
908
908
  }
909
- var showMenuScreen, showWordGridScreen;
909
+ var FOOTER_HOTKEY_STYLE, showMenuScreen, showWordGridScreen;
910
910
  var init_screens = __esm({
911
911
  "src/screen/screens.js"() {
912
912
  init_components();
913
913
  init_list_components();
914
914
  init_key_bindings();
915
+ FOOTER_HOTKEY_STYLE = { bold: true, color: "white", dimColor: false };
915
916
  showMenuScreen = showListScreen;
916
917
  showWordGridScreen = showMultiColumnListScreen;
917
918
  }
@@ -1025,9 +1026,44 @@ var init_follow_scroll = __esm({
1025
1026
  }
1026
1027
  });
1027
1028
 
1028
- // src/screen/scrollable-text.js
1029
- import { useState as useState3, useEffect as useEffect2, useMemo, useRef as useRef2, createElement as createElement2 } from "react";
1030
- import { Box as Box4, Text as Text5 } from "ink";
1029
+ // src/screen/visible-text.js
1030
+ function stripAnsi(text) {
1031
+ return String(text ?? "").replace(ANSI_RE, "");
1032
+ }
1033
+ function visibleWidth(text) {
1034
+ return [...stripAnsi(text)].length;
1035
+ }
1036
+ function sliceVisible(text, cols) {
1037
+ const w = Math.max(0, Math.floor(Number(cols) || 0));
1038
+ const s = String(text ?? "");
1039
+ if (w === 0) return "";
1040
+ let vis = 0;
1041
+ let out = "";
1042
+ let i = 0;
1043
+ while (i < s.length && vis < w) {
1044
+ ANSI_RE.lastIndex = i;
1045
+ const m = ANSI_RE.exec(s);
1046
+ if (m && m.index === i) {
1047
+ out += m[0];
1048
+ i += m[0].length;
1049
+ continue;
1050
+ }
1051
+ const cp = s.codePointAt(i);
1052
+ const ch = String.fromCodePoint(cp);
1053
+ out += ch;
1054
+ i += ch.length;
1055
+ vis += 1;
1056
+ }
1057
+ return out;
1058
+ }
1059
+ function padEndVisible(text, cols) {
1060
+ const w = Math.max(0, Math.floor(Number(cols) || 0));
1061
+ const s = String(text ?? "");
1062
+ const vis = visibleWidth(s);
1063
+ if (vis === w) return s;
1064
+ if (vis > w) return sliceVisible(s, w);
1065
+ return s + " ".repeat(w - vis);
1066
+ }
1031
1067
  function wrapTextLines(text, cols) {
1032
1068
  const w = Math.max(1, Math.floor(Number(cols) || 1));
1033
1069
  const out = [];
@@ -1036,20 +1072,30 @@ function wrapTextLines(text, cols) {
1036
1072
  out.push("");
1037
1073
  continue;
1038
1074
  }
1075
+ if (visibleWidth(raw) <= w) {
1076
+ out.push(raw);
1077
+ continue;
1078
+ }
1039
1079
  let rest = raw;
1040
- while (rest.length > w) {
1041
- out.push(rest.slice(0, w));
1042
- rest = rest.slice(w);
1080
+ while (visibleWidth(rest) > w) {
1081
+ const head = sliceVisible(rest, w);
1082
+ out.push(head);
1083
+ rest = rest.slice(head.length);
1043
1084
  }
1044
1085
  if (rest.length > 0) out.push(rest);
1045
1086
  }
1046
1087
  return out;
1047
1088
  }
1048
- function padEndVisible(s, w) {
1049
- const t = String(s ?? "");
1050
- if (t.length >= w) return t.slice(0, w);
1051
- return t + " ".repeat(w - t.length);
1052
- }
1089
+ var ANSI_RE;
1090
+ var init_visible_text = __esm({
1091
+ "src/screen/visible-text.js"() {
1092
+ ANSI_RE = /\u001b(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g;
1093
+ }
1094
+ });
1095
+
1096
+ // src/screen/scrollable-text.js
1097
+ import { useState as useState3, useEffect as useEffect2, useMemo, useRef as useRef2, createElement as createElement2 } from "react";
1098
+ import { Box as Box4, Text as Text5 } from "ink";
1053
1099
  function ScrollableText({
1054
1100
  ctx,
1055
1101
  text = "",
@@ -1117,14 +1163,17 @@ function ScrollableText({
1117
1163
  }, [ctx, bindKeys, followBottom]);
1118
1164
  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" : "");
1119
1165
  const rowNodes = visible.map((line, i) => {
1120
- const body = padEndVisible(line, textWidth);
1121
1166
  const glyph = bar ? bar[i] ?? BAR_TRACK : showScrollbar ? " " : "";
1122
1167
  const isThumb = glyph === BAR_THUMB;
1123
1168
  return h5(
1124
- Text5,
1125
- { key: `L${clamped + i}` },
1126
- body,
1127
- glyph ? h5(Text5, { color: isThumb ? "cyan" : "gray" }, glyph) : null
1169
+ Box4,
1170
+ { key: `L${clamped + i}`, flexDirection: "row", width: contentCols, flexShrink: 0 },
1171
+ h5(
1172
+ Box4,
1173
+ { width: textWidth, flexShrink: 0 },
1174
+ h5(Text5, { wrap: "truncate" }, padEndVisible(line, textWidth))
1175
+ ),
1176
+ showScrollbar ? h5(Box4, { width: 1, flexShrink: 0 }, h5(Text5, { color: isThumb ? "cyan" : "gray" }, glyph)) : null
1128
1177
  );
1129
1178
  });
1130
1179
  if (bar && visible.length < viewportRows) {
@@ -1132,10 +1181,10 @@ function ScrollableText({
1132
1181
  const glyph = bar[i] ?? BAR_TRACK;
1133
1182
  rowNodes.push(
1134
1183
  h5(
1135
- Text5,
1136
- { key: `pad${i}` },
1137
- padEndVisible("", textWidth),
1138
- h5(Text5, { color: glyph === BAR_THUMB ? "cyan" : "gray" }, glyph)
1184
+ Box4,
1185
+ { key: `pad${i}`, flexDirection: "row", width: contentCols, flexShrink: 0 },
1186
+ h5(Box4, { width: textWidth, flexShrink: 0 }, h5(Text5, {}, padEndVisible("", textWidth))),
1187
+ h5(Box4, { width: 1, flexShrink: 0 }, h5(Text5, { color: glyph === BAR_THUMB ? "cyan" : "gray" }, glyph))
1139
1188
  )
1140
1189
  );
1141
1190
  }
@@ -1154,7 +1203,9 @@ var init_scrollable_text = __esm({
1154
1203
  init_components();
1155
1204
  init_scrollbar();
1156
1205
  init_follow_scroll();
1206
+ init_visible_text();
1157
1207
  init_scrollbar();
1208
+ init_visible_text();
1158
1209
  h5 = createElement2;
1159
1210
  SCROLL_KEYS = [
1160
1211
  { key: "upArrow", caption: "scroll", action: "scrollUp", order: 0 },
@@ -1299,6 +1350,7 @@ __export(screen_exports, {
1299
1350
  BAR_TRACK: () => BAR_TRACK,
1300
1351
  Box: () => Box5,
1301
1352
  Divider: () => Divider,
1353
+ FOOTER_HOTKEY_STYLE: () => FOOTER_HOTKEY_STYLE,
1302
1354
  FooterPresets: () => FooterPresets,
1303
1355
  GridCell: () => GridCell,
1304
1356
  InputField: () => InputField,
@@ -1324,6 +1376,7 @@ __export(screen_exports, {
1324
1376
  buildFooter: () => buildFooter,
1325
1377
  clampScroll: () => clampScroll,
1326
1378
  formatBindingKey: () => formatBindingKey,
1379
+ formatFooterHotkey: () => formatFooterHotkey,
1327
1380
  h: () => createElement3,
1328
1381
  isScrolledToBottom: () => isScrolledToBottom,
1329
1382
  load: () => load,
@@ -1331,6 +1384,7 @@ __export(screen_exports, {
1331
1384
  nextScrollAfterContentChange: () => nextScrollAfterContentChange,
1332
1385
  nextScrollAfterUserMove: () => nextScrollAfterUserMove,
1333
1386
  organizeFooterMessages: () => organizeFooterMessages,
1387
+ padEndVisible: () => padEndVisible,
1334
1388
  scrollbarGlyphs: () => scrollbarGlyphs,
1335
1389
  showListScreen: () => showListScreen,
1336
1390
  showMenuScreen: () => showMenuScreen,
@@ -1338,6 +1392,8 @@ __export(screen_exports, {
1338
1392
  showMultiColumnListWithPreviewScreen: () => showMultiColumnListWithPreviewScreen,
1339
1393
  showScreen: () => showScreen,
1340
1394
  showWordGridScreen: () => showWordGridScreen,
1395
+ sliceVisible: () => sliceVisible,
1396
+ stripAnsi: () => stripAnsi,
1341
1397
  useCallback: () => useCallback,
1342
1398
  useEffect: () => useEffect3,
1343
1399
  useInput: () => useInput2,
@@ -1345,6 +1401,7 @@ __export(screen_exports, {
1345
1401
  useMemo: () => useMemo2,
1346
1402
  useRef: () => useRef3,
1347
1403
  useState: () => useState4,
1404
+ visibleWidth: () => visibleWidth,
1348
1405
  wrapTextLines: () => wrapTextLines
1349
1406
  });
1350
1407
  import React2, { useState as useState4, useEffect as useEffect3, useLayoutEffect, useRef as useRef3, useMemo as useMemo2, useCallback, memo, createElement as createElement3 } from "react";
@@ -1366,6 +1423,7 @@ var init_screen = __esm({
1366
1423
  init_components();
1367
1424
  init_ui_elements();
1368
1425
  init_scrollable_text();
1426
+ init_visible_text();
1369
1427
  init_follow_scroll();
1370
1428
  init_scrollbar();
1371
1429
  init_key_bindings();