react-native-video-trim 6.2.3 → 7.0.1

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.
@@ -77,6 +77,29 @@ class VideoTrimmerViewController: UIViewController {
77
77
  private var headerTextColor: Double?
78
78
  private var headerView: UIView?
79
79
 
80
+ private(set) var rotationCount = 0
81
+ private(set) var isFlipped = false
82
+ private var isVideoType = true
83
+ private var playerContainerView: UIView!
84
+ private var transformStackView: UIStackView?
85
+
86
+ private var cropBtn: UIButton?
87
+ private var cropOverlayView: CropOverlayView?
88
+ private(set) var isCropActive = false
89
+
90
+ private struct TransformSnapshot: Equatable {
91
+ let rotationCount: Int
92
+ let isFlipped: Bool
93
+ let isCropActive: Bool
94
+ let cropNormalized: CGRect?
95
+ }
96
+ private var undoStack: [TransformSnapshot] = []
97
+ private var redoStack: [TransformSnapshot] = []
98
+ private var undoBtn: UIButton?
99
+ private var redoBtn: UIButton?
100
+ private var preCropSnapshot: TransformSnapshot?
101
+
102
+
80
103
  var isSeekInProgress: Bool = false // Marker
81
104
  private var chaseTime = CMTime.zero
82
105
  private var preferredFrameRate: Float = 23.98
@@ -260,31 +283,44 @@ class VideoTrimmerViewController: UIViewController {
260
283
  headerView = UIView()
261
284
  headerView!.translatesAutoresizingMaskIntoConstraints = false
262
285
  view.addSubview(headerView!)
263
- let headerTextView = UITextView()
264
- headerTextView.text = headerText
265
- headerTextView.textAlignment = .center
266
-
267
- headerTextView.textColor = RCTConvert.uiColor(headerTextColor)
268
- // UIColor.color(fromHexNumber: headerTextColor as NSNumber?, defaultColor: .white)
269
-
270
- headerTextView.font = UIFont.systemFont(ofSize: CGFloat(headerTextSize)) // Set font size here
271
- headerTextView.translatesAutoresizingMaskIntoConstraints = false
272
- headerView!.addSubview(headerTextView)
286
+ let scrollView = UIScrollView()
287
+ scrollView.showsHorizontalScrollIndicator = false
288
+ scrollView.showsVerticalScrollIndicator = false
289
+ scrollView.translatesAutoresizingMaskIntoConstraints = false
290
+ headerView!.addSubview(scrollView)
291
+
292
+ let headerLabel = UILabel()
293
+ headerLabel.text = headerText
294
+ headerLabel.textAlignment = .center
295
+ headerLabel.textColor = RCTConvert.uiColor(headerTextColor)
296
+ headerLabel.font = UIFont.systemFont(ofSize: CGFloat(headerTextSize))
297
+ headerLabel.numberOfLines = 1
298
+ headerLabel.translatesAutoresizingMaskIntoConstraints = false
299
+ scrollView.addSubview(headerLabel)
273
300
 
274
301
  NSLayoutConstraint.activate([
275
- // HeaderView constraints
276
302
  headerView!.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
277
303
  headerView!.leadingAnchor.constraint(equalTo: view.leadingAnchor),
278
304
  headerView!.trailingAnchor.constraint(equalTo: view.trailingAnchor),
279
- headerView!.heightAnchor.constraint(greaterThanOrEqualToConstant: 50),
280
305
 
281
- // HeaderText constraints
282
- headerTextView.topAnchor.constraint(equalTo: headerView!.topAnchor),
283
- headerTextView.bottomAnchor.constraint(equalTo: headerView!.bottomAnchor),
284
- headerTextView.leadingAnchor.constraint(equalTo: headerView!.leadingAnchor),
285
- headerTextView.trailingAnchor.constraint(equalTo: headerView!.trailingAnchor),
306
+ scrollView.topAnchor.constraint(equalTo: headerView!.topAnchor, constant: 6),
307
+ scrollView.bottomAnchor.constraint(equalTo: headerView!.bottomAnchor, constant: -2),
308
+ scrollView.leadingAnchor.constraint(equalTo: headerView!.leadingAnchor),
309
+ scrollView.trailingAnchor.constraint(equalTo: headerView!.trailingAnchor),
310
+ scrollView.heightAnchor.constraint(equalTo: headerLabel.heightAnchor),
311
+
312
+ headerLabel.topAnchor.constraint(equalTo: scrollView.topAnchor),
313
+ headerLabel.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
314
+ headerLabel.leadingAnchor.constraint(greaterThanOrEqualTo: scrollView.leadingAnchor, constant: 16),
315
+ headerLabel.trailingAnchor.constraint(lessThanOrEqualTo: scrollView.trailingAnchor, constant: -16),
286
316
  ])
287
317
 
318
+ let centerX = headerLabel.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor)
319
+ centerX.priority = .defaultHigh
320
+ let minWidth = headerLabel.widthAnchor.constraint(greaterThanOrEqualTo: scrollView.widthAnchor, constant: -32)
321
+ minWidth.priority = .defaultLow
322
+ NSLayoutConstraint.activate([centerX, minWidth])
323
+
288
324
  view.layoutIfNeeded() // layout after activate constraints, otherwise headerView height = screen height, which leads to playerViewController is missing at runtime
289
325
  }
290
326
  }
@@ -407,14 +443,38 @@ class VideoTrimmerViewController: UIViewController {
407
443
  }
