@bobfrankston/rmfmail 1.0.666 → 1.0.668

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.
Files changed (38) hide show
  1. package/TODO.md +2 -0
  2. package/bin/mailx.js +114 -111
  3. package/bin/mailx.js.map +1 -1
  4. package/bin/mailx.ts +117 -95
  5. package/client/app.bundle.js +143 -13
  6. package/client/app.bundle.js.map +2 -2
  7. package/client/components/alarms.js +138 -16
  8. package/client/components/alarms.js.map +1 -1
  9. package/client/components/alarms.ts +138 -17
  10. package/client/components/calendar-sidebar.js +39 -3
  11. package/client/components/calendar-sidebar.js.map +1 -1
  12. package/client/components/calendar-sidebar.ts +39 -2
  13. package/client/compose/compose.bundle.js.map +1 -1
  14. package/client/index.html +6 -0
  15. package/client/lib/api-client.ts +1 -1
  16. package/client/styles/components.css +4 -0
  17. package/package.json +1 -1
  18. package/packages/mailx-host/index.d.ts +5 -0
  19. package/packages/mailx-host/index.d.ts.map +1 -1
  20. package/packages/mailx-host/index.js +5 -0
  21. package/packages/mailx-host/index.js.map +1 -1
  22. package/packages/mailx-host/index.ts +5 -0
  23. package/packages/mailx-host/package.json +1 -1
  24. package/packages/mailx-service/index.d.ts +1 -0
  25. package/packages/mailx-service/index.d.ts.map +1 -1
  26. package/packages/mailx-service/index.js +95 -9
  27. package/packages/mailx-service/index.js.map +1 -1
  28. package/packages/mailx-service/index.ts +93 -10
  29. package/packages/mailx-service/local-store.d.ts +4 -0
  30. package/packages/mailx-service/local-store.d.ts.map +1 -1
  31. package/packages/mailx-service/local-store.js +55 -1
  32. package/packages/mailx-service/local-store.js.map +1 -1
  33. package/packages/mailx-service/local-store.ts +51 -1
  34. package/packages/mailx-store/db.d.ts +1 -0
  35. package/packages/mailx-store/db.d.ts.map +1 -1
  36. package/packages/mailx-store/db.js +11 -4
  37. package/packages/mailx-store/db.js.map +1 -1
  38. package/packages/mailx-store/db.ts +13 -5
@@ -17,7 +17,7 @@
17
17
  * hidden — alarm won't fire until user returns, which matches Thunderbird's
18
18
  * behavior (focus-gated). OS-level notifications are a separate follow-up.
19
19
  */
20
- import { getCalendarEvents, getTasks, showReminderPopup } from "../lib/api-client.js";
20
+ import { getCalendarEvents, getTasks, showReminderPopup, deleteCalendarEvent } from "../lib/api-client.js";
21
21
  const DISMISSED_KEY = "mailx-alarm-dismissed"; // { uuid: true }
22
22
  const SNOOZED_KEY = "mailx-alarm-snoozed"; // { uuid: epoch-ms-end }
23
23
  const CAL_LEAD_MS = 10 * 60 * 1000; // 10 min before cal event start
