@bobfrankston/rmfmail 1.2.331 → 1.2.335
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/android-bootstrap.bundle.js +60 -2
- package/client/android-bootstrap.bundle.js.map +2 -2
- package/client/app.bundle.js +2 -1
- package/client/app.bundle.js.map +2 -2
- package/client/components/message-viewer.js +6 -1
- package/client/components/message-viewer.js.map +1 -1
- package/client/components/message-viewer.ts +6 -1
- package/client/compose/compose.bundle.js +90 -10
- package/client/compose/compose.bundle.js.map +4 -4
- package/client/compose/compose.js +49 -5
- package/client/compose/compose.js.map +1 -1
- package/client/compose/compose.ts +47 -5
- package/client/lib/rmf-tiny.js +34 -4
- package/client/package.json +1 -1
- package/package.json +5 -5
- package/packages/mailx-imap/package-lock.json +2 -2
- package/packages/mailx-imap/package.json +1 -1
- package/packages/mailx-service/index.d.ts.map +1 -1
- package/packages/mailx-service/index.js +7 -1
- package/packages/mailx-service/index.js.map +1 -1
- package/packages/mailx-service/index.ts +7 -1
- package/packages/mailx-service/package.json +1 -1
- package/packages/mailx-service/sync-queue.d.ts +7 -0
- package/packages/mailx-service/sync-queue.d.ts.map +1 -1
- package/packages/mailx-service/sync-queue.js +9 -0
- package/packages/mailx-service/sync-queue.js.map +1 -1
- package/packages/mailx-service/sync-queue.ts +10 -0
- package/packages/mailx-settings/package.json +1 -1
- package/packages/mailx-store/package.json +1 -1
- package/packages/mailx-store-web/package.json +1 -1
- package/packages/mailx-types/index.d.ts.map +1 -1
- package/packages/mailx-types/index.js +14 -0
- package/packages/mailx-types/index.js.map +1 -1
- package/packages/mailx-types/index.ts +13 -0
- package/packages/mailx-types/package.json +1 -1
|
@@ -3022,14 +3022,34 @@ async function createTinyMceEditor(container2, opts = {}) {
|
|
|
3022
3022
|
};
|
|
3023
3023
|
reader.readAsDataURL(file);
|
|
3024
3024
|
});
|
|
3025
|
+
const restoreBlobSrcs = (html) => html.replace(/src="(blob:[^"]+)"/g, (m, uri) => {
|
|
3026
|
+
const info = ed.editorUpload?.blobCache?.getByUri?.(uri);
|
|
3027
|
+
return info ? `src="data:${info.blob().type};base64,${info.base64()}"` : m;
|
|
3028
|
+
});
|
|
3025
3029
|
ed.on("GetContent", (e) => {
|
|
3026
3030
|
if (!e?.content || typeof e.content !== "string" || !e.content.includes("blob:"))
|
|
3027
3031
|
return;
|
|
3028
|
-
e.content = e.content
|
|
3029
|
-
const info = ed.editorUpload?.blobCache?.getByUri?.(uri);
|
|
3030
|
-
return info ? `src="data:${info.blob().type};base64,${info.base64()}"` : m;
|
|
3031
|
-
});
|
|
3032
|
+
e.content = restoreBlobSrcs(e.content);
|
|
3032
3033
|
});
|
|
3034
|
+
const copyWithImageData = (e) => {
|
|
3035
|
+
if (!e?.clipboardData)
|
|
3036
|
+
return;
|
|
3037
|
+
let html = "";
|
|
3038
|
+
try {
|
|
3039
|
+
html = ed.selection.getContent({ format: "html" }) || "";
|
|
3040
|
+
} catch {
|
|
3041
|
+
return;
|
|
3042
|
+
}
|
|
3043
|
+
if (!html.includes("blob:"))
|
|
3044
|
+
return;
|
|
3045
|
+
e.preventDefault();
|
|
3046
|
+
e.clipboardData.setData("text/html", restoreBlobSrcs(html));
|
|
3047
|
+
e.clipboardData.setData("text/plain", ed.selection.getContent({ format: "text" }) || "");
|
|
3048
|
+
if (e.type === "cut")
|
|
3049
|
+
ed.execCommand("Delete");
|
|
3050
|
+
};
|
|
3051
|
+
ed.on("copy", copyWithImageData);
|
|
3052
|
+
ed.on("cut", copyWithImageData);
|
|
3033
3053
|
ed.on("OpenWindow", () => {
|
|
3034
3054
|
requestAnimationFrame(() => requestAnimationFrame(() => {
|
|
3035
3055
|
const ta = document.querySelector(".tox-dialog textarea");
|
|
@@ -5367,6 +5387,43 @@ function keepTypingOutsideLinks(native) {
|
|
|
5367
5387
|
// client/compose/compose.ts
|
|
5368
5388
|
init_api_client();
|
|
5369
5389
|
init_edit_commands();
|
|
5390
|
+
|
|
5391
|
+
// packages/mailx-types/log-changes.js
|
|
5392
|
+
var RESTATE_AFTER_MS = 60 * 60 * 1e3;
|
|
5393
|
+
|
|
5394
|
+
// packages/mailx-types/reminder-state.js
|
|
5395
|
+
var DISMISSED_RETENTION_MS = 30 * 864e5;
|
|
5396
|
+
var SNOOZED_RETENTION_MS = 7 * 864e5;
|
|
5397
|
+
|
|
5398
|
+
// packages/mailx-types/index.js
|
|
5399
|
+
function htmlToPlainText(html) {
|
|
5400
|
+
if (!html)
|
|
5401
|
+
return "";
|
|
5402
|
+
let s = html;
|
|
5403
|
+
s = s.replace(/<style\b[^>]*>[\s\S]*?<\/style>/gi, "");
|
|
5404
|
+
s = s.replace(/<script\b[^>]*>[\s\S]*?<\/script>/gi, "");
|
|
5405
|
+
s = s.replace(/<br\s*\/?\s*>/gi, "\n");
|
|
5406
|
+
s = s.replace(/<\/(p|div|li|tr|h[1-6]|blockquote|pre|section|article)\s*>/gi, "\n");
|
|
5407
|
+
s = s.replace(/<li\b[^>]*>/gi, " \u2022 ");
|
|
5408
|
+
s = s.replace(/<a\b[^>]*href\s*=\s*(['"])([^'"]*)\1[^>]*>([\s\S]*?)<\/a>/gi, (_m, _q, href, text) => {
|
|
5409
|
+
const t = text.replace(/<[^>]+>/g, "").trim();
|
|
5410
|
+
return t && t !== href ? `${t} (${href})` : href;
|
|
5411
|
+
});
|
|
5412
|
+
s = s.replace(/<img\b[^>]*>/gi, (tag) => {
|
|
5413
|
+
const src = (tag.match(/\bsrc\s*=\s*(['"])([^'"]*)\1/i) || [])[2]?.trim() || "";
|
|
5414
|
+
const alt = (tag.match(/\balt\s*=\s*(['"])([^'"]*)\1/i) || [])[2]?.trim() || "";
|
|
5415
|
+
const remote = /^https?:\/\//i.test(src);
|
|
5416
|
+
if (remote)
|
|
5417
|
+
return alt ? `[image: ${alt}] (${src})` : `(${src})`;
|
|
5418
|
+
return alt ? `[image: ${alt}]` : "[image]";
|
|
5419
|
+
});
|
|
5420
|
+
s = s.replace(/<[^>]+>/g, "");
|
|
5421
|
+
s = s.replace(/ /gi, " ").replace(/&/gi, "&").replace(/</gi, "<").replace(/>/gi, ">").replace(/"/gi, '"').replace(/'/gi, "'").replace(/'/gi, "'").replace(/—/gi, "\u2014").replace(/–/gi, "\u2013").replace(/…/gi, "\u2026").replace(/&#(\d+);/g, (_m, n) => String.fromCodePoint(parseInt(n, 10))).replace(/&#x([0-9a-f]+);/gi, (_m, h) => String.fromCodePoint(parseInt(h, 16)));
|
|
5422
|
+
s = s.replace(/[ \t]+/g, " ").split("\n").map((l) => l.replace(/^[ \t]+|[ \t]+$/g, "")).join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
5423
|
+
return s;
|
|
5424
|
+
}
|
|
5425
|
+
|
|
5426
|
+
// client/compose/compose.ts
|
|
5370
5427
|
init_context_menu();
|
|
5371
5428
|
installConsoleCapture();
|
|
5372
5429
|
connectEvents();
|
|
@@ -5560,6 +5617,7 @@ async function tryEditor(type) {
|
|
|
5560
5617
|
}
|
|
5561
5618
|
} catch {
|
|
5562
5619
|
}
|
|
5620
|
+
if (ed) wirePasteImageInlining(ed, type);
|
|
5563
5621
|
return ed;
|
|
5564
5622
|
} catch (e) {
|
|
5565
5623
|
logClientEvent("compose-editor-create-failed", { type, error: String(e?.message || e) });
|
|
@@ -5943,7 +6001,7 @@ function setupAutocomplete(input) {
|
|
|
5943
6001
|
before = before.replace(/[ \t,]+$/, "");
|
|
5944
6002
|
if (contact) {
|
|
5945
6003
|
const haystack = `${contact.name} ${contact.email}`.toLowerCase();
|
|
5946
|
-
const stripStranded = (s) =>
|
|
6004
|
+
const stripStranded = (s) => splitRecipients2(s).map((p) => p.trim()).filter((p) => p.length > 0 && (p.includes("@") || !haystack.includes(p.toLowerCase()))).join(", ");
|
|
5947
6005
|
if (before) before = stripStranded(before);
|
|
5948
6006
|
const afterCore = after.replace(/^[\s,]+/, "").replace(/[\s,]+$/, "");
|
|
5949
6007
|
if (afterCore) {
|
|
@@ -6097,7 +6155,7 @@ for (const field of [toInput, ccInput, bccInput, subjectInput]) {
|
|
|
6097
6155
|
smartTab(field);
|
|
6098
6156
|
});
|
|
6099
6157
|
}
|
|
6100
|
-
function
|
|
6158
|
+
function splitRecipients2(s) {
|
|
6101
6159
|
const raw = [];
|
|
6102
6160
|
let cur = "", inQuote = false;
|
|
6103
6161
|
for (const c of s) {
|
|
@@ -6145,7 +6203,7 @@ function formatAddrs(addrs) {
|
|
|
6145
6203
|
}
|
|
6146
6204
|
function parseAddrs(s) {
|
|
6147
6205
|
if (!s.trim()) return [];
|
|
6148
|
-
return
|
|
6206
|
+
return splitRecipients2(s).map((p) => p.trim()).filter((p) => p.length > 0).map((part) => {
|
|
6149
6207
|
const match = part.match(/^(.+?)\s*<(.+?)>$/);
|
|
6150
6208
|
if (match) {
|
|
6151
6209
|
let name = match[1].trim();
|
|
@@ -6457,7 +6515,7 @@ async function saveDraft2() {
|
|
|
6457
6515
|
from: getFromAddress(),
|
|
6458
6516
|
subject: subjectInput.value,
|
|
6459
6517
|
bodyHtml: editor.getHtml(),
|
|
6460
|
-
bodyText: editor.
|
|
6518
|
+
bodyText: htmlToPlainText(editor.getHtml()),
|
|
6461
6519
|
to: toInput.value,
|
|
6462
6520
|
cc: ccInput.value,
|
|
6463
6521
|
bcc: bccInput.value,
|
|
@@ -6495,6 +6553,29 @@ function editorBodyEl() {
|
|
|
6495
6553
|
return document.querySelector(".ql-editor") || document.querySelector(".tt-content .tiptap");
|
|
6496
6554
|
}
|
|
6497
6555
|
var INLINE_CHECKED_ATTR = "data-mailx-inline-checked";
|
|
6556
|
+
function wirePasteImageInlining(ed, type) {
|
|
6557
|
+
const onPaste = () => {
|
|
6558
|
+
const body2 = editorBodyEl();
|
|
6559
|
+
if (!body2) return;
|
|
6560
|
+
for (const img of Array.from(body2.querySelectorAll(`img:not([${INLINE_CHECKED_ATTR}])`))) {
|
|
6561
|
+
img.setAttribute(INLINE_CHECKED_ATTR, "1");
|
|
6562
|
+
}
|
|
6563
|
+
setTimeout(() => {
|
|
6564
|
+
void inlinePastedRemoteImages();
|
|
6565
|
+
}, 50);
|
|
6566
|
+
setTimeout(() => {
|
|
6567
|
+
void inlinePastedRemoteImages();
|
|
6568
|
+
}, 800);
|
|
6569
|
+
};
|
|
6570
|
+
const native = ed?.nativeEditor;
|
|
6571
|
+
if (type === "tinymce" && native?.on) {
|
|
6572
|
+
native.on("paste", onPaste);
|
|
6573
|
+
return;
|
|
6574
|
+
}
|
|
6575
|
+
const body = editorBodyEl();
|
|
6576
|
+
if (body) body.addEventListener("paste", onPaste);
|
|
6577
|
+
else console.error("[compose] paste-image inlining not wired: no editor body");
|
|
6578
|
+
}
|
|
6498
6579
|
async function inlinePastedRemoteImages() {
|
|
6499
6580
|
const body = editorBodyEl();
|
|
6500
6581
|
if (!body) return;
|
|
@@ -6516,7 +6597,6 @@ async function inlinePastedRemoteImages() {
|
|
|
6516
6597
|
}
|
|
6517
6598
|
function scheduleDraftSave() {
|
|
6518
6599
|
markComposeDirty();
|
|
6519
|
-
void inlinePastedRemoteImages();
|
|
6520
6600
|
if (draftDebounceTimer) clearTimeout(draftDebounceTimer);
|
|
6521
6601
|
draftDebounceTimer = setTimeout(() => {
|
|
6522
6602
|
draftDebounceTimer = null;
|
|
@@ -6706,7 +6786,7 @@ document.getElementById("btn-send")?.addEventListener("click", async () => {
|
|
|
6706
6786
|
bcc: parseAddrs(bccExpanded),
|
|
6707
6787
|
subject: subjectInput.value,
|
|
6708
6788
|
bodyHtml: editor.getHtml(),
|
|
6709
|
-
bodyText: editor.
|
|
6789
|
+
bodyText: htmlToPlainText(editor.getHtml()),
|
|
6710
6790
|
attachments: attachments.map((a) => ({ filename: a.filename, mimeType: a.mimeType, dataBase64: a.dataBase64 })),
|
|
6711
6791
|
// Threading headers — daemon writes In-Reply-To and References into
|
|
6712
6792
|
// the outgoing MIME from these. Without them every reply lands as a
|