Move keyboardmapping store to stores.ts, simplified some things, updated settings.tsx to set the keyboard layout properly.

This commit is contained in:
William Johnstone 2025-02-01 18:38:40 +00:00
parent 0e855adc35
commit 7c40e2e011
No known key found for this signature in database
GPG Key ID: 89703D0D4B3BB0FE
11 changed files with 90 additions and 96 deletions

View File

@ -5,18 +5,18 @@ import {
useRTCStore,
useSettingsStore,
useVideoStore,
useKeyboardMappingsStore,
} from "@/hooks/stores";
import { useEffect, useState } from "react";
import { keyboardMappingsStore } from "@/keyboardMappings/KeyboardMappingStore";
export default function InfoBar() {
const [keys, setKeys] = useState(keyboardMappingsStore.keys);
const [modifiers, setModifiers] = useState(keyboardMappingsStore.modifiers);
const [keys, setKeys] = useState(useKeyboardMappingsStore.keys);
const [modifiers, setModifiers] = useState(useKeyboardMappingsStore.modifiers);
useEffect(() => {
const unsubscribeKeyboardStore = keyboardMappingsStore.subscribe(() => {
setKeys(keyboardMappingsStore.keys);
setModifiers(keyboardMappingsStore.modifiers);
const unsubscribeKeyboardStore = useKeyboardMappingsStore.subscribe(() => {
setKeys(useKeyboardMappingsStore.keys);
setModifiers(useKeyboardMappingsStore.modifiers);
});
return unsubscribeKeyboardStore; // Cleanup on unmount
}, []);

View File

@ -4,11 +4,9 @@ import { Button } from "@components/Button";
import Card from "@components/Card";
import { ChevronDownIcon } from "@heroicons/react/16/solid";
import "react-simple-keyboard/build/css/index.css";
import { useHidStore, useUiStore } from "@/hooks/stores";
import { useHidStore, useUiStore, useKeyboardMappingsStore } from "@/hooks/stores";
import { Transition } from "@headlessui/react";
import { cx } from "@/cva.config";
//import { keys, modifiers } from "@/keyboardMappings/KeyboardMappingStore";
import { keyboardMappingsStore } from "@/keyboardMappings/KeyboardMappingStore";
import useKeyboard from "@/hooks/useKeyboard";
import DetachIconRaw from "@/assets/detach-icon.svg";
import AttachIconRaw from "@/assets/attach-icon.svg";
@ -22,13 +20,13 @@ const AttachIcon = ({ className }: { className?: string }) => {
};
function KeyboardWrapper() {
const [keys, setKeys] = useState(keyboardMappingsStore.keys);
const [modifiers, setModifiers] = useState(keyboardMappingsStore.modifiers);
const [keys, setKeys] = useState(useKeyboardMappingsStore.keys);
const [modifiers, setModifiers] = useState(useKeyboardMappingsStore.modifiers);
useEffect(() => {
const unsubscribeKeyboardStore = keyboardMappingsStore.subscribe(() => {
setKeys(keyboardMappingsStore.keys);
setModifiers(keyboardMappingsStore.modifiers);
const unsubscribeKeyboardStore = useKeyboardMappingsStore.subscribe(() => {
setKeys(useKeyboardMappingsStore.keys);
setModifiers(useKeyboardMappingsStore.modifiers);
});
return unsubscribeKeyboardStore; // Cleanup on unmount
}, []);

View File

@ -6,8 +6,8 @@ import {
useSettingsStore,
useUiStore,
useVideoStore,
useKeyboardMappingsStore,
} from "@/hooks/stores";
import { keyboardMappingsStore } from "@/keyboardMappings/KeyboardMappingStore";
import { useResizeObserver } from "@/hooks/useResizeObserver";
import { cx } from "@/cva.config";
import VirtualKeyboard from "@components/VirtualKeyboard";
@ -18,13 +18,13 @@ import { useJsonRpc } from "@/hooks/useJsonRpc";
import { ConnectionErrorOverlay, HDMIErrorOverlay, LoadingOverlay } from "./VideoOverlay";
export default function WebRTCVideo() {
const [keys, setKeys] = useState(keyboardMappingsStore.keys);
const [modifiers, setModifiers] = useState(keyboardMappingsStore.modifiers);
const [keys, setKeys] = useState(useKeyboardMappingsStore.keys);
const [modifiers, setModifiers] = useState(useKeyboardMappingsStore.modifiers);
useEffect(() => {
const unsubscribeKeyboardStore = keyboardMappingsStore.subscribe(() => {
setKeys(keyboardMappingsStore.keys);
setModifiers(keyboardMappingsStore.modifiers);
const unsubscribeKeyboardStore = useKeyboardMappingsStore.subscribe(() => {
setKeys(useKeyboardMappingsStore.keys);
setModifiers(useKeyboardMappingsStore.modifiers);
});
return unsubscribeKeyboardStore; // Cleanup on unmount
}, []);
@ -218,12 +218,15 @@ export default function WebRTCVideo() {
const prev = useHidStore.getState();
let code = e.code;
const key = e.key;
console.log(e);
console.log(key);
// if (document.activeElement?.id !== "videoFocusTrap") {
// if (document.activeElement?.id !== "videoFocusTrap") {hH
// console.log("KEYUP: Not focusing on the video", document.activeElement);
// return;
// }
console.log(document.activeElement);
//
// console.log(document.activeElement);
setIsNumLockActive(e.getModifierState("NumLock"));
setIsCapsLockActive(e.getModifierState("CapsLock"));
@ -289,6 +292,7 @@ export default function WebRTCVideo() {
prev.activeModifiers.filter(k => k !== modifiers[e.code]),
);
console.log(e.key);
sendKeyboardEvent([...new Set(newKeys)], [...new Set(newModifiers)]);
},
[

View File

@ -3,28 +3,27 @@ import { GridCard } from "@components/Card";
import { TextAreaWithLabel } from "@components/TextArea";
import { SectionHeader } from "@components/SectionHeader";
import { useJsonRpc } from "@/hooks/useJsonRpc";
import { useHidStore, useRTCStore, useUiStore } from "@/hooks/stores";
import { useHidStore, useRTCStore, useUiStore, useKeyboardMappingsStore } from "@/hooks/stores";
import notifications from "../../notifications";
import { useCallback, useEffect, useRef, useState } from "react";
import { LuCornerDownLeft } from "react-icons/lu";
import { ExclamationCircleIcon } from "@heroicons/react/16/solid";
import { useClose } from "@headlessui/react";
import { keyboardMappingsStore } from "@/keyboardMappings/KeyboardMappingStore";
const hidKeyboardPayload = (keys: number[], modifier: number) => {
return { keys, modifier };
};
export default function PasteModal() {
const [keys, setKeys] = useState(keyboardMappingsStore.keys);
const [chars, setChars] = useState(keyboardMappingsStore.chars);
const [modifiers, setModifiers] = useState(keyboardMappingsStore.modifiers);
const [keys, setKeys] = useState(useKeyboardMappingsStore.keys);
const [chars, setChars] = useState(useKeyboardMappingsStore.chars);
const [modifiers, setModifiers] = useState(useKeyboardMappingsStore.modifiers);
useEffect(() => {
const unsubscribeKeyboardStore = keyboardMappingsStore.subscribe(() => {
setKeys(keyboardMappingsStore.keys);
setChars(keyboardMappingsStore.chars);
setModifiers(keyboardMappingsStore.modifiers);
const unsubscribeKeyboardStore = useKeyboardMappingsStore.subscribe(() => {
setKeys(useKeyboardMappingsStore.keys);
setChars(useKeyboardMappingsStore.chars);
setModifiers(useKeyboardMappingsStore.modifiers);
});
return unsubscribeKeyboardStore; // Cleanup on unmount
}, []);
@ -54,13 +53,14 @@ export default function PasteModal() {
try {
for (const char of text) {
const { key, shift, alt } = chars[char] ?? {};
const { key, shift, altLeft, altRight } = chars[char] ?? {};
if (!key) continue;
// Build the modifier bitmask
const modifier =
(shift ? modifiers["ShiftLeft"] : 0) |
(alt ? modifiers["AltLeft"] : 0);
(altLeft ? modifiers["AltLeft"] : 0) |
(altRight ? modifiers["AltRight"] : 0); // This is important for a lot of keyboard layouts, right and left alt have different functions
await new Promise<void>((resolve, reject) => {
send(

View File

@ -4,6 +4,7 @@ import {
useSettingsStore,
useUiStore,
useUpdateStore,
useKeyboardMappingsStore,
} from "@/hooks/stores";
import { Checkbox } from "@components/Checkbox";
import { Button, LinkButton } from "@components/Button";
@ -25,8 +26,6 @@ import LocalAuthPasswordDialog from "@/components/LocalAuthPasswordDialog";
import { LocalDevice } from "@routes/devices.$id";
import { useRevalidator } from "react-router-dom";
import { ShieldCheckIcon } from "@heroicons/react/20/solid";
import { keyboardMappingsStore } from "@/keyboardMappings/KeyboardMappingStore";
import { KeyboardLayout } from "@/keyboardMappings/KeyboardLayouts";
export function SettingsItem({
title,
@ -157,8 +156,7 @@ export default function SettingsSidebar() {
);
return;
}
// TODO set this to update to the actual layout chosen
keyboardMappingsStore.setLayout(KeyboardLayout.UKApple)
useKeyboardMappingsStore.setLayout(keyboardLayout)
setKeyboardLayout(keyboardLayout);
});
};
@ -294,6 +292,7 @@ export default function SettingsSidebar() {
send("getKeyboardLayout", {}, resp => {
if ("error" in resp) return;
setKeyboardLayout(String(resp.result));
useKeyboardMappingsStore.setLayout(String(resp.result))
});
send("getStreamQualityFactor", {}, resp => {
@ -545,7 +544,7 @@ export default function SettingsSidebar() {
size="SM"
label=""
// TODO figure out how to make this selector wider like the EDID one?
//fullWidth
//fullWidthƒ
value={keyboardLayout}
options={[
{ value: "uk", label: "GB" },

View File

@ -1,5 +1,6 @@
import { create } from "zustand";
import { createJSONStorage, persist } from "zustand/middleware";
import { getKeyboardMappings } from "@/keyboardMappings/KeyboardLayouts";
// Utility function to append stats to a Map
const appendStatToMap = <T extends { timestamp: number }>(
@ -528,3 +529,39 @@ export const useLocalAuthModalStore = create<LocalAuthModalState>(set => ({
setModalView: view => set({ modalView: view }),
setErrorMessage: message => set({ errorMessage: message }),
}));
class KeyboardMappingsStore {
private _layout: string = 'us';
private _subscribers: (() => void)[] = [];
public keys = getKeyboardMappings(this._layout).keys;
public chars = getKeyboardMappings(this._layout).chars;
public modifiers = getKeyboardMappings(this._layout).modifiers;
setLayout(newLayout: string) {
if (this._layout === newLayout) return;
this._layout = newLayout;
const updatedMappings = getKeyboardMappings(newLayout);
this.keys = updatedMappings.keys;
this.chars = updatedMappings.chars;
this.modifiers = updatedMappings.modifiers;
this._notifySubscribers();
}
getLayout() {
return this._layout;
}
subscribe(callback: () => void) {
this._subscribers.push(callback);
return () => {
this._subscribers = this._subscribers.filter(sub => sub !== callback); // Cleanup
};
}
private _notifySubscribers() {
this._subscribers.forEach(callback => callback());
}
}
export const useKeyboardMappingsStore = new KeyboardMappingsStore();

View File

@ -1,20 +1,15 @@
import {keysUKApple, charsUKApple, modifiersUKApple } from './layouts/uk_apple';
import {keysUS, charsUS, modifiersUS } from './layouts/us';
export enum KeyboardLayout {
US = "us",
UKApple = "uk_apple",
}
export function getKeyboardMappings(layout: KeyboardLayout) {
switch (layout) {
case KeyboardLayout.UKApple:
return {
keys: keysUKApple,
chars: charsUKApple,
modifiers: modifiersUKApple,
};
case KeyboardLayout.US:
export function getKeyboardMappings(layout: string) {
switch (layout) {
case "uk_apple":
return {
keys: keysUKApple,
chars: charsUKApple,
modifiers: modifiersUKApple,
};
case "us":
default:
return {
keys: keysUS,
@ -22,4 +17,4 @@ export function getKeyboardMappings(layout: KeyboardLayout) {
modifiers: modifiersUS,
};
}
}
}

View File

@ -1,39 +0,0 @@
import { getKeyboardMappings, KeyboardLayout } from "@/keyboardMappings/KeyboardLayouts";
// TODO Move this in with all the other stores?
class KeyboardMappingsStore {
private _layout: KeyboardLayout = KeyboardLayout.US;
private _subscribers: (() => void)[] = [];
public keys = getKeyboardMappings(this._layout).keys;
public chars = getKeyboardMappings(this._layout).chars;
public modifiers = getKeyboardMappings(this._layout).modifiers;
setLayout(newLayout: KeyboardLayout) {
if (this._layout === newLayout) return;
this._layout = newLayout;
const updatedMappings = getKeyboardMappings(newLayout);
this.keys = updatedMappings.keys;
this.chars = updatedMappings.chars;
this.modifiers = updatedMappings.modifiers;
this._notifySubscribers();
}
getLayout() {
return this._layout;
}
subscribe(callback: () => void) {
this._subscribers.push(callback);
return () => {
this._subscribers = this._subscribers.filter(sub => sub !== callback); // Cleanup
};
}
private _notifySubscribers() {
this._subscribers.forEach(callback => callback());
}
}
export const keyboardMappingsStore = new KeyboardMappingsStore();

View File

View File

@ -12,11 +12,11 @@ export const charsUKApple = {
"~": { key: "Backquote", shift: true },
"\\" : { key: "Backslash", shift: false },
"|": { key: "Backslash", shift: true },
"#": { key: "Digit3", shift: false, alt: true },
"#": { key: "Digit3", shift: false, altLeft: true },
"£": { key: "Digit3", shift: true },
"@": { key: "Digit2", shift: true },
"\"": { key: "Quote", shift: true },
} as Record<string, { key: string | number; shift: boolean; alt?: boolean; }>;
} as Record<string, { key: string | number; shift: boolean; altLeft?: boolean; altRight?: boolean; }>;
// Modifiers are typically the same between UK and US layouts
export const modifiersUKApple = {

View File

@ -200,8 +200,8 @@ export const charsUS = {
"\n": { key: "Enter", shift: false },
Enter: { key: "Enter", shift: false },
Tab: { key: "Tab", shift: false },
} as Record<string, { key: string | number; shift: boolean; alt?: boolean; }>;
} as Record<string, { key: string | number; shift: boolean; altLeft?: boolean; altRight?: boolean; }>;
export const modifiersUS = {
ControlLeft: 0x01,
ControlRight: 0x10,