Files
webmail/app/desktop.css
T
Linus Rath b79781cc94 fix: never lose a draft moving between windows and keep credentials off the window channel
Review of the pop-out work found ways to lose text, a wrong sign-out and
changes reaching ordinary browser tabs.

- Drafts: a closing window hands text over only to a main window holding
  its login, and asks instead when the last save failed, the browser is
  offline, a save is running or plugins could still have a say. A failed
  rescue goes to "Continue draft". The opening window lets go only once
  the composer is on screen, also for Edit draft in a message window.
  Send options travel with the draft. Closing during a send asks first.
- Sessions: answers are encrypted for the window that asked, and only a
  window holding the login answers. A renewal queued on the lock can be
  called off by a sign-out, signs nobody out once its account is gone, and
  takes the cached token after waiting (Rauthy `nbf`, #552).
- Ordinary tabs run none of the host. No lock is held for life.
- A message window hides after a refused session and waits for its
  folders; a composer stays on screen with its text. Another tab dropping a
  login it couldn't restore no longer closes pop-outs.
- Plain-text mail stays selectable in the desktop app. Folder save renames
  reserved and shell names and skips one refused name. All display-mode
  checks know the overlay. The attachment bar keeps to one row.

The earlier test that accepted text being gone after a failed rescue now
asserts that it reaches the "Continue draft" slot.
2026-11-03 08:37:10 +01:00

77 lines
2.1 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. An HTML message is in its own frame; a plain-text one is in the page,
* and the rule at the end keeps it selectable wherever it sits.
*/
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-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;
}
/* A plain-text message renders in the page, also inside rows and buttons. */
html[data-desktop] [data-plugin-source="email-body"],
html[data-desktop] [data-plugin-source="email-body"] * {
-webkit-user-select: text;
user-select: text;
cursor: text;
}
html[data-desktop] [data-plugin-source="email-body"] a {
cursor: pointer;
}