diff --git a/ui/src/components/Terminal.tsx b/ui/src/components/Terminal.tsx
index 5451afe..f5d662d 100644
--- a/ui/src/components/Terminal.tsx
+++ b/ui/src/components/Terminal.tsx
@@ -67,19 +67,19 @@ function Terminal({
}) {
const enableTerminal = useUiStore(state => state.terminalType == type);
const setTerminalType = useUiStore(state => state.setTerminalType);
- const setDisableKeyboardFocusTrap = useUiStore(state => state.setDisableVideoFocusTrap);
+ const setDisableVideoFocusTrap = useUiStore(state => state.setDisableVideoFocusTrap);
const { instance, ref } = useXTerm({ options: TERMINAL_CONFIG });
useEffect(() => {
setTimeout(() => {
- setDisableKeyboardFocusTrap(enableTerminal);
+ setDisableVideoFocusTrap(enableTerminal);
}, 500);
return () => {
- setDisableKeyboardFocusTrap(false);
+ setDisableVideoFocusTrap(false);
};
- }, [ref, instance, enableTerminal, setDisableKeyboardFocusTrap, type]);
+ }, [enableTerminal, setDisableVideoFocusTrap]);
const readyState = dataChannel.readyState;
useEffect(() => {
@@ -116,7 +116,7 @@ function Terminal({
const { domEvent } = e;
if (domEvent.key === "Escape") {
setTerminalType("none");
- setDisableKeyboardFocusTrap(false);
+ setDisableVideoFocusTrap(false);
domEvent.preventDefault();
}
});
@@ -131,7 +131,7 @@ function Terminal({
onDataHandler.dispose();
onKeyHandler.dispose();
};
- }, [instance, dataChannel, readyState, setDisableKeyboardFocusTrap, setTerminalType]);
+ }, [dataChannel, instance, readyState, setDisableVideoFocusTrap, setTerminalType]);
useEffect(() => {
if (!instance) return;
@@ -158,7 +158,7 @@ function Terminal({
return () => {
window.removeEventListener("resize", handleResize);
};
- }, [ref, instance]);
+ }, [instance]);
return (
{
- return { keys, modifier };
+const hidKeyboardPayload = (modifier: number, keys: number[]) => {
+ return { modifier, keys };
};
const modifierCode = (shift?: boolean, altRight?: boolean) => {
@@ -62,49 +62,56 @@ export default function PasteModal() {
const onConfirmPaste = useCallback(async () => {
setPasteMode(false);
setDisableVideoFocusTrap(false);
+
if (rpcDataChannel?.readyState !== "open" || !TextAreaRef.current) return;
- if (!safeKeyboardLayout) return;
- if (!chars[safeKeyboardLayout]) return;
+ const keyboard: KeyboardLayout = selectedKeyboard(safeKeyboardLayout);
+ if (!keyboard) return;
+
const text = TextAreaRef.current.value;
try {
for (const char of text) {
- const { key, shift, altRight, deadKey, accentKey } = chars[safeKeyboardLayout][char]
+ const keyprops = keyboard.chars[char];
+ if (!keyprops) continue;
+
+ const { key, shift, altRight, deadKey, accentKey } = keyprops;
if (!key) continue;
- const keyz = [ keys[key] ];
- const modz = [ modifierCode(shift, altRight) ];
-
- if (deadKey) {
- keyz.push(keys["Space"]);
- modz.push(noModifier);
- }
+ // if this is an accented character, we need to send that accent FIRST
if (accentKey) {
- keyz.unshift(keys[accentKey.key])
- modz.unshift(modifierCode(accentKey.shift, accentKey.altRight))
+ await sendKeystroke({modifier: modifierCode(accentKey.shift, accentKey.altRight), keys: [ keys[accentKey.key] ] })
}
- for (const [index, kei] of keyz.entries()) {
- await new Promise((resolve, reject) => {
- send(
- "keyboardReport",
- hidKeyboardPayload([kei], modz[index]),
- params => {
- if ("error" in params) return reject(params.error);
- send("keyboardReport", hidKeyboardPayload([], 0), params => {
- if ("error" in params) return reject(params.error);
- resolve();
- });
- },
- );
- });
+ // now send the actual key
+ await sendKeystroke({ modifier: modifierCode(shift, altRight), keys: [ keys[key] ]});
+
+ // if what was requested was a dead key, we need to send an unmodified space to emit
+ // just the accent character
+ if (deadKey) {
+ await sendKeystroke({ modifier: noModifier, keys: [ keys["Space"] ] });
}
+
+ // now send a message with no keys down to "release" the keys
+ await sendKeystroke({ modifier: 0, keys: [] });
}
} catch (error) {
- console.error(error);
+ console.error("Failed to paste text:", error);
notifications.error("Failed to paste text");
}
- }, [rpcDataChannel?.readyState, send, setDisableVideoFocusTrap, setPasteMode, safeKeyboardLayout]);
+
+ async function sendKeystroke(stroke: KeyStroke) {
+ await new Promise((resolve, reject) => {
+ send(
+ "keyboardReport",
+ hidKeyboardPayload(stroke.modifier, stroke.keys),
+ params => {
+ if ("error" in params) return reject(params.error);
+ resolve();
+ }
+ );
+ });
+ }
+ }, [rpcDataChannel?.readyState, safeKeyboardLayout, send, setDisableVideoFocusTrap, setPasteMode]);
useEffect(() => {
if (TextAreaRef.current) {
@@ -154,7 +161,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[safeKeyboardLayout][char]),
+ .filter(char => !selectedKeyboard(safeKeyboardLayout).chars[char]),
),
];
@@ -175,7 +182,7 @@ export default function PasteModal() {
- Sending text using keyboard layout: {layouts[safeKeyboardLayout]}
+ Sending text using keyboard layout: {selectedKeyboard(safeKeyboardLayout).name}
diff --git a/ui/src/components/popovers/WakeOnLan/Index.tsx b/ui/src/components/popovers/WakeOnLan/Index.tsx
index f4f4951..1cf7f18 100644
--- a/ui/src/components/popovers/WakeOnLan/Index.tsx
+++ b/ui/src/components/popovers/WakeOnLan/Index.tsx
@@ -14,7 +14,7 @@ import AddDeviceForm from "./AddDeviceForm";
export default function WakeOnLanModal() {
const [storedDevices, setStoredDevices] = useState([]);
const [showAddForm, setShowAddForm] = useState(false);
- const setDisableFocusTrap = useUiStore(state => state.setDisableVideoFocusTrap);
+ const setDisableVideoFocusTrap = useUiStore(state => state.setDisableVideoFocusTrap);
const rpcDataChannel = useRTCStore(state => state.rpcDataChannel);
@@ -24,9 +24,9 @@ export default function WakeOnLanModal() {
const [addDeviceErrorMessage, setAddDeviceErrorMessage] = useState(null);
const onCancelWakeOnLanModal = useCallback(() => {
+ setDisableVideoFocusTrap(false);
close();
- setDisableFocusTrap(false);
- }, [close, setDisableFocusTrap]);
+ }, [close, setDisableVideoFocusTrap]);
const onSendMagicPacket = useCallback(
(macAddress: string) => {
@@ -43,12 +43,12 @@ export default function WakeOnLanModal() {
}
} else {
notifications.success("Magic Packet sent successfully");
- setDisableFocusTrap(false);
+ setDisableVideoFocusTrap(false);
close();
}
});
},
- [close, rpcDataChannel?.readyState, send, setDisableFocusTrap],
+ [close, rpcDataChannel?.readyState, send, setDisableVideoFocusTrap],
);
const syncStoredDevices = useCallback(() => {
@@ -78,7 +78,7 @@ export default function WakeOnLanModal() {
}
});
},
- [storedDevices, send, syncStoredDevices],
+ [send, storedDevices, syncStoredDevices],
);
const onAddDevice = useCallback(
diff --git a/ui/src/hooks/stores.ts b/ui/src/hooks/stores.ts
index 6bc7e17..b7ad53e 100644
--- a/ui/src/hooks/stores.ts
+++ b/ui/src/hooks/stores.ts
@@ -941,5 +941,5 @@ export const useMacrosStore = create((set, get) => ({
} finally {
set({ loading: false });
}
- },
+ }
}));
diff --git a/ui/src/keyboardLayouts.ts b/ui/src/keyboardLayouts.ts
index 3b835b1..4ae3ad9 100644
--- a/ui/src/keyboardLayouts.ts
+++ b/ui/src/keyboardLayouts.ts
@@ -1,45 +1,32 @@
-import { chars as chars_fr_BE, name as name_fr_BE } from "@/keyboardLayouts/fr_BE"
-import { chars as chars_cs_CZ, name as name_cs_CZ } from "@/keyboardLayouts/cs_CZ"
-import { chars as chars_en_UK, name as name_en_UK } from "@/keyboardLayouts/en_UK"
-import { chars as chars_en_US, name as name_en_US } from "@/keyboardLayouts/en_US"
-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"
+export interface KeyStroke { modifier: number; keys: number[]; }
+export interface KeyInfo { key: string | number; shift?: boolean, altRight?: boolean }
+export interface KeyCombo extends KeyInfo { deadKey?: boolean, accentKey?: KeyInfo }
+export interface KeyboardLayout { isoCode: string, name: string, chars: Record }
-interface KeyInfo { key: string | number; shift?: boolean, altRight?: boolean }
-export type KeyCombo = KeyInfo & { deadKey?: boolean, accentKey?: KeyInfo }
+// to add a new layout, create a file like the above and add it to the list
+import { cs_CZ } from "@/keyboardLayouts/cs_CZ"
+import { de_CH } from "@/keyboardLayouts/de_CH"
+import { de_DE } from "@/keyboardLayouts/de_DE"
+import { en_US } from "@/keyboardLayouts/en_US"
+import { en_UK } from "@/keyboardLayouts/en_UK"
+import { es_ES } from "@/keyboardLayouts/es_ES"
+import { fr_BE } from "@/keyboardLayouts/fr_BE"
+import { fr_CH } from "@/keyboardLayouts/fr_CH"
+import { fr_FR } from "@/keyboardLayouts/fr_FR"
+import { it_IT } from "@/keyboardLayouts/it_IT"
+import { nb_NO } from "@/keyboardLayouts/nb_NO"
+import { sv_SE } from "@/keyboardLayouts/sv_SE"
-export const layouts: Record = {
- be_FR: name_fr_BE,
- 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 keyboards: KeyboardLayout[] = [ cs_CZ, de_CH, de_DE, en_UK, en_US, es_ES, fr_BE, fr_CH, fr_FR, it_IT, nb_NO, sv_SE ];
-export const chars: Record> = {
- be_FR: chars_fr_BE,
- 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,
+export const selectedKeyboard = (isoCode: string): KeyboardLayout => {
+ // fallback to original behaviour of en-US if no isoCode given
+ return keyboards.find(keyboard => keyboard.isoCode == isoCode)
+ ?? keyboards.find(keyboard => keyboard.isoCode == "en-US")!;
};
+
+export const keyboardOptions = () => {
+ return keyboards.map((keyboard) => {
+ return { label: keyboard.name, value: keyboard.isoCode }
+ });
+}
diff --git a/ui/src/keyboardLayouts/cs_CZ.ts b/ui/src/keyboardLayouts/cs_CZ.ts
index a289d75..e4f8822 100644
--- a/ui/src/keyboardLayouts/cs_CZ.ts
+++ b/ui/src/keyboardLayouts/cs_CZ.ts
@@ -1,6 +1,6 @@
-import { KeyCombo } from "../keyboardLayouts"
+import { KeyboardLayout, KeyCombo } from "../keyboardLayouts"
-export const name = "Čeština";
+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
@@ -13,7 +13,7 @@ const keyOverdot = { key: "Digit8", shift: true, altRight: true } // overdot (do
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 = {
+const chars = {
A: { key: "KeyA", shift: true },
"Ä": { key: "KeyA", shift: true, accentKey: keyTrema },
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
@@ -242,3 +242,9 @@ export const chars = {
Enter: { key: "Enter" },
Tab: { key: "Tab" },
} as Record;
+
+export const cs_CZ: KeyboardLayout = {
+ isoCode: "cs-CZ",
+ name: name,
+ chars: chars
+};
\ No newline at end of file
diff --git a/ui/src/keyboardLayouts/de_CH.ts b/ui/src/keyboardLayouts/de_CH.ts
index 06c0619..4743bcf 100644
--- a/ui/src/keyboardLayouts/de_CH.ts
+++ b/ui/src/keyboardLayouts/de_CH.ts
@@ -1,6 +1,6 @@
-import { KeyCombo } from "../keyboardLayouts"
+import { KeyboardLayout, KeyCombo } from "../keyboardLayouts"
-export const name = "Schwiizerdütsch";
+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
@@ -8,7 +8,7 @@ const keyHat = { key: "Equal" } // accent circonflexe (accent hat), mark ^ place
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 = {
+const chars = {
A: { key: "KeyA", shift: true },
"Ä": { key: "KeyA", shift: true, accentKey: keyTrema },
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
@@ -163,3 +163,9 @@ export const chars = {
Enter: { key: "Enter" },
Tab: { key: "Tab" },
} as Record;
+
+export const de_CH: KeyboardLayout = {
+ isoCode: "de-CH",
+ name: name,
+ chars: chars
+};
\ No newline at end of file
diff --git a/ui/src/keyboardLayouts/de_DE.ts b/ui/src/keyboardLayouts/de_DE.ts
index 87a8d2e..89b7eed 100644
--- a/ui/src/keyboardLayouts/de_DE.ts
+++ b/ui/src/keyboardLayouts/de_DE.ts
@@ -1,12 +1,12 @@
-import { KeyCombo } from "../keyboardLayouts"
+import { KeyboardLayout, KeyCombo } from "../keyboardLayouts"
-export const name = "Deutsch";
+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
const keyGrave = { key: "Equal", shift: true } // accent grave, mark ` placed above the letter
-export const chars = {
+const chars = {
A: { key: "KeyA", shift: true },
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
"Â": { key: "KeyA", shift: true, accentKey: keyHat },
@@ -150,3 +150,9 @@ export const chars = {
Enter: { key: "Enter" },
Tab: { key: "Tab" },
} as Record;
+
+export const de_DE: KeyboardLayout = {
+ isoCode: "de-DE",
+ name: name,
+ chars: chars
+};
\ No newline at end of file
diff --git a/ui/src/keyboardLayouts/en_UK.ts b/ui/src/keyboardLayouts/en_UK.ts
index ed0c8dd..a5ef779 100644
--- a/ui/src/keyboardLayouts/en_UK.ts
+++ b/ui/src/keyboardLayouts/en_UK.ts
@@ -1,8 +1,8 @@
-import { KeyCombo } from "../keyboardLayouts"
+import { KeyboardLayout, KeyCombo } from "../keyboardLayouts"
-export const name = "English (UK)";
+const name = "English (UK)";
-export const chars = {
+const chars = {
A: { key: "KeyA", shift: true },
B: { key: "KeyB", shift: true },
C: { key: "KeyC", shift: true },
@@ -105,3 +105,9 @@ export const chars = {
Enter: { key: "Enter" },
Tab: { key: "Tab" },
} as Record
+
+export const en_UK: KeyboardLayout = {
+ isoCode: "en-UK",
+ name: name,
+ chars: chars
+};
\ No newline at end of file
diff --git a/ui/src/keyboardLayouts/en_US.ts b/ui/src/keyboardLayouts/en_US.ts
index 592bf27..cd7aaf6 100644
--- a/ui/src/keyboardLayouts/en_US.ts
+++ b/ui/src/keyboardLayouts/en_US.ts
@@ -1,8 +1,8 @@
-import { KeyCombo } from "../keyboardLayouts"
+import { KeyboardLayout, KeyCombo } from "../keyboardLayouts"
-export const name = "English (US)";
+const name = "English (US)";
-export const chars = {
+const chars = {
A: { key: "KeyA", shift: true },
B: { key: "KeyB", shift: true },
C: { key: "KeyC", shift: true },
@@ -111,3 +111,9 @@ export const chars = {
Insert: { key: "Insert", shift: false },
Delete: { key: "Delete", shift: false },
} as Record
+
+export const en_US: KeyboardLayout = {
+ isoCode: "en-US",
+ name: name,
+ chars: chars
+};
\ No newline at end of file
diff --git a/ui/src/keyboardLayouts/es_ES.ts b/ui/src/keyboardLayouts/es_ES.ts
index 47fc230..9eb1d6a 100644
--- a/ui/src/keyboardLayouts/es_ES.ts
+++ b/ui/src/keyboardLayouts/es_ES.ts
@@ -1,6 +1,6 @@
-import { KeyCombo } from "../keyboardLayouts"
+import { KeyboardLayout, KeyCombo } from "../keyboardLayouts"
-export const name = "Español";
+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
@@ -8,7 +8,7 @@ const keyHat = { key: "BracketRight", shift: true } // accent circonflexe (accen
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 = {
+const chars = {
A: { key: "KeyA", shift: true },
"Ä": { key: "KeyA", shift: true, accentKey: keyTrema },
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
@@ -166,3 +166,9 @@ export const chars = {
Enter: { key: "Enter" },
Tab: { key: "Tab" },
} as Record;
+
+export const es_ES: KeyboardLayout = {
+ isoCode: "es-ES",
+ name: name,
+ chars: chars
+};
\ No newline at end of file
diff --git a/ui/src/keyboardLayouts/fr_BE.ts b/ui/src/keyboardLayouts/fr_BE.ts
index 2b8b34c..bd417e0 100644
--- a/ui/src/keyboardLayouts/fr_BE.ts
+++ b/ui/src/keyboardLayouts/fr_BE.ts
@@ -1,6 +1,6 @@
-import { KeyCombo } from "../keyboardLayouts"
+import { KeyboardLayout, KeyCombo } from "../keyboardLayouts"
-export const name = "Belgisch Nederlands";
+const name = "Belgisch Nederlands";
const keyTrema = { key: "BracketLeft", shift: true } // tréma (umlaut), two dots placed above a vowel
const keyHat = { key: "BracketLeft" } // accent circonflexe (accent hat), mark ^ placed above the letter
@@ -8,7 +8,7 @@ const keyAcute = { key: "Semicolon", altRight: true } // accent aigu (acute acce
const keyGrave = { key: "Quote", shift: true } // accent grave, mark ` placed above the letter
const keyTilde = { key: "Slash", altRight: true } // tilde, mark ~ placed above the letter
-export const chars = {
+const chars = {
A: { key: "KeyQ", shift: true },
"Ä": { key: "KeyQ", shift: true, accentKey: keyTrema },
"Â": { key: "KeyQ", shift: true, accentKey: keyHat },
@@ -165,3 +165,9 @@ export const chars = {
Enter: { key: "Enter" },
Tab: { key: "Tab" },
} as Record;
+
+export const fr_BE: KeyboardLayout = {
+ isoCode: "fr-BE",
+ name: name,
+ chars: chars
+};
\ No newline at end of file
diff --git a/ui/src/keyboardLayouts/fr_CH.ts b/ui/src/keyboardLayouts/fr_CH.ts
index cf1d3df..0ba8cb4 100644
--- a/ui/src/keyboardLayouts/fr_CH.ts
+++ b/ui/src/keyboardLayouts/fr_CH.ts
@@ -1,11 +1,11 @@
-import { KeyCombo } from "../keyboardLayouts"
+import { KeyboardLayout, KeyCombo } from "../keyboardLayouts"
-import { chars as chars_de_CH } from "./de_CH"
+import { de_CH } from "./de_CH"
-export const name = "Français de Suisse";
+const name = "Français de Suisse";
-export const chars = {
- ...chars_de_CH,
+const chars = {
+ ...de_CH.chars,
"è": { key: "BracketLeft" },
"ü": { key: "BracketLeft", shift: true },
"é": { key: "Semicolon" },
@@ -13,3 +13,9 @@ export const chars = {
"à": { key: "Quote" },
"ä": { key: "Quote", shift: true },
} as Record;
+
+export const fr_CH: KeyboardLayout = {
+ isoCode: "fr-CH",
+ name: name,
+ chars: chars
+};
diff --git a/ui/src/keyboardLayouts/fr_FR.ts b/ui/src/keyboardLayouts/fr_FR.ts
index 27a03fd..29d5104 100644
--- a/ui/src/keyboardLayouts/fr_FR.ts
+++ b/ui/src/keyboardLayouts/fr_FR.ts
@@ -1,11 +1,11 @@
-import { KeyCombo } from "../keyboardLayouts"
+import { KeyboardLayout, KeyCombo } from "../keyboardLayouts"
-export const name = "Français";
+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
-export const chars = {
+const chars = {
A: { key: "KeyQ", shift: true },
"Ä": { key: "KeyQ", shift: true, accentKey: keyTrema },
"Â": { key: "KeyQ", shift: true, accentKey: keyHat },
@@ -137,3 +137,9 @@ export const chars = {
Enter: { key: "Enter" },
Tab: { key: "Tab" },
} as Record;
+
+export const fr_FR: KeyboardLayout = {
+ isoCode: "fr-FR",
+ name: name,
+ chars: chars
+};
\ No newline at end of file
diff --git a/ui/src/keyboardLayouts/it_IT.ts b/ui/src/keyboardLayouts/it_IT.ts
index 9de61c5..0ff6e24 100644
--- a/ui/src/keyboardLayouts/it_IT.ts
+++ b/ui/src/keyboardLayouts/it_IT.ts
@@ -1,8 +1,8 @@
-import { KeyCombo } from "../keyboardLayouts"
+import { KeyboardLayout, KeyCombo } from "../keyboardLayouts"
-export const name = "Italiano";
+const name = "Italiano";
-export const chars = {
+const chars = {
A: { key: "KeyA", shift: true },
B: { key: "KeyB", shift: true },
C: { key: "KeyC", shift: true },
@@ -111,3 +111,9 @@ export const chars = {
Enter: { key: "Enter" },
Tab: { key: "Tab" },
} as Record;
+
+export const it_IT: KeyboardLayout = {
+ isoCode: "it-IT",
+ name: name,
+ chars: chars
+};
\ No newline at end of file
diff --git a/ui/src/keyboardLayouts/nb_NO.ts b/ui/src/keyboardLayouts/nb_NO.ts
index 83918b2..4dae9c8 100644
--- a/ui/src/keyboardLayouts/nb_NO.ts
+++ b/ui/src/keyboardLayouts/nb_NO.ts
@@ -1,6 +1,6 @@
-import { KeyCombo } from "../keyboardLayouts"
+import { KeyboardLayout, KeyCombo } from "../keyboardLayouts"
-export const name = "Norsk bokmål";
+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
@@ -8,7 +8,7 @@ const keyHat = { key: "BracketRight", shift: true } // accent circonflexe (accen
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 = {
+const chars = {
A: { key: "KeyA", shift: true },
"Ä": { key: "KeyA", shift: true, accentKey: keyTrema },
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
@@ -165,3 +165,9 @@ export const chars = {
Enter: { key: "Enter" },
Tab: { key: "Tab" },
} as Record;
+
+export const nb_NO: KeyboardLayout = {
+ isoCode: "nb-NO",
+ name: name,
+ chars: chars
+};
\ No newline at end of file
diff --git a/ui/src/keyboardLayouts/sv_SE.ts b/ui/src/keyboardLayouts/sv_SE.ts
index 75197cb..fbde3d0 100644
--- a/ui/src/keyboardLayouts/sv_SE.ts
+++ b/ui/src/keyboardLayouts/sv_SE.ts
@@ -1,6 +1,6 @@
-import { KeyCombo } from "../keyboardLayouts"
+import { KeyboardLayout, KeyCombo } from "../keyboardLayouts"
-export const name = "Svenska";
+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
@@ -8,7 +8,7 @@ const keyHat = { key: "BracketRight", shift: true } // accent circonflexe (accen
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 = {
+const chars = {
A: { key: "KeyA", shift: true },
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
"Â": { key: "KeyA", shift: true, accentKey: keyHat },
@@ -162,3 +162,9 @@ export const chars = {
Enter: { key: "Enter" },
Tab: { key: "Tab" },
} as Record;
+
+export const sv_SE: KeyboardLayout = {
+ isoCode: "sv-SE",
+ name: name,
+ chars: chars
+};
\ No newline at end of file
diff --git a/ui/src/keyboardMappings.ts b/ui/src/keyboardMappings.ts
index 891b96e..bb24fbb 100644
--- a/ui/src/keyboardMappings.ts
+++ b/ui/src/keyboardMappings.ts
@@ -1,17 +1,19 @@
// Key codes and modifiers correspond to definitions in the
// [Linux USB HID gadget driver](https://www.kernel.org/doc/Documentation/usb/gadget_hid.txt)
+// [Section 10. Keyboard/Keypad Page 0x07](https://usb.org/sites/default/files/hut1_21.pdf)
export const keys = {
ArrowDown: 0x51,
ArrowLeft: 0x50,
ArrowRight: 0x4f,
ArrowUp: 0x52,
- Backquote: 0x35,
+ Backquote: 0x35, // aka Grave
Backslash: 0x31,
Backspace: 0x2a,
- BracketLeft: 0x2f,
- BracketRight: 0x30,
+ BracketLeft: 0x2f, // aka LeftBrace
+ BracketRight: 0x30, // aka RightBrace
CapsLock: 0x39,
Comma: 0x36,
+ Compose: 0x65,
ContextMenu: 0,
Delete: 0x4c,
Digit0: 0x27,
@@ -40,10 +42,21 @@ export const keys = {
F10: 0x43,
F11: 0x44,
F12: 0x45,
- F13: 0x68,
+ F14: 0x69,
+ F15: 0x6a,
+ F16: 0x6b,
+ F17: 0x6c,
+ F18: 0x6d,
+ F19: 0x6e,
+ F20: 0x6f,
+ F21: 0x70,
+ F22: 0x71,
+ F23: 0x72,
+ F24: 0x73,
Home: 0x4a,
+ HashTilde: 0x32, // non-US # and ~
Insert: 0x49,
- IntlBackslash: 0x64,
+ IntlBackslash: 0x64, // non-US \ and |
KeyA: 0x04,
KeyB: 0x05,
KeyC: 0x06,
@@ -72,30 +85,35 @@ export const keys = {
KeyZ: 0x1d,
KeypadExclamation: 0xcf,
Minus: 0x2d,
- NumLock: 0x53,
- Numpad0: 0x62,
- Numpad1: 0x59,
- Numpad2: 0x5a,
- Numpad3: 0x5b,
- Numpad4: 0x5c,
+ None: 0x00,
+ NumLock: 0x53, // and Clear
+ Numpad0: 0x62, // and Insert
+ Numpad1: 0x59, // and End
+ Numpad2: 0x5a, // and Down Arrow
+ Numpad3: 0x5b, // and Page Down
+ Numpad4: 0x5c, // and Left Arrow
Numpad5: 0x5d,
- Numpad6: 0x5e,
- Numpad7: 0x5f,
- Numpad8: 0x60,
- Numpad9: 0x61,
+ Numpad6: 0x5e, // and Right Arrow
+ Numpad7: 0x5f, // and Home
+ Numpad8: 0x60, // and Up Arrow
+ Numpad9: 0x61, // and Page Up
NumpadAdd: 0x57,
+ NumpadComma: 0x85,
+ NumpadDecimal: 0x63,
NumpadDivide: 0x54,
NumpadEnter: 0x58,
NumpadEqual: 0x67,
+ NumpadLeftParen: 0xb6,
NumpadMultiply: 0x55,
+ NumpadRightParen: 0xb7,
NumpadSubtract: 0x56,
- NumpadDecimal: 0x63,
PageDown: 0x4e,
PageUp: 0x4b,
Period: 0x37,
PrintScreen: 0x46,
Pause: 0x48,
- Quote: 0x34,
+ Power: 0x66,
+ Quote: 0x34, // aka Single Quote or Apostrophe
ScrollLock: 0x47,
Semicolon: 0x33,
Slash: 0x38,
diff --git a/ui/src/routes/devices.$id.settings.keyboard.tsx b/ui/src/routes/devices.$id.settings.keyboard.tsx
index 12ed6f2..d9b0d54 100644
--- a/ui/src/routes/devices.$id.settings.keyboard.tsx
+++ b/ui/src/routes/devices.$id.settings.keyboard.tsx
@@ -4,7 +4,7 @@ import { KeyboardLedSync, useSettingsStore } from "@/hooks/stores";
import { useJsonRpc } from "@/hooks/useJsonRpc";
import notifications from "@/notifications";
import { SettingsPageHeader } from "@components/SettingsPageheader";
-import { layouts } from "@/keyboardLayouts";
+import { keyboardOptions } from "@/keyboardLayouts";
import { Checkbox } from "@/components/Checkbox";
import { SelectMenuBasic } from "../components/SelectMenuBasic";
@@ -32,7 +32,7 @@ export default function SettingsKeyboardRoute() {
return "en-US";
}, [keyboardLayout]);
- const layoutOptions = Object.entries(layouts).map(([code, language]) => { return { value: code, label: language } })
+ const layoutOptions = keyboardOptions();
const ledSyncOptions = [
{ value: "auto", label: "Automatic" },
{ value: "browser", label: "Browser Only" },
diff --git a/ui/src/routes/devices.$id.settings.tsx b/ui/src/routes/devices.$id.settings.tsx
index 1e888f6..b729bf0 100644
--- a/ui/src/routes/devices.$id.settings.tsx
+++ b/ui/src/routes/devices.$id.settings.tsx
@@ -79,7 +79,7 @@ export default function SettingsRoute() {
return () => {
setDisableVideoFocusTrap(false);
};
- }, [setDisableVideoFocusTrap, sendKeyboardEvent]);
+ }, [sendKeyboardEvent, setDisableVideoFocusTrap]);
return (
diff --git a/ui/src/routes/devices.$id.tsx b/ui/src/routes/devices.$id.tsx
index 8cdb5b3..1785bcd 100644
--- a/ui/src/routes/devices.$id.tsx
+++ b/ui/src/routes/devices.$id.tsx
@@ -707,7 +707,7 @@ export default function KvmIdRoute() {
}, [diskChannel, file]);
// System update
- const disableKeyboardFocusTrap = useUiStore(state => state.disableVideoFocusTrap);
+ const disableVideoFocusTrap = useUiStore(state => state.disableVideoFocusTrap);
const [kvmTerminal, setKvmTerminal] = useState
(null);
const [serialConsole, setSerialConsole] = useState(null);
@@ -805,7 +805,7 @@ export default function KvmIdRoute() {
)}