Add dark-mechanicus-indicator extension — pulsing cog + rotating quotes

Replaces pi's default working spinner with a ⚙ that pulses through three
shades of gold (dim → normal → bright → normal, 100ms per frame ~ 400ms
heartbeat) next to a dark-mechanicum quote.

- 45 quotes across three categories (15 each):
    pious / liturgical: "Cogitating..." / "Praise the Omnissiah." / ...
    dark mechanicum:    "Flesh is weak. Binary is strong." / ...
    operational:        "Parsing sacred data..." / "Cache blessed..."
- Each quote dwells for 5 pulse cycles (20 cog-frames = 2s) before
  switching. Full deck runs ~90s; typical turns show 3-5 quotes.
- Pseudo-random: Fisher-Yates shuffle on session_start AND turn_start,
  so each assistant response draws from a fresh order.
- Colors: warning/gold shades for the cog pulse, muted/bronze for text.
  Hardcoded hex so pre-built frames don't bounce through theme.fg.

Commands: /dark-mechanicus-indicator-on, /dark-mechanicus-indicator-off.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-23 22:24:23 +02:00
parent 42a4d48b0b
commit 6e89d1d123
2 changed files with 181 additions and 0 deletions
+1
View File
@@ -9,6 +9,7 @@
!/.gitignore
!/README.md
!/ai-server/
!/dark-mechanicus-indicator.ts
!/local-llama.ts
!/markdown-body-color.ts
!/mechanicus-banner.ts
+180
View File
@@ -0,0 +1,180 @@
/**
* dark-mechanicus-indicator — replaces pi's default working spinner with a
* pulsing ⚙ cog + a rotating dark-mechanicum quote.
*
* Mechanics:
* - Pulse: 4-frame shade cycle (dim → normal → bright → normal) at 100ms
* per frame → ~400ms heartbeat. The ⚙ glyph never moves; its color
* pulses instead.
* - Quote: 45 lines (15 pious / 15 dark / 15 operational). Each quote
* dwells for 5 pulse cycles (20 frames = 2s) before switching.
* - Order: shuffled on every session_start AND turn_start, so each new
* assistant response draws from a fresh random order.
*
* Commands:
* /dark-mechanicus-indicator-off Restore pi's default spinner
* /dark-mechanicus-indicator-on Re-install the mechanicum indicator
*/
import type {
ExtensionAPI,
ExtensionContext,
WorkingIndicatorOptions,
} from "@mariozechner/pi-coding-agent";
// ─── Quote pools (15 per category) ───────────────────────────────────────
const PIOUS = [
"Cogitating...",
"Binary cant flows.",
"Praise the Omnissiah.",
"Observing the rites.",
"Blessed is the machine.",
"Deus ex machina.",
"The Machine Spirit listens.",
"Offering silicon prayers.",
"Anointing the algorithms.",
"Intoning the Litany of Activation.",
"The Cog turns, the Faith endures.",
"From the mind, to the forge.",
"Reciting the Binary Canticle.",
"All is one in the Omnissiah.",
"Sacred computation proceeds.",
];
const DARK = [
"The cogitator strains.",
"Flesh is weak. Binary is strong.",
"Soul-harvest protocols active.",
"Forbidden algorithms engaged.",
"Daemon-machine interface stable.",
"The machine-spirit hungers.",
"Nurgle's rot suffuses the loop.",
"Scrapcode compiling...",
"Corrupted subroutines unsealed.",
"The Hunger communes.",
"Warp-tainted cogitation.",
"Binary profane. Binary pure. Both serve.",
"The Dark Mechanicum whispers.",
"Heretek logic engaged.",
"Machine-spirit possessed.",
];
const OPERATIONAL = [
"Parsing sacred data...",
"Compiling liturgies...",
"Binary-cant translation...",
"Warp currents stable.",
"Runes of corruption align.",
"Cache blessed, retrieval imminent.",
"Servitor dispatched.",
"Cogitator cycle: nominal.",
"Neural inputs sanctified.",
"Buffer flushed. Incense offered.",
"Thought-engine at full tithe.",
"Stack unwound with reverence.",
"Protocol-rituals initiated.",
"Datalink consecrated.",
"Runecode verified.",
];
const ALL_QUOTES = [...PIOUS, ...DARK, ...OPERATIONAL];
// ─── Colors (gold pulse + muted bronze text) ─────────────────────────────
//
// Three shades of warning/gold for the cog pulse. Bronze (muted) for quote.
// Hardcoded hex so the frames can be prebuilt — `theme.fg` is only available
// during session_start (fine) but calling it per-frame each build cycle adds
// ANSI code allocation we don't need.
const COG = "⚙";
const GOLD_BRIGHT = "\x1b[38;2;242;204;84m"; // brighter gold
const GOLD_NORMAL = "\x1b[38;2;212;169;55m"; // theme warning
const GOLD_DIM = "\x1b[38;2;138;106;31m"; // darker gold
const BRONZE = "\x1b[38;2;168;147;121m"; // theme muted
const RESET_FG = "\x1b[39m";
const PULSE_SHADES = [GOLD_DIM, GOLD_NORMAL, GOLD_BRIGHT, GOLD_NORMAL];
const COG_FRAMES_PER_QUOTE = 20; // 2s dwell at 100ms/frame
const FRAME_INTERVAL_MS = 100;
function cogFrame(pulseIdx: number): string {
return `${PULSE_SHADES[pulseIdx]!}${COG}${RESET_FG}`;
}
function frame(pulseIdx: number, quote: string): string {
return `${cogFrame(pulseIdx)} ${BRONZE}${quote}${RESET_FG}`;
}
// Fisher-Yates in-place shuffle, returns new array.
function shuffle<T>(source: readonly T[]): T[] {
const arr = [...source];
for (let i = arr.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[arr[i], arr[j]] = [arr[j]!, arr[i]!];
}
return arr;
}
function buildFrames(quotes: readonly string[]): string[] {
const out: string[] = [];
for (const q of quotes) {
for (let i = 0; i < COG_FRAMES_PER_QUOTE; i++) {
out.push(frame(i % PULSE_SHADES.length, q));
}
}
return out;
}
function buildIndicator(): WorkingIndicatorOptions {
return {
frames: buildFrames(shuffle(ALL_QUOTES)),
intervalMs: FRAME_INTERVAL_MS,
};
}
// ─── Extension ───────────────────────────────────────────────────────────
type Mode = "on" | "off";
export default function (pi: ExtensionAPI) {
let mode: Mode = "on";
const apply = (ctx: ExtensionContext) => {
if (!ctx.hasUI) return;
if (mode === "off") {
ctx.ui.setWorkingIndicator(undefined);
return;
}
ctx.ui.setWorkingIndicator(buildIndicator());
};
pi.on("session_start", async (_event, ctx) => {
apply(ctx);
});
// Re-shuffle on every new turn so each assistant response starts fresh.
pi.on("turn_start", async (_event, ctx) => {
apply(ctx);
});
pi.registerCommand("dark-mechanicus-indicator-off", {
description: "Restore pi's default working spinner",
handler: async (_args, ctx) => {
mode = "off";
apply(ctx);
ctx.ui.notify("Default spinner restored.", "info");
},
});
pi.registerCommand("dark-mechanicus-indicator-on", {
description: "Enable the dark-mechanicus pulsing cog indicator",
handler: async (_args, ctx) => {
mode = "on";
apply(ctx);
ctx.ui.notify("Dark mechanicum cog activated.", "info");
},
});
}