131 KiB
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Unreleased
[0.48.2] - 2026-05-06
Changed
-
Brand color refresh: The primary accent color has been updated from cool indigo (
#4F46E5) to a rich, warm amber (#92400Ein light mode,#FBBF24in dark mode) across all design tokens, the logo, and the GitHub Pages documentation site.Why this change? Indigo carried the aesthetic of a productivity tool — focused, corporate, digital. As Oikos has grown into a home for thousands of families, we wanted the visual identity to better reflect what the app actually is: a warm, shared space for everyday life together. Amber — deep and earthy in light environments, bright and inviting in dark ones — communicates exactly that. It evokes warmth, reliability, and the kind of unhurried intimacy that family life deserves.
From an accessibility standpoint, Amber-800 (
#92400E) achieves a contrast ratio of 7.20:1 against white, exceeding the WCAG AA threshold and meeting WCAG AAA. The dark mode value (#FBBF24) maintains the same readability standard. The transition is purely cosmetic — no data, settings, or behavior has changed. -
Logo: updated the gradient on
docs/logo.svgand all inline SVG instances from violet (#8B5CF6to#6C3AED) to amber (#B45309to#92400E).
[0.48.1] - 2026-05-06
Fixed
- Settings: CalDAV and CardDAV "Add Account" modals now correctly display Cancel and Save buttons. Previously, the
onSavecallback ran immediately on modal open, triggering a required-fields validation error against empty fields and leaving the form with no way to submit.
[0.48.0] - 2026-05-06
Added
- Multi-person assignment: tasks and calendar events can now be assigned to multiple family members simultaneously. A new
task_assignments/event_assignmentsjoin table (migration v32) stores the assignments; existing single-user data is migrated automatically. - Avatar stack: task cards, Kanban cards, and the calendar agenda view display stacked avatars for all assigned users (up to 3 visible, then a
+Noverflow badge). - Shared UserMultiSelect component (
public/components/user-multi-select.js): checkbox-based dropdown used in both the task modal and the calendar event modal; replaces the previous single-user<select>. assigned_tofilter extended:GET /api/v1/tasks?assigned_to=<id>andGET /api/v1/calendar?assigned_to=<id>now match any task/event where the user appears in the assignments list.
Changed
- API response for tasks and calendar events now includes
assigned_users: [{id, display_name, color}]array alongside the legacyassigned_to/assigned_name/assigned_colorfields. - Recurring task completion copies all multi-person assignments to the new recurring instance.
[0.47.5] - 2026-05-06
Changed
- Settings — Sync tab: open standards (CalDAV, CardDAV, ICS subscriptions) are now grouped first under a dedicated "CalDAV & CardDAV" section; cloud services (Google Calendar, Apple Calendar) move to a secondary "Cloud Services" section. Fixes a raw
<h2>heading inside the CalDAV card (now usessettings-card__titlelike all other cards). - Navigation — shared sub-tabs component: extracted
renderSubTabs()(public/utils/sub-tabs.js+public/styles/sub-tabs.css) as the single implementation for all sub-module navigation. Settings tabs and kitchen tabs now share the same pill-style bar (icon + label, sticky, horizontally scrollable, group separators). Removes ~120 lines of duplicated CSS fromkitchen-tabs.cssandsettings.css. - Test loader:
test-browser-loader.mjsnow resolves browser-absolute/utils/*.jsimports to thepublic/directory automatically, eliminating the need for per-module stubs.
[0.47.4] - 2026-05-06
Fixed
- Modal: add
onClosecallback toopenModal()so promise-based modals (confirmModal,promptModal,selectModal) resolve correctly on Escape and overlay-click without duplicate event listeners. - Modal: fix
_initialFormTimeoutleak — timeout is now tracked and cancelled on re-open or close, preventing stale dirty-check snapshots. - Calendar: replace
popup.innerHTMLwithinsertAdjacentHTMLin the event popup (project constraint); addtruncateDescription()to cap long event descriptions at 500 characters. - Validation: extend
DATETIME_REto accept ISO 8601 datetimes with milliseconds and timezone offsets; normalise datetime inputs toYYYY-MM-DDTHH:MMbefore storing.
Changed
- Docker: switch from named Docker volume to host-mounted bind mounts;
DATA_DIR(default./data) andBACKUP_DIR(default./backups) can be set in.envto control storage locations. - Startup log: include app version in the server start message.
[0.47.3] - 2026-05-06
Changed
- Documentation: SPEC.md updated to reflect v0.45–v0.47 changes — CardDAV Accounts and CardDAV Addressbook Selection tables added; Contacts table expanded with multi-value fields and CardDAV columns; new contact_phones, contact_emails, contact_addresses sub-tables documented; Birthdays table reflects configurable reminder offset columns; External Calendars table notes apple→caldav migration; Tasks module documents bulk actions; Contacts module documents CardDAV multi-account sync; Birthdays module reflects flexible reminder offsets; Settings module updated for Synchronization tab, module toggles, scheduled backups, and CardDAV UI.
- README: Birthdays feature description updated to reflect customizable reminder offsets; Backup feature description updated to mention automatic scheduled backups.
Removed
- Repository: archived implemented cleanup plan (
docs/designs/2026-05-04-repo-cleanup-design.md→docs/archive/designs/); removed settings sidebar prototype HTML (docs/designs/2026-05-04-settings-sidebar-demo.html).
[0.47.2] - 2026-05-05
Changed
- Dependencies: updated express-rate-limit from 8.4.1 to 8.5.0 (async store initialization support) and tsdav from 2.1.8 to 2.2.0 (native fetch, enhanced OAuth token handling, improved CalDAV/CardDAV sync reliability, security improvements).
[0.47.1] - 2026-05-04
Fixed
- Settings page crash: fixed ReferenceError "loadCalDAVAccounts is not defined" when opening Settings. Root cause: loadCalDAVAccounts and loadCardDAVAccounts were defined inside the render function but called from bindIcsEvents (outside render scope). Functions are now top-level exports with user parameter.
[0.47.0] - 2026-05-04
Added
- Settings UX overhaul: renamed Calendar tab to Synchronization with dedicated sections for Calendar Sync and Contact Sync. Improved information architecture with visual tab grouping using CSS separators between functional areas (module settings, synchronization, personal, administration).
- CardDAV UI: complete user interface for CardDAV contact synchronization in Settings. Add/delete CardDAV accounts (iCloud, Nextcloud, Radicale, Baikal), enable/disable individual addressbooks, manual sync trigger, real-time status indicators. Empty state onboarding for first-time setup.
- Status badges: visual sync status indicators (success, error, syncing) with animated spinner for active syncs across CalDAV and CardDAV integrations.
Changed
- Settings navigation: Calendar tab replaced by unified Synchronization tab containing both calendar and contact sync options. Existing CalDAV calendar accounts remain accessible in the Calendar Sync section.
[0.46.0] - 2026-05-04
Added
- Flexible birthday reminders (#123): customizable reminder offsets for birthdays with preset options (none, at time, 15min, 1h, 1d, 2d, 1w, 2w) and custom intervals (minutes, hours, days, weeks). Users can now configure exactly when to be reminded of upcoming birthdays. Database migration 31 adds
reminder_offset,reminder_custom_amount,reminder_custom_unitcolumns to birthdays table. UI component integrated into birthday modal. Backend service calculates reminder time based on offset and supports disabling reminders when offset is empty.
Fixed
- Service worker protocol guard: added check to skip non-HTTP protocols (e.g., chrome-extension://) in fetch handler to prevent errors with browser extensions.
[0.45.0] - 2026-05-04
Added
- CardDAV contacts integration (#122): generic multi-account CardDAV sync for contacts. Connect multiple CardDAV servers (Nextcloud, iCloud, Radicale, Baikal) simultaneously. 8 new API routes for account management (
/api/v1/contacts/cardav/*): create/delete accounts, test connections, discover/refresh addressbooks, toggle addressbook selection, sync contacts. Per-addressbook enable/disable via checkboxes. New service:server/services/cardav-sync.js. New router:server/routes/cardav.js. Database tables:carddav_accounts,carddav_addressbook_selection. - Multi-value contact fields: contacts now support multiple phones, emails, and addresses per contact. Each entry has a label (e.g., "mobile", "work", "home"), value, and optional
isPrimaryflag. Extends existing contact routes:GET /contacts/:id,POST /contacts,PUT /contacts/:id. Database tables:contact_phones,contact_emails,contact_addresses. Atomic transactions with replacement semantics on update. Backward compatible with legacy single-field contacts. Array validators:validatePhones(),validateEmails(),validateAddresses()with length limits, format checks, and type validation.
[0.44.1] - 2026-05-04
Fixed
- CalDAV migration crash: fixed CHECK constraint violation during v0.44.0 migration that caused container restart loop. The apple→caldav
external_sourceconversion now happens during table rebuild instead of before, preventing the constraint error (#119, #120).
[0.44.0] - 2026-05-04
Added
- Generic CalDAV multi-account sync (#90): replaced single Apple CalDAV integration with a generic multi-account CalDAV solution. Connect multiple CalDAV servers (iCloud, Nextcloud, Radicale, Baikal) simultaneously. Per-account calendar selection via checkboxes in Settings → Calendar. Bidirectional sync with optional outbound target selection per event. Existing Apple CalDAV data is automatically migrated on upgrade. New database tables:
caldav_accounts,caldav_calendar_selection. New service:server/services/caldav-sync.js. New API routes:/calendar/caldav/*. Enhanced UI in Settings and Calendar event modal.
Changed
- Calendar feature description: README.md and docs/SPEC.md updated to reflect multi-account CalDAV support instead of single Apple CalDAV integration.
[0.43.0] - 2026-05-04
Added
- Automatic scheduled backups: database backups are now created automatically on a configurable cron schedule (default: 2 AM daily). Old backups are rotated automatically, keeping only the last N copies (default: 7). Configuration via
.envvariables:BACKUP_ENABLED,BACKUP_SCHEDULE,BACKUP_DIR,BACKUP_KEEP. Settings → Backup displays scheduler status, schedule, retention policy, last backup timestamp, and a manual trigger button.
[0.42.0] - 2026-05-04
Added
- Module toggles (Settings → General, admin-only): individual modules (Tasks, Calendar, Shopping, Meals, Recipes, Birthdays, Notes, Contacts, Budget, Documents) can be disabled to hide them from the navigation. Data is preserved and reappears when the module is re-enabled. Dashboard and Settings remain essential and cannot be disabled.
- Bulk actions for tasks (List view only): select multiple tasks via checkboxes and apply batch operations (mark done, mark open, archive, delete). Bulk select toggle appears in the toolbar; selected count and action bar appear when tasks are checked. Kanban view remains single-task oriented.
[0.41.0] - 2026-05-01
Added
- Birthday badge: the birthdays nav item now shows a badge when any family member has a birthday within the next 3 days.
- Recent filter chips: the task filter bar now shows up to three recently used filters as quick-access chips, persisted in
localStorage. - Calendar icon search: the event icon picker now includes a live search field to quickly find icons by keyword, with results grouped by category.
- Calendar icon categories: event icons are now organised into labelled category groups (transport, sports, health, nature, leisure, social, work, home, food, other).
- Repeat indicator on calendar events: events with a recurrence rule now display a small repeat icon in both month and week views.
- 3-day week view on mobile: the calendar week view automatically switches to a 3-day window on screens narrower than 640 px for better readability.
- Widget size presets: the dashboard widget size selector uses named presets (Tiny, Narrow, Standard, Large, Full) instead of raw grid dimension values.
Changed
- Required-field markers: title fields in the task, event, and budget modals now show a required-field asterisk via the
.required-markerCSS class. - Modal drag handle touch target: the bottom-sheet drag handle has a 44 px tall invisible hit area so it can be grabbed comfortably.
- Swipe affordance: list rows with swipe actions show a subtle chevron hint to signal the gesture.
- Budget tab height: budget tab buttons have a minimum height of 40 px to meet touch-target requirements.
[0.40.1] - 2026-05-01
Changed
- Typography tightening: page titles and modal titles use tighter letter-spacing (
-0.5 px/-0.8 pxon desktop) andtext-wrap: balanceto eliminate orphaned words on wrapped headings. - Warm-tinted shadows: all elevation shadows (
smthroughxl) now use a warm-tinted base colour (rgba(18, 14, 8, …)) that matches the warm neutral palette instead of pure black. - Button radius: regular buttons use
--radius-md(12 px) instead of--radius-sm(8 px), creating a clear visual distinction from text inputs. - Empty-state icons: icons in empty states pick up a 40 % tint of the current module accent colour, making them feel contextually connected to each module rather than uniformly grey.
- Search section labels: category headings inside the search overlay are now sentence-case instead of all-caps, improving readability.
Fixed
- Tabular figures: currency amounts (budget summary cards, transaction list, loan cards, chart rows), weather temperature, dashboard metrics, and calendar time labels now use
font-variant-numeric: tabular-numsso digit columns remain visually aligned.
[0.40.0] - 2026-05-01
Added
- Budget loans tracker (PR #117 by @rafaelfoster): a new Loans tab in the Budget module lets you create instalment-based loans, record individual payments, track remaining balance and due months, and filter budget transactions by loan. Paid-off loans are automatically marked as closed. Full CRUD with confirmation modals.
- Dashboard widget sizes: each dashboard widget now has a configurable size (columns × rows). Sizes are persisted in user preferences and survive page reloads.
- Extended date formats: Settings → General now offers four additional date format options —
MM.DD.YYYY,YYYY.MM.DD,YYYY/MM/DD, andDD/MM/YYYY— alongside the existing formats.
Fixed
dmydate format preserved: the existingDD.MM.YYYYbehaviour of thedmypreference is unchanged for all current users; a newDD/MM/YYYYoption (dmy_slash) is available for those who want slashes.
[0.39.2] - 2026-05-01
Fixed
- Budget date picker: the date input in Budget → New Entry / Edit Entry now uses a native date picker on iOS and Android instead of a plain text field.
[0.39.1] - 2026-05-01
Added
- Swedish translation completed (PR #115 by @olsson82): all previously untranslated strings in
sv.jsonnow have Swedish equivalents (attachment fields, API token settings, budget categories, backup hint, onboarding, offline banner). - i18n gap fix: calendar and notes colour names,
emptyHinttexts, keyboard-shortcut labels,tasks.navLabelOverdue, andbirthdays.photoOptionaladded to all 13 non-German locale files (ar, el, en, es, fr, hi, it, ja, pt, ru, tr, uk, zh).
[0.39.0] - 2026-04-30
Added
- Time format preference: Settings → General now includes a 24-hour / AM·PM toggle. The selected format is persisted in household preferences (backend) and localStorage, and takes effect globally for all time displays in calendar and tasks. Time inputs in modals accept both formats and normalise on blur. The calendar also remembers the last selected view (month / week / day) across sessions.
[0.38.4] - 2026-04-30
Fixed
- Dashboard portrait mode on mobile: layout no longer overflows to landscape width;
overflow: visibleoverride in the Admin Dashboard Layout CSS block has been removed so the correctoverflow: cliptakes effect, and.app-contentnow usesoverflow-x: hidden(instead ofclip) to properly contain layout overflow at the scroll container level
[0.38.3] - 2026-04-30
Fixed
- Dashboard portrait mode on Android: horizontal scrollbar no longer appears due to subpixel overflow in the main scroll container (
overflow-x: clipadded to.app-content)
[0.38.2] - 2026-04-30
Fixed
- Recurring calendar events with
FREQ=WEEKLY;INTERVAL=N;BYDAY=...(N > 1) now correctly skip N−1 weeks between occurrences instead of repeating every week
[0.38.1] - 2026-04-30
Changed
- Docs: SPEC.md —
family_documentsandfamily_document_accesstables added;calendar_eventsextended withiconand four attachment columns;contactsandbirthdaysextended withfamily_user_id; Tasksstatusincludesarchived; Documents module section added; Calendar section updated with icons, file attachments, and overlapping event rendering; Settings section updated with Backup Management tab and family member contact fields - Docs: BACKLOG.md — completed features table brought up to date through v0.38.0 (v0.30.0–v0.38.0 entries added)
- Docs: README.md — Backup entry added to the feature table; Documents entry updated with exact category count
- Docs: CONTRIBUTING.md —
innerHTMLsecurity note updated to reflect currentinsertAdjacentHTML/replaceChildren/esc()pattern; individual test-suite commands listed
[0.38.0] - 2026-04-30
Added
- FAB entry animation now stops after 5 page views (long loop progressive reduction)
- Search keyboard shortcut hint (
/) hides permanently after first keyboard use - Success toasts are suppressed after 50 successful saves to reduce noise for power users
- Empty state CTA button fades in with a short delay to draw attention as the primary action
- Form fields pulse with a red glow on the second or subsequent validation failure on the same field
- Shopping quick-add input shows a brief accent-colour glow after each successful item add
[0.37.2] - 2026-04-30
Changed
- Search bar in More sheet: added hover, active, and focus states with accent colour highlight and subtle scale feedback
- Search bar icon changes to accent colour on hover and press for clearer trigger affordance
- Keyboard shortcut hint (
/) shown inside search bar on desktop as discoverability signal
[0.37.1] - 2026-04-30
Changed
- Bottom navigation: Tasks replaces Search as a primary tab bar item
- More menu: layout changed from two columns to a three-column grid (two rows of three)
- Search: embedded as a narrow bar at the top of the More sheet instead of a standalone bottom-nav button
[0.37.0] - 2026-04-30
Added
- Calendar: drag-and-drop file upload dropzone for event attachments (consistent with Documents module)
- Calendar: popup positioning now fully viewport-aware (flips above anchor if insufficient space below)
Fixed
- Calendar: event attachments with raw base64 data (no
data:prefix) now render correctly as images - Calendar: "file too large" error is now shown correctly when saving an oversized attachment
Changed
- Theme init script extracted from inline
<script>to/theme-init.jsfor a stricter Content Security Policy ('self'only, no SHA hash) - Modal overlay is now vertically centered on mobile (with safe-area insets) matching desktop behavior; rounded corners on all sides
- Modal
max-heightis computed from100dvhminus safe-area insets for accurate sizing on notched devices
[0.36.1] - 2026-04-29
Fixed
- Date input: default date format changed from US (
MM/DD/YYYY) to day-month-year (DD.MM.YYYY) for new users - Date input: dot-separated dates (
DD.MM.YYYY) are now accepted in addition to slash-separated dates - Date input:
dmyplaceholder and display format updated to use dots instead of slashes
[0.36.0] - 2026-04-29
Added
- Navigation: Kitchen (Meals/Recipes/Shopping) is now grouped as a single "Küche" entry in the desktop sidebar, consistent with the mobile bottom bar
- UX: empty states in Tasks, Notes, Contacts, Shopping, Recipes and Budget now include a primary CTA button that triggers the page FAB
- UX:
friendlyError(err)helper added towindow.oikos; unhandled promise rejections now show status-code-aware messages (offline, forbidden, not found, server error, timeout) instead of raw error text - i18n: five new
common.error*keys (offline, forbidden, notFound, server, timeout) added to all 15 locale files
Changed
- Navigation: more-button icon changed from
grid-2x2toellipsis(matches the sheet it opens) - Navigation: desktop sidebar expands labels at 1 280 px instead of 1 440 px
- UX: search overlay input field is now at the top, results below (standard top-to-bottom scan path)
- UX: touch targets for kitchen tabs and shopping list tabs raised to 44 px (iOS minimum)
- UX: dashboard metric values enlarged to
xl/boldand labels styled as2xs/uppercasefor clearer data hierarchy - Onboarding: step 2 text and icon updated to accurately describe the navigation structure (···-button and module groups); step 3 text and icon updated to explain the FAB and swipe gestures
[0.35.0] - 2026-04-29
Added
- Settings: new admin-only "Backup Management" tab with database download and restore via file upload (drag-and-drop supported)
- API: admin-only endpoints
GET /api/v1/backup/database,POST /api/v1/backup/restore,GET /api/v1/backup/status - Database:
backupToFile()andrestoreFromFile()helpers with validation against Oikos schema and automatic pre-restore rollback copy - CLI:
scripts/restore-backup.jsfor operational restores outside Docker - Docs: updated installation guide with Docker Compose backup/restore commands
- i18n: backup management keys added to all 15 locale files
[0.34.1] - 2026-04-29
Fixed
- Kitchen tabs bar disappeared after navigating to Shopping, because the page overwrote the container a second time after loading data
[0.34.0] - 2026-04-29
Added
- Navigation: new "Küche" (Kitchen) button in the bottom bar groups Meals, Recipes and Shopping behind a single entry point with a persistent tab bar inside each sub-module
- Navigation: new "Suche" (Search) button added to the bottom bar for one-tap access to the search overlay
- Kitchen tabs bar: sticky segment-control (Meals / Recipes / Shopping) injected at the top of each sub-module page; remembers the last active tab via sessionStorage
- Keyboard shortcuts:
g knavigates to Kitchen (last tab),g k m→ Meals,g k r→ Recipes,g k s→ Shopping - i18n:
nav.kitchen,nav.searchandshortcuts.goKitchenkeys added to all 15 locale files
Changed
- Navigation: bottom bar reorganised — Dashboard, Calendar, Küche, Suche, Mehr (5 items)
- Navigation: Meals, Recipes and Shopping removed from the More sheet; they are accessible via the Kitchen tab bar and the sidebar on desktop
- More sheet: reduced from 3-column to 2-column grid for larger touch targets; search trigger removed
- More sheet: drag-handle added at the top; swipe-down gesture closes the sheet
[0.33.1] - 2026-04-29
Changed
- Navigation: removed the dedicated Search button from the bottom bar; the bottom bar now shows three primary module links plus the More button
- Navigation: the More sheet now opens with a full-width pill-shaped search trigger at the top, replacing the grid-cell search item
- Search: the search overlay input field is now positioned at the bottom of the screen (thumb zone) instead of the top
[0.33.0] - 2026-04-29
Added
- Calendar: overlapping timed events in week and day views now render side-by-side using a column-layout algorithm instead of stacking on top of each other
- Calendar: events support optional file attachments (images, PDFs, and office documents up to 5 MB); images are shown inline in the event popup, other files as a download link
- Birthdays: redesigned edit modal with photo avatar and name/date fields displayed side by side
Fixed
- Calendar: attachment i18n keys are fully translated in all 15 locales (German translation added; Portuguese diacritics corrected)
[0.32.3] - 2026-04-29
Added
- Typography: Plus Jakarta Sans variable font (200–800 weight) self-hosted under
public/fonts/— consistent branding across all platforms with no CDN dependency at runtime - Dashboard: visual hierarchy for primary widgets — Tasks and Calendar always span two columns; Weather and Shopping span two columns at the three-column breakpoint only
- Dashboard: subtle accent border on primary (wide) widgets using the active module accent colour
Changed
- Module toolbars (Tasks, Notes, Calendar, Contacts, Shopping) are now sticky — they remain visible at the top while scrolling long lists
Fixed
- Sticky toolbars: changed
overflow: hiddentooverflow: clipon Calendar, Notes, Contacts, and Shopping page roots soposition: stickyworks correctly on child toolbar elements - Dashboard: explicit
grid-column: span 1for secondary widgets at the 768 px (two-column) breakpoint to prevent implicit layout jumps
[0.32.2] - 2026-04-29
Changed
- Bottom navigation restructured: Dashboard, Tasks, Calendar as first three primary slots; Search promoted to a dedicated fourth bottom-nav button (no longer buried in the More sheet)
- Sidebar tooltips added for the collapsed mode (1024–1439 px) — hovering an icon now shows a label tooltip so module names remain discoverable without expanding the sidebar
[0.32.1] - 2026-04-29
Fixed
- i18n: complete documents and tasks translations for all 15 locales — gridView, listView, viewToggle, file labels, action labels, toast messages, status labels, and the five new tasks keys (statusArchived, archiveButton, archivedToast, kanbanArchived, reminderNeedsDueDate) were untranslated in all non-English locales (#103)
[0.32.0] - 2026-04-29
Added
- Documents: new Family Documents module — upload, search, and manage family files (PDF, images, text, Office) with grid/list view, per-document visibility (family, selected members, private), category tagging (medical, school, identity, insurance, finance, home, vehicle, legal, travel, pets, warranty, taxes, work, other), archive/restore, and download actions (#104)
- Documents: drag-and-drop upload area in the new-document modal (#104)
- Tasks: archive button on task cards; archived status supported in kanban view and filter (#104)
- Tasks: inline reminder preset UI — offset from due date/time with 15 min, 1 h, 1 d, 2 d, 1 w, 2 w, or custom offset presets (#104)
- i18n: Documents and updated Tasks keys translated in all 15 locales
Fixed
- Modal: discard-changes confirmation no longer corrupts overlay state when a confirm dialog is triggered from within another modal (#104)
- RRule: "Until" date field moved inside the recurrence options row for better layout (#104)
[0.31.2] - 2026-04-29
Added
- Settings: edit button (pencil icon) on each ICS subscription row — opens a modal to update name, color, and shared visibility via the existing PATCH endpoint (#100)
[0.31.1] - 2026-04-29
Fixed
- Settings: birthday date fields (profile, new member, edit member) now use the native date picker on iOS
- Birthdays: birth date field now uses the native date picker on iOS
[0.31.0] - 2026-04-29
Added
- Family: phone, email, and birthday fields on family member records, automatically synced to Contacts and Birthdays
- Settings: dedicated "Family Management" tab (admin-only) for managing family members including contact details
- Settings: dedicated "API Tokens" tab (admin-only) for token management
- Calendar: local tooth SVG icon for dentist events replaces the drill icon (migration 24 restores tooth icon for existing events)
- i18n:
reset,tabFamily,tabApiTokens, and family member field keys translated in all 15 locales
Changed
- Settings: avatar editor uses icon buttons instead of a file input label for a cleaner UX
- Settings: tab bar constrained to standard app width so all tabs fit in one row on desktop
- Family members page moved from Account tab to its own Family tab (admin only); Account tab stays focused on personal profile and password
Fixed
- Theme toggle:
data-themeattribute removed when reset to system default (previously left stale) - Calendar: dentist icon normalised —
toothis now the canonical stored value (drillaccepted as alias for backwards compatibility) - i18n: missing translations for family member fields added to ar, el, es, fr, hi, it, ja, ru, sv, tr, uk, zh
[0.30.3] - 2026-04-28
Changed
- Birthdays: all family members can now view, edit, and delete any birthday entry regardless of who created it
[0.30.2] - 2026-04-28
Fixed
- Calendar: date inputs in the event modal reverted from
type="text"totype="date", restoring the native date picker on iOS and other mobile browsers
[0.30.1] - 2026-04-28
Fixed
- i18n/el: corrected typo
Διδαγραφή→Διαγραφήinrecipes.deleteConfirm(fix was missing from v0.30.0 release build)
[0.30.0] - 2026-04-28
Added
- i18n: recipe strings translated in 13 locales (ar, el, es, fr, hi, it, ja, pt, ru, sv, tr, zh, uk) — contributed by @baragoon
- i18n:
emptyHint.recipesadded to all updated locales; Ukrainian locale additionally gains fullemptyHinttranslations for all modules - i18n:
nav.recipestranslated in all 13 locales
[0.29.3] - 2026-04-28
Fixed
- Dashboard: weather widget background gradient was overridden by the higher-specificity
.dashboard .widget { background: var(--color-surface) }rule, causing white text on a white background in light mode
[0.29.2] - 2026-04-28
Changed
- Docs: SPEC updated with Reminders, Birthdays, and Family Management tables and module sections; Users table reflects
family_roleandavatar_datacolumns - Docs: README lists Reminders and Birthdays in the feature tagline and Highlights section
- Docs: BACKLOG completed-features table brought up to date through v0.29.1
[0.29.1] - 2026-04-28
Changed
- Dependency:
express-rate-limitupdated from 8.3.2 to 8.4.1
[0.29.0] - 2026-04-28
Added
- Calendar: events can now have a custom icon chosen from 102 validated Lucide icons via a visual icon picker — icon is persisted in the database (
calendar_events.icon) - Calendar: reminders now offer additional presets (2 days, 1 week, 2 weeks before) plus a fully custom option with configurable number and time unit (minutes/hours/days/weeks)
- Calendar: birthday events are automatically assigned the
cakeicon when synced to the calendar - i18n: new reminder preset and custom-reminder labels added to all 16 locales
Changed
- Calendar, Tasks, Meals, Birthdays, Budget: date inputs now use locale-aware text fields (respecting the user's configured date format: MDY / DMY / YMD) instead of native
<input type="date">— inputs auto-correct format on blur - Calendar:
formatDateinside the module now delegates to the i18n-awareformatDatefromi18n.jsfor consistent locale formatting across all views
Fixed
- Calendar: dentist icon
tooth(unavailable in Lucide) replaced bydrill; existing events withicon = 'tooth'are migrated todrillvia migration 22 - Calendar: reminder
remind_atis now calculated correctly for all-day events (usesT09:00as base time instead of midnight)
Database
- Migration 21:
ALTER TABLE calendar_events ADD COLUMN icon TEXT NOT NULL DEFAULT 'calendar' - Migration 22: normalizes legacy
toothicon values todrill
[0.28.1] - 2026-04-27
Fixed
- Google Calendar:
upsertGoogleEventsuseddb.transaction()instead ofdb.get().transaction(), causing aTypeError: Cannot read properties of undefined (reading 'status')on every initial sync — no events were imported
[0.28.0] - 2026-04-27
Added
- Navigation: sidebar nav items now show a native tooltip in the icon-only breakpoint (1024–1279 px), making all 11 modules discoverable without labels
- PWA: offline banner appears at the top of the screen when the device loses connectivity, and hides automatically when the connection is restored
- Desktop: global keyboard shortcuts —
/(search),n(new),?(shortcut overview),g d/t/c/s/n(navigate to module) - Dashboard: widget order is now adjustable via drag-and-drop in the Customize modal; order is persisted in user preferences
- UX:
deleteWithUndoutility inux.js— birthdays deletion now offers an undo toast identical to tasks, notes, contacts, and meals - UX: contextual onboarding hints added to empty states in all modules (tasks, contacts, notes, budget, shopping, birthdays, recipes)
Changed
- Dashboard: widget title icons use
--color-text-secondaryinstead of the module accent color, reducing visual noise when all widgets are visible - Performance:
reminders.cssis now lazy-loaded on demand instead of being included in every page load
Fixed
- UI: modal close button increased from 40 px to 44 px to meet Apple HIG minimum tap target
- UI:
.widget__linkelements now have a 44 px minimum touch target height with correct padding - CSS: removed dead
.fabCSS block — all pages use.page-fab - UX: toasts can now be dismissed by swiping horizontally (> 40 px)
[0.27.1] - 2026-04-27
Fixed
- Google Calendar: null/undefined items returned by the Google API are now skipped instead of crashing the sync with a
TypeError - Google Calendar: the OAuth callback now awaits the initial sync before redirecting, so sync failures are correctly shown as an error in the UI instead of a false success
[0.27.0] - 2026-04-27
Added
- Settings: family roles (Dad, Mom, Parent, Child, Grandparent, Relative, Family member) are now separate from system access roles — each family member can have a descriptive family role independent of their admin status
- Settings: profile picture upload for the current user (PNG, JPEG, WebP; auto-resized to 512 px on the client side)
- Settings: admin users can now edit existing family member profiles (name, username, family role, system-admin flag, color, profile picture) via a new Edit button on each member row
- Settings: new System admin checkbox replaces the Admin/Member role dropdown when creating a new family member
- Dashboard: family widget avatars now display profile pictures when available
- API: new read-only
GET /api/v1/family/membersendpoint listing family members without exposing usernames or system roles - API:
PATCH /api/v1/auth/users/:id— admin endpoint to update any family member's profile - API:
PATCH /api/v1/auth/me/profile— self-service endpoint to update own display name, color, and profile picture - i18n: new locale keys for all new UI strings across all 16 supported languages
[0.26.5] - 2026-04-27
Changed
- Birthdays: increased maximum photo upload size from ~0.9 MB to 5 MB
[0.26.4] - 2026-04-27
Changed
- Dashboard: weather widget is now the first entry in the default widget order
- Dashboard: widgets in the same grid row now share the same height (via flex stretch), eliminating the patchwork gaps between shorter and taller widgets
[0.26.3] - 2026-04-27
Fixed
- Birthdays: "Discard changes?" dialog appeared immediately after successfully saving a birthday because
closeModal()was called withoutforce: true, triggering the dirty-form check on a programmatic close - Dashboard (PWA): widget items (tasks, events, meals, notes, birthdays, shopping lists) occasionally blocked vertical swipe-to-scroll; added
touch-action: pan-yso the browser passes vertical pan gestures through to the scroll container
[0.26.2] - 2026-04-27
Fixed
- Dashboard: KPI summary bar removed — it duplicated the same widget categories (tasks, calendar, birthdays…) that are already visible as full widgets directly below
- Dashboard: replaced the two-column main/side workspace layout with the established flat responsive grid so all widgets are consistently left-aligned across all screen sizes in the web view
[0.26.1] - 2026-04-27
Fixed
- Dashboard:
path is not definedcrash on every navigation —renderPage()referenced a barepathvariable instead ofroute.path - Dashboard: shopping lists widget caused a server-side SQL error (
HAVINGclause on non-aggregate query) resulting in an empty widget for all users
[0.26.0] - 2026-04-27
Added
- Birthdays module: track family birthdays with name, birth date, optional photo and notes; each entry is automatically synced to the calendar as a yearly recurring event and to the reminder system
- Birthdays dashboard widget: shows the next upcoming birthdays at a glance with age and days-until labels
- Family Participants dashboard widget: displays the number of users added to the family with avatar initials
- Budget Overview dashboard widget: shows monthly income, expenses, balance, savings rate and top expense category
- Dashboard widget customisation extended to include the three new widgets (birthdays, budget, family)
- Settings › General: admin option to set a custom application name shown in the sidebar, browser title and login screen
- Birthday translations across all 16 supported locales
Changed
- Service worker: mutable JS and CSS assets now use network-first caching to eliminate stale-asset issues after deployments
[0.25.8] - 2026-04-27
Fixed
- Test suite:
makeInputmock intest-modal-utils.jsnow implementssetAttribute/removeAttributeso blur-validation tests correctly verify the newaria-invalidattribute behaviour
[0.25.7] - 2026-04-27
Added
- Navigation: a dedicated screen-reader announcer (
aria-live="polite") announces the page name on every route change instead of reading the entire page content
Changed
- Color pickers (notes, calendar): swatches now use
role="radiogroup"with localized color names instead of hex codes,aria-checkedreflects the selected state, and Arrow keys navigate between options - Navigation badges: badge counts are now hidden from screen readers (
aria-hidden); the parent nav link'saria-labelis updated to include the count in plain text (e.g. "Aufgaben, 3 überfällig") - Main content area: removed
aria-live="polite"from<main>— it was causing screen readers to read the full page on every navigation
Fixed
- Form validation:
aria-invalid="true"is now set on invalid inputs in all modals and on the login form so screen readers can announce field errors
[0.25.6] - 2026-04-27
Changed
- Tasks: completing a task now animates the strikethrough line instead of snapping it on instantly
- Modal: save button shows a spinner during async API calls; the spinner disappears immediately if form validation fails, and on API error when the button is re-enabled
- Toast: the Undo button now gives tactile press feedback (scale + removes browser tap highlight) for reliable interaction within the 5-second window
[0.25.5] - 2026-04-26
Added
- Navigation: the "More" button now shows the name and icon of the active secondary module instead of the generic label, making it clear which module is open
- Dashboard: first-time onboarding overlay guides new users through the app's three core navigation areas
Changed
- Navigation: renamed "Pinnwand" to "Notizen" for clarity
- Login: submit button shows a spinner during authentication; empty fields are highlighted individually with red borders instead of a single generic error message
Fixed
- Modal: closing a modal when the form has unsaved changes no longer double-fires the guard due to a missing
_isClosingflag; the close button now uses an arrow-function listener to avoid stale closure issues
[0.25.4] - 2026-04-26
Added
- Modal: closing a modal (via Escape, swipe, overlay click, or X button) now shows a "Discard changes?" confirmation dialog when the form has been modified since it was opened; saving or deleting bypasses the prompt
[0.25.3] - 2026-04-26
Changed
- Delete actions in all seven modules (tasks, notes, budget, calendar, contacts, meals, recipes) and shopping list deletion no longer show a confirmation dialog; instead the item is removed immediately and a toast with an Undo button gives a 5-second window to reverse the action before the API call is made
[0.25.2] - 2026-04-26
Changed
- Docs:
SPEC.mdupdated to reflect all changes since v0.24.0 — Budget Entries table now documentssubcategorycolumn and DB-backedcategoryFK; newBudget Categories,Budget Subcategories, andAPI Tokensdata-model tables added; Settings section updated with API Tokens tab, corrected language list (added Japanese, Arabic, Hindi, Portuguese), and tab count (six → seven); Budget module section now covers subcategories, custom categories, and all new endpoints; new API Documentation section documents OpenAPI 3.0 spec and authentication options; design tokens--blur-2xsand--module-remindersadded to Colors section - Docs:
README.mdHighlights updated — Budget Tracking now mentions DB-backed subcategories; new API Tokens entry added
[0.25.1] - 2026-04-26
Changed
- Dashboard: empty widget states now render as a compact inline row (icon + text) instead of a centred column, saving ~40px of vertical space per empty widget on mobile
Fixed
- Dashboard: widget body bottom padding increased from 12px to 16px for slightly more breathing room
- Dashboard: widget reordering in "Anpassen" modal now uses the View Transition API for smooth animations; respects
prefers-reduced-motion
[0.25.0] - 2026-04-25
Added
- API token authentication: admins can create named Bearer / X-API-Key tokens for external integrations; tokens are SHA-256-hashed at rest, support optional expiry and revocation, and track last-used timestamp
- Settings: new "API Tokens" section for admins to create and revoke tokens; the full token value is shown only once immediately after creation
- OpenAPI 3.0 specification served at
/api/v1/openapi.jsonand/openapi.json(download via?download=1) - Budget: new endpoints
GET /api/v1/budget/categoriesandGET /api/v1/budget/categories/:key/subcategorieswith optional?lang=localisation
Changed
server/logger.jsnow serialisesErrorobjects into structured JSON fields (name, message, stack) instead of logging{}
[0.24.4] - 2026-04-26
Added
- Accessibility:
layout.cssnow has a@media (prefers-contrast: more)block — ghost and secondary buttons get explicit borders, cards lose decorative shadows, form inputs get a 2px border, focus rings become thicker (3px, 4px offset), and active nav items get an underline as a colour-independent indicator
Fixed
- Design tokens: corrected
--sidebar-width-expandedcomment from1280px+to1440px+to match the actual breakpoint inlayout.css
[0.24.3] - 2026-04-26
Added
- Design tokens:
--blur-2xs: blur(2px)added to the blur scale — fills the gap below--blur-xs(4px), used for subtle overlay blurs - Design tokens:
--module-reminders: #0E7490(Cyan-700, WCAG AA) added for the reminders feature; dark mode variant#22D3EE(Cyan-400)
Fixed
- Design tokens: hardcoded
blur(16px),blur(2px), andblur(12px)inlayout.cssreplaced withvar(--blur-md),var(--blur-2xs), andvar(--blur-sm)—prefers-reduced-transparencynow correctly disables all backdrop-filter effects including bottom nav, more-sheet backdrop, and sticky headers - Accessibility:
layout.cssnow has aprefers-reduced-transparencyblock for.nav-bottom,.more-backdrop, and.sticky-header— these three elements previously kept their backdrop-filter active even when the user requested reduced transparency - Reminders: reminder bell icon in toasts now uses
var(--module-reminders)instead of the genericvar(--color-accent)
[0.24.2] - 2026-04-26
Fixed
- Design tokens: added missing
--shadow-xland--shadow-xstokens (with dark mode variants) — resolves undefined CSS custom property references in kanban drag ghost and dashboard widget toggle - Design tokens:
--color-surface-raisedreplaced with--color-surface-hoverindashboard.css— was undefined, causing unstyled hover states in the widget customizer - Design tokens:
--color-textreplaced with--color-text-primaryindashboard.css— was undefined, causing invisible text on hover in the widget customizer - Design tokens: hardcoded
font-weightvalues (700,500,600) inreminders.cssreplaced with--font-weight-bold,--font-weight-medium,--font-weight-semibold
[0.24.1] - 2026-04-25
Fixed
- Accessibility: skip-to-content link added to
index.html— keyboard users can now bypass navigation and jump directly to main content - Accessibility: removed
role="presentation"from modal overlay — restores screen reader access and resolves conflict with existingaria-label - Accessibility: search overlay now traps keyboard focus — tabbing can no longer escape the overlay into the hidden page behind it
- Interaction: modal swipe-to-close — kept
draggingflag active on upswing so the panel snaps back correctly instead of getting stuck - Rendering: SVG gradient IDs in the logo are now unique per render — prevents DOM ID collisions when the logo is mounted more than once
- Touch targets:
.btn--icon-smminimum size raised from 36×36px to 44×44px (--target-base) — meets iOS minimum touch target guideline - Design tokens: added
--target-base: 44pxand documented--target-sm: 32pxas visual-only (not a touch target)
[0.24.0] - 2026-04-25
Added
- Budget: expense categories are now stored in the database (
budget_categoriestable) as stable English slugs, replacing hardcoded German strings - Budget: subcategory support for all expense entries — 35 predefined subcategories across 8 top-level categories (housing, food, transport, personal_health, leisure, shopping_clothing, education, financial_other)
- Budget: users can add custom categories and subcategories directly from the entry modal via inline "+ category" / "+ subcategory" buttons
- Budget: new API endpoints
POST /api/v1/budget/categoriesandPOST /api/v1/budget/categories/:key/subcategoriesfor custom category/subcategory creation - Budget: subcategory displayed alongside category in each entry's metadata line
- Budget: CSV export now includes a subcategory column and English column headers
- i18n: all 14 non-German locales extended with new budget category keys (
catHousing,catTransport,catPersonalHealth,catShoppingClothing,catFinancialOther) and all 35 subcategory label keys - All server-side log messages and API error strings translated from German to English — contributed by @rafaelfoster
Changed
- Budget category labels for existing entries migrated to new slug keys via DB migration 15; display names remain fully localised through the i18n system
[0.23.17] - 2026-04-25
Fixed
- Italian (it) locale: translated all missing strings in the recipes section (
nav.recipes,meals.savedRecipeLabel,meals.savedRecipePlaceholder,meals.saveAsRecipe,meals.recipeScaleLabel, and allrecipes.*keys) — contributed by @albanobattistella
[0.23.16] - 2026-04-24
Changed
- Design tokens: replaced all remaining hardcoded color and size values in
layout.css,glass.css,dashboard.css, andreminders.csswith CSS custom properties - Design tokens: added
--text-2xs,--color-overlay-glass,--color-backdrop-glass,--glass-border-overlay,--glass-highlight-mid,--glass-inset-bottom-base,--glass-inset-bottom-hover,--glass-inset-thumb, and--glass-inset-inputtotokens.css
[0.23.15] - 2026-04-24
Fixed
- All non-German locales (ar, el, en, es, fr, hi, it, ja, pt, ru, sv, tr, uk, zh): added missing translation keys for
nav.more,calendar.ics.reset/resetToast,settings.ics.*,tasks.filter*,tasks.swiped*,search.*, andreminders.*— these were falling back to German strings for all non-German users
[0.23.14] - 2026-04-23
Fixed
- Swedish (sv) locale: corrected five translation errors in the recipes section (
titleRequired,copySuffix,urlLabel,openLink,emptyDescription) — contributed by @olsson82
[0.23.13] - 2026-04-22
Security
- Installer: replaced template-literal URL construction with the
URLconstructor when setting the final "Open Oikos" link, eliminating a potential DOM-based XSS vector (CodeQL js/xss-through-dom, GitHub Advisory #7)
[0.23.12] - 2026-04-22
Fixed
- iOS PWA: bottom navigation bar gap resolved by removing
overflow: hiddenfrom<html>(iOS Safari bug: this property clipsposition: fixeddescendants) and restoring thebody::afterfill approach; nav bar height is no longer inflated by the safe area padding
[0.23.11] - 2026-04-22
Fixed
- iOS PWA: bottom navigation bar now extends into the home indicator safe area via
padding-bottom: env(safe-area-inset-bottom), reliably eliminating the gap at the screen bottom
[0.23.10] - 2026-04-22
Fixed
- iOS PWA: safe area fill now uses the same surface color as the bottom navigation bar, so it matches in both light and dark mode
[0.23.9] - 2026-04-22
Fixed
- iOS PWA: a
body::afterpseudo-element now fills the home indicator safe area with the same glass background as the bottom navigation, eliminating the gap between the nav bar and the screen edge
[0.23.8] - 2026-04-22
Fixed
- iOS PWA: bottom navigation bar now extends into the home indicator safe area, removing the gap between the nav and the screen edge
[0.23.7] - 2026-04-22
Fixed
- Navigation: sidebar logo now uses the official
docs/logo.svgartwork (house + chimney on gradient background) instead of a generic Lucide home icon; gradient colors are driven by CSS tokens
[0.23.6] - 2026-04-22
Changed
- Dashboard: greeting widget now adapts its gradient to the time of day — warm amber-orange in the morning (before 11:00), indigo during the day, and violet in the evening (after 18:00)
- Dashboard: FAB speed-dial open/close rotation now uses a spring cubic-bezier for a more natural feel
- Navigation: sidebar logo is now a proper SVG house icon on a gradient background instead of the CSS letter placeholder
[0.23.5] - 2026-04-22
Changed
- Dashboard: each widget now uses its module accent color (green for tasks, violet for calendar, orange for meals, pink for shopping, amber for notes) for its header icon, badge, and link instead of the global indigo accent
- Dashboard: meal slots now display their type-specific color (amber for breakfast, green for lunch, indigo for dinner, orange for snack) on icon and label when a meal is planned
- Dashboard: pinned note cards now show a subtle background tint matching the note's color
- Dashboard: widget and card hover lift increased from 1 px to 2 px for more perceptible feedback on desktop
- Navigation: active bottom-nav tab now shows a pill-shaped highlight behind the icon for a clearer location indicator
- Shopping widget: progress bar height increased from 4 px to 6 px for better visual weight
- Empty state icons inside widgets now use the tertiary text color instead of the disabled color for improved visibility
[0.23.4] - 2026-04-22
Changed
- Docs: web installer (
node tools/installer/install-server.js) is now Option A in all installation guides (README.md,docs/installation.md, GitHub Pagesdocs/install.html); the pre-built Docker image method is relabelled Option B and the build-from-source method Option C
[0.23.3] - 2026-04-22
Fixed
- Weather widget: wind speed is no longer multiplied by 3.6 when
OPENWEATHER_UNITS=imperial(the API already returns mph; the conversion was only correct for metric/standard) - Weather widget: wind unit label now shows
mphfor imperial andkm/hfor metric/standard instead of always showingkm/h
[0.23.2] - 2026-04-22
Fixed
- Calendar: ICS-synced events now render at the correct local hour and day in week/day/month/agenda views; day-matching and hour-positioning previously used raw string slices which returned UTC values instead of browser-local time for events stored with a
Zsuffix
[0.23.1] - 2026-04-22
Security
- Installer: host and port inputs are now validated against a strict hostname regex and integer range check (1–65535) before being used in any DOM sink or URL template — prevents XSS-through-DOM (CodeQL js/xss-through-dom alert #7)
[0.23.0] - 2026-04-21
Added
- Calendar:
external_calendarsDB table (migration v14) stores display name and color per synced Google/Apple calendar;calendar_eventsgains acalendar_ref_idFK used for join-based name/color lookup in all calendar and dashboard queries - Calendar: Google and Apple sync services now fetch the calendar's display name and background color via
upsertExternalCalendar()and persist them to the new table - Calendar: event popup, agenda, month, week, and day views now show the external calendar name as a colored
event-cal-labelbadge whencal_nameis present - Calendar: event popup and dashboard events list now display the event location using
fmtLocation()which strips RFC 5545 backslash-escapes (\n,\,,\;,\\) and normalizes semicolons/newlines to comma-separated inline text - Utils:
fmtLocation(raw)helper added tohtml.jsfor normalizing ICSLOCATIONproperty strings - i18n: task due-date keys (
tasks.overdue,tasks.dueSoon,tasks.dueToday,tasks.dueTomorrow,tasks.noDueDate) added to all 16 supported locale files
Changed
- Dashboard: widget headers flattened — glass card replaced with transparent surface + bottom border; clock icon added to the urgent-tasks chip; overdue and due-soon counts computed separately using
effectiveDue()for accuracy - Glass toolbar (desktop ≥ 1024 px): rounded card style (
border-radius, fullborder) replaced with flat background +border-top: 3px solid var(--module-accent)+ bottom border only, consistent with other page toolbars - Shopping and Budget page headers:
border-top: 3px solid var(--module-accent)accent stripe added to.list-tabs-barand.budget-nav, matching the visual language of all other module headers - Calendar agenda: event color indicator changed from a 10 px circle to a 3 px full-height left bar (
width: 3px; align-items: stretch), matching the dashboard upcoming-events style - Tasks: filter panel now defaults to
status: 'open'on first load instead of showing all tasks including completed ones - SW cache: bumped to
oikos-shell-v50/oikos-pages-v45/oikos-assets-v45
Fixed
- Tasks / Dashboard: sort order now strictly follows effective due date ascending; overdue tasks (due date+time in the past) always surface first in all views — list groups, Kanban columns, and the dashboard urgentTasks widget. Priority is used only as a tiebreaker for tasks sharing the same due datetime. Server-side sort moved from SQL to JavaScript using
effectiveDue()for timezone-correctdue_timehandling (SQLiteDATE('now')is UTC-only) - Tasks: due date chip now shows the time component when
due_timeis set; overdue/soon/today/tomorrow states are computed against the current moment rather than midnight - Dashboard: widget navigation links changed from
<a href>to<button type="button">to prevent iOS Safari from intercepting touch events before the JS click handler fires;.widget__headergivenposition: relative; z-index: 2to lift it above the backdrop-filter::afterpseudo-element stacking context - Dashboard: FAB shortcut buttons now programmatically click the page's primary add-button after navigation, opening the new-item modal directly without requiring a second tap
- Calendar: week-view allday row no longer stretches column widths when event titles are long —
.allday-cellnow hasmin-width: 0; overflow: hiddento constrain grid cells that would otherwise expand to fitwhite-space: nowrapcontent - Calendar: incorrect
|| 'var(--color-accent)'color fallback removed from all five event rendering sites in month, week, allday, and popup views; events without a color now render without an inlinebackground-colordeclaration - Modal: sheet swipe adds a 10 px dead zone before the
translateYtransform is applied, preventing involuntary micro-transforms on normal taps; thestyle.transform = ''reset intouchendis deferred viarequestAnimationFrameso iOS WebKit does not cancel the subsequentclickevent on child buttons — fixes delete-confirm and edit buttons not responding after a partial swipe - Modal:
_doClosenow receives and captures the overlay element before any animation; prevents a race condition where opening a new modal (e.g. a confirm dialog) before the previous close animation finished caused_doCloseto remove the new modal and leave its buttons permanently unresponsive - Router: page auto-reloads 8 s after the SW-update toast is shown, matching the toast's own display duration so the reload is never missed
- Layout: modal overlay uses
overflow: hiddenand bottom-sheet scroll container usesoverflow-x: hiddento prevent horizontal scroll bleed on narrow viewports; form inputs getmin-width: 0; box-sizing: border-boxto prevent overflow out of two-column grid containers - Reminders: field grid changed from
1fr 1frtorepeat(2, minmax(0, 1fr))to prevent content from exceeding the grid track width - PWA: double
padding-bottomon PWA bottom nav removed — the safe-area padding inpwa.csswas applied twice, causing an extra gap on iPhone safe-area screens
[0.22.3] - 2026-04-21
Fixed
- Landing page setup commands now render with correct line breaks. The
.code-blockelement has nowhite-space: pre, so explicit<br>tags are required; they were previously missing, causing all commands to flow as a single line.
[0.22.2] - 2026-04-21
Fixed
- Locale file (
de.json) no longer causes a JSON parse error that made the app completely unusable. Therecipes.deleteConfirmvalue contained a bare ASCII double-quote inside a JSON string, which prematurely terminated the string and broke every page load. - ICS calendar subscriptions now respect the
COUNTparameter in RRULE (RFC 5545). Previously, events with a limited number of occurrences (e.g.RRULE:FREQ=WEEKLY;COUNT=3) were incorrectly shown as upcoming because the expansion loop iterated to the sync window end regardless of the occurrence limit.
[0.22.1] - 2026-04-21
Fixed
- App no longer gets stuck on the "Oikos" splash screen when opened in a new tab. Two root causes addressed: (1)
sw.jswas not updated in v0.22.0, so the browser kept the old Service Worker and served stale cached files (oldrouter.js,meals.js) via Stale-While-Revalidate — cache versions are now bumped (shell v35, pages v30) to force reinstallation and fresh file delivery. (2) A fatal error ininitI18n()(e.g. locale fetch failure offline) left the splash screen visible forever — the router IIFE now catches such errors, hides the splash screen, and renders a recoverable error state. - Service Worker now pre-caches
/pages/recipes.jsand/styles/recipes.cssintroduced in v0.22.0, enabling offline access to the Recipes page.
[0.22.0] - 2026-04-21
Added
- Recipes module: create, edit, duplicate, and delete reusable recipes with title, notes, a recipe link, and a per-ingredient category. Accessible via the new
/recipesroute and nav entry. - "Add to meal plan" action on recipe cards navigates to Meals and pre-fills the modal with the selected recipe.
- Meals modal: select a saved recipe to auto-fill title, notes, URL, and ingredients; scale ingredient quantities by a numeric factor; save the current meal as a new recipe in one click.
GET/POST /api/v1/recipes,PUT/DELETE /api/v1/recipes/:idREST endpoints with full validation and ingredient sync.- Migration 13:
recipesandrecipe_ingredientstables;recipe_idFK column onmeals.
[0.21.1] - 2026-04-21
Fixed
- ICS calendar subscription sync no longer fails with "ON CONFLICT clause does not match any PRIMARY KEY or UNIQUE constraint". Migration 12 replaces the partial unique index on
(subscription_id, external_calendar_id)with a full unique index, which SQLite's upsert conflict-target syntax requires.
[0.21.0] - 2026-04-21
Added
POST /api/v1/auth/setupbootstrap endpoint: creates the first admin user when the users table is empty, enabling first-run setup in Docker without shell access to the container volume. Returns 403 once any user exists.install.sh: interactive CLI wizard (7 steps) guiding users from a blank server to a running Oikos instance — prerequisites check, domain/port/timezone config, auto-generated or manual security secrets, optional weather and calendar integrations, Docker startup with health polling, and admin account creation. Supports--env-filefor non-interactive/CI deployments.- Web installer (
tools/installer/): browser-based setup wizard served by a zero-dependency Node.js server onlocalhost:8090. Covers the same steps as the CLI installer through a single-file SPA. Auto-terminates after successful setup or 30 minutes of inactivity.
[0.20.43] - 2026-04-21
Added
POST /api/v1/auth/setupbootstrap endpoint: creates the first admin user when no users exist, enabling Docker-based deployments to initialise via HTTP without direct filesystem DB access. Returns 403 once any user exists.
[0.20.42] - 2026-04-21
Added
.claude/tooling committed with the repo: skills (release-prep,fix-issue,pr-review,issue-triage), subagents (pr-reviewer,repo-auditor), path-scoped rules (server-routes,public-pages,tests,db-migrations), and a PostToolUse hook (block-innerhtml.sh) that enforces the innerHTML ban on save. Contributors using Claude Code now get the same guardrails and workflows automatically.
Changed
.gitignore: no longer excludes the entire.claude/directory — only.claude/settings.local.jsonand.claude/worktrees/stay out, so shared tooling is versioned while local permissions remain private.
[0.20.41] - 2026-04-21
Fixed
- Race condition in
router.js: whenauth.me()failed during initial navigation,_pendingLoginRedirectwas not cleared before callingnavigate('/login')from the catch block, causing thefinallyhandler to launch a second concurrent navigation. If the second navigation was still in progress when the user submitted the login form,navigate('/', user)was silently blocked — login appeared to succeed but the dashboard never loaded (most noticeable on iOS Safari PWA with iCloud Keychain autofill)
Added
- Version number displayed on the login page (fetched from new
GET /api/v1/versionendpoint, no auth required), so users can verify which release their PWA is running
[0.20.40] - 2026-04-21
Changed
docs/SPEC.mdDesign System → Colors: replaced outdated code block with current Indigo-based palette (#4F46E5primary, module color semantics, priority/severity separation, dark mode Indigo-400 accent,--glass-inset-*specular tokens); added palette rationale and WCAG contrast notes inline
[0.20.39] - 2026-04-21
Changed
docs/SPEC.md: documenticsas a validexternal_sourcevalue, addsubscription_idanduser_modifiedcolumns to Calendar Events data model, add ICS Subscriptions table definition, expand Calendar module section with ICS subscription feature details, update Settings sectionREADME.md: update Calendar Sync highlight to mention ICS/webcal URL subscriptions
[0.20.38] - 2026-04-21
Added
- ICS-URL calendar subscriptions: any user can subscribe to external calendars via HTTPS or webcal:// URL
- Per-subscription visibility (private or shared with all family members), custom color, and manual sync trigger
- REST API:
GET/POST /api/v1/calendar/subscriptions,PATCH/DELETE /api/v1/calendar/subscriptions/:id,POST /api/v1/calendar/subscriptions/:id/sync POST /api/v1/calendar/:id/resetendpoint to clearuser_modifiedon ICS events, allowing the next sync to restore upstream data- ICS visibility filter on
GET /api/v1/calendarand/upcoming: private subscription events are hidden from other users - ICS subscription management card in Settings → Kalender tab (below Apple Calendar): subscription list with color dot, visibility badge, last-sync timestamp; inline add form with URL, name, color picker, and shared toggle; sync and delete actions
- "Auf Original zurücksetzen" link in calendar event popup for user-modified ICS events
user_modified = 1is set automatically when any externally-sourced event (ICS, Google, Apple) is edited by the user- ICS sync integrated into the periodic
runSync()scheduler alongside Google and Apple Calendar - All new UI strings in
public/locales/de.jsonundersettings.ics.*andcalendar.ics.*
[0.20.37] - 2026-04-20
Added
server/services/ics-subscription.js: core ICS subscription service with SSRF-protected fetch (DNS pre-resolution against private IP ranges), ETag/Last-Modified conditional fetching, 10 MB response size limit, 15 s timeout, webcal:// → https:// normalization, RRULE expansion via sync window (−6 / +12 months), upsert-on-conflict withuser_modifiedguard, stale-event cleanup viajson_each, and in-memory mutex to prevent concurrent syncs of the same subscription
[0.20.36] - 2026-04-20
Added
- Migration v10: new
ics_subscriptionstable with fields for name, URL, color, shared flag, created_by, etag, last_modified, last_sync, and created_at - Migration v11:
calendar_eventstable recreated to extend theexternal_sourceCHECK constraint to include'ics', and two new columns added —subscription_id(FK toics_subscriptionswith CASCADE delete) anduser_modified(integer flag, default 0) - Unique partial index
idx_calendar_sub_extidon(subscription_id, external_calendar_id)prevents duplicate UIDs within a single ICS subscription while allowing the same UID across different subscriptions test:ics-subtest suite with 10 tests covering subscription CRUD, ICS event insertion, UNIQUE constraint enforcement, cascade delete, visibility filtering, and CHECK constraint validation
[0.20.35] - 2026-04-20
Changed
- Extracted ICS parser functions (
unfoldLines,parseICS,formatICSDate,tzLocalToUTC,applyDuration) fromapple-calendar.jsinto a new shared moduleserver/services/ics-parser.js, plus a newexpandRRULEhelper — pure refactor, no logic changes - Added
test:ics-parsertest suite covering line unfolding, all-day/UTC event parsing, and RRULE expansion
[0.20.34] - 2026-04-20
Fixed
- Login on Safari/iOS PWA no longer loops back to the login screen when credentials are wrong:
apiFetchno longer dispatchesauth:expiredfor 401 responses from/auth/login(where 401 means invalid credentials, not expired session) — the error message is now shown correctly instead of the form being silently re-rendered
[0.20.33] - 2026-04-20
Fixed
- Weather widget: forecast min/max temperatures are now aggregated across all 3-hour intervals of each day instead of reading
temp_min/temp_maxfrom a single snapshot — the OWM free-tier/forecastendpoint reports near-identical values per 3h window, so min and max were always the same; icon and description still use the noon entry (12:00, fallback 15:00)
[0.20.32] - 2026-04-20
Changed
- Dark mode switched from blue-tinted (Option B) to deep warm (Option A): background
#1A1A18, cards#222220, sidebar#141413— warmer and more inviting feel
[0.20.31] - 2026-04-20
Fixed
- Toast success/warning/danger now use dark text in dark mode — previously white text on vivid lime/amber/pink backgrounds had contrast ratios of ~1.3–1.5:1 (unreadable); now 13–15:1 (WCAG AAA)
- List stagger animation tapers naturally (35→30→25→22→18→15→12→9→7ms steps) and covers 9 items before capping, replacing the abrupt constant 175ms from item 6 onward
[0.20.30] - 2026-04-20
Changed
- Desktop body font size increased from 14px to 15px (
--text-base) for improved readability - Interactive cards now show a stronger pressed state on touch (scale 0.98 + surface-3 background) instead of the barely-perceptible scale(0.99)
Fixed
- Added clarifying comment to
--color-surface-2token explaining its recessed/sunken semantics (darker than background in dark mode, not an elevation level)
[0.20.29] - 2026-04-20
Changed
- Dark mode now uses a blue-tinted color palette (Option B): background
#0F1117, surfaces#161A26/#1E2336, sidebar#0B0D14— creates stronger visual hierarchy and complements the Indigo accent
Fixed
- Defined missing CSS tokens
--color-surface-elevatedand--color-surface-hoverused by More-Sheet items, Search overlay inputs and results (previously transparent/invisible backgrounds)
[0.20.28] - 2026-04-20
Fixed
- Dark mode "System" setting now reliably follows the OS preference on every page load, even in browsers where JavaScript
matchMediais restricted (e.g. Brave with fingerprint protection); CSS@media (prefers-color-scheme: dark)now serves as the authoritative source for system preference detection instead of JS
[0.20.27] - 2026-04-20
Fixed
- Selecting "System" theme in settings now immediately applies the OS dark/light preference instead of reverting to light mode until the next page reload
[0.20.26] - 2026-04-20
Added
- Meals: floating action button (FAB) now appears fixed at the bottom-right corner, opening a quick-add modal for today with the first visible meal type pre-selected
[0.20.25] - 2026-04-20
Fixed
- Theme selection no longer reverts to light mode on page reload when "System" is chosen; the init script now correctly resolves the
systempreference to the OS dark/light state instead of treating it as a literaldata-themevalue
[0.20.24] - 2026-04-20
Added
- Tasks: subtle green edge indicator on touch devices hints at the swipe-left gesture without requiring an actual swipe (hidden during active swipe)
- Global search: new search overlay accessible from the "More" sheet — searches tasks, calendar events, and notes simultaneously; results link directly to the relevant record
- Navigation: bottom bar now shows 4 primary items plus a "More" button that opens a slide-up sheet with remaining sections and the search entry point; replaces the old 2-page swipe approach
Changed
- Server:
VALID_CATEGORIESin tasks route updated to English keys to match the v9 DB migration
[0.20.23] - 2026-04-20
Added
- Tasks: filter bar replaced by a compact toggle panel — only active filter chips are shown inline; a "Filter" button (with active-count badge) opens a grouped panel with Status, Priority, and Person sections, plus a clear-all button
Changed
- Tasks: category values stored in the database are now English keys (
household,school,shopping,repair,health,finance,leisure,misc) instead of German strings — migration v9 converts all existing rows automatically; display labels are unchanged
[0.20.22] - 2026-04-20
Added
- Tasks: Kanban board now supports touch drag-and-drop on mobile — a ghost card follows the finger and drops into the target column on release
- Tasks: swipe-left to mark done/open now shows a 5-second undo toast that reverts the status change
- Tasks: opening a task card from the Dashboard now navigates to
/tasksand immediately opens the edit modal for that task (deep-link via?open=<id>)
Fixed
- Router: query parameters (e.g.
?open=123) are now stripped before route matching, so parameterised URLs resolve correctly without falling back to the home page
[0.20.21] - 2026-04-20
Changed
- Dashboard: eliminated double-render flicker — initial paint uses skeleton widgets and a stat-less greeting; real widgets replace skeletons in-place without resetting
container.innerHTML - Dashboard: weather widget now derives temperature unit symbol (°C / °F / K) from the
unitsfield returned by the weather API instead of always showing °C - Dark mode: removed duplicate
@media (prefers-color-scheme: dark)block fromtokens.css; system-preference detection moved to amatchMedialistener inindex.htmlfor flash-free sync - Tasks: view-toggle (list / Kanban) fades out at 40% opacity during re-render and fades back in, giving visible feedback of the switch
Fixed
- Tasks: inline
style="width/height"on all Lucide icon instances replaced with utility CSS classes (icon-xs…icon-2xl,icon-11) defined inlayout.css - Tasks: edit-button inline size overrides removed; replaced with new
.btn--icon-smutility class - Tasks:
textarearesize: verticaland selectmin-height: 44pxmoved from inline styles tolayout.css - Dashboard:
chipIconinline style variable eliminated; chip icons now useclass="icon-sm" - Dashboard: settings, refresh, chevron, and other action icons converted from inline styles to CSS classes
- Weather API: server now forwards the configured
unitsvalue in the response payload so the frontend can render the correct unit symbol
[0.20.20] - 2026-04-20
Fixed
- Accessibility:
--module-notescolor raised from#CA8A04(4.08:1) to#A16207(6.3:1) — now WCAG AA compliant for normal text including nav labels - Accessibility: Task status button
aria-labelnow reflects actual action — says "mark as open" for completed tasks instead of always "mark as done" - i18n: Added
tasks.markOpenkey to all 15 locale files for the corrected aria-label
[0.20.19] - 2026-04-20
Changed
- Design: two hardcoded color values in
dashboard.cssreplaced with design tokens —drop-shadow(0 2px 4px rgba(0,0,0,0.15))on.weather-widget__iconreplaced with new--shadow-drop-icontoken;rgba(0,0,0,0.25)on.fab-backdropreplaced with new--color-backdrop-fabtoken - Design:
--shadow-drop-iconand--color-backdrop-fabadded totokens.css(shadow and overlay sections respectively)
[0.20.18] - 2026-04-20
Changed
- Meals: Zutaten-Kategorien im Mahlzeiten-Dialog auf lebensmittelrelevante Kategorien beschränkt (Haushalt und Drogerie ausgeblendet)
- Refactoring: Kategorie-Übersetzungslogik (
categoryLabel) undDEFAULT_CATEGORY_NAMEin neues geteiltes Utilitypublic/utils/shopping-categories.jsausgelagert; Shopping- und Meals-Seiten nutzen nun die gemeinsame Implementierung
[0.20.17] - 2026-04-20
Changed
- Design: dark-mode token architecture refactored to private-variable indirection (
--_name) intokens.css— all tokens with dark-mode overrides now have a private--_tokenvariant that holds the actual value, while public tokens (--color-*,--module-*,--glass-*etc.) are stablevar(--_token)references. Both dark blocks (@media prefers-color-scheme: darkand[data-theme="dark"]) now only override the compact private tokens; the public API never needs to be touched again for dark-mode changes. The redundant explicit--color-surface-2override was removed from both dark blocks (it is already correctly derived viavar(--neutral-50)). No visual change.
[0.20.16] - 2026-04-19
Changed
- Design: PWA
theme-colormeta tag updated from#2563EBto#4F46E5(Indigo-600) to match the new primary accent; install-prompt CSS fallback updated from#2554C7to#4338CA, and hardcoded#fffreplaced withvar(--color-text-on-accent, #fff) - Design: five new
--glass-inset-*tokens added totokens.css(--glass-inset-soft0.18,--glass-inset-base0.20,--glass-inset-medium0.22,--glass-inset-elevated0.28,--glass-inset-strong0.32); ten hardcodedinset 0 1px 0 rgba(255,255,255,…)literals inglass.cssandtasks.cssreplaced with the corresponding token references — no visual change - Design:
@media printblock inlayout.cssnormalised from CSS shorthand hex (#fff,#000,#ddd) to explicit six-digit notation (#ffffff,#000000,#cccccc) for consistency
[0.20.15] - 2026-04-19
Changed
- Design: primary accent migrated from
#2563EB(Tailwind Blue-600) to#4F46E5(Indigo-600) for a warmer, more distinctive tone that harmonises with the existing warm-neutral surface palette and--color-accent-secondary; all Indigo-family tokens updated accordingly across light and dark mode - Design: module accent colours decoupled from severity colours — Meals moved to Orange-700 (
#C2410C), Shopping to Pink-600 (#DB2777), Budget to Teal-700 (#0F766E); previous Orange sharing between Meals, Shopping, Warning and Priority-Medium made badges semantically ambiguous - Design: Warning (
#A15C0A) and Danger (#B91C1C) raised to higher contrast ratios (5.2:1 and 6.9:1 respectively) for improved readability on white - Design: Priority-Medium separated into Amber-700 (
#A16207, 6.3:1) so it is visually distinct from Warning and Meals in the same row - Design: dark-mode accent shifted to Indigo-400/500 (
#818CF8/#6366F1) to preserve hue identity from light mode instead of the previous hue-shifted Sky-Blue
Fixed
- Tasks: overdue badge base styles (background colour, size, border-radius) moved from the dynamically-unloaded
tasks.csstolayout.css, so the badge remains visible in the navigation bar on every page, not just while the Tasks page is active (closes #56) - Tasks: subtask checkbox icon refactored from inline
style="color:#fff"to.subtask-item__checkbox-iconCSS class usingvar(--color-text-on-accent) - Reminders: three stale CSS fallback values removed (
var(--color-priority-urgent, #EF4444),var(--color-accent, #2563EB),var(--color-border, rgba(0,0,0,0.1)));color: #fffreplaced withvar(--color-text-on-accent) - Dashboard: widget customise button glass highlight replaced with existing
--color-glass*tokens instead of hardcodedrgba(255,255,255,…)literals
Accessibility
prefers-contrast: moreblock now overrides--module-notesto#A16207(6.3:1) to meet AA normal-text threshold in high-contrast mode
[0.20.14] - 2026-04-19
Fixed
- Tasks: overdue badge now consistently overlays the top-right corner of the nav icon in all three layouts (mobile bottom nav, collapsed sidebar, expanded sidebar). Root cause: the badge was positioned absolutely relative to the full-width
.nav-itemflex container, causing misalignment. Fixed by wrapping the icon SVG in a.nav-item__icon-wrapspan at runtime and appending the badge there instead (closes #56)
[0.20.13] - 2026-04-19
Added
- Budget: income entries now have dedicated categories (Earned Income, Investment Income, Transfer & Gift Income, Government & Social Benefits, Other Income) separate from expense categories; the category dropdown in the budget modal updates dynamically when switching between income and expense types (closes #55)
- Budget: all 15 supported locales include translations for the new income categories
[0.20.12] - 2026-04-19
Fixed
- Tasks: active filters are now correctly re-applied when navigating away from and back to the Tasks tab. Previously the filter chip appeared active but all tasks were shown, because the initial data fetch in
render()always called/taskswithout query parameters, ignoring the persistedstate.filters. Fixed by building the filter query inrender()the same wayloadTasks()does, so the first fetch already respects the current filter state (closes #49)
[0.20.11] - 2026-04-19
Fixed
- PWA: modal header (task / calendar event) no longer scrolls out of view when the form content exceeds the modal height. Root cause:
position: stickyon.modal-panel__headerfails on iOS WebKit when the scroll container (.modal-panel) haspadding-topapplied (a known WebKit quirk). Fixed by restructuring the modal layout:.modal-panelis now aflex-columncontainer withoverflow: hidden, and scrolling is handled by.modal-panel__body(overflow-y: auto; flex: 1). The header is always visible as a non-scrolled flex sibling. Swipe-to-close updated to read scroll position from.modal-panel__bodyinstead of.modal-panel(closes #50)
[0.20.10] - 2026-04-18
Changed
- Upgraded Express 4 → 5 (
^5.2.1): modernised wildcard SPA fallback route from'*'to'/{*path}'for compatibility with path-to-regexp v8; all other Express APIs in the codebase were already Express 5 compatible (closes #54)
[0.20.9] - 2026-04-18
Added
- Ukrainian (uk) translation (closes #52)
- Ukrainian Hryvnia (UAH) currency option in budget settings
- Shopping list category names are now translated in the settings panel; rename and delete dialogs also use the translated name
Fixed
- Server-side
VALID_CURRENCIESnow matches the frontend list —AED,BRL,INR, andSARwere accepted by the UI but rejected by the API
[0.20.8] - 2026-04-18
Changed
- Dependencies updated:
better-sqlite39 → 12,dotenv16 → 17,express-rate-limit7 → 8,express-session1.18 → 1.19,helmet8.0 → 8.1,googleapis144 → 171,tsdav2.0 → 2.1 (closes #53) - Added GitHub Dependabot configuration for automated weekly dependency updates
[0.20.7] - 2026-04-16
Fixed
- iOS PWA: large empty area visible between the bottom navigation bar and the physical screen edge. Root cause:
body::after(which covers the home indicator safe area) had the samez-indexas the nav bar (100) but was painted after all child elements by the browser's compositing order, causing it to render on top of the nav's glass background with a mismatched color (color-mixvsvar(--glass-bg)). Fixed by aligningbody::aftertovar(--glass-bg)andvar(--blur-md)(identical to the nav) and lowering itsz-indextoz-nav - 1so the nav always renders on top in the overlap area. - iOS PWA: app zoomed in when the virtual keyboard appeared and remained zoomed after the keyboard was dismissed, causing nav items and other elements to move outside the visible viewport. Added
focusin/focusoutlisteners inrouter.jsthat temporarily setmaximum-scale=1on the viewport meta tag while anINPUT,TEXTAREA, orSELECTis focused (prevents WKWebView auto-zoom), then restoremaximum-scale=5after a 150 ms delay once the field loses focus (preserves manual zoom for accessibility).
[0.20.6] - 2026-04-16
Fixed
- Android PWA: page transitions were taking ~1 second, making navigation feel sluggish. Two root causes addressed: (1)
glass.cssextended the page-in animation duration from0.20sto0.30swith a spring easing (ease-glass) — reverted to0.20sin and0.12sout to match the layout baseline. (2) During transitions, dozens ofbackdrop-filtercomposited layers (widgets, task cards, inputs, toolbars) were all rendered simultaneously for both the outgoing and incoming page, overwhelming mid-range Android GPUs. Addedhtml.navigatingstate:router.jssets this class for the duration of each page transition, andglass.cssoverrides allbackdrop-filterin the content area tononefor that window, then restores them once the animation ends (closes #48).
[0.20.5] - 2026-04-16
Fixed
- iOS PWA: persistent gap between the bottom navigation bar and the physical screen edge. Two root causes addressed: (1)
will-change: transformon the flex-child nav caused iOS WebKit's compositor to misplace the GPU layer — removed permanently; CSStransformtransitions work with hardware acceleration on modern iOS without this hint. (2) Added-webkit-fill-availableas a height fallback before100dvhon.app-shellto guard against iOS WebKit versions where100dvhis computed slightly smaller than the actual WKWebView height.
[0.20.4] - 2026-04-16
Fixed
- iOS PWA: bottom navigation bar appeared visually higher than on Android. Changed
.nav-bottomfromposition: fixedto a flex child of.app-shell(position: relative; flex-shrink: 0). Withposition: fixedandwill-change: transform(used for the hide/show animation), iOS's compositor could misplace the nav bar. As a flex child at the end of aheight: 100dvhcontainer, the nav is guaranteed to sit flush at the physical screen bottom on all platforms. Removed the redundantpadding-bottomclearance from.app-content,.tasks-page, and.dashboard(no longer needed since the nav no longer overlaps the content area).
[0.20.3] - 2026-04-16
Fixed
- iOS PWA: two visually distinct color zones at the bottom of the screen (below the bottom navigation bar). The
body::afterpseudo-element that covers the home indicator safe area now matches the bottom nav's appearance exactly - using the same semi-transparent background (color-mix) andbackdrop-filter: blur(16px) saturate(180%)- so the navigation bar blends seamlessly into the bottom edge of the screen.
[0.20.2] - 2026-04-16
Fixed
- iOS PWA: "Dashboard kann nicht geladen werden" toast after opening the PWA due to an
auth:expiredrace condition. When the session cookie was cleared by iOS between opens, the 401 response triggeredauth:expiredwhile navigation was still in progress (isNavigating=true), causing the redirect to/loginto be silently dropped. A_pendingLoginRedirectflag now defers the redirect until navigation completes. - SW cache bumped (shell v34, pages v29) to force iOS devices to pick up the previous CSRF fix that may still have been served from stale cache.
[0.20.1] - 2026-04-15
Fixed
- iOS PWA: recurring "forbidden" (403) errors caused by CSRF token desync after app resume. The server now sends the correct CSRF token as
X-CSRF-Tokenresponse header on every API response (not just/auth/meand/auth/login). The client reads the header from every response - including 403 errors - enabling instant self-healing without an extra/auth/meround-trip. SW cache bumped to v33 to ensure iOS PWA users pick up the fix.
[0.20.0] - 2026-04-15
Added
- Reminders: set time-based reminders on tasks and calendar events (closes #13)
- Tasks: enable a reminder with a custom date and time via the task edit modal
- Calendar events: choose an offset (at time, 15 min, 1 hour, or 1 day before) via the event edit dialog
- In-app toast notifications (built via DOM API, no external dependencies) appear when a reminder is due
- Browser Notification API support - reminders fire as system notifications when permission is granted
- Client-side polling every 60 seconds checks for pending reminders
- Reminders can be dismissed individually; dismissed reminders no longer appear
- Bell badge on each reminder shows pending count when reminders are due
- DB migration #8 adds
reminderstable withentity_type,entity_id,remind_at,dismissedfields and appropriate indexes
[0.19.6] - 2026-04-15
Added
- Meals: ingredient category selection when adding ingredients to a meal - each ingredient can now be assigned a shopping category (e.g. Fruit & Vegetables, Dairy, Meat & Fish) directly in the meal editor. Categories are automatically applied when transferring ingredients to the shopping list, so items appear pre-sorted in their correct category groups (closes #33)
[0.19.5] - 2026-04-14
Fixed
- iOS PWA: black gap below bottom navigation in standalone mode - iOS reserves the home indicator area outside the CSS viewport, leaving a visible black strip. A fixed
::afterpseudo-element onbodynow fills this area with the surface color. Also added explicitbackground-colortobodyelement.
[0.19.4] - 2026-04-14
Fixed
- iOS: persistent "forbidden" (403) errors caused by iOS Safari/PWA not reliably exposing CSRF cookie via
document.cookie. CSRF token is now returned in the response body of/auth/loginand/auth/meand stored in-memory, bypassing cookie read issues entirely. Cookie is still set as fallback. - CSRF retry:
/auth/merefresh now reads the token from the response body instead of relying on the cookie being available. Also handles expired sessions (401) during retry instead of silently failing.
[0.19.3] - 2026-04-14
Added
- Docker: multi-architecture image support (linux/amd64 + linux/arm64) - enables self-hosting on Raspberry Pi and other ARM64 devices (closes #44)
[0.19.2] - 2026-04-14
Improved
- Accessibility: FAB focus ring now uses a double-ring pattern (inner
--color-bg, outer--color-accent) visible on any background - previously hardcoded#fffwas invisible on light backgrounds - Accessibility: added
forced-colorsmedia query fallback for Windows High Contrast Mode (buttons, cards, modals, active nav items) - Design tokens: extracted
--color-accent-secondary,--content-max-width-narrow,--cal-hour-height- eliminates last hardcoded values in layout, settings, and calendar CSS - Dark mode: Apple sync logo in settings now uses semantic tokens (
--color-text-primary/--color-bg) instead of fixed neutrals that didn't invert correctly - Sidebar logo gradient now references
--color-accent-secondarytoken instead of hardcoded#7C5CFC
[0.19.1] - 2026-04-14
Fixed
- iOS PWA: "Forbidden" errors after app resume - CSRF cookie was not renewed on
/auth/me(the first API call after iOS kills and restarts the standalone webapp). iOS aggressively purges cookies of background webapps, causing CSRF token mismatch on all subsequent POST/PUT/DELETE requests - CSRF middleware: added try-catch and hex validation to prevent server crash from corrupted token cookies (iOS can mangle cookie values)
- API client: automatic CSRF token refresh and retry on 403 - state-changing requests that fail due to stale CSRF tokens are now transparently retried after renewing the token via
/auth/me - Service Worker: added 200ms delay before
controllerchangereload to prevent blank page on iOS standalone mode (the new SW needs time to completeclients.claim()before the page reloads)
[0.19.0] - 2026-04-14
Added
- i18n: Japanese (ja) locale - full translation with 567 keys; Hiragana/Katakana/Kanji script
- i18n: Arabic (ar) locale - full translation with 567 keys; RTL-ready text
- i18n: Hindi (hi) locale - full translation with 567 keys; Devanagari script
- i18n: Portuguese (pt) locale - full translation with 567 keys; Brazilian Portuguese
- Budget: AED (UAE Dirham), BRL (Brazilian Real), INR (Indian Rupee), SAR (Saudi Riyal) added to currency list
- Service Worker: new locale files pre-cached in APP_SHELL for offline support (sw v31)
[0.18.2] - 2026-04-14
Fixed
- Login failure behind Caddy/nginx reverse proxy in Docker: default
TRUST_PROXYchanged from'loopback'to1(trust one proxy hop). With'loopback', Express ignoredX-Forwarded-Proto: httpsfrom Caddy (which runs on a Docker bridge IP, not loopback), causingreq.secure = falseand express-session to silently drop the session cookie. The new default of1correctly handles any single-proxy setup without requiring manual configuration. docker-compose.yml: added inline comments explaining reverse proxy vs. direct-access configurationdocs/docker-compose.portainer.yml: added explicitTRUST_PROXYvariable with default1
[0.18.1] - 2026-04-14
Added
- Customizable dashboard layout: users can now show/hide individual widgets and reorder them via a settings button in the greeting header
- New "Anpassen" button (settings icon) in the dashboard greeting widget opens a modal with toggle switches and up/down controls for each widget (Tasks, Calendar, Shopping, Meals, Notes, Weather)
- Widget configuration persisted server-side via
dashboard_widgetspreference key insync_configtable - survives page reload and applies across all family members - Reset to default layout button in the customize modal
- New i18n keys for all 10 supported locales:
dashboard.customize,dashboard.customizeTitle,dashboard.customizeReset,dashboard.customizeSaved,dashboard.weather,dashboard.customizeMoveUp,dashboard.customizeMoveDown - Backend:
GET /api/v1/preferencesnow includesdashboard_widgetsin the response;PUT /api/v1/preferencesacceptsdashboard_widgetsarray with validation and normalization
[0.18.0] - 2026-04-14
Added
- Glass Phase 4: Liquid Glass Vibrancy + Tint - deeper glass penetration across all UI surfaces
- New glass tokens in
tokens.css:--glass-bg-card(52% opacity),--glass-bg-card-hover,--glass-bg-input,--glass-bg-toolbar,--glass-tint-strength(6% light / 8% dark) with full dark mode and accessibility overrides - Dashboard widgets now use semi-transparent glass backgrounds with
backdrop-filter: blur(8px) saturate(180%)- content beneath widgets shines through - Module tint: each widget gets a subtle accent color gradient overlay via
::afterpseudo-element usingcolor-mix(module-accent, 6%, transparent)- dashboard cards carry a hint of their module's color - Task cards, note items, and meal slots use glass backgrounds with blur for consistent vibrancy
- Page toolbars (Tasks, Notes, Contacts, Calendar) rendered as glass bars with module accent tint
- Form inputs, group toggles, and FAB speed-dial actions use glass vibrancy backgrounds
- App content background uses a radial gradient with the active module accent for ambient vibrancy
- Skeleton loading states use glass backgrounds for visual consistency
- All new glass effects gated behind
@supports (backdrop-filter)for progressive enhancement - Accessibility: all new effects respect
prefers-reduced-transparency(solid fallbacks) andprefers-reduced-motion - Load-order safety: all glass selectors use parent-scoped specificity (
.dashboard .widget,.tasks-page .task-card) to prevent override by on-demand page CSS
[0.17.4] - 2026-04-13
Fixed
- iOS PWA: bottom navigation no longer shifts upward in standalone mode - root cause was
bodyhavingmin-height: 100dvhand nooverflow: hidden, which allowed the body to scroll; in iOS WebKit standalone mode, body scroll movesposition: fixedelements rather than keeping them pinned; fix:htmlandbodyare nowoverflow: hiddenwith fixed height so all scrolling is confined to.app-content - Service worker: cache bumped to
shell-v30to ensure iOS devices receive the updatedreset.css
[0.17.3] - 2026-04-13
Fixed
- CSS:
glass.cssnow works on Safari < 18 - all@supportschecks extended to(backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))so older Safari (which only understands the-webkit-prefix) no longer skips the entire block - CSS: non-blur glass styles (background-color, border, box-shadow) moved outside
@supportsblocks - they are now always active on all browsers and devices, regardless ofbackdrop-filtersupport
[0.17.2] - 2026-04-13
Fixed
- Auth: session cookie and CSRF cookie changed from
SameSite=StricttoSameSite=Lax- Safari's ITP (Intelligent Tracking Prevention) was blockingStrictcookies on certain navigations (direct URL entry, reverse proxy), causing a 401 on login while other browsers worked fine (#46)
[0.17.1] - 2026-04-13
Fixed
- Service worker:
glass.csswas missing from the shell cache list - on already-installed PWA instances the file was never loaded and no glass effects were visible; cache bumped toshell-v29 - CSS load order:
.widgetglass shadow and border were overridden bydashboard.css(module CSS loads afterglass.css); glass styles moved directly intodashboard.css - CSS load order:
.filter-chip--activeglass state was overridden bytasks.css;@supports backdrop-filterblock moved intotasks.css - CSS load order:
.priority-badgeborder-radius was reset tovar(--radius-xs)bytasks.css, losing the capsule shape; corrected tovar(--radius-glass-chip)intasks.css glass.css: removed dead.sticky-headerrule (class is not used anywhere in the HTML)
[0.17.0] - 2026-04-13
Added
- Design system:
public/styles/glass.css- new additive layer (~430 lines) implementing Liquid Glass aesthetics: translucent surfaces,backdrop-filterblur, capsule shapes, specular highlights, and spring-based motion; loaded globally afterlayout.css, all blur effects gated behind@supports (backdrop-filter: blur(1px)) - Design system: Section 16 "Glass Tokens" added to
tokens.css- ~50 new custom properties covering--glass-bg*,--glass-border*,--blur-xs/sm/md/lg/xl,--opacity-glass-*,--glass-highlight*,--glass-shadow-sm/md/lg,--radius-glass-card/inner/chip/button,--ease-glass,--transition-glass; full dark mode overrides in both@media (prefers-color-scheme: dark)and[data-theme="dark"]blocks - Navigation: bottom bar now auto-hides on scroll-down (mobile only, < 1024px), reappears on scroll-up with 4 px hysteresis; implemented via
initNavHideOnScroll()inrouter.jsand.nav-bottom--hiddenCSS class inglass.css;will-change: transformon.nav-bottomfor smooth GPU-composited animation - Animations: modal entrance uses spring easing (
glass-modal-scale-in+glass-sheet-inkeyframes) instead of linear fade; page transitions use spring-eased translate instead of plain ease-out; list items stagger with springcubic-bezier(0.34, 1.56, 0.64, 1)spring curve - Accessibility:
prefers-reduced-transparency,prefers-reduced-motion, andprefers-contrast: moremedia query blocks in bothtokens.cssandglass.css- glass effects deactivate and solid fallbacks activate automatically
Changed
- Glass input styles:
.contacts-toolbar__search-input,.notes-toolbar__search-input, and.quick-add__inputnow use--radius-glass-button,--glass-border-subtle, and acolor-mixfocus ring for visual consistency with the glass layer (applied directly in module CSS files to respect CSS load order) - Bottom nav / sidebar: glass blur surface, subtle top highlight, elevated shadow via
glass.css - Modal: glass overlay, spring entrance, capsule close button, specular FAB ring pulse (
fab-ring-pulsekeyframe) - Buttons / FAB: capsule shape via
--radius-glass-button, specular inner highlight on primary buttons, glass hover glow on secondary - Skeleton loading: upgraded shimmer gradient uses glass highlight colors
- Focus rings: animated expand-contract ring via
glass-focus-ringkeyframe, applied to interactive glass elements - PWA viewport:
maximum-scalechanged from1to5(WCAG 1.4.4 - Resize Text, users can pinch-zoom again) - Theme color meta tag:
#007AFF→#2563EB(light) and#1C1C1E→#222220(dark) to match updated token palette
Fixed
- Accessibility:
--color-text-tertiarycorrected from#737370to#6B6B68(passes WCAG AA 4.5:1 on--color-bg) - Accessibility:
--color-infocorrected from#54AEFFto#0969DA(passes WCAG AA 4.5:1 on white) - Accessibility: modal overlay now carries
aria-labelandrole="presentation"for correct screen-reader semantics - Settings: fixed three stale token references (
--color-background→--color-bg,--duration-fast→--transition-fast,--color-surface-raised→--color-surface-2) - Notes: fixed stale token reference
--color-text→--color-text-primaryin search input border - Dashboard: weather widget gradient now uses
var(--color-accent-deep)instead of hardcoded#1E5CB3 - Meals: badge padding now uses spacing tokens (
var(--space-0h) var(--space-2)) instead of hardcoded2px 8px
[0.16.3] - 2026-04-13
Added
- i18n: five new UI languages - French (fr), Turkish (tr), Russian (ru), Greek (el), and Chinese Simplified (zh) with full translations of all keys
- Budget: TRY (Turkish Lira) and RUB (Russian Ruble) added to the list of selectable currencies in Settings
- i18n: Italian locale now includes the complete
rrulesection (was missing previously)
[0.16.2] - 2026-04-13
Added
- Budget: CNY (Chinese Yuan) added to the list of selectable currencies in Settings (#42)
[0.16.1] - 2026-04-13
Fixed
- i18n: fallback language for unsupported browser locales changed from German to English (#43)
- Apple CalDAV sync: calendar events with a
TZIDparameter are now correctly converted to UTC instead of being treated as floating local time, fixing wrong start times for events synced from iOS Calendar (#43)
[0.16.0] - 2026-04-06
Added
- Settings: categorized tab navigation - six tabs (General, Meals, Budget, Shopping, Calendar, Account) replace the flat scrolling layout (#30)
- Settings: active tab persists across page navigations via sessionStorage
- Settings: Calendar tab is automatically activated when returning from a Google/Apple OAuth callback
- Settings: tab bar is sticky so it stays visible while scrolling through tab content
- Settings: all tab labels fully translated in de, en, es, it, sv
[0.15.0] - 2026-04-06
Changed
- Modal: two-column form layouts now use reusable
.modal-gridand.modal-grid--2CSS classes instead of inlinestyleattributes - applied across Calendar, Meals, and Tasks modals (#38) - Modal: panel on mobile now has a subtle border and large shadow for better depth and visual separation (#38)
- Modal: form groups inside grid layouts no longer need inline
margin-bottom:0overrides - handled by.modal-grid > .form-grouprule (#38)
[0.14.4] - 2026-04-06
Fixed
- PWA iOS: pinch-to-zoom disabled - added
user-scalable=no, maximum-scale=1to viewport meta tag for native-app feel (#16) - PWA iOS: residual body scroll fully blocked - added
overflow: hiddentohtml, bodyso any minimal content overflow can no longer make the page body scrollable (#16) - Service worker cache bumped to v28/v27 (#16)
[0.14.3] - 2026-04-06
Fixed
- PWA iOS: scroll bleed fully resolved -
padding-top: env(safe-area-inset-top)moved frombodyto.app-shell; body-padding was pushing.app-shell(height: 100dvh) beyond the viewport bottom, allowing the page body itself to scroll (#16) - PWA iOS: all fixed-height page containers (Calendar, Shopping, Meals, Notes, Budget, Contacts) now subtract
--safe-area-inset-topfrom their height calculation so they no longer overflow.app-contentin standalone mode (#16) - Added
--safe-area-inset-topCSS token (mirrorsenv(safe-area-inset-top, 0px)) for consistent use across all page layout calculations (#16) - Service worker cache bumped to v27/v26 to ensure CSS changes are picked up on next update (#16)
[0.14.2] - 2026-04-06
Fixed
- Modal: overlay tap now reliably closes the modal on iOS Safari / PWA - added
cursor: pointerto the overlay (iOS requires this on non-interactive elements to fire click events) and atouchendfallback (#29) - Modal: close button enlarged from 32px to 40px to meet Apple's 44px touch-target recommendation (#29)
- Modal: swipe-to-close no longer triggers when scrolling content inside the sheet - drag only activates from the top handle zone or when the panel is scrolled to the top (#29)
[0.14.1] - 2026-04-06
Fixed
- Calendar: toolbar no longer overflows on narrow screens (< 580px) - view buttons (Monat/Woche/Tag/Agenda) now wrap to a second row so navigation and label remain fully visible (#31)
- Tasks: page title no longer visually overlaps action buttons on narrow screens - title now truncates with ellipsis when space is constrained (#31)
- Shopping: list name no longer overlaps action buttons when the name is long or the "clear checked" button is visible - name now truncates cleanly (#31)
[0.14.0] - 2026-04-05
Added
- Spanish (Español) translation - all sections fully translated (tasks, calendar, meals, shopping, budget, notes, contacts, settings) (#28)
[0.13.0] - 2026-04-05
Added
- Meals: optional recipe link per meal - add a URL in the meal modal and a link icon appears on the card for one-tap access to the recipe (#18)
- Meals:
recipe_urlfield stored in the database (migration v6)
[0.12.0] - 2026-04-05
Added
- Shopping: custom categories - add, rename, delete and reorder shopping list categories in Settings → Shopping (#26)
- Shopping: categories are now stored in the database (
shopping_categoriestable, migration v5) and fully customizable per household - Shopping: category order in the shopping list reflects the custom sort order from Settings
- Shopping: items belonging to a deleted category are automatically moved to the next available category
[0.11.9] - 2026-04-05
Changed
- README: updated highlights to mention Kanban quick-status buttons and configurable budget currency; replaced docker badge with GHCR link
- docs/installation.md: restructured setup into Option A (pre-built GHCR image, no clone needed) and Option B (build from source); updated Updates section accordingly; added tip to SQLCipher troubleshooting entry
- docs/index.html (GitHub Pages): updated Get Started code block to show pre-built image path; updated task and budget feature descriptions (EN + DE) to reflect new features
[0.11.8] - 2026-04-05
Changed
docker-compose.ymlnow references the pre-built GHCR image (ghcr.io/ulsklyc/oikos:latest) by default - no local build needed to get started (#25)- README Quick Start now shows both the pre-built image path (no clone required) and the build-from-source path
[0.11.7] - 2026-04-05
Added
- Kanban view: quick-status button on each card to advance status without drag-and-drop (open → in progress → done → open) - useful for touch devices and kiosk browsers (#24)
[0.11.6] - 2026-04-05
Fixed
- Swedish translation: added missing rrule keys (recurrence frequency, weekday abbreviations, unit labels) - contributed by @olsson82 (#23)
[0.11.5] - 2026-04-05
Fixed
- Shopping list category dropdown now shows translated labels instead of hardcoded German strings (#21)
- Recurrence fields in task and calendar modals now fully translated (labels, frequency options, weekday abbreviations, unit labels) (#21)
[0.11.3] - 2026-04-05
Added
- Swedish (Svenska) translation - contributed by @olsson82 (#19)
- Italian (Italiano) is now explicitly listed as a language option in Settings
[0.11.2] - 2026-04-05
Added
- Configurable currency for the budget section: choose from 13 currencies (EUR, USD, GBP, SEK, NOK, DKK, CHF, PLN, CZK, HUF, JPY, AUD, CAD) in Settings → Budget (#20)
- Currency preference is stored household-wide via the preferences API and applied to all budget amounts and formatting
[0.11.1] - 2026-04-05
Fixed
- Fix dashboard meal widget ignoring meal type visibility settings - todayMeals query now reads visible_meal_types from sync_config and filters accordingly, consistent with the Meals page (#14)
[0.11.0] - 2026-04-05
Added
- Microinteraction improvements: subtle entrance animations, hover/active feedback, and transition polish across cards, buttons, FABs, and nav items
Fixed
- Fix touch scroll on dashboard and all pages - use
heightinstead ofmin-heighton app-shell to prevent overflow blocking touch scroll on iOS/Android - Add
inputmodeandautocompleteattributes to form inputs for better mobile keyboard and autofill UX - Resolve design system audit violations: align spacing, color, border-radius, and shadow usage to tokens throughout all pages and components
- Fix touch scrolling regression in calendar, budget, and contacts introduced by layout refactor
[0.10.0] - 2026-04-04
Added
- Customizable meal type visibility: toggle breakfast, lunch, dinner, snack on/off in Settings (#14)
- New household-wide preferences API (
GET/PUT /api/v1/preferences) using existingsync_configtable - New "Meal Plan" section in Settings page with checkbox toggles per meal type
- Meals page filters displayed slots based on household preference
- i18n keys for meal visibility settings in DE, EN, IT
[0.9.1] - 2026-04-04
Added
- Persist task view mode (list/kanban) across sessions via localStorage (#17)
- Support URL parameter
?view=kanbanto open tasks directly in Kanban view - ideal for tablet kiosk setups - View toggle button reflects the persisted/URL-driven view on page load
[0.9.0] - 2026-04-04
Added
- Optional task priority: new "None" level allows tasks without urgency, reducing visual noise for routine tasks (#15)
- "None" is now the default priority for new tasks
- Tasks with no priority hide the priority badge entirely in list and dashboard views
- DB migration v4 extends priority CHECK constraint to include 'none'
- i18n keys for "None" priority in de, en, it locales
[0.8.2] - 2026-04-04
Fixed
- Fix UI overlap and scroll bleed on iOS PWA - remove double safe-area padding from body that caused content to shift under status bar (#16)
- Fix page containers using wrong nav height token (56px instead of 68px including dot indicator), causing content to render behind bottom nav on all pages
- Add
overflow: hiddento all fixed-height page containers (shopping, meals, notes, budget, contacts) to prevent scroll bleed - Add
overscroll-behavior-y: containto app-content to prevent rubber-banding scroll propagation - Fix FAB position on all pages to account for full bottom nav height including dot indicator
- Bump service worker cache version to v23
[0.8.1] - 2026-04-04
Fixed
- Replace native
prompt()dialogs with custom modals in shopping (create/rename list), tasks (add subtask), and meals (choose shopping list) - native prompts were unreliable on mobile/PWA, requiring multiple clicks to close (#12)
[0.8.0] - 2026-04-04
Added
- Shopping list widget on dashboard - shows lists with open items, progress bar, and item preview (discussion #9)
[0.7.7] - 2026-04-04
Fixed
- Fix modal not closing on mobile when tapping Cancel or Save - add fallback timer for cases where CSS animationend event does not fire (prefers-reduced-motion, tab switch, etc.)
[0.7.6] - 2026-04-04
Fixed
- Fix untranslated category names in tasks (group headers), budget (bar chart labels, transaction meta) - all displayed category strings now go through i18n mapping (#11)
[0.7.5] - 2026-04-04
Fixed
- Fix flash of unstyled content (FOUC) during page transitions - old module stylesheet is now kept until old content is removed from DOM, new content hidden until render completes
- Smooth nav-item tap transition (0.12s ease) instead of abrupt scale snap
- Add
:focus-visibleoutline to interactive cards, buttons, FABs, and toggles for keyboard navigation
Added
- Custom iOS-style toggle switch component (
.toggle) replacing native checkboxes in calendar, notes, and budget modals - Toast notification icons - SVG checkmark (success), alert circle (danger), warning triangle (warning) alongside color coding
- Empty-state fade-in animation (0.4s ease-out, respects
prefers-reduced-motion) - Swipe haptic feedback at threshold -
vibrate(15)fires when swipe reaches 80px during touchmove in tasks and shopping - Interface design system documentation (
.interface-design/system.md)
[0.7.4] - 2026-04-04
Fixed
- Replace hardcoded
box-shadowvalues in.btn--primarywith--shadow-sm/--shadow-mdtokens - Replace
border-radius: 50%withvar(--radius-full)in layout and calendar styles - Align ~25 off-grid spacing values (5px, 6px, 7px, 14px, 15px, 22px, 26px, 34px) to 4px grid using
--space-*tokens
Changed
- Extract 8 hardcoded
rgba()colors from dashboard, shopping, and weather styles into new design tokens (--color-glass,--color-glass-hover,--color-glass-border,--color-danger-translucent)
[0.7.3] - 2026-04-04
Accessibility
- Increase font-size to 16px (
--text-md) on mobile forquick-add__input,quick-add__qty,quick-add__cat(shopping),notes-toolbar__search-input, andcontacts-toolbar__search-input- prevents iOS auto-zoom on input focus (WCAG touch-friendly inputs)
Performance
- Lazy-load page-specific stylesheets on route change instead of loading all 10 upfront in
index.html- reduces initial CSS payload; only tokens, reset, pwa, layout, and login styles are render-blocking
[0.7.2] - 2026-04-04
Accessibility
- Rename
#page-contentto#main-contentso the existing skip-to-content link targets the semantic<main>landmark correctly - Add
sr-onlypriority labels to dashboard task items - screen readers now announce priority level instead of relying on color alone (WCAG 1.4.1)
Fixed
- Replace hardcoded hex values in greeting widget gradient with
--color-accent-active/--color-accenttokens - dark mode now correctly themes the greeting banner - Replace hardcoded
gap: 2pxwith--space-0htoken in greeting widget
[0.7.1] - 2026-04-04
Security
- Fix stored XSS across all pages - extract shared
esc()utility (public/utils/html.js) and apply HTML escaping to all user-controlled data in innerHTML templates (titles, names, locations, descriptions, colors, notes content, autocomplete suggestions) - Remove
user-scalable=noandmaximum-scale=1from viewport meta tag - restores pinch-to-zoom accessibility (WCAG 1.4.4)
Changed
- Deduplicate 8 identical
escHtml()functions (tasks, shopping, calendar, notes, meals, contacts, budget, settings) into single sharedesc()import fromutils/html.js - Shared
esc()also escapes single quotes ('to') for safer attribute contexts
0.7.0 - 2026-04-04
Security
- Upgrade bcrypt from 5.1.1 to 6.0.0 - resolves 4 HIGH path traversal CVEs in transitive
tardependency via@mapbox/node-pre-gyp - Remove hardcoded fallback session secret - server now always throws if
SESSION_SECRETis unset, regardless ofNODE_ENV
Changed
- Breaking: Migrate entire server and test suite from CommonJS to ESM - all
require()/module.exportsreplaced withimport/export;"type": "module"added topackage.json - Replace 40+ unstructured
console.*calls withserver/logger.js- thin wrapper supportingLOG_LEVELenv var (debug/info/warn/error), zero new dependencies - Translate
package.jsondescription to English for consistency with all other documentation - Translate
.env.examplecomments from German to English for international contributors - Translate
.gitignorecomments to English
Removed
- Remove internal audit documents (
docs/claude-md-audit.md,docs/repo-audit-2026-04-02.md) from tracked files - Remove empty
.worktrees/leftover directory
Added
- Add
CODE_OF_CONDUCT.md(Contributor Covenant v2.1) - Add
.gitignorepatterns for audit report files (docs/audit-report-*.md,docs/*-audit.md)
0.6.0 - 2026-04-03
Fixed
- Fix budget entry update failing with "Internal Error" when changing category -
datevalidator import shadowed thedatefield from the request body, causing SQLite to receive a function reference instead of a string value (fixes #8)
0.5.9 - 2026-04-03
Security
- Fix stored XSS in task titles and subtask titles - all user-provided text in tasks.js is now escaped via
escHtml()before insertion into innerHTML templates - Fix stored XSS in settings page member list - display_name and username are now escaped via
escHtml()inmemberHtml() - Fix rate limiter bypass via X-Forwarded-For IP spoofing -
trust proxynow defaults toloopbackinstead of unconditional1; configurable viaTRUST_PROXYenv var - Fix Google OAuth CSRF - add cryptographic
stateparameter to OAuth flow, validated on callback - Fix CSV injection in budget export - fields starting with
=,+,-,@, tab, or carriage return are now prefixed with apostrophe - Fix missing session invalidation on user deletion - all active sessions of deleted users are now destroyed
- Restrict username to
[a-zA-Z0-9._-]with minimum 3 characters, preventing HTML/script injection via usernames - Restrict Google Calendar sync trigger (
POST /google/sync) and Apple Calendar sync trigger (POST /apple/sync) to admin role - Add warning log when Apple CalDAV credentials are stored without DB encryption enabled
0.5.8 - 2026-04-03
Added
- Add Italian (Italiano) localization - full translation of all 497 i18n keys (thanks @albanobattistella, PR #7)
- Add Italian as selectable language in Settings locale picker
0.5.7 - 2026-04-03
Fixed
- Fix recurring calendar events not expanding - RRULE parser now strips the
RRULE:prefix used by ICS/CalDAV, which previously caused all recurrence rules to be silently ignored - Fix recurring multi-day events not appearing when their start date falls before the view window but the event spans into it
- Fix all-day recurring event instances getting datetime end values instead of date-only format
- Add YEARLY recurrence frequency support for birthday and anniversary events
0.5.6 - 2026-04-03
Fixed
- Fix all-day calendar events appearing on the correct day and the following day - ICS DTEND for DATE values is exclusive per RFC 5545, now correctly adjusted (fixes #5)
- Fix multi-day events not showing when using DURATION instead of DTEND - add ICS DURATION property support in CalDAV parser
- Fix birthdays from Apple Calendar not syncing - birthday calendars are no longer excluded from sync
- Fix outbound ICS builder using inclusive DTEND for all-day events - now correctly emits exclusive DTEND per RFC 5545
0.5.5 - 2026-04-03
Fixed
- Fix iCloud Calendar sync failing with FOREIGN KEY constraint error -
created_bywas hardcoded to user ID 1 instead of resolving dynamically (fixes #4) - Sync all iCloud calendars instead of only the first one - previously only a single calendar was imported, ignoring Family, subscribed, and other calendars
- Add missing
cfgDelhelper function used byclearCredentials- disconnecting Apple Calendar would crash - Skip unreachable or broken calendars gracefully instead of aborting the entire sync
0.5.4 - 2026-04-03
Fixed
- Fix SQLCipher PRAGMA key syntax error on fresh install - hex-encoded key must be wrapped in double quotes for valid PRAGMA syntax (fixes #3)
0.5.3 - 2026-04-03
Security
- Fix SQLCipher PRAGMA key interpolation - encryption keys containing single quotes no longer crash on startup; key is now hex-encoded
- Enforce minimum password length (8 characters) when admin creates new users - previously any 1-character password was accepted
- Add length bounds on username (64 chars) and display_name (128 chars) to prevent unbounded input
- Add input length bounds on login (username 64 chars, password 1024 chars)
- Invalidate all other sessions when a user changes their password - previously active sessions survived password reset
- Session and CSRF cookies now have
secure: trueby default; HTTP is only allowed whenSESSION_SECURE=falseis explicitly set in.env- previously cookies were sent withoutSecureflag in non-production environments - Document authorization model in SECURITY.md - clarify that all family members share read/write access to all data by design
Changed
- Use multi-stage Docker build to exclude build tools (python3, make, g++) from runtime image
- Exclude
docs/directory from Docker image via.dockerignore - Consolidate
dotenv.config()to single call inserver/index.js- remove duplicate calls fromserver/db.jsandserver/auth.js
0.5.2 - 2026-04-01
Security
- Add rate limiting to SPA fallback route to prevent file system hammering via unauthenticated wildcard requests
- Add CSRF protection to auth routes that change state (logout, create user, change password, delete user) - previously bypassed global CSRF middleware due to router registration order
- Fix incomplete vCard escaping in contacts export - backslash characters are now escaped first before other special characters (
,,;, newline), preventing injection via contact fields - Restrict CI workflow GITHUB_TOKEN to
contents: read(principle of least privilege)
0.5.1 - 2026-04-01
Fixed
- Meals: fixed crash when dragging a meal slot -
draggingstate is now destructured beforecleanup()runs, preventing a null-reference error on drop - i18n:
t()now resolves dot-notation keys against nested locale JSON objects (e.g.t('nav.tasks')correctly returns"Aufgaben"instead of the raw key string); affects all pages, components, and navigation - PWA: replaced placeholder "O" icons with the actual Oikos house logo across all icon variants (192, 512, maskable 192, maskable 512, apple-touch-icon, favicon); maskable variants use full-bleed background with logo within the 80% safe zone - fixes Android home screen showing only a blue circle
- PWA: weather widget icons (OpenWeatherMap) now render correctly in installed PWA on Android; service worker no longer intercepts cross-origin image requests (opaque responses caused silent rendering failures in standalone mode)
- Settings: language selector replaced from cramped radio buttons to a native
<select>dropdown using the standardform-inputstyle
Changed
- PWA manifest: added
idfield anddisplay_overridearray for reliable Chrome Android PWA recognition;manifest.jsonis now served withContent-Type: application/manifest+json - Service worker (v22):
/i18n.jsand locale files added to app-shell cache; cross-origin asset requests excluded from cache-first strategy
0.5.0 - 2026-03-31
Added
- i18n: full internationalisation system (
public/i18n.js) with German (de) and English (en) support; language auto-detected fromnavigator.language, overridable via Settings - i18n: all user-facing strings moved to locale files (
public/locales/de.json,public/locales/en.json); 489 translation keys covering all modules - i18n: locale switch without page reload - all pages, components and navigation re-render via
locale-changedcustom event - i18n:
oikos-locale-pickerWeb Component in Settings - three options: System (follows browser language), Deutsch, English - i18n: dates and times formatted with
Intl.DateTimeFormatusing the active locale;formatDate()andformatTime()exported fromi18n.js - i18n: fallback chain (active locale → German → key) ensures no untranslated keys are shown even if a future locale file is incomplete
- i18n: adding a new language requires only one JSON file (
public/locales/xx.json) and one line inSUPPORTED_LOCALES
0.4.0 - 2026-03-31
Fixed
- Mobile: toast notifications no longer overlap with the bottom navigation bar - introduced
--nav-bottom-heighttoken (scroll area 56px + dots indicator 12px) used consistently by toast container and app content padding - Mobile: FAB and page-FAB are now hidden when the virtual keyboard is open, preventing them from covering form inputs; detection uses
visualViewport.resizewith a 75% height threshold - UI: added missing dark-mode colour overrides for shopping, notes, contacts, budget, and settings module tokens - accent stripes now render at readable pastel values in dark theme
- UI: meals week-navigation bar now shows module accent top-border stripe; settings page now declares --module-accent for consistency with all other modules
Added
- Shopping: swipe-left to toggle checked/unchecked, swipe-right to delete items on mobile; × delete button hidden on mobile in favour of swipe gesture
- Notes: client-side full-text search bar in toolbar - filters by title and content instantly; shows "Keine Treffer" empty state when no match
- Dashboard: weather widget refresh button (top-right corner) + automatic 30-minute refresh interval; interval is cleared when navigating away
- Contacts: vCard export button per contact (downloads .vcf file); vCard import via file input in toolbar (parses FN, TEL, EMAIL, ADR, NOTE, CATEGORIES fields)
- PWA: offline fallback page (
/offline.html) served by service worker when network is unavailable and index.html is not cached; page includes a reload button - UI: module accent colours now applied to three visual layers - active nav tab (bottom bar + sidebar), toolbar top-border stripe, and list/card left-border stripe - giving each module a distinct colour identity
0.3.0 - 2026-03-31
Added
- Calendar: recurring events are now expanded in GET /api/v1/calendar - all occurrences within the requested date window are returned as virtual instances; duration is preserved; instances are marked with is_recurring_instance=1 and shown with a ↻ icon in the agenda view; /upcoming also expands recurring events within a 90-day window
- Budget: recurring entries auto-generate instances for each viewed month; instances deleted by the user are skipped permanently via
budget_recurrence_skippedtable; generated instances are marked with ↩ in the transaction list - Budget: month-over-month comparison in summary cards - each card (Einnahmen, Ausgaben, Saldo) shows a trend line (▲/▼ + delta amount vs. previous month); previous month summary is fetched in parallel with current month
- Meals: drag & drop between slots and days using Pointer Events (touch + mouse); ghost element follows pointer; drop on occupied slot swaps meals; reduced-motion: no ghost animation, interaction still works
- Settings: Apple CalDAV credentials form (URL, Apple-ID, app-specific password) with live connection test; admin can connect and disconnect via UI without restarting the server; DB-stored credentials take precedence over .env vars; auto-sync runs every 15 min (configurable via SYNC_INTERVAL_MINUTES)
0.2.1 - 2026-03-30
Fixed
- Accumulating click listeners on
#notes-grid: listener is now registered once inrender()via event delegation instead of re-registered in everyrenderGrid()call - Accumulating anonymous
documentclick listener in dashboard FAB:initFab()now accepts an AbortSignal;render()aborts the previous signal before creating a new one, eliminating listener leaks across navigation cycles - Add
btnError()shake feedback to notes.js save error handler for consistency with other modules - Calendar event popup
closePopuplistener now checkspopup.isConnectedto self-remove correctly after navigation without a click
Added
- CSS alias
.form-labelalongside.labelto cover usage innotes.jsandsettings.jswithout requiring a mass-rename - Tests for
wireBlurValidation,btnSuccess, andbtnError(12 cases) intest-modal-utils.js
0.2.0 - 2026-03-30
Changed
- Directional slide-x page transitions (forward = right, backward = left) with race condition guard
- PWA install prompt delayed until 2 user interactions; dismiss window reduced from 30 to 7 days; interaction counter resets on dismiss
- Unified card padding to 16px (
--space-4) across tasks, contacts, budget, and meals modules
Added
- Staggered fade-in animation for list items on page load across all modules (tasks, shopping, meals, contacts, budget, notes, calendar agenda)
- Unified empty states using shared
.empty-stateclass across all modules (replaces per-module CSS) stagger()andvibrate()UX utilities inpublic/utils/ux.jswith full test coverage- Proportional opacity on swipe-reveal action areas in tasks (already implemented, confirmed)
- FAB colors tied to per-module accent tokens via CSS custom properties
scrollIntoViewfor focused inputs when virtual keyboard opens in modals (300ms delay)- Consistent vibration feedback via
vibrate()utility across tasks, shopping, contacts, budget, and notes - Bottom sheet modal on mobile (< 768px) with drag handle, slide-in animation, and swipe-to-close
- Enter-key navigation between form fields in modals; Enter on last field triggers submit
- Blur-triggered inline validation for required fields with error/success border states
wireBlurValidation(),btnSuccess(), andbtnError()exported frommodal.js- Submit button checkmark-success (700ms) and shake-error feedback animations
0.1.0 - 2026-03-29
Initial release of Oikos - a self-hosted family planner for 2–6 person households. Runs as a Docker container behind Nginx with SSL, no cloud dependency.
Added
- Dashboard with time-of-day greeting, urgent tasks, upcoming events, today's meals, pinned notes, and weather widget (OpenWeatherMap integration with 3–5 day forecast scaling by screen size)
- Task management with categories, priorities, due dates, subtasks (max 2 levels), list and Kanban views, swipe gestures on mobile (swipe left = toggle done, swipe right = edit), and recurring tasks via iCal RRULE
- Shopping lists with multiple named lists, supermarket-aisle sorting, autocomplete from history, optimistic checkbox toggle, and bulk-clear of checked items
- Weekly meal planner with breakfast/lunch/dinner/snack grid (Mon–Sun), ingredient tracking per meal, and one-click transfer of ingredients to shopping lists
- Calendar with month, week, day, and agenda views, multi-day event support, color-coded entries, and family member assignment
- Google Calendar sync via OAuth 2.0 with incremental sync tokens and Apple CalDAV sync via tsdav, both bidirectional
- Pinboard (notes) with color-coded sticky notes, pin-to-top, Markdown formatting toolbar (bold, italic, lists, headings, code, links), and automatic text contrast based on background color
- Contacts directory with category filtering (doctor, emergency, trades, etc.), full-text search, and direct tel:/mailto:/maps: links
- Budget tracker with income/expense logging, monthly navigation, category breakdown bar charts (pure CSS), and CSV export
- Settings page for password change, calendar sync status, and family member management
- Authentication with session-based login (bcrypt, httpOnly/secure/sameSite cookies, 7-day TTL), admin-only user creation, and rate-limited login (5 attempts/min with 15-min lockout)
- CSRF protection using Double Submit Cookie pattern with timing-safe comparison
- Progressive Web App with app-shell caching (service worker with stale-while-revalidate for static assets, network-first for navigation, network-only for API), custom install prompt for Android and iOS, dynamic theme-color per module, safe area inset handling, and offline fallback
- Responsive design with mobile bottom navigation (swipeable pages with dot indicator), collapsible sidebar on tablet, and full sidebar on desktop
- Dark mode with system preference detection and manual toggle, warm-tinted neutral color scale
- Design system with CSS custom properties (tokens for colors, spacing, typography, shadows, radii, z-indices), module-specific accent colors, and consistent component patterns
- Accessibility improvements: skip link, sr-only headings on all pages, aria-hidden decorative icons, aria-label on icon-only buttons, token-based touch targets (44–48px), 12px minimum font size, and prefers-reduced-motion support
- Docker deployment with docker-compose, optional SQLCipher encryption (AES-256), and nginx.conf example
- Setup script (
node setup.js) for initial admin account creation with LAN-reachable URL display - Input validation middleware with centralized rules (string length, date/time format, enum, color) across all API routes
- Content Security Policy via Helmet with strict CSP, self-hosted Lucide Icons (no CDN at runtime)
- Lazy loading with per-page ES module imports cached in memory, Cache-Control headers (immutable for assets, must-revalidate for code), and service worker update notification
Security
- Fail-fast on missing
SESSION_SECRETin production - Rate limiting on login endpoint and global API limiter (300 req/min/IP)
- No user data cached by service worker (API requests are network-only)
- Hardened
.gitignoreand.dockerignoreto prevent accidental secret or binary leakage