Eine Nacht voller Auftraege von Filipe. Der Reihe nach: 1. VERTRAULICH MELDEN -- der groesste neue Teil. "es soll auch eine kategorie also eine hauptkachel geben wo die community leute sich anonym melden koennen wenn sie probleme haben ... rechte hand und dogfather sollen zugriff auf diese anonyme nachrichten haben. also anonym fuer die anderen." WICHTIG, UND ES STEHT SO AUF DER SEITE: "anonym" heisst hier VERTRAULICH. DogFather und die rechte Hand sehen den Namen -- so bestellt und auch richtig, ohne Namen kann man niemandem helfen. Anonym ist es gegenueber allen anderen: kein Modi, kein anderes Mitglied. Wer glaubt, er schreibe unerkannt, schreibt anders und fuehlt sich hinterher getaeuscht -- deshalb steht der Satz im Formular, bevor jemand tippt. Der strikte Wechsel (melden -> warten -> antworten -> warten) ist im SERVER erzwungen, nicht nur im Knopf. Ein ausgegrauter Knopf ist keine Regel. Geschlossen wird nur von der Leitung; abgeschlossene Faelle werden nach 90 Tagen samt Nachrichten geloescht -- hier stehen die empfindlichsten Texte des Hauses. Modis sind ausdruecklich AUSSEN VOR: Sehr oft geht es in diesen Meldungen um eine Moderationsentscheidung. 2. JEDE KATEGORIE LAESST SICH ZUKLAPPEN. "man soll auf dieser seite jede kategorie auf und zu klappen koennen mit einem button ... ueberall wo so eine liste entstehen kann." Das Muster gab es auf der Startseite schon; es steht jetzt als `abschnittKlappbar` in kopf.js und wird von den Brettern und dem Zeitstrahl benutzt. Die Koepfe sind echte <button> (fuer die Tastatur), der Zustand haelt in localStorage, und er haengt je Brett UND Art -- sonst waere "Regel zugeklappt" ueberall gleichzeitig zu. 3. DIE SPRUNGLEISTE SIEHT AUS WIE ETWAS ZUM ANFASSEN. Vorher unterstrichene Woerter, die aussahen wie eine Fusszeile. Jetzt Marken mit Rahmen in einem eigenen Block. 4. DIE STUNDEN SIND LILA. UMGERECHNET, NICHT NEU GEWAEHLT: Jeder der fuenf Verlaufsstopps wurde nach OKLCH zerlegt, der Farbton auf 303 Grad gedreht, zurueckgerechnet. Helligkeit und Buntheit blieben gleich -- eine frei gegriffene Palette waere heller oder bunter geworden, und die Uhr damit unruhiger. 5. DIE KARTEN "UNSERE SEITEN": babyblau und lila, mit schwebenden Logos. Beide Logos wurden zu MASKEN gerechnet (tools/logo-maske.mjs): Die PNG tragen nur Alpha, die Farbe kommt aus dem CSS. Dadurch passt dasselbe Logo in jede Kachel, egal welchen Ton sie hat -- und VanVans 378-KB- Logo wurde dabei zu 109 KB. Vier Stueck je Karte, verschieden gross, zwei Bahnen, 26 bis 41 Sekunden Umlauf. Der erste Versuch war mit 5-9 % Deckkraft praktisch unsichtbar; jetzt 10-16 %. 6. "ENTWICKLUNG" HEISST JETZT "EURE AUFGABEN". Filipe hat recht: Die Kachel fuehrt seit dem 11.09. auf den Katalog -- eine Aufgabenliste. Der Name blieb stehen, weil beim Umhaengen des Ziels niemand ihn angefasst hat. Der Name "Entwicklung" ist damit frei fuer das, was er darunter versteht (Auswertung je Person mit Verlauf und Text) -- das ist noch NICHT gebaut. DREI PRUEFUNGEN, DIE ZUFAELLIG GRUEN WAREN, sind nebenbei aufgefallen: - pruef-kachelraster wartete feste 500 ms auf eine gestaffelte Einblendung und mass vier Reihen, wo drei sind. Jetzt reducedMotion. - pruef-start-ansicht scrollte 600 px und nahm an, danach liege keine Kachel mehr unter dem Zeiger. - pruef-wege-nach-draussen zaehlte das Kachelraster ueber ALLE Gruppen statt je Gruppe. GEMESSEN: pruef-hilfe (neu) 55/0, pruef-wege-nach-draussen 64/0, pruef-jeder-hat-eine-seite 65/0, pruef-community-sicht 10/0, pruef-bereiche-lesend, pruef-steckbrief, pruef-start-ansicht, pruef-kachelraster alle gruen. Co-Authored-By: Claude Opus 5 <[email protected]>
135 lines
5.7 KiB
JavaScript
135 lines
5.7 KiB
JavaScript
/* =====================================================================
|
|
AUS EINEM LOGO EINE MASKE MACHEN (19.09.2026)
|
|
|
|
Filipe: „die kachel links soll dogfather logo als hintergrund haben
|
|
und rechts das logo von vanvan. die logos sollen auch richtig geil im
|
|
hintegrrund sein. die sollen paar mal sein in der kacheln und so wie
|
|
schweben, sich ganz leicht bewegen."
|
|
|
|
WARUM EINE MASKE UND NICHT DAS BILD SELBST:
|
|
|
|
Ein Logo als Hintergrundbild bringt seine eigenen Farben mit. Beide
|
|
hier sind schwarz auf weiss -- auf einer dunklen Kachel waere das
|
|
entweder ein weisser Kasten (VanVan, kein Alphakanal) oder ein
|
|
schwarzer Fleck, den man nicht sieht (DogFather). Umfaerben liesse
|
|
sich das nur mit Filtern, die man raten muss.
|
|
|
|
Eine Maske traegt gar keine Farbe. Sie sagt nur, WO etwas zu sehen
|
|
ist -- die Farbe kommt aus dem CSS. Damit laesst sich dasselbe Logo
|
|
in Babyblau, in Lila und morgen in Gruen zeichnen, ohne eine neue
|
|
Datei.
|
|
|
|
WIE DIE MASKE ENTSTEHT: Alpha = (1 - Helligkeit) x vorhandenes
|
|
Alpha. Wo das Logo schwarz ist, wird die Maske dicht; wo es weiss
|
|
oder durchsichtig ist, bleibt sie offen. Die Zeichnung bleibt
|
|
dadurch erhalten -- eine reine Silhouette waere ein Klecks.
|
|
|
|
UND KLEINER. Das Original von VanVan ist 387 KB. Fuer ein
|
|
Hintergrundmuster, das nie groesser als ein paar hundert Pixel
|
|
erscheint, ist das Verschwendung: Es wird auf 320 px gerechnet, und
|
|
uebrig bleiben wenige Kilobyte.
|
|
|
|
Aufrufen mit: node tools/logo-maske.mjs
|
|
===================================================================== */
|
|
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
|
|
import { join, dirname } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const HIER = dirname(fileURLToPath(import.meta.url));
|
|
const WURZEL = join(HIER, "..");
|
|
const ZIEL = join(WURZEL, "assets", "img", "marke");
|
|
|
|
/** Woraus wird was gemacht. Beide Quellen sind schwarz-weiss. */
|
|
const AUFTRAEGE = [
|
|
{ quelle: join(WURZEL, "assets/img/marke/logo-kopf-frei.png"),
|
|
ziel: join(ZIEL, "maske-dogi.png"), kante: 320,
|
|
was: "Der Huskykopf" },
|
|
{ quelle: join(WURZEL, "assets/img/marke/vanvan-logo-quelle.png"),
|
|
ziel: join(ZIEL, "maske-vanvan.png"), kante: 320,
|
|
was: "Van's DIY & Bastelbedarf" },
|
|
];
|
|
|
|
const fehlend = AUFTRAEGE.filter((a) => !existsSync(a.quelle));
|
|
if (fehlend.length) {
|
|
console.error("KANN NICHT ARBEITEN -- diese Vorlagen fehlen:");
|
|
for (const f of fehlend) console.error(" " + f.quelle);
|
|
console.error("");
|
|
console.error("Das VanVan-Logo liegt unter https://vans-diy-bastelbedarf.com/logo.png");
|
|
console.error("und gehoert VanVan. Es wird hier nur verkleinert und entfaerbt.");
|
|
process.exit(2);
|
|
}
|
|
|
|
const { chromium } = await import(
|
|
"file:///C:/Users/qciga/Documents/Obelix/Analyse/node_modules/playwright/index.mjs");
|
|
const browser = await chromium.launch();
|
|
const seite = await browser.newPage();
|
|
|
|
for (const auftrag of AUFTRAEGE) {
|
|
const roh = readFileSync(auftrag.quelle).toString("base64");
|
|
const ergebnis = await seite.evaluate(async ([daten, kante]) => {
|
|
const bild = new Image();
|
|
bild.src = "data:image/png;base64," + daten;
|
|
await bild.decode();
|
|
|
|
/* Quadratisch und mittig -- beide Logos sind schon nahezu
|
|
quadratisch, aber "nahezu" ist keine Zusicherung. */
|
|
const s = Math.max(bild.width, bild.height);
|
|
const gross = document.createElement("canvas");
|
|
gross.width = s; gross.height = s;
|
|
const gk = gross.getContext("2d");
|
|
gk.drawImage(bild, (s - bild.width) / 2, (s - bild.height) / 2);
|
|
|
|
const d = gk.getImageData(0, 0, s, s);
|
|
const p = d.data;
|
|
for (let i = 0; i < p.length; i += 4) {
|
|
/* Helligkeit nach Wahrnehmung, nicht als Mittelwert: Gruen
|
|
wiegt schwerer als Blau. Bei Schwarz-Weiss macht das keinen
|
|
Unterschied, bei einem spaeter farbigen Logo schon. */
|
|
const hell = (0.2126 * p[i] + 0.7152 * p[i + 1] + 0.0722 * p[i + 2]) / 255;
|
|
const alpha = p[i + 3] / 255;
|
|
const neu = Math.round((1 - hell) * alpha * 255);
|
|
p[i] = p[i + 1] = p[i + 2] = 255; // Farbe egal, die Maske traegt nur Alpha
|
|
p[i + 3] = neu;
|
|
}
|
|
gk.putImageData(d, 0, 0);
|
|
|
|
/* Verkleinern in EINEM Schritt mit hoher Glaettung. Mehrere
|
|
Halbierungen waeren schaerfer, aber hier ist ein weiches
|
|
Hintergrundmuster gewollt. */
|
|
const klein = document.createElement("canvas");
|
|
klein.width = kante; klein.height = kante;
|
|
const kk = klein.getContext("2d");
|
|
kk.imageSmoothingEnabled = true;
|
|
kk.imageSmoothingQuality = "high";
|
|
kk.drawImage(gross, 0, 0, kante, kante);
|
|
|
|
/* Wie viel ist ueberhaupt sichtbar? Eine Maske, die fast leer ist,
|
|
waere ein stiller Fehlschlag -- man sieht auf der Kachel nichts
|
|
und sucht im CSS. */
|
|
const kd = kk.getImageData(0, 0, kante, kante).data;
|
|
let sichtbar = 0;
|
|
for (let i = 3; i < kd.length; i += 4) if (kd[i] > 24) sichtbar++;
|
|
|
|
return {
|
|
png: klein.toDataURL("image/png").split(",")[1],
|
|
anteil: sichtbar / (kante * kante),
|
|
};
|
|
}, [roh, auftrag.kante]);
|
|
|
|
const bytes = Buffer.from(ergebnis.png, "base64");
|
|
if (!existsSync(ZIEL)) mkdirSync(ZIEL, { recursive: true });
|
|
writeFileSync(auftrag.ziel, bytes);
|
|
|
|
const vorher = readFileSync(auftrag.quelle).length;
|
|
const anteil = (ergebnis.anteil * 100).toFixed(1);
|
|
const warnung = ergebnis.anteil < 0.03 || ergebnis.anteil > 0.9
|
|
? " ACHTUNG: sieht nicht nach einem Logo aus" : "";
|
|
console.log(`${auftrag.was}:`);
|
|
console.log(` ${Math.round(vorher / 1024)} KB -> ${Math.round(bytes.length / 1024)} KB`
|
|
+ ` (${auftrag.kante} px, ${anteil} % gedeckt)${warnung}`);
|
|
}
|
|
|
|
await browser.close();
|
|
console.log("");
|
|
console.log("Fertig. Die Masken tragen keine Farbe -- die kommt aus dem CSS.");
|