ispbills-icli 4.0.4 → 4.0.5
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/package.json +1 -1
- package/src/layout.js +86 -7
package/package.json
CHANGED
package/src/layout.js
CHANGED
|
@@ -1,16 +1,95 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
2
|
+
|
|
1
3
|
// ── Terminal width ─────────────────────────────────────────────────────────────
|
|
4
|
+
let cachedCols = 0;
|
|
5
|
+
let lastProbeAt = 0;
|
|
6
|
+
let fallbackCols = 0;
|
|
7
|
+
|
|
8
|
+
function toPosInt(v) {
|
|
9
|
+
const n = Number.parseInt(String(v ?? ''), 10);
|
|
10
|
+
return Number.isFinite(n) && n > 0 ? n : 0;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function streamCols(stream) {
|
|
14
|
+
const direct = toPosInt(stream?.columns);
|
|
15
|
+
if (direct) return direct;
|
|
16
|
+
|
|
17
|
+
if (typeof stream?.getWindowSize === 'function') {
|
|
18
|
+
try {
|
|
19
|
+
const win = stream.getWindowSize();
|
|
20
|
+
const fromWin = Array.isArray(win) ? toPosInt(win[0]) : toPosInt(win?.columns);
|
|
21
|
+
if (fromWin) return fromWin;
|
|
22
|
+
} catch {}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return 0;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function probeFallbackCols() {
|
|
29
|
+
if (fallbackCols > 0) return fallbackCols;
|
|
30
|
+
|
|
31
|
+
const candidates = [];
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
candidates.push(toPosInt(execSync('tput cols', { stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim()));
|
|
35
|
+
} catch {}
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
const stty = execSync('stty size 2>/dev/null', { stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim();
|
|
39
|
+
const parts = stty.split(/\s+/);
|
|
40
|
+
candidates.push(toPosInt(parts[1]));
|
|
41
|
+
} catch {}
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
const mode = execSync('mode con', { stdio: ['ignore', 'pipe', 'ignore'] }).toString();
|
|
45
|
+
const m = mode.match(/Columns:\s*(\d+)/i);
|
|
46
|
+
candidates.push(toPosInt(m?.[1]));
|
|
47
|
+
} catch {}
|
|
48
|
+
|
|
49
|
+
fallbackCols = Math.max(0, ...candidates.filter(Boolean));
|
|
50
|
+
return fallbackCols;
|
|
51
|
+
}
|
|
52
|
+
|
|
2
53
|
export function termCols() {
|
|
3
|
-
|
|
4
|
-
if (
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
54
|
+
const now = Date.now();
|
|
55
|
+
if (cachedCols > 0 && now - lastProbeAt < 250) return cachedCols;
|
|
56
|
+
|
|
57
|
+
const envCols = toPosInt(process.env.COLUMNS || process.env.TERM_WIDTH);
|
|
58
|
+
if (envCols) {
|
|
59
|
+
cachedCols = envCols;
|
|
60
|
+
lastProbeAt = now;
|
|
61
|
+
return envCols;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const stdoutCols = streamCols(process.stdout);
|
|
65
|
+
if (stdoutCols) {
|
|
66
|
+
cachedCols = stdoutCols;
|
|
67
|
+
lastProbeAt = now;
|
|
68
|
+
return stdoutCols;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const stderrCols = streamCols(process.stderr);
|
|
72
|
+
if (stderrCols) {
|
|
73
|
+
cachedCols = stderrCols;
|
|
74
|
+
lastProbeAt = now;
|
|
75
|
+
return stderrCols;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const probed = probeFallbackCols();
|
|
79
|
+
cachedCols = probed || 120;
|
|
80
|
+
lastProbeAt = now;
|
|
81
|
+
return cachedCols;
|
|
8
82
|
}
|
|
9
83
|
export const cols = () => Math.max(40, termCols());
|
|
10
84
|
export const inner = () => cols() - 4; // usable width inside │ … │
|
|
11
85
|
export const onResize = (cb) => {
|
|
12
|
-
|
|
13
|
-
|
|
86
|
+
const wrapped = () => {
|
|
87
|
+
cachedCols = 0;
|
|
88
|
+
fallbackCols = 0;
|
|
89
|
+
cb();
|
|
90
|
+
};
|
|
91
|
+
process.stdout.on('resize', wrapped);
|
|
92
|
+
process.stderr.on('resize', wrapped);
|
|
14
93
|
};
|
|
15
94
|
|
|
16
95
|
// ── ANSI-aware string helpers ─────────────────────────────────────────────────
|