@smileid/web-components 11.4.5 → 11.6.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 (132) hide show
  1. package/dist/esm/DocumentCaptureScreens-DjSTdVP-.js +5398 -0
  2. package/dist/esm/DocumentCaptureScreens-DjSTdVP-.js.map +1 -0
  3. package/dist/esm/{Navigation-Bb7MPLE8.js → Navigation-6DH3vF4-.js} +28 -22
  4. package/dist/esm/Navigation-6DH3vF4-.js.map +1 -0
  5. package/dist/esm/{PoweredBySmileId-CxbaihMu.js → PoweredBySmileId-DoKwoPUd.js} +424 -6
  6. package/dist/esm/PoweredBySmileId-DoKwoPUd.js.map +1 -0
  7. package/dist/esm/SelfieCaptureScreens-CtX-4Tco.js +11470 -0
  8. package/dist/esm/SelfieCaptureScreens-CtX-4Tco.js.map +1 -0
  9. package/dist/esm/combobox.js +1 -1
  10. package/dist/esm/document.js +1 -1
  11. package/dist/esm/end-user-consent.js +713 -2
  12. package/dist/esm/end-user-consent.js.map +1 -1
  13. package/dist/esm/index-BqyuTk9f.js +1366 -0
  14. package/dist/esm/{index-C4RTMbgw.js.map → index-BqyuTk9f.js.map} +1 -1
  15. package/dist/esm/localisation.js +1 -1
  16. package/dist/esm/main.js +14 -14
  17. package/dist/esm/navigation.js +1 -1
  18. package/dist/esm/package-CjZI-cNQ.js +2540 -0
  19. package/dist/esm/package-CjZI-cNQ.js.map +1 -0
  20. package/dist/esm/selfie.js +1 -1
  21. package/dist/esm/smart-camera-web.js +81 -37
  22. package/dist/esm/smart-camera-web.js.map +1 -1
  23. package/dist/esm/totp-consent.js +731 -2
  24. package/dist/esm/totp-consent.js.map +1 -1
  25. package/dist/esm/validate.js +31 -0
  26. package/dist/esm/validate.js.map +1 -0
  27. package/dist/smart-camera-web.js +1513 -383
  28. package/dist/smart-camera-web.js.map +1 -1
  29. package/dist/types/main.d.ts +18 -1
  30. package/dist/types/validate.d.ts +21 -0
  31. package/lib/components/document/src/DocumentCaptureScreens.js +97 -18
  32. package/lib/components/document/src/assets/lottie.d.ts +12 -0
  33. package/lib/components/document/src/assets/svg-inline.d.ts +8 -0
  34. package/lib/components/document/src/document-auto-capture/DocumentAutoCapture.stories.js +75 -0
  35. package/lib/components/document/src/document-auto-capture/DocumentAutoCapture.tsx +1458 -0
  36. package/lib/components/document/src/document-auto-capture/README.md +73 -0
  37. package/lib/components/document/src/document-auto-capture/assets/Greenbook_Shimmer.svg +42 -0
  38. package/lib/components/document/src/document-auto-capture/assets/ID_Back_Shimmer.svg +8 -0
  39. package/lib/components/document/src/document-auto-capture/assets/ID_Front_Shimmer.svg +20 -0
  40. package/lib/components/document/src/document-auto-capture/assets/Passport-Shimmer.svg +143 -0
  41. package/lib/components/document/src/document-auto-capture/assets/shimmers.ts +21 -0
  42. package/lib/components/document/src/document-auto-capture/assets/svg-raw.d.ts +4 -0
  43. package/lib/components/document/src/document-auto-capture/components/CaptureButton.tsx +122 -0
  44. package/lib/components/document/src/document-auto-capture/components/Overlay.tsx +167 -0
  45. package/lib/components/document/src/document-auto-capture/components/TuningPanel.tsx +856 -0
  46. package/lib/components/document/src/document-auto-capture/constants/captureLayout.ts +58 -0
  47. package/lib/components/document/src/document-auto-capture/detection/cvErrorRecovery.ts +40 -0
  48. package/lib/components/document/src/document-auto-capture/detection/documentAspect.ts +20 -0
  49. package/lib/components/document/src/document-auto-capture/detection/qualityScoring.ts +35 -0
  50. package/lib/components/document/src/document-auto-capture/detection/seamRejection.ts +209 -0
  51. package/lib/components/document/src/document-auto-capture/detection/synthesisTiming.ts +10 -0
  52. package/lib/components/document/src/document-auto-capture/hooks/useCamera.ts +117 -0
  53. package/lib/components/document/src/document-auto-capture/hooks/useCardDetection.ts +3059 -0
  54. package/lib/components/document/src/document-auto-capture/index.ts +4 -0
  55. package/lib/components/document/src/document-auto-capture/theme.ts +40 -0
  56. package/lib/components/document/src/document-auto-capture/utils/debug.ts +25 -0
  57. package/lib/components/document/src/document-auto-capture/utils/opencvLoader.ts +86 -0
  58. package/lib/components/document/src/document-capture-instructions/DocumentCaptureInstructions.tsx +327 -244
  59. package/lib/components/document/src/document-capture-review/DocumentCaptureReview.js +153 -189
  60. package/lib/components/document/src/document-capture-submission/DocumentCaptureSubmission.tsx +432 -0
  61. package/lib/components/document/src/document-capture-submission/index.js +3 -0
  62. package/lib/components/navigation/src/Navigation.js +27 -8
  63. package/lib/components/selfie/README.md +13 -0
  64. package/lib/components/selfie/src/SelfieCaptureScreens.js +56 -8
  65. package/lib/components/selfie/src/enhanced-smartselfie-capture/EnhancedSmartSelfieCapture.tsx +684 -0
  66. package/lib/components/selfie/src/enhanced-smartselfie-capture/EnhancedSmartSelfieConsent.tsx +71 -0
  67. package/lib/components/selfie/src/enhanced-smartselfie-capture/EnhancedSmartSelfieSubmission.tsx +181 -0
  68. package/lib/components/selfie/src/enhanced-smartselfie-capture/OvalProgress.tsx +87 -0
  69. package/lib/components/selfie/src/enhanced-smartselfie-capture/assets/Icon.svg +8 -0
  70. package/lib/components/selfie/src/enhanced-smartselfie-capture/assets/accessories.svg +77 -0
  71. package/lib/components/selfie/src/enhanced-smartselfie-capture/assets/active_liveness_animation.lottie +0 -0
  72. package/lib/components/selfie/src/enhanced-smartselfie-capture/assets/device.svg +12 -0
  73. package/lib/components/selfie/src/enhanced-smartselfie-capture/assets/device_orientation.lottie +0 -0
  74. package/lib/components/selfie/src/enhanced-smartselfie-capture/assets/good.svg +52 -0
  75. package/lib/components/selfie/src/enhanced-smartselfie-capture/assets/id-card.svg +9 -0
  76. package/lib/components/selfie/src/enhanced-smartselfie-capture/assets/illustrations.tsx +852 -0
  77. package/lib/components/selfie/src/enhanced-smartselfie-capture/assets/instructions-img.svg +3 -0
  78. package/lib/components/selfie/src/enhanced-smartselfie-capture/assets/multiple-faces.svg +69 -0
  79. package/lib/components/selfie/src/enhanced-smartselfie-capture/assets/person.svg +6 -0
  80. package/lib/components/selfie/src/enhanced-smartselfie-capture/assets/phone.svg +8 -0
  81. package/lib/components/selfie/src/enhanced-smartselfie-capture/assets/poor-lighting.svg +53 -0
  82. package/lib/components/selfie/src/enhanced-smartselfie-capture/assets/too_dark_animation.lottie +0 -0
  83. package/lib/components/selfie/src/enhanced-smartselfie-capture/components/ActiveLivenessOverlay.tsx +226 -0
  84. package/lib/components/selfie/src/enhanced-smartselfie-capture/components/AlertDisplay.tsx +38 -0
  85. package/lib/components/selfie/src/enhanced-smartselfie-capture/components/BackNavigation.tsx +45 -0
  86. package/lib/components/selfie/src/enhanced-smartselfie-capture/components/CameraPreview.tsx +96 -0
  87. package/lib/components/selfie/src/enhanced-smartselfie-capture/components/CaptureControls.tsx +97 -0
  88. package/lib/components/selfie/src/enhanced-smartselfie-capture/components/CaptureGuidelines.tsx +374 -0
  89. package/lib/components/selfie/src/enhanced-smartselfie-capture/components/ConsentView.tsx +460 -0
  90. package/lib/components/selfie/src/enhanced-smartselfie-capture/components/SubmissionView.tsx +426 -0
  91. package/lib/components/selfie/src/enhanced-smartselfie-capture/components/index.ts +3 -0
  92. package/lib/components/selfie/src/enhanced-smartselfie-capture/constants.ts +23 -0
  93. package/lib/components/selfie/src/enhanced-smartselfie-capture/hooks/index.ts +2 -0
  94. package/lib/components/selfie/src/enhanced-smartselfie-capture/hooks/useCamera.ts +238 -0
  95. package/lib/components/selfie/src/enhanced-smartselfie-capture/hooks/useFaceCapture.ts +1075 -0
  96. package/lib/components/selfie/src/enhanced-smartselfie-capture/index.ts +1 -0
  97. package/lib/components/selfie/src/enhanced-smartselfie-capture/utils/alertMessages.ts +20 -0
  98. package/lib/components/selfie/src/enhanced-smartselfie-capture/utils/canvas.ts +108 -0
  99. package/lib/components/selfie/src/enhanced-smartselfie-capture/utils/faceDetection.ts +545 -0
  100. package/lib/components/selfie/src/enhanced-smartselfie-capture/utils/imageCapture.ts +66 -0
  101. package/lib/components/selfie/src/enhanced-smartselfie-capture/utils/imageQuality.ts +151 -0
  102. package/lib/components/selfie/src/enhanced-smartselfie-capture/utils/index.ts +5 -0
  103. package/lib/components/selfie/src/enhanced-smartselfie-capture/utils/mediapipeManager.ts +215 -0
  104. package/lib/components/selfie/src/selfie-capture-wrapper/SelfieCaptureWrapper.tsx +24 -1
  105. package/lib/components/selfie/src/smartselfie-capture/SmartSelfieCapture.tsx +2 -2
  106. package/lib/components/selfie/src/smartselfie-capture/hooks/useFaceCapture.ts +15 -7
  107. package/lib/components/selfie/src/smartselfie-capture/utils/canvas.ts +4 -6
  108. package/lib/components/signature-pad/package.json +1 -1
  109. package/lib/components/smart-camera-web/src/README.md +11 -0
  110. package/lib/components/smart-camera-web/src/SmartCameraWeb.js +89 -8
  111. package/lib/components/totp-consent/src/TotpConsent.js +1 -1
  112. package/lib/domain/localisation/index.js +2 -2
  113. package/package.json +9 -5
  114. package/dist/esm/DocumentCaptureScreens-D2G0NOQr.js +0 -4147
  115. package/dist/esm/DocumentCaptureScreens-D2G0NOQr.js.map +0 -1
  116. package/dist/esm/EndUserConsent-uHfA3txP.js +0 -717
  117. package/dist/esm/EndUserConsent-uHfA3txP.js.map +0 -1
  118. package/dist/esm/Navigation-Bb7MPLE8.js.map +0 -1
  119. package/dist/esm/PoweredBySmileId-CxbaihMu.js.map +0 -1
  120. package/dist/esm/SelfieCaptureScreens-Dr7VzON7.js +0 -7651
  121. package/dist/esm/SelfieCaptureScreens-Dr7VzON7.js.map +0 -1
  122. package/dist/esm/TotpConsent-Depzg0ti.js +0 -734
  123. package/dist/esm/TotpConsent-Depzg0ti.js.map +0 -1
  124. package/dist/esm/index-C4RTMbgw.js +0 -1360
  125. package/dist/esm/package-D6YrpMcO.js +0 -565
  126. package/dist/esm/package-D6YrpMcO.js.map +0 -1
  127. package/dist/esm/styles-BTEClL7R.js +0 -419
  128. package/dist/esm/styles-BTEClL7R.js.map +0 -1
  129. /package/lib/components/document/src/assets/lottie/{taking photo of green book passport.lottie → greenbook.lottie} +0 -0
  130. /package/lib/components/document/src/assets/lottie/{taking photo of ID FLIP 2D.lottie → id-card-flip.lottie} +0 -0
  131. /package/lib/components/document/src/assets/lottie/{taking photo of ID.lottie → id-card.lottie} +0 -0
  132. /package/lib/components/document/src/assets/lottie/{taking photo of passport 2.lottie → passport.lottie} +0 -0
