mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 00:57:45 +00:00
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:
co-authored by
Claude Opus 5.5
İzzetEmre
Jinwoo-H
parent
bad74e1b7c
commit
8c3775f8d6
@@ -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)
|
||||
})
|
||||
})
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user