Commit Graph
491 Commits
Author SHA1 Message Date
dealerwebandLinus Rath 1512b9afc0 Feature: editable layout-preserving quote island
Replying to / forwarding a layout-heavy HTML email (nested tables, MJML,
Outlook divs) destroyed its layout: ProseMirror re-parsed the quoted body
through its strict schema and discarded anything that didn't fit. The quoted
original is now held verbatim in a new atomic QuotedHtml node and never parsed
into the schema; its NodeView renders inside a shadow root so app CSS can't
cascade in and the in-editor view matches the sent mail 1:1.

- quoted-html.ts (new): QuotedHtml atom node + shadow-DOM NodeView (inner
  contentEditable for redaction), serializeEditorContent(), buildQuotedHtmlBlock().
- rich-text-editor: register the node; emit via serializeEditorContent (not
  getHTML) so the verbatim island survives.
- composer: both HTML reply/forward paths embed the original as an island
  (sanitize -> cid-rewrite -> buildQuotedHtmlBlock); the signature-swap effect
  serializes via serializeEditorContent and treats the island as a quote
  boundary so the splice never cuts into the quoted body.

atom:true means Backspace at the boundary / Ctrl+A+Delete removes the whole
quote in one go.
2026-05-30 16:43:50 +02:00
Linus Rath 8d79145dba Merge remote-tracking branch 'origin/main' into pr/quote-header-i18n 2026-05-30 16:15:18 +02:00
dealerwebandLinus Rath bebb394f54 Feature: read receipts (MDN, RFC 8098)
Bulwark had no read-receipt support (JMAP/Stalwart have no native MDN).
End-to-end, client-side, in three parts:

- Request (compose): a toolbar toggle (MailCheck, green when on) sets
  Disposition-Notification-To on the outgoing message via the JMAP
  "header:<name>:asText" create property. Threaded composer -> page ->
  email-store -> client.sendEmail. Default from requestReadReceiptDefault.

- Detect (viewer): reads Disposition-Notification-To case-insensitively from
  the parsed headers and shows a banner (green Send / red Ignore) in the
  unified notification bar. Hidden in Sent/Drafts/Trash/Junk and once handled.
  message/disposition-notification + message/delivery-status report parts are
  filtered out of the attachment list.

- Respond (MDN): lib/mdn.ts builds an RFC 8098 multipart/report (text/plain +
  message/disposition-notification, UTF-8/base64, localized subject + body).
  client.sendReadReceipt uploads the blob, imports it into Sent via
  Email/import, then submits with an explicit envelope. Both Send and Ignore
  set the $MDNSent keyword (RFC 3503) so no client re-prompts. Behaviour
  configurable: ask / always / never.

New: lib/mdn.ts, read-receipt-banner.tsx. Settings (requestReadReceiptDefault,
readReceiptResponse) + UI. All 17 locales.
2026-05-30 15:58:39 +02:00
dealerwebandLinus Rath 2ba0003e16 Feature: localizable sandboxed plugins (manifest locales + api.i18n.t)
The plugin runtime received the active locale (init payload + 'locale-change')
and plugins could declare a `locales` map, but none of it was usable: the
locales never reached the runtime, and buildPluginApi exposed no i18n. So
plugin code calling pluginApi.i18n.t(...) (as the External Link Warning plugin
does) always got undefined and fell back to English.

Thread plugin locales end to end and surface an i18n API:
- ServerPlugin gains `locales`; the upload route persists manifest.locales
  (alongside configSchema/settingsSchema), and /api/plugins surfaces it to the
  client so it flows registry -> client -> sandbox host-bridge -> runtime.
- runtime sets __PLUGIN_LOCALE__ at init (not only on later 'locale-change')
  and buildPluginApi exposes `i18n.locale` + `i18n.t(key, vars)` resolving
  against the plugin's declared locales (manifest.locales) with English/key
  fallback and {placeholder} interpolation.

Lets any sandboxed plugin localize its strings from its manifest.
2026-05-30 15:56:55 +02:00
dealerwebandLinus Rath 4c1d0931a1 Fix: deduplicate localized reply/forward subject prefixes
Replying to a reply produced "Re: Re: foo" (and German used the English
"Re:"/"Fwd:" instead of "AW:"/"WG:"). Four code paths built reply/forward
subjects and only one deduplicated - and only for the English prefix, so
cross-locale threads accumulated chains.

New lib/subject-prefix.ts strips any leading run of reply/forward markers
across ~35 tokens from all supported languages (plus Outlook Re[2]: and
Eudora Re*2: counters), then prepends the locale-appropriate prefix. All four
call sites (composer getInitialSubject, the two page.tsx sites, and the three
pro-tab handlers) now use buildReplySubject/buildForwardSubject. German prefix
corrected to AW:/WG:.
2026-05-30 15:47:53 +02:00
dealerwebandLinus Rath 8353b28b33 Feature: extended filter rules — attachment field + multi-value conditions
Adds an "Attachment" condition field (is present / of type <ext>) backed by
the RFC 5703 Sieve mime extension, matching the filename in both
Content-Disposition and Content-Type headers so real-world senders that only
put the name in Content-Type (Microsoft SMTPSVC, etc.) are caught. Users type
extensions (pdf, doc) not MIME types.

