Feat: Chat-Verbesserungen — echte Umlaute, TTS-Filter, Lautstärke-Regler, Sprechblasen

- Umlaute: System-Prompt erzwingt echte ä/ö/ü/ß (Hermes spiegelte bei Technik den ASCII-Hint).
- TTS-Filter: cleanForTTS entfernt URLs/Markdown/Code/Emojis vor der Sprachausgabe (Anzeige bleibt).
- Lautstärke: GainNode in AudioQueue + Regler (Default 60%, live); Probe-hören respektiert ihn.
  (Bugfix: Number(null)===0 → wäre sonst stumm gewesen.)
- Chat: Sprechblasen (User rechts / Hermes links), Zeilenumbrüche, Auto-Scroll, Tipp-Indikator.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-06-28 02:44:20 +02:00
parent 49c9e27502
commit d07fe1de66
9 changed files with 396 additions and 306 deletions
@@ -17,8 +17,20 @@ export function VoiceControls() {
const [engine, setEngine] = useState(localStorage.getItem("mc_voice_engine") || "elevenlabs")
const [voice, setVoice] = useState(localStorage.getItem("mc_voice_voice") || "")
const [previewing, setPreviewing] = useState(false)
const [volume, setVolume] = useState(() => {
const raw = localStorage.getItem("mc_voice_volume")
if (raw === null || raw === "") return 0.6 // Number(null)===0 → sonst aus Versehen stumm
const v = Number(raw)
return Number.isNaN(v) ? 0.6 : v
})
const previewAudio = useRef<HTMLAudioElement | null>(null)
const onVolume = (v: number) => {
setVolume(v)
localStorage.setItem("mc_voice_volume", String(v))
window.dispatchEvent(new CustomEvent("mc-voice-volume", { detail: v }))
}
const saveVoice = (eng: string, v: string) => {
setEngine(eng); setVoice(v)
localStorage.setItem("mc_voice_engine", eng)
@@ -55,6 +67,7 @@ export function VoiceControls() {
const url = URL.createObjectURL(await r.blob())
previewAudio.current?.pause()
const a = new Audio(url)
a.volume = Math.min(1, volume) // Regler auch für die Probe respektieren
previewAudio.current = a
a.onended = () => URL.revokeObjectURL(url)
await a.play()
@@ -114,6 +127,17 @@ export function VoiceControls() {
{previewing ? <Loader2 className="h-3.5 w-3.5 animate-spin" /> : <Volume2 className="h-3.5 w-3.5" />}
{previewing ? "Spielt …" : "Probe hören"}
</button>
{/* Lautstärke */}
<div className="flex items-center gap-2 pt-0.5">
<Volume2 className="h-3.5 w-3.5 shrink-0 text-muted-foreground" />
<input
type="range" min={0} max={1.2} step={0.05} value={volume}
onChange={(e) => onVolume(Number(e.target.value))}
className="h-1 flex-1 cursor-pointer accent-primary"
title="Lautstärke"
/>
<span className="w-9 text-right text-[11px] tabular-nums text-muted-foreground">{Math.round(volume * 100)}%</span>
</div>
{elKeyMissing && (
<p className="text-[11px] text-amber-300">
Keine ElevenLabs-Stimmen Key in <code>~/.hermes/.env</code> fehlt, oder keine eigene Stimme angelegt.