Files
dogfather-universe/webdesign/diagnose.html
T
DogFatherGitandClaude Opus 5 b9f6829a63 Diagnoseseite: Text landete im Statuskreis statt daneben
Filipes Screenshot zeigte den Beschreibungstext als schmale
Buchstabensaeule ueber die halbe Seite laufen. Die Ursache ist eindeutig
und mein Fehler:

  d.innerHTML = '<span class="mark">✓</span><div><b></b><span></span></div>';
  d.querySelector("span").textContent = text;

querySelector liefert den ERSTEN passenden span -- und das war der
Statuskreis, nicht das Textfeld darunter. Der gesamte Beschreibungstext
wurde also in einen 26 Pixel breiten Kreis geschrieben und lief dort
heraus.

Zwei Regeln machten es schlimmer:
  .zeile span { ... }   traf ebenfalls BEIDE spans
  fehlendes min-width:0 verhinderte, dass die Textspalte schrumpfen darf

Jetzt wird die Zeile Element fuer Element aufgebaut, mit direkten
Verweisen statt Suche -- da gibt es nichts zu verwechseln. Die
CSS-Regeln zielen auf eigene Klassen (.inhalt, .text) statt auf den
Elementnamen, der Kreis ist auf feste 26px genagelt und schneidet
ueberzaehligen Inhalt ab, statt die Seite aufzubrechen.

Auch die feste Platzhalterzeile im HTML nutzte noch den alten Aufbau und
haette denselben Fehler gezeigt, sobald sie sichtbar wird.

GEPRUEFT im Browser: alle sechs Zeilen, jeder Kreis exakt 26x26, Text
danebenstehend. Zusaetzlich als Screenshot angesehen -- bei einem
Anzeigefehler ist Messen allein nicht genug, denn genau das hatte die
erste Fassung ja auch bestanden.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-23 17:13:34 +02:00

271 lines
11 KiB
HTML

