Something went wrong. Try again.
fork of prime agent with changes i want but are not upstreamable probqbly (and some fixes)
Something went wrong. Try again.
11 kB · 382 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383/** * Armin says hi! A fun easter egg with animated XBM art. */
import type { Component, TUI } from "@earendil-works/pi-tui";import { theme } from "../theme/theme.js";
// XBM image: 31x36 pixels, LSB first, 1=background, 0=foregroundconst WIDTH = 31;const HEIGHT = 36;const BITS = [ 0xff, 0xff, 0xff, 0x7f, 0xff, 0xf0, 0xff, 0x7f, 0xff, 0xed, 0xff, 0x7f, 0xff, 0xdb, 0xff, 0x7f, 0xff, 0xb7, 0xff, 0x7f, 0xff, 0x77, 0xfe, 0x7f, 0x3f, 0xf8, 0xfe, 0x7f, 0xdf, 0xff, 0xfe, 0x7f, 0xdf, 0x3f, 0xfc, 0x7f, 0x9f, 0xc3, 0xfb, 0x7f, 0x6f, 0xfc, 0xf4, 0x7f, 0xf7, 0x0f, 0xf7, 0x7f, 0xf7, 0xff, 0xf7, 0x7f, 0xf7, 0xff, 0xe3, 0x7f, 0xf7, 0x07, 0xe8, 0x7f, 0xef, 0xf8, 0x67, 0x70, 0x0f, 0xff, 0xbb, 0x6f, 0xf1, 0x00, 0xd0, 0x5b, 0xfd, 0x3f, 0xec, 0x53, 0xc1, 0xff, 0xef, 0x57, 0x9f, 0xfd, 0xee, 0x5f, 0x9f, 0xfc, 0xae, 0x5f, 0x1f, 0x78, 0xac, 0x5f, 0x3f, 0x00, 0x50, 0x6c, 0x7f, 0x00, 0xdc, 0x77, 0xff, 0xc0, 0x3f, 0x78, 0xff, 0x01, 0xf8, 0x7f, 0xff, 0x03, 0x9c, 0x78, 0xff, 0x07, 0x8c, 0x7c, 0xff, 0x0f, 0xce, 0x78, 0xff, 0xff, 0xcf, 0x7f, 0xff, 0xff, 0xcf, 0x78, 0xff, 0xff, 0xdf, 0x78, 0xff, 0xff, 0xdf, 0x7d, 0xff, 0xff, 0x3f, 0x7e, 0xff, 0xff, 0xff, 0x7f,];
const BYTES_PER_ROW = Math.ceil(WIDTH / 8);const DISPLAY_HEIGHT = Math.ceil(HEIGHT / 2); // Half-block rendering
type Effect = "typewriter" | "scanline" | "rain" | "fade" | "crt" | "glitch" | "dissolve";
const EFFECTS: Effect[] = ["typewriter", "scanline", "rain", "fade", "crt", "glitch", "dissolve"];
// Get pixel at (x, y): true = foreground, false = backgroundfunction getPixel(x: number, y: number): boolean { if (y >= HEIGHT) return false; const byteIndex = y * BYTES_PER_ROW + Math.floor(x / 8); const bitIndex = x % 8; return ((BITS[byteIndex] >> bitIndex) & 1) === 0;}
// Get the character for a cell (2 vertical pixels packed)function getChar(x: number, row: number): string { const upper = getPixel(x, row * 2); const lower = getPixel(x, row * 2 + 1); if (upper && lower) return "█"; if (upper) return "▀"; if (lower) return "▄"; return " ";}
// Build the final image gridfunction buildFinalGrid(): string[][] { const grid: string[][] = []; for (let row = 0; row < DISPLAY_HEIGHT; row++) { const line: string[] = []; for (let x = 0; x < WIDTH; x++) { line.push(getChar(x, row)); } grid.push(line); } return grid;}
export class ArminComponent implements Component { private ui: TUI; private interval: ReturnType<typeof setInterval> | null = null; private effect: Effect; private finalGrid: string[][]; private currentGrid: string[][]; private effectState: Record<string, unknown> = {}; private cachedLines: string[] = []; private cachedWidth = 0; private gridVersion = 0; private cachedVersion = -1;
constructor(ui: TUI) { this.ui = ui; this.effect = EFFECTS[Math.floor(Math.random() * EFFECTS.length)]; this.finalGrid = buildFinalGrid(); this.currentGrid = this.createEmptyGrid();
this.initEffect(); this.startAnimation(); }
invalidate(): void { this.cachedWidth = 0; }
render(width: number): string[] { if (width === this.cachedWidth && this.cachedVersion === this.gridVersion) { return this.cachedLines; }
const padding = 1; const availableWidth = width - padding;
this.cachedLines = this.currentGrid.map((row) => { // Clip row to available width before applying color const clipped = row.slice(0, availableWidth).join(""); const padRight = Math.max(0, width - padding - clipped.length); return ` ${theme.fg("accent", clipped)}${" ".repeat(padRight)}`; });
// Add "ARMIN SAYS HI" at the end const message = "ARMIN SAYS HI"; const msgPadRight = Math.max(0, width - padding - message.length); this.cachedLines.push(` ${theme.fg("accent", message)}${" ".repeat(msgPadRight)}`);
this.cachedWidth = width; this.cachedVersion = this.gridVersion;
return this.cachedLines; }
private createEmptyGrid(): string[][] { return Array.from({ length: DISPLAY_HEIGHT }, () => Array(WIDTH).fill(" ")); }
private initEffect(): void { switch (this.effect) { case "typewriter": this.effectState = { pos: 0 }; break; case "scanline": this.effectState = { row: 0 }; break; case "rain": // Track falling position for each column this.effectState = { drops: Array.from({ length: WIDTH }, () => ({ y: -Math.floor(Math.random() * DISPLAY_HEIGHT * 2), settled: 0, })), }; break; case "fade": { // Shuffle all pixel positions const positions: [number, number][] = []; for (let row = 0; row < DISPLAY_HEIGHT; row++) { for (let x = 0; x < WIDTH; x++) { positions.push([row, x]); } } // Fisher-Yates shuffle for (let i = positions.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [positions[i], positions[j]] = [positions[j], positions[i]]; } this.effectState = { positions, idx: 0 }; break; } case "crt": this.effectState = { expansion: 0 }; break; case "glitch": this.effectState = { phase: 0, glitchFrames: 8 }; break; case "dissolve": { // Start with random noise this.currentGrid = Array.from({ length: DISPLAY_HEIGHT }, () => Array.from({ length: WIDTH }, () => { const chars = [" ", "░", "▒", "▓", "█", "▀", "▄"]; return chars[Math.floor(Math.random() * chars.length)]; }), ); // Shuffle positions for gradual resolve const dissolvePositions: [number, number][] = []; for (let row = 0; row < DISPLAY_HEIGHT; row++) { for (let x = 0; x < WIDTH; x++) { dissolvePositions.push([row, x]); } } for (let i = dissolvePositions.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [dissolvePositions[i], dissolvePositions[j]] = [dissolvePositions[j], dissolvePositions[i]]; } this.effectState = { positions: dissolvePositions, idx: 0 }; break; } } }
private startAnimation(): void { const fps = this.effect === "glitch" ? 60 : 30; this.interval = setInterval(() => { const done = this.tickEffect(); this.updateDisplay(); this.ui.requestRender(); if (done) { this.stopAnimation(); } }, 1000 / fps); }
private stopAnimation(): void { if (this.interval) { clearInterval(this.interval); this.interval = null; } }
private tickEffect(): boolean { switch (this.effect) { case "typewriter": return this.tickTypewriter(); case "scanline": return this.tickScanline(); case "rain": return this.tickRain(); case "fade": return this.tickFade(); case "crt": return this.tickCrt(); case "glitch": return this.tickGlitch(); case "dissolve": return this.tickDissolve(); default: return true; } }
private tickTypewriter(): boolean { const state = this.effectState as { pos: number }; const pixelsPerFrame = 3;
for (let i = 0; i < pixelsPerFrame; i++) { const row = Math.floor(state.pos / WIDTH); const x = state.pos % WIDTH; if (row >= DISPLAY_HEIGHT) return true; this.currentGrid[row][x] = this.finalGrid[row][x]; state.pos++; } return false; }
private tickScanline(): boolean { const state = this.effectState as { row: number }; if (state.row >= DISPLAY_HEIGHT) return true;
// Copy row for (let x = 0; x < WIDTH; x++) { this.currentGrid[state.row][x] = this.finalGrid[state.row][x]; } state.row++; return false; }
private tickRain(): boolean { const state = this.effectState as { drops: { y: number; settled: number }[]; };
let allSettled = true; this.currentGrid = this.createEmptyGrid();
for (let x = 0; x < WIDTH; x++) { const drop = state.drops[x];
// Draw settled pixels for (let row = DISPLAY_HEIGHT - 1; row >= DISPLAY_HEIGHT - drop.settled; row--) { if (row >= 0) { this.currentGrid[row][x] = this.finalGrid[row][x]; } }
// Check if this column is done if (drop.settled >= DISPLAY_HEIGHT) continue;
allSettled = false;
// Find the target row for this column (lowest non-space pixel) let targetRow = -1; for (let row = DISPLAY_HEIGHT - 1 - drop.settled; row >= 0; row--) { if (this.finalGrid[row][x] !== " ") { targetRow = row; break; } }
// Move drop down drop.y++;
// Draw falling drop if (drop.y >= 0 && drop.y < DISPLAY_HEIGHT) { if (targetRow >= 0 && drop.y >= targetRow) { // Settle drop.settled = DISPLAY_HEIGHT - targetRow; drop.y = -Math.floor(Math.random() * 5) - 1; } else { // Still falling this.currentGrid[drop.y][x] = "▓"; } } }
return allSettled; }
private tickFade(): boolean { const state = this.effectState as { positions: [number, number][]; idx: number }; const pixelsPerFrame = 15;
for (let i = 0; i < pixelsPerFrame; i++) { if (state.idx >= state.positions.length) return true; const [row, x] = state.positions[state.idx]; this.currentGrid[row][x] = this.finalGrid[row][x]; state.idx++; } return false; }
private tickCrt(): boolean { const state = this.effectState as { expansion: number }; const midRow = Math.floor(DISPLAY_HEIGHT / 2);
this.currentGrid = this.createEmptyGrid();
// Draw from middle expanding outward const top = midRow - state.expansion; const bottom = midRow + state.expansion;
for (let row = Math.max(0, top); row <= Math.min(DISPLAY_HEIGHT - 1, bottom); row++) { for (let x = 0; x < WIDTH; x++) { this.currentGrid[row][x] = this.finalGrid[row][x]; } }
state.expansion++; return state.expansion > DISPLAY_HEIGHT; }
private tickGlitch(): boolean { const state = this.effectState as { phase: number; glitchFrames: number };
if (state.phase < state.glitchFrames) { // Glitch phase: show corrupted version this.currentGrid = this.finalGrid.map((row) => { const offset = Math.floor(Math.random() * 7) - 3; const glitchRow = [...row];
// Random horizontal offset if (Math.random() < 0.3) { const shifted = glitchRow.slice(offset).concat(glitchRow.slice(0, offset)); return shifted.slice(0, WIDTH); }
// Random vertical swap if (Math.random() < 0.2) { const swapRow = Math.floor(Math.random() * DISPLAY_HEIGHT); return [...this.finalGrid[swapRow]]; }
return glitchRow; }); state.phase++; return false; }
// Final frame: show clean image this.currentGrid = this.finalGrid.map((row) => [...row]); return true; }
private tickDissolve(): boolean { const state = this.effectState as { positions: [number, number][]; idx: number }; const pixelsPerFrame = 20;
for (let i = 0; i < pixelsPerFrame; i++) { if (state.idx >= state.positions.length) return true; const [row, x] = state.positions[state.idx]; this.currentGrid[row][x] = this.finalGrid[row][x]; state.idx++; } return false; }
private updateDisplay(): void { this.gridVersion++; }
dispose(): void { this.stopAnimation(); }}