diff --git a/CHANGELOG.md b/CHANGELOG.md index 412e02c..8b9c736 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · versioned wi ## [Unreleased] ### Changed +- **Audiobook namespace wiring.** Network helpers and public casting, recast, export, and save actions now register + after their lazy modules load, completing the namespace bridge without removing legacy global entry points. - **Audiobook live cast-view extraction.** The live cast feed, roster, settings, and prompt controls now live in `audiobook-cast.js`; the legacy `audiobookCastView` entry point remains available for compatibility. - **Rehearser voice-matching extraction.** Local voice matching, preview-line selection, fish.audio search/import, diff --git a/VERSION b/VERSION index c2988cb..a359225 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.24.48 +1.24.49 diff --git a/static/index.html b/static/index.html index d2bb41f..655721c 100644 --- a/static/index.html +++ b/static/index.html @@ -10,7 +10,7 @@ - + @@ -27,12 +27,12 @@ - - - - - - + + + + + + @@ -397,7 +397,7 @@ window.toggleNavTree = function(treeId, chevronId) { - + diff --git a/static/js/audiobook.js b/static/js/audiobook.js index 5441dc6..fa11de2 100644 --- a/static/js/audiobook.js +++ b/static/js/audiobook.js @@ -3562,3 +3562,14 @@ function audiobookExportDialog() { modal.style.display = 'flex'; } } + +window.TTVC = window.TTVC || {}; +window.TTVC.audiobook = window.TTVC.audiobook || {}; +Object.assign(window.TTVC.audiobook, { + startCast: audiobookCast, + openCastView: audiobookOpenCastView, + recastUnknown: audiobookRecastUnknown, + recastSelectedCharacters: audiobookRecastSelectedCharacters, + export: audiobookExport, + saveAsRehearsal: audiobookSaveAsRehearsal, +}); diff --git a/static/js/audiobook/network.js b/static/js/audiobook/network.js index 6608b0f..36aa2e0 100644 --- a/static/js/audiobook/network.js +++ b/static/js/audiobook/network.js @@ -85,3 +85,11 @@ async function audiobookAttributeStream(body, view, outerSignal, idleTimeoutMs = if (outerSignal) outerSignal.removeEventListener('abort', onAbort); } } + +window.TTVC = window.TTVC || {}; +window.TTVC.audiobook = window.TTVC.audiobook || {}; +Object.assign(window.TTVC.audiobook, { + fetchWithTimeout: audiobookFetchWithTimeout, + attributeStream: audiobookAttributeStream, + timeoutSeconds: audiobookTimeoutSeconds, +}); diff --git a/tests/lazy-loading.spec.js b/tests/lazy-loading.spec.js index 97da28c..4b0ca12 100644 --- a/tests/lazy-loading.spec.js +++ b/tests/lazy-loading.spec.js @@ -23,3 +23,16 @@ test('heavy feature modules load on demand', async ({ page }) => { await page.waitForTimeout(100); expect(requests.filter(url => url.includes('/rehearser.js')).length).toBe(countBeforeRepeat); }); + +test('Audiobook lazy modules publish their namespace APIs after loading', async ({ page }) => { + await page.goto('/', { waitUntil: 'domcontentloaded' }); + await page.waitForFunction(() => typeof window.navTo === 'function', null, { timeout: 15_000 }); + await page.waitForSelector('#app-skeleton', { state: 'detached', timeout: 15_000 }); + await page.evaluate(() => window.navTo('s-reader')); + await page.waitForFunction(() => typeof window.audiobookCastView === 'function', null, { timeout: 15_000 }); + await expect.poll(() => page.evaluate(() => ({ + castView: typeof window.TTVC?.audiobook?.cast?.castView, + stream: typeof window.TTVC?.audiobook?.attributeStream, + startCast: typeof window.TTVC?.audiobook?.startCast, + }))).toEqual({ castView: 'function', stream: 'function', startCast: 'function' }); +});