diff --git a/apps/web/features/landing/components/download/all-platforms.test.tsx b/apps/web/features/landing/components/download/all-platforms.test.tsx
new file mode 100644
index 0000000000..6d75480a7b
--- /dev/null
+++ b/apps/web/features/landing/components/download/all-platforms.test.tsx
@@ -0,0 +1,57 @@
+import { render, screen, within } from "@testing-library/react";
+import { describe, expect, it, vi } from "vitest";
+import { AllPlatforms } from "./all-platforms";
+
+vi.mock("../../i18n", () => ({
+ useLocale: () => ({
+ t: {
+ download: {
+ allPlatforms: {
+ title: "All platforms",
+ macArm64Label: "macOS · Apple Silicon",
+ macX64Label: "macOS · Intel",
+ winX64Label: "Windows · x64",
+ winArm64Label: "Windows · ARM64",
+ linuxX64Label: "Linux · x64",
+ linuxArm64Label: "Linux · ARM64",
+ formatDmg: ".dmg",
+ formatZip: ".zip",
+ formatExe: ".exe",
+ formatAppImage: ".AppImage",
+ formatDeb: ".deb",
+ formatRpm: ".rpm",
+ unavailable: "Not available",
+ },
+ footer: { allReleases: "View all releases" },
+ },
+ },
+ }),
+}));
+
+describe("AllPlatforms", () => {
+ it("lists Intel macOS downloads separately from Apple Silicon", () => {
+ render(
+ ,
+ );
+
+ const intelLabel = screen.getByText("macOS · Intel");
+ const intelRow = intelLabel.parentElement?.parentElement;
+ expect(intelRow).not.toBeNull();
+ expect(within(intelRow!).getByRole("link", { name: ".dmg" })).toHaveAttribute(
+ "href",
+ "https://downloads.test/mac-x64.dmg",
+ );
+ expect(within(intelRow!).getByRole("link", { name: ".zip" })).toHaveAttribute(
+ "href",
+ "https://downloads.test/mac-x64.zip",
+ );
+ });
+});
diff --git a/apps/web/features/landing/components/download/all-platforms.tsx b/apps/web/features/landing/components/download/all-platforms.tsx
index e0d07a185f..46e20a022b 100644
--- a/apps/web/features/landing/components/download/all-platforms.tsx
+++ b/apps/web/features/landing/components/download/all-platforms.tsx
@@ -35,7 +35,7 @@ export function AllPlatforms({
}
- label={d.macLabel}
+ label={d.macArm64Label}
formats={[
{
label: d.formatDmg,
@@ -48,6 +48,21 @@ export function AllPlatforms({
]}
unavailable={d.unavailable}
/>
+
}
+ label={d.macX64Label}
+ formats={[
+ {
+ label: d.formatDmg,
+ href: assets.macX64Dmg,
+ },
+ {
+ label: d.formatZip,
+ href: assets.macX64Zip,
+ },
+ ]}
+ unavailable={d.unavailable}
+ />
}
label={d.winX64Label}
@@ -111,10 +126,8 @@ export function AllPlatforms({
/>
- {d.intelNote}
-
{isFallbackNeeded(assets) ? (
-
+
{
+ it("links confidently detected Intel Macs to the x64 installers", () => {
+ const content = resolveContent(
+ { os: "mac", arch: "x64", archConfident: true },
+ {
+ macArm64Dmg: "https://downloads.test/mac-arm64.dmg",
+ macArm64Zip: "https://downloads.test/mac-arm64.zip",
+ macX64Dmg: "https://downloads.test/mac-x64.dmg",
+ macX64Zip: "https://downloads.test/mac-x64.zip",
+ },
+ false,
+ createEnDict(true).download.hero,
+ );
+
+ expect(content.primary).toEqual({
+ href: "https://downloads.test/mac-x64.dmg",
+ label: "Download (.dmg)",
+ disabled: false,
+ });
+ expect(content.alt).toEqual({
+ href: "https://downloads.test/mac-x64.zip",
+ label: "or download .zip",
+ });
+ });
+});
diff --git a/apps/web/features/landing/components/download/hero.tsx b/apps/web/features/landing/components/download/hero.tsx
index e73a3b2e26..613c49ca66 100644
--- a/apps/web/features/landing/components/download/hero.tsx
+++ b/apps/web/features/landing/components/download/hero.tsx
@@ -96,7 +96,7 @@ interface HeroContent {
type HeroDict = ReturnType["t"]["download"]["hero"];
-function resolveContent(
+export function resolveContent(
detected: DetectResult | null,
assets: DownloadAssets,
versionUnavailable: boolean,
@@ -111,17 +111,28 @@ function resolveContent(
if (detected.os === "mac") {
// Only Chromium high-entropy returns arch confidently. Safari
// always reports Intel even on Apple Silicon, so we treat
- // "non-confident" as arm64 + add a small Intel disclaimer.
+ // "non-confident" as arm64 + point Intel users to the matrix below.
if (detected.arch === "x64" && detected.archConfident) {
+ const dmg = assets.macX64Dmg;
+ const zip = assets.macX64Zip;
return {
title: d.macIntel.title,
sub: d.macIntel.sub,
- primary: {
- href: "#cli",
- label: d.macIntel.disabledCta,
- disabled: true,
- },
- hint: d.macIntel.intelHint,
+ primary: dmg
+ ? {
+ href: dmg,
+ label: d.macIntel.primary,
+ disabled: false,
+ }
+ : versionUnavailable
+ ? { href: "#", label: d.macIntel.primary, disabled: true }
+ : undefined,
+ alt: zip
+ ? {
+ href: zip,
+ label: d.macIntel.altZip,
+ }
+ : undefined,
};
}
const dmg = assets.macArm64Dmg;
diff --git a/apps/web/features/landing/i18n/en.ts b/apps/web/features/landing/i18n/en.ts
index 6f0959780b..c1876f1cce 100644
--- a/apps/web/features/landing/i18n/en.ts
+++ b/apps/web/features/landing/i18n/en.ts
@@ -2271,10 +2271,9 @@ export function createEnDict(allowSignup: boolean): LandingDict {
},
macIntel: {
title: "Multica for macOS",
- sub: "Apple Silicon required — Intel Macs not yet supported.",
- disabledCta: "Apple Silicon required",
- intelHint:
- "On an Intel Mac? Use the CLI below — it runs the same daemon.",
+ sub: "Intel · bundled daemon, zero setup",
+ primary: "Download (.dmg)",
+ altZip: "or download .zip",
},
winX64: {
title: "Multica for Windows",
@@ -2296,12 +2295,13 @@ export function createEnDict(allowSignup: boolean): LandingDict {
title: "Choose your platform",
sub: "All installers are listed below.",
},
- safariMacHint: "On an Intel Mac? Use the CLI below.",
+ safariMacHint: "On an Intel Mac? Choose the Intel download below.",
archFallbackHint: "Wrong architecture? See all formats below.",
},
allPlatforms: {
title: "All platforms",
- macLabel: "macOS · Apple Silicon",
+ macArm64Label: "macOS · Apple Silicon",
+ macX64Label: "macOS · Intel",
winX64Label: "Windows · x64",
winArm64Label: "Windows · ARM64",
linuxX64Label: "Linux · x64",
@@ -2312,8 +2312,6 @@ export function createEnDict(allowSignup: boolean): LandingDict {
formatAppImage: ".AppImage",
formatDeb: ".deb",
formatRpm: ".rpm",
- intelNote:
- "Apple Silicon only — Intel Macs not supported in this release.",
unavailable: "Not available",
},
cli: {
diff --git a/apps/web/features/landing/i18n/ja.ts b/apps/web/features/landing/i18n/ja.ts
index 9c80653942..f92b5fffd9 100644
--- a/apps/web/features/landing/i18n/ja.ts
+++ b/apps/web/features/landing/i18n/ja.ts
@@ -1773,10 +1773,9 @@ export function createJaDict(allowSignup: boolean): LandingDict {
},
macIntel: {
title: "macOS 版 Multica",
- sub: "Apple Silicon が必要です。Intel Mac はまだ対応していません。",
- disabledCta: "Apple Silicon が必要",
- intelHint:
- "Intel Mac をお使いですか? 下の CLI をご利用ください。同じデーモンが動きます。",
+ sub: "Intel · デーモン同梱、設定不要",
+ primary: "ダウンロード(.dmg)",
+ altZip: "または .zip をダウンロード",
},
winX64: {
title: "Windows 版 Multica",
@@ -1798,12 +1797,13 @@ export function createJaDict(allowSignup: boolean): LandingDict {
title: "プラットフォームを選択",
sub: "すべてのインストーラーは下にまとまっています。",
},
- safariMacHint: "Intel Mac をお使いですか? 下の CLI をご利用ください。",
+ safariMacHint: "Intel Mac をお使いですか? 下の Intel 版を選択してください。",
archFallbackHint: "アーキテクチャが合いませんか? 下ですべての形式を確認してください。",
},
allPlatforms: {
title: "すべてのプラットフォーム",
- macLabel: "macOS · Apple Silicon",
+ macArm64Label: "macOS · Apple Silicon",
+ macX64Label: "macOS · Intel",
winX64Label: "Windows · x64",
winArm64Label: "Windows · ARM64",
linuxX64Label: "Linux · x64",
@@ -1814,8 +1814,6 @@ export function createJaDict(allowSignup: boolean): LandingDict {
formatAppImage: ".AppImage",
formatDeb: ".deb",
formatRpm: ".rpm",
- intelNote:
- "Apple Silicon のみ対応です。このリリースでは Intel Mac には対応していません。",
unavailable: "利用できません",
},
cli: {
diff --git a/apps/web/features/landing/i18n/ko.ts b/apps/web/features/landing/i18n/ko.ts
index 8b1fcd361c..8172d45530 100644
--- a/apps/web/features/landing/i18n/ko.ts
+++ b/apps/web/features/landing/i18n/ko.ts
@@ -1798,10 +1798,9 @@ export function createKoDict(allowSignup: boolean): LandingDict {
},
macIntel: {
title: "macOS용 Multica",
- sub: "Apple Silicon이 필요합니다. Intel Mac은 아직 지원하지 않습니다.",
- disabledCta: "Apple Silicon 필요",
- intelHint:
- "Intel Mac을 사용 중인가요? 아래 CLI를 사용하세요. 같은 데몬이 실행됩니다.",
+ sub: "Intel · 데몬 포함, 별도 설정 없음",
+ primary: "다운로드(.dmg)",
+ altZip: "또는 .zip 다운로드",
},
winX64: {
title: "Windows용 Multica",
@@ -1823,12 +1822,13 @@ export function createKoDict(allowSignup: boolean): LandingDict {
title: "플랫폼 선택",
sub: "모든 설치 파일은 아래에 정리되어 있습니다.",
},
- safariMacHint: "Intel Mac을 사용 중인가요? 아래 CLI를 사용하세요.",
+ safariMacHint: "Intel Mac을 사용 중인가요? 아래에서 Intel 버전을 선택하세요.",
archFallbackHint: "아키텍처가 맞지 않나요? 아래에서 모든 형식을 확인하세요.",
},
allPlatforms: {
title: "모든 플랫폼",
- macLabel: "macOS · Apple Silicon",
+ macArm64Label: "macOS · Apple Silicon",
+ macX64Label: "macOS · Intel",
winX64Label: "Windows · x64",
winArm64Label: "Windows · ARM64",
linuxX64Label: "Linux · x64",
@@ -1839,8 +1839,6 @@ export function createKoDict(allowSignup: boolean): LandingDict {
formatAppImage: ".AppImage",
formatDeb: ".deb",
formatRpm: ".rpm",
- intelNote:
- "Apple Silicon만 지원합니다. 이번 릴리스에서는 Intel Mac을 지원하지 않습니다.",
unavailable: "사용할 수 없음",
},
cli: {
diff --git a/apps/web/features/landing/i18n/types.ts b/apps/web/features/landing/i18n/types.ts
index 48ba946179..7731ca5dfe 100644
--- a/apps/web/features/landing/i18n/types.ts
+++ b/apps/web/features/landing/i18n/types.ts
@@ -145,8 +145,8 @@ export type LandingDict = {
macIntel: {
title: string;
sub: string;
- disabledCta: string;
- intelHint: string;
+ primary: string;
+ altZip: string;
};
winX64: { title: string; sub: string; primary: string };
winArm64: { title: string; sub: string; primary: string };
@@ -162,7 +162,8 @@ export type LandingDict = {
};
allPlatforms: {
title: string;
- macLabel: string;
+ macArm64Label: string;
+ macX64Label: string;
winX64Label: string;
winArm64Label: string;
linuxX64Label: string;
@@ -173,7 +174,6 @@ export type LandingDict = {
formatAppImage: string;
formatDeb: string;
formatRpm: string;
- intelNote: string;
unavailable: string;
};
cli: {
diff --git a/apps/web/features/landing/i18n/zh.ts b/apps/web/features/landing/i18n/zh.ts
index 23bde8ebc7..4e25340f6d 100644
--- a/apps/web/features/landing/i18n/zh.ts
+++ b/apps/web/features/landing/i18n/zh.ts
@@ -2270,9 +2270,9 @@ export function createZhDict(allowSignup: boolean): LandingDict {
},
macIntel: {
title: "Multica for macOS",
- sub: "需要 Apple Silicon——暂不支持 Intel Mac。",
- disabledCta: "需要 Apple Silicon",
- intelHint: "在 Intel Mac 上?请使用下方 CLI——底层跑的是同一个 daemon。",
+ sub: "Intel · 内置守护进程,无需配置",
+ primary: "下载 (.dmg)",
+ altZip: "或下载 .zip",
},
winX64: {
title: "Multica for Windows",
@@ -2294,12 +2294,13 @@ export function createZhDict(allowSignup: boolean): LandingDict {
title: "选择你的平台",
sub: "下方是所有支持的安装包。",
},
- safariMacHint: "在 Intel Mac 上?请使用下方 CLI。",
+ safariMacHint: "在 Intel Mac 上?请在下方选择 Intel 版本。",
archFallbackHint: "架构不对?下方是所有可选格式。",
},
allPlatforms: {
title: "所有平台",
- macLabel: "macOS · Apple Silicon",
+ macArm64Label: "macOS · Apple Silicon",
+ macX64Label: "macOS · Intel",
winX64Label: "Windows · x64",
winArm64Label: "Windows · ARM64",
linuxX64Label: "Linux · x64",
@@ -2310,7 +2311,6 @@ export function createZhDict(allowSignup: boolean): LandingDict {
formatAppImage: ".AppImage",
formatDeb: ".deb",
formatRpm: ".rpm",
- intelNote: "仅支持 Apple Silicon——Intel Mac 目前暂不支持。",
unavailable: "暂不可用",
},
cli: {
diff --git a/apps/web/features/landing/utils/os-detect.ts b/apps/web/features/landing/utils/os-detect.ts
index 4d7e35679b..f27c510917 100644
--- a/apps/web/features/landing/utils/os-detect.ts
+++ b/apps/web/features/landing/utils/os-detect.ts
@@ -7,8 +7,8 @@
* Known limitation: Safari on macOS always reports `Intel Mac OS X`
* in the UA string even on Apple Silicon, and Safari does not
* implement userAgentData. This function therefore returns `arm64`
- * as the best default for any Mac — UI surfaces a small "On Intel
- * Mac? Use CLI." hint to cover the Intel minority.
+ * as the best default for any Mac — UI surfaces a small hint that
+ * points Intel users to the architecture-specific download below.
*/
export type OSName = "mac" | "windows" | "linux" | "unknown";
@@ -85,7 +85,7 @@ export async function detectOS(): Promise {
let arch: Arch = "unknown";
if (os === "mac") {
- // Best default. Real Intel Mac users will see the disclaimer.
+ // Best default. Real Intel Mac users will see the architecture hint.
arch = "arm64";
} else if (/arm|aarch/i.test(ua)) {
arch = "arm64";
diff --git a/apps/web/features/landing/utils/parse-release-assets.test.ts b/apps/web/features/landing/utils/parse-release-assets.test.ts
new file mode 100644
index 0000000000..ce867354e2
--- /dev/null
+++ b/apps/web/features/landing/utils/parse-release-assets.test.ts
@@ -0,0 +1,33 @@
+import { describe, expect, it } from "vitest";
+import { parseReleaseAssets } from "./parse-release-assets";
+
+function asset(name: string) {
+ return {
+ name,
+ browser_download_url: `https://github.test/releases/${name}`,
+ };
+}
+
+describe("parseReleaseAssets", () => {
+ it("keeps both Apple Silicon and Intel macOS installers", () => {
+ const assets = parseReleaseAssets([
+ asset("multica-desktop-0.4.2-mac-arm64.dmg"),
+ asset("multica-desktop-0.4.2-mac-arm64.zip"),
+ asset("multica-desktop-0.4.2-mac-x64.dmg"),
+ asset("multica-desktop-0.4.2-mac-x64.zip"),
+ asset("multica-desktop-0.4.2-mac-x64.dmg.blockmap"),
+ asset("latest-x64-mac.yml"),
+ ]);
+
+ expect(assets).toEqual({
+ macArm64Dmg:
+ "https://github.test/releases/multica-desktop-0.4.2-mac-arm64.dmg",
+ macArm64Zip:
+ "https://github.test/releases/multica-desktop-0.4.2-mac-arm64.zip",
+ macX64Dmg:
+ "https://github.test/releases/multica-desktop-0.4.2-mac-x64.dmg",
+ macX64Zip:
+ "https://github.test/releases/multica-desktop-0.4.2-mac-x64.zip",
+ });
+ });
+});
diff --git a/apps/web/features/landing/utils/parse-release-assets.ts b/apps/web/features/landing/utils/parse-release-assets.ts
index 8579e2f04b..9deb7640da 100644
--- a/apps/web/features/landing/utils/parse-release-assets.ts
+++ b/apps/web/features/landing/utils/parse-release-assets.ts
@@ -21,6 +21,8 @@ export interface GitHubAsset {
export interface DownloadAssets {
macArm64Dmg?: string;
macArm64Zip?: string;
+ macX64Dmg?: string;
+ macX64Zip?: string;
winX64Exe?: string;
winArm64Exe?: string;
linuxAmd64AppImage?: string;
@@ -63,9 +65,13 @@ export function parseReleaseAssets(raw: GitHubAsset[]): DownloadAssets {
const url = asset.browser_download_url;
if (platform === "mac") {
- if (archLower !== "arm64") continue; // we only ship arm64 today
- if (extLower === "dmg") out.macArm64Dmg = url;
- else if (extLower === "zip") out.macArm64Zip = url;
+ if (archLower === "arm64") {
+ if (extLower === "dmg") out.macArm64Dmg = url;
+ else if (extLower === "zip") out.macArm64Zip = url;
+ } else if (archLower === "x64") {
+ if (extLower === "dmg") out.macX64Dmg = url;
+ else if (extLower === "zip") out.macX64Zip = url;
+ }
} else if (platform === "windows") {
if (extLower !== "exe") continue;
if (archLower === "x64") out.winX64Exe = url;