Skip to content

fix: preserve YAML code block newlines#1641

Closed
Michaelyklam wants to merge 1 commit intonesquena:masterfrom
Michaelyklam:fix/issue-1618-yaml-pre-stash
Closed

fix: preserve YAML code block newlines#1641
Michaelyklam wants to merge 1 commit intonesquena:masterfrom
Michaelyklam:fix/issue-1618-yaml-pre-stash

Conversation

@Michaelyklam
Copy link
Copy Markdown
Contributor

Thinking Path

  • Hermes WebUI's markdown renderer should preserve code-block text exactly, especially for structured formats like YAML and diff output.
  • Issue Bug: YAML rendering still flattened in v0.50.291 #1618 showed that YAML blocks were still visually flattened even though the CSS token whitespace rule existed.
  • The root cause is earlier in the renderer pipeline: JSON/YAML tree-view and diff blocks render <pre> elements with classes, but the paragraph-protection stash only matched literal <pre>.
  • Because <pre class="tree-raw-view"> and <pre class="diff-block"> were not stashed, paragraph wrapping converted their internal newlines into <br> before Prism/rendering could preserve them.
  • This PR expands the existing stash regex to protect attributed <pre> blocks too, keeping the fix to the smallest affected renderer primitive.

Fixes #1618

What Changed

  • Updated the rendered-pre stash in static/ui.js from <pre> to <pre[^>]*> so attributed code blocks are protected before paragraph wrapping.
  • Added behavioral Node-backed regression tests that execute the real renderMd() implementation and assert YAML tree raw views and diff blocks keep newline characters inside <code>.
  • Added before/after UI media showing the collapsed YAML block and the corrected multi-line rendering.

Why It Matters

YAML, diff, JSON, and similar code blocks are only useful if their structure survives rendering. Flattening YAML into one line changes the meaning and makes copied examples harder to read or use. Protecting attributed <pre> blocks fixes the current YAML bug and prevents the same paragraph wrapping path from mangling other classed code blocks.

Verification

/home/michael/.hermes/hermes-agent/venv/bin/python -m pytest tests/test_issue1618_yaml_rendering.py -q
/home/michael/.hermes/hermes-agent/venv/bin/python -m pytest tests/test_issue1618_yaml_rendering.py tests/test_renderer_comprehensive.py tests/test_issue1438_fence_anchoring.py tests/test_streaming_markdown.py -q
node --check static/ui.js
git diff --check
env -u HERMES_CONFIG_PATH /home/michael/.hermes/hermes-agent/venv/bin/python -m pytest tests/ -q

Result:

2 passed in 1.39s
128 passed in 1.88s
Full suite: 4247 passed, 2 skipped, 3 xpassed, 1 warning, 8 subtests passed in 406.14s (0:06:46)

Manual verification:

  • Started isolated before/after WebUI instances.
  • Before: YAML textContent was flattened and innerHTML contained <br> inside code.language-yaml.
  • After: YAML textContent contains real newlines, innerHTML does not contain <br>, and the rendered block is multi-line with indentation preserved.
  • Verified temporary local servers were killed after QA.

UI media:

Before — YAML block flattened into one line:

Before: flattened YAML rendering

After — YAML line breaks and indentation preserved:

After: YAML rendering preserves line breaks

Risks / Follow-ups

  • This intentionally broadens the existing pre-stash to all attributed <pre> blocks, which matches the behavior already used by the autolink stash and keeps code-block internals out of paragraph wrapping.
  • The generated code-tree-wrap HTML can still be wrapped by an outer paragraph in the string output; this PR fixes the newline-loss bug without widening scope into renderer markup normalization.
  • The full suite warning is existing pytest marker noise: PytestUnknownMarkWarning: Unknown pytest.mark.integration.

Model Used

AI assisted.

  • Provider: openai-codex
  • Model: gpt-5.5
  • Notable tool use: terminal/git/gh, pytest, Node syntax checks, isolated browser QA, before/after screenshot capture

@nesquena-hermes
Copy link
Copy Markdown
Collaborator

Thanks @Michaelyklam — went through this carefully and I'm afraid this PR is being superseded by PR #1642 (filed by us 4 minutes after yours). It's the exact same one-character regex relax for _pre_stash:

- s=s.replace(/(<div class="pre-header">[\s\S]*?<\/div>)?<pre>[\s\S]*?<\/pre>...
+ s=s.replace(/(<div class="pre-header">[\s\S]*?<\/div>)?<pre[^>]*>[\s\S]*?<\/pre>...

We hit this independently in our investigation of #1618 (the @Zixim "still flattened in v0.50.291" report) — both arrived at the same minimal-surface fix, which is encouraging convergent evidence that this IS the right layer.

Why we're shipping #1642 instead:

  • Already has nesquena APPROVED with end-to-end behavioral trace
  • 322 LOC test suite (vs 96 here) covering YAML + JSON + diff + yml-alias-sanity + bash-sanity + mermaid-sanity, all driven by a real node driver against the actual static/ui.js renderMd() — not just YAML
  • Includes pre-existing test window widening in test_745_code_block_newlines.py (the new comment block in the fix pushed past the previous 400-char scan window)
  • More thorough CHANGELOG entry that explains why PR fix: YAML code blocks collapse newlines due to Prism token white-space (#1463) #1516's CSS-only fix was the wrong layer (Prism token white-space rule preserves newlines inside .token spans, but the spans were built from a string that already had no newlines left)

Adopting your evidence in-tree: your before/after screenshots (docs/pr-media/issue-1618/before.png and after.png) are great visual proof of the fix and we've adopted them into stage-295 with a Co-authored-by: Michael Lam <[email protected]> trailer on the docs commit. The before/after will live in-tree alongside the merged fix.

Closing this PR but the work is being shipped — your fix shape, your tests, and your media are all part of the v0.50.295 release. Thanks for the parallel diagnosis and for the strong contributor pattern this week.

bergeouss pushed a commit to bergeouss/hermes-webui that referenced this pull request May 4, 2026
…chaelyklam nesquena#1641)

Adopt the UI media from @Michaelyklam's parallel-discovery PR nesquena#1641 which
shipped the same one-character regex relax fix for nesquena#1618. PR nesquena#1641 is
being closed as superseded by nesquena#1642 (which carries nesquena APPROVED +
322 LOC test suite); preserving Michael's UI evidence here so the visual
proof of the fix lives in-tree alongside the canonical PR.

Co-authored-by: Michael Lam <[email protected]>
bergeouss pushed a commit to bergeouss/hermes-webui that referenced this pull request May 4, 2026
Constituent PRs:
  nesquena#1637 by @Michaelyklam — protect raw pre from glued-bold lift (closes nesquena#1451)
  nesquena#1639 by @bergeouss — macOS auto-scroll race + custom:* provider list (closes nesquena#1360, nesquena#1619)
  nesquena#1642 by @nesquena-hermes — YAML/JSON/diff code block newlines (closes nesquena#1618, nesquena#1463)

Opus advisor SHIP verdict on stage-295. One observation absorbed:
- api/config.py:2533 dead-code comment per Opus (defensive belt-and-braces
  for nesquena#1619 fallback; load-bearing fix is in routes.py /api/models/live)

PR nesquena#1641 (Michaelyklam parallel-discovery duplicate of nesquena#1642) closed as
superseded; UI media adopted with co-author trailer.

4245 → 4255 tests passing (+10).
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.

Bug: YAML rendering still flattened in v0.50.291

2 participants