Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit c19da89

Browse filesBrowse files
author
patj
committed
Wire download links directly to the latest release installers
os-detect.js -> downloads.js: after OS highlight, resolve the latest release via the GitHub API and point every download link at the exact installer asset (asset names carry the version, so we read them, not guess URLs). Validated against real v26.7.1 assets — incl. separate macOS Intel dmg. - Per-variant links carry data-dl asset patterns; hero button + card links get real browser_download_url; Windows stays on the MS Store - 'v26.7.1' chip becomes live from the real latest tag (data-latest-tag) - localStorage cache (1h) keeps well under GitHub's rate limit - Graceful fallback: API failure/offline/artifact-CSP keeps the existing /releases/latest links; no-JS still works - CSP: add connect-src https://api.github.com - build.py inlines downloads.js into the single-file artifact
1 parent a08cc11 commit c19da89
Copy full SHA for c19da89

5 files changed

+131-43Lines changed: 131 additions & 43 deletions

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎_headers‎

Copy file name to clipboardExpand all lines: _headers
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
X-Frame-Options: DENY
77
Referrer-Policy: strict-origin-when-cross-origin
88
Permissions-Policy: geolocation=(), microphone=(), camera=(), interest-cohort=()
9-
Content-Security-Policy: default-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; script-src 'self'; font-src 'self' data:; base-uri 'self'; form-action 'self'; frame-ancestors 'none'; upgrade-insecure-requests
9+
Content-Security-Policy: default-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; script-src 'self'; connect-src 'self' https://api.github.com; font-src 'self' data:; base-uri 'self'; form-action 'self'; frame-ancestors 'none'; upgrade-insecure-requests
1010

1111
# Fingerprinted/rarely-changing media can cache hard
1212
/assets/*
Collapse file

‎assets/js/downloads.js‎

Copy file name to clipboard
+119Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/* AetherSDR download wiring — progressive enhancement.
2+
*
3+
* 1. Highlights the download card matching the visitor's OS (no network).
4+
* 2. Resolves the LATEST release from the GitHub API and points every
5+
* download link straight at the correct installer asset — the asset
6+
* filenames carry the version, so we read them rather than guess URLs.
7+
*
8+
* If the API is unavailable (offline, rate-limited, or the artifact CSP),
9+
* every link keeps its fallback href to the GitHub "latest release" page,
10+
* so nothing ever breaks.
11+
*/
12+
(function () {
13+
var REPO = 'aethersdr/AetherSDR';
14+
var API = 'https://api.github.com/repos/' + REPO + '/releases/latest';
15+
var STORE_URL = 'https://apps.microsoft.com/detail/9nc6bmwfn811';
16+
var CACHE_KEY = 'aetherLatestRelease';
17+
var TTL = 3600 * 1000; // 1 hour
18+
19+
function detectOS() {
20+
var uaData = navigator.userAgentData;
21+
var platform = (uaData && uaData.platform) || navigator.platform || '';
22+
var s = (platform + ' ' + (navigator.userAgent || '')).toLowerCase();
23+
if (/mac|iphone|ipad|ipod|darwin/.test(s)) return 'macos';
24+
if (/win/.test(s)) return 'windows';
25+
if (/linux|x11|android|cros/.test(s)) return 'linux';
26+
return null;
27+
}
28+
29+
var currentOS = detectOS();
30+
31+
function highlight() {
32+
if (!currentOS) return;
33+
var card = document.querySelector('.dl-card[data-os="' + currentOS + '"]');
34+
if (!card) return;
35+
card.classList.add('is-recommended');
36+
var badge = card.querySelector('.dl-reco');
37+
if (badge) badge.hidden = false;
38+
}
39+
40+
// First asset whose name contains one of the priority substrings.
41+
function pick(assets, patterns) {
42+
for (var p = 0; p < patterns.length; p++) {
43+
var pat = patterns[p].trim().toLowerCase();
44+
if (!pat) continue;
45+
for (var i = 0; i < assets.length; i++) {
46+
if (assets[i].name.toLowerCase().indexOf(pat) !== -1) return assets[i];
47+
}
48+
}
49+
return null;
50+
}
51+
52+
function wire(release) {
53+
if (!release || !release.assets) return;
54+
var assets = release.assets;
55+
56+
// Per-variant links carry their own asset patterns.
57+
var links = document.querySelectorAll('[data-dl]');
58+
for (var i = 0; i < links.length; i++) {
59+
var asset = pick(assets, links[i].getAttribute('data-dl').split(','));
60+
if (asset) links[i].href = asset.browser_download_url;
61+
}
62+
63+
// Hero primary button -> the detected OS's installer (Store on Windows).
64+
var primary = document.querySelector('[data-dl-primary]');
65+
if (primary && currentOS) {
66+
if (currentOS === 'windows') {
67+
primary.href = STORE_URL;
68+
primary.target = '_blank';
69+
primary.rel = 'noopener';
70+
} else {
71+
var patts = currentOS === 'macos'
72+
? ['apple-silicon.dmg', '.dmg']
73+
: ['x86_64.appimage', '.appimage'];
74+
var a = pick(assets, patts);
75+
if (a) primary.href = a.browser_download_url;
76+
}
77+
}
78+
79+
// Live "latest release" version label.
80+
if (release.tag_name) {
81+
var tag = release.tag_name.charAt(0) === 'v' ? release.tag_name : 'v' + release.tag_name;
82+
var tags = document.querySelectorAll('[data-latest-tag]');
83+
for (var j = 0; j < tags.length; j++) tags[j].textContent = tag;
84+
}
85+
}
86+
87+
function readCache() {
88+
try {
89+
var o = JSON.parse(localStorage.getItem(CACHE_KEY));
90+
return o && Date.now() - o.t < TTL ? o.r : null;
91+
} catch (e) { return null; }
92+
}
93+
function writeCache(r) {
94+
try { localStorage.setItem(CACHE_KEY, JSON.stringify({ t: Date.now(), r: r })); } catch (e) {}
95+
}
96+
97+
function loadRelease() {
98+
var cached = readCache();
99+
if (cached) { wire(cached); return; }
100+
if (!window.fetch) return;
101+
fetch(API, { headers: { Accept: 'application/vnd.github+json' } })
102+
.then(function (res) { if (!res.ok) throw new Error('github ' + res.status); return res.json(); })
103+
.then(function (release) {
104+
var slim = {
105+
tag_name: release.tag_name,
106+
assets: (release.assets || []).map(function (a) {
107+
return { name: a.name, browser_download_url: a.browser_download_url };
108+
})
109+
};
110+
writeCache(slim);
111+
wire(slim);
112+
})
113+
.catch(function () { /* keep fallback /releases/latest links */ });
114+
}
115+
116+
function init() { highlight(); loadRelease(); }
117+
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
118+
else init();
119+
})();
Collapse file

