baudy/src/server/terminal.ts
Corey Johnson 7cce1f6bbd Merge probablycorey/refactor-server-split
Resolve conflicts: accept server split, apply hint reorder
to terminal.ts, add JSX pragma to phone.tsx

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 13:34:28 -07:00

130 lines
4.0 KiB
TypeScript

import { loopbackTest, playAudio, startMicListener } from './audio'
import { handleGuess, getSecret } from './game'
import type { GuessResult } from './game'
const RESET = '\x1b[0m'
const BOLD = '\x1b[1m'
const DIM = '\x1b[2m'
const GREEN = '\x1b[32m'
const YELLOW = '\x1b[33m'
const BLUE = '\x1b[34m'
const CYAN = '\x1b[36m'
const RED = '\x1b[31m'
async function prompt(question: string): Promise<string> {
process.stdout.write(question)
for await (const line of console) {
return line.trim()
}
return ''
}
function getLocalHostname() {
const proc = Bun.spawnSync(['scutil', '--get', 'LocalHostName'])
if (proc.exitCode === 0) return proc.stdout.toString().trim() + '.local'
return 'localhost'
}
function getDeviceName(type: 'input' | 'output'): string {
const args = type === 'input' ? ['-c', '-t', 'input'] : ['-c']
const proc = Bun.spawnSync(['SwitchAudioSource', ...args])
if (proc.exitCode === 0) return proc.stdout.toString().trim()
return 'system default'
}
function logGuess(result: GuessResult) {
const { type, guess, guessCount } = result
if (type === 'higher') {
console.log(`${DIM}Guess #${guessCount}:${RESET} ${BOLD}${guess}${RESET}${YELLOW}📢 Higher!${RESET}`)
return
}
if (type === 'lower') {
console.log(`${DIM}Guess #${guessCount}:${RESET} ${BOLD}${guess}${RESET}${BLUE}📢 Lower!${RESET}`)
return
}
console.log(`${DIM}Guess #${guessCount}:${RESET} ${BOLD}${guess}${RESET}${GREEN}🎉 CORRECT!${RESET}`)
console.log()
console.log(`${CYAN}New round! Secret number: ${BOLD}${getSecret()}${RESET}`)
console.log(`${'─'.repeat(40)}`)
console.log()
}
async function onMessage(text: string) {
await Bun.sleep(500)
if (text === 'HELLO') {
console.log(`${GREEN}${BOLD}Player connected via audio!${RESET}`)
await playAudio('HEY BUDDY')
return
}
const result = await handleGuess(text)
if (result) logGuess(result)
}
export async function startup(port: number) {
const soxCheck = Bun.spawnSync(['which', 'sox'])
if (soxCheck.exitCode !== 0) {
console.error(`\n ${BOLD}sox is not installed.${RESET} Run: brew install sox\n`)
process.exit(1)
}
console.clear()
console.log()
console.log(`${BOLD}Corey's Screechy Audio Demo${RESET}`)
console.log()
const speaker = getDeviceName('output')
const mic = getDeviceName('input')
console.log(`${BOLD}Speaker:${RESET} ${GREEN}${speaker}${RESET}`)
console.log(`${BOLD}Microphone:${RESET} ${GREEN}${mic}${RESET}`)
console.log()
console.log(`${YELLOW}${BOLD}🔊 Turn your volume up!${RESET}`)
console.log(`${DIM}Testing audio: playing a chirp and listening for it...${RESET}`)
console.log()
const loopbackOk = await loopbackTest()
if (loopbackOk) {
console.log(`${GREEN}${BOLD}✓ Audio working!${RESET} ${DIM}Speaker → mic pipeline verified${RESET}`)
} else {
console.log(`${RED}${BOLD}✗ Couldn't hear the test chirp.${RESET}`)
console.log()
console.log(`${YELLOW}Try:${RESET}`)
console.log(` • Disconnect headphones — sound needs to travel through the air`)
console.log(` • Check System Settings > Sound (output: "${speaker}", input: "${mic}")`)
console.log(` • Turn your volume up`)
console.log()
await prompt(` Press Enter to continue anyway... `)
}
console.clear()
console.log()
console.log(`${BOLD}Corey's Screechy Audio Demo${RESET}`)
console.log(`${DIM}Speaker: ${speaker} · Mic: ${mic}${RESET}`)
console.log()
const hostname = getLocalHostname()
const url = `https://${hostname}:${port}`
console.log(`${GREEN}${BOLD}Scan QR code on your phone to play!${RESET}`)
console.log()
try {
const QRCode = await import('qrcode')
const qr = await QRCode.toString(url, { type: 'terminal', small: true })
console.log(qr)
} catch {
console.log(`${BOLD}${CYAN}${url}${RESET}`)
console.log()
}
console.log(`${BOLD}Secret number: ${GREEN}${getSecret()}${RESET}`)
console.log(`${'─'.repeat(40)}`)
console.log(`${DIM}🎤 Listening for guesses...${RESET}`)
console.log()
startMicListener(onMessage)
}