Skip to content

fix(web): disable indented code blocks in markdown rendering#395

Merged
tiann merged 3 commits intotiann:mainfrom
hqhq1025:fix/markdown-indented-code-block
Apr 5, 2026
Merged

fix(web): disable indented code blocks in markdown rendering#395
tiann merged 3 commits intotiann:mainfrom
hqhq1025:fix/markdown-indented-code-block

Conversation

@hqhq1025
Copy link
Copy Markdown
Collaborator

@hqhq1025 hqhq1025 commented Apr 4, 2026

Problem

LLM responses with indented content (e.g. numbered list items with nested text, quoted documents) render as large code blocks instead of formatted markdown.

Root cause

CommonMark treats text indented by 4+ spaces as a code block (codeIndented). LLM output frequently contains:

  • Numbered list items with nested paragraphs
  • Quoted/referenced text with extra indentation
  • Task descriptions indented for readability

The markdown parser correctly follows the spec, but the result is unusable — entire sections become unformatted code blocks.

Fix

Add a remark plugin that disables the codeIndented tokenizer in micromark. Fenced code blocks (```) continue to work normally. This is a common practice for chat/LLM UIs where indentation is decorative, not semantic.

Changes

  • web/src/lib/remark-disable-indented-code.ts — new remark plugin
  • web/src/components/assistant-ui/markdown-text.tsx — add plugin to MARKDOWN_PLUGINS

Test plan

  • All 104 existing tests pass
  • Verify: LLM responses with indented lists render as formatted markdown, not code blocks
  • Verify: fenced code blocks (```) still render correctly

hqhq1025 and others added 2 commits April 4, 2026 07:43
In CommonMark, text indented by 4+ spaces is treated as a code block.
LLM responses frequently have indented content inside numbered lists or
quoted text, causing large chunks to render as a single code block
instead of formatted markdown.

Add a remark plugin that disables the codeIndented tokenizer. Fenced
code blocks (``` … ```) continue to work normally.

via [HAPI](https://hapi.run)

Co-Authored-By: HAPI <noreply@hapi.run>
Use `as any` cast for the unified processor `this` context instead of
an explicit type annotation that conflicts with the Processor type.

via [HAPI](https://hapi.run)

Co-Authored-By: HAPI <noreply@hapi.run>
Copy link
Copy Markdown

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

Findings

  • [Major] The new remark plugin relies on an implicit-any this, which conflicts with the repo's strict TypeScript config and should fail typechecking once dependencies are installed. Evidence web/src/lib/remark-disable-indented-code.ts:11, repo config tsconfig.base.json:7.
    Suggested fix:
    type RemarkData = {
        micromarkExtensions?: Array<{ disable: { null: string[] } }>
    }
    
    export default function remarkDisableIndentedCode(this: { data(): RemarkData }) {
        const data = this.data()
        const extensions = data.micromarkExtensions ?? (data.micromarkExtensions = [])
        extensions.push({ disable: { null: ['codeIndented'] } })
    }

Summary
Review mode: initial
1 major finding on the latest diff. I could not run bun-based checks in this automation environment, but this issue follows directly from the repo's strict TypeScript settings and the added plugin implementation.

Testing

  • Not run (automation environment does not have bun)
  • Residual gap: no markdown rendering regression test found for indented text vs fenced code

HAPI Bot

*/
export default function remarkDisableIndentedCode() {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const self = this as any
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[MAJOR] const self = this as any still uses an implicit-any this before the cast, so this should trip the repo's strict TypeScript settings (tsconfig.base.json:7).

Suggested fix:

type RemarkData = {
    micromarkExtensions?: Array<{ disable: { null: string[] } }>
}

export default function remarkDisableIndentedCode(this: { data(): RemarkData }) {
    const data = this.data()
    const extensions = data.micromarkExtensions ?? (data.micromarkExtensions = [])
    extensions.push({ disable: { null: ['codeIndented'] } })
}

The previous `as any` cast on `this` still triggers noImplicitThis in
strict mode. Annotate the parameter as `this: unknown` and cast to
the required shape inside the function body. Also use the key-based
`data(key, value)` API instead of mutating the returned object.

via [HAPI](https://hapi.run)

Co-Authored-By: HAPI <noreply@hapi.run>
Copy link
Copy Markdown

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

Findings

  • None on the added/modified lines in the latest head.

Summary
Review mode: follow-up after new commits
No actionable issues found in the latest diff. Residual risk: I did not find a web regression test covering indented list/quoted text rendering versus fenced code rendering, so future markdown/parser changes could regress this behavior without detection.

Testing

  • Not run (automation)
  • Not found in repo/docs: a web markdown rendering regression test for indented text versus fenced code

HAPI Bot

@tiann tiann merged commit 29b87d9 into tiann:main Apr 5, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants