From ab392e2ab983a4c87e241b457c98244251b2206a Mon Sep 17 00:00:00 2001 From: Peter Stone Date: Thu, 23 Jul 2026 22:32:14 +0000 Subject: Strip
chrome before Readability parses; show full article title in reader content - Confirmed live (Ars Technica): a section-kicker badge, the dek/subhead, and an orphaned duplicate hero-image caption were all leaking into extracted content because they live in a
Readability doesn't exclude by tag, only deprioritizes in scoring. Strip
from the DOM ourselves before handing off to Readability -- a pre-processing decision on our side, not a change to Readability's own algorithm, per instruction not to touch it. - Render the article's full title as a heading at the top of the reader content itself (wrapReaderHtml), not just the top bar, which truncates to one line. This also replaces whatever heading Readability happened to keep in-body, now that
(and any

inside it) is stripped pre-parse. - Bump extraction timeout 15s -> 25s: confirmed live, several heavy real-world pages were timing out before onPageFinished ever fired, well short of a real parsing failure. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01EZ7ikw2ukGJFTHE3bJS7zL --- .../java/org/terst/feedreader/ui/LinkReaderScreen.kt | 4 ++-- .../main/java/org/terst/feedreader/ui/ReaderScreen.kt | 4 ++-- .../main/java/org/terst/feedreader/ui/ReaderWebView.kt | 17 +++++++++++++++-- 3 files changed, 19 insertions(+), 6 deletions(-) (limited to 'app/src/main/java/org/terst/feedreader/ui') diff --git a/app/src/main/java/org/terst/feedreader/ui/LinkReaderScreen.kt b/app/src/main/java/org/terst/feedreader/ui/LinkReaderScreen.kt index 1a715b0..4c7251f 100644 --- a/app/src/main/java/org/terst/feedreader/ui/LinkReaderScreen.kt +++ b/app/src/main/java/org/terst/feedreader/ui/LinkReaderScreen.kt @@ -224,13 +224,13 @@ fun LinkReaderScreen(url: String, onBack: () -> Unit, onOpenLink: (String) -> Un modifier = Modifier.fillMaxSize(), factory = { ctx -> newReaderWebView(ctx, onOpenLink).apply { - loadDataWithBaseURL(url, wrapReaderHtml(currentExtractedContent), "text/html", "UTF-8", null) + loadDataWithBaseURL(url, wrapReaderHtml(currentExtractedContent, extractedTitle), "text/html", "UTF-8", null) loadedContent.value = currentExtractedContent } }, update = { webView -> if (loadedContent.value != currentExtractedContent) { - webView.loadDataWithBaseURL(url, wrapReaderHtml(currentExtractedContent), "text/html", "UTF-8", null) + webView.loadDataWithBaseURL(url, wrapReaderHtml(currentExtractedContent, extractedTitle), "text/html", "UTF-8", null) loadedContent.value = currentExtractedContent } }, diff --git a/app/src/main/java/org/terst/feedreader/ui/ReaderScreen.kt b/app/src/main/java/org/terst/feedreader/ui/ReaderScreen.kt index 6e1f939..5ebf2ba 100644 --- a/app/src/main/java/org/terst/feedreader/ui/ReaderScreen.kt +++ b/app/src/main/java/org/terst/feedreader/ui/ReaderScreen.kt @@ -469,13 +469,13 @@ private fun ReaderContent( modifier = Modifier.fillMaxSize(), factory = { ctx -> newReaderWebView(ctx, onOpenLink).apply { - loadDataWithBaseURL(baseUrl, wrapReaderHtml(content), "text/html", "UTF-8", null) + loadDataWithBaseURL(baseUrl, wrapReaderHtml(content, article?.title), "text/html", "UTF-8", null) loadedContent.value = content } }, update = { webView -> if (loadedContent.value != content) { - webView.loadDataWithBaseURL(baseUrl, wrapReaderHtml(content), "text/html", "UTF-8", null) + webView.loadDataWithBaseURL(baseUrl, wrapReaderHtml(content, article?.title), "text/html", "UTF-8", null) loadedContent.value = content } }, diff --git a/app/src/main/java/org/terst/feedreader/ui/ReaderWebView.kt b/app/src/main/java/org/terst/feedreader/ui/ReaderWebView.kt index 503f71e..f95dfed 100644 --- a/app/src/main/java/org/terst/feedreader/ui/ReaderWebView.kt +++ b/app/src/main/java/org/terst/feedreader/ui/ReaderWebView.kt @@ -28,14 +28,27 @@ fun newReaderWebView(context: Context, onOpenLink: (String) -> Unit): WebView = } } +private fun escapeHtml(text: String): String = text + .replace("&", "&") + .replace("<", "<") + .replace(">", ">") + .replace("\"", """) + .replace("'", "'") + // Hardcoded dark palette, not @media (prefers-color-scheme) -- the app itself always // forces dark colorScheme() regardless of system setting (see MainActivity), so reader // content should match rather than following a system light/dark the app itself ignores. -fun wrapReaderHtml(bodyContent: String): String = """ +// +// title is rendered here, as a real heading at the top of the content, rather than relying +// on whatever heading Readability happened to keep in bodyContent (increasingly nothing -- +// see ReaderExtractor's
stripping) or on the top app bar's title, which truncates +// to one line and was the only place a long title showed up before this. +fun wrapReaderHtml(bodyContent: String, title: String? = null): String = """ - $bodyContent + ${if (!title.isNullOrBlank()) "

${escapeHtml(title)}

" else ""}$bodyContent """.trimIndent() -- cgit v1.2.3