add: Initial support for patching.
parent
b1994c0c40
commit
d0241a48b5
13
bun.lock
13
bun.lock
|
|
@ -8,6 +8,7 @@
|
|||
"@radix-ui/react-accordion": "^1.2.12",
|
||||
"@radix-ui/react-checkbox": "^1.3.3",
|
||||
"@radix-ui/react-label": "^2.1.8",
|
||||
"@radix-ui/react-progress": "^1.1.8",
|
||||
"@radix-ui/react-select": "^2.2.6",
|
||||
"@radix-ui/react-separator": "^1.1.8",
|
||||
"@radix-ui/react-slider": "^1.3.6",
|
||||
|
|
@ -15,7 +16,6 @@
|
|||
"@tailwindcss/vite": "^4.1.17",
|
||||
"@tanstack/react-form": "^1.25.0",
|
||||
"@tauri-apps/api": "^2",
|
||||
"@tauri-apps/plugin-notification": "~2",
|
||||
"@tauri-apps/plugin-opener": "^2",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
|
|
@ -24,6 +24,7 @@
|
|||
"react-dom": "^19.1.0",
|
||||
"tailwind-merge": "^3.4.0",
|
||||
"tailwindcss": "^4.1.17",
|
||||
"ts-pattern": "^5.9.0",
|
||||
"zod": "^4.1.12",
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
@ -186,6 +187,8 @@
|
|||
|
||||
"@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
|
||||
|
||||
"@radix-ui/react-progress": ["@radix-ui/react-progress@1.1.8", "", { "dependencies": { "@radix-ui/react-context": "1.1.3", "@radix-ui/react-primitive": "2.1.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-+gISHcSPUJ7ktBy9RnTqbdKW78bcGke3t6taawyZ71pio1JewwGSJizycs7rLhGTvMJYCQB1DBK4KQsxs7U8dA=="],
|
||||
|
||||
"@radix-ui/react-select": ["@radix-ui/react-select@2.2.6", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ=="],
|
||||
|
||||
"@radix-ui/react-separator": ["@radix-ui/react-separator@1.1.8", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-sDvqVY4itsKwwSMEe0jtKgfTh+72Sy3gPmQpjqcQneqQ4PFmr/1I0YA+2/puilhggCe2gJcx5EBAYFkWkdpa5g=="],
|
||||
|
|
@ -330,8 +333,6 @@
|
|||
|
||||
"@tauri-apps/cli-win32-x64-msvc": ["@tauri-apps/cli-win32-x64-msvc@2.9.4", "", { "os": "win32", "cpu": "x64" }, "sha512-EdYd4c9wGvtPB95kqtEyY+bUR+k4kRw3IA30mAQ1jPH6z57AftT8q84qwv0RDp6kkEqOBKxeInKfqi4BESYuqg=="],
|
||||
|
||||
"@tauri-apps/plugin-notification": ["@tauri-apps/plugin-notification@2.3.3", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-Zw+ZH18RJb41G4NrfHgIuofJiymusqN+q8fGUIIV7vyCH+5sSn5coqRv/MWB9qETsUs97vmU045q7OyseCV3Qg=="],
|
||||
|
||||
"@tauri-apps/plugin-opener": ["@tauri-apps/plugin-opener@2.5.2", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-ei/yRRoCklWHImwpCcDK3VhNXx+QXM9793aQ64YxpqVF0BDuuIlXhZgiAkc15wnPVav+IbkYhmDJIv5R326Mew=="],
|
||||
|
||||
"@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="],
|
||||
|
|
@ -470,6 +471,8 @@
|
|||
|
||||
"tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
|
||||
|
||||
"ts-pattern": ["ts-pattern@5.9.0", "", {}, "sha512-6s5V71mX8qBUmlgbrfL33xDUwO0fq48rxAu2LBE11WBeGdpCPOsXksQbZJHvHwhrd3QjUusd3mAOM5Gg0mFBLg=="],
|
||||
|
||||
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"tw-animate-css": ["tw-animate-css@1.4.0", "", {}, "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ=="],
|
||||
|
|
@ -496,6 +499,10 @@
|
|||
|
||||
"@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
|
||||
|
||||
"@radix-ui/react-progress/@radix-ui/react-context": ["@radix-ui/react-context@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ieIFACdMpYfMEjF0rEf5KLvfVyIkOz6PDGyNnP+u+4xQ6jny3VCgA4OgXOwNx2aUkxn8zx9fiVcM8CfFYv9Lxw=="],
|
||||
|
||||
"@radix-ui/react-progress/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.4", "", { "dependencies": { "@radix-ui/react-slot": "1.2.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg=="],
|
||||
|
||||
"@radix-ui/react-select/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
|
||||
|
||||
"@radix-ui/react-separator/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.4", "", { "dependencies": { "@radix-ui/react-slot": "1.2.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg=="],
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Config</title>
|
||||
</head>
|
||||
<body class="dark select-none">
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/config.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -4,9 +4,8 @@
|
|||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Tauri + React + Typescript</title>
|
||||
<title>Patcher</title>
|
||||
</head>
|
||||
|
||||
<body class="dark select-none">
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
"@radix-ui/react-accordion": "^1.2.12",
|
||||
"@radix-ui/react-checkbox": "^1.3.3",
|
||||
"@radix-ui/react-label": "^2.1.8",
|
||||
"@radix-ui/react-progress": "^1.1.8",
|
||||
"@radix-ui/react-select": "^2.2.6",
|
||||
"@radix-ui/react-separator": "^1.1.8",
|
||||
"@radix-ui/react-slider": "^1.3.6",
|
||||
|
|
@ -21,7 +22,6 @@
|
|||
"@tailwindcss/vite": "^4.1.17",
|
||||
"@tanstack/react-form": "^1.25.0",
|
||||
"@tauri-apps/api": "^2",
|
||||
"@tauri-apps/plugin-notification": "~2",
|
||||
"@tauri-apps/plugin-opener": "^2",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
|
|
@ -30,6 +30,7 @@
|
|||
"react-dom": "^19.1.0",
|
||||
"tailwind-merge": "^3.4.0",
|
||||
"tailwindcss": "^4.1.17",
|
||||
"ts-pattern": "^5.9.0",
|
||||
"zod": "^4.1.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
|||
|
|
@ -7,13 +7,19 @@ name = "Config"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"display-info",
|
||||
"futures-util",
|
||||
"memmap2",
|
||||
"rayon",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tauri",
|
||||
"tauri-build",
|
||||
"tauri-plugin-notification",
|
||||
"tauri-plugin-opener",
|
||||
"tokio",
|
||||
"walkdir",
|
||||
"windows 0.62.2",
|
||||
"xxhash-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -505,6 +511,16 @@ dependencies = [
|
|||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.10.1"
|
||||
|
|
@ -528,9 +544,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"core-foundation",
|
||||
"core-foundation 0.10.1",
|
||||
"core-graphics-types",
|
||||
"foreign-types",
|
||||
"foreign-types 0.5.0",
|
||||
"libc",
|
||||
]
|
||||
|
||||
|
|
@ -541,7 +557,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"core-foundation",
|
||||
"core-foundation 0.10.1",
|
||||
"libc",
|
||||
]
|
||||
|
||||
|
|
@ -572,6 +588,25 @@ dependencies = [
|
|||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
|
||||
dependencies = [
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.21"
|
||||
|
|
@ -833,6 +868,12 @@ version = "1.0.20"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||
|
||||
[[package]]
|
||||
name = "embed-resource"
|
||||
version = "3.0.6"
|
||||
|
|
@ -853,6 +894,15 @@ version = "1.2.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7"
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "endi"
|
||||
version = "1.1.0"
|
||||
|
|
@ -975,6 +1025,15 @@ version = "1.0.7"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
|
||||
dependencies = [
|
||||
"foreign-types-shared 0.1.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types"
|
||||
version = "0.5.0"
|
||||
|
|
@ -982,7 +1041,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965"
|
||||
dependencies = [
|
||||
"foreign-types-macros",
|
||||
"foreign-types-shared",
|
||||
"foreign-types-shared 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -996,6 +1055,12 @@ dependencies = [
|
|||
"syn 2.0.110",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types-shared"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types-shared"
|
||||
version = "0.3.1"
|
||||
|
|
@ -1028,6 +1093,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1406,6 +1472,25 @@ dependencies = [
|
|||
"syn 2.0.110",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386"
|
||||
dependencies = [
|
||||
"atomic-waker",
|
||||
"bytes",
|
||||
"fnv",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"http",
|
||||
"indexmap 2.12.0",
|
||||
"slab",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.3"
|
||||
|
|
@ -1504,6 +1589,7 @@ dependencies = [
|
|||
"bytes",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"h2",
|
||||
"http",
|
||||
"http-body",
|
||||
"httparse",
|
||||
|
|
@ -1515,6 +1601,38 @@ dependencies = [
|
|||
"want",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-rustls"
|
||||
version = "0.27.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58"
|
||||
dependencies = [
|
||||
"http",
|
||||
"hyper",
|
||||
"hyper-util",
|
||||
"rustls",
|
||||
"rustls-pki-types",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-tls"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
"hyper-util",
|
||||
"native-tls",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-util"
|
||||
version = "0.1.18"
|
||||
|
|
@ -1534,9 +1652,11 @@ dependencies = [
|
|||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
"system-configuration",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
"windows-registry",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1943,18 +2063,6 @@ version = "0.1.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
|
||||
|
||||
[[package]]
|
||||
name = "mac-notification-sys"
|
||||
version = "0.6.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ee70bb2bba058d58e252d2944582d634fc884fc9c489a966d428dedcf653e97"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"objc2 0.6.3",
|
||||
"objc2-foundation 0.3.2",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "markup5ever"
|
||||
version = "0.14.1"
|
||||
|
|
@ -2058,6 +2166,23 @@ dependencies = [
|
|||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "native-tls"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"openssl",
|
||||
"openssl-probe",
|
||||
"openssl-sys",
|
||||
"schannel",
|
||||
"security-framework",
|
||||
"security-framework-sys",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ndk"
|
||||
version = "0.9.0"
|
||||
|
|
@ -2113,20 +2238,6 @@ version = "0.1.14"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
|
||||
|
||||
[[package]]
|
||||
name = "notify-rust"
|
||||
version = "4.11.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6442248665a5aa2514e794af3b39661a8e73033b1cc5e59899e1276117ee4400"
|
||||
dependencies = [
|
||||
"futures-lite",
|
||||
"log",
|
||||
"mac-notification-sys",
|
||||
"serde",
|
||||
"tauri-winrt-notification",
|
||||
"zbus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-conv"
|
||||
version = "0.1.0"
|
||||
|
|
@ -2462,6 +2573,50 @@ dependencies = [
|
|||
"pathdiff",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.75"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"cfg-if",
|
||||
"foreign-types 0.3.2",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"openssl-macros",
|
||||
"openssl-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-macros"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.110",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-probe"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.111"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "option-ext"
|
||||
version = "0.2.0"
|
||||
|
|
@ -2912,16 +3067,6 @@ dependencies = [
|
|||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
|
||||
dependencies = [
|
||||
"rand_chacha 0.9.0",
|
||||
"rand_core 0.9.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.2.2"
|
||||
|
|
@ -2942,16 +3087,6 @@ dependencies = [
|
|||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core 0.9.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.5.1"
|
||||
|
|
@ -2970,15 +3105,6 @@ dependencies = [
|
|||
"getrandom 0.2.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
|
||||
dependencies = [
|
||||
"getrandom 0.3.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.2.0"
|
||||
|
|
@ -3003,6 +3129,26 @@ version = "0.6.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539"
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rayon-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.18"
|
||||
|
|
@ -3080,22 +3226,31 @@ checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f"
|
|||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
"encoding_rs",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"h2",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
"hyper-rustls",
|
||||
"hyper-tls",
|
||||
"hyper-util",
|
||||
"js-sys",
|
||||
"log",
|
||||
"mime",
|
||||
"native-tls",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"rustls-pki-types",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"sync_wrapper",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tokio-util",
|
||||
"tower",
|
||||
"tower-http",
|
||||
|
|
@ -3107,6 +3262,20 @@ dependencies = [
|
|||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.17.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"getrandom 0.2.16",
|
||||
"libc",
|
||||
"untrusted",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.1"
|
||||
|
|
@ -3129,6 +3298,39 @@ dependencies = [
|
|||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.23.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"rustls-pki-types",
|
||||
"rustls-webpki",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pki-types"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a"
|
||||
dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.103.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.22"
|
||||
|
|
@ -3150,6 +3352,15 @@ dependencies = [
|
|||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "schannel"
|
||||
version = "0.1.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "schemars"
|
||||
version = "0.8.22"
|
||||
|
|
@ -3207,6 +3418,29 @@ version = "1.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "2.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"core-foundation 0.9.4",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
"security-framework-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "security-framework-sys"
|
||||
version = "2.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "selectors"
|
||||
version = "0.24.0"
|
||||
|
|
@ -3505,7 +3739,7 @@ dependencies = [
|
|||
"bytemuck",
|
||||
"cfg_aliases",
|
||||
"core-graphics",
|
||||
"foreign-types",
|
||||
"foreign-types 0.5.0",
|
||||
"js-sys",
|
||||
"log",
|
||||
"objc2 0.5.2",
|
||||
|
|
@ -3587,6 +3821,12 @@ version = "0.11.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||
|
||||
[[package]]
|
||||
name = "swift-rs"
|
||||
version = "1.0.7"
|
||||
|
|
@ -3640,6 +3880,27 @@ dependencies = [
|
|||
"syn 2.0.110",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-configuration"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"core-foundation 0.9.4",
|
||||
"system-configuration-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-configuration-sys"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-deps"
|
||||
version = "6.2.2"
|
||||
|
|
@ -3661,7 +3922,7 @@ checksum = "f3a753bdc39c07b192151523a3f77cd0394aa75413802c883a0f6f6a0e5ee2e7"
|
|||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"block2 0.6.2",
|
||||
"core-foundation",
|
||||
"core-foundation 0.10.1",
|
||||
"core-graphics",
|
||||
"crossbeam-channel",
|
||||
"dispatch",
|
||||
|
|
@ -3841,25 +4102,6 @@ dependencies = [
|
|||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-notification"
|
||||
version = "2.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01fc2c5ff41105bd1f7242d8201fdf3efd70749b82fa013a17f2126357d194cc"
|
||||
dependencies = [
|
||||
"log",
|
||||
"notify-rust",
|
||||
"rand 0.9.2",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_repr",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"thiserror 2.0.17",
|
||||
"time",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-opener"
|
||||
version = "2.5.2"
|
||||
|
|
@ -3983,18 +4225,6 @@ dependencies = [
|
|||
"toml 0.9.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-winrt-notification"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b1e66e07de489fe43a46678dd0b8df65e0c973909df1b60ba33874e297ba9b9"
|
||||
dependencies = [
|
||||
"quick-xml 0.37.5",
|
||||
"thiserror 2.0.17",
|
||||
"windows 0.61.3",
|
||||
"windows-version",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.23.0"
|
||||
|
|
@ -4109,11 +4339,45 @@ dependencies = [
|
|||
"bytes",
|
||||
"libc",
|
||||
"mio",
|
||||
"parking_lot",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.110",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-native-tls"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
|
||||
dependencies = [
|
||||
"native-tls",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-rustls"
|
||||
version = "0.26.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61"
|
||||
dependencies = [
|
||||
"rustls",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.17"
|
||||
|
|
@ -4403,6 +4667,12 @@ version = "1.12.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.7"
|
||||
|
|
@ -4451,6 +4721,12 @@ dependencies = [
|
|||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
|
||||
[[package]]
|
||||
name = "version-compare"
|
||||
version = "0.2.1"
|
||||
|
|
@ -4999,6 +5275,17 @@ dependencies = [
|
|||
"windows-link 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-registry"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720"
|
||||
dependencies = [
|
||||
"windows-link 0.2.1",
|
||||
"windows-result 0.4.1",
|
||||
"windows-strings 0.5.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.3.4"
|
||||
|
|
@ -5044,6 +5331,15 @@ dependencies = [
|
|||
"windows-targets 0.42.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
|
|
@ -5413,6 +5709,12 @@ version = "0.2.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56"
|
||||
|
||||
[[package]]
|
||||
name = "xxhash-rust"
|
||||
version = "0.8.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3"
|
||||
|
||||
[[package]]
|
||||
name = "yoke"
|
||||
version = "0.8.1"
|
||||
|
|
@ -5538,6 +5840,12 @@ dependencies = [
|
|||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
|
||||
|
||||
[[package]]
|
||||
name = "zerotrie"
|
||||
version = "0.2.3"
|
||||
|
|
|
|||
|
|
@ -23,7 +23,13 @@ tauri-plugin-opener = "2"
|
|||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
display-info = "0.5.7"
|
||||
tauri-plugin-notification = "2"
|
||||
xxhash-rust = { version = "0.8.15", features = ["xxh3"] }
|
||||
reqwest = { version = "0.12", features = ["json", "stream", "blocking"] }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
rayon = "1.10"
|
||||
memmap2 = "0.9"
|
||||
futures-util = "0.3"
|
||||
walkdir = "2.3"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
windows = { version = "0.62", features = ["Win32_Graphics_Direct3D9"] }
|
||||
|
|
|
|||
|
|
@ -2,14 +2,11 @@
|
|||
"$schema": "../gen/schemas/desktop-schema.json",
|
||||
"identifier": "default",
|
||||
"description": "Capability for the main window",
|
||||
"windows": [
|
||||
"main"
|
||||
],
|
||||
"windows": ["main", "config"],
|
||||
"permissions": [
|
||||
"core:default",
|
||||
"opener:default",
|
||||
"core:window:default",
|
||||
"core:window:allow-start-dragging",
|
||||
"notification:default"
|
||||
"core:window:allow-start-dragging"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,29 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use std::fs;
|
||||
use std::{fs, fs::File, sync::Mutex};
|
||||
use std::path::PathBuf;
|
||||
use tauri::Manager;
|
||||
use tauri_plugin_notification::NotificationExt;
|
||||
use std::collections::HashMap;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use futures_util::StreamExt;
|
||||
use tauri::{AppHandle, Emitter, Manager};
|
||||
use xxhash_rust::xxh3::xxh3_64;
|
||||
use rayon::prelude::*;
|
||||
use memmap2::Mmap;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
pub const DIR: &str = if cfg!(debug_assertions) {
|
||||
if cfg!(target_os = "windows") {
|
||||
"C:\\Users\\Ego\\Desktop\\test\\"
|
||||
} else {
|
||||
"/home/ego/Documents/metin2/client/dist/"
|
||||
}
|
||||
} else {
|
||||
"./"
|
||||
};
|
||||
|
||||
// TODO HANDLE ERRORS PROPERLY
|
||||
// TODO SUPPORT REGEX
|
||||
// TODO SUPPORT ALL FILE MOVES
|
||||
pub const SERVER: &str = "http://192.168.31.60:8080";
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Config {
|
||||
|
|
@ -22,19 +43,8 @@ pub struct Config {
|
|||
pub fog_mode_on: bool,
|
||||
pub camera_mode: CameraMode,
|
||||
pub private_shop: PrivateShop,
|
||||
// pub object_culling: bool,
|
||||
// pub visibility: i32,
|
||||
// pub software_cursor: bool,
|
||||
// pub is_save_id: i32,
|
||||
// pub save_id: i32,
|
||||
// pub preloading_delay_time: i32,
|
||||
// pub decompressed_texture: bool,
|
||||
// pub use_default_ime: bool,
|
||||
// pub software_tiling: i32,
|
||||
}
|
||||
|
||||
// ------------------ Enums ------------------
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum Locale {
|
||||
|
|
@ -137,7 +147,7 @@ impl Default for Config {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_config_path(_app: &tauri::AppHandle) -> PathBuf {
|
||||
fn get_config_path(_app: &AppHandle) -> PathBuf {
|
||||
std::env::current_exe()
|
||||
.expect("Failed to get executable path")
|
||||
.parent()
|
||||
|
|
@ -147,7 +157,7 @@ fn get_config_path(_app: &tauri::AppHandle) -> PathBuf {
|
|||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn get_config(app: tauri::AppHandle) -> Config {
|
||||
fn get_config(app: AppHandle) -> Config {
|
||||
let config_path = get_config_path(&app);
|
||||
|
||||
match fs::read_to_string(&config_path) {
|
||||
|
|
@ -160,39 +170,39 @@ fn get_config(app: tauri::AppHandle) -> Config {
|
|||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn set_config(config: Config, app: tauri::AppHandle) -> bool {
|
||||
fn set_config(config: Config, app: AppHandle) -> bool {
|
||||
let config_path = get_config_path(&app);
|
||||
|
||||
match serde_json::to_string_pretty(&config) {
|
||||
Ok(json) => match fs::write(&config_path, json) {
|
||||
Ok(_) => {
|
||||
app.notification()
|
||||
.builder()
|
||||
.title("Configuration")
|
||||
.body("Saved Metin2 client settings")
|
||||
.show()
|
||||
.unwrap();
|
||||
// app.notification()
|
||||
// .builder()
|
||||
// .title("Configuration")
|
||||
// .body("Saved Metin2 client settings")
|
||||
// .show()
|
||||
// .unwrap();
|
||||
|
||||
true
|
||||
}
|
||||
Err(e) => {
|
||||
app.notification()
|
||||
.builder()
|
||||
.title("Configuration")
|
||||
.body(&format!("Could not save config: {}", e))
|
||||
.show()
|
||||
.unwrap();
|
||||
Err(_e) => {
|
||||
// app.notification()
|
||||
// .builder()
|
||||
// .title("Configuration")
|
||||
// .body(&format!("Could not save config: {}", e))
|
||||
// .show()
|
||||
// .unwrap();
|
||||
|
||||
false
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
app.notification()
|
||||
.builder()
|
||||
.title("Configuration")
|
||||
.body(&format!("Could not serialize config: {}", e))
|
||||
.show()
|
||||
.unwrap();
|
||||
Err(_e) => {
|
||||
// app.notification()
|
||||
// .builder()
|
||||
// .title("Configuration")
|
||||
// .body(&format!("Could not serialize config: {}", e))
|
||||
// .show()
|
||||
// .unwrap();
|
||||
|
||||
false
|
||||
}
|
||||
|
|
@ -289,20 +299,297 @@ fn get_resolutions() -> Vec<Resolution> {
|
|||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn close(app: tauri::AppHandle) {
|
||||
fn close(app: AppHandle) {
|
||||
app.exit(0);
|
||||
}
|
||||
|
||||
pub fn pack_hash(app: AppHandle) -> std::io::Result<Vec<(String, u64)>> {
|
||||
let entries: Vec<PathBuf> = WalkDir::new(DIR)
|
||||
.into_iter()
|
||||
.filter_map(|e| e.ok())
|
||||
.filter(|e| e.path().is_file())
|
||||
.map(|e| e.path().to_path_buf())
|
||||
.collect();
|
||||
|
||||
let results = Mutex::new(Vec::with_capacity(entries.len()));
|
||||
|
||||
entries.par_iter().try_for_each(|path| -> std::io::Result<()> {
|
||||
// Relative path to DIR
|
||||
let rel_path = path.strip_prefix(DIR)
|
||||
.unwrap()
|
||||
.to_string_lossy()
|
||||
.replace("\\", "/"); // normalize
|
||||
|
||||
app.emit("file", &rel_path)
|
||||
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
|
||||
|
||||
let file = File::open(path)?;
|
||||
let mmap = unsafe { Mmap::map(&file)? };
|
||||
let hash = xxh3_64(&mmap);
|
||||
|
||||
results.lock().unwrap().push((rel_path, hash));
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(results.into_inner().unwrap())
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct ChecksumResponse {
|
||||
checksums: HashMap<String, String>,
|
||||
}
|
||||
|
||||
async fn fetch_server_checksums() -> Result<HashMap<String, u64>, String> {
|
||||
let response = reqwest::get(format!("{}/checksum", SERVER))
|
||||
.await
|
||||
.map_err(|e| format!("Failed to fetch checksums: {}", e))?;
|
||||
|
||||
let parsed: ChecksumResponse = response
|
||||
.json()
|
||||
.await
|
||||
.map_err(|e| format!("Failed to parse checksums: {}", e))?;
|
||||
|
||||
let mut out = HashMap::new();
|
||||
|
||||
for (name, hash_str) in parsed.checksums {
|
||||
let hash = hash_str
|
||||
.parse::<u64>()
|
||||
.map_err(|_| format!("Invalid checksum number for file: {}", name))?;
|
||||
|
||||
// Normalize separators
|
||||
let normalized_name = name.replace("\\", "/");
|
||||
out.insert(normalized_name, hash);
|
||||
}
|
||||
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[serde(into = "u8", try_from = "u8")]
|
||||
pub enum Status {
|
||||
Awaiting = 0,
|
||||
Fetching = 1,
|
||||
Checksumming = 2,
|
||||
Verifying = 3,
|
||||
Updating = 4,
|
||||
Ready = 5,
|
||||
}
|
||||
|
||||
impl From<Status> for u8 {
|
||||
fn from(status: Status) -> Self {
|
||||
status as u8
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for Status {
|
||||
type Error = String;
|
||||
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
0 => Ok(Status::Awaiting),
|
||||
1 => Ok(Status::Fetching),
|
||||
2 => Ok(Status::Checksumming),
|
||||
3 => Ok(Status::Verifying),
|
||||
4 => Ok(Status::Updating),
|
||||
5 => Ok(Status::Ready),
|
||||
_ => Err(format!("Invalid status value: {}", value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn download_file(app_handle: &AppHandle, url: &str, dest_path: &str, file_name: &str) -> Result<(), String> {
|
||||
if tokio::fs::metadata(dest_path).await.is_ok() {
|
||||
let _ = tokio::fs::remove_file(dest_path).await;
|
||||
}
|
||||
|
||||
let client = reqwest::Client::new();
|
||||
let response = client.get(url)
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| format!("Failed to download {}: {}", file_name, e))?;
|
||||
|
||||
let total_size = response
|
||||
.content_length()
|
||||
.ok_or_else(|| format!("Failed to get content length for {}", file_name))?;
|
||||
|
||||
let mut stream = response.bytes_stream();
|
||||
let mut file = tokio::fs::File::create(dest_path)
|
||||
.await
|
||||
.map_err(|e| format!("Failed to create file {}: {}", dest_path, e))?;
|
||||
|
||||
let mut downloaded: u64 = 0;
|
||||
|
||||
while let Some(chunk) = stream.next().await {
|
||||
let chunk = chunk
|
||||
.map_err(|e| format!("Error while downloading {}: {}", file_name, e))?;
|
||||
|
||||
file.write_all(&chunk)
|
||||
.await
|
||||
.map_err(|e| format!("Failed to write {}: {}", dest_path, e))?;
|
||||
|
||||
downloaded += chunk.len() as u64;
|
||||
|
||||
// Emit progress safely
|
||||
if let Err(e) = app_handle.emit("download", serde_json::json!({
|
||||
"file": file_name,
|
||||
"downloaded": downloaded,
|
||||
"total": total_size,
|
||||
"percent": (downloaded as f64 / total_size as f64 * 100.0) as u8
|
||||
})) {
|
||||
eprintln!("Failed to emit download progress: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
pub struct WindowState {
|
||||
config_open: Mutex<bool>,
|
||||
}
|
||||
|
||||
impl Default for WindowState {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
config_open: Mutex::new(false),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn open_config(app: AppHandle) -> Result<(), String> {
|
||||
let state = app.state::<WindowState>();
|
||||
let mut is_open = state.config_open.lock().unwrap();
|
||||
|
||||
if *is_open {
|
||||
if let Some(window) = app.get_webview_window("config") {
|
||||
let _ = window.close();
|
||||
return Ok(());
|
||||
} else {
|
||||
*is_open = false;
|
||||
}
|
||||
}
|
||||
|
||||
*is_open = true;
|
||||
|
||||
let window = tauri::WebviewWindowBuilder::new(
|
||||
&app,
|
||||
"config",
|
||||
tauri::WebviewUrl::App("config.html".into())
|
||||
)
|
||||
.title("Configuration")
|
||||
.inner_size(800.0, 600.0)
|
||||
.resizable(false)
|
||||
.decorations(false)
|
||||
.visible(true)
|
||||
.build()
|
||||
.map_err(|e| format!("Failed to create config window: {}", e))?;
|
||||
|
||||
let app_handle = app.clone();
|
||||
window.on_window_event(move |event| {
|
||||
if let tauri::WindowEvent::Destroyed = event {
|
||||
let state = app_handle.state::<WindowState>();
|
||||
*state.config_open.lock().unwrap() = false;
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn run() {
|
||||
tauri::Builder::default()
|
||||
.plugin(tauri_plugin_notification::init())
|
||||
.plugin(tauri_plugin_opener::init())
|
||||
.manage(WindowState::default())
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
open_config,
|
||||
get_resolutions,
|
||||
get_config,
|
||||
set_config,
|
||||
close,
|
||||
])
|
||||
.setup(|app| {
|
||||
let app_handle = app.handle().clone();
|
||||
|
||||
let _ = fs::create_dir_all(DIR);
|
||||
|
||||
tauri::async_runtime::spawn(async move {
|
||||
let _ = app_handle.emit("status", Status::Fetching).unwrap();
|
||||
|
||||
let server_hashes = match fetch_server_checksums().await {
|
||||
Ok(hashes) => hashes,
|
||||
Err(e) => {
|
||||
let _ = app_handle.emit("error", e.to_string()).unwrap();
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let _ = app_handle.emit("status", Status::Checksumming).unwrap();
|
||||
|
||||
let local_hashes = match tokio::task::spawn_blocking({
|
||||
let app_handle = app_handle.clone();
|
||||
move || pack_hash(app_handle)
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(Ok(hashes)) => hashes,
|
||||
Ok(Err(e)) => {
|
||||
let _ = app_handle.emit("error", e.to_string()).unwrap();
|
||||
return;
|
||||
}
|
||||
Err(e) => {
|
||||
let _ = app_handle.emit("error", e.to_string()).unwrap();
|
||||
return;
|
||||
}
|
||||
};
|
||||
let local_map: HashMap<String, u64> = local_hashes.into_iter().collect();
|
||||
let _ = app_handle.emit("status", Status::Verifying).unwrap();
|
||||
|
||||
let mut updatable: HashMap<String, u64> = HashMap::new();
|
||||
|
||||
for (filename, server_hash) in &server_hashes {
|
||||
match local_map.get(filename) {
|
||||
Some(local_hash) => {
|
||||
if local_hash != server_hash {
|
||||
updatable.insert(filename.clone(), *server_hash);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
updatable.insert(filename.clone(), *server_hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !updatable.is_empty() {
|
||||
let _ = app_handle.emit("status", Status::Updating).unwrap();
|
||||
|
||||
for (file, _hash) in &updatable {
|
||||
let url = format!("{}/{}", SERVER, file);
|
||||
|
||||
let mut dest_path = PathBuf::from(DIR);
|
||||
dest_path.push(file);
|
||||
|
||||
if let Some(parent) = dest_path.parent() {
|
||||
if let Err(e) = fs::create_dir_all(parent) {
|
||||
let _ = app_handle.emit("error", format!("Failed to create dir: {}", e.to_string()));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
match download_file(&app_handle, &url, dest_path.to_str().unwrap(), file).await {
|
||||
Ok(_) => {},
|
||||
Err(e) => {
|
||||
let _ = app_handle.emit("error", e.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let _ = app_handle.emit("status", Status::Ready).unwrap();
|
||||
});
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,9 +14,9 @@
|
|||
"app": {
|
||||
"windows": [
|
||||
{
|
||||
"title": "Configuration",
|
||||
"width": 800,
|
||||
"height": 600,
|
||||
"title": "Patcher",
|
||||
"width": 600,
|
||||
"height": 300,
|
||||
"resizable": false,
|
||||
"decorations": false
|
||||
}
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 2.5 MiB |
|
|
@ -0,0 +1,40 @@
|
|||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { CogIcon, XIcon } from "lucide-react";
|
||||
import { useCallback } from "react";
|
||||
|
||||
export default function DragBar({ isMain }: { isMain?: boolean }) {
|
||||
const close = useCallback(async () => {
|
||||
if (isMain) {
|
||||
await invoke("close");
|
||||
return;
|
||||
}
|
||||
|
||||
await invoke("open_config");
|
||||
}, [isMain]);
|
||||
|
||||
const config = useCallback(async () => {
|
||||
await invoke("open_config");
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="w-full grid grid-cols-[1fr_auto_auto] fixed top-0 inset-x-0 z-50 gap-px">
|
||||
<div data-tauri-drag-region />
|
||||
{isMain && (
|
||||
<button
|
||||
title="Config"
|
||||
onClick={config}
|
||||
className="bg-muted/20 hover:bg-gray-400/40 transition-colors py-1.5 px-4 shrink-0"
|
||||
>
|
||||
<CogIcon className="size-5" />
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
title="Close"
|
||||
onClick={close}
|
||||
className="bg-muted/20 hover:bg-red-800/40 transition-colors py-1.5 px-4 shrink-0"
|
||||
>
|
||||
<XIcon className="size-5" />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
import * as React from "react"
|
||||
import * as ProgressPrimitive from "@radix-ui/react-progress"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Progress({
|
||||
className,
|
||||
value,
|
||||
...props
|
||||
}: React.ComponentProps<typeof ProgressPrimitive.Root>) {
|
||||
return (
|
||||
<ProgressPrimitive.Root
|
||||
data-slot="progress"
|
||||
className={cn(
|
||||
"bg-primary/20 relative h-2 w-full overflow-hidden rounded-full",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ProgressPrimitive.Indicator
|
||||
data-slot="progress-indicator"
|
||||
className="bg-primary h-full w-full flex-1 transition-all"
|
||||
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
|
||||
/>
|
||||
</ProgressPrimitive.Root>
|
||||
)
|
||||
}
|
||||
|
||||
export { Progress }
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import App from "./pages/config";
|
||||
import "./index.css";
|
||||
|
||||
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
);
|
||||
165
src/index.css
165
src/index.css
|
|
@ -3,6 +3,102 @@
|
|||
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
|
||||
:root {
|
||||
--background: oklch(1 0 0);
|
||||
--foreground: oklch(0.145 0 0);
|
||||
--card: oklch(1 0 0);
|
||||
--card-foreground: oklch(0.145 0 0);
|
||||
--popover: oklch(1 0 0);
|
||||
--popover-foreground: oklch(0.145 0 0);
|
||||
--primary: oklch(0.205 0 0);
|
||||
--primary-foreground: oklch(0.985 0 0);
|
||||
--secondary: oklch(0.97 0 0);
|
||||
--secondary-foreground: oklch(0.205 0 0);
|
||||
--muted: oklch(0.97 0 0);
|
||||
--muted-foreground: oklch(0.556 0 0);
|
||||
--accent: oklch(0.97 0 0);
|
||||
--accent-foreground: oklch(0.205 0 0);
|
||||
--destructive: oklch(0.577 0.245 27.325);
|
||||
--destructive-foreground: oklch(1 0 0);
|
||||
--border: oklch(0.922 0 0);
|
||||
--input: oklch(0.922 0 0);
|
||||
--ring: oklch(0.708 0 0);
|
||||
--chart-1: oklch(87% 0 0);
|
||||
--chart-2: oklch(70.8% 0 0);
|
||||
--chart-3: oklch(55.6% 0 0);
|
||||
--chart-4: oklch(43.9% 0 0);
|
||||
--chart-5: oklch(37.1% 0 0);
|
||||
--sidebar: oklch(0.985 0 0);
|
||||
--sidebar-foreground: oklch(0.145 0 0);
|
||||
--sidebar-primary: oklch(0.205 0 0);
|
||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||
--sidebar-accent: oklch(0.97 0 0);
|
||||
--sidebar-accent-foreground: oklch(0.205 0 0);
|
||||
--sidebar-border: oklch(0.922 0 0);
|
||||
--sidebar-ring: oklch(0.708 0 0);
|
||||
--header: oklch(1 0 0);
|
||||
--header-foreground: oklch(0.145 0 0);
|
||||
--footer: oklch(1 0 0);
|
||||
--footer-foreground: oklch(0.145 0 0);
|
||||
--code: oklch(1 0 0);
|
||||
--code-foreground: oklch(0.708 0 0);
|
||||
--code-highlight: oklch(0.27 0 0);
|
||||
--code-number: oklch(0.72 0 0);
|
||||
--code-selection: oklch(0.922 0 0);
|
||||
--code-border: oklch(0.922 0 0);
|
||||
--radius: 0rem;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: oklch(0.145 0 0);
|
||||
--foreground: oklch(0.985 0 0);
|
||||
--card: oklch(0.205 0 0);
|
||||
--card-foreground: oklch(0.985 0 0);
|
||||
--popover: oklch(0.269 0 0);
|
||||
--popover-foreground: oklch(0.985 0 0);
|
||||
--primary: oklch(0.722 0.09 222.3);
|
||||
--primary-foreground: oklch(0.1134 0 0);
|
||||
--secondary: oklch(0.722 0.09 0);
|
||||
--secondary-foreground: oklch(0.985 0 0);
|
||||
--muted: oklch(0.269 0 0);
|
||||
--muted-foreground: oklch(0.708 0 0);
|
||||
--accent: oklch(0.371 0 0);
|
||||
--accent-foreground: oklch(0.985 0 0);
|
||||
--destructive: oklch(0.704 0.191 22.216);
|
||||
--destructive-foreground: oklch(0.985 0 0);
|
||||
--border: oklch(1 0 0 / 10%);
|
||||
--input: oklch(1 0 0 / 15%);
|
||||
--ring: oklch(0.556 0 0);
|
||||
--chart-1: oklch(87% 0 0);
|
||||
--chart-2: oklch(70.8% 0 0);
|
||||
--chart-3: oklch(55.6% 0 0);
|
||||
--chart-4: oklch(43.9% 0 0);
|
||||
--chart-5: oklch(37.1% 0 0);
|
||||
--sidebar: oklch(0.205 0 0);
|
||||
--sidebar-foreground: oklch(0.985 0 0);
|
||||
--sidebar-primary: oklch(0.488 0.243 264.376);
|
||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||
--sidebar-accent: oklch(0.269 0 0);
|
||||
--sidebar-accent-foreground: oklch(0.985 0 0);
|
||||
--sidebar-border: oklch(1 0 0 / 10%);
|
||||
--sidebar-ring: oklch(0.439 0 0);
|
||||
--header: oklch(0.145 0 0);
|
||||
--header-foreground: oklch(0.985 0 0);
|
||||
--footer: oklch(0.145 0 0);
|
||||
--footer-foreground: oklch(0.985 0 0);
|
||||
--code: oklch(0.2 0 0);
|
||||
--code-foreground: oklch(0.708 0 0);
|
||||
--code-highlight: oklch(0.27 0 0);
|
||||
--code-number: oklch(0.72 0 0);
|
||||
--code-selection: oklch(0.922 0 0);
|
||||
--code-border: oklch(1 0 0 / 10%);
|
||||
--radius: 0rem;
|
||||
}
|
||||
|
||||
.container {
|
||||
@apply mx-auto;
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
--radius-sm: calc(var(--radius) - 4px);
|
||||
--radius-md: calc(var(--radius) - 2px);
|
||||
|
|
@ -41,75 +137,6 @@
|
|||
--color-sidebar-ring: var(--sidebar-ring);
|
||||
}
|
||||
|
||||
:root {
|
||||
--radius: 0.625rem;
|
||||
--background: oklch(1 0 0);
|
||||
--foreground: oklch(0.145 0 0);
|
||||
--card: oklch(1 0 0);
|
||||
--card-foreground: oklch(0.145 0 0);
|
||||
--popover: oklch(1 0 0);
|
||||
--popover-foreground: oklch(0.145 0 0);
|
||||
--primary: oklch(0.205 0 0);
|
||||
--primary-foreground: oklch(0.985 0 0);
|
||||
--secondary: oklch(0.97 0 0);
|
||||
--secondary-foreground: oklch(0.205 0 0);
|
||||
--muted: oklch(0.97 0 0);
|
||||
--muted-foreground: oklch(0.556 0 0);
|
||||
--accent: oklch(0.97 0 0);
|
||||
--accent-foreground: oklch(0.205 0 0);
|
||||
--destructive: oklch(0.577 0.245 27.325);
|
||||
--border: oklch(0.922 0 0);
|
||||
--input: oklch(0.922 0 0);
|
||||
--ring: oklch(0.708 0 0);
|
||||
--chart-1: oklch(0.646 0.222 41.116);
|
||||
--chart-2: oklch(0.6 0.118 184.704);
|
||||
--chart-3: oklch(0.398 0.07 227.392);
|
||||
--chart-4: oklch(0.828 0.189 84.429);
|
||||
--chart-5: oklch(0.769 0.188 70.08);
|
||||
--sidebar: oklch(0.985 0 0);
|
||||
--sidebar-foreground: oklch(0.145 0 0);
|
||||
--sidebar-primary: oklch(0.205 0 0);
|
||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||
--sidebar-accent: oklch(0.97 0 0);
|
||||
--sidebar-accent-foreground: oklch(0.205 0 0);
|
||||
--sidebar-border: oklch(0.922 0 0);
|
||||
--sidebar-ring: oklch(0.708 0 0);
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: oklch(0.145 0 0);
|
||||
--foreground: oklch(0.985 0 0);
|
||||
--card: oklch(0.205 0 0);
|
||||
--card-foreground: oklch(0.985 0 0);
|
||||
--popover: oklch(0.205 0 0);
|
||||
--popover-foreground: oklch(0.985 0 0);
|
||||
--primary: oklch(0.922 0 0);
|
||||
--primary-foreground: oklch(0.205 0 0);
|
||||
--secondary: oklch(0.269 0 0);
|
||||
--secondary-foreground: oklch(0.985 0 0);
|
||||
--muted: oklch(0.269 0 0);
|
||||
--muted-foreground: oklch(0.708 0 0);
|
||||
--accent: oklch(0.269 0 0);
|
||||
--accent-foreground: oklch(0.985 0 0);
|
||||
--destructive: oklch(0.704 0.191 22.216);
|
||||
--border: oklch(1 0 0 / 10%);
|
||||
--input: oklch(1 0 0 / 15%);
|
||||
--ring: oklch(0.556 0 0);
|
||||
--chart-1: oklch(0.488 0.243 264.376);
|
||||
--chart-2: oklch(0.696 0.17 162.48);
|
||||
--chart-3: oklch(0.769 0.188 70.08);
|
||||
--chart-4: oklch(0.627 0.265 303.9);
|
||||
--chart-5: oklch(0.645 0.246 16.439);
|
||||
--sidebar: oklch(0.205 0 0);
|
||||
--sidebar-foreground: oklch(0.985 0 0);
|
||||
--sidebar-primary: oklch(0.488 0.243 264.376);
|
||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||
--sidebar-accent: oklch(0.269 0 0);
|
||||
--sidebar-accent-foreground: oklch(0.985 0 0);
|
||||
--sidebar-border: oklch(1 0 0 / 10%);
|
||||
--sidebar-ring: oklch(0.556 0 0);
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border outline-ring/50;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import App from "./App";
|
||||
import App from "./pages/patcher";
|
||||
import "./index.css";
|
||||
|
||||
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { z } from "zod";
|
|||
import { PropsWithChildren, useCallback, useEffect, useState } from "react";
|
||||
import { useForm } from "@tanstack/react-form";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { CornerUpLeftIcon, Loader2Icon, SaveIcon, XIcon } from "lucide-react";
|
||||
import { CornerUpLeftIcon, Loader2Icon, SaveIcon } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Field,
|
||||
|
|
@ -32,6 +32,7 @@ import {
|
|||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
} from "@/components/ui/accordion";
|
||||
import DragBar from "@/components/drag-bar";
|
||||
|
||||
const config = z.object({
|
||||
$schema: z.string(),
|
||||
|
|
@ -73,16 +74,6 @@ const config = z.object({
|
|||
view_distance: z.number().min(0).max(100),
|
||||
show_sales_text: z.boolean(),
|
||||
}),
|
||||
|
||||
// object_culling: z.boolean(), // Should be 0
|
||||
// visibility: z.number().int(), // Should be 3
|
||||
// software_cursor: z.boolean(), // Always false
|
||||
// is_save_id: z.number().int(),
|
||||
// save_id: z.number().int(),
|
||||
// preloading_delay_time: z.number().int(), // Default seems to be 20
|
||||
// decompressed_texture: z.boolean(), // Should always be true
|
||||
// use_default_ime: z.boolean(), // Should be false
|
||||
// software_tiling: z.number().int(), // Should always be 0 for auto tiling
|
||||
});
|
||||
|
||||
type Config = z.infer<typeof config>;
|
||||
|
|
@ -98,7 +89,7 @@ const Category = ({
|
|||
return (
|
||||
<AccordionItem
|
||||
value={id}
|
||||
className="bg-linear-to-t from-muted/10 to-muted/0 py-1"
|
||||
className="bg-linear-to-tr from-primary/5 to-primary/0 py-1"
|
||||
>
|
||||
<AccordionTrigger className="hover:no-underline! px-6 py-4 rounded-none! items-center">
|
||||
<div>
|
||||
|
|
@ -112,6 +103,7 @@ const Category = ({
|
|||
</AccordionItem>
|
||||
);
|
||||
};
|
||||
|
||||
const Settings = ({
|
||||
resolutions,
|
||||
initialConfig,
|
||||
|
|
@ -156,7 +148,12 @@ const Settings = ({
|
|||
}}
|
||||
className="flex flex-col h-screen"
|
||||
>
|
||||
<Accordion type="single" collapsible defaultValue="general">
|
||||
<Accordion
|
||||
type="single"
|
||||
collapsible
|
||||
defaultValue="general"
|
||||
className="divide-primary/10 divide-y"
|
||||
>
|
||||
<Category id="general" title="General" subtitle="Basic settings">
|
||||
<form.Field
|
||||
name="locale"
|
||||
|
|
@ -250,7 +247,6 @@ const Settings = ({
|
|||
}}
|
||||
/>
|
||||
</Category>
|
||||
<Separator />
|
||||
<Category id="display" title="Display" subtitle="Screen settings">
|
||||
<div className="flex items-center gap-3">
|
||||
<Checkbox
|
||||
|
|
@ -450,7 +446,6 @@ const Settings = ({
|
|||
}}
|
||||
/>
|
||||
</Category>
|
||||
<Separator />
|
||||
<Category id="sound" title="Sound" subtitle="Volume settings">
|
||||
<FieldLegend>Sound</FieldLegend>
|
||||
<FieldDescription>Volume settings</FieldDescription>
|
||||
|
|
@ -511,7 +506,6 @@ const Settings = ({
|
|||
}}
|
||||
/>
|
||||
</Category>
|
||||
<Separator />
|
||||
<Category
|
||||
id="graphics"
|
||||
title="Graphics"
|
||||
|
|
@ -750,26 +744,13 @@ function App() {
|
|||
setConfig(baseConfig);
|
||||
}, []);
|
||||
|
||||
const close = useCallback(async () => {
|
||||
await invoke("close");
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
loadResolutions();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<main>
|
||||
<div className="w-full grid grid-cols-[auto_max-content] fixed top-0 inset-x-0 z-10">
|
||||
<div data-tauri-drag-region />
|
||||
<button
|
||||
title="Close"
|
||||
onClick={close}
|
||||
className="bg-muted/20 hover:bg-muted transition-colors py-0.5 px-3"
|
||||
>
|
||||
<XIcon />
|
||||
</button>
|
||||
</div>
|
||||
<DragBar />
|
||||
{config ? (
|
||||
<Settings resolutions={resolutions} initialConfig={config} />
|
||||
) : (
|
||||
|
|
@ -0,0 +1,176 @@
|
|||
import { P, match } from "ts-pattern";
|
||||
import {
|
||||
createContext,
|
||||
PropsWithChildren,
|
||||
useContext,
|
||||
useEffect,
|
||||
useState,
|
||||
} from "react";
|
||||
import DragBar from "@/components/drag-bar";
|
||||
import { Progress } from "@/components/ui/progress";
|
||||
import { listen } from "@tauri-apps/api/event";
|
||||
import Background from "@/assets/background.jpg";
|
||||
|
||||
const prettyBytes = (num: number, precision = 3, addSpace = true) => {
|
||||
const UNITS = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
||||
if (Math.abs(num) < 1) return num + (addSpace ? " " : "") + UNITS[0];
|
||||
const exponent = Math.min(
|
||||
Math.floor(Math.log10(num < 0 ? -num : num) / 3),
|
||||
UNITS.length - 1
|
||||
);
|
||||
const n = Number(
|
||||
((num < 0 ? -num : num) / 1000 ** exponent).toPrecision(precision)
|
||||
);
|
||||
return (num < 0 ? "-" : "") + n + (addSpace ? " " : "") + UNITS[exponent];
|
||||
};
|
||||
|
||||
const PatcherProgress = () => {
|
||||
const state = useStatus();
|
||||
|
||||
return (
|
||||
<div className="px-8 py-4 text-sm self-center">
|
||||
{match(state)
|
||||
.with({ error: P.not(P.nullish) }, ({ error }) => (
|
||||
<pre>{JSON.stringify(error)}</pre>
|
||||
))
|
||||
.with({ status: Status.Awaiting }, () => (
|
||||
<div className="animate-pulse">Starting patcher...</div>
|
||||
))
|
||||
.with({ status: Status.Fetching }, () => (
|
||||
<div className="animate-pulse">Fetching checksums...</div>
|
||||
))
|
||||
.with({ status: Status.Checksumming }, () => (
|
||||
<div className="animate-pulse">Checking local files...</div>
|
||||
))
|
||||
.with({ status: Status.Verifying }, () => (
|
||||
<div className="animate-pulse">Verifying files...</div>
|
||||
))
|
||||
.with({ status: Status.Updating }, ({ download }) => (
|
||||
<div>
|
||||
<div className="mb-2 animate-pulse">Updating files...</div>
|
||||
{download && (
|
||||
<div className="grid gap-1.5">
|
||||
<Progress value={download.percent} />
|
||||
<span
|
||||
className="text-xs opacity-75
|
||||
"
|
||||
>
|
||||
{download.file}: {prettyBytes(download.downloaded)}/
|
||||
{prettyBytes(download.total)}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))
|
||||
.otherwise(() => (
|
||||
<div className="text-primary brightness-150">Ready!</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
enum Status {
|
||||
Awaiting,
|
||||
Fetching,
|
||||
Checksumming,
|
||||
Verifying,
|
||||
Updating,
|
||||
Ready,
|
||||
}
|
||||
|
||||
type Download = {
|
||||
file: string;
|
||||
downloaded: number;
|
||||
total: number;
|
||||
percent: number;
|
||||
};
|
||||
|
||||
const stateContext = createContext<{
|
||||
status: Status;
|
||||
error: string | null;
|
||||
download: Download | null;
|
||||
}>({
|
||||
status: Status.Awaiting,
|
||||
error: null,
|
||||
download: null,
|
||||
});
|
||||
|
||||
const StateProvider = ({ children }: PropsWithChildren) => {
|
||||
const [status, setStatus] = useState<Status>(Status.Awaiting);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [download, setDownload] = useState<Download | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const setupListeners = async () => {
|
||||
const statusUnlisten = await listen<Status>("status", (event) => {
|
||||
setStatus(event.payload);
|
||||
});
|
||||
const errorUnlisten = await listen<string>("error", (event) => {
|
||||
setError(event.payload);
|
||||
});
|
||||
const downloadUnlisten = await listen<Download>("download", (event) => {
|
||||
setDownload(event.payload);
|
||||
});
|
||||
|
||||
return () => {
|
||||
statusUnlisten();
|
||||
errorUnlisten();
|
||||
downloadUnlisten();
|
||||
};
|
||||
};
|
||||
|
||||
let cleanup: () => void;
|
||||
setupListeners().then((cleanupFn) => {
|
||||
cleanup = cleanupFn;
|
||||
});
|
||||
|
||||
return () => {
|
||||
if (cleanup) cleanup();
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<stateContext.Provider value={{ status, error, download }}>
|
||||
{children}
|
||||
</stateContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
const useStatus = () => useContext(stateContext);
|
||||
|
||||
const Patcher = () => {
|
||||
const { status, error } = useStatus();
|
||||
|
||||
return (
|
||||
<>
|
||||
<img
|
||||
src={Background}
|
||||
alt="Background"
|
||||
className="absolute inset-0 size-full object-center object-cover"
|
||||
/>
|
||||
<div className="grid grid-cols-[1fr_auto] items-stretch border-t border-primary/10 absolute bottom-0 inset-x-0 z-10 bg-background/90 backdrop-blur-2xl">
|
||||
<PatcherProgress />
|
||||
<button
|
||||
type="button"
|
||||
className="px-12 py-8 bg-linear-to-tr from-primary/80 to-primary/50 font-semibold text-sm hover:brightness-125 transition-all disabled:grayscale-75 disabled:opacity-50"
|
||||
disabled={status !== Status.Ready || !!error}
|
||||
>
|
||||
Start now
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<StateProvider>
|
||||
<main className="w-screen h-screen relative overflow-hidden">
|
||||
<DragBar isMain />
|
||||
<Patcher />
|
||||
</main>
|
||||
</StateProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
Loading…
Reference in New Issue