Calendula port to PC
  • Kotlin 99%
  • Shell 1%
Find a file
alex 1fc87acb3b Send cache metadata on every resultType response (2.2.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".
2026-08-17 21:20:23 +02:00
docs Send cache metadata on every resultType response (2.2.1) 2026-08-17 21:20:23 +02:00
gradle Implement MCP server to expose calendars to AI assistants 2026-08-17 20:54:06 +02:00
packaging Send cache metadata on every resultType response (2.2.1) 2026-08-17 21:20:23 +02:00
screenshots docs: add more screenshots 2026-08-15 11:12:39 +02:00
src Send cache metadata on every resultType response (2.2.1) 2026-08-17 21:20:23 +02:00
.gitignore Ignore gradle wrapper 2026-08-15 10:34:04 +02:00
build.gradle.kts Send cache metadata on every resultType response (2.2.1) 2026-08-17 21:20:23 +02:00
build.sh fix(ui): esc global key handling, mouse back/forward interception, out-of-month opacity, animation cascading on refresh, and bump version 1.7.0 2026-08-14 23:03:09 +02:00
gradle.properties Port Calendula to a native Linux desktop app 2026-08-14 15:05:56 +02:00
LICENSE Add MIT LICENSE from upstream 2026-08-14 18:12:17 +02:00
README.md Implement MCP server to expose calendars to AI assistants 2026-08-17 20:54:06 +02:00
settings.gradle.kts Port Calendula to a native Linux desktop app 2026-08-14 15:05:56 +02:00
summary.md Update summary.md for the 2.2.0 MCP hardening 2026-08-17 21:15:40 +02:00

Calendula Desktop

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.

Linux x86_64 Kotlin + Compose MIT License

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 (RRULE truncation)
  • 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-COLOR properties (hex and CSS color names), falling back to the calendar's color
  • Reminder notifications (notify-send) for events with a VALARM
  • 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 Compose MaterialTheme with the same slate palette (the MaterialExpressiveTheme API 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.