Gemessen, nicht geschaetzt: Auf 1440 px lagen zwischen dem Ende des Textes und der Uhr rund 300 px Leere. Dort stehen jetzt HEUTE (Termine), ALS NAECHSTES (Uhrzeit) und OFFEN (Punkte, Farbe nach Lage). Keine zweite Zaehlung: Die Zahlen kommen aus denselben zwei Quellen, die die Kacheln darunter fuellen. Zwei Rechenwege fuer dieselbe Zahl laufen auseinander, und dann stehen zwei Wahrheiten auf einem Bildschirm. EINE Fassung mit Stegen, nicht drei Kaestchen. Der erste Anlauf gab jedem Wert eine eigene Fraesung -- im Bild sah das aus wie aufgeklebte Plaettchen. Ein Instrumentenblock ist EIN eingelassenes Feld, in dem Stege trennen; das Licht laeuft dann einmal ueber eine Kante statt sechsmal. Auf dem Handy geht der Block auf volle Breite und richtet sich nach der Uhr, nicht nach dem Rand. EINE ANNAHME KORRIGIERT: In meiner Merkliste stand "Werktisch-Aufbau nach VanVans Business Hub, Prozentring links". Im Hub nachgesehen -- es gibt dort keinen Ring und keinen solchen Aufbau, nur eine schlichte buehne-hero. Filipes Verweis galt der UHR, und die ist laengst gebaut. Meine eigenen Notizen altern wie jede andere Bestandsliste. DREI BEFUNDE AUS EIGENEN PRUEFUNGEN, alle behoben: 1. pruef-css-klassen: Die Zahl der Schriftgroessen unter 11,5 px war um genau eine gestiegen -- .stand__schild stand auf 9,3 px. Gesperrte Grossbuchstaben in 9 px liest man nicht, man erraet sie. Jetzt 11,5 px mit etwas engerer Sperrung, damit drei Schilder bei 320 px weiterhin nebeneinander passen (nachgemessen: 287 px, nichts abgeschnitten). 2. pruef-struktur: pruef-arten.mjs bildete das Tagesdatum aus UTC. Nachts zwischen 00:00 und 02:00 waere sie rot geworden, ohne dass am Code etwas falsch ist. Derselbe Fehler war mir am selben Abend schon im Messskript passiert -- dort hatte ich "Heute=0" gemessen und den Code verdaechtigt, der richtig lag. 3. Beim Bauen fast eingebaut: margin-left:auto von der Uhrgruppe genommen, weil der neue Block sie ja schon nach rechts schiebt. Er tut das nur, solange er da ist -- bis zur ersten Antwort steht er auf hidden, und die Uhr waere sichtbar weggesprungen. Neu: server/pruef-ueberlappung.mjs. Misst auf 5 Seiten x 4 Breiten, ob ein Bedienelement ueber einem anderen liegt (am 06.09. lag der Sicht-Umschalter bei 412 px auf zwoelf Seiten ueber dem Chat-Knopf). Ueberlappungen INNERHALB eines Bedienelements zaehlen nicht -- ein durchsichtiges select ueber seinem eigenen Schild ist die uebliche Bauart, und eine Warnung, die immer kommt, ist keine Warnung mehr. Mit Gegenprobe: ein absichtlich verschobener Knopf muss erkannt werden. Co-Authored-By: Claude Opus 5 <[email protected]>
158 lines
7.7 KiB
JavaScript
158 lines
7.7 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];
|
||
|
||
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 } });
|
||
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 } });
|
||
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];
|
||
letzt.style.position = "fixed";
|
||
letzt.style.left = erst.left + "px";
|
||
letzt.style.top = erst.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);
|