@capgo/camera-preview 7.3.12 → 7.4.0-beta.10

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