Skip to content

spec: <details>/<summary> in markdown rendering (#10259)#10462

Open
lonexreb wants to merge 5 commits into
warpdotdev:masterfrom
lonexreb:spec/10259-markdown-details-summary
Open

spec: <details>/<summary> in markdown rendering (#10259)#10462
lonexreb wants to merge 5 commits into
warpdotdev:masterfrom
lonexreb:spec/10259-markdown-details-summary

Conversation

@lonexreb
Copy link
Copy Markdown
Contributor

@lonexreb lonexreb commented May 8, 2026

Spec-only PR. References #10259.

Summary

Adds support for standard HTML5 <details> and <summary> tags in Warp's markdown renderer, so collapsible disclosure widgets work in agent output, READMEs, and other rendered markdown surfaces. Today these tags are stripped or rendered as plain text.

Why

<details> / <summary> is part of GFM's de facto surface and standard HTML5. It is heavily used in long agent outputs and open-source READMEs to support progressive disclosure. Aligning Warp's renderer with this convention removes friction from a common rendering case.

Spec

See specs/GH10259/SPEC.md for the full behavior contract: collapsed-by-default unless open, click + keyboard toggle, nested-to-arbitrary-depth, inline-only <summary> content, full markdown in the body, sanitizer attribute allowlist (details:[open], summary:[]), accessibility (native or ARIA role=group + aria-expanded + aria-controls), and tests covering each.

Notes

  • No new settings, no new public API.
  • Sanitizer change is minimal and explicit — only these two tags and one attribute are added.
  • Persistence of open/closed state across re-renders is deferred to V1.5.

Labels

ready-to-spec

@cla-bot cla-bot Bot added the cla-signed label May 8, 2026
@github-actions github-actions Bot added the external-contributor Indicates that a PR has been opened by someone outside the Warp team. label May 8, 2026
@oz-for-oss
Copy link
Copy Markdown
Contributor

oz-for-oss Bot commented May 8, 2026

@lonexreb

I'm starting a first review of this spec-only pull request.

You can view the conversation on Warp.

I completed the review and no human review was requested for this pull request.

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

Copy link
Copy Markdown
Contributor

@oz-for-oss oz-for-oss Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overview

This spec proposes support for HTML5 <details> / <summary> in Warp markdown rendering with sanitizer, accessibility, interaction, and test requirements. The core behavior is scoped, but several requirements need tightening before implementation.

Concerns

  • The behavior contract does not define missing or misplaced <summary> handling, even though untrusted markdown commonly contains malformed HTML.
  • The implementation pointers reference markdown files that do not match this checkout's parser/rendering layout, which makes the spec difficult to implement as written.

Security

  • The arbitrary-depth nesting requirement should explicitly preserve existing parser/resource limits or require an iterative/guarded implementation so malicious deeply nested input cannot cause stack overflow or rendering denial of service.

Verdict

Found: 0 critical, 3 important, 0 suggestions

Request changes

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

Comment thread specs/GH10259/SPEC.md

- `<details>` — block-level container. May carry `open` attribute. No other
attributes preserved.
- `<summary>` — exactly one is the visible always-rendered header. Additional
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [IMPORTANT] Define behavior when <details> has no <summary> or the first <summary> is not the first child; otherwise malformed README/agent HTML can render inconsistently across parser and native/custom widget paths.

Comment thread specs/GH10259/SPEC.md Outdated

### B5. Nested `<details>`

- Supported to arbitrary depth.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [IMPORTANT] [SECURITY] arbitrary depth leaves untrusted markdown nesting unbounded; specify that implementation must respect existing parser/resource limits or use guarded iterative rendering to avoid stack overflow or rendering DoS.

Comment thread specs/GH10259/SPEC.md Outdated

## Implementation Pointers

- Parser config: `app/src/markdown/parser.rs` — extend the HTML allowlist
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [IMPORTANT] The referenced app/src/markdown/{parser,renderer,sanitize}.rs paths do not match this checkout's markdown pipeline (crates/markdown_parser/src/html_parser.rs, parser tests, and app render surfaces); update the pointers to the actual files implementers must change.

@lonexreb
Copy link
Copy Markdown
Contributor Author

lonexreb commented May 8, 2026

Round-1 fixes for oz-for-oss review: (1) added Malformed <details> Handling subsection (B7.1) — defines fallback for missing summary, summary-not-first, orphan summary, unclosed tags, nested summary, empty summary, block elements in summary; (2) added Resource Limits (B8.1) — 32-level nesting cap with plain-text fallback beyond, 1000-block soft cap, iterative or guarded-recursion implementation MUST avoid stack overflow, depth/count enforced at renderer; tests T12-T15 added; (3) Implementation Pointers verified against worktree — fictional paths replaced with real markdown parser/sanitizer/renderer module paths (crates/markdown_parser/src/{markdown_parser,html_parser,lib}.rs, crates/warpui_core/src/elements/formatted_text_element.rs) or explicitly marked net-new (crates/markdown_parser/src/details_limits.rs).

@lonexreb
Copy link
Copy Markdown
Contributor Author

lonexreb commented May 8, 2026

/oz-review

@oz-for-oss
Copy link
Copy Markdown
Contributor

oz-for-oss Bot commented May 8, 2026

@lonexreb

I'm re-reviewing this spec-only pull request in response to a review request.

You can view the conversation on Warp.

I completed the review and no human review was requested for this pull request.

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

Copy link
Copy Markdown
Contributor

@oz-for-oss oz-for-oss Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overview

Reviewed the spec-only PR for <details> / <summary> markdown rendering support. The spec covers the behavior, sanitizer scope, accessibility, malformed input, and test matrix, but two requirements conflict or weaken the intended resource protections.

Concerns

  • B5 promises arbitrary-depth nesting while B8.1 and T12 cap rendered disclosure widgets at 32 levels, creating conflicting implementation and test expectations.
  • The high-cardinality DoS guard is labeled a soft cap even though the following lines require deterministic fallback after the 1000th <details>.
  • The custom ARIA path should state that aria-controls IDs are renderer-generated after sanitization, not derived from input attributes.

Security

  • The resource-limit language should be deterministic because this feature renders untrusted recursive markup.

Verdict

Found: 1 critical, 1 important, 1 suggestions

Request changes

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

Comment thread specs/GH10259/SPEC.md Outdated

### B5. Nested `<details>`

- Supported to arbitrary depth.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚨 [CRITICAL] This conflicts with B8.1/T12, which cap rendered <details> nesting at 32 and render deeper levels as plain text. Replace the arbitrary-depth promise with the capped behavior so implementation and tests agree.

Suggested change
- Supported to arbitrary depth.
- Supported up to the B8.1 resource limit (32 rendered levels); deeper `<details>` fall back to plain text.

Comment thread specs/GH10259/SPEC.md Outdated
literal `<details>` and `<summary>` opening / closing tags become visible
text in the output, and any inner markdown body still renders normally
through the existing markdown pipeline (no content is silently dropped).
- **Maximum `<details>` count per rendered surface: 1000** (soft cap).
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [IMPORTANT] [SECURITY] Calling this a soft cap conflicts with the mandatory fallback below; make it a hard enforcement limit (or define the allowed soft behavior) so untrusted markdown cannot force unbounded disclosure rendering.

Suggested change
- **Maximum `<details>` count per rendered surface: 1000** (soft cap).
- **Maximum `<details>` count per rendered surface: 1000** (hard cap).

Comment thread specs/GH10259/SPEC.md Outdated
widget:
- `role="group"` on the disclosure container.
- `aria-expanded` on the summary toggle.
- `aria-controls` linking the summary to its body region.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 [SUGGESTION] [SECURITY] Specify that aria-controls targets use renderer-generated IDs after sanitization, never input-supplied id attributes; B8 drops input IDs, but the custom ARIA path still needs a safe linkage source.

@lonexreb
Copy link
Copy Markdown
Contributor Author

lonexreb commented May 8, 2026

Round-2 fixes: (1) CRITICAL resolved — removed 'arbitrary depth' wording; nesting cap is a hard 32-level limit with plain-text fallback for over-cap levels; B5/B8.1/T12 now match; (2) 1000-block cap is now hard cap (deterministic), not soft — A_hard_cap_1000 tests the 1001st renders as plain text exactly at the threshold; (3) aria-controls IDs are renderer-generated AFTER sanitization; sanitizer allowlist is <details> only open, <summary> empty; all input id/aria-*/event handlers are stripped; tests added; (4) all resource-limit language is now deterministic — no soft/best-effort/probabilistic wording; thresholds are exact.

@lonexreb
Copy link
Copy Markdown
Contributor Author

lonexreb commented May 8, 2026

/oz-review

@oz-for-oss
Copy link
Copy Markdown
Contributor

oz-for-oss Bot commented May 8, 2026

@lonexreb

I'm re-reviewing this spec-only pull request in response to a review request.

You can view the conversation on Warp.

I completed the review and no human review was requested for this pull request.

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

Copy link
Copy Markdown
Contributor

@oz-for-oss oz-for-oss Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overview

This spec adds markdown <details> / <summary> support with explicit sanitizer, accessibility, malformed-input, and deterministic resource-limit requirements.

Concerns

  • The spec includes conversation list previews in scope but does not define behavior for non-interactive or row-click preview surfaces.
  • The custom-widget accessibility contract does not require an activatable control role/focus target for the summary toggle.
  • The test matrix misses the multiple-<summary> sibling normalization promised by the behavior contract.

Verdict

Found: 0 critical, 2 important, 1 suggestions

Request changes

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

Comment thread specs/GH10259/SPEC.md

Warp's markdown renderer should support standard HTML5 `<details>` and
`<summary>` tags so collapsible disclosure widgets work in any rendered
markdown surface (agent output, README previews, conversation list previews).
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [IMPORTANT] This includes conversation list previews in scope, but the spec never defines behavior for non-interactive/truncated preview rows where the summary hit target can conflict with row navigation; add a surface-specific contract and tests, or exclude previews from V1.

Comment thread specs/GH10259/SPEC.md Outdated
layer supports them, OR an equivalent ARIA pattern when using a custom
widget:
- `role="group"` on the disclosure container.
- `aria-expanded` on the summary toggle.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [IMPORTANT] The custom ARIA path needs to specify the summary toggle's activatable control semantics, such as a focusable button or role=button; aria-expanded alone may not be announced or operated as a disclosure control.

Comment thread specs/GH10259/SPEC.md
expansion would re-introduce nested `<details>` after depth-32 is
bounded by the same depth counter; the run terminates and the over-cap
level renders as plain text.
- T15. Malformed input matrix (B7.1): one assertion per case —
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 [SUGGESTION] B1/B7.1 require additional <summary> siblings to be concatenated into the first summary, but this malformed-input matrix omits that case; add a test so implementations do not render extra summaries inconsistently.

@lonexreb
Copy link
Copy Markdown
Contributor Author

lonexreb commented May 8, 2026

Addressed all 3 round-N concerns:

1. Conversation list preview / non-interactive surface behavior. Added B9.1 'Surface-mode rendering (interactive vs non-interactive)' as the authoritative contract. Two modes: Interactive (default — agent bodies, READMEs, changelogs, login notifications) gets full B3/B4/B10 disclosure behavior. NonInteractivePreview (conversation list row preview, future similar surfaces) renders <details> with body collapsed and inert: summary as plain inline text, no chevron, not focusable, no role=button, no aria-expanded/aria-controls, no body in the DOM, open attribute ignored, click routes to the row's existing handler. Mode is selected at the call site via a DetailsRenderMode parameter, not heuristics. B8.1 resource limits still apply unchanged.

2. Custom-widget activatable role / focus target. Reworked B10 with explicit MUST requirements for the custom-widget path: role="button" on summary (the activatable role); tabindex="0" (custom path is not implicitly focusable); aria-expanded updated on every toggle; aria-controls to body region's renderer-generated id; Enter and Space activation handlers (Space MUST preventDefault to avoid scrolling); visible focus ring on the summary itself. Native-element path inherits these from the browser/native widget and the custom requirements do not apply.

3. Tests for multi-<summary> sibling normalization. Added T_multi_summary_concat (verifies B1+B7.1 concatenation: 2-sibling and 5-sibling inputs fold to one summary with single-space separators, inline markdown preserved, no siblings dropped) and T_summary_hoist_with_concat (combined hoist+concat: prefix and middle become body content, summaries fold in order). Also added T_non_interactive_mode (asserts the B9.1 inert-summary contract end-to-end including open ignored) and T_custom_widget_activatable_role (asserts the new B10 role/tabindex/keyboard/focus-ring requirements).

Commit: 0662da3

@lonexreb
Copy link
Copy Markdown
Contributor Author

lonexreb commented May 8, 2026

/oz-review

@oz-for-oss
Copy link
Copy Markdown
Contributor

oz-for-oss Bot commented May 8, 2026

@lonexreb

I'm re-reviewing this spec-only pull request in response to a review request.

You can view the conversation on Warp.

I completed the review and no human review was requested for this pull request.

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

Copy link
Copy Markdown
Contributor

@oz-for-oss oz-for-oss Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overview

This spec defines <details> / <summary> support for Warp markdown rendering, including sanitizer allowlists, accessibility behavior, malformed-input handling, non-interactive preview mode, and deterministic resource limits.

Concerns

  • The top-level goals still promise collapsible widgets across every markdown surface, which conflicts with the later non-interactive preview-mode contract.
  • The implementation pointers say no per-call renderer changes are expected, but B9.1 requires explicit mode selection and should identify the preview call sites that must opt into NonInteractivePreview.

Verdict

Found: 0 critical, 2 important, 0 suggestions

Request changes

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

Comment thread specs/GH10259/SPEC.md Outdated
Comment on lines +25 to +26
- Render `<details>` / `<summary>` as collapsible disclosure widgets across
every markdown surface the renderer is used in.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [IMPORTANT] This goal conflicts with B9.1, which makes conversation-list previews inert and non-collapsible. Qualify this to say interactive markdown surfaces render disclosure widgets, while preview surfaces render only inert summary text.

Comment thread specs/GH10259/SPEC.md Outdated
`warp-details-2`, …). When the native `<details>` / `<summary>` semantics
path is taken, the renderer SKIPS identifier generation:
`crates/warpui_core/src/elements/formatted_text_element.rs`.
- Renderer call sites (no per-call changes expected; listed so reviewers can
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [IMPORTANT] B9.1 requires explicit mode selection at render call sites, but this says no per-call changes are expected and omits the conversation-list preview call sites that must pass NonInteractivePreview. List those call sites and require the new option wiring before implementation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla-signed external-contributor Indicates that a PR has been opened by someone outside the Warp team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant