From c4c1b44ffadec8592e619484792538b22f838f66 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] 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 | 186 +++++++++++++++-- src/tests/setup.js | 9 + src/tests/unit/components/Thread.vue.spec.js | 198 +++++++++++++++++++ 3 files changed, 381 insertions(+), 12 deletions(-) diff --git a/src/components/Thread.vue b/src/components/Thread.vue index 2fc98d88df..69c171dd19 100644 --- a/src/components/Thread.vue +++ b/src/components/Thread.vue @@ -11,9 +11,14 @@ :error="errorTitle ? errorTitle : t('mail', 'Not found')" :message="errorMessage" />