From 9150a60630ee8d9e9984913d0a4229b2eb966679 Mon Sep 17 00:00:00 2001 From: sudacode Date: Sat, 24 Oct 2026 23:28:58 -0700 Subject: [PATCH] fix(overlay): forward mpv input while subtitle sidebar is open (#300) --- .../subtitle-sidebar-mpv-key-passthrough.md | 4 ++ src/renderer/handlers/keyboard.test.ts | 38 +++++++++++++++++++ src/renderer/handlers/keyboard.ts | 3 +- 3 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 changes/subtitle-sidebar-mpv-key-passthrough.md diff --git a/changes/subtitle-sidebar-mpv-key-passthrough.md b/changes/subtitle-sidebar-mpv-key-passthrough.md new file mode 100644 index 00000000..ef4f373f --- /dev/null +++ b/changes/subtitle-sidebar-mpv-key-passthrough.md @@ -0,0 +1,4 @@ +type: fixed +area: overlay + +- mpv keys and mouse buttons (such as `9`/`0` volume and double-click fullscreen) pass through the overlay again while the subtitle sidebar is open; only input inside the sidebar stays with it. diff --git a/src/renderer/handlers/keyboard.test.ts b/src/renderer/handlers/keyboard.test.ts index 3c55d917..d23b6b74 100644 --- a/src/renderer/handlers/keyboard.test.ts +++ b/src/renderer/handlers/keyboard.test.ts @@ -481,6 +481,8 @@ function installKeyboardTestGlobals() { setSessionBindings: (value: CompiledSessionBinding[]) => { sessionBindings = value; }, + // A plain overlay element over playback, outside any SubMiner UI. + createPlaybackTarget: () => new TestElement(), createInteractiveTarget: () => { const target = new TestElement(); target.closest = (selector: string) => (selector.includes('.modal') ? target : null); @@ -1297,6 +1299,42 @@ test('Hachidori attention without an open popup pane still forwards imported mpv } }); +test('an open subtitle sidebar only keeps imported mpv input aimed at the sidebar', async () => { + const { ctx, handlers, testGlobals } = createKeyboardHandlerHarness(); + + try { + testGlobals.setGetMpvInputBindings(async () => ({ + keys: ['0', 'MBTN_LEFT_DBL'], + blockedKeys: [], + })); + await handlers.setupMpvInputForwarding(); + await wait(0); + ctx.state.subtitleSidebarModalOpen = true; + const sidebarTarget = testGlobals.createInteractiveTarget(); + const playbackTarget = testGlobals.createPlaybackTarget(); + + // Sidebar input runs first, while no key or button is held, so a held key cannot + // hide a forwarded press. Its release has no matching press to forward. + testGlobals.dispatchKeydown({ key: '0', code: 'Digit0', target: sidebarTarget }); + testGlobals.dispatchMousedown({ button: 0, target: sidebarTarget }); + testGlobals.dispatchMousedown({ button: 0, type: 'mouseup', target: sidebarTarget }); + assert.deepEqual(testGlobals.mpvCommands, []); + + testGlobals.dispatchKeydown({ key: '0', code: 'Digit0', target: playbackTarget }); + testGlobals.dispatchMousedown({ button: 0, target: playbackTarget }); + testGlobals.dispatchMousedown({ button: 0, type: 'mouseup', target: playbackTarget }); + + assert.deepEqual(testGlobals.mpvCommands, [ + ['keydown', '0'], + ['keydown', 'MBTN_LEFT'], + ['keyup', 'MBTN_LEFT'], + ]); + } finally { + ctx.state.subtitleSidebarModalOpen = false; + testGlobals.restore(); + } +}); + test('keyboard mode: lookup toggle opens a lookup during Hachidori attention without a pane', async () => { const { ctx, handlers, testGlobals } = createKeyboardHandlerHarness(); diff --git a/src/renderer/handlers/keyboard.ts b/src/renderer/handlers/keyboard.ts index 262399b4..b1c0f47a 100644 --- a/src/renderer/handlers/keyboard.ts +++ b/src/renderer/handlers/keyboard.ts @@ -123,12 +123,13 @@ export function createKeyboardHandlers( } // Imported mpv bindings only run when no SubMiner UI could be the input's target. + // The subtitle sidebar is a side panel over playback, so only input inside it (a + // `.modal` target) stays with SubMiner. function canForwardToMpv(target: EventTarget | null): boolean { return ( keyboardConfigLoaded && !ctx.state.playlistBrowserModalOpen && !ctx.state.youtubePickerModalOpen && - !ctx.state.subtitleSidebarModalOpen && !isLookupPopupOpen(ctx.state, document) && !isInteractiveTarget(target) );