Remove width: max-content and display: inline-block from email content
CSS that caused horizontal scroll and left-side text clipping. Collapse
empty table cells/containers when external images are blocked to prevent
large blank areas in newsletter emails.
Implement Phase 1 of contacts support — the biggest missing feature
on the roadmap. Uses RFC 9553 (JSContact) data model and RFC 9610
(JMAP for Contacts) when the server advertises the capability,
falling back to localStorage for servers without contact support.
- JMAP types for ContactCard, AddressBook, NameComponent, etc.
- JMAP client methods: CRUD, search, address book listing
- Refactored request() to accept dynamic `using` capabilities
- Zustand contact store with dual-mode persistence
- Full contacts page with two-column layout (list + detail/edit)
- Contact form with multi-email/phone, context labels, validation
- Composer autocomplete on To/Cc/Bcc with keyboard nav and ARIA
- Sidebar navigation link to contacts
- Auth integration: fetch on login, clear on logout
- i18n: contacts.* namespace across all 8 languages
Add support for Spanish, Italian, German, Dutch, and Portuguese with complete translations for all features including identity management, newsletter unsubscribe, and accessibility improvements.
Changes:
- Add 5 new locale directories with full translations
- Update IntlProvider to load all 8 language message files
- Redesign language switcher from button group to dropdown for better scalability
- Update routing and request configuration to handle new locales
- Improve Select component with dir="auto" for RTL support
- Reorganize notification keys in translation files for consistency
- Update all documentation to reflect expanded language support
Add Japanese (ja) as the third supported language alongside English and French. Includes 650+ translation keys covering all UI sections: login, sidebar, email operations, settings (all 8 tabs), context menus, keyboard shortcuts, threads, identity management, and error messages.
Uses formal Japanese (敬語 keigo) appropriate for business email communication. All client and server-side i18n infrastructure updated to load Japanese translations.
Emails containing <style> tags were injecting global CSS rules that
affected the entire application UI. For example, button:hover rules
from emails would override all button hover states app-wide.
Changes:
- Block <style> tags in email sanitization config (security fix)
- Keep inline style attributes for element-specific formatting
- Add explicit bg-transparent to banner buttons (defense in depth)
- Add CSS isolation to banner container
- Fix theme dependency in email content memoization
This prevents malicious or poorly-formatted emails from breaking the
UI or being used for phishing via CSS injection attacks.
- Replace hardcoded hex colors (#666, #999) with CSS variables in email preview
- Strengthen blockquote text color from #6b7280 to #4b5563 (5.3:1 → 7.8:1 contrast)
- Strengthen quoted email text color to match blockquote improvement
- Add comprehensive accessibility guidelines to CLAUDE.md
- Document color contrast standards, testing requirements, and best practices
- All changes maintain dark mode compatibility
Achieves WCAG 2.0 Level AA compliance for all text elements.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implement RFC 2369 List-Unsubscribe support with security validation (protocol whitelist, XSS prevention), two-step confirmation UI, and localStorage persistence. Add intelligent color transformation for dark mode to fix unreadable inline email styles using WCAG 2.0 luminance calculation. Complete batch spam undo operations for context menu multi-select. Update documentation with new features and comprehensive test coverage (97 tests).
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
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>
- Add useMemo for expensive tag suggestions computation
- Add useCallback for identity CRUD handlers
- Extract magic numbers to constants (MAX_RECENT_TAGS, MAX_TAG_LENGTH)
- Improve JMAP error messages with context
Performance: Reduces unnecessary re-renders in identity modal
Maintainability: Makes limits configurable and self-documenting
UX: Users get clearer error messages when operations fail
No functional changes. All tests passing.
Enable users to manage multiple sending identities and organize incoming mail with sub-addressing (user+tag@domain.com format). Provides better email organization, privacy through disposable addresses, and flexibility for users with multiple roles or accounts.
Replaces all remaining hardcoded strings with translation keys to ensure full localization support across the application. This allows users to experience the entire interface in their preferred language without any English fallbacks.
Fixes ENVIRONMENT_FALLBACK warning by detecting user's browser timezone
and passing it to NextIntlClientProvider. This ensures users worldwide
see email timestamps in their local timezone (USA, Europe, Asia, etc.)
without manual configuration.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implements /api/health endpoint for Docker/Kubernetes liveness and readiness probes.
- Basic mode: Returns 200 OK or 503 based on memory usage thresholds
- Detailed mode (?detailed=true): Includes memory stats, uptime, and environment info
- HEAD method support for lightweight polling
- Memory-based health monitoring (85% warning, 95% critical)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Refactored i18n implementation to support seamless language switching
without page reload by pre-loading all translations and using
locale-aware navigation helpers.
Changes:
- Created centralized routing config (i18n/routing.ts) with localePrefix: 'never'
- Added type-safe navigation helpers (i18n/navigation.ts)
- Implemented custom IntlProvider with pre-loaded EN/FR translations
- Updated all pages to use new useRouter from i18n/navigation
- Added language switcher to appearance settings
- Removed URL-based locale routing (no more /en/ or /fr/ prefixes)
- Language preference persisted via Zustand localStorage
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add tablet list auto-hide: email list collapses when selecting an email
on tablet (768-1024px), with back button to return to two-pane view
- Make sender info scrollable on mobile/tablet while keeping subject bar
with action buttons sticky at top for quick access
- Extend overlay sidebar behavior to tablet viewport (768-1024px)
- Add body scroll lock when sidebar is open on mobile/tablet
- Fix backdrop only rendering on mobile/tablet (not desktop)
- Add proper i18n keys for back_to_list, important, close
- Simplify desktop-only CSS classes by removing redundant breakpoint prefixes
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
Allow users to trust specific senders so their images load automatically
without clicking "Show images" each time. Addresses privacy while
improving UX for known senders.
- Add trustedSenders state to settings store with add/remove/check methods
- Show "Always trust this sender" button in email viewer (ask/block modes)
- Add TrustedSendersModal for managing trusted senders in settings
- Include search, manual add, and avatar display in modal
- Full i18n support (en/fr)
Closes#3
- Add getIdentities() method to JMAP client
- Fetch and store identities in auth store on login
- Add From selector in composer (dropdown for multiple identities)
- Pass identity email/ID through send flow instead of using username
- Add translations for From field (en/fr)
This allows proper sender address handling for LDAP and other auth
backends where username differs from email address.
Closes#5
- Fix textarea colors for proper theme support (light/dark mode)
- Fix modal height to prevent composer from collapsing
- Add explicit "Discard" button in footer for better UX
- Fix hardcoded colors in attachments section for theme support
- Use translations for all user-facing text
Closes#6
- Add pagination support to searchEmails (was limited to 50 results)
- Search now scoped to current mailbox/folder
- Support shared mailbox folders in search
- Display total results count (e.g., "50 of 1400 conversations")
- Add clear (X) button to search input
- Re-run search when changing folders during active search
- Remove unused sidebar screenshot
- Add NEXT_PUBLIC_APP_NAME env variable for custom branding
- Update login page to use configurable app name
- Simplify default title in locales to 'Webmail'
- 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
- Implement error boundaries with graceful fallbacks for all major components
(sidebar, email list, email viewer, composer)
- Add push notifications via EventSource for real-time email updates
- Show connection status indicator in sidebar footer
- Play notification sound and show toast for new emails
- Add global error handler and error reporting utilities
- Configure ESLint with modern flat config
- Various fixes: unused variable warnings, proper cleanup on disconnect
- Update translations for error messages (EN/FR)
- showPreview: Conditionally render email preview in list based on setting
- externalContentPolicy: Control external content (ask/block/allow modes)
- deleteAction: Move to trash or permanently delete based on preference
- debugMode: Add conditional logging via new lib/debug.ts utility
Also includes:
- Add moveToTrash method to JMAP client for trash-based deletion
- Update TODO.md with verified implementation status
- Migrate middleware.ts to proxy.ts (Next.js deprecation)
- Fix dark mode folder selection not visible by improving accent color contrast (#1e3a8a)
- Fix inbox not selected by default on login (auto-select primary account inbox)
- Fix email store not cleared on logout (proper state reset between sessions)
- Add settings page with appearance, email behavior, and advanced options
- Integrate mark-as-read delay setting with proper useEffect implementation
- Reset dataLoaded flag on logout to ensure fresh data load on next login
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add overflow-hidden to sidebar container to prevent absolute positioned menu from extending page height
- Improve theme consistency in email viewer security indicators
- Add AI spam analysis display (X-Spam-LLM header support)
- Enhance dark mode support with better color variables
- Update CLAUDE.md with commit/push guidelines
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fix pagination to work with Stalwart JMAP server which doesn't return
the total count. Instead of relying on total:
- If we get exactly limit emails (50), assume there might be more
- If we get fewer than limit, we've reached the end
Also fixed observer target rendering - it now always renders instead of
being conditional, ensuring the intersection observer can work properly.
Add pagination support to load emails progressively as user scrolls:
- Update JMAP client getEmails() to accept position parameter
- Return pagination metadata (hasMore, total) from getEmails()
- Add loadMoreEmails() method to email store for loading next page
- Implement intersection observer in EmailList for automatic loading
- Display loading indicator while fetching more emails
- Show "no more emails" message at end of list
Users can now seamlessly scroll through all emails, loading 50 at a time.
Implemented full support for JMAP shared folders (delegated mailboxes):
Core Features:
- Multi-account mailbox fetching from all available JMAP accounts
- Virtual "Shared Folders" parent with account grouping in sidebar
- ID namespacing to prevent collisions across accounts (e.g., "b0:a" for shared Inbox)
- Account-aware email operations (fetch, view, mark as read)
Technical Implementation:
- Store all accounts from JMAP session (primary + shared)
- Transform mailbox IDs: primary uses original, shared use "accountId:mailboxId" format
- Store originalId for JMAP queries while using namespaced IDs in UI
- Transform email mailboxIds to match namespaced mailbox IDs for proper counter updates
- Pass accountId parameter through entire operation chain (fetch, view, mark read)
UI/UX Enhancements:
- Blue Users/User icons for shared folders in sidebar
- Hierarchical display with account grouping
- Proper unread counters that update when marking emails read
- Auto-mark emails as read when opened in shared folders
Bug Fixes:
- Fixed email content fetching showing wrong message in shared folders
- Fixed unread counters not updating for shared mailboxes
- Fixed mark-as-read not working on server for shared accounts
- Cleaned up debug logging
Added dynamic page titles that update based on current context:
- Mailbox view: "Inbox (3) - Webmail" (with unread count)
- Reading email: "[Subject] - Webmail"
- Composing: "New Message - Webmail" (or Reply/Forward)
- Login page: "Login - Webmail"
Improved UX with at-a-glance information in browser tabs and better
navigation context. Supports i18n for all title components.
When sending emails, they were not being marked with the $seen keyword,
causing them to appear as unread in the Sent folder. Added $seen: true
to keywords for both draft-based sends and new email sends.