react-native-inapp-inspector 2.4.2 → 2.5.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.
Files changed (202) hide show
  1. package/android/src/main/java/com/inappinspector/NetworkInspectorModule.kt +523 -2
  2. package/dist/commonjs/capture.d.ts +2 -0
  3. package/dist/commonjs/capture.js +1 -1
  4. package/dist/commonjs/components/AnalyticsDetail.js +23 -23
  5. package/dist/commonjs/components/AnalyticsEventCard.js +1 -1
  6. package/dist/commonjs/components/AnalyticsGraph.js +44 -44
  7. package/dist/commonjs/components/AnimatedBrandOwl.js +32 -32
  8. package/dist/commonjs/components/AppHeaderLogo.js +6 -6
  9. package/dist/commonjs/components/BrandCircleIcon.js +28 -28
  10. package/dist/commonjs/components/BrandSquareIcon.js +33 -33
  11. package/dist/commonjs/components/ConsoleLogCard.js +1 -1
  12. package/dist/commonjs/components/Inspector/AnalyticsTab.js +2 -2
  13. package/dist/commonjs/components/Inspector/ConfirmationModal.js +23 -25
  14. package/dist/commonjs/components/Inspector/ConsoleTab.js +5 -5
  15. package/dist/commonjs/components/Inspector/CrashTab.js +44 -11
  16. package/dist/commonjs/components/Inspector/DeviceInfoTab.js +1 -1
  17. package/dist/commonjs/components/Inspector/FabLauncher.js +30 -25
  18. package/dist/commonjs/components/Inspector/FloatingCaptureWidget.d.ts +3 -0
  19. package/dist/commonjs/components/Inspector/FloatingCaptureWidget.js +54 -0
  20. package/dist/commonjs/components/Inspector/GlobalCrashScreenModal.d.ts +11 -0
  21. package/dist/commonjs/components/Inspector/GlobalCrashScreenModal.js +234 -0
  22. package/dist/commonjs/components/Inspector/InspectorHeader.js +137 -127
  23. package/dist/commonjs/components/Inspector/LanguageSelectorModal.d.ts +1 -1
  24. package/dist/commonjs/components/Inspector/LanguageSelectorModal.js +114 -32
  25. package/dist/commonjs/components/Inspector/MainScreen.js +26 -19
  26. package/dist/commonjs/components/Inspector/MediaGalleryTab.js +38 -35
  27. package/dist/commonjs/components/Inspector/MediaPreviewModal.d.ts +36 -0
  28. package/dist/commonjs/components/Inspector/MediaPreviewModal.js +345 -123
  29. package/dist/commonjs/components/Inspector/NetworkTab.js +14 -14
  30. package/dist/commonjs/components/Inspector/PushCard.js +1 -1
  31. package/dist/commonjs/components/Inspector/PushDetail.js +9 -9
  32. package/dist/commonjs/components/Inspector/PushTab.js +3 -3
  33. package/dist/commonjs/components/Inspector/ReduxTab.js +9 -7
  34. package/dist/commonjs/components/Inspector/SecondaryTelemetryStrip.d.ts +2 -0
  35. package/dist/commonjs/components/Inspector/SecondaryTelemetryStrip.js +43 -0
  36. package/dist/commonjs/components/Inspector/SettingsPanel.js +102 -73
  37. package/dist/commonjs/components/Inspector/SocketCard.js +1 -1
  38. package/dist/commonjs/components/Inspector/SocketDetail.js +73 -63
  39. package/dist/commonjs/components/Inspector/SocketTab.js +4 -4
  40. package/dist/commonjs/components/Inspector/StorageTab.js +10 -10
  41. package/dist/commonjs/components/Inspector/SupportModal.js +2 -2
  42. package/dist/commonjs/components/Inspector/SupportPage.js +1 -1
  43. package/dist/commonjs/components/JsonViewer.js +47 -45
  44. package/dist/commonjs/components/LogCard.js +22 -20
  45. package/dist/commonjs/components/ModuleErrorBoundary.js +5 -5
  46. package/dist/commonjs/components/NetworkIcons.d.ts +27 -0
  47. package/dist/commonjs/components/NetworkIcons.js +103 -5
  48. package/dist/commonjs/constants/version.d.ts +1 -1
  49. package/dist/commonjs/constants/version.js +1 -1
  50. package/dist/commonjs/crash.d.ts +1 -1
  51. package/dist/commonjs/crash.js +1 -1
  52. package/dist/commonjs/customHooks/crashHandler.d.ts +2 -1
  53. package/dist/commonjs/customHooks/crashHandler.js +7 -7
  54. package/dist/commonjs/editor.d.ts +13 -0
  55. package/dist/commonjs/editor.js +1 -0
  56. package/dist/commonjs/helpers/index.d.ts +1 -0
  57. package/dist/commonjs/helpers/index.js +1 -1
  58. package/dist/commonjs/helpers/settingsStore.js +1 -1
  59. package/dist/commonjs/helpers/telemetry.js +1 -1
  60. package/dist/commonjs/i18n/index.js +1 -1
  61. package/dist/commonjs/i18n/locales/ar.json +1 -1
  62. package/dist/commonjs/i18n/locales/bn.json +1 -1
  63. package/dist/commonjs/i18n/locales/de.json +1 -1
  64. package/dist/commonjs/i18n/locales/en.json +1 -1
  65. package/dist/commonjs/i18n/locales/es.json +1 -1
  66. package/dist/commonjs/i18n/locales/fr.json +1 -1
  67. package/dist/commonjs/i18n/locales/gu.json +1 -1
  68. package/dist/commonjs/i18n/locales/hi.json +1 -1
  69. package/dist/commonjs/i18n/locales/id.json +1 -1
  70. package/dist/commonjs/i18n/locales/it.json +1 -1
  71. package/dist/commonjs/i18n/locales/ja.json +1 -1
  72. package/dist/commonjs/i18n/locales/kn.json +1 -1
  73. package/dist/commonjs/i18n/locales/ko.json +1 -1
  74. package/dist/commonjs/i18n/locales/ml.json +1 -1
  75. package/dist/commonjs/i18n/locales/mr.json +1 -1
  76. package/dist/commonjs/i18n/locales/nl.json +1 -1
  77. package/dist/commonjs/i18n/locales/pa.json +1 -1
  78. package/dist/commonjs/i18n/locales/pl.json +1 -1
  79. package/dist/commonjs/i18n/locales/pt.json +1 -1
  80. package/dist/commonjs/i18n/locales/ru.json +1 -1
  81. package/dist/commonjs/i18n/locales/ta.json +1 -1
  82. package/dist/commonjs/i18n/locales/te.json +1 -1
  83. package/dist/commonjs/i18n/locales/tr.json +1 -1
  84. package/dist/commonjs/i18n/locales/vi.json +1 -1
  85. package/dist/commonjs/i18n/locales/zh.json +1 -1
  86. package/dist/commonjs/index.d.ts +4 -1
  87. package/dist/commonjs/index.js +3 -3
  88. package/dist/commonjs/native/NativeInspector.d.ts +3 -0
  89. package/dist/commonjs/native/NativeInspector.js +1 -1
  90. package/dist/commonjs/native/NativeNetworkInspector.d.ts +4 -0
  91. package/dist/commonjs/styles/AppColors.d.ts +42 -2
  92. package/dist/commonjs/styles/AppColors.js +1 -1
  93. package/dist/commonjs/styles/common.js +1 -1
  94. package/dist/commonjs/styles/index.d.ts +7 -0
  95. package/dist/commonjs/styles/index.js +1 -1
  96. package/dist/commonjs/types/editor.d.ts +96 -0
  97. package/dist/commonjs/types/editor.js +1 -0
  98. package/dist/commonjs/types/interfaces.d.ts +21 -0
  99. package/dist/esm/capture.d.ts +2 -0
  100. package/dist/esm/capture.js +1 -1
  101. package/dist/esm/components/AnalyticsDetail.js +31 -31
  102. package/dist/esm/components/AnalyticsEventCard.js +1 -1
  103. package/dist/esm/components/AnalyticsGraph.js +74 -74
  104. package/dist/esm/components/AnimatedBrandOwl.js +114 -114
  105. package/dist/esm/components/AppHeaderLogo.js +7 -7
  106. package/dist/esm/components/BrandCircleIcon.js +70 -70
  107. package/dist/esm/components/BrandSquareIcon.js +61 -61
  108. package/dist/esm/components/ConsoleLogCard.js +1 -1
  109. package/dist/esm/components/Inspector/AnalyticsTab.js +14 -14
  110. package/dist/esm/components/Inspector/ConfirmationModal.js +23 -25
  111. package/dist/esm/components/Inspector/ConsoleTab.js +17 -17
  112. package/dist/esm/components/Inspector/CrashTab.js +202 -169
  113. package/dist/esm/components/Inspector/DeviceInfoTab.js +75 -75
  114. package/dist/esm/components/Inspector/FabLauncher.js +56 -51
  115. package/dist/esm/components/Inspector/FloatingCaptureWidget.d.ts +3 -0
  116. package/dist/esm/components/Inspector/FloatingCaptureWidget.js +54 -0
  117. package/dist/esm/components/Inspector/GlobalCrashScreenModal.d.ts +11 -0
  118. package/dist/esm/components/Inspector/GlobalCrashScreenModal.js +234 -0
  119. package/dist/esm/components/Inspector/InspectorHeader.js +162 -152
  120. package/dist/esm/components/Inspector/LanguageSelectorModal.d.ts +1 -1
  121. package/dist/esm/components/Inspector/LanguageSelectorModal.js +140 -58
  122. package/dist/esm/components/Inspector/MainScreen.js +61 -54
  123. package/dist/esm/components/Inspector/MediaGalleryTab.js +55 -52
  124. package/dist/esm/components/Inspector/MediaPreviewModal.d.ts +36 -0
  125. package/dist/esm/components/Inspector/MediaPreviewModal.js +345 -123
  126. package/dist/esm/components/Inspector/NetworkTab.js +74 -74
  127. package/dist/esm/components/Inspector/PushCard.js +1 -1
  128. package/dist/esm/components/Inspector/PushDetail.js +18 -18
  129. package/dist/esm/components/Inspector/PushTab.js +1 -1
  130. package/dist/esm/components/Inspector/ReduxTab.js +18 -16
  131. package/dist/esm/components/Inspector/SecondaryTelemetryStrip.d.ts +2 -0
  132. package/dist/esm/components/Inspector/SecondaryTelemetryStrip.js +43 -0
  133. package/dist/esm/components/Inspector/SettingsPanel.js +224 -195
  134. package/dist/esm/components/Inspector/SocketCard.js +8 -8
  135. package/dist/esm/components/Inspector/SocketDetail.js +106 -96
  136. package/dist/esm/components/Inspector/SocketTab.js +18 -18
  137. package/dist/esm/components/Inspector/StorageTab.js +47 -47
  138. package/dist/esm/components/Inspector/SupportModal.js +64 -64
  139. package/dist/esm/components/Inspector/SupportPage.js +5 -5
  140. package/dist/esm/components/JsonViewer.js +51 -49
  141. package/dist/esm/components/LogCard.js +41 -39
  142. package/dist/esm/components/ModuleErrorBoundary.js +10 -10
  143. package/dist/esm/components/NetworkIcons.d.ts +27 -0
  144. package/dist/esm/components/NetworkIcons.js +101 -3
  145. package/dist/esm/constants/version.d.ts +1 -1
  146. package/dist/esm/constants/version.js +1 -1
  147. package/dist/esm/crash.d.ts +1 -1
  148. package/dist/esm/crash.js +1 -1
  149. package/dist/esm/customHooks/crashHandler.d.ts +2 -1
  150. package/dist/esm/customHooks/crashHandler.js +7 -7
  151. package/dist/esm/editor.d.ts +13 -0
  152. package/dist/esm/editor.js +1 -0
  153. package/dist/esm/helpers/index.d.ts +1 -0
  154. package/dist/esm/helpers/index.js +4 -4
  155. package/dist/esm/helpers/settingsStore.js +1 -1
  156. package/dist/esm/helpers/telemetry.js +1 -1
  157. package/dist/esm/i18n/index.js +1 -1
  158. package/dist/esm/i18n/locales/ar.json +1 -1
  159. package/dist/esm/i18n/locales/bn.json +1 -1
  160. package/dist/esm/i18n/locales/de.json +1 -1
  161. package/dist/esm/i18n/locales/en.json +1 -1
  162. package/dist/esm/i18n/locales/es.json +1 -1
  163. package/dist/esm/i18n/locales/fr.json +1 -1
  164. package/dist/esm/i18n/locales/gu.json +1 -1
  165. package/dist/esm/i18n/locales/hi.json +1 -1
  166. package/dist/esm/i18n/locales/id.json +1 -1
  167. package/dist/esm/i18n/locales/it.json +1 -1
  168. package/dist/esm/i18n/locales/ja.json +1 -1
  169. package/dist/esm/i18n/locales/kn.json +1 -1
  170. package/dist/esm/i18n/locales/ko.json +1 -1
  171. package/dist/esm/i18n/locales/ml.json +1 -1
  172. package/dist/esm/i18n/locales/mr.json +1 -1
  173. package/dist/esm/i18n/locales/nl.json +1 -1
  174. package/dist/esm/i18n/locales/pa.json +1 -1
  175. package/dist/esm/i18n/locales/pl.json +1 -1
  176. package/dist/esm/i18n/locales/pt.json +1 -1
  177. package/dist/esm/i18n/locales/ru.json +1 -1
  178. package/dist/esm/i18n/locales/ta.json +1 -1
  179. package/dist/esm/i18n/locales/te.json +1 -1
  180. package/dist/esm/i18n/locales/tr.json +1 -1
  181. package/dist/esm/i18n/locales/vi.json +1 -1
  182. package/dist/esm/i18n/locales/zh.json +1 -1
  183. package/dist/esm/index.d.ts +4 -1
  184. package/dist/esm/index.js +5 -5
  185. package/dist/esm/native/NativeInspector.d.ts +3 -0
  186. package/dist/esm/native/NativeInspector.js +1 -1
  187. package/dist/esm/native/NativeNetworkInspector.d.ts +4 -0
  188. package/dist/esm/styles/AppColors.d.ts +42 -2
  189. package/dist/esm/styles/AppColors.js +1 -1
  190. package/dist/esm/styles/common.js +1 -1
  191. package/dist/esm/styles/index.d.ts +7 -0
  192. package/dist/esm/styles/index.js +1 -1
  193. package/dist/esm/types/editor.d.ts +96 -0
  194. package/dist/esm/types/editor.js +1 -0
  195. package/dist/esm/types/interfaces.d.ts +21 -0
  196. package/editor.d.ts +1 -0
  197. package/editor.js +1 -0
  198. package/ios/NetworkInspectorModule.mm +429 -0
  199. package/package.json +10 -1
  200. package/react-native-inapp-inspector.podspec +1 -1
  201. package/src/native/NativeInspector.ts +57 -4
  202. package/src/native/NativeNetworkInspector.ts +4 -0
