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

129 lines
4.9 KiB
JavaScript

(async function () {
'use strict';
const SECTIONS = [
's-voices', 's-clone', 's-design', 's-studio', 's-tryout',
's-performance', 's-routing', 's-connect', 's-settings',
's-llms', 's-conversation',
];
// Determine which section to show first (same logic as nav.js)
var _savedSection = localStorage.getItem('ttsvc_section');
var _activeSection = (_savedSection && SECTIONS.indexOf(_savedSection) !== -1)
? _savedSection : 's-voices';
// ── Fetch app version once for JS cache-busting ──────────────────────────
var _appVersion = 'dev';
try {
var _vr = await fetch('/api/version');
var _vd = await _vr.json();
_appVersion = _vd.version || _appVersion;
} catch (_) {}
var _ts = Date.now(); // sections always fresh during a session
function _inject(id, html) {
var el = document.getElementById(id);
if (el) el.innerHTML = html;
}
// ── 1. Load ONLY the active section before anything else ─────────────────
// Feature modules set up DOM event listeners — the active section's elements
// must exist when JS runs. Other sections are loaded in step 2.
try {
var _ar = await fetch('/static/sections/' + _activeSection + '.html?v=' + _ts);
if (!_ar.ok) throw new Error(_ar.status + ' ' + _ar.statusText);
_inject(_activeSection, await _ar.text());
} catch (e) {
var _ael = document.getElementById(_activeSection);
if (_ael) _ael.innerHTML =
'<p style="color:var(--red);padding:24px">Failed to load section <b>' +
_activeSection + '</b>: ' + e.message + '</p>';
}
// ── 2. Fetch remaining sections in background (parallel with JS loading) ──
// These run concurrently with utils + settings below. Because section HTML
// files are much smaller than JS, they finish before the feature-module batch
// needs them.
var _bgSections = Promise.all(
SECTIONS.filter(function (id) { return id !== _activeSection; })
.map(async function (id) {
try {
var res = await fetch('/static/sections/' + id + '.html?v=' + _ts);
if (!res.ok) throw new Error(res.status + ' ' + res.statusText);
_inject(id, await res.text());
} catch (e) {
var el = document.getElementById(id);
if (el) el.innerHTML =
'<p style="color:var(--red);padding:24px">Failed to load section <b>' +
id + '</b>: ' + e.message + '</p>';
}
})
);
// ── 3. Load JS in parallel batches ────────────────────────────────────────
// Scripts with async=false are FETCHED in parallel but EXECUTED in DOM
// insertion order — safe for modules that share a global scope.
function _load(src) {
return new Promise(function (resolve, reject) {
var s = document.createElement('script');
s.src = src + '?v=' + _appVersion;
s.async = false; // fetch in parallel, execute in DOM order
s.onload = resolve;
s.onerror = function () { reject(new Error('Failed to load ' + src)); };
document.body.appendChild(s);
});
}
function _loadBatch(srcs) {
return Promise.all(srcs.map(_load));
}
// A — foundation (sequential: each must finish before the next batch starts)
await _load('/static/js/utils.js');
await _load('/static/js/settings.js');
// Wait for background sections BEFORE feature modules run.
// Feature modules set up event listeners on DOM elements; all section HTML
// must already be injected. On a local server the sections finish well before
// settings.js completes, so this await is typically instant.
await _bgSections;
// C — feature modules (9 files fetched in parallel, executed in order)
await _loadBatch([
'/static/js/voice-inspector.js',
'/static/js/voice-sources.js',
'/static/js/integrations.js',
'/static/js/routing.js',
'/static/js/voice-clone.js',
'/static/js/voice-library.js',
'/static/js/tts-preview.js',
'/static/js/benchmark.js',
'/static/js/stt.js',
]);
// 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
await _load('/static/nav.js');
// ── Remove skeleton, reveal sections ────────────────────────────────────
var sk = document.getElementById('app-skeleton');
if (sk) {
sk.classList.add('sk-hidden');
setTimeout(function () { if (sk.parentNode) sk.parentNode.removeChild(sk); }, 300);
}
document.querySelectorAll('.page-section').forEach(function (s) {
s.style.display = '';
});
})();