Skip to content

Render HTML wiki artifacts in an iframe - #87

Draft
sfc-gh-nbellante wants to merge 3 commits into
mainfrom
feature/render-html-wiki-artifacts
Draft

Render HTML wiki artifacts in an iframe#87
sfc-gh-nbellante wants to merge 3 commits into
mainfrom
feature/render-html-wiki-artifacts

Conversation

@sfc-gh-nbellante

@sfc-gh-nbellante sfc-gh-nbellante commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Why

The agent wiki explorer lists every file it finds in the streamlit/streamlit.wiki clone — fetch_wiki_documents() walks the repo with rglob("*") and filters nothing by extension. So self-contained HTML artifacts, like the visual PR reports the explaining-pull-requests skill 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 shape is_markdown / is_image already use: an HTML_EXTENSIONS = {".html", ".htm"} set, an is_html_path() predicate keyed off the existing lowercased-extension helper, and an is_html field on the WikiDocument TypedDict populated in build_wiki_documents(). Nothing else consumes the new field, and is_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. .html is removed from CODE_LANGUAGE_BY_EXTENSION, and a new _render_html_asset() branch is inserted into the dispatch chain before the code branch — order matters, because leaving .html in that map would have kept the old behaviour winning. The renderer fetches the artifact through the same fetch_wiki_document_text() path the code branch uses, so error and empty-body handling stay consistent with its neighbours, then hands the text to streamlit.components.v1.html(...) at a fixed HTML_ASSET_IFRAME_HEIGHT = 900.

The choice worth understanding is components.html over st.html or st.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, with scrolling=True handling 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: a report.html path is flagged is_html and not markdown or image, and a sibling notes.txt in 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.html needs 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 the feature/render-html-wiki-artifacts branch ref, so those links will break once the branch is deleted after merge.

Verified

I booted this branch's code and drove it. Preview at http://localhost:4310/, all observations at e795ac5:

  • HTML artifact renders as a page — opened /agent_wiki_explorer?file=pull-requests/16385/report.html, the one real .html file currently in the live wiki (1 of 129 documents). One <iframe title="st.iframe"> on the page with a 35,751-character srcdoc and offsetHeight exactly 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.
  • Embedded scripts execute — answered all four quiz questions (radio groups qa1qa4) and clicked Submit inside the frame. It graded live: .score read 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.
  • View source still works — the expander starts closed, and opening it renders one st.code block beginning <!DOCTYPE html> <html lang="en">, syntax-highlighted.
  • No regression for other assets — opened /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_apps regex exclusion, finalize.py word 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:

  • The iframe height is a hard-coded 900px, not measured from content. Short reports get whitespace below them; long ones scroll inside the frame.
  • The .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 --check reported 65 files already formatted, ty passed, mypy found no issues in 57 source files.
  • uv run pytest — 26 passed, including the 8 in tests/test_agent_wiki.py.
  • GitHub's check job — SUCCESS on e795ac5; PR is MERGEABLE / CLEAN against main.
  • Not verified: rendering at narrow viewports, and the .htm extension specifically (the classification set covers it, but no .htm file exists in the live wiki to open).

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>
@sfc-gh-nbellante sfc-gh-nbellante added agent-steward Claimed for PR upkeep; do not merge or undraft agent-ready Checks green and description current; ready to read labels Aug 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent-ready Checks green and description current; ready to read agent-steward Claimed for PR upkeep; do not merge or undraft

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant