capacitor-card-scanner 0.0.1

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.
@@ -0,0 +1,131 @@
1
+ package pt.tcgclassifier.cardscanner;
2
+
3
+ import android.Manifest;
4
+ import com.getcapacitor.JSObject;
5
+ import com.getcapacitor.PermissionState;
6
+ import com.getcapacitor.Plugin;
7
+ import com.getcapacitor.PluginCall;
8
+ import com.getcapacitor.PluginMethod;
9
+ import com.getcapacitor.annotation.CapacitorPlugin;
10
+ import com.getcapacitor.annotation.Permission;
11
+ import com.getcapacitor.annotation.PermissionCallback;
12
+
13
+ @CapacitorPlugin(name = "CardScanner", permissions = { @Permission(strings = { Manifest.permission.CAMERA }, alias = CardScannerPlugin.CAMERA) })
14
+ public class CardScannerPlugin extends Plugin {
15
+
16
+ static final String CAMERA = "camera";
17
+
18
+ private CardScanner implementation;
19
+
20
+ @Override
21
+ public void load() {
22
+ implementation = new CardScanner(getActivity(), getBridge().getWebView());
23
+ }
24
+
25
+ @Override
26
+ protected void handleOnDestroy() {
27
+ getActivity().runOnUiThread(() -> implementation.stop());
28
+ }
29
+
30
+ @PluginMethod
31
+ public void start(PluginCall call) {
32
+ if (getPermissionState(CAMERA) != PermissionState.GRANTED) {
33
+ requestPermissionForAlias(CAMERA, call, "cameraPermsCallback");
34
+ return;
35
+ }
36
+ doStart(call);
37
+ }
38
+
39
+ @PermissionCallback
40
+ private void cameraPermsCallback(PluginCall call) {
41
+ if (getPermissionState(CAMERA) == PermissionState.GRANTED) {
42
+ doStart(call);
43
+ } else {
44
+ call.reject("Camera permission denied", "PERMISSION_DENIED");
45
+ }
46
+ }
47
+
48
+ private void doStart(PluginCall call) {
49
+ boolean front = "front".equals(call.getString("position", "rear"));
50
+ getActivity().runOnUiThread(() -> implementation.start(front, voidCallback(call)));
51
+ }
52
+
53
+ @PluginMethod
54
+ public void stop(PluginCall call) {
55
+ getActivity().runOnUiThread(() -> {
56
+ implementation.stop();
57
+ call.resolve();
58
+ });
59
+ }
60
+
61
+ @PluginMethod
62
+ public void flip(PluginCall call) {
63
+ if (!ensureRunning(call)) return;
64
+ getActivity().runOnUiThread(() -> implementation.flip(voidCallback(call)));
65
+ }
66
+
67
+ @PluginMethod
68
+ public void capture(PluginCall call) {
69
+ if (!ensureRunning(call)) return;
70
+ int quality = Math.max(0, Math.min(100, call.getInt("quality", 85)));
71
+ implementation.capture(
72
+ quality,
73
+ new CardScanner.Callback<>() {
74
+ @Override
75
+ public void onSuccess(CardScanner.Photo photo) {
76
+ JSObject ret = new JSObject();
77
+ ret.put("base64", photo.base64);
78
+ ret.put("width", photo.width);
79
+ ret.put("height", photo.height);
80
+ call.resolve(ret);
81
+ }
82
+
83
+ @Override
84
+ public void onError(String message, Exception e) {
85
+ call.reject(message, e);
86
+ }
87
+ }
88
+ );
89
+ }
90
+
91
+ @PluginMethod
92
+ public void setTorch(PluginCall call) {
93
+ if (!ensureRunning(call)) return;
94
+ implementation.setTorch(Boolean.TRUE.equals(call.getBoolean("enabled", false)));
95
+ call.resolve();
96
+ }
97
+
98
+ @PluginMethod
99
+ public void setZoom(PluginCall call) {
100
+ if (!ensureRunning(call)) return;
101
+ Float ratio = call.getFloat("ratio");
102
+ if (ratio == null) {
103
+ call.reject("Missing 'ratio'");
104
+ return;
105
+ }
106
+ implementation.setZoom(ratio);
107
+ call.resolve();
108
+ }
109
+
110
+ private boolean ensureRunning(PluginCall call) {
111
+ if (!implementation.isRunning()) {
112
+ call.reject("Camera is not running", "NOT_RUNNING");
113
+ return false;
114
+ }
115
+ return true;
116
+ }
117
+
118
+ private CardScanner.Callback<Void> voidCallback(PluginCall call) {
119
+ return new CardScanner.Callback<>() {
120
+ @Override
121
+ public void onSuccess(Void result) {
122
+ call.resolve();
123
+ }
124
+
125
+ @Override
126
+ public void onError(String message, Exception e) {
127
+ call.reject(message, e);
128
+ }
129
+ };
130
+ }
131
+ }
File without changes
package/dist/docs.json ADDED
@@ -0,0 +1,296 @@
1
+ {
2
+ "api": {
3
+ "name": "CardScannerPlugin",
4
+ "slug": "cardscannerplugin",
5
+ "docs": "",
6
+ "tags": [],
7
+ "methods": [
8
+ {
9
+ "name": "start",
10
+ "signature": "(options?: StartOptions | undefined) => Promise<void>",
11
+ "parameters": [
12
+ {
13
+ "name": "options",
14
+ "docs": "",
15
+ "type": "StartOptions | undefined"
16
+ }
17
+ ],
18
+ "returns": "Promise<void>",
19
+ "tags": [],
20
+ "docs": "Opens the camera **behind** the WebView. The WebView background is made\ntransparent, so the HTML page must also use transparent backgrounds\nwhere the camera should be visible.",
21
+ "complexTypes": [
22
+ "StartOptions"
23
+ ],
24
+ "slug": "start"
25
+ },
26
+ {
27
+ "name": "stop",
28
+ "signature": "() => Promise<void>",
29
+ "parameters": [],
30
+ "returns": "Promise<void>",
31
+ "tags": [],
32
+ "docs": "Stops the camera and removes the preview.",
33
+ "complexTypes": [],
34
+ "slug": "stop"
35
+ },
36
+ {
37
+ "name": "capture",
38
+ "signature": "(options?: CaptureOptions | undefined) => Promise<CaptureResult>",
39
+ "parameters": [
40
+ {
41
+ "name": "options",
42
+ "docs": "",
43
+ "type": "CaptureOptions | undefined"
44
+ }
45
+ ],
46
+ "returns": "Promise<CaptureResult>",
47
+ "tags": [],
48
+ "docs": "Takes a photo from the running camera.",
49
+ "complexTypes": [
50
+ "CaptureResult",
51
+ "CaptureOptions"
52
+ ],
53
+ "slug": "capture"
54
+ },
55
+ {
56
+ "name": "setTorch",
57
+ "signature": "(options: TorchOptions) => Promise<void>",
58
+ "parameters": [
59
+ {
60
+ "name": "options",
61
+ "docs": "",
62
+ "type": "TorchOptions"
63
+ }
64
+ ],
65
+ "returns": "Promise<void>",
66
+ "tags": [],
67
+ "docs": "Turns the flashlight on/off.",
68
+ "complexTypes": [
69
+ "TorchOptions"
70
+ ],
71
+ "slug": "settorch"
72
+ },
73
+ {
74
+ "name": "setZoom",
75
+ "signature": "(options: ZoomOptions) => Promise<void>",
76
+ "parameters": [
77
+ {
78
+ "name": "options",
79
+ "docs": "",
80
+ "type": "ZoomOptions"
81
+ }
82
+ ],
83
+ "returns": "Promise<void>",
84
+ "tags": [],
85
+ "docs": "Sets the zoom ratio.",
86
+ "complexTypes": [
87
+ "ZoomOptions"
88
+ ],
89
+ "slug": "setzoom"
90
+ },
91
+ {
92
+ "name": "flip",
93
+ "signature": "() => Promise<void>",
94
+ "parameters": [],
95
+ "returns": "Promise<void>",
96
+ "tags": [],
97
+ "docs": "Switches between the rear and front camera.",
98
+ "complexTypes": [],
99
+ "slug": "flip"
100
+ },
101
+ {
102
+ "name": "checkPermissions",
103
+ "signature": "() => Promise<PermissionStatus>",
104
+ "parameters": [],
105
+ "returns": "Promise<PermissionStatus>",
106
+ "tags": [],
107
+ "docs": "",
108
+ "complexTypes": [
109
+ "PermissionStatus"
110
+ ],
111
+ "slug": "checkpermissions"
112
+ },
113
+ {
114
+ "name": "requestPermissions",
115
+ "signature": "() => Promise<PermissionStatus>",
116
+ "parameters": [],
117
+ "returns": "Promise<PermissionStatus>",
118
+ "tags": [],
119
+ "docs": "",
120
+ "complexTypes": [
121
+ "PermissionStatus"
122
+ ],
123
+ "slug": "requestpermissions"
124
+ }
125
+ ],
126
+ "properties": []
127
+ },
128
+ "interfaces": [
129
+ {
130
+ "name": "StartOptions",
131
+ "slug": "startoptions",
132
+ "docs": "",
133
+ "tags": [],
134
+ "methods": [],
135
+ "properties": [
136
+ {
137
+ "name": "position",
138
+ "tags": [
139
+ {
140
+ "text": "'rear'",
141
+ "name": "default"
142
+ }
143
+ ],
144
+ "docs": "Which camera to use.",
145
+ "complexTypes": [
146
+ "CameraPosition"
147
+ ],
148
+ "type": "CameraPosition"
149
+ }
150
+ ]
151
+ },
152
+ {
153
+ "name": "CaptureResult",
154
+ "slug": "captureresult",
155
+ "docs": "",
156
+ "tags": [],
157
+ "methods": [],
158
+ "properties": [
159
+ {
160
+ "name": "base64",
161
+ "tags": [],
162
+ "docs": "Base64 encoded JPEG (without the `data:` prefix).",
163
+ "complexTypes": [],
164
+ "type": "string"
165
+ },
166
+ {
167
+ "name": "width",
168
+ "tags": [],
169
+ "docs": "",
170
+ "complexTypes": [],
171
+ "type": "number"
172
+ },
173
+ {
174
+ "name": "height",
175
+ "tags": [],
176
+ "docs": "",
177
+ "complexTypes": [],
178
+ "type": "number"
179
+ }
180
+ ]
181
+ },
182
+ {
183
+ "name": "CaptureOptions",
184
+ "slug": "captureoptions",
185
+ "docs": "",
186
+ "tags": [],
187
+ "methods": [],
188
+ "properties": [
189
+ {
190
+ "name": "quality",
191
+ "tags": [
192
+ {
193
+ "text": "85",
194
+ "name": "default"
195
+ }
196
+ ],
197
+ "docs": "JPEG quality (0-100).",
198
+ "complexTypes": [],
199
+ "type": "number | undefined"
200
+ }
201
+ ]
202
+ },
203
+ {
204
+ "name": "TorchOptions",
205
+ "slug": "torchoptions",
206
+ "docs": "",
207
+ "tags": [],
208
+ "methods": [],
209
+ "properties": [
210
+ {
211
+ "name": "enabled",
212
+ "tags": [],
213
+ "docs": "",
214
+ "complexTypes": [],
215
+ "type": "boolean"
216
+ }
217
+ ]
218
+ },
219
+ {
220
+ "name": "ZoomOptions",
221
+ "slug": "zoomoptions",
222
+ "docs": "",
223
+ "tags": [],
224
+ "methods": [],
225
+ "properties": [
226
+ {
227
+ "name": "ratio",
228
+ "tags": [],
229
+ "docs": "Zoom ratio, clamped between the camera's min and max.",
230
+ "complexTypes": [],
231
+ "type": "number"
232
+ }
233
+ ]
234
+ },
235
+ {
236
+ "name": "PermissionStatus",
237
+ "slug": "permissionstatus",
238
+ "docs": "",
239
+ "tags": [],
240
+ "methods": [],
241
+ "properties": [
242
+ {
243
+ "name": "camera",
244
+ "tags": [],
245
+ "docs": "",
246
+ "complexTypes": [
247
+ "PermissionState"
248
+ ],
249
+ "type": "PermissionState"
250
+ }
251
+ ]
252
+ }
253
+ ],
254
+ "enums": [],
255
+ "typeAliases": [
256
+ {
257
+ "name": "CameraPosition",
258
+ "slug": "cameraposition",
259
+ "docs": "",
260
+ "types": [
261
+ {
262
+ "text": "'rear'",
263
+ "complexTypes": []
264
+ },
265
+ {
266
+ "text": "'front'",
267
+ "complexTypes": []
268
+ }
269
+ ]
270
+ },
271
+ {
272
+ "name": "PermissionState",
273
+ "slug": "permissionstate",
274
+ "docs": "",
275
+ "types": [
276
+ {
277
+ "text": "'prompt'",
278
+ "complexTypes": []
279
+ },
280
+ {
281
+ "text": "'prompt-with-rationale'",
282
+ "complexTypes": []
283
+ },
284
+ {
285
+ "text": "'granted'",
286
+ "complexTypes": []
287
+ },
288
+ {
289
+ "text": "'denied'",
290
+ "complexTypes": []
291
+ }
292
+ ]
293
+ }
294
+ ],
295
+ "pluginConfigs": []
296
+ }
@@ -0,0 +1,52 @@
1
+ import type { PermissionState } from '@capacitor/core';
2
+ export type CameraPosition = 'rear' | 'front';
3
+ export interface StartOptions {
4
+ /**
5
+ * Which camera to use.
6
+ * @default 'rear'
7
+ */
8
+ position?: CameraPosition;
9
+ }
10
+ export interface CaptureOptions {
11
+ /**
12
+ * JPEG quality (0-100).
13
+ * @default 85
14
+ */
15
+ quality?: number;
16
+ }
17
+ export interface CaptureResult {
18
+ /** Base64 encoded JPEG (without the `data:` prefix). */
19
+ base64: string;
20
+ width: number;
21
+ height: number;
22
+ }
23
+ export interface TorchOptions {
24
+ enabled: boolean;
25
+ }
26
+ export interface ZoomOptions {
27
+ /** Zoom ratio, clamped between the camera's min and max. */
28
+ ratio: number;
29
+ }
30
+ export interface PermissionStatus {
31
+ camera: PermissionState;
32
+ }
33
+ export interface CardScannerPlugin {
34
+ /**
35
+ * Opens the camera **behind** the WebView. The WebView background is made
36
+ * transparent, so the HTML page must also use transparent backgrounds
37
+ * where the camera should be visible.
38
+ */
39
+ start(options?: StartOptions): Promise<void>;
40
+ /** Stops the camera and removes the preview. */
41
+ stop(): Promise<void>;
42
+ /** Takes a photo from the running camera. */
43
+ capture(options?: CaptureOptions): Promise<CaptureResult>;
44
+ /** Turns the flashlight on/off. */
45
+ setTorch(options: TorchOptions): Promise<void>;
46
+ /** Sets the zoom ratio. */
47
+ setZoom(options: ZoomOptions): Promise<void>;
48
+ /** Switches between the rear and front camera. */
49
+ flip(): Promise<void>;
50
+ checkPermissions(): Promise<PermissionStatus>;
51
+ requestPermissions(): Promise<PermissionStatus>;
52
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=definitions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["import type { PermissionState } from '@capacitor/core';\n\nexport type CameraPosition = 'rear' | 'front';\n\nexport interface StartOptions {\n /**\n * Which camera to use.\n * @default 'rear'\n */\n position?: CameraPosition;\n}\n\nexport interface CaptureOptions {\n /**\n * JPEG quality (0-100).\n * @default 85\n */\n quality?: number;\n}\n\nexport interface CaptureResult {\n /** Base64 encoded JPEG (without the `data:` prefix). */\n base64: string;\n width: number;\n height: number;\n}\n\nexport interface TorchOptions {\n enabled: boolean;\n}\n\nexport interface ZoomOptions {\n /** Zoom ratio, clamped between the camera's min and max. */\n ratio: number;\n}\n\nexport interface PermissionStatus {\n camera: PermissionState;\n}\n\nexport interface CardScannerPlugin {\n /**\n * Opens the camera **behind** the WebView. The WebView background is made\n * transparent, so the HTML page must also use transparent backgrounds\n * where the camera should be visible.\n */\n start(options?: StartOptions): Promise<void>;\n\n /** Stops the camera and removes the preview. */\n stop(): Promise<void>;\n\n /** Takes a photo from the running camera. */\n capture(options?: CaptureOptions): Promise<CaptureResult>;\n\n /** Turns the flashlight on/off. */\n setTorch(options: TorchOptions): Promise<void>;\n\n /** Sets the zoom ratio. */\n setZoom(options: ZoomOptions): Promise<void>;\n\n /** Switches between the rear and front camera. */\n flip(): Promise<void>;\n\n checkPermissions(): Promise<PermissionStatus>;\n\n requestPermissions(): Promise<PermissionStatus>;\n}\n"]}
@@ -0,0 +1,4 @@
1
+ import type { CardScannerPlugin } from './definitions';
2
+ declare const CardScanner: CardScannerPlugin;
3
+ export * from './definitions';
4
+ export { CardScanner };
@@ -0,0 +1,7 @@
1
+ import { registerPlugin } from '@capacitor/core';
2
+ const CardScanner = registerPlugin('CardScanner', {
3
+ web: () => import('./web').then((m) => new m.CardScannerWeb()),
4
+ });
5
+ export * from './definitions';
6
+ export { CardScanner };
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAIjD,MAAM,WAAW,GAAG,cAAc,CAAoB,aAAa,EAAE;IACnE,GAAG,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,cAAc,EAAE,CAAC;CAC/D,CAAC,CAAC;AAEH,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,WAAW,EAAE,CAAC","sourcesContent":["import { registerPlugin } from '@capacitor/core';\n\nimport type { CardScannerPlugin } from './definitions';\n\nconst CardScanner = registerPlugin<CardScannerPlugin>('CardScanner', {\n web: () => import('./web').then((m) => new m.CardScannerWeb()),\n});\n\nexport * from './definitions';\nexport { CardScanner };\n"]}
@@ -0,0 +1,19 @@
1
+ import { WebPlugin } from '@capacitor/core';
2
+ import type { CaptureOptions, CaptureResult, CardScannerPlugin, PermissionStatus, StartOptions, TorchOptions, ZoomOptions } from './definitions';
3
+ /**
4
+ * Browser implementation, useful for testing the layout on a desktop.
5
+ * The <video> element is placed fixed behind the page (z-index -1).
6
+ */
7
+ export declare class CardScannerWeb extends WebPlugin implements CardScannerPlugin {
8
+ private video;
9
+ private stream;
10
+ private position;
11
+ start(options?: StartOptions): Promise<void>;
12
+ stop(): Promise<void>;
13
+ capture(options?: CaptureOptions): Promise<CaptureResult>;
14
+ setTorch(_options: TorchOptions): Promise<void>;
15
+ setZoom(_options: ZoomOptions): Promise<void>;
16
+ flip(): Promise<void>;
17
+ checkPermissions(): Promise<PermissionStatus>;
18
+ requestPermissions(): Promise<PermissionStatus>;
19
+ }
@@ -0,0 +1,86 @@
1
+ import { WebPlugin } from '@capacitor/core';
2
+ /**
3
+ * Browser implementation, useful for testing the layout on a desktop.
4
+ * The <video> element is placed fixed behind the page (z-index -1).
5
+ */
6
+ export class CardScannerWeb extends WebPlugin {
7
+ constructor() {
8
+ super(...arguments);
9
+ this.video = null;
10
+ this.stream = null;
11
+ this.position = 'rear';
12
+ }
13
+ async start(options) {
14
+ var _a;
15
+ await this.stop();
16
+ this.position = (_a = options === null || options === void 0 ? void 0 : options.position) !== null && _a !== void 0 ? _a : 'rear';
17
+ this.stream = await navigator.mediaDevices.getUserMedia({
18
+ video: { facingMode: this.position === 'front' ? 'user' : 'environment' },
19
+ audio: false,
20
+ });
21
+ const video = document.createElement('video');
22
+ video.setAttribute('playsinline', '');
23
+ video.muted = true;
24
+ video.autoplay = true;
25
+ Object.assign(video.style, {
26
+ position: 'fixed',
27
+ inset: '0',
28
+ width: '100%',
29
+ height: '100%',
30
+ objectFit: 'cover',
31
+ zIndex: '-1',
32
+ });
33
+ video.srcObject = this.stream;
34
+ document.body.appendChild(video);
35
+ await video.play();
36
+ this.video = video;
37
+ }
38
+ async stop() {
39
+ var _a, _b;
40
+ (_a = this.stream) === null || _a === void 0 ? void 0 : _a.getTracks().forEach((t) => t.stop());
41
+ this.stream = null;
42
+ (_b = this.video) === null || _b === void 0 ? void 0 : _b.remove();
43
+ this.video = null;
44
+ }
45
+ async capture(options) {
46
+ var _a, _b;
47
+ if (!this.video)
48
+ throw new Error('Camera is not running');
49
+ const canvas = document.createElement('canvas');
50
+ canvas.width = this.video.videoWidth;
51
+ canvas.height = this.video.videoHeight;
52
+ (_a = canvas.getContext('2d')) === null || _a === void 0 ? void 0 : _a.drawImage(this.video, 0, 0);
53
+ const quality = ((_b = options === null || options === void 0 ? void 0 : options.quality) !== null && _b !== void 0 ? _b : 85) / 100;
54
+ const base64 = canvas.toDataURL('image/jpeg', quality).split(',')[1];
55
+ return { base64, width: canvas.width, height: canvas.height };
56
+ }
57
+ async setTorch(_options) {
58
+ throw this.unavailable('Torch is not available on web');
59
+ }
60
+ async setZoom(_options) {
61
+ throw this.unavailable('Zoom is not available on web');
62
+ }
63
+ async flip() {
64
+ await this.start({ position: this.position === 'rear' ? 'front' : 'rear' });
65
+ }
66
+ async checkPermissions() {
67
+ try {
68
+ const status = await navigator.permissions.query({ name: 'camera' });
69
+ return { camera: status.state };
70
+ }
71
+ catch (_a) {
72
+ return { camera: 'prompt' };
73
+ }
74
+ }
75
+ async requestPermissions() {
76
+ try {
77
+ const s = await navigator.mediaDevices.getUserMedia({ video: true });
78
+ s.getTracks().forEach((t) => t.stop());
79
+ return { camera: 'granted' };
80
+ }
81
+ catch (_a) {
82
+ return { camera: 'denied' };
83
+ }
84
+ }
85
+ }
86
+ //# sourceMappingURL=web.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"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","sourcesContent":["import { WebPlugin } from '@capacitor/core';\n\nimport type {\n CameraPosition,\n CaptureOptions,\n CaptureResult,\n CardScannerPlugin,\n PermissionStatus,\n StartOptions,\n TorchOptions,\n ZoomOptions,\n} from './definitions';\n\n/**\n * Browser implementation, useful for testing the layout on a desktop.\n * The <video> element is placed fixed behind the page (z-index -1).\n */\nexport class CardScannerWeb extends WebPlugin implements CardScannerPlugin {\n private video: HTMLVideoElement | null = null;\n private stream: MediaStream | null = null;\n private position: CameraPosition = 'rear';\n\n async start(options?: StartOptions): Promise<void> {\n await this.stop();\n this.position = options?.position ?? 'rear';\n\n this.stream = await navigator.mediaDevices.getUserMedia({\n video: { facingMode: this.position === 'front' ? 'user' : 'environment' },\n audio: false,\n });\n\n const video = document.createElement('video');\n video.setAttribute('playsinline', '');\n video.muted = true;\n video.autoplay = true;\n Object.assign(video.style, {\n position: 'fixed',\n inset: '0',\n width: '100%',\n height: '100%',\n objectFit: 'cover',\n zIndex: '-1',\n });\n video.srcObject = this.stream;\n document.body.appendChild(video);\n await video.play();\n this.video = video;\n }\n\n async stop(): Promise<void> {\n this.stream?.getTracks().forEach((t) => t.stop());\n this.stream = null;\n this.video?.remove();\n this.video = null;\n }\n\n async capture(options?: CaptureOptions): Promise<CaptureResult> {\n if (!this.video) throw new Error('Camera is not running');\n const canvas = document.createElement('canvas');\n canvas.width = this.video.videoWidth;\n canvas.height = this.video.videoHeight;\n canvas.getContext('2d')?.drawImage(this.video, 0, 0);\n const quality = (options?.quality ?? 85) / 100;\n const base64 = canvas.toDataURL('image/jpeg', quality).split(',')[1];\n return { base64, width: canvas.width, height: canvas.height };\n }\n\n async setTorch(_options: TorchOptions): Promise<void> {\n throw this.unavailable('Torch is not available on web');\n }\n\n async setZoom(_options: ZoomOptions): Promise<void> {\n throw this.unavailable('Zoom is not available on web');\n }\n\n async flip(): Promise<void> {\n await this.start({ position: this.position === 'rear' ? 'front' : 'rear' });\n }\n\n async checkPermissions(): Promise<PermissionStatus> {\n try {\n const status = await navigator.permissions.query({ name: 'camera' as PermissionName });\n return { camera: status.state };\n } catch {\n return { camera: 'prompt' };\n }\n }\n\n async requestPermissions(): Promise<PermissionStatus> {\n try {\n const s = await navigator.mediaDevices.getUserMedia({ video: true });\n s.getTracks().forEach((t) => t.stop());\n return { camera: 'granted' };\n } catch {\n return { camera: 'denied' };\n }\n }\n}\n"]}