@trustchex/react-native-sdk 1.361.0 → 1.362.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 (53) hide show
  1. package/TrustchexSDK.podspec +3 -1
  2. package/android/src/main/java/com/trustchex/reactnativesdk/TrustchexSDKPackage.kt +0 -13
  3. package/android/src/main/java/com/trustchex/reactnativesdk/camera/TrustchexCameraManager.kt +0 -8
  4. package/android/src/main/java/com/trustchex/reactnativesdk/camera/TrustchexCameraView.kt +59 -39
  5. package/android/src/main/java/com/trustchex/reactnativesdk/opencv/OpenCVModule.kt +94 -13
  6. package/ios/Camera/TrustchexCameraManager.m +0 -2
  7. package/ios/Camera/TrustchexCameraManager.swift +0 -7
  8. package/ios/Camera/TrustchexCameraView.swift +16 -47
  9. package/ios/OpenCV/OpenCVHelper.h +17 -0
  10. package/ios/OpenCV/OpenCVHelper.mm +128 -0
  11. package/ios/OpenCV/OpenCVModule.h +6 -0
  12. package/ios/OpenCV/OpenCVModule.mm +141 -0
  13. package/ios/TrustchexSDK-Bridging-Header.h +8 -0
  14. package/lib/module/Screens/Debug/MRZTestScreen.js +175 -0
  15. package/lib/module/Shared/Components/DebugNavigationPanel.js +4 -0
  16. package/lib/module/Shared/Components/EIDScanner.js +0 -78
  17. package/lib/module/Shared/Components/IdentityDocumentCamera.js +188 -150
  18. package/lib/module/Shared/Components/QrCodeScannerCamera.js +0 -3
  19. package/lib/module/Shared/Libs/mrz.utils.js +265 -0
  20. package/lib/module/Trustchex.js +4 -0
  21. package/lib/module/index.js +1 -0
  22. package/lib/module/version.js +1 -1
  23. package/lib/typescript/src/Screens/Debug/MRZTestScreen.d.ts +3 -0
  24. package/lib/typescript/src/Screens/Debug/MRZTestScreen.d.ts.map +1 -0
  25. package/lib/typescript/src/Shared/Components/DebugNavigationPanel.d.ts.map +1 -1
  26. package/lib/typescript/src/Shared/Components/EIDScanner.d.ts.map +1 -1
  27. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts +3 -1
  28. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts.map +1 -1
  29. package/lib/typescript/src/Shared/Components/QrCodeScannerCamera.d.ts.map +1 -1
  30. package/lib/typescript/src/Shared/Components/TrustchexCamera.d.ts +0 -19
  31. package/lib/typescript/src/Shared/Components/TrustchexCamera.d.ts.map +1 -1
  32. package/lib/typescript/src/Shared/Libs/mrz.utils.d.ts +18 -1
  33. package/lib/typescript/src/Shared/Libs/mrz.utils.d.ts.map +1 -1
  34. package/lib/typescript/src/Trustchex.d.ts.map +1 -1
  35. package/lib/typescript/src/index.d.ts +3 -0
  36. package/lib/typescript/src/index.d.ts.map +1 -1
  37. package/lib/typescript/src/version.d.ts +1 -1
  38. package/package.json +2 -1
  39. package/src/Screens/Debug/MRZTestScreen.tsx +209 -0
  40. package/src/Shared/Components/DebugNavigationPanel.tsx +5 -0
  41. package/src/Shared/Components/EIDScanner.tsx +0 -53
  42. package/src/Shared/Components/IdentityDocumentCamera.tsx +228 -146
  43. package/src/Shared/Components/QrCodeScannerCamera.tsx +0 -9
  44. package/src/Shared/Components/TrustchexCamera.tsx +0 -20
  45. package/src/Shared/Libs/mrz.utils.ts +289 -1
  46. package/src/Trustchex.tsx +5 -0
  47. package/src/index.tsx +3 -0
  48. package/src/version.ts +1 -1
  49. package/android/src/main/java/com/trustchex/reactnativesdk/mrz/MRZValidationModule.kt +0 -785
  50. package/android/src/main/java/com/trustchex/reactnativesdk/mrz/MRZValidator.kt +0 -419
  51. package/ios/MRZValidation.m +0 -39
  52. package/ios/MRZValidation.swift +0 -802
  53. package/ios/MRZValidator.swift +0 -466
