支持托盘 (#181)

* Initial plan

* Add system tray icon feature with show/quit menu

Co-authored-by: BTMuli <72692909+BTMuli@users.noreply.github.com>

* Apply Rust code formatting

Co-authored-by: BTMuli <72692909+BTMuli@users.noreply.github.com>

* Address code review feedback: improve documentation and reduce duplication

Co-authored-by: BTMuli <72692909+BTMuli@users.noreply.github.com>

* Make tray behavior configurable - add closeToTray setting

- Add closeToTray setting in app store (default: false)
- Add UI toggle in PageConfig.vue for "关闭到托盘" setting
- Modify window close handler to emit event instead of always hiding
- Add hide_main_window and quit_app commands in backend
- Frontend listens for close event and respects user preference
- When closeToTray is enabled, window minimizes to tray on close
- When disabled, application quits on close (original behavior)

Co-authored-by: BTMuli <72692909+BTMuli@users.noreply.github.com>

* Remove duplicate SUB_WINDOW_LABELS constant - use shared constant from lib.rs

Co-authored-by: BTMuli <72692909+BTMuli@users.noreply.github.com>

* Fix tray icon issues: remove duplicate tray, add icon loading, close sub-windows on hide

1. Remove duplicate tray icon: Removed trayIcon config from tauri.conf.json (was creating 2 trays)
2. Add proper icon loading: Use image crate to decode PNG and create RGBA icon data
3. Close sub-windows when hiding: Modified hide_main_window to close all sub-windows before hiding
4. Sync with master branch: Merged latest commit c8fe03c

Co-authored-by: BTMuli <72692909+BTMuli@users.noreply.github.com>

* Address code review: remove anyhow, add separator, improve comments

1. Remove anyhow dependency - Use Tauri's InvalidIcon error instead
2. Add menu separator between "显示窗口" and "退出应用"
3. Add comment explaining image crate necessity (Tauri Image only accepts raw RGBA)
4. Keep HTTPS URLs with note about SSH preference for local development
5. Image crate remains necessary as Tauri doesn't provide PNG decoding

Co-authored-by: BTMuli <72692909+BTMuli@users.noreply.github.com>

* Revert to SSH URLs for plugin dependencies as requested

Changed all tauri-plugin git URLs from HTTPS to SSH format.
Project CI has SSH configuration, so SSH is the preferred method.

Co-authored-by: BTMuli <72692909+BTMuli@users.noreply.github.com>

* Use storeToRefs for closeToTray property

Changed closeToTray access to use storeToRefs in both App.vue and PageConfig.vue
for better reactivity and consistency with other store properties.

Co-authored-by: BTMuli <72692909+BTMuli@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: BTMuli <72692909+BTMuli@users.noreply.github.com>
This commit is contained in:
Copilot
2025-12-14 14:30:15 +08:00
committed by GitHub
parent c8fe03c7ee
commit 7544fa1489
8 changed files with 800 additions and 28 deletions

639
src-tauri/Cargo.lock generated
View File

@@ -7,6 +7,7 @@ name = "TeyvatGuide"
version = "0.8.8"
dependencies = [
"chrono",
"image",
"log",
"prost",
"prost-types",
@@ -57,6 +58,24 @@ dependencies = [
"memchr",
]
[[package]]
name = "aligned"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "377e4c0ba83e4431b10df45c1d4666f178ea9c552cac93e60c3a88bf32785923"
dependencies = [
"as-slice",
]
[[package]]
name = "aligned-vec"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b"
dependencies = [
"equator",
]
[[package]]
name = "alloc-no-stdlib"
version = "2.0.4"
@@ -110,12 +129,38 @@ version = "1.0.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
[[package]]
name = "arbitrary"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
[[package]]
name = "arg_enum_proc_macro"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.111",
]
[[package]]
name = "arrayvec"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
[[package]]
name = "as-slice"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516"
dependencies = [
"stable_deref_trait",
]
[[package]]
name = "async-broadcast"
version = "0.7.2"
@@ -291,6 +336,49 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "av-scenechange"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f321d77c20e19b92c39e7471cf986812cbb46659d2af674adc4331ef3f18394"
dependencies = [
"aligned",
"anyhow",
"arg_enum_proc_macro",
"arrayvec",
"log",
"num-rational",
"num-traits",
"pastey",
"rayon",
"thiserror 2.0.17",
"v_frame",
"y4m",
]
[[package]]
name = "av1-grain"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cfddb07216410377231960af4fcab838eaa12e013417781b78bd95ee22077f8"
dependencies = [
"anyhow",
"arrayvec",
"log",
"nom",
"num-rational",
"v_frame",
]
[[package]]
name = "avif-serialize"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47c8fbc0f831f4519fe8b810b6a7a91410ec83031b8233f730a0480029f6a23f"
dependencies = [
"arrayvec",
]
[[package]]
name = "base64"
version = "0.21.7"
@@ -309,6 +397,12 @@ version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba"
[[package]]
name = "bit_field"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6"
[[package]]
name = "bitflags"
version = "1.3.2"
@@ -324,6 +418,15 @@ dependencies = [
"serde_core",
]
[[package]]
name = "bitstream-io"
version = "4.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60d4bd9d1db2c6bdf285e223a7fa369d5ce98ec767dec949c6ca62863ce61757"
dependencies = [
"core2",
]
[[package]]
name = "bitvec"
version = "1.0.1"
@@ -420,6 +523,12 @@ dependencies = [
"alloc-stdlib",
]
[[package]]
name = "built"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4ad8f11f288f48ca24471bbd51ac257aaeaaa07adae295591266b792902ae64"
[[package]]
name = "bumpalo"
version = "3.19.0"
@@ -472,6 +581,12 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "byteorder-lite"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
[[package]]
name = "bytes"
version = "1.11.0"
@@ -555,6 +670,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c481bdbf0ed3b892f6f806287d72acd515b352a4ec27a208489b8c1bc839633a"
dependencies = [
"find-msvc-tools",
"jobserver",
"libc",
"shlex",
]
@@ -611,6 +728,12 @@ dependencies = [
"windows-link 0.2.1",
]
[[package]]
name = "color_quant"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
[[package]]
name = "combine"
version = "4.6.7"
@@ -741,6 +864,15 @@ dependencies = [
"libc",
]
[[package]]
name = "core2"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505"
dependencies = [
"memchr",
]
[[package]]
name = "cpufeatures"
version = "0.2.17"
@@ -783,6 +915,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-queue"
version = "0.3.12"
@@ -1146,6 +1297,26 @@ dependencies = [
"regex",
]
[[package]]
name = "equator"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc"
dependencies = [
"equator-macro",
]
[[package]]
name = "equator-macro"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.111",
]
[[package]]
name = "equivalent"
version = "1.0.2"
@@ -1205,12 +1376,47 @@ dependencies = [
"pin-project-lite",
]
[[package]]
name = "exr"
version = "1.74.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4300e043a56aa2cb633c01af81ca8f699a321879a7854d3896a0ba89056363be"
dependencies = [
"bit_field",
"half",
"lebe",
"miniz_oxide",
"rayon-core",
"smallvec",
"zune-inflate",
]
[[package]]
name = "fastrand"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]]
name = "fax"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f05de7d48f37cd6730705cbca900770cab77a89f413d23e100ad7fad7795a0ab"
dependencies = [
"fax_derive",
]
[[package]]
name = "fax_derive"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.111",
]
[[package]]
name = "fdeflate"
version = "0.3.7"
@@ -1593,6 +1799,16 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "gif"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5df2ba84018d80c213569363bdcd0c64e6933c67fe4c1d60ecf822971a3c35e"
dependencies = [
"color_quant",
"weezl",
]
[[package]]
name = "gio"
version = "0.18.4"
@@ -1760,6 +1976,17 @@ dependencies = [
"tracing",
]
[[package]]
name = "half"
version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b"
dependencies = [
"cfg-if",
"crunchy",
"zerocopy",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
@@ -1999,7 +2226,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc50b891e4acf8fe0e71ef88ec43ad82ee07b3810ad09de10f1d01f072ed4b98"
dependencies = [
"byteorder",
"png",
"png 0.17.16",
]
[[package]]
@@ -2110,6 +2337,46 @@ dependencies = [
"icu_properties",
]
[[package]]
name = "image"
version = "0.25.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6506c6c10786659413faa717ceebcb8f70731c0a60cbae39795fdf114519c1a"
dependencies = [
"bytemuck",
"byteorder-lite",
"color_quant",
"exr",
"gif",
"image-webp",
"moxcms",
"num-traits",
"png 0.18.0",
"qoi",
"ravif",
"rayon",
"rgb",
"tiff",
"zune-core 0.5.0",
"zune-jpeg 0.5.6",
]
[[package]]
name = "image-webp"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3"
dependencies = [
"byteorder-lite",
"quick-error",
]
[[package]]
name = "imgref"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7c5cedc30da3a610cac6b4ba17597bdf7152cf974e8aab3afb3d54455e371c8"
[[package]]
name = "indexmap"
version = "1.9.3"
@@ -2142,6 +2409,17 @@ dependencies = [
"cfb",
]
[[package]]
name = "interpolate_name"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.111",
]
[[package]]
name = "ipnet"
version = "2.11.0"
@@ -2237,6 +2515,16 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
[[package]]
name = "jobserver"
version = "0.1.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
dependencies = [
"getrandom 0.3.4",
"libc",
]
[[package]]
name = "js-sys"
version = "0.3.83"
@@ -2301,6 +2589,12 @@ dependencies = [
"spin",
]
[[package]]
name = "lebe"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8"
[[package]]
name = "libappindicator"
version = "0.9.0"
@@ -2331,6 +2625,16 @@ version = "0.2.178"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091"
[[package]]
name = "libfuzzer-sys"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5037190e1f70cbeef565bd267599242926f724d3b8a9f510fd7e0b540cfa4404"
dependencies = [
"arbitrary",
"cc",
]
[[package]]
name = "libloading"
version = "0.7.4"
@@ -2405,6 +2709,15 @@ dependencies = [
"value-bag",
]
[[package]]
name = "loop9"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062"
dependencies = [
"imgref",
]
[[package]]
name = "lru-slab"
version = "0.1.2"
@@ -2448,6 +2761,16 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5"
[[package]]
name = "maybe-rayon"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519"
dependencies = [
"cfg-if",
"rayon",
]
[[package]]
name = "md-5"
version = "0.10.6"
@@ -2500,6 +2823,16 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "moxcms"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80986bbbcf925ebd3be54c26613d861255284584501595cf418320c078945608"
dependencies = [
"num-traits",
"pxfm",
]
[[package]]
name = "muda"
version = "0.17.1"
@@ -2515,7 +2848,7 @@ dependencies = [
"objc2-core-foundation",
"objc2-foundation 0.3.2",
"once_cell",
"png",
"png 0.17.16",
"serde",
"thiserror 2.0.17",
"windows-sys 0.60.2",
@@ -2576,6 +2909,31 @@ version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
[[package]]
name = "nom"
version = "8.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405"
dependencies = [
"memchr",
]
[[package]]
name = "noop_proc_macro"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8"
[[package]]
name = "num-bigint"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
dependencies = [
"num-integer",
"num-traits",
]
[[package]]
name = "num-bigint-dig"
version = "0.8.6"
@@ -2598,6 +2956,17 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
[[package]]
name = "num-derive"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.111",
]
[[package]]
name = "num-integer"
version = "0.1.46"
@@ -2618,6 +2987,17 @@ dependencies = [
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
dependencies = [
"num-bigint",
"num-integer",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.19"
@@ -3077,6 +3457,18 @@ dependencies = [
"windows-link 0.2.1",
]
[[package]]
name = "paste"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "pastey"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec"
[[package]]
name = "pathdiff"
version = "0.2.3"
@@ -3308,6 +3700,19 @@ dependencies = [
"miniz_oxide",
]
[[package]]
name = "png"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97baced388464909d42d89643fe4361939af9b7ce7a31ee32a168f832a70f2a0"
dependencies = [
"bitflags 2.10.0",
"crc32fast",
"fdeflate",
"flate2",
"miniz_oxide",
]
[[package]]
name = "polling"
version = "3.11.0"
@@ -3420,6 +3825,25 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "profiling"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773"
dependencies = [
"profiling-procmacros",
]
[[package]]
name = "profiling-procmacros"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b"
dependencies = [
"quote",
"syn 2.0.111",
]
[[package]]
name = "prost"
version = "0.14.1"
@@ -3488,6 +3912,30 @@ dependencies = [
"psl-types",
]
[[package]]
name = "pxfm"
version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7186d3822593aa4393561d186d1393b3923e9d6163d3fbfd6e825e3e6cf3e6a8"
dependencies = [
"num-traits",
]
[[package]]
name = "qoi"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001"
dependencies = [
"bytemuck",
]
[[package]]
name = "quick-error"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
[[package]]
name = "quick-xml"
version = "0.38.4"
@@ -3683,12 +4131,82 @@ dependencies = [
"rand_core 0.5.1",
]
[[package]]
name = "rav1e"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43b6dd56e85d9483277cde964fd1bdb0428de4fec5ebba7540995639a21cb32b"
dependencies = [
"aligned-vec",
"arbitrary",
"arg_enum_proc_macro",
"arrayvec",
"av-scenechange",
"av1-grain",
"bitstream-io",
"built",
"cfg-if",
"interpolate_name",
"itertools",
"libc",
"libfuzzer-sys",
"log",
"maybe-rayon",
"new_debug_unreachable",
"noop_proc_macro",
"num-derive",
"num-traits",
"paste",
"profiling",
"rand 0.9.2",
"rand_chacha 0.9.0",
"simd_helpers",
"thiserror 2.0.17",
"v_frame",
"wasm-bindgen",
]
[[package]]
name = "ravif"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef69c1990ceef18a116855938e74793a5f7496ee907562bd0857b6ac734ab285"
dependencies = [
"avif-serialize",
"imgref",
"loop9",
"quick-error",
"rav1e",
"rayon",
"rgb",
]
[[package]]
name = "raw-window-handle"
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"
@@ -3837,6 +4355,12 @@ dependencies = [
"windows-sys 0.60.2",
]
[[package]]
name = "rgb"
version = "0.8.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce"
[[package]]
name = "ring"
version = "0.17.14"
@@ -4356,6 +4880,15 @@ version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
[[package]]
name = "simd_helpers"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6"
dependencies = [
"quote",
]
[[package]]
name = "simdutf8"
version = "0.1.5"
@@ -4961,7 +5494,7 @@ dependencies = [
"ico",
"json-patch",
"plist",
"png",
"png 0.17.16",
"proc-macro2",
"quote",
"semver",
@@ -5011,7 +5544,7 @@ dependencies = [
[[package]]
name = "tauri-plugin-deep-link"
version = "2.4.5"
source = "git+ssh://git@github.com/tauri-apps/plugins-workspace.git?branch=v2#d9d51eb8ea2fd6ce3298ec0dc370cc04351a9b98"
source = "git+https://github.com/tauri-apps/plugins-workspace.git?branch=v2#ce6835d50ff7800dcfb8508a98e9ee83771fb283"
dependencies = [
"dunce",
"plist",
@@ -5031,7 +5564,7 @@ dependencies = [
[[package]]
name = "tauri-plugin-dialog"
version = "2.4.2"
source = "git+ssh://git@github.com/tauri-apps/plugins-workspace.git?branch=v2#d9d51eb8ea2fd6ce3298ec0dc370cc04351a9b98"
source = "git+https://github.com/tauri-apps/plugins-workspace.git?branch=v2#ce6835d50ff7800dcfb8508a98e9ee83771fb283"
dependencies = [
"log",
"raw-window-handle",
@@ -5048,7 +5581,7 @@ dependencies = [
[[package]]
name = "tauri-plugin-fs"
version = "2.4.4"
source = "git+ssh://git@github.com/tauri-apps/plugins-workspace.git?branch=v2#d9d51eb8ea2fd6ce3298ec0dc370cc04351a9b98"
source = "git+https://github.com/tauri-apps/plugins-workspace.git?branch=v2#ce6835d50ff7800dcfb8508a98e9ee83771fb283"
dependencies = [
"anyhow",
"dunce",
@@ -5069,7 +5602,7 @@ dependencies = [
[[package]]
name = "tauri-plugin-http"
version = "2.5.4"
source = "git+ssh://git@github.com/tauri-apps/plugins-workspace.git?branch=v2#d9d51eb8ea2fd6ce3298ec0dc370cc04351a9b98"
source = "git+https://github.com/tauri-apps/plugins-workspace.git?branch=v2#ce6835d50ff7800dcfb8508a98e9ee83771fb283"
dependencies = [
"bytes",
"cookie_store",
@@ -5092,7 +5625,7 @@ dependencies = [
[[package]]
name = "tauri-plugin-log"
version = "2.7.1"
source = "git+ssh://git@github.com/tauri-apps/plugins-workspace.git?branch=v2#d9d51eb8ea2fd6ce3298ec0dc370cc04351a9b98"
source = "git+https://github.com/tauri-apps/plugins-workspace.git?branch=v2#ce6835d50ff7800dcfb8508a98e9ee83771fb283"
dependencies = [
"android_logger",
"byte-unit",
@@ -5113,7 +5646,7 @@ dependencies = [
[[package]]
name = "tauri-plugin-opener"
version = "2.5.2"
source = "git+ssh://git@github.com/tauri-apps/plugins-workspace.git?branch=v2#d9d51eb8ea2fd6ce3298ec0dc370cc04351a9b98"
source = "git+https://github.com/tauri-apps/plugins-workspace.git?branch=v2#ce6835d50ff7800dcfb8508a98e9ee83771fb283"
dependencies = [
"dunce",
"glob",
@@ -5134,7 +5667,7 @@ dependencies = [
[[package]]
name = "tauri-plugin-os"
version = "2.3.2"
source = "git+ssh://git@github.com/tauri-apps/plugins-workspace.git?branch=v2#d9d51eb8ea2fd6ce3298ec0dc370cc04351a9b98"
source = "git+https://github.com/tauri-apps/plugins-workspace.git?branch=v2#ce6835d50ff7800dcfb8508a98e9ee83771fb283"
dependencies = [
"gethostname",
"log",
@@ -5151,7 +5684,7 @@ dependencies = [
[[package]]
name = "tauri-plugin-process"
version = "2.3.1"
source = "git+ssh://git@github.com/tauri-apps/plugins-workspace.git?branch=v2#d9d51eb8ea2fd6ce3298ec0dc370cc04351a9b98"
source = "git+https://github.com/tauri-apps/plugins-workspace.git?branch=v2#ce6835d50ff7800dcfb8508a98e9ee83771fb283"
dependencies = [
"tauri",
"tauri-plugin",
@@ -5160,7 +5693,7 @@ dependencies = [
[[package]]
name = "tauri-plugin-shell"
version = "2.3.3"
source = "git+ssh://git@github.com/tauri-apps/plugins-workspace.git?branch=v2#d9d51eb8ea2fd6ce3298ec0dc370cc04351a9b98"
source = "git+https://github.com/tauri-apps/plugins-workspace.git?branch=v2#ce6835d50ff7800dcfb8508a98e9ee83771fb283"
dependencies = [
"encoding_rs",
"log",
@@ -5180,7 +5713,7 @@ dependencies = [
[[package]]
name = "tauri-plugin-single-instance"
version = "2.3.6"
source = "git+ssh://git@github.com/tauri-apps/plugins-workspace.git?branch=v2#d9d51eb8ea2fd6ce3298ec0dc370cc04351a9b98"
source = "git+https://github.com/tauri-apps/plugins-workspace.git?branch=v2#ce6835d50ff7800dcfb8508a98e9ee83771fb283"
dependencies = [
"serde",
"serde_json",
@@ -5194,7 +5727,7 @@ dependencies = [
[[package]]
name = "tauri-plugin-sql"
version = "2.3.1"
source = "git+ssh://git@github.com/tauri-apps/plugins-workspace.git?branch=v2#d9d51eb8ea2fd6ce3298ec0dc370cc04351a9b98"
source = "git+https://github.com/tauri-apps/plugins-workspace.git?branch=v2#ce6835d50ff7800dcfb8508a98e9ee83771fb283"
dependencies = [
"futures-core",
"indexmap 2.12.1",
@@ -5374,6 +5907,20 @@ dependencies = [
"syn 2.0.111",
]
[[package]]
name = "tiff"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af9605de7fee8d9551863fd692cce7637f548dbd9db9180fcc07ccc6d26c336f"
dependencies = [
"fax",
"flate2",
"half",
"quick-error",
"weezl",
"zune-jpeg 0.4.21",
]
[[package]]
name = "time"
version = "0.3.44"
@@ -5690,7 +6237,7 @@ dependencies = [
"objc2-core-graphics",
"objc2-foundation 0.3.2",
"once_cell",
"png",
"png 0.17.16",
"serde",
"thiserror 2.0.17",
"windows-sys 0.60.2",
@@ -5859,6 +6406,17 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "v_frame"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "666b7727c8875d6ab5db9533418d7c764233ac9c0cff1d469aec8fa127597be2"
dependencies = [
"aligned-vec",
"num-traits",
"wasm-bindgen",
]
[[package]]
name = "value-bag"
version = "1.12.0"
@@ -6129,6 +6687,12 @@ dependencies = [
"windows-core 0.61.2",
]
[[package]]
name = "weezl"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88"
[[package]]
name = "whoami"
version = "1.6.1"
@@ -6776,6 +7340,12 @@ dependencies = [
"pkg-config",
]
[[package]]
name = "y4m"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5a4b21e1a62b67a2970e6831bc091d7b87e119e7f9791aef9702e3bef04448"
[[package]]
name = "yoke"
version = "0.8.1"
@@ -6940,6 +7510,45 @@ dependencies = [
"syn 2.0.111",
]
[[package]]
name = "zune-core"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a"
[[package]]
name = "zune-core"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "111f7d9820f05fd715df3144e254d6fc02ee4088b0644c0ffd0efc9e6d9d2773"
[[package]]
name = "zune-inflate"
version = "0.2.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02"
dependencies = [
"simd-adler32",
]
[[package]]
name = "zune-jpeg"
version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713"
dependencies = [
"zune-core 0.4.12",
]
[[package]]
name = "zune-jpeg"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f520eebad972262a1dde0ec455bce4f8b298b1e5154513de58c114c4c54303e8"
dependencies = [
"zune-core 0.5.0",
]
[[package]]
name = "zvariant"
version = "5.8.0"

View File

@@ -21,12 +21,13 @@ tauri-build = { version = "2.5.3", features = [] }
[dependencies]
chrono = "0.4.42"
image = "0.25"
log = "0.4.29"
prost = "0.14.1"
prost-types = "0.14.1"
serde = { version = "1.0.228", features = ["derive"] }
serde_json = "1.0.145"
tauri = { version = "2.9.4", features = [] }
tauri = { version = "2.9.4", features = ["tray-icon"] }
tauri-utils = "2.8.1"
url = "2.5.7"
walkdir = "2.5.0"

View File

@@ -127,3 +127,31 @@ pub fn is_in_admin() -> bool {
}
}
}
// 隐藏主窗口到托盘
#[tauri::command]
pub async fn hide_main_window(app_handle: AppHandle) {
// 关闭所有子窗口
for label in crate::SUB_WINDOW_LABELS.iter() {
if let Some(sub) = app_handle.get_webview_window(label) {
let _ = sub.destroy();
}
}
// 隐藏主窗口
if let Some(window) = app_handle.get_webview_window("TeyvatGuide") {
let _ = window.hide();
}
}
// 退出应用
#[tauri::command]
pub async fn quit_app(app_handle: AppHandle) {
// 关闭所有子窗口
for label in crate::SUB_WINDOW_LABELS.iter() {
if let Some(sub) = app_handle.get_webview_window(label) {
let _ = sub.destroy();
}
}
// 退出应用
app_handle.exit(0);
}

View File

@@ -4,6 +4,7 @@
mod client;
mod commands;
mod plugins;
mod tray;
mod utils;
#[cfg(target_os = "windows")]
mod watchdog;
@@ -11,9 +12,14 @@ mod watchdog;
mod yae;
use crate::client::create_mhy_client;
use crate::commands::{create_window, execute_js, get_dir_size, init_app, is_in_admin};
use crate::commands::{
create_window, execute_js, get_dir_size, hide_main_window, init_app, is_in_admin, quit_app,
};
use crate::plugins::{build_log_plugin, build_si_plugin};
use tauri::{generate_context, generate_handler, Manager, Window, WindowEvent};
use tauri::{generate_context, generate_handler, Emitter, Manager, Window, WindowEvent};
// 子窗口 label 的数组
pub const SUB_WINDOW_LABELS: [&str; 3] = ["Sub_window", "Dev_JSON", "mhy_client"];
// 窗口事件处理
fn window_event_handler(app: &Window, event: &WindowEvent) {
@@ -21,16 +27,12 @@ fn window_event_handler(app: &Window, event: &WindowEvent) {
WindowEvent::CloseRequested { api, .. } => {
api.prevent_close();
if app.label() == "TeyvatGuide" {
// 窗口 label 的数组
const SUB_WINDOW_LABELS: [&str; 3] = ["Sub_window", "Dev_JSON", "mhy_client"];
for label in SUB_WINDOW_LABELS.iter() {
let sub = app.get_webview_window(label);
if sub.is_some() {
sub.unwrap().destroy().unwrap();
}
}
// 窗口:发送事件让前端根据配置决定是隐藏还是退出
let _ = app.emit("main-window-close-requested", ());
} else {
// 子窗口:直接销毁
app.destroy().unwrap();
}
app.destroy().unwrap();
}
_ => {}
}
@@ -78,6 +80,9 @@ pub fn run() {
.plugin(tauri_plugin_sql::Builder::default().build())
.plugin(build_log_plugin())
.setup(|_app| {
// 创建系统托盘图标
tray::create_tray(_app.handle())
.expect("Failed to initialize system tray icon. Please check if the tray icon file exists and the system supports tray icons.");
let _window = _app.get_webview_window("TeyvatGuide");
#[cfg(debug_assertions)]
if _window.is_some() {
@@ -92,6 +97,8 @@ pub fn run() {
get_dir_size,
create_mhy_client,
is_in_admin,
hide_main_window,
quit_app,
#[cfg(target_os = "windows")]
yae::call_yae_dll,
#[cfg(target_os = "windows")]

78
src-tauri/src/tray.rs Normal file
View File

@@ -0,0 +1,78 @@
//! @file src/tray.rs
//! @desc 系统托盘模块,负责创建和管理系统托盘图标
//! @since Beta v0.8.8
use tauri::image::Image;
use tauri::menu::{MenuBuilder, MenuItemBuilder, PredefinedMenuItem};
use tauri::tray::{MouseButton, MouseButtonState, TrayIconBuilder, TrayIconEvent};
use tauri::{AppHandle, Manager, Runtime};
/// 创建系统托盘图标
pub fn create_tray<R: Runtime>(app: &AppHandle<R>) -> tauri::Result<()> {
// 创建托盘菜单
let show_item = MenuItemBuilder::with_id("show", "显示窗口").build(app)?;
let separator = PredefinedMenuItem::separator(app)?;
let quit_item = MenuItemBuilder::with_id("quit", "退出应用").build(app)?;
let menu = MenuBuilder::new(app).item(&show_item).item(&separator).item(&quit_item).build()?;
// 加载托盘图标
// 在不同操作系统上,托盘图标的显示效果可能有所不同:
// - Windows: 使用 .ico 格式获得最佳效果
// - macOS: 支持 .icns 和 .png 格式
// - Linux: 通常使用 .png 格式
let icon_bytes = include_bytes!("../icons/32x32.png");
// 使用 image crate 解码 PNG 图标
// Tauri 的 Image 结构只接受原始 RGBA 数据,因此需要使用 image crate 解码 PNG
let img = image::load_from_memory(icon_bytes).map_err(|e| {
tauri::Error::InvalidIcon(std::io::Error::new(std::io::ErrorKind::InvalidData, e))
})?;
let rgba = img.to_rgba8();
let (width, height) = rgba.dimensions();
let icon = Image::new_owned(rgba.into_raw(), width, height);
// 创建托盘图标并设置事件处理
let _tray = TrayIconBuilder::new()
.icon(icon)
.tooltip("Teyvat Guide")
.menu(&menu)
.on_menu_event(|app, event| {
match event.id.as_ref() {
"show" => {
if let Some(window) = app.get_webview_window("TeyvatGuide") {
let _ = window.show();
let _ = window.set_focus();
}
}
"quit" => {
// 关闭所有子窗口
for label in crate::SUB_WINDOW_LABELS.iter() {
if let Some(sub) = app.get_webview_window(label) {
let _ = sub.destroy();
}
}
// 退出应用
app.exit(0);
}
_ => {}
}
})
.on_tray_icon_event(|tray, event| {
if let TrayIconEvent::Click {
button: MouseButton::Left,
button_state: MouseButtonState::Up,
..
} = event
{
let app = tray.app_handle();
if let Some(window) = app.get_webview_window("TeyvatGuide") {
let _ = window.show();
let _ = window.set_focus();
}
}
})
.build(app)?;
Ok(())
}

View File

@@ -36,8 +36,9 @@ import { computed, onMounted, onUnmounted, ref } from "vue";
import { useRouter } from "vue-router";
const router = useRouter();
const { theme, needResize, deviceInfo, isLogin, userDir, buildTime } = storeToRefs(useAppStore());
const { theme, needResize, deviceInfo, isLogin, userDir, buildTime, closeToTray } = storeToRefs(useAppStore());
const { uid, briefInfo, account, cookie } = storeToRefs(useUserStore());
const appStore = useAppStore();
const isMain = ref<boolean>(false);
const vuetifyTheme = computed<string>(() => (theme.value === "dark" ? "dark" : "light"));
@@ -46,6 +47,7 @@ let themeListener: UnlistenFn | null = null;
let dpListener: UnlistenFn | null = null;
let resizeListener: UnlistenFn | null = null;
let yaeListener: UnlistenFn | null = null;
let closeListener: UnlistenFn | null = null;
let yaeFlag: Array<string> = [];
onMounted(async () => {
@@ -58,6 +60,7 @@ onMounted(async () => {
await core.invoke("init_app");
dpListener = await event.listen<string>("active_deep_link", handleDpListen);
yaeListener = await event.listen<TGApp.Plugins.Yae.RsEvent>("yae_read", handleYaeListen);
closeListener = await event.listen("main-window-close-requested", handleWindowClose);
}
if (needResize.value !== "false") await resizeWindow();
document.documentElement.className = theme.value;
@@ -87,6 +90,10 @@ onUnmounted(() => {
resizeListener();
resizeListener = null;
}
if (closeListener !== null) {
closeListener();
closeListener = null;
}
});
/**
@@ -359,6 +366,26 @@ async function checkUpdate(): Promise<void> {
await openUrl("https://app.btmuli.ink/docs/TeyvatGuide/changelogs.html");
}
}
/**
* 处理主窗口关闭请求
* @since Beta v0.8.8
* @returns {Promise<void>}
*/
async function handleWindowClose(): Promise<void> {
try {
// 根据用户设置决定是隐藏到托盘还是退出应用
if (closeToTray.value) {
await core.invoke("hide_main_window");
} else {
await core.invoke("quit_app");
}
} catch (e) {
if (e instanceof Error) {
await TGLogger.Error(`[App][handleWindowClose] ${e.name}: ${e.message}`);
} else console.error(e);
}
}
</script>
<style lang="css" scoped>
.app-container {

View File

@@ -98,6 +98,22 @@
/>
</template>
</v-list-item>
<v-list-item title="关闭到托盘" subtitle="关闭窗口时最小化到系统托盘而不是退出应用">
<template #prepend>
<div class="config-icon">
<v-icon>mdi-tray-arrow-down</v-icon>
</div>
</template>
<template #append>
<v-switch
v-model="closeToTray"
:label="closeToTray ? '开启' : '关闭'"
:inset="true"
class="config-switch"
color="#FAC51E"
/>
</template>
</v-list-item>
<v-list-item title="无痕浏览" subtitle="关闭后将记录帖子浏览记录">
<template #prepend>
<div class="config-icon">
@@ -184,6 +200,7 @@ const {
buildTime,
imageQualityPercent,
incognito,
closeToTray,
} = storeToRefs(appStore);
const homeStore = useHomeStore();

View File

@@ -63,6 +63,8 @@ const useAppStore = defineStore(
const postViewWide = ref<boolean>(true);
/* 是否取消点赞 */
const cancelLike = ref<boolean>(true);
/* 关闭窗口时最小化到托盘 */
const closeToTray = ref<boolean>(false);
/**
* 初始化应用状态
@@ -85,6 +87,7 @@ const useAppStore = defineStore(
incognito.value = true;
postViewWide.value = true;
cancelLike.value = true;
closeToTray.value = false;
initDevice();
}
@@ -140,6 +143,7 @@ const useAppStore = defineStore(
incognito,
postViewWide,
cancelLike,
closeToTray,
init,
changeTheme,
getImageUrl,
@@ -167,6 +171,7 @@ const useAppStore = defineStore(
"sidebar",
"postViewWide",
"cancelLike",
"closeToTray",
],
},
{