fix(remend): guard incomplete HTML tag handler against math blocks - #617
Open
torsello wants to merge 1 commit into
Open
fix(remend): guard incomplete HTML tag handler against math blocks#617torsello wants to merge 1 commit into
torsello wants to merge 1 commit into
Conversation
Contributor
|
@torsello is attempting to deploy a commit to the Vercel Team on Vercel. A member of the Team first needs to authorize it. |
torsello
force-pushed
the
fix/html-tag-math-guard
branch
from
September 10, 2026 12:40
73c27f6 to
72a16f3
Compare
`handleIncompleteHtmlTag` checked `isInsideCodeBlock` but never the
`isWithinMathBlock` guard the emphasis handlers already use. An ordinary
comparison inside math -- `$$ I = \sum_{j<k} p_j $$` -- therefore matched
the incomplete-tag pattern and deleted everything from the `<` to the end
of the string. The katex handler then auto-closed the orphaned `$$`, so
KaTeX received truncated input and rendered a parse error in muted text
while the rest of the message never reached the DOM.
Since the pattern is leftmost-matching and always runs to the end of the
string, every later `<` that starts a tag name is an equally valid
candidate. The handler now walks forward past candidates that sit inside
code or math rather than bailing out, so a genuine incomplete tag that
follows a math expression is still stripped.
Refs vercel#616
torsello
force-pushed
the
fix/html-tag-math-guard
branch
from
September 10, 2026 13:04
72a16f3 to
dd1ddec
Compare
lofcz
added a commit
to lofcz/streamdown-ng
that referenced
this pull request
Sep 11, 2026
Port vercel/streamdown#617 by @torsello: skip < candidates inside math blocks and walk forward to later candidates instead of bailing out, so a comparison like \sum_{j<k} no longer swallows the message tail while genuine incomplete tags are still stripped.
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.
Description
handleIncompleteHtmlTagguarded against code blocks but never against math, so an ordinarycomparison inside a math expression matched the incomplete-tag pattern and deleted everything
from the
<to the end of the string.Because the
katexhandler (priority 70) runs afterhtmlTags(priority 10), the now-orphaned$$was auto-closed, so KaTeX received unterminated input and rendered aParseErrorin--color-muted-foreground. The net effect for the reader is a message that silently endsmid-formula in grey text, with no error surfaced anywhere.
remendalready implements and exportsisWithinMathBlock, and the emphasis handlers alreadyuse it —
htmlTagswas simply never covered.Type of Change
Related Issues
Related to #616
Deliberately not
Fixes— this covers the math half of that issue. See Additional Notes.Changes Made
Added an
isWithinMathBlockguard tohandleIncompleteHtmlTag, gated behind the samehasMathDelimitersfast path the emphasis handlers use, so theO(n)scan is skippedentirely for text with no math delimiters.
Changed the handler to walk forward through candidates instead of bailing out on the
first guarded one. This is the non-obvious half.
incompleteHtmlTagPatternisleftmost-matching and its
[^>]*$tail means the match always runs to the end of thestring — so every later
<that starts a tag name is an equally valid candidate. Simplyreturning
textwhen the first match falls inside math would trade one bug for another:The same walk-forward now applies to code blocks, which fixes a smaller unreported case
on its own: a
<inside a fenced block no longer masks a real incomplete tag after it.Added 4 test cases to
packages/remend/__tests__/html-tags.test.ts.Added a patch changeset for
remend.Testing
New cases cover block
$$, inline$, both LaTeX delimiter forms (\(…\)and\[…\]), andthe walk-forward behaviour. I confirmed all four fail on
mainbefore the fix and pass after —the walk-forward case in particular fails against a guard-only implementation too.
Manual verification against the exact reproductions from the issue:
Intro\n\n$$\nI = \sum_{j<k} p_j\n$$\n\nTAIL\sum_{jinline $$A_{j<k}$$ more\n\nTAILinline $$A_{j$$Hello <divHelloHello(unchanged behaviour)Test Coverage
remend: 493 tests across 26 files, all passing.Full monorepo suite: 1172 tests across 85 files, 6/6 turbo tasks green.
biome checkreports no findings on both touched files.Note:
tsc --noEmitreports twoTS18048errors inpackages/remend/src/strikethrough-handler.ts.I verified these are present on
mainand unrelated to this change.Screenshots/Demos
Not applicable — no visual surface changes. The before/after is shown as code above.
Checklist
pnpm changeset)Documentation left unchecked: this is a behavioural fix with no public API change, so there is
nothing in the docs to update. Happy to add a note to the termination docs if you'd like one.
Changeset
.changeset/html-tag-math-guard.md—patchbump forremend.Additional Notes
What this PR intentionally leaves out. #616 raises a second, broader problem: the handler
also fires on text that is already complete, so ordinary prose like
the loop runs while i<nstill loses its remainder. That one is not a missing guard — it needs an explicit signal,
because
remend(text)currently cannot distinguish a streaming tail from settled text, andevery tail-oriented heuristic keeps firing after the stream ends.
The issue suggests an option or a
modeargument mirroringStreamdown'smode="static".That is a public API decision affecting more than this handler, so I did not want to make it
unilaterally in a first contribution. Happy to open a follow-up if you want to shape the API —
or to fold it into this PR if you already know which direction you'd prefer.