@bobfrankston/rmfmail 1.2.315 → 1.2.317
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 +35 -15
- package/.llm/trusted-lists.md +11 -0
- package/client/android-bootstrap.bundle.js +3 -0
- package/client/android-bootstrap.bundle.js.map +2 -2
- package/client/app.bundle.js +124 -51
- package/client/app.bundle.js.map +2 -2
- package/client/app.js +12 -0
- package/client/app.js.map +1 -1
- package/client/app.ts +11 -0
- package/client/components/calendar-sidebar.js +160 -77
- package/client/components/calendar-sidebar.js.map +1 -1
- package/client/components/calendar-sidebar.ts +147 -70
- package/client/compose/compose.bundle.js +4 -0
- package/client/compose/compose.bundle.js.map +2 -2
- package/client/index.html +6 -6
- 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/mailxapi.js +4 -1
- package/npmchanges.md +55 -0
- 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 +14 -0
- package/packages/mailx-service/index.d.ts.map +1 -1
- package/packages/mailx-service/index.js +31 -0
- package/packages/mailx-service/index.js.map +1 -1
- package/packages/mailx-service/index.ts +29 -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-web/package.json +1 -1
- package/packages/mailx-store-web/web-service.d.ts +4 -0
- package/packages/mailx-store-web/web-service.d.ts.map +1 -1
- package/packages/mailx-store-web/web-service.js +1 -0
- package/packages/mailx-store-web/web-service.js.map +1 -1
- package/packages/mailx-store-web/web-service.ts +1 -0
- package/packages/mailx-types/mailx-api.d.ts +5 -0
- package/packages/mailx-types/mailx-api.d.ts.map +1 -1
- package/packages/mailx-types/mailx-api.ts +2 -0
- package/packages/mailx-types/package.json +1 -1
package/client/app.bundle.js
CHANGED
|
@@ -106,6 +106,7 @@ __export(api_client_exports, {
|
|
|
106
106
|
reauthGoogleScopes: () => reauthGoogleScopes,
|
|
107
107
|
reauthenticate: () => reauthenticate,
|
|
108
108
|
recordSpamReport: () => recordSpamReport,
|
|
109
|
+
refreshCalendarNow: () => refreshCalendarNow,
|
|
109
110
|
registerMailto: () => registerMailto,
|
|
110
111
|
removeUserDictWord: () => removeUserDictWord,
|
|
111
112
|
renameFolder: () => renameFolder,
|
|
@@ -259,6 +260,9 @@ function getPrimaryAccount(feature) {
|
|
|
259
260
|
function getCalendarEvents(fromMs, toMs) {
|
|
260
261
|
return ipc().getCalendarEvents?.(fromMs, toMs) ?? Promise.resolve([]);
|
|
261
262
|
}
|
|
263
|
+
function refreshCalendarNow() {
|
|
264
|
+
return ipc().refreshCalendarNow?.() ?? Promise.resolve({ ok: false, reason: "not available" });
|
|
265
|
+
}
|
|
262
266
|
function getCalendars() {
|
|
263
267
|
return ipc().getCalendars?.() ?? Promise.resolve([]);
|
|
264
268
|
}
|
|
@@ -6664,6 +6668,22 @@ __export(calendar_sidebar_exports, {
|
|
|
6664
6668
|
isCalendarSidebarOn: () => isCalendarSidebarOn,
|
|
6665
6669
|
showCalendarSidebar: () => showCalendarSidebar
|
|
6666
6670
|
});
|
|
6671
|
+
function followToday() {
|
|
6672
|
+
if (viewPinned)
|
|
6673
|
+
return;
|
|
6674
|
+
const t = /* @__PURE__ */ new Date();
|
|
6675
|
+
viewYear = t.getFullYear();
|
|
6676
|
+
viewMonth = t.getMonth();
|
|
6677
|
+
viewDay = t.getDate();
|
|
6678
|
+
}
|
|
6679
|
+
function armMidnightRollover() {
|
|
6680
|
+
const now = /* @__PURE__ */ new Date();
|
|
6681
|
+
const next = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1, 0, 0, 5);
|
|
6682
|
+
window.setTimeout(() => {
|
|
6683
|
+
void refresh();
|
|
6684
|
+
armMidnightRollover();
|
|
6685
|
+
}, next.getTime() - now.getTime());
|
|
6686
|
+
}
|
|
6667
6687
|
function calendarKind(id, primary) {
|
|
6668
6688
|
if (primary)
|
|
6669
6689
|
return "personal";
|
|
@@ -6743,7 +6763,7 @@ async function renderCalendarList() {
|
|
|
6743
6763
|
host.innerHTML = "";
|
|
6744
6764
|
} else {
|
|
6745
6765
|
const sorted = [...list].sort((a, b) => (a.primary ? 0 : 1) - (b.primary ? 0 : 1) || a.name.localeCompare(b.name));
|
|
6746
|
-
host.innerHTML = sorted.map((c) => `<label class="cal-side-cal-row" title="${escapeHtml6(c.name)}">
|
|
6766
|
+
host.innerHTML = sorted.map((c) => `<label class="cal-side-cal-row" title="${escapeHtml6(c.name)} \u2014 untick to hide its events in this list. Which calendars are listed is set in Google Calendar: the ones selected there appear; use Refresh after changing it.">
|
|
6747
6767
|
<input type="checkbox" class="cal-side-cal-check" data-cal-id="${escapeHtml6(c.id)}" ${hiddenCalendars.has(c.id) ? "" : "checked"}>
|
|
6748
6768
|
${calIconHtml(c)}
|
|
6749
6769
|
<span class="cal-side-cal-name">${escapeHtml6(c.name)}</span>
|
|
@@ -6952,7 +6972,7 @@ function renderEvents(events) {
|
|
|
6952
6972
|
<span class="cal-side-event-title cal-side-event-holiday-title" title="${escapeHtml6(e.title)}">${calIconHtml(info)} ${escapeHtml6(e.title)}</span>
|
|
6953
6973
|
</div>`;
|
|
6954
6974
|
} else {
|
|
6955
|
-
const titleAttr = link ? 'title="Click to open in Google Calendar"' : "";
|
|
6975
|
+
const titleAttr = link ? 'title="Click to open in Google Calendar to edit it. Right-click for more."' : 'title="Saved in rmfmail, syncing to Google \u2014 it can be opened there once it has synced. Right-click to delete."';
|
|
6956
6976
|
html += `<div class="cal-side-event" data-id="${e.id}"${recurAttr} data-link="${escapeHtml6(link)}" ${titleAttr}>
|
|
6957
6977
|
${calIconHtml(info)}
|
|
6958
6978
|
<span class="cal-side-event-time">${escapeHtml6(formatTime(e))}</span>
|
|
@@ -6997,18 +7017,43 @@ function renderEvents(events) {
|
|
|
6997
7017
|
el.addEventListener("contextmenu", (e) => {
|
|
6998
7018
|
e.preventDefault();
|
|
6999
7019
|
const link = el.dataset.link;
|
|
7020
|
+
const uuid = el.dataset.id || "";
|
|
7021
|
+
const title = el.querySelector(".cal-side-event-title")?.textContent?.trim() || "this event";
|
|
7000
7022
|
const items = [];
|
|
7001
7023
|
if (link)
|
|
7002
7024
|
items.push({
|
|
7003
|
-
label: "
|
|
7025
|
+
label: `Edit "${title}" in Google Calendar`,
|
|
7004
7026
|
action: () => openInBrowser(link)
|
|
7005
7027
|
});
|
|
7006
7028
|
items.push({
|
|
7007
7029
|
label: "Open Google Calendar",
|
|
7008
7030
|
action: () => openInBrowser("https://calendar.google.com/")
|
|
7009
7031
|
});
|
|
7010
|
-
if (
|
|
7011
|
-
|
|
7032
|
+
if (uuid) {
|
|
7033
|
+
items.push({ label: "", action: () => {
|
|
7034
|
+
}, separator: true });
|
|
7035
|
+
items.push({
|
|
7036
|
+
label: `Delete "${title}"`,
|
|
7037
|
+
tooltip: "Removes it from your calendar in rmfmail now and from Google when it syncs.",
|
|
7038
|
+
action: async () => {
|
|
7039
|
+
if (!await confirmAt(e.clientX, e.clientY, `Delete "${title}"`))
|
|
7040
|
+
return;
|
|
7041
|
+
try {
|
|
7042
|
+
await deleteCalendarEvent(uuid);
|
|
7043
|
+
const st = document.getElementById("status-sync");
|
|
7044
|
+
if (st)
|
|
7045
|
+
st.textContent = `Deleted event: ${title}`;
|
|
7046
|
+
await refresh();
|
|
7047
|
+
} catch (err) {
|
|
7048
|
+
const st = document.getElementById("status-sync");
|
|
7049
|
+
if (st)
|
|
7050
|
+
st.textContent = `Couldn't delete "${title}": ${err?.message || err}`;
|
|
7051
|
+
console.error("[calendar] delete failed:", err);
|
|
7052
|
+
}
|
|
7053
|
+
}
|
|
7054
|
+
});
|
|
7055
|
+
}
|
|
7056
|
+
showContextMenu(e.clientX, e.clientY, items, { editTarget: e.target });
|
|
7012
7057
|
});
|
|
7013
7058
|
});
|
|
7014
7059
|
}
|
|
@@ -7108,6 +7153,7 @@ async function renderTasks(prefetched) {
|
|
|
7108
7153
|
});
|
|
7109
7154
|
}
|
|
7110
7155
|
async function refresh() {
|
|
7156
|
+
followToday();
|
|
7111
7157
|
renderHead();
|
|
7112
7158
|
const from = new Date(viewYear, viewMonth, viewDay);
|
|
7113
7159
|
let prefetchedTasks;
|
|
@@ -7141,7 +7187,7 @@ function openNewEventDialog() {
|
|
|
7141
7187
|
heading.textContent = "New event";
|
|
7142
7188
|
const hint = document.createElement("div");
|
|
7143
7189
|
hint.style.cssText = "font-size:0.9em;color:var(--color-text-muted);";
|
|
7144
|
-
hint.textContent = "Describe the event in plain English \u2014 AI extracts the details and
|
|
7190
|
+
hint.textContent = "Describe the event in plain English \u2014 AI extracts the details and saves it to your calendar. Edit it afterwards if it needs adjusting.";
|
|
7145
7191
|
const ta = document.createElement("textarea");
|
|
7146
7192
|
ta.style.cssText = "width:100%;min-height:120px;font:inherit;padding:8px;border-radius:4px;border:1px solid var(--color-border, #ccc);box-sizing:border-box;resize:vertical;";
|
|
7147
7193
|
ta.placeholder = "Lunch with John tomorrow at noon at Joe's Pizza";
|
|
@@ -7212,54 +7258,60 @@ function openNewEventDialog() {
|
|
|
7212
7258
|
return;
|
|
7213
7259
|
}
|
|
7214
7260
|
}
|
|
7215
|
-
const api = window.mailxapi;
|
|
7216
7261
|
const targets = events.length ? events : [null];
|
|
7262
|
+
const saved = [];
|
|
7217
7263
|
for (const ev of targets) {
|
|
7218
|
-
const
|
|
7219
|
-
|
|
7220
|
-
|
|
7221
|
-
|
|
7222
|
-
|
|
7264
|
+
const row = eventToLocalRow(ev, text);
|
|
7265
|
+
try {
|
|
7266
|
+
await createCalendarEvent(row);
|
|
7267
|
+
saved.push(row.title);
|
|
7268
|
+
} catch (e) {
|
|
7269
|
+
status.textContent = `Couldn't save "${row.title}": ${e?.message || e}`;
|
|
7270
|
+
createBtn.disabled = false;
|
|
7271
|
+
cancelBtn.disabled = false;
|
|
7272
|
+
return;
|
|
7273
|
+
}
|
|
7223
7274
|
}
|
|
7224
7275
|
close();
|
|
7276
|
+
const st = document.getElementById("status-sync");
|
|
7277
|
+
if (st)
|
|
7278
|
+
st.textContent = saved.length === 1 ? `Saved event: ${saved[0]}` : `Saved ${saved.length} events: ${saved.join("; ")}`;
|
|
7279
|
+
await refresh();
|
|
7225
7280
|
});
|
|
7226
7281
|
}
|
|
7227
|
-
function
|
|
7228
|
-
const
|
|
7229
|
-
|
|
7230
|
-
|
|
7231
|
-
|
|
7232
|
-
|
|
7233
|
-
|
|
7234
|
-
|
|
7235
|
-
|
|
7236
|
-
|
|
7237
|
-
|
|
7238
|
-
return `${date}T${hh}${mm}${ss || "00"}`;
|
|
7239
|
-
};
|
|
7240
|
-
if (event?.title)
|
|
7241
|
-
params.push(`text=${encodeURIComponent(event.title)}`);
|
|
7282
|
+
function eventToLocalRow(event, fallbackText) {
|
|
7283
|
+
const startOfToday = /* @__PURE__ */ new Date();
|
|
7284
|
+
startOfToday.setHours(0, 0, 0, 0);
|
|
7285
|
+
if (!event?.startISO) {
|
|
7286
|
+
return { title: (event?.title || fallbackText).slice(0, 200), startMs: startOfToday.getTime(), endMs: startOfToday.getTime() + 864e5, allDay: true };
|
|
7287
|
+
}
|
|
7288
|
+
const allDay = !!event.allDay || !event.startISO.includes("T");
|
|
7289
|
+
const startMs = allDay ? (/* @__PURE__ */ new Date(event.startISO.slice(0, 10) + "T00:00:00")).getTime() : wallClockToMs(event.startISO, event.timeZone);
|
|
7290
|
+
let endMs;
|
|
7291
|
+
if (event.endISO)
|
|
7292
|
+
endMs = allDay ? (/* @__PURE__ */ new Date(event.endISO.slice(0, 10) + "T00:00:00")).getTime() : wallClockToMs(event.endISO, event.timeZone);
|
|
7242
7293
|
else
|
|
7243
|
-
|
|
7244
|
-
if (
|
|
7245
|
-
|
|
7246
|
-
|
|
7247
|
-
|
|
7248
|
-
|
|
7249
|
-
|
|
7250
|
-
|
|
7251
|
-
|
|
7252
|
-
|
|
7253
|
-
|
|
7294
|
+
endMs = startMs + (allDay ? 864e5 : 36e5);
|
|
7295
|
+
if (allDay && endMs <= startMs)
|
|
7296
|
+
endMs = startMs + 864e5;
|
|
7297
|
+
return { title: event.title || fallbackText.slice(0, 200), startMs, endMs, allDay, location: event.location || void 0, notes: event.notes || void 0 };
|
|
7298
|
+
}
|
|
7299
|
+
function wallClockToMs(iso, timeZone) {
|
|
7300
|
+
const local = new Date(iso).getTime();
|
|
7301
|
+
if (!timeZone)
|
|
7302
|
+
return local;
|
|
7303
|
+
try {
|
|
7304
|
+
let ms = local;
|
|
7305
|
+
for (let i = 0; i < 2; i++) {
|
|
7306
|
+
const parts = new Intl.DateTimeFormat("en-US", { timeZone, hourCycle: "h23", year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit" }).formatToParts(new Date(ms)).reduce((o, p) => (o[p.type] = p.value, o), {});
|
|
7307
|
+
const asIfLocal = (/* @__PURE__ */ new Date(`${parts.year}-${parts.month}-${parts.day}T${parts.hour}:${parts.minute}:${parts.second}`)).getTime();
|
|
7308
|
+
ms += local - asIfLocal;
|
|
7254
7309
|
}
|
|
7310
|
+
return ms;
|
|
7311
|
+
} catch (e) {
|
|
7312
|
+
console.warn(`[calendar] unknown time zone "${timeZone}", reading the time as local:`, e?.message || e);
|
|
7313
|
+
return local;
|
|
7255
7314
|
}
|
|
7256
|
-
if (event?.location)
|
|
7257
|
-
params.push(`location=${encodeURIComponent(event.location)}`);
|
|
7258
|
-
if (event?.notes)
|
|
7259
|
-
params.push(`details=${encodeURIComponent(event.notes)}`);
|
|
7260
|
-
if (event?.timeZone)
|
|
7261
|
-
params.push(`ctz=${encodeURIComponent(event.timeZone)}`);
|
|
7262
|
-
return `${base}&${params.join("&")}`;
|
|
7263
7315
|
}
|
|
7264
7316
|
async function showCalendarSidebar() {
|
|
7265
7317
|
const el = document.getElementById("calendar-sidebar");
|
|
@@ -7303,6 +7355,7 @@ function initCalendarSidebar() {
|
|
|
7303
7355
|
el.addEventListener("click", fn);
|
|
7304
7356
|
};
|
|
7305
7357
|
wireOnce("cal-side-prev", () => {
|
|
7358
|
+
viewPinned = true;
|
|
7306
7359
|
const d = new Date(viewYear, viewMonth, viewDay - 1);
|
|
7307
7360
|
viewYear = d.getFullYear();
|
|
7308
7361
|
viewMonth = d.getMonth();
|
|
@@ -7310,6 +7363,7 @@ function initCalendarSidebar() {
|
|
|
7310
7363
|
refresh();
|
|
7311
7364
|
});
|
|
7312
7365
|
wireOnce("cal-side-next", () => {
|
|
7366
|
+
viewPinned = true;
|
|
7313
7367
|
const d = new Date(viewYear, viewMonth, viewDay + 1);
|
|
7314
7368
|
viewYear = d.getFullYear();
|
|
7315
7369
|
viewMonth = d.getMonth();
|
|
@@ -7317,12 +7371,22 @@ function initCalendarSidebar() {
|
|
|
7317
7371
|
refresh();
|
|
7318
7372
|
});
|
|
7319
7373
|
wireOnce("cal-side-today", () => {
|
|
7320
|
-
|
|
7321
|
-
|
|
7322
|
-
viewMonth = t.getMonth();
|
|
7323
|
-
viewDay = t.getDate();
|
|
7374
|
+
viewPinned = false;
|
|
7375
|
+
followToday();
|
|
7324
7376
|
refresh();
|
|
7325
7377
|
});
|
|
7378
|
+
if (!window.__mailxCalRolloverArmed) {
|
|
7379
|
+
window.__mailxCalRolloverArmed = true;
|
|
7380
|
+
armMidnightRollover();
|
|
7381
|
+
window.addEventListener("focus", () => {
|
|
7382
|
+
if (!isCalendarSidebarOn())
|
|
7383
|
+
return;
|
|
7384
|
+
void refreshCalendarNow().then((r) => {
|
|
7385
|
+
if (!r?.ok && r?.reason && !/refreshed \d+ s ago/.test(r.reason))
|
|
7386
|
+
console.log(`[calendar] focus refresh not started: ${r.reason}`);
|
|
7387
|
+
}).catch((e) => console.warn("[calendar] focus refresh failed:", e?.message || e));
|
|
7388
|
+
});
|
|
7389
|
+
}
|
|
7326
7390
|
wireOnce("cal-side-new", () => {
|
|
7327
7391
|
openNewEventDialog();
|
|
7328
7392
|
});
|
|
@@ -7357,7 +7421,12 @@ function initCalendarSidebar() {
|
|
|
7357
7421
|
if (btn)
|
|
7358
7422
|
btn.disabled = true;
|
|
7359
7423
|
try {
|
|
7360
|
-
await Promise.all([renderCalendarList(), refresh()]);
|
|
7424
|
+
const [, , pulled] = await Promise.all([renderCalendarList(), refresh(), refreshCalendarNow()]);
|
|
7425
|
+
if (!pulled?.ok) {
|
|
7426
|
+
const st = document.getElementById("status-sync");
|
|
7427
|
+
if (st)
|
|
7428
|
+
st.textContent = `Calendar not pulled from Google: ${pulled?.reason || "unknown"}`;
|
|
7429
|
+
}
|
|
7361
7430
|
} finally {
|
|
7362
7431
|
setTimeout(() => {
|
|
7363
7432
|
btn?.classList.remove("cal-side-refreshing");
|
|
@@ -7451,7 +7520,7 @@ function initCalendarSidebar() {
|
|
|
7451
7520
|
}
|
|
7452
7521
|
}
|
|
7453
7522
|
}
|
|
7454
|
-
var SIDEBAR_PREF, SHOW_RECURRING_PREF, SHOW_DONE_PREF, HORIZON_DAYS_PREF, HORIZON_DEFAULT_DAYS, HOLIDAY_HORIZON_MS, viewYear, viewMonth, viewDay, lastEvents, lastOverdueTasks, calById, calendarList, hiddenCalendars, selectedTaskUuids, TASK_DELETE_NOTE;
|
|
7523
|
+
var SIDEBAR_PREF, SHOW_RECURRING_PREF, SHOW_DONE_PREF, HORIZON_DAYS_PREF, HORIZON_DEFAULT_DAYS, HOLIDAY_HORIZON_MS, viewYear, viewMonth, viewDay, viewPinned, lastEvents, lastOverdueTasks, calById, calendarList, hiddenCalendars, selectedTaskUuids, TASK_DELETE_NOTE;
|
|
7455
7524
|
var init_calendar_sidebar = __esm({
|
|
7456
7525
|
"client/components/calendar-sidebar.js"() {
|
|
7457
7526
|
"use strict";
|
|
@@ -7466,6 +7535,7 @@ var init_calendar_sidebar = __esm({
|
|
|
7466
7535
|
viewYear = (/* @__PURE__ */ new Date()).getFullYear();
|
|
7467
7536
|
viewMonth = (/* @__PURE__ */ new Date()).getMonth();
|
|
7468
7537
|
viewDay = (/* @__PURE__ */ new Date()).getDate();
|
|
7538
|
+
viewPinned = false;
|
|
7469
7539
|
lastEvents = [];
|
|
7470
7540
|
lastOverdueTasks = [];
|
|
7471
7541
|
calById = /* @__PURE__ */ new Map();
|
|
@@ -9643,6 +9713,9 @@ window.__btick && window.__btick("app.ts module body executing");
|
|
|
9643
9713
|
}, true);
|
|
9644
9714
|
document.addEventListener("contextmenu", (e) => {
|
|
9645
9715
|
if (e.defaultPrevented) return;
|
|
9716
|
+
const t = e.target;
|
|
9717
|
+
const tag = t?.tagName;
|
|
9718
|
+
if (tag === "TEXTAREA" || tag === "INPUT" && !["checkbox", "radio", "button", "submit", "range", "color", "file"].includes(t.type)) return;
|
|
9646
9719
|
e.preventDefault();
|
|
9647
9720
|
const items = editMenuItems({ target: e.target });
|
|
9648
9721
|
if (items.length > 0) showContextMenu(e.clientX, e.clientY, items);
|