Commit Graph
172 Commits
Author SHA1 Message Date
Linus Rath 989385f4f6 Merge branch 'feat/desktop-offline' into feat/do-ux-compose-fixes
# Conflicts:
#	components/email/email-viewer.tsx
#	components/settings/accessibility-settings.tsx
#	lib/__tests__/settings-search.test.ts
#	lib/settings-search.ts
2026-11-04 09:54:25 +01:00
Linus Rath ceaceb7f2a Merge branch 'feat/desktop-offline' into feat/do-ux-compose-fixes
# Conflicts:
#	components/email/email-composer.tsx
#	components/email/email-viewer.tsx
#	locales/ar/common.json
#	locales/ca/common.json
#	locales/cs/common.json
#	locales/da/common.json
#	locales/de/common.json
#	locales/en/common.json
#	locales/es/common.json
#	locales/et/common.json
#	locales/fa/common.json
#	locales/fr/common.json
#	locales/he/common.json
#	locales/hu/common.json
#	locales/it/common.json
#	locales/ja/common.json
#	locales/ko/common.json
#	locales/lv/common.json
#	locales/mn/common.json
#	locales/nb/common.json
#	locales/nl/common.json
#	locales/pl/common.json
#	locales/pt/common.json
#	locales/ro/common.json
#	locales/ru/common.json
#	locales/sk/common.json
#	locales/th/common.json
#	locales/tr/common.json
#	locales/uk/common.json
#	locales/vi/common.json
#	locales/zh-TW/common.json
#	locales/zh/common.json
2026-11-04 09:20:50 +01:00
Linus Rath fcc9fc0cc0 Merge branch 'feat/desktop-offline' into feat/do-ux-mail-fixes
# Conflicts:
#	components/email/email-viewer.tsx
#	components/mail/mail-app.tsx
#	components/offline/offline-host.tsx
#	components/ui/welcome-banner.tsx
#	locales/ar/common.json
#	locales/ca/common.json
#	locales/cs/common.json
#	locales/da/common.json
#	locales/de/common.json
#	locales/en/common.json
#	locales/es/common.json
#	locales/et/common.json
#	locales/fa/common.json
#	locales/fr/common.json
#	locales/he/common.json
#	locales/hu/common.json
#	locales/it/common.json
#	locales/ja/common.json
#	locales/ko/common.json
#	locales/lv/common.json
#	locales/mn/common.json
#	locales/nb/common.json
#	locales/nl/common.json
#	locales/pl/common.json
#	locales/pt/common.json
#	locales/ro/common.json
#	locales/ru/common.json
#	locales/sk/common.json
#	locales/th/common.json
#	locales/tr/common.json
#	locales/uk/common.json
#	locales/vi/common.json
#	locales/zh-TW/common.json
#	locales/zh/common.json
2026-11-04 09:11:09 +01:00
Linus Rath 044647ff34 fix: keep the desktop layout in a half-screen window and at 150 % zoom
A window snapped to half the screen (640-800 px) or a 1366x768 laptop
at 150 % (911 px) got the phone and tablet layout: bottom tab bar,
folders in a drawer, Settings as a phone list with a Back arrow. The
layout switched at the 64rem (1024 px) breakpoint.

Now the phone layout starts below 600 px. From 600 to 1024 px the app
is a compact desktop: app rail at the side, folder sidebar folded to
its icons (the existing collapsed state, opened per session with its
toggle), Settings with its two panes and a narrower page list. Below
840 px the split layout opens a message in the list's place, as the
focused layout does. This holds in the browser and the desktop app,
in the Pro shell (which now runs from 600 px) and under Mountain View.

- `hooks/use-media-query.ts`: classifyWidth, PHONE_BELOW 600,
  FULL_DESKTOP 1024; the tablet class (`isTablet`) is gone, the store
  has `isCompact` instead. Plugins still see "tablet" for it.
- `app/globals.css`: a `desk:` breakpoint at 37.5rem for the layout
  classes, and a `tap:` variant for 44px rows on phones and touch
  screens below 1024 px (was every window below 1024 px).
- Touch screens below 1024 px keep swiping list rows (was phones only).
- The boot skeletons, boot script, mail app, sidebar, reader, admin
  shell and calendar, contacts and files follow the new widths.

