@capgo/camera-preview 7.4.0-beta.9 → 7.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +242 -50
  2. package/android/gradle/wrapper/gradle-wrapper.properties +1 -1
  3. package/android/src/main/java/com/ahm/capacitor/camera/preview/CameraPreview.java +1249 -143
  4. package/android/src/main/java/com/ahm/capacitor/camera/preview/CameraXView.java +3400 -1432
  5. package/android/src/main/java/com/ahm/capacitor/camera/preview/GridOverlayView.java +95 -58
  6. package/android/src/main/java/com/ahm/capacitor/camera/preview/model/CameraDevice.java +55 -46
  7. package/android/src/main/java/com/ahm/capacitor/camera/preview/model/CameraLens.java +61 -52
  8. package/android/src/main/java/com/ahm/capacitor/camera/preview/model/CameraSessionConfiguration.java +160 -72
  9. package/android/src/main/java/com/ahm/capacitor/camera/preview/model/LensInfo.java +29 -23
  10. package/android/src/main/java/com/ahm/capacitor/camera/preview/model/ZoomFactors.java +24 -23
  11. package/dist/docs.json +441 -40
  12. package/dist/esm/definitions.d.ts +167 -25
  13. package/dist/esm/definitions.js.map +1 -1
  14. package/dist/esm/index.d.ts +2 -0
  15. package/dist/esm/index.js +24 -1
  16. package/dist/esm/index.js.map +1 -1
  17. package/dist/esm/web.d.ts +23 -3
  18. package/dist/esm/web.js +463 -65
  19. package/dist/esm/web.js.map +1 -1
  20. package/dist/plugin.cjs.js +485 -64
  21. package/dist/plugin.cjs.js.map +1 -1
  22. package/dist/plugin.js +485 -64
  23. package/dist/plugin.js.map +1 -1
  24. package/ios/Sources/{CapgoCameraPreview → CapgoCameraPreviewPlugin}/CameraController.swift +731 -315
  25. package/ios/Sources/CapgoCameraPreviewPlugin/Plugin.swift +1902 -0
  26. package/package.json +11 -3
  27. package/android/.gradle/8.14.2/checksums/checksums.lock +0 -0
  28. package/android/.gradle/8.14.2/checksums/md5-checksums.bin +0 -0
  29. package/android/.gradle/8.14.2/checksums/sha1-checksums.bin +0 -0
  30. package/android/.gradle/8.14.2/executionHistory/executionHistory.bin +0 -0
  31. package/android/.gradle/8.14.2/executionHistory/executionHistory.lock +0 -0
  32. package/android/.gradle/8.14.2/fileChanges/last-build.bin +0 -0
  33. package/android/.gradle/8.14.2/fileHashes/fileHashes.bin +0 -0
  34. package/android/.gradle/8.14.2/fileHashes/fileHashes.lock +0 -0
  35. package/android/.gradle/8.14.2/fileHashes/resourceHashesCache.bin +0 -0
  36. package/android/.gradle/8.14.2/gc.properties +0 -0
  37. package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  38. package/android/.gradle/buildOutputCleanup/cache.properties +0 -2
  39. package/android/.gradle/buildOutputCleanup/outputFiles.bin +0 -0
  40. package/android/.gradle/file-system.probe +0 -0
  41. package/android/.gradle/vcs-1/gc.properties +0 -0
  42. package/ios/Sources/CapgoCameraPreview/Plugin.swift +0 -1369
  43. /package/ios/Sources/{CapgoCameraPreview → CapgoCameraPreviewPlugin}/GridOverlayView.swift +0 -0
