Merge pull request 'Reload-Countdown, CHANGELOG (#108) und Aufnahmeseite: "gespeichert" statt "verworfen"' (#111) from fds2610-bot/monitor:main into main

Reviewed-on: https://codeberg.org/fds2610/monitor/pulls/111
This commit is contained in:
fds2610 2026-10-01 13:45:59 +02:00
commit 90897c956c
7 changed files with 48 additions and 16 deletions

View file

@ -30,9 +30,11 @@ CODEBERG_TOKEN=
# Mit eingerichtetem Webhook (siehe unten) ist das nur noch das Sicherheitsnetz fuer # Mit eingerichtetem Webhook (siehe unten) ist das nur noch das Sicherheitsnetz fuer
# verpasste Zustellungen und darf gross bleiben - Issue-Aenderungen landen dann in Sekunden # verpasste Zustellungen und darf gross bleiben - Issue-Aenderungen landen dann in Sekunden
# auf dem Board, nicht erst beim naechsten Lauf. # auf dem Board, nicht erst beim naechsten Lauf.
# Speist ausserdem den rein kosmetischen Reload-Countdown im Header der Buero-Visualisierung # Speist ausserdem den Reload-Countdown im Header der Buero-Visualisierung (Service "web",
# (Service "web", siehe docker/nginx/default.conf.template) - der Countdown zaehlt nur mit, # siehe docker/nginx/default.conf.template) - erreicht der Countdown 0, laedt der Browser die
# er loest selbst keinen echten Reload/Sync aus. # Seite tatsaechlich neu (Issue #108), damit ein dauerhaft offener Wand-Tab laengstens nach
# einem Intervall den aktuell deployten Frontend-Stand bekommt. Auf den periodischen
# Codeberg-Sync hat das keinen Einfluss, dafuer ist weiterhin allein das Backend zustaendig.
MONITOR_SYNC_INTERVAL=600 MONITOR_SYNC_INTERVAL=600
# Secret des Codeberg-Webhooks (Repo -> Einstellungen -> Webhooks -> Forgejo, Ziel-URL # Secret des Codeberg-Webhooks (Repo -> Einstellungen -> Webhooks -> Forgejo, Ziel-URL

10
CHANGELOG.md Normal file
View file

@ -0,0 +1,10 @@
# Changelog
Format in Anlehnung an [Keep a Changelog](https://keepachangelog.com/de/1.0.0/).
## Unreleased
- Reload-Countdown im Header der Buero-Visualisierung laedt die Seite bei 0 tatsaechlich neu,
statt nur kosmetisch herunterzuzaehlen - ein dauerhaft offener Wand-Tab bekommt dadurch
laengstens nach einem `MONITOR_SYNC_INTERVAL` den aktuell deployten Frontend-Stand
(#108).

View file

@ -20,7 +20,11 @@ die dazugehörigen Klärungen in [Detailfragen.md](Detailfragen.md) und
Agenten laufen animiert zwischen Schreibtisch/Board/Monitor-Wand/Kaffee-Ecke (Phase 3-5, Agenten laufen animiert zwischen Schreibtisch/Board/Monitor-Wand/Kaffee-Ecke (Phase 3-5,
siehe Visualization.md Abschnitt 9). Ist die API nicht erreichbar, zeigt die Szene siehe Visualization.md Abschnitt 9). Ist die API nicht erreichbar, zeigt die Szene
ersatzweise Demo-Daten — kenntlich gemacht durch einen roten Banner „API nicht erreichbar!". ersatzweise Demo-Daten — kenntlich gemacht durch einen roten Banner „API nicht erreichbar!".
Die Basis-URL der API kommt aus `MONITOR_API_BASE` (siehe `.env.example`). Die Basis-URL der API kommt aus `MONITOR_API_BASE` (siehe `.env.example`). Ein dauerhaft
offener Tab (z.B. auf einer Monitor-Wand) lädt sich spätestens alle `MONITOR_SYNC_INTERVAL`
Sekunden selbst neu (Reload-Countdown im Header, Issue #108), damit er immer den aktuell
deployten Frontend-Stand zeigt statt auf unbestimmte Zeit eine alte Version weiterlaufen zu
lassen.
- Klick auf den PO öffnet ein Modal, in dem sich die vom Codeberg-Sync gelesenen Projekte - Klick auf den PO öffnet ein Modal, in dem sich die vom Codeberg-Sync gelesenen Projekte
(Owner/Repo + Farbe) hinzufügen, bearbeiten und löschen lassen (Issue #27) — Änderungen (Owner/Repo + Farbe) hinzufügen, bearbeiten und löschen lassen (Issue #27) — Änderungen
wirken sofort im laufenden `sync`-Dienst, ohne Neustart. Schreibende Aktionen fragen einmalig wirken sofort im laufenden `sync`-Dienst, ohne Neustart. Schreibende Aktionen fragen einmalig

View file

@ -18,8 +18,9 @@ server {
# definiert, bleibt ihr Platzhalter stehen und nginx verweigert den Start # definiert, bleibt ihr Platzhalter stehen und nginx verweigert den Start
# ("unknown \"monitor_api_base\" variable") - besser ein lauter Fehler als eine still # ("unknown \"monitor_api_base\" variable") - besser ein lauter Fehler als eine still
# falsche URL. docker-compose.yml setzt beide Variablen daher immer (ggf. mit Default). # falsche URL. docker-compose.yml setzt beide Variablen daher immer (ggf. mit Default).
# MONITOR_SYNC_INTERVAL speist nur den rein kosmetischen Reload-Countdown im Header # MONITOR_SYNC_INTERVAL speist den Reload-Countdown im Header (Issue #12) - der laedt die
# (Issue #12) - hat keinen Einfluss auf den tatsaechlichen Update-Takt der Szene. # Seite bei 0 tatsaechlich per location.reload() neu (Issue #108), hat aber keinen Einfluss
# auf den laufenden Update-Takt der Szene selbst (SSE/Polling, siehe office-render.js).
location = /config.js { location = /config.js {
default_type application/javascript; default_type application/javascript;
add_header Cache-Control "no-store"; add_header Cache-Control "no-store";

View file

@ -349,6 +349,7 @@ function renderPullRequestStripePattern() {
width: 10, width: 10,
height: 10, height: 10,
patternUnits: "userSpaceOnUse", patternUnits: "userSpaceOnUse",
patternTransform: "rotate(45)",
}); });
pattern.appendChild(el("rect", { x: 0, y: 0, width: 10, height: 3, fill: "#000000" })); pattern.appendChild(el("rect", { x: 0, y: 0, width: 10, height: 3, fill: "#000000" }));
defs.appendChild(pattern); defs.appendChild(pattern);
@ -2537,9 +2538,12 @@ function setAgentProblemBanner(agents) {
banner.classList.toggle("api-banner-warning", !expired && Boolean(expiring)); banner.classList.toggle("api-banner-warning", !expired && Boolean(expiring));
} }
// Rein kosmetischer Countdown im Header (Issue #12): zaehlt unabhaengig vom tatsaechlichen // Countdown im Header (Issue #12, seit Issue #108 mit echtem Reload): zaehlt unabhaengig vom
// SSE-/Polling-Takt von MONITOR_SYNC_INTERVAL (aus config.js, siehe office.html) auf 0 // tatsaechlichen SSE-/Polling-Takt von MONITOR_SYNC_INTERVAL (aus config.js, siehe office.html)
// herunter und startet danach von vorn - loest selbst keinen Reload aus. // auf 0 herunter und laedt die Seite dann per location.reload() neu, statt nur wieder von vorn
// zu zaehlen - ein dauerhaft offener Wand-Tab holt sich damit laengstens nach einem Intervall
// den aktuell deployten Frontend-Stand (sonst zeigt er auf unbestimmte Zeit die alte
// JS-Version weiter an, selbst wenn ein Fix laengst gemergt/deployt ist).
function startReloadCountdown() { function startReloadCountdown() {
const countdownEl = document.getElementById("reload-countdown"); const countdownEl = document.getElementById("reload-countdown");
if (!countdownEl) { if (!countdownEl) {
@ -2551,7 +2555,12 @@ function startReloadCountdown() {
countdownEl.textContent = `${remaining}s`; countdownEl.textContent = `${remaining}s`;
window.setInterval(() => { window.setInterval(() => {
remaining = nextCountdownSeconds(remaining, intervalSeconds); const next = nextCountdownSeconds(remaining, intervalSeconds);
if (next === intervalSeconds) {
window.location.reload();
return;
}
remaining = next;
countdownEl.textContent = `${remaining}s`; countdownEl.textContent = `${remaining}s`;
}, 1000); }, 1000);
} }

View file

@ -1045,9 +1045,11 @@ export function advancePoVisitStage(stage) {
} }
// --------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------
// Reload-Countdown im Header (Issue #12): rein kosmetischer Sekundenzaehler, der die // Reload-Countdown im Header (Issue #12, seit Issue #108 mit echtem Reload): Sekundenzaehler,
// Sync-Frequenz des Backends (MONITOR_SYNC_INTERVAL, siehe .env.example) im Header // der die Sync-Frequenz des Backends (MONITOR_SYNC_INTERVAL, siehe .env.example) im Header
// widerspiegelt - ohne selbst irgendetwas neu zu laden (siehe office-render.js). // widerspiegelt - erreicht er 0, laedt office-render.js die Seite tatsaechlich neu, damit ein
// dauerhaft offener Wand-Tab laengstens nach einem Intervall den aktuell deployten
// Frontend-Stand bekommt.
// --------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------
export const DEFAULT_RELOAD_INTERVAL_SECONDS = 600; export const DEFAULT_RELOAD_INTERVAL_SECONDS = 600;

View file

@ -113,12 +113,16 @@ function stopRecording() {
startButton.hidden = false; startButton.hidden = false;
} }
function discardRecording() { function resetRecording() {
recordedBlob = null; recordedBlob = null;
audioPreview.removeAttribute("src"); audioPreview.removeAttribute("src");
audioPreview.hidden = true; audioPreview.hidden = true;
uploadButton.hidden = true; uploadButton.hidden = true;
discardButton.hidden = true; discardButton.hidden = true;
}
function discardRecording() {
resetRecording();
setStatus("Aufnahme verworfen."); setStatus("Aufnahme verworfen.");
} }
@ -133,8 +137,8 @@ async function upload() {
const extension = (recordedBlob.type.split("/")[1] || "webm").split(";")[0]; const extension = (recordedBlob.type.split("/")[1] || "webm").split(";")[0];
try { try {
await uploadRecording(recordedBlob, `aufnahme.${extension}`); await uploadRecording(recordedBlob, `aufnahme.${extension}`);
setStatus("Hochgeladen, danke!"); resetRecording();
discardRecording(); setStatus("Aufnahme gespeichert, danke!");
} catch (error) { } catch (error) {
setStatus(error.message, true); setStatus(error.message, true);
} finally { } finally {