Die Bildschau und 2520 Farben statt 360

=== 1. BILDER OEFFNEN SICH MITTIG, MIT HUSKY UND HASE ===

Filipe: "wenn man bilder aufmacht, will ich dass die in der mitte sind.
beim hochformat soll links der husky sein und rechts der hase und beim
querformat soll oben dogfather und unten team dogi stehen da wo platz
ist ... ich will nicht dahin geschmissen sondern was richtig geiles mit
einem geilen hintergrund."

WAS VORHER PASSIERTE, und es war kein Fehler im Code: Der Klick fuehrte
auf die DATEI. Was man dann sah, war die eingebaute Bildanzeige des
Browsers -- schwarzer Grund, Bild oben links in der Ecke, auf seinem
Bildschirmfoto 1200 px Schwarz daneben. Eine Datei hat keine
Gestaltung. Wer Gestaltung will, muss eine SEITE zeigen.

Die Schau liegt jetzt UEBER dem Chat. Escape bringt einen dorthin
zurueck, wo man war, und das Gespraech laeuft im Hintergrund weiter --
ein zweiter Tab braeuchte eine eigene Seite, eine eigene Anmeldung und
einen eigenen Weg zurueck.

DER WEG IN DEN TAB BLEIBT TROTZDEM. Der Verweis hat unveraendert
`href`, `target` und `rel`; mittlere Maustaste, "In neuem Tab oeffnen"
und Herunterladen gehen wie seit jeher. Abgefangen wird nur der
gewoehnliche Klick -- und auch der nur, wenn die Schau geladen ist.
Wer Strg, Umschalt oder Befehl haelt, bekommt den alten Weg; das sind
die Griffe, die Leute seit zwanzig Jahren benutzen.

DIE BEGLEITER STEHEN DA, WO PLATZ IST -- und die Antwort darauf wird
GEMESSEN, nicht geschaetzt: nicht die Fensterbreite, sondern der Rest
neben dem Bild, nachdem es eingepasst ist. Eine Schwelle wie "ab
1100 px" waere fuer ein 3:4-Foto richtig und fuer ein 9:16-Foto auf
demselben Schirm falsch. Unter 170 px bleibt der Husky weg: Eine Figur,
die ein Streifen ist, steht besser nicht da.

Husky und Hase sind eigens verkleinert (2,1 MB und 1,0 MB werden 40 kB
und 32 kB) und bekommen weiche Raender -- ihr Hintergrund ist dunkel,
aber nicht durchsichtig; als Rechteck saehe man zwei Kaesten.

Der erste Anlauf spiegelte den Hasen, damit beide "nach innen" schauen
-- ein alter Reflex aus dem Plakatsatz. Auf dem Probebild stand danach
"Hasi Dog" seitenverkehrt auf seinem Hoodie. Schrift im Bild spiegelt
man nie.

