Force resp.encoding='utf-8' in _fetch_ics so umlauts (ö/ä/ü) from sources
like Ferienwiki.de are decoded correctly instead of showing as ö/ä/ü.
Add default_event_duration_minutes to UserSettings (nullable int, same pattern
as default_reminder_minutes) with DB migration, GET/PUT in settings_router.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Render event descriptions as sanitized HTML (links/line breaks) instead of
raw escaped text; no script execution
- Reminder picker: presets + custom number+unit (minutes/hours/days/weeks)
- Grey out + hint the reminder editor when a calendar's notifications are off
(reminders are kept, just not fired)
- New synced setting default_event_duration_minutes (default 60) for new events
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
All-day continuation bars now use CSS clip-path polygon for the tapered
arrow shape; bars stay inset within the popup (no edge bleed). Version v51.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
All-day events in the "+N weitere" popup now render as colored bars matching
the month-grid style: continues-left/right arrows when the event spans beyond
the selected day. Timed events keep dot+time+title format. Version bump v50.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Tab title shows only "Calendarr" (version stays in sidebar/impressum)
- Fix lane assignment in month view: use 2D per-column occupancy grid instead of
global colEnd tracking; eliminates empty rows caused by non-overlapping events
being pushed to lower lanes
- "+N weitere" now opens an inline popup listing all events for that day
(all-day first, then by time); clicking an event opens its detail popup
- Double-click on day cell navigates to day view (was already implemented)
- Remove hardcoded "v18" from login screen impressum button
- Bump version to v49
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Date picker: clicking a day only selects it now; the choice is committed
when you leave the picker (click outside) or press Save — for both date and
datetime modes (previously datetime discarded on click-away, and date
committed instantly on click, neither of which was wanted).
- The day right-click context menu ("Neuen Termin erstellen") now closes when
an event popup opens (the event row stops propagation, so its own
outside-click closer never fired).
- version v47 -> v48.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A native <select> can't render the group icon SVG, so the calendar picker
is replaced with a styled dropdown: colour dot + group icon + name, grouped
by account/source with a "Groups" section. The native <select> is kept
hidden as the value holder + change-event source, so all existing save and
private/reminder-row logic is unchanged. Click-outside / Escape close it.
version v46 -> v47.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Stop the blinking text caret / selection on non-editable UI chrome
(headings, buttons, icons, dropdowns): body is user-select:none, with text
selection opted back in for inputs/textareas. Felt like an editable field.
- Reminder options trimmed to 5/15/30 min, 1 h, 1 day, 1 week (dropped 10 min,
2 h, 2 days; added 1 week). Labels now read "5 Minuten vorher", "1 Tag
vorher", "1 Woche vorher" instead of "vor 5 Min." / "vor 1 Tag(en)".
- Group calendars can now be muted (reminder bell) in the sidebar when owned.
- Event modal corners rounded (20px) to match the pill buttons.
- version v45 -> v46.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Event popup: new "Reminders" section for local events — show/add/remove
multiple reminders, prefilled with the global default on new events; sent
in the local create/update body and synced like any other event field.
- Sidebar calendar list: per-calendar reminder bell toggle (PUTs the new
reminders_enabled flag); disabled state shown dimmed.
- Date picker: a day click now commits immediately in date-only mode instead
of requiring an extra "Save" click.
- Start/End: moving the end before the start now shifts the start back
(mirroring the start handler) instead of showing an error toast.
- Calendar dropdown: group calendars moved under a "Groups" optgroup; dropped
the 👥 emoji prefix (a native <select> can't render the icon SVG).
- Month grid: selectedDate defaults to today so the highlight no longer
follows the cells while navigating before any click.
- Today cell tint now derives from --today-color instead of hardcoded blue.
- version v44 -> v45.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New boolean column on every calendar type (CalDAV, local, Google, Home
Assistant, iCal subscription), default ON so existing data is unchanged.
Lets a calendar be shown without generating reminders/notifications.
- models.py: reminders_enabled on the five calendar models
- main.py _migrate(): ALTER TABLE ADD COLUMN for each table
- caldav/google/homeassistant/local/ical routers: expose the flag in the
calendar serialization and accept it in the calendar/subscription update
endpoints
Clients map event -> (source, calendar_id) -> reminders_enabled to skip
scheduling notifications for disabled calendars.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
local_events gains a `reminders` TEXT column (comma-separated minutes-before-
start, like exdate); EventCreate/EventUpdate accept a `reminders: [int]` list
and build_local_event_dict emits it back as a list. user_settings gains
`default_reminder_minutes` (nullable int, null = off), exposed/updatable via
/api/settings (explicit null persists as off). Migrations added in _migrate().
Clients (iOS/Android) schedule the OS notifications locally from these.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Moved the group member filter from a top bar into the left sidebar (a
"Mitglieder" section that replaces the personal calendar list while a group is
active). Each member row has a checkbox (show/hide their events) and a colour
dot that opens the colour picker to recolour that member just for this viewer —
stored per-device in localStorage and applied over the server colour. Bumped v44.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Group icons are semantic keys rendered as inline SVG (mirroring iOS SF Symbols /
Android Material) in the picker, group list and sidebar flags — instead of OS
emoji that vary per platform. Legacy emoji values still render as a fallback.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Group icons move from OS-emoji (which render differently per platform) to
semantic keys rendered natively per client. The combined view's display_title
therefore no longer embeds an icon glyph — group-calendar events are
distinguished by their colour; only the owner/creator first-name is prefixed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
In the group overlay a per-member filter bar appears under the banner: tap a
member (or the group calendar) to hide/show their events, Outlook-style.
Filtering is client-side by event owner; members + colours load from the group
detail. Bumped version.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Use the server-provided display_title (group icon + owner prefix) instead of
the client-side prefix, so web/iOS/Android show group events identically.
Falls back to the local prefix for older servers. Bumped to v41.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Findings from the security review:
- HIGH: private local events leaked in full (title/location/description) to
anyone who could READ a shared or group calendar via GET /api/caldav/events —
the private_event_visibility rule was only enforced in /groups/{id}/combined.
Now enforced in the merge read too, via a shared helper (apply_event_privacy)
so the two paths can't drift.
- HIGH: 'busy' masking was a blacklist that still leaked creator identity,
source-calendar name, recurrence rule and per-event colour. Replaced with a
whitelist (mask_busy_event): only timing/identity/render fields survive.
- MEDIUM: .ics import had no size limit (raw = await file.read()) → memory DoS.
Now capped at 5 MB (413), read before creating any calendar.
- LOW/INFO: profile email now checked for uniqueness + basic format; display
name / username / email length-capped and control-chars stripped.
Deferred (tracked): RRULE expansion cap at the trust boundary, SQLite
PRAGMA foreign_keys + ON DELETE cascade, and JWT-by-user-id + token version.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The group-event icon + owner-name prefix was being done per-client (iOS only),
so web/Android were inconsistent. The /groups/{id}/combined endpoint now emits
a decorated `display_title` per event (group's own icon for group-calendar
events, owner first-name for other members' events) while keeping the raw
`title` for editing. All clients can render this identically. 18 tests pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The event detail popup looked plainly "HTML". Redesigned it: translucent
glass surface (backdrop-blur), the event's colour as an accent strip + tinted
header + glowing dot, body rows with subtle leading icons (time/location/notes/
calendar/creator), rounded 16px corners, layered shadow, and a soft scale+fade
entrance (respecting prefers-reduced-motion). Body rows restructured to
icon+text; JS now drives row visibility and sets --ev-color. Bumped to v40.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mein voriges flex/align-center hat "1 JUN" in die Mitte der ganzen Zelle
geschoben. Jetzt: Tageszahl bleibt oben links (wie alle Tage), Monatskürzel
sitzt klein/dezent inline daneben (cell-day inline-flex + kleiner Marker).
Version v38.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Pro Mitglied eine Farbe (group_members.color, auto aus Palette, vom Owner
oder Mitglied selbst änderbar via PUT /groups/{id}/members/{uid}/color).
- Gruppentermin-Farbe = Farbe des Gruppenkalenders.
- API liefert Farben aus: GET /groups & /groups/{id} (member.color,
group_calendar_color), GET /groups/{id}/combined (display_color pro Event)
-> Apps können dieselben Farben anzeigen. Test ergänzt (18 grün).
- Web nutzt display_color; Gruppenkalender im Termin-Editor mit 👥 markiert
(Gruppentermine ohne Gruppenansicht erstellbar); Mitglieder-Farben im
Verwalten-Dialog editierbar. Version v37.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Monatsmarker ("JUN") sitzt jetzt inline neben der Tageszahl ("1 JUN") statt
darüber -> einheitliche Zeilenhöhe; Termine der Woche rutschen nicht mehr
nach unten und überlappen nicht mehr mit "+X weitere".
- Gruppenkalender erscheint in "Meine Kalender" (mit 👥-Markierung) und kann
aus-/eingeblendet werden; Besitzer kann ihn umfärben. Recolor fremder
Kalender abgefangen (nur Besitzer). Version v36.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- "+ Gruppentermin"-Button im Gruppenansicht-Banner: oeffnet den Editor mit
dem Gruppenkalender vorausgewaehlt -> jeder kann direkt eintragen.
- Banner + aktive Gruppe nutzen jetzt die Akzentfarbe (color-mix, Fallback)
statt Blau. Version v35.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Gruppe: wählbares Emoji-Icon (groups.icon-Spalte + PUT /api/groups/{id});
wird in der Sidebar statt des Zahnrads angezeigt; Verwalten jetzt klares "⋯".
Gruppe umbenennen möglich (war vorher gesperrt).
- "Meine Kalender": der aktuell für Gruppen sichtbare Kalender wird mit 👥
gekennzeichnet.
- Gruppenansicht: Gruppenkalender-Termine zeigen, wer sie hinzugefügt hat
(👥 Vorname: Titel) und sind nach Ersteller eingefärbt; jeder kann weiterhin
Termine im Gruppenkalender anlegen. Version v34.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Kombinierte Ansicht: kryptisches "[SC]"-Initialen-Präfix ersetzt durch den
Vornamen ("Guido: Titel") für fremde Termine; eigene Termine ohne Präfix;
Gruppen-Termine mit 👥. Zusätzlich feste Farbkodierung pro Besitzer, damit
jedes Mitglied als Gruppe lesbar ist. Version v33.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Profil: Anzeigename + Login-Name editierbar (vorher Benutzername read-only).
Login-Namenwechsel speichert den frisch zurueckgegebenen Token.
- Menue/Dropdown und "Erstellt von"/Picker zeigen den Anzeigenamen.
- localStorage-User um display_name ergaenzt. Version v32.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Plus-Icon (Kalender/Gruppen hinzufuegen): fehlerhafter SVG-Pfad (v11 statt
v6) -> korrekter, symmetrischer Plus-Pfad.
- Entfernen/Auge-Button per display:none statt opacity:0 -> reserviert keinen
Platz mehr; Kalendername nutzt volle Breite und kuerzt erst beim Hover
(wenn das Auge erscheint). Version v31.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Beim Ziehen wandert die Zeile jetzt live zwischen die anderen, die Liste
macht sichtbar Platz an der Zielposition. Container-dragover wird nur einmal
gebunden (kein Listener-Stacking pro Render). Version v30.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Die Quelle/Konto stand inline rechts und hat die Kalendernamen abgeschnitten.
Jetzt im title-Tooltip des Eintrags ("Name · Quelle"); der Name nutzt die
volle Breite. Version v29.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Sidebar: eine flache Kalenderliste statt Quellen-Gruppen; Quelle/Konto
klein-grau inline rechts neben dem Namen; per Drag&Drop sortierbar
(Reihenfolge pro Geraet in localStorage).
- Gruppenkalender serverseitig auch beim Besitzer als group:true markiert
-> erscheint nicht mehr in der "Fuer Gruppen sichtbar"-Auswahl und nicht
in der normalen Kalenderliste (nur unter Gruppen).
- Settings-URL-State: uiSettingsOpen wird beim Init aus der URL gesetzt,
bevor das erste writeUrlState() es ueberschreibt -> Reload bleibt jetzt
wirklich in den Einstellungen.
- Auswahl-Markierungen (Mitglieder/Gruppen-Sichtbar) in Akzentfarbe,
CSS-gezeichnet statt blauer Emoji. Version v28.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Import: Dedupe doppelter UIDs innerhalb der Datei (Nextcloud exportiert
wiederkehrende Termine als mehrere VEVENTs gleicher UID) -> kein
UNIQUE-constraint-500 mehr; Commit abgesichert. Test ergaenzt (15 gruen).
- Picker (Gruppen-Sichtbarkeit + Mitglieder): als <div>-Zeilen statt <label>,
damit die globale ".form-group label"-Uppercase/Grau-Regel das Layout nicht
mehr zerschiesst. Saubere .pick-row-Optik (Checkbox/Radio links, Name links).
- Einstellungen haben jetzt eigenen URL-State (#...&settings=1): Reload/Cache-
leeren bleibt in den Einstellungen statt zur Kalenderansicht zu springen.
- Version v27.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Einstellungen: neuer "Kalender"-Abschnitt – Radio-Auswahl, welcher eigene
Kalender fuer Gruppenmitglieder sichtbar ist (group_visible_calendar_id).
- Sidebar: geteilte Kalender in eigener Sektion "Mit dir geteilt" (mit
Besitzername) statt inline bei "Meine Kalender"; Gruppenkalender dort
ausgeblendet (erscheinen unter Gruppen).
- Lokale Checkbox tolerant: geteilte/Gruppen-Kalender werden client-seitig
ein-/ausgeblendet (kein 404-PUT als Nicht-Besitzer).
- Import-Fehler: zeigt HTTP-Status / "Datei zu gross" statt "unbekannter
Fehler" (z.B. nginx 413). Version v26.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Neues user_settings.group_visible_calendar_id: jedes Mitglied waehlt EINEN
lokalen Kalender, der in seinen Gruppen sichtbar ist. Die kombinierte
Ansicht ueberlagert nur diesen (statt aller) Kalender je Mitglied + den
Gruppenkalender; private Termine weiter gefiltert. Settings GET/PUT erweitert
(nullbar). Tests angepasst + ergaenzt (14 gruen).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Eigene .group-member-item-Klasse statt der generischen Picker-Klasse —
behebt versetzte Checkboxen / rechtsbuendige Namen. Version v25.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Bisher bekamen /static/js und /static/css max-age=7200. Da index.html
no-cache ist, konnte eine frische HTML mit 2h-altem, gecachtem JS/CSS
gepaart werden — neue Features (z.B. Gruppen-Button) ohne passenden
Handler. JS/CSS revalidieren jetzt bei jedem Load (304 wenn unveraendert);
Icons & uebrige Assets behalten 2h. Deploys greifen so sofort beim Reload.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Sidebar-Sektion "Gruppen": Liste, Erstellen (Name + Mitglieder-Picker),
Verwalten (Mitglieder hinzufuegen/entfernen), Loeschen.
- Gruppenansicht: laedt /api/groups/{id}/combined fuer den sichtbaren
Bereich; Event-Titel werden mit Besitzer-Initialen bzw. Gruppen-Icon
praefixt; Banner mit "Gruppenansicht verlassen".
- Server: GET /api/local/calendars liefert nun auch Gruppenkalender
(group:true, read_write) fuer Mitglieder, damit sie im Editor waehlbar
sind. Test ergaenzt (13 gruen).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Ersteller-Zeile im Event-Popup (nur wenn Ersteller != aktueller User).
- Privat-Toggle im Event-Editor (nur lokale Kalender) + Sichtbarkeits-
Auswahl (hidden|busy) in den Einstellungen.
- Lokale Kalender in Settings & Sidebar: Teilen/Importieren/Exportieren-
Aktionen (nur eigene; geteilte mit "geteilt von"-Badge, kein Loeschen).
- Share-Modal: Benutzerverzeichnis mit Suche, read/read_write, Freigaben
entfernen.
- api.js: download()-Helper fuer iCal-Export (Blob).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Beim letzten Beta->Master-Merge sind die <<<<<<< / ======= / >>>>>>>
Marker mit committet worden. Das hat i18n.js mit einem SyntaxError beim
Parsen abgebrochen und damit den gesamten Frontend-Start kaputt gemacht
(=> komplett schwarze Seite, weil applyTheme nie lief).
Acht Bloecke aufgeloest, in allen Faellen die HEAD-Seite behalten
(neue Features: copy-Key, URL-State, all-day-continues-Logik, Event-
Popup-Header). v22 / sw cache v22.
Vorher waren "Calendarr v18" in index.html hardcoded und wurden bei
Releases nie mit gebumpt — v19/v20 wurden zwar in version.js gepflegt,
landeten aber nie im Tab-Titel. Jetzt liest calendar.js APP_VERSION
direkt aus version.js und setzt sowohl document.title als auch das
Impressum-Footer-Label, damit das nicht mehr auseinanderlaufen kann.
v21 / sw cache v21
- 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.