Skip to content

[feat] Complete incomplete markdown syntax during streaming#13939

Merged
lukasmasuch merged 9 commits intodevelopfrom
lukasmasuch/remend-markdown
Mar 17, 2026
Merged

[feat] Complete incomplete markdown syntax during streaming#13939
lukasmasuch merged 9 commits intodevelopfrom
lukasmasuch/remend-markdown

Conversation

@lukasmasuch
Copy link
Copy Markdown
Collaborator

@lukasmasuch lukasmasuch commented Feb 13, 2026

Describe your changes

  • Add internal _markdown method with unterminated_parsing parameter for streaming support
  • Add unterminated_parsing proto field to control frontend markdown completion behavior
  • Integrates the remend package to automatically close unclosed markdown syntax (e.g., **bold, `code, ```code blocks) while content is being streamed via st.write_stream
  • Updates write_stream to use _markdown with unterminated_parsing=True during streaming
  • Updates frontend to conditionally apply remend only when unterminatedParsing=true
  • Improves visual appearance during streaming by showing properly styled text instead of raw markdown syntax characters
  • Skips processing for labels, HTML content, and when unterminatedParsing is not set

Testing Plan

  • Unit Tests (Python) - Tests for _markdown with unterminated_parsing parameter
  • Unit Tests (Frontend) - Tests for unterminatedParsing conditional behavior
  • E2E Tests - Basic write_stream functionality (note: mid-stream state can't be reliably tested)

@lukasmasuch lukasmasuch added change:feature PR contains new feature or enhancement implementation impact:users PR changes affect end users labels Feb 13, 2026
Copilot AI review requested due to automatic review settings February 13, 2026 10:27
@lukasmasuch lukasmasuch added the impact:users PR changes affect end users label Feb 13, 2026
@snyk-io
Copy link
Copy Markdown
Contributor

snyk-io bot commented Feb 13, 2026

⚠️ Snyk checks are incomplete.

Status Scan Engine Critical High Medium Low Total (0)
⚠️ Open Source Security 0 0 0 0 See details
⚠️ Licenses 0 0 0 0 See details

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 13, 2026

✅ PR preview is ready!

Name Link
📦 Wheel file https://core-previews.s3-us-west-2.amazonaws.com/pr-13939/streamlit-1.55.0-py3-none-any.whl
📦 @streamlit/component-v2-lib Download from artifacts
🕹️ Preview app pr-13939.streamlit.app (☁️ Deploy here if not accessible)

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR integrates the remend package to automatically complete incomplete markdown syntax during streaming operations via st.write_stream. This improves the visual appearance by showing properly styled text instead of raw markdown characters while content is being streamed character-by-character.

Changes:

  • Added remend npm package (v1.2.0) dependency for markdown syntax completion
  • Integrated remend in StreamlitMarkdown component to auto-close unclosed markdown (bold, italic, code blocks, etc.) during rendering
  • Added comprehensive unit tests for remend integration covering both positive cases (completion) and negative cases (when NOT to apply)
  • Added E2E tests to verify the feature works end-to-end with actual streaming

Reviewed changes

Copilot reviewed 6 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
frontend/yarn.lock Added yarn lock entries for the new [email protected] dependency
frontend/lib/package.json Added remend package dependency
frontend/lib/src/components/shared/StreamlitMarkdown/StreamlitMarkdown.tsx Integrated remend to complete incomplete markdown syntax, skipping for labels and HTML content; updated useMemo dependencies
frontend/lib/src/components/shared/StreamlitMarkdown/StreamlitMarkdown.test.tsx Added comprehensive unit tests for remend integration including positive and negative test cases
e2e_playwright/st_write_stream.py Added test cases for streaming incomplete bold and code block markdown
e2e_playwright/st_write_stream_test.py Added E2E tests to verify incomplete markdown is properly styled during streaming
NOTICES Added Apache 2.0 license notice for the remend package from Vercel

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 13, 2026

✅ Bundle size change is within normal range

Metric This Branch develop Change (%)
Total (gzip) 7.85 MiB 7.85 MiB +0.04%
Entry (gzip) 97.23 KiB 97.23 KiB 0.00%

📊 View detailed bundle comparison

@lukasmasuch lukasmasuch marked this pull request as draft February 13, 2026 10:58
@lukasmasuch lukasmasuch added the update-snapshots Trigger snapshot autofix workflow label Feb 20, 2026
@github-actions github-actions bot removed the update-snapshots Trigger snapshot autofix workflow label Feb 20, 2026
@lukasmasuch lukasmasuch added the update-snapshots Trigger snapshot autofix workflow label Feb 20, 2026
@github-actions github-actions bot removed the update-snapshots Trigger snapshot autofix workflow label Feb 20, 2026
lukasmasuch pushed a commit that referenced this pull request Feb 20, 2026
## Describe your changes

Automated snapshot updates for #13939 created via the snapshot autofix
CI workflow.

This workflow was triggered by adding the `update-snapshots` label to a
PR after Playwright E2E tests failed with snapshot mismatches.

**Updated snapshots:** 12 file(s)

⚠️ **Please review the snapshot changes carefully** - they could mask
visual bugs if accepted blindly.

This PR targets a feature branch and can be merged without review
approval.

Co-authored-by: Streamlit Bot <[email protected]>
lukasmasuch and others added 6 commits March 16, 2026 15:10
Integrates the remend package to automatically close unclosed markdown
syntax (e.g., **bold, *italic, `code, ```code blocks) while content is
being streamed via st.write_stream. This improves the visual appearance
during streaming by showing properly styled text instead of raw markdown
syntax characters.
- Pass generator directly to write_stream instead of wrapping in lambda
  (lambda returns a generator but is not itself a generator function)
- Add negative assertions to verify raw markdown syntax is not visible
Underscores in identifiers (like Python repr strings `<bound method
Foo._bar>`) were incorrectly being treated as incomplete italic markdown
and getting extra underscores appended. This caused the st_components_v1
E2E test to fail.

Disabled italic completion in remend (both `*` and `_` styles) to prevent
this issue. Bold (`**`) completion still works for streaming scenarios.
Automated snapshot updates for #13939 created via the snapshot autofix
CI workflow.

This workflow was triggered by adding the `update-snapshots` label to a
PR after Playwright E2E tests failed with snapshot mismatches.

**Updated snapshots:** 12 file(s)

⚠️ **Please review the snapshot changes carefully** - they could mask
visual bugs if accepted blindly.

This PR targets a feature branch and can be merged without review
approval.

Co-authored-by: Streamlit Bot <[email protected]>
…pport

- Add internal `_markdown` method to avoid code duplication in markdown handling
- Add `unterminated_parsing` proto field to control remend markdown completion
- Update `write_stream` to use `_markdown` with `unterminated_parsing=True` during streaming
- Update frontend to conditionally apply remend only when `unterminatedParsing=true`
- Remove E2E tests for incomplete markdown (cannot reliably capture mid-stream state)
- Add Python and frontend unit tests for `_markdown` with `unterminated_parsing` parameter

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@lukasmasuch lukasmasuch force-pushed the lukasmasuch/remend-markdown branch from 69fc289 to 0a53b93 Compare March 16, 2026 14:10
@github-actions
Copy link
Copy Markdown
Contributor

Orphaned Snapshots Detected

This PR contains orphaned e2e snapshots that are no longer used by tests.

Orphaned snapshots by test:

  st_write_stream_test: 6 orphaned files

These are the snapshots I found that appear to be orphaned:

    "st_write_stream-incomplete_bold[chromium].png",
    "st_write_stream-incomplete_bold[firefox].png",
    "st_write_stream-incomplete_bold[webkit].png",
    "st_write_stream-incomplete_code_block[chromium].png",
    "st_write_stream-incomplete_code_block[firefox].png",
    "st_write_stream-incomplete_code_block[webkit].png",

To fix this:

  1. Run python scripts/snapshot_cleanup.py locally to clean them up
  2. Or review the snapshots manually to ensure they're actually orphaned
  3. If they're incorrectly flagged, copy the filenames above and add them to DISALLOWED_SNAPSHOTS in scripts/snapshot_cleanup.py
  4. Commit and push the changes

This helps keep our snapshot directory clean and our tests maintainable.

- Delete orphaned st_write_stream snapshots (incomplete_bold, incomplete_code_block)
  that no longer correspond to existing tests
- Revert st_table-10/21 snapshot changes that were accidentally included
  from a previous snapshot autofix run but are unrelated to this PR

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@lukasmasuch lukasmasuch marked this pull request as ready for review March 16, 2026 19:51
@lukasmasuch lukasmasuch added the ai-review If applied to PR or issue will run AI review workflow label Mar 16, 2026
@lukasmasuch
Copy link
Copy Markdown
Collaborator Author

@cursor review

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 9 out of 10 changed files in this pull request and generated 1 comment.

@github-actions github-actions bot removed the ai-review If applied to PR or issue will run AI review workflow label Mar 16, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Summary

This PR adds automatic completion of incomplete markdown syntax during st.write_stream streaming. When content is streamed token-by-token (e.g., from an LLM), unclosed markdown syntax like **bold or `code is automatically completed on the frontend using the remend library (v1.2.0, Apache 2.0, from Vercel/Streamdown), improving visual appearance during streaming.

Key changes:

  • Proto: New unterminated_parsing bool field (field 6) in Markdown.proto.
  • Backend: New internal _markdown() method on MarkdownMixin that accepts unterminated_parsing. The public markdown() method delegates to it. write_stream() calls _markdown(unterminated_parsing=True) during streaming; flush_stream_response() uses the regular markdown() call (without the flag) so the final output is unaffected.
  • Frontend: remend is applied conditionally in RenderedMarkdown when unterminatedParsing is true, skipping labels and HTML content. Italic completion is disabled to avoid false positives with underscores in Python identifiers.

Code Quality

All three reviewers agreed: the code is clean, well-structured, and follows Streamlit's established patterns.

Specific strengths noted across reviews:

  • The _markdown() internal method is a clean refactoring — the leading underscore correctly signals internal use, and the public markdown() delegates without duplicating logic.
  • The unterminatedParsing prop is properly threaded through the component hierarchy (Markdown.tsxStreamlitMarkdownRenderedMarkdown).
  • Guard conditions (unterminatedParsing && !isLabel && !allowHTML) are well-reasoned: labels are short/complete strings, and HTML content could interfere with remend's text processing.
  • The useMemo dependency array is correctly updated to include all relevant dependencies.
  • The decision to disable italic completion ({ italic: false }) is pragmatic and well-documented — Python repr strings like <bound method Foo._bar> would cause false positives.
  • _markdown() intentionally lacks @gather_metrics, so intermediate streaming updates are not individually counted — only the encompassing write_stream call is tracked.
  • The NOTICES file is properly updated with the Apache 2.0 license for remend.

No code quality issues identified by any reviewer.

Test Coverage

Python unit tests (lib/tests/streamlit/elements/markdown_test.py):

  • test_unterminated_parsing_sets_proto_field: Verifies _markdown(unterminated_parsing=True) sets the proto field.
  • test_unterminated_parsing_defaults_to_false: Verifies the default behavior.
  • Tests follow existing patterns using DeltaGeneratorTestCase.

Frontend unit tests (StreamlitMarkdown.test.tsx):

  • Parameterized positive tests for bold and code completion with unterminatedParsing=true.
  • Negative test for italic completion being disabled (underscore false-positive guard).
  • Parameterized negative tests covering all skip conditions: isLabel=true, allowHTML=true, unterminatedParsing=false, and unterminatedParsing not set.

E2E tests: No new E2E tests added. All reviewers agreed this is reasonable since mid-stream state cannot be reliably tested end-to-end.

Minor gap (noted by 2 of 3 reviewers): The existing test_st_write_stream_cursor test in write_test.py captures intermediate streaming deltas but doesn't verify the unterminated_parsing field. Adding an assertion for unterminated_parsing on intermediate deltas would strengthen coverage. Both gpt-5.3-codex-high and opus-4.6-thinking flagged this independently; all agreed it is non-blocking.

Backwards Compatibility

All three reviewers agreed: no breaking changes.

  • The protobuf field unterminated_parsing = 6 is additive and defaults to false.
  • The public st.markdown() API signature is unchanged; _markdown() is internal-only.
  • The unterminatedParsing prop defaults to undefined/false in the frontend.
  • The final output after streaming completes is rendered with the standard markdown() call, so end results are identical.

Security & Risk

All three reviewers agreed: low risk, no security concerns.

  • remend is Apache 2.0 licensed from Vercel's Streamdown project (reputable source). It's a pure text-processing function — no network requests, filesystem access, or code execution. Pinned to v1.2.0 with checksum in yarn.lock.
  • No XSS risk: remend only adds closing markdown syntax tokens; output is still processed through ReactMarkdown with sanitization.
  • HTML content is explicitly excluded from remend processing (!allowHTML), mitigating interference with custom HTML rendering or sanitization.
  • No changes to WebSocket handling, authentication, file serving, CORS, CSP, or session management.

One reviewer (gpt-5.3-codex-high) noted that standard dependency vetting/supply-chain review should still apply to remend, which is reasonable general guidance.

External test recommendation

  • Recommend external_test: No (unanimous)
  • Triggered categories: None
  • Evidence: Changes are purely a rendering enhancement during markdown streaming. No transport, auth, routing, embedding, or cross-origin changes.
  • Confidence: High (unanimous)

Accessibility

All three reviewers agreed: no accessibility concerns. The changes only affect intermediate visual representation during streaming. One reviewer (opus-4.6-thinking) noted this is arguably an accessibility improvement — screen readers see properly styled text rather than raw markdown syntax characters during streaming.

Recommendations

  1. Add unterminated_parsing assertion to existing write_stream test (non-blocking, raised by 2/3 reviewers): The test_st_write_stream_cursor test in write_test.py could verify unterminated_parsing=True on intermediate deltas and False on final output.

  2. Consider lazy-loading remend (low-priority optimization, raised by 1/3 reviewers): Since remend is only needed during streaming, lazy-loading could reduce initial bundle size for non-streaming use cases. However, given remend is a small library, this is a minor optimization.

  3. Standard dependency vetting for remend (raised by 1/3 reviewers): Ensure license/security advisory monitoring for the new dependency.

Reviewer Consensus

Aspect gemini-3.1-pro gpt-5.3-codex-high opus-4.6-thinking
Verdict APPROVED APPROVED APPROVED
Code Quality Clean, well-structured Clean, well-scoped Well-structured, follows patterns
Test Coverage Adequate Good (minor gap) Adequate (minor gap)
Security No concerns No concerns (dep vetting) Low risk, no concerns
External Tests No No No
Accessibility No concerns No regressions Improvement for streaming

All three reviewers unanimously approved with no blocking issues. The only recommendations are minor, non-blocking test hardening and optimization suggestions.

Missing Reviews

All expected models completed their reviews:

  • gemini-3.1-pro — reviewed
  • gpt-5.3-codex-high — reviewed
  • opus-4.6-thinking — reviewed

Verdict

APPROVED: All three reviewers unanimously approved. The feature is well-implemented, follows Streamlit patterns, is backward-compatible, adequately tested, and introduces no security, accessibility, or regression risks. The only recommendations are non-blocking minor test improvements.


This is a consolidated AI review by opus-4.6-thinking.


📋 Review by `gemini-3.1-pro`

Summary

This PR introduces support for completing incomplete markdown syntax (like **bold or `code) while content is being streamed via st.write_stream. It adds an internal _markdown method with an unterminated_parsing parameter, updates the protobuf definition, and integrates the remend package in the frontend to handle the markdown completion.

