1
0
mirror of git://f0xx.org/android_cast synced 2026-07-29 03:38:52 +03:00
This commit is contained in:
Anton Afanasyeu
2026-05-27 16:44:50 +02:00
parent 92fc65e02e
commit 43f802de5d
19 changed files with 957 additions and 0 deletions

View File

@@ -169,6 +169,157 @@
z-index: 1;
}
.hub-compass-hit {
position: fixed;
left: calc(50% + 252px);
top: calc(50% + 226px);
width: 110px;
height: 110px;
z-index: 2;
border-radius: 50%;
border: 2px solid rgba(219, 229, 241, 0.45);
background:
radial-gradient(circle at 35% 35%, rgba(231, 236, 243, 0.35), rgba(10, 14, 20, 0.22) 68%, rgba(10, 14, 20, 0.1) 100%);
box-shadow:
-6px -8px 8px rgba(160, 215, 255, 0.12),
8px 10px 14px rgba(9, 13, 19, 0.24);
cursor: pointer;
transform: translate(-50%, -50%) scale(1);
transition: transform 0.22s ease, border-width 0.22s ease, box-shadow 0.22s ease;
}
.hub-compass-hit::before,
.hub-compass-hit::after {
content: "";
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
border-radius: 1px;
background: rgba(231, 236, 243, 0.82);
transition: width 0.22s ease, height 0.22s ease;
}
.hub-compass-hit::before {
width: 3px;
height: 46px;
}
.hub-compass-hit::after {
width: 46px;
height: 3px;
}
.hub-compass-hit:hover,
.hub-compass-hit:focus-visible {
transform: translate(-50%, -50%) scale(1.25);
border-width: 2.3px;
box-shadow:
-8px -10px 10px rgba(180, 228, 255, 0.22),
14px 16px 22px rgba(7, 10, 16, 0.34),
22px 26px 34px rgba(6, 8, 14, 0.42);
outline: none;
}
.hub-compass-hit:hover::before,
.hub-compass-hit:hover::after,
.hub-compass-hit:focus-visible::before,
.hub-compass-hit:focus-visible::after {
width: 52px;
height: 3.45px;
}
.hub-compass-hit:hover::before,
.hub-compass-hit:focus-visible::before {
width: 3.45px;
height: 52px;
}
.hub-docs-modal {
position: fixed;
inset: 0;
z-index: 20;
}
.hub-docs-backdrop {
position: absolute;
inset: 0;
background: rgba(4, 8, 14, 0.66);
}
.hub-docs-panel {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: min(980px, calc(100vw - 40px));
max-height: calc(100vh - 56px);
display: flex;
flex-direction: column;
}
.hub-docs-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 14px 16px 10px;
border-bottom: 1px solid var(--border);
}
.hub-docs-header h2 {
margin: 0;
font-size: 1.15rem;
font-weight: 700;
}
.hub-docs-close {
border: 1px solid var(--border);
background: var(--bg2);
color: var(--text);
border-radius: 8px;
width: 32px;
height: 32px;
cursor: pointer;
}
.hub-docs-body {
overflow: auto;
padding: 16px;
font-size: 14px;
line-height: 1.5;
}
.hub-docs-body h3 {
margin: 16px 0 8px;
font-size: 1.03rem;
}
.hub-docs-body h4 {
margin: 12px 0 6px;
font-size: 0.96rem;
}
.hub-docs-body table {
width: 100%;
border-collapse: collapse;
margin: 10px 0 14px;
}
.hub-docs-body th,
.hub-docs-body td {
border: 1px solid var(--border);
padding: 8px;
text-align: left;
vertical-align: top;
}
.hub-docs-body a[data-doc-link] {
color: var(--accent);
text-decoration: underline;
cursor: pointer;
}
@media (max-width: 860px) {
.hub-cards {
grid-template-columns: 1fr;

View File

@@ -16,6 +16,19 @@
</head>
<body class="hub-page">
<div id="hub-logo-stage" class="hub-logo-stage" hidden aria-hidden="true"></div>
<button id="hub-compass-hit" class="hub-compass-hit" type="button" aria-label="Open deployment model"></button>
<div id="hub-docs-modal" class="hub-docs-modal" hidden aria-hidden="true">
<div class="hub-docs-backdrop" data-close-docs></div>
<section class="hub-docs-panel card card--lift" role="dialog" aria-modal="true" aria-labelledby="hub-docs-title">
<header class="hub-docs-header">
<h2 id="hub-docs-title">Deployment model</h2>
<button type="button" class="hub-docs-close" data-close-docs aria-label="Close">x</button>
</header>
<div id="hub-docs-body" class="hub-docs-body">
<p class="muted">Loading...</p>
</div>
</section>
</div>
<div class="shell hub-shell">
<main class="main-pane hub-main">
<header class="hub-header">
@@ -120,6 +133,58 @@
stage.hidden = false;
stage.setAttribute('aria-hidden', 'true');
var docsModal = document.getElementById('hub-docs-modal');
var docsBody = document.getElementById('hub-docs-body');
var compassHit = document.getElementById('hub-compass-hit');
if (!docsModal || !docsBody || !compassHit) return;
function closeDocs() {
docsModal.hidden = true;
docsModal.setAttribute('aria-hidden', 'true');
}
function openDocs() {
docsModal.hidden = false;
docsModal.setAttribute('aria-hidden', 'false');
}
function loadDocs(section) {
var url = '/app/androidcast_project/crashes/api/hub_deploy_docs.php';
if (section) url += '?section=' + encodeURIComponent(section);
docsBody.innerHTML = '<p class="muted">Loading...</p>';
openDocs();
fetch(url, { headers: { 'Accept': 'application/json' } })
.then(function (res) { return res.json(); })
.then(function (payload) {
if (!payload || !payload.ok) {
docsBody.innerHTML = '<p class="muted">Failed to load deployment model.</p>';
return;
}
docsBody.innerHTML = payload.html || '<p class="muted">No content.</p>';
})
.catch(function () {
docsBody.innerHTML = '<p class="muted">Network error while loading deployment model.</p>';
});
}
compassHit.addEventListener('click', function () { loadDocs('intro'); });
docsModal.addEventListener('click', function (ev) {
var target = ev.target;
if (!(target instanceof Element)) return;
if (target.hasAttribute('data-close-docs')) {
closeDocs();
return;
}
var link = target.closest('[data-doc-link]');
if (link) {
ev.preventDefault();
loadDocs(link.getAttribute('data-doc-link') || 'intro');
}
});
document.addEventListener('keydown', function (ev) {
if (ev.key === 'Escape' && !docsModal.hidden) closeDocs();
});
})();
</script>
</body>

