Emit heading prefixes, skip decorative HTML and demo previews in markdown stream - #6054
Merged
Merged
Conversation
…o previews Closes findings 1/2/3 from zauberzeug#6007 with a single shared helper. Introduce `website/design.py:override_markdown(element, markdown)` — a thin wrapper that replaces an element's `Accept: text/markdown` rendering with the given string and returns the element. Used here to: 1. Emit `#` / `##` heading prefixes for `section_heading` and `subheading` so docs pages expose their structure in the markdown stream (finding 1). 2. Zero out decorative chrome in `subheading` and `phosphor_icon` — `<div id=...>` anchor targets, `<i class="ph-...">` Phosphor icons, and empty `[](#...)` anchor links no longer leak into markdown (finding 3). 3. Skip the `browser_window` demo placeholder, whose lazy preview never hydrates server-side, so `localhost:8080` / `loading.gif` stop appearing in the markdown stream (finding 2). Per zauberzeug#6007 (Falko's reply), this collapses the previous five private subclasses (`_MarkdownH1`, `_MarkdownH2Link`, `_MarkdownH2Label`, `_DecorativeHtml`, `_DecorativeLink`) and an inline instance-level monkey-patch into one helper. Type-ignore and pylint-disable suppressions live inside the helper only. Adds `test_instance_level_render_markdown_override` to lock in the contract the helper depends on: `_render_markdown` can be replaced per instance without touching class state. Phase 2 of zauberzeug#5889; supersedes fork-only PRs #140, #141, #142. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bound the TypeVar to ui.element directly (dropping the nicegui.element import), move it next to its only use, trim docstrings, and chain .classes()/.style() inside the override_markdown() calls.
The docs left-drawer lazy-loads its navigation tree behind an intersection observer; the `/static/loading.gif` placeholder is visible server-side, so it leaked `` before the content on every docs page. Wrap it with override_markdown(..., '').
falkoschindler
approved these changes
Jun 5, 2026
falkoschindler
left a comment
Contributor
There was a problem hiding this comment.
Approving. The override_markdown consolidation is the right shape — one shared helper instead of five private subclasses + a monkey-patch, with both the type: ignore and pylint: disable confined to it.
Verified the markdown stream live (Accept: text/markdown on /documentation/button and /documentation/section_text_elements):
- Heading hierarchy ✓ — H1 (
# ui.*button*) fromsection_heading, H2s fromsubheadingin both branches (## Icons,## [Label](/documentation/label)). - Demo previews suppressed ✓ — no
localhost:8080chrome, no demo spinner; the demo code block still survives viapython_window. - Decorative HTML gone ✓ — no raw
<i class="ph-…">, anchor<div>s, or empty[](#anchor)icon-links in the content.
A couple of small things I folded in on top while verifying:
- Helper tidy-up — bound the
TypeVartoui.elementdirectly (drops thenicegui.elementimport) and chained.classes()/.style()inside theoverride_markdown(...)calls so thewithlines read as "build element, then wrap". - One more placeholder leak — the docs left-drawer lazy-loads its nav tree behind an intersection observer, and its
/static/loading.gifplaceholder was leakingbefore the content on every docs page. Same family as thebrowser_windowcase, so wrapped it withoverride_markdown(..., '')inmain.py. Now 0 occurrences in the stream.
The remaining header/nav and copy-button noise is genuinely separate scope (copy buttons being what #6052 cleans up). 👍
falkoschindler
enabled auto-merge
June 5, 2026 18:07
falkoschindler
added a commit
to evnchn/nicegui
that referenced
this pull request
Jun 8, 2026
Re-add the `.strip()` dropped in b62aef4 so a whitespace-only child falls back to `[Button]` instead of `[Button: ]`, and document why the guard only surfaces a single plain line of child content. Add a test pinning that a decorative child whose markdown is suppressed (mirroring the website's `override_markdown(..., '')` from zauberzeug#6054) leaves the button rendering as `[Button]`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
pull Bot
pushed a commit
to codingwatching/nicegui
that referenced
this pull request
Jun 8, 2026
…g#6052) ### Motivation Phase 2 polish for zauberzeug#5889 (markdown content negotiation). `Button._render_markdown` only inspected the `label` and `icon` props, so a button that carried its meaning through an `aria-label` or a child element — e.g. `ui.button().props('aria-label="Save"')`, or a button wrapping a `ui.label` — collapsed to a bare `[Button]`, dropping information a reading agent could have used. This PR surfaces that information, while **keeping `[Button]` as the representation for genuinely empty / decorative buttons** (such as the copy buttons whose only child is a phosphor icon). Dropping the `[Button]` token itself for decorative buttons was considered (per [zauberzeug#6007 finding 4](zauberzeug#6007)) but not done here: the placeholder is a useful structural signal in the stream, and zauberzeug#6054 already stops the decorative child markup from leaking. ### Implementation `nicegui/elements/button.py:_render_markdown()` now resolves, in order: 1. `label` prop → `[Button: <label>]` (existing) 2. `aria-label` prop → `[Button: <aria-label>]` (new) 3. `icon` prop → `[Button: icon:<icon>]` (existing) 4. A single plain line of child markdown (no newlines, no `[`/`]`) → `[Button: <child>]` (new) 5. Otherwise → `[Button]` (unchanged) The child fallback is deliberately conservative: multi-child, bracketed (e.g. nested links), or empty/decorative children fall through to `[Button]` rather than garbling the `[Button: ...]` wrapper. Children whose markdown is suppressed via `override_markdown(..., '')` (zauberzeug#6054) render empty and are skipped, so a copy button stays `[Button]` without exposing raw `<i class="ph-...">` HTML. Tests in `tests/test_markdown_response.py` cover the `aria-label` priority (below `label`, above `icon`), single-line child content, and children that fall back to `[Button]` (a nested-link child, a multi-child button, and a whitespace-only child). ### Progress - [x] The PR title is a short phrase starting with a verb like "Add ...", "Fix ...", "Update ...", "Remove ...", etc. - [x] The implementation is complete. - [x] This PR does not address a security issue. - [x] Pytests have been added/updated. - [x] Documentation is not necessary (agent-facing rendering change). - [x] No breaking changes to the public API — empty `ui.button()` still renders `[Button]`; only buttons carrying an `aria-label` or a single-line child gain a label. No Python API signature changes. --------- Co-authored-by: evnchn <evnchn@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Falko Schindler <falko@zauberzeug.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.
Drafted by @evnchn with Claude Code (Opus 4.7); diff reviewed before pushing. Implements the structural pattern (
override_markdownhelper) Falko proposed in the #6007 triage.Motivation
Phase 2 polish for #5889 (markdown content negotiation, merged 2026-04-24). Closes findings 1–3 from discussion #6007:
*Text* Elements, section H2s as plain links. Agents reading the page had no structural cues.browser_windowrenderedlocalhost:8080+because the lazy preview never hydrates server-side.<i class="ph-...">icons,<div id="...">anchor targets, and empty[](#anchor)link icons appeared as raw HTML / empty links in the stream.Per the approved triage, the original three triage PRs each introduced private subclasses + per-call-site monkey-patches. Falko's suggestion was a single shared helper. This PR is that consolidation.
Implementation
Introduces
website/design.py:override_markdown(element, markdown):_E = TypeVar('_E', bound=Element)preserves call-site element types so chained.classes()/.style()still type-check. Both suppressions live inside the helper only.Applied in three places:
section_heading()— wraps the titleui.markdownwithf'# {title_}'.subheading()— wraps the anchorui.html(<div id=...>)and the icon-onlyui.link(target=#...)with''; wraps the actual subheadingui.link/ui.labelwithf'## [{text}]({link})'orf'## {text}'.phosphor_icon()— wraps itsui.html(<i class="ph-...">)with''.browser_window()inwebsite/documentation/windows.py— wraps the whole window with''(the chrome and lazy preview are irrelevant to a markdown reader).This collapses what would otherwise be five private subclasses (
_MarkdownH1,_MarkdownH2Link,_MarkdownH2Label,_DecorativeHtml,_DecorativeLink) and one inline instance-patch into a single shared helper.Scope / location decision
Per Falko's suggestion, the helper stays in
website/design.pyrather than being promoted tonicegui.helpers— it's a site-rendering concern, not a public API contract, until a second user-facing case appears.Test
Adds
test_instance_level_render_markdown_overridetotests/test_markdown_response.py, locking in the underlying contract the helper depends on:_render_markdowncan be replaced per instance without touching class state.Composes with #6052
This PR is the companion referenced in #6052's body. #6052 alone surfaces
[Button: <i class="ph-duotone ph-copy"></i>]for copy buttons becauseui.html._render_markdown()returns the raw content string and the button now recurses into children. With this PR applied,phosphor_icon()returns an element wrapped withoverride_markdown(..., ''), so the child renders to'', the button's_children_to_markdown().strip()is empty, and the button falls through to''. Copy-button noise collapses to zero. Either PR can land first; both are needed for the full effect.Local verification
Same diff is also open at evnchn#150 for fork-side CI verification.
Progress
test_instance_level_render_markdown_override).markdown=Trueopt-in introduced in AddAccept: text/markdowncontent negotiation for NiceGUI pages #5889 is unchanged).Accept: text/markdownresponses on the docs site is affected.