Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

g1t.sh
132 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();
The desktop app runs under tauri dev: the launcher served from the CLI's localhost stays in the window.69 config::note_dev_server(&handle);
preparing for cloud work70 config::load(&handle);
71 config::allow_site(&handle)?;
The desktop app has settings of its own and a menu, and it is for using g1t, not browsing it.72 let hidden = settings::starts_hidden(&handle);
73 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.74 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.75 let builder = WebviewWindowBuilder::new(app, WINDOW, WebviewUrl::App("index.html".into()))
preparing for cloud work76 .title("g1t")
77 .inner_size(1280.0, 840.0)
78 .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.79 .visible(false)
The desktop app draws its own title bar, as Slack and Discord do, and the site can call the app again.80 .initialization_script(bridge::script(WINDOW, custom))
preparing for cloud work81 .on_navigation({
82 let handle = handle.clone();
83 move |url| navigation::allowed(&handle, url)
84 })
85 .on_page_load({
86 let handle = handle.clone();
87 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.88 });
89 // macOS has one menu bar, the app's; elsewhere the menu is the window's own.
90 #[cfg(target_os = "macos")]
91 app.set_menu(menu::build(&handle)?)?;
92 #[cfg(not(target_os = "macos"))]
93 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.94 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.95 // Opened at sign-in, g1t waits in the tray; otherwise the window comes up where it was.
96 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.97 #[cfg(not(target_os = "macos"))]
98 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.99 if !hidden {
100 let _ = window.show();
101 }
102 if prefs.zoom != 1.0 {
103 let _ = window.set_zoom(prefs.zoom);
104 }
105 // Closing the window keeps g1t running, for notifications, unless settings say it quits.
preparing for cloud work106 window.on_window_event({
107 let handle = handle.clone();
108 move |event| {
109 if let WindowEvent::CloseRequested { api, .. } = event {
110 api.prevent_close();
The desktop app has settings of its own and a menu, and it is for using g1t, not browsing it.111 tray::close(&handle);
preparing for cloud work112 }
113 }
114 });
115 tray::install(&handle)?;
116 links::install(&handle);
117 updates::schedule(handle);
118 Ok(())
119 })
120 .build(tauri::generate_context!())
121 .expect("g1t could not start")
122 .run(|app, event| match event {
123 // The dock icon, clicked while the window is hidden.
124 #[cfg(target_os = "macos")]
125 RunEvent::Reopen { .. } => tray::show(app),
126 // No window open is not a reason to quit: the tray is still there.
127 RunEvent::ExitRequested { api, code: None, .. } => api.prevent_exit(),
128 _ => {
129 let _ = app;
130 }
131 });
132}