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
169 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
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r95//! system, a badge on the icon, a tray, a menu, `g1t://` links, the server
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.6//! to use, the app's own settings, updates that install themselves, and a
7//! self-hosted runner for the signed-in person (runner/mod.rs)
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r98//! (docs.g1t.sh/guides/desktop/).
preparing for cloud work9//!
10//! The site finds the app at `window.g1tDesktop` (bridge.js), the one
11//! contract between the two; what the page asks of it is answered by the
12//! commands in bridge.rs, each of which checks that the page asking is the
13//! configured server's.
14#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
15
16mod badge;
17mod bridge;
18mod config;
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r919mod frame;
preparing for cloud work20mod links;
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r921mod menu;
preparing for cloud work22mod navigation;
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.23mod runner;
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r924mod settings;
preparing for cloud work25mod tray;
26mod updates;
27
Merge the desktop app's settings and runner in the main window: the runner is a dot beside the title bar's Settings button with a menu of its own (its state, each kind of job and what it runs, Start and Stop, updates, the log), Settings opens as a dialog in the window with its sections down the side, the separate windows stay only for when the site isn't showing, and every button that asks the app for something shows it is working until something happens.28use tauri::webview::{PageLoadEvent, WebviewBuilder};
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r929use tauri::{RunEvent, WebviewUrl, WindowEvent};
preparing for cloud work30use tauri_plugin_window_state::{StateFlags, WindowExt};
31
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r932/// What the window keeps between launches: its size, place, and whether it
33/// was maximized or full screen; not its frame.
34const KEPT: StateFlags = StateFlags::all().difference(StateFlags::DECORATIONS);
35
preparing for cloud work36/// The one window's label.
37pub const WINDOW: &str = "main";
38
39fn main() {
40 tauri::Builder::default()
41 // A second launch (a g1t:// link, or the icon clicked again) goes to
42 // the app already running: the link reaches links.rs through the
43 // deep-link plugin, and the window comes forward.
44 .plugin(tauri_plugin_single_instance::init(|app, _args, _cwd| tray::show(app)))
45 .plugin(tauri_plugin_deep_link::init())
46 .plugin(tauri_plugin_notification::init())
47 .plugin(tauri_plugin_opener::init())
48 .plugin(tauri_plugin_updater::Builder::new().build())
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r949 // The window's size and place come back in setup, without the plugin
50 // also showing it: at sign-in g1t may start in the tray. Whether it
51 // has the system's frame is the title bar setting's (frame.rs), never
52 // what an earlier run left: kept, it would put the frame back round
53 // g1t's own title bar.
54 .plugin(
55 tauri_plugin_window_state::Builder::new()
56 .with_state_flags(KEPT)
57 .skip_initial_state(WINDOW)
58 .with_denylist(&[settings::SETTINGS])
59 .build(),
60 )
61 // Opening at sign-in, which settings turn on; the argument tells that start from someone's.
62 .plugin(tauri_plugin_autostart::init(tauri_plugin_autostart::MacosLauncher::LaunchAgent, Some(vec![settings::AT_LOGIN])))
preparing for cloud work63 .manage(config::State::default())
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r964 .manage(frame::State::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.65 .manage(runner::Shared::default())
preparing for cloud work66 .invoke_handler(tauri::generate_handler![
67 bridge::desktop_info,
68 bridge::notify,
69 bridge::set_badge,
70 bridge::open_url,
71 bridge::reachable,
72 bridge::set_server,
73 bridge::check_for_updates,
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r974 bridge::open_settings,
75 bridge::get_settings,
76 bridge::save_settings,
77 bridge::set_open_at_login,
78 bridge::change_server,
79 bridge::restart_to_update,
80 bridge::restart,
81 bridge::window_control,
82 bridge::window_menu,
83 bridge::title_bar_page,
84 bridge::title_bar_current,
85 bridge::title_bar_act,
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.86 bridge::runner_status,
87 bridge::runner_offer,
88 bridge::runner_register,
89 bridge::runner_control,
Merge the desktop app's settings and runner in the main window: the runner is a dot beside the title bar's Settings button with a menu of its own (its state, each kind of job and what it runs, Start and Stop, updates, the log), Settings opens as a dialog in the window with its sections down the side, the separate windows stay only for when the site isn't showing, and every button that asks the app for something shows it is working until something happens.90 bridge::site_ready,
preparing for cloud work91 ])
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r992 .on_menu_event(|app, event| menu::handle(app, event.id().as_ref()))
preparing for cloud work93 .setup(|app| {
94 let handle = app.handle().clone();
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r995 config::note_dev_server(&handle);
preparing for cloud work96 config::load(&handle);
97 config::allow_site(&handle)?;
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r998 let hidden = settings::starts_hidden(&handle);
99 let prefs = config::settings(&handle);
100 let custom = frame::started(&handle);
101 // The site's web view: the launcher first, which goes on to the server.
102 let site = WebviewBuilder::new(frame::SITE, WebviewUrl::App("index.html".into()))
103 .initialization_script(bridge::script(custom))
preparing for cloud work104 .on_navigation({
105 let handle = handle.clone();
106 move |url| navigation::allowed(&handle, url)
107 })
108 .on_page_load({
109 let handle = handle.clone();
Merge the desktop app's settings and runner in the main window: the runner is a dot beside the title bar's Settings button with a menu of its own (its state, each kind of job and what it runs, Start and Stop, updates, the log), Settings opens as a dialog in the window with its sections down the side, the separate windows stay only for when the site isn't showing, and every button that asks the app for something shows it is working until something happens.110 move |_webview, payload| config::at(&handle, payload.url().clone(), matches!(payload.event(), PageLoadEvent::Started))
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9111 });
112 let builder = frame::main_window(app, custom)?;
113 // macOS has one menu bar, the app's; elsewhere the menu is the window's own.
114 #[cfg(target_os = "macos")]
115 app.set_menu(menu::build(&handle)?)?;
116 #[cfg(not(target_os = "macos"))]
117 let builder = builder.menu(menu::build(&handle)?);
118 let window = builder.build()?;
119 // Opened at sign-in, g1t waits in the tray; otherwise the window comes up where it was.
120 let _ = window.restore_state(KEPT - StateFlags::VISIBLE);
121 frame::fill(&window, custom, site)?;
122 frame::layout(&window);
123 #[cfg(not(target_os = "macos"))]
124 frame::menu_bar(&window, custom);
125 if !hidden {
126 let _ = window.show();
127 if let Some(site) = frame::site(&handle) {
128 let _ = site.set_focus();
129 }
130 }
131 if let Some(site) = frame::site(&handle).filter(|_| prefs.zoom != 1.0) {
132 let _ = site.set_zoom(prefs.zoom);
133 }
134 // Closing the window keeps g1t running, for notifications, unless
135 // settings say it quits. A new size gives the bar and the site theirs.
preparing for cloud work136 window.on_window_event({
137 let handle = handle.clone();
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9138 let window = window.clone();
139 move |event| match event {
140 WindowEvent::CloseRequested { api, .. } => {
preparing for cloud work141 api.prevent_close();
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9142 tray::close(&handle);
preparing for cloud work143 }
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9144 WindowEvent::Resized(_) | WindowEvent::ScaleFactorChanged { .. } => frame::layout(&window),
145 _ => {}
preparing for cloud work146 }
147 });
148 tray::install(&handle)?;
149 links::install(&handle);
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.150 updates::schedule(handle.clone());
151 // The runner starts on a thread of its own, a few seconds after the window.
152 runner::install(&handle);
preparing for cloud work153 Ok(())
154 })
155 .build(tauri::generate_context!())
156 .expect("g1t could not start")
157 .run(|app, event| match event {
158 // The dock icon, clicked while the window is hidden.
159 #[cfg(target_os = "macos")]
160 RunEvent::Reopen { .. } => tray::show(app),
161 // No window open is not a reason to quit: the tray is still there.
162 RunEvent::ExitRequested { api, code: None, .. } => api.prevent_exit(),
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.163 // However g1t ends, the runner it started ends with it.
164 RunEvent::Exit => runner::shutdown(app),
preparing for cloud work165 _ => {
166 let _ = app;
167 }
168 });
169}

This file's history is long; its oldest lines are credited to the oldest commit read.