Files
ai-podcast/frontend/index.html
tcpsyn c70f83d04a Cost monitoring, PTT fix, Devon tuning, WEIRD pool expansion, YT thumbnails, LLM SEO, publish ep37
- Add real-time LLM/TTS cost tracking with live status bar display and post-show reports
- Fix PTT bug where Devon suggestion layout shift stopped recording via mouseleave
- Devon: facts-only during calls, full personality between calls
- Double WEIRD topic pool (109→203), bump weight to 14-25%
- Auto-generate YouTube thumbnails with bold hook text in publish pipeline
- LLM SEO: llms.txt, robots.txt for LLM crawlers, structured data, BreadcrumbList schemas
- Publish episode 37

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 05:33:27 -06:00

301 lines
15 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Luke at The Roost</title>
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
<div id="app">
<header>
<h1>Luke at The Roost</h1>
<div class="header-buttons">
<button id="on-air-btn" class="on-air-btn off">OFF AIR</button>
<button id="rec-btn" class="rec-btn" title="Record stems for post-production">REC</button>
<button id="new-session-btn" class="new-session-btn">New Session</button>
<button id="export-session-btn">Export</button>
<button id="settings-btn">Settings</button>
</div>
<div id="show-clock" class="show-clock">
<span class="clock-time" id="clock-time"></span>
<span id="show-timers" class="show-timers hidden">
<span class="clock-divider">|</span>
<span class="clock-label">On Air:</span>
<span class="clock-value" id="clock-runtime">0:00:00</span>
<span class="clock-divider">|</span>
<span class="clock-label">Est. Final:</span>
<span class="clock-value clock-estimate" id="clock-estimate">0:00</span>
<span class="clock-divider">|</span>
<span class="clock-label">Cost:</span>
<span class="clock-value clock-cost" id="clock-cost">$0.00</span>
</span>
</div>
</header>
<main>
<!-- Callers -->
<section class="callers-section">
<h2>Callers <span id="session-id" class="session-id"></span></h2>
<div id="callers" class="caller-grid"></div>
<!-- Active Call Indicator -->
<div id="active-call" class="active-call hidden">
<div id="real-caller-info" class="caller-info hidden">
<span class="caller-type real">LIVE</span>
<span id="real-caller-name"></span>
<span id="real-caller-channel" class="channel-badge"></span>
<span id="real-caller-duration" class="call-duration"></span>
<button id="hangup-real-btn" class="hangup-btn small">Hang Up</button>
</div>
<div id="ai-caller-info" class="caller-info hidden">
<span class="caller-type ai">AI</span>
<span id="ai-caller-name"></span>
<div class="ai-controls">
<div class="mode-toggle">
<button id="mode-manual" class="mode-btn active">Manual</button>
<button id="mode-auto" class="mode-btn">Auto</button>
</div>
<button id="ai-respond-btn" class="respond-btn">Let them respond</button>
</div>
<button id="hangup-ai-btn" class="hangup-btn small">Hang Up</button>
</div>
<label class="auto-followup-label">
<input type="checkbox" id="auto-followup"> Auto Follow-Up
</label>
</div>
<div id="call-status" class="call-status">No active call</div>
<div id="caller-info-panel" class="caller-info-panel hidden">
<div class="caller-info-row">
<span id="caller-shape-badge" class="info-badge shape"></span>
<span id="caller-energy-badge" class="info-badge energy"></span>
<span id="caller-emotion" class="info-badge emotion"></span>
</div>
<div id="caller-signature" class="caller-signature"></div>
<div id="caller-situation" class="caller-situation"></div>
<details id="caller-background-details" class="caller-background-full">
<summary>Full Background</summary>
<div id="caller-background"></div>
</details>
</div>
<div class="call-actions">
<button id="wrapup-btn" class="wrapup-btn" disabled>Wrap It Up <span class="shortcut-label">W</span></button>
<button id="hangup-btn" class="hangup-btn" disabled>Hang Up <span class="shortcut-label">H</span></button>
</div>
</section>
<!-- Call Queue -->
<section class="queue-section">
<h2>Incoming Calls <span class="section-subtitle">(208) 439-5853</span></h2>
<div id="call-queue" class="call-queue">
<div class="queue-empty">No callers waiting</div>
</div>
</section>
<!-- Voicemail -->
<section class="voicemail-section">
<h2>Voicemail <span id="voicemail-badge" class="voicemail-badge hidden">0</span></h2>
<div id="voicemail-list" class="voicemail-list">
<div class="queue-empty">No voicemails</div>
</div>
</section>
<!-- Listener Emails -->
<section class="voicemail-section">
<h2>Emails <span id="email-badge" class="voicemail-badge hidden">0</span></h2>
<div id="email-list" class="voicemail-list email-list">
<div class="queue-empty">No emails</div>
</div>
</section>
<!-- Chat -->
<section class="chat-section">
<div id="chat" class="chat-log"></div>
<div class="devon-bar">
<div class="devon-ask-row">
<input type="text" id="devon-input" placeholder="Ask Devon..." class="devon-input">
<button id="devon-ask-btn" class="devon-ask-btn">Ask <span class="shortcut-label">D</span></button>
<button id="devon-interject-btn" class="devon-interject-btn" title="Devon interjects on current conversation">Interject</button>
<label class="devon-monitor-label" title="Devon auto-monitors conversations">
<input type="checkbox" id="devon-monitor" checked> Monitor
</label>
</div>
<div id="devon-suggestion" class="devon-suggestion hidden">
<span class="devon-suggestion-text">Devon has something</span>
<button id="devon-play-btn" class="devon-play-btn">Play</button>
<button id="devon-dismiss-btn" class="devon-dismiss-btn">Dismiss</button>
</div>
</div>
<div class="talk-controls">
<button id="talk-btn" class="talk-btn">Hold to Talk</button>
<button id="type-btn" class="type-btn">Type</button>
</div>
<div id="status" class="status hidden"></div>
</section>
<!-- Music / Ads / Idents -->
<div class="media-row">
<section class="music-section">
<h2>Music</h2>
<select id="track-select"></select>
<div class="music-controls">
<button id="play-btn">Play <span class="shortcut-label">M</span></button>
<button id="stop-btn">Stop</button>
<input type="range" id="volume" min="0" max="100" value="30">
</div>
</section>
<section class="music-section">
<h2>Ads</h2>
<select id="ad-select"></select>
<div class="music-controls">
<button id="ad-play-btn">Play Ad</button>
<button id="ad-stop-btn">Stop</button>
</div>
</section>
<section class="music-section">
<h2>Idents</h2>
<select id="ident-select"></select>
<div class="music-controls">
<button id="ident-play-btn">Play Ident</button>
<button id="ident-stop-btn">Stop</button>
</div>
</section>
</div>
<!-- Sound Effects -->
<section class="sounds-section">
<h2>Sounds</h2>
<div id="soundboard" class="soundboard"></div>
</section>
<!-- Server Log -->
<section class="log-section">
<div class="log-header">
<h2>Server Log</h2>
<div class="server-controls">
<button id="log-toggle-btn" class="log-toggle-btn">Show ▼</button>
<button id="restart-server-btn" class="server-btn restart">Restart</button>
<button id="stop-server-btn" class="server-btn stop">Stop</button>
<label class="auto-scroll-label">
<input type="checkbox" id="auto-scroll" checked> Auto-scroll
</label>
</div>
</div>
<div class="log-body collapsed">
<div id="server-log" class="server-log"></div>
</div>
</section>
</main>
<!-- Settings Modal -->
<div id="settings-modal" class="modal hidden">
<div class="modal-content">
<h2>Settings</h2>
<!-- Audio Devices -->
<div class="settings-group">
<h3>Audio Routing</h3>
<div class="device-row">
<label>
Input Device
<select id="input-device"></select>
</label>
<label>
Ch
<input type="number" id="input-channel" value="1" min="1" max="16" class="channel-input">
</label>
</div>
<div class="device-row">
<label>
Output Device
<select id="output-device"></select>
</label>
</div>
<div class="channel-row">
<label>Caller Ch <input type="number" id="caller-channel" value="3" min="1" max="16" class="channel-input"></label>
<label>Live Ch <input type="number" id="live-caller-channel" value="9" min="1" max="16" class="channel-input"></label>
<label>Music Ch <input type="number" id="music-channel" value="5" min="1" max="16" class="channel-input"></label>
<label>SFX Ch <input type="number" id="sfx-channel" value="7" min="1" max="16" class="channel-input"></label>
<label>Ad Ch <input type="number" id="ad-channel" value="11" min="1" max="16" class="channel-input"></label>
<label>Ident Ch <input type="number" id="ident-channel" value="15" min="1" max="16" class="channel-input"></label>
</div>
</div>
<!-- LLM Settings -->
<div class="settings-group">
<h3>LLM Provider</h3>
<label>
Provider
<select id="provider">
<option value="openrouter">OpenRouter</option>
<option value="ollama">Ollama</option>
</select>
</label>
<div id="openrouter-settings">
<label>
Model
<select id="openrouter-model"></select>
</label>
</div>
<div id="ollama-settings" class="hidden">
<label>
Model
<select id="ollama-model"></select>
</label>
<label>
Host
<input type="text" id="ollama-host" value="http://localhost:11434">
</label>
<button type="button" id="refresh-ollama" class="refresh-btn">Refresh Models</button>
</div>
</div>
<!-- TTS Settings -->
<div class="settings-group">
<h3>TTS Provider</h3>
<label>
Provider
<select id="tts-provider">
<option value="inworld">Inworld (High quality, natural)</option>
<option value="f5tts">F5-TTS (Most natural local)</option>
<option value="elevenlabs">ElevenLabs (Best quality, paid)</option>
<option value="kokoro">Kokoro MLX (Fast, Apple Silicon)</option>
<option value="chattts">ChatTTS (Conversational)</option>
<option value="styletts2">StyleTTS2 (Voice cloning)</option>
<option value="vits">VITS (Fast local)</option>
<option value="bark">Bark (Expressive, supports [laughs])</option>
</select>
</label>
<label class="checkbox">
<input type="checkbox" id="phone-filter">
Phone filter on voices
</label>
</div>
<div class="modal-buttons">
<button id="save-settings">Save</button>
<button id="close-settings">Close</button>
</div>
</div>
</div>
<!-- Type Modal -->
<div id="type-modal" class="modal hidden">
<div class="modal-content">
<h2>Type Message</h2>
<textarea id="type-input" rows="3" placeholder="Type what you want to say..."></textarea>
<div class="modal-buttons">
<button id="send-type">Send</button>
<button id="close-type">Cancel</button>
</div>
</div>
</div>
</div>
<script src="/js/app.js?v=22"></script>
</body>
</html>