@@ -6,6 +6,7 @@
6
6
  #import <ReplayKit/ReplayKit.h>
7
7
  #import <AVFoundation/AVFoundation.h>
8
8
  #import <AVKit/AVKit.h>
9
+ #import <CoreImage/CoreImage.h>
9
10
  #import <ImageIO/ImageIO.h>
10
11
  #import <MobileCoreServices/MobileCoreServices.h>
11
12
 
@@ -1911,6 +1912,434 @@ RCT_EXPORT_METHOD(clearAllCapturedMedia:(RCTPromiseResolveBlock)resolve
1911
1912
  });
1912
1913
  }
1913
1914
 
1915
+ // ─────────────────────────────────────────────────────────────────────────────
1916
+ // 100% Native Media Editor: Photo Editing (CoreImage + ImageIO)
1917
+ // ─────────────────────────────────────────────────────────────────────────────
1918
+
1919
+ RCT_EXPORT_METHOD(editPhoto:(NSDictionary *)options
1920
+ resolve:(RCTPromiseResolveBlock)resolve
1921
+ reject:(RCTPromiseRejectBlock)reject) {
1922
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
1923
+ @try {
1924
+ NSString *rawUri = options[@"uri"];
1925
+ if (!rawUri || rawUri.length == 0) {
1926
+ reject(@"INVALID_URI", @"Image URI is missing or empty", nil);
1927
+ return;
1928
+ }
1929
+
1930
+ NSString *cleanPath = rawUri;
1931
+ if ([cleanPath hasPrefix:@"file://"]) {
1932
+ cleanPath = [cleanPath substringFromIndex:7];
1933
+ }
1934
+ cleanPath = [cleanPath stringByRemovingPercentEncoding];
1935
+
1936
+ UIImage *sourceImage = [UIImage imageWithContentsOfFile:cleanPath];
1937
+ if (!sourceImage) {
1938
+ reject(@"IMAGE_LOAD_FAILED", @"Failed to load source image", nil);
1939
+ return;
1940
+ }
1941
+
1942
+ // 1. Convert to CIImage
1943
+ CIImage *ciImage = [CIImage imageWithCGImage:sourceImage.CGImage];
1944
+ if (!ciImage) {
1945
+ ciImage = [CIImage imageWithContentsOfURL:[NSURL fileURLWithPath:cleanPath]];
1946
+ }
1947
+ if (!ciImage) {
1948
+ reject(@"CIIMAGE_FAILED", @"Failed to initialize CoreImage pipeline", nil);
1949
+ return;
1950
+ }
1951
+
1952
+ // 2. Apply Rotation & Flip
1953
+ NSInteger rotation = [options[@"rotation"] integerValue];
1954
+ BOOL flipH = [options[@"flipHorizontal"] boolValue];
1955
+ BOOL flipV = [options[@"flipVertical"] boolValue];
1956
+
1957
+ CGAffineTransform transform = CGAffineTransformIdentity;
1958
+ if (rotation == 90) {
1959
+ transform = CGAffineTransformRotate(transform, -M_PI_2);
1960
+ } else if (rotation == 180) {
1961
+ transform = CGAffineTransformRotate(transform, M_PI);
1962
+ } else if (rotation == 270) {
1963
+ transform = CGAffineTransformRotate(transform, M_PI_2);
1964
+ }
1965
+ if (flipH) {
1966
+ transform = CGAffineTransformScale(transform, -1, 1);
1967
+ }
1968
+ if (flipV) {
1969
+ transform = CGAffineTransformScale(transform, 1, -1);
1970
+ }
1971
+ if (!CGAffineTransformIsIdentity(transform)) {
1972
+ ciImage = [ciImage imageByApplyingTransform:transform];
1973
+ CGRect newExtent = ciImage.extent;
1974
+ CGAffineTransform fixOrigin = CGAffineTransformMakeTranslation(-newExtent.origin.x, -newExtent.origin.y);
1975
+ ciImage = [ciImage imageByApplyingTransform:fixOrigin];
1976
+ }
1977
+
1978
+ // 3. Apply Crop
1979
+ NSDictionary *cropDict = options[@"crop"];
1980
+ if (cropDict && [cropDict isKindOfClass:[NSDictionary class]]) {
1981
+ CGFloat x = [cropDict[@"x"] doubleValue];
1982
+ CGFloat y = [cropDict[@"y"] doubleValue];
1983
+ CGFloat width = [cropDict[@"width"] doubleValue];
1984
+ CGFloat height = [cropDict[@"height"] doubleValue];
1985
+ BOOL isNorm = [cropDict[@"isNormalized"] boolValue];
1986
+
1987
+ CGRect extent = ciImage.extent;
1988
+ if (isNorm) {
1989
+ x *= extent.size.width;
1990
+ y *= extent.size.height;
1991
+ width *= extent.size.width;
1992
+ height *= extent.size.height;
1993
+ }
1994
+ // CoreImage coordinate system (bottom-left origin)
1995
+ CGFloat ciY = extent.size.height - (y + height);
1996
+ CGRect cropRect = CGRectMake(x, ciY, width, height);
1997
+ CGRect intersection = CGRectIntersection(extent, cropRect);
1998
+ if (!CGRectIsNull(intersection) && intersection.size.width > 0 && intersection.size.height > 0) {
1999
+ ciImage = [ciImage imageByCroppingToRect:intersection];
2000
+ }
2001
+ }
2002
+
2003
+ // 4. Color Grading Adjustments (CIColorControls, CITemperatureAndTint, CIVignette)
2004
+ NSDictionary *adjustments = options[@"adjustments"];
2005
+ if (adjustments && [adjustments isKindOfClass:[NSDictionary class]]) {
2006
+ NSNumber *brightness = adjustments[@"brightness"];
2007
+ NSNumber *contrast = adjustments[@"contrast"];
2008
+ NSNumber *saturation = adjustments[@"saturation"];
2009
+
2010
+ if (brightness || contrast || saturation) {
2011
+ CIFilter *colorControls = [CIFilter filterWithName:@"CIColorControls"];
2012
+ [colorControls setValue:ciImage forKey:kCIInputImageKey];
2013
+ if (brightness) [colorControls setValue:brightness forKey:kCIInputBrightnessKey];
2014
+ if (contrast) [colorControls setValue:contrast forKey:kCIInputContrastKey];
2015
+ if (saturation) [colorControls setValue:saturation forKey:kCIInputSaturationKey];
2016
+ ciImage = colorControls.outputImage ?: ciImage;
2017
+ }
2018
+
2019
+ NSNumber *temperature = adjustments[@"temperature"];
2020
+ if (temperature && [temperature doubleValue] != 0) {
2021
+ CIFilter *tempFilter = [CIFilter filterWithName:@"CITemperatureAndTint"];
2022
+ [tempFilter setValue:ciImage forKey:kCIInputImageKey];
2023
+ CIVector *neutral = [CIVector vectorWithX:6500 Y:0];
2024
+ CGFloat shift = [temperature doubleValue] * 2000;
2025
+ CIVector *target = [CIVector vectorWithX:6500 + shift Y:0];
2026
+ [tempFilter setValue:neutral forKey:@"inputNeutral"];
2027
+ [tempFilter setValue:target forKey:@"inputTargetNeutral"];
2028
+ ciImage = tempFilter.outputImage ?: ciImage;
2029
+ }
2030
+
2031
+ NSNumber *vignette = adjustments[@"vignette"];
2032
+ if (vignette && [vignette doubleValue] > 0) {
2033
+ CIFilter *vigFilter = [CIFilter filterWithName:@"CIVignette"];
2034
+ [vigFilter setValue:ciImage forKey:kCIInputImageKey];
2035
+ [vigFilter setValue:@([vignette doubleValue] * 2.0) forKey:kCIInputIntensityKey];
2036
+ [vigFilter setValue:@(1.0) forKey:kCIInputRadiusKey];
2037
+ ciImage = vigFilter.outputImage ?: ciImage;
2038
+ }
2039
+
2040
+ NSNumber *sharpen = adjustments[@"sharpen"];
2041
+ if (sharpen && [sharpen doubleValue] > 0) {
2042
+ CIFilter *sharpFilter = [CIFilter filterWithName:@"CISharpenLuminance"];
2043
+ [sharpFilter setValue:ciImage forKey:kCIInputImageKey];
2044
+ [sharpFilter setValue:@([sharpen doubleValue] * 2.0) forKey:kCIInputSharpnessKey];
2045
+ ciImage = sharpFilter.outputImage ?: ciImage;
2046
+ }
2047
+ }
2048
+
2049
+ // 5. Preset Filters
2050
+ NSString *preset = options[@"filterPreset"];
2051
+ if (preset && preset.length > 0 && ![preset isEqualToString:@"none"]) {
2052
+ NSString *filterName = nil;
2053
+ if ([preset isEqualToString:@"mono"]) filterName = @"CIPhotoEffectMono";
2054
+ else if ([preset isEqualToString:@"noir"]) filterName = @"CIPhotoEffectNoir";
2055
+ else if ([preset isEqualToString:@"sepia"]) filterName = @"CISepiaTone";
2056
+ else if ([preset isEqualToString:@"vibrant"]) filterName = @"CIPhotoEffectChrome";
2057
+ else if ([preset isEqualToString:@"fade"]) filterName = @"CIPhotoEffectFade";
2058
+ else if ([preset isEqualToString:@"vintage"]) filterName = @"CIPhotoEffectInstant";
2059
+
2060
+ if (filterName) {
2061
+ CIFilter *pFilter = [CIFilter filterWithName:filterName];
2062
+ [pFilter setValue:ciImage forKey:kCIInputImageKey];
2063
+ ciImage = pFilter.outputImage ?: ciImage;
2064
+ }
2065
+ }
2066
+
2067
+ // 6. GPU Render to File
2068
+ CIContext *context = [CIContext contextWithOptions:@{kCIContextUseSoftwareRenderer: @(NO)}];
2069
+ CGImageRef cgImage = [context createCGImage:ciImage fromRect:ciImage.extent];
2070
+ if (!cgImage) {
2071
+ reject(@"RENDER_FAILED", @"Failed to render CGImage", nil);
2072
+ return;
2073
+ }
2074
+
2075
+ UIImage *resultImage = [UIImage imageWithCGImage:cgImage];
2076
+ CGImageRelease(cgImage);
2077
+
2078
+ // 7. Apply Redactions & Annotations Overlay if provided
2079
+ NSArray *redactions = options[@"redactions"];
2080
+ NSArray *annotations = options[@"annotations"];
2081
+ if ((redactions && [redactions isKindOfClass:[NSArray class]] && redactions.count > 0) ||
2082
+ (annotations && [annotations isKindOfClass:[NSArray class]] && annotations.count > 0)) {
2083
+ UIGraphicsBeginImageContextWithOptions(resultImage.size, NO, 1.0);
2084
+ [resultImage drawInRect:CGRectMake(0, 0, resultImage.size.width, resultImage.size.height)];
2085
+ CGContextRef ctx = UIGraphicsGetCurrentContext();
2086
+
2087
+ if (redactions && [redactions isKindOfClass:[NSArray class]]) {
2088
+ for (NSDictionary *box in redactions) {
2089
+ CGFloat x = [box[@"x"] doubleValue];
2090
+ CGFloat y = [box[@"y"] doubleValue];
2091
+ CGFloat w = [box[@"width"] doubleValue];
2092
+ CGFloat h = [box[@"height"] doubleValue];
2093
+ BOOL isNorm = [box[@"isNormalized"] boolValue];
2094
+ if (isNorm) {
2095
+ x *= resultImage.size.width;
2096
+ y *= resultImage.size.height;
2097
+ w *= resultImage.size.width;
2098
+ h *= resultImage.size.height;
2099
+ }
2100
+ CGContextSetFillColorWithColor(ctx, [UIColor blackColor].CGColor);
2101
+ CGContextFillRect(ctx, CGRectMake(x, y, w, h));
2102
+ }
2103
+ }
2104
+
2105
+ if (annotations && [annotations isKindOfClass:[NSArray class]]) {
2106
+ for (NSDictionary *ann in annotations) {
2107
+ CGFloat x = [ann[@"x"] doubleValue];
2108
+ CGFloat y = [ann[@"y"] doubleValue];
2109
+ CGFloat w = [ann[@"width"] doubleValue];
2110
+ CGFloat h = [ann[@"height"] doubleValue];
2111
+ BOOL isNorm = [ann[@"isNormalized"] boolValue];
2112
+ if (isNorm) {
2113
+ x *= resultImage.size.width;
2114
+ y *= resultImage.size.height;
2115
+ w *= resultImage.size.width;
2116
+ h *= resultImage.size.height;
2117
+ }
2118
+ CGContextSetStrokeColorWithColor(ctx, [UIColor redColor].CGColor);
2119
+ CGContextSetLineWidth(ctx, 4.0);
2120
+ CGContextStrokeRect(ctx, CGRectMake(x, y, w, h));
2121
+ }
2122
+ }
2123
+
2124
+ resultImage = UIGraphicsGetImageFromCurrentImageContext();
2125
+ UIGraphicsEndImageContext();
2126
+ }
2127
+
2128
+ NSString *format = [options[@"format"] lowercaseString] ?: @"jpeg";
2129
+ CGFloat quality = options[@"quality"] ? [options[@"quality"] doubleValue] : 0.9;
2130
+ NSData *imageData = nil;
2131
+ NSString *ext = @"jpg";
2132
+ NSString *mime = @"image/jpeg";
2133
+
2134
+ if ([format isEqualToString:@"png"]) {
2135
+ imageData = UIImagePNGRepresentation(resultImage);
2136
+ ext = @"png";
2137
+ mime = @"image/png";
2138
+ } else {
2139
+ imageData = UIImageJPEGRepresentation(resultImage, quality);
2140
+ }
2141
+
2142
+ NSDateFormatter *df = [[NSDateFormatter alloc] init];
2143
+ [df setDateFormat:@"yyyyMMdd_HHmmss_SSS"];
2144
+ NSString *fileName = [NSString stringWithFormat:@"edit_%@.%@", [df stringFromDate:[NSDate date]], ext];
2145
+ NSString *outPath = [[self getCapturesDirectory] stringByAppendingPathComponent:fileName];
2146
+ [imageData writeToFile:outPath atomically:YES];
2147
+
2148
+ resolve(@{
2149
+ @"uri": [NSURL fileURLWithPath:outPath].absoluteString,
2150
+ @"width": @(resultImage.size.width),
2151
+ @"height": @(resultImage.size.height),
2152
+ @"size": @(imageData.length),
2153
+ @"mimeType": mime,
2154
+ @"format": format,
2155
+ });
2156
+ } @catch (NSException *ex) {
2157
+ reject(@"EDIT_FAILED", ex.reason ?: @"Photo edit failed", nil);
2158
+ }
2159
+ });
2160
+ }
2161
+
2162
+ // ─────────────────────────────────────────────────────────────────────────────
2163
+ // 100% Native Media Editor: Video Trimming (AVFoundation)
2164
+ // ─────────────────────────────────────────────────────────────────────────────
2165
+
2166
+ RCT_EXPORT_METHOD(trimVideo:(NSDictionary *)options
2167
+ resolve:(RCTPromiseResolveBlock)resolve
2168
+ reject:(RCTPromiseRejectBlock)reject) {
2169
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
2170
+ @try {
2171
+ NSString *rawUri = options[@"uri"];
2172
+ if (!rawUri || rawUri.length == 0) {
2173
+ reject(@"INVALID_URI", @"Video URI is missing", nil);
2174
+ return;
2175
+ }
2176
+
2177
+ NSURL *sourceUrl = [NSURL URLWithString:rawUri];
2178
+ if (!sourceUrl.scheme) {
2179
+ sourceUrl = [NSURL fileURLWithPath:rawUri];
2180
+ }
2181
+
2182
+ AVURLAsset *asset = [AVURLAsset URLAssetWithURL:sourceUrl options:nil];
2183
+ double startMs = [options[@"startTimeMs"] doubleValue];
2184
+ double endMs = [options[@"endTimeMs"] doubleValue];
2185
+ BOOL mute = [options[@"mute"] boolValue] || [options[@"muteAudio"] boolValue];
2186
+
2187
+ AVAssetTrack *videoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] firstObject];
2188
+ if (!videoTrack) {
2189
+ reject(@"NO_VIDEO_TRACK", @"Source video has no valid video track", nil);
2190
+ return;
2191
+ }
2192
+
2193
+ double trackDurationSec = CMTimeGetSeconds(videoTrack.timeRange.duration);
2194
+ if (isnan(trackDurationSec) || trackDurationSec <= 0) {
2195
+ trackDurationSec = CMTimeGetSeconds(asset.duration);
2196
+ }
2197
+ double trackDurationMs = trackDurationSec * 1000.0;
2198
+ if (endMs > trackDurationMs || endMs <= 0) {
2199
+ endMs = trackDurationMs;
2200
+ }
2201
+ if (startMs >= endMs) {
2202
+ startMs = 0;
2203
+ }
2204
+
2205
+ AVMutableComposition *composition = [AVMutableComposition composition];
2206
+ AVMutableCompositionTrack *compVideoTrack = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
2207
+
2208
+ CMTime startTime = CMTimeMakeWithSeconds(startMs / 1000.0, 600);
2209
+ CMTime durationTime = CMTimeMakeWithSeconds((endMs - startMs) / 1000.0, 600);
2210
+ CMTimeRange timeRange = CMTimeRangeMake(startTime, durationTime);
2211
+
2212
+ NSError *error = nil;
2213
+ [compVideoTrack insertTimeRange:timeRange ofTrack:videoTrack atTime:kCMTimeZero error:&error];
2214
+ compVideoTrack.preferredTransform = videoTrack.preferredTransform;
2215
+
2216
+ if (!mute) {
2217
+ AVAssetTrack *audioTrack = [[asset tracksWithMediaType:AVMediaTypeAudio] firstObject];
2218
+ if (audioTrack) {
2219
+ AVMutableCompositionTrack *compAudioTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
2220
+ CMTimeRange audioTimeRange = timeRange;
2221
+ double audioDurationSec = CMTimeGetSeconds(audioTrack.timeRange.duration);
2222
+ if (!isnan(audioDurationSec) && audioDurationSec > 0) {
2223
+ double audioDurationMs = audioDurationSec * 1000.0;
2224
+ if (endMs > audioDurationMs) {
2225
+ audioTimeRange = CMTimeRangeMake(startTime, CMTimeMakeWithSeconds((audioDurationMs - startMs) / 1000.0, 600));
2226
+ }
2227
+ }
2228
+ [compAudioTrack insertTimeRange:audioTimeRange ofTrack:audioTrack atTime:kCMTimeZero error:nil];
2229
+ }
2230
+ }
2231
+
2232
+ NSString *preset = AVAssetExportPresetHighestQuality;
2233
+ NSString *quality = options[@"quality"];
2234
+ if ([quality isEqualToString:@"medium"]) preset = AVAssetExportPreset1280x720;
2235
+ else if ([quality isEqualToString:@"low"]) preset = AVAssetExportPreset640x480;
2236
+
2237
+ AVAssetExportSession *exportSession = [AVAssetExportSession exportSessionWithAsset:composition presetName:preset];
2238
+ NSDateFormatter *df = [[NSDateFormatter alloc] init];
2239
+ [df setDateFormat:@"yyyyMMdd_HHmmss_SSS"];
2240
+ NSString *fileName = [NSString stringWithFormat:@"trim_%@.mp4", [df stringFromDate:[NSDate date]]];
2241
+ NSString *outPath = [[self getCapturesDirectory] stringByAppendingPathComponent:fileName];
2242
+ NSURL *outUrl = [NSURL fileURLWithPath:outPath];
2243
+
2244
+ exportSession.outputURL = outUrl;
2245
+ exportSession.outputFileType = AVFileTypeMPEG4;
2246
+ exportSession.shouldOptimizeForNetworkUse = YES;
2247
+
2248
+ [exportSession exportAsynchronouslyWithCompletionHandler:^{
2249
+ if (exportSession.status == AVAssetExportSessionStatusCompleted) {
2250
+ NSDictionary *attrs = [[NSFileManager defaultManager] attributesOfItemAtPath:outPath error:nil];
2251
+ CGSize natSize = CGSizeApplyAffineTransform(videoTrack.naturalSize, videoTrack.preferredTransform);
2252
+ resolve(@{
2253
+ @"uri": outUrl.absoluteString,
2254
+ @"durationMs": @(endMs - startMs),
2255
+ @"size": @([attrs fileSize]),
2256
+ @"width": @(fabs(natSize.width)),
2257
+ @"height": @(fabs(natSize.height)),
2258
+ @"format": @"mp4",
2259
+ });
2260
+ } else {
2261
+ reject(@"EXPORT_FAILED", exportSession.error.localizedDescription ?: @"Video export failed", exportSession.error);
2262
+ }
2263
+ }];
2264
+ } @catch (NSException *ex) {
2265
+ reject(@"TRIM_FAILED", ex.reason ?: @"Video trim failed", nil);
2266
+ }
2267
+ });
2268
+ }
2269
+
2270
+ // ─────────────────────────────────────────────────────────────────────────────
2271
+ // 100% Native Media Editor: Filmstrip Thumbnail Generator (AVAssetImageGenerator)
2272
+ // ─────────────────────────────────────────────────────────────────────────────
2273
+
2274
+ RCT_EXPORT_METHOD(generateFilmstrip:(NSDictionary *)options
2275
+ resolve:(RCTPromiseResolveBlock)resolve
2276
+ reject:(RCTPromiseRejectBlock)reject) {
2277
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
2278
+ @try {
2279
+ NSString *rawUri = options[@"uri"];
2280
+ if (!rawUri || rawUri.length == 0) {
2281
+ reject(@"INVALID_URI", @"Video URI is missing", nil);
2282
+ return;
2283
+ }
2284
+
2285
+ NSURL *sourceUrl = [NSURL URLWithString:rawUri];
2286
+ if (!sourceUrl.scheme) {
2287
+ sourceUrl = [NSURL fileURLWithPath:rawUri];
2288
+ }
2289
+
2290
+ AVURLAsset *asset = [AVURLAsset URLAssetWithURL:sourceUrl options:nil];
2291
+ NSInteger count = options[@"count"] ? [options[@"count"] integerValue] : 10;
2292
+ if (count <= 0) count = 10;
2293
+ if (count > 50) count = 50;
2294
+
2295
+ CGFloat targetWidth = options[@"maxWidth"] ? [options[@"maxWidth"] doubleValue] : (options[@"targetWidth"] ? [options[@"targetWidth"] doubleValue] : 120);
2296
+ CGFloat targetHeight = options[@"maxHeight"] ? [options[@"maxHeight"] doubleValue] : (options[@"targetHeight"] ? [options[@"targetHeight"] doubleValue] : 120);
2297
+ CGFloat quality = options[@"quality"] ? [options[@"quality"] doubleValue] : 0.7;
2298
+
2299
+ double durationSeconds = CMTimeGetSeconds(asset.duration);
2300
+ if (durationSeconds <= 0) {
2301
+ reject(@"INVALID_DURATION", @"Unable to determine video duration", nil);
2302
+ return;
2303
+ }
2304
+
2305
+ AVAssetImageGenerator *generator = [AVAssetImageGenerator assetImageGeneratorWithAsset:asset];
2306
+ generator.appliesPreferredTrackTransform = YES;
2307
+ generator.maximumSize = CGSizeMake(targetWidth, targetHeight);
2308
+ generator.requestedTimeToleranceBefore = kCMTimeZero;
2309
+ generator.requestedTimeToleranceAfter = kCMTimeZero;
2310
+
2311
+ NSMutableArray *thumbnails = [NSMutableArray arrayWithCapacity:count];
2312
+ double step = durationSeconds / (double)count;
2313
+
2314
+ for (NSInteger i = 0; i < count; i++) {
2315
+ double targetSec = i * step;
2316
+ CMTime time = CMTimeMakeWithSeconds(targetSec, 600);
2317
+ NSError *genErr = nil;
2318
+ CGImageRef cgImage = [generator copyCGImageAtTime:time actualTime:NULL error:&genErr];
2319
+ if (cgImage) {
2320
+ UIImage *img = [UIImage imageWithCGImage:cgImage];
2321
+ CGImageRelease(cgImage);
2322
+ NSData *data = UIImageJPEGRepresentation(img, quality);
2323
+ NSString *b64 = [data base64EncodedStringWithOptions:0];
2324
+ NSString *dataUrl = [NSString stringWithFormat:@"data:image/jpeg;base64,%@", b64];
2325
+ [thumbnails addObject:@{
2326
+ @"timeMs": @(targetSec * 1000.0),
2327
+ @"uri": dataUrl,
2328
+ @"index": @(i),
2329
+ }];
2330
+ }
2331
+ }
2332
+
2333
+ resolve(@{
2334
+ @"thumbnails": thumbnails,
2335
+ @"durationMs": @(durationSeconds * 1000.0),
2336
+ });
2337
+ } @catch (NSException *ex) {
2338
+ reject(@"FILMSTRIP_FAILED", ex.reason ?: @"Filmstrip generation failed", nil);
2339
+ }
2340
+ });
2341
+ }
2342
+
1914
2343
 
