mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-11-04 16:14:07 +00:00
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`.
65 lines
1.8 KiB
CSS
65 lines
1.8 KiB
CSS
/*
|
|
* The desktop app is an app, not a page. Everything here hangs on
|
|
* html[data-desktop], which the boot script sets when the preload bridge is
|
|
* there (lib/desktop/boot.ts), so a browser never matches any of it.
|
|
*
|
|
* UI chrome is listed one by one, instead of switching selection off for the
|
|
* whole page and listing what may stay on: a message, a contact's details or
|
|
* an event's notes must not stop being selectable because nobody thought of
|
|
* them. Message bodies are in their own frame and aren't touched at all.
|
|
*/
|
|
|
|
html[data-desktop],
|
|
html[data-desktop] body {
|
|
overscroll-behavior: none;
|
|
}
|
|
|
|
html[data-desktop] :is(
|
|
button,
|
|
summary,
|
|
label,
|
|
nav,
|
|
[role="button"],
|
|
[role="navigation"],
|
|
[role="toolbar"],
|
|
[role="menu"],
|
|
[role="menubar"],
|
|
[role="menuitem"],
|
|
[role="menuitemcheckbox"],
|
|
[role="menuitemradio"],
|
|
[role="tab"],
|
|
[role="tablist"],
|
|
[role="listbox"],
|
|
[role="option"],
|
|
[role="tree"],
|
|
[role="treeitem"],
|
|
[role="row"],
|
|
[data-email-id],
|
|
[data-composer-header],
|
|
[data-mv-compose-titlebar],
|
|
[data-window-title-bar]
|
|
) {
|
|
-webkit-user-select: none;
|
|
user-select: none;
|
|
cursor: default;
|
|
}
|
|
|
|
/* A field inside a label or a row is still a field. */
|
|
html[data-desktop] :is(input, textarea, select, [contenteditable="true"], [contenteditable="plaintext-only"]) {
|
|
-webkit-user-select: text;
|
|
user-select: text;
|
|
}
|
|
|
|
html[data-desktop] :is(input:not([type="checkbox"], [type="radio"], [type="range"], [type="button"], [type="submit"]), textarea, [contenteditable="true"], [contenteditable="plaintext-only"]) {
|
|
cursor: text;
|
|
}
|
|
|
|
/* No dragging an icon or a link by accident. What the app means to drag says so. */
|
|
html[data-desktop] :is(img, a, svg) {
|
|
-webkit-user-drag: none;
|
|
}
|
|
|
|
html[data-desktop] [draggable="true"] {
|
|
-webkit-user-drag: element;
|
|
}
|