/* ===================================================================== WER MACHT DIE SUPPORT-SEITE ZU BREIT? (25.09.2026) ===================================================================== `pruef-breiten` meldet auf jeder Breite unter 1440 px einen Ueberstand -- 13 px auf dem Handy, 41 px auf dem Tablet quer -- und die Liste der Schuldigen bleibt leer: `[]`. Damit weiss man, DASS etwas zu breit ist, und nicht WAS. DIESE MESSUNG SUCHT DEN SCHULDIGEN, indem sie jedes Element im Dokument fragt, ob seine rechte Kante ueber die Dokumentbreite hinausragt -- und dann den KLEINSTEN nennt, der es tut. Der groesste ist immer , und der sagt nichts; der kleinste ist der, den man anfassen muss. Aufrufen mit: node server/mess-support-ueberstand.mjs ===================================================================== */ import { mkdtempSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { eigenerPort } from "./helfer-port.mjs"; const PORT = await eigenerPort(import.meta); const ordner = mkdtempSync(join(tmpdir(), "ws-ueber-")); process.env.WORKSPACE_DB = join(ordner, "workspace.db"); process.env.PORT = String(PORT); process.env.SITE_ACCESS_SECRET = "lokaler-test"; process.env.SITE_PUBLIC_LAUNCH_AT = "2020-01-01T00:00:00+01:00"; const express = (await import("express")).default; const ec = express.response.cookie; express.response.cookie = function (n, w, o) { return ec.call(this, n, w, { ...(o || {}), secure: false }); }; import { notbremse } from "./helfer-notbremse.mjs"; await import("./index.js"); notbremse(240_000, "mess-support-ueberstand"); await new Promise((r) => setTimeout(r, 900)); process.on("uncaughtException", (f) => { console.error("ABSTURZ:", f); process.exit(7); }); const BASIS = `http://127.0.0.1:${PORT}`; const { DatabaseSync } = await import("node:sqlite"); const { scryptSync, randomBytes, createHmac } = await import("node:crypto"); const d = new DatabaseSync(process.env.WORKSPACE_DB); const jetzt = new Date().toISOString(); const sch = randomBytes(32).toString("hex"); d.prepare("INSERT INTO einstellungen (schluessel, wert, geaendert, von)" + " VALUES (?,?,?,NULL) ON CONFLICT(schluessel) DO NOTHING") .run("code_kennung_schluessel", sch, jetzt); const anlegen = (name, rolle, code) => { const s = randomBytes(16).toString("hex"); const h = scryptSync(code, s, 64, { N: 32768, r: 8, p: 1, maxmem: 96 * 1024 * 1024 }).toString("hex"); return d.prepare("INSERT INTO personen" + " (name, rolle, code_hash, code_salt, code_n, code_kennung, aktiv, erstellt)" + " VALUES (?,?,?,?,?,?,1,?) RETURNING id") .get(name, rolle, h, s, 32768, createHmac("sha256", sch).update(code).digest("hex"), jetzt).id; }; anlegen("Filipe", "admin", "CODE-DOGI-0001"); const idModi = anlegen("Miss", "modi", "CODE-MODI-0001"); /* Eine Meldung, damit die Seite nicht leer ist -- ein leerer Eingang kann gar nicht zu breit sein. */ d.prepare(`INSERT INTO support_meldungen (person_id, von_name, von_rolle, text, seite, stand, erstellt, geaendert) VALUES (?,?,?,?,?,?,?,?)`) .run(idModi, "Miss", "modi", "Der Kalender springt beim Wischen manchmal zwei Monate weiter, " + "und danach komme ich nicht mehr zurueck.", "/workspace/kalender.html", "neu", jetzt, jetzt); d.close(); const an = await fetch(BASIS + "/workspace/api/anmelden", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ rolle: "admin", code: "CODE-DOGI-0001" }), }); const keks = (an.headers.getSetCookie?.() || []).map((z) => z.split(";")[0]).join("; "); const pw = await import( "file:///C:/Users/qciga/Documents/Obelix/Analyse/node_modules/playwright/index.mjs"); const browser = await pw.chromium.launch(); for (const [wie, breite] of [["Handy", 390], ["Tablet quer", 1024], ["Laptop", 1280]]) { const kontext = await browser.newContext({ viewport: { width: breite, height: 900 }, hasTouch: breite <= 860 }); await kontext.addCookies(keks.split("; ").map((k) => { const [n, ...r] = k.split("="); return { name: n, value: r.join("="), domain: "127.0.0.1", path: "/" }; })); const seite = await kontext.newPage(); await seite.goto(BASIS + "/workspace/" + (process.env.SEITE || "support.html"), { waitUntil: "networkidle" }); await seite.waitForTimeout(900); const fund = await seite.evaluate(() => { const doc = document.documentElement; const ueber = doc.scrollWidth - doc.clientWidth; const grenze = doc.clientWidth; const schuld = []; for (const e of document.querySelectorAll("*")) { const r = e.getBoundingClientRect(); if (r.width === 0 && r.height === 0) continue; const rechts = r.right + window.scrollX; if (rechts <= grenze + 0.5) continue; /* Nur das KLEINSTE, das herausragt -- ein Elternteil ragt immer mit heraus, wenn es ein Kind tut, und nennt den Grund nicht. */ const hatSchuldigesKind = [...e.children].some((k) => { const kr = k.getBoundingClientRect(); return kr.right + window.scrollX > grenze + 0.5; }); if (hatSchuldigesKind) continue; const st = getComputedStyle(e); schuld.push({ was: e.tagName.toLowerCase() + (e.id ? "#" + e.id : "") + (e.className && typeof e.className === "string" ? "." + e.className.trim().split(/\s+/).join(".") : ""), links: Math.round(r.left), rechts: Math.round(rechts), breite: Math.round(r.width), ueberstand: Math.round(rechts - grenze), position: st.position, transform: st.transform === "none" ? "" : st.transform.slice(0, 40), minBreite: st.minWidth, text: (e.textContent || "").trim().slice(0, 30), }); } return { ueber, grenze, schuld: schuld.sort((a, b) => b.ueberstand - a.ueberstand) }; }); console.log(`\n=== ${wie} (${breite} px) -- Ueberstand ${fund.ueber} px ===`); if (!fund.schuld.length) console.log(" niemand ragt heraus (dann ist es ein Rand/Schatten)"); for (const s of fund.schuld.slice(0, 6)) { console.log(` ${s.was}`); console.log(` links ${s.links}, rechts ${s.rechts} (Grenze ${fund.grenze}), ` + `breit ${s.breite}, ueber ${s.ueberstand}`); console.log(` position:${s.position} min-width:${s.minBreite}` + (s.transform ? ` transform:${s.transform}` : "") + (s.text ? ` "${s.text}"` : "")); } await kontext.close(); } await browser.close(); try { rmSync(ordner, { recursive: true, force: true }); } catch { /* egal */ } process.exit(0);