Files
pi-skill/extensions/agent-banner.ts
2026-05-25 16:41:08 +07:00

91 lines
2.8 KiB
TypeScript

// ABOUTME: Displays ASCII art banner above the editor on session start.
// ABOUTME: Reads art from ~/Desktop/agent.txt or uses embedded default; hides on first input.
/**
* Agent Banner — ASCII art at the top of the pi app on startup
*
* Displays the agent logo/banner above the editor when a session starts or when
* switching to a new session (/new). Hides automatically on first user input.
* Art is read from ~/Desktop/agent.txt, or falls back to embedded default.
* Footer is handled by footer.ts (model widget + status bar).
*
* Usage: Add to packages in settings.json
*/
import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
import { readFileSync, existsSync } from "node:fs";
import { join } from "node:path";
import { homedir } from "node:os";
import { applyExtensionDefaults } from "./lib/themeMap.ts";
const DEFAULT_ART = ` ▄▄
█████▄ ▄████▄ ▄████▄ █████▄ ▄██▄▄▄
▄▄▄▄██ ██ ██ ██▄▄██ ██ ██ ▀██▀▀▀
██▄▄██ ██▄▄██ ██▄▄▄▄ ██ ██ ██▄▄▄
▀▀▀▀▀ ▀▀▀██ ▀▀▀▀▀ ▀▀ ▀▀ ▀▀▀▀
████▀ `;
function loadArt(): string {
const path = join(homedir(), "Desktop", "agent.txt");
if (existsSync(path)) {
try {
return readFileSync(path, "utf-8").trimEnd();
} catch {
// fall through to default
}
}
return DEFAULT_ART;
}
export function showBanner(ctx: ExtensionContext) {
if (!ctx.hasUI) return;
const art = loadArt();
const split = art.split("\n");
const firstNonEmpty = split.findIndex((l) => l.trim() !== "");
const lines = firstNonEmpty >= 0 ? split.slice(firstNonEmpty) : split;
ctx.ui.setWidget(
"agent-banner",
(_tui, theme) => ({
invalidate() {},
render(width: number): string[] {
const rendered = lines.map((line) => theme.fg("accent", line));
rendered.push("");
return rendered;
},
}),
{ placement: "aboveEditor" },
);
}
let bannerCtx: ExtensionContext | null = null;
let bannerVisible = false;
export function isBannerVisible(): boolean {
return bannerVisible;
}
export default function (pi: ExtensionAPI) {
pi.on("session_start", async (_event, ctx: ExtensionContext) => {
applyExtensionDefaults(import.meta.url, ctx);
bannerCtx = ctx;
bannerVisible = true;
showBanner(ctx);
});
// Show banner when switching to a new session (/new)
pi.on("session_switch", async (_event, ctx: ExtensionContext) => {
bannerCtx = ctx;
bannerVisible = true;
showBanner(ctx);
});
// Hide banner on first user input — art shows only until you start typing
pi.on("input", async () => {
if (bannerCtx?.hasUI) {
bannerCtx.ui.setWidget("agent-banner", undefined);
bannerVisible = false;
}
});
}