@bobfrankston/rmfmail 1.1.107 → 1.1.108

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
@@ -7,7 +7,7 @@ import { initMessageList, loadMessages, loadUnifiedInbox, loadSearchResults, rel
7
7
  import { seenOf, flaggedOf, draftOf, setSeen, setFlagged } from "@bobfrankston/mailx-types";
8
8
  import { initTabs, setActiveView as setActiveTabView, openTab } from "./components/tabs.js";
9
9
  import { getCurrentMessage, initViewer, popOutCurrentMessage, toggleFullscreenPreview, showPreviewBodyMenu, wrapHtmlBody } from "./components/message-viewer.js";
10
- import { connectWebSocket, onWsEvent, triggerSync, syncAccount, reauthenticate, getAccounts, getFolders, deleteMessage, deleteMessages, undeleteMessage, restartServer, getSyncPending, getVersion, getSettings, saveSettings, getAutocompleteSettings, saveAutocompleteSettings, repairAccounts, updateFlags, markAsSpamMessages, logClientEvent, sendMessage as apiSendMessage, subscribeStore } from "./lib/api-client.js";
10
+ import { connectWebSocket, onWsEvent, triggerSync, syncAccount, reauthenticate, getAccounts, getFolders, deleteMessage, deleteMessages, undeleteMessage, restartServer, getSyncPending, getVersion, getSettings, saveSettings, getAutocompleteSettings, saveAutocompleteSettings, repairAccounts, updateFlags, markAsSpamMessages, logClientEvent, sendMessage as apiSendMessage, subscribeStore, cancelServerSearch } from "./lib/api-client.js";
11
11
  import * as messageState from "./lib/message-state.js";
12
12
  // ── New message badge (favicon + title) ──
13
13
  /** The user-visible app name. Single point of change for the rename;
@@ -1282,19 +1282,19 @@ function quoteBody(msg) {
1282
1282
  const date = new Date(msg.date).toLocaleString();
1283
1283
  const from = msg.from.name ? `${msg.from.name} <${msg.from.address}>` : msg.from.address;
1284
1284
  const body = sanitizeQuotedBody(msg);
1285
- // Lead with a real empty paragraph (not bare <br><br>) so every editor
1286
- // has a proper block for the caret to land in and for the user's reply
1287
- // to flow into. Bare <br>s aren't a block container TinyMCE's caret
1288
- // fell through to the quote and typing landed unpredictably (Bob
1289
- // 2026-05-21). `<p><br></p>` is the standard "empty editable paragraph".
1290
- return `<p><br></p><div class="reply"><p>On ${date}, ${from} wrote:</p><blockquote>${body}</blockquote></div>`;
1285
+ // Lead with an empty paragraph so every editor has a real block for the
1286
+ // caret to land in and the user's reply to flow into — bare <br>s aren't
1287
+ // a block container, so TinyMCE's caret fell through to the quote (Bob
1288
+ // 2026-05-21). The blank-line spacing lives in <br>s AFTER the </p>, not
1289
+ // inside it.
1290
+ return `<p></p><br><br><div class="reply"><p>On ${date}, ${from} wrote:</p><blockquote>${body}</blockquote></div>`;
1291
1291
  }
1292
1292
  function forwardBody(msg) {
1293
1293
  const date = new Date(msg.date).toLocaleString();
1294
1294
  const from = msg.from.name ? `${msg.from.name} &lt;${msg.from.address}&gt;` : msg.from.address;
1295
1295
  const to = msg.to.map((a) => a.name ? `${a.name} &lt;${a.address}&gt;` : a.address).join(", ");
1296
1296
  const body = sanitizeQuotedBody(msg);
1297
- return `<p><br></p><div class="reply"><p>---------- Forwarded message ----------<br>From: ${from}<br>Date: ${date}<br>Subject: ${msg.subject}<br>To: ${to}</p>${body}</div>`;
1297
+ return `<p></p><br><br><div class="reply"><p>---------- Forwarded message ----------<br>From: ${from}<br>Date: ${date}<br>Subject: ${msg.subject}<br>To: ${to}</p>${body}</div>`;
1298
1298
  }
1299
1299
  let lastDeleted = null;
1300
1300
  let lastMoved = null;
@@ -2042,11 +2042,14 @@ function doSearch(immediate = false) {
2042
2042
  const serverOn = !!serverCheck?.checked;
2043
2043
  // Any re-run aborts a pending server pass — it'll be rescheduled below
2044
2044
  // if still wanted. This is the "editing the search aborts + restarts"
2045
- // behavior: each keystroke cancels the prior server search.
2045
+ // behavior: each keystroke cancels the prior server search. clearTimeout
2046
+ // kills a not-yet-fired pass; cancelServerSearch() aborts one that's
2047
+ // already mid-sweep on the daemon (generation bump → loop bails).
2046
2048
  if (serverSearchTimer) {
2047
2049
  clearTimeout(serverSearchTimer);
2048
2050
  serverSearchTimer = null;
2049
2051
  }
2052
+ cancelServerSearch();
2050
2053
  // "This folder" scope: instant client-side filter of the visible rows.
2051
2054
  // Only when the server checkbox is OFF — with it on we want the real
2052
2055
  // local+server search, not a row filter.
@@ -2093,6 +2096,7 @@ searchInput?.addEventListener("input", () => {
2093
2096
  if (serverSearchTimer) {
2094
2097
  clearTimeout(serverSearchTimer);
2095
2098
  serverSearchTimer = null;
2099
+ cancelServerSearch();
2096
2100
  }
2097
2101
  updateSearchHighlight();
2098
2102
  if (searchInput.value.trim() === "") {
@@ -2124,6 +2128,7 @@ searchInput?.addEventListener("keydown", (e) => {
2124
2128
  clearTimeout(serverSearchTimer);
2125
2129
  serverSearchTimer = null;
2126
2130
  }
2131
+ cancelServerSearch();
2127
2132
  updateSearchHighlight();
2128
2133
  clearSearchMode();
2129
2134
  // Clear any client-side filters
@@ -4525,6 +4530,17 @@ getSettings().then((s) => {
4525
4530
  }).catch(() => { });
4526
4531
  // Save editor choice to server settings
4527
4532
  function saveEditorSetting(editor) {
4533
+ // Update the localStorage cache SYNCHRONOUSLY. compose.ts reads
4534
+ // `mailx-editor-type` from localStorage at module-load to pick the
4535
+ // editor — its async getSettings() refresh only runs when a compose
4536
+ // window opens, and reads localStorage FIRST. Without this write the
4537
+ // cache stays stale and the next compose keeps the old editor until a
4538
+ // full app restart (Bob 2026-05-21: "changed to quill but got tinymce
4539
+ // until I restarted"). With it, the very next compose-open is correct.
4540
+ try {
4541
+ localStorage.setItem("mailx-editor-type", editor);
4542
+ }
4543
+ catch { /* private mode */ }
4528
4544
  getSettings().then((settings) => {
4529
4545
  settings.ui = { ...settings.ui, editor };
4530
4546
  saveSettings(settings);