fix(shortcuts): Cmd+C no longer pastes on remapped Latin keyboard layouts (#29587)

* fix(shortcuts): Cmd+C no longer pastes on remapped Latin keyboard layouts

On macOS the main-process paste intercept matched `code === 'KeyV'` OR
`key === 'v'`, so on Turkish F (where the C letter sits on the physical
KeyV) Cmd+C was routed to ui:appMenuPaste. Trust the produced key when it
is a Latin letter or digit and only fall back to the physical code for
non-Latin layouts (Cyrillic etc.), matching the shared keybinding matcher.

Fixes #29574

Claude-Session: https://claude.ai/code/session_01TYbS9P1pnW4WRN7bGpuNaW

* test(shortcuts): cover Dvorak, Korean and IME composition for the macOS paste intercept

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(shortcuts): match the macOS paste intercept by produced key token, in its own module

The produced key token wins on every Latin layout, punctuation included (BEPO puts . on KeyV); non-Latin and IME keys fall back to the physical key.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: İzzetEmre <emreizzet@hotmail.com>
Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Incursor Teknoloji
2026-10-18 18:13:55 -04:00
committed by GitHub
co-authored by Claude Opus 5.5 İzzetEmre Jinwoo-H
parent bad74e1b7c
commit 8c3775f8d6
4 changed files with 65 additions and 13 deletions
@@ -0,0 +1,50 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { isMacAppPasteInput } from './mac-app-paste-input'
function cmdKey(key: string, code: string, extra: Partial<Electron.Input> = {}): Electron.Input {
return {
type: 'keyDown',
key,
code,
isAutoRepeat: false,
isComposing: false,
meta: true,
control: false,
alt: false,
shift: false,
location: 0,
modifiers: ['meta'],
...extra
}
}
describe('isMacAppPasteInput', () => {
beforeEach(() => {
vi.spyOn(process, 'platform', 'get').mockReturnValue('darwin')
})
afterEach(() => {
vi.restoreAllMocks()
})
it.each([
['US Cmd+V', cmdKey('v', 'KeyV'), true],
['Caps Lock Cmd+V', cmdKey('V', 'KeyV'), true],
['Turkish F Cmd+V (V lives on KeyC)', cmdKey('v', 'KeyC'), true],
['Turkish F Cmd+C (C lives on KeyV)', cmdKey('c', 'KeyV'), false],
['Dvorak Cmd+V (V lives on Period)', cmdKey('v', 'Period'), true],
['Dvorak Cmd+K (K lives on KeyV)', cmdKey('k', 'KeyV'), false],
['BÉPO Cmd+. (. lives on KeyV)', cmdKey('.', 'KeyV'), false],
['Cyrillic Cmd+V (non-Latin key on KeyV)', cmdKey('м', 'KeyV'), true],
['Korean Cmd+V (Hangul jamo on KeyV)', cmdKey('ㅍ', 'KeyV'), true],
['Cmd+V during IME composition', cmdKey('Process', 'KeyV'), true],
['Cmd+Shift+V', cmdKey('V', 'KeyV', { shift: true }), false]
])('%s', (_name, input, expected) => {
expect(isMacAppPasteInput(input)).toBe(expected)
})
it('never claims paste off macOS', () => {
vi.spyOn(process, 'platform', 'get').mockReturnValue('linux')
expect(isMacAppPasteInput(cmdKey('v', 'KeyV'))).toBe(false)
})
})
+14
View File
@@ -0,0 +1,14 @@
import { logicalKeyTokenFromInput, physicalCodeKeyTokenFromInput } from '../../shared/keybindings'
export function isMacAppPasteInput(input: Electron.Input): boolean {
return (
process.platform === 'darwin' &&
input.type === 'keyDown' &&
input.meta &&
!input.control &&
!input.alt &&
!input.shift &&
// Why: the produced key wins so remapped layouts (Turkish F puts C on KeyV) don't paste; non-Latin and IME keys fall back to the physical key.
(logicalKeyTokenFromInput(input) ?? physicalCodeKeyTokenFromInput(input)) === 'V'
)
}
@@ -20,7 +20,7 @@ import type { Store } from '../persistence'
import type { CreateMainWindowOptions } from './main-window-contracts'
import type { MainWindowFocusLifecycle } from './main-window-focus-lifecycle'
import { sendResolvedWindowShortcutAction } from './main-window-shortcut-actions'
import { isMacAppPasteInput } from './main-window-visual-lifecycle'
import { isMacAppPasteInput } from './mac-app-paste-input'
export function installMainWindowShortcutRouting(args: {
focus: MainWindowFocusLifecycle
@@ -95,18 +95,6 @@ export function installMacosVisibilityRepaint(window: BrowserWindow): void {
})
}
export function isMacAppPasteInput(input: Electron.Input): boolean {
return (
process.platform === 'darwin' &&
input.type === 'keyDown' &&
input.meta &&
!input.control &&
!input.alt &&
!input.shift &&
(input.code === 'KeyV' || input.key.toLowerCase() === 'v')
)
}
// Why: titlebar content center sits ~18 CSS px from top (×zoom); traffic lights are ~12px tall, so top edge = center − 6.
export const TITLEBAR_CSS_CENTER = 18
export const TRAFFIC_LIGHT_RADIUS = 6