[PR #16] [MERGED] fix: prevent streaming size jump from setext heading flicker #18

Closed
opened 2026-06-05 15:22:54 -04:00 by yindo · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/Mintplex-Labs/anythingllm-embed/pull/16
Author: @gitofinho
Created: 5/7/2026
Status: Merged
Merged: 5/11/2026
Merged by: @timothycarambat

Base: mainHead: fix/streaming-setext-flicker


📝 Commits (2)

  • 98de5ba fix: prevent streaming size jump from setext heading flicker
  • a99117a remove useless tests

📊 Changes

1 file changed (+12 additions, -2 deletions)

View changed files

📝 src/utils/chat/markdown.js (+12 -2)

📄 Description

Refs Mintplex-Labs/anything-llm#5588

What

Disables markdown-it's setext-heading rule (lheading) in the embed widget's chat renderer.

- .disable("list");
+ .disable(["list", "lheading"]);

Why

When the chat assistant streams a response, lines visibly jump in size for one frame whenever the model emits a hyphen-prefixed line (text\n-). The line above briefly renders as an <h2> heading and reverts to a paragraph as soon as more content arrives. In responses with bullet lists this happens per bullet, so the chat text appears to repeatedly grow and shrink during streaming.

Root cause: lheading (setext) interprets text\n-… as a heading the moment a single - lands on the next line; the next chunk reverts it. Token-by-token re-render via dangerouslySetInnerHTML (PromptReply/index.jsx) paints each transient frame. The widget's tailwind.config.js corePlugins.preflight = false setting amplifies the visual jump because browser-default heading sizes leak through.

Reproduction (current behavior on main):

const md = require('markdown-it')({ html: false, typographer: true }).disable('list');

md.render('Title\n')        // → <p>Title</p>
md.render('Title\n-')       // → <h2>Title</h2>            ← jump
md.render('Title\n- item')  // → <p>Title\n- item</p>      ← revert

md.render('연수 일정\n-')      // → <h2>연수 일정</h2>          ← Korean responses
md.render('연수 일정\n- 4/15') // → <p>연수 일정\n- 4/15</p>     ← revert

After the patch, Title\n- stays a paragraph until the rest of the bullet line arrives — no flip, no flicker.

Behavior change scope

Limited to setext headings only:

  • Title\n=== no longer renders as <h1> (renders as paragraph + literal ===).
  • Title\n--- no longer renders as <h2> (renders as paragraph + horizontal rule once the third dash lands — same end state as a blank-line-separated ---).
  • ATX (# Header, ## Section, …) is unchanged.

In practice, current LLM outputs we have sampled use ATX headings exclusively — we have not observed setext output from any major model.

Tests

Added scripts/streaming-render-spec.mjs plus a test:render script to package.json. The spec asserts:

  1. Title\n- does NOT render as <h2> (setext flicker)
  2. Title\n--- does NOT render as <h2>
  3. Title\n=== does NOT render as <h1>
  4. # Header still renders as <h1>Header</h1> (ATX must keep working)

Plain Node, no new devDependencies. Run with yarn test:render (or node scripts/streaming-render-spec.mjs). The spec also includes a small drift sentinel that bails out if the production renderer's .disable(...) call no longer matches what the spec mirrors, so the two stay in sync.

I went with this form because the repo currently has no vitest/jest setup, and bundling a full test framework into a 1-line bug fix felt heavier than appropriate. Happy to convert this to a proper vitest test (option a) or drop it entirely (option c) if you'd rather — I left option flags in #5588 and either path is a quick follow-up.

Risk / rollout

  • 1-line behavior change in src/utils/chat/markdown.js.
  • No effect on existing chat UI, CSS, or build output beyond the rendered HTML for setext input — and no LLM in our sample emits that.
  • If preserving setext is still desired, happy to switch to a config-flag escape hatch instead.

Closes Mintplex-Labs/anything-llm#5588 once merged (cross-repo close — manual close may be needed).


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/Mintplex-Labs/anythingllm-embed/pull/16 **Author:** [@gitofinho](https://github.com/gitofinho) **Created:** 5/7/2026 **Status:** ✅ Merged **Merged:** 5/11/2026 **Merged by:** [@timothycarambat](https://github.com/timothycarambat) **Base:** `main` ← **Head:** `fix/streaming-setext-flicker` --- ### 📝 Commits (2) - [`98de5ba`](https://github.com/Mintplex-Labs/anythingllm-embed/commit/98de5ba884acc231819085c23d14111f9ab89515) fix: prevent streaming size jump from setext heading flicker - [`a99117a`](https://github.com/Mintplex-Labs/anythingllm-embed/commit/a99117a3b5c46436160b5b25dafc26ce7baa3423) remove useless tests ### 📊 Changes **1 file changed** (+12 additions, -2 deletions) <details> <summary>View changed files</summary> 📝 `src/utils/chat/markdown.js` (+12 -2) </details> ### 📄 Description Refs Mintplex-Labs/anything-llm#5588 ## What Disables `markdown-it`'s setext-heading rule (`lheading`) in the embed widget's chat renderer. ```diff - .disable("list"); + .disable(["list", "lheading"]); ``` ## Why When the chat assistant streams a response, lines visibly **jump in size for one frame** whenever the model emits a hyphen-prefixed line (`text\n-`). The line above briefly renders as an `<h2>` heading and reverts to a paragraph as soon as more content arrives. In responses with bullet lists this happens *per bullet*, so the chat text appears to repeatedly grow and shrink during streaming. Root cause: `lheading` (setext) interprets `text\n-…` as a heading the moment a single `-` lands on the next line; the next chunk reverts it. Token-by-token re-render via `dangerouslySetInnerHTML` (`PromptReply/index.jsx`) paints each transient frame. The widget's `tailwind.config.js` `corePlugins.preflight = false` setting amplifies the visual jump because browser-default heading sizes leak through. Reproduction (current behavior on `main`): ```js const md = require('markdown-it')({ html: false, typographer: true }).disable('list'); md.render('Title\n') // → <p>Title</p> md.render('Title\n-') // → <h2>Title</h2> ← jump md.render('Title\n- item') // → <p>Title\n- item</p> ← revert md.render('연수 일정\n-') // → <h2>연수 일정</h2> ← Korean responses md.render('연수 일정\n- 4/15') // → <p>연수 일정\n- 4/15</p> ← revert ``` After the patch, `Title\n-` stays a paragraph until the rest of the bullet line arrives — no flip, no flicker. ## Behavior change scope Limited to setext headings only: - `Title\n===` no longer renders as `<h1>` (renders as paragraph + literal `===`). - `Title\n---` no longer renders as `<h2>` (renders as paragraph + horizontal rule once the third dash lands — same end state as a blank-line-separated `---`). - ATX (`# Header`, `## Section`, …) is **unchanged**. In practice, current LLM outputs we have sampled use ATX headings exclusively — we have not observed setext output from any major model. ## Tests Added `scripts/streaming-render-spec.mjs` plus a `test:render` script to `package.json`. The spec asserts: 1. `Title\n-` does NOT render as `<h2>` (setext flicker) 2. `Title\n---` does NOT render as `<h2>` 3. `Title\n===` does NOT render as `<h1>` 4. `# Header` still renders as `<h1>Header</h1>` (ATX must keep working) Plain Node, no new devDependencies. Run with `yarn test:render` (or `node scripts/streaming-render-spec.mjs`). The spec also includes a small drift sentinel that bails out if the production renderer's `.disable(...)` call no longer matches what the spec mirrors, so the two stay in sync. I went with this form because the repo currently has no `vitest`/`jest` setup, and bundling a full test framework into a 1-line bug fix felt heavier than appropriate. Happy to convert this to a proper `vitest` test (option a) or drop it entirely (option c) if you'd rather — I left option flags in #5588 and either path is a quick follow-up. ## Risk / rollout - 1-line behavior change in `src/utils/chat/markdown.js`. - No effect on existing chat UI, CSS, or build output beyond the rendered HTML for setext input — and no LLM in our sample emits that. - If preserving setext is still desired, happy to switch to a config-flag escape hatch instead. Closes Mintplex-Labs/anything-llm#5588 once merged (cross-repo close — manual close may be needed). --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
yindo added the pull-request label 2026-06-05 15:22:54 -04:00
yindo closed this issue 2026-06-05 15:22:54 -04:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Mintplex-Labs/anythingllm-embed#18