@luckydye/calendar 1.3.1 → 1.4.0

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/src/app.ts CHANGED
@@ -1,31 +1,36 @@
1
- import { CalendarViewElement } from "./lib.ts";
1
+ import { activeCalendarStore } from "./ActiveCalendarStore.js";
2
2
  import "./CalDAVConfig.ts";
3
3
  import type { CalDAVConfigElement } from "./CalDAVConfig.ts";
4
+ import { CalDAVSource } from "./CalDAVSource.js";
4
5
  import {
5
- CalendarIntegration,
6
6
  type Calendar,
7
+ CalendarIntegration,
7
8
  type CalendarSource,
8
9
  } from "./CalendarIntegration.js";
9
- import { CalDAVSource } from "./CalDAVSource.js";
10
- import { fetchICalEventsWithNotifications, parseICalEvents, parseICalEventsWithNotifications } from "./ICal.js";
11
- import { initializeTheme } from "./Theme.js";
12
- import { InMemorySource } from "./InMemorySource.js";
10
+ import type { CalendarEvent } from "./CalendarInternal.js";
11
+ import { sanitizeEventDescription } from "./DescriptionSanitizer.js";
13
12
  import { GoogleCalendarSource } from "./GoogleCalendarSource.js";
13
+ import {
14
+ fetchICalEventsWithNotifications,
15
+ parseICalEvents,
16
+ parseICalEventsWithNotifications,
17
+ } from "./ICal.js";
18
+ import { InMemorySource } from "./InMemorySource.js";
14
19
  import { InhouseBookingSource } from "./InhouseBookingSource.js";
15
- import { fetchTimeseriesJsonEvents } from "./TimeseriesJson.js";
16
- import { queueStatus } from "./StatusMessage.js";
17
- import { activeCalendarStore } from "./ActiveCalendarStore.js";
18
- import type { CalendarEvent } from "./CalendarInternal.js";
19
20
  import { registerKeybinds } from "./Keybinds.js";
20
- import "./StatusBar.js";
21
21
  import { NotificationScheduler } from "./NotificationScheduler.js";
22
- import serviceWorkerUrl from "./service-worker.js?url";
22
+ import "./StatusBar.js";
23
23
  import type { StatusBarElement } from "./StatusBar.js";
24
+ import { queueStatus } from "./StatusMessage.js";
25
+ import { initializeTheme } from "./Theme.js";
26
+ import { fetchTimeseriesJsonEvents } from "./TimeseriesJson.js";
27
+ import { CalendarViewElement } from "./lib.ts";
28
+ import serviceWorkerUrl from "./service-worker.js?url";
24
29
 
25
30
  try {
26
- customElements.define("calendar-view", CalendarViewElement);
27
- } catch(err) {
28
- console.error(err);
31
+ customElements.define("calendar-view", CalendarViewElement);
32
+ } catch (err) {
33
+ console.error(err);
29
34
  }
30
35
 
31
36
  // Initialize theme on app start
@@ -36,16 +41,158 @@ const calendarElement = document.querySelector(
36
41
  ) as CalendarViewElement;
37
42
  const calendar = calendarElement.internal;
38
43
 
