Adds a "Send email to group" action (To / Cc / Bcc) that opens the composer
pre-filled with the group's members in the chosen field, preserving each
member's display name. It is available both in the group context menu (between
"Edit Group" and "Delete") and in the group detail panel's header (shown when
the group has at least one member with an email). The single-contact "Send
email" button in the contact detail panel uses the same path.
Routing is internal, not via mailto:. Contacts is its own route and the composer
lives in the mail route, so the handoff stashes the recipients
(savePendingMailto) and does a client-side router.push("/"); the main route's
existing consumePendingMailto effect opens the composer in the current account.
This avoids the OS mailto handler (which could open a different mail app) and
the protocol round-trip's full-page reload, which dropped the in-memory
per-account JMAP clients of a multi-account session (a logout).
- contacts/page.tsx: openComposeInApp(recipients, field) shared helper;
handleComposeGroupFromSidebar (deduped "Name <email>" members, empty -> toast)
and handleComposeContact; wired to the sidebar, group detail, contact detail.
- contact-group-detail.tsx: onComposeGroup(field) prop + To/Cc/Bcc header control
(shown when the group has emailable members).
- contacts-sidebar.tsx: onComposeGroup(groupId, field) prop + "Send email to
group" submenu between Edit and Delete.
- contact-detail.tsx: onCompose() prop; the button is no longer a mailto: link.
- mailto.ts: recipient splitter is quote-aware (reuses the composer's
splitRecipients) so a comma in a display name survives — still useful for real
OS mailto: links.
- i18n: contacts.groups.send_email{,_to,_cc,_bcc} and no_member_emails across all
locales.
Display names round-trip via formatRecipient -> parseRecipientList.
Alternative to the quote-aware string fix: represent committed To/Cc/Bcc
recipients as Recipient[] ({name?, email}) with a separate input-text
string per field, instead of a single comma-joined string parsed with
split(','). Structured recipients can never be torn apart on a delimiter,
so a display name containing a comma ("Doo, John <john@doo.org>", as
produced on Reply-All) stays a single chip.
- email-composer-utils: add Recipient type, parseRecipient/formatRecipient,
and parseRecipientList/formatRecipientList for the (de)serialization
boundary (ComposerDraftData stays a string; quoting keeps it lossless).
Remove the now-unused string-chip helpers.
- email-composer: to/cc/bcc are Recipient[]; toInput/ccInput/bccInput hold
the in-progress text. Reply/forward init, autocomplete, chip edit, drag &
drop (payload now carries the structured recipient), send/draft/validation
and template paths all operate on arrays. withInput() folds uncommitted
typed text into the send/validation set.
- Tests updated for the array contract; add comma-in-name chip coverage.
Adds native HTML5 drag-and-drop so users can move recipient email
address chips between the To, CC, and BCC fields in the composer.
Chips dragged onto the Cc/Bcc toggle buttons auto-reveal the hidden
field and place the chip there.
The quick-reply box built its body with appendPlainTextSignature, which runs
the identity's HTML signature through htmlToPlainText, and sent a text-only
message (htmlBody was undefined). A formatted signature (e.g. <strong>…) was
therefore flattened to plain text in the sent mail, even though it previewed
correctly in the identity editor. The full composer already builds an HTML
signature block; quick reply did not.
Add an appendHtmlSignature helper (mirrors the composer's send-time block) and,
when the sending identity has an HTML signature, send a matching HTML body from
handleQuickReply so the markup is preserved. Text-only identities keep the
plain-text-only behavior.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Restricts src to https: URLs or base64-embedded raster data: URIs (png/jpeg/gif/webp).
- SVG is excluded for safety reasons.
- Images with a disallowed src are removed entirely so they don't render as broken-image icons.
* Added account selection for protocol links when multiple connected accounts are available, including mailto: links
* Added support for handling mailto: links in an already-open PWA/session instead of always opening a new tab
* Added webcal: protocol handling for calendar links
* Added account selection for webcal: links when multiple calendar-capable accounts are connected
* Added an import-or-subscribe choice for detected webcal calendars
* Added protocol handler settings for registering mail and calendar handlers and choosing the open mode
* Added service worker/session coordination for passing protocol requests between browser/PWA contexts
* Added tests and translations for the new protocol handler flows
Adds an Override toggle in the composer's From row. When enabled, name
and address become free-text inputs. Mail is still submitted through the
selected identity, but the outgoing message's From: header — and the
SMTP envelope MAIL FROM when different — is set from the override.
The existing "Auto-select Reply Address" setting is extended: if the
incoming message was addressed to an alias on a domain that matches one
of your identities but isn't itself an identity (classic domain catch-
all), it now auto-enables Override and pre-fills the alias. Quick reply
honors the same resolution. The setting is relabeled to reflect the
broader behavior.
JMAP: client.sendEmail gains an optional envelopeMailFrom; when set, the
EmailSubmission includes an explicit envelope with that mailFrom and the
to/cc/bcc as rcptTo so header-From and envelope can diverge (JMAP §7.3).
S/MIME: override is incompatible with sign/encrypt and is refused with a
clear error — signing a different visible From from the identity's
certificate Subject would produce messages clients reject.
Tests: resolveReplyFrom covers exact match, sub-address stripping,
catch-all detection, identity preference, and foreign-domain null.
Drops the 0.15 REST management API and routes all account/auth/crypto/
principal operations through Stalwart 0.16's schema-driven JMAP
endpoint via a single passthrough (/api/account/stalwart/jmap).
- New client helper `stalwartJmap` + typed `requireResult`
- account-security-store rewritten against x:AccountPassword, x:AppPassword,
x:AccountSettings, x:Account (with currentSecret for TOTP ops)
- Client-side TOTP setup via `otpauth`; server-generated app password
secrets shown once on create
- Admin check switched to /api/account permissions
(sysAccountQuery/sysTenantQuery/sysSystemSettingsGet)
- Removed sieve vacation-overwrite workaround (fixed upstream #1251)
- Deleted old REST routes, StalwartClient, stale tests; added new
tests for passthrough + store