package/dist/plugin.js CHANGED
@@ -15,6 +15,29 @@ var capacitorCapacitorCameraView = (function (exports, core) {
15
15
  const CameraPreview = core.registerPlugin("CameraPreview", {
16
16
  web: () => Promise.resolve().then(function () { return web; }).then((m) => new m.CameraPreviewWeb()),
17
17
  });
18
+ async function getBase64FromFilePath(filePath) {
19
+ const url = core.Capacitor.convertFileSrc(filePath);
20
+ const response = await fetch(url);
21
+ if (!response.ok) {
22
+ throw new Error(`Failed to read file at path: ${filePath} (status ${response.status})`);
23
+ }
24
+ const blob = await response.blob();
25
+ return await new Promise((resolve, reject) => {
26
+ const reader = new FileReader();
27
+ reader.onloadend = () => {
28
+ const dataUrl = reader.result;
29
+ const commaIndex = dataUrl.indexOf(",");
30
+ resolve(commaIndex >= 0 ? dataUrl.substring(commaIndex + 1) : dataUrl);
31
+ };
32
+ reader.onerror = () => reject(reader.error);
33
+ reader.readAsDataURL(blob);
34
+ });
35
+ }
36
+ async function deleteFile(path) {
37
+ // Use native bridge to delete file to handle platform-specific permissions/URIs
38
+ const { success } = await CameraPreview.deleteFile({ path });
39
+ return !!success;
40
+ }
18
41
 
19
42
  const DEFAULT_VIDEO_ID = "capgo_video";
20
43
  class CameraPreviewWeb extends core.WebPlugin {
@@ -28,6 +51,72 @@ var capacitorCapacitorCameraView = (function (exports, core) {
28
51
  this.currentDeviceId = null;
29
52
  this.videoElement = null;
30
53
  this.isStarted = false;
54
+ this.orientationListenerBound = false;
55
+ }
56
+ getCurrentOrientation() {
57
+ var _a, _b;
58
+ try {
59
+ const so = screen.orientation;
60
+ const type = (so === null || so === void 0 ? void 0 : so.type) || (so === null || so === void 0 ? void 0 : so.mozOrientation) || (so === null || so === void 0 ? void 0 : so.msOrientation);
61
+ if (typeof type === "string") {
62
+ if (type.includes("portrait-primary"))
63
+ return "portrait";
64
+ if (type.includes("portrait-secondary"))
65
+ return "portrait-upside-down";
66
+ if (type.includes("landscape-primary"))
67
+ return "landscape-left";
68
+ if (type.includes("landscape-secondary"))
69
+ return "landscape-right";
70
+ if (type.includes("landscape"))
71
+ return "landscape";
72
+ if (type.includes("portrait"))
73
+ return "portrait";
74
+ }
75
+ const angle = window.orientation;
76
+ if (typeof angle === "number") {
77
+ if (angle === 0)
78
+ return "portrait";
79
+ if (angle === 180)
80
+ return "portrait-upside-down";
81
+ if (angle === 90)
82
+ return "landscape-right";
83
+ if (angle === -90)
84
+ return "landscape-left";
85
+ if (angle === 270)
86
+ return "landscape-left";
87
+ }
88
+ if ((_a = window.matchMedia("(orientation: portrait)")) === null || _a === void 0 ? void 0 : _a.matches) {
89
+ return "portrait";
90
+ }
91
+ if ((_b = window.matchMedia("(orientation: landscape)")) === null || _b === void 0 ? void 0 : _b.matches) {
92
+ return "landscape";
93
+ }
94
+ }
95
+ catch (e) {
96
+ console.error(e);
97
+ }
98
+ return "unknown";
99
+ }
100
+ ensureOrientationListener() {
101
+ if (this.orientationListenerBound)
102
+ return;
103
+ const emit = () => {
104
+ this.notifyListeners("orientationChange", {
105
+ orientation: this.getCurrentOrientation(),
106
+ });
107
+ };
108
+ window.addEventListener("orientationchange", emit);
109
+ window.addEventListener("resize", emit);
110
+ this.orientationListenerBound = true;
111
+ }
112
+ async getOrientation() {
113
+ return { orientation: this.getCurrentOrientation() };
114
+ }
115
+ getSafeAreaInsets() {
116
+ throw new Error("Method not implemented.");
117
+ }
118
+ async getZoomButtonValues() {
119
+ throw new Error("getZoomButtonValues not supported under the web platform");
31
120
  }
32
121
  async getSupportedPictureSizes() {
33
122
  throw new Error("getSupportedPictureSizes not supported under the web platform");
@@ -43,6 +132,7 @@ var capacitorCapacitorCameraView = (function (exports, core) {
43
132
  this.isStarted = false;
44
133
  const parent = document.getElementById((options === null || options === void 0 ? void 0 : options.parent) || "");
45
134
  const gridMode = (options === null || options === void 0 ? void 0 : options.gridMode) || "none";
135
+ const positioning = (options === null || options === void 0 ? void 0 : options.positioning) || "top";
46
136
  if (options.position) {
47
137
  this.isBackCamera = options.position === "rear";
48
138
  }
@@ -50,7 +140,9 @@ var capacitorCapacitorCameraView = (function (exports, core) {
50
140
  if (video) {
51
141
  video.remove();
52
142
  }
53
- const container = options.parent ? document.getElementById(options.parent) : document.body;
143
+ const container = options.parent
144
+ ? document.getElementById(options.parent)
145
+ : document.body;
54
146
  if (!container) {
55
147
  throw new Error("container not found");
56
148
  }
@@ -60,45 +152,57 @@ var capacitorCapacitorCameraView = (function (exports, core) {
60
152
  this.videoElement.playsInline = true;
61
153
  this.videoElement.muted = true;
62
154
  this.videoElement.autoplay = true;
155
+ // Remove objectFit as we'll match camera's native aspect ratio
156
+ this.videoElement.style.backgroundColor = "transparent";
157
+ // Reset any default margins that might interfere
158
+ this.videoElement.style.margin = "0";
159
+ this.videoElement.style.padding = "0";
63
160
  container.appendChild(this.videoElement);
64
161
  if (options.toBack) {
65
162
  this.videoElement.style.zIndex = "-1";
66
163
  }
164
+ // Default to 4:3 if no aspect ratio or size specified
165
+ const useDefaultAspectRatio = !options.aspectRatio && !options.width && !options.height;
166
+ const effectiveAspectRatio = options.aspectRatio || (useDefaultAspectRatio ? "4:3" : null);
67
167
  if (options.width) {
68
168
  this.videoElement.width = options.width;
169
+ this.videoElement.style.width = `${options.width}px`;
69
170
  }
70
171
  if (options.height) {
71
172
  this.videoElement.height = options.height;
173
+ this.videoElement.style.height = `${options.height}px`;
72
174
  }
73
- if (options.x) {
175
+ // Handle positioning - center if x or y not provided
176
+ const centerX = options.x === undefined;
177
+ const centerY = options.y === undefined;
178
+ // Always set position to absolute for proper positioning
179
+ this.videoElement.style.position = "absolute";
180
+ console.log("Initial positioning flags:", {
181
+ centerX,
182
+ centerY,
183
+ x: options.x,
184
+ y: options.y,
185
+ });
186
+ if (options.x !== undefined) {
74
187
  this.videoElement.style.left = `${options.x}px`;
75
188
  }
189
+ if (options.y !== undefined) {
190
+ this.videoElement.style.top = `${options.y}px`;
191
+ }
76
192
  // Create and add grid overlay if needed
77
193
  if (gridMode !== "none") {
78
194
  const gridOverlay = this.createGridOverlay(gridMode);
79
195
  gridOverlay.id = "camera-grid-overlay";
80
196
  parent === null || parent === void 0 ? void 0 : parent.appendChild(gridOverlay);
81
197
  }
82
- if (options.y) {
83
- this.videoElement.style.top = `${options.y}px`;
84
- }
85
- if (options.aspectRatio) {
86
- const [widthRatio, heightRatio] = options.aspectRatio.split(':').map(Number);
87
- const ratio = widthRatio / heightRatio;
88
- if (options.width) {
89
- this.videoElement.height = options.width / ratio;
90
- }
91
- else if (options.height) {
92
- this.videoElement.width = options.height * ratio;
93
- }
94
- }
95
- else {
96
- this.videoElement.style.objectFit = 'cover';
97
- }
198
+ // Aspect ratio handling is now done after getting camera stream
199
+ // Store centering flags for later use
200
+ const needsCenterX = centerX;
201
+ const needsCenterY = centerY;
202
+ console.log("Centering flags stored:", { needsCenterX, needsCenterY });
203
+ // First get the camera stream with basic constraints
98
204
  const constraints = {
99
205
  video: {
100
- width: { ideal: this.videoElement.width || 640 },
101
- height: { ideal: this.videoElement.height || window.innerHeight },
102
206
  facingMode: this.isBackCamera ? "environment" : "user",
103
207
  },
104
208
  };
@@ -109,16 +213,226 @@ var capacitorCapacitorCameraView = (function (exports, core) {
109
213
  if (!this.videoElement) {
110
214
  throw new Error("video element not found");
111
215
  }
216
+ // Get the actual camera dimensions from the video track
217
+ const videoTrack = stream.getVideoTracks()[0];
218
+ const settings = videoTrack.getSettings();
219
+ const cameraWidth = settings.width || 640;
220
+ const cameraHeight = settings.height || 480;
221
+ const cameraAspectRatio = cameraWidth / cameraHeight;
222
+ console.log("Camera native dimensions:", {
223
+ width: cameraWidth,
224
+ height: cameraHeight,
225
+ aspectRatio: cameraAspectRatio,
226
+ });
227
+ console.log("Container dimensions:", {
228
+ width: container.offsetWidth,
229
+ height: container.offsetHeight,
230
+ id: container.id,
231
+ });
232
+ // Now adjust video element size based on camera's native aspect ratio
233
+ if (!options.width && !options.height && !options.aspectRatio) {
234
+ // No size specified, fit camera view within container bounds
235
+ const containerWidth = container.offsetWidth || window.innerWidth;
236
+ const containerHeight = container.offsetHeight || window.innerHeight;
237
+ // Calculate dimensions that fit within container while maintaining camera aspect ratio
238
+ let targetWidth, targetHeight;
239
+ // Try fitting to container width first
240
+ targetWidth = containerWidth;
241
+ targetHeight = targetWidth / cameraAspectRatio;
242
+ // If height exceeds container, fit to height instead
243
+ if (targetHeight > containerHeight) {
244
+ targetHeight = containerHeight;
245
+ targetWidth = targetHeight * cameraAspectRatio;
246
+ }
247
+ console.log("Video element dimensions:", {
248
+ width: targetWidth,
249
+ height: targetHeight,
250
+ container: { width: containerWidth, height: containerHeight },
251
+ });
252
+ this.videoElement.width = targetWidth;
253
+ this.videoElement.height = targetHeight;
254
+ this.videoElement.style.width = `${targetWidth}px`;
255
+ this.videoElement.style.height = `${targetHeight}px`;
256
+ // Center the video element within its parent container
257
+ if (needsCenterX || options.x === undefined) {
258
+ const x = Math.round((containerWidth - targetWidth) / 2);
259
+ this.videoElement.style.left = `${x}px`;
260
+ }
261
+ if (needsCenterY || options.y === undefined) {
262
+ let y;
263
+ switch (positioning) {
264
+ case "top":
265
+ y = 0;
266
+ break;
267
+ case "bottom":
268
+ y = window.innerHeight - targetHeight;
269
+ break;
270
+ case "center":
271
+ default:
272
+ y = Math.round((window.innerHeight - targetHeight) / 2);
273
+ break;
274
+ }
275
+ this.videoElement.style.setProperty("top", `${y}px`, "important");
276
+ // Force a style recalculation
277
+ this.videoElement.offsetHeight;
278
+ console.log("Positioning video:", {
279
+ positioning,
280
+ viewportHeight: window.innerHeight,
281
+ targetHeight,
282
+ calculatedY: y,
283
+ actualTop: this.videoElement.style.top,
284
+ position: this.videoElement.style.position,
285
+ });
286
+ }
287
+ }
288
+ else if (effectiveAspectRatio && !options.width && !options.height) {
289
+ // Aspect ratio specified but no size
290
+ const [widthRatio, heightRatio] = effectiveAspectRatio
291
+ .split(":")
292
+ .map(Number);
293
+ const targetRatio = widthRatio / heightRatio;
294
+ const viewportWidth = window.innerWidth;
295
+ const viewportHeight = window.innerHeight;
296
+ let targetWidth, targetHeight;
297
+ // Try fitting to viewport width first
298
+ targetWidth = viewportWidth;
299
+ targetHeight = targetWidth / targetRatio;
300
+ // If height exceeds viewport, fit to height instead
301
+ if (targetHeight > viewportHeight) {
302
+ targetHeight = viewportHeight;
303
+ targetWidth = targetHeight * targetRatio;
304
+ }
305
+ this.videoElement.width = targetWidth;
306
+ this.videoElement.height = targetHeight;
307
+ this.videoElement.style.width = `${targetWidth}px`;
308
+ this.videoElement.style.height = `${targetHeight}px`;
309
+ // Center the video element within its parent container
310
+ if (needsCenterX || options.x === undefined) {
311
+ const parentWidth = container.offsetWidth || viewportWidth;
312
+ const x = Math.round((parentWidth - targetWidth) / 2);
313
+ this.videoElement.style.left = `${x}px`;
314
+ }
315
+ if (needsCenterY || options.y === undefined) {
316
+ const parentHeight = container.offsetHeight || viewportHeight;
317
+ let y;
318
+ switch (positioning) {
319
+ case "top":
320
+ y = 0;
321
+ break;
322
+ case "bottom":
323
+ y = parentHeight - targetHeight;
324
+ break;
325
+ case "center":
326
+ default:
327
+ y = Math.round((parentHeight - targetHeight) / 2);
328
+ break;
329
+ }
330
+ this.videoElement.style.top = `${y}px`;
331
+ }
332
+ }
112
333
  this.videoElement.srcObject = stream;
113
334
  if (!this.isBackCamera) {
114
335
  this.videoElement.style.transform = "scaleX(-1)";
115
336
  }
337
+ // Set initial zoom level if specified and supported
338
+ if (options.initialZoomLevel !== undefined &&
339
+ options.initialZoomLevel !== 1.0) {
340
+ // videoTrack already declared above
341
+ if (videoTrack) {
342
+ const capabilities = videoTrack.getCapabilities();
343
+ if (capabilities.zoom) {
344
+ const zoomLevel = options.initialZoomLevel;
345
+ const minZoom = capabilities.zoom.min || 1;
346
+ const maxZoom = capabilities.zoom.max || 1;
347
+ if (zoomLevel < minZoom || zoomLevel > maxZoom) {
348
+ stream.getTracks().forEach((track) => track.stop());
349
+ throw new Error(`Initial zoom level ${zoomLevel} is not available. Valid range is ${minZoom} to ${maxZoom}`);
350
+ }
351
+ try {
352
+ await videoTrack.applyConstraints({
353
+ advanced: [{ zoom: zoomLevel }],
354
+ });
355
+ }
356
+ catch (error) {
357
+ console.warn(`Failed to set initial zoom level: ${error}`);
358
+ // Don't throw, just continue without zoom
359
+ }
360
+ }
361
+ }
362
+ }
116
363
  this.isStarted = true;
364
+ this.ensureOrientationListener();
365
+ // Wait for video to be ready and get actual dimensions
366
+ await new Promise((resolve) => {
367
+ const videoEl = this.videoElement;
368
+ if (!videoEl) {
369
+ throw new Error("video element not found");
370
+ }
371
+ if (videoEl.readyState >= 2) {
372
+ resolve();
373
+ }
374
+ else {
375
+ videoEl.addEventListener("loadeddata", () => resolve(), {
376
+ once: true,
377
+ });
378
+ }
379
+ });
380
+ // Ensure centering is applied after DOM updates
381
+ await new Promise((resolve) => requestAnimationFrame(resolve));
382
+ console.log("About to re-center, flags:", { needsCenterX, needsCenterY });
383
+ // Re-apply centering with correct parent dimensions
384
+ if (needsCenterX) {
385
+ const parentWidth = container.offsetWidth;
386
+ const x = Math.round((parentWidth - this.videoElement.offsetWidth) / 2);
387
+ this.videoElement.style.left = `${x}px`;
388
+ console.log("Re-centering X:", {
389
+ parentWidth,
390
+ videoWidth: this.videoElement.offsetWidth,
391
+ x,
392
+ });
393
+ }
394
+ if (needsCenterY) {
395
+ let y;
396
+ switch (positioning) {
397
+ case "top":
398
+ y = 0;
399
+ break;
400
+ case "bottom":
401
+ y = window.innerHeight - this.videoElement.offsetHeight;
402
+ break;
403
+ case "center":
404
+ default:
405
+ y = Math.round((window.innerHeight - this.videoElement.offsetHeight) / 2);
406
+ break;
407
+ }
408
+ this.videoElement.style.setProperty("top", `${y}px`, "important");
409
+ console.log("Re-positioning Y:", {
410
+ positioning,
411
+ viewportHeight: window.innerHeight,
412
+ videoHeight: this.videoElement.offsetHeight,
413
+ y,
414
+ position: this.videoElement.style.position,
415
+ top: this.videoElement.style.top,
416
+ });
417
+ }
418
+ // Get the actual rendered dimensions after video is loaded
419
+ const rect = this.videoElement.getBoundingClientRect();
420
+ const computedStyle = window.getComputedStyle(this.videoElement);
421
+ console.log("Final video element state:", {
422
+ rect: { x: rect.x, y: rect.y, width: rect.width, height: rect.height },
423
+ style: {
424
+ position: computedStyle.position,
425
+ left: computedStyle.left,
426
+ top: computedStyle.top,
427
+ width: computedStyle.width,
428
+ height: computedStyle.height,
429
+ },
430
+ });
117
431
  return {
118
- width: this.videoElement.width,
119
- height: this.videoElement.height,
120
- x: this.videoElement.getBoundingClientRect().x,
121
- y: this.videoElement.getBoundingClientRect().y,
432
+ width: Math.round(rect.width),
433
+ height: Math.round(rect.height),
434
+ x: Math.round(rect.x),
435
+ y: Math.round(rect.y),
122
436
  };
123
437
  }
124
438
  stopStream(stream) {
@@ -152,14 +466,57 @@ var capacitorCapacitorCameraView = (function (exports, core) {
152
466
  if (video && video.videoWidth > 0 && video.videoHeight > 0) {
153
467
  const canvas = document.createElement("canvas");
154
468
  const context = canvas.getContext("2d");
155
- canvas.width = video.videoWidth;
156
- canvas.height = video.videoHeight;
469
+ // Calculate capture dimensions
470
+ let captureWidth = video.videoWidth;
471
+ let captureHeight = video.videoHeight;
472
+ let sourceX = 0;
473
+ let sourceY = 0;
474
+ // Check for conflicting parameters
475
+ if (options.aspectRatio && (options.width || options.height)) {
476
+ reject(new Error("Cannot set both aspectRatio and size (width/height). Use setPreviewSize after start."));
477
+ return;
478
+ }
479
+ // Handle aspect ratio if no width/height specified
480
+ if (!options.width && !options.height && options.aspectRatio) {
481
+ const [widthRatio, heightRatio] = options.aspectRatio
482
+ .split(":")
483
+ .map(Number);
484
+ if (widthRatio && heightRatio) {
485
+ // For capture in portrait orientation, swap the aspect ratio (16:9 becomes 9:16)
486
+ const isPortrait = video.videoHeight > video.videoWidth;
487
+ const targetAspectRatio = isPortrait
488
+ ? heightRatio / widthRatio
489
+ : widthRatio / heightRatio;
490
+ const videoAspectRatio = video.videoWidth / video.videoHeight;
491
+ if (videoAspectRatio > targetAspectRatio) {
492
+ // Video is wider than target - crop sides
493
+ captureWidth = video.videoHeight * targetAspectRatio;
494
+ captureHeight = video.videoHeight;
495
+ sourceX = (video.videoWidth - captureWidth) / 2;
496
+ }
497
+ else {
498
+ // Video is taller than target - crop top/bottom
499
+ captureWidth = video.videoWidth;
500
+ captureHeight = video.videoWidth / targetAspectRatio;
501
+ sourceY = (video.videoHeight - captureHeight) / 2;
502
+ }
503
+ }
504
+ }
505
+ else if (options.width || options.height) {
506
+ // If width or height is specified, use them
507
+ if (options.width)
508
+ captureWidth = options.width;
509
+ if (options.height)
510
+ captureHeight = options.height;
511
+ }
512
+ canvas.width = captureWidth;
513
+ canvas.height = captureHeight;
157
514
  // flip horizontally back camera isn't used
158
515
  if (!this.isBackCamera) {
159
- context === null || context === void 0 ? void 0 : context.translate(video.videoWidth, 0);
516
+ context === null || context === void 0 ? void 0 : context.translate(captureWidth, 0);
160
517
  context === null || context === void 0 ? void 0 : context.scale(-1, 1);
161
518
  }
162
- context === null || context === void 0 ? void 0 : context.drawImage(video, 0, 0, video.videoWidth, video.videoHeight);
519
+ context === null || context === void 0 ? void 0 : context.drawImage(video, sourceX, sourceY, captureWidth, captureHeight, 0, 0, captureWidth, captureHeight);
163
520
  if (options.saveToGallery) ;
164
521
  if (options.withExifLocation) ;
165
522
  if ((options.format || "jpeg") === "jpeg") {
@@ -253,7 +610,7 @@ var capacitorCapacitorCameraView = (function (exports, core) {
253
610
  throw new Error("getAvailableDevices not supported under the web platform");
254
611
  }
255
612
  const devices = await navigator.mediaDevices.enumerateDevices();
256
- const videoDevices = devices.filter(device => device.kind === 'videoinput');
613
+ const videoDevices = devices.filter((device) => device.kind === "videoinput");
257
614
  // Group devices by position (front/back)
258
615
  const frontDevices = [];
259
616
  const backDevices = [];
@@ -263,15 +620,19 @@ var capacitorCapacitorCameraView = (function (exports, core) {
263
620
  // Determine device type based on label
264
621
  let deviceType = exports.DeviceType.WIDE_ANGLE;
265
622
  let baseZoomRatio = 1.0;
266
- if (labelLower.includes('ultra') || labelLower.includes('0.5')) {
623
+ if (labelLower.includes("ultra") || labelLower.includes("0.5")) {
267
624
  deviceType = exports.DeviceType.ULTRA_WIDE;
268
625
  baseZoomRatio = 0.5;
269
626
  }
270
- else if (labelLower.includes('telephoto') || labelLower.includes('tele') || labelLower.includes('2x') || labelLower.includes('3x')) {
627
+ else if (labelLower.includes("telephoto") ||
628
+ labelLower.includes("tele") ||
629
+ labelLower.includes("2x") ||
630
+ labelLower.includes("3x")) {
271
631
  deviceType = exports.DeviceType.TELEPHOTO;
272
632
  baseZoomRatio = 2.0;
273
633
  }
274
- else if (labelLower.includes('depth') || labelLower.includes('truedepth')) {
634
+ else if (labelLower.includes("depth") ||
635
+ labelLower.includes("truedepth")) {
275
636
  deviceType = exports.DeviceType.TRUE_DEPTH;
276
637
  baseZoomRatio = 1.0;
277
638
  }
@@ -282,10 +643,10 @@ var capacitorCapacitorCameraView = (function (exports, core) {
282
643
  focalLength: 4.25,
283
644
  baseZoomRatio,
284
645
  minZoom: 1.0,
285
- maxZoom: 1.0
646
+ maxZoom: 1.0,
286
647
  };
287
648
  // Determine position and add to appropriate array
288
- if (labelLower.includes('back') || labelLower.includes('rear')) {
649
+ if (labelLower.includes("back") || labelLower.includes("rear")) {
289
650
  backDevices.push(lensInfo);
290
651
  }
291
652
  else {
@@ -300,8 +661,8 @@ var capacitorCapacitorCameraView = (function (exports, core) {
300
661
  position: "front",
301
662
  lenses: frontDevices,
302
663
  isLogical: false,
303
- minZoom: Math.min(...frontDevices.map(d => d.minZoom)),
304
- maxZoom: Math.max(...frontDevices.map(d => d.maxZoom))
664
+ minZoom: Math.min(...frontDevices.map((d) => d.minZoom)),
665
+ maxZoom: Math.max(...frontDevices.map((d) => d.maxZoom)),
305
666
  });
306
667
  }
307
668
  if (backDevices.length > 0) {
@@ -311,8 +672,8 @@ var capacitorCapacitorCameraView = (function (exports, core) {
311
672
  position: "rear",
312
673
  lenses: backDevices,
313
674
  isLogical: false,
314
- minZoom: Math.min(...backDevices.map(d => d.minZoom)),
315
- maxZoom: Math.max(...backDevices.map(d => d.maxZoom))
675
+ minZoom: Math.min(...backDevices.map((d) => d.minZoom)),
676
+ maxZoom: Math.max(...backDevices.map((d) => d.maxZoom)),
316
677
  });
317
678
  }
318
679
  return { devices: result };
@@ -337,18 +698,22 @@ var capacitorCapacitorCameraView = (function (exports, core) {
337
698
  let baseZoomRatio = 1.0;
338
699
  if (this.currentDeviceId) {
339
700
  const devices = await navigator.mediaDevices.enumerateDevices();
340
- const device = devices.find(d => d.deviceId === this.currentDeviceId);
701
+ const device = devices.find((d) => d.deviceId === this.currentDeviceId);
341
702
  if (device) {
342
703
  const labelLower = device.label.toLowerCase();
343
- if (labelLower.includes('ultra') || labelLower.includes('0.5')) {
704
+ if (labelLower.includes("ultra") || labelLower.includes("0.5")) {
344
705
  deviceType = exports.DeviceType.ULTRA_WIDE;
345
706
  baseZoomRatio = 0.5;
346
707
  }
347
- else if (labelLower.includes('telephoto') || labelLower.includes('tele') || labelLower.includes('2x') || labelLower.includes('3x')) {
708
+ else if (labelLower.includes("telephoto") ||
709
+ labelLower.includes("tele") ||
710
+ labelLower.includes("2x") ||
711
+ labelLower.includes("3x")) {
348
712
  deviceType = exports.DeviceType.TELEPHOTO;
349
713
  baseZoomRatio = 2.0;
350
714
  }
351
- else if (labelLower.includes('depth') || labelLower.includes('truedepth')) {
715
+ else if (labelLower.includes("depth") ||
716
+ labelLower.includes("truedepth")) {
352
717
  deviceType = exports.DeviceType.TRUE_DEPTH;
353
718
  baseZoomRatio = 1.0;
354
719
  }
@@ -359,7 +724,7 @@ var capacitorCapacitorCameraView = (function (exports, core) {
359
724
  focalLength: 4.25,
360
725
  deviceType,
361
726
  baseZoomRatio,
362
- digitalZoom: currentZoom / baseZoomRatio
727
+ digitalZoom: currentZoom / baseZoomRatio,
363
728
  };
364
729
  return {
365
730
  min: capabilities.zoom.min || 1,
@@ -383,9 +748,10 @@ var capacitorCapacitorCameraView = (function (exports, core) {
383
748
  throw new Error("zoom not supported by this device");
384
749
  }
385
750
  const zoomLevel = Math.max(capabilities.zoom.min || 1, Math.min(capabilities.zoom.max || 1, options.level));
751
+ // Note: autoFocus is not supported on web platform
386
752
  try {
387
753
  await videoTrack.applyConstraints({
388
- advanced: [{ zoom: zoomLevel }]
754
+ advanced: [{ zoom: zoomLevel }],
389
755
  });
390
756
  }
391
757
  catch (error) {
@@ -418,8 +784,11 @@ var capacitorCapacitorCameraView = (function (exports, core) {
418
784
  try {
419
785
  // Try to determine camera position from device
420
786
  const devices = await navigator.mediaDevices.enumerateDevices();
421
- const device = devices.find(d => d.deviceId === options.deviceId);
422
- this.isBackCamera = (device === null || device === void 0 ? void 0 : device.label.toLowerCase().includes('back')) || (device === null || device === void 0 ? void 0 : device.label.toLowerCase().includes('rear')) || false;
787
+ const device = devices.find((d) => d.deviceId === options.deviceId);
788
+ this.isBackCamera =
789
+ (device === null || device === void 0 ? void 0 : device.label.toLowerCase().includes("back")) ||
790
+ (device === null || device === void 0 ? void 0 : device.label.toLowerCase().includes("rear")) ||
791
+ false;
423
792
  const stream = await navigator.mediaDevices.getUserMedia(constraints);
424
793
  video.srcObject = stream;
425
794
  // Update video transform based on camera
@@ -447,15 +816,15 @@ var capacitorCapacitorCameraView = (function (exports, core) {
447
816
  if (width && height) {
448
817
  const ratio = width / height;
449
818
  // Check for portrait camera ratios: 4:3 -> 3:4, 16:9 -> 9:16
450
- if (Math.abs(ratio - (3 / 4)) < 0.01) {
451
- return { aspectRatio: '4:3' };
819
+ if (Math.abs(ratio - 3 / 4) < 0.01) {
820
+ return { aspectRatio: "4:3" };
452
821
  }
453
- if (Math.abs(ratio - (9 / 16)) < 0.01) {
454
- return { aspectRatio: '16:9' };
822
+ if (Math.abs(ratio - 9 / 16) < 0.01) {
823
+ return { aspectRatio: "16:9" };
455
824
  }
456
825
  }
457
826
  // Default to 4:3 if no specific aspect ratio is matched
458
- return { aspectRatio: '4:3' };
827
+ return { aspectRatio: "4:3" };
459
828
  }
460
829
  async setAspectRatio(options) {
461
830
  const video = document.getElementById(DEFAULT_VIDEO_ID);
@@ -463,7 +832,9 @@ var capacitorCapacitorCameraView = (function (exports, core) {
463
832
  throw new Error("camera is not running");
464
833
  }
465
834
  if (options.aspectRatio) {
466
- const [widthRatio, heightRatio] = options.aspectRatio.split(':').map(Number);
835
+ const [widthRatio, heightRatio] = options.aspectRatio
836
+ .split(":")
837
+ .map(Number);
467
838
  // For camera, use portrait orientation: 4:3 becomes 3:4, 16:9 becomes 9:16
468
839
  const ratio = heightRatio / widthRatio;
469
840
  // Get current position and size
@@ -499,22 +870,26 @@ var capacitorCapacitorCameraView = (function (exports, core) {
499
870
  video.style.height = `${newHeight}px`;
500
871
  video.style.left = `${x}px`;
501
872
  video.style.top = `${y}px`;
502
- video.style.position = 'absolute';
873
+ video.style.position = "absolute";
874
+ const offsetX = newWidth / 8;
875
+ const offsetY = newHeight / 8;
503
876
  return {
504
877
  width: Math.round(newWidth),
505
878
  height: Math.round(newHeight),
506
- x: Math.round(x),
507
- y: Math.round(y)
879
+ x: Math.round(x + offsetX),
880
+ y: Math.round(y + offsetY),
508
881
  };
509
882
  }
510
883
  else {
511
- video.style.objectFit = 'cover';
884
+ video.style.objectFit = "cover";
512
885
  const rect = video.getBoundingClientRect();
886
+ const offsetX = rect.width / 8;
887
+ const offsetY = rect.height / 8;
513
888
  return {
514
889
  width: Math.round(rect.width),
515
890
  height: Math.round(rect.height),
516
- x: Math.round(rect.left),
517
- y: Math.round(rect.top)
891
+ x: Math.round(rect.left + offsetX),
892
+ y: Math.round(rect.top + offsetY),
518
893
  };
519
894
  }
520
895
  }
@@ -566,18 +941,20 @@ var capacitorCapacitorCameraView = (function (exports, core) {
566
941
  }
567
942
  async getGridMode() {
568
943
  // Web implementation - default to none
569
- return { gridMode: 'none' };
944
+ return { gridMode: "none" };
570
945
  }
571
946
  async getPreviewSize() {
572
947
  const video = document.getElementById(DEFAULT_VIDEO_ID);
573
948
  if (!video) {
574
949
  throw new Error("camera is not running");
575
950
  }
951
+ const offsetX = video.width / 8;
952
+ const offsetY = video.height / 8;
576
953
  return {
577
- x: video.offsetLeft,
578
- y: video.offsetTop,
954
+ x: video.offsetLeft + offsetX,
955
+ y: video.offsetTop + offsetY,
579
956
  width: video.width,
580
- height: video.height
957
+ height: video.height,
581
958
  };
582
959
  }
583
960
  async setPreviewSize(options) {
@@ -589,13 +966,55 @@ var capacitorCapacitorCameraView = (function (exports, core) {
589
966
  video.style.top = `${options.y}px`;
590
967
  video.width = options.width;
591
968
  video.height = options.height;
969
+ const offsetX = options.width / 8;
970
+ const offsetY = options.height / 8;
592
971
  return {
593
972
  width: options.width,
594
973
  height: options.height,
595
- x: options.x,
596
- y: options.y
974
+ x: options.x + offsetX,
975
+ y: options.y + offsetY,
597
976
  };
598
977
  }
978
+ async setFocus(options) {
979
+ // Reject if values are outside 0-1 range
980
+ if (options.x < 0 || options.x > 1 || options.y < 0 || options.y > 1) {
981
+ throw new Error("Focus coordinates must be between 0 and 1");
982
+ }
983
+ const video = document.getElementById(DEFAULT_VIDEO_ID);
984
+ if (!(video === null || video === void 0 ? void 0 : video.srcObject)) {
985
+ throw new Error("camera is not running");
986
+ }
987
+ const stream = video.srcObject;
988
+ const videoTrack = stream.getVideoTracks()[0];
989
+ if (!videoTrack) {
990
+ throw new Error("no video track found");
991
+ }
992
+ const capabilities = videoTrack.getCapabilities();
993
+ // Check if focusing is supported
994
+ if (capabilities.focusMode) {
995
+ try {
996
+ // Web API supports focus mode settings but not coordinate-based focus
997
+ // Setting to manual mode allows for coordinate focus if supported
998
+ await videoTrack.applyConstraints({
999
+ advanced: [
1000
+ {
1001
+ focusMode: "manual",
1002
+ focusDistance: 0.5, // Mid-range focus as fallback
1003
+ },
1004
+ ],
1005
+ });
1006
+ }
1007
+ catch (error) {
1008
+ console.warn(`setFocus is not fully supported on this device: ${error}. Focus coordinates (${options.x}, ${options.y}) were provided but cannot be applied.`);
1009
+ }
1010
+ }
1011
+ else {
1012
+ console.warn("Focus control is not supported on this device. Focus coordinates were provided but cannot be applied.");
1013
+ }
1014
+ }
1015
+ async deleteFile(_options) {
1016
+ throw new Error("deleteFile not supported under the web platform");
1017
+ }
599
1018
  }
600
1019
 
601
1020
  var web = /*#__PURE__*/Object.freeze({
@@ -604,6 +1023,8 @@ var capacitorCapacitorCameraView = (function (exports, core) {
604
1023
  });
605
1024
 
606
1025
  exports.CameraPreview = CameraPreview;
1026
+ exports.deleteFile = deleteFile;
1027
+ exports.getBase64FromFilePath = getBase64FromFilePath;
607
1028
 
608
1029
  return exports;
609
1030