diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index b60ed5a..7e16a91 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1,9 +1,23 @@ mod commands; use commands::{read_file, save_file, get_file_info, list_directory_files}; +use tauri::{Manager, Emitter}; +use std::sync::Mutex; #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { + // Store the CLI file path to emit after window is ready + let cli_file_path: Mutex> = Mutex::new(None); + + // Get CLI arguments early + let args: Vec = std::env::args().collect(); + if args.len() > 1 { + let file_path = &args[1]; + if file_path.ends_with(".md") || file_path.ends_with(".markdown") { + *cli_file_path.lock().unwrap() = Some(file_path.clone()); + } + } + tauri::Builder::default() .plugin(tauri_plugin_opener::init()) .plugin(tauri_plugin_fs::init()) @@ -14,6 +28,25 @@ pub fn run() { get_file_info, list_directory_files ]) + .setup(move |app| { + // Listen for window ready event + let file_path = cli_file_path.lock().unwrap().clone(); + + if let Some(path) = file_path { + let app_handle = app.handle().clone(); + + // Use a small delay to ensure window is fully ready + std::thread::spawn(move || { + std::thread::sleep(std::time::Duration::from_millis(500)); + + if let Some(window) = app_handle.get_webview_window("main") { + let _ = window.emit("file-open-from-cli", path); + } + }); + } + + Ok(()) + }) .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 783818c..fb425a4 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -35,7 +35,17 @@ "icons/128x128@2x.png", "icons/icon.icns", "icons/icon.ico" + ], + "fileAssociations": [ + { + "ext": [ + "md", + "markdown" + ], + "name": "Markdown Document", + "description": "Markdown file", + "role": "Editor" + } ] } -} - +} \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx index eebe3fe..8e5a2b3 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,7 +1,8 @@ import { useState, useEffect, useCallback } from "react"; import { invoke } from "@tauri-apps/api/core"; import { open, save } from "@tauri-apps/plugin-dialog"; -import { listen, TauriEvent } from "@tauri-apps/api/event"; +import { listen } from "@tauri-apps/api/event"; + import { ThemeProvider } from "./context/ThemeContext"; import { TitleBar } from "./components/TitleBar"; @@ -145,6 +146,31 @@ function AppContent() { } }, [filePath, content]); + // Listen for file open from CLI (when app is opened with a file by double-click) + useEffect(() => { + const setupCliFileOpen = async () => { + const unlisten = await listen("file-open-from-cli", async (event) => { + const filePath = event.payload; + if (filePath) { + await loadFile(filePath); + } + }); + + return unlisten; + }; + + let unlisten: (() => void) | undefined; + setupCliFileOpen().then((fn) => { + unlisten = fn; + }); + + return () => { + if (unlisten) { + unlisten(); + } + }; + }, [loadFile]); + // Toggle mode const handleToggleMode = useCallback(() => { setMode((prev) => (prev === "preview" ? "code" : "preview"));