- 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
- 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
- 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)
- 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
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.
JMAP spec states "The client may specify a partId OR a blobId, but not both."
We were incorrectly including both fields, causing "Cannot specify both partId
and blobId" errors when creating drafts with attachments.
Fixed by removing partId and size fields from attachment objects. The server
calculates size automatically from the blob content.
JMAP doesn't allow updating immutable properties (from, to, subject) on
existing drafts. Changed createDraft to destroy old draft and create new
one when updating, which allows attachments to be added/modified properly.
Also added comprehensive debug logging to uploadBlob and createDraft to
help troubleshoot upload issues.
Cleaned up console output by removing all debug console.log statements
throughout the codebase:
- Removed 31 debug logs from lib/jmap/client.ts (connection, requests, etc.)
- Removed 4 debug logs from stores/email-store.ts (quota fetch, markAsRead)
Kept all console.error() statements for proper error handling.
The console is now clean with only relevant error messages displayed.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Email Sending:
- Fix identity lookup for email submission (now properly fetches identities from server)
- Add Sent mailbox requirement for sending emails
- Add proper error handling and validation for email submission
Quick Reply Form:
- Replace dummy input with fully functional quick reply system
- Add state management for reply text with auto-expand on focus
- Implement direct send functionality without modal
- Add loading states and error handling
- Show character counter and action buttons (Cancel, More options, Send)
- Use real user avatar and email from auth store
- Auto-refresh email list after sending
Draft Auto-Save Integration:
- Fix critical issue where auto-saved drafts were ignored when sending
- Pass draftId through entire send chain (composer → store → client)
- Wait for pending auto-save before sending to prevent race conditions
- Add draft cleanup with discard confirmation when closing composer
- Delete orphaned drafts when user confirms discard
- Add i18n support for discard confirmation (EN/FR)
Benefits:
- No more orphaned drafts in Drafts folder
- Better performance (updates existing draft vs creating new email)
- Cleaner UX with proper draft lifecycle management
- Quick replies are truly quick (no modal needed)
- All user-facing messages properly translated
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Update auth store to properly detect 'Invalid username or password' error
- Map error to existing 'invalid_credentials' translation key
- Provide better user feedback when login fails
- Added server capability detection to check supported features
- Implemented keep-alive mechanism with periodic ping (every 30s)
- Added automatic session reconnection on connection failure
- Implemented draft auto-save with 2-second debounce
- Added visual indicators for draft save status
- Implemented attachment upload support with progress indication
- Added file attachment UI in email composer
- Updated JMAP client with blob upload functionality
- Enhanced disconnect handling to properly cleanup resources
- Updated TODO.md to reflect completed tasks
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
- Add next-intl for internationalization
- Create English and French translation files
- Implement language switcher component in sidebar
- Configure automatic browser language detection
- Update all components to use translation keys
- Set up locale-aware routing with [locale] directory structure
- Add i18n guidelines to CLAUDE.md for future development
- Store user language preference in localStorage
The app now supports:
- English (en) and French (fr) languages
- Automatic browser language detection on first visit
- User language preference persistence
- Real-time language switching without page reload
- Complete translation coverage for all UI elements
- Set up Next.js 15.5 with TypeScript and Turbopack
- Configured Tailwind CSS v4 with minimalist design system
- Created core email components: list, viewer, composer
- Implemented sidebar navigation with mailbox folders
- Added JMAP client wrapper using jmap-jam library
- Set up Zustand for state management
- Created responsive layout with three-pane view
- Added mock data for development