The demo user becomes Nora Lindqvist, engineering lead at Meridian Labs,
in the week of a product launch. The fixtures tell one story across mail,
contacts, calendar, tasks and files, and exercise the features the
screenshots and the public demo should show:
- a second, mail-only login (her personal domain) and the shared Support
mailbox delegated to the work account, so the account switcher, the
shared section and the merged inbox have real content
- raw headers the way Stalwart writes them: the sender check fails on
the phishing mail, newsletters carry a one-click unsubscribe, one mail
asks for a read receipt, and spam scores show in the details
- an iMIP invitation with a served .ics, an event waiting for the answer
on the calendar, and RSVP patches applied in the demo client
- @mentions as mailto links in a received mail, a verification code
twelve minutes old, nested folders, named tags on the mail, contact
groups, a scheduled message and tasks switched on
- every attachment and file backed by real bytes under public/demo,
generated by `scripts/demo-assets/generate.mjs`; contact photos are
bundled portraits instead of requests to randomuser.me
- folder counts computed from the mail instead of hand-kept numbers
Demo login seeds the tags, templates and merged-inbox settings once per
device and switches the cross-account gate on for the session; "Reset"
restores them. `bulwark-demo-simulate=off` in localStorage stops the
simulated incoming mail for screenshot runs.
Expanding a conversation in the message list (row click, expand chevron,
or the `x` shortcut) and clicking a row in a Pro folder tab marked the
messages read straight away. They ignored "Mark as read: Never" and any
delay, which the reading pane and the conversation view follow.
Route them through one helper, scheduleAutoMarkAsRead: -1 never marks,
0 marks at once, any other value marks after that many milliseconds and
returns a cancel function.
- New store action autoMarkExpandedThreadAsRead keeps one pending timer
per thread key. Collapsing the thread, or collapsing all threads, drops
it. Both expansion paths (EmailList and the `x` handler in MailApp) use
it.
- MailApp's handleEmailSelect drops the pending mark of every expanded
thread other than the one the newly opened message belongs to, through
cancelAutoMarkThreadAsRead. Moving on with `j`/`k` or a click no longer
lets the delayed mark land on a conversation the user has left. The
thread stays expanded.
- ProFolderTabBody schedules the row mark through the helper and drops a
pending one when another row is opened or the tab unmounts.
With several accounts connected, the composer treated every account's
identities as "own" when deciding whether a message was self-sent (#703).
A message that account B sent to account A therefore looked self-sent
when replying from A, so the reply was addressed to the original To -
account A itself - and From was switched to B.
Self-sent now means "sent by the account holding the original":
buildReplyRecipients takes an optional selfSentEmails list (defaulting to
ownEmails, so single-account behaviour is unchanged), and the composer
passes the source account's identities there and to the From selection.
Reply-all still drops every connected account's addresses.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A new feature gate, archiveActionEnabled (default on), lets the admin turn the
Archive action off for installations that archive mail elsewhere, such as a
mail gateway. With it off, Bulwark offers no Archive anywhere: the viewer's
button and its overflow entry, the context menu for one or several messages,
Mountain View's selection bar, the hover and swipe actions, the E shortcut and
its help row, archiving after an export, and the archive mode setting (also in
the settings search). With it off nothing creates an Archive folder:
ensureArchiveMailbox returns null. Existing Archive folders stay ordinary
folders, moving mail into them by hand still works, and the plugin API is
unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>