‎assets/js/os-detect.js‎

Copy file name to clipboardExpand all lines: assets/js/os-detect.js
-31Lines changed: 0 additions & 31 deletions
This file was deleted.
Collapse file

‎index.html‎

Copy file name to clipboardExpand all lines: index.html
+9-9Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ <h1>The native workstation for your <span class="grad-text">whole station</span>
6767
not just in front of the radio.
6868
</p>
6969
<div class="hero-cta">
70-
<a class="btn btn-primary" href="#download">
70+
<a class="btn btn-primary" href="#download" data-dl-primary>
7171
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v12m0 0 4-4m-4 4-4-4M4 17v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2"/></svg>
7272
Download for your platform
7373
</a>
@@ -81,7 +81,7 @@ <h1>The native workstation for your <span class="grad-text">whole station</span>
8181
</a>
8282
</div>
8383
<div class="hero-meta">
84-
<span class="chip"><b>v26.7.1</b> · latest release</span>
84+
<span class="chip"><b data-latest-tag>v26.7.1</b> · latest release</span>
8585
<span class="chip">GPL v3</span>
8686
<span class="chip">Qt6 · C++20</span>
8787
<span class="chip">GPG-signed commits</span>
@@ -295,17 +295,17 @@ <h2>Native builds, released together</h2>
295295
<span class="dl-reco" hidden>Your platform</span>
296296
<div class="os"><svg viewBox="0 0 24 24"><path d="M12 2C9.5 2 8.7 4 8.7 6.2c0 1 .2 1.6.2 2.4-.5.6-2.4 2.9-2.4 6.1 0 2 .6 3.4 1.4 4.4.4.5.2 1.3-.4 1.6-.7.4-1.2 1-1.2 1.6 0 .8.9 1.2 2.2 1.2h6.6c1.3 0 2.2-.4 2.2-1.2 0-.6-.5-1.2-1.2-1.6-.6-.3-.8-1.1-.4-1.6.8-1 1.4-2.4 1.4-4.4 0-3.2-1.9-5.5-2.4-6.1 0-.8.2-1.4.2-2.4C15.3 4 14.5 2 12 2Z"/></svg> Linux</div>
297297
<div class="variants">
298-
<a href="https://github.com/aethersdr/AetherSDR/releases/latest">AppImage <span class="arch">x86_64</span></a>
299-
<a href="https://github.com/aethersdr/AetherSDR/releases/latest">AppImage <span class="arch">aarch64 · Pi</span></a>
298+
<a data-dl="x86_64.AppImage" href="https://github.com/aethersdr/AetherSDR/releases/latest">AppImage <span class="arch">x86_64</span></a>
299+
<a data-dl="aarch64.AppImage" href="https://github.com/aethersdr/AetherSDR/releases/latest">AppImage <span class="arch">aarch64 · Pi</span></a>
300300
</div>
301301
</div>
302302

