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 { createSessionHelpModal } from './modals/session-help.js';
|
||||
import { createSubtitleSidebarModal } from './modals/subtitle-sidebar.js';
|
||||
import { isControllerInteractionBlocked } from './controller-interaction-blocking.js';
|
||||
import { createRuntimeOptionsModal } from './modals/runtime-options.js';
|
||||
import { createSubsyncModal } from './modals/subsync.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 {
|
||||
const suppressSubtitles = isAnySettingsModalOpen();
|
||||
document.body.classList.toggle('settings-modal-open', suppressSubtitles);
|
||||
@@ -323,7 +328,7 @@ function startControllerPolling(): void {
|
||||
},
|
||||
getKeyboardModeEnabled: () => ctx.state.keyboardDrivenModeEnabled,
|
||||
getLookupWindowOpen: () => ctx.state.yomitanPopupVisible || isYomitanPopupVisible(document),
|
||||
getInteractionBlocked: () => isAnyModalOpen(),
|
||||
getInteractionBlocked: () => isControllerInputBlocked(),
|
||||
toggleKeyboardMode: () => keyboardHandlers.handleKeyboardModeToggleRequested(),
|
||||
toggleLookup: () => keyboardHandlers.handleLookupWindowToggleRequested(),
|
||||
closeLookup: () => {
|
||||
|
||||
Reference in New Issue
Block a user