From 14cd37d8d7f02b6e553a17918202d7e7725566b5 Mon Sep 17 00:00:00 2001 From: sudacode Date: Sun, 7 Jun 2026 23:18:00 -0700 Subject: [PATCH] fix(update): separate in-flight dedup keys for manual check vs install - manual:check and manual:install now tracked independently; install no longer reuses a check's in-flight promise - add toggleNotificationHistory (Ctrl+N) shortcut to config example and docs --- docs-site/public/config.example.jsonc | 3 ++- docs-site/shortcuts.md | 1 + .../runtime/update/update-service.test.ts | 22 +++++++++++++++++++ src/main/runtime/update/update-service.ts | 16 ++++++++++---- 4 files changed, 37 insertions(+), 5 deletions(-) diff --git a/docs-site/public/config.example.jsonc b/docs-site/public/config.example.jsonc index d17fcc2f..7f2a182d 100644 --- a/docs-site/public/config.example.jsonc +++ b/docs-site/public/config.example.jsonc @@ -208,7 +208,8 @@ "openSessionHelp": "CommandOrControl+Slash", // Accelerator that opens the session help / keybinding cheatsheet. "openControllerSelect": "Alt+C", // Accelerator that opens the controller selection and learn-mode modal. "openControllerDebug": "Alt+Shift+C", // Accelerator that opens the controller debug modal with live axis/button readouts. - "toggleSubtitleSidebar": "Backslash" // Accelerator that toggles the subtitle sidebar visibility. + "toggleSubtitleSidebar": "Backslash", // Accelerator that toggles the subtitle sidebar visibility. + "toggleNotificationHistory": "Ctrl+N" // Accelerator that toggles the overlay notification history panel. }, // Overlay keyboard shortcuts. Set a shortcut to null to disable. // ========================================== diff --git a/docs-site/shortcuts.md b/docs-site/shortcuts.md index 0439123c..626cb56a 100644 --- a/docs-site/shortcuts.md +++ b/docs-site/shortcuts.md @@ -82,6 +82,7 @@ Mouse-hover playback behavior is configured separately from shortcuts: `subtitle | `Ctrl/Cmd+Shift+O` | Open runtime options palette | `shortcuts.openRuntimeOptions` | | `Ctrl/Cmd+/` | Open session help modal | `shortcuts.openSessionHelp` | | `Ctrl+Shift+J` | Open Jimaku subtitle search modal | `shortcuts.openJimaku` | +| `Ctrl+N` | Toggle overlay notification history panel | `shortcuts.toggleNotificationHistory` | | `Ctrl+Alt+C` | Open the manual YouTube subtitle picker | `keybindings` | | `Ctrl+Alt+S` | Open subtitle sync (subsync) modal | `shortcuts.triggerSubsync` | | `\` | Toggle subtitle sidebar | `subtitleSidebar.toggleKey` | diff --git a/src/main/runtime/update/update-service.test.ts b/src/main/runtime/update/update-service.test.ts index 368ce69c..370a9308 100644 --- a/src/main/runtime/update/update-service.test.ts +++ b/src/main/runtime/update/update-service.test.ts @@ -293,6 +293,28 @@ test('concurrent update checks share one in-flight check', async () => { assert.equal(checkCount, 1); }); +test('manual install request does not reuse in-flight manual check', async () => { + let checkCount = 0; + const resolveChecks: Array<(value: { available: boolean; version: string }) => void> = []; + const { deps } = createDeps({ + checkAppUpdate: () => + new Promise((resolve) => { + checkCount += 1; + resolveChecks.push(resolve); + }), + }); + const service = createUpdateService(deps); + const manualCheck = service.checkForUpdates({ source: 'manual' }); + const manualInstall = service.checkForUpdates({ source: 'manual', installWhenAvailable: true }); + + await Promise.resolve(); + assert.equal(checkCount, 2); + for (const resolve of resolveChecks) { + resolve({ available: false, version: '0.14.0' }); + } + await Promise.all([manualCheck, manualInstall]); +}); + test('manual update check does not reuse in-flight automatic check', async () => { let checkCount = 0; const resolveChecks: Array<(value: { available: boolean; version: string }) => void> = []; diff --git a/src/main/runtime/update/update-service.ts b/src/main/runtime/update/update-service.ts index 035078e2..c9d0c24f 100644 --- a/src/main/runtime/update/update-service.ts +++ b/src/main/runtime/update/update-service.ts @@ -108,7 +108,14 @@ function summarizeError(error: unknown): string { } export function createUpdateService(deps: UpdateServiceDeps) { - const inFlightBySource = new Map>(); + const inFlightBySource = new Map>(); + + function getInFlightKey(request: UpdateCheckRequest): string { + if (request.source === 'manual') { + return request.installWhenAvailable ? 'manual:install' : 'manual:check'; + } + return request.source; + } async function runCheck(request: UpdateCheckRequest): Promise { const now = deps.now(); @@ -206,12 +213,13 @@ export function createUpdateService(deps: UpdateServiceDeps) { return { checkForUpdates(request: UpdateCheckRequest): Promise { - const inFlight = inFlightBySource.get(request.source); + const key = getInFlightKey(request); + const inFlight = inFlightBySource.get(key); if (inFlight) return inFlight; const nextInFlight = runCheck(request).finally(() => { - inFlightBySource.delete(request.source); + inFlightBySource.delete(key); }); - inFlightBySource.set(request.source, nextInFlight); + inFlightBySource.set(key, nextInFlight); return nextInFlight; }, startAutomaticChecks(options: { startupDelayMs?: number; pollIntervalMs?: number } = {}): void {