@po.dev/pi-usage-dashboard 0.1.0 → 0.1.2
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/index.ts +28 -12
- package/package.json +17 -4
package/index.ts
CHANGED
|
@@ -781,14 +781,16 @@ class UsageComponent {
|
|
|
781
781
|
if (model.total === 0) return [...lines, th.fg("dim", " No usage data for this period"), ""];
|
|
782
782
|
|
|
783
783
|
const labelW = Math.max(formatAxisCost(model.max).length, 3);
|
|
784
|
-
const plotW = Math.max(10, Math.min(width - labelW - 2, model.days.length));
|
|
784
|
+
const plotW = Math.max(10, Math.min(Math.floor((width - labelW - 2) / 2), model.days.length));
|
|
785
785
|
const start = Math.max(0, model.days.length - plotW);
|
|
786
786
|
const days = model.days.slice(start);
|
|
787
|
+
const axisW = Math.max(0, days.length * 2 - 1);
|
|
787
788
|
const height = 8;
|
|
788
789
|
for (let row = height; row >= 1; row--) {
|
|
789
790
|
const threshold = (model.max * row) / height;
|
|
790
791
|
let line = th.fg("dim", `${row === height ? formatAxisCost(model.max) : row === 1 ? "$0" : ""}`.padStart(labelW) + " │");
|
|
791
|
-
for (
|
|
792
|
+
for (let d = 0; d < days.length; d++) {
|
|
793
|
+
const day = days[d]!;
|
|
792
794
|
let acc = 0, owner = -1;
|
|
793
795
|
for (let i = 0; i < model.providers.length; i++) {
|
|
794
796
|
const p = model.providers[i]!;
|
|
@@ -796,12 +798,12 @@ class UsageComponent {
|
|
|
796
798
|
acc += day.providers.get(p.name) ?? 0;
|
|
797
799
|
if (acc >= threshold) { owner = i; break; }
|
|
798
800
|
}
|
|
799
|
-
line += owner < 0 ? " " : seriesColor(owner) + "█" + COLOR_RESET;
|
|
801
|
+
line += (owner < 0 ? " " : seriesColor(owner) + "█" + COLOR_RESET) + (d === days.length - 1 ? "" : " ");
|
|
800
802
|
}
|
|
801
803
|
lines.push(line);
|
|
802
804
|
}
|
|
803
|
-
lines.push(th.fg("dim", " ".repeat(labelW) + " └" + "─".repeat(
|
|
804
|
-
lines.push(th.fg("dim", " ".repeat(labelW + 2) + (days[0]?.label ?? "") + " ".repeat(Math.max(1, days.
|
|
805
|
+
lines.push(th.fg("dim", " ".repeat(labelW) + " └" + "─".repeat(axisW)));
|
|
806
|
+
lines.push(th.fg("dim", " ".repeat(labelW + 2) + (days[0]?.label ?? "") + " ".repeat(Math.max(1, axisW - visibleWidth((days[0]?.label ?? "") + (days.at(-1)?.label ?? "")))) + (days.at(-1)?.label ?? "")));
|
|
805
807
|
lines.push("");
|
|
806
808
|
for (let i = 0; i < model.providers.length; i++) {
|
|
807
809
|
const p = model.providers[i]!;
|
|
@@ -824,11 +826,14 @@ class UsageComponent {
|
|
|
824
826
|
const total = detail ? detail.chars : this.promptSections.reduce((sum, section) => sum + section.chars, 0);
|
|
825
827
|
const source = this.historySelected ? `History ${this.historySelected.slice(0, 8)}` : "Current session";
|
|
826
828
|
const lines = [th.bold(detail ? `${detail.label} sources` : `${source} prompt sources`), th.fg("dim", detail ? "Esc back · source sizes" : "Assembled system prompt · source sizes · Enter details"), ""];
|
|
829
|
+
const colors: ("accent" | "success" | "warning" | "thinkingHigh")[] = ["accent", "success", "warning", "thinkingHigh"];
|
|
827
830
|
if (!detail) {
|
|
828
831
|
const widthBar = Math.max(20, Math.min(width - 4, 60));
|
|
829
|
-
const colors: ("accent" | "success" | "warning" | "thinkingHigh")[] = ["accent", "success", "warning", "thinkingHigh"];
|
|
830
832
|
let bar = "";
|
|
831
|
-
for (let i = 0; i < rows.length; i++)
|
|
833
|
+
for (let i = 0; i < rows.length; i++) {
|
|
834
|
+
if (i > 0) bar += " ";
|
|
835
|
+
bar += th.fg(colors[i % colors.length]!, "▬".repeat(Math.round(rows[i]!.chars / Math.max(total, 1) * widthBar)));
|
|
836
|
+
}
|
|
832
837
|
lines.push(bar, "");
|
|
833
838
|
}
|
|
834
839
|
for (let i = 0; i < rows.length; i++) {
|
|
@@ -837,8 +842,9 @@ class UsageComponent {
|
|
|
837
842
|
const pct = total ? `${(chars / total * 100).toFixed(1)}%` : "0.0%";
|
|
838
843
|
const selected = i === this.insightIndex;
|
|
839
844
|
const marker = selected ? th.fg("accent", "▸ ") : th.fg("dim", "· ");
|
|
845
|
+
const indicator = detail ? "" : th.fg(colors[i % colors.length]!, "■ ");
|
|
840
846
|
const suffix = th.fg("dim", `${formatNumber(chars)} chars ${pct}`);
|
|
841
|
-
lines.push(`${marker}${selected ? th.fg("accent", row.label) : row.label}${" ".repeat(Math.max(1, width - visibleWidth(marker + row.label + suffix)))}${suffix}`);
|
|
847
|
+
lines.push(`${marker}${indicator}${selected ? th.fg("accent", row.label) : row.label}${" ".repeat(Math.max(1, width - visibleWidth(marker + indicator + row.label + suffix)))}${suffix}`);
|
|
842
848
|
}
|
|
843
849
|
lines.push("", th.fg("dim", "[↑↓] select [Enter] open [Esc] back"));
|
|
844
850
|
return lines;
|
|
@@ -1126,18 +1132,27 @@ class UsageComponent {
|
|
|
1126
1132
|
// Extension Entry Point
|
|
1127
1133
|
// =============================================================================
|
|
1128
1134
|
|
|
1129
|
-
function
|
|
1135
|
+
function footerModelLabel(ctx: Pick<ExtensionCommandContext, "model" | "thinkingLevel">): string {
|
|
1136
|
+
const model = ctx.model;
|
|
1137
|
+
if (!model) return "no-model";
|
|
1138
|
+
const label = `(${model.provider}) ${model.id}`;
|
|
1139
|
+
return model.reasoning && ctx.thinkingLevel ? `${label} • ${ctx.thinkingLevel}` : label;
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
function setUsageFooter(ctx: Pick<ExtensionCommandContext, "ui" | "model" | "thinkingLevel">, totals: TotalStats): void {
|
|
1130
1143
|
ctx.ui.setFooter((_tui, theme) => ({
|
|
1131
1144
|
invalidate() {},
|
|
1132
1145
|
render(width: number): string[] {
|
|
1133
|
-
const
|
|
1134
|
-
|
|
1146
|
+
const usage = theme.fg("thinkingHigh", "Usage:") + " " + theme.fg("accent", formatCost(totals.cost)) + " · " + theme.fg("text", `${formatTokens(totals.tokens.total)} tokens`) + " · " + theme.fg("success", `↑${formatTokens(totals.tokens.input + totals.tokens.cacheWrite)}`) + " · " + theme.fg("warning", `↓${formatTokens(totals.tokens.output)}`) + " · " + theme.fg("thinkingHigh", `${formatTokens(totals.tokens.cacheRead + totals.tokens.cacheWrite)} cache`) + theme.fg("dim", " · (Today)");
|
|
1147
|
+
const model = theme.fg("dim", footerModelLabel(ctx));
|
|
1148
|
+
const gap = width - visibleWidth(usage) - visibleWidth(model);
|
|
1149
|
+
return [gap >= 2 ? usage + " ".repeat(gap) + model : truncateToWidth(usage, width)];
|
|
1135
1150
|
},
|
|
1136
1151
|
}));
|
|
1137
1152
|
}
|
|
1138
1153
|
|
|
1139
1154
|
export default function (pi: ExtensionAPI) {
|
|
1140
|
-
const refreshFooter = (ctx:
|
|
1155
|
+
const refreshFooter = (ctx: Pick<ExtensionCommandContext, "hasUI" | "ui" | "model" | "thinkingLevel">) => {
|
|
1141
1156
|
if (!ctx.hasUI) return;
|
|
1142
1157
|
void collectUsageData().then((data) => {
|
|
1143
1158
|
if (!data) return;
|
|
@@ -1155,6 +1170,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1155
1170
|
};
|
|
1156
1171
|
pi.on("session_start", (_event, ctx) => { snapshotPrompt(ctx); refreshFooter(ctx); });
|
|
1157
1172
|
pi.on("message_end", (_event, ctx) => { snapshotPrompt(ctx); refreshFooter(ctx); });
|
|
1173
|
+
pi.on("model_select", (_event, ctx) => { refreshFooter(ctx); });
|
|
1158
1174
|
pi.registerCommand("usage", {
|
|
1159
1175
|
description: "Show usage statistics dashboard",
|
|
1160
1176
|
handler: async (_args: string, ctx: ExtensionCommandContext) => {
|
package/package.json
CHANGED
|
@@ -1,13 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@po.dev/pi-usage-dashboard",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Local token and cost dashboard for pi sessions",
|
|
5
|
-
"keywords": [
|
|
5
|
+
"keywords": [
|
|
6
|
+
"pi-package",
|
|
7
|
+
"pi",
|
|
8
|
+
"usage",
|
|
9
|
+
"tokens",
|
|
10
|
+
"dashboard"
|
|
11
|
+
],
|
|
6
12
|
"license": "MIT",
|
|
7
|
-
"files": [
|
|
13
|
+
"files": [
|
|
14
|
+
"*.ts",
|
|
15
|
+
"README.md"
|
|
16
|
+
],
|
|
8
17
|
"peerDependencies": {
|
|
9
18
|
"@earendil-works/pi-coding-agent": "*",
|
|
10
19
|
"@earendil-works/pi-tui": "*"
|
|
11
20
|
},
|
|
12
|
-
"pi": {
|
|
21
|
+
"pi": {
|
|
22
|
+
"extensions": [
|
|
23
|
+
"./index.ts"
|
|
24
|
+
]
|
|
25
|
+
}
|
|
13
26
|
}
|