Steps to reproduce
-
Open the Sandbox vault (Help → Sandbox vault). No community plugins, themes, or snippets needed.
-
Create a note containing this
queryblock (16 characters total, counting from the first backtick of the opening fence to the last backtick of the closing fence):Some text. ```query foo ``` -
In Live Preview, click on “Some text.”, then press the Down arrow until the cursor moves into the code block.
Did you follow the troubleshooting guide? [Y]
Expected result
The block expands to source view with normal code-block styling on all three lines (HyperMD-codeblock line classes, code background), as happens for longer blocks.
Actual result
Only the opening fence line gets its code-block line classes. The content line (foo) and the closing fence render as bare paragraph lines — no HyperMD-codeblock class, no background. Typing any character anywhere restores correct rendering. Moving the cursor out and back in reproduces it again.
The same happens for any rendered block (query, mermaid, and every registerMarkdownCodeBlockProcessor language, e.g. Dataview) whose total source length is 19 characters or less. At 20 characters and above (e.g. ```query + foobarbaz), rendering is correct. Plain (non-rendered) languages such as js are unaffected at any length.
Environment
SYSTEM INFO:
Obsidian version: 1.13.7
Installer version: 1.12.7
Operating system: Darwin Kernel Version 25.2.0: Tue Nov 18 21:09:56 PST 2025; root:xnu-12377.61.12~1/RELEASE_ARM64_T6041 25.2.0
Login status: logged in
Language: zh
Catalyst license: insider
Insider build toggle: on
Live preview: on
Base theme: adapt to system
Community theme: none
Snippets enabled: 0
Restricted mode: on
RECOMMENDATIONS:
none
Additional information
Screenshots below (taken in the Sandbox vault, default theme): block A (16 chars) broken after cursor entry vs block B (22 chars) rendering correctly — same block type, same note, the only variable is source length.
Analysis of app.js (1.13.7) suggests where the 20 comes from:
-
Visible-range computation calls
RangeSet.spans(decorations, viewport.from, viewport.to, iterator, 20)withminPointSize = 20, so a block-replace decoration (the rendered code block widget) shorter than 20 characters is not treated as a point — the text it covers stays insidevisibleRangeseven while the widget is displayed. -
The line-class decorator plugin (the one that turns
HyperMD-*line-class node props intoDecoration.line) iterates the syntax tree overvisibleRangeswith a monotonically advancing position guard (if (c > lineBlockAt(from).from) return). While the widget is displayed, all covered source lines belong to one line block, so after the first line’s tokens are emitted, the guard drops the line-class decorations for every following line of the block. -
When the cursor enters the block, the widget decoration is removed — but for a sub-20-character block
visibleRangesdo not change (the text was never excluded) and the syntax tree has not changed either, so the plugin’supdate(which rebuilds only on tree change orviewportChanged) keeps the stale, incomplete decorations. Any edit changes the tree and repairs the display; for blocks ≥ 20 characters the widget’s removal changesvisibleRanges, so they render correctly on the first try.
The syntax tree itself is correct in the broken state — all three lines carry their full HyperMD-codeblock(-begin/-end) line-class props. Only the decoration set is stale.
A · 16 chars · broken after cursor entry (Sandbox vault, default theme):
B · 22 chars · renders correctly:

