Skip to content

Commit ec35b1f

Browse files
committed
Website: Detect Intel/Apple without flicker
- The download button briefly showed the universal binary (40 MB) before the async `getHighEntropyValues` call resolved and swapped to the arch-specific one (27 MB) - Now uses the WebGL renderer string for synchronous detection on first paint — Apple Silicon GPUs report "Apple M1/M2/GPU" while Intel Macs report "Intel/AMD" - Falls back to `localStorage` cache from a previous async detection if WebGL is unavailable, then to the async Client Hints call as before - Only affects Chrome/Edge (where the flicker occurred). Firefox/Safari don't support Client Hints and always showed the universal binary — unchanged.
1 parent b8b058a commit ec35b1f

1 file changed

Lines changed: 53 additions & 17 deletions

File tree

apps/website/src/layouts/Layout.astro

Lines changed: 53 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -110,29 +110,65 @@ const {
110110

111111
<script is:inline>
112112
;(function () {
113+
function applyArch(arch) {
114+
document.querySelectorAll('a[data-download-link]').forEach(function (el) {
115+
var url = arch === 'aarch64' ? el.dataset.dmgArm : el.dataset.dmgIntel
116+
if (url) el.href = url
117+
el.dataset.umamiEventArch = arch
118+
var size = arch === 'aarch64' ? el.dataset.sizeArm : el.dataset.sizeIntel
119+
if (size) {
120+
el.querySelectorAll('[data-download-size]').forEach(function (s) {
121+
s.textContent = s.textContent.replace(/\d+ MB/, size)
122+
})
123+
}
124+
})
125+
document.querySelectorAll('[data-download-dropdown] [data-arch]').forEach(function (el) {
126+
if (el.dataset.arch === arch) {
127+
el.dataset.recommended = 'true'
128+
}
129+
})
130+
}
131+
132+
// Detect arch synchronously to avoid flash of universal binary on first paint.
133+
// WebGL renderer reports "Apple M1/M2/GPU" on Apple Silicon, "Intel/AMD" on Intel Macs.
134+
// Falls back to localStorage cache from a previous async detection.
135+
function detectArchSync() {
136+
try {
137+
var canvas = document.createElement('canvas')
138+
var gl = canvas.getContext('webgl')
139+
if (!gl) return null
140+
var ext = gl.getExtension('WEBGL_debug_renderer_info')
141+
if (!ext) return null
142+
var renderer = gl.getParameter(ext.UNMASKED_RENDERER_WEBGL)
143+
if (/Apple M|Apple GPU/.test(renderer)) return 'aarch64'
144+
if (/Intel|AMD/.test(renderer)) return 'x86_64'
145+
} catch (e) {}
146+
return null
147+
}
148+
149+
var syncArch = detectArchSync()
150+
if (!syncArch) {
151+
try {
152+
syncArch = localStorage.getItem('cmdr-arch')
153+
} catch (e) {
154+
/* localStorage unavailable */
155+
}
156+
}
157+
if (syncArch) applyArch(syncArch)
158+
159+
// Detect arch async and update cache for next visit
113160
if (!navigator.userAgentData) return
114161
navigator.userAgentData
115162
.getHighEntropyValues(['architecture'])
116163
.then(function (ua) {
117164
var arch = ua.architecture === 'arm' ? 'aarch64' : ua.architecture === 'x86' ? 'x86_64' : null
118165
if (!arch) return
119-
document.querySelectorAll('a[data-download-link]').forEach(function (el) {
120-
var url = arch === 'aarch64' ? el.dataset.dmgArm : el.dataset.dmgIntel
121-
if (url) el.href = url
122-
el.dataset.umamiEventArch = arch
123-
var size = arch === 'aarch64' ? el.dataset.sizeArm : el.dataset.sizeIntel
124-
if (size) {
125-
el.querySelectorAll('[data-download-size]').forEach(function (s) {
126-
s.textContent = s.textContent.replace(/\d+ MB/, size)
127-
})
128-
}
129-
})
130-
// Mark the detected architecture as recommended in split-button dropdowns
131-
document.querySelectorAll('[data-download-dropdown] [data-arch]').forEach(function (el) {
132-
if (el.dataset.arch === arch) {
133-
el.dataset.recommended = 'true'
134-
}
135-
})
166+
try {
167+
localStorage.setItem('cmdr-arch', arch)
168+
} catch (e) {
169+
/* localStorage unavailable */
170+
}
171+
applyArch(arch)
136172
})
137173
.catch(function () {})
138174
})()

0 commit comments

Comments
 (0)