rcal
rcal is a terminal calendar for quick month, week, and day navigation. It is
built in Rust with ratatui, crossterm, and the time crate.
Install
From a checkout of this repository:
cargo install --path .
For development:
cargo run -- --date 2026-04-23
Usage
rcal [--config PATH|--no-config] [--date YYYY-MM-DD] [--events-file PATH] [--holiday-source off|us-federal|nager] [--holiday-country CC]
rcal config init [--path PATH] [--force]
rcal providers microsoft auth login --account ID [--browser]
rcal providers microsoft auth logout --account ID
rcal providers microsoft auth inspect --account ID
rcal providers microsoft calendars list --account ID
rcal providers microsoft setup --account ID [--browser] [--calendar ID]
rcal providers microsoft sync [--account ID]
rcal providers microsoft status
rcal reminders run [--events-file PATH] [--state-file PATH] [--once]
rcal reminders install [--events-file PATH] [--state-file PATH]
rcal reminders uninstall
rcal reminders status
rcal reminders test [--verbose]
Options:
--config PATH: load a specific TOML config file.--no-config: ignore any discovered config file.--date YYYY-MM-DD: open with a deterministic selected date.--events-file PATH: read and write local user-created events atPATH. By default, rcal uses$XDG_DATA_HOME/rcal/events.json,$HOME/.local/share/rcal/events.json, or a temp fallback.--holiday-source us-federal: use offline U.S. federal holidays. This is the default.--holiday-source off: disable holiday rendering.--holiday-source nager: fetch public holidays from Nager.Date on demand.--holiday-country CC: two-letter country code for Nager.Date. This option requires--holiday-source nager; default isUS.--help: show CLI help.--version: show the installed version.
Config is discovered at $XDG_CONFIG_HOME/rcal/config.toml, else
~/.config/rcal/config.toml. It is never created automatically; run
rcal config init to write a commented starter file. Omitted settings keep
built-in defaults, and CLI flags override config values. Config can set the
events file, holiday source and country, reminder state file, and normal-mode
keybindings. It can also configure the Microsoft provider. Modal/form keys stay
fixed for now.
Nager.Date is cache-first and opt-in. Default startup does not need network access.
Controls
- Arrow keys move the selected date.
?opens contextual help.+opens the Create event modal.- In day view,
copens the Copy confirmation for the selected editable event. - In day view,
dopens the Delete confirmation for the selected editable event. Enteropens the focused day view.Escreturns from day view to month view.qexits.- In day view, Left/Right move to the previous or next day while staying in day view.
- Digits jump immediately to a day in the visible month. A quick second digit
refines the selected day, so
1selects day 1 and1then6selects day 16. - Weekday initials jump within the selected week. Use
tufor Tuesday,thfor Thursday,sufor Sunday, andsafor Saturday. - Left click selects a visible date; double-click a visible date to open day view.
The create/edit modal supports timed events, single-day all-day events,
recurrence, location, notes, and multiple reminder offsets. Its Calendar
field controls where the event is saved; use Left/Right on that field to cycle
between local storage and configured editable provider calendars. Local events
are stored as JSON, while Microsoft events are written through Graph and then
shown immediately from the provider cache.
Reminder notifications are delivered by a user-level background service. Use
rcal reminders install to install it, rcal reminders status to inspect it,
and rcal reminders test to send a test notification. On macOS, notification
delivery uses osascript because it is more reliable for CLI-launched
notifications than the generic notification backend. Reminder install snapshots
the resolved events and state file paths, so reinstall the service after config
changes that affect reminders.
Layout
rcal tries to render the full month first. If the terminal is too constrained,
it falls back to the selected week. If even that cannot fit cleanly, it falls
back to a focused day summary.
Microsoft Provider
Microsoft Graph is the first remote provider. It is cache-first: the TUI reads the local Microsoft cache instantly, and you refresh remote data explicitly:
rcal providers microsoft setup --account work --browser
rcal
The setup command uses rcal's official public/native Microsoft client ID, opens
the Microsoft login flow, selects your default editable calendar, writes
~/.config/rcal/config.toml, performs the first sync, and sets new event
creation to Microsoft by default. No client secret is used or stored; rcal
stores user tokens in the OS keychain.
Use --calendar CALENDAR_ID if you already know which editable calendar to use.
You can list calendars later with:
rcal providers microsoft calendars list --account work
Microsoft Account Setup
The normal setup flow is:
rcal providers microsoft setup --account work --browser
rcal providers microsoft status
rcal
For advanced testing, you may still override the Microsoft app registration in
config.toml. Omit client_id and tenant to use rcal's official public app:
[providers]
create_target = "microsoft" # or "local" to keep new events local-only
[providers.microsoft]
enabled = true
default_account = "work"
default_calendar = "CALENDAR_ID"
sync_past_days = 30
sync_future_days = 365
[[providers.microsoft.accounts]]
id = "work"
# client_id = "AZURE_APP_CLIENT_ID"
# tenant = "common"
redirect_port = 8765
calendars = ["CALENDAR_ID"]
Manual sync remains available:
rcal providers microsoft sync --account work
Deprecated manual onboarding flow, pending deletion:
rcal config init
# edit ~/.config/rcal/config.toml with providers.microsoft account/calendar data
rcal providers microsoft auth login --account work --browser
rcal providers microsoft calendars list --account work
# copy an editable calendar ID into default_calendar and calendars
rcal providers microsoft sync --account work
Use rcal providers microsoft setup --account work --browser instead. The
manual flow is kept temporarily for advanced custom-app testing and for users
upgrading from early provider builds.
Use rcal providers microsoft auth inspect --account work to inspect safe
token claims such as audience, scopes, tenant, and expiry. The command does not
print the token body.
The provider syncs configured calendars through Graph calendarView, caches
selected events separately from the local events JSON, and routes
create/edit/delete/copy operations for Microsoft events back through Graph.
Provider reminders fire from cached provider events after a sync; the reminder
daemon does not sync remote calendars itself.
Current Limits
- Google Calendar, CalDAV, and other providers are not implemented yet.
- Microsoft sync is manual and cache-first; there is no background provider sync daemon yet.
- Packaging is currently source-based through Cargo.
Development
CI runs the same core commands used locally:
cargo fmt --all -- --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test --all-targets --all-features
The project intentionally keeps private planning notes under .docs/; they are
not part of the tracked release surface.
View source
| 1 | # rcal |
| 2 | |
| 3 | `rcal` is a terminal calendar for quick month, week, and day navigation. It is |
| 4 | built in Rust with `ratatui`, `crossterm`, and the `time` crate. |
| 5 | |
| 6 | |
| 7 | ## Install |
| 8 | |
| 9 | From a checkout of this repository: |
| 10 | |
| 11 | ```sh |
| 12 | cargo install --path . |
| 13 | ``` |
| 14 | |
| 15 | For development: |
| 16 | |
| 17 | ```sh |
| 18 | cargo run -- --date 2026-04-23 |
| 19 | ``` |
| 20 | |
| 21 | ## Usage |
| 22 | |
| 23 | ```sh |
| 24 | rcal [--config PATH|--no-config] [--date YYYY-MM-DD] [--events-file PATH] [--holiday-source off|us-federal|nager] [--holiday-country CC] |
| 25 | rcal config init [--path PATH] [--force] |
| 26 | rcal providers microsoft auth login --account ID [--browser] |
| 27 | rcal providers microsoft auth logout --account ID |
| 28 | rcal providers microsoft auth inspect --account ID |
| 29 | rcal providers microsoft calendars list --account ID |
| 30 | rcal providers microsoft setup --account ID [--browser] [--calendar ID] |
| 31 | rcal providers microsoft sync [--account ID] |
| 32 | rcal providers microsoft status |
| 33 | rcal reminders run [--events-file PATH] [--state-file PATH] [--once] |
| 34 | rcal reminders install [--events-file PATH] [--state-file PATH] |
| 35 | rcal reminders uninstall |
| 36 | rcal reminders status |
| 37 | rcal reminders test [--verbose] |
| 38 | ``` |
| 39 | |
| 40 | Options: |
| 41 | |
| 42 | - `--config PATH`: load a specific TOML config file. |
| 43 | - `--no-config`: ignore any discovered config file. |
| 44 | - `--date YYYY-MM-DD`: open with a deterministic selected date. |
| 45 | - `--events-file PATH`: read and write local user-created events at `PATH`. |
| 46 | By default, rcal uses `$XDG_DATA_HOME/rcal/events.json`, |
| 47 | `$HOME/.local/share/rcal/events.json`, or a temp fallback. |
| 48 | - `--holiday-source us-federal`: use offline U.S. federal holidays. This is the |
| 49 | default. |
| 50 | - `--holiday-source off`: disable holiday rendering. |
| 51 | - `--holiday-source nager`: fetch public holidays from Nager.Date on demand. |
| 52 | - `--holiday-country CC`: two-letter country code for Nager.Date. This option |
| 53 | requires `--holiday-source nager`; default is `US`. |
| 54 | - `--help`: show CLI help. |
| 55 | - `--version`: show the installed version. |
| 56 | |
| 57 | Config is discovered at `$XDG_CONFIG_HOME/rcal/config.toml`, else |
| 58 | `~/.config/rcal/config.toml`. It is never created automatically; run |
| 59 | `rcal config init` to write a commented starter file. Omitted settings keep |
| 60 | built-in defaults, and CLI flags override config values. Config can set the |
| 61 | events file, holiday source and country, reminder state file, and normal-mode |
| 62 | keybindings. It can also configure the Microsoft provider. Modal/form keys stay |
| 63 | fixed for now. |
| 64 | |
| 65 | Nager.Date is cache-first and opt-in. Default startup does not need network |
| 66 | access. |
| 67 | |
| 68 | ## Controls |
| 69 | |
| 70 | - Arrow keys move the selected date. |
| 71 | - `?` opens contextual help. |
| 72 | - `+` opens the Create event modal. |
| 73 | - In day view, `c` opens the Copy confirmation for the selected editable event. |
| 74 | - In day view, `d` opens the Delete confirmation for the selected editable event. |
| 75 | - `Enter` opens the focused day view. |
| 76 | - `Esc` returns from day view to month view. |
| 77 | - `q` exits. |
| 78 | - In day view, Left/Right move to the previous or next day while staying in day |
| 79 | view. |
| 80 | - Digits jump immediately to a day in the visible month. A quick second digit |
| 81 | refines the selected day, so `1` selects day 1 and `1` then `6` selects day |
| 82 | 16. |
| 83 | - Weekday initials jump within the selected week. Use `tu` for Tuesday, `th` |
| 84 | for Thursday, `su` for Sunday, and `sa` for Saturday. |
| 85 | - Left click selects a visible date; double-click a visible date to open day |
| 86 | view. |
| 87 | |
| 88 | The create/edit modal supports timed events, single-day all-day events, |
| 89 | recurrence, location, notes, and multiple reminder offsets. Its `Calendar` |
| 90 | field controls where the event is saved; use Left/Right on that field to cycle |
| 91 | between local storage and configured editable provider calendars. Local events |
| 92 | are stored as JSON, while Microsoft events are written through Graph and then |
| 93 | shown immediately from the provider cache. |
| 94 | Reminder notifications are delivered by a user-level background service. Use |
| 95 | `rcal reminders install` to install it, `rcal reminders status` to inspect it, |
| 96 | and `rcal reminders test` to send a test notification. On macOS, notification |
| 97 | delivery uses `osascript` because it is more reliable for CLI-launched |
| 98 | notifications than the generic notification backend. Reminder install snapshots |
| 99 | the resolved events and state file paths, so reinstall the service after config |
| 100 | changes that affect reminders. |
| 101 | |
| 102 | ## Layout |
| 103 | |
| 104 | `rcal` tries to render the full month first. If the terminal is too constrained, |
| 105 | it falls back to the selected week. If even that cannot fit cleanly, it falls |
| 106 | back to a focused day summary. |
| 107 | |
| 108 | |
| 109 | ## Microsoft Provider |
| 110 | |
| 111 | Microsoft Graph is the first remote provider. It is cache-first: the TUI reads |
| 112 | the local Microsoft cache instantly, and you refresh remote data explicitly: |
| 113 | |
| 114 | ```sh |
| 115 | rcal providers microsoft setup --account work --browser |
| 116 | rcal |
| 117 | ``` |
| 118 | |
| 119 | The setup command uses rcal's official public/native Microsoft client ID, opens |
| 120 | the Microsoft login flow, selects your default editable calendar, writes |
| 121 | `~/.config/rcal/config.toml`, performs the first sync, and sets new event |
| 122 | creation to Microsoft by default. No client secret is used or stored; rcal |
| 123 | stores user tokens in the OS keychain. |
| 124 | |
| 125 | Use `--calendar CALENDAR_ID` if you already know which editable calendar to use. |
| 126 | You can list calendars later with: |
| 127 | |
| 128 | ```sh |
| 129 | rcal providers microsoft calendars list --account work |
| 130 | ``` |
| 131 | |
| 132 | ### Microsoft Account Setup |
| 133 | |
| 134 | The normal setup flow is: |
| 135 | |
| 136 | ```sh |
| 137 | rcal providers microsoft setup --account work --browser |
| 138 | rcal providers microsoft status |
| 139 | rcal |
| 140 | ``` |
| 141 | |
| 142 | For advanced testing, you may still override the Microsoft app registration in |
| 143 | `config.toml`. Omit `client_id` and `tenant` to use rcal's official public app: |
| 144 | |
| 145 | ```toml |
| 146 | [providers] |
| 147 | create_target = "microsoft" # or "local" to keep new events local-only |
| 148 | |
| 149 | [providers.microsoft] |
| 150 | enabled = true |
| 151 | default_account = "work" |
| 152 | default_calendar = "CALENDAR_ID" |
| 153 | sync_past_days = 30 |
| 154 | sync_future_days = 365 |
| 155 | |
| 156 | [[providers.microsoft.accounts]] |
| 157 | id = "work" |
| 158 | # client_id = "AZURE_APP_CLIENT_ID" |
| 159 | # tenant = "common" |
| 160 | redirect_port = 8765 |
| 161 | calendars = ["CALENDAR_ID"] |
| 162 | ``` |
| 163 | |
| 164 | Manual sync remains available: |
| 165 | |
| 166 | ```sh |
| 167 | rcal providers microsoft sync --account work |
| 168 | ``` |
| 169 | |
| 170 | Deprecated manual onboarding flow, pending deletion: |
| 171 | |
| 172 | ```sh |
| 173 | rcal config init |
| 174 | # edit ~/.config/rcal/config.toml with providers.microsoft account/calendar data |
| 175 | rcal providers microsoft auth login --account work --browser |
| 176 | rcal providers microsoft calendars list --account work |
| 177 | # copy an editable calendar ID into default_calendar and calendars |
| 178 | rcal providers microsoft sync --account work |
| 179 | ``` |
| 180 | |
| 181 | Use `rcal providers microsoft setup --account work --browser` instead. The |
| 182 | manual flow is kept temporarily for advanced custom-app testing and for users |
| 183 | upgrading from early provider builds. |
| 184 | |
| 185 | Use `rcal providers microsoft auth inspect --account work` to inspect safe |
| 186 | token claims such as audience, scopes, tenant, and expiry. The command does not |
| 187 | print the token body. |
| 188 | |
| 189 | The provider syncs configured calendars through Graph `calendarView`, caches |
| 190 | selected events separately from the local events JSON, and routes |
| 191 | create/edit/delete/copy operations for Microsoft events back through Graph. |
| 192 | Provider reminders fire from cached provider events after a sync; the reminder |
| 193 | daemon does not sync remote calendars itself. |
| 194 | |
| 195 | |
| 196 | ## Current Limits |
| 197 | |
| 198 | - Google Calendar, CalDAV, and other providers are not implemented yet. |
| 199 | - Microsoft sync is manual and cache-first; there is no background provider |
| 200 | sync daemon yet. |
| 201 | - Packaging is currently source-based through Cargo. |
| 202 | |
| 203 | ## Development |
| 204 | |
| 205 | CI runs the same core commands used locally: |
| 206 | |
| 207 | ```sh |
| 208 | cargo fmt --all -- --check |
| 209 | cargo clippy --all-targets --all-features -- -D warnings |
| 210 | cargo test --all-targets --all-features |
| 211 | ``` |
| 212 | |
| 213 | The project intentionally keeps private planning notes under `.docs/`; they are |
| 214 | not part of the tracked release surface. |