Restore multi-copy digit capture and add AniList selection (#56)

This commit is contained in:
2026-04-25 21:44:55 -07:00
committed by GitHub
parent 7ac51cd5e9
commit d8934647a9
140 changed files with 4097 additions and 326 deletions
+49
View File
@@ -330,6 +330,7 @@ function installKeyboardTestGlobals() {
function createKeyboardHandlerHarness() {
const testGlobals = installKeyboardTestGlobals();
const subtitleRootClassList = createClassList();
const subtitleContainerClassList = createClassList();
let controllerSelectKeydownCount = 0;
let openControllerSelectCount = 0;
let openControllerDebugCount = 0;
@@ -349,6 +350,7 @@ function createKeyboardHandlerHarness() {
querySelectorAll: () => wordNodes,
},
subtitleContainer: {
classList: subtitleContainerClassList,
contains: () => false,
},
overlay: testGlobals.overlay,
@@ -365,6 +367,7 @@ function createKeyboardHandlerHarness() {
const handlers = createKeyboardHandlers(ctx as never, {
handleRuntimeOptionsKeydown: () => false,
handleCharacterDictionaryKeydown: () => false,
handleSubsyncKeydown: () => false,
handleKikuKeydown: () => false,
handleJimakuKeydown: () => false,
@@ -404,6 +407,26 @@ function createKeyboardHandlerHarness() {
};
}
test('primary subtitle visibility key hides and restores the subtitle bar without mpv sub-visibility', async () => {
const { ctx, handlers, testGlobals } = createKeyboardHandlerHarness();
try {
await handlers.setupMpvInputForwarding();
testGlobals.dispatchKeydown({ key: 'v', code: 'KeyV' });
assert.equal(ctx.dom.subtitleContainer.classList.contains('primary-sub-hidden'), true);
testGlobals.dispatchKeydown({ key: 'v', code: 'KeyV' });
assert.equal(ctx.dom.subtitleContainer.classList.contains('primary-sub-hidden'), false);
assert.equal(
testGlobals.mpvCommands.some((command) => command.includes('sub-visibility')),
false,
);
} finally {
testGlobals.restore();
}
});
test('session help chord resolver follows remapped session bindings', async () => {
const { handlers, testGlobals } = createKeyboardHandlerHarness();
@@ -1119,6 +1142,32 @@ test('session binding: Ctrl+Shift+O dispatches runtime options locally', async (
}
});
test('session binding: copy subtitle multiple captures follow-up digit locally', async () => {
const { handlers, testGlobals } = createKeyboardHandlerHarness();
try {
await handlers.setupMpvInputForwarding();
handlers.updateSessionBindings([
{
sourcePath: 'shortcuts.copySubtitleMultiple',
originalKey: 'Ctrl+M',
key: { code: 'KeyM', modifiers: ['ctrl'] },
actionType: 'session-action',
actionId: 'copySubtitleMultiple',
},
] as never);
testGlobals.dispatchKeydown({ key: 'm', code: 'KeyM', ctrlKey: true });
testGlobals.dispatchKeydown({ key: '3', code: 'Digit3' });
assert.deepEqual(testGlobals.sessionActions, [
{ actionId: 'copySubtitleMultiple', payload: { count: 3 } },
]);
} finally {
testGlobals.restore();
}
});
test('keyboard mode: h moves left when popup is closed', async () => {
const { ctx, handlers, testGlobals } = createKeyboardHandlerHarness();
+26
View File
@@ -12,6 +12,7 @@ export function createKeyboardHandlers(
ctx: RendererContext,
options: {
handleRuntimeOptionsKeydown: (e: KeyboardEvent) => boolean;
handleCharacterDictionaryKeydown: (e: KeyboardEvent) => boolean;
handleSubsyncKeydown: (e: KeyboardEvent) => boolean;
handleKikuKeydown: (e: KeyboardEvent) => boolean;
handleJimakuKeydown: (e: KeyboardEvent) => boolean;
@@ -360,6 +361,20 @@ export function createKeyboardHandlers(
);
}
function isPrimarySubtitleVisibilityToggle(e: KeyboardEvent): boolean {
return e.code === 'KeyV' && !e.ctrlKey && !e.altKey && !e.metaKey && !e.shiftKey && !e.repeat;
}
function togglePrimarySubtitleBarVisibility(): void {
const visible = !ctx.state.primarySubtitleBarVisible;
ctx.state.primarySubtitleBarVisible = visible;
if (visible) {
ctx.dom.subtitleContainer.classList.remove('primary-sub-hidden');
} else {
ctx.dom.subtitleContainer.classList.add('primary-sub-hidden');
}
}
async function handleMarkWatched(): Promise<void> {
const marked = await window.electronAPI.markActiveVideoWatched();
if (marked) {
@@ -1004,6 +1019,10 @@ export function createKeyboardHandlers(
options.handleRuntimeOptionsKeydown(e);
return;
}
if (ctx.state.characterDictionaryModalOpen) {
options.handleCharacterDictionaryKeydown(e);
return;
}
if (ctx.state.subsyncModalOpen) {
options.handleSubsyncKeydown(e);
return;
@@ -1060,6 +1079,12 @@ export function createKeyboardHandlers(
return;
}
if (isPrimarySubtitleVisibilityToggle(e)) {
e.preventDefault();
togglePrimarySubtitleBarVisibility();
return;
}
if (ctx.state.yomitanPopupVisible || isYomitanPopupVisible(document)) {
if (handleYomitanPopupKeybind(e)) {
e.preventDefault();
@@ -1147,6 +1172,7 @@ export function createKeyboardHandlers(
updateSessionBindings,
syncKeyboardTokenSelection,
handleSubtitleContentUpdated,
togglePrimarySubtitleBarVisibility,
handleKeyboardModeToggleRequested,
handleLookupWindowToggleRequested,
closeLookupWindow,