Skip to content

v0.3.4

Choose a tag to compare

@karatektus karatektus released this 20 Oct 06:49
· 124 commits to main since this release

Image: ghcr.io/karatektus/pl_mail:0.3.4

Fixed Gmail imports stuck on Google's quota. Google lowered the per-user limit to 6,000 units a minute for projects created since May 2026 and raised the cost of fetching a message with its body from 5 units to 60 (its table says 20, which is the cost without the body); plMail paced imports for 15,000 at the old cost, so on a new project most of what an import asked for was refused, the log filled with rate-limit warnings and an import could stop without resuming. plMail now reads the limit out of Google's first refusal and keeps to it — nothing to configure — counts a message at its real cost, fetches five to a request so a minute's worth is never asked for in one second, and shares one allowance between its workers. An account Google still refuses at the right limit is slowed down and speeds up again by itself. An import longer than an hour no longer fetches again what an earlier pass already brought in. A refusal is no longer an error: whatever met it — a batch, an import page, a sync, a label change being pushed — waits out the minute and carries on. The import line in the background-work indicator now shows the quota, roughly how much of it is in use, how many messages a minute are coming in, how long the rest of the import takes, and says so when Google has paused it. No migration. #90, reported by @karamanliev. Your own tabs on an account's page. Clicking an account in the sidebar offered the five built-in tabs and none of the ones you made. Mail filed in one of yours is in that tab only — not in Primary or any other — so on an account's page it was in no tab at all. The page now has the same tabs as the top-level Inbox, yours included, each shown while it holds mail in that account, and ?tab= accepts yours. Counts are per account; a link to a tab that is not yours, or was deleted, opens Primary as it does in the Inbox. No migration.

Fixed the browser tab icon in Firefox. Settings → Appearance → Logo changed the icon in the app but not in the tab, which kept showing the wax seal. Every page also linked a fixed 192px wax-seal PNG as a second tab icon, and Firefox prefers that to the SVG for a tab. The PNG is gone from the tab icons; the home-screen and install icons are unchanged. No migration. #74, by @Flouss.

Fixed messages with invalid bytes not being stored. Over IMAP, a mail whose text was labelled us-ascii but written in Latin-1 — "Grüße" from a mailer nobody configured — was refused by the database ("invalid byte sequence for encoding UTF8: 0xfc"), asked for again on each of the next syncs and then given up on, so it never appeared. The same happened with such a byte in a Message-ID, In-Reply-To or References header, and in an attachment's content type or content ID. All of them are now read the way a subject or a file name with that byte already was, and the message arrives with its umlauts. A message that was given up on before this is not fetched again by itself. No migration. #81

Clearer read/unread button. On an unread row, "Mark as read" wore the closed envelope, while the same action above a selection wears the open one — which looked like a Promotions problem only because that is where mail is unread. A row's button now matches the toolbar: open envelope to mark read, closed envelope to mark unread. No migration. #61, by @Flouss.

New Gmail and Microsoft accounts get their own colour. Accounts added with a password were already given the first colour no other account was wearing; accounts connected through Google or Microsoft skipped that step and all kept the first colour, so their dots in the sidebar and the corner mark on every message row looked the same. They now take the first free colour like any other. Accounts you connected before this keep the colour they have; the swatch on the account's row in Settings → Mail accounts changes it. No migration. #60, by @Flouss.

Readable mail list beside an open message. A row went to a single line once the list was about 575px wide, which left the subject around 120px and its preview wrapped next to it ("Facture pour Co…") whenever the reading pane was open, or on a large phone. A row now goes to one line only once the list is 75rem (about 1200px) wide. Below that it is two lines — sender and subject, then the preview across the whole row — and on a list narrower than about 575px, a phone, the three it always was. Not less, because a one-line row in a 65rem list (the list beside the reading pane on a 2560px screen) still cut real subjects after about 30 characters. Archive, delete, snooze and the other row actions appear on hover in two- and three-line rows too, instead of only in one-line rows. A two-line row has no unread dot; like a one-line row it says unread with a bold sender and subject. Also fixed: with the two-line preview setting on, a one-line row wrapped its preview anyway. No setting; no migration. #62, by @Flouss. The calendar button can skip a view. The button in the top bar has always been a three-way switch: mail, then the calendar beside the mail, then the calendar full width. Settings → Calendar → Behaviour has one choice, Calendar button, that can make it a two-way one. Full width only goes between the mail and the calendar full width, one press each way, and clicking a mail link while the calendar is full width goes back to the mail instead of dropping into a split. Split only goes between the mail and the calendar beside it. All three is the default, so the button behaves as before until you change it. Nothing is added to the database. #80, by @Flouss.

