On Safari/iOS PWA cold start or after cookie clear, logging in with wrong
credentials triggered auth:expired, re-rendering the login page and losing
the error message. The login endpoint returns 401 for invalid credentials,
not for session expiry, so apiFetch must not fire auth:expired in that path.
Resolves#68
Co-authored-by: Ulas Kalayci <ulas.kalayci@googlemail.com>
All tokens with dark-mode overrides gain a private --_name counterpart in :root.
Public tokens (--color-*, --module-*, --glass-* etc.) become stable var(--_name)
references. Both dark blocks now only override compact private tokens — no more
manual two-block sync for every future colour change.
Also removes the redundant --color-surface-2 dark override (already auto-derived
via var(--neutral-50)). No visual change.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Bundles the Indigo accent migration, module-colour decoupling, WCAG
contrast improvements and nav-badge base-style relocation into one
release. See CHANGELOG.md [0.20.15] for full details.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
render() always fetched /tasks without query params, so active filter
chips appeared selected but all tasks were shown after navigating away
and back. Fixed by building the same filter query in render() that
loadTasks() uses, keeping both parallel fetches and correct filtering.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
position: sticky on .modal-panel__header failed on iOS WebKit when the
scroll container had padding-top applied (drag-handle spacing). Restructured
modal layout: .modal-panel is now a flex-column with overflow:hidden and
.modal-panel__body handles scrolling (overflow-y:auto, flex:1). The header
is a non-scrolled flex sibling, so it stays visible without sticky. Updated
swipe-to-close to read .modal-panel__body scroll position.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add Ukrainian (uk) locale to SUPPORTED_LOCALES and locale picker
- Add public/locales/uk.json (622 keys, full Ukrainian translation)
- Add UAH (Ukrainian Hryvnia) to SUPPORTED_CURRENCIES and VALID_CURRENCIES
- Add CATEGORY_I18N map and catLabel() in settings.js to translate default
shopping category names in the settings panel; rename and delete dialogs
now also use the translated name instead of the raw German DB string
- Align server VALID_CURRENCIES with frontend: add missing AED, BRL, INR, SAR
Co-Authored-By: baragoon <baragoon@users.noreply.github.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two causes of ~1s navigation delay fixed:
1. glass.css Section 19 was extending the page-in animation from 0.20s
to 0.30s using spring easing. Reverted to 0.20s in / 0.12s out.
2. During transitions, dozens of backdrop-filter layers (widgets, cards,
inputs, toolbars) were composited simultaneously for both the outgoing
and incoming page, overloading mid-range Android GPUs.
Added html.navigating class: router.js sets it at transition start,
glass.css overrides all app-content backdrop-filters to none while
active, animationend removes it once the in-animation completes.
will-change: transform on a position:relative flex child causes iOS WebKit
to composite the element at an incorrect position - creating a visible gap
below the nav bar. Remove it; CSS transform transitions use hardware
acceleration automatically on modern iOS without this hint.
Add -webkit-fill-available before 100dvh on .app-shell as a fallback for
iOS WebKit versions where 100dvh is computed slightly smaller than the
actual WKWebView height.
Switch .nav-bottom from position: fixed to a flex child of .app-shell.
With position: fixed and will-change: transform (used for the hide/show
animation), iOS compositor layers can misplace the element. As a flex
child (position: relative; flex-shrink: 0) at the end of a height: 100dvh
container the nav is guaranteed to sit flush at the physical screen bottom.
Remove padding-bottom clearance from .app-content, .tasks-page and
.dashboard - no longer needed since the nav no longer overlaps the
content area.
iOS Safari in PWA standalone mode unreliably handles cookies, causing
CSRF token desync between client and server after app resume. Previous
fixes (response body token in /auth/me and /auth/login) still left a
window where the token could go stale.
Now the server sends X-CSRF-Token response header on every API response
(via csrfMiddleware), including 403 error responses. The client reads
this header from every response, enabling instant self-healing: a 403
extracts the correct token from the error response itself and retries
without needing an extra /auth/me round-trip.
SW cache bumped to v33 to ensure existing iOS PWA installs pick up the
new client code.
When adding ingredients in the meal editor, each ingredient now has a
category dropdown. Categories are stored on the ingredient and applied
automatically when transferring to the shopping list, so items appear
pre-grouped by category without manual re-sorting.
iOS reserves the home indicator area outside the CSS viewport, leaving
a black strip below the bottom navigation. A fixed body::after pseudo-
element now fills this zone with the surface color. Also added explicit
background-color on body for consistent rendering.
iOS Safari (especially PWA/standalone mode) unreliably exposes cookies
via document.cookie, causing CSRF token mismatch on state-changing
requests. The CSRF token is now included in /auth/login and /auth/me
response bodies and stored in-memory on the client. Cookie remains as
fallback. Retry mechanism also improved to read token from response
body and handle expired sessions.
Add QEMU and multi-platform build (linux/amd64 + linux/arm64) to
GitHub Actions workflow, enabling self-hosting on Raspberry Pi and
other ARM64 devices.
- FAB focus ring: double-ring pattern replacing invisible #fff outline
- forced-colors media query for Windows High Contrast Mode
- New tokens: --color-accent-secondary, --content-max-width-narrow, --cal-hour-height
- Apple sync logo uses semantic tokens for correct dark mode inversion
- Sidebar logo gradient references token instead of hardcoded #7C5CFC
With the previous default of 'loopback', Express ignored X-Forwarded-Proto
headers from Caddy/nginx when running in Docker (bridge IP, not loopback).
This caused req.secure=false, which made express-session silently drop the
session cookie on login - resulting in a 401 on every subsequent request.
Changing the default to 1 (trust one proxy hop) fixes this for all standard
Docker+reverse-proxy deployments without requiring manual configuration.
Users can now show/hide widgets and reorder them via a settings button
in the greeting header. Configuration is persisted server-side in
sync_config (dashboard_widgets key) and shared across all family members.
- Greeting widget gets a settings icon button opening a customize modal
- Modal lists all widgets (tasks, calendar, shopping, meals, notes,
weather) with toggle switches and up/down reorder buttons
- Reset to default layout available in the modal
- GET /preferences now returns dashboard_widgets; PUT accepts it
- All 10 locales updated with new i18n keys
Dashboard widgets, task cards, note items, meal slots, form inputs,
toolbars, and FAB actions now use semi-transparent glass backgrounds
with backdrop-filter blur. Each surface gets a subtle module accent
color tint via color-mix gradient overlay. App background uses a
radial accent gradient for ambient vibrancy.
New tokens: --glass-bg-card, --glass-bg-input, --glass-bg-toolbar,
--glass-tint-strength with full dark mode and accessibility overrides.