@trustchex/react-native-sdk 1.513.1 → 1.513.3

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.
@@ -15,6 +15,15 @@ export const MIN_MLI_FACE_SIZE_PERCENT = 0.01; // MLI (Multi Layer Image): min 1
15
15
  export const REQUIRED_CONSISTENT_MRZ_READS = 2;
16
16
  export const REQUIRED_CONSISTENT_DOCTYPE_DETECTIONS = 3;
17
17
 
18
+ // Frames a check-digit-VALID MRZ consensus must stay UNCHANGED before it is
19
+ // accepted regardless of the per-cell vote-margin ("confident") gate. This
20
+ // unblocks a dense passport MRZ where one stubborn OCR-B look-alike keeps a
21
+ // single cell below the margin threshold forever — the scan would otherwise
22
+ // wedge on "hold steady". Set well above the aggregator's stabilityTarget (3)
23
+ // so the normal confident+stable path handles the common case and this only
24
+ // rescues the persistent-look-alike tail (~1.5s at ~8fps).
25
+ export const MRZ_STREAK_ACCEPT_OVERRIDE = 12;
26
+
18
27
  // After this long on a single scan step without completing it, surface the
19
28
  // "Having trouble?" prompt so the user isn't stranded on a live camera with no
20
29
  // timeout, retry, or help. Per-step (re-armed on each step transition).
@@ -24,7 +24,7 @@ import { useTheme } from "../Contexts/ThemeContext.js";
24
24
  import DebugOverlay, { TestModePanel } from "./DebugOverlay.js";
25
25
  import { getStatusMessage, getFrameToScreenTransform, transformBoundsToScreen, getScanAreaBounds, angleBetweenPoints, detectDocumentType, determineDocumentTypeToSet, areMRZFieldsEqual, hasRequiredMRZFields, validateFacePosition, isPassportDocumentCode, StableFlag } from "./IdentityDocumentCamera.utils.js";
26
26
  import { handlePassportFlow, handleIDFrontFlow, handleIDBackFlow, getNextStepAfterHologram } from "./IdentityDocumentCamera.flows.js";
27
- import { HOLOGRAM_IMAGE_COUNT, HOLOGRAM_DETECTION_THRESHOLD, HOLOGRAM_DETECTION_RETRY_COUNT, HOLOGRAM_CAPTURE_INTERVAL, HOLOGRAM_MAX_FRAMES_WITHOUT_FACE, MIN_BRIGHTNESS_THRESHOLD, MAX_BRIGHTNESS_THRESHOLD, FACE_EDGE_MARGIN_PERCENT, MAX_CONSECUTIVE_QUALITY_FAILURES, REQUIRED_CONSISTENT_DOCTYPE_DETECTIONS, HELP_PROMPT_DELAY_MS, SIGNATURE_TEXT_REGEX, MRZ_BLOCK_PATTERN, MIN_CARD_FACE_SIZE_PERCENT } from "./IdentityDocumentCamera.constants.js";
27
+ import { HOLOGRAM_IMAGE_COUNT, HOLOGRAM_DETECTION_THRESHOLD, HOLOGRAM_DETECTION_RETRY_COUNT, HOLOGRAM_CAPTURE_INTERVAL, HOLOGRAM_MAX_FRAMES_WITHOUT_FACE, MIN_BRIGHTNESS_THRESHOLD, FACE_EDGE_MARGIN_PERCENT, MAX_CONSECUTIVE_QUALITY_FAILURES, REQUIRED_CONSISTENT_DOCTYPE_DETECTIONS, MRZ_STREAK_ACCEPT_OVERRIDE, HELP_PROMPT_DELAY_MS, SIGNATURE_TEXT_REGEX, MRZ_BLOCK_PATTERN, MIN_CARD_FACE_SIZE_PERCENT } from "./IdentityDocumentCamera.constants.js";
28
28
 
29
29
  // Re-export types for backward compatibility
