pi-hypercharm-provider 1.3.29 → 1.3.30
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/README.md +7 -6
- package/index.ts +5 -4
- package/package.json +1 -1
- package/status.ts +22 -9
- package/tests/status.smoke.ts +27 -11
package/README.md
CHANGED
|
@@ -186,12 +186,13 @@ Non-interactive toggles:
|
|
|
186
186
|
|
|
187
187
|
`glyphs: "auto"` replaces the footer glyphs (bolt, gem, warning, auth arrow,
|
|
188
188
|
separator) with ASCII equivalents on legacy terminals (mintty/Cygwin), whose
|
|
189
|
-
cell-width tables disagree with the extension's width math. There a
|
|
190
|
-
|
|
191
|
-
renderer and leaves stale rows behind. The widget
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
189
|
+
cell-width tables disagree with the extension's width math. There a glyph the
|
|
190
|
+
terminal renders wider than the extension measures overflows the widget line,
|
|
191
|
+
which desynchronizes pi's renderer and leaves stale rows behind. The widget
|
|
192
|
+
also right-aligns the account side flush with the terminal's last column, the
|
|
193
|
+
way pi's built-in footer does, and clamps an explicit `unicode` choice to
|
|
194
|
+
ASCII on legacy terminals; the statusbar is not edge-padded and always honors
|
|
195
|
+
the exact choice.
|
|
195
196
|
|
|
196
197
|
|
|
197
198
|
## Authentication
|
package/index.ts
CHANGED
|
@@ -77,10 +77,11 @@
|
|
|
77
77
|
*
|
|
78
78
|
* - glyphs "auto" swaps the emoji footer glyphs for ASCII on legacy
|
|
79
79
|
* terminals (mintty/Cygwin), whose cell-width tables disagree with the
|
|
80
|
-
* width math and
|
|
81
|
-
* force a set. The widget
|
|
82
|
-
*
|
|
83
|
-
*
|
|
80
|
+
* width math and can overflow the widget line. "unicode"/"ascii"
|
|
81
|
+
* force a set. The widget right-aligns the account side flush with the
|
|
82
|
+
* terminal's last column (as pi's built-in footer does) and clamps an
|
|
83
|
+
* explicit "unicode" to ASCII on legacy terminals; the statusbar is
|
|
84
|
+
* not edge-padded and honors the exact choice.
|
|
84
85
|
* /hypercharm-status refresh (re-fetch balance/team now)
|
|
85
86
|
* /hypercharm-status reset
|
|
86
87
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-hypercharm-provider",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.30",
|
|
4
4
|
"description": "HyperCharm provider extension for pi - Access DeepSeek, GLM, Kimi, Qwen, MiniMax, Gemma, and GPT-OSS models through the Charm Hyper API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|
package/status.ts
CHANGED
|
@@ -21,10 +21,13 @@
|
|
|
21
21
|
* or when the glyphs config asks for it (see GlyphMode). Older mintty
|
|
22
22
|
* builds measure these glyphs with cell tables that disagree with the
|
|
23
23
|
* ones above; the statusbar absorbs that (its lines are not edge-padded)
|
|
24
|
-
* but the
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
24
|
+
* but a glyph the terminal renders wider than the math above measures it
|
|
25
|
+
* overflows the widget line, which desyncs pi's row bookkeeping and
|
|
26
|
+
* leaves ghost rows behind.
|
|
27
|
+
* - render() budgets the terminal's full width, so the right zone ends
|
|
28
|
+
* flush with the last column. Overlong lines are what pi rejects — its
|
|
29
|
+
* renderer throws when a line's visible width exceeds the terminal — so
|
|
30
|
+
* the width math above must never undercount a glyph.
|
|
28
31
|
*/
|
|
29
32
|
|
|
30
33
|
export type DisplayMode = "widget" | "statusbar" | "off";
|
|
@@ -299,10 +302,16 @@ export function termVisWidth(str: string): number {
|
|
|
299
302
|
export function truncateAnsi(str: string, maxCols: number, ellipsis = "…"): string {
|
|
300
303
|
if (maxCols <= 0) return "";
|
|
301
304
|
if (termVisWidth(str) <= maxCols) return str;
|
|
305
|
+
// Reserve the ellipsis's own width, not a fixed column: ASCII "..." is
|
|
306
|
+
// three cells, and a line that overshoots the terminal is what pi's
|
|
307
|
+
// renderer rejects — it throws instead of wrapping. An ellipsis too wide
|
|
308
|
+
// for the budget is itself cut to it.
|
|
309
|
+
const ellipsisWidth = termVisWidth(ellipsis);
|
|
310
|
+
if (ellipsisWidth >= maxCols) return truncateAnsi(ellipsis, maxCols, "");
|
|
302
311
|
let result = "";
|
|
303
312
|
let visWidth = 0;
|
|
304
313
|
let i = 0;
|
|
305
|
-
const target = maxCols -
|
|
314
|
+
const target = maxCols - ellipsisWidth;
|
|
306
315
|
while (i < str.length) {
|
|
307
316
|
const code = str.charCodeAt(i);
|
|
308
317
|
if (code === 0x1b && i + 1 < str.length && str.charCodeAt(i + 1) === 0x5b) {
|
|
@@ -360,10 +369,14 @@ export class StatusLineWidget {
|
|
|
360
369
|
invalidate(): void {}
|
|
361
370
|
|
|
362
371
|
render(width: number): string[] {
|
|
363
|
-
//
|
|
364
|
-
//
|
|
365
|
-
//
|
|
366
|
-
|
|
372
|
+
// Budget the terminal's full width, so the right zone ends flush with the
|
|
373
|
+
// last column. pi's built-in footer right-aligns its model/thinking text
|
|
374
|
+
// the same way and both rows share one frame, so holding a column back
|
|
375
|
+
// reads as a gap under a flush right edge. pi clears each line before
|
|
376
|
+
// writing it and separates rows with CR/LF, so ending on the final column
|
|
377
|
+
// carries no wrap hazard — the lines its renderer rejects are the
|
|
378
|
+
// overlong ones.
|
|
379
|
+
const w = Math.max(1, width);
|
|
367
380
|
const leftVis = termVisWidth(this.leftRaw);
|
|
368
381
|
if (leftVis > w) {
|
|
369
382
|
return [this.theme.fg("dim", truncateAnsi(this.leftRaw, w, this.glyphs.ellipsis))];
|
package/tests/status.smoke.ts
CHANGED
|
@@ -101,38 +101,40 @@ const left = buildSessionLine({ requests: 7, spendHc: 1.24 })!;
|
|
|
101
101
|
assert.ok(left.startsWith("\u26A1 "), "unicode glyph set is the default");
|
|
102
102
|
const widget = new StatusLineWidget(fakeTheme, left, tiers, false);
|
|
103
103
|
|
|
104
|
-
// Wide: full tier, left-right justified
|
|
105
|
-
//
|
|
106
|
-
//
|
|
104
|
+
// Wide: full tier, left-right justified to the terminal's last column
|
|
105
|
+
// (pi's built-in footer right-aligns its model text the same way, so both
|
|
106
|
+
// rows end flush).
|
|
107
107
|
const wide = widget.render(80);
|
|
108
108
|
assert.equal(wide.length, 1);
|
|
109
|
-
assert.equal(termVisWidth(wide[0]),
|
|
109
|
+
assert.equal(termVisWidth(wide[0]), 80);
|
|
110
110
|
assert.ok(stripAnsi(wide[0]).startsWith("⚡ 1.24 hc"));
|
|
111
111
|
assert.ok(stripAnsi(wide[0]).endsWith("⟳ 29d"));
|
|
112
112
|
|
|
113
|
-
// Medium: drops to a compressed tier, still exactly width
|
|
113
|
+
// Medium: drops to a compressed tier, still exactly width
|
|
114
114
|
const med = widget.render(52);
|
|
115
|
-
assert.equal(termVisWidth(med[0]),
|
|
115
|
+
assert.equal(termVisWidth(med[0]), 52);
|
|
116
116
|
assert.ok(!stripAnsi(med[0]).includes("⟳"), "compressed tiers drop auth first");
|
|
117
117
|
|
|
118
118
|
// Narrow: no tier fits → left only, padded
|
|
119
119
|
const narrow = widget.render(termVisWidth(left) + 3);
|
|
120
|
-
assert.equal(termVisWidth(narrow[0]), termVisWidth(left) +
|
|
120
|
+
assert.equal(termVisWidth(narrow[0]), termVisWidth(left) + 3);
|
|
121
121
|
assert.ok(stripAnsi(narrow[0]).startsWith("⚡"));
|
|
122
122
|
assert.ok(!stripAnsi(narrow[0]).includes("◆"));
|
|
123
123
|
|
|
124
124
|
// Narrower than left itself: truncation never overflows (crash guard)
|
|
125
125
|
const tiny = widget.render(10);
|
|
126
|
-
assert.equal(termVisWidth(tiny[0]),
|
|
126
|
+
assert.equal(termVisWidth(tiny[0]), 10);
|
|
127
127
|
|
|
128
128
|
// Left empty (session gated) → right-aligned account line
|
|
129
129
|
const rightOnly = new StatusLineWidget(fakeTheme, "", tiers, false);
|
|
130
130
|
const ro = rightOnly.render(70);
|
|
131
|
-
assert.equal(termVisWidth(ro[0]),
|
|
131
|
+
assert.equal(termVisWidth(ro[0]), 70, "account side hugs the last column");
|
|
132
|
+
assert.equal(stripAnsi(ro[0]).length - stripAnsi(ro[0]).trimStart().length, 70 - termVisWidth(tiers[0]),
|
|
133
|
+
"left zone stays empty: account text starts where the tier width demands");
|
|
132
134
|
assert.ok(stripAnsi(ro[0]).endsWith("⟳ 29d"));
|
|
133
135
|
|
|
134
136
|
// No data at all
|
|
135
|
-
assert.deepEqual(new StatusLineWidget(fakeTheme, "", [], false).render(40), [fakeTheme.fg("dim", "") + " ".repeat(
|
|
137
|
+
assert.deepEqual(new StatusLineWidget(fakeTheme, "", [], false).render(40), [fakeTheme.fg("dim", "") + " ".repeat(40)]);
|
|
136
138
|
|
|
137
139
|
// Warning color wired through
|
|
138
140
|
const warn = new StatusLineWidget(fakeTheme, "", buildAccountTiers(acc({ balance: 10 }), true), true);
|
|
@@ -197,11 +199,25 @@ assert.ok(asciiTiers[0].endsWith("~ 29d"), `got ${asciiTiers[0]}`);
|
|
|
197
199
|
const asciiWidget = new StatusLineWidget(fakeTheme, asciiLine, asciiTiers, true, ASCII_GLYPHS).render(60)[0];
|
|
198
200
|
assert.equal([...stripAnsi(asciiWidget)].every((c) => c.charCodeAt(0) < 128), true);
|
|
199
201
|
|
|
202
|
+
// ASCII truncation must fit the width too: the three-cell "..." used to
|
|
203
|
+
// overshoot by two columns on a narrow terminal, and pi's renderer throws
|
|
204
|
+
// on an overlong line instead of wrapping it.
|
|
205
|
+
for (const width of [10, 14, 17]) {
|
|
206
|
+
const line = new StatusLineWidget(fakeTheme, asciiLine, asciiTiers, true, ASCII_GLYPHS).render(width)[0];
|
|
207
|
+
assert.equal(termVisWidth(line), width, `ascii truncation must fit width ${width}`);
|
|
208
|
+
assert.equal([...stripAnsi(line)].every((c) => c.charCodeAt(0) < 128), true, `ascii truncation stays ascii at ${width}`);
|
|
209
|
+
}
|
|
210
|
+
|
|
200
211
|
// Unicode mode keeps the glyphs (regression guard for the default path)
|
|
201
212
|
assert.ok(buildAccountTiers(acc({ balance: 10 }), true)[0].includes("\u26A0 \u25C6"));
|
|
202
213
|
|
|
203
214
|
// Truncation takes the caller's ellipsis so ASCII mode stays ASCII
|
|
204
|
-
|
|
215
|
+
// The ellipsis is billed its own width: ASCII "..." (3 cells) leaves 2 for text
|
|
216
|
+
assert.equal(truncateAnsi("abcdefghij", 5, "..."), "ab...");
|
|
217
|
+
assert.equal(termVisWidth(truncateAnsi("abcdefghij", 5, "...")), 5);
|
|
218
|
+
// An ellipsis wider than the whole budget is itself cut to it
|
|
219
|
+
assert.equal(truncateAnsi("abcdefghij", 2, "..."), "..");
|
|
220
|
+
assert.equal(truncateAnsi("abcdefghij", 1, "..."), ".");
|
|
205
221
|
assert.ok(truncateAnsi("abcdefghij", 5).endsWith("\u2026"));
|
|
206
222
|
|
|
207
223
|
assert.equal(coerceStatusConfig({ glyphs: "bogus" }).glyphs, "auto");
|