From 7544fa14897bfdc47b21a97cbfac39502a49ee84 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sun, 14 Dec 2025 14:30:15 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20=E6=94=AF=E6=8C=81=E6=89=98?= =?UTF-8?q?=E7=9B=98=20(#181)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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> --- src-tauri/Cargo.lock | 639 +++++++++++++++++++++++++++++++- src-tauri/Cargo.toml | 3 +- src-tauri/src/commands.rs | 28 ++ src-tauri/src/lib.rs | 29 +- src-tauri/src/tray.rs | 78 ++++ src/App.vue | 29 +- src/pages/common/PageConfig.vue | 17 + src/store/modules/app.ts | 5 + 8 files changed, 800 insertions(+), 28 deletions(-) create mode 100644 src-tauri/src/tray.rs diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 30878c7b..c4638619 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -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" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 29ff1abc..5684c3e7 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -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" diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs index be6119a1..c61c7314 100644 --- a/src-tauri/src/commands.rs +++ b/src-tauri/src/commands.rs @@ -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); +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index a7eb7f29..96891d3f 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -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")] diff --git a/src-tauri/src/tray.rs b/src-tauri/src/tray.rs new file mode 100644 index 00000000..64186342 --- /dev/null +++ b/src-tauri/src/tray.rs @@ -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(app: &AppHandle) -> 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(()) +} diff --git a/src/App.vue b/src/App.vue index 136c9eaa..a733e1af 100644 --- a/src/App.vue +++ b/src/App.vue @@ -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(false); const vuetifyTheme = computed(() => (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 = []; onMounted(async () => { @@ -58,6 +60,7 @@ onMounted(async () => { await core.invoke("init_app"); dpListener = await event.listen("active_deep_link", handleDpListen); yaeListener = await event.listen("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 { await openUrl("https://app.btmuli.ink/docs/TeyvatGuide/changelogs.html"); } } + +/** + * 处理主窗口关闭请求 + * @since Beta v0.8.8 + * @returns {Promise} + */ +async function handleWindowClose(): Promise { + 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); + } +}