Rich text <-> Markdown source toggle collapses undo history to one step, and corrupts non-trivial markdown on each round-trip

Environment: GitLab.com, Chrome (latest stable), macOS.

Core defect (minimal repro, no special markdown needed):

  1. Open a comment box, type one two three four five, press Enter, type six seven eight (two separate lines, typed as distinct actions).
  2. Switch to Markdown source, then back to Rich text (or vice versa), with no content edits, just the toggle.
  3. Press Cmd+Z once.

Expected: Undo steps back through your actual edit history (e.g. removes the second line, or the last few characters typed).

Actual: A single Cmd+Z deletes all content at once: both lines vanish in one step. Redo (Cmd+Shift+Z) brings everything back in one step. There is no way to step through intermediate states. This indicates that switching editor modes replaces the entire document as one atomic transaction in the undo stack, discarding the incremental history that existed before the switch.

Downstream consequence: repeated toggling corrupts content that doesn't round-trip losslessly.

Example 1:

<details>
<summary>Test summary with `inline code` and "quotes"</summary>

Some intro text with a trailing backslash\ and a literal escaped backtick \`here\`.

```python
def foo():
    return "hello\tworld"  # comment with \n backslash-n
```

Another paragraph with **bold**, *italic*, and a line that's just ---.

    indented code block line one
    indented code block line two

<details>
<summary>Nested summary</summary>

```
nested fenced block
with a trailing backslash\
and ``double backtick`` inline code
```

</details>

</details>

Repeating "switch to Rich text -> switch back to Markdown source" a handful of times (no edits) produces:

  • <details>/<summary> tags stripped on one round, then reappear reshaped on a later round.
  • Fenced and indented code blocks lose their fence/indentation markers, demoted to plain paragraph text.
  • **bold**/*italic* markers stripped outright (literal * characters removed from the source).
  • Backslashes preceding escaped characters grow with each round-trip: unbounded, compounding growth (dozens to hundreds of characters within a handful of toggles).
  • An orphaned, duplicate </details> appears (more closes than opens).
  • Stray, detached code-fence tokens appear elsewhere in the document with no matching partner.
  • In one session, continued toggling made the editor unresponsive; afterward the field contained only an empty <details><summary></summary></details>, meaning total content loss (fortunately unsaved).

A single-character change is enough to trigger the same class of corruption, faster. Changing only the closing delimiter of the nested inline code span from double backticks to a mismatched double-open/triple-close (2-vs-3 backtick count) and repeating the same toggle a few times produced, within fewer round-trips:

  • The same orphaned/duplicate </details> pattern (3 closing tags for 2 opening tags).
  • The python fence's opening 4-backtick marker with no closing fence anywhere in the document until a stray 4-backtick marker that appears much later, positioned after both real <details>/</details> pairs. Tracing fence-matching in the raw source, that later marker is the only thing that could close the original fence, meaning the entire rest of the document, including both real <details> structures, would be swallowed as literal text inside one giant code block, leaving a single </details> dangling outside any fence with no possible matching open tag at all.

This shows the corruption doesn't require elaborate input: a minor, easily-made mismatch (e.g. an extra backtick) is sufficient to destabilize the round-trip.

Confirmed NOT server-side: Extensive REST API testing (POST/PUT /notes, /markdown render preview) with the same content showed byte-exact storage across repeated saves and non-corrupting rendering. This only reproduces through the browser's Rich text <-> Markdown source toggle, pointing at the Content Editor's (ProseMirror) parse/serialize round-trip.

Impact: A completely ordinary action (switching editor view modes) can silently destroy content, and the expected safety net (undo) makes it worse by being all-or-nothing rather than incremental. Should be treated as a data-loss bug.

Related:

Edited by 🤖 GitLab Bot 🤖