@smileid/usesmileid 12.2.0 → 12.3.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 (169) hide show
  1. package/CHANGELOG.md +5 -1
  2. package/README.md +57 -18
  3. package/dist/camera/bounding_box_metering_region.d.ts +10 -0
  4. package/dist/camera/bounding_box_metering_region.js +1 -0
  5. package/dist/camera/camera_defaults.d.ts +0 -2
  6. package/dist/camera/camera_defaults.js +1 -1
  7. package/dist/camera/document_image_pipeline.d.ts +9 -0
  8. package/dist/camera/document_image_pipeline.js +1 -1
  9. package/dist/camera/imported_document_folder.d.ts +15 -0
  10. package/dist/camera/imported_document_folder.js +1 -0
  11. package/dist/camera/kamera_config_factory.d.ts +11 -1
  12. package/dist/camera/kamera_config_factory.js +1 -1
  13. package/dist/camera/use_smile_id_frame_stream.js +1 -1
  14. package/dist/camera/use_smile_id_kamera_camera_system.d.ts +46 -2
  15. package/dist/camera/use_smile_id_kamera_camera_system.js +1 -1
  16. package/dist/core/exception/use_smile_id_error_code.js +1 -1
  17. package/dist/core/exception/use_smile_id_network_exception.d.ts +10 -3
  18. package/dist/core/exception/use_smile_id_network_exception.js +1 -1
  19. package/dist/core/is_development_build.d.ts +6 -0
  20. package/dist/core/is_development_build.js +1 -0
  21. package/dist/core/models/config.d.ts +6 -0
  22. package/dist/core/models/ml_config.d.ts +1 -1
  23. package/dist/core/models/network_config.d.ts +4 -1
  24. package/dist/core/models/params.d.ts +8 -6
  25. package/dist/core/models/result.d.ts +11 -0
  26. package/dist/core/models/result.js +1 -1
  27. package/dist/core/perf/capture_perf_trace.d.ts +90 -0
  28. package/dist/core/perf/capture_perf_trace.js +1 -0
  29. package/dist/data/api/interceptors/logging_interceptor.js +1 -1
  30. package/dist/data/api/interceptors/smile_auth_interceptor.js +1 -1
  31. package/dist/data/api/request_capture.d.ts +16 -0
  32. package/dist/data/api/request_capture.js +1 -0
  33. package/dist/data/api/smile_id_api.d.ts +1 -1
  34. package/dist/data/api/smile_id_api.js +1 -1
  35. package/dist/data/factory/networking_factory.d.ts +3 -1
  36. package/dist/data/factory/networking_factory.js +1 -1
  37. package/dist/data/model/captured_job.d.ts +49 -0
  38. package/dist/data/model/captured_job.js +1 -0
  39. package/dist/data/model/job_request.d.ts +1 -1
  40. package/dist/data/model/sealed_job_request.d.ts +15 -0
  41. package/dist/data/model/sealed_job_request.js +1 -0
  42. package/dist/data/model/token_payload.d.ts +5 -7
  43. package/dist/data/model/token_payload.js +1 -1
  44. package/dist/data/model/user_details_payload.d.ts +6 -4
  45. package/dist/data/repository/use_smile_id_repository.d.ts +7 -0
  46. package/dist/data/repository/use_smile_id_repository_impl.d.ts +29 -2
  47. package/dist/data/repository/use_smile_id_repository_impl.js +1 -1
  48. package/dist/data/security/job_package_crypto.d.ts +41 -0
  49. package/dist/data/security/job_package_crypto.js +1 -0
  50. package/dist/data/security/job_package_sealer.d.ts +35 -0
  51. package/dist/data/security/job_package_sealer.js +1 -0
  52. package/dist/data/security/signing_crypto.d.ts +34 -0
  53. package/dist/data/security/signing_crypto.js +1 -1
  54. package/dist/data/util/retry_util.d.ts +9 -6
  55. package/dist/data/util/retry_util.js +1 -1
  56. package/dist/debug/selfie_preview_debug_geometry.d.ts +13 -13
  57. package/dist/debug/selfie_preview_debug_geometry.js +1 -1
  58. package/dist/debug/use_smile_id_debug.d.ts +25 -2
  59. package/dist/debug/use_smile_id_debug.js +1 -1
  60. package/dist/debug/use_smile_id_document_preview_debug_overlay.js +1 -1
  61. package/dist/debug/use_smile_id_selfie_preview_debug_overlay.d.ts +5 -1
  62. package/dist/debug/use_smile_id_selfie_preview_debug_overlay.js +1 -1
  63. package/dist/index.d.ts +4 -1
  64. package/dist/index.js +1 -1
  65. package/dist/l10n/strings_en.d.ts +3 -3
  66. package/dist/l10n/strings_en.js +2 -2
  67. package/dist/l10n/use_smile_id_localizations.d.ts +5 -7
  68. package/dist/l10n/use_smile_id_localizations.js +1 -1
  69. package/dist/metadata/metadata_configuration_mappers.d.ts +3 -5
  70. package/dist/metadata/metadata_configuration_mappers.js +1 -1
  71. package/dist/metadata/use_smile_id_metadata_factory.js +1 -1
  72. package/dist/presentation/assets/si_dark_lighting_animation.json +1 -927
  73. package/dist/presentation/assets/si_device_orientation_animation.json +1 -850
  74. package/dist/presentation/assets/si_document_green_book_hero_animation.json +1 -4740
  75. package/dist/presentation/assets/si_document_hero_animation.json +1 -4870
  76. package/dist/presentation/assets/si_document_hero_back_animation.json +1 -2122
  77. package/dist/presentation/assets/si_document_passport_hero_animation.json +1 -14540
  78. package/dist/presentation/assets/si_ess_all_directions_hero_animation.json +1 -5906
  79. package/dist/presentation/assets/si_ess_animation_all_directions.json +1 -5906
  80. package/dist/presentation/assets/si_selfie_hero_animation.json +1 -1468
  81. package/dist/presentation/assets/si_smile_to_capture_animation.json +1 -1412
  82. package/dist/presentation/components/helpers/captured_image_uri.d.ts +11 -0
  83. package/dist/presentation/components/helpers/captured_image_uri.js +1 -0
  84. package/dist/presentation/components/helpers/captured_oval_region.d.ts +40 -0
  85. package/dist/presentation/components/helpers/captured_oval_region.js +1 -0
  86. package/dist/presentation/components/helpers/document_interior_crop.d.ts +13 -2
  87. package/dist/presentation/components/helpers/document_interior_crop.js +1 -1
  88. package/dist/presentation/components/helpers/enhanced_smart_selfie_animation_frames.d.ts +35 -29
  89. package/dist/presentation/components/helpers/enhanced_smart_selfie_animation_frames.js +1 -1
  90. package/dist/presentation/components/helpers/selfie_interior_crop.js +1 -1
  91. package/dist/presentation/components/use_smile_id_capture_content.d.ts +5 -2
  92. package/dist/presentation/components/use_smile_id_capture_content.js +1 -1
  93. package/dist/presentation/components/use_smile_id_captured_document_image.d.ts +8 -4
  94. package/dist/presentation/components/use_smile_id_captured_document_image.js +1 -1
  95. package/dist/presentation/components/use_smile_id_captured_image_oval.d.ts +6 -0
  96. package/dist/presentation/components/use_smile_id_captured_image_oval.js +1 -1
  97. package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_clip.d.ts +17 -7
  98. package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_clip.js +1 -1
  99. package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_direction_animation.d.ts +13 -14
  100. package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_direction_animation.js +1 -1
  101. package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_full_animation.js +1 -1
  102. package/dist/presentation/components/use_smile_id_face_oval_image.js +1 -1
  103. package/dist/presentation/flow/analytics/unsatisfied_reasons.d.ts +14 -0
  104. package/dist/presentation/flow/analytics/unsatisfied_reasons.js +1 -0
  105. package/dist/presentation/flow/analytics/use_smile_id_analytics_event.d.ts +8 -7
  106. package/dist/presentation/flow/analytics/use_smile_id_analytics_event.js +1 -1
  107. package/dist/presentation/flow/analytics/use_smile_id_analytics_event_kind.d.ts +9 -0
  108. package/dist/presentation/flow/analytics/use_smile_id_analytics_event_kind.js +1 -1
  109. package/dist/presentation/flow/config/flow_configuration_camera.d.ts +9 -0
  110. package/dist/presentation/flow/config/flow_configuration_camera.js +1 -0
  111. package/dist/presentation/flow/dsl/builder/config_builder.d.ts +6 -0
  112. package/dist/presentation/flow/dsl/builder/config_builder.js +1 -1
  113. package/dist/presentation/flow/dsl/builder/ml_builder.d.ts +1 -7
  114. package/dist/presentation/flow/dsl/builder/ml_builder.js +1 -1
  115. package/dist/presentation/flow/dsl/use_smile_id_flow_builder.d.ts +0 -9
  116. package/dist/presentation/flow/dsl/use_smile_id_flow_builder.js +1 -1
  117. package/dist/presentation/flow/flow_navigation_manager.d.ts +39 -70
  118. package/dist/presentation/flow/flow_navigation_manager.js +1 -1
  119. package/dist/presentation/flow/flow_navigation_manager_analytics.d.ts +23 -0
  120. package/dist/presentation/flow/flow_navigation_manager_analytics.js +1 -0
  121. package/dist/presentation/flow/flow_navigation_manager_documents.d.ts +18 -0
  122. package/dist/presentation/flow/flow_navigation_manager_documents.js +1 -0
  123. package/dist/presentation/flow/flow_navigation_manager_job_request.d.ts +7 -0
  124. package/dist/presentation/flow/flow_navigation_manager_job_request.js +1 -0
  125. package/dist/presentation/flow/flow_navigation_manager_routing.d.ts +12 -0
  126. package/dist/presentation/flow/flow_navigation_manager_routing.js +1 -0
  127. package/dist/presentation/flow/flow_navigation_manager_submission.d.ts +17 -0
  128. package/dist/presentation/flow/flow_navigation_manager_submission.js +1 -0
  129. package/dist/presentation/flow/job_submission_state.d.ts +4 -0
  130. package/dist/presentation/flow/screen/helpers/face_session_config.d.ts +1 -1
  131. package/dist/presentation/flow/screen/helpers/failure_message.js +1 -1
  132. package/dist/presentation/flow/screen/orchestrated_capture_screen.js +1 -1
  133. package/dist/presentation/flow/screen/orchestrated_instructions_screen.js +1 -1
  134. package/dist/presentation/flow/screen/orchestrated_preview_screen.js +1 -1
  135. package/dist/presentation/flow/screen/orchestrated_processing_screen.js +1 -1
  136. package/dist/presentation/flow/use_smile_id_flow.js +1 -1
  137. package/dist/presentation/flow/validation/flow_validator.d.ts +0 -19
  138. package/dist/presentation/flow/validation/flow_validator.js +1 -1
  139. package/dist/presentation/flow/validation/job_type_validator.js +1 -1
  140. package/dist/presentation/screen/helpers/capture_screen_helpers.d.ts +0 -12
  141. package/dist/presentation/screen/helpers/capture_screen_helpers.js +1 -1
  142. package/dist/presentation/screen/helpers/capture_ui_state.d.ts +33 -6
  143. package/dist/presentation/screen/helpers/capture_ui_state.js +1 -1
  144. package/dist/presentation/screen/helpers/dropped_frames.d.ts +6 -0
  145. package/dist/presentation/screen/helpers/dropped_frames.js +1 -0
  146. package/dist/presentation/screen/helpers/ess_turn_haptics.d.ts +2 -0
  147. package/dist/presentation/screen/helpers/ess_turn_haptics.js +1 -1
  148. package/dist/presentation/screen/helpers/instructions_screen_helpers.d.ts +5 -1
  149. package/dist/presentation/screen/helpers/instructions_screen_helpers.js +1 -1
  150. package/dist/presentation/screen/helpers/use_capture_orientation_lock.d.ts +23 -1
  151. package/dist/presentation/screen/helpers/use_capture_orientation_lock.js +1 -1
  152. package/dist/presentation/screen/helpers/use_debounced_feedback_key.d.ts +1 -1
  153. package/dist/presentation/screen/helpers/use_debounced_feedback_key.js +1 -1
  154. package/dist/presentation/screen/use_smile_id_capture_screen.d.ts +4 -1
  155. package/dist/presentation/screen/use_smile_id_capture_screen.js +1 -1
  156. package/dist/presentation/screen/use_smile_id_consent_screen.js +1 -1
  157. package/dist/presentation/screen/use_smile_id_preview_screen.d.ts +3 -0
  158. package/dist/presentation/screen/use_smile_id_preview_screen.js +1 -1
  159. package/dist/presentation/screen/use_smile_id_processing_screen.d.ts +3 -0
  160. package/dist/presentation/screen/use_smile_id_processing_screen.js +1 -1
  161. package/dist/presentation/utils/locale_utils.d.ts +3 -6
  162. package/dist/presentation/utils/locale_utils.js +1 -1
  163. package/dist/testing/scenario_expectations.d.ts +7 -2
  164. package/dist/testing/scenario_expectations.js +1 -1
  165. package/dist/testing/scripted_scan_results.d.ts +2 -1
  166. package/dist/testing/scripted_scan_results.js +1 -1
  167. package/package.json +7 -7
  168. package/dist/presentation/flow/validation/user_details_validator.d.ts +0 -8
  169. package/dist/presentation/flow/validation/user_details_validator.js +0 -1
