feat(keybindings): add mouse button support for mpv keybindings (#103)

This commit is contained in:
2026-05-31 22:22:38 -07:00
committed by GitHub
parent e6a004ab8b
commit 487143802a
14 changed files with 281 additions and 5 deletions
@@ -162,6 +162,46 @@ test('compileSessionBindings resolves CommandOrControl in DOM key strings per pl
);
});
test('compileSessionBindings supports mpv mouse button keybindings', () => {
const result = compileSessionBindings({
shortcuts: createShortcuts(),
keybindings: [
createKeybinding('MBTN_BACK', ['sub-seek', -1]),
createKeybinding('Shift+MBTN_FORWARD', ['sub-seek', 1]),
],
platform: 'win32',
});
assert.deepEqual(result.warnings, []);
assert.deepEqual(
result.bindings.map((binding) => ({
code: binding.key.code,
modifiers: binding.key.modifiers,
command: binding.actionType === 'mpv-command' ? binding.command : null,
})),
[
{ code: 'MBTN_BACK', modifiers: [], command: ['sub-seek', -1] },
{ code: 'MBTN_FORWARD', modifiers: ['shift'], command: ['sub-seek', 1] },
],
);
});
test('compileSessionBindings keeps mouse buttons scoped to keybindings', () => {
const result = compileSessionBindings({
shortcuts: createShortcuts({
openJimaku: 'MBTN_BACK',
}),
keybindings: [createKeybinding('MBTN_BACK', ['sub-seek', -1])],
platform: 'win32',
});
assert.deepEqual(result.bindings.map((binding) => binding.sourcePath), ['keybindings[0].key']);
assert.deepEqual(
result.warnings.map((warning) => `${warning.kind}:${warning.path}`),
['unsupported:shortcuts.openJimaku'],
);
});
test('compileSessionBindings drops conflicting bindings that canonicalize to the same key', () => {
const result = compileSessionBindings({
shortcuts: createShortcuts({
+18 -2
View File
@@ -30,6 +30,13 @@ type DraftBinding = {
};
const MODIFIER_ORDER: SessionKeyModifier[] = ['ctrl', 'alt', 'shift', 'meta'];
const MPV_MOUSE_BUTTON_CODES = new Set([
'MBTN_LEFT',
'MBTN_MID',
'MBTN_RIGHT',
'MBTN_BACK',
'MBTN_FORWARD',
]);
const SESSION_SHORTCUT_ACTIONS: Array<{
key: keyof Omit<ConfiguredShortcuts, 'multiCopyTimeoutMs'>;
@@ -64,9 +71,18 @@ function isValidCommandEntry(value: unknown): value is string | number {
return typeof value === 'string' || typeof value === 'number';
}
function normalizeCodeToken(token: string): string | null {
function normalizeCodeToken(
token: string,
options: { allowMouseButtons?: boolean } = {},
): string | null {
const normalized = token.trim();
if (!normalized) return null;
if (options.allowMouseButtons === true) {
const normalizedMouse = normalized.toUpperCase();
if (MPV_MOUSE_BUTTON_CODES.has(normalizedMouse)) {
return normalizedMouse;
}
}
if (/^[a-z]$/i.test(normalized)) {
return `Key${normalized.toUpperCase()}`;
}
@@ -238,7 +254,7 @@ function parseDomKeyString(
};
}
const code = normalizeCodeToken(keyToken);
const code = normalizeCodeToken(keyToken, { allowMouseButtons: true });
if (!code) {
return {
key: null,