fix: voice inspector TDZ crash + batch ops respect selection (v1.9.7)
- voice-inspector: move `curGender` const before its first use — it was declared at line 86 but referenced at line 61, causing a ReferenceError (temporal dead zone) that silently aborted selectVoice() on every click - Calc dB, Precompute, Batch Seeds now filter to checked voices when a bulk selection is active, matching Benchmark's existing behaviour Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
6b52a346d5
commit
217d92b91a
@ -9,6 +9,14 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · versioned wi
|
||||
|
||||
---
|
||||
|
||||
## [1.9.7] — 2026-06-27
|
||||
|
||||
### Fixed
|
||||
- **Voice inspector broken**: `curGender` was referenced before its `const` declaration inside `selectVoice()`, causing a temporal dead zone ReferenceError that silently prevented the inspector from opening. Clicking a voice now correctly opens the detail panel again.
|
||||
- **Batch operations ignore selection**: Calc dB, Precompute, and Batch Seeds now operate only on the checked (selected) voices when a selection is active, matching the existing behaviour of Benchmark. Previously all three always ran on all active / visible voices regardless of selection.
|
||||
|
||||
---
|
||||
|
||||
## [1.9.6] — 2026-06-26
|
||||
|
||||
### Fixed
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
|
||||
<!-- ── Core styles (local — no CDN dependency for first paint) ────────── -->
|
||||
<link rel="stylesheet" href="/static/vendor/mdi/materialdesignicons.min.css">
|
||||
<link rel="stylesheet" href="/static/style.css?v=1.9.6">
|
||||
<link rel="stylesheet" href="/static/style.css?v=1.9.7">
|
||||
|
||||
|
||||
<!-- ── Flag icons — non-blocking (loaded async, icons appear after JS) ── -->
|
||||
@ -308,7 +308,7 @@
|
||||
<script src="/static/vendor/wavesurfer-regions.min.js"></script>
|
||||
|
||||
<!-- loader.js: fetches sections → loads JS modules → removes skeleton -->
|
||||
<script src="/static/loader.js?v=1.9.6"></script>
|
||||
<script src="/static/loader.js?v=1.9.7"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -431,10 +431,13 @@ function attachSeedFinder(voiceId, body) {
|
||||
// writes to the same cache the per-voice Seed Finder reads, so opening any voice
|
||||
// later shows its samples instantly.
|
||||
function seedFinderBatchAll() {
|
||||
const ids = (typeof activeVoiceIds === 'function')
|
||||
const _sfUseSelected = typeof _bulkSelected !== 'undefined' && _bulkSelected.size > 0;
|
||||
const ids = _sfUseSelected
|
||||
? [..._bulkSelected]
|
||||
: (typeof activeVoiceIds === 'function')
|
||||
? activeVoiceIds()
|
||||
: (window._voices || []).filter(v => v.enabled !== false).map(v => v.id);
|
||||
if (!ids.length) { if (typeof toast === 'function') toast('No active voices', 'error'); return; }
|
||||
if (!ids.length) { if (typeof toast === 'function') toast('No voices selected', 'error'); return; }
|
||||
|
||||
// In-app config dialog (no browser prompts): pick the seed range, then Start.
|
||||
document.getElementById('seedbatch-overlay')?.remove();
|
||||
@ -442,7 +445,7 @@ function seedFinderBatchAll() {
|
||||
ov.className = 'audiobook-overlay'; ov.id = 'seedbatch-overlay';
|
||||
ov.innerHTML = `<div class="audiobook-box">
|
||||
<div class="audiobook-title"><span class="mdi mdi-dice-multiple-outline"></span> Batch seed generation</div>
|
||||
<div class="audiobook-msg">Generate & cache Seed Finder samples for all <b>${ids.length}</b> active voices. Already-cached seeds are skipped, so it's resumable; you can cancel anytime.</div>
|
||||
<div class="audiobook-msg">Generate & cache Seed Finder samples for <b>${ids.length}</b> ${_sfUseSelected ? 'selected' : 'active'} voice${ids.length !== 1 ? 's' : ''}. Already-cached seeds are skipped, so it's resumable; you can cancel anytime.</div>
|
||||
<div class="seed-finder-row seed-finder-params" style="margin:12px 0">
|
||||
<div class="seed-finder-field">
|
||||
<label>Seeds: from</label>
|
||||
|
||||
@ -45,6 +45,7 @@ function selectVoice(wrap) {
|
||||
const langCode = v.lang || wrap.dataset.lang || '';
|
||||
const flagCc = v.flag || wrap.dataset.flagCc || langCode;
|
||||
const rating = v.rating || 0;
|
||||
const curGender = v.gender || 'N';
|
||||
|
||||
const nameParts = voiceId.split('_');
|
||||
// Prefer an explicit display name (e.g. the character name for Rehearser voices);
|
||||
@ -83,7 +84,6 @@ function selectVoice(wrap) {
|
||||
const _gM = {F:'♀', M:'♂', N:'⚥', '':'?'};
|
||||
const _gL = {F:'Female', M:'Male', N:'Diverse', '':'—'};
|
||||
const _gC = {F:'g-f', M:'g-m', N:'g-n', '':'g-n'};
|
||||
const curGender = v.gender || 'N';
|
||||
const genderLabelHtml = `${_gM[curGender]||'?'} ${_gL[curGender]||'—'}`;
|
||||
|
||||
inspector.innerHTML = `
|
||||
|
||||
@ -417,7 +417,9 @@ async function clientVoiceLoudness(v) {
|
||||
}
|
||||
|
||||
async function clientCalculateVoiceDb() {
|
||||
const voices = visibleLibraryVoices();
|
||||
const voices = (_bulkSelected && _bulkSelected.size > 0)
|
||||
? (_voices || []).filter(v => _bulkSelected.has(v.id))
|
||||
: visibleLibraryVoices();
|
||||
const errors = [];
|
||||
let calculated = 0;
|
||||
const stats = {startedAt: Date.now(), ok: 0, slow: 0, errors: 0, middleLabel: 'Skipped'};
|
||||
@ -622,7 +624,8 @@ function libraryTargetDb() {
|
||||
|
||||
$('calculate-db-btn').addEventListener('click', async () => {
|
||||
$('calculate-db-btn').disabled = true;
|
||||
status('Calculating voice loudness…');
|
||||
const _calcTarget = (_bulkSelected && _bulkSelected.size > 0) ? `${_bulkSelected.size} selected` : 'visible';
|
||||
status(`Calculating voice loudness (${_calcTarget})…`);
|
||||
try {
|
||||
const d = await clientCalculateVoiceDb();
|
||||
renderVoiceList();
|
||||
@ -907,9 +910,11 @@ $('copy-active-voices-btn').addEventListener('click', async () => {
|
||||
// (re)builds it from the wav when missing — so this is purely a warm-up.
|
||||
$('precompute-embeddings-btn')?.addEventListener('click', async () => {
|
||||
const backend = libraryTtsBackend();
|
||||
const ids = activeVoiceIds();
|
||||
if (!ids.length) { toast('No active voices to precompute', 'error'); return; }
|
||||
if (!confirm(`Precompute speaker embeddings for ${ids.length} active voice(s) via “${backend}”?\n\nThis warms each voice so the engine caches its .pt and first playback is instant.`)) return;
|
||||
const _useSelected = _bulkSelected && _bulkSelected.size > 0;
|
||||
const ids = _useSelected ? [..._bulkSelected] : activeVoiceIds();
|
||||
const _scopeLabel = _useSelected ? `${ids.length} selected` : `${ids.length} active`;
|
||||
if (!ids.length) { toast('No voices to precompute', 'error'); return; }
|
||||
if (!confirm(`Precompute speaker embeddings for ${_scopeLabel} voice(s) via “${backend}”?\n\nThis warms each voice so the engine caches its .pt and first playback is instant.`)) return;
|
||||
|
||||
const btn = $('precompute-embeddings-btn'); if (btn) btn.disabled = true;
|
||||
const ov = document.createElement('div');
|
||||
|
||||
Loading…
Reference in New Issue
Block a user