@nmakarov/cli-toolkit 0.81.0 → 0.82.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.
@@ -1042,7 +1042,44 @@ var init_follow_scroll = __esm({
1042
1042
  }
1043
1043
  });
1044
1044
 
1045
- // src/screen/scrollable-text.js
1045
+ // src/screen/visible-text.js
1046
+ function stripAnsi(text) {
1047
+ return String(text ?? "").replace(ANSI_RE, "");
1048
+ }
1049
+ function visibleWidth(text) {
1050
+ return [...stripAnsi(text)].length;
1051
+ }
1052
+ function sliceVisible(text, cols) {
1053
+ const w = Math.max(0, Math.floor(Number(cols) || 0));
1054
+ const s = String(text ?? "");
1055
+ if (w === 0) return "";
1056
+ let vis = 0;
1057
+ let out = "";
1058
+ let i = 0;
1059
+ while (i < s.length && vis < w) {
1060
+ ANSI_RE.lastIndex = i;
1061
+ const m = ANSI_RE.exec(s);
1062
+ if (m && m.index === i) {
1063
+ out += m[0];
1064
+ i += m[0].length;
1065
+ continue;
1066
+ }
1067
+ const cp = s.codePointAt(i);
1068
+ const ch = String.fromCodePoint(cp);
1069
+ out += ch;
1070
+ i += ch.length;
1071
+ vis += 1;
1072
+ }
1073
+ return out;
1074
+ }
1075
+ function padEndVisible(text, cols) {
1076
+ const w = Math.max(0, Math.floor(Number(cols) || 0));
1077
+ const s = String(text ?? "");
1078
+ const vis = visibleWidth(s);
1079
+ if (vis === w) return s;
1080
+ if (vis > w) return sliceVisible(s, w);
1081
+ return s + " ".repeat(w - vis);
1082
+ }
1046
1083
  function wrapTextLines(text, cols) {
1047
1084
  const w = Math.max(1, Math.floor(Number(cols) || 1));
1048
1085
  const out = [];
@@ -1051,20 +1088,28 @@ function wrapTextLines(text, cols) {
1051
1088
  out.push("");
1052
1089
  continue;
1053
1090
  }
1091
+ if (visibleWidth(raw) <= w) {
1092
+ out.push(raw);
1093
+ continue;
1094
+ }
1054
1095
  let rest = raw;
1055
- while (rest.length > w) {
1056
- out.push(rest.slice(0, w));
1057
- rest = rest.slice(w);
1096
+ while (visibleWidth(rest) > w) {
1097
+ const head = sliceVisible(rest, w);
1098
+ out.push(head);
1099
+ rest = rest.slice(head.length);
1058
1100
  }
1059
1101
  if (rest.length > 0) out.push(rest);
1060
1102
  }
1061
1103
  return out;
1062
1104
  }
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
- }
1105
+ var ANSI_RE;
1106
+ var init_visible_text = __esm({
1107
+ "src/screen/visible-text.js"() {
1108
+ ANSI_RE = /\u001b(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g;
1109
+ }
1110
+ });
1111
+
1112
+ // src/screen/scrollable-text.js
1068
1113
  function ScrollableText({
1069
1114
  ctx,
1070
1115
  text = "",
@@ -1132,14 +1177,17 @@ function ScrollableText({
1132
1177
  }, [ctx, bindKeys, followBottom]);
1133
1178
  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
1179
  const rowNodes = visible.map((line, i) => {
1135
- const body = padEndVisible(line, textWidth);
1136
1180
  const glyph = bar ? bar[i] ?? BAR_TRACK : showScrollbar ? " " : "";
1137
1181
  const isThumb = glyph === BAR_THUMB;
1138
1182
  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
1183
+ import_ink5.Box,
1184
+ { key: `L${clamped + i}`, flexDirection: "row", width: contentCols, flexShrink: 0 },
1185
+ h5(
1186
+ import_ink5.Box,
1187
+ { width: textWidth, flexShrink: 0 },
1188
+ h5(import_ink5.Text, { wrap: "truncate" }, padEndVisible(line, textWidth))
1189
+ ),
1190
+ showScrollbar ? h5(import_ink5.Box, { width: 1, flexShrink: 0 }, h5(import_ink5.Text, { color: isThumb ? "cyan" : "gray" }, glyph)) : null
1143
1191
  );
1144
1192
  });
1145
1193
  if (bar && visible.length < viewportRows) {
@@ -1147,10 +1195,10 @@ function ScrollableText({
1147
1195
  const glyph = bar[i] ?? BAR_TRACK;
1148
1196
  rowNodes.push(
1149
1197
  h5(
1150
- import_ink5.Text,
1151
- { key: `pad${i}` },
1152
- padEndVisible("", textWidth),
1153
- h5(import_ink5.Text, { color: glyph === BAR_THUMB ? "cyan" : "gray" }, glyph)
1198
+ import_ink5.Box,
1199
+ { key: `pad${i}`, flexDirection: "row", width: contentCols, flexShrink: 0 },
1200
+ h5(import_ink5.Box, { width: textWidth, flexShrink: 0 }, h5(import_ink5.Text, {}, padEndVisible("", textWidth))),
1201
+ h5(import_ink5.Box, { width: 1, flexShrink: 0 }, h5(import_ink5.Text, { color: glyph === BAR_THUMB ? "cyan" : "gray" }, glyph))
1154
1202
  )
1155
1203
  );
1156
1204
  }
@@ -1171,7 +1219,9 @@ var init_scrollable_text = __esm({
1171
1219
  init_components();
1172
1220
  init_scrollbar();
1173
1221
  init_follow_scroll();
1222
+ init_visible_text();
1174
1223
  init_scrollbar();
1224
+ init_visible_text();
1175
1225
  h5 = import_react5.createElement;
1176
1226
  SCROLL_KEYS = [
1177
1227
  { key: "upArrow", caption: "scroll", action: "scrollUp", order: 0 },
@@ -1348,6 +1398,7 @@ __export(screen_exports, {
1348
1398
  nextScrollAfterContentChange: () => nextScrollAfterContentChange,
1349
1399
  nextScrollAfterUserMove: () => nextScrollAfterUserMove,
1350
1400
  organizeFooterMessages: () => organizeFooterMessages,
1401
+ padEndVisible: () => padEndVisible,
1351
1402
  scrollbarGlyphs: () => scrollbarGlyphs,
1352
1403
  showListScreen: () => showListScreen,
1353
1404
  showMenuScreen: () => showMenuScreen,
@@ -1355,6 +1406,8 @@ __export(screen_exports, {
1355
1406
  showMultiColumnListWithPreviewScreen: () => showMultiColumnListWithPreviewScreen,
1356
1407
  showScreen: () => showScreen,
1357
1408
  showWordGridScreen: () => showWordGridScreen,
1409
+ sliceVisible: () => sliceVisible,
1410
+ stripAnsi: () => stripAnsi,
1358
1411
  useCallback: () => import_react6.useCallback,
1359
1412
  useEffect: () => import_react6.useEffect,
1360
1413
  useInput: () => import_ink6.useInput,
@@ -1362,6 +1415,7 @@ __export(screen_exports, {
1362
1415
  useMemo: () => import_react6.useMemo,
1363
1416
  useRef: () => import_react6.useRef,
1364
1417
  useState: () => import_react6.useState,
1418
+ visibleWidth: () => visibleWidth,
1365
1419
  wrapTextLines: () => wrapTextLines
1366
1420
  });
1367
1421
  async function load() {
@@ -1383,6 +1437,7 @@ var init_screen = __esm({
1383
1437
  init_components();
1384
1438
  init_ui_elements();
1385
1439
  init_scrollable_text();
1440
+ init_visible_text();
1386
1441
  init_follow_scroll();
1387
1442
  init_scrollbar();
1388
1443
  init_key_bindings();