@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.
Files changed (2) hide show
  1. package/index.ts +28 -12
  2. 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 (const day of days) {
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(days.length)));
804
- lines.push(th.fg("dim", " ".repeat(labelW + 2) + (days[0]?.label ?? "") + " ".repeat(Math.max(1, days.length - 12)) + (days.at(-1)?.label ?? "")));
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++) bar += th.fg(colors[i % colors.length]!, "█".repeat(Math.round(rows[i]!.chars / Math.max(total, 1) * widthBar)));
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 setUsageFooter(ctx: { ui: ExtensionCommandContext["ui"] }, totals: TotalStats): void {
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 line = 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)");
1134
- return [truncateToWidth(line, width)];
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: { hasUI: boolean; ui: ExtensionCommandContext["ui"] }) => {
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.0",
3
+ "version": "0.1.2",
4
4
  "description": "Local token and cost dashboard for pi sessions",
5
- "keywords": ["pi-package", "pi", "usage", "tokens", "dashboard"],
5
+ "keywords": [
6
+ "pi-package",
7
+ "pi",
8
+ "usage",
9
+ "tokens",
10
+ "dashboard"
11
+ ],
6
12
  "license": "MIT",
7
- "files": ["*.ts", "README.md"],
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": { "extensions": ["./index.ts"] }
21
+ "pi": {
22
+ "extensions": [
23
+ "./index.ts"
24
+ ]
25
+ }
13
26
  }