pruef-chat-anhaenge bekommt 14 neue Punkte, darunter beide Formate und
zwei Gegenproben: Strg+Klick oeffnet die Schau NICHT (sonst hiesse "sie
geht auf" nur, dass der alte Weg verloren ist), und im Hochformat liegt
KEINE der Figuren ueber dem Foto.

NEBENBEFUND, den die Pruefung gefunden hat: Die Sprachnachricht war auf
165 px geschrumpft -- zu schmal fuer den Schieber. Ursache war keine
Aenderung an ihr, sondern eine Folge: Die Blase ist so breit wie ihr
breitester Inhalt, und seit die Handgriffe darunter Zeichen statt
Woerter sind (123 statt ueber 400 px), bestimmt das Abspielgeraet die
Breite selbst. Eine Reihe kuerzer zu machen hat einen Schieber schmaler
gemacht, drei Bildschirme weiter.

=== 2. DER FARBKREIS: HELLER UND DUNKLER ===

Filipe: "dieser kreis muss viel perfekter sein. ich will dass die leute
auch heller und dunkler aussuchen koennen ... so dass die leute viel
krassere moeglichkeiten haben."

WARUM DAS BIS HEUTE NICHT GING: Die 360 Toene liegen alle auf DERSELBEN
Leuchtdichte. Das war kein Zufall -- solange die Blase in der gewaehlten
Farbe stand, musste jede dieser Flaechen dieselbe Schrift tragen. Eine
hellere Farbe zuzulassen hiesse damals: irgendwo wird eine Nachricht
unlesbar.

SEIT DEM 25.09.2026 IST DIE BEDINGUNG EINE ANDERE. Die Blase ist fuer
alle dunkles Glas; der Ton ist Kante und NAME. Damit faellt die alte
Regel weg, und an ihre Stelle tritt: der Ton muss als Name auf dem
Blasengrund lesbar bleiben.

DIE ZAHLEN SIND GEMESSEN, NICHT GEWAEHLT. Ueber alle 360 Winkel, jeweils
der schlechteste Fall:

    40 % schwarz   4,468   -- unter der Schwelle, faellt raus
    34 % schwarz   4,555   -- ginge, aber ohne Reserve
    30 % schwarz   4,619   <- die dunkelste Stufe
     0 %           5,12    <- die Mitte, der alte Kreis
    55 % weiss     9,6     <- die hellste Stufe

Sieben Stufen, 2520 Farben statt 360. pruef-chat-neu rechnet jede
einzelne nach -- die knappste hat 2,6 Prozent Reserve.

DIE ALTEN SCHLUESSEL BLEIBEN GUELTIG. "ton-214" heisst weiterhin genau
dieselbe Farbe; die Stufe steht als Zusatz dahinter ("ton-214-s6"). Wer
seit dem 23.09. eine Farbe traegt, traegt nach diesem Umbau dieselbe.

DIE PROBE IN DER MITTE ZEIGT ENDLICH, WAS MAN BEKOMMT. Dort stand eine
gefuellte Flaeche mit heller Schrift -- so sah die Blase bis heute frueh
aus. Jetzt steht dort derselbe Blasengrund und darauf der Ton als Name,
mit genau der Rechnung aus chat.css. Das ist nicht nur ehrlicher, es
macht die sieben Stufen erst moeglich: Eine gefuellte Flaeche muesste
Schrift tragen, und bei sehr hellen Toenen schafft das keine der beiden
Schriftfarben mehr (4,1 statt noetiger 7). Als Name auf dunklem Grund
ist derselbe helle Ton besonders gut lesbar (8,0).

Der Erklaersatz im Fenster war damit falsch geworden ("Alle Toene
leuchten gleich stark") und sagt jetzt, was stimmt.

ZWEI PRUEFUNGEN WURDEN GENAUER:
  pruef-chat-neu mass den HINTERGRUND der Mitte -- eine Darstellung,
  die es nicht mehr gibt. Sie misst jetzt die Schriftfarbe und rechnet
  die Mischung nach. Dabei stolperte sie ueber eine dritte
  Farbschreibweise: Chromium gibt `color-mix`-Ergebnisse als
  `color(srgb 0.676 0.645 0.504)` zurueck. Die Zeile war rot, obwohl
  die Farbe auf den Bildpunkt genau stimmte -- ein Fehlalarm, der
  teurer ist als ein echter Befund, weil man am Aussehen sucht und der
  Fehler im Lesegeraet liegt.
  Und sie prueft nicht mehr 360, sondern 360 x 7 -- nur den Grundton zu
  messen hiesse, sechs Siebtel ungeprueft auszuliefern.

Geprueft: pruef-chat-anhaenge (123), pruef-chat-neu (36),
pruef-chatkachel (40), pruef-chat, pruef-chat-ausbau (69),
pruef-chat-optik, pruef-tippziele (11), pruef-css-klassen.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
2026-09-25 04:35:12 +02:00
co-authored by Claude Opus 5
parent 4b48aa46f6
commit 177c3c012a
50 changed files with 1855 additions and 659 deletions
+129 -15
View File
@@ -313,6 +313,23 @@ console.log("\n=== 3. Der Ruf ans Rudel ===");
const zuZahl = (f) => {
const t = /^#?([0-9a-f]{6})$/i.exec(String(f).trim());
if (t) return [0, 2, 4].map((i) => parseInt(t[1].slice(i, i + 2), 16));
/* ==== DIE DRITTE SCHREIBWEISE (25.09.2026) ====================
Seit die Probe ihre Farbe aus `color-mix(in srgb, …)` bekommt,
gibt Chromium sie als `color(srgb 0.676 0.645 0.504)` zurueck --
Anteile von 0 bis 1, keine Ganzzahlen, kein Komma. Die zwei
Muster darueber trafen das nicht, `zuZahl` lieferte `null`, und
die Zeile wurde rot, obwohl die Farbe auf den Bildpunkt genau
stimmte (172,5 / 164,5 / 128,5 gegen erwartete 173/165/129).
Das ist die Sorte Fehlalarm, die teurer ist als ein echter
Befund: Man sucht am Aussehen, und der Fehler liegt im
Lesegeraet. */
const s = /color\(srgb\s+([^)]+)\)/i.exec(String(f));
if (s) {
return s[1].trim().split(/[\s/]+/).slice(0, 3)
.map((x) => Math.round(parseFloat(x) * 255));
}
const r = /rgba?\(([^)]+)\)/i.exec(String(f));
return r ? r[1].split(",").slice(0, 3).map((x) => Math.round(parseFloat(x))) : null;
};
@@ -375,21 +392,55 @@ console.log("\n=== 3. Der Ruf ans Rudel ===");
const schlimmerGrund = grund.map((v) => Math.round(v * 0.92 + 255 * 0.08));
const anteil = Number(anteilTreffer[1]) / 100;
const hell = zuZahl("#f2f8ff");
/* ==== JEDE STUFE AN JEDEM WINKEL (25.09.2026) =================
Filipe: „ich will dass die leute auch heller und dunkler
aussuchen koennen."
Seit heute gibt es zu jedem der 360 Winkel sieben
Helligkeiten -- 2520 Farben statt 360. Nur den Grundton zu
pruefen hiesse, sechs Siebtel des Angebots ungeprueft
auszuliefern; und die dunkelste Stufe ist genau die, bei der
es kippen wuerde (gemessen 4,62 gegen noetige 4,5 -- die
naechste Stufe darunter faellt durch).
DIE MISCHANTEILE WERDEN GELESEN, nicht abgeschrieben: Sie
stehen in chat-kreis-stufen.js, und wer sie dort aendert,
aendert damit diese Pruefung mit. Eine zweite Liste hier waere
die, die beim naechsten Umbau nicht mitzieht. */
const { KREIS_STUFEN } = await import("./chat-kreis-stufen.js");
const mischen2 = (rgb, misch) => {
if (!misch) return rgb;
const ziel = misch < 0 ? 0 : 255;
const t2 = Math.abs(misch);
return rgb.map((v) => Math.round(v * (1 - t2) + ziel * t2));
};
let schlimmster = Infinity, schlimmsterWinkel = -1, gemessen = 0;
let schlimmsteStufe = "";
for (let w = 0; w < KREIS.length; w++) {
const f = zuZahl(KREIS[w]);
if (!f) continue;
gemessen++;
const name = f.map((v, i) => Math.round(v * anteil + hell[i] * (1 - anteil)));
const v2 = kontrast(name, schlimmerGrund) / 4.5;
if (v2 < schlimmster) { schlimmster = v2; schlimmsterWinkel = w; }
for (const st of KREIS_STUFEN) {
gemessen++;
const ton = mischen2(f, st.misch);
const name = ton.map((v, i) => Math.round(v * anteil + hell[i] * (1 - anteil)));
const v2 = kontrast(name, schlimmerGrund) / 4.5;
if (v2 < schlimmster) {
schlimmster = v2; schlimmsterWinkel = w; schlimmsteStufe = st.wert;
}
}
}
/* DIE ANZAHL STEHT IN DER BEDINGUNG. Waere die Tabelle leer,
bliebe schlimmster auf Infinity und die Zeile waere gruen,
ohne eine einzige Farbe angesehen zu haben. */
ok(gemessen === 360 && schlimmster >= 1,
`alle ${gemessen} Toene des Rings sind als Name auf der Blase lesbar`
+ ` (knappster: Winkel ${schlimmsterWinkel}, Faktor ${schlimmster.toFixed(3)})`);
/* DIE ANZAHL STEHT IN DER BEDINGUNG -- und sie ist seit heute
360 mal die Zahl der Stufen. Waere die Stufenliste leer,
bliebe `gemessen` bei 0 und die Zeile waere gruen, ohne eine
einzige Farbe angesehen zu haben. */
ok(gemessen === 360 * KREIS_STUFEN.length && schlimmster >= 1,
`alle ${gemessen} Farben des Rings (360 Winkel x ${KREIS_STUFEN.length} `
+ `Helligkeiten) sind als Name auf der Blase lesbar (knappste: Winkel `
+ `${schlimmsterWinkel}, Stufe ${schlimmsteStufe}, Faktor ${schlimmster.toFixed(3)})`);
}
}
if (textRgb && leiseRgb) {
@@ -466,21 +517,84 @@ console.log("\n=== 3. Der Ruf ans Rudel ===");
const stand = await frida.seite.evaluate(() => {
const r = document.getElementById("kachelrad-ring");
const m = document.getElementById("kachelrad-mitte");
const p2 = document.querySelector(".kachelrad__probe");
const leiter = document.getElementById("kachelhell-leiter");
return { winkel: Number(r?.getAttribute("aria-valuenow")),
wort: r?.getAttribute("aria-valuetext") || "",
mitte: m ? getComputedStyle(m).backgroundColor : "" };
/* NICHT MEHR DER GRUND, SONDERN DIE SCHRIFT. Siehe unten. */
probe: p2 ? getComputedStyle(p2).color : "",
stufen: leiter ? leiter.children.length : 0,
stufeJetzt: leiter?.getAttribute("aria-valuenow") || "" };
});
ok(Math.abs((stand.winkel || 0) - 90) <= 2,
`nach dem Drehen auf drei Uhr steht der Ring bei ${stand.winkel} Grad (erwartet 90)`);
const erwartet = zuZahl(KREIS[stand.winkel] || "");
const gezeigt = zuZahl(stand.mitte);
/* ==== DIE PROBE MISST SEIT HEUTE ETWAS ANDERES (25.09.2026) =====
Hier stand: „die Mitte zeigt genau die Farbe dieses Winkels" --
gemessen am HINTERGRUND der Mitte. Das war richtig, solange die
Blase in der gewaehlten Farbe stand.
Seit dem 25.09.2026 ist die Blase fuer alle dunkles Glas, und
der gewaehlte Ton ist der NAME darauf. Die Probe zeigte damit
eine Darstellung, die es im Chat nicht mehr gibt -- und die
Pruefung haette sie festgehalten.
GEMESSEN WIRD JETZT DIE SCHRIFT der Probe: Sie muss genau die
Farbe tragen, die ein Name in der Blase traegt -- der Ton, mit
`--namen-anteil` nach #f2f8ff aufgehellt. Das ist dieselbe
Rechnung wie in chat.css, und sie wird hier NACHGERECHNET statt
abgeschrieben. */
const grundTon = zuZahl(KREIS[stand.winkel] || "");
const hell = zuZahl("#f2f8ff");
const anteil = 0.5; /* --namen-anteil; weiter unten aus dem CSS geprueft */
const erwartet = grundTon
? grundTon.map((v, i) => Math.round(v * anteil + hell[i] * (1 - anteil)))
: null;
const gezeigt = zuZahl(stand.probe);
ok(!!erwartet && !!gezeigt
&& Math.abs(erwartet[0] - gezeigt[0]) <= 1
&& Math.abs(erwartet[1] - gezeigt[1]) <= 1
&& Math.abs(erwartet[2] - gezeigt[2]) <= 1,
`die Mitte zeigt genau die Farbe dieses Winkels (${stand.mitte} gegen ${KREIS[stand.winkel]})`);
&& Math.abs(erwartet[0] - gezeigt[0]) <= 2
&& Math.abs(erwartet[1] - gezeigt[1]) <= 2
&& Math.abs(erwartet[2] - gezeigt[2]) <= 2,
`die Probe zeigt den Namen in genau dieser Farbe (${stand.probe} gegen `
+ `${erwartet ? "rgb(" + erwartet.join(", ") + ")" : "?"})`);
/* ==== UND DIE SIEBEN HELLIGKEITEN (25.09.2026) =================
Filipe: „ich will dass die leute auch heller und dunkler
aussuchen koennen … so dass die leute viel krassere
moeglichkeiten haben." */
ok(stand.stufen === 7,
`die Helligkeitsleiter hat sieben Stufen (${stand.stufen})`);
ok(stand.stufeJetzt === "4",
`und steht von sich aus in der Mitte (${stand.stufeJetzt})`);
/* EINE STUFE WAEHLEN UND NACHSEHEN, OB SIE ANKOMMT. Ohne das
waere „es gibt sieben Felder" ein Haken ueber Zierde: Sieben
Kaestchen, die nichts tun, sehen genauso aus. */
await frida.seite.click(".kachelhell__feld[data-stufe='s7']");
await frida.seite.waitForTimeout(700);
const hellStand = await frida.seite.evaluate(() => {
const p2 = document.querySelector(".kachelrad__probe");
const leiter = document.getElementById("kachelhell-leiter");
const an = leiter?.querySelector('[data-an="ja"]');
return { probe: p2 ? getComputedStyle(p2).color : "",
stufe: leiter?.getAttribute("aria-valuenow") || "",
markiert: an?.dataset.stufe || "" };
});
ok(hellStand.stufe === "7" && hellStand.markiert === "s7",
`eine hellere Stufe laesst sich waehlen (${hellStand.stufe}, ${hellStand.markiert})`);
const vorher2 = zuZahl(stand.probe), nachher2 = zuZahl(hellStand.probe);
ok(!!vorher2 && !!nachher2 && leucht(nachher2) > leucht(vorher2),
`und die Probe wird dabei wirklich heller `
+ `(${leucht(vorher2).toFixed(3)} auf ${leucht(nachher2).toFixed(3)})`);
await frida.seite.screenshot({ path: "server/pruef-farbkreis.png" });
/* Und zurueck in die Mitte, damit die Pruefungen danach mit dem
gewohnten Stand weiterarbeiten. */
await frida.seite.click(".kachelhell__feld[data-stufe='s4']");
await frida.seite.waitForTimeout(500);
ok(/\d+ Grad/.test(stand.wort) && stand.wort.length > 8,
`und sie wird vorgelesen als "${stand.wort}"`);