tts-voice-creator-clone-and.../static/nav.js
mARTin-B78 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

162 lines
6.9 KiB
JavaScript

(function () {
'use strict';
const TAB_SECTION_MAP = {
library: 's-voices',
source: 's-clone',
save: 's-clone',
design: 's-design',
custom: 's-design',
getvoices: 's-studio',
generation: 's-tryout',
'stt-tts': 's-tryout',
performance: 's-performance',
routing: 's-routing',
integrations: 's-connect',
howto: 's-connect',
settings: 's-settings',
llms: 's-llms'
};
const SECTIONS = ['s-voices', 's-clone', 's-design', 's-studio', 's-tryout', 's-performance', 's-routing', 's-connect', 's-settings', 's-llms', 's-conversation'];
function runSideEffects(name) {
if (name === 'library' && typeof loadVoiceLibrary === 'function') loadVoiceLibrary();
if ((name === 'integrations' || name === 'howto') && typeof renderIntegrationSnippets === 'function') {
if (typeof loadVoiceLibrary === 'function' && !(window._voices && window._voices.length)) loadVoiceLibrary();
renderIntegrationSnippets();
}
if (name === 'routing' && typeof loadRoutingTab === 'function') loadRoutingTab();
if (name === 'getvoices' && typeof loadGetVoices === 'function') loadGetVoices();
}
function applySettingsPage(cat) {
document.querySelectorAll('.s-settings-page').forEach(function (p) {
p.classList.toggle('is-active', p.dataset.page === cat);
});
document.querySelectorAll('[data-settings-cat]').forEach(function (el) {
el.classList.toggle('is-active', el.dataset.settingsCat === cat);
});
}
function showSection(sectionId) {
localStorage.setItem('ttsvc_section', sectionId);
SECTIONS.forEach(function (id) {
var el = document.getElementById(id);
if (el) el.classList.toggle('is-active', id === sectionId);
});
document.querySelectorAll('[data-nav-section]').forEach(function (item) {
item.classList.toggle('active', item.dataset.navSection === sectionId);
});
// Voices nav tree
var voicesTree = document.getElementById('nav-voices-tree');
var voicesChevron = document.getElementById('nav-voices-chevron');
if (voicesTree) voicesTree.classList.toggle('open', sectionId === 's-voices');
if (voicesChevron) voicesChevron.classList.toggle('open', sectionId === 's-voices');
// Settings nav tree
var settingsTree = document.getElementById('nav-settings-tree');
var settingsChevron = document.getElementById('nav-settings-chevron');
if (settingsTree) settingsTree.classList.toggle('open', sectionId === 's-settings');
if (settingsChevron) settingsChevron.classList.toggle('open', sectionId === 's-settings');
// Engines nav tree
var enginesTree = document.getElementById('nav-engines-tree');
var enginesChevron = document.getElementById('nav-engines-chevron');
if (enginesTree) enginesTree.classList.toggle('open', sectionId === 's-llms');
if (enginesChevron) enginesChevron.classList.toggle('open', sectionId === 's-llms');
// When entering settings, show the active sub-page (default: connections)
if (sectionId === 's-settings') {
applySettingsPage(window._settingsSidebarCat || 'connections');
}
// When entering engines, show the active sub-page (default: llm)
if (sectionId === 's-llms') {
applyEnginesPage(window._enginesSidebarCat || 'llm');
}
var main = document.getElementById('main-content');
if (main) main.scrollTop = 0;
}
window.navTo = function (sectionId) {
showSection(sectionId);
var tabName = Object.keys(TAB_SECTION_MAP).find(function (k) {
return TAB_SECTION_MAP[k] === sectionId;
});
if (tabName) runSideEffects(tabName);
};
window.switchTab = function (name) {
var sectionId = TAB_SECTION_MAP[name];
if (sectionId) showSection(sectionId);
runSideEffects(name);
return true;
};
// ── Voice tree sub-navigation ─────────────────────────────────────────────
window._voiceSidebarCat = 'all';
window.navVoicesCat = function (cat) {
navTo('s-voices');
window._voiceSidebarCat = cat;
document.querySelectorAll('[data-voice-cat]').forEach(function (el) {
el.classList.toggle('is-active', el.dataset.voiceCat === cat);
});
if (typeof renderVoiceList === 'function') renderVoiceList();
};
window.updateVoiceTree = function (voices) {
if (!voices) return;
function n(fn) { return voices.filter(fn).length; }
function set(id, v) { var el = document.getElementById(id); if (el) el.textContent = v || ''; }
set('ntc-all', n(function () { return true; }));
set('ntc-cloned', n(function (v) { return v.has_ref; }));
set('ntc-designed', n(function (v) { return !v.has_ref; }));
set('ntc-favorites',n(function (v) { return (v.rating || 0) >= 4; }));
set('ntc-hidden', n(function (v) { return v.enabled === false; }));
set('nav-voices-count', voices.length);
};
// ── Settings tree sub-navigation ──────────────────────────────────────────
window._settingsSidebarCat = 'connections';
window.navSettingsCat = function (cat) {
window._settingsSidebarCat = cat;
localStorage.setItem('ttsvc_settings_cat', cat);
navTo('s-settings'); // showSection will call applySettingsPage(cat)
if (cat === 'logs' && typeof loadSettingsLogs === 'function') loadSettingsLogs();
if (cat === 'about' && typeof renderSettingsAbout === 'function') renderSettingsAbout();
};
// ── Engines tree sub-navigation ───────────────────────────────────────────
function applyEnginesPage(cat) {
document.querySelectorAll('.s-engines-page').forEach(function (p) {
p.classList.toggle('is-active', p.dataset.page === cat);
});
document.querySelectorAll('[data-engines-cat]').forEach(function (el) {
el.classList.toggle('is-active', el.dataset.enginesCat === cat);
});
}
window._enginesSidebarCat = 'llm';
window.navEnginesCat = function (cat) {
window._enginesSidebarCat = cat;
localStorage.setItem('ttsvc_engines_cat', cat);
navTo('s-llms'); // showSection will call applyEnginesPage(cat)
if ((cat === 'tts' || cat === 'stt') && typeof loadLocalContainers === 'function') loadLocalContainers();
};
// Restore last-used section from localStorage (fallback: My Voices)
var _savedSection = localStorage.getItem('ttsvc_section');
window._settingsSidebarCat = localStorage.getItem('ttsvc_settings_cat') || 'connections';
window._enginesSidebarCat = localStorage.getItem('ttsvc_engines_cat') || 'llm';
var _startSection = (_savedSection && SECTIONS.includes(_savedSection)) ? _savedSection : 's-voices';
showSection(_startSection);
var _startTab = Object.keys(TAB_SECTION_MAP).find(function (k) { return TAB_SECTION_MAP[k] === _startSection; }) || 'library';
runSideEffects(_startTab);
})();