Echte Ursache (per Firefox/Chromium gegen die Live-App verifiziert): Beim Wegklicken
eines Listeneintrags wird die Seite ausgehängt → der Inhalt von .app-main kollabiert →
der Container springt auf scrollTop 0 → das löste ein scroll-Event aus, das 0 als neue
Position speicherte und die gemerkte Stelle ZERSTÖRTE, kurz bevor wir wegnavigierten.
Fix:
- scroll-Events ignorieren, wenn der Container gerade NICHT scrollbar ist
(scrollHeight ~ clientHeight = Inhalt kollabiert/Seitenwechsel) → kein „unechter" 0-Save.
- Wiederherstellung hält den Schutz über die gesamte Settling-Phase: bei jeder DOM-Änderung
Position erneut setzen, erst nach ~350 ms Ruhe freigeben (gegen Reflow-Races beim Nachladen).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Die App scrollt NICHT das Fenster: .app-shell ist height:100dvh/overflow:hidden, der
Inhaltsbereich .app-main hat overflow-y:auto. window.scrollY/scrollTo waren daher
wirkungslos (am Desktop sichtbar). Der Hook arbeitet jetzt auf .app-main (scrollTop/
scrollTo, Scroll-Listener am Container) und beobachtet den Inhalt per MutationObserver,
um nach dem Nachladen exakt zur gemerkten Position zu springen. Fallback auf window,
falls der Container fehlt.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Zwei Probleme bei der großen Rennmausliste (und allen Infinite-Scroll-Listen) nach
Zurück-Navigation behoben:
1) Event- statt Timer-basiert: Die Scroll-Wiederherstellung hängt sich jetzt per
ResizeObserver an die Höhenänderung der Seite (Liste ist wieder da) und springt dann
zur gemerkten Position — statt auf einen festen Timer zu warten. Bricht bei eigener
Scroll-Eingabe ab; Sicherheitsnetz nach 15 s.
2) Infinite-Scroll baut Höhe wieder auf: useInfiniteList merkt sich (optionaler
restoreKey) die geladene Seitenzahl je Query und lädt sie nach einer Zurück-
Navigation wieder nach — sonst war die Liste nur 1 Seite hoch und die Position
unerreichbar. Aktiviert für Rennmäuse, Kontakte, Würfe, Becken, Anfragen, Verträge.
tsc/eslint/vitest grün; e2e (Listen + Navigation, 41) grün.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Bei einem HMR-/Fast-Refresh-Update rendert die Seite kurz neu (Liste kollabiert →
Sprung nach oben), ohne Navigation/pagehide. Jetzt vor `vite:beforeUpdate`/
`vite:beforeFullReload` sichern und nach `vite:afterUpdate` wiederherstellen (nur Dev;
import.meta.hot ist in Produktion undefiniert). Restore-Fenster auf 4 s erhöht, damit
auch langsam nachladende Listen die Position noch erreichen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Neuer Hook useScrollRestoration (einmal im AppShell gemountet) erhält die
Scrollposition je Route über:
- Zurück/Vorwärts-Navigation (POP restauriert, PUSH startet oben),
- Bildschirm-Sperre / App-Hintergrund (visibilitychange, pagehide/pageshow) —
damit das Handy nach dem Entsperren nicht mehr oben landet.
Robust gegen nachladende Listen (rAF-Nachjustierung bis ~2,5 s, während die Liste
wächst), bricht bei eigener Scroll-Eingabe ab, Deep-Links (?focus=, #anchor) haben
Vorrang. Die bisher nur in der Tickets-Seite eingebaute Logik wurde durch den
globalen Hook ersetzt.
tsc/eslint/vitest grün; e2e (navigation/listen/tickets, 42) grün.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>