Code Quality

The code changes are well-structured and clean. The extraction of the internal _markdown method in lib/streamlit/elements/markdown.py is a good approach to avoid exposing the unterminated_parsing parameter in the public API. The frontend integration of remend in frontend/lib/src/components/shared/StreamlitMarkdown/StreamlitMarkdown.tsx is appropriately conditional, ensuring it only runs when needed and avoiding interference with HTML or labels.

Test Coverage

  • Python Unit Tests: Added tests in lib/tests/streamlit/elements/markdown_test.py to verify that the unterminated_parsing parameter correctly sets the protobuf field and defaults to False.
  • Frontend Unit Tests: Added comprehensive tests in frontend/lib/src/components/shared/StreamlitMarkdown/StreamlitMarkdown.test.tsx to verify the conditional application of remend and its behavior with different markdown elements.
  • E2E Tests: The PR description notes that mid-stream state cannot be reliably tested end-to-end, which is a reasonable limitation. Existing write_stream E2E tests should cover the final output.

Backwards Compatibility

The changes are fully backwards compatible. The public st.markdown API remains unchanged, and the unterminated_parsing behavior is only enabled internally during streaming via st.write_stream.

Security & Risk

No significant security or regression risks identified. The remend library is only applied to the markdown string before parsing, and HTML content is explicitly excluded from this processing (!allowHTML), which mitigates potential interference with custom HTML rendering or sanitization.