Tests changed with the behaviour: the tablet cases in pro-gate,
mail-app-mv-composer-move, mail-app-compose-handoff,
mail-app-thread-expand-mark-read, mobile-tour-end and
thread-list-item-columns now run on a phone or a compact desktop, as
those widths no longer exist as a tablet. mail-app-short-window-tab-bar
is replaced by mail-app-compact-layout: a phone on its side is a
compact desktop with the rail at the side, so the short-window tab bar
rule is gone. Class and media-query assertions follow desk:/tap: and
37.5rem; the 32px-text test uses 1100 px, since 1440 px at 32px is now
a compact desktop.
2026-11-04 09:07:06 +01:00
Linus Rath de09da137b Merge branch 'feat/do-menu-winctl' into feat/do-menu-fixes 2026-11-04 07:50:16 +01:00
Linus Rath 0ced5482d5 feat: list your contacts in the composer's recipient picker, with the directory on its own tab 2026-11-04 07:03:15 +01:00
Linus Rath e29a6553e6 fix: keep notices and the offline bar clear of the window's controls, give each a close, and put a pane's close first where it would sit beside the window's 2026-11-04 07:02:53 +01:00
Linus Rath 5c2d306964 feat: add New event, New task, New contact, Import mail and the composer's Send, Save draft and Attach files to the desktop menus 2026-11-04 06:59:09 +01:00
Linus Rath 5fc87eafbd Merge branch 'feat/do-menu-audit' into feat/do-menu-fixes
# Conflicts:
#	components/email/email-context-menu.tsx
#	components/mail/mail-app.tsx
2026-11-04 06:19:07 +01:00
Linus Rath 8dfaa39607 fix: leave Edit, Move and Delete out for contacts in a read-only address book
The two "Add to group" tests click the entry by its new key, which ends
in "…" because it opens a group picker.
2026-11-04 05:32:24 +01:00
Linus Rath e74d2aaa72 fix: keep the app's own buttons out from under the window's controls and move a pane's close away from the window's 2026-11-04 05:27:57 +01:00
Linus Rath 529d334496 Merge branch 'fix/ui-bug-hunt' 2026-11-03 19:44:46 +01:00
Linus Rath 151cedf992 Merge branch 'feat/do-app-lock' into feat/desktop-offline 2026-11-03 18:46:05 +01:00
Linus Rath 383e579eec feat: run the app to the top of the window, with the window's controls in its own toolbars
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
2026-11-03 16:52:42 +01:00
Linus Rath fb3c82eed5 feat: lock the app with a PIN, a passkey or Windows Hello, with mail out of memory and the local store closed
The lock is off by default, and an OS policy can require it and set the
timeout. It locks after a time without use counted across all windows and
tabs, on OS screen lock in the desktop app, and from Lock now.

Locking saves open composers, stops the outbox at its next message, closes
the store and stops its worker with every key, then reloads the page, so
nothing of the old page stays in reach. The new page holds the app back at a
Suspense boundary until someone unlocks. Pop-outs get no session while
locked, push notifications name nobody, and subjects leave plain storage.

