Skip to content

Calendar and Timetable

P3DROVFX edited this page Oct 12, 2026 · 1 revision

Calendar and Timetable

The Timetable is the calendar tab of the Cheat Sheet (Super + /, then the first tab). It is a real, editable calendar — day, 3-day, week and month views, a side rail for details and editing, recurring events with per-occurrence exceptions, tasks with due dates, public holidays, contact birthdays, moon phases, live ESPN scores and notifications — and it is the single calendar the rest of the shell reads: the Calendar desktop widgets, At a Glance, the lock screen, the dock, the Clock app's alarm suggestions, Modes & Routines and the AI assistant.

It does not talk to Google or Microsoft directly. Events live as ordinary .ics files read by khal, and vdirsyncer keeps those files in sync with Google Calendar, Nextcloud, Fastmail, a public ICS URL or any CalDAV server. The wiring, the setup commands and the failure modes are on Khal Integration; this page is about what you can do in the Timetable.

Without khal the Timetable still opens — tasks, holidays, birthdays and sports work — and says No calendar backend / Install khal to create and browse events here. Everything that creates or reads events needs khal.

Quick start

  1. Install khal and vdirsyncer and point them at your calendar (the Set up Google Calendar tutorial in the Welcome app walks through it; see Khal Integration). Local-only use needs only khal and one local calendar directory.
  2. Super + / opens the Cheat Sheet on the Timetable. The view switch in the header (next to the close button) picks Day · 3 days · Week · Month; Ctrl + V cycles them, and Ctrl + Shift + D (or 1) / 2 or 3 / W / M (or 4) jump straight to Day / 3 days / Week / Month.
  3. Click a day to open it in the right rail; click New event (or a free slot in the week grid) to create one. A new event appears immediately and is written to the calendar in the background.

Views

Month

A grid whose cell geometry is computed (not a GridLayout), so the day under the cursor is exact during a drag. Nothing scrolls: the month always fits, and a cell with no room collapses into a +N counter.

  • Chips for events, round pills for sports (sports_score), square-check chips for tasks, a cake chip for birthdays; recurring series with two or more occurrences can be shown as continuous bars (Group recurring events) instead of repeating chips.
  • Density — comfortable (24 px chips), compact (16 px) or dots.
  • Drag to move. Drag an event chip to another day. A floating proxy follows the pointer and the event moves to that day. Recurring events ask for the scope first (below).
  • Left rail — Upcoming. Today's hero, then every day that has something within calendar.timetable.upcomingHorizonDays (14), grouped Today / Tomorrow / This week / Later; overdue tasks sit in their own block above Today; groups collapse and remember it.
  • Day cells show the weather icon and, with the option on, the moon phase; public holidays tint the cell; weekends are marked with a diagonal-hatch texture rather than a second colour, so it survives the today / holiday / hover fills.
  • Backlog chip. Tasks without a due date become one chip on today's cell ("N tasks waiting for a day"), which opens the full undated list in the rail.
  • Label filter from the ICS categories present in the visible events.

Day, 3 days and Week

One time grid parametrised by the number of visible days (1, 3 or 7). The hour scale zooms with Ctrl + scroll in steps (40 / 56 / 72 / 96 / 120 px per hour), keeping the minute under the pointer fixed. The header combines the weekday with the real date, shows the day's weather (min / max in your unit), an all-day strip (up to two rows, then +N more expands it), a pill with the day's sports count, public holidays, and a floating next event button that appears when the next event is scrolled out of view and scrolls to it. A timed block with a recognised Meet, Zoom or Teams link shows a one-click Join action. A faint current-time line crosses today, with the local time zone (GMT±HH) and ISO week number at the top of the hour gutter. Start with today (cheatsheet.timetableTodayFirst) makes the week and 3-day views begin on today instead of the first day of the configured week (time.firstDayOfWeek).

Navigation: arrows, Page Up/Down, Home and Enter work in both grids; in the week view up/down scroll an hour, in the month view they move a week.

The day briefing

The day page in the rail starts with a briefing card: how many events are left today, overdue tasks, what is next, and a Plan my day / Ask AI about today button that hands the same facts to the AI chat. Everything on the card comes from data already loaded; nothing is fetched for it.


Creating and editing events

