Bestenliste, Devlog-Filter, Galerie und leere Bereiche
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled

Vier Stellen, die beim Durchsehen der Seiten aufgefallen sind.

Die Bestenliste zeigte nur Namen — und für Platz 1 bis 3 denselben Pokal
ohne Nummer, wodurch die Reihenfolge nur an der Position erkennbar war.
Jetzt: Rangnummer, Avatar und die ersten drei Plätze farblich abgesetzt.
Die Avatare kommen aus dem Member-Cache, der durch den GuildMembers-Intent
ohnehin gefüllt ist — das kostet keinen einzigen Discord-Aufruf. Wer nicht
im Cache liegt, bekommt seinen Anfangsbuchstaben statt eines leeren Kreises.

Die Projekt-Chips im Devlog-Archiv waren bloß Beschriftung. Jetzt filtern
Chips mit Anzahl nach Projekt (ECOGAME / D4RKBOT) — man will meist nur
eins von beiden lesen. Serverseitig über LIKE auf die erste Zeile, wo das
Projekt ohnehin steht; das spart eine Spalte und eine Migration. Eine
aktive Suche schlägt den Filter, sonst müsste die Volltextsuche beides
verbinden, ohne dass jemand danach fragt.

Galerie: Vorschaubilder von 240 auf 340 Pixel, und Name plus Datum stehen
dauerhaft am Bild statt nur beim Überfahren — am Handy gab es kein Hover
und damit nie eine Zuordnung.

Events, Changelog, Galerie und Level endeten bei wenig Inhalt mit einem
einzelnen Satz in einer sonst leeren Seite; das sah nach Fehler aus statt
nach „noch nichts da". Jetzt steht dort ein Block mit Symbol, Erklärung
was hier erscheinen wird, und Verweisen auf belebte Bereiche.

Dazu ein Sprung-nach-oben-Knopf im Devlog-Archiv und im Changelog, die
beide sehr lang werden.

