@bobfrankston/rmfmail 1.0.612 → 1.0.619
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/TODO.md +6 -0
- package/client/app.js +8 -1
- package/client/app.js.map +1 -1
- package/client/app.ts +8 -1
- package/client/components/alarms.js +91 -173
- package/client/components/alarms.js.map +1 -1
- package/client/components/alarms.ts +85 -158
- package/client/components/message-list.js +21 -6
- package/client/components/message-list.js.map +1 -1
- package/client/components/message-list.ts +23 -6
- package/client/components/message-viewer.js +10 -0
- package/client/components/message-viewer.js.map +1 -1
- package/client/components/message-viewer.ts +10 -0
- package/package.json +1 -1
- package/packages/mailx-imap/index.d.ts.map +1 -1
- package/packages/mailx-imap/index.js +3 -0
- package/packages/mailx-imap/index.js.map +1 -1
- package/packages/mailx-imap/index.ts +3 -0
- package/packages/mailx-imap/package-lock.json +2 -2
- package/packages/mailx-imap/package.json +1 -1
- package/packages/mailx-store/db.d.ts.map +1 -1
- package/packages/mailx-store/db.js +36 -0
- package/packages/mailx-store/db.js.map +1 -1
- package/packages/mailx-store/db.ts +29 -0
- package/packages/mailx-store/package.json +1 -1
package/TODO.md
CHANGED
|
@@ -787,6 +787,12 @@ Was part of S1 "local-first reconciliation refactor" until 2026-04-23 when S1's
|
|
|
787
787
|
|
|
788
788
|
| Item | Version |
|
|
789
789
|
|---|---|
|
|
790
|
+
| **Header encoded-words: uniform decode at the storage seam** — User reports 2026-05-09: ".eml file with strange header" (chase / spam .eml had `From: "=?UTF-8?B?...?=" <s@x>` — encoded-word inside quoted-string, RFC-strictly forbidden but common in the wild). Bob: "you aren't parsing encoded header fields correctly. It's not subject that is encoded. You should uniformly process header escapes early in the parsing." Right answer: not per-callsite patches. Added `decodeHeaderWords` helper at the top of `mailx-store/db.ts` using `libmime.decodeWords`; called once in `upsertMessage` for `subject`, `from.name`, `to[].name`, `cc[].name`. Every storage path (IMAP fetch, Gmail API, manual append, eml replay) now decodes uniformly. Safe on already-decoded text — no `=?` markers → no-op. Old rows in DB keep their bad names until re-fetched; new fetches and any sync action that re-upserts the row pick up the fix. | v1.0.618 |
|
|
791
|
+
| **List selection: clicks during in-flight IPC no longer bounce** — User report 2026-05-09: "I just selected the 10:03 message and it showed and then bounced back to the 15:49. I have to be quick to capture the 10:03 before it bounces away." Root cause: `loadUnifiedInbox` and `loadMessages` captured `savedUid` at function start, then awaited the IPC. If you clicked a different row during the await, the new selection painted briefly, then `renderMessages` (post-IPC) cleared it and `restoreSelection` restored the *captured* old uid — your click was overwritten. Fix: read the selected uid LIVE — once before each `restoreSelection` call (just before `renderMessages` clears the DOM). The user's mid-flight click survives the re-render. | v1.0.617 |
|
|
792
|
+
| **Allowlist click: refresh in-memory cache after unblock** — User report 2026-05-09: "I pressed unblock but again it failed. With chase the next run found it unblocked. You added it to allowed but don't know it!" Persistence WAS working — next launch saw the chase.com domain in allowlist. The current-session re-render path was broken: `loadRemote` swapped the iframe but never updated `currentMessage` or the WebView-side `parsedCache` (added this morning for second-view speed). Any subsequent showMessage call hit the stale blocked-version cache and re-rendered the banner. Now `loadRemote` writes the unblocked message into both `currentMessage` and `parsedCachePut(accountId, uid, full)` so future renders agree with what the user just saw. | v1.0.616 |
|
|
793
|
+
| **Reminder popup: unified path** — User report 2026-05-09: "popup calendar has none of the new features for editing, snoozing etc." Two divergent code paths (`showPopup` for in-WebView, `showOsPopupForItem` for msger) had different button sets, different post-click logic, different aggregation. Collapsed into a single `firePopupForItem(item)` with the render target picked internally — `showInWebViewPopup()` (DOM overlay, single item) when no msger host, `showReminderPopup()` (OS window) when there is. Both paths now share: openPopups dedup, button labels (Snooze 15m / Snooze 1h / Dismiss / Open), post-click switch (Open opens htmlLink + 2-min snooze + state clear, Dismiss = permanent, X/Esc = 15-min auto-snooze), `firedThisSession` semantics. The aggregated multi-item panel is gone — one popup per due item, same shape on both render targets. Behavior changes are now reviewable in one place. | v1.0.615 |
|
|
794
|
+
| **Reminder popup: skip if one for the same event is already open** — User report 2026-05-09: "just popped up again. Can you figure out you are already showing the popup?" Yes — the OS popup was fire-and-forget without an open-tracking registry; if a 2-min Open-snooze expired while the user was still looking at the first popup, pollAlarms would happily open a second window for the same uuid. Added `openPopups: Set<uuid>` registered at popup-open and cleared in `finally` (so even unhandled rejections don't leak the entry). pollAlarms filter now checks both `firedThisSession` AND `openPopups`. Doesn't fix the underlying "two paths in alarms.ts" structural issue — `showPopup` (browser fallback) and `showOsPopupForItem` (msger) still diverge. That unification is filed as the next step. | v1.0.614 |
|
|
795
|
+
| **Del key works with list selection even when focus is in search box** — User report 2026-05-09: "selected all npm messages and pressed del, nothing happened, but the top trashcan worked." Root cause: Del-key handler short-circuited whenever `e.target.tagName === "INPUT"` so JSONC-editor / compose-form Del would do its native thing. Search input retaining focus after a filter triggered the same guard. Fix: only defer to native Del when there's NO list selection AND focus is in a text surface. With multi-selected rows the user's intent is "delete those" regardless of focus. | v1.0.613 |
|
|
790
796
|
| **Cold-start: parallel imports + measurement timestamps** — User feedback 2026-05-09: "It used to start much faster, I'm very suspicious of your diagnosis." Real culprit found by reading the code (not theorizing): seven `await import()` calls in `bin/mailx.ts` runs sequentially before `showService` — `mailx-store`, `mailx-imap`, `mailx-service`, `mailx-service/jsonrpc.js`, `mailx-settings`, `node-tcp-transport`, `mailx-store/file-store.js`. Each blocked the next; they have no inter-dependencies. Now batched into one `Promise.all`, paying ESM resolution + module init cost once concurrently instead of seven times sequentially. Plus checkpoint timestamps (`[boot N ms] label`) at modules-loaded, settings-loaded, DB-opened, body-path-migration, calling-showService, addAccount-start, addAccount-complete — so next startup the log shows the actual timeline. Now we'll see numbers, not theory. | v1.0.611 |
|
|
791
797
|
| **App name single source of truth (APP_NAME)** — User feedback 2026-05-09: "the string rmfmail should be a named value not inline." HTML had hardcoded "rmfmail" in `<title>`, About button, app-version spans, startup-status. Now: HTML carries placeholder text, `propagateAppName()` runs at module load and stamps `APP_NAME` into every surface. APP_NAME is the only place the literal lives — rename the app, update one constant. | v1.0.610 |
|
|
792
798
|
| **Allowlist click reliability + honest startup string** — User reports 2026-05-09: (a) "I clicked on already show @chase.com why isn't it unblocking?" — handler awaited `allowRemoteContent` BEFORE re-rendering; if the persist threw (atomicWrite fails / GDrive unavailable / IPC error) the unhandled rejection silently aborted the click. Now re-render runs FIRST, persist runs in background, errors surface in the status bar. (b) "What server are you referring to?" — startup overlay said "Connecting to server..." despite there being no separate server (mailx is one integrated package). Now says "Loading…" and the JS hydration replaces it with the real status as soon as the IPC bridge is alive. | v1.0.609 |
|
package/client/app.js
CHANGED
|
@@ -2462,11 +2462,18 @@ document.addEventListener("keydown", (e) => {
|
|
|
2462
2462
|
// Ctrl+D or Delete = Delete selected messages.
|
|
2463
2463
|
// P15: don't hijack Delete inside text inputs / textareas / contenteditable
|
|
2464
2464
|
// — JSONC editor's Delete key was being eaten because we always preventDefault'd.
|
|
2465
|
+
// 2026-05-09 fix: if the LIST has multiple selected rows, the user
|
|
2466
|
+
// clearly means "delete those" regardless of where focus is — search
|
|
2467
|
+
// input retaining focus after filtering shouldn't make the key dead.
|
|
2468
|
+
// Only defer to native behavior when there's no list selection AND
|
|
2469
|
+
// focus is in a text-editing surface.
|
|
2465
2470
|
if ((e.ctrlKey && e.key === "d") || e.key === "Delete") {
|
|
2466
2471
|
const t = e.target;
|
|
2467
2472
|
const tag = t?.tagName;
|
|
2468
2473
|
const editable = t?.isContentEditable;
|
|
2469
|
-
|
|
2474
|
+
const inEditable = tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT" || editable;
|
|
2475
|
+
const listSelectionCount = document.querySelectorAll("#ml-body .ml-row.selected").length;
|
|
2476
|
+
if (inEditable && listSelectionCount === 0)
|
|
2470
2477
|
return;
|
|
2471
2478
|
e.preventDefault();
|
|
2472
2479
|
deleteSelection();
|