Files
dogfather-universe/server/pruef-ueberlappung.mjs
T
DogFatherGit 315eb08cc4 Eine Wache dafuer, dass am Telefon mit dem Finger gemessen wird
DER FUND VON HEUTE WAR GROESSER ALS DIE EINE FUSSZEILE

`page.setViewportSize({ width: 412 })` aendert nur die Groesse.
`hasTouch` gehoert zum KONTEXT und laesst sich danach nicht mehr
setzen -- ohne ihn meldet der Browser `pointer: fine`, und KEINE
einzige Regel aus `@media (pointer: coarse)` greift. Dort stehen im
ganzen Haus die 44-Pixel-Beruehrziele.

Eine Messung mit Mauszeiger auf 412 Pixeln vermisst also eine Seite,
die es auf keinem Telefon gibt -- und meldet dabei "in Ordnung".
Das ist die dritte Sorte falscher Haken: nicht uebersprungen, nicht
rot, sondern gruen und wertlos.

GEZAEHLT: 30 Dateien oeffnen ein Telefonformat. 16 davon messen
darin Groessen, ohne einen Finger zu haben.

pruef-ueberlappung IST UMGESTELLT

Sie sucht Bedienelemente, die uebereinander liegen -- also genau die
Groessen, die erst unter `pointer: coarse` entstehen. Sie hat bis
heute mit dem Zeiger gemessen. Mit Finger: 20 Seiten-Breiten-Paare,
0 Befunde. Das ist zum ersten Mal wirklich geprueft und nicht nur
behauptet. Oberhalb von 860 px bleibt es beim Zeiger; ein Laptop
hat keinen Finger.

DIE UEBRIGEN 16 WERDEN NICHT HEUTE NACHT UMGESTELLT

Sechzehn Pruefungen anzufassen und jede einzeln neu laufen zu lassen
waere ein Gesamtlauf durch die Hintertuer -- und genau die Ausrede,
die in CLAUDE.md steht. Stattdessen haelt `pruef-fingermass.mjs`
eine Grundlinie: Sie meldet nicht 16 Fehler auf einmal (eine
Warnung, die immer kommt, wird weggeklickt und nimmt den echten Fund
mit), sondern schlaegt an, sobald eine SIEBZEHNTE dazukommt.

Sie schlaegt auch an, wenn die Zahl SINKT. Wer eine Messung
repariert und die Grundlinie stehen laesst, deckt Platz fuer die
naechste Suende. Das kostet eine Zeile und haelt die Wache scharf.

DIE ERSTE ZAHL EINER WACHE IST SELTEN DIE RICHTIGE

Mein grep hatte 18 gezaehlt. Zwei davon waren Zahlen in Kommentaren
("gemessen bei 412 px"), keine Fenster. Die Pruefung liest deshalb
nur die Stelle, die ein Fenster AUFMACHT, und blendet Kommentare
vorher aus.

DREI AUSGAENGE, SECHS GEGENPROBEN

Findet sie weniger als 50 Pruefdateien, ist das kein "alles in
Ordnung", sondern "konnte nicht nachsehen" (Rueckgabewert 2). Die
Gegenproben decken beide Richtungen ab, darunter der Fall
`setViewportSize` -- der kann `hasTouch` gar nicht nachtragen und
zaehlt deshalb immer.

`tools/alles-pruefen.mjs` liest den Ordner, keine gepflegte Liste --
die neue Wache laeuft ohne Zutun mit.

GEPRUEFT
  pruef-fingermass    3 / 0   (neu)
  pruef-ueberlappung  EXIT 0 -- 20 Paare, 0 Befunde, jetzt mit Finger
  pruef-portnummern   15 / 0
2026-09-29 14:04:06 +02:00

