/** * KHAELOR * File: src/tui/splash.ts * Description: The opening moment — wordmark with an ember gradient sweep, ~600 ms, skippable, first-run only (TUI v2 §2). * * Author: Simon-Pierre Boucher * Contact: contact@spboucher.ai */ import { gradientSweepFrame, gradientText } from "./theme/gradient.js"; import type { Rgb } from "./theme/gradient.js"; import type { Theme } from "./theme.js"; /** The KHAELOR wordmark (block letters, no box-drawing dependence). */ export const WORDMARK: readonly string[] = [ "██╗ ██╗██╗ ██╗ █████╗ ███████╗██╗ ██████╗ ██████╗", "██║ ██╔╝██║ ██║██╔══██╗██╔════╝██║ ██╔═══██╗██╔══██╗", "█████╔╝ ███████║███████║█████╗ ██║ ██║ ██║██████╔╝", "██╔═██╗ ██╔══██║██╔══██║██╔══╝ ██║ ██║ ██║██╔══██╗", "██║ ██╗██║ ██║██║ ██║███████╗███████╗╚██████╔╝██║ ██║", "╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝", ]; export const WORDMARK_TAGLINE = "understand · design · implement"; const EMBER: Rgb = [255, 107, 53]; const EMBER_GLOW: Rgb = [255, 184, 107]; /** Total sweep frames (~600 ms at one frame per 60 ms tick, TUI v2 §2). */ export const SPLASH_FRAMES = 10; /** * One frame of the reveal: the ember gradient sweeps left → right, then the * final frame settles on the static ember→glow ramp. Pure — the caller owns * timing (16 ms budget rule) and skip-on-any-key. */ export function renderSplashFrame(frame: number, truecolor: boolean): string[] { const lines: string[] = [""]; for (const row of WORDMARK) { if (!truecolor) { lines.push(` ${row}`); } else if (frame >= SPLASH_FRAMES - 1) { lines.push(` ${gradientText(row, EMBER, EMBER_GLOW)}`); } else { const offset = Math.round((row.length / SPLASH_FRAMES) * (SPLASH_FRAMES - 1 - frame)); lines.push(` ${gradientSweepFrame(row, EMBER, EMBER_GLOW, offset)}`); } } lines.push(` ${truecolor ? gradientText(WORDMARK_TAGLINE, EMBER_GLOW, EMBER) : WORDMARK_TAGLINE}`); lines.push(""); return lines; } /** The compact returning-user line: `KHAELOR ▸ my-project · main` (TUI v2 §2). */ export function renderCompactBrand( theme: Theme, project: string, branch: string | null, ): string { const mark = theme.paintGradient("ember", "KHAELOR"); const context = branch !== null ? `${project} · ${branch}` : project; return ` ${mark} ${theme.paint("dim", "▸")} ${theme.paint("text", context)}`; }