@capgo/camera-preview 7.4.0-beta.1 → 7.4.0-beta.11

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 (35) hide show
  1. package/README.md +195 -31
  2. package/android/.gradle/8.14.2/checksums/checksums.lock +0 -0
  3. package/android/.gradle/8.14.2/checksums/md5-checksums.bin +0 -0
  4. package/android/.gradle/8.14.2/checksums/sha1-checksums.bin +0 -0
  5. package/android/.gradle/8.14.2/executionHistory/executionHistory.bin +0 -0
  6. package/android/.gradle/8.14.2/executionHistory/executionHistory.lock +0 -0
  7. package/android/.gradle/8.14.2/fileHashes/fileHashes.bin +0 -0
  8. package/android/.gradle/8.14.2/fileHashes/fileHashes.lock +0 -0
  9. package/android/.gradle/8.14.2/fileHashes/resourceHashesCache.bin +0 -0
  10. package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  11. package/android/.gradle/file-system.probe +0 -0
  12. package/android/build.gradle +3 -1
  13. package/android/src/main/AndroidManifest.xml +5 -3
  14. package/android/src/main/java/com/ahm/capacitor/camera/preview/CameraPreview.java +473 -88
  15. package/android/src/main/java/com/ahm/capacitor/camera/preview/CameraXView.java +2065 -704
  16. package/android/src/main/java/com/ahm/capacitor/camera/preview/GridOverlayView.java +95 -0
  17. package/android/src/main/java/com/ahm/capacitor/camera/preview/model/CameraDevice.java +55 -46
  18. package/android/src/main/java/com/ahm/capacitor/camera/preview/model/CameraLens.java +61 -52
  19. package/android/src/main/java/com/ahm/capacitor/camera/preview/model/CameraSessionConfiguration.java +152 -59
  20. package/android/src/main/java/com/ahm/capacitor/camera/preview/model/LensInfo.java +29 -23
  21. package/android/src/main/java/com/ahm/capacitor/camera/preview/model/ZoomFactors.java +24 -23
  22. package/dist/docs.json +235 -6
  23. package/dist/esm/definitions.d.ts +119 -3
  24. package/dist/esm/definitions.js.map +1 -1
  25. package/dist/esm/web.d.ts +47 -3
  26. package/dist/esm/web.js +297 -96
  27. package/dist/esm/web.js.map +1 -1
  28. package/dist/plugin.cjs.js +293 -96
  29. package/dist/plugin.cjs.js.map +1 -1
  30. package/dist/plugin.js +293 -96
  31. package/dist/plugin.js.map +1 -1
  32. package/ios/Sources/CapgoCameraPreview/CameraController.swift +364 -218
  33. package/ios/Sources/CapgoCameraPreview/GridOverlayView.swift +65 -0
  34. package/ios/Sources/CapgoCameraPreview/Plugin.swift +886 -242
  35. package/package.json +1 -1
package/dist/plugin.js CHANGED
@@ -16,6 +16,7 @@ var capacitorCapacitorCameraView = (function (exports, core) {
16
16
  web: () => Promise.resolve().then(function () { return web; }).then((m) => new m.CameraPreviewWeb()),
17
17
  });
18
18
 
