diff --git a/ui-tui/src/bootBanner.ts b/ui-tui/src/bootBanner.ts new file mode 100644 index 000000000..2aac254a2 --- /dev/null +++ b/ui-tui/src/bootBanner.ts @@ -0,0 +1,36 @@ +// Prints the Hermes banner as raw ANSI to stdout before React/Ink load. +// Gives the user instant visual feedback during the ~170ms dynamic-import +// window; `` wipes the normal-screen buffer when Ink +// mounts, so there is no double-banner. +// +// Palette is hardcoded to match DEFAULT_THEME — drifting the theme's +// banner colors here is fine, Ink's real render takes over in ~200ms. + +const GOLD = '\x1b[38;2;255;215;0m' +const AMBER = '\x1b[38;2;255;191;0m' +const BRONZE = '\x1b[38;2;205;127;50m' +const DIM = '\x1b[38;2;184;134;11m' +const RESET = '\x1b[0m' + +const LOGO = [ + '██╗ ██╗███████╗██████╗ ███╗ ███╗███████╗███████╗ █████╗ ██████╗ ███████╗███╗ ██╗████████╗', + '██║ ██║██╔════╝██╔══██╗████╗ ████║██╔════╝██╔════╝ ██╔══██╗██╔════╝ ██╔════╝████╗ ██║╚══██╔══╝', + '███████║█████╗ ██████╔╝██╔████╔██║█████╗ ███████╗█████╗███████║██║ ███╗█████╗ ██╔██╗ ██║ ██║ ', + '██╔══██║██╔══╝ ██╔══██╗██║╚██╔╝██║██╔══╝ ╚════██║╚════╝██╔══██║██║ ██║██╔══╝ ██║╚██╗██║ ██║ ', + '██║ ██║███████╗██║ ██║██║ ╚═╝ ██║███████╗███████║ ██║ ██║╚██████╔╝███████╗██║ ╚████║ ██║ ', + '╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═══╝ ╚═╝ ' +] + +const GRADIENT = [GOLD, GOLD, AMBER, AMBER, BRONZE, BRONZE] +const LOGO_WIDTH = 98 + +export function bootBanner(cols: number = process.stdout.columns || 80): string { + const lines = + cols >= LOGO_WIDTH + ? LOGO.map((text, i) => `${GRADIENT[i]}${text}${RESET}`) + : [`\x1b[1m${GOLD}⚕ NOUS HERMES${RESET}`] + + return ( + '\n' + lines.join('\n') + '\n' + `${DIM}⚕ Nous Research · Messenger of the Digital Gods${RESET}\n\n` + ) +} diff --git a/ui-tui/src/entry.tsx b/ui-tui/src/entry.tsx index abbcf7a4c..3c21de93e 100644 --- a/ui-tui/src/entry.tsx +++ b/ui-tui/src/entry.tsx @@ -1,8 +1,10 @@ #!/usr/bin/env node -// Import order matters for cold start: `GatewayClient` has only node-builtin -// deps (<20ms), so spawning the python gateway before loading @hermes/ink -// + App (~200ms combined) gives python ~200ms of free parallel time to run -// its own module imports instead of starting those after node is done. +// Import order matters for cold start: `GatewayClient` + `bootBanner` have +// only node-builtin deps (<20ms), so we can paint the banner and spawn the +// python gateway before loading @hermes/ink + App (~170ms combined). +// `` wipes the normal-screen buffer on Ink mount, so the +// boot banner is replaced seamlessly by the real React render. +import { bootBanner } from './bootBanner.js' import { GatewayClient } from './gatewayClient.js' if (!process.stdin.isTTY) { @@ -10,6 +12,8 @@ if (!process.stdin.isTTY) { process.exit(0) } +process.stdout.write(bootBanner()) + const gw = new GatewayClient() gw.start()