const btn = document.getElementById('record-btn'); const statusText = document.getElementById('status-text'); const headerStatus = document.getElementById('header-status'); const preview = document.getElementById('preview'); const instructionsEl = document.getElementById('instructions'); const transcriptList = document.getElementById('transcript-list'); const userChip = document.getElementById('user-chip'); const logoutBtn = document.getElementById('logout-btn'); const STATUS_LABELS = { idle: 'Bereit', recording: 'Aufnahme läuft\u2026', processing: 'Wird verarbeitet\u2026', error: 'Fehler', }; // Auth token is stored in sessionStorage so it's gone when the tab closes. // On first load, if no token is present the server will redirect to /login. const token = sessionStorage.getItem('token'); function authHeaders() { return token ? { 'Authorization': `Bearer ${token}` } : {}; } function apiFetch(url, options = {}) { return fetch(url, { ...options, headers: { 'Content-Type': 'application/json', ...authHeaders(), ...(options.headers || {}) }, }); } logoutBtn.addEventListener('click', () => { apiFetch('/logout', { method: 'POST' }).finally(() => { sessionStorage.removeItem('token'); location.href = '/login'; }); }); instructionsEl.addEventListener('input', async () => { await apiFetch('/instructions', { method: 'POST', body: JSON.stringify({ instructions: instructionsEl.value }), }); }); function setStatus(status) { btn.className = status; headerStatus.className = `status-badge ${status}`; const label = STATUS_LABELS[status] || status; statusText.textContent = label; headerStatus.textContent = label; btn.disabled = status === 'processing'; } btn.addEventListener('click', () => apiFetch('/toggle', { method: 'POST' })); function connectWs() { const proto = location.protocol === 'https:' ? 'wss:' : 'ws:'; const ws = new WebSocket(`${proto}//${location.host}/ws?token=${encodeURIComponent(token || '')}`); ws.onmessage = (e) => { const msg = JSON.parse(e.data); if (msg.event === 'processing') setStatus('processing'); if (msg.event === 'transcribed' || msg.event === 'refined') { const text = msg.raw || msg.markdown || ''; preview.textContent = text; preview.classList.add('has-content'); } if (msg.event === 'saved') { setStatus('idle'); loadTranscripts(); } if (msg.event === 'error') { setStatus('idle'); preview.textContent = `Fehler: ${msg.message}`; } }; ws.onclose = () => setTimeout(connectWs, 2000); } async function loadTranscripts() { const r = await apiFetch('/transcripts'); if (!r.ok) return; const items = await r.json(); transcriptList.replaceChildren( ...items.map((t) => { const div = document.createElement('div'); div.className = 'transcript-item'; const name = document.createElement('span'); name.textContent = t.filename.replace('.md', ''); const meta = document.createElement('span'); meta.className = 'meta'; meta.textContent = `${Math.round(t.size / 1024 * 10) / 10} KB`; div.append(name, meta); div.addEventListener('click', () => { apiFetch('/open', { method: 'POST', body: JSON.stringify({ path: t.path }), }); }); return div; }) ); } (async () => { const r = await apiFetch('/status'); if (r.status === 401) { location.href = '/login'; return; } const data = await r.json(); setStatus(data.status); if (data.username) { userChip.textContent = data.username; } connectWs(); loadTranscripts(); })();