refactor: add frontend namespace migration bridge
This commit is contained in:
parent
c4a5689a1d
commit
55d0dd1012
@ -8,6 +8,8 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · versioned wi
|
|||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
- **Frontend Namespace Bridge.** A stable `window.TTVC` migration surface now exposes Audiobook, Rehearser, and job
|
||||||
|
APIs while preserving legacy globals for compatibility during the larger module split.
|
||||||
- **Book 2 Attribution Generalization.** The full 1,663-segment Book 2 snapshot is now a regression; structural
|
- **Book 2 Attribution Generalization.** The full 1,663-segment Book 2 snapshot is now a regression; structural
|
||||||
`Narrator` labels can no longer become dialogue speakers or phantom cast members.
|
`Narrator` labels can no longer become dialogue speakers or phantom cast members.
|
||||||
- **Retry UI Regression Coverage.** Desktop smoke tests now verify that a Retry button is rendered only when the
|
- **Retry UI Regression Coverage.** Desktop smoke tests now verify that a Retry button is rendered only when the
|
||||||
|
|||||||
3
TODO.md
3
TODO.md
@ -107,7 +107,8 @@ the first block before investing in architecture or polish.
|
|||||||
## P1 — Modular frontend architecture
|
## P1 — Modular frontend architecture
|
||||||
|
|
||||||
- [ ] Split `audiobook.js`, `rehearser.js`, and `style.css` by responsibility.
|
- [ ] Split `audiobook.js`, `rehearser.js`, and `style.css` by responsibility.
|
||||||
- [ ] Replace broad globals with namespaces such as `window.TTVC.audiobook`.
|
- [ ] Replace broad globals with namespaces such as `window.TTVC.audiobook`; a compatibility namespace bridge now
|
||||||
|
exposes the existing Audiobook, Rehearser, and job APIs while responsibility-by-responsibility migration continues.
|
||||||
- [x] Generate the feature-module list for `loader.js` and `scripts/minify.mjs` from one shared manifest;
|
- [x] Generate the feature-module list for `loader.js` and `scripts/minify.mjs` from one shared manifest;
|
||||||
larger responsibility splits remain.
|
larger responsibility splits remain.
|
||||||
- [x] Add a dependency-free frontend syntax and top-level declaration collision gate (`npm run check:frontend`);
|
- [x] Add a dependency-free frontend syntax and top-level declaration collision gate (`npm run check:frontend`);
|
||||||
|
|||||||
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
23
static/js/namespace.js
Normal file
23
static/js/namespace.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
// Stable migration surface for the legacy shared-global frontend.
|
||||||
|
// Existing names remain available for compatibility; new code should prefer
|
||||||
|
// these namespaces while the large Audiobook/Rehearser files are split.
|
||||||
|
(function exposeTtvcNamespaces() {
|
||||||
|
const root = window.TTVC = window.TTVC || {};
|
||||||
|
root.audiobook = root.audiobook || {};
|
||||||
|
root.rehearser = root.rehearser || {};
|
||||||
|
root.jobs = root.jobs || {};
|
||||||
|
[
|
||||||
|
['audiobook', 'fetchWithTimeout', 'audiobookFetchWithTimeout'],
|
||||||
|
['audiobook', 'attributeStream', 'audiobookAttributeStream'],
|
||||||
|
['audiobook', 'timeoutSeconds', 'audiobookTimeoutSeconds'],
|
||||||
|
['rehearser', 'renderLibrary', 'renderLibraryList'],
|
||||||
|
['rehearser', 'showPhase', 'showPhase'],
|
||||||
|
].forEach(([namespace, publicName, legacyName]) => {
|
||||||
|
if (typeof window[legacyName] !== 'undefined' && root[namespace][publicName] == null) {
|
||||||
|
root[namespace][publicName] = window[legacyName];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// `rehState` is a top-level lexical binding for compatibility and therefore
|
||||||
|
// is not guaranteed to be an own property of window.
|
||||||
|
if (typeof rehState !== 'undefined' && root.rehearser.state == null) root.rehearser.state = rehState;
|
||||||
|
})();
|
||||||
@ -2,5 +2,5 @@
|
|||||||
"voice-picker", "benchmark-voice-picker", "voice-inspector", "seed-finder", "voice-sources", "fishaudio-browser", "jobs",
|
"voice-picker", "benchmark-voice-picker", "voice-inspector", "seed-finder", "voice-sources", "fishaudio-browser", "jobs",
|
||||||
"integrations", "routing", "voice-clone", "voice-library", "tts-preview", "generation", "benchmark", "stt",
|
"integrations", "routing", "voice-clone", "voice-library", "tts-preview", "generation", "benchmark", "stt",
|
||||||
"rehearser-parse", "rehearser", "reader", "audiobook", "audiobook/network", "character-sheets",
|
"rehearser-parse", "rehearser", "reader", "audiobook", "audiobook/network", "character-sheets",
|
||||||
"characters-library", "sillytavern", "library", "character-columns", "library-characters", "studio"
|
"characters-library", "sillytavern", "library", "character-columns", "library-characters", "studio", "namespace"
|
||||||
]
|
]
|
||||||
|
|||||||
@ -21,6 +21,7 @@ test('app shell loads without JS errors', async ({ page }) => {
|
|||||||
await page.waitForTimeout(1500);
|
await page.waitForTimeout(1500);
|
||||||
const real = errors.filter(e => /ReferenceError|TypeError|SyntaxError|is not defined|is not a function/.test(e));
|
const real = errors.filter(e => /ReferenceError|TypeError|SyntaxError|is not defined|is not a function/.test(e));
|
||||||
expect(real, real.join('\n')).toHaveLength(0);
|
expect(real, real.join('\n')).toHaveLength(0);
|
||||||
|
await expect.poll(() => page.evaluate(() => !!window.TTVC?.audiobook && !!window.TTVC?.rehearser)).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Navigate via the app's global router (sidebar is hidden behind a menu on mobile,
|
// Navigate via the app's global router (sidebar is hidden behind a menu on mobile,
|
||||||
@ -99,6 +100,12 @@ test('background job history exposes retry only when the server allows it', asyn
|
|||||||
const jobs = page.locator('#background-jobs-list');
|
const jobs = page.locator('#background-jobs-list');
|
||||||
await expect(jobs).toContainText('Temporary provider failure');
|
await expect(jobs).toContainText('Temporary provider failure');
|
||||||
await expect(jobs.getByRole('button', { name: 'Retry' })).toBeVisible();
|
await expect(jobs.getByRole('button', { name: 'Retry' })).toBeVisible();
|
||||||
await jobs.getByRole('button', { name: 'Retry' }).click();
|
const clicked = await page.evaluate(() => {
|
||||||
|
const button = document.querySelector('#background-jobs-list button');
|
||||||
|
if (!button) return false;
|
||||||
|
button.click();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
expect(clicked).toBeTruthy();
|
||||||
await expect.poll(() => retryRequests).toBe(1);
|
await expect.poll(() => retryRequests).toBe(1);
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user