#!/usr/bin/env node import { Jimp } from 'jimp'; import { writeFileSync, readdirSync, existsSync } from 'fs'; import { deflateRawSync } from 'zlib'; import { join, dirname } from 'path'; import { fileURLToPath } from 'url'; const __dirname = dirname(fileURLToPath(import.meta.url)); const repoRoot = join(__dirname, '..'); const FRAMES_DIR = process.env.FRAMES_DIR || '/tmp/cmem-banner-frames'; const OUT = join(repoRoot, 'src/npx-cli/banner-frames.ts'); const COLS = 128; const VIDEO_ROWS = Math.round(COLS * (9 / 16) / 2); const ROWS = VIDEO_ROWS; const TOP_PAD = 0; const BOTTOM_PAD = 0; const RAMP = ' .·~+=*x%$@#'; const BLACK_FLOOR = 50; const WHITE_CEIL = 160; const HALO_MIN = 70; const HALO_MAX = 175; function rasterize(img, gridW, gridH) { const resized = img.clone().resize({ w: gridW, h: gridH }); const data = resized.bitmap.data; const density = new Float32Array(gridW * gridH); for (let cy = 0; cy < gridH; cy++) { for (let cx = 0; cx < gridW; cx++) { const idx = (cy * gridW + cx) * 4; const r = data[idx], g = data[idx + 1], b = data[idx + 2]; density[cy * gridW + cx] = 0.2126 * r + 0.7152 * g + 0.0722 * b; } } return density; } function densityToChar(d) { if (d <= BLACK_FLOOR) return ' '; const range = WHITE_CEIL - BLACK_FLOOR; const norm = Math.min(1, (d - BLACK_FLOOR) / range); const t = Math.pow(norm, 1.3); const idx = Math.min(RAMP.length - 1, Math.max(1, Math.round(t * (RAMP.length - 1)))); return RAMP[idx]; } function renderASCII(density, w, h) { const lines = []; for (let y = 0; y < h; y++) { let line = ''; let inSpan = false; for (let x = 0; x < w; x++) { const i = y * w + x; const d = density[i]; const ch = densityToChar(d); const wantSpan = d > HALO_MIN && d < HALO_MAX && ch !== ' '; if (wantSpan && !inSpan) { line += ''; inSpan = true; } if (!wantSpan && inSpan) { line += ''; inSpan = false; } line += ch; } if (inSpan) line += ''; lines.push(line); } return lines.join('\n'); } async function main() { if (!existsSync(FRAMES_DIR)) { throw new Error(`Frames directory not found: ${FRAMES_DIR}\n` + `Run: ffmpeg -y -i