- Rename: Add google and homeassistant branches to the dblclick-rename
save() handler; backend already accepted name updates for both sources
- Group icon: Pass groupIconForLocalCal(cal.id) into sidebar entries so
the group's custom icon (home/heart/work/…) shows instead of always
defaulting to the people icon
- Share icon: New share_calendar_icon field in UserSettings (+ migration)
replaces the hardcoded SHARE_ICON SVG; users pick from GROUP_ICON_KEYS
in Settings → Darstellung via a new icon-picker row
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The filter-sheet Switch now calls setCalendarHiddenSynced(), which
updates local hiddenKeys immediately AND fires a sidebar_hidden PATCH
to the server for caldav/google/homeassistant calendars. This makes
the quick-hide consistent with the web sidebar across all devices.
CalendarFilterEntry gains a source field so the sheet knows which
backend to target.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- groupVisible calendars get a share icon instead of the people icon;
people icon is reserved for actual group calendars
- sidebar scrollbar hidden (scrollbar-width: none + webkit)
- modal-body scrollbar hidden — fixes Impressum scroll indicator
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Instead of absolute positioning (which caused icons to overlay text),
use min-height:40px on .cal-item so the row is always tall enough for
28px mini-btns. Buttons stay in the flex flow with display:none when
hidden — no space reserved, no text truncation, no height shift on hover.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Buttons (.cal-item-remove, .cal-item-bell) are now position:absolute so
they never participate in flex layout — names get full available width at
all times. Buttons fade in over the right edge of the item on hover with
no layout shift and no row-height change.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- modal-card: overflow:hidden + flex column; modal-body scrolls instead,
so native scrollbar never overlaps the 20px border-radius corners
- cal-item-remove / cal-item-bell: display:none→flex replaced with
opacity:0/pointer-events:none→1/auto, eliminating reflow on hover
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
URL gains &stab=<panel> when settings are open (e.g. #...&settings=1&stab=accounts).
Tab switches update the URL; reload reopens settings on the same tab.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Notifications now show "in 30 Min. · Ort" instead of just the location,
giving users immediate context about how soon the event starts.
Default event duration picker moved from the View section to right below
the profile/privacy group, consistent with the iOS app arrangement.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
- Add reminder editor (presets + custom number+unit) wired through save
- Per-calendar reminder mute: filter-sheet toggle + greyed-out editor hint;
reminders are kept, never deleted
- New AlarmManager-based NotificationScheduler + channel + POST_NOTIFICATIONS;
skips muted calendars
- New synced setting default_event_duration_minutes for new events
Co-Authored-By: Claude Opus 4.8 <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>
Transparent background caused Pixel launcher to inject its own tinted
ring. Replace with #16703D (sampled from iOS icon background) so the
launcher shape-clips cleanly without any visible halo.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Without mipmap-anydpi-v26 XML, Pixel launcher wraps legacy PNGs in a
white ring template. Adding adaptive-icon XML with the iOS-matching
foreground PNG at correct per-density sizes (108dp canvas) eliminates
the nested-ring appearance.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Use Modifier.size(20.dp) instead of height(20.dp) so the spinner is
constrained on both axes and doesn't get cut off horizontally.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Material 3 menus use shapes.extraSmall (4dp default) which looked boxy;
bump to 14dp so all dropdown popups (view switcher, group switcher, ...)
get rounded corners matching the iOS look.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Long-press on a day now opens a preview popup (ported from the iOS
DayContextPreviewView): all-day events as colored bars with tapered
ends when they continue beyond the day, timed events as dot+time+title
rows, plus quick actions (new event, week view, day view)
- Month scroll performance: stable item keys + contentType in the
LazyColumn, remembered Instant.now()/theme colors, remembered callbacks
so visible week rows stay skippable during recomposition
- Fix "+N" and "KW" labels overlapping the lowest event lane (row bottom
padding 6dp -> 16dp, mirrors the iOS fix)
- Event detail: delete is now a quiet red text button instead of a
full-width filled container
- Top bar: hairline divider so the bar/calendar boundary is visible
- FAB: explicit circular shape
Co-Authored-By: Claude Fable 5 <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>
CalEvent gains `reminders` (parsed from the server); AppSettings/SettingsStore
gain `defaultReminderMinutes` (null = off) and it's sent via updateSettings;
createLocalEvent/updateLocalEvent/eventBody and CalendarViewModel.saveEvent
thread `reminders` through. UI (editor + settings picker) and the local
notification scheduling follow next.
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 are semantic keys rendered as native Material icons (GroupIcons)
in the picker, group list, top-bar switcher and banner — mirroring iOS/web —
instead of OS emoji. Legacy emoji values render as a fallback. decorateGroup
fallback no longer prepends a glyph (server display_title is authoritative).
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>
The calendar filter, in group mode, lists the group's members (+ the shared
group calendar) with toggles to hide each individually. Filtering is client-side
via CalendarViewModel.hiddenGroupKeys (gm:<id> / gc), reset on group switch;
members + colours loaded from the group detail.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
CalEvent parses display_title; decorateGroup uses it (group icon + owner prefix
from the server) instead of the hardcoded glyph, with a fallback for older
servers. Raw title kept for editing.
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>
Top-Bar-Gruppenumschalter ist jetzt eine tonale Pille (Akzentfarbe im
Gruppenmodus) statt eines flachen Icons – klar als Button erkennbar; das
Dropdown markiert die aktive Auswahl. Gruppen-Verwaltung: Tipp auf eine Gruppe
öffnet direkt deren Ansicht, Verwalten liegt aufs Zahnrad. Menü-Bottom-Sheet
respektiert jetzt das Navigationsleisten-Inset (+ Scroll-Fallback), sodass
Abmelden/Server wechseln nicht mehr hinter den Android-Buttons liegen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Neuer Gruppen-Verwaltungsscreen (Liste, Erstellen, Verwalten: Name, wählbares
Emoji-Icon, Mitglieder, server-definierte Mitgliederfarben, Löschen) über das
Menü. Top-Bar-Umschalter (Mein Kalender / Gruppen) + Akzent-Banner schalten den
Kalender in die kombinierte Gruppenansicht: Events laufen durch die normale
Cache-/Render-Pipeline (Monat/Woche/Tag/Termine), mit Namens-Präfix der
Besitzer und 👥 für Gruppentermine; im Gruppenmodus kein Sichtbarkeitsfilter.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
AccountsScreen überarbeitet: editierbare Farb-Punkte (lokal, iCal, sowie
Unterkalender von CalDAV/Google/HA via Farbwähler-Dialog); lokale Kalender
zeigen Gruppen-Marker + "geteilt von" und ein Aktionsmenü (Teilen/Importieren/
Exportieren/Löschen je nach Besitz & Berechtigung). Teilen-Sheet mit
Benutzersuche + Berechtigung; Import via OpenDocument, Export via CreateDocument.
Repository: importIcsFile(bytes) + Farb-Update-Methoden; ViewModel um Farb-,
Sharing- und Import/Export-Logik erweitert.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Einstellungen zeigen jetzt ein Profil-Kapitel: Anzeigename (editierbar),
Anmeldename (read-only), E-Mail + Speichern; Privatsphäre (private Termine
beschäftigt/verbergen) und geteilter Kalender (Dropdown der eigenen lokalen
Kalender) — serverseitig geladen und gezielt gespeichert.
Datenebene um Farb-Endpunkte ergänzt (lokal/iCal PUT + setCalendarColor für
caldav/google/ha) als Basis für die Farbbearbeitung.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Event-Detail zeigt "Erstellt von" (wenn != ich) + Privat-Hinweis; Editor hat
Privat-Toggle (nur lokale Kalender, durch saveEvent/Repo durchgereicht).
Login speichert userId + displayName (CredentialStore) für Vergleiche.
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>