expo-paste-input 0.1.14 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/build/.transforms/04cfbef6d6438014a0f56f30321e9943/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/expo/modules/pasteinput/ExpoPasteInputView$1.dex +0 -0
- package/android/build/.transforms/04cfbef6d6438014a0f56f30321e9943/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/expo/modules/pasteinput/ExpoPasteInputView$ClipboardPayload.dex +0 -0
- package/android/build/.transforms/04cfbef6d6438014a0f56f30321e9943/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/expo/modules/pasteinput/ExpoPasteInputView$createActionModeCallback$1.dex +0 -0
- package/android/build/.transforms/04cfbef6d6438014a0f56f30321e9943/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/expo/modules/pasteinput/ExpoPasteInputView.dex +0 -0
- package/android/build/.transforms/61052c0b101cc72f1be04cd5637e14e3/transformed/classes/classes_dex/classes.dex +0 -0
- package/android/build/intermediates/compile_library_classes_jar/debug/bundleLibCompileToJarDebug/classes.jar +0 -0
- package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/expo/modules/pasteinput/ExpoPasteInputView$1.class +0 -0
- package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/expo/modules/pasteinput/ExpoPasteInputView$ClipboardPayload.class +0 -0
- package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/expo/modules/pasteinput/ExpoPasteInputView$createActionModeCallback$1.class +0 -0
- package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/expo/modules/pasteinput/ExpoPasteInputView.class +0 -0
- package/android/build/intermediates/runtime_library_classes_jar/debug/bundleLibRuntimeToJarDebug/classes.jar +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/counters.tab +1 -1
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/last-build.bin +0 -0
- package/android/build/kotlin/compileDebugKotlin/classpath-snapshot/shrunk-classpath-snapshot.bin +0 -0
- package/android/build/kotlin/compileDebugKotlin/local-state/build-history.bin +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/pasteinput/ExpoPasteInputView$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/pasteinput/ExpoPasteInputView$ClipboardPayload.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/pasteinput/ExpoPasteInputView$createActionModeCallback$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/pasteinput/ExpoPasteInputView.class +0 -0
- package/android/src/main/java/expo/modules/pasteinput/ExpoPasteInputView.kt +22 -69
- package/ios/ExpoPasteInputView.swift +49 -91
- package/package.json +1 -1
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab
CHANGED
|
Binary file
|
|
Binary file
|
package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.values.at
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab
CHANGED
|
Binary file
|
|
Binary file
|
package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab
CHANGED
|
Binary file
|
|
Binary file
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
10
|
|
2
2
|
0
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.len
CHANGED
|
Binary file
|
|
Binary file
|
package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab_i
CHANGED
|
Binary file
|
|
Binary file
|
package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream
CHANGED
|
Binary file
|
|
Binary file
|
package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.len
CHANGED
|
Binary file
|
package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.values.at
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/android/build/kotlin/compileDebugKotlin/classpath-snapshot/shrunk-classpath-snapshot.bin
CHANGED
|
Binary file
|
|
Binary file
|
package/android/build/tmp/kotlin-classes/debug/expo/modules/pasteinput/ExpoPasteInputView$1.class
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/android/build/tmp/kotlin-classes/debug/expo/modules/pasteinput/ExpoPasteInputView.class
CHANGED
|
Binary file
|
|
@@ -11,7 +11,6 @@ import android.view.View
|
|
|
11
11
|
import android.view.ViewGroup
|
|
12
12
|
import android.view.ActionMode
|
|
13
13
|
import android.widget.EditText
|
|
14
|
-
import kotlin.math.max
|
|
15
14
|
import androidx.core.view.ContentInfoCompat
|
|
16
15
|
import androidx.core.view.OnReceiveContentListener
|
|
17
16
|
import androidx.core.view.ViewCompat
|
|
@@ -61,91 +60,45 @@ class ExpoPasteInputView(context: Context, appContext: AppContext) : ExpoView(co
|
|
|
61
60
|
})
|
|
62
61
|
}
|
|
63
62
|
|
|
64
|
-
/**
|
|
65
|
-
* Ensure this wrapper behaves like a normal container in React Native layouts.
|
|
66
|
-
*
|
|
67
|
-
* Without an explicit measure/layout pass-through, some parent layouts can end up
|
|
68
|
-
* treating this view as having near-zero height, collapsing the wrapped EditText.
|
|
69
|
-
*/
|
|
70
63
|
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
64
|
+
// Let the wrapper resolve its own size first, then size children to the
|
|
65
|
+
// resolved content box so wrapped RN inputs can fill like a normal container.
|
|
66
|
+
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
|
67
|
+
|
|
68
|
+
val availableWidth = (measuredWidth - paddingLeft - paddingRight).coerceAtLeast(0)
|
|
69
|
+
val availableHeight = (measuredHeight - paddingTop - paddingBottom).coerceAtLeast(0)
|
|
70
|
+
|
|
71
|
+
val childWidthSpec = MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.EXACTLY)
|
|
72
|
+
val childHeightSpec = MeasureSpec.makeMeasureSpec(availableHeight, MeasureSpec.EXACTLY)
|
|
74
73
|
|
|
75
74
|
for (i in 0 until childCount) {
|
|
76
75
|
val child = getChildAt(i)
|
|
77
|
-
if (child.visibility
|
|
78
|
-
|
|
79
|
-
val lp = child.layoutParams
|
|
80
|
-
if (lp is MarginLayoutParams) {
|
|
81
|
-
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0)
|
|
82
|
-
maxChildWidth = max(maxChildWidth, child.measuredWidth + lp.leftMargin + lp.rightMargin)
|
|
83
|
-
maxChildHeight = max(maxChildHeight, child.measuredHeight + lp.topMargin + lp.bottomMargin)
|
|
84
|
-
} else {
|
|
85
|
-
measureChild(child, widthMeasureSpec, heightMeasureSpec)
|
|
86
|
-
maxChildWidth = max(maxChildWidth, child.measuredWidth)
|
|
87
|
-
maxChildHeight = max(maxChildHeight, child.measuredHeight)
|
|
76
|
+
if (child.visibility != View.GONE) {
|
|
77
|
+
child.measure(childWidthSpec, childHeightSpec)
|
|
88
78
|
}
|
|
89
|
-
|
|
90
|
-
childState = View.combineMeasuredStates(childState, child.measuredState)
|
|
91
79
|
}
|
|
92
|
-
|
|
93
|
-
maxChildWidth += paddingLeft + paddingRight
|
|
94
|
-
maxChildHeight += paddingTop + paddingBottom
|
|
95
|
-
|
|
96
|
-
val measuredWidth = View.resolveSizeAndState(maxChildWidth, widthMeasureSpec, childState)
|
|
97
|
-
val measuredHeight =
|
|
98
|
-
View.resolveSizeAndState(maxChildHeight, heightMeasureSpec, childState shl View.MEASURED_HEIGHT_STATE_SHIFT)
|
|
99
|
-
|
|
100
|
-
setMeasuredDimension(measuredWidth, measuredHeight)
|
|
101
80
|
}
|
|
102
81
|
|
|
103
82
|
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
|
|
104
|
-
val
|
|
105
|
-
val
|
|
106
|
-
val
|
|
83
|
+
val left = paddingLeft
|
|
84
|
+
val top = paddingTop
|
|
85
|
+
val right = (r - l - paddingRight).coerceAtLeast(left)
|
|
86
|
+
val bottom = (b - t - paddingBottom).coerceAtLeast(top)
|
|
107
87
|
|
|
108
88
|
for (i in 0 until childCount) {
|
|
109
89
|
val child = getChildAt(i)
|
|
110
|
-
if (child.visibility
|
|
111
|
-
|
|
112
|
-
val lp = child.layoutParams
|
|
113
|
-
val left: Int
|
|
114
|
-
val top: Int
|
|
115
|
-
val right: Int
|
|
116
|
-
val bottom: Int
|
|
117
|
-
|
|
118
|
-
if (lp is MarginLayoutParams) {
|
|
119
|
-
left = parentLeft + lp.leftMargin
|
|
120
|
-
top = parentTop + lp.topMargin
|
|
121
|
-
right = (parentRight - lp.rightMargin).coerceAtLeast(left)
|
|
122
|
-
bottom = (top + child.measuredHeight).coerceAtMost(b - t - paddingBottom - lp.bottomMargin)
|
|
123
|
-
} else {
|
|
124
|
-
left = parentLeft
|
|
125
|
-
top = parentTop
|
|
126
|
-
right = parentRight.coerceAtLeast(left)
|
|
127
|
-
bottom = (top + child.measuredHeight).coerceAtMost(b - t - paddingBottom)
|
|
90
|
+
if (child.visibility != View.GONE) {
|
|
91
|
+
child.layout(left, top, right, bottom)
|
|
128
92
|
}
|
|
129
|
-
|
|
130
|
-
child.layout(left, top, right, bottom)
|
|
131
93
|
}
|
|
132
94
|
}
|
|
133
|
-
|
|
134
|
-
// Pass through all touch events to children - never intercept
|
|
135
|
-
override fun onInterceptTouchEvent(ev: android.view.MotionEvent?): Boolean {
|
|
136
|
-
return false
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
override fun onTouchEvent(event: android.view.MotionEvent?): Boolean {
|
|
140
|
-
return false
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
override fun dispatchTouchEvent(ev: android.view.MotionEvent?): Boolean {
|
|
144
|
-
return super.dispatchTouchEvent(ev)
|
|
145
|
-
}
|
|
146
|
-
|
|
95
|
+
|
|
147
96
|
override fun onViewAdded(child: View?) {
|
|
148
97
|
super.onViewAdded(child)
|
|
98
|
+
// Let default container sizing recalculate after RN inserts a child.
|
|
99
|
+
requestLayout()
|
|
100
|
+
invalidate()
|
|
101
|
+
|
|
149
102
|
// Re-scan for text input when a new child is added
|
|
150
103
|
if (!isMonitoring) {
|
|
151
104
|
startMonitoring()
|
|
@@ -511,14 +511,17 @@ class ExpoPasteInputView: ExpoView {
|
|
|
511
511
|
var attachmentRanges: [NSRange] = []
|
|
512
512
|
var mediaPayloads: [MediaPayload] = []
|
|
513
513
|
|
|
514
|
+
// Only track ranges for attachments we successfully extract a real payload
|
|
515
|
+
// from. Attachments without a payload (e.g. iOS dictation placeholders)
|
|
516
|
+
// are left alone — sanitizing them would delete characters the system
|
|
517
|
+
// manages itself, and emitting "unsupported" would raise a spurious error.
|
|
514
518
|
attributedText.enumerateAttribute(.attachment, in: NSRange(location: 0, length: attributedText.length), options: []) { value, range, _ in
|
|
515
519
|
guard let attachment = value as? NSTextAttachment else {
|
|
516
520
|
return
|
|
517
521
|
}
|
|
518
522
|
|
|
519
|
-
attachmentRanges.append(range)
|
|
520
|
-
|
|
521
523
|
if let payload = self.extractMediaPayload(from: attachment, textView: textView, range: range) {
|
|
524
|
+
attachmentRanges.append(range)
|
|
522
525
|
mediaPayloads.append(payload)
|
|
523
526
|
}
|
|
524
527
|
}
|
|
@@ -529,9 +532,8 @@ class ExpoPasteInputView: ExpoView {
|
|
|
529
532
|
return
|
|
530
533
|
}
|
|
531
534
|
|
|
532
|
-
attachmentRanges.append(range)
|
|
533
|
-
|
|
534
535
|
if let payload = self.extractMediaPayload(from: adaptiveGlyph) {
|
|
536
|
+
attachmentRanges.append(range)
|
|
535
537
|
mediaPayloads.append(payload)
|
|
536
538
|
}
|
|
537
539
|
}
|
|
@@ -539,17 +541,12 @@ class ExpoPasteInputView: ExpoView {
|
|
|
539
541
|
|
|
540
542
|
attachmentRanges = uniqueRanges(attachmentRanges)
|
|
541
543
|
|
|
542
|
-
guard !
|
|
544
|
+
guard !mediaPayloads.isEmpty else {
|
|
543
545
|
return
|
|
544
546
|
}
|
|
545
547
|
|
|
546
548
|
sanitizeAttachments(in: textView, ranges: attachmentRanges)
|
|
547
549
|
|
|
548
|
-
guard !mediaPayloads.isEmpty else {
|
|
549
|
-
handleUnsupportedPaste()
|
|
550
|
-
return
|
|
551
|
-
}
|
|
552
|
-
|
|
553
550
|
emitImagesAsync(for: mediaPayloads)
|
|
554
551
|
}
|
|
555
552
|
|
|
@@ -651,6 +648,11 @@ class ExpoPasteInputView: ExpoView {
|
|
|
651
648
|
}
|
|
652
649
|
|
|
653
650
|
private func extractMediaPayload(from attachment: NSTextAttachment, textView: UITextView, range: NSRange) -> MediaPayload? {
|
|
651
|
+
// Only accept attachments that carry real image payloads. We intentionally
|
|
652
|
+
// do not fall back to `image(forBounds:)` or rendering the text view's
|
|
653
|
+
// hierarchy, because system-inserted attachments (e.g. the iOS dictation
|
|
654
|
+
// placeholder) draw themselves via those paths and would cause us to
|
|
655
|
+
// emit a screenshot of the composer as a "pasted image".
|
|
654
656
|
if let fileWrapperData = attachment.fileWrapper?.regularFileContents,
|
|
655
657
|
let payload = extractMediaPayload(fromData: fileWrapperData) {
|
|
656
658
|
return payload
|
|
@@ -667,20 +669,6 @@ class ExpoPasteInputView: ExpoView {
|
|
|
667
669
|
return .image(image)
|
|
668
670
|
}
|
|
669
671
|
|
|
670
|
-
let attachmentBounds = attachment.bounds.size.width > 0 && attachment.bounds.size.height > 0
|
|
671
|
-
? attachment.bounds
|
|
672
|
-
: CGRect(origin: .zero, size: CGSize(width: 128, height: 128))
|
|
673
|
-
|
|
674
|
-
if let image = attachment.image(forBounds: attachmentBounds, textContainer: textView.textContainer, characterIndex: range.location),
|
|
675
|
-
image.size.width > 0,
|
|
676
|
-
image.size.height > 0 {
|
|
677
|
-
return .image(image)
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
if let renderedImage = renderTextAttachment(in: textView, range: range) {
|
|
681
|
-
return .image(renderedImage)
|
|
682
|
-
}
|
|
683
|
-
|
|
684
672
|
return nil
|
|
685
673
|
}
|
|
686
674
|
|
|
@@ -701,47 +689,6 @@ class ExpoPasteInputView: ExpoView {
|
|
|
701
689
|
return .imageData(data)
|
|
702
690
|
}
|
|
703
691
|
|
|
704
|
-
private func renderTextAttachment(in textView: UITextView, range: NSRange) -> UIImage? {
|
|
705
|
-
let glyphRange = textView.layoutManager.glyphRange(forCharacterRange: range, actualCharacterRange: nil)
|
|
706
|
-
var rect = textView.layoutManager.boundingRect(forGlyphRange: glyphRange, in: textView.textContainer)
|
|
707
|
-
|
|
708
|
-
rect.origin.x += textView.textContainerInset.left - textView.contentOffset.x
|
|
709
|
-
rect.origin.y += textView.textContainerInset.top - textView.contentOffset.y
|
|
710
|
-
rect = rect.integral
|
|
711
|
-
|
|
712
|
-
guard rect.width > 1, rect.height > 1 else {
|
|
713
|
-
return nil
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
let format = UIGraphicsImageRendererFormat.default()
|
|
717
|
-
format.scale = textView.window?.screen.scale ?? UIScreen.main.scale
|
|
718
|
-
format.opaque = false
|
|
719
|
-
|
|
720
|
-
let image = UIGraphicsImageRenderer(size: rect.size, format: format).image { _ in
|
|
721
|
-
let drawRect = CGRect(
|
|
722
|
-
origin: CGPoint(x: -rect.origin.x, y: -rect.origin.y),
|
|
723
|
-
size: textView.bounds.size
|
|
724
|
-
)
|
|
725
|
-
|
|
726
|
-
if textView.window != nil {
|
|
727
|
-
textView.drawHierarchy(in: drawRect, afterScreenUpdates: false)
|
|
728
|
-
} else {
|
|
729
|
-
guard let context = UIGraphicsGetCurrentContext() else {
|
|
730
|
-
return
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
context.translateBy(x: -rect.origin.x, y: -rect.origin.y)
|
|
734
|
-
textView.layer.render(in: context)
|
|
735
|
-
}
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
guard image.size.width > 0, image.size.height > 0 else {
|
|
739
|
-
return nil
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
return image
|
|
743
|
-
}
|
|
744
|
-
|
|
745
692
|
@available(iOS 18.0, *)
|
|
746
693
|
private func handleAdaptiveImageGlyphInsertion(_ adaptiveGlyph: NSAdaptiveImageGlyph) -> Bool {
|
|
747
694
|
guard let payload = extractMediaPayload(from: adaptiveGlyph) else {
|
|
@@ -910,32 +857,32 @@ class ExpoPasteInputView: ExpoView {
|
|
|
910
857
|
return headerBytes == gif87aSignature || headerBytes == gif89aSignature
|
|
911
858
|
}
|
|
912
859
|
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
860
|
+
private func inferredImageFileExtension(from imageSource: CGImageSource, fallbackData data: Data) -> String {
|
|
861
|
+
if let type = CGImageSourceGetType(imageSource) as String? {
|
|
862
|
+
switch type {
|
|
863
|
+
case "public.png":
|
|
864
|
+
return "png"
|
|
865
|
+
case "public.jpeg":
|
|
866
|
+
return "jpg"
|
|
867
|
+
case "public.gif":
|
|
868
|
+
return "gif"
|
|
869
|
+
case "public.webp", "org.webmproject.webp":
|
|
870
|
+
return "webp"
|
|
871
|
+
case "public.heic":
|
|
872
|
+
return "heic"
|
|
873
|
+
case "public.heif":
|
|
874
|
+
return "heif"
|
|
875
|
+
case "public.tiff":
|
|
876
|
+
return "tiff"
|
|
877
|
+
default:
|
|
878
|
+
break
|
|
879
|
+
}
|
|
931
880
|
}
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
return nil
|
|
881
|
+
|
|
882
|
+
if isGIFData(data) {
|
|
883
|
+
return "gif"
|
|
936
884
|
}
|
|
937
|
-
|
|
938
|
-
return image
|
|
885
|
+
return "png"
|
|
939
886
|
}
|
|
940
887
|
|
|
941
888
|
private func processTextPaste() {
|
|
@@ -1016,11 +963,22 @@ class ExpoPasteInputView: ExpoView {
|
|
|
1016
963
|
}
|
|
1017
964
|
|
|
1018
965
|
private func writeTemporaryImageData(_ data: Data) -> String? {
|
|
1019
|
-
guard let
|
|
966
|
+
guard let imageSource = CGImageSourceCreateWithData(data as CFData, nil),
|
|
967
|
+
CGImageSourceGetCount(imageSource) > 0 else {
|
|
1020
968
|
return nil
|
|
1021
969
|
}
|
|
1022
970
|
|
|
1023
|
-
|
|
971
|
+
let fileExtension = inferredImageFileExtension(from: imageSource, fallbackData: data)
|
|
972
|
+
let fileURL = FileManager.default.temporaryDirectory
|
|
973
|
+
.appendingPathComponent(UUID().uuidString)
|
|
974
|
+
.appendingPathExtension(fileExtension)
|
|
975
|
+
|
|
976
|
+
do {
|
|
977
|
+
try data.write(to: fileURL)
|
|
978
|
+
return fileURL.absoluteString
|
|
979
|
+
} catch {
|
|
980
|
+
return nil
|
|
981
|
+
}
|
|
1024
982
|
}
|
|
1025
983
|
|
|
1026
984
|
private func writeTemporaryImage(_ image: UIImage) -> String? {
|
package/package.json
CHANGED