PR updates

This commit is contained in:
Marc Brooks 2025-10-11 00:51:03 +00:00
parent 66ab743dfe
commit 774615557c
8 changed files with 134 additions and 24 deletions

112
ui/package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "kvm-ui", "name": "kvm-ui",
"version": "2025.10.09.0200", "version": "2025.10.10.2300",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "kvm-ui", "name": "kvm-ui",
"version": "2025.10.09.0200", "version": "2025.10.10.2300",
"dependencies": { "dependencies": {
"@headlessui/react": "^2.2.9", "@headlessui/react": "^2.2.9",
"@headlessui/tailwindcss": "^0.2.2", "@headlessui/tailwindcss": "^0.2.2",
@ -22,7 +22,7 @@
"dayjs": "^1.11.18", "dayjs": "^1.11.18",
"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.22", "framer-motion": "^12.23.24",
"lodash.throttle": "^4.1.1", "lodash.throttle": "^4.1.1",
"mini-svg-data-uri": "^1.4.4", "mini-svg-data-uri": "^1.4.4",
"react": "^19.2.0", "react": "^19.2.0",
@ -31,7 +31,7 @@
"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.4", "react-router": "^7.9.4",
"react-simple-keyboard": "^3.8.127", "react-simple-keyboard": "^3.8.129",
"react-use-websocket": "^4.13.0", "react-use-websocket": "^4.13.0",
"react-xtermjs": "^1.0.10", "react-xtermjs": "^1.0.10",
"recharts": "^3.2.1", "recharts": "^3.2.1",
@ -62,8 +62,8 @@
"@vitejs/plugin-react-swc": "^4.1.0", "@vitejs/plugin-react-swc": "^4.1.0",
"autoprefixer": "^10.4.21", "autoprefixer": "^10.4.21",
"eslint": "^9.37.0", "eslint": "^9.37.0",
"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-react": "^7.37.5", "eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^7.0.0", "eslint-plugin-react-hooks": "^7.0.0",
"eslint-plugin-react-refresh": "^0.4.23", "eslint-plugin-react-refresh": "^0.4.23",
@ -1274,6 +1274,19 @@
"node": ">= 8" "node": ">= 8"
} }
}, },
"node_modules/@pkgr/core": {
"version": "0.2.9",
"resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz",
"integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^12.20.0 || ^14.18.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/pkgr"
}
},
"node_modules/@react-aria/focus": { "node_modules/@react-aria/focus": {
"version": "3.21.2", "version": "3.21.2",
"resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.21.2.tgz", "resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.21.2.tgz",
@ -4013,6 +4026,8 @@
"integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"optional": true,
"peer": true,
"bin": { "bin": {
"eslint-config-prettier": "bin/cli.js" "eslint-config-prettier": "bin/cli.js"
}, },
@ -4132,6 +4147,37 @@
"semver": "bin/semver.js" "semver": "bin/semver.js"
} }
}, },
"node_modules/eslint-plugin-prettier": {
"version": "5.5.4",
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.4.tgz",
"integrity": "sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==",
"dev": true,
"license": "MIT",
"dependencies": {
"prettier-linter-helpers": "^1.0.0",
"synckit": "^0.11.7"
},
"engines": {
"node": "^14.18.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/eslint-plugin-prettier"
},
"peerDependencies": {
"@types/eslint": ">=8.0.0",
"eslint": ">=8.0.0",
"eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0",
"prettier": ">=3.0.0"
},
"peerDependenciesMeta": {
"@types/eslint": {
"optional": true
},
"eslint-config-prettier": {
"optional": true
}
}
},
"node_modules/eslint-plugin-react": { "node_modules/eslint-plugin-react": {
"version": "7.37.5", "version": "7.37.5",
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz",
@ -4360,6 +4406,13 @@
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/fast-diff": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz",
"integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==",
"dev": true,
"license": "Apache-2.0"
},
"node_modules/fast-glob": { "node_modules/fast-glob": {
"version": "3.3.3", "version": "3.3.3",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
@ -4540,12 +4593,12 @@
} }
}, },
"node_modules/framer-motion": { "node_modules/framer-motion": {
"version": "12.23.22", "version": "12.23.24",
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.22.tgz", "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.24.tgz",
"integrity": "sha512-ZgGvdxXCw55ZYvhoZChTlG6pUuehecgvEAJz0BHoC5pQKW1EC5xf1Mul1ej5+ai+pVY0pylyFfdl45qnM1/GsA==", "integrity": "sha512-HMi5HRoRCTou+3fb3h9oTLyJGBxHfW+HnNE25tAXOvVx/IvwMHK0cx7IR4a2ZU6sh3IX1Z+4ts32PcYBOqka8w==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"motion-dom": "^12.23.21", "motion-dom": "^12.23.23",
"motion-utils": "^12.23.6", "motion-utils": "^12.23.6",
"tslib": "^2.4.0" "tslib": "^2.4.0"
}, },
@ -5869,9 +5922,9 @@
} }
}, },
"node_modules/motion-dom": { "node_modules/motion-dom": {
"version": "12.23.21", "version": "12.23.23",
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.21.tgz", "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.23.tgz",
"integrity": "sha512-5xDXx/AbhrfgsQmSE7YESMn4Dpo6x5/DTZ4Iyy4xqDvVHWvFVoV+V2Ri2S/ksx+D40wrZ7gPYiMWshkdoqNgNQ==", "integrity": "sha512-n5yolOs0TQQBRUFImrRfs/+6X4p3Q4n1dUEqt/H58Vx7OW6RF+foWEgmTVDhIWJIMXOuNNL0apKH2S16en9eiA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"motion-utils": "^12.23.6" "motion-utils": "^12.23.6"
@ -6249,6 +6302,19 @@
"url": "https://github.com/prettier/prettier?sponsor=1" "url": "https://github.com/prettier/prettier?sponsor=1"
} }
}, },
"node_modules/prettier-linter-helpers": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
"integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
"dev": true,
"license": "MIT",
"dependencies": {
"fast-diff": "^1.1.2"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/prettier-plugin-tailwindcss": { "node_modules/prettier-plugin-tailwindcss": {
"version": "0.6.14", "version": "0.6.14",
"resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.6.14.tgz", "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.6.14.tgz",
@ -6498,9 +6564,9 @@
} }
}, },
"node_modules/react-simple-keyboard": { "node_modules/react-simple-keyboard": {
"version": "3.8.127", "version": "3.8.129",
"resolved": "https://registry.npmjs.org/react-simple-keyboard/-/react-simple-keyboard-3.8.127.tgz", "resolved": "https://registry.npmjs.org/react-simple-keyboard/-/react-simple-keyboard-3.8.129.tgz",
"integrity": "sha512-CncdXLnJ3tBlB6iEHtkgj5W21ns/DdKKO1bCy9pLWey5xONf+KAComVVnDsnAaC0b4LLI7frWBDjOT01vj8dew==", "integrity": "sha512-dvZ+LjOAVkFFay8wZsg//VIMKqfr7tCp28scyFgidAufGjJ60yqWUdckTI1xue827DNb/rbiRuQm5B+3GjcEFQ==",
"license": "MIT", "license": "MIT",
"peerDependencies": { "peerDependencies": {
"react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
@ -7107,6 +7173,22 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/synckit": {
"version": "0.11.11",
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz",
"integrity": "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@pkgr/core": "^0.2.9"
},
"engines": {
"node": "^14.18.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/synckit"
}
},
"node_modules/tabbable": { "node_modules/tabbable": {
"version": "6.2.0", "version": "6.2.0",
"resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz",

View File

@ -1,7 +1,7 @@
{ {
"name": "kvm-ui", "name": "kvm-ui",
"private": true, "private": true,
"version": "2025.10.09.0200", "version": "2025.10.10.2300",
"type": "module", "type": "module",
"engines": { "engines": {
"node": "^22.15.0" "node": "^22.15.0"
@ -35,7 +35,7 @@
"dayjs": "^1.11.18", "dayjs": "^1.11.18",
"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.22", "framer-motion": "^12.23.24",
"lodash.throttle": "^4.1.1", "lodash.throttle": "^4.1.1",
"mini-svg-data-uri": "^1.4.4", "mini-svg-data-uri": "^1.4.4",
"react": "^19.2.0", "react": "^19.2.0",
@ -44,7 +44,7 @@
"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.4", "react-router": "^7.9.4",
"react-simple-keyboard": "^3.8.127", "react-simple-keyboard": "^3.8.129",
"react-use-websocket": "^4.13.0", "react-use-websocket": "^4.13.0",
"react-xtermjs": "^1.0.10", "react-xtermjs": "^1.0.10",
"recharts": "^3.2.1", "recharts": "^3.2.1",

View File

@ -255,7 +255,7 @@ function KeyboardWrapper() {
)} )}
</div> </div>
<h2 className="self-center font-sans text-sm leading-none font-medium text-slate-700 select-none dark:text-slate-300"> <h2 className="self-center font-sans text-sm leading-none font-medium text-slate-700 select-none dark:text-slate-300">
m.virtual_keyboard_header() {m.virtual_keyboard_header()}
</h2> </h2>
<div className="absolute right-2 flex items-center gap-x-2"> <div className="absolute right-2 flex items-center gap-x-2">
<div className="hidden md:flex gap-x-2 items-center"> <div className="hidden md:flex gap-x-2 items-center">

View File

@ -70,7 +70,7 @@ export default function DeviceList({
animationDelay: "0.2s", animationDelay: "0.2s",
}} }}
> >
<Button size="SM" theme="blank" text={m.close()} onClick={onCancelWakeOnLanModal} /> <Button size="SM" theme="blank" text={m.close()} onClick={onCancelWakeOnLanModal} />
<Button <Button
size="SM" size="SM"
theme="primary" theme="primary"

View File

@ -245,6 +245,8 @@ export class KeyboardMacroReportMessage extends RpcMessage {
...fromUint32toUint8(this.stepCount), ...fromUint32toUint8(this.stepCount),
]), 0); ]), 0);
let offset = 6;
for (let i = 0; i < this.stepCount; i++) { for (let i = 0; i < this.stepCount; i++) {
const step = this.steps[i]; const step = this.steps[i];
if (!withinUint8Range(step.modifier)) { if (!withinUint8Range(step.modifier)) {
@ -271,8 +273,8 @@ export class KeyboardMacroReportMessage extends RpcMessage {
...fromUint16toUint8(step.delay), ...fromUint16toUint8(step.delay),
]); ]);
const offset = 6 + i * 9;
data.set(macroBinary, offset); data.set(macroBinary, offset);
offset += 9;
} }
return data; return data;