@@ -199,34 +199,131 @@ async function firePopupForItem(item) {
199
199
  if (openPopups.has(item.uuid))
200
200
  return;
201
201
  openPopups.add(item.uuid);
202
+ // Buttons map to msger's `buttons` array for the keyboard-default
203
+ // detection in template mode; in rawHtml we render our own controls,
204
+ // but the array still flows back as the default-button fallback if
205
+ // the user closes the window without picking. Order = priority.
206
+ const buttons = ["snooze", "Dismiss"];
207
+ if (item.htmlLink)
208
+ buttons.push("Open");
209
+ if (item.kind === "calendar")
210
+ buttons.push("Delete");
211
+ // rawHtml mode. msger's design intent is that we deliver any HTML
212
+ // form and the parent process receives whatever `window.ipc.postMessage`
213
+ // serializes — `MessageBoxResult` includes a `form: serde_json::Value`
214
+ // field for exactly this. We mirror Thunderbird's snooze popup: a
215
+ // row of preset chips PLUS a custom "N units" form. Snooze submits
216
+ // `{button:"snooze", form:{minutes:N}}` so the daemon can handle any
217
+ // duration; Dismiss/Open/Delete still discriminate by button label.
218
+ const icon = item.kind === "calendar" ? "📅" : "☑";
219
+ const kindLabel = item.kind === "calendar" ? "Calendar event" : "Task due";
220
+ // Presets in minutes. Tuned to TBird's defaults — covers the common
221
+ // "give me 15 more minutes" through "remind me tomorrow morning."
222
+ const presets = [
223
+ { label: "5m", minutes: 5 },
224
+ { label: "15m", minutes: 15 },
225
+ { label: "30m", minutes: 30 },
226
+ { label: "1h", minutes: 60 },
227
+ { label: "2h", minutes: 120 },
228
+ { label: "1d", minutes: 60 * 24 },
229
+ ];
230
+ const presetChips = presets.map(p => `<button type="button" class="chip" data-snooze-min="${p.minutes}">${p.label}</button>`).join("");
231
+ const actionBtns = ["Dismiss"];
232
+ if (item.htmlLink)
233
+ actionBtns.push("Open");
234
+ if (item.kind === "calendar")
235
+ actionBtns.push("Delete");
236
+ const actionHtml = actionBtns.map(b => `<button type="button" class="action" data-btn="${escapeHtml(b)}">${escapeHtml(b)}</button>`).join("");
202
237
  const html = `<!DOCTYPE html>
203
238
  <html><head><meta charset="utf-8"><style>
204
- body { font: 13px system-ui, sans-serif; margin: 0; padding: 16px 18px; background: #fff; color: #222; }
205
- .icon { display: inline-block; width: 18px; text-align: center; margin-right: 4px; }
206
- .title { font-size: 1.05em; font-weight: 600; margin-bottom: 6px; }
207
- .when { color: #555; font-size: 0.95em; margin-bottom: 4px; }
239
+ html, body { height: 100%; }
240
+ body { font: 13px system-ui, sans-serif; margin: 0; padding: 14px 16px; background: #fff; color: #222; display:flex; flex-direction:column; box-sizing:border-box; gap: 8px; }
241
+ .icon { display: inline-block; width: 20px; text-align: center; margin-right: 4px; }
242
+ .title { font-size: 1.1em; font-weight: 600; }
243
+ .when { color: #555; font-size: 0.95em; }
208
244
  .kind { color: #888; font-size: 0.85em; }
245
+ .spacer { flex: 1; }
246
+ .row { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
247
+ .row-label { color: #555; font-size: 0.85em; margin-right: 2px; }
248
+ button { font: 13px system-ui, sans-serif; cursor: pointer; }
249
+ .chip { padding: 3px 9px; border: 1px solid #ccc; border-radius: 12px; background: #f7f7f7; color: #222; }
250
+ .chip:hover { background: #e8f0fe; border-color: #0066cc; color: #0066cc; }
251
+ .custom { display: flex; align-items: center; gap: 4px; }
252
+ .custom input[type=number] { width: 48px; padding: 2px 4px; border: 1px solid #ccc; border-radius: 3px; font: inherit; }
253
+ .custom select { padding: 2px 4px; border: 1px solid #ccc; border-radius: 3px; font: inherit; background: #fff; }
254
+ .custom button.ok { padding: 2px 8px; border: 1px solid #0066cc; border-radius: 3px; background: #0066cc; color: #fff; }
255
+ .custom button.ok:hover { background: #0052a3; }
256
+ .actions { display: flex; gap: 8px; justify-content: flex-end; margin-top: 4px; }
257
+ .actions button.action { padding: 5px 14px; border: none; border-radius: 4px; background: #0066cc; color: #fff; min-width: 70px; }
258
+ .actions button.action:hover { background: #0052a3; }
259
+ .actions button.action[data-btn="Delete"] { background: #b00; }
260
+ .actions button.action[data-btn="Delete"]:hover { background: #800; }
209
261
  </style></head><body>
210
- <div class="title"><span class="icon">${item.kind === "calendar" ? "📅" : "☑"}</span>${escapeHtml(item.title)}</div>
262
+ <div class="title"><span class="icon">${icon}</span>${escapeHtml(item.title)}</div>
211
263
  <div class="when">${escapeHtml(formatWhen(item.whenMs))}</div>
212
- <div class="kind">${item.kind === "calendar" ? "Calendar event" : "Task due"}</div>
264
+ <div class="kind">${kindLabel}</div>
265
+ <div class="row">
266
+ <span class="row-label">Snooze:</span>
267
+ ${presetChips}
268
+ </div>
269
+ <div class="row custom">
270
+ <span class="row-label">Custom:</span>
271
+ <input type="number" id="customN" value="5" min="1" max="999">
272
+ <select id="customUnit">
273
+ <option value="1">minutes</option>
274
+ <option value="60">hours</option>
275
+ <option value="1440">days</option>
276
+ </select>
277
+ <button type="button" class="ok" id="customGo">Snooze</button>
278
+ </div>
279
+ <div class="spacer"></div>
280
+ <div class="actions">${actionHtml}</div>
281
+ <script>
282
+ function send(payload) {
283
+ try { window.ipc.postMessage(JSON.stringify(payload)); }
284
+ catch (e) { try { window.close(); } catch (_) {} }
285
+ }
286
+ // Preset chips → snooze N minutes
287
+ document.querySelectorAll("button[data-snooze-min]").forEach(function(b) {
288
+ b.addEventListener("click", function() {
289
+ var m = parseInt(b.getAttribute("data-snooze-min"), 10) || 15;
290
+ send({ button: "snooze", form: { minutes: m } });
291
+ });
292
+ });
293
+ // Custom snooze form → snooze N * unit-multiplier minutes
294
+ var customGo = document.getElementById("customGo");
295
+ var customN = document.getElementById("customN");
296
+ var customUnit = document.getElementById("customUnit");
297
+ function submitCustom() {
298
+ var n = parseInt(customN.value, 10);
299
+ if (!isFinite(n) || n < 1) n = 5;
300
+ var mult = parseInt(customUnit.value, 10) || 1;
301
+ send({ button: "snooze", form: { minutes: n * mult } });
302
+ }
303
+ customGo.addEventListener("click", submitCustom);
304
+ customN.addEventListener("keydown", function(e) { if (e.key === "Enter") submitCustom(); });
305
+ // Action buttons (Dismiss/Open/Delete) → discriminate by label.
306
+ document.querySelectorAll("button[data-btn]").forEach(function(b) {
307
+ b.addEventListener("click", function() {
308
+ send({ button: b.getAttribute("data-btn") });
309
+ });
310
+ });
311
+ </script>
213
312
  </body></html>`;
214
- // Button labels. Open opens the htmlLink (Google Calendar editor)
215
- // and clears all local alarm state so the next poll tick treats the
216
- // event as a fresh entry — if the user moves the start time during
217
- // the edit, the new alarm fires off the new time, not the old one.
218
- // Tasks have no htmlLink so Open is skipped for them.
219
- const buttons = ["Snooze 15m", "Snooze 1h", "Dismiss"];
220
- if (item.htmlLink)
221
- buttons.push("Open");
222
313
  const title = item.kind === "calendar" ? "Calendar reminder" : "Task reminder";
223
314
  const hasHost = !!window.mailxapi?.showReminderPopup;
315
+ // Result now carries an optional `form` object — see msger's
316
+ // MessageBoxResult.form (serde_json::Value passthrough). Snooze
317
+ // chips/inputs send `{ button: "snooze", form: { minutes: N } }`.
224
318
  let r;
225
319
  try {
226
320
  if (hasHost) {
227
321
  r = await showReminderPopup({
228
322
  title, html, buttons,
229
- size: { width: 480, height: 210 },
323
+ // Sized for the new snooze form (chips row + custom input
324
+ // row + action buttons) plus title/when/kind. Width fits
325
+ // "5m 15m 30m 1h 2h 1d" on one line in Segoe UI.
326
+ size: { width: 560, height: 300 },
230
327
  });
231
328
  }
232
329
  else {
@@ -270,6 +367,18 @@ async function firePopupForItem(item) {
270
367
  firedThisSession.delete(item.uuid);
271
368
  snoozeItem(item, 2);
272
369
  break;
370
+ case "snooze": {
371
+ // Generic snooze, duration in `form.minutes`. Falls back to
372
+ // 15 min if the form is malformed or missing the value (the
373
+ // chip / custom-form JS always populates it, so this is just
374
+ // defense-in-depth).
375
+ const m = Math.max(1, Math.floor(r.form?.minutes ?? 15));
376
+ snoozeItem(item, m);
377
+ break;
378
+ }
379
+ // Legacy literal labels — retained so a pre-upgrade popup whose
380
+ // bundle still says "Snooze 15m" / "Snooze 1h" continues to work
381
+ // through a daemon-side rebuild without a UI restart.
273
382
  case "Snooze 15m":
274
383
  snoozeItem(item, 15);
275
384
  break;
@@ -283,6 +392,19 @@ async function firePopupForItem(item) {
283
392
  saveDismissed(m);
284
393
  }
285
394
  break;
395
+ case "Delete":
396
+ // Remove the event server-side. Local row gets the pending-delete
397
+ // mark inside the service; reconcile picks it up. Also dismiss
398
+ // the alarm so it can't re-fire on a stale local row before the
399
+ // delete propagates.
400
+ {
401
+ const m = loadDismissed();
402
+ m[item.uuid] = true;
403
+ saveDismissed(m);
404
+ }
405
+ firedThisSession.delete(item.uuid);
406
+ deleteCalendarEvent(item.uuid).catch((e) => console.error(` [alarm] delete failed for ${item.uuid}: ${e?.message || e}`));
407
+ break;
286
408
  default:
287
409
  // User closed the window via X / Esc / "closed" / "dismissed"
288
410
  // without picking a labeled button. Treat as a 15-minute
@@ -1 +1 @@
1
- {"version":3,"file":"alarms.js","sourceRoot":"","sources":["alarms.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"alarms.js","sourceRoot":"","sources":["alarms.ts"],"names":[],"mappings":"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"}
@@ -18,7 +18,7 @@
18
18
  * behavior (focus-gated). OS-level notifications are a separate follow-up.
19
19
  */
20
20
 
21
- import { getCalendarEvents, getTasks, showReminderPopup } from "../lib/api-client.js";
21
+ import { getCalendarEvents, getTasks, showReminderPopup, deleteCalendarEvent } from "../lib/api-client.js";
22
22
 
23
23
  const DISMISSED_KEY = "mailx-alarm-dismissed"; // { uuid: true }
24
24
  const SNOOZED_KEY = "mailx-alarm-snoozed"; // { uuid: epoch-ms-end }
@@ -192,35 +192,134 @@ async function firePopupForItem(item: AlarmItem): Promise<void> {
192
192
  if (openPopups.has(item.uuid)) return;
193
193
  openPopups.add(item.uuid);
194
194
 
195
+ // Buttons map to msger's `buttons` array for the keyboard-default
196
+ // detection in template mode; in rawHtml we render our own controls,
197
+ // but the array still flows back as the default-button fallback if
198
+ // the user closes the window without picking. Order = priority.
199
+ const buttons: string[] = ["snooze", "Dismiss"];
200
+ if (item.htmlLink) buttons.push("Open");
201
+ if (item.kind === "calendar") buttons.push("Delete");
202
+
203
+ // rawHtml mode. msger's design intent is that we deliver any HTML
204
+ // form and the parent process receives whatever `window.ipc.postMessage`
205
+ // serializes — `MessageBoxResult` includes a `form: serde_json::Value`
206
+ // field for exactly this. We mirror Thunderbird's snooze popup: a
207
+ // row of preset chips PLUS a custom "N units" form. Snooze submits
208
+ // `{button:"snooze", form:{minutes:N}}` so the daemon can handle any
209
+ // duration; Dismiss/Open/Delete still discriminate by button label.
210
+ const icon = item.kind === "calendar" ? "📅" : "☑";
211
+ const kindLabel = item.kind === "calendar" ? "Calendar event" : "Task due";
212
+ // Presets in minutes. Tuned to TBird's defaults — covers the common
213
+ // "give me 15 more minutes" through "remind me tomorrow morning."
214
+ const presets: Array<{ label: string; minutes: number }> = [
215
+ { label: "5m", minutes: 5 },
216
+ { label: "15m", minutes: 15 },
217
+ { label: "30m", minutes: 30 },
218
+ { label: "1h", minutes: 60 },
219
+ { label: "2h", minutes: 120 },
220
+ { label: "1d", minutes: 60 * 24 },
221
+ ];
222
+ const presetChips = presets.map(p =>
223
+ `<button type="button" class="chip" data-snooze-min="${p.minutes}">${p.label}</button>`
224
+ ).join("");
225
+ const actionBtns: string[] = ["Dismiss"];
226
+ if (item.htmlLink) actionBtns.push("Open");
227
+ if (item.kind === "calendar") actionBtns.push("Delete");
228
+ const actionHtml = actionBtns.map(b =>
229
+ `<button type="button" class="action" data-btn="${escapeHtml(b)}">${escapeHtml(b)}</button>`
230
+ ).join("");
231
+
195
232
  const html = `<!DOCTYPE html>
196
233
  <html><head><meta charset="utf-8"><style>
197
- body { font: 13px system-ui, sans-serif; margin: 0; padding: 16px 18px; background: #fff; color: #222; }
198
- .icon { display: inline-block; width: 18px; text-align: center; margin-right: 4px; }
199
- .title { font-size: 1.05em; font-weight: 600; margin-bottom: 6px; }
200
- .when { color: #555; font-size: 0.95em; margin-bottom: 4px; }
234
+ html, body { height: 100%; }
235
+ body { font: 13px system-ui, sans-serif; margin: 0; padding: 14px 16px; background: #fff; color: #222; display:flex; flex-direction:column; box-sizing:border-box; gap: 8px; }
236
+ .icon { display: inline-block; width: 20px; text-align: center; margin-right: 4px; }
237
+ .title { font-size: 1.1em; font-weight: 600; }
238
+ .when { color: #555; font-size: 0.95em; }
201
239
  .kind { color: #888; font-size: 0.85em; }
240
+ .spacer { flex: 1; }
241
+ .row { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
242
+ .row-label { color: #555; font-size: 0.85em; margin-right: 2px; }
243
+ button { font: 13px system-ui, sans-serif; cursor: pointer; }
244
+ .chip { padding: 3px 9px; border: 1px solid #ccc; border-radius: 12px; background: #f7f7f7; color: #222; }
245
+ .chip:hover { background: #e8f0fe; border-color: #0066cc; color: #0066cc; }
246
+ .custom { display: flex; align-items: center; gap: 4px; }
247
+ .custom input[type=number] { width: 48px; padding: 2px 4px; border: 1px solid #ccc; border-radius: 3px; font: inherit; }
248
+ .custom select { padding: 2px 4px; border: 1px solid #ccc; border-radius: 3px; font: inherit; background: #fff; }
249
+ .custom button.ok { padding: 2px 8px; border: 1px solid #0066cc; border-radius: 3px; background: #0066cc; color: #fff; }
250
+ .custom button.ok:hover { background: #0052a3; }
251
+ .actions { display: flex; gap: 8px; justify-content: flex-end; margin-top: 4px; }
252
+ .actions button.action { padding: 5px 14px; border: none; border-radius: 4px; background: #0066cc; color: #fff; min-width: 70px; }
253
+ .actions button.action:hover { background: #0052a3; }
254
+ .actions button.action[data-btn="Delete"] { background: #b00; }
255
+ .actions button.action[data-btn="Delete"]:hover { background: #800; }
202
256
  </style></head><body>
203
- <div class="title"><span class="icon">${item.kind === "calendar" ? "📅" : "☑"}</span>${escapeHtml(item.title)}</div>
257
+ <div class="title"><span class="icon">${icon}</span>${escapeHtml(item.title)}</div>
204
258
  <div class="when">${escapeHtml(formatWhen(item.whenMs))}</div>
205
- <div class="kind">${item.kind === "calendar" ? "Calendar event" : "Task due"}</div>
259
+ <div class="kind">${kindLabel}</div>
260
+ <div class="row">
261
+ <span class="row-label">Snooze:</span>
262
+ ${presetChips}
263
+ </div>
264
+ <div class="row custom">
265
+ <span class="row-label">Custom:</span>
266
+ <input type="number" id="customN" value="5" min="1" max="999">
267
+ <select id="customUnit">
268
+ <option value="1">minutes</option>
269
+ <option value="60">hours</option>
270
+ <option value="1440">days</option>
271
+ </select>
272
+ <button type="button" class="ok" id="customGo">Snooze</button>
273
+ </div>
274
+ <div class="spacer"></div>
275
+ <div class="actions">${actionHtml}</div>
276
+ <script>
277
+ function send(payload) {
278
+ try { window.ipc.postMessage(JSON.stringify(payload)); }
279
+ catch (e) { try { window.close(); } catch (_) {} }
280
+ }
281
+ // Preset chips → snooze N minutes
282
+ document.querySelectorAll("button[data-snooze-min]").forEach(function(b) {
283
+ b.addEventListener("click", function() {
284
+ var m = parseInt(b.getAttribute("data-snooze-min"), 10) || 15;
285
+ send({ button: "snooze", form: { minutes: m } });
286
+ });
287
+ });
288
+ // Custom snooze form → snooze N * unit-multiplier minutes
289
+ var customGo = document.getElementById("customGo");
290
+ var customN = document.getElementById("customN");
291
+ var customUnit = document.getElementById("customUnit");
292
+ function submitCustom() {
293
+ var n = parseInt(customN.value, 10);
294
+ if (!isFinite(n) || n < 1) n = 5;
295
+ var mult = parseInt(customUnit.value, 10) || 1;
296
+ send({ button: "snooze", form: { minutes: n * mult } });
297
+ }
298
+ customGo.addEventListener("click", submitCustom);
299
+ customN.addEventListener("keydown", function(e) { if (e.key === "Enter") submitCustom(); });
300
+ // Action buttons (Dismiss/Open/Delete) → discriminate by label.
301
+ document.querySelectorAll("button[data-btn]").forEach(function(b) {
302
+ b.addEventListener("click", function() {
303
+ send({ button: b.getAttribute("data-btn") });
304
+ });
305
+ });
306
+ </script>
206
307
  </body></html>`;
207
308
 
208
- // Button labels. Open opens the htmlLink (Google Calendar editor)
209
- // and clears all local alarm state so the next poll tick treats the
210
- // event as a fresh entry — if the user moves the start time during
211
- // the edit, the new alarm fires off the new time, not the old one.
212
- // Tasks have no htmlLink so Open is skipped for them.
213
- const buttons: string[] = ["Snooze 15m", "Snooze 1h", "Dismiss"];
214
- if (item.htmlLink) buttons.push("Open");
215
-
216
309
  const title = item.kind === "calendar" ? "Calendar reminder" : "Task reminder";
217
310
  const hasHost = !!(window as any).mailxapi?.showReminderPopup;
218
- let r: { button: string };
311
+ // Result now carries an optional `form` object — see msger's
312
+ // MessageBoxResult.form (serde_json::Value passthrough). Snooze
313
+ // chips/inputs send `{ button: "snooze", form: { minutes: N } }`.
314
+ let r: { button: string; form?: { minutes?: number } };
219
315
  try {
220
316
  if (hasHost) {
221
317
  r = await showReminderPopup({
222
318
  title, html, buttons,
223
- size: { width: 480, height: 210 },
319
+ // Sized for the new snooze form (chips row + custom input
320
+ // row + action buttons) plus title/when/kind. Width fits
321
+ // "5m 15m 30m 1h 2h 1d" on one line in Segoe UI.
322
+ size: { width: 560, height: 300 },
224
323
  });
225
324
  } else {
226
325
  r = await showInWebViewPopup({ title, html, buttons });
@@ -256,11 +355,33 @@ async function firePopupForItem(item: AlarmItem): Promise<void> {
256
355
  firedThisSession.delete(item.uuid);
257
356
  snoozeItem(item, 2);
258
357
  break;
358
+ case "snooze": {
359
+ // Generic snooze, duration in `form.minutes`. Falls back to
360
+ // 15 min if the form is malformed or missing the value (the
361
+ // chip / custom-form JS always populates it, so this is just
362
+ // defense-in-depth).
363
+ const m = Math.max(1, Math.floor(r.form?.minutes ?? 15));
364
+ snoozeItem(item, m);
365
+ break;
366
+ }
367
+ // Legacy literal labels — retained so a pre-upgrade popup whose
368
+ // bundle still says "Snooze 15m" / "Snooze 1h" continues to work
369
+ // through a daemon-side rebuild without a UI restart.
259
370
  case "Snooze 15m": snoozeItem(item, 15); break;
260
371
  case "Snooze 1h": snoozeItem(item, 60); break;
261
372
  case "Dismiss":
262
373
  { const m = loadDismissed(); m[item.uuid] = true; saveDismissed(m); }
263
374
  break;
375
+ case "Delete":
376
+ // Remove the event server-side. Local row gets the pending-delete
377
+ // mark inside the service; reconcile picks it up. Also dismiss
378
+ // the alarm so it can't re-fire on a stale local row before the
379
+ // delete propagates.
380
+ { const m = loadDismissed(); m[item.uuid] = true; saveDismissed(m); }
381
+ firedThisSession.delete(item.uuid);
382
+ deleteCalendarEvent(item.uuid).catch((e: any) =>
383
+ console.error(` [alarm] delete failed for ${item.uuid}: ${e?.message || e}`));
384
+ break;
264
385
  default:
265
386
  // User closed the window via X / Esc / "closed" / "dismissed"
266
387
  // without picking a labeled button. Treat as a 15-minute
@@ -13,7 +13,7 @@
13
13
  * All storage goes through the service-side two-way cache (calendar_events
14
14
  * and tasks tables); this file does not use localStorage for data.
15
15
  */
16
- import { getCalendarEvents, getTasks, createTask, updateTask, deleteTask, reauthGoogleScopes, } from "../lib/api-client.js";
16
+ import { getCalendarEvents, getTasks, createTask, updateTask, deleteTask, reauthGoogleScopes, getSettings, saveSettings, } from "../lib/api-client.js";
17
17
  import { showContextMenu } from "./context-menu.js";
18
18
  const SIDEBAR_PREF = "mailx-calendar-sidebar-on";
19
19
  const SHOW_RECURRING_PREF = "mailx-cal-show-recurring";
@@ -90,6 +90,7 @@ async function fetchUpcoming(from) {
90
90
  source: r.providerId ? "google" : "local",
91
91
  recurringEventId: r.recurringEventId,
92
92
  htmlLink: r.htmlLink,
93
+ isHoliday: !!r.isHoliday,
93
94
  }));
94
95
  }
95
96
  function formatDayHeader(d, today, tomorrow) {
@@ -191,8 +192,14 @@ function renderEvents(events) {
191
192
  }
192
193
  const recurMark = e.recurringEventId ? `<span class="cal-side-event-recur" title="Recurring event">↻</span>` : "";
193
194
  const link = e.htmlLink || "";
194
- html += `<div class="cal-side-event" data-id="${e.id}" data-link="${escapeHtml(link)}" ${link ? 'title="Click to open in Google Calendar"' : ""}>
195
- <span class="cal-side-event-dot ${e.source === "google" ? "g" : "l"}"></span>
195
+ const holidayAttr = e.isHoliday ? ' data-holiday="1"' : "";
196
+ // Holidays don't get the Google Calendar deep-link (it's read-only) —
197
+ // suppress data-link and the "Click to open" hint for those rows.
198
+ const clickable = !e.isHoliday;
199
+ const titleAttr = clickable && link ? 'title="Click to open in Google Calendar"' : "";
200
+ const dataLink = clickable ? `data-link="${escapeHtml(link)}"` : "";
201
+ html += `<div class="cal-side-event" data-id="${e.id}"${holidayAttr} ${dataLink} ${titleAttr}>
202
+ <span class="cal-side-event-dot ${e.isHoliday ? "h" : (e.source === "google" ? "g" : "l")}"></span>
196
203
  <span class="cal-side-event-time">${escapeHtml(formatTime(e))}</span>
197
204
  <span class="cal-side-event-title">${escapeHtml(e.title)}${recurMark}</span>
198
205
  </div>`;
@@ -624,6 +631,35 @@ export function initCalendarSidebar() {
624
631
  refresh();
625
632
  });
626
633
  }
634
+ // Holiday-calendar toggles. Each lives in service settings (not
635
+ // localStorage) because the daemon's refresh loop decides which
636
+ // calendars to pull — the preference has to round-trip to it.
637
+ // Two separate Google public calendars today: US federal holidays
638
+ // and Jewish holidays. Generalization to arbitrary user-supplied
639
+ // calendars is filed as Q140 (low priority unless the audience
640
+ // broadens beyond Bob).
641
+ const wireHolidayCheckbox = (cbId, settingsKey) => {
642
+ const cb = document.getElementById(cbId);
643
+ if (!cb || cb.__wired)
644
+ return;
645
+ cb.__wired = true;
646
+ getSettings().then((s) => {
647
+ cb.checked = !!s?.calendar?.[settingsKey];
648
+ }).catch(() => { });
649
+ cb.addEventListener("change", async () => {
650
+ try {
651
+ const s = await getSettings();
652
+ s.calendar = { ...(s.calendar || {}), [settingsKey]: cb.checked };
653
+ await saveSettings(s);
654
+ }
655
+ catch (e) {
656
+ console.error(`[cal] ${settingsKey} save failed:`, e);
657
+ }
658
+ refresh();
659
+ });
660
+ };
661
+ wireHolidayCheckbox("cal-side-show-holidays", "showHolidays");
662
+ wireHolidayCheckbox("cal-side-show-jewish-holidays", "showJewishHolidays");
627
663
  // Horizon input — how many days ahead to list events. Bounded 1..365.
628
664
  const horizonInput = document.getElementById("cal-side-horizon");
629
665
  if (horizonInput && !horizonInput.__wired) {