Added German (T1) mappings, UK mappings, updated UK apple mappings. Added functionality to disable keyboard mapping.

This commit is contained in:
William Johnstone 2025-02-25 00:44:17 +00:00
parent 8732a6aff8
commit 40b1c70be0
No known key found for this signature in database
GPG Key ID: 89703D0D4B3BB0FE
11 changed files with 279 additions and 78 deletions

View File

@ -12,25 +12,27 @@ type WakeOnLanDevice struct {
} }
type Config struct { type Config struct {
CloudURL string `json:"cloud_url"` CloudURL string `json:"cloud_url"`
CloudToken string `json:"cloud_token"` CloudToken string `json:"cloud_token"`
GoogleIdentity string `json:"google_identity"` GoogleIdentity string `json:"google_identity"`
JigglerEnabled bool `json:"jiggler_enabled"` JigglerEnabled bool `json:"jiggler_enabled"`
AutoUpdateEnabled bool `json:"auto_update_enabled"` AutoUpdateEnabled bool `json:"auto_update_enabled"`
KeyboardLayout string `json:"keyboard_layout"` KeyboardLayout string `json:"keyboard_layout"`
IncludePreRelease bool `json:"include_pre_release"` KeyboardMappingEnabled bool `json:"keyboard_mapping_enabled"`
HashedPassword string `json:"hashed_password"` IncludePreRelease bool `json:"include_pre_release"`
LocalAuthToken string `json:"local_auth_token"` HashedPassword string `json:"hashed_password"`
LocalAuthMode string `json:"localAuthMode"` //TODO: fix it with migration LocalAuthToken string `json:"local_auth_token"`
WakeOnLanDevices []WakeOnLanDevice `json:"wake_on_lan_devices"` LocalAuthMode string `json:"localAuthMode"` //TODO: fix it with migration
WakeOnLanDevices []WakeOnLanDevice `json:"wake_on_lan_devices"`
} }
const configPath = "/userdata/kvm_config.json" const configPath = "/userdata/kvm_config.json"
var defaultConfig = &Config{ var defaultConfig = &Config{
CloudURL: "https://api.jetkvm.com", CloudURL: "https://api.jetkvm.com",
AutoUpdateEnabled: true, // Set a default value AutoUpdateEnabled: true, // Set a default value
KeyboardLayout: "us", KeyboardLayout: "us",
KeyboardMappingEnabled: false,
} }
var config *Config var config *Config

View File

