datakeen-session-react 1.1.194-rc.0 → 1.1.194-rc.2

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.
@@ -11,10 +11,18 @@ var DEFAULT_RESOLUTION = {
11
11
  width: 4096,
12
12
  height: 3072,
13
13
  };
14
+ // Aligned with imageProcessing.ts's MAX_WIDTH (2048): the app downsizes every
15
+ // captured image to that width right after capture (DocumentCheck.tsx ->
16
+ // resizeAfterCapture), so synthetically upscaling beyond it here only adds a
17
+ // multi-second canvas encode (observed up to several seconds on mobile) for
18
+ // pixels that get thrown away a moment later. Previously targeted 4800x3200
19
+ // with up to 4x upscale — this caused the capture button to appear "stuck"
20
+ // for a few seconds after the flash, with no real quality benefit since the
21
+ // oversized image never survives the post-capture resize anyway.
14
22
  var QUALITY_REQUIREMENTS = {
15
- minArea: 3500 * 2200,
16
- targetArea: 4800 * 3200,
17
- minLongSide: 3200,
23
+ minArea: 1600000, // ~ 1600x1000, same proportion to targetArea as before
24
+ targetArea: 2048 * 1536,
25
+ minLongSide: 2048,
18
26
  };
19
27
  var supportsImageCapture = function () {
20
28
  return typeof window !== "undefined" && "ImageCapture" in window;
@@ -115,7 +123,7 @@ var CameraService = /** @class */ (function () {
115
123
  };
116
124
  CameraService.prototype._doStartCamera = function (videoElement, options) {
117
125
  return tslib_es6.__awaiter(this, void 0, void 0, function () {
118
- var facingMode, preferImageCapture, desiredWidth, desiredHeight, maxResolution, targetWidth, targetHeight, videoConstraints, stream, err_2, errorMessage;
126
+ var facingMode, preferImageCapture, desiredWidth, desiredHeight, videoConstraints, stream, err_2, errorMessage;
119
127
  var _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
120
128
  return tslib_es6.__generator(this, function (_o) {
121
129
  switch (_o.label) {
@@ -126,7 +134,7 @@ var CameraService = /** @class */ (function () {
126
134
  desiredHeight = (_g = (_f = options.idealResolution) === null || _f === void 0 ? void 0 : _f.height) !== null && _g !== void 0 ? _g : DEFAULT_RESOLUTION.height;
127
135
  _o.label = 1;
128
136
  case 1:
129
- _o.trys.push([1, 9, , 10]);
137
+ _o.trys.push([1, 8, , 9]);
130
138
  // Close any existing stream before opening a new one
131
139
  if (this.stream) {
132
140
  this.stream.getTracks().forEach(function (t) { return t.stop(); });
@@ -134,24 +142,10 @@ var CameraService = /** @class */ (function () {
134
142
  this.videoTrack = null;
135
143
  this.imageCapture = null;
136
144
  }
137
- return [4 /*yield*/, this.getMaxCameraResolution(facingMode, options.deviceId)];
138
- case 2:
139
- maxResolution = _o.sent();
140
- targetWidth = maxResolution
141
- ? Math.min(desiredWidth, maxResolution.width)
142
- : desiredWidth;
143
- targetHeight = maxResolution
144
- ? Math.min(desiredHeight, maxResolution.height)
145
- : desiredHeight;
146
- console.debug("[CameraService] Resolution negotiation", {
147
- desired: { width: desiredWidth, height: desiredHeight },
148
- deviceMax: maxResolution !== null && maxResolution !== void 0 ? maxResolution : "unknown",
149
- target: { width: targetWidth, height: targetHeight },
150
- });
151
145
  videoConstraints = {
152
146
  facingMode: facingMode,
153
- width: { ideal: targetWidth },
154
- height: { ideal: targetHeight },
147
+ width: { ideal: desiredWidth },
148
+ height: { ideal: desiredHeight },
155
149
  };
156
150
  if (options.deviceId) {
157
151
  videoConstraints.deviceId = { exact: options.deviceId };
@@ -162,25 +156,29 @@ var CameraService = /** @class */ (function () {
162
156
  return [4 /*yield*/, navigator.mediaDevices.getUserMedia({
163
157
  video: videoConstraints,
164
158
  })];
165
- case 3:
159
+ case 2:
166
160
  stream = _o.sent();
167
161
  this.stream = stream;
168
162
  this.videoTrack = (_j = stream.getVideoTracks()[0]) !== null && _j !== void 0 ? _j : null;
169
163
  this.lastTrackSettings = (_l = (_k = this.videoTrack) === null || _k === void 0 ? void 0 : _k.getSettings()) !== null && _l !== void 0 ? _l : null;
170
- if (!this.videoTrack) return [3 /*break*/, 7];
171
- _o.label = 4;
172
- case 4:
173
- _o.trys.push([4, 6, , 7]);
164
+ console.debug("[CameraService] Camera opened", {
165
+ desired: { width: desiredWidth, height: desiredHeight },
166
+ actual: this.lastTrackSettings,
167
+ });
168
+ if (!this.videoTrack) return [3 /*break*/, 6];
169
+ _o.label = 3;
170
+ case 3:
171
+ _o.trys.push([3, 5, , 6]);
174
172
  return [4 /*yield*/, this.videoTrack.applyConstraints({
175
173
  advanced: [{ focusMode: "continuous" }],
176
174
  })];
175
+ case 4:
176
+ _o.sent();
177
+ return [3 /*break*/, 6];
177
178
  case 5:
178
179
  _o.sent();
179
- return [3 /*break*/, 7];
180
+ return [3 /*break*/, 6];
180
181
  case 6:
181
- _o.sent();
182
- return [3 /*break*/, 7];
183
- case 7:
184
182
  if (supportsImageCapture() && this.videoTrack && preferImageCapture) {
185
183
  try {
186
184
  this.imageCapture = new ImageCapture(this.videoTrack);
@@ -198,14 +196,14 @@ var CameraService = /** @class */ (function () {
198
196
  }
199
197
  videoElement.srcObject = stream;
200
198
  return [4 /*yield*/, this.waitForVideoReady(videoElement)];
201
- case 8:
199
+ case 7:
202
200
  _o.sent();
203
201
  return [2 /*return*/, {
204
202
  success: true,
205
203
  settings: (_m = this.lastTrackSettings) !== null && _m !== void 0 ? _m : undefined,
206
204
  captureMethod: this.captureMethod,
207
205
  }];
208
- case 9:
206
+ case 8:
209
207
  err_2 = _o.sent();
210
208
  console.error("Erreur lors de l'accès à la caméra:", err_2);
211
209
  errorMessage = "Une erreur inconnue s'est produite lors de l'accès à la caméra.";
@@ -222,7 +220,7 @@ var CameraService = /** @class */ (function () {
222
220
  }
223
221
  }
224
222
  return [2 /*return*/, { success: false, error: errorMessage }];
225
- case 10: return [2 /*return*/];
223
+ case 9: return [2 /*return*/];
226
224
  }
227
225
  });
228
226
  });
@@ -347,60 +345,6 @@ var CameraService = /** @class */ (function () {
347
345
  CameraService.prototype.getCurrentCaptureMethod = function () {
348
346
  return this.captureMethod;
349
347
  };
350
- /**
351
- * Probes the camera to discover its maximum supported resolution.
352
- * Opens a temporary low-res stream, reads getCapabilities(), then closes it.
353
- * Returns null if capabilities cannot be determined (the caller should fallback to defaults).
354
- */
355
- CameraService.prototype.getMaxCameraResolution = function (facingMode, deviceId) {
356
- return tslib_es6.__awaiter(this, void 0, void 0, function () {
357
- var constraints, probeStream, track, maxWidth, maxHeight, capabilities, error_2;
358
- var _a, _b;
359
- return tslib_es6.__generator(this, function (_c) {
360
- switch (_c.label) {
361
- case 0:
362
- _c.trys.push([0, 2, , 3]);
363
- constraints = { facingMode: facingMode };
364
- if (deviceId) {
365
- constraints.deviceId = { exact: deviceId };
366
- }
367
- return [4 /*yield*/, navigator.mediaDevices.getUserMedia({
368
- video: constraints,
369
- })];
370
- case 1:
371
- probeStream = _c.sent();
372
- track = probeStream.getVideoTracks()[0];
373
- if (!track) {
374
- probeStream.getTracks().forEach(function (t) { return t.stop(); });
375
- return [2 /*return*/, null];
376
- }
377
- maxWidth = void 0;
378
- maxHeight = void 0;
379
- // getCapabilities() is supported on Chrome/Edge/Android but not all browsers
380
- if (typeof track.getCapabilities === "function") {
381
- capabilities = track.getCapabilities();
382
- maxWidth = (_a = capabilities.width) === null || _a === void 0 ? void 0 : _a.max;
383
- maxHeight = (_b = capabilities.height) === null || _b === void 0 ? void 0 : _b.max;
384
- }
385
- // Stop the probe stream immediately
386
- probeStream.getTracks().forEach(function (t) { return t.stop(); });
387
- if (maxWidth && maxHeight) {
388
- console.debug("[CameraService] Device max resolution", {
389
- width: maxWidth,
390
- height: maxHeight,
391
- });
392
- return [2 /*return*/, { width: maxWidth, height: maxHeight }];
393
- }
394
- return [2 /*return*/, null];
395
- case 2:
396
- error_2 = _c.sent();
397
- console.warn("[CameraService] Could not probe camera capabilities", error_2);
398
- return [2 /*return*/, null];
399
- case 3: return [2 /*return*/];
400
- }
401
- });
402
- });
403
- };
404
348
  CameraService.prototype.waitForVideoReady = function (video) {
405
349
  return tslib_es6.__awaiter(this, void 0, void 0, function () {
406
350
  var playError_1;
@@ -526,7 +470,7 @@ var CameraService = /** @class */ (function () {
526
470
  };
527
471
  CameraService.prototype.loadCanvasSourceFromBlob = function (blob) {
528
472
  return tslib_es6.__awaiter(this, void 0, void 0, function () {
529
- var bitmap_1, error_3, objectUrl, image;
473
+ var bitmap_1, error_2, objectUrl, image;
530
474
  return tslib_es6.__generator(this, function (_a) {
531
475
  switch (_a.label) {
532
476
  case 0:
@@ -550,8 +494,8 @@ var CameraService = /** @class */ (function () {
550
494
  },
551
495
  }];
552
496
  case 3:
553
- error_3 = _a.sent();
554
- console.warn("createImageBitmap a échoué, fallback sur HTMLImageElement", error_3);
497
+ error_2 = _a.sent();
498
+ console.warn("createImageBitmap a échoué, fallback sur HTMLImageElement", error_2);
555
499
  return [3 /*break*/, 4];
556
500
  case 4:
557
501
  objectUrl = URL.createObjectURL(blob);
@@ -1 +1 @@
1
- {"version":3,"file":"cameraService.js","sources":["../../../../src/services/cameraService.ts"],"sourcesContent":["import type { CameraFacingMode } from \"../types/camera\";\nimport { professionalImageQuality } from \"./professionalImageQuality\";\nimport type { QualityAnalysis } from \"./professionalImageQuality\";\n\n// Permit to enable development mode to test without a camera\nconst DEV_MODE = false; // import.meta.env.DEV;\n// Default image path for development mode\nconst DEFAULT_TEST_IMAGE = \"/mock-id-card.jpeg\";\n\ntype CaptureMethod = \"imageCapture\" | \"canvas\" | \"mock\";\n\nexport interface CameraStartOptions {\n facingMode?: CameraFacingMode;\n idealResolution?: {\n width?: number;\n height?: number;\n };\n deviceId?: string;\n advancedConstraints?: MediaTrackConstraints[];\n preferImageCapture?: boolean;\n}\n\nexport interface CameraStartResult {\n success: boolean;\n error?: string;\n settings?: MediaTrackSettings;\n captureMethod?: CaptureMethod;\n}\n\nexport interface DocumentCropArea {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nexport interface CaptureDocumentOptions {\n facingMode?: CameraFacingMode;\n format?: \"png\" | \"jpeg\";\n jpegQuality?: number;\n preferImageCapture?: boolean;\n}\n\nexport interface CaptureDocumentResult {\n dataUrl: string;\n method: CaptureMethod;\n analysis?: QualityAnalysis;\n sourceDimensions?: { width: number; height: number };\n cropDimensions?: { width: number; height: number };\n}\n\nconst DEFAULT_RESOLUTION = {\n width: 4096,\n height: 3072,\n};\n\nconst QUALITY_REQUIREMENTS = {\n minArea: 3500 * 2200,\n targetArea: 4800 * 3200,\n minLongSide: 3200,\n};\n\nconst supportsImageCapture = (): boolean =>\n typeof window !== \"undefined\" && \"ImageCapture\" in window;\n\ninterface CanvasSource {\n source: CanvasImageSource;\n width: number;\n height: number;\n cleanup?: () => void;\n}\n\nexport class CameraService {\n private stream: MediaStream | null = null;\n private videoTrack: MediaStreamTrack | null = null;\n private imageCapture: ImageCapture | null = null;\n private devMode: boolean = DEV_MODE;\n private mockImagePath: string = DEFAULT_TEST_IMAGE;\n private captureMethod: CaptureMethod = \"canvas\";\n private lastTrackSettings: MediaTrackSettings | null = null;\n private startingPromise: Promise<CameraStartResult> | null = null;\n\n private async startMockCamera(\n videoElement: HTMLVideoElement | null\n ): Promise<CameraStartResult> {\n try {\n if (!videoElement) {\n return { success: false, error: \"Élément vidéo non disponible\" };\n }\n\n const img = new Image();\n img.src = this.mockImagePath;\n\n await new Promise<void>((resolve, reject) => {\n img.onload = () => resolve();\n img.onerror = () =>\n reject(\n new Error(`Impossible de charger l'image: ${this.mockImagePath}`)\n );\n });\n\n const canvas = document.createElement(\"canvas\");\n canvas.width = img.width;\n canvas.height = img.height;\n const ctx = canvas.getContext(\"2d\");\n\n if (!ctx) {\n return { success: false, error: \"Contexte du canvas non disponible\" };\n }\n\n ctx.drawImage(img, 0, 0, canvas.width, canvas.height);\n\n const stream: MediaStream = canvas.captureStream(30);\n this.stream = stream;\n this.captureMethod = \"mock\";\n videoElement.srcObject = stream;\n\n return { success: true, captureMethod: \"mock\" };\n } catch (err) {\n console.error(\"Erreur lors de la création du flux vidéo mock:\", err);\n return {\n success: false,\n error: `Erreur lors de la création du flux vidéo mock: ${\n err instanceof Error ? err.message : String(err)\n }`,\n };\n }\n }\n\n async startCamera(\n videoElement: HTMLVideoElement | null,\n options: CameraStartOptions = {}\n ): Promise<CameraStartResult> {\n if (this.devMode) {\n return this.startMockCamera(videoElement);\n }\n\n if (!videoElement) {\n return { success: false, error: \"Élément vidéo non disponible\" };\n }\n\n if (!navigator?.mediaDevices?.getUserMedia) {\n return {\n success: false,\n error: \"La capture vidéo n'est pas supportée sur cet appareil.\",\n };\n }\n\n // Si un startCamera est déjà en cours, attendre qu'il se termine plutôt que d'en lancer un second\n if (this.startingPromise) {\n return this.startingPromise;\n }\n\n this.startingPromise = this._doStartCamera(videoElement!, options);\n try {\n return await this.startingPromise;\n } finally {\n this.startingPromise = null;\n }\n }\n\n private async _doStartCamera(\n videoElement: HTMLVideoElement,\n options: CameraStartOptions\n ): Promise<CameraStartResult> {\n const facingMode = options.facingMode ?? \"environment\";\n const preferImageCapture = options.preferImageCapture ?? true;\n const desiredWidth = options.idealResolution?.width ?? DEFAULT_RESOLUTION.width;\n const desiredHeight = options.idealResolution?.height ?? DEFAULT_RESOLUTION.height;\n\n try {\n // Close any existing stream before opening a new one\n if (this.stream) {\n this.stream.getTracks().forEach((t) => t.stop());\n this.stream = null;\n this.videoTrack = null;\n this.imageCapture = null;\n }\n\n // Step 1: Probe the camera to discover its maximum supported resolution\n const maxResolution = await this.getMaxCameraResolution(facingMode, options.deviceId);\n\n // Use the lesser of the desired and the device maximum\n const targetWidth = maxResolution\n ? Math.min(desiredWidth, maxResolution.width)\n : desiredWidth;\n const targetHeight = maxResolution\n ? Math.min(desiredHeight, maxResolution.height)\n : desiredHeight;\n\n console.debug(\"[CameraService] Resolution negotiation\", {\n desired: { width: desiredWidth, height: desiredHeight },\n deviceMax: maxResolution ?? \"unknown\",\n target: { width: targetWidth, height: targetHeight },\n });\n\n const videoConstraints: MediaTrackConstraints = {\n facingMode,\n width: { ideal: targetWidth },\n height: { ideal: targetHeight },\n };\n\n if (options.deviceId) {\n videoConstraints.deviceId = { exact: options.deviceId };\n }\n\n if (options.advancedConstraints?.length) {\n videoConstraints.advanced = options.advancedConstraints;\n }\n\n // Step 2: Open the stream with adapted constraints\n const stream = await navigator.mediaDevices.getUserMedia({\n video: videoConstraints,\n });\n\n this.stream = stream;\n this.videoTrack = stream.getVideoTracks()[0] ?? null;\n this.lastTrackSettings = this.videoTrack?.getSettings() ?? null;\n\n // Step 3: Apply continuous autofocus via applyConstraints after stream is open.\n // Done separately so a rejection (unsupported constraint) never kills the stream.\n if (this.videoTrack) {\n try {\n await this.videoTrack.applyConstraints({\n advanced: [{ focusMode: \"continuous\" } as MediaTrackConstraints],\n });\n } catch {\n // Device doesn't support focusMode — stream stays open, autofocus just won't activate\n }\n }\n\n if (supportsImageCapture() && this.videoTrack && preferImageCapture) {\n try {\n this.imageCapture = new ImageCapture(this.videoTrack);\n this.captureMethod = \"imageCapture\";\n } catch (error) {\n console.warn(\n \"ImageCapture API indisponible, fallback sur capture canvas\",\n error\n );\n this.imageCapture = null;\n this.captureMethod = \"canvas\";\n }\n } else {\n this.imageCapture = null;\n this.captureMethod = \"canvas\";\n }\n\n videoElement.srcObject = stream;\n\n await this.waitForVideoReady(videoElement);\n\n return {\n success: true,\n settings: this.lastTrackSettings ?? undefined,\n captureMethod: this.captureMethod,\n };\n } catch (err) {\n console.error(\"Erreur lors de l'accès à la caméra:\", err);\n\n let errorMessage =\n \"Une erreur inconnue s'est produite lors de l'accès à la caméra.\";\n\n if (err instanceof DOMException) {\n if (err.name === \"NotFoundError\") {\n errorMessage = \"Aucune caméra n'a été détectée sur votre appareil.\";\n } else if (err.name === \"NotAllowedError\") {\n errorMessage =\n \"L'accès à la caméra a été refusé. Veuillez autoriser l'accès dans les paramètres de votre navigateur.\";\n } else {\n errorMessage = `Impossible d'accéder à la caméra: ${err.message}`;\n }\n }\n\n return { success: false, error: errorMessage };\n }\n }\n\n async captureDocument(\n videoElement: HTMLVideoElement | null,\n cropArea?: DocumentCropArea | null,\n options: CaptureDocumentOptions = {}\n ): Promise<CaptureDocumentResult> {\n if (!videoElement) {\n throw new Error(\"Élément vidéo requis pour capturer le document\");\n }\n\n if (!videoElement.videoWidth || !videoElement.videoHeight) {\n throw new Error(\n \"La vidéo n'est pas prête. Impossible de déterminer les dimensions.\"\n );\n }\n\n const format = options.format ?? \"jpeg\";\n const jpegQuality = options.jpegQuality ?? 0.95;\n const facingMode = options.facingMode ?? \"environment\";\n const preferImageCapture = options.preferImageCapture ?? true;\n\n const effectiveCropArea: DocumentCropArea = cropArea ?? {\n x: 0,\n y: 0,\n width: videoElement.videoWidth,\n height: videoElement.videoHeight,\n };\n\n console.debug(\"[CameraService] captureDocument request\", {\n format,\n jpegQuality,\n facingMode,\n preferImageCapture,\n cropArea: effectiveCropArea,\n videoDimensions: {\n width: videoElement.videoWidth,\n height: videoElement.videoHeight,\n },\n });\n\n if (this.imageCapture && preferImageCapture) {\n try {\n const result = await this.captureWithImageCapture(\n videoElement,\n effectiveCropArea,\n {\n format,\n jpegQuality,\n facingMode,\n }\n );\n\n if (result) {\n console.debug(\"[CameraService] captureDocument via ImageCapture\", {\n cropArea: effectiveCropArea,\n result,\n });\n return result;\n }\n } catch (error) {\n console.warn(\n \"Capture haute résolution impossible, fallback sur capture vidéo\",\n error\n );\n }\n }\n\n const fallback = professionalImageQuality.autoEnhanceImage(\n videoElement,\n effectiveCropArea,\n {\n outputFormat: format,\n jpegQuality,\n flipHorizontal: facingMode === \"user\",\n ...QUALITY_REQUIREMENTS,\n }\n );\n\n console.debug(\"[CameraService] captureDocument via video fallback\", {\n cropArea: effectiveCropArea,\n analysis: fallback.analysis,\n method: this.captureMethod,\n sourceDimensions: {\n width: videoElement.videoWidth,\n height: videoElement.videoHeight,\n },\n });\n\n return {\n dataUrl: fallback.dataUrl,\n analysis: fallback.analysis,\n method: this.captureMethod,\n sourceDimensions: {\n width: videoElement.videoWidth,\n height: videoElement.videoHeight,\n },\n cropDimensions: {\n width: effectiveCropArea.width,\n height: effectiveCropArea.height,\n },\n };\n }\n\n captureFrame(\n videoElement: HTMLVideoElement | null,\n canvasElement: HTMLCanvasElement | null\n ): string | null {\n if (!videoElement || !canvasElement || !videoElement.videoWidth) {\n return null;\n }\n\n const ctx = canvasElement.getContext(\"2d\");\n if (!ctx) return null;\n\n canvasElement.width = videoElement.videoWidth;\n canvasElement.height = videoElement.videoHeight;\n\n ctx.drawImage(\n videoElement,\n 0,\n 0,\n canvasElement.width,\n canvasElement.height\n );\n\n return canvasElement.toDataURL(\"image/jpeg\");\n }\n\n stopCamera() {\n if (this.stream) {\n this.stream.getTracks().forEach((track) => track.stop());\n this.stream = null;\n }\n\n this.videoTrack = null;\n this.imageCapture = null;\n this.lastTrackSettings = null;\n this.captureMethod = this.devMode ? \"mock\" : \"canvas\";\n }\n\n setDevMode(enabled: boolean): void {\n this.devMode = enabled;\n }\n\n setMockImage(imagePath: string): void {\n this.mockImagePath = imagePath;\n }\n\n isDevModeEnabled(): boolean {\n return this.devMode;\n }\n\n getCurrentCaptureMethod(): CaptureMethod {\n return this.captureMethod;\n }\n\n /**\n * Probes the camera to discover its maximum supported resolution.\n * Opens a temporary low-res stream, reads getCapabilities(), then closes it.\n * Returns null if capabilities cannot be determined (the caller should fallback to defaults).\n */\n private async getMaxCameraResolution(\n facingMode: string,\n deviceId?: string\n ): Promise<{ width: number; height: number } | null> {\n try {\n const constraints: MediaTrackConstraints = { facingMode };\n if (deviceId) {\n constraints.deviceId = { exact: deviceId };\n }\n\n const probeStream = await navigator.mediaDevices.getUserMedia({\n video: constraints,\n });\n\n const track = probeStream.getVideoTracks()[0];\n if (!track) {\n probeStream.getTracks().forEach((t) => t.stop());\n return null;\n }\n\n let maxWidth: number | undefined;\n let maxHeight: number | undefined;\n\n // getCapabilities() is supported on Chrome/Edge/Android but not all browsers\n if (typeof track.getCapabilities === \"function\") {\n const capabilities = track.getCapabilities();\n maxWidth = (capabilities as Record<string, any>).width?.max;\n maxHeight = (capabilities as Record<string, any>).height?.max;\n }\n\n // Stop the probe stream immediately\n probeStream.getTracks().forEach((t) => t.stop());\n\n if (maxWidth && maxHeight) {\n console.debug(\"[CameraService] Device max resolution\", {\n width: maxWidth,\n height: maxHeight,\n });\n return { width: maxWidth, height: maxHeight };\n }\n\n return null;\n } catch (error) {\n console.warn(\"[CameraService] Could not probe camera capabilities\", error);\n return null;\n }\n }\n\n private async waitForVideoReady(video: HTMLVideoElement): Promise<void> {\n // Explicitly call play() — required on some Android devices even with autoplay attribute\n try {\n await video.play();\n } catch (playError) {\n console.warn(\"[CameraService] video.play() failed, relying on autoplay\", playError);\n }\n\n if (\n video.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA &&\n video.videoWidth > 0\n ) {\n return;\n }\n\n await new Promise<void>((resolve) => {\n const TIMEOUT_MS = 5000;\n\n const onReady = () => {\n clearTimeout(timeout);\n video.removeEventListener(\"loadeddata\", onReady);\n video.removeEventListener(\"playing\", onReady);\n resolve();\n };\n\n // Listen for both events — whichever fires first\n video.addEventListener(\"loadeddata\", onReady, { once: true });\n video.addEventListener(\"playing\", onReady, { once: true });\n\n // Safety timeout to avoid hanging indefinitely\n const timeout = setTimeout(() => {\n video.removeEventListener(\"loadeddata\", onReady);\n video.removeEventListener(\"playing\", onReady);\n console.warn(\"[CameraService] waitForVideoReady timed out after\", TIMEOUT_MS, \"ms\");\n resolve();\n }, TIMEOUT_MS);\n });\n }\n\n private async captureWithImageCapture(\n videoElement: HTMLVideoElement,\n cropArea: DocumentCropArea,\n options: Required<Pick<CaptureDocumentOptions, \"format\" | \"jpegQuality\">> & {\n facingMode: CameraFacingMode;\n }\n ): Promise<CaptureDocumentResult | null> {\n if (!this.imageCapture) {\n return null;\n }\n\n // Use the actual stream resolution for takePhoto to ensure consistency\n const photoSettings: PhotoSettings = {};\n if (this.lastTrackSettings?.width && this.lastTrackSettings?.height) {\n photoSettings.imageWidth = this.lastTrackSettings.width;\n photoSettings.imageHeight = this.lastTrackSettings.height;\n console.debug(\"[CameraService] takePhoto with explicit resolution\", photoSettings);\n }\n\n const blob = await this.imageCapture.takePhoto(photoSettings);\n const { source, width, height, cleanup } = await this.loadCanvasSourceFromBlob(\n blob\n );\n\n try {\n const ratioX = width / videoElement.videoWidth;\n const ratioY = height / videoElement.videoHeight;\n\n if (!isFinite(ratioX) || !isFinite(ratioY)) {\n throw new Error(\"Ratios de capture invalides\");\n }\n\n const scaled = this.scaleCropArea(cropArea, ratioX, ratioY, width, height);\n\n const analysis = professionalImageQuality.analyzeQuality(\n scaled.width,\n scaled.height,\n QUALITY_REQUIREMENTS\n );\n\n const outputWidth = Math.max(\n 1,\n Math.round(scaled.width * analysis.recommendedScale)\n );\n const outputHeight = Math.max(\n 1,\n Math.round(scaled.height * analysis.recommendedScale)\n );\n\n const canvas = document.createElement(\"canvas\");\n canvas.width = outputWidth;\n canvas.height = outputHeight;\n\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) {\n throw new Error(\"Impossible de créer le contexte de rendu\");\n }\n\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = \"high\";\n\n if (options.facingMode === \"user\") {\n ctx.translate(outputWidth, 0);\n ctx.scale(-1, 1);\n }\n\n ctx.drawImage(\n source,\n scaled.x,\n scaled.y,\n scaled.width,\n scaled.height,\n 0,\n 0,\n outputWidth,\n outputHeight\n );\n\n const dataUrl =\n options.format === \"jpeg\"\n ? canvas.toDataURL(\"image/jpeg\", options.jpegQuality)\n : canvas.toDataURL(\"image/png\");\n\n console.debug(\"[CameraService] ImageCapture crop applied\", {\n originalDimensions: { width, height },\n crop: scaled,\n output: { width: outputWidth, height: outputHeight },\n analysis,\n });\n\n return {\n dataUrl,\n analysis,\n method: \"imageCapture\",\n sourceDimensions: { width, height },\n cropDimensions: {\n width: scaled.width,\n height: scaled.height,\n },\n };\n } finally {\n if (source instanceof ImageBitmap && typeof source.close === \"function\") {\n source.close();\n }\n\n cleanup?.();\n }\n }\n\n private async loadCanvasSourceFromBlob(blob: Blob): Promise<CanvasSource> {\n if (typeof createImageBitmap === \"function\") {\n try {\n const bitmap = await createImageBitmap(blob, {\n imageOrientation: \"from-image\",\n } as ImageBitmapOptions);\n\n return {\n source: bitmap,\n width: bitmap.width,\n height: bitmap.height,\n cleanup: () => {\n if (typeof bitmap.close === \"function\") {\n bitmap.close();\n }\n },\n };\n } catch (error) {\n console.warn(\"createImageBitmap a échoué, fallback sur HTMLImageElement\", error);\n }\n }\n\n const objectUrl = URL.createObjectURL(blob);\n\n const image = await new Promise<HTMLImageElement>((resolve, reject) => {\n const img = new Image();\n img.onload = () => resolve(img);\n img.onerror = (event) => reject(event);\n img.src = objectUrl;\n });\n\n return {\n source: image,\n width: image.naturalWidth,\n height: image.naturalHeight,\n cleanup: () => URL.revokeObjectURL(objectUrl),\n };\n }\n\n private scaleCropArea(\n cropArea: DocumentCropArea,\n ratioX: number,\n ratioY: number,\n maxWidth: number,\n maxHeight: number\n ): { x: number; y: number; width: number; height: number } {\n const scaledX = Math.max(0, Math.round(cropArea.x * ratioX));\n const scaledY = Math.max(0, Math.round(cropArea.y * ratioY));\n const scaledWidth = Math.min(\n maxWidth - scaledX,\n Math.round(cropArea.width * ratioX)\n );\n const scaledHeight = Math.min(\n maxHeight - scaledY,\n Math.round(cropArea.height * ratioY)\n );\n\n if (scaledWidth <= 0 || scaledHeight <= 0) {\n throw new Error(\"Zone de capture invalide après scaling\");\n }\n\n return {\n x: scaledX,\n y: scaledY,\n width: scaledWidth,\n height: scaledHeight,\n };\n }\n}\n\nexport const cameraService = new CameraService();\n"],"names":["__awaiter","professionalImageQuality","__assign"],"mappings":"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"}
1
+ {"version":3,"file":"cameraService.js","sources":["../../../../src/services/cameraService.ts"],"sourcesContent":["import type { CameraFacingMode } from \"../types/camera\";\nimport { professionalImageQuality } from \"./professionalImageQuality\";\nimport type { QualityAnalysis } from \"./professionalImageQuality\";\n\n// Permit to enable development mode to test without a camera\nconst DEV_MODE = false; // import.meta.env.DEV;\n// Default image path for development mode\nconst DEFAULT_TEST_IMAGE = \"/mock-id-card.jpeg\";\n\ntype CaptureMethod = \"imageCapture\" | \"canvas\" | \"mock\";\n\nexport interface CameraStartOptions {\n facingMode?: CameraFacingMode;\n idealResolution?: {\n width?: number;\n height?: number;\n };\n deviceId?: string;\n advancedConstraints?: MediaTrackConstraints[];\n preferImageCapture?: boolean;\n}\n\nexport interface CameraStartResult {\n success: boolean;\n error?: string;\n settings?: MediaTrackSettings;\n captureMethod?: CaptureMethod;\n}\n\nexport interface DocumentCropArea {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nexport interface CaptureDocumentOptions {\n facingMode?: CameraFacingMode;\n format?: \"png\" | \"jpeg\";\n jpegQuality?: number;\n preferImageCapture?: boolean;\n}\n\nexport interface CaptureDocumentResult {\n dataUrl: string;\n method: CaptureMethod;\n analysis?: QualityAnalysis;\n sourceDimensions?: { width: number; height: number };\n cropDimensions?: { width: number; height: number };\n}\n\nconst DEFAULT_RESOLUTION = {\n width: 4096,\n height: 3072,\n};\n\n// Aligned with imageProcessing.ts's MAX_WIDTH (2048): the app downsizes every\n// captured image to that width right after capture (DocumentCheck.tsx ->\n// resizeAfterCapture), so synthetically upscaling beyond it here only adds a\n// multi-second canvas encode (observed up to several seconds on mobile) for\n// pixels that get thrown away a moment later. Previously targeted 4800x3200\n// with up to 4x upscale — this caused the capture button to appear \"stuck\"\n// for a few seconds after the flash, with no real quality benefit since the\n// oversized image never survives the post-capture resize anyway.\nconst QUALITY_REQUIREMENTS = {\n minArea: 1_600_000, // ~ 1600x1000, same proportion to targetArea as before\n targetArea: 2048 * 1536,\n minLongSide: 2048,\n};\n\nconst supportsImageCapture = (): boolean =>\n typeof window !== \"undefined\" && \"ImageCapture\" in window;\n\ninterface CanvasSource {\n source: CanvasImageSource;\n width: number;\n height: number;\n cleanup?: () => void;\n}\n\nexport class CameraService {\n private stream: MediaStream | null = null;\n private videoTrack: MediaStreamTrack | null = null;\n private imageCapture: ImageCapture | null = null;\n private devMode: boolean = DEV_MODE;\n private mockImagePath: string = DEFAULT_TEST_IMAGE;\n private captureMethod: CaptureMethod = \"canvas\";\n private lastTrackSettings: MediaTrackSettings | null = null;\n private startingPromise: Promise<CameraStartResult> | null = null;\n\n private async startMockCamera(\n videoElement: HTMLVideoElement | null\n ): Promise<CameraStartResult> {\n try {\n if (!videoElement) {\n return { success: false, error: \"Élément vidéo non disponible\" };\n }\n\n const img = new Image();\n img.src = this.mockImagePath;\n\n await new Promise<void>((resolve, reject) => {\n img.onload = () => resolve();\n img.onerror = () =>\n reject(\n new Error(`Impossible de charger l'image: ${this.mockImagePath}`)\n );\n });\n\n const canvas = document.createElement(\"canvas\");\n canvas.width = img.width;\n canvas.height = img.height;\n const ctx = canvas.getContext(\"2d\");\n\n if (!ctx) {\n return { success: false, error: \"Contexte du canvas non disponible\" };\n }\n\n ctx.drawImage(img, 0, 0, canvas.width, canvas.height);\n\n const stream: MediaStream = canvas.captureStream(30);\n this.stream = stream;\n this.captureMethod = \"mock\";\n videoElement.srcObject = stream;\n\n return { success: true, captureMethod: \"mock\" };\n } catch (err) {\n console.error(\"Erreur lors de la création du flux vidéo mock:\", err);\n return {\n success: false,\n error: `Erreur lors de la création du flux vidéo mock: ${\n err instanceof Error ? err.message : String(err)\n }`,\n };\n }\n }\n\n async startCamera(\n videoElement: HTMLVideoElement | null,\n options: CameraStartOptions = {}\n ): Promise<CameraStartResult> {\n if (this.devMode) {\n return this.startMockCamera(videoElement);\n }\n\n if (!videoElement) {\n return { success: false, error: \"Élément vidéo non disponible\" };\n }\n\n if (!navigator?.mediaDevices?.getUserMedia) {\n return {\n success: false,\n error: \"La capture vidéo n'est pas supportée sur cet appareil.\",\n };\n }\n\n // Si un startCamera est déjà en cours, attendre qu'il se termine plutôt que d'en lancer un second\n if (this.startingPromise) {\n return this.startingPromise;\n }\n\n this.startingPromise = this._doStartCamera(videoElement!, options);\n try {\n return await this.startingPromise;\n } finally {\n this.startingPromise = null;\n }\n }\n\n private async _doStartCamera(\n videoElement: HTMLVideoElement,\n options: CameraStartOptions\n ): Promise<CameraStartResult> {\n const facingMode = options.facingMode ?? \"environment\";\n const preferImageCapture = options.preferImageCapture ?? true;\n const desiredWidth = options.idealResolution?.width ?? DEFAULT_RESOLUTION.width;\n const desiredHeight = options.idealResolution?.height ?? DEFAULT_RESOLUTION.height;\n\n try {\n // Close any existing stream before opening a new one\n if (this.stream) {\n this.stream.getTracks().forEach((t) => t.stop());\n this.stream = null;\n this.videoTrack = null;\n this.imageCapture = null;\n }\n\n // Ask directly for the desired resolution as an \"ideal\" constraint — the\n // browser negotiates the closest resolution the device actually supports\n // in this single call. There is no separate probe stream: opening a\n // camera stream just to read its capabilities and closing it right after\n // doubled the hardware-open latency and could trigger a second\n // permission prompt on some mobile browsers.\n const videoConstraints: MediaTrackConstraints = {\n facingMode,\n width: { ideal: desiredWidth },\n height: { ideal: desiredHeight },\n };\n\n if (options.deviceId) {\n videoConstraints.deviceId = { exact: options.deviceId };\n }\n\n if (options.advancedConstraints?.length) {\n videoConstraints.advanced = options.advancedConstraints;\n }\n\n const stream = await navigator.mediaDevices.getUserMedia({\n video: videoConstraints,\n });\n\n this.stream = stream;\n this.videoTrack = stream.getVideoTracks()[0] ?? null;\n this.lastTrackSettings = this.videoTrack?.getSettings() ?? null;\n\n console.debug(\"[CameraService] Camera opened\", {\n desired: { width: desiredWidth, height: desiredHeight },\n actual: this.lastTrackSettings,\n });\n\n // Step 3: Apply continuous autofocus via applyConstraints after stream is open.\n // Done separately so a rejection (unsupported constraint) never kills the stream.\n if (this.videoTrack) {\n try {\n await this.videoTrack.applyConstraints({\n advanced: [{ focusMode: \"continuous\" } as MediaTrackConstraints],\n });\n } catch {\n // Device doesn't support focusMode — stream stays open, autofocus just won't activate\n }\n }\n\n if (supportsImageCapture() && this.videoTrack && preferImageCapture) {\n try {\n this.imageCapture = new ImageCapture(this.videoTrack);\n this.captureMethod = \"imageCapture\";\n } catch (error) {\n console.warn(\n \"ImageCapture API indisponible, fallback sur capture canvas\",\n error\n );\n this.imageCapture = null;\n this.captureMethod = \"canvas\";\n }\n } else {\n this.imageCapture = null;\n this.captureMethod = \"canvas\";\n }\n\n videoElement.srcObject = stream;\n\n await this.waitForVideoReady(videoElement);\n\n return {\n success: true,\n settings: this.lastTrackSettings ?? undefined,\n captureMethod: this.captureMethod,\n };\n } catch (err) {\n console.error(\"Erreur lors de l'accès à la caméra:\", err);\n\n let errorMessage =\n \"Une erreur inconnue s'est produite lors de l'accès à la caméra.\";\n\n if (err instanceof DOMException) {\n if (err.name === \"NotFoundError\") {\n errorMessage = \"Aucune caméra n'a été détectée sur votre appareil.\";\n } else if (err.name === \"NotAllowedError\") {\n errorMessage =\n \"L'accès à la caméra a été refusé. Veuillez autoriser l'accès dans les paramètres de votre navigateur.\";\n } else {\n errorMessage = `Impossible d'accéder à la caméra: ${err.message}`;\n }\n }\n\n return { success: false, error: errorMessage };\n }\n }\n\n async captureDocument(\n videoElement: HTMLVideoElement | null,\n cropArea?: DocumentCropArea | null,\n options: CaptureDocumentOptions = {}\n ): Promise<CaptureDocumentResult> {\n if (!videoElement) {\n throw new Error(\"Élément vidéo requis pour capturer le document\");\n }\n\n if (!videoElement.videoWidth || !videoElement.videoHeight) {\n throw new Error(\n \"La vidéo n'est pas prête. Impossible de déterminer les dimensions.\"\n );\n }\n\n const format = options.format ?? \"jpeg\";\n const jpegQuality = options.jpegQuality ?? 0.95;\n const facingMode = options.facingMode ?? \"environment\";\n const preferImageCapture = options.preferImageCapture ?? true;\n\n const effectiveCropArea: DocumentCropArea = cropArea ?? {\n x: 0,\n y: 0,\n width: videoElement.videoWidth,\n height: videoElement.videoHeight,\n };\n\n console.debug(\"[CameraService] captureDocument request\", {\n format,\n jpegQuality,\n facingMode,\n preferImageCapture,\n cropArea: effectiveCropArea,\n videoDimensions: {\n width: videoElement.videoWidth,\n height: videoElement.videoHeight,\n },\n });\n\n if (this.imageCapture && preferImageCapture) {\n try {\n const result = await this.captureWithImageCapture(\n videoElement,\n effectiveCropArea,\n {\n format,\n jpegQuality,\n facingMode,\n }\n );\n\n if (result) {\n console.debug(\"[CameraService] captureDocument via ImageCapture\", {\n cropArea: effectiveCropArea,\n result,\n });\n return result;\n }\n } catch (error) {\n console.warn(\n \"Capture haute résolution impossible, fallback sur capture vidéo\",\n error\n );\n }\n }\n\n const fallback = professionalImageQuality.autoEnhanceImage(\n videoElement,\n effectiveCropArea,\n {\n outputFormat: format,\n jpegQuality,\n flipHorizontal: facingMode === \"user\",\n ...QUALITY_REQUIREMENTS,\n }\n );\n\n console.debug(\"[CameraService] captureDocument via video fallback\", {\n cropArea: effectiveCropArea,\n analysis: fallback.analysis,\n method: this.captureMethod,\n sourceDimensions: {\n width: videoElement.videoWidth,\n height: videoElement.videoHeight,\n },\n });\n\n return {\n dataUrl: fallback.dataUrl,\n analysis: fallback.analysis,\n method: this.captureMethod,\n sourceDimensions: {\n width: videoElement.videoWidth,\n height: videoElement.videoHeight,\n },\n cropDimensions: {\n width: effectiveCropArea.width,\n height: effectiveCropArea.height,\n },\n };\n }\n\n captureFrame(\n videoElement: HTMLVideoElement | null,\n canvasElement: HTMLCanvasElement | null\n ): string | null {\n if (!videoElement || !canvasElement || !videoElement.videoWidth) {\n return null;\n }\n\n const ctx = canvasElement.getContext(\"2d\");\n if (!ctx) return null;\n\n canvasElement.width = videoElement.videoWidth;\n canvasElement.height = videoElement.videoHeight;\n\n ctx.drawImage(\n videoElement,\n 0,\n 0,\n canvasElement.width,\n canvasElement.height\n );\n\n return canvasElement.toDataURL(\"image/jpeg\");\n }\n\n stopCamera() {\n if (this.stream) {\n this.stream.getTracks().forEach((track) => track.stop());\n this.stream = null;\n }\n\n this.videoTrack = null;\n this.imageCapture = null;\n this.lastTrackSettings = null;\n this.captureMethod = this.devMode ? \"mock\" : \"canvas\";\n }\n\n setDevMode(enabled: boolean): void {\n this.devMode = enabled;\n }\n\n setMockImage(imagePath: string): void {\n this.mockImagePath = imagePath;\n }\n\n isDevModeEnabled(): boolean {\n return this.devMode;\n }\n\n getCurrentCaptureMethod(): CaptureMethod {\n return this.captureMethod;\n }\n\n private async waitForVideoReady(video: HTMLVideoElement): Promise<void> {\n // Explicitly call play() — required on some Android devices even with autoplay attribute\n try {\n await video.play();\n } catch (playError) {\n console.warn(\"[CameraService] video.play() failed, relying on autoplay\", playError);\n }\n\n if (\n video.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA &&\n video.videoWidth > 0\n ) {\n return;\n }\n\n await new Promise<void>((resolve) => {\n const TIMEOUT_MS = 5000;\n\n const onReady = () => {\n clearTimeout(timeout);\n video.removeEventListener(\"loadeddata\", onReady);\n video.removeEventListener(\"playing\", onReady);\n resolve();\n };\n\n // Listen for both events — whichever fires first\n video.addEventListener(\"loadeddata\", onReady, { once: true });\n video.addEventListener(\"playing\", onReady, { once: true });\n\n // Safety timeout to avoid hanging indefinitely\n const timeout = setTimeout(() => {\n video.removeEventListener(\"loadeddata\", onReady);\n video.removeEventListener(\"playing\", onReady);\n console.warn(\"[CameraService] waitForVideoReady timed out after\", TIMEOUT_MS, \"ms\");\n resolve();\n }, TIMEOUT_MS);\n });\n }\n\n private async captureWithImageCapture(\n videoElement: HTMLVideoElement,\n cropArea: DocumentCropArea,\n options: Required<Pick<CaptureDocumentOptions, \"format\" | \"jpegQuality\">> & {\n facingMode: CameraFacingMode;\n }\n ): Promise<CaptureDocumentResult | null> {\n if (!this.imageCapture) {\n return null;\n }\n\n // Use the actual stream resolution for takePhoto to ensure consistency\n const photoSettings: PhotoSettings = {};\n if (this.lastTrackSettings?.width && this.lastTrackSettings?.height) {\n photoSettings.imageWidth = this.lastTrackSettings.width;\n photoSettings.imageHeight = this.lastTrackSettings.height;\n console.debug(\"[CameraService] takePhoto with explicit resolution\", photoSettings);\n }\n\n const blob = await this.imageCapture.takePhoto(photoSettings);\n const { source, width, height, cleanup } = await this.loadCanvasSourceFromBlob(\n blob\n );\n\n try {\n const ratioX = width / videoElement.videoWidth;\n const ratioY = height / videoElement.videoHeight;\n\n if (!isFinite(ratioX) || !isFinite(ratioY)) {\n throw new Error(\"Ratios de capture invalides\");\n }\n\n const scaled = this.scaleCropArea(cropArea, ratioX, ratioY, width, height);\n\n const analysis = professionalImageQuality.analyzeQuality(\n scaled.width,\n scaled.height,\n QUALITY_REQUIREMENTS\n );\n\n const outputWidth = Math.max(\n 1,\n Math.round(scaled.width * analysis.recommendedScale)\n );\n const outputHeight = Math.max(\n 1,\n Math.round(scaled.height * analysis.recommendedScale)\n );\n\n const canvas = document.createElement(\"canvas\");\n canvas.width = outputWidth;\n canvas.height = outputHeight;\n\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) {\n throw new Error(\"Impossible de créer le contexte de rendu\");\n }\n\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = \"high\";\n\n if (options.facingMode === \"user\") {\n ctx.translate(outputWidth, 0);\n ctx.scale(-1, 1);\n }\n\n ctx.drawImage(\n source,\n scaled.x,\n scaled.y,\n scaled.width,\n scaled.height,\n 0,\n 0,\n outputWidth,\n outputHeight\n );\n\n const dataUrl =\n options.format === \"jpeg\"\n ? canvas.toDataURL(\"image/jpeg\", options.jpegQuality)\n : canvas.toDataURL(\"image/png\");\n\n console.debug(\"[CameraService] ImageCapture crop applied\", {\n originalDimensions: { width, height },\n crop: scaled,\n output: { width: outputWidth, height: outputHeight },\n analysis,\n });\n\n return {\n dataUrl,\n analysis,\n method: \"imageCapture\",\n sourceDimensions: { width, height },\n cropDimensions: {\n width: scaled.width,\n height: scaled.height,\n },\n };\n } finally {\n if (source instanceof ImageBitmap && typeof source.close === \"function\") {\n source.close();\n }\n\n cleanup?.();\n }\n }\n\n private async loadCanvasSourceFromBlob(blob: Blob): Promise<CanvasSource> {\n if (typeof createImageBitmap === \"function\") {\n try {\n const bitmap = await createImageBitmap(blob, {\n imageOrientation: \"from-image\",\n } as ImageBitmapOptions);\n\n return {\n source: bitmap,\n width: bitmap.width,\n height: bitmap.height,\n cleanup: () => {\n if (typeof bitmap.close === \"function\") {\n bitmap.close();\n }\n },\n };\n } catch (error) {\n console.warn(\"createImageBitmap a échoué, fallback sur HTMLImageElement\", error);\n }\n }\n\n const objectUrl = URL.createObjectURL(blob);\n\n const image = await new Promise<HTMLImageElement>((resolve, reject) => {\n const img = new Image();\n img.onload = () => resolve(img);\n img.onerror = (event) => reject(event);\n img.src = objectUrl;\n });\n\n return {\n source: image,\n width: image.naturalWidth,\n height: image.naturalHeight,\n cleanup: () => URL.revokeObjectURL(objectUrl),\n };\n }\n\n private scaleCropArea(\n cropArea: DocumentCropArea,\n ratioX: number,\n ratioY: number,\n maxWidth: number,\n maxHeight: number\n ): { x: number; y: number; width: number; height: number } {\n const scaledX = Math.max(0, Math.round(cropArea.x * ratioX));\n const scaledY = Math.max(0, Math.round(cropArea.y * ratioY));\n const scaledWidth = Math.min(\n maxWidth - scaledX,\n Math.round(cropArea.width * ratioX)\n );\n const scaledHeight = Math.min(\n maxHeight - scaledY,\n Math.round(cropArea.height * ratioY)\n );\n\n if (scaledWidth <= 0 || scaledHeight <= 0) {\n throw new Error(\"Zone de capture invalide après scaling\");\n }\n\n return {\n x: scaledX,\n y: scaledY,\n width: scaledWidth,\n height: scaledHeight,\n };\n }\n}\n\nexport const cameraService = new CameraService();\n"],"names":["__awaiter","professionalImageQuality","__assign"],"mappings":"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"}
@@ -0,0 +1,182 @@
1
+ 'use strict';
2
+
3
+ var tslib_es6 = require('../node_modules/tslib/tslib.es6.js');
4
+
5
+ // Détection de document légère basée sur des contours (Canvas 2D), sans modèle
6
+ // ML externe. Remplace l'ancienne approche TensorFlow.js + coco-ssd, qui
7
+ // classait le document via des classes d'objets génériques ("book", "cell
8
+ // phone", "tv", "laptop") — peu fiable pour une CNI/passeport et coûteuse à
9
+ // charger (téléchargement de modèle + init WebGL), ce qui contribuait à la
10
+ // lenteur perçue à l'ouverture de la caméra.
11
+ //
12
+ // Le service analyse la luminosité et la netteté (variance du Laplacien, une
13
+ // mesure classique de flou) de la zone du cadre-guide, et en déduit un signal
14
+ // de présence de document ainsi qu'un indice ("hint") exploitable pour guider
15
+ // l'utilisateur (trop sombre / trop de reflets / rien à cadrer). Il ne bloque
16
+ // jamais la capture : c'est un signal d'aide, pas une porte.
17
+ var DETECTION_THRESHOLDS = {
18
+ minBrightness: 0.12,
19
+ maxBrightness: 0.92,
20
+ minSharpness: 0.08,
21
+ };
22
+ var STABILITY_WINDOW = 4; // détections positives consécutives requises avant isStable=true
23
+ var DETECTION_INTERVAL_MS = 200;
24
+ var SAMPLE_WIDTH = 64;
25
+ var SAMPLE_HEIGHT = 64;
26
+ /**
27
+ * Analyse pure d'une frame : luminosité moyenne + netteté (variance du
28
+ * Laplacien). Pas d'état, pas de dépendance DOM — facilement testable avec
29
+ * des buffers de pixels synthétiques.
30
+ */
31
+ function analyzeDocumentFrame(imageData) {
32
+ var data = imageData.data, width = imageData.width, height = imageData.height;
33
+ var pixelCount = width * height;
34
+ var gray = new Float32Array(pixelCount);
35
+ var sum = 0;
36
+ for (var i = 0, p = 0; i < data.length; i += 4, p++) {
37
+ var g = (data[i] + data[i + 1] + data[i + 2]) / (3 * 255);
38
+ gray[p] = g;
39
+ sum += g;
40
+ }
41
+ var brightness = pixelCount > 0 ? sum / pixelCount : 0;
42
+ var laplacianAbsSum = 0;
43
+ var samples = 0;
44
+ for (var y = 1; y < height - 1; y++) {
45
+ for (var x = 1; x < width - 1; x++) {
46
+ var idx = y * width + x;
47
+ var laplacian = gray[idx - 1] + gray[idx + 1] + gray[idx - width] + gray[idx + width] - 4 * gray[idx];
48
+ laplacianAbsSum += Math.abs(laplacian);
49
+ samples++;
50
+ }
51
+ }
52
+ var meanAbsLaplacian = samples > 0 ? laplacianAbsSum / samples : 0;
53
+ // Normalisation empirique : un damier noir/blanc pur (contraste maximal) donne ~4.
54
+ var sharpness = Math.min(1, meanAbsLaplacian / 4);
55
+ return { brightness: brightness, sharpness: sharpness };
56
+ }
57
+ /**
58
+ * Décision pure hasDocument/hint à partir des métriques de qualité. Ordre de
59
+ * priorité des hints : luminosité (trop sombre/trop de reflets) avant absence
60
+ * de contours, car une image trop sombre ou surexposée ne peut de toute façon
61
+ * pas révéler de contours fiables.
62
+ */
63
+ function decideDetection(quality) {
64
+ if (quality.brightness < DETECTION_THRESHOLDS.minBrightness) {
65
+ return { hasDocument: false, hint: "too_dark" };
66
+ }
67
+ if (quality.brightness > DETECTION_THRESHOLDS.maxBrightness) {
68
+ return { hasDocument: false, hint: "too_bright" };
69
+ }
70
+ if (quality.sharpness < DETECTION_THRESHOLDS.minSharpness) {
71
+ return { hasDocument: false, hint: "no_document" };
72
+ }
73
+ return { hasDocument: true, hint: "ok" };
74
+ }
75
+ /**
76
+ * Échantillonneur par défaut : downscale la zone (ou toute la frame vidéo si
77
+ * aucune région n'est fournie) vers un petit canvas fixe, pour un coût CPU
78
+ * constant et faible quelle que soit la résolution source.
79
+ */
80
+ var defaultSampleFrame = function (video, region) {
81
+ var _a, _b, _c, _d;
82
+ var sourceWidth = (_a = region === null || region === void 0 ? void 0 : region.width) !== null && _a !== void 0 ? _a : video.videoWidth;
83
+ var sourceHeight = (_b = region === null || region === void 0 ? void 0 : region.height) !== null && _b !== void 0 ? _b : video.videoHeight;
84
+ if (!sourceWidth || !sourceHeight) {
85
+ return null;
86
+ }
87
+ var canvas = document.createElement("canvas");
88
+ canvas.width = SAMPLE_WIDTH;
89
+ canvas.height = SAMPLE_HEIGHT;
90
+ var ctx = canvas.getContext("2d");
91
+ if (!ctx) {
92
+ return null;
93
+ }
94
+ ctx.drawImage(video, (_c = region === null || region === void 0 ? void 0 : region.x) !== null && _c !== void 0 ? _c : 0, (_d = region === null || region === void 0 ? void 0 : region.y) !== null && _d !== void 0 ? _d : 0, sourceWidth, sourceHeight, 0, 0, SAMPLE_WIDTH, SAMPLE_HEIGHT);
95
+ return ctx.getImageData(0, 0, SAMPLE_WIDTH, SAMPLE_HEIGHT);
96
+ };
97
+ var DocumentEdgeDetectionService = /** @class */ (function () {
98
+ function DocumentEdgeDetectionService(sampleFrame) {
99
+ if (sampleFrame === void 0) { sampleFrame = defaultSampleFrame; }
100
+ this.detectionTimeoutId = null;
101
+ this.stopped = true;
102
+ this.consecutiveMatches = 0;
103
+ this.lastRawHasDocument = null;
104
+ this.sampleFrame = sampleFrame;
105
+ }
106
+ DocumentEdgeDetectionService.prototype.startDocumentDetection = function (videoElement, region, onDetectionChange) {
107
+ return tslib_es6.__awaiter(this, void 0, void 0, function () {
108
+ var detect;
109
+ var _this = this;
110
+ return tslib_es6.__generator(this, function (_a) {
111
+ this.stopDocumentDetection();
112
+ this.stopped = false;
113
+ this.consecutiveMatches = 0;
114
+ this.lastRawHasDocument = null;
115
+ detect = function () {
116
+ if (_this.stopped) {
117
+ return;
118
+ }
119
+ if (!videoElement || !videoElement.videoWidth) {
120
+ _this.scheduleNext(detect);
121
+ return;
122
+ }
123
+ var imageData;
124
+ try {
125
+ imageData = _this.sampleFrame(videoElement, region);
126
+ }
127
+ catch (error) {
128
+ console.error("[DocumentEdgeDetection] Error sampling frame:", error);
129
+ _this.scheduleNext(detect);
130
+ return;
131
+ }
132
+ if (!imageData) {
133
+ _this.scheduleNext(detect);
134
+ return;
135
+ }
136
+ var quality = analyzeDocumentFrame(imageData);
137
+ var _a = decideDetection(quality), hasDocument = _a.hasDocument, hint = _a.hint;
138
+ if (hasDocument && _this.lastRawHasDocument) {
139
+ _this.consecutiveMatches += 1;
140
+ }
141
+ else {
142
+ _this.consecutiveMatches = hasDocument ? 1 : 0;
143
+ }
144
+ _this.lastRawHasDocument = hasDocument;
145
+ var isStable = hasDocument && _this.consecutiveMatches >= STABILITY_WINDOW;
146
+ onDetectionChange({
147
+ hasDocument: hasDocument,
148
+ isStable: isStable,
149
+ hint: hint,
150
+ quality: quality,
151
+ timestamp: Date.now(),
152
+ });
153
+ _this.scheduleNext(detect);
154
+ };
155
+ detect();
156
+ return [2 /*return*/, { stop: function () { return _this.stopDocumentDetection(); } }];
157
+ });
158
+ });
159
+ };
160
+ DocumentEdgeDetectionService.prototype.stopDocumentDetection = function () {
161
+ this.stopped = true;
162
+ if (this.detectionTimeoutId) {
163
+ clearTimeout(this.detectionTimeoutId);
164
+ this.detectionTimeoutId = null;
165
+ }
166
+ };
167
+ DocumentEdgeDetectionService.prototype.scheduleNext = function (callback) {
168
+ if (this.detectionTimeoutId) {
169
+ clearTimeout(this.detectionTimeoutId);
170
+ }
171
+ this.detectionTimeoutId = window.setTimeout(callback, DETECTION_INTERVAL_MS);
172
+ };
173
+ return DocumentEdgeDetectionService;
174
+ }());
175
+ var documentEdgeDetectionService = new DocumentEdgeDetectionService();
176
+
177
+ exports.DETECTION_THRESHOLDS = DETECTION_THRESHOLDS;
178
+ exports.DocumentEdgeDetectionService = DocumentEdgeDetectionService;
179
+ exports.analyzeDocumentFrame = analyzeDocumentFrame;
180
+ exports.decideDetection = decideDetection;
181
+ exports.documentEdgeDetectionService = documentEdgeDetectionService;
182
+ //# sourceMappingURL=documentEdgeDetectionService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"documentEdgeDetectionService.js","sources":["../../../../src/services/documentEdgeDetectionService.ts"],"sourcesContent":["// Détection de document légère basée sur des contours (Canvas 2D), sans modèle\n// ML externe. Remplace l'ancienne approche TensorFlow.js + coco-ssd, qui\n// classait le document via des classes d'objets génériques (\"book\", \"cell\n// phone\", \"tv\", \"laptop\") — peu fiable pour une CNI/passeport et coûteuse à\n// charger (téléchargement de modèle + init WebGL), ce qui contribuait à la\n// lenteur perçue à l'ouverture de la caméra.\n//\n// Le service analyse la luminosité et la netteté (variance du Laplacien, une\n// mesure classique de flou) de la zone du cadre-guide, et en déduit un signal\n// de présence de document ainsi qu'un indice (\"hint\") exploitable pour guider\n// l'utilisateur (trop sombre / trop de reflets / rien à cadrer). Il ne bloque\n// jamais la capture : c'est un signal d'aide, pas une porte.\n\nexport interface ImageDataLike {\n data: Uint8ClampedArray;\n width: number;\n height: number;\n}\n\nexport interface DetectionRegion {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nexport type DocumentDetectionHint = \"too_dark\" | \"too_bright\" | \"no_document\" | \"ok\";\n\nexport interface DocumentQuality {\n brightness: number; // 0..1\n sharpness: number; // 0..1 (normalisé)\n}\n\nexport interface DocumentDetectionResult {\n hasDocument: boolean;\n isStable: boolean;\n hint: DocumentDetectionHint;\n quality: DocumentQuality;\n timestamp: number;\n}\n\nexport type FrameSampler = (\n video: HTMLVideoElement,\n region: DetectionRegion | null\n) => ImageDataLike | null;\n\nexport const DETECTION_THRESHOLDS = {\n minBrightness: 0.12,\n maxBrightness: 0.92,\n minSharpness: 0.08,\n};\n\nconst STABILITY_WINDOW = 4; // détections positives consécutives requises avant isStable=true\nconst DETECTION_INTERVAL_MS = 200;\nconst SAMPLE_WIDTH = 64;\nconst SAMPLE_HEIGHT = 64;\n\n/**\n * Analyse pure d'une frame : luminosité moyenne + netteté (variance du\n * Laplacien). Pas d'état, pas de dépendance DOM — facilement testable avec\n * des buffers de pixels synthétiques.\n */\nexport function analyzeDocumentFrame(imageData: ImageDataLike): DocumentQuality {\n const { data, width, height } = imageData;\n const pixelCount = width * height;\n\n const gray = new Float32Array(pixelCount);\n let sum = 0;\n for (let i = 0, p = 0; i < data.length; i += 4, p++) {\n const g = (data[i] + data[i + 1] + data[i + 2]) / (3 * 255);\n gray[p] = g;\n sum += g;\n }\n const brightness = pixelCount > 0 ? sum / pixelCount : 0;\n\n let laplacianAbsSum = 0;\n let samples = 0;\n for (let y = 1; y < height - 1; y++) {\n for (let x = 1; x < width - 1; x++) {\n const idx = y * width + x;\n const laplacian =\n gray[idx - 1] + gray[idx + 1] + gray[idx - width] + gray[idx + width] - 4 * gray[idx];\n laplacianAbsSum += Math.abs(laplacian);\n samples++;\n }\n }\n const meanAbsLaplacian = samples > 0 ? laplacianAbsSum / samples : 0;\n // Normalisation empirique : un damier noir/blanc pur (contraste maximal) donne ~4.\n const sharpness = Math.min(1, meanAbsLaplacian / 4);\n\n return { brightness, sharpness };\n}\n\n/**\n * Décision pure hasDocument/hint à partir des métriques de qualité. Ordre de\n * priorité des hints : luminosité (trop sombre/trop de reflets) avant absence\n * de contours, car une image trop sombre ou surexposée ne peut de toute façon\n * pas révéler de contours fiables.\n */\nexport function decideDetection(\n quality: DocumentQuality\n): { hasDocument: boolean; hint: DocumentDetectionHint } {\n if (quality.brightness < DETECTION_THRESHOLDS.minBrightness) {\n return { hasDocument: false, hint: \"too_dark\" };\n }\n if (quality.brightness > DETECTION_THRESHOLDS.maxBrightness) {\n return { hasDocument: false, hint: \"too_bright\" };\n }\n if (quality.sharpness < DETECTION_THRESHOLDS.minSharpness) {\n return { hasDocument: false, hint: \"no_document\" };\n }\n return { hasDocument: true, hint: \"ok\" };\n}\n\n/**\n * Échantillonneur par défaut : downscale la zone (ou toute la frame vidéo si\n * aucune région n'est fournie) vers un petit canvas fixe, pour un coût CPU\n * constant et faible quelle que soit la résolution source.\n */\nconst defaultSampleFrame: FrameSampler = (video, region) => {\n const sourceWidth = region?.width ?? video.videoWidth;\n const sourceHeight = region?.height ?? video.videoHeight;\n if (!sourceWidth || !sourceHeight) {\n return null;\n }\n\n const canvas = document.createElement(\"canvas\");\n canvas.width = SAMPLE_WIDTH;\n canvas.height = SAMPLE_HEIGHT;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) {\n return null;\n }\n\n ctx.drawImage(\n video,\n region?.x ?? 0,\n region?.y ?? 0,\n sourceWidth,\n sourceHeight,\n 0,\n 0,\n SAMPLE_WIDTH,\n SAMPLE_HEIGHT\n );\n\n return ctx.getImageData(0, 0, SAMPLE_WIDTH, SAMPLE_HEIGHT);\n};\n\nexport class DocumentEdgeDetectionService {\n private detectionTimeoutId: number | null = null;\n private stopped = true;\n private consecutiveMatches = 0;\n private lastRawHasDocument: boolean | null = null;\n private readonly sampleFrame: FrameSampler;\n\n constructor(sampleFrame: FrameSampler = defaultSampleFrame) {\n this.sampleFrame = sampleFrame;\n }\n\n async startDocumentDetection(\n videoElement: HTMLVideoElement | null,\n region: DetectionRegion | null,\n onDetectionChange: (result: DocumentDetectionResult) => void\n ): Promise<{ stop: () => void }> {\n this.stopDocumentDetection();\n this.stopped = false;\n this.consecutiveMatches = 0;\n this.lastRawHasDocument = null;\n\n const detect = () => {\n if (this.stopped) {\n return;\n }\n\n if (!videoElement || !videoElement.videoWidth) {\n this.scheduleNext(detect);\n return;\n }\n\n let imageData: ImageDataLike | null;\n try {\n imageData = this.sampleFrame(videoElement, region);\n } catch (error) {\n console.error(\"[DocumentEdgeDetection] Error sampling frame:\", error);\n this.scheduleNext(detect);\n return;\n }\n\n if (!imageData) {\n this.scheduleNext(detect);\n return;\n }\n\n const quality = analyzeDocumentFrame(imageData);\n const { hasDocument, hint } = decideDetection(quality);\n\n if (hasDocument && this.lastRawHasDocument) {\n this.consecutiveMatches += 1;\n } else {\n this.consecutiveMatches = hasDocument ? 1 : 0;\n }\n this.lastRawHasDocument = hasDocument;\n\n const isStable = hasDocument && this.consecutiveMatches >= STABILITY_WINDOW;\n\n onDetectionChange({\n hasDocument,\n isStable,\n hint,\n quality,\n timestamp: Date.now(),\n });\n\n this.scheduleNext(detect);\n };\n\n detect();\n\n return { stop: () => this.stopDocumentDetection() };\n }\n\n stopDocumentDetection() {\n this.stopped = true;\n if (this.detectionTimeoutId) {\n clearTimeout(this.detectionTimeoutId);\n this.detectionTimeoutId = null;\n }\n }\n\n private scheduleNext(callback: () => void) {\n if (this.detectionTimeoutId) {\n clearTimeout(this.detectionTimeoutId);\n }\n this.detectionTimeoutId = window.setTimeout(callback, DETECTION_INTERVAL_MS);\n }\n}\n\nexport const documentEdgeDetectionService = new DocumentEdgeDetectionService();\n"],"names":[],"mappings":"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"}
@@ -4,7 +4,7 @@ import { useI18n } from '../../hooks/useI18n.js';
4
4
 
5
5
  var COUNTDOWN_DURATION = 3; // seconds
6
6
  var CameraMask = function (_a) {
7
- var _b = _a.isPortrait, isPortrait = _b === void 0 ? false : _b, _c = _a.facingMode, facingMode = _c === void 0 ? "environment" : _c, _d = _a.isCountingDown, isCountingDown = _d === void 0 ? false : _d, _e = _a.countdown, countdown = _e === void 0 ? 0 : _e, _f = _a.maskType, maskType = _f === void 0 ? "id" : _f, _g = _a.documentDetected, documentDetected = _g === void 0 ? false : _g, onMaskRectChange = _a.onMaskRectChange;
7
+ var _b = _a.isPortrait, isPortrait = _b === void 0 ? false : _b, _c = _a.facingMode, facingMode = _c === void 0 ? "environment" : _c, _d = _a.isCountingDown, isCountingDown = _d === void 0 ? false : _d, _e = _a.countdown, countdown = _e === void 0 ? 0 : _e, _f = _a.maskType, maskType = _f === void 0 ? "id" : _f, _g = _a.documentDetected, documentDetected = _g === void 0 ? false : _g, qualityHint = _a.qualityHint, onMaskRectChange = _a.onMaskRectChange;
8
8
  var _h = useState(null), videoOrientation = _h[0], setVideoOrientation = _h[1];
9
9
  var t = useI18n().t;
10
10
  var maskRef = useRef(null);
@@ -76,7 +76,11 @@ var CameraMask = function (_a) {
76
76
  width: maskType === "document" ? (effectiveIsPortrait ? "85%" : "60%") : (effectiveIsPortrait ? "95%" : "85%"),
77
77
  }, "data-mask": maskType === "document" ? "document-frame" : "id-frame", ref: maskRef, children: [jsx("div", { "data-testid": "camera-mask-border", className: "absolute inset-0 transition-colors duration-300 ease-in-out", style: {
78
78
  borderWidth: "3px",
79
- borderColor: documentDetected ? "#11E5C5" : "#ffffff",
79
+ borderColor: documentDetected
80
+ ? "#11E5C5"
81
+ : qualityHint === "too_dark" || qualityHint === "too_bright"
82
+ ? "#F59E0B"
83
+ : "#ffffff",
80
84
  borderStyle: documentDetected ? "solid" : "dashed",
81
85
  borderRadius: "8px",
82
86
  } }), documentDetected && (jsxs("div", { "data-testid": "document-detected-badge", className: "absolute -top-9 left-1/2 -translate-x-1/2 flex items-center gap-1 bg-[#11E5C5] text-white text-xs font-semibold px-2 py-1 rounded-full whitespace-nowrap", children: [jsx("span", { "aria-hidden": "true", children: "\u2713" }), jsx("span", { children: t("id_check.camera.document_detected", "Document détecté") })] })), isCountingDown && (jsx("svg", { className: "absolute inset-0 w-full h-full", style: {