@bobfrankston/rmfmail 1.0.665 → 1.0.666

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
@@ -1024,7 +1024,19 @@ function sanitizeQuotedBody(msg) {
1024
1024
  body = body.replace(/<style[^>]*>[\s\S]*?<\/style>/gi, "");
1025
1025
  body = body.replace(/\s+style="[^"]*"/gi, "");
1026
1026
  body = body.replace(/\s+class="[^"]*"/gi, "");
1027
- body = body.replace(/\s+(width|height|align|valign|bgcolor|cellpadding|cellspacing|border)="[^"]*"/gi, "");
1027
+ // Strip layout-table attrs only off non-<img> tags. Earlier this regex
1028
+ // ate width/height on <img> too, which is what made App Store / Play
1029
+ // Store buttons render huge in quoted replies (the source ad had
1030
+ // explicit width="120" height="40" on each image). Q138.
1031
+ body = body.replace(/<(?!img\b)([^>]*?)\s+(width|height|align|valign|bgcolor|cellpadding|cellspacing|border)="[^"]*"([^>]*)>/gi, (_m, before, _attr, after) => `<${before}${after}>`);
1032
+ // Loop until no more matches — a single tag with multiple stripped
1033
+ // attrs needs more than one pass since the regex only handles one
1034
+ // attribute per match.
1035
+ let prev = "";
1036
+ while (prev !== body) {
1037
+ prev = body;
1038
+ body = body.replace(/<(?!img\b)([^>]*?)\s+(width|height|align|valign|bgcolor|cellpadding|cellspacing|border)="[^"]*"([^>]*)>/gi, (_m, before, _attr, after) => `<${before}${after}>`);
1039
+ }
1028
1040
  body = body.replace(/<table[^>]*>/gi, "<div>").replace(/<\/table>/gi, "</div>");
1029
1041
  body = body.replace(/<t[rdh][^>]*>/gi, "").replace(/<\/t[rdh]>/gi, " ");
1030
1042
  body = body.replace(/<thead[^>]*>|<\/thead>|<tbody[^>]*>|<\/tbody>/gi, "");
@@ -4036,8 +4048,29 @@ optEditorTiptap?.addEventListener("change", () => {
4036
4048
  saveEditorSetting("tiptap");
4037
4049
  });
4038
4050
  optEditorTinymce?.addEventListener("change", () => {
4039
- if (optEditorTinymce.checked)
4051
+ if (optEditorTinymce.checked) {
4040
4052
  saveEditorSetting("tinymce");
4053
+ // Q133 pre-warm — fire the TinyMCE bundle fetch right now so the
4054
+ // next compose-open finds tinymce.min.js already in WebView2's
4055
+ // HTTP cache. Without this, the first compose after picking
4056
+ // TinyMCE in Settings blocks ~1 s while the bundle downloads.
4057
+ try {
4058
+ // Default points at the locally bundled TinyMCE (build-tinymce.js
4059
+ // copies it from node_modules). Path is relative to index.html
4060
+ // which lives at the client/ root; compose.html (in client/compose/)
4061
+ // uses `../lib/tinymce/...` instead.
4062
+ const cdnUrl = localStorage.getItem("mailx-tinymce-cdn") || "lib/tinymce/tinymce.min.js";
4063
+ // Use <link rel="prefetch"> — fires the request as a hint,
4064
+ // doesn't execute the script (we don't want a stray global
4065
+ // tinymce in the main window; it lives in the compose iframe).
4066
+ const link = document.createElement("link");
4067
+ link.rel = "prefetch";
4068
+ link.as = "script";
4069
+ link.href = cdnUrl;
4070
+ document.head.appendChild(link);
4071
+ }
4072
+ catch { /* prefetch is best-effort */ }
4073
+ }
4041
4074
  });
4042
4075
  // External editor preference (Edit-in-Word handoff target). Stored under
4043
4076
  // settings.externalEditor so the service can read it via loadSettings().
@@ -4460,4 +4493,38 @@ if (isApp) {
4460
4493
  window.addEventListener("beforeunload", sendGeometry);
4461
4494
  setInterval(sendGeometry, 60_000);
4462
4495
  }
4496
+ // Boot-snapshot writer — captures key DOM regions to localStorage so the
4497
+ // next cold start hydrates instantly (see index.html's hydrateFromBootSnapshot).
4498
+ // Saves every 30 s and on beforeunload; pages with no inbox open just save
4499
+ // empty strings, which the loader treats as "skip this region."
4500
+ function saveBootSnapshot() {
4501
+ try {
4502
+ const folderTree = document.getElementById("folder-tree");
4503
+ const messageList = document.getElementById("ml-body");
4504
+ const folderTitle = document.getElementById("ml-folder-title");
4505
+ // Only save when at least one region has content. An in-flight render
4506
+ // (e.g. user clicked a folder, list is empty for a tick) would
4507
+ // otherwise clobber the previous good snapshot.
4508
+ const ftHtml = folderTree?.innerHTML?.trim() || "";
4509
+ const mlHtml = messageList?.innerHTML?.trim() || "";
4510
+ if (!ftHtml && !mlHtml)
4511
+ return;
4512
+ const snap = {
4513
+ folderTree: ftHtml,
4514
+ messageList: mlHtml,
4515
+ folderTitle: folderTitle?.innerHTML?.trim() || "",
4516
+ savedAt: Date.now(),
4517
+ };
4518
+ localStorage.setItem("mailx-boot-snapshot", JSON.stringify(snap));
4519
+ }
4520
+ catch { /* best-effort */ }
4521
+ }
4522
+ window.addEventListener("beforeunload", saveBootSnapshot);
4523
+ // Also save when the user tabs away — a Windows kill of an inactive
4524
+ // app would otherwise lose the snapshot up to 30 s old.
4525
+ document.addEventListener("visibilitychange", () => {
4526
+ if (document.visibilityState === "hidden")
4527
+ saveBootSnapshot();
4528
+ });
4529
+ setInterval(saveBootSnapshot, 30_000);
4463
4530
  //# sourceMappingURL=app.js.map