@bobfrankston/rmfmail 1.2.274 → 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 +169 -0
- package/client/android-bootstrap.bundle.js.map +2 -2
- package/client/app.bundle.js +123 -23
- package/client/app.bundle.js.map +4 -4
- package/client/app.js +16 -1
- package/client/app.js.map +1 -1
- package/client/app.ts +16 -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-viewer.js +42 -17
- package/client/components/message-viewer.js.map +1 -1
- package/client/components/message-viewer.ts +42 -16
- package/client/package.json +1 -1
- package/client/styles/components.css +48 -0
- package/package.json +8 -8
- package/packages/mailx-imap/package-lock.json +2 -2
- package/packages/mailx-imap/package.json +2 -2
- 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-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-31304 → node_modules.npmglobalize-stash-59216}/.package-lock.json +0 -0
package/.commitmsg
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
Preview hotkeys fired twice — Ctrl+F opened two compose windows
|
|
2
|
+
|
|
3
|
+
A keydown in the message preview reached app.ts by TWO routes on desktop
|
|
4
|
+
WebView2: the iframe's own inline script posts every keydown to the parent as
|
|
5
|
+
`previewKey` (app.ts re-dispatches it), and installPreviewControls ALSO attached
|
|
6
|
+
a contentDocument listener that re-dispatched a synthetic keydown of its own.
|
|
7
|
+
The srcdoc document is same-origin, so both are live. One Ctrl+F therefore ran
|
|
8
|
+
openCompose("forward") twice and opened two compose popouts — the second never
|
|
9
|
+
finished loading and sat blank and "(Not Responding)" until it was killed (Bob
|
|
10
|
+
2026-08-23, "Compose: Fwd: FirstCare Slide Deck (Not Responding)"; the log shows
|
|
11
|
+
openCompose-entry mode=forward twice in the same millisecond, two popoutCompose
|
|
12
|
+
windows, and the loaded one exiting while the blank duplicate survived). Ctrl+D
|
|
13
|
+
and Ctrl+P doubled the same way.
|
|
14
|
+
|
|
15
|
+
The inline forwarder is the mechanism that survives — it is the only one that
|
|
16
|
+
works on Android WebView, where parent-side contentDocument listeners never
|
|
17
|
+
fire. installPreviewControls keeps only what is genuinely iframe-local: zoom and
|
|
18
|
+
Ctrl+A.
|
|
19
|
+
|
|
20
|
+
Also: push the Windows taskbar overlay only when the unread count CHANGES.
|
|
21
|
+
Windows holds the overlay icon until the next SetOverlayIcon, so the host cannot
|
|
22
|
+
free the icon it just handed over; re-sending an identical badge every few
|
|
23
|
+
seconds cost GDI objects for nothing and helped drive rmfmail.exe to the
|
|
24
|
+
10,000-object per-process cap (measured at GDI=9995, with 3423
|
|
25
|
+
"CreateIconIndirect failed: Not enough memory resources" in one day's log).
|
|
26
|
+
msger 0.1.429 fixes the leak itself by freeing the retired icon.
|
|
@@ -2583,10 +2583,12 @@ __export(mailx_types_exports, {
|
|
|
2583
2583
|
flaggedOf: () => flaggedOf,
|
|
2584
2584
|
formatSender: () => formatSender,
|
|
2585
2585
|
htmlToPlainText: () => htmlToPlainText,
|
|
2586
|
+
inviteHasGoogleRsvp: () => inviteHasGoogleRsvp,
|
|
2586
2587
|
isAddressToken: () => isAddressToken,
|
|
2587
2588
|
isJunkPreviewText: () => isJunkPreviewText,
|
|
2588
2589
|
mergeReminderStates: () => mergeReminderStates,
|
|
2589
2590
|
normalizeReminderState: () => normalizeReminderState,
|
|
2591
|
+
parseInvite: () => parseInvite,
|
|
2590
2592
|
parseSearchQuery: () => parseSearchQuery,
|
|
2591
2593
|
reminderStatesEqual: () => reminderStatesEqual,
|
|
2592
2594
|
sanitizeHtml: () => sanitizeHtml,
|
|
@@ -2887,6 +2889,173 @@ function formatSender(addr) {
|
|
|
2887
2889
|
return { text: address, claimed: null };
|
|
2888
2890
|
return { text: `${name} <${address}>`, claimed: displayNameClaimsOtherAddress(name, address) };
|
|
2889
2891
|
}
|
|
2892
|
+
function unfoldIcs(text) {
|
|
2893
|
+
const raw = (text || "").replace(/\r\n/g, "\n").split("\n");
|
|
2894
|
+
const out = [];
|
|
2895
|
+
for (const line of raw) {
|
|
2896
|
+
if (/^[ \t]/.test(line) && out.length)
|
|
2897
|
+
out[out.length - 1] += line.slice(1);
|
|
2898
|
+
else
|
|
2899
|
+
out.push(line);
|
|
2900
|
+
}
|
|
2901
|
+
return out;
|
|
2902
|
+
}
|
|
2903
|
+
function unescapeIcs(v) {
|
|
2904
|
+
return (v || "").replace(/\\n/gi, "\n").replace(/\\([;,\\])/g, "$1");
|
|
2905
|
+
}
|
|
2906
|
+
function splitIcsLine(line) {
|
|
2907
|
+
let inQuote = false, colon = -1;
|
|
2908
|
+
for (let i = 0; i < line.length; i++) {
|
|
2909
|
+
const c = line[i];
|
|
2910
|
+
if (c === '"')
|
|
2911
|
+
inQuote = !inQuote;
|
|
2912
|
+
else if (c === ":" && !inQuote) {
|
|
2913
|
+
colon = i;
|
|
2914
|
+
break;
|
|
2915
|
+
}
|
|
2916
|
+
}
|
|
2917
|
+
if (colon < 0)
|
|
2918
|
+
return null;
|
|
2919
|
+
const namePart = line.slice(0, colon);
|
|
2920
|
+
const value = line.slice(colon + 1);
|
|
2921
|
+
const bits = namePart.split(";");
|
|
2922
|
+
const key = (bits.shift() || "").toUpperCase();
|
|
2923
|
+
const params = {};
|
|
2924
|
+
for (const b of bits) {
|
|
2925
|
+
const eq = b.indexOf("=");
|
|
2926
|
+
if (eq < 0)
|
|
2927
|
+
continue;
|
|
2928
|
+
params[b.slice(0, eq).toUpperCase()] = b.slice(eq + 1).replace(/^"|"$/g, "");
|
|
2929
|
+
}
|
|
2930
|
+
return { key, params, value };
|
|
2931
|
+
}
|
|
2932
|
+
function icsDateToIso(v, isUtc) {
|
|
2933
|
+
const m = (v || "").match(/^(\d{4})(\d{2})(\d{2})(?:T(\d{2})(\d{2})(\d{2})(Z)?)?$/);
|
|
2934
|
+
if (!m)
|
|
2935
|
+
return "";
|
|
2936
|
+
const [, y, mo, d, hh, mm, ss, z] = m;
|
|
2937
|
+
if (!hh)
|
|
2938
|
+
return `${y}-${mo}-${d}`;
|
|
2939
|
+
const base = `${y}-${mo}-${d}T${hh}:${mm}:${ss}`;
|
|
2940
|
+
return z || isUtc ? base + "Z" : base;
|
|
2941
|
+
}
|
|
2942
|
+
function parseCalAddress(value, params) {
|
|
2943
|
+
const email = (value || "").replace(/^mailto:/i, "").trim();
|
|
2944
|
+
return { name: (params.CN || "").trim() || email, email };
|
|
2945
|
+
}
|
|
2946
|
+
function parseInvite(ics) {
|
|
2947
|
+
if (!ics || !/BEGIN:VEVENT/i.test(ics))
|
|
2948
|
+
return null;
|
|
2949
|
+
const lines = unfoldIcs(ics);
|
|
2950
|
+
const out = {
|
|
2951
|
+
method: "",
|
|
2952
|
+
uid: "",
|
|
2953
|
+
sequence: 0,
|
|
2954
|
+
summary: "",
|
|
2955
|
+
description: "",
|
|
2956
|
+
location: "",
|
|
2957
|
+
start: "",
|
|
2958
|
+
end: "",
|
|
2959
|
+
tzid: "",
|
|
2960
|
+
allDay: false,
|
|
2961
|
+
organizer: { name: "", email: "" },
|
|
2962
|
+
attendees: [],
|
|
2963
|
+
rrule: "",
|
|
2964
|
+
status: "",
|
|
2965
|
+
fromGoogle: false
|
|
2966
|
+
};
|
|
2967
|
+
let inEvent = false;
|
|
2968
|
+
let depthNonEvent = 0;
|
|
2969
|
+
for (const line of lines) {
|
|
2970
|
+
const up = line.toUpperCase();
|
|
2971
|
+
if (up.startsWith("BEGIN:VEVENT")) {
|
|
2972
|
+
inEvent = true;
|
|
2973
|
+
continue;
|
|
2974
|
+
}
|
|
2975
|
+
if (up.startsWith("END:VEVENT")) {
|
|
2976
|
+
inEvent = false;
|
|
2977
|
+
continue;
|
|
2978
|
+
}
|
|
2979
|
+
if (!inEvent && (up.startsWith("BEGIN:VTIMEZONE") || up.startsWith("BEGIN:VALARM"))) {
|
|
2980
|
+
depthNonEvent++;
|
|
2981
|
+
continue;
|
|
2982
|
+
}
|
|
2983
|
+
if (up.startsWith("END:VTIMEZONE") || up.startsWith("END:VALARM")) {
|
|
2984
|
+
if (depthNonEvent)
|
|
2985
|
+
depthNonEvent--;
|
|
2986
|
+
continue;
|
|
2987
|
+
}
|
|
2988
|
+
if (inEvent && up.startsWith("BEGIN:VALARM")) {
|
|
2989
|
+
depthNonEvent++;
|
|
2990
|
+
continue;
|
|
2991
|
+
}
|
|
2992
|
+
const p = splitIcsLine(line);
|
|
2993
|
+
if (!p)
|
|
2994
|
+
continue;
|
|
2995
|
+
if (!inEvent) {
|
|
2996
|
+
if (p.key === "METHOD")
|
|
2997
|
+
out.method = p.value.trim().toUpperCase();
|
|
2998
|
+
else if (p.key === "PRODID" && /google/i.test(p.value))
|
|
2999
|
+
out.fromGoogle = true;
|
|
3000
|
+
continue;
|
|
3001
|
+
}
|
|
3002
|
+
if (depthNonEvent)
|
|
3003
|
+
continue;
|
|
3004
|
+
switch (p.key) {
|
|
3005
|
+
case "SUMMARY":
|
|
3006
|
+
out.summary = unescapeIcs(p.value);
|
|
3007
|
+
break;
|
|
3008
|
+
case "DESCRIPTION":
|
|
3009
|
+
out.description = unescapeIcs(p.value);
|
|
3010
|
+
break;
|
|
3011
|
+
case "LOCATION":
|
|
3012
|
+
out.location = unescapeIcs(p.value);
|
|
3013
|
+
break;
|
|
3014
|
+
case "UID":
|
|
3015
|
+
out.uid = p.value.trim();
|
|
3016
|
+
break;
|
|
3017
|
+
case "STATUS":
|
|
3018
|
+
out.status = p.value.trim().toUpperCase();
|
|
3019
|
+
break;
|
|
3020
|
+
case "RRULE":
|
|
3021
|
+
out.rrule = p.value.trim();
|
|
3022
|
+
break;
|
|
3023
|
+
case "SEQUENCE":
|
|
3024
|
+
out.sequence = parseInt(p.value, 10) || 0;
|
|
3025
|
+
break;
|
|
3026
|
+
case "DTSTART":
|
|
3027
|
+
out.start = icsDateToIso(p.value.trim(), false);
|
|
3028
|
+
out.tzid = p.params.TZID || out.tzid;
|
|
3029
|
+
out.allDay = (p.params.VALUE || "").toUpperCase() === "DATE" || !/T/.test(p.value);
|
|
3030
|
+
break;
|
|
3031
|
+
case "DTEND":
|
|
3032
|
+
out.end = icsDateToIso(p.value.trim(), false);
|
|
3033
|
+
out.tzid = out.tzid || p.params.TZID || "";
|
|
3034
|
+
break;
|
|
3035
|
+
case "ORGANIZER":
|
|
3036
|
+
out.organizer = parseCalAddress(p.value, p.params);
|
|
3037
|
+
break;
|
|
3038
|
+
case "ATTENDEE": {
|
|
3039
|
+
const a = parseCalAddress(p.value, p.params);
|
|
3040
|
+
out.attendees.push({
|
|
3041
|
+
name: a.name,
|
|
3042
|
+
email: a.email,
|
|
3043
|
+
status: (p.params.PARTSTAT || "NEEDS-ACTION").toUpperCase(),
|
|
3044
|
+
rsvp: (p.params.RSVP || "").toUpperCase() === "TRUE"
|
|
3045
|
+
});
|
|
3046
|
+
break;
|
|
3047
|
+
}
|
|
3048
|
+
}
|
|
3049
|
+
}
|
|
3050
|
+
if (!out.summary && !out.start)
|
|
3051
|
+
return null;
|
|
3052
|
+
return out;
|
|
3053
|
+
}
|
|
3054
|
+
function inviteHasGoogleRsvp(bodyHtml) {
|
|
3055
|
+
if (!bodyHtml)
|
|
3056
|
+
return false;
|
|
3057
|
+
return /calendar\.google\.com\/calendar\/event\?action=RESPOND/i.test(bodyHtml);
|
|
3058
|
+
}
|
|
2890
3059
|
var _SEEN, _FLAGGED, _ANSWERED, _DRAFT, _DELETED, seenOf, flaggedOf, answeredOf, draftOf, deletedOf, setSeen, setFlagged, setAnswered, setDraft, toggleSeen, toggleFlagged;
|
|
2891
3060
|
var init_mailx_types = __esm({
|
|
2892
3061
|
"packages/mailx-types/index.js"() {
|