11---
22// Stitch baseline: dark hero screen codegen, adapted for Astro + Fontsource + truthful content
33const GITHUB = ' https://github.com/InterestingSoftware/SproutGit' ;
4+ const RELEASES_API = ' https://api.github.com/repos/InterestingSoftware/SproutGit/releases/latest' ;
5+ const RELEASES_PAGE = ` ${GITHUB }/releases/latest ` ;
46---
57
68<!-- Nav -->
79<header class =" fixed top-0 left-0 right-0 z-50 transition-all duration-300" id =" site-nav" >
810 <nav class =" flex justify-between items-center w-full px-12 py-5 max-w-screen-2xl mx-auto" >
9- <a href =" /" class =" flex items-center gap-2.5 no-underline text-[var (--sg-text)] " >
11+ <a href =" /" class =" flex items-center gap-2.5 no-underline text-(--sg-text)" >
1012 <img src =" /logo.svg" alt =" SproutGit logo" width =" 28" height =" 28" class =" w-7 h-7" />
1113 <span class =" font-bold text-lg tracking-tight" style =" letter-spacing:-0.025em;" >SproutGit</span >
1214 </a >
1315
1416 <div class =" hidden md:flex items-center gap-10" >
15- <a href =" #why" class =" text-sm font-medium text-[var (--sg-text-dim)] hover:text-[var (--sg-text)] transition-colors" >Why worktrees?</a >
16- <a href =" #features" class =" text-sm font-medium text-[var (--sg-text-dim)] hover:text-[var (--sg-text)] transition-colors" >Features</a >
17+ <a href =" #why" class =" text-sm font-medium text-(--sg-text-dim) hover:text-(--sg-text) transition-colors" >Why worktrees?</a >
18+ <a href =" #features" class =" text-sm font-medium text-(--sg-text-dim) hover:text-(--sg-text) transition-colors" >Features</a >
1719 <a href ={ GITHUB } target =" _blank" rel =" noopener noreferrer"
18- class =" flex items-center gap-2 text-sm font-medium px-4 py-2 rounded-lg border border-[var (--sg-border)] text-[var (--sg-text-dim)] hover:border-[var (--sg-primary)] hover:text-[var (--sg-primary)] transition-all" >
20+ class =" flex items-center gap-2 text-sm font-medium px-4 py-2 rounded-lg border border-(--sg-border) text-(--sg-text-dim) hover:border-(--sg-primary) hover:text-(--sg-primary) transition-all" >
1921 <svg width =" 16" height =" 16" viewBox =" 0 0 24 24" fill =" currentColor" aria-hidden =" true" >
2022 <path d =" M12 2C6.477 2 2 6.477 2 12c0 4.42 2.865 8.166 6.839 9.489.5.092.682-.217.682-.482 0-.237-.008-.866-.013-1.7-2.782.603-3.369-1.342-3.369-1.342-.454-1.155-1.11-1.462-1.11-1.462-.908-.62.069-.608.069-.608 1.003.07 1.531 1.03 1.531 1.03.892 1.529 2.341 1.087 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.11-4.555-4.943 0-1.091.39-1.984 1.029-2.683-.103-.253-.446-1.27.098-2.647 0 0 .84-.269 2.75 1.025A9.578 9.578 0 0 1 12 6.836c.85.004 1.705.114 2.504.336 1.909-1.294 2.747-1.025 2.747-1.025.546 1.377.203 2.394.1 2.647.64.699 1.028 1.592 1.028 2.683 0 3.842-2.339 4.687-4.566 4.935.359.309.678.919.678 1.852 0 1.336-.012 2.415-.012 2.743 0 .267.18.578.688.48C19.138 20.163 22 16.418 22 12c0-5.523-4.477-10-10-10z" />
2123 </svg >
@@ -54,7 +56,7 @@ const GITHUB = 'https://github.com/InterestingSoftware/SproutGit';
5456 </div >
5557
5658 <!-- Headline — Stitch layout with gradient text -->
57- <h1 data-animate class =" font-bold leading-[0.92] tracking-tighter text-[var (--sg-text)] "
59+ <h1 data-animate class =" font-bold leading-[0.92] tracking-tighter text-(--sg-text)"
5860 style =" font-size: clamp(3rem, 7vw, 5.5rem); letter-spacing: -0.045em;" >
5961 Git built for<br >
6062 <span style =" background: linear-gradient(135deg, var(--sg-primary) 0%, var(--sg-mint) 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;" >
@@ -68,22 +70,86 @@ const GITHUB = 'https://github.com/InterestingSoftware/SproutGit';
6870 </p >
6971
7072 <!-- CTAs -->
71- <div data-animate class =" flex flex-wrap gap-4 mt-2" >
72- <a href ={ GITHUB } target =" _blank" rel =" noopener noreferrer"
73- class =" flex items-center gap-3 px-8 py-4 rounded-xl text-lg font-bold text-white transition-all hover:-translate-y-0.5"
74- style =" background: var(--sg-primary); box-shadow: 0 0 0 0 color-mix(in srgb, var(--sg-primary) 30%, transparent);" >
75- <svg width =" 18" height =" 18" viewBox =" 0 0 24 24" fill =" currentColor" aria-hidden =" true" >
76- <path d =" M12 2C6.477 2 2 6.477 2 12c0 4.42 2.865 8.166 6.839 9.489.5.092.682-.217.682-.482 0-.237-.008-.866-.013-1.7-2.782.603-3.369-1.342-3.369-1.342-.454-1.155-1.11-1.462-1.11-1.462-.908-.62.069-.608.069-.608 1.003.07 1.531 1.03 1.531 1.03.892 1.529 2.341 1.087 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.11-4.555-4.943 0-1.091.39-1.984 1.029-2.683-.103-.253-.446-1.27.098-2.647 0 0 .84-.269 2.75 1.025A9.578 9.578 0 0 1 12 6.836c.85.004 1.705.114 2.504.336 1.909-1.294 2.747-1.025 2.747-1.025.546 1.377.203 2.394.1 2.647.64.699 1.028 1.592 1.028 2.683 0 3.842-2.339 4.687-4.566 4.935.359.309.678.919.678 1.852 0 1.336-.012 2.415-.012 2.743 0 .267.18.578.688.48C19.138 20.163 22 16.418 22 12c0-5.523-4.477-10-10-10z" />
77- </svg >
78- View on GitHub
79- </a >
80- <a href =" #features"
81- class =" flex items-center gap-2 px-8 py-4 rounded-xl text-lg font-bold transition-all"
82- style =" color: var(--sg-primary); border: 1.5px solid color-mix(in srgb, var(--sg-primary) 30%, transparent);" >
83- See features ↓
84- </a >
73+ <div data-animate class =" hero-downloads mt-2" data-download-root data-api ={ RELEASES_API } data-fallback ={ RELEASES_PAGE } >
74+ <div class =" flex flex-wrap gap-4" >
75+ <a href ={ RELEASES_PAGE }
76+ class =" flex items-center gap-3 px-8 py-4 rounded-xl text-lg font-bold text-white transition-all hover:-translate-y-0.5"
77+ style =" background: var(--sg-primary); box-shadow: 0 0 0 0 color-mix(in srgb, var(--sg-primary) 30%, transparent);"
78+ data-primary-download >
79+ Download preview
80+ </a >
81+ <a href =" #features"
82+ class =" flex items-center gap-2 px-8 py-4 rounded-xl text-lg font-bold transition-all"
83+ style =" color: var(--sg-primary); border: 1.5px solid color-mix(in srgb, var(--sg-primary) 30%, transparent);" >
84+ See features ↓
85+ </a >
86+ <div class =" hero-download-menu-wrap" >
87+ <button type =" button"
88+ class =" download-platforms-trigger flex items-center gap-2 px-6 py-4 rounded-xl text-base font-bold transition-all"
89+ style =" color: var(--sg-text); border: 1.5px solid color-mix(in srgb, var(--sg-border) 85%, transparent); background: color-mix(in srgb, var(--sg-surface) 72%, transparent);"
90+ aria-expanded =" false"
91+ aria-controls =" platform-downloads-menu"
92+ data-platforms-trigger >
93+ Other platforms
94+ <span aria-hidden =" true" >▾</span >
95+ </button >
96+ <div id =" platform-downloads-menu" class =" download-platforms-menu" hidden data-platforms-menu >
97+ <a href ={ RELEASES_PAGE } target =" _blank" rel =" noopener noreferrer" data-platform-link =" macos" >macOS</a >
98+ <a href ={ RELEASES_PAGE } target =" _blank" rel =" noopener noreferrer" data-platform-link =" windows" >Windows</a >
99+ <a href ={ RELEASES_PAGE } target =" _blank" rel =" noopener noreferrer" data-platform-link =" linux" >Linux</a >
100+ </div >
101+ </div >
102+ </div >
103+
104+ <div class =" download-links-row" >
105+ <span class =" download-links-label" data-download-status >Loading latest release…</span >
106+ <a href ={ GITHUB } target =" _blank" rel =" noopener noreferrer" >GitHub</a >
107+ <a href ={ RELEASES_PAGE } target =" _blank" rel =" noopener noreferrer" >All releases</a >
108+ <a href ={ RELEASES_PAGE } target =" _blank" rel =" noopener noreferrer" data-inline-platform-link =" macos" >macOS</a >
109+ <a href ={ RELEASES_PAGE } target =" _blank" rel =" noopener noreferrer" data-inline-platform-link =" windows" >Windows</a >
110+ <a href ={ RELEASES_PAGE } target =" _blank" rel =" noopener noreferrer" data-inline-platform-link =" linux" >Linux</a >
111+ </div >
85112 </div >
86113
114+ <dialog class =" download-warning-dialog" data-mac-dialog >
115+ <div class =" download-warning-panel" >
116+ <div class =" download-warning-header" >
117+ <div >
118+ <h2 >Unsigned macOS preview build</h2 >
119+ </div >
120+ <button type =" button" class =" download-warning-close" aria-label =" Close download warning" data-close-mac-dialog >✕</button >
121+ </div >
122+
123+ <p >
124+ While we are in early development, SproutGit for macOS is not signed or notarized yet.
125+ Because of that, macOS may show warnings such as "can’t be opened" or "is damaged and can’t be opened," even when downloaded from our official release page.
126+ </p >
127+ <p >
128+ By launch, we plan to ship signed builds so this warning goes away.
129+ For now, if you downloaded SproutGit from our official site or GitHub releases, follow the steps below to allow this app.
130+ </p >
131+
132+ <ol >
133+ <li >Open the downloaded `.dmg` and drag SproutGit into Applications.</li >
134+ <li >In Finder, open Applications and Control-click SproutGit.</li >
135+ <li >Choose Open, then confirm Open again in the system dialog.</li >
136+ <li >If it still says "is damaged", remove quarantine in Terminal: <code >xattr -dr com.apple.quarantine /Applications/SproutGit.app</code ></li >
137+ <li >Then try opening SproutGit again (or run: <code >open /Applications/SproutGit.app</code >).</li >
138+ </ol >
139+
140+ <div class =" download-warning-actions" >
141+ <button type =" button" class =" download-warning-secondary" data-close-mac-dialog >Cancel</button >
142+ <a href ={ RELEASES_PAGE }
143+ target =" _blank"
144+ rel =" noopener noreferrer"
145+ class =" download-warning-primary"
146+ data-mac-download-link >
147+ Continue to download
148+ </a >
149+ </div >
150+ </div >
151+ </dialog >
152+
87153 <!-- Meta strip -->
88154 <div data-animate class =" flex items-center gap-8 mt-6 opacity-60" >
89155 <div class =" flex flex-col" >
@@ -99,7 +165,7 @@ const GITHUB = 'https://github.com/InterestingSoftware/SproutGit';
99165 </div >
100166
101167 <!-- Right: Graph panel — adapted from Stitch SVG + floating cards -->
102- <div class =" lg:col-span-5 relative flex justify-center items-center h-[560px] " data-animate data-graph >
168+ <div class =" lg:col-span-5 relative flex justify-center items-center h-140 " data-animate data-graph >
103169
104170 <!-- Panel -->
105171 <div class =" absolute inset-0 rounded-3xl overflow-hidden border flex items-center justify-center p-6"
@@ -192,6 +258,193 @@ const GITHUB = 'https://github.com/InterestingSoftware/SproutGit';
192258 </section >
193259</main >
194260
261+ <script is:inline >
262+ (() => {
263+ const root = document.querySelector('[data-download-root]');
264+ if (!root) return;
265+
266+ const apiUrl = root.getAttribute('data-api') || 'https://api.github.com/repos/InterestingSoftware/SproutGit/releases/latest';
267+ const fallbackUrl = root.getAttribute('data-fallback') || 'https://github.com/InterestingSoftware/SproutGit/releases/latest';
268+ const primaryLink = root.querySelector('[data-primary-download]');
269+ const trigger = root.querySelector('[data-platforms-trigger]');
270+ const menu = root.querySelector('[data-platforms-menu]');
271+ const status = root.querySelector('[data-download-status]');
272+ const dialog = document.querySelector('[data-mac-dialog]');
273+ const macDownloadLink = document.querySelector('[data-mac-download-link]');
274+ const closeButtons = document.querySelectorAll('[data-close-mac-dialog]');
275+
276+ const menuLinks = {
277+ macos: root.querySelector('[data-platform-link="macos"]'),
278+ windows: root.querySelector('[data-platform-link="windows"]'),
279+ linux: root.querySelector('[data-platform-link="linux"]'),
280+ };
281+
282+ const inlineLinks = {
283+ macos: root.querySelector('[data-inline-platform-link="macos"]'),
284+ windows: root.querySelector('[data-inline-platform-link="windows"]'),
285+ linux: root.querySelector('[data-inline-platform-link="linux"]'),
286+ };
287+
288+ const detectOS = () => {
289+ const rawUserAgentData = 'userAgentData' in navigator ? navigator['userAgentData'] : undefined;
290+ /** @type {{ platform?: string } | undefined} */
291+ const userAgentData = rawUserAgentData && typeof rawUserAgentData === 'object' ? rawUserAgentData : undefined;
292+ const platform = (userAgentData && typeof userAgentData.platform === 'string'
293+ ? userAgentData.platform
294+ : navigator.platform || navigator.userAgent || '').toLowerCase();
295+
296+ if (platform.includes('mac')) return 'macos';
297+ if (platform.includes('win')) return 'windows';
298+ if (platform.includes('linux') || platform.includes('x11')) return 'linux';
299+ return 'unknown';
300+ };
301+
302+ const assetMatchers = {
303+ macos: (/** @type {{ name: string }} */ asset) => asset.name.endsWith('.dmg') && /aarch64|arm64/i.test(asset.name),
304+ windows: (/** @type {{ name: string }} */ asset) => asset.name.endsWith('.msi'),
305+ linux: (/** @type {{ name: string }} */ asset) => asset.name.endsWith('.AppImage'),
306+ };
307+
308+ const platformLabels = {
309+ macos: 'macOS',
310+ windows: 'Windows',
311+ linux: 'Linux',
312+ unknown: 'your OS',
313+ };
314+
315+ const links = {
316+ macos: fallbackUrl,
317+ windows: fallbackUrl,
318+ linux: fallbackUrl,
319+ };
320+
321+ const recommended = detectOS();
322+
323+ const setStatus = (/** @type {string} */ message) => {
324+ if (status) status.textContent = message;
325+ };
326+
327+ const setPrimaryLink = (/** @type {string} */ href, /** @type {string} */ label) => {
328+ if (!(primaryLink instanceof HTMLAnchorElement)) return;
329+ primaryLink.href = href;
330+ primaryLink.textContent = label;
331+ };
332+
333+ const syncLinks = () => {
334+ if (menuLinks.macos instanceof HTMLAnchorElement) menuLinks.macos.href = links.macos;
335+ if (menuLinks.windows instanceof HTMLAnchorElement) menuLinks.windows.href = links.windows;
336+ if (menuLinks.linux instanceof HTMLAnchorElement) menuLinks.linux.href = links.linux;
337+
338+ if (inlineLinks.macos instanceof HTMLAnchorElement) inlineLinks.macos.href = links.macos;
339+ if (inlineLinks.windows instanceof HTMLAnchorElement) inlineLinks.windows.href = links.windows;
340+ if (inlineLinks.linux instanceof HTMLAnchorElement) inlineLinks.linux.href = links.linux;
341+
342+ if (macDownloadLink instanceof HTMLAnchorElement) {
343+ macDownloadLink.href = links.macos;
344+ }
345+ };
346+
347+ const closeMenu = () => {
348+ if (!(trigger instanceof HTMLButtonElement) || !(menu instanceof HTMLElement)) return;
349+ menu.hidden = true;
350+ trigger.setAttribute('aria-expanded', 'false');
351+ };
352+
353+ if (trigger instanceof HTMLButtonElement && menu instanceof HTMLElement) {
354+ trigger.addEventListener('click', () => {
355+ const isHidden = menu.hidden;
356+ menu.hidden = !isHidden;
357+ trigger.setAttribute('aria-expanded', isHidden ? 'true' : 'false');
358+ });
359+
360+ document.addEventListener('click', (event) => {
361+ if (!(event.target instanceof Node)) {
362+ closeMenu();
363+ return;
364+ }
365+
366+ if (!root.contains(event.target)) closeMenu();
367+ });
368+
369+ document.addEventListener('keydown', (event) => {
370+ if (event.key === 'Escape') closeMenu();
371+ });
372+ }
373+
374+ closeButtons.forEach((button) => {
375+ button.addEventListener('click', () => {
376+ if (dialog instanceof HTMLDialogElement) {
377+ dialog.close();
378+ }
379+ });
380+ });
381+
382+ if (dialog instanceof HTMLDialogElement) {
383+ dialog.addEventListener('click', (event) => {
384+ const rect = dialog.getBoundingClientRect();
385+ const inside =
386+ rect.top <= event.clientY &&
387+ event.clientY <= rect.top + rect.height &&
388+ rect.left <= event.clientX &&
389+ event.clientX <= rect.left + rect.width;
390+
391+ if (!inside) {
392+ dialog.close();
393+ }
394+ });
395+ }
396+
397+ if (primaryLink instanceof HTMLAnchorElement) {
398+ primaryLink.addEventListener('click', (event) => {
399+ if (recommended !== 'macos') return;
400+ if (!(dialog instanceof HTMLDialogElement)) return;
401+ event.preventDefault();
402+ dialog.showModal();
403+ });
404+ }
405+
406+ syncLinks();
407+ setPrimaryLink(fallbackUrl, recommended === 'unknown' ? 'Download preview' : `Download for ${platformLabels[recommended]}`);
408+ setStatus('Loading latest release…');
409+
410+ fetch(apiUrl, {
411+ headers: {
412+ Accept: 'application/vnd.github+json',
413+ },
414+ })
415+ .then((response) => {
416+ if (!response.ok) {
417+ throw new Error(`GitHub release lookup failed with status ${response.status}`);
418+ }
419+ return response.json();
420+ })
421+ .then((release) => {
422+ const assets = Array.isArray(release.assets) ? release.assets : [];
423+ links.macos = assets.find(assetMatchers.macos)?.browser_download_url || fallbackUrl;
424+ links.windows = assets.find(assetMatchers.windows)?.browser_download_url || fallbackUrl;
425+ links.linux = assets.find(assetMatchers.linux)?.browser_download_url || fallbackUrl;
426+ syncLinks();
427+
428+ const primaryHref = recommended === 'macos'
429+ ? links.macos
430+ : recommended === 'windows'
431+ ? links.windows
432+ : recommended === 'linux'
433+ ? links.linux
434+ : fallbackUrl;
435+ const primaryLabel = recommended === 'unknown' ? 'Download preview' : `Download for ${platformLabels[recommended]}`;
436+
437+ setPrimaryLink(primaryHref, primaryLabel);
438+ setStatus(`Latest release ${release.tag_name || 'available'}.`);
439+ })
440+ .catch(() => {
441+ syncLinks();
442+ setPrimaryLink(fallbackUrl, recommended === 'unknown' ? 'Download preview' : `Download for ${platformLabels[recommended]}`);
443+ setStatus('Using GitHub release links.');
444+ });
445+ })();
446+ </script >
447+
195448<style >
196449 @media (max-width: 768px) {
197450 .lg\:col-span-5 { display: none; }
0 commit comments