Commit Graph

209 Commits

Author SHA1 Message Date
a0d94b140e Fix clone UX: sample text, live monitor, quality, STT picker, OGG accept
1. Sample text: expose initCloneSampleText as a window function and call
   it from nav.js runSideEffects when the clone section is activated,
   ensuring the textarea is always populated even if the IIFE ran before
   the element existed.

2. Better sample texts: all 8 languages rewritten to ~38 words / ~15 s,
   first-person, phonetically rich, proper Unicode diacritics.

3. Live mic monitor: a level-meter (18-bar) + scrolling oscilloscope
   canvas (ring-buffer, 300 px, colour-coded) added to the microphone
   card.  "Check level" / "Stop monitor" buttons start/stop it
   independently; clicking Record starts it automatically.
   Uses raw mic constraints (no echo-cancel / AGC) for cleaner voice clone
   audio.  Mic gain slider and dB readout included.

4. Recording quality: MediaRecorder now requests audioBitsPerSecond:256000
   in both voice-clone.js and stt.js.

5. STT engine picker: Recognition engine <select> + Refresh button added
   above the Auto-transcribe button in Step 3.  refreshSttBackends() now
   syncs both stt-tts-stt-backend and clone-stt-backend.  The transcribe
   call passes the chosen backend to /api/transcribe.

6. File input: explicit extension list added to accept= for OGG/OPUS.

7. CSS: .mic-live-wave style added (dark/light theme variants).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 17:58:28 +02:00
4eb132e2b0 Restore recommended sentence + language switcher to Clone a Voice
Adds the 'Read aloud' sample-sentence box back to the microphone card
in the Clone a Voice tab. Language switcher covers EN/DE/IT/ES/FR/PT/NL/PL
with phonetically diverse sentences (same as the My Voices panel).
Uses the existing .sample-read-box / .sample-sentence CSS so it looks
identical to the equivalent panel in the voice library.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 22:30:37 +02:00
bf458283af Show spoken sentence in typing bubble; fix VAD noise sensitivity
Sentence text in typing bubble:
- Backend sends 'text' field with each audio SSE event (the sentence
  being synthesised)
- Frontend audio queue stores {url, text} pairs
- playNextAudio() writes the sentence text into the '...' typing bubble
  when LLM tokens haven't arrived yet (convCurrentSentenceBubble)
- convCurrentSentenceBubble cleared as soon as first LLM token arrives
  so normal streaming takes over seamlessly

VAD noise fixes:
- VAD_THRESHOLD: 0.01 → 0.02 (background noise no longer counts as speech)
- VAD_MIN_REC_MS: 400 → 800ms (8/10s wait before silence detection starts,
  gives user time to begin speaking without initial noise triggering send)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 20:50:44 +02:00
13c79768b4 Fix speech gate, hallucination filter, and latency
Speech gate (silence detection):
- vadHadSpeech flag: VAD auto-stop cancels without calling STT when no
  speech was detected (fixes "[STT] No speech detected → gibberish" loop)
- cancelNextBlob flag: onstop skips processBlob when VAD cancels silently
- vadLastVoiceMs: gates preview transcription on actual detected speech
  (prevents "reich" hallucination on initial silence chunks)

Hallucination filter:
- Client: HALLUCINATION_RE strips known Whisper phantoms from preview
- Server: _is_hallucination() in generate() treats "reich" / "danke" /
  "thank you" etc. as "No speech detected" → never reaches LLM

Latency:
- VAD_SILENCE_MS: 1500 → 1000 ms (sends 500 ms sooner per turn)
- VAD_MIN_REC_MS: 500 → 400 ms
- MediaRecorder timeslice: 2500 → 1500 ms (preview text updates faster)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 20:38:53 +02:00
c122089ef8 Parallel-load inactive sections alongside JS to cut startup time
Previously all 11 section HTMLs had to complete before JS loading began.
Now only the active section loads first (needed before JS runs so event
listeners find their DOM elements). The other 10 sections fetch in parallel
with utils + settings; an explicit await before the 9-module feature batch
guarantees all section HTML is injected before any feature module sets up
its DOM listeners. On a local server the HTML files finish well before
settings.js completes, so the await is typically instant.

