@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.bundle.js +13 -2
- package/client/app.bundle.js.map +2 -2
- package/client/app.js +25 -9
- package/client/app.js.map +1 -1
- package/client/app.ts +22 -10
- package/client/compose/compose.bundle.js +4 -0
- package/client/compose/compose.bundle.js.map +2 -2
- package/client/lib/api-client.js +5 -0
- package/client/lib/api-client.js.map +1 -1
- package/client/lib/api-client.ts +6 -0
- package/package.json +3 -3
- package/packages/mailx-service/index.d.ts +8 -0
- package/packages/mailx-service/index.d.ts.map +1 -1
- package/packages/mailx-service/index.js +26 -1
- package/packages/mailx-service/index.js.map +1 -1
- package/packages/mailx-service/index.ts +28 -0
- package/packages/mailx-service/jsonrpc.js +3 -0
- package/packages/mailx-service/jsonrpc.js.map +1 -1
- package/packages/mailx-service/jsonrpc.ts +3 -0
- /package/packages/mailx-imap/{node_modules.npmglobalize-stash-61168 → node_modules.npmglobalize-stash-72068}/.package-lock.json +0 -0
package/client/app.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { initMessageList, loadMessages, loadUnifiedInbox, loadSearchResults, rel
|
|
|
8
8
|
import { seenOf, flaggedOf, draftOf, setSeen, setFlagged } from "@bobfrankston/mailx-types";
|
|
9
9
|
import { initTabs, setActiveView as setActiveTabView, openTab, type ViewTab } from "./components/tabs.js";
|
|
10
10
|
import { showMessage, getCurrentMessage, initViewer, popOutCurrentMessage, toggleFullscreenPreview, showPreviewBodyMenu, wrapHtmlBody } from "./components/message-viewer.js";
|
|
11
|
-
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";
|
|
11
|
+
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";
|
|
12
12
|
import * as messageState from "./lib/message-state.js";
|
|
13
13
|
|
|
14
14
|
// ── New message badge (favicon + title) ──
|
|
@@ -1237,12 +1237,12 @@ function quoteBody(msg: any): string {
|
|
|
1237
1237
|
const date = new Date(msg.date).toLocaleString();
|
|
1238
1238
|
const from = msg.from.name ? `${msg.from.name} <${msg.from.address}>` : msg.from.address;
|
|
1239
1239
|
const body = sanitizeQuotedBody(msg);
|
|
1240
|
-
// Lead with
|
|
1241
|
-
//
|
|
1242
|
-
//
|
|
1243
|
-
//
|
|
1244
|
-
//
|
|
1245
|
-
return `<p
|
|
1240
|
+
// Lead with an empty paragraph so every editor has a real block for the
|
|
1241
|
+
// caret to land in and the user's reply to flow into — bare <br>s aren't
|
|
1242
|
+
// a block container, so TinyMCE's caret fell through to the quote (Bob
|
|
1243
|
+
// 2026-05-21). The blank-line spacing lives in <br>s AFTER the </p>, not
|
|
1244
|
+
// inside it.
|
|
1245
|
+
return `<p></p><br><br><div class="reply"><p>On ${date}, ${from} wrote:</p><blockquote>${body}</blockquote></div>`;
|
|
1246
1246
|
}
|
|
1247
1247
|
|
|
1248
1248
|
function forwardBody(msg: any): string {
|
|
@@ -1250,7 +1250,7 @@ function forwardBody(msg: any): string {
|
|
|
1250
1250
|
const from = msg.from.name ? `${msg.from.name} <${msg.from.address}>` : msg.from.address;
|
|
1251
1251
|
const to = msg.to.map((a: any) => a.name ? `${a.name} <${a.address}>` : a.address).join(", ");
|
|
1252
1252
|
const body = sanitizeQuotedBody(msg);
|
|
1253
|
-
return `<p
|
|
1253
|
+
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>`;
|
|
1254
1254
|
}
|
|
1255
1255
|
|
|
1256
1256
|
// ── Delete with undo ──
|
|
@@ -1950,8 +1950,11 @@ function doSearch(immediate = false): void {
|
|
|
1950
1950
|
|
|
1951
1951
|
// Any re-run aborts a pending server pass — it'll be rescheduled below
|
|
1952
1952
|
// if still wanted. This is the "editing the search aborts + restarts"
|
|
1953
|
-
// behavior: each keystroke cancels the prior server search.
|
|
1953
|
+
// behavior: each keystroke cancels the prior server search. clearTimeout
|
|
1954
|
+
// kills a not-yet-fired pass; cancelServerSearch() aborts one that's
|
|
1955
|
+
// already mid-sweep on the daemon (generation bump → loop bails).
|
|
1954
1956
|
if (serverSearchTimer) { clearTimeout(serverSearchTimer); serverSearchTimer = null; }
|
|
1957
|
+
cancelServerSearch();
|
|
1955
1958
|
|
|
1956
1959
|
// "This folder" scope: instant client-side filter of the visible rows.
|
|
1957
1960
|
// Only when the server checkbox is OFF — with it on we want the real
|
|
@@ -2003,7 +2006,7 @@ let reloadDebounceTimer: ReturnType<typeof setTimeout> | null = null;
|
|
|
2003
2006
|
|
|
2004
2007
|
searchInput?.addEventListener("input", () => {
|
|
2005
2008
|
clearTimeout(searchTimeout);
|
|
2006
|
-
if (serverSearchTimer) { clearTimeout(serverSearchTimer); serverSearchTimer = null; }
|
|
2009
|
+
if (serverSearchTimer) { clearTimeout(serverSearchTimer); serverSearchTimer = null; cancelServerSearch(); }
|
|
2007
2010
|
updateSearchHighlight();
|
|
2008
2011
|
if (searchInput.value.trim() === "") {
|
|
2009
2012
|
// Cleared — reset immediately, no debounce. Must exit search mode
|
|
@@ -2029,6 +2032,7 @@ searchInput?.addEventListener("keydown", (e) => {
|
|
|
2029
2032
|
if (e.key === "Escape") {
|
|
2030
2033
|
searchInput.value = "";
|
|
2031
2034
|
if (serverSearchTimer) { clearTimeout(serverSearchTimer); serverSearchTimer = null; }
|
|
2035
|
+
cancelServerSearch();
|
|
2032
2036
|
updateSearchHighlight();
|
|
2033
2037
|
clearSearchMode();
|
|
2034
2038
|
// Clear any client-side filters
|
|
@@ -4219,6 +4223,14 @@ getSettings().then((s: any) => {
|
|
|
4219
4223
|
|
|
4220
4224
|
// Save editor choice to server settings
|
|
4221
4225
|
function saveEditorSetting(editor: string): void {
|
|
4226
|
+
// Update the localStorage cache SYNCHRONOUSLY. compose.ts reads
|
|
4227
|
+
// `mailx-editor-type` from localStorage at module-load to pick the
|
|
4228
|
+
// editor — its async getSettings() refresh only runs when a compose
|
|
4229
|
+
// window opens, and reads localStorage FIRST. Without this write the
|
|
4230
|
+
// cache stays stale and the next compose keeps the old editor until a
|
|
4231
|
+
// full app restart (Bob 2026-05-21: "changed to quill but got tinymce
|
|
4232
|
+
// until I restarted"). With it, the very next compose-open is correct.
|
|
4233
|
+
try { localStorage.setItem("mailx-editor-type", editor); } catch { /* private mode */ }
|
|
4222
4234
|
getSettings().then((settings: any) => {
|
|
4223
4235
|
settings.ui = { ...settings.ui, editor };
|
|
4224
4236
|
saveSettings(settings);
|
|
@@ -44,6 +44,7 @@ __export(api_client_exports, {
|
|
|
44
44
|
allowRemoteContent: () => allowRemoteContent,
|
|
45
45
|
autocomplete: () => autocomplete,
|
|
46
46
|
cancelQueuedOutgoing: () => cancelQueuedOutgoing,
|
|
47
|
+
cancelServerSearch: () => cancelServerSearch,
|
|
47
48
|
closeWordEdit: () => closeWordEdit,
|
|
48
49
|
connectEvents: () => connectEvents,
|
|
49
50
|
connectWebSocket: () => connectWebSocket,
|
|
@@ -217,6 +218,9 @@ function getUnifiedInbox(page = 1, pageSize = 50) {
|
|
|
217
218
|
function searchMessages(query, page = 1, pageSize = 50, scope = "all", accountId = "", folderId = 0, includeTrashSpam = false) {
|
|
218
219
|
return ipc().searchMessages(query, page, pageSize, scope, accountId, folderId, includeTrashSpam);
|
|
219
220
|
}
|
|
221
|
+
function cancelServerSearch() {
|
|
222
|
+
return ipc().cancelServerSearch?.();
|
|
223
|
+
}
|
|
220
224
|
function getMessage(accountId, uid, allowRemote = false, folderId) {
|
|
221
225
|
return ipc().getMessage(accountId, uid, allowRemote, folderId);
|
|
222
226
|
}
|