Files
dotfiles/pi/files/agent/extensions/custom-footer.ts
2026-02-21 12:56:20 +00:00

65 lines
2.1 KiB
TypeScript

/**
* Custom Footer Extension - demonstrates ctx.ui.setFooter()
*
* footerData exposes data not otherwise accessible:
* - getGitBranch(): current git branch
* - getExtensionStatuses(): texts from ctx.ui.setStatus()
*
* Token stats come from ctx.sessionManager/ctx.model (already accessible).
*/
import type { AssistantMessage } from "@mariozechner/pi-ai";
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
import { truncateToWidth, visibleWidth } from "@mariozechner/pi-tui";
export default function (pi: ExtensionAPI) {
let enabled = false;
pi.registerCommand("footer", {
description: "Toggle custom footer",
handler: async (_args, ctx) => {
enabled = !enabled;
if (enabled) {
ctx.ui.setFooter((tui, theme, footerData) => {
const unsub = footerData.onBranchChange(() => tui.requestRender());
return {
dispose: unsub,
invalidate() {},
render(width: number): string[] {
// Compute tokens from ctx (already accessible to extensions)
let input = 0,
output = 0,
cost = 0;
for (const e of ctx.sessionManager.getBranch()) {
if (e.type === "message" && e.message.role === "assistant") {
const m = e.message as AssistantMessage;
input += m.usage.input;
output += m.usage.output;
cost += m.usage.cost.total;
}
}
// Get git branch (not otherwise accessible)
const branch = footerData.getGitBranch();
const fmt = (n: number) => (n < 1000 ? `${n}` : `${(n / 1000).toFixed(1)}k`);
const left = theme.fg("dim", `${fmt(input)}${fmt(output)} $${cost.toFixed(3)}`);
const branchStr = branch ? ` (${branch})` : "";
const right = theme.fg("dim", `${ctx.model?.id || "no-model"}${branchStr}`);
const pad = " ".repeat(Math.max(1, width - visibleWidth(left) - visibleWidth(right)));
return [truncateToWidth(left + pad + right, width)];
},
};
});
ctx.ui.notify("Custom footer enabled", "info");
} else {
ctx.ui.setFooter(undefined);
ctx.ui.notify("Default footer restored", "info");
}
},
});
}