@noobdemon/noob-cli 1.12.9 → 1.12.10

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/CHANGELOG.md CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  Tất cả thay đổi đáng kể của `@noobdemon/noob-cli` được ghi vào file này.
4
4
 
5
+ ## [1.12.10] - 2026-06-13
6
+
7
+ ### Fixed
8
+ - **Word-wrap output model trong TUI** (`src/tui.js`): vài user thấy text model gửi tràn 1 dòng không tự xuống dòng. Nguyên nhân: `commit()` in block thẳng, để terminal lo wrap — nhưng vài terminal không auto-wrap (DECAWM off / emulator lạ). Fix: soft-wrap chủ động theo `cols()` trong `commit()`, khớp với status bar (vốn đã wrap qua `wrapText`). Extract helper `softWrapLine(text, width)` (pure soft-wrap, ANSI-aware, cắt word boundary, không tail-follow/truncate) tách từ phần dùng chung của `wrapText`; `commit()` split theo `\n` gốc → wrap từng đoạn → join, giữ nguyên cấu trúc dòng model.
9
+
5
10
  ## [1.12.9] - 2026-06-13
6
11
 
7
12
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@noobdemon/noob-cli",
3
- "version": "1.12.9",
3
+ "version": "1.12.10",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
package/src/tui.js CHANGED
@@ -42,6 +42,30 @@ function findVisPos(text, targetVis) {
42
42
  // thấy đúng phần đang được viết thay vì các dòng đầu tiên. Nếu wrap nhiều hơn
43
43
  // `maxLines` dòng → dòng đầu page thêm "…" phía trước (và cắt 1 ký tự cuối để
44
44
  // giữ nguyên độ rộng terminal, tránh re-wrap) để báo "có nội dung bị ẩn trên".
45
+ // Soft-wrap thuần 1 đoạn (KHÔNG chứa '\n') thành mảng dòng vis-width ≤ width.
46
+ // Cắt ưu tiên word boundary; ANSI-aware. Không tail-follow, không truncate.
47
+ function softWrapLine(text, width) {
48
+ const hasAnsi = /\x1b/.test(text);
49
+ const RESET = '\x1b[0m';
50
+ const close = (line) => (hasAnsi ? line + RESET : line);
51
+ if (visLen(text) <= width) return [close(text)];
52
+ const lines = [];
53
+ let remaining = text;
54
+ while (remaining) {
55
+ if (visLen(remaining) <= width) {
56
+ lines.push(remaining);
57
+ break;
58
+ }
59
+ let cutPos = findVisPos(remaining, width);
60
+ const slice = remaining.slice(0, cutPos);
61
+ const lastSpace = slice.lastIndexOf(' ');
62
+ if (lastSpace > width * 0.3) cutPos = lastSpace;
63
+ lines.push(remaining.slice(0, cutPos).trimEnd());
64
+ remaining = remaining.slice(cutPos).trimStart();
65
+ }
66
+ return lines.map(close);
67
+ }
68
+
45
69
  function wrapText(text, width, maxLines) {
46
70
  if (!text) return [''];
47
71
  const hasAnsi = /\x1b/.test(text);
@@ -414,6 +438,13 @@ export function createTui({ onLine, onInterrupt, onEOF, onShiftTab, completer }
414
438
  // In khối text VĨNH VIỄN phía trên thanh, rồi vẽ lại thanh.
415
439
  function commit(block) {
416
440
  const rs = rows();
441
+ // Soft-wrap chủ động theo cols(): vài terminal không auto-wrap dòng dài →
442
+ // text model tràn 1 dòng. Wrap từng dòng logic, giữ '\n' gốc.
443
+ const width = cols();
444
+ block = block
445
+ .split('\n')
446
+ .map((ln) => softWrapLine(ln, width).join('\n'))
447
+ .join('\n');
417
448
  let s = `${ESC}[?25l` + eraseSeq();
418
449
  s += block;
419
450
  if (!block.endsWith('\n')) s += '\n';