From 6e89d1d123e5962093012c6cb4ad76289b807432 Mon Sep 17 00:00:00 2001 From: shahondin1624 Date: Thu, 23 Apr 2026 22:24:23 +0200 Subject: [PATCH] =?UTF-8?q?Add=20dark-mechanicus-indicator=20extension=20?= =?UTF-8?q?=E2=80=94=20pulsing=20cog=20+=20rotating=20quotes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- .gitignore | 1 + dark-mechanicus-indicator.ts | 180 +++++++++++++++++++++++++++++++++++ 2 files changed, 181 insertions(+) create mode 100644 dark-mechanicus-indicator.ts diff --git a/.gitignore b/.gitignore index 09e8c6b..60a76a0 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ !/.gitignore !/README.md !/ai-server/ +!/dark-mechanicus-indicator.ts !/local-llama.ts !/markdown-body-color.ts !/mechanicus-banner.ts diff --git a/dark-mechanicus-indicator.ts b/dark-mechanicus-indicator.ts new file mode 100644 index 0000000..4cf8a05 --- /dev/null +++ b/dark-mechanicus-indicator.ts @@ -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(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"); + }, + }); +}