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
739 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.

Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r91/* The settings window, in the launcher's colours (launcher.css): rows of
2 settings under small headings, each with its switch on the right. */
3:root {
4 --bg: #0f0f11;
5 --fg: #ededf0;
6 --muted: #9a9aa6;
7 --line: #26262c;
8 --surface: #17171b;
9 --accent: #b6a8ff;
10 --accent-ink: #15112e;
11 --error: #ff8a8a;
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.12 /* States, as dots and labels: running, on its way, needs a look, failing. */
13 --ok: #4ade80;
14 --working: #60a5fa;
15 --warning: #fbbf24;
16 --raised: #1e1e24;
17 --line-strong: #34343c;
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r918 color-scheme: dark;
19}
20* {
21 box-sizing: border-box;
22}
23html,
24body {
25 margin: 0;
26}
27body {
28 background: var(--bg);
29 color: var(--fg);
30 font: 14px/1.45 system-ui, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
31 -webkit-font-smoothing: antialiased;
32 user-select: none;
33 cursor: default;
34}
35/* g1t's own title bar, when the window has no system frame: fixed at the top, 36px tall. */
36.titlebar {
37 position: fixed;
38 inset: 0 0 auto 0;
39 z-index: 10;
40 height: 36px;
41 display: flex;
42 align-items: center;
43 justify-content: space-between;
44 padding-left: 14px;
45 background: #050507;
46 color: var(--muted);
47 font-size: 12px;
48 font-weight: 500;
49}
50.titlebar[hidden] {
51 display: none;
52}
53.mac .titlebar {
54 padding-left: 78px;
55}
56.mac .titlebar .caption {
57 display: none;
58}
59.titlebar .caption {
60 all: unset;
61 display: inline-flex;
62 align-items: center;
63 justify-content: center;
64 width: 46px;
65 height: 36px;
66}
67.titlebar .caption:hover {
68 background: #c42b1c;
69 color: #fff;
70}
71/* Under it, the page scrolls on its own, so its scrollbar starts below the bar. */
72body.framed .page {
73 position: fixed;
74 inset: 36px 0 0 0;
75 overflow-y: auto;
76}
77main {
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.78 max-width: 38rem;
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r979 margin: 0 auto;
80 padding: 1.5rem 1.25rem 1.25rem;
81}
82h1 {
83 font-size: 1.25rem;
84 font-weight: 600;
85 margin: 0 0 0.25rem;
86}
87h2 {
88 font-size: 0.75rem;
89 font-weight: 600;
90 letter-spacing: 0.06em;
91 text-transform: uppercase;
92 color: var(--muted);
93 margin: 0 0 0.25rem;
94}
95p {
96 margin: 0;
97}
98.lede {
99 margin-bottom: 1.5rem;
100}
101.muted {
102 color: var(--muted);
103}
104.small {
105 font-size: 0.8125rem;
106}
107section {
108 margin-bottom: 1.5rem;
109}
110.setting {
111 display: flex;
112 align-items: center;
113 justify-content: space-between;
114 gap: 1rem;
115 padding: 0.75rem 0;
116 border-bottom: 1px solid var(--line);
117}
The desktop app is 0.3.4, and the runner has a window of its own. Beside the title bar's Settings button a chip says what the runner is doing, live, with a dot in its colour (Runner running, Running a job, Running 2 jobs, Runner starting, Runner updating, Runner stopped, Runner offline, Runner needs you), as Docker Desktop shows its engine; in a narrow window only the dot shows. It opens the Runner window, as the tray's runner row now does: at its top what the whole runner is doing, with what it is running, its version and workspace, and its own Stop or Start; then each kind of job's card as before; then its setup (running jobs here at all, how jobs run, the workspace, the computer with Check for runner updates and View on g1t); and its log, open and following along as it is written. Settings keeps the runner as one line, what it is doing with Open runner, and each window saves only its own settings over what the app has now, so neither undoes the other's. The chip's words are diagnosis.rs's, the Runner window is made off the main thread as Settings now is, and it has a capability of its own with only the commands it uses; the title bar may now ask the runner's status. Two things fixed on the way: a settings row hidden until it applies, Restart to change the title bar, showed all the time, since its row's display beat the hidden attribute; and in a narrow window the title bar's buttons ran over the search, which now shrinks instead. The desktop guide describes the chip, the Runner window and where each runner setting now is.118/* A row hidden until it applies, such as Restart to change the title bar. */
119.setting[hidden] {
120 display: none;
121}
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9122section .setting:last-child {
123 border-bottom: 0;
124}
125.setting > span {
126 display: flex;
127 flex-direction: column;
128 gap: 0.125rem;
129 min-width: 0;
130}
131.setting strong {
132 font-weight: 500;
133 overflow-wrap: anywhere;
134}
135.setting .muted {
136 font-size: 0.8125rem;
137}
138.setting.off {
139 opacity: 0.5;
140}
141input[type="checkbox"][role="switch"] {
142 appearance: none;
143 flex: none;
144 width: 36px;
145 height: 20px;
146 margin: 0;
147 border-radius: 999px;
148 background: var(--line);
149 position: relative;
150 transition: background 0.15s;
151}
152input[type="checkbox"][role="switch"]::after {
153 content: "";
154 position: absolute;
155 top: 2px;
156 left: 2px;
157 width: 16px;
158 height: 16px;
159 border-radius: 50%;
160 background: var(--fg);
161 transition: transform 0.15s;
162}
163input[type="checkbox"][role="switch"]:checked {
164 background: var(--accent);
165}
166input[type="checkbox"][role="switch"]:checked::after {
167 transform: translateX(16px);
168 background: var(--accent-ink);
169}
170input:disabled {
171 opacity: 0.5;
172}
173@media (prefers-reduced-motion: reduce) {
174 input[type="checkbox"][role="switch"],
175 input[type="checkbox"][role="switch"]::after {
176 transition: none;
177 }
178}
179button,
180select {
181 flex: none;
182 font: inherit;
183 font-size: 0.8125rem;
184 font-weight: 500;
185 padding: 0.375rem 0.75rem;
186 border-radius: 8px;
187 border: 1px solid var(--line);
188 background: var(--surface);
189 color: var(--fg);
190 cursor: default;
191}
192button:hover,
193select:hover {
194 border-color: #3a3a44;
195}
196.setting > .buttons {
197 flex-direction: row;
198 flex: none;
199 gap: 0.5rem;
200}
The desktop app is 0.3.2, and its runner updates are fast, seen and only ever g1t's. The app now trusts a g1t-runner release only when its manifest's Ed25519 signature, latest.json.sig, checks out against g1t's runner release key, which is built into the app (the public half of RUNNER_RELEASE_PUBLIC_KEY, kept in runner/native.rs, where development builds have it too; G1T_RUNNER_RELEASE_KEY overrides it for a g1t with its own key), as the runner already did with its own updates. A manifest with no signature, or one that doesn't check out, is refused and nothing in it is used: the runner keeps the version it has and says "Update refused: not signed by g1t", or, with no runner installed yet, "Not running: the runner's release isn't signed by g1t", each with why and what to do. It looks every hour rather than every six, on a thread of its own so the runner is looked after meanwhile, and what it finds waits, downloaded and checked, until each kind of job is idle, then goes in kind by kind, never cutting a job short: Linux jobs take the release's image (sandbox update) as soon as they have no job, while Windows jobs, whose runner is the program itself, are stopped after their job (sandbox down --only windows --drain), the program is replaced and they start again, the other kind running on throughout; a runner in Docker or on the computer stops between jobs and starts on the new one. The program there is moved aside rather than overwritten, so a status the app is asking at that moment can't stop it. Each kind of job's card in Settings shows its runner version, when the app last looked ("Checked 12 min ago") and a newer one waiting ("0.3.2 ready, installs after this job", "Installing 0.3.2"), and "Check for runner updates" beside the computer's name looks at once and installs what it finds, even with "Download updates by themselves" off, which otherwise stops the hourly look. The desktop report carries the same (snake_case: the diagnosis's update, with checked_at, ready and note, and each kind's update), so the Runners page's Desktops cards show it, "Checked" with how long ago, and this computer's row has the button too. All the words are diagnosis.rs's. The desktop guide says how updates work, what each card shows, the new troubleshooting rows and where the release key lives. Tests cover the signature with a key pair of their own and with release 0.3.0's manifest and signature as g1t.sh published them, kept byte for byte (.gitattributes), the wording, the cards and the report.201/* A row with buttons beside its words: in a narrow window, the buttons go
202 under them rather than squeezing them into a column. */
203.setting:has(> .buttons) {
204 flex-wrap: wrap;
205 row-gap: 0.5rem;
206}
207.setting:has(> .buttons) > span:first-child {
208 flex: 1 1 14rem;
209}
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9210button.primary {
211 background: var(--accent);
212 border-color: var(--accent);
213 color: var(--accent-ink);
214}
215button.primary:hover {
216 filter: brightness(1.06);
217}
218button[hidden] {
219 display: none;
220}
221button:disabled {
222 opacity: 0.6;
223}
224button:focus-visible,
225select:focus-visible,
226input:focus-visible,
227a:focus-visible {
228 outline: 2px solid var(--accent);
229 outline-offset: 2px;
230}
231a {
232 color: var(--muted);
233 text-decoration: underline;
234 text-underline-offset: 3px;
235}
236a:hover {
237 color: var(--fg);
238}
239.error {
240 color: var(--error);
241 font-size: 0.875rem;
242 margin-bottom: 1rem;
243}
244.foot {
245 text-align: center;
246}
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.247code {
248 font: 0.8125rem ui-monospace, SFMono-Regular, Consolas, monospace;
249}
250/* The runner's state: a dot in its tone, what it is doing, and when it
251 isn't running, why and the fix, with its buttons. */
252.diagnosis {
253 display: flex;
254 align-items: flex-start;
255 gap: 0.75rem;
256 margin: 0.25rem 0 0.5rem;
257 padding: 0.75rem;
258 border: 1px solid var(--line);
259 border-radius: 10px;
260 background: var(--surface);
261}
262.diagnosis-text {
263 display: flex;
264 flex-direction: column;
265 gap: 0.25rem;
266 min-width: 0;
267 flex: 1;
268 font-size: 0.8125rem;
269}
270.diagnosis-text strong {
271 font-size: 0.875rem;
272 font-weight: 500;
273}
274.diagnosis-text .buttons {
275 display: flex;
276 flex-wrap: wrap;
277 gap: 0.5rem;
278 margin-top: 0.25rem;
279}
280.diagnosis-text .buttons:empty {
281 display: none;
282}
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.283.diagnosis[hidden] {
Merge the desktop app's sandbox: its default runner now starts g1t-runner's sandbox on Windows, macOS and Linux, and says per kind of job (Linux, Windows) what it is doing or why it cannot and how to fix it.284 display: none;
285}
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.286.dot {
287 flex: none;
288 width: 8px;
289 height: 8px;
290 margin-top: 0.4rem;
291 border-radius: 50%;
292 background: var(--muted);
293}
294.dot.ok {
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.295 background: var(--ok);
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.296}
297.dot.working {
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.298 background: var(--working);
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.299}
300.dot.warning {
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.301 background: var(--warning);
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.302}
303.dot.problem {
304 background: var(--error);
305}
306.warning-note {
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.307 color: var(--warning);
308}
309.visually-hidden {
310 position: absolute;
311 width: 1px;
312 height: 1px;
313 margin: -1px;
314 padding: 0;
315 overflow: hidden;
316 clip: rect(0 0 0 0);
317 white-space: nowrap;
318 border: 0;
319}
320
321/* Sandboxed, each kind of job it runs, side by side: Linux jobs, Windows
322 jobs. Two to a row where they fit, one under the other where they don't. */
323.kinds {
324 display: grid;
325 grid-template-columns: repeat(auto-fit, minmax(15.5rem, 1fr));
326 gap: 0.75rem;
327 margin: 0.5rem 0 0.75rem;
328}
329.kinds[hidden] {
330 display: none;
331}
332.kinds.single {
333 grid-template-columns: 1fr;
334}
335.kind {
336 display: flex;
337 flex-direction: column;
338 gap: 0.625rem;
339 min-width: 0;
340 padding: 0.875rem;
341 border: 1px solid var(--line);
342 border-radius: 12px;
343 background: var(--surface);
344 transition: border-color 0.2s;
345}
346/* A kind that needs a look says so with its edge as well as its label. */
347.kind[data-tone="problem"] {
348 border-color: rgba(255, 138, 138, 0.35);
349}
350.kind[data-tone="warning"] {
351 border-color: rgba(251, 191, 36, 0.3);
352}
353/* Its icon, and beside it its name over its state. */
354.kind-head {
355 display: flex;
356 align-items: center;
357 gap: 0.75rem;
358}
359.kind-icon {
360 flex: none;
361 display: grid;
362 place-items: center;
363 width: 40px;
364 height: 40px;
365 border-radius: 10px;
366 background: var(--raised);
367 color: var(--fg);
368}
369.kind.off .kind-icon {
370 color: var(--muted);
371}
372.kind-name {
373 display: flex;
374 flex-direction: column;
375 align-items: flex-start;
376 gap: 0.25rem;
377 min-width: 0;
378 flex: 1 1 auto;
379}
380.kind-name h3 {
381 margin: 0;
382 font-size: 0.9375rem;
383 font-weight: 600;
384 letter-spacing: -0.005em;
385}
386.kind-about {
387 margin: 0;
388 color: var(--muted);
389 font-size: 0.8125rem;
390 line-height: 1.4;
391}
392/* Its state, as a label: a dot in its tone, and a few words. */
393.pill {
394 display: inline-flex;
395 align-items: center;
396 gap: 0.375rem;
397 flex: none;
398 padding: 0.0625rem 0.5rem 0.0625rem 0.4375rem;
399 border: 1px solid var(--line-strong);
400 border-radius: 999px;
401 color: var(--muted);
402 font-size: 0.75rem;
403 font-weight: 500;
404 line-height: 1.4;
405 white-space: nowrap;
406}
407.pill-dot {
408 width: 6px;
409 height: 6px;
410 border-radius: 50%;
411 background: currentColor;
412}
413.pill[data-tone="ok"] {
414 border-color: rgba(74, 222, 128, 0.35);
415 background: rgba(74, 222, 128, 0.1);
416 color: var(--ok);
417}
418.pill[data-tone="working"] {
419 border-color: rgba(96, 165, 250, 0.35);
420 background: rgba(96, 165, 250, 0.1);
421 color: var(--working);
422}
423.pill[data-tone="warning"] {
424 border-color: rgba(251, 191, 36, 0.35);
425 background: rgba(251, 191, 36, 0.1);
426 color: var(--warning);
427}
428.pill[data-tone="problem"] {
429 border-color: rgba(255, 138, 138, 0.4);
430 background: rgba(255, 138, 138, 0.1);
431 color: var(--error);
432}
433/* On its way (running a job, starting, downloading): the dot breathes. */
434.pill.live .pill-dot {
435 animation: breathe 1.6s ease-in-out infinite;
436}
437@keyframes breathe {
438 50% {
439 opacity: 0.35;
440 }
441}
442.kind-body {
443 display: flex;
444 flex-direction: column;
445 gap: 0.25rem;
446 font-size: 0.8125rem;
447 line-height: 1.45;
448}
449.kind-body[hidden] {
450 display: none;
451}
452.kind-body p {
453 margin: 0;
454}
455.kind-headline {
456 color: var(--fg);
457 font-size: 0.875rem;
458 font-weight: 500;
459}
460.kind-cause {
461 color: var(--muted);
462}
463.kind-fix {
464 color: var(--fg);
465}
466.kind-body code,
467.diagnosis-text code {
468 padding: 0.0625rem 0.3125rem;
469 border-radius: 5px;
470 background: var(--raised);
471 color: var(--fg);
472 font-size: 0.75rem;
473 overflow-wrap: anywhere;
474 user-select: text;
475}
476.kind-job {
477 display: flex;
478 flex-direction: column;
479 gap: 0.125rem;
480 padding: 0.5rem 0.625rem;
481 border-radius: 8px;
482 background: var(--raised);
483}
484.kind-job-label {
485 color: var(--muted);
486 font-size: 0.6875rem;
487 font-weight: 600;
488 letter-spacing: 0.05em;
489 text-transform: uppercase;
490}
491.kind-job-what {
492 min-width: 0;
493 overflow-wrap: anywhere;
494}
495.kind-job-what a {
496 color: var(--fg);
497 font-weight: 500;
498}
499.kind-job-what a:hover {
500 color: var(--accent);
501}
502.kind-progress {
503 display: flex;
504 align-items: center;
505 gap: 0.625rem;
506}
507.kind-progress .bar {
508 flex: 1;
509 height: 6px;
510 overflow: hidden;
511 border-radius: 999px;
512 background: var(--line);
513}
514.kind-progress .bar span {
515 display: block;
516 height: 100%;
517 border-radius: 999px;
518 background: var(--accent);
519 transition: width 0.4s ease;
520}
521.kind-progress .small {
522 min-width: 2.5rem;
523 text-align: right;
524 font-variant-numeric: tabular-nums;
525}
526/* What it is on g1t: there to read, never in the way. */
527.kind-details {
528 display: grid;
529 gap: 0.125rem;
530 margin: 0;
531 padding-top: 0.5rem;
532 border-top: 1px solid var(--line);
533 font-size: 0.75rem;
534}
535.kind-details[hidden] {
536 display: none;
537}
538.kind-details div {
539 display: flex;
540 align-items: baseline;
541 gap: 0.5rem;
542 min-width: 0;
543}
544.kind-details dt {
545 flex: none;
546 width: 4.25rem;
547 color: var(--muted);
548}
549.kind-details dd {
550 display: flex;
551 align-items: baseline;
552 flex-wrap: wrap;
553 gap: 0 0.5rem;
554 min-width: 0;
555 margin: 0;
556 color: var(--fg);
557 overflow-wrap: anywhere;
558 user-select: text;
559}
The desktop app is 0.3.2, and its runner updates are fast, seen and only ever g1t's. The app now trusts a g1t-runner release only when its manifest's Ed25519 signature, latest.json.sig, checks out against g1t's runner release key, which is built into the app (the public half of RUNNER_RELEASE_PUBLIC_KEY, kept in runner/native.rs, where development builds have it too; G1T_RUNNER_RELEASE_KEY overrides it for a g1t with its own key), as the runner already did with its own updates. A manifest with no signature, or one that doesn't check out, is refused and nothing in it is used: the runner keeps the version it has and says "Update refused: not signed by g1t", or, with no runner installed yet, "Not running: the runner's release isn't signed by g1t", each with why and what to do. It looks every hour rather than every six, on a thread of its own so the runner is looked after meanwhile, and what it finds waits, downloaded and checked, until each kind of job is idle, then goes in kind by kind, never cutting a job short: Linux jobs take the release's image (sandbox update) as soon as they have no job, while Windows jobs, whose runner is the program itself, are stopped after their job (sandbox down --only windows --drain), the program is replaced and they start again, the other kind running on throughout; a runner in Docker or on the computer stops between jobs and starts on the new one. The program there is moved aside rather than overwritten, so a status the app is asking at that moment can't stop it. Each kind of job's card in Settings shows its runner version, when the app last looked ("Checked 12 min ago") and a newer one waiting ("0.3.2 ready, installs after this job", "Installing 0.3.2"), and "Check for runner updates" beside the computer's name looks at once and installs what it finds, even with "Download updates by themselves" off, which otherwise stops the hourly look. The desktop report carries the same (snake_case: the diagnosis's update, with checked_at, ready and note, and each kind's update), so the Runners page's Desktops cards show it, "Checked" with how long ago, and this computer's row has the button too. All the words are diagnosis.rs's. The desktop guide says how updates work, what each card shows, the new troubleshooting rows and where the release key lives. Tests cover the signature with a key pair of their own and with release 0.3.0's manifest and signature as g1t.sh published them, kept byte for byte (.gitattributes), the wording, the cards and the report.560/* A newer version waiting, over when g1t last looked. */
561.kind-update {
562 display: grid;
563 gap: 0.0625rem;
564}
565.kind-update-ready {
566 color: var(--accent);
567}
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.568button.link {
569 display: inline-flex;
570 align-items: center;
571 gap: 0.25rem;
572 padding: 0;
573 border: 0;
574 background: none;
575 color: var(--fg);
576 font-size: inherit;
577 font-weight: 400;
578 text-decoration: underline;
579 text-decoration-color: var(--line-strong);
580 text-underline-offset: 3px;
581}
582button.link:hover {
583 color: var(--accent);
584 text-decoration-color: currentColor;
585}
586button.link svg {
587 flex: none;
588 color: var(--muted);
589}
590.kind-actions {
591 display: flex;
592 flex-wrap: wrap;
593 align-items: center;
594 gap: 0.5rem;
595 margin-top: auto;
596}
597.kind-actions[hidden] {
598 display: none;
599}
600.kind-actions .end {
601 margin-left: auto;
602}
603button.quiet {
604 background: transparent;
605 color: var(--muted);
606}
607button.quiet:hover {
608 color: var(--fg);
609}
610@media (prefers-reduced-motion: reduce) {
611 .pill.live .pill-dot {
612 animation: none;
613 }
614 .kind,
615 .kind-progress .bar span {
616 transition: none;
617 }
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.618}
619details.log {
620 padding: 0.75rem 0;
621}
622details.log summary {
623 color: var(--muted);
624 font-size: 0.8125rem;
625}
626details.log pre {
627 max-height: 12rem;
628 overflow: auto;
629 margin: 0.5rem 0;
630 padding: 0.5rem;
631 border-radius: 8px;
632 background: #050507;
633 color: var(--muted);
634 font: 0.75rem/1.4 ui-monospace, SFMono-Regular, Consolas, monospace;
635 white-space: pre-wrap;
636 overflow-wrap: anywhere;
637 user-select: text;
638}
The desktop app is 0.3.4, and the runner has a window of its own. Beside the title bar's Settings button a chip says what the runner is doing, live, with a dot in its colour (Runner running, Running a job, Running 2 jobs, Runner starting, Runner updating, Runner stopped, Runner offline, Runner needs you), as Docker Desktop shows its engine; in a narrow window only the dot shows. It opens the Runner window, as the tray's runner row now does: at its top what the whole runner is doing, with what it is running, its version and workspace, and its own Stop or Start; then each kind of job's card as before; then its setup (running jobs here at all, how jobs run, the workspace, the computer with Check for runner updates and View on g1t); and its log, open and following along as it is written. Settings keeps the runner as one line, what it is doing with Open runner, and each window saves only its own settings over what the app has now, so neither undoes the other's. The chip's words are diagnosis.rs's, the Runner window is made off the main thread as Settings now is, and it has a capability of its own with only the commands it uses; the title bar may now ask the runner's status. Two things fixed on the way: a settings row hidden until it applies, Restart to change the title bar, showed all the time, since its row's display beat the hidden attribute; and in a narrow window the title bar's buttons ran over the search, which now shrinks instead. The desktop guide describes the chip, the Runner window and where each runner setting now is.639/* The Runner window's head: what the whole runner is doing, as the title
640 bar's chip says it, with its Start or Stop, as Docker Desktop shows its
641 engine. */
642.engine {
643 display: flex;
644 align-items: center;
645 gap: 0.75rem;
646 margin: 0 0 1rem;
647 padding: 0.875rem 1rem;
648 border: 1px solid var(--line);
649 border-radius: 10px;
650 background: var(--surface);
651}
652.engine .dot {
653 width: 10px;
654 height: 10px;
655 margin-top: 0;
656}
657.engine .dot.working {
658 animation: breathe 1.6s ease-in-out infinite;
659}
660.engine-text {
661 display: flex;
662 flex-direction: column;
663 gap: 0.125rem;
664 flex: 1;
665 min-width: 0;
666}
667.engine-text strong {
668 font-size: 0.9375rem;
669 font-weight: 500;
670}
671.engine-text .muted {
672 font-size: 0.8125rem;
673 overflow-wrap: anywhere;
674}
675@keyframes breathe {
676 50% {
677 opacity: 0.35;
678 }
679}
680@media (prefers-reduced-motion: reduce) {
681 .engine .dot.working {
682 animation: none;
683 }
684}
685/* Its log, open and following along. */
686pre.live-log {
687 max-height: 16rem;
688 overflow: auto;
689 margin: 0.5rem 0;
690 padding: 0.625rem 0.75rem;
691 border: 1px solid var(--line);
692 border-radius: 8px;
693 background: #050507;
694 color: var(--muted);
695 font: 0.75rem/1.45 ui-monospace, SFMono-Regular, Consolas, monospace;
696 white-space: pre-wrap;
697 overflow-wrap: anywhere;
698 user-select: text;
699}
700p.buttons {
701 display: flex;
702 gap: 0.5rem;
703 margin: 0;
704}
705/* Settings' line for the runner: its dot beside what it is doing. */
706.runner-line {
707 display: inline-flex;
708 align-items: center;
709 gap: 0.5rem;
710}
711.runner-line .dot {
712 margin-top: 0;
713}
Merge main into rm-agents: mirroring removal, open signups, free-tier limits, the g1t CLI and runner fixes, with the agent removal kept. The CLI's table of operations is regenerated from the API without the agent, customize and AI credit operations; no CLI command used them.714/* A button at work: a small spinner before its label until the status that follows. */
715button.busy {
716 cursor: progress;
717}
718button.busy::before {
719 content: "";
720 display: inline-block;
721 width: 0.75em;
722 height: 0.75em;
723 margin-right: 0.5em;
724 vertical-align: -0.1em;
725 border: 2px solid currentColor;
726 border-right-color: transparent;
727 border-radius: 50%;
728 animation: spin 0.8s linear infinite;
729}
730@keyframes spin {
731 to {
732 transform: rotate(360deg);
733 }
734}
735@media (prefers-reduced-motion: reduce) {
736 button.busy::before {
737 animation: none;
738 }
739}

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