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 own settings: how it starts, what closing the window does, |
| Merge branch 'worktree-agent-a6b141eb3a7e9cf01' | 2 | //! how it tells the person something arrived, its title bar, zoom, the |
| 3 | //! web inspector, updates, and the runner. They open as a dialog over the site, which the | |
| Merge branch 'main' into rm-agents | 4 | //! site's page draws (apps/web components/desktop-settings.tsx), from the |
| 5 | //! title bar, the menu, the tray, or the site's account menu. While the | |
| 6 | //! window shows no page of the site's (the launcher, the offline page, | |
| 7 | //! signing in), they open in a window of their own instead | |
| 8 | //! (`launcher/settings.html`). They are kept with the server in | |
| 9 | //! `desktop.json` (config.rs). Whether g1t opens when someone signs in to | |
| 10 | //! their computer is kept by the system itself, through the autostart | |
| 11 | //! plugin, and read back from it. | |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9 | 12 | |
| 13 | use serde::{Deserialize, Serialize}; | |
| Merge branch 'main' into rm-agents | 14 | use tauri::{AppHandle, Emitter, EventTarget, Manager, WebviewUrl, WebviewWindowBuilder}; |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9 | 15 | use tauri_plugin_autostart::ManagerExt; |
| 16 | ||
| 17 | use crate::config; | |
| 18 | ||
| Merge branch 'main' into rm-agents | 19 | /// The settings window's label: the settings when the site can't draw them. |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9 | 20 | pub const SETTINGS: &str = "settings"; |
| Merge branch 'main' into rm-agents | 21 | /// The Runner window's label: the runner on its own (launcher/runner.html), |
| 22 | /// when the site can't draw its menu under the title bar's dot. | |
| The desktop app is 0.3.4, and the runner has a window of its own. Beside the title bar's Settings button a chip says what the runner is doing, live, with a dot in its colour (Runner running, Running a job, Running 2 jobs, Runner starting, Runner updating, Runner stopped, Runner offline, Runner needs you), as Docker Desktop shows its engine; in a narrow window only the dot shows. It opens the Runner window, as the tray's runner row now does: at its top what the whole runner is doing, with what it is running, its version and workspace, and its own Stop or Start; then each kind of job's card as before; then its setup (running jobs here at all, how jobs run, the workspace, the computer with Check for runner updates and View on g1t); and its log, open and following along as it is written. Settings keeps the runner as one line, what it is doing with Open runner, and each window saves only its own settings over what the app has now, so neither undoes the other's. The chip's words are diagnosis.rs's, the Runner window is made off the main thread as Settings now is, and it has a capability of its own with only the commands it uses; the title bar may now ask the runner's status. Two things fixed on the way: a settings row hidden until it applies, Restart to change the title bar, showed all the time, since its row's display beat the hidden attribute; and in a narrow window the title bar's buttons ran over the search, which now shrinks instead. The desktop guide describes the chip, the Runner window and where each runner setting now is. | 23 | pub const RUNNER: &str = "runner"; |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9 | 24 | |
| 25 | /// The argument the system starts g1t with at sign-in, so it can tell a | |
| 26 | /// start of its own from someone opening it. | |
| 27 | pub const AT_LOGIN: &str = "--at-login"; | |
| 28 | ||
| 29 | /// The zoom levels the View menu steps through. | |
| 30 | pub const ZOOMS: &[f64] = &[0.67, 0.75, 0.8, 0.9, 1.0, 1.1, 1.25, 1.5, 1.75, 2.0]; | |
| 31 | ||
| 32 | #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] | |
| 33 | #[serde(default, rename_all = "camelCase")] | |
| 34 | pub struct Settings { | |
| 35 | /// Opened at sign-in, g1t waits in the tray instead of opening its window. | |
| 36 | pub start_hidden: bool, | |
| 37 | /// Closing the window leaves g1t running in the tray, for notifications; | |
| 38 | /// otherwise closing it quits. | |
| 39 | pub close_to_tray: bool, | |
| 40 | /// Notifications from the system, for what the site says needs the person. | |
| 41 | pub notifications: bool, | |
| 42 | /// The taskbar icon flashes, or the dock icon bounces, with a notification. | |
| 43 | pub flash: bool, | |
| 44 | /// What needs the person, counted on the icon. | |
| 45 | pub badge: bool, | |
| 46 | /// The system's title bar, and on Windows and Linux its menu bar, in | |
| 47 | /// place of g1t's own (frame.rs). Takes effect when g1t restarts. | |
| 48 | pub system_title_bar: bool, | |
| 49 | /// The site's zoom, 1 for as it is. | |
| 50 | pub zoom: f64, | |
| Merge branch 'worktree-agent-a6b141eb3a7e9cf01' | 51 | /// The web inspector for the site's page, from the View menu, F12 and |
| 52 | /// Ctrl+Shift+I (⌥⌘I on macOS); off unless someone turns it on | |
| 53 | /// (devtools.rs). On macOS and Linux it takes effect when g1t restarts. | |
| 54 | pub developer_tools: bool, | |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9 | 55 | /// Updates download by themselves; a person can still check from the menu. |
| 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. | 56 | /// The runner's own updates follow it too (runner/mod.rs). |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9 | 57 | pub auto_update: bool, |
| 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. | 58 | /// Runs the workspace's self-hosted jobs on this computer while g1t is |
| 59 | /// open (runner/mod.rs). On unless someone turns it off. | |
| 60 | pub runner: bool, | |
| 61 | /// How the runner runs: sandboxed unless someone chooses otherwise. | |
| 62 | pub runner_backend: crate::runner::Backend, | |
| 63 | /// The workspace it runs for; the one the person is in when it first registers. | |
| 64 | pub runner_workspace: Option<String>, | |
| Merge the desktop runner's settings: a card for each kind of job, Linux and Windows, each with its state, job, cause and fix, and its own Start and Stop; an honest wait for a release's sandbox images, checked again by itself; and the same on the Runners page. | 65 | /// Sandboxed, each kind of job on or off on its own: Linux jobs can |
| 66 | /// run while Windows jobs are stopped. All on unless someone stops one. | |
| 67 | pub runner_capabilities: RunnerCapabilities, | |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9 | 68 | } |
| 69 | ||
| Merge the desktop runner's settings: a card for each kind of job, Linux and Windows, each with its state, job, cause and fix, and its own Start and Stop; an honest wait for a release's sandbox images, checked again by itself; and the same on the Runners page. | 70 | /// Each kind of job the sandbox runs, on or off (`runnerCapabilities` in |
| 71 | /// desktop.json: `{"linux": true, "windows": false}`). | |
| 72 | #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] | |
| 73 | #[serde(default)] | |
| 74 | pub struct RunnerCapabilities { | |
| 75 | pub linux: bool, | |
| 76 | pub windows: bool, | |
| 77 | pub macos: bool, | |
| 78 | } | |
| 79 | ||
| 80 | impl Default for RunnerCapabilities { | |
| 81 | fn default() -> Self { | |
| 82 | Self { linux: true, windows: true, macos: true } | |
| 83 | } | |
| 84 | } | |
| 85 | ||
| 86 | impl RunnerCapabilities { | |
| 87 | /// Whether a kind of job is on; one this app doesn't know is. | |
| 88 | pub fn on(&self, id: &str) -> bool { | |
| 89 | match id { | |
| 90 | "linux" => self.linux, | |
| 91 | "windows" => self.windows, | |
| 92 | "macos" => self.macos, | |
| 93 | _ => true, | |
| 94 | } | |
| 95 | } | |
| 96 | ||
| 97 | /// Turns a kind of job on or off: false for a kind this app doesn't know. | |
| 98 | pub fn set(&mut self, id: &str, on: bool) -> bool { | |
| 99 | let kind = match id { | |
| 100 | "linux" => &mut self.linux, | |
| 101 | "windows" => &mut self.windows, | |
| 102 | "macos" => &mut self.macos, | |
| 103 | _ => return false, | |
| 104 | }; | |
| 105 | *kind = on; | |
| 106 | true | |
| 107 | } | |
| 108 | } | |
| 109 | ||
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9 | 110 | impl Default for Settings { |
| 111 | fn default() -> Self { | |
| 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. | 112 | Self { |
| 113 | start_hidden: true, | |
| 114 | close_to_tray: true, | |
| 115 | notifications: true, | |
| 116 | flash: true, | |
| 117 | badge: true, | |
| 118 | system_title_bar: false, | |
| 119 | zoom: 1.0, | |
| Merge branch 'worktree-agent-a6b141eb3a7e9cf01' | 120 | developer_tools: false, |
| 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. | 121 | auto_update: true, |
| 122 | runner: true, | |
| 123 | runner_backend: crate::runner::Backend::Sandboxed, | |
| 124 | runner_workspace: None, | |
| Merge the desktop runner's settings: a card for each kind of job, Linux and Windows, each with its state, job, cause and fix, and its own Start and Stop; an honest wait for a release's sandbox images, checked again by itself; and the same on the Runners page. | 125 | runner_capabilities: RunnerCapabilities::default(), |
| 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. | 126 | } |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9 | 127 | } |
| 128 | } | |
| 129 | ||
| 130 | impl Settings { | |
| 131 | /// A zoom the app offers, nearest to the one asked for. | |
| 132 | pub fn clamp_zoom(zoom: f64) -> f64 { | |
| 133 | ZOOMS.iter().copied().min_by(|a, b| (a - zoom).abs().total_cmp(&(b - zoom).abs())).unwrap_or(1.0) | |
| 134 | } | |
| 135 | } | |
| 136 | ||
| 137 | /// What the settings window shows: the settings, and what the app and the | |
| 138 | /// system know. | |
| 139 | #[derive(Serialize)] | |
| 140 | #[serde(rename_all = "camelCase")] | |
| 141 | pub struct View { | |
| 142 | #[serde(flatten)] | |
| 143 | pub settings: Settings, | |
| 144 | pub open_at_login: bool, | |
| 145 | pub version: String, | |
| 146 | pub platform: String, | |
| 147 | pub server: String, | |
| 148 | pub update_ready: Option<String>, | |
| 149 | /// When the app last asked for updates and heard back, in milliseconds since 1970. | |
| 150 | pub checked_at: Option<u64>, | |
| 151 | /// The title bar setting differs from the one the windows have: a restart applies it. | |
| 152 | pub title_bar_changed: bool, | |
| 153 | /// Whether this window draws g1t's own title bar, which the page then draws (settings.html). | |
| 154 | pub custom_frame: bool, | |
| Merge branch 'worktree-agent-a6b141eb3a7e9cf01' | 155 | /// The developer tools setting differs from the one the site's page |
| 156 | /// has, on macOS and Linux: a restart applies it (devtools.rs). | |
| 157 | pub developer_tools_changed: bool, | |
| 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. | 158 | /// The runner's state, diagnosis and recent log. |
| 159 | pub runner_view: crate::runner::View, | |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9 | 160 | } |
| 161 | ||
| 162 | pub fn view(app: &AppHandle) -> View { | |
| 163 | View { | |
| 164 | settings: config::settings(app), | |
| 165 | open_at_login: app.autolaunch().is_enabled().unwrap_or(false), | |
| 166 | version: env!("CARGO_PKG_VERSION").into(), | |
| 167 | platform: std::env::consts::OS.into(), | |
| 168 | server: config::server(app).to_string(), | |
| 169 | update_ready: crate::updates::ready(app), | |
| 170 | checked_at: crate::updates::checked_at(app), | |
| 171 | title_bar_changed: crate::frame::started(app) != crate::frame::custom(app), | |
| 172 | custom_frame: crate::frame::started(app), | |
| Merge branch 'worktree-agent-a6b141eb3a7e9cf01' | 173 | developer_tools_changed: crate::devtools::changed(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. | 174 | runner_view: crate::runner::view(app), |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9 | 175 | } |
| 176 | } | |
| 177 | ||
| 178 | /// Keeps new settings and puts them to work at once. | |
| 179 | pub fn apply(app: &AppHandle, mut settings: Settings) -> Result<(), String> { | |
| 180 | settings.zoom = Settings::clamp_zoom(settings.zoom); | |
| 181 | let before = config::settings(app); | |
| 182 | config::set_settings(app, settings.clone())?; | |
| 183 | if let Some(site) = crate::frame::site(app).filter(|_| before.zoom != settings.zoom) { | |
| 184 | let _ = site.set_zoom(settings.zoom); | |
| 185 | } | |
| 186 | if before.badge != settings.badge { | |
| 187 | crate::badge::refresh(app); | |
| 188 | } | |
| Merge branch 'worktree-agent-a6b141eb3a7e9cf01' | 189 | if before.developer_tools != settings.developer_tools { |
| 190 | crate::devtools::setting_changed(app); | |
| 191 | } | |
| Merge the desktop runner's settings: a card for each kind of job, Linux and Windows, each with its state, job, cause and fix, and its own Start and Stop; an honest wait for a release's sandbox images, checked again by itself; and the same on the Runners page. | 192 | let runner = |s: &Settings| (s.runner, s.runner_backend, s.runner_workspace.clone(), s.runner_capabilities.clone()); |
| 193 | if runner(&before) != runner(&settings) { | |
| 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. | 194 | crate::runner::settings_changed(app); |
| 195 | } | |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9 | 196 | Ok(()) |
| 197 | } | |
| 198 | ||
| 199 | /// Turns opening at sign-in on or off, with the system. | |
| 200 | pub fn open_at_login(app: &AppHandle, on: bool) -> Result<(), String> { | |
| 201 | let autolaunch = app.autolaunch(); | |
| 202 | let result = if on { autolaunch.enable() } else { autolaunch.disable() }; | |
| 203 | result.map_err(|error| error.to_string()) | |
| 204 | } | |
| 205 | ||
| 206 | /// Steps the zoom in (1), out (-1), or back to as it is (0). | |
| 207 | pub fn zoom(app: &AppHandle, step: i32) { | |
| 208 | let mut settings = config::settings(app); | |
| 209 | let at = ZOOMS.iter().position(|zoom| *zoom == Settings::clamp_zoom(settings.zoom)).unwrap_or(4); | |
| 210 | settings.zoom = match step { | |
| 211 | 0 => 1.0, | |
| 212 | 1 => ZOOMS[(at + 1).min(ZOOMS.len() - 1)], | |
| 213 | _ => ZOOMS[at.saturating_sub(1)], | |
| 214 | }; | |
| 215 | let _ = apply(app, settings); | |
| 216 | } | |
| 217 | ||
| 218 | /// Whether this launch is the system's, at sign-in, and g1t should wait in the tray. | |
| 219 | pub fn starts_hidden(app: &AppHandle) -> bool { | |
| 220 | std::env::args().any(|arg| arg == AT_LOGIN) && config::settings(app).start_hidden | |
| 221 | } | |
| 222 | ||
| 223 | /// What the settings window listens for: check for updates now. | |
| 224 | pub const CHECK_EVENT: &str = "g1t-check-updates"; | |
| 225 | /// What it listens for: an update is downloaded, with its version. | |
| 226 | pub const READY_EVENT: &str = "g1t-update-ready"; | |
| Merge branch 'main' into rm-agents | 227 | /// What the site's page listens for: open the settings dialog (`Opening`). |
| 228 | pub const OPEN_EVENT: &str = "g1t-open-settings"; | |
| 229 | /// What the site's page listens for: the runner menu under the title | |
| 230 | /// bar's dot (`RunnerMenu`). | |
| 231 | pub const RUNNER_MENU_EVENT: &str = "g1t-runner-menu"; | |
| 232 | ||
| 233 | /// The settings dialog, opened at a section (`runner`, `updates`), and | |
| 234 | /// looking for updates as it opens when `check` is set. | |
| 235 | #[derive(Clone, Debug, Serialize)] | |
| 236 | pub struct Opening { | |
| 237 | pub section: Option<&'static str>, | |
| 238 | pub check: bool, | |
| 239 | } | |
| 240 | ||
| 241 | /// The runner menu: `toggle`, from the dot, or `open`. `at` is where the | |
| 242 | /// dot is across the bar, as a share of its width, so the menu hangs under | |
| 243 | /// it whatever the site's zoom. | |
| 244 | #[derive(Clone, Debug, Serialize)] | |
| 245 | pub struct RunnerMenu { | |
| 246 | pub action: &'static str, | |
| 247 | pub at: Option<f64>, | |
| 248 | } | |
| 249 | ||
| 250 | /// Whether the site's page can draw the settings and the runner menu | |
| 251 | /// itself: the window is on one of the server's pages, and that page said | |
| 252 | /// it can (`site_ready` in bridge.rs). A page of an older site never says | |
| 253 | /// so, and they open in windows of their own, as they did. | |
| 254 | fn in_site(app: &AppHandle) -> bool { | |
| 255 | config::on_site(app) && config::site_ready(app) | |
| 256 | } | |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9 | 257 | |
| Merge branch 'main' into rm-agents | 258 | /// Opens the settings, or brings them forward. |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9 | 259 | pub fn open(app: &AppHandle) { |
| Merge branch 'main' into rm-agents | 260 | open_at(app, None, false); |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9 | 261 | } |
| 262 | ||
| Merge branch 'main' into rm-agents | 263 | /// "Check for updates…" in a menu: the settings open at Updates and check, |
| 264 | /// and say what they found, the download included. | |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9 | 265 | pub fn open_and_check(app: &AppHandle) { |
| Merge branch 'main' into rm-agents | 266 | open_at(app, Some("updates"), true); |
| 267 | } | |
| 268 | ||
| 269 | /// The settings at their Runner section: how jobs run, the workspace, each | |
| 270 | /// kind of job. A fix that asks the person to choose one opens them there. | |
| 271 | pub fn open_runner_settings(app: &AppHandle) { | |
| 272 | if in_site(app) { | |
| 273 | open_at(app, Some("runner"), false); | |
| 274 | } else { | |
| 275 | open_runner_window(app); | |
| 276 | } | |
| 277 | } | |
| 278 | ||
| 279 | /// The settings over the site, the main window shown if it was hidden; in | |
| 280 | /// a window of their own while the site isn't there to draw them. | |
| 281 | fn open_at(app: &AppHandle, section: Option<&'static str>, check: bool) { | |
| 282 | if in_site(app) { | |
| 283 | crate::tray::show(app); | |
| 284 | let _ = app.emit_to(EventTarget::webview(crate::frame::SITE), OPEN_EVENT, Opening { section, check }); | |
| 285 | return; | |
| 286 | } | |
| 287 | show(app, check); | |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9 | 288 | } |
| 289 | ||
| Merge branch 'main' into rm-agents | 290 | /// The runner menu, opened (the tray's runner row) or, from the title |
| 291 | /// bar's dot, opened or closed. While the site can't draw it, the Runner | |
| 292 | /// window. | |
| 293 | pub fn runner_menu(app: &AppHandle, toggle: bool, at: Option<f64>) { | |
| 294 | if in_site(app) { | |
| 295 | crate::tray::show(app); | |
| 296 | let at = at.filter(|at| at.is_finite()).map(|at| at.clamp(0.0, 1.0)); | |
| 297 | let menu = RunnerMenu { action: if toggle { "toggle" } else { "open" }, at }; | |
| 298 | let _ = app.emit_to(EventTarget::webview(crate::frame::SITE), RUNNER_MENU_EVENT, menu); | |
| 299 | return; | |
| 300 | } | |
| 301 | open_runner_window(app); | |
| 302 | } | |
| 303 | ||
| The desktop app is 0.3.4, and the runner has a window of its own. Beside the title bar's Settings button a chip says what the runner is doing, live, with a dot in its colour (Runner running, Running a job, Running 2 jobs, Runner starting, Runner updating, Runner stopped, Runner offline, Runner needs you), as Docker Desktop shows its engine; in a narrow window only the dot shows. It opens the Runner window, as the tray's runner row now does: at its top what the whole runner is doing, with what it is running, its version and workspace, and its own Stop or Start; then each kind of job's card as before; then its setup (running jobs here at all, how jobs run, the workspace, the computer with Check for runner updates and View on g1t); and its log, open and following along as it is written. Settings keeps the runner as one line, what it is doing with Open runner, and each window saves only its own settings over what the app has now, so neither undoes the other's. The chip's words are diagnosis.rs's, the Runner window is made off the main thread as Settings now is, and it has a capability of its own with only the commands it uses; the title bar may now ask the runner's status. Two things fixed on the way: a settings row hidden until it applies, Restart to change the title bar, showed all the time, since its row's display beat the hidden attribute; and in a narrow window the title bar's buttons ran over the search, which now shrinks instead. The desktop guide describes the chip, the Runner window and where each runner setting now is. | 304 | /// Opens the Runner window, or brings it forward. |
| Merge branch 'main' into rm-agents | 305 | pub fn open_runner_window(app: &AppHandle) { |
| The desktop app is 0.3.4, and the runner has a window of its own. Beside the title bar's Settings button a chip says what the runner is doing, live, with a dot in its colour (Runner running, Running a job, Running 2 jobs, Runner starting, Runner updating, Runner stopped, Runner offline, Runner needs you), as Docker Desktop shows its engine; in a narrow window only the dot shows. It opens the Runner window, as the tray's runner row now does: at its top what the whole runner is doing, with what it is running, its version and workspace, and its own Stop or Start; then each kind of job's card as before; then its setup (running jobs here at all, how jobs run, the workspace, the computer with Check for runner updates and View on g1t); and its log, open and following along as it is written. Settings keeps the runner as one line, what it is doing with Open runner, and each window saves only its own settings over what the app has now, so neither undoes the other's. The chip's words are diagnosis.rs's, the Runner window is made off the main thread as Settings now is, and it has a capability of its own with only the commands it uses; the title bar may now ask the runner's status. Two things fixed on the way: a settings row hidden until it applies, Restart to change the title bar, showed all the time, since its row's display beat the hidden attribute; and in a narrow window the title bar's buttons ran over the search, which now shrinks instead. The desktop guide describes the chip, the Runner window and where each runner setting now is. | 306 | if !forward(app, RUNNER) { |
| 307 | made_aside(app, Window { label: RUNNER, page: "runner.html", title: "g1t runner", size: (720.0, 760.0) }); | |
| 308 | } | |
| 309 | } | |
| 310 | ||
| 311 | /// Brings a window that is open forward: false when it isn't. | |
| 312 | fn forward(app: &AppHandle, label: &str) -> bool { | |
| 313 | let Some(window) = app.get_webview_window(label) else { return false }; | |
| 314 | let _ = window.unminimize(); | |
| 315 | let _ = window.show(); | |
| 316 | let _ = window.set_focus(); | |
| 317 | true | |
| 318 | } | |
| 319 | ||
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9 | 320 | fn show(app: &AppHandle, check: bool) { |
| The desktop app is 0.3.4, and the runner has a window of its own. Beside the title bar's Settings button a chip says what the runner is doing, live, with a dot in its colour (Runner running, Running a job, Running 2 jobs, Runner starting, Runner updating, Runner stopped, Runner offline, Runner needs you), as Docker Desktop shows its engine; in a narrow window only the dot shows. It opens the Runner window, as the tray's runner row now does: at its top what the whole runner is doing, with what it is running, its version and workspace, and its own Stop or Start; then each kind of job's card as before; then its setup (running jobs here at all, how jobs run, the workspace, the computer with Check for runner updates and View on g1t); and its log, open and following along as it is written. Settings keeps the runner as one line, what it is doing with Open runner, and each window saves only its own settings over what the app has now, so neither undoes the other's. The chip's words are diagnosis.rs's, the Runner window is made off the main thread as Settings now is, and it has a capability of its own with only the commands it uses; the title bar may now ask the runner's status. Two things fixed on the way: a settings row hidden until it applies, Restart to change the title bar, showed all the time, since its row's display beat the hidden attribute; and in a narrow window the title bar's buttons ran over the search, which now shrinks instead. The desktop guide describes the chip, the Runner window and where each runner setting now is. | 321 | if forward(app, SETTINGS) { |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9 | 322 | if check { |
| 323 | let _ = app.emit_to(SETTINGS, CHECK_EVENT, ()); | |
| 324 | } | |
| 325 | return; | |
| 326 | } | |
| The desktop app is 0.3.4, and the runner has a window of its own. Beside the title bar's Settings button a chip says what the runner is doing, live, with a dot in its colour (Runner running, Running a job, Running 2 jobs, Runner starting, Runner updating, Runner stopped, Runner offline, Runner needs you), as Docker Desktop shows its engine; in a narrow window only the dot shows. It opens the Runner window, as the tray's runner row now does: at its top what the whole runner is doing, with what it is running, its version and workspace, and its own Stop or Start; then each kind of job's card as before; then its setup (running jobs here at all, how jobs run, the workspace, the computer with Check for runner updates and View on g1t); and its log, open and following along as it is written. Settings keeps the runner as one line, what it is doing with Open runner, and each window saves only its own settings over what the app has now, so neither undoes the other's. The chip's words are diagnosis.rs's, the Runner window is made off the main thread as Settings now is, and it has a capability of its own with only the commands it uses; the title bar may now ask the runner's status. Two things fixed on the way: a settings row hidden until it applies, Restart to change the title bar, showed all the time, since its row's display beat the hidden attribute; and in a narrow window the title bar's buttons ran over the search, which now shrinks instead. The desktop guide describes the chip, the Runner window and where each runner setting now is. | 327 | // A window still loading has no listener yet: the address asks instead. |
| 328 | let page = if check { "settings.html?check" } else { "settings.html" }; | |
| 329 | made_aside(app, Window { label: SETTINGS, page, title: "g1t settings", size: (620.0, 720.0) }); | |
| 330 | } | |
| 331 | ||
| 332 | /// A window of the app's own pages. | |
| 333 | struct Window { | |
| 334 | label: &'static str, | |
| 335 | page: &'static str, | |
| 336 | title: &'static str, | |
| 337 | size: (f64, f64), | |
| 338 | } | |
| 339 | ||
| 340 | /// Makes a window on a thread of its own, whoever asks. The app's windows | |
| 341 | /// open from synchronous commands too (the title bar's buttons, the site's | |
| 342 | /// account menu), which run on the main thread, and on Windows a window | |
| 343 | /// made there deadlocks: it appears, white, and its page never loads. From | |
| 344 | /// another thread, Tauri hands the making to the event loop and waits. | |
| 345 | fn made_aside(app: &AppHandle, window: Window) { | |
| The desktop app is 0.3.3: Settings opens again on Windows, and an update found as g1t starts installs itself. Since 0.3.0, Settings opened from the title bar's button or the site's account menu showed a white window and nothing else: both are synchronous commands, which run on the main thread, and on Windows a webview window made there deadlocks (Tauri's known issue), so the window appeared and its page never loaded. The settings window is now made on a thread of its own whoever asks, and Tauri hands the making to the event loop. And so that opening g1t again is all it takes to leave a copy that misbehaves, the app looks for a newer version as it starts, three seconds after the window rather than fifteen, and one it downloads within its first two minutes is installed straight away, g1t opening again on it, before anyone has begun working in it; a runner in the middle of a job is never cut short for it, and then the update waits for Restart to update as before. After that it looks every hour rather than every six, and what it finds still waits for a restart or quitting. The desktop guide's Updates section says so. | 346 | let app = app.clone(); |
| The desktop app is 0.3.4, and the runner has a window of its own. Beside the title bar's Settings button a chip says what the runner is doing, live, with a dot in its colour (Runner running, Running a job, Running 2 jobs, Runner starting, Runner updating, Runner stopped, Runner offline, Runner needs you), as Docker Desktop shows its engine; in a narrow window only the dot shows. It opens the Runner window, as the tray's runner row now does: at its top what the whole runner is doing, with what it is running, its version and workspace, and its own Stop or Start; then each kind of job's card as before; then its setup (running jobs here at all, how jobs run, the workspace, the computer with Check for runner updates and View on g1t); and its log, open and following along as it is written. Settings keeps the runner as one line, what it is doing with Open runner, and each window saves only its own settings over what the app has now, so neither undoes the other's. The chip's words are diagnosis.rs's, the Runner window is made off the main thread as Settings now is, and it has a capability of its own with only the commands it uses; the title bar may now ask the runner's status. Two things fixed on the way: a settings row hidden until it applies, Restart to change the title bar, showed all the time, since its row's display beat the hidden attribute; and in a narrow window the title bar's buttons ran over the search, which now shrinks instead. The desktop guide describes the chip, the Runner window and where each runner setting now is. | 347 | let spawned = std::thread::Builder::new().name(format!("g1t-{}-window", window.label)).spawn(move || build(&app, &window)); |
| The desktop app is 0.3.3: Settings opens again on Windows, and an update found as g1t starts installs itself. Since 0.3.0, Settings opened from the title bar's button or the site's account menu showed a white window and nothing else: both are synchronous commands, which run on the main thread, and on Windows a webview window made there deadlocks (Tauri's known issue), so the window appeared and its page never loaded. The settings window is now made on a thread of its own whoever asks, and Tauri hands the making to the event loop. And so that opening g1t again is all it takes to leave a copy that misbehaves, the app looks for a newer version as it starts, three seconds after the window rather than fifteen, and one it downloads within its first two minutes is installed straight away, g1t opening again on it, before anyone has begun working in it; a runner in the middle of a job is never cut short for it, and then the update waits for Restart to update as before. After that it looks every hour rather than every six, and what it finds still waits for a restart or quitting. The desktop guide's Updates section says so. | 348 | if let Err(error) = spawned { |
| The desktop app is 0.3.4, and the runner has a window of its own. Beside the title bar's Settings button a chip says what the runner is doing, live, with a dot in its colour (Runner running, Running a job, Running 2 jobs, Runner starting, Runner updating, Runner stopped, Runner offline, Runner needs you), as Docker Desktop shows its engine; in a narrow window only the dot shows. It opens the Runner window, as the tray's runner row now does: at its top what the whole runner is doing, with what it is running, its version and workspace, and its own Stop or Start; then each kind of job's card as before; then its setup (running jobs here at all, how jobs run, the workspace, the computer with Check for runner updates and View on g1t); and its log, open and following along as it is written. Settings keeps the runner as one line, what it is doing with Open runner, and each window saves only its own settings over what the app has now, so neither undoes the other's. The chip's words are diagnosis.rs's, the Runner window is made off the main thread as Settings now is, and it has a capability of its own with only the commands it uses; the title bar may now ask the runner's status. Two things fixed on the way: a settings row hidden until it applies, Restart to change the title bar, showed all the time, since its row's display beat the hidden attribute; and in a narrow window the title bar's buttons ran over the search, which now shrinks instead. The desktop guide describes the chip, the Runner window and where each runner setting now is. | 349 | eprintln!("g1t: a window could not open: {error}"); |
| The desktop app is 0.3.3: Settings opens again on Windows, and an update found as g1t starts installs itself. Since 0.3.0, Settings opened from the title bar's button or the site's account menu showed a white window and nothing else: both are synchronous commands, which run on the main thread, and on Windows a webview window made there deadlocks (Tauri's known issue), so the window appeared and its page never loaded. The settings window is now made on a thread of its own whoever asks, and Tauri hands the making to the event loop. And so that opening g1t again is all it takes to leave a copy that misbehaves, the app looks for a newer version as it starts, three seconds after the window rather than fifteen, and one it downloads within its first two minutes is installed straight away, g1t opening again on it, before anyone has begun working in it; a runner in the middle of a job is never cut short for it, and then the update waits for Restart to update as before. After that it looks every hour rather than every six, and what it finds still waits for a restart or quitting. The desktop guide's Updates section says so. | 350 | } |
| 351 | } | |
| 352 | ||
| The desktop app is 0.3.4, and the runner has a window of its own. Beside the title bar's Settings button a chip says what the runner is doing, live, with a dot in its colour (Runner running, Running a job, Running 2 jobs, Runner starting, Runner updating, Runner stopped, Runner offline, Runner needs you), as Docker Desktop shows its engine; in a narrow window only the dot shows. It opens the Runner window, as the tray's runner row now does: at its top what the whole runner is doing, with what it is running, its version and workspace, and its own Stop or Start; then each kind of job's card as before; then its setup (running jobs here at all, how jobs run, the workspace, the computer with Check for runner updates and View on g1t); and its log, open and following along as it is written. Settings keeps the runner as one line, what it is doing with Open runner, and each window saves only its own settings over what the app has now, so neither undoes the other's. The chip's words are diagnosis.rs's, the Runner window is made off the main thread as Settings now is, and it has a capability of its own with only the commands it uses; the title bar may now ask the runner's status. Two things fixed on the way: a settings row hidden until it applies, Restart to change the title bar, showed all the time, since its row's display beat the hidden attribute; and in a narrow window the title bar's buttons ran over the search, which now shrinks instead. The desktop guide describes the chip, the Runner window and where each runner setting now is. | 353 | fn build(app: &AppHandle, window: &Window) { |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9 | 354 | let custom = crate::frame::started(app); |
| Merge branch 'worktree-agent-a6b141eb3a7e9cf01' | 355 | // Never an inspector: the devtools feature gives every web view one |
| 356 | // unless it is told otherwise (src/devtools.rs). | |
| The desktop app is 0.3.4, and the runner has a window of its own. Beside the title bar's Settings button a chip says what the runner is doing, live, with a dot in its colour (Runner running, Running a job, Running 2 jobs, Runner starting, Runner updating, Runner stopped, Runner offline, Runner needs you), as Docker Desktop shows its engine; in a narrow window only the dot shows. It opens the Runner window, as the tray's runner row now does: at its top what the whole runner is doing, with what it is running, its version and workspace, and its own Stop or Start; then each kind of job's card as before; then its setup (running jobs here at all, how jobs run, the workspace, the computer with Check for runner updates and View on g1t); and its log, open and following along as it is written. Settings keeps the runner as one line, what it is doing with Open runner, and each window saves only its own settings over what the app has now, so neither undoes the other's. The chip's words are diagnosis.rs's, the Runner window is made off the main thread as Settings now is, and it has a capability of its own with only the commands it uses; the title bar may now ask the runner's status. Two things fixed on the way: a settings row hidden until it applies, Restart to change the title bar, showed all the time, since its row's display beat the hidden attribute; and in a narrow window the title bar's buttons ran over the search, which now shrinks instead. The desktop guide describes the chip, the Runner window and where each runner setting now is. | 357 | let builder = WebviewWindowBuilder::new(app, window.label, WebviewUrl::App(window.page.into())) |
| Merge branch 'worktree-agent-a6b141eb3a7e9cf01' | 358 | .title(window.title) |
| 359 | .devtools(false); | |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9 | 360 | let built = crate::frame::dialog(builder, custom) |
| The desktop app is 0.3.4, and the runner has a window of its own. Beside the title bar's Settings button a chip says what the runner is doing, live, with a dot in its colour (Runner running, Running a job, Running 2 jobs, Runner starting, Runner updating, Runner stopped, Runner offline, Runner needs you), as Docker Desktop shows its engine; in a narrow window only the dot shows. It opens the Runner window, as the tray's runner row now does: at its top what the whole runner is doing, with what it is running, its version and workspace, and its own Stop or Start; then each kind of job's card as before; then its setup (running jobs here at all, how jobs run, the workspace, the computer with Check for runner updates and View on g1t); and its log, open and following along as it is written. Settings keeps the runner as one line, what it is doing with Open runner, and each window saves only its own settings over what the app has now, so neither undoes the other's. The chip's words are diagnosis.rs's, the Runner window is made off the main thread as Settings now is, and it has a capability of its own with only the commands it uses; the title bar may now ask the runner's status. Two things fixed on the way: a settings row hidden until it applies, Restart to change the title bar, showed all the time, since its row's display beat the hidden attribute; and in a narrow window the title bar's buttons ran over the search, which now shrinks instead. The desktop guide describes the chip, the Runner window and where each runner setting now is. | 361 | .inner_size(window.size.0, window.size.1) |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9 | 362 | .min_inner_size(440.0, 420.0) |
| 363 | .maximizable(false) | |
| 364 | .minimizable(false) | |
| 365 | .center() | |
| 366 | // The page is bundled with the app; its links go to the browser (settings.js). | |
| 367 | .on_navigation(config::is_launcher) | |
| 368 | .build(); | |
| 369 | match built { | |
| 370 | Ok(window) => { | |
| 371 | let _ = window.set_focus(); | |
| 372 | } | |
| The desktop app is 0.3.4, and the runner has a window of its own. Beside the title bar's Settings button a chip says what the runner is doing, live, with a dot in its colour (Runner running, Running a job, Running 2 jobs, Runner starting, Runner updating, Runner stopped, Runner offline, Runner needs you), as Docker Desktop shows its engine; in a narrow window only the dot shows. It opens the Runner window, as the tray's runner row now does: at its top what the whole runner is doing, with what it is running, its version and workspace, and its own Stop or Start; then each kind of job's card as before; then its setup (running jobs here at all, how jobs run, the workspace, the computer with Check for runner updates and View on g1t); and its log, open and following along as it is written. Settings keeps the runner as one line, what it is doing with Open runner, and each window saves only its own settings over what the app has now, so neither undoes the other's. The chip's words are diagnosis.rs's, the Runner window is made off the main thread as Settings now is, and it has a capability of its own with only the commands it uses; the title bar may now ask the runner's status. Two things fixed on the way: a settings row hidden until it applies, Restart to change the title bar, showed all the time, since its row's display beat the hidden attribute; and in a narrow window the title bar's buttons ran over the search, which now shrinks instead. The desktop guide describes the chip, the Runner window and where each runner setting now is. | 373 | Err(error) => eprintln!("g1t: the {} window could not open: {error}", window.label), |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9 | 374 | } |
| 375 | } | |
| 376 | ||
| 377 | #[cfg(test)] | |
| 378 | mod tests { | |
| 379 | use super::Settings; | |
| 380 | ||
| 381 | #[test] | |
| 382 | fn a_missing_setting_is_its_default() { | |
| 383 | let settings: Settings = serde_json::from_str(r#"{"closeToTray":false}"#).unwrap(); | |
| 384 | assert!(!settings.close_to_tray); | |
| 385 | assert!(settings.notifications); | |
| 386 | assert_eq!(settings.zoom, 1.0); | |
| Merge branch 'worktree-agent-a6b141eb3a7e9cf01' | 387 | // The web inspector is off for someone who never turned it on. |
| 388 | assert!(!settings.developer_tools); | |
| 389 | let developer: Settings = serde_json::from_str(r#"{"developerTools":true}"#).unwrap(); | |
| 390 | assert!(developer.developer_tools); | |
| 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. | 391 | // The runner is on, and sandboxed, for someone who never chose. |
| 392 | assert!(settings.runner); | |
| 393 | assert_eq!(settings.runner_backend, crate::runner::Backend::Sandboxed); | |
| 394 | let chosen: Settings = serde_json::from_str(r#"{"runner":false,"runnerBackend":"host","runnerWorkspace":"acme"}"#).unwrap(); | |
| 395 | assert!(!chosen.runner); | |
| 396 | assert_eq!(chosen.runner_backend, crate::runner::Backend::Host); | |
| 397 | assert_eq!(chosen.runner_workspace.as_deref(), Some("acme")); | |
| Merge the desktop runner's settings: a card for each kind of job, Linux and Windows, each with its state, job, cause and fix, and its own Start and Stop; an honest wait for a release's sandbox images, checked again by itself; and the same on the Runners page. | 398 | // Every kind of job is on until someone stops one; a kind left out is on. |
| 399 | assert!(chosen.runner_capabilities.on("linux") && chosen.runner_capabilities.on("windows")); | |
| 400 | let one_off: Settings = serde_json::from_str(r#"{"runnerCapabilities":{"windows":false}}"#).unwrap(); | |
| 401 | assert!(one_off.runner_capabilities.on("linux")); | |
| 402 | assert!(!one_off.runner_capabilities.on("windows")); | |
| 403 | let mut kinds = one_off.runner_capabilities.clone(); | |
| 404 | assert!(kinds.set("windows", true) && kinds.on("windows")); | |
| 405 | assert!(!kinds.set("plan9", false)); | |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9 | 406 | } |
| 407 | ||
| 408 | #[test] | |
| 409 | fn zoom_is_one_the_app_offers() { | |
| 410 | assert_eq!(Settings::clamp_zoom(1.07), 1.1); | |
| 411 | assert_eq!(Settings::clamp_zoom(9.0), 2.0); | |
| 412 | assert_eq!(Settings::clamp_zoom(0.1), 0.67); | |
| 413 | } | |
| 414 | } |
This file's history is long; its oldest lines are credited to the oldest commit read.