This commit is contained in:
Marc Brooks 2025-06-20 14:19:18 -05:00 committed by GitHub
commit 13160f211c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 336 additions and 123 deletions

View File

@ -41,6 +41,12 @@ export default function InfoBar() {
const keyboardLedStateSyncAvailable = useHidStore(state => state.keyboardLedStateSyncAvailable); const keyboardLedStateSyncAvailable = useHidStore(state => state.keyboardLedStateSyncAvailable);
const keyboardLedSync = useSettingsStore(state => state.keyboardLedSync); const keyboardLedSync = useSettingsStore(state => state.keyboardLedSync);
const isShiftActive = useHidStore(state => state.isShiftActive);
const isCtrlActive = useHidStore(state => state.isCtrlActive);
const isAltActive = useHidStore(state => state.isAltActive);
const isMetaActive = useHidStore(state => state.isMetaActive);
const isAltGrActive = useHidStore(state => state.isAltGrActive);
const isTurnServerInUse = useRTCStore(state => state.isTurnServerInUse); const isTurnServerInUse = useRTCStore(state => state.isTurnServerInUse);
const usbState = useHidStore(state => state.usbState); const usbState = useHidStore(state => state.usbState);
@ -135,6 +141,56 @@ export default function InfoBar() {
{keyboardLedSync === "browser" ? "Browser" : "Host"} {keyboardLedSync === "browser" ? "Browser" : "Host"}
</div> </div>
) : null} ) : null}
<div
className={cx(
"shrink-0 p-1 px-1.5 text-xs",
isShiftActive
? "text-black dark:text-white"
: "text-slate-800/20 dark:text-slate-300/20",
)}
>
Shift
</div>
<div
className={cx(
"shrink-0 p-1 px-1.5 text-xs",
isCtrlActive
? "text-black dark:text-white"
: "text-slate-800/20 dark:text-slate-300/20",
)}
>
Ctrl
</div>
<div
className={cx(
"shrink-0 p-1 px-1.5 text-xs",
isAltActive
? "text-black dark:text-white"
: "text-slate-800/20 dark:text-slate-300/20",
)}
>
Alt
</div>
<div
className={cx(
"shrink-0 p-1 px-1.5 text-xs",
isMetaActive
? "text-black dark:text-white"
: "text-slate-800/20 dark:text-slate-300/20",
)}
>
Meta
</div>
<div
className={cx(
"shrink-0 p-1 px-1.5 text-xs",
isAltGrActive
? "text-black dark:text-white"
: "text-slate-800/20 dark:text-slate-300/20",
)}
>
AltGr
</div>
<div <div
className={cx( className={cx(
"shrink-0 p-1 px-1.5 text-xs", "shrink-0 p-1 px-1.5 text-xs",

View File

@ -27,6 +27,7 @@ const AttachIcon = ({ className }: { className?: string }) => {
function KeyboardWrapper() { function KeyboardWrapper() {
const [layoutName, setLayoutName] = useState("default"); const [layoutName, setLayoutName] = useState("default");
const [depressedButtons, setDepressedButtons] = useState("");
const keyboardRef = useRef<HTMLDivElement>(null); const keyboardRef = useRef<HTMLDivElement>(null);
const showAttachedVirtualKeyboard = useUiStore( const showAttachedVirtualKeyboard = useUiStore(
@ -54,6 +55,21 @@ function KeyboardWrapper() {
const setIsCapsLockActive = useHidStore(state => state.setIsCapsLockActive); const setIsCapsLockActive = useHidStore(state => state.setIsCapsLockActive);
const isShiftActive = useHidStore(state => state.isShiftActive);
const setIsShiftActive = useHidStore(state => state.setIsShiftActive);
const isCtrlActive = useHidStore(state => state.isCtrlActive);
const setIsCtrlActive = useHidStore(state => state.setIsCtrlActive);
const isAltActive = useHidStore(state => state.isAltActive);
const setIsAltActive = useHidStore(state => state.setIsAltActive);
const isMetaActive = useHidStore(state => state.isMetaActive);
const setIsMetaActive = useHidStore(state => state.setIsMetaActive);
const isAltGrActive = useHidStore(state => state.isAltGrActive);
const setIsAltGrActive = useHidStore(state => state.setIsAltGrActive);
const startDrag = useCallback((e: MouseEvent | TouchEvent) => { const startDrag = useCallback((e: MouseEvent | TouchEvent) => {
if (!keyboardRef.current) return; if (!keyboardRef.current) return;
if (e instanceof TouchEvent && e.touches.length > 1) return; if (e instanceof TouchEvent && e.touches.length > 1) return;
@ -123,18 +139,29 @@ function KeyboardWrapper() {
}; };
}, [endDrag, onDrag, startDrag]); }, [endDrag, onDrag, startDrag]);
const onKeyDown = useCallback( useEffect(() => {
(key: string) => { // if you have the CapsLock "down", then the shift state is inverted
const isKeyShift = key === "{shift}" || key === "ShiftLeft" || key === "ShiftRight"; const effectiveShift = isCapsLockActive ? false === isShiftActive : isShiftActive;
const isKeyCaps = key === "CapsLock"; setLayoutName(effectiveShift ? "shift" : "default");
const cleanKey = key.replace(/[()]/g, ""); },
const keyHasShiftModifier = key.includes("("); [setLayoutName, isCapsLockActive, isShiftActive]
);
// Handle toggle of layout for shift or caps lock // this causes the buttons to look depressed/clicked depending on the sticky state
const toggleLayout = () => { useEffect(() => {
setLayoutName(prevLayout => (prevLayout === "default" ? "shift" : "default")); let buttons = "None "; // make sure we name at least one (fake) button
}; if (isCapsLockActive) buttons += "CapsLock ";
if (isShiftActive) buttons += "ShiftLeft ShiftRight ";
if (isCtrlActive) buttons += "ControlLeft ControlRight ";
if (isAltActive) buttons += "AltLeft AltRight ";
if (isMetaActive) buttons += "MetaLeft MetaRight ";
setDepressedButtons(buttons.trimEnd());
},
[setDepressedButtons, isCapsLockActive, isShiftActive, isCtrlActive, isAltActive, isMetaActive, isAltGrActive]
);
const onKeyPress = useCallback((key: string) => {
// handle the fake combo keys first
if (key === "CtrlAltDelete") { if (key === "CtrlAltDelete") {
sendKeyboardEvent( sendKeyboardEvent(
[keys["Delete"]], [keys["Delete"]],
@ -164,39 +191,71 @@ function KeyboardWrapper() {
return; return;
} }
if (isKeyShift || isKeyCaps) { // strip away the parens for shifted characters
toggleLayout(); const cleanKey = key.replace(/[()]/g, "");
if (isCapsLockActive) { const passthrough = ["PrintScreen", "SystemRequest", "Pause", "Break", "ScrollLock", "Enter", "Space"].find((value) => value === cleanKey);
if (!isKeyboardLedManagedByHost) {
setIsCapsLockActive(false); if (passthrough) {
} emitkeycode(cleanKey);
sendKeyboardEvent([keys["CapsLock"]], []);
return; return;
} }
}
// Handle caps lock state change // adjust the sticky state of the Shift/Ctrl/Alt/Meta/AltGr
if (isKeyCaps && !isKeyboardLedManagedByHost) { if (key === "CapsLock" && !isKeyboardLedManagedByHost)
setIsCapsLockActive(!isCapsLockActive); setIsCapsLockActive(!isCapsLockActive);
else if (key === "ShiftLeft" || key === "ShiftRight")
setIsShiftActive(!isShiftActive);
else if (key === "ControlLeft" || key === "ControlRight")
setIsCtrlActive(!isCtrlActive);
else if (key === "AltLeft" || key === "AltRight")
setIsAltActive(!isAltActive);
else if (key === "MetaLeft" || key === "MetaRight")
setIsMetaActive(!isMetaActive);
else if (key === "AltGr")
setIsAltGrActive(!isAltGrActive);
emitkeycode(cleanKey);
function emitkeycode(key: string) {
const effectiveMods: number[] = [];
if (isShiftActive)
effectiveMods.push(modifiers["ShiftLeft"]);
if (isCtrlActive)
effectiveMods.push(modifiers["ControlLeft"]);
if (isAltActive)
effectiveMods.push(modifiers["AltLeft"]);
if (isMetaActive)
effectiveMods.push(modifiers["MetaLeft"]);
if (isAltGrActive) {
effectiveMods.push(modifiers["MetaRight"]);
effectiveMods.push(modifiers["CtrlLeft"]);
} }
// Collect new active keys and modifiers const keycode = keys[key];
const newKeys = keys[cleanKey] ? [keys[cleanKey]] : []; if (keycode) {
const newModifiers = // send the keycode with modifiers
keyHasShiftModifier && !isCapsLockActive ? [modifiers["ShiftLeft"]] : []; sendKeyboardEvent([keycode], effectiveMods);
// Update current keys and modifiers
sendKeyboardEvent(newKeys, newModifiers);
// If shift was used as a modifier and caps lock is not active, revert to default layout
if (keyHasShiftModifier && !isCapsLockActive) {
setLayoutName("default");
} }
setTimeout(resetKeyboardState, 100); // release the key (if one pressed), but retain the modifiers
setTimeout(() => sendKeyboardEvent([], effectiveMods), 50);
}
}, },
[isCapsLockActive, isKeyboardLedManagedByHost, sendKeyboardEvent, resetKeyboardState, setIsCapsLockActive], [isKeyboardLedManagedByHost,
setIsCapsLockActive, isCapsLockActive,
setIsShiftActive, isShiftActive,
setIsCtrlActive, isCtrlActive,
setIsAltActive, isAltActive,
setIsMetaActive, isMetaActive,
setIsAltGrActive, isAltGrActive,
sendKeyboardEvent, resetKeyboardState
],
); );
const virtualKeyboard = useHidStore(state => state.isVirtualKeyboardEnabled); const virtualKeyboard = useHidStore(state => state.isVirtualKeyboardEnabled);
@ -276,12 +335,16 @@ function KeyboardWrapper() {
<Keyboard <Keyboard
baseClass="simple-keyboard-main" baseClass="simple-keyboard-main"
layoutName={layoutName} layoutName={layoutName}
onKeyPress={onKeyDown} onKeyPress={onKeyPress}
buttonTheme={[ buttonTheme={[
{ {
class: "combination-key", class: "combination-key",
buttons: "CtrlAltDelete AltMetaEscape CtrlAltBackspace", buttons: "CtrlAltDelete AltMetaEscape CtrlAltBackspace",
}, },
{
class: "depressed-key",
buttons: depressedButtons
},
]} ]}
display={keyDisplayMap} display={keyDisplayMap}
layout={{ layout={{
@ -305,8 +368,6 @@ function KeyboardWrapper() {
], ],
}} }}
disableButtonHold={true} disableButtonHold={true}
syncInstanceInputs={true}
debug={false}
/> />
<div className="controlArrows"> <div className="controlArrows">
@ -314,25 +375,24 @@ function KeyboardWrapper() {
baseClass="simple-keyboard-control" baseClass="simple-keyboard-control"
theme="simple-keyboard hg-theme-default hg-layout-default" theme="simple-keyboard hg-theme-default hg-layout-default"
layoutName={layoutName} layoutName={layoutName}
onKeyPress={onKeyDown} onKeyPress={onKeyPress}
display={keyDisplayMap} display={keyDisplayMap}
layout={{ layout={{
default: ["PrintScreen ScrollLock Pause", "Insert Home Pageup", "Delete End Pagedown"], default: ["PrintScreen ScrollLock Pause", "Insert Home PageUp", "Delete End PageDown"],
shift: ["(PrintScreen) ScrollLock (Pause)", "Insert Home Pageup", "Delete End Pagedown"], shift: ["(PrintScreen) ScrollLock (Pause)", "Insert Home PageUp", "Delete End PageDown"],
}} }}
syncInstanceInputs={true} disableButtonHold={true}
debug={false}
/> />
<Keyboard <Keyboard
baseClass="simple-keyboard-arrows" baseClass="simple-keyboard-arrows"
theme="simple-keyboard hg-theme-default hg-layout-default" theme="simple-keyboard hg-theme-default hg-layout-default"
onKeyPress={onKeyDown} onKeyPress={onKeyPress}
display={keyDisplayMap} display={keyDisplayMap}
layout={{ layout={{
default: ["ArrowUp", "ArrowLeft ArrowDown ArrowRight"], default: ["ArrowUp", "ArrowLeft ArrowDown ArrowRight"],
shift: ["ArrowUp", "ArrowLeft ArrowDown ArrowRight"],
}} }}
syncInstanceInputs={true} disableButtonHold={true}
debug={false}
/> />
</div> </div>
</div> </div>

View File

@ -71,6 +71,15 @@ export default function WebRTCVideo() {
const hdmiError = ["no_lock", "no_signal", "out_of_range"].includes(hdmiState); const hdmiError = ["no_lock", "no_signal", "out_of_range"].includes(hdmiState);
const isVideoLoading = !isPlaying; const isVideoLoading = !isPlaying;
// Keyboard related states
const {
setIsShiftActive,
setIsCtrlActive,
setIsAltActive,
setIsMetaActive,
setIsAltGrActive
} = useHidStore();
const [blockWheelEvent, setBlockWheelEvent] = useState(false); const [blockWheelEvent, setBlockWheelEvent] = useState(false);
// Misc states and hooks // Misc states and hooks
@ -423,6 +432,12 @@ export default function WebRTCVideo() {
setIsScrollLockActive(e.getModifierState("ScrollLock")); setIsScrollLockActive(e.getModifierState("ScrollLock"));
} }
setIsShiftActive(e.getModifierState("Shift"))
setIsCtrlActive(e.getModifierState("Control"))
setIsAltActive(e.getModifierState("Alt"))
setIsMetaActive(e.getModifierState("Meta"))
setIsAltGrActive(e.getModifierState("AltGraph"))
if (code == "IntlBackslash" && ["`", "~"].includes(key)) { if (code == "IntlBackslash" && ["`", "~"].includes(key)) {
code = "Backquote"; code = "Backquote";
} else if (code == "Backquote" && ["§", "±"].includes(key)) { } else if (code == "Backquote" && ["§", "±"].includes(key)) {
@ -452,12 +467,17 @@ export default function WebRTCVideo() {
sendKeyboardEvent([...new Set(newKeys)], [...new Set(newModifiers)]); sendKeyboardEvent([...new Set(newKeys)], [...new Set(newModifiers)]);
}, },
[ [
handleModifierKeys,
sendKeyboardEvent,
isKeyboardLedManagedByHost, isKeyboardLedManagedByHost,
setIsNumLockActive, setIsNumLockActive,
setIsCapsLockActive, setIsCapsLockActive,
setIsScrollLockActive, setIsScrollLockActive,
setIsShiftActive,
setIsCtrlActive,
setIsAltActive,
setIsMetaActive,
setIsAltGrActive,
handleModifierKeys,
sendKeyboardEvent
], ],
); );
@ -472,6 +492,12 @@ export default function WebRTCVideo() {
setIsScrollLockActive(e.getModifierState("ScrollLock")); setIsScrollLockActive(e.getModifierState("ScrollLock"));
} }
setIsShiftActive(e.getModifierState("Shift"))
setIsCtrlActive(e.getModifierState("Control"))
setIsAltActive(e.getModifierState("Alt"))
setIsMetaActive(e.getModifierState("Meta"))
setIsAltGrActive(e.getModifierState("AltGraph"))
// Filtering out the key that was just released (keys[e.code]) // Filtering out the key that was just released (keys[e.code])
const newKeys = prev.activeKeys.filter(k => k !== keys[e.code]).filter(Boolean); const newKeys = prev.activeKeys.filter(k => k !== keys[e.code]).filter(Boolean);
@ -484,12 +510,17 @@ export default function WebRTCVideo() {
sendKeyboardEvent([...new Set(newKeys)], [...new Set(newModifiers)]); sendKeyboardEvent([...new Set(newKeys)], [...new Set(newModifiers)]);
}, },
[ [
handleModifierKeys,
sendKeyboardEvent,
isKeyboardLedManagedByHost, isKeyboardLedManagedByHost,
setIsNumLockActive, setIsNumLockActive,
setIsCapsLockActive, setIsCapsLockActive,
setIsScrollLockActive, setIsScrollLockActive,
setIsShiftActive,
setIsCtrlActive,
setIsAltActive,
setIsMetaActive,
setIsAltGrActive,
handleModifierKeys,
sendKeyboardEvent
], ],
); );

View File

@ -481,6 +481,21 @@ export interface HidState {
isVirtualKeyboardEnabled: boolean; isVirtualKeyboardEnabled: boolean;
setVirtualKeyboardEnabled: (enabled: boolean) => void; setVirtualKeyboardEnabled: (enabled: boolean) => void;
isShiftActive: boolean;
setIsShiftActive: (enabled: boolean) => void;
isCtrlActive: boolean;
setIsCtrlActive: (enabled: boolean) => void;
isAltActive: boolean;
setIsAltActive: (enabled: boolean) => void;
isMetaActive: boolean;
setIsMetaActive: (enabled: boolean) => void;
isAltGrActive: boolean;
setIsAltGrActive: (enabled: boolean) => void;
isPasteModeEnabled: boolean; isPasteModeEnabled: boolean;
setPasteModeEnabled: (enabled: boolean) => void; setPasteModeEnabled: (enabled: boolean) => void;
@ -527,6 +542,21 @@ export const useHidStore = create<HidState>((set, get) => ({
isVirtualKeyboardEnabled: false, isVirtualKeyboardEnabled: false,
setVirtualKeyboardEnabled: enabled => set({ isVirtualKeyboardEnabled: enabled }), setVirtualKeyboardEnabled: enabled => set({ isVirtualKeyboardEnabled: enabled }),
isShiftActive: false,
setIsShiftActive: enabled => set({ isShiftActive: enabled }),
isCtrlActive: false,
setIsCtrlActive: enabled => set({ isCtrlActive: enabled }),
isAltActive: false,
setIsAltActive: enabled => set({ isAltActive: enabled }),
isMetaActive: false,
setIsMetaActive: enabled => set({ isMetaActive: enabled }),
isAltGrActive: false,
setIsAltGrActive: enabled => set({ isAltGrActive: enabled }),
isPasteModeEnabled: false, isPasteModeEnabled: false,
setPasteModeEnabled: enabled => set({ isPasteModeEnabled: enabled }), setPasteModeEnabled: enabled => set({ isPasteModeEnabled: enabled }),

View File

@ -222,6 +222,10 @@ video::-webkit-media-controls {
background: none; background: none;
} }
.simple-keyboard .hg-button.depressed-key {
@apply border-gray-800! border-b-gray-600! border-t-gray-900! bg-gray-700!;
}
.simple-keyboard .hg-button.selectedButton { .simple-keyboard .hg-button.selectedButton {
background: rgba(5, 25, 70, 0.53); background: rgba(5, 25, 70, 0.53);
@apply text-white; @apply text-white;

View File

@ -5,14 +5,14 @@ export const keys = {
ArrowLeft: 0x50, ArrowLeft: 0x50,
ArrowRight: 0x4f, ArrowRight: 0x4f,
ArrowUp: 0x52, ArrowUp: 0x52,
Backquote: 0x35, Backquote: 0x35, // aka Grave
Backslash: 0x31, Backslash: 0x31,
Backspace: 0x2a, Backspace: 0x2a,
BracketLeft: 0x2f, BracketLeft: 0x2f, // aka LeftBrace
BracketRight: 0x30, BracketRight: 0x30, // aka RightBrace
CapsLock: 0x39, CapsLock: 0x39,
Comma: 0x36, Comma: 0x36,
ContextMenu: 0, Compose: 0x65,
Delete: 0x4c, Delete: 0x4c,
Digit0: 0x27, Digit0: 0x27,
Digit1: 0x1e, Digit1: 0x1e,
@ -41,6 +41,18 @@ export const keys = {
F11: 0x44, F11: 0x44,
F12: 0x45, F12: 0x45,
F13: 0x68, F13: 0x68,
F14: 0x69,
F15: 0x6a,
F16: 0x6b,
F17: 0x6c,
F18: 0x6d,
F19: 0x6e,
F20: 0x6f,
F21: 0x70,
F22: 0x71,
F23: 0x72,
F24: 0x73,
HashTilde: 0x32,
Home: 0x4a, Home: 0x4a,
Insert: 0x49, Insert: 0x49,
IntlBackslash: 0x64, IntlBackslash: 0x64,
@ -70,37 +82,58 @@ export const keys = {
KeyX: 0x1b, KeyX: 0x1b,
KeyY: 0x1c, KeyY: 0x1c,
KeyZ: 0x1d, KeyZ: 0x1d,
KeypadComma: 0x85,
KeypadEqual: 0x86,
KeyRO: 0x87,
KatakanaHiragana: 0x88,
Yen: 0x89,
Henkan: 0x8a,
Muhenkan: 0x8b,
KPJPComma: 0x8c,
International7: 0x8d,
International8: 0x8e,
International9: 0x8f,
Hangeul: 0x90,
Hanja: 0x91,
Katakana: 0x92,
Hiragana: 0x93,
Zenkakuhankaku:0x94,
KeypadLeftParen: 0xb6,
KeypadRightParen: 0xb7,
KeypadExclamation: 0xcf, KeypadExclamation: 0xcf,
Minus: 0x2d, Minus: 0x2d,
NumLock: 0x53, None: 0x00,
Numpad0: 0x62, NumLock: 0x53, // and Clear
Numpad1: 0x59, Numpad0: 0x62, // and Insert
Numpad2: 0x5a, Numpad1: 0x59, // and End
Numpad3: 0x5b, Numpad2: 0x5a, // and Down Arrow
Numpad4: 0x5c, Numpad3: 0x5b, // and Page Down
Numpad4: 0x5c, // and Left Arrow
Numpad5: 0x5d, Numpad5: 0x5d,
Numpad6: 0x5e, Numpad6: 0x5e, // and Right Arrow
Numpad7: 0x5f, Numpad7: 0x5f, // and Home
Numpad8: 0x60, Numpad8: 0x60, // and Up Arrow
Numpad9: 0x61, Numpad9: 0x61, // and Page Up
NumpadAdd: 0x57, NumpadPlus: 0x57,
NumpadDivide: 0x54, NumpadSlash: 0x54,
NumpadEnter: 0x58, NumpadEnter: 0x58,
NumpadEqual: 0x67, NumpadEqual: 0x67,
NumpadMultiply: 0x55, NumpadAsterisk: 0x55,
NumpadSubtract: 0x56, NumpadMinus: 0x56,
NumpadDecimal: 0x63, NumpadDecimal: 0x63, // aka NumpadDot and Delete
Overflow: 0x01,
PageDown: 0x4e, PageDown: 0x4e,
PageUp: 0x4b, PageUp: 0x4b,
Period: 0x37, Period: 0x37, // aka Dot
PrintScreen: 0x46, PrintScreen: 0x46,
Pause: 0x48, Pause: 0x48,
Quote: 0x34, Power: 0x66,
Quote: 0x34, // aka Apostrophe
ScrollLock: 0x47, ScrollLock: 0x47,
Semicolon: 0x33, Semicolon: 0x33,
Slash: 0x38, Slash: 0x38,
Space: 0x2c, Space: 0x2c,
SystemRequest: 0x9a, SystemRequest: 0x9a, // aka Attention
Tab: 0x2b, Tab: 0x2b,
} as Record<string, number>; } as Record<string, number>;
@ -131,23 +164,23 @@ export const keyDisplayMap: Record<string, string> = {
AltMetaEscape: "Alt + Meta + Escape", AltMetaEscape: "Alt + Meta + Escape",
CtrlAltBackspace: "Ctrl + Alt + Backspace", CtrlAltBackspace: "Ctrl + Alt + Backspace",
Escape: "esc", Escape: "esc",
Tab: "tab", Tab: "tab ⇥",
Backspace: "backspace", Backspace: "backspace ⌫",
"(Backspace)": "backspace",
Enter: "enter", Enter: "enter",
CapsLock: "caps lock", CapsLock: "caps lock ⇪",
ShiftLeft: "shift", ShiftLeft: "shift ⇧",
ShiftRight: "shift", ShiftRight: "⇧ shift",
ControlLeft: "ctrl", ControlLeft: "ctrl ⌃",
AltLeft: "alt", ControlRight: "⌃ ctrl",
AltRight: "alt", AltLeft: "alt ⌥",
MetaLeft: "meta", AltRight: "⌥ alt",
MetaRight: "meta", MetaLeft: "meta ⌘", // "meta ⊞" for windows
MetaRight: "⌘ meta",// "≣ meta" for windows
Space: " ", Space: " ",
Insert: "insert", Insert: "ins",
Home: "home", Home: "home",
PageUp: "page up", PageUp: "page up",
Delete: "delete", Delete: "del",
End: "end", End: "end",
PageDown: "page down", PageDown: "page down",
ArrowLeft: "←", ArrowLeft: "←",
@ -213,13 +246,12 @@ export const keyDisplayMap: Record<string, string> = {
Numpad0: "Num 0", Numpad1: "Num 1", Numpad2: "Num 2", Numpad0: "Num 0", Numpad1: "Num 1", Numpad2: "Num 2",
Numpad3: "Num 3", Numpad4: "Num 4", Numpad5: "Num 5", Numpad3: "Num 3", Numpad4: "Num 4", Numpad5: "Num 5",
Numpad6: "Num 6", Numpad7: "Num 7", Numpad8: "Num 8", Numpad6: "Num 6", Numpad7: "Num 7", Numpad8: "Num 8",
Numpad9: "Num 9", NumpadAdd: "Num +", NumpadSubtract: "Num -", Numpad9: "Num 9", NumpadPlus: "Num +", NumpadMinus: "Num -",
NumpadMultiply: "Num *", NumpadDivide: "Num /", NumpadDecimal: "Num .", NumpadAsterisk: "Num *", NumpadSlash: "Num /", NumpadDecimal: "Num .",
NumpadEqual: "Num =", NumpadEnter: "Num Enter", NumpadEqual: "Num =", NumpadEnter: "Num Enter",
NumLock: "Num Lock", NumLock: "Num Lock",
// Modals // Modals
PrintScreen: "prt sc", ScrollLock: "scr lk", Pause: "pause", PrintScreen: "prt sc", ScrollLock: "scr lk", Pause: "pause",
"(PrintScreen)": "sys rq", "(Pause)": "break", "(PrintScreen)": "sys rq", "(Pause)": "break"
SystemRequest: "sys rq", Break: "break"
}; };