- Default-Schriftfarbe = #FFFFFF, Default-Hintergrund = #000000
- Wenn Schrift- und Hintergrundfarbe zu wenig Kontrast haben (< 2.5:1),
wird automatisch auf weiss-auf-schwarz zurueckgefallen. So kann
man sich nicht mehr in eine unsichtbare Seite manoevrieren.
- Color-Picker zeigt jetzt die wirksame Default-Farbe in der Vorschau
(statt leer/transparent), auch wenn keine Override gesetzt ist.
- Live-Vorschau beim Tippen statt erst bei Blur (input-Event)
- Hex-Werte werden auch ohne fuehrendes '#' akzeptiert ("ff0000" -> "#FF0000")
- Reset-Button wendet Standardwerte sofort an
- v19 / sw cache v19
Die bisherigen Stufen-Wähler ("Dunkel/Mittel/Hell/Maximum" und
"Kaum/Subtil/Normal/Stark") für Schrift- bzw. Linienkontrast sind durch
echte Hex-Color-Picker ersetzt. Zusätzlich kann jetzt auch die
Hintergrundfarbe der Seite frei gewählt werden.
Wenn ein Override gesetzt ist:
- text_color → setzt --text-1 direkt, --text-2/--text-3 werden
daraus per shadeHex(-0.25 / -0.55) abgeleitet, damit der Hue passt
- line_color → setzt --border, --border-light wird leicht abgedunkelt
- bg_color → setzt --bg-app, daraus werden Topbar/Sidebar/Surface/
Hover/Active per shadeHex(+0.10…+0.40) konsistent hochskaliert
Per "Reset"-Knopf wird der Override geleert und die alte Stufen-Logik
(falls noch vorhanden) bzw. der Default-Theme greift wieder.
Backend:
- 3 neue nullable VARCHAR(7)-Spalten in user_settings (text_color,
line_color, bg_color) inkl. Migrationen in main.py
- settings_router nutzt model_dump(exclude_unset=True) und respektiert
explizite null-Werte nur für diese 3 Override-Felder, damit Reset
funktioniert
Auch enthalten: Auflösen der Merge-Konflikte in sw.js, index.html,
version.js (HEAD-Stand v17 behalten) und Bump auf v18.
Das Popup hatte vorher Text+Icon-Buttons in einem Footer mit
verschwendeter vertikaler Höhe. Jetzt:
- Color-Dot + Titel links (volle Breite, kann sauber umbrechen)
- Kompakte 30px-Icon-Toolbar rechts oben: Bearbeiten / Kopieren /
Löschen / Schließen
- Icons im Ruhezustand transparent (nur SVG sichtbar, sehr dezent)
- Auf Hover: runder farbiger Hintergrund. Edit/Copy in Primärfarbe,
Delete in Akzentrot, Close in neutralem bg-hover
- Klick gibt mit Scale-Down (.9) taktilen Feedback
- Popup-Breite leicht erhöht (340 → 360 px) damit Titel + Toolbar
bequem nebeneinander passen
- Trash- und Copy-SVG-Pfade auf den 24x24-viewBox normalisiert
(waren vorher zu lang)
Version v16 → v17.
Wenn der Browser noch die alte CSS bzw. i18n.js aus dem Cache hatte,
lief das neu strukturierte Popup ins Leere:
- SVGs ohne CSS-Width-Constraint nahmen die Browser-Standardgröße
(300×150) an → riesige Icons, Layout brach in Vertikalstapel
- Der Key "copy" fehlte in der alten i18n.js → "Kopieren" wurde durch
den Roh-Key "copy" ersetzt
Robust gemacht:
- SVGs der Action-Buttons bekommen jetzt direkt im HTML width="16"
height="16" — funktioniert auch ohne dass die zugehörige CSS-Regel
geladen wurde
- applyLang() in i18n.js fällt bei fehlendem Schlüssel auf den
HTML-Default-Text zurück, anstatt den Key als Text einzuschreiben
(gleiches Prinzip für data-i18n, -i18n-ph, -i18n-title)
Version v15 → v16.
Vorher haben Bearbeiten/Kopieren/Löschen/Schließen im Header über die
Hälfte der Breite gefressen, sodass der Titel auf 2-3 Zeilen
zusammenschrumpfen musste.
Neues Layout:
- Schließen-X klein in der oberen rechten Ecke (absolut positioniert)
- Header zeigt nur Color-Dot + Titel — voller Platz fürs Lesen
- Drei beschriftete Aktions-Buttons (Bearbeiten / Kopieren / Löschen)
als gleichbreite Reihe im Footer
- Hover-Tint folgt der Primärfarbe; Löschen tönt zur Akzentfarbe
- Popup-Breite leicht erhöht (300 → 340 px) für mehr Atemraum
- Mobile bekommt die Action-Buttons etwas kompakter
IDs der Buttons unverändert (popup-edit/copy/delete/close), bestehende
JS-Handler funktionieren weiter.
Version v14 → v15.
Die drei Aktions-Icons (Bearbeiten, Kopieren, Löschen) und der
Schließen-X im Termin-Popup hatten bisher nur den schlichten
icon-btn-Hover (graue Fläche). Jetzt im selben modernen Stil wie die
neuen Pill-Buttons:
- Bearbeiten/Kopieren/Löschen: Hover bekommt Primärfarben-Tint
(color-mix-Hintergrund + farbige Schrift) plus dezenten farbigen
Schatten
- Schließen-X: Hover zeigt die Akzentfarbe (rot), passend zur
destruktiven Geste
- Klick fühlt sich mit kurzem Scale-Down (.92) taktiler an
Version v13 → v14.
Großes Frontend-Update für alle Buttons. Der Stil orientiert sich an
modernen App-Designs (Pill mit dezentem Schatten, sanft "abhebender"
Hover-Effekt), die Farbe folgt der gewählten Primärfarbe des Users
dynamisch via color-mix().
- .btn: fully rounded (border-radius: 999px), grösseres Padding,
smooth Transitions für Schatten/Transform/Brightness
- .btn-primary: Primärfarbe als Hintergrund + dezenter farbiger
Schatten; Hover hebt um 1px, Schatten wird kräftiger, leichte
Aufhellung
- .btn-secondary: dezenter Border, auf Hover wird er primär-farben
- .btn-ghost / .btn-danger entsprechend angepasst
- .btn-fab (Sidebar "Erstellen"): jetzt in Primärfarbe statt grau,
passt zum FAB unten rechts auf Mobile und zur Marken-Sprache
- .icon-btn: kleines Scale-Down beim Drücken, Focus-Ring sichtbar
für Tastatur-Nutzer
- Form-Inputs: 8px Radius, sanfter Hover-Border, beim Focus jetzt
Primärfarben-Ring (color-mix-Glow)
Fix: kaputtes Plus-SVG am Kalender-Hinzufügen-Button — Vertikalbalken
war zu lang (v12 statt v6), jetzt symmetrisch.
Version v12 → v13.
Bisher konnten alte JS-/CSS-Dateien durch Service-Worker- und Browser-
Cache hartnäckig hängen bleiben, obwohl auf dem Server schon eine neue
Version lag. Strategie jetzt:
Backend (main.py)
- Neue HTTP-Middleware setzt explizite Cache-Control-Header:
* /, /index.html, /manifest.json, /sw.js, /static/js/version.js
bekommen no-cache, no-store, must-revalidate
* /static/* und /icons/* bekommen public, max-age=7200,
must-revalidate (2 h)
* SPA-Fallback-Antworten ebenfalls no-cache
* /api/* bleibt unangetastet
Service Worker (sw.js)
- Wechsel von Cache-First zu Network-First für alles
- Cache wird nur noch für die index.html-Offline-Hülle vorgehalten,
nicht mehr für JS/CSS — Browser-HTTP-Cache übernimmt das mit den
2-h-Headern vom Server
- Bei Netzwerkfehler bleibt nur die HTML-Shell offline verfügbar
Version v11 → v12 (auch SW-Cache-Key).
Bisher bekam nur .topbar Safe-Area-Padding, aber .content-wrapper
rechnete weiter starr mit --topbar-h. Im PWA-Standalone-Modus auf
iPhones mit Notch lief der Kalender dadurch oben in die Status-Bar
und unten in den Home-Indicator hinein — die Wochentag-Header und
Tagesnummern der ersten Zeile waren verdeckt, die letzte Zeile
zu kurz.
- .content-wrapper: margin-top und height berechnen jetzt safe-top
und safe-bottom mit ein
- .sidebar (Mobile-Overlay): top startet ebenfalls unterhalb der
vergrösserten Topbar
Version v10 → v11.
Bisher bekam nur .topbar Safe-Area-Padding, aber .content-wrapper
rechnete weiter starr mit --topbar-h. Im PWA-Standalone-Modus auf
iPhones mit Notch lief der Kalender dadurch oben in die Status-Bar
und unten in den Home-Indicator hinein — die Wochentag-Header und
Tagesnummern der ersten Zeile waren verdeckt, die letzte Zeile
zu kurz.
- .content-wrapper: margin-top und height berechnen jetzt safe-top
und safe-bottom mit ein
- .sidebar (Mobile-Overlay): top startet ebenfalls unterhalb der
vergrösserten Topbar
Version v10 → v11.
- Erstellen-Button in der Sidebar: SVG-Path war asymmetrisch
(M19 13h-6v9...) — Vertikalbalken hing nach unten heraus. Jetzt
Standard-Plus mit gleich langen Armen.
- Service Worker holt index.html und version.js ab sofort per
Network-First — neue Releases greifen damit beim nächsten
Seiten-Reload, ohne dass der SW manuell deregistriert werden muss.
Statics bleiben Cache-First für Offline-Tauglichkeit; auf
Aktivierung wird der alte Cache komplett gelöscht.
Version v9 → v10.
- Erstellen-Button in der Sidebar: SVG-Path war asymmetrisch
(M19 13h-6v9...) — Vertikalbalken hing nach unten heraus. Jetzt
Standard-Plus mit gleich langen Armen.
- Service Worker holt index.html und version.js ab sofort per
Network-First — neue Releases greifen damit beim nächsten
Seiten-Reload, ohne dass der SW manuell deregistriert werden muss.
Statics bleiben Cache-First für Offline-Tauglichkeit; auf
Aktivierung wird der alte Cache komplett gelöscht.
Version v9 → v10.
- Titel im Topbar wird auf Mobile auf 2 Zeilen aufgeteilt: Hauptlabel
(z.B. "Mai – Jun") oben, Jahr ("2026") darunter in kleinerer Schrift.
Auf Desktop bleibt es einzeilig durch margin-left auf der Year-Span.
- Event-Popup: 44px-Mindestgröße der Icon-Buttons greift hier nicht
mehr — Buttons bleiben kompakt 32px, weniger Gap, schmaleres Popup
(max 92vw / 340px), sodass das Schließen-X nicht aus dem Rand
herausragt.
- Monatsansicht auf Mobile: Startuhrzeit ("00:00 Lemgo") wird
versteckt, nur der Titel ist sichtbar. Auf Desktop wie bisher mit
Uhrzeit-Präfix. Die Info bleibt im Termin-Popup verfügbar.
Version v8 → v9.
- Titel im Topbar wird auf Mobile auf 2 Zeilen aufgeteilt: Hauptlabel
(z.B. "Mai – Jun") oben, Jahr ("2026") darunter in kleinerer Schrift.
Auf Desktop bleibt es einzeilig durch margin-left auf der Year-Span.
- Event-Popup: 44px-Mindestgröße der Icon-Buttons greift hier nicht
mehr — Buttons bleiben kompakt 32px, weniger Gap, schmaleres Popup
(max 92vw / 340px), sodass das Schließen-X nicht aus dem Rand
herausragt.
- Monatsansicht auf Mobile: Startuhrzeit ("00:00 Lemgo") wird
versteckt, nur der Titel ist sichtbar. Auf Desktop wie bisher mit
Uhrzeit-Präfix. Die Info bleibt im Termin-Popup verfügbar.
Version v8 → v9.
- "Heute"-Button auf Mobile wieder im Topbar-Center sichtbar
(kompakter mit weniger Padding) statt nur im View-Popup.
- Neuer runder Floating-Action-Button unten rechts auf Mobile mit
Plus-Icon, öffnet das "Termin erstellen"-Modal — Google-Calendar-
artige Bedienung.
- Der "Erstellen"-Button in der Sidebar wird auf Mobile ausgeblendet,
weil der FAB ihn ersetzt. Auf Desktop bleibt alles wie bisher.
- iOS-Safe-Area unten respektiert (Home-Indicator).
Version v7 → v8.
- "Heute"-Button auf Mobile wieder im Topbar-Center sichtbar
(kompakter mit weniger Padding) statt nur im View-Popup.
- Neuer runder Floating-Action-Button unten rechts auf Mobile mit
Plus-Icon, öffnet das "Termin erstellen"-Modal — Google-Calendar-
artige Bedienung.
- Der "Erstellen"-Button in der Sidebar wird auf Mobile ausgeblendet,
weil der FAB ihn ersetzt. Auf Desktop bleibt alles wie bisher.
- iOS-Safe-Area unten respektiert (Home-Indicator).
Version v7 → v8.
Damit lädt beim Swipen durch Monate erst nach ~10 Monaten in beide
Richtungen erneut Daten nach. Vorher reichte der Cache nur ±2 Monate,
sodass nach 2-3 Wischen ein Spinner kam.
- CACHE_BUF 56 → 300 Tage (initial ±10 Monate)
- PREFETCH_EXT 56 → 180 Tage (Verlängerung bei Edge ~6 Monate)
- PREFETCH_EDGE 28 → 90 Tage (Trigger ~3 Monate vor Cache-Rand)
Version v6 → v7.
Damit lädt beim Swipen durch Monate erst nach ~10 Monaten in beide
Richtungen erneut Daten nach. Vorher reichte der Cache nur ±2 Monate,
sodass nach 2-3 Wischen ein Spinner kam.
- CACHE_BUF 56 → 300 Tage (initial ±10 Monate)
- PREFETCH_EXT 56 → 180 Tage (Verlängerung bei Edge ~6 Monate)
- PREFETCH_EDGE 28 → 90 Tage (Trigger ~3 Monate vor Cache-Rand)
Version v6 → v7.
Wenn aktiviert, bekommt der JWT-Token statt der üblichen 7 Tage eine
Lebensdauer von 180 Tagen. Der Token liegt wie bisher in localStorage,
bleibt also bis zum manuellen Löschen / Cookie-Reset gültig.
- backend/routers/auth_router.py: LoginRequest.remember_me, längere
expires_delta beim Token-Erstellen
- index.html: Checkbox unter dem 2FA-Feld
- api.js: login() reicht remember_me als 4. Parameter durch
- app.js: Wert aus #login-remember lesen und mitschicken
- Version v5 → v6
Wenn aktiviert, bekommt der JWT-Token statt der üblichen 7 Tage eine
Lebensdauer von 180 Tagen. Der Token liegt wie bisher in localStorage,
bleibt also bis zum manuellen Löschen / Cookie-Reset gültig.
- backend/routers/auth_router.py: LoginRequest.remember_me, längere
expires_delta beim Token-Erstellen
- index.html: Checkbox unter dem 2FA-Feld
- api.js: login() reicht remember_me als 4. Parameter durch
- app.js: Wert aus #login-remember lesen und mitschicken
- Version v5 → v6
- View-Switcher auf Mobile in Popup-Menü ausgelagert (neuer Icon-Button
rechts in der Topbar). Dadurch wird in der Topbar Platz frei für
prev/next + Monatstitel ("Mai 2026" usw.).
- Topbar-Settings-Icon auf Mobile ausgeblendet, dafür neuer
"Einstellungen"-Eintrag im User-Dropdown. "Heute" wandert ins
View-Popup.
- KW-Bubble: von oben-links nach unten-links verschoben — überlappt
jetzt nicht mehr die Tagesnummer.
- Termine in der Monatsansicht zeigen wieder ihren Text (kleinere
14px-Höhe, 9px Schrift) statt nur farbiger Punkte.
- Long-Press auf einen Tag öffnet das Kontextmenü "Termin erstellen"
(synthetisches contextmenu-Event nach 500 ms ohne Bewegung). Der
nachfolgende synthetische Click wird unterdrückt.
- Settings-Modal: Sidebar (Darstellung/Konten/Benutzerverwaltung) auf
Mobile als slide-in Overlay mit Hamburger-Toggle. Auf Desktop bleibt
sie immer sichtbar.
- Version v4 → v5 (auch SW-Cache)
- View-Switcher auf Mobile in Popup-Menü ausgelagert (neuer Icon-Button
rechts in der Topbar). Dadurch wird in der Topbar Platz frei für
prev/next + Monatstitel ("Mai 2026" usw.).
- Topbar-Settings-Icon auf Mobile ausgeblendet, dafür neuer
"Einstellungen"-Eintrag im User-Dropdown. "Heute" wandert ins
View-Popup.
- KW-Bubble: von oben-links nach unten-links verschoben — überlappt
jetzt nicht mehr die Tagesnummer.
- Termine in der Monatsansicht zeigen wieder ihren Text (kleinere
14px-Höhe, 9px Schrift) statt nur farbiger Punkte.
- Long-Press auf einen Tag öffnet das Kontextmenü "Termin erstellen"
(synthetisches contextmenu-Event nach 500 ms ohne Bewegung). Der
nachfolgende synthetische Click wird unterdrückt.
- Settings-Modal: Sidebar (Darstellung/Konten/Benutzerverwaltung) auf
Mobile als slide-in Overlay mit Hamburger-Toggle. Auf Desktop bleibt
sie immer sichtbar.
- Version v4 → v5 (auch SW-Cache)
- Viewport: maximum-scale=1, user-scalable=no — kein Pinch-Zoom mehr
- Profil-Dropdown öffnet wieder: overflow:hidden auf .topbar-right
in der Mobile-Media-Query entfernt (hatte das absolut positionierte
Dropdown abgeschnitten)
- Long-Press auf Kalenderzellen markiert keinen Text mehr:
user-select/touch-callout/tap-highlight in der ganzen Mobile-UI aus
- Long-Press auf Avatar zeigt nicht "Bild speichern":
-webkit-touch-callout:none + pointer-events:none auf <img>
- Kalenderwochen erscheinen als kleine Bubble oben links in jeder
Zeile statt als eigene 38px-Spalte
- Status-Bar-Overlap im Settings-Modal behoben: safe-area-inset-top
auf .settings-page-header und Modal-Header in der Mobile-Media-Query
- Swipe links/rechts auf #view-container navigiert prev/next
(≥60 px, überwiegend horizontal, < 700 ms)
- Version v3 → v4 (auch SW-Cache)
- Viewport: maximum-scale=1, user-scalable=no — kein Pinch-Zoom mehr
- Profil-Dropdown öffnet wieder: overflow:hidden auf .topbar-right
in der Mobile-Media-Query entfernt (hatte das absolut positionierte
Dropdown abgeschnitten)
- Long-Press auf Kalenderzellen markiert keinen Text mehr:
user-select/touch-callout/tap-highlight in der ganzen Mobile-UI aus
- Long-Press auf Avatar zeigt nicht "Bild speichern":
-webkit-touch-callout:none + pointer-events:none auf <img>
- Kalenderwochen erscheinen als kleine Bubble oben links in jeder
Zeile statt als eigene 38px-Spalte
- Status-Bar-Overlap im Settings-Modal behoben: safe-area-inset-top
auf .settings-page-header und Modal-Header in der Mobile-Media-Query
- Swipe links/rechts auf #view-container navigiert prev/next
(≥60 px, überwiegend horizontal, < 700 ms)
- Version v3 → v4 (auch SW-Cache)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Macht Calendarr installierbar (Manifest + Service Worker) und auf
Smartphones bedienbar — additive Änderungen, kein Refactoring der
bestehenden Logik, Theme/Variablen unverändert.
PWA:
- frontend/manifest.json (theme #4285f4, bg #0e0e14, name/icons/scope)
- frontend/sw.js (cache-first für Statics, network-first für /api/*)
- frontend/icons/icon-192.png + icon-512.png + icon.svg
- backend/main.py: Routen für /manifest.json, /sw.js, /icons/* damit
diese Pfade nicht vom SPA-Fallback abgefangen werden
- index.html: manifest-Link, theme-color, apple-touch-icon, apple-* Meta
- app.js: Service-Worker-Registrierung am Ende
Mobile (≤ 768px, additiv am Ende von app.css):
- Sidebar als Overlay mit body.sidebar-open + Backdrop-Element
- View-Switcher horizontal scrollbar wenn er nicht passt
- Monatsansicht zeigt nur farbige Punkte statt Titel
- Wochenansicht reduziert auf Tagesspalte (heute) wenn heute in der
Woche ist (via :has()), sonst Standard-7-Spalten
- Modale auf voller Breite/Höhe
- Tap-Targets ≥ 44px (icon-btn, btn)
- Kein horizontaler Page-Overflow
- iOS-Safe-Area für Notch/Home-Indicator
Version v2 → v3.
Macht Calendarr installierbar (Manifest + Service Worker) und auf
Smartphones bedienbar — additive Änderungen, kein Refactoring der
bestehenden Logik, Theme/Variablen unverändert.
PWA:
- frontend/manifest.json (theme #4285f4, bg #0e0e14, name/icons/scope)
- frontend/sw.js (cache-first für Statics, network-first für /api/*)
- frontend/icons/icon-192.png + icon-512.png + icon.svg
- backend/main.py: Routen für /manifest.json, /sw.js, /icons/* damit
diese Pfade nicht vom SPA-Fallback abgefangen werden
- index.html: manifest-Link, theme-color, apple-touch-icon, apple-* Meta
- app.js: Service-Worker-Registrierung am Ende
Mobile (≤ 768px, additiv am Ende von app.css):
- Sidebar als Overlay mit body.sidebar-open + Backdrop-Element
- View-Switcher horizontal scrollbar wenn er nicht passt
- Monatsansicht zeigt nur farbige Punkte statt Titel
- Wochenansicht reduziert auf Tagesspalte (heute) wenn heute in der
Woche ist (via :has()), sonst Standard-7-Spalten
- Modale auf voller Breite/Höhe
- Tap-Targets ≥ 44px (icon-btn, btn)
- Kein horizontaler Page-Overflow
- iOS-Safe-Area für Notch/Home-Indicator
Version v2 → v3.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Home Assistant unterstützt keinen Password-Grant — deshalb kam immer
"Ungültige Anmeldedaten", egal was eingegeben wurde. Jetzt wird der
Nutzer nach demselben Muster wie bei Google zur HA-Login-Seite
weitergeleitet, meldet sich dort an und kommt zurück zu Calendarr.
Änderungen:
- Neuer POST /api/homeassistant/auth-url und GET /callback Endpoint
- Account speichert client_id für spätere Token-Refreshes
- Modal: "Benutzername/Passwort" → "Mit Home Assistant anmelden"
- Frontend behandelt ?ha_connected=1 / ?ha_error=... nach Rückkehr
- Version v1 → v2
Home Assistant unterstützt keinen Password-Grant — deshalb kam immer
"Ungültige Anmeldedaten", egal was eingegeben wurde. Jetzt wird der
Nutzer nach demselben Muster wie bei Google zur HA-Login-Seite
weitergeleitet, meldet sich dort an und kommt zurück zu Calendarr.
Änderungen:
- Neuer POST /api/homeassistant/auth-url und GET /callback Endpoint
- Account speichert client_id für spätere Token-Refreshes
- Modal: "Benutzername/Passwort" → "Mit Home Assistant anmelden"
- Frontend behandelt ?ha_connected=1 / ?ha_error=... nach Rückkehr
- Version v1 → v2
Neue version.js als Single Point of Truth (APP_VERSION).
Sidebar, Login-Screen und Impressum-Modal zeigen die aktuelle
Version an — ab jetzt bei jeder Änderung v2, v3 ... hochzählen.
Startet bei v1.
Neue version.js als Single Point of Truth (APP_VERSION).
Sidebar, Login-Screen und Impressum-Modal zeigen die aktuelle
Version an — ab jetzt bei jeder Änderung v2, v3 ... hochzählen.
Startet bei v1.