diff options
| author | Brendan Allan <[email protected]> | 2025-12-18 17:17:31 +0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-12-18 03:17:31 -0600 |
| commit | b70d186bd1e33adea9efdfc6d08788bffa9056f6 (patch) | |
| tree | e5cdcc190d4f7fddea72a7718bd743e8b3127868 | |
| parent | 647331de28fc27052aa530b4e6d89ff3beb4ec11 (diff) | |
| download | opencode-b70d186bd1e33adea9efdfc6d08788bffa9056f6.tar.gz opencode-b70d186bd1e33adea9efdfc6d08788bffa9056f6.zip | |
tauri: server spawn fail dialog w/ copy logs button (#5729)
| -rw-r--r-- | packages/tauri/src-tauri/Cargo.lock | 290 | ||||
| -rw-r--r-- | packages/tauri/src-tauri/Cargo.toml | 1 | ||||
| -rw-r--r-- | packages/tauri/src-tauri/src/lib.rs | 96 |
3 files changed, 376 insertions, 11 deletions
diff --git a/packages/tauri/src-tauri/Cargo.lock b/packages/tauri/src-tauri/Cargo.lock index 42a7dae81..647421b05 100644 --- a/packages/tauri/src-tauri/Cargo.lock +++ b/packages/tauri/src-tauri/Cargo.lock @@ -57,6 +57,27 @@ dependencies = [ ] [[package]] +name = "arboard" +version = "3.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0348a1c054491f4bfe6ab86a7b6ab1e44e45d899005de92f58b3df180b36ddaf" +dependencies = [ + "clipboard-win", + "image", + "log", + "objc2 0.6.3", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation 0.3.2", + "parking_lot", + "percent-encoding", + "windows-sys 0.60.2", + "wl-clipboard-rs", + "x11rb", +] + +[[package]] name = "ashpd" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -350,6 +371,12 @@ 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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -487,6 +514,15 @@ dependencies = [ ] [[package]] +name = "clipboard-win" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4" +dependencies = [ + "error-code", +] + +[[package]] name = "combine" version = "4.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -595,6 +631,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] name = "crypto-common" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -928,6 +970,12 @@ dependencies = [ ] [[package]] +name = "error-code" +version = "3.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" + +[[package]] name = "event-listener" version = "5.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -955,6 +1003,26 @@ 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.110", +] + +[[package]] name = "fdeflate" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -992,6 +1060,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" [[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + +[[package]] name = "flate2" version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1008,6 +1082,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] name = "foreign-types" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1453,6 +1533,17 @@ dependencies = [ ] [[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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1460,6 +1551,15 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" @@ -1633,7 +1733,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc50b891e4acf8fe0e71ef88ec43ad82ee07b3810ad09de10f1d01f072ed4b98" dependencies = [ "byteorder", - "png", + "png 0.17.16", ] [[package]] @@ -1745,6 +1845,20 @@ dependencies = [ ] [[package]] +name = "image" +version = "0.25.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6506c6c10786659413faa717ceebcb8f70731c0a60cbae39795fdf114519c1a" +dependencies = [ + "bytemuck", + "byteorder-lite", + "moxcms", + "num-traits", + "png 0.18.0", + "tiff", +] + +[[package]] name = "indexmap" version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2104,6 +2218,16 @@ dependencies = [ ] [[package]] +name = "moxcms" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac9557c559cd6fc9867e122e20d2cbefc9ca29d80d027a8e39310920ed2f0a97" +dependencies = [ + "num-traits", + "pxfm", +] + +[[package]] name = "muda" version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2118,7 +2242,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", @@ -2180,6 +2304,15 @@ 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 = "num-conv" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2549,6 +2682,7 @@ dependencies = [ "serde_json", "tauri", "tauri-build", + "tauri-plugin-clipboard-manager", "tauri-plugin-dialog", "tauri-plugin-opener", "tauri-plugin-os", @@ -2683,6 +2817,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] +name = "petgraph" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" +dependencies = [ + "fixedbitset", + "hashbrown 0.15.5", + "indexmap 2.12.1", +] + +[[package]] name = "phf" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2872,6 +3017,19 @@ dependencies = [ ] [[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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2984,6 +3142,21 @@ dependencies = [ ] [[package]] +name = "pxfm" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7186d3822593aa4393561d186d1393b3923e9d6163d3fbfd6e825e3e6cf3e6a8" +dependencies = [ + "num-traits", +] + +[[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.37.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4103,7 +4276,7 @@ dependencies = [ "ico", "json-patch", "plist", - "png", + "png 0.17.16", "proc-macro2", "quote", "semver", @@ -4151,6 +4324,21 @@ dependencies = [ ] [[package]] +name = "tauri-plugin-clipboard-manager" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "206dc20af4ed210748ba945c2774e60fd0acd52b9a73a028402caf809e9b6ecf" +dependencies = [ + "arboard", + "log", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "thiserror 2.0.17", +] + +[[package]] name = "tauri-plugin-dialog" version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4490,6 +4678,20 @@ dependencies = [ ] [[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", +] + +[[package]] name = "time" version = "0.3.44" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4784,13 +4986,24 @@ 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", ] [[package]] +name = "tree_magic_mini" +version = "3.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8765b90061cba6c22b5831f675da109ae5561588290f9fa2317adab2714d5a6" +dependencies = [ + "memchr", + "nom", + "petgraph", +] + +[[package]] name = "try-lock" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5108,6 +5321,19 @@ dependencies = [ ] [[package]] +name = "wayland-protocols-wlr" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efd94963ed43cf9938a090ca4f7da58eb55325ec8200c3848963e98dc25b78ec" +dependencies = [ + "bitflags 2.10.0", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] name = "wayland-scanner" version = "0.31.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5239,6 +5465,12 @@ dependencies = [ ] [[package]] +name = "weezl" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" + +[[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5707,6 +5939,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" [[package]] +name = "wl-clipboard-rs" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9651471a32e87d96ef3a127715382b2d11cc7c8bb9822ded8a7cc94072eb0a3" +dependencies = [ + "libc", + "log", + "os_pipe", + "rustix", + "thiserror 2.0.17", + "tree_magic_mini", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-protocols-wlr", +] + +[[package]] name = "writeable" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5779,6 +6029,23 @@ dependencies = [ ] [[package]] +name = "x11rb" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414" +dependencies = [ + "gethostname", + "rustix", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" + +[[package]] name = "xattr" version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5966,6 +6233,21 @@ dependencies = [ ] [[package]] +name = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" + +[[package]] +name = "zune-jpeg" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713" +dependencies = [ + "zune-core", +] + +[[package]] name = "zvariant" version = "5.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/packages/tauri/src-tauri/Cargo.toml b/packages/tauri/src-tauri/Cargo.toml index a39c22011..4cb82d94d 100644 --- a/packages/tauri/src-tauri/Cargo.toml +++ b/packages/tauri/src-tauri/Cargo.toml @@ -26,6 +26,7 @@ tauri-plugin-updater = "2" tauri-plugin-process = "2" tauri-plugin-store = "2" tauri-plugin-window-state = "2" +tauri-plugin-clipboard-manager = "2" serde = { version = "1", features = ["derive"] } serde_json = "1" diff --git a/packages/tauri/src-tauri/src/lib.rs b/packages/tauri/src-tauri/src/lib.rs index 08bec528d..f39096031 100644 --- a/packages/tauri/src-tauri/src/lib.rs +++ b/packages/tauri/src-tauri/src/lib.rs @@ -1,4 +1,5 @@ use std::{ + collections::VecDeque, net::{SocketAddr, TcpListener}, sync::{Arc, Mutex}, time::{Duration, Instant}, @@ -6,6 +7,8 @@ use std::{ #[cfg(target_os = "macos")] use tauri::TitleBarStyle; use tauri::{AppHandle, LogicalSize, Manager, RunEvent, WebviewUrl, WebviewWindow}; +use tauri_plugin_clipboard_manager::ClipboardExt; +use tauri_plugin_dialog::{DialogExt, MessageDialogButtons, MessageDialogResult}; use tauri_plugin_shell::process::{CommandChild, CommandEvent}; use tauri_plugin_shell::ShellExt; use tokio::net::TcpSocket; @@ -13,6 +16,11 @@ use tokio::net::TcpSocket; #[derive(Clone)] struct ServerState(Arc<Mutex<Option<CommandChild>>>); +#[derive(Clone)] +struct LogState(Arc<Mutex<VecDeque<String>>>); + +const MAX_LOG_ENTRIES: usize = 200; + #[tauri::command] fn kill_sidecar(app: AppHandle) { let Some(server_state) = app.try_state::<ServerState>() else { @@ -35,7 +43,37 @@ fn kill_sidecar(app: AppHandle) { println!("Killed server"); } -fn get_sidecar_port() -> u16 { +#[tauri::command] +async fn copy_logs_to_clipboard(app: AppHandle) -> Result<(), String> { + let log_state = app.try_state::<LogState>().ok_or("Log state not found")?; + + let logs = log_state + .0 + .lock() + .map_err(|_| "Failed to acquire log lock")?; + + let log_text = logs.iter().cloned().collect::<Vec<_>>().join(""); + + app.clipboard() + .write_text(log_text) + .map_err(|e| format!("Failed to copy to clipboard: {}", e))?; + + Ok(()) +} + +#[tauri::command] +async fn get_logs(app: AppHandle) -> Result<String, String> { + let log_state = app.try_state::<LogState>().ok_or("Log state not found")?; + + let logs = log_state + .0 + .lock() + .map_err(|_| "Failed to acquire log lock")?; + + Ok(logs.iter().cloned().collect::<Vec<_>>().join("")) +} + +fn get_sidecar_port() -> u32 { option_env!("OPENCODE_PORT") .map(|s| s.to_string()) .or_else(|| std::env::var("OPENCODE_PORT").ok()) @@ -46,14 +84,17 @@ fn get_sidecar_port() -> u16 { .local_addr() .expect("Failed to get local address") .port() - }) + }) as u32 } fn get_user_shell() -> String { std::env::var("SHELL").unwrap_or_else(|_| "/bin/sh".to_string()) } -fn spawn_sidecar(app: &AppHandle, port: u16) -> CommandChild { +fn spawn_sidecar(app: &AppHandle, port: u32) -> CommandChild { + let log_state = app.state::<LogState>(); + let log_state_clone = log_state.inner().clone(); + #[cfg(target_os = "windows")] let (mut rx, child) = app .shell() @@ -92,10 +133,28 @@ fn spawn_sidecar(app: &AppHandle, port: u16) -> CommandChild { CommandEvent::Stdout(line_bytes) => { let line = String::from_utf8_lossy(&line_bytes); print!("{line}"); + + // Store log in shared state + if let Ok(mut logs) = log_state_clone.0.lock() { + logs.push_back(format!("[STDOUT] {}", line)); + // Keep only the last MAX_LOG_ENTRIES + while logs.len() > MAX_LOG_ENTRIES { + logs.pop_front(); + } + } } CommandEvent::Stderr(line_bytes) => { let line = String::from_utf8_lossy(&line_bytes); eprint!("{line}"); + + // Store log in shared state + if let Ok(mut logs) = log_state_clone.0.lock() { + logs.push_back(format!("[STDERR] {}", line)); + // Keep only the last MAX_LOG_ENTRIES + while logs.len() > MAX_LOG_ENTRIES { + logs.pop_front(); + } + } } _ => {} } @@ -105,12 +164,12 @@ fn spawn_sidecar(app: &AppHandle, port: u16) -> CommandChild { child } -async fn is_server_running(port: u16) -> bool { +async fn is_server_running(port: u32) -> bool { TcpSocket::new_v4() .unwrap() .connect(SocketAddr::new( "127.0.0.1".parse().expect("Failed to parse IP"), - port, + port as u16, )) .await .is_ok() @@ -128,10 +187,18 @@ pub fn run() { .plugin(tauri_plugin_shell::init()) .plugin(tauri_plugin_process::init()) .plugin(tauri_plugin_opener::init()) - .invoke_handler(tauri::generate_handler![kill_sidecar]) + .plugin(tauri_plugin_clipboard_manager::init()) + .invoke_handler(tauri::generate_handler![ + kill_sidecar, + copy_logs_to_clipboard, + get_logs + ]) .setup(move |app| { let app = app.handle().clone(); + // Initialize log state + app.manage(LogState(Arc::new(Mutex::new(VecDeque::new())))); + tauri::async_runtime::spawn(async move { let port = get_sidecar_port(); @@ -143,7 +210,22 @@ pub fn run() { let timestamp = Instant::now(); loop { if timestamp.elapsed() > Duration::from_secs(7) { - todo!("Handle server spawn timeout"); + let res = app.dialog() + .message("Failed to spawn OpenCode CLI. Copy logs using the button below and send them to the team for assistance.") + .title("Startup Failed") + .buttons(MessageDialogButtons::OkCancelCustom("Copy Logs And Exit".to_string(), "Exit".to_string())) + .blocking_show_with_result(); + + if matches!(&res, MessageDialogResult::Custom(name) if name == "Copy Logs And Exit") { + match copy_logs_to_clipboard(app.clone()).await { + Ok(()) => println!("Logs copied to clipboard successfully"), + Err(e) => println!("Failed to copy logs to clipboard: {}", e), + } + } + + app.exit(1); + + return; } tokio::time::sleep(Duration::from_millis(10)).await; |
