yinshaohua287 downloadsA unified calendar view syncing events from Google Calendar, CalDAV, and ICS feeds into one beautiful interface.
A unified calendar view for Obsidian that aggregates events from Google Calendar, CalDAV servers, and ICS feeds into a single month view — with Chinese lunar calendar, solar terms, and public holiday annotations.

.ics URLisDesktopOnly: false)main.js, manifest.json, and styles.css from the latest release<Vault>/.obsidian/plugins/uni-calendar/Open Settings → UniCalendar to configure calendar sources.
UniCalendar supports three Google proxy modes:
host and port, for example 127.0.0.1 and 7897.Custom proxy mode is for standard HTTP CONNECT proxies. UniCalendar opens a CONNECT tunnel to Google, upgrades it to TLS, and then sends the original Google OAuth token and Calendar API requests through that tunnel. It is not a forwarding endpoint and does not use a target= query parameter.
The legacy Google proxy address field is kept only for older saved data migration. New saves store proxyMode, proxyHost, and proxyPort, and clear the old proxyUrl value.
For Feishu/Lark CalDAV performance notes and troubleshooting, see Feishu CalDAV diagnostics.
| Setting | Default | Description |
|---|---|---|
| Sync interval | 15 min | How often to auto-sync all sources |
| Show lunar calendar | On | Display lunar dates and festivals in month cells |
| Show holidays | On | Overlay public holiday and workday annotations |
| Month overflow mode | Expand | How to handle cells with more events than fit |
Use Settings → UniCalendar → Event title filters to hide noisy events across all calendar sources without deleting the underlying event data.
Each rule supports two modes:
Examples:
WaytoAGI晚8点共学 with exact match hides only events whose title is exactly WaytoAGI晚8点共学WaytoAGI with contains hides any event whose title contains WaytoAGIMatching is case-insensitive and ignores leading/trailing or repeated whitespace.
Ctrl/Cmd+P) and search for Open calendar< and > arrows in the view headerUniCalendar operates entirely locally. Calendar credentials are stored in your vault's plugin data file (<Vault>/.obsidian/plugins/uni-calendar/data.json). No data is sent to any third-party service other than the calendar providers you explicitly configure. Holiday data is fetched from the jsdelivr CDN on first load and cached locally.
UniCalendar is an Obsidian community plugin. The TypeScript entry point is main.ts, bundled by esbuild into root-level main.js, which Obsidian loads together with manifest.json and optional styles.css.
src/
main.ts # Plugin lifecycle, command/view registration
lunar/ # Lunar calendar, solar terms, and holiday services
models/ # Shared domain types
settings/ # Settings tab and configuration UI
store/ # Event storage, filtering, and deduplication
sync/ # Google, CalDAV, and ICS sync adapters
views/ # Calendar view and event detail modal
tests/ # Vitest test suites and Obsidian mocks
.planning/ # Version-controlled GSD Core plans and history
Keep src/main.ts small and place feature logic in focused modules under src/.
Project planning uses GSD Core. Its project context, requirements, roadmap, phase plans, summaries, and milestone archive are version controlled under .planning/. The local .gsd/ directory is retained only as a legacy migration source and is not used for new planning work.
Install dependencies into the project-local node_modules/ directory from the repository root. No PowerShell Profile function or external dependency directory is required:
npm install
# Install dependencies.
npm install
# Watch mode, auto-recompile on save.
npm run dev
# Production build.
npm run build
# Run tests.
npm test
# Lint.
npm run lint
Copy main.js, manifest.json, and styles.css to <Vault>/.obsidian/plugins/uni-calendar/, then reload Obsidian and enable the plugin in Settings → Community plugins.
manifest.json fields accurate, including id, name, version, minAppVersion, description, and isDesktopOnly.manifest.json id as stable after release.manifest.json and versions.json.v.manifest.json, main.js, and styles.css as individual release assets.isDesktopOnly is false.