plMail

Calendar

plMail keeps a calendar beside the mail rather than in a separate application, because most of what ends up on a calendar arrives as mail in the first place. This page covers the calendar itself: the four views, the two shapes it takes on screen, and everything the event editor can do. Invitations and events read out of messages are in Invitations and events from mail; reminders are in Reminders; mirroring somebody else's calendar is in Connected calendars.

The calendar

The two shapes

The calendar is one feature rendered two ways, and which one you get depends on where you opened it from rather than on any setting.

Docked beside the mail. The calendar button in the topbar slides a pane in next to the message list. It is a sibling of the mail pane, not an overlay, so the two split the row between them — the handle in the middle moves the boundary and one grows exactly as much as the other shrinks. Drag it, or focus it and use the arrow keys; double-click puts it back. The width is remembered per user between 320 and 900 pixels, defaulting to 380, and it is written into the page server-side so the pane is already the right size on the first paint instead of jumping once the browser catches up.

Below 1024px wide the row cannot hold a sidebar, a readable message list and a readable calendar at once, so the pane takes the whole row and the mail steps aside. It is the same pane and the same toggle — closing it puts the mail back exactly as it was, with no navigation in between. Below 768px there is no pane at all and the calendar is its own page.

Its own page. /calendar replaces the mail view entirely. This is what a phone gets, and what anyone gets who navigates there directly or follows a link.

The four views

Day, Week, Month and Agenda. A view is part of the URL — /calendar/week/2026-08-05 — rather than something the browser remembers, which means every view of every date is bookmarkable, the browser's back button does the obvious thing, and the range behind it stays one indexed query.

View What it covers
Day The one day.
Week Monday to Sunday.
Month Six weeks, starting on the Monday of the week the 1st falls in — so the days spilling in from the neighbouring months are shown, as a month grid always does.
Agenda A rolling list of the next 30 days, skipping the empty time between entries.

The toolbar above the grid holds Previous, Today and Next, the date or range you are looking at, the view switcher, and a New event button. Previous and Next step by a month in month view, by seven days in week view, and by a day everywhere else — agenda included, because it is a rolling list rather than a page.

The full page opens on Week. The docked pane opens on Agenda — it is 380 pixels of a shared row, and a month grid in that width is a lot of empty cells. The pane still offers all four views, as icons rather than words, because the version that dropped the switcher entirely left the pane able to show nothing but its agenda.

In month view a cell draws as many of its day's entries as fit and fades the last one where it runs out, rather than counting them. It used to say N more, and the count could not be right: a cell's height is the window's divided by six and an entry is one line or two depending on whether it has a time, so the number was always a guess about a box nobody had measured. The date is a link — it opens that day, which is where the rest of them are.

Resting the pointer on a day floats that whole day out. The cell becomes a small panel, wide enough for the full titles and long enough for every entry. Nothing in it moves as it opens — it grows right and down from exactly where the list already was — so the entry you were looking at is still under the pointer.

Where a cell is too narrow for words it stops using them. Seven columns of a phone are about forty pixels each, which is not a width a title or even a time can finish in, so below that an entry is drawn as a bar in its calendar's colour — how many, and which calendars, at a glance. Tapping the date opens the day, where they are written out.

The agenda groups its entries under a heading per day, which stays at the top of the list while that day's events are on screen. The start and end time — or All day — sit in a lane of their own down the left, so the titles line up down the list rather than starting wherever the time happened to end.

The time grid

Day and Week are drawn as a time grid: hours down the left, and every event drawn where it actually starts and as long as it actually lasts. Overlapping events are placed in lanes, so three things happening at once are three narrower blocks side by side, and the lane count holds steady across an unbroken run of overlaps rather than changing block by block.

The docked pane draws the same grid the page does. It used to keep an older column list instead, on the reasoning that seven positioned columns need roughly a screen's width — true of 380 pixels, and no longer the right conclusion, because the pane's width is now yours: drag its handle as wide as you like, and past the end of its range it becomes the calendar full-width. A pane that quietly drew a different calendar was the worse answer, since widening it to see the timeline gave you the list anyway.

A narrow week pans rather than squeezing. Seven columns used to divide whatever width there was, so a phone or a narrow pane gave each of them about fifty pixels and a block showed its colour and little else. A column has a floor now: below the width where seven of them fit, the grid slides sideways under a hour gutter that stays put, a day at a time, and four days are legible where none were. Day is still one click away in the toolbar and still a single full-width column — a choice now rather than the only way to read a narrow week.

