From ea4078740e8925b3c4cf4765171a159d0e7cba07 Mon Sep 17 00:00:00 2001 From: Dogfather Date: Wed, 7 Oct 2026 00:04:49 +0200 Subject: [PATCH] Pipeline: die Stufenfarbe kommt endlich auf dem Bildschirm an Filipe: "zuerst will ich dass du die kategorien und die kacheln viel krasser geiler machst." Beim Nachmessen stand fest, warum die Pipeline flach aussah: Drei Regeln in scouting.css kamen gar nicht an, seit es den Baukasten (module.css) gibt -- und module.css NENNT zwei davon im eigenen Text. --ton war an JEDER Karte #db4b66, dem Rot der Seite. Daraus nimmt der Baukasten Kantenlicht, die drei Eckwinkel und das Licht beim Zeigen. Sechs Stufenfarben in der Datei, eine auf dem Schirm. .kk::before (3px) gemessen 560px breit -- das ist das Kantenlicht des Baukastens, das dasselbe Pseudoelement belegt. Die farbige Pipeline-Kante gab es nicht. .kk { border } /:hover module.css setzt `border: 0`, gleich stark und spaeter im Ladeweg. Das Haus hatte die Lehre schon: aufgaben.css setzt `--ton` an fuenf Spalten mit genau dieser Begruendung. Hierher uebertragen hat es nie jemand. WAS JETZT ANDERS IST * Jede Stufe traegt ihren Ton -- Eckwinkel, Kantenlicht und Hover laufen damit von kuehl (neu entdeckt) zu gruen (uebergeben). * Die Farbkante der Karte liegt im HINTERGRUND statt auf einem Pseudoelement (beide gehoeren dem Baukasten). 6px, weil das Kantenlicht 1,6px davon verdeckt -- bei 3px blieb nichts Sichtbares. * Etappennummern 01-05, abgeleitet aus WEITER statt daneben geschrieben. "Abgelehnt" bekommt keine: keine Etappe nach vorn. * Stufentitel in der eigenen Farbe (vorher einheitlich --akzent), mit Weiss aufgehellt -- 4,3:1 waeren bei fett-versal 12,5px zu wenig. * Faellig-Saum als inset-Schatten wie bei .kachel[data-warn], mit (0,3,0) gegen die Modulliste; vorher border-color und damit wirkungslos. * Karte: Plattform als Schild, Handle in gleichbreiter Schrift, Haarlinie zwischen "wer" und "lohnt sich", Reichweite deutlicher. DAS MESSGERAET, DAS GEFEHLT HAT: module.css notiert "NICHT MITREPARIERT, weil es kein Messgeraet dafuer gibt ... Hover laesst sich nicht pruefen." Doch -- pruef-scouting-felder faehrt den Zeiger jetzt auf die Karte und liest nach (6px -> 9px). DREI FUNDE IN DEN EIGENEN WERKZEUGEN, alle von Hauswachen oder der eigenen Messung: * bild-arten hatte keine Notbremse; page.hover() blieb haengen und liess beim Abbrechen einen Server auf Port 4336 zurueck. Jetzt notbremse(240s) und mouse.move statt hover(). * color-mix(in srgb, var(--rand)) -- mit EINER Farbe ungueltig; der ganze Verlauf fiel aus, uebrig blieb Abstand ohne Linie. * Im Pruefausdruck steckte ein Backspace-Byte (0x08), das die Kommandozeile aus einer Zeichenfolge gemacht hatte. Sah im Quelltext richtig aus, traf nie. Jetzt startsWith. pruef-scouting-felder: 64 -> 72 Pruefungen, gruen. Gegenprobe gefahren (einer Stufe den Ton genommen -> rot, mit #db4b66 als Beleg). Statt des 226-Sekunden-Hauslaufs pruef-handy misst die Datei jetzt selbst auf 320px: 97 Teile, keines ueber dem Rand, kein seitliches Schieben. Dazu gruen: css-klassen, lesbarkeit, tippziele, fingermass, haus-trennung, leerzustand, deutsche-texte, zeichen (der eine Rest dort ist bereich.css und aelter). Co-Authored-By: Claude Opus 5 --- server/bild-arten.mjs | 86 ++++++++++ server/pruef-scouting-felder.mjs | 117 +++++++++++++ workspace/anleitung.html | 28 ++-- workspace/anruf-probe.html | 10 +- workspace/app.webmanifest | 8 +- workspace/assets/css/scouting.css | 268 ++++++++++++++++++++++++++---- workspace/assets/js/scouting.js | 42 ++++- workspace/aufgaben.html | 38 ++--- workspace/automation.html | 32 ++-- workspace/befinden.html | 34 ++-- workspace/bereich.html | 44 ++--- workspace/bewerben.html | 38 ++--- workspace/bewerbungen.html | 36 ++-- workspace/buehne.html | 6 +- workspace/calls.html | 34 ++-- workspace/chat.html | 40 ++--- workspace/content.html | 34 ++-- workspace/crew-index.html | 12 +- workspace/crew.webmanifest | 8 +- workspace/dateien.html | 34 ++-- workspace/entwicklung.html | 36 ++-- workspace/hilfe.html | 30 ++-- workspace/index.html | 10 +- workspace/kalender.html | 34 ++-- workspace/leistung.html | 32 ++-- workspace/manager-ziele.html | 28 ++-- workspace/material.html | 32 ++-- workspace/notizen.html | 28 ++-- workspace/personen.html | 32 ++-- workspace/profil.html | 36 ++-- workspace/reaktion.html | 38 ++--- workspace/rechte.html | 34 ++-- workspace/report.html | 36 ++-- workspace/scouting.html | 32 ++-- workspace/start.html | 38 ++--- workspace/startcheck.html | 34 ++-- workspace/steckbrief.html | 36 ++-- workspace/support.html | 28 ++-- workspace/tafel.html | 6 +- workspace/talente.html | 36 ++-- workspace/team.html | 32 ++-- workspace/teamlage.html | 34 ++-- workspace/teilen.html | 30 ++-- workspace/treff-moderation.html | 32 ++-- workspace/treff-regeln.html | 32 ++-- workspace/uebersicht.html | 32 ++-- workspace/unsere-seiten.html | 30 ++-- workspace/unterstuetzen.html | 32 ++-- workspace/werdegang.html | 32 ++-- workspace/wissen.html | 34 ++-- 50 files changed, 1164 insertions(+), 721 deletions(-) diff --git a/server/bild-arten.mjs b/server/bild-arten.mjs index b342a077..1ae11581 100644 --- a/server/bild-arten.mjs +++ b/server/bild-arten.mjs @@ -12,6 +12,19 @@ import { mkdtempSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; +/* NOTBREMSE (nachgeruestet 06.10.2026, nachdem genau das passiert ist). + + Dieses Werkzeug startet einen Server und einen Browser. Beim Versuch, + die Farbkante unter dem Zeiger zu messen, blieb `page.hover()` haengen + -- und ein Stillstand sieht von aussen genauso aus wie "laeuft noch". + Schlimmer: Nach dem Abbrechen von aussen blieb der Server auf Port + 4336 stehen und haette den naechsten Lauf blockiert. + + Dieselbe Regel wie fuer die Pruefungen, und aus demselben Grund: Eine + Frist, die ueberschritten wird, ist ein FEHLER mit Meldung -- kein + Warten ohne Ende. */ +import { notbremse } from "./helfer-notbremse.mjs"; +notbremse(240_000, "bild-arten"); const ordner = mkdtempSync(join(tmpdir(), "ws-arten-")); process.env.WORKSPACE_DB = join(ordner, "workspace.db"); @@ -104,6 +117,79 @@ for (const s of spalten) { console.log(` ${String(s.stufe).padEnd(14)} ${String(s.titel).padEnd(8)} ${String(s.zahl).padStart(2)} ` + `x=${String(s.x).padStart(4)} b=${String(s.breite).padStart(4)} Linie=${s.linie.padStart(5)} ${s.namen}`); } +/* WAS KOMMT AN DER KARTE WIRKLICH AN? (06.10.2026) + + `module.css` belegt `::before` (Kantenlicht) und `::after` (Raster, + Eckwinkel) JEDER Karte und wird NACH scouting.css geladen. Was + scouting.css auf dieselben Pseudoelemente legt, verliert damit -- + ohne dass irgendwo ein Fehler erscheint. Diese Messung sagt, welcher + Ton tatsaechlich gilt und wie breit die farbige Kante wirklich ist. */ +console.log("\n--- Was an der Karte wirklich ankommt ---"); +const kante = await seite.$eval(".kk", (k) => { + const vor = getComputedStyle(k, "::before"); + return { + ton: getComputedStyle(k).getPropertyValue("--ton").trim() || "(nicht gesetzt)", + stufenfarbe: getComputedStyle(k).getPropertyValue("--stufe-farbe").trim() || "(nicht gesetzt)", + vorBreite: vor.width, vorInhalt: vor.content, + grund: getComputedStyle(k).backgroundImage.slice(0, 60), + }; +}); +console.log(" --ton:", kante.ton, " --stufe-farbe:", kante.stufenfarbe); +console.log(" ::before Breite:", kante.vorBreite, " (3px = Kante von scouting.css, sonst ueberschrieben)"); +console.log(" Grundbild:", kante.grund); + +/* WIE BREIT IST DIE FARBKANTE WIRKLICH -- und kommt sie durch? + Gemessen wird nicht die Regel, sondern das Bild: die Farbe des + Bildpunktes zwei Pixel rechts von der linken Kartenkante, einmal in + Ruhe und einmal unter dem Zeiger. Eine Kante, die man nur im + Quelltext sieht, ist keine. */ +const kantenMass = await seite.evaluate(() => { + const k = document.querySelector(".kk"); + const s = getComputedStyle(k); + return { groesse: s.backgroundSize, lage: s.backgroundPosition, kante: s.getPropertyValue("--kante").trim() }; +}); +console.log(" background-size:", kantenMass.groesse, "| --kante:", kantenMass.kante || "(nicht gesetzt)"); +/* MIT mouse.move AUF DIE MITTE DER KARTE, NICHT MIT page.hover(). + `hover()` wartet darauf, dass das Ziel "bedienbar" ist -- sichtbar, + stabil, nicht verdeckt. Bei einer Karte, ueber der das Hintergrundbild + der Seite liegt, kann diese Pruefung dreissig Sekunden brauchen oder + nie fertig werden. Gemessen werden soll hier aber nicht die + Bedienbarkeit, sondern was der Zeiger an der Farbe aendert -- dafuer + genuegt es, den Zeiger hinzubewegen. */ +{ + const m = await seite.$eval(".kk", (k) => { + const r = k.getBoundingClientRect(); + return { x: Math.round(r.x + r.width / 2), y: Math.round(r.y + r.height / 2) }; + }); + await seite.mouse.move(m.x, m.y); + await seite.waitForTimeout(400); +} +const unterZeiger = await seite.evaluate(() => + getComputedStyle(document.querySelector(".kk")).getPropertyValue("--kante").trim()); +console.log(" --kante unter dem Zeiger:", unterZeiger || "(nicht gesetzt)"); +/* Kommt die Haarlinie zwischen Kopf und Zahlen an? Ein ungueltiger + Farbwert laesst `border-image` lautlos ausfallen -- uebrig bleibt + Abstand ohne Linie, und das sieht im Quelltext richtig aus. */ +const linie = await seite.$eval(".kk__info", (k) => { + const s2 = getComputedStyle(k); + return { quelle: s2.borderImageSource.slice(0, 48), breite: s2.borderTopWidth }; +}); +console.log(" Haarlinie in der Karte:", linie.breite, "|", linie.quelle); +/* Eine Nahaufnahme der linken Kante -- 90 px breit, damit man sie + ueberhaupt sehen kann. */ +/* Eine Karte OHNE Faellig-Saum: Bei Tamy liegt der warme Innenring + ueber der Stufenkante, und dann misst man den Ring statt der Kante. */ +const kasten = await seite.$eval(".kk:not([data-faellig])", (k) => { + const r = k.getBoundingClientRect(); + /* `width`/`height` -- nicht `w`/`h`. Playwright meldet dann + "clip.width: expected float, got undefined"; gefunden hat es die + Notbremse, nicht das Auge. */ + return { x: Math.round(r.x), y: Math.round(r.y), width: 90, height: Math.round(r.height) }; +}); +await seite.screenshot({ path: "server/bild-arten-kante.png", clip: kasten }); +await seite.mouse.move(0, 0); +await seite.waitForTimeout(300); + const stufe1 = await seite.$('.stufe[data-stufe="neu"]'); await stufe1.screenshot({ path: "server/bild-arten-rechner.png" }); await seite.screenshot({ path: "server/bild-arten-rechner-ganz.png", fullPage: false }); diff --git a/server/pruef-scouting-felder.mjs b/server/pruef-scouting-felder.mjs index f0333aca..ac4bcae8 100644 --- a/server/pruef-scouting-felder.mjs +++ b/server/pruef-scouting-felder.mjs @@ -466,6 +466,95 @@ console.log("\n=== Die Seite selbst ==="); await seite.click("#neu-zu"); await seite.waitForTimeout(250); + /* ===== DIE OPTIK DER STUFE KOMMT WIRKLICH AN (06.10.2026) ======== + + module.css notiert selbst: „`.kk:hover` in scouting.css ... setzt + border-color -- und die Grundregel oben setzt `border: 0`, gleich + stark und spaeter im Ladeweg. Diese vier Regeln tun also vermutlich + nichts." Und: „NICHT MITREPARIERT, weil es kein Messgeraet dafuer + gibt ... Hover laesst sich nicht pruefen." + + DOCH -- es laesst sich pruefen. Hier steht das Messgeraet. + + ERST DEN ZEIGER WEGSETZEN. Beim ersten Anlauf meldete diese + Messung „die Kante ist 9px statt 6" und „unter dem Zeiger waechst + sie nicht (9 -> 9)" -- beides stimmte, und beides war kein Befund + am Programm: Der Klick auf „Bearbeiten" weiter oben hatte die Maus + auf der ersten Karte stehen lassen. Gemessen wurde also zweimal der + Zustand UNTER dem Zeiger. Eine Messung, die ihren Ausgangszustand + nicht herstellt, misst den Rest des Tests mit. */ + await seite.mouse.move(2, 2); + await seite.waitForTimeout(300); + const optik = await seite.$$eval(".stufe", (gs) => gs.map((g) => { + const k = g.querySelector(".kk"); + const s2 = k ? getComputedStyle(k) : null; + return { + stufe: g.dataset.stufe, + ton: getComputedStyle(g).getPropertyValue("--ton").trim(), + farbe: getComputedStyle(g).getPropertyValue("--stufe-farbe").trim(), + kante: s2 ? s2.getPropertyValue("--kante").trim() : null, + kantenbreite: s2 ? s2.backgroundSize.split(",")[0].trim() : null, + nr: g.querySelector(".stufe__nr")?.textContent.trim() || null, + }; + })); + /* JEDE Stufe traegt IHREN Ton -- nicht den der Seite. Gezaehlt wird + in der Bedingung, nicht nur im Text: Ein `every()` auf einer leeren + Liste waere `true` und wuerde nichts beweisen. */ + ok(optik.length >= 3 && optik.every((o) => o.ton && o.ton === o.farbe), + `jede der ${optik.length} Stufen traegt ihren eigenen Ton ` + + `(${optik.map((o) => o.stufe + "=" + o.ton).join(", ")})`); + /* Und die Toene sind verschieden -- sonst waere „jede traegt ihren + eigenen" auch dann wahr, wenn alle denselben haetten. */ + ok(new Set(optik.map((o) => o.ton)).size === optik.length, + `und zwar jede eine andere (${new Set(optik.map((o) => o.ton)).size} Toene auf ${optik.length} Stufen)`); + /* `backgroundSize` liefert BEIDE Masse der ersten Ebene ("6px 100%"). + Der erste Anlauf verglich mit "6px" und meldete rot, obwohl ueberall + 6px standen -- ein Messfehler, der wie ein Befund aussah. + + DER ZWEITE ANLAUF MELDETE DASSELBE, und die Ursache war eine + andere: Beim Schreiben ueber die Kommandozeile wurde aus der + Zeichenfolge fuer eine Wortgrenze ein echtes Steuerzeichen (0x08, + Backspace) mitten im Ausdruck. Ein Ausdruck mit einem Backspace + darin trifft natuerlich nie. Im Quelltext sah die Zeile voellig + richtig aus -- sichtbar wurde das Byte erst mit `cat -v`. Genau + davor warnt pruef-zeichen ("Serverdateien mit Steuerzeichen"), und + genau sie haette es gefunden. + + Deshalb jetzt `startsWith` statt eines Ausdrucks: Daran gibt es + nichts, was beim Schreiben verstuemmelt werden koennte. */ + ok(optik.length >= 3 && optik.every((o) => (o.kantenbreite || "").startsWith("6px")), + `die Farbkante der Karte ist 6px breit (${optik.map((o) => o.kantenbreite).join(", ")})`); + /* Die Etappennummer wird aus WEITER abgeleitet: 01 bis 05, und + „abgelehnt" bekommt keine. */ + const nummern = Object.fromEntries(optik.map((o) => [o.stufe, o.nr])); + ok(nummern.neu === "01" && nummern.angesprochen === "02", + `die Stufen sind nummeriert (neu=${nummern.neu}, angesprochen=${nummern.angesprochen})`); + + /* HOVER -- gemessen mit dem Zeiger, nicht geglaubt. + + `mouse.move` und nicht `page.hover()`: Letzteres wartet auf + „bedienbar" und blieb beim ersten Anlauf haengen. Gemessen werden + soll hier die Farbe, nicht die Bedienbarkeit. */ + const vorher = await seite.$eval(".kk", (k) => getComputedStyle(k).getPropertyValue("--kante").trim()); + const mitte = await seite.$eval(".kk", (k) => { + const r = k.getBoundingClientRect(); + return { x: Math.round(r.x + r.width / 2), y: Math.round(r.y + r.height / 2) }; + }); + await seite.mouse.move(mitte.x, mitte.y); + await seite.waitForTimeout(350); + const nachher = await seite.$eval(".kk", (k) => getComputedStyle(k).getPropertyValue("--kante").trim()); + ok(vorher === "6px" && nachher === "9px", + `unter dem Zeiger waechst die Kante (${vorher} -> ${nachher})`); + await seite.mouse.move(0, 0); + await seite.waitForTimeout(200); + + /* Die Haarlinie in der Karte. Ein ungueltiger Farbwert laesst + `border-image` lautlos ausfallen -- „none" heisst, sie fehlt. */ + const haar = await seite.$eval(".kk__info", + (k) => getComputedStyle(k).borderImageSource); + ok(haar.includes("gradient"), + `die Haarlinie zwischen Kopf und Zahlen ist da (${haar.slice(0, 28)}…)`); + /* ===== GEGENPROBE: AUF DEM HANDY LIEGEN SIE UNTEREINANDER ======== Ohne diese Messung wuesste niemand, ob die Trennung ueberhaupt @@ -500,6 +589,34 @@ console.log("\n=== Die Seite selbst ==="); ok(schmal.every((k) => k.breite >= 300), `keine Spalte wird gequetscht (${schmal.map((k) => k.breite).join(" / ")} px)`); + /* ===== UND AUF DEM SCHMALSTEN GERAET, DAS ES GIBT (320 px) ======= + + Die Karte hat links 26px Polster bekommen (davor 21), dazu eine + 6px-Kante. Was auf 390px passt, muss auf 320px nicht passen -- und + ein Element, das ueber den Rand steht, erzeugt seitliches Scrollen + auf der ganzen Seite. + + GEMESSEN STATT DEN HAUSLAUF GESTARTET: `pruef-handy` prueft 186 + Dinge auf vielen Seiten und braucht 226 Sekunden. Die Frage hier + ist eine einzige und in vier Sekunden beantwortet. */ + const engKontext = await browser.newContext({ + viewport: { width: 320, height: 720 }, hasTouch: true, isMobile: true, + }); + await engKontext.addCookies(await ctx.cookies()); + const eng = await engKontext.newPage(); + await eng.goto(seite.url(), { waitUntil: "networkidle" }); + await eng.waitForTimeout(900); + const ueber = await eng.$$eval(".stufe *", (ks) => ks + .filter((k) => k.getBoundingClientRect().right > window.innerWidth + 1) + .slice(0, 4) + .map((k) => `${k.className || k.tagName} bis ${Math.round(k.getBoundingClientRect().right)}px`)); + const gemessen = await eng.$$eval(".stufe *", (ks) => ks.length); + ok(gemessen > 20 && ueber.length === 0, + `auf 320px steht keines der ${gemessen} Teile ueber den Rand (${ueber.join(", ") || "keins"})`); + const quer = await eng.evaluate(() => + document.documentElement.scrollWidth - document.documentElement.clientWidth); + ok(quer <= 1, `und die Seite laesst sich nicht seitlich schieben (${quer} px)`); + await browser.close(); } diff --git a/workspace/anleitung.html b/workspace/anleitung.html index 292be482..d5012fd1 100644 --- a/workspace/anleitung.html +++ b/workspace/anleitung.html @@ -7,17 +7,17 @@ - - - - - + + + + + - - + + @@ -289,16 +289,16 @@ Und anleitung.js braucht es doppelt: Symbol, Farbe, Name und Adresse jeder Karte kommen von dort, nicht vom Server. --> - + - - - - - - + + + + + + diff --git a/workspace/anruf-probe.html b/workspace/anruf-probe.html index 42710598..2a7038f9 100644 --- a/workspace/anruf-probe.html +++ b/workspace/anruf-probe.html @@ -16,8 +16,8 @@ verdeckt. Gefunden hat es pruef-struktur. Richtig sind `gate.css` (die Farbwerte des Hauses) und `haus.css`. --> - - + + - +