diff --git a/main.js b/main.js index 9582d45..f06d2b3 100644 --- a/main.js +++ b/main.js @@ -396,6 +396,12 @@ async function start() { res.set('Cache-Control', 'no-store, no-cache, must-revalidate, private'); } })); + expressApp.use(express.static(path.join(frontendDir, 'src'), { + etag: false, + setHeaders: (res) => { + res.set('Cache-Control', 'no-store, no-cache, must-revalidate, private'); + } + })); expressApp.get('*', (req, res) => { res.set('Cache-Control', 'no-store, no-cache, must-revalidate, private'); res.sendFile(path.join(frontendDir, 'index.html')); diff --git a/src-tauri/build.rs b/src-tauri/build.rs index a4cbc29..7cbf82c 100644 --- a/src-tauri/build.rs +++ b/src-tauri/build.rs @@ -15,6 +15,18 @@ fn main() { } } } + + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + let node_path = std::path::Path::new("../bin/node"); + if node_path.exists() { + let _ = std::fs::set_permissions( + node_path, + std::fs::Permissions::from_mode(0o755), + ); + } + } tauri_build::build(); } \ No newline at end of file diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 98ee82d..c372515 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -7,11 +7,19 @@ use tauri::{Emitter, Manager, State, WebviewUrl, WebviewWindowBuilder}; fn debug_log(msg: &str) { let log_dir = if let Ok(app_data) = std::env::var("APPDATA") { - std::path::PathBuf::from(app_data).join("vyla-home").join("vyla-home-files").join("logs") + std::path::PathBuf::from(app_data) + .join("vyla-home") + .join("vyla-home-files") + .join("logs") } else if let Ok(home) = std::env::var("HOME") { - std::path::PathBuf::from(home).join("Library").join("Logs").join("VylaHome") + std::path::PathBuf::from(home) + .join("Library") + .join("Logs") + .join("VylaHome") } else if let Ok(user_profile) = std::env::var("USERPROFILE") { - std::path::PathBuf::from(user_profile).join(".vyla-home").join("logs") + std::path::PathBuf::from(user_profile) + .join(".vyla-home") + .join("logs") } else { std::env::temp_dir().join("vyla-home-logs") }; @@ -19,7 +27,11 @@ fn debug_log(msg: &str) { let _ = std::fs::create_dir_all(&log_dir); let log_path = log_dir.join("rust-debug.log"); - if let Ok(mut file) = std::fs::OpenOptions::new().create(true).append(true).open(&log_path) { + if let Ok(mut file) = std::fs::OpenOptions::new() + .create(true) + .append(true) + .open(&log_path) + { use std::io::Write; let _ = writeln!(file, "[RUST] {}", msg); } @@ -109,6 +121,7 @@ fn find_node_binary(app: &tauri::App) -> Option { if node_exe.exists() { return Some(clean_path(&node_exe)); } + let node = p.join("node"); if node.exists() { return Some(clean_path(&node)); @@ -166,7 +179,6 @@ fn resolve_script_and_root(app: &tauri::App) -> (String, String) { resource_dir.join("_up_").join("main.js"), resource_dir.join("up").join("main.js"), ]; - for candidate in candidates { if candidate.exists() { let root_dir = candidate.parent().unwrap(); @@ -256,12 +268,12 @@ const LINK_HANDLER_SCRIPT: &str = r##" pointer-events: auto !important; -webkit-app-region: no-drag !important; } - + #vyla-floating-nav:hover { width: 82px !important; padding-right: 6px !important; } - + .vyla-nav-logo-wrap { width: 42px !important; min-width: 42px !important; @@ -276,7 +288,7 @@ const LINK_HANDLER_SCRIPT: &str = r##" pointer-events: auto !important; padding: 0 !important; } - + .vyla-nav-logo-wrap img { width: 26px !important; height: 26px !important; @@ -286,7 +298,7 @@ const LINK_HANDLER_SCRIPT: &str = r##" pointer-events: none !important; display: block !important; } - + .vyla-nav-links { display: flex !important; align-items: center !important; @@ -299,13 +311,13 @@ const LINK_HANDLER_SCRIPT: &str = r##" z-index: 2 !important; margin-left: 2px !important; } - + #vyla-floating-nav:hover .vyla-nav-links { opacity: 1 !important; transform: translateX(0) !important; pointer-events: auto !important; } - + .vyla-nav-btn { width: 32px !important; min-width: 32px !important; @@ -324,11 +336,8 @@ const LINK_HANDLER_SCRIPT: &str = r##" opacity: 0.85 !important; padding: 0 !important; } - - .vyla-nav-btn:hover { - opacity: 1 !important; - } - + + .vyla-nav-btn:hover { opacity: 1 !important; } .vyla-nav-btn svg { width: 20px !important; height: 20px !important; @@ -336,7 +345,7 @@ const LINK_HANDLER_SCRIPT: &str = r##" pointer-events: none !important; display: block !important; } - + #tauri-custom-titlebar { position: fixed !important; top: 0 !important; @@ -354,7 +363,7 @@ const LINK_HANDLER_SCRIPT: &str = r##" margin: 0 !important; padding: 0 !important; } - + .tauri-drag-region { position: absolute !important; inset: 0 !important; @@ -362,7 +371,7 @@ const LINK_HANDLER_SCRIPT: &str = r##" cursor: default !important; pointer-events: auto !important; } - + .tauri-window-controls { display: flex !important; height: 32px !important; @@ -371,7 +380,7 @@ const LINK_HANDLER_SCRIPT: &str = r##" pointer-events: auto !important; z-index: 2147483648 !important; } - + button.tauri-btn { width: 46px !important; min-width: 46px !important; @@ -386,7 +395,7 @@ const LINK_HANDLER_SCRIPT: &str = r##" cursor: pointer !important; outline: none !important; } - + button.tauri-btn:hover { background: rgba(255, 255, 255, 0.2) !important; } button#tauri-btn-close:hover { background: #e81123 !important; color: #ffffff !important; } button.tauri-btn svg { width: 10px !important; height: 10px !important; fill: currentColor !important; pointer-events: none !important; } @@ -455,11 +464,12 @@ const LINK_HANDLER_SCRIPT: &str = r##" } } - if (document.readyState === 'loading') { + if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', injectLiquidNavBar); } else { injectLiquidNavBar(); } + const navObserver = new MutationObserver(() => { if (!document.getElementById('vyla-floating-nav')) { injectLiquidNavBar(); @@ -541,7 +551,7 @@ async fn kill_orphans() -> Result<(), String> { .args(&["-9", "-f", "node"]) .output(); } - + Ok(()) } @@ -578,6 +588,123 @@ fn get_logo_base64(app: tauri::AppHandle) -> Result { Err("icon_white.png not found".to_string()) } +fn handle_tauri_cmd(w: &tauri::WebviewWindow, msg: &BackendMessage) { + let window_label = msg + .data + .get("window") + .and_then(|v| v.as_str()) + .unwrap_or("main"); + + debug_log(&format!("[MAIN-DEBUG] Finding window by label: {}", window_label)); + + let cmd_window = match w.app_handle().get_webview_window(window_label) { + Some(win) => win, + None => { + debug_log(&format!("[TAURI-CMD] Window '{}' NOT FOUND!", window_label)); + return; + } + }; + + let cmd = match msg.data.get("cmd").and_then(|c| c.as_str()) { + Some(c) => c, + None => return, + }; + + debug_log(&format!("[TAURI-CMD] Executing action: {}", cmd)); + + match cmd { + "win_minimize" => { + let res = cmd_window.minimize(); + debug_log(&format!("[TAURI-CMD] Minimize result: {:?}", res)); + } + "win_maximize" => { + let is_max = cmd_window.is_maximized().unwrap_or(false); + debug_log(&format!("[TAURI-CMD] Currently maximized: {}", is_max)); + let res = if is_max { + cmd_window.unmaximize() + } else { + cmd_window.maximize() + }; + debug_log(&format!("[TAURI-CMD] Maximize/Unmaximize result: {:?}", res)); + } + "win_close" => { + let res = cmd_window.close(); + debug_log(&format!("[TAURI-CMD] Close result: {:?}", res)); + } + "win_drag" => { + let res = cmd_window.start_dragging(); + debug_log(&format!("[TAURI-CMD] Drag result: {:?}", res)); + } + "open_new_window" => { + debug_log("[TAURI-CMD-DEBUG] open_new_window command received"); + debug_log(&format!( + "[TAURI-CMD-DEBUG] Full message data: {}", + serde_json::to_string(&msg.data).unwrap_or_default() + )); + + let url = match msg.data.get("url").and_then(|u| u.as_str()) { + Some(u) => u.to_string(), + None => { + debug_log("[TAURI-CMD-DEBUG] ERROR: No URL found in message data"); + debug_log(&format!( + "[TAURI-CMD-DEBUG] Available keys: {:?}", + msg.data.as_object().map(|o| o.keys().collect::>()) + )); + return; + } + }; + + debug_log(&format!("[TAURI-CMD-DEBUG] URL extracted: {}", url)); + debug_log(&format!("[TAURI-CMD-DEBUG] URL length: {}", url.len())); + debug_log("[TAURI-CMD-DEBUG] Attempting to dispatch to main thread"); + + let app_handle = w.app_handle().clone(); + let closure_handle = app_handle.clone(); + + match app_handle.run_on_main_thread(move || { + debug_log("[TAURI-CMD-DEBUG] Now on main thread, calling open_new_window"); + match open_new_window(closure_handle, url) { + Ok(_) => debug_log("[TAURI-CMD-DEBUG] open_new_window succeeded"), + Err(e) => debug_log(&format!("[TAURI-CMD-DEBUG] open_new_window error: {}", e)), + } + }) { + Ok(_) => debug_log("[TAURI-CMD-DEBUG] Main thread dispatch succeeded"), + Err(e) => debug_log(&format!("[TAURI-CMD-DEBUG] Main thread dispatch failed: {}", e)), + } + } + _ => {} + } +} + +fn handle_ready(w: &tauri::WebviewWindow, msg: &BackendMessage) { + let url_str = match msg.data.get("url").and_then(|u| u.as_str()) { + Some(u) => u, + None => return, + }; + + let url_parsed = match url_str.parse::() { + Ok(u) => u, + Err(_) => return, + }; + + #[cfg(target_os = "macos")] + { + use objc2::msg_send; + use objc2::runtime::AnyObject; + debug_log("Attempting with_webview on ready event"); + let result = w.with_webview(|webview| unsafe { + debug_log("Inside with_webview closure"); + let wk_webview = webview.inner() as *mut AnyObject; + let config: *mut AnyObject = msg_send![wk_webview, configuration]; + let _: () = msg_send![config, setUpgradeKnownHostsToHTTPS: false]; + debug_log("setUpgradeKnownHostsToHTTPS set to false"); + }); + debug_log(&format!("with_webview result: {:?}", result)); + } + + let _ = w.navigate(url_parsed); +} + fn main() { debug_log("=== Vyla Home Executable Started ==="); @@ -603,7 +730,7 @@ fn main() { std::path::PathBuf::from(appdata_env) .join("vyla-home") .join("vyla-home-files") - } else if let Ok(home) = std::env::var("HOME") { + } else if let Ok(home) = std::env::var("HOME") { #[cfg(target_os = "macos")] let base = std::path::PathBuf::from(&home) .join("Library") @@ -640,7 +767,10 @@ fn main() { } if version_mismatch { - debug_log(&format!("App version updated to {}. Cleaning old files.", current_app_version)); + debug_log(&format!( + "App version updated to {}. Cleaning old files.", + current_app_version + )); let dirs_to_remove = ["frontend", "player", "stream-api", "auth-proxy", "config"]; for d in dirs_to_remove { let _ = std::fs::remove_dir_all(vyla_files.join(d)); @@ -658,19 +788,9 @@ fn main() { .initialization_script(LINK_HANDLER_SCRIPT) .build()?; - #[cfg(target_os = "macos")] - { - use objc2::msg_send; - use objc2::runtime::AnyObject; - let _ = window.with_webview(|webview| unsafe { - let wk_webview = webview.inner() as *mut AnyObject; - let config: *mut AnyObject = msg_send![wk_webview, configuration]; - let _: () = msg_send![config, setUpgradeKnownHostsToHTTPS: false]; - }); - } - let (main_js, root_dir) = resolve_script_and_root(app); debug_log("Searching for node binary..."); + let node_bin = match find_node_binary(app) { Some(bin) => { debug_log(&format!("Found node binary: {}", bin)); @@ -686,7 +806,6 @@ fn main() { }; let vyla_files_str = clean_path(&vyla_files); - std::thread::sleep(std::time::Duration::from_millis(400)); let mut cmd = Command::new(&node_bin); @@ -711,6 +830,7 @@ fn main() { "Node child process spawned successfully, PID: {}", child.id() )); + if let Some(stderr) = child.stderr.take() { let w = window.clone(); std::thread::spawn(move || { @@ -718,8 +838,14 @@ fn main() { let reader = BufReader::new(stderr); for line in reader.lines().flatten() { debug_log(&format!("[NODE STDERR] {}", line)); - if line.contains("Error") || line.contains("Exception") || line.contains("MODULE_NOT_FOUND") { - let _ = w.emit("error", serde_json::json!({ "message": format!("Backend failed: {}", line) })); + if line.contains("Error") + || line.contains("Exception") + || line.contains("MODULE_NOT_FOUND") + { + let _ = w.emit( + "error", + serde_json::json!({ "message": format!("Backend failed: {}", line) }), + ); } } }); @@ -732,152 +858,24 @@ fn main() { let reader = BufReader::new(stdout); for line in reader.lines().flatten() { debug_log(&format!("[NODE STDOUT] {}", line)); - if let Ok(msg) = serde_json::from_str::(&line) { - debug_log(&format!("[MAIN-DEBUG] Received event: {}", msg.event)); - debug_log(&format!( - "[MAIN-DEBUG] Event data: {}", - serde_json::to_string(&msg.data).unwrap_or_default() - )); - if msg.event == "tauri-cmd" { - let window_label = msg - .data - .get("window") - .and_then(|val| val.as_str()) - .unwrap_or("main"); - debug_log(&format!( - "[MAIN-DEBUG] Finding window by label: {}", - window_label - )); - if let Some(cmd_window) = - w.app_handle().get_webview_window(window_label) - { - if let Some(cmd) = - msg.data.get("cmd").and_then(|c| c.as_str()) - { - debug_log(&format!( - "[TAURI-CMD] Executing action: {}", - cmd - )); - match cmd { - "win_minimize" => { - let res = cmd_window.minimize(); - debug_log(&format!( - "[TAURI-CMD] Minimize result: {:?}", - res - )); - } - "win_maximize" => { - let is_max = cmd_window - .is_maximized() - .unwrap_or(false); - debug_log(&format!( - "[TAURI-CMD] Currently maximized: {}", - is_max - )); - let res = if is_max { - cmd_window.unmaximize() - } else { - cmd_window.maximize() - }; - debug_log(&format!( - "[TAURI-CMD] Maximize/Unmaximize result: {:?}", - res - )); - } - "win_close" => { - let res = cmd_window.close(); - debug_log(&format!( - "[TAURI-CMD] Close result: {:?}", - res - )); - } - "win_drag" => { - let res = cmd_window.start_dragging(); - debug_log(&format!( - "[TAURI-CMD] Drag result: {:?}", - res - )); - } - "open_new_window" => { - debug_log("[TAURI-CMD-DEBUG] open_new_window command received"); - debug_log(&format!( - "[TAURI-CMD-DEBUG] Full message data: {}", - serde_json::to_string(&msg.data) - .unwrap_or_default() - )); - if let Some(url) = msg - .data - .get("url") - .and_then(|u| u.as_str()) - { - debug_log(&format!( - "[TAURI-CMD-DEBUG] URL extracted: {}", - url - )); - debug_log(&format!( - "[TAURI-CMD-DEBUG] URL length: {}", - url.len() - )); - debug_log("[TAURI-CMD-DEBUG] Attempting to dispatch to main thread"); - let app_handle = w.app_handle().clone(); - let closure_handle = app_handle.clone(); - let url_owned = url.to_string(); - let dispatch_result = app_handle - .run_on_main_thread(move || { - debug_log("[TAURI-CMD-DEBUG] Now on main thread, calling open_new_window"); - match open_new_window( - closure_handle, - url_owned, - ) { - Ok(_) => { - debug_log("[TAURI-CMD-DEBUG] open_new_window succeeded"); - } - Err(e) => { - debug_log(&format!( - "[TAURI-CMD-DEBUG] open_new_window error: {}", - e - )); - } - } - }); - match dispatch_result { - Ok(_) => debug_log( - "[TAURI-CMD-DEBUG] Main thread dispatch succeeded", - ), - Err(e) => debug_log(&format!( - "[TAURI-CMD-DEBUG] Main thread dispatch failed: {}", - e - )), - } - } else { - debug_log("[TAURI-CMD-DEBUG] ERROR: No URL found in message data"); - debug_log(&format!( - "[TAURI-CMD-DEBUG] Available keys: {:?}", - msg.data - .as_object() - .map(|o| o.keys().collect::>()) - )); - } - } - _ => {} - } - } - } else { - debug_log(&format!( - "[TAURI-CMD] Window '{}' NOT FOUND!", - window_label - )); - } - } else if msg.event == "ready" { - if let Some(url_str) = msg.data.get("url").and_then(|u| u.as_str()) { - if let Ok(url_parsed) = url_str.parse::() { - let _ = w.navigate(url_parsed); - } - } -} else { - let _ = w.emit(&msg.event, msg.data.clone()); -} + let msg = match serde_json::from_str::(&line) { + Ok(m) => m, + Err(_) => continue, + }; + + debug_log(&format!("[MAIN-DEBUG] Received event: {}", msg.event)); + debug_log(&format!( + "[MAIN-DEBUG] Event data: {}", + serde_json::to_string(&msg.data).unwrap_or_default() + )); + + match msg.event.as_str() { + "tauri-cmd" => handle_tauri_cmd(&w, &msg), + "ready" => handle_ready(&w, &msg), + _ => { + let _ = w.emit(&msg.event, msg.data.clone()); + } } } }); @@ -901,7 +899,7 @@ fn main() { Ok(()) }) - .on_window_event(|window, event| { + .on_window_event(|window, event| { if let tauri::WindowEvent::Destroyed = event { if window.label() == "main" { let state: State = window.state();