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.
| preparing for cloud work | 1 | //! The tray icon and its menu, and showing and hiding the window. Closing |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9 | 2 | //! the window hides it, unless settings say closing quits; g1t keeps |
| 3 | //! running for notifications, and the tray's Open, or the icon, brings it | |
| 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. | 4 | //! back. The runner's state is a row of its own, which opens settings, |
| 5 | //! with Start or Stop under it (runner/mod.rs). Quit is here too. The menu's rows are handled with the app | |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9 | 6 | //! menu's (menu.rs). |
| preparing for cloud work | 7 | |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9 | 8 | use std::sync::Mutex; |
| preparing for cloud work | 9 | |
| 10 | use tauri::menu::{Menu, MenuItem, PredefinedMenuItem}; | |
| 11 | use tauri::tray::{MouseButton, MouseButtonState, TrayIconBuilder, TrayIconEvent}; | |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9 | 12 | use tauri::{AppHandle, Wry}; |
| preparing for cloud work | 13 | |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9 | 14 | use crate::{config, updates}; |
| preparing for cloud work | 15 | |
| 16 | pub const TRAY: &str = "g1t"; | |
| 17 | ||
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9 | 18 | /// The menu rows that restart into a downloaded update, the tray's and |
| 19 | /// the Help menu's: off until one is. | |
| 20 | static RESTART: Mutex<Vec<MenuItem<Wry>>> = Mutex::new(Vec::new()); | |
| 21 | ||
| 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. | 22 | /// The runner's rows: what it is doing, and Start or Stop. |
| 23 | static RUNNER: Mutex<Option<(MenuItem<Wry>, MenuItem<Wry>)>> = Mutex::new(None); | |
| 24 | ||
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9 | 25 | /// A row that restarts into the update, kept so `update_ready` can turn it on. |
| 26 | pub fn restart_item(app: &AppHandle) -> tauri::Result<MenuItem<Wry>> { | |
| 27 | let item = MenuItem::with_id(app, "restart", "Restart to finish updating", false, None::<&str>)?; | |
| 28 | RESTART.lock().unwrap().push(item.clone()); | |
| 29 | Ok(item) | |
| 30 | } | |
| preparing for cloud work | 31 | |
| 32 | pub fn install(app: &AppHandle) -> tauri::Result<()> { | |
| 33 | let open = MenuItem::with_id(app, "open", "Open g1t", true, None::<&str>)?; | |
| 34 | let check = MenuItem::with_id(app, "check", "Check for updates…", true, None::<&str>)?; | |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9 | 35 | let restart = restart_item(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. | 36 | let runner_view = crate::runner::view(app); |
| 37 | let runner = MenuItem::with_id(app, "runner-status", crate::runner::headline(&runner_view), true, None::<&str>)?; | |
| 38 | let runner_toggle = MenuItem::with_id(app, "runner-toggle", toggle_text(&runner_view), true, None::<&str>)?; | |
| 39 | *RUNNER.lock().unwrap() = Some((runner.clone(), runner_toggle.clone())); | |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9 | 40 | let settings = MenuItem::with_id(app, "settings", "Settings…", true, None::<&str>)?; |
| preparing for cloud work | 41 | let server = MenuItem::with_id(app, "server", "Use another g1t…", true, None::<&str>)?; |
| 42 | let quit = MenuItem::with_id(app, "quit", "Quit g1t", true, None::<&str>)?; | |
| 43 | let menu = Menu::with_items( | |
| 44 | app, | |
| 45 | &[ | |
| 46 | &open, | |
| 47 | &PredefinedMenuItem::separator(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. | 48 | &runner, |
| 49 | &runner_toggle, | |
| 50 | &PredefinedMenuItem::separator(app)?, | |
| preparing for cloud work | 51 | &check, |
| 52 | &restart, | |
| 53 | &PredefinedMenuItem::separator(app)?, | |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9 | 54 | &settings, |
| preparing for cloud work | 55 | &server, |
| 56 | &PredefinedMenuItem::separator(app)?, | |
| 57 | &quit, | |
| 58 | ], | |
| 59 | )?; | |
| 60 | let mut tray = TrayIconBuilder::with_id(TRAY) | |
| 61 | .menu(&menu) | |
| 62 | .show_menu_on_left_click(false) | |
| 63 | .tooltip("g1t") | |
| 64 | .on_tray_icon_event(|tray, event| { | |
| 65 | if let TrayIconEvent::Click { button: MouseButton::Left, button_state: MouseButtonState::Up, .. } = event { | |
| 66 | show(tray.app_handle()); | |
| 67 | } | |
| 68 | }); | |
| 69 | if let Some(icon) = app.default_window_icon() { | |
| 70 | tray = tray.icon(icon.clone()); | |
| 71 | } | |
| 72 | tray.build(app)?; | |
| 73 | Ok(()) | |
| 74 | } | |
| 75 | ||
| 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. | 76 | fn toggle_text(view: &crate::runner::View) -> &'static str { |
| 77 | if view.enabled && !view.paused { "Stop running jobs" } else { "Start running jobs" } | |
| 78 | } | |
| 79 | ||
| 80 | /// The runner changed: its rows, and the icon's tooltip, say what it is doing now. | |
| 81 | pub fn runner_changed(app: &AppHandle, view: &crate::runner::View) { | |
| 82 | if let Some((status, toggle)) = RUNNER.lock().unwrap().as_ref() { | |
| 83 | let _ = status.set_text(crate::runner::headline(view)); | |
| 84 | let _ = toggle.set_text(toggle_text(view)); | |
| 85 | } | |
| 86 | if let Some(tray) = app.tray_by_id(TRAY) { | |
| 87 | let _ = tray.set_tooltip(Some(format!("g1t · {}", crate::runner::headline(view)))); | |
| 88 | } | |
| 89 | } | |
| 90 | ||
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9 | 91 | /// The update is downloaded: the menus say so, and offer the restart. |
| preparing for cloud work | 92 | pub fn update_ready(version: &str) { |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9 | 93 | for item in RESTART.lock().unwrap().iter() { |
| preparing for cloud work | 94 | let _ = item.set_text(format!("Restart to update to {version}")); |
| 95 | let _ = item.set_enabled(true); | |
| 96 | } | |
| 97 | } | |
| 98 | ||
| 99 | pub fn show(app: &AppHandle) { | |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9 | 100 | if let Some(window) = crate::frame::window(app) { |
| preparing for cloud work | 101 | let _ = window.show(); |
| 102 | let _ = window.unminimize(); | |
| 103 | let _ = window.set_focus(); | |
| 104 | } | |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9 | 105 | if let Some(site) = crate::frame::site(app) { |
| 106 | let _ = site.set_focus(); | |
| 107 | } | |
| 108 | } | |
| 109 | ||
| 110 | /// The window was closed: it waits in the tray, or g1t quits, as settings say. | |
| 111 | pub fn close(app: &AppHandle) { | |
| 112 | if config::settings(app).close_to_tray { | |
| 113 | hide(app); | |
| 114 | } else { | |
| 115 | updates::quit(app); | |
| 116 | } | |
| preparing for cloud work | 117 | } |
| 118 | ||
| 119 | pub fn hide(app: &AppHandle) { | |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9 | 120 | if let Some(window) = crate::frame::window(app) { |
| preparing for cloud work | 121 | let _ = window.hide(); |
| 122 | } | |
| 123 | } | |
| 124 | ||
| 125 | /// The address of the launcher page bundled with the app. | |
| 126 | pub fn launcher_url(query: &str) -> url::Url { | |
| 127 | let base = if cfg!(windows) { "http://tauri.localhost/" } else { "tauri://localhost/" }; | |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9 | 128 | let mut url = config::dev_server().cloned().unwrap_or_else(|| url::Url::parse(base).expect("the launcher's address is a URL")); |
| preparing for cloud work | 129 | url.set_path("/index.html"); |
| 130 | if !query.is_empty() { | |
| 131 | url.set_query(Some(query)); | |
| 132 | } | |
| 133 | url | |
| 134 | } | |
| 135 | ||
| 136 | /// Takes the window to the launcher: `change` to choose another server. | |
| 137 | pub fn launcher(app: &AppHandle, query: &str) { | |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9 | 138 | if let Some(site) = crate::frame::site(app) { |
| 139 | let _ = site.navigate(launcher_url(query)); | |
| preparing for cloud work | 140 | show(app); |
| 141 | } | |
| 142 | } |
This file's history is long; its oldest lines are credited to the oldest commit read.