markdown · 9945 bytes Raw Blame History

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 providers google auth login --account ID
rcal providers google auth logout --account ID
rcal providers google auth inspect --account ID
rcal providers google calendars list --account ID
rcal providers google setup --account ID [--client-id ID] [--client-secret SECRET] [--calendar ID]
rcal providers google sync [--account ID]
rcal providers google 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 at PATH. 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 is US.
  • --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 and Google Calendar providers. 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, c opens the Copy confirmation for the selected editable event.
  • In day view, d opens the Delete confirmation for the selected editable event.
  • Enter opens the focused day view.
  • Esc returns from day view to month view.
  • q exits.
  • 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 1 selects day 1 and 1 then 6 selects day 16.
  • Weekday initials jump within the selected week. Use tu for Tuesday, th for Thursday, su for Sunday, and sa for 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 provider events are written through their remote API 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.

Google Calendar Provider

Google Calendar support is also cache-first. You refresh remote data explicitly:

rcal providers google setup --account personal
rcal

The setup flow uses rcal's official Google OAuth Desktop client when the build includes one. It opens browser auth, selects your default editable calendar, writes ~/.config/rcal/config.toml, performs the first sync, and sets new event creation to Google by default. User tokens are stored in the OS keychain.

Use --calendar CALENDAR_ID to choose a known editable calendar. You can list calendars later with:

rcal providers google calendars list --account personal

Manual sync:

rcal providers google sync --account personal

Generated Google config looks like:

[providers]
create_target = "google" # or "local" to keep new events local-only

[providers.google]
enabled = true
default_account = "personal"
default_calendar = "primary"
sync_past_days = 30
sync_future_days = 365

[[providers.google.accounts]]
id = "personal"
# client_id = "GOOGLE_CLIENT_ID"         # optional custom OAuth client
# client_secret = "GOOGLE_CLIENT_SECRET" # optional custom OAuth client secret
redirect_port = 8766
calendars = ["primary"]

Advanced custom-client setup remains available for development builds or for users who want their own Google OAuth quota/project:

rcal providers google setup \
  --account personal \
  --client-id GOOGLE_CLIENT_ID \
  --client-secret GOOGLE_CLIENT_SECRET

The Google provider syncs configured calendars through the Calendar API, caches selected events separately from the local events JSON, and routes create/edit/delete/copy operations for Google events back through Google Calendar. Provider reminders fire from cached Google events after a sync.