External test recommendation

  • Recommend external_test: No
  • Triggered categories: None
  • Evidence:
    • The changes only affect markdown string preprocessing during streaming and do not touch routing, auth, websockets, embedding, or cross-origin behavior.
  • Suggested external_test focus areas: N/A
  • Confidence: High
  • Assumptions and gaps: None

Accessibility

No accessibility concerns. The changes only affect the intermediate visual representation of markdown during streaming, which improves the user experience by reducing raw syntax flicker.

Recommendations

No specific recommendations for improvement. The implementation is solid and well-tested.

Verdict

APPROVED: The changes are well-implemented, safely scoped to streaming contexts, and adequately tested.


This is an automated AI review by gemini-3.1-pro.

📋 Review by `gpt-5.3-codex-high`

Summary

This PR adds streaming-specific markdown completion for unterminated syntax (via remend) so partially streamed text renders more cleanly while st.write_stream is in progress. It introduces a new internal backend path (_markdown + proto flag), wires the flag through frontend markdown rendering, and adds focused backend/frontend unit tests.

Code Quality

The implementation is clean and well-scoped:

  • Backend changes are minimal and isolated: st.markdown() behavior is preserved while st.write_stream uses the new internal _markdown path for intermediate frames (lib/streamlit/elements/markdown.py, lib/streamlit/elements/write.py).
  • Frontend logic is explicitly gated so remend only runs for streaming mode and only when allowHTML is false and the content is not a label (frontend/lib/src/components/shared/StreamlitMarkdown/StreamlitMarkdown.tsx).
  • Proto changes are backward-safe (new optional field) and naming is consistent across Python/proto/TS layers (proto/streamlit/proto/Markdown.proto).

