/ extensions / titlebar-spinner.ts
titlebar-spinner.ts
1 /** 2 * Titlebar Spinner Extension 3 * 4 * Shows a braille spinner animation in the terminal title while the agent is working. 5 * Uses `ctx.ui.setTitle()` to update the terminal title via the extension API. 6 * 7 * Usage: 8 * pi --extension examples/extensions/titlebar-spinner.ts 9 */ 10 11 import path from "node:path"; 12 import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent"; 13 14 const BRAILLE_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]; 15 16 function getBaseTitle(pi: ExtensionAPI): string { 17 const cwd = path.basename(process.cwd()); 18 const session = pi.getSessionName(); 19 return session ? `π - ${session} - ${cwd}` : `π - ${cwd}`; 20 } 21 22 export default function (pi: ExtensionAPI) { 23 let timer: ReturnType<typeof setInterval> | null = null; 24 let frameIndex = 0; 25 26 function stopAnimation(ctx: ExtensionContext) { 27 if (timer) { 28 clearInterval(timer); 29 timer = null; 30 } 31 frameIndex = 0; 32 ctx.ui.setTitle(getBaseTitle(pi)); 33 } 34 35 function startAnimation(ctx: ExtensionContext) { 36 stopAnimation(ctx); 37 timer = setInterval(() => { 38 const frame = BRAILLE_FRAMES[frameIndex % BRAILLE_FRAMES.length]; 39 const cwd = path.basename(process.cwd()); 40 const session = pi.getSessionName(); 41 const title = session ? `${frame} π - ${session} - ${cwd}` : `${frame} π - ${cwd}`; 42 ctx.ui.setTitle(title); 43 frameIndex++; 44 }, 80); 45 } 46 47 pi.on("agent_start", async (_event, ctx) => { 48 startAnimation(ctx); 49 }); 50 51 pi.on("agent_end", async (_event, ctx) => { 52 stopAnimation(ctx); 53 }); 54 55 pi.on("session_shutdown", async (_event, ctx) => { 56 stopAnimation(ctx); 57 }); 58 }