@bobfrankston/rmfmail 1.2.272 → 1.2.276
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/.commitmsg +26 -0
- package/client/android-bootstrap.bundle.js +184 -0
- package/client/android-bootstrap.bundle.js.map +2 -2
- package/client/app.bundle.js +235 -31
- package/client/app.bundle.js.map +4 -4
- package/client/app.js +35 -1
- package/client/app.js.map +1 -1
- package/client/app.ts +36 -1
- package/client/components/invite-card.js +107 -0
- package/client/components/invite-card.js.map +1 -0
- package/client/components/invite-card.ts +119 -0
- package/client/components/message-list.js +9 -1
- package/client/components/message-list.js.map +1 -1
- package/client/components/message-list.ts +9 -1
- package/client/components/message-viewer.js +82 -19
- package/client/components/message-viewer.js.map +1 -1
- package/client/components/message-viewer.ts +79 -18
- package/client/compose/compose.bundle.js +4 -0
- package/client/compose/compose.bundle.js.map +2 -2
- package/client/lib/api-client.js +6 -0
- package/client/lib/api-client.js.map +1 -1
- package/client/lib/api-client.ts +6 -0
- package/client/lib/approved-senders.js +66 -0
- package/client/lib/approved-senders.js.map +1 -0
- package/client/lib/approved-senders.ts +63 -0
- package/client/lib/mailxapi.js +3 -0
- package/client/package.json +1 -1
- package/client/styles/components.css +48 -0
- package/package.json +10 -10
- package/packages/mailx-imap/package-lock.json +2 -2
- package/packages/mailx-imap/package.json +2 -2
- package/packages/mailx-service/index.d.ts +15 -0
- package/packages/mailx-service/index.d.ts.map +1 -1
- package/packages/mailx-service/index.js +18 -0
- package/packages/mailx-service/index.js.map +1 -1
- package/packages/mailx-service/index.ts +19 -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/packages/mailx-service/package.json +1 -1
- package/packages/mailx-settings/package.json +1 -1
- package/packages/mailx-store/package.json +1 -1
- package/packages/mailx-store/store.d.ts +5 -0
- package/packages/mailx-store/store.d.ts.map +1 -1
- package/packages/mailx-store/store.js +21 -0
- package/packages/mailx-store/store.js.map +1 -1
- package/packages/mailx-store/store.ts +27 -0
- package/packages/mailx-store-web/android-bootstrap.js +1 -0
- package/packages/mailx-store-web/android-bootstrap.js.map +1 -1
- package/packages/mailx-store-web/android-bootstrap.ts +1 -0
- package/packages/mailx-store-web/package.json +1 -1
- package/packages/mailx-store-web/web-service.d.ts +11 -0
- package/packages/mailx-store-web/web-service.d.ts.map +1 -1
- package/packages/mailx-store-web/web-service.js +14 -0
- package/packages/mailx-store-web/web-service.js.map +1 -1
- package/packages/mailx-store-web/web-service.ts +15 -0
- package/packages/mailx-types/index.d.ts +56 -0
- package/packages/mailx-types/index.d.ts.map +1 -1
- package/packages/mailx-types/index.js +187 -0
- package/packages/mailx-types/index.js.map +1 -1
- package/packages/mailx-types/index.ts +192 -0
- package/packages/mailx-types/package.json +1 -1
- /package/packages/mailx-imap/{node_modules.npmglobalize-stash-72180 → node_modules.npmglobalize-stash-59216}/.package-lock.json +0 -0
package/client/app.bundle.js
CHANGED
|
@@ -56,6 +56,7 @@ __export(api_client_exports, {
|
|
|
56
56
|
flagSenderOrDomain: () => flagSenderOrDomain,
|
|
57
57
|
formatJsonc: () => formatJsonc,
|
|
58
58
|
getAccounts: () => getAccounts,
|
|
59
|
+
getAllowlist: () => getAllowlist,
|
|
59
60
|
getAttachment: () => getAttachment,
|
|
60
61
|
getAutocompleteSettings: () => getAutocompleteSettings,
|
|
61
62
|
getCalendarEvents: () => getCalendarEvents,
|
|
@@ -350,6 +351,9 @@ function addUserDictWords(words) {
|
|
|
350
351
|
function removeUserDictWord(word) {
|
|
351
352
|
return ipc().removeUserDictWord?.(word) ?? Promise.resolve([]);
|
|
352
353
|
}
|
|
354
|
+
function getAllowlist() {
|
|
355
|
+
return ipc().getAllowlist?.() ?? Promise.resolve({ senders: [], domains: [], recipients: [], flaggedSenders: [], flaggedDomains: [] });
|
|
356
|
+
}
|
|
353
357
|
function flagSenderOrDomain(type, value) {
|
|
354
358
|
return ipc().flagSenderOrDomain?.(type, value) ?? Promise.resolve({ flagged: false });
|
|
355
359
|
}
|
|
@@ -1310,6 +1314,61 @@ var init_message_state = __esm({
|
|
|
1310
1314
|
}
|
|
1311
1315
|
});
|
|
1312
1316
|
|
|
1317
|
+
// client/lib/approved-senders.js
|
|
1318
|
+
var approved_senders_exports = {};
|
|
1319
|
+
__export(approved_senders_exports, {
|
|
1320
|
+
approvedCounts: () => approvedCounts,
|
|
1321
|
+
isSenderApproved: () => isSenderApproved,
|
|
1322
|
+
noteApprovedLocally: () => noteApprovedLocally,
|
|
1323
|
+
refreshApprovedSenders: () => refreshApprovedSenders
|
|
1324
|
+
});
|
|
1325
|
+
async function refreshApprovedSenders() {
|
|
1326
|
+
try {
|
|
1327
|
+
const list = await getAllowlist();
|
|
1328
|
+
senders = new Set((list.senders || []).map(norm));
|
|
1329
|
+
domains = new Set((list.domains || []).map(norm));
|
|
1330
|
+
loaded = true;
|
|
1331
|
+
} catch (e) {
|
|
1332
|
+
console.warn("[approved-senders] refresh failed:", e?.message || e);
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
function isSenderApproved(address) {
|
|
1336
|
+
if (!loaded)
|
|
1337
|
+
return false;
|
|
1338
|
+
const a = norm(address);
|
|
1339
|
+
if (!a)
|
|
1340
|
+
return false;
|
|
1341
|
+
if (senders.has(a))
|
|
1342
|
+
return true;
|
|
1343
|
+
const d = domainOf(a);
|
|
1344
|
+
return !!d && domains.has(d);
|
|
1345
|
+
}
|
|
1346
|
+
function noteApprovedLocally(type, value) {
|
|
1347
|
+
const v = norm(value);
|
|
1348
|
+
if (!v)
|
|
1349
|
+
return;
|
|
1350
|
+
if (type === "sender")
|
|
1351
|
+
senders.add(v);
|
|
1352
|
+
else
|
|
1353
|
+
domains.add(v);
|
|
1354
|
+
loaded = true;
|
|
1355
|
+
}
|
|
1356
|
+
function approvedCounts() {
|
|
1357
|
+
return { senders: senders.size, domains: domains.size, loaded };
|
|
1358
|
+
}
|
|
1359
|
+
var senders, domains, loaded, norm, domainOf;
|
|
1360
|
+
var init_approved_senders = __esm({
|
|
1361
|
+
"client/lib/approved-senders.js"() {
|
|
1362
|
+
"use strict";
|
|
1363
|
+
init_api_client();
|
|
1364
|
+
senders = /* @__PURE__ */ new Set();
|
|
1365
|
+
domains = /* @__PURE__ */ new Set();
|
|
1366
|
+
loaded = false;
|
|
1367
|
+
norm = (s) => (s || "").trim().toLowerCase();
|
|
1368
|
+
domainOf = (addr) => norm(addr).split("@").pop() || "";
|
|
1369
|
+
}
|
|
1370
|
+
});
|
|
1371
|
+
|
|
1313
1372
|
// packages/mailx-types/contact-rules.js
|
|
1314
1373
|
var init_contact_rules = __esm({
|
|
1315
1374
|
"packages/mailx-types/contact-rules.js"() {
|
|
@@ -1408,6 +1467,11 @@ function formatSender(addr) {
|
|
|
1408
1467
|
return { text: address, claimed: null };
|
|
1409
1468
|
return { text: `${name} <${address}>`, claimed: displayNameClaimsOtherAddress(name, address) };
|
|
1410
1469
|
}
|
|
1470
|
+
function inviteHasGoogleRsvp(bodyHtml) {
|
|
1471
|
+
if (!bodyHtml)
|
|
1472
|
+
return false;
|
|
1473
|
+
return /calendar\.google\.com\/calendar\/event\?action=RESPOND/i.test(bodyHtml);
|
|
1474
|
+
}
|
|
1411
1475
|
var _SEEN, _FLAGGED, _DRAFT, seenOf, flaggedOf, draftOf, setSeen, setFlagged;
|
|
1412
1476
|
var init_mailx_types = __esm({
|
|
1413
1477
|
"packages/mailx-types/index.js"() {
|
|
@@ -1429,6 +1493,100 @@ var init_mailx_types = __esm({
|
|
|
1429
1493
|
}
|
|
1430
1494
|
});
|
|
1431
1495
|
|
|
1496
|
+
// client/components/invite-card.js
|
|
1497
|
+
function formatWhen(inv) {
|
|
1498
|
+
if (!inv.start)
|
|
1499
|
+
return "";
|
|
1500
|
+
if (inv.allDay) {
|
|
1501
|
+
try {
|
|
1502
|
+
const d = /* @__PURE__ */ new Date(inv.start + "T00:00:00");
|
|
1503
|
+
return d.toLocaleDateString(void 0, { weekday: "long", year: "numeric", month: "long", day: "numeric" }) + " (all day)";
|
|
1504
|
+
} catch {
|
|
1505
|
+
return inv.start + " (all day)";
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
const instant = (() => {
|
|
1509
|
+
if (/Z$/.test(inv.start))
|
|
1510
|
+
return new Date(inv.start);
|
|
1511
|
+
if (!inv.tzid)
|
|
1512
|
+
return new Date(inv.start);
|
|
1513
|
+
try {
|
|
1514
|
+
const guess = /* @__PURE__ */ new Date(inv.start + "Z");
|
|
1515
|
+
const asZone = new Date(guess.toLocaleString("en-US", { timeZone: inv.tzid }));
|
|
1516
|
+
const asUtc = new Date(guess.toLocaleString("en-US", { timeZone: "UTC" }));
|
|
1517
|
+
return new Date(guess.getTime() + (asUtc.getTime() - asZone.getTime()));
|
|
1518
|
+
} catch {
|
|
1519
|
+
return new Date(inv.start);
|
|
1520
|
+
}
|
|
1521
|
+
})();
|
|
1522
|
+
if (isNaN(instant.getTime()))
|
|
1523
|
+
return inv.start;
|
|
1524
|
+
const localZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
1525
|
+
const opts = {
|
|
1526
|
+
weekday: "short",
|
|
1527
|
+
month: "short",
|
|
1528
|
+
day: "numeric",
|
|
1529
|
+
hour: "numeric",
|
|
1530
|
+
minute: "2-digit"
|
|
1531
|
+
};
|
|
1532
|
+
let out = instant.toLocaleString(void 0, opts);
|
|
1533
|
+
if (inv.end && !/Z$/.test(inv.end)) {
|
|
1534
|
+
try {
|
|
1535
|
+
const endInstant = new Date(instant.getTime() + ((/* @__PURE__ */ new Date(inv.end + "Z")).getTime() - (/* @__PURE__ */ new Date(inv.start + "Z")).getTime()));
|
|
1536
|
+
out += " \u2013 " + endInstant.toLocaleString(void 0, { hour: "numeric", minute: "2-digit" });
|
|
1537
|
+
} catch {
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
if (inv.tzid && inv.tzid !== localZone) {
|
|
1541
|
+
out += ` (sent as ${inv.start.slice(11, 16)} ${inv.tzid})`;
|
|
1542
|
+
}
|
|
1543
|
+
return out;
|
|
1544
|
+
}
|
|
1545
|
+
function buildInviteCard(inv, bodyHtml) {
|
|
1546
|
+
if (!inv || !inv.summary && !inv.start)
|
|
1547
|
+
return null;
|
|
1548
|
+
const card = document.createElement("div");
|
|
1549
|
+
card.className = "mv-invite";
|
|
1550
|
+
if (inv.method === "CANCEL")
|
|
1551
|
+
card.classList.add("mv-invite-cancelled");
|
|
1552
|
+
const when = formatWhen(inv);
|
|
1553
|
+
const isMeetLink = /^https?:/;
|
|
1554
|
+
const googleHandles = inv.fromGoogle && inviteHasGoogleRsvp(bodyHtml);
|
|
1555
|
+
const rows = [];
|
|
1556
|
+
if (when)
|
|
1557
|
+
rows.push(`<div class="mv-invite-when">${esc(when)}</div>`);
|
|
1558
|
+
if (inv.location) {
|
|
1559
|
+
rows.push(isMeetLink ? `<div class="mv-invite-row"><span class="mv-invite-label">Where</span><a href="${esc(inv.location)}" target="_blank" rel="noopener noreferrer">${esc(inv.location)}</a></div>` : `<div class="mv-invite-row"><span class="mv-invite-label">Where</span>${esc(inv.location)}</div>`);
|
|
1560
|
+
}
|
|
1561
|
+
if (inv.organizer.email) {
|
|
1562
|
+
rows.push(`<div class="mv-invite-row"><span class="mv-invite-label">Organizer</span>${esc(inv.organizer.name)}${inv.organizer.name !== inv.organizer.email ? " <" + esc(inv.organizer.email) + ">" : ""}</div>`);
|
|
1563
|
+
}
|
|
1564
|
+
if (inv.attendees.length) {
|
|
1565
|
+
const who = inv.attendees.map((a) => `${esc(a.name || a.email)} <span class="mv-invite-partstat">(${esc(PARTSTAT_LABEL[a.status] || a.status.toLowerCase())})</span>`).join(", ");
|
|
1566
|
+
rows.push(`<div class="mv-invite-row"><span class="mv-invite-label">Guests</span>${who}</div>`);
|
|
1567
|
+
}
|
|
1568
|
+
if (inv.rrule)
|
|
1569
|
+
rows.push(`<div class="mv-invite-row"><span class="mv-invite-label">Repeats</span><code>${esc(inv.rrule)}</code></div>`);
|
|
1570
|
+
const badge = inv.method === "CANCEL" ? "Cancelled" : inv.method === "REPLY" ? "Reply" : inv.status === "TENTATIVE" ? "Tentative" : "Invitation";
|
|
1571
|
+
card.innerHTML = `<div class="mv-invite-head"><span class="mv-invite-badge">${esc(badge)}</span><span class="mv-invite-summary">${esc(inv.summary || "(no title)")}</span></div>` + rows.join("") + (googleHandles ? `<div class="mv-invite-note">Respond with the Yes / No / Maybe buttons in the message below \u2014 they go straight to Google Calendar.</div>` : "");
|
|
1572
|
+
return card;
|
|
1573
|
+
}
|
|
1574
|
+
var PARTSTAT_LABEL, esc;
|
|
1575
|
+
var init_invite_card = __esm({
|
|
1576
|
+
"client/components/invite-card.js"() {
|
|
1577
|
+
"use strict";
|
|
1578
|
+
init_mailx_types();
|
|
1579
|
+
PARTSTAT_LABEL = {
|
|
1580
|
+
"ACCEPTED": "accepted",
|
|
1581
|
+
"DECLINED": "declined",
|
|
1582
|
+
"TENTATIVE": "maybe",
|
|
1583
|
+
"NEEDS-ACTION": "no reply yet",
|
|
1584
|
+
"DELEGATED": "delegated"
|
|
1585
|
+
};
|
|
1586
|
+
esc = (s) => (s || "").replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c]);
|
|
1587
|
+
}
|
|
1588
|
+
});
|
|
1589
|
+
|
|
1432
1590
|
// client/components/address-book.js
|
|
1433
1591
|
var address_book_exports = {};
|
|
1434
1592
|
__export(address_book_exports, {
|
|
@@ -2302,19 +2460,6 @@ function installPreviewControls(iframe) {
|
|
|
2302
2460
|
}
|
|
2303
2461
|
if (e.ctrlKey && !e.altKey && !e.metaKey && (e.key === "a" || e.key === "A"))
|
|
2304
2462
|
return;
|
|
2305
|
-
const synth = new KeyboardEvent("keydown", {
|
|
2306
|
-
key: e.key,
|
|
2307
|
-
code: e.code,
|
|
2308
|
-
ctrlKey: e.ctrlKey,
|
|
2309
|
-
shiftKey: e.shiftKey,
|
|
2310
|
-
altKey: e.altKey,
|
|
2311
|
-
metaKey: e.metaKey,
|
|
2312
|
-
bubbles: true,
|
|
2313
|
-
cancelable: true
|
|
2314
|
-
});
|
|
2315
|
-
const allowDefault = document.dispatchEvent(synth);
|
|
2316
|
-
if (!allowDefault)
|
|
2317
|
-
e.preventDefault();
|
|
2318
2463
|
});
|
|
2319
2464
|
doc.addEventListener("wheel", (e) => {
|
|
2320
2465
|
if (!e.ctrlKey)
|
|
@@ -2351,6 +2496,7 @@ function clearViewer() {
|
|
|
2351
2496
|
headerEl.hidden = true;
|
|
2352
2497
|
if (attEl)
|
|
2353
2498
|
attEl.hidden = true;
|
|
2499
|
+
document.getElementById("mv-invite")?.remove();
|
|
2354
2500
|
setTimeout(() => {
|
|
2355
2501
|
try {
|
|
2356
2502
|
const stillPlaceholder = !!document.querySelector("#mv-body .mv-empty");
|
|
@@ -2610,6 +2756,16 @@ async function showMessage(accountId, uid, folderId, specialUse, isRetry = false
|
|
|
2610
2756
|
fromEl.textContent = formatAddr(msg.from);
|
|
2611
2757
|
setRecipientLine(toEl, msg.to, msg.cc, msg.deliveredTo);
|
|
2612
2758
|
headerEl.querySelector(".mv-subject").textContent = msg.subject;
|
|
2759
|
+
document.getElementById("mv-invite")?.remove();
|
|
2760
|
+
try {
|
|
2761
|
+
const card = buildInviteCard(msg.invite, String(msg.bodyHtml || ""));
|
|
2762
|
+
if (card) {
|
|
2763
|
+
card.id = "mv-invite";
|
|
2764
|
+
headerEl.insertAdjacentElement("afterend", card);
|
|
2765
|
+
}
|
|
2766
|
+
} catch (e) {
|
|
2767
|
+
console.warn("[viewer] invite card failed:", e?.message || e);
|
|
2768
|
+
}
|
|
2613
2769
|
document.dispatchEvent(new CustomEvent("mailx-message-shown", { detail: { accountId } }));
|
|
2614
2770
|
for (const el of [fromEl, toEl]) {
|
|
2615
2771
|
el.addEventListener("contextmenu", (e) => {
|
|
@@ -2693,6 +2849,36 @@ async function showMessage(accountId, uid, folderId, specialUse, isRetry = false
|
|
|
2693
2849
|
items.push({ label: "", action: () => {
|
|
2694
2850
|
}, separator: true });
|
|
2695
2851
|
}
|
|
2852
|
+
if (el === fromEl && msg.from?.address) {
|
|
2853
|
+
const addr = String(msg.from.address);
|
|
2854
|
+
const dom = addr.split("@").pop() || "";
|
|
2855
|
+
const claimed = displayNameClaimsOtherAddress(String(msg.from.name || ""), addr);
|
|
2856
|
+
if (claimed && !isSenderApproved(addr)) {
|
|
2857
|
+
const approve = async (type, value) => {
|
|
2858
|
+
noteApprovedLocally(type, value);
|
|
2859
|
+
try {
|
|
2860
|
+
await allowRemoteContent(type, value);
|
|
2861
|
+
await refreshApprovedSenders();
|
|
2862
|
+
document.dispatchEvent(new CustomEvent("mailx-allowlist-changed"));
|
|
2863
|
+
const st = document.getElementById("status-sync");
|
|
2864
|
+
if (st)
|
|
2865
|
+
st.textContent = `Marked valid: ${value}`;
|
|
2866
|
+
} catch (e2) {
|
|
2867
|
+
const st = document.getElementById("status-sync");
|
|
2868
|
+
if (st) {
|
|
2869
|
+
st.textContent = `Couldn't save "${value}" to the allowlist \u2014 ${e2?.message || e2}`;
|
|
2870
|
+
st.style.color = "oklch(0.65 0.2 25)";
|
|
2871
|
+
}
|
|
2872
|
+
console.error("[allowlist] approve failed:", e2);
|
|
2873
|
+
}
|
|
2874
|
+
};
|
|
2875
|
+
items.push({ label: `\u2713 Mark ${addr} as a valid sender`, action: () => void approve("sender", addr) });
|
|
2876
|
+
if (dom)
|
|
2877
|
+
items.push({ label: `\u2713 Trust all senders at ${dom}`, action: () => void approve("domain", dom) });
|
|
2878
|
+
items.push({ label: "", action: () => {
|
|
2879
|
+
}, separator: true });
|
|
2880
|
+
}
|
|
2881
|
+
}
|
|
2696
2882
|
if (omitted > 0) {
|
|
2697
2883
|
items.push({
|
|
2698
2884
|
label: `\u2026 ${omitted} more recipients \u2014 copy the whole list`,
|
|
@@ -3282,7 +3468,7 @@ ${escapeText(reputationBlind)}"` : ""}>${reputationText}</div>` : "") + `<div cl
|
|
|
3282
3468
|
}
|
|
3283
3469
|
function formatAddr(addr) {
|
|
3284
3470
|
const { text, claimed } = formatSender(addr);
|
|
3285
|
-
if (!claimed)
|
|
3471
|
+
if (!claimed || isSenderApproved(addr.address))
|
|
3286
3472
|
return text;
|
|
3287
3473
|
return `${text} \u2014 display name claims ${claimed}`;
|
|
3288
3474
|
}
|
|
@@ -3891,11 +4077,11 @@ function buildPrintableDoc(m) {
|
|
|
3891
4077
|
if (!m)
|
|
3892
4078
|
return null;
|
|
3893
4079
|
const fmt = (a) => a?.name ? `${a.name} <${a.address || ""}>` : a?.address || "";
|
|
3894
|
-
const
|
|
3895
|
-
const row = (label, value) => value ? `<div><strong>${label}:</strong> ${
|
|
4080
|
+
const esc2 = escapeHtmlLocal;
|
|
4081
|
+
const row = (label, value) => value ? `<div><strong>${label}:</strong> ${esc2(value)}</div>` : "";
|
|
3896
4082
|
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>`;
|
|
3897
|
-
const bodyHtml = m.bodyHtml ? m.bodyHtml : `<pre style="white-space:pre-wrap;word-break:break-word;font-family:system-ui,sans-serif;">${
|
|
3898
|
-
return `<!DOCTYPE html><html><head><meta charset="utf-8"><title>${
|
|
4083
|
+
const bodyHtml = m.bodyHtml ? m.bodyHtml : `<pre style="white-space:pre-wrap;word-break:break-word;font-family:system-ui,sans-serif;">${esc2(m.bodyText || "")}</pre>`;
|
|
4084
|
+
return `<!DOCTYPE html><html><head><meta charset="utf-8"><title>${esc2(m.subject || "Message")}</title><style>@media print{body{margin:0;}}body{margin:16px;}img{max-width:100%;}</style></head><body>${headerHtml}${bodyHtml}</body></html>`;
|
|
3899
4085
|
}
|
|
3900
4086
|
function printCurrentMessage() {
|
|
3901
4087
|
if (!currentMessage)
|
|
@@ -4107,6 +4293,8 @@ var init_message_viewer = __esm({
|
|
|
4107
4293
|
"client/components/message-viewer.js"() {
|
|
4108
4294
|
"use strict";
|
|
4109
4295
|
init_api_client();
|
|
4296
|
+
init_approved_senders();
|
|
4297
|
+
init_invite_card();
|
|
4110
4298
|
init_context_menu();
|
|
4111
4299
|
init_message_state();
|
|
4112
4300
|
init_message_list();
|
|
@@ -5530,6 +5718,7 @@ var init_message_list = __esm({
|
|
|
5530
5718
|
init_folder_picker();
|
|
5531
5719
|
init_fold_text();
|
|
5532
5720
|
init_mailx_types();
|
|
5721
|
+
init_approved_senders();
|
|
5533
5722
|
currentSpecialUse = "";
|
|
5534
5723
|
lastClickedRow = null;
|
|
5535
5724
|
loading = false;
|
|
@@ -5644,7 +5833,7 @@ var init_message_list = __esm({
|
|
|
5644
5833
|
from.textContent = msg.to.map((a) => a.name || a.address).join(", ");
|
|
5645
5834
|
} else {
|
|
5646
5835
|
const fromName2 = msg.from.name || msg.from.address;
|
|
5647
|
-
const fromClaim = displayNameClaimsOtherAddress(msg.from.name || "", msg.from.address || "");
|
|
5836
|
+
const fromClaim = isSenderApproved(msg.from.address || "") ? null : displayNameClaimsOtherAddress(msg.from.name || "", msg.from.address || "");
|
|
5648
5837
|
from.textContent = fromClaim ? `${fromName2} \u2014 actually ${msg.from.address}` : fromName2;
|
|
5649
5838
|
if (fromClaim) {
|
|
5650
5839
|
from.classList.add("ml-from-spoofed");
|
|
@@ -7295,7 +7484,7 @@ async function collectDueAlarms(now) {
|
|
|
7295
7484
|
}
|
|
7296
7485
|
return items;
|
|
7297
7486
|
}
|
|
7298
|
-
function
|
|
7487
|
+
function formatWhen2(ms) {
|
|
7299
7488
|
if (!ms)
|
|
7300
7489
|
return "";
|
|
7301
7490
|
const d = new Date(ms);
|
|
@@ -7418,7 +7607,7 @@ async function firePopupForItem(item) {
|
|
|
7418
7607
|
.actions button.action[data-btn="Delete"]:hover { background: #800; }
|
|
7419
7608
|
</style></head><body>
|
|
7420
7609
|
<div class="title"><span class="icon">${icon}</span>${escapeHtml7(item.title)}</div>
|
|
7421
|
-
<div class="when">${escapeHtml7(
|
|
7610
|
+
<div class="when">${escapeHtml7(formatWhen2(item.whenMs))}</div>
|
|
7422
7611
|
<div class="kind">${kindLabel}</div>
|
|
7423
7612
|
<div class="row">
|
|
7424
7613
|
<span class="row-label">Snooze:</span>
|
|
@@ -9170,6 +9359,7 @@ window.__btick && window.__btick("app.ts module body executing");
|
|
|
9170
9359
|
var baseTitle = APP_NAME;
|
|
9171
9360
|
var lastSeenCount = 0;
|
|
9172
9361
|
var badgeCount = 0;
|
|
9362
|
+
var lastOverlayCount = -1;
|
|
9173
9363
|
function updateBadge(count) {
|
|
9174
9364
|
badgeCount = count;
|
|
9175
9365
|
document.title = count > 0 ? `(${count}) ${baseTitle}` : baseTitle;
|
|
@@ -9206,7 +9396,8 @@ function updateBadge(count) {
|
|
|
9206
9396
|
link.href = dataUrl;
|
|
9207
9397
|
try {
|
|
9208
9398
|
const hostApi = window.mailxapi;
|
|
9209
|
-
if (hostApi?.setTaskbarOverlay) {
|
|
9399
|
+
if (hostApi?.setTaskbarOverlay && count !== lastOverlayCount) {
|
|
9400
|
+
lastOverlayCount = count;
|
|
9210
9401
|
if (count > 0) {
|
|
9211
9402
|
const bc = document.createElement("canvas");
|
|
9212
9403
|
bc.width = 32;
|
|
@@ -9902,12 +10093,12 @@ async function openCompose(mode, overrideMsg, overrideAccountId) {
|
|
|
9902
10093
|
}
|
|
9903
10094
|
if (msg && (mode === "editAsNew" || mode === "forward") && Array.isArray(msg.attachments) && msg.attachments.length) {
|
|
9904
10095
|
const msgAccountId = current?.accountId || accountId;
|
|
9905
|
-
const
|
|
10096
|
+
const loaded2 = [];
|
|
9906
10097
|
for (const att of msg.attachments) {
|
|
9907
10098
|
try {
|
|
9908
10099
|
const data = await getAttachment(msgAccountId, msg.uid, att.id, msg.folderId);
|
|
9909
10100
|
if (data?.content) {
|
|
9910
|
-
|
|
10101
|
+
loaded2.push({
|
|
9911
10102
|
filename: data.filename || att.filename || "attachment",
|
|
9912
10103
|
mimeType: data.contentType || att.contentType || "application/octet-stream",
|
|
9913
10104
|
dataBase64: data.content
|
|
@@ -9917,7 +10108,7 @@ async function openCompose(mode, overrideMsg, overrideAccountId) {
|
|
|
9917
10108
|
console.error(`[compose] ${mode}: failed to load attachment ${att.id}: ${e?.message || e}`);
|
|
9918
10109
|
}
|
|
9919
10110
|
}
|
|
9920
|
-
if (
|
|
10111
|
+
if (loaded2.length) init.attachments = loaded2;
|
|
9921
10112
|
}
|
|
9922
10113
|
if (preferWindow && !frame) {
|
|
9923
10114
|
try {
|
|
@@ -12379,6 +12570,19 @@ function applyThreadFilter() {
|
|
|
12379
12570
|
console.error("pending mailto pickup failed:", e?.message || e);
|
|
12380
12571
|
}
|
|
12381
12572
|
})();
|
|
12573
|
+
(async () => {
|
|
12574
|
+
try {
|
|
12575
|
+
const { refreshApprovedSenders: refreshApprovedSenders2 } = await Promise.resolve().then(() => (init_approved_senders(), approved_senders_exports));
|
|
12576
|
+
await refreshApprovedSenders2();
|
|
12577
|
+
document.dispatchEvent(new CustomEvent("mailx-allowlist-changed"));
|
|
12578
|
+
} catch (e) {
|
|
12579
|
+
console.warn("approved-sender index load failed:", e?.message || e);
|
|
12580
|
+
}
|
|
12581
|
+
})();
|
|
12582
|
+
document.addEventListener("mailx-allowlist-changed", () => {
|
|
12583
|
+
void Promise.resolve().then(() => (init_message_list(), message_list_exports)).then((m) => m.reloadCurrentFolder?.()).catch(() => {
|
|
12584
|
+
});
|
|
12585
|
+
});
|
|
12382
12586
|
(async () => {
|
|
12383
12587
|
try {
|
|
12384
12588
|
const { consumePendingShare: consumePendingShare2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
|
|
@@ -12902,11 +13106,11 @@ function offsetToLineCol(src, pos) {
|
|
|
12902
13106
|
return { pos, line, col };
|
|
12903
13107
|
}
|
|
12904
13108
|
function renderMarkdown(md) {
|
|
12905
|
-
const
|
|
13109
|
+
const esc2 = (s) => s.replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c]);
|
|
12906
13110
|
const blocks = [];
|
|
12907
13111
|
let src = md.replace(/```(\w*)\n([\s\S]*?)```/g, (_m, _lang, code) => {
|
|
12908
13112
|
const i = blocks.length;
|
|
12909
|
-
blocks.push(`<pre class="mailx-help-code"><code>${
|
|
13113
|
+
blocks.push(`<pre class="mailx-help-code"><code>${esc2(code)}</code></pre>`);
|
|
12910
13114
|
return `\0BLOCK${i}\0`;
|
|
12911
13115
|
});
|
|
12912
13116
|
const lines = src.split(/\r?\n/);
|
|
@@ -12926,7 +13130,7 @@ function renderMarkdown(md) {
|
|
|
12926
13130
|
}
|
|
12927
13131
|
};
|
|
12928
13132
|
function inline(s) {
|
|
12929
|
-
s =
|
|
13133
|
+
s = esc2(s);
|
|
12930
13134
|
s = s.replace(/`([^`]+)`/g, (_m, c) => `<code>${c}</code>`);
|
|
12931
13135
|
s = s.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>");
|
|
12932
13136
|
s = s.replace(/(^|[^*])\*([^*\n]+)\*/g, "$1<em>$2</em>");
|
|
@@ -13630,12 +13834,12 @@ document.addEventListener("mailx-popout-message", (async (e) => {
|
|
|
13630
13834
|
draftId: msg.mailxDraftId || ""
|
|
13631
13835
|
};
|
|
13632
13836
|
if (Array.isArray(msg.attachments) && msg.attachments.length) {
|
|
13633
|
-
const
|
|
13837
|
+
const loaded2 = [];
|
|
13634
13838
|
for (const att of msg.attachments) {
|
|
13635
13839
|
try {
|
|
13636
13840
|
const data = await getAttachment(accountId, msg.uid, att.id, msg.folderId);
|
|
13637
13841
|
if (data?.content) {
|
|
13638
|
-
|
|
13842
|
+
loaded2.push({
|
|
13639
13843
|
filename: data.filename || att.filename || "attachment",
|
|
13640
13844
|
mimeType: data.contentType || att.contentType || "application/octet-stream",
|
|
13641
13845
|
dataBase64: data.content
|
|
@@ -13645,7 +13849,7 @@ document.addEventListener("mailx-popout-message", (async (e) => {
|
|
|
13645
13849
|
console.error(`[compose] edit draft: failed to load attachment ${att.id} ("${att.filename}"): ${e2?.message || e2}`);
|
|
13646
13850
|
}
|
|
13647
13851
|
}
|
|
13648
|
-
if (
|
|
13852
|
+
if (loaded2.length) init.attachments = loaded2;
|
|
13649
13853
|
}
|
|
13650
13854
|
handOffComposeInit(showComposeOverlay(msg.subject ? `Edit: ${msg.subject}` : "Edit draft"), init);
|
|
13651
13855
|
return;
|