No merge-blocking code quality issues found.

Test Coverage

Coverage is generally good for the new behavior:

  • Frontend tests verify positive completion cases plus key negative gates (isLabel, allowHTML, and missing/false unterminatedParsing) in frontend/lib/src/components/shared/StreamlitMarkdown/StreamlitMarkdown.test.tsx (around lines 1186-1245).
  • Backend tests verify proto field wiring and default behavior in lib/tests/streamlit/elements/markdown_test.py (around lines 31-45).

Minor gap:

  • There is no direct backend test that exercises write_stream and asserts the intermediate markdown delta sets unterminated_parsing=True while final flush behavior remains unchanged.

Backwards Compatibility

Backwards compatibility looks good:

  • The proto field is additive (bool unterminated_parsing = 6) and defaults to false for older senders/receivers.
  • Public st.markdown API surface is unchanged; the new _markdown method is internal.
  • Non-streaming rendering path is unaffected unless the new flag is explicitly set.

Security & Risk

No high-risk security concerns identified in this diff:

  • No changes to auth/session/cookies/routes/websocket handshake logic.
  • No new dynamic code execution patterns (eval, exec, Function, subprocess, etc.).
  • Markdown completion is disabled when allowHTML=true, reducing interference with existing HTML-handling paths (frontend/lib/src/components/shared/StreamlitMarkdown/StreamlitMarkdown.tsx, around lines 1129-1137).

Risk note:

  • A new frontend dependency (remend) is introduced (frontend/lib/package.json, frontend/yarn.lock). This is expected for the feature, but standard dependency vetting still applies.

External test recommendation

  • Recommend external_test: No
  • Triggered categories: None
  • Key evidence:
    • lib/streamlit/elements/write.py (around lines 253-256): only sets a markdown-rendering flag during write_stream.
    • proto/streamlit/proto/Markdown.proto (line 41): additive markdown element field; no transport/auth/routing policy changes.
    • frontend/lib/src/components/shared/StreamlitMarkdown/StreamlitMarkdown.tsx (around lines 1129-1137): purely client-side markdown preprocessing gate.
  • Suggested external test focus areas: N/A required. Optional sanity check in hosted/proxied environments could confirm streamed markdown visual parity.
  • Confidence: High
  • Assumptions/gaps: Assessment is based on changed files in this PR only; no hidden runtime/config changes outside this diff.

Accessibility

No accessibility regressions identified:

  • No new interactive controls or focus-management changes.
  • Rendering changes are text-formatting behavior only, and existing semantic markdown output remains intact.

Recommendations

  1. Add one backend write_stream unit test that validates intermediate deltas set unterminated_parsing=True and final flush output remains unchanged.
  2. Keep dependency risk checks for remend in normal supply-chain review (license/security advisory monitoring).

