fix: compatible with non-standard modifier keys

This commit is contained in:
wj-xiao
2026-01-26 11:12:19 +08:00
parent 7460ebfd07
commit d5cd706078
3 changed files with 55 additions and 16 deletions

View File

@@ -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 {

View File

@@ -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) {

View File

@@ -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;
}