Files
lefarcen 441ab84b0b fix(web): hide an od-card whose JSON does not parse (#8264)
* fix(web): hide an od-card whose JSON does not parse

A closed `<od-card>…</od-card>` block whose payload fails to parse was
painted to the user as prose — Markdown and all, so `user_profile` came
out italic. A tail comma, a missing `summary`, a misspelled `type`: the
model writes all three, and each one put raw protocol markup on screen.

Product ruling (user, 2026-09-18): "od-card 如果 json 不对, 就不显示,
不然用户会觉得是乱码...还不如不显示". The block is now dropped instead.

The drop lives in the web render helper `splitShellCards`, not in the
shared `splitOnOdCards` parser: that parser is a lossless split whose
other callers (`chat-protocol-context` Markdown skip-ranges, daemon
`memory-verify`) read spans of the original text and need every
character back. Both chat surfaces — shell narration (`SayBlock`) and
prose (`AssistantMessage`) — already route through `splitShellCards`.

"Malformed" and "still streaming" stay separate states: dropping needs a
matched close tag, so a card mid-flight is still withheld and can still
complete into a real card. A fenced code block quoting the protocol is
still user prose, and a valid card still renders as `OdCardView`.

Refs OPEND-2745

* fix(web): classify Markdown over the text a dropped card leaves behind

Dropping a malformed block without recomputing the Markdown context opened
a second leak of exactly the kind this change exists to close. `codeRanges`
was still derived from the raw input, so markup inside a payload the user
never sees kept voting: a payload carrying an unclosed ``` fence on its own
line marked everything after it as code, the next perfectly valid card was
skipped by `rangeContains`, and the tail `appendText` emitted that valid
card as raw `<od-card …>` markup.

Markdown context is now classified over the text that will actually be
rendered — the prose retained so far in the current render plus the
unconsumed suffix — and positions are mapped into that view.

Recomputing from the suffix alone would be wrong in the other direction. A
dropped block is a hole in one continuous render, not a break in it: the
prose before and after it is handed to Markdown as a single string, so the
prose before the drop must keep its say over what follows. Judged on the
suffix alone, a ``` that sits mid-line in the real render becomes a
line-leading fence that hides everything below it — the same leak again.
A rendered card, by contrast, really does end the render, so that branch
keeps resetting as before.

Refs OPEND-2745
2026-09-18 05:57:03 +00:00
..