Skip to content

fix(chat): don't apply hard-break newlines to AI-streamed messages#385

Open
FelipeRamos-neuro wants to merge 1 commit into
plmbr:mainfrom
FelipeRamos-neuro:fix/chat-code-block-trailing-whitespace
Open

fix(chat): don't apply hard-break newlines to AI-streamed messages#385
FelipeRamos-neuro wants to merge 1 commit into
plmbr:mainfrom
FelipeRamos-neuro:fix/chat-code-block-trailing-whitespace

Conversation

@FelipeRamos-neuro

Copy link
Copy Markdown

Summary

Fixes #384

  • The chat message renderer (src/chat-sidebar.tsx:891) applied a blanket .replace(/\n/gi, ' \n') hard-break transform to every rendered message, not just user input as the adjacent comment intended.
  • Since this is a raw string transform applied before markdown parsing, it injected trailing double-spaces into every line of AI-streamed fenced code blocks — invisible on screen, but present in the DOM text, corrupting copied code with trailing whitespace (breaking whitespace-sensitive languages like Python, or tripping lint/format diffs after paste).
  • Fix: scope the hard-break transform to msg.from === 'user' only, matching the comment's stated intent. AI-streamed messages already contain well-formed markdown and don't need the hack.

See #384 for the full root-cause writeup and reproduction.

Test plan

  • Full Jest suite passes (29 suites / 371 tests, no regressions)
  • Verified via isolated repro against the pinned react-markdown@9.0.1 + remark-gfm@4.0.0: AI-message code blocks no longer carry trailing whitespace on any line, while user-message hard breaks are preserved
  • Manual verification in a running JupyterLab chat panel (not done — no way to launch the dev UI in this environment)

🤖 Generated with Claude Code

The blanket `.replace(/\n/gi, '  \n')` in the chat message renderer ran
over every message, not just user input as the comment intended. Since
it's a raw string transform applied before markdown parsing, it injected
trailing double-spaces into every line of AI-streamed fenced code
blocks, corrupting copy-pasted code with invisible trailing whitespace.

Scope the hard-break transform to user messages only.
@pjdoland pjdoland added the enhancement New feature or request label Jul 9, 2026

@pjdoland pjdoland left a comment

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.

Thanks for this, and nice root-cause writeup in #384. The trailing-whitespace-in-code-blocks bug is real and worth fixing: injecting \n before markdown parsing does corrupt copied code.

I think the sender-based scoping trades one bug for another, though, because the real axis here is code-fence context rather than who sent the message:

  1. Regression for AI prose. MarkdownRenderer uses only remarkGfm (no remark-breaks), so once the hard-break transform no longer applies to AI messages, single newlines in AI output become CommonMark soft breaks and collapse to spaces. Plain-text AI output that separates lines with single newlines (not blank lines or list syntax) now renders run-on: First point\nSecond point shows as First point Second point on one line, where it used to be two. It is lower-frequency since most model output is well-formed markdown, but it is a real visible change, and it would not show up in the code-block repro.

  2. The same bug still hits user-pasted code. The .replace still runs on user messages, so if a user pastes a fenced code snippet, every line still gets trailing double-spaces. Scoping by sender only fixes the AI direction.

Both of these go away if the transform is markdown-aware instead of a pre-parse string replace. The cleanest fix is to drop the .replace entirely and add remark-breaks to remarkPlugins in markdown-renderer.tsx alongside remarkGfm. remark-breaks converts soft breaks to hard breaks at the AST level, so it respects code fences (no corruption for AI or user code) while preserving intended line breaks in all prose. That resolves the run-on regression and the user-code case in one move, and removes the string hack. It does add remark-breaks as a dependency, but it is a standard, tiny remark plugin.

Would you be up for switching to that approach? Happy to help if anything is unclear.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Chat markdown hard-break transform injects trailing whitespace into every line of code blocks

2 participants