@bobfrankston/rmfmail 1.0.634 → 1.0.643
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 +4 -1
- package/client/app.js +22 -0
- package/client/app.js.map +1 -1
- package/client/app.ts +15 -0
- package/client/components/alarms.js +17 -7
- package/client/components/alarms.js.map +1 -1
- package/client/components/alarms.ts +17 -6
- package/client/compose/compose.css +14 -1
- package/client/compose/compose.html +1 -0
- package/client/compose/compose.js +34 -15
- package/client/compose/compose.js.map +1 -1
- package/client/compose/compose.ts +37 -8
- package/client/compose/editor.js +44 -3
- package/client/compose/editor.js.map +1 -1
- package/client/compose/editor.ts +49 -3
- package/client/index.html +273 -15
- package/client/styles/components.css +14 -0
- package/docs/prod-android.md +55 -69
- package/docs/rmf-tiny.md +156 -0
- package/package.json +7 -1
- package/packages/mailx-store-web/package.json +1 -1
package/TODO.md
CHANGED
|
@@ -215,7 +215,7 @@ Items blocked on a design decision. Short-form question here; full context in th
|
|
|
215
215
|
| [**C46**](#ext46) | PARTIAL | Mailto handler split out as [P115](#ext115) (registry-only, not blocked). Remaining (Share target, tray, notifications, MSIX) still blocked on MSIX packaging. |
|
|
216
216
|
| **C127** | OPTIONAL | **Push relay for calendar / mail / tasks** — design at [`docs/push-relay.md`](push-relay.md). New `MailApps/relayer/` package, alerter-shape (single Node Express+ws process, deploy alongside other small services). Generic API: client POSTs `/subscribe` with upstream + params, relay registers the upstream `events.watch` and returns an SSE/WS endpoint the client holds open. Per-upstream adapter files (`google-calendar.ts`, `google-gmail.ts`, `ms-graph-events.ts`); fan-out + replay buffer + subscription store generic. Client side: `pushRelayUrl` + credentials in settings, falls back to syncToken polling when relay unreachable. **Build only when polling latency demonstrably matters** — for typical use, 5–10 s syncToken cadence is functionally instant. Push is a freshness boost, not a correctness fix. |
|
|
217
217
|
| **C128** | OPEN | **Body pre-parse on prefetch** — first click on any unviewed message pays `simpleParser` + `sanitizeHtml` cost (~100–500 ms on rich HTML). The parsedCache (added 2026-05-09) eliminates 2nd-and-later view costs but not the first. Fix: extend prefetch to ALSO parse + sanitize the body during background fetch and store the result alongside the .eml (e.g., `<uuid>.parsed.json` next to `<uuid>.eml`, or a `body_html_cached` column). Click-time path reads pre-parsed JSON, no parser invocation. Touches `mailx-imap/index.ts` prefetch path + `mailx-store/file-store.ts` for the parsed-blob storage. |
|
|
218
|
-
| **C129** | OPEN | **Android prod/dev split via
|
|
218
|
+
| **C129** | OPEN | **Android prod/dev split via filename/URL channel** — User 2026-05-09 (revised): no separate MAUI project. Same source tree builds; output filenames distinguish channels: Phase 1 (start now) writes `rmfmaildev.apk` alongside today's `mailx-maui.apk`. Phase 2 (when a release is declared) switches to `rmfmail-<version>.apk` versioned + `rmfmaildev.apk` for latest, with promotion = re-pointing `rmfmail.apk` at a chosen version. Single APK contents per build; channel is runtime setting in AppUpdater (Settings toggle for which URL to poll). Side-by-side install on one device NOT supported by this design (single Android package id) — that's a separate need. See `docs/prod-android.md` for full plan. Phase 1 ~30 min; full flow ~2.5 hours when activated. |
|
|
219
219
|
| **C130** | OPEN | **Reset prefetch error budget after fresh sync** — Android: `errors`/`failedThisSession` accumulate within a prefetch session. Already resets on next syncAll cycle (since each `prefetchBodies(account)` call is fresh). Verify this works in practice via logit; if a long sync session burns through budget and stays stuck, add an explicit reset on `bodyAvailable` events that fire while prefetch is still running. |
|
|
220
220
|
|
|
221
221
|
<a id="concerns"></a>
|
|
@@ -236,6 +236,9 @@ Previously shown as showstoppers; moved here because they haven't recurred on re
|
|
|
236
236
|
|
|
237
237
|
Small, self-contained items. Pick them up between higher-priority blocks without asking. Bump version per fix.
|
|
238
238
|
|
|
239
|
+
- **Q133 — TinyMCE: pre-warm CDN fetch / progressive editor swap.** Bob 2026-05-10: "if fetching tinymce takes time it should be done in background and only activate when it is available." Today, picking TinyMCE in Settings means the FIRST compose-open after that setting blocks for ~1s while the CDN bundle downloads. Better: when user toggles to TinyMCE in Settings, fire a fetch (or a `<link rel="modulepreload">`) so the bundle is in browser cache by the time they hit Compose. Even better: open compose with Quill instantly, swap the editor to TinyMCE in place once the bundle resolves. Touches `compose.ts` editor init + `app.ts` Settings toggle handler.
|
|
240
|
+
- **Q132 — importgen bugs blocking "always use importgen" rule for rmfmail.** Three issues, fix in `y:\dev\utils\importgen`: (1) reads CWD's `package.json`, so running from `client/` reads `client/package.json` (declares only quill); needs a `-p` flag or to walk up. (2) `resolveEntryPoint` blows up on packages with conditional `exports` (e.g. `".": { "import": { "node": "...", "browser": "..." } }`) — `dotExport.import` becomes an object, then `entryPoint.startsWith` throws. (3) Doesn't follow dynamic `await import()` calls if it scans imports rather than package.json deps (still need to verify which path it actually takes). Until fixed, rmfmail's import map is hand-maintained.
|
|
241
|
+
- **Q131 — Show holidays in calendar sidebar.** Thunderbird-style "Show holidays" checkbox. When checked, daemon's `getCalendarEvents` ALSO fetches `en.usa#holiday@group.v.calendar.google.com` (Google's standard US holidays) and merges into results, marking each with `isHoliday: true`. UI renders holiday rows with a distinct style (muted color, no edit/delete actions). Concrete edits: (a) `mailx-service/index.ts` line 1075ish `getCalendarEvents` — also call `gsync.listCalendarEvents(tp, fromMs, toMs, "en.usa#holiday@group.v.calendar.google.com")` when settings.calendar.showHolidays is true, store with `is_holiday=1` flag; (b) `mailx-store/db.ts` `calendar_events` schema — add `is_holiday` column; (c) `client/components/calendar-sidebar.ts` — render rows with `data-holiday="1"` styled distinctly; (d) settings UI radio/checkbox in the calendar sidebar's options popover. ~2 hours.
|
|
239
242
|
- **Q117 — Spring-loaded folders (drag-hover-expand).** When the user is dragging messages and hovers over a collapsed folder in the folder tree, expand it after a short hover delay so the user can see / target child folders. Auto-collapse back to its prior state when the drag leaves the folder OR the drop completes. Standard pattern in Outlook / Thunderbird / Finder. Implementation: `dragenter` on a folder row starts a timer (named constant `DRAG_HOVER_EXPAND_MS`, ~600ms target); `dragleave` cancels; on timer fire, set an `expanded-during-drag` flag and toggle the disclosure triangle. `dragend` / `drop` walks back up clearing flags and restoring prior state. No persistence — purely transient. (Per `feedback_no_magic_numbers.md`: timing values go in named constants, not inline.)
|
|
240
243
|
- **Verify rail-view / rail-settings dropdowns in narrow mode (paused 2026-04-30).** Added `rail-view` icon (👁) and `openMenuFromRail()` in `client/app.ts` that reparents the toolbar dropdown to `<body>` and switches to `position:fixed` anchored to the rail icon — so the toolbar's `display:none` in narrow mode no longer hides the menu. Compile not yet re-verified after the edits because Bash kept wedging; user paused to start the monorepo reorg (mailx → mailxapp). When resuming: run `tsc` in `client/`, sanity-check that opening View / Settings from the rail in narrow mode shows the dropdown anchored next to the icon, and that toolbar buttons in wide mode still work (they just toggle `hidden` on the same dropdown DOM, which is now a body child but still works).
|
|
241
244
|
|
package/client/app.js
CHANGED
|
@@ -3811,6 +3811,22 @@ const settingsBtn = document.getElementById("btn-settings");
|
|
|
3811
3811
|
const settingsDropdown = document.getElementById("settings-dropdown");
|
|
3812
3812
|
const optEditorQuill = document.getElementById("opt-editor-quill");
|
|
3813
3813
|
const optEditorTiptap = document.getElementById("opt-editor-tiptap");
|
|
3814
|
+
const optEditorTinymce = document.getElementById("opt-editor-tinymce");
|
|
3815
|
+
const optTinymceCdn = document.getElementById("opt-tinymce-cdn");
|
|
3816
|
+
// Restore TinyMCE CDN URL (Android-only path) from localStorage; the
|
|
3817
|
+
// adapter reads it on demand. Persisted client-side because it's a
|
|
3818
|
+
// per-device setting (Android needs CDN; desktop has npm install).
|
|
3819
|
+
try {
|
|
3820
|
+
if (optTinymceCdn)
|
|
3821
|
+
optTinymceCdn.value = localStorage.getItem("mailx-tinymce-cdn") || "";
|
|
3822
|
+
}
|
|
3823
|
+
catch { /* */ }
|
|
3824
|
+
optTinymceCdn?.addEventListener("change", () => {
|
|
3825
|
+
try {
|
|
3826
|
+
localStorage.setItem("mailx-tinymce-cdn", optTinymceCdn.value.trim());
|
|
3827
|
+
}
|
|
3828
|
+
catch { /* */ }
|
|
3829
|
+
});
|
|
3814
3830
|
settingsBtn?.addEventListener("click", (e) => {
|
|
3815
3831
|
e.stopPropagation();
|
|
3816
3832
|
if (viewDropdown)
|
|
@@ -3830,6 +3846,8 @@ getSettings().then((s) => {
|
|
|
3830
3846
|
optEditorQuill.checked = ed === "quill";
|
|
3831
3847
|
if (optEditorTiptap)
|
|
3832
3848
|
optEditorTiptap.checked = ed === "tiptap";
|
|
3849
|
+
if (optEditorTinymce)
|
|
3850
|
+
optEditorTinymce.checked = ed === "tinymce";
|
|
3833
3851
|
}).catch(() => { });
|
|
3834
3852
|
// Save editor choice to server settings
|
|
3835
3853
|
function saveEditorSetting(editor) {
|
|
@@ -3846,6 +3864,10 @@ optEditorTiptap?.addEventListener("change", () => {
|
|
|
3846
3864
|
if (optEditorTiptap.checked)
|
|
3847
3865
|
saveEditorSetting("tiptap");
|
|
3848
3866
|
});
|
|
3867
|
+
optEditorTinymce?.addEventListener("change", () => {
|
|
3868
|
+
if (optEditorTinymce.checked)
|
|
3869
|
+
saveEditorSetting("tinymce");
|
|
3870
|
+
});
|
|
3849
3871
|
// External editor preference (Edit-in-Word handoff target). Stored under
|
|
3850
3872
|
// settings.externalEditor so the service can read it via loadSettings().
|
|
3851
3873
|
// "auto" tries Word → LibreOffice → OS default; explicit values force
|