<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Diagnose — Dogfather Webdesign</title>
<meta name="robots" content="noindex, nofollow" />
<meta name="theme-color" content="#05070b" />
<style>
/* ---------------------------------------------------------------------
BEWUSST OHNE EXTERNE DATEIEN.
Diese Seite muss auch dann funktionieren, wenn genau das kaputt ist,
was sie untersuchen soll: veraltete CSS- und JS-Dateien aus dem
Zwischenspeicher. Würde sie webdesign.css oder wd-core.js laden,
könnte sie am selben Problem scheitern und wäre nutzlos.
Deshalb steht hier alles inline und es gibt keinen Service Worker.
--------------------------------------------------------------------- */
:root { color-scheme: dark; }
* { box-sizing: border-box; }
body {
margin: 0; padding: 1.5rem 1rem 3rem;
background: #05070b; color: #eef4fb;
font: 16px/1.6 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}
.hu { max-width: 720px; margin: 0 auto; }
h1 { font-size: 1.5rem; margin: 0 0 .3rem; }
p.lead { color: #8f9fb4; margin: 0 0 1.6rem; }
h2 { font-size: 1.05rem; margin: 1.8rem 0 .6rem; color: #7fd0e8; }
.zeile {
display: flex; gap: .8rem; align-items: flex-start;
padding: .8rem .9rem; margin-bottom: .5rem;
border: 1px solid rgba(127,208,232,.16); border-radius: 10px;
background: rgba(16,26,41,.5);
}
/* min-width:0 ist Pflicht, kein Feinschliff: Ohne das weigert sich ein
Flex-Kind, unter seine Inhaltsbreite zu schrumpfen, und langer Text
sprengt die Karte seitlich. */
.zeile .inhalt { min-width: 0; flex: 1 1 auto; }
.zeile b { display: block; margin-bottom: .1rem; }
/* Nur der Beschreibungstext, NICHT jeder span in der Zeile -- sonst
traefe die Regel auch den Statuskreis. */
.zeile .text { color: #8f9fb4; font-size: .9rem; overflow-wrap: anywhere; }
.mark {
flex: 0 0 26px; width: 26px; height: 26px; border-radius: 50%;
display: grid; place-items: center;
font-size: .95rem; line-height: 1;
/* Der Kreis enthaelt genau ein Zeichen. Sollte je mehr hineingeraten,
bricht es nicht die Seite auf, sondern wird abgeschnitten. */
overflow: hidden;
}
.gut { background: rgba(111,214,163,.15); color: #6fd6a3; }
.warn { background: rgba(232,200,119,.18); color: #e8c877; }
.bad { background: rgba(255,157,157,.15); color: #ff9d9d; }
.wart { background: rgba(143,159,180,.15); color: #8f9fb4; }
button {
font: inherit; font-weight: 700; cursor: pointer;
padding: .85rem 1.3rem; border-radius: 10px; border: 0;
background: linear-gradient(135deg, #a9e2f2, #7fd0e8); color: #04060a;
min-height: 48px;
}
button.zweit { background: transparent; color: #7fd0e8; border: 1px solid rgba(127,208,232,.34); }
.knoepfe { display: flex; flex-wrap: wrap; gap: .6rem; margin-top: 1rem; }
.hinweis {
margin-top: 1.6rem; padding: 1rem 1.1rem; border-radius: 12px;
background: rgba(232,200,119,.08); border: 1px solid rgba(232,200,119,.32);
}
.hinweis b { color: #e8c877; }
code { background: rgba(127,208,232,.1); padding: .1rem .35rem; border-radius: 5px; font-size: .88em; }
</style>
</head>
<body>
<div class="hu">
<h1>Diagnose</h1>
<p class="lead">
Diese Seite prüft, warum der Verwaltungs- oder Portalbereich nicht lädt.
Sie lädt selbst keine externen Dateien — sie funktioniert also auch dann,
wenn genau die das Problem sind.
</p>
<h2>Prüfungen</h2>
<div id="ergebnis">
<div class="zeile"><span class="mark wart" aria-hidden="true">…</span><div class="inhalt"><b>Prüfung läuft</b><span class="text">Einen Moment.</span></div></div>
</div>
<div class="knoepfe">
<button type="button" id="reparieren">Zwischenspeicher leeren und neu laden</button>
<button type="button" class="zweit" id="nochmal">Nochmal prüfen</button>
</div>
<div class="hinweis">
<p style="margin:0 0 .4rem"><b>Was der grosse Knopf macht</b></p>
<p style="margin:0;font-size:.92rem">
Er meldet den Service Worker ab und löscht alle zwischengespeicherten
Dateien dieser Seite. Danach holt der Browser alles frisch vom Server.
<strong>Deine Anmeldung und deine Daten bleiben unberührt</strong> —
gelöscht werden nur Kopien von Dateien, die ohnehin jederzeit neu
geladen werden können.
</p>
</div>
</div>
<script>
/* Alles inline und ohne Abhängigkeiten — siehe Begründung im CSS-Kopf. */
(function () {
"use strict";
var API = "https://postfach.dogfather-universe.com";
var ziel = document.getElementById("ergebnis");
/* Die Zeile wird Element fuer Element gebaut, NICHT ueber innerHTML mit
anschliessendem querySelector.
Genau daran ist die erste Fassung gescheitert: Sie schrieb
d.querySelector("span").textContent = text -- und querySelector
liefert den ERSTEN passenden span. Das war der Statuskreis, nicht das
Textfeld darunter. Der ganze Beschreibungstext landete dadurch in
einem 26 Pixel breiten Kreis und lief als schmale Buchstabensaeule
ueber die halbe Seite.
Mit direkten Verweisen auf die erzeugten Elemente kann das nicht
passieren -- es gibt nichts zu verwechseln. */
function zeile(art, titel, text) {
var zeichen = { gut: "✓", warn: "!", bad: "✕", wart: "…" };
var d = document.createElement("div");
d.className = "zeile";
var kreis = document.createElement("span");
kreis.className = "mark " + art;
kreis.textContent = zeichen[art] || "?";
kreis.setAttribute("aria-hidden", "true");
var inhalt = document.createElement("div");
inhalt.className = "inhalt";
var ueberschrift = document.createElement("b");
ueberschrift.textContent = titel;
var beschreibung = document.createElement("span");
beschreibung.className = "text";
beschreibung.textContent = text;
inhalt.appendChild(ueberschrift);
inhalt.appendChild(beschreibung);
d.appendChild(kreis);
d.appendChild(inhalt);
ziel.appendChild(d);
}
async function pruefen() {
ziel.innerHTML = "";
/* 1. Service Worker — der häufigste Grund für "es lädt die alte
Fassung". Er überlebt ein normales Neuladen. */
if ("serviceWorker" in navigator) {
try {
var regs = await navigator.serviceWorker.getRegistrations();
var meine = regs.filter(function (r) {
return (r.scope || "").indexOf("/webdesign") >= 0;
});
if (!meine.length) {
zeile("gut", "Service Worker", "Keiner aktiv — Dateien kommen direkt vom Server.");
} else {
zeile("warn", "Service Worker aktiv",
meine.length + " registriert. Er kann alte Dateien ausliefern. "
+ "Der Knopf unten meldet ihn ab.");
}
} catch (e) {
zeile("warn", "Service Worker", "Konnte nicht geprüft werden: " + e.message);
}
} else {
zeile("gut", "Service Worker", "Von diesem Browser nicht unterstützt — kein Problem.");
}
/* 2. Zwischenspeicher */
if ("caches" in window) {
try {
var namen = await caches.keys();
var meineC = namen.filter(function (n) { return n.indexOf("webdesign") >= 0; });
if (!meineC.length) zeile("gut", "Zwischenspeicher", "Leer.");
else zeile("warn", "Zwischenspeicher", "Enthält: " + meineC.join(", "));
} catch (e) {
zeile("warn", "Zwischenspeicher", "Nicht prüfbar: " + e.message);
}
}
/* 3. Ist der Server erreichbar? Ein Endpunkt, der ohne Anmeldung
antwortet -- 401 ist hier die RICHTIGE Antwort und beweist,
dass alles bis dorthin funktioniert. */
try {
var t0 = Date.now();
var a = await fetch(API + "/webdesign/admin/anfragen", { cache: "no-store" });
var ms = Date.now() - t0;
if (a.status === 401) {
zeile("gut", "Server erreichbar", "Antwortet in " + ms + " ms (Status 401 = erwartet ohne Anmeldung).");
} else if (a.status === 404) {
zeile("bad", "Server veraltet", "Status 404 — der interne Dienst kennt diese Adresse noch nicht.");
} else {
zeile("warn", "Server antwortet", "Status " + a.status + " nach " + ms + " ms.");
}
} catch (e) {
zeile("bad", "Server NICHT erreichbar",
"Der Browser kommt nicht an " + API + " heran. " + (e.message || ""));
}
/* 4. Die neuen Adressen */
try {
var b = await fetch(API + "/webdesign/admin/einstellungen", { cache: "no-store" });
if (b.status === 401) zeile("gut", "Zahlungen-Bereich", "Vorhanden und geschützt.");
else if (b.status === 404) zeile("bad", "Zahlungen-Bereich fehlt",
"Der Server muss noch aktualisiert werden (git pull + Neustart).");
else zeile("warn", "Zahlungen-Bereich", "Status " + b.status + ".");
} catch (e) {
zeile("bad", "Zahlungen-Bereich", "Nicht erreichbar: " + (e.message || ""));
}
/* 5. Anmeldung im Browser vorhanden? */
try {
var tok = sessionStorage.getItem("wd-vw-token");
if (tok) zeile("gut", "Anmeldung Verwaltung", "Ein Sitzungsschlüssel liegt vor.");
else zeile("warn", "Anmeldung Verwaltung",
"Keiner vorhanden — die Verwaltung fragt nach dem Zugangscode. Das ist normal, "
+ "wenn die Seite zwischendurch geschlossen war.");
} catch (e) {
zeile("warn", "Anmeldung", "Speicher nicht lesbar: " + e.message);
}
/* 6. Welche Fassung liegt im Browser? */
try {
var c = await fetch("/assets/css/webdesign.css?diagnose=" + Date.now(), { cache: "no-store" });
var txt = await c.text();
var hatNeu = txt.indexOf("--wd-blau-rgb") >= 0;
zeile(hatNeu ? "gut" : "bad", "Gestaltungsdatei",
hatNeu ? "Aktuelle Fassung wird ausgeliefert."
: "VERALTETE Fassung. Der Knopf unten hilft.");
} catch (e) {
zeile("warn", "Gestaltungsdatei", "Nicht prüfbar: " + e.message);
}
}
document.getElementById("nochmal").addEventListener("click", pruefen);
document.getElementById("reparieren").addEventListener("click", async function () {
var k = this;
k.disabled = true;
k.textContent = "Wird bereinigt …";
try {
if ("serviceWorker" in navigator) {
var regs = await navigator.serviceWorker.getRegistrations();
for (var i = 0; i < regs.length; i++) await regs[i].unregister();
}
if ("caches" in window) {
var namen = await caches.keys();
for (var j = 0; j < namen.length; j++) await caches.delete(namen[j]);
}
} catch (e) { /* weiter -- neu laden hilft meistens trotzdem */ }
k.textContent = "Fertig — wird neu geladen …";
/* Mit Zeitstempel, damit auch der Browser-eigene Zwischenspeicher
umgangen wird. */
setTimeout(function () {
location.href = "/webdesign/verwaltung.html?frisch=" + Date.now();
}, 700);
});
pruefen();
})();
</script>
</body>
</html>