No description
  • TypeScript 54.5%
  • Vue 43.6%
  • JavaScript 1.9%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
SitiSchu b92abbd658
All checks were successful
Build and package / build (push) Successful in 31s
Unify refresh button and drop agenda create button
2026-10-23 11:04:20 +02:00
.forgejo Delete release before moving its tag 2026-10-21 19:44:25 +02:00
config Initial commit: OpenCloud CalDAV calendar app 2026-10-21 16:15:49 +02:00
docs/screenshots Retake screenshots at 1600x1000 in dark mode 2026-10-22 22:58:35 +02:00
src Unify refresh button and drop agenda create button 2026-10-23 11:04:20 +02:00
tests/unit Sort calendar search results from newest to oldest 2026-10-23 00:04:04 +02:00
.gitignore Initial commit: OpenCloud CalDAV calendar app 2026-10-21 16:15:49 +02:00
.prettierignore Initial commit: OpenCloud CalDAV calendar app 2026-10-21 16:15:49 +02:00
.prettierrc.json Initial commit: OpenCloud CalDAV calendar app 2026-10-21 16:15:49 +02:00
.renovaterc.json Initial commit: OpenCloud CalDAV calendar app 2026-10-21 16:15:49 +02:00
docker-compose.yml Initial commit: OpenCloud CalDAV calendar app 2026-10-21 16:15:49 +02:00
eslint.config.js Initial commit: OpenCloud CalDAV calendar app 2026-10-21 16:15:49 +02:00
LICENSE Initial commit: OpenCloud CalDAV calendar app 2026-10-21 16:15:49 +02:00
package.json Release 1.2.0 2026-10-23 00:17:07 +02:00
pnpm-lock.yaml Initial commit: OpenCloud CalDAV calendar app 2026-10-21 16:15:49 +02:00
README.md docs: align calendar README with contacts layout 2026-10-22 23:06:33 +02:00
tsconfig.json Initial commit: OpenCloud CalDAV calendar app 2026-10-21 16:15:49 +02:00
vite.config.ts Initial commit: OpenCloud CalDAV calendar app 2026-10-21 16:15:49 +02:00

Warning: This project is fully vibe coded. Use at your own risk.

Calendar for OpenCloud

A web app for OpenCloud that reads and manages calendars and appointments from the OpenCloud CalDAV endpoint (/caldav/). It is based on web-app-skeleton.

Table of contents

Screenshots

Calendar

Calendar month view with calendar sidebar, month grid, and day agenda

Calendar events in the OpenCloud global search

Calendar settings

Per-calendar color and search settings

Build and install

pnpm build

Copy the contents of dist/ into $OC_DATA_DIR/web/assets/apps/calendar and restart OpenCloud. The generated manifest.json is used for app discovery.

Prebuilt packages

.forgejo/workflows/build.yml runs the tests, type checks, lint, and build on every push and pull request, then packages the result as opencloud-calendar-<version>.zip.

Every push to master replaces a rolling Latest development build prerelease (latest tag) with the new zip and SHA256SUMS. Pushing a v* tag publishes a separate versioned release instead. Both are downloadable from the repository's Releases page.

The archive contains a top-level calendar/ directory, so it unpacks directly into the apps folder:

unzip opencloud-calendar-<version>.zip -d "$OC_DATA_DIR/web/assets/apps"

Then restart OpenCloud (for example systemctl restart opencloud, or restart the OpenCloud container). The workflow expects a Forgejo runner with the docker label.

Features

  • CalDAV discovery through current-user-principal and calendar-home-set
  • Calendar list with colors, visibility toggles, and appointment counts
  • Month grid with previous/next/today navigation
  • Day agenda with appointment details (time, location, description, calendar)
  • Create, edit, move, and delete appointments, including all-day and multi-day events
  • Event dialog with title, calendar, all-day/time controls, location, description, and repeat rules
  • Simple recurrence (daily, weekly, monthly, yearly) with an interval and an end by count or date
  • Whole-series editing for recurring appointments; existing recurrence exceptions are preserved
  • Recurring events, exceptions, all-day events, and multi-day events
  • Drag non-recurring appointments onto another day to reschedule them
  • Create calendars, edit their name, color, and description, and delete them
  • Per-calendar option to include or exclude appointments from global search
  • Calendar events in OpenCloud's global search (title, description, and location), including a full results page and links that open the appointment in the calendar
  • Partial-failure handling: one inaccessible calendar does not hide the others
  • Calendar creation via MKCALENDAR, property updates via PROPPATCH, and deletion via DELETE
  • Appointment mutations via conditional PUT/DELETE (If-Match/If-None-Match)