View File

@@ -0,0 +1,96 @@
<?php
declare(strict_types=1);
require_once __DIR__ . '/../../src/bootstrap.php';
header('Content-Type: application/json; charset=utf-8');
$section = strtolower(trim((string)($_GET['section'] ?? 'intro')));
if ($section === '') $section = 'intro';
function hub_docs_nav(): string {
return '<nav><strong>(2) TABLE OF CONTENTS</strong><ul>'
. '<li><a data-doc-link="intro" href="?section=intro">(1) INTRODUCTION</a></li>'
. '<li><a data-doc-link="deploy" href="?section=deploy">(3) LOCAL SINGLE-STEP TEST DEPLOYMENT</a></li>'
. '<li><a data-doc-link="orchestration" href="?section=orchestration">(4) ORCHESTRATION MODEL</a></li>'
. '<li><a data-doc-link="routing" href="?section=routing">(5) XHR SUPERVISED VIEW MODEL</a></li>'
. '<li><a data-doc-link="ops" href="?section=ops">(6) OPERATIONS CHECKLIST</a></li>'
. '</ul></nav>';
}
function hub_docs_intro(): string {
return '<h3>(1) INTRODUCTION</h3>'
. '<h4>(1.1) authors</h4>'
. '<p>Project owner: Anton Afanaasyeu. Implementation assistant: Cursor coding agent.</p>'
. '<h4>(1.2) revision details</h4>'
. '<p>Revision scope: landing compass interaction, XHR-driven deployment docs view, Docker orchestration model integration for next branch.</p>'
. hub_docs_nav();
}
function hub_docs_deploy(): string {
return '<h3>(3) LOCAL SINGLE-STEP TEST DEPLOYMENT</h3>'
. '<h4>(3.1) preparations, environment</h4>'
. '<p>Prerequisites: docker + docker compose plugin, git checkout of android cast on branch <code>next</code>.</p>'
. '<p>Run one command from repo: <code>cd orchestration && ./deploy.sh</code></p>'
. '<h4>(3.2) package prerequisites by distro</h4>'
. '<table><thead><tr><th>Distro</th><th>Install steps</th></tr></thead><tbody>'
. '<tr><td>Alpine</td><td><code>apk add docker docker-cli-compose bash git curl</code><br><code>rc-update add docker default && service docker start</code></td></tr>'
. '<tr><td>Debian / Ubuntu</td><td><code>apt update && apt install -y docker.io docker-compose-plugin git curl</code><br><code>systemctl enable --now docker</code></td></tr>'
. '<tr><td>Fedora</td><td><code>dnf install -y docker docker-compose-plugin git curl</code><br><code>systemctl enable --now docker</code></td></tr>'
. '<tr><td>Arch</td><td><code>pacman -S --noconfirm docker docker-compose git curl</code><br><code>systemctl enable --now docker</code></td></tr>'
. '</tbody></table>'
. '<h4>(3.3) local checks</h4>'
. '<ul><li><code>http://localhost:8080/app/androidcast_project/</code> (hub)</li>'
. '<li><code>http://localhost:8080/app/androidcast_project/crashes/</code> (crashes/tasks)</li>'
. '<li><code>http://localhost:8080/app/androidcast_project/git/</code> (redirect to gitea FE)</li></ul>'
. hub_docs_nav();
}
function hub_docs_orchestration(): string {
return '<h3>(4) ORCHESTRATION MODEL</h3>'
. '<p>All infra runs in docker with shared source bind-mount where possible.</p>'
. '<ul>'
. '<li>landing FE (nginx): serves hub and routes subpaths</li>'
. '<li>gitea FE (nginx) + gitea app</li>'
. '<li>crashes FE (nginx) + php-fpm + mariadb</li>'
. '<li>single command create/update: <code>orchestration/deploy.sh</code></li>'
. '</ul>'
. '<p>Base images: nginx, php-fpm, mariadb. This stays minimal and reproducible on laptop devices.</p>'
. hub_docs_nav();
}
function hub_docs_routing(): string {
return '<h3>(5) XHR SUPERVISED VIEW MODEL</h3>'
. '<p>The Compass click opens a modal. Content is requested via XMLHttpRequest/fetch from BE endpoint '
. '<code>/app/androidcast_project/crashes/api/hub_deploy_docs.php</code>.</p>'
. '<p>Internal links in this modal use <code>data-doc-link</code> and are intercepted on frontend, then re-requested through BE. '
. 'Browser URL bar is not changed.</p>'
. '<p>Static assets remain at original paths and are not rewritten in this view.</p>'
. hub_docs_nav();
}
function hub_docs_ops(): string {
return '<h3>(6) OPERATIONS CHECKLIST</h3>'
. '<ol>'
. '<li>Pull latest <code>next</code></li>'
. '<li>Run <code>orchestration/deploy.sh</code></li>'
. '<li>Verify hub/crashes/gitea endpoints</li>'
. '<li>For app changes: edit source in repo, rerun deploy script</li>'
. '</ol>'
. hub_docs_nav();
}
$map = [
'intro' => 'hub_docs_intro',
'deploy' => 'hub_docs_deploy',
'orchestration' => 'hub_docs_orchestration',
'routing' => 'hub_docs_routing',
'ops' => 'hub_docs_ops',
];
if (!isset($map[$section])) $section = 'intro';
echo safe_json_encode([
'ok' => true,
'section' => $section,
'html' => $map[$section](),
]);

View File

@@ -85,6 +85,11 @@ if ($route === '/api/ticket_attachment.php' || str_ends_with($route, '/api/ticke
exit;
}
if ($route === '/api/hub_deploy_docs.php' || str_ends_with($route, '/api/hub_deploy_docs.php')) {
require __DIR__ . '/api/hub_deploy_docs.php';
exit;
}
if ($route === '/logout') {
Auth::logout();
header('Location: ' . $base . '/login');