feat(web): Stimmen-Umschalter (piper/chatterbox/cloud) in der UI
Dropdown im Menue setzt tts_provider als Request-Override fuer Text- und Sprach-Turns -> live umschaltbar zwischen schnellem piper, hochwertigem chatterbox und Cloud. Asset-Version v5. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
5f01607b79
commit
b7c59d8c85
3 changed files with 20 additions and 4 deletions
|
|
@ -7,6 +7,13 @@ const statusEl = $("#status");
|
||||||
const promptEl = $("#prompt");
|
const promptEl = $("#prompt");
|
||||||
const formEl = $("#prompt-form");
|
const formEl = $("#prompt-form");
|
||||||
const micBtn = $("#mic");
|
const micBtn = $("#mic");
|
||||||
|
const ttsSel = $("#tts");
|
||||||
|
|
||||||
|
// Gewählter TTS-Provider (leer = Default/piper) als Request-Override.
|
||||||
|
function overrides() {
|
||||||
|
const t = ttsSel && ttsSel.value;
|
||||||
|
return t ? { tts_provider: t } : {};
|
||||||
|
}
|
||||||
|
|
||||||
let busy = false;
|
let busy = false;
|
||||||
// Session-ID pro Nutzer (sonst "gehoert einem anderen Nutzer"-Konflikt). Wird aus
|
// Session-ID pro Nutzer (sonst "gehoert einem anderen Nutzer"-Konflikt). Wird aus
|
||||||
|
|
@ -169,7 +176,7 @@ async function sendText(text) {
|
||||||
addMessage("user", text);
|
addMessage("user", text);
|
||||||
statusEl.textContent = "denkt …";
|
statusEl.textContent = "denkt …";
|
||||||
await runTurn("/ws/chat", (ws) => {
|
await runTurn("/ws/chat", (ws) => {
|
||||||
ws.send(JSON.stringify({ text, stream: true, audio_stream: true }));
|
ws.send(JSON.stringify({ text, stream: true, audio_stream: true, ...overrides() }));
|
||||||
});
|
});
|
||||||
busy = false;
|
busy = false;
|
||||||
}
|
}
|
||||||
|
|
@ -219,7 +226,7 @@ async function sendVoice(bytes) {
|
||||||
await ensureSession();
|
await ensureSession();
|
||||||
statusEl.textContent = "verarbeite Sprache …";
|
statusEl.textContent = "verarbeite Sprache …";
|
||||||
await runTurn("/ws/voice", (ws) => {
|
await runTurn("/ws/voice", (ws) => {
|
||||||
ws.send(JSON.stringify({ type: "start", format: "webm", stream: true, audio_stream: true }));
|
ws.send(JSON.stringify({ type: "start", format: "webm", stream: true, audio_stream: true, ...overrides() }));
|
||||||
ws.send(bytes);
|
ws.send(bytes);
|
||||||
ws.send(JSON.stringify({ type: "end" }));
|
ws.send(JSON.stringify({ type: "end" }));
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,20 @@
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<title>Voice Assistant</title>
|
<title>Voice Assistant</title>
|
||||||
<link rel="stylesheet" href="/style.css?v=4" />
|
<link rel="stylesheet" href="/style.css?v=5" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="layout">
|
<div id="layout">
|
||||||
<aside id="menu">
|
<aside id="menu">
|
||||||
<h2>Voice Assistant</h2>
|
<h2>Voice Assistant</h2>
|
||||||
<div id="identity" class="muted">lade …</div>
|
<div id="identity" class="muted">lade …</div>
|
||||||
|
<label id="tts-row">Stimme:
|
||||||
|
<select id="tts">
|
||||||
|
<option value="">Schnell (piper)</option>
|
||||||
|
<option value="chatterbox">Hohe Qualität (chatterbox)</option>
|
||||||
|
<option value="openrouter">Cloud (OpenRouter)</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
<div id="admin" class="hidden">
|
<div id="admin" class="hidden">
|
||||||
<h3>Admin</h3>
|
<h3>Admin</h3>
|
||||||
<button id="reload-users">Nutzer laden</button>
|
<button id="reload-users">Nutzer laden</button>
|
||||||
|
|
@ -31,6 +38,6 @@
|
||||||
<div id="status" class="muted"></div>
|
<div id="status" class="muted"></div>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
<script src="/app.js?v=4"></script>
|
<script src="/app.js?v=5"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,8 @@ body {
|
||||||
#menu h3 { font-size: 0.9rem; margin: 0 0 0.5rem; text-transform: uppercase; letter-spacing: 0.05em; }
|
#menu h3 { font-size: 0.9rem; margin: 0 0 0.5rem; text-transform: uppercase; letter-spacing: 0.05em; }
|
||||||
#menu a { color: #9fd3ff; }
|
#menu a { color: #9fd3ff; }
|
||||||
#identity { font-size: 0.9rem; }
|
#identity { font-size: 0.9rem; }
|
||||||
|
#tts-row { font-size: 0.85rem; display: flex; gap: 0.4rem; align-items: center; }
|
||||||
|
#tts { flex: 1; padding: 0.3rem; border-radius: 0.4rem; border: 1px solid #52606d; }
|
||||||
.muted { color: #7b8794; font-size: 0.85rem; }
|
.muted { color: #7b8794; font-size: 0.85rem; }
|
||||||
.hidden { display: none; }
|
.hidden { display: none; }
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue