fix(stage-tamagotchi): failed to listen update

This commit is contained in:
Neko Ayaka
2025-06-29 23:17:57 +08:00
parent ad1afb4bd1
commit df4ddb9b7a
13 changed files with 479 additions and 156 deletions
Generated
+111 -86
View File
@@ -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",
+2
View File
@@ -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",
@@ -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"
@@ -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<tauri::WebviewWindow, tauri::Error> {
pub fn new_chat_window(app: &tauri::AppHandle) -> Result<tauri::WebviewWindow> {
let mut builder = WebviewWindowBuilder::new(
app,
"chat",
@@ -28,5 +29,7 @@ pub fn new_chat_window(app: &tauri::AppHandle) -> Result<tauri::WebviewWindow, t
builder = builder.traffic_light_position(tauri::LogicalPosition::new(14.0, 20.0));
}
return builder.build();
let window = builder.build()?;
Ok(window)
}
@@ -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_settings_window(app: &tauri::AppHandle) -> Result<tauri::WebviewWindow, tauri::Error> {
pub fn new_settings_window(app: &tauri::AppHandle) -> Result<tauri::WebviewWindow> {
let mut builder = WebviewWindowBuilder::new(
app,
"settings",
@@ -28,5 +29,7 @@ pub fn new_settings_window(app: &tauri::AppHandle) -> Result<tauri::WebviewWindo
builder = builder.traffic_light_position(tauri::LogicalPosition::new(14.0, 20.0));
}
return builder.build();
let window = builder.build()?;
Ok(window)
}
+11 -12
View File
@@ -1,4 +1,4 @@
use std::{str::FromStr, sync::atomic::Ordering, time::Duration};
use std::{sync::atomic::Ordering, time::Duration};
use log::info;
use tauri::{
@@ -109,20 +109,16 @@ fn load_whisper_model(window: tauri::Window) -> 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![
@@ -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 {
@@ -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<AiriTamagotchiEvents>()
const { invoke } = useTauriCore()
const unListenFuncs = ref<(() => void)[]>([])
const mousePos = ref<Point>({ x: 0, y: 0 })
const windowFrame = ref<WindowFrame>({
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,
}
}
+175 -4
View File
@@ -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<ES = Events>() {
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<ES = Events>() {
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<E extends keyof ES>(event: E, callback: EventCallback<ES[E]>) {
@@ -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<IM extends Record<keyof IM, InvokeMethodShape> = 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<IM extends Record<keyof IM, InvokeMethodShape> = 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,
}
}
@@ -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) {
+30 -45
View File
@@ -1,16 +1,16 @@
<script setup lang="ts">
import type { AiriTamagotchiEvents, Point, WindowFrame } from '../composables/tauri'
import type { AiriTamagotchiEvents, Point } from '../composables/tauri'
import { WidgetStage } from '@proj-airi/stage-ui/components'
import { useMcpStore } from '@proj-airi/stage-ui/stores'
import { connectServer } from '@proj-airi/tauri-plugin-mcp'
import { invoke } from '@tauri-apps/api/core'
import { useWindowSize } from '@vueuse/core'
import { storeToRefs } from 'pinia'
import { computed, onMounted, onUnmounted, ref } from 'vue'
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
import ResourceStatusIsland from '../components/Widgets/ResourceStatusIsland/index.vue'
import { useAppRuntime } from '../composables/runtime'
import { useTauriEvent } from '../composables/tauri'
import { useTauriCore, useTauriEvent } from '../composables/tauri'
import { useTauriWindowClickThrough } from '../composables/tauri-click-through'
import { useWindowShortcuts } from '../composables/window-shortcuts'
import { useResourcesStore } from '../stores/resources'
import { useWindowControlStore } from '../stores/window-controls'
@@ -18,15 +18,34 @@ import { WindowControlMode } from '../types/window-controls'
import { startClickThrough, stopClickThrough } from '../utils/windows'
useWindowShortcuts()
const { platform } = useAppRuntime()
const windowStore = useWindowControlStore()
const mcpStore = useMcpStore()
const { listen } = useTauriEvent<AiriTamagotchiEvents>()
// const { invoke: wrappedInvoke } = useTauriCore()
const { width, height } = useWindowSize()
const centerPos = computed(() => ({ x: width.value / 2, y: height.value / 2 }))
const live2dFocusAt = ref<Point>(centerPos.value)
const isCursorInside = ref(false)
const shouldHideView = computed(() => isCursorInside.value && !windowStore.isControlActive && windowStore.isIgnoringMouseEvent)
const { listen } = useTauriEvent<AiriTamagotchiEvents>()
const { invoke } = useTauriCore()
const { live2dLookAtX, live2dLookAtY } = useTauriWindowClickThrough(centerPos)
const mcpStore = useMcpStore()
const { connected, serverCmd, serverArgs } = storeToRefs(mcpStore)
watch([live2dLookAtX, live2dLookAtY], ([x, y]) => live2dFocusAt.value = { x, y }, { immediate: true })
// // Initialize window persistence system
// const windowPersistence = useWindowPersistence({
// autoSave: true,
// autoRestore: true,
// constrainToDisplays: true,
// centerPointConstraint: true,
// scaleAware: true,
// monitorDisplayChanges: true
// })
const modeIndicatorClass = computed(() => {
switch (windowStore.controlMode) {
case WindowControlMode.MOVE:
@@ -60,33 +79,8 @@ function openChat() {
invoke('open_chat_window')
}
const shouldHideView = computed(() => {
return isCursorInside.value && !windowStore.isControlActive && windowStore.isIgnoringMouseEvent
})
const live2dFocusAt = ref<Point>({ x: window.innerWidth / 2, y: window.innerHeight / 2 })
function onTauriPositionCursorAndWindowFrameEvent(event: { payload: [Point, WindowFrame] }) {
const [mouseLocation, windowFrame] = event.payload
isCursorInside.value = mouseLocation.x >= windowFrame.origin.x && mouseLocation.x <= windowFrame.origin.x + windowFrame.size.width && mouseLocation.y >= windowFrame.origin.y && mouseLocation.y <= windowFrame.origin.y + windowFrame.size.height
if (platform.value === 'macos') {
live2dFocusAt.value = {
x: mouseLocation.x - windowFrame.origin.x,
y: windowFrame.size.height - mouseLocation.y + windowFrame.origin.y,
}
return
}
live2dFocusAt.value = {
x: mouseLocation.x - windowFrame.origin.x,
y: mouseLocation.y - windowFrame.origin.y,
}
}
onMounted(async () => {
unListenFuncs.push(await listen('tauri-app:window-click-through:position-cursor-and-window-frame', onTauriPositionCursorAndWindowFrameEvent))
unListenFuncs.push(await listen('tauri-app:model-load-progress', useResourcesStore().appendResource))
unListenFuncs.push(await listen('tauri-app:model-load-progress', event => useResourcesStore().appendResource(event)))
// Load models
invoke('load_models')
@@ -117,10 +111,6 @@ if (import.meta.hot) { // For better DX
invoke('stop_monitor')
})
import.meta.hot.on('vite:afterUpdate', async () => {
if (unListenFuncs.length === 0) {
unListenFuncs.push(await listen('tauri-app:window-click-through:position-cursor-and-window-frame', onTauriPositionCursorAndWindowFrameEvent))
}
invoke('start_monitor')
})
}
@@ -183,7 +173,7 @@ if (import.meta.hot) { // For better DX
<div
v-if="windowStore.controlMode === WindowControlMode.MOVE"
data-tauri-drag-region
class="drag-region absolute left-0 top-0 z-999 h-full w-full flex items-center justify-center overflow-hidden"
class="absolute left-0 top-0 z-999 h-full w-full flex cursor-grab items-center justify-center overflow-hidden"
>
<div
class="absolute h-32 w-full flex items-center justify-center overflow-hidden rounded-xl"
@@ -225,11 +215,6 @@ if (import.meta.hot) { // For better DX
}
}
.drag-region {
app-region: drag;
cursor: grab !important;
}
@keyframes wall-move {
0% {
transform: translateX(calc(var(--wall-width) * -2));
@@ -7,7 +7,6 @@ operations are available from the mcp plugin.
All operations are enabled by default.
#### This default permission set includes the following:
- `allow-connect-server`
+20
View File
@@ -312,6 +312,12 @@ importers:
'@tauri-apps/plugin-os':
specifier: ^2.3.0
version: 2.3.0
'@tauri-apps/plugin-positioner':
specifier: ^2.3.0
version: 2.3.0
'@tauri-apps/plugin-window-state':
specifier: ^2.3.0
version: 2.3.0
'@tresjs/cientos':
specifier: ^4.3.1
version: 4.3.1(@tresjs/core@4.3.6(three@0.177.0)(typescript@5.8.3)(vue@3.5.17(typescript@5.8.3)))(@types/three@0.177.0)(react@18.3.1)(three@0.177.0)(typescript@5.8.3)(vue@3.5.17(typescript@5.8.3))
@@ -4808,6 +4814,12 @@ packages:
'@tauri-apps/plugin-os@2.3.0':
resolution: {integrity: sha512-dm3bDsMuUngpIQdJ1jaMkMfyQpHyDcaTIKTFaAMHoKeUd+Is3UHO2uzhElr6ZZkfytIIyQtSVnCWdW2Kc58f3g==}
'@tauri-apps/plugin-positioner@2.3.0':
resolution: {integrity: sha512-YMRcvA+WcULnAS+UjqGLP9ekWAgmBCgvkzT2OTkE8TWcu9jVo/Z79K0nEJhDuhZO/O0b77X0Opd+wDkEDYPeDw==}
'@tauri-apps/plugin-window-state@2.3.0':
resolution: {integrity: sha512-BfhM0gm6jsF+VyYeOZ3eNqDfRJ2YPMh0a5qesD8AizVGv7wVB3cGLqh6EaeMpGl7y6kP9lvEMYkDRHuqKbcsgw==}
'@tokenizer/token@0.3.0':
resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==}
@@ -15385,6 +15397,14 @@ snapshots:
dependencies:
'@tauri-apps/api': 2.6.0
'@tauri-apps/plugin-positioner@2.3.0':
dependencies:
'@tauri-apps/api': 2.6.0
'@tauri-apps/plugin-window-state@2.3.0':
dependencies:
'@tauri-apps/api': 2.6.0
'@tokenizer/token@0.3.0': {}
'@tresjs/cientos@4.3.1(@tresjs/core@4.3.6(three@0.177.0)(typescript@5.8.3)(vue@3.5.17(typescript@5.8.3)))(@types/three@0.177.0)(react@18.3.1)(three@0.177.0)(typescript@5.8.3)(vue@3.5.17(typescript@5.8.3))':