@bobfrankston/rmfmail 1.2.79 → 1.2.80

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -15,9 +15,35 @@ npm install -g @bobfrankston/rmfmail
15
15
  rmfmail
16
16
  ```
17
17
 
18
- Requires Node.js 22 or later.
18
+ Requires Node.js 24.2 or later.
19
19
 
20
- On Windows, the native WebView2 app launches automatically via IPC (no HTTP server needed). Use `rmfmail --server` for browser mode at `http://127.0.0.1:9333`.
20
+ On Windows, the native WebView2 app launches automatically via IPC (no HTTP server needed).
21
+
22
+ ## Updating
23
+
24
+ Update with [`@bobfrankston/bobup`](https://www.npmjs.com/package/@bobfrankston/bobup) — it lists and updates globally-installed `@bobfrankston/*` packages (cross-platform):
25
+
26
+ ```bash
27
+ npm install -g @bobfrankston/bobup # once
28
+ bobup # update rmfmail + any other @bobfrankston tools to latest
29
+ bobup update rmfmail # update only rmfmail
30
+ bobup list # preview what's outdated first
31
+ ```
32
+
33
+ After updating, restart the app to pick up the new version: `rmfmail -kill` then `rmfmail`. (Plain `npm install -g @bobfrankston/rmfmail` still works as well.)
34
+
35
+ ## Platforms
36
+
37
+ | Platform | Status | How it runs |
38
+ |----------|--------|-------------|
39
+ | Windows | ✅ | Native WebView2 window via the **msger** host (default); IPC, no HTTP server |
40
+ | Linux | ✅ | **msger** host (WebKit WebView) |
41
+ | macOS | ⏳ planned | **msgview** (Electron) host — set `MAILX_HOST=msgview` once the adapter ships |
42
+ | Android | ✅ | Standalone **MAUI** app — its own local store + direct IMAP/Gmail sync; side-load the APK |
43
+
44
+ The host is chosen at runtime via `@bobfrankston/mailx-host` (override with the `MAILX_HOST` env var). The Android app is a full standalone client — not a remote view of the desktop — so it works with the desktop turned off.
45
+
46
+ > **`--server` (Express HTTP/browser) mode was removed (2026-06).** The client became IPC-only, leaving the browser path with no transport, so the mode was dead. Use the native desktop app or the standalone Android app instead.
21
47
 
22
48
  ## First-Time Setup
23
49
 
@@ -114,20 +140,17 @@ rmfmail --import my-accounts.jsonc
114
140
 
115
141
  This merges the accounts into your Google Drive settings (deduplicates by email address). Existing accounts are preserved.
116
142
 
117
- ### Gmail OAuth Prerequisites
143
+ ### Gmail OAuth
118
144
 
119
- Gmail OAuth requires a one-time Google Cloud setup:
120
- 1. Go to [Google Cloud Console](https://console.cloud.google.com/) and create a project
121
- 2. Enable the **Gmail API** (and **People API** for contacts)
122
- 3. Create **OAuth 2.0 credentials** (Desktop app type)
123
- 4. Download `credentials.json` to the iflow package directory
124
- 5. First connection opens a browser for consent. Tokens refresh automatically.
145
+ No setup required for normal use — rmfmail **ships with OAuth credentials**, so the first Gmail connection just opens a browser for consent and tokens refresh automatically.
146
+
147
+ **Advanced / self-hosting (optional):** to use your *own* OAuth app instead of the bundled credentials, create a project in the [Google Cloud Console](https://console.cloud.google.com/), enable the **Gmail API** (and **People API** for contacts), create **OAuth 2.0 credentials** (Desktop app type), and save them as `~/.rmfmail/google-credentials.json`. Outlook/Graph works the same way via `~/.rmfmail/microsoft-credentials.json`.
125
148
 
126
149
  ## Usage
127
150
 
128
151
  ### Layout
129
152
 
130
- A vertical **icon rail** sits on the far left (Thunderbird / Dovecot style) with one-click access to Compose, Inbox, All Inboxes, and Settings; Calendar / Tasks / Contacts slots are placeholders until those features land. The rail is always visible on wide and medium screens; it folds into the hamburger menu on narrow ones.
153
+ A vertical **icon rail** sits on the far left (Thunderbird / Dovecot style) with one-click access to Compose, Inbox, All Inboxes, Contacts (Google address book), Calendar, Tasks, View options, and Settings. Calendar/Tasks open the right-side sidebar (Google Calendar + Tasks); Contacts opens the address book. The rail is always visible on wide and medium screens; it folds into the hamburger menu on narrow ones.
131
154
 
132
155
  ### Reading Mail
133
156
 
@@ -159,10 +182,12 @@ A vertical **icon rail** sits on the far left (Thunderbird / Dovecot style) with
159
182
 
160
183
  - **Delete** or **Ctrl+D** -- Delete selected messages (moves to Trash; second delete in Trash is a hard delete + EXPUNGE)
161
184
  - **Ctrl+Z** -- Undo the last **delete or move** (whichever came last, 60s window)
162
- - **Ctrl+A** -- Select all messages in the list
185
+ - **Ctrl+A** -- Select all messages (enters multi-select; row avatars turn into checkboxes). **Escape** or a plain click exits multi-select.
186
+ - **Multi-select actions** -- With rows selected, right-click → **Flag/Unflag**, **Mark read/unread**, or **Move** acts on the whole selection. Clicking the **star** on any selected row flags/unflags the *entire* selection at once.
187
+ - **R** or **Ctrl+Q** -- Toggle read/unread on the focused message (bare R defers while typing in a field; Ctrl+Q always works). Auto mark-as-read (with a configurable delay) is on by default — see Settings.
163
188
  - **Drag and drop** -- Move messages to a folder by dragging them
164
189
  - **Right-click a message → Move to folder…** -- Searchable folder picker; useful on narrow layouts where the folder tree is hidden
165
- - Click the **star** column to flag/unflag a message
190
+ - Click the **star** column to flag/unflag a single message
166
191
  - **Unsubscribe** button appears when the message has a List-Unsubscribe header. One-click (RFC 8058) when the sender advertises `List-Unsubscribe-Post: List-Unsubscribe=One-Click`; otherwise opens the URL or a pre-filled compose for `mailto:` lists.
167
192
  - **Right-click on a From/To/Cc address** -- Copy name, Copy address, Copy both, Add to contacts, or Reply/Reply All/Forward
168
193
  - **Right-click in the message body → Translate** (opt-in) -- Uses the configured AI provider; select text first to translate just the selection. Off by default; enable under **Settings → AI translate**.
@@ -179,6 +204,8 @@ Type in the search bar and press **Enter**. Use the dropdown to search:
179
204
 
180
205
  Supports regex: wrap pattern in `/slashes/`.
181
206
 
207
+ **Search as find:** selecting a result then closing the search (clear the box or press **Escape**) returns you to that message in its real folder, instead of bouncing back to where you started — so search doubles as a "jump to this message" tool. Search is **ephemeral**: it never persists across a restart. An empty result reads *No messages found by "query"* so it's clear the search (not the mailbox) is empty.
208
+
182
209
  ### Folder Operations
183
210
 
184
211
  Right-click a folder for options:
@@ -187,19 +214,29 @@ Right-click a folder for options:
187
214
  - Rename / Delete folder
188
215
  - Empty (Trash and Junk only)
189
216
 
217
+ Drag a folder onto another to **reparent** it (server-side IMAP `RENAME` / Gmail label / Outlook move).
218
+
190
219
  ### View Options
191
220
 
192
221
  Under **View** in the toolbar:
193
222
  - **Two-line view** -- Show preview snippet below each subject
194
223
  - **Preview pane** -- Toggle the message reader panel
195
224
  - **Preview snippets** -- Show snippet text in message rows
196
- - **Flagged only** -- Show only starred messages
225
+ - **Flag stars** -- Show the grey ☆ on every row (on by default). Turn it off to hide empty stars; flagged ★ rows keep their gold star, and a row's ☆ reappears on hover so you can still click to flag.
226
+ - **Group by thread** -- Collapse reply chains to the newest message with a count pill; click the pill for a popup of the whole conversation
227
+ - **Only this conversation** -- Filter the list to the selected message's thread
228
+ - **Flagged only** -- Show only starred messages (the list takes a flag-coloured wash so the filter is obvious). Session-only — resets on restart.
229
+ - **Priority senders only** -- Show only mail from senders/domains marked priority in `contacts.jsonc`
197
230
  - **Folder counts** -- Show total message counts on all folders
231
+ - **Calendar sidebar** -- Show the right-side calendar / tasks panel
198
232
 
199
233
  ### Settings
200
234
 
201
235
  Under **Settings** in the toolbar:
202
- - **Editor** -- Choose between Quill (default) and tiptap for compose
236
+ - **Editor** -- Choose between Quill (default), tiptap, or TinyMCE for compose
237
+ - **External editor (Edit in Word)** -- Open the compose body in Microsoft Word or LibreOffice Writer for heavy editing (Auto / Word / LibreOffice)
238
+ - **Auto mark-as-read** -- Mark a viewed message read after a short, configurable delay (delay in seconds; 0 = immediately). Turn off to require an explicit Mark-read.
239
+ - **Check sender reputation (DNSBLs)** -- When opening a message with remote content, look the sender's domain up in three free blocklists in parallel (off by default; each query leaks the bare domain to that DNSBL)
203
240
  - **AI autocomplete** -- Enable LLM-powered writing suggestions (Ollama, Claude, or OpenAI)
204
241
  - **AI translate** -- Enable the right-click Translate item in the message viewer (off by default; uses the same provider as autocomplete)
205
242
  - **AI proofread** -- Enable the proofread path (off by default; provider method available, UI wiring in progress)
@@ -216,9 +253,10 @@ All AI features are opt-in. Provider + API key live in the autocomplete settings
216
253
  | Ctrl+F | Forward |
217
254
  | Delete / Ctrl+D | Delete |
218
255
  | Ctrl+Z | Undo last delete or move |
219
- | Ctrl+A | Select all |
256
+ | Ctrl+A | Select all (enter multi-select) |
257
+ | R / Ctrl+Q | Toggle read/unread on the focused message |
220
258
  | F5 | Sync all folders |
221
- | Escape | Clear search / close menus |
259
+ | Escape | Exit multi-select / clear search / close menus |
222
260
 
223
261
  **In the compose editor:**
224
262
 
@@ -248,21 +286,24 @@ All AI features are opt-in. Provider + API key live in the autocomplete settings
248
286
 
249
287
  Flag style: single-dash is canonical (`-kill`, `-setup`, ...). The parser
250
288
  also accepts double-dash (`--kill`, `--setup`) as a synonym, so older
251
- scripts that used `--server` / `--email` keep working.
289
+ scripts that used `--email` / `--import` keep working.
252
290
 
253
291
  ```
254
292
  rmfmail Start the app (native window via IPC)
255
293
  rmfmail -email <addr> First-time setup with this email (skips prompt)
256
294
  rmfmail -verbose Show log output in terminal (default: log file only)
257
295
  rmfmail -import <file> Import accounts.jsonc into Google Drive
258
- rmfmail -server Start HTTP server for dev/remote (http://localhost:9333)
259
296
 
260
297
  rmfmail -kill Kill running rmfmail processes + clean up WAL files
261
- rmfmail -repair Re-sync message metadata (fix garbled subjects)
262
- rmfmail -rebuild Wipe local cache and re-download everything from IMAP
263
298
  rmfmail -setup Interactive first-time setup (CLI)
299
+ rmfmail -add Add another account (CLI)
264
300
  rmfmail -test Test IMAP/SMTP connectivity for all accounts
301
+ rmfmail -repair Re-sync message metadata, keep .eml files (fix garbled subjects)
302
+ rmfmail -rebuild Wipe local cache and re-download everything from IMAP
303
+ rmfmail -recover Rebuild the DB index from the .eml files on disk (no network)
304
+ rmfmail -fix-flags Reconcile local ★ flags against the server's actual flagged set
265
305
  rmfmail -reauth Clear cached OAuth tokens; next start re-consents
306
+ rmfmail -log Print the log file path and exit
266
307
  rmfmail -v Show version
267
308
 
268
309
  rmfmail -send <file> Enqueue a .ltr/.eml for sending. Daemon parses
@@ -319,12 +360,14 @@ MSIX packaging or a hash-aware helper.
319
360
  `-register-mailto` if the picker is showing a stale "Node.js JavaScript
320
361
  Runtime" entry from an older install.
321
362
 
322
- ### -repair vs -rebuild
363
+ ### -repair vs -rebuild vs -recover
323
364
 
324
365
  **-repair** clears message metadata (subjects, flags, sender names) from the database but preserves your downloaded `.eml` message files. Folder sync state resets so rmfmail re-fetches all envelopes on next run. Use this when subjects show garbled characters.
325
366
 
326
367
  **-rebuild** deletes the entire database and message store, then re-downloads everything from IMAP. Accounts and settings are preserved. Use this for a clean start or to reclaim disk space.
327
368
 
369
+ **-recover** rebuilds the database index purely from the `.eml` files already on disk — no network. Use it to get back everything you had after a DB loss when you'd rather not re-download from the server.
370
+
328
371
  ## Configuration
329
372
 
330
373
  All config files live in `~/.rmfmail/` (on Windows: `C:\Users\You\.rmfmail\`). On first start, an existing `~/.mailx/` directory is auto-renamed to `~/.rmfmail/`.
@@ -477,7 +520,6 @@ Use `rmfmail --verbose` to see logs in the terminal instead.
477
520
 
478
521
  - **IPC-first** -- Default mode uses msger (Rust/WebView2) with bidirectional IPC. No TCP server, no CLOSE_WAIT zombies.
479
522
  - **Host abstraction** -- rmfmail talks to the WebView host through `@bobfrankston/mailx-host`, which picks msger (default) or msgview (Electron, planned for Mac and niche Linux) at runtime. Override with `MAILX_HOST=msger|msgview`. Internal package names retain the historical `mailx-*` prefix; only the public package was renamed.
480
- - **HTTP fallback** -- `--server` flag starts Express for browser/remote access.
481
523
  - **Local-first** -- Changes update local DB immediately; background worker syncs to IMAP.
482
524
  - **Offline reading** -- Full message bodies cached as .eml files.
483
525
  - **IMAP IDLE** -- Instant new-mail notifications + periodic sync fallback.
package/bin/mailx.js CHANGED
@@ -8,8 +8,6 @@
8
8
  *
9
9
  * Launch:
10
10
  * rmfmail Start daemon + open WebView (IPC, no TCP)
11
- * rmfmail -server Start Express HTTP server (dev/remote)
12
- * rmfmail -no-browser Server mode without auto-opening a browser
13
11
  * rmfmail -another Don't replace any existing rmfmail daemon
14
12
  * rmfmail -verbose Echo logs to terminal (default: log file only)
15
13
  * rmfmail -debug-server Launch debug introspection server
@@ -498,7 +496,7 @@ const repairMode = hasFlag("repair");
498
496
  const recoverMode = hasFlag("recover");
499
497
  const importMode = hasFlag("import");
500
498
  // Validate arguments
501
- const knownFlags = ["verbose", "kill", "v", "version", "setup", "add", "test", "rebuild", "repair", "recover", "fix-flags", "relink-bodies", "log", "import", "email", "mail", "daemon", "reauth", "mailto", "register-mailto", "unregister-mailto", "allow-elevated", "another", "server", "no-browser", "debug-server", "send", "account"];
499
+ const knownFlags = ["verbose", "kill", "v", "version", "setup", "add", "test", "rebuild", "repair", "recover", "fix-flags", "relink-bodies", "log", "import", "email", "mail", "daemon", "reauth", "mailto", "register-mailto", "unregister-mailto", "allow-elevated", "another", "no-browser", "debug-server", "send", "account"];
502
500
  for (const arg of args) {
503
501
  // Strip a leading -/-- and any `=value` suffix before checking.
504
502
  const flag = arg.replace(/^--?/, "").split("=")[0];
@@ -507,7 +505,6 @@ for (const arg of args) {
507
505
  console.error("Usage: rmfmail [options] (-- prefix also accepted for every flag)\n" +
508
506
  " Launch:\n" +
509
507
  " (no args) Start daemon + open WebView (IPC, no TCP)\n" +
510
- " -server Start Express HTTP server (dev/remote)\n" +
511
508
  " -no-browser Server mode without auto-opening a browser\n" +
512
509
  " -another Don't replace any existing rmfmail daemon\n" +
513
510
  " -verbose Echo logs to terminal (default: log file only)\n" +
@@ -1280,7 +1277,6 @@ if (hasFlag("v") || hasFlag("version")) {
1280
1277
  console.log(` miscinfo ${ver("@bobfrankston/miscinfo")}`);
1281
1278
  console.log(` oauth ${ver("@bobfrankston/oauthsupport")}`);
1282
1279
  console.log(` store ${ver("@bobfrankston/mailx-store")}`);
1283
- console.log(` server ${ver("@bobfrankston/mailx-server")}`);
1284
1280
  console.log(` api ${ver("@bobfrankston/mailx-api")}`);
1285
1281
  console.log(` platform ${process.platform} ${process.arch}`);
1286
1282
  process.exit(0);
@@ -1923,18 +1919,13 @@ RFC 5322 with CRLF line endings. Bodies are quoted-printable encoded (readable i
1923
1919
  catch (e) {
1924
1920
  console.error(` [readme] Could not write sending README: ${e?.message || e}`);
1925
1921
  }
1926
- // `--server` mode hand off to the Express HTTP server package (self-
1927
- // contained; initializes its own DB / IMAP manager / MailxService). No
1928
- // WebView2, no IPC bridge. Useful for remote access (phone before MAUI
1929
- // was a thing) and for JSON-RPC debugging from devtools. Loopback by
1930
- // default; passes --external through if the user asked for it. See C34.
1931
- if (hasFlag("server")) {
1932
- writeInstanceFile(process.pid);
1933
- await import("@bobfrankston/mailx-server");
1934
- // mailx-server's index.ts self-invokes `start()` — once imported,
1935
- // Express owns the event loop. Don't fall through to showService.
1936
- return;
1937
- }
1922
+ // `--server` (Express HTTP browser mode) was REMOVED 2026-06-29. The client
1923
+ // went IPC-only (everything through the msger `mailxapi` bridge; the old
1924
+ // `fetch("/api/...")` path was deleted), and nothing injected a browser-side
1925
+ // bridge so the served page could no longer talk to the server and the
1926
+ // mode was effectively dead. The Android app now covers the "use it from a
1927
+ // phone" case it originally existed for. The `mailx-api` Express router lives
1928
+ // on, used only by the optional `--debug-server` dev introspection endpoint.
1938
1929
  // Sync runs on its OWN thread now: spawn the sync worker (it hosts
1939
1930
  // ImapManager + its own write connection) and hand the main thread a proxy
1940
1931
  // that quacks like ImapManager (method calls forwarded over the bus,