Sequential steps cut from: sections(11∥) + utils + settings + features + ...
                        to: section(1) + max(utils+settings, sections(10∥)) + features + ...

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 20:21:35 +02:00
34870fed27 Pipeline TTS with LLM streaming for lower latency
Replace serial LLM-wait-TTS with overlapped execution:
- LLM streams via background thread → asyncio.Queue (non-blocking event loop)
- _sentence_split() detects sentence boundaries in the token stream
- asyncio.create_task fires TTS for each sentence immediately — TTS for
  sentence 1 runs while LLM is still generating sentences 2, 3, …
- Audio chunks stream to frontend in order as each task completes
- Time-to-first-audio drops from (LLM total + TTS total) to
  roughly (LLM time-to-first-sentence + TTS latency for one sentence)

Frontend audio queue:
- enqueueAudio() / playNextAudio() chain multi-chunk responses seamlessly
- clearAudio() stops playback and cancels queue on new turn or mic click
- scheduleAutoMic() waits for queue to drain before restarting mic
- Error paths clear the queue to avoid stale audio playing after failure

Also fix missing contextlib import (silent bug when audio temp files
needed cleanup in the STT path).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 19:06:07 +02:00
f4688ecc0c v1.2.0: VAD, live preview, hands-free mic, restore last section
Conversation playground:
- Live speech preview: MediaRecorder sends accumulated audio to
  /api/transcribe-bytes every 2.5 s; interim Whisper result shown in
  the text input field while recording. Web Speech API tried first as
  a faster path when available (HTTPS/localhost).
- VAD auto-stop: AudioContext AnalyserNode measures RMS every frame;
  auto-stops after 1.5 s silence with a visible countdown. Auto-stop
  toggle to revert to click-to-stop.
- Hands-free mode: mic auto-restarts after the agent finishes speaking
  via audio.ended event + generation-counter cancellation. Hands-free
  toggle (on by default) to disable.

Navigation:
- Persist active section and sub-page in localStorage; hard-reload
  returns to the same page instead of always jumping to My Voices.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 18:47:00 +02:00
b33829ded5 Add skeleton loading, self-host assets, fix LLM empty response
Perceived startup speed:
- index.html: animated shimmer skeleton (header + toolbar + 8 voice cards)
  visible immediately; fades out when loader.js finishes
- WaveSurfer (57KB) and MDI icon font (394KB woff2) now served from
  static/vendor/ — removes 3 render-blocking external requests from <head>
- Flag-icons CSS loaded async (rel=preload onload trick) — non-blocking

loader.js:
- Fade out skeleton + remove from DOM (300ms transition)
- Reveal page-sections after JS finishes loading

Conversation playground:
- Qwen3 thinking mode fix: fall back to delta.reasoning_content when
  delta.content is empty so think-only LLM turns produce visible output
- Better error message with /no-think hint when LLM returns empty

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 17:28:42 +02:00
a5efac5da5 Speed up startup: parallel JS loading + versioned asset caching
loader.js:
- Fetch /api/version first; append ?v=<version> to every script URL
- Load scripts with async=false (fetch in parallel, execute in DOM order)
- 4 sequential batches instead of 17 sequential awaits:
    utils → settings → [9 feature modules] → init → [4 post-init] → nav
- 17 round-trips → 6 round-trips; 9 and 4 files now download in parallel
- Section HTML keeps ?v=<timestamp> (no-store, always fresh)

