mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-03-24 12:11:29 -07:00
fix(renderer): keep controller input active with sidebar open
This commit is contained in:
@@ -0,0 +1,57 @@
|
|||||||
|
---
|
||||||
|
id: TASK-231
|
||||||
|
title: Restore controller input while subtitle sidebar is open
|
||||||
|
status: Done
|
||||||
|
assignee:
|
||||||
|
- '@codex'
|
||||||
|
created_date: '2026-03-24 00:15'
|
||||||
|
updated_date: '2026-03-24 00:15'
|
||||||
|
labels:
|
||||||
|
- bug
|
||||||
|
- controller
|
||||||
|
- subtitle-sidebar
|
||||||
|
- overlay
|
||||||
|
dependencies: []
|
||||||
|
references:
|
||||||
|
- /home/sudacode/projects/japanese/SubMiner/src/renderer/renderer.ts
|
||||||
|
- /home/sudacode/projects/japanese/SubMiner/src/renderer/controller-interaction-blocking.ts
|
||||||
|
- /home/sudacode/projects/japanese/SubMiner/src/renderer/controller-interaction-blocking.test.ts
|
||||||
|
priority: high
|
||||||
|
ordinal: 54900
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||||
|
|
||||||
|
When keyboard-only mode is active, opening the subtitle sidebar should not disable controller navigation and lookup/mining controls. Restore controller input while the sidebar is open, while keeping true modal dialogs blocking controller actions.
|
||||||
|
|
||||||
|
<!-- SECTION:DESCRIPTION:END -->
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
<!-- AC:BEGIN -->
|
||||||
|
|
||||||
|
- [x] #1 Opening the subtitle sidebar does not block controller input for keyboard-only mode actions.
|
||||||
|
- [x] #2 Controller-select/debug and other true modal dialogs still block controller actions while open.
|
||||||
|
- [x] #3 Focused regression coverage exists for the sidebar-open controller gating rule.
|
||||||
|
|
||||||
|
<!-- AC:END -->
|
||||||
|
|
||||||
|
## Implementation Notes
|
||||||
|
|
||||||
|
<!-- SECTION:NOTES:BEGIN -->
|
||||||
|
|
||||||
|
Root cause: renderer gamepad polling used the broad `isAnyModalOpen()` check as its interaction gate, and that list includes `subtitleSidebarModalOpen`. The subtitle sidebar is non-modal for controller usage, so gamepad input was being suppressed whenever the sidebar was visible.
|
||||||
|
|
||||||
|
Fixed by extracting a dedicated controller-interaction blocking helper that excludes the subtitle sidebar but keeps the existing blocking behavior for true modal dialogs.
|
||||||
|
|
||||||
|
<!-- SECTION:NOTES:END -->
|
||||||
|
|
||||||
|
## Final Summary
|
||||||
|
|
||||||
|
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||||
|
|
||||||
|
Restored controller input while the subtitle sidebar is open by switching gamepad polling to a dedicated modal-blocking rule that leaves the sidebar controller-passive. Added a regression test covering the sidebar-open exception and preserving hard blocks for actual modal dialogs.
|
||||||
|
|
||||||
|
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||||
36
src/renderer/controller-interaction-blocking.test.ts
Normal file
36
src/renderer/controller-interaction-blocking.test.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import test from 'node:test';
|
||||||
|
|
||||||
|
import { isControllerInteractionBlocked } from './controller-interaction-blocking.js';
|
||||||
|
|
||||||
|
test('subtitle sidebar stays controller-passive while other modals block controller input', () => {
|
||||||
|
assert.equal(
|
||||||
|
isControllerInteractionBlocked({
|
||||||
|
controllerSelectModalOpen: false,
|
||||||
|
controllerDebugModalOpen: false,
|
||||||
|
jimakuModalOpen: false,
|
||||||
|
kikuModalOpen: false,
|
||||||
|
runtimeOptionsModalOpen: false,
|
||||||
|
subsyncModalOpen: false,
|
||||||
|
youtubePickerModalOpen: false,
|
||||||
|
sessionHelpModalOpen: false,
|
||||||
|
subtitleSidebarModalOpen: true,
|
||||||
|
}),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
isControllerInteractionBlocked({
|
||||||
|
controllerSelectModalOpen: false,
|
||||||
|
controllerDebugModalOpen: false,
|
||||||
|
jimakuModalOpen: false,
|
||||||
|
kikuModalOpen: false,
|
||||||
|
runtimeOptionsModalOpen: true,
|
||||||
|
subsyncModalOpen: false,
|
||||||
|
youtubePickerModalOpen: false,
|
||||||
|
sessionHelpModalOpen: false,
|
||||||
|
subtitleSidebarModalOpen: false,
|
||||||
|
}),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
});
|
||||||
24
src/renderer/controller-interaction-blocking.ts
Normal file
24
src/renderer/controller-interaction-blocking.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
type ControllerInteractionModalState = {
|
||||||
|
controllerSelectModalOpen: boolean;
|
||||||
|
controllerDebugModalOpen: boolean;
|
||||||
|
jimakuModalOpen: boolean;
|
||||||
|
kikuModalOpen: boolean;
|
||||||
|
runtimeOptionsModalOpen: boolean;
|
||||||
|
subsyncModalOpen: boolean;
|
||||||
|
youtubePickerModalOpen: boolean;
|
||||||
|
sessionHelpModalOpen: boolean;
|
||||||
|
subtitleSidebarModalOpen: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function isControllerInteractionBlocked(state: ControllerInteractionModalState): boolean {
|
||||||
|
return (
|
||||||
|
state.controllerSelectModalOpen ||
|
||||||
|
state.controllerDebugModalOpen ||
|
||||||
|
state.jimakuModalOpen ||
|
||||||
|
state.kikuModalOpen ||
|
||||||
|
state.runtimeOptionsModalOpen ||
|
||||||
|
state.subsyncModalOpen ||
|
||||||
|
state.youtubePickerModalOpen ||
|
||||||
|
state.sessionHelpModalOpen
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -35,6 +35,7 @@ import { createJimakuModal } from './modals/jimaku.js';
|
|||||||
import { createKikuModal } from './modals/kiku.js';
|
import { createKikuModal } from './modals/kiku.js';
|
||||||
import { createSessionHelpModal } from './modals/session-help.js';
|
import { createSessionHelpModal } from './modals/session-help.js';
|
||||||
import { createSubtitleSidebarModal } from './modals/subtitle-sidebar.js';
|
import { createSubtitleSidebarModal } from './modals/subtitle-sidebar.js';
|
||||||
|
import { isControllerInteractionBlocked } from './controller-interaction-blocking.js';
|
||||||
import { createRuntimeOptionsModal } from './modals/runtime-options.js';
|
import { createRuntimeOptionsModal } from './modals/runtime-options.js';
|
||||||
import { createSubsyncModal } from './modals/subsync.js';
|
import { createSubsyncModal } from './modals/subsync.js';
|
||||||
import { createYoutubeTrackPickerModal } from './modals/youtube-track-picker.js';
|
import { createYoutubeTrackPickerModal } from './modals/youtube-track-picker.js';
|
||||||
@@ -88,6 +89,10 @@ function isAnyModalOpen(): boolean {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isControllerInputBlocked(): boolean {
|
||||||
|
return isControllerInteractionBlocked(ctx.state);
|
||||||
|
}
|
||||||
|
|
||||||
function syncSettingsModalSubtitleSuppression(): void {
|
function syncSettingsModalSubtitleSuppression(): void {
|
||||||
const suppressSubtitles = isAnySettingsModalOpen();
|
const suppressSubtitles = isAnySettingsModalOpen();
|
||||||
document.body.classList.toggle('settings-modal-open', suppressSubtitles);
|
document.body.classList.toggle('settings-modal-open', suppressSubtitles);
|
||||||
@@ -323,7 +328,7 @@ function startControllerPolling(): void {
|
|||||||
},
|
},
|
||||||
getKeyboardModeEnabled: () => ctx.state.keyboardDrivenModeEnabled,
|
getKeyboardModeEnabled: () => ctx.state.keyboardDrivenModeEnabled,
|
||||||
getLookupWindowOpen: () => ctx.state.yomitanPopupVisible || isYomitanPopupVisible(document),
|
getLookupWindowOpen: () => ctx.state.yomitanPopupVisible || isYomitanPopupVisible(document),
|
||||||
getInteractionBlocked: () => isAnyModalOpen(),
|
getInteractionBlocked: () => isControllerInputBlocked(),
|
||||||
toggleKeyboardMode: () => keyboardHandlers.handleKeyboardModeToggleRequested(),
|
toggleKeyboardMode: () => keyboardHandlers.handleKeyboardModeToggleRequested(),
|
||||||
toggleLookup: () => keyboardHandlers.handleLookupWindowToggleRequested(),
|
toggleLookup: () => keyboardHandlers.handleLookupWindowToggleRequested(),
|
||||||
closeLookup: () => {
|
closeLookup: () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user