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
322 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 desktop bridge: what the site finds at `window.g1tDesktop`, run at
2// the start of every page the window loads (bridge.rs puts it there). The
3// site's lib/notify-client.ts describes the contract from its side; when
4// the bridge is there, notifications go to the system, the badge to the
5// icon, links out to the browser, and g1t:// links arrive as `onNavigate`.
6// Each call is a command in bridge.rs, which only answers the server's
7// pages; on any other page the calls fail quietly.
8(function () {
9 if (window.g1tDesktop) return;
10 var VERSION = "__VERSION__";
11 var PLATFORM = "__PLATFORM__";
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r912 // Whether g1t's own title bar is above this page ("custom"), or the
13 // system's ("system"). The bar is a page of its own (src/frame.rs); this
14 // page only tells it what to show (`report`), and is never under it.
15 var FRAME = "__FRAME__";
16 // The site's pages for reading about g1t, not using it, which open in
17 // the browser (navigation.rs); one ending in "/" is every page under it.
18 var OUTSIDE = "__OUTSIDE__";
preparing for cloud work19
20 function invoke(command, args) {
21 var core = window.__TAURI__ && window.__TAURI__.core;
22 if (core && typeof core.invoke === "function") return core.invoke(command, args || {});
23 var internals = window.__TAURI_INTERNALS__;
24 if (internals && typeof internals.invoke === "function") return internals.invoke(command, args || {});
25 return Promise.reject(new Error("g1t desktop: this page cannot reach the app"));
26 }
27
28 function quietly(promise) {
29 promise.catch(function (error) {
30 console.warn("g1t desktop:", error && error.message ? error.message : error);
31 });
32 }
33
34 var listeners = [];
35 var listening = false;
36 function listen() {
37 if (listening) return;
38 listening = true;
39 var events = window.__TAURI__ && window.__TAURI__.event;
40 if (!events || typeof events.listen !== "function") return;
41 events
42 .listen("g1t-navigate", function (event) {
43 var href = event && event.payload;
44 if (typeof href !== "string") return;
45 listeners.slice().forEach(function (listener) {
46 try {
47 listener(href);
48 } catch (error) {
49 console.warn("g1t desktop: a navigate listener failed", error);
50 }
51 });
52 })
53 .catch(function () {});
54 }
55
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.56 // An app event, to a listener; returns what stops listening.
57 function on(name, listener) {
58 var events = window.__TAURI__ && window.__TAURI__.event;
59 if (!events || typeof events.listen !== "function") return function () {};
60 var stop = null;
61 var stopped = false;
62 events
63 .listen(name, function (event) {
64 try {
65 listener(event && event.payload);
66 } catch (error) {
67 console.warn("g1t desktop: a " + name + " listener failed", error);
68 }
69 })
70 .then(function (unlisten) {
71 if (stopped) unlisten();
72 else stop = unlisten;
73 })
74 .catch(function () {});
75 return function () {
76 stopped = true;
77 if (stop) stop();
78 };
79 }
80
preparing for cloud work81 var bridge = {
82 version: VERSION,
83 platform: PLATFORM,
84 // A native notification, for a message to the person while the window is behind another.
85 notify: function (notification) {
86 quietly(invoke("notify", { title: String(notification.title || "g1t"), body: String(notification.body || "") }));
87 },
88 // What needs the person, on the icon and the tray; 0 clears it.
89 setBadge: function (count) {
90 quietly(invoke("set_badge", { count: Math.max(0, Math.floor(Number(count) || 0)) }));
91 },
92 // A link to somewhere else, in the browser the system uses.
93 openUrl: function (url) {
94 quietly(invoke("open_url", { url: String(url) }));
95 },
96 // g1t:// links, as site paths such as "/acme/-/chat/dm/chn_1".
97 onNavigate: function (listener) {
98 listeners.push(listener);
99 listen();
100 return function () {
101 var at = listeners.indexOf(listener);
102 if (at >= 0) listeners.splice(at, 1);
103 };
104 },
105 // Looks for an update now: "current", "ready:<version>", or rejects with why.
106 checkForUpdates: function () {
107 return invoke("check_for_updates");
108 },
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9109 // The app's own settings, in a window of their own.
110 openSettings: function () {
111 quietly(invoke("open_settings"));
112 },
113 // The title bar's search, in its middle: the site hands it a label
114 // ("Search Flagon") and what opens its search. Returns what takes it away.
115 setSearch: function (label, open) {
116 var mine = typeof open === "function" ? { label: String(label || "Search"), open: open } : null;
117 search = mine;
118 report();
119 return function () {
120 if (search === mine) {
121 search = null;
122 report();
123 }
124 };
125 },
126 // Whether g1t draws the title bar, and how tall it is: 0 under the system's.
127 // The page is below it, never under it: this says only that the bar is there.
128 titleBarHeight: FRAME === "custom" ? 36 : 0,
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.129 // The runner the app keeps on this computer (src/runner/mod.rs).
130 runner: Object.freeze({
131 // What it is doing: its diagnosis (why it isn't running, and the
132 // fix), the workspace, how jobs run, and its last log lines.
133 status: function () {
134 return invoke("runner_status");
135 },
136 // Who is signed in, the workspaces they own, and the one they are in;
137 // resolves to the workspace a registration token is wanted for, or null.
138 offer: function (offer) {
139 return invoke("runner_offer", {
140 signedIn: !!offer.signedIn,
141 workspaces: (offer.workspaces || []).map(function (w) {
142 return { slug: String(w.slug), name: w.name == null ? null : String(w.name) };
143 }),
144 current: offer.current == null ? null : String(offer.current),
145 });
146 },
147 // A registration token for the workspace the app asked for, or why there is none.
148 register: function (answer) {
149 return invoke("runner_register", {
150 workspace: String(answer.workspace),
151 token: answer.token == null ? null : String(answer.token),
152 allowed: answer.allowed !== false,
153 error: answer.error == null ? null : String(answer.error),
154 });
155 },
Merge the desktop runner's settings: a card for each kind of job, Linux and Windows, each with its state, job, cause and fix, and its own Start and Stop; an honest wait for a release's sandbox images, checked again by itself; and the same on the Runners page.156 // Start, stop, or a diagnosis's fix, by its id; Start and Stop of one
157 // kind of job alone (`linux`, `windows`) with its id as `capability`.
158 act: function (action, capability) {
159 var args = { action: String(action) };
160 if (capability != null) args.capability = String(capability);
161 return invoke("runner_control", args);
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.162 },
163 // The app wants a registration token for a workspace.
164 onWanted: function (listener) {
165 return on("g1t-runner-wanted", listener);
166 },
167 // The runner's status changed.
168 onStatus: function (listener) {
169 return on("g1t-runner-status", listener);
170 },
171 }),
preparing for cloud work172 };
173 Object.defineProperty(window, "g1tDesktop", { value: Object.freeze(bridge), writable: false, configurable: false });
174
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9175 // Links out. A link to another site, or to one of the site's pages for
176 // reading about g1t, opens in the system's browser, and the window stays
177 // where it is; a link that asks for a new tab, or a click
preparing for cloud work178 // with Ctrl or Cmd held, goes on in this window, which has no tabs.
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9179 function outside(url) {
180 var path = url.pathname.length > 1 ? url.pathname.replace(/\/+$/, "") : url.pathname;
181 return OUTSIDE.some(function (page) {
182 return page.charAt(page.length - 1) === "/" ? path.indexOf(page) === 0 : path === page;
183 });
184 }
preparing for cloud work185 function destination(href) {
186 try {
187 var url = new URL(href, location.href);
188 return /^https?:$/.test(url.protocol) ? url : null;
189 } catch (error) {
190 return null;
191 }
192 }
193 document.addEventListener(
194 "click",
195 function (event) {
196 if (event.defaultPrevented || event.button !== 0) return;
197 var anchor = event.target && event.target.closest ? event.target.closest("a[href]") : null;
198 if (!anchor || anchor.hasAttribute("download")) return;
199 var url = destination(anchor.getAttribute("href"));
200 if (!url) return;
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9201 if (url.origin !== location.origin || outside(url)) {
preparing for cloud work202 event.preventDefault();
203 event.stopImmediatePropagation();
204 bridge.openUrl(url.href);
205 } else if (anchor.target === "_blank" || event.metaKey || event.ctrlKey) {
206 event.preventDefault();
207 event.stopImmediatePropagation();
208 location.assign(url.href);
209 }
210 },
211 true,
212 );
213 var open = window.open;
214 window.open = function (href, target, features) {
215 var url = destination(href);
216 if (url) {
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9217 if (url.origin === location.origin && !outside(url)) location.assign(url.href);
preparing for cloud work218 else bridge.openUrl(url.href);
219 return null;
220 }
221 return typeof open === "function" ? open.call(window, href, target, features) : null;
222 };
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9223
224 // ── The title bar ─────────────────────────────────────────────────────
225 // What the bar above the page shows of it: the title, the search the
226 // site handed over (`setSearch`), whether there is a page to go back or
227 // forward to, and the page's colours, so the bar is the colour of g1t's
228 // rail, light or dark, or of whatever page is showing. Sent whenever any
229 // of it changes; the bar's search comes back as an event.
230 var search = null;
231 var report = function () {};
232 if (FRAME !== "custom") return;
233
234 // A colour the page names, resolved: `var(--color-rail)` is light-dark(…) until it is drawn.
235 var probe = null;
236 function resolve(value) {
237 if (!document.body) return "";
238 if (!probe) {
239 probe = document.createElement("span");
240 probe.setAttribute("aria-hidden", "true");
241 probe.style.cssText = "position:absolute;width:0;height:0;overflow:hidden;pointer-events:none;";
242 }
243 if (probe.parentNode !== document.body) document.body.appendChild(probe);
244 probe.style.color = "";
245 probe.style.color = value;
246 var color = getComputedStyle(probe).color;
247 probe.remove();
248 return color;
249 }
250 function opaque(color) {
251 return !!color && color !== "transparent" && !/rgba\(.*,\s*0\)$/.test(color);
252 }
253 function luminance(color) {
254 var parts = (color.match(/[\d.]+/g) || []).map(Number);
255 return parts.length < 3 ? 0 : (0.2126 * parts[0] + 0.7152 * parts[1] + 0.0722 * parts[2]) / 255;
256 }
257 // g1t's pages name their colours (apps/web/app/app.css); any other page has its own background and text.
258 function colors() {
259 if (!document.body) return null;
260 var root = getComputedStyle(document.documentElement);
261 var ours = root.getPropertyValue("--color-rail").trim() !== "";
262 var body = getComputedStyle(document.body);
263 var background = ours ? resolve("var(--color-rail)") : body.backgroundColor;
264 if (!opaque(background)) background = root.backgroundColor;
265 if (!opaque(background)) return null;
266 var foreground = ours ? resolve("var(--color-fg)") : body.color;
267 return {
268 background: background,
269 foreground: foreground,
270 muted: ours ? resolve("var(--color-muted)") : foreground,
271 accent: ours ? resolve("var(--color-accent)") : foreground,
272 dark: luminance(background) < 0.5,
273 };
274 }
275
276 var sent = "";
277 var waiting = 0;
278 report = function () {
279 clearTimeout(waiting);
280 waiting = setTimeout(function () {
281 var nav = window.navigation;
282 var page = {
283 title: document.title || "g1t",
284 search: search ? search.label : null,
285 canGoBack: nav && typeof nav.canGoBack === "boolean" ? nav.canGoBack : null,
286 canGoForward: nav && typeof nav.canGoForward === "boolean" ? nav.canGoForward : null,
287 colors: colors(),
288 };
289 var text = JSON.stringify(page);
290 if (text === sent) return;
291 sent = text;
292 invoke("title_bar_page", { page: page }).catch(function () {});
293 }, 30);
294 };
295
296 document.addEventListener("DOMContentLoaded", function () {
297 report();
298 if (document.head) new MutationObserver(report).observe(document.head, { subtree: true, childList: true, characterData: true });
299 // The site's Light, Dark or Auto (data-theme on <html>), and the system's own.
300 new MutationObserver(report).observe(document.documentElement, { attributes: true, attributeFilter: ["data-theme", "class", "style"] });
301 });
302 window.addEventListener("load", report);
303 window.addEventListener("pageshow", report);
304 window.addEventListener("popstate", report);
305 if (window.matchMedia) {
306 var scheme = window.matchMedia("(prefers-color-scheme: dark)");
307 if (typeof scheme.addEventListener === "function") scheme.addEventListener("change", report);
308 }
309 if (window.navigation && typeof window.navigation.addEventListener === "function") {
310 window.navigation.addEventListener("currententrychange", report);
311 }
312
313 // The bar's search was clicked: the site's search opens.
314 var events = window.__TAURI__ && window.__TAURI__.event;
315 if (events && typeof events.listen === "function") {
316 events
317 .listen("g1t-search", function () {
318 if (search) search.open();
319 })
320 .catch(function () {});
321 }
preparing for cloud work322})();

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