Files
dotfiles/home/.pi/agent/extensions/terminal-status-title.js
T
Lap Tran 4c6e85c97c Switch firstmate to OpenCode 2 and refresh nix-darwin pins
Point fm at the isolated oc2 wrapper, upgrade Homebrew packages on rebuild, and declare ffmpeg, docker-desktop, puremac, utm, node, and tea. Update nixpkgs, home-manager, and nix-homebrew locks. Track Pi agent config, crew-dispatch, and the Gitea PR helper that home.nix already expected.
2026-09-18 13:58:49 -04:00

138 lines
3.1 KiB
JavaScript

const DEFAULT_TITLE = "π";
const PREFIX = "π";
const MAX_TITLE_LENGTH = 40;
const SPINNER_INTERVAL_MS = 120;
const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
function truncateTitle(title) {
if (title.length <= MAX_TITLE_LENGTH) return title;
return title.slice(0, MAX_TITLE_LENGTH - 3) + "...";
}
function basename(path) {
if (!path) return DEFAULT_TITLE;
const trimmed = path.replace(/[\\/]+$/, "");
if (!trimmed) return DEFAULT_TITLE;
return trimmed.split(/[\\/]/).pop() || DEFAULT_TITLE;
}
function getSessionName(pi) {
const name = pi.getSessionName?.();
return typeof name === "string" ? name.trim() : "";
}
function getRawTitle(pi, ctx) {
return getSessionName(pi) || basename(ctx.cwd);
}
function isSpinningStatus(status) {
return status === "working";
}
function statusIndicator(status, spinnerFrame) {
if (isSpinningStatus(status)) {
if (SPINNER_FRAMES.length === 0) return "◉";
return SPINNER_FRAMES[spinnerFrame % SPINNER_FRAMES.length];
}
if (status === "done") return "✓";
if (status === "error") return "✗";
return "○";
}
function formatTitle(pi, ctx, status, spinnerFrame) {
const rawTitle = getRawTitle(pi, ctx);
const suffix = rawTitle === DEFAULT_TITLE ? DEFAULT_TITLE : `${PREFIX} | ${truncateTitle(rawTitle)}`;
return `${statusIndicator(status, spinnerFrame)} | ${suffix}`;
}
export default function terminalStatusTitle(pi) {
let status = "idle";
let spinnerFrame = 0;
let spinnerInterval;
let deferredWrite;
let lastCtx;
function clearDeferredWrite() {
if (!deferredWrite) return;
clearTimeout(deferredWrite);
deferredWrite = undefined;
}
function writeTitle(ctx = lastCtx) {
if (!ctx?.hasUI) return;
lastCtx = ctx;
ctx.ui.setTitle(formatTitle(pi, ctx, status, spinnerFrame));
}
function stopSpinner() {
if (!spinnerInterval) return;
clearInterval(spinnerInterval);
spinnerInterval = undefined;
spinnerFrame = 0;
}
function startSpinner(ctx) {
if (!ctx?.hasUI || spinnerInterval) return;
spinnerFrame = 0;
spinnerInterval = setInterval(() => {
if (!isSpinningStatus(status)) {
stopSpinner();
return;
}
spinnerFrame = (spinnerFrame + 1) % SPINNER_FRAMES.length;
writeTitle();
}, SPINNER_INTERVAL_MS);
spinnerInterval.unref?.();
}
function setStatus(nextStatus, ctx) {
clearDeferredWrite();
status = nextStatus;
lastCtx = ctx;
if (isSpinningStatus(status)) {
startSpinner(ctx);
} else {
stopSpinner();
}
writeTitle(ctx);
}
function scheduleWrite(ctx) {
clearDeferredWrite();
deferredWrite = setTimeout(() => {
deferredWrite = undefined;
writeTitle(ctx);
}, 0);
deferredWrite.unref?.();
}
pi.on("session_start", async (_event, ctx) => {
setStatus("idle", ctx);
scheduleWrite(ctx);
});
pi.on("agent_start", async (_event, ctx) => {
setStatus("working", ctx);
});
pi.on("agent_settled", async (_event, ctx) => {
setStatus("done", ctx);
});
pi.on("session_shutdown", async () => {
clearDeferredWrite();
stopSpinner();
});
}