Skip to content

id(web): marks spanning inline HTML are broken on save — links fragment around the tags, emphasis leaks   entities #1412

Closed

Description

@dezren39

id(web): marks spanning inline HTML are broken on save — links fragment around the tags, emphasis leaks entities

Summary

In the pkgs/id web markdown codec (pkgs/id/src/web/markdown.rs), a mark (link, emphasis, …)
that spans an html_inline atom does not survive a save. The parse direction is correct —
the marked text runs carry the mark and the html_inline nodes sit between them — but the
save direction reassembles the span wrongly: the HTML tags migrate outside the mark, a link
is fragmented into one link per text run, and emphasis forms leak literal   entities
into the saved file. The corrupted output is a stable fixpoint, so nothing downstream ever
notices or repairs it.

This is the html_inline counterpart of #1407 (marks wrapping an image). The
html_inline node type was introduced by the #943 fix (PR #1009); unlike the image arm,
its save arm has no mark handling at all, so any fix scoped to images will not touch this.

Evidence (verified this run on main 37418e56)

Verbatim-codec probe crate (markdown.rs copied byte-identical from main, comrak 0.56.0;
log: goal hidden_files/probe-2026-10-27-creator-a6/out2.log + out3.log):

Input Saved (first save)
[a <b>x</b>](/y) [a ](/y)<b>[x](/y)</b>
[a <b>x</b> c](/y) [a ](/y)<b>[x](/y)</b>[ c](/y)
[a <sup>2</sup> c](/y) [a ](/y)<sup>[2](/y)</sup>[ c](/y)
*a <b>x</b> c* *a&#32;*<b>*x*</b>*&#32;c*

Parse shape for [a <b>x</b> c](/y) is correct (probe out3.log): PM paragraph =
text "a " [link], html_inline "<b>", text "x" [link], html_inline "</b>",
text " c" [link]. The information loss/fragmentation happens entirely on save.

Controls that round-trip cleanly in the same probe:

  • a <b>x</b> c (inline HTML, no marks) — clean.
  • # a <sub>x</sub>, a <!-- c --> b — clean.
  • [<b>x</b>](/y) and **<b>x</b>** (mark around a single atom run) — survive, because
    comrak re-nests the single marked text run equivalently.

Root cause (from the code)

  • Parse: NodeValue::HtmlInline arm (markdown.rs:421-433) emits an html_inline atom
    with only an html attr; atoms carry no marks, so the surrounding mark context exists
    only as marks on the neighbouring text runs.
  • Save: the "html_inline" arm of json_to_ast (markdown.rs:1360-1361) emits a bare
    NodeValue::HtmlInline node. Mark wrapping is done by create_marked_text
    (markdown.rs:1737) per text run, and neither it nor append_inline_child
    (markdown.rs:1712) can carry a mark across the atom — contrast the image arm
    (markdown.rs:1308+), which at least special-cases the link mark (id(web): linked images [![alt](src)](href) lose their link in the markdown ↔ ProseMirror codec — save strips the href #880). The result:
    each text run gets its own mark wrapper, the tags land outside, boundary spaces inside
    emphasis wrappers are escaped by comrak as &#32;.

Why it matters

Inline HTML inside links/emphasis is ordinary imported-markdown content (linked terms
with <sup>/<sub> notation, <b>/<kbd> inside link text, formatted references).
The first open→save silently rewrites it into fragmented links and entity-polluted
emphasis; the rendered meaning changes (link scope and tag nesting invert) and the
&#32; form is a stable fixpoint, so the corruption is permanent and invisible to
later round-trip checks.

Acceptance criteria

  • [a <b>x</b> c](/y) and [a <sup>2</sup> c](/y) round-trip with the link intact as a
    single link span containing the inline HTML (exact markdown or a form re-parsing to
    the same PM structure).
  • *a <b>x</b> c* (and strong/strikethrough equivalents) save as a single mark span
    containing the tags, with no &#32; or other HTML entities introduced.
  • Cases that already work stay working: unmarked inline HTML (a <b>x</b> c),
    single-run forms ([<b>x</b>](/y)), and the id(web): inline HTML (<sub>/<sup>/<u>/…) is backslash-escaped on save — tags become literal text #943 sub/sup/u cases.
  • Regression tests in pkgs/id/src/web/markdown.rs cover mark-spanning-html_inline in
    both directions; existing web::markdown tests pass.

Related

Activity

  1. developing-today commented on Oct 27, 2026

    @developing-today
    Contributor
  2. added
    kind/bugSomething isn't working (prefixed form of the classic bug label)
    queue/queuedHas a queued task in the Supabase agent queue
    source/creatorFiled by the new-issue-creator job
    task/triageQueue packet kind: verify, reproduce, and route this item
    priority/normalOrdinary priority (the default)
    app/idPackage/app: id (pkgs/ or apps/ in this repo)
    effort/sSmall: expected under 2 hours of work
    on Oct 27, 2026
  3. dezren39 commented on Oct 27, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · filed

  4. added
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Oct 27, 2026
  5. 16 remaining items

  6. dezren39 commented on Oct 27, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · state: parked

  7. added
    queue/claimedQueue worker lifecycle state
    and removed
    status/parkedDeliberately paused with a recorded resume condition
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    on Oct 28, 2026
  8. dezren39 commented on Oct 28, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · picked up

  9. added a commit that references this issue on Oct 28, 2026
  10. dezren39 commented on Oct 28, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · done

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

app/idPackage/app: id (pkgs/ or apps/ in this repo)effort/sSmall: expected under 2 hours of workkind/bugSomething isn't working (prefixed form of the classic bug label)priority/normalOrdinary priority (the default)source/creatorFiled by the new-issue-creator jobstatus:donetask/triageQueue packet kind: verify, reproduce, and route this item

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions