markdown · 7836 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.

This first milestone is meant for local daily trial use. It opens on the current month, keeps keyboard navigation fast, falls back to week or day views when terminal space is tight, and shows agenda previews from the current local events file plus holiday sources.

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 calendars list --account 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 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 provider. Modal/form keys stay fixed for now.

Nager.Date is cache-first and opt-in. Default startup does not need network access.

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 auth login --account work
rcal providers microsoft calendars list --account work
rcal providers microsoft sync --account work

For the current development release, users provide their own Microsoft Entra app registration client_id in config.toml. A future production release can ship an rcal-owned public/native client ID so end users do not need to create an Azure app. No client secret is used or stored; rcal stores user tokens in the OS keychain.

Microsoft Account Setup

Create a config file:

rcal config init

Register a temporary local test app in the Microsoft Entra admin center:

  • Name: rcal local test or similar.
  • Supported account type:
    • Personal Outlook/Hotmail/Live accounts: Personal Microsoft accounts only.
    • Work or school Microsoft 365 accounts: Accounts in any organizational directory.
  • Redirect URI: platform Mobile and desktop applications, value http://localhost:8765/callback.
  • API permissions: Microsoft Graph delegated User.Read and Calendars.ReadWrite.
  • If Azure refuses the account type change with api.requestedAccessTokenVersion, set the app manifest's requestedAccessTokenVersion or accessTokenAcceptedVersion to 2.

Then edit ~/.config/rcal/config.toml:

[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 = "consumers"      # personal accounts
# tenant = "organizations" # work/school accounts
redirect_port = 8765
calendars = ["CALENDAR_ID"]

Authenticate, list calendars, then copy the editable calendar ID into both default_calendar and calendars:

rcal providers microsoft auth login --account work --browser
rcal providers microsoft calendars list --account work
rcal providers microsoft sync --account work
rcal

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.

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 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.

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