Verdict

APPROVED: The feature is well-implemented, backward-compatible, and adequately tested for its primary behavior, with only minor non-blocking test hardening opportunities.


This is an automated AI review by gpt-5.3-codex-high. Please verify the feedback and use your judgment.

📋 Review by `opus-4.6-thinking`

Summary

This PR adds automatic completion of incomplete markdown syntax during st.write_stream streaming. When content is being streamed token-by-token (e.g., from an LLM), unclosed markdown syntax like **bold or `code is now automatically completed on the frontend, improving visual appearance during streaming.

Key changes:

  • Proto: New unterminated_parsing bool field (field 6) in Markdown.proto.
  • Backend: New internal _markdown() method on MarkdownMixin that accepts unterminated_parsing. The public markdown() method delegates to it. write_stream() now calls _markdown(unterminated_parsing=True) during streaming.
  • Frontend: New remend dependency (v1.2.0, Apache 2.0, from Vercel/Streamdown) applied conditionally in RenderedMarkdown when unterminatedParsing is true, skipping labels and HTML content. Italic completion is disabled to avoid false positives with underscores in Python identifiers.

Code Quality

The code is well-structured and follows Streamlit's established patterns:

  • The _markdown() internal method is a clean refactoring. The leading underscore correctly signals internal use, and the public markdown() method now delegates to it without duplicating logic (lib/streamlit/elements/markdown.py:40-66).
  • The unterminatedParsing prop is properly threaded through the component hierarchy: Markdown.tsxStreamlitMarkdownRenderedMarkdown.
  • The useMemo dependency array in RenderedMarkdown is correctly updated to include [source, isLabel, allowHTML, unterminatedParsing] (StreamlitMarkdown.tsx:1140).
  • The guard conditions (unterminatedParsing && !isLabel && !allowHTML) in StreamlitMarkdown.tsx:1132 are well-reasoned: labels are short/complete strings, and HTML content could interfere with remend's text processing.
  • The decision to disable italic completion ({ italic: false }) is pragmatic and well-documented in comments — Python repr strings like <bound method Foo._bar> would otherwise cause false positives.
  • Good design: flush_stream_response() in write.py:185 uses the regular stream_container.markdown() (without unterminated_parsing), ensuring the final rendered content is the true markdown without any fixup applied.
  • Metrics tracking: _markdown() intentionally does not have @gather_metrics, so intermediate streaming updates are not individually counted — only the encompassing write_stream call is tracked. This is appropriate.
  • The NOTICES file is updated with the Apache 2.0 license for remend.

No issues found with code quality.

Test Coverage

Python unit tests (lib/tests/streamlit/elements/markdown_test.py):

  • test_unterminated_parsing_sets_proto_field: Verifies _markdown(unterminated_parsing=True) sets the proto field.
  • test_unterminated_parsing_defaults_to_false: Verifies the default behavior.
  • These tests follow the pattern of existing tests in the file and use DeltaGeneratorTestCase appropriately.

Frontend unit tests (StreamlitMarkdown.test.tsx):

  • Parameterized positive tests for bold and code completion with unterminatedParsing=true.
  • Negative test for italic completion being disabled (the underscore false-positive guard).
  • Parameterized negative tests covering all skip conditions: isLabel=true, allowHTML=true, unterminatedParsing=false, and unterminatedParsing not set.
  • Good use of both positive assertions (toBeVisible, tagName check) and negative assertions (toBeNull for elements that should not appear).

E2E tests: No new E2E tests were added. The PR description notes that mid-stream state can't be reliably tested in E2E. The existing st_write_stream_test.py tests verify the final output after streaming completes, which remains unchanged.

Minor observation: The existing test_st_write_stream_cursor test in write_test.py captures intermediate streaming deltas but doesn't verify the unterminated_parsing field. While the test still passes (it only checks body), adding an assertion for unterminated_parsing in intermediate deltas could strengthen coverage. However, this is a minor suggestion, not a blocker.

Overall, the test coverage is adequate for the scope of the changes.

Backwards Compatibility

No breaking changes:

  • The protobuf field unterminated_parsing = 6 is a new optional bool field defaulting to false. Existing messages without this field are handled correctly.
  • The public st.markdown() API signature is unchanged; the _markdown() method is internal-only (underscore prefix, not exposed in st namespace).
  • The unterminatedParsing prop defaults to undefined/false in the frontend, so existing Markdown elements render identically.
  • The only behavioral change is during st.write_stream streaming, where intermediate markdown updates now have unterminated_parsing=True. The final output (after streaming completes) is rendered with the standard markdown() call, so the end result is identical.

Security & Risk

Low risk. The changes are purely a rendering enhancement:

  • remend package: Apache 2.0 licensed, from Vercel's Streamdown project (a reputable source). It's a pure text-processing function that completes incomplete markdown syntax — no network requests, filesystem access, or code execution. The yarn.lock pins it to version 1.2.0 with a specific checksum.
  • No XSS risk: remend only adds closing markdown syntax tokens (e.g., **, `). It doesn't inject HTML or execute JavaScript. The output is still processed through the existing markdown rendering pipeline (ReactMarkdown with sanitization).
  • No security-sensitive areas touched: No changes to WebSocket handling, authentication, file serving, CORS, CSP, or session management.
  • Error resilience: If remend were to throw on malformed input, the ErrorBoundary wrapping ReactMarkdown would catch the render failure. However, remend is designed to handle arbitrary markdown input gracefully.