@@ -0,0 +1,11 @@
1
+ /** Records that a new take was just written to `uri`; call it as the capture is recorded. */
2
+ export declare function markCapturedTake(uri: string): void;
3
+ /**
4
+ * `uri` tagged with the take it holds, so every screen showing one take shares
5
+ * one cache entry and a retake gets its own. The image loaders read a `file://`
6
+ * URI's path, so the query only changes the cache key. A URI with no recorded
7
+ * take, one that is not a file, or one already tagged comes back unchanged.
8
+ */
9
+ export declare function capturedImageUri(uri: string): string;
10
+ /** Test seam: forgets every recorded take. */
11
+ export declare function resetCapturedTakesForTesting(): void;
@@ -0,0 +1 @@
1
+ "use strict";const t=new Map;let a=Date.now();const r="take=";export function markCapturedTake(e){a+=1,t.set(e,a)}export function capturedImageUri(e){const n=t.get(e);return n===void 0||!e.startsWith("file://")?e:`${e}${e.includes("?")?"&":"?"}${r}${n}`}export function resetCapturedTakesForTesting(){t.clear()}
@@ -0,0 +1,40 @@
1
+ import { type SelfieSize } from "./selfie_interior_crop";
2
+ /** A rectangle in layout units, or image pixels. */
3
+ export type RegionRect = {
4
+ readonly x: number;
5
+ readonly y: number;
6
+ readonly width: number;
7
+ readonly height: number;
8
+ };
9
+ /** Width over height of the upright selfie camera frame: a 4:3 stream held in portrait. */
10
+ export declare const SELFIE_FRAME_ASPECT: number;
11
+ /**
12
+ * What a selfie capture screen laid out, so the review screens show what its oval showed: the size
13
+ * its camera covered and the shape (width over height) of the upright frames the camera showed.
14
+ */
15
+ export type SelfieCaptureGeometry = {
16
+ readonly viewport: SelfieSize;
17
+ readonly frameAspect: number;
18
+ };
19
+ /**
20
+ * How a captured selfie is placed in a review screen's oval so it shows what the capture screen's
21
+ * oval showed.
22
+ *
23
+ * The capture screen fills its `viewport` with the camera frame (`frameAspect`, width over
24
+ * height), covered and centred, and draws the oval where `FaceOvalDefaults` puts it: above the
25
+ * screen's centre. The selfie is saved from that frame, centre-cropped to its configured size, so
26
+ * centring it in the review oval would show a region lower than the one the user framed their face
27
+ * in (their neck) and smaller. Instead the region the capture oval covered fills the review oval.
28
+ *
29
+ * The frame's border ring stays out of view as before (`SelfieInteriorCrop`): the scale is never
30
+ * below the interior's cover scale, and the image is never moved so far that the ring shows.
31
+ */
32
+ export declare function capturedOvalRegion(viewport: SelfieSize, frameAspect?: number): {
33
+ regionIn: (image: SelfieSize) => RegionRect;
34
+ scale: (image: SelfieSize, box: SelfieSize) => number;
35
+ offset: (drawn: SelfieSize, box: SelfieSize) => {
36
+ readonly x: number;
37
+ readonly y: number;
38
+ };
39
+ imageRect: (image: SelfieSize, box: SelfieSize) => RegionRect;
40
+ };
@@ -0,0 +1 @@
1
+ "use strict";import{FaceOvalDefaults as l}from"../face_oval_defaults";import{SelfieInteriorCrop as w}from"./selfie_interior_crop";export const SELFIE_FRAME_ASPECT=3/4;export function capturedOvalRegion(i,c=SELFIE_FRAME_ASPECT){function d(t){if(r(t)||r(i)||!Number.isFinite(c)||c<=0)return{x:0,y:0,width:t.width,height:t.height};const h=t.width/t.height>=c?{width:t.width,height:t.width/c}:{width:t.height*c,height:t.height},n=(h.width-t.width)/2,o=(h.height-t.height)/2,e=Math.max(i.width/h.width,i.height/h.height),y=(i.width-h.width*e)/2,M=(i.height-h.height*e)/2,f=i.width*l.ovalWidthRatio,R=i.height*l.ovalHeightRatio,F=(i.width-f)/2,S=i.height*l.ovalTopRatio;return{x:(F-y)/e-n,y:(S-M)/e-o,width:f/e,height:R/e}}function g(t,h){if(r(t)||r(h))return 1;const n=d(t),o=n.width>0&&n.height>0?Math.max(h.width/n.width,h.height/n.height):0;return Math.max(o,w.coverScale(t,h))}function s(t,h){if(r(t)||r(h))return{x:0,y:0};const n=d(t),o=w.borderFraction*Math.min(t.width,t.height);return{x:a(h.width/2-(n.x+n.width/2),h.width,t.width,o),y:a(h.height/2-(n.y+n.height/2),h.height,t.height,o)}}function u(t,h){const n=g(t,h),o={width:t.width*n,height:t.height*n},e=s(o,h);return{x:e.x,y:e.y,...o}}return{regionIn:d,scale:g,offset:s,imageRect:u}}function a(i,c,d,g){const s=c-(d-g),u=-g;return s>u?(c-d)/2:Math.min(Math.max(i,s),u)}function r(i){return i.width<=0||i.height<=0}
@@ -20,7 +20,8 @@ export type DocumentImageRect = {
20
20
  * each edge, so showing the outline alone could cut a captured document off; the
21
21
  * screens that show the document fit the outline grown by that slack into their
22
22
  * card instead (`shownSize`). A manual-shutter still or a gallery import is saved
23
- * without a border, so its border fraction is 0 and it is shown whole.
23
+ * without a border, so its border fraction is 0 and it is shown whole, in a card
24
+ * of its own shape (`cardAspectRatio`).
24
25
  */
25
26
  export declare const DocumentInteriorCrop: {
26
27
  /** The full border, as a fraction of the saved document's short side on every edge. */
@@ -39,10 +40,20 @@ export declare const DocumentInteriorCrop: {
39
40
  * edge, and held to the image. The whole image when `borderFraction` is 0.
40
41
  */
41
42
  readonly shownSize: (imageSize: SelfieSize, borderFraction: number) => SelfieSize;
43
+ /**
44
+ * Aspect ratio of the card a document is drawn in: `documentAspectRatio`, the
45
+ * shape of the outline the user framed, for a document saved with a border.
46
+ * One saved with none takes the shape of its `imageSize` once that is known,
47
+ * so the whole image fills the card with nothing beside it; a portrait photo
48
+ * of a passport would otherwise be a strip down the middle of a wide card.
49
+ */
50
+ readonly cardAspectRatio: (imageSize: SelfieSize | null, documentAspectRatio: number, borderFraction: number) => number;
42
51
  /**
43
52
  * Uniform scale at which an `imageSize` document is drawn in a `boxSize` card,
44
53
  * centred: its `shownSize` part fitted to the card, or, with no border, the
45
- * whole image covering it. Returns 1 when either size is degenerate.
54
+ * whole image fitted to it: a gallery import keeps the shape it was picked
55
+ * in, and covering the card with it would cut the document off. Returns 1
56
+ * when either size is degenerate.
46
57
  */
47
58
  readonly cardScale: (imageSize: SelfieSize, boxSize: SelfieSize, borderFraction: number) => number;
48
59
  /**
@@ -1 +1 @@
1
- "use strict";export const DocumentInteriorCrop={fullBorderFraction:.15,guideEdgeSlack:.1,shownSize(t,h){const n=Math.min(Math.max(h,0),DocumentInteriorCrop.fullBorderFraction)*Math.min(t.width,t.height),c=1+2*DocumentInteriorCrop.guideEdgeSlack;return{width:Math.min(t.width,(t.width-2*n)*c),height:Math.min(t.height,(t.height-2*n)*c)}},cardScale(t,h,r){if(t.width<=0||t.height<=0||h.width<=0||h.height<=0)return 1;if(r<=0)return Math.max(h.width/t.width,h.height/t.height);const n=DocumentInteriorCrop.shownSize(t,r);return Math.min(h.width/n.width,h.height/n.height)},imageRect(t,h,r){const n=DocumentInteriorCrop.cardScale(t,h,r),c=t.width*n,d=t.height*n;return{left:(h.width-c)/2,top:(h.height-d)/2,width:c,height:d}}};
1
+ "use strict";export const DocumentInteriorCrop={fullBorderFraction:.15,guideEdgeSlack:.1,shownSize(t,h){const n=Math.min(Math.max(h,0),DocumentInteriorCrop.fullBorderFraction)*Math.min(t.width,t.height),d=1+2*DocumentInteriorCrop.guideEdgeSlack;return{width:Math.min(t.width,(t.width-2*n)*d),height:Math.min(t.height,(t.height-2*n)*d)}},cardAspectRatio(t,h,r){return r>0||t===null||t.width<=0||t.height<=0?h:t.width/t.height},cardScale(t,h,r){if(t.width<=0||t.height<=0||h.width<=0||h.height<=0)return 1;if(r<=0)return Math.min(h.width/t.width,h.height/t.height);const n=DocumentInteriorCrop.shownSize(t,r);return Math.min(h.width/n.width,h.height/n.height)},imageRect(t,h,r){const n=DocumentInteriorCrop.cardScale(t,h,r),d=t.width*n,c=t.height*n;return{left:(h.width-d)/2,top:(h.height-c)/2,width:d,height:c}}};
@@ -6,59 +6,65 @@ export type EnhancedSmartSelfieFrameRange = {
6
6
  };
7
7
  /**
8
8
  * Per-direction keyframe sub-ranges within the
9
- * `si_ess_animation_all_directions` composition (419 frames @ 30 fps).
10
- * Each direction splits into a grey `pending` span (the user has not yet
11
- * held the requested pose) and a green `matched` span (the pose currently
12
- * holds the direction). Pinned against the asset's layer in/out points —
13
- * it carries no named markers — so this is a cross-platform contract:
14
- * change only in lockstep with the sibling SDKs and re-derive when the
15
- * asset changes.
9
+ * `si_ess_animation_all_directions` composition (419 frames @ 30 fps),
10
+ * scrubbed by a turn's progress. Each direction has a grey `turning` span,
11
+ * where the drawn head turns and the grey chevrons slide out, and a green
12
+ * `holding` span, where the head holds the turn and the green chevron slides
13
+ * further out. The spans skip the frames where the chevrons are not drawn yet
14
+ * or have faded. Pinned against the asset's layer in/out points — it carries
15
+ * no named markers — so this is a cross-platform contract: change only in
16
+ * lockstep with the sibling SDKs and re-derive when the asset changes.
16
17
  */
17
18
  export declare const EnhancedSmartSelfieAnimationFrames: {
18
19
  readonly left: {
19
- readonly pending: {
20
- readonly startFrame: 1;
21
- readonly endFrame: 41;
20
+ readonly turning: {
21
+ readonly startFrame: 8;
22
+ readonly endFrame: 38;
22
23
  };
23
- readonly matched: {
24
+ readonly holding: {
24
25
  readonly startFrame: 42;
25
- readonly endFrame: 106;
26
+ readonly endFrame: 90;
26
27
  };
27
28
  };
28
29
  readonly right: {
29
- readonly pending: {
30
- readonly startFrame: 139;
31
- readonly endFrame: 179;
30
+ readonly turning: {
31
+ readonly startFrame: 146;
32
+ readonly endFrame: 176;
32
33
  };
33
- readonly matched: {
34
+ readonly holding: {
34
35
  readonly startFrame: 180;
35
- readonly endFrame: 241;
36
+ readonly endFrame: 225;
36
37
  };
37
38
  };
38
39
  readonly up: {
39
- readonly pending: {
40
- readonly startFrame: 277;
41
- readonly endFrame: 317;
40
+ readonly turning: {
41
+ readonly startFrame: 284;
42
+ readonly endFrame: 314;
42
43
  };
43
- readonly matched: {
44
+ readonly holding: {
44
45
  readonly startFrame: 318;
45
- readonly endFrame: 380;
46
+ readonly endFrame: 365;
46
47
  };
47
48
  };
48
49
  };
50
+ /**
51
+ * Share of a turn's progress taken by the turn toward the pose; the hold at
52
+ * the pose takes the rest. Matches the native `TURN_SHARE_OF_OVAL`.
53
+ */
54
+ export declare const ENHANCED_SMART_SELFIE_TURN_SHARE = 0.8;
49
55
  /** Total frame count of the all-directions composition. */
50
56
  export declare const ENHANCED_SMART_SELFIE_TOTAL_FRAMES = 419;
51
57
  /** Composition frame rate (frames per second). */
52
58
  export declare const ENHANCED_SMART_SELFIE_FRAME_RATE = 30;
53
59
  /**
54
- * Playback rate for the ESS cues. At native speed the arrows read as
55
- * hectic while the user mirrors the turn; 0.6× lets the motion be
56
- * followed.
60
+ * Playback rate for the ESS demo loop. At native speed the arrows read as
61
+ * hectic; 0.6× lets the motion be followed.
57
62
  */
58
63
  export declare const ENHANCED_SMART_SELFIE_ANIMATION_SPEED = 0.6;
59
64
  /**
60
- * Resolve the keyframe span for [direction] — the green `matched` span
61
- * while [poseMatched], otherwise the grey `pending` span. Pure, so the
62
- * pending↔matched contract is unit-testable without rendering.
65
+ * The keyframe that shows [turnProgress] of a turn toward [direction]. The
66
+ * turn toward the pose, the first {@link ENHANCED_SMART_SELFIE_TURN_SHARE} of
67
+ * the progress, scrubs the grey turning span; the hold at the pose, the rest,
68
+ * scrubs the green holding span. Pure, and the same in every SDK.
63
69
  */
64
- export declare function enhancedSmartSelfieFrameRange(direction: ScanHeadTurnDirection, poseMatched: boolean): EnhancedSmartSelfieFrameRange;
70
+ export declare function enhancedSmartSelfieTurnFrame(direction: ScanHeadTurnDirection, turnProgress: number): number;
@@ -1 +1 @@
1
- "use strict";export const EnhancedSmartSelfieAnimationFrames={left:{pending:{startFrame:1,endFrame:41},matched:{startFrame:42,endFrame:106}},right:{pending:{startFrame:139,endFrame:179},matched:{startFrame:180,endFrame:241}},up:{pending:{startFrame:277,endFrame:317},matched:{startFrame:318,endFrame:380}}},ENHANCED_SMART_SELFIE_TOTAL_FRAMES=419,ENHANCED_SMART_SELFIE_FRAME_RATE=30,ENHANCED_SMART_SELFIE_ANIMATION_SPEED=.6;export function enhancedSmartSelfieFrameRange(t,a){const e=EnhancedSmartSelfieAnimationFrames[t];return a?e.matched:e.pending}
1
+ "use strict";export const EnhancedSmartSelfieAnimationFrames={left:{turning:{startFrame:8,endFrame:38},holding:{startFrame:42,endFrame:90}},right:{turning:{startFrame:146,endFrame:176},holding:{startFrame:180,endFrame:225}},up:{turning:{startFrame:284,endFrame:314},holding:{startFrame:318,endFrame:365}}},ENHANCED_SMART_SELFIE_TURN_SHARE=.8,ENHANCED_SMART_SELFIE_TOTAL_FRAMES=419,ENHANCED_SMART_SELFIE_FRAME_RATE=30,ENHANCED_SMART_SELFIE_ANIMATION_SPEED=.6;export function enhancedSmartSelfieTurnFrame(e,a){const{turning:o,holding:s}=EnhancedSmartSelfieAnimationFrames[e],r=Math.min(Math.max(a,0),1),t=ENHANCED_SMART_SELFIE_TURN_SHARE,n=r<t?o:s,E=r<t?r/t:(r-t)/(1-t);return n.startFrame+(n.endFrame-n.startFrame)*E}
@@ -1 +1 @@
1
- "use strict";import{useEffect as o,useState as d}from"react";import{Image as i}from"react-native";export const SelfieInteriorCrop={borderFraction:.15,coverScale(t,e){if(t.width<=0||t.height<=0||e.width<=0||e.height<=0)return 1;const n=SelfieInteriorCrop.borderFraction*Math.min(t.width,t.height);return Math.max(e.width/(t.width-2*n),e.height/(t.height-2*n))},unknownSizeRect(t){const e=1/(1-2*SelfieInteriorCrop.borderFraction),n=t.width*e,r=t.height*e;return{x:(t.width-n)/2,y:(t.height-r)/2,width:n,height:r}}};export function decodeUprightImageSize(t){return new Promise(e=>{if(typeof i.getSize!="function"){e(null);return}i.getSize(t,(n,r)=>e(n>0&&r>0?{width:n,height:r}:null),()=>e(null))})}export function useImageSize(t){const[e,n]=d({uri:t,result:{kind:"pending"}});return o(()=>{if(!t)return;let r=!0;return decodeUprightImageSize(t).then(h=>{r&&n({uri:t,result:h?{kind:"read",size:h}:{kind:"unreadable"}})}),()=>{r=!1}},[t]),e.uri===t?e.result:{kind:"pending"}}
1
+ "use strict";import{useEffect as o,useState as d}from"react";import{Image as h}from"react-native";import{capturedImageUri as u}from"./captured_image_uri";export const SelfieInteriorCrop={borderFraction:.15,coverScale(t,e){if(t.width<=0||t.height<=0||e.width<=0||e.height<=0)return 1;const n=SelfieInteriorCrop.borderFraction*Math.min(t.width,t.height);return Math.max(e.width/(t.width-2*n),e.height/(t.height-2*n))},unknownSizeRect(t){const e=1/(1-2*SelfieInteriorCrop.borderFraction),n=t.width*e,r=t.height*e;return{x:(t.width-n)/2,y:(t.height-r)/2,width:n,height:r}}};export function decodeUprightImageSize(t){return new Promise(e=>{if(typeof h.getSize!="function"){e(null);return}h.getSize(u(t),(n,r)=>e(n>0&&r>0?{width:n,height:r}:null),()=>e(null))})}export function useImageSize(t){const[e,n]=d({uri:t,result:{kind:"pending"}});return o(()=>{if(!t)return;let r=!0;return decodeUprightImageSize(t).then(i=>{r&&n({uri:t,result:i?{kind:"read",size:i}:{kind:"unreadable"}})}),()=>{r=!1}},[t]),e.uri===t?e.result:{kind:"pending"}}
@@ -13,8 +13,6 @@ export type UseSmileIDCaptureContentState = {
13
13
  readonly enableEnhancedLiveness: boolean;
14
14
  /** Active ESS head-turn direction, null outside a turn. */
15
15
  readonly animationDirection: ScanHeadTurnDirection | null;
16
- /** `true` while the head pose holds {@link animationDirection} — switches the cue grey→green. */
17
- readonly poseMatched: boolean;
18
16
  /** Settled (debounced) si_* feedback key; null hides the feedback bubble. */
19
17
  readonly feedbackKey: SiKey | null;
20
18
  /** Show the Start Capture control (pre-flight instruction state only). */
@@ -38,6 +36,11 @@ export type UseSmileIDCaptureContentProps = {
38
36
  readonly state: UseSmileIDCaptureContentState;
39
37
  /** 0..1 — bottom-to-top oval fill; always 0 under Enhanced Smart Selfie. */
40
38
  readonly captureProgress: number;
39
+ /**
40
+ * 0..1 through the active ESS head turn, in 1/40 steps; scrubs the direction
41
+ * arrow. Kept off the state snapshot, like {@link captureProgress}.
42
+ */
43
+ readonly turnProgress?: number;
41
44
  /** Camera preview slot — previews/tests inject an inert placeholder. */
42
45
  readonly cameraPreview: ReactNode;
43
46
  readonly onStartCapture: () => void;
@@ -1 +1 @@
1
- "use strict";import{jsx as i,Fragment as v,jsxs as c}from"react/jsx-runtime";import{memo as B,useState as A}from"react";import{Pressable as L,StyleSheet as T,Text as _,View as f}from"react-native";import{SafeAreaView as g}from"react-native-safe-area-context";import{CaptureType as U}from"../../core/models/ml_config";import{AttributionResolver as x}from"../flow/config/attribution_resolver";import{defaultLightTheme as b}from"../theme/theme";import{useUseSmileIDTheme as E}from"../theme/theme_context";import{isDocumentCaptureActive as y,UseSmileIDOverlayState as M}from"../state/use_smile_id_overlay_state";import{SiOutlineClose as K}from"./icons";import{useUseSmileIDLocalizations as F}from"../../l10n/use_smile_id_localizations_provider";import{resolveFeedbackCopy as H}from"../screen/helpers/capture_screen_helpers";import{ATTRIBUTION_TEST_ID as R,UseSmileIDAttribution as W}from"./use_smile_id_attribution";import{UseSmileIDBackButton as j}from"./use_smile_id_back_button";import{UseSmileIDButton as G}from"./use_smile_id_button";import{UseSmileIDDocumentOverlay as O,resolveDocumentSide as z}from"./use_smile_id_document_overlay";import{UseSmileIDDocumentCaptureControls as P}from"./use_smile_id_document_capture_controls";import{UseSmileIDFaceOverlay as V}from"./use_smile_id_face_overlay";import{UseSmileIDCaptureInstructionAnimation as N}from"./use_smile_id_capture_instruction_animation";import{enhancedSmartSelfieInstructionAnimationKey as $}from"./helpers/capture_instruction_animation_spec";import{UseSmileIDEnhancedSmartSelfieDirectionAnimation as q}from"./use_smile_id_enhanced_smart_selfie_direction_animation";import{UseSmileIDEnhancedSmartSelfieFullAnimation as J}from"./use_smile_id_enhanced_smart_selfie_full_animation";import{FaceOvalDefaults as Q}from"./face_oval_defaults";const S=()=>{};function X(t){const C=E(),n=t.theme??C,a=F(),{state:e}=t,[o,D]=A({width:0,height:0}),I=m=>{const{width:d,height:u}=m.nativeEvent.layout;D(h=>h.width===d&&h.height===u?h:{width:d,height:u}),t.onViewportLayout?.({width:d,height:u})},r=e.captureType===U.selfie,w=x.resolveForCapture({captureType:e.captureType,showAttribution:t.showAttribution}),k=!r&&o.width>o.height,s=e.feedbackKey?H(e.feedbackKey,a):null,p=t.startButton;return c(f,{style:[l.root,{backgroundColor:n.colors.offBlack}],testID:"si_capture_screen",onLayout:I,children:[t.cameraPreview,o.width>0&&o.height>0?c(v,{children:[r?i(V,{state:e.overlayState,progress:t.captureProgress,containerWidth:o.width,containerHeight:o.height,enableEnhancedLiveness:e.enableEnhancedLiveness,theme:n}):i(O,{state:e.overlayState,containerWidth:o.width,containerHeight:o.height,theme:n,documentSide:z(e.documentSide,e.isBackSide),...e.documentTypeKind!==void 0?{documentTypeKind:e.documentTypeKind}:{},...e.knownIdAspectRatio!==void 0?{knownIdAspectRatio:e.knownIdAspectRatio}:{},...s!==null?{feedbackText:s}:{}}),r?i(N,{feedbackKey:e.enableEnhancedLiveness?$(e.feedbackKey):e.feedbackKey,containerWidth:o.width,containerHeight:o.height}):null,r&&e.enableEnhancedLiveness?e.animationDirection!==null?i(q,{direction:e.animationDirection,poseMatched:e.poseMatched,containerWidth:o.width,containerHeight:o.height}):e.overlayState===M.IDLE?i(J,{containerWidth:o.width,containerHeight:o.height}):null:null,r?null:i(P,{showManualShutter:(e.showManualShutter??!1)&&t.onManualShutter!==void 0&&!y(e.overlayState),showGallery:(e.showGalleryUpload??!1)&&t.onGalleryUpload!==void 0&&!y(e.overlayState),isLandscape:o.width>o.height,enabled:t.documentControlsEnabled??!0,onManualShutter:t.onManualShutter??S,onGallery:t.onGalleryUpload??S,shutterAccessibilityLabel:a.siCdDocumentCaptureShutter,galleryAccessibilityLabel:a.siCdDocumentGallery,closeAccessibilityLabel:a.siCdClose,onClose:t.onClose})]}):null,c(g,{edges:["top","left","right"],style:[l.topBar,{paddingHorizontal:n.spacing.medium,paddingTop:n.spacing.medium}],pointerEvents:"box-none",children:[t.onBack?i(j,{onClick:t.onBack,forceLightMode:!0,theme:n,testID:"si_capture_back_button"}):i(f,{style:l.chromeSpacer}),t.onClose&&!k?i(L,{onPress:t.onClose,accessibilityRole:"button",accessibilityLabel:a.siCdClose,testID:"si_capture_close_button",style:({pressed:m})=>[l.closeButton,{backgroundColor:b.colors.offBlack,borderColor:b.colors.backButtonBorder,opacity:m?.7:1}],children:i(K,{size:20,color:b.colors.offWhite})}):null]}),s!==null&&r?i(f,{style:[l.feedbackBubble,{paddingHorizontal:n.spacing.medium,paddingVertical:n.spacing.small}],pointerEvents:"none",children:i(_,{style:{...n.typography.cardTitle,color:n.colors.primary,textAlign:"center"},testID:"si_capture_feedback_text",children:s})}):null,c(g,{edges:["bottom","left","right"],style:[l.bottom,{paddingHorizontal:n.spacing.medium,paddingBottom:n.spacing.large,gap:n.spacing.small}],pointerEvents:"box-none",children:[e.showCaptureButton?p?p({onClick:t.onStartCapture,enabled:t.startCaptureEnabled}):i(G,{label:a.siCaptureStartButton,onClick:t.onStartCapture,variant:"primary",enabled:t.startCaptureEnabled,forceLightMode:!0,theme:n,testID:"si_capture_start_button"}):null,w?i(W,{forceLightMode:!0,theme:n,testID:R}):null]})]})}export const UseSmileIDCaptureContent=B(X);const l=T.create({root:{flex:1},topBar:{position:"absolute",top:0,start:0,end:0,flexDirection:"row",justifyContent:"space-between",alignItems:"flex-start"},closeButton:{width:40,height:40,borderRadius:20,borderWidth:1,alignItems:"center",justifyContent:"center"},chromeSpacer:{width:40,height:40},feedbackBubble:{position:"absolute",top:`${Math.round(Q.feedbackTopRatio*100)}%`,alignSelf:"center",maxWidth:"85%"},bottom:{position:"absolute",bottom:0,start:0,end:0,alignItems:"center"}});
1
+ "use strict";import{jsx as i,Fragment as v,jsxs as c}from"react/jsx-runtime";import{memo as B,useState as A}from"react";import{Pressable as L,StyleSheet as T,Text as _,View as f}from"react-native";import{SafeAreaView as g}from"react-native-safe-area-context";import{CaptureType as U}from"../../core/models/ml_config";import{AttributionResolver as x}from"../flow/config/attribution_resolver";import{defaultLightTheme as b}from"../theme/theme";import{useUseSmileIDTheme as E}from"../theme/theme_context";import{isDocumentCaptureActive as y,UseSmileIDOverlayState as K}from"../state/use_smile_id_overlay_state";import{SiOutlineClose as M}from"./icons";import{useUseSmileIDLocalizations as F}from"../../l10n/use_smile_id_localizations_provider";import{resolveFeedbackCopy as H}from"../screen/helpers/capture_screen_helpers";import{ATTRIBUTION_TEST_ID as R,UseSmileIDAttribution as W}from"./use_smile_id_attribution";import{UseSmileIDBackButton as j}from"./use_smile_id_back_button";import{UseSmileIDButton as G}from"./use_smile_id_button";import{UseSmileIDDocumentOverlay as O,resolveDocumentSide as P}from"./use_smile_id_document_overlay";import{UseSmileIDDocumentCaptureControls as z}from"./use_smile_id_document_capture_controls";import{UseSmileIDFaceOverlay as V}from"./use_smile_id_face_overlay";import{UseSmileIDCaptureInstructionAnimation as N}from"./use_smile_id_capture_instruction_animation";import{enhancedSmartSelfieInstructionAnimationKey as $}from"./helpers/capture_instruction_animation_spec";import{UseSmileIDEnhancedSmartSelfieDirectionAnimation as q}from"./use_smile_id_enhanced_smart_selfie_direction_animation";import{UseSmileIDEnhancedSmartSelfieFullAnimation as J}from"./use_smile_id_enhanced_smart_selfie_full_animation";import{FaceOvalDefaults as Q}from"./face_oval_defaults";const S=()=>{};function X(t){const C=E(),n=t.theme??C,a=F(),{state:e}=t,[o,D]=A({width:0,height:0}),I=m=>{const{width:d,height:u}=m.nativeEvent.layout;D(h=>h.width===d&&h.height===u?h:{width:d,height:u}),t.onViewportLayout?.({width:d,height:u})},r=e.captureType===U.selfie,w=x.resolveForCapture({captureType:e.captureType,showAttribution:t.showAttribution}),k=!r&&o.width>o.height,s=e.feedbackKey?H(e.feedbackKey,a):null,p=t.startButton;return c(f,{style:[l.root,{backgroundColor:n.colors.offBlack}],testID:"si_capture_screen",onLayout:I,children:[t.cameraPreview,o.width>0&&o.height>0?c(v,{children:[r?i(V,{state:e.overlayState,progress:t.captureProgress,containerWidth:o.width,containerHeight:o.height,enableEnhancedLiveness:e.enableEnhancedLiveness,theme:n}):i(O,{state:e.overlayState,containerWidth:o.width,containerHeight:o.height,theme:n,documentSide:P(e.documentSide,e.isBackSide),...e.documentTypeKind!==void 0?{documentTypeKind:e.documentTypeKind}:{},...e.knownIdAspectRatio!==void 0?{knownIdAspectRatio:e.knownIdAspectRatio}:{},...s!==null?{feedbackText:s}:{}}),r?i(N,{feedbackKey:e.enableEnhancedLiveness?$(e.feedbackKey):e.feedbackKey,containerWidth:o.width,containerHeight:o.height}):null,r&&e.enableEnhancedLiveness?e.animationDirection!==null?i(q,{direction:e.animationDirection,turnProgress:t.turnProgress??0,containerWidth:o.width,containerHeight:o.height}):e.overlayState===K.IDLE?i(J,{containerWidth:o.width,containerHeight:o.height}):null:null,r?null:i(z,{showManualShutter:(e.showManualShutter??!1)&&t.onManualShutter!==void 0&&!y(e.overlayState),showGallery:(e.showGalleryUpload??!1)&&t.onGalleryUpload!==void 0&&!y(e.overlayState),isLandscape:o.width>o.height,enabled:t.documentControlsEnabled??!0,onManualShutter:t.onManualShutter??S,onGallery:t.onGalleryUpload??S,shutterAccessibilityLabel:a.siCdDocumentCaptureShutter,galleryAccessibilityLabel:a.siCdDocumentGallery,closeAccessibilityLabel:a.siCdClose,onClose:t.onClose})]}):null,c(g,{edges:["top","left","right"],style:[l.topBar,{paddingHorizontal:n.spacing.medium,paddingTop:n.spacing.medium}],pointerEvents:"box-none",children:[t.onBack?i(j,{onClick:t.onBack,forceLightMode:!0,theme:n,testID:"si_capture_back_button"}):i(f,{style:l.chromeSpacer}),t.onClose&&!k?i(L,{onPress:t.onClose,accessibilityRole:"button",accessibilityLabel:a.siCdClose,testID:"si_capture_close_button",style:({pressed:m})=>[l.closeButton,{backgroundColor:b.colors.offBlack,borderColor:b.colors.backButtonBorder,opacity:m?.7:1}],children:i(M,{size:20,color:b.colors.offWhite})}):null]}),s!==null&&r?i(f,{style:[l.feedbackBubble,{paddingHorizontal:n.spacing.medium,paddingVertical:n.spacing.small}],pointerEvents:"none",children:i(_,{style:{...n.typography.cardTitle,color:n.colors.primary,textAlign:"center"},testID:"si_capture_feedback_text",children:s})}):null,c(g,{edges:["bottom","left","right"],style:[l.bottom,{paddingHorizontal:n.spacing.medium,paddingBottom:n.spacing.large,gap:n.spacing.small}],pointerEvents:"box-none",children:[e.showCaptureButton?p?p({onClick:t.onStartCapture,enabled:t.startCaptureEnabled}):i(G,{label:a.siCaptureStartButton,onClick:t.onStartCapture,variant:"primary",enabled:t.startCaptureEnabled,forceLightMode:!0,theme:n,testID:"si_capture_start_button"}):null,w?i(W,{forceLightMode:!0,theme:n,testID:R}):null]})]})}export const UseSmileIDCaptureContent=B(X);const l=T.create({root:{flex:1},topBar:{position:"absolute",top:0,start:0,end:0,flexDirection:"row",justifyContent:"space-between",alignItems:"flex-start"},closeButton:{width:40,height:40,borderRadius:20,borderWidth:1,alignItems:"center",justifyContent:"center"},chromeSpacer:{width:40,height:40},feedbackBubble:{position:"absolute",top:`${Math.round(Q.feedbackTopRatio*100)}%`,alignSelf:"center",maxWidth:"85%"},bottom:{position:"absolute",bottom:0,start:0,end:0,alignItems:"center"}});
@@ -3,7 +3,10 @@ import type { UseSmileIDTheme } from "../theme/theme";
3
3
  export type UseSmileIDCapturedDocumentImageProps = {
4
4
  /** Local file URI (`file:///…`) of the captured document side. */
5
5
  readonly uri?: string;
6
- /** Capture-frame aspect ratio (width / height) the box keeps while fitting the region. */
6
+ /**
7
+ * Capture-frame aspect ratio (width / height) the box keeps while fitting the region; a document
8
+ * saved with no border keeps its image's own instead.
9
+ */
7
10
  readonly aspectRatio: number;
8
11
  /**
9
12
  * The hidden border `uri` was saved with, as a fraction of its short side on every edge. The
@@ -30,8 +33,9 @@ export type UseSmileIDCapturedDocumentImageProps = {
30
33
  readonly theme?: UseSmileIDTheme;
31
34
  };
32
35
  /**
33
- * Captured document image at its capture aspect, clipped to an 8dp rounded rectangle and sized
34
- * to the largest box that fits the padded region — the rectangular counterpart to
35
- * {@link UseSmileIDCapturedImageOval}. Shared by the document preview and the processing screen.
36
+ * Captured document image at its capture aspect, or a document saved with no border at its own,
37
+ * clipped to an 8dp rounded rectangle and sized to the largest box that fits the padded region —
38
+ * the rectangular counterpart to {@link UseSmileIDCapturedImageOval}. Shared by the document
39
+ * preview and the processing screen.
36
40
  */
37
41
  export declare function UseSmileIDCapturedDocumentImage(props: UseSmileIDCapturedDocumentImageProps): import("react").JSX.Element;
@@ -1 +1 @@
1
- "use strict";import{jsx as l,jsxs as L}from"react/jsx-runtime";import{useState as b}from"react";import{Image as j,StyleSheet as f,View as c}from"react-native";import{useUseSmileIDTheme as E}from"../theme/theme_context";import{fitCapturedDocumentImage as F}from"./helpers/captured_document_image_fit";import{DocumentInteriorCrop as B}from"./helpers/document_interior_crop";import{useImageSize as O}from"./helpers/selfie_interior_crop";import{useDocumentPreviewOverlayEnabled as k}from"../../debug/use_smile_id_debug";import{DocumentPreviewHiddenRegionsInPlace as A}from"../../debug/use_smile_id_document_preview_debug_overlay";const I=8;export function UseSmileIDCapturedDocumentImage(e){const w=E(),x=e.theme??w,u=e.aspectRatio,g=x.spacing.large,[h,v]=b({width:0,height:0}),D=d=>{const{width:i,height:o}=d.nativeEvent.layout;v(t=>t.width===i&&t.height===o?t:{width:i,height:o})},R=F(u,h.width-2*g,h.height)??{...n.fillWidth,aspectRatio:u},a=e.borderFraction??0,y=k()&&e.showDebugOverlay===!0,r=O(a>0||y?e.uri:void 0),[s,S]=b(null),C=d=>{const{width:i,height:o}=d.nativeEvent.layout;S(t=>t?.width===i&&t.height===o?t:{width:i,height:o}),e.onCardLayout?.({width:i,height:o})},m=a>0&&s!==null&&r.kind==="read"?B.imageRect(r.size,s,a):null,z=!!e.uri&&(a<=0||m!==null||r.kind==="unreadable");return l(c,{style:[n.region,{paddingHorizontal:g}],onLayout:D,testID:e.testID,children:L(c,{style:[n.imageBox,R],onLayout:C,children:[z?l(j,{source:{uri:e.uri},resizeMode:"cover",style:m?[n.interiorImage,m]:n.image,testID:e.imageTestID,...e.imageAccessibilityLabel?{accessible:!0,accessibilityRole:"image",accessibilityLabel:e.imageAccessibilityLabel}:{}}):null,y&&s!==null&&r.kind==="read"?l(c,{pointerEvents:"none",style:f.absoluteFill,children:l(A,{imageSize:r.size,cardSize:s,borderFraction:a})}):null,e.overlayChild?l(c,{pointerEvents:"box-none",style:n.overlay,children:e.overlayChild}):null]})})}const n=f.create({region:{flex:1,alignItems:"center",justifyContent:"center"},imageBox:{borderRadius:I,overflow:"hidden"},fillWidth:{width:"100%"},image:{width:"100%",height:"100%",borderRadius:I},interiorImage:{position:"absolute"},overlay:{position:"absolute",top:0,bottom:0,start:0,end:0,alignItems:"center",justifyContent:"center"}});
1
+ "use strict";import{jsx as l,jsxs as E}from"react/jsx-runtime";import{useState as b}from"react";import{Image as k,StyleSheet as f,View as c}from"react-native";import{useUseSmileIDTheme as F}from"../theme/theme_context";import{fitCapturedDocumentImage as A}from"./helpers/captured_document_image_fit";import{DocumentInteriorCrop as I}from"./helpers/document_interior_crop";import{useImageSize as B}from"./helpers/selfie_interior_crop";import{useDocumentPreviewOverlayEnabled as O}from"../../debug/use_smile_id_debug";import{DocumentPreviewHiddenRegionsInPlace as U}from"../../debug/use_smile_id_document_preview_debug_overlay";import{capturedImageUri as P}from"./helpers/captured_image_uri";const w=8;export function UseSmileIDCapturedDocumentImage(e){const d=e.uri===void 0?void 0:P(e.uri),x=F(),R=e.theme??x,n=e.borderFraction??0,t=B(d),g=I.cardAspectRatio(t.kind==="read"?t.size:null,e.aspectRatio,n),h=R.spacing.large,[y,v]=b({width:0,height:0}),D=u=>{const{width:o,height:a}=u.nativeEvent.layout;v(i=>i.width===o&&i.height===a?i:{width:o,height:a})},S=A(g,y.width-2*h,y.height)??{...r.fillWidth,aspectRatio:g},z=O()&&e.showDebugOverlay===!0,[s,C]=b(null),L=u=>{const{width:o,height:a}=u.nativeEvent.layout;C(i=>i?.width===o&&i.height===a?i:{width:o,height:a}),e.onCardLayout?.({width:o,height:a})},m=n>0&&s!==null&&t.kind==="read"?I.imageRect(t.size,s,n):null,j=!!d&&(m!==null||t.kind==="unreadable"||n<=0&&t.kind==="read");return l(c,{style:[r.region,{paddingHorizontal:h}],onLayout:D,testID:e.testID,children:E(c,{style:[r.imageBox,S],onLayout:L,children:[j?l(k,{source:{uri:d},resizeMode:n>0?"cover":"contain",style:m?[r.interiorImage,m]:r.image,testID:e.imageTestID,...e.imageAccessibilityLabel?{accessible:!0,accessibilityRole:"image",accessibilityLabel:e.imageAccessibilityLabel}:{}}):null,z&&s!==null&&t.kind==="read"?l(c,{pointerEvents:"none",style:f.absoluteFill,children:l(U,{imageSize:t.size,cardSize:s,borderFraction:n})}):null,e.overlayChild?l(c,{pointerEvents:"box-none",style:r.overlay,children:e.overlayChild}):null]})})}const r=f.create({region:{flex:1,alignItems:"center",justifyContent:"center"},imageBox:{borderRadius:w,overflow:"hidden"},fillWidth:{width:"100%"},image:{width:"100%",height:"100%",borderRadius:w},interiorImage:{position:"absolute"},overlay:{position:"absolute",top:0,bottom:0,start:0,end:0,alignItems:"center",justifyContent:"center"}});
@@ -1,5 +1,6 @@
1
1
  import { type ReactNode } from "react";
2
2
  import type { UseSmileIDTheme } from "../theme/theme";
3
+ import { type SelfieCaptureGeometry } from "./helpers/captured_oval_region";
3
4
  export type UseSmileIDCapturedImageOvalProps = {
4
5
  /**
5
6
  * Full screen width. The oval proportions are sized against the
@@ -29,6 +30,11 @@ export type UseSmileIDCapturedImageOvalProps = {
29
30
  * Only the preview screen sets it; inert in release builds.
30
31
  */
31
32
  readonly showDebugOverlay?: boolean;
33
+ /**
34
+ * @internal What the flow's selfie capture screen laid out; omitted maps through this screen's
35
+ * size and the 4:3 selfie stream.
36
+ */
37
+ readonly captureGeometry?: SelfieCaptureGeometry;
32
38
  readonly testID?: string;
33
39
  readonly theme?: UseSmileIDTheme;
34
40
  };
@@ -1 +1 @@
1
- "use strict";import{jsx as i,jsxs as v}from"react/jsx-runtime";import{useId as I,useState as D}from"react";import{StyleSheet as H,View as s}from"react-native";import C,{ClipPath as R,Defs as W,Ellipse as z,Image as E}from"react-native-svg";import{useSelfiePreviewOverlayEnabled as L}from"../../debug/use_smile_id_debug";import{UseSmileIDSelfiePreviewDebugOverlay as O}from"../../debug/use_smile_id_selfie_preview_debug_overlay";import{useUseSmileIDTheme as j}from"../theme/theme_context";import{computeScaledOvalGeometry as _}from"./helpers/scaled_oval_geometry";import{SelfieInteriorCrop as y,useImageSize as P}from"./helpers/selfie_interior_crop";export function UseSmileIDCapturedImageOval(e){const f=j(),b=e.theme??f,[c,x]=D(0),g=`si_oval_clip_${I().replace(/:/g,"")}`,S=L(),o=P(e.uri),w=l=>{x(l.nativeEvent.layout.height)},t=e.screenWidth>0&&e.screenHeight>0&&c>0?_(e.screenWidth,e.screenHeight,c,b.spacing.medium):null,a=t!==null&&t.scale>0,n=(()=>{if(!t||o.kind==="pending")return null;const l={width:t.ovalWidth,height:t.ovalHeight};if(o.kind==="unreadable")return y.unknownSizeRect(l);const m=y.coverScale(o.size,l),d=o.size.width*m,u=o.size.height*m;return{x:(l.width-d)/2,y:(l.height-u)/2,width:d,height:u}})(),r=t?{width:t.ovalWidth,height:t.ovalHeight,left:t.ovalLeft,top:t.ovalTop}:{width:0,height:0,left:0,top:0};return v(s,{style:h.region,onLayout:w,testID:e.testID,children:[e.uri?i(s,{pointerEvents:"none",style:[h.oval,r],testID:e.imageTestID,...e.imageAccessibilityLabel?{accessible:!0,accessibilityRole:"image",accessibilityLabel:e.imageAccessibilityLabel}:{},children:a&&t&&n?v(C,{width:"100%",height:"100%",viewBox:`0 0 ${t.ovalWidth} ${t.ovalHeight}`,children:[i(W,{children:i(R,{id:g,children:i(z,{cx:t.ovalWidth/2,cy:t.ovalHeight/2,rx:t.ovalWidth/2,ry:t.ovalHeight/2})})}),i(E,{href:{uri:e.uri},x:n.x,y:n.y,width:n.width,height:n.height,preserveAspectRatio:"xMidYMid slice",clipPath:`url(#${g})`})]}):null}):null,e.overlayChild&&a?i(s,{pointerEvents:"box-none",style:[h.overlay,r],children:e.overlayChild}):null,e.showDebugOverlay&&S&&e.uri&&a?i(O,{uri:e.uri,ovalRect:r}):null]})}const h=H.create({region:{flex:1},oval:{position:"absolute"},overlay:{position:"absolute",alignItems:"center",justifyContent:"center"}});
1
+ "use strict";import{jsx as i,jsxs as u}from"react/jsx-runtime";import{useId as x,useState as S}from"react";import{StyleSheet as w,View as s}from"react-native";import D,{ClipPath as H,Defs as R,Ellipse as W,Image as C}from"react-native-svg";import{useSelfiePreviewOverlayEnabled as O}from"../../debug/use_smile_id_debug";import{UseSmileIDSelfiePreviewDebugOverlay as p}from"../../debug/use_smile_id_selfie_preview_debug_overlay";import{useUseSmileIDTheme as E}from"../theme/theme_context";import{computeScaledOvalGeometry as L}from"./helpers/scaled_oval_geometry";import{SelfieInteriorCrop as j,useImageSize as _}from"./helpers/selfie_interior_crop";import{capturedImageUri as z}from"./helpers/captured_image_uri";import{capturedOvalRegion as A}from"./helpers/captured_oval_region";export function UseSmileIDCapturedImageOval(e){const l=e.uri===void 0?void 0:z(e.uri),v=E(),y=e.theme??v,[m,f]=S(0),d=`si_oval_clip_${x().replace(/:/g,"")}`,b=O(),a=_(l),I=n=>{f(n.nativeEvent.layout.height)},t=e.screenWidth>0&&e.screenHeight>0&&m>0?L(e.screenWidth,e.screenHeight,m,y.spacing.medium):null,r=t!==null&&t.scale>0,g=A(e.captureGeometry?.viewport??{width:e.screenWidth,height:e.screenHeight},e.captureGeometry?.frameAspect),o=(()=>{if(!t||a.kind==="pending")return null;const n={width:t.ovalWidth,height:t.ovalHeight};return a.kind==="unreadable"?j.unknownSizeRect(n):g.imageRect(a.size,n)})(),c=t?{width:t.ovalWidth,height:t.ovalHeight,left:t.ovalLeft,top:t.ovalTop}:{width:0,height:0,left:0,top:0};return u(s,{style:h.region,onLayout:I,testID:e.testID,children:[l?i(s,{pointerEvents:"none",style:[h.oval,c],testID:e.imageTestID,...e.imageAccessibilityLabel?{accessible:!0,accessibilityRole:"image",accessibilityLabel:e.imageAccessibilityLabel}:{},children:r&&t&&o?u(D,{width:"100%",height:"100%",viewBox:`0 0 ${t.ovalWidth} ${t.ovalHeight}`,children:[i(R,{children:i(H,{id:d,children:i(W,{cx:t.ovalWidth/2,cy:t.ovalHeight/2,rx:t.ovalWidth/2,ry:t.ovalHeight/2})})}),i(C,{href:{uri:l},x:o.x,y:o.y,width:o.width,height:o.height,preserveAspectRatio:"xMidYMid slice",clipPath:`url(#${d})`})]}):null}):null,e.overlayChild&&r?i(s,{pointerEvents:"box-none",style:[h.overlay,c],children:e.overlayChild}):null,e.showDebugOverlay&&b&&l&&r?i(p,{uri:l,ovalRect:c,placement:g}):null]})}const h=w.create({region:{flex:1},oval:{position:"absolute"},overlay:{position:"absolute",alignItems:"center",justifyContent:"center"}});
@@ -7,20 +7,30 @@ export declare const ESS_ALL_DIRECTIONS_ANIMATION: AnimationObject;
7
7
  export declare const ENHANCED_SMART_SELFIE_ARROW_FIT = 1.15;
8
8
  /** Nudge so the composition's face lands on the oval centre. */
9
9
  export declare const VERTICAL_ALIGNMENT_OFFSET = -15;
10
+ /**
11
+ * The square box both ESS cues draw in, centred on the face oval: the oval's
12
+ * width times {@link ENHANCED_SMART_SELFIE_ARROW_FIT}, nudged by
13
+ * {@link VERTICAL_ALIGNMENT_OFFSET}. One source, so the demo and the
14
+ * direction arrow always line up.
15
+ */
16
+ export declare function essAnimationBoxStyle(containerWidth: number, containerHeight: number): {
17
+ position: "absolute";
18
+ left: number;
19
+ top: number;
20
+ width: number;
21
+ height: number;
22
+ };
10
23
  export type UseSmileIDEnhancedSmartSelfieClipProps = {
11
- readonly startFrame: number;
12
24
  readonly endFrame: number;
13
- /** Play forward-then-back instead of restarting from the first frame. */
14
- readonly pingPong: boolean;
15
25
  readonly containerWidth: number;
16
26
  readonly containerHeight: number;
17
27
  readonly testID: string;
18
28
  };
19
29
  /**
20
- * Shared renderer for the Enhanced Smart Selfie cues — plays a frame
21
- * sub-range of the all-directions composition in a loop, centred on
22
- * the face oval. Reduced motion holds the clip's final frame.
30
+ * Renderer for the Enhanced Smart Selfie demo — loops the all-directions
31
+ * composition, centred on the face oval. Reduced motion holds the clip's
32
+ * final frame.
23
33
  *
24
- * @internal Used by the full / direction ESS animation components.
34
+ * @internal Used by the full ESS animation component.
25
35
  */
26
36
  export declare function UseSmileIDEnhancedSmartSelfieClip(props: UseSmileIDEnhancedSmartSelfieClipProps): import("react").JSX.Element | null;
@@ -1 +1 @@
1
- "use strict";import{jsx as n}from"react/jsx-runtime";import{useEffect as _,useRef as m}from"react";import{View as u}from"react-native";import f from"lottie-react-native";import{useReducedMotion as I}from"../utils/use_reduced_motion";import{FaceOvalDefaults as E}from"./face_oval_defaults";import{ENHANCED_SMART_SELFIE_ANIMATION_SPEED as d,ENHANCED_SMART_SELFIE_TOTAL_FRAMES as p}from"./helpers/enhanced_smart_selfie_animation_frames";export const ESS_ALL_DIRECTIONS_ANIMATION=require("../assets/si_ess_animation_all_directions.json"),ENHANCED_SMART_SELFIE_ARROW_FIT=1.15,VERTICAL_ALIGNMENT_OFFSET=-15;export function UseSmileIDEnhancedSmartSelfieClip(t){const r=I(),{startFrame:o,endFrame:e,pingPong:i,containerWidth:s,containerHeight:a}=t;if(s<=0||a<=0)return null;const l=s*E.ovalWidthRatio*ENHANCED_SMART_SELFIE_ARROW_FIT,h=a*E.ovalCenterYRatio,c={position:"absolute",left:(s-l)/2,top:h-l/2+VERTICAL_ALIGNMENT_OFFSET,width:l,height:l};return r?n(u,{pointerEvents:"none",style:c,testID:t.testID,children:n(f,{source:ESS_ALL_DIRECTIONS_ANIMATION,progress:e/p,autoPlay:!1,loop:!1,style:{width:"100%",height:"100%"}})}):i?n(A,{startFrame:o,endFrame:e,containerStyle:c,testID:t.testID}):n(u,{pointerEvents:"none",style:c,testID:t.testID,children:n(f,{source:ESS_ALL_DIRECTIONS_ANIMATION,autoPlay:!0,loop:!0,speed:d,style:{width:"100%",height:"100%"}})})}function A(t){const{startFrame:r,endFrame:o}=t,e=m(null),i=m(!0);_(()=>{i.current=!0,e.current?.play(r,o)},[r,o]);const s=a=>{a||(i.current=!i.current,i.current?e.current?.play(r,o):e.current?.play(o,r))};return n(u,{pointerEvents:"none",style:t.containerStyle,testID:t.testID,children:n(f,{ref:e,source:ESS_ALL_DIRECTIONS_ANIMATION,loop:!1,autoPlay:!1,speed:d,onAnimationFinish:s,style:{width:"100%",height:"100%"}})})}
1
+ "use strict";import{jsx as i}from"react/jsx-runtime";import{View as l}from"react-native";import a from"lottie-react-native";import{useReducedMotion as _}from"../utils/use_reduced_motion";import{FaceOvalDefaults as E}from"./face_oval_defaults";import{ENHANCED_SMART_SELFIE_ANIMATION_SPEED as c,ENHANCED_SMART_SELFIE_TOTAL_FRAMES as u}from"./helpers/enhanced_smart_selfie_animation_frames";export const ESS_ALL_DIRECTIONS_ANIMATION=require("../assets/si_ess_animation_all_directions.json"),ENHANCED_SMART_SELFIE_ARROW_FIT=1.15,VERTICAL_ALIGNMENT_OFFSET=-15;export function essAnimationBoxStyle(t,n){const e=t*E.ovalWidthRatio*ENHANCED_SMART_SELFIE_ARROW_FIT,o=n*E.ovalCenterYRatio;return{position:"absolute",left:(t-e)/2,top:o-e/2+VERTICAL_ALIGNMENT_OFFSET,width:e,height:e}}export function UseSmileIDEnhancedSmartSelfieClip(t){const n=_(),{endFrame:e,containerWidth:o,containerHeight:r}=t;if(o<=0||r<=0)return null;const s=essAnimationBoxStyle(o,r);return n?i(l,{pointerEvents:"none",style:s,testID:t.testID,children:i(a,{source:ESS_ALL_DIRECTIONS_ANIMATION,progress:e/u,autoPlay:!1,loop:!1,style:{width:"100%",height:"100%"}})}):i(l,{pointerEvents:"none",style:s,testID:t.testID,children:i(a,{source:ESS_ALL_DIRECTIONS_ANIMATION,autoPlay:!0,loop:!0,speed:c,style:{width:"100%",height:"100%"}})})}
@@ -1,24 +1,23 @@
1
1
  import type { ScanHeadTurnDirection } from "@smileid/usesmileid_bridge";
2
+ /**
3
+ * How long the arrow takes to reach a new progress: short, so it stays with
4
+ * the head. The same as the other SDKs.
5
+ */
6
+ export declare const ENHANCED_SMART_SELFIE_TURN_FOLLOW_MS = 100;
2
7
  export type UseSmileIDEnhancedSmartSelfieDirectionAnimationProps = {
3
8
  /** Active head-turn direction from the scan state; null renders nothing. */
4
9
  readonly direction: ScanHeadTurnDirection | null;
5
- /**
6
- * `true` while the head pose holds {@link direction}. Switches the cue
7
- * from the grey "pending" keyframe span to the green "matched" span.
8
- */
9
- readonly poseMatched: boolean;
10
+ /** How much of the turn is done, 0–1; scrubs the cue. */
11
+ readonly turnProgress: number;
10
12
  readonly containerWidth: number;
11
13
  readonly containerHeight: number;
12
14
  };
13
15
  /**
14
- * Enhanced Smart Selfie head-turn cue — plays the active direction's
15
- * frame sub-range of the all-directions composition, ping-ponging until
16
- * the turn completes. Grey while prompting; the range flips to the green
17
- * "matched" span once {@link poseMatched} holds. Both the direction and
18
- * the pose-match flip switch the frame range IN PLACE on the same Lottie
19
- * view (no unmount/remount), so the cue never reinitialises mid-turn.
20
- * Reduced motion holds the clip's final (turned-head) frame. Never
21
- * mirrored in RTL so the analyzer sees identical expected head-turn
22
- * directions in every locale.
16
+ * Enhanced Smart Selfie head-turn cue, moved by the turn itself rather than
17
+ * looped: as the head turns toward {@link direction} the drawn head turns and
18
+ * the grey chevrons slide out; as it holds there the chevron turns green and
19
+ * slides further. Turning back moves it back, so the cue stays in step with
20
+ * the head. Never mirrored in RTL so the analyzer sees identical expected
21
+ * head-turn directions in every locale.
23
22
  */
24
23
  export declare function UseSmileIDEnhancedSmartSelfieDirectionAnimation(props: UseSmileIDEnhancedSmartSelfieDirectionAnimationProps): import("react").JSX.Element | null;
@@ -1 +1 @@
1
- "use strict";import{jsx as t}from"react/jsx-runtime";import{enhancedSmartSelfieFrameRange as r}from"./helpers/enhanced_smart_selfie_animation_frames";import{UseSmileIDEnhancedSmartSelfieClip as a}from"./use_smile_id_enhanced_smart_selfie_clip";export function UseSmileIDEnhancedSmartSelfieDirectionAnimation(e){if(e.direction===null)return null;const{startFrame:n,endFrame:i}=r(e.direction,e.poseMatched);return t(a,{startFrame:n,endFrame:i,pingPong:!0,containerWidth:e.containerWidth,containerHeight:e.containerHeight,testID:"si_ess_direction_animation"})}
1
+ "use strict";import{jsx as m}from"react/jsx-runtime";import{useEffect as l,useRef as c}from"react";import{Animated as u,Easing as f,View as d}from"react-native";import{AnimatedLottieView as _}from"./helpers/animated_lottie_view";import{useReducedMotion as E}from"../utils/use_reduced_motion";import{ENHANCED_SMART_SELFIE_TOTAL_FRAMES as S,enhancedSmartSelfieTurnFrame as A}from"./helpers/enhanced_smart_selfie_animation_frames";import{ESS_ALL_DIRECTIONS_ANIMATION as p,essAnimationBoxStyle as g}from"./use_smile_id_enhanced_smart_selfie_clip";export const ENHANCED_SMART_SELFIE_TURN_FOLLOW_MS=100;export function UseSmileIDEnhancedSmartSelfieDirectionAnimation(i){const{direction:n,containerWidth:r,containerHeight:o}=i,s=E(),e=n===null?0:A(n,i.turnProgress)/S,t=c(new u.Value(e)).current;return l(()=>{if(s){t.setValue(e);return}const a=u.timing(t,{toValue:e,duration:ENHANCED_SMART_SELFIE_TURN_FOLLOW_MS,easing:f.linear,useNativeDriver:!1});return a.start(),()=>a.stop()},[e,s,t]),n===null||r<=0||o<=0?null:m(d,{pointerEvents:"none",style:g(r,o),testID:"si_ess_direction_animation",children:m(_,{source:p,progress:t,autoPlay:!1,loop:!1,style:{width:"100%",height:"100%"}})})}
@@ -1 +1 @@
1
- "use strict";import{jsx as i}from"react/jsx-runtime";import{ENHANCED_SMART_SELFIE_TOTAL_FRAMES as n}from"./helpers/enhanced_smart_selfie_animation_frames";import{UseSmileIDEnhancedSmartSelfieClip as t}from"./use_smile_id_enhanced_smart_selfie_clip";export function UseSmileIDEnhancedSmartSelfieFullAnimation(e){return i(t,{startFrame:0,endFrame:n,pingPong:!1,containerWidth:e.containerWidth,containerHeight:e.containerHeight,testID:"si_ess_full_animation"})}
1
+ "use strict";import{jsx as i}from"react/jsx-runtime";import{ENHANCED_SMART_SELFIE_TOTAL_FRAMES as n}from"./helpers/enhanced_smart_selfie_animation_frames";import{UseSmileIDEnhancedSmartSelfieClip as t}from"./use_smile_id_enhanced_smart_selfie_clip";export function UseSmileIDEnhancedSmartSelfieFullAnimation(e){return i(t,{endFrame:n,containerWidth:e.containerWidth,containerHeight:e.containerHeight,testID:"si_ess_full_animation"})}
@@ -1 +1 @@
1
- "use strict";import{jsx as n,jsxs as a}from"react/jsx-runtime";import c,{ClipPath as h,Defs as s,Ellipse as m,Image as d}from"react-native-svg";import{StyleSheet as g,View as f}from"react-native";import{computeFaceOvalGeometry as u}from"./face_oval_defaults";export function UseSmileIDFaceOvalImage(e){if(e.containerWidth<=0||e.containerHeight<=0||e.uri.length===0)return null;const{ovalWidth:t,ovalHeight:i,ovalLeft:l,ovalTop:o}=u(e.containerWidth,e.containerHeight),r="si_face_oval_image_clip";return n(f,{pointerEvents:"none",style:[v.container,{width:t,height:i,left:l,top:o}],testID:e.testID,children:a(c,{width:"100%",height:"100%",viewBox:`0 0 ${t} ${i}`,children:[n(s,{children:n(h,{id:r,children:n(m,{cx:t/2,cy:i/2,rx:t/2,ry:i/2})})}),n(d,{href:{uri:e.uri},x:0,y:0,width:t,height:i,preserveAspectRatio:"xMidYMid slice",clipPath:`url(#${r})`})]})})}const v=g.create({container:{position:"absolute"}});
1
+ "use strict";import{jsx as r,jsxs as c}from"react/jsx-runtime";import h,{ClipPath as s,Defs as m,Ellipse as d,Image as g}from"react-native-svg";import{StyleSheet as f,View as u}from"react-native";import{computeFaceOvalGeometry as v}from"./face_oval_defaults";import{capturedImageUri as x}from"./helpers/captured_image_uri";export function UseSmileIDFaceOvalImage(e){const n=x(e.uri);if(e.containerWidth<=0||e.containerHeight<=0||n.length===0)return null;const{ovalWidth:t,ovalHeight:i,ovalLeft:a,ovalTop:l}=v(e.containerWidth,e.containerHeight),o="si_face_oval_image_clip";return r(u,{pointerEvents:"none",style:[p.container,{width:t,height:i,left:a,top:l}],testID:e.testID,children:c(h,{width:"100%",height:"100%",viewBox:`0 0 ${t} ${i}`,children:[r(m,{children:r(s,{id:o,children:r(d,{cx:t/2,cy:i/2,rx:t/2,ry:i/2})})}),r(g,{href:{uri:n},x:0,y:0,width:t,height:i,preserveAspectRatio:"xMidYMid slice",clipPath:`url(#${o})`})]})})}const p=f.create({container:{position:"absolute"}});
@@ -0,0 +1,14 @@
1
+ /**
2
+ * The `selfie_session_restarted` reasons this SDK builds itself, as the
3
+ * shared `analytics-contract.json` lists them. The native capture reports
4
+ * the rest. @internal
5
+ */
6
+ export declare const UnsatisfiedReasons: {
7
+ /** The reason sent when the native capture restarts without one. */
8
+ readonly fallback: "unknown";
9
+ /**
10
+ * The prefix of every reason the selfie upload check reports
11
+ * (`upload_check_` plus the problem), which the native bridge builds.
12
+ */
13
+ readonly uploadCheckPrefix: "upload_check_";
14
+ };
@@ -0,0 +1 @@
1
+ "use strict";export const UnsatisfiedReasons={fallback:"unknown",uploadCheckPrefix:"upload_check_"};
@@ -16,6 +16,8 @@ export declare const UseSmileIDAnalyticsEventTypes: {
16
16
  readonly documentCaptured: "document_captured";
17
17
  readonly retakeRequested: "retake_requested";
18
18
  readonly jobSubmitted: "job_submitted";
19
+ /** Fired instead of `jobSubmitted` when `skipApiSubmission` hands the assembled job to the partner. Extras: `job_type`. */
20
+ readonly jobCaptured: "job_captured";
19
21
  readonly flowCompleted: "flow_completed";
20
22
  /**
21
23
  * Selfie capture session restarts after Unsatisfied. Extras:
@@ -60,16 +62,15 @@ export declare const UseSmileIDAnalyticsScreenNames: {
60
62
  readonly processing: "processing";
61
63
  };
62
64
  /**
63
- * Stable wire-identifier strings for the `reason` extras key.
64
- * Partner dashboards aggregate on these — never change a value here
65
- * without bumping the wire format.
65
+ * Stable wire-identifier strings for `selfie_frame_rejected`'s `reason`
66
+ * extras key, and `qualityDropTimeout`, one of `selfie_session_restarted`'s
67
+ * (all of which the shared analytics contract lists as `restartReasons`).
68
+ * Partner dashboards aggregate on these — never change a value here without
69
+ * bumping the wire format.
66
70
  */
67
71
  export declare const UseSmileIDAnalyticsReasons: {
68
- readonly outOfFrame: "out_of_frame";
69
- readonly timeout: "timeout";
70
- readonly qualityDropTimeout: "quality_drop_timeout";
71
72
  readonly motionBlur: "motion_blur";
72
- readonly notSmiling: "not_smiling";
73
+ readonly qualityDropTimeout: "quality_drop_timeout";
73
74
  };
74
75
  /** Read an event back as a typed `UseSmileIDAnalyticsEventKind` with `analyticsEventKind(event)`. */
75
76
  export type UseSmileIDAnalyticsEvent = {
@@ -1 +1 @@
1
- "use strict";export const UseSmileIDAnalyticsEventTypes={flowStarted:"flow_started",screenViewed:"screen_viewed",consentCaptured:"consent_captured",selfieCaptured:"selfie_captured",documentCaptured:"document_captured",retakeRequested:"retake_requested",jobSubmitted:"job_submitted",flowCompleted:"flow_completed",selfieSessionRestarted:"selfie_session_restarted",selfieFrameRejected:"selfie_frame_rejected"},UseSmileIDAnalyticsParamKeys={jobType:"job_type",jobTypeId:"job_type_id",screenName:"screen_name",sessionId:"session_id",timestamp:"timestamp",documentSide:"document_side",decision:"decision",livenessImageCount:"liveness_image_count",attempt:"attempt",jobId:"job_id",result:"result",errorMessage:"error_message",reason:"reason",framesDiscarded:"frames_discarded",durationMs:"duration_ms",iou:"iou",probability:"probability"},UseSmileIDAnalyticsScreenNames={consent:"consent",instructions:"instructions",capture:"capture",preview:"preview",processing:"processing"},UseSmileIDAnalyticsReasons={outOfFrame:"out_of_frame",timeout:"timeout",qualityDropTimeout:"quality_drop_timeout",motionBlur:"motion_blur",notSmiling:"not_smiling"};
1
+ "use strict";export const UseSmileIDAnalyticsEventTypes={flowStarted:"flow_started",screenViewed:"screen_viewed",consentCaptured:"consent_captured",selfieCaptured:"selfie_captured",documentCaptured:"document_captured",retakeRequested:"retake_requested",jobSubmitted:"job_submitted",jobCaptured:"job_captured",flowCompleted:"flow_completed",selfieSessionRestarted:"selfie_session_restarted",selfieFrameRejected:"selfie_frame_rejected"},UseSmileIDAnalyticsParamKeys={jobType:"job_type",jobTypeId:"job_type_id",screenName:"screen_name",sessionId:"session_id",timestamp:"timestamp",documentSide:"document_side",decision:"decision",livenessImageCount:"liveness_image_count",attempt:"attempt",jobId:"job_id",result:"result",errorMessage:"error_message",reason:"reason",framesDiscarded:"frames_discarded",durationMs:"duration_ms",iou:"iou",probability:"probability"},UseSmileIDAnalyticsScreenNames={consent:"consent",instructions:"instructions",capture:"capture",preview:"preview",processing:"processing"},UseSmileIDAnalyticsReasons={motionBlur:"motion_blur",qualityDropTimeout:"quality_drop_timeout"};
@@ -49,6 +49,11 @@ export type UseSmileIDAnalyticsEventKind =
49
49
  readonly jobType: JobType;
50
50
  readonly attempt: number;
51
51
  }
52
+ /** `skipApiSubmission` handed the assembled job to the partner instead of submitting it. */
53
+ | {
54
+ readonly kind: "jobCaptured";
55
+ readonly jobType: JobType;
56
+ }
52
57
  /** Smile ID accepted the submission; `jobId` is sent when it is known. */
53
58
  | {
54
59
  readonly kind: "flowCompletedSuccess";
@@ -59,6 +64,10 @@ export type UseSmileIDAnalyticsEventKind =
59
64
  readonly kind: "flowCompletedFailure";
60
65
  readonly errorMessage: string;
61
66
  }
67
+ /** `skipApiSubmission` handed the job to the partner; nothing was submitted. */
68
+ | {
69
+ readonly kind: "flowCompletedCaptured";
70
+ }
62
71
  /** The selfie session restarted after its requirements stopped being met. */
63
72
  | {
64
73
  readonly kind: "selfieSessionRestarted";
@@ -1 +1 @@
1
- "use strict";import{DocumentSide as l}from"@smileid/usesmileid_bridge";import{JobType as m}from"../../../core/models/job_type";import{UseSmileIDAnalyticsEventTypes as s,UseSmileIDAnalyticsParamKeys as n,UseSmileIDAnalyticsScreenNames as S}from"./use_smile_id_analytics_event";const o="granted",u="success",a="failure";function c(e){return{[n.jobType]:String(e),[n.jobTypeId]:String(e)}}export function encodeAnalyticsEventKind(e){const t=s;switch(e.kind){case"flowStarted":return{type:t.flowStarted,extras:c(e.jobType)};case"screenViewed":return{type:t.screenViewed,extras:{[n.screenName]:e.screen}};case"consentCaptured":return{type:t.consentCaptured,extras:{[n.decision]:o}};case"selfieCaptured":return{type:t.selfieCaptured,extras:{[n.livenessImageCount]:String(e.livenessImageCount)}};case"documentCaptured":return{type:t.documentCaptured,extras:{[n.documentSide]:e.side}};case"retakeRequested":return{type:t.retakeRequested,extras:{}};case"jobSubmitted":return{type:t.jobSubmitted,extras:{...c(e.jobType),[n.attempt]:String(e.attempt)}};case"flowCompletedSuccess":return{type:t.flowCompleted,extras:{[n.result]:u,...e.jobId?{[n.jobId]:e.jobId}:{}}};case"flowCompletedFailure":return{type:t.flowCompleted,extras:{[n.result]:a,[n.errorMessage]:e.errorMessage}};case"selfieSessionRestarted":return{type:t.selfieSessionRestarted,extras:{[n.reason]:e.reason,[n.framesDiscarded]:String(e.framesDiscarded),[n.durationMs]:String(e.durationMs)}};case"selfieFrameRejected":return{type:t.selfieFrameRejected,extras:{[n.reason]:e.reason}}}}function d(e){if(!(e===void 0||!/^-?\d+$/.test(e)))return Number(e)}function f(e){const t=d(e[n.jobTypeId])??d(e[n.jobType]);return Object.values(m).find(r=>r===t)}const y=Object.values(S),b=Object.values(l),p={[s.flowStarted]:e=>{const t=f(e);return t===void 0?void 0:{kind:"flowStarted",jobType:t}},[s.screenViewed]:e=>{const t=e[n.screenName];return t!==void 0&&y.includes(t)?{kind:"screenViewed",screen:t}:void 0},[s.consentCaptured]:e=>e[n.decision]===o?{kind:"consentCaptured"}:void 0,[s.selfieCaptured]:e=>{const t=d(e[n.livenessImageCount]);return t===void 0?void 0:{kind:"selfieCaptured",livenessImageCount:t}},[s.documentCaptured]:e=>{const t=e[n.documentSide];return t!==void 0&&b.includes(t)?{kind:"documentCaptured",side:t}:void 0},[s.retakeRequested]:()=>({kind:"retakeRequested"}),[s.jobSubmitted]:e=>{const t=f(e),r=d(e[n.attempt]);return t===void 0||r===void 0?void 0:{kind:"jobSubmitted",jobType:t,attempt:r}},[s.flowCompleted]:e=>{switch(e[n.result]){case u:{const t=e[n.jobId];return t?{kind:"flowCompletedSuccess",jobId:t}:{kind:"flowCompletedSuccess"}}case a:{const t=e[n.errorMessage];return t===void 0?void 0:{kind:"flowCompletedFailure",errorMessage:t}}default:return}},[s.selfieSessionRestarted]:e=>{const t=e[n.reason],r=d(e[n.framesDiscarded]),i=d(e[n.durationMs]);return t===void 0||r===void 0||i===void 0?void 0:{kind:"selfieSessionRestarted",reason:t,framesDiscarded:r,durationMs:i}},[s.selfieFrameRejected]:e=>{const t=e[n.reason];return t===void 0?void 0:{kind:"selfieFrameRejected",reason:t}}};export function analyticsEventKind(e){return Object.prototype.hasOwnProperty.call(p,e.type)?p[e.type]?.(e.extras):void 0}
1
+ "use strict";import{DocumentSide as m}from"@smileid/usesmileid_bridge";import{JobType as S}from"../../../core/models/job_type";import{UseSmileIDAnalyticsEventTypes as r,UseSmileIDAnalyticsParamKeys as n,UseSmileIDAnalyticsScreenNames as y}from"./use_smile_id_analytics_event";const a="granted",c="success",f="failure",p="captured";function o(e){return{[n.jobType]:String(e),[n.jobTypeId]:String(e)}}export function encodeAnalyticsEventKind(e){const t=r;switch(e.kind){case"flowStarted":return{type:t.flowStarted,extras:o(e.jobType)};case"screenViewed":return{type:t.screenViewed,extras:{[n.screenName]:e.screen}};case"consentCaptured":return{type:t.consentCaptured,extras:{[n.decision]:a}};case"selfieCaptured":return{type:t.selfieCaptured,extras:{[n.livenessImageCount]:String(e.livenessImageCount)}};case"documentCaptured":return{type:t.documentCaptured,extras:{[n.documentSide]:e.side}};case"retakeRequested":return{type:t.retakeRequested,extras:{}};case"jobSubmitted":return{type:t.jobSubmitted,extras:{...o(e.jobType),[n.attempt]:String(e.attempt)}};case"jobCaptured":return{type:t.jobCaptured,extras:o(e.jobType)};case"flowCompletedSuccess":return{type:t.flowCompleted,extras:{[n.result]:c,...e.jobId?{[n.jobId]:e.jobId}:{}}};case"flowCompletedFailure":return{type:t.flowCompleted,extras:{[n.result]:f,[n.errorMessage]:e.errorMessage}};case"flowCompletedCaptured":return{type:t.flowCompleted,extras:{[n.result]:p}};case"selfieSessionRestarted":return{type:t.selfieSessionRestarted,extras:{[n.reason]:e.reason,[n.framesDiscarded]:String(e.framesDiscarded),[n.durationMs]:String(e.durationMs)}};case"selfieFrameRejected":return{type:t.selfieFrameRejected,extras:{[n.reason]:e.reason}}}}function s(e){if(!(e===void 0||!/^-?\d+$/.test(e)))return Number(e)}function i(e){const t=s(e[n.jobTypeId])??s(e[n.jobType]);return Object.values(S).find(d=>d===t)}const C=Object.values(y),b=Object.values(m),l={[r.flowStarted]:e=>{const t=i(e);return t===void 0?void 0:{kind:"flowStarted",jobType:t}},[r.screenViewed]:e=>{const t=e[n.screenName];return t!==void 0&&C.includes(t)?{kind:"screenViewed",screen:t}:void 0},[r.consentCaptured]:e=>e[n.decision]===a?{kind:"consentCaptured"}:void 0,[r.selfieCaptured]:e=>{const t=s(e[n.livenessImageCount]);return t===void 0?void 0:{kind:"selfieCaptured",livenessImageCount:t}},[r.documentCaptured]:e=>{const t=e[n.documentSide];return t!==void 0&&b.includes(t)?{kind:"documentCaptured",side:t}:void 0},[r.retakeRequested]:()=>({kind:"retakeRequested"}),[r.jobSubmitted]:e=>{const t=i(e),d=s(e[n.attempt]);return t===void 0||d===void 0?void 0:{kind:"jobSubmitted",jobType:t,attempt:d}},[r.jobCaptured]:e=>{const t=i(e);return t===void 0?void 0:{kind:"jobCaptured",jobType:t}},[r.flowCompleted]:e=>{switch(e[n.result]){case c:{const t=e[n.jobId];return t?{kind:"flowCompletedSuccess",jobId:t}:{kind:"flowCompletedSuccess"}}case f:{const t=e[n.errorMessage];return t===void 0?void 0:{kind:"flowCompletedFailure",errorMessage:t}}case p:return{kind:"flowCompletedCaptured"};default:return}},[r.selfieSessionRestarted]:e=>{const t=e[n.reason],d=s(e[n.framesDiscarded]),u=s(e[n.durationMs]);return t===void 0||d===void 0||u===void 0?void 0:{kind:"selfieSessionRestarted",reason:t,framesDiscarded:d,durationMs:u}},[r.selfieFrameRejected]:e=>{const t=e[n.reason];return t===void 0?void 0:{kind:"selfieFrameRejected",reason:t}}};export function analyticsEventKind(e){return Object.prototype.hasOwnProperty.call(l,e.type)?l[e.type]?.(e.extras):void 0}
@@ -0,0 +1,9 @@
1
+ import type { FlowConfiguration } from "./flow_configuration";
2
+ /** Whether a step of `configuration` captures with the camera, selfie or document. */
3
+ export declare function opensCamera(configuration: FlowConfiguration): boolean;
4
+ /**
5
+ * Starts the camera stack's one-off set-up now when a step of `configuration` captures, so the
6
+ * capture screen's camera doesn't wait for it. Opens no camera, needs no permission, and kamera's
7
+ * `prewarm` never rejects.
8
+ */
9
+ export declare function prewarmCameraIfNeeded(configuration: FlowConfiguration, prewarm?: () => Promise<void>): void;
@@ -0,0 +1 @@
1
+ "use strict";import{KameraSession as n}from"@smileid/kamera";export function opensCamera(e){return e.screens.steps.some(r=>r.kind==="capture")}export function prewarmCameraIfNeeded(e,r=()=>n.prewarm()){opensCamera(e)&&r()}