From 22849fceab304d314e6e849ee5120b5ea2d39101 Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Fri, 2 May 2025 00:52:03 +0200 Subject: [PATCH 01/29] Enable multiple keyboard layouts for paste text from host --- config.go | 2 + jsonrpc.go | 2 + ui/src/components/popovers/PasteModal.tsx | 38 ++++- ui/src/hooks/stores.ts | 3 + ui/src/keyboardLayouts.ts | 12 ++ ui/src/keyboardLayouts/de_CH.ts | 140 ++++++++++++++++++ ui/src/keyboardLayouts/en_US.ts | 109 ++++++++++++++ ui/src/keyboardMappings.ts | 112 +------------- ui/src/main.tsx | 15 +- .../routes/devices.$id.settings.keyboard.tsx | 77 ++++++++++ ui/src/routes/devices.$id.settings.mouse.tsx | 2 +- ui/src/routes/devices.$id.settings.tsx | 15 +- 12 files changed, 406 insertions(+), 121 deletions(-) create mode 100644 ui/src/keyboardLayouts.ts create mode 100644 ui/src/keyboardLayouts/de_CH.ts create mode 100644 ui/src/keyboardLayouts/en_US.ts create mode 100644 ui/src/routes/devices.$id.settings.keyboard.tsx diff --git a/config.go b/config.go index 196a73d..e699ff3 100644 --- a/config.go +++ b/config.go @@ -87,6 +87,7 @@ type Config struct { LocalAuthMode string `json:"localAuthMode"` //TODO: fix it with migration WakeOnLanDevices []WakeOnLanDevice `json:"wake_on_lan_devices"` KeyboardMacros []KeyboardMacro `json:"keyboard_macros"` + KeyboardLayout string `json:"keyboard_layout"` EdidString string `json:"hdmi_edid_string"` ActiveExtension string `json:"active_extension"` DisplayRotation string `json:"display_rotation"` @@ -109,6 +110,7 @@ var defaultConfig = &Config{ ActiveExtension: "", KeyboardMacros: []KeyboardMacro{}, DisplayRotation: "270", + KeyboardLayout: "en-US", DisplayMaxBrightness: 64, DisplayDimAfterSec: 120, // 2 minutes DisplayOffAfterSec: 1800, // 30 minutes diff --git a/jsonrpc.go b/jsonrpc.go index db9b680..cb06fa3 100644 --- a/jsonrpc.go +++ b/jsonrpc.go @@ -1042,6 +1042,8 @@ var rpcHandlers = map[string]RPCHandler{ "setUsbDevices": {Func: rpcSetUsbDevices, Params: []string{"devices"}}, "setUsbDeviceState": {Func: rpcSetUsbDeviceState, Params: []string{"device", "enabled"}}, "setCloudUrl": {Func: rpcSetCloudUrl, Params: []string{"apiUrl", "appUrl"}}, + "getKeyboardLayout": {Func: rpcGetKeyboardLayout}, + "setKeyboardLayout": {Func: rpcSetKeyboardLayout, Params: []string{"layout"}}, "getKeyboardMacros": {Func: getKeyboardMacros}, "setKeyboardMacros": {Func: setKeyboardMacros, Params: []string{"params"}}, } diff --git a/ui/src/components/popovers/PasteModal.tsx b/ui/src/components/popovers/PasteModal.tsx index 6f234a8..25dfff0 100644 --- a/ui/src/components/popovers/PasteModal.tsx +++ b/ui/src/components/popovers/PasteModal.tsx @@ -8,8 +8,9 @@ import { GridCard } from "@components/Card"; import { TextAreaWithLabel } from "@components/TextArea"; import { SettingsPageHeader } from "@components/SettingsPageheader"; import { useJsonRpc } from "@/hooks/useJsonRpc"; -import { useHidStore, useRTCStore, useUiStore } from "@/hooks/stores"; -import { chars, keys, modifiers } from "@/keyboardMappings"; +import { useHidStore, useRTCStore, useUiStore, useDeviceSettingsStore } from "@/hooks/stores"; +import { keys, modifiers } from "@/keyboardMappings"; +import { layouts, chars } from "@/keyboardLayouts"; import notifications from "@/notifications"; const hidKeyboardPayload = (keys: number[], modifier: number) => { @@ -27,6 +28,11 @@ export default function PasteModal() { const [invalidChars, setInvalidChars] = useState([]); const close = useClose(); + const keyboardLayout = useDeviceSettingsStore(state => state.keyboardLayout); + const setKeyboardLayout = useDeviceSettingsStore( + state => state.setKeyboardLayout, + ); + const onCancelPasteMode = useCallback(() => { setPasteMode(false); setDisableVideoFocusTrap(false); @@ -42,13 +48,25 @@ export default function PasteModal() { try { for (const char of text) { - const { key, shift } = chars[char] ?? {}; + const { key, shift, altRight, space, capsLock } = chars[keyboardLayout][char] ?? {}; if (!key) continue; + const keyz = [keys[key]]; + if (space) { + keyz.push(keys["Space"]); + } + if (capsLock) { + keyz.unshift(keys["CapsLock"]); + keyz.push(keys["CapsLock"]); + } + + const modz = shift ? modifiers["ShiftLeft"] : 0 + | (altRight ? modifiers["AltRight"] : 0); + await new Promise((resolve, reject) => { send( "keyboardReport", - hidKeyboardPayload([keys[key]], shift ? modifiers["ShiftLeft"] : 0), + hidKeyboardPayload(keyz, modz), params => { if ("error" in params) return reject(params.error); send("keyboardReport", hidKeyboardPayload([], 0), params => { @@ -69,6 +87,11 @@ export default function PasteModal() { if (TextAreaRef.current) { TextAreaRef.current.focus(); } + + send("getKeyboardLayout", {}, resp => { + if ("error" in resp) return; + setKeyboardLayout(resp.result as string); + }); }, []); return ( @@ -113,7 +136,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 => !chars[char]), + .filter(char => !chars[keyboardLayout][char]), ), ]; @@ -132,6 +155,11 @@ export default function PasteModal() { )} +
+

+ Sending key codes for keyboard layout {layouts[keyboardLayout]} +

+
diff --git a/ui/src/hooks/stores.ts b/ui/src/hooks/stores.ts index 147f285..e9a9a88 100644 --- a/ui/src/hooks/stores.ts +++ b/ui/src/hooks/stores.ts @@ -347,6 +347,8 @@ export interface DeviceSettingsState { trackpadThreshold: number; scrollSensitivity: "low" | "default" | "high"; setScrollSensitivity: (sensitivity: DeviceSettingsState["scrollSensitivity"]) => void; + keyboardLayout: string; + setKeyboardLayout: (layout: string) => void; } export interface RemoteVirtualMediaState { @@ -879,4 +881,5 @@ export const useMacrosStore = create((set, get) => ({ set({ loading: false }); } }, + } })); diff --git a/ui/src/keyboardLayouts.ts b/ui/src/keyboardLayouts.ts new file mode 100644 index 0000000..15c3be4 --- /dev/null +++ b/ui/src/keyboardLayouts.ts @@ -0,0 +1,12 @@ +import { chars as chars_en_US } from "@/keyboardLayouts/en_US" +import { chars as chars_de_CH } from "@/keyboardLayouts/de_CH" + +export const layouts = { + "en_US": "English (US)", + "de_CH": "Swiss German" +} as Record; + +export const chars = { + "en_US": chars_en_US, + "de_CH": chars_de_CH, +} as Record>; diff --git a/ui/src/keyboardLayouts/de_CH.ts b/ui/src/keyboardLayouts/de_CH.ts new file mode 100644 index 0000000..405feb8 --- /dev/null +++ b/ui/src/keyboardLayouts/de_CH.ts @@ -0,0 +1,140 @@ +export const chars = { + A: { key: "KeyA", shift: true }, + B: { key: "KeyB", shift: true }, + C: { key: "KeyC", shift: true }, + D: { key: "KeyD", shift: true }, + E: { key: "KeyE", shift: true }, + F: { key: "KeyF", shift: true }, + G: { key: "KeyG", shift: true }, + H: { key: "KeyH", shift: true }, + I: { key: "KeyI", shift: true }, + 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 }, + 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 }, + 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", altRight: true }, + b: { key: "KeyB" }, + c: { key: "KeyC" }, + d: { key: "KeyD" }, + "ð": { key: "KeyD", altRight: true }, + e: { key: "KeyE" }, + f: { key: "KeyF" }, + "đ": { key: "KeyF", altRight: true }, + g: { key: "KeyG" }, + "ŋ": { key: "KeyG", altRight: true }, + h: { key: "KeyH" }, + "ħ": { key: "KeyH", altRight: true }, + i: { key: "KeyI" }, + "→": { key: "KeyI", altRight: true }, + j: { key: "KeyJ" }, + k: { key: "KeyK" }, + "ĸ": { key: "KeyK", altRight: true }, + l: { key: "KeyL" }, + "ł": { key: "KeyL", altRight: true }, + m: { key: "KeyM" }, + "µ": { key: "KeyM", altRight: true }, + n: { key: "KeyN" }, + o: { key: "KeyO" }, + "œ": { key: "KeyO", altRight: true }, + p: { key: "KeyP" }, + "þ": { key: "KeyP", altRight: true }, + q: { key: "KeyQ" }, + r: { key: "KeyR" }, + "¶": { key: "KeyR", altRight: true }, + s: { key: "KeyS" }, + "ß": { key: "KeyS", altRight: true }, + t: { key: "KeyT" }, + "ŧ": { key: "KeyT", altRight: true }, + u: { key: "KeyU" }, + "↓": { key: "KeyU", altRight: true }, + v: { key: "KeyV" }, + "„": { key: "KeyV", altRight: true }, + w: { key: "KeyW" }, + "ſ": { key: "KeyW", altRight: true }, + x: { key: "KeyX" }, + "»": { key: "KeyX", altRight: true }, + y: { key: "KeyZ" }, + "←": { key: "KeyZ", altRight: true }, + z: { key: "KeyY" }, + "«": { key: "KeyY", altRight: true }, + "§": { key: "Backquote" }, + "°": { key: "Backquote", shift: 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 }, + "¼": { key: "Digit4", altRight: true }, + 5: { key: "Digit5" }, + "%": { key: "Digit5", shift: true }, + "½": { key: "Digit5", altRight: 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 }, + "¢": { key: "Digit8", altRight: true }, + 9: { key: "Digit9" }, + ")": { key: "Digit9", shift: true }, + 0: { key: "Digit0" }, + "=": { key: "Digit0", shift: true }, + "'": { key: "Minus" }, + "?": { key: "Minus", shift: true }, + "^": { key: "Equal", space: true }, // dead key + "`": { key: "Equal", shift: true }, + "~": { key: "Equal", altRight: true, space: true }, // dead key + "ü": { key: "BracketLeft" }, + "è": { key: "BracketLeft", shift: true }, + "[": { key: "BracketLeft", altRight: true }, + "Ü": { key: "BracketLeft", capsLock: true }, + "!": { key: "BracketRight", shift: true }, + "]": { key: "BracketRight", altRight: true }, + "ö": { key: "Semicolon" }, + "é": { key: "Semicolon", shift: true }, + "Ö": { key: "Semicolon", capsLock: true }, + "ä": { key: "Quote" }, + "à": { key: "Quote", shift: true }, + "{": { key: "Quote", altRight: true }, + "Ä": { key: "Quote", capsLock: true }, + "$": { key: "Backslash" }, + "£": { key: "Backslash", shift: true }, + "}": { key: "Backslash", altRight: true }, + ",": { key: "Comma" }, + ";": { key: "Comma", shift: true }, + "•": { key: "Comma", altRight: true }, + ".": { key: "Period" }, + ":": { key: "Period", shift: true }, + "·": { key: "Period", altRight: true }, + "-": { key: "Slash" }, + "_": { key: "Slash", shift: true }, + "<": { key: "IntlBackslash" }, + ">": { key: "IntlBackslash", shift: true }, + "\\": { key: "IntlBackslash", altRight: true }, + "€": { key: "KeyE", altRight: true }, + " ": { key: "Space" }, + "\n": { key: "Enter" }, + Enter: { key: "Enter" }, + Tab: { key: "Tab" }, +} as Record diff --git a/ui/src/keyboardLayouts/en_US.ts b/ui/src/keyboardLayouts/en_US.ts new file mode 100644 index 0000000..65da319 --- /dev/null +++ b/ui/src/keyboardLayouts/en_US.ts @@ -0,0 +1,109 @@ +export const chars = { + A: { key: "KeyA", shift: true }, + B: { key: "KeyB", shift: true }, + C: { key: "KeyC", shift: true }, + D: { key: "KeyD", shift: true }, + E: { key: "KeyE", shift: true }, + F: { key: "KeyF", shift: true }, + G: { key: "KeyG", shift: true }, + H: { key: "KeyH", shift: true }, + I: { key: "KeyI", shift: true }, + 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 }, + 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 }, + V: { key: "KeyV", shift: true }, + W: { key: "KeyW", shift: true }, + X: { key: "KeyX", shift: true }, + Y: { key: "KeyY", shift: true }, + Z: { key: "KeyZ", shift: true }, + a: { key: "KeyA", shift: false }, + b: { key: "KeyB", shift: false }, + c: { key: "KeyC", shift: false }, + d: { key: "KeyD", shift: false }, + e: { key: "KeyE", shift: false }, + f: { key: "KeyF", shift: false }, + g: { key: "KeyG", shift: false }, + h: { key: "KeyH", shift: false }, + i: { key: "KeyI", shift: false }, + j: { key: "KeyJ", shift: false }, + k: { key: "KeyK", shift: false }, + l: { key: "KeyL", shift: false }, + m: { key: "KeyM", shift: false }, + n: { key: "KeyN", shift: false }, + o: { key: "KeyO", shift: false }, + p: { key: "KeyP", shift: false }, + q: { key: "KeyQ", shift: false }, + r: { key: "KeyR", shift: false }, + s: { key: "KeyS", shift: false }, + t: { key: "KeyT", shift: false }, + u: { key: "KeyU", shift: false }, + v: { key: "KeyV", shift: false }, + w: { key: "KeyW", shift: false }, + x: { key: "KeyX", shift: false }, + y: { key: "KeyY", shift: false }, + z: { key: "KeyZ", shift: false }, + 1: { key: "Digit1", shift: false }, + "!": { key: "Digit1", shift: true }, + 2: { key: "Digit2", shift: false }, + "@": { key: "Digit2", shift: true }, + 3: { key: "Digit3", shift: false }, + "#": { key: "Digit3", shift: true }, + 4: { key: "Digit4", shift: false }, + $: { key: "Digit4", shift: true }, + "%": { key: "Digit5", shift: true }, + 5: { key: "Digit5", shift: false }, + "^": { key: "Digit6", shift: true }, + 6: { key: "Digit6", shift: false }, + "&": { key: "Digit7", shift: true }, + 7: { key: "Digit7", shift: false }, + "*": { key: "Digit8", shift: true }, + 8: { key: "Digit8", shift: false }, + "(": { key: "Digit9", shift: true }, + 9: { key: "Digit9", shift: false }, + ")": { key: "Digit0", shift: true }, + 0: { key: "Digit0", shift: false }, + "-": { key: "Minus", shift: false }, + _: { key: "Minus", shift: true }, + "=": { key: "Equal", shift: false }, + "+": { key: "Equal", shift: true }, + "'": { key: "Quote", shift: false }, + '"': { key: "Quote", shift: true }, + ",": { key: "Comma", shift: false }, + "<": { key: "Comma", shift: true }, + "/": { key: "Slash", shift: false }, + "?": { key: "Slash", shift: true }, + ".": { key: "Period", shift: false }, + ">": { key: "Period", shift: true }, + ";": { key: "Semicolon", shift: false }, + ":": { key: "Semicolon", shift: true }, + "[": { key: "BracketLeft", shift: false }, + "{": { key: "BracketLeft", shift: true }, + "]": { key: "BracketRight", shift: false }, + "}": { key: "BracketRight", shift: true }, + "\\": { key: "Backslash", shift: false }, + "|": { key: "Backslash", shift: true }, + "`": { key: "Backquote", shift: false }, + "~": { key: "Backquote", shift: true }, + "§": { key: "IntlBackslash", shift: false }, + "±": { 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 }, + SystemRequest: { key: "Prt Sc", shift: true }, + ScrollLock: { key: "ScrollLock", shift: false}, + Pause: { key: "Pause", shift: false }, + Break: { key: "Pause", shift: true }, + Insert: { key: "Insert", shift: false }, + Delete: { key: "Delete", shift: false }, +} as Record diff --git a/ui/src/keyboardMappings.ts b/ui/src/keyboardMappings.ts index d191f50..891b96e 100644 --- a/ui/src/keyboardMappings.ts +++ b/ui/src/keyboardMappings.ts @@ -43,7 +43,7 @@ export const keys = { F13: 0x68, Home: 0x4a, Insert: 0x49, - IntlBackslash: 0x31, + IntlBackslash: 0x64, KeyA: 0x04, KeyB: 0x05, KeyC: 0x06, @@ -104,116 +104,6 @@ export const keys = { Tab: 0x2b, } as Record; -export const chars = { - A: { key: "KeyA", shift: true }, - B: { key: "KeyB", shift: true }, - C: { key: "KeyC", shift: true }, - D: { key: "KeyD", shift: true }, - E: { key: "KeyE", shift: true }, - F: { key: "KeyF", shift: true }, - G: { key: "KeyG", shift: true }, - H: { key: "KeyH", shift: true }, - I: { key: "KeyI", shift: true }, - 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 }, - 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 }, - V: { key: "KeyV", shift: true }, - W: { key: "KeyW", shift: true }, - X: { key: "KeyX", shift: true }, - Y: { key: "KeyY", shift: true }, - Z: { key: "KeyZ", shift: true }, - a: { key: "KeyA", shift: false }, - b: { key: "KeyB", shift: false }, - c: { key: "KeyC", shift: false }, - d: { key: "KeyD", shift: false }, - e: { key: "KeyE", shift: false }, - f: { key: "KeyF", shift: false }, - g: { key: "KeyG", shift: false }, - h: { key: "KeyH", shift: false }, - i: { key: "KeyI", shift: false }, - j: { key: "KeyJ", shift: false }, - k: { key: "KeyK", shift: false }, - l: { key: "KeyL", shift: false }, - m: { key: "KeyM", shift: false }, - n: { key: "KeyN", shift: false }, - o: { key: "KeyO", shift: false }, - p: { key: "KeyP", shift: false }, - q: { key: "KeyQ", shift: false }, - r: { key: "KeyR", shift: false }, - s: { key: "KeyS", shift: false }, - t: { key: "KeyT", shift: false }, - u: { key: "KeyU", shift: false }, - v: { key: "KeyV", shift: false }, - w: { key: "KeyW", shift: false }, - x: { key: "KeyX", shift: false }, - y: { key: "KeyY", shift: false }, - z: { key: "KeyZ", shift: false }, - 1: { key: "Digit1", shift: false }, - "!": { key: "Digit1", shift: true }, - 2: { key: "Digit2", shift: false }, - "@": { key: "Digit2", shift: true }, - 3: { key: "Digit3", shift: false }, - "#": { key: "Digit3", shift: true }, - 4: { key: "Digit4", shift: false }, - $: { key: "Digit4", shift: true }, - "%": { key: "Digit5", shift: true }, - 5: { key: "Digit5", shift: false }, - "^": { key: "Digit6", shift: true }, - 6: { key: "Digit6", shift: false }, - "&": { key: "Digit7", shift: true }, - 7: { key: "Digit7", shift: false }, - "*": { key: "Digit8", shift: true }, - 8: { key: "Digit8", shift: false }, - "(": { key: "Digit9", shift: true }, - 9: { key: "Digit9", shift: false }, - ")": { key: "Digit0", shift: true }, - 0: { key: "Digit0", shift: false }, - "-": { key: "Minus", shift: false }, - _: { key: "Minus", shift: true }, - "=": { key: "Equal", shift: false }, - "+": { key: "Equal", shift: true }, - "'": { key: "Quote", shift: false }, - '"': { key: "Quote", shift: true }, - ",": { key: "Comma", shift: false }, - "<": { key: "Comma", shift: true }, - "/": { key: "Slash", shift: false }, - "?": { key: "Slash", shift: true }, - ".": { key: "Period", shift: false }, - ">": { key: "Period", shift: true }, - ";": { key: "Semicolon", shift: false }, - ":": { key: "Semicolon", shift: true }, - "[": { key: "BracketLeft", shift: false }, - "{": { key: "BracketLeft", shift: true }, - "]": { key: "BracketRight", shift: false }, - "}": { key: "BracketRight", shift: true }, - "\\": { key: "Backslash", shift: false }, - "|": { key: "Backslash", shift: true }, - "`": { key: "Backquote", shift: false }, - "~": { key: "Backquote", shift: true }, - "§": { key: "IntlBackslash", shift: false }, - "±": { 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 }, - SystemRequest: { key: "Prt Sc", shift: true }, - ScrollLock: { key: "ScrollLock", shift: false}, - Pause: { key: "Pause", shift: false }, - Break: { key: "Pause", shift: true }, - Insert: { key: "Insert", shift: false }, - Delete: { key: "Delete", shift: false }, -} as Record; - export const modifiers = { ControlLeft: 0x01, ControlRight: 0x10, diff --git a/ui/src/main.tsx b/ui/src/main.tsx index cbd5e25..e3badd1 100644 --- a/ui/src/main.tsx +++ b/ui/src/main.tsx @@ -32,7 +32,8 @@ import { CLOUD_API, DEVICE_API } from "./ui.config"; import OtherSessionRoute from "./routes/devices.$id.other-session"; import MountRoute from "./routes/devices.$id.mount"; import * as SettingsRoute from "./routes/devices.$id.settings"; -import SettingsKeyboardMouseRoute from "./routes/devices.$id.settings.mouse"; +import SettingsMouseRoute from "./routes/devices.$id.settings.mouse"; +import SettingsKeyboardRoute from "./routes/devices.$id.settings.keyboard"; import api from "./api"; import * as SettingsIndexRoute from "./routes/devices.$id.settings._index"; import SettingsAdvancedRoute from "./routes/devices.$id.settings.advanced"; @@ -147,7 +148,11 @@ if (isOnDevice) { }, { path: "mouse", - element: , + element: , + }, + { + path: "keyboard", + element: , }, { path: "advanced", @@ -276,7 +281,11 @@ if (isOnDevice) { }, { path: "mouse", - element: , + element: , + }, + { + path: "keyboard", + element: , }, { path: "advanced", diff --git a/ui/src/routes/devices.$id.settings.keyboard.tsx b/ui/src/routes/devices.$id.settings.keyboard.tsx new file mode 100644 index 0000000..b4981af --- /dev/null +++ b/ui/src/routes/devices.$id.settings.keyboard.tsx @@ -0,0 +1,77 @@ +import { useCallback, useEffect } from "react"; + +import { useDeviceSettingsStore } from "@/hooks/stores"; +import { useJsonRpc } from "@/hooks/useJsonRpc"; +import notifications from "@/notifications"; +import { SettingsPageHeader } from "@components/SettingsPageheader"; +import { layouts } from "@/keyboardLayouts"; + +import { FeatureFlag } from "../components/FeatureFlag"; +import { SelectMenuBasic } from "../components/SelectMenuBasic"; + +import { SettingsItem } from "./devices.$id.settings"; + +export default function SettingsKeyboardRoute() { + const keyboardLayout = useDeviceSettingsStore(state => state.keyboardLayout); + const setKeyboardLayout = useDeviceSettingsStore( + state => state.setKeyboardLayout, + ); + + const layoutOptions = Object.entries(layouts).map(([code, language]) => { return { value: code, label: language } }) + + const [send] = useJsonRpc(); + + useEffect(() => { + send("getKeyboardLayout", {}, resp => { + if ("error" in resp) return; + setKeyboardLayout(resp.result as string); + }); + }, []); + + const onKeyboardLayoutChange = useCallback( + (e: React.ChangeEvent) => { + const layout = e.target.value; + send("setKeyboardLayout", { layout }, 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); + }); + }, + [send, setKeyboardLayout], + ); + + return ( +
+ + +
+ + { /* this menu item could be renamed to plain "Keyboard layout" in the future, when also the virtual keyboard layout mappings are being implemented */ } + + + +

