Uh oh!
There was an error while loading. Please reload this page.
Render inline mail images in the body, drop the ones nothing references - #378
Merged
Conversation
A mail client embeds an image it attached to a message by pointing at its MIME part: `<img src="cid:...">`. Mail connectors treated every such part as an ordinary attachment, which produced two problems. An image the body still references was listed below the message instead of appearing where the sender placed it — and because connectors report no dimensions for it, it rendered in a fixed preview box at nothing like its real size. The Outlook connector dropped these parts entirely, so a screenshot pasted into a message never showed up at all. Worse, connectors trim quoted history out of a reply's body but classified attachments against the raw message. A sender's signature image is re-embedded by every reply in a chain, so each message carried a part whose only reference sat in the quote that had just been removed. Those orphans became an attachment on every message in a long thread — often a blank placeholder the reader had never seen, because the sender's own mail gateway had already stripped the image. Parts are now classified against the body that survives trimming: - Referenced by the retained body -> the fileRef action carries the part's `contentId`, and Plot renders the image inline where the sender put it. - Not referenced -> dropped. There is nowhere to render it, and listing it as an attachment shows a file the reader cannot place. - No HTML body to test against (a plain-text message) -> left as an attachment rather than silently discarded. Adds `contentId` to `ActionType.fileRef`, `contentId`/`inline` to `ImapMessage.attachments`, and `referencedContentIds`/`normalizeContentId` to the signals entry point so all three mail connectors share one rule. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AZjF9kXrBHeX4415ifBXvZ
Uh oh!
There was an error while loading. Please reload this page.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
A mail client embeds an image it attached to a message by pointing at its MIME part —
<img src="cid:...">. Mail connectors treated every such part as an ordinary attachment, which produced two problems.Inline images never rendered inline. An image the body still references was listed as an attachment below the message rather than appearing where the sender placed it, and since connectors report no dimensions for these parts it rendered in a fixed preview box at nothing like its real size. The Outlook connector filtered inline parts out altogether, so a screenshot pasted into a message never appeared at all.
Trimmed quotes left orphans behind. Connectors cut quoted history out of a reply's body, but classified attachments against the raw message. A sender's signature image is re-embedded by every reply in a chain, so each message carried a part whose only reference sat in the quote that had just been removed. Those orphans became an attachment on every message in a long thread — frequently a blank placeholder the reader had never seen, because the sender's own mail gateway had already stripped the image.
What changed
Parts are classified against the body that survives trimming:
fileRefcarries the part'scontentId; Plot renders it inline where the sender put itThat last row matters: a plain-text message carries no
cid:references to match against, so absence of a reference is not evidence the part is an orphan.SDK
contentIdonActionType.fileRef— set it only when the note's content still references the part. A non-null value tells Plot to render the image in the body instead of appending an attachment chip.contentIdandinlineonImapMessage.attachments. Amultipart/relatedimage routinely omitsContent-Dispositionentirely, so a bareContent-IDis itself the signal that the part belongs to the body.referencedContentIdsandnormalizeContentIdon the signals entry point, so every mail connector shares one rule rather than three approximations of it.Connectors
Content-ID/Content-Dispositionper part and classifies against the post-trim body.contentIdin the attachments$selectand replaces its blanket!isInlinefilter. This is what makes a pasted inline image appear at all.ImapAttachment.Tests
Each connector gains coverage for the three outcomes — referenced, orphaned, and no-HTML-body — plus parser-level tests for Content-ID extraction and the bare-Content-ID inline case.
google363,outlook145,apple456, all green;plot lintclean in all three.🤖 Generated with Claude Code
https://claude.ai/code/session_01AZjF9kXrBHeX4415ifBXvZ