@bobfrankston/rmfmail 1.2.96 → 1.2.97
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 +110 -19
- package/client/app.bundle.js.map +3 -3
- package/client/components/message-viewer.js +132 -25
- package/client/components/message-viewer.js.map +1 -1
- package/client/components/message-viewer.ts +116 -23
- package/client/compose/compose.bundle.js +4 -0
- package/client/compose/compose.bundle.js.map +2 -2
- package/client/lib/api-client.js +4 -0
- package/client/lib/api-client.js.map +1 -1
- package/client/lib/api-client.ts +5 -0
- package/client/lib/mailxapi.js +3 -0
- package/package.json +1 -1
- package/packages/mailx-service/index.d.ts +7 -0
- package/packages/mailx-service/index.d.ts.map +1 -1
- package/packages/mailx-service/index.js +40 -0
- package/packages/mailx-service/index.js.map +1 -1
- package/packages/mailx-service/index.ts +31 -0
- package/packages/mailx-service/jsonrpc.js +2 -0
- package/packages/mailx-service/jsonrpc.js.map +1 -1
- package/packages/mailx-service/jsonrpc.ts +2 -0
package/client/app.bundle.js
CHANGED
|
@@ -55,6 +55,7 @@ __export(api_client_exports, {
|
|
|
55
55
|
getDiagnostics: () => getDiagnostics,
|
|
56
56
|
getFolders: () => getFolders,
|
|
57
57
|
getMessage: () => getMessage,
|
|
58
|
+
getMessageSource: () => getMessageSource,
|
|
58
59
|
getMessages: () => getMessages,
|
|
59
60
|
getOutboxStatus: () => getOutboxStatus,
|
|
60
61
|
getPrimaryAccount: () => getPrimaryAccount,
|
|
@@ -617,6 +618,9 @@ async function openAttachment(accountId, uid, attachmentId, folderId, filename)
|
|
|
617
618
|
const fn = ipc().openAttachment;
|
|
618
619
|
return fn ? fn(accountId, uid, attachmentId, folderId, filename) : void 0;
|
|
619
620
|
}
|
|
621
|
+
async function getMessageSource(accountId, uid, folderId) {
|
|
622
|
+
return ipc().getMessageSource(accountId, uid, folderId);
|
|
623
|
+
}
|
|
620
624
|
async function getDeviceAccounts() {
|
|
621
625
|
return ipc().getDeviceAccounts?.() ?? [];
|
|
622
626
|
}
|
|
@@ -1311,7 +1315,13 @@ function showPreviewBodyMenu(absX, absY, selectedText, sourceWindow, linkUrl, li
|
|
|
1311
1315
|
} },
|
|
1312
1316
|
{ label: "", action: () => {
|
|
1313
1317
|
}, separator: true },
|
|
1314
|
-
{ label: "Print\u2026 (Ctrl+P)", action: () => printCurrentMessage() }
|
|
1318
|
+
{ label: "Print\u2026 (Ctrl+P)", action: () => printCurrentMessage() },
|
|
1319
|
+
{ label: "Save message (.eml)\u2026", action: () => {
|
|
1320
|
+
void saveCurrentMessageAsEml();
|
|
1321
|
+
} },
|
|
1322
|
+
{ label: "Save message as HTML\u2026", action: () => {
|
|
1323
|
+
void saveCurrentMessageAsHtml();
|
|
1324
|
+
} }
|
|
1315
1325
|
);
|
|
1316
1326
|
showContextMenu(absX, absY, items);
|
|
1317
1327
|
}
|
|
@@ -2060,6 +2070,29 @@ async function showMessage(accountId, uid, folderId, specialUse, isRetry = false
|
|
|
2060
2070
|
attEl.hidden = true;
|
|
2061
2071
|
if (msg.attachments?.length) {
|
|
2062
2072
|
attEl.hidden = false;
|
|
2073
|
+
for (const u of dragOutUrls) {
|
|
2074
|
+
try {
|
|
2075
|
+
URL.revokeObjectURL(u);
|
|
2076
|
+
} catch {
|
|
2077
|
+
}
|
|
2078
|
+
}
|
|
2079
|
+
dragOutUrls.length = 0;
|
|
2080
|
+
const dragCache = /* @__PURE__ */ new Map();
|
|
2081
|
+
void (async () => {
|
|
2082
|
+
for (const a1 of msg.attachments) {
|
|
2083
|
+
if ((a1.size || 0) > 50 * 1024 * 1024)
|
|
2084
|
+
continue;
|
|
2085
|
+
try {
|
|
2086
|
+
const data = await getAttachment(accountId, uid, a1.id, msg.folderId);
|
|
2087
|
+
const bytes = Uint8Array.from(atob(data.content), (c) => c.charCodeAt(0));
|
|
2088
|
+
const blob = new Blob([bytes], { type: data.contentType || "application/octet-stream" });
|
|
2089
|
+
const url = URL.createObjectURL(blob);
|
|
2090
|
+
dragOutUrls.push(url);
|
|
2091
|
+
dragCache.set(a1.id, { url, contentType: data.contentType || "application/octet-stream" });
|
|
2092
|
+
} catch {
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2095
|
+
})();
|
|
2063
2096
|
const saveAttachmentAt = async (idx) => {
|
|
2064
2097
|
const a0 = msg.attachments[idx];
|
|
2065
2098
|
const name = a0.filename || "attachment";
|
|
@@ -2158,21 +2191,20 @@ async function showMessage(accountId, uid, folderId, specialUse, isRetry = false
|
|
|
2158
2191
|
}
|
|
2159
2192
|
});
|
|
2160
2193
|
chip.draggable = true;
|
|
2161
|
-
chip.addEventListener("dragstart",
|
|
2194
|
+
chip.addEventListener("dragstart", (e) => {
|
|
2162
2195
|
if (!e.dataTransfer)
|
|
2163
2196
|
return;
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
const
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
e.dataTransfer.setData("DownloadURL", downloadUrl);
|
|
2172
|
-
e.dataTransfer.effectAllowed = "copy";
|
|
2173
|
-
} catch (err) {
|
|
2174
|
-
console.error(`Attachment drag-out failed: ${err.message || err}`);
|
|
2197
|
+
const cached2 = dragCache.get(att.id);
|
|
2198
|
+
if (!cached2) {
|
|
2199
|
+
e.preventDefault();
|
|
2200
|
+
const statusEl = document.getElementById("status-sync");
|
|
2201
|
+
if (statusEl)
|
|
2202
|
+
statusEl.textContent = "Attachment still loading \u2014 try dragging again in a moment.";
|
|
2203
|
+
return;
|
|
2175
2204
|
}
|
|
2205
|
+
const safeName = (att.filename || "attachment").replace(/[\r\n"\/\\]/g, "_");
|
|
2206
|
+
e.dataTransfer.setData("DownloadURL", `${cached2.contentType}:${safeName}:${cached2.url}`);
|
|
2207
|
+
e.dataTransfer.effectAllowed = "copy";
|
|
2176
2208
|
});
|
|
2177
2209
|
chip.addEventListener("contextmenu", (e) => {
|
|
2178
2210
|
e.preventDefault();
|
|
@@ -2663,16 +2695,22 @@ function popOutToWindow() {
|
|
|
2663
2695
|
}
|
|
2664
2696
|
}, 600);
|
|
2665
2697
|
}
|
|
2666
|
-
function
|
|
2667
|
-
if (!
|
|
2668
|
-
return;
|
|
2669
|
-
const m = currentMessage;
|
|
2698
|
+
function buildPrintableDoc(m) {
|
|
2699
|
+
if (!m)
|
|
2700
|
+
return null;
|
|
2670
2701
|
const fmt = (a) => a?.name ? `${a.name} <${a.address || ""}>` : a?.address || "";
|
|
2671
2702
|
const esc = escapeHtmlLocal;
|
|
2672
2703
|
const row = (label, value) => value ? `<div><strong>${label}:</strong> ${esc(value)}</div>` : "";
|
|
2673
2704
|
const headerHtml = `<div style="font-family:system-ui,Segoe UI,sans-serif;font-size:11pt;border-bottom:1px solid #999;padding-bottom:8px;margin-bottom:14px;line-height:1.5;">` + row("From", fmt(m.from || {})) + row("To", (m.to || []).map(fmt).join(", ")) + (m.cc?.length ? row("Cc", m.cc.map(fmt).join(", ")) : "") + row("Subject", m.subject || "") + row("Date", m.date ? new Date(m.date).toLocaleString() : "") + `</div>`;
|
|
2674
2705
|
const bodyHtml = m.bodyHtml ? m.bodyHtml : `<pre style="white-space:pre-wrap;word-break:break-word;font-family:system-ui,sans-serif;">${esc(m.bodyText || "")}</pre>`;
|
|
2675
|
-
|
|
2706
|
+
return `<!DOCTYPE html><html><head><meta charset="utf-8"><title>${esc(m.subject || "Message")}</title><style>@media print{body{margin:0;}}body{margin:16px;}img{max-width:100%;}</style></head><body>${headerHtml}${bodyHtml}</body></html>`;
|
|
2707
|
+
}
|
|
2708
|
+
function printCurrentMessage() {
|
|
2709
|
+
if (!currentMessage)
|
|
2710
|
+
return;
|
|
2711
|
+
const doc = buildPrintableDoc(currentMessage);
|
|
2712
|
+
if (!doc)
|
|
2713
|
+
return;
|
|
2676
2714
|
const iframe = document.createElement("iframe");
|
|
2677
2715
|
iframe.style.cssText = "position:fixed;left:-9999px;width:1px;height:1px;border:0;";
|
|
2678
2716
|
iframe.srcdoc = doc;
|
|
@@ -2701,6 +2739,58 @@ function printCurrentMessage() {
|
|
|
2701
2739
|
}, { once: true });
|
|
2702
2740
|
document.body.appendChild(iframe);
|
|
2703
2741
|
}
|
|
2742
|
+
async function saveBlobAs(suggestedName, blob) {
|
|
2743
|
+
const picker = window.showSaveFilePicker;
|
|
2744
|
+
if (typeof picker === "function") {
|
|
2745
|
+
let handle;
|
|
2746
|
+
try {
|
|
2747
|
+
handle = await picker({ suggestedName });
|
|
2748
|
+
} catch (e) {
|
|
2749
|
+
if (e?.name === "AbortError")
|
|
2750
|
+
return;
|
|
2751
|
+
throw e;
|
|
2752
|
+
}
|
|
2753
|
+
const writable = await handle.createWritable();
|
|
2754
|
+
await writable.write(blob);
|
|
2755
|
+
await writable.close();
|
|
2756
|
+
return;
|
|
2757
|
+
}
|
|
2758
|
+
const url = URL.createObjectURL(blob);
|
|
2759
|
+
const a = document.createElement("a");
|
|
2760
|
+
a.href = url;
|
|
2761
|
+
a.download = suggestedName;
|
|
2762
|
+
a.style.display = "none";
|
|
2763
|
+
document.body.appendChild(a);
|
|
2764
|
+
a.click();
|
|
2765
|
+
setTimeout(() => {
|
|
2766
|
+
a.remove();
|
|
2767
|
+
URL.revokeObjectURL(url);
|
|
2768
|
+
}, 5e3);
|
|
2769
|
+
}
|
|
2770
|
+
async function saveCurrentMessageAsEml() {
|
|
2771
|
+
if (!currentMessage)
|
|
2772
|
+
return;
|
|
2773
|
+
try {
|
|
2774
|
+
const src = await getMessageSource(currentAccountId, currentMessage.uid, currentMessage.folderId);
|
|
2775
|
+
const bytes = Uint8Array.from(atob(src.dataBase64), (c) => c.charCodeAt(0));
|
|
2776
|
+
await saveBlobAs(src.filename || "message.eml", new Blob([bytes], { type: "message/rfc822" }));
|
|
2777
|
+
} catch (e) {
|
|
2778
|
+
window.dispatchEvent(new CustomEvent("mailx-alert", { detail: { message: `Couldn't save message: ${e?.message || e}`, key: "message-save" } }));
|
|
2779
|
+
}
|
|
2780
|
+
}
|
|
2781
|
+
async function saveCurrentMessageAsHtml() {
|
|
2782
|
+
if (!currentMessage)
|
|
2783
|
+
return;
|
|
2784
|
+
const doc = buildPrintableDoc(currentMessage);
|
|
2785
|
+
if (!doc)
|
|
2786
|
+
return;
|
|
2787
|
+
const base = (currentMessage.subject || "message").replace(/[<>:"/\\|?*\r\n]/g, "_").slice(0, 80).trim() || "message";
|
|
2788
|
+
try {
|
|
2789
|
+
await saveBlobAs(`${base}.html`, new Blob([doc], { type: "text/html" }));
|
|
2790
|
+
} catch (e) {
|
|
2791
|
+
window.dispatchEvent(new CustomEvent("mailx-alert", { detail: { message: `Couldn't save message: ${e?.message || e}`, key: "message-save" } }));
|
|
2792
|
+
}
|
|
2793
|
+
}
|
|
2704
2794
|
function spawnDesktopPopout(msg, accountId) {
|
|
2705
2795
|
const wrapper = document.createElement("div");
|
|
2706
2796
|
wrapper.className = "compose-overlay viewer-popout";
|
|
@@ -2817,7 +2907,7 @@ function spawnDesktopPopout(msg, accountId) {
|
|
|
2817
2907
|
function escapeHtmlLocal(s) {
|
|
2818
2908
|
return (s || "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
2819
2909
|
}
|
|
2820
|
-
var currentMessage, currentAccountId, showMessageGeneration, retryCount, lastEnvelope, PARSED_CACHE_LIMIT, parsedCache, sessionAllowedRemote, recentFetchErrors, ZOOM_KEY, ZOOM_MIN, ZOOM_MAX, ZOOM_STEP, previewZoom;
|
|
2910
|
+
var currentMessage, currentAccountId, dragOutUrls, showMessageGeneration, retryCount, lastEnvelope, PARSED_CACHE_LIMIT, parsedCache, sessionAllowedRemote, recentFetchErrors, ZOOM_KEY, ZOOM_MIN, ZOOM_MAX, ZOOM_STEP, previewZoom;
|
|
2821
2911
|
var init_message_viewer = __esm({
|
|
2822
2912
|
"client/components/message-viewer.js"() {
|
|
2823
2913
|
"use strict";
|
|
@@ -2828,6 +2918,7 @@ var init_message_viewer = __esm({
|
|
|
2828
2918
|
init_mailx_types();
|
|
2829
2919
|
currentMessage = null;
|
|
2830
2920
|
currentAccountId = "";
|
|
2921
|
+
dragOutUrls = [];
|
|
2831
2922
|
showMessageGeneration = 0;
|
|
2832
2923
|
retryCount = 0;
|
|
2833
2924
|
lastEnvelope = null;
|