408
444
 
409
445
  try? AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, options: [])
446
+
447
+ setupTransformButtons()
448
+
449
+ let topAnchor: NSLayoutYAxisAnchor
450
+ if let transformStack = transformStackView {
451
+ topAnchor = transformStack.bottomAnchor
452
+ } else if let headerView = headerView {
453
+ topAnchor = headerView.bottomAnchor
454
+ } else {
455
+ topAnchor = view.safeAreaLayoutGuide.topAnchor
456
+ }
457
+
458
+ playerContainerView = UIView()
459
+ playerContainerView.clipsToBounds = true
460
+ view.addSubview(playerContainerView)
461
+ playerContainerView.translatesAutoresizingMaskIntoConstraints = false
462
+ NSLayoutConstraint.activate([
463
+ playerContainerView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
464
+ playerContainerView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
465
+ playerContainerView.topAnchor.constraint(equalTo: topAnchor, constant: 4),
466
+ playerContainerView.bottomAnchor.constraint(equalTo: trimmer.topAnchor, constant: -16)
467
+ ])
468
+
410
469
  addChild(playerController)
411
- view.addSubview(playerController.view)
470
+ playerContainerView.addSubview(playerController.view)
412
471
  playerController.view.translatesAutoresizingMaskIntoConstraints = false
472
+ let bracketInset: CGFloat = 4
413
473
  NSLayoutConstraint.activate([
414
- playerController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
415
- playerController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
416
- playerController.view.topAnchor.constraint(equalTo: headerView != nil ? headerView!.bottomAnchor : view.safeAreaLayoutGuide.topAnchor),
417
- playerController.view.bottomAnchor.constraint(equalTo: trimmer.topAnchor, constant: -16)
474
+ playerController.view.leadingAnchor.constraint(equalTo: playerContainerView.leadingAnchor, constant: bracketInset),
475
+ playerController.view.trailingAnchor.constraint(equalTo: playerContainerView.trailingAnchor, constant: -bracketInset),
476
+ playerController.view.topAnchor.constraint(equalTo: playerContainerView.topAnchor, constant: bracketInset),
477
+ playerController.view.bottomAnchor.constraint(equalTo: playerContainerView.bottomAnchor, constant: -bracketInset)
418
478
  ])
419
479
 
420
480
  // Add observer for the end of playback
@@ -427,6 +487,364 @@ class VideoTrimmerViewController: UIViewController {
427
487
  playBtn.setImage(self.playIcon, for: .normal)
428
488
  }
429
489
 
