Mac-Worker: - /api/summarize, /api/protocol, /api/preload akzeptieren optionales model-Feld → überschreibt den .env-Default pro Call, kein Restart nötig - /api/models listet die lokal verfügbaren Ollama-Modelle + Default LXC: - User.default_model + Job.model (Migration für SQLite) - /api/auth/me liefert default_model, PATCH speichert ihn sofort - /api/mac/models proxiet die Liste an die UI - Pipeline reicht job.model an Mac-Worker durch; leer = Worker-Default - create_job & reprocess akzeptieren model-Override - Snapshot-Meta nutzt job.model (genauer als /health-Snapshot) UI: - Header-Dropdown "Modell:" neben "Profil:" — sofort-speichert User-Default - Jeder Job zeigt sein Modell als Tag (lila); leer bedeutet Worker-Default - Reprocess öffnet einen Modal-Picker (statt simplem confirm), Auswahl aller verfügbaren Modelle inkl. "Worker-Default" Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
191 lines
7.4 KiB
HTML
191 lines
7.4 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="de">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
|
<meta name="theme-color" content="#0f1115" />
|
|
<title>Voice-Agent — Transkription</title>
|
|
<link rel="stylesheet" href="/static/style.css" />
|
|
<link rel="manifest" href="/static/manifest.webmanifest" />
|
|
<link rel="icon" href="/static/icon.svg" type="image/svg+xml" />
|
|
<link rel="apple-touch-icon" href="/static/icon-192.png" />
|
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
|
<meta name="apple-mobile-web-app-title" content="Voice-Agent" />
|
|
<meta name="mobile-web-app-capable" content="yes" />
|
|
</head>
|
|
<body>
|
|
<header>
|
|
<div class="header-row">
|
|
<div class="header-left">
|
|
<h1>Voice-Agent</h1>
|
|
<p class="sub desktop-only">Sitzungsaufnahmen automatisch transkribieren und protokollieren</p>
|
|
</div>
|
|
<div class="header-right">
|
|
<div id="mac-status" class="badge desktop-only">Mac-Backend: prüfe …</div>
|
|
<div id="user-bar" class="user-bar hidden">
|
|
<span class="muted desktop-only">Angemeldet als</span>
|
|
<strong id="user-name"></strong>
|
|
<span id="user-admin-badge" class="badge admin-badge hidden">Admin</span>
|
|
<span id="header-profile-wrap" class="header-profile-wrap hidden">
|
|
<span class="muted desktop-only">·</span>
|
|
<span class="muted desktop-only">Profil:</span>
|
|
<select id="header-profile" class="header-profile" title="Standard-Profil — wird sofort gespeichert"></select>
|
|
</span>
|
|
<span id="header-model-wrap" class="header-profile-wrap hidden">
|
|
<span class="muted desktop-only">·</span>
|
|
<span class="muted desktop-only">Modell:</span>
|
|
<select id="header-model" class="header-profile" title="Standard-LLM — wird sofort gespeichert. Leer = Worker-Default."></select>
|
|
</span>
|
|
<button type="button" id="logout-btn" class="btn-link">Abmelden</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<!-- Auth-Overlay (Login oder Erst-Setup) -->
|
|
<div id="auth-overlay" class="overlay hidden">
|
|
<div class="auth-card">
|
|
<h2 id="auth-title">Anmelden</h2>
|
|
<p id="auth-info" class="muted hidden"></p>
|
|
<form id="auth-form">
|
|
<label>
|
|
Benutzername
|
|
<input type="text" id="auth-username" autocomplete="username" required />
|
|
</label>
|
|
<label>
|
|
Passwort
|
|
<input type="password" id="auth-password" autocomplete="current-password" required />
|
|
</label>
|
|
<button type="submit" id="auth-submit">Anmelden</button>
|
|
</form>
|
|
<p id="auth-msg" class="msg"></p>
|
|
</div>
|
|
</div>
|
|
|
|
<main id="app" class="hidden">
|
|
<section class="card upload-card">
|
|
<h2 class="desktop-only">Neue Aufnahme hochladen</h2>
|
|
<form id="upload-form">
|
|
<!-- HERO: Aufnahme-Button (auf Mobile groß und zentriert) -->
|
|
<button type="button" id="record-toggle" class="btn-record">
|
|
<span class="rec-dot"></span>
|
|
<span id="record-toggle-label">Aufnahme starten</span>
|
|
</button>
|
|
|
|
<!-- Aufnahme-Bühne -->
|
|
<div id="record-pane" class="record-pane hidden">
|
|
<div class="record-timer" id="record-timer">00:00</div>
|
|
<div class="record-status" id="record-status">Aufnahme läuft</div>
|
|
<div class="record-controls">
|
|
<button type="button" id="rec-pause" class="btn-record-ctrl">Pause</button>
|
|
<button type="button" id="rec-stop" class="btn-record-ctrl danger">Stoppen</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Aufnahme bereit -->
|
|
<div id="record-result" class="record-result hidden">
|
|
<span class="muted">Aufnahme bereit:</span>
|
|
<span id="record-filename"></span>
|
|
<button type="button" id="record-discard" class="btn-link btn-danger">Verwerfen</button>
|
|
</div>
|
|
|
|
<!-- Profil + Titel -->
|
|
<div class="upload-meta">
|
|
<label id="profile-label" class="hidden">
|
|
Verarbeitungs-Profil
|
|
<select id="profile-select"></select>
|
|
<small class="muted desktop-only">Standard: <span id="default-profile-name">—</span></small>
|
|
</label>
|
|
<label>
|
|
Titel <span class="muted">(optional)</span>
|
|
<input type="text" name="title" id="title" placeholder="z. B. Monatsbesprechung" />
|
|
</label>
|
|
</div>
|
|
|
|
<!-- Datei-Upload als Klappe (auf Mobile collapsed, auf Desktop offen) -->
|
|
<details class="audio-file-details" id="audio-file-details">
|
|
<summary>Lieber eine vorhandene Audio-Datei hochladen?</summary>
|
|
<label class="audio-file-label">
|
|
<span class="muted">MP3, WAV, M4A, MP4, OGG, FLAC, WebM</span>
|
|
<input type="file" name="audio" id="audio" accept="audio/*,.mp3,.wav,.m4a,.mp4,.ogg,.flac,.webm" />
|
|
</label>
|
|
</details>
|
|
|
|
<button type="submit" id="submit-btn">Hochladen & verarbeiten</button>
|
|
</form>
|
|
<div id="upload-progress" class="hidden">
|
|
<div class="bar"><div class="bar-fill"></div></div>
|
|
<small class="muted">Upload läuft …</small>
|
|
</div>
|
|
<p id="upload-msg" class="msg"></p>
|
|
</section>
|
|
|
|
<section class="card jobs-card">
|
|
<h2>Jobs</h2>
|
|
<!-- Desktop: Tabelle -->
|
|
<table id="jobs-table" class="desktop-only">
|
|
<thead>
|
|
<tr>
|
|
<th class="col-id">#</th>
|
|
<th class="col-title">Titel / Datei</th>
|
|
<th class="col-status">Status</th>
|
|
<th class="col-progress">Fortschritt</th>
|
|
<th class="col-updated">Aktualisiert</th>
|
|
<th class="col-dl">Downloads</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="jobs-body">
|
|
<tr><td colspan="6" class="muted">Noch keine Jobs.</td></tr>
|
|
</tbody>
|
|
</table>
|
|
<!-- Mobile: Card-Liste -->
|
|
<div id="jobs-cards" class="jobs-cards mobile-only">
|
|
<div class="muted">Noch keine Jobs.</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Nur sichtbar für Admins -->
|
|
<section id="admin-card" class="card hidden admin-collapsible">
|
|
<h2 class="admin-toggle" id="admin-toggle">Benutzer verwalten <span class="admin-toggle-icon">▾</span></h2>
|
|
<div class="admin-body">
|
|
<form id="user-form" class="user-form">
|
|
<label>
|
|
Benutzername
|
|
<input type="text" id="new-username" autocomplete="off" required pattern="[A-Za-z0-9._@\-]+" />
|
|
</label>
|
|
<label>
|
|
Passwort (min. 8 Zeichen)
|
|
<input type="password" id="new-password" autocomplete="new-password" required minlength="8" />
|
|
</label>
|
|
<label class="inline-check">
|
|
<input type="checkbox" id="new-is-admin" />
|
|
Admin-Rechte
|
|
</label>
|
|
<button type="submit">Anlegen</button>
|
|
</form>
|
|
<p id="user-msg" class="msg"></p>
|
|
<table id="users-table">
|
|
<thead>
|
|
<tr>
|
|
<th>#</th>
|
|
<th>Benutzer</th>
|
|
<th>Rolle</th>
|
|
<th>Angelegt</th>
|
|
<th>Aktionen</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="users-body"></tbody>
|
|
</table>
|
|
</div>
|
|
</section>
|
|
</main>
|
|
|
|
<footer>
|
|
<small class="muted">DSGVO-konform, alles lokal verarbeitet.</small>
|
|
</footer>
|
|
|
|
<script src="/static/app.js"></script>
|
|
</body>
|
|
</html>
|