Replace the default Next.js favicon.ico with an SVG mail icon from
Lucide and add useFaviconBadge, a canvas-based hook that draws a red
unread-count badge on the tab favicon. Shows "+" when count > 9. On
Safari 15 and below (no Canvas roundRect), falls back to fillRect.
Also fixes a real JMAP push bug in the email store: Mailbox state
changes on push weren't always paired with Email changes, so unread
counts in the sidebar could go stale when a new message arrived. Now
Email changes refresh mailbox counts too, and the Mailbox branch
de-dups when both fire in the same push.
Credits #63 (@jabiinfante).
Remove ~60 console.error/log/warn calls from application code that were
leaking implementation details to browser devtools. Errors are already
surfaced to users via toast notifications or state updates.
Add missing rationale to all 9 bare eslint-disable comments so future
readers understand why deps are intentionally excluded.
P0 Core:
- Sandboxed iframe rendering for rich HTML emails (CSS isolation)
- API retry with exponential backoff for transient JMAP failures
- Mobile bottom action bar with touch-friendly email actions
- Long-press context menu + tap-to-expand submenus on touch devices
P1 Polish:
- Tag counts sidebar section with JMAP batch queries
- Empty folder option for Junk/Trash with batch delete
- Extra-compact density option (28px rows, 44px on touch)
- SPF/DKIM/DMARC security tooltips with plain-language explanations
- Resizable sidebars with drag, touch, and keyboard support
- Expandable sender info panel in email viewer
All 8 locales updated (en/fr/ja/es/it/de/nl/pt).
New OAUTH_ONLY env var hides username/password fields and promotes the
SSO button as the primary login method. Useful for deployments that
want to enforce OAuth-only authentication.
Includes retry button when OAuth discovery fails to avoid dead-end
login pages.
Closes#32
The context menu's "Move to folder" submenu was practically unusable:
scroll events inside the folder list closed the entire menu, and moving
the mouse from the trigger to the submenu dismissed it instantly.
Additionally, the JMAP moveEmail call silently discarded server errors
and always used the primary account ID, causing moves to appear to
succeed locally while the server rejected them.
Closesroot-fr/jmap-webmail#19
Basic Auth sessions now persist across page refreshes via an encrypted
httpOnly cookie (AES-256-GCM). The feature is opt-in: admin sets
SESSION_SECRET env var, user checks "Remember me" at login.
Rework the 2FA input from a checkbox+animated panel to a discreet
"I have a 2FA code" text link that reveals the TOTP field on click.
Support external IdPs (Keycloak, Authentik) that handle auth separately
from the JMAP server via OAUTH_ISSUER_URL. Also surfaces discovery
failures with logged errors and a visible warning banner instead of
silently hiding the SSO button.
Add OAuth2 authorization code flow with PKCE as an alternative to
basic auth. Supports Stalwart's built-in OAuth provider with automatic
token refresh, session persistence via httpOnly cookies, and graceful
fallback to basic auth when OAuth is not configured.
Address GitHub issue #13 (events not spanning real duration, hour label alignment)
and add five standard calendar UX features:
- Click-drag on empty time slots to create events with pre-filled time range
- Resize events by dragging bottom edge handle (15-min snap, optimistic JMAP update)
- Recurring event edit/delete scope dialog (this/following/all occurrences)
- Double-click quick create with inline title input (PT1H default)
- Event duplication button in modal (clones +1 day, opens for editing)
Shared interaction logic extracted to hooks/use-time-grid-interactions.ts.
All features include i18n (8 locales), accessibility (ARIA, pointer events),
and proper error handling with toast feedback.
- Add ConfirmDialog component with promise-based useConfirmDialog hook
- Add WelcomeBanner onboarding component with localStorage persistence
- Add NavigationRail (desktop icon rail + mobile bottom tab bar)
- Polish login form (shake on error, TOTP slide, password visibility, session expired banner)
- Add inline form validation with shake animation in email composer and contacts
- Add empty state patterns for contacts (no data vs no search results)
- Improve toast system with undo action support and typed durations
- Add template settings tab to settings page
- Refactor sidebar (cleaner code, remove unused imports)
- Add WCAG AA reduced-motion, safe area insets, sr-only live region
- Add shake/slide animations in globals.css
- Update i18n for all 8 locales
Evaluate JMAP CalendarEventAlert triggers client-side and display toast
notifications when alert times are reached. Extracts notification sound
into shared utility and mounts ToastContainer globally (fixes silent
toast failures across all components).
- Pure alert utilities: offset parsing, fire time computation (start/end),
effective alerts resolution with useDefaultAlerts, pending alert filtering
- Zustand persist store for acknowledged alert deduplication (24h retention)
- Global hook with 60s interval check, proactive 24h event fetch via ref
(isolated from calendar store), stale closure protection
- Settings: calendarNotificationsEnabled, calendarNotificationSound toggles
- Toast icon customization, CalendarAlertProvider in layout
- i18n: all 8 locales (EN/FR/JA/ES/IT/DE/NL/PT)
- 44 tests (36 alert utils + 8 notification store), 558 total passing
Implement comprehensive spam management allowing users to report spam emails and restore false positives. Emails are moved to/from the Junk folder with toast notifications and undo support.
Key features:
- Mark as spam: moves email to Junk folder with 5-second undo
- Mark as not spam: restores email from Junk to inbox
- Batch spam operations via context menu
- Smart toggle based on current folder (Junk vs other folders)
- Keyboard shortcut (Shift+!) for quick spam reporting
- Full i18n support (EN/FR)
- Shared mailbox compatibility
UX improvements:
- Toast notifications with undo action using sonner library
- Auto-deselect email after spam action
- Visual distinction with red destructive styling
- Desktop button + context menu + keyboard shortcuts
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Complete internationalization coverage for all identity-related UI elements,
including mobile navigation, batch actions, and recipient formatting. Improve
sub-address badge to detect and display tags for emails received at
user+tag@domain.com addresses, not just sent emails. Add contextual recipient
display showing "me" for current user and "Name and X others" for multiple
recipients. Update documentation to reflect completed identity management
implementation.
- Add reusable focus trap hook (use-focus-trap.ts)
- Traps Tab/Shift+Tab within modal
- Restores focus on close
- Handles disabled elements gracefully
- Enhance identity modal with WCAG 2.1 compliance
- Add role="dialog" and aria-modal="true"
- Add aria-labelledby linking to modal title
- Implement focus trap on open
- Add ARIA live regions to identity form
- Error messages announced to screen readers
- aria-describedby links inputs to errors
- aria-invalid state on validation failure
Accessibility: Improves keyboard navigation and screen reader UX
Standards: WCAG 2.1 AA compliant for modal interactions
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Enable post-build configuration by reading env vars at request time
instead of build time. This allows Docker containers to be configured
without rebuilding the image.
- Add /api/config endpoint for runtime config
- Add useConfig hook with caching
- Update login page with proper loading/error states
- Add i18n translations for config errors
- Maintain backwards compatibility with NEXT_PUBLIC_* vars
- Implement thread grouping by threadId in email list
- Add ThreadListItem with collapsed/expanded states for desktop
- Create ThreadConversationView for full-screen mobile experience
- Add thread utility functions for grouping and sorting
- Extend JMAP client with getThread() and getThreadEmails() methods
- Add keyboard shortcut 'x' to expand/collapse threads
- Add thread expansion state management in email store
- Fix React hooks order in login page
- Add .env.example for environment configuration
- Add husky for git hooks
New features:
- Keyboard shortcuts (j/k navigation, r/R/f for reply/forward, s star, e archive, # delete)
- Drag-and-drop emails to mailbox folders with multi-select support
- Right-click context menus on email list items with all actions
- Mobile-responsive adaptive layout with hamburger menu navigation
New files:
- hooks/use-keyboard-shortcuts.ts - Global keyboard shortcut handling
- hooks/use-email-drag.ts, hooks/use-mailbox-drop.ts - Native HTML5 DnD API
- hooks/use-context-menu.ts, components/ui/context-menu.tsx - Context menu system
- hooks/use-media-query.ts, stores/ui-store.ts - Responsive breakpoint handling
- components/layout/mobile-header.tsx - Mobile navigation header
- contexts/drag-drop-context.tsx - Drag-drop state management
- components/keyboard-shortcuts-modal.tsx - Help modal (? key)
- components/email/email-context-menu.tsx - Email-specific context menu
Updated TODO.md with completed tasks and code audit summary