Merge branch 'worktree-agent-a6b141eb3a7e9cf01'
18 files+254−120/18 viewed
| 450 | 450 | ||
| 451 | 451 | // Every setting, as the app has it now (the settings window changes the | |
| 452 | 452 | // rest), with the runner's own as they are on the page. | |
| 453 | − | var KEPT = ["startHidden", "closeToTray", "notifications", "flash", "badge", "systemTitleBar", "autoUpdate", "zoom", "runner", "runnerBackend", "runnerWorkspace", "runnerCapabilities"]; | |
| 453 | + | var KEPT = ["startHidden", "closeToTray", "notifications", "flash", "badge", "systemTitleBar", "autoUpdate", "zoom", "developerTools", "runner", "runnerBackend", "runnerWorkspace", "runnerCapabilities"]; | |
| 454 | 454 | function save() { | |
| 455 | 455 | invoke("get_settings") | |
| 456 | 456 | .then(function (fresh) { |
| 8 | 8 | (function () { | |
| 9 | 9 | var SWITCHES = ["startHidden", "closeToTray", "notifications", "flash", "badge", "systemTitleBar", "autoUpdate"]; | |
| 10 | 10 | // The settings the app keeps (src/settings.rs Settings); a save sends them all. | |
| 11 | − | var KEPT = SWITCHES.concat(["zoom", "runner", "runnerBackend", "runnerWorkspace", "runnerCapabilities"]); | |
| 11 | + | var KEPT = SWITCHES.concat(["zoom", "developerTools", "runner", "runnerBackend", "runnerWorkspace", "runnerCapabilities"]); | |
| 12 | 12 | var current = null; | |
| 13 | 13 | ||
| 14 | 14 | function byId(id) { |
| 1350 | 1350 | "tauri-plugin-window-state", | |
| 1351 | 1351 | "tokio", | |
| 1352 | 1352 | "url", | |
| 1353 | + | "webview2-com", | |
| 1353 | 1354 | ] | |
| 1354 | 1355 | ||
| 1355 | 1356 | [[package]] |
| 17 | 17 | ||
| 18 | 18 | [dependencies] | |
| 19 | 19 | # `unstable`: a window holding more than one web view, so the title bar | |
| 20 | − | # and the site sit one above the other (src/frame.rs). | |
| 21 | − | tauri = { version = "2", features = ["tray-icon", "image-png", "unstable"] } | |
| 20 | + | # and the site sit one above the other (src/frame.rs). `devtools`: the web | |
| 21 | + | # inspector in release builds too, which the Developer tools setting turns | |
| 22 | + | # on (src/devtools.rs). On macOS it reaches it through WebKit's private | |
| 23 | + | # API, which the Mac App Store refuses; g1t ships from g1t.sh, not there. | |
| 24 | + | tauri = { version = "2", features = ["tray-icon", "image-png", "unstable", "devtools"] } | |
| 22 | 25 | # Updates: a signed manifest at g1t.sh/downloads/desktop/latest.json. | |
| 23 | 26 | tauri-plugin-updater = "2" | |
| 24 | 27 | # Native notifications, from the site's live feed. | |
| ⋯ | |||
| 51 | 54 | [target.'cfg(target_os = "linux")'.dependencies] | |
| 52 | 55 | gtk = "0.18" | |
| 53 | 56 | ||
| 57 | + | # Turning WebView2's own inspector on and off while g1t runs | |
| 58 | + | # (src/devtools.rs); the same WebView2 bindings that Tauri brings. | |
| 59 | + | [target.'cfg(windows)'.dependencies] | |
| 60 | + | webview2-com = "0.39" | |
| 61 | + | ||
| 54 | 62 | [profile.release] | |
| 55 | 63 | strip = "debuginfo" | |
| 56 | 64 | lto = "thin" | |
| 29 | 29 | "runner_register", | |
| 30 | 30 | "runner_control", | |
| 31 | 31 | "site_ready", | |
| 32 | + | "open_devtools", | |
| 32 | 33 | ]))) | |
| 33 | 34 | .expect("the app's build could not be prepared"); | |
| 34 | 35 | } |
| 160 | 160 | report(); | |
| 161 | 161 | }, | |
| 162 | 162 | }), | |
| 163 | + | // The web inspector for this page, while Developer tools is on in the | |
| 164 | + | // app's settings (src/devtools.rs); rejects with where to turn it on | |
| 165 | + | // while it is off. | |
| 166 | + | devtools: Object.freeze({ | |
| 167 | + | open: function () { | |
| 168 | + | return invoke("open_devtools"); | |
| 169 | + | }, | |
| 170 | + | }), | |
| 163 | 171 | // The title bar's search, in its middle: the site hands it a label | |
| 164 | 172 | // ("Search Flagon") and what opens its search. Returns what takes it away. | |
| 165 | 173 | setSearch: function (label, open) { | |
| ⋯ | |||
| 271 | 279 | return typeof open === "function" ? open.call(window, href, target, features) : null; | |
| 272 | 280 | }; | |
| 273 | 281 | ||
| 282 | + | // F12 opens the web inspector, or closes it, while Developer tools is on; | |
| 283 | + | // the View menu's row has Ctrl+Shift+I (⌥⌘I on macOS). With it off, or | |
| 284 | + | // on a page that isn't the site's, the app says no and the key is the | |
| 285 | + | // page's as ever. | |
| 286 | + | document.addEventListener( | |
| 287 | + | "keydown", | |
| 288 | + | function (event) { | |
| 289 | + | if (event.key !== "F12" || event.ctrlKey || event.metaKey || event.altKey || event.shiftKey) return; | |
| 290 | + | invoke("open_devtools", { toggle: true }).catch(function () {}); | |
| 291 | + | }, | |
| 292 | + | true, | |
| 293 | + | ); | |
| 294 | + | ||
| 274 | 295 | // ── The title bar ───────────────────────────────────────────────────── | |
| 275 | 296 | // What the bar above the page shows of it: the title, the search the | |
| 276 | 297 | // site handed over (`setSearch`), whether there is a page to go back or | |
| 13 | 13 | use url::Url; | |
| 14 | 14 | ||
| 15 | 15 | use crate::settings::{self, Settings}; | |
| 16 | − | use crate::{badge, config, frame, runner, updates}; | |
| 16 | + | use crate::{badge, config, devtools, frame, runner, updates}; | |
| 17 | 17 | ||
| 18 | 18 | const SCRIPT: &str = include_str!("bridge.js"); | |
| 19 | 19 | ||
| ⋯ | |||
| 259 | 259 | Ok(()) | |
| 260 | 260 | } | |
| 261 | 261 | ||
| 262 | + | /// The web inspector for the page asking, the site's in the window: | |
| 263 | + | /// opened, or with `toggle` opened or closed (F12, from bridge.js). Only | |
| 264 | + | /// while Developer tools is on in settings (devtools.rs); otherwise an | |
| 265 | + | /// error that says where to turn it on. | |
| 266 | + | #[tauri::command] | |
| 267 | + | pub fn open_devtools(app: AppHandle, webview: Webview, toggle: Option<bool>) -> Result<(), String> { | |
| 268 | + | from_site_page(&app, &webview)?; | |
| 269 | + | if toggle == Some(true) { devtools::toggle(&app) } else { devtools::open(&app) } | |
| 270 | + | } | |
| 271 | + | ||
| 262 | 272 | /// What the settings show. | |
| 263 | 273 | #[tauri::command] | |
| 264 | 274 | pub fn get_settings(app: AppHandle, webview: Webview) -> Result<settings::View, String> { | |
| 182 | 182 | "allow-restart", | |
| 183 | 183 | "allow-restart-to-update", | |
| 184 | 184 | "allow-change-server", | |
| 185 | + | // The web inspector, which answers only while Developer tools is on. | |
| 186 | + | "allow-open-devtools", | |
| 185 | 187 | ]; | |
| 186 | 188 | ||
| 187 | 189 | /// What GitHub's pages may call while someone signs in: telling the title bar their title. |
| 1 | + | //! The web inspector, for the site's page in the window: off unless | |
| 2 | + | //! someone turns on Developer tools in settings, for when a page goes | |
| 3 | + | //! wrong and they want its console. With it on, View › Developer tools | |
| 4 | + | //! (Ctrl+Shift+I, or ⌥⌘I on macOS), F12 on the page (bridge.js), and the | |
| 5 | + | //! site's own `g1tDesktop.devtools.open()` open it; with it off, none of | |
| 6 | + | //! them do anything. | |
| 7 | + | //! | |
| 8 | + | //! Release builds have the inspector only through Tauri's `devtools` | |
| 9 | + | //! feature (Cargo.toml). Whether a web view has it at all is decided when | |
| 10 | + | //! the web view is made. On Windows, WebView2 lets it change while running, | |
| 11 | + | //! its own F12, Ctrl+Shift+I and Inspect with it, so the setting takes | |
| 12 | + | //! effect at once. WebKit, on macOS and Linux, keeps what it was made with, | |
| 13 | + | //! and its Inspect Element with it, until g1t restarts; the settings then | |
| 14 | + | //! offer the restart, as they do for the title bar. | |
| 15 | + | ||
| 16 | + | use std::sync::Mutex; | |
| 17 | + | ||
| 18 | + | use tauri::menu::MenuItem; | |
| 19 | + | use tauri::{AppHandle, Webview, Wry}; | |
| 20 | + | ||
| 21 | + | use crate::config; | |
| 22 | + | ||
| 23 | + | /// Whether the setting is on now. | |
| 24 | + | pub fn on(app: &AppHandle) -> bool { | |
| 25 | + | config::settings(app).developer_tools | |
| 26 | + | } | |
| 27 | + | ||
| 28 | + | /// Whether the site's web view was made with the inspector, which WebKit | |
| 29 | + | /// keeps until g1t restarts: decided once, as it is made. | |
| 30 | + | static STARTED: std::sync::OnceLock<bool> = std::sync::OnceLock::new(); | |
| 31 | + | ||
| 32 | + | /// Whether this run's site web view has the inspector. | |
| 33 | + | pub fn started(app: &AppHandle) -> bool { | |
| 34 | + | *STARTED.get_or_init(|| on(app)) | |
| 35 | + | } | |
| 36 | + | ||
| 37 | + | /// The setting differs from what the site's web view was made with, and | |
| 38 | + | /// only a restart changes that: never on Windows, where it changes at once. | |
| 39 | + | pub fn changed(app: &AppHandle) -> bool { | |
| 40 | + | !cfg!(windows) && started(app) != on(app) | |
| 41 | + | } | |
| 42 | + | ||
| 43 | + | /// The menu's Developer tools rows, kept so a change of the setting can | |
| 44 | + | /// turn them on or off; a row that is off has no shortcut either. | |
| 45 | + | static ITEMS: Mutex<Vec<MenuItem<Wry>>> = Mutex::new(Vec::new()); | |
| 46 | + | ||
| 47 | + | /// The menu row, with its shortcut: Ctrl+Shift+I, or ⌥⌘I on macOS. | |
| 48 | + | pub fn item(app: &AppHandle) -> tauri::Result<MenuItem<Wry>> { | |
| 49 | + | let shortcut = if cfg!(target_os = "macos") { "Cmd+Alt+I" } else { "Ctrl+Shift+I" }; | |
| 50 | + | let item = MenuItem::with_id(app, "devtools", "Developer tools", on(app), Some(shortcut))?; | |
| 51 | + | ITEMS.lock().unwrap().push(item.clone()); | |
| 52 | + | Ok(item) | |
| 53 | + | } | |
| 54 | + | ||
| 55 | + | /// Whether the inspector opens now: the setting is on, and the site's web | |
| 56 | + | /// view was made with it, or is WebView2's, which takes it at once. | |
| 57 | + | pub fn usable(app: &AppHandle) -> bool { | |
| 58 | + | on(app) && !changed(app) | |
| 59 | + | } | |
| 60 | + | ||
| 61 | + | /// The setting changed: the menu rows follow it, and so does WebView2's | |
| 62 | + | /// own way in. Turned off, an inspector that is open closes where the | |
| 63 | + | /// system can close it (not on Windows, whose WebView2 has no way to). | |
| 64 | + | pub fn setting_changed(app: &AppHandle) { | |
| 65 | + | let on = on(app); | |
| 66 | + | for item in ITEMS.lock().unwrap().iter() { | |
| 67 | + | let _ = item.set_enabled(usable(app)); | |
| 68 | + | } | |
| 69 | + | allow(app, on); | |
| 70 | + | if !on && let Some(site) = crate::frame::site(app) { | |
| 71 | + | site.close_devtools(); | |
| 72 | + | } | |
| 73 | + | } | |
| 74 | + | ||
| 75 | + | /// Lets WebView2 open the inspector, from its own F12, Ctrl+Shift+I and | |
| 76 | + | /// Inspect as well as when g1t asks, or not. WebKit keeps what its web | |
| 77 | + | /// view was made with. | |
| 78 | + | #[cfg(windows)] | |
| 79 | + | pub fn allow(app: &AppHandle, on: bool) { | |
| 80 | + | let Some(site) = crate::frame::site(app) else { return }; | |
| 81 | + | let _ = site.with_webview(move |webview| { | |
| 82 | + | // SAFETY: the controller is the site's, alive while the web view is, | |
| 83 | + | // and this runs on the thread that owns it. | |
| 84 | + | let _ = unsafe { webview.controller().CoreWebView2().and_then(|core| core.Settings()).and_then(|settings| settings.SetAreDevToolsEnabled(on)) }; | |
| 85 | + | }); | |
| 86 | + | } | |
| 87 | + | ||
| 88 | + | #[cfg(not(windows))] | |
| 89 | + | pub fn allow(_app: &AppHandle, _on: bool) {} | |
| 90 | + | ||
| 91 | + | /// The site's web view, while the inspector opens: an error otherwise, | |
| 92 | + | /// which the site's `devtools.open()` sees. | |
| 93 | + | fn site_if_on(app: &AppHandle) -> Result<Webview, String> { | |
| 94 | + | if !on(app) { | |
| 95 | + | return Err("Developer tools are off. Turn them on in the app's settings, under Window.".into()); | |
| 96 | + | } | |
| 97 | + | if changed(app) { | |
| 98 | + | return Err("Developer tools turn on when g1t restarts.".into()); | |
| 99 | + | } | |
| 100 | + | crate::frame::site(app).ok_or_else(|| "g1t's page is not open".into()) | |
| 101 | + | } | |
| 102 | + | ||
| 103 | + | /// Opens the inspector for the site's page. | |
| 104 | + | pub fn open(app: &AppHandle) -> Result<(), String> { | |
| 105 | + | site_if_on(app)?.open_devtools(); | |
| 106 | + | Ok(()) | |
| 107 | + | } | |
| 108 | + | ||
| 109 | + | /// The menu row and the shortcuts: opens the inspector, or closes it if it | |
| 110 | + | /// is open. WebView2 can't tell whether it is, nor close it, so on | |
| 111 | + | /// Windows it only opens, and the inspector's own window closes it. | |
| 112 | + | pub fn toggle(app: &AppHandle) -> Result<(), String> { | |
| 113 | + | let site = site_if_on(app)?; | |
| 114 | + | if site.is_devtools_open() { site.close_devtools() } else { site.open_devtools() } | |
| 115 | + | Ok(()) | |
| 116 | + | } |
| 88 | 88 | let top = if custom { HEIGHT } else { 0.0 }; | |
| 89 | 89 | if custom { | |
| 90 | 90 | window.add_child( | |
| 91 | − | WebviewBuilder::new(BAR, WebviewUrl::App("titlebar.html".into())).background_color(RAIL).on_navigation(config::is_launcher), | |
| 91 | + | // Never an inspector: the devtools feature gives every web view | |
| 92 | + | // one unless it is told otherwise (src/devtools.rs). | |
| 93 | + | WebviewBuilder::new(BAR, WebviewUrl::App("titlebar.html".into())).background_color(RAIL).on_navigation(config::is_launcher).devtools(false), | |
| 92 | 94 | LogicalPosition::new(0.0, 0.0), | |
| 93 | 95 | LogicalSize::new(size.width, HEIGHT), | |
| 94 | 96 | )?; |
| 16 | 16 | mod badge; | |
| 17 | 17 | mod bridge; | |
| 18 | 18 | mod config; | |
| 19 | + | mod devtools; | |
| 19 | 20 | mod frame; | |
| 20 | 21 | mod links; | |
| 21 | 22 | mod menu; | |
| ⋯ | |||
| 88 | 89 | bridge::runner_register, | |
| 89 | 90 | bridge::runner_control, | |
| 90 | 91 | bridge::site_ready, | |
| 92 | + | bridge::open_devtools, | |
| 91 | 93 | ]) | |
| 92 | 94 | .on_menu_event(|app, event| menu::handle(app, event.id().as_ref())) | |
| 93 | 95 | .setup(|app| { | |
| ⋯ | |||
| 101 | 103 | // The site's web view: the launcher first, which goes on to the server. | |
| 102 | 104 | let site = WebviewBuilder::new(frame::SITE, WebviewUrl::App("index.html".into())) | |
| 103 | 105 | .initialization_script(bridge::script(custom)) | |
| 106 | + | // The web inspector, as the Developer tools setting has it now (devtools.rs). | |
| 107 | + | .devtools(devtools::started(&handle)) | |
| 104 | 108 | .on_navigation({ | |
| 105 | 109 | let handle = handle.clone(); | |
| 106 | 110 | move |url| navigation::allowed(&handle, url) | |
| 48 | 48 | &item("zoom-out", "Zoom out", Some("CmdOrCtrl+-"))?, | |
| 49 | 49 | &separator()?, | |
| 50 | 50 | &PredefinedMenuItem::fullscreen(app, None)?, | |
| 51 | + | &separator()?, | |
| 52 | + | &crate::devtools::item(app)?, | |
| 51 | 53 | ], | |
| 52 | 54 | )?; | |
| 53 | 55 | let go = Submenu::with_items( | |
| ⋯ | |||
| 156 | 158 | "zoom-in" => settings::zoom(app, 1), | |
| 157 | 159 | "zoom-out" => settings::zoom(app, -1), | |
| 158 | 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 | + | } | |
| 159 | 165 | "back" => eval(app, "history.back()"), | |
| 160 | 166 | "forward" => eval(app, "history.forward()"), | |
| 161 | 167 | "home" => links::go(app, "/"), | |
| 1 | 1 | //! The app's own settings: how it starts, what closing the window does, | |
| 2 | − | //! how it tells the person something arrived, its title bar, zoom, | |
| 3 | − | //! updates, and the runner. They open as a dialog over the site, which the | |
| 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 | |
| 4 | 4 | //! site's page draws (apps/web components/desktop-settings.tsx), from the | |
| 5 | 5 | //! title bar, the menu, the tray, or the site's account menu. While the | |
| 6 | 6 | //! window shows no page of the site's (the launcher, the offline page, | |
| ⋯ | |||
| 48 | 48 | pub system_title_bar: bool, | |
| 49 | 49 | /// The site's zoom, 1 for as it is. | |
| 50 | 50 | pub zoom: f64, | |
| 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, | |
| 51 | 55 | /// Updates download by themselves; a person can still check from the menu. | |
| 52 | 56 | /// The runner's own updates follow it too (runner/mod.rs). | |
| 53 | 57 | pub auto_update: bool, | |
| ⋯ | |||
| 113 | 117 | badge: true, | |
| 114 | 118 | system_title_bar: false, | |
| 115 | 119 | zoom: 1.0, | |
| 120 | + | developer_tools: false, | |
| 116 | 121 | auto_update: true, | |
| 117 | 122 | runner: true, | |
| 118 | 123 | runner_backend: crate::runner::Backend::Sandboxed, | |
| ⋯ | |||
| 147 | 152 | pub title_bar_changed: bool, | |
| 148 | 153 | /// Whether this window draws g1t's own title bar, which the page then draws (settings.html). | |
| 149 | 154 | pub custom_frame: bool, | |
| 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, | |
| 150 | 158 | /// The runner's state, diagnosis and recent log. | |
| 151 | 159 | pub runner_view: crate::runner::View, | |
| 152 | 160 | } | |
| ⋯ | |||
| 162 | 170 | checked_at: crate::updates::checked_at(app), | |
| 163 | 171 | title_bar_changed: crate::frame::started(app) != crate::frame::custom(app), | |
| 164 | 172 | custom_frame: crate::frame::started(app), | |
| 173 | + | developer_tools_changed: crate::devtools::changed(app), | |
| 165 | 174 | runner_view: crate::runner::view(app), | |
| 166 | 175 | } | |
| 167 | 176 | } | |
| ⋯ | |||
| 177 | 186 | if before.badge != settings.badge { | |
| 178 | 187 | crate::badge::refresh(app); | |
| 179 | 188 | } | |
| 189 | + | if before.developer_tools != settings.developer_tools { | |
| 190 | + | crate::devtools::setting_changed(app); | |
| 191 | + | } | |
| 180 | 192 | let runner = |s: &Settings| (s.runner, s.runner_backend, s.runner_workspace.clone(), s.runner_capabilities.clone()); | |
| 181 | 193 | if runner(&before) != runner(&settings) { | |
| 182 | 194 | crate::runner::settings_changed(app); | |
| ⋯ | |||
| 340 | 352 | ||
| 341 | 353 | fn build(app: &AppHandle, window: &Window) { | |
| 342 | 354 | let custom = crate::frame::started(app); | |
| 355 | + | // Never an inspector: the devtools feature gives every web view one | |
| 356 | + | // unless it is told otherwise (src/devtools.rs). | |
| 343 | 357 | let builder = WebviewWindowBuilder::new(app, window.label, WebviewUrl::App(window.page.into())) | |
| 344 | − | .title(window.title); | |
| 358 | + | .title(window.title) | |
| 359 | + | .devtools(false); | |
| 345 | 360 | let built = crate::frame::dialog(builder, custom) | |
| 346 | 361 | .inner_size(window.size.0, window.size.1) | |
| 347 | 362 | .min_inner_size(440.0, 420.0) | |
| ⋯ | |||
| 369 | 384 | assert!(!settings.close_to_tray); | |
| 370 | 385 | assert!(settings.notifications); | |
| 371 | 386 | assert_eq!(settings.zoom, 1.0); | |
| 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); | |
| 372 | 391 | // The runner is on, and sandboxed, for someone who never chose. | |
| 373 | 392 | assert!(settings.runner); | |
| 374 | 393 | assert_eq!(settings.runner_backend, crate::runner::Backend::Sandboxed); | |
| 21 | 21 | computer runs the workspace's `runs-on: self-hosted` jobs while g1t is | |
| 22 | 22 | open, with nothing to install or register. See [the runner](#the-runner). | |
| 23 | 23 | - **Settings of its own**: opening when you sign in to your computer, | |
| 24 | − | what closing the window does, notifications, the badge, zoom, updates | |
| 25 | − | and the runner. | |
| 24 | + | what closing the window does, notifications, the badge, zoom, | |
| 25 | + | developer tools, updates and the runner. | |
| 26 | 26 | ||
| 27 | 27 | Get it at [g1t.sh/download](https://g1t.sh/download), or from your account | |
| 28 | 28 | menu, **Get the desktop app**. The page opens on the system you visit it | |
| ⋯ | |||
| 134 | 134 | | --- | --- | | |
| 135 | 135 | | **File** (**g1t** on macOS) | **Settings** (Ctrl+, or ⌘,), **Use another g1t…**, **Close window** (Ctrl+W), **Quit g1t** (Ctrl+Q or ⌘Q) | | |
| 136 | 136 | | **Edit** | Undo, redo, cut, copy, paste and select all | | |
| 137 | − | | **View** | **Reload** (Ctrl+R), **Actual size** (Ctrl+0), **Zoom in** (Ctrl+=), **Zoom out** (Ctrl+-), full screen | | |
| 137 | + | | **View** | **Reload** (Ctrl+R), **Actual size** (Ctrl+0), **Zoom in** (Ctrl+=), **Zoom out** (Ctrl+-), full screen, **Developer tools** (Ctrl+Shift+I, or ⌥⌘I), once [turned on](#developer-tools) | | |
| 138 | 138 | | **Go** | **Back** (Alt+Left, or ⌘[), **Forward** (Alt+Right, or ⌘]), **Home**, **Notifications** | | |
| 139 | 139 | | **Window** | Minimize and maximize | | |
| 140 | 140 | | **Help** | **Desktop app guide** and **g1t.sh status**, in your browser; **Check for updates…**; **Restart to update**, once one is downloaded; **About g1t** | | |
| ⋯ | |||
| 168 | 168 | | **Show a badge on the icon** | On | The count, or the dot on Windows. Off, the icon stays clean; the tray's hover text still counts. | | |
| 169 | 169 | | **Use the system's title bar** | Off | The system's title bar in place of [g1t's own](#the-title-bar), with the menu bar under it on Windows and Linux. Takes effect when g1t restarts. | | |
| 170 | 170 | | **Zoom** | 100% | How large g1t is drawn, from 67% to 200%. The View menu changes it too. | | |
| 171 | + | | **Developer tools** | Off | F12 or Ctrl+Shift+I (⌥⌘I on macOS), and **Developer tools** in the View menu, open the web inspector for the page. See [Developer tools](#developer-tools). | | |
| 171 | 172 | | **Download updates by themselves** | On | Off, the app never checks on its own; **Check for updates** still does. The runner's updates follow it too: off, only **Check for runner updates** looks for a newer runner. | | |
| 172 | 173 | ||
| 173 | 174 | **Updates** also shows the app's version and whether it is up to date, | |
| ⋯ | |||
| 182 | 183 | changes, the answer comes, or, after 20 seconds with no change, it is | |
| 183 | 184 | yours to press again. If the app couldn't do it, the settings say why. | |
| 184 | 185 | ||
| 186 | + | ### Developer tools | |
| 187 | + | ||
| 188 | + | When a page goes wrong, such as one that says *Something went wrong*, the | |
| 189 | + | web inspector shows why: the page's console, its network requests and its | |
| 190 | + | code. It is off unless you turn on **Developer tools** in the settings' | |
| 191 | + | **Window** section. Then any of these opens it for the page in the window: | |
| 192 | + | ||
| 193 | + | - **F12**, on the page. | |
| 194 | + | - **Ctrl+Shift+I** on Windows and Linux, **⌥⌘I** (Cmd+Option+I) on macOS. | |
| 195 | + | - **Developer tools** in the View menu. | |
| 196 | + | ||
| 197 | + | On macOS and Linux the same keys, or the menu row, close it again; on | |
| 198 | + | Windows, close the inspector's own window. With the setting off, none of | |
| 199 | + | them open it, and the View menu's row is greyed out. On Windows the change | |
| 200 | + | takes effect at once; on macOS and Linux it takes effect when g1t | |
| 201 | + | restarts, and the settings offer **Restart now**. | |
| 202 | + | ||
| 203 | + | g1t's pages can open it too, from `window.g1tDesktop.devtools.open()`, | |
| 204 | + | which works only while the setting is on and only for g1t's own pages. | |
| 205 | + | ||
| 185 | 206 | ## The runner | |
| 186 | 207 | ||
| 187 | 208 | The app runs a [self-hosted runner](/guides/self-hosted-runners/) for | |
| 309 | 309 | const words = platformWords(view.platform); | |
| 310 | 310 | const zoomId = useId(); | |
| 311 | 311 | const restart = useBusy(String(view.titleBarChanged), fail); | |
| 312 | + | const restartForTools = useBusy(String(view.developerToolsChanged), fail); | |
| 312 | 313 | return ( | |
| 313 | 314 | <> | |
| 314 | 315 | <SwitchCard title="Use the system's title bar" checked={view.systemTitleBar} onCheckedChange={(on) => change({ systemTitleBar: on })}> | |
| ⋯ | |||
| 343 | 344 | > | |
| 344 | 345 | {words.zoom} | |
| 345 | 346 | </SettingCard> | |
| 347 | + | {/* An app from before Developer tools has no such setting to change. */} | |
| 348 | + | {typeof view.developerTools === "boolean" && ( | |
| 349 | + | <SwitchCard title="Developer tools" checked={view.developerTools} onCheckedChange={(on) => change({ developerTools: on })}> | |
| 350 | + | {words.developerTools} | |
| 351 | + | </SwitchCard> | |
| 352 | + | )} | |
| 353 | + | {view.developerToolsChanged && !view.titleBarChanged && ( | |
| 354 | + | <SettingCard | |
| 355 | + | title={view.developerTools ? "Restart to turn on developer tools" : "Restart to turn off developer tools"} | |
| 356 | + | control={ | |
| 357 | + | <Button size="sm" variant="accent" busy={restartForTools.busy} onClick={() => restartForTools.run(() => desktopSettings()?.restart())}> | |
| 358 | + | Restart now | |
| 359 | + | </Button> | |
| 360 | + | } | |
| 361 | + | > | |
| 362 | + | g1t opens again where you were. | |
| 363 | + | </SettingCard> | |
| 364 | + | )} | |
| 346 | 365 | </> | |
| 347 | 366 | ); | |
| 348 | 367 | } | |
| 12 | 12 | badge: true, | |
| 13 | 13 | systemTitleBar: false, | |
| 14 | 14 | zoom: 1.1, | |
| 15 | + | developerTools: false, | |
| 15 | 16 | autoUpdate: true, | |
| 16 | 17 | runner: true, | |
| 17 | 18 | runnerBackend: "sandboxed", | |
| ⋯ | |||
| 25 | 26 | checkedAt: null, | |
| 26 | 27 | titleBarChanged: false, | |
| 27 | 28 | customFrame: true, | |
| 29 | + | developerToolsChanged: false, | |
| 28 | 30 | runnerView: {} as DesktopSettingsView["runnerView"], | |
| 29 | 31 | ...over, | |
| 30 | 32 | }; | |
| 25 | 25 | badge: boolean; | |
| 26 | 26 | systemTitleBar: boolean; | |
| 27 | 27 | zoom: number; | |
| 28 | + | /** The web inspector for the page, from F12, Ctrl+Shift+I (⌥⌘I on macOS) and the View menu (src/devtools.rs). */ | |
| 29 | + | developerTools: boolean; | |
| 28 | 30 | autoUpdate: boolean; | |
| 29 | 31 | runner: boolean; | |
| 30 | 32 | runnerBackend: DesktopBackend; | |
| ⋯ | |||
| 45 | 47 | /** The title bar setting differs from the one the window has: a restart applies it. */ | |
| 46 | 48 | titleBarChanged: boolean; | |
| 47 | 49 | customFrame: boolean; | |
| 50 | + | /** The developer tools setting differs from the one the page has, on macOS and Linux: a restart applies it. */ | |
| 51 | + | developerToolsChanged: boolean; | |
| 48 | 52 | runnerView: DesktopRunnerView; | |
| 49 | 53 | }; | |
| 50 | 54 | ||
| ⋯ | |||
| 89 | 93 | "badge", | |
| 90 | 94 | "systemTitleBar", | |
| 91 | 95 | "zoom", | |
| 96 | + | "developerTools", | |
| 92 | 97 | "autoUpdate", | |
| 93 | 98 | "runner", | |
| 94 | 99 | "runnerBackend", | |
| ⋯ | |||
| 139 | 144 | flash: mac ? "Bounce the dock icon" : platform === "windows" ? "Flash the taskbar icon" : "Ask for attention", | |
| 140 | 145 | systemTitleBar: mac ? "In place of g1t's own, which sits under the window's buttons." : "In place of g1t's own, with the menu bar under it.", | |
| 141 | 146 | zoom: mac ? "The View menu changes it too: ⌘+, ⌘− and ⌘0." : "The View menu changes it too: Ctrl+=, Ctrl+− and Ctrl+0.", | |
| 147 | + | developerTools: `F12 or ${mac ? "⌥⌘I" : "Ctrl+Shift+I"} opens the web inspector for the page, with its console, for when something goes wrong. So does Developer tools in the View menu.`, | |
| 142 | 148 | }; | |
| 143 | 149 | } | |
| 144 | 150 | ||
| 94 | 94 | * // diagnosis, who to register for, its token, and its fixes | |
| 95 | 95 | * // (lib/desktop-runner.ts has the details). | |
| 96 | 96 | * runner: { status, offer, register, act, onWanted, onStatus }, | |
| 97 | + | * // The web inspector for this page, while Developer tools is on in the | |
| 98 | + | * // app's settings; rejects, saying where to turn it on, while it is off. | |
| 99 | + | * devtools: { open() { return Promise.resolve() } }, | |
| 97 | 100 | * }; | |
| 98 | 101 | * | |
| 99 | 102 | * The app also opens the site's pages for reading about g1t (pricing, | |
| ⋯ | |||
| 113 | 116 | openSettings?(): void; | |
| 114 | 117 | setSearch?(label: string, open: () => void): () => void; | |
| 115 | 118 | titleBarHeight?: number; | |
| 119 | + | devtools?: { open(): Promise<void> }; | |
| 116 | 120 | runner?: DesktopRunnerBridge; | |
| 117 | 121 | settings?: DesktopSettingsBridge; | |
| 118 | 122 | }; | |