import { appBasePath, appPath } from "@agent-native/core/client/api-path"; import { useT } from "@agent-native/core/client/i18n"; import { IconBrandChrome, IconBrandApple, IconBrandWindows, IconDeviceDesktop, IconExternalLink, } from "@tabler/icons-react"; import { useEffect, useState } from "react"; import { Button } from "@/components/ui/button"; import { Skeleton } from "@/components/ui/skeleton"; import enMessages from "@/i18n/en-US"; import { clipsChromeExtensionUrl, markDesktopAppDownloaded, useClipsChromeExtensionEnabled, } from "@/lib/capture-install-options"; export function meta() { return [ { title: enMessages.downloadRoute.pageTitle }, { name: "description", content: enMessages.downloadRoute.description, }, ]; } type PlatformId = "mac" | "windows" | "linux"; interface PlatformVariant { id: PlatformId; label: string; sublabel: string; assetKinds: readonly ( | "mac-universal" | "mac-arm64" | "mac-x64" | "windows-msi" | "linux-appimage" | "linux-deb" | "linux-rpm" )[]; icon: typeof IconBrandApple; } const LATEST_JSON_URL = `${appBasePath()}/api/clips-latest.json`; const RELEASE_PAGE_URL = "https://github.com/BuilderIO/agent-native/releases?q=clips-v"; const VARIANTS: PlatformVariant[] = [ { id: "mac", label: "macOS", sublabel: "Universal (Apple Silicon + Intel)", assetKinds: ["mac-universal", "mac-arm64", "mac-x64"], icon: IconBrandApple, }, { id: "windows", label: "Windows", sublabel: "64-bit MSI installer", assetKinds: ["windows-msi"], icon: IconBrandWindows, }, { id: "linux", label: "Linux", sublabel: "AppImage, Debian, and RPM packages", assetKinds: ["linux-appimage", "linux-deb", "linux-rpm"], icon: IconDeviceDesktop, }, ]; interface Manifest { version: string; tag: string; pub_date: string | null; notes?: string; assets: { name: string; url: string; size: number; kind: string; }[]; } function detectPlatform(): PlatformId | null { if (typeof navigator === "undefined") return null; const ua = navigator.userAgent; if (/Windows/i.test(ua)) return "windows"; if (/Mac/i.test(ua)) return "mac"; if (/Linux|X11/i.test(ua) && !/Android/i.test(ua)) return "linux"; return null; } function pickAsset( manifest: Manifest | null, variant: PlatformVariant, ): { url: string; name: string } | null { if (!manifest) return null; for (const kind of variant.assetKinds) { const asset = manifest.assets.find((a) => a.kind === kind); if (asset) return { url: asset.url, name: asset.name }; } return null; } function primaryDownloadButton( variant: PlatformVariant, manifest: Manifest | null, manifestError: boolean, downloadLabel: string, ) { const asset = pickAsset(manifest, variant); const Icon = variant.icon; if (asset) { return ( {downloadLabel} ); } if (manifest === null && !manifestError) { return ; } return ( {downloadLabel} ); } function secondaryDownloadButton( variant: PlatformVariant, manifest: Manifest | null, manifestError: boolean, downloadLabel: string, ) { const asset = pickAsset(manifest, variant); const Icon = variant.icon; const className = "h-auto gap-1.5 px-2 py-1 text-sm font-normal text-muted-foreground hover:bg-transparent hover:text-foreground"; if (asset) { return ( {downloadLabel} ); } if (manifest === null && !manifestError) { return ; } return ( {downloadLabel} ); } export default function DownloadPage() { const chromeExtensionEnabled = useClipsChromeExtensionEnabled(); const t = useT(); const [manifest, setManifest] = useState(null); const [manifestError, setManifestError] = useState(false); const [detected, setDetected] = useState(null); useEffect(() => { setDetected(detectPlatform()); }, []); useEffect(() => { let cancelled = false; fetch(LATEST_JSON_URL) .then((r) => (r.ok ? r.json() : Promise.reject(new Error(`${r.status}`)))) .then((json) => { if (!cancelled) setManifest(json as Manifest); }) .catch(() => { if (!cancelled) setManifestError(true); }); return () => { cancelled = true; }; }, []); const primary = VARIANTS.find((v) => v.id === detected) ?? VARIANTS[0]; const secondary = VARIANTS.filter((v) => v.id !== primary.id); return ( Clips {t("downloadRoute.backToLibrary")} {t("downloadRoute.clipsDesktop")} {t("downloadRoute.heroDescription")} {primaryDownloadButton( primary, manifest, manifestError, t("downloadRoute.downloadFor", { platform: primary.label }), )} {secondary.map((variant) => ( {secondaryDownloadButton( variant, manifest, manifestError, t("downloadRoute.alsoFor", { platform: variant.label }), )} ))} {manifest ? ( <> {manifest.pub_date ? t("downloadRoute.versionReleased", { version: manifest.version, date: new Date(manifest.pub_date).toLocaleDateString(), }) : t("downloadRoute.version", { version: manifest.version, })} > ) : manifestError ? ( <>{t("downloadRoute.manifestError")}> ) : ( <>{t("downloadRoute.loadingRelease")}> )} {chromeExtensionEnabled && ( {t("downloadRoute.chromeTitle")} {t("downloadRoute.chromeDescription")} {clipsChromeExtensionUrl ? ( {t("downloadRoute.installChrome")} ) : ( <> {t("downloadRoute.chromePending")} > )} )} ); }
{t("downloadRoute.heroDescription")}
{t("downloadRoute.chromeDescription")}