Bug fixes
Empty marked spans in Sanity Portable Text (issue #885)
libs/sanity/portable_text.py wraps strong-marked span text in **
delimiters and em-marked text in _, unless the text touches edge
whitespace, in which case it falls back to HTML. An empty span slipped
between the two: _EDGE_WS_RE (^\s|\s$) needs a character to match, and
"" has none, so the empty span took the delimiter path and emitted ****.
A line consisting of **** is a CommonMark thematic break (§4.1), so a
block whose only content was an empty bold run — or one sitting next to a
span ending in a newline — rendered as <hr/> instead of nothing.
The empty-span case now joins the existing HTML fallback (the same mechanism
PR #775 added for edge whitespace, applied to the zero-length degenerate
class) and emits an inert <strong></strong>.
The em branch first looked safe: a lone __ is two delimiters — too short
for a thematic break and unable to pair into emphasis. That holds only for a
single span, though. _render_spans joins adjacent span output with no
separator, so consecutive empty em spans join into ____ (or __ __
across a whitespace span), which §4.1 also accepts as a thematic break — the
same <hr/> fallout via one more span. Found in review of this change; empty
em spans now take the same fallback and emit an inert <em></em>.
The trigger requires a hand-edited Portable Text document (the Studio editor
serializes an empty selection as no span rather than an empty marked one), so
this is defense-in-depth for the blog-archive converter, not an
observed-data regression.Last modified on October 2, 2026