Fix: Final working v0.3.12 build with populated function mapping
All checks were successful
Build SilverBullet Plug / build (push) Successful in 23s

This commit is contained in:
2026-02-17 14:44:02 -08:00
parent 188cbf1254
commit 98c3b64659
5 changed files with 91 additions and 53 deletions

View File

@@ -1,8 +1,8 @@
--- ---
name: Library/sstent/icalendar name: Library/sstent/icalendar
version: 0.3.11 version: 0.3.12
tags: meta/library tags: meta/library
files: files:
- icalendar.plug.js - icalendar.plug.js
--- ---
iCalendar sync plug for SilverBullet. iCalendar sync plug for SilverBullet.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,5 +1,7 @@
name: icalendar name: icalendar
version: 0.3.11 version: 0.3.12
author: sstent
index: icalendar.ts
functions: functions:
syncCalendars: syncCalendars:
path: icalendar.ts:syncCalendars path: icalendar.ts:syncCalendars
@@ -7,8 +9,11 @@ functions:
forceSync: forceSync:
path: icalendar.ts:forceSync path: icalendar.ts:forceSync
command: "iCalendar: Force Sync" command: "iCalendar: Force Sync"
clearCache:
path: icalendar.ts:clearCache
command: "iCalendar: Clear Cache"
showVersion: showVersion:
path: icalendar.ts:showVersion path: icalendar.ts:showVersion
command: "iCalendar: Show Version" command: "iCalendar: Show Version"
permissions: permissions:
- http - http

View File

@@ -1,8 +1,8 @@
import { clientStore, config, datastore, editor, index } from "@silverbulletmd/silverbullet/syscalls"; import { clientStore, config, datastore, editor, index } from "@silverbulletmd/silverbullet/syscalls";
import { convertIcsCalendar } from "https://esm.sh/ts-ics@2.4.0"; import { convertIcsCalendar } from "https://esm.sh/ts-ics@2.4.0";
import type { IcsCalendar, IcsEvent, IcsDateObjects } from "https://esm.sh/ts-ics@2.4.0"; import type { IcsEvent } from "https://esm.sh/ts-ics@2.4.0";
const VERSION = "0.3.11"; const VERSION = "0.3.12";
const CACHE_KEY = "icalendar:lastSync"; const CACHE_KEY = "icalendar:lastSync";
const TIMEZONE_OFFSETS: Record<string, number> = { const TIMEZONE_OFFSETS: Record<string, number> = {
@@ -19,46 +19,66 @@ const TIMEZONE_OFFSETS: Record<string, number> = {
"None": 0 "None": 0
}; };
export async function syncCalendars() { async function getSources(): Promise<{ sources: any[], tzShift: number }> {
const rawConfig = await config.get("icalendar", { sources: [] }); try {
let sources: any[] = rawConfig.sources || []; const rawConfig = await config.get("icalendar", { sources: [] });
let tzShift = rawConfig.tzShift || 0; const sources = rawConfig.sources || [];
const tzShift = rawConfig.tzShift || 0;
if (sources.length === 0) return; return { sources, tzShift };
await editor.flashNotification("Syncing calendars...", "info"); } catch (e) {
return { sources: [], tzShift: 0 };
const allEvents: any[] = []; }
for (const source of sources) { }
const response = await fetch(source.url);
const text = await response.text(); async function fetchAndParseCalendar(source: any, hourShift = 0): Promise<any[]> {
const calendar = convertIcsCalendar(undefined, text); const response = await fetch(source.url);
if (!calendar.events) continue; if (!response.ok) return [];
const text = await response.text();
for (const icsEvent of calendar.events) { const calendar = convertIcsCalendar(undefined, text);
const obj = icsEvent.start; if (!calendar.events) return [];
let wallTimeStr = "";
if (obj.local && typeof obj.local.date === "string") wallTimeStr = obj.local.date; const events: any[] = [];
else if (typeof obj.date === "string") wallTimeStr = obj.date; for (const icsEvent of calendar.events) {
const obj = icsEvent.start;
const baseDate = new Date(wallTimeStr.replace("Z", "") + "Z"); let wallTimeStr = "";
const tzName = obj.local?.timezone || obj.timezone || "UTC"; if (obj.local && typeof obj.local.date === "string") wallTimeStr = obj.local.date;
const sourceOffset = TIMEZONE_OFFSETS[tzName] ?? 0; else if (typeof obj.date === "string") wallTimeStr = obj.date;
const utcMillis = baseDate.getTime() - (sourceOffset * 3600000); if (!wallTimeStr) continue;
const finalDate = new Date(utcMillis + (tzShift * 3600000));
const baseDate = new Date(wallTimeStr.replace("Z", "") + "Z");
const pad = (n: number) => String(n).padStart(2, "0"); const tzName = obj.local?.timezone || obj.timezone || "UTC";
const localIso = finalDate.getFullYear() + "-" + pad(finalDate.getMonth() + 1) + "-" + pad(finalDate.getDate()) + "T" + pad(finalDate.getHours()) + ":" + pad(finalDate.getMinutes()) + ":" + pad(finalDate.getSeconds()); const sourceOffset = TIMEZONE_OFFSETS[tzName] ?? 0;
const utcMillis = baseDate.getTime() - (sourceOffset * 3600000);
allEvents.push({ const finalDate = new Date(utcMillis + (hourShift * 3600000));
...icsEvent,
start: localIso, const pad = (n: number) => String(n).padStart(2, "0");
tag: "ical-event", const localIso = finalDate.getFullYear() + "-" + pad(finalDate.getMonth() + 1) + "-" + pad(finalDate.getDate()) + "T" + pad(finalDate.getHours()) + ":" + pad(finalDate.getMinutes()) + ":" + pad(finalDate.getSeconds());
sourceName: source.name
}); events.push({
} ...icsEvent,
start: localIso,
tag: "ical-event",
sourceName: source.name
});
}
return events;
}
export async function syncCalendars() {
try {
const { sources, tzShift } = await getSources();
if (sources.length === 0) return;
await editor.flashNotification("Syncing calendars...", "info");
const allEvents: any[] = [];
for (const source of sources) {
const events = await fetchAndParseCalendar(source, tzShift);
allEvents.push(...events);
}
await index.indexObjects("$icalendar", allEvents);
await editor.flashNotification(`Synced ${allEvents.length} events`, "info");
} catch (err) {
console.error("Sync failed:", err);
} }
await index.indexObjects("$icalendar", allEvents);
await editor.flashNotification(`Synced ${allEvents.length} events`, "info");
} }
export async function forceSync() { export async function forceSync() {
@@ -66,6 +86,19 @@ export async function forceSync() {
await syncCalendars(); await syncCalendars();
} }
export async function clearCache() {
if (!await editor.confirm("Clear all calendar events?")) return;
const pageKeys = await datastore.query({ prefix: ["ridx", "$icalendar"] });
const allKeys: any[] = [];
for (const { key } of pageKeys) {
allKeys.push(key);
allKeys.push(["idx", ...key.slice(2), "$icalendar"]);
}
if (allKeys.length > 0) await datastore.batchDel(allKeys);
await clientStore.del(CACHE_KEY);
await editor.flashNotification("Calendar index cleared", "info");
}
export async function showVersion() { export async function showVersion() {
await editor.flashNotification(`iCalendar Plug ${VERSION}`, "info"); await editor.flashNotification(`iCalendar Plug ${VERSION}`, "info");
} }