Also makes each text condition accept comma-separated multiple values emitted
as a Sieve string list (OR within the condition), so "(domain1 OR domain2)
AND attachment pdf/xml" is expressible in one rule. value is now string |
string[] (single-value rules stay strings -> backward compatible). New filter
locale keys in all 17 locales.
2026-05-30 15:45:33 +02:00
dealerwebandLinus Rath 229992853b Fix: localize the PWA install prompt
The PWA install prompt was hardcoded English regardless of the selected UI
language (and the large English block tripped Chrome's translate popup on
Android). Add a pwa_install namespace to all 17 locales, switch the component
to useTranslations, and move <PWAInstallPrompt /> from (main)/layout into
(main)/[locale]/layout so it renders inside the IntlProvider. The title keeps
the dynamic {appName}, so per-domain branding still applies.
2026-05-30 15:35:05 +02:00
dealerweb 05e2837f6b Fix: localize reply/forward quote header incl. sender address
The reply/forward quote header was always emitted in English ("On {date},
{from} wrote:", "---------- Forwarded message ----------", From/Date/Subject)
regardless of UI language, in both the main path (lib/quote-header.ts) and the
composer's inline fallback. quote-header.ts now takes an optional localized
QuoteHeaderLabels set (English defaults preserved for back-compat); page.tsx
builds it from a new quote_header message namespace, and the composer fallback
uses the same keys. Added the quote_header namespace to all 17 locales.

Also folds in the forward-sender-address fix: the forward "From:" line now
shows the full "Name <email>" like every mail client (the reply line keeps the
bare name, which reads naturally in "On … wrote:").
2026-05-30 14:09:23 +02:00
Linus Rath 8ae5ecba41 feat: policy-controlled push relay URL with optional user lock 2026-05-28 19:45:14 +02:00
Linus Rath 4ff05bd4ec fix: editable HTML signature in new mail; clean state on every compose entry #329 2026-05-28 19:21:44 +02:00
31e96d6a46 Feat: Scheduled send and send delay #322
* ADD DOC

* Scheduld Send

* add new shortcuts

* fix

* fix

* fix bugs

* rework

* fix draft duplicating

* fix err

* some fixes

* fixes from review

* fixes from review

* fixes from review

* disable password managers for recipients

* fix email store lazy load

* add translations

* fix styling

* fixes

---------

Co-authored-by: Linus Rath <139418639+rathlinus@users.noreply.github.com>
2026-05-28 18:46:49 +02:00
Shuki VakninandLinus Rath 82be047708 fix(email-viewer): stop shattering table cells with word-break: break-word
The global rule

    td, th { word-break: break-word; }

was breaking HTML-email tables one glyph per row whenever a column was
narrow, especially for Hebrew/Arabic/CJK headers and long English
strings. The non-standard `word-break: break-word` keyword behaves
like `break-all` in some engines, splitting words at arbitrary
character boundaries even when the word would fit if the column auto-
expanded.

`overflow-wrap: break-word` is already set on body/table, so the rule
only needs to add min-content relaxation for cells. `overflow-wrap:
anywhere` does exactly that without re-introducing break-all
behaviour.

Repro: any transactional Hebrew/RTL order-summary email — each header
(`מוצר`, `כמות`, `מחיר`) collapses to one glyph per row. After the fix
they render on a single line.

Closes #341.
2026-05-28 18:32:06 +02:00
Linus Rath 7d1fb73290 fix: scope Ctrl/Cmd+Enter send to focused composer 2026-05-28 18:28:07 +02:00
Shuki VakninandLinus Rath 2818a16f06 feat(composer): Ctrl+Enter / Cmd+Enter sends the open draft
Adds the universal "send with the platform modifier" shortcut every
mainstream mail client (Gmail, Outlook, Apple Mail, Proton, Tutanota,
Fastmail, Thunderbird) supports. Closes #343.

Behaviour:

* Window-level keydown listener registered while the composer is
  mounted. Fires when focus is anywhere inside the composer — chip
  inputs, subject, body textarea, or the rich-text contentEditable.
* Plain Enter is untouched; only Enter + Ctrl (Win/Linux) or Cmd
  (macOS) triggers send. Shift/Alt modifiers are ignored so existing
  autocomplete-confirm / chip-commit Enters are not hijacked.
* Routes through a ref so handleSend's per-render rebind doesn't
  re-register the listener every render.
* All existing send-time validation, attachment-warning, draft-save
  and undo-send flows still apply — the shortcut just calls the
  same handleSend() as the toolbar button.
* Listed in the Keyboard Shortcuts dialog under the existing
  Composer section.

Tested:

* Compose -> type body -> Ctrl+Enter -> Outbox.
* Cc/Bcc autocomplete suggestion + Enter still selects (alt-free
  Enter without Ctrl, so the new listener bails).
* Subject input -> Ctrl+Enter -> sends.
* Body Enter without modifier -> newline.
2026-05-28 18:23:37 +02:00
Linus Rath 62ebe443f9 fix: align continued multi-week events with week's left edge 2026-05-25 18:13:57 +02:00
Linus Rath 3c0faba837 fix: collapse focus mail layout to multi-line on mobile 2026-05-25 18:05:01 +02:00
Linus Rath e2abc8dee9 fix: prefix remaining <img>, favicon, and WebDAV URLs with basePath #319 2026-05-25 16:45:13 +02:00
Linus Rath 42ec34be21 fix: show end date in event popover for multi-day events #318 2026-05-25 16:28:03 +02:00
Linus Rath 534894c38d feat: locale-aware date format in email list with preset picker #331 2026-05-25 16:17:32 +02:00
Linus Rath 537707d9ed feat: include group inboxes in unified mailbox view #328 2026-05-23 16:01:22 +02:00
Linus Rath d13934c2a6 fix: match user-avatar treatment on quick reply 2026-05-23 15:35:10 +02:00
Linus Rath acc90eb455 feat: add "Move to Trash and mark as read" delete action #323 2026-05-22 19:08:02 +02:00
Linus Rath c46de636e0 fix: keep a gutter on bare-HTML emails on mobile 2026-05-22 17:47:24 +02:00
Linus Rath 4b463f9691 fix: prefix hand-written URLs with basePath for subpath deployments 2026-05-22 16:38:55 +02:00
Linus Rath 58e4a3d117 feat: add post-export action setting (keep/archive/trash) 2026-05-22 15:36:15 +02:00
Linus Rath e3f6ae874d feat: add settings template for multi-email .zip filename 2026-05-22 15:30:15 +02:00
Linus Rath 52f5a5b42c feat: support importing emails from .zip archives 2026-05-22 15:22:33 +02:00
Linus Rath 3ac14ecf38 feat: add filename transform settings 2026-05-22 14:57:25 +02:00
Linus Rath ca0d6805cf feat: add Downloads settings tab with template editor for .eml and attachment filenames 2026-05-22 14:46:25 +02:00
Linus Rath 8bcb487442 feat: name dragged/exported .eml files as "date (from-to) subject" with ASCII-only chars 2026-05-22 14:28:36 +02:00
Linus Rath d3778e6521 fix: handle malformed event dates in calendar route #316 2026-05-22 14:04:02 +02:00
Linus Rath 4269d0589c feat: collapse empty viewer pane and hide placeholder in Pro mode 2026-05-22 12:19:34 +02:00
Linus Rath ac4a89120d fix: preserve inline images when replying #163 2026-05-22 12:06:55 +02:00
Linus Rath 704a259432 feat: hide empty-state placeholder in email viewer pane 2026-05-22 12:04:40 +02:00
Linus Rath 1c02970ae1 fix: use canonical INBOX in Sieve filter paths #313 2026-05-22 11:51:25 +02:00
Linus Rath fc5f6f43d6 feat: expose PWA, app identity, and extension directory keys in JSON config #312 2026-05-21 23:35:58 +02:00
Linus Rath 9b22ef810e fix: allow adding contacts from mail recipient popover on mobile #306 2026-05-21 23:23:45 +02:00
Linus Rath dbc0eea148 feat: group composer From dropdown by account in Pro shell 2026-05-21 23:07:02 +02:00
Linus Rath e80412b6fd feat: show contacts from all logged-in accounts in Pro shell 2026-05-21 22:32:17 +02:00
Linus Rath 2c825af689 fix: show avatars in calendar/address book sharing menu 2026-05-21 18:58:46 +02:00
Linus Rath 4cfee4f672 feat: split owned vs shared calendars per account in sidebar in Pro shell 2026-05-21 18:54:09 +02:00
Linus Rath 7076f1ded8 feat: show calendars from all logged-in accounts in Pro shell 2026-05-21 18:48:55 +02:00
Linus Rath 9fbcdf7a5f fix: hide mail sidebar header in Pro shell 2026-05-21 18:19:30 +02:00
Linus Rath c9cda3e203 fix: parent navigation detaching account in Pro shell file browser 2026-05-21 18:06:59 +02:00
Linus Rath 3540bf42d9 fix: use Avatar component in Pro shell file account picker 2026-05-21 18:05:03 +02:00
Linus Rath 33e655bcce feat: narrow-pane sidebars and cross-account file picker in Pro shell 2026-05-21 17:54:03 +02:00
Linus Rath 15a67a14b3 fix: parent dir navigation jumping to root in file browser 2026-05-21 17:27:49 +02:00
Linus Rath c9435f7580 feat: always show unified mailbox in Pro shell 2026-05-21 17:01:34 +02:00
Linus Rath 426d344aa8 feat: multi-account mail sidebar and client routing for Pro shell 2026-05-21 16:52:03 +02:00
Linus Rath eb6e5f589e fix: hide redundant account switcher in mail sidebar inside Pro shell 2026-05-21 15:54:33 +02:00