106 Commits

Author SHA1 Message Date
c4374b42ea default to Inbox perspective, show GTD item detail, reduce toolbar
- default to Inbox perspective (not INBOX mailbox) after first sync
- selectItemForDetail bridges GTD item selection to detail pane by
  creating a synthetic ThreadSummary for email items
- reduce GTD toolbar from 5 to 3 buttons (defer, complete, discard),
  someday/project remain accessible via keyboard shortcuts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 12:56:19 +01:00
3c21a4c461 fix empty detail view on thread select, reduce toolbar overflow
- add .id(selectedThread?.id) to ThreadDetailView to force SwiftUI
  NavigationSplitView detail re-evaluation on selection change
- selectMailbox clears selectedThread/messages to avoid stale state
- remove defer/project buttons from folder toolbar (4 buttons instead of 7),
  keyboard shortcuts still work

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 12:50:19 +01:00
367359abe6 filter thread list by selected mailbox, fix sidebar selection
- threadSummariesFromDB accepts optional mailboxId filter, uses EXISTS subquery
  to show only threads containing messages in the selected mailbox
- add selectMailbox() and selectPerspective() to MailViewModel for clean
  state transitions between folder view and GTD perspective view
- sidebar highlights selected mailbox/perspective, clears state on switch
- default to INBOX mailbox after first sync

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 12:44:41 +01:00
6bd1163c6c mark messages as read when selecting a thread
Updates local flags and enqueues \\Seen flag action for IMAP sync.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 12:36:57 +01:00
fc90d71021 decode RFC 2047 in To/CC names, convert dates to ISO 8601 for correct sorting
- decode RFC 2047 encoded words in To/CC address display names
- convert IMAP RFC 2822 dates to ISO 8601 before storing in MessageRecord,
  so SQLite ORDER BY text comparison sorts chronologically
- strip parenthesized timezone names from dates (e.g. "+0100 (CET)")

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 12:34:10 +01:00
a71f609f3b decode RFC 2047 subjects/sender names, parse RFC 2822 dates from IMAP envelopes
- decode RFC 2047 encoded words in subject and fromName via RFC2047Decoder
  before storing in MessageRecord (fixes =?utf-8?Q?...?= raw display)
- add RFC 2822 date formatters to Queries.parseDate (EEE, dd MMM yyyy HH:mm:ss Z)
  so IMAP envelope dates parse correctly instead of falling back to distantPast
- parseDate now tries ISO 8601, RFC 2822 with day-of-week, RFC 2822 without

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 12:30:34 +01:00
0c31bdc152 fix insertMessages: use COUNT(*) instead of MessageRecord.fetchOne for existence check
MessageRecord.fetchOne decodes all columns but the query only selects id,
causing "column not found: accountId". Use COUNT(*) which returns a plain Int.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 12:24:32 +01:00
0433ed1f7e fix double sync race: remove duplicate syncNow calls, add sync guard
- connectAccount() and loadExistingAccount() no longer call syncNow() —
  the .task modifier on mailView handles initial sync + periodic sync
- add isSyncing guard to prevent concurrent syncNow() calls that race
  on the IMAP connection and cause "Previous command interrupted"
- reload mailboxes after each sync (needed on first sync when DB is empty)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 12:22:39 +01:00
df78397870 fix stale IMAP connection: clean up old connection on reconnect, robust disconnect
- connect() now cleans up any stale connection/event loop group before
  creating a new one, preventing leaked resources after failed syncs
- disconnect() uses try? for both channel close and group shutdown so
  "Already closed" errors don't prevent cleanup

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 12:10:53 +01:00
01605a01ec fix FK constraint: skip duplicate messages before thread reconstruction
- insertMessages checks (mailboxId, uid) existence before INSERT, returns
  only actually inserted records
- syncMailbox only runs ThreadReconstructor on newly inserted messages,
  preventing FK violations from stale UUIDs referencing ignored records
- improve error logging to show full error description

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 12:02:55 +01:00
ff91e397e8 fix sync: INSERT OR IGNORE for duplicate messages, fix leaked IMAP continuations
- insertMessages uses INSERT OR IGNORE on (mailboxId, uid) conflict instead of
  crashing on UNIQUE constraint when messages are re-fetched after restart
- IMAPResponseHandler.sendCommand resumes any leaked previous continuation before
  registering a new one, preventing DuplicateCommandTag errors
- add channelInactive handler to resume pending continuations on connection drop
- add error type to sync failure log for better diagnostics

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 11:58:29 +01:00
1e39a2bd43 fix offline: remove deadlocking syncShutdownGracefully from deinit, limit flag sync range
- remove deinit from IMAPConnection, SMTPConnection — syncShutdownGracefully
  blocks and deadlocks if called on NIO event loop thread
