(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-rehearser', 's-performance', 's-routing', 's-connect', 's-settings', 's-llms', 's-conversation']; function runSideEffects(name) { if ((name === 'source' || name === 'save') && typeof initCloneSampleText === 'function') initCloneSampleText(); 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); // ── Mobile sidebar drawer ──────────────────────────────────────────────── var sidebar = document.getElementById('sidebar'); var backdrop = document.getElementById('sidebar-backdrop'); var toggleBtn = document.getElementById('sidebar-toggle'); function openSidebar() { if (!sidebar) return; sidebar.classList.add('open'); if (backdrop) backdrop.classList.add('open'); document.body.style.overflow = 'hidden'; // prevent scroll behind drawer } function closeSidebar() { if (!sidebar) return; sidebar.classList.remove('open'); if (backdrop) backdrop.classList.remove('open'); document.body.style.overflow = ''; } if (toggleBtn) toggleBtn.addEventListener('click', openSidebar); if (backdrop) backdrop.addEventListener('click', closeSidebar); // Close drawer when a nav item is tapped on mobile document.querySelectorAll('#sidebar .nav-item, #sidebar .nav-tree-item').forEach(function (el) { el.addEventListener('click', function () { if (window.innerWidth <= 767) closeSidebar(); }); }); // ── Mobile voice inspector: back button ────────────────────────────────── // When a voice row is selected on mobile, show inspector full-height. // The inspector JS will call window.onMobileInspectorOpen/Close as hooks. window.onMobileInspectorOpen = function () { var wb = document.querySelector('.voices-workbench'); if (!wb || window.innerWidth > 767) return; wb.classList.add('mobile-inspector-open'); // Inject back button if not already there var insp = document.getElementById('voices-inspector'); if (insp && !insp.querySelector('.insp-mobile-back')) { var back = document.createElement('div'); back.className = 'insp-mobile-back'; back.innerHTML = ' All voices'; back.addEventListener('click', function () { window.onMobileInspectorClose && window.onMobileInspectorClose(); }); insp.insertBefore(back, insp.firstChild); } }; window.onMobileInspectorClose = function () { var wb = document.querySelector('.voices-workbench'); if (wb) wb.classList.remove('mobile-inspector-open'); var back = document.querySelector('.insp-mobile-back'); if (back) back.remove(); }; })();