Skip to content

Commit

Merge branch 'worktree-agent-a6b141eb3a7e9cf01'

syntaqxcommitted Parents4ce10ebfe618fcBrowse files
18 files+254−120/18 viewed
+1−1
450450
451451 // Every setting, as the app has it now (the settings window changes the
452452 // 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"];
454454 function save() {
455455 invoke("get_settings")
456456 .then(function (fresh) {
+1−1
88 (function () {
99 var SWITCHES = ["startHidden", "closeToTray", "notifications", "flash", "badge", "systemTitleBar", "autoUpdate"];
1010 // 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"]);
1212 var current = null;
1313
1414 function byId(id) {
+1−0
13501350 "tauri-plugin-window-state",
13511351 "tokio",
13521352 "url",
1353+ "webview2-com",
13531354 ]
13541355
13551356 [[package]]
+10−2
1717
1818 [dependencies]
1919 # `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"] }
2225 # Updates: a signed manifest at g1t.sh/downloads/desktop/latest.json.
2326 tauri-plugin-updater = "2"
2427 # Native notifications, from the site's live feed.
5154 [target.'cfg(target_os = "linux")'.dependencies]
5255 gtk = "0.18"
5356
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+
5462 [profile.release]
5563 strip = "debuginfo"
5664 lto = "thin"
+1−0
2929 "runner_register",
3030 "runner_control",
3131 "site_ready",
32+ "open_devtools",
3233 ])))
3334 .expect("the app's build could not be prepared");
3435 }
+21−0
160160 report();
161161 },
162162 }),
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+ }),
163171 // The title bar's search, in its middle: the site hands it a label
164172 // ("Search Flagon") and what opens its search. Returns what takes it away.
165173 setSearch: function (label, open) {
271279 return typeof open === "function" ? open.call(window, href, target, features) : null;
272280 };
273281
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+
274295 // ── The title bar ─────────────────────────────────────────────────────
275296 // What the bar above the page shows of it: the title, the search the
276297 // site handed over (`setSearch`), whether there is a page to go back or
+11−1
1313 use url::Url;
1414
1515 use crate::settings::{self, Settings};
16−use crate::{badge, config, frame, runner, updates};
16+use crate::{badge, config, devtools, frame, runner, updates};
1717
1818 const SCRIPT: &str = include_str!("bridge.js");
1919
259259 Ok(())
260260 }
261261
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+
262272 /// What the settings show.
263273 #[tauri::command]
264274 pub fn get_settings(app: AppHandle, webview: Webview) -> Result<settings::View, String> {
+2−0
182182 "allow-restart",
183183 "allow-restart-to-update",
184184 "allow-change-server",
185+ // The web inspector, which answers only while Developer tools is on.
186+ "allow-open-devtools",
185187 ];
186188
187189 /// What GitHub's pages may call while someone signs in: telling the title bar their title.
+116−0
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+}
+3−1
8888 let top = if custom { HEIGHT } else { 0.0 };
8989 if custom {
9090 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),
9294 LogicalPosition::new(0.0, 0.0),
9395 LogicalSize::new(size.width, HEIGHT),
9496 )?;
+4−0
1616 mod badge;
1717 mod bridge;
1818 mod config;
19+mod devtools;
1920 mod frame;
2021 mod links;
2122 mod menu;
8889 bridge::runner_register,
8990 bridge::runner_control,
9091 bridge::site_ready,
92+ bridge::open_devtools,
9193 ])
9294 .on_menu_event(|app, event| menu::handle(app, event.id().as_ref()))
9395 .setup(|app| {
101103 // The site's web view: the launcher first, which goes on to the server.
102104 let site = WebviewBuilder::new(frame::SITE, WebviewUrl::App("index.html".into()))
103105 .initialization_script(bridge::script(custom))
106+ // The web inspector, as the Developer tools setting has it now (devtools.rs).
107+ .devtools(devtools::started(&handle))
104108 .on_navigation({
105109 let handle = handle.clone();
106110 move |url| navigation::allowed(&handle, url)
+6−0
4848 &item("zoom-out", "Zoom out", Some("CmdOrCtrl+-"))?,
4949 &separator()?,
5050 &PredefinedMenuItem::fullscreen(app, None)?,
51+ &separator()?,
52+ &crate::devtools::item(app)?,
5153 ],
5254 )?;
5355 let go = Submenu::with_items(
156158 "zoom-in" => settings::zoom(app, 1),
157159 "zoom-out" => settings::zoom(app, -1),
158160 "zoom-reset" => settings::zoom(app, 0),
161+ // Off with the setting, as the row is.
162+ "devtools" => {
163+ let _ = crate::devtools::toggle(app);
164+ }
159165 "back" => eval(app, "history.back()"),
160166 "forward" => eval(app, "history.forward()"),
161167 "home" => links::go(app, "/"),
+22−3
11 //! 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
44 //! site's page draws (apps/web components/desktop-settings.tsx), from the
55 //! title bar, the menu, the tray, or the site's account menu. While the
66 //! window shows no page of the site's (the launcher, the offline page,
4848 pub system_title_bar: bool,
4949 /// The site's zoom, 1 for as it is.
5050 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,
5155 /// Updates download by themselves; a person can still check from the menu.
5256 /// The runner's own updates follow it too (runner/mod.rs).
5357 pub auto_update: bool,
113117 badge: true,
114118 system_title_bar: false,
115119 zoom: 1.0,
120+ developer_tools: false,
116121 auto_update: true,
117122 runner: true,
118123 runner_backend: crate::runner::Backend::Sandboxed,
147152 pub title_bar_changed: bool,
148153 /// Whether this window draws g1t's own title bar, which the page then draws (settings.html).
149154 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,
150158 /// The runner's state, diagnosis and recent log.
151159 pub runner_view: crate::runner::View,
152160 }
162170 checked_at: crate::updates::checked_at(app),
163171 title_bar_changed: crate::frame::started(app) != crate::frame::custom(app),
164172 custom_frame: crate::frame::started(app),
173+ developer_tools_changed: crate::devtools::changed(app),
165174 runner_view: crate::runner::view(app),
166175 }
167176 }
177186 if before.badge != settings.badge {
178187 crate::badge::refresh(app);
179188 }
189+ if before.developer_tools != settings.developer_tools {
190+ crate::devtools::setting_changed(app);
191+ }
180192 let runner = |s: &Settings| (s.runner, s.runner_backend, s.runner_workspace.clone(), s.runner_capabilities.clone());
181193 if runner(&before) != runner(&settings) {
182194 crate::runner::settings_changed(app);
340352
341353 fn build(app: &AppHandle, window: &Window) {
342354 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).
343357 let builder = WebviewWindowBuilder::new(app, window.label, WebviewUrl::App(window.page.into()))
344− .title(window.title);
358+ .title(window.title)
359+ .devtools(false);
345360 let built = crate::frame::dialog(builder, custom)
346361 .inner_size(window.size.0, window.size.1)
347362 .min_inner_size(440.0, 420.0)
369384 assert!(!settings.close_to_tray);
370385 assert!(settings.notifications);
371386 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);
372391 // The runner is on, and sandboxed, for someone who never chose.
373392 assert!(settings.runner);
374393 assert_eq!(settings.runner_backend, crate::runner::Backend::Sandboxed);
+24−3
2121 computer runs the workspace's `runs-on: self-hosted` jobs while g1t is
2222 open, with nothing to install or register. See [the runner](#the-runner).
2323 - **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.
2626
2727 Get it at [g1t.sh/download](https://g1t.sh/download), or from your account
2828 menu, **Get the desktop app**. The page opens on the system you visit it
134134 | --- | --- |
135135 | **File** (**g1t** on macOS) | **Settings** (Ctrl+, or ⌘,), **Use another g1t…**, **Close window** (Ctrl+W), **Quit g1t** (Ctrl+Q or ⌘Q) |
136136 | **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) |
138138 | **Go** | **Back** (Alt+Left, or ⌘[), **Forward** (Alt+Right, or ⌘]), **Home**, **Notifications** |
139139 | **Window** | Minimize and maximize |
140140 | **Help** | **Desktop app guide** and **g1t.sh status**, in your browser; **Check for updates…**; **Restart to update**, once one is downloaded; **About g1t** |
168168 | **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. |
169169 | **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. |
170170 | **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). |
171172 | **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. |
172173
173174 **Updates** also shows the app's version and whether it is up to date,
182183 changes, the answer comes, or, after 20 seconds with no change, it is
183184 yours to press again. If the app couldn't do it, the settings say why.
184185
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+
185206 ## The runner
186207
187208 The app runs a [self-hosted runner](/guides/self-hosted-runners/) for
+19−0
309309 const words = platformWords(view.platform);
310310 const zoomId = useId();
311311 const restart = useBusy(String(view.titleBarChanged), fail);
312+ const restartForTools = useBusy(String(view.developerToolsChanged), fail);
312313 return (
313314 <>
314315 <SwitchCard title="Use the system's title bar" checked={view.systemTitleBar} onCheckedChange={(on) => change({ systemTitleBar: on })}>
343344 >
344345 {words.zoom}
345346 </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+ )}
346365 </>
347366 );
348367 }
+2−0
1212 badge: true,
1313 systemTitleBar: false,
1414 zoom: 1.1,
15+ developerTools: false,
1516 autoUpdate: true,
1617 runner: true,
1718 runnerBackend: "sandboxed",
2526 checkedAt: null,
2627 titleBarChanged: false,
2728 customFrame: true,
29+ developerToolsChanged: false,
2830 runnerView: {} as DesktopSettingsView["runnerView"],
2931 ...over,
3032 };
+6−0
2525 badge: boolean;
2626 systemTitleBar: boolean;
2727 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;
2830 autoUpdate: boolean;
2931 runner: boolean;
3032 runnerBackend: DesktopBackend;
4547 /** The title bar setting differs from the one the window has: a restart applies it. */
4648 titleBarChanged: boolean;
4749 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;
4852 runnerView: DesktopRunnerView;
4953 };
5054
8993 "badge",
9094 "systemTitleBar",
9195 "zoom",
96+ "developerTools",
9297 "autoUpdate",
9398 "runner",
9499 "runnerBackend",
139144 flash: mac ? "Bounce the dock icon" : platform === "windows" ? "Flash the taskbar icon" : "Ask for attention",
140145 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.",
141146 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.`,
142148 };
143149 }
144150
+4−0
9494 * // diagnosis, who to register for, its token, and its fixes
9595 * // (lib/desktop-runner.ts has the details).
9696 * 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() } },
97100 * };
98101 *
99102 * The app also opens the site's pages for reading about g1t (pricing,
113116 openSettings?(): void;
114117 setSearch?(label: string, open: () => void): () => void;
115118 titleBarHeight?: number;
119+ devtools?: { open(): Promise<void> };
116120 runner?: DesktopRunnerBridge;
117121 settings?: DesktopSettingsBridge;
118122 };