@ -143,6 +143,18 @@ func rpcSetKeyboardLayout(KeyboardLayout string) (string, error) {
return KeyboardLayout, nil return KeyboardLayout, nil
} }
func rpcGetKeyboardMappingState() (bool, error) {
return config.KeyboardMappingEnabled, nil
}
func rpcSetKeyboardMappingState(enabled bool) (bool, error) {
config.KeyboardMappingEnabled = enabled
if err := SaveConfig(); err != nil {
return config.KeyboardMappingEnabled, fmt.Errorf("failed to save config: %w", err)
}
return enabled, nil
}
var streamFactor = 1.0 var streamFactor = 1.0
func rpcGetStreamQualityFactor() (float64, error) { func rpcGetStreamQualityFactor() (float64, error) {
@ -521,51 +533,53 @@ func rpcResetConfig() error {
// TODO: replace this crap with code generator // TODO: replace this crap with code generator
var rpcHandlers = map[string]RPCHandler{ var rpcHandlers = map[string]RPCHandler{
"ping": {Func: rpcPing}, "ping": {Func: rpcPing},
"getDeviceID": {Func: rpcGetDeviceID}, "getDeviceID": {Func: rpcGetDeviceID},
"deregisterDevice": {Func: rpcDeregisterDevice}, "deregisterDevice": {Func: rpcDeregisterDevice},
"getCloudState": {Func: rpcGetCloudState}, "getCloudState": {Func: rpcGetCloudState},
"keyboardReport": {Func: rpcKeyboardReport, Params: []string{"modifier", "keys"}}, "keyboardReport": {Func: rpcKeyboardReport, Params: []string{"modifier", "keys"}},
"absMouseReport": {Func: rpcAbsMouseReport, Params: []string{"x", "y", "buttons"}}, "absMouseReport": {Func: rpcAbsMouseReport, Params: []string{"x", "y", "buttons"}},
"wheelReport": {Func: rpcWheelReport, Params: []string{"wheelY"}}, "wheelReport": {Func: rpcWheelReport, Params: []string{"wheelY"}},
"getVideoState": {Func: rpcGetVideoState}, "getVideoState": {Func: rpcGetVideoState},
"getUSBState": {Func: rpcGetUSBState}, "getUSBState": {Func: rpcGetUSBState},
"unmountImage": {Func: rpcUnmountImage}, "unmountImage": {Func: rpcUnmountImage},
"rpcMountBuiltInImage": {Func: rpcMountBuiltInImage, Params: []string{"filename"}}, "rpcMountBuiltInImage": {Func: rpcMountBuiltInImage, Params: []string{"filename"}},
"setJigglerState": {Func: rpcSetJigglerState, Params: []string{"enabled"}}, "setJigglerState": {Func: rpcSetJigglerState, Params: []string{"enabled"}},
"getJigglerState": {Func: rpcGetJigglerState}, "getJigglerState": {Func: rpcGetJigglerState},
"sendWOLMagicPacket": {Func: rpcSendWOLMagicPacket, Params: []string{"macAddress"}}, "sendWOLMagicPacket": {Func: rpcSendWOLMagicPacket, Params: []string{"macAddress"}},
"getKeyboardLayout": {Func: rpcGetKeyboardLayout}, "getKeyboardLayout": {Func: rpcGetKeyboardLayout},
"setKeyboardLayout": {Func: rpcSetKeyboardLayout, Params: []string{"kbLayout"}}, "setKeyboardLayout": {Func: rpcSetKeyboardLayout, Params: []string{"kbLayout"}},
"getStreamQualityFactor": {Func: rpcGetStreamQualityFactor}, "setKeyboardMappingState": {Func: rpcSetKeyboardMappingState, Params: []string{"enabled"}},
"setStreamQualityFactor": {Func: rpcSetStreamQualityFactor, Params: []string{"factor"}}, "getKeyboardMappingState": {Func: rpcGetKeyboardMappingState},
"getAutoUpdateState": {Func: rpcGetAutoUpdateState}, "getStreamQualityFactor": {Func: rpcGetStreamQualityFactor},
"setAutoUpdateState": {Func: rpcSetAutoUpdateState, Params: []string{"enabled"}}, "setStreamQualityFactor": {Func: rpcSetStreamQualityFactor, Params: []string{"factor"}},
"getEDID": {Func: rpcGetEDID}, "getAutoUpdateState": {Func: rpcGetAutoUpdateState},
"setEDID": {Func: rpcSetEDID, Params: []string{"edid"}}, "setAutoUpdateState": {Func: rpcSetAutoUpdateState, Params: []string{"enabled"}},
"getDevChannelState": {Func: rpcGetDevChannelState}, "getEDID": {Func: rpcGetEDID},
"setDevChannelState": {Func: rpcSetDevChannelState, Params: []string{"enabled"}}, "setEDID": {Func: rpcSetEDID, Params: []string{"edid"}},
"getUpdateStatus": {Func: rpcGetUpdateStatus}, "getDevChannelState": {Func: rpcGetDevChannelState},
"tryUpdate": {Func: rpcTryUpdate}, "setDevChannelState": {Func: rpcSetDevChannelState, Params: []string{"enabled"}},
"getDevModeState": {Func: rpcGetDevModeState}, "getUpdateStatus": {Func: rpcGetUpdateStatus},
"setDevModeState": {Func: rpcSetDevModeState, Params: []string{"enabled"}}, "tryUpdate": {Func: rpcTryUpdate},
"getSSHKeyState": {Func: rpcGetSSHKeyState}, "getDevModeState": {Func: rpcGetDevModeState},
"setSSHKeyState": {Func: rpcSetSSHKeyState, Params: []string{"sshKey"}}, "setDevModeState": {Func: rpcSetDevModeState, Params: []string{"enabled"}},
"setMassStorageMode": {Func: rpcSetMassStorageMode, Params: []string{"mode"}}, "getSSHKeyState": {Func: rpcGetSSHKeyState},
"getMassStorageMode": {Func: rpcGetMassStorageMode}, "setSSHKeyState": {Func: rpcSetSSHKeyState, Params: []string{"sshKey"}},
"isUpdatePending": {Func: rpcIsUpdatePending}, "setMassStorageMode": {Func: rpcSetMassStorageMode, Params: []string{"mode"}},
"getUsbEmulationState": {Func: rpcGetUsbEmulationState}, "getMassStorageMode": {Func: rpcGetMassStorageMode},
"setUsbEmulationState": {Func: rpcSetUsbEmulationState, Params: []string{"enabled"}}, "isUpdatePending": {Func: rpcIsUpdatePending},
"checkMountUrl": {Func: rpcCheckMountUrl, Params: []string{"url"}}, "getUsbEmulationState": {Func: rpcGetUsbEmulationState},
"getVirtualMediaState": {Func: rpcGetVirtualMediaState}, "setUsbEmulationState": {Func: rpcSetUsbEmulationState, Params: []string{"enabled"}},
"getStorageSpace": {Func: rpcGetStorageSpace}, "checkMountUrl": {Func: rpcCheckMountUrl, Params: []string{"url"}},
"mountWithHTTP": {Func: rpcMountWithHTTP, Params: []string{"url", "mode"}}, "getVirtualMediaState": {Func: rpcGetVirtualMediaState},
"mountWithWebRTC": {Func: rpcMountWithWebRTC, Params: []string{"filename", "size", "mode"}}, "getStorageSpace": {Func: rpcGetStorageSpace},
"mountWithStorage": {Func: rpcMountWithStorage, Params: []string{"filename", "mode"}}, "mountWithHTTP": {Func: rpcMountWithHTTP, Params: []string{"url", "mode"}},
"listStorageFiles": {Func: rpcListStorageFiles}, "mountWithWebRTC": {Func: rpcMountWithWebRTC, Params: []string{"filename", "size", "mode"}},
"deleteStorageFile": {Func: rpcDeleteStorageFile, Params: []string{"filename"}}, "mountWithStorage": {Func: rpcMountWithStorage, Params: []string{"filename", "mode"}},
"startStorageFileUpload": {Func: rpcStartStorageFileUpload, Params: []string{"filename", "size"}}, "listStorageFiles": {Func: rpcListStorageFiles},
"getWakeOnLanDevices": {Func: rpcGetWakeOnLanDevices}, "deleteStorageFile": {Func: rpcDeleteStorageFile, Params: []string{"filename"}},
"setWakeOnLanDevices": {Func: rpcSetWakeOnLanDevices, Params: []string{"params"}}, "startStorageFileUpload": {Func: rpcStartStorageFileUpload, Params: []string{"filename", "size"}},
"resetConfig": {Func: rpcResetConfig}, "getWakeOnLanDevices": {Func: rpcGetWakeOnLanDevices},
"setWakeOnLanDevices": {Func: rpcSetWakeOnLanDevices, Params: []string{"params"}},
"resetConfig": {Func: rpcResetConfig},
} }

View File

@ -24,6 +24,7 @@ type SelectMenuProps = Pick<
const sizes = { const sizes = {
XS: "h-[24.5px] pl-3 pr-8 text-xs", XS: "h-[24.5px] pl-3 pr-8 text-xs",
SM: "h-[32px] pl-3 pr-8 text-[13px]", SM: "h-[32px] pl-3 pr-8 text-[13px]",
SM_Wide: "h-[32px] pl-3 pr-8 mr-5 text-[13px]",
MD: "h-[40px] pl-4 pr-10 text-sm", MD: "h-[40px] pl-4 pr-10 text-sm",
LG: "h-[48px] pl-4 pr-10 px-5 text-base", LG: "h-[48px] pl-4 pr-10 px-5 text-base",
}; };

View File

@ -17,6 +17,10 @@ import useKeyboard from "@/hooks/useKeyboard";
import { useJsonRpc } from "@/hooks/useJsonRpc"; import { useJsonRpc } from "@/hooks/useJsonRpc";
import { ConnectionErrorOverlay, HDMIErrorOverlay, LoadingOverlay } from "./VideoOverlay"; import { ConnectionErrorOverlay, HDMIErrorOverlay, LoadingOverlay } from "./VideoOverlay";
// TODO Implement keyboard lock API to resolve #127
// https://developer.chrome.com/docs/capabilities/web-apis/keyboard-lock
// An appropriate error message will need to be displayed in order to alert users to browser compatibility issues.
export default function WebRTCVideo() { export default function WebRTCVideo() {
const [keys, setKeys] = useState(useKeyboardMappingsStore.keys); const [keys, setKeys] = useState(useKeyboardMappingsStore.keys);
const [chars, setChars] = useState(useKeyboardMappingsStore.chars); const [chars, setChars] = useState(useKeyboardMappingsStore.chars);
@ -155,8 +159,6 @@ export default function WebRTCVideo() {
// Invert the scroll value to match expected behavior // Invert the scroll value to match expected behavior
const invertedScroll = -roundedScroll; const invertedScroll = -roundedScroll;
// TODO remove debug logs
console.log("wheelReport", { wheelY: invertedScroll });
send("wheelReport", { wheelY: invertedScroll }); send("wheelReport", { wheelY: invertedScroll });
// TODO this is making scrolling feel slow and sluggish, also throwing a violation in chrome // TODO this is making scrolling feel slow and sluggish, also throwing a violation in chrome
@ -179,7 +181,7 @@ export default function WebRTCVideo() {
// TODO remove debug logging // TODO remove debug logging
console.log(shiftKey + " " +ctrlKey + " " +altKey + " " +metaKey + " " +mappedKeyModifers.shift + " "+mappedKeyModifers.altLeft + " "+mappedKeyModifers.altRight + " ") console.log(shiftKey + " " +ctrlKey + " " +altKey + " " +metaKey + " " +mappedKeyModifers.shift + " "+mappedKeyModifers.altLeft + " "+mappedKeyModifers.altRight + " ")
const filteredModifiers = activeModifiers.filter(Boolean);3 const filteredModifiers = activeModifiers.filter(Boolean);
// Example: activeModifiers = [0x01, 0x02, 0x04, 0x08] // Example: activeModifiers = [0x01, 0x02, 0x04, 0x08]
// Assuming 0x01 = ControlLeft, 0x02 = ShiftLeft, 0x04 = AltLeft, 0x08 = MetaLeft // Assuming 0x01 = ControlLeft, 0x02 = ShiftLeft, 0x04 = AltLeft, 0x08 = MetaLeft
return ( return (
@ -210,8 +212,13 @@ export default function WebRTCVideo() {
modifier => modifier =>
altKey || altKey ||
mappedKeyModifers.altLeft || mappedKeyModifers.altLeft ||
(modifier !== modifiers["AltLeft"]),
)
.filter(
modifier =>
altKey ||
mappedKeyModifers.altRight || mappedKeyModifers.altRight ||
(modifier !== modifiers["AltLeft"] && modifier !== modifiers["AltRight"]), (modifier !== modifiers["AltRight"])
) )
// Meta: Keep if Meta is pressed or if the key isn't a Meta key // Meta: Keep if Meta is pressed or if the key isn't a Meta key
// Example: If metaKey is true, keep all modifiers // Example: If metaKey is true, keep all modifiers
@ -230,8 +237,9 @@ export default function WebRTCVideo() {
async (e: KeyboardEvent) => { async (e: KeyboardEvent) => {
e.preventDefault(); e.preventDefault();
const prev = useHidStore.getState(); const prev = useHidStore.getState();
let code = e.code; const code = e.code;
const localisedKey = e.key; console.log("MAPPING ENABLED: " + settings.keyboardMappingEnabled)
var localisedKey = settings.keyboardMappingEnabled ? e.key : code;
console.log(e); console.log(e);
console.log("Localised Key: " + localisedKey); console.log("Localised Key: " + localisedKey);
@ -282,12 +290,12 @@ export default function WebRTCVideo() {
// event, so we need to clear the keys after a short delay // event, so we need to clear the keys after a short delay
// https://bugs.chromium.org/p/chromium/issues/detail?id=28089 // https://bugs.chromium.org/p/chromium/issues/detail?id=28089
// https://bugzilla.mozilla.org/show_bug.cgi?id=1299553 // https://bugzilla.mozilla.org/show_bug.cgi?id=1299553
// TODO add this to the activekey state
// TODO set this to remove from activekeystate as well
if (e.metaKey) { if (e.metaKey) {
setTimeout(() => { setTimeout(() => {
const prev = useHidStore.getState(); const prev = useHidStore.getState();
sendKeyboardEvent([], newModifiers || prev.activeModifiers); sendKeyboardEvent([], newModifiers || prev.activeModifiers);
activeKeyState.current.delete("MetaLeft");
activeKeyState.current.delete("MetaRight");
}, 10); }, 10);
} }
@ -302,6 +310,7 @@ export default function WebRTCVideo() {
chars, chars,
keys, keys,
modifiers, modifiers,
settings,
], ],
); );

View File

@ -79,7 +79,7 @@ export default function PasteModal() {
} catch (error) { } catch (error) {
notifications.error("Failed to paste text"); notifications.error("Failed to paste text");
} }
}, [rpcDataChannel?.readyState, send, setDisableVideoFocusTrap, setPasteMode]); }, [rpcDataChannel?.readyState, send, setDisableVideoFocusTrap, setPasteMode, chars, keys, modifiers]);
useEffect(() => { useEffect(() => {
if (TextAreaRef.current) { if (TextAreaRef.current) {
@ -144,6 +144,9 @@ export default function PasteModal() {
The following characters won&apos;t be pasted as the current keyboard layout does not contain a valid mapping:{" "} The following characters won&apos;t be pasted as the current keyboard layout does not contain a valid mapping:{" "}
{invalidChars.join(", ")} {invalidChars.join(", ")}
</span> </span>
<span className="text-xs text-red-500 dark:text-red-400">
Tip: You can set your desired keyboard layout in settings, and remember to enable keyboard mapping.
</span>
</div> </div>
)} )}
</div> </div>

View File

@ -79,6 +79,7 @@ export default function SettingsSidebar() {
const settings = useSettingsStore(); const settings = useSettingsStore();
const [send] = useJsonRpc(); const [send] = useJsonRpc();
const [keyboardLayout, setKeyboardLayout] = useState("us"); const [keyboardLayout, setKeyboardLayout] = useState("us");
const [kbMappingEnabled, setKeyboardMapping] = useState(false);
const [streamQuality, setStreamQuality] = useState("1"); const [streamQuality, setStreamQuality] = useState("1");
const [autoUpdate, setAutoUpdate] = useState(true); const [autoUpdate, setAutoUpdate] = useState(true);
const [devChannel, setDevChannel] = useState(false); const [devChannel, setDevChannel] = useState(false);
@ -161,6 +162,20 @@ export default function SettingsSidebar() {
}); });
}; };
const handleKeyboardMappingChange = (enabled: boolean) => {
send("setKeyboardMappingState", { enabled }, resp => {
if ("error" in resp) {
notifications.error(
`Failed to set keyboard maping state state: ${resp.error.data || "Unknown error"}`,
);
return;
}
settings.setkeyboardMappingEnabled(enabled);
useKeyboardMappingsStore.setMappingsState(enabled);
setKeyboardMapping(enabled);
});
};
const handleStreamQualityChange = (factor: string) => { const handleStreamQualityChange = (factor: string) => {
send("setStreamQualityFactor", { factor: Number(factor) }, resp => { send("setStreamQualityFactor", { factor: Number(factor) }, resp => {
if ("error" in resp) { if ("error" in resp) {
@ -295,6 +310,13 @@ export default function SettingsSidebar() {
useKeyboardMappingsStore.setLayout(String(resp.result)) useKeyboardMappingsStore.setLayout(String(resp.result))
}); });
send("getKeyboardMappingState", {}, resp => {
if ("error" in resp) return;
setKeyboardMapping(resp.result as boolean);
settings.setkeyboardMappingEnabled(resp.result as boolean);
useKeyboardMappingsStore.setMappingsState(resp.result as boolean);
});
send("getStreamQualityFactor", {}, resp => { send("getStreamQualityFactor", {}, resp => {
if ("error" in resp) return; if ("error" in resp) return;
setStreamQuality(String(resp.result)); setStreamQuality(String(resp.result));
@ -536,20 +558,32 @@ export default function SettingsSidebar() {
description="Customize keyboard behaviour" description="Customize keyboard behaviour"
/> />
<div className="space-y-4"> <div className="space-y-4">
<SettingsItem
title="Enable Keyboard Mapping"
description="Enables mapping of keys from your native layout to the layout of the target device"
>
<Checkbox
checked={kbMappingEnabled}
onChange={e => {
handleKeyboardMappingChange(e.target.checked);
}}
/>
</SettingsItem>
<SettingsItem <SettingsItem
title="Keyboard Layout" title="Keyboard Layout"
description="Set keyboard layout (this should match the target machine)" description="Set keyboard layout (this should match the target machine)"
> >
<SelectMenuBasic <SelectMenuBasic
size="SM" size="SM_Wide"
label="" label=""
// TODO figure out how to make this selector wider like the EDID one? // TODO figure out how to make this selector wider like the EDID one?, (done but not sure if in desired way.)
//fullWidthƒ //fullWidth
value={keyboardLayout} value={keyboardLayout}
options={[ options={[
{ value: "uk", label: "GB" },
{ value: "uk_apple", label: "GB Apple" },
{ value: "us", label: "US" }, { value: "us", label: "US" },
{ value: "uk", label: "UK" },
{ value: "uk_apple", label: "UK (Apple)" },
{ value: "de_t1", label: "German (T1)" },
]} ]}
onChange={e => handleKeyboardLayoutChange(e.target.value)} onChange={e => handleKeyboardLayoutChange(e.target.value)}
/> />

View File

@ -265,6 +265,9 @@ interface SettingsState {
mouseMode: string; mouseMode: string;
setMouseMode: (mode: string) => void; setMouseMode: (mode: string) => void;
keyboardMappingEnabled: boolean;
setkeyboardMappingEnabled: (enabled: boolean) => void;
debugMode: boolean; debugMode: boolean;
setDebugMode: (enabled: boolean) => void; setDebugMode: (enabled: boolean) => void;
@ -276,6 +279,9 @@ interface SettingsState {
export const useSettingsStore = create( export const useSettingsStore = create(
persist<SettingsState>( persist<SettingsState>(
set => ({ set => ({
keyboardMappingEnabled: false,
setkeyboardMappingEnabled: enabled => set({keyboardMappingEnabled: enabled}),
isCursorHidden: false, isCursorHidden: false,
setCursorVisibility: enabled => set({ isCursorHidden: enabled }), setCursorVisibility: enabled => set({ isCursorHidden: enabled }),
@ -535,18 +541,42 @@ export const useLocalAuthModalStore = create<LocalAuthModalState>(set => ({
class KeyboardMappingsStore { class KeyboardMappingsStore {
private _layout: string = 'us'; private _layout: string = 'us';
private _subscribers: (() => void)[] = []; private _subscribers: (() => void)[] = [];
private _mappingsEnabled: boolean = false;
public keys = getKeyboardMappings(this._layout).keys; public keys = getKeyboardMappings(this._layout).keys;
public chars = getKeyboardMappings(this._layout).chars; public chars = getKeyboardMappings(this._layout).chars;
public modifiers = getKeyboardMappings(this._layout).modifiers; public modifiers = getKeyboardMappings(this._layout).modifiers;
private mappedKeys = getKeyboardMappings(this._layout).keys;
private mappedChars = getKeyboardMappings(this._layout).chars;
private mappedModifiers = getKeyboardMappings(this._layout).modifiers;
setLayout(newLayout: string) { setLayout(newLayout: string) {
if (this._layout === newLayout) return; if (this._layout === newLayout) return;
this._layout = newLayout; this._layout = newLayout;
const updatedMappings = getKeyboardMappings(newLayout); const updatedMappings = getKeyboardMappings(newLayout);
this.keys = updatedMappings.keys; this.mappedKeys = updatedMappings.keys;
this.chars = updatedMappings.chars; this.mappedChars = updatedMappings.chars;
this.modifiers = updatedMappings.modifiers; this.mappedModifiers = updatedMappings.modifiers;
if (this._mappingsEnabled) {
this.keys = this.mappedKeys;
this.chars = this.mappedChars;
this.modifiers = this.mappedModifiers;
this._notifySubscribers();
}
}
setMappingsState(enabled: boolean) {
this._mappingsEnabled = enabled;
if (this._mappingsEnabled) {
this.keys = this.mappedKeys;
this.chars = this.mappedChars;
this.modifiers = this.mappedModifiers;
} else {
this.keys = getKeyboardMappings('us').keys;
this.chars = getKeyboardMappings('us').chars;
this.modifiers = getKeyboardMappings('us').modifiers;
}
this._notifySubscribers(); this._notifySubscribers();
} }

View File

@ -1,5 +1,7 @@
import {keysUKApple, charsUKApple, modifiersUKApple } from './layouts/uk_apple'; import {keysUKApple, charsUKApple, modifiersUKApple } from './layouts/uk_apple';
import {keysUK, charsUK, modifiersUK } from './layouts/uk';
import {keysUS, charsUS, modifiersUS } from './layouts/us'; import {keysUS, charsUS, modifiersUS } from './layouts/us';
import { keysDE_T1, charsDE_T1, modifiersDE_T1 } from './layouts/de_t1';
export function getKeyboardMappings(layout: string) { export function getKeyboardMappings(layout: string) {
switch (layout) { switch (layout) {
@ -9,6 +11,18 @@ export function getKeyboardMappings(layout: string) {
chars: charsUKApple, chars: charsUKApple,
modifiers: modifiersUKApple, modifiers: modifiersUKApple,
}; };
case "uk":
return {
keys: keysUK,
chars: charsUK,
modifiers: modifiersUK,
};
case "de_t1":
return {
keys: keysDE_T1,
chars: charsDE_T1,
modifiers: modifiersDE_T1,
};
case "us": case "us":
default: default:
return { return {

View File

@ -0,0 +1,69 @@
import { charsUS, keysUS, modifiersUS } from "./us";
export const keysDE_T1 = {
...keysUS,
} as Record<string, number>;
export const charsDE_T1 = {
...charsUS,
"y": { key: "KeyZ", shift: false },
"Y": { key: "KeyZ", shift: true },
"z": { key: "KeyY", shift: false },
"Z": { key: "KeyY", shift: true },
"ä": { key: "Quote", shift: false },
"Ä": { key: "Quote", shift: true },
"ö": { key: "Semicolon", shift: false },
"Ö": { key: "Semicolon", shift: true },
"ü": { key: "BracketLeft", shift: false },
"Ü": { key: "BracketLeft", shift: true },
"ß": { key: "Minus", shift: false },
"?": { key: "Minus", shift: true },
"§": { key: "Digit3", shift: true },
"°": { key: "Backquote", shift: true },
"@": { key: "KeyQ", shift: false, altRight: true },
"\"": { key: "Digit2", shift: true },
"#": { key: "Backslash", shift: false },
"'": { key: "Backslash", shift: true },
".": { key: "Period", shift: false },
":": { key: "Period", shift: true },
",": { key: "Comma", shift: false },
";": { key: "Comma", shift: true },
"-": { key: "Slash", shift: false },
"_": { key: "Slash", shift: true },
"*": { key: "BracketRight", shift: true },
"+": { key: "BracketRight", shift: false },
"=": { key: "Digit0", shift: true },
"~": { key: "BracketRight", shift: false, altRight: true },
"{": { key: "Digit7", shift: false, altRight: true },
"}": { key: "Digit0", shift: false, altRight: true },
"[": { key: "Digit8", shift: false, altRight: true },
"]": { key: "Digit9", shift: false, altRight: true },
"\\": { key: "Minus", shift: false, altRight: true },
"|": { key: "IntlBackslash", shift: true, altRight: true },
"<": { key: "IntlBackslash", shift: false },
">": { key: "IntlBackslash", shift: true },
"^": {key: "Backquote", shift: false},
"€": { key: "KeyE", shift: false, altRight: true },
"²": {key: "Digit2", shift: false, altRight: true },
"³": {key: "Digit3", shift: false, altRight: true },
"μ": {key: "KeyM", shift: false, altRight: true },
} as Record<string, { key: string; shift: boolean; altLeft?: boolean; altRight?: boolean }>;
export const modifiersDE_T1 = {
...modifiersUS,
} as Record<string, number>;

View File

@ -0,0 +1,24 @@
import { charsUS, keysUS, modifiersUS } from "./us";
export const keysUK = {
...keysUS,
} as Record<string, number>;
export const charsUK = {
...charsUS,
"`": { key: "Backquote", shift: false },
"~": { key: "Backslash", shift: true },
"\\": { key: "IntlBacklash", shift: false },
"|": { key: "IntlBacklash", shift: true },
"#": { key: "Backslash", shift: false },
"£": { key: "Digit3", shift: true },
"@": { key: "Quote", shift: true },
"\"": { key: "Digit2", shift: true },
"¬": { key: "Backquote", shift: true },
"¦": { key: "Backquote", shift: false, altRight: true },
"€": { key: "Digit4", shift: false, altRight: true },
} as Record<string, { key: string; shift: boolean; altLeft?: boolean; altRight?: boolean; }>;
export const modifiersUK = {
...modifiersUS,
} as Record<string, number>;

View File

@ -16,6 +16,7 @@ export const charsUKApple = {
"£": { key: "Digit3", shift: true }, "£": { key: "Digit3", shift: true },
"@": { key: "Digit2", shift: true }, "@": { key: "Digit2", shift: true },
"\"": { key: "Quote", shift: true }, "\"": { key: "Quote", shift: true },
"¬": { key: "KeyL", shift: false, altLeft: true},
} as Record<string, { key: string; shift: boolean; altLeft?: boolean; altRight?: boolean; }>; } as Record<string, { key: string; shift: boolean; altLeft?: boolean; altRight?: boolean; }>;
// Modifiers are typically the same between UK and US layouts // Modifiers are typically the same between UK and US layouts