feat: add realtime Gitea event stream widget
This commit is contained in:
1 parent
0fc4b2684e
commit
9df3815417
4 files changed
+112
-1
No files matched your search
@@ -52,6 +52,8 @@ textarea { resize: vertical; min-height: 120px; }
|
||||
.dot { width: 8px; height: 8px; border-radius: 50%; background: #22c55e; box-shadow: 0 0 8px #22c55e; }
|
||||
.widget { border: 1px solid #1b2d45; border-radius: 12px; padding: 10px; background: linear-gradient(180deg,#0f1d33,#0b1526); }
|
||||
.widget h3 { margin: 4px 0 8px; font-size: 13px; color: #7aa1c9; }
|
||||
.event { padding: 8px 0; border-bottom: 1px solid #1b2d45; }
|
||||
.event:last-child { border-bottom: 0; }
|
||||
.obi { width:14px; height:14px; background: url('data:image/svg+xml;utf8,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22><rect width=%2224%22 height=%2224%22 rx=%226%22 fill=%22%230b1526%22/><circle cx=%2212%22 cy=%2212%22 r=%226%22 fill=%22%2360a5fa%22/></svg>') center/contain no-repeat; display:inline-block; }
|
||||
.footer { padding: 12px; text-align: center; color:#4e6b8a; font-size:12px; }
|
||||
@keyframes fadein { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } }
|
||||
@@ -97,6 +99,10 @@ textarea { resize: vertical; min-height: 120px; }
|
||||
<summary><h2>Pull Requests</h2></summary>
|
||||
<div id="prs-content" class="muted">Loading…</div>
|
||||
</details>
|
||||
<details class="panel" data-panel-key="gitea-events" open>
|
||||
<summary><h2>Gitea event stream</h2></summary>
|
||||
<div id="gitea-events" class="muted" aria-live="polite">Loading…</div>
|
||||
</details>
|
||||
<details class="panel" data-panel-key="widgets" open>
|
||||
<summary><h2>Live Widgets</h2></summary>
|
||||
<div id="widgets" class="stack"></div>
|
||||
@@ -229,6 +235,26 @@ textarea { resize: vertical; min-height: 120px; }
|
||||
el.innerHTML = deltas.length ? deltas.map(d => '<div class="suggestion ' + d.priority + '"><span class="pill">' + escapeHtml(d.priority) + '</span> <strong>' + escapeHtml(d.action) + '</strong> ' + escapeHtml(d.target || '') + '<div class="muted">' + escapeHtml(d.panel) + '</div></div>').join('') : '<div class="muted">No suggestions yet.</div>';
|
||||
}
|
||||
|
||||
function paintEventStream(events) {
|
||||
const el = qs('#gitea-events');
|
||||
el.innerHTML = events.length ? events.slice(0, 12).map(event =>
|
||||
'<div class="event"><strong>' + escapeHtml(event.actor?.login || 'Gitea') + '</strong> ' +
|
||||
escapeHtml(event.type || 'activity') +
|
||||
'<div class="small">' + escapeHtml(event.repo?.full_name || '') +
|
||||
(event.created_at ? ' · ' + escapeHtml(fmt(event.created_at)) : '') + '</div></div>'
|
||||
).join('') : '<div class="muted">No recent Gitea activity.</div>';
|
||||
}
|
||||
|
||||
async function loadEventStream() {
|
||||
try {
|
||||
const res = await fetch('/api/v1/events', { headers: { Accept: 'application/json' } });
|
||||
if (!res.ok) throw new Error('HTTP ' + res.status);
|
||||
paintEventStream(await res.json());
|
||||
} catch (e) {
|
||||
qs('#gitea-events').innerHTML = '<div class="muted">Event stream unavailable.</div>';
|
||||
}
|
||||
}
|
||||
|
||||
async function tickWidgets() {
|
||||
try {
|
||||
const res = await fetch('/api/v1/context', { headers: { Accept: 'application/json' } });
|
||||
@@ -318,7 +344,9 @@ textarea { resize: vertical; min-height: 120px; }
|
||||
|
||||
qs('#refresh').addEventListener('click', load);
|
||||
load();
|
||||
loadEventStream();
|
||||
setInterval(load, 8000);
|
||||
setInterval(loadEventStream, 5000);
|
||||
setInterval(tickWidgets, 2000);
|
||||
tickWidgets();
|
||||
|
||||
|
||||
Reference in new issue
Block a user