BIG pi update with claude chat

This commit is contained in:
Jonas H
2026-04-24 14:22:59 +02:00
parent fbb00a49ba
commit 248667468c
24 changed files with 4225 additions and 1112 deletions

View File

@@ -4,11 +4,10 @@
* Replaces the built-in pi footer with a single clean line that assembles
* status from all other extensions:
*
* \uEF85 | S ⣿⣶⣀⣀⣀ 34% 2h 55m | W ⣿⣿⣷⣀⣀ 68% ⟳ Fri 09:00 | C ⣿⣀⣀⣀⣀ 20% | Sonnet 4.6 | rust-analyzer | MCP: 1/2
* ~dir | S ⣿⣶⣀⣀⣀ 34% 2h 55m | W ⣿⣿⣷⣀⣀ 68% ⟳ Fri 09:00 | C ⣿⣀⣀⣀⣀ 20% | Sonnet 4.6 | rust-analyzer | MCP: 1/2
*
* Status sources:
* "claude-account" — set by claude-account-switch.ts → just the icon
* "usage-bars" — set by usage-bars extension → S/W bars (pass-through)
* usage:update event — set by usage-bars extension → S/W bars (Claude usage, always shown)
* ctx.getContextUsage() → C bar (rendered here)
* ctx.model → model short name
* "lsp" — set by lsp-pi extension → strip "LSP " prefix
@@ -77,11 +76,6 @@ function formatDurationMs(ms: number): string {
return "<1m";
}
// Nerd Font codepoints matched to what claude-account-switch.ts emits
const ICON_PERSONAL = "\uEF85"; // U+EF85 — home
const ICON_WORK = "\uDB80\uDCD6"; // U+F00D6 — briefcase (surrogate pair)
const ICON_UNKNOWN = "\uF420"; // U+F420 — claude default
export default function (pi: ExtensionAPI) {
let ctx: any = null;
let tuiRef: any = null;
@@ -112,18 +106,7 @@ export default function (pi: ExtensionAPI) {
: cwd;
parts.push(theme.fg("dim", dir));
// 2. Account icon
const acctRaw = statuses.get("claude-account");
if (acctRaw !== undefined) {
const clean = stripAnsi(acctRaw).trim();
let icon: string;
if (clean.includes("personal")) icon = ICON_PERSONAL;
else if (clean.includes("work")) icon = ICON_WORK;
else icon = ICON_UNKNOWN;
parts.push(theme.fg("dim", icon));
}
// 3. S / W usage bars + C bar — joined as one |-separated block
// 2. S / W usage bars + C bar — joined as one |-separated block
const usageRaw = statuses.get("usage-bars");
const contextUsage = ctx?.getContextUsage?.();
{
@@ -135,8 +118,8 @@ export default function (pi: ExtensionAPI) {
const session = Math.max(0, Math.min(100, Math.round(usageSession)));
const weekly = Math.max(0, Math.min(100, Math.round(usageWeekly)));
let sPart = theme.fg("muted", "S ") + renderBrailleBar(theme, session) + " " + theme.fg("dim", `${session}%`);
let wPart = theme.fg("muted", "W ") + renderBrailleBar(theme, weekly) + " " + theme.fg("dim", `${weekly}%`);
let sPart = theme.fg("muted", "\uF4F5 S ") + renderBrailleBar(theme, session) + " " + theme.fg("dim", `${session}%`);
let wPart = theme.fg("muted", "\uF4F5 W ") + renderBrailleBar(theme, weekly) + " " + theme.fg("dim", `${weekly}%`);
if (sessionResetsAt !== null) {
const msLeft = sessionResetsAt - Date.now();
@@ -156,34 +139,51 @@ export default function (pi: ExtensionAPI) {
if (contextUsage && contextUsage.percent !== null) {
const pct = Math.round(contextUsage.percent);
const chatStatus = statuses.get("chat-claude");
const isChatActive = !!chatStatus && chatStatus.includes("Claude");
// When chat is active and context is high, show warning indicator
let cLabel = "C";
let cColor = "muted";
if (isChatActive && pct >= 70) {
cLabel = pct >= 90 ? "C⚠" : "C⚡";
cColor = pct >= 90 ? "error" : "warning";
}
const cBar =
theme.fg("muted", "C ") +
theme.fg(cColor, cLabel + " ") +
renderBrailleBar(theme, pct) +
" " +
theme.fg("dim", `${pct}%`);
theme.fg(pct >= 70 && isChatActive ? "warning" : "dim", `${pct}%`);
block = block ? block + pipeSep + cBar : cBar;
}
if (block) parts.push(block);
}
// 4. Model short name
// 3. Model short name
const modelId = ctx?.model?.id;
if (modelId) parts.push(theme.fg("dim", getModelShortName(modelId)));
// 5. LSP — strip "LSP" prefix and activity dot
// 4. LSP — strip "LSP" prefix and activity dot
const lspRaw = statuses.get("lsp");
if (lspRaw) {
const clean = stripAnsi(lspRaw).trim().replace(/^LSP\s*[•·]?\s*/i, "").trim();
if (clean) parts.push(theme.fg("dim", clean));
}
// 6. MCP — strip " servers" suffix
// 5. MCP — strip " servers" suffix
const mcpRaw = statuses.get("mcp");
if (mcpRaw) {
const clean = stripAnsi(mcpRaw).trim().replace(/\s*servers?.*$/, "").trim();
if (clean) parts.push(theme.fg("dim", clean));
}
// 6. Active Claude chat session
const chatRaw = statuses.get("chat-claude");
if (chatRaw) {
parts.push(theme.fg("accent", stripAnsi(chatRaw).trim()));
}
return parts.join(sep);
}
@@ -209,7 +209,7 @@ export default function (pi: ExtensionAPI) {
// ---------------------------------------------------------------------------
// Event handlers
// ---------------------------------------------------------------------------
pi.on("session_start", async (_event, _ctx) => {
pi.on("session_start", (_event, _ctx) => {
ctx = _ctx;
installFooter(_ctx);
});
@@ -230,12 +230,7 @@ export default function (pi: ExtensionAPI) {
if (tuiRef) tuiRef.requestRender();
});
// Re-render when account switches (usage:update comes from usage-bars setStatus which
// already triggers a render, but account icon needs a nudge too)
pi.events.on("claude-account:switched", () => {
if (tuiRef) tuiRef.requestRender();
});
// Listen for usage updates — store raw values so we can build bars + dynamic
// countdown directly rather than parsing the ANSI status string from usage-bars.
pi.events.on("usage:update", (data: any) => {