feat: Phase 1 auto-scroll engine for Reader feed
ENGINE (ScrollViewDriver.swift):
- UIViewRepresentable placed inside ScrollView (zero size)
- Finds parent UIScrollView via view hierarchy traversal
- CADisplayLink at 60fps drives contentOffset.y smoothly
- Speed: 1.0x = 60pt/sec, adjustable 0.25x–3.0x in 0.25 steps
- User touch detection: intercepts UIScrollViewDelegate
scrollViewWillBeginDragging → stops auto-scroll immediately
- Stops at bottom (contentOffset >= maxOffset)
- Forwards all delegate methods to SwiftUI's original delegate
INTEGRATION (EntryListView):
- Accepts @Binding isAutoScrolling + scrollSpeed
- ScrollViewDriver placed as first child in ScrollView
- Auto-scroll stops on: user touch, navigation back (onAppear),
filter change, sub-tab change, reaching bottom
CONTROLS (ReaderTabView — temporary, Phase 1):
- Play/Stop button in toolbar (play.fill / stop.fill)
- When playing: [-] speed [+] controls appear inline
- Speed shown as "1.00x" with monospacedDigit
MARK-AS-READ:
- Auto-scroll drives real UIScrollView contentOffset
- This moves LazyVStack rows, triggering their GeometryReader
onChange callbacks — the existing mark-as-read system fires
naturally with no special case or bypass needed
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>