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
+234
View File
@@ -0,0 +1,234 @@
/* =====================================================================
DIE BILDSCHAU (25.09.2026)
=====================================================================
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. es soll richtig geil gemacht werden alles … ich
will nicht dahin geschmissen sondern was richtig geiles mit einem
geilen hintergrund."
---------------------------------------------------------------------
WAS VORHER PASSIERTE
Ein Klick auf ein Foto oeffnete die Datei selbst in einem neuen Tab.
Was man dann sah, war nicht unsere Seite, sondern die eingebaute
Bildanzeige des Browsers: schwarzer Grund, Bild oben links in der
Ecke, Dateiname in der Titelleiste. Auf Filipes Bildschirmfoto nimmt
das Foto die linke Haelfte ein, rechts sind 1200 px Schwarz.
Das ist kein Fehler im Browser -- er zeigt eine Datei, und eine
Datei hat keine Gestaltung. Wer Gestaltung will, muss eine SEITE
zeigen.
---------------------------------------------------------------------
WARUM EINE SCHAU IN DER SEITE UND KEIN ZWEITER TAB
Ein zweiter Tab braucht eine eigene Seite, eine eigene Anmeldung und
einen eigenen Weg zurueck. Die Schau liegt UEBER dem Chat: Man ist
mit Escape wieder da, wo man war, und das Gespraech laeuft im
Hintergrund weiter.
DER WEG IN DEN TAB BLEIBT TROTZDEM. Das Foto haengt weiterhin an
einem echten Verweis mit `target="_blank"` -- mittlere Maustaste,
„In neuem Tab oeffnen" und Herunterladen funktionieren wie vorher.
Abgefangen wird nur der GEWOEHNLICHE Klick. Ein Verweis, der kein
Verweis mehr ist, nimmt Leuten Wege weg, die sie seit Jahren kennen.
---------------------------------------------------------------------
WARUM `<dialog>` UND KEIN EIGENES DIV
`showModal()` bringt drei Dinge mit, die man sonst von Hand baut und
dabei vergisst: Escape schliesst, der Tastaturfokus bleibt drin, und
alles dahinter ist fuer Vorleseprogramme stumm. Drei eigene
Loesungen waeren drei Gelegenheiten fuer einen Fehler.
---------------------------------------------------------------------
DIE BEGLEITER
HOCHFORMAT: Husky links, Hase rechts. Dort ist der Platz -- ein
hohes Foto laesst links und rechts je ein Viertel des Bildschirms
frei.
QUERFORMAT: „DogFather" oben, „Team Dogi" unten. Dort ist der Platz
-- ein breites Foto laesst oben und unten Streifen frei.
SIE ERSCHEINEN NUR, WENN WIRKLICH PLATZ IST. Gemessen wird nicht die
Fensterbreite, sondern der Rest neben dem Bild: Ein hohes Foto auf
einem Telefon fuellt den Schirm, und ein Husky, der darueberliegt,
waere keine Zierde, sondern ein Hindernis.
SIE SIND LEISE. `aria-hidden` -- ein Vorleseprogramm soll nicht
„Grafik Husky" sagen, wenn jemand ein Foto ansehen will. Und sie
liegen HINTER dem Bild, nie darauf.
===================================================================== */
(function () {
'use strict';
var schau = null; // das <dialog>, einmal gebaut und wiederverwendet
var teile = null; // { bild, name, tab, husky, hase, oben, unten }
var zurueck = null; // wohin der Fokus nach dem Schliessen gehoert
function el(art, klasse, text) {
var e = document.createElement(art);
if (klasse) e.className = klasse;
if (text != null) e.textContent = text;
return e;
}
function bauen() {
if (schau) return;
schau = document.createElement('dialog');
schau.className = 'bildschau';
schau.setAttribute('aria-label', 'Bild ansehen');
/* DER HINTERGRUND IST EIN EIGENES STUECK und nicht der Grund des
Dialogs: Er soll sich unabhaengig vom Inhalt bewegen duerfen
(Verlauf, Schleier, Koernung), ohne dass der Inhalt etwas davon
erbt. */
var grund = el('div', 'bildschau__grund');
grund.setAttribute('aria-hidden', 'true');
schau.appendChild(grund);
/* Die Begleiter liegen HINTER dem Bild -- eigene Ebene, damit das
Bild in jedem Fall obenauf ist. */
var husky = el('img', 'bildschau__figur bildschau__figur--links');
husky.src = 'assets/img/schau-husky.webp';
husky.alt = '';
husky.setAttribute('aria-hidden', 'true');
husky.loading = 'lazy';
husky.decoding = 'async';
var hase = el('img', 'bildschau__figur bildschau__figur--rechts');
hase.src = 'assets/img/schau-hase.webp';
hase.alt = '';
hase.setAttribute('aria-hidden', 'true');
hase.loading = 'lazy';
hase.decoding = 'async';
schau.appendChild(husky);
schau.appendChild(hase);
/* Die zwei Schriftzuege fuers Querformat. */
var oben = el('div', 'bildschau__wort bildschau__wort--oben');
oben.setAttribute('aria-hidden', 'true');
var zeichen = el('img', 'bildschau__zeichen');
zeichen.src = 'assets/img/marke-husky.webp';
zeichen.alt = '';
oben.appendChild(zeichen);
oben.appendChild(el('span', 'bildschau__wort-text', 'DogFather'));
var unten = el('div', 'bildschau__wort bildschau__wort--unten');
unten.setAttribute('aria-hidden', 'true');
unten.appendChild(el('span', 'bildschau__wort-text', 'Team Dogi'));
schau.appendChild(oben);
schau.appendChild(unten);
var buehne = el('div', 'bildschau__buehne');
var bild = el('img', 'bildschau__bild');
bild.alt = '';
buehne.appendChild(bild);
schau.appendChild(buehne);
/* Die Leiste steht OBEN und nicht ueber dem Bild: Sie soll
erreichbar sein, ohne etwas zu verdecken. */
var leiste = el('div', 'bildschau__leiste');
var name = el('span', 'bildschau__name');
var tab = el('a', 'bildschau__knopf', 'Im Tab öffnen');
tab.target = '_blank';
tab.rel = 'noopener noreferrer';
var zu = el('button', 'bildschau__knopf bildschau__zu');
zu.type = 'button';
zu.setAttribute('aria-label', 'Bild schließen');
zu.title = 'Schließen (Esc)';
zu.textContent = '✕';
zu.addEventListener('click', function () { schau.close(); });
leiste.appendChild(name);
leiste.appendChild(tab);
leiste.appendChild(zu);
schau.appendChild(leiste);
/* EIN KLICK NEBEN DAS BILD SCHLIESST. Das erwartet jeder, der
schon einmal ein Foto angesehen hat. Ein Klick AUF das Bild
nicht -- sonst schliesst es sich, waehrend man hineinzoomt. */
schau.addEventListener('click', function (e) {
if (e.target === schau || e.target === grund || e.target === buehne) schau.close();
});
schau.addEventListener('close', function () {
/* Die Quelle loesen: Ein grosses Foto im Speicher zu halten,
nachdem niemand es mehr ansieht, ist Last ohne Zweck. */
teile.bild.removeAttribute('src');
if (zurueck && document.contains(zurueck)) {
try { zurueck.focus(); } catch (f) { /* egal */ }
}
zurueck = null;
});
document.body.appendChild(schau);
teile = { bild: bild, name: name, tab: tab, husky: husky, hase: hase,
oben: oben, unten: unten };
}
/** Hoch, quer oder fast quadratisch -- und ist ueberhaupt Platz?
*
* GEMESSEN WIRD DER REST NEBEN DEM BILD, nicht die Fensterbreite.
* Eine Schwelle wie „ab 1100 px zeigen" waere eine Rechnung von
* gestern: Sie stimmt fuer ein 3:4-Foto und ist bei einem 9:16-Foto
* auf demselben Schirm falsch. Dieses Haus ist an festen Breiten
* schon dreimal hereingefallen.
*/
function lageBestimmen(b, h) {
if (!(b > 0 && h > 0)) return 'keine';
var fb = window.innerWidth, fh = window.innerHeight;
/* So gross wird das Bild: es passt in 92 % Breite und 82 % Hoehe. */
var masz = Math.min(fb * 0.92 / b, (fh * 0.82) / h);
var zb = b * masz, zh = h * masz;
var restSeite = (fb - zb) / 2;
var restOben = (fh - zh) / 2;
if (h > b * 1.05) {
/* Eine Figur braucht rund 170 px, damit sie eine Figur ist und
kein Streifen. Weniger, und sie steht besser gar nicht da. */
return restSeite >= 170 ? 'hoch' : 'keine';
}
if (b > h * 1.05) {
/* Ein Schriftzug braucht rund 70 px Luft -- darunter klebt er am
Bildrand und sieht aus wie ein Versehen. */
return restOben >= 70 ? 'quer' : 'keine';
}
return restOben >= 70 ? 'quer' : 'keine';
}
function lageSetzen() {
if (!schau || !schau.open) return;
var b = teile.bild.naturalWidth, h = teile.bild.naturalHeight;
schau.dataset.lage = lageBestimmen(b, h);
}
/** Ein Bild gross ansehen.
* @param {{weg:string,name:string}} a das Bild
* @param {Element} [woher] wohin der Fokus zurueckgeht
*/
function zeigen(a, woher) {
if (!a || !a.weg) return;
bauen();
zurueck = woher || document.activeElement;
teile.bild.src = a.weg;
teile.bild.alt = a.name || 'Foto';
teile.name.textContent = a.name || '';
teile.tab.href = a.weg;
schau.dataset.lage = 'keine';
/* ERST WENN DAS BILD DA IST, steht sein Format fest. Vorher sind
naturalWidth und naturalHeight null -- und „null zu null" waere
weder hoch noch quer. Liegt es schon im Zwischenspeicher, ist
`complete` sofort wahr und die Zeile darunter greift. */
teile.bild.onload = lageSetzen;
if (teile.bild.complete) lageSetzen();
if (!schau.open) schau.showModal();
}
/* Dreht jemand das Telefon oder zieht das Fenster kleiner, aendert
sich der Platz -- und damit die Antwort auf „passt eine Figur
daneben". */
window.addEventListener('resize', lageSetzen);
window.Bildschau = { zeigen: zeigen };
})();
+225 -8
View File
@@ -57,6 +57,31 @@
/* Die 360 Farben des Rings, Index = Winkel. Kommt vom Server; hier
wird nichts gerechnet (Begruendung steht an der Route). */
let kreisToene = [];
/* Die sieben Helligkeiten und welche davon die Mitte ist. Beides
kommt vom Server (siehe chat-kreis-stufen.js). */
let kreisStufen = [{ wert: 's4', misch: 0, name: 'normal' }];
let kreisMitte = 's4';
/** Einen Grundton heller oder dunkler mischen.
*
* `misch` ist negativ fuer dunkler (Richtung Schwarz) und positiv
* fuer heller (Richtung Weiss) -- dieselbe Rechnung wie
* `color-mix(in srgb, …)` und wie `tonFarbe()` im Server. Sie steht
* hier ein zweites Mal, und das ist Absicht: Die ZAHLEN kommen vom
* Server, nur das Mischen selbst passiert im Browser. Eine Farbe,
* die 2520-mal ueber die Leitung geht, waere teurer als drei Zeilen
* Rechnung.
*/
function tonMischen(grund, misch) {
const s = String(grund).replace('#', '');
if (s.length !== 6) return grund;
const teile = [0, 2, 4].map((i) => parseInt(s.slice(i, i + 2), 16));
const ziel = misch < 0 ? 0 : 255;
const t = Math.abs(misch);
return '#' + teile
.map((v) => Math.round(v * (1 - t) + ziel * t).toString(16).padStart(2, '0'))
.join('');
}
let meineKachel = null;
let autoKachel = null;
@@ -86,9 +111,39 @@
Eine Sonderbehandlung an zehn Stellen waere neun Gelegenheiten
gewesen, eine davon zu vergessen. */
kreisToene = Array.isArray(d.kreis) ? d.kreis : [];
/* ==== DIE SIEBEN HELLIGKEITEN (25.09.2026) ==================
Filipe: „ich will dass die leute auch heller und dunkler
aussuchen koennen."
NICHT 2520 FERTIGE FARBEN, sondern 360 Grundtoene und sieben
Mischanteile -- die ganze Tabelle waere 20 kB fuer etwas, das
eine Zeile Rechnung ist.
DIE ZAHLEN KOMMEN VOM SERVER und stehen nicht hier: Sie sind
aus der Kontrastrechnung hergeleitet (chat-kreis-stufen.js),
und eine zweite Fassung im Browser waere ab der naechsten
Messung falsch -- unbemerkt, denn sie lieferte ja weiter
„irgendeine Farbe". Kommt nichts, bleibt es bei der einen
Mitte: der Kreis von gestern. */
kreisStufen = Array.isArray(d.kreis_stufen) && d.kreis_stufen.length
? d.kreis_stufen : [{ wert: 's4', misch: 0, name: 'normal' }];
kreisMitte = d.kreis_mitte || 's4';
kreisToene.forEach((farbe, winkel) => {
KACHEL_FARBE['ton-' + winkel] = farbe;
if (d.kreis_schrift) KACHEL_SCHRIFT['ton-' + winkel] = d.kreis_schrift;
/* Jede Stufe bekommt ihren eigenen Schluessel in derselben
Tabelle. Damit funktionieren ALLE Stellen, die eine Farbe
nachschlagen, unveraendert weiter -- dieselbe Ueberlegung
wie am 23.09., als der Kreis dazukam. */
for (const s of kreisStufen) {
if (!s.misch) continue;
KACHEL_FARBE['ton-' + winkel + '-' + s.wert] = tonMischen(farbe, s.misch);
}
/* Und der Schluessel MIT der Mitte zeigt auf dieselbe Farbe
wie der ohne -- sonst waere „ton-214" und „ton-214-s4"
zweierlei, und beide kaemen vor. */
KACHEL_FARBE['ton-' + winkel + '-' + kreisMitte] = farbe;
});
kachelKnopfZeigen();
} catch { /* ohne die Liste bleibt alles beim Standard -- kein Grund
@@ -300,8 +355,22 @@
zwischen zwei geprueften Werten liegt.
===================================================================== */
let radWinkel = 0;
/* Welche der sieben Helligkeiten gerade gilt. Der Ring bestimmt den
Farbton, die Leiter die Helligkeit -- zwei Achsen derselben
Farbe. */
let radStufe = 's4';
let radUhr = null;
/** Der Schluessel aus beidem. Die Mitte bekommt KEINEN Zusatz:
* „ton-214" ist seit dem 23.09. der gueltige Schluessel, und wer
* ihn traegt, soll nach diesem Umbau nicht ploetzlich einen anderen
* in der Datenbank stehen haben. */
function radSchluessel() {
return radStufe === kreisMitte
? 'ton-' + radWinkel
: 'ton-' + radWinkel + '-' + radStufe;
}
function radBauen() {
const ring = $('kachelrad-ring');
const rad = $('kachelrad');
@@ -318,8 +387,13 @@
Wer Babyblau traegt, hat keinen Winkel -- dann zeigt der Griff
auf den naechstgelegenen, ohne dass etwas umgestellt wird. */
const jetzt = KACHEL_FARBE[meineKachel] || KACHEL_FARBE[autoKachel] || null;
const t = /^ton-(\d{1,3})$/.exec(String(meineKachel || ''));
/* Der Schluessel darf seit dem 25.09.2026 eine Stufe tragen
("ton-214-s6"). Ohne Zusatz gilt die Mitte -- so wie der Server
es auch liest. */
const t = /^ton-(\d{1,3})(?:-(s[1-7]))?$/.exec(String(meineKachel || ''));
radWinkel = t ? Number(t[1]) : (jetzt ? naechsterWinkel(jetzt).winkel : 0);
radStufe = (t && t[2]) || kreisMitte;
leiterBauen();
radZeigen(radWinkel, { nurAnzeige: true });
}
@@ -333,24 +407,138 @@
ring.setAttribute('aria-valuenow', String(w));
/* EINE ZAHL IN GRAD SAGT NIEMANDEM ETWAS. Vorgelesen wird der
Farbname, den ein Mensch benutzt -- "Blaugruen, 190 Grad". */
ring.setAttribute('aria-valuetext', farbwort(w) + ', ' + w + ' Grad');
/* DIE HELLIGKEIT GEHOERT IN DEN VORLESETEXT. Ohne sie saegte eine
Sprachausgabe bei allen sieben Stufen dasselbe -- und man haette
kein Mittel, die Wahl zu hoeren. */
ring.setAttribute('aria-valuetext',
farbwort(w) + ', ' + w + ' Grad, ' + stufenName(radStufe));
if (mitte) {
const farbe = kreisToene[w];
mitte.style.background = farbe || '';
const s = KACHEL_SCHRIFT['ton-' + w];
mitte.style.color = (s && s.text) || '';
/* ==== DIE PROBE ZEIGT, WIE ES WIRKLICH AUSSIEHT (25.09.2026)
Hier stand eine gefuellte Flaeche in der gewaehlten Farbe mit
heller Schrift darauf. So sah die Blase bis zum 25.09. aus --
seither ist sie fuer alle dunkles Glas, und der gewaehlte Ton
ist der NAME darauf.
Die Probe zeigte also etwas, das es nicht mehr gibt. Jetzt
zeigt sie genau die Rechnung aus chat.css: der Ton, mit Weiss
aufgehellt (`--namen-anteil`), auf dem Blasengrund. Wer hier
eine Farbe waehlt, sieht die Schrift, die die anderen im Chat
sehen werden.
NEBENWIRKUNG, UND SIE IST DER GRUND, WARUM DIE HELLIGKEITEN
UEBERHAUPT GEHEN: Eine gefuellte Flaeche muesste Schrift
tragen -- und bei sehr hellen Toenen schafft das keine der
beiden Schriftfarben mehr (gemessen: 4,1 statt noetiger 7).
Als NAME auf dunklem Grund ist derselbe helle Ton dagegen
besonders gut lesbar (8,0). Die neue Probe ist damit nicht nur
ehrlicher, sie macht die sieben Stufen erst moeglich. */
const farbe = KACHEL_FARBE[radSchluessel()] || kreisToene[w];
mitte.style.background = '';
mitte.style.setProperty('--probe-ton', farbe || '');
}
if (!nurAnzeige) {
/* Die eigenen Blasen im Verlauf faerben sich mit -- man
entscheidet sich ja nicht fuer einen Fleck, sondern dafuer,
wie die eigenen Nachrichten aussehen. */
meineKachel = 'ton-' + w;
meineKachel = radSchluessel();
/* DIE LEITER FAERBT MIT. Ohne diese Zeile zeigten die sieben
Felder die Helligkeiten des Tons von vorhin -- man waehlt
Gruen und sieht sieben Rottoene darunter. */
leiterZeigen();
kachelKnopfZeigen();
eigeneKachelnFaerben();
auswahlMarkieren();
}
}
/** Der Name einer Helligkeitsstufe -- fuer Sprachausgabe und Titel. */
function stufenName(wert) {
return (kreisStufen.find((s) => s.wert === wert) || {}).name || 'normal';
}
/* ==== DIE HELLIGKEITSLEITER (25.09.2026) =========================
Filipe: „ich will dass die leute auch heller und dunkler aussuchen
koennen … so dass die leute viel krassere moeglichkeiten haben."
SIEBEN FELDER, JEDES IN SEINER ECHTEN FARBE -- nicht sieben graue
Kaestchen mit Beschriftung. Man waehlt eine Farbe; also soll man
Farben sehen. Die Felder faerben sich beim Drehen am Ring mit: Wer
den Farbton aendert, sieht sofort, wie die sieben Helligkeiten
DIESES Tons aussehen.
EINE LEISTE UND KEIN ZWEITER KREIS: Farbton laeuft rund (359 Grad
grenzt an 0), Helligkeit nicht -- sie hat ein dunkles und ein
helles Ende. Eine runde Form fuer etwas, das gerade verlaeuft,
waere eine Form, die luegt.
`role="slider"` AM GANZEN KASTEN und nicht sieben Knoepfe: Es ist
EIN Wert mit sieben Stufen. Sieben Knoepfe waeren sieben
Tabulatorschritte fuer eine Entscheidung. */
function leiterBauen() {
const leiter = $('kachelhell-leiter');
if (!leiter) return;
leiter.textContent = '';
kreisStufen.forEach((s, i) => {
const feld = el('span', 'kachelhell__feld');
feld.dataset.stufe = s.wert;
feld.title = s.name;
/* aria-hidden: Der Wert steht am Kasten (`aria-valuenow`), die
Felder sind seine Darstellung. Ohne das laese eine Sprachausgabe
sieben leere Felder vor. */
feld.setAttribute('aria-hidden', 'true');
feld.addEventListener('click', () => stufeWaehlen(i, true));
leiter.append(feld);
});
leiterZeigen();
}
/** Die Felder auf den aktuellen Farbton bringen und die Wahl zeigen. */
function leiterZeigen() {
const leiter = $('kachelhell-leiter');
if (!leiter) return;
const nr = Math.max(0, kreisStufen.findIndex((s) => s.wert === radStufe));
leiter.setAttribute('aria-valuenow', String(nr + 1));
leiter.setAttribute('aria-valuetext', stufenName(radStufe));
for (const feld of leiter.children) {
const s = kreisStufen.find((x) => x.wert === feld.dataset.stufe);
const grund = kreisToene[radWinkel];
if (s && grund) feld.style.background = s.misch ? tonMischen(grund, s.misch) : grund;
feld.dataset.an = feld.dataset.stufe === radStufe ? 'ja' : 'nein';
}
}
/** Eine Stufe waehlen -- ueber den Index, damit Pfeiltasten und
* Klick denselben Weg gehen. */
function stufeWaehlen(nr, sichern) {
const n = Math.max(0, Math.min(kreisStufen.length - 1, nr));
radStufe = kreisStufen[n].wert;
radZeigen(radWinkel);
leiterZeigen();
if (sichern) radSichern(0);
}
function leiterBedienen() {
const leiter = $('kachelhell-leiter');
if (!leiter) return;
leiter.addEventListener('keydown', (e) => {
const nr = kreisStufen.findIndex((s) => s.wert === radStufe);
let ziel = null;
if (e.key === 'ArrowLeft' || e.key === 'ArrowDown') ziel = nr - 1;
else if (e.key === 'ArrowRight' || e.key === 'ArrowUp') ziel = nr + 1;
else if (e.key === 'Home') ziel = 0;
else if (e.key === 'End') ziel = kreisStufen.length - 1;
if (ziel === null) return;
e.preventDefault();
/* Mit einer halben Sekunde Ruhe speichern -- wer mit der Taste
durch die Stufen faehrt, soll nicht sieben Anfragen ausloesen.
Dieselbe Ueberlegung wie am Ring. */
stufeWaehlen(ziel, false);
radSichern(500);
});
}
/** Ein Wort fuer den Bereich, damit eine Sprachausgabe etwas sagen kann. */
function farbwort(w) {
const namen = [
@@ -367,7 +555,7 @@
/** Nach der Bewegung einmal speichern. */
function radSichern(verzoegerung = 0) {
clearTimeout(radUhr);
radUhr = setTimeout(() => kachelSetzen('ton-' + radWinkel), verzoegerung);
radUhr = setTimeout(() => kachelSetzen(radSchluessel()), verzoegerung);
}
function radBedienen() {
@@ -416,6 +604,7 @@
function kachelwahlBedienen() {
radBedienen();
leiterBedienen();
const knopf = $('kachel-oeffnen'), dlg = $('kachel-wahl'), zu = $('kachel-zu');
if (!knopf || !dlg) return;
knopf.addEventListener('click', () => { kachelwahlBauen(); dlg.showModal(); });
@@ -2257,6 +2446,34 @@
kasten.style.maxWidth = `${breit}px`;
}
/* ==== EIN KLICK OEFFNET DIE SCHAU (25.09.2026) ================
Filipe: „wenn man bilder aufmacht, will ich dass die in der
mitte sind."
Bis heute fuehrte der Verweis auf die DATEI, und was man dann
sah, war die eingebaute Bildanzeige des Browsers: schwarzer
Grund, Bild in der Ecke. Das ist kein Fehler des Browsers --
eine Datei hat keine Gestaltung.
DER VERWEIS BLEIBT EIN VERWEIS. `target`, `rel` und `href`
stehen unveraendert da; 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 ueberhaupt geladen ist. Faellt die Datei aus, ist man
wieder beim alten Verhalten statt bei gar keinem.
`metaKey`, `ctrlKey`, `shiftKey` UND DIE MITTLERE TASTE sind
ausgenommen: Das sind die Griffe, mit denen Leute seit zwanzig
Jahren in einem neuen Tab oeffnen. Wer sie abfaengt, nimmt
etwas weg, das niemand vermisst -- bis er es braucht. */
kasten.addEventListener('click', (e) => {
if (e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
if (!window.Bildschau) return;
e.preventDefault();
window.Bildschau.zeigen(a, kasten);
});
const bild = document.createElement('img');
bild.src = a.weg;
bild.alt = a.name || 'Foto';