From 9851f1b3c25979340633cb18d8b37f75263c13ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobi=C3=A1=C5=A1=20Va=C5=A1=C5=A5=C3=A1k?= Date: Sat, 22 Aug 2026 01:43:28 +0200 Subject: [PATCH 1/2] feat(ui): fold the thread subject as the thread scrolls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A long subject wrapped over several lines and pushed the message down, and the sticky header kept those lines on screen for the whole thread. Fold it to a single line as the thread scrolls instead, tied to the scroll position rather than to a timer, so that a reader who stops halfway finds it halfway. The header hands back the height the subject gives up. Without that, everything below moves up by as much on top of the scrolling that caused the fold, and the thread runs away from the finger; it also ties how fast the subject folds to how fast the thread moves, so neither can be chosen on its own. A line is measured in lh rather than --default-line-height, which the server sets to a bare ratio: subtracting it from a length voids the declaration it sits in, and the fold never ran at all. Scroll anchoring is off. The header changing height had the browser scroll back to compensate, which moved the thread over the mark that started the fold and unfolded it again, in a loop that got worse the longer the subject was. That loop is what sank the attempt in #11754. The mobile offsets that compensated for the wrapping subject go with it, and the subject is indented past the back button that the app content lays over the header. Assisted-by: ClaudeCode:claude-opus-5 Signed-off-by: Tobiáš Vašťák --- src/components/Thread.vue | 180 +++++++++++++++-- src/tests/setup.js | 9 + src/tests/unit/components/Thread.vue.spec.js | 195 +++++++++++++++++++ 3 files changed, 367 insertions(+), 17 deletions(-) diff --git a/src/components/Thread.vue b/src/components/Thread.vue index 100c9eac09..61024c0f93 100644 --- a/src/components/Thread.vue +++ b/src/components/Thread.vue @@ -11,9 +11,14 @@ :error="errorTitle ? errorTitle : t('mail', 'Not found')" :message="errorMessage" />