30
30
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
@@ -72,11 +72,6 @@ const IdentityDocumentCamera = ({
72
72
  const [currentSecondaryFaceImage, setCurrentSecondaryFaceImage] = useState(undefined);
73
73
  const [isBrightnessLow, setIsBrightnessLow] = useState(false);
74
74
  const [isFrameBlurry, setIsFrameBlurry] = useState(false);
75
- // Glare was being silently rejected (frames dropped with no user feedback), so
76
- // a user fighting reflections just saw the scan hang. Surface it so we can show
77
- // an actionable "move away from glare" hint. Set at each glare rejection;
78
- // cleared when a frame passes the brightness check (a fresh, glare-free frame).
79
- const [isGlareDetected, setIsGlareDetected] = useState(false);
80
75
  const [hasGuideShown, setHasGuideShown] = useState(false);
81
76
  // "Having trouble?" escape: if a scan step doesn't complete within
82
77
  // HELP_PROMPT_DELAY_MS the user is otherwise stuck on a live camera forever
@@ -99,7 +94,7 @@ const IdentityDocumentCamera = ({
99
94
  const consecutiveQualityFailures = useRef(0);
100
95
  const mrzDetectionCurrentRetryCount = useRef(0);
101
96
 
102
- // Hysteresis for the on-screen quality guidance. Brightness/blur/glare are
97
+ // Hysteresis for the on-screen quality guidance. Brightness/blur are
103
98
  // measured every frame; feeding the raw per-frame booleans straight into
104
99
  // state made the guidance strobe (a single blurry hand-shake frame flipped
105
100
  // "hold steady" on and off). These smooth each flag: it appears after a few
@@ -108,19 +103,11 @@ const IdentityDocumentCamera = ({
108
103
  // problems), clear slow (don't flicker off on one good frame).
109
104
  const brightnessLowFlag = useRef(new StableFlag(2, 5));
110
105
  const blurryFlag = useRef(new StableFlag(3, 6));
111
- const glareFlag = useRef(new StableFlag(2, 5));
112
- // Glare is asserted by accept-time helpers that run inside the (separately
113
- // scoped) handleFaceAndText callback, so it can't flow through a handler
114
- // local. This ref is cleared at the top of each frame and set true if any
115
- // glare check in that frame found glare; the handler reads it to feed
116
- // `glareFlag`. `hadGlareText` distinguishes "card present but glaring" from
117
- // an empty view so the warning only shows over an actual document.
118
- const rawGlareRef = useRef(false);
119
106
  // Whether a document was visible in the PREVIOUS frame (recognized text,
120
107
  // barcode, or face). The quality guidance is stabilized BEFORE the quality
121
108
  // gate (so dark/blurry frames — which the gate early-returns on — can still
122
109
  // raise their warnings), but document-presence is only known LATER in the
123
- // frame, so blur/glare are gated on the previous frame's value. One-frame lag
110
+ // frame, so blur is gated on the previous frame's value. One-frame lag
124
111
  // at ~8fps is imperceptible and the hysteresis smooths it.
125
112
  const documentPresentRef = useRef(false);
126
113
 
@@ -191,8 +178,7 @@ const IdentityDocumentCamera = ({
191
178
  frameW: 0,
192
179
  frameH: 0,
193
180
  hadLowBrightness: false,
194
- hadBlurryFrames: false,
195
- hadGlare: false
181
+ hadBlurryFrames: false
196
182
  });
197
183
  const [faceDetectionEnabled, setFaceDetectionEnabled] = useState(true);
198
184
  const faceImages = useRef([]);
@@ -258,8 +244,7 @@ const IdentityDocumentCamera = ({
258
244
  frameW: 0,
259
245
  frameH: 0,
260
246
  hadLowBrightness: false,
261
- hadBlurryFrames: false,
262
- hadGlare: false
247
+ hadBlurryFrames: false
263
248
  };
264
249
  cachedBarcode.current = null;
265
250
  isCompletionCallbackInvoked.current = false;
@@ -295,7 +280,7 @@ const IdentityDocumentCamera = ({
295
280
  }, [isFocused, hasPermission, hasGuideShown]);
296
281
  useEffect(() => {
297
282
  if (!hasGuideShown || !appContext.currentWorkflowStep?.data?.voiceGuidanceActive) return;
298
- const message = getStatusMessage(nextStep, status, detectedDocumentType, isBrightnessLow, isFrameBlurry, isGlareDetected, allElementsDetected, elementsOutsideScanArea, t);
283
+ const message = getStatusMessage(nextStep, status, detectedDocumentType, isBrightnessLow, isFrameBlurry, allElementsDetected, elementsOutsideScanArea, t);
299
284
  if (message) {
300
285
  if (nextStep === 'COMPLETED') {
301
286
  if (isCompletionAnnouncementSpoken.current) {
@@ -308,7 +293,7 @@ const IdentityDocumentCamera = ({
308
293
  const shouldBypassInterval = nextStep !== 'COMPLETED';
309
294
  speak(message, shouldBypassInterval);
310
295
  }
311
- }, [appContext.currentWorkflowStep?.data?.voiceGuidanceActive, hasGuideShown, isBrightnessLow, isFrameBlurry, isGlareDetected, nextStep, status, detectedDocumentType, allElementsDetected, elementsOutsideScanArea, t]);
296
+ }, [appContext.currentWorkflowStep?.data?.voiceGuidanceActive, hasGuideShown, isBrightnessLow, isFrameBlurry, nextStep, status, detectedDocumentType, allElementsDetected, elementsOutsideScanArea, t]);
312
297
  useEffect(() => {
313
298
  if (status === 'INCORRECT') {
314
299
  const timeout = setTimeout(() => {
@@ -436,54 +421,12 @@ const IdentityDocumentCamera = ({
436
421
  }
437
422
  };
438
423
 
439
- // Check if face image has glare (brightness exceeds threshold). Also surfaces
440
- // the result to `isGlareDetected` so the user gets an actionable hint instead
441
- // of a silently-stalled scan.
442
- const checkFaceGlare = async faceImage => {
443
- try {
444
- // Check entire face region for glare
445
- const hasGlare = await OpenCVModule.isRectangularRegionBright(faceImage, 0, 0, 100,
446
- // Full face width
447
- 100,
448
- // Full face height
449
- MAX_BRIGHTNESS_THRESHOLD);
450
- // OR-accumulate into the per-frame glare signal (reset at frame top); the
451
- // frame handler feeds it through the stabilized UI flag. Also record the
452
- // diagnostic.
453
- if (hasGlare) {
454
- rawGlareRef.current = true;
455
- diagStats.current.hadGlare = true;
456
- }
457
- return hasGlare;
458
- } catch (error) {
459
- return false; // Assume no glare on error
460
- }
461
- };
462
-
463
424
  // Check if face is fully visible (not cropped at edges)
464
425
  const isFaceFullyVisible = (face, frameWidth, frameHeight) => {
465
426
  const margin = FACE_EDGE_MARGIN_PERCENT;
466
427
  const bounds = face.bounds;
467
428
  return bounds.x >= frameWidth * margin && bounds.y >= frameHeight * margin && bounds.x + bounds.width <= frameWidth * (1 - margin) && bounds.y + bounds.height <= frameHeight * (1 - margin);
468
429
  };
469
-
470
- // Check if document image has glare
471
- const checkDocumentGlare = async (image, width, height) => {
472
- try {
473
- // Check center 80% region for glare (document area)
474
- const hasGlare = await OpenCVModule.isRectangularRegionBright(image, Math.round(width * 0.1), Math.round(height * 0.1), Math.round(width * 0.8), Math.round(height * 0.8), MAX_BRIGHTNESS_THRESHOLD);
475
- // OR-accumulate into the per-frame glare signal (reset at frame top); the
476
- // frame handler feeds it through the stabilized UI flag. Also record the
477
- // diagnostic.
478
- if (hasGlare) {
479
- rawGlareRef.current = true;
480
- diagStats.current.hadGlare = true;
481
- }
482
- return hasGlare;
483
- } catch (error) {
484
- return false;
485
- }
486
- };
487
430
  const setNextStepAndVibrate = useCallback((nextStepType, fromStep) => {
488
431
  if (fromStep) {
489
432
  setCompletedStep(fromStep);
@@ -550,12 +493,9 @@ const IdentityDocumentCamera = ({
550
493
  // carried over from the previous side).
551
494
  brightnessLowFlag.current.reset(false);
552
495
  blurryFlag.current.reset(false);
553
- glareFlag.current.reset(false);
554
- rawGlareRef.current = false;
555
496
  documentPresentRef.current = false;
556
497
  setIsBrightnessLow(false);
557
498
  setIsFrameBlurry(false);
558
- setIsGlareDetected(false);
559
499
  if (nextStepType !== 'COMPLETED') {
560
500
  setTimeout(() => {
561
501
  setStatus('SEARCHING');
@@ -818,13 +758,11 @@ const IdentityDocumentCamera = ({
818
758
  if (shouldCropFaces && croppedFaces.length > 0 && croppedFaces[0] && facePositionValid && !currentFaceImage) {
819
759
  // Validate face quality before accepting
820
760
  const faceFullyVisible = primaryFaces[0] && isFaceFullyVisible(primaryFaces[0], frameWidth, frameHeight);
821
- const hasGlare = await checkFaceGlare(croppedFaces[0]);
822
- if (!faceFullyVisible || hasGlare) {
823
- // Reject face with glare or partially visible
761
+ if (!faceFullyVisible) {
762
+ // Reject partially visible face
824
763
  if (isDebugEnabled()) {
825
764
  debugLog('IdentityDocumentCamera', '[FACE] Rejected', {
826
- fullyVisible: faceFullyVisible,
827
- hasGlare
765
+ fullyVisible: faceFullyVisible
828
766
  });
829
767
  }
830
768
  // Continue scanning without locking this face
@@ -872,7 +810,19 @@ const IdentityDocumentCamera = ({
872
810
  // vote must be unchanged across `stabilityTarget` consecutive contributing
873
811
  // frames, which is exactly the "only accept once stable" guarantee.
874
812
  // `addFrame` runs for every frame above, so the consensus is always current.
875
- const mrzStableAndValid = consensus.stable && parsedMRZData?.valid === true;
813
+ // `consensus.stable` also requires a "confident" vote — every contested
814
+ // cell's winner must lead by a margin. On a dense TD3 passport MRZ (88
815
+ // cells) a single stubborn OCR-B look-alike (A↔4, S↔5) can keep one cell
816
+ // at a 1-vote margin indefinitely, so `stable` never flips and the scan
817
+ // wedges on "hold steady" — even though the check-digit-VALID consensus
818
+ // has been UNCHANGED for many frames. Check digits are the real correctness
819
+ // guarantee; a long unchanged, valid streak is conclusive. So also accept
820
+ // once a valid consensus has held unchanged for MRZ_STREAK_ACCEPT_OVERRIDE
821
+ // frames (well above `stabilityTarget`), independent of the margin gate.
822
+ // Short streaks still require the full confident+stable gate, preserving
823
+ // the early-misread protection.
824
+ const mrzValidStreakSettled = parsedMRZData?.valid === true && consensus.stableStreak >= MRZ_STREAK_ACCEPT_OVERRIDE;
825
+ const mrzStableAndValid = (consensus.stable || mrzValidStreakSettled) && parsedMRZData?.valid === true;
876
826
 
877
827
  // Surface the reliable-detection signal to the render so the UI can show a
878
828
  // green "GO" the moment (and only once) the consensus is stable + valid.
@@ -940,16 +890,6 @@ const IdentityDocumentCamera = ({
940
890
  setStatus(mrzText ? 'SCANNING' : 'SEARCHING');
941
891
  return;
942
892
  }
943
-
944
- // Check for glare on ID back before accepting
945
- const hasGlare = await checkDocumentGlare(image, frameWidth, frameHeight);
946
- if (hasGlare) {
947
- if (isDebugEnabled()) {
948
- debugLog('IdentityDocumentCamera', '[ID_BACK] Rejected - glare detected');
949
- }
950
- setStatus('SCANNING');
951
- return;
952
- }
953
893
  if (isDebugEnabled()) {
954
894
  debugLog('IdentityDocumentCamera', '[ID_BACK] MRZ validated', {
955
895
  documentNumber: parsedMRZData?.fields?.documentNumber,
@@ -1071,22 +1011,13 @@ const IdentityDocumentCamera = ({
1071
1011
  if (!isTorchOnRef.current) {
1072
1012
  setIsTorchOn(true);
1073
1013
  }
1074
-
1075
- // Check hologram face for glare
1076
- const hasGlare = await checkFaceGlare(primaryFaceOnly);
1077
- if (!hasGlare) {
1078
- faceImages.current.push(primaryFaceOnly);
1079
- lastHologramCaptureTime.current = Date.now();
1080
- hologramImageCountRef.current = faceImages.current.length;
1081
- if (isDebugEnabled()) {
1082
- debugLog('IdentityDocumentCamera', '[HOLOGRAM] Frame captured', {
1083
- frameIndex: faceImages.current.length - 1,
1084
- totalFrames: faceImages.current.length
1085
- });
1086
- }
1087
- } else if (isDebugEnabled()) {
1088
- debugLog('IdentityDocumentCamera', '[HOLOGRAM] Rejected glare frame', {
1089
- collectedCount: faceImages.current.length
1014
+ faceImages.current.push(primaryFaceOnly);
1015
+ lastHologramCaptureTime.current = Date.now();
1016
+ hologramImageCountRef.current = faceImages.current.length;
1017
+ if (isDebugEnabled()) {
1018
+ debugLog('IdentityDocumentCamera', '[HOLOGRAM] Frame captured', {
1019
+ frameIndex: faceImages.current.length - 1,
1020
+ totalFrames: faceImages.current.length
1090
1021
  });
1091
1022
  }
1092
1023
  if (faceImages.current.length > 0) {
@@ -1356,16 +1287,6 @@ const IdentityDocumentCamera = ({
1356
1287
  continueScanning(true);
1357
1288
  return;
1358
1289
  }
1359
-
1360
- // Check for glare on passport before accepting
1361
- const hasGlare = await checkDocumentGlare(image, frameWidth, frameHeight);
1362
- if (hasGlare) {
1363
- if (isDebugEnabled()) {
1364
- debugLog('IdentityDocumentCamera', '[PASSPORT] Rejected - glare detected');
1365
- }
1366
- continueScanning(false);
1367
- return;
1368
- }
1369
1290
  setDetectedDocumentType('PASSPORT');
1370
1291
  setStatus('SCANNED');
1371
1292
  const nextPassportStep = flowResult.nextAction === 'PROCEED_TO_COMPLETED' ? 'COMPLETED' : 'SCAN_HOLOGRAM';
@@ -1382,16 +1303,6 @@ const IdentityDocumentCamera = ({
1382
1303
  continueScanning(flowResult.nextAction !== 'REJECT_AS_PASSPORT');
1383
1304
  return;
1384
1305
  }
1385
-
1386
- // Check for glare on ID front before accepting
1387
- const hasGlare = await checkDocumentGlare(image, frameWidth, frameHeight);
1388
- if (hasGlare) {
1389
- if (isDebugEnabled()) {
1390
- debugLog('IdentityDocumentCamera', '[ID_FRONT] Rejected - glare detected');
1391
- }
1392
- continueScanning(false);
1393
- return;
1394
- }
1395
1306
  setDetectedDocumentType('ID_FRONT');
1396
1307
  setStatus('SCANNED');
1397
1308
  const nextIdFrontStep = onlyMRZScan ? 'SCAN_ID_BACK' : 'SCAN_HOLOGRAM';
@@ -1460,18 +1371,6 @@ const IdentityDocumentCamera = ({
1460
1371
  const rawBrightnessLow = !isRegionBright;
1461
1372
  if (rawBrightnessLow) diagStats.current.hadLowBrightness = true;
1462
1373
 
1463
- // Per-frame glare is asserted by the accept-time helpers (checkDocumentGlare
1464
- // /checkFaceGlare), but those run inside handleFaceAndText which is fired
1465
- // (async, un-awaited) LATER in this handler — so this frame's glare result
1466
- // isn't known yet at the stabilization block below. We therefore feed the
1467
- // stabilizer the PREVIOUS frame's glare signal (captured here before the
1468
- // reset). At ~8fps the one-frame lag is imperceptible and the hysteresis
1469
- // smooths it anyway; without this the read would always see false and the
1470
- // glare warning could never fire. Reset after capturing so a now-clean
1471
- // view clears (no latch).
1472
- const prevFrameGlare = rawGlareRef.current;
1473
- rawGlareRef.current = false;
1474
-
1475
1374
  // Check blur only in center region (area of interest) to avoid false positives
1476
1375
  // from iOS depth-of-field background blur
1477
1376
  let isNotBlurry = true;
@@ -1521,8 +1420,8 @@ const IdentityDocumentCamera = ({
1521
1420
  // MUST run BEFORE the quality-gate early-return below: a dark or blurry
1522
1421
  // frame is exactly what makes hasAcceptableQuality false, so if this ran
1523
1422
  // after the gate the "too dark" / "hold steady" warnings could never
1524
- // raise (their triggering frames would have already returned). Blur/glare
1525
- // are gated on document presence; brightness shows regardless, since a
1423
+ // raise (their triggering frames would have already returned). Blur
1424
+ // is gated on document presence; brightness shows regardless, since a
1526
1425
  // dark room is worth flagging before the card is even framed.
1527
1426
  //
1528
1427
  // Presence combines the previous frame's confirmed value (documentPresent-
@@ -1531,13 +1430,12 @@ const IdentityDocumentCamera = ({
1531
1430
  // processing). Without the raw proxy, a document that is blurry/dark from
1532
1431
  // its very first framed moment would keep failing the quality gate and
1533
1432
  // early-returning before documentPresentRef is ever set true — suppressing
1534
- // its blur/glare hints for ~30 frames. The proxy lets the hint fire on the
1433
+ // its blur hints for ~30 frames. The proxy lets the hint fire on the
1535
1434
  // first framed frame.
1536
1435
  const rawDocumentPresent = (frame.faces?.length ?? 0) > 0 || (frame.textBlocks?.length ?? 0) > 0 || (frame.barcodes?.length ?? 0) > 0;
1537
1436
  const documentWasPresent = documentPresentRef.current || rawDocumentPresent;
1538
1437
  setIsBrightnessLow(brightnessLowFlag.current.push(rawBrightnessLow));
1539
1438
  setIsFrameBlurry(blurryFlag.current.push(documentWasPresent && isBlurry));
1540
- setIsGlareDetected(glareFlag.current.push(documentWasPresent && prevFrameGlare));
1541
1439
  // -------------------------------------------------------------------
1542
1440
 
1543
1441
  // Store quality metrics in ref for access in handleFaceAndText callback
@@ -1598,6 +1496,24 @@ const IdentityDocumentCamera = ({
1598
1496
  }))
1599
1497
  };
1600
1498
 
1499
+ // Isolate the MRZ text (bottom-of-document blocks matching the MRZ
1500
+ // pattern) and feed ONLY that to the multi-frame consensus. CRITICAL:
1501
+ // this must run for EVERY build, not just debug — the identical logic
1502
+ // used to live only inside an `if (isDebugEnabled())` overlay block, so
1503
+ // in release `mrzOnlyText` was never set and the aggregator fell back to
1504
+ // the FULL OCR text (including the passport header "PASSPORT / TÜR /
1505
+ // REPUBLIC OF…"). That header noise kept the per-character consensus from
1506
+ // ever stabilizing, so a passport wedged on "hold steady" forever in
1507
+ // release while working in debug/example. Passport MRZ sits low on the
1508
+ // page; the `y > bottom-half` filter drops the header/photo-area text.
1509
+ if (textBlocks.length > 0) {
1510
+ const bottomHalf = frame.height * 0.5;
1511
+ const mrzBlocks = textBlocks.filter(block => block.blockFrame && block.blockFrame.y > bottomHalf && MRZ_BLOCK_PATTERN.test(block.text));
1512
+ if (mrzBlocks.length > 0) {
1513
+ scannedText.mrzOnlyText = [...mrzBlocks].sort((a, b) => (a.blockFrame?.y || 0) - (b.blockFrame?.y || 0)).map(b => b.text).join('\n');
1514
+ }
1515
+ }
1516
+
1601
1517
  // Read barcodes directly from native ML Kit results
1602
1518
  let barcodes = [];
1603
1519
  if (frame.barcodes) {
@@ -1979,12 +1895,6 @@ const IdentityDocumentCamera = ({
1979
1895
  current: 3,
1980
1896
  total: 3
1981
1897
  })}` : ''
1982
- }), nextStep === 'SCAN_HOLOGRAM' && status !== 'SCANNED' && /*#__PURE__*/_jsx(TextView, {
1983
- style: styles.hologramProgress,
1984
- children: t('identityDocumentCamera.hologramProgress', {
1985
- current: hologramImageCount,
1986
- total: HOLOGRAM_IMAGE_COUNT
1987
- })
1988
1898
  }), /*#__PURE__*/_jsx(AnimatedText, {
1989
1899
  style: [styles.topZoneText,
1990
1900
  // Priority order for coloring (later styles override earlier ones)
@@ -1997,12 +1907,12 @@ const IdentityDocumentCamera = ({
1997
1907
  opacity: errorFlashAnim
1998
1908
  },
1999
1909
  // 3. Warning (yellow) - quality issues
2000
- (isBrightnessLow || isFrameBlurry || isGlareDetected) && styles.topZoneTextWarning,
1910
+ (isBrightnessLow || isFrameBlurry) && styles.topZoneTextWarning,
2001
1911
  // 4. Scanning (green) - all elements detected AND inside scan area
2002
- status === 'SCANNING' && allElementsDetected && elementsOutsideScanArea.length === 0 && !isBrightnessLow && !isFrameBlurry && !isGlareDetected && styles.topZoneTextScanning
1912
+ status === 'SCANNING' && allElementsDetected && elementsOutsideScanArea.length === 0 && !isBrightnessLow && !isFrameBlurry && styles.topZoneTextScanning
2003
1913
  // 5. Default (white) - aligning (not all detected OR elements outside scan area)
2004
1914
  ],
2005
- children: getStatusMessage(nextStep, status, detectedDocumentType, isBrightnessLow, isFrameBlurry, isGlareDetected, allElementsDetected, elementsOutsideScanArea, t)
1915
+ children: getStatusMessage(nextStep, status, detectedDocumentType, isBrightnessLow, isFrameBlurry, allElementsDetected, elementsOutsideScanArea, t)
2006
1916
  })]
2007
1917
  }), /*#__PURE__*/_jsx(View, {
2008
1918
  style: styles.leftZone
@@ -2034,7 +1944,7 @@ const IdentityDocumentCamera = ({
2034
1944
  loop: true,
2035
1945
  autoPlay: true
2036
1946
  }) : null
2037
- }), isDebugEnabled() && /*#__PURE__*/_jsx(DebugOverlay, {
1947
+ }), __DEV__ && isDebugEnabled() && /*#__PURE__*/_jsx(DebugOverlay, {
2038
1948
  nextStep: nextStep,
2039
1949
  status: status,
2040
1950
  detectedDocumentType: detectedDocumentType,
@@ -2054,7 +1964,7 @@ const IdentityDocumentCamera = ({
2054
1964
  hologramImageCount: hologramImageCount,
2055
1965
  allElementsDetected: allElementsDetected,
2056
1966
  elementsOutsideScanArea: elementsOutsideScanArea
2057
- }), testMode && testModeData && /*#__PURE__*/_jsx(TestModePanel, {
1967
+ }), __DEV__ && testMode && testModeData && /*#__PURE__*/_jsx(TestModePanel, {
2058
1968
  mrzText: testModeData.mrzText
2059
1969
  }), showHelpPrompt && !helpSheetVisible && nextStep !== 'COMPLETED' && /*#__PURE__*/_jsx(TouchableOpacity, {
2060
1970
  style: styles.helpPrompt,
@@ -2171,13 +2081,6 @@ const styles = StyleSheet.create({
2171
2081
  fontWeight: '500',
2172
2082
  marginBottom: 4
2173
2083
  },
2174
- hologramProgress: {
2175
- color: '#4CAF50',
2176
- fontSize: 14,
2177
- textAlign: 'center',
2178
- fontWeight: '600',
2179
- marginBottom: 4
2180
- },
2181
2084
  topZoneText: {
2182
2085
  color: 'white',
2183
2086
  fontSize: 20,
@@ -147,7 +147,7 @@ export function transformBoundsToScreen(bounds, scale, offsetX, offsetY) {
147
147
  * Unified status message logic used by both voice guidance and render text.
148
148
  * Returns the appropriate i18n key arguments for the current scan state.
149
149
  */
150
- export function getStatusMessage(nextStep, status, detectedDocumentType, isBrightnessLow, isFrameBlurry, isGlareDetected, allElementsDetected, elementsOutsideScanArea, t) {
150
+ export function getStatusMessage(nextStep, status, detectedDocumentType, isBrightnessLow, isFrameBlurry, allElementsDetected, elementsOutsideScanArea, t) {
151
151
  if (nextStep === 'COMPLETED') {
152
152
  return t('identityDocumentCamera.scanCompleted');
153
153
  }
@@ -173,14 +173,6 @@ export function getStatusMessage(nextStep, status, detectedDocumentType, isBrigh
173
173
  if (isBrightnessLow) {
174
174
  return t('identityDocumentCamera.lowBrightness');
175
175
  }
176
-
177
- // Glare is a primary failure mode (reflections off the laminate/photo). It was
178
- // previously detected but never communicated, so the scan just appeared to
179
- // hang. Tell the user how to fix it. Ranked above blur because a glare hotspot
180
- // both blocks acceptance and often reads as a "sharp" (non-blurry) frame.
181
- if (isGlareDetected) {
182
- return t('identityDocumentCamera.reduceGlare');
183
- }
184
176
  if (isFrameBlurry) {
185
177
  return t('identityDocumentCamera.avoidBlur');
186
178
  }
@@ -182,10 +182,8 @@ export default {
182
182
  'identityDocumentCamera.frontSide': 'Front',
183
183
  'identityDocumentCamera.backSide': 'Back',
184
184
  'identityDocumentCamera.hologramCheck': 'Hologram',
185
- 'identityDocumentCamera.hologramProgress': 'Capturing hologram… {{current}}/{{total}}',
186
185
  'identityDocumentCamera.keepSteady': 'Keep device steady',
187
186
  'identityDocumentCamera.avoidBlur': 'Keep device steady',
188
- 'identityDocumentCamera.reduceGlare': 'Glare detected. Tilt the document or move away from direct light.',
189
187
  'identityDocumentCamera.wrongSideFront': 'Show the front side of your document',
190
188
  'identityDocumentCamera.wrongSideBack': 'Show the back side of your document',
191
189
  'identityDocumentCamera.idCardDetected': 'ID detected',
@@ -182,10 +182,8 @@ export default {
182
182
  'identityDocumentCamera.frontSide': 'Ön',
183
183
  'identityDocumentCamera.backSide': 'Arka',
184
184
  'identityDocumentCamera.hologramCheck': 'Hologram',
185
- 'identityDocumentCamera.hologramProgress': 'Hologram yakalanıyor… {{current}}/{{total}}',
186
185
  'identityDocumentCamera.keepSteady': 'Cihazı sabit tutun',
187
186
  'identityDocumentCamera.avoidBlur': 'Cihazı sabit tutun',
188
- 'identityDocumentCamera.reduceGlare': 'Parlama algılandı. Belgeyi hafifçe eğin veya doğrudan ışıktan uzaklaşın.',
189
187
  'identityDocumentCamera.wrongSideFront': 'Belgenizin ön yüzünü gösterin',
190
188
  'identityDocumentCamera.wrongSideBack': 'Belgenizin arka yüzünü gösterin',
191
189
  'identityDocumentCamera.idCardDetected': 'Kimlik kartı algılandı',
@@ -2,4 +2,4 @@
2
2
 
3
3
  // This file is auto-generated. Do not edit manually.
4
4
  // Version is synced from package.json during build.
5
- export const SDK_VERSION = '1.513.1';
5
+ export const SDK_VERSION = '1.513.3';
@@ -11,6 +11,7 @@ export declare const MIN_CARD_FACE_SIZE_PERCENT = 0.05;
11
11
  export declare const MIN_MLI_FACE_SIZE_PERCENT = 0.01;
12
12
  export declare const REQUIRED_CONSISTENT_MRZ_READS = 2;
13
13
  export declare const REQUIRED_CONSISTENT_DOCTYPE_DETECTIONS = 3;
14
+ export declare const MRZ_STREAK_ACCEPT_OVERRIDE = 12;
14
15
  export declare const HELP_PROMPT_DELAY_MS = 25000;
15
16
  export declare const SIGNATURE_REGEX: RegExp;
16
17
  export declare const SIGNATURE_TEXT_REGEX: RegExp;
@@ -1 +1 @@
1
- {"version":3,"file":"IdentityDocumentCamera.constants.d.ts","sourceRoot":"","sources":["../../../../../src/Shared/Components/IdentityDocumentCamera.constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,oBAAoB,KAAK,CAAC;AACvC,eAAO,MAAM,4BAA4B,OAAO,CAAC;AACjD,eAAO,MAAM,8BAA8B,IAAI,CAAC;AAChD,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAC7C,eAAO,MAAM,gCAAgC,KAAK,CAAC;AAEnD,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAC3C,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAC5C,eAAO,MAAM,gCAAgC,KAAK,CAAC;AACnD,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAC7C,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAC/C,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAE9C,eAAO,MAAM,6BAA6B,IAAI,CAAC;AAC/C,eAAO,MAAM,sCAAsC,IAAI,CAAC;AAKxD,eAAO,MAAM,oBAAoB,QAAQ,CAAC;AAE1C,eAAO,MAAM,eAAe,QAAkC,CAAC;AAC/D,eAAO,MAAM,oBAAoB,QAAyB,CAAC;AAC3D,eAAO,MAAM,iBAAiB,QAAsB,CAAC;AASrD,eAAO,MAAM,iBAAiB,QAA+B,CAAC;AAS9D,eAAO,MAAM,oBAAoB,QAAyC,CAAC;AAI3E,eAAO,MAAM,8BAA8B,IAAI,CAAC;AAChD,eAAO,MAAM,sBAAsB;;;;;;;;;;CAUzB,CAAC;AAKX,eAAO,MAAM,0BAA0B,MAAM,CAAC;AAC9C,eAAO,MAAM,gCAAgC,MAAM,CAAC;AACpD,eAAO,MAAM,2BAA2B,OAAO,CAAC;AAChD,eAAO,MAAM,mBAAmB,MAAM,CAAC;AACvC,eAAO,MAAM,mBAAmB,MAAM,CAAC"}
1
+ {"version":3,"file":"IdentityDocumentCamera.constants.d.ts","sourceRoot":"","sources":["../../../../../src/Shared/Components/IdentityDocumentCamera.constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,oBAAoB,KAAK,CAAC;AACvC,eAAO,MAAM,4BAA4B,OAAO,CAAC;AACjD,eAAO,MAAM,8BAA8B,IAAI,CAAC;AAChD,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAC7C,eAAO,MAAM,gCAAgC,KAAK,CAAC;AAEnD,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAC3C,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAC5C,eAAO,MAAM,gCAAgC,KAAK,CAAC;AACnD,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAC7C,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAC/C,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAE9C,eAAO,MAAM,6BAA6B,IAAI,CAAC;AAC/C,eAAO,MAAM,sCAAsC,IAAI,CAAC;AASxD,eAAO,MAAM,0BAA0B,KAAK,CAAC;AAK7C,eAAO,MAAM,oBAAoB,QAAQ,CAAC;AAE1C,eAAO,MAAM,eAAe,QAAkC,CAAC;AAC/D,eAAO,MAAM,oBAAoB,QAAyB,CAAC;AAC3D,eAAO,MAAM,iBAAiB,QAAsB,CAAC;AASrD,eAAO,MAAM,iBAAiB,QAA+B,CAAC;AAS9D,eAAO,MAAM,oBAAoB,QAAyC,CAAC;AAI3E,eAAO,MAAM,8BAA8B,IAAI,CAAC;AAChD,eAAO,MAAM,sBAAsB;;;;;;;;;;CAUzB,CAAC;AAKX,eAAO,MAAM,0BAA0B,MAAM,CAAC;AAC9C,eAAO,MAAM,gCAAgC,MAAM,CAAC;AACpD,eAAO,MAAM,2BAA2B,OAAO,CAAC;AAChD,eAAO,MAAM,mBAAmB,MAAM,CAAC;AACvC,eAAO,MAAM,mBAAmB,MAAM,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"IdentityDocumentCamera.d.ts","sourceRoot":"","sources":["../../../../../src/Shared/Components/IdentityDocumentCamera.tsx"],"names":[],"mappings":"AA6EA,OAAO,KAAK,EACV,mBAAmB,EACnB,SAAS,EACT,2BAA2B,EAK5B,MAAM,gCAAgC,CAAC;AAGxC,YAAY,EAAE,mBAAmB,EAAE,SAAS,EAAE,2BAA2B,EAAE,CAAC;AAC5E,YAAY,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAMnE,QAAA,MAAM,sBAAsB,GAAI,uDAI7B,2BAA2B,4CA02F7B,CAAC;AA0LF,eAAe,sBAAsB,CAAC"}
1
+ {"version":3,"file":"IdentityDocumentCamera.d.ts","sourceRoot":"","sources":["../../../../../src/Shared/Components/IdentityDocumentCamera.tsx"],"names":[],"mappings":"AA6EA,OAAO,KAAK,EACV,mBAAmB,EACnB,SAAS,EACT,2BAA2B,EAK5B,MAAM,gCAAgC,CAAC;AAGxC,YAAY,EAAE,mBAAmB,EAAE,SAAS,EAAE,2BAA2B,EAAE,CAAC;AAC5E,YAAY,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAMnE,QAAA,MAAM,sBAAsB,GAAI,uDAI7B,2BAA2B,4CA2uF7B,CAAC;AAmLF,eAAe,sBAAsB,CAAC"}
@@ -82,7 +82,7 @@ export declare function transformBoundsToScreen(bounds: {
82
82
  * Unified status message logic used by both voice guidance and render text.
83
83
  * Returns the appropriate i18n key arguments for the current scan state.
84
84
  */
85
- export declare function getStatusMessage(nextStep: ScanStep, status: ScanStatus, detectedDocumentType: DocumentType, isBrightnessLow: boolean, isFrameBlurry: boolean, isGlareDetected: boolean, allElementsDetected: boolean, elementsOutsideScanArea: string[], t: (key: string, params?: Record<string, unknown>) => string): string;
85
+ export declare function getStatusMessage(nextStep: ScanStep, status: ScanStatus, detectedDocumentType: DocumentType, isBrightnessLow: boolean, isFrameBlurry: boolean, allElementsDetected: boolean, elementsOutsideScanArea: string[], t: (key: string, params?: Record<string, unknown>) => string): string;
86
86
  /**
87
87
  * Calculate angle from two points in degrees
88
88
  */
@@ -1 +1 @@
1
- {"version":3,"file":"IdentityDocumentCamera.utils.d.ts","sourceRoot":"","sources":["../../../../../src/Shared/Components/IdentityDocumentCamera.utils.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"IdentityDocumentCamera.utils.d.ts","sourceRoot":"","sources":["../../../../../src/Shared/Components/IdentityDocumentCamera.utils.ts"],"names":[],"mappings":"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"}
@@ -180,10 +180,8 @@ declare const _default: {
180
180
  'identityDocumentCamera.frontSide': string;
181
181
  'identityDocumentCamera.backSide': string;
182
182
  'identityDocumentCamera.hologramCheck': string;
183
- 'identityDocumentCamera.hologramProgress': string;
184
183
  'identityDocumentCamera.keepSteady': string;
185
184
  'identityDocumentCamera.avoidBlur': string;
186
- 'identityDocumentCamera.reduceGlare': string;
187
185
  'identityDocumentCamera.wrongSideFront': string;
188
186
  'identityDocumentCamera.wrongSideBack': string;
189
187
  'identityDocumentCamera.idCardDetected': string;
@@ -1 +1 @@
1
- {"version":3,"file":"en.d.ts","sourceRoot":"","sources":["../../../../../src/Translation/Resources/en.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,wBAsVE"}
1
+ {"version":3,"file":"en.d.ts","sourceRoot":"","sources":["../../../../../src/Translation/Resources/en.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,wBAkVE"}
@@ -180,10 +180,8 @@ declare const _default: {
180
180
  'identityDocumentCamera.frontSide': string;
181
181
  'identityDocumentCamera.backSide': string;
182
182
  'identityDocumentCamera.hologramCheck': string;
183
- 'identityDocumentCamera.hologramProgress': string;
184
183
  'identityDocumentCamera.keepSteady': string;
185
184
  'identityDocumentCamera.avoidBlur': string;
186
- 'identityDocumentCamera.reduceGlare': string;
187
185
  'identityDocumentCamera.wrongSideFront': string;
188
186
  'identityDocumentCamera.wrongSideBack': string;
189
187
  'identityDocumentCamera.idCardDetected': string;
@@ -1 +1 @@
1
- {"version":3,"file":"tr.d.ts","sourceRoot":"","sources":["../../../../../src/Translation/Resources/tr.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,wBAyVE"}
1
+ {"version":3,"file":"tr.d.ts","sourceRoot":"","sources":["../../../../../src/Translation/Resources/tr.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,wBAqVE"}
@@ -1,2 +1,2 @@
1
- export declare const SDK_VERSION = "1.513.1";
1
+ export declare const SDK_VERSION = "1.513.3";
2
2
  //# sourceMappingURL=version.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trustchex/react-native-sdk",
3
- "version": "1.513.1",
3
+ "version": "1.513.3",
4
4
  "description": "Trustchex mobile app react native SDK for android or ios devices",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -14,6 +14,15 @@ export const MIN_MLI_FACE_SIZE_PERCENT = 0.01; // MLI (Multi Layer Image): min 1
14
14
  export const REQUIRED_CONSISTENT_MRZ_READS = 2;
15
15
  export const REQUIRED_CONSISTENT_DOCTYPE_DETECTIONS = 3;
16
16
 
17
+ // Frames a check-digit-VALID MRZ consensus must stay UNCHANGED before it is
18
+ // accepted regardless of the per-cell vote-margin ("confident") gate. This
19
+ // unblocks a dense passport MRZ where one stubborn OCR-B look-alike keeps a
20
+ // single cell below the margin threshold forever — the scan would otherwise
21
+ // wedge on "hold steady". Set well above the aggregator's stabilityTarget (3)
22
+ // so the normal confident+stable path handles the common case and this only
23
+ // rescues the persistent-look-alike tail (~1.5s at ~8fps).
24
+ export const MRZ_STREAK_ACCEPT_OVERRIDE = 12;
25
+
17
26
  // After this long on a single scan step without completing it, surface the
18
27
  // "Having trouble?" prompt so the user isn't stranded on a live camera with no
19
28
  // timeout, retry, or help. Per-step (re-armed on each step transition).
@@ -65,11 +65,11 @@ import {
65
65
  HOLOGRAM_CAPTURE_INTERVAL,
66
66
  HOLOGRAM_MAX_FRAMES_WITHOUT_FACE,
67
67
  MIN_BRIGHTNESS_THRESHOLD,
68
- MAX_BRIGHTNESS_THRESHOLD,
69
68
  FACE_EDGE_MARGIN_PERCENT,
70
69
  MAX_CONSECUTIVE_QUALITY_FAILURES,
71
70
  REQUIRED_CONSISTENT_MRZ_READS,
72
71
  REQUIRED_CONSISTENT_DOCTYPE_DETECTIONS,
72
+ MRZ_STREAK_ACCEPT_OVERRIDE,
73
73
  HELP_PROMPT_DELAY_MS,
74
74
  SIGNATURE_TEXT_REGEX,
75
75
  MRZ_BLOCK_PATTERN,
@@ -141,11 +141,6 @@ const IdentityDocumentCamera = ({
141
141
  >(undefined);
142
142
  const [isBrightnessLow, setIsBrightnessLow] = useState(false);
143
143
  const [isFrameBlurry, setIsFrameBlurry] = useState(false);
144
- // Glare was being silently rejected (frames dropped with no user feedback), so
145
- // a user fighting reflections just saw the scan hang. Surface it so we can show
146
- // an actionable "move away from glare" hint. Set at each glare rejection;
147
- // cleared when a frame passes the brightness check (a fresh, glare-free frame).
148
- const [isGlareDetected, setIsGlareDetected] = useState(false);
149
144
  const [hasGuideShown, setHasGuideShown] = useState(false);
150
145
  // "Having trouble?" escape: if a scan step doesn't complete within
151
146
  // HELP_PROMPT_DELAY_MS the user is otherwise stuck on a live camera forever
@@ -176,7 +171,7 @@ const IdentityDocumentCamera = ({
176
171
  const consecutiveQualityFailures = useRef(0);
177
172
  const mrzDetectionCurrentRetryCount = useRef(0);
178
173
 
179
- // Hysteresis for the on-screen quality guidance. Brightness/blur/glare are
174
+ // Hysteresis for the on-screen quality guidance. Brightness/blur are
180
175
  // measured every frame; feeding the raw per-frame booleans straight into
181
176
  // state made the guidance strobe (a single blurry hand-shake frame flipped
182
177
  // "hold steady" on and off). These smooth each flag: it appears after a few
@@ -185,19 +180,11 @@ const IdentityDocumentCamera = ({
185
180
  // problems), clear slow (don't flicker off on one good frame).
186
181
  const brightnessLowFlag = useRef(new StableFlag(2, 5));
187
182
  const blurryFlag = useRef(new StableFlag(3, 6));
188
- const glareFlag = useRef(new StableFlag(2, 5));
189
- // Glare is asserted by accept-time helpers that run inside the (separately
190
- // scoped) handleFaceAndText callback, so it can't flow through a handler
191
- // local. This ref is cleared at the top of each frame and set true if any
192
- // glare check in that frame found glare; the handler reads it to feed
193
- // `glareFlag`. `hadGlareText` distinguishes "card present but glaring" from
194
- // an empty view so the warning only shows over an actual document.
195
- const rawGlareRef = useRef(false);
196
183
  // Whether a document was visible in the PREVIOUS frame (recognized text,
197
184
  // barcode, or face). The quality guidance is stabilized BEFORE the quality
198
185
  // gate (so dark/blurry frames — which the gate early-returns on — can still
199
186
  // raise their warnings), but document-presence is only known LATER in the
200
- // frame, so blur/glare are gated on the previous frame's value. One-frame lag
187
+ // frame, so blur is gated on the previous frame's value. One-frame lag
201
188
  // at ~8fps is imperceptible and the hysteresis smooths it.
202
189
  const documentPresentRef = useRef(false);
203
190
 
@@ -275,7 +262,6 @@ const IdentityDocumentCamera = ({
275
262
  frameH: 0,
276
263
  hadLowBrightness: false,
277
264
  hadBlurryFrames: false,
278
- hadGlare: false,
279
265
  });
280
266
  const [faceDetectionEnabled, setFaceDetectionEnabled] = useState(true);
281
267
  const faceImages = useRef<string[]>([]);
@@ -390,7 +376,6 @@ const IdentityDocumentCamera = ({
390
376
  frameH: 0,
391
377
  hadLowBrightness: false,
392
378
  hadBlurryFrames: false,
393
- hadGlare: false,
394
379
  };
395
380
  cachedBarcode.current = null;
396
381
  isCompletionCallbackInvoked.current = false;
@@ -439,7 +424,6 @@ const IdentityDocumentCamera = ({
439
424
  detectedDocumentType,
440
425
  isBrightnessLow,
441
426
  isFrameBlurry,
442
- isGlareDetected,
443
427
  allElementsDetected,
444
428
  elementsOutsideScanArea,
445
429
  t
@@ -462,7 +446,6 @@ const IdentityDocumentCamera = ({
462
446
  hasGuideShown,
463
447
  isBrightnessLow,
464
448
  isFrameBlurry,
465
- isGlareDetected,
466
449
  nextStep,
467
450
  status,
468
451
  detectedDocumentType,
@@ -630,33 +613,6 @@ const IdentityDocumentCamera = ({
630
613
  }
631
614
  };
632
615
 
633
- // Check if face image has glare (brightness exceeds threshold). Also surfaces
634
- // the result to `isGlareDetected` so the user gets an actionable hint instead
635
- // of a silently-stalled scan.
636
- const checkFaceGlare = async (faceImage: string): Promise<boolean> => {
637
- try {
638
- // Check entire face region for glare
639
- const hasGlare = await OpenCVModule.isRectangularRegionBright(
640
- faceImage,
641
- 0,
642
- 0,
643
- 100, // Full face width
644
- 100, // Full face height
645
- MAX_BRIGHTNESS_THRESHOLD
646
- );
647
- // OR-accumulate into the per-frame glare signal (reset at frame top); the
648
- // frame handler feeds it through the stabilized UI flag. Also record the
649
- // diagnostic.
650
- if (hasGlare) {
651
- rawGlareRef.current = true;
652
- diagStats.current.hadGlare = true;
653
- }
654
- return hasGlare;
655
- } catch (error) {
656
- return false; // Assume no glare on error
657
- }
658
- };
659
-
660
616
  // Check if face is fully visible (not cropped at edges)
661
617
  const isFaceFullyVisible = (
662
618
  face: Face,
@@ -673,35 +629,6 @@ const IdentityDocumentCamera = ({
673
629
  );
674
630
  };
675
631
 
676
- // Check if document image has glare
677
- const checkDocumentGlare = async (
678
- image: string,
679
- width: number,
680
- height: number
681
- ): Promise<boolean> => {
682
- try {
683
- // Check center 80% region for glare (document area)
684
- const hasGlare = await OpenCVModule.isRectangularRegionBright(
685
- image,
686
- Math.round(width * 0.1),
687
- Math.round(height * 0.1),
688
- Math.round(width * 0.8),
689
- Math.round(height * 0.8),
690
- MAX_BRIGHTNESS_THRESHOLD
691
- );
692
- // OR-accumulate into the per-frame glare signal (reset at frame top); the
693
- // frame handler feeds it through the stabilized UI flag. Also record the
694
- // diagnostic.
695
- if (hasGlare) {
696
- rawGlareRef.current = true;
697
- diagStats.current.hadGlare = true;
698
- }
699
- return hasGlare;
700
- } catch (error) {
701
- return false;
702
- }
703
- };
704
-
705
632
  const setNextStepAndVibrate = useCallback(
706
633
  (
707
634
  nextStepType:
@@ -780,12 +707,9 @@ const IdentityDocumentCamera = ({
780
707
  // carried over from the previous side).
781
708
  brightnessLowFlag.current.reset(false);
782
709
  blurryFlag.current.reset(false);
783
- glareFlag.current.reset(false);
784
- rawGlareRef.current = false;
785
710
  documentPresentRef.current = false;
786
711
  setIsBrightnessLow(false);
787
712
  setIsFrameBlurry(false);
788
- setIsGlareDetected(false);
789
713
 
790
714
  if (nextStepType !== 'COMPLETED') {
791
715
  setTimeout(() => {
@@ -1189,14 +1113,12 @@ const IdentityDocumentCamera = ({
1189
1113
  const faceFullyVisible =
1190
1114
  primaryFaces[0] &&
1191
1115
  isFaceFullyVisible(primaryFaces[0], frameWidth, frameHeight);
1192
- const hasGlare = await checkFaceGlare(croppedFaces[0]);
1193
1116
 
1194
- if (!faceFullyVisible || hasGlare) {
1195
- // Reject face with glare or partially visible
1117
+ if (!faceFullyVisible) {
1118
+ // Reject partially visible face
1196
1119
  if (isDebugEnabled()) {
1197
1120
  debugLog('IdentityDocumentCamera', '[FACE] Rejected', {
1198
1121
  fullyVisible: faceFullyVisible,
1199
- hasGlare,
1200
1122
  });
1201
1123
  }
1202
1124
  // Continue scanning without locking this face
@@ -1252,8 +1174,23 @@ const IdentityDocumentCamera = ({
1252
1174
  // vote must be unchanged across `stabilityTarget` consecutive contributing
1253
1175
  // frames, which is exactly the "only accept once stable" guarantee.
1254
1176
  // `addFrame` runs for every frame above, so the consensus is always current.
1177
+ // `consensus.stable` also requires a "confident" vote — every contested
1178
+ // cell's winner must lead by a margin. On a dense TD3 passport MRZ (88
1179
+ // cells) a single stubborn OCR-B look-alike (A↔4, S↔5) can keep one cell
1180
+ // at a 1-vote margin indefinitely, so `stable` never flips and the scan
1181
+ // wedges on "hold steady" — even though the check-digit-VALID consensus
1182
+ // has been UNCHANGED for many frames. Check digits are the real correctness
1183
+ // guarantee; a long unchanged, valid streak is conclusive. So also accept
1184
+ // once a valid consensus has held unchanged for MRZ_STREAK_ACCEPT_OVERRIDE
1185
+ // frames (well above `stabilityTarget`), independent of the margin gate.
1186
+ // Short streaks still require the full confident+stable gate, preserving
1187
+ // the early-misread protection.
1188
+ const mrzValidStreakSettled =
1189
+ parsedMRZData?.valid === true &&
1190
+ consensus.stableStreak >= MRZ_STREAK_ACCEPT_OVERRIDE;
1255
1191
  const mrzStableAndValid =
1256
- consensus.stable && parsedMRZData?.valid === true;
1192
+ (consensus.stable || mrzValidStreakSettled) &&
1193
+ parsedMRZData?.valid === true;
1257
1194
 
1258
1195
  // Surface the reliable-detection signal to the render so the UI can show a
1259
1196
  // green "GO" the moment (and only once) the consensus is stable + valid.
@@ -1337,23 +1274,6 @@ const IdentityDocumentCamera = ({
1337
1274
  return;
1338
1275
  }
1339
1276
 
1340
- // Check for glare on ID back before accepting
1341
- const hasGlare = await checkDocumentGlare(
1342
- image,
1343
- frameWidth,
1344
- frameHeight
1345
- );
1346
- if (hasGlare) {
1347
- if (isDebugEnabled()) {
1348
- debugLog(
1349
- 'IdentityDocumentCamera',
1350
- '[ID_BACK] Rejected - glare detected'
1351
- );
1352
- }
1353
- setStatus('SCANNING');
1354
- return;
1355
- }
1356
-
1357
1277
  if (isDebugEnabled()) {
1358
1278
  debugLog('IdentityDocumentCamera', '[ID_BACK] MRZ validated', {
1359
1279
  documentNumber: parsedMRZData?.fields?.documentNumber,
@@ -1530,29 +1450,17 @@ const IdentityDocumentCamera = ({
1530
1450
  setIsTorchOn(true);
1531
1451
  }
1532
1452
 
1533
- // Check hologram face for glare
1534
- const hasGlare = await checkFaceGlare(primaryFaceOnly);
1535
- if (!hasGlare) {
1536
- faceImages.current.push(primaryFaceOnly);
1537
- lastHologramCaptureTime.current = Date.now();
1538
- hologramImageCountRef.current = faceImages.current.length;
1453
+ faceImages.current.push(primaryFaceOnly);
1454
+ lastHologramCaptureTime.current = Date.now();
1455
+ hologramImageCountRef.current = faceImages.current.length;
1539
1456
 
1540
- if (isDebugEnabled()) {
1541
- debugLog(
1542
- 'IdentityDocumentCamera',
1543
- '[HOLOGRAM] Frame captured',
1544
- {
1545
- frameIndex: faceImages.current.length - 1,
1546
- totalFrames: faceImages.current.length,
1547
- }
1548
- );
1549
- }
1550
- } else if (isDebugEnabled()) {
1457
+ if (isDebugEnabled()) {
1551
1458
  debugLog(
1552
1459
  'IdentityDocumentCamera',
1553
- '[HOLOGRAM] Rejected glare frame',
1460
+ '[HOLOGRAM] Frame captured',
1554
1461
  {
1555
- collectedCount: faceImages.current.length,
1462
+ frameIndex: faceImages.current.length - 1,
1463
+ totalFrames: faceImages.current.length,
1556
1464
  }
1557
1465
  );
1558
1466
  }
@@ -1955,23 +1863,6 @@ const IdentityDocumentCamera = ({
1955
1863
  return;
1956
1864
  }
1957
1865
 
1958
- // Check for glare on passport before accepting
1959
- const hasGlare = await checkDocumentGlare(
1960
- image,
1961
- frameWidth,
1962
- frameHeight
1963
- );
1964
- if (hasGlare) {
1965
- if (isDebugEnabled()) {
1966
- debugLog(
1967
- 'IdentityDocumentCamera',
1968
- '[PASSPORT] Rejected - glare detected'
1969
- );
1970
- }
1971
- continueScanning(false);
1972
- return;
1973
- }
1974
-
1975
1866
  setDetectedDocumentType('PASSPORT');
1976
1867
  setStatus('SCANNED');
1977
1868
 
@@ -2005,23 +1896,6 @@ const IdentityDocumentCamera = ({
2005
1896
  return;
2006
1897
  }
2007
1898
 
2008
- // Check for glare on ID front before accepting
2009
- const hasGlare = await checkDocumentGlare(
2010
- image,
2011
- frameWidth,
2012
- frameHeight
2013
- );
2014
- if (hasGlare) {
2015
- if (isDebugEnabled()) {
2016
- debugLog(
2017
- 'IdentityDocumentCamera',
2018
- '[ID_FRONT] Rejected - glare detected'
2019
- );
2020
- }
2021
- continueScanning(false);
2022
- return;
2023
- }
2024
-
2025
1899
  setDetectedDocumentType('ID_FRONT');
2026
1900
  setStatus('SCANNED');
2027
1901
 
@@ -2136,18 +2010,6 @@ const IdentityDocumentCamera = ({
2136
2010
  const rawBrightnessLow = !isRegionBright;
2137
2011
  if (rawBrightnessLow) diagStats.current.hadLowBrightness = true;
2138
2012
 
2139
- // Per-frame glare is asserted by the accept-time helpers (checkDocumentGlare
2140
- // /checkFaceGlare), but those run inside handleFaceAndText which is fired
2141
- // (async, un-awaited) LATER in this handler — so this frame's glare result
2142
- // isn't known yet at the stabilization block below. We therefore feed the
2143
- // stabilizer the PREVIOUS frame's glare signal (captured here before the
2144
- // reset). At ~8fps the one-frame lag is imperceptible and the hysteresis
2145
- // smooths it anyway; without this the read would always see false and the
2146
- // glare warning could never fire. Reset after capturing so a now-clean
2147
- // view clears (no latch).
2148
- const prevFrameGlare = rawGlareRef.current;
2149
- rawGlareRef.current = false;
2150
-
2151
2013
  // Check blur only in center region (area of interest) to avoid false positives
2152
2014
  // from iOS depth-of-field background blur
2153
2015
  let isNotBlurry = true;
@@ -2197,8 +2059,8 @@ const IdentityDocumentCamera = ({
2197
2059
  // MUST run BEFORE the quality-gate early-return below: a dark or blurry
2198
2060
  // frame is exactly what makes hasAcceptableQuality false, so if this ran
2199
2061
  // after the gate the "too dark" / "hold steady" warnings could never
2200
- // raise (their triggering frames would have already returned). Blur/glare
2201
- // are gated on document presence; brightness shows regardless, since a
2062
+ // raise (their triggering frames would have already returned). Blur
2063
+ // is gated on document presence; brightness shows regardless, since a
2202
2064
  // dark room is worth flagging before the card is even framed.
2203
2065
  //
2204
2066
  // Presence combines the previous frame's confirmed value (documentPresent-
@@ -2207,7 +2069,7 @@ const IdentityDocumentCamera = ({
2207
2069
  // processing). Without the raw proxy, a document that is blurry/dark from
2208
2070
  // its very first framed moment would keep failing the quality gate and
2209
2071
  // early-returning before documentPresentRef is ever set true — suppressing
2210
- // its blur/glare hints for ~30 frames. The proxy lets the hint fire on the
2072
+ // its blur hints for ~30 frames. The proxy lets the hint fire on the
2211
2073
  // first framed frame.
2212
2074
  const rawDocumentPresent =
2213
2075
  (frame.faces?.length ?? 0) > 0 ||
@@ -2217,9 +2079,6 @@ const IdentityDocumentCamera = ({
2217
2079
  documentPresentRef.current || rawDocumentPresent;
2218
2080
  setIsBrightnessLow(brightnessLowFlag.current.push(rawBrightnessLow));
2219
2081
  setIsFrameBlurry(blurryFlag.current.push(documentWasPresent && isBlurry));
2220
- setIsGlareDetected(
2221
- glareFlag.current.push(documentWasPresent && prevFrameGlare)
2222
- );
2223
2082
  // -------------------------------------------------------------------
2224
2083
 
2225
2084
  // Store quality metrics in ref for access in handleFaceAndText callback
@@ -2284,6 +2143,32 @@ const IdentityDocumentCamera = ({
2284
2143
  })),
2285
2144
  };
2286
2145
 
2146
+ // Isolate the MRZ text (bottom-of-document blocks matching the MRZ
2147
+ // pattern) and feed ONLY that to the multi-frame consensus. CRITICAL:
2148
+ // this must run for EVERY build, not just debug — the identical logic
2149
+ // used to live only inside an `if (isDebugEnabled())` overlay block, so
2150
+ // in release `mrzOnlyText` was never set and the aggregator fell back to
2151
+ // the FULL OCR text (including the passport header "PASSPORT / TÜR /
2152
+ // REPUBLIC OF…"). That header noise kept the per-character consensus from
2153
+ // ever stabilizing, so a passport wedged on "hold steady" forever in
2154
+ // release while working in debug/example. Passport MRZ sits low on the
2155
+ // page; the `y > bottom-half` filter drops the header/photo-area text.
2156
+ if (textBlocks.length > 0) {
2157
+ const bottomHalf = frame.height * 0.5;
2158
+ const mrzBlocks = textBlocks.filter(
2159
+ (block) =>
2160
+ block.blockFrame &&
2161
+ block.blockFrame.y > bottomHalf &&
2162
+ MRZ_BLOCK_PATTERN.test(block.text)
2163
+ );
2164
+ if (mrzBlocks.length > 0) {
2165
+ scannedText.mrzOnlyText = [...mrzBlocks]
2166
+ .sort((a, b) => (a.blockFrame?.y || 0) - (b.blockFrame?.y || 0))
2167
+ .map((b) => b.text)
2168
+ .join('\n');
2169
+ }
2170
+ }
2171
+
2287
2172
  // Read barcodes directly from native ML Kit results
2288
2173
  let barcodes: Barcode[] = [];
2289
2174
  if (frame.barcodes) {
@@ -2800,18 +2685,6 @@ const IdentityDocumentCamera = ({
2800
2685
  </TextView>
2801
2686
  )}
2802
2687
 
2803
- {/* Hologram capture progress — 16 frames are collected while the user
2804
- tilts the document; show N/16 so "tilt slowly" feels responsive
2805
- instead of a silent wait. */}
2806
- {nextStep === 'SCAN_HOLOGRAM' && status !== 'SCANNED' && (
2807
- <TextView style={styles.hologramProgress}>
2808
- {t('identityDocumentCamera.hologramProgress', {
2809
- current: hologramImageCount,
2810
- total: HOLOGRAM_IMAGE_COUNT,
2811
- })}
2812
- </TextView>
2813
- )}
2814
-
2815
2688
  <AnimatedText
2816
2689
  style={[
2817
2690
  styles.topZoneText,
@@ -2827,7 +2700,7 @@ const IdentityDocumentCamera = ({
2827
2700
  opacity: errorFlashAnim,
2828
2701
  },
2829
2702
  // 3. Warning (yellow) - quality issues
2830
- (isBrightnessLow || isFrameBlurry || isGlareDetected) &&
2703
+ (isBrightnessLow || isFrameBlurry) &&
2831
2704
  styles.topZoneTextWarning,
2832
2705
  // 4. Scanning (green) - all elements detected AND inside scan area
2833
2706
  status === 'SCANNING' &&
@@ -2835,7 +2708,6 @@ const IdentityDocumentCamera = ({
2835
2708
  elementsOutsideScanArea.length === 0 &&
2836
2709
  !isBrightnessLow &&
2837
2710
  !isFrameBlurry &&
2838
- !isGlareDetected &&
2839
2711
  styles.topZoneTextScanning,
2840
2712
  // 5. Default (white) - aligning (not all detected OR elements outside scan area)
2841
2713
  ]}
@@ -2846,7 +2718,6 @@ const IdentityDocumentCamera = ({
2846
2718
  detectedDocumentType,
2847
2719
  isBrightnessLow,
2848
2720
  isFrameBlurry,
2849
- isGlareDetected,
2850
2721
  allElementsDetected,
2851
2722
  elementsOutsideScanArea,
2852
2723
  t
@@ -2899,7 +2770,9 @@ const IdentityDocumentCamera = ({
2899
2770
  />
2900
2771
  ) : null}
2901
2772
  </View>
2902
- {isDebugEnabled() && (
2773
+ {/* Debug overlay is DEV-only: hard-gated on __DEV__ so a release build
2774
+ can never render it, even if a consumer passes debug={true}. */}
2775
+ {__DEV__ && isDebugEnabled() && (
2903
2776
  <DebugOverlay
2904
2777
  nextStep={nextStep}
2905
2778
  status={status}
@@ -2922,7 +2795,7 @@ const IdentityDocumentCamera = ({
2922
2795
  elementsOutsideScanArea={elementsOutsideScanArea}
2923
2796
  />
2924
2797
  )}
2925
- {testMode && testModeData && (
2798
+ {__DEV__ && testMode && testModeData && (
2926
2799
  <TestModePanel mrzText={testModeData.mrzText} />
2927
2800
  )}
2928
2801
 
@@ -3070,13 +2943,6 @@ const styles = StyleSheet.create({
3070
2943
  fontWeight: '500',
3071
2944
  marginBottom: 4,
3072
2945
  },
3073
- hologramProgress: {
3074
- color: '#4CAF50',
3075
- fontSize: 14,
3076
- textAlign: 'center',
3077
- fontWeight: '600',
3078
- marginBottom: 4,
3079
- },
3080
2946
  topZoneText: {
3081
2947
  color: 'white',
3082
2948
  fontSize: 20,
@@ -172,7 +172,6 @@ export function getStatusMessage(
172
172
  detectedDocumentType: DocumentType,
173
173
  isBrightnessLow: boolean,
174
174
  isFrameBlurry: boolean,
175
- isGlareDetected: boolean,
176
175
  allElementsDetected: boolean,
177
176
  elementsOutsideScanArea: string[],
178
177
  t: (key: string, params?: Record<string, unknown>) => string
@@ -204,14 +203,6 @@ export function getStatusMessage(
204
203
  return t('identityDocumentCamera.lowBrightness');
205
204
  }
206
205
 
207
- // Glare is a primary failure mode (reflections off the laminate/photo). It was
208
- // previously detected but never communicated, so the scan just appeared to
209
- // hang. Tell the user how to fix it. Ranked above blur because a glare hotspot
210
- // both blocks acceptance and often reads as a "sharp" (non-blurry) frame.
211
- if (isGlareDetected) {
212
- return t('identityDocumentCamera.reduceGlare');
213
- }
214
-
215
206
  if (isFrameBlurry) {
216
207
  return t('identityDocumentCamera.avoidBlur');
217
208
  }
@@ -238,12 +238,8 @@ export default {
238
238
  'identityDocumentCamera.frontSide': 'Front',
239
239
  'identityDocumentCamera.backSide': 'Back',
240
240
  'identityDocumentCamera.hologramCheck': 'Hologram',
241
- 'identityDocumentCamera.hologramProgress':
242
- 'Capturing hologram… {{current}}/{{total}}',
243
241
  'identityDocumentCamera.keepSteady': 'Keep device steady',
244
242
  'identityDocumentCamera.avoidBlur': 'Keep device steady',
245
- 'identityDocumentCamera.reduceGlare':
246
- 'Glare detected. Tilt the document or move away from direct light.',
247
243
  'identityDocumentCamera.wrongSideFront':
248
244
  'Show the front side of your document',
249
245
  'identityDocumentCamera.wrongSideBack': 'Show the back side of your document',
@@ -239,12 +239,8 @@ export default {
239
239
  'identityDocumentCamera.frontSide': 'Ön',
240
240
  'identityDocumentCamera.backSide': 'Arka',
241
241
  'identityDocumentCamera.hologramCheck': 'Hologram',
242
- 'identityDocumentCamera.hologramProgress':
243
- 'Hologram yakalanıyor… {{current}}/{{total}}',
244
242
  'identityDocumentCamera.keepSteady': 'Cihazı sabit tutun',
245
243
  'identityDocumentCamera.avoidBlur': 'Cihazı sabit tutun',
246
- 'identityDocumentCamera.reduceGlare':
247
- 'Parlama algılandı. Belgeyi hafifçe eğin veya doğrudan ışıktan uzaklaşın.',
248
244
  'identityDocumentCamera.wrongSideFront': 'Belgenizin ön yüzünü gösterin',
249
245
  'identityDocumentCamera.wrongSideBack': 'Belgenizin arka yüzünü gösterin',
250
246
  'identityDocumentCamera.idCardDetected': 'Kimlik kartı algılandı',
package/src/version.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  // This file is auto-generated. Do not edit manually.
2
2
  // Version is synced from package.json during build.
3
- export const SDK_VERSION = '1.513.1';
3
+ export const SDK_VERSION = '1.513.3';