Skip to content
131 linesCodeBlameRaw

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

preparing for cloud work1//! The g1t desktop app: g1t.sh, or a g1t of your own, in a window of its
2//! own. The window loads the live site, so every change to g1t reaches it
3//! the moment it is deployed and nothing here needs a new build for it;
4//! only what is native lives in this program: notifications from the
The desktop app has settings of its own and a menu, and it is for using g1t, not browsing it.5//! system, a badge on the icon, a tray, a menu, `g1t://` links, the server
6//! to use, the app's own settings, and updates that install themselves
7//! (docs.g1t.sh/guides/desktop/).
preparing for cloud work8//!
9//! The site finds the app at `window.g1tDesktop` (bridge.js), the one
10//! contract between the two; what the page asks of it is answered by the
11//! commands in bridge.rs, each of which checks that the page asking is the
12//! configured server's.
13#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
14
15mod badge;
16mod bridge;
17mod config;
The desktop app draws its own title bar, as Slack and Discord do, and the site can call the app again.18mod frame;
preparing for cloud work19mod links;
The desktop app has settings of its own and a menu, and it is for using g1t, not browsing it.20mod menu;
preparing for cloud work21mod navigation;
The desktop app has settings of its own and a menu, and it is for using g1t, not browsing it.22mod settings;
preparing for cloud work23mod tray;
24mod updates;
25
26use tauri::{RunEvent, WebviewUrl, WebviewWindowBuilder, WindowEvent};
27use tauri_plugin_window_state::{StateFlags, WindowExt};
28
29/// The one window's label.
30pub const WINDOW: &str = "main";
31
32fn main() {
33 tauri::Builder::default()
34 // A second launch (a g1t:// link, or the icon clicked again) goes to
35 // the app already running: the link reaches links.rs through the
36 // deep-link plugin, and the window comes forward.
37 .plugin(tauri_plugin_single_instance::init(|app, _args, _cwd| tray::show(app)))
38 .plugin(tauri_plugin_deep_link::init())
39 .plugin(tauri_plugin_notification::init())
40 .plugin(tauri_plugin_opener::init())
41 .plugin(tauri_plugin_updater::Builder::new().build())
The desktop app has settings of its own and a menu, and it is for using g1t, not browsing it.42 // The window's size and place come back in setup, without the plugin
43 // also showing it: at sign-in g1t may start in the tray.
44 .plugin(tauri_plugin_window_state::Builder::new().skip_initial_state(WINDOW).with_denylist(&[settings::SETTINGS]).build())
45 // Opening at sign-in, which settings turn on; the argument tells that start from someone's.
46 .plugin(tauri_plugin_autostart::init(tauri_plugin_autostart::MacosLauncher::LaunchAgent, Some(vec![settings::AT_LOGIN])))
preparing for cloud work47 .manage(config::State::default())
48 .invoke_handler(tauri::generate_handler![
49 bridge::desktop_info,
50 bridge::notify,
51 bridge::set_badge,
52 bridge::open_url,
53 bridge::reachable,
54 bridge::set_server,
55 bridge::check_for_updates,
The desktop app has settings of its own and a menu, and it is for using g1t, not browsing it.56 bridge::open_settings,
57 bridge::get_settings,
58 bridge::save_settings,
59 bridge::set_open_at_login,
60 bridge::change_server,
Checking for updates in the desktop app answers in its settings window, with a restart beside a downloaded version.61 bridge::restart_to_update,
The desktop app draws its own title bar, as Slack and Discord do, and the site can call the app again.62 bridge::restart,
63 bridge::window_control,
64 bridge::window_menu,
preparing for cloud work65 ])
The desktop app has settings of its own and a menu, and it is for using g1t, not browsing it.66 .on_menu_event(|app, event| menu::handle(app, event.id().as_ref()))
preparing for cloud work67 .setup(|app| {
68 let handle = app.handle().clone();
69 config::load(&handle);
70 config::allow_site(&handle)?;
The desktop app has settings of its own and a menu, and it is for using g1t, not browsing it.71 let hidden = settings::starts_hidden(&handle);
72 let prefs = config::settings(&handle);
The desktop app draws its own title bar, as Slack and Discord do, and the site can call the app again.73 let custom = frame::started(&handle);
The desktop app has settings of its own and a menu, and it is for using g1t, not browsing it.74 let builder = WebviewWindowBuilder::new(app, WINDOW, WebviewUrl::App("index.html".into()))
preparing for cloud work75 .title("g1t")
76 .inner_size(1280.0, 840.0)
77 .min_inner_size(720.0, 480.0)
The desktop app has settings of its own and a menu, and it is for using g1t, not browsing it.78 .visible(false)
The desktop app draws its own title bar, as Slack and Discord do, and the site can call the app again.79 .initialization_script(bridge::script(WINDOW, custom))
preparing for cloud work80 .on_navigation({
81 let handle = handle.clone();
82 move |url| navigation::allowed(&handle, url)
83 })
84 .on_page_load({
85 let handle = handle.clone();
86 move |_webview, payload| config::at(&handle, payload.url().clone())
The desktop app has settings of its own and a menu, and it is for using g1t, not browsing it.87 });
88 // macOS has one menu bar, the app's; elsewhere the menu is the window's own.
89 #[cfg(target_os = "macos")]
90 app.set_menu(menu::build(&handle)?)?;
91 #[cfg(not(target_os = "macos"))]
92 let builder = builder.menu(menu::build(&handle)?);
The desktop app draws its own title bar, as Slack and Discord do, and the site can call the app again.93 let window = frame::style(builder, custom).build()?;
The desktop app has settings of its own and a menu, and it is for using g1t, not browsing it.94 // Opened at sign-in, g1t waits in the tray; otherwise the window comes up where it was.
95 let _ = window.restore_state(StateFlags::all() - StateFlags::VISIBLE);
The desktop app draws its own title bar, as Slack and Discord do, and the site can call the app again.96 #[cfg(not(target_os = "macos"))]
97 frame::menu_bar(&window, custom);
The desktop app has settings of its own and a menu, and it is for using g1t, not browsing it.98 if !hidden {
99 let _ = window.show();
100 }
101 if prefs.zoom != 1.0 {
102 let _ = window.set_zoom(prefs.zoom);
103 }
104 // Closing the window keeps g1t running, for notifications, unless settings say it quits.
preparing for cloud work105 window.on_window_event({
106 let handle = handle.clone();
107 move |event| {
108 if let WindowEvent::CloseRequested { api, .. } = event {
109 api.prevent_close();
The desktop app has settings of its own and a menu, and it is for using g1t, not browsing it.110 tray::close(&handle);
preparing for cloud work111 }
112 }
113 });
114 tray::install(&handle)?;
115 links::install(&handle);
116 updates::schedule(handle);
117 Ok(())
118 })
119 .build(tauri::generate_context!())
120 .expect("g1t could not start")
121 .run(|app, event| match event {
122 // The dock icon, clicked while the window is hidden.
123 #[cfg(target_os = "macos")]
124 RunEvent::Reopen { .. } => tray::show(app),
125 // No window open is not a reason to quit: the tray is still there.
126 RunEvent::ExitRequested { api, code: None, .. } => api.prevent_exit(),
127 _ => {
128 let _ = app;
129 }
130 });
131}