const COLORS = ['#60a5fa','#a78bfa','#f472b6','#34d399','#fb923c','#38bdf8','#e879f9','#a3e635'];
const speakerColor = (id) => COLORS[(id || 0) % COLORS.length];
const speakerInitial = (name) => (name || '?')[0].toUpperCase();
const fmtTime = (iso) => new Date(iso).toLocaleTimeString([], {hour:'2-digit',minute:'2-digit',second:'2-digit'});
const fmtDate = (iso) => new Date(iso).toLocaleDateString([], {weekday:'long',month:'long',day:'numeric',year:'numeric'});
const today = () => new Date().toISOString().slice(0,10);
let _currentDate = today();
function showPanel(name, el) {
document.querySelectorAll('.panel').forEach(p => p.classList.remove('active'));
document.querySelectorAll('.nav-item').forEach(n => n.classList.remove('active'));
document.getElementById('panel-' + name).classList.add('active');
if (el) el.classList.add('active');
if (name === 'log') loadLog();
if (name === 'search') renderSearch();
if (name === 'speakers') loadSpeakers();
if (name === 'rag') renderRag();
if (name === 'rooms') loadRooms();
}
function escHtml(s) {
return String(s).replace(/&/g,'&').replace(//g,'>');
}
// --- Daily Log ---
async function loadLog(date) {
if (date) _currentDate = date;
const panel = document.getElementById('panel-log');
const utterances = await fetch(`/api/utterances?date=${_currentDate}`).then(r => r.json()).catch(() => []);
const nav = `
${fmtDate(_currentDate + 'T12:00:00')}
`;
if (!utterances.length) {
panel.innerHTML = nav + '
No utterances recorded for this day.
';
return;
}
const byHour = {};
for (const u of [...utterances].reverse()) {
const key = new Date(u.start_time).toLocaleTimeString([], {hour:'2-digit',minute:'2-digit'});
if (!byHour[key]) byHour[key] = [];
byHour[key].push(u);
}
let html = nav;
for (const [timeKey, utts] of Object.entries(byHour)) {
html += `
${timeKey}
`;
for (const u of utts) {
const color = speakerColor(u.speaker_id);
const name = u.speaker_name || 'Unknown';
const badge = u.match_status === 'unknown' ? 'unknown'
: u.match_status === 'ambiguous' ? 'ambiguous' : '';
const actions = !u.speaker_id ? `
`).join('');
}
// --- Speakers ---
async function loadSpeakers() {
const panel = document.getElementById('panel-speakers');
const [speakers, unreviewed] = await Promise.all([
fetch('/api/speakers').then(r => r.json()).catch(() => []),
fetch('/api/utterances?match_status=unreviewed').then(r => r.json()).catch(() => []),
]);
let html = '';
if (unreviewed.length) {
html += `
${unreviewed.length} utterances need speaker tags
`;
}
html += `
Speakers
`;
for (const s of speakers) {
const color = speakerColor(s.id);
html += `
${speakerInitial(s.name)}
${escHtml(s.name)}
${s.utterance_count} utterances
`;
}
html += `
+ Add speaker
`;
panel.innerHTML = html;
}
async function createSpeaker() {
const name = prompt('Speaker name:');
if (!name) return;
await fetch('/api/speakers', { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({name}) });
loadSpeakers();
}
// --- Ask AI ---
const _ragHistory = [];
function renderRag() {
const panel = document.getElementById('panel-rag');
panel.innerHTML = `
Ask AI
`;
renderRagMessages();
}
function renderRagMessages() {
const el = document.getElementById('ragMessages');
if (!el) return;
if (!_ragHistory.length) { el.innerHTML = '
`;
for (const r of rooms) {
const active = r.is_active;
const dotColor = active ? '#4ade80' : '#475569';
const statusText = active ? 'Streaming' : 'Disconnected';
const cls = active ? 'streaming' : '';
html += `