From df4ddb9b7aca1158edb8443e6f42b33cadbddf31 Mon Sep 17 00:00:00 2001 From: Neko Ayaka Date: Sun, 29 Jun 2025 23:16:13 +0800 Subject: [PATCH] fix(stage-tamagotchi): failed to listen update --- Cargo.lock | 197 ++++++++++-------- apps/stage-tamagotchi/package.json | 2 + apps/stage-tamagotchi/src-tauri/Cargo.toml | 2 + .../src-tauri/src/app_windows/chat.rs | 7 +- .../src-tauri/src/app_windows/settings.rs | 7 +- apps/stage-tamagotchi/src-tauri/src/lib.rs | 23 +- .../src-tauri/src/whisper/whisper.rs | 9 +- .../src/composables/tauri-click-through.ts | 105 ++++++++++ .../stage-tamagotchi/src/composables/tauri.ts | 179 +++++++++++++++- .../src/composables/window-shortcuts.ts | 8 + apps/stage-tamagotchi/src/pages/index.vue | 75 +++---- .../permissions/autogenerated/reference.md | 1 - pnpm-lock.yaml | 20 ++ 13 files changed, 479 insertions(+), 156 deletions(-) create mode 100644 apps/stage-tamagotchi/src/composables/tauri-click-through.ts diff --git a/Cargo.lock b/Cargo.lock index c3518961e..606ed6a49 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -203,7 +203,9 @@ dependencies = [ "tauri-plugin-log", "tauri-plugin-mcp", "tauri-plugin-os", + "tauri-plugin-positioner", "tauri-plugin-prevent-default", + "tauri-plugin-window-state", "tokenizers", "tokio", "url", @@ -404,9 +406,9 @@ dependencies = [ [[package]] name = "brotli" -version = "7.0.0" +version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" +checksum = "9991eea70ea4f293524138648e41ee89b0b2b12ddef3b255effa43c8056e0e0d" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -415,9 +417,9 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "4.0.3" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a334ef7c9e23abf0ce748e8cd309037da93e606ad52eb372e4ce327a0dcfbdfd" +checksum = "874bb8112abecc98cbd6d81ea4fa7e94fb9449648c93cc89aa40c81c24d7de03" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -804,7 +806,7 @@ checksum = "3fdb1325a1cece981e8a296ab8f0f9b63ae357bd0784a9faaf548cc7b480707a" dependencies = [ "castaway", "cfg-if", - "itoa 1.0.15", + "itoa", "rustversion", "ryu", "serde", @@ -1011,15 +1013,15 @@ dependencies = [ [[package]] name = "cssparser" -version = "0.27.2" +version = "0.29.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a" +checksum = "f93d03419cb5950ccfd3daf3ff1c7a36ace64609a1a8746d493df1ca0afde0fa" dependencies = [ "cssparser-macros", "dtoa-short", - "itoa 0.4.8", + "itoa", "matches", - "phf 0.8.0", + "phf 0.10.1", "proc-macro2", "quote", "smallvec", @@ -2313,16 +2315,14 @@ dependencies = [ [[package]] name = "html5ever" -version = "0.26.0" +version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" +checksum = "3b7410cae13cbc75623c98ac4cbfd1f0bedddf3227afc24f370cf0f50a44a11c" dependencies = [ "log", "mac", "markup5ever", - "proc-macro2", - "quote", - "syn 1.0.109", + "match_token", ] [[package]] @@ -2333,7 +2333,7 @@ checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ "bytes", "fnv", - "itoa 1.0.15", + "itoa", ] [[package]] @@ -2378,7 +2378,7 @@ dependencies = [ "http", "http-body", "httparse", - "itoa 1.0.15", + "itoa", "pin-project-lite", "smallvec", "tokio", @@ -2686,12 +2686,6 @@ dependencies = [ "either", ] -[[package]] -name = "itoa" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" - [[package]] name = "itoa" version = "1.0.15" @@ -2788,14 +2782,13 @@ dependencies = [ [[package]] name = "kuchikiki" -version = "0.8.2" +version = "0.8.8-speedreader" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e4755b7b995046f510a7520c42b2fed58b77bd94d5a87a8eb43d2fd126da8" +checksum = "02cb977175687f33fa4afa0c95c112b987ea1443e5a51c8f8ff27dc618270cc2" dependencies = [ "cssparser", "html5ever", - "indexmap 1.9.3", - "matches", + "indexmap 2.9.0", "selectors", ] @@ -2950,18 +2943,29 @@ dependencies = [ [[package]] name = "markup5ever" -version = "0.11.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" +checksum = "c7a7213d12e1864c0f002f52c2923d4556935a43dec5e71355c2760e0f6e7a18" dependencies = [ "log", - "phf 0.10.1", - "phf_codegen 0.10.0", + "phf 0.11.3", + "phf_codegen 0.11.3", "string_cache", "string_cache_codegen", "tendril", ] +[[package]] +name = "match_token" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a9689d8d44bf9964484516275f5cd4c9b59457a6940c1d5d0ecbb94510a36b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.103", +] + [[package]] name = "matches" version = "0.1.10" @@ -3079,9 +3083,9 @@ dependencies = [ [[package]] name = "muda" -version = "0.16.1" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4de14a9b5d569ca68d7c891d613b390cf5ab4f851c77aaa2f9e435555d3d9492" +checksum = "58b89bf91c19bf036347f1ab85a81c560f08c0667c8601bece664d860a600988" dependencies = [ "crossbeam-channel", "dpi", @@ -3762,9 +3766,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" dependencies = [ - "phf_macros 0.8.0", "phf_shared 0.8.0", - "proc-macro-hack", ] [[package]] @@ -3773,7 +3775,9 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" dependencies = [ + "phf_macros 0.10.0", "phf_shared 0.10.0", + "proc-macro-hack", ] [[package]] @@ -3798,12 +3802,12 @@ dependencies = [ [[package]] name = "phf_codegen" -version = "0.10.0" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" +checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" dependencies = [ - "phf_generator 0.10.0", - "phf_shared 0.10.0", + "phf_generator 0.11.3", + "phf_shared 0.11.3", ] [[package]] @@ -3838,12 +3842,12 @@ dependencies = [ [[package]] name = "phf_macros" -version = "0.8.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" +checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0" dependencies = [ - "phf_generator 0.8.0", - "phf_shared 0.8.0", + "phf_generator 0.10.0", + "phf_shared 0.10.0", "proc-macro-hack", "proc-macro2", "quote", @@ -4805,22 +4809,20 @@ dependencies = [ [[package]] name = "selectors" -version = "0.22.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe" +checksum = "0c37578180969d00692904465fb7f6b3d50b9a2b952b87c23d0e2e5cb5013416" dependencies = [ "bitflags 1.3.2", "cssparser", "derive_more", "fxhash", "log", - "matches", "phf 0.8.0", "phf_codegen 0.8.0", "precomputed-hash", "servo_arc", "smallvec", - "thin-slice", ] [[package]] @@ -4886,7 +4888,7 @@ version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ - "itoa 1.0.15", + "itoa", "memchr", "ryu", "serde", @@ -4928,7 +4930,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", - "itoa 1.0.15", + "itoa", "ryu", "serde", ] @@ -4988,9 +4990,9 @@ dependencies = [ [[package]] name = "servo_arc" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432" +checksum = "d52aa42f8fdf0fed91e5ce7f23d8138441002fa31dca008acf47e6fd4721f741" dependencies = [ "nodrop", "stable_deref_trait", @@ -5475,9 +5477,9 @@ dependencies = [ [[package]] name = "tao" -version = "0.33.0" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e59c1f38e657351a2e822eadf40d6a2ad4627b9c25557bc1180ec1b3295ef82" +checksum = "49c380ca75a231b87b6c9dd86948f035012e7171d1a7c40a9c2890489a7ffd8a" dependencies = [ "bitflags 2.9.1", "core-foundation 0.10.1", @@ -5537,17 +5539,16 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tauri" -version = "2.5.1" +version = "2.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7b0bc1aec81bda6bc455ea98fcaed26b3c98c1648c627ad6ff1c704e8bf8cbc" +checksum = "124e129c9c0faa6bec792c5948c89e86c90094133b0b9044df0ce5f0a8efaa0d" dependencies = [ "anyhow", "bytes", "dirs", "dunce", "embed_plist", - "futures-util", - "getrandom 0.2.16", + "getrandom 0.3.3", "glob", "gtk", "heck 0.5.0", @@ -5589,9 +5590,9 @@ dependencies = [ [[package]] name = "tauri-build" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a0350f0df1db385ca5c02888a83e0e66655c245b7443db8b78a70da7d7f8fc" +checksum = "12f025c389d3adb83114bec704da973142e82fc6ec799c7c750c5e21cefaec83" dependencies = [ "anyhow", "cargo_toml", @@ -5611,9 +5612,9 @@ dependencies = [ [[package]] name = "tauri-codegen" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93f035551bf7b11b3f51ad9bc231ebbe5e085565527991c16cf326aa38cdf47" +checksum = "f5df493a1075a241065bc865ed5ef8d0fbc1e76c7afdc0bf0eccfaa7d4f0e406" dependencies = [ "base64 0.22.1", "brotli", @@ -5638,9 +5639,9 @@ dependencies = [ [[package]] name = "tauri-macros" -version = "2.2.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8db4df25e2d9d45de0c4c910da61cd5500190da14ae4830749fee3466dddd112" +checksum = "f237fbea5866fa5f2a60a21bea807a2d6e0379db070d89c3a10ac0f2d4649bbc" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -5652,9 +5653,9 @@ dependencies = [ [[package]] name = "tauri-plugin" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37a5ebe6a610d1b78a94650896e6f7c9796323f408800cef436e0fa0539de601" +checksum = "1d9a0bd00bf1930ad1a604d08b0eb6b2a9c1822686d65d7f4731a7723b8901d3" dependencies = [ "anyhow", "glob", @@ -5736,6 +5737,21 @@ dependencies = [ "thiserror 2.0.12", ] +[[package]] +name = "tauri-plugin-positioner" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a01e373ea3f3f5f46d40f434ba13bd12fa4833aabab50dfc09f6362bad27c95" +dependencies = [ + "log", + "serde", + "serde_json", + "serde_repr", + "tauri", + "tauri-plugin", + "thiserror 2.0.12", +] + [[package]] name = "tauri-plugin-prevent-default" version = "1.3.1" @@ -5752,10 +5768,25 @@ dependencies = [ ] [[package]] -name = "tauri-runtime" -version = "2.6.0" +name = "tauri-plugin-window-state" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00f004905d549854069e6774533d742b03cacfd6f03deb08940a8677586cbe39" +checksum = "5a3d22b21b9cec73601b512a868f7c74f93c044d44fd6ca1c84e9d6afb6b1559" +dependencies = [ + "bitflags 2.9.1", + "log", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "thiserror 2.0.12", +] + +[[package]] +name = "tauri-runtime" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e7bb73d1bceac06c20b3f755b2c8a2cb13b20b50083084a8cf3700daf397ba4" dependencies = [ "cookie", "dpi", @@ -5775,9 +5806,9 @@ dependencies = [ [[package]] name = "tauri-runtime-wry" -version = "2.6.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f85d056f4d4b014fe874814034f3416d57114b617a493a4fe552580851a3f3a2" +checksum = "902b5aa9035e16f342eb64f8bf06ccdc2808e411a2525ed1d07672fa4e780bad" dependencies = [ "gtk", "http", @@ -5802,9 +5833,9 @@ dependencies = [ [[package]] name = "tauri-utils" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2900399c239a471bcff7f15c4399eb1a8c4fe511ba2853e07c996d771a5e0a4" +checksum = "41743bbbeb96c3a100d234e5a0b60a46d5aa068f266160862c7afdbf828ca02e" dependencies = [ "anyhow", "brotli", @@ -5873,12 +5904,6 @@ dependencies = [ "utf-8", ] -[[package]] -name = "thin-slice" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" - [[package]] name = "thiserror" version = "1.0.69" @@ -5926,7 +5951,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" dependencies = [ "deranged", - "itoa 1.0.15", + "itoa", "libc", "num-conv", "num_threads", @@ -6224,9 +6249,9 @@ dependencies = [ [[package]] name = "tray-icon" -version = "0.20.1" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7eee98ec5c90daf179d55c20a49d8c0d043054ce7c26336c09a24d31f14fa0" +checksum = "2da75ec677957aa21f6e0b361df0daab972f13a5bee3606de0638fd4ee1c666a" dependencies = [ "crossbeam-channel", "dirs", @@ -6722,9 +6747,9 @@ dependencies = [ [[package]] name = "webview2-com" -version = "0.37.0" +version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b542b5cfbd9618c46c2784e4d41ba218c336ac70d44c55e47b251033e7d85601" +checksum = "d4ba622a989277ef3886dd5afb3e280e3dd6d974b766118950a08f8f678ad6a4" dependencies = [ "webview2-com-macros", "webview2-com-sys", @@ -6747,9 +6772,9 @@ dependencies = [ [[package]] name = "webview2-com-sys" -version = "0.37.0" +version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae2d11c4a686e4409659d7891791254cf9286d3cfe0eef54df1523533d22295" +checksum = "36695906a1b53a3bf5c4289621efedac12b73eeb0b89e7e1a89b517302d5d75c" dependencies = [ "thiserror 2.0.12", "windows 0.61.3", @@ -7285,9 +7310,9 @@ checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" [[package]] name = "wry" -version = "0.51.2" +version = "0.52.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c886a0a9d2a94fd90cfa1d929629b79cfefb1546e2c7430c63a47f0664c0e4e2" +checksum = "12a714d9ba7075aae04a6e50229d6109e3d584774b99a6a8c60de1698ca111b9" dependencies = [ "base64 0.22.1", "block2 0.6.1", diff --git a/apps/stage-tamagotchi/package.json b/apps/stage-tamagotchi/package.json index f59edaa1c..0f1818328 100644 --- a/apps/stage-tamagotchi/package.json +++ b/apps/stage-tamagotchi/package.json @@ -32,6 +32,8 @@ "@tauri-apps/api": "^2.6.0", "@tauri-apps/plugin-global-shortcut": "^2.3.0", "@tauri-apps/plugin-os": "^2.3.0", + "@tauri-apps/plugin-positioner": "^2.3.0", + "@tauri-apps/plugin-window-state": "^2.3.0", "@tresjs/cientos": "^4.3.1", "@tresjs/core": "^4.3.6", "@vueuse/core": "^13.4.0", diff --git a/apps/stage-tamagotchi/src-tauri/Cargo.toml b/apps/stage-tamagotchi/src-tauri/Cargo.toml index 73ddd4c37..8597ad3a9 100644 --- a/apps/stage-tamagotchi/src-tauri/Cargo.toml +++ b/apps/stage-tamagotchi/src-tauri/Cargo.toml @@ -48,6 +48,8 @@ byteorder = "1.5.0" clap = { version = "4.5.40", features = ["derive"] } tokenizers = "0.21.2" url = "2.5.4" +tauri-plugin-window-state = "2.3.0" +tauri-plugin-positioner = "2.3.0" [target.'cfg(target_os = "macos")'.dependencies] objc2 = "0.6.1" diff --git a/apps/stage-tamagotchi/src-tauri/src/app_windows/chat.rs b/apps/stage-tamagotchi/src-tauri/src/app_windows/chat.rs index fd7af2fce..87c7f56fb 100644 --- a/apps/stage-tamagotchi/src-tauri/src/app_windows/chat.rs +++ b/apps/stage-tamagotchi/src-tauri/src/app_windows/chat.rs @@ -1,10 +1,11 @@ use std::path::Path; +use anyhow::{Ok, Result}; #[cfg(target_os = "macos")] use tauri::TitleBarStyle; use tauri::{WebviewUrl, WebviewWindowBuilder}; -pub fn new_chat_window(app: &tauri::AppHandle) -> Result { +pub fn new_chat_window(app: &tauri::AppHandle) -> Result { let mut builder = WebviewWindowBuilder::new( app, "chat", @@ -28,5 +29,7 @@ pub fn new_chat_window(app: &tauri::AppHandle) -> Result Result { +pub fn new_settings_window(app: &tauri::AppHandle) -> Result { let mut builder = WebviewWindowBuilder::new( app, "settings", @@ -28,5 +29,7 @@ pub fn new_settings_window(app: &tauri::AppHandle) -> Result anyhow::Result<()> { candle_core::Device::Cpu }; - info!("Using device: {device:?}"); - let whisper_model = whisper::whisper::WhichWhisperModel::Tiny; - - info!("Loading whisper model: {:?}", whisper_model); - let _ = whisper::whisper::WhisperProcessor::new(whisper_model, device.clone(), progress_manager)?; + Ok(()) } #[tauri::command] async fn load_models(window: tauri::Window) -> Result<(), String> { - println!("load_models"); - load_whisper_model(window).unwrap(); + let _ = load_whisper_model(window); + Ok(()) } @@ -175,11 +171,11 @@ pub fn run() { .plugin(tauri_plugin_mcp::Builder.build()) .plugin(tauri_plugin_os::init()) .plugin(tauri_plugin_global_shortcut::Builder::new().build()) + .plugin(tauri_plugin_window_state::Builder::default().build()) + .plugin(tauri_plugin_positioner::init()) .manage(WindowClickThroughState::default()) .setup(|app| { - let mut builder = WebviewWindowBuilder::new(app, "main", WebviewUrl::default()); - - builder = builder + let mut builder = WebviewWindowBuilder::new(app, "main", WebviewUrl::default()) .title("AIRI") .decorations(false) .inner_size(450.0, 600.0) @@ -212,7 +208,9 @@ pub fn run() { let settings_item = MenuItem::with_id(app, "settings", "Settings", true, None::<&str>)?; let hide_item = MenuItem::with_id(app, "hide", "Hide", true, None::<&str>)?; let show_item = MenuItem::with_id(app, "show", "Show", true, None::<&str>)?; + let menu = Menu::with_items(app, &[&settings_item, &hide_item, &show_item, &quit_item])?; + #[cfg(debug_assertions)] { let show_devtools_item = @@ -257,12 +255,13 @@ pub fn run() { if let Some(window) = window { window.open_devtools(); } - } + }, _ => {} }) .show_menu_on_left_click(true) .build(app) .unwrap(); + Ok(()) }) .invoke_handler(tauri::generate_handler![ diff --git a/apps/stage-tamagotchi/src-tauri/src/whisper/whisper.rs b/apps/stage-tamagotchi/src-tauri/src/whisper/whisper.rs index e3e35558d..0eba9da9a 100644 --- a/apps/stage-tamagotchi/src-tauri/src/whisper/whisper.rs +++ b/apps/stage-tamagotchi/src-tauri/src/whisper/whisper.rs @@ -6,6 +6,7 @@ use candle_nn::VarBuilder; use candle_transformers::models::whisper::{self as whisper_model, Config, audio}; use clap::ValueEnum; use hf_hub::{Repo, RepoType, api::sync::ApiBuilder}; +use log::info; use tokenizers::Tokenizer; use crate::whisper::progress; @@ -116,20 +117,20 @@ impl WhisperProcessor { let config_filename = repo.download_with_progress("config.json", manager.clone().new_for("config.json"))?; - println!("config_filename: {:?}", config_filename.display()); + info!("config_filename: {:?}", config_filename.display()); let tokenizer_filename = repo.download_with_progress("tokenizer.json", manager.clone().new_for("tokenizer.json"))?; - println!("tokenizer_filename: {:?}", tokenizer_filename.display()); + info!("tokenizer_filename: {:?}", tokenizer_filename.display()); let model_filename = repo.download_with_progress( "model.safetensors", manager.clone().new_for("model.safetensors"), )?; - println!("model_filename: {:?}", model_filename.display()); + info!("model_filename: {:?}", model_filename.display()); let config: Config = serde_json::from_str(&std::fs::read_to_string(config_filename)?)?; let tokenizer = Tokenizer::from_file(tokenizer_filename).map_err(anyhow::Error::msg)?; - println!("Loading Whisper model from: {:?}", model_filename.display()); + info!("Loading Whisper model from: {:?}", model_filename.display()); // SAFETY: This is safe because we are using a mmaped file and the safetensors library guarantees that the data is valid. let var_builder = unsafe { diff --git a/apps/stage-tamagotchi/src/composables/tauri-click-through.ts b/apps/stage-tamagotchi/src/composables/tauri-click-through.ts new file mode 100644 index 000000000..637dcdd86 --- /dev/null +++ b/apps/stage-tamagotchi/src/composables/tauri-click-through.ts @@ -0,0 +1,105 @@ +import type { MaybeRefOrGetter } from 'vue' + +import type { AiriTamagotchiEvents, Point, WindowFrame } from './tauri' + +import { onMounted, ref, toValue, watch } from 'vue' + +import { useAppRuntime } from './runtime' +import { useTauriCore, useTauriEvent } from './tauri' + +export function useTauriPointAndWindowFrame() { + const { listen } = useTauriEvent() + const { invoke } = useTauriCore() + + const unListenFuncs = ref<(() => void)[]>([]) + + const mousePos = ref({ x: 0, y: 0 }) + const windowFrame = ref({ + origin: { x: 0, y: 0 }, + size: { width: 0, height: 0 }, + }) + + function _on(event: { payload: [Point, WindowFrame] }) { + const [pos, frame] = event.payload + + mousePos.value = pos + windowFrame.value = frame + } + + onMounted(() => { + listen('tauri-app:window-click-through:position-cursor-and-window-frame', (event) => { + _on(event) + }).then((fn) => { + unListenFuncs.value.push(fn) + }) + }) + + if (import.meta.hot) { // For better DX + import.meta.hot.on('vite:beforeUpdate', () => { + unListenFuncs.value.forEach(fn => fn?.()) + unListenFuncs.value.length = 0 + + invoke('stop_monitor') + }) + + import.meta.hot.on('vite:afterUpdate', async () => { + if (unListenFuncs.value.length === 0) { + unListenFuncs.value.push(await listen('tauri-app:window-click-through:position-cursor-and-window-frame', (event) => { + _on(event) + })) + } + + invoke('start_monitor') + }) + } + + return { + mousePos, + windowFrame, + } +} + +export function useTauriWindowClickThrough(live2DLookAtDefault: MaybeRefOrGetter<{ x: number, y: number }>) { + const { platform, isInitialized } = useAppRuntime() + const { mousePos, windowFrame } = useTauriPointAndWindowFrame() + + const live2dLookAtX = ref(toValue(live2DLookAtDefault).x) + const live2dLookAtY = ref(toValue(live2DLookAtDefault).y) + + const isCursorInside = ref(false) + + function updateLive2DLookAt() { + if (platform.value === 'macos') { + live2dLookAtX.value = mousePos.value.x - windowFrame.value.origin.x + live2dLookAtY.value = windowFrame.value.size.height - mousePos.value.y + windowFrame.value.origin.y + + return + } + + live2dLookAtX.value = mousePos.value.x - windowFrame.value.origin.x + live2dLookAtY.value = mousePos.value.y - windowFrame.value.origin.y + } + + watch(mousePos, () => updateLive2DLookAt()) + + function updateIsCursorInside() { + isCursorInside.value + = mousePos.value.x >= windowFrame.value.origin.x + && mousePos.value.x <= windowFrame.value.origin.x + windowFrame.value.size.width + && mousePos.value.y >= windowFrame.value.origin.y + && mousePos.value.y <= windowFrame.value.origin.y + windowFrame.value.size.height + } + + watch(isInitialized, async (initialized) => { + if (initialized) { + updateLive2DLookAt() + updateIsCursorInside() + } + }) + + return { + isCursorInside, + live2dLookAtX, + live2dLookAtY, + } +} diff --git a/apps/stage-tamagotchi/src/composables/tauri.ts b/apps/stage-tamagotchi/src/composables/tauri.ts index f044f2447..8622c57a3 100644 --- a/apps/stage-tamagotchi/src/composables/tauri.ts +++ b/apps/stage-tamagotchi/src/composables/tauri.ts @@ -1,5 +1,7 @@ import type { InvokeArgs, InvokeOptions } from '@tauri-apps/api/core' import type { EventCallback, EventName, UnlistenFn } from '@tauri-apps/api/event' +import type { Position } from '@tauri-apps/plugin-positioner' +import type { StateFlags } from '@tauri-apps/plugin-window-state' import { withRetry } from '@moeru/std' import { computedAsync, until } from '@vueuse/core' @@ -50,13 +52,64 @@ interface Events { export interface AiriTamagotchiEvents extends Events { 'tauri-app:window-click-through:position-cursor-and-window-frame': [Point, WindowFrame] 'tauri-app:model-load-progress': [string, number] + 'tauri-app:window-position-changed': WindowPosition + 'tauri-app:window-state-saved': undefined + 'tauri-app:window-state-restored': undefined + 'tauri-app:display-changed': DisplayInfo 'mcp_plugin_destroyed': undefined + 'tauri-app:invoke-returns:plugins-window-get-display-info': [[number, number], [number, number]] +} + +export interface Monitor { + id: number + name: string + is_primary: boolean + bounds: WindowFrame + work_area: WindowFrame + scale_factor: number +} + +export interface DisplayInfo { + monitors: Monitor[] + primary_monitor_id: number +} + +export interface WindowPosition { + x: number + y: number + width: number + height: number +} + +export enum PlacementStrategy { + Center = 'Center', + TopLeft = 'TopLeft', + TopRight = 'TopRight', + BottomLeft = 'BottomLeft', + BottomRight = 'BottomRight', + NearCursor = 'NearCursor', + RestorePrevious = 'RestorePrevious', + AvoidOverlap = 'AvoidOverlap', +} + +export interface WindowPlacementRequest { + window_size: Size + preferred_position?: Point + placement_strategy: PlacementStrategy +} + +export interface WindowPlacementResult { + position: WindowPosition + target_monitor_id: number + is_constrained: boolean } export function useTauriEvent() { const { platform, isInitialized } = useAppRuntime() - const tauriEventApi = computedAsync(() => { + const tauriEventApi = computedAsync(async () => { + await until(isInitialized).toBeTruthy() + if (platform.value !== 'web') { return untilImported(() => import('@tauri-apps/api/event'), console.warn) } @@ -75,7 +128,7 @@ export function useTauriEvent() { throw new Error('Tauri event API not available') } - return untilNoError(() => imported.listen(event as EventName, callback), console.warn) + return await imported.listen(event as EventName, callback) } async function listen(event: E, callback: EventCallback) { @@ -110,6 +163,38 @@ export interface InvokeMethods { options: undefined returns: void } + + // Window positioning methods + plugins_window_get_display_info: { + args: undefined + options: undefined + returns: DisplayInfo + } + plugins_window_calculate_window_placement: { + args: { request: WindowPlacementRequest } + options: undefined + returns: WindowPlacementResult + } + plugins_window_apply_window_position: { + args: { position: WindowPosition } + options: undefined + returns: void + } + plugins_window_save_window_state: { + args: undefined + options: undefined + returns: void + } + plugins_window_restore_window_state: { + args: undefined + options: undefined + returns: void + } + plugins_window_get_current_window_info: { + args: undefined + options: undefined + returns: WindowPosition + } } interface InvokeMethodShape { @@ -121,7 +206,9 @@ interface InvokeMethodShape { export function useTauriCore = InvokeMethods>() { const { platform, isInitialized } = useAppRuntime() - const tauriCoreApi = computedAsync(() => { + const tauriCoreApi = computedAsync(async () => { + await until(isInitialized).toBeTruthy() + if (platform.value !== 'web') { return untilImported(() => import('@tauri-apps/api/core'), console.warn) } @@ -145,10 +232,94 @@ export function useTauriCore = In throw new Error('Tauri core API not available') } - return await untilNoError(() => imported.invoke(command as string, args as InvokeArgs | undefined, options as InvokeOptions | undefined), console.warn) + return await imported.invoke(command as string, args as InvokeArgs | undefined, options as InvokeOptions | undefined) } return { invoke, } } + +export function useTauriPositioner() { + const { platform, isInitialized } = useAppRuntime() + + const tauriPositionerApi = computedAsync(async () => { + await until(isInitialized).toBeTruthy() + + if (platform.value !== 'web') { + return untilImported(() => import('@tauri-apps/plugin-positioner'), console.warn) + } + }) + + async function ensureImported() { + await until(isInitialized).toBeTruthy() + + if (platform.value === 'web') { + console.warn('Attempted to use Tauri positioner in web platform') + return + } + + await until(tauriPositionerApi).toBeTruthy() + const imported = await tauriPositionerApi.value + if (!imported) { + throw new Error('Tauri positioner API not available') + } + } + + async function moveWindow(to: Position) { + await ensureImported() + return tauriPositionerApi.value?.moveWindow(to) + } + + return { + moveWindow, + } +} + +export function useTauriWindowState() { + const { platform, isInitialized } = useAppRuntime() + + const tauriWindowStateApi = computedAsync(async () => { + await until(isInitialized).toBeTruthy() + + if (platform.value !== 'web') { + return untilImported(() => import('@tauri-apps/plugin-window-state'), console.warn) + } + }) + + async function ensureImported() { + await until(isInitialized).toBeTruthy() + + if (platform.value === 'web') { + console.warn('Attempted to save window state in web platform') + return + } + + await until(tauriWindowStateApi).toBeTruthy() + const imported = await tauriWindowStateApi.value + if (!imported) { + throw new Error('Tauri window state API not available') + } + } + + async function saveWindowState(stateFlag: StateFlags) { + await ensureImported() + return tauriWindowStateApi.value?.saveWindowState(stateFlag) + } + + async function restoreState(stateFlag: StateFlags, windowLabel = 'main') { + await ensureImported() + return tauriWindowStateApi.value?.restoreState(windowLabel, stateFlag) + } + + async function restoreStateCurrent(stateFlag: StateFlags) { + await ensureImported() + return tauriWindowStateApi.value?.restoreStateCurrent(stateFlag) + } + + return { + saveWindowState, + restoreState, + restoreStateCurrent, + } +} diff --git a/apps/stage-tamagotchi/src/composables/window-shortcuts.ts b/apps/stage-tamagotchi/src/composables/window-shortcuts.ts index fb8df51fa..1953d348e 100644 --- a/apps/stage-tamagotchi/src/composables/window-shortcuts.ts +++ b/apps/stage-tamagotchi/src/composables/window-shortcuts.ts @@ -1,15 +1,23 @@ import type { ShortcutEvent } from '@tauri-apps/plugin-global-shortcut' import { register, unregisterAll } from '@tauri-apps/plugin-global-shortcut' +import { until } from '@vueuse/shared' import { storeToRefs } from 'pinia' import { watch } from 'vue' import { useShortcutsStore } from '../stores/shortcuts' +import { useAppRuntime } from './runtime' export function useWindowShortcuts() { const { shortcuts } = storeToRefs(useShortcutsStore()) + const { platform, isInitialized } = useAppRuntime() watch(shortcuts, async () => { + await until(isInitialized).toBeTruthy() + if (platform.value === 'web') { + return + } + await unregisterAll() for (const handler of shortcuts.value) { diff --git a/apps/stage-tamagotchi/src/pages/index.vue b/apps/stage-tamagotchi/src/pages/index.vue index 5eeee2904..57227ea32 100644 --- a/apps/stage-tamagotchi/src/pages/index.vue +++ b/apps/stage-tamagotchi/src/pages/index.vue @@ -1,16 +1,16 @@