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
188 lines
9.5 KiB
JavaScript
188 lines
9.5 KiB
JavaScript
/* 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);
|