fix(markdown): de-indent over-indented fence bodies under numeric lists - #231
Open
detail-app[bot] wants to merge 1 commit into
Open
detail-app[bot] wants to merge 1 commit into
detail-app[bot] wants to merge 1 commit into
Conversation
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.
Detail bug report: View on Detail
Bug
Fenced code blocks nested inside ordered (numeric) list items rendered with spurious leading spaces on every body line when the fence was indented a few columns past the list item's continuation column. For a
1.item (continuation column 3), a fence at indent 4, 5, or 6 renderedcodeTextas" npm install\n"," npm install\n"," npm install\n"instead of"npm install\n". The corruption window scaled with marker width (12.→ 5–7,100.→ 6–8) and affected both streaming and non-streaming renders identically.Root cause: In
nestNumericListFences(numericListFenceNesting.ts), the fence opener/closer are re-indented to the list's continuation column (targetIndentation), but the body shift was computed asMath.max(0, continuation − openerIndent). For over-indented openers this clamps to0, so the body keeps its original larger indent while the opener is pulled left — andmarked, which strips the opener's (now smaller) indent from each content line, leavesopenerIndent − continuationextra spaces on every sufficiently-indented body line.Fix
Apply the signed shift consistently to the opener and every body line, clipped at each body line's own leading-space count. In
numericListFenceNesting.ts:bodyShift = continuation − openerIndentat fence-open time, split intobodyIndentation(rightward prepend, for the existing under-indentedD ≥ 0case) and a newbodyDeindentation = max(0, −bodyShift)(leftward strip, for the over-indentedD < 0case).min(bodyDeindentation, leadingSpaceCount(line))leading spaces from each line before prependingbodyIndentation, so the body moves by the same signed delta as the opener and the non-whitespace suffix is preserved. The closing-fence branch already re-indented totargetIndentationregardless, so it was already consistent.This establishes the invariant: for a body line with
kleading spaces and opener deltaD = targetIndentation − openerIndent, the emitted line hasmax(0, k + D)leading spaces.Testing
numericListFenceNesting.test.tsandmarkdown.test.tssuites pass (12 + 49 tests). Added regression tests covering the over-indented bug window (indent 4–6 under1.), multi-digit markers (12./100.at continuation+1..+3), a ragged body (opener/body at indent 4 with a 0-indent line in between rendered aslineA\nlineB\n), and column-zero/under-indented cases (indent 0–3) to guard against reintroducing the fence-escapes-list failure the preprocessor exists to fix. Extended the existing mixed-indentation test to assert exacttextContent(it previously only checked structure, so the over-indented sub-case was silently corrupted). All assertions hold for bothisStreaming=falseandisStreaming=true, confirming streaming/non-streaming parity.svelte-checktypecheck, oxlint (incl.--type-aware), eslint, ast-grep, andoxfmtformat check all pass with zero errors/warnings; the vite production build succeeds.playwright install --with-deps, then ran the existinganswer-accessibility.spec.ts(renders the chat-answer DOM throughparseMarkdownagainst a stubbed SSE stream, runs WCAG A/AA scans). 4/4 tests pass — the renderedol > li pre > codestructure is still well-formed.OPENAI_API_KEY(Gateway embeddings require OPENAI_API_KEY to be configured,EmbeddingConfig.java:76), which isn't present in this environment. The Playwright e2e and vitest streaming-parity assertions cover the same production render path this smoke targets.Closes #218
Automatic Fixes PRs can be configured here.