- Kotlin 99%
- Shell 1%
tools/list returned `resultType` without the `ttlMs` and `cacheScope` that accompany it, so a client validating the result rejected the whole tool list. The failure mode is quiet and misleading: the server reports as connected and simply exposes no tools, which reads as "the assistant can't see my calendar" rather than as a protocol error. tools/list now caches like the handshake (60s, public — tool definitions carry no calendar data). tools/call declares ttlMs 0 and cacheScope private, so a read of the user's calendar is never cached or shared. Verified against Claude Code, which now reports the server as connected with its tools loaded instead of "tools fetch failed". |
||
|---|---|---|
| docs | ||
| gradle | ||
| packaging | ||
| screenshots | ||
| src | ||
| .gitignore | ||
| build.gradle.kts | ||
| build.sh | ||
| gradle.properties | ||
| LICENSE | ||
| README.md | ||
| settings.gradle.kts | ||
| summary.md | ||
Calendula Desktop
A native Linux calendar.
A desktop fork of Calendula —
the Material 3 calendar — rebuilt with Compose Multiplatform to talk directly to a
CalDAV server.
About
This is a fork/port of upstream Calendula,
an Android calendar app. The original reads and writes Android's
CalendarContract (any calendar already synced to the device); this desktop
version replaces that data layer with a small CalDAV client, so it connects
straight to a CalDAV server (Nextcloud, Radicale, Baikal, Fastmail, …) and needs
no Android stack.
What carries over from upstream:
- The domain model, recurrence engine, and ICS codec (ported faithfully and JVM-tested).
- The Calendula identity: seed-derived slate palette, light/dark theme, and the real launcher icon.
What is different, because it's a PC:
- A sidebar (Month / Week / Day / Agenda / Settings) instead of the Android drawer.
- Settings live in-app and cover appearance, view layout, and new-event defaults — plus the CalDAV server connection.
- Reminder notifications are fired with
notify-send. - The password is stored in the OS keyring (Secret Service) when available.
✨ Features
- Month, week, day, and agenda views with a sidebar switcher
- Week/day time-grid timelines (all-day strip, timed blocks, a "now" line, tap-an-empty-slot to create)
- Full event details — times, location, description, reminders, recurrence
- Create, edit, and delete events; delete a single occurrence (
EXDATE) or a series from an occurrence onwards (RRULEtruncation) - Recurrence expansion for
DAILY/WEEKLY/MONTHLY/YEARLY,INTERVAL,COUNT,UNTIL,BYDAY,BYMONTH,BYMONTHDAY,BYSETPOS,WKST - Per-event colors from the ICS
COLOR/X-APPLE-CALENDAR-COLORproperties (hex and CSS color names), falling back to the calendar's color - Reminder notifications (
notify-send) for events with aVALARM - Settings — theme (system/light/dark), dynamic colour (matugen), week start, 12/24-hour time, default view, hour lines, dim-past, default event duration & reminders, and change server
- In-app CalDAV setup — paste server URL, username, and password; no file editing required
- AI assistant (MCP) server — expose your calendars to Claude, Gemini, and other MCP clients as callable tools (opt-in)
🔧 Configuration
On first launch (or via Settings → Server → Change server) you connect with a
server URL, username, and password. This writes
~/.config/calendula/config.properties:
caldav.url=https://cloud.example.com/remote.php/dav/
caldav.username=your-user
The password is stored in the OS keyring (via secret-tool / Secret Service);
it only falls back to caldav.password in the config file on systems without a
keyring. Environment variables CALDAV_URL, CALDAV_USERNAME, and
CALDAV_PASSWORD take precedence over the file.
User preferences live in ~/.config/calendula/settings.properties.
🎨 Dynamic colour (matugen)
Calendula can take its whole Material colour scheme from matugen instead of the built-in slate palette. Enable it under Settings → Appearance → Dynamic → Matugen.
The repo ships a ready-made template at
packaging/matugen/calendula.template.
Point a matugen template at it and write the output to
~/.cache/matugen/calendula.colors:
# ~/.config/matugen/config.toml
[templates.calendula]
input_path = '~/.../calendula-desktop/packaging/matugen/calendula.template'
output_path = '~/.cache/matugen/calendula.colors'
Then run matugen once (e.g. matugen image ~/wallpaper.png) so the file exists.
Calendula reads it at launch and whenever the Dynamic setting changes; the
output path can be overridden with the CALENDULA_MATUGEN_COLORS environment
variable. If the file is missing or malformed, the app silently falls back to
the built-in palette.
🤖 AI assistants (MCP)
Calendula can expose your calendars to AI assistants (Claude, Gemini, Cursor, …)
as a set of tools over the Model Context Protocol.
Enable it under Settings → Integrations → AI assistants, then point your
assistant at calendula --mcp. See docs/mcp.md for the config
JSON and file locations.
📦 Install
Two ways to run it on Linux (x86_64):
AppImage
Download (or build) Calendula-<version>-x86_64.AppImage, make it executable, and
run it. The AppImage bundles its own JRE — no Java required on the target system.
Arch Linux (pacman)
./build.sh also builds the Arch package (calendula-desktop-<version>-1-x86_64.pkg.tar.zst)
into dist/. Install it with:
sudo pacman -U dist/calendula-desktop-<version>-1-x86_64.pkg.tar.zst
The package depends on java-runtime>=17 and installs the calendula launcher,
desktop entry, and hicolor icon. (You can also run makepkg -si manually inside
packaging/arch/, but build.sh is the supported path.)
🛠 Build from source
Requires JDK 17 (set JAVA_HOME, e.g. /usr/lib/jvm/java-17-openjdk):
./gradlew run # launch the app (needs a display)
./gradlew build # compile + run tests
./build.sh # build AppImage + Arch package into dist/
build.sh additionally needs jpackage (bundled with JDK 17),
appimagetool for the AppImage,
and makepkg + fakeroot for the Arch package.
📁 Project layout
src/jvmMain/kotlin/de/jeanlucmakiola/calendula/
domain/ — models, recurrence rules, event form (ported from upstream)
data/
caldav/ — CalDavClient (WebDAV/HTTP+XML), IcsCodec, Recurrence, Keyring
prefs/ — settings store
reminders/ — ReminderNotifier + notify-send backend
CalendarDataSource.kt / CalDavDataSource.kt — the calendar seam + impl
ui/ — AppState, sidebar, month/week/day/agenda views, settings, editor
Main.kt — desktop entry point + setup screen
packaging/
arch/ — PKGBUILD, launcher, desktop entry, icon
icon/ — the launcher SVG (single source of truth for the icon)
matugen/ — the matugen template for dynamic colour
build.sh — builds the AppImage + Arch package into dist/
⚖️ Differences from upstream
- Data layer: CalDAV client instead of Android
CalendarContract. - Theme: upstream uses Material 3 Expressive via the Android-only
floret-kit; this port uses stable ComposeMaterialThemewith the same slate palette (theMaterialExpressiveThemeAPI is only public in Compose Multiplatform's alpha material3 line). - Not ported (Android-specific): widgets, per-app language, contact special-dates, launcher-name/font choices, and multi-account UI. (Android's wallpaper dynamic colour is replaced by the matugen integration above.)
📜 License
MIT — Jean-Luc Makiola. This is a fork of Calendula, also MIT.