1915
2344
  #ifdef RCT_NEW_ARCH_ENABLED
1916
2345
  - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-inapp-inspector",
3
- "version": "2.4.2",
3
+ "version": "2.5.0",
4
4
  "description": "The zero-config, all-in-one in-app debugger & network logger for React Native & Expo (Flipper / Chucker alternative). Inspect Network (fetch/Axios/GraphQL), Console logs with Metro Symbolication, Redux State, Firebase Analytics, Storage (AsyncStorage/MMKV), Hardware Telemetry, and JS Bundle size directly on device.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -87,6 +87,13 @@
87
87
  "require": "./dist/commonjs/socket.js",
88
88
  "default": "./dist/commonjs/socket.js"
89
89
  },
90
+ "./editor": {
91
+ "react-native": "./dist/commonjs/editor.js",
92
+ "types": "./dist/commonjs/editor.d.ts",
93
+ "import": "./dist/esm/editor.js",
94
+ "require": "./dist/commonjs/editor.js",
95
+ "default": "./dist/commonjs/editor.js"
96
+ },
90
97
  "./metro": {
91
98
  "types": "./dist/commonjs/metro.d.ts",
92
99
  "import": "./dist/esm/metro.js",
@@ -102,6 +109,8 @@
102
109
  "assets",
103
110
  "metro.js",
104
111
  "metro.d.ts",
112
+ "editor.js",
113
+ "editor.d.ts",
105
114
  "README.md",
106
115
  "android",
107
116
  "ios",
@@ -13,7 +13,7 @@ Pod::Spec.new do |s|
13
13
  s.source = { :git => "https://github.com/vengatmacuser/react-native-inapp-inspector.git", :tag => "v#{s.version}" }
14
14
  s.source_files = "ios/**/*.{h,m,mm,swift}"
15
15
  s.requires_arc = true
16
- s.frameworks = "AVFoundation", "AVKit", "ReplayKit", "CoreMedia", "CoreVideo", "ImageIO", "MobileCoreServices", "QuartzCore"
16
+ s.frameworks = "AVFoundation", "AVKit", "ReplayKit", "CoreMedia", "CoreVideo", "ImageIO", "MobileCoreServices", "QuartzCore", "CoreImage"
17
17
 
18
18
  # React Native New Architecture (TurboModules & Codegen) support
19
19
  if respond_to?(:install_modules_dependencies, true)
@@ -42,7 +42,9 @@ export interface NativeCrashEvent {
42
42
 
43
43
  // Seamless TurboModule (New Architecture / JSI) & Legacy NativeModules Bridge resolution
44
44
  const NativeModule: any =
45
- NativeNetworkInspector || NativeModules.NetworkInspectorModule;
45
+ NativeNetworkInspector ||
46
+ NativeModules.NetworkInspectorModule ||
47
+ NativeModules.NetworkInspector;
46
48
 
47
49
  export const isNativeModuleAvailable = (): boolean => {
48
50
  return !!NativeModule;
@@ -611,9 +613,9 @@ export const fetchCapturedMediaList = async (): Promise<CapturedMediaItem[]> =>
611
613
  return [];
612
614
  }
613
615
  try {
614
- const jsonStr: string = await NativeModule.getCapturedMedia();
615
- if (!jsonStr) return [];
616
- const parsed = JSON.parse(jsonStr);
616
+ const raw: any = await NativeModule.getCapturedMedia();
617
+ if (!raw) return [];
618
+ const parsed = typeof raw === 'string' ? JSON.parse(raw) : raw;
617
619
  return Array.isArray(parsed) ? parsed : [];
618
620
  } catch {
619
621
  return [];
@@ -666,4 +668,55 @@ export const copyMediaToClipboard = async (filePath: string): Promise<boolean> =
666
668
  }
667
669
  };
668
670
 
671
+ /**
672
+ * 100% Native photo editing (lossless crop, rotate, flip, and GPU color grading).
673
+ */
674
+ export const editNativePhoto = async (
675
+ options: import('../types/editor').PhotoEditOptions,
676
+ ): Promise<import('../types/editor').PhotoEditResult | null> => {
677
+ if (!NativeModule || !NativeModule.editPhoto) {
678
+ return null;
679
+ }
680
+ try {
681
+ const result = await NativeModule.editPhoto(options);
682
+ return result as import('../types/editor').PhotoEditResult;
683
+ } catch (err) {
684
+ return null;
685
+ }
686
+ };
687
+
688
+ /**
689
+ * 100% Native hardware-accelerated video trimming and export.
690
+ */
691
+ export const trimNativeVideo = async (
692
+ options: import('../types/editor').VideoTrimOptions,
693
+ ): Promise<import('../types/editor').VideoTrimResult | null> => {
694
+ if (!NativeModule || !NativeModule.trimVideo) {
695
+ return null;
696
+ }
697
+ try {
698
+ const result = await NativeModule.trimVideo(options);
699
+ return result as import('../types/editor').VideoTrimResult;
700
+ } catch (err) {
701
+ return null;
702
+ }
703
+ };
704
+
705
+ /**
706
+ * 100% Native video filmstrip thumbnail generator for timeline scrubbers.
707
+ */
708
+ export const generateNativeFilmstrip = async (
709
+ options: import('../types/editor').FilmstripOptions,
710
+ ): Promise<import('../types/editor').FilmstripResult | null> => {
711
+ if (!NativeModule || !NativeModule.generateFilmstrip) {
712
+ return null;
713
+ }
714
+ try {
715
+ const result = await NativeModule.generateFilmstrip(options);
716
+ return result as import('../types/editor').FilmstripResult;
717
+ } catch (err) {
718
+ return null;
719
+ }
720
+ };
721
+
669
722
 
@@ -26,6 +26,10 @@ export interface Spec extends TurboModule {
26
26
  getCapturedMedia(): Promise<string>;
27
27
  deleteCapturedMedia(uri: string): Promise<boolean>;
28
28
  clearAllCapturedMedia(): Promise<boolean>;
29
+ copyMediaToClipboard(filePath: string): Promise<Object>;
30
+ editPhoto(options: Object): Promise<Object>;
31
+ trimVideo(options: Object): Promise<Object>;
32
+ generateFilmstrip(options: Object): Promise<Object>;
29
33
  addListener(eventName: string): void;
30
34
  removeListeners(count: number): void;
31
35
  }