Skip to content

feat: support saving original image#548

Open
eszlamczyk wants to merge 2 commits into
mainfrom
feat/380/save-original-image
Open

feat: support saving original image#548
eszlamczyk wants to merge 2 commits into
mainfrom
feat/380/save-original-image

Conversation

@eszlamczyk

@eszlamczyk eszlamczyk commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

What/Why?

Closes #380

Long-pressing an image in EnrichedMarkdownText and tapping Save to Photos saved the processed render instead of the source image. Instrumentation of ENRMImageAttachment showed that UIKit's attachment context menu, save action, and drag session all read the plain NSTextAttachment.image property (_attachmentHasImage:, _imageFromAttachment:, _defaultMenuForInteractableItem), which we populated with:

  • inline images: the scaled, border-radius-clipped bitmap - saved image was tiny and clipped,
  • block images re-created via the processed-image cache: a stale 1×1 placeholder - saved image was a blank pixel.

The fix separates the two roles the image property was playing:

  • image now always holds the original downloaded image - set as soon as the download completes, on both the fresh-process and cache-hit paths. This is what UIKit serves to Save to Photos, Copy Image, drag-and-drop, and RTFD copy/paste.
  • The processed (scaled + border-radius) bitmap lives only in loadedImage, returned from imageForBounds: for display - so on-screen rendering is unchanged.
  • The 1×1 loading placeholder moved to its own placeholderImage property. Before an image finishes loading, image is nil, so the system no longer offers image actions that would save a blank pixel.

Testing

In the example app (iOS simulator), with logging on the attachment accessors:

  1. Long-press a block image - Save to Photos => saved image is the full-resolution original (800×207 instead of the clipped 341×200 render).
  2. Long-press an inline image → Save to Photos → original 80×73 instead of the processed 20×20 thumbnail.
  3. Re-add an image so the attachment is rebuilt from the processed-image cache → long-press save still gets the original (previously a blank 1×1 placeholder).
  4. Verified display is unchanged: imageForBounds: still returns the processed bitmap for drawing (sizing and border radius intact), and the drag preview still shows the rendered view.

Screenshots

Note the cut corners

Before After
Image after saving image image

PR Checklist

  • Code compiles and runs on iOS
  • Code compiles and runs on Android
  • Updated documentation/README if applicable
  • Ran example app to verify changes
  • E2E tests are passing
  • Required E2E tests have been added (if applicable)

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the iOS ENRMImageAttachment behavior so UIKit “image actions” (Save to Photos / Copy Image / drag-and-drop) use the original downloaded image rather than the processed (scaled/clipped) render, while keeping on-screen rendering unchanged.

Changes:

  • Separates “original image for system actions” (NSTextAttachment.image) from “processed image for display” (loadedImage via imageForBounds:).
  • Introduces a dedicated placeholderImage so image can remain nil until the real image is available.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

The image downloader invokes its completion synchronously on original-cache
hits, which can happen on the async render queue during markdown parsing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@eszlamczyk
eszlamczyk requested a review from hryhoriiK97 July 17, 2026 13:56
@eszlamczyk
eszlamczyk marked this pull request as ready for review July 17, 2026 13:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support full Markdown image display and original image saving

2 participants