@@ -41,7 +45,43 @@ function render() {
app.querySelectorAll('.tab').forEach(b => b.onclick = () => { state.tab = b.dataset.tab; render(); });
app.querySelector('#themetoggle').onclick = () =>
applyTheme(document.documentElement.dataset.theme === 'dark' ? 'light' : 'dark');
- ({ record: viewRecord, bluetooth: viewBluetooth, recordings: viewRecordings, voices: viewVoices, glossary: viewGlossary })[state.tab]();
+ app.querySelector('#recbtn').onclick = toggleRecord;
+ if (state.recording) startTimerUI();
+ ({ bluetooth: viewBluetooth, recordings: viewRecordings, voices: viewVoices, glossary: viewGlossary })[state.tab]();
+}
+
+// Header record button: start = defaults only (default source = CallRecMix,
+// codec auto, speaker count auto — the old options tab is gone on purpose).
+// A live placeholder card appears at the top of the recordings list instantly.
+async function toggleRecord() {
+ const btn = document.querySelector('#recbtn');
+ if (!state.recording) {
+ try {
+ await api.post('/api/record/start', {});
+ state.recording = true; state.recStart = Date.now();
+ render();
+ if (state.tab === 'recordings') { state.lastListSig = null; refreshRecList(); }
+ } catch (err) { toast(err.message, true); }
+ } else {
+ if (btn) { btn.disabled = true; }
+ clearInterval(state.timer); state.timer = null;
+ try {
+ const r = await api.post('/api/record/stop', {});
+ state.recording = false;
+ toast('Aufnahme gespeichert – wird transkribiert…');
+ state.tab = 'recordings'; render();
+ state.lastListSig = null; refreshRecList();
+ openRecording(r.id);
+ } catch (err) { state.recording = false; toast('Stop fehlgeschlagen: ' + err.message, true); render(); }
+ }
+}
+// Tick every .rectime element (header + list placeholder) from one interval.
+function startTimerUI() {
+ clearInterval(state.timer);
+ state.timer = setInterval(() => {
+ const t = fmt(Date.now() - state.recStart);
+ document.querySelectorAll('[data-rectime]').forEach(el => el.textContent = t);
+ }, 500);
}
// ---------- Bluetooth ----------
@@ -94,63 +134,6 @@ async function viewBluetooth() {
load().catch(e => listEl.innerHTML = '
' + e.message + '
');
}
-// ---------- Record ----------
-async function viewRecord() {
- const v = document.querySelector('#view');
- v.innerHTML = `
-
-
-
-
-
-
-
-
-
-
-
Wenn du die Anzahl der Sprecher kennst, hier eintragen — verbessert die Trennung. Sonst leer lassen (Auto). Titel, Zusammenfassung, ToDos & Sprecher-Erkennung laufen automatisch per KI.
'; state.lastListSig = 'empty'; scheduleNext(); return; }
// Signature of everything the list RENDERS except the live ⏱ seconds (those
// tick via ensureAnaTicker without a rebuild). If nothing meaningful changed,
// DON'T touch the DOM at all — no flicker, no scroll jump, selection kept.
@@ -352,7 +335,15 @@ async function refreshRecList() {
// Fresh tab entry: only the newest day (first group, recs are newest-first)
// stays open; manual toggles afterwards are respected until the next entry.
if (state.resetCollapse) { state.collapsedDates = new Set(groups.slice(1)); state.resetCollapse = false; }
- listEl.innerHTML = groups.map(day => {
+ // Live placeholder while a recording runs: its own card at the very top.
+ const liveHtml = state.recording ? `
+