@@ -2,10 +2,11 @@
2
2
 
3
3
  /* eslint-disable react-native/no-inline-styles */
4
4
  import React, { useEffect, useState, useRef, useCallback } from 'react';
5
- import { View, StyleSheet, Text as TextView, Platform, StatusBar, Vibration, Linking, Image, ActivityIndicator, PermissionsAndroid, Dimensions } from 'react-native';
5
+ import { View, StyleSheet, Text as TextView, Platform, StatusBar, Vibration, Linking, Image, ActivityIndicator, PermissionsAndroid, Dimensions, ScrollView } from 'react-native';
6
6
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
7
7
  import { TrustchexCamera } from "./TrustchexCamera.js";
8
8
  import { NativeModules } from 'react-native';
9
+ import mrzUtils from "../Libs/mrz.utils.js";
9
10
  import { useKeepAwake } from "../Libs/native-keep-awake.utils.js";
10
11
  import { useIsFocused } from '@react-navigation/native';
11
12
  import { useTranslation } from 'react-i18next';
@@ -28,7 +29,8 @@ const MIN_BRIGHTNESS_THRESHOLD = 45;
28
29
  const MAX_CONSECUTIVE_QUALITY_FAILURES = 30;
29
30
  const IdentityDocumentCamera = ({
30
31
  onlyMRZScan,
31
- onIdentityDocumentScanned
32
+ onIdentityDocumentScanned,
33
+ testMode = false
32
34
  }) => {
33
35
  useKeepAwake();
34
36
  const theme = useTheme();
@@ -84,6 +86,9 @@ const IdentityDocumentCamera = ({
84
86
  // Barcode caching - persist detected barcode across frames for reliability
85
87
  const cachedBarcode = useRef(null);
86
88
 
89
+ // Test mode tracking
90
+ const [testModeData, setTestModeData] = useState(null);
91
+
87
92
  // Helper to compare MRZ field values (ignore raw text variations)
88
93
  const areMRZFieldsEqual = useCallback((fields1, fields2) => {
89
94
  if (!fields1 || !fields2) return false;
@@ -355,7 +360,7 @@ const IdentityDocumentCamera = ({
355
360
  }, 1000);
356
361
  }
357
362
  }, [setIsTorchOn]);
358
- const handleFaceAndText = useCallback(async (text, faces, frameWidth, frameHeight, barcode, image, elementsOutside, nativeMrzResult) => {
363
+ const handleFaceAndText = useCallback(async (text, faces, frameWidth, frameHeight, barcode, image, elementsOutside, scannedText) => {
359
364
  const detectDocumentType = (facesParam, ocrText, mrzFields, frameWidthParam, mrzTextParam) => {
360
365
  // Relaxed signature detection: matches signature/imza variants and OCR errors
361
366
  const hasSignatureMatch = /s[gi]g?n[au]?t[u]?r|imz[a]s?/i.test(ocrText);
@@ -530,17 +535,44 @@ const IdentityDocumentCamera = ({
530
535
  setStatus('SEARCHING');
531
536
  return;
532
537
  }
533
- const parsedMRZData = nativeMrzResult?.valid && nativeMrzResult.documentCode ? {
534
- valid: true,
535
- fields: nativeMrzResult
536
- } : nativeMrzResult?.documentCode ? {
537
- valid: false,
538
- fields: nativeMrzResult
539
- } : {
540
- valid: false,
541
- fields: null
538
+
539
+ // Use JavaScript MRZ validation with corrections
540
+ // Prefer MRZ-only text if available (from detected MRZ blocks),
541
+ // otherwise fall back to all text (for backward compatibility)
542
+ const textForValidation = scannedText?.mrzOnlyText || text;
543
+ const mrzValidationResult = mrzUtils.validateMRZWithCorrections(textForValidation);
544
+ const parsedMRZData = {
545
+ valid: mrzValidationResult.valid,
546
+ fields: mrzValidationResult.fields || null
542
547
  };
543
- const mrzText = parsedMRZData.valid ? nativeMrzResult?.rawLines : null;
548
+
549
+ // Capture test mode data
550
+ if (testMode && text && text.includes('<')) {
551
+ const mrzOnlyText = scannedText?.mrzOnlyText || text;
552
+ setTestModeData({
553
+ mrzText: mrzOnlyText,
554
+ timestamp: Date.now()
555
+ });
556
+ }
557
+
558
+ // Log MRZ validation details for debugging
559
+ if (isDebugEnabled() && text && text.includes('<')) {
560
+ const mrzLines = text.split('\n').filter(line => line.includes('<') && line.length > 20);
561
+ if (mrzLines.length >= 2) {
562
+ console.log('[MRZ Debug] Raw OCR text lines:', mrzLines.map(l => `"${l}"`));
563
+ console.log('[MRZ Debug] Validation result:', {
564
+ valid: mrzValidationResult.valid,
565
+ format: mrzValidationResult.format,
566
+ documentCode: mrzValidationResult.fields?.documentCode,
567
+ documentNumber: mrzValidationResult.fields?.documentNumber,
568
+ optional1: mrzValidationResult.fields?.optional1,
569
+ error: mrzValidationResult.error
570
+ });
571
+ }
572
+ }
573
+
574
+ // Extract raw MRZ lines from text if validation succeeded
575
+ const mrzText = parsedMRZData.valid ? mrzUtils.fixMRZ(text) : null;
544
576
 
545
577
  // MRZ stability check - require consistent valid reads to avoid OCR noise
546
578
  // Compare parsed field values instead of raw text to handle OCR variations in filler characters
@@ -609,9 +641,17 @@ const IdentityDocumentCamera = ({
609
641
  // CRITICAL: Only accept MRZ with valid checksums AND consistent reads
610
642
  // AND ensure all required fields are present
611
643
  const mrzAccepted = parsedMRZData?.valid === true && hasRequiredFields && mrzStableAndValid;
644
+
645
+ // For Turkish ID cards, barcode should match MRZ optional1 (serial number)
646
+ // But some cards have encoding differences, so be lenient
612
647
  const barcodeMatchesMRZ = barcodeToUse?.rawValue?.trim() === parsedMRZData?.fields?.optional1?.trim();
648
+
649
+ // If barcode doesn't match exactly, check if it contains the optional1 value
650
+ const barcodeContainsMRZ = barcodeToUse?.rawValue?.includes(parsedMRZData?.fields?.optional1?.trim() || '') || parsedMRZData?.fields?.optional1?.includes(barcodeToUse?.rawValue?.trim() || '');
651
+
613
652
  // Require barcode for all documents (no special card fallback)
614
- const barcodeAccepted = onlyMRZScan || barcodeMatchesMRZ;
653
+ // Accept if exact match OR if one contains the other (handles encoding differences)
654
+ const barcodeAccepted = onlyMRZScan || barcodeMatchesMRZ || !!barcodeToUse?.rawValue && barcodeContainsMRZ;
615
655
 
616
656
  // CRITICAL: Require all document elements to be in frame before accepting
617
657
  // For ID_BACK: MRZ + barcode (check directly, not via state to avoid timing issues)
@@ -652,8 +692,11 @@ const IdentityDocumentCamera = ({
652
692
  onlyMRZScan,
653
693
  hasBarcodeValue: !!barcodeToUse?.rawValue,
654
694
  barcodeMatchesMRZ,
695
+ barcodeContainsMRZ,
655
696
  mrzOptional1: parsedMRZData?.fields?.optional1,
656
697
  barcodeValue: barcodeToUse?.rawValue,
698
+ barcodeValueTrimmed: barcodeToUse?.rawValue?.trim(),
699
+ optional1Trimmed: parsedMRZData?.fields?.optional1?.trim(),
657
700
  barcodeSource: barcodeToUse === cachedBarcode.current ? 'cached' : 'current'
658
701
  });
659
702
  }
@@ -1158,7 +1201,7 @@ const IdentityDocumentCamera = ({
1158
1201
  }
1159
1202
  setStatus('SCANNING');
1160
1203
  }
1161
- }, [nextStep, frameDimensions, currentHologramImage, currentFaceImage, hasRequiredMRZFields, areMRZFieldsEqual, detectedDocumentType, onlyMRZScan, isTorchOn, setIsTorchOn, setNextStepAndVibrate, onIdentityDocumentScanned, logMRZDetails, logMRZValidationFailure, currentSecondaryFaceImage, detectHologramNative]);
1204
+ }, [nextStep, frameDimensions, currentHologramImage, currentFaceImage, hasRequiredMRZFields, areMRZFieldsEqual, detectedDocumentType, onlyMRZScan, isTorchOn, setIsTorchOn, setNextStepAndVibrate, onIdentityDocumentScanned, logMRZDetails, logMRZValidationFailure, currentSecondaryFaceImage, detectHologramNative, mrzUtils]);
1162
1205
  const handleFrame = useCallback(async event => {
1163
1206
  if (!isCameraInitialized.current) {
1164
1207
  return;
@@ -1253,6 +1296,8 @@ const IdentityDocumentCamera = ({
1253
1296
  const resultText = textBlocks.map(b => b.text).join('\n');
1254
1297
  const scannedText = {
1255
1298
  resultText,
1299
+ mrzOnlyText: undefined,
1300
+ // Will be set below if MRZ blocks detected
1256
1301
  blocks: textBlocks.map(block => ({
1257
1302
  blockText: block.text || '',
1258
1303
  blockFrame: block.blockFrame ?? {
@@ -1423,6 +1468,12 @@ const IdentityDocumentCamera = ({
1423
1468
  const mrzBlocks = textBlocks.filter(block => block.blockFrame && block.blockFrame.y > bottomHalf && mrzPattern.test(block.text));
1424
1469
  console.log('[Debug] MRZ blocks found:', mrzBlocks.length);
1425
1470
  if (mrzBlocks.length > 0) {
1471
+ // Extract MRZ-only text from detected blocks (sorted by Y position for correct line order)
1472
+ const sortedMrzBlocks = [...mrzBlocks].sort((a, b) => (a.blockFrame?.y || 0) - (b.blockFrame?.y || 0));
1473
+ scannedText.mrzOnlyText = sortedMrzBlocks.map(b => b.text).join('\n');
1474
+ if (isDebugEnabled()) {
1475
+ console.log('[MRZ Extraction] Using only MRZ blocks:', scannedText.mrzOnlyText.substring(0, 100));
1476
+ }
1426
1477
  const minX = Math.min(...mrzBlocks.map(b => b.blockFrame.x));
1427
1478
  const minY = Math.min(...mrzBlocks.map(b => b.blockFrame.y));
1428
1479
  const maxX = Math.max(...mrzBlocks.map(b => b.blockFrame.x + b.blockFrame.width));
@@ -1660,7 +1711,7 @@ const IdentityDocumentCamera = ({
1660
1711
  }
1661
1712
  }
1662
1713
  setElementsOutsideScanArea(outsideElements);
1663
- handleFaceAndText(scannedText.resultText ?? '', detectedFaces, frame.width, frame.height, barcodes.length ? barcodes[0] : undefined, base64Image, outsideElements.length > 0, frame.mrzResult);
1714
+ handleFaceAndText(scannedText.resultText ?? '', detectedFaces, frame.width, frame.height, barcodes.length ? barcodes[0] : undefined, base64Image, outsideElements.length > 0, scannedText);
1664
1715
  } catch (error) {
1665
1716
  console.warn('Frame processing error:', error?.message);
1666
1717
  }
@@ -1745,7 +1796,6 @@ const IdentityDocumentCamera = ({
1745
1796
  enableFrameProcessing: isActive,
1746
1797
  enableFaceDetection: isActive && faceDetectionEnabled,
1747
1798
  enableTextRecognition: isActive,
1748
- enableMrzValidation: isActive,
1749
1799
  enableBarcodeScanning: isActive && nextStep === 'SCAN_ID_BACK',
1750
1800
  includeBase64: isActive,
1751
1801
  targetFps: 10,
@@ -2184,32 +2234,6 @@ const IdentityDocumentCamera = ({
2184
2234
  }],
2185
2235
  children: `${currentSecondaryFaceImage ? '✓ ' : ''}2nd Face`
2186
2236
  })]
2187
- }), isDebugEnabled() && /*#__PURE__*/_jsxs(View, {
2188
- style: styles.imageContainer,
2189
- children: [_currentHologramMaskImage ? /*#__PURE__*/_jsx(Image, {
2190
- source: {
2191
- uri: `data:image/jpeg;base64,${_currentHologramMaskImage}`
2192
- },
2193
- style: styles.faceImage
2194
- }) : /*#__PURE__*/_jsx(View, {
2195
- style: [styles.faceImage, {
2196
- backgroundColor: '#333',
2197
- justifyContent: 'center'
2198
- }],
2199
- children: /*#__PURE__*/_jsx(TextView, {
2200
- style: {
2201
- color: '#666',
2202
- fontSize: 10,
2203
- textAlign: 'center'
2204
- },
2205
- children: "Waiting..."
2206
- })
2207
- }), /*#__PURE__*/_jsx(TextView, {
2208
- style: [styles.imageContainerText, _currentHologramMaskImage && {
2209
- color: '#4CAF50'
2210
- }],
2211
- children: `${_currentHologramMaskImage ? '✓ ' : ''}Mask`
2212
- })]
2213
2237
  }), isDebugEnabled() && /*#__PURE__*/_jsxs(View, {
2214
2238
  style: styles.imageContainer,
2215
2239
  children: [currentHologramImage ? /*#__PURE__*/_jsx(Image, {
@@ -2269,31 +2293,30 @@ const IdentityDocumentCamera = ({
2269
2293
  children: `${currentHologramImage ? '✓ ' : latestHologramFaceImage ? '⏳ ' : ''}Hologram`
2270
2294
  })]
2271
2295
  }), isDebugEnabled() && /*#__PURE__*/_jsxs(View, {
2272
- style: styles.debugInfoContainer,
2273
- children: [/*#__PURE__*/_jsx(TextView, {
2274
- style: styles.debugInfoText,
2275
- children: `Step: ${nextStep}`
2276
- }), /*#__PURE__*/_jsx(TextView, {
2277
- style: styles.debugInfoText,
2278
- children: `Status: ${status}`
2279
- }), /*#__PURE__*/_jsx(TextView, {
2280
- style: styles.debugInfoText,
2281
- children: `Face: ${currentFaceImage ? '✓ CAPTURED' : '✗ WAITING'} ${!faceDetectionEnabled ? '(DISABLED)' : ''}`
2282
- }), /*#__PURE__*/_jsx(TextView, {
2283
- style: styles.debugInfoText,
2284
- children: `Hologram: ${currentHologramImage ? '✓ CAPTURED' : `${hologramImageCount}/${HOLOGRAM_IMAGE_COUNT} imgs`} (retry ${hologramDetectionCurrentRetryCount.current}/${HOLOGRAM_DETECTION_RETRY_COUNT})`
2285
- }), /*#__PURE__*/_jsx(TextView, {
2286
- style: styles.debugInfoText,
2287
- children: `2nd Face: ${currentSecondaryFaceImage ? '✓ CAPTURED' : '✗ WAITING'} (retry ${secondaryFaceDetectionCurrentRetryCount.current}/${SECOND_FACE_DETECTION_RETRY_COUNT})`
2288
- }), /*#__PURE__*/_jsx(TextView, {
2289
- style: styles.debugInfoText,
2290
- children: `Flash: ${isTorchOn ? '🔦 ON' : '🔦 OFF'}`
2291
- }), /*#__PURE__*/_jsx(TextView, {
2292
- style: styles.debugInfoText,
2293
- children: `Brightness: ${isBrightnessLow ? '⚠️ LOW' : '✓ OK'}`
2296
+ style: styles.imageContainer,
2297
+ children: [_currentHologramMaskImage ? /*#__PURE__*/_jsx(Image, {
2298
+ source: {
2299
+ uri: `data:image/jpeg;base64,${_currentHologramMaskImage}`
2300
+ },
2301
+ style: styles.faceImage
2302
+ }) : /*#__PURE__*/_jsx(View, {
2303
+ style: [styles.faceImage, {
2304
+ backgroundColor: '#333',
2305
+ justifyContent: 'center'
2306
+ }],
2307
+ children: /*#__PURE__*/_jsx(TextView, {
2308
+ style: {
2309
+ color: '#666',
2310
+ fontSize: 10,
2311
+ textAlign: 'center'
2312
+ },
2313
+ children: "Waiting..."
2314
+ })
2294
2315
  }), /*#__PURE__*/_jsx(TextView, {
2295
- style: styles.debugInfoText,
2296
- children: `Blur: ${isFrameBlurry ? '⚠️ BLURRY' : '✓ OK'}`
2316
+ style: [styles.imageContainerText, _currentHologramMaskImage && {
2317
+ color: '#4CAF50'
2318
+ }],
2319
+ children: `${_currentHologramMaskImage ? '✓ ' : ''}Mask`
2297
2320
  })]
2298
2321
  })]
2299
2322
  })
@@ -2323,98 +2346,123 @@ const IdentityDocumentCamera = ({
2323
2346
  loop: true,
2324
2347
  autoPlay: true
2325
2348
  }) : null
2326
- }), isDebugEnabled() && /*#__PURE__*/_jsxs(View, {
2349
+ }), isDebugEnabled() && /*#__PURE__*/_jsx(SafeAreaView, {
2327
2350
  style: {
2328
2351
  position: 'absolute',
2329
- top: 10,
2330
- right: 10,
2331
- backgroundColor: 'rgba(0, 0, 0, 0.85)',
2332
- padding: 10,
2333
- borderRadius: 8,
2334
- borderWidth: 1,
2335
- borderColor: '#FF6B6B',
2336
- maxWidth: 200
2352
+ top: 0,
2353
+ left: 0,
2354
+ right: 0,
2355
+ alignItems: 'center',
2356
+ pointerEvents: 'none'
2337
2357
  },
2338
- children: [/*#__PURE__*/_jsx(TextView, {
2339
- style: {
2340
- color: '#FF6B6B',
2341
- fontSize: 11,
2342
- fontWeight: 'bold',
2343
- marginBottom: 6
2344
- },
2345
- children: "\uD83D\uDC1B DEBUG MODE"
2346
- }), /*#__PURE__*/_jsx(TextView, {
2347
- style: {
2348
- color: '#88D8B0',
2349
- fontSize: 9,
2350
- marginBottom: 2
2351
- },
2352
- children: `Step: ${nextStep}`
2353
- }), /*#__PURE__*/_jsx(TextView, {
2354
- style: {
2355
- color: '#88D8B0',
2356
- fontSize: 9,
2357
- marginBottom: 2
2358
- },
2359
- children: `Status: ${status}`
2360
- }), /*#__PURE__*/_jsx(TextView, {
2361
- style: {
2362
- color: '#88D8B0',
2363
- fontSize: 9,
2364
- marginBottom: 2
2365
- },
2366
- children: `Doc Type: ${detectedDocumentType}`
2367
- }), /*#__PURE__*/_jsx(TextView, {
2358
+ children: /*#__PURE__*/_jsxs(View, {
2368
2359
  style: {
2369
- color: '#88D8B0',
2370
- fontSize: 9,
2371
- marginBottom: 2
2360
+ marginTop: 10,
2361
+ backgroundColor: 'rgba(0, 0, 0, 0.85)',
2362
+ padding: 10,
2363
+ borderRadius: 8,
2364
+ borderWidth: 1,
2365
+ borderColor: '#FF6B6B',
2366
+ minWidth: 200
2372
2367
  },
2373
- children: `Face: ${currentFaceImage ? '✓' : '✗'}`
2374
- }), !onlyMRZScan && /*#__PURE__*/_jsxs(_Fragment, {
2375
2368
  children: [/*#__PURE__*/_jsx(TextView, {
2369
+ style: {
2370
+ color: '#FF6B6B',
2371
+ fontSize: 11,
2372
+ fontWeight: 'bold',
2373
+ marginBottom: 6,
2374
+ textAlign: 'center'
2375
+ },
2376
+ children: "DEBUG MODE"
2377
+ }), /*#__PURE__*/_jsx(TextView, {
2378
+ style: {
2379
+ color: '#88D8B0',
2380
+ fontSize: 9,
2381
+ marginBottom: 2
2382
+ },
2383
+ children: `Step: ${nextStep}`
2384
+ }), /*#__PURE__*/_jsx(TextView, {
2385
+ style: {
2386
+ color: '#88D8B0',
2387
+ fontSize: 9,
2388
+ marginBottom: 2
2389
+ },
2390
+ children: `Status: ${status}`
2391
+ }), /*#__PURE__*/_jsx(TextView, {
2392
+ style: {
2393
+ color: '#88D8B0',
2394
+ fontSize: 9,
2395
+ marginBottom: 2
2396
+ },
2397
+ children: `Doc Type: ${detectedDocumentType}`
2398
+ }), /*#__PURE__*/_jsx(TextView, {
2399
+ style: {
2400
+ color: '#88D8B0',
2401
+ fontSize: 9,
2402
+ marginBottom: 2
2403
+ },
2404
+ children: `Brightness: ${isBrightnessLow ? '⚠️ LOW' : '✓'}`
2405
+ }), /*#__PURE__*/_jsx(TextView, {
2376
2406
  style: {
2377
2407
  color: '#88D8B0',
2378
2408
  fontSize: 9,
2379
2409
  marginBottom: 2
2380
2410
  },
2381
- children: `2nd Face: ${currentSecondaryFaceImage ? '' : ''}`
2411
+ children: `Blur: ${isFrameBlurry ? '⚠️' : ''}`
2382
2412
  }), /*#__PURE__*/_jsx(TextView, {
2383
2413
  style: {
2384
2414
  color: '#88D8B0',
2385
2415
  fontSize: 9,
2386
2416
  marginBottom: 2
2387
2417
  },
2388
- children: `Hologram: ${hologramImageCount}/${HOLOGRAM_IMAGE_COUNT}`
2418
+ children: `Flash: ${isTorchOn ? '🔦' : '○'}`
2419
+ }), /*#__PURE__*/_jsx(TextView, {
2420
+ style: {
2421
+ color: '#88D8B0',
2422
+ fontSize: 9
2423
+ },
2424
+ children: `Face Detection: ${faceDetectionEnabled ? '✓' : '✗'}`
2389
2425
  })]
2390
- }), /*#__PURE__*/_jsx(TextView, {
2391
- style: {
2392
- color: '#88D8B0',
2393
- fontSize: 9,
2394
- marginBottom: 2
2395
- },
2396
- children: `Brightness: ${isBrightnessLow ? '⚠️ LOW' : '✓'}`
2397
- }), /*#__PURE__*/_jsx(TextView, {
2398
- style: {
2399
- color: '#88D8B0',
2400
- fontSize: 9,
2401
- marginBottom: 2
2402
- },
2403
- children: `Blur: ${isFrameBlurry ? '⚠️' : '✓'}`
2404
- }), /*#__PURE__*/_jsx(TextView, {
2405
- style: {
2406
- color: '#88D8B0',
2407
- fontSize: 9,
2408
- marginBottom: 2
2409
- },
2410
- children: `Flash: ${isTorchOn ? '🔦' : '○'}`
2411
- }), /*#__PURE__*/_jsx(TextView, {
2426
+ })
2427
+ }), testMode && testModeData && /*#__PURE__*/_jsx(SafeAreaView, {
2428
+ style: {
2429
+ position: 'absolute',
2430
+ bottom: 0,
2431
+ left: 0,
2432
+ right: 0,
2433
+ maxHeight: '40%',
2434
+ backgroundColor: 'rgba(0, 0, 0, 0.95)',
2435
+ borderTopWidth: 2,
2436
+ borderTopColor: '#FFA500'
2437
+ },
2438
+ children: /*#__PURE__*/_jsx(ScrollView, {
2412
2439
  style: {
2413
- color: '#88D8B0',
2414
- fontSize: 9
2440
+ flex: 1
2415
2441
  },
2416
- children: `Face Detection: ${faceDetectionEnabled ? '✓' : '✗'}`
2417
- })]
2442
+ children: /*#__PURE__*/_jsxs(View, {
2443
+ style: {
2444
+ padding: 10
2445
+ },
2446
+ children: [/*#__PURE__*/_jsx(TextView, {
2447
+ style: {
2448
+ color: '#FFA500',
2449
+ fontSize: 12,
2450
+ fontWeight: 'bold',
2451
+ marginBottom: 8,
2452
+ textAlign: 'center'
2453
+ },
2454
+ children: "MRZ Text Read"
2455
+ }), /*#__PURE__*/_jsx(TextView, {
2456
+ style: {
2457
+ color: '#FFFFFF',
2458
+ fontSize: 9,
2459
+ fontFamily: 'monospace',
2460
+ lineHeight: 16
2461
+ },
2462
+ children: testModeData.mrzText.split('\n').map((line, i) => `Line ${i + 1}: ${line} (${line.length} chars)`).join('\n')
2463
+ })]
2464
+ })
2465
+ })
2418
2466
  })]
2419
2467
  })]
2420
2468
  });
@@ -2523,7 +2571,7 @@ const styles = StyleSheet.create({
2523
2571
  display: 'flex',
2524
2572
  flexDirection: 'row',
2525
2573
  gap: 10,
2526
- justifyContent: 'flex-start',
2574
+ justifyContent: 'center',
2527
2575
  flexWrap: 'wrap'
2528
2576
  },
2529
2577
  cardDetectionRow: {
@@ -2563,16 +2611,6 @@ const styles = StyleSheet.create({
2563
2611
  flexDirection: 'column',
2564
2612
  alignItems: 'center'
2565
2613
  },
2566
- debugInfoContainer: {
2567
- flex: 1,
2568
- paddingLeft: 10
2569
- },
2570
- debugInfoText: {
2571
- color: 'white',
2572
- fontSize: 8,
2573
- fontWeight: 'bold',
2574
- marginBottom: 2
2575
- },
2576
2614
  guide: {
2577
2615
  flex: 1,
2578
2616
  display: 'flex',
@@ -39,12 +39,9 @@ const QrCodeScannerCamera = ({
39
39
  right: width * (0.97 - marginBuffer),
40
40
  bottom: height * (0.65 - marginBuffer)
41
41
  };
42
- console.log(`[QR Frame Check] Frame: ${width}x${height}, Scanning: left=${scanningFrame.left}, top=${scanningFrame.top}, right=${scanningFrame.right}, bottom=${scanningFrame.bottom}`);
43
- console.log(`[QR Frame Check] Code bbox: left=${boundingBox.left}, top=${boundingBox.top}, right=${boundingBox.right}, bottom=${boundingBox.bottom}`);
44
42
 
45
43
  // Check if entire bounding box is within scanning frame (with margin buffer)
46
44
  const isInFrame = boundingBox.left >= scanningFrame.left && boundingBox.top >= scanningFrame.top && boundingBox.right <= scanningFrame.right && boundingBox.bottom <= scanningFrame.bottom;
47
- console.log(`[QR Frame Check] Is in frame: ${isInFrame}`);
48
45
  return isInFrame;
49
46
  };
50
47
  const handleFrame = useCallback(async event => {