/* ===================================================================== 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"; /* DIE NUMMER KOMMT AUS DEM ALPHABET, WIE BEI DEN PRUEFUNGEN (01.10.2026). Hier stand eine feste 5457 mit einer eigenen, kurzen Fassung des Waechters daneben. Die konnte weniger als das Original: Sie kannte den dritten Ausgang nicht (EACCES -- das System gibt den Port dauerhaft nicht her) und sagte nicht, wie man nachsieht, wer ihn haelt. Eine Abschrift eines Waechters ist dieselbe Falle wie import { eigenerPort } from "./helfer-port.mjs"; eine Abschrift einer Spaltenliste. */ const PORT = await eigenerPort(import.meta); 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);