feat(docs): detect ARM platforms and badge the current-platform bundle

This commit is contained in:
t8y2
2026-10-04 21:09:41 +08:00
parent 36ac001d70
commit e224690049
+52 -4
View File
@@ -19,6 +19,7 @@ const i18n = {
jdbcPluginInstallHint: "Import this ZIP in DBX from Settings > Driver Manager > JDBC Drivers > Local Install.",
bundles: "Offline Bundles",
bundlesDesc: "Platform-specific ZIP packages that include the agent registry, database drivers, native agents, and the matching JRE.",
currentPlatform: "Current platform",
drivers: "Database Drivers",
driversDesc: "Single-driver .tar.zst packages for Java agents. Install the matching JRE separately when it is not already available.",
nativeAgents: "Native Agents",
@@ -76,6 +77,7 @@ const i18n = {
jdbcPluginInstallHint: "在 DBX 的“设置 > 驱动管理 > JDBC 驱动 > 本地安装”中导入这个 ZIP。",
bundles: "整包下载",
bundlesDesc: "按平台提供的 ZIP 离线包,包含 Agent registry、数据库驱动、原生 Agent 和匹配的 JRE。",
currentPlatform: "当前平台",
drivers: "数据库驱动",
driversDesc: "Java Agent 的单驱动 .tar.zst 包;目标机器尚未安装 JRE 时需要另外安装一次对应 JRE。",
nativeAgents: "原生 Agent",
@@ -139,6 +141,32 @@ function detectBundlePlatform(): string {
return "windows-x64";
}
/**
* Apple Silicon Macs report "Intel Mac OS X" in their UA, so the sync pass
* defaults them to the x64 build. Chromium-based browsers expose the real CPU
* architecture via the async User-Agent Client Hints API; Safari and Firefox
* keep the UA-based guess.
*/
async function refineBundlePlatformWithHints(): Promise<string | null> {
const uaData = (
navigator as Navigator & {
userAgentData?: { getHighEntropyValues?: (hints: string[]) => Promise<{ architecture?: string }> };
}
).userAgentData;
if (!uaData?.getHighEntropyValues) return null;
try {
const hints = await uaData.getHighEntropyValues(["architecture"]);
if (hints.architecture !== "arm" && hints.architecture !== "arm64") return null;
const ua = navigator.userAgent;
if (/Macintosh|Mac OS/i.test(ua)) return "macos-aarch64";
if (/Windows/i.test(ua)) return "windows-aarch64";
if (/Linux|X11/i.test(ua)) return "linux-aarch64";
return null;
} catch {
return null;
}
}
function platformKey(j: JreDisplayEntry): string {
return `${j.jreKey}-${j.platformKey}`;
}
@@ -197,6 +225,7 @@ export function DriversClient({ initialCatalog, initialRegistry }: { initialCata
const [selectedNativePlatforms, setSelectedNativePlatforms] = useState<Record<string, string>>({});
const [bundlePlatform, setBundlePlatform] = useState("windows-x64");
const [detectedPlatform, setDetectedPlatform] = useState<string | null>(null);
const [selectedKeys, setSelectedKeys] = useState<ReadonlySet<string>>(() => new Set());
const [includeJre, setIncludeJre] = useState(false);
const [bundleSource, setBundleSource] = useState<BundleSource>("cnb");
@@ -205,6 +234,7 @@ export function DriversClient({ initialCatalog, initialRegistry }: { initialCata
const [bundleError, setBundleError] = useState<string | null>(null);
const [bundleDone, setBundleDone] = useState<number | null>(null);
const bundleAbortRef = useRef<AbortController | null>(null);
const platformTouchedRef = useRef(false);
const bundles = catalog?.bundles ?? [];
const drivers = catalog?.drivers ?? [];
@@ -267,8 +297,22 @@ export function DriversClient({ initialCatalog, initialRegistry }: { initialCata
useEffect(() => {
// Detected after hydration: the prerendered HTML cannot know the visitor's
// platform, and a differing initial state would hydration-mismatch.
setBundlePlatform(detectBundlePlatform());
// platform, and a differing initial state would hydration-mismatch. The
// detected platform also badges the matching full-bundle row, so it is kept
// separately from the custom-tab selector the visitor may re-target.
const initial = detectBundlePlatform();
setDetectedPlatform(initial);
setBundlePlatform(initial);
let cancelled = false;
refineBundlePlatformWithHints().then((refined) => {
if (cancelled || !refined) return;
setDetectedPlatform(refined);
// Never override a platform the visitor picked while the hint resolved.
if (!platformTouchedRef.current) setBundlePlatform(refined);
});
return () => {
cancelled = true;
};
}, []);
const bundleOptions = useMemo(() => buildCustomBundleOptions(initialRegistry, bundlePlatform), [initialRegistry, bundlePlatform]);
@@ -290,6 +334,7 @@ export function DriversClient({ initialCatalog, initialRegistry }: { initialCata
};
const changeBundlePlatform = (platformKey: string) => {
platformTouchedRef.current = true;
setBundlePlatform(platformKey);
setSelectedKeys(new Set());
};
@@ -394,10 +439,10 @@ export function DriversClient({ initialCatalog, initialRegistry }: { initialCata
<main className="landing">
<LandingNav lang={lang} active="drivers" />
<section className="pt-[100px] pb-8 max-[760px]:pt-[80px] max-[760px]:pb-6">
<section className="pt-[100px] pb-6 max-[760px]:pt-[80px] max-[760px]:pb-4">
<div className="max-w-[1180px] mx-auto px-7 max-[760px]:px-[18px]">
<div className="grid justify-items-center max-w-[900px] mx-auto text-center">
<h1 className="text-[clamp(30px,4vw,46px)] font-[820] leading-[1.08] tracking-tight text-landing-ink">{t.title}</h1>
<h1 className="sr-only">{t.title}</h1>
<p className="min-w-0 mx-auto text-[15px] font-[460] leading-[1.7] text-landing-muted max-w-[760px] max-[760px]:text-[13px] max-[760px]:whitespace-normal max-[760px]:max-w-[300px]">{t.subtitle}</p>
</div>
</div>
@@ -525,6 +570,9 @@ export function DriversClient({ initialCatalog, initialRegistry }: { initialCata
<td className="min-w-0 px-5 py-3 font-medium text-landing-ink max-[760px]:px-0">
<div className="flex min-w-0 items-center gap-2">
<span className="min-w-0 truncate">{bundle.platformLabel}</span>
{bundle.platformKey === detectedPlatform && (
<span className="shrink-0 rounded-[5px] border border-landing-green/35 bg-landing-green/10 px-1.5 py-0.5 text-[11px] font-[650] text-landing-green">{t.currentPlatform}</span>
)}
<span className="hidden shrink-0 rounded-[5px] border border-landing-blue/35 bg-landing-blue/10 px-1.5 py-0.5 font-mono text-[11px] text-landing-sky max-[760px]:inline">ZIP</span>
</div>
</td>