fix: stabilize async library navigation
This commit is contained in:
parent
9637784833
commit
fbb5fc52dd
@ -23,6 +23,8 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · versioned wi
|
||||
### Changed
|
||||
- **API Key Handoff Cache Policy.** Settings and explicit external-key regeneration responses now send `no-store`
|
||||
headers so browser and intermediary caches cannot retain the client-visible access-key handoff.
|
||||
- **Navigation Race Protection.** Rehearser phase requests now survive late feature-module initialization, and
|
||||
overlapping character-library refreshes cannot overwrite a newer table/card view.
|
||||
- **API Boundary Hardening.** External API authentication is enabled by default, request and mutation limits are
|
||||
enforced, and settings responses redact provider credentials for non-browser callers.
|
||||
- **Server-side Provider Credentials.** Provider keys are now masked even for same-origin Settings responses, are
|
||||
|
||||
2
static/dist/main.min.js
vendored
2
static/dist/main.min.js
vendored
File diff suppressed because one or more lines are too long
@ -4,15 +4,19 @@
|
||||
|
||||
// ── Characters / Cast ─────────────────────────────────────────────────────────
|
||||
|
||||
let _libCharsRenderSeq = 0;
|
||||
|
||||
async function libraryRenderCharacters() {
|
||||
const container = document.getElementById('lib-chars-list');
|
||||
if (!container) return;
|
||||
const renderSeq = ++_libCharsRenderSeq;
|
||||
|
||||
// This page can open before the shared voice library has finished loading.
|
||||
// Ratings live in window._voices, so rendering during that gap makes every
|
||||
// assigned voice look unrated even when its .meta.json still has a rating.
|
||||
if ((!Array.isArray(window._voices) || !window._voices.length) && typeof loadVoiceLibrary === 'function') {
|
||||
try { await loadVoiceLibrary(); } catch (e) { console.warn('[characters] voice library load failed', e); }
|
||||
if (renderSeq !== _libCharsRenderSeq) return;
|
||||
}
|
||||
|
||||
// Fetch BEFORE touching the DOM: a transient failure here (e.g. the
|
||||
@ -28,6 +32,7 @@ async function libraryRenderCharacters() {
|
||||
if (typeof toast === 'function') toast('Failed to load characters — keeping the current view', 'error');
|
||||
return;
|
||||
}
|
||||
if (renderSeq !== _libCharsRenderSeq) return;
|
||||
|
||||
// Every action on this page (remove voice, auto-design, delete...) re-runs
|
||||
// this full rebuild via `refresh()`. Collapsing the container down to a
|
||||
|
||||
@ -385,19 +385,16 @@
|
||||
el.classList.toggle('is-active', Number(el.dataset.rehearserPhase) === phase);
|
||||
});
|
||||
// delegate to rehearser's own showPhase() when it's ready
|
||||
if (typeof showPhase === 'function') {
|
||||
showPhase(phase);
|
||||
// Re-assert after the current initialization turn. This is harmless
|
||||
// when the module was already ready and closes the short race above.
|
||||
setTimeout(function () {
|
||||
var applyRequestedPhase = function () {
|
||||
if (document.getElementById('s-rehearser')?.classList.contains('is-active') && typeof showPhase === 'function') {
|
||||
showPhase(phase);
|
||||
}
|
||||
}, 0);
|
||||
} else {
|
||||
// rehearser.js not yet loaded — store the intent and let init pick it up
|
||||
// from its initialization block.
|
||||
}
|
||||
};
|
||||
// The section loader and feature module can finish after this function is
|
||||
// called. Retry across their initialization window, including when
|
||||
// showPhase did not exist on the first call.
|
||||
applyRequestedPhase();
|
||||
[0, 50, 250, 500, 1000].forEach(function (delay) { setTimeout(applyRequestedPhase, delay); });
|
||||
};
|
||||
|
||||
// Keep sidebar tree in sync when rehearser changes phase internally
|
||||
|
||||
@ -26,6 +26,7 @@ test('Characters / Cast supports profile sorting, column visibility, and voice r
|
||||
await page.waitForSelector('#lib-chars-list', { state: 'attached', timeout: 15_000 });
|
||||
await page.waitForFunction(() => typeof window.libraryRenderCharacters === 'function', null, { timeout: 15_000 });
|
||||
await page.locator('#nav-library-head').evaluate(el => el.click());
|
||||
await expect(page.locator('#s-library')).toHaveClass(/is-active/, { timeout: 15_000 });
|
||||
await page.locator('.lib-tab[data-library-view="characters"]').evaluate(el => el.click());
|
||||
await expect(page.locator('#s-library')).toHaveClass(/is-active/, { timeout: 15_000 });
|
||||
const tableToggle = page.locator('.lib-chars-view-toggle button[data-view="table"]');
|
||||
|
||||
Loading…
Reference in New Issue
Block a user