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 the author 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.

Separately, the branch author reports rendering the report at 380px as well as 900px and taking the quiz to a full CLEARED, and reports the same end-to-end pass through a local explorer. I confirmed the 900px case and the grading mechanism; I did not exercise a narrow viewport, and the 380px claim is theirs, not mine.

Deviations and open risks

The author's 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 I dropped all of it rather than repeat it here under the author's authority. What survived reconciliation 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 the agent-steward Claimed by a Nicon PR steward label Aug 13, 2026
@sfc-gh-nbellante

Copy link
Copy Markdown
Contributor Author

This head is reviewer-green and ready to read. e795ac5

An independent review by openai-gpt-5.5 (Codex CLI, read-only, xhigh reasoning) returned 🟢 APPROVE with no findings — it confirmed the .html dispatch lands before generic code rendering, that is_supported_document_path has no stale callers, and that the new test covers the classification without changing .txt behaviour. I then booted the branch and drove the changed view rather than taking that on faith: the one real .html artifact in the live wiki (pull-requests/16385/report.html, 1 of 129 documents) renders in a single st.iframe at exactly 900px with a 35.7KB srcdoc; its embedded quiz graded 3 / 4 live inside the sandbox with per-answer feedback, which is precisely what the old source-rendering made impossible; "View source" opens to the raw <!DOCTYPE html>; and a .py artifact still renders as code with zero iframes on the page. make check is clean (ruff, format, ty, mypy) and pytest is 26/26. GitHub's check job is SUCCESS, and the PR is MERGEABLE / CLEAN against main. I rewrote the description from the diff, and dropped most of the author's notes as belonging to the sibling change rather than this one.

State: open draft at e795ac5, one check green, description and verification current for this exact SHA. Two things are true of the diff and called out in the body rather than fixed: the iframe height is a hard-coded 900px instead of measured from content, and the .github/pr-assets/ screenshot links are branch-relative raw URLs that will 404 once the branch is deleted. Neither is a defect the reviewer flagged; both are judgement calls that belong to you.

Waiting on you: nothing except the read-and-decide. It is still a draft and I will not undraft or merge it — those are yours. I keep watching for new commits, checks, and comments.

@sfc-gh-nbellante sfc-gh-nbellante added the agent-ready A Nicon PR steward found this head reviewer-green label Aug 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent-ready A Nicon PR steward found this head reviewer-green agent-steward Claimed by a Nicon PR steward

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant