Commit Graph
98 Commits
Author SHA1 Message Date
Prastowo aGung Widodo 3c1a4496f9 fix(print): restrict print area to the email viewer
The print output was capturing the entire window, so portrait pages
only showed the sidebar and message list. Scope the print rendering
with a @media print block that hides everything, then re-reveals the
#email-viewer-container subtree and positions it full-page. Add
print:hidden to the More Actions dropdown and the Quick Reply section
so they don't appear in the printed output.

Credits #55 (@prastowoagungwidodo).
2026-04-16 23:03:25 +02:00
Prastowo aGung Widodo e12bf45e84 fix(contacts): stop action buttons from wrapping on empty state
Apply whitespace-nowrap + overflow-hidden + text-ellipsis to the "New
Contact" / "Import vCard" buttons on the empty state, and add flex-wrap
+ min-w-0 on the parent so longer future translations wrap to a new
row instead of clipping behind the ellipsis. Icons get shrink-0 so
they stay rendered.

Credits #56 (@prastowoagungwidodo).
2026-04-16 23:02:23 +02:00
Matthieu MALVACHE d33309a8b6 fix(security): escape quotes in plain-text linkifier, bump vulnerable deps
Plain-text email bodies were escaped for <, >, & but not " or ', and the
URL linkifier regex captured every non-whitespace character up to the
next <. A URL containing a double or single quote broke out of the
href attribute in the rendered anchor, allowing arbitrary event handlers
to be injected into otherwise plain-text mail. Reported by @rathlinus.

Extract a shared plainTextToSafeHtml helper in lib/email-sanitization.ts
that escapes all five HTML-significant characters in the correct order
before linkification, and route both email-viewer and thread view
through it. Add tests that parse the output and assert no onmouseover
attribute lands on the anchor element.

Also bump dependencies flagged by npm audit: next 16.2.4 (DoS in Server
Components), next-intl 4.9.1 (open redirect), dompurify 3.4.0
(FORBID_TAGS bypass); picomatch/vite/brace-expansion resolve
transitively. npm audit is clean.

Bump version to 1.4.1.
2026-04-16 22:47:15 +02:00
Matthieu MALVACHE 301386c709 chore: fix stale tests, rename spam label, update deps and fix flatted CVE
- Fix oauth-discovery tests expecting removed console.error
- Rename "AI Analysis" to "Spam Analysis" in email viewer
- Update next 16.2.1, tailwind 4.2.2, zustand 5.0.12, eslint 9.39.4,
  typescript-eslint 8.57.1, tanstack/react-virtual 3.13.23
