Compare commits

...

2 Commits

Author SHA1 Message Date
Marc Brooks 1a58f1cad0
Centralized keyboard layout and localized display maps 2025-08-20 17:08:48 -05:00
Marc Brooks 6cd8a7ae95
WIP on feature/polish-keyboard 2025-08-20 03:14:00 -05:00
25 changed files with 575 additions and 332 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

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

@ -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 } from "@/keyboardMappings";
export const DetachIcon = ({ className }: { className?: string }) => {
return <img src={DetachIconRaw} alt="Detach Icon" className={className} />;
@ -36,6 +37,8 @@ function KeyboardWrapper() {
const [position, setPosition] = useState({ x: 0, y: 0 });
const [newPosition, setNewPosition] = useState({ x: 0, y: 0 });
const { keyboard } = useKeyboardLayout();
/*
// These will be used to display the currently pressed keys and modifiers on the virtual keyboard
@ -253,29 +256,9 @@ function KeyboardWrapper() {
buttons: "CtrlAltDelete AltMetaEscape CtrlAltBackspace",
},
]}
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 MetaLeft AltLeft Space AltRight MetaRight CtrlRight",
],
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 AltRight MetaRight CtrlRight",
],
}}
display={keyboard.keyDisplayMap}
layout={keyboard.virtualKeyboard.main}
disableButtonHold={true}
syncInstanceInputs={true}
debug={false}
/>
@ -285,26 +268,20 @@ function KeyboardWrapper() {
theme="simple-keyboard hg-theme-default hg-layout-default"
layoutName={layoutName}
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}
display={keyboard.keyDisplayMap}
layout={keyboard.virtualKeyboard.control}
debug={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}
display={keyboard.keyDisplayMap}
layout={keyboard.virtualKeyboard.arrows}
debug={false}
/>
</div>
{ /* TODO add optional number pad */ }
</div>
</div>
</Card>

View File

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

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

@ -2,11 +2,16 @@ 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>,
keyDisplayMap: Record<string, string>,
virtualKeyboard: { main: { default: string[], shift: string[] }, control?: { default: string[], shift?: string[] }, controlSection?: { default: string[], shift?: string[] } }
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
@ -21,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,10 +1,13 @@
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", shift: true },
@ -152,7 +155,11 @@ const chars = {
} as Record<string, KeyCombo>;
export const de_DE: KeyboardLayout = {
isoCode: "de-DE",
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 = "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

@ -125,36 +125,49 @@ export const chars = {
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",
Escape: "Esc",
Tab: "Tab",
Backspace: "Backspace",
"(Backspace)": "Backspace",
Enter: "Enter",
CapsLock: "Caps Lock",
ShiftLeft: "Shift",
ShiftRight: "Shift",
ControlLeft: "Ctrl",
AltGraph: "AltGr",
AltLeft: "Alt",
AltRight: "Alt",
AltGraph: "AltGr",
MetaLeft: "Meta",
MetaRight: "Meta",
Space: " ",
Insert: "Insert",
Home: "Home",
PageUp: "PgUp",
Delete: "Delete",
End: "End",
PageDown: "PgDn",
Clear: "Clear",
ArrowDown: "↓",
ArrowLeft: "←",
ArrowRight: "→",
ArrowUp: "↑",
ArrowDown: "↓",
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",
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",
@ -220,9 +233,9 @@ export const keyDisplayMap: Record<string, string> = {
NumpadDelete: "Del", NumLock: "Num Lock",
// Modals
PrintScreen: "PrtSc", ScrollLock: "Scroll Lock", Pause: "Pause",
"(PrintScreen)": "SysRq", "(Pause)": "Break",
SystemRequest: "SysRq", Break: "Break"
PrintScreen: "Prt Sc", ScrollLock: "Scr Lk", Pause: "Pause",
"(PrintScreen)": "Sys Rq", "(Pause)": "Break",
SystemRequest: "Sys Rq", Break: "Break"
};
export const virtualKeyboard = {
@ -246,7 +259,7 @@ export const virtualKeyboard = {
"ControlLeft MetaLeft AltLeft Space AltGr MetaRight Menu ControlRight",
]
},
controlSection: {
control: {
default: [
"PrintScreen ScrollLock Pause",
"Insert Home PageUp",
@ -259,7 +272,7 @@ export const virtualKeyboard = {
],
},
simpleArrows: {
arrows: {
default: [
"ArrowUp",
"ArrowLeft ArrowDown ArrowRight"],
@ -290,4 +303,6 @@ export const en_US: KeyboardLayout = {
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: keyHacek },
"Ç": { key: "KeyC", shift: true, accentKey: keyCedilla }, // "Ç": { key: "Backslash", shift: true },
c: { key: "KeyC" },
"č": { 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, meta: 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" }, W: { key: "KeyW", shift: true },
"ž": { 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 },
"#": { key: "Digit3", altRight: 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

@ -236,20 +236,22 @@ export const keys = {
} as Record<string, number>;
export const deadKeys = {
0x005e: "Circumflex",
0x02c7: "Caron",
0x00b7: "Dot",
0x02d8: "Breve",
0x002c: "Comma",
0x00b0: "Kreis",
0x00b4: "Acute",
0x00a8: "Umlaut",
0x0060: "Grave",
0x007e: "Tilde",
0x02dd: "DoubleAcute",
0x02db: "Ogonek",
0x00b8: "Cedilla",
} as Record<number, string>
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,
@ -271,116 +273,4 @@ export const hidKeyToModifierMask = {
0xe5: modifiers.ShiftRight,
0xe6: modifiers.AltRight,
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",
AltGr: "AltGr",
} as Record<string, string>;
export const keyDisplayMap: Record<string, string> = {
AltMetaEscape: "Alt + Meta + Escape",
CtrlAltBackspace: "Ctrl + Alt + Backspace",
CtrlAltDelete: "Ctrl + Alt + Delete",
AltGraph: "alt gr",
AltLeft: "alt",
AltRight: "alt",
ArrowDown: "↓",
ArrowLeft: "←",
ArrowRight: "→",
ArrowUp: "↑",
Backspace: "backspace",
"(Backspace)": "backspace",
CapsLock: "caps lock",
ControlLeft: "ctrl",
Delete: "delete",
End: "end",
Enter: "enter",
Escape: "esc",
Home: "home",
Insert: "insert",
MetaLeft: "meta",
MetaRight: "meta",
PageDown: "page down",
PageUp: "page up",
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",
NumLock: "Num Lock",
// Modals
PrintScreen: "prt sc", ScrollLock: "scr lk", Pause: "pause",
"(PrintScreen)": "sys rq", "(Pause)": "break",
SystemRequest: "sys rq", Break: "break"
};
} as Record<number, number>;

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);