Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9 | 1 | //! The app's menu: File, Edit, View, Go, Window and Help, as a desktop app |
| 2 | //! has. On macOS it is the menu bar at the top of the screen, with g1t's | |
| 3 | //! own menu first; on Windows and Linux it sits under the window's title, | |
| 4 | //! and settings can hide it. The tray's menu (tray.rs) shares its ids, and | |
| 5 | //! every menu event, from either, comes to `handle`. | |
| 6 | ||
| 7 | use tauri::menu::{AboutMetadata, Menu, MenuItem, PredefinedMenuItem, Submenu}; | |
| 8 | use tauri::{AppHandle, Wry}; | |
| 9 | use tauri_plugin_opener::OpenerExt; | |
| 10 | ||
| 11 | use crate::{links, settings, tray, updates}; | |
| 12 | ||
| 13 | const DOCS: &str = "https://docs.g1t.sh/guides/desktop/"; | |
| 14 | const STATUS: &str = "https://status.g1t.sh/"; | |
| 15 | ||
| 16 | pub fn build(app: &AppHandle) -> tauri::Result<Menu<Wry>> { | |
| 17 | let separator = || PredefinedMenuItem::separator(app); | |
| 18 | let item = |id: &str, text: &str, accelerator: Option<&str>| MenuItem::with_id(app, id, text, true, accelerator); | |
| 19 | ||
| 20 | let settings_item = item("settings", if cfg!(target_os = "macos") { "Settings…" } else { "Settings" }, Some("CmdOrCtrl+,"))?; | |
| 21 | let check = item("check", "Check for updates…", None)?; | |
| 22 | let server = item("server", "Use another g1t…", None)?; | |
| 23 | let quit = item("quit", "Quit g1t", Some("CmdOrCtrl+Q"))?; | |
| 24 | ||
| 25 | let edit = Submenu::with_items( | |
| 26 | app, | |
| 27 | "Edit", | |
| 28 | true, | |
| 29 | &[ | |
| 30 | &PredefinedMenuItem::undo(app, None)?, | |
| 31 | &PredefinedMenuItem::redo(app, None)?, | |
| 32 | &separator()?, | |
| 33 | &PredefinedMenuItem::cut(app, None)?, | |
| 34 | &PredefinedMenuItem::copy(app, None)?, | |
| 35 | &PredefinedMenuItem::paste(app, None)?, | |
| 36 | &PredefinedMenuItem::select_all(app, None)?, | |
| 37 | ], | |
| 38 | )?; | |
| 39 | let view = Submenu::with_items( | |
| 40 | app, | |
| 41 | "View", | |
| 42 | true, | |
| 43 | &[ | |
| 44 | &item("reload", "Reload", Some("CmdOrCtrl+R"))?, | |
| 45 | &separator()?, | |
| 46 | &item("zoom-reset", "Actual size", Some("CmdOrCtrl+0"))?, | |
| 47 | &item("zoom-in", "Zoom in", Some("CmdOrCtrl+="))?, | |
| 48 | &item("zoom-out", "Zoom out", Some("CmdOrCtrl+-"))?, | |
| 49 | &separator()?, | |
| 50 | &PredefinedMenuItem::fullscreen(app, None)?, | |
| 51 | ], | |
| 52 | )?; | |
| 53 | let go = Submenu::with_items( | |
| 54 | app, | |
| 55 | "Go", | |
| 56 | true, | |
| 57 | &[ | |
| 58 | &item("back", "Back", Some(if cfg!(target_os = "macos") { "Cmd+[" } else { "Alt+Left" }))?, | |
| 59 | &item("forward", "Forward", Some(if cfg!(target_os = "macos") { "Cmd+]" } else { "Alt+Right" }))?, | |
| 60 | &separator()?, | |
| 61 | &item("home", "Home", None)?, | |
| 62 | &item("notifications", "Notifications", None)?, | |
| 63 | ], | |
| 64 | )?; | |
| 65 | let window = Submenu::with_items( | |
| 66 | app, | |
| 67 | "Window", | |
| 68 | true, | |
| 69 | &[&PredefinedMenuItem::minimize(app, None)?, &PredefinedMenuItem::maximize(app, None)?], | |
| 70 | )?; | |
| 71 | let help = Submenu::with_items( | |
| 72 | app, | |
| 73 | "Help", | |
| 74 | true, | |
| 75 | &[&item("docs", "Desktop app guide", None)?, &item("status", "g1t.sh status", None)?, &separator()?, &check, &tray::restart_item(app)?], | |
| 76 | )?; | |
| 77 | // macOS keeps About in g1t's own menu; elsewhere it closes Help. | |
| 78 | #[cfg(not(target_os = "macos"))] | |
| 79 | { | |
| 80 | help.append(&separator()?)?; | |
| 81 | help.append(&PredefinedMenuItem::about(app, Some("About g1t"), Some(about()))?)?; | |
| 82 | } | |
| 83 | ||
| 84 | #[cfg(target_os = "macos")] | |
| 85 | { | |
| 86 | let g1t = Submenu::with_items( | |
| 87 | app, | |
| 88 | "g1t", | |
| 89 | true, | |
| 90 | &[ | |
| 91 | &PredefinedMenuItem::about(app, Some("About g1t"), Some(about()))?, | |
| 92 | &separator()?, | |
| 93 | &settings_item, | |
| 94 | &server, | |
| 95 | &separator()?, | |
| 96 | &PredefinedMenuItem::services(app, None)?, | |
| 97 | &separator()?, | |
| 98 | &PredefinedMenuItem::hide(app, None)?, | |
| 99 | &PredefinedMenuItem::hide_others(app, None)?, | |
| 100 | &PredefinedMenuItem::show_all(app, None)?, | |
| 101 | &separator()?, | |
| 102 | &quit, | |
| 103 | ], | |
| 104 | )?; | |
| 105 | let file = Submenu::with_items(app, "File", true, &[&PredefinedMenuItem::close_window(app, None)?])?; | |
| 106 | Menu::with_items(app, &[&g1t, &file, &edit, &view, &go, &window, &help]) | |
| 107 | } | |
| 108 | #[cfg(not(target_os = "macos"))] | |
| 109 | { | |
| 110 | let file = Submenu::with_items( | |
| 111 | app, | |
| 112 | "File", | |
| 113 | true, | |
| 114 | &[&settings_item, &server, &separator()?, &item("close", "Close window", Some("CmdOrCtrl+W"))?, &quit], | |
| 115 | )?; | |
| 116 | Menu::with_items(app, &[&file, &edit, &view, &go, &window, &help]) | |
| 117 | } | |
| 118 | } | |
| 119 | ||
| 120 | fn about() -> AboutMetadata<'static> { | |
| 121 | AboutMetadata { | |
| 122 | name: Some("g1t".into()), | |
| 123 | version: Some(env!("CARGO_PKG_VERSION").into()), | |
| 124 | copyright: Some("Flagon, Inc.".into()), | |
| 125 | website: Some("https://g1t.sh".into()), | |
| 126 | website_label: Some("g1t.sh".into()), | |
| 127 | ..Default::default() | |
| 128 | } | |
| 129 | } | |
| 130 | ||
| 131 | /// Runs the script on the main window's page. | |
| 132 | fn eval(app: &AppHandle, script: &str) { | |
| 133 | if let Some(site) = crate::frame::site(app) { | |
| 134 | let _ = site.eval(script); | |
| 135 | } | |
| 136 | } | |
| 137 | ||
| 138 | /// A menu row was chosen, from the app's menu or the tray's. | |
| 139 | pub fn handle(app: &AppHandle, id: &str) { | |
| 140 | match id { | |
| 141 | "open" => tray::show(app), | |
| Merge the desktop app's runner: it starts with the app for the signed-in person, sandboxed by default and running nothing where no sandbox is available yet, registers through the site, keeps itself up to date, and says everywhere what it is doing, why it is not, and how to fix it. | 142 | "settings" | "runner-status" => settings::open(app), |
| 143 | "runner-toggle" => crate::runner::toggle(app), | |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9 | 144 | "server" => tray::launcher(app, "change"), |
| 145 | // The settings window asks, and shows the answer where it cannot be missed. | |
| 146 | "check" => settings::open_and_check(app), | |
| 147 | "restart" => updates::restart(app), | |
| 148 | "quit" => updates::quit(app), | |
| 149 | "close" => tray::close(app), | |
| 150 | "reload" => { | |
| 151 | if let Some(site) = crate::frame::site(app) { | |
| 152 | let _ = site.reload(); | |
| 153 | } | |
| 154 | } | |
| 155 | "zoom-in" => settings::zoom(app, 1), | |
| 156 | "zoom-out" => settings::zoom(app, -1), | |
| 157 | "zoom-reset" => settings::zoom(app, 0), | |
| 158 | "back" => eval(app, "history.back()"), | |
| 159 | "forward" => eval(app, "history.forward()"), | |
| 160 | "home" => links::go(app, "/"), | |
| 161 | "notifications" => links::go(app, "/notifications"), | |
| 162 | "docs" => { | |
| 163 | let _ = app.opener().open_url(DOCS, None::<&str>); | |
| 164 | } | |
| 165 | "status" => { | |
| 166 | let _ = app.opener().open_url(STATUS, None::<&str>); | |
| 167 | } | |
| 168 | _ => {} | |
| 169 | } | |
| 170 | } |
This file's history is long; its oldest lines are credited to the oldest commit read.