Commit Graph
512 Commits
Author SHA1 Message Date
Linus Rath 7c21b0d0f9 Merge branch 'main' of https://github.com/bulwarkmail/webmail 2026-10-11 18:59:34 +02:00
Linus Rath 32f74e2083 fix: keep the quoted message when tabbing into a reply or forward 2026-10-11 18:58:55 +02:00
Linus Rath 9dc0f9c892 fix: label the composer toolbar buttons and group send options in a menu 2026-10-11 18:58:26 +02:00
Linus Rath 3efa370e42 fix: split typed addresses on commas and stop sends to invalid addresses 2026-10-11 18:55:46 +02:00
Linus Rath ab97ed2441 fix: add the highlighted suggestion on Enter instead of the typed text 2026-10-11 18:53:15 +02:00
Linus Rath fcc6c15227 fix: close the recipient suggestions once the typed address is added 2026-10-11 18:49:03 +02:00
Linus Rath 7401ea6658 fix: recolour mails for dark mode instead of inverting them
Dark mode used to invert the whole message body with a CSS filter and
re-invert images and coloured cells. Brand colours came out shifted
(red turned pink), white cells set through bgcolor stayed white, and
the result changed after loading remote content because the blocker
strips the background attributes the selectors relied on.

The rendered body is now read back through computed styles and its
colours are rewritten in place: light backgrounds are darkened in their
own hue, vivid and dark fills stay as sent, text and borders only change
when they lack contrast, and anything painted on a background image is
left alone. Transparent dark images get their original light background
behind them when their pixels show they would vanish. The thread
conversation view uses the same pass.

Also fixed along the way:
- blocking remote content dropped every leading `<style>` block, so
  mails lost their CSS until images were loaded
- a mail's `prefers-color-scheme` rules followed the OS instead of the
  app theme
2026-10-11 18:43:04 +02:00
Linus Rath 89d8bcc453 fix: hide the paperclip in the list when a message only has inline images 2026-10-11 17:57:04 +02:00
Linus Rath face095a83 fix: align the contact sidebar header with the toolbar and use app buttons for its actions 2026-10-11 17:53:43 +02:00
Linus Rath 50771cd082 fix: close the composer after a plugin send and keep the draft when it fails #1207 2026-10-11 17:44:26 +02:00
Linus Rath 7cbd33dbc8 fix: act on the ticked account's mail in batch actions #1061
Batch delete grouped the selection by bare email id, so with the same
id in two accounts, permanent-deleting B's row destroyed A's message
and left B's alone. Copy had the same lookup and went out through the
wrong account.

- resolve the selection to email objects by key, including ticked
  members of an expanded thread that are not list rows
- delete, move, mark read, tag and copy group by those objects
- rows, thread checkboxes, drag and drop compare by key, so ticked
  rows show as checked in unified and tag views
- the Mountain View toolbar uses the same resolver
2026-10-10 21:51:19 +02:00
Linus Rath e567df3ab3 Merge pull request #1061 from lucamzanon/fix/cross-account-selection-key
fix: scope batch selection to the owning account
2026-10-10 19:29:02 +02:00
Linus Rath 53c1bd29a1 fix: show a toast when a Mountain View toolbar action fails #1168
The toolbar's run() had only try/finally, so a failed archive or tag
write rejected silently. Delete, move and archive now go through
runBatchEmailAction with the same messages as the default list, and
tag, read and spam actions show their success and error toasts.

The top bar also starts update polling. It replaces the rail, which
used to start it, so the update dot on the admin icon never showed.
2026-10-10 19:24:10 +02:00
Linus Rath 4af42858f0 Merge pull request #1168 from lucamzanon/feat/mountain-view-layout
feat: add Mountain View, an optional alternative layout
2026-10-10 19:23:59 +02:00
lucamzanon e8da96a625 fix(compose): keep body text from showing through the pinned toolbar
The formatting toolbar sticks to the top of the composer while the body
scrolls. Its background was bg-muted/30 with a backdrop blur, and in a
browser that skips backdrop-filter the body text showed sharp under the
buttons. The toolbar now uses the colour bg-muted/30 gives over the
page, made opaque: it looks the same at rest and hides the text when
pinned.
2026-10-09 09:05:07 +02:00
lucamzanon 04cc00f895 fix(mountain-view): plain sheet under the conversation, round avatar button
Under the message the reply buttons sat on the tinted strip the default
layout uses for its quick reply, a different colour from the message
above them in the light theme. Mountain View now reads the whole
conversation on the page colour.

