Skip to content

feat(reader): render Markdown in a WebView with the desktop app's renderer - #62

Merged
usamaiqb merged 2 commits into
mainfrom
feat/webview-reader
Oct 23, 2026
Merged

usamaiqb merged 2 commits into
mainfrom
feat/webview-reader

Conversation

@usamaiqb

Copy link
Copy Markdown
Owner

The reader now renders Markdown in a WebView, using the same renderer as MarkReader Desktop, in place of Markwon. Its built files are vendored into app/src/main/assets/renderer/.

  • The page gets the document's Markdown source and renders it there. Source code files are wrapped in a code block, so they get real highlight.js grammars, and the Prism-based SourceCodeRenderer is gone.
  • The space under the toolbar is page padding, so the bar no longer leaves an empty strip when it collapses.
  • Reader preferences carry over through ReaderRendererSettings. Two gaps remain: there is no sepia or amoled theme, and the bundled fonts don't reach the page yet.
  • Fallback: a WebView too old to run the renderer (e.g. Chromium 52 on API 24) used to show a blank page. Now the reader shows the document source with a warning naming the WebView version. The check is whether the page ever reports it is ready, so it catches every cause, not one version number. The logic lives in ViewerChoices as pure functions with tests.

Not yet: search and the outline are hidden while the renderer draws the page, and raw mode keeps both. The editor preview still uses Markwon.

Closes #17, closes #29, closes #30

The viewer's rendered path was Markwon building spans; it is now the
document renderer shared with the desktop app, running in a WebView.

`ReaderContent` gains `Markdown(source)` in place of `Html`. The renderer
is JavaScript inside the page, so Kotlin never holds rendered markup and
could only produce it by growing a second Markdown implementation -- what
crosses the bridge is source, and the page renders it. Source code reaches
the same variant by being fenced into a code block, which is what gives it
real highlight.js grammars: the Prism approximations that stood in for
rust, ruby and toml are gone, and the standalone Prism renderer with them.
That fencing belongs in the renderer rather than here, and says so.

The document is decoded once. The host could read it itself, but the
viewer already had, so a text-carrying `showDocument` sits beside the
path-based one.

Editor previews keep Markwon through a temporary `loadEditorPreview`,
since that pane is still a TextView.

Chrome inset crosses into the page as document padding rather than being
applied to the WebView from outside: the chrome collapses on scroll, and
an outer inset leaves a dead band behind when it goes.

Search and the table of contents index text this side holds, so both are
hidden while a document is drawn by the renderer. Raw mode keeps them.
Rebuilding them in the page is planned.

Reader preferences map across, with two gaps recorded rather than papered
over: the renderer has no sepia or amoled palette, and the bundled fonts
live in resources the WebView cannot load.
…ot start

A WebView too old to run the shared renderer showed a blank page: no
error, no timeout, nothing to act on. Confirmed on an API 24 emulator,
whose WebView is Chromium 52 -- below the engine our own modules need,
since they use optional chaining throughout.

The reader now falls back to the document source with a warning naming
the WebView version. Raw mode already exists and needs no WebView, which
is why it is the fallback rather than a second native renderer kept alive
for the purpose; keeping one would mean Markwon could never be deleted.

Detection is behavioural rather than a version comparison. The page
reports ready only once its whole module graph has resolved and executed,
so the absence of that covers every cause at once -- an engine too old for
the syntax, an asset that failed to load, a policy that refused a module
-- and does not go stale when the renderer adopts newer syntax. It will:
the floor moved to optional chaining without anyone noticing. The version
is read only to make the message specific, never to decide anything.

The mode chip said "Rendered mode" while showing source, which is the
same untruth the format-name chip already guards against, and the
raw/rendered toggle offered a switch between two identical views.

The three decisions behind all of this move out of the composable into
pure functions, because their deciding input is unreachable without an
old WebView: left inline they would be verified once and then rot.
@usamaiqb usamaiqb self-assigned this Oct 22, 2026
@usamaiqb
usamaiqb merged commit aafc4c1 into main Oct 23, 2026
3 checks passed
@usamaiqb
usamaiqb deleted the feat/webview-reader branch October 23, 2026 10:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add wrap support to tables Feature Request: Add support for Mermaid Diagrams RTL support

1 participant