mirror of https://github.com/jetkvm/kvm.git
Compare commits
5 Commits
4390b3f3c7
...
b0dab75dbd
Author | SHA1 | Date |
---|---|---|
|
b0dab75dbd | |
|
7267347261 | |
|
393bc122d4 | |
|
6d13e1be12 | |
|
a5b07b4862 |
|
@ -30,8 +30,8 @@ var defaultGadgetConfig = map[string]gadgetConfigItem{
|
||||||
attrs: gadgetAttributes{
|
attrs: gadgetAttributes{
|
||||||
"bcdUSB": "0x0200", // USB 2.0
|
"bcdUSB": "0x0200", // USB 2.0
|
||||||
"idVendor": "0x1d6b", // The Linux Foundation
|
"idVendor": "0x1d6b", // The Linux Foundation
|
||||||
"idProduct": "0104", // Multifunction Composite Gadget
|
"idProduct": "0x0104", // Multifunction Composite Gadget
|
||||||
"bcdDevice": "0100",
|
"bcdDevice": "0x0100", // USB2
|
||||||
},
|
},
|
||||||
configAttrs: gadgetAttributes{
|
configAttrs: gadgetAttributes{
|
||||||
"MaxPower": "250", // in unit of 2mA
|
"MaxPower": "250", // in unit of 2mA
|
||||||
|
|
19
jsonrpc.go
19
jsonrpc.go
|
@ -681,10 +681,11 @@ func rpcResetConfig() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
type DCPowerState struct {
|
type DCPowerState struct {
|
||||||
IsOn bool `json:"isOn"`
|
IsOn bool `json:"isOn"`
|
||||||
Voltage float64 `json:"voltage"`
|
Voltage float64 `json:"voltage"`
|
||||||
Current float64 `json:"current"`
|
Current float64 `json:"current"`
|
||||||
Power float64 `json:"power"`
|
Power float64 `json:"power"`
|
||||||
|
RestoreState int `json:"restoreState"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func rpcGetDCPowerState() (DCPowerState, error) {
|
func rpcGetDCPowerState() (DCPowerState, error) {
|
||||||
|
@ -700,6 +701,15 @@ func rpcSetDCPowerState(enabled bool) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func rpcSetDCRestoreState(state int) error {
|
||||||
|
logger.Info().Int("state", state).Msg("Setting DC restore state")
|
||||||
|
err := setDCRestoreState(state)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to set DC restore state: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func rpcGetActiveExtension() (string, error) {
|
func rpcGetActiveExtension() (string, error) {
|
||||||
return config.ActiveExtension, nil
|
return config.ActiveExtension, nil
|
||||||
}
|
}
|
||||||
|
@ -1088,6 +1098,7 @@ var rpcHandlers = map[string]RPCHandler{
|
||||||
"getBacklightSettings": {Func: rpcGetBacklightSettings},
|
"getBacklightSettings": {Func: rpcGetBacklightSettings},
|
||||||
"getDCPowerState": {Func: rpcGetDCPowerState},
|
"getDCPowerState": {Func: rpcGetDCPowerState},
|
||||||
"setDCPowerState": {Func: rpcSetDCPowerState, Params: []string{"enabled"}},
|
"setDCPowerState": {Func: rpcSetDCPowerState, Params: []string{"enabled"}},
|
||||||
|
"setDCRestoreState": {Func: rpcSetDCRestoreState, Params: []string{"state"}},
|
||||||
"getActiveExtension": {Func: rpcGetActiveExtension},
|
"getActiveExtension": {Func: rpcGetActiveExtension},
|
||||||
"setActiveExtension": {Func: rpcSetActiveExtension, Params: []string{"extensionId"}},
|
"setActiveExtension": {Func: rpcSetActiveExtension, Params: []string{"extensionId"}},
|
||||||
"getATXState": {Func: rpcGetATXState},
|
"getATXState": {Func: rpcGetATXState},
|
||||||
|
|
39
serial.go
39
serial.go
|
@ -142,6 +142,7 @@ var dcState DCPowerState
|
||||||
func runDCControl() {
|
func runDCControl() {
|
||||||
scopedLogger := serialLogger.With().Str("service", "dc_control").Logger()
|
scopedLogger := serialLogger.With().Str("service", "dc_control").Logger()
|
||||||
reader := bufio.NewReader(port)
|
reader := bufio.NewReader(port)
|
||||||
|
hasRestoreFeature := false
|
||||||
for {
|
for {
|
||||||
line, err := reader.ReadString('\n')
|
line, err := reader.ReadString('\n')
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -151,7 +152,13 @@ func runDCControl() {
|
||||||
|
|
||||||
// Split the line by semicolon
|
// Split the line by semicolon
|
||||||
parts := strings.Split(strings.TrimSpace(line), ";")
|
parts := strings.Split(strings.TrimSpace(line), ";")
|
||||||
if len(parts) != 4 {
|
if len(parts) == 5 {
|
||||||
|
scopedLogger.Debug().Str("line", line).Msg("Detected DC extension with restore feature")
|
||||||
|
hasRestoreFeature = true
|
||||||
|
} else if len(parts) == 4 {
|
||||||
|
scopedLogger.Debug().Str("line", line).Msg("Detected DC extension without restore feature")
|
||||||
|
hasRestoreFeature = false
|
||||||
|
} else {
|
||||||
scopedLogger.Warn().Str("line", line).Msg("Invalid line")
|
scopedLogger.Warn().Str("line", line).Msg("Invalid line")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -163,6 +170,17 @@ func runDCControl() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
dcState.IsOn = powerState == 1
|
dcState.IsOn = powerState == 1
|
||||||
|
if hasRestoreFeature {
|
||||||
|
restoreState, err := strconv.Atoi(parts[4])
|
||||||
|
if err != nil {
|
||||||
|
scopedLogger.Warn().Err(err).Msg("Invalid restore state")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
dcState.RestoreState = restoreState
|
||||||
|
} else {
|
||||||
|
// -1 means not supported
|
||||||
|
dcState.RestoreState = -1
|
||||||
|
}
|
||||||
milliVolts, err := strconv.ParseFloat(parts[1], 64)
|
milliVolts, err := strconv.ParseFloat(parts[1], 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
scopedLogger.Warn().Err(err).Msg("Invalid voltage")
|
scopedLogger.Warn().Err(err).Msg("Invalid voltage")
|
||||||
|
@ -210,6 +228,25 @@ func setDCPowerState(on bool) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setDCRestoreState(state int) error {
|
||||||
|
_, err := port.Write([]byte("\n"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
command := "RESTORE_MODE_OFF\n"
|
||||||
|
switch state {
|
||||||
|
case 1:
|
||||||
|
command = "RESTORE_MODE_ON\n"
|
||||||
|
case 2:
|
||||||
|
command = "RESTORE_MODE_LAST_STATE\n"
|
||||||
|
}
|
||||||
|
_, err = port.Write([]byte(command))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
var defaultMode = &serial.Mode{
|
var defaultMode = &serial.Mode{
|
||||||
BaudRate: 115200,
|
BaudRate: 115200,
|
||||||
DataBits: 8,
|
DataBits: 8,
|
||||||
|
|
|
@ -262,23 +262,6 @@ export default function Actionbar({
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/* {useSettingsStore().actionBarCtrlAltDel && (
|
|
||||||
<div className="hidden lg:block">
|
|
||||||
<Button
|
|
||||||
size="XS"
|
|
||||||
theme="light"
|
|
||||||
text="Ctrl + Alt + Del"
|
|
||||||
LeadingIcon={FaLock}
|
|
||||||
onClick={() => {
|
|
||||||
sendKeyboardEvent(
|
|
||||||
[keys["Delete"]],
|
|
||||||
[modifiers["ControlLeft"], modifiers["AltLeft"]],
|
|
||||||
);
|
|
||||||
setTimeout(resetKeyboardState, 100);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)} */}
|
|
||||||
<div>
|
<div>
|
||||||
<Button
|
<Button
|
||||||
size="XS"
|
size="XS"
|
||||||
|
|
|
@ -67,19 +67,19 @@ function Terminal({
|
||||||
}) {
|
}) {
|
||||||
const enableTerminal = useUiStore(state => state.terminalType == type);
|
const enableTerminal = useUiStore(state => state.terminalType == type);
|
||||||
const setTerminalType = useUiStore(state => state.setTerminalType);
|
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 });
|
const { instance, ref } = useXTerm({ options: TERMINAL_CONFIG });
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setDisableKeyboardFocusTrap(enableTerminal);
|
setDisableVideoFocusTrap(enableTerminal);
|
||||||
}, 500);
|
}, 500);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
setDisableKeyboardFocusTrap(false);
|
setDisableVideoFocusTrap(false);
|
||||||
};
|
};
|
||||||
}, [ref, instance, enableTerminal, setDisableKeyboardFocusTrap, type]);
|
}, [enableTerminal, setDisableVideoFocusTrap]);
|
||||||
|
|
||||||
const readyState = dataChannel.readyState;
|
const readyState = dataChannel.readyState;
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -116,7 +116,7 @@ function Terminal({
|
||||||
const { domEvent } = e;
|
const { domEvent } = e;
|
||||||
if (domEvent.key === "Escape") {
|
if (domEvent.key === "Escape") {
|
||||||
setTerminalType("none");
|
setTerminalType("none");
|
||||||
setDisableKeyboardFocusTrap(false);
|
setDisableVideoFocusTrap(false);
|
||||||
domEvent.preventDefault();
|
domEvent.preventDefault();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -131,7 +131,7 @@ function Terminal({
|
||||||
onDataHandler.dispose();
|
onDataHandler.dispose();
|
||||||
onKeyHandler.dispose();
|
onKeyHandler.dispose();
|
||||||
};
|
};
|
||||||
}, [instance, dataChannel, readyState, setDisableKeyboardFocusTrap, setTerminalType]);
|
}, [dataChannel, instance, readyState, setDisableVideoFocusTrap, setTerminalType]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!instance) return;
|
if (!instance) return;
|
||||||
|
@ -158,7 +158,7 @@ function Terminal({
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener("resize", handleResize);
|
window.removeEventListener("resize", handleResize);
|
||||||
};
|
};
|
||||||
}, [ref, instance]);
|
}, [instance]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|
|
@ -8,12 +8,14 @@ import { useJsonRpc } from "@/hooks/useJsonRpc";
|
||||||
import notifications from "@/notifications";
|
import notifications from "@/notifications";
|
||||||
import FieldLabel from "@components/FieldLabel";
|
import FieldLabel from "@components/FieldLabel";
|
||||||
import LoadingSpinner from "@components/LoadingSpinner";
|
import LoadingSpinner from "@components/LoadingSpinner";
|
||||||
|
import {SelectMenuBasic} from "@components/SelectMenuBasic";
|
||||||
|
|
||||||
interface DCPowerState {
|
interface DCPowerState {
|
||||||
isOn: boolean;
|
isOn: boolean;
|
||||||
voltage: number;
|
voltage: number;
|
||||||
current: number;
|
current: number;
|
||||||
power: number;
|
power: number;
|
||||||
|
restoreState: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DCPowerControl() {
|
export function DCPowerControl() {
|
||||||
|
@ -43,6 +45,20 @@ export function DCPowerControl() {
|
||||||
getDCPowerState(); // Refresh state after change
|
getDCPowerState(); // Refresh state after change
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
const handleRestoreChange = (state: number) => {
|
||||||
|
// const state = powerState?.restoreState === 0 ? 1 : powerState?.restoreState === 1 ? 2 : 0;
|
||||||
|
send("setDCRestoreState", { state }, resp => {
|
||||||
|
if ("error" in resp) {
|
||||||
|
notifications.error(
|
||||||
|
`Failed to set DC power state: ${resp.error.data || "Unknown error"}`,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
getDCPowerState(); // Refresh state after change
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getDCPowerState();
|
getDCPowerState();
|
||||||
|
@ -63,7 +79,7 @@ export function DCPowerControl() {
|
||||||
<LoadingSpinner className="h-6 w-6 text-blue-500 dark:text-blue-400" />
|
<LoadingSpinner className="h-6 w-6 text-blue-500 dark:text-blue-400" />
|
||||||
</Card>
|
</Card>
|
||||||
) : (
|
) : (
|
||||||
<Card className="h-[160px] animate-fadeIn opacity-0">
|
<Card className="animate-fadeIn opacity-0">
|
||||||
<div className="space-y-4 p-3">
|
<div className="space-y-4 p-3">
|
||||||
{/* Power Controls */}
|
{/* Power Controls */}
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
|
@ -84,6 +100,21 @@ export function DCPowerControl() {
|
||||||
onClick={() => handlePowerToggle(false)}
|
onClick={() => handlePowerToggle(false)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
{powerState.restoreState > -1 ? (
|
||||||
|
<div className="flex items-center">
|
||||||
|
<SelectMenuBasic
|
||||||
|
size="SM"
|
||||||
|
label="Restore Power Loss"
|
||||||
|
value={powerState.restoreState}
|
||||||
|
onChange={e => handleRestoreChange(parseInt(e.target.value))}
|
||||||
|
options={[
|
||||||
|
{ value: '0', label: "Power OFF" },
|
||||||
|
{ value: '1', label: "Power ON" },
|
||||||
|
{ value: '2', label: "Last State" },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
<hr className="border-slate-700/30 dark:border-slate-600/30" />
|
<hr className="border-slate-700/30 dark:border-slate-600/30" />
|
||||||
|
|
||||||
{/* Status Display */}
|
{/* Status Display */}
|
||||||
|
|
|
@ -10,11 +10,11 @@ import { SettingsPageHeader } from "@components/SettingsPageheader";
|
||||||
import { useJsonRpc } from "@/hooks/useJsonRpc";
|
import { useJsonRpc } from "@/hooks/useJsonRpc";
|
||||||
import { useHidStore, useRTCStore, useUiStore, useSettingsStore } from "@/hooks/stores";
|
import { useHidStore, useRTCStore, useUiStore, useSettingsStore } from "@/hooks/stores";
|
||||||
import { keys, modifiers } from "@/keyboardMappings";
|
import { keys, modifiers } from "@/keyboardMappings";
|
||||||
import { layouts, chars } from "@/keyboardLayouts";
|
import { KeyStroke, KeyboardLayout, selectedKeyboard } from "@/keyboardLayouts";
|
||||||
import notifications from "@/notifications";
|
import notifications from "@/notifications";
|
||||||
|
|
||||||
const hidKeyboardPayload = (keys: number[], modifier: number) => {
|
const hidKeyboardPayload = (modifier: number, keys: number[]) => {
|
||||||
return { keys, modifier };
|
return { modifier, keys };
|
||||||
};
|
};
|
||||||
|
|
||||||
const modifierCode = (shift?: boolean, altRight?: boolean) => {
|
const modifierCode = (shift?: boolean, altRight?: boolean) => {
|
||||||
|
@ -62,49 +62,56 @@ export default function PasteModal() {
|
||||||
const onConfirmPaste = useCallback(async () => {
|
const onConfirmPaste = useCallback(async () => {
|
||||||
setPasteMode(false);
|
setPasteMode(false);
|
||||||
setDisableVideoFocusTrap(false);
|
setDisableVideoFocusTrap(false);
|
||||||
|
|
||||||
if (rpcDataChannel?.readyState !== "open" || !TextAreaRef.current) return;
|
if (rpcDataChannel?.readyState !== "open" || !TextAreaRef.current) return;
|
||||||
if (!safeKeyboardLayout) return;
|
const keyboard: KeyboardLayout = selectedKeyboard(safeKeyboardLayout);
|
||||||
if (!chars[safeKeyboardLayout]) return;
|
if (!keyboard) return;
|
||||||
|
|
||||||
const text = TextAreaRef.current.value;
|
const text = TextAreaRef.current.value;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (const char of text) {
|
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;
|
if (!key) continue;
|
||||||
|
|
||||||
const keyz = [ keys[key] ];
|
// if this is an accented character, we need to send that accent FIRST
|
||||||
const modz = [ modifierCode(shift, altRight) ];
|
|
||||||
|
|
||||||
if (deadKey) {
|
|
||||||
keyz.push(keys["Space"]);
|
|
||||||
modz.push(noModifier);
|
|
||||||
}
|
|
||||||
if (accentKey) {
|
if (accentKey) {
|
||||||
keyz.unshift(keys[accentKey.key])
|
await sendKeystroke({modifier: modifierCode(accentKey.shift, accentKey.altRight), keys: [ keys[accentKey.key] ] })
|
||||||
modz.unshift(modifierCode(accentKey.shift, accentKey.altRight))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const [index, kei] of keyz.entries()) {
|
// now send the actual key
|
||||||
await new Promise<void>((resolve, reject) => {
|
await sendKeystroke({ modifier: modifierCode(shift, altRight), keys: [ keys[key] ]});
|
||||||
send(
|
|
||||||
"keyboardReport",
|
// if what was requested was a dead key, we need to send an unmodified space to emit
|
||||||
hidKeyboardPayload([kei], modz[index]),
|
// just the accent character
|
||||||
params => {
|
if (deadKey) {
|
||||||
if ("error" in params) return reject(params.error);
|
await sendKeystroke({ modifier: noModifier, keys: [ keys["Space"] ] });
|
||||||
send("keyboardReport", hidKeyboardPayload([], 0), params => {
|
|
||||||
if ("error" in params) return reject(params.error);
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// now send a message with no keys down to "release" the keys
|
||||||
|
await sendKeystroke({ modifier: 0, keys: [] });
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error("Failed to paste text:", error);
|
||||||
notifications.error("Failed to paste text");
|
notifications.error("Failed to paste text");
|
||||||
}
|
}
|
||||||
}, [rpcDataChannel?.readyState, send, setDisableVideoFocusTrap, setPasteMode, safeKeyboardLayout]);
|
|
||||||
|
async function sendKeystroke(stroke: KeyStroke) {
|
||||||
|
await new Promise<void>((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(() => {
|
useEffect(() => {
|
||||||
if (TextAreaRef.current) {
|
if (TextAreaRef.current) {
|
||||||
|
@ -154,7 +161,7 @@ export default function PasteModal() {
|
||||||
// @ts-expect-error TS doesn't recognize Intl.Segmenter in some environments
|
// @ts-expect-error TS doesn't recognize Intl.Segmenter in some environments
|
||||||
[...new Intl.Segmenter().segment(value)]
|
[...new Intl.Segmenter().segment(value)]
|
||||||
.map(x => x.segment)
|
.map(x => x.segment)
|
||||||
.filter(char => !chars[safeKeyboardLayout][char]),
|
.filter(char => !selectedKeyboard(safeKeyboardLayout).chars[char]),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -175,7 +182,7 @@ export default function PasteModal() {
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<p className="text-xs text-slate-600 dark:text-slate-400">
|
<p className="text-xs text-slate-600 dark:text-slate-400">
|
||||||
Sending text using keyboard layout: {layouts[safeKeyboardLayout]}
|
Sending text using keyboard layout: {selectedKeyboard(safeKeyboardLayout).name}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -14,7 +14,7 @@ import AddDeviceForm from "./AddDeviceForm";
|
||||||
export default function WakeOnLanModal() {
|
export default function WakeOnLanModal() {
|
||||||
const [storedDevices, setStoredDevices] = useState<StoredDevice[]>([]);
|
const [storedDevices, setStoredDevices] = useState<StoredDevice[]>([]);
|
||||||
const [showAddForm, setShowAddForm] = useState(false);
|
const [showAddForm, setShowAddForm] = useState(false);
|
||||||
const setDisableFocusTrap = useUiStore(state => state.setDisableVideoFocusTrap);
|
const setDisableVideoFocusTrap = useUiStore(state => state.setDisableVideoFocusTrap);
|
||||||
|
|
||||||
const rpcDataChannel = useRTCStore(state => state.rpcDataChannel);
|
const rpcDataChannel = useRTCStore(state => state.rpcDataChannel);
|
||||||
|
|
||||||
|
@ -24,9 +24,9 @@ export default function WakeOnLanModal() {
|
||||||
const [addDeviceErrorMessage, setAddDeviceErrorMessage] = useState<string | null>(null);
|
const [addDeviceErrorMessage, setAddDeviceErrorMessage] = useState<string | null>(null);
|
||||||
|
|
||||||
const onCancelWakeOnLanModal = useCallback(() => {
|
const onCancelWakeOnLanModal = useCallback(() => {
|
||||||
|
setDisableVideoFocusTrap(false);
|
||||||
close();
|
close();
|
||||||
setDisableFocusTrap(false);
|
}, [close, setDisableVideoFocusTrap]);
|
||||||
}, [close, setDisableFocusTrap]);
|
|
||||||
|
|
||||||
const onSendMagicPacket = useCallback(
|
const onSendMagicPacket = useCallback(
|
||||||
(macAddress: string) => {
|
(macAddress: string) => {
|
||||||
|
@ -43,12 +43,12 @@ export default function WakeOnLanModal() {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
notifications.success("Magic Packet sent successfully");
|
notifications.success("Magic Packet sent successfully");
|
||||||
setDisableFocusTrap(false);
|
setDisableVideoFocusTrap(false);
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[close, rpcDataChannel?.readyState, send, setDisableFocusTrap],
|
[close, rpcDataChannel?.readyState, send, setDisableVideoFocusTrap],
|
||||||
);
|
);
|
||||||
|
|
||||||
const syncStoredDevices = useCallback(() => {
|
const syncStoredDevices = useCallback(() => {
|
||||||
|
@ -78,7 +78,7 @@ export default function WakeOnLanModal() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[storedDevices, send, syncStoredDevices],
|
[send, storedDevices, syncStoredDevices],
|
||||||
);
|
);
|
||||||
|
|
||||||
const onAddDevice = useCallback(
|
const onAddDevice = useCallback(
|
||||||
|
|
|
@ -308,9 +308,6 @@ interface SettingsState {
|
||||||
keyboardLayout: string;
|
keyboardLayout: string;
|
||||||
setKeyboardLayout: (layout: string) => void;
|
setKeyboardLayout: (layout: string) => void;
|
||||||
|
|
||||||
actionBarCtrlAltDel: boolean;
|
|
||||||
setActionBarCtrlAltDel: (enabled: boolean) => void;
|
|
||||||
|
|
||||||
keyboardLedSync: KeyboardLedSync;
|
keyboardLedSync: KeyboardLedSync;
|
||||||
setKeyboardLedSync: (sync: KeyboardLedSync) => void;
|
setKeyboardLedSync: (sync: KeyboardLedSync) => void;
|
||||||
|
|
||||||
|
@ -359,9 +356,6 @@ export const useSettingsStore = create(
|
||||||
keyboardLayout: "en-US",
|
keyboardLayout: "en-US",
|
||||||
setKeyboardLayout: layout => set({ keyboardLayout: layout }),
|
setKeyboardLayout: layout => set({ keyboardLayout: layout }),
|
||||||
|
|
||||||
actionBarCtrlAltDel: false,
|
|
||||||
setActionBarCtrlAltDel: enabled => set({ actionBarCtrlAltDel: enabled }),
|
|
||||||
|
|
||||||
keyboardLedSync: "auto",
|
keyboardLedSync: "auto",
|
||||||
setKeyboardLedSync: sync => set({ keyboardLedSync: sync }),
|
setKeyboardLedSync: sync => set({ keyboardLedSync: sync }),
|
||||||
|
|
||||||
|
@ -941,5 +935,5 @@ export const useMacrosStore = create<MacrosState>((set, get) => ({
|
||||||
} finally {
|
} finally {
|
||||||
set({ loading: false });
|
set({ loading: false });
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -1,45 +1,32 @@
|
||||||
import { chars as chars_fr_BE, name as name_fr_BE } from "@/keyboardLayouts/fr_BE"
|
export interface KeyStroke { modifier: number; keys: number[]; }
|
||||||
import { chars as chars_cs_CZ, name as name_cs_CZ } from "@/keyboardLayouts/cs_CZ"
|
export interface KeyInfo { key: string | number; shift?: boolean, altRight?: boolean }
|
||||||
import { chars as chars_en_UK, name as name_en_UK } from "@/keyboardLayouts/en_UK"
|
export interface KeyCombo extends KeyInfo { deadKey?: boolean, accentKey?: KeyInfo }
|
||||||
import { chars as chars_en_US, name as name_en_US } from "@/keyboardLayouts/en_US"
|
export interface KeyboardLayout { isoCode: string, name: string, chars: Record<string, KeyCombo> }
|
||||||
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"
|
|
||||||
|
|
||||||
interface KeyInfo { key: string | number; shift?: boolean, altRight?: boolean }
|
// to add a new layout, create a file like the above and add it to the list
|
||||||
export type KeyCombo = KeyInfo & { deadKey?: boolean, accentKey?: KeyInfo }
|
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<string, string> = {
|
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 ];
|
||||||
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 chars: Record<string, Record<string, KeyCombo>> = {
|
export const selectedKeyboard = (isoCode: string): KeyboardLayout => {
|
||||||
be_FR: chars_fr_BE,
|
// fallback to original behaviour of en-US if no isoCode given
|
||||||
cs_CZ: chars_cs_CZ,
|
return keyboards.find(keyboard => keyboard.isoCode == isoCode)
|
||||||
en_UK: chars_en_UK,
|
?? keyboards.find(keyboard => keyboard.isoCode == "en-US")!;
|
||||||
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 keyboardOptions = () => {
|
||||||
|
return keyboards.map((keyboard) => {
|
||||||
|
return { label: keyboard.name, value: keyboard.isoCode }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -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 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 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 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
|
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 },
|
A: { key: "KeyA", shift: true },
|
||||||
"Ä": { key: "KeyA", shift: true, accentKey: keyTrema },
|
"Ä": { key: "KeyA", shift: true, accentKey: keyTrema },
|
||||||
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
|
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
|
||||||
|
@ -242,3 +242,9 @@ export const chars = {
|
||||||
Enter: { key: "Enter" },
|
Enter: { key: "Enter" },
|
||||||
Tab: { key: "Tab" },
|
Tab: { key: "Tab" },
|
||||||
} as Record<string, KeyCombo>;
|
} as Record<string, KeyCombo>;
|
||||||
|
|
||||||
|
export const cs_CZ: KeyboardLayout = {
|
||||||
|
isoCode: "cs-CZ",
|
||||||
|
name: name,
|
||||||
|
chars: chars
|
||||||
|
};
|
|
@ -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 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 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 keyGrave = { key: "Equal", shift: true } // accent grave, mark ` placed above the letter
|
||||||
const keyTilde = { key: "Equal", altRight: true } // tilde, 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 },
|
A: { key: "KeyA", shift: true },
|
||||||
"Ä": { key: "KeyA", shift: true, accentKey: keyTrema },
|
"Ä": { key: "KeyA", shift: true, accentKey: keyTrema },
|
||||||
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
|
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
|
||||||
|
@ -163,3 +163,9 @@ export const chars = {
|
||||||
Enter: { key: "Enter" },
|
Enter: { key: "Enter" },
|
||||||
Tab: { key: "Tab" },
|
Tab: { key: "Tab" },
|
||||||
} as Record<string, KeyCombo>;
|
} as Record<string, KeyCombo>;
|
||||||
|
|
||||||
|
export const de_CH: KeyboardLayout = {
|
||||||
|
isoCode: "de-CH",
|
||||||
|
name: name,
|
||||||
|
chars: chars
|
||||||
|
};
|
|
@ -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 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 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
|
const keyGrave = { key: "Equal", shift: true } // accent grave, mark ` placed above the letter
|
||||||
|
|
||||||
export const chars = {
|
const chars = {
|
||||||
A: { key: "KeyA", shift: true },
|
A: { key: "KeyA", shift: true },
|
||||||
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
|
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
|
||||||
"Â": { key: "KeyA", shift: true, accentKey: keyHat },
|
"Â": { key: "KeyA", shift: true, accentKey: keyHat },
|
||||||
|
@ -150,3 +150,9 @@ export const chars = {
|
||||||
Enter: { key: "Enter" },
|
Enter: { key: "Enter" },
|
||||||
Tab: { key: "Tab" },
|
Tab: { key: "Tab" },
|
||||||
} as Record<string, KeyCombo>;
|
} as Record<string, KeyCombo>;
|
||||||
|
|
||||||
|
export const de_DE: KeyboardLayout = {
|
||||||
|
isoCode: "de-DE",
|
||||||
|
name: name,
|
||||||
|
chars: chars
|
||||||
|
};
|
|
@ -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 },
|
A: { key: "KeyA", shift: true },
|
||||||
B: { key: "KeyB", shift: true },
|
B: { key: "KeyB", shift: true },
|
||||||
C: { key: "KeyC", shift: true },
|
C: { key: "KeyC", shift: true },
|
||||||
|
@ -105,3 +105,9 @@ export const chars = {
|
||||||
Enter: { key: "Enter" },
|
Enter: { key: "Enter" },
|
||||||
Tab: { key: "Tab" },
|
Tab: { key: "Tab" },
|
||||||
} as Record<string, KeyCombo>
|
} as Record<string, KeyCombo>
|
||||||
|
|
||||||
|
export const en_UK: KeyboardLayout = {
|
||||||
|
isoCode: "en-UK",
|
||||||
|
name: name,
|
||||||
|
chars: chars
|
||||||
|
};
|
|
@ -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 },
|
A: { key: "KeyA", shift: true },
|
||||||
B: { key: "KeyB", shift: true },
|
B: { key: "KeyB", shift: true },
|
||||||
C: { key: "KeyC", shift: true },
|
C: { key: "KeyC", shift: true },
|
||||||
|
@ -111,3 +111,9 @@ export const chars = {
|
||||||
Insert: { key: "Insert", shift: false },
|
Insert: { key: "Insert", shift: false },
|
||||||
Delete: { key: "Delete", shift: false },
|
Delete: { key: "Delete", shift: false },
|
||||||
} as Record<string, KeyCombo>
|
} as Record<string, KeyCombo>
|
||||||
|
|
||||||
|
export const en_US: KeyboardLayout = {
|
||||||
|
isoCode: "en-US",
|
||||||
|
name: name,
|
||||||
|
chars: chars
|
||||||
|
};
|
|
@ -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 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 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 keyGrave = { key: "BracketRight" } // accent grave, mark ` placed above the letter
|
||||||
const keyTilde = { key: "Key4", altRight: true } // tilde, 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 },
|
A: { key: "KeyA", shift: true },
|
||||||
"Ä": { key: "KeyA", shift: true, accentKey: keyTrema },
|
"Ä": { key: "KeyA", shift: true, accentKey: keyTrema },
|
||||||
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
|
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
|
||||||
|
@ -166,3 +166,9 @@ export const chars = {
|
||||||
Enter: { key: "Enter" },
|
Enter: { key: "Enter" },
|
||||||
Tab: { key: "Tab" },
|
Tab: { key: "Tab" },
|
||||||
} as Record<string, KeyCombo>;
|
} as Record<string, KeyCombo>;
|
||||||
|
|
||||||
|
export const es_ES: KeyboardLayout = {
|
||||||
|
isoCode: "es-ES",
|
||||||
|
name: name,
|
||||||
|
chars: chars
|
||||||
|
};
|
|
@ -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 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
|
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 keyGrave = { key: "Quote", shift: true } // accent grave, mark ` placed above the letter
|
||||||
const keyTilde = { key: "Slash", altRight: true } // tilde, 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 },
|
A: { key: "KeyQ", shift: true },
|
||||||
"Ä": { key: "KeyQ", shift: true, accentKey: keyTrema },
|
"Ä": { key: "KeyQ", shift: true, accentKey: keyTrema },
|
||||||
"Â": { key: "KeyQ", shift: true, accentKey: keyHat },
|
"Â": { key: "KeyQ", shift: true, accentKey: keyHat },
|
||||||
|
@ -165,3 +165,9 @@ export const chars = {
|
||||||
Enter: { key: "Enter" },
|
Enter: { key: "Enter" },
|
||||||
Tab: { key: "Tab" },
|
Tab: { key: "Tab" },
|
||||||
} as Record<string, KeyCombo>;
|
} as Record<string, KeyCombo>;
|
||||||
|
|
||||||
|
export const fr_BE: KeyboardLayout = {
|
||||||
|
isoCode: "fr-BE",
|
||||||
|
name: name,
|
||||||
|
chars: chars
|
||||||
|
};
|
|
@ -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 = {
|
const chars = {
|
||||||
...chars_de_CH,
|
...de_CH.chars,
|
||||||
"è": { key: "BracketLeft" },
|
"è": { key: "BracketLeft" },
|
||||||
"ü": { key: "BracketLeft", shift: true },
|
"ü": { key: "BracketLeft", shift: true },
|
||||||
"é": { key: "Semicolon" },
|
"é": { key: "Semicolon" },
|
||||||
|
@ -13,3 +13,9 @@ export const chars = {
|
||||||
"à": { key: "Quote" },
|
"à": { key: "Quote" },
|
||||||
"ä": { key: "Quote", shift: true },
|
"ä": { key: "Quote", shift: true },
|
||||||
} as Record<string, KeyCombo>;
|
} as Record<string, KeyCombo>;
|
||||||
|
|
||||||
|
export const fr_CH: KeyboardLayout = {
|
||||||
|
isoCode: "fr-CH",
|
||||||
|
name: name,
|
||||||
|
chars: chars
|
||||||
|
};
|
||||||
|
|
|
@ -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 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
|
const keyHat = { key: "BracketLeft" } // accent circonflexe (accent hat), mark ^ placed above the letter
|
||||||
|
|
||||||
export const chars = {
|
const chars = {
|
||||||
A: { key: "KeyQ", shift: true },
|
A: { key: "KeyQ", shift: true },
|
||||||
"Ä": { key: "KeyQ", shift: true, accentKey: keyTrema },
|
"Ä": { key: "KeyQ", shift: true, accentKey: keyTrema },
|
||||||
"Â": { key: "KeyQ", shift: true, accentKey: keyHat },
|
"Â": { key: "KeyQ", shift: true, accentKey: keyHat },
|
||||||
|
@ -137,3 +137,9 @@ export const chars = {
|
||||||
Enter: { key: "Enter" },
|
Enter: { key: "Enter" },
|
||||||
Tab: { key: "Tab" },
|
Tab: { key: "Tab" },
|
||||||
} as Record<string, KeyCombo>;
|
} as Record<string, KeyCombo>;
|
||||||
|
|
||||||
|
export const fr_FR: KeyboardLayout = {
|
||||||
|
isoCode: "fr-FR",
|
||||||
|
name: name,
|
||||||
|
chars: chars
|
||||||
|
};
|
|
@ -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 },
|
A: { key: "KeyA", shift: true },
|
||||||
B: { key: "KeyB", shift: true },
|
B: { key: "KeyB", shift: true },
|
||||||
C: { key: "KeyC", shift: true },
|
C: { key: "KeyC", shift: true },
|
||||||
|
@ -111,3 +111,9 @@ export const chars = {
|
||||||
Enter: { key: "Enter" },
|
Enter: { key: "Enter" },
|
||||||
Tab: { key: "Tab" },
|
Tab: { key: "Tab" },
|
||||||
} as Record<string, KeyCombo>;
|
} as Record<string, KeyCombo>;
|
||||||
|
|
||||||
|
export const it_IT: KeyboardLayout = {
|
||||||
|
isoCode: "it-IT",
|
||||||
|
name: name,
|
||||||
|
chars: chars
|
||||||
|
};
|
|
@ -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 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 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 keyGrave = { key: "Equal", shift: true } // accent grave, mark ` placed above the letter
|
||||||
const keyTilde = { key: "BracketRight", altRight: true } // tilde, 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 },
|
A: { key: "KeyA", shift: true },
|
||||||
"Ä": { key: "KeyA", shift: true, accentKey: keyTrema },
|
"Ä": { key: "KeyA", shift: true, accentKey: keyTrema },
|
||||||
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
|
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
|
||||||
|
@ -165,3 +165,9 @@ export const chars = {
|
||||||
Enter: { key: "Enter" },
|
Enter: { key: "Enter" },
|
||||||
Tab: { key: "Tab" },
|
Tab: { key: "Tab" },
|
||||||
} as Record<string, KeyCombo>;
|
} as Record<string, KeyCombo>;
|
||||||
|
|
||||||
|
export const nb_NO: KeyboardLayout = {
|
||||||
|
isoCode: "nb-NO",
|
||||||
|
name: name,
|
||||||
|
chars: chars
|
||||||
|
};
|
|
@ -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 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 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 keyGrave = { key: "Equal", shift: true } // accent grave, mark ` placed above the letter
|
||||||
const keyTilde = { key: "BracketRight", altRight: true } // tilde, 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 },
|
A: { key: "KeyA", shift: true },
|
||||||
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
|
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
|
||||||
"Â": { key: "KeyA", shift: true, accentKey: keyHat },
|
"Â": { key: "KeyA", shift: true, accentKey: keyHat },
|
||||||
|
@ -162,3 +162,9 @@ export const chars = {
|
||||||
Enter: { key: "Enter" },
|
Enter: { key: "Enter" },
|
||||||
Tab: { key: "Tab" },
|
Tab: { key: "Tab" },
|
||||||
} as Record<string, KeyCombo>;
|
} as Record<string, KeyCombo>;
|
||||||
|
|
||||||
|
export const sv_SE: KeyboardLayout = {
|
||||||
|
isoCode: "sv-SE",
|
||||||
|
name: name,
|
||||||
|
chars: chars
|
||||||
|
};
|
|
@ -1,17 +1,19 @@
|
||||||
// Key codes and modifiers correspond to definitions in the
|
// Key codes and modifiers correspond to definitions in the
|
||||||
// [Linux USB HID gadget driver](https://www.kernel.org/doc/Documentation/usb/gadget_hid.txt)
|
// [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 = {
|
export const keys = {
|
||||||
ArrowDown: 0x51,
|
ArrowDown: 0x51,
|
||||||
ArrowLeft: 0x50,
|
ArrowLeft: 0x50,
|
||||||
ArrowRight: 0x4f,
|
ArrowRight: 0x4f,
|
||||||
ArrowUp: 0x52,
|
ArrowUp: 0x52,
|
||||||
Backquote: 0x35,
|
Backquote: 0x35, // aka Grave
|
||||||
Backslash: 0x31,
|
Backslash: 0x31,
|
||||||
Backspace: 0x2a,
|
Backspace: 0x2a,
|
||||||
BracketLeft: 0x2f,
|
BracketLeft: 0x2f, // aka LeftBrace
|
||||||
BracketRight: 0x30,
|
BracketRight: 0x30, // aka RightBrace
|
||||||
CapsLock: 0x39,
|
CapsLock: 0x39,
|
||||||
Comma: 0x36,
|
Comma: 0x36,
|
||||||
|
Compose: 0x65,
|
||||||
ContextMenu: 0,
|
ContextMenu: 0,
|
||||||
Delete: 0x4c,
|
Delete: 0x4c,
|
||||||
Digit0: 0x27,
|
Digit0: 0x27,
|
||||||
|
@ -40,10 +42,21 @@ export const keys = {
|
||||||
F10: 0x43,
|
F10: 0x43,
|
||||||
F11: 0x44,
|
F11: 0x44,
|
||||||
F12: 0x45,
|
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,
|
Home: 0x4a,
|
||||||
|
HashTilde: 0x32, // non-US # and ~
|
||||||
Insert: 0x49,
|
Insert: 0x49,
|
||||||
IntlBackslash: 0x64,
|
IntlBackslash: 0x64, // non-US \ and |
|
||||||
KeyA: 0x04,
|
KeyA: 0x04,
|
||||||
KeyB: 0x05,
|
KeyB: 0x05,
|
||||||
KeyC: 0x06,
|
KeyC: 0x06,
|
||||||
|
@ -72,30 +85,35 @@ export const keys = {
|
||||||
KeyZ: 0x1d,
|
KeyZ: 0x1d,
|
||||||
KeypadExclamation: 0xcf,
|
KeypadExclamation: 0xcf,
|
||||||
Minus: 0x2d,
|
Minus: 0x2d,
|
||||||
NumLock: 0x53,
|
None: 0x00,
|
||||||
Numpad0: 0x62,
|
NumLock: 0x53, // and Clear
|
||||||
Numpad1: 0x59,
|
Numpad0: 0x62, // and Insert
|
||||||
Numpad2: 0x5a,
|
Numpad1: 0x59, // and End
|
||||||
Numpad3: 0x5b,
|
Numpad2: 0x5a, // and Down Arrow
|
||||||
Numpad4: 0x5c,
|
Numpad3: 0x5b, // and Page Down
|
||||||
|
Numpad4: 0x5c, // and Left Arrow
|
||||||
Numpad5: 0x5d,
|
Numpad5: 0x5d,
|
||||||
Numpad6: 0x5e,
|
Numpad6: 0x5e, // and Right Arrow
|
||||||
Numpad7: 0x5f,
|
Numpad7: 0x5f, // and Home
|
||||||
Numpad8: 0x60,
|
Numpad8: 0x60, // and Up Arrow
|
||||||
Numpad9: 0x61,
|
Numpad9: 0x61, // and Page Up
|
||||||
NumpadAdd: 0x57,
|
NumpadAdd: 0x57,
|
||||||
|
NumpadComma: 0x85,
|
||||||
|
NumpadDecimal: 0x63,
|
||||||
NumpadDivide: 0x54,
|
NumpadDivide: 0x54,
|
||||||
NumpadEnter: 0x58,
|
NumpadEnter: 0x58,
|
||||||
NumpadEqual: 0x67,
|
NumpadEqual: 0x67,
|
||||||
|
NumpadLeftParen: 0xb6,
|
||||||
NumpadMultiply: 0x55,
|
NumpadMultiply: 0x55,
|
||||||
|
NumpadRightParen: 0xb7,
|
||||||
NumpadSubtract: 0x56,
|
NumpadSubtract: 0x56,
|
||||||
NumpadDecimal: 0x63,
|
|
||||||
PageDown: 0x4e,
|
PageDown: 0x4e,
|
||||||
PageUp: 0x4b,
|
PageUp: 0x4b,
|
||||||
Period: 0x37,
|
Period: 0x37,
|
||||||
PrintScreen: 0x46,
|
PrintScreen: 0x46,
|
||||||
Pause: 0x48,
|
Pause: 0x48,
|
||||||
Quote: 0x34,
|
Power: 0x66,
|
||||||
|
Quote: 0x34, // aka Single Quote or Apostrophe
|
||||||
ScrollLock: 0x47,
|
ScrollLock: 0x47,
|
||||||
Semicolon: 0x33,
|
Semicolon: 0x33,
|
||||||
Slash: 0x38,
|
Slash: 0x38,
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
|
|
||||||
import { Checkbox } from "@/components/Checkbox";
|
|
||||||
import { SettingsPageHeader } from "@/components/SettingsPageheader";
|
|
||||||
import { useSettingsStore } from "@/hooks/stores";
|
|
||||||
|
|
||||||
import { SettingsItem } from "./devices.$id.settings";
|
|
||||||
|
|
||||||
export default function SettingsCtrlAltDelRoute() {
|
|
||||||
const enableCtrlAltDel = useSettingsStore(state => state.actionBarCtrlAltDel);
|
|
||||||
const setEnableCtrlAltDel = useSettingsStore(state => state.setActionBarCtrlAltDel);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="space-y-4">
|
|
||||||
<SettingsPageHeader
|
|
||||||
title="Action Bar"
|
|
||||||
description="Customize the action bar of your JetKVM interface"
|
|
||||||
/>
|
|
||||||
<div className="space-y-4">
|
|
||||||
<SettingsItem title="Enable Ctrl-Alt-Del" description="Enable the Ctrl-Alt-Del key on the virtual keyboard">
|
|
||||||
<Checkbox
|
|
||||||
checked={enableCtrlAltDel}
|
|
||||||
onChange={e => setEnableCtrlAltDel(e.target.checked)}
|
|
||||||
/>
|
|
||||||
</SettingsItem>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -116,15 +116,6 @@ export default function SettingsHardwareRoute() {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</SettingsItem>
|
</SettingsItem>
|
||||||
{/* <SettingsItem
|
|
||||||
title="Enable Ctrl+Alt+Del Action Bar"
|
|
||||||
description="Enable or disable the action bar action for sending a Ctrl+Alt+Del to the host"
|
|
||||||
>
|
|
||||||
<Checkbox
|
|
||||||
checked={actionBarConfig.ctrlAltDel}
|
|
||||||
onChange={onActionBarItemChange("ctrlAltDel")}
|
|
||||||
/>
|
|
||||||
</SettingsItem> */}
|
|
||||||
{settings.backlightSettings.max_brightness != 0 && (
|
{settings.backlightSettings.max_brightness != 0 && (
|
||||||
<>
|
<>
|
||||||
<SettingsItem
|
<SettingsItem
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { KeyboardLedSync, useSettingsStore } from "@/hooks/stores";
|
||||||
import { useJsonRpc } from "@/hooks/useJsonRpc";
|
import { useJsonRpc } from "@/hooks/useJsonRpc";
|
||||||
import notifications from "@/notifications";
|
import notifications from "@/notifications";
|
||||||
import { SettingsPageHeader } from "@components/SettingsPageheader";
|
import { SettingsPageHeader } from "@components/SettingsPageheader";
|
||||||
import { layouts } from "@/keyboardLayouts";
|
import { keyboardOptions } from "@/keyboardLayouts";
|
||||||
import { Checkbox } from "@/components/Checkbox";
|
import { Checkbox } from "@/components/Checkbox";
|
||||||
|
|
||||||
import { SelectMenuBasic } from "../components/SelectMenuBasic";
|
import { SelectMenuBasic } from "../components/SelectMenuBasic";
|
||||||
|
@ -32,7 +32,7 @@ export default function SettingsKeyboardRoute() {
|
||||||
return "en_US";
|
return "en_US";
|
||||||
}, [keyboardLayout]);
|
}, [keyboardLayout]);
|
||||||
|
|
||||||
const layoutOptions = Object.entries(layouts).map(([code, language]) => { return { value: code, label: language } })
|
const layoutOptions = keyboardOptions();
|
||||||
const ledSyncOptions = [
|
const ledSyncOptions = [
|
||||||
{ value: "auto", label: "Automatic" },
|
{ value: "auto", label: "Automatic" },
|
||||||
{ value: "browser", label: "Browser Only" },
|
{ value: "browser", label: "Browser Only" },
|
||||||
|
|
|
@ -79,7 +79,7 @@ export default function SettingsRoute() {
|
||||||
return () => {
|
return () => {
|
||||||
setDisableVideoFocusTrap(false);
|
setDisableVideoFocusTrap(false);
|
||||||
};
|
};
|
||||||
}, [setDisableVideoFocusTrap, sendKeyboardEvent]);
|
}, [sendKeyboardEvent, setDisableVideoFocusTrap]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="pointer-events-auto relative mx-auto max-w-4xl translate-x-0 transform text-left dark:text-white">
|
<div className="pointer-events-auto relative mx-auto max-w-4xl translate-x-0 transform text-left dark:text-white">
|
||||||
|
|
|
@ -707,7 +707,7 @@ export default function KvmIdRoute() {
|
||||||
}, [diskChannel, file]);
|
}, [diskChannel, file]);
|
||||||
|
|
||||||
// System update
|
// System update
|
||||||
const disableKeyboardFocusTrap = useUiStore(state => state.disableVideoFocusTrap);
|
const disableVideoFocusTrap = useUiStore(state => state.disableVideoFocusTrap);
|
||||||
|
|
||||||
const [kvmTerminal, setKvmTerminal] = useState<RTCDataChannel | null>(null);
|
const [kvmTerminal, setKvmTerminal] = useState<RTCDataChannel | null>(null);
|
||||||
const [serialConsole, setSerialConsole] = useState<RTCDataChannel | null>(null);
|
const [serialConsole, setSerialConsole] = useState<RTCDataChannel | null>(null);
|
||||||
|
@ -805,7 +805,7 @@ export default function KvmIdRoute() {
|
||||||
)}
|
)}
|
||||||
<div className="relative h-full">
|
<div className="relative h-full">
|
||||||
<FocusTrap
|
<FocusTrap
|
||||||
paused={disableKeyboardFocusTrap}
|
paused={disableVideoFocusTrap}
|
||||||
focusTrapOptions={{
|
focusTrapOptions={{
|
||||||
allowOutsideClick: true,
|
allowOutsideClick: true,
|
||||||
escapeDeactivates: false,
|
escapeDeactivates: false,
|
||||||
|
|
Loading…
Reference in New Issue