## Bug 1: First open didn't mark as read
ROOT CAUSE: Race condition. markAsRead set local status="read",
then getEntry returned the server's status="unread" (API sync
hadn't completed yet) and overwrote the local mutation.
FIX:
- markAsRead runs FIRST, before getEntry (was concurrent before)
- After getEntry, merge server response but PRESERVE local
status/starred (which may differ from server due to race)
- currentEntry syncs from vm.entries after markAsRead, ensuring
the toolbar reflects the correct state
## Bug 2: Long articles freeze before scrollable
ROOT CAUSE: WKWebView.scrollView.isScrollEnabled = false, embedded
inside SwiftUI ScrollView with .frame(height: webViewHeight).
For a 15000px article, WebKit had to render the entire document,
JavaScript measured document.body.scrollHeight, SwiftUI relaid out
the 15000px frame — all blocking before scroll became responsive.
FIX:
- WKWebView now handles its own scrolling (isScrollEnabled = true)
- Removed SwiftUI ScrollView wrapper around article
- Removed contentHeight binding and height measurement JavaScript
- Removed the Coordinator's didFinish height evaluation
- Article header (title, feed, time) moved into the HTML document
so it scrolls naturally with the content
- WKWebView fills available space, scrolls natively via WebKit's
compositor thread — immediate scroll response
Both fixes preserve the shared WKWebView architecture.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>