/* ===================================================================== SIEHT MAN, WO DER TASTATURFOKUS STEHT? ===================================================================== Gebaut am 25.09.2026, weil `pruef-neue-seiten` einen Kontrast von 1,07 meldete ("Schoen, dass du da bist") und die Stelle im CSS tadellos aussah. Diese Messung liest die Farben an Ort und Stelle aus und geht die Elternkette hoch, bis sie eine deckende Flaeche findet -- genau das, was die Pruefung tut, nur sichtbar. Ergebnis damals: dunkler Text auf HELLBLAUER Flaeche, rund 10:1. Die Pruefung konnte das Format `color(srgb ...)` nicht lesen, ging deshalb zum fast schwarzen Elternteil weiter und rechnete dunkel gegen dunkel. Ohne diese Messung haette ich an einem CSS geaendert, an dem nichts war. Aufrufen mit: node server/mess-farbe-am-text.mjs "" ===================================================================== */ import { mkdtempSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; const SEITE = process.argv[2] || "wissen.html"; const SUCHE = process.argv[3] || ".kachel"; const PORT = 5457; /* IST DIE NUMMER FREI? Sonst misst dieses Werkzeug an einem fremden Server -- und liefert ein Ergebnis, das zu einem ganz anderen Stand gehoert. Die Pruefungen holen sich ihre Nummer ueber helfer-port; ein Messwerkzeug, das von Hand gestartet wird, braucht wenigstens diese eine Frage. */ { const netz = await import("node:net"); const frei = await new Promise((fertig) => { const s = netz.createServer(); s.once("error", () => fertig(false)); s.once("listening", () => s.close(() => fertig(true))); s.listen(PORT, "127.0.0.1"); }); if (!frei) { console.error(`Port ${PORT} ist belegt -- hier laeuft schon etwas. ` + "Gemessen wuerde sonst ein fremder Stand. Abgebrochen."); process.exit(3); } } const ordner = mkdtempSync(join(tmpdir(), "ws-kontrast-")); 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(200_000, "mess-kontrast"); await new Promise((r) => setTimeout(r, 900)); 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"); anlegen("Sina", "scout", "CODE-SCOUT-0001"); d.close(); const an = await fetch(BASIS + "/workspace/api/anmelden", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ rolle: "scout", code: "CODE-SCOUT-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(); const kontext = await browser.newContext({ viewport: { width: 1440, height: 900 } }); 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/${SEITE}`, { waitUntil: "networkidle" }); await seite.waitForTimeout(900); const stil = (e) => { const c = getComputedStyle(e); return { outlineStyle: c.outlineStyle, outlineWidth: c.outlineWidth, outlineColor: c.outlineColor, boxShadow: c.boxShadow, borderColor: c.borderColor, backgroundColor: c.backgroundColor, textDecorationLine: c.textDecorationLine, filter: c.filter, clipPath: c.clipPath }; }; const vorher = await seite.evaluate((sel) => { const e = document.querySelector(sel); if (!e) return null; e.setAttribute("data-mess", "1"); const c = getComputedStyle(e); return { text: (e.textContent || "").trim().slice(0, 30), tag: e.tagName, outline: `${c.outlineStyle} ${c.outlineWidth} ${c.outlineColor}`, boxShadow: c.boxShadow, borderColor: c.borderColor, backgroundColor: c.backgroundColor, filter: c.filter, clipPath: c.clipPath.slice(0, 60) }; }, SUCHE); if (!vorher) { console.log(`${SUCHE} nicht gefunden auf ${SEITE}`); process.exit(1); } await seite.evaluate(() => document.body.focus?.()); let gefunden = null; for (let i = 0; i < 40 && !gefunden; i++) { await seite.keyboard.press("Tab"); await seite.waitForTimeout(140); gefunden = await seite.evaluate(() => { const e = document.activeElement; if (!e || !e.hasAttribute?.("data-mess")) return null; const c = getComputedStyle(e); return { outline: `${c.outlineStyle} ${c.outlineWidth} ${c.outlineColor}`, boxShadow: c.boxShadow, borderColor: c.borderColor, backgroundColor: c.backgroundColor, filter: c.filter, clipPath: c.clipPath.slice(0, 60), fv: e.matches(":focus-visible"), f: e.matches(":focus") }; }); } console.log(` === ${SUCHE} auf ${SEITE} (${vorher.tag}, "${vorher.text}") ===`); if (!gefunden) console.log(" mit der Tastatur NICHT erreicht"); else { console.log(` :focus ${gefunden.f} :focus-visible ${gefunden.fv}`); for (const k of ["outline", "boxShadow", "borderColor", "backgroundColor", "filter", "clipPath"]) { const gleich = vorher[k] === gefunden[k]; console.log(` ${k.padEnd(16)} ${gleich ? "gleich " : "ANDERS "} ` + `${String(vorher[k]).slice(0, 55)}` + (gleich ? "" : ` -> ${String(gefunden[k]).slice(0, 55)}`)); } } await browser.close(); process.exit(0);