External test recommendation

  • Recommend external_test: No
  • Triggered categories: None
  • Evidence:
    • proto/streamlit/proto/Markdown.proto: New optional bool field — no transport/handshake changes.
    • lib/streamlit/elements/write.py: Uses _markdown instead of markdown for streaming updates — internal rendering change only.
    • frontend/lib/src/components/shared/StreamlitMarkdown/StreamlitMarkdown.tsx: Text processing addition via remend — no cross-origin, embedding, or asset serving changes.
  • Suggested external_test focus areas: None — this is a pure rendering enhancement with no embedding, auth, routing, or cross-origin impact.
  • Confidence: High
  • Assumptions and gaps: Assumes remend is a pure text-processing function with no side effects (verified via source inspection and documentation).

Accessibility

No accessibility impact. The changes affect only the text content passed to the existing markdown renderer during streaming. No new interactive elements, ARIA attributes, or focus management changes are introduced. The rendered markdown structure remains the same — remend only ensures that during streaming, incomplete syntax (like **bold) is properly closed so that screen readers and visual rendering see styled text rather than raw markdown syntax characters. This is arguably an accessibility improvement for streaming content.

Recommendations

  1. Consider adding unterminated_parsing assertion to existing write_stream test: The test_st_write_stream_cursor test in write_test.py:610-625 captures intermediate streaming deltas. Adding assert el.markdown.unterminated_parsing is True for intermediate deltas and verifying it's False for the final output would strengthen the test coverage for the end-to-end streaming flow.

  2. Consider lazy-loading remend: The remend import is currently at the top level of StreamlitMarkdown.tsx. Since remend is only needed during streaming (when unterminatedParsing is true), lazy-loading it similar to other optional plugins (katex, raw, emoji) could reduce initial bundle size for non-streaming use cases. However, given that remend is a small library, this is a low-priority optimization.

Verdict

APPROVED: Clean, well-structured feature that improves streaming UX by completing incomplete markdown syntax. The implementation follows Streamlit patterns, test coverage is adequate, and there are no security, compatibility, or accessibility concerns.


This is an automated AI review by opus-4.6-thinking.

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

This reverts the italic disable that was added to work around underscore
issues. Italic completion (*text*) is now enabled during streaming.

Also fixes a latent test bug where the allowHTML=true test case wasn't
waiting for the async plugin to load.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
text_alignment: TextAlignment = "left",
unterminated_parsing: bool = False,
) -> DeltaGenerator:
"""Internal markdown with streaming support via unterminated_parsing."""
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nit: This comment doesn't seem 100% aligned to the scope of this function, since not all instances of this will use unterminated_parsing.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Updated 👍

Address PR review comment: the docstring now accurately reflects that
this is a general internal method with extended options, not specifically
for streaming with unterminated_parsing.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@lukasmasuch lukasmasuch merged commit 649e3bc into develop Mar 17, 2026
40 of 42 checks passed
@lukasmasuch lukasmasuch deleted the lukasmasuch/remend-markdown branch March 17, 2026 18:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

change:feature PR contains new feature or enhancement implementation impact:users PR changes affect end users

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants