From 0b3109cbf41fb3630bd6cc490e4fd38b23688200 Mon Sep 17 00:00:00 2001 From: mARTin-B78 Date: Sun, 31 May 2026 19:19:20 +0200 Subject: [PATCH] UX: faster load, skeleton fix, About cleanup, name hint, Connect Apps docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Loader: - Post-init modules (engines, ai-backends, generation, conversation) now load AFTER the skeleton is removed instead of before. The UI is visible ~500 ms sooner on average; those four modules load while the user is already browsing Voices / Clone / Design. - Removed the sttReady event approach that was triggering a duplicate /api/stt-backends call; init.js already populates all STT selects once on startup. Skeleton: - Replaced the card-grid placeholder with a two-column workbench skeleton (voice list rows on the left + inspector placeholder on the right) that matches the real My Voices layout. Connect Apps: - MCP Server, /speak REST endpoint, and Global hotkey daemon sections moved from Settings → About to Connect Apps, where they belong. - About page now has GitHub + Releases links instead. Clone section: - Added a hint note beneath the sample-text textarea reminding the user to replace the placeholder name (Sam / Alex / Marco …) with their own. Co-Authored-By: Claude Sonnet 4.6 --- static/index.html | 139 +++++++++++--------------------- static/js/stt.js | 1 - static/js/voice-clone.js | 5 +- static/loader.js | 22 ++--- static/sections/s-clone.html | 1 + static/sections/s-connect.html | 58 +++++++++++++ static/sections/s-settings.html | 66 ++------------- 7 files changed, 124 insertions(+), 168 deletions(-) diff --git a/static/index.html b/static/index.html index 26dae4d..fa8ee3f 100644 --- a/static/index.html +++ b/static/index.html @@ -51,11 +51,15 @@ .sk-icon { width: 32px; height: 32px; border-radius: 8px; flex-shrink: 0; } .sk-title { display: flex; flex-direction: column; gap: 7px; } .sk-toolbar { display: flex; gap: 10px; align-items: center; } - .sk-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 12px; } - .sk-card { border-radius: 10px; overflow: hidden; } - .sk-card-img { height: 56px; border-radius: 0; } - .sk-card-body { padding: 10px 12px; display: flex; flex-direction: column; gap: 6px; } - .sk-row { display: flex; gap: 10px; margin-top: 4px; } + .sk-row { display: flex; gap: 10px; align-items: center; } + .sk-workbench { display: grid; grid-template-columns: 300px 1fr; gap: 0; border: 1px solid var(--border,#e5e7eb); border-radius: 10px; overflow: hidden; min-height: 480px; } + .sk-list { border-right: 1px solid var(--border,#e5e7eb); display: flex; flex-direction: column; } + .sk-list-row { display: flex; align-items: center; gap: 10px; padding: 10px 12px; border-bottom: 1px solid var(--border,#e5e7eb); } + .sk-avatar { width: 36px; height: 36px; border-radius: 8px; flex-shrink: 0; } + .sk-list-body { display: flex; flex-direction: column; gap: 5px; flex: 1; min-width: 0; } + .sk-insp { padding: 20px; display: flex; flex-direction: column; gap: 16px; } + .sk-insp-head { display: flex; flex-direction: column; gap: 8px; } + @media (max-width: 700px) { .sk-workbench { grid-template-columns: 1fr; } .sk-insp { display: none; } } @@ -74,6 +78,15 @@
Settings
+ +
+ + Voice Creator +
+ + + +
@@ -168,98 +181,38 @@
- -
- -
-
-
-
-
-
-
-
-
-
-
+ +
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
diff --git a/static/js/stt.js b/static/js/stt.js index 26fa623..874363b 100644 --- a/static/js/stt.js +++ b/static/js/stt.js @@ -286,5 +286,4 @@ $('stt-tts-save-wav-btn')?.addEventListener('click', () => { sttTtsDownload(sttTtsOutputBlob, ($('stt-tts-voice-select').value || 'stt_tts') + '_stt_tts.wav'); }); -window.dispatchEvent(new Event('sttReady')); diff --git a/static/js/voice-clone.js b/static/js/voice-clone.js index 3b9be51..af064c1 100644 --- a/static/js/voice-clone.js +++ b/static/js/voice-clone.js @@ -903,9 +903,8 @@ $('clone-refresh-stt-btn')?.addEventListener('click', async () => { try { await refreshSttBackends($('clone-stt-backend')?.value); } finally { $('clone-refresh-stt-btn').disabled = false; } }); -// Populate STT dropdown on load (refreshSttBackends is defined in stt.js which loads after us) -if (typeof refreshSttBackends === 'function') refreshSttBackends().catch(() => {}); -else window.addEventListener('sttReady', () => refreshSttBackends().catch(() => {})); +// STT dropdown is populated by init.js → refreshSttBackends() which runs after all +// feature modules (including stt.js) are loaded. No extra call needed here. $('transcribe-btn').addEventListener('click', async () => { const id = trimmedFileId||designedFileId||currentFileId; diff --git a/static/loader.js b/static/loader.js index a8940fa..6a0cd3c 100644 --- a/static/loader.js +++ b/static/loader.js @@ -108,18 +108,10 @@ // D — init (needs everything above to be defined) await _load('/static/js/init.js'); - // E — post-init modules (4 files fetched in parallel, executed in order) - await _loadBatch([ - '/static/js/engines.js', - '/static/js/ai-backends.js', - '/static/js/generation.js', - '/static/js/conversation.js', - ]); - - // F — navigation + // F — navigation (must run before revealing UI so the correct section shows) await _load('/static/nav.js'); - // ── Remove skeleton, reveal sections ──────────────────────────────────── + // ── Remove skeleton, reveal sections — as soon as nav is ready ────────── var sk = document.getElementById('app-skeleton'); if (sk) { sk.classList.add('sk-hidden'); @@ -128,4 +120,14 @@ document.querySelectorAll('.page-section').forEach(function (s) { s.style.display = ''; }); + + // E — post-init modules load in background AFTER the UI is already visible. + // Engines, AI backends, generation, and conversation sections load while + // the user browses Voices / Clone / Design, not before. + _loadBatch([ + '/static/js/engines.js', + '/static/js/ai-backends.js', + '/static/js/generation.js', + '/static/js/conversation.js', + ]); })(); diff --git a/static/sections/s-clone.html b/static/sections/s-clone.html index 44eb98c..04a6c6d 100644 --- a/static/sections/s-clone.html +++ b/static/sections/s-clone.html @@ -45,6 +45,7 @@
+

Replace the name (Sam / Alex / Marco …) with your own name before recording.

diff --git a/static/sections/s-connect.html b/static/sections/s-connect.html index d5e5f3a..be98bf1 100644 --- a/static/sections/s-connect.html +++ b/static/sections/s-connect.html @@ -7,6 +7,64 @@
+ + +
+

MCP Server

+

Expose voice tools to Claude Code, Cursor, and other MCP-aware agents via Streamable HTTP (JSON-RPC 2.0). Tools: speak, transcribe, list_captures, list_profiles.

+
+
+ + http://localhost:7890/mcp +
+
+ + claude mcp add voice-creator --transport http --url http://localhost:7890/mcp --header "X-Voice-Creator-Client-Id: claude-code" +
+
+ +
{"mcpServers":{"voice-creator":{"url":"http://localhost:7890/mcp","headers":{"X-Voice-Creator-Client-Id":"my-agent"}}}}
+
+
+
+ + +
+

/speak REST endpoint

+

Generate audio from any app or script without routing rules.

+
+
+ +
curl -X POST http://localhost:7890/speak \
+  -H "Content-Type: application/json" \
+  -H "X-Voice-Creator-Client-Id: my-script" \
+  -d '{"text":"Hello world","voice":"EN_F_Anna"}' \
+  --output speech.wav
+
+
+ +
curl -X PUT http://localhost:7890/speak/bindings/my-script \
+  -H "Content-Type: application/json" \
+  -d '{"voice":"EN_F_Anna"}'
+
+
+
+ + +
+

Global hotkey daemon

+

Push-to-talk transcription that types the result into any focused window on the host machine.

+
+
+ +
pip install pynput sounddevice soundfile pyperclip requests
+python hotkey_daemon.py --server http://localhost:7890
+ Hold Ctrl+Shift+Space to record, release to transcribe and type. Linux: install xdotool for direct key injection. +
+
+
+ +

Use voices in other apps

The editor creates and manages the voice files. External apps should connect to the Creator proxy or a reachable TTS backend, then use one of the active voice names.

diff --git a/static/sections/s-settings.html b/static/sections/s-settings.html index fee34a0..540299d 100644 --- a/static/sections/s-settings.html +++ b/static/sections/s-settings.html @@ -523,68 +523,12 @@ MDI v7.4.47
+ +

MCP server, REST /speak endpoint, and global hotkey daemon are documented under Connect Apps.

- -
-
- MCP Server - Expose voice tools to Claude Code, Cursor, and other MCP-aware agents -
-
-
- - http://localhost:7890/mcp - Streamable HTTP (JSON-RPC 2.0). Tools: speak, transcribe, list_captures, list_profiles. -
-
- - claude mcp add voice-creator --transport http --url http://localhost:7890/mcp --header "X-Voice-Creator-Client-Id: claude-code" -
-
- -
{"mcpServers":{"voice-creator":{"url":"http://localhost:7890/mcp","headers":{"X-Voice-Creator-Client-Id":"my-agent"}}}}
-
-
-
- -
-
- /speak REST endpoint - Generate audio from any app or script without routing rules -
-
-
- -
curl -X POST http://localhost:7890/speak \
-  -H "Content-Type: application/json" \
-  -H "X-Voice-Creator-Client-Id: my-script" \
-  -d '{"text":"Hello world","voice":"EN_F_Anna"}' \
-  --output speech.wav
-
-
- -
curl -X PUT http://localhost:7890/speak/bindings/my-script \
-  -H "Content-Type: application/json" \
-  -d '{"voice":"EN_F_Anna"}'
-
-
-
- -
-
- Global hotkey daemon - Push-to-talk transcription that types into any focused window -
-
-
- -
pip install pynput sounddevice soundfile pyperclip requests
-python hotkey_daemon.py --server http://localhost:7890
- Hold Ctrl+Shift+Space to record, release to transcribe and type. Linux: install xdotool for direct key injection. -
-
-
-