diff --git a/loading.html b/loading.html
new file mode 100644
index 0000000..e2b1388
--- /dev/null
+++ b/loading.html
@@ -0,0 +1,67 @@
+
+
+
+
+
+ 启动中...
+
+
+
+
+
+
diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock
index 314ad0c..8956323 100644
--- a/src-tauri/Cargo.lock
+++ b/src-tauri/Cargo.lock
@@ -791,6 +791,15 @@ version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7"
+[[package]]
+name = "encoding_rs"
+version = "0.8.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
+dependencies = [
+ "cfg-if",
+]
+
[[package]]
name = "endi"
version = "1.1.1"
@@ -2299,6 +2308,16 @@ dependencies = [
"pin-project-lite",
]
+[[package]]
+name = "os_pipe"
+version = "1.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967"
+dependencies = [
+ "libc",
+ "windows-sys 0.61.2",
+]
+
[[package]]
name = "pango"
version = "0.18.3"
@@ -3190,12 +3209,44 @@ dependencies = [
"digest",
]
+[[package]]
+name = "shared_child"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e362d9935bc50f019969e2f9ecd66786612daae13e8f277be7bfb66e8bed3f7"
+dependencies = [
+ "libc",
+ "sigchld",
+ "windows-sys 0.60.2",
+]
+
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+[[package]]
+name = "sigchld"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "47106eded3c154e70176fc83df9737335c94ce22f821c32d17ed1db1f83badb1"
+dependencies = [
+ "libc",
+ "os_pipe",
+ "signal-hook",
+]
+
+[[package]]
+name = "signal-hook"
+version = "0.3.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2"
+dependencies = [
+ "libc",
+ "signal-hook-registry",
+]
+
[[package]]
name = "signal-hook-registry"
version = "1.4.8"
@@ -3563,6 +3614,8 @@ dependencies = [
"tauri",
"tauri-build",
"tauri-plugin-opener",
+ "tauri-plugin-shell",
+ "tokio",
]
[[package]]
@@ -3618,6 +3671,27 @@ dependencies = [
"zbus",
]
+[[package]]
+name = "tauri-plugin-shell"
+version = "2.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "39b76f884a3937e04b631ffdc3be506088fa979369d25147361352f2f352e5ed"
+dependencies = [
+ "encoding_rs",
+ "log",
+ "open",
+ "os_pipe",
+ "regex",
+ "schemars 0.8.22",
+ "serde",
+ "serde_json",
+ "shared_child",
+ "tauri",
+ "tauri-plugin",
+ "thiserror 2.0.17",
+ "tokio",
+]
+
[[package]]
name = "tauri-runtime"
version = "2.9.2"
diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml
index 94ef6e7..1528511 100644
--- a/src-tauri/Cargo.toml
+++ b/src-tauri/Cargo.toml
@@ -20,6 +20,8 @@ tauri-build = { version = "2", features = [] }
[dependencies]
tauri = { version = "2", features = [] }
tauri-plugin-opener = "2"
+tauri-plugin-shell = "2"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
+tokio = { version = "1", features = ["net"] }
diff --git a/src-tauri/binaries/.gitignore b/src-tauri/binaries/.gitignore
new file mode 100644
index 0000000..d6b7ef3
--- /dev/null
+++ b/src-tauri/binaries/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore
diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json
index 4cdbf49..e847cbb 100644
--- a/src-tauri/capabilities/default.json
+++ b/src-tauri/capabilities/default.json
@@ -5,6 +5,15 @@
"windows": ["main"],
"permissions": [
"core:default",
- "opener:default"
+ "opener:default",
+ {
+ "identifier": "shell:allow-execute",
+ "allow": [
+ {
+ "name": "binaries/nitro-server",
+ "sidecar": true
+ }
+ ]
+ }
]
}
diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs
index 4a277ef..bcc634c 100644
--- a/src-tauri/src/lib.rs
+++ b/src-tauri/src/lib.rs
@@ -1,13 +1,99 @@
+use tauri_plugin_shell::ShellExt;
+use tauri_plugin_shell::process::CommandEvent;
+use std::time::Duration;
+
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
#[tauri::command]
fn greet(name: &str) -> String {
format!("Hello, {}! You've been greeted from Rust!", name)
}
+// 检查端口是否可用
+async fn is_port_available(port: u16) -> bool {
+ tokio::net::TcpListener::bind(format!("127.0.0.1:{}", port))
+ .await
+ .is_ok()
+}
+
+// 查找可用端口
+async fn find_available_port(start: u16) -> u16 {
+ for port in start..start + 100 {
+ if is_port_available(port).await {
+ return port;
+ }
+ }
+ start // 回退到起始端口
+}
+
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_opener::init())
+ .plugin(tauri_plugin_shell::init())
+ .setup(|app| {
+ let app_handle = app.handle().clone();
+
+ // 异步启动 Nitro sidecar
+ tauri::async_runtime::spawn(async move {
+ // 查找可用端口
+ let port = find_available_port(3000).await;
+ println!("使用端口: {}", port);
+
+ // 启动 sidecar
+ let sidecar = app_handle
+ .shell()
+ .sidecar("nitro-server")
+ .expect("无法找到 nitro-server sidecar")
+ .env("NITRO_PORT", port.to_string());
+
+ let (mut rx, mut _child) = sidecar.spawn().expect("启动 sidecar 失败");
+
+ // 监听 stdout,等待服务器就绪信号
+ let start_time = std::time::Instant::now();
+ let timeout = Duration::from_secs(5);
+ let mut server_ready = false;
+
+ while let Some(event) = rx.recv().await {
+ if let CommandEvent::Stdout(line) = event {
+ let output = String::from_utf8_lossy(&line);
+ println!("Nitro: {}", output);
+
+ // 检测服务器启动成功的标志
+ if output.contains("Listening on:") || output.contains("localhost") {
+ server_ready = true;
+ println!("✓ Nitro 服务器启动成功!");
+
+ // 创建主窗口
+ let url = format!("http://localhost:{}", port);
+ tauri::WebviewWindowBuilder::new(
+ &app_handle,
+ "main",
+ tauri::WebviewUrl::External(url.parse().unwrap())
+ )
+ .title("Nitro Application")
+ .inner_size(1200.0, 800.0)
+ .build()
+ .expect("创建窗口失败");
+
+ break;
+ }
+ }
+
+ // 超时检查
+ if start_time.elapsed() > timeout {
+ eprintln!("✗ 启动超时:Nitro 服务器未能在 5 秒内启动");
+ break;
+ }
+ }
+
+ if !server_ready {
+ eprintln!("✗ Nitro 服务器启动失败");
+ std::process::exit(1);
+ }
+ });
+
+ Ok(())
+ })
.invoke_handler(tauri::generate_handler![greet])
.run(tauri::generate_context!())
.expect("error while running tauri application");
diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json
index e2f6e57..3cb8041 100644
--- a/src-tauri/tauri.conf.json
+++ b/src-tauri/tauri.conf.json
@@ -11,13 +11,7 @@
},
"app": {
"withGlobalTauri": true,
- "windows": [
- {
- "title": "tauri-demo",
- "width": 800,
- "height": 600
- }
- ],
+ "windows": [],
"security": {
"csp": null
}
@@ -31,6 +25,9 @@
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico"
+ ],
+ "externalBin": [
+ "binaries/nitro-server"
]
}
}