@bobfrankston/rmfmail 1.1.45 → 1.1.51

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.
Files changed (47) hide show
  1. package/TODO.md +11 -0
  2. package/client/app.bundle.js +371 -80
  3. package/client/app.bundle.js.map +4 -4
  4. package/client/app.js +167 -32
  5. package/client/app.js.map +1 -1
  6. package/client/app.ts +153 -32
  7. package/client/components/calendar-sidebar.js +221 -88
  8. package/client/components/calendar-sidebar.js.map +1 -1
  9. package/client/components/calendar-sidebar.ts +224 -83
  10. package/client/compose/compose.bundle.js +14 -0
  11. package/client/compose/compose.bundle.js.map +2 -2
  12. package/client/compose/spellcheck.js +15 -0
  13. package/client/compose/spellcheck.js.map +1 -1
  14. package/client/compose/spellcheck.ts +14 -0
  15. package/client/help/search-help.js +75 -0
  16. package/client/help/search-help.js.map +1 -0
  17. package/client/help/search-help.ts +75 -0
  18. package/client/index.html +7 -7
  19. package/client/lib/api-client.js +5 -0
  20. package/client/lib/api-client.js.map +1 -1
  21. package/client/lib/api-client.ts +5 -0
  22. package/client/lib/mailxapi.js +1 -0
  23. package/client/styles/components.css +204 -6
  24. package/docs/search.md +5 -1
  25. package/package.json +2 -3
  26. package/packages/mailx-service/google-sync.d.ts +3 -0
  27. package/packages/mailx-service/google-sync.d.ts.map +1 -1
  28. package/packages/mailx-service/google-sync.js +14 -2
  29. package/packages/mailx-service/google-sync.js.map +1 -1
  30. package/packages/mailx-service/google-sync.ts +17 -2
  31. package/packages/mailx-service/index.d.ts +11 -0
  32. package/packages/mailx-service/index.d.ts.map +1 -1
  33. package/packages/mailx-service/index.js +99 -127
  34. package/packages/mailx-service/index.js.map +1 -1
  35. package/packages/mailx-service/index.ts +91 -122
  36. package/packages/mailx-service/jsonrpc.js +2 -0
  37. package/packages/mailx-service/jsonrpc.js.map +1 -1
  38. package/packages/mailx-service/jsonrpc.ts +2 -0
  39. package/packages/mailx-settings/index.d.ts.map +1 -1
  40. package/packages/mailx-settings/index.js +4 -1
  41. package/packages/mailx-settings/index.js.map +1 -1
  42. package/packages/mailx-settings/index.ts +4 -1
  43. package/packages/mailx-store/db.d.ts.map +1 -1
  44. package/packages/mailx-store/db.js +8 -3
  45. package/packages/mailx-store/db.js.map +1 -1
  46. package/packages/mailx-store/db.ts +8 -3
  47. /package/packages/mailx-imap/{node_modules.npmglobalize-stash-28848 → node_modules.npmglobalize-stash-40840}/.package-lock.json +0 -0
package/TODO.md CHANGED
@@ -248,6 +248,17 @@ Small, self-contained items. Pick them up between higher-priority blocks without
248
248
  When exposing the calendar picker to users, ship a curated allowlist of vetted IDs rather than a free-form text input, OR show a warning when a user enters a non-`.official` / non-proper-noun ID. The naming asymmetry is on Google.
249
249
 
250
250
  Alternative source worth evaluating before generalizing: **Hebcal** (`https://www.hebcal.com/`) publishes a more rigorously curated Jewish-only feed (iCal + JSON API), and similar projects exist for other traditions. A `JsonHolidayProvider` interface that abstracts Google vs Hebcal vs ICS-by-URL behind a common shape would let users plug in whatever feed they trust.