A block writes its time on one line and its title under it, with the calendar's colour as a bar down the left, so a narrow column shows a whole title where it used to show four characters of one. Where a title still cannot finish — the all-day band above the grid is one line tall by definition — resting the pointer on it widens it to whatever it had to say, over its neighbours, and puts it back when you leave.

Month and Agenda have no time axis in either shape. A month cell is a couple of square centimetres and has no room to say where in the day anything is; an agenda's whole value is that it skips the empty time, which is exactly the axis a grid would draw.

The grid is drawn on your clock. The hours down the side, where the red current-time line sits, and the time a new event is proposed at when you double-click empty space all follow the timezone in Settings → General. That is the same zone every other timestamp in the app is read against. Where a calendar carries a time zone of its own it wins for the events on it, as it always has; what changed is the fallback, which used to be the container's own idea of the time — UTC — so the current-time line could sit an hour or two away from the labels beside it and a new event was proposed at a slot already in the past.

Three things about the grid are worth knowing:

A seven-day week is tight below about 640 pixels — each column ends up around fifty pixels and a block says its colour and little else. The paths a phone actually takes into the calendar avoid it: the pane, which is what opens from the mail, keeps the agenda, and Day view is one tap away in the toolbar and is a single full-width column.

Creating and editing an event

New event in the toolbar, the + on a day heading, or clicking an existing event, all open the same editor. It is a modal over whatever page you were on — the calendar page, or a mail view with the calendar docked beside it — and saving puts you back there rather than dragging you to the calendar.

The fields are Title, Starts, Ends, All day, Calendars, Repeat, Location, Description and Reminders, with Apply to appearing when the event repeats. An event saved with an empty title is called Untitled rather than refused. An end before the start is refused: Those times do not work — the end has to come after the start.

Delete sits beside Save in the same form. On an event plMail read out of a message there is also Not an event, which is a different thing from deleting — see Invitations and events from mail. Download .ics takes the event out as a calendar file.

Repeating events

Repeat offers Does not repeat, Every day, Every week, Every month and Every year. More elaborate rules — every second Tuesday, weekdays only — are not written in plMail, but they are carried faithfully when they arrive from somewhere that can write them: an invitation, an imported .ics, or a connected calendar. Saving such an event from the editor with the dropdown left alone does not flatten it.

A repeating event is stored once, as a rule, and the individual occurrences are drawn from it. They are written out into a bounded window around today — a year back and two years forward — with a hard ceiling of 1000 occurrences per event, so a rule that says "every second, forever" is finite rather than fatal. The expansion happens in the event's own time zone, so a 09:00 Berlin standup is at 09:00 Berlin in November as well as in July.

This occurrence, or all of them

When you open an editor from an occurrence of a repeating event, the editor is opened on that occurrence: the times shown are the ones that occurrence actually has, including any move it has already had. Apply to then offers two answers.

This event changes only the occurrence you opened. Nothing happens to the series or to any other instance — the change is filed against the series as a patch, keyed by where the rule originally put that instance, so editing the same occurrence twice updates the patch rather than stacking a second one beside it.

All events changes the series. This is the one that surprises people, so it is worth stating what it does to the times: because the editor was showing that occurrence's times, the fields are read as the change you made to that occurrence, and the same shift and the same new duration are applied to the series. Renaming a weekly meeting from its fifth occurrence therefore renames the series and leaves it starting where it always started; pushing that fifth occurrence half an hour later and choosing All events moves every occurrence half an hour later. The alternative — reading the fields as the series' new absolute times — would drag the whole series onto the day you happened to be looking at.

Two limits follow from how a per-occurrence edit is stored:

Deleting works the same way. All events removes the series; This event takes one occurrence off it, leaving the series and every other instance untouched.

Dragging and resizing

On the time grid, an event block can be dragged to another time or another day, and resized by its bottom edge. Changes snap to fifteen minutes. Nothing is written while you drag — the preview is drawn in the browser and thrown away, and the change is submitted as an ordinary form when you let go, so the grid you end up looking at is the one the server drew. A request that fails cannot leave a block sitting somewhere the database disagrees with.

The keyboard does the same work: focus a block, then hold Alt and use the arrow keys to move it, Alt+Shift to change how long it lasts, Enter to save and Escape to put it back. Every change is announced, so a screen reader hears where the block went.

A repeating event asks the same this-one-or-all question a drag would otherwise answer silently, in a dialog, before anything is submitted. An event on a calendar that does not accept changes cannot be dragged at all, and says so.

