Compare commits

...

7 Commits

Author SHA1 Message Date
Marc Brooks 2ff93e7342
Merge e89491a513 into 28919bf37c 2025-11-06 00:48:29 +00:00
Marc Brooks e89491a513
Change label of cancel button to close if no macro running. 2025-11-05 18:48:22 -06:00
Marc Brooks a9a436cadc
Lower minimum play delay for pastes. 2025-11-05 18:36:41 -06:00
Marc Brooks 71c902c34e
Upgrade UI packages
dayjs 1.11.18 -> 1.11.19
react-hook-form 7.65.0 -> 7.66.0
react-simple-keyboard 3.8.131 -> 3.8.132
validator 13.15.15 -> 13.15.20
@eslint/js 9.39.0 -> 9.39.1
@types/validator 13.15.3 -> 13.15.4
@typescript-eslint/eslint-plugin 8.46.2 -> 8.46.3
@typescript-eslinst/parser 8.46.2 -> 8.46.3
@vitejs/plugin-react-swc 4.2.0 -> 4.2.1
eslint 9.38.0 -> 9.39.1
globals 16.4.0 -> 16.5.0
vite 7.1.12 -> 7.2.0
2025-11-05 15:41:33 -06:00
Marc Brooks 2698fbd541
Fix CoPilot/Lint complaints 2025-11-05 15:26:19 -06:00
Aveline 28919bf37c
fix: await sleep needs to be called inside async function (#946) 2025-11-05 13:25:13 -06:00
Marc Brooks 4090592112
chore: add delay before forced page-reload (#916) 2025-11-04 15:12:03 +01:00
8 changed files with 312 additions and 364 deletions

View File

@ -52,7 +52,6 @@ func GetQueueIndex(messageType MessageType) (int, time.Duration) {
return MacroQueue, 60 * time.Second return MacroQueue, 60 * time.Second
default: default:
return OtherQueue, 5 * time.Second return OtherQueue, 5 * time.Second
} }
} }