View File

@ -395,7 +395,33 @@ function ErrorBoundary() {
if (error.status === 404) return <NotFoundPage />; if (error.status === 404) return <NotFoundPage />;
} }
const errorMessage: string | null = error?.data?.error?.message ?? error?.message ?? null; const getErrorMessage = (err: unknown): string | null => {
// If it's a route error response, try to read a string at err.data.error.message or err.data.error safely
if (isRouteErrorResponse(err)) {
const data = (err as { data?: unknown }).data;
if (data && typeof data === "object") {
const maybeError = (data as Record<string, unknown>)["error"];
if (maybeError) {
if (typeof maybeError === "object") {
const msg = (maybeError as Record<string, unknown>)["message"];
if (typeof msg === "string") return msg;
} else if (typeof maybeError === "string") {
return maybeError;
}
}
}
}
// Fallback: check plain object message property
if (err && typeof err === "object") {
const maybeMsg = (err as Record<string, unknown>)["message"];
if (typeof maybeMsg === "string") return maybeMsg;
}
return null;
};
const errorMessage = getErrorMessage(error);
return ( return (
<div className="h-full w-full"> <div className="h-full w-full">

View File

@ -8,6 +8,7 @@ import {
} from "react-icons/lu"; } from "react-icons/lu";
import { PlusCircleIcon, ExclamationTriangleIcon } from "@heroicons/react/20/solid"; import { PlusCircleIcon, ExclamationTriangleIcon } from "@heroicons/react/20/solid";
import { TrashIcon } from "@heroicons/react/16/solid"; import { TrashIcon } from "@heroicons/react/16/solid";
import DebianIcon from "@assets/debian-icon.png"; import DebianIcon from "@assets/debian-icon.png";
import UbuntuIcon from "@assets/ubuntu-icon.png"; import UbuntuIcon from "@assets/ubuntu-icon.png";
import FedoraIcon from "@assets/fedora-icon.png"; import FedoraIcon from "@assets/fedora-icon.png";
@ -17,7 +18,6 @@ import NetBootIcon from "@assets/netboot-icon.svg";
import LogoBlueIcon from "@assets/logo-blue.svg"; import LogoBlueIcon from "@assets/logo-blue.svg";
import LogoWhiteIcon from "@assets/logo-white.svg"; import LogoWhiteIcon from "@assets/logo-white.svg";
import { cx } from "@/cva.config"; import { cx } from "@/cva.config";
import { JsonRpcResponse, useJsonRpc } from "@hooks/useJsonRpc"; import { JsonRpcResponse, useJsonRpc } from "@hooks/useJsonRpc";
import AutoHeight from "@components/AutoHeight"; import AutoHeight from "@components/AutoHeight";
import { Button } from "@components/Button"; import { Button } from "@components/Button";