The app uses the access token from the OpenCloud session (useAuthStore) and sends it as a Bearer token. Authentication is terminated by the OpenCloud proxy, which forwards the authenticated user to Radicale (or another CalDAV backend).

Architecture

src/
  caldav/
    client.ts      CalDAV transport, discovery, event queries, calendar/event mutations
    xml.ts         PROPFIND/REPORT/MKCALENDAR/PROPPATCH builders and multistatus parsing
    ics.ts         iCalendar parser and recurrence expansion
    generate.ts    VEVENT/RRULE generation, series updates, event-to-input conversion
  search/
    provider.ts    Global search provider (preview and full list)
    calendarCache.ts Cached calendars for search
    types.ts       Search result item and type guard
  components/      Sidebar, month grid, day agenda, dialogs, color picker, search views
  utils/color.ts   Hex/HSV conversion for the in-app color picker
  utils/date.ts    Local-time date and month-grid helpers
  utils/link.ts    Appointment deep-link query helpers
  utils/search.ts  Search term extraction from KQL
  views/           Calendar page
  types.ts         Domain model shared by every layer

The layers are independent:

  • caldav/ knows HTTP, DAV XML, and iCalendar. It does not import Vue or UI code.
  • CalendarView owns asynchronous state and exposes data plus actions to its children.
  • Components receive data through props and emit intent. They do not call the network.
  • types.ts is the shared contract. Add a new field there, populate it in ics.ts, then render it in a component.

CalendarEvent keeps icsData (the original VCALENDAR), and parseCalendarObject expands recurrence without discarding exceptions. updateEventIcs rewrites the master VEVENT and preserves exceptions, so series edits and moves keep the rest of the calendar data intact. Adding a new mutation follows the same pattern: add the conditional DAV call to CalDavClient and reuse the serializer, without replacing the parser.

Development

Requirements:

  • Node.js 22 or newer
  • pnpm (use the version from the packageManager field)
  • Docker and Docker Compose

Install dependencies and start the watcher:

pnpm install
pnpm build:w

Start the development stack in another terminal:

docker compose up

Open https://host.docker.internal:9200 and sign in with admin / admin. The built app is mounted from ./dist into /web/apps/calendar and is discovered automatically. The included Radicale service creates a Personal Calendar on first access.

pnpm build:w needs to be running before the app can be loaded. A browser reload is required after a rebuild.

The stack publishes port 9200, so stop any other OpenCloud dev stack before docker compose up. To deploy into an existing instance instead, copy dist/ to $OC_DATA_DIR/web/assets/apps/calendar and restart OpenCloud.

CalDAV backend

The development setup routes CalDAV through OpenCloud. For an existing installation, add the following routes to the OpenCloud proxy configuration and point them at your CalDAV server:

additional_policies:
  - name: default
    routes:
      - endpoint: /caldav/
        backend: http://radicale:5232
        remote_user_header: X-Remote-User
        skip_x_access_token: true
        additional_headers:
          - X-Script-Name: /caldav
      - endpoint: /.well-known/caldav
        backend: http://radicale:5232
        remote_user_header: X-Remote-User
        skip_x_access_token: true
        additional_headers:
          - X-Script-Name: /caldav

The app talks to /caldav/ on the same origin as OpenCloud, so no additional CSP or CORS configuration is required.

Testing

pnpm test:unit
pnpm check:types
pnpm lint

The unit tests cover CalDAV XML parsing, iCalendar parsing (all-day and floating times, recurrence expansion, EXDATE, exceptions, cancellation, malformed data), ICS generation and recurrence rules (escaping, exclusive all-day ends, timezone cleanup, series updates), the authenticated CalDAV client (discovery, search, calendar and event mutations, ETag and 412 handling, move ordering, failure isolation, error mapping), the global search provider (KQL extraction, result mapping, source filtering, caching, limit handling), and the color, date, and deep-link helpers.