diff --git a/apps/client/package.json b/apps/client/package.json
index bcc591818d..4a9b78bb67 100644
--- a/apps/client/package.json
+++ b/apps/client/package.json
@@ -22,6 +22,7 @@
"@fullcalendar/interaction": "6.1.20",
"@fullcalendar/list": "6.1.20",
"@fullcalendar/multimonth": "6.1.20",
+ "@fullcalendar/rrule": "6.1.20",
"@fullcalendar/timegrid": "6.1.20",
"@maplibre/maplibre-gl-leaflet": "0.1.3",
"@mermaid-js/layout-elk": "0.2.0",
@@ -63,6 +64,7 @@
"react-i18next": "16.5.4",
"react-window": "2.2.7",
"reveal.js": "5.2.1",
+ "rrule": "2.8.1",
"svg-pan-zoom": "3.6.2",
"tabulator-tables": "6.3.1",
"vanilla-js-wheel-zoom": "9.0.4"
diff --git a/apps/client/src/widgets/collections/calendar/event_builder.spec.ts b/apps/client/src/widgets/collections/calendar/event_builder.spec.ts
index 2c872a14e5..88299e8b65 100644
--- a/apps/client/src/widgets/collections/calendar/event_builder.spec.ts
+++ b/apps/client/src/widgets/collections/calendar/event_builder.spec.ts
@@ -1,4 +1,4 @@
-import { describe, expect, it } from "vitest";
+import { describe, expect, it, vi } from "vitest";
import { buildNote, buildNotes } from "../../../test/easy-froca.js";
import { buildEvent, buildEvents } from "./event_builder.js";
import { LOCALE_MAPPINGS } from "./index.js";
@@ -148,7 +148,7 @@ describe("Promoted attributes", () => {
expect(event).toHaveLength(1);
expect(event[0]?.promotedAttributes).toMatchObject([
[ "assignee", "Target note" ]
- ])
+ ]);
});
it("supports start time and end time", async () => {
@@ -177,6 +177,86 @@ describe("Promoted attributes", () => {
});
+
+describe("Recurrence", () => {
+ it("supports valid recurrence without end date", async () => {
+ const noteIds = buildNotes([
+ {
+ title: "Recurring Event",
+ "#startDate": "2025-05-05",
+ "#recurrence": "FREQ=DAILY;COUNT=5"
+ }
+ ]);
+ const events = await buildEvents(noteIds);
+
+ expect(events).toHaveLength(1);
+ expect(events[0]).toMatchObject({
+ title: "Recurring Event",
+ start: "2025-05-05",
+ });
+ expect(events[0].rrule).toContain("DTSTART:20250505");
+ expect(events[0].rrule).toContain("FREQ=DAILY;COUNT=5");
+ expect(events[0].end).toBeUndefined();
+ });
+
+ it("supports recurrence with start and end time (duration calculated)", async () => {
+ const noteIds = buildNotes([
+ {
+ title: "Timed Recurring Event",
+ "#startDate": "2025-05-05",
+ "#startTime": "13:00",
+ "#endTime": "15:30",
+ "#recurrence": "FREQ=WEEKLY;COUNT=3"
+ }
+ ]);
+ const events = await buildEvents(noteIds);
+
+ expect(events).toHaveLength(1);
+ expect(events[0]).toMatchObject({
+ title: "Timed Recurring Event",
+ start: "2025-05-05T13:00:00",
+ duration: "02:30"
+ });
+ expect(events[0].rrule).toContain("DTSTART:20250505T130000");
+ expect(events[0].end).toBeUndefined();
+ });
+
+ it("removes end date when recurrence is valid", async () => {
+ const noteIds = buildNotes([
+ {
+ title: "Recurring With End",
+ "#startDate": "2025-05-05",
+ "#endDate": "2025-05-07",
+ "#recurrence": "FREQ=DAILY;COUNT=2"
+ }
+ ]);
+ const events = await buildEvents(noteIds);
+
+ expect(events).toHaveLength(1);
+ expect(events[0].rrule).toBeDefined();
+ expect(events[0].end).toBeUndefined();
+ });
+
+ it("writes to console on invalid recurrence rule", async () => {
+ const noteIds = buildNotes([
+ {
+ title: "Invalid Recurrence",
+ "#startDate": "2025-05-05",
+ "#recurrence": "RRULE:FREQ=INVALID"
+ }
+ ]);
+
+ const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
+ await buildEvents(noteIds);
+ const calledWithInvalid = consoleSpy.mock.calls.some(call =>
+ call[0].includes("has an invalid #recurrence string")
+ );
+ expect(calledWithInvalid).toBe(true);
+ consoleSpy.mockRestore();
+ });
+});
+
+
describe("Building locales", () => {
it("every language has a locale defined", async () => {
for (const { id, contentOnly } of LOCALES) {
diff --git a/apps/client/src/widgets/collections/calendar/event_builder.ts b/apps/client/src/widgets/collections/calendar/event_builder.ts
index f4611ccd7e..dec64feee8 100644
--- a/apps/client/src/widgets/collections/calendar/event_builder.ts
+++ b/apps/client/src/widgets/collections/calendar/event_builder.ts
@@ -1,17 +1,22 @@
import { EventInput, EventSourceFuncArg, EventSourceInput } from "@fullcalendar/core/index.js";
+import { dayjs } from "@triliumnext/commons";
import clsx from "clsx";
+import { start } from "repl";
+import * as rruleLib from 'rrule';
import FNote from "../../../entities/fnote";
import froca from "../../../services/froca";
import server from "../../../services/server";
-import { formatDateToLocalISO, getCustomisableLabel, getMonthsInDateRange, offsetDate } from "./utils";
+import toastService from "../../../services/toast";
+import { getCustomisableLabel, getMonthsInDateRange } from "./utils";
interface Event {
startDate: string,
endDate?: string | null,
startTime?: string | null,
endTime?: string | null,
- isArchived?: boolean;
+ isArchived?: boolean,
+ recurrence?: string | null;
}
export async function buildEvents(noteIds: string[]) {
@@ -28,8 +33,17 @@ export async function buildEvents(noteIds: string[]) {
const endDate = getCustomisableLabel(note, "endDate", "calendar:endDate");
const startTime = getCustomisableLabel(note, "startTime", "calendar:startTime");
const endTime = getCustomisableLabel(note, "endTime", "calendar:endTime");
+ const recurrence = getCustomisableLabel(note, "recurrence", "calendar:recurrence");
const isArchived = note.hasLabel("archived");
- events.push(await buildEvent(note, { startDate, endDate, startTime, endTime, isArchived }));
+ try {
+ events.push(await buildEvent(note, { startDate, endDate, startTime, endTime, recurrence, isArchived }));
+ } catch (error) {
+ if (error instanceof Error) {
+ const errorMessage = error.message;
+ toastService.showError(errorMessage);
+ console.error(errorMessage);
+ }
+ }
}
return events.flat();
@@ -59,6 +73,7 @@ export async function buildEventsForCalendar(note: FNote, e: EventSourceFuncArg)
events.push(await buildEvent(dateNote, { startDate }));
+
if (dateNote.hasChildren()) {
const childNoteIds = await dateNote.getSubtreeNoteIds();
for (const childNoteId of childNoteIds) {
@@ -79,7 +94,7 @@ export async function buildEventsForCalendar(note: FNote, e: EventSourceFuncArg)
return events.flat();
}
-export async function buildEvent(note: FNote, { startDate, endDate, startTime, endTime, isArchived }: Event) {
+export async function buildEvent(note: FNote, { startDate, endDate, startTime, endTime, recurrence, isArchived }: Event) {
const customTitleAttributeName = note.getLabelValue("calendar:title");
const titles = await parseCustomTitle(customTitleAttributeName, note);
const colorClass = note.getColorClass();
@@ -98,9 +113,10 @@ export async function buildEvent(note: FNote, { startDate, endDate, startTime, e
startDate = (startTime ? `${startDate}T${startTime}:00` : startDate);
if (!startTime) {
- const endDateOffset = offsetDate(endDate ?? startDate, 1);
- if (endDateOffset) {
- endDate = formatDateToLocalISO(endDateOffset);
+ if (endDate) {
+ endDate = dayjs(endDate).add(1, "day").format("YYYY-MM-DD");
+ } else if (startDate) {
+ endDate = dayjs(startDate).add(1, "day").format("YYYY-MM-DD");
}
}
@@ -118,6 +134,30 @@ export async function buildEvent(note: FNote, { startDate, endDate, startTime, e
if (endDate) {
eventData.end = endDate;
}
+
+ if (recurrence) {
+ // Generate rrule string
+ const rruleString = `DTSTART:${dayjs(startDate).format("YYYYMMDD[T]HHmmss")}\n${recurrence}`;
+
+ // Validate rrule string
+ let rruleValid = true;
+ try {
+ rruleLib.rrulestr(rruleString, { forceset: true }) as rruleLib.RRuleSet;
+ } catch {
+ rruleValid = false;
+ }
+
+ if (rruleValid) {
+ delete eventData.end;
+ eventData.rrule = rruleString;
+ if (endDate){
+ const duration = dayjs.duration(dayjs(endDate).diff(dayjs(startDate)));
+ eventData.duration = duration.format("HH:mm");
+ }
+ } else {
+ throw new Error(`Note "${note.noteId} ${note.title}" has an invalid #recurrence string ${recurrence}. Excluding...`);
+ }
+ }
events.push(eventData);
}
return events;
diff --git a/apps/client/src/widgets/collections/calendar/index.tsx b/apps/client/src/widgets/collections/calendar/index.tsx
index 23f8371125..349d666e6c 100644
--- a/apps/client/src/widgets/collections/calendar/index.tsx
+++ b/apps/client/src/widgets/collections/calendar/index.tsx
@@ -252,6 +252,7 @@ function usePlugins(isEditable: boolean, isCalendarRoot: boolean) {
plugins.push((await import("@fullcalendar/timegrid")).default);
plugins.push((await import("@fullcalendar/list")).default);
plugins.push((await import("@fullcalendar/multimonth")).default);
+ plugins.push((await import("@fullcalendar/rrule")).default);
if (isEditable || isCalendarRoot) {
plugins.push((await import("@fullcalendar/interaction")).default);
}
diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Collections/Calendar.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Collections/Calendar.html
index 472192219c..4149ac77f0 100644
--- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Collections/Calendar.html
+++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Collections/Calendar.html
@@ -185,6 +185,18 @@
at which the event ends (in relation with endDate if
present, or startDate).
+
#recurrence
+ RRULE string
+ that if present, determines whether a task should repeat or not. Note that
+ it does not include the DTSTART attribute,
+ which is derived from the #startDate and
+ #startTimedirectly. For examples of valid RRULE strings
+ see https://icalendar.org/rrule-tool.html
+ #color
When not used in a Journal, the calendar is recursive. That is, it will look for events not just in its child notes but also in the children of these child notes.
++
The built in calendar view also supports repeating tasks. If a child note + of the calendar has a #recurrence label with a valid recurrence, that event + will repeat on the calendar according to the recurrence string.
+For example, to make a note repeat on the calendar:
+Every Day - #recurrence="FREQ=DAILY;INTERVAL=1"
+
Every 3 days - #recurrence="FREQ=DAILY;INTERVAL=3"
+
Every week - #recurrence="FREQ=WEEKLY;INTERVAL=1"
+
Every 2 weeks on Monday, Wednesday and Friday - #recurrence="FREQ=WEEKLY;INTERVAL=2;BYDAY=MO,WE,FR"
+
Every 3 months - #recurrence="FREQ=MONTHLY;INTERVAL=3"
+
Every 2 months on the First Sunday - #recurrence="FREQ=MONTHLY;INTERVAL=2;BYDAY=1SU"
+
Every month on the Last Friday - #recurrence="FREQ=MONTHLY;INTERVAL=1;BYDAY=-1FR"
+
And so on.
+For other examples of valid RRULE strings
+ see https://icalendar.org/rrule-tool.html
+
Note that the recurrence string does not include the DTSTART attribute
+ as defined in the iCAL specifications. This is derived directly from the
+ startDateand startTime attributes
If you want to override the label the calendar uses to fetch the recurrence
+ string, you can use the #calendar:recurrence attribute.
+ For example, you can set #calendar:recurrence=taskRepeats.
+ Then you can set your recurrence string like #taskRepeats="FREQ=DAILY;INTERVAL=1"
+
Also note that the recurrence label can be made promoted as with the start + and end dates.
+ +
It is possible to integrate the calendar view into the Journal with day
diff --git a/docs/User Guide/User Guide/Collections/Calendar.md b/docs/User Guide/User Guide/Collections/Calendar.md
index f7309feacd..82bb59b5d8 100644
--- a/docs/User Guide/User Guide/Collections/Calendar.md
+++ b/docs/User Guide/User Guide/Collections/Calendar.md
@@ -72,11 +72,12 @@ For each note of the calendar, the following attributes can be used:
| `#endDate` | Similar to `startDate`, mentions the end date if the event spans across multiple days. The date is inclusive, so the end day is also considered. The attribute can be missing for single-day events. |
| `#startTime` | The time the event starts at. If this value is missing, then the event is considered a full-day event. The format is `HH:MM` (hours in 24-hour format and minutes). |
| `#endTime` | Similar to `startTime`, it mentions the time at which the event ends (in relation with `endDate` if present, or `startDate`). |
+| `#recurrence` | This is an optional CalDAV `RRULE` string that if present, determines whether a task should repeat or not. Note that it does not include the `DTSTART` attribute, which is derived from the `#startDate` and `#startTime` directly. For examples of valid `RRULE` strings see [https://icalendar.org/rrule-tool.html](https://icalendar.org/rrule-tool.html) |
| `#color` | Displays the event with a specified color (named such as `red`, `gray` or hex such as `#FF0000`). This will also change the color of the note in other places such as the note tree. |
-| `#calendar:color` | **❌️ Removed since v0.100.0. Use** `**#color**` **instead.**
Similar to `#color`, but applies the color only for the event in the calendar and not for other places such as the note tree. |
+| `#calendar:color` | **❌️ Removed since v0.100.0. Use** `**#color**` **instead.**
Similar to `#color`, but applies the color only for the event in the calendar and not for other places such as the note tree. |
| `#iconClass` | If present, the icon of the note will be displayed to the left of the event title. |
| `#calendar:title` | Changes the title of an event to point to an attribute of the note other than the title, can either a label or a relation (without the `#` or `~` symbol). See _Use-cases_ for more information. |
-| `#calendar:displayedAttributes` | Allows displaying the value of one or more attributes in the calendar like this:

`#weight="70" #Mood="Good" #calendar:displayedAttributes="weight,Mood"`
It can also be used with relations, case in which it will display the title of the target note:
`~assignee=@My assignee #calendar:displayedAttributes="assignee"` |
+| `#calendar:displayedAttributes` | Allows displaying the value of one or more attributes in the calendar like this:

`#weight="70" #Mood="Good" #calendar:displayedAttributes="weight,Mood"`
It can also be used with relations, case in which it will display the title of the target note:
`~assignee=@My assignee #calendar:displayedAttributes="assignee"` |
| `#calendar:startDate` | Allows using a different label to represent the start date, other than `startDate` (e.g. `expiryDate`). The label name **must not be** prefixed with `#`. If the label is not defined for a note, the default will be used instead. |
| `#calendar:endDate` | Similar to `#calendar:startDate`, allows changing the attribute which is being used to read the end date. |
| `#calendar:startTime` | Similar to `#calendar:startDate`, allows changing the attribute which is being used to read the start time. |
@@ -102,6 +103,32 @@ This will result in:
When not used in a Journal, the calendar is recursive. That is, it will look for events not just in its child notes but also in the children of these child notes.
+## Recurrence
+
+The built in calendar view also supports repeating tasks. If a child note of the calendar has a #recurrence label with a valid recurrence, that event will repeat on the calendar according to the recurrence string.
+
+For example, to make a note repeat on the calendar:
+
+* Every Day - `#recurrence="FREQ=DAILY;INTERVAL=1"`
+* Every 3 days - `#recurrence="FREQ=DAILY;INTERVAL=3"`
+* Every week - `#recurrence="FREQ=WEEKLY;INTERVAL=1"`
+* Every 2 weeks on Monday, Wednesday and Friday - `#recurrence="FREQ=WEEKLY;INTERVAL=2;BYDAY=MO,WE,FR"`
+* Every 3 months - `#recurrence="FREQ=MONTHLY;INTERVAL=3"`
+* Every 2 months on the First Sunday - `#recurrence="FREQ=MONTHLY;INTERVAL=2;BYDAY=1SU"`
+* Every month on the Last Friday - `#recurrence="FREQ=MONTHLY;INTERVAL=1;BYDAY=-1FR"`
+* And so on.
+
+For other examples of valid `RRULE` strings see [https://icalendar.org/rrule-tool.html](https://icalendar.org/rrule-tool.html)
+
+Note that the recurrence string does not include the `DTSTART` attribute as defined in the iCAL specifications. This is derived directly from the `startDate` and `startTime` attributes
+
+If you want to override the label the calendar uses to fetch the recurrence string, you can use the `#calendar:recurrence` attribute. For example, you can set `#calendar:recurrence=taskRepeats`. Then you can set your recurrence string like `#taskRepeats="FREQ=DAILY;INTERVAL=1"`
+
+Also note that the recurrence label can be made promoted as with the start and end dates.
+
+> [!WARNING]
+> If the recurrence string is not valid, a toast will be shown with the note ID and title of the note with the erroneous recurrence message. This note will not be added to the calendar
+
## Use-cases
### Using with the Journal / calendar
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 4170ce63e5..7ccffa7937 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -200,6 +200,9 @@ importers:
'@fullcalendar/multimonth':
specifier: 6.1.20
version: 6.1.20(@fullcalendar/core@6.1.20)
+ '@fullcalendar/rrule':
+ specifier: 6.1.20
+ version: 6.1.20(@fullcalendar/core@6.1.20)(rrule@2.8.1)
'@fullcalendar/timegrid':
specifier: 6.1.20
version: 6.1.20(@fullcalendar/core@6.1.20)
@@ -323,6 +326,9 @@ importers:
reveal.js:
specifier: 5.2.1
version: 5.2.1
+ rrule:
+ specifier: 2.8.1
+ version: 2.8.1
svg-pan-zoom:
specifier: 3.6.2
version: 3.6.2
@@ -3392,6 +3398,12 @@ packages:
peerDependencies:
'@fullcalendar/core': ~6.1.20
+ '@fullcalendar/rrule@6.1.20':
+ resolution: {integrity: sha512-5Awk7bmaA97hSZRpIBehenXkYreVIvx8nnaMFZ/LDGRuK1mgbR4vSUrDTvVU+oEqqKnj/rqMBByWqN5NeehQxw==}
+ peerDependencies:
+ '@fullcalendar/core': ~6.1.20
+ rrule: ^2.6.0
+
'@fullcalendar/timegrid@6.1.20':
resolution: {integrity: sha512-4H+/MWbz3ntA50lrPif+7TsvMeX3R1GSYjiLULz0+zEJ7/Yfd9pupZmAwUs/PBpA6aAcFmeRr0laWfcz1a9V1A==}
peerDependencies:
@@ -13131,6 +13143,9 @@ packages:
resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==}
engines: {node: '>= 18'}
+ rrule@2.8.1:
+ resolution: {integrity: sha512-hM3dHSBMeaJ0Ktp7W38BJZ7O1zOgaFEsn41PDk+yHoEtfLV+PoJt9E9xAlZiWgf/iqEqionN0ebHFZIDAp+iGw==}
+
rrweb-cssom@0.8.0:
resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==}
@@ -16043,6 +16058,8 @@ snapshots:
'@ckeditor/ckeditor5-core': 47.4.0
'@ckeditor/ckeditor5-upload': 47.4.0
ckeditor5: 47.4.0
+ transitivePeerDependencies:
+ - supports-color
'@ckeditor/ckeditor5-ai@47.4.0(bufferutil@4.0.9)(utf-8-validate@6.0.5)':
dependencies:
@@ -16183,6 +16200,8 @@ snapshots:
'@ckeditor/ckeditor5-utils': 47.4.0
'@ckeditor/ckeditor5-widget': 47.4.0
es-toolkit: 1.39.5
+ transitivePeerDependencies:
+ - supports-color
'@ckeditor/ckeditor5-cloud-services@47.4.0':
dependencies:
@@ -16397,6 +16416,8 @@ snapshots:
'@ckeditor/ckeditor5-utils': 47.4.0
ckeditor5: 47.4.0
es-toolkit: 1.39.5
+ transitivePeerDependencies:
+ - supports-color
'@ckeditor/ckeditor5-editor-inline@47.4.0':
dependencies:
@@ -16512,6 +16533,8 @@ snapshots:
'@ckeditor/ckeditor5-utils': 47.4.0
ckeditor5: 47.4.0
es-toolkit: 1.39.5
+ transitivePeerDependencies:
+ - supports-color
'@ckeditor/ckeditor5-font@47.4.0':
dependencies:
@@ -16586,6 +16609,8 @@ snapshots:
'@ckeditor/ckeditor5-utils': 47.4.0
'@ckeditor/ckeditor5-widget': 47.4.0
ckeditor5: 47.4.0
+ transitivePeerDependencies:
+ - supports-color
'@ckeditor/ckeditor5-html-embed@47.4.0':
dependencies:
@@ -16770,8 +16795,6 @@ snapshots:
'@ckeditor/ckeditor5-utils': 47.4.0
ckeditor5: 47.4.0
es-toolkit: 1.39.5
- transitivePeerDependencies:
- - supports-color
'@ckeditor/ckeditor5-merge-fields@47.4.0':
dependencies:
@@ -16794,8 +16817,6 @@ snapshots:
'@ckeditor/ckeditor5-ui': 47.4.0
'@ckeditor/ckeditor5-utils': 47.4.0
ckeditor5: 47.4.0
- transitivePeerDependencies:
- - supports-color
'@ckeditor/ckeditor5-operations-compressor@47.4.0':
dependencies:
@@ -16850,8 +16871,6 @@ snapshots:
'@ckeditor/ckeditor5-utils': 47.4.0
'@ckeditor/ckeditor5-widget': 47.4.0
ckeditor5: 47.4.0
- transitivePeerDependencies:
- - supports-color
'@ckeditor/ckeditor5-pagination@47.4.0':
dependencies:
@@ -16959,8 +16978,6 @@ snapshots:
'@ckeditor/ckeditor5-ui': 47.4.0
'@ckeditor/ckeditor5-utils': 47.4.0
ckeditor5: 47.4.0
- transitivePeerDependencies:
- - supports-color
'@ckeditor/ckeditor5-slash-command@47.4.0':
dependencies:
@@ -16973,8 +16990,6 @@ snapshots:
'@ckeditor/ckeditor5-ui': 47.4.0
'@ckeditor/ckeditor5-utils': 47.4.0
ckeditor5: 47.4.0
- transitivePeerDependencies:
- - supports-color
'@ckeditor/ckeditor5-source-editing-enhanced@47.4.0':
dependencies:
@@ -17022,8 +17037,6 @@ snapshots:
'@ckeditor/ckeditor5-utils': 47.4.0
ckeditor5: 47.4.0
es-toolkit: 1.39.5
- transitivePeerDependencies:
- - supports-color
'@ckeditor/ckeditor5-table@47.4.0':
dependencies:
@@ -17036,8 +17049,6 @@ snapshots:
'@ckeditor/ckeditor5-widget': 47.4.0
ckeditor5: 47.4.0
es-toolkit: 1.39.5
- transitivePeerDependencies:
- - supports-color
'@ckeditor/ckeditor5-template@47.4.0':
dependencies:
@@ -17150,8 +17161,6 @@ snapshots:
'@ckeditor/ckeditor5-engine': 47.4.0
'@ckeditor/ckeditor5-utils': 47.4.0
es-toolkit: 1.39.5
- transitivePeerDependencies:
- - supports-color
'@ckeditor/ckeditor5-widget@47.4.0':
dependencies:
@@ -17171,8 +17180,6 @@ snapshots:
'@ckeditor/ckeditor5-utils': 47.4.0
ckeditor5: 47.4.0
es-toolkit: 1.39.5
- transitivePeerDependencies:
- - supports-color
'@codemirror/autocomplete@6.18.6':
dependencies:
@@ -18514,6 +18521,11 @@ snapshots:
'@fullcalendar/core': 6.1.20
'@fullcalendar/daygrid': 6.1.20(@fullcalendar/core@6.1.20)
+ '@fullcalendar/rrule@6.1.20(@fullcalendar/core@6.1.20)(rrule@2.8.1)':
+ dependencies:
+ '@fullcalendar/core': 6.1.20
+ rrule: 2.8.1
+
'@fullcalendar/timegrid@6.1.20(@fullcalendar/core@6.1.20)':
dependencies:
'@fullcalendar/core': 6.1.20
@@ -30286,6 +30298,10 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ rrule@2.8.1:
+ dependencies:
+ tslib: 2.8.1
+
rrweb-cssom@0.8.0:
optional: true