Files
kvm/ui/src/components/VirtualKeyboard.tsx
2026-05-16 16:40:04 +08:00

932 lines
39 KiB
TypeScript

import { Switch , Button as AntdButton } from "antd";
import { LockClosedIcon } from "@heroicons/react/16/solid";
import { useShallow } from "zustand/react/shallow";
import { AnimatePresence, motion } from "framer-motion";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import Keyboard from "react-simple-keyboard";
import { useReactAt } from "i18n-auto-extractor/react";
import { isMobile } from "react-device-detect";
import Card from "@components/Card";
import "react-simple-keyboard/build/css/index.css";
import DetachIconRaw from "@/assets/detach-icon.svg";
import { cx } from "@/cva.config";
import { useHidStore, useSettingsStore, useUiStore } from "@/hooks/stores";
import useKeyboard from "@/hooks/useKeyboard";
import useKeyboardLayout from "@/hooks/useKeyboardLayout";
import { keyDisplayMap2, keys, modifiers, sKeyDisplayMap, latchingKeys } from "@/keyboardMappings";
import { dark_bg2_style} from "@/layout/theme_color";
import GoBottomSvg from "@/assets/second/gobottom.svg?react";
export const DetachIcon = ({ className }: { className?: string }) => {
return <img src={DetachIconRaw} alt="Detach Icon" className={className} />;
};
function KeyboardWrapper() {
const { $at } = useReactAt();
const [layoutName, setLayoutName] = useState("default");
const keyboardRef = useRef<HTMLDivElement>(null);
const showAttachedVirtualKeyboard = useUiStore(
state => state.isAttachedVirtualKeyboardVisible,
);
const { sendKeyboardEvent, resetKeyboardState } = useKeyboard();
const { selectedKeyboard } = useKeyboardLayout();
const keyDisplayMap = useMemo(() => {
return selectedKeyboard.keyDisplayMap;
}, [selectedKeyboard]);
const virtualKeyboardLayout = useMemo(() => {
return selectedKeyboard.virtualKeyboard;
}, [selectedKeyboard]);
const [isDragging, setIsDragging] = useState(false);
const [position, setPosition] = useState({ x: 0, y: 0 });
const [newPosition, setNewPosition] = useState({ x: 0, y: 0 });
// State for locked modifier keys
const [lockedModifiers, setLockedModifiers] = useState({
ctrl: false,
alt: false,
meta: false,
shift: false,
});
// Toggle for modifier key behavior: true = lock mode, false = direct trigger mode
const [modifierLockMode, setModifierLockMode] = useState(isMobile);
// Clear locked modifiers when switching to direct mode
useEffect(() => {
if (!modifierLockMode) {
setLockedModifiers({
ctrl: false,
alt: false,
meta: false,
shift: false,
});
setLayoutName("default");
}
}, [modifierLockMode]);
// Force sticky mode on mobile
useEffect(() => {
if (isMobile) {
setModifierLockMode(true);
}
}, [isMobile]);
const [useNum, setUseNum] = useState(false);
const [useFn, setUseFn] = useState(false);
const [stickyModifiers, setStickyModifiers] = useState<string[]>([]);
const modifierButtonTheme = useMemo(
() => [{ class: "hg-mod-active", buttons: stickyModifiers.join(" ") }],
[stickyModifiers],
);
const isCapsLockActive = useHidStore(useShallow(state => state.keyboardLedState?.caps_lock));
// HID related states
const keyboardLedStateSyncAvailable = useHidStore(state => state.keyboardLedStateSyncAvailable);
const keyboardLedSync = useSettingsStore(state => state.keyboardLedSync);
const isKeyboardLedManagedByHost = useMemo(() =>
keyboardLedSync !== "browser" && keyboardLedStateSyncAvailable,
[keyboardLedSync, keyboardLedStateSyncAvailable],
);
const setIsCapsLockActive = useHidStore(state => state.setIsCapsLockActive);
const startDrag = useCallback((e: MouseEvent | TouchEvent) => {
if (!keyboardRef.current) return;
if (e instanceof TouchEvent && e.touches.length > 1) return;
setIsDragging(true);
const clientX = e instanceof TouchEvent ? e.touches[0].clientX : e.clientX;
const clientY = e instanceof TouchEvent ? e.touches[0].clientY : e.clientY;
const rect = keyboardRef.current.getBoundingClientRect();
setPosition({
x: clientX - rect.left,
y: clientY - rect.top,
});
}, []);
const onDrag = useCallback(
(e: MouseEvent | TouchEvent) => {
if (!keyboardRef.current) return;
if (isDragging) {
const clientX = e instanceof TouchEvent ? e.touches[0].clientX : e.clientX;
const clientY = e instanceof TouchEvent ? e.touches[0].clientY : e.clientY;
const newX = clientX - position.x;
const newY = clientY - position.y;
const rect = keyboardRef.current.getBoundingClientRect();
const maxX = window.innerWidth - rect.width;
const maxY = window.innerHeight - rect.height;
setNewPosition({
x: Math.min(maxX, Math.max(0, newX)),
y: Math.min(maxY, Math.max(0, newY)),
});
}
},
[isDragging, position.x, position.y],
);
const endDrag = useCallback(() => {
setIsDragging(false);
}, []);
useEffect(() => {
const handle = keyboardRef.current;
if (handle) {
handle.addEventListener("touchstart", startDrag);
handle.addEventListener("mousedown", startDrag);
}
document.addEventListener("mouseup", endDrag);
document.addEventListener("touchend", endDrag);
document.addEventListener("mousemove", onDrag);
document.addEventListener("touchmove", onDrag);
return () => {
if (handle) {
handle.removeEventListener("touchstart", startDrag);
handle.removeEventListener("mousedown", startDrag);
}
document.removeEventListener("mouseup", endDrag);
document.removeEventListener("touchend", endDrag);
document.removeEventListener("mousemove", onDrag);
document.removeEventListener("touchmove", onDrag);
};
}, [endDrag, onDrag, startDrag]);
const onKeyDown = useCallback(
(key: string, e?: MouseEvent) => {
if (e) {
e.preventDefault();
}
const isKeyShift = key === "{shift}" || key === "ShiftLeft" || key === "ShiftRight";
const isKeyCaps = key === "CapsLock";
const cleanKey = key.replace(/[()]/g, "");
const keyHasShiftModifier = key.includes("(");
// Check if this is a modifier key press
const isModifierKey = key === "ControlLeft" || key === "AltLeft" || key === "MetaLeft" ||
key === "AltRight" || key === "MetaRight" || isKeyShift;
// Handle toggle of layout for shift or caps lock
const toggleLayout = () => {
setLayoutName(prevLayout => (prevLayout === "default" ? "shift" : "default"));
};
// Handle modifier key press
if (key === "ControlLeft") {
if (modifierLockMode) {
// Lock mode: toggle lock state
setLockedModifiers(prev => ({ ...prev, ctrl: !prev.ctrl }));
} else {
// Direct trigger mode: send key press and release immediately
sendKeyboardEvent([], [modifiers["ControlLeft"]]);
setTimeout(resetKeyboardState, 100);
}
return;
}
if (key === "AltLeft" || key === "AltRight") {
if (modifierLockMode) {
setLockedModifiers(prev => ({ ...prev, alt: !prev.alt }));
} else {
sendKeyboardEvent([], [modifiers[key]]);
setTimeout(resetKeyboardState, 100);
}
return;
}
if (key === "MetaLeft" || key === "MetaRight") {
if (modifierLockMode) {
setLockedModifiers(prev => ({ ...prev, meta: !prev.meta }));
} else {
sendKeyboardEvent([], [modifiers[key]]);
setTimeout(resetKeyboardState, 100);
}
return;
}
if (isKeyShift) {
if (modifierLockMode) {
setLockedModifiers(prev => ({ ...prev, shift: !prev.shift }));
if (lockedModifiers.shift) {
// If unlocking shift, return to default layout
setLayoutName("default");
} else {
// If locking shift, switch to shift layout
toggleLayout();
}
} else {
sendKeyboardEvent([], [modifiers["ShiftLeft"]]);
setTimeout(resetKeyboardState, 100);
}
return;
}
if (key === "CtrlAltDelete") {
sendKeyboardEvent(
[keys["Delete"]],
[modifiers["ControlLeft"], modifiers["AltLeft"]],
);
setTimeout(resetKeyboardState, 100);
return;
}
if (key === "AltMetaEscape") {
sendKeyboardEvent(
[keys["Escape"]],
[modifiers["MetaLeft"], modifiers["AltLeft"]],
);
setTimeout(resetKeyboardState, 100);
return;
}
if (key === "CtrlAltBackspace") {
sendKeyboardEvent(
[keys["Backspace"]],
[modifiers["ControlLeft"], modifiers["AltLeft"]],
);
setTimeout(resetKeyboardState, 100);
return;
}
if (isKeyCaps) {
toggleLayout();
if (isCapsLockActive) {
if (!isKeyboardLedManagedByHost) {
setIsCapsLockActive(false);
}
sendKeyboardEvent([keys["CapsLock"]], []);
setTimeout(resetKeyboardState, 100);
return;
}
}
// Handle caps lock state change
if (isKeyCaps && !isKeyboardLedManagedByHost) {
setIsCapsLockActive(!isCapsLockActive);
}
// Collect new active keys and modifiers
const newKeys = keys[cleanKey] ? [keys[cleanKey]] : [];
const newModifiers: number[] = [];
// Add locked modifiers
if (lockedModifiers.ctrl) {
newModifiers.push(modifiers["ControlLeft"]);
}
if (lockedModifiers.alt) {
newModifiers.push(modifiers["AltLeft"]);
}
if (lockedModifiers.meta) {
newModifiers.push(modifiers["MetaLeft"]);
}
if (lockedModifiers.shift && !isCapsLockActive) {
newModifiers.push(modifiers["ShiftLeft"]);
}
// Add shift modifier for keys with parentheses (if not caps lock and shift not locked)
if (keyHasShiftModifier && !isCapsLockActive && !lockedModifiers.shift) {
newModifiers.push(modifiers["ShiftLeft"]);
}
// Update current keys and modifiers
sendKeyboardEvent(newKeys, newModifiers);
// If shift was used as a modifier and caps lock is not active and shift is not locked, revert to default layout
if (keyHasShiftModifier && !isCapsLockActive && !lockedModifiers.shift) {
setLayoutName("default");
}
// Auto-unlock modifiers after regular key press (not for combination keys)
if (!isModifierKey && newKeys.length > 0) {
setLockedModifiers({
ctrl: false,
alt: false,
meta: false,
shift: false,
});
setLayoutName("default");
}
setTimeout(resetKeyboardState, 100);
},
[isCapsLockActive, isKeyboardLedManagedByHost, sendKeyboardEvent, resetKeyboardState, setIsCapsLockActive, lockedModifiers, modifierLockMode],
);
const virtualKeyboard = useHidStore(state => state.isVirtualKeyboardEnabled);
const setVirtualKeyboard = useHidStore(state => state.setVirtualKeyboardEnabled);
const modifierLockButtons = [
lockedModifiers.ctrl ? "ControlLeft" : "",
lockedModifiers.alt ? "AltLeft AltRight" : "",
lockedModifiers.meta ? "MetaLeft MetaRight" : "",
lockedModifiers.shift ? "ShiftLeft ShiftRight" : "",
].filter(Boolean).join(" ").trim();
return (
<div
className="transition-all duration-200 ease-in-out"
style={{
marginBottom: virtualKeyboard ? "0px" : `-${500}px`,
}}
>
<AnimatePresence>
{virtualKeyboard && (
<motion.div
initial={{ opacity: 0, y: "100%" }}
animate={{ opacity: 1, y: "0%" }}
exit={{ opacity: 0, y: "100%" }}
transition={{
duration: 0.2,
ease: "easeInOut",
}}
>
<div
className={cx(
!showAttachedVirtualKeyboard
? "fixed left-0 top-0 z-50 select-none"
: "relative",
)}
ref={keyboardRef}
style={{
...(!showAttachedVirtualKeyboard
? { transform: `translate(${newPosition.x}px, ${newPosition.y}px)` }
: {}),
}}
>
<Card
className={cx("overflow-hidden", {
"rounded-none": showAttachedVirtualKeyboard,
})}
>
{isMobile ?
<>
<div
className={`flex items-center justify-center w-full border-b border-b-slate-800/30 px-1 py-0 dark:border-b-slate-300/20 ${dark_bg2_style}`}>
<style>
{`
.simple-keyboard-topcontrol .hg-button.hg-mod-active {
background-color: rgba(22,152,217,0.15) !important;
border-color: rgba(22,152,217,0.6) !important;
}
.simple-keyboard-main .hg-button.modifier-locked,
.simple-keyboard-topcontrol .hg-button.modifier-locked,
.simple-keyboard-main2 .hg-button.modifier-locked {
background-color: rgba(22,152,217,0.3) !important;
border-color: rgba(22,152,217,0.8) !important;
color: rgba(22,152,217,1) !important;
}
html.dark .simple-keyboard-main .hg-button.modifier-locked,
html.dark .simple-keyboard-topcontrol .hg-button.modifier-locked,
html.dark .simple-keyboard-main2 .hg-button.modifier-locked {
background-color: rgba(22,152,217,0.5) !important;
color: white !important;
}
html.dark .simple-keyboard-topcontrol,
html.dark .simple-keyboard-main2 {
background-color: transparent !important;
}
html.dark .simple-keyboard-topcontrol .hg-button,
html.dark .simple-keyboard-main2 .hg-button {
background-color: rgba(26,26,26,1);
color: white;
}
/* Add click animation */
.hg-button:active {
background-color: rgba(0,0,0,0.2) !important;
}
html.dark .hg-button:active {
background-color: rgba(255,255,255,0.2) !important;
}
html.dark .simple-keyboard-topcontrol .hg-button:active,
html.dark .simple-keyboard-main2 .hg-button:active {
background-color: rgba(255,255,255,0.2) !important;
}
.hg-button {
transition: background-color 0.02s ease-in-out;
}
`}
</style>
<Keyboard
baseClass="simple-keyboard-topcontrol"
layoutName={layoutName}
buttonTheme={[
...(stickyModifiers.length ? [{ class: "hg-mod-active", buttons: stickyModifiers.join(" ") }] : []),
...(modifierLockMode && modifierLockButtons ? [{ class: "modifier-locked", buttons: modifierLockButtons }] : [])
]}
onKeyPress={(key: string) => {
if (key === "Back") {
setVirtualKeyboard(false);
return;
}
if (key === "ShiftLeft" || key === "ControlLeft" || key === "AltLeft" || key === "MetaLeft") {
onKeyDown(key);
return;
}
onKeyDown(key);
}}
display={sKeyDisplayMap}
layout={{
default: [
"Escape Tab MetaLeft PageUp ArrowUp PageDown Delete",
"ShiftLeft ControlLeft AltLeft ArrowLeft ArrowDown ArrowRight Back",
],
shift: [
"Escape Tab MetaLeft PageUp ArrowUp PageDown Delete",
"ShiftLeft ControlLeft AltLeft ArrowLeft ArrowDown ArrowRight Back",
],
}}
disableButtonHold={true}
syncInstanceInputs={true}
debug={false}
/>
</div>
<div
className={`flex items-center justify-center flex-col border-b border-b-slate-800/30 px-1 py-0 dark:border-b-slate-300/20 ${dark_bg2_style}`}>
{
useNum?
<>
<div className={"flex flex-row w-full justify-between items-center h-1/4"}>
<Keyboard
baseClass="simple-keyboard-main2"
layoutName={layoutName}
onKeyPress={onKeyDown}
display={keyDisplayMap2}
buttonTheme={
modifierLockMode && modifierLockButtons
? [{ class: "modifier-locked", buttons: modifierLockButtons }]
: []
}
layout={{
default: [
useFn
? "F1 F2 F3 F4 F5 F6 F7 F8 F9 F10"
: "Digit1 Digit2 Digit3 Digit4 Digit5 Digit6 Digit7 Digit8 Digit9 Digit0",
],
shift: [
useFn
? "F1 F2 F3 F4 F5 F6 F7 F8 F9 F10"
: "(Digit1) (Digit2) (Digit3) (Digit4) (Digit5) (Digit6) (Digit7) (Digit8) (Digit9) (Digit0)",
],
}}
disableButtonHold={true}
syncInstanceInputs={true}
debug={false}
/>
</div>
<div className={"flex flex-row w-full justify-between items-center h-1/4 px-3"}>
<Keyboard
baseClass="simple-keyboard-main2"
layoutName={layoutName}
onKeyPress={(key: string) => {
if (key === "Fn") {
setUseFn(prev => !prev);
return;
}
onKeyDown(key);
}}
display={keyDisplayMap2}
buttonTheme={
modifierLockMode && modifierLockButtons
? [{ class: "modifier-locked", buttons: modifierLockButtons }]
: []
}
layout={{
default: [
useFn
? "Fn Backquote F11 F12 BracketLeft BracketRight Backslash"
: "Fn Backquote Minus Equal BracketLeft BracketRight Backslash",
],
shift: [
useFn
? "Fn Backquote F11 F12 BracketLeft BracketRight Backslash"
: "Fn (Backquote) (Minus) (Equal) (BracketLeft) (BracketRight) (Backslash)",
],
}}
disableButtonHold={true}
syncInstanceInputs={true}
debug={false}
/>
</div>
<div className={"flex flex-row w-full justify-between items-center h-1/4"}>
<Keyboard
baseClass="simple-keyboard-main2"
layoutName={layoutName}
onKeyPress={onKeyDown}
display={keyDisplayMap2}
buttonTheme={
modifierLockMode && modifierLockButtons
? [{ class: "modifier-locked", buttons: modifierLockButtons }]
: []
}
layout={{
default: [
"ShiftLeft Semicolon Quote Period Slash Backspace",
],
shift: [
"ShiftLeft (Semicolon) (Quote) (Period) (Slash) Backspace",
],
}}
disableButtonHold={true}
syncInstanceInputs={true}
debug={false}
/>
</div>
<div className={"flex flex-row w-full justify-between items-center h-1/4"}>
<div className={"w-1/5"}>
<Keyboard
baseClass="simple-keyboard-main2"
layoutName={layoutName}
onKeyPress={() => {
setUseNum(false);
}}
display={keyDisplayMap2}
layout={{
default: [
"abc",
],
shift: [
"abc",
],
}} />
</div>
<div className={"w-3/5"}>
<Keyboard
baseClass="simple-keyboard-main2"
layoutName={layoutName}
onKeyPress={onKeyDown}
display={keyDisplayMap2}
layout={{
default: [
"Space",
],
shift: [
"Space",
],
}}
disableButtonHold={true}
syncInstanceInputs={true}
debug={false}
/>
</div>
<div className={"w-1/5"}>
<Keyboard
baseClass="simple-keyboard-main2"
layoutName={layoutName}
onKeyPress={onKeyDown}
display={keyDisplayMap2}
layout={{
default: [
"Enter",
],
shift: [
"Enter",
],
}} />
</div>
</div>
</>
:
<>
<div className={"flex flex-row w-full justify-between items-center h-1/4"}>
<Keyboard
baseClass="simple-keyboard-main2"
layoutName={layoutName}
onKeyPress={onKeyDown}
display={keyDisplayMap2}
layout={{
default: [
"KeyQ KeyW KeyE KeyR KeyT KeyY KeyU KeyI KeyO KeyP",
],
shift: [
"(KeyQ) (KeyW) (KeyE) (KeyR) (KeyT) (KeyY) (KeyU) (KeyI) (KeyO) (KeyP)",
],
}}
disableButtonHold={true}
syncInstanceInputs={true}
debug={false}
/>
</div>
<div className={"flex flex-row w-full justify-between items-center h-1/4 px-3"}>
<Keyboard
baseClass="simple-keyboard-main2"
layoutName={layoutName}
onKeyPress={onKeyDown}
display={keyDisplayMap2}
layout={{
default: [
"KeyA KeyS KeyD KeyF KeyG KeyH KeyJ KeyK KeyL",
],
shift: [
"(KeyA) (KeyS) (KeyD) (KeyF) (KeyG) (KeyH) (KeyJ) (KeyK) (KeyL)",
],
}}
disableButtonHold={true}
syncInstanceInputs={true}
debug={false}
/>
</div>
<div className={"flex flex-row w-full justify-between items-center h-1/4"}>
<Keyboard
baseClass="simple-keyboard-main2"
layoutName={layoutName}
onKeyPress={onKeyDown}
display={keyDisplayMap2}
layout={{
default: [
"ShiftLeft KeyZ KeyX KeyC KeyV KeyB KeyN KeyM Backspace",
],
shift: [
"ShiftLeft (KeyZ) (KeyX) (KeyC) (KeyV) (KeyB) (KeyN) (KeyM) Backspace",
],
}}
disableButtonHold={true}
syncInstanceInputs={true}
debug={false}
/>
</div>
<div className={"flex flex-row w-full justify-between items-center h-1/4"}>
<div className={"w-1/5"}>
<Keyboard
baseClass="simple-keyboard-main2"
layoutName={layoutName}
onKeyPress={() => {
setUseNum(true);
}}
display={keyDisplayMap2}
layout={{
default: [
"123",
],
shift: [
"123",
],
}} />
</div>
<div className={"w-3/5"}>
<Keyboard
baseClass="simple-keyboard-main2"
layoutName={layoutName}
onKeyPress={onKeyDown}
display={keyDisplayMap2}
layout={{
default: [
"Space",
],
shift: [
"Space",
],
}}
disableButtonHold={true}
syncInstanceInputs={true}
debug={false}
/>
</div>
<div className={"w-1/5"}>
<Keyboard
baseClass="simple-keyboard-main2"
layoutName={layoutName}
onKeyPress={onKeyDown}
display={keyDisplayMap2}
layout={{
default: [
"Enter",
],
shift: [
"Enter",
],
}}
disableButtonHold={true}
syncInstanceInputs={true}
debug={false}
/>
</div>
</div>
</>
}
</div>
</>
:
<>
<div
className={`w-full h-[36px] flex items-center justify-between border-b border-b-slate-800/30 px-2 dark:border-b-slate-300/20 ${dark_bg2_style}`}>
<div className=" left-2 flex items-center gap-x-2">
{!isMobile && (
<div className="flex items-center gap-x-2 ml-2">
<Switch
size="small"
checked={modifierLockMode}
onChange={setModifierLockMode}
checkedChildren={<LockClosedIcon className="w-3 h-3 mt-0.5" />}
/>
<span className="text-[10px] text-gray-500">
{$at("Sticky Keys")}
</span>
</div>
)}
</div>
<h2 className="select-none self-center font-sans text-[12px] text-[rgba(102,102,102,1)]">
{$at("Virtual Keyboard")}
</h2>
<div className="h-full flex items-center">
<div style={{ width: "1px", height: "100%" }}
className={"bg-[rgba(229,229,229,1)] dark:bg-[rgba(56,56,56,1)]"}></div>
<AntdButton
type={"text"}
icon={<GoBottomSvg />}
onClick={() => setVirtualKeyboard(false)}
>
{$at("Hide")}
</AntdButton>
</div>
</div>
<div>
<div className={`flex flex-col ${dark_bg2_style} md:flex-col `}>
<style>
{`
html.dark .simple-keyboard-main,
html.dark .simple-keyboard-control,
html.dark .simple-keyboard-arrows {
background-color: transparent !important;
}
html.dark .simple-keyboard-main .hg-button,
html.dark .simple-keyboard-control .hg-button,
html.dark .simple-keyboard-arrows .hg-button {
background-color: rgba(44, 44, 46, 1) !important;
color: white !important;
}
/* Add click animation */
.hg-button:active {
background-color: rgba(0,0,0,0.2) !important;
}
html.dark .hg-button:active {
background-color: rgba(255,255,255,0.2) !important;
}
/* Specific overrides for dark mode PC layout sections */
html.dark .simple-keyboard-main .hg-button:active,
html.dark .simple-keyboard-control .hg-button:active,
html.dark .simple-keyboard-arrows .hg-button:active {
background-color: rgba(255,255,255,0.2) !important;
}
.hg-button {
transition: background-color 0.05s ease-in-out;
}
`}
</style>
<div style={{ width: "40%" }} className={"flex items-start justify-center flex-col"}>
<div className={"h-[10px]"}></div>
<Keyboard
baseClass="simple-keyboard-main"
layoutName={layoutName}
onKeyPress={onKeyDown}
buttonTheme={
modifierLockMode && modifierLockButtons
? [{ class: "modifier-locked", buttons: modifierLockButtons }]
: []
}
display={keyDisplayMap}
layout={{
default: [
"CtrlAltDelete AltMetaEscape CtrlAltBackspace",
],
shift: [
"CtrlAltDelete AltMetaEscape CtrlAltBackspace",
],
}}
disableButtonHold={true}
syncInstanceInputs={true}
debug={false}
/>
</div>
<div className={`flex flex-col md:flex-row ${dark_bg2_style}`}>
<Keyboard
baseClass="simple-keyboard-main"
layoutName={layoutName}
onKeyPress={onKeyDown}
display={keyDisplayMap}
buttonTheme={
modifierLockMode && modifierLockButtons
? [{ class: "modifier-locked", buttons: modifierLockButtons }]
: []
}
layout={virtualKeyboardLayout.main}
disableButtonHold={true}
syncInstanceInputs={true}
debug={false}
/>
{/*<div className="controlArrows">*/}
<div style={{
display: "flex",
flex: 1,
alignItems: "center",
flexDirection: "column",
justifyContent: "space-between",
}}>
<Keyboard
baseClass="simple-keyboard-control"
theme="simple-keyboard hg-theme-default hg-layout-default"
layoutName={layoutName}
onKeyPress={onKeyDown}
display={keyDisplayMap}
layout={virtualKeyboardLayout.control}
syncInstanceInputs={true}
debug={false}
/>
<div style={{
display: "flex",
width: "100%",
alignItems: "center",
flexDirection: "column",
justifyContent: "space-between",
}}>
<div style={{
display: "flex",
width: "34%",
alignItems: "center",
flexDirection: "column",
justifyContent: "space-between",
}}>
<Keyboard
baseClass="simple-keyboard-arrows"
theme="simple-keyboard hg-theme-default hg-layout-default"
onKeyPress={onKeyDown}
display={keyDisplayMap}
layout={{
default: [virtualKeyboardLayout.arrows?.default?.[0] || "ArrowUp"],
}}
syncInstanceInputs={true}
debug={false}
/>
</div>
<Keyboard
baseClass="simple-keyboard-arrows"
theme="simple-keyboard hg-theme-default hg-layout-default"
onKeyPress={onKeyDown}
display={keyDisplayMap}
layout={{
default: [virtualKeyboardLayout.arrows?.default?.[1] || "ArrowLeft ArrowDown ArrowRight"],
}}
syncInstanceInputs={true}
debug={false}
/>
</div>
</div>
</div>
</div>
</div>
</>}
</Card>
</div>
</motion.div>
)}
</AnimatePresence>
</div>
);
}
export default KeyboardWrapper;