diff --git a/docs/brand/README.md b/docs/brand/README.md new file mode 100644 index 0000000..0db8686 --- /dev/null +++ b/docs/brand/README.md @@ -0,0 +1,60 @@ +# Marken-Dateien + +Alles hier stammt aus einem einzigen Original: dem D4RKST3R-Logo in 2048×2048 +mit Transparenz (Wolfskopf über dem Schriftzug-Banner). + +## Was hier liegt + +| Datei | Was | Wofür | +|---|---|---| +| `lockup.png` | volles Logo, auf den Inhalt beschnitten | Kopfzeilen, Discord-Banner, OG-Bilder | +| `mark.png` | nur der Wolfskopf, 1024×1024 | alles Quadratische — Favicon, Avatar | +| `build.py` | erzeugt beide plus die abgeleiteten Icons | Neubau nach Logo-Änderung | + +## Warum zwei Fassungen + +Das volle Lockup ist bei 16 oder 32 Pixeln unbrauchbar — der Schriftzug wird zu +einem orangen Balken. Für Favicon, Avatar und Navbar-Logo wird deshalb nur der +Kopf verwendet, mit Ohrenspitzen und etwas Schulter. Der Zuschnitt steht in +`build.py` als `KOPF` und wurde von Hand gegen drei Varianten geprüft: enger +schneidet die Ohren ab, weiter fängt schon den Banner ein. + +## Warum SVG mit eingebettetem PNG + +Gitea will `logo.svg` und `favicon.svg` — echte SVG-Dateien. Das Original ist +aber eine Rastergrafik mit Verläufen und Schattierung; nachvektorisieren würde +sie matschig machen. Deshalb wird das PNG in eine SVG-Hülle gelegt +(``). Das ist gültiges SVG, +Gitea nimmt es an, und es sieht aus wie das Original. + +Die eingebettete Auflösung ist bewusst klein gehalten: das Navbar-Logo hängt an +jedem Seitenaufruf, 192 px reichen für eine Darstellung mit rund 30 px. + +## Wohin die erzeugten Dateien gehen + +`build.py` schreibt direkt an beide Ziele: + +``` +A:/eco/Gitea/data/gitea/public/assets/img/ + logo.svg Navbar + App-Icon + favicon.svg Browser-Tab + apple-touch-icon.png iOS-Lesezeichen (180 px, deckend) + avatar_default.png Standardavatar für Konten ohne Bild (512 px) + +frontend/public/ + favicon.svg + apple-touch-icon.png +``` + +Die Icons auf dunklem Grund sind **deckend** (`#0a0a0a`, abgerundet) — iOS legt +hinter transparente Lesezeichen sonst Schwarz oder Weiß, je nach Laune, und der +Wolf ist selbst fast schwarz. + +## Neu bauen + +```bash +python docs/brand/build.py +``` + +Danach für Gitea `docker restart gitea` — die Dateien unter `custom/` werden +beim Start eingelesen. Fürs Frontend reicht der normale Build. diff --git a/docs/brand/build.py b/docs/brand/build.py new file mode 100644 index 0000000..9eb930c --- /dev/null +++ b/docs/brand/build.py @@ -0,0 +1,107 @@ +# Baut aus dem Original-Logo den Satz an Marken-Dateien: +# - die Wolfskopf-Marke freigestellt (fuer Favicon, Avatar, kleine Groessen) +# - das volle Lockup beschnitten (fuer Kopfzeilen, Discord, OG-Bilder) +# - Icons auf dunklem Grund (iOS und Gitea-Standardavatar moegen kein Alpha) +# - SVG-Huellen mit eingebettetem PNG (Gitea will .svg, das Original ist +# eine Rastergrafik mit Verlaeufen — nachvektorisieren wuerde matschen) +import base64 +import io +import pathlib + +from PIL import Image, ImageDraw + +QUELLE = pathlib.Path('A:/Users/DAR/logo.png') +MARKE = pathlib.Path('A:/eco/dc_devlog_bot/docs/brand') +GITEA = pathlib.Path('A:/eco/Gitea/data/gitea/public/assets/img') +FRONTEND = pathlib.Path('A:/eco/dc_devlog_bot/frontend/public') + +# Kopf mit Ohrenspitzen und etwas Schulter, ohne Schriftzug (im 2048er Original) +KOPF = (330, 20, 1740, 1430) +DUNKEL = (10, 10, 10, 255) # --bg der Marke + +for d in (MARKE, GITEA, FRONTEND): + d.mkdir(parents=True, exist_ok=True) + +original = Image.open(QUELLE).convert('RGBA') +marke = original.crop(KOPF) +lockup = original.crop(original.getchannel('A').getbbox()) + + +def png_bytes(bild, groesse=None): + aus = bild if groesse is None else bild.resize((groesse, groesse), Image.LANCZOS) + puffer = io.BytesIO() + aus.save(puffer, 'PNG', optimize=True) + return puffer.getvalue() + + +def gerundet(bild, kante, radius_anteil=0.22, rand=0.08): + """Marke mittig auf dunklem, abgerundetem Quadrat — deckend.""" + flaeche = Image.new('RGBA', (kante, kante), (0, 0, 0, 0)) + maske = Image.new('L', (kante, kante), 0) + ImageDraw.Draw(maske).rounded_rectangle( + (0, 0, kante - 1, kante - 1), radius=int(kante * radius_anteil), fill=255) + flaeche.paste(Image.new('RGBA', (kante, kante), DUNKEL), (0, 0), maske) + + innen = int(kante * (1 - 2 * rand)) + klein = bild.resize((innen, innen), Image.LANCZOS) + flaeche.alpha_composite(klein, ((kante - innen) // 2, (kante - innen) // 2)) + return flaeche + + +def svg_huelle(daten, kante): + """SVG mit eingebettetem PNG — gueltig, und Gitea nimmt nur .svg.""" + b64 = base64.b64encode(daten).decode('ascii') + return ( + f'\n' + ) + + +geschrieben = [] + + +def schreib(pfad, daten): + pfad.write_bytes(daten if isinstance(daten, bytes) else daten.encode('utf-8')) + geschrieben.append((pfad, len(daten))) + + +# ── Quelldateien im Repo, versioniert ──────────────── +schreib(MARKE / 'mark.png', png_bytes(marke, 1024)) +schreib(MARKE / 'lockup.png', png_bytes(lockup)) + +# ── Gitea ──────────────────────────────────────────── +# Navbar-Logo und Favicon: die Marke, das Lockup ist bei 30 px unlesbar. +# 192 px reichen — Gitea zeigt das Logo in der Navbar mit rund 30 px, und die +# Datei haengt an jedem Seitenaufruf. +schreib(GITEA / 'logo.svg', svg_huelle(png_bytes(marke, 192), 192)) +schreib(GITEA / 'favicon.svg', svg_huelle(png_bytes(gerundet(marke, 128), 128), 128)) +schreib(GITEA / 'apple-touch-icon.png', png_bytes(gerundet(marke, 180))) +schreib(GITEA / 'avatar_default.png', png_bytes(gerundet(marke, 512))) + +# ── Hub + Bot-Produktseite ─────────────────────────── +schreib(FRONTEND / 'favicon.svg', svg_huelle(png_bytes(gerundet(marke, 128), 128), 128)) +schreib(FRONTEND / 'apple-touch-icon.png', png_bytes(gerundet(marke, 180))) + +for pfad, n in geschrieben: + print(f'{n/1024:8.1f} KB {pfad}') + +# ── Vorschau: liest die Marke in klein noch? ───────── +# Landet neben diesem Skript, oben auf hellem, unten auf dunklem Grund — +# so sieht man vor dem Ausrollen, ab welcher Groesse es kippt. +KANTEN = (16, 32, 64, 128) +breite = sum(k + 40 for k in KANTEN) + 30 +vorschau = Image.new('RGBA', (breite, 200), (24, 24, 28, 255)) +x = 30 +for kante in KANTEN: + for grund, y in (((250, 250, 250, 255), 20), (DUNKEL, 110)): + feld = Image.new('RGBA', (kante + 16, kante + 16), grund) + feld.alpha_composite(gerundet(marke, kante), (8, 8)) + vorschau.alpha_composite(feld, (x, y)) + x += kante + 40 +ziel = pathlib.Path(__file__).parent / 'preview.png' +vorschau.convert('RGB').save(ziel) +print(f'\nVorschau ({"/".join(map(str, KANTEN))} px): {ziel}') diff --git a/docs/brand/lockup.png b/docs/brand/lockup.png new file mode 100644 index 0000000..30b1959 Binary files /dev/null and b/docs/brand/lockup.png differ diff --git a/docs/brand/mark.png b/docs/brand/mark.png new file mode 100644 index 0000000..5374e5b Binary files /dev/null and b/docs/brand/mark.png differ diff --git a/docs/brand/preview.png b/docs/brand/preview.png new file mode 100644 index 0000000..5c84564 Binary files /dev/null and b/docs/brand/preview.png differ diff --git a/frontend/index.html b/frontend/index.html index b894f5f..eba5a25 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -6,6 +6,7 @@