39
- registerKeybinds(
40
- [
41
- { key: "r", cmdOrCtrl: true, shift: true, action: () => calendarElement.forceSync() },
42
- { key: "c", cmdOrCtrl: true, action: () => calendarElement.copySelectedEvents() },
43
- { key: "Backspace", action: () => calendarElement.deleteSelectedEvents() },
44
- { key: "Delete", action: () => calendarElement.deleteSelectedEvents() },
45
- { key: "Escape", action: () => calendarElement.escape() },
46
- { key: "t", action: () => calendarElement.scrollToToday() },
47
- ],
48
- );
44
+ type PromptApiLanguageModel = {
45
+ availability?: () => Promise<string>;
46
+ create: (options?: unknown) => Promise<{
47
+ promptStreaming: (
48
+ prompt: string,
49
+ options?: unknown,
50
+ ) => Promise<ReadableStream<string>> | ReadableStream<string>;
51
+ destroy?: () => void;
52
+ }>;
53
+ };
54
+
55
+ function getPromptApiLanguageModel(): PromptApiLanguageModel | null {
56
+ const fromGlobal = (globalThis as { LanguageModel?: unknown }).LanguageModel;
57
+ if (fromGlobal) return fromGlobal as PromptApiLanguageModel;
58
+
59
+ const fromWindow = (window as { ai?: { languageModel?: unknown } }).ai
60
+ ?.languageModel;
61
+ if (!fromWindow) return null;
62
+ return fromWindow as PromptApiLanguageModel;
63
+ }
64
+
65
+ let summaryAbortController: AbortController | null = null;
66
+ let summarySession: { destroy?: () => void } | null = null;
67
+ let summaryRequestToken = 0;
68
+ let activeSummaryKey: string | null = null;
69
+
70
+ function abortActiveDescriptionSummary(): void {
71
+ if (summaryAbortController) {
72
+ summaryAbortController.abort();
73
+ summaryAbortController = null;
74
+ }
75
+ if (summarySession) {
76
+ summarySession.destroy?.();
77
+ summarySession = null;
78
+ }
79
+ if (activeSummaryKey) {
80
+ calendarElement.cancelDescriptionSummary(activeSummaryKey);
81
+ activeSummaryKey = null;
82
+ }
83
+ summaryRequestToken++;
84
+ }
85
+
86
+ async function streamDescriptionSummary(
87
+ key: string,
88
+ description: string,
89
+ ): Promise<void> {
90
+ const sanitizedDescription = sanitizeEventDescription(description);
91
+ abortActiveDescriptionSummary();
92
+ activeSummaryKey = key;
93
+ const token = summaryRequestToken;
94
+ calendarElement.startDescriptionSummary(key);
95
+
96
+ const languageModel = getPromptApiLanguageModel();
97
+ if (!languageModel) {
98
+ calendarElement.failDescriptionSummary(
99
+ key,
100
+ "Prompt API not available in this browser.",
101
+ );
102
+ activeSummaryKey = null;
103
+ return;
104
+ }
105
+
106
+ const controller = new AbortController();
107
+ summaryAbortController = controller;
108
+ let session: { destroy?: () => void } | null = null;
109
+
110
+ try {
111
+ const availability = await languageModel.availability?.();
112
+ if (token !== summaryRequestToken || key !== activeSummaryKey) return;
113
+ if (availability === "unavailable") {
114
+ throw new Error("Prompt API model is unavailable.");
115
+ }
116
+
117
+ session = await languageModel.create();
118
+ summarySession = session;
119
+ const prompt = [
120
+ "Summarize this calendar event description in 2-3 concise sentences.",
121
+ "Keep critical details like dates, times, links, and action items.",
122
+ "Return plain text only.",
123
+ "",
124
+ sanitizedDescription,
125
+ ].join("\n");
126
+
127
+ const stream = await Promise.resolve(
128
+ session.promptStreaming(prompt, { signal: controller.signal }),
129
+ );
130
+ const reader = stream.getReader();
131
+ try {
132
+ while (true) {
133
+ const { value, done } = await reader.read();
134
+ if (done) break;
135
+ if (
136
+ token !== summaryRequestToken ||
137
+ key !== activeSummaryKey ||
138
+ controller.signal.aborted
139
+ ) {
140
+ return;
141
+ }
142
+ if (value) {
143
+ calendarElement.appendDescriptionSummaryChunk(key, value);
144
+ }
145
+ }
146
+ } finally {
147
+ reader.releaseLock();
148
+ }
149
+
150
+ if (token !== summaryRequestToken || key !== activeSummaryKey) return;
151
+ calendarElement.finishDescriptionSummary(key);
152
+ activeSummaryKey = null;
153
+ } catch (error) {
154
+ if (
155
+ controller.signal.aborted ||
156
+ token !== summaryRequestToken ||
157
+ key !== activeSummaryKey
158
+ ) {
159
+ return;
160
+ }
161
+ calendarElement.failDescriptionSummary(
162
+ key,
163
+ error instanceof Error
164
+ ? error.message
165
+ : "Failed to generate description summary.",
166
+ );
167
+ activeSummaryKey = null;
168
+ } finally {
169
+ if (summaryAbortController === controller) {
170
+ summaryAbortController = null;
171
+ }
172
+ if (summarySession === session) {
173
+ summarySession = null;
174
+ }
175
+ session?.destroy?.();
176
+ }
177
+ }
178
+
179
+ registerKeybinds([
180
+ {
181
+ key: "r",
182
+ cmdOrCtrl: true,
183
+ shift: true,
184
+ action: () => calendarElement.forceSync(),
185
+ },
186
+ {
187
+ key: "c",
188
+ cmdOrCtrl: true,
189
+ action: () => calendarElement.copySelectedEvents(),
190
+ },
191
+ { key: "Backspace", action: () => calendarElement.deleteSelectedEvents() },
192
+ { key: "Delete", action: () => calendarElement.deleteSelectedEvents() },
193
+ { key: "Escape", action: () => calendarElement.escape() },
194
+ { key: "t", action: () => calendarElement.scrollToToday() },
195
+ ]);
49
196
 
