g1t.sh/download is a marketing page: it is drawn with the site's header and footer whoever opens it, never inside the app's rail and sidebar, in the shape of Pricing, with Download in the public header, its phone menu and the footer. The desktop app's check in CI runs on the larger machine the Rust tests use; on the small one the linker ran out of room and died with a bus error on every push since the app arrived.
5 files+41−270/5 viewed
| 99 | 99 | ||
| 100 | 100 | desktop: | |
| 101 | 101 | name: Desktop app | |
| 102 | − | runs-on: ubuntu-latest | |
| 102 | + | # The larger machine, as the Rust job: linking Tauri on the small one | |
| 103 | + | # ran out of room and the linker died with a bus error. | |
| 104 | + | runs-on: g1t-4core | |
| 103 | 105 | timeout-minutes: 30 | |
| 104 | 106 | steps: | |
| 105 | 107 | - uses: actions/checkout@v5 |
| 76 | 76 | links: [ | |
| 77 | 77 | ["Explore", "/explore"], | |
| 78 | 78 | ["Pricing", "/pricing"], | |
| 79 | + | ["Download", "/download"], | |
| 79 | 80 | ["Chat", "https://docs.g1t.sh/guides/chat/"], | |
| 80 | 81 | ["Agents", "https://docs.g1t.sh/guides/agents/"], | |
| 81 | 82 | ["Working with g1t", "https://docs.g1t.sh/guides/working-with-g1t/"], |
| 5 | 5 | * into one menu. Someone still confirming their address gets their | |
| 6 | 6 | * account's menu in place of signing in. | |
| 7 | 7 | */ | |
| 8 | − | import { BookOpen, ChevronDown, Compass, CreditCard, LogIn, LogOut, Menu, Plus, Search, Settings } from "lucide-react"; | |
| 8 | + | import { BookOpen, ChevronDown, Compass, CreditCard, LogIn, LogOut, Menu, MonitorDown, Plus, Search, Settings } from "lucide-react"; | |
| 9 | 9 | import { useState } from "react"; | |
| 10 | 10 | import { Form, Link, NavLink, useParams, useSubmit } from "react-router"; | |
| 11 | 11 | ||
| ⋯ | |||
| 106 | 106 | /> | |
| 107 | 107 | <nav aria-label="Main" className="hidden items-center gap-0.5 sm:flex"> | |
| 108 | 108 | <HeaderLink to="/explore">Explore</HeaderLink> | |
| 109 | + | <HeaderLink to="/download">Download</HeaderLink> | |
| 109 | 110 | <HeaderLink to="https://docs.g1t.sh/">Docs</HeaderLink> | |
| 110 | 111 | </nav> | |
| 111 | 112 | <div className="ml-auto flex items-center gap-2"> | |
| ⋯ | |||
| 124 | 125 | </Link> | |
| 125 | 126 | </DropdownMenuItem> | |
| 126 | 127 | <DropdownMenuItem asChild className="min-h-11"> | |
| 128 | + | <Link to="/download"> | |
| 129 | + | <MonitorDown /> | |
| 130 | + | Download | |
| 131 | + | </Link> | |
| 132 | + | </DropdownMenuItem> | |
| 133 | + | <DropdownMenuItem asChild className="min-h-11"> | |
| 127 | 134 | <Link to="https://docs.g1t.sh/"> | |
| 128 | 135 | <BookOpen /> | |
| 129 | 136 | Docs | |
| 33 | 33 | "/integrations/github/setup", | |
| 34 | 34 | ]); | |
| 35 | 35 | ||
| 36 | + | /** Marketing pages: the public header and footer, even for someone signed in. */ | |
| 37 | + | const MARKETING = new Set(["/download"]); | |
| 38 | + | ||
| 36 | 39 | /** The page's path without a trailing slash or a click's `.data`. */ | |
| 37 | 40 | function framePath(pathname: string): string { | |
| 38 | 41 | const path = pathname.replace(/\.data$/, ""); | |
| ⋯ | |||
| 47 | 50 | export function frameOf(pathname: string, viewer: { signedIn: boolean; workspace: boolean }): Frame { | |
| 48 | 51 | const path = framePath(pathname); | |
| 49 | 52 | if (STANDALONE.has(path)) return "standalone"; | |
| 53 | + | // Marketing pages are the site's front, whoever reads them: never inside the app's rail and sidebar. | |
| 54 | + | if (MARKETING.has(path)) return "public"; | |
| 50 | 55 | // An invite link, and signing in with GitHub, are the front door. | |
| 51 | 56 | if (path.startsWith("/invite/") || path === "/auth/github" || path.startsWith("/auth/github/")) return "standalone"; | |
| 52 | 57 | if (path === "/invitations" && viewer.signedIn && !viewer.workspace) return "standalone"; | |
| 6 | 6 | ||
| 7 | 7 | import type { Route } from "./+types/download"; | |
| 8 | 8 | import { QrCode } from "../components/qr-code"; | |
| 9 | − | import { TrustPage } from "../components/trust-page"; | |
| 10 | 9 | import { ButtonLink } from "../components/ui"; | |
| 11 | 10 | import { Card } from "../components/ui/card"; | |
| 12 | 11 | import { HOSTED_ADDRESSES } from "../lib/addresses"; | |
| ⋯ | |||
| 144 | 143 | const featured = yours.find((d) => d.kind === "installer" || d.kind === "dmg" || d.kind === "appimage") ?? yours[0] ?? null; | |
| 145 | 144 | const href = (download: DesktopDownload) => `/downloads/desktop/${release!.version}/${download.name}`; | |
| 146 | 145 | ||
| 146 | + | // A marketing page, drawn like Pricing: the site's header and footer, | |
| 147 | + | // never the app's rail (lib/chrome.ts, MARKETING). | |
| 147 | 148 | return ( | |
| 148 | − | <TrustPage | |
| 149 | − | eyebrow="Desktop app" | |
| 150 | − | title="g1t on your desktop" | |
| 151 | − | lede={ | |
| 152 | − | <p> | |
| 153 | − | The whole of g1t in a window of its own: chat, agents, artifacts and code, for Windows, macOS and Linux. | |
| 154 | − | {release && ( | |
| 155 | − | <> | |
| 156 | − | {" "} | |
| 157 | − | Version {release.version} | |
| 158 | − | {release.pub_date && ( | |
| 159 | − | <> | |
| 160 | − | , released{" "} | |
| 161 | − | {new Date(release.pub_date).toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric" })} | |
| 162 | − | </> | |
| 163 | − | )} | |
| 164 | − | . | |
| 165 | − | </> | |
| 166 | − | )} | |
| 167 | − | </p> | |
| 168 | − | } | |
| 169 | − | > | |
| 170 | − | <div className="max-w-3xl"> | |
| 149 | + | <main className="mx-auto max-w-4xl px-4 py-12"> | |
| 150 | + | <p className="text-sm font-medium text-accent">Desktop and phone apps</p> | |
| 151 | + | <h1 className="mt-2 text-3xl font-semibold tracking-tight text-balance sm:text-4xl">g1t on your desktop, and in your pocket</h1> | |
| 152 | + | <p className="mt-3 max-w-2xl text-muted"> | |
| 153 | + | The whole of g1t in a window of its own: chat, agents, artifacts and code, for Windows, macOS and Linux, with notifications | |
| 154 | + | from your system and updates that install themselves. | |
| 155 | + | {release && ( | |
| 156 | + | <> | |
| 157 | + | {" "} | |
| 158 | + | Version {release.version} | |
| 159 | + | {release.pub_date && ( | |
| 160 | + | <> | |
| 161 | + | , released{" "} | |
| 162 | + | {new Date(release.pub_date).toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric" })} | |
| 163 | + | </> | |
| 164 | + | )} | |
| 165 | + | . | |
| 166 | + | </> | |
| 167 | + | )} | |
| 168 | + | </p> | |
| 169 | + | <div className="mt-8"> | |
| 171 | 170 | {!release && ( | |
| 172 | 171 | <Card className="p-5"> | |
| 173 | 172 | <p className="font-medium">The first release is on its way.</p> | |
| ⋯ | |||
| 253 | 252 | )} | |
| 254 | 253 | <PhoneApp site={site} hosted={hosted} mobile={mobile} android={android} /> | |
| 255 | 254 | </div> | |
| 256 | − | </TrustPage> | |
| 255 | + | </main> | |
| 257 | 256 | ); | |
| 258 | 257 | } | |
| 259 | 258 | ||