The account avatar in the top bar got a square highlight while every
other button there is round.
2026-10-09 08:35:17 +02:00
lucamzanon 7c35ed360f fix(mountain-view): labels, selection and controls that did not match
- The refresh button was labelled "Loading" and the overflow menu
  "Mark folder as read"; they now say Refresh and More actions.
- The Starred row stayed highlighted while a tag was open, and its icon
  ignored the colourful-icons setting.
- Reply, Reply all and Forward use the standard small button size.
- With a single login the account popover offers one Sign out pill
  instead of a pill-less footer row.
- The compose backdrop matches the other dialogs.
- Layout settings hide the mail layout and toolbar label options, which
  do nothing under Mountain View; their values still apply to the
  default layout.
2026-10-09 08:27:01 +02:00
lucamzanon 96cdeb1d66 Merge remote-tracking branch 'origin/main' into up/fix/cross-account-selection-key
# Conflicts:
#	components/email/email-list.tsx
#	stores/email-store.ts
2026-10-08 10:11:20 +02:00
lucamzanon ade30622f2 Merge remote-tracking branch 'origin/main' into up/feat/mountain-view-layout
# Conflicts:
#	components/mail/mail-app.tsx
2026-10-08 10:05:29 +02:00
Linus Rath f87be32388 fix: keep an empty draft subject empty so send still warns #1189 2026-10-07 20:22:51 +02:00
Linus Rath 1b9aeff4e5 Merge pull request #1188 from VasilisNtovolos/fix/shared-account-blob-downloads
fix: route shared-account blob downloads by message owner
2026-10-07 19:33:24 +02:00
Linus Rath 8889346371 feat: warn when a message's sender can't be verified 2026-10-07 19:04:15 +02:00
VasilisNtovolos bb3ef6d9fc fix: route shared-account blob downloads by message owner 2026-10-07 15:57:45 +03:00
Linus Rath 92f1973bcc feat: attach files from the Files app in the composer #1179 2026-10-06 15:18:58 +02:00
lucamzanon d632956231 feat: add Mountain View, an optional alternative layout
Add an "Interface layout" setting at the top of Settings > Layout with two
options, Default and Mountain View. Default is the existing layout and stays
the default, so nothing changes unless a user opts in.

Mountain View rearranges the chrome while keeping the theme's colours, icons
and type:

- desktop: a top bar with a wide search field (filters drop from the field),
  an apps grid and the account avatar instead of the left app rail; flat
  navigation with Starred under Inbox and the rest behind "More"; a Compose
  pill; one list toolbar with select-all, refresh, overflow menu, a 1-N of
  total range and the batch actions; rows led by a checkbox and a star; an
  icon-only message toolbar with Reply / Reply all / Forward under the
  message; compose in a docked window; an account popover with per-account
  unread counts; j/k cursor, x to select and g-sequences
- phone: rounded search header, compact rows with the star on the preview
  line, and Reply / Reply all / Forward at the end of a message instead of
  the bottom action bar

It is one setting (`interfaceLayout`), written to a data attribute on <html>
that keys a block of CSS tokens in globals.css, plus a few components gated
on the flag. The mail layout in force is derived (effectiveMailLayout), so
the user's saved mail layout is never rewritten. The setting is listed in
the admin policy tab, and the new strings are translated in all locales.