@@ -0,0 +1,3 @@
1
+ <svg width="245" height="328" viewBox="0 0 245 328" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M122.5 1C156.164 1 186.562 19.3464 208.509 48.8516C230.456 78.3568 244 119.071 244 164C244 208.929 230.456 249.643 208.509 279.148C186.562 308.654 156.164 327 122.5 327C88.836 327 58.438 308.654 36.4912 279.148C14.5444 249.643 1 208.929 1 164C1 119.071 14.5444 78.3568 36.4912 48.8516C58.438 19.3464 88.836 1 122.5 1Z" stroke="#C4C4C4" stroke-width="2"/>
3
+ </svg>
@@ -0,0 +1,69 @@
1
+ <svg width="47" height="79" viewBox="0 0 47 79" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <g filter="url(#filter0_dd_6_9295)">
3
+ <g clip-path="url(#clip0_6_9295)">
4
+ <path d="M7 75C4.79086 75 3 73.2091 3 71L3 6C3 3.79086 4.79086 2 7 2H40C42.2091 2 44 3.79086 44 6V71C44 73.2091 42.2091 75 40 75H7Z" fill="white" fill-opacity="0.01" shape-rendering="crispEdges"/>
5
+ <g clip-path="url(#clip1_6_9295)">
6
+ <rect width="73" height="41" transform="matrix(0 -1 1 0 3 75)" fill="white"/>
7
+ <rect x="11" y="63" width="25" height="6" rx="3" fill="#848282"/>
8
+ <path d="M23.5338 13.3046C32.3117 13.3046 39.4387 22.9976 39.4387 34.9359C39.4387 46.8743 32.3117 56.5673 23.5338 56.5673C14.7559 56.5673 7.62891 46.8743 7.62891 34.9359C7.62891 22.9976 14.7559 13.3046 23.5338 13.3046Z" stroke="#EF4343" stroke-width="0.5"/>
9
+ <path d="M26.1612 35.0191C25.5397 34.8303 24.6695 35.1915 24.7811 36.3849C24.8745 37.3846 25.313 38.2932 26.3866 38.0268C26.9201 37.8944 26.79 37.3091 26.7396 36.5572C26.6892 35.8052 26.7826 35.208 26.1612 35.0191Z" fill="#2D2A29"/>
10
+ <path d="M26.1612 35.0191C25.5397 34.8303 24.6695 35.1915 24.7811 36.3849C24.8745 37.3846 25.313 38.2932 26.3866 38.0268" fill="white"/>
11
+ <path d="M26.1612 35.0191C25.5397 34.8303 24.6695 35.1915 24.7811 36.3849C24.8745 37.3846 25.313 38.2932 26.3866 38.0268" stroke="#1F1C1C" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"/>
12
+ <path d="M35.3777 34.987C35.9979 34.7938 36.8706 35.1489 36.7673 36.3431C36.6809 37.3434 36.2487 38.2551 35.1734 37.9962C34.6389 37.8675 34.765 37.2813 34.8101 36.529C34.8552 35.7768 34.7576 35.1802 35.3777 34.987Z" fill="#2D2A29"/>
13
+ <path d="M35.3777 34.987C35.9979 34.7938 36.8706 35.1489 36.7673 36.3431C36.6809 37.3434 36.2487 38.2551 35.1734 37.9962" fill="white"/>
14
+ <path d="M35.3777 34.987C35.9979 34.7938 36.8706 35.1489 36.7673 36.3431C36.6809 37.3434 36.2487 38.2551 35.1734 37.9962" stroke="#1F1C1C" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"/>
15
+ <path d="M30.7029 29.2139C25.2108 29.209 26.0415 34.507 26.1182 35.9632C26.1949 37.4194 26.9017 42.2239 30.6294 42.3465C34.5695 42.476 35.3164 37.5547 35.4062 35.9308C35.4961 34.3068 36.1469 29.2187 30.7029 29.2139Z" fill="white"/>
16
+ <path d="M30.7029 29.2139C25.2108 29.209 26.0415 34.507 26.1182 35.9632C26.1949 37.4194 26.9017 42.2239 30.6294 42.3465C34.5695 42.476 35.3164 37.5547 35.4062 35.9308C35.4961 34.3068 36.1469 29.2187 30.7029 29.2139Z" stroke="#1F1C1C" stroke-width="0.5"/>
17
+ <path d="M28.7001 35.1184C28.9472 35.1176 29.1487 35.3817 29.1499 35.7078C29.151 36.034 28.9513 36.2995 28.7043 36.3003C28.4572 36.3012 28.2556 36.0371 28.2545 35.7109C28.2534 35.3848 28.4531 35.1193 28.7001 35.1184Z" fill="#1F1C1C"/>
18
+ <path d="M32.8784 35.1038C33.1254 35.103 33.327 35.3671 33.3281 35.6932C33.3292 36.0194 33.1296 36.2849 32.8825 36.2857C32.6354 36.2866 32.4339 36.0225 32.4327 35.6964C32.4316 35.3702 32.6313 35.1047 32.8784 35.1038Z" fill="#1F1C1C"/>
19
+ <path d="M27.9345 34.769C28.1722 34.4339 28.8224 34.3181 29.0972 34.3649" stroke="#1F1C1C" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"/>
20
+ <path d="M33.5932 34.7492C33.3532 34.4158 32.7022 34.3046 32.4278 34.3533" stroke="#1F1C1C" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"/>
21
+ <path d="M31.0595 35.6713C31.0595 35.6713 31.1124 37.1515 31.0297 37.3786C30.9469 37.6057 30.6248 37.6785 30.5651 37.6787" stroke="#1F1C1C" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"/>
22
+ <path d="M30.3555 39.7499C30.528 39.8119 30.9882 39.8119 31.1741 39.7499" stroke="#1F1C1C" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"/>
23
+ <path d="M29.5518 39.0201C29.5518 39.0201 29.8063 39.1345 30.7227 39.1304C31.7227 39.126 32.066 39.0233 32.066 39.0233C32.066 39.0233 31.5524 39.0994 30.7227 39.104C29.997 39.1078 29.5518 39.0201 29.5518 39.0201Z" fill="white"/>
24
+ <path d="M29.5518 39.0201C29.5518 39.0201 29.8063 39.1345 30.7227 39.1304C31.7227 39.126 32.066 39.0233 32.066 39.0233C32.066 39.0233 31.5524 39.0994 30.7227 39.104C29.997 39.1078 29.5518 39.0201 29.5518 39.0201Z" stroke="#1F1C1C" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"/>
25
+ <path d="M13.2099 37.2291C12.2247 36.9297 10.8452 37.5023 11.0222 39.3941C11.1703 40.9788 11.8654 42.4192 13.5672 41.9969C14.4129 41.787 14.2067 40.8592 14.1268 39.6672C14.0469 38.4752 14.195 37.5284 13.2099 37.2291Z" fill="#2D2A29"/>
26
+ <path d="M13.2099 37.2291C12.2247 36.9297 10.8452 37.5023 11.0222 39.3941C11.1703 40.9788 11.8654 42.4192 13.5672 41.9969" fill="white"/>
27
+ <path d="M13.2099 37.2291C12.2247 36.9297 10.8452 37.5023 11.0222 39.3941C11.1703 40.9788 11.8654 42.4192 13.5672 41.9969" stroke="#1F1C1C" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"/>
28
+ <path d="M27.8199 37.1781C28.803 36.8719 30.1865 37.4348 30.0227 39.3278C29.8857 40.9135 29.2006 42.3587 27.496 41.9483C26.6488 41.7443 26.8485 40.8151 26.9201 39.6225C26.9916 38.43 26.8369 37.4843 27.8199 37.1781Z" fill="#2D2A29"/>
29
+ <path d="M27.8199 37.1781C28.803 36.8719 30.1865 37.4348 30.0227 39.3278C29.8857 40.9135 29.2006 42.3587 27.496 41.9483" fill="white"/>
30
+ <path d="M27.8199 37.1781C28.803 36.8719 30.1865 37.4348 30.0227 39.3278C29.8857 40.9135 29.2006 42.3587 27.496 41.9483" stroke="#1F1C1C" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"/>
31
+ <path d="M20.4093 28.0265C11.7033 28.0187 13.0201 36.4171 13.1417 38.7255C13.2633 41.0339 14.3837 48.65 20.2927 48.8442C26.5387 49.0495 27.7226 41.2484 27.865 38.6741C28.0075 36.0998 29.0391 28.0342 20.4093 28.0265Z" fill="white"/>
32
+ <path d="M20.4093 28.0265C11.7033 28.0187 13.0201 36.4171 13.1417 38.7255C13.2633 41.0339 14.3837 48.65 20.2927 48.8442C26.5387 49.0495 27.7226 41.2484 27.865 38.6741C28.0075 36.0998 29.0391 28.0342 20.4093 28.0265Z" stroke="#1F1C1C" stroke-width="0.5"/>
33
+ <path d="M17.2341 37.3864C17.6258 37.3851 17.9452 37.8037 17.947 38.3207C17.9488 38.8377 17.6323 39.2586 17.2406 39.26C16.849 39.2613 16.5295 38.8427 16.5277 38.3257C16.5259 37.8087 16.8424 37.3878 17.2341 37.3864Z" fill="#1F1C1C"/>
34
+ <path d="M23.8576 37.3633C24.2493 37.3619 24.5688 37.7806 24.5706 38.2976C24.5724 38.8146 24.2558 39.2355 23.8642 39.2368C23.4725 39.2382 23.153 38.8196 23.1512 38.3026C23.1494 37.7855 23.466 37.3647 23.8576 37.3633Z" fill="#1F1C1C"/>
35
+ <path d="M16.0213 36.8325C16.3981 36.3013 17.4288 36.1178 17.8643 36.192" stroke="#1F1C1C" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"/>
36
+ <path d="M24.9915 36.8012C24.611 36.2726 23.5791 36.0964 23.1441 36.1736" stroke="#1F1C1C" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"/>
37
+ <path d="M20.975 38.2628C20.975 38.2628 21.0589 40.6092 20.9277 40.9692C20.7965 41.3292 20.2859 41.4446 20.1913 41.4449" stroke="#1F1C1C" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"/>
38
+ <path d="M19.8584 44.7282C20.132 44.8265 20.8614 44.8265 21.156 44.7282" stroke="#1F1C1C" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"/>
39
+ <path d="M18.5845 43.5715C18.5845 43.5715 18.988 43.7528 20.4407 43.7463C22.0258 43.7393 22.57 43.5764 22.57 43.5764C22.57 43.5764 21.7558 43.6972 20.4407 43.7043C19.2903 43.7104 18.5845 43.5715 18.5845 43.5715Z" fill="white"/>
40
+ <path d="M18.5845 43.5715C18.5845 43.5715 18.988 43.7528 20.4407 43.7463C22.0258 43.7393 22.57 43.5764 22.57 43.5764C22.57 43.5764 21.7558 43.6972 20.4407 43.7043C19.2903 43.7104 18.5845 43.5715 18.5845 43.5715Z" stroke="#1F1C1C" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"/>
41
+ </g>
42
+ </g>
43
+ <path d="M3.5 71L3.5 6C3.5 4.06701 5.067 2.5 7 2.5H40C41.933 2.5 43.5 4.06701 43.5 6V71C43.5 72.933 41.933 74.5 40 74.5H7C5.067 74.5 3.5 72.933 3.5 71Z" stroke="#E2E8F0" shape-rendering="crispEdges"/>
44
+ </g>
45
+ <defs>
46
+ <filter id="filter0_dd_6_9295" x="0" y="0" width="47" height="79" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
47
+ <feFlood flood-opacity="0" result="BackgroundImageFix"/>
48
+ <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
49
+ <feOffset dy="1"/>
50
+ <feGaussianBlur stdDeviation="1"/>
51
+ <feComposite in2="hardAlpha" operator="out"/>
52
+ <feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.06 0"/>
53
+ <feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_6_9295"/>
54
+ <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
55
+ <feOffset dy="1"/>
56
+ <feGaussianBlur stdDeviation="1.5"/>
57
+ <feComposite in2="hardAlpha" operator="out"/>
58
+ <feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0"/>
59
+ <feBlend mode="normal" in2="effect1_dropShadow_6_9295" result="effect2_dropShadow_6_9295"/>
60
+ <feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow_6_9295" result="shape"/>
61
+ </filter>
62
+ <clipPath id="clip0_6_9295">
63
+ <path d="M7 75C4.79086 75 3 73.2091 3 71L3 6C3 3.79086 4.79086 2 7 2H40C42.2091 2 44 3.79086 44 6V71C44 73.2091 42.2091 75 40 75H7Z" fill="white"/>
64
+ </clipPath>
65
+ <clipPath id="clip1_6_9295">
66
+ <rect width="73" height="41" fill="white" transform="matrix(0 -1 1 0 3 75)"/>
67
+ </clipPath>
68
+ </defs>
69
+ </svg>
@@ -0,0 +1,6 @@
1
+ <svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M17.5259 12.481C15.9716 11.7663 13.9456 11.7261 11.82 12.5305C7.95534 13.9929 5.10325 17.7391 5.02148 21.2369L6.00759 21.685C6.08937 18.1872 8.94146 14.441 12.8062 12.9785C14.9317 12.1742 16.9577 12.2144 18.512 12.9291L17.5259 12.481Z" fill="#5E646E"/>
3
+ <path d="M7.10258 21.3367L19.9414 16.368C20.3486 16.2104 20.7391 16.0061 21.1011 15.7685C20.1269 12.7384 16.5834 11.4886 12.7121 12.9869C8.8407 14.4851 5.98575 18.2111 5.90651 21.6489C6.29418 21.5964 6.69541 21.4943 7.10581 21.3355" fill="#2D2B2A"/>
4
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M10.1918 10.6578C9.35637 10.2783 8.77166 9.53851 8.63569 8.52842C8.36375 6.50823 9.98974 4.17197 12.2676 3.31002C13.4065 2.87904 14.4927 2.89827 15.3281 3.27776L16.8982 3.99116C16.0627 3.61167 14.9765 3.59244 13.8376 4.02342C11.5598 4.88537 9.93376 7.22163 10.2057 9.24182C10.3417 10.2519 10.9264 10.9917 11.7618 11.3712L10.1918 10.6578Z" fill="#2D2B2A"/>
5
+ <path d="M13.5061 3.72185C15.9636 2.77078 18.197 3.74443 18.4949 5.89664C18.7927 8.04884 17.0421 10.5644 14.5846 11.5154C12.1271 12.4665 9.89368 11.4928 9.59587 9.34063C9.29805 7.18842 11.0487 4.67292 13.5061 3.72185Z" fill="#F9F0E7"/>
6
+ </svg>
@@ -0,0 +1,8 @@
1
+ <svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M11.4194 10.3225L10.5181 11.076L9.82031 11.583L10.7216 10.8295L11.4194 10.3225Z" fill="#2D2B2A"/>
3
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M10.5181 11.0759C10.267 11.2857 10.2227 11.717 10.3631 12.1575C10.9696 14.0608 12.3778 16.3257 13.7094 17.5344C13.9837 17.7837 14.2892 17.8556 14.5129 17.6934L13.8151 18.2004C13.5914 18.3625 13.2859 18.2907 13.0116 18.0414C11.68 16.8327 10.2718 14.5678 9.66527 12.6645C9.52489 12.2239 9.56924 11.7927 9.82025 11.5829L10.5181 11.0759Z" fill="#2D2B2A"/>
4
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M14.5127 17.6931C14.5404 17.6731 14.5668 17.6494 14.5917 17.6222L15.408 16.732C15.4331 16.7047 15.4598 16.6807 15.4878 16.6602L14.79 17.1672C14.762 17.1877 14.7353 17.2116 14.7102 17.239L13.8939 18.1291C13.869 18.1564 13.8426 18.18 13.8149 18.2001L14.5127 17.6931Z" fill="#2D2B2A"/>
5
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M15.4878 16.6604C15.6931 16.51 15.9684 16.5435 16.223 16.7578L18.6869 18.836C19.0169 19.1147 19.2004 19.6137 19.1281 20.0374L18.6364 22.9129C18.6015 23.117 18.5171 23.2708 18.3935 23.3618L17.6957 23.8688C17.8193 23.7778 17.9037 23.624 17.9386 23.4199L18.4303 20.5444C18.5026 20.1207 18.3191 19.6216 17.9891 19.343L15.5252 17.2648C15.2706 17.0505 14.9953 17.017 14.79 17.1674L15.4878 16.6604Z" fill="#2D2B2A"/>
6
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M18.3943 23.3616C18.3307 23.4086 18.2566 23.4388 18.1735 23.4508C18.1328 23.4562 18.0902 23.4588 18.0447 23.458C12.7802 23.3492 5.59157 11.7986 6.8411 5.45554C6.85204 5.40145 6.8648 5.35028 6.87938 5.30282C6.92584 5.15547 7.0004 5.04379 7.09708 4.97263L6.39928 5.47961C6.3026 5.55077 6.22804 5.66245 6.18158 5.80981C6.167 5.85726 6.15423 5.90843 6.14329 5.96253C4.89377 12.3056 12.0824 23.8562 17.3469 23.965C17.3924 23.9657 17.435 23.9632 17.4757 23.9577C17.5588 23.9458 17.6329 23.9155 17.6965 23.8686L18.3943 23.3616Z" fill="#F9F0E7"/>
7
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M6.84053 5.456C6.85147 5.4019 6.86423 5.35074 6.87882 5.30328C6.96816 5.0199 7.16143 4.86846 7.41608 4.88341L9.78396 5.02251C10.1328 5.04315 10.4756 5.3898 10.6093 5.85628L11.606 9.33848C11.7233 9.74744 11.651 10.129 11.4195 10.3226L10.5181 11.076C10.2671 11.2858 10.2228 11.717 10.3632 12.1577C10.9697 14.0609 12.3779 16.3258 13.7095 17.5345C14.0177 17.8146 14.3653 17.8707 14.592 17.6226L15.4082 16.7325C15.6179 16.5045 15.9339 16.5145 16.2232 16.7581L18.6871 18.8363C19.0171 19.115 19.2007 19.614 19.1284 20.0377L18.6367 22.9132C18.5838 23.2224 18.4173 23.4161 18.1729 23.4512C18.1322 23.4567 18.0897 23.4592 18.0441 23.4584C12.7796 23.3497 5.59101 11.7991 6.84053 5.456Z" fill="#2D2B2A"/>
8
+ </svg>
@@ -0,0 +1,53 @@
1
+ <svg width="47" height="79" viewBox="0 0 47 79" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <g filter="url(#filter0_dd_6_9517)">
3
+ <g clip-path="url(#clip0_6_9517)">
4
+ <path d="M7 75C4.79086 75 3 73.2091 3 71L3 6C3 3.79086 4.79086 2 7 2H40C42.2091 2 44 3.79086 44 6V71C44 73.2091 42.2091 75 40 75H7Z" fill="white" fill-opacity="0.01" shape-rendering="crispEdges"/>
5
+ <g clip-path="url(#clip1_6_9517)">
6
+ <rect width="73" height="41" transform="matrix(0 -1 1 0 3 75)" fill="white"/>
7
+ <path d="M13.1627 34.2082C11.7528 33.7797 9.7785 34.5992 10.0317 37.3067C10.2437 39.5746 11.2385 41.636 13.674 41.0316C14.8844 40.7312 14.5892 39.4034 14.4749 37.6975C14.3606 35.9916 14.5726 34.6366 13.1627 34.2082Z" fill="#2D2A29"/>
8
+ <path d="M13.1627 34.2082C11.7528 33.7797 9.7785 34.5992 10.0317 37.3067C10.2437 39.5746 11.2385 41.636 13.674 41.0316" fill="white"/>
9
+ <path d="M13.1627 34.2082C11.7528 33.7797 9.7785 34.5992 10.0317 37.3067C10.2437 39.5746 11.2385 41.636 13.674 41.0316" stroke="#1F1C1C" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"/>
10
+ <path d="M34.0715 34.1352C35.4783 33.6969 37.4583 34.5025 37.224 37.2118C37.0278 39.481 36.0474 41.5494 33.6078 40.962C32.3953 40.6701 32.6813 39.3402 32.7836 37.6335C32.886 35.9269 32.6646 34.5734 34.0715 34.1352Z" fill="#2D2A29"/>
11
+ <path d="M34.0715 34.1352C35.4783 33.6969 37.4583 34.5025 37.224 37.2118C37.0278 39.481 36.0474 41.5494 33.6078 40.962" fill="white"/>
12
+ <path d="M34.0715 34.1352C35.4783 33.6969 37.4583 34.5025 37.224 37.2118C37.0278 39.481 36.0474 41.5494 33.6078 40.962" stroke="#1F1C1C" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"/>
13
+ <path d="M23.4663 21.0379C11.0066 21.0268 12.8912 33.0461 13.0652 36.3497C13.2392 39.6534 14.8428 50.5533 23.2995 50.8312C32.2383 51.125 33.9327 39.9604 34.1366 36.2762C34.3404 32.592 35.8168 21.0489 23.4663 21.0379Z" fill="white"/>
14
+ <path d="M23.4663 21.0379C11.0066 21.0268 12.8912 33.0461 13.0652 36.3497C13.2392 39.6534 14.8428 50.5533 23.2995 50.8312C32.2383 51.125 33.9327 39.9604 34.1366 36.2762C34.3404 32.592 35.8168 21.0489 23.4663 21.0379Z" stroke="#1F1C1C" stroke-width="0.5"/>
15
+ <path d="M18.9219 34.4335C19.4824 34.4316 19.9396 35.0307 19.9422 35.7706C19.9448 36.5105 19.4918 37.1129 18.9313 37.1148C18.3707 37.1168 17.9135 36.5176 17.9109 35.7777C17.9083 35.0378 18.3614 34.4355 18.9219 34.4335Z" fill="#1F1C1C"/>
16
+ <path d="M28.4014 34.4002C28.9619 34.3982 29.4191 34.9974 29.4217 35.7373C29.4243 36.4772 28.9713 37.0796 28.4108 37.0815C27.8502 37.0835 27.393 36.4843 27.3904 35.7444C27.3878 35.0045 27.8409 34.4022 28.4014 34.4002Z" fill="#1F1C1C"/>
17
+ <path d="M17.1864 33.6408C17.7256 32.8805 19.2007 32.618 19.824 32.7241" stroke="#1F1C1C" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"/>
18
+ <path d="M30.0238 33.5958C29.4792 32.8393 28.0023 32.5871 27.3798 32.6976" stroke="#1F1C1C" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"/>
19
+ <path d="M24.2757 35.6876C24.2757 35.6876 24.3958 39.0457 24.208 39.5609C24.0202 40.0762 23.2895 40.2412 23.1541 40.2417" stroke="#1F1C1C" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"/>
20
+ <path d="M22.6777 44.9406C23.0693 45.0812 24.1131 45.0812 24.5348 44.9406" stroke="#1F1C1C" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"/>
21
+ <path d="M20.8545 43.285C20.8545 43.285 21.432 43.5445 23.511 43.5352C25.7796 43.5252 26.5584 43.292 26.5584 43.292C26.5584 43.292 25.3932 43.4648 23.511 43.4751C21.8646 43.4838 20.8545 43.285 20.8545 43.285Z" fill="white"/>
22
+ <path d="M20.8545 43.285C20.8545 43.285 21.432 43.5445 23.511 43.5352C25.7796 43.5252 26.5584 43.292 26.5584 43.292C26.5584 43.292 25.3932 43.4648 23.511 43.4751C21.8646 43.4838 20.8545 43.285 20.8545 43.285Z" stroke="#1F1C1C" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"/>
23
+ <path d="M23.9049 13C32.6828 13 39.8098 22.693 39.8098 34.6314C39.8098 46.5697 32.6828 56.2627 23.9049 56.2627C15.127 56.2627 8 46.5697 8 34.6314C8 22.693 15.127 13 23.9049 13Z" fill="#0D0D0D" fill-opacity="0.7" stroke="#EF4343" stroke-width="0.5"/>
24
+ </g>
25
+ <rect x="11" y="63" width="25" height="6" rx="3" fill="#848282"/>
26
+ </g>
27
+ <path d="M3.5 71L3.5 6C3.5 4.06701 5.067 2.5 7 2.5H40C41.933 2.5 43.5 4.06701 43.5 6V71C43.5 72.933 41.933 74.5 40 74.5H7C5.067 74.5 3.5 72.933 3.5 71Z" stroke="#E2E8F0" shape-rendering="crispEdges"/>
28
+ </g>
29
+ <defs>
30
+ <filter id="filter0_dd_6_9517" x="0" y="0" width="47" height="79" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
31
+ <feFlood flood-opacity="0" result="BackgroundImageFix"/>
32
+ <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
33
+ <feOffset dy="1"/>
34
+ <feGaussianBlur stdDeviation="1"/>
35
+ <feComposite in2="hardAlpha" operator="out"/>
36
+ <feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.06 0"/>
37
+ <feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_6_9517"/>
38
+ <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
39
+ <feOffset dy="1"/>
40
+ <feGaussianBlur stdDeviation="1.5"/>
41
+ <feComposite in2="hardAlpha" operator="out"/>
42
+ <feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0"/>
43
+ <feBlend mode="normal" in2="effect1_dropShadow_6_9517" result="effect2_dropShadow_6_9517"/>
44
+ <feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow_6_9517" result="shape"/>
45
+ </filter>
46
+ <clipPath id="clip0_6_9517">
47
+ <path d="M7 75C4.79086 75 3 73.2091 3 71L3 6C3 3.79086 4.79086 2 7 2H40C42.2091 2 44 3.79086 44 6V71C44 73.2091 42.2091 75 40 75H7Z" fill="white"/>
48
+ </clipPath>
49
+ <clipPath id="clip1_6_9517">
50
+ <rect width="73" height="41" fill="white" transform="matrix(0 -1 1 0 3 75)"/>
51
+ </clipPath>
52
+ </defs>
53
+ </svg>
@@ -0,0 +1,226 @@
1
+ import { useEffect, useRef, useState } from 'preact/hooks';
2
+ import type { FunctionComponent } from 'preact';
3
+ // dotLottie-web renders .lottie packages onto a <canvas>. The animation
4
+ // payloads are base64-inlined at build time (see vite.config.ts) so the
5
+ // distributable bundle remains a single file and no sibling asset hosting
6
+ // is required of consumers.
7
+ import { DotLottie } from '@lottiefiles/dotlottie-web';
8
+ import type { HeadPoseDirection } from '../utils/faceDetection';
9
+ import activeLivenessSrc from '../assets/active_liveness_animation.lottie';
10
+ import tooDarkSrc from '../assets/too_dark_animation.lottie';
11
+ import deviceOrientationSrc from '../assets/device_orientation.lottie';
12
+
13
+ interface ActiveLivenessOverlayProps {
14
+ /** Currently required pose (drives which segment plays). */
15
+ pose: HeadPoseDirection | null;
16
+ /**
17
+ * The user's currently classified head pose. When this matches the
18
+ * required `pose`, the overlay tints green to confirm the turn was
19
+ * successful. The animation itself keeps playing as normal.
20
+ */
21
+ currentPose?: HeadPoseDirection | null;
22
+ /** When true, swap to the "too dark" animation to prompt better lighting. */
23
+ isTooDark?: boolean;
24
+ /**
25
+ * When true, swap to the "rotate device" animation. Takes precedence over
26
+ * `isTooDark` and `pose` because the user can't make progress on any other
27
+ * check until they rotate the device back to portrait.
28
+ */
29
+ isLandscape?: boolean;
30
+ }
31
+
32
+ /**
33
+ * Per-pose frame ranges split by match state. The bundled .lottie was
34
+ * authored with two sibling layers for each direction — `arrow N` (gray)
35
+ * and `arrow - green N` — staggered so the green takes over later in the
36
+ * segment. We exploit that by scrubbing to the gray-only sub-range while
37
+ * the user is still being prompted (`pending`) and jumping to the
38
+ * green-only sub-range the moment the user's head pose matches the
39
+ * required direction (`matched`). This keeps the head/circle base art in
40
+ * its native colours and avoids tinting the whole canvas via CSS filters.
41
+ *
42
+ * Source ranges (layer ip/op from the lottie JSON):
43
+ * left – gray 1-112 | green 42-106
44
+ * right – gray 139-183 | green 180-241
45
+ * up – gray 277-321 | green 318-380
46
+ *
47
+ * The user-facing selfie preview is mirrored, so the on-screen arrows
48
+ * automatically appear flipped relative to the prompt label — no extra
49
+ * direction mapping needed here.
50
+ */
51
+ const POSE_FRAME_RANGES: Record<
52
+ HeadPoseDirection,
53
+ { pending: [number, number]; matched: [number, number] }
54
+ > = {
55
+ left: { pending: [1, 41], matched: [42, 106] },
56
+ right: { pending: [139, 179], matched: [180, 241] },
57
+ up: { pending: [277, 317], matched: [318, 380] },
58
+ };
59
+
60
+ /**
61
+ * Translucent face overlay shown during Enhanced SmartSelfie capture.
62
+ *
63
+ * - Anchored to the centre of the camera oval.
64
+ * - 75% opacity so the user's actual face remains visible underneath.
65
+ * - Plays the segment matching the currently-required head pose; segments
66
+ * loop until the pose changes.
67
+ */
68
+ export const ActiveLivenessOverlay: FunctionComponent<
69
+ ActiveLivenessOverlayProps
70
+ > = ({ pose, currentPose = null, isTooDark = false, isLandscape = false }) => {
71
+ const canvasRef = useRef<HTMLCanvasElement>(null);
72
+ const animRef = useRef<DotLottie | null>(null);
73
+ // Track which .lottie source is currently loaded so we only rebuild the
74
+ // player when actually switching between the pose and too-dark variants.
75
+ const loadedVariantRef = useRef<'pose' | 'too-dark' | 'orientation' | null>(
76
+ null,
77
+ );
78
+ // Lottie pose arrows were authored at a snappy pace; played at native
79
+ // speed they feel hectic and the segment cuts on pose change read as
80
+ // hard jumps. A small slowdown calms both.
81
+ const PLAYBACK_SPEED = 0.6;
82
+ // Brief fade applied to the canvas around a segment swap so the playhead
83
+ // jump (setSegment is instantaneous) reads as a crossfade rather than a
84
+ // hard cut. Matched should keep the user's attention, so the fade is
85
+ // short.
86
+ const SEGMENT_FADE_MS = 180;
87
+ const [fading, setFading] = useState(false);
88
+ // Tracks the segment inputs we last applied so we can skip the fade on
89
+ // the initial mount (the player is already showing the right segment)
90
+ // and on no-op re-renders where neither pose nor matched changed.
91
+ const lastAppliedRef = useRef<{
92
+ pose: HeadPoseDirection | null;
93
+ matched: boolean;
94
+ } | null>(null);
95
+
96
+ // Green tint applies only when the user's live head pose matches the
97
+ // required prompt — reserved for an actual successful turn, not for the
98
+ // canned guidance loop.
99
+ const matched = !!pose && !!currentPose && pose === currentPose;
100
+
101
+ useEffect(() => {
102
+ if (!canvasRef.current) return undefined;
103
+
104
+ const variant: 'pose' | 'too-dark' | 'orientation' = (() => {
105
+ if (isLandscape) return 'orientation';
106
+ if (isTooDark) return 'too-dark';
107
+ return 'pose';
108
+ })();
109
+ if (loadedVariantRef.current === variant && animRef.current) {
110
+ return undefined;
111
+ }
112
+
113
+ if (animRef.current) {
114
+ animRef.current.destroy();
115
+ animRef.current = null;
116
+ }
117
+
118
+ // Initial segment for the freshly-built player. `pose` uses the prompt
119
+ // range (gray sub-range, since a fresh build can't yet be `matched`);
120
+ // `too-dark` and `orientation` play their full timeline as a single
121
+ // looping animation.
122
+ const initialSegment = (() => {
123
+ if (variant === 'pose' && pose) return POSE_FRAME_RANGES[pose].pending;
124
+ return undefined;
125
+ })();
126
+ const src = (() => {
127
+ if (variant === 'orientation') return deviceOrientationSrc;
128
+ if (variant === 'too-dark') return tooDarkSrc;
129
+ return activeLivenessSrc;
130
+ })();
131
+ const anim = new DotLottie({
132
+ canvas: canvasRef.current,
133
+ src,
134
+ loop: true,
135
+ autoplay: true,
136
+ speed: PLAYBACK_SPEED,
137
+ ...(initialSegment ? { segment: initialSegment } : {}),
138
+ renderConfig: { autoResize: true },
139
+ });
140
+ animRef.current = anim;
141
+ loadedVariantRef.current = variant;
142
+ // Player just rebuilt with its initial segment — clear the applied
143
+ // record so the segment-change effect treats the next dep evaluation
144
+ // as the first one and doesn't trigger a redundant fade.
145
+ lastAppliedRef.current = null;
146
+
147
+ return () => {
148
+ anim.destroy();
149
+ animRef.current = null;
150
+ loadedVariantRef.current = null;
151
+ };
152
+ }, [isTooDark, isLandscape]);
153
+
154
+ useEffect(() => {
155
+ const anim = animRef.current;
156
+ if (!anim || isTooDark || isLandscape) return undefined;
157
+
158
+ const prev = lastAppliedRef.current;
159
+ const isFirst = prev === null;
160
+ const unchanged =
161
+ !isFirst && prev.pose === pose && prev.matched === matched;
162
+ if (unchanged) return undefined;
163
+
164
+ // First time we see this player: it was built with the right initial
165
+ // segment already, so just record state and don't trigger a fade.
166
+ if (isFirst) {
167
+ lastAppliedRef.current = { pose, matched };
168
+ return undefined;
169
+ }
170
+
171
+ // Real transition — fade out, swap segment at the bottom of the
172
+ // fade, then fade back in. This hides the playhead jump that
173
+ // setSegment causes.
174
+ setFading(true);
175
+ const t = window.setTimeout(() => {
176
+ if (pose) {
177
+ const ranges = POSE_FRAME_RANGES[pose];
178
+ const [start, end] = matched ? ranges.matched : ranges.pending;
179
+ anim.setSegment(start, end);
180
+ anim.play();
181
+ } else {
182
+ anim.resetSegment();
183
+ anim.play();
184
+ }
185
+ lastAppliedRef.current = { pose, matched };
186
+ setFading(false);
187
+ }, SEGMENT_FADE_MS);
188
+ return () => window.clearTimeout(t);
189
+ }, [pose, matched, isTooDark, isLandscape]);
190
+
191
+ return (
192
+ <>
193
+ <canvas
194
+ ref={canvasRef}
195
+ className={`active-liveness-overlay${fading ? ' is-fading' : ''}`}
196
+ aria-hidden="true"
197
+ />
198
+ <style>{`
199
+ .active-liveness-overlay {
200
+ position: absolute;
201
+ top: 50%;
202
+ left: 50%;
203
+ transform: translate(-50%, -50%);
204
+ width: 90%;
205
+ aspect-ratio: 1;
206
+ pointer-events: none;
207
+ opacity: 0.75;
208
+ z-index: 2;
209
+ /* Canvas elements default to inline; force block so width/height
210
+ driven by CSS apply cleanly. */
211
+ display: block;
212
+ transition: opacity ${SEGMENT_FADE_MS}ms ease;
213
+ /* No colour filters: the lottie asset already paints arrows in
214
+ their correct colours (gray during the pending sub-range,
215
+ green during the matched sub-range). The face/circle base art
216
+ keeps its native palette. */
217
+ }
218
+ .active-liveness-overlay.is-fading {
219
+ opacity: 0;
220
+ }
221
+ `}</style>
222
+ </>
223
+ );
224
+ };
225
+
226
+ export default ActiveLivenessOverlay;
@@ -0,0 +1,38 @@
1
+ import type { FunctionComponent } from 'preact';
2
+
3
+ interface AlertDisplayProps {
4
+ alertTitle: string;
5
+ themeColor?: string;
6
+ }
7
+
8
+ export const AlertDisplay: FunctionComponent<AlertDisplayProps> = ({
9
+ alertTitle,
10
+ themeColor = '#151F72',
11
+ }) =>
12
+ alertTitle ? (
13
+ <>
14
+ <div className="alert-message">
15
+ <div className="alert-title" style={{ color: themeColor }}>
16
+ {alertTitle}
17
+ </div>
18
+ </div>
19
+
20
+ <style>{`
21
+ .alert-message {
22
+ margin-top: clamp(12px, 2.5dvh, 24px);
23
+ padding: 0 16px;
24
+ text-align: center;
25
+ width: 100%;
26
+ }
27
+
28
+ .alert-title {
29
+ font-family: "DM Sans", system-ui, sans-serif;
30
+ font-size: 14px;
31
+ font-style: normal;
32
+ font-weight: 700;
33
+ line-height: 20px;
34
+ text-align: center;
35
+ }
36
+ `}</style>
37
+ </>
38
+ ) : null;
@@ -0,0 +1,45 @@
1
+ import type { FunctionComponent } from 'preact';
2
+ import { useEffect, useRef } from 'preact/hooks';
3
+
4
+ interface BackNavigationProps {
5
+ onBack: () => void;
6
+ themeColor?: string;
7
+ }
8
+
9
+ /**
10
+ * Thin Preact wrapper around <smileid-navigation hide-close> that bridges
11
+ * the element's custom `navigation.back` event to a Preact callback prop.
12
+ * Kept in ESS so callers don't have to think about the underlying element.
13
+ */
14
+ export const BackNavigation: FunctionComponent<BackNavigationProps> = ({
15
+ onBack,
16
+ themeColor,
17
+ }) => {
18
+ const ref = useRef<HTMLElement | null>(null);
19
+
20
+ useEffect(() => {
21
+ const el = ref.current;
22
+ if (!el) return undefined;
23
+ const handler = () => onBack();
24
+ el.addEventListener('navigation.back', handler);
25
+ return () => el.removeEventListener('navigation.back', handler);
26
+ }, [onBack]);
27
+
28
+ return (
29
+ // @ts-expect-error custom element
30
+ <smileid-navigation
31
+ ref={ref}
32
+ hide-close=""
33
+ theme-color={themeColor || undefined}
34
+ class="ess-back-navigation"
35
+ style={{
36
+ // Match the ESS design: solid dark circle with a white arrow,
37
+ // overriding Navigation's default translucent grey pill.
38
+ '--smileid-navigation-button-bg': '#1f1f1f',
39
+ '--smileid-navigation-icon-color': '#FFFFFF',
40
+ }}
41
+ />
42
+ );
43
+ };
44
+
45
+ export default BackNavigation;
@@ -0,0 +1,96 @@
1
+ import type { ComponentChildren, FunctionComponent, Ref } from 'preact';
2
+ import OvalProgress from '../OvalProgress';
3
+
4
+ interface CameraPreviewProps {
5
+ videoRef: Ref<HTMLVideoElement>;
6
+ canvasRef: Ref<HTMLCanvasElement>;
7
+ facingMode: 'user' | 'environment';
8
+ themeColor: string;
9
+ /** Optional overlay rendered inside the video container (e.g. active-liveness face animation). */
10
+ overlay?: ComponentChildren;
11
+ /** Optional override for the oval border colour (used to signal error states). */
12
+ borderColor?: string;
13
+ /** Highlight the offending side of the oval when a quality check fails. */
14
+ errorSide?: 'top' | 'right' | 'bottom' | 'left' | 'all' | null;
15
+ }
16
+
17
+ export const CameraPreview: FunctionComponent<CameraPreviewProps> = ({
18
+ videoRef,
19
+ canvasRef,
20
+ facingMode,
21
+ overlay,
22
+ borderColor,
23
+ errorSide = null,
24
+ }) => (
25
+ <>
26
+ <div className="camera-preview-container">
27
+ <div className="video-wrapper">
28
+ <div className="video-container">
29
+ <video
30
+ ref={videoRef}
31
+ autoPlay
32
+ playsInline
33
+ muted
34
+ className={`camera-video ${facingMode === 'user' ? 'mirror' : ''}`}
35
+ />
36
+ <canvas
37
+ ref={canvasRef}
38
+ className={`camera-canvas ${facingMode === 'user' ? 'mirror' : ''}`}
39
+ />
40
+ {overlay}
41
+ </div>
42
+ </div>
43
+ <OvalProgress borderColor={borderColor} errorSide={errorSide} />
44
+ </div>
45
+
46
+ <style>{`
47
+ .camera-preview-container {
48
+ position: relative;
49
+ width: auto;
50
+ height: clamp(220px, 42dvh, 418px);
51
+ aspect-ratio: 311 / 418;
52
+ max-width: 100%;
53
+ margin: 0 auto;
54
+ flex-shrink: 1;
55
+ min-height: 0;
56
+ }
57
+
58
+ .video-wrapper {
59
+ width: 100%;
60
+ height: 100%;
61
+ margin: 0 auto;
62
+ position: relative;
63
+ border: 2px solid #C4C4C4;
64
+ border-radius: 50%;
65
+ overflow: hidden;
66
+ background: #F5F7FA;
67
+ box-sizing: border-box;
68
+ }
69
+
70
+ .video-container {
71
+ position: absolute;
72
+ top: 0;
73
+ left: 0;
74
+ width: 100%;
75
+ height: 100%;
76
+ }
77
+
78
+ .camera-video,
79
+ .camera-canvas {
80
+ position: absolute;
81
+ top: 50%;
82
+ left: 50%;
83
+ transform: translate(-50%, -50%);
84
+ }
85
+
86
+ .camera-video.mirror,
87
+ .camera-canvas.mirror {
88
+ transform: translate(-50%, -50%) scaleX(-1);
89
+ }
90
+
91
+ .camera-canvas {
92
+ pointer-events: none;
93
+ }
94
+ `}</style>
95
+ </>
96
+ );