/* 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