One random lock key wraps each account's key as a layer. The PIN wraps the
lock key with PBKDF2 at 600,000 iterations, with a wait after wrong tries
that a reload doesn't reset. A passkey with PRF and the desktop's OS storage
wrap it too. Details and limits are in the decisions file.
2026-11-03 13:08:37 +01:00
Linus Rath dccf4e8b9f chore: merge feat/desktop-offline into the windows branch 2026-11-03 07:31:32 +01:00
Linus Rath 0444cbd4a2 fix: let the last item of a list or day scroll clear of the add button on a phone 2026-11-03 07:01:09 +01:00
Linus Rath bb2393cf04 feat: open messages and drafts in their own windows and save all attachments to a folder
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`.
2026-11-03 06:50:21 +01:00
Linus Rath 8931e4ad48 fix: show the whole phone number and email address in the contact form in a narrow pane 2026-11-03 05:28:26 +01:00
Linus Rath 3bcd10b95f fix: keep the contact actions in reach in a narrow pane and on one row on a phone 2026-11-03 05:28:25 +01:00
Linus Rath c65d18eaae feat: open mail, calendar and contact files from the OS and share into a new draft
The installed app only handled mailto: and webcal: launches. The launch
consumer dropped files, and nothing could hand a File to the composer or
the import dialogs, because the pending mailto is JSON in sessionStorage.

- manifest: `shortcuts` named in the browser's language, `file_handlers`
  for .eml, .ics and .vcf, and `share_target`, all with the base path and
  per-domain branding. Lite's manifest is unchanged.
- `lib/launch-handoff.ts`: the one place files and links wait, in memory
  and in the Cache API, so they survive a sign-in that leaves the page.
  The desktop bridge can call the same functions, since launchQueue never
  fires in Electron. Sign-out clears it.
- launch consumer: routes files by type, and takes a shortcut to an open
  window, which focus-existing would otherwise only focus.
- .eml opens in a new host for the preview, also before sign-in, with the
  strictest device policy deciding about saving. .ics and .vcf open their
  import dialogs through a new `initialFile` prop. A .ics needs the server,
  so offline the dialog says so and carries on once back online.
- composer: `initialFiles` goes through `addFiles`, so plugin vetoes and
  size limits still apply, also from a Pro compose tab.
- `sw.js` takes the share POST before the offline check and keeps the files
  for the page. Without a worker, `/protocol/share` passes text and links
  on and the page says the files are lost. `protocol` joins the app
  sections and the handler pages join the shell, so a mailto: launch
  offline no longer gets a network error.

Browsers without launchQueue or a service worker behave as before.
2026-11-03 05:26:01 +01:00
Linus Rath e9e07ddb64 Merge main into feat/assistant-everywhere 2026-11-03 01:35:57 +01:00
Linus Rath ad36f8d382 fix: show the side panel only where the plugin does, and keep it steady
- 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.
2026-11-02 23:54:53 +01:00
Linus Rath 8fcd647215 feat: let plugins show a side panel next to Calendar, Contacts, Files and Settings 2026-11-02 21:59:36 +01:00
Linus Rath 57c1d5d472 feat: show the contacts, files and plugin page shapes while the app loads 2026-11-01 19:36:09 +01:00
Linus Rath 0401352e6e Merge branch 'feat/enterprise-pack-d'
# Conflicts:
#	components/email/thread-conversation-view.tsx
#	lib/admin/outgoing-hosts-registry.ts
2026-11-01 10:48:21 +01:00
Linus Rath e80f5ade81 feat: let admins turn off downloads, export, printing and the install prompt, and add a watermark
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.
2026-11-01 08:52:34 +01:00
Linus Rath b3369c7e78 feat: send every link click through the link check 2026-11-01 08:31:27 +01:00
Linus Rath d3fecae85f feat: hide the contacts directory when the server refuses it, and let users or admins turn it off #1457 2026-10-31 16:22:24 +01:00
Linus Rath 35be105a73 fix: size small text in rem so it follows the browser and app text size 2026-10-29 19:38:45 +01:00
Linus Rath b22968a7df fix: show the message list, not only the reader, with a large browser text size and wrap cut-off labels 2026-10-29 19:38:45 +01:00
Linus Rath 9f6c7b99c9 fix: raise text contrast to 4.5:1 in every built-in theme, light and dark 2026-10-29 19:38:45 +01:00
Linus Rath 1111dd6709 fix: show the focus outline on menu items, grid cells and other roving-focus controls 2026-10-29 19:38:44 +01:00
Linus Rath 4e2affe406 fix: name the remaining search boxes, filter fields and settings inputs that only had a placeholder 2026-10-29 18:03:00 +01:00
Linus Rath ef13556a01 fix: give every contact form field a label screen readers read out 2026-10-29 18:02:59 +01:00
Linus Rath 693687dc0b fix: show a contact's copy and compose buttons on keyboard focus and touch 2026-10-29 17:53:51 +01:00
Linus Rath 8e55184e1f fix: name pane resize handles, expose their width and keep their arrow keys from the mail shortcuts 2026-10-29 17:53:51 +01:00
Linus Rath dcc504fcfa feat: add landmarks, an h1 per app, skip links and F6 between panes 2026-10-29 17:53:35 +01:00
Linus Rath 13d697ddfb feat: turn off every single-key shortcut when the setting is off 2026-10-29 17:53:34 +01:00
Linus Rath 368acca121 fix: stop app shortcuts from acting behind open dialogs and menus 2026-10-29 17:53:33 +01:00
Linus Rath 63375f6870 Merge pull request #1410 from motofix/fix/1407-vcard-4-export
fix: export contacts as valid vCard 4.0 #1407
2026-10-29 00:13:30 +01:00
Linus Rath 8b35962efb fix: show section labels in sentence case instead of all caps 2026-10-28 22:26:39 +01:00
Linus Rath 39eb38a0fa fix: give the mail, calendar, contacts, files and settings headers one search field and one button style 2026-10-28 22:16:48 +01:00
Linus Rath 38720f2d43 feat: print contact lists, contacts, groups and directory entries from Print or Ctrl+P 2026-10-28 21:08:21 +01:00
Julien Béti a3fdd72763 fix: export contacts as valid vCard 4.0 #1407
The export declared VERSION:3.0 but wrote vCard 4.0 syntax, and wrote the JSContact phone feature \"mobile\" as TYPE=MOBILE, which is no vCard type, so importers dropped such numbers.

- Declare VERSION:4.0 and write 4.0 throughout: basic-format dates and timestamps, data: URIs for inline media, VALUE=text for a UID or RELATED that is no URI, geo: URIs, lines folded at 75 octets, CRLF after every line, and the webmail's own PRODID.
- Map "mobile" to CELL and back; MAIN-NUMBER and x- types are kept.
- Write N before FN: some importers ignore the name otherwise.
- The contact form stores mobile phones as "mobile" (RFC 9553) and still reads the "cell" it used to write.
2026-10-28 17:27:07 +01:00
Linus Rath 4421c3ce7e fix: spin refresh icons the way their arrows point 2026-10-28 17:00:50 +01:00
Linus Rath ac5596165a fix: show only the app's own message for a wrong address in the contact form, without the browser bubble over it 2026-10-28 13:05:43 +01:00
Linus Rath 0eb68f1743 Merge branch 'ux/settings-admin2' 2026-10-28 11:06:39 +01:00
Linus Rath f91bee132f fix: keep the contact form's name fields readable on phones and tablets 2026-10-28 10:43:55 +01:00
Linus Rath 2f790a2d75 fix: keep toasts above the Save and Cancel buttons of the contact and group forms 2026-10-28 10:43:55 +01:00