Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

g1t.sh
177 linesCodeBlameRaw
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
7use tauri::menu::{AboutMetadata, Menu, MenuItem, PredefinedMenuItem, Submenu};
8use tauri::{AppHandle, Wry};
9use tauri_plugin_opener::OpenerExt;
10
11use crate::{links, settings, tray, updates};
12
13const DOCS: &str = "https://docs.g1t.sh/guides/desktop/";
14const STATUS: &str = "https://status.g1t.sh/";
15
16pub 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 &separator()?,
52 &crate::devtools::item(app)?,
53 ],
54 )?;
55 let go = Submenu::with_items(
56 app,
57 "Go",
58 true,
59 &[
60 &item("back", "Back", Some(if cfg!(target_os = "macos") { "Cmd+[" } else { "Alt+Left" }))?,
61 &item("forward", "Forward", Some(if cfg!(target_os = "macos") { "Cmd+]" } else { "Alt+Right" }))?,
62 &separator()?,
63 &item("home", "Home", None)?,
64 &item("notifications", "Notifications", None)?,
65 ],
66 )?;
67 let window = Submenu::with_items(
68 app,
69 "Window",
70 true,
71 &[&PredefinedMenuItem::minimize(app, None)?, &PredefinedMenuItem::maximize(app, None)?],
72 )?;
73 let help = Submenu::with_items(
74 app,
75 "Help",
76 true,
77 &[&item("docs", "Desktop app guide", None)?, &item("status", "g1t.sh status", None)?, &separator()?, &check, &tray::restart_item(app)?],
78 )?;
79 // macOS keeps About in g1t's own menu; elsewhere it closes Help.
80 #[cfg(not(target_os = "macos"))]
81 {
82 help.append(&separator()?)?;
83 help.append(&PredefinedMenuItem::about(app, Some("About g1t"), Some(about()))?)?;
84 }
85
86 #[cfg(target_os = "macos")]
87 {
88 let g1t = Submenu::with_items(
89 app,
90 "g1t",
91 true,
92 &[
93 &PredefinedMenuItem::about(app, Some("About g1t"), Some(about()))?,
94 &separator()?,
95 &settings_item,
96 &server,
97 &separator()?,
98 &PredefinedMenuItem::services(app, None)?,
99 &separator()?,
100 &PredefinedMenuItem::hide(app, None)?,
101 &PredefinedMenuItem::hide_others(app, None)?,
102 &PredefinedMenuItem::show_all(app, None)?,
103 &separator()?,
104 &quit,
105 ],
106 )?;
107 let file = Submenu::with_items(app, "File", true, &[&PredefinedMenuItem::close_window(app, None)?])?;
108 Menu::with_items(app, &[&g1t, &file, &edit, &view, &go, &window, &help])
109 }
110 #[cfg(not(target_os = "macos"))]
111 {
112 let file = Submenu::with_items(
113 app,
114 "File",
115 true,
116 &[&settings_item, &server, &separator()?, &item("close", "Close window", Some("CmdOrCtrl+W"))?, &quit],
117 )?;
118 Menu::with_items(app, &[&file, &edit, &view, &go, &window, &help])
119 }
120}
121
122fn about() -> AboutMetadata<'static> {
123 AboutMetadata {
124 name: Some("g1t".into()),
125 version: Some(env!("CARGO_PKG_VERSION").into()),
126 copyright: Some("Flagon, Inc.".into()),
127 website: Some("https://g1t.sh".into()),
128 website_label: Some("g1t.sh".into()),
129 ..Default::default()
130 }
131}
132
133/// Runs the script on the main window's page.
134fn eval(app: &AppHandle, script: &str) {
135 if let Some(site) = crate::frame::site(app) {
136 let _ = site.eval(script);
137 }
138}
139
140/// A menu row was chosen, from the app's menu or the tray's.
141pub fn handle(app: &AppHandle, id: &str) {
142 match id {
143 "open" => tray::show(app),
144 "settings" => settings::open(app),
145 "runner-status" => settings::runner_menu(app, false, None),
146 "runner-toggle" => crate::runner::toggle(app),
147 "server" => tray::launcher(app, "change"),
148 // The settings ask, and show the answer where it cannot be missed.
149 "check" => settings::open_and_check(app),
150 "restart" => updates::restart(app),
151 "quit" => updates::quit(app),
152 "close" => tray::close(app),
153 "reload" => {
154 if let Some(site) = crate::frame::site(app) {
155 let _ = site.reload();
156 }
157 }
158 "zoom-in" => settings::zoom(app, 1),
159 "zoom-out" => settings::zoom(app, -1),
160 "zoom-reset" => settings::zoom(app, 0),
161 // Off with the setting, as the row is.
162 "devtools" => {
163 let _ = crate::devtools::toggle(app);
164 }
165 "back" => eval(app, "history.back()"),
166 "forward" => eval(app, "history.forward()"),
167 "home" => links::go(app, "/"),
168 "notifications" => links::go(app, "/notifications"),
169 "docs" => {
170 let _ = app.opener().open_url(DOCS, None::<&str>);
171 }
172 "status" => {
173 let _ = app.opener().open_url(STATUS, None::<&str>);
174 }
175 _ => {}
176 }
177}