···11+diff --git a/node_modules/expo-paste-input/ios/ExpoPasteInputView.swift b/node_modules/expo-paste-input/ios/ExpoPasteInputView.swift
22+index 2164aec4ec1d..d216db6d2927 100644
33+--- a/node_modules/expo-paste-input/ios/ExpoPasteInputView.swift
44++++ b/node_modules/expo-paste-input/ios/ExpoPasteInputView.swift
55+@@ -511,14 +511,17 @@ class ExpoPasteInputView: ExpoView {
66+ var attachmentRanges: [NSRange] = []
77+ var mediaPayloads: [MediaPayload] = []
88+99++ // Only track ranges for attachments we successfully extract a real payload
1010++ // from. Attachments without a payload (e.g. iOS dictation placeholders)
1111++ // are left alone — sanitizing them would delete characters the system
1212++ // manages itself, and emitting "unsupported" would raise a spurious error.
1313+ attributedText.enumerateAttribute(.attachment, in: NSRange(location: 0, length: attributedText.length), options: []) { value, range, _ in
1414+ guard let attachment = value as? NSTextAttachment else {
1515+ return
1616+ }
1717+1818+- attachmentRanges.append(range)
1919+-
2020+ if let payload = self.extractMediaPayload(from: attachment, textView: textView, range: range) {
2121++ attachmentRanges.append(range)
2222+ mediaPayloads.append(payload)
2323+ }
2424+ }
2525+@@ -529,9 +532,8 @@ class ExpoPasteInputView: ExpoView {
2626+ return
2727+ }
2828+2929+- attachmentRanges.append(range)
3030+-
3131+ if let payload = self.extractMediaPayload(from: adaptiveGlyph) {
3232++ attachmentRanges.append(range)
3333+ mediaPayloads.append(payload)
3434+ }
3535+ }
3636+@@ -539,17 +541,12 @@ class ExpoPasteInputView: ExpoView {
3737+3838+ attachmentRanges = uniqueRanges(attachmentRanges)
3939+4040+- guard !attachmentRanges.isEmpty else {
4141+- return
4242+- }
4343+-
4444+- sanitizeAttachments(in: textView, ranges: attachmentRanges)
4545+-
4646+ guard !mediaPayloads.isEmpty else {
4747+- handleUnsupportedPaste()
4848+ return
4949+ }
5050+5151++ sanitizeAttachments(in: textView, ranges: attachmentRanges)
5252++
5353+ emitImagesAsync(for: mediaPayloads)
5454+ }
5555+5656+@@ -651,6 +648,11 @@ class ExpoPasteInputView: ExpoView {
5757+ }
5858+5959+ private func extractMediaPayload(from attachment: NSTextAttachment, textView: UITextView, range: NSRange) -> MediaPayload? {
6060++ // Only accept attachments that carry real image payloads. We intentionally
6161++ // do not fall back to `image(forBounds:)` or rendering the text view's
6262++ // hierarchy, because system-inserted attachments (e.g. the iOS dictation
6363++ // placeholder) draw themselves via those paths and would cause us to
6464++ // emit a screenshot of the composer as a "pasted image".
6565+ if let fileWrapperData = attachment.fileWrapper?.regularFileContents,
6666+ let payload = extractMediaPayload(fromData: fileWrapperData) {
6767+ return payload
6868+@@ -667,20 +669,6 @@ class ExpoPasteInputView: ExpoView {
6969+ return .image(image)
7070+ }
7171+7272+- let attachmentBounds = attachment.bounds.size.width > 0 && attachment.bounds.size.height > 0
7373+- ? attachment.bounds
7474+- : CGRect(origin: .zero, size: CGSize(width: 128, height: 128))
7575+-
7676+- if let image = attachment.image(forBounds: attachmentBounds, textContainer: textView.textContainer, characterIndex: range.location),
7777+- image.size.width > 0,
7878+- image.size.height > 0 {
7979+- return .image(image)
8080+- }
8181+-
8282+- if let renderedImage = renderTextAttachment(in: textView, range: range) {
8383+- return .image(renderedImage)
8484+- }
8585+-
8686+ return nil
8787+ }
8888+8989+@@ -701,47 +689,6 @@ class ExpoPasteInputView: ExpoView {
9090+ return .imageData(data)
9191+ }
9292+9393+- private func renderTextAttachment(in textView: UITextView, range: NSRange) -> UIImage? {
9494+- let glyphRange = textView.layoutManager.glyphRange(forCharacterRange: range, actualCharacterRange: nil)
9595+- var rect = textView.layoutManager.boundingRect(forGlyphRange: glyphRange, in: textView.textContainer)
9696+-
9797+- rect.origin.x += textView.textContainerInset.left - textView.contentOffset.x
9898+- rect.origin.y += textView.textContainerInset.top - textView.contentOffset.y
9999+- rect = rect.integral
100100+-
101101+- guard rect.width > 1, rect.height > 1 else {
102102+- return nil
103103+- }
104104+-
105105+- let format = UIGraphicsImageRendererFormat.default()
106106+- format.scale = textView.window?.screen.scale ?? UIScreen.main.scale
107107+- format.opaque = false
108108+-
109109+- let image = UIGraphicsImageRenderer(size: rect.size, format: format).image { _ in
110110+- let drawRect = CGRect(
111111+- origin: CGPoint(x: -rect.origin.x, y: -rect.origin.y),
112112+- size: textView.bounds.size
113113+- )
114114+-
115115+- if textView.window != nil {
116116+- textView.drawHierarchy(in: drawRect, afterScreenUpdates: false)
117117+- } else {
118118+- guard let context = UIGraphicsGetCurrentContext() else {
119119+- return
120120+- }
121121+-
122122+- context.translateBy(x: -rect.origin.x, y: -rect.origin.y)
123123+- textView.layer.render(in: context)
124124+- }
125125+- }
126126+-
127127+- guard image.size.width > 0, image.size.height > 0 else {
128128+- return nil
129129+- }
130130+-
131131+- return image
132132+- }
133133+-
134134+ @available(iOS 18.0, *)
135135+ private func handleAdaptiveImageGlyphInsertion(_ adaptiveGlyph: NSAdaptiveImageGlyph) -> Bool {
136136+ guard let payload = extractMediaPayload(from: adaptiveGlyph) else {
+22
patches/expo-paste-input+0.1.15.patch.md
···11+# Expo Paste Input Patch
22+33+`expo-paste-input` observes `UITextView.textDidChangeNotification` and treats any
44+`NSTextAttachment` in the text view's `attributedText` as a pasted image. When
55+it can't find a real image payload on an attachment, it falls back to
66+`image(forBounds:)` and, failing that, to a `drawHierarchy` screenshot of the
77+text view at the attachment's glyph rect.
88+99+iOS Dictation inserts its own `NSTextAttachment` (the shimmer/cursor indicator)
1010+into the text view during dictation. Those attachments don't carry real image
1111+data, so the fallbacks would fire — emitting a zoomed-in screenshot of the
1212+composer as if the user had pasted an image at the end of dictation.
1313+1414+This patch:
1515+1616+- Removes the `image(forBounds:)` and `renderTextAttachment` fallbacks in
1717+ `extractMediaPayload` so the library only accepts attachments carrying a real
1818+ payload (`fileWrapper`, `contents`, or `image`).
1919+- Only sanitizes (deletes) attachment ranges that produced a payload, and
2020+ skips the "unsupported" toast when an attachment has no payload. Unknown
2121+ system attachments like the dictation placeholder are left alone rather
2222+ than being ripped out from under iOS.