feat(diarization-ui): redesign modern app-like UI and add PWA manifest/service worker/icon

This commit is contained in:
2026-03-21 14:30:15 +01:00
parent be416b7766
commit 3215dde312

89
app.py
View File

@@ -6,7 +6,7 @@ from typing import Optional
import requests
from fastapi import FastAPI, File, Form, HTTPException, UploadFile
from fastapi.responses import HTMLResponse, PlainTextResponse
from fastapi.responses import HTMLResponse, PlainTextResponse, Response
API_BASE = os.getenv("API_BASE", "http://gx10.aquantico.lan:8093").rstrip("/")
OLLAMA_BASE_URL = os.getenv("OLLAMA_BASE_URL", "http://gx10.aquantico.lan:11434").rstrip("/")
@@ -93,29 +93,46 @@ def init_db():
def layout(title: str, body: str) -> str:
return f"""
<!doctype html>
<html><head><meta charset='utf-8'><meta name='viewport' content='width=device-width, initial-scale=1'>
<html>
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width, initial-scale=1, viewport-fit=cover'>
<meta name='theme-color' content='#0f172a'>
<link rel='manifest' href='/manifest.webmanifest'>
<link rel='icon' href='/icon.svg' type='image/svg+xml'>
<title>{title}</title>
<style>
body{{font-family:Arial;margin:0;display:flex;min-height:100vh}}
nav{{width:240px;background:#111;color:#fff;padding:16px}}
nav a{{display:block;color:#fff;text-decoration:none;padding:8px 10px;border-radius:6px;margin:4px 0}}
nav a:hover{{background:#2a2a2a}}
main{{flex:1;padding:20px;max-width:1200px}}
.card{{border:1px solid #ddd;border-radius:8px;padding:12px;margin:10px 0}}
input,select,textarea,button{{padding:8px;font-size:14px}}
textarea{{width:100%;min-height:140px}}
pre{{white-space:pre-wrap;background:#111;color:#0f0;padding:10px;border-radius:8px}}
:root{{--bg:#0b1020;--bg2:#111827;--card:#0f172a;--txt:#e5e7eb;--muted:#94a3b8;--acc:#22d3ee;--acc2:#38bdf8;--ok:#34d399;--border:#1f2937}}
*{{box-sizing:border-box}}
body{{font-family:Inter,system-ui,Arial;margin:0;background:linear-gradient(180deg,var(--bg),#020617);color:var(--txt);display:flex;min-height:100vh}}
nav{{width:250px;background:rgba(15,23,42,.92);backdrop-filter:blur(8px);border-right:1px solid var(--border);padding:16px;position:sticky;top:0;height:100vh}}
.brand{{font-weight:700;letter-spacing:.3px;margin:0 0 12px 0}}
nav a{{display:flex;gap:10px;align-items:center;color:var(--txt);text-decoration:none;padding:10px 12px;border-radius:12px;margin:6px 0;border:1px solid transparent}}
nav a:hover{{background:#0b1222;border-color:#243244}}
main{{flex:1;padding:18px;max-width:1200px}}
.card{{background:rgba(15,23,42,.88);border:1px solid var(--border);border-radius:16px;padding:14px;margin:10px 0;box-shadow:0 8px 30px rgba(0,0,0,.25)}}
input,select,textarea,button{{padding:10px 12px;font-size:14px;border-radius:12px;border:1px solid #334155;background:#0b1222;color:var(--txt)}}
button{{background:linear-gradient(90deg,var(--acc),var(--acc2));color:#001018;border:none;font-weight:700}}
button:hover{{filter:brightness(1.05)}}
textarea{{width:100%;min-height:150px}}
pre{{white-space:pre-wrap;background:#020617;color:#86efac;padding:12px;border-radius:12px;border:1px solid #1e293b}}
.row{{display:flex;gap:8px;flex-wrap:wrap;align-items:center}}
small{{color:#666}}
</style></head>
small{{color:var(--muted)}}
.hint{{color:var(--muted);font-size:13px}}
@media (max-width:900px){{nav{{width:86px;padding:10px}} nav a span{{display:none}} .brand{{font-size:12px}} main{{padding:12px}}}}
</style>
<script>
if ('serviceWorker' in navigator) {{ window.addEventListener('load', () => navigator.serviceWorker.register('/sw.js').catch(()=>{{}})); }}
</script>
</head>
<body>
<nav>
<h3>Menü</h3>
<a href='/'>Upload</a>
<a href='/library'>Datenbank</a>
<a href='/prompts'>Prompt-Konfig</a>
<a href='/run'>Prompt ausführen</a>
<a href='/healthz'>Health</a>
<div class='brand'>🎙️ Audio Copilot</div>
<a href='/'><span>⤴️</span><span>Upload</span></a>
<a href='/library'><span>🗂️</span><span>Datenbank</span></a>
<a href='/prompts'><span>🧩</span><span>Prompts</span></a>
<a href='/run'><span>🤖</span><span>Prompt ausführen</span></a>
<a href='/healthz'><span>💚</span><span>Health</span></a>
</nav>
<main>{body}</main>
</body></html>
@@ -143,6 +160,40 @@ def startup():
init_db()
@app.get("/manifest.webmanifest")
def manifest():
return {
"name": "Audio Copilot",
"short_name": "Copilot",
"start_url": "/",
"display": "standalone",
"background_color": "#020617",
"theme_color": "#0f172a",
"icons": [
{"src": "/icon.svg", "sizes": "any", "type": "image/svg+xml", "purpose": "any maskable"}
],
}
@app.get("/icon.svg")
def icon_svg():
svg = """<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 128 128'><rect rx='24' width='128' height='128' fill='#0f172a'/><text x='64' y='82' font-size='72' text-anchor='middle'>🎙️</text></svg>"""
return Response(content=svg, media_type="image/svg+xml")
@app.get("/sw.js")
def sw_js():
js = """
self.addEventListener('install', (event) => { self.skipWaiting(); });
self.addEventListener('activate', (event) => { event.waitUntil(clients.claim()); });
self.addEventListener('fetch', (event) => {
if (event.request.method !== 'GET') return;
event.respondWith(fetch(event.request).catch(() => new Response('offline', {status: 503})));
});
"""
return Response(content=js, media_type="application/javascript")
@app.get("/healthz")
def healthz():
return {"ok": True, "api_base": API_BASE, "ollama_base_url": OLLAMA_BASE_URL, "ollama_model": OLLAMA_MODEL, "db_path": DB_PATH}