import type { App } from '$apps' import { TOES_URL } from '$apps' const RENDER_DEBOUNCE = 50 let _apps: App[] = [] let _enabled = process.stdout.isTTY ?? false let _lastRender = 0 let _renderTimer: Timer | undefined let _showEmoji = false export const setShowEmoji = (show: boolean) => { _showEmoji = show scheduleRender() } export function appLog(app: App, ...msg: string[]) { if (!_enabled) { console.log('🐾', `[${app.name}]`, msg.join(' ')) } } export function hostLog(...msg: string[]) { if (!_enabled) { console.log('🐾', msg.join(' ')) } } export function setApps(apps: App[]) { _apps = apps scheduleRender() } const formatStatus = (app: App): string => { switch (app.state) { case 'running': return '\x1b[32m●\x1b[0m' case 'starting': return '\x1b[33m◐\x1b[0m' case 'stopping': return '\x1b[33m◑\x1b[0m' case 'stopped': return '\x1b[90m○\x1b[0m' case 'invalid': return '\x1b[31m✕\x1b[0m' default: return '\x1b[90m?\x1b[0m' } } const formatAppLine = (app: App, indent: boolean): string => { const status = formatStatus(app) const port = app.port ? `\x1b[90m:${app.port}\x1b[0m` : '' const prefix = indent ? ' ' : '' if (_showEmoji) { const icon = app.icon ?? '📦' return `${prefix}${icon} ${app.name} ${status}${port}` } else { return `${prefix}${status} ${app.name}${port}` } } const scheduleRender = () => { if (_renderTimer) return const elapsed = Date.now() - _lastRender const delay = Math.max(0, RENDER_DEBOUNCE - elapsed) _renderTimer = setTimeout(() => { _renderTimer = undefined render() }, delay) } function render() { if (!_enabled) return _lastRender = Date.now() const lines: string[] = [] // Clear screen and move cursor to top lines.push('\x1b[2J\x1b[H') // Header lines.push(`\x1b[1m🐾 Toes\x1b[0m \x1b[90m${TOES_URL}\x1b[0m`) lines.push('') // Apps section const regularApps = _apps.filter(a => !a.tool) const tools = _apps.filter(a => a.tool) if (tools.length === 0) { // No tools, just list apps without header/indent for (const app of regularApps) { lines.push(formatAppLine(app, false)) } lines.push('') } else { if (regularApps.length > 0) { lines.push('\x1b[1mApps\x1b[0m') for (const app of regularApps) { lines.push(formatAppLine(app, true)) } lines.push('') } lines.push('\x1b[1mTools\x1b[0m') for (const app of tools) { lines.push(formatAppLine(app, true)) } lines.push('') } process.stdout.write(lines.join('\n')) }