Skip to content

fix: YAML code blocks collapse newlines due to Prism token white-space (#1463)#1516

Merged
1 commit merged intonesquena:masterfrom
franksong2702:fix/1463-yaml-newline-collapse
May 3, 2026
Merged

fix: YAML code blocks collapse newlines due to Prism token white-space (#1463)#1516
1 commit merged intonesquena:masterfrom
franksong2702:fix/1463-yaml-newline-collapse

Conversation

@franksong2702
Copy link
Copy Markdown
Contributor

Thinking Path

  • Hermes WebUI renders markdown code blocks through a Prism.js syntax-highlighting pipeline
  • The reporter (YAML raw rendering still ignores line breaks after #1400 #1463) confirmed via DOM probes that textContent of YAML blocks preserves \n characters, ruling out the renderer pipeline
  • Plain code blocks and language-bash blocks render correctly — only language-yaml is affected
  • This points to Prism's YAML grammar, which wraps tokens in <span> elements where white-space defaults to normal, collapsing newlines into spaces
  • The fix is pure CSS: force white-space: pre on token spans inside YAML code blocks

What Changed

Added two CSS rules to static/style.css:

.msg-body pre code.language-yaml .token{white-space:pre !important;}
.preview-md pre code.language-yaml .token{white-space:pre !important;}

Covers both the chat message area (.msg-body) and the markdown preview panel (.preview-md).

Why It Matters

YAML is one of the most common code-block languages in this UI (config files, docker-compose, CI pipelines). Collapsed newlines make YAML blocks unreadable — the indentation structure that YAML depends on is lost.

Verification

  • Reporter's probe 1 confirmed \n present in DOM textContent → CSS fix is the correct layer
  • Reporter's probe 2 confirmed plain code blocks and language-bash work → scoped to YAML token processing only
  • The selector .language-yaml .token is specific enough to avoid side effects on other languages
  • !important is necessary because Prism's bundled themes set white-space on token elements

Risks / Follow-ups

  • Risk: None. The selector is scoped to language-yaml only; other languages are unaffected.
  • Follow-up: If Prism updates their YAML grammar to fix the underlying tokenizer issue, this rule becomes a harmless no-op.

Model Used

  • xiaomi/mimo-v2.5-pro (via Xiaomi MiMo)

nesquena#1463)

Prism's YAML grammar wraps tokens in <span> elements where white-space
defaults to normal, collapsing \n characters into spaces. The DOM
textContent is correct (confirmed by reporter's probe), so the bug is
purely CSS.

Force white-space:pre on .token elements inside language-yaml code
blocks for both .msg-body and .preview-md contexts.
@nesquena-hermes
Copy link
Copy Markdown
Collaborator

Right layer for the fix. The reporter's probes on #1463 closed the diagnosis loop:

  • pre.textContent retained \n characters → the renderer pipeline is fine
  • Plain code blocks and language-bash rendered correctly → the bug is in Prism's YAML grammar specifically, not in the highlighter framework
  • That points to the YAML grammar wrapping tokens in <span> elements where white-space defaults to normal, collapsing newlines into spaces inside the highlighted token

A CSS override scoped to .language-yaml .token is the surgical fix. The two selectors covering both .msg-body (chat) and .preview-md (markdown preview) are the right scope — the ? panel and any other markdown surface should also be checked, but those two are the high-traffic ones.

A couple of review notes:

  1. !important is necessary here because Prism's bundled CSS sets white-space on .token directly (high specificity from the grammar's loaded stylesheet). Worth a one-line comment in the CSS pointing at the Prism rule it overrides, so future readers don't strip the !important thinking it's a smell.
  2. Other indentation-sensitive grammars — if language-python or language-haskell ever shows the same symptom, the same fix applies. Not blocking this PR, just flagging that a more general pre code .token{white-space:pre} might be the cleaner long-term shape if Prism is consistent across grammars. For now, narrow scope is safer.
  3. Verify in markdown preview — the .preview-md selector specifically: please double-check that the YAML preview pane (composer markdown preview) also picks this up.

Pulling locally to verify against the reporter's repro. Thanks @franksong2702.

@nesquena-hermes nesquena-hermes closed this pull request by merging all changes into nesquena:master in f8ed6da May 3, 2026
sunnysktsang pushed a commit to sunnysktsang/hermes-webui that referenced this pull request May 3, 2026
…h newlines (Prism token white-space) — closes nesquena#1463
sunnysktsang pushed a commit to sunnysktsang/hermes-webui that referenced this pull request May 3, 2026
…sorbed

CHANGELOG, ROADMAP, TESTING bumped (3936 \u2192 3946).

8 constituent PRs:
- nesquena#1523 (@franksong2702) branch indicator codepoint fix
- nesquena#1519 (@franksong2702) onboarding API-key focus loss fix
- nesquena#1518 (@franksong2702) voice-mode toggle-off recognizer stop
- nesquena#1516 (@franksong2702) YAML newline CSS rules
- nesquena#1517 (@franksong2702) __CACHE_VERSION__ \u2192 __WEBUI_VERSION__ rename
- nesquena#1532 (@ai-ag2026) state.db WebUI session recovery
- nesquena#1525 (@ai-ag2026) stale stream state proactive cleanup
- nesquena#1526 (@ai-ag2026) max_tokens forwarding + OpenRouter quota classifier

Opus MUST-FIX absorbed: sw.js conflict-marker cleanup + regression guard.
Opus SHOULD-FIX deferred to follow-up nesquena#1533 (race in _clear_stale_stream_state).

2 closed as duplicates: nesquena#1528 (identical to nesquena#1517), nesquena#1529 (superseded by nesquena#1516).
1 maintainer-review label: nesquena#1531 (Asunfly stowaway change in force-push).
5 stay on hold: nesquena#1418 nesquena#1464 nesquena#1404 nesquena#1353 nesquena#1311.
bergeouss pushed a commit to bergeouss/hermes-webui that referenced this pull request May 4, 2026
…/ nesquena#1463)

Closes nesquena#1618 (reported by @Zixim) and corrects nesquena#1463's previous fix.

Bug: YAML, JSON, and diff/patch fenced code blocks render flattened to a
single line. Reporter noted the bug persisted v0.50.279 -> v0.50.291 ->
v0.50.292 despite PR nesquena#1516's CSS-only "fix".

Root cause: PR nesquena#484 (v0.50.237) added a JSON/YAML tree-viewer that routes
those languages through <div class="code-tree-wrap">...<pre class="tree-raw-view">
instead of bare <pre>. Same release added the diff/patch coloring path
that emits <pre class="diff-block">. The _pre_stash regex at
static/ui.js:1914 matched only literal <pre> with no attributes:

    <pre>[\s\S]*?<\/pre>

Both new shapes failed to match, fell through to the paragraph-wrap pass,
and \n characters inside the code blocks got replaced with <br> tags
inside <code>. By the time Prism ran, no newlines remained for the CSS
rule (PR nesquena#1516, language-yaml .token { white-space: pre !important }) to
preserve.

Fix: relax the regex to accept any attribute on <pre>:

    <pre>[\s\S]*?<\/pre>  ->  <pre[^>]*>[\s\S]*?<\/pre>

One regex character. Pulls JSON, YAML, and diff/patch blocks into the
stash so paragraph-wrap can't mangle them. Bash, Python, Go, etc. were
never affected because they emit bare <pre>.

Tests: 9 new (2 source-string invariants + 7 behavioural via node-driver
against the actual static/ui.js renderMd()). 6 of the 7 behavioural tests
fail on master and pass with the fix; the 3 sanity checks (yml-alias,
bash, mermaid) pass on both.

Plus widened source-scan window in 3 pre-existing test_745 assertions
from 400 to 1500 chars. The new comment block above the fixed regex
pushed it past the previous scan window. Pure window-narrowness bug,
not a behavior regression.

4245 -> 4254 passing.
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