Die Trennlinie hat jetzt eine 'Stufen'-Form: unten unter den letzten
Tagen des Vormonats in derselben Zeile, dann links runter zum 1. des
neuen Monats, dann oben über die ersten Tage des neuen Monats.
So ist die Monatsgrenze visuell vollständig umrandet.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Vier Korrekturen:
- Linie verschwand hinter Events: Pseudo-Elemente mit z-index 4 statt
box-shadow inset, damit Trennlinien immer über den Event-Bars liegen
- Waagerechte Linie auch bei Monatswechsel mitten in einer Zeile (vorher
nur wenn der Monat am Zeilenanfang begann)
- "1" verschwand hinter Events: cell-day und month-marker bekommen
z-index 3 + position relative, plus Events-Overlay wird in Zeilen mit
Monatsmarker um ~26px nach unten geschoben
- Mehr Abstand zwischen Monatskürzel und Trennlinie (padding-top 8px,
margin-bottom am marker positiv statt negativ)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
In der rolling Monatsansicht wird jetzt am Monatswechsel:
- eine dickere Trennlinie gezeichnet (links bei Wechsel mitten in Zeile,
oben bei Zeilenstart)
- das 3-Buchstaben-Monatskürzel (z.B. JUL, AUG) groß über der "1"
angezeigt
Beide Farben (Linie und Kürzel) sind in den Einstellungen unter
"Farben" individuell anpassbar (Default: #7090c0).
Backend: neue UserSettings-Felder month_divider_color und month_label_color
mit Migration. Frontend: applyTheme setzt entsprechende CSS-Variablen.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
HA's Google-Calendar-Integration unterstützt kein calendar/event/update
und gibt 'not_supported: Calendar does not support event update' zurück.
In dem Fall wird jetzt automatisch der Termin gelöscht und neu erstellt
(beide Operationen werden von der Integration unterstützt). Der Termin
bekommt dabei eine neue UID, aber für den User sieht es wie ein Update aus.
iCal speichert DTEND exklusiv (Tag NACH dem letzten Tag). Bisher
führte das dazu, dass ein Termin mit Ende=18.08 nur bis zum 17.08
angezeigt wurde, obwohl der User 18.08 als letzten Tag erwartete.
Fix: Im Date-Picker arbeiten wir jetzt mit inklusiven End-Daten
('endet am 18.08' = 18.08 ist letzter Tag) und konvertieren beim
Speichern auf exklusiv (DTEND=19.08). Beim Laden umgekehrt: -1 Tag
fürs Anzeigen im Picker.
Betrifft: openEditEventModal, openCopyEditModal, Save-Handler.
Über der Kalenderliste im Kopieren-Menü gibt es jetzt eine Checkbox
'Vor dem Kopieren bearbeiten'. Wenn aktiviert und ein Ziel-Kalender
geklickt wird, öffnet sich der Termin-erstellen-Dialog mit allen
Daten des Quell-Termins vorausgefüllt (Titel, Datum, Ort, Beschreibung,
Farbe, Wiederholung) und dem Ziel-Kalender vorausgewählt.
- populateCalendarSelect: filtert jetzt nach !sidebar_hidden statt
enabled. Unchecked (versteckte) Kalender bleiben so im
Termin-erstellen-Dropdown verfügbar
- buildWritableCalendars: HA-Kalender werden als Kopier-Ziele aufgeführt
- copyEventToCalendar: routet HA-Ziele über /homeassistant/events
Endpoint (vorher fielen sie in den CalDAV-Fallback)
CalDAV-Events hatten bisher kein source-Feld gesetzt. applyCalendarColor
filtert aber via ev.source !== 'caldav', sodass der Patch nie auf
CalDAV-Events angewendet wurde – die Farbe blieb sichtbar bis F5.
Jetzt wird source: 'caldav' beim Anreichern der Events gesetzt.
Nach dem Speichern eines Termins wird das gecachte Event-Objekt
direkt in-place gepatcht und die View neu gerendert. Vorher war die
neue Farbe erst nach F5 sichtbar, weil zwar fetchAndRender(true)
aufgerufen wurde, aber der Render-Pfad das Update nicht zuverlässig
übernommen hat.
Manche HA-Integrationen registrieren nur den WebSocket-Handler, keinen
Service-Call. Die HA-Web-UI nutzt deshalb den WebSocket-Pfad. Calendarr
macht das jetzt auch:
- _ha_ws_call: minimaler WebSocket-Client für eine einzelne Command
- create: erst WS, dann Service-Call als Fallback
- update: nur WS (Service-Call existiert oft nicht)
- delete: nur WS (Service-Call existiert oft nicht)
Neue Dependency: websocket-client==1.8.0
HA's Service-Call-Schema akzeptiert je nach Version verschiedene
Body-Shapes für entity_id. Wir probieren jetzt der Reihe nach:
1. entity_id als String
2. entity_id als Liste
3. target-Wrapper
Wenn alle fehlschlagen, klare Anweisung zum HA-Developer-Tools-Test.
calendar.delete_event schlägt mit 400 fehl, wenn die HA-Integration
das Feature nicht unterstützt (z.B. Google-Calendar via HA hat nur
CREATE_EVENT, kein DELETE/UPDATE).
- Versucht erst Service-Call, dann REST DELETE als Fallback
- Bei 400 wird der User aufgeklärt, dass die Integration vermutlich
kein Löschen unterstützt
- _ha_format_dt: Parst ISO-Datetime zu datetime-Objekt, emittiert
ohne Millisekunden, MIT Timezone-Offset. Vorher landeten Termine
am falschen Datum, weil das Frontend UTC schickt aber wir die
Timezone gestrippt haben → HA hat als lokale Zeit interpretiert
- Leere Strings werden nicht mehr in den Body aufgenommen (HA
Validator könnte diese ablehnen)
- Logging in create/delete/update für besseres Debugging der HA-Calls
- POST /api/homeassistant/events Endpoint mit calendar.create_event
- Frontend: HA-Termine erstellen statt 'nicht unterstützt' Toast
- Datetime-Format an HA-Konvention angepasst:
'YYYY-MM-DD HH:MM:SS' (Space-Separator, ohne Timezone)
- _ha_format_dt Helper für ISO → HA Datetime-Konvertierung
calendar.update_event existiert erst ab HA 2024.6. Wenn der Service
nicht verfügbar ist (400), wird stattdessen delete_event + create_event
verwendet. Funktioniert mit HA 2022.5+.
PUT/DELETE /api/calendars/{entity_id}/{uid} existieren nicht in HA.
Stattdessen: POST /api/services/calendar/update_event und
POST /api/services/calendar/delete_event (HA 2023.x+)
- HA Update/Delete: UID wird URL-encoded (@ → %40), Delete mit
Fallback auf Service-Call API für ältere HA-Versionen
- Lösch-Dialog: Event-Popup wird geschlossen BEVOR der Bestätigungsdialog
erscheint, kein Überlappen mehr
- HA-Events: Update/Delete-Endpoints via HA REST API implementiert
- HA read-only Guard entfernt, stattdessen korrekte API-Anbindung
- Selected-Day: Outline-Ring statt gefüllter Kreis (Today bleibt gefüllt)
- Serien-Löschung: RECURRENCE-ID aus CalDAV-Events erkennen, damit
expandierte Serientermine als recurring markiert werden und der
Lösch-Dialog Einzel-/Serienlöschung anbietet
- caldav_client: del+add statt direkter Zuweisung bei VEVENT-Properties
(behebt "DTSTART MUST appear exactly once" Validierungsfehler)
- HA-Events als read-only behandeln (kein Bearbeiten/Löschen im Popup)
- [object Object] Toast behoben: HA-Events fallen nicht mehr in CalDAV-Pfad
- End-Datum passt sich automatisch an wenn Start geändert wird (Duration bleibt erhalten)
- Erstellen-Button nutzt den aktuell angesehenen Tag statt immer heute
- Monatsansicht: Einzelklick = Tag auswählen, Doppelklick = Tagesansicht, Rechtsklick = Kontextmenü
- CalDAV URL-Matching robuster (Normalisierung, Path-Fallback, calendar_id Parameter)
- iCal-Abo-Termine sind nicht mehr bearbeitbar (Read-Only-Schutz)
- Wiederkehrende Termine mit RRULE-Support (täglich/wöchentlich/monatlich/jährlich/benutzerdefiniert)
Der bisherige multiDayAllDayEvs-Filter hatte einen Timezone-Fehler
bei der Datumsberechnung (UTC-Parsing vs. lokale Zeit in UTC+2).
Neue Lösung: das bereits korrekt arbeitende alldayLayout wird direkt
als Quelle verwendet. Items mit colEnd > colStart sind mehrtägig —
die Spaltenindizes aus dem Layout ergeben den Tint-Bereich exakt.
Ausblenden: Events werden sofort client-seitig aus dem Cache gefiltert
(calendar_id-Match), kein Netzwerkaufruf für die Ansicht nötig.
Einblenden: fetchAndRender(force, silent=true) überspringt showLoading(),
die aktuelle Ansicht bleibt sichtbar und wird nach dem Fetch aktualisiert.
Mehrere mehrtägige Events am selben Tag erzeugen jetzt einen vertikalen
Farbverlauf (linear-gradient) statt gestapelter Ebenen, bei denen nur
die letzte Farbe sichtbar war.
- fetchAndRender(true) beim Ein-/Ausblenden eines Kalenders erzwingt
einen Neu-Abruf statt Cache-Treffer, damit die Änderung sofort sichtbar ist
- Tint-Berechnung in der Wochenansicht berücksichtigt jetzt auch
mehrtägige Ganztags-Events (z.B. Urlaub), nicht nur mehrtägige
Termin-Events — exclusive Enddaten werden dabei korrekt normalisiert
Der Wochenkalender von Google hat locale-spezifische IDs
(z.B. de.german#weeknum@...) die nicht im alten exakten Set-Filter
gefangen wurden. Dadurch wurde er in die DB gespeichert und
verursachte beim Event-Abruf einen API-Fehler.
Da der try/except die gesamte Kalender-Schleife umschloss, wurden
bei einem einzigen fehlerhaften Kalender alle anderen Events ebenfalls
verloren — Ursache für keine Termine trotz korrektem Token.
- _is_system_calendar(): prüft jetzt auch 'weeknum' als Substring
- _sync_google_calendars(): bereinigt bereits gespeicherte System-Kalender
- get_google_events(): try/except ist jetzt pro Kalender, nicht global
Wenn der Access-Token eines Google-Accounts abläuft und der Refresh
fehlschlägt, wurde die leere Terminliste bisher still zurückgegeben
(kein Log, keine UI-Meldung). Jetzt wird der Fehler geloggt, an den
Aufrufer weitergegeben und als Toast-Meldung im Frontend angezeigt
("Token abgelaufen – bitte Konto trennen und neu verbinden").
Das Events-Endpoint gibt nun {events, errors} statt ein reines Array
zurück; das Frontend extrahiert die Events entsprechend.
Statt nach Farbänderung den Cache zu invalidieren und neu zu laden,
wird calendarColor direkt in-place auf allen gecachten Events gepatcht
und dann nur renderView() aufgerufen. Kein Netzwerk-Request, sofortige
Darstellung der neuen Farbe.
Wenn die aktuelle Ansicht weniger als 4 Wochen vom Cache-Rand entfernt
ist, werden im Hintergrund 8 weitere Wochen in diese Richtung geladen
und in den Cache gemergt. Der Cache wächst damit automatisch mit der
Navigation mit — kein sichtbarer Ladevorgang auch bei langen Sprüngen.
Beim ersten Laden wird ein Fenster von ±8 Wochen um die aktuelle
Ansicht geholt. Wochenweise Navigation trifft danach den Cache
sofort (kein Spinner, kein Netzwerk). Nach echten Datenänderungen
(Event speichern/löschen, Sync, Konto-Änderungen) wird der Cache
invalidiert und neu geladen.
Month view now shows 5 weeks starting from the week containing
currentDate (not fixed to month boundaries), enabling views like
"mid-April to mid-May". Prev/Next buttons jump 4 weeks; mouse
wheel scrolls 1 week at a time with 500ms debounce.
Scrolling in month view was moving currentDate by 7 days, but the
grid always renders the complete month — so 4 scrolls were needed
before any visual change. Now each scroll step advances/retreats
by exactly one month (same as the prev/next buttons).
- Month view: Replaced day-strip+events-area with full-height column
divs (.month-col) so borders extend the full row height and clicking
anywhere in a day column (including below events) navigates to day view.
Events overlay uses pointer-events:none (pass-through) while span bars
and +N-more labels stay pointer-events:all.
- Scroll navigation: Changed wheel handler from 80ms debounce to 500ms
leading-edge throttle — one navigation per trackpad gesture.
- Custom date/time picker (date-picker.js): Dark calendar grid with
prev/next navigation, today/selected highlighting, and a CSS
scroll-snap time scroller (hours 0-23, minutes 0-59) matching the
app's primary color. Language-aware (month names, day headers via t()).
- Event modal datetime inputs replaced with hidden inputs + .dt-display
click targets that open the custom picker. setDtValue() helper keeps
hidden input and display label in sync.
- Month view: Multi-day events render as continuous Google Calendar-style
spanning bars across days/weeks using a greedy lane-packing algorithm.
Timed multi-day events no longer repeat per day.
- Mouse wheel / trackpad scrolls week-by-week in month view, day/week in
other views (debounced, prevents default page scroll).
- datetime-local/date inputs now use color-scheme:dark so the native
browser picker opens in dark mode; calendar icon styled to match.
- Contrast/hour-height selectors redesigned as connected segmented pill
controls instead of individual tiles.
- Hidden calendars list gains proper padding and separator lines.
- "Google Konten" settings panel renamed "Konten" and expanded to show
CalDAV, local calendars, iCal subscriptions, and Google accounts in
one unified panel with sync/disconnect actions.
- New i18n keys added for accounts panel in both de and en.