Render HTML wiki artifacts in an iframe - #87
Draft
sfc-gh-nbellante wants to merge 3 commits into
Draft
Conversation
Self-contained HTML artifacts (e.g. visual PR reports) were listed by the agent wiki explorer but fell through to the code branch, so they rendered as syntax-highlighted source with any scripts inert. Add `is_html` alongside the existing `is_markdown` / `is_image` flags and dispatch it to `components.v1.html`, which renders in a sandboxed iframe: the artifact's own stylesheet cannot leak into the app chrome, and its scripts still run. A "View source" expander keeps the old behaviour available. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
The agent wiki explorer lists every file it finds in the
streamlit/streamlit.wikiclone —fetch_wiki_documents()walks the repo withrglob("*")and filters nothing by extension. So self-contained HTML artifacts, like the visual PR reports theexplaining-pull-requestsskill generates, have always shown up in the file picker. They just fell through to the generic code branch and rendered as syntax-highlighted source. A report meant to be read as a page arrived as a wall of markup, and anything it scripted — an interactive quiz, tab switching — was inert. Linking someone to a report was pointless: they got the source of the report instead of the report.What / How
Three code files, +49/−1 across 6 changed files (the other three are PNGs, see below).
app/utils/agent_wiki.py(+9) adds the classification, following the shapeis_markdown/is_imagealready use: anHTML_EXTENSIONS = {".html", ".htm"}set, anis_html_path()predicate keyed off the existing lowercased-extension helper, and anis_htmlfield on theWikiDocumentTypedDict populated inbuild_wiki_documents(). Nothing else consumes the new field, andis_supported_document_path()is untouched — this PR does not change which files get listed, only how one kind of them renders.app/agent_wiki_explorer.py(+23/−1) does the rendering..htmlis removed fromCODE_LANGUAGE_BY_EXTENSION, and a new_render_html_asset()branch is inserted into the dispatch chain before the code branch — order matters, because leaving.htmlin that map would have kept the old behaviour winning. The renderer fetches the artifact through the samefetch_wiki_document_text()path the code branch uses, so error and empty-body handling stay consistent with its neighbours, then hands the text tostreamlit.components.v1.html(...)at a fixedHTML_ASSET_IFRAME_HEIGHT = 900.The choice worth understanding is
components.htmloverst.htmlorst.markdown(unsafe_allow_html=True): it renders into a sandboxed iframe. That buys two things at once — the artifact's own stylesheet cannot leak out into the app chrome, and its<script>tags still execute, which is what makes an embedded quiz work at all. The cost is a fixed-height viewport, since an iframe cannot size itself to its content from the outside; 900px is a deliberate pick so a report's first screen is visible, withscrolling=Truehandling the rest. A "View source" expander below the frame keeps the old syntax-highlighted view one click away rather than removing it.tests/test_agent_wiki.py(+17) covers the classification as a pure function: areport.htmlpath is flaggedis_htmland not markdown or image, and a siblingnotes.txtin the same folder is not flagged — the assertion that guards against the new branch swallowing plain-text artifacts. The rendering itself is not unit-tested;components.htmlneeds a running script context, so that behaviour is covered by the manual pass below instead.Also on the branch: three PNGs committed under
.github/pr-assets/, showing the same artifact before and after. They are linked below as raw URLs against thefeature/render-html-wiki-artifactsbranch ref, so those links will break once the branch is deleted after merge.main)Verified
I booted this branch's code and drove it. Preview at
http://localhost:4310/, all observations ate795ac5:/agent_wiki_explorer?file=pull-requests/16385/report.html, the one real.htmlfile currently in the live wiki (1 of 129 documents). One<iframe title="st.iframe">on the page with a 35,751-charactersrcdocandoffsetHeightexactly 900. Inside it:document.title= "PR 16385 — text_input types", an<h1>, and 2 scripts. The report renders on its own white card while the surrounding app is in dark mode — visible proof the sandbox is holding its styling in and the app's out.qa1–qa4) and clicked Submit inside the frame. It graded live:.scoreread 3 / 4, the stamp flipped to "Reread and retry", and 5 option rows picked up correctness classes with per-answer explanations rendered. This is the behaviour the old source-rendering made impossible.st.codeblock beginning<!DOCTYPE html> <html lang="en">, syntax-highlighted./agent_wiki_explorer?file=issues/12065/repro_app.py: zero iframes on the page, still rendered as code.Recorded earlier on this branch and not re-run for this revision: the report rendering at 380px as well as 900px, the quiz taken through to a full CLEARED, and the same end-to-end pass through a local explorer. The 900px case and the grading mechanism are confirmed above; a narrow viewport was not exercised here.
Deviations and open risks
The working notes for this branch are mostly about the sibling change that produces these reports — a proto-field-vs-enum-value detection widening, an
e2e_appsregex exclusion,finalize.pyword budgets, three helper scripts. None of that appears in this diff, so none of it is repeated here. What does carry over is the dependency direction: this PR is the consumer, and it is what makes a report link in the sibling PR resolve to a rendered report instead of source.Two things genuinely left open, both visible in the diff:
.github/pr-assets/screenshot links are branch-relative raw URLs and will 404 after the branch is deleted.Testing
make check— clean: ruff lint passed,ruff format --checkreported 65 files already formatted,typassed, mypy found no issues in 57 source files.uv run pytest— 26 passed, including the 8 intests/test_agent_wiki.py.checkjob — SUCCESS one795ac5; PR isMERGEABLE/CLEANagainstmain..htmextension specifically (the classification set covers it, but no.htmfile exists in the live wiki to open).