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

125 lines
7.7 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TTS Voice Creator</title>
<!-- WaveSurfer must load synchronously before app.js initialises audio -->
<script src="https://unpkg.com/wavesurfer.js@7/dist/wavesurfer.min.js"></script>
<script src="https://unpkg.com/wavesurfer.js@7/dist/plugins/regions.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mdi/font@7.4.47/css/materialdesignicons.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flag-icons@7.3.2/css/flag-icons.min.css">
<link rel="stylesheet" href="/static/style.css">
</head>
<body>
<!-- Hidden tabs nav — required for JS backend availability detection -->
<nav class="tabs" style="display:none" aria-hidden="true">
<div class="tab active" data-tab="library" data-backend-required="voice_clone" tabindex="-1"><span class="tab-title">Voice Clone</span></div>
<div class="tab" data-tab="design" data-backend-required="voice_design" tabindex="-1"><span class="tab-title">Voice Design</span></div>
<div class="tab" data-tab="custom" data-backend-required="customvoice" tabindex="-1"><span class="tab-title">Voice Custom</span></div>
<div class="tab" data-tab="generation" data-backend-required="any_tts" tabindex="-1"><span class="tab-title">TTS Generation</span></div>
<div class="tab" data-tab="stt-tts" data-backend-required="any_tts" tabindex="-1"><span class="tab-title">STT-TTS</span></div>
<div class="tab" data-tab="performance" data-backend-required="any_tts" tabindex="-1"><span class="tab-title">Performance</span></div>
<div class="tab" data-tab="routing" tabindex="-1"><span class="tab-title">Routing</span></div>
<div class="tab" data-tab="integrations" tabindex="-1"><span class="tab-title">How to</span></div>
<div class="tab" data-tab="getvoices" tabindex="-1"><span class="tab-title">Get Voices</span></div>
<div class="tab" data-tab="settings" tabindex="-1"><span class="tab-title">Settings</span></div>
</nav>
<!-- App shell -->
<div id="app-shell">
<!-- Sidebar -->
<aside id="sidebar">
<div class="sidebar-brand">
<h1><span class="mdi mdi-microphone-variant"></span> Voice Creator</h1>
<p>Clone &middot; Design &middot; Deploy</p>
</div>
<nav class="sidebar-nav">
<div class="nav-group-label">Voices</div>
<div class="nav-item nav-tree-head active" data-nav-section="s-voices" id="nav-voices-head" onclick="navTo('s-voices')">
<span class="nav-icon"><span class="mdi mdi-account-voice"></span></span>
<span class="nav-label">My Voices</span>
<span class="nav-badge" id="nav-voices-count"></span>
<span class="nav-chevron" id="nav-voices-chevron"><span class="mdi mdi-chevron-down"></span></span>
</div>
<div class="nav-tree open" id="nav-voices-tree">
<div class="nav-tree-item is-active" data-voice-cat="all" onclick="navVoicesCat('all')">All voices <span class="ntc" id="ntc-all"></span></div>
<div class="nav-tree-item" data-voice-cat="cloned" onclick="navVoicesCat('cloned')">Cloned <span class="ntc" id="ntc-cloned"></span></div>
<div class="nav-tree-item" data-voice-cat="designed" onclick="navVoicesCat('designed')">Designed <span class="ntc" id="ntc-designed"></span></div>
<div class="nav-tree-item" data-voice-cat="favorites" onclick="navVoicesCat('favorites')">Favorites <span class="ntc" id="ntc-favorites"></span></div>
<div class="nav-tree-item" data-voice-cat="hidden" onclick="navVoicesCat('hidden')">Hidden <span class="ntc" id="ntc-hidden"></span></div>
<div class="nav-tree-item" data-voice-cat="tools" onclick="navVoicesCat('tools')">Library tools</div>
</div>
<div class="nav-item" data-nav-section="s-clone" onclick="navTo('s-clone')">
<span class="nav-icon"><span class="mdi mdi-microphone-variant"></span></span> Clone a Voice
</div>
<div class="nav-item" data-nav-section="s-design" onclick="navTo('s-design')">
<span class="nav-icon"><span class="mdi mdi-auto-fix"></span></span> Design a Voice
</div>
<div class="nav-item" data-nav-section="s-studio" onclick="navTo('s-studio')">
<span class="nav-icon"><span class="mdi mdi-earth"></span></span> Get Voices Online
</div>
<div class="nav-item" data-nav-section="s-tryout" onclick="navTo('s-tryout')">
<span class="nav-icon"><span class="mdi mdi-play"></span></span> Try It Out
</div>
<div class="nav-group-label" style="margin-top:6px">Setup</div>
<div class="nav-item" data-nav-section="s-llms" onclick="navTo('s-llms')">
<span class="nav-icon"><span class="mdi mdi-brain"></span></span> LLMs
</div>
<div class="nav-item" data-nav-section="s-routing" onclick="navTo('s-routing')">
<span class="nav-icon"><span class="mdi mdi-swap-horizontal"></span></span> App Routing
</div>
<div class="nav-item" data-nav-section="s-connect" onclick="navTo('s-connect')">
<span class="nav-icon"><span class="mdi mdi-api"></span></span> Connect Apps
</div>
<div class="nav-item nav-tree-head" data-nav-section="s-settings" id="nav-settings-head" onclick="navTo('s-settings')">
<span class="nav-icon"><span class="mdi mdi-cog-outline"></span></span>
<span class="nav-label">Settings</span>
<span class="nav-chevron" id="nav-settings-chevron"><span class="mdi mdi-chevron-down"></span></span>
</div>
<div class="nav-tree" id="nav-settings-tree">
<div class="nav-tree-item" data-settings-cat="general" onclick="navSettingsCat('general')">General</div>
<div class="nav-tree-item" data-settings-cat="connections" onclick="navSettingsCat('connections')">Connections</div>
<div class="nav-tree-item" data-settings-cat="playback" onclick="navSettingsCat('playback')">Playback</div>
<div class="nav-tree-item" data-settings-cat="payloads" onclick="navSettingsCat('payloads')">Payloads</div>
<div class="nav-tree-item" data-settings-cat="storage" onclick="navSettingsCat('storage')">Storage</div>
<div class="nav-tree-item" data-settings-cat="apikeys" onclick="navSettingsCat('apikeys')">API Keys</div>
<div class="nav-tree-item" data-settings-cat="backup" onclick="navSettingsCat('backup')">Backup</div>
<div class="nav-tree-item" data-settings-cat="logs" onclick="navSettingsCat('logs')">Logs</div>
<div class="nav-tree-item" data-settings-cat="about" onclick="navSettingsCat('about')">About</div>
</div>
</nav>
<div class="sidebar-footer">
<button id="theme-btn" title="Switch theme" style="display:none"><span class="mdi mdi-weather-sunny"></span></button>
</div>
</aside>
<!-- Main scrollable content — sections filled by loader.js -->
<main id="main-content">
<section class="page-section" id="s-voices"></section>
<section class="page-section" id="s-clone"></section>
<section class="page-section" id="s-design"></section>
<section class="page-section" id="s-studio"></section>
<section class="page-section" id="s-tryout"></section>
<section class="page-section" id="s-routing"></section>
<section class="page-section" id="s-connect"></section>
<section class="page-section" id="s-settings"></section>
<section class="page-section" id="s-llms"></section>
</main>
</div><!-- /app-shell -->
<div id="toast"></div>
<div id="status-bar">Loading…</div>
<!-- loader.js: fetches sections → loads app.js → loads nav.js -->
<script src="/static/loader.js"></script>
</body>
</html>