mirror of
git://f0xx.org/ac/ac-be-hub
synced 2026-07-29 05:58:59 +03:00
feat(hub): shared nav shell, downloads UI, footer fix
Signed-off-by: Anton Afanasyeu <a.afanasieff@gmail.com>
This commit is contained in:
@@ -1,6 +1,9 @@
|
|||||||
(function () {
|
(function () {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
/** Orchestration default channel; stable is fallback for promoted releases. */
|
||||||
|
var OTA_CHANNELS = ['staging', 'stable'];
|
||||||
|
|
||||||
function setStatus(msg) {
|
function setStatus(msg) {
|
||||||
var el = document.getElementById('hub-download-apk-status');
|
var el = document.getElementById('hub-download-apk-status');
|
||||||
if (el) {
|
if (el) {
|
||||||
@@ -8,32 +11,79 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveStableApkUrl() {
|
function fetchJson(url) {
|
||||||
return fetch('/v0/ota/channel/stable.json', { cache: 'no-store' })
|
return fetch(url, { cache: 'no-store' }).then(function (r) {
|
||||||
.then(function (r) {
|
|
||||||
if (!r.ok) {
|
if (!r.ok) {
|
||||||
throw new Error('stable channel HTTP ' + r.status);
|
throw new Error('HTTP ' + r.status + ' for ' + url);
|
||||||
}
|
|
||||||
return r.json();
|
|
||||||
})
|
|
||||||
.then(function (channel) {
|
|
||||||
var manifestUrl = channel && channel.manifestUrl;
|
|
||||||
if (!manifestUrl) {
|
|
||||||
throw new Error('stable.json missing manifestUrl');
|
|
||||||
}
|
|
||||||
return fetch(manifestUrl, { cache: 'no-store' }).then(function (r) {
|
|
||||||
if (!r.ok) {
|
|
||||||
throw new Error('manifest HTTP ' + r.status);
|
|
||||||
}
|
}
|
||||||
return r.json();
|
return r.json();
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveApkFromChannel(channelName) {
|
||||||
|
return fetchJson('/v0/ota/channel/' + encodeURIComponent(channelName) + '.json')
|
||||||
|
.then(function (channel) {
|
||||||
|
var manifestUrl = channel && channel.manifestUrl;
|
||||||
|
if (!manifestUrl) {
|
||||||
|
throw new Error(channelName + ' missing manifestUrl');
|
||||||
|
}
|
||||||
|
return fetchJson(manifestUrl);
|
||||||
})
|
})
|
||||||
.then(function (manifest) {
|
.then(function (manifest) {
|
||||||
var url = manifest && (manifest.apkUrl || manifest.bundleUrl);
|
var url =
|
||||||
|
(manifest && (manifest.browserApkUrl || manifest.apkUrl)) || '';
|
||||||
if (!url) {
|
if (!url) {
|
||||||
throw new Error('manifest missing apkUrl');
|
throw new Error('manifest missing browserApkUrl/apkUrl');
|
||||||
}
|
}
|
||||||
return url;
|
if (!manifest.browserApkUrl && /\.otapkg(\?|$)/.test(url)) {
|
||||||
|
url = url.replace(/\.otapkg(\?|$)/, '.apk$1');
|
||||||
|
}
|
||||||
|
var nameMatch = url.match(/\/([^/?#]+\.apk)(?:\?|$)/i);
|
||||||
|
var filename = nameMatch ? nameMatch[1] : 'android_cast-latest.apk';
|
||||||
|
return { url: url, filename: filename, channel: channelName };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveApkUrl() {
|
||||||
|
var chain = Promise.reject(new Error('no channel'));
|
||||||
|
OTA_CHANNELS.forEach(function (ch) {
|
||||||
|
chain = chain.catch(function () {
|
||||||
|
return resolveApkFromChannel(ch);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return chain;
|
||||||
|
}
|
||||||
|
|
||||||
|
function triggerDownload(url, filename) {
|
||||||
|
if (/\.apk(\?|$)/i.test(url)) {
|
||||||
|
var a = document.createElement('a');
|
||||||
|
a.href = url;
|
||||||
|
a.download = filename;
|
||||||
|
a.rel = 'noopener';
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
a.remove();
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
return fetch(url, { cache: 'no-store' })
|
||||||
|
.then(function (r) {
|
||||||
|
if (!r.ok) {
|
||||||
|
throw new Error('download HTTP ' + r.status);
|
||||||
|
}
|
||||||
|
return r.blob();
|
||||||
|
})
|
||||||
|
.then(function (blob) {
|
||||||
|
var blobUrl = URL.createObjectURL(blob);
|
||||||
|
var link = document.createElement('a');
|
||||||
|
link.href = blobUrl;
|
||||||
|
link.download = filename;
|
||||||
|
link.rel = 'noopener';
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
link.remove();
|
||||||
|
setTimeout(function () {
|
||||||
|
URL.revokeObjectURL(blobUrl);
|
||||||
|
}, 60000);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,14 +94,17 @@
|
|||||||
}
|
}
|
||||||
btn.addEventListener('click', function () {
|
btn.addEventListener('click', function () {
|
||||||
btn.disabled = true;
|
btn.disabled = true;
|
||||||
setStatus('Resolving stable OTA channel…');
|
setStatus('Resolving OTA channel…');
|
||||||
resolveStableApkUrl()
|
resolveApkUrl()
|
||||||
.then(function (url) {
|
.then(function (resolved) {
|
||||||
setStatus('Downloading…');
|
setStatus('Downloading ' + resolved.filename + ' (' + resolved.channel + ')…');
|
||||||
window.location.href = url;
|
return triggerDownload(resolved.url, resolved.filename);
|
||||||
})
|
})
|
||||||
.catch(function (err) {
|
.catch(function (err) {
|
||||||
setStatus('APK unavailable — run builder OTA publish (stable). ' + (err.message || err));
|
setStatus(
|
||||||
|
'APK unavailable — run builder with OTA publish (staging). ' +
|
||||||
|
(err.message || err)
|
||||||
|
);
|
||||||
})
|
})
|
||||||
.finally(function () {
|
.finally(function () {
|
||||||
btn.disabled = false;
|
btn.disabled = false;
|
||||||
|
|||||||
@@ -67,7 +67,7 @@
|
|||||||
document.documentElement.setAttribute('lang', lang);
|
document.documentElement.setAttribute('lang', lang);
|
||||||
var footerLeft = document.querySelector('.platform-footer__left[data-i18n]');
|
var footerLeft = document.querySelector('.platform-footer__left[data-i18n]');
|
||||||
if (footerLeft) {
|
if (footerLeft) {
|
||||||
var holder = footerLeft.getAttribute('data-holder') || 'Anton Afanaasyeu';
|
var holder = footerLeft.getAttribute('data-holder') || 'Anton Afanasyeu';
|
||||||
var year = footerLeft.getAttribute('data-year') || String(new Date().getFullYear());
|
var year = footerLeft.getAttribute('data-year') || String(new Date().getFullYear());
|
||||||
footerLeft.textContent = t('footer.copyright', { holder: holder, year: year });
|
footerLeft.textContent = t('footer.copyright', { holder: holder, year: year });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,10 +60,10 @@
|
|||||||
"page6.btn_docs": "Other documents",
|
"page6.btn_docs": "Other documents",
|
||||||
"page6.hint": "Wiki opens in Gitea; other documents lists ac-docs drafts, DRs, and specs.",
|
"page6.hint": "Wiki opens in Gitea; other documents lists ac-docs drafts, DRs, and specs.",
|
||||||
"page7.title": "Downloads",
|
"page7.title": "Downloads",
|
||||||
"page7.lead": "Latest stable builds from the orchestration pipeline (next integration).",
|
"page7.lead": "Latest builds from the orchestration pipeline (staging channel, branch next).",
|
||||||
"page7.btn_apk": "Android APK",
|
"page7.btn_apk": "Android APK",
|
||||||
"page7.btn_studio": "Session Studio",
|
"page7.btn_studio": "Session Studio",
|
||||||
"page7.hint": "APK matches the stable OTA channel signature and version.",
|
"page7.hint": "APK is the installable package from the staging OTA channel (falls back to stable).",
|
||||||
"page8.title": "Resources",
|
"page8.title": "Resources",
|
||||||
"page8.lead": "Source control, consoles, and integration entry points.",
|
"page8.lead": "Source control, consoles, and integration entry points.",
|
||||||
"page8.btn_git": "Git Access",
|
"page8.btn_git": "Git Access",
|
||||||
|
|||||||
@@ -60,10 +60,10 @@
|
|||||||
"page6.btn_docs": "Прочие документы",
|
"page6.btn_docs": "Прочие документы",
|
||||||
"page6.hint": "Wiki в Gitea; каталог — черновики ac-docs, DR и specs.",
|
"page6.hint": "Wiki в Gitea; каталог — черновики ac-docs, DR и specs.",
|
||||||
"page7.title": "Загрузки",
|
"page7.title": "Загрузки",
|
||||||
"page7.lead": "Последние stable-сборки из orchestration (ветка next).",
|
"page7.lead": "Последние сборки из orchestration (канал staging, ветка next).",
|
||||||
"page7.btn_apk": "Android APK",
|
"page7.btn_apk": "Android APK",
|
||||||
"page7.btn_studio": "Session Studio",
|
"page7.btn_studio": "Session Studio",
|
||||||
"page7.hint": "APK совпадает с stable OTA-каналом.",
|
"page7.hint": "APK — установочный пакет из staging OTA-канала (fallback на stable).",
|
||||||
"page8.title": "Ресурсы",
|
"page8.title": "Ресурсы",
|
||||||
"page8.lead": "Исходники, консоли и точки интеграции.",
|
"page8.lead": "Исходники, консоли и точки интеграции.",
|
||||||
"page8.btn_git": "Git Access",
|
"page8.btn_git": "Git Access",
|
||||||
|
|||||||
@@ -259,12 +259,28 @@
|
|||||||
gap: 12px;
|
gap: 12px;
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
}
|
}
|
||||||
.landing-gallery img {
|
.landing-gallery__tile {
|
||||||
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
aspect-ratio: 4 / 3;
|
aspect-ratio: 4 / 3;
|
||||||
object-fit: cover;
|
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
|
background: linear-gradient(145deg, rgba(61, 139, 253, 0.22), rgba(12, 24, 48, 0.85));
|
||||||
|
}
|
||||||
|
.landing-gallery__tile--network {
|
||||||
|
background: linear-gradient(145deg, rgba(61, 139, 253, 0.35), rgba(8, 18, 36, 0.9));
|
||||||
|
}
|
||||||
|
.landing-gallery__tile--cluster {
|
||||||
|
background: linear-gradient(145deg, rgba(46, 196, 182, 0.28), rgba(8, 18, 36, 0.9));
|
||||||
|
}
|
||||||
|
.landing-gallery__tile--ops {
|
||||||
|
background: linear-gradient(145deg, rgba(255, 159, 67, 0.25), rgba(8, 18, 36, 0.9));
|
||||||
|
}
|
||||||
|
.landing-gallery__tile--team {
|
||||||
|
background: linear-gradient(145deg, rgba(155, 89, 182, 0.28), rgba(8, 18, 36, 0.9));
|
||||||
|
}
|
||||||
|
.landing-gallery__tile--collab {
|
||||||
|
background: linear-gradient(145deg, rgba(61, 139, 253, 0.22), rgba(46, 196, 182, 0.18));
|
||||||
}
|
}
|
||||||
|
|
||||||
.landing-feature-grid {
|
.landing-feature-grid {
|
||||||
@@ -475,14 +491,63 @@
|
|||||||
.landing-rail { transition: none; }
|
.landing-rail { transition: none; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Downloads / documentation big rounded CTAs */
|
/* Downloads — tall cards: icon on top, resource name below */
|
||||||
.landing-download-grid {
|
.landing-download-grid {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 16px;
|
gap: 20px;
|
||||||
margin: 20px 0 12px;
|
margin: 20px 0 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.landing-download-card {
|
||||||
|
min-width: 200px;
|
||||||
|
width: min(220px, 100%);
|
||||||
|
min-height: 168px;
|
||||||
|
padding: 24px 20px 20px;
|
||||||
|
border-radius: 18px;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: none;
|
||||||
|
display: inline-flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 16px;
|
||||||
|
transition: box-shadow 0.2s ease, transform 0.15s ease;
|
||||||
|
border: 1px solid rgba(61, 139, 253, 0.35);
|
||||||
|
}
|
||||||
|
|
||||||
|
.landing-download-card__icon {
|
||||||
|
width: 72px;
|
||||||
|
height: 72px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center;
|
||||||
|
background-size: contain;
|
||||||
|
opacity: 0.95;
|
||||||
|
}
|
||||||
|
|
||||||
|
.landing-download-card__icon--android {
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%233DDC84'%3E%3Cpath d='M17.6 9.48l1.84-3.18c.16-.31-.04-.68-.38-.74a.495.495 0 0 0-.55.27l-1.87 3.24a11.43 11.43 0 0 0-8.12 0L6.65 5.83a.495.495 0 0 0-.55-.27c-.34.06-.54.43-.38.74L7.56 9.48C4.01 11.27 1.46 14.79 1 19h22c-.46-4.21-3.01-7.73-6.54-9.52zM7 16.5c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm10 0c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1z'/%3E%3C/svg%3E");
|
||||||
|
}
|
||||||
|
|
||||||
|
.landing-download-card__icon--studio {
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23F4A460'%3E%3Cpath d='M6 3h12l1 3H5l1-3zm-1 5h14v11c0 1.1-.9 2-2 2H7c-1.1 0-2-.9-2-2V8zm4 2v2h2v-2H9zm4 0v2h2v-2h-2z'/%3E%3Cpath d='M8 19h8v1H8v-1z' fill='%23654321'/%3E%3C/svg%3E");
|
||||||
|
}
|
||||||
|
|
||||||
|
.landing-download-card__label {
|
||||||
|
line-height: 1.25;
|
||||||
|
letter-spacing: 0.01em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.landing-download-card:hover,
|
||||||
|
.landing-download-card:focus-visible {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 0 30px rgba(61, 139, 253, 0.45);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Legacy pill buttons (documentation page, resources) */
|
||||||
.landing-download-btn {
|
.landing-download-btn {
|
||||||
min-width: 200px;
|
min-width: 200px;
|
||||||
min-height: 56px;
|
min-height: 56px;
|
||||||
|
|||||||
73
index.php
73
index.php
@@ -39,9 +39,9 @@ function hub_h(string $s): string {
|
|||||||
<link rel="icon" type="image/x-icon" href="/app/androidcast_project/favicon.ico">
|
<link rel="icon" type="image/x-icon" href="/app/androidcast_project/favicon.ico">
|
||||||
<link rel="apple-touch-icon" href="/app/androidcast_project/apple-touch-icon.ico">
|
<link rel="apple-touch-icon" href="/app/androidcast_project/apple-touch-icon.ico">
|
||||||
<link rel="stylesheet" href="/app/androidcast_project/issues/assets/css/app.css">
|
<link rel="stylesheet" href="/app/androidcast_project/issues/assets/css/app.css">
|
||||||
<link rel="stylesheet" href="hub.css?v=20260610overlay">
|
<link rel="stylesheet" href="hub.css?v=20260711overlay">
|
||||||
<link rel="stylesheet" href="hub-logo-layers.css?v=20260610overlay">
|
<link rel="stylesheet" href="hub-logo-layers.css?v=20260711overlay">
|
||||||
<link rel="stylesheet" href="hub-landing.css?v=20260613layout">
|
<link rel="stylesheet" href="hub-landing.css?v=20260711layout">
|
||||||
<script src="/app/androidcast_project/issues/assets/js/nav_shell.js" defer></script>
|
<script src="/app/androidcast_project/issues/assets/js/nav_shell.js" defer></script>
|
||||||
<script src="assets/hub-i18n.js" defer></script>
|
<script src="assets/hub-i18n.js" defer></script>
|
||||||
<script src="assets/hub-landing.js" defer></script>
|
<script src="assets/hub-landing.js" defer></script>
|
||||||
@@ -139,55 +139,14 @@ function hub_h(string $s): string {
|
|||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div class="landing-frame">
|
<div class="landing-frame">
|
||||||
<nav class="nav-pane landing-left" id="landing-nav-pane" aria-label="Android Cast apps">
|
<?php platform_render_nav_shell([
|
||||||
<button type="button" class="nav-handle" id="landing-nav-handle" data-i18n-aria="nav.toggle" data-i18n-title="nav.toggle" aria-label="Navigation" title="Navigation">
|
'project_base' => $hubProjectBase,
|
||||||
<span class="nav-icon nav-icon--menu" aria-hidden="true"></span>
|
'issues_base' => $hubIssuesBase,
|
||||||
</button>
|
'active' => '',
|
||||||
<ul class="nav-list">
|
'username' => $hubUsername,
|
||||||
<li><a href="issues/?view=home" class="nav-link" data-i18n-title="nav.console" title="Console"><span class="nav-icon nav-icon--home"></span><span class="nav-text"><span class="nav-label" data-i18n="nav.console">Console</span><span class="nav-desc" data-i18n="nav.console_desc">Crash triage home and workspace cards.</span></span></a></li>
|
'pane_id' => 'landing-nav-pane',
|
||||||
<li><a href="git/" class="nav-link" data-i18n-title="nav.git" title="Git"><span class="nav-icon nav-icon--git"></span><span class="nav-text"><span class="nav-label" data-i18n="nav.git">Git</span><span class="nav-desc" data-i18n="nav.git_desc">Browse repos, branches, and tags.</span></span></a></li>
|
'handle_id' => 'landing-nav-handle',
|
||||||
<li><a href="issues/?view=reports" class="nav-link" data-i18n-title="nav.issues" title="Issues"><span class="nav-icon nav-icon--reports"></span><span class="nav-text"><span class="nav-label" data-i18n="nav.issues">Issues</span><span class="nav-desc" data-i18n="nav.issues_desc">Browse and triage crash reports.</span></span></a></li>
|
]); ?>
|
||||||
<li><a href="issues/?view=tickets" class="nav-link" data-i18n-title="nav.tickets" title="Tickets"><span class="nav-icon nav-icon--tickets"></span><span class="nav-text"><span class="nav-label" data-i18n="nav.tickets">Tickets</span><span class="nav-desc" data-i18n="nav.tickets_desc">Roadmap tasks, QA items, and tags.</span></span></a></li>
|
|
||||||
<li><a href="graphs/" class="nav-link" data-i18n-title="nav.graphs" title="Analytics"><span class="nav-icon nav-icon--graphs"></span><span class="nav-text"><span class="nav-label" data-i18n="nav.graphs">Analytics</span><span class="nav-desc" data-i18n="nav.graphs_desc">Sessions, live cast stats, and device activity.</span></span></a></li>
|
|
||||||
<li><a href="monitor/" class="nav-link" data-i18n-title="nav.monitor" title="Monitoring"><span class="nav-icon nav-icon--monitor"></span><span class="nav-text"><span class="nav-label" data-i18n="nav.monitor">Monitoring</span><span class="nav-desc" data-i18n="nav.monitor_desc">Grafana dashboards — cluster health, alerts, exporters.</span></span></a></li>
|
|
||||||
<li><a href="issues/?view=live_sessions" class="nav-link" data-i18n-title="nav.live_sessions" title="Live sessions"><span class="nav-icon nav-icon--live"></span><span class="nav-text"><span class="nav-label" data-i18n="nav.live_sessions">Live sessions</span><span class="nav-desc" data-i18n="nav.live_sessions_desc">Cast intents, join stats, session history.</span></span></a></li>
|
|
||||||
<li><a href="issues/live/education" class="nav-link" data-i18n-title="nav.education" title="Education demo"><span class="nav-icon nav-icon--education"></span><span class="nav-text"><span class="nav-label" data-i18n="nav.education">Education (demo)</span><span class="nav-desc" data-i18n="nav.education_desc">Browser screen-share trial (5 min).</span></span></a></li>
|
|
||||||
<li><a href="issues/?view=remote_access" class="nav-link" data-i18n-title="nav.remote" title="Remote access"><span class="nav-icon nav-icon--remote"></span><span class="nav-text"><span class="nav-label" data-i18n="nav.remote">Remote access</span><span class="nav-desc" data-i18n="nav.remote_desc">WireGuard sessions and device reachability.</span></span></a></li>
|
|
||||||
<li><a href="monitor/" class="nav-link" data-i18n-title="nav.monitor" title="Monitoring"><span class="nav-icon nav-icon--monitor"></span><span class="nav-text"><span class="nav-label" data-i18n="nav.monitor">Monitoring</span><span class="nav-desc" data-i18n="nav.monitor_desc">Grafana dashboards — cluster health, alerts, exporters.</span></span></a></li>
|
|
||||||
<li><a href="issues/?view=short_links" class="nav-link" data-i18n-title="nav.short_links" title="Short links"><span class="nav-icon nav-icon--link"></span><span class="nav-text"><span class="nav-label" data-i18n="nav.short_links">Short links</span><span class="nav-desc" data-i18n="nav.short_links_desc">Mint bearer tokens and shorten URLs for s.f0xx.org.</span></span></a></li>
|
|
||||||
<li><a href="build/" class="nav-link" data-i18n-title="nav.builder" title="Builder"><span class="nav-icon nav-icon--builder"></span><span class="nav-text"><span class="nav-label" data-i18n="nav.builder">Builder</span><span class="nav-desc" data-i18n="nav.builder_desc">Docker CI for APK baking and OTA artifacts.</span></span></a></li>
|
|
||||||
</ul>
|
|
||||||
<div class="nav-user">
|
|
||||||
<?php if ($hubUsername !== ''): ?>
|
|
||||||
<span class="nav-user-name" title="<?= hub_h($hubUsername) ?>">
|
|
||||||
<span class="nav-icon nav-icon--user" aria-hidden="true"></span>
|
|
||||||
<span class="nav-label"><?= hub_h($hubUsername) ?></span>
|
|
||||||
</span>
|
|
||||||
<a href="<?= hub_h($hubIssuesBase) ?>/account-security"
|
|
||||||
class="nav-link"
|
|
||||||
data-i18n-aria="nav.security" data-i18n-title="nav.security"
|
|
||||||
aria-label="Security" title="Security">
|
|
||||||
<span class="nav-icon nav-icon--user" aria-hidden="true"></span>
|
|
||||||
<span class="nav-label" data-i18n="nav.security">Security</span>
|
|
||||||
</a>
|
|
||||||
<a href="<?= hub_h($hubProjectBase) ?>/logout"
|
|
||||||
class="nav-link nav-link--logout"
|
|
||||||
data-i18n-aria="nav.logout" data-i18n-title="nav.logout"
|
|
||||||
aria-label="Logout" title="Logout">
|
|
||||||
<span class="nav-icon nav-icon--logout" aria-hidden="true"></span>
|
|
||||||
<span class="nav-label" data-i18n="nav.logout">Logout</span>
|
|
||||||
</a>
|
|
||||||
<?php else: ?>
|
|
||||||
<a href="<?= hub_h($hubProjectBase) ?>/login"
|
|
||||||
class="nav-link"
|
|
||||||
data-i18n-aria="nav.sign_in" data-i18n-title="nav.sign_in"
|
|
||||||
aria-label="Sign in" title="Sign in">
|
|
||||||
<span class="nav-icon nav-icon--user" aria-hidden="true"></span>
|
|
||||||
<span class="nav-label" data-i18n="nav.sign_in">Sign in</span>
|
|
||||||
</a>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<main class="landing-scroll" id="landing-scroll" tabindex="-1">
|
<main class="landing-scroll" id="landing-scroll" tabindex="-1">
|
||||||
<?php require __DIR__ . '/landing-pages.inc.php'; ?>
|
<?php require __DIR__ . '/landing-pages.inc.php'; ?>
|
||||||
@@ -208,16 +167,6 @@ function hub_h(string $s): string {
|
|||||||
'use_i18n' => false,
|
'use_i18n' => false,
|
||||||
'extra_class' => 'bottom-bar',
|
'extra_class' => 'bottom-bar',
|
||||||
]); ?>
|
]); ?>
|
||||||
<script>
|
|
||||||
(function () {
|
|
||||||
var footerLeft = document.querySelector('.platform-footer__left');
|
|
||||||
if (footerLeft) {
|
|
||||||
footerLeft.setAttribute('data-i18n', 'footer.copyright');
|
|
||||||
footerLeft.setAttribute('data-holder', 'Anton Afanaasyeu');
|
|
||||||
footerLeft.setAttribute('data-year', '2026 - current');
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
<script>
|
<script>
|
||||||
(function () {
|
(function () {
|
||||||
var sel = document.getElementById('theme-select');
|
var sel = document.getElementById('theme-select');
|
||||||
|
|||||||
@@ -32,10 +32,10 @@
|
|||||||
<div class="landing-page-inner card card--lift">
|
<div class="landing-page-inner card card--lift">
|
||||||
<h2 data-i18n="page3.title">About</h2>
|
<h2 data-i18n="page3.title">About</h2>
|
||||||
<p class="landing-lead" data-i18n="page3.lead">Android Cast connects devices, backends, and operators across your stack.</p>
|
<p class="landing-lead" data-i18n="page3.lead">Android Cast connects devices, backends, and operators across your stack.</p>
|
||||||
<div class="landing-gallery">
|
<div class="landing-gallery" aria-hidden="true">
|
||||||
<img src="https://images.unsplash.com/photo-1451187580459-43490279c0fa?w=400&h=300&fit=crop" alt="" loading="lazy" decoding="async">
|
<span class="landing-gallery__tile landing-gallery__tile--network"></span>
|
||||||
<img src="https://images.unsplash.com/photo-1526374965328-7f61d4dc18c5?w=400&h=300&fit=crop" alt="" loading="lazy" decoding="async">
|
<span class="landing-gallery__tile landing-gallery__tile--cluster"></span>
|
||||||
<img src="https://images.unsplash.com/photo-1558494949-ef010cbdcc31?w=400&h=300&fit=crop" alt="" loading="lazy" decoding="async">
|
<span class="landing-gallery__tile landing-gallery__tile--ops"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -80,12 +80,18 @@
|
|||||||
<section class="landing-page" id="landing-page-7" data-page="7" aria-label="Downloads">
|
<section class="landing-page" id="landing-page-7" data-page="7" aria-label="Downloads">
|
||||||
<div class="landing-page-inner card card--lift">
|
<div class="landing-page-inner card card--lift">
|
||||||
<h2 data-i18n="page7.title">Downloads</h2>
|
<h2 data-i18n="page7.title">Downloads</h2>
|
||||||
<p class="landing-lead" data-i18n="page7.lead">Latest stable builds from the orchestration pipeline (<code>next</code> integration).</p>
|
<p class="landing-lead" data-i18n="page7.lead">Latest builds from the orchestration pipeline (<code>staging</code> channel, branch <code>next</code>).</p>
|
||||||
<div class="landing-download-grid">
|
<div class="landing-download-grid">
|
||||||
<button type="button" class="landing-download-btn btn btn-primary" id="hub-download-apk" data-i18n="page7.btn_apk">Android APK</button>
|
<button type="button" class="landing-download-card btn btn-primary" id="hub-download-apk">
|
||||||
<a class="landing-download-btn btn btn-primary" href="/v0/downloads/ac-session-studio.jar" download data-i18n="page7.btn_studio">Session Studio</a>
|
<span class="landing-download-card__icon landing-download-card__icon--android" aria-hidden="true"></span>
|
||||||
|
<span class="landing-download-card__label" data-i18n="page7.btn_apk">Android APK</span>
|
||||||
|
</button>
|
||||||
|
<a class="landing-download-card btn btn-primary" href="/v0/downloads/ac-session-studio.jar" download>
|
||||||
|
<span class="landing-download-card__icon landing-download-card__icon--studio" aria-hidden="true"></span>
|
||||||
|
<span class="landing-download-card__label" data-i18n="page7.btn_studio">Session Studio</span>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<p class="muted landing-download-hint" id="hub-download-apk-status" data-i18n="page7.hint">APK matches the stable OTA channel signature and version.</p>
|
<p class="muted landing-download-hint" id="hub-download-apk-status" data-i18n="page7.hint">APK is the installable package from the staging OTA channel (falls back to stable).</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@@ -104,9 +110,9 @@
|
|||||||
<div class="landing-page-inner card card--lift">
|
<div class="landing-page-inner card card--lift">
|
||||||
<h2 data-i18n="page9.title">People</h2>
|
<h2 data-i18n="page9.title">People</h2>
|
||||||
<p class="landing-lead" data-i18n="page9.lead">Engineers and operators shipping cast, crash, and build workflows every day.</p>
|
<p class="landing-lead" data-i18n="page9.lead">Engineers and operators shipping cast, crash, and build workflows every day.</p>
|
||||||
<div class="landing-gallery">
|
<div class="landing-gallery" aria-hidden="true">
|
||||||
<img src="https://images.unsplash.com/photo-1522071820081-009f0129c71c?w=400&h=300&fit=crop" alt="" loading="lazy" decoding="async">
|
<span class="landing-gallery__tile landing-gallery__tile--team"></span>
|
||||||
<img src="https://images.unsplash.com/photo-1600880292203-757bb62b4baf?w=400&h=300&fit=crop" alt="" loading="lazy" decoding="async">
|
<span class="landing-gallery__tile landing-gallery__tile--collab"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
Reference in New Issue
Block a user