619
ui/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
{ {
"name": "kvm-ui", "name": "kvm-ui",
"private": true, "private": true,
"version": "2025.10.24.2140", "version": "2025.11.05.2130",
"type": "module", "type": "module",
"engines": { "engines": {
"node": "^22.20.0" "node": "^22.20.0"
@ -38,7 +38,7 @@
"@xterm/addon-webgl": "^0.18.0", "@xterm/addon-webgl": "^0.18.0",
"@xterm/xterm": "^5.5.0", "@xterm/xterm": "^5.5.0",
"cva": "^1.0.0-beta.4", "cva": "^1.0.0-beta.4",
"dayjs": "^1.11.18", "dayjs": "^1.11.19",
"eslint-import-resolver-alias": "^1.1.2", "eslint-import-resolver-alias": "^1.1.2",
"focus-trap-react": "^11.0.4", "focus-trap-react": "^11.0.4",
"framer-motion": "^12.23.24", "framer-motion": "^12.23.24",
@ -47,24 +47,24 @@
"react": "^19.2.0", "react": "^19.2.0",
"react-animate-height": "^3.2.3", "react-animate-height": "^3.2.3",
"react-dom": "^19.2.0", "react-dom": "^19.2.0",
"react-hook-form": "^7.65.0", "react-hook-form": "^7.66.0",
"react-hot-toast": "^2.6.0", "react-hot-toast": "^2.6.0",
"react-icons": "^5.5.0", "react-icons": "^5.5.0",
"react-router": "^7.9.5", "react-router": "^7.9.5",
"react-simple-keyboard": "^3.8.131", "react-simple-keyboard": "^3.8.132",
"react-use-websocket": "^4.13.0", "react-use-websocket": "^4.13.0",
"react-xtermjs": "^1.0.10", "react-xtermjs": "^1.0.10",
"recharts": "^3.3.0", "recharts": "^3.3.0",
"tailwind-merge": "^3.3.1", "tailwind-merge": "^3.3.1",
"usehooks-ts": "^3.1.1", "usehooks-ts": "^3.1.1",
"uuid": "^13.0.0", "uuid": "^13.0.0",
"validator": "^13.15.15", "validator": "^13.15.20",
"zustand": "^4.5.2" "zustand": "^4.5.2"
}, },
"devDependencies": { "devDependencies": {
"@eslint/compat": "^1.4.1", "@eslint/compat": "^1.4.1",
"@eslint/eslintrc": "^3.3.1", "@eslint/eslintrc": "^3.3.1",
"@eslint/js": "^9.39.0", "@eslint/js": "^9.39.1",
"@inlang/cli": "^3.0.12", "@inlang/cli": "^3.0.12",
"@inlang/paraglide-js": "^2.4.0", "@inlang/paraglide-js": "^2.4.0",
"@inlang/plugin-m-function-matcher": "^2.1.0", "@inlang/plugin-m-function-matcher": "^2.1.0",
@ -77,25 +77,25 @@
"@types/react": "^19.2.2", "@types/react": "^19.2.2",
"@types/react-dom": "^19.2.2", "@types/react-dom": "^19.2.2",
"@types/semver": "^7.7.1", "@types/semver": "^7.7.1",
"@types/validator": "^13.15.3", "@types/validator": "^13.15.4",
"@typescript-eslint/eslint-plugin": "^8.46.2", "@typescript-eslint/eslint-plugin": "^8.46.3",
"@typescript-eslint/parser": "^8.46.2", "@typescript-eslint/parser": "^8.46.3",
"@vitejs/plugin-react-swc": "^4.2.0", "@vitejs/plugin-react-swc": "^4.2.1",
"autoprefixer": "^10.4.21", "autoprefixer": "^10.4.21",
"eslint": "^9.38.0", "eslint": "^9.39.1",
"eslint-config-prettier": "^10.1.8", "eslint-config-prettier": "^10.1.8",
"eslint-plugin-import": "^2.32.0", "eslint-plugin-import": "^2.32.0",
"eslint-plugin-prettier": "^5.5.4", "eslint-plugin-prettier": "^5.5.4",
"eslint-plugin-react": "^7.37.5", "eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.24", "eslint-plugin-react-refresh": "^0.4.24",
"globals": "^16.4.0", "globals": "^16.5.0",
"postcss": "^8.5.6", "postcss": "^8.5.6",
"prettier": "^3.6.2", "prettier": "^3.6.2",
"prettier-plugin-tailwindcss": "^0.7.1", "prettier-plugin-tailwindcss": "^0.7.1",
"tailwindcss": "^4.1.16", "tailwindcss": "^4.1.16",
"typescript": "^5.9.3", "typescript": "^5.9.3",
"vite": "^7.1.12", "vite": "^7.2.0",
"vite-tsconfig-paths": "^5.1.4" "vite-tsconfig-paths": "^5.1.4"
} }
} }

View File

