feat: native menu and system tray integration

This commit is contained in:
2026-05-18 00:07:31 +05:30
parent 004af5575b
commit 046d31e9f0
3 changed files with 225 additions and 1 deletions
+12
View File
@@ -1,3 +1,6 @@
pub mod menu;
pub mod tray;
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_log::Builder::new().build())
@@ -41,6 +44,15 @@ pub fn run() {
])
.setup(|app| {
log::info!("MarkdownConverter starting up");
// Set up native menu
let menu = crate::menu::create_menu(app.handle())?;
app.set_menu(menu)?;
app.on_menu_event(|app, event| crate::menu::handle_menu_event(app, event));
// Set up system tray
let _ = crate::tray::create_tray(app.handle());
Ok(())
})
.on_window_event(|window, event| {
+168
View File
@@ -0,0 +1,168 @@
use tauri::{AppHandle, Emitter, menu::{Menu, MenuItem, Submenu, PredefinedMenuItem}};
pub fn create_menu(app: &AppHandle) -> tauri::Result<Menu<tauri::Wry>> {
let file_menu = Submenu::with_items(
app,
"File",
true,
&[
&MenuItem::with_id(app, "new", "New", true, Some("CmdOrCtrl+N"))?,
&MenuItem::with_id(app, "open", "Open...", true, Some("CmdOrCtrl+O"))?,
&MenuItem::with_id(app, "open_pdf", "Open PDF...", true, Some("CmdOrCtrl+Shift+O"))?,
&MenuItem::with_id(app, "save", "Save", true, Some("CmdOrCtrl+S"))?,
&MenuItem::with_id(app, "save_as", "Save As...", true, Some("CmdOrCtrl+Shift+S"))?,
&PredefinedMenuItem::separator(app)?,
&MenuItem::with_id(app, "print", "Print Preview", true, Some("CmdOrCtrl+P"))?,
&PredefinedMenuItem::separator(app)?,
&MenuItem::with_id(app, "import", "Import Document...", true, Some("CmdOrCtrl+I"))?,
&Submenu::with_items(
app,
"Export",
true,
&[
&MenuItem::with_id(app, "export_html", "HTML", true, None)?,
&MenuItem::with_id(app, "export_pdf", "PDF", true, None)?,
&MenuItem::with_id(app, "export_docx", "DOCX", true, None)?,
&MenuItem::with_id(app, "export_latex", "LaTeX", true, None)?,
&MenuItem::with_id(app, "export_rtf", "RTF", true, None)?,
&MenuItem::with_id(app, "export_odt", "ODT", true, None)?,
&MenuItem::with_id(app, "export_epub", "EPUB", true, None)?,
&PredefinedMenuItem::separator(app)?,
&MenuItem::with_id(app, "export_pptx", "PowerPoint (PPTX)", true, None)?,
&MenuItem::with_id(app, "export_csv", "CSV (Tables)", true, None)?,
],
)?,
&PredefinedMenuItem::separator(app)?,
&MenuItem::with_id(app, "quit", "Quit", true, Some("CmdOrCtrl+Q"))?,
],
)?;
let edit_menu = Submenu::with_items(
app,
"Edit",
true,
&[
&MenuItem::with_id(app, "undo", "Undo", true, Some("CmdOrCtrl+Z"))?,
&MenuItem::with_id(app, "redo", "Redo", true, Some("CmdOrCtrl+Shift+Z"))?,
&PredefinedMenuItem::separator(app)?,
&MenuItem::with_id(app, "cut", "Cut", true, Some("CmdOrCtrl+X"))?,
&MenuItem::with_id(app, "copy", "Copy", true, Some("CmdOrCtrl+C"))?,
&MenuItem::with_id(app, "paste", "Paste", true, Some("CmdOrCtrl+V"))?,
&MenuItem::with_id(app, "select_all", "Select All", true, Some("CmdOrCtrl+A"))?,
&PredefinedMenuItem::separator(app)?,
&MenuItem::with_id(app, "find", "Find & Replace", true, Some("CmdOrCtrl+F"))?,
],
)?;
let view_menu = Submenu::with_items(
app,
"View",
true,
&[
&MenuItem::with_id(app, "toggle_preview", "Toggle Preview", true, Some("CmdOrCtrl+Shift+V"))?,
&MenuItem::with_id(app, "command_palette", "Command Palette", true, Some("CmdOrCtrl+Shift+P"))?,
&PredefinedMenuItem::separator(app)?,
&Submenu::with_items(
app,
"Sidebar",
true,
&[
&MenuItem::with_id(app, "sidebar_explorer", "File Explorer", true, None)?,
&MenuItem::with_id(app, "sidebar_git", "Git", true, None)?,
&MenuItem::with_id(app, "sidebar_snippets", "Snippets", true, None)?,
&MenuItem::with_id(app, "sidebar_templates", "Templates", true, None)?,
],
)?,
&MenuItem::with_id(app, "toggle_bottom_panel", "Bottom Panel (REPL)", true, None)?,
&PredefinedMenuItem::separator(app)?,
&Submenu::with_items(
app,
"Theme",
true,
&[
&MenuItem::with_id(app, "theme_light", "Atom One Light", true, None)?,
&MenuItem::with_id(app, "theme_dark", "Dark", true, None)?,
&MenuItem::with_id(app, "theme_solarized", "Solarized Light", true, None)?,
&MenuItem::with_id(app, "theme_monokai", "Monokai", true, None)?,
&MenuItem::with_id(app, "theme_github", "GitHub Light", true, None)?,
],
)?,
],
)?;
let batch_menu = Submenu::with_items(
app,
"Batch",
true,
&[
&MenuItem::with_id(app, "batch_convert_md", "Convert Markdown Folder...", true, None)?,
&PredefinedMenuItem::separator(app)?,
&MenuItem::with_id(app, "batch_image", "Batch Image Conversion...", true, None)?,
&MenuItem::with_id(app, "batch_audio", "Batch Audio Conversion...", true, None)?,
&MenuItem::with_id(app, "batch_video", "Batch Video Conversion...", true, None)?,
],
)?;
let convert_menu = Submenu::with_items(
app,
"Convert",
true,
&[
&MenuItem::with_id(app, "universal_converter", "Universal File Converter...", true, Some("CmdOrCtrl+Shift+C"))?,
],
)?;
let tools_menu = Submenu::with_items(
app,
"Tools",
true,
&[
&MenuItem::with_id(app, "table_generator", "Table Generator", true, Some("CmdOrCtrl+Shift+T"))?,
&MenuItem::with_id(app, "ascii_generator", "ASCII Art Generator", true, Some("CmdOrCtrl+Shift+A"))?,
],
)?;
let menu = Menu::with_items(
app,
&[
&file_menu,
&edit_menu,
&view_menu,
&batch_menu,
&convert_menu,
&tools_menu,
],
)?;
Ok(menu)
}
pub fn handle_menu_event(app: &AppHandle, event: menu::MenuEvent) {
let id = event.id().as_ref();
match id {
"new" => { let _ = app.emit("file-new", ()); }
"open" => { let _ = app.emit("menu-open", "open"); }
"save" => { let _ = app.emit("file-save", ()); }
"toggle_preview" => { let _ = app.emit("toggle-preview", ()); }
"command_palette" => { let _ = app.emit("toggle-command-palette", ()); }
"toggle_bottom_panel" => { let _ = app.emit("toggle-bottom-panel", ()); }
"sidebar_explorer" => { let _ = app.emit("toggle-sidebar-panel", "explorer"); }
"sidebar_git" => { let _ = app.emit("toggle-sidebar-panel", "git"); }
"sidebar_snippets" => { let _ = app.emit("toggle-sidebar-panel", "snippets"); }
"sidebar_templates" => { let _ = app.emit("toggle-sidebar-panel", "templates"); }
"theme_light" => { let _ = app.emit("theme-changed", "atomonelight"); }
"theme_dark" => { let _ = app.emit("theme-changed", "dark"); }
"theme_solarized" => { let _ = app.emit("theme-changed", "solarized"); }
"theme_monokai" => { let _ = app.emit("theme-changed", "monokai"); }
"theme_github" => { let _ = app.emit("theme-changed", "github"); }
"undo" => { let _ = app.emit("undo", ()); }
"redo" => { let _ = app.emit("redo", ()); }
"find" => { let _ = app.emit("toggle-find", ()); }
"quit" => { app.exit(0); }
"cut" => { /* native cut handled by OS */ }
"copy" => { /* native copy handled by OS */ }
"paste" => { /* native paste handled by OS */ }
"select_all" => { /* native select all handled by OS */ }
_ => {}
}
}
+44
View File
@@ -0,0 +1,44 @@
use tauri::{AppHandle, Emitter, tray::{TrayIconBuilder, MouseButton, MouseButtonState, TrayIconEvent}, menu::{Menu, MenuItem}};
pub fn create_tray(app: &AppHandle) -> tauri::Result<()> {
let tray_menu = Menu::with_items(
app,
&[
&MenuItem::with_id(app, "show", "Show MarkdownConverter", true, None)?,
&MenuItem::with_id(app, "new_file", "New File", true, None)?,
&MenuItem::with_id(app, "open_file", "Open File...", true, None)?,
&MenuItem::with_id(app, "quit", "Quit", true, None)?,
],
)?;
let _tray = TrayIconBuilder::new()
.icon(app.default_window_icon().unwrap().clone())
.menu(&tray_menu)
.menu_on_left_click(false)
.on_menu_event(|app, event| {
match event.id().as_ref() {
"show" => {
if let Some(window) = app.get_webview_window("main") {
let _ = window.show();
let _ = window.set_focus();
}
}
"new_file" => { let _ = app.emit("file-new", ()); }
"open_file" => { let _ = app.emit("menu-open", "open"); }
"quit" => { app.exit(0); }
_ => {}
}
})
.on_tray_icon_event(|tray, event| {
if let TrayIconEvent::Click { button: MouseButton::Left, button_state: MouseButtonState::Up, .. } = event {
let app = tray.app_handle();
if let Some(window) = app.get_webview_window("main") {
let _ = window.show();
let _ = window.set_focus();
}
}
})
.build(app)?;
Ok(())
}