hypercal 2.1.4 A desktop app, an endpoint AI clients can drive the calendar through, and calendars you can publish as a feed. **A desktop app.** hypercal now ships as an Electron shell, in two modes. Local mode runs the portable backend in the app's own process, against a real SQLite file under your user-data directory, so the desktop build gets the whole feature set the Android one gives up: filesystem backups, database snapshots, and a /dav that another app on the same machine can actually reach. Remote mode runs no backend at all. It points the window at your own instance and runs that instance's frontend, which is the only arrangement in which passkeys can work, because WebAuthn compares origins and a loopback proxy would present the wrong one. Closing the window hides it to a panel indicator instead of quitting, so the backend, its pollers and the CalDAV listener stay up with the window put away. The packages are an AppImage, a tar.gz, a deb, an rpm, a Flatpak and an AUR PKGBUILD that installs the tarball with its launcher entry and icons. There is no auto-updater, on purpose. Every one of those formats is owned by a package manager, and a second thing replacing the binary would fight it. The app asks the releases API once, and if something newer exists it raises the same toast the web app uses, with a link to the notes. It fails silently: offline, proxied, timed out and already-current all mean say nothing. **An MCP endpoint.** /mcp lets an AI client read and edit the calendar over the Model Context Protocol, with twelve tools covering search, create, update, recurrence overrides, conflict checks and free-slot queries. It authenticates with the app-specific passwords that already exist for CalDAV. It is off by default, and it is an admin setting rather than an environment variable, so an admin can see the feature exists and switch it on without shell access to the deployment. The setting is read per request, so both directions take effect at once, including for a client already connected. A disabled endpoint answers 503 identically to a good token and a bad one, so it is not somewhere to test app passwords. It stays off until someone decides otherwise, because turning it on lets every app password already minted on the instance reach /mcp. **Published calendar feeds.** A calendar can be published at /feed/<token>.ics for anything that subscribes to a URL. Encryption is checked when you publish and again on every serve, so no future write path can leak a private calendar by forgetting a hook, and making a calendar private auto-unpublishes it rather than failing for a reason you would have to go fix elsewhere. The feed path is redacted in the request log, since a subscribed client refetches forever and would otherwise write a live credential into the log several times an hour. Agents cannot publish. The MCP tools can see and revoke a feed, but making a calendar world-readable is not a capability a bearer token handed to an AI client should carry. **Export one calendar.** /api/export.ics and /api/export.hypercalendar now take an optional calendar id. Both also filter trashed events, which they did not before, so exports and the daily backups built on them silently carried deleted events and disagreed with what the app showed. **Themes.** Three new palettes, Midnight, Nord and Paper, bringing the set to six. Follow system is new as well, and is a preference rather than a seventh palette: it picks the first palette matching your OS scheme, so the radius scale and type stack still resolve from a real theme. Underneath, four of the six are now derived from Radix Colors rather than values somebody picked, and a theme is one object in one file instead of six coordinated edits across an enum, a stylesheet, a picker, a label map and the pre-paint script. Blacksite and Nord are deliberately not derived, one being a port of a specific skin and the other a published palette in its own right. Importing a professional scale still needed measuring against our own surfaces, and the contrast test caught three places where Radix's guarantees do not carry across; the generator handles each of them rather than a hand-applied patch. **Dialogs, menus and tooltips.** The hand-rolled dialog, menu, select, popover and tooltip variants are one primitive each now, which is what makes tooltips work on touch. **The calendar list is rebuilt.** Encrypted, read-only, shared, published and CalDAV state are badges with visible text and an sr-only phrase, rather than glyphs behind a native title. Sync health renders for CalDAV links too: the server has always written lastFetchError for both mechanisms and only the UI distinguished them, so a failing link showed nothing at all. Sections group and collapse with counts. **Drag an event between windows.** Holding Alt arms a native drag on an event card, so it can be dropped into another window of the same browser, with a real ghost drawn while it is in flight. Fixes: - A write from a locked window could send an encrypted event's title, description and location to the server in the clear, and without the encrypted flag. It now refuses. A times-only write still passes through, so moving or resizing an event from a locked window keeps working. - Shutdown waited on the SSE stream the app holds open per client for change notifications, which is never idle, so server.close() hung. Not a desktop-only fix: under Docker the same hang meant waiting for SIGKILL on every deploy. - The desktop app could not be quit, and its first-run mode chooser was dead code. Both were turned up by the new Electron test suite. better-sqlite3 is on 13, which is N-API and so needs no rebuild against Electron's ABI. Android: versionCode 10. Feeds and /mcp are server features; the on-device backend reports 501 and 503 for them rather than pretending.