@bobfrankston/rmfmail 1.1.105 → 1.1.106

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/client/app.js CHANGED
@@ -4495,29 +4495,44 @@ optEditorTiptap?.addEventListener("change", () => {
4495
4495
  saveEditorSetting("tiptap");
4496
4496
  });
4497
4497
  optEditorTinymce?.addEventListener("change", () => {
4498
- if (optEditorTinymce.checked) {
4499
- saveEditorSetting("tinymce");
4500
- // Q133 pre-warm — fire the TinyMCE bundle fetch right now so the
4501
- // next compose-open finds tinymce.min.js already in WebView2's
4502
- // HTTP cache. Without this, the first compose after picking
4503
- // TinyMCE in Settings blocks ~1 s while the bundle downloads.
4504
- try {
4505
- // Default points at the locally bundled TinyMCE (build-tinymce.js
4506
- // copies it from node_modules). Path is relative to index.html
4507
- // which lives at the client/ root; compose.html (in client/compose/)
4508
- // uses `../lib/tinymce/...` instead.
4509
- const cdnUrl = localStorage.getItem("mailx-tinymce-cdn") || "lib/tinymce/tinymce.min.js";
4510
- // Use <link rel="prefetch"> — fires the request as a hint,
4511
- // doesn't execute the script (we don't want a stray global
4512
- // tinymce in the main window; it lives in the compose iframe).
4513
- const link = document.createElement("link");
4514
- link.rel = "prefetch";
4515
- link.as = "script";
4516
- link.href = cdnUrl;
4517
- document.head.appendChild(link);
4518
- }
4519
- catch { /* prefetch is best-effort */ }
4520
- }
4498
+ if (!optEditorTinymce.checked)
4499
+ return;
4500
+ saveEditorSetting("tinymce");
4501
+ // Q133 + 2026-05-21 warm the TinyMCE bundle into the HTTP cache so the
4502
+ // first compose-open doesn't stall downloading it. The old code used a
4503
+ // silent <link rel=prefetch> no completion signal, so picking TinyMCE
4504
+ // looked like a frozen UI ("hang while fetching"). Now we do an explicit
4505
+ // fetch() (I/O, already off the UI thread a Worker buys nothing for a
4506
+ // download) and surface a pending ready indicator on the menu item.
4507
+ const cdnUrl = localStorage.getItem("mailx-tinymce-cdn") || "lib/tinymce/tinymce.min.js";
4508
+ const label = optEditorTinymce.closest("label");
4509
+ let status = document.getElementById("opt-editor-tinymce-status");
4510
+ if (label && !status) {
4511
+ status = document.createElement("span");
4512
+ status.id = "opt-editor-tinymce-status";
4513
+ status.className = "tb-menu-status";
4514
+ label.appendChild(status);
4515
+ }
4516
+ const setStatus = (text, state) => {
4517
+ if (!status)
4518
+ return;
4519
+ status.textContent = text;
4520
+ status.dataset.state = state;
4521
+ };
4522
+ setStatus(" loading…", "pending"); // CSS ::before draws the spinner
4523
+ fetch(cdnUrl, { cache: "force-cache" })
4524
+ .then(r => (r.ok ? r.arrayBuffer() : Promise.reject(new Error(`HTTP ${r.status}`))))
4525
+ .then(() => {
4526
+ setStatus(" ✓ ready", "ready");
4527
+ // Clear the badge after a few seconds — but only if still "ready"
4528
+ // (don't wipe a later pending/error state from a re-pick).
4529
+ setTimeout(() => { if (status?.dataset.state === "ready")
4530
+ setStatus("", "idle"); }, 4000);
4531
+ })
4532
+ .catch(e => {
4533
+ setStatus(" ⚠ load failed", "error");
4534
+ console.error("[tinymce] pre-warm fetch failed:", e?.message || e);
4535
+ });
4521
4536
  });
4522
4537
  // External editor preference (Edit-in-Word handoff target). Stored under
4523
4538
  // settings.externalEditor so the service can read it via loadSettings().