- index.php: Landing page with feature showcase, editions, roadmap - download.php: WebTorrent P2P download (no torrent client needed) - apps.php: Ecosystem app downloads (Browser, IDE, Veil, Pulse) - releases.php: Full changelog RC1 through RC8 - docs.php: Technical documentation and build specs - security.php: Kernel hardening transparency report - developers.php: Developer foundation and contribution guide - compare.php: Head-to-head vs Ubuntu/Mint/Fedora/Arch - about.php: Company provenance, founder, verification commands - 404.html: Branded error page - JSON-LD structured data on 4 pages - Twitter Card + OpenGraph meta tags on all pages - Security headers (HSTS, X-Frame-Options, CSP)
1110 lines
42 KiB
PHP
1110 lines
42 KiB
PHP
<?php
|
||
/**
|
||
* Alfred Linux — P2P Download Page
|
||
* Browser-based WebTorrent client — no client install needed.
|
||
* When download completes, users are encouraged to keep seeding.
|
||
*/
|
||
header('Cache-Control: no-cache, no-store, must-revalidate');
|
||
$pageTitle = "Download Alfred Linux — Powered by the People";
|
||
$isoName = "alfred-linux-4.0-rc8-amd64.iso"; // Updated when new builds ship
|
||
$isoSize = "2.4 GB";
|
||
$version = "4.0 RC8";
|
||
$torrentURL = "/downloads/{$isoName}.torrent";
|
||
$magnetURI = "magnet:?xt=urn:btih:5eedd261364a0975763fc1a24cab433ab598692a&dn=alfred-linux-4.0-rc8-amd64-20260407.iso&tr=wss%3A%2F%2Ftracker.openwebtorrent.com&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce&tr=udp%3A%2F%2Fopen.stealth.si%3A80%2Fannounce&tr=udp%3A%2F%2Ftracker.torrent.eu.org%3A451%2Fannounce&tr=http%3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce";
|
||
?>
|
||
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title><?= htmlspecialchars($pageTitle) ?></title>
|
||
<meta name="description" content="Download Alfred Linux via peer-to-peer — directly in your browser. No torrent client needed. Seed to help the community.">
|
||
<meta property="og:title" content="Download Alfred Linux — Powered by the People">
|
||
<meta property="og:description" content="Download Alfred Linux via P2P — directly in your browser. No torrent client needed. Seed to help the community.">
|
||
<meta property="og:url" content="https://alfredlinux.com/download">
|
||
<meta property="og:image" content="https://alfredlinux.com/og-image.png">
|
||
<meta property="og:type" content="website">
|
||
<meta name="twitter:card" content="summary_large_image">
|
||
<meta name="twitter:title" content="Download Alfred Linux — Powered by the People">
|
||
<meta name="twitter:description" content="Download Alfred Linux via P2P — directly in your browser. No torrent client needed. Seed to help the community.">
|
||
<meta name="twitter:image" content="https://alfredlinux.com/og-image.png">
|
||
<link rel="canonical" href="https://alfredlinux.com/download">
|
||
<link rel="icon" href="/favicon.ico">
|
||
<style>
|
||
:root {
|
||
--bg: #0a0a0f;
|
||
--surface: #12121a;
|
||
--border: #1e1e2e;
|
||
--accent: #6c5ce7;
|
||
--accent2: #00cec9;
|
||
--gold: #fdcb6e;
|
||
--text: #e0e0e0;
|
||
--dim: #888;
|
||
--success: #00b894;
|
||
--danger: #e17055;
|
||
}
|
||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||
body {
|
||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
|
||
background: var(--bg);
|
||
color: var(--text);
|
||
min-height: 100vh;
|
||
overflow-x: hidden;
|
||
}
|
||
|
||
/* ── Nav ──────────────────────────────────── */
|
||
nav {
|
||
display: flex; align-items: center; justify-content: space-between;
|
||
padding: 1rem 2rem;
|
||
border-bottom: 1px solid var(--border);
|
||
background: rgba(10,10,15,0.95);
|
||
position: sticky; top: 0; z-index: 100;
|
||
backdrop-filter: blur(12px);
|
||
}
|
||
nav .logo { font-size: 1.3rem; font-weight: 700; }
|
||
nav .logo span { color: var(--accent); }
|
||
nav .links { display: flex; gap: 1.5rem; }
|
||
nav .links a { color: var(--dim); text-decoration: none; font-size: 0.9rem; transition: color 0.2s; }
|
||
nav .links a:hover { color: var(--text); }
|
||
|
||
/* ── Hero ─────────────────────────────────── */
|
||
.hero {
|
||
text-align: center;
|
||
padding: 4rem 2rem 2rem;
|
||
}
|
||
.hero h1 {
|
||
font-size: clamp(2rem, 5vw, 3.5rem);
|
||
font-weight: 800;
|
||
line-height: 1.1;
|
||
margin-bottom: 1rem;
|
||
}
|
||
.hero h1 .glow {
|
||
background: linear-gradient(135deg, var(--accent), var(--accent2));
|
||
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
|
||
}
|
||
.hero p {
|
||
color: var(--dim);
|
||
font-size: 1.15rem;
|
||
max-width: 640px;
|
||
margin: 0 auto 2rem;
|
||
line-height: 1.6;
|
||
}
|
||
.badge {
|
||
display: inline-block;
|
||
background: rgba(108,92,231,0.15);
|
||
color: var(--accent);
|
||
padding: 0.3rem 0.8rem;
|
||
border-radius: 999px;
|
||
font-size: 0.8rem;
|
||
font-weight: 600;
|
||
border: 1px solid rgba(108,92,231,0.3);
|
||
margin-bottom: 1.5rem;
|
||
}
|
||
|
||
/* ── Download Card ────────────────────────── */
|
||
.dl-card {
|
||
max-width: 720px;
|
||
margin: 0 auto 3rem;
|
||
background: var(--surface);
|
||
border: 1px solid var(--border);
|
||
border-radius: 16px;
|
||
padding: 2.5rem;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
.dl-card::before {
|
||
content: '';
|
||
position: absolute; top: 0; left: 0; right: 0; height: 3px;
|
||
background: linear-gradient(90deg, var(--accent), var(--accent2), var(--gold));
|
||
}
|
||
|
||
/* Start state */
|
||
#start-section { text-align: center; }
|
||
#start-section h2 { font-size: 1.5rem; margin-bottom: 0.5rem; }
|
||
#start-section .meta { color: var(--dim); font-size: 0.9rem; margin-bottom: 1.5rem; }
|
||
|
||
.btn-torrent {
|
||
display: inline-flex; align-items: center; gap: 0.5rem;
|
||
background: linear-gradient(135deg, var(--accent), #5a4bd1);
|
||
color: #fff;
|
||
border: none; border-radius: 12px;
|
||
padding: 1rem 2.5rem;
|
||
font-size: 1.1rem; font-weight: 700;
|
||
cursor: pointer;
|
||
transition: transform 0.15s, box-shadow 0.15s;
|
||
}
|
||
.btn-torrent:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 8px 30px rgba(108,92,231,0.4);
|
||
}
|
||
.btn-torrent svg { width: 22px; height: 22px; }
|
||
|
||
.alt-links {
|
||
margin-top: 1rem;
|
||
font-size: 0.85rem; color: var(--dim);
|
||
}
|
||
.alt-links a { color: var(--accent); text-decoration: none; }
|
||
.alt-links a:hover { text-decoration: underline; }
|
||
|
||
.btn-direct {
|
||
display: inline-flex; align-items: center; gap: 0.5rem;
|
||
background: rgba(255,255,255,0.08);
|
||
color: #ccc;
|
||
border: 1px solid rgba(255,255,255,0.15); border-radius: 12px;
|
||
padding: 0.75rem 2rem;
|
||
font-size: 1rem; font-weight: 600;
|
||
cursor: pointer; text-decoration: none;
|
||
transition: transform 0.15s, background 0.15s;
|
||
margin-top: 0.75rem;
|
||
}
|
||
.btn-direct:hover {
|
||
transform: translateY(-1px);
|
||
background: rgba(255,255,255,0.12);
|
||
color: #fff;
|
||
}
|
||
.btn-direct svg { width: 18px; height: 18px; }
|
||
|
||
/* Progress state */
|
||
#progress-section { display: none; }
|
||
.progress-bar-outer {
|
||
width: 100%;
|
||
height: 28px;
|
||
background: rgba(255,255,255,0.05);
|
||
border-radius: 14px;
|
||
overflow: hidden;
|
||
margin-bottom: 1.5rem;
|
||
position: relative;
|
||
}
|
||
.progress-bar-inner {
|
||
height: 100%;
|
||
width: 0%;
|
||
background: linear-gradient(90deg, var(--accent), var(--accent2));
|
||
border-radius: 14px;
|
||
transition: width 0.5s ease;
|
||
position: relative;
|
||
}
|
||
.progress-bar-inner::after {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0; left: 0; right: 0; bottom: 0;
|
||
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.15), transparent);
|
||
animation: shimmer 2s infinite;
|
||
}
|
||
@keyframes shimmer { 0%{transform:translateX(-100%)} 100%{transform:translateX(100%)} }
|
||
|
||
.progress-pct {
|
||
position: absolute;
|
||
top: 50%; left: 50%; transform: translate(-50%,-50%);
|
||
font-weight: 700; font-size: 0.85rem; z-index: 2;
|
||
text-shadow: 0 1px 3px rgba(0,0,0,0.5);
|
||
}
|
||
|
||
.stats-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
|
||
gap: 1rem;
|
||
margin-bottom: 1.5rem;
|
||
}
|
||
.stat-box {
|
||
background: rgba(255,255,255,0.03);
|
||
border: 1px solid var(--border);
|
||
border-radius: 10px;
|
||
padding: 0.8rem 1rem;
|
||
text-align: center;
|
||
}
|
||
.stat-box .label { font-size: 0.75rem; color: var(--dim); text-transform: uppercase; letter-spacing: 0.05em; }
|
||
.stat-box .value { font-size: 1.3rem; font-weight: 700; margin-top: 0.3rem; }
|
||
|
||
/* Complete / Seeding state */
|
||
#seed-section { display: none; text-align: center; }
|
||
.seed-banner {
|
||
background: linear-gradient(135deg, rgba(0,184,148,0.1), rgba(108,92,231,0.1));
|
||
border: 1px solid rgba(0,184,148,0.3);
|
||
border-radius: 16px;
|
||
padding: 2.5rem;
|
||
margin-bottom: 2rem;
|
||
}
|
||
.seed-banner h2 {
|
||
font-size: 1.8rem;
|
||
margin-bottom: 0.5rem;
|
||
background: linear-gradient(135deg, var(--success), var(--accent2));
|
||
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
|
||
}
|
||
.seed-banner p {
|
||
color: var(--text);
|
||
font-size: 1.1rem;
|
||
line-height: 1.6;
|
||
max-width: 520px;
|
||
margin: 0 auto;
|
||
}
|
||
.seed-banner .big-msg {
|
||
font-size: 1.5rem;
|
||
font-weight: 800;
|
||
color: var(--gold);
|
||
margin-top: 1.5rem;
|
||
-webkit-text-fill-color: unset;
|
||
}
|
||
|
||
.seed-stats {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
|
||
gap: 1rem;
|
||
margin-bottom: 2rem;
|
||
}
|
||
|
||
.seed-timer {
|
||
font-size: 2.5rem;
|
||
font-weight: 800;
|
||
font-variant-numeric: tabular-nums;
|
||
background: linear-gradient(135deg, var(--gold), var(--accent));
|
||
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
|
||
margin-bottom: 0.5rem;
|
||
}
|
||
.seed-timer-label { color: var(--dim); font-size: 0.85rem; }
|
||
|
||
.btn-save {
|
||
display: inline-flex; align-items: center; gap: 0.5rem;
|
||
background: var(--success);
|
||
color: #fff;
|
||
border: none; border-radius: 12px;
|
||
padding: 0.8rem 2rem;
|
||
font-size: 1rem; font-weight: 700;
|
||
cursor: pointer;
|
||
margin-top: 1rem;
|
||
}
|
||
.btn-save:hover { opacity: 0.9; }
|
||
|
||
.share-btn {
|
||
display: inline-flex; align-items: center; gap: 0.5rem;
|
||
background: rgba(108,92,231,0.15);
|
||
color: var(--accent);
|
||
border: 1px solid rgba(108,92,231,0.3);
|
||
border-radius: 10px;
|
||
padding: 0.6rem 1.5rem;
|
||
font-size: 0.9rem; font-weight: 600;
|
||
cursor: pointer;
|
||
margin: 0.5rem;
|
||
transition: all 0.2s;
|
||
}
|
||
.share-btn:hover { background: rgba(108,92,231,0.25); }
|
||
|
||
/* ── Community Section ────────────────────── */
|
||
.community {
|
||
max-width: 720px;
|
||
margin: 0 auto 4rem;
|
||
text-align: center;
|
||
}
|
||
.community h2 { font-size: 1.8rem; margin-bottom: 1rem; }
|
||
.community p { color: var(--dim); max-width: 540px; margin: 0 auto 2rem; line-height: 1.6; }
|
||
|
||
.swarm-vis {
|
||
background: var(--surface);
|
||
border: 1px solid var(--border);
|
||
border-radius: 16px;
|
||
padding: 2rem;
|
||
min-height: 200px;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
.swarm-vis canvas { width: 100%; height: 200px; }
|
||
|
||
.swarm-counter {
|
||
font-size: 3rem;
|
||
font-weight: 800;
|
||
background: linear-gradient(135deg, var(--accent), var(--accent2));
|
||
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
|
||
}
|
||
.swarm-label { color: var(--dim); font-size: 0.9rem; }
|
||
|
||
/* ── How It Works ─────────────────────────── */
|
||
.how-it-works {
|
||
max-width: 720px;
|
||
margin: 0 auto 4rem;
|
||
padding: 0 2rem;
|
||
}
|
||
.how-it-works h2 { text-align: center; font-size: 1.5rem; margin-bottom: 2rem; }
|
||
.steps {
|
||
display: grid; gap: 1.5rem;
|
||
}
|
||
.step {
|
||
display: flex; gap: 1.2rem; align-items: flex-start;
|
||
background: var(--surface);
|
||
border: 1px solid var(--border);
|
||
border-radius: 12px;
|
||
padding: 1.5rem;
|
||
}
|
||
.step-num {
|
||
width: 36px; height: 36px;
|
||
background: linear-gradient(135deg, var(--accent), var(--accent2));
|
||
border-radius: 50%;
|
||
display: flex; align-items: center; justify-content: center;
|
||
font-weight: 800; font-size: 0.9rem;
|
||
flex-shrink: 0;
|
||
}
|
||
.step h3 { font-size: 1rem; margin-bottom: 0.3rem; }
|
||
.step p { color: var(--dim); font-size: 0.9rem; line-height: 1.5; }
|
||
|
||
/* ── Footer ───────────────────────────────── */
|
||
footer {
|
||
text-align: center;
|
||
padding: 2rem;
|
||
border-top: 1px solid var(--border);
|
||
color: var(--dim);
|
||
font-size: 0.85rem;
|
||
}
|
||
footer a { color: var(--accent); text-decoration: none; }
|
||
|
||
/* ── Error state ──────────────────────────── */
|
||
.error-box {
|
||
display: none;
|
||
background: rgba(225,112,85,0.1);
|
||
border: 1px solid rgba(225,112,85,0.3);
|
||
border-radius: 10px;
|
||
padding: 1rem 1.5rem;
|
||
margin-top: 1rem;
|
||
color: var(--danger);
|
||
font-size: 0.9rem;
|
||
}
|
||
|
||
/* ── Flash to USB Guide ────────────────── */
|
||
.flash-guide {
|
||
max-width: 720px;
|
||
margin: 0 auto 3rem;
|
||
background: var(--surface);
|
||
border: 1px solid var(--border);
|
||
border-radius: 16px;
|
||
padding: 2.5rem;
|
||
display: none;
|
||
}
|
||
.flash-guide.visible { display: block; }
|
||
.flash-guide h2 {
|
||
font-size: 1.5rem;
|
||
margin-bottom: 0.5rem;
|
||
text-align: center;
|
||
}
|
||
.flash-guide .subtitle {
|
||
color: var(--dim);
|
||
text-align: center;
|
||
font-size: 0.95rem;
|
||
margin-bottom: 2rem;
|
||
}
|
||
.flash-tabs {
|
||
display: flex;
|
||
gap: 0.5rem;
|
||
margin-bottom: 1.5rem;
|
||
border-bottom: 1px solid var(--border);
|
||
padding-bottom: 0.5rem;
|
||
}
|
||
.flash-tab {
|
||
background: none;
|
||
border: none;
|
||
color: var(--dim);
|
||
font-size: 0.9rem;
|
||
font-weight: 600;
|
||
padding: 0.5rem 1rem;
|
||
cursor: pointer;
|
||
border-radius: 8px 8px 0 0;
|
||
transition: all 0.2s;
|
||
}
|
||
.flash-tab:hover { color: var(--text); }
|
||
.flash-tab.active {
|
||
color: var(--accent);
|
||
background: rgba(108,92,231,0.1);
|
||
border-bottom: 2px solid var(--accent);
|
||
}
|
||
.flash-panel { display: none; }
|
||
.flash-panel.active { display: block; }
|
||
.flash-panel h3 {
|
||
font-size: 1.1rem;
|
||
margin-bottom: 0.8rem;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
}
|
||
.flash-panel ol {
|
||
list-style: decimal;
|
||
padding-left: 1.5rem;
|
||
color: var(--text);
|
||
line-height: 2;
|
||
font-size: 0.95rem;
|
||
}
|
||
.flash-panel ol li { margin-bottom: 0.3rem; }
|
||
.flash-panel a {
|
||
color: var(--accent);
|
||
text-decoration: none;
|
||
font-weight: 600;
|
||
}
|
||
.flash-panel a:hover { text-decoration: underline; }
|
||
.flash-cmd {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
background: rgba(0,0,0,0.4);
|
||
border: 1px solid var(--border);
|
||
border-radius: 8px;
|
||
padding: 0.6rem 1rem;
|
||
margin: 0.5rem 0;
|
||
font-family: 'SF Mono', 'Fira Code', monospace;
|
||
font-size: 0.85rem;
|
||
color: var(--accent2);
|
||
overflow-x: auto;
|
||
}
|
||
.flash-cmd code { flex: 1; white-space: nowrap; }
|
||
.flash-cmd button {
|
||
background: none;
|
||
border: 1px solid var(--border);
|
||
color: var(--dim);
|
||
border-radius: 6px;
|
||
padding: 0.3rem 0.6rem;
|
||
cursor: pointer;
|
||
font-size: 0.75rem;
|
||
flex-shrink: 0;
|
||
}
|
||
.flash-cmd button:hover { color: var(--text); border-color: var(--accent); }
|
||
.flash-warn {
|
||
background: rgba(253,203,110,0.1);
|
||
border: 1px solid rgba(253,203,110,0.25);
|
||
border-radius: 8px;
|
||
padding: 0.7rem 1rem;
|
||
color: var(--gold);
|
||
font-size: 0.85rem;
|
||
margin-top: 0.8rem;
|
||
}
|
||
|
||
@media (max-width: 600px) {
|
||
.dl-card { margin: 0 1rem 3rem; padding: 1.5rem; }
|
||
.flash-guide { margin: 0 1rem 3rem; padding: 1.5rem; }
|
||
.stats-grid { grid-template-columns: 1fr 1fr; }
|
||
nav { padding: 0.8rem 1rem; }
|
||
.hero { padding: 2rem 1rem 1rem; }
|
||
.flash-tabs { flex-wrap: wrap; }
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<!-- ── Nav ──────────────────────────────────────── -->
|
||
<nav>
|
||
<div class="logo">Alfred <span>Linux</span></div>
|
||
<div class="links">
|
||
<a href="/">Home</a>
|
||
<a href="/download" style="color:var(--accent);font-weight:600;">Download</a>
|
||
<a href="/apps">Apps</a>
|
||
<a href="/docs">Docs</a>
|
||
<a href="/releases">Releases</a>
|
||
<a href="/security">Security</a>
|
||
<a href="/developers">Developers</a>
|
||
<a href="/compare">Compare</a>
|
||
<a href="/about">About</a>
|
||
</div>
|
||
</nav>
|
||
|
||
<!-- ── Hero ─────────────────────────────────────── -->
|
||
<section class="hero">
|
||
<div class="badge">⚡ Peer-to-Peer · Zero Install · Browser-Native</div>
|
||
<h1>Download at the <span class="glow">Speed of the Swarm</span></h1>
|
||
<p>No torrent client needed. Your browser IS the client. Every downloader becomes a seeder.<br>
|
||
The more people who join, the faster everyone downloads.</p>
|
||
</section>
|
||
|
||
<!-- ── Download Card ───────────────────────────── -->
|
||
<div class="dl-card">
|
||
|
||
<!-- Start -->
|
||
<div id="start-section">
|
||
<h2>Alfred Linux <?= htmlspecialchars($version) ?></h2>
|
||
<div class="meta"><?= htmlspecialchars($isoName) ?> · <?= htmlspecialchars($isoSize) ?> · Debian Trixie 13 · Kernel 7.0.0-rc7 · 16 Hooks · 32 Security Modules · Hardened</div>
|
||
<button class="btn-torrent" onclick="startDownload()">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 5v14M5 12l7 7 7-7"/></svg>
|
||
Start P2P Download
|
||
</button>
|
||
<div class="alt-links">
|
||
<a href="<?= htmlspecialchars($torrentURL) ?>">Download .torrent file</a> (for desktop torrent clients like qBittorrent)
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Progress -->
|
||
<div id="progress-section">
|
||
<div class="progress-bar-outer">
|
||
<div class="progress-bar-inner" id="progress-bar"></div>
|
||
<div class="progress-pct" id="progress-pct">0%</div>
|
||
</div>
|
||
<div class="stats-grid">
|
||
<div class="stat-box">
|
||
<div class="label">Downloaded</div>
|
||
<div class="value" id="stat-downloaded">0 MB</div>
|
||
</div>
|
||
<div class="stat-box">
|
||
<div class="label">Speed</div>
|
||
<div class="value" id="stat-speed">0 KB/s</div>
|
||
</div>
|
||
<div class="stat-box">
|
||
<div class="label">Peers</div>
|
||
<div class="value" id="stat-peers">0</div>
|
||
</div>
|
||
<div class="stat-box">
|
||
<div class="label">ETA</div>
|
||
<div class="value" id="stat-eta">—</div>
|
||
</div>
|
||
<div class="stat-box">
|
||
<div class="label">Uploaded</div>
|
||
<div class="value" id="stat-uploaded">0 MB</div>
|
||
</div>
|
||
</div>
|
||
<p style="color:var(--dim);font-size:0.85rem;text-align:center;">
|
||
You're already sharing pieces with other downloaders! ✨
|
||
</p>
|
||
</div>
|
||
|
||
<!-- Seed / Complete -->
|
||
<div id="seed-section">
|
||
<div class="seed-banner">
|
||
<h2>🎉 Download Complete!</h2>
|
||
<p>Your copy of Alfred Linux is ready. But here's the thing —</p>
|
||
<p class="big-msg">
|
||
The longer you keep this page open,<br>
|
||
the faster the new free world is built<br>
|
||
with YOUR help!
|
||
</p>
|
||
</div>
|
||
|
||
<div class="seed-timer" id="seed-timer">00:00:00</div>
|
||
<div class="seed-timer-label">Time you've been seeding for the community</div>
|
||
|
||
<div class="seed-stats">
|
||
<div class="stat-box">
|
||
<div class="label">You've Shared</div>
|
||
<div class="value" id="seed-uploaded">0 MB</div>
|
||
</div>
|
||
<div class="stat-box">
|
||
<div class="label">Upload Speed</div>
|
||
<div class="value" id="seed-speed">0 KB/s</div>
|
||
</div>
|
||
<div class="stat-box">
|
||
<div class="label">Souls Saved</div>
|
||
<div class="value" id="seed-peers" style="color:var(--gold);">0</div>
|
||
</div>
|
||
<div class="stat-box">
|
||
<div class="label">Your Ratio</div>
|
||
<div class="value" id="seed-ratio">0.00</div>
|
||
</div>
|
||
</div>
|
||
|
||
<button class="btn-save" id="save-btn">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="18" height="18"><path d="M12 5v14M5 12l7 7 7-7"/></svg>
|
||
Save ISO to Disk
|
||
</button>
|
||
|
||
<div style="margin-top: 2rem;">
|
||
<p style="color:var(--dim);font-size:0.9rem;margin-bottom:0.8rem;">Spread the word:</p>
|
||
<button class="share-btn" onclick="shareTwitter()">𝕏 Share on X</button>
|
||
<button class="share-btn" onclick="shareLink()">🔗 Copy Link</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="error-box" id="error-box"></div>
|
||
</div>
|
||
|
||
<!-- ── Flash to USB Guide ──────────────────────── -->
|
||
<div class="flash-guide" id="flash-guide">
|
||
<h2>Flash to USB Drive</h2>
|
||
<p class="subtitle">Turn your ISO into a bootable USB stick in minutes</p>
|
||
|
||
<div class="flash-tabs">
|
||
<button class="flash-tab active" onclick="showFlashTab('windows')">Windows</button>
|
||
<button class="flash-tab" onclick="showFlashTab('mac')">macOS</button>
|
||
<button class="flash-tab" onclick="showFlashTab('linux')">Linux</button>
|
||
<button class="flash-tab" onclick="showFlashTab('etcher')">balenaEtcher</button>
|
||
</div>
|
||
|
||
<div class="flash-panel active" id="flash-windows">
|
||
<h3>💻 Rufus (Windows)</h3>
|
||
<ol>
|
||
<li>Download <a href="https://rufus.ie" target="_blank" rel="noopener">Rufus</a> (free, portable, no install needed)</li>
|
||
<li>Insert a USB drive (8 GB minimum)</li>
|
||
<li>Open Rufus → select your USB under <strong>Device</strong></li>
|
||
<li>Click <strong>SELECT</strong> → choose the Alfred Linux ISO you just saved</li>
|
||
<li>Partition scheme: <strong>GPT</strong> · Target: <strong>UEFI</strong></li>
|
||
<li>Click <strong>START</strong> → wait until “READY” appears</li>
|
||
<li>Reboot your PC and boot from USB (usually F12 / F2 / DEL at startup)</li>
|
||
</ol>
|
||
</div>
|
||
|
||
<div class="flash-panel" id="flash-mac">
|
||
<h3>🍎 Terminal (macOS)</h3>
|
||
<ol>
|
||
<li>Insert a USB drive (8 GB minimum)</li>
|
||
<li>Open Terminal and find your USB disk:</li>
|
||
</ol>
|
||
<div class="flash-cmd"><code>diskutil list</code><button onclick="copyCmd(this)">Copy</button></div>
|
||
<ol start="3">
|
||
<li>Unmount the USB (replace <strong>diskN</strong> with your disk):</li>
|
||
</ol>
|
||
<div class="flash-cmd"><code>diskutil unmountDisk /dev/diskN</code><button onclick="copyCmd(this)">Copy</button></div>
|
||
<ol start="4">
|
||
<li>Flash the ISO (use <strong>rdiskN</strong> for speed):</li>
|
||
</ol>
|
||
<div class="flash-cmd"><code>sudo dd if=~/Downloads/<?= htmlspecialchars($isoName) ?> of=/dev/rdiskN bs=4m status=progress</code><button onclick="copyCmd(this)">Copy</button></div>
|
||
<ol start="5">
|
||
<li>Wait until complete, then eject and reboot from USB</li>
|
||
</ol>
|
||
<div class="flash-warn">⚠️ Double-check the disk number! <code>dd</code> overwrites the target without confirmation.</div>
|
||
</div>
|
||
|
||
<div class="flash-panel" id="flash-linux">
|
||
<h3>🐧 Terminal (Linux)</h3>
|
||
<ol>
|
||
<li>Insert a USB drive (8 GB minimum)</li>
|
||
<li>Find your USB device:</li>
|
||
</ol>
|
||
<div class="flash-cmd"><code>lsblk</code><button onclick="copyCmd(this)">Copy</button></div>
|
||
<ol start="3">
|
||
<li>Flash the ISO (replace <strong>/dev/sdX</strong> with your USB — e.g. /dev/sdb):</li>
|
||
</ol>
|
||
<div class="flash-cmd"><code>sudo dd if=~/Downloads/<?= htmlspecialchars($isoName) ?> of=/dev/sdX bs=4M status=progress oflag=sync</code><button onclick="copyCmd(this)">Copy</button></div>
|
||
<ol start="4">
|
||
<li>Wait until complete, then reboot and boot from USB</li>
|
||
</ol>
|
||
<div class="flash-warn">⚠️ Make sure <code>/dev/sdX</code> is your USB, not your main drive! Check <code>lsblk</code> output carefully.</div>
|
||
</div>
|
||
|
||
<div class="flash-panel" id="flash-etcher">
|
||
<h3>⚡ balenaEtcher (Any OS)</h3>
|
||
<ol>
|
||
<li>Download <a href="https://etcher.balena.io" target="_blank" rel="noopener">balenaEtcher</a> (free, works on Windows/Mac/Linux)</li>
|
||
<li>Open Etcher → click <strong>Flash from file</strong> → select the Alfred Linux ISO</li>
|
||
<li>Click <strong>Select target</strong> → choose your USB drive</li>
|
||
<li>Click <strong>Flash!</strong> and wait for completion + verification</li>
|
||
<li>Reboot and boot from USB</li>
|
||
</ol>
|
||
<p style="color:var(--dim);font-size:0.85rem;margin-top:1rem;">Etcher is the easiest option if you're not comfortable with command-line tools. It validates the write automatically.</p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ── Community Swarm ─────────────────────────── -->
|
||
<section class="community">
|
||
<h2>The Alfred Swarm</h2>
|
||
<p>Every dot is a person — downloading, seeding, building the future of computing together.</p>
|
||
<div class="swarm-vis">
|
||
<div class="swarm-counter" id="swarm-count">0</div>
|
||
<div class="swarm-label">active peers in the swarm right now</div>
|
||
<canvas id="swarm-canvas"></canvas>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ── Integrity Verification ───────────────────── -->
|
||
<section class="how-it-works" style="border-top:1px solid var(--border);">
|
||
<h2>Verify Your Download</h2>
|
||
<p style="color:var(--dim);text-align:center;max-width:700px;margin:0 auto 2rem;line-height:1.6;">
|
||
Two independent hash algorithms — different math, different authors, different attack surfaces.<br>
|
||
If an attacker somehow breaks one, the other catches it. No other distro does this.
|
||
</p>
|
||
<div style="background:var(--surface);border:1px solid var(--border);border-radius:12px;padding:1.5rem;max-width:800px;margin:0 auto 1.5rem;">
|
||
<h3 style="color:var(--accent2);margin-bottom:1rem;font-size:1rem;">SHA-256 <span style="color:var(--dim);font-weight:400;font-size:0.85rem;">(NIST Standard)</span></h3>
|
||
<div style="background:#0a0a0f;border-radius:8px;padding:0.8rem 1rem;font-family:monospace;font-size:0.8rem;word-break:break-all;color:var(--gold);margin-bottom:0.5rem;">
|
||
7d49ef3cfb957cb9854bd3f451ef99ec8255afd68069a89ed0cf5a847d5d79bf
|
||
</div>
|
||
<div style="font-size:0.8rem;color:var(--dim);">
|
||
<code style="color:var(--text);background:#1e1e2e;padding:2px 6px;border-radius:4px;">sha256sum alfred-linux-4.0-rc8-amd64-20260407.iso</code>
|
||
</div>
|
||
</div>
|
||
<div style="background:var(--surface);border:1px solid var(--border);border-radius:12px;padding:1.5rem;max-width:800px;margin:0 auto 1.5rem;">
|
||
<h3 style="color:var(--accent);margin-bottom:1rem;font-size:1rem;">BLAKE3 <span style="color:var(--dim);font-weight:400;font-size:0.85rem;">(Fastest & most secure hash on the planet)</span></h3>
|
||
<div style="background:#0a0a0f;border-radius:8px;padding:0.8rem 1rem;font-family:monospace;font-size:0.8rem;word-break:break-all;color:var(--accent2);margin-bottom:0.5rem;">
|
||
e021d2024599aa918972d9e6b9fd9c1d97d226ac69da035913fd7a462dbef47d
|
||
</div>
|
||
<div style="font-size:0.8rem;color:var(--dim);">
|
||
<code style="color:var(--text);background:#1e1e2e;padding:2px 6px;border-radius:4px;">b3sum alfred-linux-4.0-rc8-amd64-20260407.iso</code>
|
||
· Install: <code style="color:var(--text);background:#1e1e2e;padding:2px 6px;border-radius:4px;">pip install blake3</code> or <code style="color:var(--text);background:#1e1e2e;padding:2px 6px;border-radius:4px;">cargo install b3sum</code>
|
||
</div>
|
||
</div>
|
||
<p style="color:var(--dim);text-align:center;font-size:0.85rem;margin-top:1rem;">
|
||
Both hashes must match. If either one doesn't — <strong style="color:var(--danger);">do not install</strong>. Re-download via P2P.
|
||
</p>
|
||
</section>
|
||
|
||
<!-- ── How It Works ────────────────────────────── -->
|
||
<section class="how-it-works">
|
||
<h2>How Peer-to-Peer Download Works</h2>
|
||
<div class="steps">
|
||
<div class="step">
|
||
<div class="step-num">1</div>
|
||
<div>
|
||
<h3>Click "Start P2P Download"</h3>
|
||
<p>Your browser connects to the Alfred Linux swarm using WebTorrent — a BitTorrent client that runs natively in your browser. No plugins, no installs.</p>
|
||
</div>
|
||
</div>
|
||
<div class="step">
|
||
<div class="step-num">2</div>
|
||
<div>
|
||
<h3>Download from everyone at once</h3>
|
||
<p>Instead of one server, you download pieces from every peer in the swarm simultaneously. More people = faster download for everyone.</p>
|
||
</div>
|
||
</div>
|
||
<div class="step">
|
||
<div class="step-num">3</div>
|
||
<div>
|
||
<h3>You become a seeder</h3>
|
||
<p>While downloading, you're already sharing completed pieces with others. When done, you keep sharing as long as the page stays open.</p>
|
||
</div>
|
||
</div>
|
||
<div class="step">
|
||
<div class="step-num">4</div>
|
||
<div>
|
||
<h3>The swarm grows stronger</h3>
|
||
<p>Every seeder makes the next download faster. You're not just getting an OS — you're powering a movement. The longer you seed, the more people you help.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ── Footer ──────────────────────────────────── -->
|
||
<footer>
|
||
<p>Alfred Linux © <?= date('Y') ?> · <a href="/">alfredlinux.com</a> · <a href="/forge/">GoForge</a> · Powered by WebTorrent + the Community</p>
|
||
</footer>
|
||
|
||
<!-- ── WebTorrent Browser Client ───────────────── -->
|
||
<script src="/assets/js/webtorrent.min.js"></script>
|
||
<script>
|
||
(function() {
|
||
'use strict';
|
||
|
||
// ── Config ────────────────────────────────────
|
||
const ISO_NAME = <?= json_encode($isoName) ?>;
|
||
const DIRECT_URL = ''; // Torrent-only distribution
|
||
const TORRENT_URL = <?= json_encode($torrentURL) ?>;
|
||
const MAGNET_URI = <?= json_encode($magnetURI) ?>;
|
||
|
||
let client = null;
|
||
let torrent = null;
|
||
let seedStart = null;
|
||
let blobURL = null;
|
||
let seedTimer = null;
|
||
|
||
// ── UI Elements ───────────────────────────────
|
||
const $start = document.getElementById('start-section');
|
||
const $progress = document.getElementById('progress-section');
|
||
const $seed = document.getElementById('seed-section');
|
||
const $error = document.getElementById('error-box');
|
||
|
||
// ── Format helpers ────────────────────────────
|
||
function fmtBytes(b) {
|
||
if (b < 1024) return b + ' B';
|
||
if (b < 1048576) return (b / 1024).toFixed(1) + ' KB';
|
||
if (b < 1073741824) return (b / 1048576).toFixed(1) + ' MB';
|
||
return (b / 1073741824).toFixed(2) + ' GB';
|
||
}
|
||
function fmtSpeed(bps) {
|
||
if (bps < 1024) return bps + ' B/s';
|
||
if (bps < 1048576) return (bps / 1024).toFixed(0) + ' KB/s';
|
||
return (bps / 1048576).toFixed(1) + ' MB/s';
|
||
}
|
||
function fmtTime(secs) {
|
||
if (!secs || secs === Infinity) return '—';
|
||
const h = Math.floor(secs / 3600);
|
||
const m = Math.floor((secs % 3600) / 60);
|
||
const s = Math.floor(secs % 60);
|
||
return (h > 0 ? h + 'h ' : '') + m + 'm ' + s + 's';
|
||
}
|
||
function fmtTimer(secs) {
|
||
const h = String(Math.floor(secs / 3600)).padStart(2, '0');
|
||
const m = String(Math.floor((secs % 3600) / 60)).padStart(2, '0');
|
||
const s = String(Math.floor(secs % 60)).padStart(2, '0');
|
||
return h + ':' + m + ':' + s;
|
||
}
|
||
|
||
// ── Start Download ────────────────────────────
|
||
window.startDownload = function() {
|
||
if (!window.WebTorrent) {
|
||
showError('WebTorrent library failed to load. Please <a href="' + TORRENT_URL + '">download the .torrent file</a> and use a desktop torrent client instead.');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
client = new WebTorrent();
|
||
} catch (e) {
|
||
showError('Could not initialize WebTorrent: ' + e.message + '. <a href="' + TORRENT_URL + '">Download .torrent file</a> instead.');
|
||
return;
|
||
}
|
||
|
||
client.on('error', function(err) {
|
||
console.error('WebTorrent error:', err);
|
||
showError('Torrent error: ' + err.message + '. <a href="' + TORRENT_URL + '">Download .torrent file</a> and use a desktop torrent client instead.');
|
||
});
|
||
|
||
$start.style.display = 'none';
|
||
$progress.style.display = 'block';
|
||
|
||
// Use magnet URI directly — most reliable method for browser WebTorrent
|
||
console.log('[Alfred] Adding torrent via magnet URI');
|
||
addTorrentFromMagnet(MAGNET_URI);
|
||
};
|
||
|
||
function addTorrentFromMagnet(magnetURI) {
|
||
client.add(magnetURI, {
|
||
announce: [
|
||
'wss://tracker.openwebtorrent.com',
|
||
]
|
||
}, function(t) {
|
||
torrent = t;
|
||
console.log('Torrent added:', t.infoHash, '— Files:', t.files.length);
|
||
|
||
// Update progress periodically
|
||
var progressInterval = setInterval(function() {
|
||
var pct = Math.round(torrent.progress * 100);
|
||
document.getElementById('progress-bar').style.width = pct + '%';
|
||
document.getElementById('progress-pct').textContent = pct + '%';
|
||
document.getElementById('stat-downloaded').textContent = fmtBytes(torrent.downloaded);
|
||
document.getElementById('stat-speed').textContent = fmtSpeed(torrent.downloadSpeed);
|
||
document.getElementById('stat-peers').textContent = torrent.numPeers;
|
||
document.getElementById('stat-eta').textContent = fmtTime(torrent.timeRemaining / 1000);
|
||
document.getElementById('stat-uploaded').textContent = fmtBytes(torrent.uploaded);
|
||
|
||
// Update swarm counter
|
||
document.getElementById('swarm-count').textContent = torrent.numPeers;
|
||
}, 500);
|
||
|
||
torrent.on('done', function() {
|
||
clearInterval(progressInterval);
|
||
showSeedMode();
|
||
});
|
||
});
|
||
};
|
||
|
||
// ── Seed Mode ─────────────────────────────────
|
||
function showSeedMode() {
|
||
$progress.style.display = 'none';
|
||
$seed.style.display = 'block';
|
||
seedStart = Date.now();
|
||
|
||
// Save button — use File System Access API for large files, fall back to direct HTTP
|
||
var saveBtn = document.getElementById('save-btn');
|
||
saveBtn.onclick = async function() {
|
||
// Try modern File System Access API first (Chrome/Edge 86+)
|
||
if (window.showSaveFilePicker) {
|
||
try {
|
||
var handle = await window.showSaveFilePicker({
|
||
suggestedName: ISO_NAME,
|
||
types: [{ description: 'ISO Image', accept: { 'application/octet-stream': ['.iso'] } }]
|
||
});
|
||
var writable = await handle.createWritable();
|
||
var file = torrent.files[0];
|
||
var stream = file.createReadStream();
|
||
saveBtn.textContent = 'Saving...';
|
||
saveBtn.disabled = true;
|
||
|
||
stream.on('data', function(chunk) { writable.write(chunk); });
|
||
stream.on('end', function() {
|
||
writable.close();
|
||
saveBtn.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="18" height="18"><path d="M20 6L9 17l-5-5"/></svg> Saved!';
|
||
});
|
||
stream.on('error', function(e) {
|
||
console.error('Stream error:', e);
|
||
writable.close();
|
||
// Fall back to direct download
|
||
triggerDirectDownload();
|
||
});
|
||
return;
|
||
} catch (e) {
|
||
if (e.name === 'AbortError') return; // user cancelled
|
||
console.warn('File System Access failed:', e);
|
||
}
|
||
}
|
||
// Fallback: direct HTTP download (file already came via P2P, this just saves it)
|
||
triggerDirectDownload();
|
||
};
|
||
|
||
function triggerDirectDownload() {
|
||
// Save from WebTorrent blob — ISO is not served via HTTP
|
||
if (!torrent || !torrent.files || !torrent.files[0]) {
|
||
showError('No downloaded file available. Please re-download via P2P.');
|
||
return;
|
||
}
|
||
torrent.files[0].getBlob(function(err, blob) {
|
||
if (err) {
|
||
showError('Could not create file: ' + err.message);
|
||
return;
|
||
}
|
||
var url = URL.createObjectURL(blob);
|
||
var a = document.createElement('a');
|
||
a.href = url;
|
||
a.download = ISO_NAME;
|
||
a.click();
|
||
setTimeout(function() { URL.revokeObjectURL(url); }, 60000);
|
||
});
|
||
}
|
||
|
||
// Show the Flash to USB guide
|
||
var flashGuide = document.getElementById('flash-guide');
|
||
if (flashGuide) flashGuide.classList.add('visible');
|
||
|
||
// Seed stats updater
|
||
seedTimer = setInterval(function() {
|
||
var elapsed = Math.floor((Date.now() - seedStart) / 1000);
|
||
document.getElementById('seed-timer').textContent = fmtTimer(elapsed);
|
||
document.getElementById('seed-uploaded').textContent = fmtBytes(torrent.uploaded);
|
||
document.getElementById('seed-speed').textContent = fmtSpeed(torrent.uploadSpeed);
|
||
document.getElementById('seed-peers').textContent = torrent.numPeers;
|
||
document.getElementById('seed-ratio').textContent = torrent.ratio.toFixed(2);
|
||
document.getElementById('swarm-count').textContent = torrent.numPeers;
|
||
}, 1000);
|
||
}
|
||
|
||
// ── Share ─────────────────────────────────────
|
||
window.shareTwitter = function() {
|
||
var text = encodeURIComponent(
|
||
"I just downloaded Alfred Linux via peer-to-peer — directly in my browser! 🐧⚡\n\n" +
|
||
"No torrent client needed. The more people who download, the faster it gets for everyone.\n\n" +
|
||
"Join the swarm: https://alfredlinux.com/download\n\n#AlfredLinux #OpenSource #P2P"
|
||
);
|
||
window.open('https://x.com/intent/tweet?text=' + text, '_blank', 'noopener,noreferrer');
|
||
};
|
||
|
||
window.shareLink = function() {
|
||
navigator.clipboard.writeText('https://alfredlinux.com/download').then(function() {
|
||
var btn = document.querySelector('.share-btn:last-child');
|
||
btn.textContent = '✅ Copied!';
|
||
setTimeout(function() { btn.textContent = '🔗 Copy Link'; }, 2000);
|
||
});
|
||
};
|
||
|
||
// ── Error handling ────────────────────────────
|
||
function showError(msg) {
|
||
$error.style.display = 'block';
|
||
$error.innerHTML = msg;
|
||
}
|
||
|
||
// ── Swarm Visualization ───────────────────────
|
||
(function initSwarmVis() {
|
||
var canvas = document.getElementById('swarm-canvas');
|
||
if (!canvas) return;
|
||
var ctx = canvas.getContext('2d');
|
||
var particles = [];
|
||
|
||
function resize() {
|
||
canvas.width = canvas.parentElement.clientWidth;
|
||
canvas.height = 200;
|
||
}
|
||
resize();
|
||
window.addEventListener('resize', resize);
|
||
|
||
// Create initial particles
|
||
for (var i = 0; i < 30; i++) {
|
||
particles.push({
|
||
x: Math.random() * canvas.width,
|
||
y: Math.random() * canvas.height,
|
||
vx: (Math.random() - 0.5) * 0.8,
|
||
vy: (Math.random() - 0.5) * 0.8,
|
||
r: 2 + Math.random() * 3,
|
||
color: Math.random() > 0.5 ? '#6c5ce7' : '#00cec9',
|
||
});
|
||
}
|
||
|
||
function draw() {
|
||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||
|
||
// Draw connections
|
||
for (var i = 0; i < particles.length; i++) {
|
||
for (var j = i + 1; j < particles.length; j++) {
|
||
var dx = particles[i].x - particles[j].x;
|
||
var dy = particles[i].y - particles[j].y;
|
||
var dist = Math.sqrt(dx * dx + dy * dy);
|
||
if (dist < 120) {
|
||
ctx.beginPath();
|
||
ctx.strokeStyle = 'rgba(108,92,231,' + (1 - dist / 120) * 0.2 + ')';
|
||
ctx.lineWidth = 0.5;
|
||
ctx.moveTo(particles[i].x, particles[i].y);
|
||
ctx.lineTo(particles[j].x, particles[j].y);
|
||
ctx.stroke();
|
||
}
|
||
}
|
||
}
|
||
|
||
// Draw particles
|
||
for (var k = 0; k < particles.length; k++) {
|
||
var p = particles[k];
|
||
p.x += p.vx;
|
||
p.y += p.vy;
|
||
if (p.x < 0 || p.x > canvas.width) p.vx *= -1;
|
||
if (p.y < 0 || p.y > canvas.height) p.vy *= -1;
|
||
|
||
ctx.beginPath();
|
||
ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2);
|
||
ctx.fillStyle = p.color;
|
||
ctx.fill();
|
||
ctx.beginPath();
|
||
ctx.arc(p.x, p.y, p.r + 4, 0, Math.PI * 2);
|
||
ctx.fillStyle = p.color.replace(')', ',0.15)').replace('rgb', 'rgba');
|
||
ctx.fill();
|
||
}
|
||
|
||
requestAnimationFrame(draw);
|
||
}
|
||
draw();
|
||
|
||
// Add particles when peers connect
|
||
setInterval(function() {
|
||
var count = torrent ? torrent.numPeers : 0;
|
||
while (particles.length < Math.min(count + 10, 80)) {
|
||
particles.push({
|
||
x: Math.random() * canvas.width,
|
||
y: Math.random() * canvas.height,
|
||
vx: (Math.random() - 0.5) * 0.8,
|
||
vy: (Math.random() - 0.5) * 0.8,
|
||
r: 2 + Math.random() * 3,
|
||
color: Math.random() > 0.5 ? '#6c5ce7' : '#00cec9',
|
||
});
|
||
}
|
||
}, 3000);
|
||
})();
|
||
|
||
// ── Flash to USB tab switching ────────────────
|
||
window.showFlashTab = function(tab) {
|
||
document.querySelectorAll('.flash-tab').forEach(function(t) { t.classList.remove('active'); });
|
||
document.querySelectorAll('.flash-panel').forEach(function(p) { p.classList.remove('active'); });
|
||
event.target.classList.add('active');
|
||
var panel = document.getElementById('flash-' + tab);
|
||
if (panel) panel.classList.add('active');
|
||
};
|
||
|
||
window.copyCmd = function(btn) {
|
||
var code = btn.parentElement.querySelector('code').textContent;
|
||
navigator.clipboard.writeText(code).then(function() {
|
||
btn.textContent = 'Copied!';
|
||
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
|
||
});
|
||
};
|
||
|
||
// Auto-select the right tab based on OS
|
||
(function detectOS() {
|
||
var ua = navigator.userAgent;
|
||
var tab = 'etcher'; // default
|
||
if (/Win/.test(ua)) tab = 'windows';
|
||
else if (/Mac/.test(ua)) tab = 'mac';
|
||
else if (/Linux/.test(ua)) tab = 'linux';
|
||
document.querySelectorAll('.flash-tab').forEach(function(t) { t.classList.remove('active'); });
|
||
document.querySelectorAll('.flash-panel').forEach(function(p) { p.classList.remove('active'); });
|
||
var tabs = document.querySelectorAll('.flash-tab');
|
||
tabs.forEach(function(t) { if (t.textContent.toLowerCase().indexOf(tab === 'windows' ? 'windows' : tab === 'mac' ? 'mac' : tab === 'linux' ? 'linux' : 'etcher') !== -1) t.classList.add('active'); });
|
||
var panel = document.getElementById('flash-' + tab);
|
||
if (panel) panel.classList.add('active');
|
||
})();
|
||
|
||
// ── Warn before leaving while seeding ─────────
|
||
window.addEventListener('beforeunload', function(e) {
|
||
if (torrent && torrent.done) {
|
||
e.preventDefault();
|
||
e.returnValue = 'You\'re currently seeding Alfred Linux for the community. Are you sure you want to leave?';
|
||
}
|
||
});
|
||
|
||
})();
|
||
</script>
|
||
|
||
<footer style="text-align:center;padding:1.5rem;color:#94a3b8;font-size:.85rem;border-top:1px solid rgba(255,255,255,0.06);">
|
||
© <?= date('Y') ?> <a href="https://gositeme.com" style="color:#6366f1;text-decoration:none;">GoSiteMe Inc.</a> — Alfred Linux · Open Source (AGPL-3.0)
|
||
</footer>
|
||
|
||
</body>
|
||
</html>
|