+ Pasting text sends individual key strokes to the target device. The keyboard layout determines which key codes are being sent. Ensure that the keyboard layout in JetKVM matches the settings in the operating system. +

+
+
+
+ ); +} diff --git a/ui/src/routes/devices.$id.settings.mouse.tsx b/ui/src/routes/devices.$id.settings.mouse.tsx index 8d88a2b..0c710ce 100644 --- a/ui/src/routes/devices.$id.settings.mouse.tsx +++ b/ui/src/routes/devices.$id.settings.mouse.tsx @@ -15,7 +15,7 @@ import { cx } from "../cva.config"; import { SettingsItem } from "./devices.$id.settings"; -export default function SettingsKeyboardMouseRoute() { +export default function SettingsMouseRoute() { const hideCursor = useSettingsStore(state => state.isCursorHidden); const setHideCursor = useSettingsStore(state => state.setCursorVisibility); diff --git a/ui/src/routes/devices.$id.settings.tsx b/ui/src/routes/devices.$id.settings.tsx index 3d28dd5..05c3c9d 100644 --- a/ui/src/routes/devices.$id.settings.tsx +++ b/ui/src/routes/devices.$id.settings.tsx @@ -1,6 +1,7 @@ import { NavLink, Outlet, useLocation } from "react-router-dom"; import { LuSettings, + LuMouse, LuKeyboard, LuVideo, LuCpu, @@ -149,11 +150,23 @@ export default function SettingsRoute() { className={({ isActive }) => (isActive ? "active" : "")} >
- + +

Mouse

+
+ (isActive ? "active" : "")} + > +
+ +

Keyboard

+
+
+
Date: Fri, 2 May 2025 01:32:12 +0200 Subject: [PATCH 02/29] Trema is the more robust method for capital umlauts --- ui/src/components/popovers/PasteModal.tsx | 44 ++++++++++++++--------- ui/src/keyboardLayouts.ts | 2 +- ui/src/keyboardLayouts/de_CH.ts | 8 ++--- 3 files changed, 32 insertions(+), 22 deletions(-) diff --git a/ui/src/components/popovers/PasteModal.tsx b/ui/src/components/popovers/PasteModal.tsx index 25dfff0..9e2258c 100644 --- a/ui/src/components/popovers/PasteModal.tsx +++ b/ui/src/components/popovers/PasteModal.tsx @@ -48,34 +48,44 @@ export default function PasteModal() { try { for (const char of text) { - const { key, shift, altRight, space, capsLock } = chars[keyboardLayout][char] ?? {}; + const { key, shift, altRight, space, capsLock, trema } = chars[keyboardLayout][char] ?? {}; if (!key) continue; - const keyz = [keys[key]]; + const keyz = [ keys[key] ]; + const modz = [ shift ? modifiers["ShiftLeft"] : 0 + | (altRight ? modifiers["AltRight"] : 0) ]; + if (space) { keyz.push(keys["Space"]); + modz.push(0); } if (capsLock) { keyz.unshift(keys["CapsLock"]); + modz.unshift(0); + keyz.push(keys["CapsLock"]); + modz.push(0); + } + if (trema) { + keyz.unshift(keys["BracketRight"]); // trema ¨ + modz.unshift(0) } - const modz = shift ? modifiers["ShiftLeft"] : 0 - | (altRight ? modifiers["AltRight"] : 0); - - await new Promise((resolve, reject) => { - send( - "keyboardReport", - hidKeyboardPayload(keyz, modz), - params => { - if ("error" in params) return reject(params.error); - send("keyboardReport", hidKeyboardPayload([], 0), params => { + for (const [index, keyy] of keyz.entries()) { + await new Promise((resolve, reject) => { + send( + "keyboardReport", + hidKeyboardPayload([keyy], modz[index]), + params => { if ("error" in params) return reject(params.error); - resolve(); - }); - }, - ); - }); + send("keyboardReport", hidKeyboardPayload([], 0), params => { + if ("error" in params) return reject(params.error); + resolve(); + }); + }, + ); + }); + } } } catch (error) { console.error(error); diff --git a/ui/src/keyboardLayouts.ts b/ui/src/keyboardLayouts.ts index 15c3be4..a995e3d 100644 --- a/ui/src/keyboardLayouts.ts +++ b/ui/src/keyboardLayouts.ts @@ -9,4 +9,4 @@ export const layouts = { export const chars = { "en_US": chars_en_US, "de_CH": chars_de_CH, -} as Record>; +} as Record>; diff --git a/ui/src/keyboardLayouts/de_CH.ts b/ui/src/keyboardLayouts/de_CH.ts index 405feb8..893ebe4 100644 --- a/ui/src/keyboardLayouts/de_CH.ts +++ b/ui/src/keyboardLayouts/de_CH.ts @@ -1,5 +1,6 @@ export const chars = { A: { key: "KeyA", shift: true }, + "Ä": { key: "KeyA", shift: true, trema: true }, B: { key: "KeyB", shift: true }, C: { key: "KeyC", shift: true }, D: { key: "KeyD", shift: true }, @@ -14,12 +15,14 @@ export const chars = { M: { key: "KeyM", shift: true }, N: { key: "KeyN", shift: true }, O: { key: "KeyO", shift: true }, + "Ö": { key: "KeyO", shift: true, trema: true }, 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, trema: true }, V: { key: "KeyV", shift: true }, W: { key: "KeyW", shift: true }, X: { key: "KeyX", shift: true }, @@ -108,16 +111,13 @@ export const chars = { "ü": { key: "BracketLeft" }, "è": { key: "BracketLeft", shift: true }, "[": { key: "BracketLeft", altRight: true }, - "Ü": { key: "BracketLeft", capsLock: true }, "!": { key: "BracketRight", shift: true }, "]": { key: "BracketRight", altRight: true }, "ö": { key: "Semicolon" }, "é": { key: "Semicolon", shift: true }, - "Ö": { key: "Semicolon", capsLock: true }, "ä": { key: "Quote" }, "à": { key: "Quote", shift: true }, "{": { key: "Quote", altRight: true }, - "Ä": { key: "Quote", capsLock: true }, "$": { key: "Backslash" }, "£": { key: "Backslash", shift: true }, "}": { key: "Backslash", altRight: true }, @@ -137,4 +137,4 @@ export const chars = { "\n": { key: "Enter" }, Enter: { key: "Enter" }, Tab: { key: "Tab" }, -} as Record +} as Record From 99a5e9d385ff073fccdd5734b4ed4c82b6caceea Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Fri, 2 May 2025 01:51:20 +0200 Subject: [PATCH 03/29] Improve error handling and pre-loading --- ui/src/components/popovers/PasteModal.tsx | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/ui/src/components/popovers/PasteModal.tsx b/ui/src/components/popovers/PasteModal.tsx index 9e2258c..c6d2f1c 100644 --- a/ui/src/components/popovers/PasteModal.tsx +++ b/ui/src/components/popovers/PasteModal.tsx @@ -33,6 +33,13 @@ export default function PasteModal() { state => state.setKeyboardLayout, ); + useEffect(() => { + send("getKeyboardLayout", {}, resp => { + if ("error" in resp) return; + setKeyboardLayout(resp.result as string); + }); + }, []); + const onCancelPasteMode = useCallback(() => { setPasteMode(false); setDisableVideoFocusTrap(false); @@ -48,6 +55,9 @@ export default function PasteModal() { try { for (const char of text) { + if (!keyboardLayout) continue; + if (!chars[keyboardLayout]) continue; + const { key, shift, altRight, space, capsLock, trema } = chars[keyboardLayout][char] ?? {}; if (!key) continue; @@ -97,11 +107,6 @@ export default function PasteModal() { if (TextAreaRef.current) { TextAreaRef.current.focus(); } - - send("getKeyboardLayout", {}, resp => { - if ("error" in resp) return; - setKeyboardLayout(resp.result as string); - }); }, []); return ( From a2771f0b910709f7049edf5e457a77138d58f4fd Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Fri, 2 May 2025 17:53:19 +0200 Subject: [PATCH 04/29] Improve accent handling --- ui/src/components/popovers/PasteModal.tsx | 34 ++++++------ ui/src/keyboardLayouts.ts | 5 +- ui/src/keyboardLayouts/de_CH.ts | 63 ++++++++++++++++++++--- ui/src/keyboardLayouts/en_US.ts | 4 +- 4 files changed, 80 insertions(+), 26 deletions(-) diff --git a/ui/src/components/popovers/PasteModal.tsx b/ui/src/components/popovers/PasteModal.tsx index c6d2f1c..6ad60db 100644 --- a/ui/src/components/popovers/PasteModal.tsx +++ b/ui/src/components/popovers/PasteModal.tsx @@ -17,6 +17,12 @@ const hidKeyboardPayload = (keys: number[], modifier: number) => { return { keys, modifier }; }; +const modifierCode = (shift?: boolean, altRight?: boolean) => { + return shift ? modifiers["ShiftLeft"] : 0 + | (altRight ? modifiers["AltRight"] : 0) +} +const noModifier = 0 + export default function PasteModal() { const TextAreaRef = useRef(null); const setPasteMode = useHidStore(state => state.setPasteModeEnabled); @@ -58,34 +64,26 @@ export default function PasteModal() { if (!keyboardLayout) continue; if (!chars[keyboardLayout]) continue; - const { key, shift, altRight, space, capsLock, trema } = chars[keyboardLayout][char] ?? {}; + const { key, shift, altRight, deadKey, accentKey } = chars[keyboardLayout][char] if (!key) continue; const keyz = [ keys[key] ]; - const modz = [ shift ? modifiers["ShiftLeft"] : 0 - | (altRight ? modifiers["AltRight"] : 0) ]; + const modz = [ modifierCode(shift, altRight) ]; - if (space) { + if (deadKey) { keyz.push(keys["Space"]); - modz.push(0); + modz.push(noModifier); } - if (capsLock) { - keyz.unshift(keys["CapsLock"]); - modz.unshift(0); - - keyz.push(keys["CapsLock"]); - modz.push(0); - } - if (trema) { - keyz.unshift(keys["BracketRight"]); // trema ¨ - modz.unshift(0) + if (accentKey) { + keyz.unshift(keys[accentKey.key]) + modz.unshift(modifierCode(accentKey.shift, accentKey.altRight)) } - for (const [index, keyy] of keyz.entries()) { + for (const [index, kei] of keyz.entries()) { await new Promise((resolve, reject) => { send( "keyboardReport", - hidKeyboardPayload([keyy], modz[index]), + hidKeyboardPayload([kei], modz[index]), params => { if ("error" in params) return reject(params.error); send("keyboardReport", hidKeyboardPayload([], 0), params => { @@ -172,7 +170,7 @@ export default function PasteModal() {

- Sending key codes for keyboard layout {layouts[keyboardLayout]} + Sending key codes using keyboard layout {layouts[keyboardLayout]}

diff --git a/ui/src/keyboardLayouts.ts b/ui/src/keyboardLayouts.ts index a995e3d..170a71f 100644 --- a/ui/src/keyboardLayouts.ts +++ b/ui/src/keyboardLayouts.ts @@ -1,6 +1,9 @@ import { chars as chars_en_US } from "@/keyboardLayouts/en_US" import { chars as chars_de_CH } from "@/keyboardLayouts/de_CH" +type KeyInfo = { key: string | number; shift?: boolean, altRight?: boolean } +export type KeyCombo = KeyInfo & { deadKey?: boolean, accentKey?: KeyInfo } + export const layouts = { "en_US": "English (US)", "de_CH": "Swiss German" @@ -9,4 +12,4 @@ export const layouts = { export const chars = { "en_US": chars_en_US, "de_CH": chars_de_CH, -} as Record>; +} as Record> diff --git a/ui/src/keyboardLayouts/de_CH.ts b/ui/src/keyboardLayouts/de_CH.ts index 893ebe4..519b2ad 100644 --- a/ui/src/keyboardLayouts/de_CH.ts +++ b/ui/src/keyboardLayouts/de_CH.ts @@ -1,40 +1,78 @@ +import { KeyCombo } from "../keyboardLayouts" + +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 + export const chars = { A: { key: "KeyA", shift: true }, - "Ä": { key: "KeyA", shift: true, trema: true }, + "Ä": { key: "KeyA", shift: true, accentKey: keyTrema }, + "Á": { key: "KeyA", shift: true, accentKey: keyAcute }, + "Â": { key: "KeyA", shift: true, accentKey: keyHat }, + "À": { key: "KeyA", shift: true, accentKey: keyGrave }, + "Ã": { key: "KeyA", shift: true, accentKey: keyTilde }, + "Æ": { key: "KeyA", shift: true, altRight: true }, B: { key: "KeyB", shift: true }, C: { key: "KeyC", shift: true }, D: { key: "KeyD", shift: true }, E: { key: "KeyE", shift: true }, + "Ë": { key: "KeyE", shift: true, accentKey: keyTrema }, + "É": { key: "KeyE", shift: true, accentKey: keyAcute }, + "Ê": { key: "KeyE", shift: true, accentKey: keyHat }, + "È": { key: "KeyE", shift: true, accentKey: keyGrave }, + "Ẽ": { key: "KeyE", shift: true, accentKey: keyTilde }, 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: keyTrema }, + "Í": { key: "KeyI", shift: true, accentKey: keyAcute }, + "Î": { key: "KeyI", shift: true, accentKey: keyHat }, + "Ì": { key: "KeyI", shift: true, accentKey: keyGrave }, + "Ĩ": { key: "KeyI", shift: true, accentKey: keyTilde }, 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, trema: true }, + "Ö": { key: "KeyO", shift: true, accentKey: keyTrema }, + "Ó": { key: "KeyO", shift: true, accentKey: keyAcute }, + "Ô": { key: "KeyO", shift: true, accentKey: keyHat }, + "Ò": { key: "KeyO", shift: true, accentKey: keyGrave }, + "Õ": { key: "KeyO", shift: true, accentKey: keyTilde }, + "Œ": { key: "KeyO", shift: true, altRight: true }, 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, trema: true }, + "Ü": { key: "KeyU", shift: true, accentKey: keyTrema }, + "Ú": { key: "KeyU", shift: true, accentKey: keyAcute }, + "Û": { key: "KeyU", shift: true, accentKey: keyHat }, + "Ù": { key: "KeyU", shift: true, accentKey: keyGrave }, + "Ũ": { key: "KeyU", shift: true, accentKey: keyTilde }, 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: keyTilde }, "æ": { key: "KeyA", altRight: true }, b: { key: "KeyB" }, c: { key: "KeyC" }, d: { key: "KeyD" }, "ð": { key: "KeyD", altRight: true }, e: { key: "KeyE" }, + "ë": { key: "KeyE", accentKey: keyTrema }, + "ê": { key: "KeyE", accentKey: keyHat }, + "ẽ": { key: "KeyE", accentKey: keyTilde }, f: { key: "KeyF" }, "đ": { key: "KeyF", altRight: true }, g: { key: "KeyG" }, @@ -42,6 +80,11 @@ export const chars = { h: { key: "KeyH" }, "ħ": { key: "KeyH", altRight: true }, i: { key: "KeyI" }, + "ï": { key: "KeyI", accentKey: keyTrema }, + "í": { key: "KeyI", accentKey: keyAcute }, + "î": { key: "KeyI", accentKey: keyHat }, + "ì": { key: "KeyI", accentKey: keyGrave }, + "ĩ": { key: "KeyI", accentKey: keyTilde }, "→": { key: "KeyI", altRight: true }, j: { key: "KeyJ" }, k: { key: "KeyK" }, @@ -52,6 +95,10 @@ export const chars = { "µ": { key: "KeyM", altRight: true }, n: { key: "KeyN" }, o: { key: "KeyO" }, + "ó": { key: "KeyO", accentKey: keyAcute }, + "ô": { key: "KeyO", accentKey: keyHat }, + "ò": { key: "KeyO", accentKey: keyGrave }, + "õ": { key: "KeyO", accentKey: keyTilde }, "œ": { key: "KeyO", altRight: true }, p: { key: "KeyP" }, "þ": { key: "KeyP", altRight: true }, @@ -63,6 +110,10 @@ export const chars = { t: { key: "KeyT" }, "ŧ": { key: "KeyT", altRight: true }, u: { key: "KeyU" }, + "ú": { key: "KeyU", accentKey: keyAcute }, + "û": { key: "KeyU", accentKey: keyHat }, + "ù": { key: "KeyU", accentKey: keyGrave }, + "ũ": { key: "KeyU", accentKey: keyTilde }, "↓": { key: "KeyU", altRight: true }, v: { key: "KeyV" }, "„": { key: "KeyV", altRight: true }, @@ -105,9 +156,9 @@ export const chars = { "=": { key: "Digit0", shift: true }, "'": { key: "Minus" }, "?": { key: "Minus", shift: true }, - "^": { key: "Equal", space: true }, // dead key + "^": { key: "Equal", deadKey: true }, "`": { key: "Equal", shift: true }, - "~": { key: "Equal", altRight: true, space: true }, // dead key + "~": { key: "Equal", altRight: true, deadKey: true }, "ü": { key: "BracketLeft" }, "è": { key: "BracketLeft", shift: true }, "[": { key: "BracketLeft", altRight: true }, @@ -137,4 +188,4 @@ export const chars = { "\n": { key: "Enter" }, Enter: { key: "Enter" }, Tab: { key: "Tab" }, -} as Record +} as Record; diff --git a/ui/src/keyboardLayouts/en_US.ts b/ui/src/keyboardLayouts/en_US.ts index 65da319..b1e9dea 100644 --- a/ui/src/keyboardLayouts/en_US.ts +++ b/ui/src/keyboardLayouts/en_US.ts @@ -1,3 +1,5 @@ +import { KeyCombo } from "../keyboardLayouts" + export const chars = { A: { key: "KeyA", shift: true }, B: { key: "KeyB", shift: true }, @@ -106,4 +108,4 @@ export const chars = { Break: { key: "Pause", shift: true }, Insert: { key: "Insert", shift: false }, Delete: { key: "Delete", shift: false }, -} as Record +} as Record From c90b0425c7a5dc4c47d81bab2bfe337f522ca136 Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Fri, 2 May 2025 21:00:41 +0200 Subject: [PATCH 05/29] Remove obscure Alt-Gr keys, unsure if they are supported everywhere --- ui/src/keyboardLayouts/de_CH.ts | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/ui/src/keyboardLayouts/de_CH.ts b/ui/src/keyboardLayouts/de_CH.ts index 519b2ad..2b1019e 100644 --- a/ui/src/keyboardLayouts/de_CH.ts +++ b/ui/src/keyboardLayouts/de_CH.ts @@ -13,7 +13,6 @@ export const chars = { "Â": { key: "KeyA", shift: true, accentKey: keyHat }, "À": { key: "KeyA", shift: true, accentKey: keyGrave }, "Ã": { key: "KeyA", shift: true, accentKey: keyTilde }, - "Æ": { key: "KeyA", shift: true, altRight: true }, B: { key: "KeyB", shift: true }, C: { key: "KeyC", shift: true }, D: { key: "KeyD", shift: true }, @@ -43,7 +42,6 @@ export const chars = { "Ô": { key: "KeyO", shift: true, accentKey: keyHat }, "Ò": { key: "KeyO", shift: true, accentKey: keyGrave }, "Õ": { key: "KeyO", shift: true, accentKey: keyTilde }, - "Œ": { key: "KeyO", shift: true, altRight: true }, P: { key: "KeyP", shift: true }, Q: { key: "KeyQ", shift: true }, R: { key: "KeyR", shift: true }, @@ -64,67 +62,47 @@ export const chars = { "á": { key: "KeyA", accentKey: keyAcute }, "â": { key: "KeyA", accentKey: keyHat }, "ã": { key: "KeyA", accentKey: keyTilde }, - "æ": { key: "KeyA", altRight: true }, b: { key: "KeyB" }, c: { key: "KeyC" }, d: { key: "KeyD" }, - "ð": { key: "KeyD", altRight: true }, e: { key: "KeyE" }, "ë": { key: "KeyE", accentKey: keyTrema }, "ê": { key: "KeyE", accentKey: keyHat }, "ẽ": { key: "KeyE", accentKey: keyTilde }, f: { key: "KeyF" }, - "đ": { key: "KeyF", altRight: true }, g: { key: "KeyG" }, - "ŋ": { key: "KeyG", altRight: true }, h: { key: "KeyH" }, - "ħ": { key: "KeyH", altRight: true }, i: { key: "KeyI" }, "ï": { key: "KeyI", accentKey: keyTrema }, "í": { key: "KeyI", accentKey: keyAcute }, "î": { key: "KeyI", accentKey: keyHat }, "ì": { key: "KeyI", accentKey: keyGrave }, "ĩ": { key: "KeyI", accentKey: keyTilde }, - "→": { key: "KeyI", altRight: true }, j: { key: "KeyJ" }, k: { key: "KeyK" }, - "ĸ": { key: "KeyK", altRight: true }, l: { key: "KeyL" }, - "ł": { key: "KeyL", altRight: true }, m: { key: "KeyM" }, - "µ": { key: "KeyM", altRight: true }, n: { key: "KeyN" }, o: { key: "KeyO" }, "ó": { key: "KeyO", accentKey: keyAcute }, "ô": { key: "KeyO", accentKey: keyHat }, "ò": { key: "KeyO", accentKey: keyGrave }, "õ": { key: "KeyO", accentKey: keyTilde }, - "œ": { key: "KeyO", altRight: true }, p: { key: "KeyP" }, - "þ": { key: "KeyP", altRight: true }, q: { key: "KeyQ" }, r: { key: "KeyR" }, - "¶": { key: "KeyR", altRight: true }, s: { key: "KeyS" }, - "ß": { key: "KeyS", altRight: true }, t: { key: "KeyT" }, - "ŧ": { key: "KeyT", altRight: true }, u: { key: "KeyU" }, "ú": { key: "KeyU", accentKey: keyAcute }, "û": { key: "KeyU", accentKey: keyHat }, "ù": { key: "KeyU", accentKey: keyGrave }, "ũ": { key: "KeyU", accentKey: keyTilde }, - "↓": { key: "KeyU", altRight: true }, v: { key: "KeyV" }, - "„": { key: "KeyV", altRight: true }, w: { key: "KeyW" }, - "ſ": { key: "KeyW", altRight: true }, x: { key: "KeyX" }, - "»": { key: "KeyX", altRight: true }, y: { key: "KeyZ" }, - "←": { key: "KeyZ", altRight: true }, z: { key: "KeyY" }, - "«": { key: "KeyY", altRight: true }, "§": { key: "Backquote" }, "°": { key: "Backquote", shift: true }, 1: { key: "Digit1" }, @@ -138,18 +116,14 @@ export const chars = { "#": { key: "Digit3", altRight: true }, 4: { key: "Digit4" }, "ç": { key: "Digit4", shift: true }, - "¼": { key: "Digit4", altRight: true }, 5: { key: "Digit5" }, "%": { key: "Digit5", shift: true }, - "½": { key: "Digit5", altRight: 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 }, - "¢": { key: "Digit8", altRight: true }, 9: { key: "Digit9" }, ")": { key: "Digit9", shift: true }, 0: { key: "Digit0" }, @@ -174,10 +148,8 @@ export const chars = { "}": { key: "Backslash", altRight: true }, ",": { key: "Comma" }, ";": { key: "Comma", shift: true }, - "•": { key: "Comma", altRight: true }, ".": { key: "Period" }, ":": { key: "Period", shift: true }, - "·": { key: "Period", altRight: true }, "-": { key: "Slash" }, "_": { key: "Slash", shift: true }, "<": { key: "IntlBackslash" }, From 33a4f38702904911c72b1c9337ba96fa7e79944c Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Fri, 2 May 2025 21:19:51 +0200 Subject: [PATCH 06/29] Add Swiss French --- ui/src/keyboardLayouts.ts | 3 +++ ui/src/keyboardLayouts/fr_CH.ts | 12 ++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 ui/src/keyboardLayouts/fr_CH.ts diff --git a/ui/src/keyboardLayouts.ts b/ui/src/keyboardLayouts.ts index 170a71f..fc28955 100644 --- a/ui/src/keyboardLayouts.ts +++ b/ui/src/keyboardLayouts.ts @@ -1,4 +1,5 @@ import { chars as chars_en_US } from "@/keyboardLayouts/en_US" +import { chars as chars_fr_CH } from "@/keyboardLayouts/fr_CH" import { chars as chars_de_CH } from "@/keyboardLayouts/de_CH" type KeyInfo = { key: string | number; shift?: boolean, altRight?: boolean } @@ -6,10 +7,12 @@ export type KeyCombo = KeyInfo & { deadKey?: boolean, accentKey?: KeyInfo } export const layouts = { "en_US": "English (US)", + "fr_CH": "Swiss French", "de_CH": "Swiss German" } as Record; export const chars = { "en_US": chars_en_US, + "fr_CH": chars_fr_CH, "de_CH": chars_de_CH, } as Record> diff --git a/ui/src/keyboardLayouts/fr_CH.ts b/ui/src/keyboardLayouts/fr_CH.ts new file mode 100644 index 0000000..02f2b69 --- /dev/null +++ b/ui/src/keyboardLayouts/fr_CH.ts @@ -0,0 +1,12 @@ +import { KeyCombo } from "../keyboardLayouts" +import { chars as chars_de_CH } from "./de_CH" + +export const chars = { + ...chars_de_CH, + "è": { key: "BracketLeft" }, + "ü": { key: "BracketLeft", shift: true }, + "é": { key: "Semicolon" }, + "ö": { key: "Semicolon", shift: true }, + "à": { key: "Quote" }, + "ä": { key: "Quote", shift: true }, +} as Record; From ab94eb1da418ddab0b48c8cc23c66c1bd7f13ecd Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Fri, 2 May 2025 22:11:10 +0200 Subject: [PATCH 07/29] Change line ordering --- ui/src/keyboardLayouts.ts | 3 + ui/src/keyboardLayouts/de_CH.ts | 2 +- ui/src/keyboardLayouts/de_DE.ts | 150 ++++++++++++++++++++++++++++++++ 3 files changed, 154 insertions(+), 1 deletion(-) create mode 100644 ui/src/keyboardLayouts/de_DE.ts diff --git a/ui/src/keyboardLayouts.ts b/ui/src/keyboardLayouts.ts index fc28955..69b3ea9 100644 --- a/ui/src/keyboardLayouts.ts +++ b/ui/src/keyboardLayouts.ts @@ -1,4 +1,5 @@ import { chars as chars_en_US } from "@/keyboardLayouts/en_US" +import { chars as chars_de_DE } from "@/keyboardLayouts/de_DE" import { chars as chars_fr_CH } from "@/keyboardLayouts/fr_CH" import { chars as chars_de_CH } from "@/keyboardLayouts/de_CH" @@ -7,12 +8,14 @@ export type KeyCombo = KeyInfo & { deadKey?: boolean, accentKey?: KeyInfo } export const layouts = { "en_US": "English (US)", + "de_DE": "German", "fr_CH": "Swiss French", "de_CH": "Swiss German" } as Record; export const chars = { "en_US": chars_en_US, + "de_DE": chars_de_DE, "fr_CH": chars_fr_CH, "de_CH": chars_de_CH, } as Record> diff --git a/ui/src/keyboardLayouts/de_CH.ts b/ui/src/keyboardLayouts/de_CH.ts index 2b1019e..c822a7f 100644 --- a/ui/src/keyboardLayouts/de_CH.ts +++ b/ui/src/keyboardLayouts/de_CH.ts @@ -69,6 +69,7 @@ export const chars = { "ë": { key: "KeyE", accentKey: keyTrema }, "ê": { key: "KeyE", accentKey: keyHat }, "ẽ": { key: "KeyE", accentKey: keyTilde }, + "€": { key: "KeyE", altRight: true }, f: { key: "KeyF" }, g: { key: "KeyG" }, h: { key: "KeyH" }, @@ -155,7 +156,6 @@ export const chars = { "<": { key: "IntlBackslash" }, ">": { key: "IntlBackslash", shift: true }, "\\": { key: "IntlBackslash", altRight: true }, - "€": { key: "KeyE", altRight: true }, " ": { key: "Space" }, "\n": { key: "Enter" }, Enter: { key: "Enter" }, diff --git a/ui/src/keyboardLayouts/de_DE.ts b/ui/src/keyboardLayouts/de_DE.ts new file mode 100644 index 0000000..2082f7a --- /dev/null +++ b/ui/src/keyboardLayouts/de_DE.ts @@ -0,0 +1,150 @@ +import { KeyCombo } from "../keyboardLayouts" + +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 + +export const chars = { + A: { key: "KeyA", shift: true }, + "Á": { key: "KeyA", shift: true, accentKey: keyAcute }, + "Â": { key: "KeyA", shift: true, accentKey: keyHat }, + "À": { key: "KeyA", shift: true, accentKey: keyGrave }, + B: { key: "KeyB", shift: true }, + C: { key: "KeyC", shift: true }, + D: { key: "KeyD", shift: 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" }, + g: { key: "KeyG" }, + h: { key: "KeyH" }, + i: { key: "KeyI" }, + "í": { key: "KeyI", accentKey: keyAcute }, + "î": { key: "KeyI", accentKey: keyHat }, + "ì": { key: "KeyI", accentKey: keyGrave }, + j: { key: "KeyJ" }, + k: { key: "KeyK" }, + l: { key: "KeyL" }, + m: { key: "KeyM" }, + "µ": { key: "KeyM", altRight: true }, + n: { key: "KeyN" }, + o: { key: "KeyO" }, + "ó": { key: "KeyO", accentKey: keyAcute }, + "ô": { key: "KeyO", accentKey: keyHat }, + "ò": { key: "KeyO", accentKey: keyGrave }, + p: { key: "KeyP" }, + q: { key: "KeyQ" }, + "@": { key: "KeyQ", altRight: true }, + r: { key: "KeyR" }, + s: { key: "KeyS" }, + t: { key: "KeyT" }, + u: { key: "KeyU" }, + "ú": { key: "KeyU", accentKey: keyAcute }, + "û": { key: "KeyU", accentKey: keyHat }, + "ù": { key: "KeyU", accentKey: keyGrave }, + v: { key: "KeyV" }, + w: { key: "KeyW" }, + x: { key: "KeyX" }, + y: { key: "KeyZ" }, + z: { key: "KeyY" }, + "°": { key: "Backquote", shift: true }, + "^": { key: "Backquote", deadKey: true }, + 1: { key: "Digit1" }, + "!": { key: "Digit1", shift: 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 }, + 7: { key: "Digit7" }, + "/": { key: "Digit7", shift: true }, + "{": { key: "Digit7", altRight: true }, + 8: { key: "Digit8" }, + "(": { key: "Digit8", shift: true }, + "[": { key: "Digit8", altRight: true }, + 9: { key: "Digit9" }, + ")": { key: "Digit9", shift: true }, + "]": { key: "Digit9", altRight: true }, + 0: { key: "Digit0" }, + "=": { key: "Digit0", shift: true }, + "}": { key: "Digit0", altRight: true }, + "ß": { key: "Minus" }, + "?": { key: "Minus", shift: true }, + "\\": { key: "Minus", altRight: true }, + "´": { key: "Equal", deadKey: true }, + "`": { key: "Equal", shift: true, deadKey: true }, + "ü": { key: "BracketLeft" }, + "Ü": { key: "BracketLeft", shift: true }, + "+": { key: "BracketRight" }, + "*": { key: "BracketRight", shift: true }, + "~": { key: "BracketRight", altRight: true }, + "ö": { key: "Semicolon" }, + "Ö": { key: "Semicolon", shift: true }, + "ä": { key: "Quote" }, + "Ä": { key: "Quote", shift: true }, + "#": { key: "Backslash" }, + "'": { key: "Backslash", shift: 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: "IntlBackslash", altRight: true }, + " ": { key: "Space" }, + "\n": { key: "Enter" }, + Enter: { key: "Enter" }, + Tab: { key: "Tab" }, +} as Record; From e0be7edf96e39c1585cb2eea521a97b7dfe679eb Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Fri, 2 May 2025 22:12:54 +0200 Subject: [PATCH 08/29] Fix whitespace --- ui/src/keyboardLayouts/de_DE.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/keyboardLayouts/de_DE.ts b/ui/src/keyboardLayouts/de_DE.ts index 2082f7a..08a58d3 100644 --- a/ui/src/keyboardLayouts/de_DE.ts +++ b/ui/src/keyboardLayouts/de_DE.ts @@ -91,7 +91,7 @@ export const chars = { y: { key: "KeyZ" }, z: { key: "KeyY" }, "°": { key: "Backquote", shift: true }, - "^": { key: "Backquote", deadKey: true }, + "^": { key: "Backquote", deadKey: true }, 1: { key: "Digit1" }, "!": { key: "Digit1", shift: true }, 2: { key: "Digit2" }, From 219573e25c77a2a62c9d5b469d3cd754fedd3778 Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Fri, 2 May 2025 22:55:21 +0200 Subject: [PATCH 09/29] Add French (France) --- ui/src/keyboardLayouts.ts | 3 + ui/src/keyboardLayouts/fr_FR.ts | 137 ++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100644 ui/src/keyboardLayouts/fr_FR.ts diff --git a/ui/src/keyboardLayouts.ts b/ui/src/keyboardLayouts.ts index 69b3ea9..2c0eeda 100644 --- a/ui/src/keyboardLayouts.ts +++ b/ui/src/keyboardLayouts.ts @@ -1,4 +1,5 @@ import { chars as chars_en_US } from "@/keyboardLayouts/en_US" +import { chars as chars_fr_FR } from "@/keyboardLayouts/fr_FR" import { chars as chars_de_DE } from "@/keyboardLayouts/de_DE" import { chars as chars_fr_CH } from "@/keyboardLayouts/fr_CH" import { chars as chars_de_CH } from "@/keyboardLayouts/de_CH" @@ -8,6 +9,7 @@ export type KeyCombo = KeyInfo & { deadKey?: boolean, accentKey?: KeyInfo } export const layouts = { "en_US": "English (US)", + "fr_FR": "French", "de_DE": "German", "fr_CH": "Swiss French", "de_CH": "Swiss German" @@ -15,6 +17,7 @@ export const layouts = { export const chars = { "en_US": chars_en_US, + "fr_FR": chars_fr_FR, "de_DE": chars_de_DE, "fr_CH": chars_fr_CH, "de_CH": chars_de_CH, diff --git a/ui/src/keyboardLayouts/fr_FR.ts b/ui/src/keyboardLayouts/fr_FR.ts new file mode 100644 index 0000000..9d66d16 --- /dev/null +++ b/ui/src/keyboardLayouts/fr_FR.ts @@ -0,0 +1,137 @@ +import { KeyCombo } from "../keyboardLayouts" + +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 + +export const chars = { + A: { key: "KeyQ", shift: true }, + "Ä": { key: "KeyA", shift: true, accentKey: keyTrema }, + "Â": { key: "KeyA", shift: true, accentKey: keyHat }, + B: { key: "KeyB", shift: true }, + C: { key: "KeyC", shift: true }, + D: { key: "KeyD", shift: true }, + E: { key: "KeyE", shift: true }, + "Ë": { key: "KeyE", shift: true, accentKey: keyTrema }, + "Ê": { key: "KeyE", shift: true, accentKey: keyHat }, + 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: keyTrema }, + "Î": { key: "KeyI", shift: true, accentKey: keyHat }, + J: { key: "KeyJ", shift: true }, + K: { key: "KeyK", shift: true }, + L: { key: "KeyL", shift: true }, + M: { key: "Semicolon", shift: true }, + N: { key: "KeyN", shift: true }, + O: { key: "KeyO", shift: true }, + "Ö": { key: "KeyO", shift: true, accentKey: keyTrema }, + "Ô": { key: "KeyO", shift: true, accentKey: keyHat }, + P: { key: "KeyP", shift: true }, + Q: { key: "KeyA", 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: keyTrema }, + "Û": { key: "KeyU", shift: true, accentKey: keyHat }, + V: { key: "KeyV", shift: true }, + W: { key: "KeyZ", shift: true }, + X: { key: "KeyX", shift: true }, + Y: { key: "KeyY", shift: true }, + Z: { key: "KeyW", shift: true }, + a: { key: "KeyQ" }, + "ä": { key: "KeyA", accentKey: keyTrema }, + "â": { key: "KeyA", accentKey: keyHat }, + b: { key: "KeyB" }, + c: { key: "KeyC" }, + d: { key: "KeyD" }, + e: { key: "KeyE" }, + "ë": { key: "KeyE", accentKey: keyTrema }, + "ê": { key: "KeyE", accentKey: keyHat }, + "€": { key: "KeyE", altRight: true }, + f: { key: "KeyF" }, + g: { key: "KeyG" }, + h: { key: "KeyH" }, + i: { key: "KeyI" }, + "ï": { key: "KeyI", accentKey: keyTrema }, + "î": { key: "KeyI", accentKey: keyHat }, + j: { key: "KeyJ" }, + k: { key: "KeyK" }, + l: { key: "KeyL" }, + m: { key: "Semicolon" }, + n: { key: "KeyN" }, + o: { key: "KeyO" }, + "ö": { key: "KeyO", accentKey: keyTrema }, + "ô": { key: "KeyO", accentKey: keyHat }, + p: { key: "KeyP" }, + q: { key: "KeyA" }, + r: { key: "KeyR" }, + s: { key: "KeyS" }, + t: { key: "KeyT" }, + u: { key: "KeyU" }, + "ü": { key: "KeyU", accentKey: keyTrema }, + "û": { key: "KeyU", accentKey: keyHat }, + v: { key: "KeyV" }, + w: { key: "KeyZ" }, + x: { key: "KeyX" }, + y: { key: "KeyY" }, + z: { key: "KeyW" }, + "²": { key: "Backquote" }, + "&": { key: "Digit1" }, + 1: { key: "Digit1", shift: true }, + "é": { key: "Digit2" }, + 2: { key: "Digit2", shift: true }, + "~": { key: "Digit2", altRight: true }, + "\"": { key: "Digit3" }, + 3: { key: "Digit3", shift: true }, + "#": { key: "Digit3", altRight: true }, + "'": { key: "Digit4" }, + 4: { key: "Digit4", shift: true }, + "{": { key: "Digit4", altRight: true }, + "(": { key: "Digit5" }, + 5: { key: "Digit5", shift: true }, + "[": { key: "Digit5", altRight: true }, + "-": { key: "Digit6" }, + 6: { key: "Digit6", shift: true }, + "|": { key: "Digit6", altRight: true }, + "è": { key: "Digit7" }, + 7: { key: "Digit7", shift: true }, + "`": { key: "Digit7", altRight: true }, + "_": { key: "Digit8" }, + 8: { key: "Digit8", shift: true }, + "\\": { key: "Digit8", altRight: true }, + "ç": { key: "Digit9" }, + 9: { key: "Digit9", shift: true }, + "^": { key: "Digit9", altRight: true }, + "à": { key: "Digit0" }, + 0: { key: "Digit0", shift: true }, + "@": { key: "Digit0", altRight: true }, + ")": { key: "Minus" }, + "°": { key: "Minus", shift: true }, + "]": { key: "Minus", altRight: true }, + "=": { key: "Equal" }, + "+": { key: "Equal", shift: true }, + "}": { key: "Equal", altRight: true }, + "$": { key: "BracketRight" }, + "£": { key: "BracketRight", shift: true }, + "¤": { key: "BracketRight", altRight: true }, + "ù": { key: "Quote" }, + "%": { key: "Quote", shift: true }, + "*": { key: "Backslash" }, + "µ": { key: "Backslash", shift: true }, + ",": { key: "KeyM" }, + "?": { key: "KeyM", shift: 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" }, + "\n": { key: "Enter" }, + Enter: { key: "Enter" }, + Tab: { key: "Tab" }, +} as Record; From 12f0814f8c2c0a9c163910c29b68e08e85887da7 Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Fri, 2 May 2025 22:55:46 +0200 Subject: [PATCH 10/29] Add English (UK) --- ui/src/keyboardLayouts.ts | 3 + ui/src/keyboardLayouts/en_UK.ts | 105 ++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 ui/src/keyboardLayouts/en_UK.ts diff --git a/ui/src/keyboardLayouts.ts b/ui/src/keyboardLayouts.ts index 2c0eeda..de66f8d 100644 --- a/ui/src/keyboardLayouts.ts +++ b/ui/src/keyboardLayouts.ts @@ -1,3 +1,4 @@ +import { chars as chars_en_UK } from "@/keyboardLayouts/en_UK" import { chars as chars_en_US } from "@/keyboardLayouts/en_US" import { chars as chars_fr_FR } from "@/keyboardLayouts/fr_FR" import { chars as chars_de_DE } from "@/keyboardLayouts/de_DE" @@ -8,6 +9,7 @@ type KeyInfo = { key: string | number; shift?: boolean, altRight?: boolean } export type KeyCombo = KeyInfo & { deadKey?: boolean, accentKey?: KeyInfo } export const layouts = { + "en_UK": "English (UK)", "en_US": "English (US)", "fr_FR": "French", "de_DE": "German", @@ -16,6 +18,7 @@ export const layouts = { } as Record; export const chars = { + "en_UK": chars_en_UK, "en_US": chars_en_US, "fr_FR": chars_fr_FR, "de_DE": chars_de_DE, diff --git a/ui/src/keyboardLayouts/en_UK.ts b/ui/src/keyboardLayouts/en_UK.ts new file mode 100644 index 0000000..49d623f --- /dev/null +++ b/ui/src/keyboardLayouts/en_UK.ts @@ -0,0 +1,105 @@ +import { KeyCombo } from "../keyboardLayouts" + +export const chars = { + A: { key: "KeyA", shift: true }, + B: { key: "KeyB", shift: true }, + C: { key: "KeyC", shift: true }, + D: { key: "KeyD", shift: true }, + E: { key: "KeyE", shift: true }, + F: { key: "KeyF", shift: true }, + G: { key: "KeyG", shift: true }, + H: { key: "KeyH", shift: true }, + I: { key: "KeyI", shift: true }, + 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 }, + 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 }, + V: { key: "KeyV", shift: true }, + W: { key: "KeyW", shift: true }, + X: { key: "KeyX", shift: true }, + Y: { key: "KeyY", shift: true }, + Z: { key: "KeyZ", shift: true }, + a: { key: "KeyA" }, + b: { key: "KeyB" }, + c: { key: "KeyC" }, + d: { key: "KeyD" }, + e: { key: "KeyE" }, + f: { key: "KeyF" }, + g: { key: "KeyG" }, + h: { key: "KeyH" }, + i: { key: "KeyI" }, + j: { key: "KeyJ" }, + k: { key: "KeyK" }, + l: { key: "KeyL" }, + m: { key: "KeyM" }, + n: { key: "KeyN" }, + o: { key: "KeyO" }, + p: { key: "KeyP" }, + q: { key: "KeyQ" }, + r: { key: "KeyR" }, + s: { key: "KeyS" }, + t: { key: "KeyT" }, + u: { key: "KeyU" }, + v: { key: "KeyV" }, + w: { key: "KeyW" }, + x: { key: "KeyX" }, + y: { key: "KeyY" }, + z: { key: "KeyZ" }, + 1: { key: "Digit1" }, + "!": { key: "Digit1", shift: true }, + 2: { key: "Digit2" }, + "\"": { key: "Digit2", shift: true }, + 3: { key: "Digit3" }, + "£": { key: "Digit3", shift: true }, + 4: { key: "Digit4" }, + $: { key: "Digit4", shift: true }, + "€": { key: "Digit4", altRight: true }, + 5: { key: "Digit5" }, + "%": { key: "Digit5", shift: true }, + 6: { key: "Digit6" }, + "^": { key: "Digit6", shift: 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" }, + "+": { key: "Equal", shift: true }, + "'": { key: "Quote" }, + '@': { key: "Quote", shift: true }, + ",": { key: "Comma" }, + "<": { key: "Comma", shift: true }, + "/": { key: "Slash" }, + "?": { key: "Slash", shift: true }, + ".": { key: "Period" }, + ">": { key: "Period", shift: true }, + ";": { key: "Semicolon" }, + ":": { key: "Semicolon", shift: true }, + "[": { key: "BracketLeft" }, + "{": { key: "BracketLeft", shift: true }, + "]": { key: "BracketRight" }, + "}": { key: "BracketRight", shift: true }, + "#": { key: "Backslash" }, + "~": { key: "Backslash", shift: true }, + "`": { key: "Backquote" }, + "¬": { key: "Backquote", shift: true }, + "\\": { key: "IntlBackslash" }, + "|": { key: "IntlBackslash", shift: true }, + " ": { key: "Space" }, + "\n": { key: "Enter" }, + Enter: { key: "Enter" }, + Tab: { key: "Tab" }, +} as Record From 0bf05becb4af2c4a6197f477c5d4323d6cfc051b Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Fri, 2 May 2025 23:18:56 +0200 Subject: [PATCH 11/29] Add Swedish --- ui/src/keyboardLayouts.ts | 3 + ui/src/keyboardLayouts/sv_SE.ts | 162 ++++++++++++++++++++++++++++++++ 2 files changed, 165 insertions(+) create mode 100644 ui/src/keyboardLayouts/sv_SE.ts diff --git a/ui/src/keyboardLayouts.ts b/ui/src/keyboardLayouts.ts index de66f8d..98c2bbd 100644 --- a/ui/src/keyboardLayouts.ts +++ b/ui/src/keyboardLayouts.ts @@ -2,6 +2,7 @@ import { chars as chars_en_UK } from "@/keyboardLayouts/en_UK" import { chars as chars_en_US } from "@/keyboardLayouts/en_US" import { chars as chars_fr_FR } from "@/keyboardLayouts/fr_FR" import { chars as chars_de_DE } from "@/keyboardLayouts/de_DE" +import { chars as chars_sv_SE } from "@/keyboardLayouts/sv_SE" import { chars as chars_fr_CH } from "@/keyboardLayouts/fr_CH" import { chars as chars_de_CH } from "@/keyboardLayouts/de_CH" @@ -13,6 +14,7 @@ export const layouts = { "en_US": "English (US)", "fr_FR": "French", "de_DE": "German", + "sv_SE": "Swedish", "fr_CH": "Swiss French", "de_CH": "Swiss German" } as Record; @@ -22,6 +24,7 @@ export const chars = { "en_US": chars_en_US, "fr_FR": chars_fr_FR, "de_DE": chars_de_DE, + "sv_SE": chars_sv_SE, "fr_CH": chars_fr_CH, "de_CH": chars_de_CH, } as Record> diff --git a/ui/src/keyboardLayouts/sv_SE.ts b/ui/src/keyboardLayouts/sv_SE.ts new file mode 100644 index 0000000..79e9771 --- /dev/null +++ b/ui/src/keyboardLayouts/sv_SE.ts @@ -0,0 +1,162 @@ +import { KeyCombo } from "../keyboardLayouts" + +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 + +export const chars = { + 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", shift: true, accentKey: keyTilde }, + B: { key: "KeyB", shift: true }, + C: { key: "KeyC", shift: true }, + D: { key: "KeyD", shift: true }, + E: { key: "KeyE", shift: true }, + "Ë": { key: "KeyE", shift: true, accentKey: keyTrema }, + "É": { key: "KeyE", shift: true, accentKey: keyAcute }, + "Ê": { key: "KeyE", shift: true, accentKey: keyHat }, + "È": { key: "KeyE", shift: true, accentKey: keyGrave }, + "Ẽ": { key: "KeyE", shift: true, accentKey: keyTilde }, + 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: keyTrema }, + "Í": { key: "KeyI", shift: true, accentKey: keyAcute }, + "Î": { key: "KeyI", shift: true, accentKey: keyHat }, + "Ì": { key: "KeyI", shift: true, accentKey: keyGrave }, + "Ĩ": { key: "KeyI", shift: true, accentKey: keyTilde }, + 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 }, + "Õ": { key: "KeyO", shift: true, accentKey: keyTilde }, + 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: keyTrema }, + "Ú": { key: "KeyU", shift: true, accentKey: keyAcute }, + "Û": { key: "KeyU", shift: true, accentKey: keyHat }, + "Ù": { key: "KeyU", shift: true, accentKey: keyGrave }, + "Ũ": { key: "KeyU", shift: true, accentKey: keyTilde }, + 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 }, + "ã": { key: "KeyA", accentKey: keyTilde }, + b: { key: "KeyB" }, + c: { key: "KeyC" }, + d: { key: "KeyD" }, + e: { key: "KeyE" }, + "ë": { key: "KeyE", accentKey: keyTrema }, + "é": { key: "KeyE", accentKey: keyAcute }, + "ê": { key: "KeyE", accentKey: keyHat }, + "è": { key: "KeyE", accentKey: keyGrave }, + "ẽ": { key: "KeyE", accentKey: keyTilde }, + "€": { key: "KeyE", altRight: true }, + f: { key: "KeyF" }, + g: { key: "KeyG" }, + h: { key: "KeyH" }, + i: { key: "KeyI" }, + "ï": { key: "KeyI", accentKey: keyTrema }, + "í": { key: "KeyI", accentKey: keyAcute }, + "î": { key: "KeyI", accentKey: keyHat }, + "ì": { key: "KeyI", accentKey: keyGrave }, + "ĩ": { key: "KeyI", accentKey: keyTilde }, + j: { key: "KeyJ" }, + k: { key: "KeyK" }, + l: { key: "KeyL" }, + m: { key: "KeyM" }, + n: { key: "KeyN" }, + o: { key: "KeyO" }, + "ó": { key: "KeyO", accentKey: keyAcute }, + "ô": { key: "KeyO", accentKey: keyHat }, + "ò": { key: "KeyO", accentKey: keyGrave }, + "õ": { key: "KeyO", accentKey: keyTilde }, + p: { key: "KeyP" }, + q: { key: "KeyQ" }, + r: { key: "KeyR" }, + s: { key: "KeyS" }, + t: { key: "KeyT" }, + u: { key: "KeyU" }, + "ü": { key: "KeyU", accentKey: keyTrema }, + "ú": { key: "KeyU", accentKey: keyAcute }, + "û": { key: "KeyU", accentKey: keyHat }, + "ù": { key: "KeyU", accentKey: keyGrave }, + "ũ": { key: "KeyU", accentKey: keyTilde }, + v: { key: "KeyV" }, + w: { key: "KeyW" }, + x: { key: "KeyX" }, + y: { key: "KeyZ" }, + z: { key: "KeyY" }, + "§": { key: "Backquote" }, + "½": { key: "Backquote", shift: true }, + 1: { key: "Digit1" }, + "!": { key: "Digit1", shift: 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 }, + "$": { key: "Digit4", altRight: true }, + 5: { key: "Digit5" }, + "%": { key: "Digit5", shift: true }, + 6: { key: "Digit6" }, + "&": { key: "Digit6", shift: true }, + 7: { key: "Digit7" }, + "/": { key: "Digit7", shift: true }, + "{": { key: "Digit7", altRight: true }, + 8: { key: "Digit8" }, + "(": { key: "Digit8", shift: true }, + "[": { key: "Digit8", altRight: true }, + 9: { key: "Digit9" }, + ")": { key: "Digit9", shift: true }, + "]": { key: "Digit9", altRight: true }, + 0: { key: "Digit0" }, + "=": { key: "Digit0", shift: true }, + "}": { key: "Digit0", altRight: true }, + "+": { key: "Minus" }, + "?": { key: "Minus", shift: true }, + "\\": { key: "Minus", altRight: true }, + "å": { key: "BracketLeft" }, + "Å": { key: "BracketLeft", shift: true }, + "ö": { key: "Semicolon" }, + "Ö": { key: "Semicolon", shift: true }, + "ä": { key: "Quote" }, + "Ä": { key: "Quote", shift: true }, + "'": { key: "Backslash" }, + "*": { key: "Backslash", shift: 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: "IntlBackslash", altRight: true }, + " ": { key: "Space" }, + "\n": { key: "Enter" }, + Enter: { key: "Enter" }, + Tab: { key: "Tab" }, +} as Record; From 18c7b253cabf9b9e9a7ba7d3fe507539e463d0da Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Fri, 2 May 2025 23:56:52 +0200 Subject: [PATCH 12/29] Add Spanish --- ui/src/keyboardLayouts.ts | 3 + ui/src/keyboardLayouts/es_ES.ts | 164 ++++++++++++++++++++++++++++++++ 2 files changed, 167 insertions(+) create mode 100644 ui/src/keyboardLayouts/es_ES.ts diff --git a/ui/src/keyboardLayouts.ts b/ui/src/keyboardLayouts.ts index 98c2bbd..723e855 100644 --- a/ui/src/keyboardLayouts.ts +++ b/ui/src/keyboardLayouts.ts @@ -2,6 +2,7 @@ import { chars as chars_en_UK } from "@/keyboardLayouts/en_UK" import { chars as chars_en_US } from "@/keyboardLayouts/en_US" import { chars as chars_fr_FR } from "@/keyboardLayouts/fr_FR" import { chars as chars_de_DE } from "@/keyboardLayouts/de_DE" +import { chars as chars_es_ES } from "@/keyboardLayouts/es_ES" import { chars as chars_sv_SE } from "@/keyboardLayouts/sv_SE" import { chars as chars_fr_CH } from "@/keyboardLayouts/fr_CH" import { chars as chars_de_CH } from "@/keyboardLayouts/de_CH" @@ -14,6 +15,7 @@ export const layouts = { "en_US": "English (US)", "fr_FR": "French", "de_DE": "German", + "es_ES": "Spanish", "sv_SE": "Swedish", "fr_CH": "Swiss French", "de_CH": "Swiss German" @@ -24,6 +26,7 @@ export const chars = { "en_US": chars_en_US, "fr_FR": chars_fr_FR, "de_DE": chars_de_DE, + "es_ES": chars_es_ES, "sv_SE": chars_sv_SE, "fr_CH": chars_fr_CH, "de_CH": chars_de_CH, diff --git a/ui/src/keyboardLayouts/es_ES.ts b/ui/src/keyboardLayouts/es_ES.ts new file mode 100644 index 0000000..9a53d93 --- /dev/null +++ b/ui/src/keyboardLayouts/es_ES.ts @@ -0,0 +1,164 @@ +import { KeyCombo } from "../keyboardLayouts" + +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 + +export const chars = { + A: { key: "KeyA", shift: true }, + "Ä": { key: "KeyA", shift: true, accentKey: keyTrema }, + "Á": { key: "KeyA", shift: true, accentKey: keyAcute }, + "Â": { key: "KeyA", shift: true, accentKey: keyHat }, + "À": { key: "KeyA", shift: true, accentKey: keyGrave }, + "Ã": { key: "KeyA", shift: true, accentKey: keyTilde }, + B: { key: "KeyB", shift: true }, + C: { key: "KeyC", shift: true }, + D: { key: "KeyD", shift: true }, + E: { key: "KeyE", shift: true }, + "Ë": { key: "KeyE", shift: true, accentKey: keyTrema }, + "É": { key: "KeyE", shift: true, accentKey: keyAcute }, + "Ê": { key: "KeyE", shift: true, accentKey: keyHat }, + "È": { key: "KeyE", shift: true, accentKey: keyGrave }, + "Ẽ": { key: "KeyE", shift: true, accentKey: keyTilde }, + 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: keyTrema }, + "Í": { key: "KeyI", shift: true, accentKey: keyAcute }, + "Î": { key: "KeyI", shift: true, accentKey: keyHat }, + "Ì": { key: "KeyI", shift: true, accentKey: keyGrave }, + "Ĩ": { key: "KeyI", shift: true, accentKey: keyTilde }, + 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: keyTrema }, + "Ó": { key: "KeyO", shift: true, accentKey: keyAcute }, + "Ô": { key: "KeyO", shift: true, accentKey: keyHat }, + "Ò": { key: "KeyO", shift: true, accentKey: keyGrave }, + "Õ": { key: "KeyO", shift: true, accentKey: keyTilde }, + 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: keyTrema }, + "Ú": { key: "KeyU", shift: true, accentKey: keyAcute }, + "Û": { key: "KeyU", shift: true, accentKey: keyHat }, + "Ù": { key: "KeyU", shift: true, accentKey: keyGrave }, + "Ũ": { key: "KeyU", shift: true, accentKey: keyTilde }, + V: { key: "KeyV", shift: true }, + W: { key: "KeyW", shift: true }, + X: { key: "KeyX", shift: true }, + Y: { key: "KeyY", shift: true }, + Z: { key: "KeyZ", shift: true }, + a: { key: "KeyA" }, + "ä": { key: "KeyA", accentKey: keyTrema }, + "á": { key: "KeyA", accentKey: keyAcute }, + "â": { key: "KeyA", accentKey: keyHat }, + "à": { key: "KeyA", accentKey: keyGrave }, + "ã": { key: "KeyA", accentKey: keyTilde }, + b: { key: "KeyB" }, + c: { key: "KeyC" }, + d: { key: "KeyD" }, + e: { key: "KeyE" }, + "ë": { key: "KeyE", accentKey: keyTrema }, + "é": { key: "KeyE", accentKey: keyAcute }, + "ê": { key: "KeyE", accentKey: keyHat }, + "è": { key: "KeyE", accentKey: keyGrave }, + "ẽ": { key: "KeyE", accentKey: keyTilde }, + "€": { key: "KeyE", altRight: true }, + f: { key: "KeyF" }, + g: { key: "KeyG" }, + h: { key: "KeyH" }, + i: { key: "KeyI" }, + "ï": { key: "KeyI", accentKey: keyTrema }, + "í": { key: "KeyI", accentKey: keyAcute }, + "î": { key: "KeyI", accentKey: keyHat }, + "ì": { key: "KeyI", accentKey: keyGrave }, + "ĩ": { key: "KeyI", accentKey: keyTilde }, + j: { key: "KeyJ" }, + k: { key: "KeyK" }, + l: { key: "KeyL" }, + m: { key: "KeyM" }, + n: { key: "KeyN" }, + o: { key: "KeyO" }, + "ó": { key: "KeyO", accentKey: keyAcute }, + "ô": { key: "KeyO", accentKey: keyHat }, + "ò": { key: "KeyO", accentKey: keyGrave }, + "õ": { key: "KeyO", accentKey: keyTilde }, + p: { key: "KeyP" }, + q: { key: "KeyQ" }, + r: { key: "KeyR" }, + s: { key: "KeyS" }, + t: { key: "KeyT" }, + u: { key: "KeyU" }, + "ú": { key: "KeyU", accentKey: keyAcute }, + "û": { key: "KeyU", accentKey: keyHat }, + "ù": { key: "KeyU", accentKey: keyGrave }, + "ũ": { key: "KeyU", accentKey: keyTilde }, + v: { key: "KeyV" }, + w: { key: "KeyW" }, + x: { key: "KeyX" }, + y: { key: "KeyY" }, + z: { key: "KeyZ" }, + "º": { 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" }, + "Ç": { key: "Backslash", shift: 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" }, + "\n": { key: "Enter" }, + Enter: { key: "Enter" }, + Tab: { key: "Tab" }, +} as Record; From f810f09ab0d0fe418ef616e37f06829d07defe6c Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Sat, 3 May 2025 00:20:22 +0200 Subject: [PATCH 13/29] Fix fr_FR special characters --- ui/src/keyboardLayouts/fr_FR.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/src/keyboardLayouts/fr_FR.ts b/ui/src/keyboardLayouts/fr_FR.ts index 9d66d16..755deb1 100644 --- a/ui/src/keyboardLayouts/fr_FR.ts +++ b/ui/src/keyboardLayouts/fr_FR.ts @@ -5,8 +5,8 @@ const keyHat = { key: "BracketLeft" } // accent circonflexe (accent hat), mark ^ export const chars = { A: { key: "KeyQ", shift: true }, - "Ä": { key: "KeyA", shift: true, accentKey: keyTrema }, - "Â": { key: "KeyA", shift: true, accentKey: keyHat }, + "Ä": { key: "KeyQ", shift: true, accentKey: keyTrema }, + "Â": { key: "KeyQ", shift: true, accentKey: keyHat }, B: { key: "KeyB", shift: true }, C: { key: "KeyC", shift: true }, D: { key: "KeyD", shift: true }, @@ -41,8 +41,8 @@ export const chars = { Y: { key: "KeyY", shift: true }, Z: { key: "KeyW", shift: true }, a: { key: "KeyQ" }, - "ä": { key: "KeyA", accentKey: keyTrema }, - "â": { key: "KeyA", accentKey: keyHat }, + "ä": { key: "KeyQ", accentKey: keyTrema }, + "â": { key: "KeyQ", accentKey: keyHat }, b: { key: "KeyB" }, c: { key: "KeyC" }, d: { key: "KeyD" }, From cd10112ff255995d8c641a7d7e2280febeb171bd Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Sat, 3 May 2025 00:50:40 +0200 Subject: [PATCH 14/29] Add more keys to Spanish --- ui/src/keyboardLayouts/es_ES.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ui/src/keyboardLayouts/es_ES.ts b/ui/src/keyboardLayouts/es_ES.ts index 9a53d93..9e34bea 100644 --- a/ui/src/keyboardLayouts/es_ES.ts +++ b/ui/src/keyboardLayouts/es_ES.ts @@ -89,6 +89,7 @@ export const chars = { m: { key: "KeyM" }, n: { key: "KeyN" }, o: { key: "KeyO" }, + "ö": { key: "KeyO", accentKey: keyTrema }, "ó": { key: "KeyO", accentKey: keyAcute }, "ô": { key: "KeyO", accentKey: keyHat }, "ò": { key: "KeyO", accentKey: keyGrave }, @@ -99,6 +100,7 @@ export const chars = { s: { key: "KeyS" }, t: { key: "KeyT" }, u: { key: "KeyU" }, + "ü": { key: "KeyU", accentKey: keyTrema }, "ú": { key: "KeyU", accentKey: keyAcute }, "û": { key: "KeyU", accentKey: keyHat }, "ù": { key: "KeyU", accentKey: keyGrave }, From 5fb7a2117cf056f4cedb8f82a9bfd80f3009754d Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Sat, 3 May 2025 22:41:17 +0200 Subject: [PATCH 15/29] Remove default value shift: false --- ui/src/keyboardLayouts/en_US.ts | 96 ++++++++++++++++----------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/ui/src/keyboardLayouts/en_US.ts b/ui/src/keyboardLayouts/en_US.ts index b1e9dea..02dc5ee 100644 --- a/ui/src/keyboardLayouts/en_US.ts +++ b/ui/src/keyboardLayouts/en_US.ts @@ -27,75 +27,75 @@ export const chars = { X: { key: "KeyX", shift: true }, Y: { key: "KeyY", shift: true }, Z: { key: "KeyZ", shift: true }, - a: { key: "KeyA", shift: false }, - b: { key: "KeyB", shift: false }, - c: { key: "KeyC", shift: false }, - d: { key: "KeyD", shift: false }, - e: { key: "KeyE", shift: false }, - f: { key: "KeyF", shift: false }, - g: { key: "KeyG", shift: false }, - h: { key: "KeyH", shift: false }, - i: { key: "KeyI", shift: false }, - j: { key: "KeyJ", shift: false }, - k: { key: "KeyK", shift: false }, - l: { key: "KeyL", shift: false }, - m: { key: "KeyM", shift: false }, - n: { key: "KeyN", shift: false }, - o: { key: "KeyO", shift: false }, - p: { key: "KeyP", shift: false }, - q: { key: "KeyQ", shift: false }, - r: { key: "KeyR", shift: false }, - s: { key: "KeyS", shift: false }, - t: { key: "KeyT", shift: false }, - u: { key: "KeyU", shift: false }, - v: { key: "KeyV", shift: false }, - w: { key: "KeyW", shift: false }, - x: { key: "KeyX", shift: false }, - y: { key: "KeyY", shift: false }, - z: { key: "KeyZ", shift: false }, - 1: { key: "Digit1", shift: false }, + a: { key: "KeyA" }, + b: { key: "KeyB" }, + c: { key: "KeyC" }, + d: { key: "KeyD" }, + e: { key: "KeyE" }, + f: { key: "KeyF" }, + g: { key: "KeyG" }, + h: { key: "KeyH" }, + i: { key: "KeyI" }, + j: { key: "KeyJ" }, + k: { key: "KeyK" }, + l: { key: "KeyL" }, + m: { key: "KeyM" }, + n: { key: "KeyN" }, + o: { key: "KeyO" }, + p: { key: "KeyP" }, + q: { key: "KeyQ" }, + r: { key: "KeyR" }, + s: { key: "KeyS" }, + t: { key: "KeyT" }, + u: { key: "KeyU" }, + v: { key: "KeyV" }, + w: { key: "KeyW" }, + x: { key: "KeyX" }, + y: { key: "KeyY" }, + z: { key: "KeyZ" }, + 1: { key: "Digit1" }, "!": { key: "Digit1", shift: true }, - 2: { key: "Digit2", shift: false }, + 2: { key: "Digit2" }, "@": { key: "Digit2", shift: true }, - 3: { key: "Digit3", shift: false }, + 3: { key: "Digit3" }, "#": { key: "Digit3", shift: true }, - 4: { key: "Digit4", shift: false }, + 4: { key: "Digit4" }, $: { key: "Digit4", shift: true }, "%": { key: "Digit5", shift: true }, - 5: { key: "Digit5", shift: false }, + 5: { key: "Digit5" }, "^": { key: "Digit6", shift: true }, - 6: { key: "Digit6", shift: false }, + 6: { key: "Digit6" }, "&": { key: "Digit7", shift: true }, - 7: { key: "Digit7", shift: false }, + 7: { key: "Digit7" }, "*": { key: "Digit8", shift: true }, - 8: { key: "Digit8", shift: false }, + 8: { key: "Digit8" }, "(": { key: "Digit9", shift: true }, - 9: { key: "Digit9", shift: false }, + 9: { key: "Digit9" }, ")": { key: "Digit0", shift: true }, - 0: { key: "Digit0", shift: false }, - "-": { key: "Minus", shift: false }, + 0: { key: "Digit0" }, + "-": { key: "Minus" }, _: { key: "Minus", shift: true }, - "=": { key: "Equal", shift: false }, + "=": { key: "Equal" }, "+": { key: "Equal", shift: true }, - "'": { key: "Quote", shift: false }, + "'": { key: "Quote" }, '"': { key: "Quote", shift: true }, - ",": { key: "Comma", shift: false }, + ",": { key: "Comma" }, "<": { key: "Comma", shift: true }, - "/": { key: "Slash", shift: false }, + "/": { key: "Slash" }, "?": { key: "Slash", shift: true }, - ".": { key: "Period", shift: false }, + ".": { key: "Period" }, ">": { key: "Period", shift: true }, - ";": { key: "Semicolon", shift: false }, + ";": { key: "Semicolon" }, ":": { key: "Semicolon", shift: true }, - "[": { key: "BracketLeft", shift: false }, + "[": { key: "BracketLeft" }, "{": { key: "BracketLeft", shift: true }, - "]": { key: "BracketRight", shift: false }, + "]": { key: "BracketRight" }, "}": { key: "BracketRight", shift: true }, - "\\": { key: "Backslash", shift: false }, + "\\": { key: "Backslash" }, "|": { key: "Backslash", shift: true }, - "`": { key: "Backquote", shift: false }, + "`": { key: "Backquote" }, "~": { key: "Backquote", shift: true }, - "§": { key: "IntlBackslash", shift: false }, + "§": { key: "IntlBackslash" }, "±": { key: "IntlBackslash", shift: true }, " ": { key: "Space", shift: false }, "\n": { key: "Enter", shift: false }, From dd7b2d4dcfeda883b9fc7a24687261ed696b1e57 Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Sat, 3 May 2025 23:56:56 +0200 Subject: [PATCH 16/29] Add Norwegian --- ui/src/keyboardLayouts.ts | 3 + ui/src/keyboardLayouts/nb_NO.ts | 165 ++++++++++++++++++++++++++++++++ 2 files changed, 168 insertions(+) create mode 100644 ui/src/keyboardLayouts/nb_NO.ts diff --git a/ui/src/keyboardLayouts.ts b/ui/src/keyboardLayouts.ts index 723e855..ce77e9f 100644 --- a/ui/src/keyboardLayouts.ts +++ b/ui/src/keyboardLayouts.ts @@ -2,6 +2,7 @@ import { chars as chars_en_UK } from "@/keyboardLayouts/en_UK" import { chars as chars_en_US } from "@/keyboardLayouts/en_US" import { chars as chars_fr_FR } from "@/keyboardLayouts/fr_FR" import { chars as chars_de_DE } from "@/keyboardLayouts/de_DE" +import { chars as chars_nb_NO } from "@/keyboardLayouts/nb_NO" import { chars as chars_es_ES } from "@/keyboardLayouts/es_ES" import { chars as chars_sv_SE } from "@/keyboardLayouts/sv_SE" import { chars as chars_fr_CH } from "@/keyboardLayouts/fr_CH" @@ -15,6 +16,7 @@ export const layouts = { "en_US": "English (US)", "fr_FR": "French", "de_DE": "German", + "nb_NO": "Norwegian", "es_ES": "Spanish", "sv_SE": "Swedish", "fr_CH": "Swiss French", @@ -26,6 +28,7 @@ export const chars = { "en_US": chars_en_US, "fr_FR": chars_fr_FR, "de_DE": chars_de_DE, + "nb_NO": chars_nb_NO, "es_ES": chars_es_ES, "sv_SE": chars_sv_SE, "fr_CH": chars_fr_CH, diff --git a/ui/src/keyboardLayouts/nb_NO.ts b/ui/src/keyboardLayouts/nb_NO.ts new file mode 100644 index 0000000..de18561 --- /dev/null +++ b/ui/src/keyboardLayouts/nb_NO.ts @@ -0,0 +1,165 @@ +import { KeyCombo } from "../keyboardLayouts" + +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 + +export const chars = { + A: { key: "KeyA", shift: true }, + "Ä": { key: "KeyA", shift: true, accentKey: keyTrema }, + "Á": { key: "KeyA", shift: true, accentKey: keyAcute }, + "Â": { key: "KeyA", shift: true, accentKey: keyHat }, + "À": { key: "KeyA", shift: true, accentKey: keyGrave }, + "Ã": { key: "KeyA", shift: true, accentKey: keyTilde }, + B: { key: "KeyB", shift: true }, + C: { key: "KeyC", shift: true }, + D: { key: "KeyD", shift: true }, + E: { key: "KeyE", shift: true }, + "Ë": { key: "KeyE", shift: true, accentKey: keyTrema }, + "É": { key: "KeyE", shift: true, accentKey: keyAcute }, + "Ê": { key: "KeyE", shift: true, accentKey: keyHat }, + "È": { key: "KeyE", shift: true, accentKey: keyGrave }, + "Ẽ": { key: "KeyE", shift: true, accentKey: keyTilde }, + 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: keyTrema }, + "Í": { key: "KeyI", shift: true, accentKey: keyAcute }, + "Î": { key: "KeyI", shift: true, accentKey: keyHat }, + "Ì": { key: "KeyI", shift: true, accentKey: keyGrave }, + "Ĩ": { key: "KeyI", shift: true, accentKey: keyTilde }, + 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: keyTrema }, + "Ó": { key: "KeyO", shift: true, accentKey: keyAcute }, + "Ô": { key: "KeyO", shift: true, accentKey: keyHat }, + "Ò": { key: "KeyO", shift: true, accentKey: keyGrave }, + "Õ": { key: "KeyO", shift: true, accentKey: keyTilde }, + 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: keyTrema }, + "Ú": { key: "KeyU", shift: true, accentKey: keyAcute }, + "Û": { key: "KeyU", shift: true, accentKey: keyHat }, + "Ù": { key: "KeyU", shift: true, accentKey: keyGrave }, + "Ũ": { key: "KeyU", shift: true, accentKey: keyTilde }, + 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: keyTrema }, + "á": { key: "KeyA", accentKey: keyAcute }, + "â": { key: "KeyA", accentKey: keyHat }, + "à": { key: "KeyA", accentKey: keyGrave }, + "ã": { key: "KeyA", accentKey: keyTilde }, + b: { key: "KeyB" }, + c: { key: "KeyC" }, + d: { key: "KeyD" }, + e: { key: "KeyE" }, + "ë": { key: "KeyE", accentKey: keyTrema }, + "é": { key: "KeyE", accentKey: keyAcute }, + "ê": { key: "KeyE", accentKey: keyHat }, + "è": { key: "KeyE", accentKey: keyGrave }, + "ẽ": { key: "KeyE", accentKey: keyTilde }, + "€": { key: "KeyE", altRight: true }, + f: { key: "KeyF" }, + g: { key: "KeyG" }, + h: { key: "KeyH" }, + i: { key: "KeyI" }, + "ï": { key: "KeyI", accentKey: keyTrema }, + "í": { key: "KeyI", accentKey: keyAcute }, + "î": { key: "KeyI", accentKey: keyHat }, + "ì": { key: "KeyI", accentKey: keyGrave }, + "ĩ": { key: "KeyI", accentKey: keyTilde }, + j: { key: "KeyJ" }, + k: { key: "KeyK" }, + l: { key: "KeyL" }, + m: { key: "KeyM" }, + n: { key: "KeyN" }, + o: { key: "KeyO" }, + "ö": { key: "KeyO", accentKey: keyTrema }, + "ó": { key: "KeyO", accentKey: keyAcute }, + "ô": { key: "KeyO", accentKey: keyHat }, + "ò": { key: "KeyO", accentKey: keyGrave }, + "õ": { key: "KeyO", accentKey: keyTilde }, + p: { key: "KeyP" }, + q: { key: "KeyQ" }, + r: { key: "KeyR" }, + s: { key: "KeyS" }, + t: { key: "KeyT" }, + u: { key: "KeyU" }, + "ü": { key: "KeyU", accentKey: keyTrema }, + "ú": { key: "KeyU", accentKey: keyAcute }, + "û": { key: "KeyU", accentKey: keyHat }, + "ù": { key: "KeyU", accentKey: keyGrave }, + "ũ": { key: "KeyU", accentKey: keyTilde }, + v: { key: "KeyV" }, + w: { key: "KeyW" }, + x: { key: "KeyX" }, + y: { key: "KeyZ" }, + z: { key: "KeyY" }, + "|": { key: "Backquote" }, + "§": { key: "Backquote", shift: true }, + 1: { key: "Digit1" }, + "!": { key: "Digit1", shift: 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 }, + "$": { key: "Digit4", altRight: true }, + 5: { key: "Digit5" }, + "%": { key: "Digit5", shift: true }, + 6: { key: "Digit6" }, + "&": { key: "Digit6", shift: true }, + 7: { key: "Digit7" }, + "/": { key: "Digit7", shift: true }, + "{": { key: "Digit7", altRight: true }, + 8: { key: "Digit8" }, + "(": { key: "Digit8", shift: true }, + "[": { key: "Digit8", altRight: true }, + 9: { key: "Digit9" }, + ")": { key: "Digit9", shift: true }, + "]": { key: "Digit9", altRight: true }, + 0: { key: "Digit0" }, + "=": { key: "Digit0", shift: true }, + "}": { key: "Digit0", altRight: true }, + "+": { key: "Minus" }, + "?": { key: "Minus", shift: true }, + "\\": { key: "Equal" }, + "å": { key: "BracketLeft" }, + "Å": { key: "BracketLeft", shift: true }, + "ø": { key: "Semicolon" }, + "Ø": { key: "Semicolon", shift: true }, + "æ": { key: "Quote" }, + "Æ": { key: "Quote", shift: true }, + "'": { key: "Backslash" }, + "*": { key: "Backslash", shift: 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" }, + "\n": { key: "Enter" }, + Enter: { key: "Enter" }, + Tab: { key: "Tab" }, +} as Record; From 8364c37f9aa1b8c0170965445975d3a3eb4e48d8 Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Sun, 4 May 2025 00:25:55 +0200 Subject: [PATCH 17/29] =?UTF-8?q?Operator=20precedence=20=F0=9F=A4=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/src/components/popovers/PasteModal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/components/popovers/PasteModal.tsx b/ui/src/components/popovers/PasteModal.tsx index 6ad60db..38c392d 100644 --- a/ui/src/components/popovers/PasteModal.tsx +++ b/ui/src/components/popovers/PasteModal.tsx @@ -18,7 +18,7 @@ const hidKeyboardPayload = (keys: number[], modifier: number) => { }; const modifierCode = (shift?: boolean, altRight?: boolean) => { - return shift ? modifiers["ShiftLeft"] : 0 + return (shift ? modifiers["ShiftLeft"] : 0) | (altRight ? modifiers["AltRight"] : 0) } const noModifier = 0 From 7065c42e91edf59529413a586d61611c2062bee9 Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Sun, 4 May 2025 00:26:32 +0200 Subject: [PATCH 18/29] Add Italian --- ui/src/keyboardLayouts.ts | 3 + ui/src/keyboardLayouts/it_IT.ts | 111 ++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 ui/src/keyboardLayouts/it_IT.ts diff --git a/ui/src/keyboardLayouts.ts b/ui/src/keyboardLayouts.ts index ce77e9f..000dfbe 100644 --- a/ui/src/keyboardLayouts.ts +++ b/ui/src/keyboardLayouts.ts @@ -2,6 +2,7 @@ import { chars as chars_en_UK } from "@/keyboardLayouts/en_UK" import { chars as chars_en_US } from "@/keyboardLayouts/en_US" import { chars as chars_fr_FR } from "@/keyboardLayouts/fr_FR" import { chars as chars_de_DE } from "@/keyboardLayouts/de_DE" +import { chars as chars_it_IT } from "@/keyboardLayouts/it_IT" import { chars as chars_nb_NO } from "@/keyboardLayouts/nb_NO" import { chars as chars_es_ES } from "@/keyboardLayouts/es_ES" import { chars as chars_sv_SE } from "@/keyboardLayouts/sv_SE" @@ -16,6 +17,7 @@ export const layouts = { "en_US": "English (US)", "fr_FR": "French", "de_DE": "German", + "it_IT": "Italian", "nb_NO": "Norwegian", "es_ES": "Spanish", "sv_SE": "Swedish", @@ -28,6 +30,7 @@ export const chars = { "en_US": chars_en_US, "fr_FR": chars_fr_FR, "de_DE": chars_de_DE, + "it_IT": chars_it_IT, "nb_NO": chars_nb_NO, "es_ES": chars_es_ES, "sv_SE": chars_sv_SE, diff --git a/ui/src/keyboardLayouts/it_IT.ts b/ui/src/keyboardLayouts/it_IT.ts new file mode 100644 index 0000000..fa87e11 --- /dev/null +++ b/ui/src/keyboardLayouts/it_IT.ts @@ -0,0 +1,111 @@ +import { KeyCombo } from "../keyboardLayouts" + +export const chars = { + A: { key: "KeyA", shift: true }, + B: { key: "KeyB", shift: true }, + C: { key: "KeyC", shift: true }, + D: { key: "KeyD", shift: true }, + E: { key: "KeyE", shift: true }, + F: { key: "KeyF", shift: true }, + G: { key: "KeyG", shift: true }, + H: { key: "KeyH", shift: true }, + I: { key: "KeyI", shift: true }, + 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 }, + 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 }, + V: { key: "KeyV", shift: true }, + W: { key: "KeyW", shift: true }, + X: { key: "KeyX", shift: true }, + Y: { key: "KeyY", shift: true }, + Z: { key: "KeyZ", shift: true }, + a: { key: "KeyA" }, + b: { key: "KeyB" }, + c: { key: "KeyC" }, + d: { key: "KeyD" }, + e: { key: "KeyE" }, + "€": { key: "KeyE", altRight: true }, + f: { key: "KeyF" }, + g: { key: "KeyG" }, + h: { key: "KeyH" }, + i: { key: "KeyI" }, + j: { key: "KeyJ" }, + k: { key: "KeyK" }, + l: { key: "KeyL" }, + m: { key: "KeyM" }, + n: { key: "KeyN" }, + o: { key: "KeyO" }, + p: { key: "KeyP" }, + q: { key: "KeyQ" }, + r: { key: "KeyR" }, + s: { key: "KeyS" }, + t: { key: "KeyT" }, + u: { key: "KeyU" }, + v: { key: "KeyV" }, + w: { key: "KeyW" }, + x: { key: "KeyX" }, + y: { key: "KeyY" }, + z: { key: "KeyZ" }, + "\\": { key: "Backquote" }, + "|": { key: "Backquote", shift: true }, + 1: { key: "Digit1" }, + "!": { key: "Digit1", shift: true }, + 2: { key: "Digit2" }, + "\"": { key: "Digit2", shift: 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 }, + 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" }, + "^": { key: "Equal", shift: true }, + "è": { key: "BracketLeft" }, + "é": { key: "BracketLeft", shift: true }, + "[": { key: "BracketLeft", altRight: true }, + "{": { key: "BracketLeft", shift: true, altRight: true }, + "+": { key: "BracketRight" }, + "*": { key: "BracketRight", shift: true }, + "]": { key: "BracketRight", altRight: true }, + "}": { key: "BracketRight", shift: true, altRight: true }, + "ò": { key: "Semicolon" }, + "ç": { key: "Semicolon", shift: true }, + "@": { key: "Semicolon", altRight: true }, + "à": { key: "Quote" }, + "°": { key: "Quote", shift: true }, + "#": { key: "Quote", altRight: true }, + "ù": { key: "Backslash" }, + "§": { key: "Backslash", shift: 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" }, + "\n": { key: "Enter" }, + Enter: { key: "Enter" }, + Tab: { key: "Tab" }, +} as Record; From e10f0db3ba4977a7301e836491fa307613c470af Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Sun, 4 May 2025 02:37:51 +0200 Subject: [PATCH 19/29] Add Czech --- ui/src/keyboardLayouts.ts | 3 + ui/src/keyboardLayouts/cs_CZ.ts | 242 ++++++++++++++++++++++++++++++++ 2 files changed, 245 insertions(+) create mode 100644 ui/src/keyboardLayouts/cs_CZ.ts diff --git a/ui/src/keyboardLayouts.ts b/ui/src/keyboardLayouts.ts index 000dfbe..35d5703 100644 --- a/ui/src/keyboardLayouts.ts +++ b/ui/src/keyboardLayouts.ts @@ -1,3 +1,4 @@ +import { chars as chars_cs_CZ } from "@/keyboardLayouts/cs_CZ" import { chars as chars_en_UK } from "@/keyboardLayouts/en_UK" import { chars as chars_en_US } from "@/keyboardLayouts/en_US" import { chars as chars_fr_FR } from "@/keyboardLayouts/fr_FR" @@ -13,6 +14,7 @@ type KeyInfo = { key: string | number; shift?: boolean, altRight?: boolean } export type KeyCombo = KeyInfo & { deadKey?: boolean, accentKey?: KeyInfo } export const layouts = { + "cs_CZ": "Czech", "en_UK": "English (UK)", "en_US": "English (US)", "fr_FR": "French", @@ -26,6 +28,7 @@ export const layouts = { } as Record; export const chars = { + "cs_CZ": chars_cs_CZ, "en_UK": chars_en_UK, "en_US": chars_en_US, "fr_FR": chars_fr_FR, diff --git a/ui/src/keyboardLayouts/cs_CZ.ts b/ui/src/keyboardLayouts/cs_CZ.ts new file mode 100644 index 0000000..e889e32 --- /dev/null +++ b/ui/src/keyboardLayouts/cs_CZ.ts @@ -0,0 +1,242 @@ +import { KeyCombo } from "../keyboardLayouts" + +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 + +export const chars = { + A: { key: "KeyA", shift: true }, + "Ä": { key: "KeyA", shift: true, accentKey: keyTrema }, + "Á": { key: "KeyA", shift: true, accentKey: keyAcute }, + "Â": { key: "KeyA", shift: true, accentKey: keyHat }, + "À": { key: "KeyA", shift: true, accentKey: keyGrave }, + "Ã": { key: "KeyA", shift: true, accentKey: keyTilde }, + "Ȧ": { key: "KeyA", shift: true, accentKey: keyOverdot }, + "Ą": { key: "KeyA", shift: true, accentKey: keyHook }, + B: { key: "KeyB", shift: true }, + "Ḃ": { key: "KeyB", shift: true, accentKEy: keyOverdot }, + C: { key: "KeyC", shift: true }, + "Č": { key: "KeyC", shift: true, accentKey: keyCaron }, + "Ċ": { key: "KeyC", shift: true, accentKey: keyOverdot }, + "Ç": { key: "KeyC", shift: true, accentKey: keyCedille }, + D: { key: "KeyD", shift: true }, + "Ď": { key: "KeyD", shift: true, accentKey: keyCaron }, + "Ḋ": { key: "KeyD", shift: true, accentKey: keyOverdot }, + E: { key: "KeyE", shift: true }, + "Ë": { key: "KeyE", shift: true, accentKey: keyTrema }, + "É": { key: "KeyE", shift: true, accentKey: keyAcute }, + "Ê": { key: "KeyE", shift: true, accentKey: keyHat }, + "Ě": { key: "KeyE", shift: true, accentKey: keyCaron }, + "È": { key: "KeyE", shift: true, accentKey: keyGrave }, + "Ẽ": { key: "KeyE", shift: true, accentKey: keyTilde }, + "Ė": { key: "KeyE", shift: true, accentKEy: keyOverdot }, + "Ę": { key: "KeyE", shift: true, accentKey: keyHook }, + F: { key: "KeyF", shift: true }, + "Ḟ": { key: "KeyF", shift: true, accentKey: keyOverdot }, + G: { key: "KeyG", shift: true }, + "Ġ": { key: "KeyG", shift: true, accentKey: keyOverdot }, + H: { key: "KeyH", shift: true }, + "Ḣ": { key: "KeyH", shift: true, accentKey: keyOverdot }, + I: { key: "KeyI", shift: true }, + "Ï": { key: "KeyI", shift: true, accentKey: keyTrema }, + "Í": { key: "KeyI", shift: true, accentKey: keyAcute }, + "Î": { key: "KeyI", shift: true, accentKey: keyHat }, + "Ì": { key: "KeyI", shift: true, accentKey: keyGrave }, + "Ĩ": { key: "KeyI", shift: true, accentKey: keyTilde }, + "İ": { key: "KeyI", shift: true, accentKey: keyOverdot }, + "Į": { key: "KeyI", shift: true, accentKey: keyHook }, + J: { key: "KeyJ", shift: true }, + K: { key: "KeyK", shift: true }, + L: { key: "KeyL", shift: true }, + "Ŀ": { key: "KeyL", shift: true }, + M: { key: "KeyM", shift: true }, + "Ṁ": { key: "KeyM", shift: true }, + N: { key: "KeyN", shift: true }, + "Ň": { key: "KeyN", shift: true, accentKey: keyCaron }, + "Ñ": { key: "KeyN", shift: true, accentKey: keyTilde }, + "Ṅ": { key: "KeyN", shift: true, accentKEy: keyOverdot }, + O: { key: "KeyO", shift: true }, + "Ö": { key: "KeyO", shift: true, accentKey: keyTrema }, + "Ó": { key: "KeyO", shift: true, accentKey: keyAcute }, + "Ô": { key: "KeyO", shift: true, accentKey: keyHat }, + "Ò": { key: "KeyO", shift: true, accentKey: keyGrave }, + "Õ": { key: "KeyO", shift: true, accentKey: keyTilde }, + "Ȯ": { key: "KeyO", shift: true, accentKey: keyOverdot }, + "Ǫ": { key: "KeyO", shift: true, accentKey: keyHook }, + P: { key: "KeyP", shift: true }, + "Ṗ": { key: "KeyP", shift: true, accentKey: keyOverdot }, + Q: { key: "KeyQ", shift: true }, + R: { key: "KeyR", shift: true }, + "Ř": { key: "KeyR", shift: true, accentKey: keyCaron }, + "Ṙ": { key: "KeyR", shift: true, accentKey: keyOverdot }, + S: { key: "KeyS", shift: true }, + "Š": { key: "KeyS", shift: true, accentKey: keyCaron }, + "Ṡ": { key: "KeyS", shift: true, accentKey: keyOverdot }, + T: { key: "KeyT", shift: true }, + "Ť": { key: "KeyT", shift: true, accentKey: keyCaron }, + "Ṫ": { key: "KeyT", shift: true, accentKey: keyOverdot }, + U: { key: "KeyU", shift: true }, + "Ü": { key: "KeyU", shift: true, accentKey: keyTrema }, + "Ú": { key: "KeyU", shift: true, accentKey: keyAcute }, + "Û": { key: "KeyU", shift: true, accentKey: keyHat }, + "Ù": { key: "KeyU", shift: true, accentKey: keyGrave }, + "Ũ": { key: "KeyU", shift: true, accentKey: keyTilde }, + "Ů": { key: "KeyU", shift: true, accentKey: keyRing }, + "Ų": { key: "KeyU", shift: true, accentKey: keyHook }, + V: { key: "KeyV", shift: true }, + W: { key: "KeyW", shift: true }, + "Ẇ": { key: "KeyW", shift: true, accentKey: keyOverdot }, + X: { key: "KeyX", shift: true }, + "Ẋ": { key: "KeyX", shift: true, accentKey: keyOverdot }, + Y: { key: "KeyY", shift: true }, + "Ý": { key: "KeyY", shift: true, accentKey: keyAcute }, + "Ẏ": { key: "KeyY", shift: true, accentKey: keyOverdot }, + Z: { key: "KeyZ", shift: true }, + "Ż": { key: "KeyZ", shift: true, accentKey: keyOverdot }, + a: { key: "KeyA" }, + "ä": { key: "KeyA", accentKey: keyTrema }, + "â": { key: "KeyA", accentKey: keyHat }, + "à": { key: "KeyA", accentKey: keyGrave }, + "ã": { key: "KeyA", accentKey: keyTilde }, + "ȧ": { key: "KeyA", accentKey: keyOverdot }, + "ą": { key: "KeyA", accentKey: keyHook }, + b: { key: "KeyB" }, + "{": { key: "KeyB", altRight: true }, + "ḃ": { key: "KeyB", accentKey: keyOverdot }, + c: { key: "KeyC" }, + "&": { key: "KeyC", altRight: true }, + "ç": { key: "KeyC", accentKey: keyCedille }, + "ċ": { key: "KeyC", accentKey: keyOverdot }, + d: { key: "KeyD" }, + "ď": { key: "KeyD", accentKey: keyCaron }, + "ḋ": { key: "KeyD", accentKey: keyOverdot }, + "Đ": { key: "KeyD", altRight: true }, + e: { key: "KeyE" }, + "ë": { key: "KeyE", accentKey: keyTrema }, + "ê": { key: "KeyE", accentKey: keyHat }, + "ẽ": { key: "KeyE", accentKey: keyTilde }, + "è": { key: "KeyE", accentKey: keyGrave }, + "ė": { key: "KeyE", accentKey: keyOverdot }, + "ę": { key: "KeyE", accentKey: keyHook }, + "€": { key: "KeyE", altRight: true }, + f: { key: "KeyF" }, + "ḟ": { key: "KeyF", accentKey: keyOverdot }, + "[": { key: "KeyF", altRight: true }, + g: { key: "KeyG" }, + "ġ": { key: "KeyG", accentKey: keyOverdot }, + "]": { key: "KeyF", altRight: true }, + h: { key: "KeyH" }, + "ḣ": { key: "KeyH", accentKey: keyOverdot }, + i: { key: "KeyI" }, + "ï": { key: "KeyI", accentKey: keyTrema }, + "î": { key: "KeyI", accentKey: keyHat }, + "ì": { key: "KeyI", accentKey: keyGrave }, + "ĩ": { key: "KeyI", accentKey: keyTilde }, + "ı": { key: "KeyI", accentKey: keyOverdot }, + "į": { key: "KeyI", accentKey: keyHook }, + j: { key: "KeyJ" }, + "ȷ": { key: "KeyJ", accentKey: keyOverdot }, + k: { key: "KeyK" }, + "ł": { key: "KeyK", altRight: true }, + l: { key: "KeyL" }, + "ŀ": { key: "KeyL", accentKey: keyOverdot }, + "Ł": { key: "KeyL", altRight: true }, + m: { key: "KeyM" }, + "ṁ": { key: "KeyM", accentKey: keyOverdot }, + n: { key: "KeyN" }, + "}": { key: "KeyN", altRight: true }, + "ň": { key: "KeyN", accentKey: keyCaron }, + "ñ": { key: "KeyN", accentKey: keyTilde }, + "ṅ": { key: "KeyN", accentKey: keyOverdot }, + o: { key: "KeyO" }, + "ö": { key: "Key0", accentKey: keyTrema }, + "ó": { key: "KeyO", accentKey: keyAcute }, + "ô": { key: "KeyO", accentKey: keyHat }, + "ò": { key: "KeyO", accentKey: keyGrave }, + "õ": { key: "KeyO", accentKey: keyTilde }, + "ȯ": { key: "KeyO", accentKey: keyOverdot }, + "ǫ": { key: "KeyO", accentKey: keyHook }, + p: { key: "KeyP" }, + "ṗ": { key: "KeyP", accentKey: keyOverdot }, + q: { key: "KeyQ" }, + r: { key: "KeyR" }, + "ṙ": { key: "KeyR", accentKey: keyOverdot }, + s: { key: "KeyS" }, + "ṡ": { key: "KeyS", accentKey: keyOverdot }, + "đ": { key: "KeyS", altRight: true }, + t: { key: "KeyT" }, + "ť": { key: "KeyT", accentKey: keyCaron }, + "ṫ": { key: "KeyT", accentKey: keyOverdot }, + u: { key: "KeyU" }, + "ü": { key: "KeyU", accentKey: keyTrema }, + "û": { key: "KeyU", accentKey: keyHat }, + "ù": { key: "KeyU", accentKey: keyGrave }, + "ũ": { key: "KeyU", accentKey: keyTilde }, + "ų": { key: "KeyU", accentKey: keyHook }, + v: { key: "KeyV" }, + "@": { key: "KeyV", altRight: true }, + w: { key: "KeyW" }, + "ẇ": { key: "KeyW", accentKey: keyOverdot }, + x: { key: "KeyX" }, + "#": { key: "KeyX", altRight: true }, + "ẋ": { key: "KeyX", accentKey: keyOverdot }, + y: { key: "KeyY" }, + "ẏ": { key: "KeyY", accentKey: keyOverdot }, + z: { key: "KeyZ" }, + "ż": { key: "KeyZ", accentKey: keyOverdot }, + ";": { key: "Backquote" }, + "°": { key: "Backquote", shift: true, deadKey: true }, + "+": { key: "Digit1" }, + 1: { key: "Digit1", shift: true }, + "ě": { key: "Digit2" }, + 2: { key: "Digit2", shift: true }, + "š": { key: "Digit3" }, + 3: { key: "Digit3", shift: true }, + "č": { key: "Digit4" }, + 4: { key: "Digit4", shift: true }, + "ř": { key: "Digit5" }, + 5: { key: "Digit5", shift: true }, + "ž": { key: "Digit6" }, + 6: { key: "Digit6", shift: true }, + "ý": { key: "Digit7" }, + 7: { key: "Digit7", shift: true }, + "á": { key: "Digit8" }, + 8: { key: "Digit8", shift: true }, + "í": { key: "Digit9" }, + 9: { key: "Digit9", shift: true }, + "é": { key: "Digit0" }, + 0: { key: "Digit0", shift: true }, + "=": { key: "Minus" }, + "%": { key: "Minus", shift: true }, + "ú": { key: "BracketLeft" }, + "/": { key: "BracketLeft", shift: true }, + ")": { key: "BracketRight" }, + "(": { key: "BracketRight", shift: true }, + "ů": { key: "Semicolon" }, + "\"": { key: "Semicolon", shift: true }, + "§": { key: "Quote" }, + "!": { key: "Quote", shift: true }, + "'": { key: "Backslash", shift: true }, + ",": { key: "Comma" }, + "?": { key: "Comma", shift: true }, + "<": { key: "Comma", altRight: true }, + ".": { key: "Period" }, + ":": { key: "Period", shift: true }, + ">": { key: "Period", altRight: true }, + "-": { key: "Slash" }, + "_": { key: "Slash", shift: true }, + "*": { key: "Slash", altRight: true }, + "\\": { key: "IntlBackslash" }, + "|": { key: "IntlBackslash", shift: true }, + " ": { key: "Space" }, + "\n": { key: "Enter" }, + Enter: { key: "Enter" }, + Tab: { key: "Tab" }, +} as Record; From f8f225df6aad1cdfecdd0e2386fd71bd763f0fba Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Thu, 8 May 2025 20:25:53 +0200 Subject: [PATCH 20/29] Move guard statements outside of loop --- ui/src/components/popovers/PasteModal.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ui/src/components/popovers/PasteModal.tsx b/ui/src/components/popovers/PasteModal.tsx index 38c392d..2f0662d 100644 --- a/ui/src/components/popovers/PasteModal.tsx +++ b/ui/src/components/popovers/PasteModal.tsx @@ -56,14 +56,13 @@ export default function PasteModal() { setPasteMode(false); setDisableVideoFocusTrap(false); if (rpcDataChannel?.readyState !== "open" || !TextAreaRef.current) return; + if (!keyboardLayout) return; + if (!chars[keyboardLayout]) return; const text = TextAreaRef.current.value; try { for (const char of text) { - if (!keyboardLayout) continue; - if (!chars[keyboardLayout]) continue; - const { key, shift, altRight, deadKey, accentKey } = chars[keyboardLayout][char] if (!key) continue; From 707a33cb07edcce24b6cb89808c58308707ceba1 Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Thu, 8 May 2025 20:26:59 +0200 Subject: [PATCH 21/29] Move language name definitions into the keyboard layout files --- ui/src/keyboardLayouts.ts | 74 ++++++++++++++++----------------- ui/src/keyboardLayouts/cs_CZ.ts | 2 + ui/src/keyboardLayouts/de_CH.ts | 2 + ui/src/keyboardLayouts/de_DE.ts | 2 + ui/src/keyboardLayouts/en_UK.ts | 2 + ui/src/keyboardLayouts/en_US.ts | 2 + ui/src/keyboardLayouts/es_ES.ts | 2 + ui/src/keyboardLayouts/fr_CH.ts | 2 + ui/src/keyboardLayouts/fr_FR.ts | 2 + ui/src/keyboardLayouts/it_IT.ts | 2 + ui/src/keyboardLayouts/nb_NO.ts | 2 + ui/src/keyboardLayouts/sv_SE.ts | 2 + 12 files changed, 59 insertions(+), 37 deletions(-) diff --git a/ui/src/keyboardLayouts.ts b/ui/src/keyboardLayouts.ts index 35d5703..8dc0dec 100644 --- a/ui/src/keyboardLayouts.ts +++ b/ui/src/keyboardLayouts.ts @@ -1,42 +1,42 @@ -import { chars as chars_cs_CZ } from "@/keyboardLayouts/cs_CZ" -import { chars as chars_en_UK } from "@/keyboardLayouts/en_UK" -import { chars as chars_en_US } from "@/keyboardLayouts/en_US" -import { chars as chars_fr_FR } from "@/keyboardLayouts/fr_FR" -import { chars as chars_de_DE } from "@/keyboardLayouts/de_DE" -import { chars as chars_it_IT } from "@/keyboardLayouts/it_IT" -import { chars as chars_nb_NO } from "@/keyboardLayouts/nb_NO" -import { chars as chars_es_ES } from "@/keyboardLayouts/es_ES" -import { chars as chars_sv_SE } from "@/keyboardLayouts/sv_SE" -import { chars as chars_fr_CH } from "@/keyboardLayouts/fr_CH" -import { chars as chars_de_CH } from "@/keyboardLayouts/de_CH" +import { chars as chars_cs_CZ, name as name_cs_CZ } from "@/keyboardLayouts/cs_CZ" +import { chars as chars_en_UK, name as name_en_UK } from "@/keyboardLayouts/en_UK" +import { chars as chars_en_US, name as name_en_US } from "@/keyboardLayouts/en_US" +import { chars as chars_fr_FR, name as name_fr_FR } from "@/keyboardLayouts/fr_FR" +import { chars as chars_de_DE, name as name_de_DE } from "@/keyboardLayouts/de_DE" +import { chars as chars_it_IT, name as name_it_IT } from "@/keyboardLayouts/it_IT" +import { chars as chars_nb_NO, name as name_nb_NO } from "@/keyboardLayouts/nb_NO" +import { chars as chars_es_ES, name as name_es_ES } from "@/keyboardLayouts/es_ES" +import { chars as chars_sv_SE, name as name_sv_SE } from "@/keyboardLayouts/sv_SE" +import { chars as chars_fr_CH, name as name_fr_CH } from "@/keyboardLayouts/fr_CH" +import { chars as chars_de_CH, name as name_de_CH } from "@/keyboardLayouts/de_CH" type KeyInfo = { key: string | number; shift?: boolean, altRight?: boolean } export type KeyCombo = KeyInfo & { deadKey?: boolean, accentKey?: KeyInfo } -export const layouts = { - "cs_CZ": "Czech", - "en_UK": "English (UK)", - "en_US": "English (US)", - "fr_FR": "French", - "de_DE": "German", - "it_IT": "Italian", - "nb_NO": "Norwegian", - "es_ES": "Spanish", - "sv_SE": "Swedish", - "fr_CH": "Swiss French", - "de_CH": "Swiss German" -} as Record; +export const layouts: Record = { + cs_CZ: name_cs_CZ, + en_UK: name_en_UK, + en_US: name_en_US, + fr_FR: name_fr_FR, + de_DE: name_de_DE, + it_IT: name_it_IT, + nb_NO: name_nb_NO, + es_ES: name_es_ES, + sv_SE: name_sv_SE, + fr_CH: name_fr_CH, + de_CH: name_de_CH, +} -export const chars = { - "cs_CZ": chars_cs_CZ, - "en_UK": chars_en_UK, - "en_US": chars_en_US, - "fr_FR": chars_fr_FR, - "de_DE": chars_de_DE, - "it_IT": chars_it_IT, - "nb_NO": chars_nb_NO, - "es_ES": chars_es_ES, - "sv_SE": chars_sv_SE, - "fr_CH": chars_fr_CH, - "de_CH": chars_de_CH, -} as Record> +export const chars: Record> = { + cs_CZ: chars_cs_CZ, + en_UK: chars_en_UK, + en_US: chars_en_US, + fr_FR: chars_fr_FR, + de_DE: chars_de_DE, + it_IT: chars_it_IT, + nb_NO: chars_nb_NO, + es_ES: chars_es_ES, + sv_SE: chars_sv_SE, + fr_CH: chars_fr_CH, + de_CH: chars_de_CH, +}; diff --git a/ui/src/keyboardLayouts/cs_CZ.ts b/ui/src/keyboardLayouts/cs_CZ.ts index e889e32..ee2799c 100644 --- a/ui/src/keyboardLayouts/cs_CZ.ts +++ b/ui/src/keyboardLayouts/cs_CZ.ts @@ -1,5 +1,7 @@ import { KeyCombo } from "../keyboardLayouts" +export const name = "Czech"; + 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 diff --git a/ui/src/keyboardLayouts/de_CH.ts b/ui/src/keyboardLayouts/de_CH.ts index c822a7f..c3f1f8c 100644 --- a/ui/src/keyboardLayouts/de_CH.ts +++ b/ui/src/keyboardLayouts/de_CH.ts @@ -1,5 +1,7 @@ import { KeyCombo } from "../keyboardLayouts" +export const name = "Swiss German"; + 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 diff --git a/ui/src/keyboardLayouts/de_DE.ts b/ui/src/keyboardLayouts/de_DE.ts index 08a58d3..2833909 100644 --- a/ui/src/keyboardLayouts/de_DE.ts +++ b/ui/src/keyboardLayouts/de_DE.ts @@ -1,5 +1,7 @@ import { KeyCombo } from "../keyboardLayouts" +export const name = "German"; + 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 diff --git a/ui/src/keyboardLayouts/en_UK.ts b/ui/src/keyboardLayouts/en_UK.ts index 49d623f..ed0c8dd 100644 --- a/ui/src/keyboardLayouts/en_UK.ts +++ b/ui/src/keyboardLayouts/en_UK.ts @@ -1,5 +1,7 @@ import { KeyCombo } from "../keyboardLayouts" +export const name = "English (UK)"; + export const chars = { A: { key: "KeyA", shift: true }, B: { key: "KeyB", shift: true }, diff --git a/ui/src/keyboardLayouts/en_US.ts b/ui/src/keyboardLayouts/en_US.ts index 02dc5ee..592bf27 100644 --- a/ui/src/keyboardLayouts/en_US.ts +++ b/ui/src/keyboardLayouts/en_US.ts @@ -1,5 +1,7 @@ import { KeyCombo } from "../keyboardLayouts" +export const name = "English (US)"; + export const chars = { A: { key: "KeyA", shift: true }, B: { key: "KeyB", shift: true }, diff --git a/ui/src/keyboardLayouts/es_ES.ts b/ui/src/keyboardLayouts/es_ES.ts index 9e34bea..f979205 100644 --- a/ui/src/keyboardLayouts/es_ES.ts +++ b/ui/src/keyboardLayouts/es_ES.ts @@ -1,5 +1,7 @@ import { KeyCombo } from "../keyboardLayouts" +export const name = "Spanish"; + 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 diff --git a/ui/src/keyboardLayouts/fr_CH.ts b/ui/src/keyboardLayouts/fr_CH.ts index 02f2b69..e47af1d 100644 --- a/ui/src/keyboardLayouts/fr_CH.ts +++ b/ui/src/keyboardLayouts/fr_CH.ts @@ -1,6 +1,8 @@ import { KeyCombo } from "../keyboardLayouts" import { chars as chars_de_CH } from "./de_CH" +export const name = "Swiss French"; + export const chars = { ...chars_de_CH, "è": { key: "BracketLeft" }, diff --git a/ui/src/keyboardLayouts/fr_FR.ts b/ui/src/keyboardLayouts/fr_FR.ts index 755deb1..a489807 100644 --- a/ui/src/keyboardLayouts/fr_FR.ts +++ b/ui/src/keyboardLayouts/fr_FR.ts @@ -1,5 +1,7 @@ import { KeyCombo } from "../keyboardLayouts" +export const name = "French"; + 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 diff --git a/ui/src/keyboardLayouts/it_IT.ts b/ui/src/keyboardLayouts/it_IT.ts index fa87e11..012734c 100644 --- a/ui/src/keyboardLayouts/it_IT.ts +++ b/ui/src/keyboardLayouts/it_IT.ts @@ -1,5 +1,7 @@ import { KeyCombo } from "../keyboardLayouts" +export const name = "Italian"; + export const chars = { A: { key: "KeyA", shift: true }, B: { key: "KeyB", shift: true }, diff --git a/ui/src/keyboardLayouts/nb_NO.ts b/ui/src/keyboardLayouts/nb_NO.ts index de18561..1507ed3 100644 --- a/ui/src/keyboardLayouts/nb_NO.ts +++ b/ui/src/keyboardLayouts/nb_NO.ts @@ -1,5 +1,7 @@ import { KeyCombo } from "../keyboardLayouts" +export const name = "Norwegian"; + 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 diff --git a/ui/src/keyboardLayouts/sv_SE.ts b/ui/src/keyboardLayouts/sv_SE.ts index 79e9771..d2af302 100644 --- a/ui/src/keyboardLayouts/sv_SE.ts +++ b/ui/src/keyboardLayouts/sv_SE.ts @@ -1,5 +1,7 @@ import { KeyCombo } from "../keyboardLayouts" +export const name = "Swedish"; + 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 From 9b3d1e041711db23981a1f1a2455f0eca2ea47fd Mon Sep 17 00:00:00 2001 From: Marc Brooks Date: Thu, 8 May 2025 16:52:55 -0500 Subject: [PATCH 22/29] Change the locale names to their native language German->Deutsch et. al. --- ui/src/keyboardLayouts/cs_CZ.ts | 2 +- ui/src/keyboardLayouts/de_CH.ts | 2 +- ui/src/keyboardLayouts/de_DE.ts | 2 +- ui/src/keyboardLayouts/es_ES.ts | 2 +- ui/src/keyboardLayouts/fr_CH.ts | 2 +- ui/src/keyboardLayouts/fr_FR.ts | 2 +- ui/src/keyboardLayouts/it_IT.ts | 2 +- ui/src/keyboardLayouts/nb_NO.ts | 2 +- ui/src/keyboardLayouts/sv_SE.ts | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ui/src/keyboardLayouts/cs_CZ.ts b/ui/src/keyboardLayouts/cs_CZ.ts index ee2799c..a289d75 100644 --- a/ui/src/keyboardLayouts/cs_CZ.ts +++ b/ui/src/keyboardLayouts/cs_CZ.ts @@ -1,6 +1,6 @@ import { KeyCombo } from "../keyboardLayouts" -export const name = "Czech"; +export const name = "Čeština"; 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 diff --git a/ui/src/keyboardLayouts/de_CH.ts b/ui/src/keyboardLayouts/de_CH.ts index c3f1f8c..624f19a 100644 --- a/ui/src/keyboardLayouts/de_CH.ts +++ b/ui/src/keyboardLayouts/de_CH.ts @@ -1,6 +1,6 @@ import { KeyCombo } from "../keyboardLayouts" -export const name = "Swiss German"; +export const name = "Schwiizerdütsch"; 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 diff --git a/ui/src/keyboardLayouts/de_DE.ts b/ui/src/keyboardLayouts/de_DE.ts index 2833909..87a8d2e 100644 --- a/ui/src/keyboardLayouts/de_DE.ts +++ b/ui/src/keyboardLayouts/de_DE.ts @@ -1,6 +1,6 @@ import { KeyCombo } from "../keyboardLayouts" -export const name = "German"; +export const name = "Deutsch"; 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 diff --git a/ui/src/keyboardLayouts/es_ES.ts b/ui/src/keyboardLayouts/es_ES.ts index f979205..47fc230 100644 --- a/ui/src/keyboardLayouts/es_ES.ts +++ b/ui/src/keyboardLayouts/es_ES.ts @@ -1,6 +1,6 @@ import { KeyCombo } from "../keyboardLayouts" -export const name = "Spanish"; +export const name = "Español"; 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 diff --git a/ui/src/keyboardLayouts/fr_CH.ts b/ui/src/keyboardLayouts/fr_CH.ts index e47af1d..9fcc013 100644 --- a/ui/src/keyboardLayouts/fr_CH.ts +++ b/ui/src/keyboardLayouts/fr_CH.ts @@ -1,7 +1,7 @@ import { KeyCombo } from "../keyboardLayouts" import { chars as chars_de_CH } from "./de_CH" -export const name = "Swiss French"; +export const name = "Français de Suisse"; export const chars = { ...chars_de_CH, diff --git a/ui/src/keyboardLayouts/fr_FR.ts b/ui/src/keyboardLayouts/fr_FR.ts index a489807..27a03fd 100644 --- a/ui/src/keyboardLayouts/fr_FR.ts +++ b/ui/src/keyboardLayouts/fr_FR.ts @@ -1,6 +1,6 @@ import { KeyCombo } from "../keyboardLayouts" -export const name = "French"; +export const name = "Français"; 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 diff --git a/ui/src/keyboardLayouts/it_IT.ts b/ui/src/keyboardLayouts/it_IT.ts index 012734c..9de61c5 100644 --- a/ui/src/keyboardLayouts/it_IT.ts +++ b/ui/src/keyboardLayouts/it_IT.ts @@ -1,6 +1,6 @@ import { KeyCombo } from "../keyboardLayouts" -export const name = "Italian"; +export const name = "Italiano"; export const chars = { A: { key: "KeyA", shift: true }, diff --git a/ui/src/keyboardLayouts/nb_NO.ts b/ui/src/keyboardLayouts/nb_NO.ts index 1507ed3..83918b2 100644 --- a/ui/src/keyboardLayouts/nb_NO.ts +++ b/ui/src/keyboardLayouts/nb_NO.ts @@ -1,6 +1,6 @@ import { KeyCombo } from "../keyboardLayouts" -export const name = "Norwegian"; +export const name = "Norsk bokmål"; 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 diff --git a/ui/src/keyboardLayouts/sv_SE.ts b/ui/src/keyboardLayouts/sv_SE.ts index d2af302..f13679a 100644 --- a/ui/src/keyboardLayouts/sv_SE.ts +++ b/ui/src/keyboardLayouts/sv_SE.ts @@ -1,6 +1,6 @@ import { KeyCombo } from "../keyboardLayouts" -export const name = "Swedish"; +export const name = "Svenska"; 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 From 146cee9309ca7a8b7ab103e955f3fcc38a4bc692 Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Tue, 13 May 2025 18:55:47 +0200 Subject: [PATCH 23/29] Move hold key handling into Go backend analogous to https://www.kernel.org/doc/Documentation/usb/gadget_hid.txt --- internal/usbgadget/hid_keyboard.go | 9 ++++++++- jsonrpc.go | 2 +- ui/src/components/popovers/PasteModal.tsx | 11 ++++------- ui/src/hooks/useKeyboard.ts | 6 +++++- usb.go | 4 ++-- 5 files changed, 20 insertions(+), 12 deletions(-) diff --git a/internal/usbgadget/hid_keyboard.go b/internal/usbgadget/hid_keyboard.go index de007e4..dbebb2d 100644 --- a/internal/usbgadget/hid_keyboard.go +++ b/internal/usbgadget/hid_keyboard.go @@ -74,7 +74,7 @@ func (u *UsbGadget) keyboardWriteHidFile(data []byte) error { return nil } -func (u *UsbGadget) KeyboardReport(modifier uint8, keys []uint8) error { +func (u *UsbGadget) KeyboardReport(modifier uint8, keys []uint8, hold bool) error { u.keyboardLock.Lock() defer u.keyboardLock.Unlock() @@ -90,6 +90,13 @@ func (u *UsbGadget) KeyboardReport(modifier uint8, keys []uint8) error { return err } + if !hold { + err := u.keyboardWriteHidFile(make([]uint8, 8)) + if err != nil { + return err + } + } + u.resetUserInputTime() return nil } diff --git a/jsonrpc.go b/jsonrpc.go index cb06fa3..3154bd7 100644 --- a/jsonrpc.go +++ b/jsonrpc.go @@ -980,7 +980,7 @@ var rpcHandlers = map[string]RPCHandler{ "getNetworkSettings": {Func: rpcGetNetworkSettings}, "setNetworkSettings": {Func: rpcSetNetworkSettings, Params: []string{"settings"}}, "renewDHCPLease": {Func: rpcRenewDHCPLease}, - "keyboardReport": {Func: rpcKeyboardReport, Params: []string{"modifier", "keys"}}, + "keyboardReport": {Func: rpcKeyboardReport, Params: []string{"modifier", "keys", "hold"}}, "absMouseReport": {Func: rpcAbsMouseReport, Params: []string{"x", "y", "buttons"}}, "relMouseReport": {Func: rpcRelMouseReport, Params: []string{"dx", "dy", "buttons"}}, "wheelReport": {Func: rpcWheelReport, Params: []string{"wheelY"}}, diff --git a/ui/src/components/popovers/PasteModal.tsx b/ui/src/components/popovers/PasteModal.tsx index 2f0662d..b8d313e 100644 --- a/ui/src/components/popovers/PasteModal.tsx +++ b/ui/src/components/popovers/PasteModal.tsx @@ -13,8 +13,8 @@ import { keys, modifiers } from "@/keyboardMappings"; import { layouts, chars } from "@/keyboardLayouts"; import notifications from "@/notifications"; -const hidKeyboardPayload = (keys: number[], modifier: number) => { - return { keys, modifier }; +const hidKeyboardPayload = (keys: number[], modifier: number, hold: boolean) => { + return { keys, modifier, hold }; }; const modifierCode = (shift?: boolean, altRight?: boolean) => { @@ -82,13 +82,10 @@ export default function PasteModal() { await new Promise((resolve, reject) => { send( "keyboardReport", - hidKeyboardPayload([kei], modz[index]), + hidKeyboardPayload([kei], modz[index], false), params => { if ("error" in params) return reject(params.error); - send("keyboardReport", hidKeyboardPayload([], 0), params => { - if ("error" in params) return reject(params.error); - resolve(); - }); + resolve(); }, ); }); diff --git a/ui/src/hooks/useKeyboard.ts b/ui/src/hooks/useKeyboard.ts index 0ce1eef..67fa1c1 100644 --- a/ui/src/hooks/useKeyboard.ts +++ b/ui/src/hooks/useKeyboard.ts @@ -4,6 +4,10 @@ import { useHidStore, useRTCStore } from "@/hooks/stores"; import { useJsonRpc } from "@/hooks/useJsonRpc"; import { keys, modifiers } from "@/keyboardMappings"; +const hidKeyboardPayload = (keys: number[], modifier: number, hold: boolean) => { + return { keys, modifier, hold }; +}; + export default function useKeyboard() { const [send] = useJsonRpc(); @@ -17,7 +21,7 @@ export default function useKeyboard() { if (rpcDataChannel?.readyState !== "open") return; const accModifier = modifiers.reduce((acc, val) => acc + val, 0); - send("keyboardReport", { keys, modifier: accModifier }); + send("keyboardReport", hidKeyboardPayload(keys, accModifier, true)); // We do this for the info bar to display the currently pressed keys for the user updateActiveKeysAndModifiers({ keys: keys, modifiers: modifiers }); diff --git a/usb.go b/usb.go index 91674c9..1ecd8d3 100644 --- a/usb.go +++ b/usb.go @@ -26,8 +26,8 @@ func initUsbGadget() { }() } -func rpcKeyboardReport(modifier uint8, keys []uint8) error { - return gadget.KeyboardReport(modifier, keys) +func rpcKeyboardReport(modifier uint8, keys []uint8, hold bool) error { + return gadget.KeyboardReport(modifier, keys, hold) } func rpcAbsMouseReport(x, y int, buttons uint8) error { From a4d6da70858d46588ef468f09bc97abc56242ecb Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Mon, 19 May 2025 19:17:14 +0200 Subject: [PATCH 24/29] Remove trailing whitespace --- ui/src/keyboardLayouts/de_CH.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/keyboardLayouts/de_CH.ts b/ui/src/keyboardLayouts/de_CH.ts index 624f19a..06c0619 100644 --- a/ui/src/keyboardLayouts/de_CH.ts +++ b/ui/src/keyboardLayouts/de_CH.ts @@ -107,7 +107,7 @@ export const chars = { y: { key: "KeyZ" }, z: { key: "KeyY" }, "§": { key: "Backquote" }, - "°": { key: "Backquote", shift: true }, + "°": { key: "Backquote", shift: true }, 1: { key: "Digit1" }, "+": { key: "Digit1", shift: true }, "|": { key: "Digit1", altRight: true }, From d0759150eeeb524b5d82c8325aa6804c4f72698f Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Tue, 20 May 2025 00:12:12 +0200 Subject: [PATCH 25/29] Fix --- ui/src/hooks/stores.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/ui/src/hooks/stores.ts b/ui/src/hooks/stores.ts index e9a9a88..fdee538 100644 --- a/ui/src/hooks/stores.ts +++ b/ui/src/hooks/stores.ts @@ -881,5 +881,4 @@ export const useMacrosStore = create((set, get) => ({ set({ loading: false }); } }, - } })); From 96985645506ab94d6d996cea5272515bf9ab955b Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Tue, 20 May 2025 00:12:53 +0200 Subject: [PATCH 26/29] Add Belgisch Nederlands --- ui/src/keyboardLayouts.ts | 3 + ui/src/keyboardLayouts/fr_BE.ts | 167 ++++++++++++++++++++++++++++++++ 2 files changed, 170 insertions(+) create mode 100644 ui/src/keyboardLayouts/fr_BE.ts diff --git a/ui/src/keyboardLayouts.ts b/ui/src/keyboardLayouts.ts index 8dc0dec..7c93eed 100644 --- a/ui/src/keyboardLayouts.ts +++ b/ui/src/keyboardLayouts.ts @@ -1,3 +1,4 @@ +import { chars as chars_fr_BE, name as name_fr_BE } from "@/keyboardLayouts/fr_BE" import { chars as chars_cs_CZ, name as name_cs_CZ } from "@/keyboardLayouts/cs_CZ" import { chars as chars_en_UK, name as name_en_UK } from "@/keyboardLayouts/en_UK" import { chars as chars_en_US, name as name_en_US } from "@/keyboardLayouts/en_US" @@ -14,6 +15,7 @@ type KeyInfo = { key: string | number; shift?: boolean, altRight?: boolean } export type KeyCombo = KeyInfo & { deadKey?: boolean, accentKey?: KeyInfo } export const layouts: Record = { + be_FR: name_fr_BE, cs_CZ: name_cs_CZ, en_UK: name_en_UK, en_US: name_en_US, @@ -28,6 +30,7 @@ export const layouts: Record = { } export const chars: Record> = { + be_FR: chars_fr_BE, cs_CZ: chars_cs_CZ, en_UK: chars_en_UK, en_US: chars_en_US, diff --git a/ui/src/keyboardLayouts/fr_BE.ts b/ui/src/keyboardLayouts/fr_BE.ts new file mode 100644 index 0000000..2b8b34c --- /dev/null +++ b/ui/src/keyboardLayouts/fr_BE.ts @@ -0,0 +1,167 @@ +import { KeyCombo } from "../keyboardLayouts" + +export const name = "Belgisch Nederlands"; + +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 + +export const chars = { + A: { key: "KeyQ", shift: true }, + "Ä": { key: "KeyQ", shift: true, accentKey: keyTrema }, + "Â": { key: "KeyQ", shift: true, accentKey: keyHat }, + "Á": { key: "KeyQ", shift: true, accentKey: keyAcute }, + "À": { key: "KeyQ", shift: true, accentKey: keyGrave }, + "Ã": { key: "KeyQ", shift: true, accentKey: keyTilde }, + B: { key: "KeyB", shift: true }, + C: { key: "KeyC", shift: true }, + D: { key: "KeyD", shift: true }, + E: { key: "KeyE", shift: true }, + "Ë": { key: "KeyE", shift: true, accentKey: keyTrema }, + "Ê": { key: "KeyE", shift: true, accentKey: keyHat }, + "É": { key: "KeyE", shift: true, accentKey: keyAcute }, + "È": { key: "KeyE", shift: true, accentKey: keyGrave }, + "Ẽ": { key: "KeyE", shift: true, accentKey: keyTilde }, + 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: keyTrema }, + "Î": { key: "KeyI", shift: true, accentKey: keyHat }, + "Í": { key: "KeyI", shift: true, accentKey: keyAcute }, + "Ì": { key: "KeyI", shift: true, accentKey: keyGrave }, + "Ĩ": { key: "KeyI", shift: true, accentKey: keyTilde }, + J: { key: "KeyJ", shift: true }, + K: { key: "KeyK", shift: true }, + L: { key: "KeyL", shift: true }, + M: { key: "Semicolon", shift: true }, + N: { key: "KeyN", shift: true }, + O: { key: "KeyO", shift: true }, + "Ö": { key: "KeyO", shift: true, accentKey: keyTrema }, + "Ô": { key: "KeyO", shift: true, accentKey: keyHat }, + "Ó": { key: "KeyO", shift: true, accentKey: keyAcute }, + "Ò": { key: "KeyO", shift: true, accentKey: keyGrave }, + "Õ": { key: "KeyO", shift: true, accentKey: keyTilde }, + P: { key: "KeyP", shift: true }, + Q: { key: "KeyA", 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: keyTrema }, + "Û": { key: "KeyU", shift: true, accentKey: keyHat }, + "Ú": { key: "KeyU", shift: true, accentKey: keyAcute }, + "Ù": { key: "KeyU", shift: true, accentKey: keyGrave }, + "Ũ": { key: "KeyU", shift: true, accentKey: keyTilde }, + 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: "KeyQ" }, + "ä": { key: "KeyQ", accentKey: keyTrema }, + "â": { key: "KeyQ", accentKey: keyHat }, + "á": { key: "KeyQ", accentKey: keyAcute }, + "ã": { key: "KeyQ", accentKey: keyTilde }, + b: { key: "KeyB" }, + c: { key: "KeyC" }, + d: { key: "KeyD" }, + e: { key: "KeyE" }, + "ë": { key: "KeyE", accentKey: keyTrema }, + "ê": { key: "KeyE", accentKey: keyHat }, + "ẽ": { key: "KeyE", accentKey: keyTilde }, + "€": { key: "KeyE", altRight: true }, + f: { key: "KeyF" }, + g: { key: "KeyG" }, + h: { key: "KeyH" }, + i: { key: "KeyI" }, + "ï": { key: "KeyI", accentKey: keyTrema }, + "î": { key: "KeyI", accentKey: keyHat }, + "í": { key: "KeyI", accentKey: keyAcute }, + "ì": { key: "KeyI", accentKey: keyGrave }, + "ĩ": { key: "KeyI", accentKey: keyTilde }, + j: { key: "KeyJ" }, + k: { key: "KeyK" }, + l: { key: "KeyL" }, + m: { key: "Semicolon" }, + n: { key: "KeyN" }, + o: { key: "KeyO" }, + "ö": { key: "KeyO", accentKey: keyTrema }, + "ó": { key: "KeyO", accentKey: keyAcute }, + "ô": { key: "KeyO", accentKey: keyHat }, + "ò": { key: "KeyO", accentKey: keyGrave }, + "õ": { key: "KeyO", accentKey: keyTilde }, + p: { key: "KeyP" }, + q: { key: "KeyA" }, + r: { key: "KeyR" }, + s: { key: "KeyS" }, + t: { key: "KeyT" }, + u: { key: "KeyU" }, + "ü": { key: "KeyU", accentKey: keyTrema }, + "û": { key: "KeyU", accentKey: keyHat }, + "ú": { key: "KeyU", accentKey: keyAcute }, + "ũ": { key: "KeyU", accentKey: keyTilde }, + v: { key: "KeyV" }, + w: { key: "KeyW" }, + x: { key: "KeyX" }, + y: { key: "KeyZ" }, + z: { key: "KeyY" }, + "²": { key: "Backquote" }, + "³": { key: "Backquote", shift: true }, + "&": { key: "Digit1" }, + 1: { key: "Digit1", shift: true }, + "|": { key: "Digit1", altRight: true }, + "é": { key: "Digit2" }, + 2: { key: "Digit2", shift: true }, + "@": { key: "Digit2", altRight: true }, + "\"": { key: "Digit3" }, + 3: { key: "Digit3", shift: true }, + "#": { key: "Digit3", altRight: true }, + "'": { key: "Digit4" }, + 4: { key: "Digit4", shift: true }, + "(": { key: "Digit5" }, + 5: { key: "Digit5", shift: true }, + "§": { key: "Digit6" }, + 6: { key: "Digit6", shift: true }, + "^": { key: "Digit6", altRight: true }, + "è": { key: "Digit7" }, + 7: { key: "Digit7", shift: true }, + "!": { key: "Digit8" }, + 8: { key: "Digit8", shift: true }, + "ç": { key: "Digit9" }, + 9: { key: "Digit9", shift: true }, + "{": { key: "Digit9", altRight: true }, + "à": { key: "Digit0" }, + 0: { key: "Digit0", shift: true }, + "}": { key: "Digit0", altRight: true }, + ")": { key: "Minus" }, + "°": { key: "Minus", shift: true }, + "-": { key: "Equal", deadKey: true }, + "_": { key: "Equal", shift: true }, + "[": { key: "BracketLeft", altRight: true }, + "$": { key: "BracketRight" }, + "*": { key: "BracketRight", altRight: true }, + "]": { key: "BracketRight", altRight: true }, + "ù": { key: "Quote" }, + "%": { key: "Quote", shift: true }, + "µ": { key: "Backslash" }, + "£": { key: "Backslash", shift: true }, + ",": { key: "KeyM" }, + "?": { key: "KeyM", shift: true }, + ";": { key: "Comma" }, + ".": { key: "Comma", shift: true }, + ":": { key: "Period" }, + "/": { key: "Period", shift: true }, + "=": { key: "Slash" }, + "+": { key: "Slash", shift: true }, + "~": { key: "Slash", deadKey: true }, + "<": { key: "IntlBackslash" }, + ">": { key: "IntlBackslash", shift: true }, + "\\": { key: "IntlBackslash", altRight: true }, + " ": { key: "Space" }, + "\n": { key: "Enter" }, + Enter: { key: "Enter" }, + Tab: { key: "Tab" }, +} as Record; From 6dd65fbba67de21429c15f45f9a56f83d46bd584 Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Tue, 20 May 2025 01:02:38 +0200 Subject: [PATCH 27/29] Add JSONRPC handling --- jsonrpc.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/jsonrpc.go b/jsonrpc.go index 3154bd7..46d88c1 100644 --- a/jsonrpc.go +++ b/jsonrpc.go @@ -877,6 +877,18 @@ func rpcSetCloudUrl(apiUrl string, appUrl string) error { return nil } +func rpcGetKeyboardLayout() (string, error) { + return config.KeyboardLayout, nil +} + +func rpcSetKeyboardLayout(layout string) error { + config.KeyboardLayout = layout + if err := SaveConfig(); err != nil { + return fmt.Errorf("failed to save config: %w", err) + } + return nil +} + func getKeyboardMacros() (interface{}, error) { macros := make([]KeyboardMacro, len(config.KeyboardMacros)) copy(macros, config.KeyboardMacros) From 7240abaf3dc83fcca6f2b9d513a0488aee79415b Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Tue, 20 May 2025 01:03:21 +0200 Subject: [PATCH 28/29] Use useSettingsStore --- ui/src/components/popovers/PasteModal.tsx | 6 +++--- ui/src/hooks/stores.ts | 8 ++++++-- ui/src/routes/devices.$id.settings.keyboard.tsx | 6 +++--- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/ui/src/components/popovers/PasteModal.tsx b/ui/src/components/popovers/PasteModal.tsx index b8d313e..61cd814 100644 --- a/ui/src/components/popovers/PasteModal.tsx +++ b/ui/src/components/popovers/PasteModal.tsx @@ -8,7 +8,7 @@ import { GridCard } from "@components/Card"; import { TextAreaWithLabel } from "@components/TextArea"; import { SettingsPageHeader } from "@components/SettingsPageheader"; import { useJsonRpc } from "@/hooks/useJsonRpc"; -import { useHidStore, useRTCStore, useUiStore, useDeviceSettingsStore } from "@/hooks/stores"; +import { useHidStore, useRTCStore, useUiStore, useSettingsStore } from "@/hooks/stores"; import { keys, modifiers } from "@/keyboardMappings"; import { layouts, chars } from "@/keyboardLayouts"; import notifications from "@/notifications"; @@ -34,8 +34,8 @@ export default function PasteModal() { const [invalidChars, setInvalidChars] = useState([]); const close = useClose(); - const keyboardLayout = useDeviceSettingsStore(state => state.keyboardLayout); - const setKeyboardLayout = useDeviceSettingsStore( + const keyboardLayout = useSettingsStore(state => state.keyboardLayout); + const setKeyboardLayout = useSettingsStore( state => state.setKeyboardLayout, ); diff --git a/ui/src/hooks/stores.ts b/ui/src/hooks/stores.ts index fdee538..01d1257 100644 --- a/ui/src/hooks/stores.ts +++ b/ui/src/hooks/stores.ts @@ -302,6 +302,9 @@ interface SettingsState { backlightSettings: BacklightSettings; setBacklightSettings: (settings: BacklightSettings) => void; + + keyboardLayout: string; + setKeyboardLayout: (layout: string) => void; } export const useSettingsStore = create( @@ -330,6 +333,9 @@ export const useSettingsStore = create( }, setBacklightSettings: (settings: BacklightSettings) => set({ backlightSettings: settings }), + + keyboardLayout: "en-US", + setKeyboardLayout: layout => set({ keyboardLayout: layout }), }), { name: "settings", @@ -347,8 +353,6 @@ export interface DeviceSettingsState { trackpadThreshold: number; scrollSensitivity: "low" | "default" | "high"; setScrollSensitivity: (sensitivity: DeviceSettingsState["scrollSensitivity"]) => void; - keyboardLayout: string; - setKeyboardLayout: (layout: string) => void; } export interface RemoteVirtualMediaState { diff --git a/ui/src/routes/devices.$id.settings.keyboard.tsx b/ui/src/routes/devices.$id.settings.keyboard.tsx index b4981af..a299049 100644 --- a/ui/src/routes/devices.$id.settings.keyboard.tsx +++ b/ui/src/routes/devices.$id.settings.keyboard.tsx @@ -1,6 +1,6 @@ import { useCallback, useEffect } from "react"; -import { useDeviceSettingsStore } from "@/hooks/stores"; +import { useSettingsStore } from "@/hooks/stores"; import { useJsonRpc } from "@/hooks/useJsonRpc"; import notifications from "@/notifications"; import { SettingsPageHeader } from "@components/SettingsPageheader"; @@ -12,8 +12,8 @@ import { SelectMenuBasic } from "../components/SelectMenuBasic"; import { SettingsItem } from "./devices.$id.settings"; export default function SettingsKeyboardRoute() { - const keyboardLayout = useDeviceSettingsStore(state => state.keyboardLayout); - const setKeyboardLayout = useDeviceSettingsStore( + const keyboardLayout = useSettingsStore(state => state.keyboardLayout); + const setKeyboardLayout = useSettingsStore( state => state.setKeyboardLayout, ); From a4c15d5c7e9a3c68a6fd75d7881bab9e506781c8 Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Tue, 20 May 2025 01:09:15 +0200 Subject: [PATCH 29/29] Revert "Move hold key handling into Go backend analogous to https://www.kernel.org/doc/Documentation/usb/gadget_hid.txt" This reverts commit 146cee9309ca7a8b7ab103e955f3fcc38a4bc692. --- internal/usbgadget/hid_keyboard.go | 9 +-------- jsonrpc.go | 2 +- ui/src/components/popovers/PasteModal.tsx | 11 +++++++---- ui/src/hooks/useKeyboard.ts | 6 +----- usb.go | 4 ++-- 5 files changed, 12 insertions(+), 20 deletions(-) diff --git a/internal/usbgadget/hid_keyboard.go b/internal/usbgadget/hid_keyboard.go index dbebb2d..de007e4 100644 --- a/internal/usbgadget/hid_keyboard.go +++ b/internal/usbgadget/hid_keyboard.go @@ -74,7 +74,7 @@ func (u *UsbGadget) keyboardWriteHidFile(data []byte) error { return nil } -func (u *UsbGadget) KeyboardReport(modifier uint8, keys []uint8, hold bool) error { +func (u *UsbGadget) KeyboardReport(modifier uint8, keys []uint8) error { u.keyboardLock.Lock() defer u.keyboardLock.Unlock() @@ -90,13 +90,6 @@ func (u *UsbGadget) KeyboardReport(modifier uint8, keys []uint8, hold bool) erro return err } - if !hold { - err := u.keyboardWriteHidFile(make([]uint8, 8)) - if err != nil { - return err - } - } - u.resetUserInputTime() return nil } diff --git a/jsonrpc.go b/jsonrpc.go index 46d88c1..5dd96da 100644 --- a/jsonrpc.go +++ b/jsonrpc.go @@ -992,7 +992,7 @@ var rpcHandlers = map[string]RPCHandler{ "getNetworkSettings": {Func: rpcGetNetworkSettings}, "setNetworkSettings": {Func: rpcSetNetworkSettings, Params: []string{"settings"}}, "renewDHCPLease": {Func: rpcRenewDHCPLease}, - "keyboardReport": {Func: rpcKeyboardReport, Params: []string{"modifier", "keys", "hold"}}, + "keyboardReport": {Func: rpcKeyboardReport, Params: []string{"modifier", "keys"}}, "absMouseReport": {Func: rpcAbsMouseReport, Params: []string{"x", "y", "buttons"}}, "relMouseReport": {Func: rpcRelMouseReport, Params: []string{"dx", "dy", "buttons"}}, "wheelReport": {Func: rpcWheelReport, Params: []string{"wheelY"}}, diff --git a/ui/src/components/popovers/PasteModal.tsx b/ui/src/components/popovers/PasteModal.tsx index 61cd814..5040572 100644 --- a/ui/src/components/popovers/PasteModal.tsx +++ b/ui/src/components/popovers/PasteModal.tsx @@ -13,8 +13,8 @@ import { keys, modifiers } from "@/keyboardMappings"; import { layouts, chars } from "@/keyboardLayouts"; import notifications from "@/notifications"; -const hidKeyboardPayload = (keys: number[], modifier: number, hold: boolean) => { - return { keys, modifier, hold }; +const hidKeyboardPayload = (keys: number[], modifier: number) => { + return { keys, modifier }; }; const modifierCode = (shift?: boolean, altRight?: boolean) => { @@ -82,10 +82,13 @@ export default function PasteModal() { await new Promise((resolve, reject) => { send( "keyboardReport", - hidKeyboardPayload([kei], modz[index], false), + hidKeyboardPayload([kei], modz[index]), params => { if ("error" in params) return reject(params.error); - resolve(); + send("keyboardReport", hidKeyboardPayload([], 0), params => { + if ("error" in params) return reject(params.error); + resolve(); + }); }, ); }); diff --git a/ui/src/hooks/useKeyboard.ts b/ui/src/hooks/useKeyboard.ts index 67fa1c1..0ce1eef 100644 --- a/ui/src/hooks/useKeyboard.ts +++ b/ui/src/hooks/useKeyboard.ts @@ -4,10 +4,6 @@ import { useHidStore, useRTCStore } from "@/hooks/stores"; import { useJsonRpc } from "@/hooks/useJsonRpc"; import { keys, modifiers } from "@/keyboardMappings"; -const hidKeyboardPayload = (keys: number[], modifier: number, hold: boolean) => { - return { keys, modifier, hold }; -}; - export default function useKeyboard() { const [send] = useJsonRpc(); @@ -21,7 +17,7 @@ export default function useKeyboard() { if (rpcDataChannel?.readyState !== "open") return; const accModifier = modifiers.reduce((acc, val) => acc + val, 0); - send("keyboardReport", hidKeyboardPayload(keys, accModifier, true)); + send("keyboardReport", { keys, modifier: accModifier }); // We do this for the info bar to display the currently pressed keys for the user updateActiveKeysAndModifiers({ keys: keys, modifiers: modifiers }); diff --git a/usb.go b/usb.go index 1ecd8d3..91674c9 100644 --- a/usb.go +++ b/usb.go @@ -26,8 +26,8 @@ func initUsbGadget() { }() } -func rpcKeyboardReport(modifier uint8, keys []uint8, hold bool) error { - return gadget.KeyboardReport(modifier, keys, hold) +func rpcKeyboardReport(modifier uint8, keys []uint8) error { + return gadget.KeyboardReport(modifier, keys) } func rpcAbsMouseReport(x, y int, buttons uint8) error {