diff --git a/ui/src/components/InfoBar.tsx b/ui/src/components/InfoBar.tsx index 4490afe..dbbaa40 100644 --- a/ui/src/components/InfoBar.tsx +++ b/ui/src/components/InfoBar.tsx @@ -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 }, []); diff --git a/ui/src/components/VirtualKeyboard.tsx b/ui/src/components/VirtualKeyboard.tsx index f056c11..fab78f9 100644 --- a/ui/src/components/VirtualKeyboard.tsx +++ b/ui/src/components/VirtualKeyboard.tsx @@ -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 }, []); diff --git a/ui/src/components/WebRTCVideo.tsx b/ui/src/components/WebRTCVideo.tsx index 8e6b867..61b1f9a 100644 --- a/ui/src/components/WebRTCVideo.tsx +++ b/ui/src/components/WebRTCVideo.tsx @@ -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)]); }, [ diff --git a/ui/src/components/popovers/PasteModal.tsx b/ui/src/components/popovers/PasteModal.tsx index 6b3878f..2387888 100644 --- a/ui/src/components/popovers/PasteModal.tsx +++ b/ui/src/components/popovers/PasteModal.tsx @@ -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((resolve, reject) => { send( diff --git a/ui/src/components/sidebar/settings.tsx b/ui/src/components/sidebar/settings.tsx index f7bd99a..a6c3fe1 100644 --- a/ui/src/components/sidebar/settings.tsx +++ b/ui/src/components/sidebar/settings.tsx @@ -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" }, diff --git a/ui/src/hooks/stores.ts b/ui/src/hooks/stores.ts index b4cfbec..29daa82 100644 --- a/ui/src/hooks/stores.ts +++ b/ui/src/hooks/stores.ts @@ -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 = ( @@ -528,3 +529,39 @@ export const useLocalAuthModalStore = create(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(); \ No newline at end of file diff --git a/ui/src/keyboardMappings/KeyboardLayouts.ts b/ui/src/keyboardMappings/KeyboardLayouts.ts index baadeab..b631d76 100644 --- a/ui/src/keyboardMappings/KeyboardLayouts.ts +++ b/ui/src/keyboardMappings/KeyboardLayouts.ts @@ -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, }; } - } \ No newline at end of file +} \ No newline at end of file diff --git a/ui/src/keyboardMappings/KeyboardMappingStore.ts b/ui/src/keyboardMappings/KeyboardMappingStore.ts deleted file mode 100644 index 2d41bc1..0000000 --- a/ui/src/keyboardMappings/KeyboardMappingStore.ts +++ /dev/null @@ -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(); \ No newline at end of file diff --git a/ui/src/keyboardMappings/layouts/uk.ts b/ui/src/keyboardMappings/layouts/uk.ts new file mode 100644 index 0000000..e69de29 diff --git a/ui/src/keyboardMappings/layouts/uk_apple.ts b/ui/src/keyboardMappings/layouts/uk_apple.ts index b9107ea..d257f43 100644 --- a/ui/src/keyboardMappings/layouts/uk_apple.ts +++ b/ui/src/keyboardMappings/layouts/uk_apple.ts @@ -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; +} as Record; // Modifiers are typically the same between UK and US layouts export const modifiersUKApple = { diff --git a/ui/src/keyboardMappings/layouts/us.ts b/ui/src/keyboardMappings/layouts/us.ts index 4b75b77..15a0071 100644 --- a/ui/src/keyboardMappings/layouts/us.ts +++ b/ui/src/keyboardMappings/layouts/us.ts @@ -200,8 +200,8 @@ export const charsUS = { "\n": { key: "Enter", shift: false }, Enter: { key: "Enter", shift: false }, Tab: { key: "Tab", shift: false }, -} as Record; - +} as Record; + export const modifiersUS = { ControlLeft: 0x01, ControlRight: 0x10,