@bobfrankston/rmfmail 1.0.681 → 1.0.688
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/bin/lean-accounts.js +0 -1
- package/client/app.bundle.js +138 -38
- package/client/app.bundle.js.map +2 -2
- package/client/app.js +12 -1
- package/client/app.js.map +1 -1
- package/client/app.ts +12 -1
- package/client/components/context-menu.js +2 -0
- package/client/components/context-menu.js.map +1 -1
- package/client/components/context-menu.ts +6 -0
- package/client/components/folder-tree.js +26 -4
- package/client/components/folder-tree.js.map +1 -1
- package/client/components/folder-tree.ts +21 -4
- package/client/components/message-list.js +108 -40
- package/client/components/message-list.js.map +1 -1
- package/client/components/message-list.ts +103 -38
- package/client/compose/compose.bundle.js +148 -15
- package/client/compose/compose.bundle.js.map +3 -3
- package/client/compose/spellcheck.js +178 -12
- package/client/compose/spellcheck.js.map +1 -1
- package/client/compose/spellcheck.ts +168 -8
- package/client/lib/api-client.js +3 -0
- package/client/lib/api-client.js.map +1 -1
- package/client/lib/api-client.ts +4 -0
- package/client/lib/mailxapi.js +3 -0
- package/client/lib/rmf-tiny.js +25 -6
- package/package.json +7 -7
- package/packages/mailx-core/index.d.ts.map +1 -1
- package/packages/mailx-core/index.js +2 -12
- package/packages/mailx-core/index.js.map +1 -1
- package/packages/mailx-core/index.ts +2 -12
- package/packages/mailx-imap/index.d.ts.map +1 -1
- package/packages/mailx-imap/index.js +51 -6
- package/packages/mailx-imap/index.js.map +1 -1
- package/packages/mailx-imap/index.ts +51 -6
- package/packages/mailx-imap/node_modules.npmglobalize-stash-78076/.package-lock.json +116 -0
- package/packages/mailx-imap/package-lock.json +2 -2
- package/packages/mailx-imap/package.json +1 -1
- package/packages/mailx-service/index.d.ts +22 -0
- package/packages/mailx-service/index.d.ts.map +1 -1
- package/packages/mailx-service/index.js +123 -0
- package/packages/mailx-service/index.js.map +1 -1
- package/packages/mailx-service/index.ts +109 -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-service/local-store.d.ts.map +1 -1
- package/packages/mailx-service/local-store.js +17 -12
- package/packages/mailx-service/local-store.js.map +1 -1
- package/packages/mailx-service/local-store.ts +15 -12
- package/packages/mailx-settings/docs/accounts.md +14 -1
- package/packages/mailx-settings/docs/npmglobalize-disttag.md +90 -0
- package/packages/mailx-settings/docs/prod-android.md +88 -0
- package/packages/mailx-settings/docs/prod.md +224 -0
- package/packages/mailx-settings/docs/push-relay.md +141 -0
- package/packages/mailx-settings/docs/rmf-tiny.md +156 -0
- package/packages/mailx-settings/index.d.ts +1 -1
- package/packages/mailx-settings/index.d.ts.map +1 -1
- package/packages/mailx-settings/index.js +1 -4
- package/packages/mailx-settings/index.js.map +1 -1
- package/packages/mailx-settings/index.ts +1 -3
- package/packages/mailx-settings/package.json +1 -1
- package/packages/mailx-store/db.d.ts +6 -0
- package/packages/mailx-store/db.d.ts.map +1 -1
- package/packages/mailx-store/db.js +67 -7
- package/packages/mailx-store/db.js.map +1 -1
- package/packages/mailx-store/db.ts +73 -7
- package/packages/mailx-store/package.json +1 -1
- package/packages/mailx-store-web/package.json +4 -1
- package/packages/mailx-store-web/web-settings.d.ts.map +1 -1
- package/packages/mailx-store-web/web-settings.js +0 -1
- package/packages/mailx-store-web/web-settings.js.map +1 -1
- package/packages/mailx-store-web/web-settings.ts +0 -1
- package/packages/mailx-types/index.d.ts +1 -2
- package/packages/mailx-types/index.d.ts.map +1 -1
- package/packages/mailx-types/index.js.map +1 -1
- package/packages/mailx-types/index.ts +1 -2
- package/packages/mailx-types/package.json +1 -1
package/bin/lean-accounts.js
CHANGED
|
@@ -98,7 +98,6 @@ function denormalizeAccount(acct, globalName) {
|
|
|
98
98
|
if (acct.defaultSend) out.defaultSend = true;
|
|
99
99
|
if (acct.enabled === false) out.enabled = false;
|
|
100
100
|
if (acct.relayDomains?.length > 0) out.relayDomains = acct.relayDomains;
|
|
101
|
-
if (acct.deliveredToPrefix?.length > 0) out.deliveredToPrefix = acct.deliveredToPrefix;
|
|
102
101
|
if (acct.identityDomains?.length > 0) out.identityDomains = acct.identityDomains;
|
|
103
102
|
|
|
104
103
|
const syncContactsDefault = provider?.imap.auth === "oauth2";
|
package/client/app.bundle.js
CHANGED
|
@@ -62,6 +62,7 @@ __export(api_client_exports, {
|
|
|
62
62
|
logClientEvent: () => logClientEvent,
|
|
63
63
|
markAsSpamMessages: () => markAsSpamMessages,
|
|
64
64
|
markFolderRead: () => markFolderRead,
|
|
65
|
+
moveFolderToTrash: () => moveFolderToTrash,
|
|
65
66
|
moveMessage: () => moveMessage,
|
|
66
67
|
moveMessages: () => moveMessages,
|
|
67
68
|
onEvent: () => onEvent,
|
|
@@ -334,6 +335,9 @@ function renameFolder(accountId, folderId, newName) {
|
|
|
334
335
|
function deleteFolder(accountId, folderId) {
|
|
335
336
|
return ipc().deleteFolder?.(accountId, folderId);
|
|
336
337
|
}
|
|
338
|
+
function moveFolderToTrash(accountId, folderId) {
|
|
339
|
+
return ipc().moveFolderToTrash?.(accountId, folderId);
|
|
340
|
+
}
|
|
337
341
|
function emptyFolder(accountId, folderId) {
|
|
338
342
|
return ipc().emptyFolder?.(accountId, folderId);
|
|
339
343
|
}
|
|
@@ -489,6 +493,8 @@ function showContextMenu(x, y, items) {
|
|
|
489
493
|
const el = document.createElement("div");
|
|
490
494
|
el.className = "ctx-item" + (item.disabled ? " ctx-disabled" : "");
|
|
491
495
|
el.textContent = item.label;
|
|
496
|
+
if (item.tooltip)
|
|
497
|
+
el.title = item.tooltip;
|
|
492
498
|
if (!item.disabled) {
|
|
493
499
|
el.addEventListener("click", () => {
|
|
494
500
|
closeContextMenu();
|
|
@@ -2164,6 +2170,57 @@ function cacheKey(mode, a, f, flagged, q) {
|
|
|
2164
2170
|
return `folder:${a}:${f}:${flagged ? "flag" : ""}`;
|
|
2165
2171
|
return `search:${q}`;
|
|
2166
2172
|
}
|
|
2173
|
+
function persistPositions() {
|
|
2174
|
+
try {
|
|
2175
|
+
const obj = {};
|
|
2176
|
+
for (const [k, v] of positionMemory)
|
|
2177
|
+
obj[k] = v;
|
|
2178
|
+
sessionStorage.setItem(POSITION_STORAGE_KEY, JSON.stringify(obj));
|
|
2179
|
+
} catch {
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2182
|
+
function currentViewKey() {
|
|
2183
|
+
if (searchMode)
|
|
2184
|
+
return cacheKey("search", void 0, void 0, void 0, currentSearchQuery);
|
|
2185
|
+
if (unifiedMode)
|
|
2186
|
+
return CACHE_KEY_UNIFIED;
|
|
2187
|
+
if (!currentAccountId2 || currentFolderId == null)
|
|
2188
|
+
return null;
|
|
2189
|
+
const flaggedOnly = document.getElementById("ml-body")?.classList.contains("flagged-only") || false;
|
|
2190
|
+
return cacheKey("folder", currentAccountId2, currentFolderId, flaggedOnly);
|
|
2191
|
+
}
|
|
2192
|
+
function rememberPosition() {
|
|
2193
|
+
const key = currentViewKey();
|
|
2194
|
+
if (!key)
|
|
2195
|
+
return;
|
|
2196
|
+
const body = document.getElementById("ml-body");
|
|
2197
|
+
if (!body)
|
|
2198
|
+
return;
|
|
2199
|
+
const sel = body.querySelector(".ml-row.selected");
|
|
2200
|
+
if (!sel)
|
|
2201
|
+
return;
|
|
2202
|
+
const uid = Number(sel.dataset.uid);
|
|
2203
|
+
if (!Number.isFinite(uid))
|
|
2204
|
+
return;
|
|
2205
|
+
positionMemory.set(key, { uid, scroll: body.scrollTop });
|
|
2206
|
+
persistPositions();
|
|
2207
|
+
}
|
|
2208
|
+
function pickRestoreUid(items, saved) {
|
|
2209
|
+
if (!items.length)
|
|
2210
|
+
return null;
|
|
2211
|
+
if (items.some((m) => m.uid === saved))
|
|
2212
|
+
return saved;
|
|
2213
|
+
let best = -1;
|
|
2214
|
+
for (const m of items) {
|
|
2215
|
+
if (typeof m.uid !== "number")
|
|
2216
|
+
continue;
|
|
2217
|
+
if (m.uid < saved && m.uid > best)
|
|
2218
|
+
best = m.uid;
|
|
2219
|
+
}
|
|
2220
|
+
if (best >= 0)
|
|
2221
|
+
return best;
|
|
2222
|
+
return typeof items[0]?.uid === "number" ? items[0].uid : null;
|
|
2223
|
+
}
|
|
2167
2224
|
function listResultsEqual(a, b) {
|
|
2168
2225
|
if (!a || a.length !== b.length)
|
|
2169
2226
|
return false;
|
|
@@ -2210,6 +2267,7 @@ function focusRow(row) {
|
|
|
2210
2267
|
showMessage(row.accountId, row.msg.uid, row.msg.folderId, void 0, false, row.msg);
|
|
2211
2268
|
onMessageSelect(row.accountId, row.msg.uid, row.msg.folderId);
|
|
2212
2269
|
document.dispatchEvent(new CustomEvent("mailx-focus-changed", { detail: row.msg }));
|
|
2270
|
+
rememberPosition();
|
|
2213
2271
|
}
|
|
2214
2272
|
function getCurrentFocused() {
|
|
2215
2273
|
return focusedRow ? focusedRow.msg : null;
|
|
@@ -2467,19 +2525,19 @@ async function loadUnifiedInbox(autoSelect = true) {
|
|
|
2467
2525
|
const fromHeader = document.querySelector(".ml-col-from");
|
|
2468
2526
|
if (fromHeader)
|
|
2469
2527
|
fromHeader.textContent = "From";
|
|
2470
|
-
const
|
|
2471
|
-
const
|
|
2528
|
+
const remembered = positionMemory.get(CACHE_KEY_UNIFIED);
|
|
2529
|
+
const savedScroll = remembered?.scroll ?? (!autoSelect ? body.scrollTop : 0);
|
|
2472
2530
|
const cached = listCache.get(CACHE_KEY_UNIFIED);
|
|
2473
2531
|
if (cached) {
|
|
2474
|
-
const preCacheUid = currentSelectedUid();
|
|
2475
2532
|
totalMessages = cached.total;
|
|
2476
2533
|
setMessages(cached.items);
|
|
2477
2534
|
renderMessages(body, "", cached.items);
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
else {
|
|
2535
|
+
const targetUid = remembered ? pickRestoreUid(cached.items, remembered.uid) : null;
|
|
2536
|
+
if (targetUid != null) {
|
|
2481
2537
|
body.scrollTop = savedScroll;
|
|
2482
|
-
restoreSelection(body,
|
|
2538
|
+
restoreSelection(body, String(targetUid));
|
|
2539
|
+
} else if (autoSelect) {
|
|
2540
|
+
selectFirst(body);
|
|
2483
2541
|
}
|
|
2484
2542
|
} else if (autoSelect) {
|
|
2485
2543
|
body.innerHTML = `<div class="ml-empty">Loading...</div>`;
|
|
@@ -2497,14 +2555,14 @@ async function loadUnifiedInbox(autoSelect = true) {
|
|
|
2497
2555
|
body.innerHTML = `<div class="ml-empty">${result.total > 0 ? `${result.total} messages syncing...` : "Syncing \u2014 messages will appear shortly"}</div>`;
|
|
2498
2556
|
return;
|
|
2499
2557
|
}
|
|
2500
|
-
const preRenderUid = currentSelectedUid();
|
|
2501
2558
|
setMessages(result.items);
|
|
2502
2559
|
renderMessages(body, "", result.items);
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
} else {
|
|
2560
|
+
const targetUid = remembered ? pickRestoreUid(result.items, remembered.uid) : null;
|
|
2561
|
+
if (targetUid != null) {
|
|
2506
2562
|
body.scrollTop = savedScroll;
|
|
2507
|
-
restoreSelection(body,
|
|
2563
|
+
restoreSelection(body, String(targetUid));
|
|
2564
|
+
} else if (autoSelect) {
|
|
2565
|
+
selectFirst(body);
|
|
2508
2566
|
}
|
|
2509
2567
|
} catch (e) {
|
|
2510
2568
|
if (e.name === "AbortError")
|
|
@@ -2604,23 +2662,23 @@ async function loadMessages(accountId, folderId, page = 1, specialUse = "", auto
|
|
|
2604
2662
|
const fromHeader = document.querySelector(".ml-col-from");
|
|
2605
2663
|
if (fromHeader)
|
|
2606
2664
|
fromHeader.textContent = showToInsteadOfFrom ? "To" : "From";
|
|
2607
|
-
const savedScroll = !autoSelect ? body.scrollTop : 0;
|
|
2608
|
-
const currentSelectedUid = () => !autoSelect ? body.querySelector(".ml-row.selected")?.getAttribute("data-uid") || null : null;
|
|
2609
2665
|
const flaggedOnly = document.getElementById("ml-body")?.classList.contains("flagged-only") || false;
|
|
2610
2666
|
const cKey = cacheKey("folder", accountId, folderId, flaggedOnly);
|
|
2667
|
+
const remembered = positionMemory.get(cKey);
|
|
2668
|
+
const savedScroll = remembered?.scroll ?? (!autoSelect ? body.scrollTop : 0);
|
|
2611
2669
|
const cached = listCache.get(cKey);
|
|
2612
2670
|
if (cached) {
|
|
2613
|
-
const preCacheUid = currentSelectedUid();
|
|
2614
2671
|
totalMessages = cached.total;
|
|
2615
2672
|
setMessages(cached.items);
|
|
2616
2673
|
renderMessages(body, accountId, cached.items);
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
else {
|
|
2674
|
+
const targetUid = remembered ? pickRestoreUid(cached.items, remembered.uid) : null;
|
|
2675
|
+
if (targetUid != null) {
|
|
2620
2676
|
requestAnimationFrame(() => {
|
|
2621
2677
|
body.scrollTop = savedScroll;
|
|
2622
|
-
restoreSelection(body,
|
|
2678
|
+
restoreSelection(body, String(targetUid));
|
|
2623
2679
|
});
|
|
2680
|
+
} else if (autoSelect) {
|
|
2681
|
+
selectFirst(body);
|
|
2624
2682
|
}
|
|
2625
2683
|
} else if (autoSelect) {
|
|
2626
2684
|
body.innerHTML = `<div class="ml-empty">Loading...</div>`;
|
|
@@ -2639,18 +2697,18 @@ async function loadMessages(accountId, folderId, page = 1, specialUse = "", auto
|
|
|
2639
2697
|
body.innerHTML = `<div class="ml-empty">${flaggedOnly ? "No flagged messages" : "No messages"}</div>`;
|
|
2640
2698
|
return;
|
|
2641
2699
|
}
|
|
2642
|
-
const preRenderUid = currentSelectedUid();
|
|
2643
2700
|
setMessages(result.items);
|
|
2644
2701
|
renderMessages(body, accountId, result.items);
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
} else {
|
|
2702
|
+
const targetUid = remembered ? pickRestoreUid(result.items, remembered.uid) : null;
|
|
2703
|
+
if (targetUid != null) {
|
|
2648
2704
|
requestAnimationFrame(() => {
|
|
2649
2705
|
if (myGen !== loadGen)
|
|
2650
2706
|
return;
|
|
2651
2707
|
body.scrollTop = savedScroll;
|
|
2652
|
-
restoreSelection(body,
|
|
2708
|
+
restoreSelection(body, String(targetUid));
|
|
2653
2709
|
});
|
|
2710
|
+
} else if (autoSelect) {
|
|
2711
|
+
selectFirst(body);
|
|
2654
2712
|
}
|
|
2655
2713
|
} catch (e) {
|
|
2656
2714
|
if (e.name === "AbortError")
|
|
@@ -2812,7 +2870,7 @@ function escapeHtml2(s) {
|
|
|
2812
2870
|
div.textContent = s;
|
|
2813
2871
|
return div.innerHTML;
|
|
2814
2872
|
}
|
|
2815
|
-
var onMessageSelect, currentAccountId2, currentFolderId, currentSpecialUse, lastClickedRow, currentPage, totalMessages, loading, unifiedMode, searchMode, currentSearchQuery, wasUnifiedBeforeSearch, showToInsteadOfFrom, touchWasScroll, currentSort, currentSortDir, loadGen, listCache, CACHE_KEY_UNIFIED, focusedRow, rowByKey, prioritySenders, priorityDomains, timeFmt, dateFmt, dateFmtSameYear, MessageRow;
|
|
2873
|
+
var onMessageSelect, currentAccountId2, currentFolderId, currentSpecialUse, lastClickedRow, currentPage, totalMessages, loading, unifiedMode, searchMode, currentSearchQuery, wasUnifiedBeforeSearch, showToInsteadOfFrom, touchWasScroll, currentSort, currentSortDir, loadGen, listCache, CACHE_KEY_UNIFIED, positionMemory, POSITION_STORAGE_KEY, focusedRow, rowByKey, prioritySenders, priorityDomains, timeFmt, dateFmt, dateFmtSameYear, MessageRow;
|
|
2816
2874
|
var init_message_list = __esm({
|
|
2817
2875
|
"client/components/message-list.js"() {
|
|
2818
2876
|
"use strict";
|
|
@@ -2835,6 +2893,19 @@ var init_message_list = __esm({
|
|
|
2835
2893
|
loadGen = 0;
|
|
2836
2894
|
listCache = /* @__PURE__ */ new Map();
|
|
2837
2895
|
CACHE_KEY_UNIFIED = "unified";
|
|
2896
|
+
positionMemory = /* @__PURE__ */ new Map();
|
|
2897
|
+
POSITION_STORAGE_KEY = "mailx-list-positions";
|
|
2898
|
+
try {
|
|
2899
|
+
const raw = sessionStorage.getItem(POSITION_STORAGE_KEY);
|
|
2900
|
+
if (raw) {
|
|
2901
|
+
const parsed = JSON.parse(raw);
|
|
2902
|
+
for (const [k, v] of Object.entries(parsed || {})) {
|
|
2903
|
+
if (typeof v?.uid === "number")
|
|
2904
|
+
positionMemory.set(k, v);
|
|
2905
|
+
}
|
|
2906
|
+
}
|
|
2907
|
+
} catch {
|
|
2908
|
+
}
|
|
2838
2909
|
focusedRow = null;
|
|
2839
2910
|
rowByKey = /* @__PURE__ */ new Map();
|
|
2840
2911
|
prioritySenders = /* @__PURE__ */ new Set();
|
|
@@ -4918,18 +4989,47 @@ function renderNode(node, container, depth) {
|
|
|
4918
4989
|
alert(`Failed: ${err.message}`);
|
|
4919
4990
|
}
|
|
4920
4991
|
}, disabled: !!node.specialUse },
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
|
|
4992
|
+
// Two delete entries. Move-to-Trash is the default; permanent
|
|
4993
|
+
// delete is a second, separated item with a tooltip so it's
|
|
4994
|
+
// visible from the menu rather than requiring a discovery
|
|
4995
|
+
// shortcut. The IMAP RENAME under Trash brings messages +
|
|
4996
|
+
// subfolders along; the server-side fallback (when Trash is
|
|
4997
|
+
// \Noinferiors) moves messages to Trash root and then deletes
|
|
4998
|
+
// the empty folder. Permanent skips Trash entirely.
|
|
4999
|
+
{
|
|
5000
|
+
label: "Move folder to Trash",
|
|
5001
|
+
action: async () => {
|
|
5002
|
+
if (!confirm(`Move folder "${node.name}" to Trash? It can be restored by dragging back out of Trash.`))
|
|
5003
|
+
return;
|
|
5004
|
+
try {
|
|
5005
|
+
await moveFolderToTrash(node.accountId, node.id);
|
|
5006
|
+
const treeContainer = document.getElementById("folder-tree");
|
|
5007
|
+
if (treeContainer)
|
|
5008
|
+
loadFolderTree(treeContainer);
|
|
5009
|
+
} catch (err) {
|
|
5010
|
+
alert(`Failed: ${err.message}`);
|
|
5011
|
+
}
|
|
5012
|
+
},
|
|
5013
|
+
disabled: !!node.specialUse,
|
|
5014
|
+
tooltip: "Renames the folder into Trash (date-suffixed if needed); use Delete permanently below to skip Trash."
|
|
5015
|
+
},
|
|
5016
|
+
{
|
|
5017
|
+
label: "Delete folder permanently",
|
|
5018
|
+
action: async () => {
|
|
5019
|
+
if (!confirm(`Permanently delete folder "${node.name}" and ALL its messages? This cannot be undone.`))
|
|
5020
|
+
return;
|
|
5021
|
+
try {
|
|
5022
|
+
await deleteFolder(node.accountId, node.id);
|
|
5023
|
+
const treeContainer = document.getElementById("folder-tree");
|
|
5024
|
+
if (treeContainer)
|
|
5025
|
+
loadFolderTree(treeContainer);
|
|
5026
|
+
} catch (err) {
|
|
5027
|
+
alert(`Failed: ${err.message}`);
|
|
5028
|
+
}
|
|
5029
|
+
},
|
|
5030
|
+
disabled: !!node.specialUse,
|
|
5031
|
+
tooltip: "Skips Trash. Same as Shift+Delete on a regular file. No undo."
|
|
5032
|
+
},
|
|
4933
5033
|
{ label: "", action: () => {
|
|
4934
5034
|
}, separator: true },
|
|
4935
5035
|
// Q57: copy IMAP path so user can paste into accounts.jsonc as
|
|
@@ -6435,7 +6535,7 @@ function addComposeResizeHandles(wrapper, frame) {
|
|
|
6435
6535
|
function sanitizeQuotedBody(msg) {
|
|
6436
6536
|
const isPlainText = !msg.bodyHtml;
|
|
6437
6537
|
if (isPlainText) {
|
|
6438
|
-
const escaped = String(msg.bodyText || "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
6538
|
+
const escaped = String(msg.bodyText || "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/\r\n?/g, "\n").replace(/\n/g, "<br>");
|
|
6439
6539
|
return `<div style="white-space:pre-wrap;font-family:inherit;margin:0">${escaped}</div>`;
|
|
6440
6540
|
}
|
|
6441
6541
|
let body = msg.bodyHtml;
|