251
+ - **Q147 — Message list stalls mid-scroll during sync churn — incremental updates + virtualization.** Bob 2026-05-16: "scrolling the list of messages, it seems stuck and finally gets loose." Design defect, not a symptom to patch. Root cause: `renderMessages` rebuilds the whole list (`new MessageRow` per row, ~10 DOM nodes each) and there is **no virtualization** — infinite scroll appends pages so a large folder holds thousands of live nodes. Sync events (and the every-30s calendar/task refresh storm makes them constant) trigger synchronous list re-render and/or `scrollIntoView` re-anchoring on the **main thread the user is scrolling** → scroll input queues until the burst clears. Existing `{scroll:false}` guards (added 2026-05-14 for the same "won't let me scroll" report) patched the symptom; the defect remains. Fix, two parts: (1) **sync events update rows in place** — a changed flag/date/folder mutates that one `MessageRow`'s DOM; never rebuild the list and never touch scroll/focus on a background reconcile; (2) **virtualize** large folders so only viewport-adjacent rows are in the DOM. Continuation of the row-objects-own-behavior direction (related: S56). Touches `message-list.ts` render + the store-subscription/event handlers.
252
+ - **Q146 — All-day events are floating calendar dates, not instants — model fix.** Bob 2026-05-16. Interim fix shipped v1.1.50: `calendarEventToLocal` parses an all-day `start.date` as *local* midnight instead of UTC (`Date.parse` treats date-only as UTC → a June 14 birthday rendered June 13 in US Eastern). That makes it correct *on the current machine in the current timezone* but the underlying model is still wrong: an all-day event — a birthday especially — is a **floating date** ("June 14", true everywhere), not a timestamp. Storing it as `start_ms` epoch ties a date to a timezone; it'll drift again on travel / timezone change / cross-device view. Proper fix: distinguish **timed events** (an instant — `start_ms`, fine) from **date events** (store the `YYYY-MM-DD` string, no timezone). Day-grouping, the day header, and rendering key off the date directly for date-events; only timed events do epoch math. Reminders on all-day events (Google's "1 day before at 9am") still need a timezone anchor — resolve that against the *local* day. Touches `GCalEvent`/`calendarEventToLocal`, `calendar_events` schema (a date column or an `all_day` + date pair), `calendarRowToObject`, the sidebar `CalEvent` + `fetchUpcoming` + `renderEvents` grouping, and the alarm scheduler. Medium model change — the v1.1.50 parse is a stopgap until it lands.
253
+ - **Q145 — From header: RFC 2047 encoded-word not decoded when it spans the whole `name <addr>`.** ✅ **Fixed v1.1.49** — `db.upsertMessage` now runs `decodeHeaderWords` over the `address` of from/to/cc, not just the display `name`. A legit address never carries an encoded-word so it's a no-op there; for the spam case the `=?utf-8?q?…=3C…=40…?=` mailbox decodes to readable text. Bob 2026-05-16, `screenshots/Screenshot 2026-05-16 141730.png`, sample `.eml` at `~/.rmfmail/mailxstore/bobma/1f/1fc0be6eb64d43e88503afaa8e424009.eml`. The From renders raw as `=?utf-8?q?PayPal_Nouveaut=C3=A9s_=3CParts=40samisec=2Ecom=3E?=@exch-smtp-out-01.wtr.livemail.co.uk`. The sender packed the entire display-name-and-address — including the `<`, `@`, `.`, `>` as `=3C =40 =2E =3E` — into ONE encoded-word, then concatenated `@domain` with no whitespace. Decoded it is `PayPal Nouveautés <Parts@samisec.com>`. mailx fails to decode it: the encoded-word isn't whitespace-delimited and sits where an addr-spec is expected, so the address parser (envelope `tokenizeParenList` / header decode) skips RFC 2047 decoding. Technically malformed mail, but Thunderbird decodes it. Fix: run encoded-word decoding on From/To/Cc display strings before address splitting, and tolerate a `=?...?=` token adjacent to non-whitespace. The broken string also propagates into the remote-content "Always: …" allowlist suggestions.
254
+ - **Q143 — Dynamic per-calendar checkboxes + per-source icons (Google-selected calendars).** ✅ **Implemented v1.1.47** (pending runtime verify). Shipped: `getCalendars()` service method, dynamic checkbox list, blue-dot/🇺🇸/✡️/🎂/monogram icons, per-event source icons, `hiddenCalendars` display-filter, `HOLIDAY_SOURCES` + `showHolidays`/`showJewishHolidays` removed. **Deferred**: multi-source badge (one event from two calendars still shows one icon — last-write-wins on `calendarId`; needs `iCalUID` + a source-set). Bob 2026-05-16, design settled. **Drop the hardcoded model**: today `HOLIDAY_SOURCES` hardcodes two Google calendar IDs and the sidebar has two fixed checkboxes (`showHolidays` / `showJewishHolidays`). Replace with a fully dynamic scheme — Bob curates *which* calendars exist by selecting them in Google Calendar; mailx enumerates the `selected` calendars (`listCalendars().filter(c => c.selected)`, already done in `refreshCalendarEvents`) and the sidebar **generates one checkbox per selected calendar**, each with an icon. Per-mailx visibility persists per calendar ID (replaces the two boolean settings). Calendar-URL input / non-Google calendar kinds are **deferred** (future TODO) — for now only Google-selected calendars.
255
+ - **Icons**: known types get a themed glyph — US holidays 🇺🇸, Jewish holidays ✡️, Birthdays 🎂 (detect via calendar ID pattern / `eventType`). Every other named calendar gets a **monogram badge**: the calendar name's first letter in a colored circle, color taken from the Google calendar's color (e.g. "Frankston Family" → "F" in a colored circle). The checkbox row and each event row from that calendar show the icon.
256
+ - **Birthdays**: shown automatically with 🎂 — no special checkbox (it's just one of the generated per-calendar checkboxes). Bob has already moved low-priority birthdays to a Contacts label so they don't flood; the two-tier "care about / FYI" split is his to manage upstream in Google for now. `eventType` (`default`/`birthday`/`fromGmail`/…) is NOT currently captured by `calendarEventToLocal` — add it.
257
+ - **Multi-source dedup**: the same event can arrive via two selected calendars. Show the row **once**, carry a *set* of source calendar IDs, render all their icons. Dedup key = `iCalUID` (stable across calendars), not `providerId`. Needs a source-list on the row replacing the single `calendarId`; the upsert passes in `refreshCalendarEvents` union into it instead of last-write-wins.
258
+ - Schema: `event_type` + source-list — no migration, calendar cache rebuilds from Google. Touches `GCalEvent` type, `calendarEventToLocal`, `refreshCalendarEvents`, a new service call exposing the enumerated calendar list (id, name, color, type) to the client, `db` calendar schema, `calendar-sidebar.ts` (dynamic checkbox list + icon rendering). Removes `HOLIDAY_SOURCES`, `showHolidays`, `showJewishHolidays`.
259
+ - **Q144 — Overdue tasks pinned to the top of the calendar list.** ✅ **Implemented v1.1.47** (pending runtime verify) — overdue block prepended in `renderEvents`, done-checkbox marks the task completed in Google (non-destructive). Bob 2026-05-16, design settled. NOT full Google-Calendar parity (which interleaves every due-dated task onto its due date). Instead: **only overdue tasks** — past due date AND `status: needsAction` — pin to the **top of the calendar event list** as a persistent "behind on these" block. Each row carries a done control (checkbox / ✓) which **marks the task completed in Google** (`updateTask` with `completedMs` → `status: "completed"`) — NOT a delete. Completing it drops it off the calendar top (no longer overdue) while the task survives in Google's Completed section. Tasks that are undated or due in the future stay only in the existing Task section — they don't appear on the calendar. Implementation: `renderEvents` in `calendar-sidebar.ts` prepends an overdue-task block built from `getTasks()` filtered to `dueMs < startOfToday && !completedMs`; reuse the existing `updateTask(uuid, {completedMs})` path the task-list checkbox already uses. Depends on the Q143 sidebar rework. Note: the existing task-list **× button is a hard `tasks.delete`** (destructive, no confirm) — distinct from the checkbox's mark-done; flagged for Bob, possible separate fix.
260
+ - **Q142 — Editor help should be HTML compiled into the app, not `editor.md`.** 2026-05-16: search help was converted from a deployed `docs/search.md` (shown raw, then rendered) to HTML compiled into the client (`client/help/search-help.ts`, dynamically imported, rendered as styled HTML in the panel). Bob's principle: feature help is application content and must display as HTML — the `docs/*.md` files are only a stand-in for proper settings UI and should hold *config-file* help only. `editor.md` is the remaining feature-help `.md` still in the deploy whitelist (`mailx-settings/index.ts` `USER_DOC_WHITELIST`). Convert it the same way: author `client/help/editor-help.ts` (or wherever the compose window's help is surfaced), point the editor's help affordance at it, drop `editor.md` from the whitelist, remove the file. Also: `docs/search.md` is now orphaned (no longer whitelisted, no longer read) — delete it; the `readConfigHelp` "search" mapping in `mailx-service` can go too.
261
+ - **Q141 — Shavuot shows in calendar sidebar with the ✡ Jewish-holidays checkbox OFF — DIAGNOSED, not a bug; design decision needed.** Bob 2026-05-16 (screenshot `screenshots/Screenshot 2026-05-16 105854.png`). Root cause confirmed from `rmfmail-2026-05-16.log`: `[calendar] enumerated 4 calendars, 3 selected` → `[calendar] pulled 10 events from "Jewish Holidays"`. There are **two independent Jewish-holiday paths** and only one is the checkbox: (1) mailx's ✡ checkbox → `showJewishHolidays` → pulls the `en.judaism#holiday` holiday-source feed; the log shows it is OFF (no "pulled N Jewish holiday events" line — only "5 US-official holiday events"). (2) `refreshCalendarEvents` enumerates *every* calendar the user marked `selected` in Google Calendar itself (`listCalendars().filter(c => c.selected)`) and pulls each as regular events. The user has Google's **"Jewish Holidays"** calendar subscribed + selected in their own Google account, so mailx faithfully pulls its 10 events — Shavuot among them — as ordinary calendar rows. The ✡ checkbox has no bearing on path (2). So mailx is behaving correctly: it shows a calendar the user selected in Google. Bob's own read ("artifact of Google Calendar") was right. **Resolutions** (pick one — needs Bob): (a) zero-code — deselect "Jewish Holidays" in Google Calendar's web UI and it stops appearing in mailx; (b) feature — add per-Google-calendar visibility toggles in the sidebar so a `selected` Google calendar can be hidden in mailx without changing Google (Q140-adjacent — the sidebar would list each enumerated calendar with a checkbox; gate the `calendarsToFetch` loop on it); (c) leave as-is. No code change made — this is a design call, not a defect.
251
262
  - **Q139 — General app-message channel from daemon to msger popups.** Bob 2026-05-11: "when an exit handler kills the popups by sending them a message — and can there be a general app message?" Today reminder popups are one-shot `showMessageBoxEx` (we have the handle for cross-platform reap). For graceful close-via-message AND other patterns (theme changes, in-flight content updates), spawn popups in `service: true` mode and use the bidirectional pipe — daemon calls `handle.send({type: "close"})` and the popup's JS listens for `_msgapiServiceEvent` events and reacts (`window.close()`, refresh content, etc.). Reuses the same wire mailx's main WebView already uses. Adapter shape: `showServicePopup(opts)` in `mailx-host` returns a `ServiceHandle` + a `result: Promise`; popup HTML's `<script>` registers `window._msgapiServiceEvent = (msg) => { if (msg.type === "close") window.close(); /* future: themeChanged, eventUpdated, … */ }`. Then `gracefulShutdown` in `bin/mailx.ts` iterates open popup handles and sends `{type:"close"}` instead of relying on the OS reaping. Pre-req: confirm msger's service mode supports the `rawHtml` content shape we use for reminders (may need a small msger PR).
252
263
  - **Q138 (original entry) — Quoted-reply image sizes lost.** Bob 2026-05-10: in the reply quote of a marketing email (Qatar Airways), the App Store / Google Play / AppGallery button images render larger than in the original. Cause: `sanitizeQuotedBody` in `client/app.ts` strips `width` / `height` attributes (along with `align`, `valign`, `bgcolor`, `cellpadding`, `cellspacing`, `border`) to flatten marketing-email layout tables. Stripping those attrs from `<img>` is collateral damage — table-only attributes there, but they also size images. Fix: in the regex, scope the attr strip to non-img tags, OR leave width/height alone everywhere (the layout flattening doesn't actually need them on tables either — `<table>→<div>` rewrites are doing the heavy lifting). ~10 line change. Low priority — visual nit, not data loss.
253
264
  - **Q136 — Server / provider capability matrix (forward backlog).** Catalog of server-specific features mailx could exploit. Detection is dynamic everywhere — IMAP via `CAPABILITY` + RFC 2971 `ID`, non-IMAP via provider-type at account setup. Each row below is its own future work item; this entry is the index.