@apollohg/react-native-prose-editor 0.5.17 → 0.5.19
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/src/main/java/com/apollohg/editor/EditorEditText.kt +950 -38
- package/android/src/main/java/com/apollohg/editor/EditorInputConnection.kt +408 -33
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoView.kt +104 -5
- package/dist/NativeRichTextEditor.js +37 -2
- package/ios/EditorAddons.swift +78 -6
- package/ios/EditorCore.xcframework/Info.plist +5 -5
- package/ios/EditorCore.xcframework/ios-arm64/libeditor_core.a +0 -0
- package/ios/EditorCore.xcframework/ios-arm64_x86_64-simulator/libeditor_core.a +0 -0
- package/ios/NativeEditorExpoView.swift +127 -18
- package/ios/RenderBridge.swift +3 -1
- package/ios/RichTextEditorView.swift +211 -14
- package/package.json +1 -1
- package/rust/android/arm64-v8a/libeditor_core.so +0 -0
- package/rust/android/armeabi-v7a/libeditor_core.so +0 -0
- package/rust/android/x86_64/libeditor_core.so +0 -0
|
@@ -827,6 +827,9 @@ final class EditorTextView: UITextView, UITextViewDelegate, UIGestureRecognizerD
|
|
|
827
827
|
let authorizedText: String
|
|
828
828
|
let selectionAnchor: UInt32?
|
|
829
829
|
let selectionHead: UInt32?
|
|
830
|
+
let authorizedSelectionUtf16Range: NSRange?
|
|
831
|
+
let rawSelectionUtf16Range: NSRange?
|
|
832
|
+
let selectionRevision: UInt64
|
|
830
833
|
let capturedWhileFirstResponder: Bool
|
|
831
834
|
let capturedWhileEditable: Bool
|
|
832
835
|
let capturedAfterBlur: Bool
|
|
@@ -991,6 +994,9 @@ final class EditorTextView: UITextView, UITextViewDelegate, UIGestureRecognizerD
|
|
|
991
994
|
private var nativeTextMutationAfterBlurDeadline: TimeInterval?
|
|
992
995
|
private var nativeTextMutationAfterBlurGeneration: UInt64?
|
|
993
996
|
private let nativeTextMutationAfterBlurGraceInterval: TimeInterval = 1.0
|
|
997
|
+
/// Last selection known to match `lastAuthorizedText`, stored in that text's UTF-16 coordinates.
|
|
998
|
+
private var lastAuthorizedSelectedUtf16Range: NSRange?
|
|
999
|
+
private var selectionRevision: UInt64 = 0
|
|
994
1000
|
private var desiredInputTraitState = InputTraitState()
|
|
995
1001
|
private var appliedInputTraitState = InputTraitState()
|
|
996
1002
|
private var pendingInputTraitChange = PendingInputTraitChange()
|
|
@@ -1399,6 +1405,7 @@ final class EditorTextView: UITextView, UITextViewDelegate, UIGestureRecognizerD
|
|
|
1399
1405
|
self?.ensureInternalTextViewDelegate()
|
|
1400
1406
|
}
|
|
1401
1407
|
_ = normalizeSelectionForEmptyBlockAutocapitalizationIfNeeded()
|
|
1408
|
+
recordAuthorizedSelectionIfPossible()
|
|
1402
1409
|
refreshTypingAttributesForSelection()
|
|
1403
1410
|
}
|
|
1404
1411
|
return didBecomeFirstResponder
|
|
@@ -1459,6 +1466,7 @@ final class EditorTextView: UITextView, UITextViewDelegate, UIGestureRecognizerD
|
|
|
1459
1466
|
let adjustedRange = NSRange(location: 0, length: 0)
|
|
1460
1467
|
guard currentRange != adjustedRange else { return false }
|
|
1461
1468
|
selectedRange = adjustedRange
|
|
1469
|
+
noteSelectionDidChange()
|
|
1462
1470
|
return true
|
|
1463
1471
|
}
|
|
1464
1472
|
|
|
@@ -1494,6 +1502,7 @@ final class EditorTextView: UITextView, UITextViewDelegate, UIGestureRecognizerD
|
|
|
1494
1502
|
|
|
1495
1503
|
_ = becomeFirstResponder()
|
|
1496
1504
|
selectedTextRange = textRange
|
|
1505
|
+
noteSelectionDidChange()
|
|
1497
1506
|
refreshNativeSelectionChromeVisibility()
|
|
1498
1507
|
onSelectionOrContentMayChange?()
|
|
1499
1508
|
scheduleSelectionSync()
|
|
@@ -1508,6 +1517,30 @@ final class EditorTextView: UITextView, UITextViewDelegate, UIGestureRecognizerD
|
|
|
1508
1517
|
return NSRange(location: location, length: length)
|
|
1509
1518
|
}
|
|
1510
1519
|
|
|
1520
|
+
private func noteSelectionDidChange() {
|
|
1521
|
+
selectionRevision &+= 1
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
private func recordAuthorizedSelectionIfPossible() {
|
|
1525
|
+
guard editorId != 0 else {
|
|
1526
|
+
lastAuthorizedSelectedUtf16Range = nil
|
|
1527
|
+
return
|
|
1528
|
+
}
|
|
1529
|
+
let currentText = textStorage.string
|
|
1530
|
+
guard currentText.utf16.count == lastAuthorizedTextStorage.length,
|
|
1531
|
+
currentText == lastAuthorizedText
|
|
1532
|
+
else {
|
|
1533
|
+
return
|
|
1534
|
+
}
|
|
1535
|
+
lastAuthorizedSelectedUtf16Range = selectedUtf16Range()
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
private func scalarRange(forUtf16Range range: NSRange) -> (from: UInt32, to: UInt32) {
|
|
1539
|
+
let start = PositionBridge.utf16OffsetToScalar(range.location, in: self)
|
|
1540
|
+
let end = PositionBridge.utf16OffsetToScalar(NSMaxRange(range), in: self)
|
|
1541
|
+
return (from: min(start, end), to: max(start, end))
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1511
1544
|
private func scheduleDeferredImageSelection(for range: NSRange) {
|
|
1512
1545
|
pendingDeferredImageSelectionRange = range
|
|
1513
1546
|
pendingDeferredImageSelectionGeneration &+= 1
|
|
@@ -2492,6 +2525,7 @@ final class EditorTextView: UITextView, UITextViewDelegate, UIGestureRecognizerD
|
|
|
2492
2525
|
func textViewDidChangeSelection(_ textView: UITextView) {
|
|
2493
2526
|
guard textView === self else { return }
|
|
2494
2527
|
ensureInternalTextViewDelegate()
|
|
2528
|
+
noteSelectionDidChange()
|
|
2495
2529
|
guard !isApplyingRustState,
|
|
2496
2530
|
!isComposing,
|
|
2497
2531
|
!nativeTextMutationCommitScheduled,
|
|
@@ -2502,6 +2536,7 @@ final class EditorTextView: UITextView, UITextViewDelegate, UIGestureRecognizerD
|
|
|
2502
2536
|
if normalizeSelectionForEmptyBlockAutocapitalizationIfNeeded() {
|
|
2503
2537
|
return
|
|
2504
2538
|
}
|
|
2539
|
+
recordAuthorizedSelectionIfPossible()
|
|
2505
2540
|
refreshNativeSelectionChromeVisibility()
|
|
2506
2541
|
onSelectionOrContentMayChange?()
|
|
2507
2542
|
scheduleSelectionSync()
|
|
@@ -2538,6 +2573,7 @@ final class EditorTextView: UITextView, UITextViewDelegate, UIGestureRecognizerD
|
|
|
2538
2573
|
}
|
|
2539
2574
|
|
|
2540
2575
|
selectedTextRange = textRange(from: start, to: end)
|
|
2576
|
+
noteSelectionDidChange()
|
|
2541
2577
|
refreshNativeSelectionChromeVisibility()
|
|
2542
2578
|
onSelectionOrContentMayChange?()
|
|
2543
2579
|
scheduleSelectionSync()
|
|
@@ -2710,6 +2746,7 @@ final class EditorTextView: UITextView, UITextViewDelegate, UIGestureRecognizerD
|
|
|
2710
2746
|
)
|
|
2711
2747
|
|
|
2712
2748
|
editorSetSelectionScalar(id: editorId, scalarAnchor: anchor, scalarHead: head)
|
|
2749
|
+
recordAuthorizedSelectionIfPossible()
|
|
2713
2750
|
refreshTypingAttributesForSelection()
|
|
2714
2751
|
editorDelegate?.editorTextView(self, selectionDidChange: docAnchor, head: docHead)
|
|
2715
2752
|
}
|
|
@@ -3339,9 +3376,17 @@ final class EditorTextView: UITextView, UITextViewDelegate, UIGestureRecognizerD
|
|
|
3339
3376
|
with: NSRange(location: prefix, length: replacementLength)
|
|
3340
3377
|
)
|
|
3341
3378
|
|
|
3342
|
-
let
|
|
3343
|
-
|
|
3344
|
-
|
|
3379
|
+
let rawSelectionUtf16Range = selectedUtf16Range()
|
|
3380
|
+
let authorizedSelectionUtf16Range = lastAuthorizedSelectedUtf16Range
|
|
3381
|
+
let targetSelectionUtf16Range = targetSelectionUtf16RangeForNativeTextMutation(
|
|
3382
|
+
rawSelectionUtf16Range: rawSelectionUtf16Range,
|
|
3383
|
+
authorizedSelectionUtf16Range: authorizedSelectionUtf16Range,
|
|
3384
|
+
replacementStartUtf16: prefix,
|
|
3385
|
+
authorizedEndUtf16: authorizedEnd,
|
|
3386
|
+
currentEndUtf16: currentEnd,
|
|
3387
|
+
currentTextUtf16Length: current.length
|
|
3388
|
+
)
|
|
3389
|
+
let selectedScalarRange = targetSelectionUtf16Range.map(scalarRange(forUtf16Range:))
|
|
3345
3390
|
let capturedAfterBlur = canAdoptNativeTextMutationAfterBlur()
|
|
3346
3391
|
|
|
3347
3392
|
return NativeTextMutation(
|
|
@@ -3352,6 +3397,9 @@ final class EditorTextView: UITextView, UITextViewDelegate, UIGestureRecognizerD
|
|
|
3352
3397
|
authorizedText: authorizedText,
|
|
3353
3398
|
selectionAnchor: selectedScalarRange?.from,
|
|
3354
3399
|
selectionHead: selectedScalarRange?.to,
|
|
3400
|
+
authorizedSelectionUtf16Range: authorizedSelectionUtf16Range,
|
|
3401
|
+
rawSelectionUtf16Range: rawSelectionUtf16Range,
|
|
3402
|
+
selectionRevision: selectionRevision,
|
|
3355
3403
|
capturedWhileFirstResponder: isFirstResponder || capturedAfterBlur,
|
|
3356
3404
|
capturedWhileEditable: isEditable,
|
|
3357
3405
|
capturedAfterBlur: capturedAfterBlur,
|
|
@@ -3362,9 +3410,37 @@ final class EditorTextView: UITextView, UITextViewDelegate, UIGestureRecognizerD
|
|
|
3362
3410
|
private func nativeTextMutationWithCurrentSelection(
|
|
3363
3411
|
_ mutation: NativeTextMutation
|
|
3364
3412
|
) -> NativeTextMutation {
|
|
3365
|
-
let
|
|
3366
|
-
|
|
3413
|
+
let currentSelectionUtf16Range = selectedUtf16Range()
|
|
3414
|
+
let didSelectionChangeAfterCapture = selectionRevision != mutation.selectionRevision
|
|
3415
|
+
let didCurrentRangeMoveAfterCapture: Bool
|
|
3416
|
+
if let currentSelectionUtf16Range,
|
|
3417
|
+
let rawSelectionUtf16Range = mutation.rawSelectionUtf16Range {
|
|
3418
|
+
didCurrentRangeMoveAfterCapture = !NSEqualRanges(
|
|
3419
|
+
currentSelectionUtf16Range,
|
|
3420
|
+
rawSelectionUtf16Range
|
|
3421
|
+
)
|
|
3422
|
+
} else {
|
|
3423
|
+
didCurrentRangeMoveAfterCapture = false
|
|
3424
|
+
}
|
|
3425
|
+
let currentSelectionDiffersFromAuthorized: Bool
|
|
3426
|
+
if let currentSelectionUtf16Range,
|
|
3427
|
+
let authorizedSelectionUtf16Range = mutation.authorizedSelectionUtf16Range {
|
|
3428
|
+
currentSelectionDiffersFromAuthorized = !NSEqualRanges(
|
|
3429
|
+
currentSelectionUtf16Range,
|
|
3430
|
+
authorizedSelectionUtf16Range
|
|
3431
|
+
)
|
|
3432
|
+
} else {
|
|
3433
|
+
currentSelectionDiffersFromAuthorized = currentSelectionUtf16Range != nil
|
|
3367
3434
|
}
|
|
3435
|
+
let shouldUseCurrentSelection = currentSelectionUtf16Range != nil
|
|
3436
|
+
&& (
|
|
3437
|
+
(didSelectionChangeAfterCapture && currentSelectionDiffersFromAuthorized)
|
|
3438
|
+
|| didCurrentRangeMoveAfterCapture
|
|
3439
|
+
|| mutation.rawSelectionUtf16Range == nil
|
|
3440
|
+
)
|
|
3441
|
+
let selectedScalarRange = shouldUseCurrentSelection
|
|
3442
|
+
? currentSelectionUtf16Range.map(scalarRange(forUtf16Range:))
|
|
3443
|
+
: nil
|
|
3368
3444
|
return NativeTextMutation(
|
|
3369
3445
|
from: mutation.from,
|
|
3370
3446
|
to: mutation.to,
|
|
@@ -3373,6 +3449,13 @@ final class EditorTextView: UITextView, UITextViewDelegate, UIGestureRecognizerD
|
|
|
3373
3449
|
authorizedText: mutation.authorizedText,
|
|
3374
3450
|
selectionAnchor: selectedScalarRange?.from ?? mutation.selectionAnchor,
|
|
3375
3451
|
selectionHead: selectedScalarRange?.to ?? mutation.selectionHead,
|
|
3452
|
+
authorizedSelectionUtf16Range: mutation.authorizedSelectionUtf16Range,
|
|
3453
|
+
rawSelectionUtf16Range: shouldUseCurrentSelection
|
|
3454
|
+
? currentSelectionUtf16Range
|
|
3455
|
+
: mutation.rawSelectionUtf16Range,
|
|
3456
|
+
selectionRevision: shouldUseCurrentSelection
|
|
3457
|
+
? selectionRevision
|
|
3458
|
+
: mutation.selectionRevision,
|
|
3376
3459
|
capturedWhileFirstResponder: mutation.capturedWhileFirstResponder,
|
|
3377
3460
|
capturedWhileEditable: mutation.capturedWhileEditable,
|
|
3378
3461
|
capturedAfterBlur: mutation.capturedAfterBlur,
|
|
@@ -3380,6 +3463,104 @@ final class EditorTextView: UITextView, UITextViewDelegate, UIGestureRecognizerD
|
|
|
3380
3463
|
)
|
|
3381
3464
|
}
|
|
3382
3465
|
|
|
3466
|
+
private func targetSelectionUtf16RangeForNativeTextMutation(
|
|
3467
|
+
rawSelectionUtf16Range: NSRange?,
|
|
3468
|
+
authorizedSelectionUtf16Range: NSRange?,
|
|
3469
|
+
replacementStartUtf16: Int,
|
|
3470
|
+
authorizedEndUtf16: Int,
|
|
3471
|
+
currentEndUtf16: Int,
|
|
3472
|
+
currentTextUtf16Length: Int
|
|
3473
|
+
) -> NSRange? {
|
|
3474
|
+
guard let authorizedSelection = authorizedSelectionUtf16Range else {
|
|
3475
|
+
return clampedUtf16Range(rawSelectionUtf16Range, length: currentTextUtf16Length)
|
|
3476
|
+
}
|
|
3477
|
+
guard authorizedSelection.location != NSNotFound else {
|
|
3478
|
+
return clampedUtf16Range(rawSelectionUtf16Range, length: currentTextUtf16Length)
|
|
3479
|
+
}
|
|
3480
|
+
|
|
3481
|
+
if let rawSelection = rawSelectionUtf16Range,
|
|
3482
|
+
rawSelection.location != NSNotFound,
|
|
3483
|
+
!NSEqualRanges(rawSelection, authorizedSelection) {
|
|
3484
|
+
return clampedUtf16Range(rawSelection, length: currentTextUtf16Length)
|
|
3485
|
+
}
|
|
3486
|
+
|
|
3487
|
+
if authorizedSelection.length == 0 {
|
|
3488
|
+
let mappedOffset = mapCollapsedAuthorizedSelectionOffsetThroughNativeTextMutation(
|
|
3489
|
+
authorizedSelection.location,
|
|
3490
|
+
replacementStartUtf16: replacementStartUtf16,
|
|
3491
|
+
authorizedEndUtf16: authorizedEndUtf16,
|
|
3492
|
+
currentEndUtf16: currentEndUtf16
|
|
3493
|
+
)
|
|
3494
|
+
let clampedOffset = min(max(mappedOffset, 0), currentTextUtf16Length)
|
|
3495
|
+
return NSRange(location: clampedOffset, length: 0)
|
|
3496
|
+
}
|
|
3497
|
+
|
|
3498
|
+
let mappedStart = mapAuthorizedSelectionOffsetThroughNativeTextMutation(
|
|
3499
|
+
authorizedSelection.location,
|
|
3500
|
+
replacementStartUtf16: replacementStartUtf16,
|
|
3501
|
+
authorizedEndUtf16: authorizedEndUtf16,
|
|
3502
|
+
currentEndUtf16: currentEndUtf16,
|
|
3503
|
+
isRangeStart: true
|
|
3504
|
+
)
|
|
3505
|
+
let mappedEnd = mapAuthorizedSelectionOffsetThroughNativeTextMutation(
|
|
3506
|
+
NSMaxRange(authorizedSelection),
|
|
3507
|
+
replacementStartUtf16: replacementStartUtf16,
|
|
3508
|
+
authorizedEndUtf16: authorizedEndUtf16,
|
|
3509
|
+
currentEndUtf16: currentEndUtf16,
|
|
3510
|
+
isRangeStart: false
|
|
3511
|
+
)
|
|
3512
|
+
let start = min(mappedStart, mappedEnd)
|
|
3513
|
+
let end = max(mappedStart, mappedEnd)
|
|
3514
|
+
let clampedStart = min(max(start, 0), currentTextUtf16Length)
|
|
3515
|
+
let clampedEnd = min(max(end, 0), currentTextUtf16Length)
|
|
3516
|
+
return NSRange(location: clampedStart, length: max(0, clampedEnd - clampedStart))
|
|
3517
|
+
}
|
|
3518
|
+
|
|
3519
|
+
private func clampedUtf16Range(_ range: NSRange?, length: Int) -> NSRange? {
|
|
3520
|
+
guard let range, range.location != NSNotFound else { return nil }
|
|
3521
|
+
let start = min(max(range.location, 0), length)
|
|
3522
|
+
let end = min(max(NSMaxRange(range), 0), length)
|
|
3523
|
+
return NSRange(location: min(start, end), length: abs(end - start))
|
|
3524
|
+
}
|
|
3525
|
+
|
|
3526
|
+
private func mapCollapsedAuthorizedSelectionOffsetThroughNativeTextMutation(
|
|
3527
|
+
_ offset: Int,
|
|
3528
|
+
replacementStartUtf16: Int,
|
|
3529
|
+
authorizedEndUtf16: Int,
|
|
3530
|
+
currentEndUtf16: Int
|
|
3531
|
+
) -> Int {
|
|
3532
|
+
// UIKit can leave a stale caret at the insertion point during autocomplete.
|
|
3533
|
+
// A collapsed authorized caret should stay collapsed after the inserted text.
|
|
3534
|
+
if replacementStartUtf16 == authorizedEndUtf16,
|
|
3535
|
+
offset == replacementStartUtf16,
|
|
3536
|
+
currentEndUtf16 > replacementStartUtf16 {
|
|
3537
|
+
return currentEndUtf16
|
|
3538
|
+
}
|
|
3539
|
+
if offset <= replacementStartUtf16 {
|
|
3540
|
+
return offset
|
|
3541
|
+
}
|
|
3542
|
+
if offset < authorizedEndUtf16 {
|
|
3543
|
+
return currentEndUtf16
|
|
3544
|
+
}
|
|
3545
|
+
return offset + currentEndUtf16 - authorizedEndUtf16
|
|
3546
|
+
}
|
|
3547
|
+
|
|
3548
|
+
private func mapAuthorizedSelectionOffsetThroughNativeTextMutation(
|
|
3549
|
+
_ offset: Int,
|
|
3550
|
+
replacementStartUtf16: Int,
|
|
3551
|
+
authorizedEndUtf16: Int,
|
|
3552
|
+
currentEndUtf16: Int,
|
|
3553
|
+
isRangeStart: Bool
|
|
3554
|
+
) -> Int {
|
|
3555
|
+
if offset <= replacementStartUtf16 {
|
|
3556
|
+
return offset
|
|
3557
|
+
}
|
|
3558
|
+
if offset >= authorizedEndUtf16 {
|
|
3559
|
+
return offset + currentEndUtf16 - authorizedEndUtf16
|
|
3560
|
+
}
|
|
3561
|
+
return isRangeStart ? replacementStartUtf16 : currentEndUtf16
|
|
3562
|
+
}
|
|
3563
|
+
|
|
3383
3564
|
private func isUtf16ScalarBoundary(_ offset: Int, in text: String) -> Bool {
|
|
3384
3565
|
guard offset >= 0, offset <= text.utf16.count else { return false }
|
|
3385
3566
|
let utf16Index = text.utf16.index(text.utf16.startIndex, offsetBy: offset)
|
|
@@ -3451,20 +3632,25 @@ final class EditorTextView: UITextView, UITextViewDelegate, UIGestureRecognizerD
|
|
|
3451
3632
|
clearNativeTextMutationAfterBlurWindow()
|
|
3452
3633
|
}
|
|
3453
3634
|
|
|
3635
|
+
private func resetPendingNativeTextMutationState() {
|
|
3636
|
+
pendingNativeTextMutation = nil
|
|
3637
|
+
nativeTextMutationCommitScheduled = false
|
|
3638
|
+
advanceNativeTextMutationGeneration()
|
|
3639
|
+
}
|
|
3640
|
+
|
|
3454
3641
|
func expireNativeTextMutationAfterBlurDeadlineForTesting() {
|
|
3455
3642
|
nativeTextMutationAfterBlurDeadline = ProcessInfo.processInfo.systemUptime - 0.001
|
|
3456
3643
|
}
|
|
3457
3644
|
|
|
3458
3645
|
func discardTransientNativeInputForEditorRebind() {
|
|
3459
|
-
|
|
3460
|
-
|
|
3646
|
+
resetPendingNativeTextMutationState()
|
|
3647
|
+
lastAuthorizedSelectedUtf16Range = nil
|
|
3461
3648
|
clearPendingInputTraitRetry()
|
|
3462
3649
|
markedTextReplacementScalarRange = nil
|
|
3463
3650
|
markedTextReplacementUtf16Range = nil
|
|
3464
3651
|
markedTextCompositionText = nil
|
|
3465
3652
|
markedTextCompositionIsExplicitlyEmpty = false
|
|
3466
3653
|
isComposing = false
|
|
3467
|
-
advanceNativeTextMutationGeneration()
|
|
3468
3654
|
}
|
|
3469
3655
|
|
|
3470
3656
|
@discardableResult
|
|
@@ -3528,7 +3714,7 @@ final class EditorTextView: UITextView, UITextViewDelegate, UIGestureRecognizerD
|
|
|
3528
3714
|
) -> Bool {
|
|
3529
3715
|
guard nativeTextMutationCommitScheduled
|
|
3530
3716
|
|| pendingNativeTextMutation != nil
|
|
3531
|
-
|| (!isComposing && textStorage.string != lastAuthorizedText)
|
|
3717
|
+
|| (!isComposing && markedTextRange == nil && textStorage.string != lastAuthorizedText)
|
|
3532
3718
|
else {
|
|
3533
3719
|
return true
|
|
3534
3720
|
}
|
|
@@ -3658,8 +3844,10 @@ final class EditorTextView: UITextView, UITextViewDelegate, UIGestureRecognizerD
|
|
|
3658
3844
|
let targetRange = NSRange(location: startUtf16, length: max(0, endUtf16 - startUtf16))
|
|
3659
3845
|
if selectedRange != targetRange {
|
|
3660
3846
|
selectedRange = targetRange
|
|
3847
|
+
noteSelectionDidChange()
|
|
3661
3848
|
}
|
|
3662
3849
|
editorSetSelectionScalar(id: editorId, scalarAnchor: anchor, scalarHead: head)
|
|
3850
|
+
recordAuthorizedSelectionIfPossible()
|
|
3663
3851
|
refreshTypingAttributesForSelection()
|
|
3664
3852
|
let docAnchor = editorScalarToDoc(id: editorId, scalar: anchor)
|
|
3665
3853
|
let docHead = editorScalarToDoc(id: editorId, scalar: head)
|
|
@@ -4783,9 +4971,7 @@ final class EditorTextView: UITextView, UITextViewDelegate, UIGestureRecognizerD
|
|
|
4783
4971
|
let update = try? JSONSerialization.jsonObject(with: data) as? [String: Any]
|
|
4784
4972
|
else { return }
|
|
4785
4973
|
let parseNanos = DispatchTime.now().uptimeNanoseconds - parseStartedAt
|
|
4786
|
-
|
|
4787
|
-
nativeTextMutationCommitScheduled = false
|
|
4788
|
-
advanceNativeTextMutationGeneration()
|
|
4974
|
+
resetPendingNativeTextMutationState()
|
|
4789
4975
|
|
|
4790
4976
|
let renderElements = update["renderElements"] as? [[String: Any]]
|
|
4791
4977
|
let selectionFromUpdate = (update["selection"] as? [String: Any])
|
|
@@ -4951,6 +5137,7 @@ final class EditorTextView: UITextView, UITextViewDelegate, UIGestureRecognizerD
|
|
|
4951
5137
|
postApplyTrace.selectionOrContentCallbackNanos
|
|
4952
5138
|
)
|
|
4953
5139
|
}
|
|
5140
|
+
recordAuthorizedSelectionIfPossible()
|
|
4954
5141
|
Self.updateLog.debug(
|
|
4955
5142
|
"[applyUpdateJSON.end] finalSelection=\(self.selectionSummary(), privacy: .public) textState=\(self.textSnapshotSummary(), privacy: .public)"
|
|
4956
5143
|
)
|
|
@@ -4967,6 +5154,7 @@ final class EditorTextView: UITextView, UITextViewDelegate, UIGestureRecognizerD
|
|
|
4967
5154
|
/// elements directly, not wrapped in an EditorUpdate).
|
|
4968
5155
|
func applyRenderJSON(_ renderJSON: String) {
|
|
4969
5156
|
ensureInternalTextViewDelegate()
|
|
5157
|
+
resetPendingNativeTextMutationState()
|
|
4970
5158
|
Self.updateLog.debug(
|
|
4971
5159
|
"[applyRenderJSON.begin] before=\(self.textSnapshotSummary(), privacy: .public)"
|
|
4972
5160
|
)
|
|
@@ -4982,6 +5170,7 @@ final class EditorTextView: UITextView, UITextViewDelegate, UIGestureRecognizerD
|
|
|
4982
5170
|
|
|
4983
5171
|
refreshPlaceholderVisibility()
|
|
4984
5172
|
_ = performPostApplyMaintenance()
|
|
5173
|
+
recordAuthorizedSelectionIfPossible()
|
|
4985
5174
|
Self.updateLog.debug(
|
|
4986
5175
|
"[applyRenderJSON.end] after=\(self.textSnapshotSummary(), privacy: .public)"
|
|
4987
5176
|
)
|
|
@@ -5039,17 +5228,20 @@ final class EditorTextView: UITextView, UITextViewDelegate, UIGestureRecognizerD
|
|
|
5039
5228
|
let adjustedRange = NSRange(location: adjustedOffset, length: 0)
|
|
5040
5229
|
if selectedRange != adjustedRange {
|
|
5041
5230
|
selectedRange = adjustedRange
|
|
5231
|
+
noteSelectionDidChange()
|
|
5042
5232
|
}
|
|
5043
5233
|
} else {
|
|
5044
5234
|
let targetRange = NSRange(location: endUtf16, length: 0)
|
|
5045
5235
|
if selectedRange != targetRange {
|
|
5046
5236
|
selectedRange = targetRange
|
|
5237
|
+
noteSelectionDidChange()
|
|
5047
5238
|
}
|
|
5048
5239
|
}
|
|
5049
5240
|
} else {
|
|
5050
5241
|
let targetRange = NSRange(location: startUtf16, length: endUtf16 - startUtf16)
|
|
5051
5242
|
if selectedRange != targetRange {
|
|
5052
5243
|
selectedRange = targetRange
|
|
5244
|
+
noteSelectionDidChange()
|
|
5053
5245
|
}
|
|
5054
5246
|
}
|
|
5055
5247
|
let assignmentNanos = DispatchTime.now().uptimeNanoseconds - assignmentStartedAt
|
|
@@ -5081,6 +5273,7 @@ final class EditorTextView: UITextView, UITextViewDelegate, UIGestureRecognizerD
|
|
|
5081
5273
|
let assignmentStartedAt = DispatchTime.now().uptimeNanoseconds
|
|
5082
5274
|
if selectedRange != targetRange {
|
|
5083
5275
|
selectedRange = targetRange
|
|
5276
|
+
noteSelectionDidChange()
|
|
5084
5277
|
}
|
|
5085
5278
|
let assignmentNanos = DispatchTime.now().uptimeNanoseconds - assignmentStartedAt
|
|
5086
5279
|
let chromeStartedAt = DispatchTime.now().uptimeNanoseconds
|
|
@@ -5099,6 +5292,7 @@ final class EditorTextView: UITextView, UITextViewDelegate, UIGestureRecognizerD
|
|
|
5099
5292
|
case "all":
|
|
5100
5293
|
let assignmentStartedAt = DispatchTime.now().uptimeNanoseconds
|
|
5101
5294
|
selectedTextRange = textRange(from: beginningOfDocument, to: endOfDocument)
|
|
5295
|
+
noteSelectionDidChange()
|
|
5102
5296
|
let assignmentNanos = DispatchTime.now().uptimeNanoseconds - assignmentStartedAt
|
|
5103
5297
|
let chromeStartedAt = DispatchTime.now().uptimeNanoseconds
|
|
5104
5298
|
showNativeSelectionChromeIfNeeded()
|
|
@@ -5150,8 +5344,11 @@ extension EditorTextView: NSTextStorageDelegate {
|
|
|
5150
5344
|
// Only care about actual character edits, not attribute-only changes.
|
|
5151
5345
|
guard editedMask.contains(.editedCharacters) else { return }
|
|
5152
5346
|
|
|
5153
|
-
// Skip if this change came from our own Rust apply path
|
|
5154
|
-
|
|
5347
|
+
// Skip if this change came from our own Rust apply path, transient IME
|
|
5348
|
+
// composition, or an inline prediction. iOS inline predictions (iOS 17+)
|
|
5349
|
+
// mutate textStorage directly and set markedTextRange without calling
|
|
5350
|
+
// setMarkedText, so isComposing remains false — check markedTextRange too.
|
|
5351
|
+
guard !isApplyingRustState, !isComposing, markedTextRange == nil else { return }
|
|
5155
5352
|
|
|
5156
5353
|
// Skip if no editor is bound yet (nothing to reconcile against).
|
|
5157
5354
|
guard editorId != 0 else { return }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@apollohg/react-native-prose-editor",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.19",
|
|
4
4
|
"description": "Native rich text editor with Rust core for React Native",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://github.com/apollohg/react-native-prose-editor",
|
|
Binary file
|
|
Binary file
|
|
Binary file
|