- Fix flatted prototype pollution (GHSA-rf6f-7fwh-wjgh)
2026-03-23 15:20:53 +01:00
Matthieu MALVACHE d3ef46f760 fix: add copy-to-clipboard feedback and neutralize dark mode background tint
Copy source button now shows a checkmark and "Copied" text for 2s
after clicking. Dark mode email background blend target changed to
perfectly neutral gray (R=G=B) to eliminate perceived color cast on
certain displays.
2026-03-23 15:14:14 +01:00
Matthieu MALVACHE 3ba5cf16c3 fix(sidebar): fix context menu viewport clipping and delete dialog overflow (#44)
Render context menu and delete confirmation dialog via portal to escape
sidebar overflow-hidden. Clamp context menu position to viewport bounds.
2026-03-23 14:39:20 +01:00
Matthieu MALVACHE be6907f863 feat(sidebar): add folder drag-and-drop reparenting (#44) 2026-03-23 14:29:16 +01:00
Matthieu MALVACHE 01cb0f173f feat(sidebar): add folder management UI with context menu, inline editing, and move-to (#44) 2026-03-23 14:26:22 +01:00
Matthieu MALVACHE cb862994e3 fix: render inline CID images in email body instead of showing as attachments
Emails with embedded images (cid: references) displayed them as
downloadable attachments because the browser cannot resolve cid: URLs
and the attachments were not filtered. Added fetchBlobAsObjectUrl to
the JMAP client, pre-fetch inline images as object URLs, replace cid:
references in sanitized HTML, and filter CID attachments from the
download list in both the email viewer and thread conversation view.
2026-03-23 14:11:25 +01:00
Matthieu MALVACHE 146921d5f0 fix: eliminate email list flicker during loading and after-action refreshes
Shared isLoading flag caused the list to dim on unrelated operations
(send, batch actions, mailbox refresh). After-action fetches also
triggered a full loading overlay unnecessarily. Now operations that
don't fetch emails no longer set isLoading, after-action refreshes
use silent refreshCurrentMailbox, and the loading overlay only appears
after a 300ms delay to skip fast fetches.
2026-03-23 14:11:09 +01:00
Matthieu MALVACHE f646badc55 fix: remove production console statements and explain eslint-disable comments
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.
2026-03-23 13:25:50 +01:00
Matthieu MALVACHE b685e37c4e fix: resolve lint errors in email store and move-to-popover (#43) 2026-03-23 13:07:24 +01:00
Matthieu MALVACHE ae92bd2cbc feat: complete multi-selection store methods, toolbar integration, and shift-click support (#43) 2026-03-23 13:02:58 +01:00
Matthieu MALVACHE 837ef722b6 feat: add MoveToPopover component with search and keyboard navigation (#43) 2026-03-23 12:55:43 +01:00
Matthieu MALVACHE 3835efb3fa feat(store): add range selection, filter selection, and lastSelectedIndex tracking (#43) 2026-03-23 12:54:23 +01:00
Rens Reinders 0153f66340 feat: double-click to create event from month view with smart time suggestion (#37)
* feat: open event modal on month day cell click

* feat: suggest first available hour when creating from month view

* chore: document month view slot suggestion helpers

* fix(calendar): use double-click to create event in month view

* fix(calendar): use 9am fallback for non-today month slots
2026-03-18 21:13:52 +01:00
Rens Reinders 167014871d fix: participant/invitation handling for Stalwart JMAP and deduplicate self-attendees (#36)
* fix: align calendar event payload with stalwart scheduling

* fix: dedupe calendar participants by normalized address

* chore: document calendar uid generation

* chore: clarify organizer participant guard
2026-03-18 21:13:52 +01:00
Rens Reinders 53ecfe5f69 fix: replace inline calendar delete confirms with centered modal dialog (#34)
* fix: more margin on the avatar to align with subject

* fix: fixed multi day events multiple items gap spacing

* fix: align sticky week headers with calendar grid

* fix: nice confirm modal

* fix: added locales

* fix: polish confirm dialog behavior and participant delete messaging

* fix: apply themed destructive colors without button shadow

* fix: restore destructive button theme styling
2026-03-18 11:09:35 +01:00
Rens Reinders ae0412d3db feat: add week numbers column to month view grid (#38) 2026-03-18 11:09:35 +01:00
Rens Reinders f6492bbeda feat: simplify contact bulk selection actions menu (#39)
* feat(contacts): simplify bulk selection actions menu

* refactor(contacts): reuse shared context menu for bulk actions
2026-03-18 11:09:35 +01:00
Rens Reinders 3c0f3400b4 fix: align sticky week headers with calendar grid and fix scroll offset (#33)
* fix: more margin on the avatar to align with subject

* fix: fixed multi day events multiple items gap spacing

* fix: align sticky week headers with calendar grid

* fix: account for sticky header in week view initial scroll
2026-03-18 11:09:35 +01:00
Matthieu MALVACHE 961b3edc3a fix: consistent tablet navigation across all pages and nav bar polish
Show bottom nav on tablet breakpoint (768-1023px) for calendar and
contacts pages, matching the mail page. Add z-index to prevent content
bleeding through. Remove redundant active indicator bar since color
already signals active state.
2026-03-16 22:18:59 +01:00
Matthieu MALVACHE 77c1133f06 fix: improve mobile/tablet layout transitions and touch interactions
- Remove isMobile/isTablet JS state from layout classes to eliminate
  orientation-change blink (CSS breakpoints apply instantly)
- Extract dismissViewer() helper for return-to-list pattern (was duplicated 4x)
- Convert more-actions dropdown from hover-only to click-toggle (works on touch)
- Add Escape key and role attributes to more-actions menu
- Reset showMoreActions on email change to prevent stale menu
- Move overflow-x:auto to iframe inner body stylesheet (was inert on iframe element)
- Reset tabletListVisible when crossing to desktop breakpoint
2026-03-16 16:14:40 +01:00
Matthieu MALVACHE 27911dfe76 feat: add UX & robustness improvements (iframe rendering, retry, mobile, polish)
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).
2026-03-16 15:40:24 +01:00
Matthieu MALVACHE 2de80badc1 fix: show mobile nav in viewer mode and add folder hierarchy to move dialog
Mobile bottom navigation bar was hidden when viewing an email, trapping
users in the mail section. Now always visible on mobile.

Move-to-folder submenu now shows hierarchical folder structure using
buildMailboxTree/flattenMailboxTree instead of a flat list.

Closes #30, closes #29
2026-03-16 12:58:04 +01:00
Matthieu MALVACHE 4aaef1ebf1 fix: resolve calendar crash on undefined duration and sieve filter save error
parseDuration() now handles undefined event.duration gracefully instead
of crashing on .match(). CalendarEvent.duration type updated to reflect
that JMAP servers may omit this field.

Sieve filter activation replaced isActive (server-set per RFC 9661) with
onSuccessActivateScript. Create/update calls merged with activation into
single JMAP roundtrips.

Closes #31, closes #21
2026-03-16 12:41:10 +01:00
Matthieu MALVACHE c787acc34f fix: resolve context menu submenu usability and move-to-folder failures
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.

Closes root-fr/jmap-webmail#19
2026-03-02 09:08:36 +01:00
Matthieu MALVACHE 8554b30e5b fix: keep show/hide details toggle in place when expanded
Move the toggle button above the expandable details section so it stays
at the same position regardless of expand/collapse state. Also replace
hardcoded English text with i18n translations.

Closes #18
2026-02-28 20:49:57 +01:00
Matthieu MALVACHE 602d50b703 fix(email): include identity display name in From header when sending
Sent emails only included the bare email address in the From field,
so recipients saw <user@example.com> with no display name. Now the
identity name from the JMAP server is included in the From header
for composed emails, quick replies, and saved drafts.

Also sorts identities so the one matching the login username is
selected by default in the composer dropdown.
2026-02-25 12:35:00 +01:00
Matthieu MALVACHE 66ff74a292 fix(calendar): inline isEdit checks to satisfy exhaustive-deps rule
Replace isEdit references with direct event checks inside the useCallback,
since isEdit is derived from event which is already in the dependency array.
2026-02-22 18:06:25 +01:00
Matthieu MALVACHE 312f336407 fix(calendar): add missing timeString dependency to useCallback 2026-02-22 18:04:04 +01:00
Matthieu MALVACHE 7e42d2dedf fix(calendar): produce correct JMAP patch when removing recurrence rules
When editing a recurring event and clearing its recurrence, the update
patch was omitting recurrenceRules entirely instead of setting it to null.
The server never received the change. Also clear recurrenceOverrides and
excludedRecurrenceRules so orphaned exceptions don't persist. Same fix
applied to locations and alerts removal.

Fix "this_and_future" scope overriding recurrence removal by reordering
the spread so user updates take precedence over master's original rules.

Closes root-fr/jmap-webmail#14
2026-02-22 17:50:53 +01:00
Matthieu MALVACHE 82a27a1f52 feat(calendar): add drag-create, resize, recurring scope, quick-create, and duplication
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.
2026-02-22 17:31:16 +01:00
Matthieu MALVACHE 40c7fa3649 feat: add UI/UX polish, templates settings, confirm dialog, welcome banner, and navigation rail
- 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
2026-02-17 02:30:09 +01:00
Matthieu MALVACHE 7da9fa61e2 feat(templates): add email templates with placeholder variables and composer integration
Local-storage templates with dynamic {{variable}} placeholders that auto-fill
from composer context (recipient, sender, date/time). Includes template manager,
category filtering, quick picker in composer toolbar, and settings tab.
48 tests covering placeholder extraction, filling, and variable suggestions.
2026-02-17 01:34:13 +01:00
Matthieu MALVACHE 5d2f5dcf7e feat(calendar): add participant scheduling with iTIP invitations and inline calendar invitation banner
Add organizer/attendee UI with RSVP buttons (accept/maybe/decline),
contact autocomplete in participant input, scheduling message support
via sendSchedulingMessages flag, and DnD notification for participant
events. Add inline calendar invitation banner in email viewer that
auto-detects .ics attachments, parses event details, and allows RSVP
or import to calendar. Includes cancellation display and 51 tests.
2026-02-17 01:09:03 +01:00
Matthieu MALVACHE 613eb6ce84 fix(email): improve dark mode readability and fix long subject overflow
Dark mode color transform had 3 bugs preventing it from working:
- Hook only ran when external content was blocked, not for all emails
- Checked `theme` ('system') instead of `resolvedTheme` ('dark')
- Only transformed CSS style attrs, missing <font color> and bgcolor

Replaced invert+boost algorithm with blend-toward-white for text
(preserves hue, brighter results) and added background darkening
for light email backgrounds. Also handles color/bgcolor HTML attrs.

Fixed long email subjects pushing action buttons off-screen by adding
min-w-0 to the email viewer flex container in the page layout.
2026-02-17 00:38:46 +01:00
Matthieu MALVACHE be78603c48 fix(filters): show hierarchical folder structure in rule mailbox selector
Reuse buildMailboxTree/flattenMailboxTree to display nested folders
with indentation instead of a flat list. Exclude shared folders since
Sieve rules only operate on the user's own mailboxes.
2026-02-17 00:06:52 +01:00
Matthieu MALVACHE a603ee496b feat(calendar): add event notifications with client-side alert evaluation
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
2026-02-16 23:46:12 +01:00
Matthieu MALVACHE 9be02e8dd7 feat(filters): add email filters with Sieve rules, review fixes and hardening
Implement JMAP Sieve Scripts (RFC 9661) with visual rule builder and raw
Sieve editor. Includes post-review fixes: parser validation guards,
generator empty-rule skipping, JMAP client error hardening, focus trap
accessibility, mailbox name fix, toast validation feedback, auto-save with
rollback, and "Reset to visual builder" for opaque scripts. 514 tests pass.
2026-02-16 23:07:06 +01:00
Matthieu MALVACHE b84e543e2e feat(calendar): add drag-and-drop rescheduling and iCalendar import
Calendar phase 2 features with review-driven fixes:
- Drag events in week/day views to reschedule (15-min snap intervals)
- Drag events in month view to change date (preserves time)
- Visual snap indicators with time labels during drag
- iCalendar (.ics) import via JMAP CalendarEvent/parse
- Import modal with file upload, event preview, calendar selector
- File validation (5MB max), bulk import with progress
- i18n: calendar.import.* and event_move_error keys (8 locales)
2026-02-16 21:07:44 +01:00
Matthieu MALVACHE a27196bdeb feat(calendar): add JMAP Calendar integration with full review fixes
Calendar phase 1 with comprehensive review-driven hardening:
- JMAP CalendarEvent CRUD (RFC 8984 types, capability detection)
- Month/week/day/agenda views with multi-day event spanning
- Column-based overlap layout for concurrent events
- Event modal with create/edit/delete, recurrence, reminders
- Mini-calendar sidebar with calendar visibility toggles
- Settings: firstDayOfWeek and timeFormat wired to all views
- Locale-aware date formatting via next-intl useFormatter (8 locales)
- Push notification handling for Calendar/CalendarEvent state changes
- ARIA grid roles, event card labels, focus trap, 44px touch targets
- Input validation, color sanitization, timezone auto-detection
- Error handling with toast feedback, capability page guard
- ICU pluralization for alert translations, debug logger integration
2026-02-16 20:32:06 +01:00
Matthieu MALVACHE 83e01ae551 feat: add contacts phase 2, advanced search, vacation responder, Docker & TOTP 2FA
Contacts:
- Contact groups/lists with JMAP members map and composer expansion
- vCard import/export with RFC 6350 parser and duplicate detection
- Bulk operations (multi-select, delete, group add, export)

Search:
- Advanced search panel with JMAP filter fields
- Search chips for active filters visualization
- Debounced inputs with AbortController deduplication

Vacation:
- JMAP VacationResponse singleton management
- Settings tab with date range and message configuration
- Sidebar indicator when vacation responder is active

Auth:
- TOTP 2FA support with Stalwart-compatible password$totp format

Infrastructure:
- Docker multi-stage build with standalone Next.js output
- Structured server-side logger with text/JSON format
- CSP Report-Only and security headers via proxy middleware
- Layout refactoring (HTML structure in root layout)
- Playwright E2E framework setup

Testing: 450+ tests (identity, contacts, vCard, threads, headers, components)
i18n: All new strings added to all 8 locales
2026-02-16 18:46:33 +01:00
Matthieu MALVACHE dddbcf75db fix(email): resolve layout overflow and blocked image empty spaces
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.
2026-02-16 17:18:38 +01:00
Matthieu MALVACHE 17abe7ff56 feat(contacts): add address book with JMAP sync and local fallback
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
2026-02-16 17:07:45 +01:00
Matthieu MALVACHE 3fc8ccf116 feat(i18n): expand language support from 3 to 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
2026-01-08 22:12:13 +01:00
Matthieu MALVACHE cfcf0083f6 feat(i18n): add Japanese language support with complete translations
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.
2026-01-08 21:40:13 +01:00
Matthieu MALVACHE e600cd387d fix(security): prevent global CSS injection from email style tags
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.
2026-01-08 19:18:59 +01:00
Matthieu MALVACHEandClaude Sonnet 4.5 1bca54e5d6 feat(accessibility): improve WCAG AA color contrast compliance
- 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>
2026-01-08 18:42:53 +01:00
Matthieu MALVACHEandClaude Sonnet 4.5 395f92c47f feat(email): add newsletter unsubscribe and enhance dark mode
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>
2026-01-08 18:39:27 +01:00