@bismawy/pi-arnative 0.2.10 → 0.2.11
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 +5 -0
- package/extensions/footer.ts +92 -11
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.2.11] - 2026-10-01
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
- The footer status area now reflows: 2 lines stay the default while everything fits, but on a narrow terminal the statuses and the metrics (tok/s, cache, context) move onto a 3rd line instead of being truncated. `extensions/footer.ts` `layoutStatusLines()` (`rows` = line budget of the whole footer, `maxRows` caps the growth); self-check covers the wide/narrow/cap cases.
|
|
7
|
+
|
|
3
8
|
## [0.2.10] - 2026-10-01
|
|
4
9
|
|
|
5
10
|
### Added
|
package/extensions/footer.ts
CHANGED
|
@@ -176,7 +176,64 @@ export function boxEditorLines(
|
|
|
176
176
|
return out;
|
|
177
177
|
}
|
|
178
178
|
|
|
179
|
+
/**
|
|
180
|
+
* Wraps the footer status lines: `groups` separated by `sep`, with the already-joined
|
|
181
|
+
* `metrics` right-aligned on the last group line when they fit, else on a line of
|
|
182
|
+
* their own.
|
|
183
|
+
*
|
|
184
|
+
* `rows` is the footer line budget (line 1 plus the status lines): 2 reproduces the
|
|
185
|
+
* classic layout as long as it fits, 3 lets the metrics take their own line instead
|
|
186
|
+
* of crowding the statuses. `maxRows` caps the growth — a group wider than the
|
|
187
|
+
* terminal is never split, the caller truncates it.
|
|
188
|
+
*/
|
|
189
|
+
export function layoutStatusLines(
|
|
190
|
+
groups: readonly string[],
|
|
191
|
+
metrics: string,
|
|
192
|
+
sep: string,
|
|
193
|
+
width: number,
|
|
194
|
+
rows: number,
|
|
195
|
+
maxRows = Number.MAX_SAFE_INTEGER,
|
|
196
|
+
vw: (s: string) => number = visibleWidth,
|
|
197
|
+
): string[] {
|
|
198
|
+
if (rows > maxRows) rows = maxRows;
|
|
199
|
+
const budget = Math.max(1, rows - 1);
|
|
200
|
+
const out: string[] = [];
|
|
201
|
+
let line = "";
|
|
202
|
+
|
|
203
|
+
for (const group of groups) {
|
|
204
|
+
const candidate = line ? line + sep + group : group;
|
|
205
|
+
// An empty line is never flushed: an oversized group must not loop here.
|
|
206
|
+
if (line && vw(candidate) > width) {
|
|
207
|
+
out.push(line);
|
|
208
|
+
line = group;
|
|
209
|
+
} else {
|
|
210
|
+
line = candidate;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (metrics) {
|
|
215
|
+
const pad = (before: string) => " ".repeat(Math.max(1, width - vw(before) - vw(metrics))) + metrics;
|
|
216
|
+
if (!line) {
|
|
217
|
+
line = pad("");
|
|
218
|
+
} else if (vw(line + sep + metrics) <= width) {
|
|
219
|
+
// Metrics share the last group line, right-aligned as before.
|
|
220
|
+
line = line + pad(line);
|
|
221
|
+
} else if (out.length < budget) {
|
|
222
|
+
// `budget` is the only thing `rows` controls: 2 lets the metrics take a
|
|
223
|
+
// line of their own instead of crowding the groups, 1 packs everything.
|
|
224
|
+
out.push(line);
|
|
225
|
+
line = pad("");
|
|
226
|
+
} else {
|
|
227
|
+
line = line + sep + metrics;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
out.push(line);
|
|
232
|
+
return out;
|
|
233
|
+
}
|
|
234
|
+
|
|
179
235
|
function parseOptimizer(raw: string | undefined): string | null {
|
|
236
|
+
|
|
180
237
|
if (!raw) return null;
|
|
181
238
|
const m = raw.match(/([A-Za-z0-9_-]+)\s+cache\s+(\d+\/\d+)·[^\s]+\s+([\d.]+%)/);
|
|
182
239
|
return m ? `${m[1]} ${m[2]} (${m[3]})` : null;
|
|
@@ -412,7 +469,6 @@ export default function (pi: ExtensionAPI) {
|
|
|
412
469
|
}
|
|
413
470
|
})();
|
|
414
471
|
const rawCache = statuses.get("pi-cache-stats");
|
|
415
|
-
const segs: string[] = [];
|
|
416
472
|
const cleanStatus = (s: string) => {
|
|
417
473
|
if (s.includes("MCP:")) {
|
|
418
474
|
let clean = stripAnsi(s);
|
|
@@ -450,12 +506,12 @@ export default function (pi: ExtensionAPI) {
|
|
|
450
506
|
return tint(clean.trim());
|
|
451
507
|
};
|
|
452
508
|
|
|
509
|
+
const groups: string[] = [];
|
|
453
510
|
const mcp = statuses.get("mcp");
|
|
454
|
-
if (mcp !== undefined)
|
|
511
|
+
if (mcp !== undefined) groups.push(cleanStatus(mcp));
|
|
455
512
|
for (const [k, s] of statuses) {
|
|
456
|
-
if (k !== "mcp" && k !== "pi-cache-stats")
|
|
513
|
+
if (k !== "mcp" && k !== "pi-cache-stats") groups.push(cleanStatus(s));
|
|
457
514
|
}
|
|
458
|
-
const left2 = segs.join(sep);
|
|
459
515
|
|
|
460
516
|
const opt = parseOptimizer(rawCache);
|
|
461
517
|
let usageStr = "";
|
|
@@ -465,13 +521,17 @@ export default function (pi: ExtensionAPI) {
|
|
|
465
521
|
// fallback
|
|
466
522
|
}
|
|
467
523
|
const speedStr = latestSpeed !== null && latestSpeed > 0 ? `${acc("\udb81\udcc5")} ${tint(`${latestSpeed.toFixed(1)} tok/s`)}` : "";
|
|
468
|
-
const
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
524
|
+
const metrics = [speedStr, opt ? `${acc("\udb80\udf5b")} ${tint(opt)}` : "", usageStr].filter(Boolean).join(` ${dim("·")} `);
|
|
525
|
+
|
|
526
|
+
// 2 footer lines normally; grow to 3 rather than crowding the second one on
|
|
527
|
+
// a narrow terminal, then truncate (a single status can still overflow).
|
|
528
|
+
const MAX_ROWS = 3;
|
|
529
|
+
for (let rows = 2; ; rows++) {
|
|
530
|
+
const wrapped = layoutStatusLines(groups, metrics, sep, width, rows, MAX_ROWS);
|
|
531
|
+
if (rows >= MAX_ROWS || wrapped.every((l) => visibleWidth(l) <= width)) {
|
|
532
|
+
for (const l of wrapped) lines.push(truncateToWidth(l, width));
|
|
533
|
+
break;
|
|
534
|
+
}
|
|
475
535
|
}
|
|
476
536
|
return lines;
|
|
477
537
|
},
|
|
@@ -664,6 +724,27 @@ if (isMain(import.meta.url)) {
|
|
|
664
724
|
assert(formatDuration(65_000) === "1m 5s", "minutes + seconds");
|
|
665
725
|
assert(formatDuration(3_700_000) === "1h 1m", "hours + minutes");
|
|
666
726
|
|
|
727
|
+
// Self-check: footer status wrapping (2 lines until it crowds, then 3).
|
|
728
|
+
const vw = (s: string) => s.length;
|
|
729
|
+
const g = (n: number) => "g".repeat(n);
|
|
730
|
+
// Wide terminal: everything on one status line, metrics right-aligned.
|
|
731
|
+
const wide = layoutStatusLines([g(4), g(4), g(4)], "Mmmmm", " | ", 40, 2, 3, vw);
|
|
732
|
+
assert(wide.length === 1, "wide: statuses share one line");
|
|
733
|
+
assert(wide[0]!.endsWith("Mmmmm") && vw(wide[0]!) === 40, "wide: metrics right-aligned on that line");
|
|
734
|
+
// Narrow: groups stop sharing a line and metrics moves to its own third line.
|
|
735
|
+
const narrow = layoutStatusLines([g(4), g(4), g(4)], g(8), " | ", 14, 3, 3, vw);
|
|
736
|
+
assert(narrow.length === 3, "narrow: grows to 3 lines");
|
|
737
|
+
assert(narrow.every((l) => vw(l) <= 14), "narrow: every line fits the terminal");
|
|
738
|
+
// The same width with a 2-line budget cannot fit -> the caller asks for row 3.
|
|
739
|
+
assert(
|
|
740
|
+
layoutStatusLines([g(4), g(4), g(4)], g(8), " | ", 14, 2, 3, vw).some((l) => vw(l) > 14),
|
|
741
|
+
"2-line budget overflows, so the caller grows to 3"
|
|
742
|
+
);
|
|
743
|
+
// Tight cap: never exceed maxRows, even though lines then overflow (caller truncates).
|
|
744
|
+
assert(layoutStatusLines([g(4), g(4), g(4)], "Mmmmm", " | ", 3, 3, 3, vw).length <= 3, "cap: 3 lines at most");
|
|
745
|
+
assert(layoutStatusLines([g(9)], "", " | ", 4, 2, 3, vw).length === 1, "oversized group is not split");
|
|
746
|
+
assert(layoutStatusLines([], "m", " | ", 8, 2, 3, vw).length === 1, "metrics only");
|
|
747
|
+
|
|
667
748
|
const plain = (s: string) => s;
|
|
668
749
|
const D = "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"; // 8 columns = content width in this test
|
|
669
750
|
const boxed = boxEditorLines([D, " hi ", " lo ", D, " /mo "], 2, plain);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bismawy/pi-arnative",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.11",
|
|
4
4
|
"description": "Arnative look for Pi: warm-neutral themes, rounded tool boxes, tabbed header, compact footer, transcript clock, and a /usage dashboard.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|