server.py:
- Add static_cache_headers middleware:
    /static/js/*?v=...  → Cache-Control: max-age=31536000, immutable
    /static/sections/*  → Cache-Control: no-store
    /static/* (no v=)   → Cache-Control: max-age=3600

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 17:11:55 +02:00
7f35d32f3e Replace named Docker volume with local bind-mount folders
config/ and logs/ are now host directories visible on the filesystem.

- docker-compose.yml: ./config → /home/app/.config/tts-voice-creator:rw
                      ./logs   → /logs:rw
  Named volume tts-voice-creator-clone-and-design-2 removed.
- portainer-stack.yml: same change with absolute host paths.
- server.py: RotatingFileHandler writes INFO+ to /logs/app.log
  (maxBytes=5MB, backupCount=3). Falls back gracefully if /logs
  is not writable.
- .gitignore: track config/ and logs/ dirs via .gitkeep but exclude
  settings.json, *.log and backups from version control.

Benefits:
- Settings and logs are human-readable on the host at any time
- Survives docker-compose down -v (was lost with named volume)
- Easy backup: cp -r config/ logs/ to any destination
- Can edit settings.json directly if needed

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 15:31:46 +02:00
2ee3432bbc Persist engine container names and card URLs to server settings
Previously: container names and dynamic card URL overrides only
written to localStorage — lost when accessing from a different
browser or after clearing browser data.

Now:
- engine_container_names added as persisted settings key (dict)
- All container name inputs tagged data-cn-key for loadSettings() lookup
- loadSettings() restores container names + dynamic URLs from server
- settings.js exposes _saveEngineContainerNames() and
  _saveEngineLocalUrls() globally so engines.js / ai-backends.js
  can call them on every input event
- Static cards (ai-backends.js initStaticDockerManagement): reads
  saved name from engine_container_names first, localStorage fallback
- Dynamic Docker stack cards (engines.js): same priority for both
  URL and container name; both write to server on change

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 15:17:02 +02:00
686fd61865 Fix NameError: restore File import removed during 422 fix 2026-05-29 14:59:46 +02:00
1474bf1c7d Fix ImportError: _AUDIO_EXTS imported from wrong module in routes/stt.py
_AUDIO_EXTS lives in core.voice, not core.audio. The dead alias
'_VOICE_AUDIO_EXTS' was never used anywhere in the file.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 14:57:06 +02:00
28b1b73184 Fix portainer-stack.yml: add core/ and routes/ mounts, update for -2 container 2026-05-29 14:53:00 +02:00
00ffb52799 Show FastAPI error detail in conversation error bubbles 2026-05-29 14:51:03 +02:00
b3a408bfd9 Fix 422 on text-only conversation turns
UploadFile | None = File(None) with from __future__ import annotations
caused FastAPI to still treat audio as required when omitted.
Changed to Optional[UploadFile] = None (no File() wrapper) so the
field is genuinely optional for text-input turns.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 14:30:06 +02:00
74e5181b7e Fix conversation input bar hidden when mic is blocked
- Move mic-blocked warning into conv-chat-window (prepend) so it scrolls
  with the chat instead of pushing the input bar off-screen
- Remove min-height:400px from conv-chat-window; give conv-chat-panel a
  viewport-relative height so the input bar is always visible at the bottom
- loader.js: append ?v=<timestamp> to section fetches to bust browser cache
  (was serving stale s-conversation.html after updates)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 14:20:52 +02:00
06ed33ca67 Add release automation: version bump script and pre-commit changelog hook
scripts/release.py:
  - --patch/--minor/--major flag bumps VERSION file
  - Renames [Unreleased] → [x.y.z] — date in CHANGELOG.md
  - Inserts fresh [Unreleased] section + correct compare links
  - Commits + creates annotated git tag
  - --dry-run flag for preview without writes
  - Prints git push + GitHub Releases URL on completion

scripts/hooks/pre-commit:
  - Warns (exit 0, non-blocking) when source files are staged but
    CHANGELOG.md or VERSION are not staged
  - Already installed in .git/hooks/

scripts/install-hooks.sh:
  - One-liner to install hooks after a fresh clone

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 14:14:41 +02:00
db6302f99d Add version number (v1.1.0) and rewrite CHANGELOG to full Keep-a-Changelog spec
- Add VERSION file (1.1.0) at repo root
- core/constants.py: expose __version__ read from VERSION file
- routes/admin.py: GET /api/version endpoint returns {version}
- Settings → About: display "v1.1.0" next to app name via /api/version fetch
- CHANGELOG.md: full rewrite following Keep a Changelog + Semantic Versioning
  - [Unreleased] staging section at top
  - [1.1.0] 2026-05-29 — security, perf, refactor, UX changes from this session
  - [1.0.0] 2026-05-28 — all pre-session features documented
  - Compare links at bottom pointing to GitHub

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 13:53:31 +02:00
0a5b607fa5 Add text input to Conversation Playground (bypass mic / STT)
Frontend:
- Add pill-shaped text input + send button (→) to the left of the mic button
- Enter key or → click sends text directly without recording audio
- Input is disabled while a turn is processing; cleared on submit
- Welcome message updated to mention both input methods
- New CSS: .conv-input-bar, .conv-text-row, .conv-text-inp, .conv-send-btn,
  .conv-divider (visual separator between text and mic sections)

Backend:
- /api/conversation/turn: audio is now optional (UploadFile | None)
- New text form field — when provided, STT step is skipped and text is
  used as the transcript directly; SSE emits transcript event with stt_ms=null
- Raises 400 if neither audio nor text is supplied

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 13:12:27 +02:00
a9d4c45a3c Improve engine card UX: unified controls, blue Connect, auto-apply, fix mic warning
Engine cards (all TTS + STT, dynamic Docker stack + static):
- Container name field now always visible on every card (was hidden for
  absent/not-installed containers)
- Connect button moved out of URL row into a dedicated dc-controls-row;
  restyled as solid blue primary CTA
- Connect now toggles to Disconnect (green) when reachable, persists state
- On successful connect, URL is auto-applied to Settings so the backend
  appears in TTS/STT dropdowns immediately — no separate "Use as" click needed
- "Use as TTS/STT" button updated: larger, teal "apply" style, tooltip,
  gains .active highlight once applied, right-aligned as the trailing action
- Unified control order on every card: [Connect/Disconnect] [Stop|Start|Restart] [Use as →]
- initStaticDockerManagement rebuilt to use the same dc-controls-row layout
  as dynamic cards, moving .llm-local-ping from URL row at init time

Conversation section:
- Fix chrome://flags URL being white-on-grey inside the mic-blocked warning
  box (global code{background:var(--panel)} overrode the inherited white text)
- Inline-styled the <code> element with a dark translucent background
- Added a Copy button so users can copy the URL without selecting invisible text

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 12:59:59 +02:00
c7a1e35539 Security audit, modular refactor, and container-name field
Security fixes:
- Block /proc /sys /dev /run /boot in /api/browse-dirs (path traversal)
- Verify yt-dlp output stays inside TEMP_DIR before registration
- Remove Access-Control-Allow-Origin: * from /api/proxy-audio
- TTL-based temp file registry (default 2h) to prevent disk fill

Performance:
- Cache settings + routing rules in memory (mtime-checked); eliminates
  per-request disk reads on every TTS call

UI:
- Add container name (optional) field to Docker stack TTS/STT engine
  cards (Qwen3 Voice Clone, Voice Design, Custom Voice, Streaming,
  NVIDIA Magpie, Parakeet) — enables Stop/Start/Restart buttons on
  all engine cards, matching the existing Other Local TTS/STT cards

Refactor — backend:
- server.py: 5560 lines → 43-line entry point
- core/ package: constants, registry, validation, docker_client,
  config, routing, audio, voice, presets, tts_helpers
- routes/ package: admin, settings, library, stt, sources, docker,
  tts, conversation (FastAPI APIRouter modules)
- Dockerfile + docker-compose.yml updated to include core/ and routes/

Refactor — frontend:
- static/app.js: 8744 lines → 16 modules in static/js/
  utils, voice-inspector, voice-sources, integrations, routing,
  settings, voice-clone, voice-library, tts-preview, benchmark,
  stt, init, engines, ai-backends, generation, conversation
- static/loader.js updated to load modules sequentially

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 12:13:07 +02:00
77a54b848f Add inline STT test panel and fix whisperx pyannote auth error message
- STT section now has a Quick test panel: select backend, hit mic button,
  see transcript. Records via MediaRecorder, posts to /api/transcribe-bytes.
- _transcribe_audio detects the whisperx-gpu 'NoneType/to' error (caused by
  pyannote/speaker-diarization-3.1 requiring a HuggingFace token) and
  replaces it with an actionable message explaining how to fix it.
- _to_wav_16k added for STT audio conversion (Whisper/wav2vec2 expect 16kHz).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 09:27:59 +02:00
27a949f1ff Convert conversation audio to 16kHz for STT (fixes whisperx alignment crash)
STT models (Whisper, WhisperX VAD, wav2vec2 alignment) all expect 16kHz.
Sending 24kHz caused whisperx's VAD to miss speech segments, leaving
alignment with None inputs → 'NoneType has no attribute to' crash.

Added _to_wav_16k() and use it in conversation/turn and transcribe-bytes
endpoints. Health check probe also uses 16kHz silence for consistency.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 09:13:12 +02:00
d16be50cc9 Fix STT health check incorrectly marking Open WebUI as whisper.cpp
Port 8080 is Open WebUI — it passes /health + /v1/models checks but
returns 405 on POST /v1/audio/transcriptions. Updated probe logic to:
- treat 405 as 'endpoint missing, try next path'
- treat non-JSON 500 as broken, JSON-500 with detail as 'audio too short' (ok)
- use 500ms silence WAV instead of 1-frame (too tiny for alignment models)

Changed whisper.cpp default from :8080 to :8085 to avoid clash with
Open WebUI. Updated s-llms.html placeholder and code snippet accordingly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 09:03:21 +02:00
8f9060d025 Detect broken STT backends via real transcription probe in health check
_stt_backend_health now sends a minimal WAV to the transcription endpoint
after passing /health. A 500 response marks the backend unavailable,
catching containers that pass health checks but crash on model load (e.g.
CTranslate2 built without CUDA support).

Error messages from _transcribe_audio now include the backend URL and
replace generic 'Internal Server Error' with an actionable explanation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 08:31:28 +02:00
28361f2db6 Surface real STT error detail instead of raw HTTP noise
_transcribe_audio now extracts the response body on HTTP errors so the
actual cause (e.g. 'CTranslate2 not compiled with CUDA support') reaches
the user instead of '500 Server Error for url: ...'. Conversation panel
also strips the 'HTTP 500:' prefix to show only the meaningful part.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 08:23:05 +02:00
d2fe6790ee Fix Connect button scope error and add custom STT cards to conversation dropdown
cardType() was defined inside initLlmsSection() IIFE but called from
renderLocalContainers() which is outside that scope, causing a silent
ReferenceError that reset every Connect click to failure. Moved cardType
to module scope.

Custom STT cards (e.g. whisperx-gpu) are now included in /api/stt-backends
and appear in the Conversation STT dropdown. Added _normalize_service_url()
so 0.0.0.0 URLs in stored cards are rewritten to host.docker.internal for
server-side health checks. _transcribe_audio() tries /transcribe as fallback
for custom backends that don't expose /v1/audio/transcriptions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 02:35:38 +02:00
7bced9f126 Fix conversation TTS dropdown showing 'No TTS backend available'
Two bugs: (1) populateConvTtsBackends() referenced window._ttsBackends
which doesn't exist — fixed to use module-scope _ttsBackends directly.
(2) Broken hook that wrapped window.refreshTtsBackendAvailability (never
set on window) — replaced with _ttsRefreshHooks array dispatched at end
of refreshTtsBackendAvailability().

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 02:11:59 +02:00
214f2c61cf Make Connect button actually verify the API, not just TCP reachability
probe-url now accepts a type param (llm/stt/tts) and checks service-
specific endpoints: LLM → /v1/models with data[] key, STT → /health
then /v1/models, TTS → /health then /voices endpoints. Random websites
and wrong services are now rejected. Connect passes the card's section
type; success toast shows which endpoint responded.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 02:06:07 +02:00
838445ba07 Show all STT/TTS backends with ✓/✗ status in conversation dropdowns
Replaces the filter-to-available-only approach with full lists that
include unavailable backends (disabled, marked ✗) so users can see
what's broken. Also extends STT retry fallback to cover HTTP 500 from
wrong model names (fixes faster-whisper CTranslate2 CUDA build issue).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 01:59:36 +02:00
ed24ff4f8f Fix conversation mic crash on HTTP/IP access with clear insecure-context warning
navigator.mediaDevices is undefined on non-secure origins (HTTP + IP).
Add early guard with red banner in the chat panel, disabled mic button,
and an 8-second toast explaining the two fixes. Also add optional ms
parameter to toast().

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 01:40:27 +02:00
239bf0dad6 Persist engine URLs and custom cards server-side (survive browser restarts)
Engine URL inputs (Ollama, vLLM, faster-whisper, etc.), custom engine
cards, and the refinement/conversation LLM URLs were stored only in
localStorage and lost on browser data clear. All four are now synced
to settings.json via _patchSettings() with localStorage as fast
initial fallback.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 01:31:46 +02:00
916123f3b4 Fix engine RAM chips with real nvidia-smi GPU memory measurements
Replace all estimated VRAM values with actual per-process GPU memory
from nvidia-smi --query-compute-apps: Magpie TTS ~10 GB, Qwen3-TTS
clone ~6 GB / others ~5 GB, XTTS ~5 GB, VibeVoice ~4 GB, Parakeet
~3 GB, nemotron-asr-streaming ~11 GB.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 00:03:11 +02:00
49e48eb4e3 Update engine RAM metric chips to measured values from docker stats
Parakeet RNNT NIM measured at 550–605 MB (unified RAM), Magpie TTS at
982 MB, and Qwen3-TTS model at 4.3 GB on disk — replacing the placeholder
estimates that were too high across the board.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 23:51:15 +02:00
6a78be7a28 Add Conversation Playground + XTTS v2 backend + VibeVoice voice fix
Conversation Playground (new section):
- WhatsApp-style chat UI with user/assistant speech bubbles
- Click-to-record mic button using MediaRecorder API
- STT → LLM streaming → TTS pipeline via SSE (POST /api/conversation/turn)
- LLM tokens stream into assistant bubble in real time
- Audio auto-plays when TTS synthesises the reply
- Right-side stats panel: STT / LLM TTFT / LLM total / TTS / Total with bar chart
- Turn history list with per-turn total time and pass/fail indicator
- Configurable: STT backend, LLM URL + model, TTS backend + voice, system prompt
- Conversation history maintained across turns (last 20 messages sent to LLM)
- GET /api/conversation/llm-models proxies model list from any OpenAI-compatible LLM

XTTS v2 backend:
- Registers xtts as a first-class TTS backend (xtts_url setting, display name,
  capabilities, health/voice discovery, OpenAI-compatible generation)
- Added XTTS URL field to Settings → Connections
- Use-as-TTS button now saves to xtts_url (not tts_url)
- Batch benchmark backend select now refreshes alongside perf/preview selectors

VibeVoice fix:
- Added /voices to _TTS_VOICE_ENDPOINTS so VibeVoice voices are discovered

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 23:27:57 +02:00
6ab67ec973 Complete engine card audit: edit mode, missing buttons, and docker management
- Fix custom card dialog edit mode: save handler now updates existing card by id
  instead of always creating a new one; cancel/backdrop also clear editId
- Add "Use as TTS" button to Piper TTS and XTTS v2 static cards
- Add VibeVoice GitHub project link to VibeVoice card
- Add Edit button to custom engine cards (pre-fills dialog for update flow)
- Add initStaticDockerManagement(): injects optional docker container name field
  and Stop/Start/Restart buttons into all static llm-local-cards with no HTML changes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 22:15:51 +02:00
fe9c497308 Add URL inputs, Use-as buttons, and custom engine cards to Engines section
- Add URL input row to all docker container cards (defaulting to host.docker.internal:{port}), with Connect button, localStorage persistence, and green card border when reachable
- Map each docker container to its settings key (tts_url, nvidia_tts_url, nvidia_asr_url, tts_stream_url) via DC_USE_MAP; add "Use as TTS/STT" button that applies the URL to settings
- Add "+ Add Custom" button to LLM / STT / TTS sub-page headers, opening a dialog with Name, Role, URL, optional Docker container name, and Description fields
- Custom cards are saved to localStorage, rendered in dc-grid-llm/stt/tts alongside docker cards, have URL input + Use-as + Remove buttons; container name field enables Stop/Start/Restart
- Expose applyAndSaveSettings and probeUrl as window globals so docker card bindings can call them after renderLocalContainers
- Add dialog CSS and .dc-delete-btn red variant

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 21:46:48 +02:00
6808db3c59 Rename LLMs to Engines with LLM/STT/TTS sub-page nav, collapsible integration cards, equal-width card grids
- Rename "LLMs" section to "Engines" with brain icon
- Add three sub-pages (Language Models / Speech to Text / Text to Speech) following the same nav-tree pattern as Settings and My Voices
- Rewrite s-llms.html: three s-engines-page divs, docker container grids (dc-grid-tts, dc-grid-stt), VibeVoice card in TTS section, static cloud API cards per category
- Add navEnginesCat() and applyEnginesPage() to nav.js; engines tree open/close in showSection()
- Remove obsolete initLlmCatTabs IIFE; fix dc-refresh-btn from ID to class-based querySelectorAll
- Make integration cards in Connect Apps collapsible (collapsed by default) with favicon/icon prepended to h3
- Unify all Engines card grids to minmax(380px, 1fr) so local, docker, and cloud cards are the same width
- Add s-engines-page CSS (display:none / is-active:flex)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 21:05:51 +02:00
e434f10d9b Add batch benchmark to Benchmark page
Batch card pre-populates from active My Voices (checked by default) with
an option to reload from the backend. Select all / deselect all buttons.
Runs each selected voice N times sequentially with a live progress bar
and stop button. Results table updates after every voice and sorts by
avg RTF fastest-first; each row shows a trend badge (faster/slower/stable)
vs the previous session for that voice. All runs are saved to History.

renderPerfHistory hoisted to module level so both single-voice and batch
IIFEs can refresh the History card after saving new entries.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 11:29:48 +02:00
e0ff837947 Fix Benchmark page not showing: add s-performance to nav.js SECTIONS
nav.js SECTIONS array controls which page-sections get is-active toggled;
s-performance was missing so the section stayed display:none. Also map
the 'performance' tab key to the new section for switchTab compatibility.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 11:01:48 +02:00
dc47aa0431 Add Benchmark page, MCP snippets, Captures settings, and history tracking
- New s-performance section: dedicated Benchmark nav entry with run form,
  per-session results table, RTF trend badge (faster/slower/stable), SVG
  sparkline chart, and a History card backed by localStorage (last 50 sessions)
- Performance tab removed from Try It Out; element IDs unchanged so JS works
- renderIntegrationSnippets: adds Python MCP server + Claude Code .mcp.json
  config snippets to the Connect Apps page (integration-card-wide styling)
- Save handler: persists all Captures settings fields (stt_language,
  stt_preferred_backend, auto_refine, refine_model, refine_* toggles,
  captures_default_voice) alongside existing settings
- CSS: integration-card-wide accent border, benchmark history rows, trend
  badges, sparkline wrapper, bench-history-toolbar

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 09:57:55 +02:00
a72e49b807 Split settings into separate sub-pages with sidebar nav tree
Each settings nav item (General, Connections, Playback, Payloads, Storage,
API Keys, Backup, Logs, About) now shows its own isolated card page.
navSettingsCat() toggles .s-settings-page visibility + active state.
Save/Reload use event delegation (.s-save-btn/.s-reload-btn classes).
Default sub-page is Connections on first entry.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 03:23:30 +02:00
cd770801dc Add settings nav tree, Logs viewer, and About page (Voicebox-style hierarchy)
- Sidebar: Settings → nav-tree-head with sub-items (General, Connections,
  Playback, Payloads, Storage, API Keys, Backup, Logs, About)
- nav.js: navSettingsCat() scrolls to section, expands tree on activate
- General: theme select synced with applyTheme, surfaces dark/light toggle
- Logs: /api/logs endpoint (300-entry circular buffer), refresh/clear/
  auto-refresh every 3 s, level filters (All/Error/Warning/Info)
- About: backend availability chips from _ttsBackends, tech stack tags
- server.py: _BufferHandler attaches to root logger, /api/logs GET+DELETE
- Fix duplicate toast on save, guard removed settings-btn reference

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 03:05:15 +02:00
f871486635 Redesign settings page (Voicebox-style) and fix remaining old icons
- Settings: flat section layout with consistent 32px padding, groups with
  inline title+subtitle, clean accordion details with chevron, focus rings
- Replace all remaining emoji (  💾 🌙 ☀️ ⚠ ✓ ↓ ★) with MDI icons
- s-studio.html: globe emoji → mdi-earth, star filter → mdi-star-outline

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 02:48:34 +02:00
61a48ee84a Replace all icons with MDI (Material Design Icons) v7.4.47 — 171 replacements across 10 files
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 02:36:33 +02:00
21a219f816 Add Voicebox-inspired features: effects, history, chunked TTS, playlist, persona, STT refinement
- Audio effects panel (reverb, compressor, chorus, pitch shift via Spotify pedalboard)
  with presets (Studio, Broadcast, Telephone, Warm, Radio) — /api/audio/effects endpoint
- Generation history: last 20 sessions, play/reuse/add to playlist
- Chunked TTS: sentence-boundary splitting for unlimited-length text + WAV merge
- Playlist: ordered clip queue with up/down reorder + Export merged WAV
- LLM STT refinement: clean transcripts with local LLM (remove fillers/repetitions/corrections)
  — /api/refine-text endpoint, configurable toggles, restore-original button
- Voice persona: character description stored in voice meta.json, shown in inspector,
  "Rewrite with persona" button in TryOut via /api/rewrite-with-persona endpoint
- Voices export/import: ZIP backup+restore via /api/voices/export + /api/voices/import
- More whitespace, cleaner UI throughout all new panels

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 02:01:31 +02:00
08a63ef2d9 Add Kokoro TTS + faster-whisper/whisper.cpp/Groq STT backends with metrics
New TTS backend: Kokoro FastAPI (82M) — OpenAI-compatible, 11 built-in
voices, only shows when server is reachable (~300 MB CPU, ~0.1× RTF).

New STT backends in Transcribe dropdown: faster-whisper (CTranslate2 GPU,
~70× RT, 1.5 GB VRAM), whisper.cpp (CPU/CUDA, ~8–15× RT, ~1 GB RAM),
Groq Whisper (fastest cloud, free 2 000 req/day, key shared with Groq LLM).

Backend help panels now show  speed ·  latency ·  quality · 💾 RAM
metric chips for all TTS and STT backends.

Active Docker Stack cards also get per-container metric chips.

AI Backends section: "Use as STT" / "Use as TTS" one-click buttons on
faster-whisper, whisper.cpp, and Kokoro cards apply URLs to Settings
without leaving the page. Groq Whisper card notes the shared key path.

Settings: Kokoro URL in TTS cluster; faster-whisper URL, whisper.cpp URL,
Groq API key in STT cluster; quick-fill buttons for all local STT engines.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 00:16:43 +02:00
90cc7b7eb1 AI Backends: local service connect/disconnect, Docker container management, 0.0.0.0 probe fix
- Add initLlmsSection() IIFE to app.js: copy buttons, API key persistence with eye toggle
  and saved badge, local service URL persistence, Connect/Disconnect toggle with server-side
  probe via /api/probe-url (avoids CORS), card turns green on success / red on failure
- Substitute 0.0.0.0 → host.docker.internal before probing (0.0.0.0 not routable from Docker)
- Add /api/local-containers, /api/probe-url, start/stop/restart endpoints to server.py
- Rewrite AI Backends section into Local / Online API categories with Docker stack grid,
  local service cards (LLM/STT/TTS) with icons and editable URL inputs, online cloud API cards
- Add bind mounts for static/ and server.py so changes take effect without image rebuild
- Add dc-grid, llm-local-grid CSS with uniform minmax(310px,1fr) card layout
- Fix VOICE_HOST_DIR default via .env so voice folders survive container recreation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 22:35:42 +02:00
04b82e49d8 Add collapsible cards with persistent state (localStorage)
- All .card > h2 cards get a rotating chevron toggle; clicking the h2
  collapses/expands all content below it
- Existing <details> elements inside cards are synced to the same
  localStorage store so their state also persists across reloads
- Default state: expanded (open); first collapse saves false, reload restores it
- Storage key: 'card-collapse-v1' → per-card slug of heading text
- CSS: chevron box matches opt-group style; <details> summary styled consistently

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 13:25:57 +02:00