Skip to content

fix(iOS): caret renders inside inline images after attribute reapplication#517

Open
fendent wants to merge 1 commit intosoftware-mansion:mainfrom
fendent:fix/caret-inside-inline-images
Open

fix(iOS): caret renders inside inline images after attribute reapplication#517
fendent wants to merge 1 commit intosoftware-mansion:mainfrom
fendent:fix/caret-inside-inline-images

Conversation

@fendent
Copy link
Copy Markdown
Contributor

@fendent fendent commented Apr 6, 2026

Summary

Fixes an issue where the text caret visually overlaps with inline images (renders inside the image bounds rather than at its edge).

The root cause is in handleDirtyRangesStyling in AttributesManager. The method removes all attributes from a dirty range (including NSAttachmentAttributeName) then re-adds them. Each mutation triggers a separate TextKit layout pass. Between the remove and re-add, TextKit sees the \uFFFC character with no attachment and calculates zero glyph width, causing the caret to be positioned inside the image.

The fix wraps the reset+reapply cycle in beginEditing/endEditing so TextKit processes all attribute changes as a single atomic layout pass.

Test Plan

  1. Insert an inline image via setImage
  2. Use arrow keys or tap to place the caret immediately before or after the image
  3. Verify the caret renders at the edge of the image, not overlapping it
  4. Type text next to the image and verify the caret stays outside the image bounds

Compatibility

OS Implemented
iOS
Android

Checklist

  • E2E tests are passing
  • Required E2E tests have been added (if applicable)

The nuke-and-recreate cycle in handleDirtyRangesStyling removes all
attributes (including NSAttachmentAttributeName) then re-adds them.
Without batching, each mutation triggers a separate layout pass.
Between the remove and re-add, TextKit sees the object replacement
character with no attachment and calculates zero glyph width, causing
the caret to render inside inline images.

Wrapping in beginEditing/endEditing coalesces the changes into a
single layout pass so the transient zero-width state is never visible.
@kacperzolkiewski
Copy link
Copy Markdown
Collaborator

Hi @fendent,
Could you provide a short video showing the behavior before and after applying your changes?
I’m not seeing a noticeable difference on my side

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes a visual rendering issue on iOS where the text caret overlaps with inline images instead of appearing at the image's edge. The root cause is that handleDirtyRangesStyling in AttributesManager removes and re-applies attributes in separate operations, causing TextKit to perform intermediate layout passes where the \uFFFC placeholder character temporarily has zero glyph width. The fix wraps the attribute reset and re-application cycle in beginEditing/endEditing calls so TextKit processes all changes as a single atomic operation.

Changes:

  • Wrapped attribute reset and re-application in beginEditing/endEditing to ensure TextKit performs atomic layout passes
  • Added explanatory comment describing the caret rendering issue and its solution

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

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.

3 participants