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

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

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

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

+
+
+
+ ); +} diff --git a/ui/src/routes/devices.$id.settings.mouse.tsx b/ui/src/routes/devices.$id.settings.mouse.tsx index d6223d0..14cc890 100644 --- a/ui/src/routes/devices.$id.settings.mouse.tsx +++ b/ui/src/routes/devices.$id.settings.mouse.tsx @@ -18,7 +18,7 @@ import { SettingsItem } from "./devices.$id.settings"; type ScrollSensitivity = "low" | "default" | "high"; -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 641a64c..0c57e0a 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,22 @@ export default function SettingsRoute() { className={({ isActive }) => (isActive ? "active" : "")} >
- +

Mouse

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

Keyboard

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

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

diff --git a/ui/src/keyboardLayouts.ts b/ui/src/keyboardLayouts.ts index a995e3d..170a71f 100644 --- a/ui/src/keyboardLayouts.ts +++ b/ui/src/keyboardLayouts.ts @@ -1,6 +1,9 @@ import { chars as chars_en_US } from "@/keyboardLayouts/en_US" import { chars as chars_de_CH } from "@/keyboardLayouts/de_CH" +type KeyInfo = { key: string | number; shift?: boolean, altRight?: boolean } +export type KeyCombo = KeyInfo & { deadKey?: boolean, accentKey?: KeyInfo } + export const layouts = { "en_US": "English (US)", "de_CH": "Swiss German" @@ -9,4 +12,4 @@ export const layouts = { export const chars = { "en_US": chars_en_US, "de_CH": chars_de_CH, -} as Record>; +} as Record> diff --git a/ui/src/keyboardLayouts/de_CH.ts b/ui/src/keyboardLayouts/de_CH.ts index 893ebe4..519b2ad 100644 --- a/ui/src/keyboardLayouts/de_CH.ts +++ b/ui/src/keyboardLayouts/de_CH.ts @@ -1,40 +1,78 @@ +import { KeyCombo } from "../keyboardLayouts" + +const keyTrema = { key: "BracketRight" } // tréma (umlaut), two dots placed above a vowel +const keyAcute = { key: "Minus", altRight: true } // accent aigu (acute accent), mark ´ placed above the letter +const keyHat = { key: "Equal" } // accent circonflexe (accent hat), mark ^ placed above the letter +const keyGrave = { key: "Equal", shift: true } // accent grave, mark ` placed above the letter +const keyTilde = { key: "Equal", altRight: true } // tilde, mark ~ placed above the letter + export const chars = { A: { key: "KeyA", shift: true }, - "Ä": { key: "KeyA", shift: true, trema: true }, + "Ä": { key: "KeyA", shift: true, accentKey: keyTrema }, + "Á": { key: "KeyA", shift: true, accentKey: keyAcute }, + "Â": { key: "KeyA", shift: true, accentKey: keyHat }, + "À": { key: "KeyA", shift: true, accentKey: keyGrave }, + "Ã": { key: "KeyA", shift: true, accentKey: keyTilde }, + "Æ": { key: "KeyA", shift: true, altRight: true }, B: { key: "KeyB", shift: true }, C: { key: "KeyC", shift: true }, D: { key: "KeyD", shift: true }, E: { key: "KeyE", shift: true }, + "Ë": { key: "KeyE", shift: true, accentKey: keyTrema }, + "É": { key: "KeyE", shift: true, accentKey: keyAcute }, + "Ê": { key: "KeyE", shift: true, accentKey: keyHat }, + "È": { key: "KeyE", shift: true, accentKey: keyGrave }, + "Ẽ": { key: "KeyE", shift: true, accentKey: keyTilde }, F: { key: "KeyF", shift: true }, G: { key: "KeyG", shift: true }, H: { key: "KeyH", shift: true }, I: { key: "KeyI", shift: true }, + "Ï": { key: "KeyI", shift: true, accentKey: keyTrema }, + "Í": { key: "KeyI", shift: true, accentKey: keyAcute }, + "Î": { key: "KeyI", shift: true, accentKey: keyHat }, + "Ì": { key: "KeyI", shift: true, accentKey: keyGrave }, + "Ĩ": { key: "KeyI", shift: true, accentKey: keyTilde }, J: { key: "KeyJ", shift: true }, K: { key: "KeyK", shift: true }, L: { key: "KeyL", shift: true }, M: { key: "KeyM", shift: true }, N: { key: "KeyN", shift: true }, O: { key: "KeyO", shift: true }, - "Ö": { key: "KeyO", shift: true, trema: true }, + "Ö": { key: "KeyO", shift: true, accentKey: keyTrema }, + "Ó": { key: "KeyO", shift: true, accentKey: keyAcute }, + "Ô": { key: "KeyO", shift: true, accentKey: keyHat }, + "Ò": { key: "KeyO", shift: true, accentKey: keyGrave }, + "Õ": { key: "KeyO", shift: true, accentKey: keyTilde }, + "Œ": { key: "KeyO", shift: true, altRight: true }, P: { key: "KeyP", shift: true }, Q: { key: "KeyQ", shift: true }, R: { key: "KeyR", shift: true }, S: { key: "KeyS", shift: true }, T: { key: "KeyT", shift: true }, U: { key: "KeyU", shift: true }, - "Ü": { key: "KeyU", shift: true, trema: true }, + "Ü": { key: "KeyU", shift: true, accentKey: keyTrema }, + "Ú": { key: "KeyU", shift: true, accentKey: keyAcute }, + "Û": { key: "KeyU", shift: true, accentKey: keyHat }, + "Ù": { key: "KeyU", shift: true, accentKey: keyGrave }, + "Ũ": { key: "KeyU", shift: true, accentKey: keyTilde }, V: { key: "KeyV", shift: true }, W: { key: "KeyW", shift: true }, X: { key: "KeyX", shift: true }, Y: { key: "KeyZ", shift: true }, Z: { key: "KeyY", shift: true }, a: { key: "KeyA" }, + "á": { key: "KeyA", accentKey: keyAcute }, + "â": { key: "KeyA", accentKey: keyHat }, + "ã": { key: "KeyA", accentKey: keyTilde }, "æ": { key: "KeyA", altRight: true }, b: { key: "KeyB" }, c: { key: "KeyC" }, d: { key: "KeyD" }, "ð": { key: "KeyD", altRight: true }, e: { key: "KeyE" }, + "ë": { key: "KeyE", accentKey: keyTrema }, + "ê": { key: "KeyE", accentKey: keyHat }, + "ẽ": { key: "KeyE", accentKey: keyTilde }, f: { key: "KeyF" }, "đ": { key: "KeyF", altRight: true }, g: { key: "KeyG" }, @@ -42,6 +80,11 @@ export const chars = { h: { key: "KeyH" }, "ħ": { key: "KeyH", altRight: true }, i: { key: "KeyI" }, + "ï": { key: "KeyI", accentKey: keyTrema }, + "í": { key: "KeyI", accentKey: keyAcute }, + "î": { key: "KeyI", accentKey: keyHat }, + "ì": { key: "KeyI", accentKey: keyGrave }, + "ĩ": { key: "KeyI", accentKey: keyTilde }, "→": { key: "KeyI", altRight: true }, j: { key: "KeyJ" }, k: { key: "KeyK" }, @@ -52,6 +95,10 @@ export const chars = { "µ": { key: "KeyM", altRight: true }, n: { key: "KeyN" }, o: { key: "KeyO" }, + "ó": { key: "KeyO", accentKey: keyAcute }, + "ô": { key: "KeyO", accentKey: keyHat }, + "ò": { key: "KeyO", accentKey: keyGrave }, + "õ": { key: "KeyO", accentKey: keyTilde }, "œ": { key: "KeyO", altRight: true }, p: { key: "KeyP" }, "þ": { key: "KeyP", altRight: true }, @@ -63,6 +110,10 @@ export const chars = { t: { key: "KeyT" }, "ŧ": { key: "KeyT", altRight: true }, u: { key: "KeyU" }, + "ú": { key: "KeyU", accentKey: keyAcute }, + "û": { key: "KeyU", accentKey: keyHat }, + "ù": { key: "KeyU", accentKey: keyGrave }, + "ũ": { key: "KeyU", accentKey: keyTilde }, "↓": { key: "KeyU", altRight: true }, v: { key: "KeyV" }, "„": { key: "KeyV", altRight: true }, @@ -105,9 +156,9 @@ export const chars = { "=": { key: "Digit0", shift: true }, "'": { key: "Minus" }, "?": { key: "Minus", shift: true }, - "^": { key: "Equal", space: true }, // dead key + "^": { key: "Equal", deadKey: true }, "`": { key: "Equal", shift: true }, - "~": { key: "Equal", altRight: true, space: true }, // dead key + "~": { key: "Equal", altRight: true, deadKey: true }, "ü": { key: "BracketLeft" }, "è": { key: "BracketLeft", shift: true }, "[": { key: "BracketLeft", altRight: true }, @@ -137,4 +188,4 @@ export const chars = { "\n": { key: "Enter" }, Enter: { key: "Enter" }, Tab: { key: "Tab" }, -} as Record +} as Record; diff --git a/ui/src/keyboardLayouts/en_US.ts b/ui/src/keyboardLayouts/en_US.ts index 65da319..b1e9dea 100644 --- a/ui/src/keyboardLayouts/en_US.ts +++ b/ui/src/keyboardLayouts/en_US.ts @@ -1,3 +1,5 @@ +import { KeyCombo } from "../keyboardLayouts" + export const chars = { A: { key: "KeyA", shift: true }, B: { key: "KeyB", shift: true }, @@ -106,4 +108,4 @@ export const chars = { Break: { key: "Pause", shift: true }, Insert: { key: "Insert", shift: false }, Delete: { key: "Delete", shift: false }, -} as Record +} as Record From 768a9f76049ec7695961c685d6400223d45c4a2d Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Fri, 2 May 2025 21:00:41 +0200 Subject: [PATCH 06/24] Remove obscure Alt-Gr keys, unsure if they are supported everywhere --- ui/src/keyboardLayouts/de_CH.ts | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/ui/src/keyboardLayouts/de_CH.ts b/ui/src/keyboardLayouts/de_CH.ts index 519b2ad..2b1019e 100644 --- a/ui/src/keyboardLayouts/de_CH.ts +++ b/ui/src/keyboardLayouts/de_CH.ts @@ -13,7 +13,6 @@ export const chars = { "Â": { key: "KeyA", shift: true, accentKey: keyHat }, "À": { key: "KeyA", shift: true, accentKey: keyGrave }, "Ã": { key: "KeyA", shift: true, accentKey: keyTilde }, - "Æ": { key: "KeyA", shift: true, altRight: true }, B: { key: "KeyB", shift: true }, C: { key: "KeyC", shift: true }, D: { key: "KeyD", shift: true }, @@ -43,7 +42,6 @@ export const chars = { "Ô": { key: "KeyO", shift: true, accentKey: keyHat }, "Ò": { key: "KeyO", shift: true, accentKey: keyGrave }, "Õ": { key: "KeyO", shift: true, accentKey: keyTilde }, - "Œ": { key: "KeyO", shift: true, altRight: true }, P: { key: "KeyP", shift: true }, Q: { key: "KeyQ", shift: true }, R: { key: "KeyR", shift: true }, @@ -64,67 +62,47 @@ export const chars = { "á": { key: "KeyA", accentKey: keyAcute }, "â": { key: "KeyA", accentKey: keyHat }, "ã": { key: "KeyA", accentKey: keyTilde }, - "æ": { key: "KeyA", altRight: true }, b: { key: "KeyB" }, c: { key: "KeyC" }, d: { key: "KeyD" }, - "ð": { key: "KeyD", altRight: true }, e: { key: "KeyE" }, "ë": { key: "KeyE", accentKey: keyTrema }, "ê": { key: "KeyE", accentKey: keyHat }, "ẽ": { key: "KeyE", accentKey: keyTilde }, f: { key: "KeyF" }, - "đ": { key: "KeyF", altRight: true }, g: { key: "KeyG" }, - "ŋ": { key: "KeyG", altRight: true }, h: { key: "KeyH" }, - "ħ": { key: "KeyH", altRight: true }, i: { key: "KeyI" }, "ï": { key: "KeyI", accentKey: keyTrema }, "í": { key: "KeyI", accentKey: keyAcute }, "î": { key: "KeyI", accentKey: keyHat }, "ì": { key: "KeyI", accentKey: keyGrave }, "ĩ": { key: "KeyI", accentKey: keyTilde }, - "→": { key: "KeyI", altRight: true }, j: { key: "KeyJ" }, k: { key: "KeyK" }, - "ĸ": { key: "KeyK", altRight: true }, l: { key: "KeyL" }, - "ł": { key: "KeyL", altRight: true }, m: { key: "KeyM" }, - "µ": { key: "KeyM", altRight: true }, n: { key: "KeyN" }, o: { key: "KeyO" }, "ó": { key: "KeyO", accentKey: keyAcute }, "ô": { key: "KeyO", accentKey: keyHat }, "ò": { key: "KeyO", accentKey: keyGrave }, "õ": { key: "KeyO", accentKey: keyTilde }, - "œ": { key: "KeyO", altRight: true }, p: { key: "KeyP" }, - "þ": { key: "KeyP", altRight: true }, q: { key: "KeyQ" }, r: { key: "KeyR" }, - "¶": { key: "KeyR", altRight: true }, s: { key: "KeyS" }, - "ß": { key: "KeyS", altRight: true }, t: { key: "KeyT" }, - "ŧ": { key: "KeyT", altRight: true }, u: { key: "KeyU" }, "ú": { key: "KeyU", accentKey: keyAcute }, "û": { key: "KeyU", accentKey: keyHat }, "ù": { key: "KeyU", accentKey: keyGrave }, "ũ": { key: "KeyU", accentKey: keyTilde }, - "↓": { key: "KeyU", altRight: true }, v: { key: "KeyV" }, - "„": { key: "KeyV", altRight: true }, w: { key: "KeyW" }, - "ſ": { key: "KeyW", altRight: true }, x: { key: "KeyX" }, - "»": { key: "KeyX", altRight: true }, y: { key: "KeyZ" }, - "←": { key: "KeyZ", altRight: true }, z: { key: "KeyY" }, - "«": { key: "KeyY", altRight: true }, "§": { key: "Backquote" }, "°": { key: "Backquote", shift: true }, 1: { key: "Digit1" }, @@ -138,18 +116,14 @@ export const chars = { "#": { key: "Digit3", altRight: true }, 4: { key: "Digit4" }, "ç": { key: "Digit4", shift: true }, - "¼": { key: "Digit4", altRight: true }, 5: { key: "Digit5" }, "%": { key: "Digit5", shift: true }, - "½": { key: "Digit5", altRight: true }, 6: { key: "Digit6" }, "&": { key: "Digit6", shift: true }, - "¬": { key: "Digit6", altRight: true }, 7: { key: "Digit7" }, "/": { key: "Digit7", shift: true }, 8: { key: "Digit8" }, "(": { key: "Digit8", shift: true }, - "¢": { key: "Digit8", altRight: true }, 9: { key: "Digit9" }, ")": { key: "Digit9", shift: true }, 0: { key: "Digit0" }, @@ -174,10 +148,8 @@ export const chars = { "}": { key: "Backslash", altRight: true }, ",": { key: "Comma" }, ";": { key: "Comma", shift: true }, - "•": { key: "Comma", altRight: true }, ".": { key: "Period" }, ":": { key: "Period", shift: true }, - "·": { key: "Period", altRight: true }, "-": { key: "Slash" }, "_": { key: "Slash", shift: true }, "<": { key: "IntlBackslash" }, From 384025ecf55913822b806d7cc1dddebd8184505b Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Fri, 2 May 2025 21:19:51 +0200 Subject: [PATCH 07/24] Add Swiss French --- ui/src/keyboardLayouts.ts | 3 +++ ui/src/keyboardLayouts/fr_CH.ts | 12 ++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 ui/src/keyboardLayouts/fr_CH.ts diff --git a/ui/src/keyboardLayouts.ts b/ui/src/keyboardLayouts.ts index 170a71f..fc28955 100644 --- a/ui/src/keyboardLayouts.ts +++ b/ui/src/keyboardLayouts.ts @@ -1,4 +1,5 @@ import { chars as chars_en_US } from "@/keyboardLayouts/en_US" +import { chars as chars_fr_CH } from "@/keyboardLayouts/fr_CH" import { chars as chars_de_CH } from "@/keyboardLayouts/de_CH" type KeyInfo = { key: string | number; shift?: boolean, altRight?: boolean } @@ -6,10 +7,12 @@ export type KeyCombo = KeyInfo & { deadKey?: boolean, accentKey?: KeyInfo } export const layouts = { "en_US": "English (US)", + "fr_CH": "Swiss French", "de_CH": "Swiss German" } as Record; export const chars = { "en_US": chars_en_US, + "fr_CH": chars_fr_CH, "de_CH": chars_de_CH, } as Record> diff --git a/ui/src/keyboardLayouts/fr_CH.ts b/ui/src/keyboardLayouts/fr_CH.ts new file mode 100644 index 0000000..02f2b69 --- /dev/null +++ b/ui/src/keyboardLayouts/fr_CH.ts @@ -0,0 +1,12 @@ +import { KeyCombo } from "../keyboardLayouts" +import { chars as chars_de_CH } from "./de_CH" + +export const chars = { + ...chars_de_CH, + "è": { key: "BracketLeft" }, + "ü": { key: "BracketLeft", shift: true }, + "é": { key: "Semicolon" }, + "ö": { key: "Semicolon", shift: true }, + "à": { key: "Quote" }, + "ä": { key: "Quote", shift: true }, +} as Record; From 926e0b8a067e1a91827bd786c124d32d9ba11efd Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Fri, 2 May 2025 22:11:10 +0200 Subject: [PATCH 08/24] Change line ordering --- ui/src/keyboardLayouts.ts | 3 + ui/src/keyboardLayouts/de_CH.ts | 2 +- ui/src/keyboardLayouts/de_DE.ts | 150 ++++++++++++++++++++++++++++++++ 3 files changed, 154 insertions(+), 1 deletion(-) create mode 100644 ui/src/keyboardLayouts/de_DE.ts diff --git a/ui/src/keyboardLayouts.ts b/ui/src/keyboardLayouts.ts index fc28955..69b3ea9 100644 --- a/ui/src/keyboardLayouts.ts +++ b/ui/src/keyboardLayouts.ts @@ -1,4 +1,5 @@ import { chars as chars_en_US } from "@/keyboardLayouts/en_US" +import { chars as chars_de_DE } from "@/keyboardLayouts/de_DE" import { chars as chars_fr_CH } from "@/keyboardLayouts/fr_CH" import { chars as chars_de_CH } from "@/keyboardLayouts/de_CH" @@ -7,12 +8,14 @@ export type KeyCombo = KeyInfo & { deadKey?: boolean, accentKey?: KeyInfo } export const layouts = { "en_US": "English (US)", + "de_DE": "German", "fr_CH": "Swiss French", "de_CH": "Swiss German" } as Record; export const chars = { "en_US": chars_en_US, + "de_DE": chars_de_DE, "fr_CH": chars_fr_CH, "de_CH": chars_de_CH, } as Record> diff --git a/ui/src/keyboardLayouts/de_CH.ts b/ui/src/keyboardLayouts/de_CH.ts index 2b1019e..c822a7f 100644 --- a/ui/src/keyboardLayouts/de_CH.ts +++ b/ui/src/keyboardLayouts/de_CH.ts @@ -69,6 +69,7 @@ export const chars = { "ë": { key: "KeyE", accentKey: keyTrema }, "ê": { key: "KeyE", accentKey: keyHat }, "ẽ": { key: "KeyE", accentKey: keyTilde }, + "€": { key: "KeyE", altRight: true }, f: { key: "KeyF" }, g: { key: "KeyG" }, h: { key: "KeyH" }, @@ -155,7 +156,6 @@ export const chars = { "<": { key: "IntlBackslash" }, ">": { key: "IntlBackslash", shift: true }, "\\": { key: "IntlBackslash", altRight: true }, - "€": { key: "KeyE", altRight: true }, " ": { key: "Space" }, "\n": { key: "Enter" }, Enter: { key: "Enter" }, diff --git a/ui/src/keyboardLayouts/de_DE.ts b/ui/src/keyboardLayouts/de_DE.ts new file mode 100644 index 0000000..2082f7a --- /dev/null +++ b/ui/src/keyboardLayouts/de_DE.ts @@ -0,0 +1,150 @@ +import { KeyCombo } from "../keyboardLayouts" + +const keyAcute = { key: "Equal" } // accent aigu (acute accent), mark ´ placed above the letter +const keyHat = { key: "Backquote" } // accent circonflexe (accent hat), mark ^ placed above the letter +const keyGrave = { key: "Equal", shift: true } // accent grave, mark ` placed above the letter + +export const chars = { + A: { key: "KeyA", shift: true }, + "Á": { key: "KeyA", shift: true, accentKey: keyAcute }, + "Â": { key: "KeyA", shift: true, accentKey: keyHat }, + "À": { key: "KeyA", shift: true, accentKey: keyGrave }, + B: { key: "KeyB", shift: true }, + C: { key: "KeyC", shift: true }, + D: { key: "KeyD", shift: true }, + E: { key: "KeyE", shift: true }, + "É": { key: "KeyE", shift: true, accentKey: keyAcute }, + "Ê": { key: "KeyE", shift: true, accentKey: keyHat }, + "È": { key: "KeyE", shift: true, accentKey: keyGrave }, + F: { key: "KeyF", shift: true }, + G: { key: "KeyG", shift: true }, + H: { key: "KeyH", shift: true }, + I: { key: "KeyI", shift: true }, + "Í": { key: "KeyI", shift: true, accentKey: keyAcute }, + "Î": { key: "KeyI", shift: true, accentKey: keyHat }, + "Ì": { key: "KeyI", shift: true, accentKey: keyGrave }, + J: { key: "KeyJ", shift: true }, + K: { key: "KeyK", shift: true }, + L: { key: "KeyL", shift: true }, + M: { key: "KeyM", shift: true }, + N: { key: "KeyN", shift: true }, + O: { key: "KeyO", shift: true }, + "Ó": { key: "KeyO", shift: true, accentKey: keyAcute }, + "Ô": { key: "KeyO", shift: true, accentKey: keyHat }, + "Ò": { key: "KeyO", shift: true, accentKey: keyGrave }, + P: { key: "KeyP", shift: true }, + Q: { key: "KeyQ", shift: true }, + R: { key: "KeyR", shift: true }, + S: { key: "KeyS", shift: true }, + T: { key: "KeyT", shift: true }, + U: { key: "KeyU", shift: true }, + "Ú": { key: "KeyU", shift: true, accentKey: keyAcute }, + "Û": { key: "KeyU", shift: true, accentKey: keyHat }, + "Ù": { key: "KeyU", shift: true, accentKey: keyGrave }, + V: { key: "KeyV", shift: true }, + W: { key: "KeyW", shift: true }, + X: { key: "KeyX", shift: true }, + Y: { key: "KeyZ", shift: true }, + Z: { key: "KeyY", shift: true }, + a: { key: "KeyA" }, + "á": { key: "KeyA", accentKey: keyAcute }, + "â": { key: "KeyA", accentKey: keyHat }, + "à": { key: "KeyA", accentKey: keyGrave}, + b: { key: "KeyB" }, + c: { key: "KeyC" }, + d: { key: "KeyD" }, + e: { key: "KeyE" }, + "é": { key: "KeyE", accentKey: keyAcute}, + "ê": { key: "KeyE", accentKey: keyHat }, + "è": { key: "KeyE", accentKey: keyGrave }, + "€": { key: "KeyE", altRight: true }, + f: { key: "KeyF" }, + g: { key: "KeyG" }, + h: { key: "KeyH" }, + i: { key: "KeyI" }, + "í": { key: "KeyI", accentKey: keyAcute }, + "î": { key: "KeyI", accentKey: keyHat }, + "ì": { key: "KeyI", accentKey: keyGrave }, + j: { key: "KeyJ" }, + k: { key: "KeyK" }, + l: { key: "KeyL" }, + m: { key: "KeyM" }, + "µ": { key: "KeyM", altRight: true }, + n: { key: "KeyN" }, + o: { key: "KeyO" }, + "ó": { key: "KeyO", accentKey: keyAcute }, + "ô": { key: "KeyO", accentKey: keyHat }, + "ò": { key: "KeyO", accentKey: keyGrave }, + p: { key: "KeyP" }, + q: { key: "KeyQ" }, + "@": { key: "KeyQ", altRight: true }, + r: { key: "KeyR" }, + s: { key: "KeyS" }, + t: { key: "KeyT" }, + u: { key: "KeyU" }, + "ú": { key: "KeyU", accentKey: keyAcute }, + "û": { key: "KeyU", accentKey: keyHat }, + "ù": { key: "KeyU", accentKey: keyGrave }, + v: { key: "KeyV" }, + w: { key: "KeyW" }, + x: { key: "KeyX" }, + y: { key: "KeyZ" }, + z: { key: "KeyY" }, + "°": { key: "Backquote", shift: true }, + "^": { key: "Backquote", deadKey: true }, + 1: { key: "Digit1" }, + "!": { key: "Digit1", shift: true }, + 2: { key: "Digit2" }, + "\"": { key: "Digit2", shift: true }, + "²": { key: "Digit2", altRight: true }, + 3: { key: "Digit3" }, + "§": { key: "Digit3", shift: true }, + "³": { key: "Digit3", altRight: true }, + 4: { key: "Digit4" }, + "$": { key: "Digit4", shift: true }, + 5: { key: "Digit5" }, + "%": { key: "Digit5", shift: true }, + 6: { key: "Digit6" }, + "&": { key: "Digit6", shift: true }, + 7: { key: "Digit7" }, + "/": { key: "Digit7", shift: true }, + "{": { key: "Digit7", altRight: true }, + 8: { key: "Digit8" }, + "(": { key: "Digit8", shift: true }, + "[": { key: "Digit8", altRight: true }, + 9: { key: "Digit9" }, + ")": { key: "Digit9", shift: true }, + "]": { key: "Digit9", altRight: true }, + 0: { key: "Digit0" }, + "=": { key: "Digit0", shift: true }, + "}": { key: "Digit0", altRight: true }, + "ß": { key: "Minus" }, + "?": { key: "Minus", shift: true }, + "\\": { key: "Minus", altRight: true }, + "´": { key: "Equal", deadKey: true }, + "`": { key: "Equal", shift: true, deadKey: true }, + "ü": { key: "BracketLeft" }, + "Ü": { key: "BracketLeft", shift: true }, + "+": { key: "BracketRight" }, + "*": { key: "BracketRight", shift: true }, + "~": { key: "BracketRight", altRight: true }, + "ö": { key: "Semicolon" }, + "Ö": { key: "Semicolon", shift: true }, + "ä": { key: "Quote" }, + "Ä": { key: "Quote", shift: true }, + "#": { key: "Backslash" }, + "'": { key: "Backslash", shift: true }, + ",": { key: "Comma" }, + ";": { key: "Comma", shift: true }, + ".": { key: "Period" }, + ":": { key: "Period", shift: true }, + "-": { key: "Slash" }, + "_": { key: "Slash", shift: true }, + "<": { key: "IntlBackslash" }, + ">": { key: "IntlBackslash", shift: true }, + "|": { key: "IntlBackslash", altRight: true }, + " ": { key: "Space" }, + "\n": { key: "Enter" }, + Enter: { key: "Enter" }, + Tab: { key: "Tab" }, +} as Record; From 66f8d7bcd6fe1ab4748ed851d98a20922f7577e4 Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Fri, 2 May 2025 22:12:54 +0200 Subject: [PATCH 09/24] Fix whitespace --- ui/src/keyboardLayouts/de_DE.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/keyboardLayouts/de_DE.ts b/ui/src/keyboardLayouts/de_DE.ts index 2082f7a..08a58d3 100644 --- a/ui/src/keyboardLayouts/de_DE.ts +++ b/ui/src/keyboardLayouts/de_DE.ts @@ -91,7 +91,7 @@ export const chars = { y: { key: "KeyZ" }, z: { key: "KeyY" }, "°": { key: "Backquote", shift: true }, - "^": { key: "Backquote", deadKey: true }, + "^": { key: "Backquote", deadKey: true }, 1: { key: "Digit1" }, "!": { key: "Digit1", shift: true }, 2: { key: "Digit2" }, From 58d59eca47508bfda345c98cfa1c859c1c7ea7ec Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Fri, 2 May 2025 22:55:21 +0200 Subject: [PATCH 10/24] Add French (France) --- ui/src/keyboardLayouts.ts | 3 + ui/src/keyboardLayouts/fr_FR.ts | 137 ++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100644 ui/src/keyboardLayouts/fr_FR.ts diff --git a/ui/src/keyboardLayouts.ts b/ui/src/keyboardLayouts.ts index 69b3ea9..2c0eeda 100644 --- a/ui/src/keyboardLayouts.ts +++ b/ui/src/keyboardLayouts.ts @@ -1,4 +1,5 @@ import { chars as chars_en_US } from "@/keyboardLayouts/en_US" +import { chars as chars_fr_FR } from "@/keyboardLayouts/fr_FR" import { chars as chars_de_DE } from "@/keyboardLayouts/de_DE" import { chars as chars_fr_CH } from "@/keyboardLayouts/fr_CH" import { chars as chars_de_CH } from "@/keyboardLayouts/de_CH" @@ -8,6 +9,7 @@ export type KeyCombo = KeyInfo & { deadKey?: boolean, accentKey?: KeyInfo } export const layouts = { "en_US": "English (US)", + "fr_FR": "French", "de_DE": "German", "fr_CH": "Swiss French", "de_CH": "Swiss German" @@ -15,6 +17,7 @@ export const layouts = { export const chars = { "en_US": chars_en_US, + "fr_FR": chars_fr_FR, "de_DE": chars_de_DE, "fr_CH": chars_fr_CH, "de_CH": chars_de_CH, diff --git a/ui/src/keyboardLayouts/fr_FR.ts b/ui/src/keyboardLayouts/fr_FR.ts new file mode 100644 index 0000000..9d66d16 --- /dev/null +++ b/ui/src/keyboardLayouts/fr_FR.ts @@ -0,0 +1,137 @@ +import { KeyCombo } from "../keyboardLayouts" + +const keyTrema = { key: "BracketLeft", shift: true } // tréma (umlaut), two dots placed above a vowel +const keyHat = { key: "BracketLeft" } // accent circonflexe (accent hat), mark ^ placed above the letter + +export const chars = { + A: { key: "KeyQ", shift: true }, + "Ä": { key: "KeyA", shift: true, accentKey: keyTrema }, + "Â": { key: "KeyA", shift: true, accentKey: keyHat }, + B: { key: "KeyB", shift: true }, + C: { key: "KeyC", shift: true }, + D: { key: "KeyD", shift: true }, + E: { key: "KeyE", shift: true }, + "Ë": { key: "KeyE", shift: true, accentKey: keyTrema }, + "Ê": { key: "KeyE", shift: true, accentKey: keyHat }, + F: { key: "KeyF", shift: true }, + G: { key: "KeyG", shift: true }, + H: { key: "KeyH", shift: true }, + I: { key: "KeyI", shift: true }, + "Ï": { key: "KeyI", shift: true, accentKey: keyTrema }, + "Î": { key: "KeyI", shift: true, accentKey: keyHat }, + J: { key: "KeyJ", shift: true }, + K: { key: "KeyK", shift: true }, + L: { key: "KeyL", shift: true }, + M: { key: "Semicolon", shift: true }, + N: { key: "KeyN", shift: true }, + O: { key: "KeyO", shift: true }, + "Ö": { key: "KeyO", shift: true, accentKey: keyTrema }, + "Ô": { key: "KeyO", shift: true, accentKey: keyHat }, + P: { key: "KeyP", shift: true }, + Q: { key: "KeyA", shift: true }, + R: { key: "KeyR", shift: true }, + S: { key: "KeyS", shift: true }, + T: { key: "KeyT", shift: true }, + U: { key: "KeyU", shift: true }, + "Ü": { key: "KeyU", shift: true, accentKey: keyTrema }, + "Û": { key: "KeyU", shift: true, accentKey: keyHat }, + V: { key: "KeyV", shift: true }, + W: { key: "KeyZ", shift: true }, + X: { key: "KeyX", shift: true }, + Y: { key: "KeyY", shift: true }, + Z: { key: "KeyW", shift: true }, + a: { key: "KeyQ" }, + "ä": { key: "KeyA", accentKey: keyTrema }, + "â": { key: "KeyA", accentKey: keyHat }, + b: { key: "KeyB" }, + c: { key: "KeyC" }, + d: { key: "KeyD" }, + e: { key: "KeyE" }, + "ë": { key: "KeyE", accentKey: keyTrema }, + "ê": { key: "KeyE", accentKey: keyHat }, + "€": { key: "KeyE", altRight: true }, + f: { key: "KeyF" }, + g: { key: "KeyG" }, + h: { key: "KeyH" }, + i: { key: "KeyI" }, + "ï": { key: "KeyI", accentKey: keyTrema }, + "î": { key: "KeyI", accentKey: keyHat }, + j: { key: "KeyJ" }, + k: { key: "KeyK" }, + l: { key: "KeyL" }, + m: { key: "Semicolon" }, + n: { key: "KeyN" }, + o: { key: "KeyO" }, + "ö": { key: "KeyO", accentKey: keyTrema }, + "ô": { key: "KeyO", accentKey: keyHat }, + p: { key: "KeyP" }, + q: { key: "KeyA" }, + r: { key: "KeyR" }, + s: { key: "KeyS" }, + t: { key: "KeyT" }, + u: { key: "KeyU" }, + "ü": { key: "KeyU", accentKey: keyTrema }, + "û": { key: "KeyU", accentKey: keyHat }, + v: { key: "KeyV" }, + w: { key: "KeyZ" }, + x: { key: "KeyX" }, + y: { key: "KeyY" }, + z: { key: "KeyW" }, + "²": { key: "Backquote" }, + "&": { key: "Digit1" }, + 1: { key: "Digit1", shift: true }, + "é": { key: "Digit2" }, + 2: { key: "Digit2", shift: true }, + "~": { key: "Digit2", altRight: true }, + "\"": { key: "Digit3" }, + 3: { key: "Digit3", shift: true }, + "#": { key: "Digit3", altRight: true }, + "'": { key: "Digit4" }, + 4: { key: "Digit4", shift: true }, + "{": { key: "Digit4", altRight: true }, + "(": { key: "Digit5" }, + 5: { key: "Digit5", shift: true }, + "[": { key: "Digit5", altRight: true }, + "-": { key: "Digit6" }, + 6: { key: "Digit6", shift: true }, + "|": { key: "Digit6", altRight: true }, + "è": { key: "Digit7" }, + 7: { key: "Digit7", shift: true }, + "`": { key: "Digit7", altRight: true }, + "_": { key: "Digit8" }, + 8: { key: "Digit8", shift: true }, + "\\": { key: "Digit8", altRight: true }, + "ç": { key: "Digit9" }, + 9: { key: "Digit9", shift: true }, + "^": { key: "Digit9", altRight: true }, + "à": { key: "Digit0" }, + 0: { key: "Digit0", shift: true }, + "@": { key: "Digit0", altRight: true }, + ")": { key: "Minus" }, + "°": { key: "Minus", shift: true }, + "]": { key: "Minus", altRight: true }, + "=": { key: "Equal" }, + "+": { key: "Equal", shift: true }, + "}": { key: "Equal", altRight: true }, + "$": { key: "BracketRight" }, + "£": { key: "BracketRight", shift: true }, + "¤": { key: "BracketRight", altRight: true }, + "ù": { key: "Quote" }, + "%": { key: "Quote", shift: true }, + "*": { key: "Backslash" }, + "µ": { key: "Backslash", shift: true }, + ",": { key: "KeyM" }, + "?": { key: "KeyM", shift: true }, + ";": { key: "Comma" }, + ".": { key: "Comma", shift: true }, + ":": { key: "Period" }, + "/": { key: "Period", shift: true }, + "!": { key: "Slash" }, + "§": { key: "Slash", shift: true }, + "<": { key: "IntlBackslash" }, + ">": { key: "IntlBackslash", shift: true }, + " ": { key: "Space" }, + "\n": { key: "Enter" }, + Enter: { key: "Enter" }, + Tab: { key: "Tab" }, +} as Record; From dd5eee81796f16551e2b7fec5bb0fbef74ae64bc Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Fri, 2 May 2025 22:55:46 +0200 Subject: [PATCH 11/24] Add English (UK) --- ui/src/keyboardLayouts.ts | 3 + ui/src/keyboardLayouts/en_UK.ts | 105 ++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 ui/src/keyboardLayouts/en_UK.ts diff --git a/ui/src/keyboardLayouts.ts b/ui/src/keyboardLayouts.ts index 2c0eeda..de66f8d 100644 --- a/ui/src/keyboardLayouts.ts +++ b/ui/src/keyboardLayouts.ts @@ -1,3 +1,4 @@ +import { chars as chars_en_UK } from "@/keyboardLayouts/en_UK" import { chars as chars_en_US } from "@/keyboardLayouts/en_US" import { chars as chars_fr_FR } from "@/keyboardLayouts/fr_FR" import { chars as chars_de_DE } from "@/keyboardLayouts/de_DE" @@ -8,6 +9,7 @@ type KeyInfo = { key: string | number; shift?: boolean, altRight?: boolean } export type KeyCombo = KeyInfo & { deadKey?: boolean, accentKey?: KeyInfo } export const layouts = { + "en_UK": "English (UK)", "en_US": "English (US)", "fr_FR": "French", "de_DE": "German", @@ -16,6 +18,7 @@ export const layouts = { } as Record; export const chars = { + "en_UK": chars_en_UK, "en_US": chars_en_US, "fr_FR": chars_fr_FR, "de_DE": chars_de_DE, diff --git a/ui/src/keyboardLayouts/en_UK.ts b/ui/src/keyboardLayouts/en_UK.ts new file mode 100644 index 0000000..49d623f --- /dev/null +++ b/ui/src/keyboardLayouts/en_UK.ts @@ -0,0 +1,105 @@ +import { KeyCombo } from "../keyboardLayouts" + +export const chars = { + A: { key: "KeyA", shift: true }, + B: { key: "KeyB", shift: true }, + C: { key: "KeyC", shift: true }, + D: { key: "KeyD", shift: true }, + E: { key: "KeyE", shift: true }, + F: { key: "KeyF", shift: true }, + G: { key: "KeyG", shift: true }, + H: { key: "KeyH", shift: true }, + I: { key: "KeyI", shift: true }, + J: { key: "KeyJ", shift: true }, + K: { key: "KeyK", shift: true }, + L: { key: "KeyL", shift: true }, + M: { key: "KeyM", shift: true }, + N: { key: "KeyN", shift: true }, + O: { key: "KeyO", shift: true }, + P: { key: "KeyP", shift: true }, + Q: { key: "KeyQ", shift: true }, + R: { key: "KeyR", shift: true }, + S: { key: "KeyS", shift: true }, + T: { key: "KeyT", shift: true }, + U: { key: "KeyU", shift: true }, + V: { key: "KeyV", shift: true }, + W: { key: "KeyW", shift: true }, + X: { key: "KeyX", shift: true }, + Y: { key: "KeyY", shift: true }, + Z: { key: "KeyZ", shift: true }, + a: { key: "KeyA" }, + b: { key: "KeyB" }, + c: { key: "KeyC" }, + d: { key: "KeyD" }, + e: { key: "KeyE" }, + f: { key: "KeyF" }, + g: { key: "KeyG" }, + h: { key: "KeyH" }, + i: { key: "KeyI" }, + j: { key: "KeyJ" }, + k: { key: "KeyK" }, + l: { key: "KeyL" }, + m: { key: "KeyM" }, + n: { key: "KeyN" }, + o: { key: "KeyO" }, + p: { key: "KeyP" }, + q: { key: "KeyQ" }, + r: { key: "KeyR" }, + s: { key: "KeyS" }, + t: { key: "KeyT" }, + u: { key: "KeyU" }, + v: { key: "KeyV" }, + w: { key: "KeyW" }, + x: { key: "KeyX" }, + y: { key: "KeyY" }, + z: { key: "KeyZ" }, + 1: { key: "Digit1" }, + "!": { key: "Digit1", shift: true }, + 2: { key: "Digit2" }, + "\"": { key: "Digit2", shift: true }, + 3: { key: "Digit3" }, + "£": { key: "Digit3", shift: true }, + 4: { key: "Digit4" }, + $: { key: "Digit4", shift: true }, + "€": { key: "Digit4", altRight: true }, + 5: { key: "Digit5" }, + "%": { key: "Digit5", shift: true }, + 6: { key: "Digit6" }, + "^": { key: "Digit6", shift: true }, + 7: { key: "Digit7" }, + "&": { key: "Digit7", shift: true }, + 8: { key: "Digit8" }, + "*": { key: "Digit8", shift: true }, + 9: { key: "Digit9" }, + "(": { key: "Digit9", shift: true }, + 0: { key: "Digit0" }, + ")": { key: "Digit0", shift: true }, + "-": { key: "Minus" }, + _: { key: "Minus", shift: true }, + "=": { key: "Equal" }, + "+": { key: "Equal", shift: true }, + "'": { key: "Quote" }, + '@': { key: "Quote", shift: true }, + ",": { key: "Comma" }, + "<": { key: "Comma", shift: true }, + "/": { key: "Slash" }, + "?": { key: "Slash", shift: true }, + ".": { key: "Period" }, + ">": { key: "Period", shift: true }, + ";": { key: "Semicolon" }, + ":": { key: "Semicolon", shift: true }, + "[": { key: "BracketLeft" }, + "{": { key: "BracketLeft", shift: true }, + "]": { key: "BracketRight" }, + "}": { key: "BracketRight", shift: true }, + "#": { key: "Backslash" }, + "~": { key: "Backslash", shift: true }, + "`": { key: "Backquote" }, + "¬": { key: "Backquote", shift: true }, + "\\": { key: "IntlBackslash" }, + "|": { key: "IntlBackslash", shift: true }, + " ": { key: "Space" }, + "\n": { key: "Enter" }, + Enter: { key: "Enter" }, + Tab: { key: "Tab" }, +} as Record From af9c8fad09c1e176640d2f87e51cbb4c98d116eb Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Fri, 2 May 2025 23:18:56 +0200 Subject: [PATCH 12/24] Add Swedish --- ui/src/keyboardLayouts.ts | 3 + ui/src/keyboardLayouts/sv_SE.ts | 162 ++++++++++++++++++++++++++++++++ 2 files changed, 165 insertions(+) create mode 100644 ui/src/keyboardLayouts/sv_SE.ts diff --git a/ui/src/keyboardLayouts.ts b/ui/src/keyboardLayouts.ts index de66f8d..98c2bbd 100644 --- a/ui/src/keyboardLayouts.ts +++ b/ui/src/keyboardLayouts.ts @@ -2,6 +2,7 @@ import { chars as chars_en_UK } from "@/keyboardLayouts/en_UK" import { chars as chars_en_US } from "@/keyboardLayouts/en_US" import { chars as chars_fr_FR } from "@/keyboardLayouts/fr_FR" import { chars as chars_de_DE } from "@/keyboardLayouts/de_DE" +import { chars as chars_sv_SE } from "@/keyboardLayouts/sv_SE" import { chars as chars_fr_CH } from "@/keyboardLayouts/fr_CH" import { chars as chars_de_CH } from "@/keyboardLayouts/de_CH" @@ -13,6 +14,7 @@ export const layouts = { "en_US": "English (US)", "fr_FR": "French", "de_DE": "German", + "sv_SE": "Swedish", "fr_CH": "Swiss French", "de_CH": "Swiss German" } as Record; @@ -22,6 +24,7 @@ export const chars = { "en_US": chars_en_US, "fr_FR": chars_fr_FR, "de_DE": chars_de_DE, + "sv_SE": chars_sv_SE, "fr_CH": chars_fr_CH, "de_CH": chars_de_CH, } as Record> diff --git a/ui/src/keyboardLayouts/sv_SE.ts b/ui/src/keyboardLayouts/sv_SE.ts new file mode 100644 index 0000000..79e9771 --- /dev/null +++ b/ui/src/keyboardLayouts/sv_SE.ts @@ -0,0 +1,162 @@ +import { KeyCombo } from "../keyboardLayouts" + +const keyTrema = { key: "BracketRight" } // tréma (umlaut), two dots placed above a vowel +const keyAcute = { key: "Equal" } // accent aigu (acute accent), mark ´ placed above the letter +const keyHat = { key: "BracketRight", shift: true } // accent circonflexe (accent hat), mark ^ placed above the letter +const keyGrave = { key: "Equal", shift: true } // accent grave, mark ` placed above the letter +const keyTilde = { key: "BracketRight", altRight: true } // tilde, mark ~ placed above the letter + +export const chars = { + A: { key: "KeyA", shift: true }, + "Á": { key: "KeyA", shift: true, accentKey: keyAcute }, + "Â": { key: "KeyA", shift: true, accentKey: keyHat }, + "À": { key: "KeyA", shift: true, accentKey: keyGrave }, + "Ã": { key: "KeyA", shift: true, accentKey: keyTilde }, + B: { key: "KeyB", shift: true }, + C: { key: "KeyC", shift: true }, + D: { key: "KeyD", shift: true }, + E: { key: "KeyE", shift: true }, + "Ë": { key: "KeyE", shift: true, accentKey: keyTrema }, + "É": { key: "KeyE", shift: true, accentKey: keyAcute }, + "Ê": { key: "KeyE", shift: true, accentKey: keyHat }, + "È": { key: "KeyE", shift: true, accentKey: keyGrave }, + "Ẽ": { key: "KeyE", shift: true, accentKey: keyTilde }, + F: { key: "KeyF", shift: true }, + G: { key: "KeyG", shift: true }, + H: { key: "KeyH", shift: true }, + I: { key: "KeyI", shift: true }, + "Ï": { key: "KeyI", shift: true, accentKey: keyTrema }, + "Í": { key: "KeyI", shift: true, accentKey: keyAcute }, + "Î": { key: "KeyI", shift: true, accentKey: keyHat }, + "Ì": { key: "KeyI", shift: true, accentKey: keyGrave }, + "Ĩ": { key: "KeyI", shift: true, accentKey: keyTilde }, + J: { key: "KeyJ", shift: true }, + K: { key: "KeyK", shift: true }, + L: { key: "KeyL", shift: true }, + M: { key: "KeyM", shift: true }, + N: { key: "KeyN", shift: true }, + O: { key: "KeyO", shift: true }, + "Ó": { key: "KeyO", shift: true, accentKey: keyAcute }, + "Ô": { key: "KeyO", shift: true, accentKey: keyHat }, + "Ò": { key: "KeyO", shift: true, accentKey: keyGrave }, + "Õ": { key: "KeyO", shift: true, accentKey: keyTilde }, + P: { key: "KeyP", shift: true }, + Q: { key: "KeyQ", shift: true }, + R: { key: "KeyR", shift: true }, + S: { key: "KeyS", shift: true }, + T: { key: "KeyT", shift: true }, + U: { key: "KeyU", shift: true }, + "Ü": { key: "KeyU", shift: true, accentKey: keyTrema }, + "Ú": { key: "KeyU", shift: true, accentKey: keyAcute }, + "Û": { key: "KeyU", shift: true, accentKey: keyHat }, + "Ù": { key: "KeyU", shift: true, accentKey: keyGrave }, + "Ũ": { key: "KeyU", shift: true, accentKey: keyTilde }, + V: { key: "KeyV", shift: true }, + W: { key: "KeyW", shift: true }, + X: { key: "KeyX", shift: true }, + Y: { key: "KeyZ", shift: true }, + Z: { key: "KeyY", shift: true }, + a: { key: "KeyA" }, + "á": { key: "KeyA", accentKey: keyAcute }, + "â": { key: "KeyA", accentKey: keyHat }, + "à": { key: "KeyA", accentKey: keyGrave }, + "ã": { key: "KeyA", accentKey: keyTilde }, + b: { key: "KeyB" }, + c: { key: "KeyC" }, + d: { key: "KeyD" }, + e: { key: "KeyE" }, + "ë": { key: "KeyE", accentKey: keyTrema }, + "é": { key: "KeyE", accentKey: keyAcute }, + "ê": { key: "KeyE", accentKey: keyHat }, + "è": { key: "KeyE", accentKey: keyGrave }, + "ẽ": { key: "KeyE", accentKey: keyTilde }, + "€": { key: "KeyE", altRight: true }, + f: { key: "KeyF" }, + g: { key: "KeyG" }, + h: { key: "KeyH" }, + i: { key: "KeyI" }, + "ï": { key: "KeyI", accentKey: keyTrema }, + "í": { key: "KeyI", accentKey: keyAcute }, + "î": { key: "KeyI", accentKey: keyHat }, + "ì": { key: "KeyI", accentKey: keyGrave }, + "ĩ": { key: "KeyI", accentKey: keyTilde }, + j: { key: "KeyJ" }, + k: { key: "KeyK" }, + l: { key: "KeyL" }, + m: { key: "KeyM" }, + n: { key: "KeyN" }, + o: { key: "KeyO" }, + "ó": { key: "KeyO", accentKey: keyAcute }, + "ô": { key: "KeyO", accentKey: keyHat }, + "ò": { key: "KeyO", accentKey: keyGrave }, + "õ": { key: "KeyO", accentKey: keyTilde }, + p: { key: "KeyP" }, + q: { key: "KeyQ" }, + r: { key: "KeyR" }, + s: { key: "KeyS" }, + t: { key: "KeyT" }, + u: { key: "KeyU" }, + "ü": { key: "KeyU", accentKey: keyTrema }, + "ú": { key: "KeyU", accentKey: keyAcute }, + "û": { key: "KeyU", accentKey: keyHat }, + "ù": { key: "KeyU", accentKey: keyGrave }, + "ũ": { key: "KeyU", accentKey: keyTilde }, + v: { key: "KeyV" }, + w: { key: "KeyW" }, + x: { key: "KeyX" }, + y: { key: "KeyZ" }, + z: { key: "KeyY" }, + "§": { key: "Backquote" }, + "½": { key: "Backquote", shift: true }, + 1: { key: "Digit1" }, + "!": { key: "Digit1", shift: true }, + 2: { key: "Digit2" }, + "\"": { key: "Digit2", shift: true }, + "@": { key: "Digit2", altRight: true }, + 3: { key: "Digit3" }, + "#": { key: "Digit3", shift: true }, + "£": { key: "Digit3", altRight: true }, + 4: { key: "Digit4" }, + "¤": { key: "Digit4", shift: true }, + "$": { key: "Digit4", altRight: true }, + 5: { key: "Digit5" }, + "%": { key: "Digit5", shift: true }, + 6: { key: "Digit6" }, + "&": { key: "Digit6", shift: true }, + 7: { key: "Digit7" }, + "/": { key: "Digit7", shift: true }, + "{": { key: "Digit7", altRight: true }, + 8: { key: "Digit8" }, + "(": { key: "Digit8", shift: true }, + "[": { key: "Digit8", altRight: true }, + 9: { key: "Digit9" }, + ")": { key: "Digit9", shift: true }, + "]": { key: "Digit9", altRight: true }, + 0: { key: "Digit0" }, + "=": { key: "Digit0", shift: true }, + "}": { key: "Digit0", altRight: true }, + "+": { key: "Minus" }, + "?": { key: "Minus", shift: true }, + "\\": { key: "Minus", altRight: true }, + "å": { key: "BracketLeft" }, + "Å": { key: "BracketLeft", shift: true }, + "ö": { key: "Semicolon" }, + "Ö": { key: "Semicolon", shift: true }, + "ä": { key: "Quote" }, + "Ä": { key: "Quote", shift: true }, + "'": { key: "Backslash" }, + "*": { key: "Backslash", shift: true }, + ",": { key: "Comma" }, + ";": { key: "Comma", shift: true }, + ".": { key: "Period" }, + ":": { key: "Period", shift: true }, + "-": { key: "Slash" }, + "_": { key: "Slash", shift: true }, + "<": { key: "IntlBackslash" }, + ">": { key: "IntlBackslash", shift: true }, + "|": { key: "IntlBackslash", altRight: true }, + " ": { key: "Space" }, + "\n": { key: "Enter" }, + Enter: { key: "Enter" }, + Tab: { key: "Tab" }, +} as Record; From cb1937aac6d4cae9e41503689ad5fe9b8a444358 Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Fri, 2 May 2025 23:56:52 +0200 Subject: [PATCH 13/24] Add Spanish --- ui/src/keyboardLayouts.ts | 3 + ui/src/keyboardLayouts/es_ES.ts | 164 ++++++++++++++++++++++++++++++++ 2 files changed, 167 insertions(+) create mode 100644 ui/src/keyboardLayouts/es_ES.ts diff --git a/ui/src/keyboardLayouts.ts b/ui/src/keyboardLayouts.ts index 98c2bbd..723e855 100644 --- a/ui/src/keyboardLayouts.ts +++ b/ui/src/keyboardLayouts.ts @@ -2,6 +2,7 @@ import { chars as chars_en_UK } from "@/keyboardLayouts/en_UK" import { chars as chars_en_US } from "@/keyboardLayouts/en_US" import { chars as chars_fr_FR } from "@/keyboardLayouts/fr_FR" import { chars as chars_de_DE } from "@/keyboardLayouts/de_DE" +import { chars as chars_es_ES } from "@/keyboardLayouts/es_ES" import { chars as chars_sv_SE } from "@/keyboardLayouts/sv_SE" import { chars as chars_fr_CH } from "@/keyboardLayouts/fr_CH" import { chars as chars_de_CH } from "@/keyboardLayouts/de_CH" @@ -14,6 +15,7 @@ export const layouts = { "en_US": "English (US)", "fr_FR": "French", "de_DE": "German", + "es_ES": "Spanish", "sv_SE": "Swedish", "fr_CH": "Swiss French", "de_CH": "Swiss German" @@ -24,6 +26,7 @@ export const chars = { "en_US": chars_en_US, "fr_FR": chars_fr_FR, "de_DE": chars_de_DE, + "es_ES": chars_es_ES, "sv_SE": chars_sv_SE, "fr_CH": chars_fr_CH, "de_CH": chars_de_CH, diff --git a/ui/src/keyboardLayouts/es_ES.ts b/ui/src/keyboardLayouts/es_ES.ts new file mode 100644 index 0000000..9a53d93 --- /dev/null +++ b/ui/src/keyboardLayouts/es_ES.ts @@ -0,0 +1,164 @@ +import { KeyCombo } from "../keyboardLayouts" + +const keyTrema = { key: "Quote", shift: true } // tréma (umlaut), two dots placed above a vowel +const keyAcute = { key: "Quote" } // accent aigu (acute accent), mark ´ placed above the letter +const keyHat = { key: "BracketRight", shift: true } // accent circonflexe (accent hat), mark ^ placed above the letter +const keyGrave = { key: "BracketRight" } // accent grave, mark ` placed above the letter +const keyTilde = { key: "Key4", altRight: true } // tilde, mark ~ placed above the letter + +export const chars = { + A: { key: "KeyA", shift: true }, + "Ä": { key: "KeyA", shift: true, accentKey: keyTrema }, + "Á": { key: "KeyA", shift: true, accentKey: keyAcute }, + "Â": { key: "KeyA", shift: true, accentKey: keyHat }, + "À": { key: "KeyA", shift: true, accentKey: keyGrave }, + "Ã": { key: "KeyA", shift: true, accentKey: keyTilde }, + B: { key: "KeyB", shift: true }, + C: { key: "KeyC", shift: true }, + D: { key: "KeyD", shift: true }, + E: { key: "KeyE", shift: true }, + "Ë": { key: "KeyE", shift: true, accentKey: keyTrema }, + "É": { key: "KeyE", shift: true, accentKey: keyAcute }, + "Ê": { key: "KeyE", shift: true, accentKey: keyHat }, + "È": { key: "KeyE", shift: true, accentKey: keyGrave }, + "Ẽ": { key: "KeyE", shift: true, accentKey: keyTilde }, + F: { key: "KeyF", shift: true }, + G: { key: "KeyG", shift: true }, + H: { key: "KeyH", shift: true }, + I: { key: "KeyI", shift: true }, + "Ï": { key: "KeyI", shift: true, accentKey: keyTrema }, + "Í": { key: "KeyI", shift: true, accentKey: keyAcute }, + "Î": { key: "KeyI", shift: true, accentKey: keyHat }, + "Ì": { key: "KeyI", shift: true, accentKey: keyGrave }, + "Ĩ": { key: "KeyI", shift: true, accentKey: keyTilde }, + J: { key: "KeyJ", shift: true }, + K: { key: "KeyK", shift: true }, + L: { key: "KeyL", shift: true }, + M: { key: "KeyM", shift: true }, + N: { key: "KeyN", shift: true }, + O: { key: "KeyO", shift: true }, + "Ö": { key: "KeyO", shift: true, accentKey: keyTrema }, + "Ó": { key: "KeyO", shift: true, accentKey: keyAcute }, + "Ô": { key: "KeyO", shift: true, accentKey: keyHat }, + "Ò": { key: "KeyO", shift: true, accentKey: keyGrave }, + "Õ": { key: "KeyO", shift: true, accentKey: keyTilde }, + P: { key: "KeyP", shift: true }, + Q: { key: "KeyQ", shift: true }, + R: { key: "KeyR", shift: true }, + S: { key: "KeyS", shift: true }, + T: { key: "KeyT", shift: true }, + U: { key: "KeyU", shift: true }, + "Ü": { key: "KeyU", shift: true, accentKey: keyTrema }, + "Ú": { key: "KeyU", shift: true, accentKey: keyAcute }, + "Û": { key: "KeyU", shift: true, accentKey: keyHat }, + "Ù": { key: "KeyU", shift: true, accentKey: keyGrave }, + "Ũ": { key: "KeyU", shift: true, accentKey: keyTilde }, + V: { key: "KeyV", shift: true }, + W: { key: "KeyW", shift: true }, + X: { key: "KeyX", shift: true }, + Y: { key: "KeyY", shift: true }, + Z: { key: "KeyZ", shift: true }, + a: { key: "KeyA" }, + "ä": { key: "KeyA", accentKey: keyTrema }, + "á": { key: "KeyA", accentKey: keyAcute }, + "â": { key: "KeyA", accentKey: keyHat }, + "à": { key: "KeyA", accentKey: keyGrave }, + "ã": { key: "KeyA", accentKey: keyTilde }, + b: { key: "KeyB" }, + c: { key: "KeyC" }, + d: { key: "KeyD" }, + e: { key: "KeyE" }, + "ë": { key: "KeyE", accentKey: keyTrema }, + "é": { key: "KeyE", accentKey: keyAcute }, + "ê": { key: "KeyE", accentKey: keyHat }, + "è": { key: "KeyE", accentKey: keyGrave }, + "ẽ": { key: "KeyE", accentKey: keyTilde }, + "€": { key: "KeyE", altRight: true }, + f: { key: "KeyF" }, + g: { key: "KeyG" }, + h: { key: "KeyH" }, + i: { key: "KeyI" }, + "ï": { key: "KeyI", accentKey: keyTrema }, + "í": { key: "KeyI", accentKey: keyAcute }, + "î": { key: "KeyI", accentKey: keyHat }, + "ì": { key: "KeyI", accentKey: keyGrave }, + "ĩ": { key: "KeyI", accentKey: keyTilde }, + j: { key: "KeyJ" }, + k: { key: "KeyK" }, + l: { key: "KeyL" }, + m: { key: "KeyM" }, + n: { key: "KeyN" }, + o: { key: "KeyO" }, + "ó": { key: "KeyO", accentKey: keyAcute }, + "ô": { key: "KeyO", accentKey: keyHat }, + "ò": { key: "KeyO", accentKey: keyGrave }, + "õ": { key: "KeyO", accentKey: keyTilde }, + p: { key: "KeyP" }, + q: { key: "KeyQ" }, + r: { key: "KeyR" }, + s: { key: "KeyS" }, + t: { key: "KeyT" }, + u: { key: "KeyU" }, + "ú": { key: "KeyU", accentKey: keyAcute }, + "û": { key: "KeyU", accentKey: keyHat }, + "ù": { key: "KeyU", accentKey: keyGrave }, + "ũ": { key: "KeyU", accentKey: keyTilde }, + v: { key: "KeyV" }, + w: { key: "KeyW" }, + x: { key: "KeyX" }, + y: { key: "KeyY" }, + z: { key: "KeyZ" }, + "º": { key: "Backquote" }, + "ª": { key: "Backquote", shift: true }, + "\\": { key: "Backquote", altRight: true }, + 1: { key: "Digit1" }, + "!": { key: "Digit1", shift: true }, + "|": { key: "Digit1", altRight: true }, + 2: { key: "Digit2" }, + "\"": { key: "Digit2", shift: true }, + "@": { key: "Digit2", altRight: true }, + 3: { key: "Digit3" }, + "·": { key: "Digit3", shift: true }, + "#": { key: "Digit3", altRight: true }, + 4: { key: "Digit4" }, + "$": { key: "Digit4", shift: true }, + 5: { key: "Digit5" }, + "%": { key: "Digit5", shift: true }, + 6: { key: "Digit6" }, + "&": { key: "Digit6", shift: true }, + "¬": { key: "Digit6", altRight: true }, + 7: { key: "Digit7" }, + "/": { key: "Digit7", shift: true }, + 8: { key: "Digit8" }, + "(": { key: "Digit8", shift: true }, + 9: { key: "Digit9" }, + ")": { key: "Digit9", shift: true }, + 0: { key: "Digit0" }, + "=": { key: "Digit0", shift: true }, + "'": { key: "Minus" }, + "?": { key: "Minus", shift: true }, + "¡": { key: "Equal", deadKey: true }, + "¿": { key: "Equal", shift: true }, + "[": { key: "BracketLeft", altRight: true }, + "+": { key: "BracketRight" }, + "*": { key: "BracketRight", shift: true }, + "]": { key: "BracketRight", altRight: true }, + "ñ": { key: "Semicolon" }, + "Ñ": { key: "Semicolon", shift: true }, + "{": { key: "Quote", altRight: true }, + "ç": { key: "Backslash" }, + "Ç": { key: "Backslash", shift: true }, + "}": { key: "Backslash", altRight: true }, + ",": { key: "Comma" }, + ";": { key: "Comma", shift: true }, + ".": { key: "Period" }, + ":": { key: "Period", shift: true }, + "-": { key: "Slash" }, + "_": { key: "Slash", shift: true }, + "<": { key: "IntlBackslash" }, + ">": { key: "IntlBackslash", shift: true }, + " ": { key: "Space" }, + "\n": { key: "Enter" }, + Enter: { key: "Enter" }, + Tab: { key: "Tab" }, +} as Record; From e4ec2c1d8dcd9a89f78dc61b7c17d9d98c8972ec Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Sat, 3 May 2025 00:20:22 +0200 Subject: [PATCH 14/24] Fix fr_FR special characters --- ui/src/keyboardLayouts/fr_FR.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/src/keyboardLayouts/fr_FR.ts b/ui/src/keyboardLayouts/fr_FR.ts index 9d66d16..755deb1 100644 --- a/ui/src/keyboardLayouts/fr_FR.ts +++ b/ui/src/keyboardLayouts/fr_FR.ts @@ -5,8 +5,8 @@ const keyHat = { key: "BracketLeft" } // accent circonflexe (accent hat), mark ^ export const chars = { A: { key: "KeyQ", shift: true }, - "Ä": { key: "KeyA", shift: true, accentKey: keyTrema }, - "Â": { key: "KeyA", shift: true, accentKey: keyHat }, + "Ä": { key: "KeyQ", shift: true, accentKey: keyTrema }, + "Â": { key: "KeyQ", shift: true, accentKey: keyHat }, B: { key: "KeyB", shift: true }, C: { key: "KeyC", shift: true }, D: { key: "KeyD", shift: true }, @@ -41,8 +41,8 @@ export const chars = { Y: { key: "KeyY", shift: true }, Z: { key: "KeyW", shift: true }, a: { key: "KeyQ" }, - "ä": { key: "KeyA", accentKey: keyTrema }, - "â": { key: "KeyA", accentKey: keyHat }, + "ä": { key: "KeyQ", accentKey: keyTrema }, + "â": { key: "KeyQ", accentKey: keyHat }, b: { key: "KeyB" }, c: { key: "KeyC" }, d: { key: "KeyD" }, From 4cbde51ce7dffb61650baa6dc15bd35ee079f9f1 Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Sat, 3 May 2025 00:50:40 +0200 Subject: [PATCH 15/24] Add more keys to Spanish --- ui/src/keyboardLayouts/es_ES.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ui/src/keyboardLayouts/es_ES.ts b/ui/src/keyboardLayouts/es_ES.ts index 9a53d93..9e34bea 100644 --- a/ui/src/keyboardLayouts/es_ES.ts +++ b/ui/src/keyboardLayouts/es_ES.ts @@ -89,6 +89,7 @@ export const chars = { m: { key: "KeyM" }, n: { key: "KeyN" }, o: { key: "KeyO" }, + "ö": { key: "KeyO", accentKey: keyTrema }, "ó": { key: "KeyO", accentKey: keyAcute }, "ô": { key: "KeyO", accentKey: keyHat }, "ò": { key: "KeyO", accentKey: keyGrave }, @@ -99,6 +100,7 @@ export const chars = { s: { key: "KeyS" }, t: { key: "KeyT" }, u: { key: "KeyU" }, + "ü": { key: "KeyU", accentKey: keyTrema }, "ú": { key: "KeyU", accentKey: keyAcute }, "û": { key: "KeyU", accentKey: keyHat }, "ù": { key: "KeyU", accentKey: keyGrave }, From 2feef185c4d3507a26e284c8d3ad6d7384a3cd16 Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Sat, 3 May 2025 22:41:17 +0200 Subject: [PATCH 16/24] Remove default value shift: false --- ui/src/keyboardLayouts/en_US.ts | 96 ++++++++++++++++----------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/ui/src/keyboardLayouts/en_US.ts b/ui/src/keyboardLayouts/en_US.ts index b1e9dea..02dc5ee 100644 --- a/ui/src/keyboardLayouts/en_US.ts +++ b/ui/src/keyboardLayouts/en_US.ts @@ -27,75 +27,75 @@ export const chars = { X: { key: "KeyX", shift: true }, Y: { key: "KeyY", shift: true }, Z: { key: "KeyZ", shift: true }, - a: { key: "KeyA", shift: false }, - b: { key: "KeyB", shift: false }, - c: { key: "KeyC", shift: false }, - d: { key: "KeyD", shift: false }, - e: { key: "KeyE", shift: false }, - f: { key: "KeyF", shift: false }, - g: { key: "KeyG", shift: false }, - h: { key: "KeyH", shift: false }, - i: { key: "KeyI", shift: false }, - j: { key: "KeyJ", shift: false }, - k: { key: "KeyK", shift: false }, - l: { key: "KeyL", shift: false }, - m: { key: "KeyM", shift: false }, - n: { key: "KeyN", shift: false }, - o: { key: "KeyO", shift: false }, - p: { key: "KeyP", shift: false }, - q: { key: "KeyQ", shift: false }, - r: { key: "KeyR", shift: false }, - s: { key: "KeyS", shift: false }, - t: { key: "KeyT", shift: false }, - u: { key: "KeyU", shift: false }, - v: { key: "KeyV", shift: false }, - w: { key: "KeyW", shift: false }, - x: { key: "KeyX", shift: false }, - y: { key: "KeyY", shift: false }, - z: { key: "KeyZ", shift: false }, - 1: { key: "Digit1", shift: false }, + a: { key: "KeyA" }, + b: { key: "KeyB" }, + c: { key: "KeyC" }, + d: { key: "KeyD" }, + e: { key: "KeyE" }, + f: { key: "KeyF" }, + g: { key: "KeyG" }, + h: { key: "KeyH" }, + i: { key: "KeyI" }, + j: { key: "KeyJ" }, + k: { key: "KeyK" }, + l: { key: "KeyL" }, + m: { key: "KeyM" }, + n: { key: "KeyN" }, + o: { key: "KeyO" }, + p: { key: "KeyP" }, + q: { key: "KeyQ" }, + r: { key: "KeyR" }, + s: { key: "KeyS" }, + t: { key: "KeyT" }, + u: { key: "KeyU" }, + v: { key: "KeyV" }, + w: { key: "KeyW" }, + x: { key: "KeyX" }, + y: { key: "KeyY" }, + z: { key: "KeyZ" }, + 1: { key: "Digit1" }, "!": { key: "Digit1", shift: true }, - 2: { key: "Digit2", shift: false }, + 2: { key: "Digit2" }, "@": { key: "Digit2", shift: true }, - 3: { key: "Digit3", shift: false }, + 3: { key: "Digit3" }, "#": { key: "Digit3", shift: true }, - 4: { key: "Digit4", shift: false }, + 4: { key: "Digit4" }, $: { key: "Digit4", shift: true }, "%": { key: "Digit5", shift: true }, - 5: { key: "Digit5", shift: false }, + 5: { key: "Digit5" }, "^": { key: "Digit6", shift: true }, - 6: { key: "Digit6", shift: false }, + 6: { key: "Digit6" }, "&": { key: "Digit7", shift: true }, - 7: { key: "Digit7", shift: false }, + 7: { key: "Digit7" }, "*": { key: "Digit8", shift: true }, - 8: { key: "Digit8", shift: false }, + 8: { key: "Digit8" }, "(": { key: "Digit9", shift: true }, - 9: { key: "Digit9", shift: false }, + 9: { key: "Digit9" }, ")": { key: "Digit0", shift: true }, - 0: { key: "Digit0", shift: false }, - "-": { key: "Minus", shift: false }, + 0: { key: "Digit0" }, + "-": { key: "Minus" }, _: { key: "Minus", shift: true }, - "=": { key: "Equal", shift: false }, + "=": { key: "Equal" }, "+": { key: "Equal", shift: true }, - "'": { key: "Quote", shift: false }, + "'": { key: "Quote" }, '"': { key: "Quote", shift: true }, - ",": { key: "Comma", shift: false }, + ",": { key: "Comma" }, "<": { key: "Comma", shift: true }, - "/": { key: "Slash", shift: false }, + "/": { key: "Slash" }, "?": { key: "Slash", shift: true }, - ".": { key: "Period", shift: false }, + ".": { key: "Period" }, ">": { key: "Period", shift: true }, - ";": { key: "Semicolon", shift: false }, + ";": { key: "Semicolon" }, ":": { key: "Semicolon", shift: true }, - "[": { key: "BracketLeft", shift: false }, + "[": { key: "BracketLeft" }, "{": { key: "BracketLeft", shift: true }, - "]": { key: "BracketRight", shift: false }, + "]": { key: "BracketRight" }, "}": { key: "BracketRight", shift: true }, - "\\": { key: "Backslash", shift: false }, + "\\": { key: "Backslash" }, "|": { key: "Backslash", shift: true }, - "`": { key: "Backquote", shift: false }, + "`": { key: "Backquote" }, "~": { key: "Backquote", shift: true }, - "§": { key: "IntlBackslash", shift: false }, + "§": { key: "IntlBackslash" }, "±": { key: "IntlBackslash", shift: true }, " ": { key: "Space", shift: false }, "\n": { key: "Enter", shift: false }, From ac42be96e71083eca0c80c920be1ab8685087405 Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Sat, 3 May 2025 23:56:56 +0200 Subject: [PATCH 17/24] Add Norwegian --- ui/src/keyboardLayouts.ts | 3 + ui/src/keyboardLayouts/nb_NO.ts | 165 ++++++++++++++++++++++++++++++++ 2 files changed, 168 insertions(+) create mode 100644 ui/src/keyboardLayouts/nb_NO.ts diff --git a/ui/src/keyboardLayouts.ts b/ui/src/keyboardLayouts.ts index 723e855..ce77e9f 100644 --- a/ui/src/keyboardLayouts.ts +++ b/ui/src/keyboardLayouts.ts @@ -2,6 +2,7 @@ import { chars as chars_en_UK } from "@/keyboardLayouts/en_UK" import { chars as chars_en_US } from "@/keyboardLayouts/en_US" import { chars as chars_fr_FR } from "@/keyboardLayouts/fr_FR" import { chars as chars_de_DE } from "@/keyboardLayouts/de_DE" +import { chars as chars_nb_NO } from "@/keyboardLayouts/nb_NO" import { chars as chars_es_ES } from "@/keyboardLayouts/es_ES" import { chars as chars_sv_SE } from "@/keyboardLayouts/sv_SE" import { chars as chars_fr_CH } from "@/keyboardLayouts/fr_CH" @@ -15,6 +16,7 @@ export const layouts = { "en_US": "English (US)", "fr_FR": "French", "de_DE": "German", + "nb_NO": "Norwegian", "es_ES": "Spanish", "sv_SE": "Swedish", "fr_CH": "Swiss French", @@ -26,6 +28,7 @@ export const chars = { "en_US": chars_en_US, "fr_FR": chars_fr_FR, "de_DE": chars_de_DE, + "nb_NO": chars_nb_NO, "es_ES": chars_es_ES, "sv_SE": chars_sv_SE, "fr_CH": chars_fr_CH, diff --git a/ui/src/keyboardLayouts/nb_NO.ts b/ui/src/keyboardLayouts/nb_NO.ts new file mode 100644 index 0000000..de18561 --- /dev/null +++ b/ui/src/keyboardLayouts/nb_NO.ts @@ -0,0 +1,165 @@ +import { KeyCombo } from "../keyboardLayouts" + +const keyTrema = { key: "BracketRight" } // tréma (umlaut), two dots placed above a vowel +const keyAcute = { key: "Equal", altRight: true } // accent aigu (acute accent), mark ´ placed above the letter +const keyHat = { key: "BracketRight", shift: true } // accent circonflexe (accent hat), mark ^ placed above the letter +const keyGrave = { key: "Equal", shift: true } // accent grave, mark ` placed above the letter +const keyTilde = { key: "BracketRight", altRight: true } // tilde, mark ~ placed above the letter + +export const chars = { + A: { key: "KeyA", shift: true }, + "Ä": { key: "KeyA", shift: true, accentKey: keyTrema }, + "Á": { key: "KeyA", shift: true, accentKey: keyAcute }, + "Â": { key: "KeyA", shift: true, accentKey: keyHat }, + "À": { key: "KeyA", shift: true, accentKey: keyGrave }, + "Ã": { key: "KeyA", shift: true, accentKey: keyTilde }, + B: { key: "KeyB", shift: true }, + C: { key: "KeyC", shift: true }, + D: { key: "KeyD", shift: true }, + E: { key: "KeyE", shift: true }, + "Ë": { key: "KeyE", shift: true, accentKey: keyTrema }, + "É": { key: "KeyE", shift: true, accentKey: keyAcute }, + "Ê": { key: "KeyE", shift: true, accentKey: keyHat }, + "È": { key: "KeyE", shift: true, accentKey: keyGrave }, + "Ẽ": { key: "KeyE", shift: true, accentKey: keyTilde }, + F: { key: "KeyF", shift: true }, + G: { key: "KeyG", shift: true }, + H: { key: "KeyH", shift: true }, + I: { key: "KeyI", shift: true }, + "Ï": { key: "KeyI", shift: true, accentKey: keyTrema }, + "Í": { key: "KeyI", shift: true, accentKey: keyAcute }, + "Î": { key: "KeyI", shift: true, accentKey: keyHat }, + "Ì": { key: "KeyI", shift: true, accentKey: keyGrave }, + "Ĩ": { key: "KeyI", shift: true, accentKey: keyTilde }, + J: { key: "KeyJ", shift: true }, + K: { key: "KeyK", shift: true }, + L: { key: "KeyL", shift: true }, + M: { key: "KeyM", shift: true }, + N: { key: "KeyN", shift: true }, + O: { key: "KeyO", shift: true }, + "Ö": { key: "KeyO", shift: true, accentKey: keyTrema }, + "Ó": { key: "KeyO", shift: true, accentKey: keyAcute }, + "Ô": { key: "KeyO", shift: true, accentKey: keyHat }, + "Ò": { key: "KeyO", shift: true, accentKey: keyGrave }, + "Õ": { key: "KeyO", shift: true, accentKey: keyTilde }, + P: { key: "KeyP", shift: true }, + Q: { key: "KeyQ", shift: true }, + R: { key: "KeyR", shift: true }, + S: { key: "KeyS", shift: true }, + T: { key: "KeyT", shift: true }, + U: { key: "KeyU", shift: true }, + "Ü": { key: "KeyU", shift: true, accentKey: keyTrema }, + "Ú": { key: "KeyU", shift: true, accentKey: keyAcute }, + "Û": { key: "KeyU", shift: true, accentKey: keyHat }, + "Ù": { key: "KeyU", shift: true, accentKey: keyGrave }, + "Ũ": { key: "KeyU", shift: true, accentKey: keyTilde }, + V: { key: "KeyV", shift: true }, + W: { key: "KeyW", shift: true }, + X: { key: "KeyX", shift: true }, + Y: { key: "KeyZ", shift: true }, + Z: { key: "KeyY", shift: true }, + a: { key: "KeyA" }, + "ä": { key: "KeyA", accentKey: keyTrema }, + "á": { key: "KeyA", accentKey: keyAcute }, + "â": { key: "KeyA", accentKey: keyHat }, + "à": { key: "KeyA", accentKey: keyGrave }, + "ã": { key: "KeyA", accentKey: keyTilde }, + b: { key: "KeyB" }, + c: { key: "KeyC" }, + d: { key: "KeyD" }, + e: { key: "KeyE" }, + "ë": { key: "KeyE", accentKey: keyTrema }, + "é": { key: "KeyE", accentKey: keyAcute }, + "ê": { key: "KeyE", accentKey: keyHat }, + "è": { key: "KeyE", accentKey: keyGrave }, + "ẽ": { key: "KeyE", accentKey: keyTilde }, + "€": { key: "KeyE", altRight: true }, + f: { key: "KeyF" }, + g: { key: "KeyG" }, + h: { key: "KeyH" }, + i: { key: "KeyI" }, + "ï": { key: "KeyI", accentKey: keyTrema }, + "í": { key: "KeyI", accentKey: keyAcute }, + "î": { key: "KeyI", accentKey: keyHat }, + "ì": { key: "KeyI", accentKey: keyGrave }, + "ĩ": { key: "KeyI", accentKey: keyTilde }, + j: { key: "KeyJ" }, + k: { key: "KeyK" }, + l: { key: "KeyL" }, + m: { key: "KeyM" }, + n: { key: "KeyN" }, + o: { key: "KeyO" }, + "ö": { key: "KeyO", accentKey: keyTrema }, + "ó": { key: "KeyO", accentKey: keyAcute }, + "ô": { key: "KeyO", accentKey: keyHat }, + "ò": { key: "KeyO", accentKey: keyGrave }, + "õ": { key: "KeyO", accentKey: keyTilde }, + p: { key: "KeyP" }, + q: { key: "KeyQ" }, + r: { key: "KeyR" }, + s: { key: "KeyS" }, + t: { key: "KeyT" }, + u: { key: "KeyU" }, + "ü": { key: "KeyU", accentKey: keyTrema }, + "ú": { key: "KeyU", accentKey: keyAcute }, + "û": { key: "KeyU", accentKey: keyHat }, + "ù": { key: "KeyU", accentKey: keyGrave }, + "ũ": { key: "KeyU", accentKey: keyTilde }, + v: { key: "KeyV" }, + w: { key: "KeyW" }, + x: { key: "KeyX" }, + y: { key: "KeyZ" }, + z: { key: "KeyY" }, + "|": { key: "Backquote" }, + "§": { key: "Backquote", shift: true }, + 1: { key: "Digit1" }, + "!": { key: "Digit1", shift: true }, + 2: { key: "Digit2" }, + "\"": { key: "Digit2", shift: true }, + "@": { key: "Digit2", altRight: true }, + 3: { key: "Digit3" }, + "#": { key: "Digit3", shift: true }, + "£": { key: "Digit3", altRight: true }, + 4: { key: "Digit4" }, + "¤": { key: "Digit4", shift: true }, + "$": { key: "Digit4", altRight: true }, + 5: { key: "Digit5" }, + "%": { key: "Digit5", shift: true }, + 6: { key: "Digit6" }, + "&": { key: "Digit6", shift: true }, + 7: { key: "Digit7" }, + "/": { key: "Digit7", shift: true }, + "{": { key: "Digit7", altRight: true }, + 8: { key: "Digit8" }, + "(": { key: "Digit8", shift: true }, + "[": { key: "Digit8", altRight: true }, + 9: { key: "Digit9" }, + ")": { key: "Digit9", shift: true }, + "]": { key: "Digit9", altRight: true }, + 0: { key: "Digit0" }, + "=": { key: "Digit0", shift: true }, + "}": { key: "Digit0", altRight: true }, + "+": { key: "Minus" }, + "?": { key: "Minus", shift: true }, + "\\": { key: "Equal" }, + "å": { key: "BracketLeft" }, + "Å": { key: "BracketLeft", shift: true }, + "ø": { key: "Semicolon" }, + "Ø": { key: "Semicolon", shift: true }, + "æ": { key: "Quote" }, + "Æ": { key: "Quote", shift: true }, + "'": { key: "Backslash" }, + "*": { key: "Backslash", shift: true }, + ",": { key: "Comma" }, + ";": { key: "Comma", shift: true }, + ".": { key: "Period" }, + ":": { key: "Period", shift: true }, + "-": { key: "Slash" }, + "_": { key: "Slash", shift: true }, + "<": { key: "IntlBackslash" }, + ">": { key: "IntlBackslash", shift: true }, + " ": { key: "Space" }, + "\n": { key: "Enter" }, + Enter: { key: "Enter" }, + Tab: { key: "Tab" }, +} as Record; From 0d28a5d9142dc77abff92070c3ff557801584d5d Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Sun, 4 May 2025 00:25:55 +0200 Subject: [PATCH 18/24] =?UTF-8?q?Operator=20precedence=20=F0=9F=A4=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/src/components/popovers/PasteModal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/components/popovers/PasteModal.tsx b/ui/src/components/popovers/PasteModal.tsx index 6ad60db..38c392d 100644 --- a/ui/src/components/popovers/PasteModal.tsx +++ b/ui/src/components/popovers/PasteModal.tsx @@ -18,7 +18,7 @@ const hidKeyboardPayload = (keys: number[], modifier: number) => { }; const modifierCode = (shift?: boolean, altRight?: boolean) => { - return shift ? modifiers["ShiftLeft"] : 0 + return (shift ? modifiers["ShiftLeft"] : 0) | (altRight ? modifiers["AltRight"] : 0) } const noModifier = 0 From d8d11a0020702441b212c821a7baeb3fdbb2fee5 Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Sun, 4 May 2025 00:26:32 +0200 Subject: [PATCH 19/24] Add Italian --- ui/src/keyboardLayouts.ts | 3 + ui/src/keyboardLayouts/it_IT.ts | 111 ++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 ui/src/keyboardLayouts/it_IT.ts diff --git a/ui/src/keyboardLayouts.ts b/ui/src/keyboardLayouts.ts index ce77e9f..000dfbe 100644 --- a/ui/src/keyboardLayouts.ts +++ b/ui/src/keyboardLayouts.ts @@ -2,6 +2,7 @@ import { chars as chars_en_UK } from "@/keyboardLayouts/en_UK" import { chars as chars_en_US } from "@/keyboardLayouts/en_US" import { chars as chars_fr_FR } from "@/keyboardLayouts/fr_FR" import { chars as chars_de_DE } from "@/keyboardLayouts/de_DE" +import { chars as chars_it_IT } from "@/keyboardLayouts/it_IT" import { chars as chars_nb_NO } from "@/keyboardLayouts/nb_NO" import { chars as chars_es_ES } from "@/keyboardLayouts/es_ES" import { chars as chars_sv_SE } from "@/keyboardLayouts/sv_SE" @@ -16,6 +17,7 @@ export const layouts = { "en_US": "English (US)", "fr_FR": "French", "de_DE": "German", + "it_IT": "Italian", "nb_NO": "Norwegian", "es_ES": "Spanish", "sv_SE": "Swedish", @@ -28,6 +30,7 @@ export const chars = { "en_US": chars_en_US, "fr_FR": chars_fr_FR, "de_DE": chars_de_DE, + "it_IT": chars_it_IT, "nb_NO": chars_nb_NO, "es_ES": chars_es_ES, "sv_SE": chars_sv_SE, diff --git a/ui/src/keyboardLayouts/it_IT.ts b/ui/src/keyboardLayouts/it_IT.ts new file mode 100644 index 0000000..fa87e11 --- /dev/null +++ b/ui/src/keyboardLayouts/it_IT.ts @@ -0,0 +1,111 @@ +import { KeyCombo } from "../keyboardLayouts" + +export const chars = { + A: { key: "KeyA", shift: true }, + B: { key: "KeyB", shift: true }, + C: { key: "KeyC", shift: true }, + D: { key: "KeyD", shift: true }, + E: { key: "KeyE", shift: true }, + F: { key: "KeyF", shift: true }, + G: { key: "KeyG", shift: true }, + H: { key: "KeyH", shift: true }, + I: { key: "KeyI", shift: true }, + J: { key: "KeyJ", shift: true }, + K: { key: "KeyK", shift: true }, + L: { key: "KeyL", shift: true }, + M: { key: "KeyM", shift: true }, + N: { key: "KeyN", shift: true }, + O: { key: "KeyO", shift: true }, + P: { key: "KeyP", shift: true }, + Q: { key: "KeyQ", shift: true }, + R: { key: "KeyR", shift: true }, + S: { key: "KeyS", shift: true }, + T: { key: "KeyT", shift: true }, + U: { key: "KeyU", shift: true }, + V: { key: "KeyV", shift: true }, + W: { key: "KeyW", shift: true }, + X: { key: "KeyX", shift: true }, + Y: { key: "KeyY", shift: true }, + Z: { key: "KeyZ", shift: true }, + a: { key: "KeyA" }, + b: { key: "KeyB" }, + c: { key: "KeyC" }, + d: { key: "KeyD" }, + e: { key: "KeyE" }, + "€": { key: "KeyE", altRight: true }, + f: { key: "KeyF" }, + g: { key: "KeyG" }, + h: { key: "KeyH" }, + i: { key: "KeyI" }, + j: { key: "KeyJ" }, + k: { key: "KeyK" }, + l: { key: "KeyL" }, + m: { key: "KeyM" }, + n: { key: "KeyN" }, + o: { key: "KeyO" }, + p: { key: "KeyP" }, + q: { key: "KeyQ" }, + r: { key: "KeyR" }, + s: { key: "KeyS" }, + t: { key: "KeyT" }, + u: { key: "KeyU" }, + v: { key: "KeyV" }, + w: { key: "KeyW" }, + x: { key: "KeyX" }, + y: { key: "KeyY" }, + z: { key: "KeyZ" }, + "\\": { key: "Backquote" }, + "|": { key: "Backquote", shift: true }, + 1: { key: "Digit1" }, + "!": { key: "Digit1", shift: true }, + 2: { key: "Digit2" }, + "\"": { key: "Digit2", shift: true }, + 3: { key: "Digit3" }, + "£": { key: "Digit3", shift: true }, + 4: { key: "Digit4" }, + "$": { key: "Digit4", shift: true }, + 5: { key: "Digit5" }, + "%": { key: "Digit5", shift: true }, + 6: { key: "Digit6" }, + "&": { key: "Digit6", shift: true }, + 7: { key: "Digit7" }, + "/": { key: "Digit7", shift: true }, + 8: { key: "Digit8" }, + "(": { key: "Digit8", shift: true }, + 9: { key: "Digit9" }, + ")": { key: "Digit9", shift: true }, + 0: { key: "Digit0" }, + "=": { key: "Digit0", shift: true }, + "'": { key: "Minus" }, + "?": { key: "Minus", shift: true }, + "ì": { key: "Equal" }, + "^": { key: "Equal", shift: true }, + "è": { key: "BracketLeft" }, + "é": { key: "BracketLeft", shift: true }, + "[": { key: "BracketLeft", altRight: true }, + "{": { key: "BracketLeft", shift: true, altRight: true }, + "+": { key: "BracketRight" }, + "*": { key: "BracketRight", shift: true }, + "]": { key: "BracketRight", altRight: true }, + "}": { key: "BracketRight", shift: true, altRight: true }, + "ò": { key: "Semicolon" }, + "ç": { key: "Semicolon", shift: true }, + "@": { key: "Semicolon", altRight: true }, + "à": { key: "Quote" }, + "°": { key: "Quote", shift: true }, + "#": { key: "Quote", altRight: true }, + "ù": { key: "Backslash" }, + "§": { key: "Backslash", shift: true }, + ",": { key: "Comma" }, + ";": { key: "Comma", shift: true }, + ".": { key: "Period" }, + ":": { key: "Period", shift: true }, + "-": { key: "Slash" }, + "_": { key: "Slash", shift: true }, + "<": { key: "IntlBackslash" }, + ">": { key: "IntlBackslash", shift: true }, + " ": { key: "Space" }, + "\n": { key: "Enter" }, + Enter: { key: "Enter" }, + Tab: { key: "Tab" }, +} as Record; From 9056dea38be8f6ef33e3e575bd69949466874bda Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Sun, 4 May 2025 02:37:51 +0200 Subject: [PATCH 20/24] Add Czech --- ui/src/keyboardLayouts.ts | 3 + ui/src/keyboardLayouts/cs_CZ.ts | 242 ++++++++++++++++++++++++++++++++ 2 files changed, 245 insertions(+) create mode 100644 ui/src/keyboardLayouts/cs_CZ.ts diff --git a/ui/src/keyboardLayouts.ts b/ui/src/keyboardLayouts.ts index 000dfbe..35d5703 100644 --- a/ui/src/keyboardLayouts.ts +++ b/ui/src/keyboardLayouts.ts @@ -1,3 +1,4 @@ +import { chars as chars_cs_CZ } from "@/keyboardLayouts/cs_CZ" import { chars as chars_en_UK } from "@/keyboardLayouts/en_UK" import { chars as chars_en_US } from "@/keyboardLayouts/en_US" import { chars as chars_fr_FR } from "@/keyboardLayouts/fr_FR" @@ -13,6 +14,7 @@ type KeyInfo = { key: string | number; shift?: boolean, altRight?: boolean } export type KeyCombo = KeyInfo & { deadKey?: boolean, accentKey?: KeyInfo } export const layouts = { + "cs_CZ": "Czech", "en_UK": "English (UK)", "en_US": "English (US)", "fr_FR": "French", @@ -26,6 +28,7 @@ export const layouts = { } as Record; export const chars = { + "cs_CZ": chars_cs_CZ, "en_UK": chars_en_UK, "en_US": chars_en_US, "fr_FR": chars_fr_FR, diff --git a/ui/src/keyboardLayouts/cs_CZ.ts b/ui/src/keyboardLayouts/cs_CZ.ts new file mode 100644 index 0000000..e889e32 --- /dev/null +++ b/ui/src/keyboardLayouts/cs_CZ.ts @@ -0,0 +1,242 @@ +import { KeyCombo } from "../keyboardLayouts" + +const keyTrema = { key: "Backslash" } // tréma (umlaut), two dots placed above a vowel +const keyAcute = { key: "Equal" } // accent aigu (acute accent), mark ´ placed above the letter +const keyHat = { key: "Digit3", shift: true, altRight: true } // accent circonflexe (accent hat), mark ^ placed above the letter +const keyCaron = { key: "Equal", shift: true } // caron or haček (inverted hat), mark ˇ placed above the letter +const keyGrave = { key: "Digit7", shift: true, altRight: true } // accent grave, mark ` placed above the letter +const keyTilde = { key: "Digit1", shift: true, altRight: true } // tilde, mark ~ placed above the letter +const keyRing = { key: "Backquote", shift: true } // kroužek (little ring), mark ° placed above the letter +const keyOverdot = { key: "Digit8", shift: true, altRight: true } // overdot (dot above), mark ˙ placed above the letter +const keyHook = { key: "Digit6", shift: true, altRight: true } // ogonoek (little hook), mark ˛ placed beneath a letter +const keyCedille = { key: "Equal", shift: true, altRight: true } // accent cedille (cedilla), mark ¸ placed beneath a letter + +export const chars = { + A: { key: "KeyA", shift: true }, + "Ä": { key: "KeyA", shift: true, accentKey: keyTrema }, + "Á": { key: "KeyA", shift: true, accentKey: keyAcute }, + "Â": { key: "KeyA", shift: true, accentKey: keyHat }, + "À": { key: "KeyA", shift: true, accentKey: keyGrave }, + "Ã": { key: "KeyA", shift: true, accentKey: keyTilde }, + "Ȧ": { key: "KeyA", shift: true, accentKey: keyOverdot }, + "Ą": { key: "KeyA", shift: true, accentKey: keyHook }, + B: { key: "KeyB", shift: true }, + "Ḃ": { key: "KeyB", shift: true, accentKEy: keyOverdot }, + C: { key: "KeyC", shift: true }, + "Č": { key: "KeyC", shift: true, accentKey: keyCaron }, + "Ċ": { key: "KeyC", shift: true, accentKey: keyOverdot }, + "Ç": { key: "KeyC", shift: true, accentKey: keyCedille }, + D: { key: "KeyD", shift: true }, + "Ď": { key: "KeyD", shift: true, accentKey: keyCaron }, + "Ḋ": { key: "KeyD", shift: true, accentKey: keyOverdot }, + E: { key: "KeyE", shift: true }, + "Ë": { key: "KeyE", shift: true, accentKey: keyTrema }, + "É": { key: "KeyE", shift: true, accentKey: keyAcute }, + "Ê": { key: "KeyE", shift: true, accentKey: keyHat }, + "Ě": { key: "KeyE", shift: true, accentKey: keyCaron }, + "È": { key: "KeyE", shift: true, accentKey: keyGrave }, + "Ẽ": { key: "KeyE", shift: true, accentKey: keyTilde }, + "Ė": { key: "KeyE", shift: true, accentKEy: keyOverdot }, + "Ę": { key: "KeyE", shift: true, accentKey: keyHook }, + F: { key: "KeyF", shift: true }, + "Ḟ": { key: "KeyF", shift: true, accentKey: keyOverdot }, + G: { key: "KeyG", shift: true }, + "Ġ": { key: "KeyG", shift: true, accentKey: keyOverdot }, + H: { key: "KeyH", shift: true }, + "Ḣ": { key: "KeyH", shift: true, accentKey: keyOverdot }, + I: { key: "KeyI", shift: true }, + "Ï": { key: "KeyI", shift: true, accentKey: keyTrema }, + "Í": { key: "KeyI", shift: true, accentKey: keyAcute }, + "Î": { key: "KeyI", shift: true, accentKey: keyHat }, + "Ì": { key: "KeyI", shift: true, accentKey: keyGrave }, + "Ĩ": { key: "KeyI", shift: true, accentKey: keyTilde }, + "İ": { key: "KeyI", shift: true, accentKey: keyOverdot }, + "Į": { key: "KeyI", shift: true, accentKey: keyHook }, + J: { key: "KeyJ", shift: true }, + K: { key: "KeyK", shift: true }, + L: { key: "KeyL", shift: true }, + "Ŀ": { key: "KeyL", shift: true }, + M: { key: "KeyM", shift: true }, + "Ṁ": { key: "KeyM", shift: true }, + N: { key: "KeyN", shift: true }, + "Ň": { key: "KeyN", shift: true, accentKey: keyCaron }, + "Ñ": { key: "KeyN", shift: true, accentKey: keyTilde }, + "Ṅ": { key: "KeyN", shift: true, accentKEy: keyOverdot }, + O: { key: "KeyO", shift: true }, + "Ö": { key: "KeyO", shift: true, accentKey: keyTrema }, + "Ó": { key: "KeyO", shift: true, accentKey: keyAcute }, + "Ô": { key: "KeyO", shift: true, accentKey: keyHat }, + "Ò": { key: "KeyO", shift: true, accentKey: keyGrave }, + "Õ": { key: "KeyO", shift: true, accentKey: keyTilde }, + "Ȯ": { key: "KeyO", shift: true, accentKey: keyOverdot }, + "Ǫ": { key: "KeyO", shift: true, accentKey: keyHook }, + P: { key: "KeyP", shift: true }, + "Ṗ": { key: "KeyP", shift: true, accentKey: keyOverdot }, + Q: { key: "KeyQ", shift: true }, + R: { key: "KeyR", shift: true }, + "Ř": { key: "KeyR", shift: true, accentKey: keyCaron }, + "Ṙ": { key: "KeyR", shift: true, accentKey: keyOverdot }, + S: { key: "KeyS", shift: true }, + "Š": { key: "KeyS", shift: true, accentKey: keyCaron }, + "Ṡ": { key: "KeyS", shift: true, accentKey: keyOverdot }, + T: { key: "KeyT", shift: true }, + "Ť": { key: "KeyT", shift: true, accentKey: keyCaron }, + "Ṫ": { key: "KeyT", shift: true, accentKey: keyOverdot }, + U: { key: "KeyU", shift: true }, + "Ü": { key: "KeyU", shift: true, accentKey: keyTrema }, + "Ú": { key: "KeyU", shift: true, accentKey: keyAcute }, + "Û": { key: "KeyU", shift: true, accentKey: keyHat }, + "Ù": { key: "KeyU", shift: true, accentKey: keyGrave }, + "Ũ": { key: "KeyU", shift: true, accentKey: keyTilde }, + "Ů": { key: "KeyU", shift: true, accentKey: keyRing }, + "Ų": { key: "KeyU", shift: true, accentKey: keyHook }, + V: { key: "KeyV", shift: true }, + W: { key: "KeyW", shift: true }, + "Ẇ": { key: "KeyW", shift: true, accentKey: keyOverdot }, + X: { key: "KeyX", shift: true }, + "Ẋ": { key: "KeyX", shift: true, accentKey: keyOverdot }, + Y: { key: "KeyY", shift: true }, + "Ý": { key: "KeyY", shift: true, accentKey: keyAcute }, + "Ẏ": { key: "KeyY", shift: true, accentKey: keyOverdot }, + Z: { key: "KeyZ", shift: true }, + "Ż": { key: "KeyZ", shift: true, accentKey: keyOverdot }, + a: { key: "KeyA" }, + "ä": { key: "KeyA", accentKey: keyTrema }, + "â": { key: "KeyA", accentKey: keyHat }, + "à": { key: "KeyA", accentKey: keyGrave }, + "ã": { key: "KeyA", accentKey: keyTilde }, + "ȧ": { key: "KeyA", accentKey: keyOverdot }, + "ą": { key: "KeyA", accentKey: keyHook }, + b: { key: "KeyB" }, + "{": { key: "KeyB", altRight: true }, + "ḃ": { key: "KeyB", accentKey: keyOverdot }, + c: { key: "KeyC" }, + "&": { key: "KeyC", altRight: true }, + "ç": { key: "KeyC", accentKey: keyCedille }, + "ċ": { key: "KeyC", accentKey: keyOverdot }, + d: { key: "KeyD" }, + "ď": { key: "KeyD", accentKey: keyCaron }, + "ḋ": { key: "KeyD", accentKey: keyOverdot }, + "Đ": { key: "KeyD", altRight: true }, + e: { key: "KeyE" }, + "ë": { key: "KeyE", accentKey: keyTrema }, + "ê": { key: "KeyE", accentKey: keyHat }, + "ẽ": { key: "KeyE", accentKey: keyTilde }, + "è": { key: "KeyE", accentKey: keyGrave }, + "ė": { key: "KeyE", accentKey: keyOverdot }, + "ę": { key: "KeyE", accentKey: keyHook }, + "€": { key: "KeyE", altRight: true }, + f: { key: "KeyF" }, + "ḟ": { key: "KeyF", accentKey: keyOverdot }, + "[": { key: "KeyF", altRight: true }, + g: { key: "KeyG" }, + "ġ": { key: "KeyG", accentKey: keyOverdot }, + "]": { key: "KeyF", altRight: true }, + h: { key: "KeyH" }, + "ḣ": { key: "KeyH", accentKey: keyOverdot }, + i: { key: "KeyI" }, + "ï": { key: "KeyI", accentKey: keyTrema }, + "î": { key: "KeyI", accentKey: keyHat }, + "ì": { key: "KeyI", accentKey: keyGrave }, + "ĩ": { key: "KeyI", accentKey: keyTilde }, + "ı": { key: "KeyI", accentKey: keyOverdot }, + "į": { key: "KeyI", accentKey: keyHook }, + j: { key: "KeyJ" }, + "ȷ": { key: "KeyJ", accentKey: keyOverdot }, + k: { key: "KeyK" }, + "ł": { key: "KeyK", altRight: true }, + l: { key: "KeyL" }, + "ŀ": { key: "KeyL", accentKey: keyOverdot }, + "Ł": { key: "KeyL", altRight: true }, + m: { key: "KeyM" }, + "ṁ": { key: "KeyM", accentKey: keyOverdot }, + n: { key: "KeyN" }, + "}": { key: "KeyN", altRight: true }, + "ň": { key: "KeyN", accentKey: keyCaron }, + "ñ": { key: "KeyN", accentKey: keyTilde }, + "ṅ": { key: "KeyN", accentKey: keyOverdot }, + o: { key: "KeyO" }, + "ö": { key: "Key0", accentKey: keyTrema }, + "ó": { key: "KeyO", accentKey: keyAcute }, + "ô": { key: "KeyO", accentKey: keyHat }, + "ò": { key: "KeyO", accentKey: keyGrave }, + "õ": { key: "KeyO", accentKey: keyTilde }, + "ȯ": { key: "KeyO", accentKey: keyOverdot }, + "ǫ": { key: "KeyO", accentKey: keyHook }, + p: { key: "KeyP" }, + "ṗ": { key: "KeyP", accentKey: keyOverdot }, + q: { key: "KeyQ" }, + r: { key: "KeyR" }, + "ṙ": { key: "KeyR", accentKey: keyOverdot }, + s: { key: "KeyS" }, + "ṡ": { key: "KeyS", accentKey: keyOverdot }, + "đ": { key: "KeyS", altRight: true }, + t: { key: "KeyT" }, + "ť": { key: "KeyT", accentKey: keyCaron }, + "ṫ": { key: "KeyT", accentKey: keyOverdot }, + u: { key: "KeyU" }, + "ü": { key: "KeyU", accentKey: keyTrema }, + "û": { key: "KeyU", accentKey: keyHat }, + "ù": { key: "KeyU", accentKey: keyGrave }, + "ũ": { key: "KeyU", accentKey: keyTilde }, + "ų": { key: "KeyU", accentKey: keyHook }, + v: { key: "KeyV" }, + "@": { key: "KeyV", altRight: true }, + w: { key: "KeyW" }, + "ẇ": { key: "KeyW", accentKey: keyOverdot }, + x: { key: "KeyX" }, + "#": { key: "KeyX", altRight: true }, + "ẋ": { key: "KeyX", accentKey: keyOverdot }, + y: { key: "KeyY" }, + "ẏ": { key: "KeyY", accentKey: keyOverdot }, + z: { key: "KeyZ" }, + "ż": { key: "KeyZ", accentKey: keyOverdot }, + ";": { key: "Backquote" }, + "°": { key: "Backquote", shift: true, deadKey: true }, + "+": { key: "Digit1" }, + 1: { key: "Digit1", shift: true }, + "ě": { key: "Digit2" }, + 2: { key: "Digit2", shift: true }, + "š": { key: "Digit3" }, + 3: { key: "Digit3", shift: true }, + "č": { key: "Digit4" }, + 4: { key: "Digit4", shift: true }, + "ř": { key: "Digit5" }, + 5: { key: "Digit5", shift: true }, + "ž": { key: "Digit6" }, + 6: { key: "Digit6", shift: true }, + "ý": { key: "Digit7" }, + 7: { key: "Digit7", shift: true }, + "á": { key: "Digit8" }, + 8: { key: "Digit8", shift: true }, + "í": { key: "Digit9" }, + 9: { key: "Digit9", shift: true }, + "é": { key: "Digit0" }, + 0: { key: "Digit0", shift: true }, + "=": { key: "Minus" }, + "%": { key: "Minus", shift: true }, + "ú": { key: "BracketLeft" }, + "/": { key: "BracketLeft", shift: true }, + ")": { key: "BracketRight" }, + "(": { key: "BracketRight", shift: true }, + "ů": { key: "Semicolon" }, + "\"": { key: "Semicolon", shift: true }, + "§": { key: "Quote" }, + "!": { key: "Quote", shift: true }, + "'": { key: "Backslash", shift: true }, + ",": { key: "Comma" }, + "?": { key: "Comma", shift: true }, + "<": { key: "Comma", altRight: true }, + ".": { key: "Period" }, + ":": { key: "Period", shift: true }, + ">": { key: "Period", altRight: true }, + "-": { key: "Slash" }, + "_": { key: "Slash", shift: true }, + "*": { key: "Slash", altRight: true }, + "\\": { key: "IntlBackslash" }, + "|": { key: "IntlBackslash", shift: true }, + " ": { key: "Space" }, + "\n": { key: "Enter" }, + Enter: { key: "Enter" }, + Tab: { key: "Tab" }, +} as Record; From 8d1475f29abe5c28dfb32357dfd6f1f5ae2b6085 Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Thu, 8 May 2025 20:25:53 +0200 Subject: [PATCH 21/24] Move guard statements outside of loop --- ui/src/components/popovers/PasteModal.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ui/src/components/popovers/PasteModal.tsx b/ui/src/components/popovers/PasteModal.tsx index 38c392d..2f0662d 100644 --- a/ui/src/components/popovers/PasteModal.tsx +++ b/ui/src/components/popovers/PasteModal.tsx @@ -56,14 +56,13 @@ export default function PasteModal() { setPasteMode(false); setDisableVideoFocusTrap(false); if (rpcDataChannel?.readyState !== "open" || !TextAreaRef.current) return; + if (!keyboardLayout) return; + if (!chars[keyboardLayout]) return; const text = TextAreaRef.current.value; try { for (const char of text) { - if (!keyboardLayout) continue; - if (!chars[keyboardLayout]) continue; - const { key, shift, altRight, deadKey, accentKey } = chars[keyboardLayout][char] if (!key) continue; From 057f6c9d578ef8b881782840a18fd6ddb6334a44 Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Thu, 8 May 2025 20:26:59 +0200 Subject: [PATCH 22/24] Move language name definitions into the keyboard layout files --- ui/src/keyboardLayouts.ts | 74 ++++++++++++++++----------------- ui/src/keyboardLayouts/cs_CZ.ts | 2 + ui/src/keyboardLayouts/de_CH.ts | 2 + ui/src/keyboardLayouts/de_DE.ts | 2 + ui/src/keyboardLayouts/en_UK.ts | 2 + ui/src/keyboardLayouts/en_US.ts | 2 + ui/src/keyboardLayouts/es_ES.ts | 2 + ui/src/keyboardLayouts/fr_CH.ts | 2 + ui/src/keyboardLayouts/fr_FR.ts | 2 + ui/src/keyboardLayouts/it_IT.ts | 2 + ui/src/keyboardLayouts/nb_NO.ts | 2 + ui/src/keyboardLayouts/sv_SE.ts | 2 + 12 files changed, 59 insertions(+), 37 deletions(-) diff --git a/ui/src/keyboardLayouts.ts b/ui/src/keyboardLayouts.ts index 35d5703..8dc0dec 100644 --- a/ui/src/keyboardLayouts.ts +++ b/ui/src/keyboardLayouts.ts @@ -1,42 +1,42 @@ -import { chars as chars_cs_CZ } from "@/keyboardLayouts/cs_CZ" -import { chars as chars_en_UK } from "@/keyboardLayouts/en_UK" -import { chars as chars_en_US } from "@/keyboardLayouts/en_US" -import { chars as chars_fr_FR } from "@/keyboardLayouts/fr_FR" -import { chars as chars_de_DE } from "@/keyboardLayouts/de_DE" -import { chars as chars_it_IT } from "@/keyboardLayouts/it_IT" -import { chars as chars_nb_NO } from "@/keyboardLayouts/nb_NO" -import { chars as chars_es_ES } from "@/keyboardLayouts/es_ES" -import { chars as chars_sv_SE } from "@/keyboardLayouts/sv_SE" -import { chars as chars_fr_CH } from "@/keyboardLayouts/fr_CH" -import { chars as chars_de_CH } from "@/keyboardLayouts/de_CH" +import { chars as chars_cs_CZ, name as name_cs_CZ } from "@/keyboardLayouts/cs_CZ" +import { chars as chars_en_UK, name as name_en_UK } from "@/keyboardLayouts/en_UK" +import { chars as chars_en_US, name as name_en_US } from "@/keyboardLayouts/en_US" +import { chars as chars_fr_FR, name as name_fr_FR } from "@/keyboardLayouts/fr_FR" +import { chars as chars_de_DE, name as name_de_DE } from "@/keyboardLayouts/de_DE" +import { chars as chars_it_IT, name as name_it_IT } from "@/keyboardLayouts/it_IT" +import { chars as chars_nb_NO, name as name_nb_NO } from "@/keyboardLayouts/nb_NO" +import { chars as chars_es_ES, name as name_es_ES } from "@/keyboardLayouts/es_ES" +import { chars as chars_sv_SE, name as name_sv_SE } from "@/keyboardLayouts/sv_SE" +import { chars as chars_fr_CH, name as name_fr_CH } from "@/keyboardLayouts/fr_CH" +import { chars as chars_de_CH, name as name_de_CH } from "@/keyboardLayouts/de_CH" type KeyInfo = { key: string | number; shift?: boolean, altRight?: boolean } export type KeyCombo = KeyInfo & { deadKey?: boolean, accentKey?: KeyInfo } -export const layouts = { - "cs_CZ": "Czech", - "en_UK": "English (UK)", - "en_US": "English (US)", - "fr_FR": "French", - "de_DE": "German", - "it_IT": "Italian", - "nb_NO": "Norwegian", - "es_ES": "Spanish", - "sv_SE": "Swedish", - "fr_CH": "Swiss French", - "de_CH": "Swiss German" -} as Record; +export const layouts: Record = { + cs_CZ: name_cs_CZ, + en_UK: name_en_UK, + en_US: name_en_US, + fr_FR: name_fr_FR, + de_DE: name_de_DE, + it_IT: name_it_IT, + nb_NO: name_nb_NO, + es_ES: name_es_ES, + sv_SE: name_sv_SE, + fr_CH: name_fr_CH, + de_CH: name_de_CH, +} -export const chars = { - "cs_CZ": chars_cs_CZ, - "en_UK": chars_en_UK, - "en_US": chars_en_US, - "fr_FR": chars_fr_FR, - "de_DE": chars_de_DE, - "it_IT": chars_it_IT, - "nb_NO": chars_nb_NO, - "es_ES": chars_es_ES, - "sv_SE": chars_sv_SE, - "fr_CH": chars_fr_CH, - "de_CH": chars_de_CH, -} as Record> +export const chars: Record> = { + cs_CZ: chars_cs_CZ, + en_UK: chars_en_UK, + en_US: chars_en_US, + fr_FR: chars_fr_FR, + de_DE: chars_de_DE, + it_IT: chars_it_IT, + nb_NO: chars_nb_NO, + es_ES: chars_es_ES, + sv_SE: chars_sv_SE, + fr_CH: chars_fr_CH, + de_CH: chars_de_CH, +}; diff --git a/ui/src/keyboardLayouts/cs_CZ.ts b/ui/src/keyboardLayouts/cs_CZ.ts index e889e32..ee2799c 100644 --- a/ui/src/keyboardLayouts/cs_CZ.ts +++ b/ui/src/keyboardLayouts/cs_CZ.ts @@ -1,5 +1,7 @@ import { KeyCombo } from "../keyboardLayouts" +export const name = "Czech"; + const keyTrema = { key: "Backslash" } // tréma (umlaut), two dots placed above a vowel const keyAcute = { key: "Equal" } // accent aigu (acute accent), mark ´ placed above the letter const keyHat = { key: "Digit3", shift: true, altRight: true } // accent circonflexe (accent hat), mark ^ placed above the letter diff --git a/ui/src/keyboardLayouts/de_CH.ts b/ui/src/keyboardLayouts/de_CH.ts index c822a7f..c3f1f8c 100644 --- a/ui/src/keyboardLayouts/de_CH.ts +++ b/ui/src/keyboardLayouts/de_CH.ts @@ -1,5 +1,7 @@ import { KeyCombo } from "../keyboardLayouts" +export const name = "Swiss German"; + const keyTrema = { key: "BracketRight" } // tréma (umlaut), two dots placed above a vowel const keyAcute = { key: "Minus", altRight: true } // accent aigu (acute accent), mark ´ placed above the letter const keyHat = { key: "Equal" } // accent circonflexe (accent hat), mark ^ placed above the letter diff --git a/ui/src/keyboardLayouts/de_DE.ts b/ui/src/keyboardLayouts/de_DE.ts index 08a58d3..2833909 100644 --- a/ui/src/keyboardLayouts/de_DE.ts +++ b/ui/src/keyboardLayouts/de_DE.ts @@ -1,5 +1,7 @@ import { KeyCombo } from "../keyboardLayouts" +export const name = "German"; + const keyAcute = { key: "Equal" } // accent aigu (acute accent), mark ´ placed above the letter const keyHat = { key: "Backquote" } // accent circonflexe (accent hat), mark ^ placed above the letter const keyGrave = { key: "Equal", shift: true } // accent grave, mark ` placed above the letter diff --git a/ui/src/keyboardLayouts/en_UK.ts b/ui/src/keyboardLayouts/en_UK.ts index 49d623f..ed0c8dd 100644 --- a/ui/src/keyboardLayouts/en_UK.ts +++ b/ui/src/keyboardLayouts/en_UK.ts @@ -1,5 +1,7 @@ import { KeyCombo } from "../keyboardLayouts" +export const name = "English (UK)"; + export const chars = { A: { key: "KeyA", shift: true }, B: { key: "KeyB", shift: true }, diff --git a/ui/src/keyboardLayouts/en_US.ts b/ui/src/keyboardLayouts/en_US.ts index 02dc5ee..592bf27 100644 --- a/ui/src/keyboardLayouts/en_US.ts +++ b/ui/src/keyboardLayouts/en_US.ts @@ -1,5 +1,7 @@ import { KeyCombo } from "../keyboardLayouts" +export const name = "English (US)"; + export const chars = { A: { key: "KeyA", shift: true }, B: { key: "KeyB", shift: true }, diff --git a/ui/src/keyboardLayouts/es_ES.ts b/ui/src/keyboardLayouts/es_ES.ts index 9e34bea..f979205 100644 --- a/ui/src/keyboardLayouts/es_ES.ts +++ b/ui/src/keyboardLayouts/es_ES.ts @@ -1,5 +1,7 @@ import { KeyCombo } from "../keyboardLayouts" +export const name = "Spanish"; + const keyTrema = { key: "Quote", shift: true } // tréma (umlaut), two dots placed above a vowel const keyAcute = { key: "Quote" } // accent aigu (acute accent), mark ´ placed above the letter const keyHat = { key: "BracketRight", shift: true } // accent circonflexe (accent hat), mark ^ placed above the letter diff --git a/ui/src/keyboardLayouts/fr_CH.ts b/ui/src/keyboardLayouts/fr_CH.ts index 02f2b69..e47af1d 100644 --- a/ui/src/keyboardLayouts/fr_CH.ts +++ b/ui/src/keyboardLayouts/fr_CH.ts @@ -1,6 +1,8 @@ import { KeyCombo } from "../keyboardLayouts" import { chars as chars_de_CH } from "./de_CH" +export const name = "Swiss French"; + export const chars = { ...chars_de_CH, "è": { key: "BracketLeft" }, diff --git a/ui/src/keyboardLayouts/fr_FR.ts b/ui/src/keyboardLayouts/fr_FR.ts index 755deb1..a489807 100644 --- a/ui/src/keyboardLayouts/fr_FR.ts +++ b/ui/src/keyboardLayouts/fr_FR.ts @@ -1,5 +1,7 @@ import { KeyCombo } from "../keyboardLayouts" +export const name = "French"; + const keyTrema = { key: "BracketLeft", shift: true } // tréma (umlaut), two dots placed above a vowel const keyHat = { key: "BracketLeft" } // accent circonflexe (accent hat), mark ^ placed above the letter diff --git a/ui/src/keyboardLayouts/it_IT.ts b/ui/src/keyboardLayouts/it_IT.ts index fa87e11..012734c 100644 --- a/ui/src/keyboardLayouts/it_IT.ts +++ b/ui/src/keyboardLayouts/it_IT.ts @@ -1,5 +1,7 @@ import { KeyCombo } from "../keyboardLayouts" +export const name = "Italian"; + export const chars = { A: { key: "KeyA", shift: true }, B: { key: "KeyB", shift: true }, diff --git a/ui/src/keyboardLayouts/nb_NO.ts b/ui/src/keyboardLayouts/nb_NO.ts index de18561..1507ed3 100644 --- a/ui/src/keyboardLayouts/nb_NO.ts +++ b/ui/src/keyboardLayouts/nb_NO.ts @@ -1,5 +1,7 @@ import { KeyCombo } from "../keyboardLayouts" +export const name = "Norwegian"; + const keyTrema = { key: "BracketRight" } // tréma (umlaut), two dots placed above a vowel const keyAcute = { key: "Equal", altRight: true } // accent aigu (acute accent), mark ´ placed above the letter const keyHat = { key: "BracketRight", shift: true } // accent circonflexe (accent hat), mark ^ placed above the letter diff --git a/ui/src/keyboardLayouts/sv_SE.ts b/ui/src/keyboardLayouts/sv_SE.ts index 79e9771..d2af302 100644 --- a/ui/src/keyboardLayouts/sv_SE.ts +++ b/ui/src/keyboardLayouts/sv_SE.ts @@ -1,5 +1,7 @@ import { KeyCombo } from "../keyboardLayouts" +export const name = "Swedish"; + const keyTrema = { key: "BracketRight" } // tréma (umlaut), two dots placed above a vowel const keyAcute = { key: "Equal" } // accent aigu (acute accent), mark ´ placed above the letter const keyHat = { key: "BracketRight", shift: true } // accent circonflexe (accent hat), mark ^ placed above the letter From 9ed19a451db3555b7359c84eb66f649b8b12ba2c Mon Sep 17 00:00:00 2001 From: Marc Brooks Date: Thu, 8 May 2025 16:52:55 -0500 Subject: [PATCH 23/24] Change the locale names to their native language German->Deutsch et. al. --- ui/src/keyboardLayouts/cs_CZ.ts | 2 +- ui/src/keyboardLayouts/de_CH.ts | 2 +- ui/src/keyboardLayouts/de_DE.ts | 2 +- ui/src/keyboardLayouts/es_ES.ts | 2 +- ui/src/keyboardLayouts/fr_CH.ts | 2 +- ui/src/keyboardLayouts/fr_FR.ts | 2 +- ui/src/keyboardLayouts/it_IT.ts | 2 +- ui/src/keyboardLayouts/nb_NO.ts | 2 +- ui/src/keyboardLayouts/sv_SE.ts | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ui/src/keyboardLayouts/cs_CZ.ts b/ui/src/keyboardLayouts/cs_CZ.ts index ee2799c..a289d75 100644 --- a/ui/src/keyboardLayouts/cs_CZ.ts +++ b/ui/src/keyboardLayouts/cs_CZ.ts @@ -1,6 +1,6 @@ import { KeyCombo } from "../keyboardLayouts" -export const name = "Czech"; +export const name = "Čeština"; const keyTrema = { key: "Backslash" } // tréma (umlaut), two dots placed above a vowel const keyAcute = { key: "Equal" } // accent aigu (acute accent), mark ´ placed above the letter diff --git a/ui/src/keyboardLayouts/de_CH.ts b/ui/src/keyboardLayouts/de_CH.ts index c3f1f8c..624f19a 100644 --- a/ui/src/keyboardLayouts/de_CH.ts +++ b/ui/src/keyboardLayouts/de_CH.ts @@ -1,6 +1,6 @@ import { KeyCombo } from "../keyboardLayouts" -export const name = "Swiss German"; +export const name = "Schwiizerdütsch"; const keyTrema = { key: "BracketRight" } // tréma (umlaut), two dots placed above a vowel const keyAcute = { key: "Minus", altRight: true } // accent aigu (acute accent), mark ´ placed above the letter diff --git a/ui/src/keyboardLayouts/de_DE.ts b/ui/src/keyboardLayouts/de_DE.ts index 2833909..87a8d2e 100644 --- a/ui/src/keyboardLayouts/de_DE.ts +++ b/ui/src/keyboardLayouts/de_DE.ts @@ -1,6 +1,6 @@ import { KeyCombo } from "../keyboardLayouts" -export const name = "German"; +export const name = "Deutsch"; const keyAcute = { key: "Equal" } // accent aigu (acute accent), mark ´ placed above the letter const keyHat = { key: "Backquote" } // accent circonflexe (accent hat), mark ^ placed above the letter diff --git a/ui/src/keyboardLayouts/es_ES.ts b/ui/src/keyboardLayouts/es_ES.ts index f979205..47fc230 100644 --- a/ui/src/keyboardLayouts/es_ES.ts +++ b/ui/src/keyboardLayouts/es_ES.ts @@ -1,6 +1,6 @@ import { KeyCombo } from "../keyboardLayouts" -export const name = "Spanish"; +export const name = "Español"; const keyTrema = { key: "Quote", shift: true } // tréma (umlaut), two dots placed above a vowel const keyAcute = { key: "Quote" } // accent aigu (acute accent), mark ´ placed above the letter diff --git a/ui/src/keyboardLayouts/fr_CH.ts b/ui/src/keyboardLayouts/fr_CH.ts index e47af1d..9fcc013 100644 --- a/ui/src/keyboardLayouts/fr_CH.ts +++ b/ui/src/keyboardLayouts/fr_CH.ts @@ -1,7 +1,7 @@ import { KeyCombo } from "../keyboardLayouts" import { chars as chars_de_CH } from "./de_CH" -export const name = "Swiss French"; +export const name = "Français de Suisse"; export const chars = { ...chars_de_CH, diff --git a/ui/src/keyboardLayouts/fr_FR.ts b/ui/src/keyboardLayouts/fr_FR.ts index a489807..27a03fd 100644 --- a/ui/src/keyboardLayouts/fr_FR.ts +++ b/ui/src/keyboardLayouts/fr_FR.ts @@ -1,6 +1,6 @@ import { KeyCombo } from "../keyboardLayouts" -export const name = "French"; +export const name = "Français"; const keyTrema = { key: "BracketLeft", shift: true } // tréma (umlaut), two dots placed above a vowel const keyHat = { key: "BracketLeft" } // accent circonflexe (accent hat), mark ^ placed above the letter diff --git a/ui/src/keyboardLayouts/it_IT.ts b/ui/src/keyboardLayouts/it_IT.ts index 012734c..9de61c5 100644 --- a/ui/src/keyboardLayouts/it_IT.ts +++ b/ui/src/keyboardLayouts/it_IT.ts @@ -1,6 +1,6 @@ import { KeyCombo } from "../keyboardLayouts" -export const name = "Italian"; +export const name = "Italiano"; export const chars = { A: { key: "KeyA", shift: true }, diff --git a/ui/src/keyboardLayouts/nb_NO.ts b/ui/src/keyboardLayouts/nb_NO.ts index 1507ed3..83918b2 100644 --- a/ui/src/keyboardLayouts/nb_NO.ts +++ b/ui/src/keyboardLayouts/nb_NO.ts @@ -1,6 +1,6 @@ import { KeyCombo } from "../keyboardLayouts" -export const name = "Norwegian"; +export const name = "Norsk bokmål"; const keyTrema = { key: "BracketRight" } // tréma (umlaut), two dots placed above a vowel const keyAcute = { key: "Equal", altRight: true } // accent aigu (acute accent), mark ´ placed above the letter diff --git a/ui/src/keyboardLayouts/sv_SE.ts b/ui/src/keyboardLayouts/sv_SE.ts index d2af302..f13679a 100644 --- a/ui/src/keyboardLayouts/sv_SE.ts +++ b/ui/src/keyboardLayouts/sv_SE.ts @@ -1,6 +1,6 @@ import { KeyCombo } from "../keyboardLayouts" -export const name = "Swedish"; +export const name = "Svenska"; const keyTrema = { key: "BracketRight" } // tréma (umlaut), two dots placed above a vowel const keyAcute = { key: "Equal" } // accent aigu (acute accent), mark ´ placed above the letter From 88b22a4378b35f23a2c3bf32f102d9faa3c2ac62 Mon Sep 17 00:00:00 2001 From: Daniel Lorch Date: Tue, 13 May 2025 18:55:47 +0200 Subject: [PATCH 24/24] Move hold key handling into Go backend analogous to https://www.kernel.org/doc/Documentation/usb/gadget_hid.txt --- internal/usbgadget/hid_keyboard.go | 9 ++++++++- jsonrpc.go | 2 +- ui/src/components/popovers/PasteModal.tsx | 11 ++++------- ui/src/hooks/useKeyboard.ts | 6 +++++- usb.go | 4 ++-- 5 files changed, 20 insertions(+), 12 deletions(-) diff --git a/internal/usbgadget/hid_keyboard.go b/internal/usbgadget/hid_keyboard.go index de007e4..dbebb2d 100644 --- a/internal/usbgadget/hid_keyboard.go +++ b/internal/usbgadget/hid_keyboard.go @@ -74,7 +74,7 @@ func (u *UsbGadget) keyboardWriteHidFile(data []byte) error { return nil } -func (u *UsbGadget) KeyboardReport(modifier uint8, keys []uint8) error { +func (u *UsbGadget) KeyboardReport(modifier uint8, keys []uint8, hold bool) error { u.keyboardLock.Lock() defer u.keyboardLock.Unlock() @@ -90,6 +90,13 @@ func (u *UsbGadget) KeyboardReport(modifier uint8, keys []uint8) error { return err } + if !hold { + err := u.keyboardWriteHidFile(make([]uint8, 8)) + if err != nil { + return err + } + } + u.resetUserInputTime() return nil } diff --git a/jsonrpc.go b/jsonrpc.go index 9742c99..a6453ce 100644 --- a/jsonrpc.go +++ b/jsonrpc.go @@ -1003,7 +1003,7 @@ var rpcHandlers = map[string]RPCHandler{ "getNetworkSettings": {Func: rpcGetNetworkSettings}, "setNetworkSettings": {Func: rpcSetNetworkSettings, Params: []string{"settings"}}, "renewDHCPLease": {Func: rpcRenewDHCPLease}, - "keyboardReport": {Func: rpcKeyboardReport, Params: []string{"modifier", "keys"}}, + "keyboardReport": {Func: rpcKeyboardReport, Params: []string{"modifier", "keys", "hold"}}, "absMouseReport": {Func: rpcAbsMouseReport, Params: []string{"x", "y", "buttons"}}, "relMouseReport": {Func: rpcRelMouseReport, Params: []string{"dx", "dy", "buttons"}}, "wheelReport": {Func: rpcWheelReport, Params: []string{"wheelY"}}, diff --git a/ui/src/components/popovers/PasteModal.tsx b/ui/src/components/popovers/PasteModal.tsx index 2f0662d..b8d313e 100644 --- a/ui/src/components/popovers/PasteModal.tsx +++ b/ui/src/components/popovers/PasteModal.tsx @@ -13,8 +13,8 @@ import { keys, modifiers } from "@/keyboardMappings"; import { layouts, chars } from "@/keyboardLayouts"; import notifications from "@/notifications"; -const hidKeyboardPayload = (keys: number[], modifier: number) => { - return { keys, modifier }; +const hidKeyboardPayload = (keys: number[], modifier: number, hold: boolean) => { + return { keys, modifier, hold }; }; const modifierCode = (shift?: boolean, altRight?: boolean) => { @@ -82,13 +82,10 @@ export default function PasteModal() { await new Promise((resolve, reject) => { send( "keyboardReport", - hidKeyboardPayload([kei], modz[index]), + hidKeyboardPayload([kei], modz[index], false), params => { if ("error" in params) return reject(params.error); - send("keyboardReport", hidKeyboardPayload([], 0), params => { - if ("error" in params) return reject(params.error); - resolve(); - }); + resolve(); }, ); }); diff --git a/ui/src/hooks/useKeyboard.ts b/ui/src/hooks/useKeyboard.ts index 0ce1eef..67fa1c1 100644 --- a/ui/src/hooks/useKeyboard.ts +++ b/ui/src/hooks/useKeyboard.ts @@ -4,6 +4,10 @@ import { useHidStore, useRTCStore } from "@/hooks/stores"; import { useJsonRpc } from "@/hooks/useJsonRpc"; import { keys, modifiers } from "@/keyboardMappings"; +const hidKeyboardPayload = (keys: number[], modifier: number, hold: boolean) => { + return { keys, modifier, hold }; +}; + export default function useKeyboard() { const [send] = useJsonRpc(); @@ -17,7 +21,7 @@ export default function useKeyboard() { if (rpcDataChannel?.readyState !== "open") return; const accModifier = modifiers.reduce((acc, val) => acc + val, 0); - send("keyboardReport", { keys, modifier: accModifier }); + send("keyboardReport", hidKeyboardPayload(keys, accModifier, true)); // We do this for the info bar to display the currently pressed keys for the user updateActiveKeysAndModifiers({ keys: keys, modifiers: modifiers }); diff --git a/usb.go b/usb.go index 91674c9..1ecd8d3 100644 --- a/usb.go +++ b/usb.go @@ -26,8 +26,8 @@ func initUsbGadget() { }() } -func rpcKeyboardReport(modifier uint8, keys []uint8) error { - return gadget.KeyboardReport(modifier, keys) +func rpcKeyboardReport(modifier uint8, keys []uint8, hold bool) error { + return gadget.KeyboardReport(modifier, keys, hold) } func rpcAbsMouseReport(x, y int, buttons uint8) error {