perf: lazy async image decoding + max-height to prevent scroll freeze
All checks were successful
Security Checks / dependency-audit (push) Successful in 13s
Security Checks / secret-scanning (push) Successful in 4s
Security Checks / dockerfile-lint (push) Successful in 5s

ROOT CAUSE: Articles from sites like onemileatatime.com serve WebP
images with .jpeg extensions. WebKit attempts JPEG decode, fails,
retries as WebP — the retry loop blocks the compositor thread during
scroll, causing ~1s freezes per image.

FIX 1 — Image attributes (ArticleHTMLBuilder.optimizeImages):
Regex injects loading="lazy" decoding="async" on all <img> tags
that don't already have loading= set. This tells WebKit to:
- decode images off the main/compositor thread (decoding=async)
- only decode when approaching viewport (loading=lazy)

FIX 2 — CSS max-height:
img { max-height: 600px; object-fit: contain; }
Limits decoded image buffer size. A 1200x900 image still displays
at full width but WebKit doesn't need to composite oversized tiles.

Both fixes are content-rendering optimizations only — no changes
to WKWebView architecture, scrolling model, or layout system.

Also marked Atmos Rewards articles as unread for testing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Yusuf Suleman
2026-04-04 00:30:37 -05:00
parent cb7907ef33
commit 7938034d85

View File

@@ -156,7 +156,7 @@ enum ArticleHTMLBuilder {
.article-meta {
font-size: 13px; color: #8B6914; line-height: 1.4;
}
img { max-width: 100%; height: auto; border-radius: 8px; margin: 12px 0; }
img { max-width: 100%; height: auto; max-height: 600px; object-fit: contain; border-radius: 8px; margin: 12px 0; }
a { color: #8B6914; }
h1, h2, h3, h4 { line-height: 1.3; margin-top: 24px; margin-bottom: 8px; }
pre, code { background: #f5f0e8; border-radius: 6px; padding: 2px 6px; font-size: 15px; overflow-x: auto; }
@@ -214,9 +214,27 @@ enum ArticleHTMLBuilder {
<h1 class="article-title">\(titleHTML)</h1>
<div class="article-meta">\(meta)</div>
</div>
<div id="article-body">\(body)</div>
<div id="article-body">\(optimizeImages(body))</div>
</body>
</html>
"""
}
/// Add loading="lazy" and decoding="async" to all <img> tags.
/// This tells WebKit to decode images off the main thread and
/// only when they approach the viewport, preventing scroll freezes
/// from large/mismatched-type images (e.g. WebP served as .jpeg).
private static let imgTagRegex = try! NSRegularExpression(
pattern: #"<img(?![^>]*loading=)([^>]*)>"#,
options: .caseInsensitive
)
private static func optimizeImages(_ html: String) -> String {
let range = NSRange(html.startIndex..., in: html)
return imgTagRegex.stringByReplacingMatches(
in: html,
range: range,
withTemplate: #"<img loading="lazy" decoding="async"$1>"#
)
}
}