screen1 -- HIGHLIGHTS AUF ANSCHLAGBRETTS PLATZ
Kein Tausch, sondern ein Aufruecken: Highlights nimmt die Stelle,
alle anderen wandern eine weiter. Inhaltlich stimmt das auch --
was die Leute selbst gemacht haben, steht vor dem, was ihnen
angesagt wird.
screen2 -- DIE CHATFARBEN SIND GERECHNET, NICHT GEGRIFFEN
Gemessen, was drinstand: Buntheit 0,002 bis 0,048, im Mittel 0,028
-- auf dem Bildschirm zwoelf Grautoene. Das lag nicht an fehlendem
Mut, sondern an der Fusszeile der Blase: Sie stand auf der leisen
Hausschrift, und gegen die darf die Flaeche kaum Farbe haben.
Deshalb ZUERST die Schrift (--blase-text/--blase-leise, eigene
Farben der Blase), DANN die Flaeche. Andersherum waere es der
Fehler vom selben Vormittag gewesen, als 27 von 31 Startkacheln
unlesbar wurden.
tools/chat-kacheln-rechnen.mjs liest beide Schriftfarben aus dem
CSS und rechnet daraus die hellste Flaeche, die sie noch tragen.
Ergebnis: Buntheit 0,072 bis 0,268, alle zwoelf auf DERSELBEN
Leuchtdichte -- also exakt demselben Kontrast (7,02 bis 7,13:1).
Zwei Anlaeufe waren falsch und stehen im Werkzeug begruendet:
- hoechste Buntheit nehmen, dann Kontrast pruefen. Musste
scheitern: gesaettigtes Gelb ist bei gleicher empfundener
Helligkeit viel heller als Blau.
- gleiche OKLab-Helligkeit statt gleicher Leuchtdichte. Das
Versprechen der zwoelf lautet "ueberall gleich gut lesbar",
und Lesbarkeit haengt an der Leuchtdichte, nicht am Eindruck.
WER NICHTS EINSTELLT, BEKOMMT TROTZDEM EINE FARBE. Gemessen an der
Live-Datenbank hatten vier von fuenfzehn eine gewaehlt -- der Chat
war fuer alle anderen einfarbig. Jetzt vergibt der Server einen der
elf bunten Toene aus der Personennummer, stabil. Schrittweite 4:
elf ist prim, also laufen alle Toene durch, und vier Schritte sind
131 Grad im Farbkreis -- aufeinanderfolgende Nummern landen so weit
auseinander wie moeglich. Mit id % 11 sassen zwei Nachbarn im
selben Gespraech magenta und rot nebeneinander.
Niemand verliert seine Wahl: veilchen, ziegel und moos sind die
drei gewaehlten und behalten ihren Farbbereich.
Dazu: Blasen mit 22px runden Kanten (nur die Ecke zur Person bleibt
spitz -- sie sagt, wer spricht), Lichtsaum oben innen, weicher
Schatten. Der Name hell und fett mit Rollenpunkt davor; er stand
auf leisem Grau mit 85 Prozent Deckung und war der schwaechste Text
der Seite, ausgerechnet der, der sagt, wer spricht.
ZWEI REGELBLOECKE FUER DIESELBE BLASE ZUSAMMENGELEGT. Der untere
gewann und hat an einem Tag zweimal Schaden angerichtet: Er stellte
die runden Kanten auf 16px zurueck (die Aenderung waere wirkungslos
ausgeliefert worden), und er mischte 26 Prozent Akzentfarbe in die
eigene Blase -- die hatte damit eine Farbe, die in CHAT_KACHELN
nicht vorkommt und die die Kontrastpruefung nie angesehen hat.
screen3 -- ALLE FARBEN DES HAUSES IN EINER KACHEL
tools/kachel-regenbogen.mjs leitet die 31 benutzten Toene ab
(bereicheFuer/zusatzBereicheFuer ueber alle Rollen und beide
Haeuser) und schreibt daraus einen Streifenverlauf mit harten
Kanten -- "nicht gemischt" war die eigentliche Ansage. Ein Verlauf
ergaebe Zwischentoene, die es im Haus nicht gibt.
Gegen einen deckenden Grund gemischt, nicht gegen --flaeche: Die
ist halbdurchsichtig, und durch 31 schmale Baender schien das
Buehnenbild -- sie verloren genau das, wofuer sie da sind.
Der Lesesaum ist staerker als auf den anderen Kacheln, und zwar
gemessen: Mit deren Werten kam der Text auf 4,45:1, fuenf
Hundertstel unter der Grenze. Gefunden hat das pruef-kachelfarben,
nicht der Blick -- 4,45 gegen 4,50 sieht man nicht.
pruef-buehne -- DIE ZAHL GEHOERT IN DIE BEDINGUNG
Die Abtastung wurde nachsichtiger (fremde Flaechen zaehlen nicht
mehr als Untergrund eines Textes). Das war richtig und hat einen
Fehlalarm beseitigt, der seit Tagen kam. Eine Lockerung kann aber
zu weit gehen, deshalb muss jetzt jeder gefundene Text in genau
einem von drei Toepfen landen: gemessen, ohne freien Untergrund,
oder aussortiert. Geht die Rechnung nicht auf, ist unterwegs etwas
still verschwunden.
Erster Anlauf war "mindestens 5 Stellen" -- und wurde auf
uebersicht.html sofort rot, weil die Seite nur vier Texte hat.
Eine feste Schwelle ist eine Rechnung von gestern; diese war keine
fuenf Minuten alt.
Gemessen: pruef-kachelfarben 22/0 (war 18, neu: die Willkommenskachel
traegt wirklich alle benutzten Farben, in beide Richtungen geprueft),
pruef-buehne 192/0, pruef-chatkachel 32/0, pruef-chat-optik 45/0,
pruef-chat 48/0, pruef-erwaehnung 69/0, pruef-start-ansicht 153/0,
pruef-treff 80/0, pruef-willkommen 67/0, pruef-treffchat 110/0,
pruef-css-klassen 30/0.
Co-Authored-By: Claude Opus 5 <[email protected]>
391 lines
18 KiB
JavaScript
391 lines
18 KiB
JavaScript
/* =====================================================================
|
||
DIE ZWÖLF CHAT-KACHELN NEU RECHNEN (22.09.2026)
|
||
---------------------------------------------------------------------
|
||
Filipe: „ich will dass die farben viel klarer und stärker sind und
|
||
die schrift vom chat soll dan auch angepasst werden ... die kachel
|
||
von den texten von den leuten im chat soll auch klarer und stärker
|
||
sein bitte."
|
||
|
||
WAS VORHER DA WAR UND WARUM ES SO BLASS WAR
|
||
Die zwölf Töne lagen bei einer Helligkeit von rund 1 % (#131a25 und
|
||
Verwandte). Auf dem Bildschirm sind das zwölf Grautöne mit einem
|
||
Hauch Farbe — genau das, was auf Filipes Bildschirmfoto zu sehen ist.
|
||
|
||
Der Grund dafür war richtig gedacht: Die Fußzeile der Blase (Uhrzeit,
|
||
„antworten", „kopieren") steht auf var(--text-leise) — einem leisen
|
||
Grau. Gegen eine kräftige Fläche wäre die unlesbar geworden. Die
|
||
dunkle Kachel war also keine Geschmacksentscheidung, sondern die
|
||
Folge einer zu leisen Schrift.
|
||
|
||
DESHALB WIRD BEIDES ZUSAMMEN GERECHNET, nicht nacheinander. Die
|
||
Blase bekommt ihre EIGENE Schriftfarbe (--blase-text/--blase-leise,
|
||
heller als die der Seite), und dieses Werkzeug rechnet aus, wie
|
||
kräftig die Fläche dann werden DARF.
|
||
|
||
Eine Farbe erst stärker zu machen und hinterher zu messen, ob die
|
||
Schrift noch geht, ist genau die Reihenfolge, mit der am selben Tag
|
||
schon einmal 27 von 31 Startkacheln unlesbar geworden sind.
|
||
|
||
DAS VERFAHREN
|
||
1. Die Schriftfarben werden aus dem CSS GELESEN, nicht hier
|
||
abgeschrieben. Eine zweite Liste wäre die, die beim nächsten
|
||
Farbwechsel vergessen wird.
|
||
2. Aus dem nötigen Kontrast und der dunkelsten dieser Schriften
|
||
folgt GENAU EINE Leuchtdichte — die hellste Fläche, die beide
|
||
Schriften noch tragen. Sie wird ausgerechnet, nicht gesucht.
|
||
3. Auf dieser einen Leuchtdichte bekommt jeder der elf Farbtöne die
|
||
höchste Buntheit, die der Bildschirm hergibt.
|
||
|
||
WARUM ALLE AUF DERSELBEN LEUCHTDICHTE LIEGEN: Die Kachel gehört
|
||
einem selbst, GESEHEN wird sie von allen anderen. Gleiche
|
||
Leuchtdichte heißt, dass der Kontrast bei jedem der zwölf derselbe
|
||
ist — niemand kann sich durch seine Wahl schlechter lesbar machen
|
||
als die anderen.
|
||
|
||
Nicht dieselbe empfundene Helligkeit, sondern dieselbe Leuchtdichte:
|
||
Das ist die Größe, an der Lesbarkeit hängt. Der erste Anlauf hat die
|
||
andere genommen und deshalb behauptet, es gebe gar keine Lösung.
|
||
|
||
AUFRUF
|
||
node tools/chat-kacheln-rechnen.mjs nur zeigen
|
||
node tools/chat-kacheln-rechnen.mjs --schreiben eintragen
|
||
===================================================================== */
|
||
|
||
import { readFileSync, writeFileSync } from "node:fs";
|
||
import { fileURLToPath } from "node:url";
|
||
import { dirname, join } from "node:path";
|
||
|
||
const HIER = dirname(fileURLToPath(import.meta.url));
|
||
const W = (...t) => join(HIER, "..", ...t);
|
||
|
||
/* ---- Farbrechnen ---------------------------------------------------- */
|
||
const zuLinear = (v) => (v <= 0.04045 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4);
|
||
const ausLinear = (v) => (v <= 0.0031308 ? v * 12.92 : 1.055 * v ** (1 / 2.4) - 0.055);
|
||
|
||
function helligkeit([r, g, b]) {
|
||
return 0.2126 * zuLinear(r / 255) + 0.7152 * zuLinear(g / 255) + 0.0722 * zuLinear(b / 255);
|
||
}
|
||
function kontrast(a, b) {
|
||
const ha = helligkeit(a), hb = helligkeit(b);
|
||
const [h, d] = ha > hb ? [ha, hb] : [hb, ha];
|
||
return (h + 0.05) / (d + 0.05);
|
||
}
|
||
function ausOklch(L, C, hGrad) {
|
||
const h = (hGrad * Math.PI) / 180;
|
||
const a = C * Math.cos(h), bb = C * Math.sin(h);
|
||
const l_ = (L + 0.3963377774 * a + 0.2158037573 * bb) ** 3;
|
||
const m_ = (L - 0.1055613458 * a - 0.0638541728 * bb) ** 3;
|
||
const s_ = (L - 0.0894841775 * a - 1.2914855480 * bb) ** 3;
|
||
const r = 4.0767416621 * l_ - 3.3077115913 * m_ + 0.2309699292 * s_;
|
||
const g = -1.2684380046 * l_ + 2.6097574011 * m_ - 0.3413193965 * s_;
|
||
const b2 = -0.0041960863 * l_ - 0.7034186147 * m_ + 1.7076147010 * s_;
|
||
return [r, g, b2];
|
||
}
|
||
/** Lässt der Bildschirm diese Farbe überhaupt zu? */
|
||
function imRaum(L, C, h) {
|
||
const [r, g, b] = ausOklch(L, C, h);
|
||
return r >= -1e-4 && g >= -1e-4 && b >= -1e-4
|
||
&& r <= 1.0001 && g <= 1.0001 && b <= 1.0001;
|
||
}
|
||
function zuRgb(L, C, h) {
|
||
return ausOklch(L, C, h)
|
||
.map((v) => Math.max(0, Math.min(255, Math.round(ausLinear(v) * 255))));
|
||
}
|
||
const hex = (rgb) => "#" + rgb.map((x) => x.toString(16).padStart(2, "0")).join("");
|
||
function ausHex(t) {
|
||
const m = t.replace("#", "");
|
||
const v = m.length === 3
|
||
? [...m].map((c) => c + c)
|
||
: [m.slice(0, 2), m.slice(2, 4), m.slice(4, 6)];
|
||
return v.map((x) => parseInt(x, 16));
|
||
}
|
||
|
||
/* ---- Die Schriftfarben AUS DEM CSS holen ---------------------------- */
|
||
/* Drei Dateien, weil die Blase in beiden Häusern vorkommt und das
|
||
Crew-Haus eigene Farben setzt. Genommen wird je Rolle die DUNKELSTE
|
||
der gefundenen — die bestimmt den schlechtesten Fall, und nur der
|
||
zählt. */
|
||
function farbenLesen() {
|
||
const dateien = ["chat.css", "crew-haus.css", "gate.css"];
|
||
const gefunden = new Map();
|
||
for (const d of dateien) {
|
||
const t = readFileSync(W("workspace", "assets", "css", d), "utf8");
|
||
for (const m of t.matchAll(/--([a-z0-9-]+):\s*(#[0-9a-fA-F]{3,6})\s*;/g)) {
|
||
if (!gefunden.has(m[1])) gefunden.set(m[1], []);
|
||
gefunden.get(m[1]).push(ausHex(m[2]));
|
||
}
|
||
}
|
||
return (name) => {
|
||
const l = gefunden.get(name);
|
||
if (!l || !l.length) return null;
|
||
return l.slice().sort((a, b) => helligkeit(a) - helligkeit(b))[0];
|
||
};
|
||
}
|
||
const dunkelste = farbenLesen();
|
||
|
||
/* WELCHE SCHRIFT STEHT AUF EINER BLASE, UND WAS BRAUCHT SIE.
|
||
---------------------------------------------------------------------
|
||
JEDE SCHRIFT HAT IHREN EIGENEN ANSPRUCH — eine gemeinsame Zahl war
|
||
hier falsch.
|
||
|
||
Erster Anlauf verlangte für beide 4,8:1. Das Ergebnis erreichte für
|
||
den Nachrichtentext 6,86:1 — und riss damit die 7:1, auf die
|
||
pruef-chatkachel seit dem 20.09. besteht. Die Begründung dort steht
|
||
und gilt: Ein Chat ist die Seite, auf der am längsten gelesen wird,
|
||
und AAA ist hier nicht zu viel verlangt.
|
||
|
||
Die Fußzeile ist kein Fließtext, sondern vier Handgriffe (Uhrzeit,
|
||
antworten, kopieren, anheften). Für sie gelten die 4,5:1 der Norm,
|
||
plus Spielraum auf 4,8 — ein Wert genau auf der Grenze wird beim
|
||
nächsten Rundungsschritt rot.
|
||
|
||
Bindend ist am Ende die STRENGERE der beiden, bezogen auf ihre
|
||
jeweilige Farbe. Welche das ist, rechnet das Werkzeug aus; es steht
|
||
hier bewusst nicht als Zahl. */
|
||
const SCHRIFTEN = [
|
||
["blase-text", ".chat-nachricht__text", 7.0],
|
||
["blase-leise", ".chat-nachricht__fuss", 4.8],
|
||
];
|
||
const pruefFarben = [];
|
||
for (const [name, wo, noetig] of SCHRIFTEN) {
|
||
const f = dunkelste(name);
|
||
if (!f) {
|
||
console.error("\nKONNTE NICHT NACHSEHEN: --" + name + " steht in keiner"
|
||
+ " der drei CSS-Dateien.\n Ohne sie lässt sich nicht ausrechnen, wie"
|
||
+ " kräftig die Fläche sein darf.\n Erst --" + name + " in chat.css"
|
||
+ " anlegen, dann dieses Werkzeug erneut aufrufen.");
|
||
process.exit(2);
|
||
}
|
||
pruefFarben.push({ name, wo, noetig, farbe: f });
|
||
}
|
||
|
||
console.log("Schriftfarben aus dem CSS gelesen:");
|
||
for (const s of pruefFarben) {
|
||
console.log(" --" + s.name.padEnd(12) + " " + hex(s.farbe) + " braucht "
|
||
+ s.noetig.toFixed(1) + ":1 " + s.wo);
|
||
}
|
||
|
||
/* ---- Die eine Leuchtdichte, auf der alle zwölf liegen ---------------
|
||
---------------------------------------------------------------------
|
||
ZWEITER ANLAUF, UND DIESMAL DIE RICHTIGE GRÖSSE.
|
||
|
||
Der erste suchte eine gemeinsame OKLab-Helligkeit — also gleiche
|
||
EMPFUNDENE Helligkeit — und meldete danach, es gebe gar keine Lösung.
|
||
Das lag nicht an der Schrift, sondern an der Größe, die ich gleich
|
||
halten wollte: Lesbarkeit hängt nicht an der empfundenen Helligkeit,
|
||
sondern an der LEUCHTDICHTE. Ein gesättigtes Grün und ein gesättigtes
|
||
Blau bei derselben OKLab-Helligkeit unterscheiden sich in der
|
||
Leuchtdichte um ein Vielfaches — und damit im Kontrast zur Schrift.
|
||
Gelb fiel deshalb bei jeder Helligkeit durch.
|
||
|
||
Das Versprechen der zwölf Töne lautet wörtlich „damit bleibt die
|
||
Schrift überall gleich gut lesbar". Gleiche Leuchtdichte heißt:
|
||
jeder der zwölf hat EXAKT denselben Kontrast. Das ist das
|
||
Versprechen, gemessen statt umschrieben.
|
||
|
||
UND SIE WIRD NICHT GESUCHT, SONDERN AUSGERECHNET. Aus dem nötigen
|
||
Kontrast und der dunkelsten Schrift auf der Blase folgt genau ein
|
||
Wert. Suchen müsste man nur, wenn er sich nicht herleiten ließe.
|
||
-------------------------------------------------------------------- */
|
||
const ANZAHL_BUNT = 11; /* + 1 neutraler Standard = 12 */
|
||
/* Start bei 258 Grad (Blau-Violett), wo die Hausfarbe ohnehin liegt —
|
||
der Sprung vom Standard zur ersten bunten Wahl ist dann keiner. */
|
||
const HUES = Array.from({ length: ANZAHL_BUNT },
|
||
(_, i) => ((i * 360) / ANZAHL_BUNT + 258) % 360);
|
||
|
||
/* WIE BUNT MUSS ES MINDESTENS WERDEN, DAMIT DER UMBAU ETWAS BRINGT.
|
||
Gemessen an den zwölf, die bis heute drinstanden: ihre Buntheit liegt
|
||
zwischen 0,002 und 0,048, im Mittel 0,028 — deshalb sahen sie auf
|
||
Filipes Bildschirmfoto aus wie zwölf Grautöne. 0,06 liegt über dem
|
||
buntesten davon. Erreicht ein Ton das nicht, ist die Schrift auf der
|
||
Blase zu dunkel — dann sagt das Werkzeug das, statt wieder blasse
|
||
Farben auszuliefern. */
|
||
const MINDEST_BUNTHEIT = 0.06;
|
||
const HOECHST_BUNTHEIT = 0.33;
|
||
|
||
/* Die hellste Fläche, die JEDE Schrift auf der Blase noch trägt. */
|
||
let zielLeuchte = Infinity;
|
||
for (const sf of pruefFarben) {
|
||
zielLeuchte = Math.min(zielLeuchte, (helligkeit(sf.farbe) + 0.05) / sf.noetig - 0.05);
|
||
}
|
||
if (!(zielLeuchte > 0)) {
|
||
console.error("\nKONNTE NICHT NACHSEHEN: Selbst eine schwarze Fläche"
|
||
+ " erreicht den noetigen Kontrast nicht.\n Dann ist die Schrift auf der"
|
||
+ " Blase zu dunkel — erst --blase-leise aufhellen.");
|
||
process.exit(2);
|
||
}
|
||
|
||
/** Der buntestmögliche Ton dieses Farbtons auf genau dieser Leuchtdichte. */
|
||
function buntest(hGrad) {
|
||
let besteC = 0, besteRgb = null;
|
||
for (let L = 0.20; L <= 0.80; L += 0.002) {
|
||
for (let c = HOECHST_BUNTHEIT; c >= 0.004; c -= 0.002) {
|
||
if (!imRaum(L, c, hGrad)) continue;
|
||
const rgb = zuRgb(L, c, hGrad);
|
||
/* Gerundete Bildpunkte treffen den Zielwert nie genau — ein
|
||
Prozent Spielraum. Der Kontrast wird danach am GERUNDETEN
|
||
Ergebnis nachgeprüft, nicht am gerechneten: ausgeliefert wird
|
||
das gerundete. */
|
||
/* EINE OBERGRENZE, KEIN MITTELWERT. Erster Anlauf erlaubte ein
|
||
Prozent nach BEIDEN Seiten -- und ein Prozent nach oben
|
||
reisst genau die Schwelle, aus der dieser Wert hergeleitet
|
||
ist. Nach unten sind drei Prozent erlaubt, damit gerundete
|
||
Bildpunkte ueberhaupt einen Treffer haben. */
|
||
const y = helligkeit(rgb);
|
||
if (y > zielLeuchte || y < zielLeuchte * 0.97) continue;
|
||
if (!pruefFarben.every((sf) => kontrast(rgb, sf.farbe) >= sf.noetig)) continue;
|
||
if (c > besteC) { besteC = c; besteRgb = rgb; }
|
||
break;
|
||
}
|
||
}
|
||
return besteRgb ? { c: besteC, rgb: besteRgb } : null;
|
||
}
|
||
|
||
const bunte = HUES.map((h) => ({ h, ...(buntest(h) || {}) }));
|
||
const fehlen = bunte.filter((b) => !b.rgb || b.c < MINDEST_BUNTHEIT);
|
||
if (fehlen.length) {
|
||
console.error("\nKONNTE NICHT NACHSEHEN: " + fehlen.length + " von "
|
||
+ ANZAHL_BUNT + " Farbtönen bleiben unter Buntheit " + MINDEST_BUNTHEIT
|
||
+ ".\n Betroffen: " + fehlen.map((f) => Math.round(f.h) + " Grad").join(", ")
|
||
+ "\n Heißt: die Schrift auf der Blase ist zu dunkel für kräftige"
|
||
+ " Farben.\n Erst --blase-leise in chat.css aufhellen, dann erneut"
|
||
+ " rechnen.");
|
||
process.exit(2);
|
||
}
|
||
|
||
/* Der neutrale Standard liegt auf derselben Leuchtdichte — sonst wäre
|
||
ausgerechnet die Voreinstellung die Ausnahme. */
|
||
/* UND ER WIRD GENAUSO NACHGEPRÜFT WIE DIE ANDEREN ELF.
|
||
Erster Anlauf prüfte hier nur die Leuchtdichte und verließ sich auf
|
||
das Prozent Spielraum. Das Ergebnis war #50555e mit 6,97:1 — um drei
|
||
Hundertstel unter der eigenen Schwelle, und ausgerechnet bei der
|
||
Voreinstellung, die die meisten sehen. Die Schlusszeile des Werkzeugs
|
||
hat es gemeldet; gefunden hat es also die Prüfung, nicht der Blick.
|
||
Ein Spielraum, der in EINE Richtung wirkt, ist kein Spielraum. */
|
||
let grundRgb = null;
|
||
for (let L = 0.20; L <= 0.80; L += 0.001) {
|
||
const rgb = zuRgb(L, 0.016, 262);
|
||
const y = helligkeit(rgb);
|
||
if (y > zielLeuchte || y < zielLeuchte * 0.97) continue;
|
||
if (!pruefFarben.every((sf) => kontrast(rgb, sf.farbe) >= sf.noetig)) continue;
|
||
grundRgb = rgb; break;
|
||
}
|
||
if (!grundRgb) {
|
||
console.error("\nKONNTE NICHT NACHSEHEN: für den neutralen Standard ließ"
|
||
+ " sich die Leuchtdichte nicht treffen.");
|
||
process.exit(2);
|
||
}
|
||
|
||
/* ---- Die zwölf ------------------------------------------------------ */
|
||
/* DIE SCHLÜSSEL LINKS SIND KEINE BESCHRIFTUNG — SIE STEHEN IN DER
|
||
DATENBANK. Wer sie umbenennt, setzt die Wahl der betroffenen Leute
|
||
still auf „Standard" zurück: `kachelPruefen` kennt den alten Wert
|
||
dann nicht mehr und gibt null.
|
||
|
||
Vor diesem Umbau nachgesehen (nur lesend auf der Live-Datenbank):
|
||
gewählt sind `veilchen` (2×), `ziegel` (1×) und `moos` (1×), sonst
|
||
nichts. Alle drei stehen weiter unten und behalten ihren Farbbereich
|
||
— Veilchen bleibt violett, Ziegel bleibt rot, Moos bleibt grün.
|
||
Niemand verliert seine Einstellung.
|
||
|
||
Die Reihenfolge ist NICHT frei: Sie gehört zu HUES, Eintrag für
|
||
Eintrag. Wer hier umsortiert, vertauscht die Farben. */
|
||
const TOENE = [
|
||
["tinte", "Tinte"], ["veilchen", "Veilchen"], ["pflaume", "Pflaume"],
|
||
["kirsche", "Kirsche"], ["ziegel", "Ziegel"], ["bernstein", "Bernstein"],
|
||
["oliv", "Oliv"], ["moos", "Moos"], ["tanne", "Tanne"],
|
||
["lagune", "Lagune"], ["ozean", "Ozean"],
|
||
];
|
||
if (TOENE.length !== HUES.length) {
|
||
console.error("\nKONNTE NICHT NACHSEHEN: " + TOENE.length + " Namen für "
|
||
+ HUES.length + " Farbtöne.");
|
||
process.exit(2);
|
||
}
|
||
/* „Neutral" und nicht mehr „Standard": Seit dem 22.09.2026 ist dieser
|
||
Ton keine Voreinstellung mehr, sondern eine Wahl -- „ich will keine
|
||
Farbe". Wer nichts einstellt, bekommt automatisch einen der elf
|
||
bunten. Ein Eintrag, der „Standard" heisst und nicht der Standard
|
||
ist, waere die unfreundlichste Art, das zu verschweigen. */
|
||
const liste = [{ wert: "grund", name: "Neutral", farbe: hex(grundRgb) }];
|
||
bunte.forEach((b, i) => {
|
||
liste.push({ wert: TOENE[i][0], name: TOENE[i][1], farbe: hex(b.rgb) });
|
||
});
|
||
|
||
console.log("\nGemeinsame Leuchtdichte: " + zielLeuchte.toFixed(4)
|
||
+ " (die zwölf bisherigen lagen bei rund 0,010 — daher das Grau)");
|
||
console.log("Buntheit: " + Math.min(...bunte.map((b) => b.c)).toFixed(3)
|
||
+ " bis " + Math.max(...bunte.map((b) => b.c)).toFixed(3)
|
||
+ " (bisher 0,002 bis 0,048)");
|
||
let schlechtester = Infinity;
|
||
for (const k of liste) {
|
||
const rgb = ausHex(k.farbe);
|
||
const ks = pruefFarben.map((s) => kontrast(rgb, s.farbe));
|
||
schlechtester = Math.min(schlechtester, ...ks);
|
||
console.log(" " + k.name.padEnd(10) + " " + k.farbe + " "
|
||
+ ks.map((v) => v.toFixed(2) + ":1").join(" "));
|
||
}
|
||
console.log("\nJede Schrift gegen alle zwoelf, schlechtester Fall:");
|
||
for (const sf of pruefFarben) {
|
||
const m = Math.min(...liste.map((k) => kontrast(ausHex(k.farbe), sf.farbe)));
|
||
console.log(" --" + sf.name.padEnd(12) + " " + m.toFixed(2) + ":1 (noetig "
|
||
+ sf.noetig.toFixed(1) + ") " + (m >= sf.noetig ? "haelt" : "ZU WENIG"));
|
||
}
|
||
|
||
/* ---- Eintragen ------------------------------------------------------ */
|
||
if (!process.argv.includes("--schreiben")) {
|
||
console.log("\nNur gezeigt. Mit --schreiben wird es eingetragen.");
|
||
process.exit(0);
|
||
}
|
||
const datei = W("server", "workspace-chat.js");
|
||
const quelle = readFileSync(datei, "utf8");
|
||
const anf = quelle.indexOf("export const CHAT_KACHELN = [");
|
||
const ende = quelle.indexOf("\n];", anf);
|
||
if (anf < 0 || ende < 0) {
|
||
console.error("KONNTE NICHT NACHSEHEN: CHAT_KACHELN steht nicht mehr da,"
|
||
+ " wo dieses Werkzeug sie sucht.");
|
||
process.exit(2);
|
||
}
|
||
const breite = Math.max(...liste.map((k) => k.wert.length)) + 3;
|
||
const neu = "export const CHAT_KACHELN = [\n"
|
||
+ liste.map((k) => " { wert: " + ('"' + k.wert + '",').padEnd(breite)
|
||
+ " name: " + ('"' + k.name + '",').padEnd(13)
|
||
+ " farbe: \"" + k.farbe + "\" },").join("\n");
|
||
writeFileSync(datei, quelle.slice(0, anf) + neu + quelle.slice(ende), "utf8");
|
||
console.log("\nEingetragen in server/workspace-chat.js");
|
||
|
||
/* ---- UND DER RUECKFALL IM STILBLATT ---------------------------------
|
||
Das Stilblatt braucht den Standardton EBENFALLS: Wer nie etwas
|
||
eingestellt hat, bekommt vom Server keinen Wert, und die Blase faellt
|
||
auf das zurueck, was in chat.css steht.
|
||
|
||
Genau daran ist der erste Anlauf am 22.09.2026 gescheitert. Die
|
||
zwoelf Toene waren gerechnet und kraeftig -- und auf dem Bildschirm
|
||
sahen ALLE Blasen aus wie vorher, weil dort noch #131a25 stand, die
|
||
alte Farbe. Gemeldet hat das kein Werkzeug; gesehen hat es erst das
|
||
Bildschirmfoto. Eine Rechnung, die nur die Haelfte der Stellen
|
||
erreicht, ist keine Rechnung, sondern eine zweite Meinung.
|
||
|
||
Deshalb schreibt dasselbe Werkzeug beide Stellen. Von Hand gepflegt
|
||
liefen sie wieder auseinander -- die Frage waere nur, wann. */
|
||
const stil = W("workspace", "assets", "css", "chat.css");
|
||
const ANFANG = "/* ---- ANFANG GERECHNET: chat-kacheln-rechnen.mjs ---- */";
|
||
const SCHLUSS = "/* ---- ENDE GERECHNET ---- */";
|
||
const stilAlt = readFileSync(stil, "utf8");
|
||
const a2 = stilAlt.indexOf(ANFANG);
|
||
const e2 = stilAlt.indexOf(SCHLUSS);
|
||
if (a2 < 0 || e2 < 0) {
|
||
console.error("\nKONNTE NICHT NACHSEHEN: Die Marken fuer den Rueckfall"
|
||
+ " stehen nicht in chat.css."+QQ.replace(QQ,"")+"\n Erwartet:\n " + ANFANG + "\n "
|
||
+ SCHLUSS + "\n Ohne sie faellt jede Blase ohne eigene Wahl auf einen"
|
||
+ " alten Wert zurueck.");
|
||
process.exit(2);
|
||
}
|
||
const stilNeu = ANFANG + "\n"
|
||
+ "/* ERZEUGT VON tools/chat-kacheln-rechnen.mjs -- NICHT VON HAND AENDERN.\n"
|
||
+ " Der Ton, den eine Blase traegt, solange niemand etwas eingestellt\n"
|
||
+ " hat. Derselbe Wert wie der Eintrag Standard in CHAT_KACHELN. */\n"
|
||
+ ".chat-nachricht { --kachel-standard: " + liste[0].farbe + "; }\n"
|
||
+ SCHLUSS;
|
||
writeFileSync(stil, stilAlt.slice(0, a2) + stilNeu + stilAlt.slice(e2 + SCHLUSS.length), "utf8");
|
||
console.log("Rueckfall in chat.css gesetzt: " + liste[0].farbe);
|