diff --git a/ui/package-lock.json b/ui/package-lock.json index 13c6e99a..f9b95684 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -48,6 +48,7 @@ "@tailwindcss/postcss": "^4.1.12", "@tailwindcss/typography": "^0.5.16", "@tailwindcss/vite": "^4.1.12", + "@types/node": "^22.18.1", "@types/react": "^19.1.12", "@types/react-dom": "^19.1.9", "@types/semver": "^7.7.1", @@ -1980,6 +1981,16 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "license": "MIT" }, + "node_modules/@types/node": { + "version": "22.18.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.18.1.tgz", + "integrity": "sha512-rzSDyhn4cYznVG+PCzGe1lwuMYJrcBS1fc3JqSa2PvtABwWo+dZ1ij5OVok3tqfpEBCBoaR4d7upFJk73HRJDw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, "node_modules/@types/react": { "version": "19.1.12", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.12.tgz", @@ -6790,6 +6801,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "devOptional": true, + "license": "MIT" + }, "node_modules/update-browserslist-db": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", diff --git a/ui/package.json b/ui/package.json index 4c988250..daa9f3bd 100644 --- a/ui/package.json +++ b/ui/package.json @@ -59,6 +59,7 @@ "@tailwindcss/postcss": "^4.1.12", "@tailwindcss/typography": "^0.5.16", "@tailwindcss/vite": "^4.1.12", + "@types/node": "^22.18.1", "@types/react": "^19.1.12", "@types/react-dom": "^19.1.9", "@types/semver": "^7.7.1", diff --git a/ui/src/hooks/useReloadPage.ts b/ui/src/hooks/useReloadPage.ts new file mode 100644 index 00000000..aa84aa1a --- /dev/null +++ b/ui/src/hooks/useReloadPage.ts @@ -0,0 +1,24 @@ +import { useCallback, useEffect, useState } from "react"; + +const isOnDevice = import.meta.env.MODE === "device"; +export function useReloadPage() { + const [pageHash, setPageHash] = useState(""); + + const fetchPageHash = useCallback(async () => { + const response = await fetch("/static-hash.json"); + const data = await response.json(); + setPageHash(data.hash); + }, [setPageHash]); + + useEffect(() => { + if (!isOnDevice) return ; + + const interval = setInterval(() => fetchPageHash(), 1000); + return () => clearInterval(interval); + }, [fetchPageHash]); + + useEffect(() => { + if (!isOnDevice) return ; + + }, [pageHash]); +} \ No newline at end of file diff --git a/ui/vite.config.ts b/ui/vite.config.ts index 44eec3a7..e05886a8 100644 --- a/ui/vite.config.ts +++ b/ui/vite.config.ts @@ -1,8 +1,10 @@ -import { defineConfig } from "vite"; +import { defineConfig, type Plugin } from "vite"; +import type { OutputAsset } from "rollup"; import react from "@vitejs/plugin-react-swc"; import tailwindcss from "@tailwindcss/vite"; import tsconfigPaths from "vite-tsconfig-paths"; import basicSsl from "@vitejs/plugin-basic-ssl"; +import crypto from "node:crypto"; declare const process: { env: { @@ -11,6 +13,37 @@ declare const process: { }; }; +const toHash = (str: string | Uint8Array) => { + return crypto.createHash("sha256").update(str).digest("hex"); +} + +const indexHtmlHashPlugin = (): Plugin => ({ + name: "index-html-hash", + enforce: "post", + generateBundle(options, bundle) { + const indexHtml = bundle["index.html"]; + for (const key in bundle) { + if (key.includes("index-")) { + console.log(bundle[key].originalFileNames); + } + } + if (indexHtml) { + bundle["static-hash.json"] = { + type: "asset", + source: JSON.stringify({ + hash: toHash((indexHtml as OutputAsset).source), + }), + fileName: "static-hash.json", + needsCodeReference: false, + name: undefined, + names: ["static-hash.json"], + originalFileName: null, + originalFileNames: ["static-hash.json"], + }; + } + } +}) + export default defineConfig(({ mode, command }) => { const isCloud = mode.indexOf("cloud") !== -1; const onDevice = mode === "device"; @@ -20,7 +53,8 @@ export default defineConfig(({ mode, command }) => { const plugins = [ tailwindcss(), tsconfigPaths(), - react() + react(), + indexHtmlHashPlugin(), ]; if (useSSL) { plugins.push(basicSsl()); @@ -37,14 +71,14 @@ export default defineConfig(({ mode, command }) => { https: useSSL, proxy: JETKVM_PROXY_URL ? { - "/me": JETKVM_PROXY_URL, - "/device": JETKVM_PROXY_URL, - "/webrtc": JETKVM_PROXY_URL, - "/auth": JETKVM_PROXY_URL, - "/storage": JETKVM_PROXY_URL, - "/cloud": JETKVM_PROXY_URL, - "/developer": JETKVM_PROXY_URL, - } + "/me": JETKVM_PROXY_URL, + "/device": JETKVM_PROXY_URL, + "/webrtc": JETKVM_PROXY_URL, + "/auth": JETKVM_PROXY_URL, + "/storage": JETKVM_PROXY_URL, + "/cloud": JETKVM_PROXY_URL, + "/developer": JETKVM_PROXY_URL, + } : undefined, }, base: onDevice && command === "build" ? "/static" : "/",