One event on several calendars

The Calendars control in the editor is a checkbox per calendar you own, ticked wherever this meeting already is. The list is every calendar, not only the ones the meeting is on, so it says two different things at once:

That second one is the trap, and the reason is worth understanding. Every copy of a meeting carries the same UID, because a UID identifies the meeting rather than the row: that is what lets an update from the organiser, a re-imported .ics and a provider's own copy all find every copy of the meeting. The calendar merges copies into a single entry only while they agree about the five things you can see on it — start, end, title, all-day, and whether it has been called off. Leave one copy behind at the old time and it no longer agrees, so it splits out and is drawn as its own entry on its own day. That is deliberate: a merged entry that quietly picked a winner would hide a real disagreement behind a tidier screen.

A merged entry shows the colours of every calendar it covers and says On Work, Personal in its tooltip. Clicking it opens one editor, with those calendars already ticked.

A calendar that does not accept changes — a mirror of a published feed, or a shared calendar you can only read — appears in the list, marked This calendar does not accept changes, and cannot be ticked. Unticking everything is refused: Nothing was chosen, so nothing was changed. Tick at least one calendar.

Delete reads the same ticks. The copies on ticked calendars go and the rest stay exactly where they are, read-only ones included. A ticked calendar the meeting was never on has nothing to delete and is simply skipped.

Dragging a merged event has no checkboxes to offer, so it means what the editor means by default: every writable copy moves.

Managing calendars

Settings → Calendars is where calendars are created, renamed, recoloured, given a time zone, hidden from the views, and nominated as the one new events land on. A calendar's time zone is the one it is read in, and the one an event carrying no zone of its own is shown in.

plMail creates two kinds of calendar for you: one default calendar per user, and one per mail account, which is where events found in that account's mail are filed. Neither can be deleted — deleting one only means it is created again, and the events would go in the meantime. Calendars you made yourself, and mirrors of remote ones, can be deleted; deleting takes every event on the calendar with it.

Hiding a calendar hides it everywhere, consistently: the views, the topbar's next-thing indicator, and the Happening soon list all read visible calendars only. The editor is the exception, and deliberately: it lists every calendar you own, hidden ones marked with a struck-through eye beside the read-only lock, because a copy of a meeting on a hidden calendar is still a fact about the meeting. Saving onto one is then an informed choice rather than a trap — and a save that lands somewhere you cannot see says so and names the calendar.

Things that bite

Unticking a calendar in the editor does not remove the event from it. It means "leave that copy alone". The copy stays where it is, stops agreeing with the ones you did change, and appears as a separate entry from then on. To take a meeting off a calendar, tick that calendar and press Delete — the delete acts on exactly the ticked ones.

An event saved to a hidden calendar appears in no view at all. The editor lists hidden calendars; the day, week, month and agenda views do not. So the save succeeds, the row exists, and the meeting is nowhere to be found until the calendar is made visible again in Settings → Calendars. plMail now confirms every save and names the calendar when it is one you cannot see, but the confirmation is the only thing that will tell you.

A copy of an event that came from mail is still an event from mail. Putting a meeting on a second calendar does not cut either copy off from the booking it was read out of: a later message moving the meeting moves both. See Invitations and events from mail for when a change you make yourself does stop that.

"All events" applies your change as a shift, not as an absolute time. Opening the fifth occurrence of a weekly meeting, changing the start to Thursday 14:00 and choosing All events moves the entire series by that difference. If you only meant that one week, choose This event.

A copy created on a second calendar starts as the plain series. It gets no participants — pushing an attendee list to a provider is how the provider decides to re-send the invitation to everyone on it — and none of the per-occurrence moves the original had already accumulated. Those instances stay where they are on the original and are drawn as their own entries until you move them on both copies.

The editor never opens inside the pane. It is rendered at the top of the page instead, because both the pane and the mail pane carry a backdrop filter, which makes them clip anything positioned inside them. This is invisible when it works and completely broken when it does not.

Reminders are not covered by the scope radio. Setting one while This event is selected sets it on the whole series, and there is no way to give one occurrence a reminder of its own.

An occurrence beyond the materialised window does not exist yet. Occurrences are written out a year back and two years forward from when the event was last saved. A repeating event that has not been touched in a long time can therefore run out of drawn occurrences at the far end; saving it again rewrites them from today.


Related: Invitations and events from mail · Reminders · Connected calendars · Sharing and booking

How it works: The calendar model — the JSCalendar object behind an event, how occurrences are materialised, and how overrides are stored.