Compare commits

...

3 Commits

Author SHA1 Message Date
Marc Brooks 681b9c0b6e
Fix compiler error
Using { send } gives the resp a type instead of any
2025-08-20 23:48:40 -05:00
Marc Brooks efb7539537
Fix de-DE chars to reflect German E2 keyboard.
https://kbdlayout.info/kbdgre2/overview+virtualkeys
Added translations for display maps.
2025-08-20 23:45:19 -05:00
Marc Brooks f26862d2e3
Centralized keyboard layout and localized display maps
The Polish programmer keyboard is incomplete WIP.
2025-08-20 21:02:08 -05:00
27 changed files with 1182 additions and 392 deletions

View File

@ -264,7 +264,10 @@ export default function Actionbar({
theme="light"
text="Settings"
LeadingIcon={LuSettings}
onClick={() => navigateTo("/settings")}
onClick={() => {
setDisableVideoFocusTrap(true);
navigateTo("/settings")
}}
/>
</div>

View File

@ -30,7 +30,7 @@ export function JigglerSetting({
},
);
const [send] = useJsonRpc();
const { send } = useJsonRpc();
const [timezones, setTimezones] = useState<string[]>([]);
useEffect(() => {

View File

@ -1,17 +1,18 @@
import { useState } from "react";
import { LuPlus } from "react-icons/lu";
import { KeySequence } from "@/hooks/stores";
import { Button } from "@/components/Button";
import { InputFieldWithLabel, FieldError } from "@/components/InputField";
import FieldLabel from "@/components/FieldLabel";
import Fieldset from "@/components/Fieldset";
import { InputFieldWithLabel, FieldError } from "@/components/InputField";
import { MacroStepCard } from "@/components/MacroStepCard";
import {
DEFAULT_DELAY,
MAX_STEPS_PER_MACRO,
MAX_KEYS_PER_STEP,
} from "@/constants/macros";
import FieldLabel from "@/components/FieldLabel";
import { KeySequence } from "@/hooks/stores";
import { useKeyboardLayout } from "@/hooks/useKeyboardLayout";
interface ValidationErrors {
name?: string;
@ -44,6 +45,7 @@ export function MacroForm({
const [keyQueries, setKeyQueries] = useState<Record<number, string>>({});
const [errors, setErrors] = useState<ValidationErrors>({});
const [errorMessage, setErrorMessage] = useState<string | null>(null);
const { keyboard } = useKeyboardLayout();
const showTemporaryError = (message: string) => {
setErrorMessage(message);
@ -234,6 +236,7 @@ export function MacroForm({
}
onDelayChange={delay => handleDelayChange(stepIndex, delay)}
isLastStep={stepIndex === (macro.steps?.length || 0) - 1}
keyboard={keyboard}
/>
))}
</div>

View File

@ -4,19 +4,22 @@ import { Button } from "@/components/Button";
import { Combobox } from "@/components/Combobox";
import { SelectMenuBasic } from "@/components/SelectMenuBasic";
import Card from "@/components/Card";
import { keys, modifiers, keyDisplayMap } from "@/keyboardMappings";
import { MAX_KEYS_PER_STEP, DEFAULT_DELAY } from "@/constants/macros";
import FieldLabel from "@/components/FieldLabel";
import { MAX_KEYS_PER_STEP, DEFAULT_DELAY } from "@/constants/macros";
import { KeyboardLayout } from "@/keyboardLayouts";
import { keys, modifiers } from "@/keyboardMappings";
// Filter out modifier keys since they're handled in the modifiers section
const modifierKeyPrefixes = ['Alt', 'Control', 'Shift', 'Meta'];
const keyOptions = Object.keys(keys)
const keyOptions = (keyDisplayMap: Record<string, string>) => {
return Object.keys(keys)
.filter(key => !modifierKeyPrefixes.some(prefix => key.startsWith(prefix)))
.map(key => ({
value: key,
label: keyDisplayMap[key] || key,
}));
}
const modifierOptions = Object.keys(modifiers).map(modifier => ({
value: modifier,
@ -67,6 +70,7 @@ interface MacroStepCardProps {
onModifierChange: (modifiers: string[]) => void;
onDelayChange: (delay: number) => void;
isLastStep: boolean;
keyboard: KeyboardLayout
}
const ensureArray = <T,>(arr: T[] | null | undefined): T[] => {
@ -84,11 +88,14 @@ export function MacroStepCard({
keyQuery,
onModifierChange,
onDelayChange,
isLastStep
isLastStep,
keyboard
}: MacroStepCardProps) {
const { keyDisplayMap } = keyboard;
const getFilteredKeys = () => {
const selectedKeys = ensureArray(step.keys);
const availableKeys = keyOptions.filter(option => !selectedKeys.includes(option.value));
const availableKeys = keyOptions(keyDisplayMap).filter(option => !selectedKeys.includes(option.value));
if (keyQuery === '') {
return availableKeys;

View File

@ -1,6 +1,6 @@
import { ChevronDownIcon } from "@heroicons/react/16/solid";
import { AnimatePresence, motion } from "framer-motion";
import { useCallback, useEffect, useRef, useState } from "react";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import Keyboard from "react-simple-keyboard";
import Card from "@components/Card";
@ -14,7 +14,8 @@ import DetachIconRaw from "@/assets/detach-icon.svg";
import { cx } from "@/cva.config";
import { useHidStore, useUiStore } from "@/hooks/stores";
import useKeyboard from "@/hooks/useKeyboard";
import { keyDisplayMap, keys } from "@/keyboardMappings";
import { useKeyboardLayout } from "@/hooks/useKeyboardLayout";
import { keys, modifiers, latchingKeys, decodeModifiers } from "@/keyboardMappings";
export const DetachIcon = ({ className }: { className?: string }) => {
return <img src={DetachIconRaw} alt="Detach Icon" className={className} />;
@ -25,31 +26,40 @@ const AttachIcon = ({ className }: { className?: string }) => {
};
function KeyboardWrapper() {
const [layoutName] = useState("default");
const keyboardRef = useRef<HTMLDivElement>(null);
const { isAttachedVirtualKeyboardVisible, setAttachedVirtualKeyboardVisibility } = useUiStore();
const { keysDownState, isVirtualKeyboardEnabled, setVirtualKeyboardEnabled } = useHidStore();
const { keysDownState, /* keyboardLedState,*/ isVirtualKeyboardEnabled, setVirtualKeyboardEnabled } = useHidStore();
const { handleKeyPress, executeMacro } = useKeyboard();
const [isDragging, setIsDragging] = useState(false);
const [position, setPosition] = useState({ x: 0, y: 0 });
const [newPosition, setNewPosition] = useState({ x: 0, y: 0 });
/*
// These will be used to display the currently pressed keys and modifiers on the virtual keyboard
const { keyboard } = useKeyboardLayout();
// used to show the modifier keys that are in the "down state" on the virtual keyboard
const keyNamesFromModifierMask = (activeModifiers: number): string[] => {
return Object.entries(modifiers).filter(m => (activeModifiers & m[1]) !== 0).map(m => m[0]);
}
//const isCapsLockActive = useMemo(() => {
// return (keyboardLedState.caps_lock);
//}, [keyboardLedState]);
// used to show the regular keys that are in the "down state" on the virtual keyboard
const keyNamesFromDownKeys = (downKeys: number[]) => {
return Object.entries(keys).filter(([_, code]) => downKeys.includes(code)).map(([name, _]) => name);
}
*/
const { isShiftActive, /*isControlActive, isAltActive, isMetaActive, isAltGrActive*/ } = useMemo(() => {
return decodeModifiers(keysDownState.modifier);
}, [keysDownState]);
const mainLayoutName = useMemo(() => {
const layoutName = isShiftActive ? "shift": "default";
return layoutName;
}, [isShiftActive]);
const keyNamesForDownKeys = useMemo(() => {
const activeModifierMask = keysDownState.modifier || 0;
const modifierNames = Object.entries(modifiers).filter(([_, mask]) => (activeModifierMask & mask) !== 0).map(([name, _]) => name);
const keysDown = keysDownState.keys || [];
const keyNames = Object.entries(keys).filter(([_, value]) => keysDown.includes(value)).map(([name, _]) => name);
return [...modifierNames,...keyNames, ' ']; // we have to have at least one space to avoid keyboard whining
}, [keysDownState]);
const startDrag = useCallback((e: MouseEvent | TouchEvent) => {
if (!keyboardRef.current) return;
if (e instanceof TouchEvent && e.touches.length > 1) return;
@ -119,10 +129,18 @@ function KeyboardWrapper() {
};
}, [endDrag, onDrag, startDrag]);
const onKeyUp = useCallback(
async (_: string, e: MouseEvent | undefined) => {
e?.preventDefault();
e?.stopPropagation();
},
[]
);
const onKeyDown = useCallback(
async (key: string) => {
const latchingKeys = ["CapsLock", "ScrollLock", "NumLock", "Meta", "Compose", "Kana"];
const dynamicKeys = ["ShiftLeft", "ShiftRight", "ControlLeft", "ControlRight", "AltLeft", "AltRight", "MetaLeft", "MetaRight"];
async (key: string, e: MouseEvent | undefined) => {
e?.preventDefault();
e?.stopPropagation();
// handle the fake key-macros we have defined for common combinations
if (key === "CtrlAltDelete") {
@ -149,8 +167,8 @@ function KeyboardWrapper() {
}
// if they press any of the dynamic keys, we send a keypress down event but we don't release it until they click it again
if (dynamicKeys.includes(key)) {
const currentlyDown = keysDownState.keys.includes(keys[key]);
if (Object.keys(modifiers).includes(key)) {
const currentlyDown = keyNamesForDownKeys.includes(key);
console.debug(`Dynamic key pressed: ${key} was currently down: ${currentlyDown}, toggling state`);
handleKeyPress(keys[key], !currentlyDown)
return;
@ -162,16 +180,9 @@ function KeyboardWrapper() {
handleKeyPress(keys[cleanKey], true);
setTimeout(() => handleKeyPress(keys[cleanKey], false), 50);
},
[executeMacro, handleKeyPress, keysDownState],
[executeMacro, handleKeyPress, keyNamesForDownKeys],
);
// TODO handle the display of down keys and the layout change for shift/caps lock
// const { isCapsLockActive } = useShallow(useHidStore());
// // Handle toggle of layout for shift or caps lock
// const toggleLayout = () => {
// setLayoutName(prevLayout => (prevLayout === "default" ? "shift" : "default"));
// };
return (
<div
className="transition-all duration-500 ease-in-out"
@ -245,66 +256,71 @@ function KeyboardWrapper() {
<div className="flex flex-col bg-blue-50/80 md:flex-row dark:bg-slate-700">
<Keyboard
baseClass="simple-keyboard-main"
layoutName={layoutName}
layoutName={mainLayoutName}
onKeyPress={onKeyDown}
onKeyReleased={onKeyUp}
buttonTheme={[
{
class: "combination-key",
buttons: "CtrlAltDelete AltMetaEscape CtrlAltBackspace",
},
{
class: "down-key",
buttons: keyNamesForDownKeys.join(" "),
},
]}
display={keyDisplayMap}
layout={{
default: [
"CtrlAltDelete AltMetaEscape CtrlAltBackspace",
"Escape F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12",
"Backquote Digit1 Digit2 Digit3 Digit4 Digit5 Digit6 Digit7 Digit8 Digit9 Digit0 Minus Equal Backspace",
"Tab KeyQ KeyW KeyE KeyR KeyT KeyY KeyU KeyI KeyO KeyP BracketLeft BracketRight Backslash",
"CapsLock KeyA KeyS KeyD KeyF KeyG KeyH KeyJ KeyK KeyL Semicolon Quote Enter",
"ShiftLeft KeyZ KeyX KeyC KeyV KeyB KeyN KeyM Comma Period Slash ShiftRight",
"ControlLeft AltLeft MetaLeft Space MetaRight AltRight",
],
shift: [
"CtrlAltDelete AltMetaEscape CtrlAltBackspace",
"Escape F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12",
"(Backquote) (Digit1) (Digit2) (Digit3) (Digit4) (Digit5) (Digit6) (Digit7) (Digit8) (Digit9) (Digit0) (Minus) (Equal) (Backspace)",
"Tab (KeyQ) (KeyW) (KeyE) (KeyR) (KeyT) (KeyY) (KeyU) (KeyI) (KeyO) (KeyP) (BracketLeft) (BracketRight) (Backslash)",
"CapsLock (KeyA) (KeyS) (KeyD) (KeyF) (KeyG) (KeyH) (KeyJ) (KeyK) (KeyL) (Semicolon) (Quote) Enter",
"ShiftLeft (KeyZ) (KeyX) (KeyC) (KeyV) (KeyB) (KeyN) (KeyM) (Comma) (Period) (Slash) ShiftRight",
"ControlLeft AltLeft MetaLeft Space MetaRight AltRight",
],
}}
display={keyboard.keyDisplayMap}
layout={keyboard.virtualKeyboard.main}
disableButtonHold={true}
syncInstanceInputs={true}
debug={false}
preventMouseDownDefault={true}
preventMouseUpDefault={true}
stopMouseDownPropagation={true}
stopMouseUpPropagation={true}
physicalKeyboardHighlight={true}
physicalKeyboardHighlightPress={true}
physicalKeyboardHighlightPreventDefault={true}
enableLayoutCandidates={false}
/>
<div className="controlArrows">
<Keyboard
baseClass="simple-keyboard-control"
theme="simple-keyboard hg-theme-default hg-layout-default"
layoutName={layoutName}
layoutName="default"
onKeyPress={onKeyDown}
display={keyDisplayMap}
layout={{
default: ["PrintScreen ScrollLock Pause", "Insert Home Pageup", "Delete End Pagedown"],
shift: ["(PrintScreen) ScrollLock (Pause)", "Insert Home Pageup", "Delete End Pagedown"],
}}
syncInstanceInputs={true}
onKeyReleased={onKeyUp}
display={keyboard.keyDisplayMap}
layout={keyboard.virtualKeyboard.control}
debug={false}
preventMouseDownDefault={true}
preventMouseUpDefault={true}
stopMouseDownPropagation={true}
stopMouseUpPropagation={true}
physicalKeyboardHighlight={true}
physicalKeyboardHighlightPress={true}
physicalKeyboardHighlightPreventDefault={true}
enableLayoutCandidates={false}
/>
<Keyboard
baseClass="simple-keyboard-arrows"
theme="simple-keyboard hg-theme-default hg-layout-default"
onKeyPress={onKeyDown}
display={keyDisplayMap}
layout={{
default: ["ArrowUp", "ArrowLeft ArrowDown ArrowRight"],
}}
syncInstanceInputs={true}
onKeyReleased={onKeyUp}
display={keyboard.keyDisplayMap}
layout={keyboard.virtualKeyboard.arrows}
debug={false}
preventMouseDownDefault={true}
preventMouseUpDefault={true}
stopMouseDownPropagation={true}
stopMouseUpPropagation={true}
physicalKeyboardHighlight={true}
physicalKeyboardHighlightPress={true}
physicalKeyboardHighlightPreventDefault={true}
enableLayoutCandidates={false}
/>
</div>
{ /* TODO add optional number pad */ }
</div>
</div>
</Card>

View File

@ -1,4 +1,4 @@
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useCallback, useEffect, useRef, useState } from "react";
import { LuCornerDownLeft } from "react-icons/lu";
import { ExclamationCircleIcon } from "@heroicons/react/16/solid";
import { useClose } from "@headlessui/react";
@ -10,7 +10,8 @@ import { SettingsPageHeader } from "@components/SettingsPageheader";
import { useJsonRpc } from "@/hooks/useJsonRpc";
import { useHidStore, useRTCStore, useUiStore, useSettingsStore } from "@/hooks/stores";
import { keys, modifiers } from "@/keyboardMappings";
import { KeyStroke, KeyboardLayout, selectedKeyboard } from "@/keyboardLayouts";
import { KeyStroke } from "@/keyboardLayouts";
import { useKeyboardLayout } from "@/hooks/useKeyboardLayout";
import notifications from "@/notifications";
const hidKeyboardPayload = (modifier: number, keys: number[]) => {
@ -18,8 +19,8 @@ const hidKeyboardPayload = (modifier: number, keys: number[]) => {
};
const modifierCode = (shift?: boolean, altRight?: boolean) => {
return (shift ? modifiers["ShiftLeft"] : 0)
| (altRight ? modifiers["AltRight"] : 0)
return (shift ? modifiers.ShiftLeft : 0)
| (altRight ? modifiers.AltRight : 0)
}
const noModifier = 0
@ -34,15 +35,8 @@ export default function PasteModal() {
const [invalidChars, setInvalidChars] = useState<string[]>([]);
const close = useClose();
const { keyboardLayout, setKeyboardLayout } = useSettingsStore();
// this ensures we always get the en-US if it hasn't been set yet
// and if we get en_US from the backend, we convert it to en-US
const safeKeyboardLayout = useMemo(() => {
if (keyboardLayout && keyboardLayout.length > 0)
return keyboardLayout.replace("en_US", "en-US");
return "en-US";
}, [keyboardLayout]);
const { setKeyboardLayout } = useSettingsStore();
const { keyboard } = useKeyboardLayout();
useEffect(() => {
send("getKeyboardLayout", {}, resp => {
@ -62,7 +56,6 @@ export default function PasteModal() {
setDisableVideoFocusTrap(false);
if (rpcDataChannel?.readyState !== "open" || !TextAreaRef.current) return;
const keyboard: KeyboardLayout = selectedKeyboard(safeKeyboardLayout);
if (!keyboard) return;
const text = TextAreaRef.current.value;
@ -109,7 +102,7 @@ export default function PasteModal() {
);
});
}
}, [rpcDataChannel?.readyState, safeKeyboardLayout, send, setDisableVideoFocusTrap, setPasteModeEnabled]);
}, [keyboard, rpcDataChannel?.readyState, send, setDisableVideoFocusTrap, setPasteModeEnabled]);
useEffect(() => {
if (TextAreaRef.current) {
@ -159,7 +152,7 @@ export default function PasteModal() {
// @ts-expect-error TS doesn't recognize Intl.Segmenter in some environments
[...new Intl.Segmenter().segment(value)]
.map(x => x.segment)
.filter(char => !selectedKeyboard(safeKeyboardLayout).chars[char]),
.filter(char => !keyboard.chars[char]),
),
];
@ -180,7 +173,7 @@ export default function PasteModal() {
</div>
<div className="space-y-4">
<p className="text-xs text-slate-600 dark:text-slate-400">
Sending text using keyboard layout: {selectedKeyboard(safeKeyboardLayout).name}
Sending text using keyboard layout: {keyboard.isoCode}-{keyboard.name}
</p>
</div>
</div>

View File

@ -6,9 +6,7 @@ import { hidKeyToModifierMask, keys, modifiers } from "@/keyboardMappings";
export default function useKeyboard() {
const { send } = useJsonRpc();
const { rpcDataChannel } = useRTCStore();
const { keysDownState, setKeysDownState } = useHidStore();
// INTRODUCTION: The earlier version of the JetKVM device shipped with all keyboard state
@ -23,8 +21,8 @@ export default function useKeyboard() {
// getKeysDownState API.
const { keyPressReportApiAvailable, setkeyPressReportApiAvailable} = useHidStore();
// sendKeyboardEvent is used to send the full keyboard state to the device for macro handling and resetting keyboard state.
// It sends the keys currently pressed and the modifier state.
// sendKeyboardEvent is used to send the full keyboard state to the device for macro handling
// and resetting keyboard state. It sends the keys currently pressed and the modifier state.
// The device will respond with the keysDownState if it supports the keyPressReport API
// or just accept the state if it does not support (returning no result)
const sendKeyboardEvent = useCallback(
@ -93,7 +91,6 @@ export default function useKeyboard() {
// is clean.
const resetKeyboardState = useCallback(
async () => {
console.debug("Resetting keyboard state");
// Reset the keys buffer to zeros and the modifier state to zero
keysDownState.keys.length = hidKeyBufferSize;
keysDownState.keys.fill(0);
@ -139,6 +136,7 @@ export default function useKeyboard() {
const handleKeyPress = useCallback(
async (key: number, press: boolean) => {
if (rpcDataChannel?.readyState !== "open") return;
if ((key || 0) === 0) return; // ignore zero key presses (they are bad mappings)
if (keyPressReportApiAvailable) {
// if the keyPress api is available, we can just send the key press event

View File

@ -0,0 +1,22 @@
import { useMemo } from "react";
import { useSettingsStore } from "@/hooks/stores";
import { KeyboardLayout, selectedKeyboard } from "@/keyboardLayouts";
export function useKeyboardLayout(): { keyboard: KeyboardLayout } {
const { keyboardLayout } = useSettingsStore();
const isoCode = useMemo(() => {
console.log("Current keyboard layout from store:", keyboardLayout);
if (keyboardLayout && keyboardLayout.length > 0)
return keyboardLayout.replace("en_US", "en-US");
return "en-US";
}, [keyboardLayout]);
const keyboard = useMemo(() => {
console.log("Selected keyboard layout:", isoCode);
return selectedKeyboard(isoCode);
}, [isoCode]);
return { keyboard };
}

View File

@ -315,6 +315,11 @@ video::-webkit-media-controls {
@apply inline-flex h-auto! w-auto! grow-0 py-1 text-xs;
}
.hg-theme-default .hg-row .down-key {
background: rgb(28, 28, 28);
@apply text-white! font-bold!;
}
.hg-theme-default .hg-row .hg-button-container,
.hg-theme-default .hg-row .hg-button:not(:last-child) {
@apply mr-[2px]! md:mr-[5px]!;

View File

@ -1,7 +1,18 @@
export interface KeyStroke { modifier: number; keys: number[]; }
export interface KeyInfo { key: string | number; shift?: boolean, altRight?: boolean }
export interface KeyCombo extends KeyInfo { deadKey?: boolean, accentKey?: KeyInfo }
export interface KeyboardLayout { isoCode: string, name: string, chars: Record<string, KeyCombo> }
export interface KeyboardLayout {
isoCode: string;
name: string;
chars: Record<string, KeyCombo>;
modifierDisplayMap: Record<string, string>;
keyDisplayMap: Record<string, string>;
virtualKeyboard: {
main: { default: string[], shift: string[] },
control?: { default: string[], shift?: string[] },
arrows?: { default: string[] }
};
}
// to add a new layout, create a file like the above and add it to the list
import { cs_CZ } from "@/keyboardLayouts/cs_CZ"
@ -15,13 +26,14 @@ import { fr_CH } from "@/keyboardLayouts/fr_CH"
import { fr_FR } from "@/keyboardLayouts/fr_FR"
import { it_IT } from "@/keyboardLayouts/it_IT"
import { nb_NO } from "@/keyboardLayouts/nb_NO"
import { pl_PL_t } from "@/keyboardLayouts/pl_PL_t"
import { sv_SE } from "@/keyboardLayouts/sv_SE"
export const keyboards: KeyboardLayout[] = [ cs_CZ, de_CH, de_DE, en_UK, en_US, es_ES, fr_BE, fr_CH, fr_FR, it_IT, nb_NO, sv_SE ];
export const keyboards: KeyboardLayout[] = [ cs_CZ, de_CH, de_DE, en_UK, en_US, es_ES, fr_BE, fr_CH, fr_FR, it_IT, nb_NO, pl_PL_t, sv_SE ];
export const selectedKeyboard = (isoCode: string): KeyboardLayout => {
// fallback to original behaviour of en-US if no isoCode given
return keyboards.find(keyboard => keyboard.isoCode == isoCode)
// fallback to original behaviour of en-US if no isoCode given or matching layout not found
return keyboards.find(keyboard => keyboard.isoCode == isoCode)
?? keyboards.find(keyboard => keyboard.isoCode == "en-US")!;
};

View File

@ -1,17 +1,20 @@
import { KeyboardLayout, KeyCombo } from "../keyboardLayouts"
const name = "Čeština";
import { en_US } from "./en_US" // for fallback of keyDisplayMap, modifierDisplayMap, and virtualKeyboard
const keyTrema = { key: "Backslash" } // tréma (umlaut), two dots placed above a vowel
const keyAcute = { key: "Equal" } // accent aigu (acute accent), mark ´ placed above the letter
const keyHat = { key: "Digit3", shift: true, altRight: true } // accent circonflexe (accent hat), mark ^ placed above the letter
const keyCaron = { key: "Equal", shift: true } // caron or haček (inverted hat), mark ˇ placed above the letter
const keyGrave = { key: "Digit7", shift: true, altRight: true } // accent grave, mark ` placed above the letter
const keyTilde = { key: "Digit1", shift: true, altRight: true } // tilde, mark ~ placed above the letter
const keyRing = { key: "Backquote", shift: true } // kroužek (little ring), mark ° placed above the letter
const keyOverdot = { key: "Digit8", shift: true, altRight: true } // overdot (dot above), mark ˙ placed above the letter
const keyHook = { key: "Digit6", shift: true, altRight: true } // ogonoek (little hook), mark ˛ placed beneath a letter
const keyCedille = { key: "Equal", shift: true, altRight: true } // accent cedille (cedilla), mark ¸ placed beneath a letter
const name = "Čeština";
const isoCode = "cs-CZ";
const keyTrema: KeyCombo = { key: "Backslash" } // tréma (umlaut), two dots placed above a vowel
const keyAcute: KeyCombo = { key: "Equal" } // accent aigu (acute accent), mark ´ placed above the letter
const keyHat: KeyCombo = { key: "Digit3", shift: true, altRight: true } // accent circonflexe (accent hat), mark ^ placed above the letter
const keyCaron: KeyCombo = { key: "Equal", shift: true } // caron or haček (inverted hat), mark ˇ placed above the letter
const keyGrave: KeyCombo = { key: "Digit7", shift: true, altRight: true } // accent grave, mark ` placed above the letter
const keyTilde: KeyCombo = { key: "Digit1", shift: true, altRight: true } // tilde, mark ~ placed above the letter
const keyRing: KeyCombo = { key: "Backquote", shift: true } // kroužek (little ring), mark ° placed above the letter
const keyOverdot: KeyCombo = { key: "Digit8", shift: true, altRight: true } // overdot (dot above), mark ˙ placed above the letter
const keyHook: KeyCombo = { key: "Digit6", shift: true, altRight: true } // ogonoek (little hook), mark ˛ placed beneath a letter
const keyCedille: KeyCombo = { key: "Equal", shift: true, altRight: true } // accent cedille (cedilla), mark ¸ placed beneath a letter
const chars = {
A: { key: "KeyA", shift: true },
@ -244,7 +247,11 @@ const chars = {
} as Record<string, KeyCombo>;
export const cs_CZ: KeyboardLayout = {
isoCode: "cs-CZ",
isoCode: isoCode,
name: name,
chars: chars
chars: chars,
// TODO need to localize these maps and layouts
keyDisplayMap: en_US.keyDisplayMap,
modifierDisplayMap: en_US.modifierDisplayMap,
virtualKeyboard: en_US.virtualKeyboard
};

View File

@ -1,12 +1,15 @@
import { KeyboardLayout, KeyCombo } from "../keyboardLayouts"
const name = "Schwiizerdütsch";
import { en_US } from "./en_US" // for fallback of keyDisplayMap, modifierDisplayMap, and virtualKeyboard
const keyTrema = { key: "BracketRight" } // tréma (umlaut), two dots placed above a vowel
const keyAcute = { key: "Minus", altRight: true } // accent aigu (acute accent), mark ´ placed above the letter
const keyHat = { key: "Equal" } // accent circonflexe (accent hat), mark ^ placed above the letter
const keyGrave = { key: "Equal", shift: true } // accent grave, mark ` placed above the letter
const keyTilde = { key: "Equal", altRight: true } // tilde, mark ~ placed above the letter
const name = "Schwiizerdütsch";
const isoCode = "de-CH";
const keyTrema: KeyCombo = { key: "BracketRight" } // tréma (umlaut), two dots placed above a vowel
const keyAcute: KeyCombo = { key: "Minus", altRight: true } // accent aigu (acute accent), mark ´ placed above the letter
const keyHat: KeyCombo = { key: "Equal" } // accent circonflexe (accent hat), mark ^ placed above the letter
const keyGrave: KeyCombo = { key: "Equal", shift: true } // accent grave, mark ` placed above the letter
const keyTilde: KeyCombo = { key: "Equal", altRight: true } // tilde, mark ~ placed above the letter
const chars = {
A: { key: "KeyA", shift: true },
@ -164,8 +167,22 @@ const chars = {
Tab: { key: "Tab" },
} as Record<string, KeyCombo>;
const keyDisplayMap = {
...en_US.keyDisplayMap,
BracketLeft: "è",
"(BracketLeft)": "ü",
Semicolon: "é",
"(Semicolon)": "ö",
Quote: "à",
"(Quote)": "ä",
} as Record<string, string>;
export const de_CH: KeyboardLayout = {
isoCode: "de-CH",
isoCode: isoCode,
name: name,
chars: chars
chars: chars,
keyDisplayMap: keyDisplayMap,
// TODO need to localize these maps and layouts
modifierDisplayMap: en_US.modifierDisplayMap,
virtualKeyboard: en_US.virtualKeyboard
};

View File

@ -1,113 +1,146 @@
import { KeyboardLayout, KeyCombo } from "../keyboardLayouts"
const name = "Deutsch";
import { en_US } from "./en_US" // for fallback of keyDisplayMap, modifierDisplayMap, and virtualKeyboard
const keyAcute = { key: "Equal" } // accent aigu (acute accent), mark ´ placed above the letter
const keyHat = { key: "Backquote" } // accent circonflexe (accent hat), mark ^ placed above the letter
const keyGrave = { key: "Equal", shift: true } // accent grave, mark ` placed above the letter
const name = "Deutsch";
const isoCode = "de-DE";
const keyAcute: KeyCombo = { key: "Equal" } // accent aigu (acute accent), mark ´ placed above the letter
const keyHat: KeyCombo = { key: "Backquote" } // accent circonflexe (accent hat), mark ^ placed above the letter
const keyGrave: KeyCombo = { key: "Equal", shift: true } // accent grave, mark ` placed above the letter
const chars = {
a: { key: "KeyA" },
"á": { key: "KeyA", accentKey: keyAcute },
"â": { key: "KeyA", accentKey: keyHat },
"à": { key: "KeyA", accentKey: keyGrave },
A: { key: "KeyA", shift: true },
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
"Â": { key: "KeyA", shift: true, accentKey: keyHat },
"À": { key: "KeyA", shift: true, accentKey: keyGrave },
"☺": { key: "KeyA", altRight: true }, // white smiling face ☺
b: { key: "KeyB" },
B: { key: "KeyB", shift: true },
"": { key: "KeyB", altRight: true }, // single left-pointing angle quotation mark,
c: { key: "KeyC" },
C: { key: "KeyC", shift: true },
"\u202f": { key: "KeyC", altRight: true }, // narrow no-break space
d: { key: "KeyD" },
D: { key: "KeyD", shift: true },
"": { key: "KeyD", altRight: true }, // prime, mark placed above the letter
e: { key: "KeyE" },
"é": { key: "KeyE", accentKey: keyAcute },
"ê": { key: "KeyE", accentKey: keyHat },
"è": { key: "KeyE", accentKey: keyGrave },
"€": { key: "KeyE", altRight: true },
E: { key: "KeyE", shift: true },
"É": { key: "KeyE", shift: true, accentKey: keyAcute },
"Ê": { key: "KeyE", shift: true, accentKey: keyHat },
"È": { key: "KeyE", shift: true, accentKey: keyGrave },
F: { key: "KeyF", shift: true },
G: { key: "KeyG", shift: true },
H: { key: "KeyH", shift: true },
I: { key: "KeyI", shift: true },
"Í": { key: "KeyI", shift: true, accentKey: keyAcute },
"Î": { key: "KeyI", shift: true, accentKey: keyHat },
"Ì": { key: "KeyI", shift: true, accentKey: keyGrave },
J: { key: "KeyJ", shift: true },
K: { key: "KeyK", shift: true },
L: { key: "KeyL", shift: true },
M: { key: "KeyM", shift: true },
N: { key: "KeyN", shift: true },
O: { key: "KeyO", shift: true },
"Ó": { key: "KeyO", shift: true, accentKey: keyAcute },
"Ô": { key: "KeyO", shift: true, accentKey: keyHat },
"Ò": { key: "KeyO", shift: true, accentKey: keyGrave },
P: { key: "KeyP", shift: true },
Q: { key: "KeyQ", shift: true },
R: { key: "KeyR", shift: true },
S: { key: "KeyS", shift: true },
T: { key: "KeyT", shift: true },
U: { key: "KeyU", shift: true },
"Ú": { key: "KeyU", shift: true, accentKey: keyAcute },
"Û": { key: "KeyU", shift: true, accentKey: keyHat },
"Ù": { key: "KeyU", shift: true, accentKey: keyGrave },
V: { key: "KeyV", shift: true },
W: { key: "KeyW", shift: true },
X: { key: "KeyX", shift: true },
Y: { key: "KeyZ", shift: true },
Z: { key: "KeyY", shift: true },
a: { key: "KeyA" },
"á": { key: "KeyA", accentKey: keyAcute },
"â": { key: "KeyA", accentKey: keyHat },
"à": { key: "KeyA", accentKey: keyGrave},
b: { key: "KeyB" },
c: { key: "KeyC" },
d: { key: "KeyD" },
e: { key: "KeyE" },
"é": { key: "KeyE", accentKey: keyAcute},
"ê": { key: "KeyE", accentKey: keyHat },
"è": { key: "KeyE", accentKey: keyGrave },
"€": { key: "KeyE", altRight: true },
f: { key: "KeyF" },
F: { key: "KeyF", shift: true },
"˟": { key: "KeyF", deadKey: true, altRight: true }, // modifier letter cross accent, ˟
G: { key: "KeyG", shift: true },
g: { key: "KeyG" },
"ẞ": { key: "KeyG", altRight: true }, // capital sharp S, ẞ
h: { key: "KeyH" },
H: { key: "KeyH", shift: true },
"ˍ": { key: "KeyH", deadKey: true, altRight: true }, // modifier letter low macron, ˍ
i: { key: "KeyI" },
"í": { key: "KeyI", accentKey: keyAcute },
"î": { key: "KeyI", accentKey: keyHat },
"ì": { key: "KeyI", accentKey: keyGrave },
I: { key: "KeyI", shift: true },
"Í": { key: "KeyI", shift: true, accentKey: keyAcute },
"Î": { key: "KeyI", shift: true, accentKey: keyHat },
"Ì": { key: "KeyI", shift: true, accentKey: keyGrave },
"˜": { key: "KeyI", deadKey: true, altRight: true }, // tilde accent, mark ˜ placed above the letter
j: { key: "KeyJ" },
J: { key: "KeyJ", shift: true },
"¸": { key: "KeyJ", deadKey: true, altRight: true }, // cedilla accent, mark ¸ placed below the letter
k: { key: "KeyK" },
K: { key: "KeyK", shift: true },
l: { key: "KeyL" },
L: { key: "KeyL", shift: true },
"ˏ": { key: "KeyL", deadKey: true, altRight: true }, // modifier letter reversed comma, ˏ
m: { key: "KeyM" },
M: { key: "KeyM", shift: true },
"µ": { key: "KeyM", altRight: true },
n: { key: "KeyN" },
N: { key: "KeyN", shift: true },
"": { key: "KeyN", altRight: true }, // en dash,
o: { key: "KeyO" },
"ó": { key: "KeyO", accentKey: keyAcute },
"ô": { key: "KeyO", accentKey: keyHat },
"ò": { key: "KeyO", accentKey: keyGrave },
O: { key: "KeyO", shift: true },
"Ó": { key: "KeyO", shift: true, accentKey: keyAcute },
"Ô": { key: "KeyO", shift: true, accentKey: keyHat },
"Ò": { key: "KeyO", shift: true, accentKey: keyGrave },
"˚": { key: "KeyO", deadKey: true, altRight: true }, // ring above, ˚
p: { key: "KeyP" },
P: { key: "KeyP", shift: true },
"ˀ": { key: "KeyP", deadKey: true, altRight: true }, // modifier letter apostrophe, ʾ
q: { key: "KeyQ" },
Q: { key: "KeyQ", shift: true },
"@": { key: "KeyQ", altRight: true },
R: { key: "KeyR", shift: true },
r: { key: "KeyR" },
"˝": { key: "KeyR", deadKey: true, altRight: true }, // double acute accent, mark ˝ placed above the letter
S: { key: "KeyS", shift: true },
s: { key: "KeyS" },
"″": { key: "KeyS", altRight: true }, // double prime, mark ″ placed above the letter
T: { key: "KeyT", shift: true },
t: { key: "KeyT" },
"ˇ": { key: "KeyT", deadKey: true, altRight: true }, // caron/hacek accent, mark ˇ placed above the letter
u: { key: "KeyU" },
"ú": { key: "KeyU", accentKey: keyAcute },
"û": { key: "KeyU", accentKey: keyHat },
"ù": { key: "KeyU", accentKey: keyGrave },
U: { key: "KeyU", shift: true },
"Ú": { key: "KeyU", shift: true, accentKey: keyAcute },
"Û": { key: "KeyU", shift: true, accentKey: keyHat },
"Ù": { key: "KeyU", shift: true, accentKey: keyGrave },
"˘": { key: "KeyU", deadKey: true, altRight: true }, // breve accent, ˘ placed above the letter
v: { key: "KeyV" },
V: { key: "KeyV", shift: true },
"«": { key: "KeyV", altRight: true }, // left-pointing double angle quotation mark, «
w: { key: "KeyW" },
W: { key: "KeyW", shift: true },
"¯": { key: "KeyW", deadKey: true, altRight: true }, // macron accent, mark ¯ placed above the letter
x: { key: "KeyX" },
X: { key: "KeyX", shift: true },
"»": { key: "KeyX", altRight: true },
// cross key between shift and y (aka OEM 102 key)
y: { key: "KeyZ" },
Y: { key: "KeyZ", shift: true },
"": { key: "KeyZ", altRight: true }, // single right-pointing angle quotation mark,
z: { key: "KeyY" },
Z: { key: "KeyY", shift: true },
"¨": { key: "KeyY", deadKey: true, altRight: true }, // diaeresis accent, mark ¨ placed above the letter
"°": { key: "Backquote", shift: true },
"^": { key: "Backquote", deadKey: true },
"|": { key: "Backquote", altRight: true },
1: { key: "Digit1" },
"!": { key: "Digit1", shift: true },
"": { key: "Digit1", altRight: true }, // single quote, mark placed above the letter
2: { key: "Digit2" },
"\"": { key: "Digit2", shift: true },
"²": { key: "Digit2", altRight: true },
"<": { key: "Digit2", altRight: true }, // non-US < and >
3: { key: "Digit3" },
"§": { key: "Digit3", shift: true },
"³": { key: "Digit3", altRight: true },
">": { key: "Digit3", altRight: true }, // non-US < and >
4: { key: "Digit4" },
"$": { key: "Digit4", shift: true },
"—": { key: "Digit4", altRight: true }, // em dash, —
5: { key: "Digit5" },
"%": { key: "Digit5", shift: true },
"¡": { key: "Digit5", altRight: true }, // inverted exclamation mark, ¡
6: { key: "Digit6" },
"&": { key: "Digit6", shift: true },
"¿": { key: "Digit6", altRight: true }, // inverted question mark, ¿
7: { key: "Digit7" },
"/": { key: "Digit7", shift: true },
"{": { key: "Digit7", altRight: true },
@ -123,36 +156,192 @@ const chars = {
"ß": { key: "Minus" },
"?": { key: "Minus", shift: true },
"\\": { key: "Minus", altRight: true },
"´": { key: "Equal", deadKey: true },
"`": { key: "Equal", shift: true, deadKey: true },
"´": { key: "Equal", deadKey: true }, // accent acute, mark ´ placed above the letter
"`": { key: "Equal", shift: true, deadKey: true }, // accent grave, mark ` placed above the letter
"˙": { key: "Equal", control: true, altRight: true, deadKey: true }, // acute accent, mark ˙ placed above the letter
"ü": { key: "BracketLeft" },
"Ü": { key: "BracketLeft", shift: true },
Escape: { key: "BracketLeft", control: true },
"ʼ": { key: "BracketLeft", altRight: true }, // modifier letter apostrophe, ʼ
"+": { key: "BracketRight" },
"*": { key: "BracketRight", shift: true },
Control: { key: "BracketRight", control: true },
"~": { key: "BracketRight", altRight: true },
"ö": { key: "Semicolon" },
"Ö": { key: "Semicolon", shift: true },
"ˌ": { key: "Semicolon", deadkey: true, altRight: true }, // modifier letter low vertical line, ˌ
"ä": { key: "Quote" },
"Ä": { key: "Quote", shift: true },
"˗": { key: "Quote", deadKey: true, altRight: true }, // modifier letter minus sign, ˗
"#": { key: "Backslash" },
"'": { key: "Backslash", shift: true },
"": { key: "Backslash", altRight: true }, // minus sign,
",": { key: "Comma" },
";": { key: "Comma", shift: true },
"\u2011": { key: "Comma", altRight: true }, // non-breaking hyphen,
".": { key: "Period" },
":": { key: "Period", shift: true },
"·": { key: "Period", altRight: true }, // middle dot, ·
"-": { key: "Slash" },
"_": { key: "Slash", shift: true },
"<": { key: "IntlBackslash" },
">": { key: "IntlBackslash", shift: true },
"|": { key: "IntlBackslash", altRight: true },
"\u00ad": { key: "Slash", altRight: true }, // soft hyphen, ­
" ": { key: "Space" },
"\n": { key: "Enter" },
Enter: { key: "Enter" },
Tab: { key: "Tab" },
} as Record<string, KeyCombo>;
export const keyDisplayMap: Record<string, string> = {
...en_US.keyDisplayMap,
// now override the English keyDisplayMap with German specific keys
// Combination keys
CtrlAltDelete: "Strg + Alt + Entf",
CtrlAltBackspace: "Strg + Alt + ←",
// German action keys
AltLeft: "Alt",
AltRight: "AltGr",
Backspace: "Rücktaste",
"(Backspace)": "Rücktaste",
CapsLock: "Feststelltaste",
Clear: "Entf",
ControlLeft: "Strg",
ControlRight: "Strg",
Delete: "Entf",
End: "Ende",
Enter: "Eingabe",
Escape: "Esc",
Home: "Pos1",
Insert: "Einfg",
Menu: "Menü",
MetaLeft: "Meta",
MetaRight: "Meta",
PageDown: "Bild ↓",
PageUp: "Bild ↑",
ShiftLeft: "Umschalt",
ShiftRight: "Umschalt",
// German umlauts and ß
BracketLeft: "ü",
"(BracketLeft)": "Ü",
Semicolon: "ö",
"(Semicolon)": "Ö",
Quote: "ä",
"(Quote)": "Ä",
Minus: "ß",
"(Minus)": "?",
Equal: "´",
"(Equal)": "`",
Backslash: "#",
"(Backslash)": "'",
// Shifted Numbers
"(Digit2)": "\"",
"(Digit3)": "§",
"(Digit6)": "&",
"(Digit7)": "/",
"(Digit8)": "(",
"(Digit9)": ")",
"(Digit0)": "=",
// Additional German symbols
Backquote: "^",
"(Backquote)": "°",
Comma: ",",
"(Comma)": ";",
Period: ".",
"(Period)": ":",
Slash: "-",
"(Slash)": "_",
// Numpad
NumpadDecimal: "Num ,",
NumpadEnter: "Num Eingabe",
NumpadInsert: "Einfg",
NumpadDelete: "Entf",
// Modals
PrintScreen: "Druck",
ScrollLock: "Rollen",
"(Pause)": "Unterbr",
}
export const modifierDisplayMap: Record<string, string> = {
ShiftLeft: "Umschalt (links)",
ShiftRight: "Umschalt (rechts)",
ControlLeft: "Strg (links)",
ControlRight: "Strg (rechts)",
AltLeft: "Alt",
AltRight: "AltGr",
MetaLeft: "Meta (links)",
MetaRight: "Meta (rechts)",
AltGr: "AltGr",
} as Record<string, string>;
export const virtualKeyboard = {
main: {
default: [
"CtrlAltDelete AltMetaEscape CtrlAltBackspace",
"Escape F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12",
"Backquote Digit1 Digit2 Digit3 Digit4 Digit5 Digit6 Digit7 Digit8 Digit9 Digit0 Minus Equal Backspace",
"Tab KeyQ KeyW KeyE KeyR KeyT KeyY KeyU KeyI KeyO KeyP BracketLeft BracketRight",
"CapsLock KeyA KeyS KeyD KeyF KeyG KeyH KeyJ KeyK KeyL Semicolon Quote Backslash Enter",
"ShiftLeft KeyZ KeyX KeyC KeyV KeyB KeyN KeyM Comma Period Slash ShiftRight",
"ControlLeft MetaLeft AltLeft Space AltGr MetaRight Menu ControlRight",
],
shift: [
"CtrlAltDelete AltMetaEscape CtrlAltBackspace",
"Escape F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12",
"(Backquote) (Digit1) (Digit2) (Digit3) (Digit4) (Digit5) (Digit6) (Digit7) (Digit8) (Digit9) (Digit0) (Minus) (Equal) (Backspace)",
"Tab (KeyQ) (KeyW) (KeyE) (KeyR) (KeyT) (KeyY) (KeyU) (KeyI) (KeyO) (KeyP) (BracketLeft) (BracketRight) (Backslash)",
"CapsLock (KeyA) (KeyS) (KeyD) (KeyF) (KeyG) (KeyH) (KeyJ) (KeyK) (KeyL) (Semicolon) (Quote) Enter",
"ShiftLeft (KeyZ) (KeyX) (KeyC) (KeyV) (KeyB) (KeyN) (KeyM) (Comma) (Period) (Slash) ShiftRight",
"ControlLeft MetaLeft AltLeft Space AltGr MetaRight Menu ControlRight",
]
},
control: {
default: [
"PrintScreen ScrollLock Pause",
"Insert Home PageUp",
"Delete End PageDown"
],
shift: [
"(PrintScreen) ScrollLock (Pause)",
"Insert Home PageUp",
"Delete End PageDown"
],
},
arrows: {
default: [
" ArrowUp ",
"ArrowLeft ArrowDown ArrowRight"],
},
numpad: {
numlocked: [
"NumLock NumpadDivide NumpadMultiply NumpadSubtract",
"Numpad7 Numpad8 Numpad9 NumpadAdd",
"Numpad4 Numpad5 Numpad6",
"Numpad1 Numpad2 Numpad3 NumpadEnter",
"Numpad0 NumpadDecimal",
],
default: [
"NumLock NumpadDivide NumpadMultiply NumpadSubtract",
"Home ArrowUp PageUp NumpadAdd",
"ArrowLeft Clear ArrowRight",
"End ArrowDown PageDown NumpadEnter",
"NumpadInsert NumpadDelete",
],
}
}
export const de_DE: KeyboardLayout = {
isoCode: "de-DE",
isoCode: isoCode,
name: name,
chars: chars
chars: chars,
keyDisplayMap: keyDisplayMap,
modifierDisplayMap: modifierDisplayMap,
virtualKeyboard: virtualKeyboard
};

View File

@ -1,6 +1,9 @@
import { KeyboardLayout, KeyCombo } from "../keyboardLayouts"
import { en_US } from "./en_US" // for fallback of keyDisplayMap, modifierDisplayMap, and virtualKeyboard
const name = "English (UK)";
const isoCode = "en-UK";
const chars = {
A: { key: "KeyA", shift: true },
@ -107,7 +110,11 @@ const chars = {
} as Record<string, KeyCombo>
export const en_UK: KeyboardLayout = {
isoCode: "en-UK",
isoCode: isoCode,
name: name,
chars: chars
chars: chars,
// TODO need to localize these maps and layouts
keyDisplayMap: en_US.keyDisplayMap,
modifierDisplayMap: en_US.modifierDisplayMap,
virtualKeyboard: en_US.virtualKeyboard
};

View File

@ -1,8 +1,18 @@
import { KeyboardLayout, KeyCombo } from "../keyboardLayouts"
const name = "English (US)";
const isoCode = "en-US";
const chars = {
// dead keys for "international" 101 keyboards TODO
/*
const keyAcute = { key: "Quote", control: true, menu: true, mark: "´" } // acute accent
const keyCedilla = { key: ".", shift: true, alt: true, mark: "¸" } // cedilla accent
const keyComma = { key: "BracketRight", shift: true, altRight: true, mark: "," } // comma accent
const keyDiaeresis = { key: "Quote", shift: true, control: true, menu: true, mark: "¨" } // diaeresis accent
const keyDegree = { key: "Semicolon", shift: true, control: true, menu: true, mark: "°" } // degree accent
*/
export const chars = {
A: { key: "KeyA", shift: true },
B: { key: "KeyB", shift: true },
C: { key: "KeyC", shift: true },
@ -89,31 +99,213 @@ const chars = {
">": { key: "Period", shift: true },
";": { key: "Semicolon" },
":": { key: "Semicolon", shift: true },
"¶": { key: "Semicolon", altRight: true }, // pilcrow sign
"[": { key: "BracketLeft" },
"{": { key: "BracketLeft", shift: true },
"«": { key: "BracketLeft", altRight: true }, // double left quote sign
"]": { key: "BracketRight" },
"}": { key: "BracketRight", shift: true },
"»": { key: "BracketRight", altRight: true }, // double right quote sign
"\\": { key: "Backslash" },
"|": { key: "Backslash", shift: true },
"¬": { key: "Backslash", altRight: true }, // not sign
"`": { key: "Backquote" },
"~": { key: "Backquote", shift: true },
"§": { key: "IntlBackslash" },
"±": { key: "IntlBackslash", shift: true },
" ": { key: "Space", shift: false },
"\n": { key: "Enter", shift: false },
Enter: { key: "Enter", shift: false },
Tab: { key: "Tab", shift: false },
PrintScreen: { key: "Prt Sc", shift: false },
" ": { key: "Space" },
"\n": { key: "Enter" },
Enter: { key: "Enter" },
Escape: { key: "Escape" },
Tab: { key: "Tab" },
PrintScreen: { key: "Prt Sc" },
SystemRequest: { key: "Prt Sc", shift: true },
ScrollLock: { key: "ScrollLock", shift: false},
Pause: { key: "Pause", shift: false },
ScrollLock: { key: "ScrollLock" },
Pause: { key: "Pause" },
Break: { key: "Pause", shift: true },
Insert: { key: "Insert", shift: false },
Delete: { key: "Delete", shift: false },
Insert: { key: "Insert" },
Delete: { key: "Delete" },
} as Record<string, KeyCombo>
export const modifierDisplayMap: Record<string, string> = {
ControlLeft: "Left Ctrl",
ControlRight: "Right Ctrl",
ShiftLeft: "Left Shift",
ShiftRight: "Right Shift",
AltLeft: "Left Alt",
AltRight: "Right Alt",
MetaLeft: "Left Meta",
MetaRight: "Right Meta",
AltGr: "AltGr",
} as Record<string, string>;
export const keyDisplayMap: Record<string, string> = {
CtrlAltDelete: "Ctrl + Alt + Delete",
AltMetaEscape: "Alt + Meta + Escape",
CtrlAltBackspace: "Ctrl + Alt + Backspace",
AltGr: "AltGr",
AltLeft: "Alt",
AltRight: "Alt",
ArrowDown: "↓",
ArrowLeft: "←",
ArrowRight: "→",
ArrowUp: "↑",
Backspace: "Backspace",
"(Backspace)": "Backspace",
CapsLock: "Caps Lock",
Clear: "Clear",
ControlLeft: "Ctrl",
ControlRight: "Ctrl",
Delete: "Delete",
End: "End",
Enter: "Enter",
Escape: "Esc",
Home: "Home",
Insert: "Insert",
Menu: "Menu",
MetaLeft: "Meta",
MetaRight: "Meta",
PageDown: "PgDn",
PageUp: "PgUp",
ShiftLeft: "Shift",
ShiftRight: "Shift",
Space: " ",
Tab: "Tab",
// Letters
KeyA: "a", KeyB: "b", KeyC: "c", KeyD: "d", KeyE: "e",
KeyF: "f", KeyG: "g", KeyH: "h", KeyI: "i", KeyJ: "j",
KeyK: "k", KeyL: "l", KeyM: "m", KeyN: "n", KeyO: "o",
KeyP: "p", KeyQ: "q", KeyR: "r", KeyS: "s", KeyT: "t",
KeyU: "u", KeyV: "v", KeyW: "w", KeyX: "x", KeyY: "y",
KeyZ: "z",
// Capital letters
"(KeyA)": "A", "(KeyB)": "B", "(KeyC)": "C", "(KeyD)": "D", "(KeyE)": "E",
"(KeyF)": "F", "(KeyG)": "G", "(KeyH)": "H", "(KeyI)": "I", "(KeyJ)": "J",
"(KeyK)": "K", "(KeyL)": "L", "(KeyM)": "M", "(KeyN)": "N", "(KeyO)": "O",
"(KeyP)": "P", "(KeyQ)": "Q", "(KeyR)": "R", "(KeyS)": "S", "(KeyT)": "T",
"(KeyU)": "U", "(KeyV)": "V", "(KeyW)": "W", "(KeyX)": "X", "(KeyY)": "Y",
"(KeyZ)": "Z",
// Numbers
Digit1: "1", Digit2: "2", Digit3: "3", Digit4: "4", Digit5: "5",
Digit6: "6", Digit7: "7", Digit8: "8", Digit9: "9", Digit0: "0",
// Shifted Numbers
"(Digit1)": "!", "(Digit2)": "@", "(Digit3)": "#", "(Digit4)": "$", "(Digit5)": "%",
"(Digit6)": "^", "(Digit7)": "&", "(Digit8)": "*", "(Digit9)": "(", "(Digit0)": ")",
// Symbols
Minus: "-",
"(Minus)": "_",
Equal: "=",
"(Equal)": "+",
BracketLeft: "[",
"(BracketLeft)": "{",
BracketRight: "]",
"(BracketRight)": "}",
Backslash: "\\",
"(Backslash)": "|",
Semicolon: ";",
"(Semicolon)": ":",
Quote: "'",
"(Quote)": "\"",
Comma: ",",
"(Comma)": "<",
Period: ".",
"(Period)": ">",
Slash: "/",
"(Slash)": "?",
Backquote: "`",
"(Backquote)": "~",
IntlBackslash: "\\",
// Function keys
F1: "F1", F2: "F2", F3: "F3", F4: "F4",
F5: "F5", F6: "F6", F7: "F7", F8: "F8",
F9: "F9", F10: "F10", F11: "F11", F12: "F12",
// Numpad
Numpad0: "Num 0", Numpad1: "Num 1", Numpad2: "Num 2",
Numpad3: "Num 3", Numpad4: "Num 4", Numpad5: "Num 5",
Numpad6: "Num 6", Numpad7: "Num 7", Numpad8: "Num 8",
Numpad9: "Num 9", NumpadAdd: "Num +", NumpadSubtract: "Num -",
NumpadMultiply: "Num *", NumpadDivide: "Num /", NumpadDecimal: "Num .",
NumpadEqual: "Num =", NumpadEnter: "Num Enter", NumpadInsert: "Ins",
NumpadDelete: "Del", NumLock: "Num Lock",
// Modals
PrintScreen: "Prt Sc", ScrollLock: "Scr Lk", Pause: "Pause",
"(PrintScreen)": "Sys Rq", "(Pause)": "Break",
SystemRequest: "Sys Rq", Break: "Break"
};
export const virtualKeyboard = {
main: {
default: [
"CtrlAltDelete AltMetaEscape CtrlAltBackspace",
"Escape F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12",
"Backquote Digit1 Digit2 Digit3 Digit4 Digit5 Digit6 Digit7 Digit8 Digit9 Digit0 Minus Equal Backspace",
"Tab KeyQ KeyW KeyE KeyR KeyT KeyY KeyU KeyI KeyO KeyP BracketLeft BracketRight Backslash",
"CapsLock KeyA KeyS KeyD KeyF KeyG KeyH KeyJ KeyK KeyL Semicolon Quote Enter",
"ShiftLeft KeyZ KeyX KeyC KeyV KeyB KeyN KeyM Comma Period Slash ShiftRight",
"ControlLeft MetaLeft AltLeft Space AltGr MetaRight Menu ControlRight",
],
shift: [
"CtrlAltDelete AltMetaEscape CtrlAltBackspace",
"Escape F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12",
"(Backquote) (Digit1) (Digit2) (Digit3) (Digit4) (Digit5) (Digit6) (Digit7) (Digit8) (Digit9) (Digit0) (Minus) (Equal) (Backspace)",
"Tab (KeyQ) (KeyW) (KeyE) (KeyR) (KeyT) (KeyY) (KeyU) (KeyI) (KeyO) (KeyP) (BracketLeft) (BracketRight) (Backslash)",
"CapsLock (KeyA) (KeyS) (KeyD) (KeyF) (KeyG) (KeyH) (KeyJ) (KeyK) (KeyL) (Semicolon) (Quote) Enter",
"ShiftLeft (KeyZ) (KeyX) (KeyC) (KeyV) (KeyB) (KeyN) (KeyM) (Comma) (Period) (Slash) ShiftRight",
"ControlLeft MetaLeft AltLeft Space AltGr MetaRight Menu ControlRight",
]
},
control: {
default: [
"PrintScreen ScrollLock Pause",
"Insert Home PageUp",
"Delete End PageDown"
],
shift: [
"(PrintScreen) ScrollLock (Pause)",
"Insert Home PageUp",
"Delete End PageDown"
],
},
arrows: {
default: [
"ArrowUp",
"ArrowLeft ArrowDown ArrowRight"],
},
numpad: {
numlocked: [
"NumLock NumpadDivide NumpadMultiply NumpadSubtract",
"Numpad7 Numpad8 Numpad9 NumpadAdd",
"Numpad4 Numpad5 Numpad6",
"Numpad1 Numpad2 Numpad3 NumpadEnter",
"Numpad0 NumpadDecimal",
],
default: [
"NumLock NumpadDivide NumpadMultiply NumpadSubtract",
"Home ArrowUp PageUp NumpadAdd",
"ArrowLeft Clear ArrowRight",
"End ArrowDown PageDown NumpadEnter",
"NumpadInsert NumpadDelete",
],
}
}
export const en_US: KeyboardLayout = {
isoCode: "en-US",
name: name,
chars: chars
};
isoCode,
name,
chars,
keyDisplayMap,
modifierDisplayMap,
virtualKeyboard
};

View File

@ -1,12 +1,15 @@
import { KeyboardLayout, KeyCombo } from "../keyboardLayouts"
const name = "Español";
import { en_US } from "./en_US" // for fallback of keyDisplayMap, modifierDisplayMap, and virtualKeyboard
const keyTrema = { key: "Quote", shift: true } // tréma (umlaut), two dots placed above a vowel
const keyAcute = { key: "Quote" } // accent aigu (acute accent), mark ´ placed above the letter
const keyHat = { key: "BracketRight", shift: true } // accent circonflexe (accent hat), mark ^ placed above the letter
const keyGrave = { key: "BracketRight" } // accent grave, mark ` placed above the letter
const keyTilde = { key: "Key4", altRight: true } // tilde, mark ~ placed above the letter
const name = "Español";
const isoCode = "es-ES";
const keyTrema: KeyCombo = { key: "Quote", shift: true } // tréma (umlaut), two dots placed above a vowel
const keyAcute: KeyCombo = { key: "Quote" } // accent aigu (acute accent), mark ´ placed above the letter
const keyHat: KeyCombo = { key: "BracketRight", shift: true } // accent circonflexe (accent hat), mark ^ placed above the letter
const keyGrave: KeyCombo = { key: "BracketRight" } // accent grave, mark ` placed above the letter
const keyTilde: KeyCombo = { key: "Key4", altRight: true } // tilde, mark ~ placed above the letter
const chars = {
A: { key: "KeyA", shift: true },
@ -168,7 +171,11 @@ const chars = {
} as Record<string, KeyCombo>;
export const es_ES: KeyboardLayout = {
isoCode: "es-ES",
isoCode: isoCode,
name: name,
chars: chars
chars: chars,
// TODO need to localize these maps and layouts
keyDisplayMap: en_US.keyDisplayMap,
modifierDisplayMap: en_US.modifierDisplayMap,
virtualKeyboard: en_US.virtualKeyboard
};

View File

@ -1,12 +1,15 @@
import { KeyboardLayout, KeyCombo } from "../keyboardLayouts"
const name = "Belgisch Nederlands";
import { en_US } from "./en_US" // for fallback of keyDisplayMap, modifierDisplayMap, and virtualKeyboard
const keyTrema = { key: "BracketLeft", shift: true } // tréma (umlaut), two dots placed above a vowel
const keyHat = { key: "BracketLeft" } // accent circonflexe (accent hat), mark ^ placed above the letter
const keyAcute = { key: "Semicolon", altRight: true } // accent aigu (acute accent), mark ´ placed above the letter
const keyGrave = { key: "Quote", shift: true } // accent grave, mark ` placed above the letter
const keyTilde = { key: "Slash", altRight: true } // tilde, mark ~ placed above the letter
const name = "Belgisch Nederlands";
const isoCode = "nl-BE";
const keyTrema: KeyCombo = { key: "BracketLeft", shift: true } // tréma (umlaut), two dots placed above a vowel
const keyHat: KeyCombo = { key: "BracketLeft" } // accent circonflexe (accent hat), mark ^ placed above the letter
const keyAcute: KeyCombo = { key: "Semicolon", altRight: true } // accent aigu (acute accent), mark ´ placed above the letter
const keyGrave: KeyCombo = { key: "Quote", shift: true } // accent grave, mark ` placed above the letter
const keyTilde: KeyCombo = { key: "Slash", altRight: true } // tilde, mark ~ placed above the letter
const chars = {
A: { key: "KeyQ", shift: true },
@ -167,7 +170,11 @@ const chars = {
} as Record<string, KeyCombo>;
export const fr_BE: KeyboardLayout = {
isoCode: "fr-BE",
isoCode: isoCode,
name: name,
chars: chars
chars: chars,
// TODO need to localize these maps and layouts
keyDisplayMap: en_US.keyDisplayMap,
modifierDisplayMap: en_US.modifierDisplayMap,
virtualKeyboard: en_US.virtualKeyboard
};

View File

@ -3,6 +3,7 @@ import { KeyboardLayout, KeyCombo } from "../keyboardLayouts"
import { de_CH } from "./de_CH"
const name = "Français de Suisse";
const isoCode = "fr-CH";
const chars = {
...de_CH.chars,
@ -14,8 +15,22 @@ const chars = {
"ä": { key: "Quote", shift: true },
} as Record<string, KeyCombo>;
const keyDisplayMap = {
...de_CH.keyDisplayMap,
"BracketLeft": "è",
"BracketLeftShift": "ü",
"Semicolon": "é",
"SemicolonShift": "ö",
"Quote": "à",
"QuoteShift": "ä",
} as Record<string, string>;
export const fr_CH: KeyboardLayout = {
isoCode: "fr-CH",
isoCode: isoCode,
name: name,
chars: chars
chars: chars,
keyDisplayMap: keyDisplayMap,
// TODO need to localize these maps and layouts
modifierDisplayMap: de_CH.modifierDisplayMap,
virtualKeyboard: de_CH.virtualKeyboard
};

View File

@ -1,9 +1,12 @@
import { KeyboardLayout, KeyCombo } from "../keyboardLayouts"
const name = "Français";
import { en_US } from "./en_US" // for fallback of keyDisplayMap, modifierDisplayMap, and virtualKeyboard
const keyTrema = { key: "BracketLeft", shift: true } // tréma (umlaut), two dots placed above a vowel
const keyHat = { key: "BracketLeft" } // accent circonflexe (accent hat), mark ^ placed above the letter
const name = "Français";
const isoCode = "fr-FR";
const keyTrema: KeyCombo = { key: "BracketLeft", shift: true } // tréma (umlaut), two dots placed above a vowel
const keyHat: KeyCombo = { key: "BracketLeft" } // accent circonflexe (accent hat), mark ^ placed above the letter
const chars = {
A: { key: "KeyQ", shift: true },
@ -139,7 +142,11 @@ const chars = {
} as Record<string, KeyCombo>;
export const fr_FR: KeyboardLayout = {
isoCode: "fr-FR",
isoCode: isoCode,
name: name,
chars: chars
chars: chars,
// TODO need to localize these maps and layouts
keyDisplayMap: en_US.keyDisplayMap,
modifierDisplayMap: en_US.modifierDisplayMap,
virtualKeyboard: en_US.virtualKeyboard
};

View File

@ -1,6 +1,9 @@
import { KeyboardLayout, KeyCombo } from "../keyboardLayouts"
import { en_US } from "./en_US" // for fallback of keyDisplayMap, modifierDisplayMap, and virtualKeyboard
const name = "Italiano";
const isoCode = "it-IT";
const chars = {
A: { key: "KeyA", shift: true },
@ -113,7 +116,11 @@ const chars = {
} as Record<string, KeyCombo>;
export const it_IT: KeyboardLayout = {
isoCode: "it-IT",
isoCode: isoCode,
name: name,
chars: chars
chars: chars,
// TODO need to localize these maps and layouts
keyDisplayMap: en_US.keyDisplayMap,
modifierDisplayMap: en_US.modifierDisplayMap,
virtualKeyboard: en_US.virtualKeyboard
};

View File

@ -1,12 +1,15 @@
import { KeyboardLayout, KeyCombo } from "../keyboardLayouts"
const name = "Norsk bokmål";
import { en_US } from "./en_US" // for fallback of keyDisplayMap, modifierDisplayMap, and virtualKeyboard
const keyTrema = { key: "BracketRight" } // tréma (umlaut), two dots placed above a vowel
const keyAcute = { key: "Equal", altRight: true } // accent aigu (acute accent), mark ´ placed above the letter
const keyHat = { key: "BracketRight", shift: true } // accent circonflexe (accent hat), mark ^ placed above the letter
const keyGrave = { key: "Equal", shift: true } // accent grave, mark ` placed above the letter
const keyTilde = { key: "BracketRight", altRight: true } // tilde, mark ~ placed above the letter
const name = "Norsk bokmål";
const isoCode = "nb-NO";
const keyTrema: KeyCombo = { key: "BracketRight" } // tréma (umlaut), two dots placed above a vowel
const keyAcute: KeyCombo = { key: "Equal", altRight: true } // accent aigu (acute accent), mark ´ placed above the letter
const keyHat: KeyCombo = { key: "BracketRight", shift: true } // accent circonflexe (accent hat), mark ^ placed above the letter
const keyGrave: KeyCombo = { key: "Equal", shift: true } // accent grave, mark ` placed above the letter
const keyTilde: KeyCombo = { key: "BracketRight", altRight: true } // tilde, mark ~ placed above the letter
const chars = {
A: { key: "KeyA", shift: true },
@ -167,7 +170,11 @@ const chars = {
} as Record<string, KeyCombo>;
export const nb_NO: KeyboardLayout = {
isoCode: "nb-NO",
isoCode: isoCode,
name: name,
chars: chars
chars: chars,
// TODO need to localize these maps and layouts
keyDisplayMap: en_US.keyDisplayMap,
modifierDisplayMap: en_US.modifierDisplayMap,
virtualKeyboard: en_US.virtualKeyboard
};

View File

@ -0,0 +1,244 @@
import { KeyboardLayout, KeyCombo } from "../keyboardLayouts"
import { en_US } from "./en_US" // for fallback of keyDisplayMap, modifierDisplayMap, and virtualKeyboard
const name = "Polski (Programista)";
const isoCode = "pl-PL-t";
const keyAcute: KeyCombo = { key: "Quote" } // accent aigu (acute accent), mark ´ placed above the letter
const keyCedilla: KeyCombo = { key: "OEM_2", altRight: true } // Cedilla mark ¸ placed below the letter in the center
const keyDiaresis: KeyCombo = { key: "Plus", shift: true } // Diaresis (not umlaut!), two dots placed above a vowel to indicate each vowel should be pronounce
const keyDotAbove: KeyCombo = { key: "", } // Dot above, single TODO!
const keyDoubleAcute: KeyCombo = { key: "˝" } // Double acute mark ˝, placed above the letter in the center
const keyGrave: KeyCombo = { key: "BracketRight" } // accent grave mark ` placed above the letter
const keyHacek: KeyCombo = { key: "", } // TODO!
const keyHat: KeyCombo = { key: "BracketRight", shift: true } // accent circonflexe (accent hat), mark ^ placed above the letter
const keyOgonek: KeyCombo = { key: ""} // Ogonek mark ˛ placed below the letter on the right side
const keyTylda: KeyCombo = { key: "Backquote", altRight: true } // Tilde mark ~ placed above the letter
const chars = {
A: { key: "KeyA", shift: true },
"Ä": { key: "KeyA", shift: true, accentKey: keyDiaresis },
"Ą": { key: "KeyA", shift: true, ctrl: true, alt: true },
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
"Â": { key: "KeyA", shift: true, accentKey: keyHat },
"À": { key: "KeyA", shift: true, accentKey: keyGrave },
"Ã": { key: "KeyA", shift: true, accentKey: keyTylda },
a: { key: "KeyA" },
"ä": { key: "KeyA", accentKey: keyDiaresis },
"ą": { key: "KeyA", accentKey: keyOgonek }, // "ą": { key: "KeyA", ctrl: true, alt: true },
"á": { key: "KeyA", accentKey: keyAcute },
"â": { key: "KeyA", accentKey: keyHat },
"à": { key: "KeyA", accentKey: keyGrave },
"ã": { key: "KeyA", accentKey: keyTylda },
B: { key: "KeyB", shift: true },
b: { key: "KeyB" },
C: { key: "KeyC", shift: true },
"Ć": { key: "KeyC", shift: true, accentKey: keyAcute },
"Č": { key: "KeyC", shift: true, accentKey: keyHacek },
"Ç": { key: "KeyC", shift: true, accentKey: keyCedilla }, // "Ç": { key: "Backslash", shift: true },
c: { key: "KeyC" },
"ć": { key: "KeyC", shift: false, accentKey: keyAcute },
"č": { key: "KeyC", accentKey: keyHacek },
"ç": { key: "KeyC", accentKey: keyCedilla }, // "ç": { key: "Backslash" },
D: { key: "KeyD", shift: true },
"Ď": { key: "KeyD", shift: true, accentKey: keyHacek },
d: { key: "KeyD" },
"ď": { key: "KeyD", accentKey: keyHacek },
E: { key: "KeyE", shift: true },
"Ë": { key: "KeyE", shift: true, accentKey: keyDiaresis },
"Ę": { key: "KeyE", shift: true, ctrl: true, alt: true, accentKey: keyOgonek },
"Ě": { key: "KeyE", shift: true, accentKey: keyHacek },
"É": { key: "KeyE", shift: true, accentKey: keyAcute },
"Ê": { key: "KeyE", shift: true, accentKey: keyHat },
"È": { key: "KeyE", shift: true, accentKey: keyGrave },
"Ẽ": { key: "KeyE", shift: true, accentKey: keyTylda },
e: { key: "KeyE" },
"ę": { key: "KeyE", ctrl: true, alt: true, accentKey: keyOgonek },
"ë": { key: "KeyE", accentKey: keyDiaresis },
"ě": { key: "KeyE", accentKey: keyHacek },
"é": { key: "KeyE", accentKey: keyAcute },
"ê": { key: "KeyE", accentKey: keyHat },
"è": { key: "KeyE", accentKey: keyGrave },
"ẽ": { key: "KeyE", accentKey: keyTylda },
F: { key: "KeyF", shift: true },
f: { key: "KeyF" },
G: { key: "KeyG", shift: true },
g: { key: "KeyG" },
H: { key: "KeyH", shift: true },
h: { key: "KeyH" },
I: { key: "KeyI", shift: true },
"Ï": { key: "KeyI", shift: true, accentKey: keyDiaresis },
"Í": { key: "KeyI", shift: true, accentKey: keyAcute },
"Î": { key: "KeyI", shift: true, accentKey: keyHat },
"Ì": { key: "KeyI", shift: true, accentKey: keyGrave },
"Ĩ": { key: "KeyI", shift: true, accentKey: keyTylda },
i: { key: "KeyI" },
"ï": { key: "KeyI", accentKey: keyDiaresis },
"í": { key: "KeyI", accentKey: keyAcute },
"î": { key: "KeyI", accentKey: keyHat },
"ì": { key: "KeyI", accentKey: keyGrave },
"ĩ": { key: "KeyI", accentKey: keyTylda },
J: { key: "KeyJ", shift: true },
j: { key: "KeyJ" },
K: { key: "KeyK", shift: true },
k: { key: "KeyK" },
L: { key: "KeyL", shift: true },
l: { key: "KeyL" },
M: { key: "KeyM", shift: true },
m: { key: "KeyM" },
N: { key: "KeyN", shift: true },
"Ň": { key: "KeyN", shift: true, accentKey: keyHacek },
n: { key: "KeyN" },
"ň": { key: "KeyR", accentKey: keyHacek },
O: { key: "KeyO", shift: true },
"Ö": { key: "KeyO", shift: true, accentKey: keyDiaresis },
"Ő": { key: "KeyO", shift: true, accentKey: keyDoubleAcute },
"Ó": { key: "KeyO", shift: true, accentKey: keyAcute }, // "Ó": { key: "KeyO", shift: true, ctrl: true, alt: true },
"Ô": { key: "KeyO", shift: true, accentKey: keyHat },
"Ò": { key: "KeyO", shift: true, accentKey: keyGrave },
"Õ": { key: "KeyO", shift: true, accentKey: keyTylda },
o: { key: "KeyO" },
"ó": { key: "KeyO", ctrl: true, alt: true, accentKey: keyAcute },
"ö": { key: "KeyO", accentKey: keyDiaresis },
"ő": { key: "KeyO", accentKey: keyDoubleAcute },
"ô": { key: "KeyO", accentKey: keyHat },
"ò": { key: "KeyO", accentKey: keyGrave },
"õ": { key: "KeyO", accentKey: keyTylda },
P: { key: "KeyP", shift: true },
p: { key: "KeyP" },
Q: { key: "KeyQ", shift: true },
q: { key: "KeyQ" },
R: { key: "KeyR", shift: true },
"Ř": { key: "KeyR", shift: true, accentKey: keyHacek },
r: { key: "KeyR" },
"ř": { key: "KeyR", accentKey: keyHacek },
S: { key: "KeyS", shift: true },
"Š": { key: "KeyS", shift: true, accentKey: keyHacek },
"Ş": { key: "KeyS", shift: true, accentKey: keyCedilla },
s: { key: "KeyS" },
"š": { key: "KeyS", accentKey: keyHacek },
"ş": { key: "KeyS", accentKey: keyCedilla },
T: { key: "KeyT", shift: true },
"Ť": { key: "KeyT", shift: true, accentKey: keyHacek },
"Ţ": { key: "KeyT", shift: true, accentKey: keyCedilla },
t: { key: "KeyT" },
"ť": { key: "KeyT", accentKey: keyHacek },
"ţ": { key: "KeyS", accentKey: keyCedilla },
U: { key: "KeyU", shift: true },
"Ü": { key: "KeyU", shift: true, accentKey: keyDiaresis },
"Ű": { key: "KeyU", shift: true, accentKey: keyDoubleAcute },
"Ú": { key: "KeyU", shift: true, accentKey: keyAcute },
"Û": { key: "KeyU", shift: true, accentKey: keyHat },
"Ù": { key: "KeyU", shift: true, accentKey: keyGrave },
"Ũ": { key: "KeyU", shift: true, accentKey: keyTylda },
u: { key: "KeyU" },
"€": { key: "KeyU", ctrl: true, alt: true },
"ü": { key: "KeyU", accentKey: keyDiaresis },
"ű": { key: "KeyU", accentKey: keyDoubleAcute },
"ú": { key: "KeyU", accentKey: keyAcute },
"û": { key: "KeyU", accentKey: keyHat },
"ù": { key: "KeyU", accentKey: keyGrave },
"ũ": { key: "KeyU", accentKey: keyTylda },
V: { key: "KeyV", shift: true },
v: { key: "KeyV" },
W: { key: "KeyW", shift: true },
w: { key: "KeyW" },
X: { key: "KeyX", shift: true },
x: { key: "KeyX" },
Y: { key: "KeyY", shift: true },
y: { key: "KeyY" },
Z: { key: "KeyZ", shift: true },
"Ž": { key: "KeyZ", shift: true, accentKey: keyHacek },
"Ź": { key: "KeyZ", shift: true, ctrl: true, meta: true, accentKey: keyAcute },
z: { key: "KeyZ" },
"ž": { key: "KeyZ", accentKey: keyHacek },
"ź": { key: "KeyX",ctrl: true, meta: true, accentKey: keyAcute }, // not a typo, it's on the X key
"ż": { key: "KeyZ", ctrl: true, meta: true, accentKey: keyDotAbove },
"º": { key: "Backquote" },
"ª": { key: "Backquote", shift: true },
"\\": { key: "Backquote", altRight: true },
1: { key: "Digit1" },
"!": { key: "Digit1", shift: true },
"|": { key: "Digit1", altRight: true },
2: { key: "Digit2" },
"\"": { key: "Digit2", shift: true },
"@": { key: "Digit2", altRight: true },
3: { key: "Digit3" },
"#": { key: "Digit3", shift: true },
4: { key: "Digit4" },
"$": { key: "Digit4", shift: true },
5: { key: "Digit5" },
"%": { key: "Digit5", shift: true },
6: { key: "Digit6" },
"^": { key: "Digit6", shift: true },
"¬": { key: "Digit6", altRight: true },
7: { key: "Digit7" },
"&": { key: "Digit7", shift: true },
8: { key: "Digit8" },
"*": { key: "Digit8", shift: true },
9: { key: "Digit9" },
"(": { key: "Digit9", shift: true },
0: { key: "Digit0" },
")": { key: "Digit0", shift: true },
"'": { key: "Minus" },
"?": { key: "Minus", shift: true },
"¡": { key: "Equal", deadKey: true },
"¿": { key: "Equal", shift: true },
"[": { key: "BracketLeft", altRight: true },
"+": { key: "BracketRight" },
//"*": { key: "BracketRight", shift: true },
"]": { key: "BracketRight", altRight: true },
"ñ": { key: "Semicolon" },
"Ñ": { key: "Semicolon", shift: true },
"{": { key: "Quote", altRight: true },
"}": { key: "Backslash", altRight: true },
",": { key: "Comma" },
";": { key: "Comma", shift: true },
".": { key: "Period" },
":": { key: "Period", shift: true },
"-": { key: "Slash" },
"_": { key: "Slash", shift: true },
"<": { key: "IntlBackslash" },
">": { key: "IntlBackslash", shift: true },
" ": { key: "Space" },
"ˇ": { key: "Space", accentKey: keyHacek },
"\n": { key: "Enter" },
Enter: { key: "Enter" },
Tab: { key: "Tab" },
} as Record<string, KeyCombo>;
export const pl_PL_t: KeyboardLayout = {
isoCode: isoCode,
name: name,
chars: chars,
keyDisplayMap: en_US.keyDisplayMap,
modifierDisplayMap: en_US.modifierDisplayMap,
virtualKeyboard: en_US.virtualKeyboard
};

View File

@ -1,12 +1,15 @@
import { KeyboardLayout, KeyCombo } from "../keyboardLayouts"
const name = "Svenska";
import { en_US } from "./en_US" // for fallback of keyDisplayMap, modifierDisplayMap, and virtualKeyboard
const keyTrema = { key: "BracketRight" } // tréma (umlaut), two dots placed above a vowel
const keyAcute = { key: "Equal" } // accent aigu (acute accent), mark ´ placed above the letter
const keyHat = { key: "BracketRight", shift: true } // accent circonflexe (accent hat), mark ^ placed above the letter
const keyGrave = { key: "Equal", shift: true } // accent grave, mark ` placed above the letter
const keyTilde = { key: "BracketRight", altRight: true } // tilde, mark ~ placed above the letter
const name = "Svenska";
const isoCode = "sv-SE";
const keyTrema: KeyCombo = { key: "BracketRight" } // tréma (umlaut), two dots placed above a vowel
const keyAcute: KeyCombo = { key: "Equal" } // accent aigu (acute accent), mark ´ placed above the letter
const keyHat: KeyCombo = { key: "BracketRight", shift: true } // accent circonflexe (accent hat), mark ^ placed above the letter
const keyGrave: KeyCombo = { key: "Equal", shift: true } // accent grave, mark ` placed above the letter
const keyTilde: KeyCombo = { key: "BracketRight", altRight: true } // tilde, mark ~ placed above the letter
const chars = {
A: { key: "KeyA", shift: true },
@ -164,7 +167,11 @@ const chars = {
} as Record<string, KeyCombo>;
export const sv_SE: KeyboardLayout = {
isoCode: "sv-SE",
isoCode: isoCode,
name: name,
chars: chars
chars: chars,
// TODO need to localize these maps and layouts
keyDisplayMap: en_US.keyDisplayMap,
modifierDisplayMap: en_US.modifierDisplayMap,
virtualKeyboard: en_US.virtualKeyboard
};

View File

@ -1,20 +1,39 @@
// Key codes and modifiers correspond to definitions in the
// [Linux USB HID gadget driver](https://www.kernel.org/doc/Documentation/usb/gadget_hid.txt)
// [Section 10. Keyboard/Keypad Page 0x07](https://usb.org/sites/default/files/hut1_21.pdf)
// [Universal Serial Bus HID Usage Tables: Section 10](https://www.usb.org/sites/default/files/documents/hut1_12v2.pdf)
// These are all the key codes (not scan codes) that an 85/101/102 keyboard might have on it
export const keys = {
Again: 0x79,
AlternateErase: 0x9d,
AltGr: 0xe6, // aka AltRight
AltLeft: 0xe2,
AltRight: 0xe6,
Application: 0x65,
ArrowDown: 0x51,
ArrowLeft: 0x50,
ArrowRight: 0x4f,
ArrowUp: 0x52,
Attention: 0x9a,
Backquote: 0x35, // aka Grave
Backslash: 0x31,
Backspace: 0x2a,
BracketLeft: 0x2f, // aka LeftBrace
BracketRight: 0x30, // aka RightBrace
Cancel: 0x9b,
CapsLock: 0x39,
Clear: 0x9c,
ClearAgain: 0xa2,
Comma: 0x36,
Compose: 0x65,
ContextMenu: 0x65, // same as Compose
Compose: 0xe3,
ContextMenu: 0x65,
ControlLeft: 0xe0,
ControlRight: 0xe4,
Copy: 0x7c,
CrSel: 0xa3,
CurrencySubunit: 0xb5,
CurrencyUnit: 0xb4,
Cut: 0x7b,
DecimalSeparator: 0xb3,
Delete: 0x4c,
Digit0: 0x27,
Digit1: 0x1e,
@ -30,6 +49,8 @@ export const keys = {
Enter: 0x28,
Equal: 0x2e,
Escape: 0x29,
Execute: 0x74,
ExSel: 0xa4,
F1: 0x3a,
F2: 0x3b,
F3: 0x3c,
@ -54,9 +75,21 @@ export const keys = {
F22: 0x71,
F23: 0x72,
F24: 0x73,
Home: 0x4a,
Find: 0x7e,
Grave: 0x35,
HashTilde: 0x32, // non-US # and ~
Help: 0x75,
Home: 0x4a,
Insert: 0x49,
International1: 0x87,
International2: 0x88,
International3: 0x89,
International4: 0x8a,
International5: 0x8b,
International6: 0x8c,
International7: 0x8d,
International8: 0x8e,
International9: 0x8f,
IntlBackslash: 0x64, // non-US \ and |
KeyA: 0x04,
KeyB: 0x05,
@ -84,10 +117,27 @@ export const keys = {
KeyX: 0x1b,
KeyY: 0x1c,
KeyZ: 0x1d,
KeypadExclamation: 0xcf,
LockingCapsLock: 0x82,
LockingNumLock: 0x83,
LockingScrollLock: 0x84,
Lang1: 0x90, // Hangul/English toggle on Korean keyboards
Lang2: 0x91, // Hanja conversion on Korean keyboards
Lang3: 0x92, // Katakana on Japanese keyboards
Lang4: 0x93, // Hiragana on Japanese keyboards
Lang5: 0x94, // Zenkaku/Hankaku toggle on Japanese keyboards
Lang6: 0x95,
Lang7: 0x96,
Lang8: 0x97,
Lang9: 0x98,
Menu: 0x76,
MetaLeft: 0xe3,
MetaRight: 0xe7,
Minus: 0x2d,
Mute: 0x7f,
NumLock: 0x53, // and Clear
Numpad0: 0x62, // and Insert
Numpad00: 0xb0,
Numpad000: 0xb1,
Numpad1: 0x59, // and End
Numpad2: 0x5a, // and Down Arrow
Numpad3: 0x5b, // and Page Down
@ -98,38 +148,111 @@ export const keys = {
Numpad8: 0x60, // and Up Arrow
Numpad9: 0x61, // and Page Up
NumpadAdd: 0x57,
NumpadAnd: 0xc7,
NumpadAt: 0xce,
NumpadBackspace: 0xbb,
NumpadBinary: 0xda,
NumpadCircumflex: 0xc3,
NumpadClear: 0xd8,
NumpadClearEntry: 0xd9,
NumpadColon: 0xcb,
NumpadComma: 0x85,
NumpadDecimal: 0x63,
NumpadDecimalBase: 0xdc,
NumpadDelete: 0x63,
NumpadDivide: 0x54,
NumpadDownArrow: 0x5a,
NumpadEnd: 0x59,
NumpadEnter: 0x58,
NumpadEqual: 0x67,
NumpadExclamation: 0xcf,
NumpadGreaterThan: 0xc6,
NumpadHexadecimal: 0xdd,
NumpadHome: 0x5f,
NumpadKeyA: 0xbc,
NumpadKeyB: 0xbd,
NumpadKeyC: 0xbe,
NumpadKeyD: 0xbf,
NumpadKeyE: 0xc0,
NumpadKeyF: 0xc1,
NumpadLeftArrow: 0x5c,
NumpadLeftBrace: 0xb8,
NumpadLeftParen: 0xb6,
NumpadLessThan: 0xc5,
NumpadLogicalAnd: 0xc8,
NumpadLogicalOr: 0xca,
NumpadMemoryAdd: 0xd3,
NumpadMemoryClear: 0xd2,
NumpadMemoryDivide: 0xd6,
NumpadMemoryMultiply: 0xd5,
NumpadMemoryRecall: 0xd1,
NumpadMemoryStore: 0xd0,
NumpadMemorySubtract: 0xd4,
NumpadMultiply: 0x55,
NumpadOctal: 0xdb,
NumpadOctathorpe: 0xcc,
NumpadOr: 0xc9,
NumpadPageDown: 0x5b,
NumpadPageUp: 0x61,
NumpadPercent: 0xc4,
NumpadPlusMinus: 0xd7,
NumpadRightArrow: 0x5e,
NumpadRightBrace: 0xb9,
NumpadRightParen: 0xb7,
NumpadSpace: 0xcd,
NumpadSubtract: 0x56,
NumpadTab: 0xba,
NumpadUpArrow: 0x60,
NumpadXOR: 0xc2,
Octothorpe: 0x32, // non-US # and ~
Operation: 0xa1,
Out: 0xa0,
PageDown: 0x4e,
PageUp: 0x4b,
Period: 0x37,
PrintScreen: 0x46,
Paste: 0x7d,
Pause: 0x48,
Period: 0x37,
Power: 0x66,
PrintScreen: 0x46,
Prior: 0x9d,
Quote: 0x34, // aka Single Quote or Apostrophe
Return: 0x9e,
ScrollLock: 0x47,
Select: 0x77,
Semicolon: 0x33,
Slash: 0x38,
Space: 0x2c,
SystemRequest: 0x9a,
Tab: 0x2b,
ControlLeft: 0xe0,
ControlRight: 0xe4,
Separator: 0x9f,
ShiftLeft: 0xe1,
ShiftRight: 0xe5,
AltLeft: 0xe2,
AltRight: 0xe6,
MetaLeft: 0xe3,
MetaRight: 0xe7,
Slash: 0x38,
Space: 0x2c,
Stop: 0x78,
SystemRequest: 0x9a,
Tab: 0x2b,
ThousandsSeparator: 0xb2,
Tilde: 0x35,
Undo: 0x7a,
VolumeDown: 0x81,
VolumeUp: 0x80,
} as Record<string, number>;
export const deadKeys = {
Acute: 0x00b4,
Breve: 0x02d8,
Caron: 0x02c7,
Cedilla: 0x00b8,
Circumflex: 0x005e, // or 0x02c6?
Comma: 0x002c,
Dot: 0x00b7,
DoubleAcute: 0x02dd,
Grave: 0x0060,
Kreis: 0x00b0,
Ogonek: 0x02db,
Ring: 0x02da,
Slash: 0x02f8,
Tilde: 0x007e,
Umlaut: 0x00a8,
} as Record<string, number>
export const modifiers = {
ControlLeft: 0x01,
ControlRight: 0x10,
@ -139,6 +262,7 @@ export const modifiers = {
AltRight: 0x40,
MetaLeft: 0x08,
MetaRight: 0x80,
AltGr: 0x40,
} as Record<string, number>;
export const hidKeyToModifierMask = {
@ -148,115 +272,18 @@ export const hidKeyToModifierMask = {
0xe3: modifiers.MetaLeft,
0xe4: modifiers.ControlRight,
0xe5: modifiers.ShiftRight,
0xe6: modifiers.AltRight,
0xe6: modifiers.AltRight, // can also be AltGr
0xe7: modifiers.MetaRight,
} as Record<number, number>;
export const modifierDisplayMap: Record<string, string> = {
ControlLeft: "Left Ctrl",
ControlRight: "Right Ctrl",
ShiftLeft: "Left Shift",
ShiftRight: "Right Shift",
AltLeft: "Left Alt",
AltRight: "Right Alt",
MetaLeft: "Left Meta",
MetaRight: "Right Meta",
} as Record<string, string>;
export const latchingKeys = ["CapsLock", "ScrollLock", "NumLock", "Meta", "Compose", "Kana"];
export const keyDisplayMap: Record<string, string> = {
CtrlAltDelete: "Ctrl + Alt + Delete",
AltMetaEscape: "Alt + Meta + Escape",
CtrlAltBackspace: "Ctrl + Alt + Backspace",
Escape: "esc",
Tab: "tab",
Backspace: "backspace",
"(Backspace)": "backspace",
Enter: "enter",
CapsLock: "caps lock",
ShiftLeft: "shift",
ShiftRight: "shift",
ControlLeft: "ctrl",
AltLeft: "alt",
AltRight: "alt",
MetaLeft: "meta",
MetaRight: "meta",
Space: " ",
Insert: "insert",
Home: "home",
PageUp: "page up",
Delete: "delete",
End: "end",
PageDown: "page down",
ArrowLeft: "←",
ArrowRight: "→",
ArrowUp: "↑",
ArrowDown: "↓",
// Letters
KeyA: "a", KeyB: "b", KeyC: "c", KeyD: "d", KeyE: "e",
KeyF: "f", KeyG: "g", KeyH: "h", KeyI: "i", KeyJ: "j",
KeyK: "k", KeyL: "l", KeyM: "m", KeyN: "n", KeyO: "o",
KeyP: "p", KeyQ: "q", KeyR: "r", KeyS: "s", KeyT: "t",
KeyU: "u", KeyV: "v", KeyW: "w", KeyX: "x", KeyY: "y",
KeyZ: "z",
// Capital letters
"(KeyA)": "A", "(KeyB)": "B", "(KeyC)": "C", "(KeyD)": "D", "(KeyE)": "E",
"(KeyF)": "F", "(KeyG)": "G", "(KeyH)": "H", "(KeyI)": "I", "(KeyJ)": "J",
"(KeyK)": "K", "(KeyL)": "L", "(KeyM)": "M", "(KeyN)": "N", "(KeyO)": "O",
"(KeyP)": "P", "(KeyQ)": "Q", "(KeyR)": "R", "(KeyS)": "S", "(KeyT)": "T",
"(KeyU)": "U", "(KeyV)": "V", "(KeyW)": "W", "(KeyX)": "X", "(KeyY)": "Y",
"(KeyZ)": "Z",
// Numbers
Digit1: "1", Digit2: "2", Digit3: "3", Digit4: "4", Digit5: "5",
Digit6: "6", Digit7: "7", Digit8: "8", Digit9: "9", Digit0: "0",
// Shifted Numbers
"(Digit1)": "!", "(Digit2)": "@", "(Digit3)": "#", "(Digit4)": "$", "(Digit5)": "%",
"(Digit6)": "^", "(Digit7)": "&", "(Digit8)": "*", "(Digit9)": "(", "(Digit0)": ")",
// Symbols
Minus: "-",
"(Minus)": "_",
Equal: "=",
"(Equal)": "+",
BracketLeft: "[",
"(BracketLeft)": "{",
BracketRight: "]",
"(BracketRight)": "}",
Backslash: "\\",
"(Backslash)": "|",
Semicolon: ";",
"(Semicolon)": ":",
Quote: "'",
"(Quote)": "\"",
Comma: ",",
"(Comma)": "<",
Period: ".",
"(Period)": ">",
Slash: "/",
"(Slash)": "?",
Backquote: "`",
"(Backquote)": "~",
IntlBackslash: "\\",
// Function keys
F1: "F1", F2: "F2", F3: "F3", F4: "F4",
F5: "F5", F6: "F6", F7: "F7", F8: "F8",
F9: "F9", F10: "F10", F11: "F11", F12: "F12",
// Numpad
Numpad0: "Num 0", Numpad1: "Num 1", Numpad2: "Num 2",
Numpad3: "Num 3", Numpad4: "Num 4", Numpad5: "Num 5",
Numpad6: "Num 6", Numpad7: "Num 7", Numpad8: "Num 8",
Numpad9: "Num 9", NumpadAdd: "Num +", NumpadSubtract: "Num -",
NumpadMultiply: "Num *", NumpadDivide: "Num /", NumpadDecimal: "Num .",
NumpadEqual: "Num =", NumpadEnter: "Num Enter",
NumLock: "Num Lock",
// Modals
PrintScreen: "prt sc", ScrollLock: "scr lk", Pause: "pause",
"(PrintScreen)": "sys rq", "(Pause)": "break",
SystemRequest: "sys rq", Break: "break"
};
export function decodeModifiers(modifier: number) {
return {
isShiftActive: (modifier & (modifiers.ShiftLeft | modifiers.ShiftRight)) !== 0,
isControlActive: (modifier & (modifiers.ControlLeft | modifiers.ControlRight)) !== 0,
isAltActive: (modifier & (modifiers.AltLeft | modifiers.AltRight)) !== 0,
isMetaActive: (modifier & (modifiers.MetaLeft | modifiers.MetaRight)) !== 0,
isAltGrActive: (modifier & modifiers.AltGr) !== 0,
};
}

View File

@ -1,28 +1,20 @@
import { useCallback, useEffect, useMemo } from "react";
import { useCallback, useEffect } from "react";
import { useSettingsStore } from "@/hooks/stores";
import { useJsonRpc } from "@/hooks/useJsonRpc";
import notifications from "@/notifications";
import { useKeyboardLayout } from "@/hooks/useKeyboardLayout";
import { SettingsPageHeader } from "@components/SettingsPageheader";
import { keyboardOptions } from "@/keyboardLayouts";
import { Checkbox } from "@/components/Checkbox";
import { SelectMenuBasic } from "../components/SelectMenuBasic";
import { SelectMenuBasic } from "@/components/SelectMenuBasic";
import { keyboardOptions } from "@/keyboardLayouts";
import notifications from "@/notifications";
import { SettingsItem } from "./devices.$id.settings";
export default function SettingsKeyboardRoute() {
const { keyboardLayout, setKeyboardLayout } = useSettingsStore();
const { setKeyboardLayout } = useSettingsStore();
const { showPressedKeys, setShowPressedKeys } = useSettingsStore();
// this ensures we always get the en-US if it hasn't been set yet
// and if we get en_US from the backend, we convert it to en-US
const safeKeyboardLayout = useMemo(() => {
if (keyboardLayout && keyboardLayout.length > 0)
return keyboardLayout.replace("en_US", "en-US");
return "en-US";
}, [keyboardLayout]);
const { keyboard } = useKeyboardLayout();
const layoutOptions = keyboardOptions();
const { send } = useJsonRpc();
@ -30,21 +22,25 @@ export default function SettingsKeyboardRoute() {
useEffect(() => {
send("getKeyboardLayout", {}, resp => {
if ("error" in resp) return;
setKeyboardLayout(resp.result as string);
const isoCode = resp.result as string;
console.log("Fetched keyboard layout from backend:", isoCode);
if (isoCode && isoCode.length > 0) {
setKeyboardLayout(isoCode);
}
});
}, [send, setKeyboardLayout]);
const onKeyboardLayoutChange = useCallback(
(e: React.ChangeEvent<HTMLSelectElement>) => {
const layout = e.target.value;
send("setKeyboardLayout", { layout }, resp => {
const isoCode = e.target.value;
send("setKeyboardLayout", { layout: isoCode }, resp => {
if ("error" in resp) {
notifications.error(
`Failed to set keyboard layout: ${resp.error.data || "Unknown error"}`,
);
}
notifications.success("Keyboard layout set successfully");
setKeyboardLayout(layout);
notifications.success("Keyboard layout set successfully to " + isoCode);
setKeyboardLayout(isoCode);
});
},
[send, setKeyboardLayout],
@ -58,7 +54,6 @@ export default function SettingsKeyboardRoute() {
/>
<div className="space-y-4">
{ /* this menu item could be renamed to plain "Keyboard layout" in the future, when also the virtual keyboard layout mappings are being implemented */ }
<SettingsItem
title="Paste text"
description="Keyboard layout of target operating system"
@ -67,7 +62,7 @@ export default function SettingsKeyboardRoute() {
size="SM"
label=""
fullWidth
value={safeKeyboardLayout}
value={keyboard.isoCode}
onChange={onKeyboardLayoutChange}
options={layoutOptions}
/>

View File

@ -17,10 +17,10 @@ import { Button } from "@/components/Button";
import EmptyCard from "@/components/EmptyCard";
import Card from "@/components/Card";
import { MAX_TOTAL_MACROS, COPY_SUFFIX, DEFAULT_DELAY } from "@/constants/macros";
import { keyDisplayMap, modifierDisplayMap } from "@/keyboardMappings";
import notifications from "@/notifications";
import { ConfirmDialog } from "@/components/ConfirmDialog";
import LoadingSpinner from "@/components/LoadingSpinner";
import { useKeyboardLayout } from "@/hooks/useKeyboardLayout";
const normalizeSortOrders = (macros: KeySequence[]): KeySequence[] => {
return macros.map((macro, index) => ({
@ -35,6 +35,7 @@ export default function SettingsMacrosRoute() {
const [actionLoadingId, setActionLoadingId] = useState<string | null>(null);
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
const [macroToDelete, setMacroToDelete] = useState<KeySequence | null>(null);
const { keyboard } = useKeyboardLayout();
const isMaxMacrosReached = useMemo(
() => macros.length >= MAX_TOTAL_MACROS,
@ -185,7 +186,7 @@ export default function SettingsMacrosRoute() {
step.modifiers.map((modifier, idx) => (
<Fragment key={`mod-${idx}`}>
<span className="font-medium text-slate-600 dark:text-slate-200">
{modifierDisplayMap[modifier] || modifier}
{keyboard.modifierDisplayMap[modifier] || modifier}
</span>
{idx < step.modifiers.length - 1 && (
<span className="text-slate-400 dark:text-slate-600">
@ -210,7 +211,7 @@ export default function SettingsMacrosRoute() {
step.keys.map((key, idx) => (
<Fragment key={`key-${idx}`}>
<span className="font-medium text-blue-600 dark:text-blue-400">
{keyDisplayMap[key] || key}
{keyboard.keyDisplayMap[key] || key}
</span>
{idx < step.keys.length - 1 && (
<span className="text-slate-400 dark:text-slate-600">
@ -297,8 +298,10 @@ export default function SettingsMacrosRoute() {
actionLoadingId,
handleDeleteMacro,
handleMoveMacro,
keyboard.modifierDisplayMap,
keyboard.keyDisplayMap,
handleDuplicateMacro,
navigate,
navigate
],
);

View File

@ -17,14 +17,13 @@ import { useResizeObserver } from "usehooks-ts";
import Card from "@/components/Card";
import { LinkButton } from "@/components/Button";
import { FeatureFlag } from "@/components/FeatureFlag";
import LoadingSpinner from "@/components/LoadingSpinner";
import { useUiStore } from "@/hooks/stores";
import useKeyboard from "@/hooks/useKeyboard";
import { FeatureFlag } from "../components/FeatureFlag";
import { cx } from "../cva.config";
/* TODO: Migrate to using URLs instead of the global state. To simplify the refactoring, we'll keep the global state for now. */
export default function SettingsRoute() {
const location = useLocation();
@ -65,16 +64,10 @@ export default function SettingsRoute() {
}, [width]);
useEffect(() => {
// disable focus trap
setTimeout(() => {
// Reset keyboard state. In case the user is pressing a key while enabling the sidebar
resetKeyboardState();
setDisableVideoFocusTrap(true);
// For some reason, the focus trap is not disabled immediately
// so we need to blur the active element
(document.activeElement as HTMLElement)?.blur();
console.debug("Just disabled focus trap");
}, 300);
resetKeyboardState();
}, 500);
return () => {
setDisableVideoFocusTrap(false);