From d9a939dff88e15e74331b2c14e705891e49fccac Mon Sep 17 00:00:00 2001 From: debpalash <4178343+debpalash@users.noreply.github.com> Date: Wed, 12 Aug 2026 02:54:50 +0000 Subject: [PATCH 1/2] fix(desktop): restore dub tracks and identify worker ports --- frontend/src/test/omniUiSchema.test.js | 14 ++++++++++++++ frontend/src/utils/omniUiSchema.js | 4 ++-- frontend/src/utils/remoteBackendProbe.test.ts | 1 + frontend/src/utils/remoteBackendProbe.ts | 2 +- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/frontend/src/test/omniUiSchema.test.js b/frontend/src/test/omniUiSchema.test.js index 45c3bff3..34f40f92 100644 --- a/frontend/src/test/omniUiSchema.test.js +++ b/frontend/src/test/omniUiSchema.test.js @@ -39,7 +39,9 @@ describe('sanitizeOmniUi', () => { language: 'en', isSidebarCollapsed: false, dubSegments: [{ id: '1', text: 'x' }], + dubTracks: ['es', 'fr'], dubStep: 'editing', + exportTracks: { original: true, es: false }, preserveBg: true, speed: 1, steps: 16, @@ -48,6 +50,18 @@ describe('sanitizeOmniUi', () => { expect(sanitizeOmniUi({ ...good })).toEqual(good); }); + it('rejects swapped persisted track shapes without dropping valid track state', () => { + const warn = vi.spyOn(console, 'warn').mockImplementation(() => {}); + expect( + sanitizeOmniUi({ + dubTracks: ['es'], + exportTracks: { original: true, es: false }, + }), + ).toEqual({ dubTracks: ['es'], exportTracks: { original: true, es: false } }); + expect(sanitizeOmniUi({ dubTracks: {}, exportTracks: [] })).toEqual({}); + warn.mockRestore(); + }); + it('schema covers every field the restore path reads', () => { // Lockstep guard: if useAppData reads a saved. not in the schema, // restore silently drops it — fail here instead of in the field. diff --git a/frontend/src/utils/omniUiSchema.js b/frontend/src/utils/omniUiSchema.js index 650bf6b3..4ca5f63e 100644 --- a/frontend/src/utils/omniUiSchema.js +++ b/frontend/src/utils/omniUiSchema.js @@ -39,10 +39,10 @@ export const OMNI_UI_SCHEMA = { dubSegments: isArrOfObj, dubLang: isStr, dubLangCode: isStr, - dubTracks: isObj, + dubTracks: isArr, dubStep: isStr, // additionally clamped by clampRestoredDubStep (#1067) dubTranscript: isStr, - exportTracks: isArr, + exportTracks: isObj, preserveBg: isBool, defaultTrack: isStr, exportHistory: isArr, diff --git a/frontend/src/utils/remoteBackendProbe.test.ts b/frontend/src/utils/remoteBackendProbe.test.ts index 78686333..f8056bd6 100644 --- a/frontend/src/utils/remoteBackendProbe.test.ts +++ b/frontend/src/utils/remoteBackendProbe.test.ts @@ -41,6 +41,7 @@ describe('remote backend probe', () => { ['https://gpu-box:3900', new TypeError('blocked by CORS policy'), 'cors'], ['http://gpu-box:3900', new TypeError('Failed to fetch'), 'network'], ['https://gpu-box:7443', new TypeError('Failed to fetch'), 'wrong_port'], + ['https://gpu-box:7443', new DOMException('aborted', 'AbortError'), 'wrong_port'], ])('classifies %s transport failures as %s', async (url, error, kind) => { const result = await probeRemoteBackend(url, '', { fetchImpl: vi.fn().mockRejectedValue(error), diff --git a/frontend/src/utils/remoteBackendProbe.ts b/frontend/src/utils/remoteBackendProbe.ts index f447aa8f..ffd10de3 100644 --- a/frontend/src/utils/remoteBackendProbe.ts +++ b/frontend/src/utils/remoteBackendProbe.ts @@ -28,8 +28,8 @@ export function disableRemoteBackend(reload: () => void): void { function transportKind(url: URL, error: unknown): RemoteProbeKind { const text = String((error as Error)?.message || error).toLowerCase(); - if ((error as Error)?.name === 'AbortError') return 'timeout'; if (url.port === '7443') return 'wrong_port'; + if ((error as Error)?.name === 'AbortError') return 'timeout'; if (/certificate|cert_|ssl|tls/.test(text)) return 'tls'; if (/cors|cross-origin/.test(text)) return 'cors'; // Browser fetch deliberately hides DNS, connection, CORS, and TLS details From 20d9cb5f1b4253717c30762166c4e5862feb32ee Mon Sep 17 00:00:00 2001 From: debpalash <4178343+debpalash@users.noreply.github.com> Date: Wed, 12 Aug 2026 11:00:33 +0000 Subject: [PATCH 2/2] docs: record remote recovery fixes --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb91f13e..0a732b75 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -62,6 +62,7 @@ the frozen-backend fallback mirror it for their toolchains. ### Fixed +- Dub language and export selections now restore without false schema warnings, and remote-worker port 7443 is identified instead of reported as a generic timeout. (#1504) - A configured remote backend now bypasses local first-run setup, verifies itself before app requests begin, and shows recovery instead of leaving the desktop stuck on Setup. (#1503) - An idle voice model now actually hands its memory back. The unload emptied the GPU cache a moment before releasing the model, so it freed nothing while reporting success — a GPU machine lending its card sat on 3.6 GB indefinitely. (#1495) - Unloading a model on an NVIDIA GPU now returns the last ~770 MB too. A single 8.5 MB cuBLAS workspace sat inside the model's memory block and kept the whole block reserved, so an idle machine held 1.2 GB instead of 470 MB no matter how often you pressed Flush Memory. (#1495)