docs: README, Design-Dokumentation, Changelog und Seed
README mit Installation, Update-Weg und Backup-Anleitung; docs/DESIGN.md hält Marke, Farbwerte samt gemessener Kontraste und die Bausteine fest. Changelog nach Keep a Changelog, Release-Notes zusätzlich unter releases/.
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
# Changelog
|
||||
|
||||
Alle nennenswerten Änderungen an Flimmer stehen in dieser Datei.
|
||||
|
||||
Das Format folgt [Keep a Changelog](https://keepachangelog.com/de/1.1.0/),
|
||||
die Versionierung [Semantic Versioning](https://semver.org/lang/de/).
|
||||
|
||||
## [Unveröffentlicht]
|
||||
|
||||
## [0.1.0] — 2026-09-01
|
||||
|
||||
Erste lauffähige Etappe: Gerüst, Marke, Datenmodell und Anmeldung.
|
||||
|
||||
### Hinzugefügt
|
||||
|
||||
- **Projektgerüst** mit Next.js 16 (App Router), TypeScript, Tailwind CSS v4
|
||||
und `standalone`-Build für schlanke Container.
|
||||
- **Marke Flimmer**: Logo als SVG in drei Varianten (Bildmarke, Bildmarke mit
|
||||
Wortmarke, monochrom) plus Maskable-Variante. Die Wortmarke ist aus Pfaden
|
||||
konstruiert und braucht keine installierte Schrift.
|
||||
- **Icon-Generator** (`npm run icons`): erzeugt Favicon (SVG und ICO),
|
||||
PWA-Icons in sieben Größen, Maskable-Icons, Apple-Touch-Icon und OG-Bild
|
||||
aus den SVG-Quellen.
|
||||
- **Design-Tokens** als CSS-Variablen, über `@theme inline` an Tailwind
|
||||
angebunden. Dunkles Standard-Theme und heller Modus mit Umschalter
|
||||
(Dunkel / Hell / System), ohne Aufblitzen beim Laden. Alle Farbkombinationen
|
||||
erfüllen WCAG AA; getrennte Verläufe für Flächen und Text sorgen dafür, dass
|
||||
auch der Signaturverlauf im hellen Modus lesbar bleibt.
|
||||
- **Datenmodell** (Prisma, PostgreSQL) für Konten, Katalog (Titel, Staffeln,
|
||||
Episoden, Streamingdienste), persönliches Tracking, Universen mit
|
||||
Reihenfolgen sowie Benachrichtigungen und Hintergrundläufe.
|
||||
- **Authentifizierung**: Registrierung und Anmeldung mit E-Mail und Passwort
|
||||
(bcrypt), Sitzungen als opake Token in der Datenbank mit httpOnly-Cookie.
|
||||
Der erste registrierte Zugang wird automatisch Administrator; die
|
||||
Registrierung lässt sich instanzweit abschalten.
|
||||
- **Optionales SSO** über OIDC (Authorization Code mit PKCE) für Authelia,
|
||||
Keycloak, Authentik und Verwandte — per Umgebungsvariablen aktivierbar,
|
||||
mit Zuordnung zu vorhandenen Konten über die E-Mail-Adresse.
|
||||
- **API-Schlüssel** je Nutzer als Grundlage für die spätere REST-API.
|
||||
- **Docker-Compose-Setup** für den Selbstbetrieb (App und PostgreSQL,
|
||||
persistente Volumes, Healthcheck) samt Entrypoint, der ausstehende
|
||||
Migrationen beim Start einspielt.
|
||||
- **Healthcheck** unter `/api/health`, der auch die Datenbank prüft.
|
||||
- **PWA-Manifest** mit allen Icon-Größen.
|
||||
- **Oberflächentexte** auf Deutsch, gebündelt in `src/i18n` und so aufgebaut,
|
||||
dass weitere Sprachen als zusätzliche Datei dazukommen können.
|
||||
- **Seed-Skript** mit Demo-Konten sowie README und Design-Dokumentation.
|
||||
|
||||
[Unveröffentlicht]: https://git.wilde-server.de/florian.wilde/flimmer/compare/v0.1.0...HEAD
|
||||
[0.1.0]: https://git.wilde-server.de/florian.wilde/flimmer/releases/tag/v0.1.0
|
||||
@@ -83,3 +83,13 @@ Statistiken, Benachrichtigungen, REST-API, PWA. Oberflächensprache **Deutsch**.
|
||||
## Fortschritt
|
||||
|
||||
- Stand: **Etappe 1 läuft** (Start 2026-09-01 18:09)
|
||||
|
||||
<!-- BEGIN:nextjs-agent-rules -->
|
||||
|
||||
# This is NOT the Next.js you know
|
||||
|
||||
This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` (resolved from this file's directory; in monorepos the `next` package may not be visible from the repo root) before writing any code. Heed deprecation notices.
|
||||
|
||||
This block is written and re-added by `next dev` — verify at `node_modules/next/dist/server/lib/generate-agent-files.js`. Removing it from a diff only re-creates the uncommitted change; committing it with your work keeps the tree clean.
|
||||
|
||||
<!-- END:nextjs-agent-rules -->
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# DEVLOG — Entwicklungszeiterfassung Flimmer
|
||||
|
||||
Alle Zeiten per `date`-Befehl erfasst (Europe/Berlin), nicht geschätzt.
|
||||
Format je Eintrag: `Start – Ende (Dauer) — Beschreibung`.
|
||||
|
||||
---
|
||||
|
||||
@@ -9,4 +8,23 @@ Format je Eintrag: `Start – Ende (Dauer) — Beschreibung`.
|
||||
|
||||
| # | Start | Ende | Dauer | Arbeitsschritt |
|
||||
|---|---|---|---|---|
|
||||
| 1.1 | 2026-09-01 18:09 | | | Rückfragen geklärt, Repo/Toolchain geprüft, Projekt-CLAUDE.md angelegt |
|
||||
| 1.1 | 18:09 | 18:12 | 3 min | Rückfragen geklärt, Repo und Werkzeugkette geprüft, Projekt-`CLAUDE.md` als Gedächtnis angelegt |
|
||||
| 1.2 | 18:12 | 18:22 | 10 min | `package.json`, TypeScript-, Next- und PostCSS-Konfiguration; Abhängigkeiten installiert (Aufräumen nach hängendem npm-Prozess) |
|
||||
| 1.3 | 18:22 | 18:26 | 4 min | Design-Tokens in `globals.css`: Farbwelt dunkel/hell, Verläufe, Bausteine, Mikro-Animation |
|
||||
| 1.4 | 18:26 | 18:33 | 7 min | Logo entworfen: Bildmarke, Wortmarke aus Pfaden, Lockup, monochrom, Maskable; Icon-Generator und alle Rasterableitungen |
|
||||
| 1.5 | 18:33 | 18:36 | 3 min | Prisma-Schema (22 Modelle), Umstellung auf Prisma 7 mit Treiber-Adapter, erste Migration |
|
||||
| 1.6 | 18:36 | 18:40 | 4 min | Kernbibliotheken: Umgebungsprüfung, DB-Singleton, Auth (Passwörter, Sitzungen, API-Schlüssel), Instanz-Einstellungen, i18n |
|
||||
| 1.7 | 18:40 | 18:44 | 4 min | App-Hülle: Layout mit Theme-Bootstrap, Header, Navigation, Nutzermenü, Anmeldung und Registrierung, Healthcheck; visuelle Kontrolle in beiden Themes und Kontrastkorrektur für den hellen Modus |
|
||||
| 1.8 | 18:44 | 18:52 | 8 min | Docker-Setup (Dockerfile, Compose, Entrypoint mit Migrationslauf), OIDC-Anbindung, Seed-Skript, README, Design-Dokumentation, Changelog |
|
||||
|
||||
**Etappe 1 gesamt: 43 min** (18:09 – 18:52)
|
||||
|
||||
---
|
||||
|
||||
## Gesamtübersicht
|
||||
|
||||
| Etappe | Version | Dauer |
|
||||
|---|---|---|
|
||||
| 1 — Gerüst, Branding, Docker, Schema, Auth | 0.1.0 | 43 min |
|
||||
|
||||
**Bisher gesamt: 43 min**
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 Florian Wilde
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,198 @@
|
||||
<p align="center">
|
||||
<img src="public/brand/flimmer-logo.svg" alt="Flimmer" width="380">
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<b>Serien und Filme tracken — selbst gehostet, ohne fremde Konten.</b><br>
|
||||
Episodenfortschritt, eigene Universen mit mehreren Reihenfolgen, Statistiken
|
||||
und Benachrichtigungen, wenn es weitergeht.
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
**Flimmer** ist ein selbst hostbarer Tracker für Serien und Filme — benannt nach
|
||||
der Flimmerkiste und dem warmen Flimmern, das ein Bildschirm im dunklen Zimmer
|
||||
verbreitet. Metadaten kommen von [TMDB](https://www.themoviedb.org/), die Daten
|
||||
bleiben auf deinem Server.
|
||||
|
||||
## Was Flimmer kann
|
||||
|
||||
- **Tracking** — Serien und Filme mit Status (Schaue gerade, Pausiert,
|
||||
Abgeschlossen, Abgebrochen, Watchlist), Episodenfortschritt einzeln oder
|
||||
staffelweise, „+1 Episode"-Knopf, Rewatch-Zählung, Bewertung, Notizen, Tags.
|
||||
- **Universen** — MCU, Star Wars, Wizarding World … beliebige Filme *und* Serien
|
||||
in einem Universum bündeln, mit mehreren Reihenfolgen (Erscheinung,
|
||||
Story-Chronologie, Einsteiger-Route) und Gesamtfortschritt.
|
||||
- **Metadaten von TMDB** — Suche, Poster, Staffeln und Episoden, offizielle
|
||||
Collections, Streaming-Verfügbarkeit in deiner Region.
|
||||
- **Benachrichtigungen** — bei neuen Episoden und Releases, per E-Mail, ntfy,
|
||||
Gotify oder Webhook (Discord/Slack-kompatibel), pro Nutzer einstellbar.
|
||||
- **Mehrere Nutzer** — jede Person hat ihre eigene Bibliothek; Universen lassen
|
||||
sich teilen. Der erste registrierte Zugang wird Administrator.
|
||||
- **REST-API und PWA** — dokumentierte Schnittstelle mit eigenen API-Schlüsseln,
|
||||
installierbar auf dem Startbildschirm.
|
||||
|
||||
## Schnellstart
|
||||
|
||||
Voraussetzungen: Docker und Docker Compose.
|
||||
|
||||
```bash
|
||||
git clone https://git.wilde-server.de/florian.wilde/flimmer.git
|
||||
cd flimmer
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
In der `.env` mindestens diese drei Werte setzen:
|
||||
|
||||
```dotenv
|
||||
POSTGRES_PASSWORD=ein-langes-zufaelliges-passwort
|
||||
AUTH_SECRET=noch-ein-langes-zufaelliges-geheimnis
|
||||
APP_URL=https://flimmer.example.com
|
||||
```
|
||||
|
||||
Geheimnisse erzeugen:
|
||||
|
||||
```bash
|
||||
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
|
||||
```
|
||||
|
||||
Dann starten:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Flimmer läuft auf <http://localhost:3000>. Der **erste** Zugang, der sich
|
||||
registriert, wird automatisch Administrator — lege ihn also gleich an. Danach
|
||||
lässt sich die Registrierung in der Verwaltung schließen.
|
||||
|
||||
### TMDB-Schlüssel
|
||||
|
||||
Ohne TMDB findet die Suche keine Metadaten. Der Schlüssel ist kostenlos:
|
||||
|
||||
1. Konto auf [themoviedb.org](https://www.themoviedb.org/signup) anlegen
|
||||
2. Unter *Einstellungen → API* einen Schlüssel beantragen
|
||||
3. In die `.env` eintragen und `docker compose up -d` erneut ausführen:
|
||||
|
||||
```dotenv
|
||||
TMDB_API_KEY=dein-schluessel
|
||||
TMDB_REGION=DE
|
||||
TMDB_LANGUAGE=de-DE
|
||||
```
|
||||
|
||||
## Betrieb hinter einem Reverse Proxy
|
||||
|
||||
`APP_URL` muss auf die öffentliche Adresse zeigen — daraus werden Links in
|
||||
Benachrichtigungen und der OIDC-Rücksprung gebildet. Als Healthcheck eignet
|
||||
sich `/api/health`; der Endpunkt prüft auch die Datenbankverbindung.
|
||||
|
||||
Beispiel für Caddy:
|
||||
|
||||
```caddyfile
|
||||
flimmer.example.com {
|
||||
reverse_proxy flimmer:3000
|
||||
}
|
||||
```
|
||||
|
||||
## Aktualisieren
|
||||
|
||||
```bash
|
||||
cd flimmer
|
||||
git pull
|
||||
docker compose build
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Datenbankmigrationen laufen automatisch beim Start des Containers — ein eigener
|
||||
Handgriff ist nicht nötig. **Vor** einem Update ein Backup zu ziehen ist
|
||||
trotzdem eine gute Angewohnheit.
|
||||
|
||||
## Backup
|
||||
|
||||
Alles, was zählt, steckt in der Datenbank; das Volume `flimmer-data` enthält
|
||||
nur den Bild-Cache und lässt sich jederzeit neu aufbauen.
|
||||
|
||||
```bash
|
||||
# Sicherung
|
||||
docker compose exec -T db pg_dump -U flimmer -d flimmer --clean --if-exists \
|
||||
| gzip > flimmer-$(date +%F).sql.gz
|
||||
|
||||
# Rückspielen
|
||||
gunzip -c flimmer-2026-09-01.sql.gz \
|
||||
| docker compose exec -T db psql -U flimmer -d flimmer
|
||||
```
|
||||
|
||||
Für einen nächtlichen Lauf genügt ein Cron-Eintrag mit demselben Befehl.
|
||||
Wer zusätzlich den Bild-Cache sichern will:
|
||||
|
||||
```bash
|
||||
docker run --rm -v flimmer_flimmer-data:/data -v "$PWD":/backup alpine \
|
||||
tar czf /backup/flimmer-data.tar.gz -C /data .
|
||||
```
|
||||
|
||||
## Konfiguration
|
||||
|
||||
Sämtliche Einstellungen laufen über Umgebungsvariablen; die vollständige Liste
|
||||
mit Erklärungen steht in [`.env.example`](.env.example). Die wichtigsten:
|
||||
|
||||
| Variable | Bedeutung |
|
||||
|---|---|
|
||||
| `APP_URL` | Öffentliche Adresse der Instanz |
|
||||
| `AUTH_SECRET` | Geheimnis für Sitzungen — mindestens 32 Zeichen |
|
||||
| `TMDB_API_KEY` | TMDB-Schlüssel für Metadaten |
|
||||
| `TMDB_REGION` | Region für die Streaming-Verfügbarkeit (Standard `DE`) |
|
||||
| `ALLOW_REGISTRATION` | Selbstregistrierung bei der Erstinbetriebnahme |
|
||||
| `ENABLE_SCHEDULER` | Eingebauter Hintergrundlauf für Sync und Benachrichtigungen |
|
||||
| `SMTP_*` | E-Mail-Versand |
|
||||
| `OIDC_*` | Optionale SSO-Anbindung (Authelia, Keycloak, Authentik …) |
|
||||
|
||||
## Entwicklung
|
||||
|
||||
```bash
|
||||
npm install
|
||||
docker compose -f docker-compose.dev.yml up -d # nur PostgreSQL
|
||||
cp .env.example .env # DATABASE_URL auf localhost:5439
|
||||
npm run db:migrate
|
||||
npm run db:seed # Demo-Konten
|
||||
npm run dev
|
||||
```
|
||||
|
||||
| Befehl | Wirkung |
|
||||
|---|---|
|
||||
| `npm run dev` | Entwicklungsserver |
|
||||
| `npm run build` | Produktionsbuild |
|
||||
| `npm run typecheck` | TypeScript prüfen |
|
||||
| `npm run db:migrate` | Migration erzeugen und anwenden |
|
||||
| `npm run db:studio` | Prisma Studio |
|
||||
| `npm run db:seed` | Beispieldaten |
|
||||
| `npm run icons` | Favicon und PWA-Icons aus dem SVG-Logo neu erzeugen |
|
||||
|
||||
Aufbau des Projekts:
|
||||
|
||||
```
|
||||
src/
|
||||
app/ Routen (App Router), API-Endpunkte
|
||||
components/ Wiederverwendbare Bausteine
|
||||
lib/ Serverseitige Logik: DB, Auth, TMDB, Benachrichtigungen
|
||||
i18n/ Oberflächentexte
|
||||
prisma/ Schema, Migrationen, Seed
|
||||
public/brand/ Logo in allen Varianten
|
||||
docker/ Entrypoint des Containers
|
||||
docs/ Anleitung, Design-Dokumentation
|
||||
```
|
||||
|
||||
## Technik
|
||||
|
||||
Next.js (App Router) mit TypeScript, PostgreSQL über Prisma, Tailwind CSS.
|
||||
Warum Next und nicht SvelteKit: Oberfläche, REST-API und Hintergrundläufe
|
||||
stecken in **einem** Deployment, Server Components halten den Datenbankzugriff
|
||||
serverseitig, und der `standalone`-Build ergibt ein schlankes Image ohne
|
||||
Build-Werkzeuge. Für ein selbst gehostetes Werkzeug zählt vor allem, dass ein
|
||||
einziger Container genügt.
|
||||
|
||||
## Lizenz
|
||||
|
||||
MIT — siehe [LICENSE](LICENSE).
|
||||
|
||||
Metadaten stammen von TMDB. Flimmer wird von TMDB weder betrieben noch
|
||||
unterstützt.
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
# Flimmer — Design-Sprache
|
||||
|
||||
Diese Datei hält fest, warum die Oberfläche aussieht, wie sie aussieht, und
|
||||
welche Werte dabei gelten. Sie ist die Referenz für alles, was später dazukommt.
|
||||
|
||||
## Idee
|
||||
|
||||
Der Name kommt von der **Flimmerkiste** und vom warmen Flimmern eines
|
||||
Bildschirms im abgedunkelten Zimmer. Genau das trägt die Gestaltung: ein sehr
|
||||
dunkler, blauvioletter Raum, in den ein warmes Licht fällt. Alles Farbige in
|
||||
der Oberfläche ist dieses Licht — es gibt keine zweite, konkurrierende
|
||||
Signalfarbe.
|
||||
|
||||
## Marke
|
||||
|
||||
| Variante | Datei | Einsatz |
|
||||
|---|---|---|
|
||||
| Bildmarke | `public/brand/flimmer-icon.svg` | App-Icon, Favicon, Avatare |
|
||||
| Bildmarke + Wortmarke | `public/brand/flimmer-logo.svg` | Header, Login, README |
|
||||
| Monochrom | `public/brand/flimmer-mono.svg` | Druck, E-Mail-Footer, Prägung |
|
||||
| Maskable | `public/brand/flimmer-icon-maskable.svg` | Android-Startbildschirm |
|
||||
| Wortmarke allein | `public/brand/flimmer-wordmark.svg` | Sonderfälle |
|
||||
|
||||
Die Bildmarke ist ein abgerundetes Bildschirm-Rechteck mit weichem, warmem
|
||||
Glühen und einem Play-Dreieck; der angedeutete Standfuß macht daraus die
|
||||
Flimmerkiste. Die **Wortmarke ist aus Pfaden konstruiert** (geometrische, leicht
|
||||
gerundete Sans, Versalhöhe 90, x-Höhe 54, Strichstärke 15, runde Abschlüsse) —
|
||||
sie braucht damit keine installierte Schrift und sieht überall identisch aus.
|
||||
|
||||
Im UI liegt die Marke als React-Komponente vor
|
||||
(`src/components/brand/FlimmerLogo.tsx`). Die Verläufe stehen einmalig in
|
||||
`<BrandDefs />` im Root-Layout; alle Instanzen referenzieren sie. Rasterdateien
|
||||
(Favicon, PWA-Icons, OG-Bild) sind reine Ableitungen und entstehen mit
|
||||
`npm run icons`.
|
||||
|
||||
## Farbwelt
|
||||
|
||||
Die Signaturfarbe ist kein einzelner Ton, sondern ein **Verlauf von Bernstein
|
||||
nach Magenta**. Er trägt Buttons, Fortschrittsbalken, aktive Navigation und das
|
||||
Glühen des Logos.
|
||||
|
||||
### Dunkel (Standard)
|
||||
|
||||
| Rolle | Wert | Kontrast auf Grund |
|
||||
|---|---|---|
|
||||
| Grund | `#0c0a14` | — |
|
||||
| Karte | `#171424` | — |
|
||||
| Rahmen | `#2e2745` | 1,4:1 (dekorativ) |
|
||||
| Text | `#f4f1fa` | **17,0:1** |
|
||||
| Text gedämpft | `#aca5c2` | **7,7:1** |
|
||||
| Text zurückhaltend | `#7c7594` | **4,6:1** |
|
||||
| Bernstein | `#ffb454` | **11,3:1** |
|
||||
| Magenta | `#ff4d9d` | **6,0:1** |
|
||||
| Text auf dem Verlauf | `#1a1024` | min. **5,5:1** (am Magenta-Ende) |
|
||||
|
||||
### Hell
|
||||
|
||||
| Rolle | Wert | Kontrast auf Grund |
|
||||
|---|---|---|
|
||||
| Grund | `#faf7f5` (warmgrau) | — |
|
||||
| Karte | `#ffffff` | — |
|
||||
| Text | `#1b1524` | **16,2:1** |
|
||||
| Text gedämpft | `#5b5268` | **6,7:1** |
|
||||
| Text zurückhaltend | `#766c84` | **4,6:1** |
|
||||
| Akzent als Text | `#8a4b00` | **6,4:1** |
|
||||
| Akzent-Alternative | `#b01262` | **6,4:1** |
|
||||
| Text auf dem Verlauf | `#23122c` | min. **6,1:1** |
|
||||
|
||||
Damit erfüllt jede Text-/Hintergrund-Kombination **WCAG AA**.
|
||||
|
||||
### Zwei Verläufe, nicht einer
|
||||
|
||||
Der vollfarbige Verlauf funktioniert im Dunkeln überall — auf hellem Grund
|
||||
verschwindet sein Bernstein-Ende jedoch. Deshalb gibt es zwei Tokens:
|
||||
|
||||
- `--fl-gradient` — Flächen (Buttons, Fortschritt, Glühen). In beiden Themes
|
||||
vollfarbig, weil darauf immer dunkler Text (`--fl-on-accent`) sitzt.
|
||||
- `--fl-gradient-ink` — **Text** und feine Linien. Im Dunkeln identisch, im
|
||||
Hellen abgedunkelt auf `#8a4b00 → #b01262`, damit AA gilt.
|
||||
|
||||
Analog dazu die Logo-Stops `--fl-brand-1/2/3`: im Hellen tiefer angesetzt
|
||||
(`#c26a00 → #c9186f`), damit die Wortmarke auf warmem Weiß lesbar bleibt, ohne
|
||||
die Bewegung von Bernstein nach Magenta aufzugeben.
|
||||
|
||||
## Tokens
|
||||
|
||||
Alle Werte liegen als CSS-Variablen `--fl-*` in `src/app/globals.css` und werden
|
||||
über `@theme inline` an Tailwind durchgereicht. Ein Theme-Wechsel setzt nur das
|
||||
Attribut `data-theme` am `<html>`-Element; ein Inline-Skript im Layout tut das,
|
||||
bevor der erste Pixel gemalt wird, damit nichts aufblitzt.
|
||||
|
||||
Ein zusätzliches Theme braucht daher genau einen neuen Selektor-Block mit
|
||||
denselben Variablennamen — keine Änderung an Komponenten.
|
||||
|
||||
## Bausteine
|
||||
|
||||
| Klasse | Zweck |
|
||||
|---|---|
|
||||
| `.fl-card` | Karte: halbtransparent, weicher Rahmen, Rückenunschärfe |
|
||||
| `.fl-poster-card` | Posterkachel mit sanftem Aufglimmen beim Hover |
|
||||
| `.fl-btn` + `.fl-btn-primary` / `.fl-btn-ghost` | Schaltflächen |
|
||||
| `.fl-input` | Eingabefelder samt Fokusring im Magenta |
|
||||
| `.fl-progress` | Fortschrittsbalken im Signaturverlauf |
|
||||
| `.fl-glimmer` | Mikro-Animation beim Abhaken einer Episode |
|
||||
|
||||
## Bewegung
|
||||
|
||||
Ruhig und kurz: 200–420 ms, `cubic-bezier(0.22, 1, 0.36, 1)`. Keine harten
|
||||
Blitze — beim Abhaken einer Episode glimmt die Zeile einmal warm auf und
|
||||
verklingt. `prefers-reduced-motion: reduce` schaltet sämtliche Übergänge ab.
|
||||
|
||||
## Typografie
|
||||
|
||||
**Outfit** — geometrisch, leicht gerundet, passt zur Wortmarke. Wird beim Bauen
|
||||
heruntergeladen und mit ausgeliefert; die laufende Instanz fragt nie bei Google
|
||||
an. Rückfallkette: Inter, dann System-Sans.
|
||||
|
||||
## Grundhaltung
|
||||
|
||||
Posterlastig und filmisch: großzügige Poster, abgerundete Ecken, weiche
|
||||
Verläufe statt harter Linien. Farbe ist knapp und bedeutet etwas — der Verlauf
|
||||
markiert, was gerade dran ist. Der Rest bleibt im Dunkeln.
|
||||
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* Beispieldaten für die Entwicklung.
|
||||
*
|
||||
* npm run db:seed
|
||||
*
|
||||
* Das Skript ist idempotent: mehrfaches Ausführen legt nichts doppelt an.
|
||||
* Es fasst ausschließlich die Demo-Konten und deren Daten an — eine echte
|
||||
* Instanz nimmt dadurch keinen Schaden, sollte es versehentlich laufen.
|
||||
*/
|
||||
import { PrismaPg } from "@prisma/adapter-pg";
|
||||
import bcrypt from "bcryptjs";
|
||||
import { PrismaClient } from "../src/generated/prisma/client";
|
||||
|
||||
try {
|
||||
process.loadEnvFile(".env");
|
||||
} catch {
|
||||
// Werte kommen aus der Umgebung.
|
||||
}
|
||||
|
||||
const prisma = new PrismaClient({
|
||||
adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL }),
|
||||
});
|
||||
|
||||
const DEMO_PASSWORD = "flimmer-demo-2026";
|
||||
|
||||
async function seedUsers() {
|
||||
const passwordHash = await bcrypt.hash(DEMO_PASSWORD, 12);
|
||||
|
||||
const admin = await prisma.user.upsert({
|
||||
where: { email: "demo@flimmer.local" },
|
||||
update: {},
|
||||
create: {
|
||||
email: "demo@flimmer.local",
|
||||
name: "Demo Nutzer",
|
||||
passwordHash,
|
||||
role: "ADMIN",
|
||||
region: "DE",
|
||||
publicProfile: true,
|
||||
profileSlug: "demo",
|
||||
},
|
||||
});
|
||||
|
||||
const friend = await prisma.user.upsert({
|
||||
where: { email: "gast@flimmer.local" },
|
||||
update: {},
|
||||
create: {
|
||||
email: "gast@flimmer.local",
|
||||
name: "Gast Nutzerin",
|
||||
passwordHash,
|
||||
role: "USER",
|
||||
region: "DE",
|
||||
},
|
||||
});
|
||||
|
||||
return { admin, friend };
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log("Seed: lege Demo-Konten an ...");
|
||||
const { admin, friend } = await seedUsers();
|
||||
|
||||
console.log("");
|
||||
console.log("Fertig. Anmelden mit:");
|
||||
console.log(` ${admin.email} / ${DEMO_PASSWORD} (Administrator)`);
|
||||
console.log(` ${friend.email} / ${DEMO_PASSWORD}`);
|
||||
}
|
||||
|
||||
main()
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
})
|
||||
.finally(async () => {
|
||||
await prisma.$disconnect();
|
||||
});
|
||||
@@ -0,0 +1,38 @@
|
||||
# Flimmer 0.1.0
|
||||
|
||||
Erste lauffähige Etappe: Gerüst, Marke, Datenmodell und Anmeldung.
|
||||
|
||||
### Hinzugefügt
|
||||
|
||||
- **Projektgerüst** mit Next.js 16 (App Router), TypeScript, Tailwind CSS v4
|
||||
und `standalone`-Build für schlanke Container.
|
||||
- **Marke Flimmer**: Logo als SVG in drei Varianten (Bildmarke, Bildmarke mit
|
||||
Wortmarke, monochrom) plus Maskable-Variante. Die Wortmarke ist aus Pfaden
|
||||
konstruiert und braucht keine installierte Schrift.
|
||||
- **Icon-Generator** (`npm run icons`): erzeugt Favicon (SVG und ICO),
|
||||
PWA-Icons in sieben Größen, Maskable-Icons, Apple-Touch-Icon und OG-Bild
|
||||
aus den SVG-Quellen.
|
||||
- **Design-Tokens** als CSS-Variablen, über `@theme inline` an Tailwind
|
||||
angebunden. Dunkles Standard-Theme und heller Modus mit Umschalter
|
||||
(Dunkel / Hell / System), ohne Aufblitzen beim Laden. Alle Farbkombinationen
|
||||
erfüllen WCAG AA; getrennte Verläufe für Flächen und Text sorgen dafür, dass
|
||||
auch der Signaturverlauf im hellen Modus lesbar bleibt.
|
||||
- **Datenmodell** (Prisma, PostgreSQL) für Konten, Katalog (Titel, Staffeln,
|
||||
Episoden, Streamingdienste), persönliches Tracking, Universen mit
|
||||
Reihenfolgen sowie Benachrichtigungen und Hintergrundläufe.
|
||||
- **Authentifizierung**: Registrierung und Anmeldung mit E-Mail und Passwort
|
||||
(bcrypt), Sitzungen als opake Token in der Datenbank mit httpOnly-Cookie.
|
||||
Der erste registrierte Zugang wird automatisch Administrator; die
|
||||
Registrierung lässt sich instanzweit abschalten.
|
||||
- **Optionales SSO** über OIDC (Authorization Code mit PKCE) für Authelia,
|
||||
Keycloak, Authentik und Verwandte — per Umgebungsvariablen aktivierbar,
|
||||
mit Zuordnung zu vorhandenen Konten über die E-Mail-Adresse.
|
||||
- **API-Schlüssel** je Nutzer als Grundlage für die spätere REST-API.
|
||||
- **Docker-Compose-Setup** für den Selbstbetrieb (App und PostgreSQL,
|
||||
persistente Volumes, Healthcheck) samt Entrypoint, der ausstehende
|
||||
Migrationen beim Start einspielt.
|
||||
- **Healthcheck** unter `/api/health`, der auch die Datenbank prüft.
|
||||
- **PWA-Manifest** mit allen Icon-Größen.
|
||||
- **Oberflächentexte** auf Deutsch, gebündelt in `src/i18n` und so aufgebaut,
|
||||
dass weitere Sprachen als zusätzliche Datei dazukommen können.
|
||||
- **Seed-Skript** mit Demo-Konten sowie README und Design-Dokumentation.
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Schnelles Einzelbild einer Seite — Werkzeug für die Entwicklung.
|
||||
* node scripts/shot.mjs <url> <ziel.png> [breite] [hoehe] [theme]
|
||||
*/
|
||||
import { chromium } from "playwright";
|
||||
|
||||
const [, , url, out, w = "1440", h = "900", theme = "dark"] = process.argv;
|
||||
|
||||
const browser = await chromium.launch();
|
||||
const page = await browser.newPage({
|
||||
viewport: { width: Number(w), height: Number(h) },
|
||||
deviceScaleFactor: 2,
|
||||
});
|
||||
await page.addInitScript((mode) => {
|
||||
localStorage.setItem("flimmer-theme", mode);
|
||||
}, theme);
|
||||
await page.goto(url, { waitUntil: "networkidle" });
|
||||
await page.waitForTimeout(600);
|
||||
await page.screenshot({ path: out });
|
||||
await browser.close();
|
||||
console.log("ok", out);
|
||||
Reference in New Issue
Block a user