188 lines
9.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* Liegt auf einer Seite irgendein Bedienelement UEBER einem anderen?
Am 06.09.2026 lag der Sicht-Umschalter bei 412 px quer ueber dem
Chat-Knopf -- auf zwoelf Seiten. Wer auf "Meine Sicht" tippte, landete
im Chat. Gefunden wurde das durch Hinsehen; das hier misst es. */
import { chromium } from "file:///C:/Users/qciga/Documents/Obelix/Analyse/node_modules/playwright/index.mjs";
import { spawn } from "node:child_process";
import { mkdtempSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { DatabaseSync } from "node:sqlite";
import { scryptSync, randomBytes } from "node:crypto";
const ordner = mkdtempSync(join(tmpdir(), "ws-kopf-"));
const DB = join(ordner, "w.db");
const PORT = 4367, BASIS = `http://127.0.0.1:${PORT}`;
const start = () => spawn(process.execPath, ["server/index.js"], {
cwd: "C:/Users/qciga/Documents/Obelix/DogiHompage",
env: { ...process.env, WORKSPACE_DB: DB, PORT: String(PORT),
SITE_ACCESS_SECRET: "lokaler-test", SITE_PUBLIC_LAUNCH_AT: "2020-01-01T00:00:00+01:00" },
stdio: ["ignore", "pipe", "pipe"] });
const warten = async () => {
for (let i = 0; i < 80; i++) { await new Promise((r) => setTimeout(r, 250));
try { const a = await fetch(BASIS + "/workspace/", { redirect: "manual" }); if (a.status < 500) return; } catch { /* noch nicht */ } }
};
let kind = start(); await warten();
kind.kill(); await new Promise((r) => setTimeout(r, 700));
{
const d = new DatabaseSync(DB);
const s = randomBytes(16).toString("hex");
const h = scryptSync("CODE-DOGI-0001", s, 64, { N: 32768, r: 8, p: 1, maxmem: 96 * 1024 * 1024 }).toString("hex");
d.prepare("INSERT INTO personen (name, rolle, code_hash, code_salt, code_n, aktiv, erstellt) VALUES ('Filipe','admin',?,?,?,1,?)")
.run(h, s, 32768, new Date().toISOString());
d.close();
}
kind = start(); await warten();
const a = await fetch(BASIS + "/workspace/api/anmelden", {
method: "POST", headers: { "Content-Type": "application/json" },
body: JSON.stringify({ rolle: "admin", code: "CODE-DOGI-0001" }) });
const kekse = (a.headers.getSetCookie?.() || []).map((z) => {
const [np] = z.split(";"); const [name, ...wert] = np.split("=");
return { name: name.trim(), value: wert.join("="), domain: "127.0.0.1", path: "/" };
});
const SEITEN = ["start.html", "kalender.html", "aufgaben.html", "calls.html", "chat.html"];
/* Die Breiten, an denen es wirklich klemmt: 320 = kleinstes uebliches
Geraet, 412 = haeufigste Android-Breite, 768 = Tablet hochkant,
1280 = kleinster Laptop. Keine davon ist geraten. */
const BREITEN = [320, 412, 768, 1280];
/* MIT FINGER, NICHT MIT MAUSZEIGER (29.09.2026).
`viewport` allein macht aus einem Browser kein Telefon. `hasTouch`
gehoert zum KONTEXT und laesst sich spaeter nicht mehr setzen;
ohne ihn meldet der Browser `pointer: fine`, und KEINE Regel aus
`@media (pointer: coarse)` greift. Dort stehen im ganzen Haus die
44-Pixel-Beruehrziele -- also genau die Groessen, deren
Ueberlappung diese Pruefung sucht. Sie hat bis heute eine Seite
vermessen, die es auf keinem Handy gibt.
Oberhalb von 860 px bleibt es beim Zeiger: Ein Laptop hat keinen. */
const b = await chromium.launch();
let funde = 0, geprueft = 0;
for (const breite of BREITEN) {
const sitz = await b.newContext({
viewport: { width: breite, height: 900 }, hasTouch: breite <= 860 });
await sitz.addCookies(kekse);
const p = await sitz.newPage();
for (const seite of SEITEN) {
await p.goto(`${BASIS}/workspace/${seite}`, { waitUntil: "networkidle" }).catch(() => {});
await p.waitForTimeout(900);
const treffer = await p.evaluate(() => {
const nimm = (n) => { const r = n.getBoundingClientRect(); return { r, n }; };
/* Nur die Kopfleiste: dort stehen die Elemente am dichtesten, und
dort war der echte Fehler. */
const leiste = document.querySelector(".kopfleiste");
if (!leiste) return ["KEINE KOPFLEISTE"];
const teile = [...leiste.querySelectorAll("button, a, select, .knopf")]
.filter((n) => { const r = n.getBoundingClientRect(); return r.width > 0 && r.height > 0; })
.map(nimm);
const raus = [];
for (let i = 0; i < teile.length; i++) {
for (let j = i + 1; j < teile.length; j++) {
const A = teile[i], B = teile[j];
if (A.n.contains(B.n) || B.n.contains(A.n)) continue; // verschachtelt ist kein Fehler
/* DASSELBE BEDIENELEMENT ZAEHLT NICHT (Befund beim Bauen).
Der erste Lauf meldete zehnmal "Meine Sicht × Meine Sicht".
Das ist ein durchsichtiges <select>, das ueber seinem
eigenen gestalteten Schild liegt -- die uebliche Bauart fuer
eine Auswahl, die aussehen soll wie ein Knopf. Wer darauf
tippt, landet genau dort, wo er hinwollte.
Die Frage lautet nicht "ueberlappen zwei Kaesten", sondern
"komme ich woanders hin als ich tippe". Ueberlappungen
INNERHALB eines Bedienelements sind deshalb kein Fund --
sonst meldet die Pruefung jedes Mal dasselbe, und eine
Warnung, die immer kommt, ist keine Warnung mehr. */
const huelleA = A.n.closest(".sicht, .knopf, label, .wer");
const huelleB = B.n.closest(".sicht, .knopf, label, .wer");
if (huelleA && huelleA === huelleB) continue;
const x = Math.min(A.r.right, B.r.right) - Math.max(A.r.left, B.r.left);
const y = Math.min(A.r.bottom, B.r.bottom) - Math.max(A.r.top, B.r.top);
if (x > 2 && y > 2) {
const kurz = (n) => (n.textContent || n.className || n.tagName).trim().slice(0, 22);
raus.push(`${kurz(A.n)} × ${kurz(B.n)} (${Math.round(x)}×${Math.round(y)} px)`);
}
}
}
return raus;
});
geprueft++;
if (treffer.length) {
funde += treffer.length;
console.log(` FEHL ${seite} @${breite}px:`);
for (const t of treffer) console.log(" " + t);
}
}
await sitz.close();
}
/* =====================================================================
DIE GEGENPROBE -- ohne sie beweist "0 Ueberlappungen" gar nichts.
Eine Pruefung, die immer bestaetigt, bestaetigt nichts. Hier wird
deshalb absichtlich ein Knopf verschoben, bis er ueber einem anderen
liegt. Schlaegt die Pruefung dann NICHT an, ist sie kaputt -- und der
ganze Lauf muss scheitern, nicht nur eine Zeile melden.
Warum ausgerechnet dieser Fehler: Am 06.09.2026 lag der
Sicht-Umschalter bei 412 px quer ueber dem Chat-Knopf, auf zwoelf
Seiten. Wer "Meine Sicht" antippte, landete im Chat. Aufgefallen ist
das durch Hinsehen -- gemessen hatte es niemand. */
{
const sitz = await b.newContext({ viewport: { width: 412, height: 900 }, hasTouch: true });
await sitz.addCookies(kekse);
const p = await sitz.newPage();
await p.goto(BASIS + "/workspace/start.html", { waitUntil: "networkidle" });
await p.waitForTimeout(900);
const merkt = await p.evaluate(() => {
const leiste = document.querySelector(".kopfleiste");
const teile = [...leiste.querySelectorAll("button, a, .knopf")]
.filter((n) => { const r = n.getBoundingClientRect(); return r.width > 0 && r.height > 0; });
if (teile.length < 2) return "zu wenige Bedienelemente zum Pruefen";
/* Den letzten Knopf genau auf den ersten legen. */
const erst = teile[0].getBoundingClientRect();
const letzt = teile[teile.length - 1];
/* MIT transform STATT position:fixed (14.09.2026).
`position: fixed` bezieht sich NICHT immer auf das Fenster: Hat
ein Vorfahr `transform`, `filter` oder `backdrop-filter`, wird er
selbst zum Bezugsrahmen. Die Kopfleiste hat so einen. Der Knopf
landete dadurch 6 px neben dem Ziel -- gemessen {x:-6, y:36} --,
die Gegenprobe meldete „nicht erkannt“, und damit konnte diese
Pruefung tagelang nicht mehr zeigen, dass sie eine Ueberlappung
ueberhaupt findet. Eine Pruefung, die das nicht kann, bestaetigt
nichts.
`transform` verschiebt relativ zur EIGENEN aktuellen Lage. Wo der
Bezugsrahmen liegt, ist damit gleichgueltig. */
/* UND OHNE UEBERBLENDUNG. Die Knoepfe der Kopfleiste haben eine
`transition`; wer unmittelbar nach dem Setzen misst, bekommt die
Lage von VORHER zurueck -- die Bewegung hat noch nicht
stattgefunden. Das war die zweite Haelfte desselben Fehlers.
Derselbe Grund, aus dem in pruef-dabei-optik das Haekchen
„0 → 0“ meldete, bis dort die Ueberblendung abgewartet wurde. */
letzt.style.transition = "none";
const vorher = letzt.getBoundingClientRect();
letzt.style.transform =
`translate(${erst.left - vorher.left}px, ${erst.top - vorher.top}px)`;
letzt.style.zIndex = "99";
// Messen wie oben, aber nur dieses eine Paar.
const a2 = teile[0].getBoundingClientRect(), b2 = letzt.getBoundingClientRect();
const x = Math.min(a2.right, b2.right) - Math.max(a2.left, b2.left);
const y = Math.min(a2.bottom, b2.bottom) - Math.max(a2.top, b2.top);
return { x: Math.round(x), y: Math.round(y) };
});
const erkannt = typeof merkt === "object" && merkt.x > 2 && merkt.y > 2;
console.log(erkannt
? ` ok Gegenprobe: ein absichtlich verschobener Knopf wird als Ueberlappung erkannt (${merkt.x}×${merkt.y} px)`
: ` FEHL Gegenprobe: der verschobene Knopf wurde NICHT erkannt (${JSON.stringify(merkt)})`);
if (!erkannt) funde++;
await sitz.close();
}
console.log(`\n${geprueft} Seiten-Breiten-Paare geprueft, ${funde} Befunde.`);
console.log(funde === 0 ? "ALLES IN ORDNUNG" : `${funde} FEHLER`);
await b.close(); kind.kill();
process.exit(funde ? 1 : 0);