19
+ const DEFAULT_VIDEO_ID = "capgo_video";
19
20
  class CameraPreviewWeb extends core.WebPlugin {
20
21
  constructor() {
21
22
  super();
@@ -25,87 +26,104 @@ var capacitorCapacitorCameraView = (function (exports, core) {
25
26
  */
26
27
  this.isBackCamera = false;
27
28
  this.currentDeviceId = null;
29
+ this.videoElement = null;
30
+ this.isStarted = false;
28
31
  }
29
32
  async getSupportedPictureSizes() {
30
33
  throw new Error("getSupportedPictureSizes not supported under the web platform");
31
34
  }
32
35
  async start(options) {
33
- var _a;
34
- await navigator.mediaDevices
35
- .getUserMedia({
36
- audio: !options.disableAudio,
37
- video: true,
38
- })
39
- .then((stream) => {
40
- // Stop any existing stream so we can request media with different constraints based on user input
41
- stream.getTracks().forEach((track) => track.stop());
42
- })
43
- .catch((error) => {
44
- Promise.reject(error);
45
- });
46
- const video = document.getElementById("video");
36
+ if (options.aspectRatio && (options.width || options.height)) {
37
+ throw new Error("Cannot set both aspectRatio and size (width/height). Use setPreviewSize after start.");
38
+ }
39
+ if (this.isStarted) {
40
+ throw new Error("camera already started");
41
+ }
42
+ this.isBackCamera = true;
43
+ this.isStarted = false;
47
44
  const parent = document.getElementById((options === null || options === void 0 ? void 0 : options.parent) || "");
48
- if (!video) {
49
- const videoElement = document.createElement("video");
50
- videoElement.id = "video";
51
- videoElement.setAttribute("class", (options === null || options === void 0 ? void 0 : options.className) || "");
52
- // Don't flip video feed if camera is rear facing
53
- if (options.position !== "rear") {
54
- videoElement.setAttribute("style", "-webkit-transform: scaleX(-1); transform: scaleX(-1);");
45
+ const gridMode = (options === null || options === void 0 ? void 0 : options.gridMode) || "none";
46
+ if (options.position) {
47
+ this.isBackCamera = options.position === "rear";
48
+ }
49
+ const video = document.getElementById(DEFAULT_VIDEO_ID);
50
+ if (video) {
51
+ video.remove();
52
+ }
53
+ const container = options.parent
54
+ ? document.getElementById(options.parent)
55
+ : document.body;
56
+ if (!container) {
57
+ throw new Error("container not found");
58
+ }
59
+ this.videoElement = document.createElement("video");
60
+ this.videoElement.id = DEFAULT_VIDEO_ID;
61
+ this.videoElement.className = options.className || "";
62
+ this.videoElement.playsInline = true;
63
+ this.videoElement.muted = true;
64
+ this.videoElement.autoplay = true;
65
+ container.appendChild(this.videoElement);
66
+ if (options.toBack) {
67
+ this.videoElement.style.zIndex = "-1";
68
+ }
69
+ if (options.width) {
70
+ this.videoElement.width = options.width;
71
+ }
72
+ if (options.height) {
73
+ this.videoElement.height = options.height;
74
+ }
75
+ if (options.x) {
76
+ this.videoElement.style.left = `${options.x}px`;
77
+ }
78
+ // Create and add grid overlay if needed
79
+ if (gridMode !== "none") {
80
+ const gridOverlay = this.createGridOverlay(gridMode);
81
+ gridOverlay.id = "camera-grid-overlay";
82
+ parent === null || parent === void 0 ? void 0 : parent.appendChild(gridOverlay);
83
+ }
84
+ if (options.y) {
85
+ this.videoElement.style.top = `${options.y}px`;
86
+ }
87
+ if (options.aspectRatio) {
88
+ const [widthRatio, heightRatio] = options.aspectRatio
89
+ .split(":")
90
+ .map(Number);
91
+ const ratio = widthRatio / heightRatio;
92
+ if (options.width) {
93
+ this.videoElement.height = options.width / ratio;
55
94
  }
56
- const userAgent = navigator.userAgent.toLowerCase();
57
- const isSafari = userAgent.includes("safari") && !userAgent.includes("chrome");
58
- // Safari on iOS needs to have the autoplay, muted and playsinline attributes set for video.play() to be successful
59
- // Without these attributes videoElement.play() will throw a NotAllowedError
60
- // https://developer.apple.com/documentation/webkit/delivering_video_content_for_safari
61
- if (isSafari) {
62
- videoElement.setAttribute("autoplay", "true");
63
- videoElement.setAttribute("muted", "true");
64
- videoElement.setAttribute("playsinline", "true");
65
- }
66
- parent === null || parent === void 0 ? void 0 : parent.appendChild(videoElement);
67
- if ((_a = navigator === null || navigator === void 0 ? void 0 : navigator.mediaDevices) === null || _a === void 0 ? void 0 : _a.getUserMedia) {
68
- const constraints = {
69
- video: {
70
- width: { ideal: options.width },
71
- height: { ideal: options.height },
72
- },
73
- };
74
- if (options.deviceId) {
75
- constraints.video.deviceId = { exact: options.deviceId };
76
- this.currentDeviceId = options.deviceId;
77
- // Try to determine camera position from device
78
- const devices = await navigator.mediaDevices.enumerateDevices();
79
- const device = devices.find(d => d.deviceId === options.deviceId);
80
- 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;
81
- }
82
- else if (options.position === "rear") {
83
- constraints.video.facingMode = "environment";
84
- this.isBackCamera = true;
85
- }
86
- else {
87
- this.isBackCamera = false;
88
- }
89
- const self = this;
90
- await navigator.mediaDevices.getUserMedia(constraints).then((stream) => {
91
- if (document.getElementById("video")) {
92
- // video.src = window.URL.createObjectURL(stream);
93
- videoElement.srcObject = stream;
94
- videoElement.play();
95
- Promise.resolve({});
96
- }
97
- else {
98
- self.stopStream(stream);
99
- Promise.reject(new Error("camera already stopped"));
100
- }
101
- }, (err) => {
102
- Promise.reject(new Error(err));
103
- });
95
+ else if (options.height) {
96
+ this.videoElement.width = options.height * ratio;
104
97
  }
105
98
  }
106
99
  else {
107
- Promise.reject(new Error("camera already started"));
100
+ this.videoElement.style.objectFit = "cover";
101
+ }
102
+ const constraints = {
103
+ video: {
104
+ width: { ideal: this.videoElement.width || 640 },
105
+ height: { ideal: this.videoElement.height || window.innerHeight },
106
+ facingMode: this.isBackCamera ? "environment" : "user",
107
+ },
108
+ };
109
+ const stream = await navigator.mediaDevices.getUserMedia(constraints);
110
+ if (!stream) {
111
+ throw new Error("could not acquire stream");
108
112
  }
113
+ if (!this.videoElement) {
114
+ throw new Error("video element not found");
115
+ }
116
+ this.videoElement.srcObject = stream;
117
+ if (!this.isBackCamera) {
118
+ this.videoElement.style.transform = "scaleX(-1)";
119
+ }
120
+ this.isStarted = true;
121
+ return {
122
+ width: this.videoElement.width,
123
+ height: this.videoElement.height,
124
+ x: this.videoElement.getBoundingClientRect().x,
125
+ y: this.videoElement.getBoundingClientRect().y,
126
+ };
109
127
  }
110
128
  stopStream(stream) {
111
129
  if (stream) {
@@ -115,16 +133,20 @@ var capacitorCapacitorCameraView = (function (exports, core) {
115
133
  }
116
134
  }
117
135
  async stop() {
118
- const video = document.getElementById("video");
136
+ const video = document.getElementById(DEFAULT_VIDEO_ID);
119
137
  if (video) {
120
138
  video.pause();
121
139
  this.stopStream(video.srcObject);
122
140
  video.remove();
141
+ this.isStarted = false;
123
142
  }
143
+ // Remove grid overlay if it exists
144
+ const gridOverlay = document.getElementById("camera-grid-overlay");
145
+ gridOverlay === null || gridOverlay === void 0 ? void 0 : gridOverlay.remove();
124
146
  }
125
147
  async capture(options) {
126
148
  return new Promise((resolve, reject) => {
127
- const video = document.getElementById("video");
149
+ const video = document.getElementById(DEFAULT_VIDEO_ID);
128
150
  if (!(video === null || video === void 0 ? void 0 : video.srcObject)) {
129
151
  reject(new Error("camera is not running"));
130
152
  return;
@@ -142,6 +164,8 @@ var capacitorCapacitorCameraView = (function (exports, core) {
142
164
  context === null || context === void 0 ? void 0 : context.scale(-1, 1);
143
165
  }
144
166
  context === null || context === void 0 ? void 0 : context.drawImage(video, 0, 0, video.videoWidth, video.videoHeight);
167
+ if (options.saveToGallery) ;
168
+ if (options.withExifLocation) ;
145
169
  if ((options.format || "jpeg") === "jpeg") {
146
170
  base64EncodedImage = canvas
147
171
  .toDataURL("image/jpeg", (options.quality || 85) / 100.0)
@@ -155,6 +179,7 @@ var capacitorCapacitorCameraView = (function (exports, core) {
155
179
  }
156
180
  resolve({
157
181
  value: base64EncodedImage,
182
+ exif: {},
158
183
  });
159
184
  });
160
185
  }
@@ -178,7 +203,7 @@ var capacitorCapacitorCameraView = (function (exports, core) {
178
203
  throw new Error(`setFlashMode not supported under the web platform${_options}`);
179
204
  }
180
205
  async flip() {
181
- const video = document.getElementById("video");
206
+ const video = document.getElementById(DEFAULT_VIDEO_ID);
182
207
  if (!(video === null || video === void 0 ? void 0 : video.srcObject)) {
183
208
  throw new Error("camera is not running");
184
209
  }
@@ -218,12 +243,12 @@ var capacitorCapacitorCameraView = (function (exports, core) {
218
243
  }
219
244
  }
220
245
  async setOpacity(_options) {
221
- const video = document.getElementById("video");
246
+ const video = document.getElementById(DEFAULT_VIDEO_ID);
222
247
  if (!!video && !!_options.opacity)
223
248
  video.style.setProperty("opacity", _options.opacity.toString());
224
249
  }
225
250
  async isRunning() {
226
- const video = document.getElementById("video");
251
+ const video = document.getElementById(DEFAULT_VIDEO_ID);
227
252
  return { isRunning: !!video && !!video.srcObject };
228
253
  }
229
254
  async getAvailableDevices() {
@@ -232,7 +257,7 @@ var capacitorCapacitorCameraView = (function (exports, core) {
232
257
  throw new Error("getAvailableDevices not supported under the web platform");
233
258
  }
234
259
  const devices = await navigator.mediaDevices.enumerateDevices();
235
- const videoDevices = devices.filter(device => device.kind === 'videoinput');
260
+ const videoDevices = devices.filter((device) => device.kind === "videoinput");
236
261
  // Group devices by position (front/back)
237
262
  const frontDevices = [];
238
263
  const backDevices = [];
@@ -242,15 +267,19 @@ var capacitorCapacitorCameraView = (function (exports, core) {
242
267
  // Determine device type based on label
243
268
  let deviceType = exports.DeviceType.WIDE_ANGLE;
244
269
  let baseZoomRatio = 1.0;
245
- if (labelLower.includes('ultra') || labelLower.includes('0.5')) {
270
+ if (labelLower.includes("ultra") || labelLower.includes("0.5")) {
246
271
  deviceType = exports.DeviceType.ULTRA_WIDE;
247
272
  baseZoomRatio = 0.5;
248
273
  }
249
- else if (labelLower.includes('telephoto') || labelLower.includes('tele') || labelLower.includes('2x') || labelLower.includes('3x')) {
274
+ else if (labelLower.includes("telephoto") ||
275
+ labelLower.includes("tele") ||
276
+ labelLower.includes("2x") ||
277
+ labelLower.includes("3x")) {
250
278
  deviceType = exports.DeviceType.TELEPHOTO;
251
279
  baseZoomRatio = 2.0;
252
280
  }
253
- else if (labelLower.includes('depth') || labelLower.includes('truedepth')) {
281
+ else if (labelLower.includes("depth") ||
282
+ labelLower.includes("truedepth")) {
254
283
  deviceType = exports.DeviceType.TRUE_DEPTH;
255
284
  baseZoomRatio = 1.0;
256
285
  }
@@ -261,10 +290,10 @@ var capacitorCapacitorCameraView = (function (exports, core) {
261
290
  focalLength: 4.25,
262
291
  baseZoomRatio,
263
292
  minZoom: 1.0,
264
- maxZoom: 1.0
293
+ maxZoom: 1.0,
265
294
  };
266
295
  // Determine position and add to appropriate array
267
- if (labelLower.includes('back') || labelLower.includes('rear')) {
296
+ if (labelLower.includes("back") || labelLower.includes("rear")) {
268
297
  backDevices.push(lensInfo);
269
298
  }
270
299
  else {
@@ -279,8 +308,8 @@ var capacitorCapacitorCameraView = (function (exports, core) {
279
308
  position: "front",
280
309
  lenses: frontDevices,
281
310
  isLogical: false,
282
- minZoom: Math.min(...frontDevices.map(d => d.minZoom)),
283
- maxZoom: Math.max(...frontDevices.map(d => d.maxZoom))
311
+ minZoom: Math.min(...frontDevices.map((d) => d.minZoom)),
312
+ maxZoom: Math.max(...frontDevices.map((d) => d.maxZoom)),
284
313
  });
285
314
  }
286
315
  if (backDevices.length > 0) {
@@ -290,14 +319,14 @@ var capacitorCapacitorCameraView = (function (exports, core) {
290
319
  position: "rear",
291
320
  lenses: backDevices,
292
321
  isLogical: false,
293
- minZoom: Math.min(...backDevices.map(d => d.minZoom)),
294
- maxZoom: Math.max(...backDevices.map(d => d.maxZoom))
322
+ minZoom: Math.min(...backDevices.map((d) => d.minZoom)),
323
+ maxZoom: Math.max(...backDevices.map((d) => d.maxZoom)),
295
324
  });
296
325
  }
297
326
  return { devices: result };
298
327
  }
299
328
  async getZoom() {
300
- const video = document.getElementById("video");
329
+ const video = document.getElementById(DEFAULT_VIDEO_ID);
301
330
  if (!(video === null || video === void 0 ? void 0 : video.srcObject)) {
302
331
  throw new Error("camera is not running");
303
332
  }
@@ -316,18 +345,22 @@ var capacitorCapacitorCameraView = (function (exports, core) {
316
345
  let baseZoomRatio = 1.0;
317
346
  if (this.currentDeviceId) {
318
347
  const devices = await navigator.mediaDevices.enumerateDevices();
319
- const device = devices.find(d => d.deviceId === this.currentDeviceId);
348
+ const device = devices.find((d) => d.deviceId === this.currentDeviceId);
320
349
  if (device) {
321
350
  const labelLower = device.label.toLowerCase();
322
- if (labelLower.includes('ultra') || labelLower.includes('0.5')) {
351
+ if (labelLower.includes("ultra") || labelLower.includes("0.5")) {
323
352
  deviceType = exports.DeviceType.ULTRA_WIDE;
324
353
  baseZoomRatio = 0.5;
325
354
  }
326
- else if (labelLower.includes('telephoto') || labelLower.includes('tele') || labelLower.includes('2x') || labelLower.includes('3x')) {
355
+ else if (labelLower.includes("telephoto") ||
356
+ labelLower.includes("tele") ||
357
+ labelLower.includes("2x") ||
358
+ labelLower.includes("3x")) {
327
359
  deviceType = exports.DeviceType.TELEPHOTO;
328
360
  baseZoomRatio = 2.0;
329
361
  }
330
- else if (labelLower.includes('depth') || labelLower.includes('truedepth')) {
362
+ else if (labelLower.includes("depth") ||
363
+ labelLower.includes("truedepth")) {
331
364
  deviceType = exports.DeviceType.TRUE_DEPTH;
332
365
  baseZoomRatio = 1.0;
333
366
  }
@@ -338,7 +371,7 @@ var capacitorCapacitorCameraView = (function (exports, core) {
338
371
  focalLength: 4.25,
339
372
  deviceType,
340
373
  baseZoomRatio,
341
- digitalZoom: currentZoom / baseZoomRatio
374
+ digitalZoom: currentZoom / baseZoomRatio,
342
375
  };
343
376
  return {
344
377
  min: capabilities.zoom.min || 1,
@@ -348,7 +381,7 @@ var capacitorCapacitorCameraView = (function (exports, core) {
348
381
  };
349
382
  }
350
383
  async setZoom(options) {
351
- const video = document.getElementById("video");
384
+ const video = document.getElementById(DEFAULT_VIDEO_ID);
352
385
  if (!(video === null || video === void 0 ? void 0 : video.srcObject)) {
353
386
  throw new Error("camera is not running");
354
387
  }
@@ -364,7 +397,7 @@ var capacitorCapacitorCameraView = (function (exports, core) {
364
397
  const zoomLevel = Math.max(capabilities.zoom.min || 1, Math.min(capabilities.zoom.max || 1, options.level));
365
398
  try {
366
399
  await videoTrack.applyConstraints({
367
- advanced: [{ zoom: zoomLevel }]
400
+ advanced: [{ zoom: zoomLevel }],
368
401
  });
369
402
  }
370
403
  catch (error) {
@@ -378,7 +411,7 @@ var capacitorCapacitorCameraView = (function (exports, core) {
378
411
  return { deviceId: this.currentDeviceId || "" };
379
412
  }
380
413
  async setDeviceId(options) {
381
- const video = document.getElementById("video");
414
+ const video = document.getElementById(DEFAULT_VIDEO_ID);
382
415
  if (!(video === null || video === void 0 ? void 0 : video.srcObject)) {
383
416
  throw new Error("camera is not running");
384
417
  }
@@ -397,8 +430,11 @@ var capacitorCapacitorCameraView = (function (exports, core) {
397
430
  try {
398
431
  // Try to determine camera position from device
399
432
  const devices = await navigator.mediaDevices.enumerateDevices();
400
- const device = devices.find(d => d.deviceId === options.deviceId);
401
- 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;
433
+ const device = devices.find((d) => d.deviceId === options.deviceId);
434
+ this.isBackCamera =
435
+ (device === null || device === void 0 ? void 0 : device.label.toLowerCase().includes("back")) ||
436
+ (device === null || device === void 0 ? void 0 : device.label.toLowerCase().includes("rear")) ||
437
+ false;
402
438
  const stream = await navigator.mediaDevices.getUserMedia(constraints);
403
439
  video.srcObject = stream;
404
440
  // Update video transform based on camera
@@ -416,6 +452,167 @@ var capacitorCapacitorCameraView = (function (exports, core) {
416
452
  throw new Error(`Failed to swap to device ${options.deviceId}: ${error}`);
417
453
  }
418
454
  }
455
+ async getAspectRatio() {
456
+ const video = document.getElementById(DEFAULT_VIDEO_ID);
457
+ if (!video) {
458
+ throw new Error("camera is not running");
459
+ }
460
+ const width = video.offsetWidth;
461
+ const height = video.offsetHeight;
462
+ if (width && height) {
463
+ const ratio = width / height;
464
+ // Check for portrait camera ratios: 4:3 -> 3:4, 16:9 -> 9:16
465
+ if (Math.abs(ratio - 3 / 4) < 0.01) {
466
+ return { aspectRatio: "4:3" };
467
+ }
468
+ if (Math.abs(ratio - 9 / 16) < 0.01) {
469
+ return { aspectRatio: "16:9" };
470
+ }
471
+ }
472
+ // Default to 4:3 if no specific aspect ratio is matched
473
+ return { aspectRatio: "4:3" };
474
+ }
475
+ async setAspectRatio(options) {
476
+ const video = document.getElementById(DEFAULT_VIDEO_ID);
477
+ if (!video) {
478
+ throw new Error("camera is not running");
479
+ }
480
+ if (options.aspectRatio) {
481
+ const [widthRatio, heightRatio] = options.aspectRatio
482
+ .split(":")
483
+ .map(Number);
484
+ // For camera, use portrait orientation: 4:3 becomes 3:4, 16:9 becomes 9:16
485
+ const ratio = heightRatio / widthRatio;
486
+ // Get current position and size
487
+ const rect = video.getBoundingClientRect();
488
+ const currentWidth = rect.width;
489
+ const currentHeight = rect.height;
490
+ const currentRatio = currentWidth / currentHeight;
491
+ let newWidth;
492
+ let newHeight;
493
+ if (currentRatio > ratio) {
494
+ // Width is larger, fit by height and center horizontally
495
+ newWidth = currentHeight * ratio;
496
+ newHeight = currentHeight;
497
+ }
498
+ else {
499
+ // Height is larger, fit by width and center vertically
500
+ newWidth = currentWidth;
501
+ newHeight = currentWidth / ratio;
502
+ }
503
+ // Calculate position
504
+ let x, y;
505
+ if (options.x !== undefined && options.y !== undefined) {
506
+ // Use provided coordinates, ensuring they stay within screen boundaries
507
+ x = Math.max(0, Math.min(options.x, window.innerWidth - newWidth));
508
+ y = Math.max(0, Math.min(options.y, window.innerHeight - newHeight));
509
+ }
510
+ else {
511
+ // Auto-center the view
512
+ x = (window.innerWidth - newWidth) / 2;
513
+ y = (window.innerHeight - newHeight) / 2;
514
+ }
515
+ video.style.width = `${newWidth}px`;
516
+ video.style.height = `${newHeight}px`;
517
+ video.style.left = `${x}px`;
518
+ video.style.top = `${y}px`;
519
+ video.style.position = "absolute";
520
+ return {
521
+ width: Math.round(newWidth),
522
+ height: Math.round(newHeight),
523
+ x: Math.round(x),
524
+ y: Math.round(y),
525
+ };
526
+ }
527
+ else {
528
+ video.style.objectFit = "cover";
529
+ const rect = video.getBoundingClientRect();
530
+ return {
531
+ width: Math.round(rect.width),
532
+ height: Math.round(rect.height),
533
+ x: Math.round(rect.left),
534
+ y: Math.round(rect.top),
535
+ };
536
+ }
537
+ }
538
+ createGridOverlay(gridMode) {
539
+ const overlay = document.createElement("div");
540
+ overlay.style.position = "absolute";
541
+ overlay.style.top = "0";
542
+ overlay.style.left = "0";
543
+ overlay.style.width = "100%";
544
+ overlay.style.height = "100%";
545
+ overlay.style.pointerEvents = "none";
546
+ overlay.style.zIndex = "10";
547
+ const divisions = gridMode === "3x3" ? 3 : 4;
548
+ // Create SVG for grid lines
549
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
550
+ svg.style.width = "100%";
551
+ svg.style.height = "100%";
552
+ svg.style.position = "absolute";
553
+ svg.style.top = "0";
554
+ svg.style.left = "0";
555
+ // Create grid lines
556
+ for (let i = 1; i < divisions; i++) {
557
+ // Vertical lines
558
+ const verticalLine = document.createElementNS("http://www.w3.org/2000/svg", "line");
559
+ verticalLine.setAttribute("x1", `${(i / divisions) * 100}%`);
560
+ verticalLine.setAttribute("y1", "0%");
561
+ verticalLine.setAttribute("x2", `${(i / divisions) * 100}%`);
562
+ verticalLine.setAttribute("y2", "100%");
563
+ verticalLine.setAttribute("stroke", "rgba(255, 255, 255, 0.5)");
564
+ verticalLine.setAttribute("stroke-width", "1");
565
+ svg.appendChild(verticalLine);
566
+ // Horizontal lines
567
+ const horizontalLine = document.createElementNS("http://www.w3.org/2000/svg", "line");
568
+ horizontalLine.setAttribute("x1", "0%");
569
+ horizontalLine.setAttribute("y1", `${(i / divisions) * 100}%`);
570
+ horizontalLine.setAttribute("x2", "100%");
571
+ horizontalLine.setAttribute("y2", `${(i / divisions) * 100}%`);
572
+ horizontalLine.setAttribute("stroke", "rgba(255, 255, 255, 0.5)");
573
+ horizontalLine.setAttribute("stroke-width", "1");
574
+ svg.appendChild(horizontalLine);
575
+ }
576
+ overlay.appendChild(svg);
577
+ return overlay;
578
+ }
579
+ async setGridMode(options) {
580
+ // Web implementation of grid mode would need to be implemented
581
+ // For now, just resolve as a no-op
582
+ console.warn(`Grid mode '${options.gridMode}' is not yet implemented for web platform`);
583
+ }
584
+ async getGridMode() {
585
+ // Web implementation - default to none
586
+ return { gridMode: "none" };
587
+ }
588
+ async getPreviewSize() {
589
+ const video = document.getElementById(DEFAULT_VIDEO_ID);
590
+ if (!video) {
591
+ throw new Error("camera is not running");
592
+ }
593
+ return {
594
+ x: video.offsetLeft,
595
+ y: video.offsetTop,
596
+ width: video.width,
597
+ height: video.height,
598
+ };
599
+ }
600
+ async setPreviewSize(options) {
601
+ const video = document.getElementById(DEFAULT_VIDEO_ID);
602
+ if (!video) {
603
+ throw new Error("camera is not running");
604
+ }
605
+ video.style.left = `${options.x}px`;
606
+ video.style.top = `${options.y}px`;
607
+ video.width = options.width;
608
+ video.height = options.height;
609
+ return {
610
+ width: options.width,
611
+ height: options.height,
612
+ x: options.x,
613
+ y: options.y,
614
+ };
615
+ }
419
616
  }
420
617
 
421
618
  var web = /*#__PURE__*/Object.freeze({