| author | |
| committer | |
| log | 04f23098e13410a325fd2b997cbf7144e872470e |
| tree | 4914e1b2e9bcc96637085e2b2f870f96dfb87ed4 |
| parent | 61b2e744df378b7a8d534494114cf59b26cbe6ca |
| signature | Signed by PGP key B5690EEEBB952194 |
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Julius Marminge <julius0216@outlook.com>
Co-authored-by: codex <codex@users.noreply.github.com>3 files changed, 56 insertions(+), 1 deletions(-)
apps/marketing/src/lib/macArch.test.ts created+20| ... | ... | @@ -0,0 +1,20 @@ |
| 1 | import { describe, expect, it } from "vite-plus/test"; | |
| 2 | ||
| 3 | import { macArchFromGpuRenderer } from "./macArch"; | |
| 4 | ||
| 5 | describe("macArchFromGpuRenderer", () => { | |
| 6 | it("detects explicit Apple Silicon renderers", () => { | |
| 7 | expect(macArchFromGpuRenderer("Apple M4 Pro")).toBe("arm64"); | |
| 8 | }); | |
| 9 | ||
| 10 | it("prefers an Intel GPU marker even when the renderer also mentions Apple", () => { | |
| 11 | expect(macArchFromGpuRenderer("ANGLE Metal Renderer: Apple, Intel Iris Plus Graphics")).toBe( | |
| 12 | "x64", | |
| 13 | ); | |
| 14 | }); | |
| 15 | ||
| 16 | it("uses x64 for ambiguous Safari and unavailable renderer values", () => { | |
| 17 | expect(macArchFromGpuRenderer("Apple GPU")).toBe("x64"); | |
| 18 | expect(macArchFromGpuRenderer("")).toBe("x64"); | |
| 19 | }); | |
| 20 | }); |
apps/marketing/src/lib/macArch.ts created+16| ... | ... | @@ -0,0 +1,16 @@ |
| 1 | const INTEL_GPU_PATTERN = /intel|amd|radeon|nvidia|geforce/i; | |
| 2 | const APPLE_SILICON_GPU_PATTERN = /\bapple\s+m\d/i; | |
| 3 | ||
| 4 | export function macArchFromGpuRenderer(renderer: string): "arm64" | "x64" { | |
| 5 | if (INTEL_GPU_PATTERN.test(renderer)) { | |
| 6 | return "x64"; | |
| 7 | } | |
| 8 | if (APPLE_SILICON_GPU_PATTERN.test(renderer)) { | |
| 9 | return "arm64"; | |
| 10 | } | |
| 11 | ||
| 12 | // Generic "Apple GPU" renderers are ambiguous on Safari. x64 is the safe | |
| 13 | // fallback because Apple Silicon can run it through Rosetta, while Intel | |
| 14 | // Macs cannot run an arm64 build. | |
| 15 | return "x64"; | |
| 16 | } |
apps/marketing/src/pages/index.astro+20-1| ... | ... | @@ -391,14 +391,33 @@ const mobileEndorsementRows = [ |
| 391 | 391 | |
| 392 | 392 | <script> |
| 393 | 393 | import { fetchLatestRelease, RELEASES_URL, type ReleaseAsset } from "../lib/releases"; |
| 394 | import { macArchFromGpuRenderer } from "../lib/macArch"; | |
| 394 | 395 | |
| 395 | 396 | type Platform = { os: "mac" | "win" | "linux"; label: string; arch?: string }; |
| 396 | 397 | |
| 398 | // navigator.userAgent reports "Intel Mac OS X" on both Intel and Apple Silicon | |
| 399 | // Macs, so probe the GPU renderer instead: Apple Silicon exposes an "Apple" GPU. | |
| 400 | function detectMacArch(): "arm64" | "x64" { | |
| 401 | try { | |
| 402 | const canvas = document.createElement("canvas"); | |
| 403 | const gl = (canvas.getContext("webgl") || | |
| 404 | canvas.getContext("experimental-webgl")) as WebGLRenderingContext | null; | |
| 405 | const debugInfo = gl?.getExtension("WEBGL_debug_renderer_info"); | |
| 406 | if (gl && debugInfo) { | |
| 407 | const renderer = String(gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL) ?? ""); | |
| 408 | return macArchFromGpuRenderer(renderer); | |
| 409 | } | |
| 410 | } catch { | |
| 411 | // fall through to the default below | |
| 412 | } | |
| 413 | return macArchFromGpuRenderer(""); | |
| 414 | } | |
| 415 | ||
| 397 | 416 | function detectPlatform(): Platform | null { |
| 398 | 417 | const ua = navigator.userAgent; |
| 399 | 418 | if (/Win/i.test(ua)) return { os: "win", label: "Download for Windows" }; |
| 400 | 419 | if (/Mac/i.test(ua)) { |
| 401 | return { os: "mac", label: "Download for macOS", arch: "arm64" }; | |
| 420 | return { os: "mac", label: "Download for macOS", arch: detectMacArch() }; | |
| 402 | 421 | } |
| 403 | 422 | if (/Linux/i.test(ua)) return { os: "linux", label: "Download for Linux" }; |
| 404 | 423 | return null; |