diff --git a/web/src/keys.test.ts b/web/src/keys.test.ts index ef5f450..14234f5 100644 --- a/web/src/keys.test.ts +++ b/web/src/keys.test.ts @@ -1,5 +1,5 @@ // @vitest-environment happy-dom -import { describe, expect, it } from 'vitest' +import { describe, expect, it, vi } from 'vitest' import { matchKeys, recordedKeys } from './keys' describe('快捷键匹配与录入', () => { @@ -13,4 +13,20 @@ describe('快捷键匹配与录入', () => { expect(recordedKeys(new KeyboardEvent('keydown', { key: 'Control', ctrlKey: true }))).toBeNull() expect(recordedKeys(new KeyboardEvent('keydown', { key: '+', ctrlKey: true, shiftKey: true }))).toContain('Plus') }) + it.each(['MacIntel', 'Win32'])('%s 录入双修饰键后可跨平台匹配,已有 Mod 组合也不会抛错', async (platform) => { + vi.stubGlobal('navigator', { platform }) + vi.resetModules() + try { + const keys = await import('./keys') + const event = new KeyboardEvent('keydown', { key: 'b', code: 'KeyB', ctrlKey: true, metaKey: true }) + expect(keys.recordedKeys(event)).toBe('Ctrl+Meta+B') + expect(keys.matchKeys(event, 'Ctrl+Meta+B')).toBe(true) + const alias = platform === 'MacIntel' ? 'Mod+Meta+B' : 'Mod+Ctrl+B' + expect(keys.physicalKeys(alias)).toBe(platform === 'MacIntel' ? 'Meta+B' : 'Ctrl+B') + expect(keys.matchKeys(new KeyboardEvent('keydown', { key: 'F1' }), alias)).toBe(false) + } finally { + vi.unstubAllGlobals() + vi.resetModules() + } + }) }) diff --git a/web/src/keys.ts b/web/src/keys.ts index e024237..91e6add 100644 --- a/web/src/keys.ts +++ b/web/src/keys.ts @@ -10,7 +10,8 @@ export function normalizeKeys(value: string): string { } export function physicalKeys(value: string): string { - return normalizeKeys(value.replace(/\bMod\b/g, isMac ? 'Meta' : 'Ctrl')) + const parts = normalizeKeys(value).split('+').map((part) => part === 'Mod' ? isMac ? 'Meta' : 'Ctrl' : part) + return normalizeKeys([...new Set(parts)].join('+')) } export function matchKeys(event: KeyboardEvent, value: string): boolean { @@ -26,8 +27,9 @@ export function matchKeys(event: KeyboardEvent, value: string): boolean { export function recordedKeys(event: KeyboardEvent): string | null { if (['Control', 'Meta', 'Alt', 'Shift', 'AltGraph', 'Dead', 'Process', 'Unidentified'].includes(event.key) || event.isComposing) return null const parts: string[] = [] - if (event.ctrlKey) parts.push(isMac ? 'Ctrl' : 'Mod') - if (event.metaKey) parts.push(isMac ? 'Mod' : 'Meta') + // 同时按下两种修饰键时保留物理组合,避免同步后 Mod 与另一键重合。 + if (event.ctrlKey) parts.push(isMac || event.metaKey ? 'Ctrl' : 'Mod') + if (event.metaKey) parts.push(!isMac || event.ctrlKey ? 'Meta' : 'Mod') if (event.altKey) parts.push('Alt') if (event.shiftKey) parts.push('Shift') const key = /^Digit[0-9]$/.test(event.code) ? event.code.slice(5) : event.key === ' ' ? 'Space' : event.key === '+' ? 'Plus' : event.key