SIGN IN SIGN UP

fix(chat): refuse Markdown image paths the parser may have glued

Review fixes on top of the local-image renderer.

Correctness. CommonMark drops `\` before ASCII punctuation, so a Windows
destination loses the separator in front of every punctuation-initial
segment: `C:\repo\shots\.preview.png` parses as `C:\repo\shots.preview.png`.
That is not merely a path that fails to load — the glued form is still inside
the root, so a `shots.preview.png` that happens to exist rendered under the
alt text of the file the agent actually named. `[Image blocked: …]` had no
such failure mode, so refusing is the floor for replacing it.
`remarkLocalImages` now refuses a destination when `\` + ASCII punctuation
occurs anywhere from the earliest possible start of the destination (past the
first `](` or `]:`) to the end of the node's source. Starting the region
early only refuses more, so no label-end guess is needed — which is what
`remarkRestoreWindowsPaths` deliberately avoids for images. The label is
excluded, so `![a \* b](./shot.png)` still resolves.

Wiring. `canvas-conversation-surface` and `live-transcript-view` now pass the
cwd they already hold. Both fell back to the folder path derived from
persisted detail, which a canvas draft's first reply does not have yet and a
delegation child's scratch folder row is missing from (created closed, with
no `folder://changed` broadcast). A derived test asserts every
`<MessageListView` mount makes the decision.

Robustness. The sanitize allow-list carried only the camelCased hast property
names, which match only because Streamdown runs `rehype-raw` ahead of
sanitize; both spellings are now listed. A `data-codeg-local-image` with no
value is left as the plain span it is, and `resolveLocalImage` takes its MIME
from the extension it already validated.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
X
xintaofei committed
049ef608b1c3b246819355e9f9a34155fb34cb7e
Parent: 402d8b0