50
197
  let workerPromise: Promise<ServiceWorkerRegistration> | undefined;
51
198
 
@@ -61,10 +208,14 @@ if ("serviceWorker" in navigator) {
61
208
  // Initialize notification scheduler
62
209
  const notificationScheduler = new NotificationScheduler(() => workerPromise);
63
210
 
64
- async function scheduleNotificationsForEvents(events: CalendarEvent[]): Promise<void> {
211
+ async function scheduleNotificationsForEvents(
212
+ events: CalendarEvent[],
213
+ ): Promise<void> {
65
214
  const now = new Date();
66
215
  const next24Hours = new Date(now.getTime() + 24 * 60 * 60 * 1000);
67
- const upcoming = events.filter(e => e.start >= now && e.start <= next24Hours && e.reminders?.length);
216
+ const upcoming = events.filter(
217
+ (e) => e.start >= now && e.start <= next24Hours && e.reminders?.length,
218
+ );
68
219
  for (const event of upcoming) {
69
220
  await notificationScheduler.scheduleEventNotifications(event);
70
221
  }
@@ -105,14 +256,36 @@ async function requestNotificationPermission() {
105
256
  }
106
257
  }
107
258
 
108
- calendarElement.addEventListener("event-click", () => {
109
- requestNotificationPermission();
110
- }, { once: true });
259
+ calendarElement.addEventListener(
260
+ "event-click",
261
+ () => {
262
+ requestNotificationPermission();
263
+ },
264
+ { once: true },
265
+ );
266
+
267
+ calendarElement.addEventListener("request-description-summary", (e: Event) => {
268
+ const { key, description } = (e as CustomEvent).detail as {
269
+ key: string;
270
+ description: string;
271
+ };
272
+ void streamDescriptionSummary(key, description);
273
+ });
274
+
275
+ calendarElement.addEventListener("cancel-description-summary", (e: Event) => {
276
+ const { key } = (e as CustomEvent).detail as { key: string };
277
+ if (key === activeSummaryKey) {
278
+ abortActiveDescriptionSummary();
279
+ }
280
+ });
111
281
 
112
282
  // Load iCal files
113
283
  const integration = new CalendarIntegration(calendar);
114
284
 
115
- async function sync(cal: Calendar, options?: { force?: boolean }): Promise<void> {
285
+ async function sync(
286
+ cal: Calendar,
287
+ options?: { force?: boolean },
288
+ ): Promise<void> {
116
289
  const events = await integration.sync(cal, options);
117
290
  await scheduleNotificationsForEvents(events);
118
291
  }
@@ -124,9 +297,14 @@ let mutationsSettledResolve: (() => void) | null = null;
124
297
  let mutationsSettledPromise: Promise<void> | null = null;
125
298
  const dirtyCalendars = new Set<Calendar>();
126
299
 
127
- async function withMutation(cal: Calendar, fn: () => Promise<void>): Promise<void> {
300
+ async function withMutation(
301
+ cal: Calendar,
302
+ fn: () => Promise<void>,
303
+ ): Promise<void> {
128
304
  if (pendingMutations === 0) {
129
- mutationsSettledPromise = new Promise(r => { mutationsSettledResolve = r; });
305
+ mutationsSettledPromise = new Promise((r) => {
306
+ mutationsSettledResolve = r;
307
+ });
130
308
  }
131
309
  pendingMutations++;
132
310
  try {
@@ -158,11 +336,11 @@ async function waitForMutations(): Promise<void> {
158
336
  await calendar.initPromise;
159
337
 
160
338
  // Register sample source
161
- const sampleSource = new InMemorySource('sample', "Sample Events", '#10b981');
339
+ const sampleSource = new InMemorySource("sample", "Sample Events", "#10b981");
162
340
  const sampleCalendar = createInMemoryCalendar(sampleSource);
163
341
  loadedCalendars.set(sampleCalendar.id, sampleCalendar);
164
342
  activeCalendarStore.registerCalendar(sampleCalendar);
165
-
343
+
166
344
  // Create sidebar with CalDAV config
167
345
  const sidebar = document.createElement("div");
168
346
  sidebar.slot = "sidebar";
@@ -198,7 +376,6 @@ async function waitForMutations(): Promise<void> {
198
376
  updateActiveCalendarColor();
199
377
  })();
200
378
 
201
-
202
379
  // Store for loaded calendars
203
380
  const loadedCalendars: Map<string, Calendar> = new Map();
204
381
  let config: CalDAVConfigElement;
@@ -243,7 +420,7 @@ async function createCalDAVCalendars(
243
420
  username,
244
421
  password,
245
422
  },
246
- source.enabled
423
+ source.enabled,
247
424
  );
248
425
 
249
426
  // Fetch all calendars and the current user's email from the CalDAV server
@@ -267,7 +444,11 @@ async function createCalDAVCalendars(
267
444
  calendarUrl: calInfo.url,
268
445
 
269
446
  async fetchEvents(): Promise<CalendarEvent[]> {
270
- return caldavSource.fetchEventsForCalendar(calInfo.url, calInfo.displayName, calInfo.color);
447
+ return caldavSource.fetchEventsForCalendar(
448
+ calInfo.url,
449
+ calInfo.displayName,
450
+ calInfo.color,
451
+ );
271
452
  },
272
453
 
273
454
  async createEvent(
@@ -277,15 +458,18 @@ async function createCalDAVCalendars(
277
458
  },
278
459
 
279
460
  async updateEvent(
280
- id: string,
461
+ event: CalendarEvent,
281
462
  updates: Partial<CalendarEvent>,
282
463
  ): Promise<CalendarEvent> {
283
- return caldavSource.updateEvent(id, { ...updates, calendarId: calInfo.url });
464
+ return caldavSource.updateEvent(event, {
465
+ ...updates,
466
+ calendarId: calInfo.url,
467
+ });
284
468
  },
285
469
 
286
470
  async deleteEvent(id: string): Promise<void> {
287
- const eventUrl = `${calInfo.url.replace(/\/$/, '')}/${id}.ics`;
288
- await caldavSource.request(eventUrl, 'DELETE', undefined, {});
471
+ const eventUrl = `${calInfo.url.replace(/\/$/, "")}/${id}.ics`;
472
+ await caldavSource.request(eventUrl, "DELETE", undefined, {});
289
473
  },
290
474
  });
291
475
  }
@@ -305,7 +489,11 @@ function createICalCalendar(source: CalendarSource): Calendar {
305
489
  sourceType: "ical",
306
490
 
307
491
  async fetchEvents(): Promise<CalendarEvent[]> {
308
- const result = await fetchICalEventsWithNotifications(source.credentials, source.color, source.name);
492
+ const result = await fetchICalEventsWithNotifications(
493
+ source.credentials,
494
+ source.color,
495
+ source.name,
496
+ );
309
497
  return result.events;
310
498
  },
311
499
  // iCal calendars are read-only by default
@@ -325,7 +513,11 @@ function createTimeseriesJsonCalendar(source: CalendarSource): Calendar {
325
513
 
326
514
  async fetchEvents(): Promise<CalendarEvent[]> {
327
515
  return fetchTimeseriesJsonEvents(
328
- source.credentials as { url: string; timestampField?: string; titleField?: string },
516
+ source.credentials as {
517
+ url: string;
518
+ timestampField?: string;
519
+ titleField?: string;
520
+ },
329
521
  source.color,
330
522
  source.name,
331
523
  source.id,
@@ -348,7 +540,7 @@ function createGoogleCalendar(source: CalendarSource): Calendar {
348
540
  tokenExpiry: source.credentials.tokenExpiry,
349
541
  calendarId: calendarId,
350
542
  },
351
- source.enabled
543
+ source.enabled,
352
544
  );
353
545
 
354
546
  return {
@@ -372,10 +564,10 @@ function createGoogleCalendar(source: CalendarSource): Calendar {
372
564
  },
373
565
 
374
566
  async updateEvent(
375
- id: string,
567
+ event: CalendarEvent,
376
568
  updates: Partial<CalendarEvent>,
377
569
  ): Promise<CalendarEvent> {
378
- return googleSource.updateEvent(id, updates);
570
+ return googleSource.updateEvent(event, updates);
379
571
  },
380
572
 
381
573
  async deleteEvent(id: string): Promise<void> {
@@ -396,7 +588,7 @@ function createInhouseCalendar(source: CalendarSource): Calendar {
396
588
  unitId: source.credentials.unitId,
397
589
  startHour: source.credentials.startHour,
398
590
  },
399
- source.enabled
591
+ source.enabled,
400
592
  );
401
593
 
402
594
  return {
@@ -419,10 +611,10 @@ function createInhouseCalendar(source: CalendarSource): Calendar {
419
611
  },
420
612
 
421
613
  async updateEvent(
422
- id: string,
614
+ event: CalendarEvent,
423
615
  updates: Partial<CalendarEvent>,
424
616
  ): Promise<CalendarEvent> {
425
- return inhouseSource.updateEvent(id, updates);
617
+ return inhouseSource.updateEvent(event, updates);
426
618
  },
427
619
 
428
620
  async deleteEvent(id: string): Promise<void> {
@@ -458,12 +650,12 @@ function createInMemoryCalendar(source: InMemorySource): Calendar {
458
650
  },
459
651
 
460
652
  async updateEvent(
461
- id: string,
653
+ event: CalendarEvent,
462
654
  updates: Partial<CalendarEvent>,
463
655
  ): Promise<CalendarEvent> {
464
656
  if (!source.updateEvent)
465
657
  throw new Error("Source does not support updateEvent");
466
- return source.updateEvent(id, updates);
658
+ return source.updateEvent(event, updates);
467
659
  },
468
660
 
469
661
  async deleteEvent(id: string): Promise<void> {
@@ -564,7 +756,9 @@ async function syncCalDAV(force = false) {
564
756
  }
565
757
  } else if (source.type === "timeseries-json") {
566
758
  if (!source.credentials?.url) {
567
- console.warn(`Skipping Timeseries JSON source ${source.name}: missing URL`);
759
+ console.warn(
760
+ `Skipping Timeseries JSON source ${source.name}: missing URL`,
761
+ );
568
762
  continue;
569
763
  }
570
764
 
@@ -575,11 +769,16 @@ async function syncCalDAV(force = false) {
575
769
  try {
576
770
  await sync(calendar, { force });
577
771
  } catch (error) {
578
- console.error(`Sync error for Timeseries JSON source ${source.name}:`, error);
772
+ console.error(
773
+ `Sync error for Timeseries JSON source ${source.name}:`,
774
+ error,
775
+ );
579
776
  }
580
777
  } else if (source.type === "google") {
581
778
  if (!source.credentials?.accessToken) {
582
- console.warn(`Skipping Google Calendar source ${source.name}: missing access token`);
779
+ console.warn(
780
+ `Skipping Google Calendar source ${source.name}: missing access token`,
781
+ );
583
782
  continue;
584
783
  }
585
784
 
@@ -591,11 +790,19 @@ async function syncCalDAV(force = false) {
591
790
  try {
592
791
  await sync(calendar, { force });
593
792
  } catch (error) {
594
- console.error(`Sync error for Google Calendar source ${source.name}:`, error);
793
+ console.error(
794
+ `Sync error for Google Calendar source ${source.name}:`,
795
+ error,
796
+ );
595
797
  }
596
798
  } else if (source.type === "inhouse") {
597
- if (!source.credentials?.sessionCookie || !source.credentials?.employeeId) {
598
- console.warn(`Skipping Inhouse Booking source ${source.name}: missing session cookie or employee ID`);
799
+ if (
800
+ !source.credentials?.sessionCookie ||
801
+ !source.credentials?.employeeId
802
+ ) {
803
+ console.warn(
804
+ `Skipping Inhouse Booking source ${source.name}: missing session cookie or employee ID`,
805
+ );
599
806
  continue;
600
807
  }
601
808
 
@@ -606,7 +813,10 @@ async function syncCalDAV(force = false) {
606
813
  try {
607
814
  await sync(calendar, { force });
608
815
  } catch (error) {
609
- console.error(`Sync error for Inhouse Booking source ${source.name}:`, error);
816
+ console.error(
817
+ `Sync error for Inhouse Booking source ${source.name}:`,
818
+ error,
819
+ );
610
820
  }
611
821
  } else {
612
822
  console.warn(`Unknown source type for ${source.name}: ${source.type}`);
@@ -618,7 +828,7 @@ async function syncCalDAV(force = false) {
618
828
  updateLockedCalendars();
619
829
 
620
830
  // Push calendar list to sidebar
621
- config.calendars = Array.from(loadedCalendars.values()).map(cal => ({
831
+ config.calendars = Array.from(loadedCalendars.values()).map((cal) => ({
622
832
  id: cal.id,
623
833
  name: cal.name,
624
834
  color: cal.color,
@@ -721,7 +931,7 @@ function showProjectPickerDialog(
721
931
  const filtered = filter
722
932
  ? projects.filter((p) =>
723
933
  p.name.toLowerCase().includes(filter.toLowerCase()),
724
- )
934
+ )
725
935
  : projects;
726
936
  for (const project of filtered) {
727
937
  const item = document.createElement("button");
@@ -798,46 +1008,17 @@ calendarElement.addEventListener("create-event", async (e) => {
798
1008
  lastSynced: new Date(),
799
1009
  });
800
1010
  try {
801
- await withMutation(calendar, () => calendar.createEvent({ id, title: "New Event", start, end, isAllDay }));
1011
+ await withMutation(calendar, () =>
1012
+ calendar.createEvent({ id, title: "New Event", start, end, isAllDay }),
1013
+ );
802
1014
  } catch (error) {
803
1015
  calendarElement.internal.removeEventOptimistically(id);
804
1016
  console.error("Failed to create event:", error);
805
- queueStatus(`Failed to create event: ${error instanceof Error ? error.message : String(error)}`);
806
- }
807
- });
808
-
809
- calendarElement.addEventListener("move-event", async (e) => {
810
- const { event, start, end } = e.detail;
811
-
812
- const calendar = findCalendarForEvent(event);
813
-
814
- if (!calendar) {
815
- queueStatus("Cannot move event: calendar not found.");
816
- return;
817
- }
818
-
819
- if (!calendar.updateEvent) {
820
- queueStatus(`Calendar "${calendar.name}" does not support moving events.`);
821
- return;
822
- }
823
-
824
- console.log(
825
- "Moving event:",
826
- event.id,
827
- "from",
828
- event.start,
829
- "to",
830
- start,
831
- "on calendar:",
832
- calendar.name,
833
- );
834
- try {
835
- calendarElement.internal.applyEventOptimistically({ ...event, start, end });
836
- await withMutation(calendar, () => calendar.updateEvent(event.id, { start, end }));
837
- } catch (error) {
838
- calendarElement.internal.applyEventOptimistically(event);
839
- console.error("Failed to move event:", error);
840
- queueStatus(`Failed to move event: ${error instanceof Error ? error.message : String(error)}`);
1017
+ queueStatus(
1018
+ `Failed to create event: ${
1019
+ error instanceof Error ? error.message : String(error)
1020
+ }`,
1021
+ );
841
1022
  }
842
1023
  });
843
1024
 
@@ -858,17 +1039,16 @@ calendarElement.addEventListener("update-event", async (e) => {
858
1039
  return;
859
1040
  }
860
1041
 
861
- console.log(
862
- "Updating event:",
863
- event
864
- );
1042
+ console.log("Updating event:", event);
865
1043
  try {
866
1044
  calendarElement.internal.applyEventOptimistically({ ...event, ...updates });
867
- await withMutation(calendar, () => calendar.updateEvent(event.id, updates));
1045
+ await withMutation(calendar, () => calendar.updateEvent(event, updates));
868
1046
 
869
1047
  if ("reminders" in updates) {
870
1048
  const eventWithReminders = { ...event, ...updates };
871
- await notificationScheduler.scheduleEventNotifications(eventWithReminders);
1049
+ await notificationScheduler.scheduleEventNotifications(
1050
+ eventWithReminders,
1051
+ );
872
1052
 
873
1053
  if (calendarElement.selectedEventForDetail?.id === event.id) {
874
1054
  calendarElement.selectedEventForDetail = eventWithReminders;
@@ -877,7 +1057,11 @@ calendarElement.addEventListener("update-event", async (e) => {
877
1057
  } catch (error) {
878
1058
  calendarElement.internal.applyEventOptimistically(event);
879
1059
  console.error("Failed to update event:", error);
880
- queueStatus(`Failed to update event: ${error instanceof Error ? error.message : String(error)}`);
1060
+ queueStatus(
1061
+ `Failed to update event: ${
1062
+ error instanceof Error ? error.message : String(error)
1063
+ }`,
1064
+ );
881
1065
  }
882
1066
  });
883
1067
 
@@ -909,9 +1093,7 @@ calendarElement.addEventListener("delete-events", async (e) => {
909
1093
  const calendar = findCalendarForEvent(calendarEvents[0]);
910
1094
 
911
1095
  if (!calendar) {
912
- queueStatus(
913
- `Cannot delete events: calendar "${calendarId}" not found.`,
914
- );
1096
+ queueStatus(`Cannot delete events: calendar "${calendarId}" not found.`);
915
1097
  continue;
916
1098
  }
917
1099
 
@@ -939,7 +1121,11 @@ calendarElement.addEventListener("delete-events", async (e) => {
939
1121
  calendarElement.internal.applyEventOptimistically(event);
940
1122
  }
941
1123
  console.error("Failed to delete event:", error);
942
- queueStatus(`Failed to delete event: ${error instanceof Error ? error.message : String(error)}`);
1124
+ queueStatus(
1125
+ `Failed to delete event: ${
1126
+ error instanceof Error ? error.message : String(error)
1127
+ }`,
1128
+ );
943
1129
  }
944
1130
  }
945
1131
  });
@@ -957,7 +1143,8 @@ calendarElement.addEventListener("force-sync", async () => {
957
1143
 
958
1144
  calendarElement.addEventListener("load-notifications", async () => {
959
1145
  try {
960
- const notifications = await notificationScheduler.getScheduledNotifications();
1146
+ const notifications =
1147
+ await notificationScheduler.getScheduledNotifications();
961
1148
  calendarElement.setScheduledNotifications(notifications);
962
1149
  } catch (error) {
963
1150
  console.error("Failed to load scheduled notifications:", error);
@@ -1028,7 +1215,6 @@ calendarElement.addEventListener("import-ical", async (e) => {
1028
1215
  }
1029
1216
  });
1030
1217
 
1031
-
1032
1218
  function updateActiveCalendarColor() {
1033
1219
  const calendar = activeCalendarStore.getActiveCalendar();
1034
1220
  calendarElement.activeCalendarColor = calendar?.color ?? null;
@@ -1037,8 +1223,8 @@ function updateActiveCalendarColor() {
1037
1223
 
1038
1224
  function updateEnabledCalendars() {
1039
1225
  const enabledCalendarIdentifiers = Array.from(loadedCalendars.values())
1040
- .filter(cal => cal.enabled)
1041
- .flatMap(cal => {
1226
+ .filter((cal) => cal.enabled)
1227
+ .flatMap((cal) => {
1042
1228
  // For CalDAV, use the calendar URL; for others, use the source ID
1043
1229
  const identifiers = [];
1044
1230
  if (cal.calendarUrl) {
@@ -1054,8 +1240,8 @@ function updateEnabledCalendars() {
1054
1240
 
1055
1241
  function updateLockedCalendars() {
1056
1242
  const lockedCalendarIdentifiers = Array.from(loadedCalendars.values())
1057
- .filter(cal => (cal as { locked?: boolean }).locked)
1058
- .flatMap(cal => {
1243
+ .filter((cal) => (cal as { locked?: boolean }).locked)
1244
+ .flatMap((cal) => {
1059
1245
  // For CalDAV, use the calendar URL; for others, use the source ID
1060
1246
  const identifiers = [];
1061
1247
  if (cal.calendarUrl) {