303303
<div class="dl-card" data-os="macos">
304304
<span class="dl-reco" hidden>Your platform</span>
305305
<div class="os"><svg viewBox="0 0 24 24"><path d="M16.4 2c.1 1.2-.3 2.3-1 3.2-.8.9-2 1.6-3.1 1.5-.1-1.1.4-2.3 1.1-3.1.8-.9 2-1.5 3-1.6ZM19 17.3c-.5 1.2-.8 1.8-1.5 2.8-1 1.5-2.5 3.3-4.3 3.3-1.6 0-2-.9-4.1-.9-2.1 0-2.6.9-4.1.9-1.8 0-3.2-1.7-4.2-3.1C-1 16-1.4 9.8 1.4 6.9 2.7 5.4 4.5 4.5 6.3 4.5c1.8 0 3 1 4.1 1 1 0 2.4-1 4.3-1 1.4 0 3.4.6 4.7 2.4-4.1 2.3-3.5 8.2.6 10.4Z"/></svg> macOS</div>
306306
<div class="variants">
307-
<a href="https://github.com/aethersdr/AetherSDR/releases/latest">DMG <span class="arch">Apple Silicon</span></a>
308-
<a href="https://github.com/aethersdr/AetherSDR/releases/latest">DMG <span class="arch">Intel · Rosetta</span></a>
307+
<a data-dl="apple-silicon.dmg,.dmg" href="https://github.com/aethersdr/AetherSDR/releases/latest">DMG <span class="arch">Apple Silicon</span></a>
308+
<a data-dl="intel.dmg,x86_64.dmg,apple-silicon.dmg,.dmg" href="https://github.com/aethersdr/AetherSDR/releases/latest">DMG <span class="arch">Intel · Rosetta</span></a>
309309
</div>
310310
</div>
311311

@@ -314,8 +314,8 @@ <h2>Native builds, released together</h2>
314314
<div class="os"><svg viewBox="0 0 24 24"><path d="M3 5.5 10.5 4.4v7.1H3V5.5ZM11.6 4.2 21 3v8.5h-9.4V4.2ZM3 12.5h7.5v7.1L3 18.5v-6ZM11.6 12.5H21V21l-9.4-1.3v-7.2Z"/></svg> Windows</div>
315315
<div class="variants">
316316
<a class="primary-dl" href="https://apps.microsoft.com/detail/9nc6bmwfn811" target="_blank" rel="noopener">Microsoft Store <span class="arch">recommended</span></a>
317-
<a href="https://github.com/aethersdr/AetherSDR/releases/latest">Installer <span class="arch">x64 setup.exe</span></a>
318-
<a href="https://github.com/aethersdr/AetherSDR/releases/latest">Portable <span class="arch">x64 zip</span></a>
317+
<a data-dl="Windows-x64-setup.exe,setup.exe" href="https://github.com/aethersdr/AetherSDR/releases/latest">Installer <span class="arch">x64 setup.exe</span></a>
318+
<a data-dl="Windows-x64-portable.zip,portable.zip,portable" href="https://github.com/aethersdr/AetherSDR/releases/latest">Portable <span class="arch">x64 zip</span></a>
319319
</div>
320320
<div class="winget"><span class="prompt">&gt; </span>winget install aethersdr -s msstore</div>
321321
<p class="winget-note">Auto-updating via the Store, or one line for PowerShell users.</p>
@@ -489,6 +489,6 @@ <h4>Resources</h4>
489489
</div>
490490
</footer>
491491

492-
<script src="assets/js/os-detect.js?v=1" defer></script>
492+
<script src="assets/js/downloads.js?v=2" defer></script>
493493
</body>
494494
</html>
Collapse file

‎scripts/build.py‎

Copy file name to clipboardExpand all lines: scripts/build.py
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,10 @@ def main():
3535

3636
# Inline JS as a real <script> before the asset pass — data-URI-ing a
3737
# script src would break execution in the single-file bundle.
38-
with open(os.path.join(ROOT, "assets/js/os-detect.js"), encoding="utf-8") as f:
38+
with open(os.path.join(ROOT, "assets/js/downloads.js"), encoding="utf-8") as f:
3939
js = f.read()
4040
html = re.sub(
41-
r'<script src="assets/js/os-detect\.js[^"]*"[^>]*></script>',
41+
r'<script src="assets/js/downloads\.js[^"]*"[^>]*></script>',
4242
f"<script>\n{js}\n</script>",
4343
html,
4444
)

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.