Up to three dated insights as a band over the list — a parcel out for
delivery, a flight tomorrow — each with what it is, when it is, and the
one button worth having. The facts were already being read; until now
the only place they surfaced was a panel somebody had to go and open.
InsightPane the whole visibility rule in one place, because
three readers need it: the fragment, the dismiss
endpoint and the tests. Empty means the strip does
not exist, whichever of the three reasons applies.
InsightPaneCtrl the lazy fragment and the dismiss. Re-stamping the
dismissal is deliberate and the opposite of
InsightActionsController::dismiss — that records a
fact about one card, this records a position on a
moving list.
InsightNotifier insights.changed on mail/user/{id}, published after
the flush and once per user per batch, so a tab that
comes to read finds rows that are actually there.
_pane.html.twig renders NOTHING when there is nothing — no heading,
no empty box, no height.
Dismissing the band means "not now": it returns when an insight it has
never shown is extracted, which is decided against createdAt and not
updatedAt. A carrier revising an estimate rewrites a row the user
already waved away, and reading that as news would bring the strip back
for the whole of a parcel's journey.
HappeningSoonController rejected a panel in the mailbox, in writing, for
two costs: width taken from panes that have none to give, and a query on
every mailbox load for a list that is usually empty. Both are answered
rather than ignored — the strip takes height, arrives in a lazy frame,
and renders nothing when empty — and that controller's doc now says so
instead of contradicting what ships beside it.
One line of this is not in either worker's scope and is the thing that
would have shipped silently broken: core--mercure drops any update type
it has no branch for, so insights.changed arrived at the hub, was
delivered, and reached nobody. The branch is there now, and the method
says out loud that a missing one fails quietly.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
35 KiB
Everything that happens between a message arriving and you being done with it: reading it, finding it later, putting it away, and writing back.
The sidebar, and what the lists mean
The left-hand rail is organised around labels rather than folders, because one label spans every account you have connected. Clicking Inbox shows the inbox of all of them at once; clicking a label you made shows everything carrying it, wherever it arrived.
| Entry | What it lists |
|---|---|
| Inbox | Everything still in the inbox, across every account, newest conversation first |
| Starred | Conversations with at least one starred message |
| Snoozed | Conversations waiting for their wake time |
| Sent, Drafts, Trash | The corresponding system label, across accounts |
| Archive | Hidden by default — switch the Archive label visible in Settings → Labels to get it |
| Accounts | One row per account; clicking it opens that account's inbox |
Clicking an account opens its inbox — the same question the top-level Inbox asks, about one mailbox instead of all of them. Its Sent, Drafts, Spam and Trash are folder rows underneath it, one click further; the account row is not an archive of everything the account has ever held.
Under Accounts, expanding an account shows the labels that actually exist on it. That list is narrower than the sidebar's own label list on purpose: the sidebar means "across every account", and the per-account list answers "what does this mailbox actually have". Which account you left expanded is remembered on the server rather than in the browser, so the sidebar renders already expanded instead of blinking open after the page has drawn.
The Labels and Accounts headings fold away. Collapsing one is remembered against your account rather than the browser, so it follows you to your phone, and it is rendered already folded rather than opening and snapping shut a moment after the page draws. What folds is the whole section — a wall of labels is what pushes the accounts off the bottom of a short window — though a nested label tree still collapses on its own for anyone who nests them.
An unread badge is a link. Clicking one opens that same view with everything already read filtered out — the inbox, Starred, Archive, Spam, Snoozed, or a label, under one account or across all of them — so the number on the pill is the number of rows you land on. That is the point of it being a count of conversations rather than of unread messages: a conversation holding three unread replies is one row, and a badge you click has to say how many rows it will give you.
While a list is narrowed it says Unread only, with Show all beside it, because a filtered list and a genuinely quiet one look identical otherwise. Everything else about the view survives the filter — the account it was narrowed to, the sort order, the tab — so Show all puts you back where you were rather than somewhere adjacent.
Trash and Drafts are not links, and neither is the Labels roll-up. The first two carry a total rather than an unread count, so there is no unread question being asked there; the roll-up stands in for several lists at once and has no single one to open.
A collapsed Labels heading carries the unread hidden underneath it, as a count of conversations. It is deliberately not the sum of the per-label numbers: a conversation filed under two labels would be counted twice, and the heading would promise more than expanding it could show. Accounts gets no such number, because there is no honest one to give — those counts are per account, and only the account you have expanded is loaded.
Every list pages at fifty conversations. Newer and Older in the toolbar move between pages.
Tabs
The inbox is split into the five Gmail categories — Primary, Social, Promotions,
Updates and Forums. For a Gmail account plMail trusts Gmail's own CATEGORY_* labels.
For everything else it works the category out from headers that were already stored, which is why
re-categorisation never needs a resync.
Each tab reads the way Gmail's do: an icon — filled in on the tab you are on — and, while a category holds mail you have never been shown, a "3 new" pill in that category's colour with a second line naming who that new mail is from, newest arrival first. That is deliberately the only number on a tab: unread already has the sidebar badge and the bold rows, so the tab keeps the one thing only it can say. The strip is live — pill and sender names update in place when mail arrives, without waiting for a reload, and a hint is retired the moment you look at its tab.
There is one override worth knowing: a sender you have written to yourself is pulled back into
Primary regardless of any bulk-mail header on the message. Open a message's Details panel and
the category row says which rule decided, and which header or domain it matched on.
The "New" marker
A conversation is new until its row has been shown to you. Not opened — shown. Scroll past something in the inbox and never click it and the badge goes, because you have stopped being surprised by it; the conversation is still unread, because you still have not read it. The two are different questions and plMail keeps both answers.
It shows as a filled New pill beside the sender on the row, as a "3 new" count pill on the inbox category tabs — the Gmail hint — and as a quiet dot, no number, on the sidebar's labels and roles and on Starred. Pill and dot both mean "something arrived here", which is what an unread count does not say.
Nothing stays new for longer than 24 hours after its last message, whether or not you ever saw the row. A marker you can only clear by looking at every row is a debt rather than a marker, so it expires on its own.
Search results retire the badge too: a row whose sender and subject you have just read in a result list has been shown, whichever list it was.
The radar
plMail reads certain facts out of mail as it arrives: parcel tracking numbers from shipping confirmations, flights from airline bookings and check-in mail, event tickets, issue and pull-request activity from GitHub notifications, one-time login codes, invoices with an amount and a due date, and subscriptions about to renew or trials about to run out. Each find becomes a small card. Dated ones queue up in the calendar's Happening Soon panel, under On your radar; a conversation that yielded something also shows its cards in a Found in this conversation strip above the messages.
A shop that never names a carrier still yields a parcel. Amazon states an order number and a link into its own tracker and no tracking number anywhere, so that is what the card carries — and a delivery day promised in words rather than digits ("arriving today", "Arriving Monday") is resolved against the mail's own arrival, never against the clock, so re-reading an old mail lands on the day it always did.
The strip above the mail list
Dated insights also appear as a band directly above the mail list — up to three of them, soonest first, each with what it is, when it is, and the one button worth having (Track package, or the thing on GitHub). It is the same set the radar panel holds, said where you already are: the panel answers "what is coming up?" for someone who went looking, the strip tells you a parcel is out for delivery when you did not.
The band updates itself. A sync that finds something new refreshes it in place, with no reload and no page you have to be on.
The ✕ on the band means "not now", not "never". Dismissing it hides the strip until an insight
it has never shown you is extracted — a parcel that ships tomorrow brings it back, while the one
you just waved away stays gone. Dismissing a single card through its ⋮ menu is the permanent
one, and when the last card goes the band goes with it. To be rid of it altogether there is a
switch under Settings → Insights; switching it off hides the band only — the facts are still
read, and still reach the radar panel and the conversation strip.
The band takes height and never width: no pane gives up a pixel for it, it is only fetched after the mail is on screen, and on the ordinary day when there is nothing to say it is not there at all.
Extraction is deterministic and local. It runs on your own server against sender domains, headers
and regular shapes — tracking numbers, flight codes, #123 — with no cloud service and no model:
nothing guesses, and a mail that matches no known shape simply yields nothing. That is a deliberate
trade — the radar would rather miss a parcel than invent one.
Every source has its own switch under Settings → Insights. Switching one off stops new cards from that source; what it already found stays until dismissed. The set of sources is extendable by design — an extractor added to a build lists itself on that settings page and starts running, without anything else changing.
New extractors do not re-read old mail on their own. app:backfill insights walks the mail already
in the database once and hands it to every enabled extractor, the same sweep the other backfill
tasks run.
One-time codes are the one source with an expiry rather than an occurrence. A code's card carries the moment the mail says the code stops working — "valid for 10 minutes", "gültig bis 09:30" — so it drops off the radar when it goes dead instead of sitting there looking usable. When the mail states no lifetime, the card carries no time at all: guessing ten minutes would either retire a code that still works or, worse, keep a dead one looking fresh.
Reporting a mail the radar missed
Because nothing guesses, a mail in a shape nobody has written a parser for yields nothing at all,
and it looks exactly like a mail with nothing in it. Report a missed insight, in a message's
⋮ menu, is how you say which of the two it was. A short dialog asks what plMail should have
spotted — "this is an invoice, due on the 3rd" — and files the report.
Reporting passes the mail on. The report carries a copy of the mail: sender, subject, arrival time and the first stretch of its text, along with your note. That copy lands in an area your administrator can read and download, and the dialog says so before you send it, because a parser can only be written from the shape of a real mail. Report the mail whose shape you want recognised, and not one whose contents you would not hand over.
Reporting the same mail again does not file a second report — it corrects the first, with your note as it stands ready to edit. What the administrator does with the pile is under Admin → Reported mail.
Threads
Replies collapse into one conversation, ordered oldest to newest with the latest message expanded. Each message has its own menu: Star, Archive, Mark as unread, Delete this message, Print, and Show original.
Show original reconstructs the message from the stored header map and the decoded body — no
raw RFC 822 blob is kept — and prints the SPF, DKIM and DMARC verdicts alongside it where the
provider recorded an Authentication-Results header. Both it and Print open in their own tab.
HTML bodies are rendered inline rather than in an iframe. Before that happens the body is put
through a sanitiser once, at ingest: cid: references are rewritten to plMail's own attachment
route, <style> blocks are flattened onto the elements they applied to, and scripts, forms,
iframes and classes are dropped. Links are forced to open away from the app.
Labels
Labels are yours to create, from Create label in the sidebar or Settings → Labels. A label has a name, one of nine colours — gray, red, orange, amber, green, teal, blue, violet or pink — and optionally a parent, which is how nesting works. A label can be hidden from the sidebar without being deleted.
Creating a label does nothing at the provider. Gmail gets it the first time the label is actually applied to a message; plain IMAP never gets a folder created for it, because a folder only matters when a message physically moves. Mirroring structural changes — renames and deletions — outward is off unless you switch it on per account; see Accounts and aliases.
Deleting a label removes it from every account, and takes any nested labels with it. Messages are not deleted; they keep their other labels. You can delete one from the sidebar's own label dialog as well as from Settings → Labels, and the confirmation says plainly that the label goes from every account and how many nested labels are going with it — rather than asking you to agree to something whose reach it has not mentioned.
Search
The search box is at the top of every page and answers at /mail/search. Free text runs as a real
full-text query against Postgres — stemmed and ranked, not a substring scan — and combines with
any operators you type. Hostnames and addresses are indexed by their parts, so wirhub finds a mail
whose body only ever says help.wirhub.de.
| Operator | Takes | Matches |
|---|---|---|
from: |
a name or address | sender address or display name |
to: |
an address | the To list |
cc: |
an address | the Cc list |
subject: |
words | the subject line |
label: |
a label name | a label you made |
has:attachment |
— | also accepts has:attachments |
is:unread, is:read, is:starred |
— | read state and stars |
in: |
a mailbox name | see below |
after: |
a date | received on or after |
before: |
a date | received before |
in: accepts inbox, sent, drafts (or draft), trash (or deleted, or bin), junk (or
spam), archive (or archived) and snoozed. Quoted values are kept together, so
from:"Ada Lovelace" works.
Suggestions appear as you type, and so do actual results — the ten most recent matching conversations, under the operator suggestions, from about the third character. They are a preview rather than the answer: they run only the passes that are fast enough to spend on every keystroke, so a match that exists only as a fragment in the middle of a word in a long body will not be among them. Pressing Enter runs the complete search, which is where those live.
The preview steps aside entirely once your query carries an operator. It cannot honour is:unread
or label:, and ten unfiltered rows under a filtered query look exactly like ten honoured ones,
which would be worse than showing nothing.
Operators are completed from the list above; from:, to: and
cc: complete against your own harvested contacts, because remembering how a sender spells their
name is usually the reason you opened the search box. Enter takes the highlighted suggestion when
the list is open and submits otherwise, Tab takes it, arrow keys move through it, and the first
Escape dismisses the list while a second clears the box. The last eight searches are kept in the
browser and offered when the box is focused and empty.
An operator plMail cannot honour — is:important, in:nowhere, a date that is not a date —
becomes plain text rather than being dropped. That is deliberate: a dropped filter is a filter you
asked for and did not get, and the result would be a page of everything that reads as though the
search had been ignored. As free text it finds little or nothing, which is at least the truth.
A query that is nothing but a half-typed operator — from: with no value — returns an empty page
rather than the whole mailbox.
Results are Most recent first. The switch beside the pagination changes that to Most relevant, which is full-text rank — the best match leads, whenever it arrived. Whichever you pick is remembered for your next search, and paging keeps it. Switching orders starts again at the first page, because page four of one order is page four of nothing in the other.
Search only covers mail that has been synced. plMail syncs everything an account holds and there is no setting that bounds it, so a large mailbox is searchable in full a few sync runs after it is added rather than immediately; see Accounts and aliases.
Acting on mail
Row buttons and the toolbar do the same things, the toolbar over everything selected. Select all has a menu beside it offering All, None, Read, Unread and Starred.
Star, Archive, Delete, Label as, Mark as read / Mark as unread and Snooze all apply to a whole conversation or to a single message, and all of them travel outward to the provider as well as changing what you see here. Archiving is modelled as removing the Inbox label and nothing else — where the message lands afterwards is the provider's business.
Refresh in the toolbar queues a sync for every active account you own and spins until those jobs have drained.
Snooze
Snoozing is archiving with a timer. The conversation leaves the Inbox now — at the provider too, not only in plMail's view of it — gains the Snoozed label, and comes back when its time is up.
The menu offers Later today, Tomorrow, This weekend, Next week, Pick a date and time, and Unsnooze on something already snoozed. Later today is 18:00 and is offered only while that is still ahead; the other three land at 08:00 on the next day, the coming Saturday and the coming Monday. All four are computed in your browser, because the server never sees a timezone for the session and would otherwise resolve "tomorrow morning" to wherever the container thinks it is.
app:mail:wake-snoozed runs every minute and is what brings conversations back. A woken
conversation is marked unread — that is the point of the feature, since a thread returning in
the state you left it in is one you have already learned to scroll past. The read state it had is
genuinely lost.
The Snoozed list is ordered by the conversation's last message, like every other list, rather than by wake time.
Attachments
Attachments show as chips under the message, with a preview for images. Clicking one downloads it; only images are ever served inline, so email-supplied HTML can never run on plMail's own origin.
Save to on an attachment pushes it out to a connected service — see Files and integrations. This works for Gmail and Microsoft messages whose attachment has never touched plMail's disk; it is materialised on first access.
Composing
Compose opens a window docked at the bottom right. Replying from inside a thread opens the editor at the foot of the conversation instead, on a wide screen; below that it falls back to the dock. Either way the window offers rich text, contact autocomplete on the address fields, and a From selector listing every active account and every sending alias on it.
Autocomplete only ever offers your own harvested contacts. On an install with more than one person, nobody's correspondents are visible to anybody else, and an address belonging to another account cannot be put on your draft by any route.
Attach files takes files from your machine, capped at 25 MB per file. Attach from a service opens the file picker for any connected service plMail can download from.
Drafts save themselves two seconds after you stop typing, and a draft is created once there are at least five characters of body or a subject — a subject typed on its own is worth keeping. Closing the window, popping it out, or attaching a file all force a save first. Below that threshold, leaving the page interrupts rather than losing what you typed. The trash button in the compose window genuinely deletes the draft rather than closing the window on top of it.
On a phone the toolbar wraps rather than scrolling out of reach, and the send pill moves up into the window's header, where the old button used to be.
Emoji
The smiley opens the full Unicode set, with categories, search, skin tones and the ones you have used recently. Picking one inserts it where the caret was.
It only ever inserts on a pick. Typing :) or :smile: leaves :) and :smile: in the
message, byte for byte — plMail does not rewrite what you type into pictures.
Search is in the language the window is in, because the emoji data is shipped per locale rather than fetched. Both the data and the colour emoji font are served by plMail itself; nothing is loaded from a CDN at runtime, so the picker works on an install with no outbound internet access at all.
Images in the message
The image button, a paste, and a drag-and-drop onto the editor all do the same thing: the picture goes into the body, where you placed it, rather than beside the message as an attachment. Same 25 MB ceiling as an attachment, and only images — anything else is refused with a reason on the status line.
Inline images travel as cid: parts of the message, which is what every mail client can resolve, so
the recipient sees the picture rather than a broken link back into plMail. They are not attachments
and are not counted as any: a message whose only picture is one you placed in the body carries no
paperclip.
Links
The link button opens a small panel with a URL field and a text field, the text prefilled
from whatever you had selected. A bare example.com is understood and stored as https:// — it is
not a path inside plMail. Only web, mail and telephone addresses can be linked; anything else is
refused there and then rather than being quietly dropped when the draft saves.
Clicking a link already in the editor shows the panel's other face: the address, and Open, Change and Remove. It dismisses on Escape, on a click outside, and as soon as the caret moves off the link.
Signature
Signatures live in Settings → Signatures. Each account has one, and any of its sending addresses can override it. Three states matter, and they are not the same:
| State | What that address signs with |
|---|---|
| Inherits | The account's signature. This is what an address does until you say otherwise. |
| Its own | Whatever you wrote for that address, instead of the account's. |
| Deliberately nothing | Its own signature, left empty. The address signs with nothing even though the account has one. |
The last two are both "an override"; the difference is whether the override is empty. That is why an empty box is not read as "inherit" — a personal alias that signs with nothing on a work mailbox that signs with a block is the whole reason the setting exists.
In the compose window the signature is inserted for you, above the quoted text, on a new message and a reply, with an empty paragraph in front of it so the caret starts in the writing space rather than inside the sign-off. A forward is deliberately unsigned — its content is the mail being passed on, and the caret starts in the To row, since the recipient is the one thing a forward cannot leave without; Insert signature adds the block when you want it. That button replaces the block in place instead of adding a second one, and so does changing the From account: the signature block is swapped and a paragraph you have already typed survives the change.
Writing inside a conversation gets a reduced header: the To field itself, typable, with From, Cc, Bcc and the subject folded away behind the chevron beside it. Replying rarely means retargeting — but forwarding always does, and the recipient is the one thing a forward is missing, so the field is the first thing under the caret rather than a line of text describing it.
A forward opens with the original folded behind the show quoted text pill. If you would rather see it spread out from the start, Settings → General → Composing has the switch — folded stays the default, and either way the quote counts as the message's content: sending a forward without a word of your own above it is not questioned as an empty mail.
Scheduled send
The chevron beside Send opens Send later: Tomorrow morning (08:00), Tomorrow afternoon (13:00), Monday morning, and Pick date & time for anything else. Tomorrow-is-Monday drops the third, because a menu offering one instant under two names is a menu you have to check.
Every time is read on your clock — the timezone and the 12-or-24-hour format from Settings → General, not the browser's — and the menu says which zone it means. A laptop still on another continent's time does not move your morning.
The floor is one minute: below that, "schedule" is "send" with a worse undo, and the picker refuses it in the window without a round trip. The ceiling is 30 days, the same limit JMAP clients are told about, so the web window and a phone cannot disagree about what was allowed.
A scheduled message is a draft that is being held. It says so on its row in Drafts and on the draft's row inside a thread — Scheduled … — and Cancel scheduled send is on that row's menu. The toast has faded long before anybody goes looking, which is the point of putting it there. A cancel is visible on every other device too, not just the one that made it.
More options
More options — the ⋮ beside the toolbar — carries the three things that change what the message is rather than how it looks:
- Priority — No priority, Low, Normal or High. "No priority" is distinct from "Normal": an untouched message says nothing about its urgency and carries no priority headers at all.
- Request read receipt — see below.
- Plain text mode — drops the formatting and sends a plain-text message. It warns first, and only when there is formatting to lose. It is reversible while the window is open; once the draft has been saved as plain text the formatting is gone for good.
Encrypt sits in the same menu, disabled, and says why: there is no encryption yet. It is named rather than hidden because a lock icon that does nothing is the one lie a mail client must not tell.
Read receipts
A read receipt is a message that goes back to the sender saying their mail was displayed. plMail does both directions, and the receiving direction is the one worth reading carefully — it is a privacy setting, not a convenience.
Asking for one
Request read receipt in the compose window's more-options menu. The request names the address you are sending from, alias included, rather than the account: a receipt has to come back to the address that asked, or nothing on the way in can match it to the message.
When one comes back, the message in Sent gains Read at …. The receipt itself is marked read and taken off the Inbox rather than being deleted, so it is there if you want it and not in your way if you do not.
Being asked for one
Settings → Aliases → Compose defaults, per address. Three modes:
| Mode | What your mailbox does |
|---|---|
| Never send | Nothing is sent, and the sender is told nothing. This is the default. |
| Ask each time | The message shows … asked to be told when you read this message, with Send receipt and No thanks. |
| Always send | A receipt goes automatically when you mark the message read. |
Never is the default and stays the default until you change it, deliberately. A receipt confirms that a specific address is live, is monitored, and was reading at a specific minute — which is exactly what somebody fishing for that gets by setting one header. Someone who never opens this setting emits nothing.
Two things narrow it further, both in the same direction:
- A receipt is only ever sent when you mark a message read yourself. A sync discovering that the message was already read in another client sends nothing: a receipt claims a person displayed the message, and a sync pass learning about last Tuesday cannot make that claim.
- A request pointing somewhere other than the sender is downgraded to "ask", however the address is set. If the return address disagrees with who the mail came from, plMail will not answer it automatically — it asks you, and says what the mismatch was. Not silence, because the legitimate version of that shape is a bulk sender collecting at its bounce address.
Undo send
Pressing send queues the message with a ten-second delay and leaves the composer exactly where it is. The Send button becomes the way back: it reads Sending… with click to cancel under it, and a second click on that same button calls the send off. That is the only cancel there is — no toast, no bar at the foot of the thread — and it works the same in the floating composer and in a reply written inside a conversation.
While the send is out the message itself is out of reach: the window is showing a copy of mail that has left, so the fields are frozen rather than editable. When the window expires the composer closes on its own, a Message sent toast confirms it, and the message takes its place in the conversation — unless it was a forward, which starts a conversation of its own and is therefore found in Sent rather than under the mail it was forwarded from.
Cancelling says nothing. The draft coming back with everything in it — recipients, subject, body, attachments — is the confirmation, and it is the same in both composers.
Undo does not race the mail out of the door in the sense that matters: the cancellation and the send job settle it between them in one step, so the database decides who won and only one of them proceeds. Win, and the message goes back to being the draft it was, with the editor reopened where it was.
Lose — press Undo in the last moments, after the job has already claimed the message — and you are told so plainly: Too late — that message had already been sent. You are not handed an editable copy of mail that has gone out. Because the window offered is eight seconds against a ten-second hold, losing is rare rather than routine.
Where to read further
- Mail ingest — the pipeline from provider to database, threading and categorisation.
- Accounts and aliases — connecting mailboxes, instant delivery, sending addresses.
- Account health — when mail stops arriving, and the repair that keeps the mailbox.
- Filters — sorting mail as it arrives, and applying a rule to mail you already have.
- JMAP — the same operations as a client sees them.
Things that bite
The cancel disappears two seconds before the message does. The send job is held for ten seconds, but the composer stops offering click to cancel after eight and closes itself. Nothing is wrong when a message goes out after the window has gone — the offer really did expire.
Closing the composer during those seconds does not stop the send. The Send button is the cancel; the close button is still just a close. Shut the window, or the tab, and the mail goes.
An Undo can lose, and losing is told to you. Click the cancel at the very edge of the window and the answer may be Too late — that message had already been sent. That is the honest outcome rather than a failure: the alternative would be handing back an editable draft of mail that is already on its way, which reads as a cancellation that worked.
Deleting a label from the sidebar is the same delete as the one in settings. It is not a "remove from this view" — the dialog is a shortcut to the same operation, across every account, nested labels included.
A signature image cannot be an inline image. A picture placed in a signature is stored as an
ordinary image reference, not as a cid: part: the sanitiser every signature is written through
drops the marker attribute that would make it one, deliberately, so a signature cannot smuggle in
something that looks like one of the message's own inline pictures. Inline images work in the
message body, where you put them.
"New" is not "unread", and losing the badge is not reading anything. Scrolling past a row in the inbox retires its New badge, because the row was put in front of you. The conversation stays unread until you open it. A thread that is unread and not new is the normal state of everything you have been meaning to get to.
A New badge you never saw expires anyway. Nothing is new for longer than 24 hours after its last message. Coming back from a fortnight away means an inbox with no New badges at all — that is the marker working, not a marker that failed to appear.
Plain-text mode is only reversible until the draft is saved. The warning says so before the switch. Once the draft has been stored as plain text there is no formatting left anywhere to come back to.
A scheduled send cannot be closer than a minute away. Typing the next whole minute is exactly the time a person picks to try the feature, and it is refused — with a reason, in the window, without a round trip. Anything from a minute to thirty days out is accepted.
Waking a snoozed conversation marks it unread, and the old read state is gone. This is chosen, not accidental, but it does mean snoozing a thread you had read leaves you with an unread thread afterwards.
A snooze time in the past is accepted. It means the next sweep, a minute later, brings the conversation straight back. That is a harmless way to say "bring this back now", not an error.
Deleting a label deletes it everywhere. The sidebar's label list is cross-account, so the
delete is too — including nested labels underneath it. A JMAP client's Mailbox/set destroy is the
per-account operation; the web UI has no equivalent.
Search only covers mail that has arrived. plMail syncs everything an account holds, but a large mailbox takes several runs to come across in full, and mail that is not in yet is not searchable however certain you are that it exists. Give it time rather than a setting.
An oversized attachment can fail with no per-file reason. PHP discards the whole request body
when it exceeds post_max_size, so nothing arrives to report an error about. plMail answers
"Upload too large" for the whole upload rather than staying silent, but it cannot tell you which
file caused it.
Marking a message read in another client does not come back. Flag changes travel outward only; incoming IMAP flag sync over the IDLE stream is not implemented.
Reporting a mail hands over a copy of it. Report a missed insight is not a vote or a thumbs-down: it stores the sender, the subject and the beginning of the message text where an administrator can read and download it. That is what makes a new extractor writable, and it is why the dialog says so before you send. On an installation you do not run yourself, report the mail whose shape you want recognised — not one whose contents you would rather keep.
A one-time code with no stated lifetime gets no expiry. The card shows the code and no time, and it stays until you dismiss it. plMail will not invent ten minutes, so the card cannot tell you whether the code still works — the mail it came from is the only thing that can.
