diff --git a/frontend/src-tauri/Cargo.lock b/frontend/src-tauri/Cargo.lock index 6294202..c0bbf9b 100644 --- a/frontend/src-tauri/Cargo.lock +++ b/frontend/src-tauri/Cargo.lock @@ -87,6 +87,8 @@ dependencies = [ "tauri-plugin-dialog", "tauri-plugin-log", "tauri-plugin-shell", + "tauri-plugin-single-instance", + "tempfile", ] [[package]] @@ -95,6 +97,137 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +[[package]] +name = "async-broadcast" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" +dependencies = [ + "event-listener", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-channel" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "pin-project-lite", + "slab", +] + +[[package]] +name = "async-io" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" +dependencies = [ + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "windows-sys 0.61.2", +] + +[[package]] +name = "async-lock" +version = "3.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-process" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" +dependencies = [ + "async-channel", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix", +] + +[[package]] +name = "async-recursion" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "async-signal" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52b5aaafa020cf5053a01f2a60e8ff5dccf550f0f77ec54a4e47285ac2bab485" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix", + "signal-hook-registry", + "slab", + "windows-sys 0.61.2", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "async-trait" +version = "0.1.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.5", +] + [[package]] name = "atk" version = "0.18.2" @@ -202,6 +335,19 @@ dependencies = [ "objc2", ] +[[package]] +name = "blocking" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a70e4329df6cb94385eed412ec92375c3cdd8a6e502493d1229b6414e4036dfa" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + [[package]] name = "borsh" version = "1.6.1" @@ -446,6 +592,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "convert_case" version = "0.4.0" @@ -830,6 +985,33 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "endi" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" + +[[package]] +name = "enumflags2" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "env_filter" version = "0.1.4" @@ -867,6 +1049,26 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "event-listener" +version = "5.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a23add41df1562121a9393cb065eab5146a1242410f23a644851e90cfd669d2" +dependencies = [ + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", +] + [[package]] name = "fastrand" version = "2.4.1" @@ -1019,6 +1221,19 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" +[[package]] +name = "futures-lite" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + [[package]] name = "futures-macro" version = "0.3.32" @@ -1407,6 +1622,12 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hermit-abi" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17592d60ebacc7d5e169f4663c5f84f9161cc90328abcfe8456f41e4dfcb284" + [[package]] name = "hex" version = "0.4.3" @@ -1918,6 +2139,12 @@ dependencies = [ "libc", ] +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + [[package]] name = "litemap" version = "0.8.2" @@ -2289,6 +2516,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + [[package]] name = "os_pipe" version = "1.2.3" @@ -2324,6 +2561,12 @@ dependencies = [ "system-deps", ] +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + [[package]] name = "parking_lot" version = "0.12.5" @@ -2552,6 +2795,17 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" +[[package]] +name = "piper" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + [[package]] name = "pkg-config" version = "0.3.33" @@ -2584,6 +2838,20 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "polling" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix", + "windows-sys 0.61.2", +] + [[package]] name = "potential_utf" version = "0.1.5" @@ -3032,6 +3300,19 @@ dependencies = [ "semver", ] +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags 2.11.1", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + [[package]] name = "rustversion" version = "1.0.22" @@ -3571,6 +3852,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "3.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12df2e0110f65b775f769bb17ef989067a1d931b2eb822bd4346631eeada89f9" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "sync_wrapper" version = "1.0.2" @@ -3881,6 +4173,22 @@ dependencies = [ "tokio", ] +[[package]] +name = "tauri-plugin-single-instance" +version = "2.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cd0cb5c412a5071b69bab6a6df1583cbb89460d4a83b6a24769b08d15b6b1e1" +dependencies = [ + "serde", + "serde_json", + "tauri", + "thiserror 2.0.18", + "tokio", + "tracing", + "windows-sys 0.60.2", + "zbus", +] + [[package]] name = "tauri-runtime" version = "2.10.1" @@ -3981,6 +4289,19 @@ dependencies = [ "toml 0.9.12+spec-1.1.0", ] +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.2", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + [[package]] name = "tendril" version = "0.4.3" @@ -4284,9 +4605,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ "pin-project-lite", + "tracing-attributes", "tracing-core", ] +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "tracing-core" version = "0.1.36" @@ -4336,6 +4669,17 @@ version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" +[[package]] +name = "uds_windows" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" +dependencies = [ + "memoffset", + "tempfile", + "windows-sys 0.61.2", +] + [[package]] name = "unic-char-property" version = "0.9.0" @@ -5397,6 +5741,76 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zbus" +version = "5.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5db4be7c075cb421e4b7ee645541604239bd243ba7c357511f4ff3a74b555907" +dependencies = [ + "async-broadcast", + "async-executor", + "async-io", + "async-lock", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "enumflags2", + "event-listener", + "futures-core", + "futures-lite", + "hex", + "libc", + "ordered-stream", + "rustix", + "serde", + "serde_repr", + "tracing", + "uds_windows", + "uuid", + "windows-sys 0.61.2", + "winnow 1.0.2", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "5.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2990635d09ade6df1868f72f8cac69a876a90981e8bd3c40b1be413f8dc88f40" +dependencies = [ + "proc-macro-crate 3.5.0", + "proc-macro2", + "quote", + "syn 3.0.5", + "zbus_names", + "zvariant", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "4.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8bf88b4a3ff53e883001e0e0115b297a9d53c31b9c1edd2bfdd853e3428624e" +dependencies = [ + "serde", + "winnow 1.0.2", + "zvariant", +] + +[[package]] +name = "zcheapstr" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1afec51604565183aeb5c54c20aeab286120d4e4460f7f76e3e8bb8c0d99473" +dependencies = [ + "serde", +] + [[package]] name = "zerocopy" version = "0.8.48" @@ -5476,3 +5890,44 @@ name = "zmij" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + +[[package]] +name = "zvariant" +version = "5.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1d34c27cc6cdd1f458427519dd6b8612f7b7e3f7b9a0b2355d041dda9869147" +dependencies = [ + "endi", + "enumflags2", + "serde", + "winnow 1.0.2", + "zcheapstr", + "zvariant_derive", + "zvariant_utils", +] + +[[package]] +name = "zvariant_derive" +version = "5.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "864155e69b4352db0c7f374917bf45d1e0c8d17659c8b3dbf9795f3673f8c497" +dependencies = [ + "proc-macro-crate 3.5.0", + "proc-macro2", + "quote", + "syn 3.0.5", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bad0294361a320b694a328460dc73add56c306150f5cb6bfafc44446120008a3" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "syn 3.0.5", + "winnow 1.0.2", +] diff --git a/frontend/src-tauri/Cargo.toml b/frontend/src-tauri/Cargo.toml index ae11f0d..5669f60 100644 --- a/frontend/src-tauri/Cargo.toml +++ b/frontend/src-tauri/Cargo.toml @@ -17,6 +17,9 @@ crate-type = ["staticlib", "cdylib", "rlib"] [build-dependencies] tauri-build = { version = "2.5.6", features = [] } +[dev-dependencies] +tempfile = "3.27.0" + [dependencies] serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } @@ -25,3 +28,4 @@ tauri = { version = "2.10.3", features = [] } tauri-plugin-log = "2" tauri-plugin-shell = "2" tauri-plugin-dialog = "2" +tauri-plugin-single-instance = "2.4.4" diff --git a/frontend/src-tauri/src/lib.rs b/frontend/src-tauri/src/lib.rs index 7743112..c6f2064 100644 --- a/frontend/src-tauri/src/lib.rs +++ b/frontend/src-tauri/src/lib.rs @@ -50,11 +50,59 @@ fn stop_backend(app: &tauri::AppHandle) { } } +fn normalize_windows_path(arg: &str) -> Option { + let trimmed = arg.trim_matches('"').trim(); + + if trimmed.starts_with('-') || trimmed.is_empty() { + return None; + } + + let path = std::path::Path::new(trimmed); + let lower = trimmed.to_lowercase(); + + if (lower.ends_with(".md") || lower.ends_with(".markdown")) && path.exists() { + if let Ok(canonical) = path.canonicalize() { + let mut path_str = canonical.to_string_lossy().to_string(); + if path_str.starts_with(r"\\?\") { + path_str = path_str[4..].to_string(); + } + Some(path_str.replace('\\', "/")) + } else { + Some(trimmed.replace('\\', "/")) + } + } else { + None + } +} + #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { let port_state = Arc::new(Mutex::new(None::)); let app = tauri::Builder::default() + .plugin(tauri_plugin_single_instance::init(|app, argv, _cwd| { + let mut paths = Vec::new(); + for arg in argv.into_iter().skip(1) { + if let Some(valid_path) = normalize_windows_path(&arg) { + paths.push(valid_path); + } + } + if !paths.is_empty() { + app.state::() + .0 + .lock() + .unwrap() + .extend(paths.clone()); + + let _ = app.emit("open-file-paths", paths); + + if let Some(window) = app.get_webview_window("main") { + let _ = window.show(); + let _ = window.unminimize(); + let _ = window.set_focus(); + } + } + })) .plugin(tauri_plugin_dialog::init()) .plugin(tauri_plugin_shell::init()) .manage(BackendPort(port_state)) @@ -71,6 +119,22 @@ pub fn run() { } }) .setup(|app| { + let args: Vec = std::env::args().collect(); + let mut initial_files = Vec::new(); + for arg in args.into_iter().skip(1) { + if let Some(valid_path) = normalize_windows_path(&arg) { + initial_files.push(valid_path); + } + } + + if !initial_files.is_empty() { + app.state::() + .0 + .lock() + .unwrap() + .extend(initial_files); + } + if cfg!(debug_assertions) { let backend_port = std::env::var("MARKDOWN_READER_BACKEND_PORT") .ok() @@ -120,9 +184,9 @@ pub fn run() { .build(tauri::generate_context!()) .expect("error while building tauri application"); - app.run(|app_handle, event| { + app.run(|app_handle, event| match event { #[cfg(target_os = "macos")] - if let tauri::RunEvent::Opened { urls } = event { + tauri::RunEvent::Opened { urls } => { let paths: Vec = urls .into_iter() .filter_map(|url| url.to_file_path().ok()) @@ -138,14 +202,63 @@ pub fn run() { .extend(paths.clone()); let _ = app_handle.emit("open-file-paths", paths); } - return; } - - if matches!( - event, - tauri::RunEvent::ExitRequested { .. } | tauri::RunEvent::Exit - ) { + tauri::RunEvent::ExitRequested { .. } | tauri::RunEvent::Exit => { stop_backend(app_handle); } + _ => {} }); } + +#[cfg(test)] +mod tests { + use super::*; + use std::fs::File; + use tempfile::tempdir; + + #[test] + fn test_normalize_windows_path_valid_md() { + let dir = tempdir().unwrap(); + let file_path = dir.path().join("test_note.md"); + File::create(&file_path).unwrap(); + + let path_str = file_path.to_string_lossy().to_string(); + + let quoted_path = format!("\"{}\"", path_str); + + let result = normalize_windows_path("ed_path); + assert!(result.is_some()); + assert!(result.unwrap().ends_with("test_note.md")); + } + + #[test] + fn test_normalize_windows_path_ignore_cli_flags() { + assert_eq!(normalize_windows_path("--config"), None); + assert_eq!(normalize_windows_path("-v"), None); + assert_eq!(normalize_windows_path(""), None); + } + + #[test] + fn test_normalize_windows_path_non_md_file() { + let dir = tempdir().unwrap(); + let file_path = dir.path().join("image.png"); + File::create(&file_path).unwrap(); + + let path_str = file_path.to_string_lossy().to_string(); + assert_eq!(normalize_windows_path(&path_str), None); + } + + #[test] + fn test_normalize_windows_path_slashes_conversion() { + let dir = tempdir().unwrap(); + let file_path = dir.path().join("nested_file.markdown"); + File::create(&file_path).unwrap(); + + let path_str = file_path.to_string_lossy().to_string(); // В Windows содержит '\' + let result = normalize_windows_path(&path_str).unwrap(); + + // Убеждаемся, что обратные слэши конвертированы в прямой '/' для JS + assert!(!result.contains('\\')); + assert!(result.contains('/')); + } +} diff --git a/frontend/src/hooks/useFileIO.ts b/frontend/src/hooks/useFileIO.ts index 39a8a1b..f3fe380 100644 --- a/frontend/src/hooks/useFileIO.ts +++ b/frontend/src/hooks/useFileIO.ts @@ -215,16 +215,41 @@ export function useFileIO({ editor, isDesktopRuntime, backendStatus, monacoRef } useEffect(() => { if (!isDesktopRuntime) return; - let cancelled = false; let unlisten: (() => void) | null = null; - const openPaths = (paths: string[]) => { if (paths.length) void openDroppedPaths(paths); }; - import("@tauri-apps/api/event") - .then(({ listen }) => listen("open-file-paths", (event) => openPaths(event.payload))) - .then((cleanup) => { if (cancelled) cleanup(); else unlisten = cleanup; return import("@tauri-apps/api/core"); }) - .then(({ invoke }) => invoke("take_pending_open_files")) - .then((paths) => { if (!cancelled) openPaths(paths); }) - .catch(console.error); - return () => { cancelled = true; unlisten?.(); }; - }, [isDesktopRuntime, openDroppedPaths]); + let cancelled = false; + let unlisten: (() => void) | null = null; + + const openPaths = (paths: string[]) => { + if (paths && paths.length > 0) void openDroppedPaths(paths); + }; + + void (async () => { + try { + const { listen } = await import("@tauri-apps/api/event"); + const cleanup = await listen("open-file-paths", (event) => openPaths(event.payload)); + + if (cancelled) { + cleanup(); + return; + } + unlisten = cleanup; + + if (backendStatus === "ready") { + console.log("[DEBUG JS] Backend is READY, requesting pending files..."); + const { invoke } = await import("@tauri-apps/api/core"); + const paths = await invoke("take_pending_open_files"); + console.log("[DEBUG JS] Received pending paths from Rust:", paths); + if (!cancelled && paths) openPaths(paths); + } + } catch (error) { + console.error("[DEBUG JS] Error setting up file-open handling:", error); + } + })(); + + return () => { + cancelled = true; + unlisten?.(); + }; + }, [isDesktopRuntime, backendStatus, openDroppedPaths]); const handleDragEnter = useCallback((event: DragEvent) => { event.preventDefault(); dragCounterRef.current += 1; }, []); const handleDragLeave = useCallback((event: DragEvent) => { event.preventDefault(); dragCounterRef.current -= 1; }, []); diff --git a/scripts/dev-tauri.bat b/scripts/dev-tauri.bat new file mode 100644 index 0000000..5fe1374 --- /dev/null +++ b/scripts/dev-tauri.bat @@ -0,0 +1,11 @@ +@echo off +setlocal +set "BASH_EXE=C:\Program Files\Git\bin\bash.exe" + +if not exist "%BASH_EXE%" ( + echo [ERROR] Git Bash not found at %BASH_EXE%. + echo Please install Git for Windows or run the script manually. + exit /b 1 +) + +"%BASH_EXE%" "%~dp0dev-tauri.sh" %* diff --git a/scripts/dev-tauri.ps1 b/scripts/dev-tauri.ps1 new file mode 100644 index 0000000..67d74c6 --- /dev/null +++ b/scripts/dev-tauri.ps1 @@ -0,0 +1,18 @@ +param( + [ValueFromRemainingArguments()]$RemainingArgs +) + +$bashPath = "C:\Program Files\Git\bin\bash.exe" +$scriptPath = Join-Path $PSScriptRoot "dev-tauri.sh" + +if (-not (Test-Path $bashPath)) { + Write-Error "Git Bash not found at $bashPath. Please install Git for Windows." + exit 1 +} + +# Если переданы аргументы (путь к файлу), пробрасываем их +if ($RemainingArgs.Count -gt 0) { + & $bashPath $scriptPath $RemainingArgs +} else { + & $bashPath $scriptPath +} diff --git a/scripts/dev-tauri.sh b/scripts/dev-tauri.sh index 53b752e..4e36174 100755 --- a/scripts/dev-tauri.sh +++ b/scripts/dev-tauri.sh @@ -18,6 +18,12 @@ if [ -d "/opt/homebrew/lib" ]; then fi resolve_python_runner() { + # Добавлена проверка для Windows (Scripts/python.exe) + if [ -x "${ROOT}/.venv/Scripts/python.exe" ] || [ -f "${ROOT}/.venv/Scripts/python.exe" ]; then + PYTHON_RUNNER=("${ROOT}/.venv/Scripts/python.exe") + return 0 + fi + if [ -x "${ROOT}/.venv/bin/python" ]; then PYTHON_RUNNER=("${ROOT}/.venv/bin/python") return 0 @@ -37,7 +43,7 @@ resolve_python_runner() { } backend_healthy() { - curl --connect-timeout 1 --max-time 2 -fsS "$1/api/health" >/dev/null 2>&1 + "${PYTHON_RUNNER[@]}" -c "import urllib.request; urllib.request.urlopen('$1/api/health', timeout=2)" >/dev/null 2>&1 } start_backend() { @@ -55,12 +61,12 @@ start_backend() { cd "$ROOT" "${PYTHON_RUNNER[@]}" -m uvicorn backend.main:app --host 127.0.0.1 --port "${port}" --reload & BACKEND_PID=$! - sleep 1 - if kill -0 "$BACKEND_PID" 2>/dev/null; then + sleep 2 + if backend_healthy "${url}"; then BACKEND_PORT="${port}" BACKEND_URL="${url}" - echo " Backend PID: $BACKEND_PID" + echo " Backend started successfully on ${url}" return 0 fi @@ -124,4 +130,4 @@ echo "" cd "$FRONTEND" MARKDOWN_READER_BACKEND_PORT="${BACKEND_PORT}" \ NEXT_PUBLIC_API_BASE_URL="${BACKEND_URL}" \ -npx tauri dev +npx tauri dev -- "$@"