diff --git a/web/src/lib/browser.ts b/web/src/lib/browser.ts index 8f1f012..6ca04ca 100644 --- a/web/src/lib/browser.ts +++ b/web/src/lib/browser.ts @@ -1,28 +1,33 @@ +export type System = 'Windows' | 'macOS' | 'Linux' | 'Unknown'; + export interface BrowserInfo { - os: string; + os: System; isChrome: boolean; } -export function getOperatingSystem(): string { +export function getOperatingSystem(): System { if (typeof window === 'undefined') { return 'Unknown'; } - const platformSource = + if ('userAgentData' in navigator) { // @ts-expect-error check userAgentData.platform - 'userAgentData' in navigator ? navigator.userAgentData.platform : navigator.platform; - - if (!platformSource) { - return 'Unknown'; + const platform = navigator.userAgentData?.platform?.toLowerCase(); + if (platform) { + if (platform === 'windows') return 'Windows'; + if (platform === 'macos') return 'macOS'; + if (platform === 'linux' || platform === 'android') return 'Linux'; + } } - const platform = platformSource.toLowerCase(); + // Fallback to User Agent + const userAgent = navigator.userAgent; - if (platform.startsWith('win')) return 'Windows'; - if (platform.startsWith('mac')) return 'macOS'; - if (platform.startsWith('linux')) return 'Linux'; + if (/Win/i.test(userAgent)) return 'Windows'; + if (/Mac|iPhone|iPod|iPad/i.test(userAgent)) return 'macOS'; + if (/Linux|Android/i.test(userAgent)) return 'Linux'; - return platformSource; + return 'Unknown'; } export function isChromeBrowser(): boolean { diff --git a/web/src/pages/desktop/keyboard/index.tsx b/web/src/pages/desktop/keyboard/index.tsx index 6514233..8533f3d 100644 --- a/web/src/pages/desktop/keyboard/index.tsx +++ b/web/src/pages/desktop/keyboard/index.tsx @@ -7,6 +7,8 @@ import { isModifier } from '@/lib/keymap'; import { client, MessageEvent } from '@/lib/websocket.ts'; import { isKeyboardEnableAtom } from '@/jotai/keyboard.ts'; +import { normalizeKeyCode } from './utils.ts'; + interface AltGrState { active: boolean; ctrlLeftTimestamp: number; @@ -23,7 +25,8 @@ export const Keyboard = () => { const isComposing = useRef(false); useEffect(() => { - if (getOperatingSystem() === 'Windows' && !altGrState.current) { + const os = getOperatingSystem(); + if (os === 'Windows' && !altGrState.current) { altGrState.current = { active: false, ctrlLeftTimestamp: 0 }; } @@ -49,8 +52,10 @@ export const Keyboard = () => { event.preventDefault(); event.stopPropagation(); - const code = event.code; - if (pressedKeys.current.has(code)) return; + const code = normalizeKeyCode(event, os); + if (!code || pressedKeys.current.has(code)) { + return; + } // When AltGr is pressed, browsers send ControlLeft followed immediately by AltRight if (altGrState.current) { @@ -79,7 +84,10 @@ export const Keyboard = () => { event.preventDefault(); event.stopPropagation(); - const code = event.code; + const code = normalizeKeyCode(event, os); + if (!code) { + return; + } // Handle AltGr state for Windows if (altGrState.current?.active) { diff --git a/web/src/pages/desktop/keyboard/utils.ts b/web/src/pages/desktop/keyboard/utils.ts new file mode 100644 index 0000000..0b199fb --- /dev/null +++ b/web/src/pages/desktop/keyboard/utils.ts @@ -0,0 +1,26 @@ +export function normalizeKeyCode(event: KeyboardEvent, os?: string): string { + if (event.code) { + return event.code; + } + + // Fallback: use event.key + event.location to determine the key + // event.location: 1 = left, 2 = right, 0 = standard (non-positional) + if (event.key === 'Shift') { + if (event.location === 0 && os === 'Windows') { + return 'ShiftRight'; + } + return event.location === 2 ? 'ShiftRight' : 'ShiftLeft'; + } + + if (event.key === 'Control') { + return event.location === 2 ? 'ControlRight' : 'ControlLeft'; + } + if (event.key === 'Alt') { + return event.location === 2 ? 'AltRight' : 'AltLeft'; + } + if (event.key === 'Meta') { + return event.location === 2 ? 'MetaRight' : 'MetaLeft'; + } + + return event.code; +}