The desktop app and an installed PWA had a row across the whole window
with the title and the window controls. That row is gone. The rail,
sidebar, list and reader run to the top of the window in their own
colours, and the controls sit in the top row of whatever is rightmost.
- `lib/window-controls-overlay.ts` finds and marks those rows before
paint, and they reserve the controls' width through
`--titlebar-right-inset`
- the rows and the rail are the drag area, everything clickable is
`no-drag`, and an invisible band lets a page with no row still move
- settings and the admin shell get a row only under the overlay, Pro's
tab strip and Mountain View's bar match the controls' height
- Alt floats the menu over the top left, in `window-menu-bar.tsx`
- skeletons carry the same marks, so nothing shifts when the app loads
Eviction now goes by the base's signal: `storeLost` and the `store-reset`
notice, with `bulwark-store-opened` as the marker and `prune-files` for files
with no key. The storage page and the lost count hang off that. What a user
picks is stored under `bulwark-offline-scope`, the key `offlineScopeFor`
reads, and `email-snapshot` follows the base's rule. `OfflineEnabled` is read
from the desktop bridge when it's there.
The offline section of Settings > Backup & reset now shows, per account, the
space each part takes, the last sync, a first sync's progress with the time
left, the window and attachment choices, and clearing the account. All
conflicts, calendar and contacts too, are listed there and counted in the
banner.
- the organisation's caps beat the user's choice and the desktop default, and
every path that takes a scope goes through `effectiveScope`
- the desktop app keeps mail without asking, says so once, and stays off for
an account where the user turned it off
- `navigator.storage.persist()` is asked on turning it on, and a refusal
warns with a way to ask again; Safari gets the 7-day hint when not installed
- a store the browser deleted is noticed, says what was lost and syncs again
- a full disk says so, with what to do
- those who had offline mode keep it, with the 30 days they had
- Keep offline in the Files app's menu
An installed app had no title bar of its own, no way to save attachments
except a zip, and "pop out" stayed inside the page. A new window was a
second copy of the app that had to restore the session itself, and on
start-up that evicts any login made without "Remember me" from the list
every window shares.
- Title bar: `display_override` with `window-controls-overlay`, one
`--titlebar-inset` variable at the page roots and skeletons, and a drag
strip with the window title. 0 without an overlay, so nothing moves.
- Save all to a folder: `showDirectoryPicker`, asking the device policy
before the picker and before each file, with the zip as the fallback.
- Pop-out routes `/popout/message/<id>` and `/popout/compose`, on offer in
an installed app or when the desktop bridge has windows. Hidden in Lite.
- A pop-out borrows the main window's session and never evicts or starts a
token timer. Every renewal now takes a Web Lock, so two windows can't
send the same refresh token, which Stalwart 1.0 may treat as a replay.
- A draft moves to its window under the same id. A closing window hands
unsaved text to the main window to save, since its own request wouldn't
survive the close.
- Read state, drafts, theme and language follow across windows, and
`html[data-desktop]` gates the app-like behaviour in `app/desktop.css`.
- The button and the panel of Calendar, Contacts, Files and Settings
show only when the plugin's shouldShow says yes, so a plugin switched
off for these views (admin, user or no service) leaves no button that
opens an empty panel, and no extra rows in Contacts and Settings.
- Files: while the details are open the panel steps aside there only;
the other views keep it as the user left it. With both stored open the
details fold when the page loads.
- Resizing stops where the view keeps its 480 px, so a drag or the arrow
keys no longer fold the panel away (and the handle keeps the focus).
- Settings and Contacts render the same element with or without the
panel, so a form keeps what was typed when the panel starts docking.
- Contacts on a phone: the panel button sits over an open contact too.
- The panel buttons of the reader and the views have one name, "Plugin
panel", with aria-pressed, and name the plugin's panel shortcut in
their title; the open panels are named landmarks.
The reader tests look the button up by its new name and pressed state
(viewer-toolbar-over-side-panel, reader-plugin-surfaces,
viewer-plugin-panel-width), since "Show/Hide plugin panel" is gone.
Organisation > Devices now sets policy.json `organisation.devices`, each
switch off by default and scoped per domain or group:
- No downloads: attachments and files only preview (reader, conversation
view, Pro tab, list chips, .eml previews, the Files app). Download,
Download all, Open in new tab, Save to Files, drag-out, the browser's
PDF viewer (pdf.js instead) and media download controls go away.
- No export: no .eml export, View source, message drag-out, or folder and
account export; a running export stops and its file is deleted when
the switch turns on. Import stays.
- No printing: no Print buttons in mail, calendar and contacts; Ctrl+P
prints a note; the reader frame prints blank.
- No install prompt: no card, and the browser's own banner is held back.
- Watermark: the user's address and the date over the reader,
previews and every printout (mail, calendar, contacts, plugins).
A message follows its own account's policy; what isn't about one message
follows the strictest signed-in account. Every user path hides its
button, and the anchor and new-tab helpers refuse as a backstop
(lib/download-guard.ts), never the blob readers. The plugin APIs
`ui.downloadFile` and `ui.print` refuse too, and the office editor frame
loses `allow-downloads`.
Two assertions in components/mail/__tests__/blob-routing.test.tsx now
expect `{ kind: 'export' }` as the fifth argument of downloadBlob for an
.eml export: the export says what it is, so the backstop checks "No
export" rather than "No downloads" for it.
- Pro tabs stamp a message of another login or a shared account with
where it came from, so the report and the move use its account.
- A selection across accounts names every address its reports go to,
and Undo says how many messages came back.
- A message the list doesn't hold runs the plugins' spam hooks too, and
every open view shows a reported message as reported at once.
- A reported message hides its unsubscribe banner, and links are off in
its attachment previews.
- Search results and All mail don't offer reporting the user's own sent
mail; the Mountain View selection bar offers it.
- The focus goes back to the button that opened the menu.
- ARF names the delivered-to address (or leaves it out), says binary for
bare line breaks and long lines, and refuses a message bigger than the
server's upload limit before downloading it.
The phishing tests that compared one address now compare the list of
addresses, and the ARF builder test passes the encoding, not a flag.
An attachment inside a previewed .eml could only be downloaded. A
certified-mail (PEC) message carries the actual mail as postacert.eml,
and a forwarded PEC carries the original PEC inside that, so the message
the user wanted to read never opened. A previewable attachment now opens
in a preview stacked on the first one, with download beside it; Escape
and Back close the inner preview first.