export const OpenWorkDownloads = () => { const STABLE_RELEASES_URL = "https://api.github.com/repos/different-ai/openwork/releases?per_page=30"; const ALPHA_RELEASE_URL = "https://api.github.com/repos/different-ai/openwork/releases/tags/alpha-macos-latest"; const GITHUB_HEADERS = { Accept: "application/vnd.github+json" }; const dateFormatter = new Intl.DateTimeFormat("en-US", { month: "short", day: "numeric", year: "numeric", }); const [activeChannel, setActiveChannel] = useState("stable"); const [stableReleases, setStableReleases] = useState([]); const [stableLoading, setStableLoading] = useState(true); const [stableError, setStableError] = useState(""); const [stableVisibleCount, setStableVisibleCount] = useState(8); const [alphaBuilds, setAlphaBuilds] = useState([]); const [alphaLoading, setAlphaLoading] = useState(false); const [alphaError, setAlphaError] = useState(""); const [alphaRequested, setAlphaRequested] = useState(false); const [alphaVisibleCount, setAlphaVisibleCount] = useState(12); const formatSize = (bytes) => `${(bytes / (1024 * 1024)).toFixed(0)} MB`; const formatDate = (date) => dateFormatter.format(new Date(date)); const githubError = (response, channel) => { const rateLimitMessage = response.status === 403 ? " The unauthenticated GitHub API rate limit is 60 requests per hour per IP." : ""; return `Could not load ${channel}: GitHub returned HTTP ${response.status}.${rateLimitMessage}`; }; const getStableGroups = (release) => { const version = release.tag_name.slice(1); const findAsset = (name) => release.assets.find((asset) => asset.name === name); return [ { label: "macOS", downloads: [ { label: "Mac (Apple Silicon)", asset: findAsset(`openwork-mac-arm64-${version}.dmg`) }, { label: "Mac (Intel)", asset: findAsset(`openwork-mac-x64-${version}.dmg`) }, ].filter((download) => download.asset), }, { label: "Windows", downloads: [ { label: "Windows (x64)", asset: findAsset(`openwork-win-x64-${version}.exe`) }, { label: "Windows (ARM64)", asset: findAsset(`openwork-win-arm64-${version}.exe`) }, ].filter((download) => download.asset), }, { label: "Linux", downloads: [ { label: "AppImage (x64)", asset: findAsset(`openwork-linux-x86_64-${version}.AppImage`) }, { label: "AppImage (ARM64)", asset: findAsset(`openwork-linux-arm64-${version}.AppImage`) }, { label: "tar.gz (x64)", asset: findAsset(`openwork-linux-x64-${version}.tar.gz`) }, { label: "tar.gz (ARM64)", asset: findAsset(`openwork-linux-arm64-${version}.tar.gz`) }, ].filter((download) => download.asset), }, ]; }; const detectPlatform = () => { if (typeof navigator === "undefined") { return { label: "macOS", assetName: (version) => `openwork-mac-arm64-${version}.dmg` }; } const platform = `${navigator.userAgent} ${navigator.platform}`.toLowerCase(); if (platform.includes("mac")) { return { label: "macOS", assetName: (version) => `openwork-mac-arm64-${version}.dmg` }; } if (platform.includes("win")) { return { label: "Windows", assetName: (version) => `openwork-win-x64-${version}.exe` }; } if (platform.includes("linux")) { return { label: "Linux", assetName: (version) => `openwork-linux-x86_64-${version}.AppImage` }; } return { label: "macOS", assetName: (version) => `openwork-mac-arm64-${version}.dmg` }; }; useEffect(() => { let active = true; const loadStable = async () => { try { const response = await fetch(STABLE_RELEASES_URL, { headers: GITHUB_HEADERS }); if (!response.ok) throw new Error(githubError(response, "stable releases")); const releases = await response.json(); if (active) { setStableReleases(releases.filter((release) => !release.draft && !release.prerelease && release.tag_name.startsWith("v"))); } } catch (error) { if (active) setStableError(error instanceof Error ? error.message : "Could not load stable releases."); } finally { if (active) setStableLoading(false); } }; void loadStable(); return () => { active = false; }; }, []); const loadAlpha = async () => { if (alphaRequested) return; setAlphaRequested(true); setAlphaLoading(true); setAlphaError(""); try { const response = await fetch(ALPHA_RELEASE_URL, { headers: GITHUB_HEADERS }); if (!response.ok) throw new Error(githubError(response, "alpha builds")); const release = await response.json(); const buildsByRun = {}; release.assets.forEach((asset) => { if (!asset.name.startsWith("openwork-mac-arm64-") || (!asset.name.endsWith(".dmg") && !asset.name.endsWith(".zip"))) return; const runMatch = asset.name.match(/-alpha\.(\d+)(?=[^0-9]|$)/); if (!runMatch) return; const run = runMatch[1]; if (!buildsByRun[run]) buildsByRun[run] = { run: Number(run), dmg: null, zip: null }; if (asset.name.endsWith(".dmg")) buildsByRun[run].dmg = asset; if (asset.name.endsWith(".zip")) buildsByRun[run].zip = asset; }); setAlphaBuilds( Object.values(buildsByRun) .filter((build) => build.dmg && build.zip) .map((build) => ({ ...build, version: build.dmg.name.replace("openwork-mac-arm64-", "").replace(/\.dmg$/, ""), })) .sort((first, second) => second.run - first.run), ); } catch (error) { setAlphaError(error instanceof Error ? error.message : "Could not load alpha builds."); } finally { setAlphaLoading(false); } }; const latestStable = stableReleases[0]; const detectedPlatform = detectPlatform(); const latestVersion = latestStable ? latestStable.tag_name.slice(1) : ""; const detectedAsset = latestStable ? latestStable.assets.find((asset) => asset.name === detectedPlatform.assetName(latestVersion)) : null; return (
{latestStable ? (
{detectedAsset ? ( Download for {detectedPlatform.label} ) : null} Version {latestVersion} · {formatDate(latestStable.published_at)}
) : null}
); };