diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..eef91c3 --- /dev/null +++ b/TODO.md @@ -0,0 +1,49 @@ +# TODO + +Carried over from the platform on 2026-08-18, when email became a plugin. Paths below that read +`sidecar/email/…` are now `sidecar/…` in this repository. + +- [x] **Gmail-style search operators** (done 2026-07-24, `0e5b91e`). `parseEmailQuery` + + `searchEmails` in `sidecar/email/store.ts` parse: `from:`/`to:`/`subject:`/`body:` → FTS5 column + filters; `has:attachment`, `is:unread`/`is:read`, `label:X`, `before:`/`after:YYYY-MM-DD` → SQL + `WHERE` on `emails`; quoted values → exact phrases; free text → prefix-AND full-text; unknown + `op:val` falls back to free text. Structured-filters-only queries skip the FTS join. Validated + on the real 18k-mail DB. Search box placeholder hints at operators. + - [x] **`OR`** (done 2026-07-24, `85d38fb`). Query splits on top-level uppercase `OR` into branches; + each branch is a self-contained condition (`id IN (FTS subquery)` + its SQL filters) and + branches are OR'd — so OR works across full-text _and_ structured filters. Verified on the 18k + DB (`from:github OR from:deepgram` = 90+7 = 97 exactly). + - [ ] **Remaining:** parenthesised grouping (nesting `(a OR b) c`), `in:sent/inbox/spam/trash` + (folder scope), and relative dates (`newer_than:7d`). Grouping needs a recursive parser. + - [ ] **Group search results into threads too.** Folder views group by conversation (`ee11c94`), but + `/email/search` still returns one row per message. Apply the same `thread_id` collapse to the + FTS result set so search matches Gmail's grouped results. + +- [x] **Conversation threading** (done 2026-07-24, `ee11c94`). `thread_id` column on `emails`: + header-based for new mail (`id` = `sha1(Message-Id)`, so `References[0]` hashes to the root's + id — `computeThreadId` in `sidecar/email/store.ts`), subject+counterpart backfill for already-synced mail. + `/messages` collapses to one row per thread (window fn) with count/unread; `/thread/:id` + + `/thread/:id/read`; reader renders a collapsible stack. Verified on synthetic + 18k real DB. + - [ ] **Upgrade old mail to exact threading.** One-time full re-fetch to capture `References` for + already-synced mail — replaces the subject+counterpart fallback (which can over-merge recurring + same-subject mail from one sender). Heavy/network-bound; opt-in. + - [ ] **Store the RFC `Message-Id` header** on ingest so replies can set a real `In-Reply-To` + (currently reply threading leans on subject/participants; `id` is a local hash, not the header). + +- [x] **Compose/reply/send** (done 2026-07-24). Gmail SMTP send with contacts autocomplete, rich + contenteditable body (inline images at the caret via paste/drag-drop, Bcc), single close button. +- [x] **`busy_timeout` on the email db** (done 2026-07-24, `9527e0b`). API + email sidecar share + `emails.db`; wait out a concurrent writer instead of 500-ing with "database is locked". + +- [ ] **Multiple views in the Email screen (tabs).** The screen grows beyond the current mailbox into + several switchable views: + - [ ] **Sender/domain management view.** Left panel: controls to group mail **by sender** or **by + domain** (room for more grouping axes later). From a group, run bulk actions on that + sender/domain: **unsubscribe + mark irrelevant**, **delete all mail** from it, **block/ignore + future incoming**, etc. Think "inbox cleanup / triage" — operate on a whole sender at once. - Needs: aggregate query (count/size per sender + per domain), a block/ignore list the + sync/IDLE path honors on incoming, an unsubscribe action (List-Unsubscribe header / link), + and bulk delete over a sender/domain. + - [ ] **Chat view.** The existing AI email-assistant flow — likely stays roughly as-is, just lives + as one of the tabs (main view). + - Open question: tabs vs. some other view switcher; which view is default. +