Includes unit tests and a Playwright smoke test against a running instance
(scripts/mountain-view-smoke/).
2026-10-05 10:36:20 +02:00
Linus Rath b51de3baaa feat: add or remove a label on all selected mails at once #1077 2026-10-04 20:54:32 +02:00
Linus Rath c317cd9679 fix: offer the shared account's folders first when moving its mail #1149 2026-10-02 23:04:28 +02:00
Linus Rath 9ee463084d Merge pull request #1150 from lucamzanon/feat/cross-account-copy
feat: copy messages to a folder of another connected account
2026-10-02 10:33:15 +02:00
Linus Rath 07625bc513 fix: show the translated check-your-Sent message for an unconfirmed send #1019
A send answered without an EmailSubmission threw a fixed English
message, which the composer showed as is in every language. It now
throws `SendUnconfirmedError`, and the composer shows the existing
`send_timeout` text, the same advice as for a send whose answer was
lost: check Sent before sending again.
2026-10-02 10:33:10 +02:00
lucamzanon f02dbf3a40 feat: copy messages to a folder of another connected account
With several accounts connected, mail could only be moved between
them (drag to another account's folder). There was no way to file a
copy in another account and keep the original where it is.

crossAccountMoveEmails and copyEmailAcrossAccounts take an optional
`{ keepOriginal: true }`. With it the source message is not destroyed,
and the store leaves the list, the open message and the selection as
they are; only the destination's folders are refreshed. Without it
nothing changes.

The message menu gets "Copy to account..." when more than one account
is connected. It lists the writable folders of the other accounts
(their folder lists are fetched when the menu opens) and copies the
selection, or the message the menu was opened on, through the new
copyEmailsToAccount action. That groups the messages by the login and
JMAP account they live in and refuses a copy into the account a
message is already in.
2026-10-01 12:53:14 +02:00
lucamzanon 81d47d74bf Merge remote-tracking branch 'origin/main' into up/fix/cross-account-selection-key
# Conflicts:
#	components/email/email-list.tsx
2026-10-01 11:58:16 +02:00
Linus Rath 26cbca73cb feat: let @ recipient mentions be turned off and announce them to screen readers #1134
Mentions change what Enter and Tab do right after `@word` and open a
list for everyone, so they get a setting next to the empty subject
warning (`recipientMentionsEnabled`, on by default, translated in
every locale). Turned off, the composer offers no candidates and the
editor keeps its keys.

While the list is open, the editor now carries `aria-autocomplete`,
`aria-controls` and `aria-activedescendant`, as the To/Cc
autocomplete does, so screen readers hear the list and the highlighted
name. Each row now leads with the `@name` that will be inserted,
followed by the full name and the address: for a surname-first name
such as "Nagy János" the inserted word is not what the name alone
suggests.
2026-10-01 11:10:04 +02:00
Linus Rath 90c8195a00 Merge pull request #1134 from dealerweb/feat/recipient-mentions
feat: mention a recipient by first name with @ in the message body
2026-10-01 11:06:00 +02:00
Linus Rath c0db46de87 fix: keep a restored forward's parts copyable and stop a send whose copy fails #1142
Three gaps in copying forwarded parts to the sending account:
- A composer stashed before its first autosave (pro tab move, unmount)
  lost the parts' owning account, because the snapshot and the restore
  dropped `sourceAccountId`. The restored forward then failed every
  save and send with blobNotFound.
- A failed copy was swallowed and the send went ahead, failing with the
  server's raw blobNotFound. It now stops with the existing "an
  attachment failed to upload" message and leaves the composer as is.
- Copies were recorded only after the whole loop, so a failure on a
  later part re-copied the earlier ones on every retry. Each part is
  now recorded as soon as it is copied.

Quoted inline images are only read at send time, so they are no longer
copied on every draft save, and at send only those the body still
shows are copied.
2026-10-01 11:05:54 +02:00
Linus Rath 182a9533bf Merge pull request #1142 from lucamzanon/fix/forward-parts-from-other-account
fix: copy forwarded parts to the sending account before saving or sending
2026-10-01 11:02:44 +02:00
Linus Rath 6bce3323a9 fix: keep previews that only start like a style sheet #1136
The leading-CSS strip emptied or cut ordinary previews:
"@media team, the brief is attached", "@Page can you check?" and
"@import the CSV ..." became empty, and "Reminder: your appointment
{date: 2026-10-01} is confirmed" became "is confirmed".

At-rule names now match in lower case only, only `@import` and
`@charset` end at a semicolon, an at-rule word with no block after it
is prose, and a selector colon must not be followed by a space.

The conversation view's collapsed cards, contact activity, the pro
folder tab and the list's row-height estimate now use `cleanPreview`
too, so they agree with the list rows. The test's invisible characters
are written as escapes.
2026-10-01 11:02:26 +02:00
Linus Rath 1a186f8962 Merge pull request #1136 from lucamzanon/fix/preview-leading-css
fix: skip a style sheet at the start of a message preview
2026-10-01 11:01:28 +02:00
lucamzanon cd9536dcf1 fix: copy forwarded parts to the sending account before saving or sending
A forward carries the original message's attachments, and the quoted
body's inline images, as part blobIds of the account that holds it
(replyTo.accountId, #1104). When the forward goes out through another
account - a From picked from another account's identities, or the
holding account has none of its own - the draft was saved and sent
through that other account, which cannot resolve those blobs: Email/set
failed with blobNotFound and the forward could not be sent.

Parts of the original are now tagged with the holding account. Before a
draft save or a send, any part owned by another account is downloaded
through that account's client and uploaded through the composing one,
once; later saves reuse the copy. Blob/copy cannot do it, as every login
is its own JMAP session. Quoted inline images are loaded through the
holding account's client as well. Forwards that go out from the holding
account keep referencing the original blobs exactly as before.
2026-10-01 04:43:49 +02:00
dealerweb 17a42c42fc fix: open another folder at the top of the message list
The list's scroll container outlives a switch of folder, so the next
folder opened wherever the last one was scrolled to - often halfway down,
or past its end. Opening another folder, tag, account, unified view or
plugin tab, or running another search, now starts the list at the top.
Refreshes, new mail and loading more keep the view and the position.
2026-09-30 23:13:27 +02:00
dealerweb 2b5110e0e4 feat: mention a recipient by first name with @ in the message body
Typing @ at the start of a word in the rich-text body offers the To and
Cc recipients (contact groups expanded) and inserts the chosen one's
first name as plain text. The name comes from the display name; with a
firstname.lastname@ address, the address tells which word of the name is
the first name, and without a name the address supplies it. A bare
initial keeps the mailbox name (@m.mustermann), and recipients who share
a first name get their full name. Bcc recipients are never offered -
naming one in the text would disclose them to everyone else.

The list only opens while a recipient matches, so Enter and Escape
otherwise keep their usual meaning. While it is open, Escape closes the
list rather than the composer, which handles Escape in the capture phase,
and the list closes when the editor loses focus.

Built on @tiptap/suggestion, at the version of the other TipTap packages.
2026-09-30 22:43:59 +02:00
lucamzanon 2d521d19b2 fix: skip a style sheet at the start of a message preview
Some servers build a message's preview from its HTML body without skipping
the <style> element, so a marketing mail's preview - in the list and in its
push notification - can read "@media screen and (min-width:600px){.hide{
display:none!important;…" and never reach the text.

Drop whole leading rules from the preview before showing it; a preview that
ends inside the style sheet was nothing but CSS and becomes empty, so the
usual "no preview" text shows instead. A rule only counts as CSS when its
block holds declarations or further rules, so prose with a brace in it
("the meeting is at {time} tomorrow") is left alone.
2026-09-30 21:35:20 +02:00
Linus Rath ac09146054 feat: turn pasted dash and numbered lines into lists in the composer 2026-09-30 20:00:01 +02:00
Linus Rath c081a51842 fix: keep blank lines when pasting plain text into the composer 2026-09-30 19:46:05 +02:00
lucamzanon 412349791c Merge remote-tracking branch 'origin/main' into fix/cross-account-selection-key
Resolve against the explicit batch targets upstream added for filter rules:
a targeted call names its messages and their account itself, the user's
selection stays keyed by owning account. Also key the context menu's
multi-select check and the rule targets, which compared a selection key
with a bare email id.
2026-09-30 18:49:47 +02:00
Linus Rath 1531dae7eb feat: create filter rules from the message right-click menu 2026-09-30 13:52:41 +02:00
lucamzanon 4ee234b7c3 fix: resolve selection keys to ids before the batch spam actions
batchMarkAsSpam and batchUndoSpam take the bare ids of the viewed account,
but were handed the selection keys. Those match only while no row carries
a source account; mail of a shared account was silently left out.
2026-09-30 09:02:58 +02:00
lucamzanon 9da6a26404 Merge remote-tracking branch 'origin/main' into fix/cross-account-selection-key
# Conflicts:
#	components/mail/mail-app.tsx
#	stores/email-store.ts
2026-09-30 09:02:58 +02:00
Linus Rath 62e07608fd fix: stop the mail list hiding dividers between some rows 2026-09-30 00:21:21 +02:00
Linus Rath 9d140fa762 Merge pull request #1052 from lucamzanon/feat/account-row-colours
feat: colour unified rows by account and give every tag a hue of its own
2026-09-29 23:30:24 +02:00
Linus Rath affa999cd0 fix: report recipients the server rejects at send #1123 2026-09-29 20:49:40 +02:00