From 22849fceab304d314e6e849ee5120b5ea2d39101 Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Fri, 2 May 2025 00:52:03 +0200 Subject: [PATCH 01/34] 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 196a73dac..e699ff316 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 db9b680ae..cb06fa356 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 6f234a87a..25dfff0af 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 147f2851d..e9a9a887a 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 000000000..15c3be4ab --- /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 000000000..405feb851 --- /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 000000000..65da3191f --- /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 d191f5096..891b96e77 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 cbd5e2513..e3badd198 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 000000000..b4981aff9 --- /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 8d88a2be7..0c710ce2a 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 3d28dd5af..05c3c9dee 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/34] 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 25dfff0af..9e2258c8d 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 15c3be4ab..a995e3d4d 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 405feb851..893ebe4a0 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/34] 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 9e2258c8d..c6d2f1c25 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/34] 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 c6d2f1c25..6ad60db85 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); - } - if (capsLock) { - keyz.unshift(keys["CapsLock"]); - modz.unshift(0); - - keyz.push(keys["CapsLock"]); - modz.push(0); + modz.push(noModifier); } - 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 a995e3d4d..170a71fc2 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 893ebe4a0..519b2addc 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 65da3191f..b1e9dea56 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/34] 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 519b2addc..2b1019e9f 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/34] 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 170a71fc2..fc289550e 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 000000000..02f2b6954 --- /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/34] 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 fc289550e..69b3ea991 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 2b1019e9f..c822a7f18 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 000000000..2082f7a0f --- /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/34] 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 2082f7a0f..08a58d341 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/34] 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 69b3ea991..2c0eeda3b 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 000000000..9d66d1654 --- /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/34] 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 2c0eeda3b..de66f8d80 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 000000000..49d623ffb --- /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/34] 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 de66f8d80..98c2bbd0b 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 000000000..79e97712d --- /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/34] 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 98c2bbd0b..723e85566 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 000000000..9a53d9366 --- /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/34] 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 9d66d1654..755deb13d 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/34] 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 9a53d9366..9e34bea68 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/34] 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 b1e9dea56..02dc5eed1 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/34] 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 723e85566..ce77e9fbd 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 000000000..de1856189 --- /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/34] =?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 6ad60db85..38c392df6 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/34] 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 ce77e9fbd..000dfbe7f 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 000000000..fa87e11e9 --- /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/34] 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 000dfbe7f..35d5703cb 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 000000000..e889e3201 --- /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/34] 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 38c392df6..2f0662df4 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/34] 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 35d5703cb..8dc0decc7 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 e889e3201..ee2799c82 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 c822a7f18..c3f1f8c61 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 08a58d341..283390987 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 49d623ffb..ed0c8dd02 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 02dc5eed1..592bf27ef 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 9e34bea68..f979205af 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 02f2b6954..e47af1d1b 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 755deb13d..a4898073e 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 fa87e11e9..012734c43 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 de1856189..1507ed337 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 79e97712d..d2af3029e 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/34] 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 ee2799c82..a289d75f8 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 c3f1f8c61..624f19abc 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 283390987..87a8d2e53 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 f979205af..47fc23030 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 e47af1d1b..9fcc0139b 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 a4898073e..27a03fda8 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 012734c43..9de61c581 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 1507ed337..83918b23d 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 d2af3029e..f13679a62 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/34] 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 de007e458..dbebb2df1 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 cb06fa356..3154bd705 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 2f0662df4..b8d313e18 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 0ce1eefc6..67fa1c142 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 91674c99a..1ecd8d35f 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/34] 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 624f19abc..06c061920 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/34] 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 e9a9a887a..fdee53818 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/34] 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 8dc0decc7..7c93eed45 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 000000000..2b8b34c3d --- /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/34] Add JSONRPC handling --- jsonrpc.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/jsonrpc.go b/jsonrpc.go index 3154bd705..46d88c1e5 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/34] 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 b8d313e18..61cd814de 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 fdee53818..01d1257a0 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 b4981aff9..a299049d9 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/34] 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 dbebb2df1..de007e458 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 46d88c1e5..5dd96da85 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 61cd814de..504057219 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 67fa1c142..0ce1eefc6 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 1ecd8d35f..91674c99a 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 { From 1460fc5ac213f0e664bac645f22a0a4dad1ebd65 Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Wed, 21 May 2025 14:37:38 +0000 Subject: [PATCH 30/34] Move FeatureFlag to navigation --- .../routes/devices.$id.settings.keyboard.tsx | 37 +++++++++---------- ui/src/routes/devices.$id.settings.tsx | 25 +++++++------ 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/ui/src/routes/devices.$id.settings.keyboard.tsx b/ui/src/routes/devices.$id.settings.keyboard.tsx index a299049d9..9ec1f079c 100644 --- a/ui/src/routes/devices.$id.settings.keyboard.tsx +++ b/ui/src/routes/devices.$id.settings.keyboard.tsx @@ -6,7 +6,6 @@ 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"; @@ -52,25 +51,23 @@ export default function SettingsKeyboardRoute() { />
- - { /* 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. -

-
+ { /* 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.tsx b/ui/src/routes/devices.$id.settings.tsx index 05c3c9dee..929712cad 100644 --- a/ui/src/routes/devices.$id.settings.tsx +++ b/ui/src/routes/devices.$id.settings.tsx @@ -20,6 +20,7 @@ import { LinkButton } from "@/components/Button"; import LoadingSpinner from "@/components/LoadingSpinner"; import { useUiStore } from "@/hooks/stores"; import useKeyboard from "@/hooks/useKeyboard"; +import { FeatureFlag } from "../components/FeatureFlag"; import { cx } from "../cva.config"; @@ -156,17 +157,19 @@ export default function SettingsRoute() { -
- (isActive ? "active" : "")} - > -
- -

Keyboard

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

Keyboard

+
+
+
+
Date: Wed, 21 May 2025 14:38:12 +0000 Subject: [PATCH 31/34] Fix: flip Y/Z --- ui/src/keyboardLayouts/sv_SE.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/src/keyboardLayouts/sv_SE.ts b/ui/src/keyboardLayouts/sv_SE.ts index f13679a62..75197cb73 100644 --- a/ui/src/keyboardLayouts/sv_SE.ts +++ b/ui/src/keyboardLayouts/sv_SE.ts @@ -56,8 +56,8 @@ export const chars = { 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 }, + Y: { key: "KeyY", shift: true }, + Z: { key: "KeyZ", shift: true }, a: { key: "KeyA" }, "á": { key: "KeyA", accentKey: keyAcute }, "â": { key: "KeyA", accentKey: keyHat }, @@ -106,8 +106,8 @@ export const chars = { v: { key: "KeyV" }, w: { key: "KeyW" }, x: { key: "KeyX" }, - y: { key: "KeyZ" }, - z: { key: "KeyY" }, + y: { key: "KeyY" }, + z: { key: "KeyZ" }, "§": { key: "Backquote" }, "½": { key: "Backquote", shift: true }, 1: { key: "Digit1" }, From 4804929ce048716321137075e460f7be54797b34 Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Wed, 21 May 2025 14:49:27 +0000 Subject: [PATCH 32/34] Add useEffect dependencies --- 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 504057219..7f2c24521 100644 --- a/ui/src/components/popovers/PasteModal.tsx +++ b/ui/src/components/popovers/PasteModal.tsx @@ -44,7 +44,7 @@ export default function PasteModal() { if ("error" in resp) return; setKeyboardLayout(resp.result as string); }); - }, []); + }, [send, setKeyboardLayout]); const onCancelPasteMode = useCallback(() => { setPasteMode(false); From fc1b304250a95a94640ed732fc94ba710653df50 Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Wed, 21 May 2025 14:50:46 +0000 Subject: [PATCH 33/34] Embolden language --- 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 7f2c24521..7b921b20a 100644 --- a/ui/src/components/popovers/PasteModal.tsx +++ b/ui/src/components/popovers/PasteModal.tsx @@ -169,7 +169,7 @@ export default function PasteModal() {

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

From 6d18f78b277c6a19762e07c98bc2771c49d6cd18 Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Wed, 21 May 2025 14:52:44 +0000 Subject: [PATCH 34/34] Add to useCallback dependencies --- 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 7b921b20a..67c0b1165 100644 --- a/ui/src/components/popovers/PasteModal.tsx +++ b/ui/src/components/popovers/PasteModal.tsx @@ -98,7 +98,7 @@ export default function PasteModal() { console.error(error); notifications.error("Failed to paste text"); } - }, [rpcDataChannel?.readyState, send, setDisableVideoFocusTrap, setPasteMode]); + }, [rpcDataChannel?.readyState, send, setDisableVideoFocusTrap, setPasteMode, keyboardLayout]); useEffect(() => { if (TextAreaRef.current) {