Flat email lists force you to act on one message at a time. cji.email groups messages into threaded conversations and provides a full multi-select engine for bulk moves and deletions — with safeguards against accidental data loss.
How Conversations Are Built from Flat IMAP Messages
IMAP servers return messages as flat lists with no conversation structure. The client reconstructs threads locally using two signals:
1. RFC Message-ID headers (In-Reply-To, References)
The most reliable method. When a reply contains an In-Reply-To header matching a known Message-ID, the messages are linked into the same conversation.
2. Subject normalization If headers are missing or incomplete, subjects are compared after stripping common prefixes:
Re:/RE:Fwd:/FW:Aw:(German reply prefix)
Messages with matching normalized subjects within the same account are grouped into the same conversation. The conversation card displays the original subject (pre-prefix) and the timestamp of the most recent reply.
The Selection System
The selection store tracks a Set<string> of selected message IDs and supports three interaction models:
| Interaction | Behavior |
|---|---|
| Checkbox click | Toggles a single message |
| Cmd/Ctrl + click | Adds to selection without deselecting others |
| Shift + click | Selects all messages between the last selection and this one |
| Header checkbox | Selects all messages visible in the active column |
Range selection works by calculating array indices in the current view, then slicing the ID list between the two clicked positions.
Bulk Actions: Thread-Level Resolution
When a bulk action is triggered on a selection of individual messages, the store resolves the set to their parent thread IDs first. This keeps conversations intact:
- Selecting 3 messages from the same thread and moving them moves the entire thread, not just those 3 messages.
- This prevents threads from being split across multiple folders.
Bulk Move
Moves all selected threads to the target folder or Kanban column. Each thread is processed sequentially with server-side IMAP confirmation.
Bulk Delete
Deletes all selected conversations. A confirmation dialog fires automatically when the selection spans more than 5 conversations — protecting against accidental large-scale deletion from an errant Cmd+A + Delete.
User selects messages → Resolve to thread IDs
→ If deleting > 5 threads: show confirmation dialog
→ Execute batch_delete_conversations (Rust)
→ Update local IndexedDB
→ Sync IMAP deletes in background
Optimistic State During Bulk Operations
Like single-message operations, bulk actions use optimistic UI updates. Selected messages are immediately removed from the view (or relocated) while the Rust backend processes the IMAP commands. If the server rejects any operation, affected messages are restored and a toast notification describes the failure.