smartcomply-web-sdk 1.0.60 → 1.0.62
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.
- package/dist/camera/CameraManager.d.ts +8 -0
- package/dist/camera/CameraManager.d.ts.map +1 -1
- package/dist/camera/CameraManager.js +13 -2
- package/dist/camera/CameraManager.js.map +1 -1
- package/dist/camera/Permission.d.ts +11 -0
- package/dist/camera/Permission.d.ts.map +1 -1
- package/dist/camera/Permission.js +23 -0
- package/dist/camera/Permission.js.map +1 -1
- package/dist/esm/camera/CameraManager.d.ts +8 -0
- package/dist/esm/camera/CameraManager.d.ts.map +1 -1
- package/dist/esm/camera/CameraManager.js +11 -1
- package/dist/esm/camera/CameraManager.js.map +1 -1
- package/dist/esm/camera/Permission.d.ts +11 -0
- package/dist/esm/camera/Permission.d.ts.map +1 -1
- package/dist/esm/camera/Permission.js +22 -0
- package/dist/esm/camera/Permission.js.map +1 -1
- package/dist/esm/flow/DocumentCapture.d.ts +1 -4
- package/dist/esm/flow/DocumentCapture.d.ts.map +1 -1
- package/dist/esm/flow/DocumentCapture.js +54 -132
- package/dist/esm/flow/DocumentCapture.js.map +1 -1
- package/dist/esm/flow/SmartComplyFlow.d.ts.map +1 -1
- package/dist/esm/flow/SmartComplyFlow.js +8 -13
- package/dist/esm/flow/SmartComplyFlow.js.map +1 -1
- package/dist/esm/utils/constants.d.ts +11 -3
- package/dist/esm/utils/constants.d.ts.map +1 -1
- package/dist/esm/utils/constants.js +11 -3
- package/dist/esm/utils/constants.js.map +1 -1
- package/dist/esm/utils/documentRules.d.ts +15 -0
- package/dist/esm/utils/documentRules.d.ts.map +1 -0
- package/dist/esm/utils/documentRules.js +27 -0
- package/dist/esm/utils/documentRules.js.map +1 -0
- package/dist/flow/DocumentCapture.d.ts +1 -4
- package/dist/flow/DocumentCapture.d.ts.map +1 -1
- package/dist/flow/DocumentCapture.js +54 -132
- package/dist/flow/DocumentCapture.js.map +1 -1
- package/dist/flow/SmartComplyFlow.d.ts.map +1 -1
- package/dist/flow/SmartComplyFlow.js +11 -16
- package/dist/flow/SmartComplyFlow.js.map +1 -1
- package/dist/smartcomply.browser.js +62 -113
- package/dist/smartcomply.browser.js.map +4 -4
- package/dist/utils/constants.d.ts +11 -3
- package/dist/utils/constants.d.ts.map +1 -1
- package/dist/utils/constants.js +11 -3
- package/dist/utils/constants.js.map +1 -1
- package/dist/utils/documentRules.d.ts +15 -0
- package/dist/utils/documentRules.d.ts.map +1 -0
- package/dist/utils/documentRules.js +31 -0
- package/dist/utils/documentRules.js.map +1 -0
- package/package.json +6 -3
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
import { PermissionStatus } from "./Permission";
|
|
2
|
+
/** Error thrown by CameraManager.open() — carries the classified permission
|
|
3
|
+
* status alongside the friendly message, so callers can decide whether a
|
|
4
|
+
* failure is worth retrying without re-deriving it from the raw DOMException. */
|
|
5
|
+
export declare class CameraAccessError extends Error {
|
|
6
|
+
readonly status: PermissionStatus;
|
|
7
|
+
constructor(message: string, status: PermissionStatus);
|
|
8
|
+
}
|
|
1
9
|
export declare class CameraManager {
|
|
2
10
|
private stream;
|
|
3
11
|
private onInterrupted;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CameraManager.d.ts","sourceRoot":"","sources":["../../src/camera/CameraManager.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"CameraManager.d.ts","sourceRoot":"","sources":["../../src/camera/CameraManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErE;;iFAEiF;AACjF,qBAAa,iBAAkB,SAAQ,KAAK;aACG,MAAM,EAAE,gBAAgB;gBAAzD,OAAO,EAAE,MAAM,EAAkB,MAAM,EAAE,gBAAgB;CAItE;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,aAAa,CAA6B;IAClD,OAAO,CAAC,kBAAkB,CAAyB;IAEnD;;;OAGG;IACG,IAAI,CAAC,WAAW,CAAC,EAAE,sBAAsB,GAAG,OAAO,CAAC,WAAW,CAAC;IAqBtE,SAAS,IAAI,WAAW,GAAG,IAAI;IAI/B;;;;;OAKG;IACH,SAAS,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI;IAKpC;;;;;;;OAOG;IACH,mBAAmB,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI;IAI/C,OAAO,CAAC,YAAY;IAiBpB,IAAI,IAAI,IAAI;CAQb"}
|
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CameraManager = void 0;
|
|
3
|
+
exports.CameraManager = exports.CameraAccessError = void 0;
|
|
4
4
|
const Permission_1 = require("./Permission");
|
|
5
|
+
/** Error thrown by CameraManager.open() — carries the classified permission
|
|
6
|
+
* status alongside the friendly message, so callers can decide whether a
|
|
7
|
+
* failure is worth retrying without re-deriving it from the raw DOMException. */
|
|
8
|
+
class CameraAccessError extends Error {
|
|
9
|
+
constructor(message, status) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.status = status;
|
|
12
|
+
this.name = "CameraAccessError";
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
exports.CameraAccessError = CameraAccessError;
|
|
5
16
|
class CameraManager {
|
|
6
17
|
constructor() {
|
|
7
18
|
this.stream = null;
|
|
@@ -23,7 +34,7 @@ class CameraManager {
|
|
|
23
34
|
};
|
|
24
35
|
const { stream, permission } = await (0, Permission_1.requestCameraAccess)(constraints || defaults);
|
|
25
36
|
if (!stream) {
|
|
26
|
-
throw new
|
|
37
|
+
throw new CameraAccessError(permission.reason || "Unable to access the camera.", permission.status);
|
|
27
38
|
}
|
|
28
39
|
this.stream = stream;
|
|
29
40
|
this._watchTracks();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CameraManager.js","sourceRoot":"","sources":["../../src/camera/CameraManager.ts"],"names":[],"mappings":";;;AAAA,
|
|
1
|
+
{"version":3,"file":"CameraManager.js","sourceRoot":"","sources":["../../src/camera/CameraManager.ts"],"names":[],"mappings":";;;AAAA,6CAAqE;AAErE;;iFAEiF;AACjF,MAAa,iBAAkB,SAAQ,KAAK;IAC1C,YAAY,OAAe,EAAkB,MAAwB;QACnE,KAAK,CAAC,OAAO,CAAC,CAAC;QAD4B,WAAM,GAAN,MAAM,CAAkB;QAEnE,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IAClC,CAAC;CACF;AALD,8CAKC;AAED,MAAa,aAAa;IAA1B;QACU,WAAM,GAAuB,IAAI,CAAC;QAClC,kBAAa,GAAwB,IAAI,CAAC;QAC1C,uBAAkB,GAAsB,EAAE,CAAC;IA+ErD,CAAC;IA7EC;;;OAGG;IACH,KAAK,CAAC,IAAI,CAAC,WAAoC;QAC7C,MAAM,QAAQ,GAA2B;YACvC,KAAK,EAAE;gBACL,UAAU,EAAE,MAAM;gBAClB,KAAK,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE;gBACtB,MAAM,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE;aACvB;YACD,KAAK,EAAE,KAAK;SACb,CAAC;QAEF,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,IAAA,gCAAmB,EAAC,WAAW,IAAI,QAAQ,CAAC,CAAC;QAClF,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,iBAAiB,CAAC,UAAU,CAAC,MAAM,IAAI,8BAA8B,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;QACtG,CAAC;QAED,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,YAAY,EAAE,CAAC;QAEpB,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED,SAAS;QACP,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED;;;;;OAKG;IACH,SAAS,CAAC,MAAmB;QAC3B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,YAAY,EAAE,CAAC;IACtB,CAAC;IAED;;;;;;;OAOG;IACH,mBAAmB,CAAC,QAAoB;QACtC,IAAI,CAAC,aAAa,GAAG,QAAQ,CAAC;IAChC,CAAC;IAEO,YAAY;QAClB,IAAI,CAAC,kBAAkB,GAAG,EAAE,CAAC;QAC7B,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,OAAO;QACzB,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;YACxC,MAAM,OAAO,GAAG,GAAG,EAAE;gBACnB,qEAAqE;gBACrE,iEAAiE;gBACjE,mEAAmE;gBACnE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;oBAChB,IAAI,CAAC,aAAa,EAAE,EAAE,CAAC;gBACzB,CAAC;YACH,CAAC,CAAC;YACF,KAAK,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YACzC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;QAClF,CAAC,CAAC,CAAC;IACL,CAAC;IAED,IAAI;QACF,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;YACzD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACrB,CAAC;QACD,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC;QACtD,IAAI,CAAC,kBAAkB,GAAG,EAAE,CAAC;IAC/B,CAAC;CACF;AAlFD,sCAkFC"}
|
|
@@ -30,6 +30,17 @@ export declare function requestCameraAccess(constraints?: MediaStreamConstraints
|
|
|
30
30
|
stream: MediaStream;
|
|
31
31
|
permission: CameraPermissionResult;
|
|
32
32
|
}>;
|
|
33
|
+
/**
|
|
34
|
+
* Whether a failed camera request is worth retrying. "denied" (user
|
|
35
|
+
* explicitly refused) and "unsupported" (no camera device at all) can never
|
|
36
|
+
* be fixed by retrying — surfacing the error immediately instead of burning
|
|
37
|
+
* several seconds of backoff first. Everything else (busy camera,
|
|
38
|
+
* unsupported constraints, anything unrecognized) is treated as
|
|
39
|
+
* potentially transient and retried — failing open avoids turning a
|
|
40
|
+
* recoverable hiccup into a hard failure on browsers that throw
|
|
41
|
+
* non-standard error names (notably Safari/iOS).
|
|
42
|
+
*/
|
|
43
|
+
export declare function isRetryableCameraError(status: PermissionStatus): boolean;
|
|
33
44
|
/**
|
|
34
45
|
* Stop all tracks on a MediaStream and release the camera.
|
|
35
46
|
* Safe to call with null/undefined.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Permission.d.ts","sourceRoot":"","sources":["../../src/camera/Permission.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,MAAM,gBAAgB,GACxB,SAAS,GACT,QAAQ,GACR,QAAQ,GACR,aAAa,GACb,SAAS,CAAC;AAEd,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,gBAAgB,CAAC;IACzB,iEAAiE;IACjE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;GAKG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,sBAAsB,CAAC,CAe7E;AAED;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CACvC,WAAW,CAAC,EAAE,sBAAsB,GACnC,OAAO,CAAC;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,UAAU,EAAE,sBAAsB,CAAA;CAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"Permission.d.ts","sourceRoot":"","sources":["../../src/camera/Permission.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,MAAM,gBAAgB,GACxB,SAAS,GACT,QAAQ,GACR,QAAQ,GACR,aAAa,GACb,SAAS,CAAC;AAEd,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,gBAAgB,CAAC;IACzB,iEAAiE;IACjE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;GAKG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,sBAAsB,CAAC,CAe7E;AAED;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CACvC,WAAW,CAAC,EAAE,sBAAsB,GACnC,OAAO,CAAC;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,UAAU,EAAE,sBAAsB,CAAA;CAAE,CAAC,CA4DtE;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAExE;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,CAGhF;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAM3C"}
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
10
|
exports.queryCameraPermission = queryCameraPermission;
|
|
11
11
|
exports.requestCameraAccess = requestCameraAccess;
|
|
12
|
+
exports.isRetryableCameraError = isRetryableCameraError;
|
|
12
13
|
exports.releaseCameraStream = releaseCameraStream;
|
|
13
14
|
exports.isCameraSupported = isCameraSupported;
|
|
14
15
|
/**
|
|
@@ -78,6 +79,15 @@ async function requestCameraAccess(constraints) {
|
|
|
78
79
|
},
|
|
79
80
|
};
|
|
80
81
|
}
|
|
82
|
+
if (name === "OverconstrainedError") {
|
|
83
|
+
return {
|
|
84
|
+
stream: null,
|
|
85
|
+
permission: {
|
|
86
|
+
status: "unknown",
|
|
87
|
+
reason: "Camera constraints not supported. Trying with different settings...",
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
81
91
|
return {
|
|
82
92
|
stream: null,
|
|
83
93
|
permission: {
|
|
@@ -87,6 +97,19 @@ async function requestCameraAccess(constraints) {
|
|
|
87
97
|
};
|
|
88
98
|
}
|
|
89
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* Whether a failed camera request is worth retrying. "denied" (user
|
|
102
|
+
* explicitly refused) and "unsupported" (no camera device at all) can never
|
|
103
|
+
* be fixed by retrying — surfacing the error immediately instead of burning
|
|
104
|
+
* several seconds of backoff first. Everything else (busy camera,
|
|
105
|
+
* unsupported constraints, anything unrecognized) is treated as
|
|
106
|
+
* potentially transient and retried — failing open avoids turning a
|
|
107
|
+
* recoverable hiccup into a hard failure on browsers that throw
|
|
108
|
+
* non-standard error names (notably Safari/iOS).
|
|
109
|
+
*/
|
|
110
|
+
function isRetryableCameraError(status) {
|
|
111
|
+
return status !== "denied" && status !== "unsupported";
|
|
112
|
+
}
|
|
90
113
|
/**
|
|
91
114
|
* Stop all tracks on a MediaStream and release the camera.
|
|
92
115
|
* Safe to call with null/undefined.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Permission.js","sourceRoot":"","sources":["../../src/camera/Permission.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;;AAqBH,sDAeC;AAUD,
|
|
1
|
+
{"version":3,"file":"Permission.js","sourceRoot":"","sources":["../../src/camera/Permission.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
import { PermissionStatus } from "./Permission";
|
|
2
|
+
/** Error thrown by CameraManager.open() — carries the classified permission
|
|
3
|
+
* status alongside the friendly message, so callers can decide whether a
|
|
4
|
+
* failure is worth retrying without re-deriving it from the raw DOMException. */
|
|
5
|
+
export declare class CameraAccessError extends Error {
|
|
6
|
+
readonly status: PermissionStatus;
|
|
7
|
+
constructor(message: string, status: PermissionStatus);
|
|
8
|
+
}
|
|
1
9
|
export declare class CameraManager {
|
|
2
10
|
private stream;
|
|
3
11
|
private onInterrupted;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CameraManager.d.ts","sourceRoot":"","sources":["../../../src/camera/CameraManager.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"CameraManager.d.ts","sourceRoot":"","sources":["../../../src/camera/CameraManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErE;;iFAEiF;AACjF,qBAAa,iBAAkB,SAAQ,KAAK;aACG,MAAM,EAAE,gBAAgB;gBAAzD,OAAO,EAAE,MAAM,EAAkB,MAAM,EAAE,gBAAgB;CAItE;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,aAAa,CAA6B;IAClD,OAAO,CAAC,kBAAkB,CAAyB;IAEnD;;;OAGG;IACG,IAAI,CAAC,WAAW,CAAC,EAAE,sBAAsB,GAAG,OAAO,CAAC,WAAW,CAAC;IAqBtE,SAAS,IAAI,WAAW,GAAG,IAAI;IAI/B;;;;;OAKG;IACH,SAAS,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI;IAKpC;;;;;;;OAOG;IACH,mBAAmB,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI;IAI/C,OAAO,CAAC,YAAY;IAiBpB,IAAI,IAAI,IAAI;CAQb"}
|
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
import { requestCameraAccess } from "./Permission";
|
|
2
|
+
/** Error thrown by CameraManager.open() — carries the classified permission
|
|
3
|
+
* status alongside the friendly message, so callers can decide whether a
|
|
4
|
+
* failure is worth retrying without re-deriving it from the raw DOMException. */
|
|
5
|
+
export class CameraAccessError extends Error {
|
|
6
|
+
constructor(message, status) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.status = status;
|
|
9
|
+
this.name = "CameraAccessError";
|
|
10
|
+
}
|
|
11
|
+
}
|
|
2
12
|
export class CameraManager {
|
|
3
13
|
constructor() {
|
|
4
14
|
this.stream = null;
|
|
@@ -20,7 +30,7 @@ export class CameraManager {
|
|
|
20
30
|
};
|
|
21
31
|
const { stream, permission } = await requestCameraAccess(constraints || defaults);
|
|
22
32
|
if (!stream) {
|
|
23
|
-
throw new
|
|
33
|
+
throw new CameraAccessError(permission.reason || "Unable to access the camera.", permission.status);
|
|
24
34
|
}
|
|
25
35
|
this.stream = stream;
|
|
26
36
|
this._watchTracks();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CameraManager.js","sourceRoot":"","sources":["../../../src/camera/CameraManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,
|
|
1
|
+
{"version":3,"file":"CameraManager.js","sourceRoot":"","sources":["../../../src/camera/CameraManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAoB,MAAM,cAAc,CAAC;AAErE;;iFAEiF;AACjF,MAAM,OAAO,iBAAkB,SAAQ,KAAK;IAC1C,YAAY,OAAe,EAAkB,MAAwB;QACnE,KAAK,CAAC,OAAO,CAAC,CAAC;QAD4B,WAAM,GAAN,MAAM,CAAkB;QAEnE,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IAClC,CAAC;CACF;AAED,MAAM,OAAO,aAAa;IAA1B;QACU,WAAM,GAAuB,IAAI,CAAC;QAClC,kBAAa,GAAwB,IAAI,CAAC;QAC1C,uBAAkB,GAAsB,EAAE,CAAC;IA+ErD,CAAC;IA7EC;;;OAGG;IACH,KAAK,CAAC,IAAI,CAAC,WAAoC;QAC7C,MAAM,QAAQ,GAA2B;YACvC,KAAK,EAAE;gBACL,UAAU,EAAE,MAAM;gBAClB,KAAK,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE;gBACtB,MAAM,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE;aACvB;YACD,KAAK,EAAE,KAAK;SACb,CAAC;QAEF,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,mBAAmB,CAAC,WAAW,IAAI,QAAQ,CAAC,CAAC;QAClF,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,iBAAiB,CAAC,UAAU,CAAC,MAAM,IAAI,8BAA8B,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;QACtG,CAAC;QAED,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,YAAY,EAAE,CAAC;QAEpB,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED,SAAS;QACP,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED;;;;;OAKG;IACH,SAAS,CAAC,MAAmB;QAC3B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,YAAY,EAAE,CAAC;IACtB,CAAC;IAED;;;;;;;OAOG;IACH,mBAAmB,CAAC,QAAoB;QACtC,IAAI,CAAC,aAAa,GAAG,QAAQ,CAAC;IAChC,CAAC;IAEO,YAAY;QAClB,IAAI,CAAC,kBAAkB,GAAG,EAAE,CAAC;QAC7B,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,OAAO;QACzB,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;YACxC,MAAM,OAAO,GAAG,GAAG,EAAE;gBACnB,qEAAqE;gBACrE,iEAAiE;gBACjE,mEAAmE;gBACnE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;oBAChB,IAAI,CAAC,aAAa,EAAE,EAAE,CAAC;gBACzB,CAAC;YACH,CAAC,CAAC;YACF,KAAK,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YACzC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;QAClF,CAAC,CAAC,CAAC;IACL,CAAC;IAED,IAAI;QACF,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;YACzD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACrB,CAAC;QACD,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC;QACtD,IAAI,CAAC,kBAAkB,GAAG,EAAE,CAAC;IAC/B,CAAC;CACF"}
|
|
@@ -30,6 +30,17 @@ export declare function requestCameraAccess(constraints?: MediaStreamConstraints
|
|
|
30
30
|
stream: MediaStream;
|
|
31
31
|
permission: CameraPermissionResult;
|
|
32
32
|
}>;
|
|
33
|
+
/**
|
|
34
|
+
* Whether a failed camera request is worth retrying. "denied" (user
|
|
35
|
+
* explicitly refused) and "unsupported" (no camera device at all) can never
|
|
36
|
+
* be fixed by retrying — surfacing the error immediately instead of burning
|
|
37
|
+
* several seconds of backoff first. Everything else (busy camera,
|
|
38
|
+
* unsupported constraints, anything unrecognized) is treated as
|
|
39
|
+
* potentially transient and retried — failing open avoids turning a
|
|
40
|
+
* recoverable hiccup into a hard failure on browsers that throw
|
|
41
|
+
* non-standard error names (notably Safari/iOS).
|
|
42
|
+
*/
|
|
43
|
+
export declare function isRetryableCameraError(status: PermissionStatus): boolean;
|
|
33
44
|
/**
|
|
34
45
|
* Stop all tracks on a MediaStream and release the camera.
|
|
35
46
|
* Safe to call with null/undefined.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Permission.d.ts","sourceRoot":"","sources":["../../../src/camera/Permission.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,MAAM,gBAAgB,GACxB,SAAS,GACT,QAAQ,GACR,QAAQ,GACR,aAAa,GACb,SAAS,CAAC;AAEd,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,gBAAgB,CAAC;IACzB,iEAAiE;IACjE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;GAKG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,sBAAsB,CAAC,CAe7E;AAED;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CACvC,WAAW,CAAC,EAAE,sBAAsB,GACnC,OAAO,CAAC;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,UAAU,EAAE,sBAAsB,CAAA;CAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"Permission.d.ts","sourceRoot":"","sources":["../../../src/camera/Permission.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,MAAM,gBAAgB,GACxB,SAAS,GACT,QAAQ,GACR,QAAQ,GACR,aAAa,GACb,SAAS,CAAC;AAEd,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,gBAAgB,CAAC;IACzB,iEAAiE;IACjE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;GAKG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,sBAAsB,CAAC,CAe7E;AAED;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CACvC,WAAW,CAAC,EAAE,sBAAsB,GACnC,OAAO,CAAC;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,UAAU,EAAE,sBAAsB,CAAA;CAAE,CAAC,CA4DtE;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAExE;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,CAGhF;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAM3C"}
|
|
@@ -72,6 +72,15 @@ export async function requestCameraAccess(constraints) {
|
|
|
72
72
|
},
|
|
73
73
|
};
|
|
74
74
|
}
|
|
75
|
+
if (name === "OverconstrainedError") {
|
|
76
|
+
return {
|
|
77
|
+
stream: null,
|
|
78
|
+
permission: {
|
|
79
|
+
status: "unknown",
|
|
80
|
+
reason: "Camera constraints not supported. Trying with different settings...",
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}
|
|
75
84
|
return {
|
|
76
85
|
stream: null,
|
|
77
86
|
permission: {
|
|
@@ -81,6 +90,19 @@ export async function requestCameraAccess(constraints) {
|
|
|
81
90
|
};
|
|
82
91
|
}
|
|
83
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* Whether a failed camera request is worth retrying. "denied" (user
|
|
95
|
+
* explicitly refused) and "unsupported" (no camera device at all) can never
|
|
96
|
+
* be fixed by retrying — surfacing the error immediately instead of burning
|
|
97
|
+
* several seconds of backoff first. Everything else (busy camera,
|
|
98
|
+
* unsupported constraints, anything unrecognized) is treated as
|
|
99
|
+
* potentially transient and retried — failing open avoids turning a
|
|
100
|
+
* recoverable hiccup into a hard failure on browsers that throw
|
|
101
|
+
* non-standard error names (notably Safari/iOS).
|
|
102
|
+
*/
|
|
103
|
+
export function isRetryableCameraError(status) {
|
|
104
|
+
return status !== "denied" && status !== "unsupported";
|
|
105
|
+
}
|
|
84
106
|
/**
|
|
85
107
|
* Stop all tracks on a MediaStream and release the camera.
|
|
86
108
|
* Safe to call with null/undefined.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Permission.js","sourceRoot":"","sources":["../../../src/camera/Permission.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Permission.js","sourceRoot":"","sources":["../../../src/camera/Permission.ts"],"names":[],"mappings":"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"}
|
|
@@ -15,7 +15,7 @@ export interface DocumentCaptureResult {
|
|
|
15
15
|
export declare class DocumentCapture {
|
|
16
16
|
private root;
|
|
17
17
|
private videoEl;
|
|
18
|
-
private
|
|
18
|
+
private cameraManager;
|
|
19
19
|
private resolveCapture;
|
|
20
20
|
private rejectCapture;
|
|
21
21
|
private frontBlob;
|
|
@@ -28,14 +28,12 @@ export declare class DocumentCapture {
|
|
|
28
28
|
*/
|
|
29
29
|
capture(container: HTMLElement, theme: ThemeColors, documentType: string, requiresBack?: boolean): Promise<DocumentCaptureResult>;
|
|
30
30
|
destroy(): void;
|
|
31
|
-
private requiresBackSide;
|
|
32
31
|
private shouldCaptureBackSide;
|
|
33
32
|
private renderChoiceScreen;
|
|
34
33
|
/** Returns an appropriate emoji icon for the document type. */
|
|
35
34
|
private _docIcon;
|
|
36
35
|
private renderCameraView;
|
|
37
36
|
private openCamera;
|
|
38
|
-
private getCameraErrorMessage;
|
|
39
37
|
private captureFrame;
|
|
40
38
|
/**
|
|
41
39
|
* Runs the local sharpness/glare/contrast/brightness check against a
|
|
@@ -51,7 +49,6 @@ export declare class DocumentCapture {
|
|
|
51
49
|
private validateFile;
|
|
52
50
|
private handleFileUpload;
|
|
53
51
|
private stopCamera;
|
|
54
|
-
private createEnhancedButton;
|
|
55
52
|
private showLoadingState;
|
|
56
53
|
private showError;
|
|
57
54
|
private createButton;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DocumentCapture.d.ts","sourceRoot":"","sources":["../../../src/flow/DocumentCapture.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"DocumentCapture.d.ts","sourceRoot":"","sources":["../../../src/flow/DocumentCapture.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAOtC;;;;;;;;GAQG;AACH,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,IAAI,CAAC;IACZ,IAAI,CAAC,EAAE,IAAI,CAAC;CACb;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,IAAI,CAA+B;IAC3C,OAAO,CAAC,OAAO,CAAiC;IAChD,OAAO,CAAC,aAAa,CAAsC;IAC3D,OAAO,CAAC,cAAc,CAA0D;IAChF,OAAO,CAAC,aAAa,CAAuC;IAC5D,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,QAAQ,CAAqB;IACrC,OAAO,CAAC,WAAW,CAA6B;IAChD,OAAO,CAAC,oBAAoB,CAAkC;IAE9D;;;OAGG;IACH,OAAO,CACL,SAAS,EAAE,WAAW,EACtB,KAAK,EAAE,WAAW,EAClB,YAAY,EAAE,MAAM,EACpB,YAAY,CAAC,EAAE,OAAO,GACrB,OAAO,CAAC,qBAAqB,CAAC;IAgCjC,OAAO,IAAI,IAAI;IAuBf,OAAO,CAAC,qBAAqB;IAO7B,OAAO,CAAC,kBAAkB;IAoK1B,+DAA+D;IAC/D,OAAO,CAAC,QAAQ;IAQhB,OAAO,CAAC,gBAAgB;YAuEV,UAAU;IAoExB,OAAO,CAAC,YAAY;IAkCpB;;;;OAIG;YACW,mBAAmB;IAYjC,+EAA+E;YACjE,kBAAkB;IAsBhC,OAAO,CAAC,WAAW;IAkHnB,OAAO,CAAC,qBAAqB;YAqCf,wBAAwB;IAUtC,OAAO,CAAC,YAAY;IAqBpB,OAAO,CAAC,gBAAgB;IAKxB,OAAO,CAAC,UAAU;IAQlB,OAAO,CAAC,gBAAgB;IAwDxB,OAAO,CAAC,SAAS;IAgCjB,OAAO,CAAC,YAAY;IA4BpB,OAAO,CAAC,aAAa;IA4ErB;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAuC5B,OAAO,CAAC,gBAAgB;CASzB"}
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { assessQuality } from "../utils/ImageQuality";
|
|
2
|
+
import { requiresBackSide, computeScaleRatio } from "../utils/documentRules";
|
|
3
|
+
import { CameraManager, CameraAccessError } from "../camera/CameraManager";
|
|
4
|
+
import { isRetryableCameraError } from "../camera/Permission";
|
|
5
|
+
import { DOC_CAMERA_IDEAL_WIDTH, DOC_CAMERA_IDEAL_HEIGHT } from "../utils/constants";
|
|
2
6
|
export class DocumentCapture {
|
|
3
7
|
constructor() {
|
|
4
8
|
this.root = null;
|
|
5
9
|
this.videoEl = null;
|
|
6
|
-
this.
|
|
10
|
+
this.cameraManager = new CameraManager();
|
|
7
11
|
this.resolveCapture = null;
|
|
8
12
|
this.rejectCapture = null;
|
|
9
13
|
this.frontBlob = null;
|
|
@@ -63,26 +67,11 @@ export class DocumentCapture {
|
|
|
63
67
|
this.backBlob = null;
|
|
64
68
|
this.currentSide = 'front';
|
|
65
69
|
}
|
|
66
|
-
requiresBackSide(documentType) {
|
|
67
|
-
// Only request back-side capture for documents where the backend OCR
|
|
68
|
-
// actually needs the reverse face. NIN cards are single-sided for OCR
|
|
69
|
-
// purposes — do NOT include "nin" here.
|
|
70
|
-
const type = documentType.toLowerCase();
|
|
71
|
-
return type.includes("voter") ||
|
|
72
|
-
type.includes("driver") ||
|
|
73
|
-
type.includes("license") ||
|
|
74
|
-
type.includes("national id") ||
|
|
75
|
-
type.includes("national id card") ||
|
|
76
|
-
type.includes("ghana");
|
|
77
|
-
// Passport: back side is the machine-readable zone but OCR reads from front only.
|
|
78
|
-
// NIN / NIN with face: front only needed.
|
|
79
|
-
// Voter's card / Driver's license / National ID card: back required.
|
|
80
|
-
}
|
|
81
70
|
shouldCaptureBackSide(documentType) {
|
|
82
|
-
// Prefer the backend-provided flag; fall back to
|
|
71
|
+
// Prefer the backend-provided flag; fall back to the shared heuristic only when absent.
|
|
83
72
|
return this.requiresBackOverride !== undefined
|
|
84
73
|
? this.requiresBackOverride
|
|
85
|
-
:
|
|
74
|
+
: requiresBackSide(documentType);
|
|
86
75
|
}
|
|
87
76
|
renderChoiceScreen(container, theme, documentType) {
|
|
88
77
|
this.root = document.createElement("div");
|
|
@@ -219,7 +208,11 @@ export class DocumentCapture {
|
|
|
219
208
|
return;
|
|
220
209
|
}
|
|
221
210
|
this.showLoadingState("Processing image...", theme);
|
|
222
|
-
|
|
211
|
+
// 2000x1500 @ 0.92 (was 1280x960 @ 0.85) — this document photo is
|
|
212
|
+
// later cropped down to just the face region for face comparison
|
|
213
|
+
// (see backend _crop_and_save_face), so the original needs enough
|
|
214
|
+
// headroom that the crop isn't left blurry from compression alone.
|
|
215
|
+
this.compressImage(file, 2000, 1500, 0.92)
|
|
223
216
|
.then((c) => this.handleFileUploadComplete(c, container, theme, documentType))
|
|
224
217
|
.catch(() => this.handleFileUploadComplete(file, container, theme, documentType));
|
|
225
218
|
}
|
|
@@ -305,28 +298,32 @@ export class DocumentCapture {
|
|
|
305
298
|
return;
|
|
306
299
|
}
|
|
307
300
|
const maxRetries = 3;
|
|
308
|
-
|
|
309
|
-
|
|
301
|
+
// Retries only transient/negotiable failures (busy camera, unsupported
|
|
302
|
+
// constraints, anything unrecognized) — a hard permission denial or "no
|
|
303
|
+
// camera exists" can never be fixed by retrying, so those short-circuit
|
|
304
|
+
// to an immediate error instead of burning ~6s of backoff first.
|
|
305
|
+
const attemptCameraAccess = async (constraints, retryCount = 0) => {
|
|
310
306
|
try {
|
|
311
|
-
return await
|
|
307
|
+
return await this.cameraManager.open(constraints);
|
|
312
308
|
}
|
|
313
309
|
catch (err) {
|
|
314
310
|
console.warn(`Camera access attempt ${retryCount + 1} failed:`, err);
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
await new Promise(resolve => setTimeout(resolve, 1000 * retryCount)); // Exponential backoff
|
|
318
|
-
return attemptCameraAccess(constraints);
|
|
311
|
+
const status = err instanceof CameraAccessError ? err.status : "unknown";
|
|
312
|
+
if (isRetryableCameraError(status) && retryCount < maxRetries - 1) {
|
|
313
|
+
await new Promise((resolve) => setTimeout(resolve, 1000 * (retryCount + 1))); // Exponential backoff
|
|
314
|
+
return attemptCameraAccess(constraints, retryCount + 1);
|
|
319
315
|
}
|
|
320
316
|
throw err;
|
|
321
317
|
}
|
|
322
318
|
};
|
|
319
|
+
let stream;
|
|
323
320
|
try {
|
|
324
321
|
// Try rear camera first
|
|
325
|
-
|
|
322
|
+
stream = await attemptCameraAccess({
|
|
326
323
|
video: {
|
|
327
324
|
facingMode: { ideal: "environment" },
|
|
328
|
-
width: { ideal:
|
|
329
|
-
height: { ideal:
|
|
325
|
+
width: { ideal: DOC_CAMERA_IDEAL_WIDTH },
|
|
326
|
+
height: { ideal: DOC_CAMERA_IDEAL_HEIGHT },
|
|
330
327
|
},
|
|
331
328
|
});
|
|
332
329
|
}
|
|
@@ -334,48 +331,37 @@ export class DocumentCapture {
|
|
|
334
331
|
console.warn("Rear camera failed, trying any camera:", err);
|
|
335
332
|
// Fallback to any camera
|
|
336
333
|
try {
|
|
337
|
-
|
|
338
|
-
video: { width: { ideal:
|
|
334
|
+
stream = await attemptCameraAccess({
|
|
335
|
+
video: { width: { ideal: DOC_CAMERA_IDEAL_WIDTH } },
|
|
339
336
|
});
|
|
340
337
|
}
|
|
341
338
|
catch (fallbackErr) {
|
|
342
|
-
|
|
343
|
-
this.rejectCapture?.(new Error(errorMessage));
|
|
339
|
+
this.rejectCapture?.(new Error(fallbackErr?.message || "Camera access failed. Please check your camera permissions and try again."));
|
|
344
340
|
return;
|
|
345
341
|
}
|
|
346
342
|
}
|
|
343
|
+
// A capture that's already resolved (photo taken, camera intentionally
|
|
344
|
+
// stopped) must not surface a stale interruption from the old stream.
|
|
345
|
+
this.cameraManager.onStreamInterrupted(() => {
|
|
346
|
+
if (this.resolveCapture || this.rejectCapture) {
|
|
347
|
+
this.rejectCapture?.(new Error("Camera connection was lost. Please check your camera and try again."));
|
|
348
|
+
}
|
|
349
|
+
});
|
|
347
350
|
if (this.videoEl) {
|
|
348
|
-
this.videoEl.srcObject =
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
getCameraErrorMessage(error) {
|
|
352
|
-
if (error.name === 'NotAllowedError') {
|
|
353
|
-
return "Camera access denied. Please allow camera permissions and try again.";
|
|
354
|
-
}
|
|
355
|
-
else if (error.name === 'NotFoundError') {
|
|
356
|
-
return "No camera found. Please ensure your device has a working camera.";
|
|
357
|
-
}
|
|
358
|
-
else if (error.name === 'NotReadableError') {
|
|
359
|
-
return "Camera is already in use by another application. Please close other apps using the camera and try again.";
|
|
360
|
-
}
|
|
361
|
-
else if (error.name === 'OverconstrainedError') {
|
|
362
|
-
return "Camera constraints not supported. Trying with different settings...";
|
|
363
|
-
}
|
|
364
|
-
else {
|
|
365
|
-
return "Camera access failed. Please check your camera permissions and try again.";
|
|
351
|
+
this.videoEl.srcObject = stream;
|
|
366
352
|
}
|
|
367
353
|
}
|
|
368
354
|
captureFrame(container, theme, documentType) {
|
|
369
355
|
if (!this.videoEl)
|
|
370
356
|
return;
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
357
|
+
// Draw at the video's native resolution — the downstream compressImage()
|
|
358
|
+
// call (see proceedWithPhoto) is the single place resolution gets capped
|
|
359
|
+
// (via computeScaleRatio, which never upscales). Resizing here too used
|
|
360
|
+
// to cap captures at 1280x960 before compressImage ever ran, silently
|
|
361
|
+
// undermining its higher resolution/quality target.
|
|
376
362
|
const canvas = document.createElement("canvas");
|
|
377
|
-
canvas.width =
|
|
378
|
-
canvas.height =
|
|
363
|
+
canvas.width = this.videoEl.videoWidth;
|
|
364
|
+
canvas.height = this.videoEl.videoHeight;
|
|
379
365
|
const ctx = canvas.getContext("2d");
|
|
380
366
|
if (!ctx)
|
|
381
367
|
return;
|
|
@@ -383,14 +369,15 @@ export class DocumentCapture {
|
|
|
383
369
|
canvas.toBlob(async (blob) => {
|
|
384
370
|
if (blob) {
|
|
385
371
|
this.stopCamera();
|
|
386
|
-
// Quality analysis runs synchronously over
|
|
387
|
-
// show a loading state so the capture->preview
|
|
388
|
-
// looks like a frozen/unresponsive tap on
|
|
372
|
+
// Quality analysis runs synchronously over the captured frame's
|
|
373
|
+
// pixel data — show a loading state so the capture->preview
|
|
374
|
+
// transition never looks like a frozen/unresponsive tap on
|
|
375
|
+
// slower devices.
|
|
389
376
|
this.showLoadingState("Analyzing photo...", theme);
|
|
390
377
|
const quality = await this._assessImageQuality(canvas, ctx);
|
|
391
378
|
this.showPreview(container, theme, blob, documentType, quality);
|
|
392
379
|
}
|
|
393
|
-
}, "image/jpeg", 0.
|
|
380
|
+
}, "image/jpeg", 0.92);
|
|
394
381
|
}
|
|
395
382
|
/**
|
|
396
383
|
* Runs the local sharpness/glare/contrast/brightness check against a
|
|
@@ -497,7 +484,8 @@ export class DocumentCapture {
|
|
|
497
484
|
}
|
|
498
485
|
useBtn.textContent = "Processing...";
|
|
499
486
|
useBtn.disabled = true;
|
|
500
|
-
|
|
487
|
+
// 2000x1500 @ 0.92 — see the matching comment on the upload-path call above.
|
|
488
|
+
this.compressImage(imageData, 2000, 1500, 0.92)
|
|
501
489
|
.then((compressed) => this.handleCaptureComplete(compressed, container, theme, documentType))
|
|
502
490
|
.catch(() => this.handleCaptureComplete(imageData, container, theme, documentType)); // fallback to original
|
|
503
491
|
};
|
|
@@ -582,78 +570,12 @@ export class DocumentCapture {
|
|
|
582
570
|
input.click();
|
|
583
571
|
}
|
|
584
572
|
stopCamera() {
|
|
585
|
-
|
|
586
|
-
this.stream.getTracks().forEach((t) => t.stop());
|
|
587
|
-
this.stream = null;
|
|
588
|
-
}
|
|
573
|
+
this.cameraManager.stop();
|
|
589
574
|
if (this.videoEl) {
|
|
590
575
|
this.videoEl.srcObject = null;
|
|
591
576
|
this.videoEl = null;
|
|
592
577
|
}
|
|
593
578
|
}
|
|
594
|
-
createEnhancedButton(label, theme, isPrimary, ariaLabel) {
|
|
595
|
-
const btn = document.createElement("button");
|
|
596
|
-
btn.textContent = label;
|
|
597
|
-
btn.setAttribute("aria-label", ariaLabel);
|
|
598
|
-
btn.style.cssText = `
|
|
599
|
-
padding:18px 24px;border-radius:16px;font-size:16px;font-weight:600;
|
|
600
|
-
cursor:pointer;transition:all 0.3s ease;border:none;width:100%;
|
|
601
|
-
min-height:56px;touch-action:manipulation;-webkit-tap-highlight-color:transparent;
|
|
602
|
-
position:relative;overflow:hidden;
|
|
603
|
-
${isPrimary
|
|
604
|
-
? `background:linear-gradient(135deg, ${theme.primary}, ${theme.primary}DD);color:#fff;box-shadow:0 8px 24px ${theme.primary}25, 0 4px 12px ${theme.primary}15;`
|
|
605
|
-
: `background:${theme.inputBg};color:${theme.text};border:2px solid ${theme.border};box-shadow:0 2px 8px ${theme.border}20;`}
|
|
606
|
-
`;
|
|
607
|
-
// Add ripple effect
|
|
608
|
-
btn.addEventListener("click", (e) => {
|
|
609
|
-
const ripple = document.createElement("span");
|
|
610
|
-
const rect = btn.getBoundingClientRect();
|
|
611
|
-
const size = Math.max(rect.width, rect.height);
|
|
612
|
-
const x = e.clientX - rect.left - size / 2;
|
|
613
|
-
const y = e.clientY - rect.top - size / 2;
|
|
614
|
-
ripple.style.cssText = `
|
|
615
|
-
position:absolute;width:${size}px;height:${size}px;
|
|
616
|
-
left:${x}px;top:${y}px;border-radius:50%;
|
|
617
|
-
background:${isPrimary ? 'rgba(255,255,255,0.3)' : theme.primary + '20'};
|
|
618
|
-
transform:scale(0);animation:ripple 0.6s ease-out;
|
|
619
|
-
pointer-events:none;
|
|
620
|
-
`;
|
|
621
|
-
btn.appendChild(ripple);
|
|
622
|
-
setTimeout(() => ripple.remove(), 600);
|
|
623
|
-
});
|
|
624
|
-
btn.addEventListener("mouseenter", () => {
|
|
625
|
-
btn.style.transform = "translateY(-2px)";
|
|
626
|
-
if (isPrimary) {
|
|
627
|
-
btn.style.boxShadow = `0 12px 32px ${theme.primary}30, 0 6px 16px ${theme.primary}20`;
|
|
628
|
-
}
|
|
629
|
-
else {
|
|
630
|
-
btn.style.boxShadow = `0 4px 16px ${theme.border}30, 0 2px 8px ${theme.border}20`;
|
|
631
|
-
btn.style.borderColor = theme.primary;
|
|
632
|
-
}
|
|
633
|
-
});
|
|
634
|
-
btn.addEventListener("mouseleave", () => {
|
|
635
|
-
btn.style.transform = "translateY(0)";
|
|
636
|
-
if (isPrimary) {
|
|
637
|
-
btn.style.boxShadow = `0 8px 24px ${theme.primary}25, 0 4px 12px ${theme.primary}15`;
|
|
638
|
-
}
|
|
639
|
-
else {
|
|
640
|
-
btn.style.boxShadow = `0 2px 8px ${theme.border}20`;
|
|
641
|
-
btn.style.borderColor = theme.border;
|
|
642
|
-
}
|
|
643
|
-
});
|
|
644
|
-
// Add ripple animation
|
|
645
|
-
if (!document.head.querySelector('style[data-ripple-animation]')) {
|
|
646
|
-
const style = document.createElement('style');
|
|
647
|
-
style.setAttribute('data-ripple-animation', 'true');
|
|
648
|
-
style.textContent = `
|
|
649
|
-
@keyframes ripple {
|
|
650
|
-
to { transform: scale(4); opacity: 0; }
|
|
651
|
-
}
|
|
652
|
-
`;
|
|
653
|
-
document.head.appendChild(style);
|
|
654
|
-
}
|
|
655
|
-
return btn;
|
|
656
|
-
}
|
|
657
579
|
showLoadingState(message, theme) {
|
|
658
580
|
if (!this.root)
|
|
659
581
|
return;
|
|
@@ -769,7 +691,7 @@ export class DocumentCapture {
|
|
|
769
691
|
const swapped = orientation >= 5 && orientation <= 8;
|
|
770
692
|
const srcW = swapped ? h : w;
|
|
771
693
|
const srcH = swapped ? w : h;
|
|
772
|
-
const ratio =
|
|
694
|
+
const ratio = computeScaleRatio(srcW, srcH, maxWidth, maxHeight);
|
|
773
695
|
const outW = Math.round(srcW * ratio);
|
|
774
696
|
const outH = Math.round(srcH * ratio);
|
|
775
697
|
const canvas = document.createElement("canvas");
|
|
@@ -816,7 +738,7 @@ export class DocumentCapture {
|
|
|
816
738
|
imgEl.onload = () => {
|
|
817
739
|
URL.revokeObjectURL(url);
|
|
818
740
|
let { naturalWidth: w, naturalHeight: h } = imgEl;
|
|
819
|
-
const ratio =
|
|
741
|
+
const ratio = computeScaleRatio(w, h, maxWidth, maxHeight);
|
|
820
742
|
const canvas = document.createElement("canvas");
|
|
821
743
|
canvas.width = Math.round(w * ratio);
|
|
822
744
|
canvas.height = Math.round(h * ratio);
|