All views share one right rail (EventSidebar) with pages: day (the day's agenda and holidays), details, edit, create, sources and tasks. It takes room from the grid instead of covering it, so the date you are working on stays visible. Dates and times open the Material pickers (a typed field plus a dial; 24-hour locales get a two-ring dial).

The editor covers: title, calendar (only writable ones are offered; the last one you used is remembered as the default), all-day, start and end, repeat (daily / weekly / monthly / yearly with weekday selection, "repeats until" or never), location, a meeting link, guests and organizer (display), status (Confirmed / Tentative / Cancelled), alarms (at the start, or a number of minutes, hours or days before), colour, labels (ICS categories, comma separated) and notes. Event ↔ Task is a toggle at the top: a task shares title, date and notes but drops the time-only fields.

Extra rows in the details:

  • Ring before it starts — turns the event into a Clock app alarm (the alarm remembers the event's UID, so changing the lead time replaces it rather than adding another).
  • Start a countdown for the time left in this event.
  • Attach a note — links the event to a note in the Notes app (stored in timetable-event-notes.json beside the notes), and Open attached note jumps to it.
  • For recurring events: the next five occurrences, and the list of exceptions (EXDATE), each with a Restore button.

Recurring events: three scopes

Edit, delete, move and resize all ask Choose how much of this series changes:

Scope What is written
Only this event An exception: EXDATE for a delete, an override occurrence for an edit or move
This and future The series is truncated before this occurrence and a new series starts here
Entire series The master event

Properties the interface does not expose (alarms, URL, LOCATION, RRULE) are preserved by every edit: the writer changes only the fields you changed.

Colours and labels

Each event can carry a semantic colour token — primary, secondary, tertiary or error — in a COLOR property, so the Material You palette stays the only source of real colours. The calendar's own colour is chosen in the calendar-colour settings from a small curated set — Primary, Secondary, Tertiary, Error, Cyan, Yellow or none — and stored in khal's config as the matching ANSI name (light blue, light green, light magenta, light red, light cyan, yellow), then rendered with the corresponding Material You token. Order of resolution: Google colour (if enabled) → event token → calendar colour → the legacy rotation. Proximity colour gradient (opt-in) paints the day / 3-day / week views with a gradient by distance from the next event instead.


Tasks in the calendar

Tasks come from the same provider as the Dashboard's To Do (local, TickTick or Google Tasks — see To-Do and Tasks). A task with a due date appears on that day; an unfinished task past due is pinned to today in the error colour instead of staying on its old day. Tasks complete from the chip; the due date goes to TickTick (dueDate) or Google Tasks (due) without shifting a day in time zones west of UTC.

Holidays, birthdays, moon, weather

  • Public holidays come from the free Nager.Date API for your country (calendar.holidays.countryCode, auto derives it from the system locale), fetched once per year and cached on disk; they colour the month cells and list in the rail.
  • Contact birthdays (calendar.timetable.birthdays.enable, off) come from the phone's contacts through KDE Connect — vCard BDAY, with or without a year (shown as "N years old"). They are a read-only projection: they never create a khal event. 29 February falls on 28 February in non-leap years.
  • Moon phases (moonPhases.enable, off) put an 8-phase badge beside the weather icon with the illuminated percentage in the tooltip; computed locally (no network), accurate to well under an hour.
  • Weather in the headers is the already-loaded Weather forecast; the Timetable has no fetch of its own.

Sports (ESPN)

Turn on Show sports events (calendar.timetable.sportsEvents, off) and the leagues and clubs you follow (the ones set for the bar's sports widget) appear as read-only games: filled pills in the month view, a count pill per day in the week view, and a rich match page in the rail — line-ups, score and statistics, leaders, a timeline with commentary, venue, broadcast, referees, recent form, head to head, news, odds, injuries and standings when the sport provides them. Games never enter khal and have no UID; they cannot be edited or dragged. Details of the service are on Sports Scores.


Calendar sources

The Calendar sources button (calendar_add_on) opens the rail's sources page. It is gated by Enable calendar sources (imports.enable, off); turning it off disconnects subscribed links from the managed vdirsyncer / khal sections but keeps the list.

  • Import ICS file — a local .ics / .ical up to 1 MiB, with up to 1000 events. UIDs are preserved (a stable UUIDv5 is generated only when a file has none), a file imported twice is skipped by UID, and read-only calendars are refused before anything is written.
  • Subscribed links — a public http(s) ICS URL mirrored as a read-only calendar. file:// and local paths are never accepted. The shell edits only its own marked blocks (II TIMETABLE SUBSCRIPTIONS) of the vdirsyncer and khal configs, writes both files atomically, and leaves everything else in them alone.
  • Gmail ICS attachments (imports.gmailIcs.enable) — scans the active Gmail account for calendar attachments (looking at up to maxMessages = 25 messages per scan, importing at most 20 attachments of 512 KiB, every scanIntervalMinutes = 60) and imports each once. It never reads the body, sender, subject or snippet; the processed account|attachment|hash keys (last 400) are remembered. See Gmail Integration.
  • Outlook (imports.outlook.enable) — a read-only mirror of your Microsoft calendar through Microsoft Graph, registered as the local read-only collection ii_timetable_outlook, so it shows in every view but can never be edited or written back by vdirsyncer. Sign-in is the OAuth device-code flow of a public client: you register an app in the Microsoft Entra admin center (enable public client flows), paste only its Application (client) ID, open the Microsoft page and enter the code. There is no client secret anywhere; the refresh token lives in the system keyring. Scopes are read-only (Calendars.Read, Mail.Read, User.Read, offline_access; the Clock app's Reminders sync asks for To Do on top). Every occurrence becomes its own ICS file with a stable UUIDv5 and an X-II-TIMETABLE-SOURCE: outlook marker; the mirror is reconciled with fsync + atomic rename and removes only marked files after a complete Graph response, so a network failure never empties your calendar. An optional scanner (icsAttachments.enable) also imports .ics attachments from your mailbox with the same limits.

Google event colours

Google keeps an event's colour in the Calendar API (colorId) — the .ics files you sync have none. Show Google event colors (googleColors.enable, off) reads the palette and per-event colours through the Calendar API (an additional authorization, Connect Google Calendar, with its own scope — the Tasks or Gmail grants do not cover calendars) and paints chips with the colour you chose in Google. The map is refreshed every refreshHours (6) and cached on disk, so the first frame is already coloured; with the feature off nothing is requested. In the details, the Google colour row writes a new colour back with a PATCH.

Notifications

Calendar notifications are real shell notifications (history, DND, lock-screen rules all apply). Every minute the next 24 hours are checked; an event's own VALARMs win, otherwise the default offsets apply (calendar.timetable.notifications.offsets, default -15m; choices At start, 5 / 15 min, 1 h, 1 day before). Options: Notify all-day events, a Daily summary at a time of your choice, and a sound. Actions on the notification: Open (opens the Timetable on that day), Join (when there is a Meet / Teams / Zoom link) and Snooze 5 m / 1 h. A delivered occurrence is remembered by occurrence-ms|uid|offset, so a reload never repeats it.


Settings

Open them from the gear in the Cheat Sheet while on the Timetable (Timetable settings): Timetable display (start with today, moon phases, birthdays, sports, proximity gradient), Calendars, reminders and sources (default reminder offsets, daily summary, calendar colours, subscribed links, Outlook), Google Calendar API (connect, colours, refresh interval) and the khal & sync setup guide. The status card at the top reports khal availability, the last sync result and an Auth required state with a Reconnect button.

Configuration (Config.options.calendar)

Key Default Meaning
holidays.enable / countryCode / showInMonthView on / auto / on Public holidays
timetable.defaultCalendar "" The khal collection new events go to (falls back to khal's own default, then the first writable)
timetable.subscriptions [] Subscribed ICS URLs
timetable.imports.enable off Master switch for sources
timetable.imports.gmailIcs.* off, 25, 60 enable, maxMessages, scanIntervalMinutes
timetable.imports.outlook.* off, 60 enable, syncIntervalMinutes, icsAttachments.*
timetable.sportsEvents off ESPN games
timetable.upcomingHorizonDays 14 Upcoming rail (clamped 1–60)
timetable.proximityColorGradient off Gradient mode
timetable.moonPhases.enable, birthdays.enable off Badges
timetable.googleColors.enable / refreshHours off / 6 Event colours from Google
timetable.notifications.* on, -15m enable, offsets, dailySummary, dailySummaryTime, notifyAllDay, sound

View, density, slot height and the collapsed rail groups are remembered in Persistent.states.cheatsheet (timetableView, timetableMonthDensity, timetableSlotHeight, timetableCollapsedUpcomingGroups).

Where the calendar shows up

  • Desktop widgets — Date Card, Calendar Minimal, Month Grid, Agenda, Next Event (upcoming events only), Pill and Upcoming 3 Days, and At a Glance. See Desktop Widgets.
  • Quick toggles and the Dashboard — the Calendar page in the Sidebar Dashboard.
  • Launcher — the optional Calendar module (off by default; khal, Google or both).
  • Dock — a calendar utility widget (Dock).
  • Clock app — From your timetable alarm suggestions (Clock App).
  • Modes & Routines — a calendar condition (an event is on or about to start).
  • AI — reviewed calendar mutations and reads (AI Integration).

Under the hood

Piece Role
services/CalendarService.qml The singleton: khal list --json over a window that grows as you navigate (3 months of slack, never shrinks), the per-day index eventsByDay, optimistic events, the serialized request queue, sync status
scripts/calendar/ics.py The UID-addressed mutation bridge. JSON Lines on stdin; read, save, deleteSeries, deleteOccurrence, overrideOccurrence, truncateSeries, splitSeries, expand, importIcs, calendar list and colour. Writes via khal import for new events and in place (temp file + os.replace) for existing ones
scripts/calendar/vdirsyncer_sync.py, vdirsyncer_reauth.py Targeted sync and one-click Google re-authorization
scripts/calendar/subscriptions.py Managed vdirsyncer / khal blocks
services/CalendarSubscriptions.qml, CalendarIcsFileImport.qml, GmailCalendarImport.qml, OutlookCalendarImport.qml, OutlookIcsImport.qml, OutlookService.qml Sources
services/GoogleCalendarService.qml, scripts/google_calendar/api.py Event colours
services/CalendarNotifier.qml Notifications
services/Holidays.qml, BirthdaysService.qml, SportsService.qml Holidays, birthdays, ESPN
modules/ii/cheatsheet/CheatsheetTimetable.qml, timetable/ The tab

A few design rules worth knowing if you hack on it: no event field is ever interpolated into a shell command (everything is JSON on stdin); week and month views consume exactly the CalendarService.eventsByDay DTO (startDate, endDate, UID and metadata in one object); ESPN games, birthdays and the Outlook mirror are never written into your personal calendar.


Related pages

← Back to Home

Clone this wiki locally