Unread counts on inbox tabs. The number is the sidebar's Inbox badge split by category, so the tabs add up to it, and it appears on every tab, the one you are on included, and updates in place on sync. Before, an unread conversation in Promotions made the Inbox badge read 1 while Primary, the tab that opens, showed nothing unread, and the only hint on the strip was a colour on the Promotions icon. The "3 new" pill is unchanged: it says what arrived that you have not been shown, the unread count says what is unread. On for everyone; Settings → Appearance → Message list switches it off per person. One column is added to the user table; the migration is reversible. #54, by @Flouss.

Change an account's colour. The coloured dot beside each account, and the mark on its messages in a mixed list, was picked for you when the account was added and could never be changed. Settings → Mail accounts now has a colour swatch on every row — Gmail and Outlook accounts included, which the Edit form is closed to — that opens the palette of eight. Two accounts may share a colour if you want them to read as a pair. No migration. #69, by @Flouss.

An open message stays open when the list changes. With the reading pane on the right, choosing another folder, switching a category tab, turning the page, sorting or searching closed the message you were reading, although it had never covered the list. It stays now, and the back arrow closes it and leaves you on the folder you moved to. The open row is marked again when you return to the folder it is in. With one pane at a time nothing changes: there the message has covered the list, so choosing a folder still closes it. No migration. #63, by @Flouss.

Sidebar "+" buttons appear on hover. Both were on screen all the time. They also appear when reached with the keyboard, and stay visible on a touch screen, where there is no pointer to reveal them with. #66, by @Flouss.

Separate model host for semantic search. Admin → AI has a tick box, Search uses a different model host, with its own provider, address, token and extra headers. Off by default. It is for running the two models on different machines: a hosted model for writing and a small one at home for search, so the mailbox is indexed on a machine you own and nothing is billed per message, or the reverse.

  • The index is kept when the search model has the same name on the new host. A vector belongs to its model, not to the address it was made at.
  • Each host gets only its own token and headers. A search host without a token is sent none.
  • Test asks both hosts, each for the model that lives on it.

Five columns are added to ai_settings, unused until the box is ticked; the migration is reversible.

Custom headers for the model host. For a reverse proxy with basic auth, a zero-trust gateway, a routing gateway with its own key, or a provider that takes a project or tenant header. Admin → AI has an Extra headers box under the token: one Name: value per line, sent with every request for either provider, stored encrypted. The page lists the names on file and never shows a value again. They are dropped when the address or the provider changes, like the token. One column is added to ai_settings; the migration is reversible.

Any OpenAI-compatible language model. Admin → AI has a Provider field: Ollama, as before, or OpenAI-compatible, which covers llama.cpp, vLLM, LM Studio, LiteLLM, hosted providers and Ollama under /v1. Writing help, summaries, sorting and search by meaning all work with either. The address is the API root including its version, such as https://api.example.com/v1, and the token is the API key.

  • Ollama is still the default and nothing changes for an existing installation.
  • Not for the other provider: the keep-alive fields, Warm up now and the "in memory" half of the model host card. The OpenAI format cannot ask for any of them, so they are put away while it is chosen.
  • A hosted provider receives your mail. Sorting sends every arriving message no rule recognised, and search sends every message once to index it. plMail sends only to the address you enter.
  • Changing the provider re-uses the index when the search model has the same name. A different model still needs a new pass from Admin → AI, as it always has.

One column is added to ai_settings; the migration is reversible. Built on #58 by @shady2k.

Fixed: the Admin → AI token is sent to the model host. It was stored and never used, so an Ollama behind a reverse proxy that asks for one answered 401 to every request while the page said a token was on file. It goes as a bearer token with every request, including the Test button and the panel on Admin → Performance. The token belongs to the address it was typed for: changing the model host without typing a token drops the stored one. No migration.

Admin → AI shows the model host's errors. The message kept the percent signs of its own placeholder, so a refusal read "HTTP %403%" and the host's reason sat between two stray percent signs.

Permanent delete is off until you turn it on. Settings → Mail → Disposal has one switch, Delete-now buttons. It decides whether the Delete forever buttons exist at all: the flame on a row in Spam and the bin, which was always there, and now the same button above a ticked selection. This changes things for anyone who used the row button: it is gone until the switch is on. With it on, a selection in Spam or the bin is deleted for good from the toolbar, instead of a trip to the bin and a second deletion there. It asks first, says how many, and removes the mail from the mail server too; there is no Undo. Select all N followed by Delete forever empties the whole of Spam or the bin in the background. # does nothing above a selection there, since the Delete it presses is replaced. The server refuses to delete for good while the switch is off, whatever a page sends. It is per person and a config backup carries it. No migration. #71, by @Flouss.

Fixed the count after deleting a selection in the bin. Ticking mail in the bin and pressing Delete took the rows off the list, and they were back on the next load: the mail was already in the bin, so there was nothing to trash, and the list reported a change anyway. The bin has no Delete above a selection now. With the Disposal switch on it has Delete forever in its place, with the same question and the same count; with it off, Move to inbox is the way out. # does nothing there. No migration.