- limit reconcileFlags to last 500 UIDs to avoid overwhelming the connection

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 11:44:45 +01:00
d5eaf35665 add attachmentCount to ThreadSummary, paperclip badges in thread and item lists
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 11:38:29 +01:00
016c163b75 add attachment chips to message view with download, Quick Look preview
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 11:36:05 +01:00
5c547f6faa add compose attachment UI: file picker, drag-drop, paste, chip strip, send path
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 11:33:25 +01:00
868d99e60e update SMTPClient.send to use formatMultipart when attachments present
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 11:27:34 +01:00
1b4556b167 add OutgoingAttachment model, attachments field on OutgoingMessage
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 11:26:03 +01:00
1c9e36a970 add attachment UI implementation plan
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 11:24:07 +01:00
a3e3618d34 add attachment UI design spec
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 10:58:07 +01:00
8c33d4d4a6 fix raw MIME body display, re-parse stale cached bodies, fix minor review items
- detect and re-fetch bodies containing unparsed MIME content (boundary markers,
  Content-Transfer-Encoding headers) from pre-MIMEParser code path
- fix MIMEParser section numbering: pass cumulative sectionPrefix in nested multiparts
  instead of resetting to empty string
- generate snippet from parsed body text when envelope snippet is missing
- add pendingAction(id:) direct lookup to MailStore, avoid re-fetching all actions
- add updateSnippet method to MailStore
- fix IMAPIdleClient.selectInbox: use incrementing tag counter instead of hardcoded tag
- use static nonisolated(unsafe) ISO8601DateFormatter in ActionQueue (avoid repeated alloc)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 10:33:08 +01:00
31ab18cb2a fix remaining code review issues, add HTML email rendering, charset-aware MIME decoding
- add charset-aware string decoding in MIMEParser (supports UTF-8, Latin-1, Windows-1252, etc.)
- fix prefetchBodies: remove broken ISO8601 date filter that prevented body fetching
- fix ensureBodyLoaded to use fetchFullMessage + MIMEParser instead of broken fetchBody
- add N+1 query fix: inboxMessagesExcludingDeferred uses SQL LEFT JOIN instead of per-message deferral check
- add inboxMessageCountExcludingDeferred for efficient perspective counts
- add unreadMessageCount, totalMessageCount queries to MailStore
- wire mailbox unread/total counts in loadMailboxes (were hardcoded to 0)
- add flag sync: reconcileFlags fetches flags for existing UIDs, updates local read/flagged state
- move account config from UserDefaults to Application Support file, auto-migrate existing config
- render HTML emails by default (toggle to plain text), render plain text as HTML for proper Unicode/emoji
- replace print() with os_log Logger in SyncCoordinator

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 10:05:19 +01:00
10b7cb2fd2 fix code review issues: deferral date format, storeFlags SELECT, event loop leaks, GTD selection tracking
- fix deferral resurfacing using VTODOParser.formatDateOnly instead of ISO8601 (date format mismatch)
- add SELECT mailbox before UID STORE in storeFlags (IMAP protocol requirement)
- pass credentials to SyncCoordinator so IDLE monitoring activates
- add selectedItem tracking to MailViewModel, wire List selection in GTD views
- fix startPeriodicSync to sleep-first, preventing duplicate sync on launch
- add deinit cleanup for EventLoopGroup in IMAPConnection, SMTPConnection
- use separate IMAP client for attachment downloads, avoid shared connection interference
- remove [weak self] from IMAPIdleClient actor Task to prevent orphaned connections
- fix isGTDPerspective to check selectedMailbox instead of items.isEmpty
- fix fetchBody to use complete RFC822 fetch instead of BODY[TEXT]
- reuse single IMAP connection per ActionQueue.flush() batch
- add requiresIMAP to ActionPayload for connection batching
- load task categories from label store instead of hardcoded empty array
- suppress NIOSSLHandler Sendable warnings via Package.swift unsafeFlags
- fix unused variable warnings across codebase

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 23:13:46 +01:00
3b82e6cd95 fix IMAPIdleHandler thread safety: pass idleTag via init, clean up event loop group on reconnect 2026-03-14 14:20:53 +01:00
55ec465677 bump periodic sync to 15min (IDLE is fast path), add MIMEParser to app targets
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 13:44:41 +01:00
961f87359a add 25 MB per-file attachment size guard to MessageFormatter
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 13:44:09 +01:00
0ba309c525 add attachment download service: fetch section from IMAP, cache to disk
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 13:43:11 +01:00
004d1ad027 remove inline body fetch from fetchEnvelopes, bodies now come from MIME-aware prefetchBodies
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 13:41:33 +01:00
9a42ae9574 add IDLE monitoring to SyncCoordinator: start/stop with capability check
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 13:40:47 +01:00
1cbe09c443 add IMAPIdleClient actor with IDLE loop, reconnect backoff, re-IDLE timer
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 13:38:39 +01:00
3ea88f6402 add IMAPIdleHandler: NIO channel handler for IDLE event streaming
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 13:37:16 +01:00
858cdf5284 replace prefetchBodies with MIME-aware parsing, store attachments during sync
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 13:35:14 +01:00
0b9bbe1255 add multipart/mixed formatting, base64 line-wrapped encoding to MessageFormatter 2026-03-14 13:32:23 +01:00
968dd91f80 add fetchFullMessage, fetchSection to IMAPClient for MIME attachment retrieval 2026-03-14 13:31:38 +01:00
0a564a05fd add attachment CRUD to MailStore, wire hasAttachments in Queries 2026-03-14 13:29:58 +01:00
17d47cfddd add v4_attachment migration: sectionPath on attachment, hasAttachments on message 2026-03-14 13:29:29 +01:00
aac7c7c0af add MIMEParser: multipart parsing, content decoding, boundary generation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 12:09:10 +01:00
cd7d39de9e add RFC 2047 encoded word decoder for MIME filenames
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 12:07:17 +01:00
a3a99e668f add MIME types: MIMEMessage, MIMEPart, MIMEAttachment, TransferEncoding
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 12:06:32 +01:00
8095000a56 add MIMEParser module target to Package.swift
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 12:06:10 +01:00
b866c3bb8e add v0.5 implementation plan: attachments, MIME parsing, IMAP IDLE 2026-03-14 11:54:26 +01:00
f28b44d445 move v0.1 artifacts to DELETE/, fix xcode build, bump calver to 2026.03.14
- move backend/, clients/, scripts/ to DELETE/ (v0.1 era, replaced by on-device arch)
- delete feature/v0.1-backend-and-macos branch
- add TaskStore dependency to project.yml
- fix ComposeViewModel deinit concurrency, make toMessageSummary public
- regenerate Xcode project, verify macOS build succeeds

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 10:40:41 +01:00
8f63b1c7c1 fix task store wiring: correct directory path, pass to sync coordinator, remove unused stub
- TaskStore directory changed from `tasks-<id>` to `<accountId>/tasks/` per spec
- TaskStore now created before SyncCoordinator and passed as `taskStore:` arg so deferral resurfacing works
- Use `mailStore.databaseWriter` instead of raw `dbPool` for consistency
- Remove unused `deferSelectedItem` stub (all callers use `deferItem(_:until:)` directly)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-14 10:31:00 +01:00
bec9b7eb40 add app UI for GTD triage: perspectives, task creation, defer/label pickers, linked tasks
- replace mailbox-only sidebar with 6 GTD perspectives (inbox, today, upcoming, projects, someday, archive)
- add TaskEditViewModel/TaskEditView for creating tasks with optional due date, linked message
- add DeferPicker (tomorrow, next week, pick date, someday) and LabelPicker (searchable, type-to-create)
- add GTD triage actions in MailViewModel: defer, file, discard, complete for unified ItemSummary
- add GTD keyboard shortcuts in ThreadListView: d (defer), ⇧D (someday), p (project), ⌘⏎ (complete), ⌫ (discard)
- show linked tasks inline in ThreadDetailView with status badges, merged timeline

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 10:31:00 +01:00
ac824cd72e add deferral resurfacing to SyncCoordinator (task 7)
Import TaskStore, add optional taskStore property, add resurfaceDeferrals()
that clears expired email deferrals and deferred task deferUntil on each sync.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-14 10:31:00 +01:00
39c0fd40bf add VTODO parser, formatter, TaskStore file I/O with cache rebuild
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 10:31:00 +01:00
a4f0761f25 add v0.4 models, schema, query extensions for GTD tasks, labels, deferrals
New model types: TaskStatus, TaskSummary, LabelInfo, ItemSummary.
GRDB records: TaskRecord, LabelRecord, ItemLabelRecord, DeferralRecord.
Database migrations v3_task, v3_label, v3_deferral with indexes.
MailStore query extensions for labels, item-labels, deferrals, task cache.
TaskStore module wrapping MailStore. Tests for all v3 tables and records.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 10:31:00 +01:00
18e7ff2c47 fix v0.5 spec: address 14 review issues (MIME, IDLE handler, hasAttachments, etc.)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 10:00:57 +01:00
e85e373914 add v0.5 design spec: attachments (send+receive), IMAP IDLE
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 09:57:02 +01:00
1049ca2675 add v0.4 implementation plan: GTD tasks, unified triage, deferrals, labels
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 08:33:21 +01:00
2e502cc08b fix v0.4 spec: address 7 review issues (deferral storage, cache rebuild, key conflict, etc.)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 08:19:19 +01:00