Beim Testen gefunden: ESCAPE '\' in einem JS-String kommt als leeres
Zeichen in SQLite an ("ESCAPE expression must be a single character") —
der Backslash müsste doppelt entwertet werden. Jetzt ist das Escape-Zeichen
ein Ausrufezeichen, das die Falle gar nicht erst aufmacht.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-31 17:44:57 +02:00
co-authored by Claude Fable 5
parent c6977f9c4a
commit 2e58268f8d
11 changed files with 314 additions and 32 deletions
+93 -11
View File
@@ -472,8 +472,8 @@ body::after {
/* ── GALERIE ───────────────────────────────────────── */
.gallery-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
gap: .9rem;
grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
gap: 1rem;
}
.shot {
position: relative; display: block; overflow: hidden;
@@ -501,16 +501,18 @@ body::after {
filter: brightness(.92);
}
.shot:hover img { transform: scale(1.06); filter: brightness(1.05); }
/* Wer das Bild gemacht hat, steht dauerhaft dran — beim Überfahren nur
deutlicher. Vorher war es nur im Hover sichtbar und damit am Handy nie. */
.shot-meta {
position: absolute; inset: auto 0 0 0;
display: flex; justify-content: space-between; align-items: baseline;
padding: .55rem .7rem .45rem;
background: linear-gradient(to top, rgba(0, 0, 0, .9), transparent);
font-family: var(--mono); font-size: .62rem; letter-spacing: .1em;
opacity: 0; transform: translateY(4px);
transition: opacity .25s, transform .25s;
padding: 1.4rem .7rem .5rem;
background: linear-gradient(to top, rgba(0, 0, 0, .92), transparent);
font-family: var(--mono); font-size: .64rem; letter-spacing: .1em;
opacity: .8;
transition: opacity .25s;
}
.shot:hover .shot-meta { opacity: 1; transform: translateY(0); }
.shot:hover .shot-meta { opacity: 1; }
.shot-author { color: var(--neon); }
.shot-date { color: var(--muted); }
@@ -1079,8 +1081,29 @@ label.toggle {
}
.lvl-row:last-child { border-bottom: none; }
.lvl-row:first-child { border-radius: 12px 12px 0 0; }
.lvl-rank { font-family: var(--mono); font-size: .85rem; color: var(--muted); min-width: 2.6rem; }
.lvl-name { font-weight: 600; min-width: 10rem; }
.lvl-rank {
display: inline-flex; align-items: center; gap: .35rem;
font-family: var(--mono); font-size: .85rem; color: var(--muted);
min-width: 3.4rem;
}
.lvl-rank-n { min-width: 1.2rem; text-align: right; }
/* Die ersten drei Plätze dürfen sich abheben — sonst sieht die Liste flach aus */
.lvl-row.top .lvl-rank-n { color: var(--text); font-size: .95rem; }
.lvl-row.top-1 { background: linear-gradient(90deg, rgba(245, 197, 24, .07), transparent 55%); }
.lvl-row.top-2 { background: linear-gradient(90deg, rgba(192, 196, 204, .05), transparent 55%); }
.lvl-row.top-3 { background: linear-gradient(90deg, rgba(192, 122, 62, .05), transparent 55%); }
.lvl-avatar {
flex: none; width: 2.1rem; height: 2.1rem; border-radius: 50%;
object-fit: cover;
background: var(--bg3); border: 1px solid var(--border);
}
.lvl-row.top-1 .lvl-avatar { border-color: rgba(245, 197, 24, .55); }
.lvl-avatar-empty {
display: inline-flex; align-items: center; justify-content: center;
font-family: var(--display); font-size: 1rem; color: var(--muted);
}
.lvl-name { font-weight: 600; min-width: 9rem; }
.lvl-progress { flex: 1; }
.lvl-level {
font-family: var(--mono); font-size: .72rem; letter-spacing: .1em;
@@ -1670,7 +1693,6 @@ button.with-icon, a.with-icon { justify-content: center; }
.srv-map, .srv-ping { display: inline-flex; align-items: center; gap: .3rem; }
/* Bestenliste: Pokal statt Medaillen-Emoji */
.lvl-rank { display: inline-flex; align-items: center; justify-content: center; }
/* Bot-Avatar-Platzhalter */
.bot-avatar-empty { display: flex; align-items: center; justify-content: center; color: var(--muted); }
@@ -1926,6 +1948,66 @@ button.with-icon, a.with-icon { justify-content: center; }
.tpl-peek { display: none; }
}
/* ── DEVLOG-FILTER ─────────────────────────────────── */
/* Gleiche Form wie die Modul-Filter — beides sind Chips zum Einschränken */
.dl-filters { display: flex; flex-wrap: wrap; gap: .4rem; margin-bottom: .9rem; }
.dl-filter {
display: inline-flex; align-items: center; gap: .45rem;
font-family: var(--mono); font-size: .62rem;
letter-spacing: .12em; text-transform: uppercase;
color: var(--muted); background: var(--bg2);
border: 1px solid var(--border); border-radius: 99px;
padding: .35rem .8rem; cursor: pointer;
transition: color .15s, border-color .15s, background .15s;
}
.dl-filter:hover { color: var(--text); border-color: var(--neon); }
.dl-filter.active { color: #0a0a0a; background: var(--neon); border-color: var(--neon); }
.dl-filter-n {
font-size: .58rem; opacity: .7;
padding-left: .35rem; border-left: 1px solid currentColor;
}
/* ── LEERE BEREICHE ────────────────────────────────── */
/* Vorher stand hier ein einzelner Satz in einer sonst leeren Seite —
das sah nach Fehler aus statt nach „noch nichts da". */
.empty-state {
display: flex; flex-direction: column; align-items: center; text-align: center;
gap: .6rem;
padding: 3.5rem 1.5rem;
border: 1px dashed var(--border); border-radius: 14px;
background: rgba(255, 255, 255, .012);
}
.empty-icon {
display: inline-flex; align-items: center; justify-content: center;
width: 3.6rem; height: 3.6rem; border-radius: 50%;
background: var(--bg3); border: 1px solid var(--border);
color: var(--muted2);
margin-bottom: .3rem;
}
.empty-title {
font-family: var(--display); font-size: 1.5rem;
letter-spacing: .04em; color: var(--text); margin: 0;
}
.empty-text {
color: var(--muted); font-weight: 300;
line-height: 1.6; max-width: 34rem; margin: 0;
}
.empty-actions { display: flex; flex-wrap: wrap; gap: .6rem; justify-content: center; margin-top: .8rem; }
/* ── NACH OBEN ─────────────────────────────────────── */
.to-top {
position: fixed; right: 1.5rem; bottom: 1.5rem; z-index: 400;
display: inline-flex; align-items: center; justify-content: center;
width: 2.8rem; height: 2.8rem; border-radius: 50%;
background: var(--bg2); border: 1px solid var(--border);
color: var(--muted); cursor: pointer;
transition: color .2s, border-color .2s, opacity .25s, transform .25s;
}
.to-top:hover { color: var(--neon); border-color: var(--neon); }
@media (max-width: 640px) {
.to-top { right: 1rem; bottom: 1rem; }
}
/* ── STELLWERTE ────────────────────────────────────── */
.tune-list {
display: flex; flex-direction: column;