490
+ // MARK: - Transform (Rotation/Flip/Crop) + Undo/Redo
491
+ private func setupTransformButtons() {
492
+ guard isVideoType else { return }
493
+
494
+ let symbolConfig = UIImage.SymbolConfiguration(pointSize: 14, weight: .medium)
495
+ let dimmed = UIColor.white.withAlphaComponent(0.5)
496
+
497
+ let flipBtn = UIButton(type: .system)
498
+ flipBtn.setImage(UIImage(systemName: "arrow.trianglehead.left.and.right.righttriangle.left.righttriangle.right", withConfiguration: symbolConfig), for: .normal)
499
+ flipBtn.tintColor = .white
500
+ flipBtn.addTarget(self, action: #selector(onFlipTapped), for: .touchUpInside)
501
+
502
+ let rotateBtn = UIButton(type: .system)
503
+ rotateBtn.setImage(UIImage(systemName: "rotate.left", withConfiguration: symbolConfig), for: .normal)
504
+ rotateBtn.tintColor = .white
505
+ rotateBtn.addTarget(self, action: #selector(onRotateTapped), for: .touchUpInside)
506
+
507
+ let cropButton = UIButton(type: .system)
508
+ cropButton.setImage(UIImage(systemName: "crop", withConfiguration: symbolConfig), for: .normal)
509
+ cropButton.tintColor = UIColor.white.withAlphaComponent(0.5)
510
+ cropButton.addTarget(self, action: #selector(onCropTapped), for: .touchUpInside)
511
+ self.cropBtn = cropButton
512
+
513
+ let undoButton = UIButton(type: .system)
514
+ undoButton.setImage(UIImage(systemName: "arrow.uturn.backward", withConfiguration: symbolConfig), for: .normal)
515
+ undoButton.tintColor = dimmed
516
+ undoButton.isEnabled = false
517
+ undoButton.addTarget(self, action: #selector(onUndoTapped), for: .touchUpInside)
518
+ self.undoBtn = undoButton
519
+
520
+ let redoButton = UIButton(type: .system)
521
+ redoButton.setImage(UIImage(systemName: "arrow.uturn.forward", withConfiguration: symbolConfig), for: .normal)
522
+ redoButton.tintColor = dimmed
523
+ redoButton.isEnabled = false
524
+ redoButton.addTarget(self, action: #selector(onRedoTapped), for: .touchUpInside)
525
+ self.redoBtn = redoButton
526
+
527
+ let leftStack = UIStackView(arrangedSubviews: [flipBtn, rotateBtn, cropButton])
528
+ leftStack.axis = .horizontal
529
+ leftStack.spacing = 12
530
+
531
+ let rightStack = UIStackView(arrangedSubviews: [undoButton, redoButton])
532
+ rightStack.axis = .horizontal
533
+ rightStack.spacing = 12
534
+
535
+ let spacer = UIView()
536
+ spacer.setContentHuggingPriority(.defaultLow, for: .horizontal)
537
+
538
+ let fullRow = UIStackView(arrangedSubviews: [leftStack, spacer, rightStack])
539
+ fullRow.axis = .horizontal
540
+ fullRow.translatesAutoresizingMaskIntoConstraints = false
541
+ fullRow.alpha = 0
542
+
543
+ view.addSubview(fullRow)
544
+ let btnSize: CGFloat = 28
545
+ NSLayoutConstraint.activate([
546
+ flipBtn.widthAnchor.constraint(equalToConstant: btnSize),
547
+ flipBtn.heightAnchor.constraint(equalToConstant: btnSize),
548
+ rotateBtn.widthAnchor.constraint(equalToConstant: btnSize),
549
+ rotateBtn.heightAnchor.constraint(equalToConstant: btnSize),
550
+ cropButton.widthAnchor.constraint(equalToConstant: btnSize),
551
+ cropButton.heightAnchor.constraint(equalToConstant: btnSize),
552
+ undoButton.widthAnchor.constraint(equalToConstant: btnSize),
553
+ undoButton.heightAnchor.constraint(equalToConstant: btnSize),
554
+ redoButton.widthAnchor.constraint(equalToConstant: btnSize),
555
+ redoButton.heightAnchor.constraint(equalToConstant: btnSize),
556
+ fullRow.topAnchor.constraint(equalTo: headerView != nil ? headerView!.bottomAnchor : view.safeAreaLayoutGuide.topAnchor, constant: 4),
557
+ fullRow.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 16),
558
+ fullRow.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -16),
559
+ ])
560
+
561
+ self.transformStackView = fullRow
562
+ }
563
+
564
+ @objc private func onFlipTapped() {
565
+ pushUndo()
566
+ isFlipped.toggle()
567
+ if enableHapticFeedback {
568
+ UIImpactFeedbackGenerator(style: .light).impactOccurred()
569
+ }
570
+ updateVideoTransform(resetCrop: true)
571
+ }
572
+
573
+ @objc private func onRotateTapped() {
574
+ pushUndo()
575
+ if isFlipped {
576
+ rotationCount = (rotationCount - 1 + 4) % 4
577
+ } else {
578
+ rotationCount = (rotationCount + 1) % 4
579
+ }
580
+ if enableHapticFeedback {
581
+ UIImpactFeedbackGenerator(style: .light).impactOccurred()
582
+ }
583
+ updateVideoTransform(resetCrop: true)
584
+ }
585
+
586
+ private func updateVideoTransform(resetCrop: Bool = false) {
587
+ let angle = -CGFloat(rotationCount) * (.pi / 2)
588
+ var transform = CGAffineTransform.identity
589
+
590
+ if isFlipped {
591
+ transform = transform.scaledBy(x: -1, y: 1)
592
+ }
593
+ transform = transform.rotated(by: angle)
594
+
595
+ if rotationCount % 2 != 0 {
596
+ let bounds = playerContainerView.bounds
597
+ if bounds.width > 0 && bounds.height > 0 {
598
+ let fitScale = min(bounds.width / bounds.height, bounds.height / bounds.width)
599
+ transform = transform.scaledBy(x: fitScale, y: fitScale)
600
+ }
601
+ }
602
+
603
+ UIView.animate(withDuration: 0.3, delay: 0, options: [.curveEaseInOut]) {
604
+ self.playerController.view.transform = transform
605
+ } completion: { _ in
606
+ if resetCrop && self.isCropActive {
607
+ self.updateCropAllowedRect()
608
+ self.cropOverlayView?.resetCrop()
609
+ }
610
+ }
611
+ }
612
+
613
+ // MARK: - Undo / Redo
614
+
615
+ private func currentSnapshot() -> TransformSnapshot {
616
+ TransformSnapshot(
617
+ rotationCount: rotationCount,
618
+ isFlipped: isFlipped,
619
+ isCropActive: isCropActive,
620
+ cropNormalized: cropNormalizedRect
621
+ )
622
+ }
623
+
624
+ private func pushUndo() {
625
+ undoStack.append(currentSnapshot())
626
+ redoStack.removeAll()
627
+ updateUndoRedoButtons()
628
+ }
629
+
630
+ @objc private func onUndoTapped() {
631
+ guard let prev = undoStack.popLast() else { return }
632
+ redoStack.append(currentSnapshot())
633
+ applySnapshot(prev)
634
+ updateUndoRedoButtons()
635
+ }
636
+
637
+ @objc private func onRedoTapped() {
638
+ guard let next = redoStack.popLast() else { return }
639
+ undoStack.append(currentSnapshot())
640
+ applySnapshot(next)
641
+ updateUndoRedoButtons()
642
+ }
643
+
644
+ private func applySnapshot(_ snap: TransformSnapshot) {
645
+ rotationCount = snap.rotationCount
646
+ isFlipped = snap.isFlipped
647
+
648
+ let angle = -CGFloat(rotationCount) * (.pi / 2)
649
+ var transform = CGAffineTransform.identity
650
+ if isFlipped { transform = transform.scaledBy(x: -1, y: 1) }
651
+ transform = transform.rotated(by: angle)
652
+ if rotationCount % 2 != 0 {
653
+ let bounds = playerContainerView.bounds
654
+ if bounds.width > 0 && bounds.height > 0 {
655
+ let fitScale = min(bounds.width / bounds.height, bounds.height / bounds.width)
656
+ transform = transform.scaledBy(x: fitScale, y: fitScale)
657
+ }
658
+ }
659
+
660
+ let wasActive = isCropActive
661
+ isCropActive = snap.isCropActive
662
+ cropBtn?.tintColor = isCropActive ? .white : UIColor.white.withAlphaComponent(0.5)
663
+
664
+ UIView.animate(withDuration: 0.3, delay: 0, options: [.curveEaseInOut]) {
665
+ self.playerController.view.transform = transform
666
+ } completion: { _ in
667
+ if self.isCropActive {
668
+ self.showCropOverlayImmediate()
669
+ self.updateCropAllowedRect()
670
+ if let norm = snap.cropNormalized {
671
+ self.setCropFromNormalized(norm)
672
+ } else {
673
+ self.cropOverlayView?.resetCrop()
674
+ }
675
+ } else if wasActive {
676
+ self.cropOverlayView?.isHidden = true
677
+ self.cropOverlayView?.alpha = 0
678
+ }
679
+ }
680
+ }
681
+
682
+ private func setCropFromNormalized(_ norm: CGRect) {
683
+ let vr = getVideoDisplayRectInContainer()
684
+ guard vr.width > 1, vr.height > 1 else { return }
685
+ cropOverlayView?.cropRect = CGRect(
686
+ x: vr.minX + norm.origin.x * vr.width,
687
+ y: vr.minY + norm.origin.y * vr.height,
688
+ width: norm.size.width * vr.width,
689
+ height: norm.size.height * vr.height
690
+ )
691
+ }
692
+
693
+ private func updateUndoRedoButtons() {
694
+ let dimmed = UIColor.white.withAlphaComponent(0.5)
695
+ undoBtn?.tintColor = undoStack.isEmpty ? dimmed : .white
696
+ undoBtn?.isEnabled = !undoStack.isEmpty
697
+ redoBtn?.tintColor = redoStack.isEmpty ? dimmed : .white
698
+ redoBtn?.isEnabled = !redoStack.isEmpty
699
+ }
700
+
701
+ // MARK: - Crop
702
+
703
+ @objc private func onCropTapped() {
704
+ isCropActive.toggle()
705
+ cropBtn?.tintColor = isCropActive ? .white : UIColor.white.withAlphaComponent(0.5)
706
+
707
+ if enableHapticFeedback {
708
+ UIImpactFeedbackGenerator(style: .light).impactOccurred()
709
+ }
710
+
711
+ if isCropActive {
712
+ showCropOverlay()
713
+ } else {
714
+ hideCropOverlay()
715
+ }
716
+ }
717
+
718
+ private func showCropOverlay() {
719
+ if let existing = cropOverlayView {
720
+ existing.isHidden = false
721
+ existing.alpha = 0
722
+ playerContainerView.layoutIfNeeded()
723
+ updateCropAllowedRect()
724
+ UIView.animate(withDuration: 0.2) { existing.alpha = 1 }
725
+ return
726
+ }
727
+
728
+ createCropOverlay()
729
+ playerContainerView.layoutIfNeeded()
730
+ updateCropAllowedRect()
731
+ UIView.animate(withDuration: 0.2) { self.cropOverlayView?.alpha = 1 }
732
+ }
733
+
734
+ private func showCropOverlayImmediate() {
735
+ if let existing = cropOverlayView {
736
+ existing.isHidden = false
737
+ existing.alpha = 1
738
+ return
739
+ }
740
+ createCropOverlay()
741
+ cropOverlayView?.alpha = 1
742
+ }
743
+
744
+ private func createCropOverlay() {
745
+ let overlay = CropOverlayView()
746
+ overlay.translatesAutoresizingMaskIntoConstraints = false
747
+ overlay.alpha = 0
748
+ playerContainerView.addSubview(overlay)
749
+ NSLayoutConstraint.activate([
750
+ overlay.leadingAnchor.constraint(equalTo: playerContainerView.leadingAnchor),
751
+ overlay.trailingAnchor.constraint(equalTo: playerContainerView.trailingAnchor),
752
+ overlay.topAnchor.constraint(equalTo: playerContainerView.topAnchor),
753
+ overlay.bottomAnchor.constraint(equalTo: playerContainerView.bottomAnchor),
754
+ ])
755
+ cropOverlayView = overlay
756
+
757
+ overlay.onCropBegan = { [weak self] in
758
+ self?.preCropSnapshot = self?.currentSnapshot()
759
+ }
760
+ overlay.onCropEnded = { [weak self] in
761
+ guard let self = self, let snap = self.preCropSnapshot else { return }
762
+ if self.currentSnapshot() != snap {
763
+ self.undoStack.append(snap)
764
+ self.redoStack.removeAll()
765
+ self.updateUndoRedoButtons()
766
+ }
767
+ self.preCropSnapshot = nil
768
+ }
769
+ }
770
+
771
+ private func hideCropOverlay() {
772
+ guard let overlay = cropOverlayView else { return }
773
+ UIView.animate(withDuration: 0.2, animations: {
774
+ overlay.alpha = 0
775
+ }) { _ in
776
+ overlay.isHidden = true
777
+ }
778
+ }
779
+
780
+ private func updateCropAllowedRect() {
781
+ guard let overlay = cropOverlayView else { return }
782
+ overlay.allowedRect = getVideoDisplayRectInContainer()
783
+ }
784
+
785
+ func getVideoDisplayRectInContainer() -> CGRect {
786
+ guard let containerView = playerContainerView,
787
+ let asset = asset,
788
+ let track = asset.tracks(withMediaType: .video).first else {
789
+ return playerContainerView?.bounds ?? .zero
790
+ }
791
+
792
+ let raw = track.naturalSize
793
+ let pt = track.preferredTransform
794
+ let angle = atan2(pt.b, pt.a)
795
+ let isSourceRotated = abs(angle - .pi / 2) < 0.1 || abs(angle + .pi / 2) < 0.1
796
+ let displayedSize = isSourceRotated
797
+ ? CGSize(width: raw.height, height: raw.width)
798
+ : raw
799
+
800
+ let pvBounds = playerController.view.bounds
801
+ guard pvBounds.width > 0, pvBounds.height > 0,
802
+ displayedSize.width > 0, displayedSize.height > 0 else {
803
+ return containerView.bounds
804
+ }
805
+
806
+ let videoAR = displayedSize.width / displayedSize.height
807
+ let viewAR = pvBounds.width / pvBounds.height
808
+
809
+ var videoRect: CGRect
810
+ if videoAR > viewAR {
811
+ let h = pvBounds.width / videoAR
812
+ videoRect = CGRect(x: 0, y: (pvBounds.height - h) / 2,
813
+ width: pvBounds.width, height: h)
814
+ } else {
815
+ let w = pvBounds.height * videoAR
816
+ videoRect = CGRect(x: (pvBounds.width - w) / 2, y: 0,
817
+ width: w, height: pvBounds.height)
818
+ }
819
+
820
+ return playerController.view.convert(videoRect, to: containerView)
821
+ }
822
+
823
+ var cropNormalizedRect: CGRect? {
824
+ guard isCropActive,
825
+ let overlay = cropOverlayView, !overlay.isHidden else { return nil }
826
+
827
+ let videoRect = getVideoDisplayRectInContainer()
828
+ guard videoRect.width > 1, videoRect.height > 1 else { return nil }
829
+
830
+ let cr = overlay.cropRect
831
+ let nx = (cr.minX - videoRect.minX) / videoRect.width
832
+ let ny = (cr.minY - videoRect.minY) / videoRect.height
833
+ let nw = cr.width / videoRect.width
834
+ let nh = cr.height / videoRect.height
835
+
836
+ if nx < 0.01 && ny < 0.01 && nw > 0.99 && nh > 0.99 {
837
+ return nil
838
+ }
839
+
840
+ return CGRect(
841
+ x: max(0, min(1, nx)),
842
+ y: max(0, min(1, ny)),
843
+ width: max(0, min(1, nw)),
844
+ height: max(0, min(1, nh))
845
+ )
846
+ }
847
+
430
848
  private func setupTimeObserver() {
431
849
  timeObserverToken = player.addPeriodicTimeObserver(forInterval: CMTime(value: 1, timescale: 30), queue: .main) { [weak self] time in
432
850
  guard let self = self else { return }
@@ -504,6 +922,7 @@ class VideoTrimmerViewController: UIViewController {
504
922
  enableHapticFeedback = config["enableHapticFeedback"] as? Bool ?? true
505
923
  zoomOnWaitingDuration = (config["zoomOnWaitingDuration"] as? Double ?? 5.0) / 1000.0 // convert ms to s
506
924
  autoplay = config["autoplay"] as? Bool ?? false
925
+ isVideoType = (config["type"] as? String ?? "video") == "video"
507
926
  headerText = config["headerText"] as? String
508
927
  headerTextSize = config["headerTextSize"] as? Int ?? 16
509
928
  headerTextColor = config["headerTextColor"] as? Double
@@ -530,6 +949,7 @@ class VideoTrimmerViewController: UIViewController {
530
949
  self.playBtn.isEnabled = true
531
950
  self.saveBtn.alpha = 1
532
951
  self.saveBtn.isEnabled = true
952
+ self.transformStackView?.alpha = 1
533
953
  })
534
954
 
535
955
  if jumpToPositionOnLoad > 0 {
@@ -1 +1 @@
1
- {"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeVideoTrim.ts"],"mappings":";;AACA,SAASA,mBAAmB,QAAQ,cAAc;;AAGlD;AACA;AACA;;AAkBA;AACA;AACA;;AAgGA;AACA;AACA;;AAQA;AACA;AACA;;AAUA;AACA;AACA;;AAcA;AACA;AACA;;AAmEA,eAAeA,mBAAmB,CAACC,YAAY,CAAO,WAAW,CAAC","ignoreList":[]}
1
+ {"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeVideoTrim.ts"],"mappings":";;AACA,SAASA,mBAAmB,QAAQ,cAAc;;AAGlD;AACA;AACA;;AAwBA;AACA;AACA;;AAgGA;AACA;AACA;;AAQA;AACA;AACA;;AAUA;AACA;AACA;;AAcA;AACA;AACA;;AAmEA,eAAeA,mBAAmB,CAACC,YAAY,CAAO,WAAW,CAAC","ignoreList":[]}
@@ -14,8 +14,7 @@ function createBaseOptions(overrides = {}) {
14
14
  outputExt: 'mp4',
15
15
  removeAfterSavedToPhoto: false,
16
16
  removeAfterFailedToSavePhoto: false,
17
- enableRotation: false,
18
- rotationAngle: 0,
17
+ enablePreciseTrimming: false,
19
18
  ...overrides
20
19
  };
21
20
  }
@@ -1 +1 @@
1
- {"version":3,"names":["VideoTrimNewArch","VideoTrimOldArch","processColor","isFabric","global","nativeFabricUIManager","VideoTrim","createBaseOptions","overrides","saveToPhoto","type","outputExt","removeAfterSavedToPhoto","removeAfterFailedToSavePhoto","enableRotation","rotationAngle","createEditorConfig","enableHapticFeedback","maxDuration","minDuration","openDocumentsOnFinish","openShareSheetOnFinish","removeAfterSavedToDocuments","removeAfterFailedToSaveDocuments","removeAfterShared","removeAfterFailedToShare","cancelButtonText","saveButtonText","enableCancelDialog","cancelDialogTitle","cancelDialogMessage","cancelDialogCancelText","cancelDialogConfirmText","enableSaveDialog","saveDialogTitle","saveDialogMessage","saveDialogCancelText","saveDialogConfirmText","trimmingText","fullScreenModalIOS","autoplay","jumpToPositionOnLoad","closeWhenFinish","enableCancelTrimming","cancelTrimmingButtonText","enableCancelTrimmingDialog","cancelTrimmingDialogTitle","cancelTrimmingDialogMessage","cancelTrimmingDialogCancelText","cancelTrimmingDialogConfirmText","headerText","headerTextSize","headerTextColor","trimmerColor","handleIconColor","zoomOnWaitingDuration","alertOnFailToLoad","alertOnFailTitle","alertOnFailMessage","alertOnFailCloseText","createTrimOptions","startTime","endTime","showEditor","filePath","config","_headerTextColor","_trimmerColor","_handleIconColor","listFiles","cleanFiles","deleteFile","trim","length","Error","closeEditor","isValidFile","url","options"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["VideoTrimNewArch","VideoTrimOldArch","processColor","isFabric","global","nativeFabricUIManager","VideoTrim","createBaseOptions","overrides","saveToPhoto","type","outputExt","removeAfterSavedToPhoto","removeAfterFailedToSavePhoto","enablePreciseTrimming","createEditorConfig","enableHapticFeedback","maxDuration","minDuration","openDocumentsOnFinish","openShareSheetOnFinish","removeAfterSavedToDocuments","removeAfterFailedToSaveDocuments","removeAfterShared","removeAfterFailedToShare","cancelButtonText","saveButtonText","enableCancelDialog","cancelDialogTitle","cancelDialogMessage","cancelDialogCancelText","cancelDialogConfirmText","enableSaveDialog","saveDialogTitle","saveDialogMessage","saveDialogCancelText","saveDialogConfirmText","trimmingText","fullScreenModalIOS","autoplay","jumpToPositionOnLoad","closeWhenFinish","enableCancelTrimming","cancelTrimmingButtonText","enableCancelTrimmingDialog","cancelTrimmingDialogTitle","cancelTrimmingDialogMessage","cancelTrimmingDialogCancelText","cancelTrimmingDialogConfirmText","headerText","headerTextSize","headerTextColor","trimmerColor","handleIconColor","zoomOnWaitingDuration","alertOnFailToLoad","alertOnFailTitle","alertOnFailMessage","alertOnFailCloseText","createTrimOptions","startTime","endTime","showEditor","filePath","config","_headerTextColor","_trimmerColor","_handleIconColor","listFiles","cleanFiles","deleteFile","trim","length","Error","closeEditor","isValidFile","url","options"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
@@ -14,10 +14,16 @@ export interface BaseOptions {
14
14
  removeAfterSavedToPhoto: boolean;
15
15
  /** Whether to remove the output file if saving to the photo library fails. */
16
16
  removeAfterFailedToSavePhoto: boolean;
17
- /** Whether to enable video rotation during trimming. */
18
- enableRotation: boolean;
19
- /** Rotation angle in degrees (e.g. `90`, `180`, `270`). Only used when `enableRotation` is `true`. */
20
- rotationAngle: number;
17
+ /**
18
+ * When `true`, FFmpeg re-encodes the video using the platform's hardware encoder
19
+ * (h264_videotoolbox on iOS, h264_mediacodec on Android) for frame-accurate trimming.
20
+ * When `false` (default), uses stream copy (`-c copy`) which is much faster but can
21
+ * only cut at keyframes — the actual start/end may drift by several seconds.
22
+ *
23
+ * Note: if the user applies any transform (flip/rotate/crop), re-encoding already
24
+ * happens regardless of this flag, so precise trimming comes for free in that case.
25
+ */
26
+ enablePreciseTrimming: boolean;
21
27
  }
22
28
  /**
23
29
  * Configuration for the video trimmer editor UI.
@@ -1 +1 @@
1
- {"version":3,"file":"NativeVideoTrim.d.ts","sourceRoot":"","sources":["../../../src/NativeVideoTrim.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2CAA2C,CAAC;AAE9E;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,qEAAqE;IACrE,WAAW,EAAE,OAAO,CAAC;IACrB,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,qDAAqD;IACrD,SAAS,EAAE,MAAM,CAAC;IAClB,sFAAsF;IACtF,uBAAuB,EAAE,OAAO,CAAC;IACjC,8EAA8E;IAC9E,4BAA4B,EAAE,OAAO,CAAC;IACtC,wDAAwD;IACxD,cAAc,EAAE,OAAO,CAAC;IACxB,sGAAsG;IACtG,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,YAAa,SAAQ,WAAW;IAC/C,2EAA2E;IAC3E,oBAAoB,EAAE,OAAO,CAAC;IAC9B,+FAA+F;IAC/F,WAAW,EAAE,MAAM,CAAC;IACpB,+FAA+F;IAC/F,WAAW,EAAE,MAAM,CAAC;IACpB,8EAA8E;IAC9E,qBAAqB,EAAE,OAAO,CAAC;IAC/B,+DAA+D;IAC/D,sBAAsB,EAAE,OAAO,CAAC;IAChC,8EAA8E;IAC9E,2BAA2B,EAAE,OAAO,CAAC;IACrC,sEAAsE;IACtE,gCAAgC,EAAE,OAAO,CAAC;IAC1C,kEAAkE;IAClE,iBAAiB,EAAE,OAAO,CAAC;IAC3B,0DAA0D;IAC1D,wBAAwB,EAAE,OAAO,CAAC;IAClC,kCAAkC;IAClC,gBAAgB,EAAE,MAAM,CAAC;IACzB,gCAAgC;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,+EAA+E;IAC/E,kBAAkB,EAAE,OAAO,CAAC;IAC5B,+CAA+C;IAC/C,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iDAAiD;IACjD,mBAAmB,EAAE,MAAM,CAAC;IAC5B,qEAAqE;IACrE,sBAAsB,EAAE,MAAM,CAAC;IAC/B,qEAAqE;IACrE,uBAAuB,EAAE,MAAM,CAAC;IAChC,6EAA6E;IAC7E,gBAAgB,EAAE,OAAO,CAAC;IAC1B,6CAA6C;IAC7C,eAAe,EAAE,MAAM,CAAC;IACxB,+CAA+C;IAC/C,iBAAiB,EAAE,MAAM,CAAC;IAC1B,mEAAmE;IACnE,oBAAoB,EAAE,MAAM,CAAC;IAC7B,mEAAmE;IACnE,qBAAqB,EAAE,MAAM,CAAC;IAC9B,oFAAoF;IACpF,YAAY,EAAE,MAAM,CAAC;IACrB,sEAAsE;IACtE,kBAAkB,EAAE,OAAO,CAAC;IAC5B,4DAA4D;IAC5D,QAAQ,EAAE,OAAO,CAAC;IAClB,yFAAyF;IACzF,oBAAoB,EAAE,MAAM,CAAC;IAC7B,wEAAwE;IACxE,eAAe,EAAE,OAAO,CAAC;IACzB,yEAAyE;IACzE,oBAAoB,EAAE,OAAO,CAAC;IAC9B,2CAA2C;IAC3C,wBAAwB,EAAE,MAAM,CAAC;IACjC,iFAAiF;IACjF,0BAA0B,EAAE,OAAO,CAAC;IACpC,wDAAwD;IACxD,yBAAyB,EAAE,MAAM,CAAC;IAClC,0DAA0D;IAC1D,2BAA2B,EAAE,MAAM,CAAC;IACpC,iEAAiE;IACjE,8BAA8B,EAAE,MAAM,CAAC;IACvC,iEAAiE;IACjE,+BAA+B,EAAE,MAAM,CAAC;IACxC,6DAA6D;IAC7D,UAAU,EAAE,MAAM,CAAC;IACnB,6CAA6C;IAC7C,cAAc,EAAE,MAAM,CAAC;IACvB,0DAA0D;IAC1D,eAAe,EAAE,MAAM,CAAC;IACxB,yEAAyE;IACzE,iBAAiB,EAAE,OAAO,CAAC;IAC3B,uCAAuC;IACvC,gBAAgB,EAAE,MAAM,CAAC;IACzB,yCAAyC;IACzC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,2DAA2D;IAC3D,oBAAoB,EAAE,MAAM,CAAC;IAC7B,kGAAkG;IAClG,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,0DAA0D;IAC1D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,8EAA8E;IAC9E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,WAAY,SAAQ,WAAW;IAC9C,oDAAoD;IACpD,SAAS,EAAE,MAAM,CAAC;IAClB,kDAAkD;IAClD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,uDAAuD;IACvD,OAAO,EAAE,OAAO,CAAC;IACjB,mEAAmE;IACnE,QAAQ,EAAE,MAAM,CAAC;IACjB,sEAAsE;IACtE,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,uDAAuD;IACvD,SAAS,EAAE,MAAM,CAAC;IAClB,qDAAqD;IACrD,OAAO,EAAE,MAAM,CAAC;IAChB,oDAAoD;IACpD,QAAQ,EAAE,MAAM,CAAC;IACjB,gDAAgD;IAChD,UAAU,EAAE,MAAM,CAAC;IACnB,yDAAyD;IACzD,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,wDAAwD;IACxD,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CAAC;IACzD,+DAA+D;IAC/D,SAAS,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC/B,sGAAsG;IACtG,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9B,0EAA0E;IAC1E,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/C,iEAAiE;IACjE,WAAW,IAAI,IAAI,CAAC;IACpB,yEAAyE;IACzE,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACxD,mFAAmF;IACnF,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAE7D,8CAA8C;IAC9C,QAAQ,CAAC,eAAe,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC;IAC7C,mEAAmE;IACnE,QAAQ,CAAC,gBAAgB,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC;IAC9C,mEAAmE;IACnE,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC;IACtC,yCAAyC;IACzC,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC;IACpC,wCAAwC;IACxC,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC;IACpC,yFAAyF;IACzF,QAAQ,CAAC,gBAAgB,EAAE,YAAY,CAAC;QACtC,gDAAgD;QAChD,UAAU,EAAE,MAAM,CAAC;QACnB,uDAAuD;QACvD,SAAS,EAAE,MAAM,CAAC;QAClB,qDAAqD;QACrD,OAAO,EAAE,MAAM,CAAC;QAChB,oDAAoD;QACpD,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC,CAAC;IACH,sDAAsD;IACtD,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;QAC3B,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC,CAAC;IACH,+DAA+D;IAC/D,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAC;QAClC,SAAS,EAAE,MAAM,CAAC;QAClB,gBAAgB,EAAE,MAAM,CAAC;QACzB,QAAQ,EAAE,MAAM,CAAC;QACjB,YAAY,EAAE,MAAM,CAAC;QACrB,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,EAAE,MAAM,CAAC;KACf,CAAC,CAAC;IACH,oEAAoE;IACpE,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;QAC7B,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC,CAAC;IACH,sEAAsE;IACtE,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;QAC5B,oDAAoD;QACpD,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC,CAAC;CACJ;;AAED,wBAAmE"}
1
+ {"version":3,"file":"NativeVideoTrim.d.ts","sourceRoot":"","sources":["../../../src/NativeVideoTrim.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAEV,YAAY,EACZ,oBAAoB,EACpB,WAAW,EACX,UAAU,EACX,MAAM,mBAAmB,CAAC;AAK3B,QAAA,MAAM,SAAS,KAAiD,CAAC;AA6EjE;;;;;;;GAOG;AACH,wBAAgB,UAAU,CACxB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,OAAO,CACb,IAAI,CAAC,YAAY,EAAE,iBAAiB,GAAG,cAAc,GAAG,iBAAiB,CAAC,CAC3E,GAAG;IACF,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,GACA,IAAI,CAeN;AAED;;;;GAIG;AACH,wBAAgB,SAAS,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAE7C;AAED;;;;GAIG;AACH,wBAAgB,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,CAE5C;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAK7D;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,IAAI,CAElC;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAEtE;AAED;;;;;;GAMG;AACH,wBAAgB,IAAI,CAClB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,OAAO,CAAC,WAAW,CAAC,GAC5B,OAAO,CAAC,UAAU,CAAC,CAErB;AAED,cAAc,mBAAmB,CAAC;AAClC,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAEV,YAAY,EACZ,oBAAoB,EACpB,WAAW,EACX,UAAU,EACX,MAAM,mBAAmB,CAAC;AAK3B,QAAA,MAAM,SAAS,KAAiD,CAAC;AA4EjE;;;;;;;GAOG;AACH,wBAAgB,UAAU,CACxB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,OAAO,CACb,IAAI,CAAC,YAAY,EAAE,iBAAiB,GAAG,cAAc,GAAG,iBAAiB,CAAC,CAC3E,GAAG;IACF,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,GACA,IAAI,CAeN;AAED;;;;GAIG;AACH,wBAAgB,SAAS,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAE7C;AAED;;;;GAIG;AACH,wBAAgB,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,CAE5C;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAK7D;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,IAAI,CAElC;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAEtE;AAED;;;;;;GAMG;AACH,wBAAgB,IAAI,CAClB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,OAAO,CAAC,WAAW,CAAC,GAC5B,OAAO,CAAC,UAAU,CAAC,CAErB;AAED,cAAc,mBAAmB,CAAC;AAClC,eAAe,SAAS,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-video-trim",
3
- "version": "6.2.3",
3
+ "version": "7.0.1",
4
4
  "description": "Video trimmer for your React Native app",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -16,10 +16,16 @@ export interface BaseOptions {
16
16
  removeAfterSavedToPhoto: boolean;
17
17
  /** Whether to remove the output file if saving to the photo library fails. */
18
18
  removeAfterFailedToSavePhoto: boolean;
19
- /** Whether to enable video rotation during trimming. */
20
- enableRotation: boolean;
21
- /** Rotation angle in degrees (e.g. `90`, `180`, `270`). Only used when `enableRotation` is `true`. */
22
- rotationAngle: number;
19
+ /**
20
+ * When `true`, FFmpeg re-encodes the video using the platform's hardware encoder
21
+ * (h264_videotoolbox on iOS, h264_mediacodec on Android) for frame-accurate trimming.
22
+ * When `false` (default), uses stream copy (`-c copy`) which is much faster but can
23
+ * only cut at keyframes — the actual start/end may drift by several seconds.
24
+ *
25
+ * Note: if the user applies any transform (flip/rotate/crop), re-encoding already
26
+ * happens regardless of this flag, so precise trimming comes for free in that case.
27
+ */
28
+ enablePreciseTrimming: boolean;
23
29
  }
24
30
 
25
31
  /**
package/src/index.tsx CHANGED
@@ -20,8 +20,7 @@ function createBaseOptions(overrides: Partial<BaseOptions> = {}): BaseOptions {
20
20
  outputExt: 'mp4',
21
21
  removeAfterSavedToPhoto: false,
22
22
  removeAfterFailedToSavePhoto: false,
23
- enableRotation: false,
24
- rotationAngle: 0,
23
+ enablePreciseTrimming: false,
25
24
  ...overrides,
26
25
  };
27
26
  }