pi-provider-status 0.2.0 → 0.3.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.
- package/README.md +1 -1
- package/extensions/provider-status.ts +75 -6
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
Balance CNY 128.40
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
The panel opens immediately; each section fills in as its request settles. Press `r` to refresh, `Esc`/`q` to close. In non-interactive mode the report is printed as a plain notification instead.
|
|
15
|
+
The panel opens immediately as a scrollable overlay; each section fills in as its request settles. Scroll with `↑`/`↓` (or `j`/`k`), `PgUp`/`PgDn` by page, `Ctrl+u`/`Ctrl+d` by half page, `Home`/`End` (or `g`/`G`) to jump to top/bottom; the mouse wheel also works over the panel in fullscreen mode. Press `r` to refresh, `Esc`/`q` to close. In non-interactive mode the report is printed as a plain notification instead.
|
|
16
16
|
|
|
17
17
|
## Install
|
|
18
18
|
|
|
@@ -48,10 +48,14 @@
|
|
|
48
48
|
*
|
|
49
49
|
* The panel opens immediately; each section fills in as its
|
|
50
50
|
* request settles. Press r to refresh, Esc (or q) to close.
|
|
51
|
+
* The panel is a clamped, scrollable overlay in both TUI modes:
|
|
52
|
+
* ↑/↓/j/k line, PgUp/PgDn page, ctrl+u/d half page, Home/End
|
|
53
|
+
* (g/G) top/bottom, plus mouse wheel in fullscreen; it stays
|
|
54
|
+
* pinned to the bottom as sections settle.
|
|
51
55
|
*/
|
|
52
56
|
|
|
53
57
|
import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
54
|
-
import { Key, matchesKey, wrapTextWithAnsi } from "@earendil-works/pi-tui";
|
|
58
|
+
import { Key, matchesKey, wrapTextWithAnsi, type TUI } from "@earendil-works/pi-tui";
|
|
55
59
|
import { execFileSync } from "node:child_process";
|
|
56
60
|
import { existsSync, readdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
57
61
|
import { homedir, platform } from "node:os";
|
|
@@ -1000,7 +1004,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1000
1004
|
})
|
|
1001
1005
|
.join("\n\n");
|
|
1002
1006
|
const updated = lastUpdated ? `updated ${new Date(lastUpdated).toLocaleTimeString()}` : "";
|
|
1003
|
-
const footer = st.dim(interactive ?
|
|
1007
|
+
const footer = st.dim(interactive ? `↑↓/PgUp scroll · r refresh · Esc/q close · ${updated}` : updated);
|
|
1004
1008
|
return `${body}\n\n${footer}`;
|
|
1005
1009
|
};
|
|
1006
1010
|
|
|
@@ -1010,7 +1014,16 @@ export default function (pi: ExtensionAPI) {
|
|
|
1010
1014
|
return;
|
|
1011
1015
|
}
|
|
1012
1016
|
|
|
1013
|
-
|
|
1017
|
+
// The panel opens as a capturing overlay in both TUI modes: in
|
|
1018
|
+
// fullscreen the input-dock slot clips a tall plain component and
|
|
1019
|
+
// the alt screen routes arrows/PgUp/PgDn/wheel to the chat transcript,
|
|
1020
|
+
// and in regular mode an overlay taller than the terminal gets sliced
|
|
1021
|
+
// by the compositor (content beyond maxHeight is dropped, scrollback
|
|
1022
|
+
// never sees it). A focused overlay receives viewport keys in both
|
|
1023
|
+
// modes, so the panel clamps itself to the terminal height and scrolls
|
|
1024
|
+
// itself. (Wheel only reaches us in fullscreen; regular mode leaves
|
|
1025
|
+
// the mouse to the terminal.)
|
|
1026
|
+
const tuiFactory = async (tui: TUI, theme: any, _kb: unknown, done: () => void) => {
|
|
1014
1027
|
const st: Styler = {
|
|
1015
1028
|
bold: (s) => theme.bold(s),
|
|
1016
1029
|
dim: (s) => theme.fg("dim", s),
|
|
@@ -1019,6 +1032,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
1019
1032
|
};
|
|
1020
1033
|
let cachedWidth = 0;
|
|
1021
1034
|
let cachedLines: string[] = [];
|
|
1035
|
+
let scrollTop = 0;
|
|
1036
|
+
let stickyBottom = true;
|
|
1022
1037
|
const refresh = (): void => {
|
|
1023
1038
|
cachedWidth = 0;
|
|
1024
1039
|
tui.requestRender();
|
|
@@ -1026,25 +1041,79 @@ export default function (pi: ExtensionAPI) {
|
|
|
1026
1041
|
onUpdate = refresh;
|
|
1027
1042
|
// Show the panel immediately; each section fills in as its query settles.
|
|
1028
1043
|
runAll();
|
|
1044
|
+
|
|
1045
|
+
// ── Scrolling (overlay is clamped to the terminal height) ──
|
|
1046
|
+
// SGR wheel sequences reach handleInput in fullscreen because a
|
|
1047
|
+
// focused overlay defers viewport input; 64 = wheel up, 65 = down.
|
|
1048
|
+
const wheelRe = /^\x1b\[<(6[45]);/;
|
|
1049
|
+
// Cap one line below the terminal so the 100% overlay never slices us.
|
|
1050
|
+
const viewportH = (): number => Math.max(4, tui.terminal.rows - 1);
|
|
1051
|
+
const scrollable = (): boolean => cachedLines.length > viewportH();
|
|
1052
|
+
const maxScrollTop = (): number => Math.max(0, cachedLines.length - Math.max(1, viewportH() - 2));
|
|
1053
|
+
const applyScroll = (top: number): void => {
|
|
1054
|
+
if (!scrollable()) return;
|
|
1055
|
+
const max = maxScrollTop();
|
|
1056
|
+
scrollTop = Math.max(0, Math.min(max, top));
|
|
1057
|
+
stickyBottom = scrollTop >= max;
|
|
1058
|
+
tui.requestRender();
|
|
1059
|
+
};
|
|
1060
|
+
const scrollBy = (delta: number): void => applyScroll((stickyBottom ? maxScrollTop() : scrollTop) + delta);
|
|
1061
|
+
|
|
1029
1062
|
return {
|
|
1030
1063
|
render(width: number): string[] {
|
|
1031
1064
|
if (width !== cachedWidth) {
|
|
1032
1065
|
cachedWidth = width;
|
|
1033
1066
|
cachedLines = wrapTextWithAnsi(renderReport(st, true), width);
|
|
1034
1067
|
}
|
|
1035
|
-
|
|
1068
|
+
const viewH = viewportH();
|
|
1069
|
+
if (cachedLines.length <= viewH) return cachedLines;
|
|
1070
|
+
const bodyH = Math.max(1, viewH - 2);
|
|
1071
|
+
const max = maxScrollTop();
|
|
1072
|
+
scrollTop = stickyBottom ? max : Math.max(0, Math.min(scrollTop, max));
|
|
1073
|
+
stickyBottom = scrollTop >= max;
|
|
1074
|
+
const out: string[] = [
|
|
1075
|
+
st.dim(scrollTop > 0 ? ` ↑ ${scrollTop} line${scrollTop === 1 ? "" : "s"} above` : ""),
|
|
1076
|
+
];
|
|
1077
|
+
out.push(...cachedLines.slice(scrollTop, scrollTop + bodyH));
|
|
1078
|
+
const below = cachedLines.length - scrollTop - bodyH;
|
|
1079
|
+
out.push(st.dim(below > 0 ? ` ↓ ${below} line${below === 1 ? "" : "s"} below` : ""));
|
|
1080
|
+
return out;
|
|
1036
1081
|
},
|
|
1037
1082
|
handleInput(data: string): void {
|
|
1038
|
-
if (matchesKey(data, Key.escape) || data === "q")
|
|
1039
|
-
|
|
1083
|
+
if (matchesKey(data, Key.escape) || data === "q") {
|
|
1084
|
+
done();
|
|
1085
|
+
return;
|
|
1086
|
+
}
|
|
1087
|
+
if (data === "r") {
|
|
1040
1088
|
runAll();
|
|
1041
1089
|
refresh();
|
|
1090
|
+
return;
|
|
1091
|
+
}
|
|
1092
|
+
const wheel = wheelRe.exec(data);
|
|
1093
|
+
if (wheel) {
|
|
1094
|
+
scrollBy(wheel[1] === "65" ? 3 : -3);
|
|
1095
|
+
return;
|
|
1042
1096
|
}
|
|
1097
|
+
const page = Math.max(1, viewportH() - 2);
|
|
1098
|
+
const half = Math.max(1, Math.floor(viewportH() / 2));
|
|
1099
|
+
if (matchesKey(data, Key.up) || data === "k") scrollBy(-1);
|
|
1100
|
+
else if (matchesKey(data, Key.down) || data === "j") scrollBy(1);
|
|
1101
|
+
else if (matchesKey(data, Key.pageUp)) scrollBy(-page);
|
|
1102
|
+
else if (matchesKey(data, Key.pageDown)) scrollBy(page);
|
|
1103
|
+
else if (matchesKey(data, Key.ctrl("u"))) scrollBy(-half);
|
|
1104
|
+
else if (matchesKey(data, Key.ctrl("d"))) scrollBy(half);
|
|
1105
|
+
else if (matchesKey(data, Key.home) || data === "g") applyScroll(0);
|
|
1106
|
+
else if (matchesKey(data, Key.end) || data === "G") applyScroll(Number.MAX_SAFE_INTEGER);
|
|
1043
1107
|
},
|
|
1044
1108
|
invalidate(): void {
|
|
1045
1109
|
cachedWidth = 0;
|
|
1046
1110
|
},
|
|
1047
1111
|
};
|
|
1112
|
+
};
|
|
1113
|
+
|
|
1114
|
+
await ctx.ui.custom<void>(tuiFactory, {
|
|
1115
|
+
overlay: true,
|
|
1116
|
+
overlayOptions: { width: "100%", maxHeight: "100%" },
|
|
1048
1117
|
});
|
|
1049
1118
|
},
|
|
1050
1119
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-provider-status",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Show remaining quota and balance for LLM providers in pi — /status panel for Kimi Coding, Zhipu ZAI, MiniMax, DeepSeek, OpenRouter, Claude and Codex subscription windows",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -18,7 +18,9 @@
|
|
|
18
18
|
"license": "MIT",
|
|
19
19
|
"author": "cyzlmh",
|
|
20
20
|
"pi": {
|
|
21
|
-
"extensions": [
|
|
21
|
+
"extensions": [
|
|
22
|
+
"./extensions"
|
|
23
|
+
]
|
|
22
24
|
},
|
|
23
25
|
"repository": {
|
|
24
26
|
"type": "git",
|