@ -19,7 +19,7 @@ import { TextAreaWithLabel } from "@components/TextArea";
// uint32 max value / 4 // uint32 max value / 4
const pasteMaxLength = 1073741824; const pasteMaxLength = 1073741824;
const defaultDelay = 20; const defaultDelay = 20;
const minimumDelay = 10; const minimumDelay = 5;
const maximumDelay = 65534; const maximumDelay = 65534;
export default function PasteModal() { export default function PasteModal() {
@ -54,10 +54,12 @@ export default function PasteModal() {
}, [send, setKeyboardLayout]); }, [send, setKeyboardLayout]);
const onCancelPasteMode = useCallback(() => { const onCancelPasteMode = useCallback(() => {
cancelExecuteMacro(); if (isPasteInProgress) {
cancelExecuteMacro();
}
setDisableVideoFocusTrap(false); setDisableVideoFocusTrap(false);
setInvalidChars([]); setInvalidChars([]);
}, [setDisableVideoFocusTrap, cancelExecuteMacro]); }, [isPasteInProgress, setDisableVideoFocusTrap, cancelExecuteMacro]);
const onConfirmPaste = useCallback(async () => { const onConfirmPaste = useCallback(async () => {
if (!TextAreaRef.current || !selectedKeyboard) return; if (!TextAreaRef.current || !selectedKeyboard) return;
@ -226,7 +228,7 @@ export default function PasteModal() {
<Button <Button
size="SM" size="SM"
theme="blank" theme="blank"
text={m.cancel()} text={isPasteInProgress ? m.cancel() : m.close()}
onClick={() => { onClick={() => {
onCancelPasteMode(); onCancelPasteMode();
close(); close();

View File

@ -386,7 +386,7 @@ export class CancelKeyboardMacroReportMessage extends RpcMessage {
constructor(token: string) { constructor(token: string) {
super(HID_RPC_MESSAGE_TYPES.CancelKeyboardMacroReport); super(HID_RPC_MESSAGE_TYPES.CancelKeyboardMacroReport);
this.token = (token == null || token === undefined || token === "") this.token = (token == null || token === "")
? "00000000-0000-0000-0000-000000000000" ? "00000000-0000-0000-0000-000000000000"
: token; : token;
} }
@ -397,11 +397,11 @@ export class CancelKeyboardMacroReportMessage extends RpcMessage {
} }
public static unmarshal(data: Uint8Array): CancelKeyboardMacroReportMessage | undefined { public static unmarshal(data: Uint8Array): CancelKeyboardMacroReportMessage | undefined {
if (data.length == 0) { if (data.length === 0) {
return new CancelKeyboardMacroReportMessage("00000000-0000-0000-0000-000000000000"); return new CancelKeyboardMacroReportMessage("00000000-0000-0000-0000-000000000000");
} }
if (data.length != 16) { if (data.length !== 16) {
throw new Error(`Invalid cancel message length: ${data.length}`); throw new Error(`Invalid cancel message length: ${data.length}`);
} }

View File

@ -12,6 +12,7 @@ import { TextAreaWithLabel } from "@components/TextArea";
import { isOnDevice } from "@/main"; import { isOnDevice } from "@/main";
import notifications from "@/notifications"; import notifications from "@/notifications";
import { m } from "@localizations/messages.js"; import { m } from "@localizations/messages.js";
import { sleep } from "@/utils";
export default function SettingsAdvancedRoute() { export default function SettingsAdvancedRoute() {
const { send } = useJsonRpc(); const { send } = useJsonRpc();
@ -311,8 +312,10 @@ export default function SettingsAdvancedRoute() {
size="SM" size="SM"
theme="light" theme="light"
text={m.advanced_reset_config_button()} text={m.advanced_reset_config_button()}
onClick={() => { onClick={async () => {
handleResetConfig(); handleResetConfig();
// Add 2s delay between resetting the configuration and calling reload() to prevent reload from interrupting the RPC call to reset things.
await sleep(2000);
window.location.reload(); window.location.reload();
}} }}
/> />

View File

@ -4,13 +4,16 @@ import { useNavigate } from "react-router";
import { useJsonRpc } from "@hooks/useJsonRpc"; import { useJsonRpc } from "@hooks/useJsonRpc";
import { Button } from "@components/Button"; import { Button } from "@components/Button";
import { m } from "@localizations/messages.js"; import { m } from "@localizations/messages.js";
import { sleep } from "@/utils";
export default function SettingsGeneralRebootRoute() { export default function SettingsGeneralRebootRoute() {
const navigate = useNavigate(); const navigate = useNavigate();
const { send } = useJsonRpc(); const { send } = useJsonRpc();
const onClose = useCallback(() => { const onClose = useCallback(async () => {
navigate(".."); // back to the devices.$id.settings page navigate(".."); // back to the devices.$id.settings page
// Add 1s delay between navigation and calling reload() to prevent reload from interrupting the navigation.
await sleep(1000);
window.location.reload(); // force a full reload to ensure the current device/cloud UI version is loaded window.location.reload(); // force a full reload to ensure the current device/cloud UI version is loaded
}, [navigate]); }, [navigate]);

View File

@ -21,8 +21,10 @@ export default function SettingsGeneralUpdateRoute() {
const { setModalView, otaState } = useUpdateStore(); const { setModalView, otaState } = useUpdateStore();
const { send } = useJsonRpc(); const { send } = useJsonRpc();
const onClose = useCallback(() => { const onClose = useCallback(async () => {
navigate(".."); // back to the devices.$id.settings page navigate(".."); // back to the devices.$id.settings page
// Add 1s delay between navigation and calling reload() to prevent reload from interrupting the navigation.
await sleep(1000);
window.location.reload(); // force a full reload to ensure the current device/cloud UI version is loaded window.location.reload(); // force a full reload to ensure the current device/cloud UI version is loaded
}, [navigate]); }, [navigate]);