History

Add downloads page and shared marketing layout (#802)

Jono Kemball committed 8 months ago

Commit b8d07eb1 parent 223c3dc4 files · +574 −241Browse files

Unified · Split

4 files
  1. apps/marketing/src/layouts/Layout.astro +228 −0
  2. apps/marketing/src/lib/releases.ts +30 −0
  3. apps/marketing/src/pages/download.astro +271 −0
  4. apps/marketing/src/pages/index.astro +45 −241
apps/marketing/src/layouts/Layout.astro+228−0
@@ -0,0 +1,228 @@
1+---
2+interface Props {
3+ title?: string;
4+ description?: string;
5+}
6+
7+const {
8+ title = "T3 Code",
9+ description = "T3 Code — The best way to code with AI.",
10+} = Astro.props;
11+---
12+
13+<html lang="en">
14+ <head>
15+ <meta charset="UTF-8" />
16+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
17+ <meta name="description" content={description} />
18+ <link rel="preconnect" href="https://fonts.googleapis.com" />
19+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
20+ <link
21+ href="https://fonts.googleapis.com/css2?family=DM+Sans:opsz,wght@9..40,400;9..40,500;9..40,600&display=swap"
22+ rel="stylesheet"
23+ />
24+ <link rel="icon" href="/favicon.ico" sizes="48x48" />
25+ <link rel="icon" type="image/png" href="/favicon-32x32.png" sizes="32x32" />
26+ <link rel="icon" type="image/png" href="/favicon-16x16.png" sizes="16x16" />
27+ <link rel="apple-touch-icon" href="/apple-touch-icon.png" />
28+ <title>{title}</title>
29+ </head>
30+ <body>
31+ <div class="page">
32+ <nav class="nav">
33+ <a href="/" class="nav-brand">
34+ <img src="/icon.png" alt="T3" class="nav-icon" />
35+ </a>
36+ <a
37+ href="https://github.com/pingdotgg/t3code"
38+ target="_blank"
39+ rel="noopener noreferrer"
40+ class="nav-github"
41+ >
42+ GitHub
43+ <span class="nav-github-arrow" aria-hidden="true">&#8599;</span>
44+ </a>
45+ </nav>
46+
47+ <main class="main">
48+ <slot />
49+ </main>
50+
51+ <footer class="footer">
52+ <span class="footer-copy">&copy; {new Date().getFullYear()} T3 Tools Inc</span>
53+ <div class="footer-links">
54+ <a href="https://github.com/pingdotgg/t3code" target="_blank" rel="noopener noreferrer">GitHub</a>
55+ <a href="https://discord.gg/jn4EGJjrvv" target="_blank" rel="noopener noreferrer">Discord</a>
56+ </div>
57+ </footer>
58+ </div>
59+ </body>
60+</html>
61+
62+<style is:global>
63+ :root {
64+ --bg: #09090b;
65+ --fg: #fafafa;
66+ --fg-muted: #a1a1aa;
67+ --fg-dim: #71717a;
68+ --border: rgba(255, 255, 255, 0.08);
69+ }
70+
71+ * {
72+ box-sizing: border-box;
73+ margin: 0;
74+ padding: 0;
75+ }
76+
77+ html {
78+ color-scheme: dark;
79+ }
80+
81+ body {
82+ background: var(--bg);
83+ color: var(--fg);
84+ font-family: "DM Sans", -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
85+ -webkit-font-smoothing: antialiased;
86+ -moz-osx-font-smoothing: grayscale;
87+ min-height: 100vh;
88+ overflow-x: hidden;
89+ }
90+
91+ body::after {
92+ content: "";
93+ position: fixed;
94+ inset: 0;
95+ pointer-events: none;
96+ opacity: 0.035;
97+ background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
98+ background-repeat: repeat;
99+ background-size: 256px 256px;
100+ z-index: 9999;
101+ }
102+
103+ a {
104+ color: inherit;
105+ text-decoration: none;
106+ }
107+
108+ img {
109+ display: block;
110+ max-width: 100%;
111+ }
112+
113+ @keyframes fade-in {
114+ from {
115+ opacity: 0;
116+ transform: translateY(8px);
117+ }
118+ to {
119+ opacity: 1;
120+ transform: translateY(0);
121+ }
122+ }
123+</style>
124+
125+<style>
126+ /* ── Page ── */
127+
128+ .page {
129+ min-height: 100vh;
130+ display: flex;
131+ flex-direction: column;
132+ overflow-x: hidden;
133+ }
134+
135+ /* ── Navbar ── */
136+
137+ .nav {
138+ display: flex;
139+ align-items: center;
140+ justify-content: space-between;
141+ padding: 1.5rem 2.5rem;
142+ opacity: 0.5;
143+ transition: opacity 0.4s ease;
144+ }
145+
146+ .nav:hover {
147+ opacity: 1;
148+ }
149+
150+ .nav-brand {
151+ display: flex;
152+ align-items: center;
153+ }
154+
155+ .nav-icon {
156+ width: 28px;
157+ height: 28px;
158+ border-radius: 6px;
159+ }
160+
161+ .nav-github {
162+ display: flex;
163+ align-items: center;
164+ gap: 0.35rem;
165+ color: var(--fg-muted);
166+ font-size: 0.875rem;
167+ transition: color 0.3s ease;
168+ }
169+
170+ .nav-github:hover {
171+ color: var(--fg);
172+ }
173+
174+ .nav-github-arrow {
175+ display: inline-block;
176+ transition: transform 0.3s ease;
177+ }
178+
179+ .nav-github:hover .nav-github-arrow {
180+ transform: translateX(2px) translateY(-1px);
181+ }
182+
183+ /* ── Main content ── */
184+
185+ .main {
186+ flex: 1;
187+ display: flex;
188+ flex-direction: column;
189+ align-items: center;
190+ }
191+
192+ /* ── Footer ── */
193+
194+ .footer {
195+ display: flex;
196+ align-items: center;
197+ justify-content: space-between;
198+ padding: 1.5rem 2.5rem;
199+ font-size: 0.8rem;
200+ color: var(--fg-dim);
201+ }
202+
203+ .footer-links {
204+ display: flex;
205+ gap: 1.25rem;
206+ }
207+
208+ .footer-links a {
209+ color: var(--fg-dim);
210+ transition: color 0.3s ease;
211+ }
212+
213+ .footer-links a:hover {
214+ color: var(--fg);
215+ }
216+
217+ /* ── Mobile ── */
218+
219+ @media (max-width: 640px) {
220+ .nav {
221+ padding: 1.25rem;
222+ }
223+
224+ .footer {
225+ padding: 1.25rem;
226+ }
227+ }
228+</style>
apps/marketing/src/lib/releases.ts+30−0
@@ -0,0 +1,30 @@
1+const REPO = "pingdotgg/t3code";
2+
3+export const RELEASES_URL = `https://github.com/${REPO}/releases`;
4+
5+const API_URL = `https://api.github.com/repos/${REPO}/releases/latest`;
6+const CACHE_KEY = "t3code-latest-release";
7+
8+export interface ReleaseAsset {
9+ name: string;
10+ browser_download_url: string;
11+}
12+
13+export interface Release {
14+ tag_name: string;
15+ html_url: string;
16+ assets: ReleaseAsset[];
17+}
18+
19+export async function fetchLatestRelease(): Promise<Release> {
20+ const cached = sessionStorage.getItem(CACHE_KEY);
21+ if (cached) return JSON.parse(cached);
22+
23+ const data = await fetch(API_URL).then((r) => r.json());
24+
25+ if (data?.assets) {
26+ sessionStorage.setItem(CACHE_KEY, JSON.stringify(data));
27+ }
28+
29+ return data;
30+}
apps/marketing/src/pages/download.astro+271−0
@@ -0,0 +1,271 @@
1+---
2+import Layout from "../layouts/Layout.astro";
3+---
4+
5+<Layout title="Download — T3 Code" description="Download T3 Code for macOS, Windows, or Linux.">
6+ <h1 class="heading">Download T3 Code</h1>
7+ <p class="subheading">
8+ <span id="version-label">Loading latest release&hellip;</span>
9+ <a id="changelog-link" class="changelog-link" href="#" target="_blank" rel="noopener noreferrer" style="display:none">View changelog<span aria-hidden="true"> &#8599;</span></a>
10+ </p>
11+
12+ <div class="platforms" id="platforms">
13+ <!-- macOS -->
14+ <section class="platform-section">
15+ <div class="platform-header">
16+ <svg class="platform-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 814 1000" aria-hidden="true"><path fill="currentColor" d="M788.1 340.9c-5.8 4.5-108.2 62.2-108.2 190.5 0 148.4 130.3 200.9 134.2 202.2-.6 3.2-20.7 71.9-68.7 141.9-42.8 61.6-87.5 123.1-155.5 123.1s-85.5-39.5-164-39.5c-76.5 0-103.7 40.8-165.9 40.8s-105.6-57-155.5-127C46.7 790.7 0 663 0 541.8c0-194.4 126.4-297.5 250.8-297.5 66.1 0 121.2 43.4 162.7 43.4 39.5 0 101.1-46 176.3-46 28.5 0 130.9 2.6 198.3 99.2zm-234-181.5c31.1-36.9 53.1-88.1 53.1-139.3 0-7.1-.6-14.3-1.9-20.1-50.6 1.9-110.8 33.7-147.1 75.8-28.5 32.4-55.1 83.6-55.1 135.5 0 7.8 1.3 15.6 1.9 18.1 3.2.6 8.4 1.3 13.6 1.3 45.4 0 102.5-30.4 135.5-71.3z"/></svg>
17+ <h2 class="platform-name">macOS</h2>
18+ </div>
19+ <div class="download-cards">
20+ <a class="download-card" data-asset="arm64.dmg" href="#">
21+ <span class="card-arch">Apple Silicon (arm64)</span>
22+ <span class="card-format">.dmg</span>
23+ </a>
24+ <a class="download-card" data-asset="x64.dmg" href="#">
25+ <span class="card-arch">Intel (x64)</span>
26+ <span class="card-format">.dmg</span>
27+ </a>
28+ </div>
29+ </section>
30+
31+ <!-- Windows -->
32+ <section class="platform-section">
33+ <div class="platform-header">
34+ <svg class="platform-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 88 88" aria-hidden="true"><path fill="currentColor" d="m0 12.402 35.687-4.86.016 34.423-35.67.203zm35.67 33.529.028 34.453L.028 75.48.026 45.7zm4.326-39.025L87.314 0v41.527l-47.318.376zm47.329 39.349-.011 41.34-47.318-6.678-.066-34.739z"/></svg>
35+ <h2 class="platform-name">Windows</h2>
36+ </div>
37+ <div class="download-cards">
38+ <a class="download-card" data-asset="x64.exe" href="#">
39+ <span class="card-arch">Windows 10, 11</span>
40+ <span class="card-format">.exe</span>
41+ </a>
42+ </div>
43+ </section>
44+
45+ <!-- Linux -->
46+ <section class="platform-section">
47+ <div class="platform-header">
48+ <svg class="platform-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 295" aria-hidden="true"><defs><linearGradient id="lt0" x1="48.548%" x2="51.047%" y1="115.276%" y2="41.364%"><stop offset="0%" stop-color="#FFEED7"/><stop offset="100%" stop-color="#BDBFC2"/></linearGradient><linearGradient id="lt6" x1="49.984%" x2="49.984%" y1="89.845%" y2="40.632%"><stop offset="0%" stop-color="#FFEED7"/><stop offset="100%" stop-color="#BDBFC2"/></linearGradient><linearGradient id="lt8" x1="49.841%" x2="50.241%" y1="13.229%" y2="94.673%"><stop offset="0%" stop-color="#FFF" stop-opacity=".8"/><stop offset="100%" stop-color="#FFF" stop-opacity="0"/></linearGradient><linearGradient id="ltc" x1="53.467%" x2="38.949%" y1="48.921%" y2="98.1%"><stop offset="0%" stop-color="#FFA63F"/><stop offset="100%" stop-color="#FF0"/></linearGradient><linearGradient id="lte" x1="30.581%" x2="65.887%" y1="34.024%" y2="89.175%"><stop offset="0%" stop-color="#FFA63F"/><stop offset="100%" stop-color="#FF0"/></linearGradient><linearGradient id="lti" x1="49.733%" x2="50.558%" y1="17.609%" y2="99.385%"><stop offset="0%" stop-color="#FFA63F"/><stop offset="100%" stop-color="#FF0"/></linearGradient></defs><g fill="none"><path fill="#000" d="M63.213 215.474c-11.387-16.346-13.591-69.606 12.947-102.39C89.292 97.383 92.69 86.455 93.7 71.67c.734-16.805-11.846-66.851 35.537-70.616 48.027-3.857 45.364 43.526 45.088 68.596-.183 21.12 15.52 33.15 26.355 49.68 19.927 30.303 18.274 82.461-3.765 110.745-27.916 35.354-51.791 20.018-67.678 21.304-29.752 1.745-30.762 17.54-66.024-35.905"/><path fill="url(#lt6)" d="M81.027 145.684c6.52-14.785 20.386-40.772 20.662-60.883 0-15.978 47.843-19.835 51.7-3.856 3.856 15.978 13.59 39.853 19.834 51.424 6.245 11.478 24.335 48.118 5.051 80.074-17.356 28.284-69.973 50.69-98.073-3.856-9.55-18.917-7.806-42.333.826-62.903"/><path fill="url(#lt8)" d="M166.428 151.285c0 16.162-15.519 37.1-42.15 36.916-27.456.183-39.118-20.754-39.118-36.916 0-16.161 18.182-29.293 40.588-29.293 22.498.092 40.68 13.132 40.68 29.293"/><path fill="#000" stroke="#000" stroke-width=".977" d="M176.805 117.86c13.59 11.02 38.292 49.587 2.204 74.748-11.846 7.806 10.468 32.508 23.049 19.927 43.618-43.894-1.102-94.308-16.53-111.664-13.774-15.151-25.987 3.49-8.723 16.989z"/><path fill="#000" stroke="#000" stroke-width="1.25" d="M79.925 122.727c-8.907 14.509-30.211 48.669-1.652 66.484 38.384 23.6 27.548 47.108-7.53 25.895-49.404-29.568-5.97-89.257 13.774-112.03 22.59-25.529 4.316 4.683-4.592 19.65z"/><path fill="url(#ltc)" stroke="#E68C3F" stroke-width="6.25" d="M51.835 258.542c-20.57-10.928-50.414 2.112-39.578-27.457 2.204-6.704-3.214-16.805.275-23.325 4.133-7.989 13.04-6.244 18.366-11.57 5.234-5.51 8.54-15.06 18.366-13.59 9.734 1.468 16.254 13.406 23.049 28.099 5.05 10.468 22.865 25.253 21.672 37.007-1.47 17.998-21.948 21.396-42.15 10.836z" transform="translate(10)"/><path fill="url(#lte)" stroke="#E68C3F" stroke-width="6.251" d="M194.445 253.49c15.06-18.273 48.578-14.508 25.988-39.577-4.775-5.418-3.306-16.989-9.183-21.947-6.887-6.061-14.509-1.102-21.488-4.224-6.979-3.398-14.325-9.918-22.865-5.327-8.54 4.684-9.459 16.805-10.285 32.783-.735 11.479-11.203 30.671-5.602 41.231 8.081 16.346 29.11 14.142 43.435-2.938z" transform="translate(10)"/><path fill="url(#lti)" stroke="#E68C3F" stroke-width="3.75" d="M97.107 66.344c3.673-3.398 12.58-13.774 29.477-2.939 3.122 2.02 5.693 2.204 11.662 4.775 12.03 4.96 6.336 16.897-6.52 20.937-5.51 1.745-10.468 8.449-20.386 7.806-8.54-.46-10.744-6.06-15.978-9.091-9.275-5.234-10.652-12.305-5.602-16.07 5.051-3.765 6.98-5.143 7.347-5.418z" transform="translate(10)"/><path fill="#000" d="M133.186 57.712c-.092 5.234 2.48 9.458 5.877 9.458 3.306 0 6.153-4.224 6.245-9.366.091-5.234-2.48-9.459-5.878-9.459-3.397 0-6.152 4.225-6.244 9.367m-21.212.092c.459 4.316-1.194 7.989-3.582 8.356-2.387.276-4.683-2.938-5.142-7.254-.46-4.316 1.194-7.99 3.581-8.357 2.388-.275 4.684 2.939 5.143 7.255"/></g></svg>
49+ <h2 class="platform-name">Linux</h2>
50+ </div>
51+ <div class="download-cards">
52+ <a class="download-card" data-asset="x86_64.AppImage" href="#">
53+ <span class="card-arch">x86_64</span>
54+ <span class="card-format">AppImage</span>
55+ </a>
56+ </div>
57+ </section>
58+ </div>
59+
60+ <p class="releases-link">
61+ Looking for older versions? Check the
62+ <a href="https://github.com/pingdotgg/t3code/releases" target="_blank" rel="noopener noreferrer">
63+ GitHub releases page<span aria-hidden="true"> &#8599;</span>
64+ </a>
65+ </p>
66+</Layout>
67+
68+<script>
69+ import { fetchLatestRelease, RELEASES_URL } from "../lib/releases";
70+
71+ async function init() {
72+ const versionLabel = document.getElementById("version-label");
73+ const cards = document.querySelectorAll<HTMLAnchorElement>(".download-card");
74+
75+ try {
76+ const release = await fetchLatestRelease();
77+
78+ if (versionLabel && release.tag_name) {
79+ versionLabel.textContent = `Latest (${release.tag_name})`;
80+ }
81+
82+ const changelogLink = document.getElementById("changelog-link") as HTMLAnchorElement | null;
83+ if (changelogLink && release.html_url) {
84+ changelogLink.href = release.html_url;
85+ changelogLink.style.display = "";
86+ }
87+
88+ cards.forEach((card) => {
89+ const suffix = card.dataset.asset;
90+ if (!suffix) return;
91+
92+ const match = (release.assets ?? []).find((a) => a.name.endsWith(`-${suffix}`));
93+ if (match) {
94+ card.href = match.browser_download_url;
95+ } else {
96+ card.href = RELEASES_URL;
97+ }
98+ });
99+ } catch {
100+ if (versionLabel) {
101+ versionLabel.textContent = "Could not load release info.";
102+ }
103+ cards.forEach((card) => {
104+ card.href = RELEASES_URL;
105+ });
106+ }
107+ }
108+
109+ init();
110+</script>
111+
112+<style>
113+ .main {
114+ padding: 8vh 1.5rem 4rem;
115+ }
116+
117+ /* ── Heading ── */
118+
119+ .heading {
120+ font-weight: 500;
121+ font-size: clamp(1.75rem, 4vw, 2.75rem);
122+ letter-spacing: -0.035em;
123+ line-height: 1.15;
124+ text-align: center;
125+ margin-bottom: 0.5rem;
126+ opacity: 0;
127+ animation: fade-in 1s ease-out forwards;
128+ }
129+
130+ .subheading {
131+ font-size: 0.9rem;
132+ color: var(--fg-dim);
133+ margin-bottom: 3.5rem;
134+ opacity: 0;
135+ animation: fade-in 1s ease-out 0.1s forwards;
136+ display: flex;
137+ align-items: center;
138+ gap: 0.75rem;
139+ }
140+
141+ .changelog-link {
142+ color: var(--fg-muted);
143+ font-size: 0.8rem;
144+ text-decoration: underline;
145+ text-decoration-color: rgba(161, 161, 170, 0.4);
146+ text-underline-offset: 3px;
147+ transition: color 0.3s ease, text-decoration-color 0.3s ease;
148+ }
149+
150+ .changelog-link:hover {
151+ color: var(--fg);
152+ text-decoration-color: var(--fg);
153+ }
154+
155+ /* ── Platforms ── */
156+
157+ .platforms {
158+ width: 100%;
159+ max-width: 720px;
160+ display: flex;
161+ flex-direction: column;
162+ gap: 2.5rem;
163+ opacity: 0;
164+ animation: fade-in 1s ease-out 0.2s forwards;
165+ }
166+
167+ .platform-section {
168+ display: flex;
169+ flex-direction: column;
170+ gap: 0.75rem;
171+ }
172+
173+ .platform-header {
174+ display: flex;
175+ align-items: center;
176+ gap: 0.6rem;
177+ padding-bottom: 0.5rem;
178+ border-bottom: 1px solid var(--border);
179+ }
180+
181+ .platform-icon {
182+ width: 1.15em;
183+ height: 1.15em;
184+ color: var(--fg-muted);
185+ flex-shrink: 0;
186+ }
187+
188+ .platform-name {
189+ font-weight: 500;
190+ font-size: 1.1rem;
191+ letter-spacing: -0.01em;
192+ }
193+
194+ /* ── Download cards ── */
195+
196+ .download-cards {
197+ display: grid;
198+ grid-template-columns: repeat(2, 1fr);
199+ gap: 0.5rem;
200+ }
201+
202+ .download-card {
203+ display: flex;
204+ flex-direction: column;
205+ gap: 0.2rem;
206+ padding: 0.85rem 1rem;
207+ border-radius: 10px;
208+ border: 1px solid var(--border);
209+ background: rgba(255, 255, 255, 0.02);
210+ transition: background 0.2s ease, border-color 0.2s ease, transform 0.2s ease;
211+ }
212+
213+ .download-card:hover {
214+ background: rgba(255, 255, 255, 0.06);
215+ border-color: rgba(255, 255, 255, 0.15);
216+ transform: translateY(-1px);
217+ }
218+
219+ .download-card:active {
220+ transform: translateY(0);
221+ }
222+
223+ .card-arch {
224+ font-weight: 600;
225+ font-size: 0.95rem;
226+ }
227+
228+ .card-format {
229+ font-size: 0.8rem;
230+ color: var(--fg-dim);
231+ }
232+
233+ /* ── Releases link ── */
234+
235+ .releases-link {
236+ margin-top: 3rem;
237+ font-size: 0.825rem;
238+ color: var(--fg-dim);
239+ opacity: 0;
240+ animation: fade-in 1s ease-out 0.35s forwards;
241+ }
242+
243+ .releases-link a {
244+ color: var(--fg-muted);
245+ text-decoration: underline;
246+ text-decoration-color: rgba(161, 161, 170, 0.4);
247+ text-underline-offset: 3px;
248+ transition: color 0.3s ease, text-decoration-color 0.3s ease;
249+ }
250+
251+ .releases-link a:hover {
252+ color: var(--fg);
253+ text-decoration-color: var(--fg);
254+ }
255+
256+ /* ── Mobile ── */
257+
258+ @media (max-width: 640px) {
259+ .main {
260+ padding: 5vh 1.25rem 4rem;
261+ }
262+
263+ .platforms {
264+ padding: 0 0.25rem;
265+ }
266+
267+ .download-cards {
268+ grid-template-columns: 1fr;
269+ }
270+ }
271+</style>
apps/marketing/src/pages/index.astro+45−241
@@ -1,75 +1,30 @@
11 ---
2+import Layout from "../layouts/Layout.astro";
23 ---
34
4−<html lang="en">
5− <head>
6− <meta charset="UTF-8" />
7− <meta name="viewport" content="width=device-width, initial-scale=1.0" />
8− <meta name="description" content="T3 Code — The best way to code with AI." />
9− <link rel="preconnect" href="https://fonts.googleapis.com" />
10− <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
11− <link
12− href="https://fonts.googleapis.com/css2?family=DM+Sans:opsz,wght@9..40,400;9..40,500;9..40,600&display=swap"
13− rel="stylesheet"
14− />
15− <link rel="icon" href="/favicon.ico" sizes="48x48" />
16− <link rel="icon" type="image/png" href="/favicon-32x32.png" sizes="32x32" />
17− <link rel="icon" type="image/png" href="/favicon-16x16.png" sizes="16x16" />
18− <link rel="apple-touch-icon" href="/apple-touch-icon.png" />
19− <title>T3 Code</title>
20− </head>
21− <body>
22− <div class="page">
23− <nav class="nav">
24− <a href="/" class="nav-brand">
25− <img src="/icon.png" alt="T3" class="nav-icon" />
26− </a>
27− <a
28− href="https://github.com/pingdotgg/t3code"
29− target="_blank"
30− rel="noopener noreferrer"
31− class="nav-github"
32− >
33− GitHub
34− <span class="nav-github-arrow" aria-hidden="true">&#8599;</span>
35− </a>
36− </nav>
37−
38− <main class="main">
39− <h1 class="tagline">T3 Code is the best way to code with AI.</h1>
40−
41− <a
42− id="download-btn"
43− href="https://github.com/pingdotgg/t3code/releases"
44− class="hero-button"
45− >
46− <svg class="hero-button-icon hero-button-icon--apple" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 814 1000" aria-hidden="true"><path fill="currentColor" d="M788.1 340.9c-5.8 4.5-108.2 62.2-108.2 190.5 0 148.4 130.3 200.9 134.2 202.2-.6 3.2-20.7 71.9-68.7 141.9-42.8 61.6-87.5 123.1-155.5 123.1s-85.5-39.5-164-39.5c-76.5 0-103.7 40.8-165.9 40.8s-105.6-57-155.5-127C46.7 790.7 0 663 0 541.8c0-194.4 126.4-297.5 250.8-297.5 66.1 0 121.2 43.4 162.7 43.4 39.5 0 101.1-46 176.3-46 28.5 0 130.9 2.6 198.3 99.2zm-234-181.5c31.1-36.9 53.1-88.1 53.1-139.3 0-7.1-.6-14.3-1.9-20.1-50.6 1.9-110.8 33.7-147.1 75.8-28.5 32.4-55.1 83.6-55.1 135.5 0 7.8 1.3 15.6 1.9 18.1 3.2.6 8.4 1.3 13.6 1.3 45.4 0 102.5-30.4 135.5-71.3z"/></svg>
47− <svg class="hero-button-icon hero-button-icon--windows" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 88 88" aria-hidden="true"><path fill="currentColor" d="m0 12.402 35.687-4.86.016 34.423-35.67.203zm35.67 33.529.028 34.453L.028 75.48.026 45.7zm4.326-39.025L87.314 0v41.527l-47.318.376zm47.329 39.349-.011 41.34-47.318-6.678-.066-34.739z"/></svg>
48− <svg class="hero-button-icon hero-button-icon--linux" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 295" aria-hidden="true"><defs><linearGradient id="lt0" x1="48.548%" x2="51.047%" y1="115.276%" y2="41.364%"><stop offset="0%" stop-color="#FFEED7"/><stop offset="100%" stop-color="#BDBFC2"/></linearGradient><linearGradient id="lt6" x1="49.984%" x2="49.984%" y1="89.845%" y2="40.632%"><stop offset="0%" stop-color="#FFEED7"/><stop offset="100%" stop-color="#BDBFC2"/></linearGradient><linearGradient id="lt8" x1="49.841%" x2="50.241%" y1="13.229%" y2="94.673%"><stop offset="0%" stop-color="#FFF" stop-opacity=".8"/><stop offset="100%" stop-color="#FFF" stop-opacity="0"/></linearGradient><linearGradient id="ltc" x1="53.467%" x2="38.949%" y1="48.921%" y2="98.1%"><stop offset="0%" stop-color="#FFA63F"/><stop offset="100%" stop-color="#FF0"/></linearGradient><linearGradient id="lte" x1="30.581%" x2="65.887%" y1="34.024%" y2="89.175%"><stop offset="0%" stop-color="#FFA63F"/><stop offset="100%" stop-color="#FF0"/></linearGradient><linearGradient id="lti" x1="49.733%" x2="50.558%" y1="17.609%" y2="99.385%"><stop offset="0%" stop-color="#FFA63F"/><stop offset="100%" stop-color="#FF0"/></linearGradient></defs><g fill="none"><path fill="#000" d="M63.213 215.474c-11.387-16.346-13.591-69.606 12.947-102.39C89.292 97.383 92.69 86.455 93.7 71.67c.734-16.805-11.846-66.851 35.537-70.616 48.027-3.857 45.364 43.526 45.088 68.596-.183 21.12 15.52 33.15 26.355 49.68 19.927 30.303 18.274 82.461-3.765 110.745-27.916 35.354-51.791 20.018-67.678 21.304-29.752 1.745-30.762 17.54-66.024-35.905"/><path fill="url(#lt6)" d="M81.027 145.684c6.52-14.785 20.386-40.772 20.662-60.883 0-15.978 47.843-19.835 51.7-3.856 3.856 15.978 13.59 39.853 19.834 51.424 6.245 11.478 24.335 48.118 5.051 80.074-17.356 28.284-69.973 50.69-98.073-3.856-9.55-18.917-7.806-42.333.826-62.903"/><path fill="url(#lt8)" d="M166.428 151.285c0 16.162-15.519 37.1-42.15 36.916-27.456.183-39.118-20.754-39.118-36.916 0-16.161 18.182-29.293 40.588-29.293 22.498.092 40.68 13.132 40.68 29.293"/><path fill="#000" stroke="#000" stroke-width=".977" d="M176.805 117.86c13.59 11.02 38.292 49.587 2.204 74.748-11.846 7.806 10.468 32.508 23.049 19.927 43.618-43.894-1.102-94.308-16.53-111.664-13.774-15.151-25.987 3.49-8.723 16.989z"/><path fill="#000" stroke="#000" stroke-width="1.25" d="M79.925 122.727c-8.907 14.509-30.211 48.669-1.652 66.484 38.384 23.6 27.548 47.108-7.53 25.895-49.404-29.568-5.97-89.257 13.774-112.03 22.59-25.529 4.316 4.683-4.592 19.65z"/><path fill="url(#ltc)" stroke="#E68C3F" stroke-width="6.25" d="M51.835 258.542c-20.57-10.928-50.414 2.112-39.578-27.457 2.204-6.704-3.214-16.805.275-23.325 4.133-7.989 13.04-6.244 18.366-11.57 5.234-5.51 8.54-15.06 18.366-13.59 9.734 1.468 16.254 13.406 23.049 28.099 5.05 10.468 22.865 25.253 21.672 37.007-1.47 17.998-21.948 21.396-42.15 10.836z" transform="translate(10)"/><path fill="url(#lte)" stroke="#E68C3F" stroke-width="6.251" d="M194.445 253.49c15.06-18.273 48.578-14.508 25.988-39.577-4.775-5.418-3.306-16.989-9.183-21.947-6.887-6.061-14.509-1.102-21.488-4.224-6.979-3.398-14.325-9.918-22.865-5.327-8.54 4.684-9.459 16.805-10.285 32.783-.735 11.479-11.203 30.671-5.602 41.231 8.081 16.346 29.11 14.142 43.435-2.938z" transform="translate(10)"/><path fill="url(#lti)" stroke="#E68C3F" stroke-width="3.75" d="M97.107 66.344c3.673-3.398 12.58-13.774 29.477-2.939 3.122 2.02 5.693 2.204 11.662 4.775 12.03 4.96 6.336 16.897-6.52 20.937-5.51 1.745-10.468 8.449-20.386 7.806-8.54-.46-10.744-6.06-15.978-9.091-9.275-5.234-10.652-12.305-5.602-16.07 5.051-3.765 6.98-5.143 7.347-5.418z" transform="translate(10)"/><path fill="#000" d="M133.186 57.712c-.092 5.234 2.48 9.458 5.877 9.458 3.306 0 6.153-4.224 6.245-9.366.091-5.234-2.48-9.459-5.878-9.459-3.397 0-6.152 4.225-6.244 9.367m-21.212.092c.459 4.316-1.194 7.989-3.582 8.356-2.387.276-4.683-2.938-5.142-7.254-.46-4.316 1.194-7.99 3.581-8.357 2.388-.275 4.684 2.939 5.143 7.255"/></g></svg>
49− <span id="download-label">Download now</span>
50− </a>
51−
52− <div class="screenshot-wrap">
53− <img src="/screenshot.jpeg" alt="T3 Code" class="screenshot" />
54− </div>
55−
56− </main>
57−
58− <footer class="footer">
59− <span class="footer-copy">&copy; {new Date().getFullYear()} T3 Tools Inc</span>
60− <div class="footer-links">
61− <a href="https://github.com/pingdotgg/t3code" target="_blank" rel="noopener noreferrer">GitHub</a>
62− <a href="https://discord.gg/jn4EGJjrvv" target="_blank" rel="noopener noreferrer">Discord</a>
63− </div>
64− </footer>
65− </div>
66− </body>
67−</html>
5+<Layout>
6+ <h1 class="tagline">T3 Code is the best way to code with AI.</h1>
7+
8+ <a
9+ id="download-btn"
10+ href="https://github.com/pingdotgg/t3code/releases"
11+ class="hero-button"
12+ >
13+ <svg class="hero-button-icon hero-button-icon--apple" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 814 1000" aria-hidden="true"><path fill="currentColor" d="M788.1 340.9c-5.8 4.5-108.2 62.2-108.2 190.5 0 148.4 130.3 200.9 134.2 202.2-.6 3.2-20.7 71.9-68.7 141.9-42.8 61.6-87.5 123.1-155.5 123.1s-85.5-39.5-164-39.5c-76.5 0-103.7 40.8-165.9 40.8s-105.6-57-155.5-127C46.7 790.7 0 663 0 541.8c0-194.4 126.4-297.5 250.8-297.5 66.1 0 121.2 43.4 162.7 43.4 39.5 0 101.1-46 176.3-46 28.5 0 130.9 2.6 198.3 99.2zm-234-181.5c31.1-36.9 53.1-88.1 53.1-139.3 0-7.1-.6-14.3-1.9-20.1-50.6 1.9-110.8 33.7-147.1 75.8-28.5 32.4-55.1 83.6-55.1 135.5 0 7.8 1.3 15.6 1.9 18.1 3.2.6 8.4 1.3 13.6 1.3 45.4 0 102.5-30.4 135.5-71.3z"/></svg>
14+ <svg class="hero-button-icon hero-button-icon--windows" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 88 88" aria-hidden="true"><path fill="currentColor" d="m0 12.402 35.687-4.86.016 34.423-35.67.203zm35.67 33.529.028 34.453L.028 75.48.026 45.7zm4.326-39.025L87.314 0v41.527l-47.318.376zm47.329 39.349-.011 41.34-47.318-6.678-.066-34.739z"/></svg>
15+ <svg class="hero-button-icon hero-button-icon--linux" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 295" aria-hidden="true"><defs><linearGradient id="lt0" x1="48.548%" x2="51.047%" y1="115.276%" y2="41.364%"><stop offset="0%" stop-color="#FFEED7"/><stop offset="100%" stop-color="#BDBFC2"/></linearGradient><linearGradient id="lt6" x1="49.984%" x2="49.984%" y1="89.845%" y2="40.632%"><stop offset="0%" stop-color="#FFEED7"/><stop offset="100%" stop-color="#BDBFC2"/></linearGradient><linearGradient id="lt8" x1="49.841%" x2="50.241%" y1="13.229%" y2="94.673%"><stop offset="0%" stop-color="#FFF" stop-opacity=".8"/><stop offset="100%" stop-color="#FFF" stop-opacity="0"/></linearGradient><linearGradient id="ltc" x1="53.467%" x2="38.949%" y1="48.921%" y2="98.1%"><stop offset="0%" stop-color="#FFA63F"/><stop offset="100%" stop-color="#FF0"/></linearGradient><linearGradient id="lte" x1="30.581%" x2="65.887%" y1="34.024%" y2="89.175%"><stop offset="0%" stop-color="#FFA63F"/><stop offset="100%" stop-color="#FF0"/></linearGradient><linearGradient id="lti" x1="49.733%" x2="50.558%" y1="17.609%" y2="99.385%"><stop offset="0%" stop-color="#FFA63F"/><stop offset="100%" stop-color="#FF0"/></linearGradient></defs><g fill="none"><path fill="#000" d="M63.213 215.474c-11.387-16.346-13.591-69.606 12.947-102.39C89.292 97.383 92.69 86.455 93.7 71.67c.734-16.805-11.846-66.851 35.537-70.616 48.027-3.857 45.364 43.526 45.088 68.596-.183 21.12 15.52 33.15 26.355 49.68 19.927 30.303 18.274 82.461-3.765 110.745-27.916 35.354-51.791 20.018-67.678 21.304-29.752 1.745-30.762 17.54-66.024-35.905"/><path fill="url(#lt6)" d="M81.027 145.684c6.52-14.785 20.386-40.772 20.662-60.883 0-15.978 47.843-19.835 51.7-3.856 3.856 15.978 13.59 39.853 19.834 51.424 6.245 11.478 24.335 48.118 5.051 80.074-17.356 28.284-69.973 50.69-98.073-3.856-9.55-18.917-7.806-42.333.826-62.903"/><path fill="url(#lt8)" d="M166.428 151.285c0 16.162-15.519 37.1-42.15 36.916-27.456.183-39.118-20.754-39.118-36.916 0-16.161 18.182-29.293 40.588-29.293 22.498.092 40.68 13.132 40.68 29.293"/><path fill="#000" stroke="#000" stroke-width=".977" d="M176.805 117.86c13.59 11.02 38.292 49.587 2.204 74.748-11.846 7.806 10.468 32.508 23.049 19.927 43.618-43.894-1.102-94.308-16.53-111.664-13.774-15.151-25.987 3.49-8.723 16.989z"/><path fill="#000" stroke="#000" stroke-width="1.25" d="M79.925 122.727c-8.907 14.509-30.211 48.669-1.652 66.484 38.384 23.6 27.548 47.108-7.53 25.895-49.404-29.568-5.97-89.257 13.774-112.03 22.59-25.529 4.316 4.683-4.592 19.65z"/><path fill="url(#ltc)" stroke="#E68C3F" stroke-width="6.25" d="M51.835 258.542c-20.57-10.928-50.414 2.112-39.578-27.457 2.204-6.704-3.214-16.805.275-23.325 4.133-7.989 13.04-6.244 18.366-11.57 5.234-5.51 8.54-15.06 18.366-13.59 9.734 1.468 16.254 13.406 23.049 28.099 5.05 10.468 22.865 25.253 21.672 37.007-1.47 17.998-21.948 21.396-42.15 10.836z" transform="translate(10)"/><path fill="url(#lte)" stroke="#E68C3F" stroke-width="6.251" d="M194.445 253.49c15.06-18.273 48.578-14.508 25.988-39.577-4.775-5.418-3.306-16.989-9.183-21.947-6.887-6.061-14.509-1.102-21.488-4.224-6.979-3.398-14.325-9.918-22.865-5.327-8.54 4.684-9.459 16.805-10.285 32.783-.735 11.479-11.203 30.671-5.602 41.231 8.081 16.346 29.11 14.142 43.435-2.938z" transform="translate(10)"/><path fill="url(#lti)" stroke="#E68C3F" stroke-width="3.75" d="M97.107 66.344c3.673-3.398 12.58-13.774 29.477-2.939 3.122 2.02 5.693 2.204 11.662 4.775 12.03 4.96 6.336 16.897-6.52 20.937-5.51 1.745-10.468 8.449-20.386 7.806-8.54-.46-10.744-6.06-15.978-9.091-9.275-5.234-10.652-12.305-5.602-16.07 5.051-3.765 6.98-5.143 7.347-5.418z" transform="translate(10)"/><path fill="#000" d="M133.186 57.712c-.092 5.234 2.48 9.458 5.877 9.458 3.306 0 6.153-4.224 6.245-9.366.091-5.234-2.48-9.459-5.878-9.459-3.397 0-6.152 4.225-6.244 9.367m-21.212.092c.459 4.316-1.194 7.989-3.582 8.356-2.387.276-4.683-2.938-5.142-7.254-.46-4.316 1.194-7.99 3.581-8.357 2.388-.275 4.684 2.939 5.143 7.255"/></g></svg>
16+ <span id="download-label">Download now</span>
17+ </a>
18+
19+ <a href="/download" class="other-platforms">Other platforms</a>
20+
21+ <div class="screenshot-wrap">
22+ <img src="/screenshot.jpeg" alt="T3 Code" class="screenshot" />
23+ </div>
24+</Layout>
6825
6926 <script>
70− const REPO = "pingdotgg/t3code";
71− const RELEASES_URL = `https://github.com/${REPO}/releases`;
72− const API_URL = `https://api.github.com/repos/${REPO}/releases/latest`;
27+ import { fetchLatestRelease, RELEASES_URL, type ReleaseAsset } from "../lib/releases";
7328
7429 type Platform = { os: "mac" | "win" | "linux"; label: string; arch?: string };
7530
@@ -87,10 +42,7 @@ · 11 unmodified lines
8742 return null;
8843 }
8944
90− function pickAsset(
91− assets: Array<{ name: string; browser_download_url: string }>,
92− platform: Platform,
93− ): string | null {
45+ function pickAsset(assets: ReleaseAsset[], platform: Platform): string | null {
9446 if (platform.os === "win") {
9547 return (
9648 assets.find((a) => a.name.endsWith("-x64.exe"))
@@ -125,16 +77,8 @@ · 28 unmodified lines
12577 label.textContent = platform.label;
12678
12779 try {
128− const cached = sessionStorage.getItem("t3code-latest-release");
129− const data = cached
130− ? JSON.parse(cached)
131− : await fetch(API_URL).then((r) => r.json());
132−
133− if (!cached && data?.assets) {
134− sessionStorage.setItem("t3code-latest-release", JSON.stringify(data));
135− }
136−
137− const url = pickAsset(data.assets ?? [], platform);
80+ const release = await fetchLatestRelease();
81+ const url = pickAsset(release.assets ?? [], platform);
13882 if (url) {
13983 btn.href = url;
14084 btn.removeAttribute("target");
@@ -149,120 +93,7 @@ · 8 unmodified lines
14993 </script>
15094
15195 <style>
152− :root {
153− --bg: #09090b;
154− --fg: #fafafa;
155− --fg-muted: #a1a1aa;
156− --fg-dim: #71717a;
157− --border: rgba(255, 255, 255, 0.08);
158− }
159−
160− * {
161− box-sizing: border-box;
162− margin: 0;
163− padding: 0;
164− }
165−
166− html {
167− color-scheme: dark;
168− }
169−
170− body {
171− background: var(--bg);
172− color: var(--fg);
173− font-family: "DM Sans", -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
174− -webkit-font-smoothing: antialiased;
175− -moz-osx-font-smoothing: grayscale;
176− min-height: 100vh;
177− overflow-x: hidden;
178− }
179−
180− body::after {
181− content: "";
182− position: fixed;
183− inset: 0;
184− pointer-events: none;
185− opacity: 0.035;
186− background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
187− background-repeat: repeat;
188− background-size: 256px 256px;
189− z-index: 9999;
190− }
191−
192− a {
193− color: inherit;
194− text-decoration: none;
195− }
196−
197− img {
198− display: block;
199− max-width: 100%;
200− }
201−
202− /* ── Page ── */
203−
204− .page {
205− min-height: 100vh;
206− display: flex;
207− flex-direction: column;
208− overflow-x: hidden;
209− }
210−
211− /* ── Navbar ── */
212−
213− .nav {
214− display: flex;
215− align-items: center;
216− justify-content: space-between;
217− padding: 1.5rem 2.5rem;
218− opacity: 0.5;
219− transition: opacity 0.4s ease;
220− }
221−
222− .nav:hover {
223− opacity: 1;
224− }
225−
226− .nav-brand {
227− display: flex;
228− align-items: center;
229− }
230−
231− .nav-icon {
232− width: 28px;
233− height: 28px;
234− border-radius: 6px;
235− }
236−
237− .nav-github {
238− display: flex;
239− align-items: center;
240− gap: 0.35rem;
241− color: var(--fg-muted);
242− font-size: 0.875rem;
243− transition: color 0.3s ease;
244− }
245−
246− .nav-github:hover {
247− color: var(--fg);
248− }
249−
250− .nav-github-arrow {
251− display: inline-block;
252− transition: transform 0.3s ease;
253− }
254−
255− .nav-github:hover .nav-github-arrow {
256− transform: translateX(2px) translateY(-1px);
257− }
258−
259− /* ── Main content ── */
260−
26196 .main {
262− flex: 1;
263− display: flex;
264− flex-direction: column;
265− align-items: center;
26697 padding-top: 10vh;
26798 }
26899
@@ -291,7 +122,7 @@ · 22 unmodified lines
291122 border-radius: 999px;
292123 font-weight: 600;
293124 font-size: 0.9rem;
294− margin-bottom: 5vh;
125+ margin-bottom: 1.25rem;
295126 opacity: 0;
296127 animation: fade-in 1s ease-out 0.15s forwards;
297128 transition: transform 0.25s ease, box-shadow 0.25s ease;
@@ -325,6 +156,25 @@ · 27 unmodified lines
325156 display: block;
326157 }
327158
159+ /* ── Other platforms link ── */
160+
161+ .other-platforms {
162+ font-size: 0.825rem;
163+ color: var(--fg-dim);
164+ margin-bottom: 5vh;
165+ opacity: 0;
166+ animation: fade-in 1s ease-out 0.3s forwards;
167+ transition: color 0.3s ease;
168+ text-decoration: underline;
169+ text-decoration-color: rgba(113, 113, 122, 0.4);
170+ text-underline-offset: 3px;
171+ }
172+
173+ .other-platforms:hover {
174+ color: var(--fg-muted);
175+ text-decoration-color: var(--fg-muted);
176+ }
177+
328178 /* ── Screenshot ── */
329179
330180 .screenshot-wrap {
@@ -358,44 +208,6 @@ · 27 unmodified lines
358208 pointer-events: none;
359209 }
360210
361− /* ── Footer ── */
362−
363− .footer {
364− display: flex;
365− align-items: center;
366− justify-content: space-between;
367− padding: 1.5rem 2.5rem;
368− font-size: 0.8rem;
369− color: var(--fg-dim);
370− }
371−
372− .footer-links {
373− display: flex;
374− gap: 1.25rem;
375− }
376−
377− .footer-links a {
378− color: var(--fg-dim);
379− transition: color 0.3s ease;
380− }
381−
382− .footer-links a:hover {
383− color: var(--fg);
384− }
385−
386− /* ── Animations ── */
387−
388− @keyframes fade-in {
389− from {
390− opacity: 0;
391− transform: translateY(8px);
392− }
393− to {
394− opacity: 1;
395− transform: translateY(0);
396− }
397− }
398−
399211 @keyframes screenshot-in {
400212 from {
401213 opacity: 0;
@@ -410,10 +222,6 @@ · 8 unmodified lines
410222 /* ── Mobile ── */
411223
412224 @media (max-width: 640px) {
413− .nav {
414− padding: 1.25rem 1.25rem;
415− }
416−
417225 .main {
418226 padding-top: 7vh;
419227 }
@@ -425,9 +233,5 @@ · 5 unmodified lines
425233 .screenshot-wrap {
426234 width: 95vw;
427235 }
428−
429− .footer {
430− padding: 1.25rem;
431− }
432236 }
433237 </style>