Current Limits

  • CalDAV and other non-Microsoft/non-Google providers are not implemented yet.
  • Provider sync is manual and cache-first; there is no background provider sync daemon yet.
  • Google OAuth verification may still be in progress for early releases. If the official client flow is unavailable in a custom build, use the advanced custom-client setup above.

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 providers google auth login --account ID
34 rcal providers google auth logout --account ID
35 rcal providers google auth inspect --account ID
36 rcal providers google calendars list --account ID
37 rcal providers google setup --account ID [--client-id ID] [--client-secret SECRET] [--calendar ID]
38 rcal providers google sync [--account ID]
39 rcal providers google status
40 rcal reminders run [--events-file PATH] [--state-file PATH] [--once]
41 rcal reminders install [--events-file PATH] [--state-file PATH]
42 rcal reminders uninstall
43 rcal reminders status
44 rcal reminders test [--verbose]
45 ```
46
47 Options:
48
49 - `--config PATH`: load a specific TOML config file.
50 - `--no-config`: ignore any discovered config file.
51 - `--date YYYY-MM-DD`: open with a deterministic selected date.
52 - `--events-file PATH`: read and write local user-created events at `PATH`.
53 By default, rcal uses `$XDG_DATA_HOME/rcal/events.json`,
54 `$HOME/.local/share/rcal/events.json`, or a temp fallback.
55 - `--holiday-source us-federal`: use offline U.S. federal holidays. This is the
56 default.
57 - `--holiday-source off`: disable holiday rendering.
58 - `--holiday-source nager`: fetch public holidays from Nager.Date on demand.
59 - `--holiday-country CC`: two-letter country code for Nager.Date. This option
60 requires `--holiday-source nager`; default is `US`.
61 - `--help`: show CLI help.
62 - `--version`: show the installed version.
63
64 Config is discovered at `$XDG_CONFIG_HOME/rcal/config.toml`, else
65 `~/.config/rcal/config.toml`. It is never created automatically; run
66 `rcal config init` to write a commented starter file. Omitted settings keep
67 built-in defaults, and CLI flags override config values. Config can set the
68 events file, holiday source and country, reminder state file, and normal-mode
69 keybindings. It can also configure the Microsoft and Google Calendar providers.
70 Modal/form keys stay fixed for now.
71
72 Nager.Date is cache-first and opt-in. Default startup does not need network
73 access.
74
75 ## Controls
76
77 - Arrow keys move the selected date.
78 - `?` opens contextual help.
79 - `+` opens the Create event modal.
80 - In day view, `c` opens the Copy confirmation for the selected editable event.
81 - In day view, `d` opens the Delete confirmation for the selected editable event.
82 - `Enter` opens the focused day view.
83 - `Esc` returns from day view to month view.
84 - `q` exits.
85 - In day view, Left/Right move to the previous or next day while staying in day
86 view.
87 - Digits jump immediately to a day in the visible month. A quick second digit
88 refines the selected day, so `1` selects day 1 and `1` then `6` selects day
89 16.
90 - Weekday initials jump within the selected week. Use `tu` for Tuesday, `th`
91 for Thursday, `su` for Sunday, and `sa` for Saturday.
92 - Left click selects a visible date; double-click a visible date to open day
93 view.
94
95 The create/edit modal supports timed events, single-day all-day events,
96 recurrence, location, notes, and multiple reminder offsets. Its `Calendar`
97 field controls where the event is saved; use Left/Right on that field to cycle
98 between local storage and configured editable provider calendars. Local events
99 are stored as JSON, while provider events are written through their remote API
100 and then shown immediately from the provider cache.
101 Reminder notifications are delivered by a user-level background service. Use
102 `rcal reminders install` to install it, `rcal reminders status` to inspect it,
103 and `rcal reminders test` to send a test notification. On macOS, notification
104 delivery uses `osascript` because it is more reliable for CLI-launched
105 notifications than the generic notification backend. Reminder install snapshots
106 the resolved events and state file paths, so reinstall the service after config
107 changes that affect reminders.
108
109 ## Layout
110
111 `rcal` tries to render the full month first. If the terminal is too constrained,
112 it falls back to the selected week. If even that cannot fit cleanly, it falls
113 back to a focused day summary.
114
115
116 ## Microsoft Provider
117
118 Microsoft Graph is the first remote provider. It is cache-first: the TUI reads
119 the local Microsoft cache instantly, and you refresh remote data explicitly:
120
121 ```sh
122 rcal providers microsoft setup --account work --browser
123 rcal
124 ```
125
126 The setup command uses rcal's official public/native Microsoft client ID, opens
127 the Microsoft login flow, selects your default editable calendar, writes
128 `~/.config/rcal/config.toml`, performs the first sync, and sets new event
129 creation to Microsoft by default. No client secret is used or stored; rcal
130 stores user tokens in the OS keychain.
131
132 Use `--calendar CALENDAR_ID` if you already know which editable calendar to use.
133 You can list calendars later with:
134
135 ```sh
136 rcal providers microsoft calendars list --account work
137 ```
138
139 ### Microsoft Account Setup
140
141 The normal setup flow is:
142
143 ```sh
144 rcal providers microsoft setup --account work --browser
145 rcal providers microsoft status
146 rcal
147 ```
148
149 For advanced testing, you may still override the Microsoft app registration in
150 `config.toml`. Omit `client_id` and `tenant` to use rcal's official public app:
151
152 ```toml
153 [providers]
154 create_target = "microsoft" # or "local" to keep new events local-only
155
156 [providers.microsoft]
157 enabled = true
158 default_account = "work"
159 default_calendar = "CALENDAR_ID"
160 sync_past_days = 30
161 sync_future_days = 365
162
163 [[providers.microsoft.accounts]]
164 id = "work"
165 # client_id = "AZURE_APP_CLIENT_ID"
166 # tenant = "common"
167 redirect_port = 8765
168 calendars = ["CALENDAR_ID"]
169 ```
170
171 Manual sync remains available:
172
173 ```sh
174 rcal providers microsoft sync --account work
175 ```
176
177 Deprecated manual onboarding flow, pending deletion:
178
179 ```sh
180 rcal config init
181 # edit ~/.config/rcal/config.toml with providers.microsoft account/calendar data
182 rcal providers microsoft auth login --account work --browser
183 rcal providers microsoft calendars list --account work
184 # copy an editable calendar ID into default_calendar and calendars
185 rcal providers microsoft sync --account work
186 ```
187
188 Use `rcal providers microsoft setup --account work --browser` instead. The
189 manual flow is kept temporarily for advanced custom-app testing and for users
190 upgrading from early provider builds.
191
192 Use `rcal providers microsoft auth inspect --account work` to inspect safe
193 token claims such as audience, scopes, tenant, and expiry. The command does not
194 print the token body.
195
196 The provider syncs configured calendars through Graph `calendarView`, caches
197 selected events separately from the local events JSON, and routes
198 create/edit/delete/copy operations for Microsoft events back through Graph.
199 Provider reminders fire from cached provider events after a sync; the reminder
200 daemon does not sync remote calendars itself.
201
202 ## Google Calendar Provider
203
204 Google Calendar support is also cache-first. You refresh remote data explicitly:
205
206 ```sh
207 rcal providers google setup --account personal
208 rcal
209 ```
210
211 The setup flow uses rcal's official Google OAuth Desktop client when the build
212 includes one. It opens browser auth, selects your default editable calendar,
213 writes `~/.config/rcal/config.toml`, performs the first sync, and sets new
214 event creation to Google by default. User tokens are stored in the OS keychain.
215
216 Use `--calendar CALENDAR_ID` to choose a known editable calendar. You can list
217 calendars later with:
218
219 ```sh
220 rcal providers google calendars list --account personal
221 ```
222
223 Manual sync:
224
225 ```sh
226 rcal providers google sync --account personal
227 ```
228
229 Generated Google config looks like:
230
231 ```toml
232 [providers]
233 create_target = "google" # or "local" to keep new events local-only
234
235 [providers.google]
236 enabled = true
237 default_account = "personal"
238 default_calendar = "primary"
239 sync_past_days = 30
240 sync_future_days = 365
241
242 [[providers.google.accounts]]
243 id = "personal"
244 # client_id = "GOOGLE_CLIENT_ID" # optional custom OAuth client
245 # client_secret = "GOOGLE_CLIENT_SECRET" # optional custom OAuth client secret
246 redirect_port = 8766
247 calendars = ["primary"]
248 ```
249
250 Advanced custom-client setup remains available for development builds or for
251 users who want their own Google OAuth quota/project:
252
253 ```sh
254 rcal providers google setup \
255 --account personal \
256 --client-id GOOGLE_CLIENT_ID \
257 --client-secret GOOGLE_CLIENT_SECRET
258 ```
259
260 The Google provider syncs configured calendars through the Calendar API,
261 caches selected events separately from the local events JSON, and routes
262 create/edit/delete/copy operations for Google events back through Google
263 Calendar. Provider reminders fire from cached Google events after a sync.
264
265
266 ## Current Limits
267
268 - CalDAV and other non-Microsoft/non-Google providers are not implemented yet.
269 - Provider sync is manual and cache-first; there is no background provider
270 sync daemon yet.
271 - Google OAuth verification may still be in progress for early releases. If the
272 official client flow is unavailable in a custom build, use the advanced
273 custom-client setup above.
274
275 ## Development
276
277 CI runs the same core commands used locally:
278
279 ```sh
280 cargo fmt --all -- --check
281 cargo clippy --all-targets --all-features -- -D warnings
282 cargo test --all-targets --all-features
283 ```
284
285 The project intentionally keeps private planning notes under `.docs/`; they are
286 not part of the tracked release surface.