@agicash/qr-scanner 0.1.0 → 0.1.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.
package/dist/index.cjs CHANGED
@@ -26,6 +26,15 @@ __export(index_exports, {
26
26
  });
27
27
  module.exports = __toCommonJS(index_exports);
28
28
 
29
+ // src/debug.ts
30
+ var enabled = false;
31
+ function setDebug(on) {
32
+ enabled = on;
33
+ }
34
+ function debug(...args) {
35
+ if (enabled) console.debug(...args);
36
+ }
37
+
29
38
  // src/camera.ts
30
39
  var CameraPermissionError = class extends Error {
31
40
  constructor(message = "Camera access denied. Please grant camera permission and try again.") {
@@ -66,16 +75,16 @@ var CameraManager = class {
66
75
  const t0 = performance.now();
67
76
  this.stream = await this.acquireStream();
68
77
  const t1 = performance.now();
69
- console.debug(`[QrScanner] acquireStream: ${(t1 - t0).toFixed(0)}ms`);
78
+ debug(`[QrScanner] acquireStream: ${(t1 - t0).toFixed(0)}ms`);
70
79
  await this.ensureBestCamera();
71
80
  const t2 = performance.now();
72
- console.debug(`[QrScanner] ensureBestCamera: ${(t2 - t1).toFixed(0)}ms`);
81
+ debug(`[QrScanner] ensureBestCamera: ${(t2 - t1).toFixed(0)}ms`);
73
82
  video.srcObject = this.stream;
74
83
  video.setAttribute("playsinline", "true");
75
84
  await video.play();
76
85
  const t3 = performance.now();
77
- console.debug(`[QrScanner] video.play: ${(t3 - t2).toFixed(0)}ms`);
78
- console.debug(`[QrScanner] camera.start total: ${(t3 - t0).toFixed(0)}ms`);
86
+ debug(`[QrScanner] video.play: ${(t3 - t2).toFixed(0)}ms`);
87
+ debug(`[QrScanner] camera.start total: ${(t3 - t0).toFixed(0)}ms`);
79
88
  if (this.facingMode === "environment" || this.facingMode === "user") {
80
89
  const finalDeviceId = this.getVideoTrack()?.getSettings().deviceId;
81
90
  if (finalDeviceId) {
@@ -150,9 +159,7 @@ var CameraManager = class {
150
159
  */
151
160
  async ensureBestCamera() {
152
161
  if (this.facingMode !== "environment" && this.facingMode !== "user") {
153
- console.debug(
154
- "[QrScanner] ensureBestCamera: skipped (specific deviceId)"
155
- );
162
+ debug("[QrScanner] ensureBestCamera: skipped (specific deviceId)");
156
163
  return;
157
164
  }
158
165
  const track = this.getVideoTrack();
@@ -160,12 +167,12 @@ var CameraManager = class {
160
167
  try {
161
168
  const capabilities = track.getCapabilities();
162
169
  if (!capabilities.focusMode || capabilities.focusMode.includes("continuous")) {
163
- console.debug(
170
+ debug(
164
171
  `[QrScanner] ensureBestCamera: skipped (focusMode: ${JSON.stringify(capabilities.focusMode)})`
165
172
  );
166
173
  return;
167
174
  }
168
- console.debug(
175
+ debug(
169
176
  `[QrScanner] ensureBestCamera: current camera lacks autofocus (focusMode: ${JSON.stringify(capabilities.focusMode)})`
170
177
  );
171
178
  } catch {
@@ -182,7 +189,7 @@ var CameraManager = class {
182
189
  const candidates = devices.filter(
183
190
  (d) => d.kind === "videoinput" && d.deviceId !== currentDeviceId
184
191
  );
185
- console.debug(
192
+ debug(
186
193
  `[QrScanner] ensureBestCamera: testing ${candidates.length} candidate camera(s)`
187
194
  );
188
195
  if (candidates.length === 0) return;
@@ -199,11 +206,11 @@ var CameraManager = class {
199
206
  },
200
207
  audio: false
201
208
  });
202
- console.debug(
209
+ debug(
203
210
  `[QrScanner] ensureBestCamera: candidate ${candidate.label || candidate.deviceId.slice(0, 8)}: getUserMedia ${(performance.now() - t).toFixed(0)}ms`
204
211
  );
205
212
  } catch {
206
- console.debug(
213
+ debug(
207
214
  `[QrScanner] ensureBestCamera: candidate ${candidate.label || candidate.deviceId.slice(0, 8)}: getUserMedia failed ${(performance.now() - t).toFixed(0)}ms`
208
215
  );
209
216
  continue;
@@ -289,12 +296,12 @@ var CameraManager = class {
289
296
  const t = performance.now();
290
297
  try {
291
298
  const stream = await navigator.mediaDevices.getUserMedia(attempts[i]);
292
- console.debug(
299
+ debug(
293
300
  `[QrScanner] getUserMedia(${labels[i]}): ${(performance.now() - t).toFixed(0)}ms \u2713`
294
301
  );
295
302
  return stream;
296
303
  } catch (err) {
297
- console.debug(
304
+ debug(
298
305
  `[QrScanner] getUserMedia(${labels[i]}): ${(performance.now() - t).toFixed(0)}ms \u2717 ${err instanceof DOMException ? err.name : err}`
299
306
  );
300
307
  if (err instanceof DOMException) {
@@ -683,7 +690,7 @@ var Scanner = class {
683
690
  }
684
691
  }
685
692
  await this.camera.start(this.video);
686
- console.debug(
693
+ debug(
687
694
  `[QrScanner] start: camera ready ${(performance.now() - t0).toFixed(0)}ms`
688
695
  );
689
696
  if (this.overlay) {
@@ -692,7 +699,7 @@ var Scanner = class {
692
699
  if (!this.worker) {
693
700
  const tw = performance.now();
694
701
  this.worker = this.createWorker();
695
- console.debug(
702
+ debug(
696
703
  `[QrScanner] start: worker created ${(performance.now() - tw).toFixed(0)}ms`
697
704
  );
698
705
  }
@@ -707,9 +714,7 @@ var Scanner = class {
707
714
  });
708
715
  this.active = true;
709
716
  this.paused = false;
710
- console.debug(
711
- `[QrScanner] start: total ${(performance.now() - t0).toFixed(0)}ms`
712
- );
717
+ debug(`[QrScanner] start: total ${(performance.now() - t0).toFixed(0)}ms`);
713
718
  }
714
719
  stop() {
715
720
  this.frameExtractor?.stop();
@@ -1051,6 +1056,15 @@ var QrScanner = class {
1051
1056
  static setWorkerUrl(url) {
1052
1057
  setWorkerUrl(url);
1053
1058
  }
1059
+ /**
1060
+ * Enable or disable debug logging (performance timings, camera selection).
1061
+ * Off by default. Useful for diagnosing camera issues in the browser console.
1062
+ * @example
1063
+ * QrScanner.setDebug(true);
1064
+ */
1065
+ static setDebug(enabled2) {
1066
+ setDebug(enabled2);
1067
+ }
1054
1068
  /** Scan a single image (not a video stream). */
1055
1069
  static scanImage(source, options) {
1056
1070
  return scanImage(source, options);
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/camera.ts","../src/frame-extractor.ts","../src/overlay.ts","../src/scan-region.ts","../src/scanner.ts","../src/scan-image.ts","../src/utils.ts"],"sourcesContent":["import { Scanner, setWorkerUrl } from './scanner.js';\nimport {\n CameraManager,\n CameraNotFoundError,\n CameraPermissionError,\n} from './camera.js';\nimport { scanImage } from './scan-image.js';\nimport { setZXingModuleOverrides } from 'zxing-wasm/reader';\nimport type {\n ScanResult,\n ScanRegion,\n ScannerOptions,\n Camera,\n Point,\n FacingMode,\n DeviceId,\n InversionMode,\n} from './types.js';\nimport type { ReaderOptions } from 'zxing-wasm/reader';\n\nexport { CameraNotFoundError, CameraPermissionError };\n\nexport type {\n ScanResult,\n ScanRegion,\n ScannerOptions,\n Camera,\n Point,\n FacingMode,\n DeviceId,\n InversionMode,\n};\n\n/**\n * High-performance QR code scanner for the web, powered by ZXing-C++ WebAssembly.\n */\nclass QrScanner {\n private scanner: Scanner;\n\n constructor(\n videoElement: HTMLVideoElement,\n onDecode: (result: ScanResult) => void,\n options: ScannerOptions = {},\n ) {\n this.scanner = new Scanner(videoElement, onDecode, options);\n }\n\n /** Start camera and begin scanning. Resolves when camera is ready. */\n async start(): Promise<void> {\n return this.scanner.start();\n }\n\n /** Stop scanning and release the camera stream. */\n stop(): void {\n this.scanner.stop();\n }\n\n /** Stop scanning, release camera, terminate worker, clean up DOM. */\n destroy(): void {\n this.scanner.destroy();\n }\n\n /** Pause scanning. If stopStreamImmediately is false, camera stays on. */\n async pause(stopStreamImmediately?: boolean): Promise<boolean> {\n return this.scanner.pause(stopStreamImmediately);\n }\n\n /** Switch to a different camera by facing mode or device ID. */\n async setCamera(facingModeOrDeviceId: FacingMode | DeviceId): Promise<void> {\n return this.scanner.setCamera(facingModeOrDeviceId);\n }\n\n /** Check if the current camera supports flash/torch. */\n async hasFlash(): Promise<boolean> {\n return this.scanner.hasFlash();\n }\n\n /** Whether flash is currently on. */\n isFlashOn(): boolean {\n return this.scanner.isFlashOn();\n }\n\n /** Toggle flash on/off. */\n async toggleFlash(): Promise<void> {\n return this.scanner.toggleFlash();\n }\n\n /** Turn flash on. */\n async turnFlashOn(): Promise<void> {\n return this.scanner.turnFlashOn();\n }\n\n /** Turn flash off. */\n async turnFlashOff(): Promise<void> {\n return this.scanner.turnFlashOff();\n }\n\n /** Set the inversion mode for detecting inverted QR codes. */\n setInversionMode(mode: InversionMode): void {\n this.scanner.setInversionMode(mode);\n }\n\n // --- Static methods ---\n\n /** Check if the device has at least one camera. */\n static hasCamera(): Promise<boolean> {\n return CameraManager.hasCamera();\n }\n\n /** List available cameras. Pass true to request labels (triggers permission prompt). */\n static listCameras(requestLabels?: boolean): Promise<Camera[]> {\n return CameraManager.listCameras(requestLabels);\n }\n\n /**\n * Pre-load the WASM binary so it's ready when the scanner starts.\n * Call this early (e.g., on app init) to avoid delay on first scan.\n */\n static async preload(): Promise<void> {\n // Trigger WASM loading by doing a minimal scan\n const pixel = new Uint8ClampedArray([255, 255, 255, 255]);\n const img = new ImageData(pixel, 1, 1);\n try {\n await scanImage(img);\n } catch {\n // Expected — no QR code in a 1x1 image. The point was to load WASM.\n }\n }\n\n /**\n * Configure WASM loading. Call before creating any scanner instance.\n * @example\n * QrScanner.configureWasm({ locateFile: (filename) => `/wasm/${filename}` });\n */\n static configureWasm(overrides: Partial<EmscriptenModule>): void {\n setZXingModuleOverrides(overrides);\n }\n\n /**\n * Set a custom URL for the worker script. Call before creating any scanner.\n * Needed for CJS consumers or non-standard bundler setups.\n * By default, the worker URL is resolved via `new URL('./worker.js', import.meta.url)`,\n * which works with Vite, webpack 5, Parcel, and other modern bundlers.\n * @example\n * QrScanner.setWorkerUrl('/assets/qr-scanner-worker.js');\n */\n static setWorkerUrl(url: string | URL): void {\n setWorkerUrl(url);\n }\n\n /** Scan a single image (not a video stream). */\n static scanImage(\n source:\n | HTMLImageElement\n | HTMLCanvasElement\n | OffscreenCanvas\n | ImageBitmap\n | ImageData\n | Blob\n | ArrayBuffer\n | Uint8Array\n | File\n | URL\n | string,\n options?: {\n scanRegion?: ScanRegion | null;\n canvas?: HTMLCanvasElement | null;\n decoderOptions?: Partial<ReaderOptions>;\n },\n ): Promise<ScanResult> {\n return scanImage(source, options);\n }\n}\n\nexport default QrScanner;\n","import type { FacingMode, DeviceId, Camera } from './types.js';\n\nexport class CameraPermissionError extends Error {\n constructor(\n message = 'Camera access denied. Please grant camera permission and try again.',\n ) {\n super(message);\n this.name = 'CameraPermissionError';\n }\n}\n\nexport class CameraNotFoundError extends Error {\n constructor(\n message = 'No camera found. Please connect a camera and try again.',\n ) {\n super(message);\n this.name = 'CameraNotFoundError';\n }\n}\n\nconst CACHE_KEY_PREFIX = '@agicash/qr-scanner:camera:';\n\nfunction getCachedDeviceId(facingMode: string): string | null {\n try {\n return localStorage.getItem(`${CACHE_KEY_PREFIX}${facingMode}`);\n } catch {\n return null;\n }\n}\n\nfunction setCachedDeviceId(facingMode: string, deviceId: string): void {\n try {\n localStorage.setItem(`${CACHE_KEY_PREFIX}${facingMode}`, deviceId);\n } catch {\n // localStorage unavailable or full — ignore\n }\n}\n\nexport interface CameraConfig {\n preferredCamera?: FacingMode | DeviceId;\n cameraResolution?: {\n width?: MediaTrackConstraintSet['width'];\n height?: MediaTrackConstraintSet['height'];\n };\n}\n\nexport class CameraManager {\n private stream: MediaStream | null = null;\n private facingMode: FacingMode | DeviceId;\n private resolution: CameraConfig['cameraResolution'];\n\n constructor(config: CameraConfig = {}) {\n this.facingMode = config.preferredCamera ?? 'environment';\n this.resolution = config.cameraResolution;\n }\n\n async start(video: HTMLVideoElement): Promise<MediaStream> {\n if (this.stream) {\n return this.stream;\n }\n\n const t0 = performance.now();\n\n this.stream = await this.acquireStream();\n const t1 = performance.now();\n console.debug(`[QrScanner] acquireStream: ${(t1 - t0).toFixed(0)}ms`);\n\n // On some devices (e.g. Samsung S24 + Brave), facingMode: 'environment'\n // picks an ultrawide camera that lacks autofocus. Check and switch to a\n // better camera BEFORE showing on screen to avoid visible flicker.\n await this.ensureBestCamera();\n const t2 = performance.now();\n console.debug(`[QrScanner] ensureBestCamera: ${(t2 - t1).toFixed(0)}ms`);\n\n video.srcObject = this.stream;\n video.setAttribute('playsinline', 'true');\n await video.play();\n const t3 = performance.now();\n console.debug(`[QrScanner] video.play: ${(t3 - t2).toFixed(0)}ms`);\n console.debug(`[QrScanner] camera.start total: ${(t3 - t0).toFixed(0)}ms`);\n\n // Cache the final camera so subsequent starts skip ensureBestCamera\n if (this.facingMode === 'environment' || this.facingMode === 'user') {\n const finalDeviceId = this.getVideoTrack()?.getSettings().deviceId;\n if (finalDeviceId) {\n setCachedDeviceId(this.facingMode, finalDeviceId);\n }\n }\n\n return this.stream;\n }\n\n stop(): void {\n if (this.stream) {\n for (const track of this.stream.getTracks()) {\n track.stop();\n }\n this.stream = null;\n }\n }\n\n async setCamera(\n facingModeOrDeviceId: FacingMode | DeviceId,\n video: HTMLVideoElement,\n ): Promise<void> {\n this.stop();\n this.facingMode = facingModeOrDeviceId;\n await this.start(video);\n }\n\n getStream(): MediaStream | null {\n return this.stream;\n }\n\n async hasFlash(): Promise<boolean> {\n const track = this.getVideoTrack();\n if (!track) return false;\n\n try {\n const capabilities = track.getCapabilities() as MediaTrackCapabilities & {\n torch?: boolean;\n };\n return capabilities.torch === true;\n } catch {\n return false;\n }\n }\n\n isFlashOn(): boolean {\n const track = this.getVideoTrack();\n if (!track) return false;\n\n const settings = track.getSettings() as MediaTrackSettings & {\n torch?: boolean;\n };\n return settings.torch === true;\n }\n\n async toggleFlash(): Promise<void> {\n if (this.isFlashOn()) {\n await this.turnFlashOff();\n } else {\n await this.turnFlashOn();\n }\n }\n\n async turnFlashOn(): Promise<void> {\n await this.setTorch(true);\n }\n\n async turnFlashOff(): Promise<void> {\n await this.setTorch(false);\n }\n\n private async setTorch(on: boolean): Promise<void> {\n const track = this.getVideoTrack();\n if (!track) {\n throw new Error('No active camera stream');\n }\n\n try {\n await track.applyConstraints({\n advanced: [{ torch: on } as MediaTrackConstraintSet],\n });\n } catch {\n throw new Error('Flash/torch is not supported on this device');\n }\n }\n\n /**\n * If the current camera lacks continuous autofocus (e.g. an ultrawide sensor\n * picked by facingMode: 'environment'), find a better camera with the same\n * facing mode and replace this.stream. Called before assigning to the video\n * element so the user never sees the wrong camera.\n */\n private async ensureBestCamera(): Promise<void> {\n if (this.facingMode !== 'environment' && this.facingMode !== 'user') {\n console.debug(\n '[QrScanner] ensureBestCamera: skipped (specific deviceId)',\n );\n return;\n }\n\n const track = this.getVideoTrack();\n if (!track) return;\n\n try {\n const capabilities = track.getCapabilities() as MediaTrackCapabilities & {\n focusMode?: string[];\n };\n if (\n !capabilities.focusMode ||\n capabilities.focusMode.includes('continuous')\n ) {\n // focusMode not reported (e.g. Safari/iOS) or has autofocus — skip.\n // Only enter the candidate loop when the browser explicitly reports\n // focusMode without 'continuous' (e.g. S24 + Brave ultrawide).\n console.debug(\n `[QrScanner] ensureBestCamera: skipped (focusMode: ${JSON.stringify(capabilities.focusMode)})`,\n );\n return;\n }\n console.debug(\n `[QrScanner] ensureBestCamera: current camera lacks autofocus (focusMode: ${JSON.stringify(capabilities.focusMode)})`,\n );\n } catch {\n return;\n }\n\n // Current camera lacks continuous autofocus.\n // Enumerate devices while stream is active (ensures deviceIds are available).\n const currentDeviceId = track.getSettings().deviceId;\n\n let devices: MediaDeviceInfo[];\n try {\n devices = await navigator.mediaDevices.enumerateDevices();\n } catch {\n return;\n }\n if (!Array.isArray(devices)) return;\n\n const candidates = devices.filter(\n (d) => d.kind === 'videoinput' && d.deviceId !== currentDeviceId,\n );\n console.debug(\n `[QrScanner] ensureBestCamera: testing ${candidates.length} candidate camera(s)`,\n );\n if (candidates.length === 0) return;\n\n // Stop current stream — mobile devices only allow one active camera\n this.stop();\n\n for (const candidate of candidates) {\n const t = performance.now();\n let candidateStream: MediaStream;\n try {\n candidateStream = await navigator.mediaDevices.getUserMedia({\n video: {\n deviceId: { exact: candidate.deviceId },\n width: this.resolution?.width ?? { ideal: 1920 },\n height: this.resolution?.height ?? { ideal: 1080 },\n },\n audio: false,\n });\n console.debug(\n `[QrScanner] ensureBestCamera: candidate ${candidate.label || candidate.deviceId.slice(0, 8)}: getUserMedia ${(performance.now() - t).toFixed(0)}ms`,\n );\n } catch {\n console.debug(\n `[QrScanner] ensureBestCamera: candidate ${candidate.label || candidate.deviceId.slice(0, 8)}: getUserMedia failed ${(performance.now() - t).toFixed(0)}ms`,\n );\n continue;\n }\n\n const candidateTrack = candidateStream.getVideoTracks()[0];\n if (!candidateTrack) {\n for (const t of candidateStream.getTracks()) t.stop();\n continue;\n }\n\n // Must match the desired facing mode\n const candidateSettings =\n candidateTrack.getSettings() as MediaTrackSettings & {\n facingMode?: string;\n };\n if (\n candidateSettings.facingMode &&\n candidateSettings.facingMode !== this.facingMode\n ) {\n for (const t of candidateStream.getTracks()) t.stop();\n continue;\n }\n\n // Check if this camera supports continuous autofocus\n try {\n const candidateCaps =\n candidateTrack.getCapabilities() as MediaTrackCapabilities & {\n focusMode?: string[];\n };\n if (candidateCaps.focusMode?.includes('continuous')) {\n this.stream = candidateStream;\n return;\n }\n } catch {\n // Can't check capabilities, skip\n }\n\n for (const t of candidateStream.getTracks()) t.stop();\n }\n\n // No better camera found — re-open the original\n try {\n this.stream = await navigator.mediaDevices.getUserMedia({\n video: {\n deviceId: currentDeviceId ? { exact: currentDeviceId } : undefined,\n width: this.resolution?.width ?? { ideal: 1920 },\n height: this.resolution?.height ?? { ideal: 1080 },\n },\n audio: false,\n });\n } catch {\n try {\n this.stream = await navigator.mediaDevices.getUserMedia(\n this.buildConstraints(),\n );\n } catch {\n // Could not recover camera\n }\n }\n }\n\n private getVideoTrack(): MediaStreamTrack | null {\n if (!this.stream) return null;\n const tracks = this.stream.getVideoTracks();\n return tracks[0] ?? null;\n }\n\n /**\n * Try getUserMedia with progressively simpler constraints.\n *\n * Some browsers (e.g. Brave on Samsung Galaxy S24) throw NotReadableError\n * when facingMode and resolution constraints are combined. Falling back to\n * fewer constraints lets us still open the camera on those browsers.\n */\n private async acquireStream(): Promise<MediaStream> {\n const labels: string[] = [];\n const attempts: MediaStreamConstraints[] = [];\n\n // If we've previously found a good camera for this facingMode, try it first\n if (this.facingMode === 'environment' || this.facingMode === 'user') {\n const cachedId = getCachedDeviceId(this.facingMode);\n if (cachedId) {\n labels.push('cached deviceId');\n const video: MediaTrackConstraints = {\n deviceId: { exact: cachedId },\n };\n if (this.resolution?.width) video.width = this.resolution.width;\n else video.width = { ideal: 1920 };\n if (this.resolution?.height) video.height = this.resolution.height;\n else video.height = { ideal: 1080 };\n attempts.push({ video, audio: false });\n }\n }\n\n // Standard fallback chain\n labels.push('full constraints', 'no resolution', 'bare minimum');\n attempts.push(\n // facingMode/deviceId + resolution\n this.buildConstraints(),\n // facingMode/deviceId only, no resolution\n this.buildConstraints(false),\n // Bare minimum\n { video: true, audio: false },\n );\n\n let lastError: unknown;\n for (let i = 0; i < attempts.length; i++) {\n const t = performance.now();\n try {\n const stream = await navigator.mediaDevices.getUserMedia(attempts[i]);\n console.debug(\n `[QrScanner] getUserMedia(${labels[i]}): ${(performance.now() - t).toFixed(0)}ms ✓`,\n );\n return stream;\n } catch (err) {\n console.debug(\n `[QrScanner] getUserMedia(${labels[i]}): ${(performance.now() - t).toFixed(0)}ms ✗ ${err instanceof DOMException ? err.name : err}`,\n );\n if (err instanceof DOMException) {\n if (err.name === 'NotAllowedError') {\n throw new CameraPermissionError();\n }\n if (err.name === 'NotFoundError') {\n throw new CameraNotFoundError();\n }\n // NotReadableError or OverconstrainedError — try next fallback\n lastError = err;\n continue;\n }\n throw err;\n }\n }\n\n throw lastError;\n }\n\n private buildConstraints(includeResolution = true): MediaStreamConstraints {\n const video: MediaTrackConstraints = {};\n\n if (includeResolution) {\n video.width = this.resolution?.width ?? { ideal: 1920 };\n video.height = this.resolution?.height ?? { ideal: 1080 };\n }\n\n if (this.facingMode === 'environment' || this.facingMode === 'user') {\n video.facingMode = this.facingMode;\n } else {\n video.deviceId = { exact: this.facingMode };\n }\n\n return { video, audio: false };\n }\n\n static async hasCamera(): Promise<boolean> {\n try {\n const devices = await navigator.mediaDevices.enumerateDevices();\n return devices.some((d) => d.kind === 'videoinput');\n } catch {\n return false;\n }\n }\n\n static async listCameras(requestLabels = false): Promise<Camera[]> {\n if (requestLabels) {\n // Requesting labels requires a temporary stream to trigger the permission prompt\n try {\n const stream = await navigator.mediaDevices.getUserMedia({\n video: true,\n });\n for (const track of stream.getTracks()) {\n track.stop();\n }\n } catch {\n // Permission denied — fall through with empty labels\n }\n }\n\n const devices = await navigator.mediaDevices.enumerateDevices();\n return devices\n .filter((d) => d.kind === 'videoinput')\n .map((d) => ({\n id: d.deviceId,\n label: d.label || `Camera ${d.deviceId.slice(0, 8)}`,\n }));\n }\n}\n","import type { ScanRegion } from './types.js';\n\nexport interface FrameExtractorConfig {\n maxScansPerSecond: number;\n getScanRegion: () => ScanRegion;\n}\n\nexport class FrameExtractor {\n private video: HTMLVideoElement;\n private canvas: HTMLCanvasElement | OffscreenCanvas;\n private ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D;\n private rafId: number | null = null;\n private running = false;\n private workerBusy = false;\n private lastScanTime = -Infinity;\n private minInterval: number;\n private getScanRegion: () => ScanRegion;\n private onFrame: ((imageData: ImageData) => void) | null = null;\n\n constructor(video: HTMLVideoElement, config: FrameExtractorConfig) {\n this.video = video;\n this.minInterval = 1000 / config.maxScansPerSecond;\n this.getScanRegion = config.getScanRegion;\n\n if (typeof OffscreenCanvas !== 'undefined') {\n this.canvas = new OffscreenCanvas(1, 1);\n this.ctx = this.canvas.getContext('2d')! as OffscreenCanvasRenderingContext2D;\n } else {\n this.canvas = document.createElement('canvas');\n this.canvas.style.display = 'none';\n this.ctx = this.canvas.getContext('2d')!;\n }\n }\n\n start(onFrame: (imageData: ImageData) => void): void {\n if (this.running) return;\n this.running = true;\n this.onFrame = onFrame;\n this.rafId = requestAnimationFrame(this.tick);\n }\n\n stop(): void {\n this.running = false;\n this.onFrame = null;\n if (this.rafId !== null) {\n cancelAnimationFrame(this.rafId);\n this.rafId = null;\n }\n }\n\n destroy(): void {\n this.stop();\n if (this.canvas instanceof HTMLCanvasElement && this.canvas.parentNode) {\n this.canvas.parentNode.removeChild(this.canvas);\n }\n }\n\n markWorkerIdle(): void {\n this.workerBusy = false;\n }\n\n markWorkerBusy(): void {\n this.workerBusy = true;\n }\n\n private tick = (): void => {\n if (!this.running) return;\n\n this.rafId = requestAnimationFrame(this.tick);\n\n // Skip if worker is still processing previous frame\n if (this.workerBusy) return;\n\n // Rate limiting\n const now = performance.now();\n if (now - this.lastScanTime < this.minInterval) return;\n\n // Skip if video isn't ready\n if (this.video.readyState < 2) return;\n\n this.lastScanTime = now;\n\n const region = this.getScanRegion();\n const sx = region.x ?? 0;\n const sy = region.y ?? 0;\n const sw = region.width ?? this.video.videoWidth;\n const sh = region.height ?? this.video.videoHeight;\n\n if (sw <= 0 || sh <= 0) return;\n\n this.canvas.width = sw;\n this.canvas.height = sh;\n\n this.ctx.drawImage(this.video, sx, sy, sw, sh, 0, 0, sw, sh);\n const imageData = this.ctx.getImageData(0, 0, sw, sh);\n\n this.workerBusy = true;\n this.onFrame?.(imageData);\n };\n}\n","import type { ScanRegion, Point } from './types.js';\n\n/**\n * Compute the actual rendered position and size of the video content\n * within the element, accounting for object-fit: cover.\n *\n * With object-fit: cover the video is scaled up to fill the element and\n * cropped. The rendered content is larger than the element, centered,\n * with negative offsets for the cropped portions.\n *\n * When object-fit is the default (fill), the rendered size equals the\n * element size and offsets are zero — so this is backwards-compatible.\n */\nfunction getRenderedVideoRect(video: HTMLVideoElement): {\n offsetX: number;\n offsetY: number;\n width: number;\n height: number;\n} {\n const elementWidth = video.clientWidth;\n const elementHeight = video.clientHeight;\n const videoWidth = video.videoWidth || 1;\n const videoHeight = video.videoHeight || 1;\n\n const objectFit = getComputedStyle(video).objectFit;\n\n if (objectFit === 'cover') {\n const scale = Math.max(\n elementWidth / videoWidth,\n elementHeight / videoHeight,\n );\n const renderedWidth = videoWidth * scale;\n const renderedHeight = videoHeight * scale;\n return {\n offsetX: (elementWidth - renderedWidth) / 2,\n offsetY: (elementHeight - renderedHeight) / 2,\n width: renderedWidth,\n height: renderedHeight,\n };\n }\n\n if (objectFit === 'contain') {\n const scale = Math.min(\n elementWidth / videoWidth,\n elementHeight / videoHeight,\n );\n const renderedWidth = videoWidth * scale;\n const renderedHeight = videoHeight * scale;\n return {\n offsetX: (elementWidth - renderedWidth) / 2,\n offsetY: (elementHeight - renderedHeight) / 2,\n width: renderedWidth,\n height: renderedHeight,\n };\n }\n\n // Default (fill / none / scale-down with no scaling needed): element dimensions\n return { offsetX: 0, offsetY: 0, width: elementWidth, height: elementHeight };\n}\n\nexport interface OverlayConfig {\n highlightScanRegion: boolean;\n highlightCodeOutline: boolean;\n customOverlay?: HTMLDivElement;\n}\n\nexport class ScanOverlay {\n private container: HTMLElement;\n private overlayEl: HTMLDivElement | null = null;\n private codeOutlineEl: SVGElement | null = null;\n private config: OverlayConfig;\n private video: HTMLVideoElement;\n\n constructor(video: HTMLVideoElement, config: OverlayConfig) {\n this.video = video;\n this.config = config;\n\n const parent = video.parentElement;\n if (!parent) {\n throw new Error(\n 'QrScanner: video element must have a parent element. ' +\n 'The parent should have position: relative.',\n );\n }\n this.container = parent;\n }\n\n setup(): void {\n if (this.config.customOverlay) {\n this.overlayEl = this.config.customOverlay;\n this.positionOverlay();\n return;\n }\n\n if (this.config.highlightScanRegion) {\n this.createScanRegionOverlay();\n }\n\n if (this.config.highlightCodeOutline) {\n this.createCodeOutline();\n }\n }\n\n updateScanRegion(region: ScanRegion): void {\n if (!this.overlayEl || this.config.customOverlay) return;\n this.positionOverlayToRegion(region);\n }\n\n updateCodeOutline(\n cornerPoints: Point[] | null,\n scanRegion?: ScanRegion,\n ): void {\n if (!this.codeOutlineEl) return;\n\n if (!cornerPoints || cornerPoints.length < 4) {\n this.codeOutlineEl.style.display = 'none';\n return;\n }\n\n this.codeOutlineEl.style.display = 'block';\n\n const polygon = this.codeOutlineEl.querySelector('polygon');\n if (!polygon) return;\n\n // Corner points are relative to the cropped scan region.\n // Add the scan region offset to get full video coordinates,\n // then scale to display coordinates (accounting for object-fit).\n const regionX = scanRegion?.x ?? 0;\n const regionY = scanRegion?.y ?? 0;\n const rendered = getRenderedVideoRect(this.video);\n const scaleX = rendered.width / this.video.videoWidth;\n const scaleY = rendered.height / this.video.videoHeight;\n\n const points = cornerPoints\n .map(\n (p) =>\n `${(p.x + regionX) * scaleX + rendered.offsetX},${(p.y + regionY) * scaleY + rendered.offsetY}`,\n )\n .join(' ');\n\n polygon.setAttribute('points', points);\n }\n\n destroy(): void {\n if (this.overlayEl && !this.config.customOverlay) {\n this.overlayEl.remove();\n }\n if (this.codeOutlineEl) {\n this.codeOutlineEl.remove();\n }\n this.overlayEl = null;\n this.codeOutlineEl = null;\n }\n\n private createScanRegionOverlay(): void {\n this.overlayEl = document.createElement('div');\n this.overlayEl.className = 'qr-scanner-region';\n\n // Overlay size: 3/4 of the smaller container dimension, centered.\n const cw = this.container.clientWidth;\n const ch = this.container.clientHeight;\n const size = Math.round((Math.min(cw, ch) * 3) / 4);\n\n Object.assign(this.overlayEl.style, {\n position: 'absolute',\n top: '50%',\n left: '50%',\n transform: 'translate(-50%, -50%)',\n width: `${size}px`,\n height: `${size}px`,\n border: '2px solid rgba(255, 255, 255, 0.5)',\n borderRadius: '8px',\n boxShadow: '0 0 0 9999px rgba(0, 0, 0, 0.5)',\n pointerEvents: 'none',\n zIndex: '10',\n });\n\n // Corner markers\n const corners = ['top-left', 'top-right', 'bottom-left', 'bottom-right'];\n for (const corner of corners) {\n const marker = document.createElement('div');\n marker.className = `qr-scanner-corner qr-scanner-corner-${corner}`;\n\n const [vertical, horizontal] = corner.split('-');\n\n Object.assign(marker.style, {\n position: 'absolute',\n width: '24px',\n height: '24px',\n [vertical]: '-2px',\n [horizontal]: '-2px',\n [`border-${vertical}`]: '3px solid white',\n [`border-${horizontal}`]: '3px solid white',\n [`border-${vertical}-${horizontal}-radius`]: '8px',\n });\n\n this.overlayEl.appendChild(marker);\n }\n\n this.container.appendChild(this.overlayEl);\n }\n\n private createCodeOutline(): void {\n const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');\n svg.setAttribute('class', 'qr-scanner-code-outline');\n\n Object.assign(svg.style, {\n position: 'absolute',\n top: '0',\n left: '0',\n width: '100%',\n height: '100%',\n pointerEvents: 'none',\n zIndex: '11',\n display: 'none',\n });\n\n const polygon = document.createElementNS(\n 'http://www.w3.org/2000/svg',\n 'polygon',\n );\n polygon.setAttribute('fill', 'none');\n polygon.setAttribute('stroke', '#00ff00');\n polygon.setAttribute('stroke-width', '3');\n polygon.setAttribute('stroke-linejoin', 'round');\n\n // Animated stroke\n const animate = document.createElementNS(\n 'http://www.w3.org/2000/svg',\n 'animate',\n );\n animate.setAttribute('attributeName', 'stroke-opacity');\n animate.setAttribute('values', '1;0.5;1');\n animate.setAttribute('dur', '1.5s');\n animate.setAttribute('repeatCount', 'indefinite');\n polygon.appendChild(animate);\n\n svg.appendChild(polygon);\n this.container.appendChild(svg);\n this.codeOutlineEl = svg;\n }\n\n private positionOverlay(): void {\n if (!this.overlayEl) return;\n\n Object.assign(this.overlayEl.style, {\n position: 'absolute',\n top: '0',\n left: '0',\n width: '100%',\n height: '100%',\n pointerEvents: 'none',\n zIndex: '10',\n });\n }\n\n private positionOverlayToRegion(_region: ScanRegion): void {\n if (!this.overlayEl) return;\n\n // Keep the overlay at a container-relative centered size rather than\n // mapping scan region coordinates to display coordinates. With\n // object-fit: cover on a portrait phone with a landscape camera, the\n // mapped region extends beyond the container — fine once the camera is\n // visible but causes a jarring jump from the initial placeholder.\n // The overlay is a visual guide; the actual scan area (in the frame\n // extractor) is unaffected and may be larger than what's shown.\n const cw = this.container.clientWidth;\n const ch = this.container.clientHeight;\n const size = Math.round((Math.min(cw, ch) * 3) / 4);\n\n // Only update size — initial CSS centering (top: 50%, left: 50%,\n // transform: translate(-50%, -50%)) persists and handles re-centering.\n Object.assign(this.overlayEl.style, {\n width: `${size}px`,\n height: `${size}px`,\n });\n }\n}\n","import type { ScanRegion } from './types.js';\n\n/**\n * Calculate the default scan region: a centered square covering\n * 2/3 of the smaller video dimension.\n */\nexport function calculateDefaultScanRegion(video: HTMLVideoElement): ScanRegion {\n const videoWidth = video.videoWidth || video.width;\n const videoHeight = video.videoHeight || video.height;\n\n const smallerDimension = Math.min(videoWidth, videoHeight);\n const size = Math.round((smallerDimension * 2) / 3);\n\n return {\n x: Math.round((videoWidth - size) / 2),\n y: Math.round((videoHeight - size) / 2),\n width: size,\n height: size,\n };\n}\n","import { CameraManager } from './camera.js';\nimport { FrameExtractor } from './frame-extractor.js';\nimport { ScanOverlay } from './overlay.js';\nimport { calculateDefaultScanRegion } from './scan-region.js';\nimport type {\n ScannerOptions,\n ScanResult,\n ScanRegion,\n InversionMode,\n WorkerRequest,\n WorkerResponse,\n} from './types.js';\nimport type { ReaderOptions } from 'zxing-wasm/reader';\n\ntype OnDecodeCallback = (result: ScanResult) => void;\n\n/**\n * Custom worker URL override. When set, this URL is used instead of the\n * default bundler-resolved worker. Useful for CJS consumers or non-standard\n * bundler setups.\n */\nlet customWorkerUrl: string | URL | null = null;\n\nexport function setWorkerUrl(url: string | URL): void {\n customWorkerUrl = url;\n}\n\nfunction resolveWorkerUrl(): string | URL {\n if (customWorkerUrl) {\n return customWorkerUrl;\n }\n // Standard pattern: modern bundlers (Vite, webpack 5, Parcel, esbuild)\n // resolve `new URL('./file', import.meta.url)` at build time,\n // copying worker.js to the output directory and returning the correct URL.\n // Falls back gracefully for CJS builds where import.meta is unavailable.\n try {\n return new URL('./worker.js', import.meta.url);\n } catch {\n throw new Error(\n '@agicash/qr-scanner: Could not resolve worker URL. ' +\n 'Call QrScanner.setWorkerUrl() with the path to the worker script before creating a scanner. ' +\n 'Example: QrScanner.setWorkerUrl(\"/path/to/@agicash/qr-scanner/dist/worker.js\")',\n );\n }\n}\n\nexport class Scanner {\n private video: HTMLVideoElement;\n private onDecode: OnDecodeCallback;\n private options: ScannerOptions;\n private camera: CameraManager;\n private frameExtractor: FrameExtractor | null = null;\n private worker: Worker | null = null;\n private overlay: ScanOverlay | null = null;\n private active = false;\n private paused = false;\n private destroyed = false;\n\n constructor(\n video: HTMLVideoElement,\n onDecode: OnDecodeCallback,\n options: ScannerOptions = {},\n ) {\n this.video = video;\n this.onDecode = onDecode;\n this.options = options;\n\n this.camera = new CameraManager({\n preferredCamera: options.preferredCamera,\n cameraResolution: options.cameraResolution,\n });\n }\n\n async start(): Promise<void> {\n if (this.destroyed) {\n throw new Error('Scanner has been destroyed');\n }\n\n if (this.active && !this.paused) {\n return; // Already running\n }\n\n const t0 = performance.now();\n\n // Show overlay immediately (CSS-centered placeholder) so the UI looks\n // ready while the camera is still loading.\n if (\n !this.overlay &&\n (this.options.highlightScanRegion ||\n this.options.highlightCodeOutline ||\n this.options.overlay)\n ) {\n try {\n this.overlay = new ScanOverlay(this.video, {\n highlightScanRegion: this.options.highlightScanRegion ?? false,\n highlightCodeOutline: this.options.highlightCodeOutline ?? false,\n customOverlay: this.options.overlay,\n });\n this.overlay.setup();\n } catch {\n // Overlay setup failed (e.g., no parent element) — continue without overlay\n }\n }\n\n // Start camera\n await this.camera.start(this.video);\n console.debug(\n `[QrScanner] start: camera ready ${(performance.now() - t0).toFixed(0)}ms`,\n );\n\n // Now that video dimensions are known, position overlay exactly\n if (this.overlay) {\n this.overlay.updateScanRegion(this.getCurrentScanRegion());\n }\n\n // Create worker if needed\n if (!this.worker) {\n const tw = performance.now();\n this.worker = this.createWorker();\n console.debug(\n `[QrScanner] start: worker created ${(performance.now() - tw).toFixed(0)}ms`,\n );\n }\n\n // Create frame extractor if needed\n if (!this.frameExtractor) {\n this.frameExtractor = new FrameExtractor(this.video, {\n maxScansPerSecond: this.options.maxScansPerSecond ?? 15,\n getScanRegion: () => this.getCurrentScanRegion(),\n });\n }\n\n // Start frame extraction loop\n this.frameExtractor.start((imageData) => {\n this.sendToWorker(imageData);\n });\n\n this.active = true;\n this.paused = false;\n\n console.debug(\n `[QrScanner] start: total ${(performance.now() - t0).toFixed(0)}ms`,\n );\n }\n\n stop(): void {\n this.frameExtractor?.stop();\n this.camera.stop();\n this.video.srcObject = null;\n this.active = false;\n this.paused = false;\n }\n\n destroy(): void {\n if (this.destroyed) return;\n this.stop();\n this.frameExtractor?.destroy();\n this.frameExtractor = null;\n this.overlay?.destroy();\n this.overlay = null;\n this.worker?.terminate();\n this.worker = null;\n this.destroyed = true;\n }\n\n async pause(stopStreamImmediately = true): Promise<boolean> {\n if (!this.active) return false;\n\n this.frameExtractor?.stop();\n this.paused = true;\n\n if (stopStreamImmediately) {\n this.camera.stop();\n this.video.srcObject = null;\n }\n\n return true;\n }\n\n async setCamera(facingModeOrDeviceId: string): Promise<void> {\n const wasActive = this.active && !this.paused;\n if (wasActive) {\n this.frameExtractor?.stop();\n }\n\n await this.camera.setCamera(facingModeOrDeviceId, this.video);\n\n if (wasActive) {\n this.frameExtractor?.start((imageData) => {\n this.sendToWorker(imageData);\n });\n }\n }\n\n async hasFlash(): Promise<boolean> {\n return this.camera.hasFlash();\n }\n\n isFlashOn(): boolean {\n return this.camera.isFlashOn();\n }\n\n async toggleFlash(): Promise<void> {\n return this.camera.toggleFlash();\n }\n\n async turnFlashOn(): Promise<void> {\n return this.camera.turnFlashOn();\n }\n\n async turnFlashOff(): Promise<void> {\n return this.camera.turnFlashOff();\n }\n\n setInversionMode(mode: InversionMode): void {\n if (!this.worker) return;\n\n const options: Partial<ReaderOptions> = {};\n switch (mode) {\n case 'original':\n options.tryInvert = false;\n break;\n case 'invert':\n options.tryInvert = true;\n break;\n case 'both':\n options.tryInvert = true;\n break;\n }\n\n const msg: WorkerRequest = { type: 'configure', options };\n this.worker.postMessage(msg);\n }\n\n isActive(): boolean {\n return this.active;\n }\n\n isPaused(): boolean {\n return this.paused;\n }\n\n isDestroyed(): boolean {\n return this.destroyed;\n }\n\n private getCurrentScanRegion(): ScanRegion {\n if (this.options.calculateScanRegion) {\n return this.options.calculateScanRegion(this.video);\n }\n return calculateDefaultScanRegion(this.video);\n }\n\n private createWorker(): Worker {\n const workerUrl = resolveWorkerUrl();\n const worker = new Worker(workerUrl, { type: 'module' });\n\n // Configure with custom decoder options\n if (this.options.decoderOptions) {\n const msg: WorkerRequest = {\n type: 'configure',\n options: this.options.decoderOptions,\n };\n worker.postMessage(msg);\n }\n\n worker.onmessage = (e: MessageEvent<WorkerResponse>) => {\n this.handleWorkerMessage(e.data);\n };\n\n worker.onerror = (err) => {\n console.error('QR Scanner worker error:', err);\n this.frameExtractor?.markWorkerIdle();\n };\n\n return worker;\n }\n\n private handleWorkerMessage(response: WorkerResponse): void {\n this.frameExtractor?.markWorkerIdle();\n\n if (response.type === 'ready') {\n return;\n }\n\n if (response.type === 'error') {\n this.options.onDecodeError?.(response.message);\n return;\n }\n\n if (response.type === 'result') {\n if (response.results.length > 0) {\n const result = response.results[0];\n this.onDecode(result);\n\n // Update overlay\n if (this.overlay) {\n const region = this.getCurrentScanRegion();\n this.overlay.updateScanRegion(region);\n this.overlay.updateCodeOutline(result.cornerPoints, region);\n }\n } else {\n this.options.onDecodeError?.('No QR code found');\n\n // Hide code outline when no QR found\n if (this.overlay) {\n this.overlay.updateCodeOutline(null);\n }\n }\n }\n }\n\n private sendToWorker(imageData: ImageData): void {\n if (!this.worker) return;\n\n const msg: WorkerRequest = { type: 'decode', imageData };\n this.worker.postMessage(msg, [imageData.data.buffer]);\n }\n}\n","import { readBarcodes, type ReaderOptions } from 'zxing-wasm/reader';\nimport type { ScanResult, ScanRegion, Point } from './types.js';\nimport { loadImageData } from './utils.js';\n\nconst defaultReaderOptions: ReaderOptions = {\n formats: ['QRCode'],\n tryHarder: true,\n tryInvert: true,\n tryRotate: true,\n tryDenoise: false,\n tryDownscale: true,\n maxNumberOfSymbols: 1,\n};\n\nfunction mapPosition(position: {\n topLeft: Point;\n topRight: Point;\n bottomLeft: Point;\n bottomRight: Point;\n}): Point[] {\n return [\n position.topLeft,\n position.topRight,\n position.bottomRight,\n position.bottomLeft,\n ];\n}\n\n/** Input types that zxing-wasm can handle directly (no canvas needed). */\ntype DirectInput = Blob | ArrayBuffer | Uint8Array | ImageData;\n\n/** Input types that need canvas-based pixel extraction. */\ntype CanvasInput = HTMLImageElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap;\n\nfunction isDirectInput(source: unknown): source is DirectInput {\n return (\n source instanceof Blob ||\n source instanceof ArrayBuffer ||\n source instanceof Uint8Array ||\n (typeof ImageData !== 'undefined' && source instanceof ImageData)\n );\n}\n\n/**\n * Scan a single image for QR codes. Does not require a camera or video stream.\n */\nexport async function scanImage(\n source:\n | CanvasInput\n | DirectInput\n | File\n | URL\n | string,\n options?: {\n scanRegion?: ScanRegion | null;\n canvas?: HTMLCanvasElement | null;\n decoderOptions?: Partial<ReaderOptions>;\n },\n): Promise<ScanResult> {\n const readerOptions: ReaderOptions = {\n ...defaultReaderOptions,\n ...options?.decoderOptions,\n formats: ['QRCode'],\n };\n\n let input: DirectInput;\n if (isDirectInput(source)) {\n input = source;\n } else if (typeof source === 'string' || source instanceof URL) {\n // URL string - fetch and pass as ArrayBuffer\n const url = source instanceof URL ? source.href : source;\n const response = await fetch(url);\n input = await response.arrayBuffer();\n } else {\n // Canvas-based sources - extract ImageData\n input = await loadImageData(source, options?.scanRegion, options?.canvas);\n }\n\n const results = await readBarcodes(input, readerOptions);\n const valid = results.filter((r) => r.isValid);\n\n if (valid.length === 0) {\n throw new Error('No QR code found in the image');\n }\n\n const first = valid[0];\n return {\n data: first.text,\n cornerPoints: mapPosition(first.position),\n };\n}\n","import type { ScanRegion } from './types.js';\n\n/**\n * Load an image source into an ImageData object, optionally cropping to a scan region.\n */\nexport async function loadImageData(\n source: HTMLImageElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap | File | Blob | URL | string,\n scanRegion?: ScanRegion | null,\n canvas?: HTMLCanvasElement | null,\n): Promise<ImageData> {\n const img = await resolveImageSource(source);\n\n const sx = scanRegion?.x ?? 0;\n const sy = scanRegion?.y ?? 0;\n const sw = scanRegion?.width ?? (img.width - sx);\n const sh = scanRegion?.height ?? (img.height - sy);\n\n let drawCanvas: HTMLCanvasElement | OffscreenCanvas;\n let ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D;\n\n if (canvas) {\n drawCanvas = canvas;\n canvas.width = sw;\n canvas.height = sh;\n ctx = canvas.getContext('2d')!;\n } else if (typeof OffscreenCanvas !== 'undefined') {\n drawCanvas = new OffscreenCanvas(sw, sh);\n ctx = drawCanvas.getContext('2d')! as OffscreenCanvasRenderingContext2D;\n } else {\n drawCanvas = document.createElement('canvas');\n drawCanvas.width = sw;\n drawCanvas.height = sh;\n ctx = drawCanvas.getContext('2d')!;\n }\n\n ctx.drawImage(img as CanvasImageSource, sx, sy, sw, sh, 0, 0, sw, sh);\n return ctx.getImageData(0, 0, sw, sh);\n}\n\nasync function resolveImageSource(\n source: HTMLImageElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap | File | Blob | URL | string,\n): Promise<HTMLImageElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap> {\n if (source instanceof HTMLImageElement || source instanceof HTMLCanvasElement || source instanceof ImageBitmap) {\n return source;\n }\n\n if (typeof OffscreenCanvas !== 'undefined' && source instanceof OffscreenCanvas) {\n return source;\n }\n\n if (source instanceof File || source instanceof Blob) {\n return createImageBitmapFromBlob(source);\n }\n\n // URL or string\n const url = source instanceof URL ? source.href : source as string;\n const response = await fetch(url);\n const blob = await response.blob();\n return createImageBitmapFromBlob(blob);\n}\n\nasync function createImageBitmapFromBlob(blob: Blob): Promise<ImageBitmap> {\n if (typeof createImageBitmap !== 'undefined') {\n return createImageBitmap(blob);\n }\n // Fallback for environments without createImageBitmap\n const url = URL.createObjectURL(blob);\n try {\n const img = new Image();\n img.src = url;\n await new Promise<void>((resolve, reject) => {\n img.onload = () => resolve();\n img.onerror = () => reject(new Error('Failed to load image'));\n });\n return img as unknown as ImageBitmap;\n } finally {\n URL.revokeObjectURL(url);\n }\n}\n"],"mappings":"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","names":["t","import_reader"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/debug.ts","../src/camera.ts","../src/frame-extractor.ts","../src/overlay.ts","../src/scan-region.ts","../src/scanner.ts","../src/scan-image.ts","../src/utils.ts"],"sourcesContent":["import { Scanner, setWorkerUrl } from './scanner.js';\nimport {\n CameraManager,\n CameraNotFoundError,\n CameraPermissionError,\n} from './camera.js';\nimport { setDebug } from './debug.js';\nimport { scanImage } from './scan-image.js';\nimport { setZXingModuleOverrides } from 'zxing-wasm/reader';\nimport type {\n ScanResult,\n ScanRegion,\n ScannerOptions,\n Camera,\n Point,\n FacingMode,\n DeviceId,\n InversionMode,\n} from './types.js';\nimport type { ReaderOptions } from 'zxing-wasm/reader';\n\nexport { CameraNotFoundError, CameraPermissionError };\n\nexport type {\n ScanResult,\n ScanRegion,\n ScannerOptions,\n Camera,\n Point,\n FacingMode,\n DeviceId,\n InversionMode,\n};\n\n/**\n * High-performance QR code scanner for the web, powered by ZXing-C++ WebAssembly.\n */\nclass QrScanner {\n private scanner: Scanner;\n\n constructor(\n videoElement: HTMLVideoElement,\n onDecode: (result: ScanResult) => void,\n options: ScannerOptions = {},\n ) {\n this.scanner = new Scanner(videoElement, onDecode, options);\n }\n\n /** Start camera and begin scanning. Resolves when camera is ready. */\n async start(): Promise<void> {\n return this.scanner.start();\n }\n\n /** Stop scanning and release the camera stream. */\n stop(): void {\n this.scanner.stop();\n }\n\n /** Stop scanning, release camera, terminate worker, clean up DOM. */\n destroy(): void {\n this.scanner.destroy();\n }\n\n /** Pause scanning. If stopStreamImmediately is false, camera stays on. */\n async pause(stopStreamImmediately?: boolean): Promise<boolean> {\n return this.scanner.pause(stopStreamImmediately);\n }\n\n /** Switch to a different camera by facing mode or device ID. */\n async setCamera(facingModeOrDeviceId: FacingMode | DeviceId): Promise<void> {\n return this.scanner.setCamera(facingModeOrDeviceId);\n }\n\n /** Check if the current camera supports flash/torch. */\n async hasFlash(): Promise<boolean> {\n return this.scanner.hasFlash();\n }\n\n /** Whether flash is currently on. */\n isFlashOn(): boolean {\n return this.scanner.isFlashOn();\n }\n\n /** Toggle flash on/off. */\n async toggleFlash(): Promise<void> {\n return this.scanner.toggleFlash();\n }\n\n /** Turn flash on. */\n async turnFlashOn(): Promise<void> {\n return this.scanner.turnFlashOn();\n }\n\n /** Turn flash off. */\n async turnFlashOff(): Promise<void> {\n return this.scanner.turnFlashOff();\n }\n\n /** Set the inversion mode for detecting inverted QR codes. */\n setInversionMode(mode: InversionMode): void {\n this.scanner.setInversionMode(mode);\n }\n\n // --- Static methods ---\n\n /** Check if the device has at least one camera. */\n static hasCamera(): Promise<boolean> {\n return CameraManager.hasCamera();\n }\n\n /** List available cameras. Pass true to request labels (triggers permission prompt). */\n static listCameras(requestLabels?: boolean): Promise<Camera[]> {\n return CameraManager.listCameras(requestLabels);\n }\n\n /**\n * Pre-load the WASM binary so it's ready when the scanner starts.\n * Call this early (e.g., on app init) to avoid delay on first scan.\n */\n static async preload(): Promise<void> {\n // Trigger WASM loading by doing a minimal scan\n const pixel = new Uint8ClampedArray([255, 255, 255, 255]);\n const img = new ImageData(pixel, 1, 1);\n try {\n await scanImage(img);\n } catch {\n // Expected — no QR code in a 1x1 image. The point was to load WASM.\n }\n }\n\n /**\n * Configure WASM loading. Call before creating any scanner instance.\n * @example\n * QrScanner.configureWasm({ locateFile: (filename) => `/wasm/${filename}` });\n */\n static configureWasm(overrides: Partial<EmscriptenModule>): void {\n setZXingModuleOverrides(overrides);\n }\n\n /**\n * Set a custom URL for the worker script. Call before creating any scanner.\n * Needed for CJS consumers or non-standard bundler setups.\n * By default, the worker URL is resolved via `new URL('./worker.js', import.meta.url)`,\n * which works with Vite, webpack 5, Parcel, and other modern bundlers.\n * @example\n * QrScanner.setWorkerUrl('/assets/qr-scanner-worker.js');\n */\n static setWorkerUrl(url: string | URL): void {\n setWorkerUrl(url);\n }\n\n /**\n * Enable or disable debug logging (performance timings, camera selection).\n * Off by default. Useful for diagnosing camera issues in the browser console.\n * @example\n * QrScanner.setDebug(true);\n */\n static setDebug(enabled: boolean): void {\n setDebug(enabled);\n }\n\n /** Scan a single image (not a video stream). */\n static scanImage(\n source:\n | HTMLImageElement\n | HTMLCanvasElement\n | OffscreenCanvas\n | ImageBitmap\n | ImageData\n | Blob\n | ArrayBuffer\n | Uint8Array\n | File\n | URL\n | string,\n options?: {\n scanRegion?: ScanRegion | null;\n canvas?: HTMLCanvasElement | null;\n decoderOptions?: Partial<ReaderOptions>;\n },\n ): Promise<ScanResult> {\n return scanImage(source, options);\n }\n}\n\nexport default QrScanner;\n","let enabled = false;\n\nexport function setDebug(on: boolean): void {\n enabled = on;\n}\n\nexport function debug(...args: unknown[]): void {\n if (enabled) console.debug(...args);\n}\n","import { debug } from './debug.js';\nimport type { FacingMode, DeviceId, Camera } from './types.js';\n\nexport class CameraPermissionError extends Error {\n constructor(\n message = 'Camera access denied. Please grant camera permission and try again.',\n ) {\n super(message);\n this.name = 'CameraPermissionError';\n }\n}\n\nexport class CameraNotFoundError extends Error {\n constructor(\n message = 'No camera found. Please connect a camera and try again.',\n ) {\n super(message);\n this.name = 'CameraNotFoundError';\n }\n}\n\nconst CACHE_KEY_PREFIX = '@agicash/qr-scanner:camera:';\n\nfunction getCachedDeviceId(facingMode: string): string | null {\n try {\n return localStorage.getItem(`${CACHE_KEY_PREFIX}${facingMode}`);\n } catch {\n return null;\n }\n}\n\nfunction setCachedDeviceId(facingMode: string, deviceId: string): void {\n try {\n localStorage.setItem(`${CACHE_KEY_PREFIX}${facingMode}`, deviceId);\n } catch {\n // localStorage unavailable or full — ignore\n }\n}\n\nexport interface CameraConfig {\n preferredCamera?: FacingMode | DeviceId;\n cameraResolution?: {\n width?: MediaTrackConstraintSet['width'];\n height?: MediaTrackConstraintSet['height'];\n };\n}\n\nexport class CameraManager {\n private stream: MediaStream | null = null;\n private facingMode: FacingMode | DeviceId;\n private resolution: CameraConfig['cameraResolution'];\n\n constructor(config: CameraConfig = {}) {\n this.facingMode = config.preferredCamera ?? 'environment';\n this.resolution = config.cameraResolution;\n }\n\n async start(video: HTMLVideoElement): Promise<MediaStream> {\n if (this.stream) {\n return this.stream;\n }\n\n const t0 = performance.now();\n\n this.stream = await this.acquireStream();\n const t1 = performance.now();\n debug(`[QrScanner] acquireStream: ${(t1 - t0).toFixed(0)}ms`);\n\n // On some devices (e.g. Samsung S24 + Brave), facingMode: 'environment'\n // picks an ultrawide camera that lacks autofocus. Check and switch to a\n // better camera BEFORE showing on screen to avoid visible flicker.\n await this.ensureBestCamera();\n const t2 = performance.now();\n debug(`[QrScanner] ensureBestCamera: ${(t2 - t1).toFixed(0)}ms`);\n\n video.srcObject = this.stream;\n video.setAttribute('playsinline', 'true');\n await video.play();\n const t3 = performance.now();\n debug(`[QrScanner] video.play: ${(t3 - t2).toFixed(0)}ms`);\n debug(`[QrScanner] camera.start total: ${(t3 - t0).toFixed(0)}ms`);\n\n // Cache the final camera so subsequent starts skip ensureBestCamera\n if (this.facingMode === 'environment' || this.facingMode === 'user') {\n const finalDeviceId = this.getVideoTrack()?.getSettings().deviceId;\n if (finalDeviceId) {\n setCachedDeviceId(this.facingMode, finalDeviceId);\n }\n }\n\n return this.stream;\n }\n\n stop(): void {\n if (this.stream) {\n for (const track of this.stream.getTracks()) {\n track.stop();\n }\n this.stream = null;\n }\n }\n\n async setCamera(\n facingModeOrDeviceId: FacingMode | DeviceId,\n video: HTMLVideoElement,\n ): Promise<void> {\n this.stop();\n this.facingMode = facingModeOrDeviceId;\n await this.start(video);\n }\n\n getStream(): MediaStream | null {\n return this.stream;\n }\n\n async hasFlash(): Promise<boolean> {\n const track = this.getVideoTrack();\n if (!track) return false;\n\n try {\n const capabilities = track.getCapabilities() as MediaTrackCapabilities & {\n torch?: boolean;\n };\n return capabilities.torch === true;\n } catch {\n return false;\n }\n }\n\n isFlashOn(): boolean {\n const track = this.getVideoTrack();\n if (!track) return false;\n\n const settings = track.getSettings() as MediaTrackSettings & {\n torch?: boolean;\n };\n return settings.torch === true;\n }\n\n async toggleFlash(): Promise<void> {\n if (this.isFlashOn()) {\n await this.turnFlashOff();\n } else {\n await this.turnFlashOn();\n }\n }\n\n async turnFlashOn(): Promise<void> {\n await this.setTorch(true);\n }\n\n async turnFlashOff(): Promise<void> {\n await this.setTorch(false);\n }\n\n private async setTorch(on: boolean): Promise<void> {\n const track = this.getVideoTrack();\n if (!track) {\n throw new Error('No active camera stream');\n }\n\n try {\n await track.applyConstraints({\n advanced: [{ torch: on } as MediaTrackConstraintSet],\n });\n } catch {\n throw new Error('Flash/torch is not supported on this device');\n }\n }\n\n /**\n * If the current camera lacks continuous autofocus (e.g. an ultrawide sensor\n * picked by facingMode: 'environment'), find a better camera with the same\n * facing mode and replace this.stream. Called before assigning to the video\n * element so the user never sees the wrong camera.\n */\n private async ensureBestCamera(): Promise<void> {\n if (this.facingMode !== 'environment' && this.facingMode !== 'user') {\n debug('[QrScanner] ensureBestCamera: skipped (specific deviceId)');\n return;\n }\n\n const track = this.getVideoTrack();\n if (!track) return;\n\n try {\n const capabilities = track.getCapabilities() as MediaTrackCapabilities & {\n focusMode?: string[];\n };\n if (\n !capabilities.focusMode ||\n capabilities.focusMode.includes('continuous')\n ) {\n // focusMode not reported (e.g. Safari/iOS) or has autofocus — skip.\n // Only enter the candidate loop when the browser explicitly reports\n // focusMode without 'continuous' (e.g. S24 + Brave ultrawide).\n debug(\n `[QrScanner] ensureBestCamera: skipped (focusMode: ${JSON.stringify(capabilities.focusMode)})`,\n );\n return;\n }\n debug(\n `[QrScanner] ensureBestCamera: current camera lacks autofocus (focusMode: ${JSON.stringify(capabilities.focusMode)})`,\n );\n } catch {\n return;\n }\n\n // Current camera lacks continuous autofocus.\n // Enumerate devices while stream is active (ensures deviceIds are available).\n const currentDeviceId = track.getSettings().deviceId;\n\n let devices: MediaDeviceInfo[];\n try {\n devices = await navigator.mediaDevices.enumerateDevices();\n } catch {\n return;\n }\n if (!Array.isArray(devices)) return;\n\n const candidates = devices.filter(\n (d) => d.kind === 'videoinput' && d.deviceId !== currentDeviceId,\n );\n debug(\n `[QrScanner] ensureBestCamera: testing ${candidates.length} candidate camera(s)`,\n );\n if (candidates.length === 0) return;\n\n // Stop current stream — mobile devices only allow one active camera\n this.stop();\n\n for (const candidate of candidates) {\n const t = performance.now();\n let candidateStream: MediaStream;\n try {\n candidateStream = await navigator.mediaDevices.getUserMedia({\n video: {\n deviceId: { exact: candidate.deviceId },\n width: this.resolution?.width ?? { ideal: 1920 },\n height: this.resolution?.height ?? { ideal: 1080 },\n },\n audio: false,\n });\n debug(\n `[QrScanner] ensureBestCamera: candidate ${candidate.label || candidate.deviceId.slice(0, 8)}: getUserMedia ${(performance.now() - t).toFixed(0)}ms`,\n );\n } catch {\n debug(\n `[QrScanner] ensureBestCamera: candidate ${candidate.label || candidate.deviceId.slice(0, 8)}: getUserMedia failed ${(performance.now() - t).toFixed(0)}ms`,\n );\n continue;\n }\n\n const candidateTrack = candidateStream.getVideoTracks()[0];\n if (!candidateTrack) {\n for (const t of candidateStream.getTracks()) t.stop();\n continue;\n }\n\n // Must match the desired facing mode\n const candidateSettings =\n candidateTrack.getSettings() as MediaTrackSettings & {\n facingMode?: string;\n };\n if (\n candidateSettings.facingMode &&\n candidateSettings.facingMode !== this.facingMode\n ) {\n for (const t of candidateStream.getTracks()) t.stop();\n continue;\n }\n\n // Check if this camera supports continuous autofocus\n try {\n const candidateCaps =\n candidateTrack.getCapabilities() as MediaTrackCapabilities & {\n focusMode?: string[];\n };\n if (candidateCaps.focusMode?.includes('continuous')) {\n this.stream = candidateStream;\n return;\n }\n } catch {\n // Can't check capabilities, skip\n }\n\n for (const t of candidateStream.getTracks()) t.stop();\n }\n\n // No better camera found — re-open the original\n try {\n this.stream = await navigator.mediaDevices.getUserMedia({\n video: {\n deviceId: currentDeviceId ? { exact: currentDeviceId } : undefined,\n width: this.resolution?.width ?? { ideal: 1920 },\n height: this.resolution?.height ?? { ideal: 1080 },\n },\n audio: false,\n });\n } catch {\n try {\n this.stream = await navigator.mediaDevices.getUserMedia(\n this.buildConstraints(),\n );\n } catch {\n // Could not recover camera\n }\n }\n }\n\n private getVideoTrack(): MediaStreamTrack | null {\n if (!this.stream) return null;\n const tracks = this.stream.getVideoTracks();\n return tracks[0] ?? null;\n }\n\n /**\n * Try getUserMedia with progressively simpler constraints.\n *\n * Some browsers (e.g. Brave on Samsung Galaxy S24) throw NotReadableError\n * when facingMode and resolution constraints are combined. Falling back to\n * fewer constraints lets us still open the camera on those browsers.\n */\n private async acquireStream(): Promise<MediaStream> {\n const labels: string[] = [];\n const attempts: MediaStreamConstraints[] = [];\n\n // If we've previously found a good camera for this facingMode, try it first\n if (this.facingMode === 'environment' || this.facingMode === 'user') {\n const cachedId = getCachedDeviceId(this.facingMode);\n if (cachedId) {\n labels.push('cached deviceId');\n const video: MediaTrackConstraints = {\n deviceId: { exact: cachedId },\n };\n if (this.resolution?.width) video.width = this.resolution.width;\n else video.width = { ideal: 1920 };\n if (this.resolution?.height) video.height = this.resolution.height;\n else video.height = { ideal: 1080 };\n attempts.push({ video, audio: false });\n }\n }\n\n // Standard fallback chain\n labels.push('full constraints', 'no resolution', 'bare minimum');\n attempts.push(\n // facingMode/deviceId + resolution\n this.buildConstraints(),\n // facingMode/deviceId only, no resolution\n this.buildConstraints(false),\n // Bare minimum\n { video: true, audio: false },\n );\n\n let lastError: unknown;\n for (let i = 0; i < attempts.length; i++) {\n const t = performance.now();\n try {\n const stream = await navigator.mediaDevices.getUserMedia(attempts[i]);\n debug(\n `[QrScanner] getUserMedia(${labels[i]}): ${(performance.now() - t).toFixed(0)}ms ✓`,\n );\n return stream;\n } catch (err) {\n debug(\n `[QrScanner] getUserMedia(${labels[i]}): ${(performance.now() - t).toFixed(0)}ms ✗ ${err instanceof DOMException ? err.name : err}`,\n );\n if (err instanceof DOMException) {\n if (err.name === 'NotAllowedError') {\n throw new CameraPermissionError();\n }\n if (err.name === 'NotFoundError') {\n throw new CameraNotFoundError();\n }\n // NotReadableError or OverconstrainedError — try next fallback\n lastError = err;\n continue;\n }\n throw err;\n }\n }\n\n throw lastError;\n }\n\n private buildConstraints(includeResolution = true): MediaStreamConstraints {\n const video: MediaTrackConstraints = {};\n\n if (includeResolution) {\n video.width = this.resolution?.width ?? { ideal: 1920 };\n video.height = this.resolution?.height ?? { ideal: 1080 };\n }\n\n if (this.facingMode === 'environment' || this.facingMode === 'user') {\n video.facingMode = this.facingMode;\n } else {\n video.deviceId = { exact: this.facingMode };\n }\n\n return { video, audio: false };\n }\n\n static async hasCamera(): Promise<boolean> {\n try {\n const devices = await navigator.mediaDevices.enumerateDevices();\n return devices.some((d) => d.kind === 'videoinput');\n } catch {\n return false;\n }\n }\n\n static async listCameras(requestLabels = false): Promise<Camera[]> {\n if (requestLabels) {\n // Requesting labels requires a temporary stream to trigger the permission prompt\n try {\n const stream = await navigator.mediaDevices.getUserMedia({\n video: true,\n });\n for (const track of stream.getTracks()) {\n track.stop();\n }\n } catch {\n // Permission denied — fall through with empty labels\n }\n }\n\n const devices = await navigator.mediaDevices.enumerateDevices();\n return devices\n .filter((d) => d.kind === 'videoinput')\n .map((d) => ({\n id: d.deviceId,\n label: d.label || `Camera ${d.deviceId.slice(0, 8)}`,\n }));\n }\n}\n","import type { ScanRegion } from './types.js';\n\nexport interface FrameExtractorConfig {\n maxScansPerSecond: number;\n getScanRegion: () => ScanRegion;\n}\n\nexport class FrameExtractor {\n private video: HTMLVideoElement;\n private canvas: HTMLCanvasElement | OffscreenCanvas;\n private ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D;\n private rafId: number | null = null;\n private running = false;\n private workerBusy = false;\n private lastScanTime = -Infinity;\n private minInterval: number;\n private getScanRegion: () => ScanRegion;\n private onFrame: ((imageData: ImageData) => void) | null = null;\n\n constructor(video: HTMLVideoElement, config: FrameExtractorConfig) {\n this.video = video;\n this.minInterval = 1000 / config.maxScansPerSecond;\n this.getScanRegion = config.getScanRegion;\n\n if (typeof OffscreenCanvas !== 'undefined') {\n this.canvas = new OffscreenCanvas(1, 1);\n this.ctx = this.canvas.getContext('2d')! as OffscreenCanvasRenderingContext2D;\n } else {\n this.canvas = document.createElement('canvas');\n this.canvas.style.display = 'none';\n this.ctx = this.canvas.getContext('2d')!;\n }\n }\n\n start(onFrame: (imageData: ImageData) => void): void {\n if (this.running) return;\n this.running = true;\n this.onFrame = onFrame;\n this.rafId = requestAnimationFrame(this.tick);\n }\n\n stop(): void {\n this.running = false;\n this.onFrame = null;\n if (this.rafId !== null) {\n cancelAnimationFrame(this.rafId);\n this.rafId = null;\n }\n }\n\n destroy(): void {\n this.stop();\n if (this.canvas instanceof HTMLCanvasElement && this.canvas.parentNode) {\n this.canvas.parentNode.removeChild(this.canvas);\n }\n }\n\n markWorkerIdle(): void {\n this.workerBusy = false;\n }\n\n markWorkerBusy(): void {\n this.workerBusy = true;\n }\n\n private tick = (): void => {\n if (!this.running) return;\n\n this.rafId = requestAnimationFrame(this.tick);\n\n // Skip if worker is still processing previous frame\n if (this.workerBusy) return;\n\n // Rate limiting\n const now = performance.now();\n if (now - this.lastScanTime < this.minInterval) return;\n\n // Skip if video isn't ready\n if (this.video.readyState < 2) return;\n\n this.lastScanTime = now;\n\n const region = this.getScanRegion();\n const sx = region.x ?? 0;\n const sy = region.y ?? 0;\n const sw = region.width ?? this.video.videoWidth;\n const sh = region.height ?? this.video.videoHeight;\n\n if (sw <= 0 || sh <= 0) return;\n\n this.canvas.width = sw;\n this.canvas.height = sh;\n\n this.ctx.drawImage(this.video, sx, sy, sw, sh, 0, 0, sw, sh);\n const imageData = this.ctx.getImageData(0, 0, sw, sh);\n\n this.workerBusy = true;\n this.onFrame?.(imageData);\n };\n}\n","import type { ScanRegion, Point } from './types.js';\n\n/**\n * Compute the actual rendered position and size of the video content\n * within the element, accounting for object-fit: cover.\n *\n * With object-fit: cover the video is scaled up to fill the element and\n * cropped. The rendered content is larger than the element, centered,\n * with negative offsets for the cropped portions.\n *\n * When object-fit is the default (fill), the rendered size equals the\n * element size and offsets are zero — so this is backwards-compatible.\n */\nfunction getRenderedVideoRect(video: HTMLVideoElement): {\n offsetX: number;\n offsetY: number;\n width: number;\n height: number;\n} {\n const elementWidth = video.clientWidth;\n const elementHeight = video.clientHeight;\n const videoWidth = video.videoWidth || 1;\n const videoHeight = video.videoHeight || 1;\n\n const objectFit = getComputedStyle(video).objectFit;\n\n if (objectFit === 'cover') {\n const scale = Math.max(\n elementWidth / videoWidth,\n elementHeight / videoHeight,\n );\n const renderedWidth = videoWidth * scale;\n const renderedHeight = videoHeight * scale;\n return {\n offsetX: (elementWidth - renderedWidth) / 2,\n offsetY: (elementHeight - renderedHeight) / 2,\n width: renderedWidth,\n height: renderedHeight,\n };\n }\n\n if (objectFit === 'contain') {\n const scale = Math.min(\n elementWidth / videoWidth,\n elementHeight / videoHeight,\n );\n const renderedWidth = videoWidth * scale;\n const renderedHeight = videoHeight * scale;\n return {\n offsetX: (elementWidth - renderedWidth) / 2,\n offsetY: (elementHeight - renderedHeight) / 2,\n width: renderedWidth,\n height: renderedHeight,\n };\n }\n\n // Default (fill / none / scale-down with no scaling needed): element dimensions\n return { offsetX: 0, offsetY: 0, width: elementWidth, height: elementHeight };\n}\n\nexport interface OverlayConfig {\n highlightScanRegion: boolean;\n highlightCodeOutline: boolean;\n customOverlay?: HTMLDivElement;\n}\n\nexport class ScanOverlay {\n private container: HTMLElement;\n private overlayEl: HTMLDivElement | null = null;\n private codeOutlineEl: SVGElement | null = null;\n private config: OverlayConfig;\n private video: HTMLVideoElement;\n\n constructor(video: HTMLVideoElement, config: OverlayConfig) {\n this.video = video;\n this.config = config;\n\n const parent = video.parentElement;\n if (!parent) {\n throw new Error(\n 'QrScanner: video element must have a parent element. ' +\n 'The parent should have position: relative.',\n );\n }\n this.container = parent;\n }\n\n setup(): void {\n if (this.config.customOverlay) {\n this.overlayEl = this.config.customOverlay;\n this.positionOverlay();\n return;\n }\n\n if (this.config.highlightScanRegion) {\n this.createScanRegionOverlay();\n }\n\n if (this.config.highlightCodeOutline) {\n this.createCodeOutline();\n }\n }\n\n updateScanRegion(region: ScanRegion): void {\n if (!this.overlayEl || this.config.customOverlay) return;\n this.positionOverlayToRegion(region);\n }\n\n updateCodeOutline(\n cornerPoints: Point[] | null,\n scanRegion?: ScanRegion,\n ): void {\n if (!this.codeOutlineEl) return;\n\n if (!cornerPoints || cornerPoints.length < 4) {\n this.codeOutlineEl.style.display = 'none';\n return;\n }\n\n this.codeOutlineEl.style.display = 'block';\n\n const polygon = this.codeOutlineEl.querySelector('polygon');\n if (!polygon) return;\n\n // Corner points are relative to the cropped scan region.\n // Add the scan region offset to get full video coordinates,\n // then scale to display coordinates (accounting for object-fit).\n const regionX = scanRegion?.x ?? 0;\n const regionY = scanRegion?.y ?? 0;\n const rendered = getRenderedVideoRect(this.video);\n const scaleX = rendered.width / this.video.videoWidth;\n const scaleY = rendered.height / this.video.videoHeight;\n\n const points = cornerPoints\n .map(\n (p) =>\n `${(p.x + regionX) * scaleX + rendered.offsetX},${(p.y + regionY) * scaleY + rendered.offsetY}`,\n )\n .join(' ');\n\n polygon.setAttribute('points', points);\n }\n\n destroy(): void {\n if (this.overlayEl && !this.config.customOverlay) {\n this.overlayEl.remove();\n }\n if (this.codeOutlineEl) {\n this.codeOutlineEl.remove();\n }\n this.overlayEl = null;\n this.codeOutlineEl = null;\n }\n\n private createScanRegionOverlay(): void {\n this.overlayEl = document.createElement('div');\n this.overlayEl.className = 'qr-scanner-region';\n\n // Overlay size: 3/4 of the smaller container dimension, centered.\n const cw = this.container.clientWidth;\n const ch = this.container.clientHeight;\n const size = Math.round((Math.min(cw, ch) * 3) / 4);\n\n Object.assign(this.overlayEl.style, {\n position: 'absolute',\n top: '50%',\n left: '50%',\n transform: 'translate(-50%, -50%)',\n width: `${size}px`,\n height: `${size}px`,\n border: '2px solid rgba(255, 255, 255, 0.5)',\n borderRadius: '8px',\n boxShadow: '0 0 0 9999px rgba(0, 0, 0, 0.5)',\n pointerEvents: 'none',\n zIndex: '10',\n });\n\n // Corner markers\n const corners = ['top-left', 'top-right', 'bottom-left', 'bottom-right'];\n for (const corner of corners) {\n const marker = document.createElement('div');\n marker.className = `qr-scanner-corner qr-scanner-corner-${corner}`;\n\n const [vertical, horizontal] = corner.split('-');\n\n Object.assign(marker.style, {\n position: 'absolute',\n width: '24px',\n height: '24px',\n [vertical]: '-2px',\n [horizontal]: '-2px',\n [`border-${vertical}`]: '3px solid white',\n [`border-${horizontal}`]: '3px solid white',\n [`border-${vertical}-${horizontal}-radius`]: '8px',\n });\n\n this.overlayEl.appendChild(marker);\n }\n\n this.container.appendChild(this.overlayEl);\n }\n\n private createCodeOutline(): void {\n const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');\n svg.setAttribute('class', 'qr-scanner-code-outline');\n\n Object.assign(svg.style, {\n position: 'absolute',\n top: '0',\n left: '0',\n width: '100%',\n height: '100%',\n pointerEvents: 'none',\n zIndex: '11',\n display: 'none',\n });\n\n const polygon = document.createElementNS(\n 'http://www.w3.org/2000/svg',\n 'polygon',\n );\n polygon.setAttribute('fill', 'none');\n polygon.setAttribute('stroke', '#00ff00');\n polygon.setAttribute('stroke-width', '3');\n polygon.setAttribute('stroke-linejoin', 'round');\n\n // Animated stroke\n const animate = document.createElementNS(\n 'http://www.w3.org/2000/svg',\n 'animate',\n );\n animate.setAttribute('attributeName', 'stroke-opacity');\n animate.setAttribute('values', '1;0.5;1');\n animate.setAttribute('dur', '1.5s');\n animate.setAttribute('repeatCount', 'indefinite');\n polygon.appendChild(animate);\n\n svg.appendChild(polygon);\n this.container.appendChild(svg);\n this.codeOutlineEl = svg;\n }\n\n private positionOverlay(): void {\n if (!this.overlayEl) return;\n\n Object.assign(this.overlayEl.style, {\n position: 'absolute',\n top: '0',\n left: '0',\n width: '100%',\n height: '100%',\n pointerEvents: 'none',\n zIndex: '10',\n });\n }\n\n private positionOverlayToRegion(_region: ScanRegion): void {\n if (!this.overlayEl) return;\n\n // Keep the overlay at a container-relative centered size rather than\n // mapping scan region coordinates to display coordinates. With\n // object-fit: cover on a portrait phone with a landscape camera, the\n // mapped region extends beyond the container — fine once the camera is\n // visible but causes a jarring jump from the initial placeholder.\n // The overlay is a visual guide; the actual scan area (in the frame\n // extractor) is unaffected and may be larger than what's shown.\n const cw = this.container.clientWidth;\n const ch = this.container.clientHeight;\n const size = Math.round((Math.min(cw, ch) * 3) / 4);\n\n // Only update size — initial CSS centering (top: 50%, left: 50%,\n // transform: translate(-50%, -50%)) persists and handles re-centering.\n Object.assign(this.overlayEl.style, {\n width: `${size}px`,\n height: `${size}px`,\n });\n }\n}\n","import type { ScanRegion } from './types.js';\n\n/**\n * Calculate the default scan region: a centered square covering\n * 2/3 of the smaller video dimension.\n */\nexport function calculateDefaultScanRegion(video: HTMLVideoElement): ScanRegion {\n const videoWidth = video.videoWidth || video.width;\n const videoHeight = video.videoHeight || video.height;\n\n const smallerDimension = Math.min(videoWidth, videoHeight);\n const size = Math.round((smallerDimension * 2) / 3);\n\n return {\n x: Math.round((videoWidth - size) / 2),\n y: Math.round((videoHeight - size) / 2),\n width: size,\n height: size,\n };\n}\n","import { CameraManager } from './camera.js';\nimport { debug } from './debug.js';\nimport { FrameExtractor } from './frame-extractor.js';\nimport { ScanOverlay } from './overlay.js';\nimport { calculateDefaultScanRegion } from './scan-region.js';\nimport type {\n ScannerOptions,\n ScanResult,\n ScanRegion,\n InversionMode,\n WorkerRequest,\n WorkerResponse,\n} from './types.js';\nimport type { ReaderOptions } from 'zxing-wasm/reader';\n\ntype OnDecodeCallback = (result: ScanResult) => void;\n\n/**\n * Custom worker URL override. When set, this URL is used instead of the\n * default bundler-resolved worker. Useful for CJS consumers or non-standard\n * bundler setups.\n */\nlet customWorkerUrl: string | URL | null = null;\n\nexport function setWorkerUrl(url: string | URL): void {\n customWorkerUrl = url;\n}\n\nfunction resolveWorkerUrl(): string | URL {\n if (customWorkerUrl) {\n return customWorkerUrl;\n }\n // Standard pattern: modern bundlers (Vite, webpack 5, Parcel, esbuild)\n // resolve `new URL('./file', import.meta.url)` at build time,\n // copying worker.js to the output directory and returning the correct URL.\n // Falls back gracefully for CJS builds where import.meta is unavailable.\n try {\n return new URL('./worker.js', import.meta.url);\n } catch {\n throw new Error(\n '@agicash/qr-scanner: Could not resolve worker URL. ' +\n 'Call QrScanner.setWorkerUrl() with the path to the worker script before creating a scanner. ' +\n 'Example: QrScanner.setWorkerUrl(\"/path/to/@agicash/qr-scanner/dist/worker.js\")',\n );\n }\n}\n\nexport class Scanner {\n private video: HTMLVideoElement;\n private onDecode: OnDecodeCallback;\n private options: ScannerOptions;\n private camera: CameraManager;\n private frameExtractor: FrameExtractor | null = null;\n private worker: Worker | null = null;\n private overlay: ScanOverlay | null = null;\n private active = false;\n private paused = false;\n private destroyed = false;\n\n constructor(\n video: HTMLVideoElement,\n onDecode: OnDecodeCallback,\n options: ScannerOptions = {},\n ) {\n this.video = video;\n this.onDecode = onDecode;\n this.options = options;\n\n this.camera = new CameraManager({\n preferredCamera: options.preferredCamera,\n cameraResolution: options.cameraResolution,\n });\n }\n\n async start(): Promise<void> {\n if (this.destroyed) {\n throw new Error('Scanner has been destroyed');\n }\n\n if (this.active && !this.paused) {\n return; // Already running\n }\n\n const t0 = performance.now();\n\n // Show overlay immediately (CSS-centered placeholder) so the UI looks\n // ready while the camera is still loading.\n if (\n !this.overlay &&\n (this.options.highlightScanRegion ||\n this.options.highlightCodeOutline ||\n this.options.overlay)\n ) {\n try {\n this.overlay = new ScanOverlay(this.video, {\n highlightScanRegion: this.options.highlightScanRegion ?? false,\n highlightCodeOutline: this.options.highlightCodeOutline ?? false,\n customOverlay: this.options.overlay,\n });\n this.overlay.setup();\n } catch {\n // Overlay setup failed (e.g., no parent element) — continue without overlay\n }\n }\n\n // Start camera\n await this.camera.start(this.video);\n debug(\n `[QrScanner] start: camera ready ${(performance.now() - t0).toFixed(0)}ms`,\n );\n\n // Now that video dimensions are known, position overlay exactly\n if (this.overlay) {\n this.overlay.updateScanRegion(this.getCurrentScanRegion());\n }\n\n // Create worker if needed\n if (!this.worker) {\n const tw = performance.now();\n this.worker = this.createWorker();\n debug(\n `[QrScanner] start: worker created ${(performance.now() - tw).toFixed(0)}ms`,\n );\n }\n\n // Create frame extractor if needed\n if (!this.frameExtractor) {\n this.frameExtractor = new FrameExtractor(this.video, {\n maxScansPerSecond: this.options.maxScansPerSecond ?? 15,\n getScanRegion: () => this.getCurrentScanRegion(),\n });\n }\n\n // Start frame extraction loop\n this.frameExtractor.start((imageData) => {\n this.sendToWorker(imageData);\n });\n\n this.active = true;\n this.paused = false;\n\n debug(`[QrScanner] start: total ${(performance.now() - t0).toFixed(0)}ms`);\n }\n\n stop(): void {\n this.frameExtractor?.stop();\n this.camera.stop();\n this.video.srcObject = null;\n this.active = false;\n this.paused = false;\n }\n\n destroy(): void {\n if (this.destroyed) return;\n this.stop();\n this.frameExtractor?.destroy();\n this.frameExtractor = null;\n this.overlay?.destroy();\n this.overlay = null;\n this.worker?.terminate();\n this.worker = null;\n this.destroyed = true;\n }\n\n async pause(stopStreamImmediately = true): Promise<boolean> {\n if (!this.active) return false;\n\n this.frameExtractor?.stop();\n this.paused = true;\n\n if (stopStreamImmediately) {\n this.camera.stop();\n this.video.srcObject = null;\n }\n\n return true;\n }\n\n async setCamera(facingModeOrDeviceId: string): Promise<void> {\n const wasActive = this.active && !this.paused;\n if (wasActive) {\n this.frameExtractor?.stop();\n }\n\n await this.camera.setCamera(facingModeOrDeviceId, this.video);\n\n if (wasActive) {\n this.frameExtractor?.start((imageData) => {\n this.sendToWorker(imageData);\n });\n }\n }\n\n async hasFlash(): Promise<boolean> {\n return this.camera.hasFlash();\n }\n\n isFlashOn(): boolean {\n return this.camera.isFlashOn();\n }\n\n async toggleFlash(): Promise<void> {\n return this.camera.toggleFlash();\n }\n\n async turnFlashOn(): Promise<void> {\n return this.camera.turnFlashOn();\n }\n\n async turnFlashOff(): Promise<void> {\n return this.camera.turnFlashOff();\n }\n\n setInversionMode(mode: InversionMode): void {\n if (!this.worker) return;\n\n const options: Partial<ReaderOptions> = {};\n switch (mode) {\n case 'original':\n options.tryInvert = false;\n break;\n case 'invert':\n options.tryInvert = true;\n break;\n case 'both':\n options.tryInvert = true;\n break;\n }\n\n const msg: WorkerRequest = { type: 'configure', options };\n this.worker.postMessage(msg);\n }\n\n isActive(): boolean {\n return this.active;\n }\n\n isPaused(): boolean {\n return this.paused;\n }\n\n isDestroyed(): boolean {\n return this.destroyed;\n }\n\n private getCurrentScanRegion(): ScanRegion {\n if (this.options.calculateScanRegion) {\n return this.options.calculateScanRegion(this.video);\n }\n return calculateDefaultScanRegion(this.video);\n }\n\n private createWorker(): Worker {\n const workerUrl = resolveWorkerUrl();\n const worker = new Worker(workerUrl, { type: 'module' });\n\n // Configure with custom decoder options\n if (this.options.decoderOptions) {\n const msg: WorkerRequest = {\n type: 'configure',\n options: this.options.decoderOptions,\n };\n worker.postMessage(msg);\n }\n\n worker.onmessage = (e: MessageEvent<WorkerResponse>) => {\n this.handleWorkerMessage(e.data);\n };\n\n worker.onerror = (err) => {\n console.error('QR Scanner worker error:', err);\n this.frameExtractor?.markWorkerIdle();\n };\n\n return worker;\n }\n\n private handleWorkerMessage(response: WorkerResponse): void {\n this.frameExtractor?.markWorkerIdle();\n\n if (response.type === 'ready') {\n return;\n }\n\n if (response.type === 'error') {\n this.options.onDecodeError?.(response.message);\n return;\n }\n\n if (response.type === 'result') {\n if (response.results.length > 0) {\n const result = response.results[0];\n this.onDecode(result);\n\n // Update overlay\n if (this.overlay) {\n const region = this.getCurrentScanRegion();\n this.overlay.updateScanRegion(region);\n this.overlay.updateCodeOutline(result.cornerPoints, region);\n }\n } else {\n this.options.onDecodeError?.('No QR code found');\n\n // Hide code outline when no QR found\n if (this.overlay) {\n this.overlay.updateCodeOutline(null);\n }\n }\n }\n }\n\n private sendToWorker(imageData: ImageData): void {\n if (!this.worker) return;\n\n const msg: WorkerRequest = { type: 'decode', imageData };\n this.worker.postMessage(msg, [imageData.data.buffer]);\n }\n}\n","import { readBarcodes, type ReaderOptions } from 'zxing-wasm/reader';\nimport type { ScanResult, ScanRegion, Point } from './types.js';\nimport { loadImageData } from './utils.js';\n\nconst defaultReaderOptions: ReaderOptions = {\n formats: ['QRCode'],\n tryHarder: true,\n tryInvert: true,\n tryRotate: true,\n tryDenoise: false,\n tryDownscale: true,\n maxNumberOfSymbols: 1,\n};\n\nfunction mapPosition(position: {\n topLeft: Point;\n topRight: Point;\n bottomLeft: Point;\n bottomRight: Point;\n}): Point[] {\n return [\n position.topLeft,\n position.topRight,\n position.bottomRight,\n position.bottomLeft,\n ];\n}\n\n/** Input types that zxing-wasm can handle directly (no canvas needed). */\ntype DirectInput = Blob | ArrayBuffer | Uint8Array | ImageData;\n\n/** Input types that need canvas-based pixel extraction. */\ntype CanvasInput = HTMLImageElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap;\n\nfunction isDirectInput(source: unknown): source is DirectInput {\n return (\n source instanceof Blob ||\n source instanceof ArrayBuffer ||\n source instanceof Uint8Array ||\n (typeof ImageData !== 'undefined' && source instanceof ImageData)\n );\n}\n\n/**\n * Scan a single image for QR codes. Does not require a camera or video stream.\n */\nexport async function scanImage(\n source:\n | CanvasInput\n | DirectInput\n | File\n | URL\n | string,\n options?: {\n scanRegion?: ScanRegion | null;\n canvas?: HTMLCanvasElement | null;\n decoderOptions?: Partial<ReaderOptions>;\n },\n): Promise<ScanResult> {\n const readerOptions: ReaderOptions = {\n ...defaultReaderOptions,\n ...options?.decoderOptions,\n formats: ['QRCode'],\n };\n\n let input: DirectInput;\n if (isDirectInput(source)) {\n input = source;\n } else if (typeof source === 'string' || source instanceof URL) {\n // URL string - fetch and pass as ArrayBuffer\n const url = source instanceof URL ? source.href : source;\n const response = await fetch(url);\n input = await response.arrayBuffer();\n } else {\n // Canvas-based sources - extract ImageData\n input = await loadImageData(source, options?.scanRegion, options?.canvas);\n }\n\n const results = await readBarcodes(input, readerOptions);\n const valid = results.filter((r) => r.isValid);\n\n if (valid.length === 0) {\n throw new Error('No QR code found in the image');\n }\n\n const first = valid[0];\n return {\n data: first.text,\n cornerPoints: mapPosition(first.position),\n };\n}\n","import type { ScanRegion } from './types.js';\n\n/**\n * Load an image source into an ImageData object, optionally cropping to a scan region.\n */\nexport async function loadImageData(\n source: HTMLImageElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap | File | Blob | URL | string,\n scanRegion?: ScanRegion | null,\n canvas?: HTMLCanvasElement | null,\n): Promise<ImageData> {\n const img = await resolveImageSource(source);\n\n const sx = scanRegion?.x ?? 0;\n const sy = scanRegion?.y ?? 0;\n const sw = scanRegion?.width ?? (img.width - sx);\n const sh = scanRegion?.height ?? (img.height - sy);\n\n let drawCanvas: HTMLCanvasElement | OffscreenCanvas;\n let ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D;\n\n if (canvas) {\n drawCanvas = canvas;\n canvas.width = sw;\n canvas.height = sh;\n ctx = canvas.getContext('2d')!;\n } else if (typeof OffscreenCanvas !== 'undefined') {\n drawCanvas = new OffscreenCanvas(sw, sh);\n ctx = drawCanvas.getContext('2d')! as OffscreenCanvasRenderingContext2D;\n } else {\n drawCanvas = document.createElement('canvas');\n drawCanvas.width = sw;\n drawCanvas.height = sh;\n ctx = drawCanvas.getContext('2d')!;\n }\n\n ctx.drawImage(img as CanvasImageSource, sx, sy, sw, sh, 0, 0, sw, sh);\n return ctx.getImageData(0, 0, sw, sh);\n}\n\nasync function resolveImageSource(\n source: HTMLImageElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap | File | Blob | URL | string,\n): Promise<HTMLImageElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap> {\n if (source instanceof HTMLImageElement || source instanceof HTMLCanvasElement || source instanceof ImageBitmap) {\n return source;\n }\n\n if (typeof OffscreenCanvas !== 'undefined' && source instanceof OffscreenCanvas) {\n return source;\n }\n\n if (source instanceof File || source instanceof Blob) {\n return createImageBitmapFromBlob(source);\n }\n\n // URL or string\n const url = source instanceof URL ? source.href : source as string;\n const response = await fetch(url);\n const blob = await response.blob();\n return createImageBitmapFromBlob(blob);\n}\n\nasync function createImageBitmapFromBlob(blob: Blob): Promise<ImageBitmap> {\n if (typeof createImageBitmap !== 'undefined') {\n return createImageBitmap(blob);\n }\n // Fallback for environments without createImageBitmap\n const url = URL.createObjectURL(blob);\n try {\n const img = new Image();\n img.src = url;\n await new Promise<void>((resolve, reject) => {\n img.onload = () => resolve();\n img.onerror = () => reject(new Error('Failed to load image'));\n });\n return img as unknown as ImageBitmap;\n } finally {\n URL.revokeObjectURL(url);\n }\n}\n"],"mappings":"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","names":["t","import_reader","enabled"]}
package/dist/index.d.cts CHANGED
@@ -95,6 +95,13 @@ declare class QrScanner {
95
95
  * QrScanner.setWorkerUrl('/assets/qr-scanner-worker.js');
96
96
  */
97
97
  static setWorkerUrl(url: string | URL): void;
98
+ /**
99
+ * Enable or disable debug logging (performance timings, camera selection).
100
+ * Off by default. Useful for diagnosing camera issues in the browser console.
101
+ * @example
102
+ * QrScanner.setDebug(true);
103
+ */
104
+ static setDebug(enabled: boolean): void;
98
105
  /** Scan a single image (not a video stream). */
99
106
  static scanImage(source: HTMLImageElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap | ImageData | Blob | ArrayBuffer | Uint8Array | File | URL | string, options?: {
100
107
  scanRegion?: ScanRegion | null;
package/dist/index.d.ts CHANGED
@@ -95,6 +95,13 @@ declare class QrScanner {
95
95
  * QrScanner.setWorkerUrl('/assets/qr-scanner-worker.js');
96
96
  */
97
97
  static setWorkerUrl(url: string | URL): void;
98
+ /**
99
+ * Enable or disable debug logging (performance timings, camera selection).
100
+ * Off by default. Useful for diagnosing camera issues in the browser console.
101
+ * @example
102
+ * QrScanner.setDebug(true);
103
+ */
104
+ static setDebug(enabled: boolean): void;
98
105
  /** Scan a single image (not a video stream). */
99
106
  static scanImage(source: HTMLImageElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap | ImageData | Blob | ArrayBuffer | Uint8Array | File | URL | string, options?: {
100
107
  scanRegion?: ScanRegion | null;
package/dist/index.js CHANGED
@@ -1,3 +1,12 @@
1
+ // src/debug.ts
2
+ var enabled = false;
3
+ function setDebug(on) {
4
+ enabled = on;
5
+ }
6
+ function debug(...args) {
7
+ if (enabled) console.debug(...args);
8
+ }
9
+
1
10
  // src/camera.ts
2
11
  var CameraPermissionError = class extends Error {
3
12
  constructor(message = "Camera access denied. Please grant camera permission and try again.") {
@@ -38,16 +47,16 @@ var CameraManager = class {
38
47
  const t0 = performance.now();
39
48
  this.stream = await this.acquireStream();
40
49
  const t1 = performance.now();
41
- console.debug(`[QrScanner] acquireStream: ${(t1 - t0).toFixed(0)}ms`);
50
+ debug(`[QrScanner] acquireStream: ${(t1 - t0).toFixed(0)}ms`);
42
51
  await this.ensureBestCamera();
43
52
  const t2 = performance.now();
44
- console.debug(`[QrScanner] ensureBestCamera: ${(t2 - t1).toFixed(0)}ms`);
53
+ debug(`[QrScanner] ensureBestCamera: ${(t2 - t1).toFixed(0)}ms`);
45
54
  video.srcObject = this.stream;
46
55
  video.setAttribute("playsinline", "true");
47
56
  await video.play();
48
57
  const t3 = performance.now();
49
- console.debug(`[QrScanner] video.play: ${(t3 - t2).toFixed(0)}ms`);
50
- console.debug(`[QrScanner] camera.start total: ${(t3 - t0).toFixed(0)}ms`);
58
+ debug(`[QrScanner] video.play: ${(t3 - t2).toFixed(0)}ms`);
59
+ debug(`[QrScanner] camera.start total: ${(t3 - t0).toFixed(0)}ms`);
51
60
  if (this.facingMode === "environment" || this.facingMode === "user") {
52
61
  const finalDeviceId = this.getVideoTrack()?.getSettings().deviceId;
53
62
  if (finalDeviceId) {
@@ -122,9 +131,7 @@ var CameraManager = class {
122
131
  */
123
132
  async ensureBestCamera() {
124
133
  if (this.facingMode !== "environment" && this.facingMode !== "user") {
125
- console.debug(
126
- "[QrScanner] ensureBestCamera: skipped (specific deviceId)"
127
- );
134
+ debug("[QrScanner] ensureBestCamera: skipped (specific deviceId)");
128
135
  return;
129
136
  }
130
137
  const track = this.getVideoTrack();
@@ -132,12 +139,12 @@ var CameraManager = class {
132
139
  try {
133
140
  const capabilities = track.getCapabilities();
134
141
  if (!capabilities.focusMode || capabilities.focusMode.includes("continuous")) {
135
- console.debug(
142
+ debug(
136
143
  `[QrScanner] ensureBestCamera: skipped (focusMode: ${JSON.stringify(capabilities.focusMode)})`
137
144
  );
138
145
  return;
139
146
  }
140
- console.debug(
147
+ debug(
141
148
  `[QrScanner] ensureBestCamera: current camera lacks autofocus (focusMode: ${JSON.stringify(capabilities.focusMode)})`
142
149
  );
143
150
  } catch {
@@ -154,7 +161,7 @@ var CameraManager = class {
154
161
  const candidates = devices.filter(
155
162
  (d) => d.kind === "videoinput" && d.deviceId !== currentDeviceId
156
163
  );
157
- console.debug(
164
+ debug(
158
165
  `[QrScanner] ensureBestCamera: testing ${candidates.length} candidate camera(s)`
159
166
  );
160
167
  if (candidates.length === 0) return;
@@ -171,11 +178,11 @@ var CameraManager = class {
171
178
  },
172
179
  audio: false
173
180
  });
174
- console.debug(
181
+ debug(
175
182
  `[QrScanner] ensureBestCamera: candidate ${candidate.label || candidate.deviceId.slice(0, 8)}: getUserMedia ${(performance.now() - t).toFixed(0)}ms`
176
183
  );
177
184
  } catch {
178
- console.debug(
185
+ debug(
179
186
  `[QrScanner] ensureBestCamera: candidate ${candidate.label || candidate.deviceId.slice(0, 8)}: getUserMedia failed ${(performance.now() - t).toFixed(0)}ms`
180
187
  );
181
188
  continue;
@@ -261,12 +268,12 @@ var CameraManager = class {
261
268
  const t = performance.now();
262
269
  try {
263
270
  const stream = await navigator.mediaDevices.getUserMedia(attempts[i]);
264
- console.debug(
271
+ debug(
265
272
  `[QrScanner] getUserMedia(${labels[i]}): ${(performance.now() - t).toFixed(0)}ms \u2713`
266
273
  );
267
274
  return stream;
268
275
  } catch (err) {
269
- console.debug(
276
+ debug(
270
277
  `[QrScanner] getUserMedia(${labels[i]}): ${(performance.now() - t).toFixed(0)}ms \u2717 ${err instanceof DOMException ? err.name : err}`
271
278
  );
272
279
  if (err instanceof DOMException) {
@@ -654,7 +661,7 @@ var Scanner = class {
654
661
  }
655
662
  }
656
663
  await this.camera.start(this.video);
657
- console.debug(
664
+ debug(
658
665
  `[QrScanner] start: camera ready ${(performance.now() - t0).toFixed(0)}ms`
659
666
  );
660
667
  if (this.overlay) {
@@ -663,7 +670,7 @@ var Scanner = class {
663
670
  if (!this.worker) {
664
671
  const tw = performance.now();
665
672
  this.worker = this.createWorker();
666
- console.debug(
673
+ debug(
667
674
  `[QrScanner] start: worker created ${(performance.now() - tw).toFixed(0)}ms`
668
675
  );
669
676
  }
@@ -678,9 +685,7 @@ var Scanner = class {
678
685
  });
679
686
  this.active = true;
680
687
  this.paused = false;
681
- console.debug(
682
- `[QrScanner] start: total ${(performance.now() - t0).toFixed(0)}ms`
683
- );
688
+ debug(`[QrScanner] start: total ${(performance.now() - t0).toFixed(0)}ms`);
684
689
  }
685
690
  stop() {
686
691
  this.frameExtractor?.stop();
@@ -1022,6 +1027,15 @@ var QrScanner = class {
1022
1027
  static setWorkerUrl(url) {
1023
1028
  setWorkerUrl(url);
1024
1029
  }
1030
+ /**
1031
+ * Enable or disable debug logging (performance timings, camera selection).
1032
+ * Off by default. Useful for diagnosing camera issues in the browser console.
1033
+ * @example
1034
+ * QrScanner.setDebug(true);
1035
+ */
1036
+ static setDebug(enabled2) {
1037
+ setDebug(enabled2);
1038
+ }
1025
1039
  /** Scan a single image (not a video stream). */
1026
1040
  static scanImage(source, options) {
1027
1041
  return scanImage(source, options);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/camera.ts","../src/frame-extractor.ts","../src/overlay.ts","../src/scan-region.ts","../src/scanner.ts","../src/scan-image.ts","../src/utils.ts","../src/index.ts"],"sourcesContent":["import type { FacingMode, DeviceId, Camera } from './types.js';\n\nexport class CameraPermissionError extends Error {\n constructor(\n message = 'Camera access denied. Please grant camera permission and try again.',\n ) {\n super(message);\n this.name = 'CameraPermissionError';\n }\n}\n\nexport class CameraNotFoundError extends Error {\n constructor(\n message = 'No camera found. Please connect a camera and try again.',\n ) {\n super(message);\n this.name = 'CameraNotFoundError';\n }\n}\n\nconst CACHE_KEY_PREFIX = '@agicash/qr-scanner:camera:';\n\nfunction getCachedDeviceId(facingMode: string): string | null {\n try {\n return localStorage.getItem(`${CACHE_KEY_PREFIX}${facingMode}`);\n } catch {\n return null;\n }\n}\n\nfunction setCachedDeviceId(facingMode: string, deviceId: string): void {\n try {\n localStorage.setItem(`${CACHE_KEY_PREFIX}${facingMode}`, deviceId);\n } catch {\n // localStorage unavailable or full — ignore\n }\n}\n\nexport interface CameraConfig {\n preferredCamera?: FacingMode | DeviceId;\n cameraResolution?: {\n width?: MediaTrackConstraintSet['width'];\n height?: MediaTrackConstraintSet['height'];\n };\n}\n\nexport class CameraManager {\n private stream: MediaStream | null = null;\n private facingMode: FacingMode | DeviceId;\n private resolution: CameraConfig['cameraResolution'];\n\n constructor(config: CameraConfig = {}) {\n this.facingMode = config.preferredCamera ?? 'environment';\n this.resolution = config.cameraResolution;\n }\n\n async start(video: HTMLVideoElement): Promise<MediaStream> {\n if (this.stream) {\n return this.stream;\n }\n\n const t0 = performance.now();\n\n this.stream = await this.acquireStream();\n const t1 = performance.now();\n console.debug(`[QrScanner] acquireStream: ${(t1 - t0).toFixed(0)}ms`);\n\n // On some devices (e.g. Samsung S24 + Brave), facingMode: 'environment'\n // picks an ultrawide camera that lacks autofocus. Check and switch to a\n // better camera BEFORE showing on screen to avoid visible flicker.\n await this.ensureBestCamera();\n const t2 = performance.now();\n console.debug(`[QrScanner] ensureBestCamera: ${(t2 - t1).toFixed(0)}ms`);\n\n video.srcObject = this.stream;\n video.setAttribute('playsinline', 'true');\n await video.play();\n const t3 = performance.now();\n console.debug(`[QrScanner] video.play: ${(t3 - t2).toFixed(0)}ms`);\n console.debug(`[QrScanner] camera.start total: ${(t3 - t0).toFixed(0)}ms`);\n\n // Cache the final camera so subsequent starts skip ensureBestCamera\n if (this.facingMode === 'environment' || this.facingMode === 'user') {\n const finalDeviceId = this.getVideoTrack()?.getSettings().deviceId;\n if (finalDeviceId) {\n setCachedDeviceId(this.facingMode, finalDeviceId);\n }\n }\n\n return this.stream;\n }\n\n stop(): void {\n if (this.stream) {\n for (const track of this.stream.getTracks()) {\n track.stop();\n }\n this.stream = null;\n }\n }\n\n async setCamera(\n facingModeOrDeviceId: FacingMode | DeviceId,\n video: HTMLVideoElement,\n ): Promise<void> {\n this.stop();\n this.facingMode = facingModeOrDeviceId;\n await this.start(video);\n }\n\n getStream(): MediaStream | null {\n return this.stream;\n }\n\n async hasFlash(): Promise<boolean> {\n const track = this.getVideoTrack();\n if (!track) return false;\n\n try {\n const capabilities = track.getCapabilities() as MediaTrackCapabilities & {\n torch?: boolean;\n };\n return capabilities.torch === true;\n } catch {\n return false;\n }\n }\n\n isFlashOn(): boolean {\n const track = this.getVideoTrack();\n if (!track) return false;\n\n const settings = track.getSettings() as MediaTrackSettings & {\n torch?: boolean;\n };\n return settings.torch === true;\n }\n\n async toggleFlash(): Promise<void> {\n if (this.isFlashOn()) {\n await this.turnFlashOff();\n } else {\n await this.turnFlashOn();\n }\n }\n\n async turnFlashOn(): Promise<void> {\n await this.setTorch(true);\n }\n\n async turnFlashOff(): Promise<void> {\n await this.setTorch(false);\n }\n\n private async setTorch(on: boolean): Promise<void> {\n const track = this.getVideoTrack();\n if (!track) {\n throw new Error('No active camera stream');\n }\n\n try {\n await track.applyConstraints({\n advanced: [{ torch: on } as MediaTrackConstraintSet],\n });\n } catch {\n throw new Error('Flash/torch is not supported on this device');\n }\n }\n\n /**\n * If the current camera lacks continuous autofocus (e.g. an ultrawide sensor\n * picked by facingMode: 'environment'), find a better camera with the same\n * facing mode and replace this.stream. Called before assigning to the video\n * element so the user never sees the wrong camera.\n */\n private async ensureBestCamera(): Promise<void> {\n if (this.facingMode !== 'environment' && this.facingMode !== 'user') {\n console.debug(\n '[QrScanner] ensureBestCamera: skipped (specific deviceId)',\n );\n return;\n }\n\n const track = this.getVideoTrack();\n if (!track) return;\n\n try {\n const capabilities = track.getCapabilities() as MediaTrackCapabilities & {\n focusMode?: string[];\n };\n if (\n !capabilities.focusMode ||\n capabilities.focusMode.includes('continuous')\n ) {\n // focusMode not reported (e.g. Safari/iOS) or has autofocus — skip.\n // Only enter the candidate loop when the browser explicitly reports\n // focusMode without 'continuous' (e.g. S24 + Brave ultrawide).\n console.debug(\n `[QrScanner] ensureBestCamera: skipped (focusMode: ${JSON.stringify(capabilities.focusMode)})`,\n );\n return;\n }\n console.debug(\n `[QrScanner] ensureBestCamera: current camera lacks autofocus (focusMode: ${JSON.stringify(capabilities.focusMode)})`,\n );\n } catch {\n return;\n }\n\n // Current camera lacks continuous autofocus.\n // Enumerate devices while stream is active (ensures deviceIds are available).\n const currentDeviceId = track.getSettings().deviceId;\n\n let devices: MediaDeviceInfo[];\n try {\n devices = await navigator.mediaDevices.enumerateDevices();\n } catch {\n return;\n }\n if (!Array.isArray(devices)) return;\n\n const candidates = devices.filter(\n (d) => d.kind === 'videoinput' && d.deviceId !== currentDeviceId,\n );\n console.debug(\n `[QrScanner] ensureBestCamera: testing ${candidates.length} candidate camera(s)`,\n );\n if (candidates.length === 0) return;\n\n // Stop current stream — mobile devices only allow one active camera\n this.stop();\n\n for (const candidate of candidates) {\n const t = performance.now();\n let candidateStream: MediaStream;\n try {\n candidateStream = await navigator.mediaDevices.getUserMedia({\n video: {\n deviceId: { exact: candidate.deviceId },\n width: this.resolution?.width ?? { ideal: 1920 },\n height: this.resolution?.height ?? { ideal: 1080 },\n },\n audio: false,\n });\n console.debug(\n `[QrScanner] ensureBestCamera: candidate ${candidate.label || candidate.deviceId.slice(0, 8)}: getUserMedia ${(performance.now() - t).toFixed(0)}ms`,\n );\n } catch {\n console.debug(\n `[QrScanner] ensureBestCamera: candidate ${candidate.label || candidate.deviceId.slice(0, 8)}: getUserMedia failed ${(performance.now() - t).toFixed(0)}ms`,\n );\n continue;\n }\n\n const candidateTrack = candidateStream.getVideoTracks()[0];\n if (!candidateTrack) {\n for (const t of candidateStream.getTracks()) t.stop();\n continue;\n }\n\n // Must match the desired facing mode\n const candidateSettings =\n candidateTrack.getSettings() as MediaTrackSettings & {\n facingMode?: string;\n };\n if (\n candidateSettings.facingMode &&\n candidateSettings.facingMode !== this.facingMode\n ) {\n for (const t of candidateStream.getTracks()) t.stop();\n continue;\n }\n\n // Check if this camera supports continuous autofocus\n try {\n const candidateCaps =\n candidateTrack.getCapabilities() as MediaTrackCapabilities & {\n focusMode?: string[];\n };\n if (candidateCaps.focusMode?.includes('continuous')) {\n this.stream = candidateStream;\n return;\n }\n } catch {\n // Can't check capabilities, skip\n }\n\n for (const t of candidateStream.getTracks()) t.stop();\n }\n\n // No better camera found — re-open the original\n try {\n this.stream = await navigator.mediaDevices.getUserMedia({\n video: {\n deviceId: currentDeviceId ? { exact: currentDeviceId } : undefined,\n width: this.resolution?.width ?? { ideal: 1920 },\n height: this.resolution?.height ?? { ideal: 1080 },\n },\n audio: false,\n });\n } catch {\n try {\n this.stream = await navigator.mediaDevices.getUserMedia(\n this.buildConstraints(),\n );\n } catch {\n // Could not recover camera\n }\n }\n }\n\n private getVideoTrack(): MediaStreamTrack | null {\n if (!this.stream) return null;\n const tracks = this.stream.getVideoTracks();\n return tracks[0] ?? null;\n }\n\n /**\n * Try getUserMedia with progressively simpler constraints.\n *\n * Some browsers (e.g. Brave on Samsung Galaxy S24) throw NotReadableError\n * when facingMode and resolution constraints are combined. Falling back to\n * fewer constraints lets us still open the camera on those browsers.\n */\n private async acquireStream(): Promise<MediaStream> {\n const labels: string[] = [];\n const attempts: MediaStreamConstraints[] = [];\n\n // If we've previously found a good camera for this facingMode, try it first\n if (this.facingMode === 'environment' || this.facingMode === 'user') {\n const cachedId = getCachedDeviceId(this.facingMode);\n if (cachedId) {\n labels.push('cached deviceId');\n const video: MediaTrackConstraints = {\n deviceId: { exact: cachedId },\n };\n if (this.resolution?.width) video.width = this.resolution.width;\n else video.width = { ideal: 1920 };\n if (this.resolution?.height) video.height = this.resolution.height;\n else video.height = { ideal: 1080 };\n attempts.push({ video, audio: false });\n }\n }\n\n // Standard fallback chain\n labels.push('full constraints', 'no resolution', 'bare minimum');\n attempts.push(\n // facingMode/deviceId + resolution\n this.buildConstraints(),\n // facingMode/deviceId only, no resolution\n this.buildConstraints(false),\n // Bare minimum\n { video: true, audio: false },\n );\n\n let lastError: unknown;\n for (let i = 0; i < attempts.length; i++) {\n const t = performance.now();\n try {\n const stream = await navigator.mediaDevices.getUserMedia(attempts[i]);\n console.debug(\n `[QrScanner] getUserMedia(${labels[i]}): ${(performance.now() - t).toFixed(0)}ms ✓`,\n );\n return stream;\n } catch (err) {\n console.debug(\n `[QrScanner] getUserMedia(${labels[i]}): ${(performance.now() - t).toFixed(0)}ms ✗ ${err instanceof DOMException ? err.name : err}`,\n );\n if (err instanceof DOMException) {\n if (err.name === 'NotAllowedError') {\n throw new CameraPermissionError();\n }\n if (err.name === 'NotFoundError') {\n throw new CameraNotFoundError();\n }\n // NotReadableError or OverconstrainedError — try next fallback\n lastError = err;\n continue;\n }\n throw err;\n }\n }\n\n throw lastError;\n }\n\n private buildConstraints(includeResolution = true): MediaStreamConstraints {\n const video: MediaTrackConstraints = {};\n\n if (includeResolution) {\n video.width = this.resolution?.width ?? { ideal: 1920 };\n video.height = this.resolution?.height ?? { ideal: 1080 };\n }\n\n if (this.facingMode === 'environment' || this.facingMode === 'user') {\n video.facingMode = this.facingMode;\n } else {\n video.deviceId = { exact: this.facingMode };\n }\n\n return { video, audio: false };\n }\n\n static async hasCamera(): Promise<boolean> {\n try {\n const devices = await navigator.mediaDevices.enumerateDevices();\n return devices.some((d) => d.kind === 'videoinput');\n } catch {\n return false;\n }\n }\n\n static async listCameras(requestLabels = false): Promise<Camera[]> {\n if (requestLabels) {\n // Requesting labels requires a temporary stream to trigger the permission prompt\n try {\n const stream = await navigator.mediaDevices.getUserMedia({\n video: true,\n });\n for (const track of stream.getTracks()) {\n track.stop();\n }\n } catch {\n // Permission denied — fall through with empty labels\n }\n }\n\n const devices = await navigator.mediaDevices.enumerateDevices();\n return devices\n .filter((d) => d.kind === 'videoinput')\n .map((d) => ({\n id: d.deviceId,\n label: d.label || `Camera ${d.deviceId.slice(0, 8)}`,\n }));\n }\n}\n","import type { ScanRegion } from './types.js';\n\nexport interface FrameExtractorConfig {\n maxScansPerSecond: number;\n getScanRegion: () => ScanRegion;\n}\n\nexport class FrameExtractor {\n private video: HTMLVideoElement;\n private canvas: HTMLCanvasElement | OffscreenCanvas;\n private ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D;\n private rafId: number | null = null;\n private running = false;\n private workerBusy = false;\n private lastScanTime = -Infinity;\n private minInterval: number;\n private getScanRegion: () => ScanRegion;\n private onFrame: ((imageData: ImageData) => void) | null = null;\n\n constructor(video: HTMLVideoElement, config: FrameExtractorConfig) {\n this.video = video;\n this.minInterval = 1000 / config.maxScansPerSecond;\n this.getScanRegion = config.getScanRegion;\n\n if (typeof OffscreenCanvas !== 'undefined') {\n this.canvas = new OffscreenCanvas(1, 1);\n this.ctx = this.canvas.getContext('2d')! as OffscreenCanvasRenderingContext2D;\n } else {\n this.canvas = document.createElement('canvas');\n this.canvas.style.display = 'none';\n this.ctx = this.canvas.getContext('2d')!;\n }\n }\n\n start(onFrame: (imageData: ImageData) => void): void {\n if (this.running) return;\n this.running = true;\n this.onFrame = onFrame;\n this.rafId = requestAnimationFrame(this.tick);\n }\n\n stop(): void {\n this.running = false;\n this.onFrame = null;\n if (this.rafId !== null) {\n cancelAnimationFrame(this.rafId);\n this.rafId = null;\n }\n }\n\n destroy(): void {\n this.stop();\n if (this.canvas instanceof HTMLCanvasElement && this.canvas.parentNode) {\n this.canvas.parentNode.removeChild(this.canvas);\n }\n }\n\n markWorkerIdle(): void {\n this.workerBusy = false;\n }\n\n markWorkerBusy(): void {\n this.workerBusy = true;\n }\n\n private tick = (): void => {\n if (!this.running) return;\n\n this.rafId = requestAnimationFrame(this.tick);\n\n // Skip if worker is still processing previous frame\n if (this.workerBusy) return;\n\n // Rate limiting\n const now = performance.now();\n if (now - this.lastScanTime < this.minInterval) return;\n\n // Skip if video isn't ready\n if (this.video.readyState < 2) return;\n\n this.lastScanTime = now;\n\n const region = this.getScanRegion();\n const sx = region.x ?? 0;\n const sy = region.y ?? 0;\n const sw = region.width ?? this.video.videoWidth;\n const sh = region.height ?? this.video.videoHeight;\n\n if (sw <= 0 || sh <= 0) return;\n\n this.canvas.width = sw;\n this.canvas.height = sh;\n\n this.ctx.drawImage(this.video, sx, sy, sw, sh, 0, 0, sw, sh);\n const imageData = this.ctx.getImageData(0, 0, sw, sh);\n\n this.workerBusy = true;\n this.onFrame?.(imageData);\n };\n}\n","import type { ScanRegion, Point } from './types.js';\n\n/**\n * Compute the actual rendered position and size of the video content\n * within the element, accounting for object-fit: cover.\n *\n * With object-fit: cover the video is scaled up to fill the element and\n * cropped. The rendered content is larger than the element, centered,\n * with negative offsets for the cropped portions.\n *\n * When object-fit is the default (fill), the rendered size equals the\n * element size and offsets are zero — so this is backwards-compatible.\n */\nfunction getRenderedVideoRect(video: HTMLVideoElement): {\n offsetX: number;\n offsetY: number;\n width: number;\n height: number;\n} {\n const elementWidth = video.clientWidth;\n const elementHeight = video.clientHeight;\n const videoWidth = video.videoWidth || 1;\n const videoHeight = video.videoHeight || 1;\n\n const objectFit = getComputedStyle(video).objectFit;\n\n if (objectFit === 'cover') {\n const scale = Math.max(\n elementWidth / videoWidth,\n elementHeight / videoHeight,\n );\n const renderedWidth = videoWidth * scale;\n const renderedHeight = videoHeight * scale;\n return {\n offsetX: (elementWidth - renderedWidth) / 2,\n offsetY: (elementHeight - renderedHeight) / 2,\n width: renderedWidth,\n height: renderedHeight,\n };\n }\n\n if (objectFit === 'contain') {\n const scale = Math.min(\n elementWidth / videoWidth,\n elementHeight / videoHeight,\n );\n const renderedWidth = videoWidth * scale;\n const renderedHeight = videoHeight * scale;\n return {\n offsetX: (elementWidth - renderedWidth) / 2,\n offsetY: (elementHeight - renderedHeight) / 2,\n width: renderedWidth,\n height: renderedHeight,\n };\n }\n\n // Default (fill / none / scale-down with no scaling needed): element dimensions\n return { offsetX: 0, offsetY: 0, width: elementWidth, height: elementHeight };\n}\n\nexport interface OverlayConfig {\n highlightScanRegion: boolean;\n highlightCodeOutline: boolean;\n customOverlay?: HTMLDivElement;\n}\n\nexport class ScanOverlay {\n private container: HTMLElement;\n private overlayEl: HTMLDivElement | null = null;\n private codeOutlineEl: SVGElement | null = null;\n private config: OverlayConfig;\n private video: HTMLVideoElement;\n\n constructor(video: HTMLVideoElement, config: OverlayConfig) {\n this.video = video;\n this.config = config;\n\n const parent = video.parentElement;\n if (!parent) {\n throw new Error(\n 'QrScanner: video element must have a parent element. ' +\n 'The parent should have position: relative.',\n );\n }\n this.container = parent;\n }\n\n setup(): void {\n if (this.config.customOverlay) {\n this.overlayEl = this.config.customOverlay;\n this.positionOverlay();\n return;\n }\n\n if (this.config.highlightScanRegion) {\n this.createScanRegionOverlay();\n }\n\n if (this.config.highlightCodeOutline) {\n this.createCodeOutline();\n }\n }\n\n updateScanRegion(region: ScanRegion): void {\n if (!this.overlayEl || this.config.customOverlay) return;\n this.positionOverlayToRegion(region);\n }\n\n updateCodeOutline(\n cornerPoints: Point[] | null,\n scanRegion?: ScanRegion,\n ): void {\n if (!this.codeOutlineEl) return;\n\n if (!cornerPoints || cornerPoints.length < 4) {\n this.codeOutlineEl.style.display = 'none';\n return;\n }\n\n this.codeOutlineEl.style.display = 'block';\n\n const polygon = this.codeOutlineEl.querySelector('polygon');\n if (!polygon) return;\n\n // Corner points are relative to the cropped scan region.\n // Add the scan region offset to get full video coordinates,\n // then scale to display coordinates (accounting for object-fit).\n const regionX = scanRegion?.x ?? 0;\n const regionY = scanRegion?.y ?? 0;\n const rendered = getRenderedVideoRect(this.video);\n const scaleX = rendered.width / this.video.videoWidth;\n const scaleY = rendered.height / this.video.videoHeight;\n\n const points = cornerPoints\n .map(\n (p) =>\n `${(p.x + regionX) * scaleX + rendered.offsetX},${(p.y + regionY) * scaleY + rendered.offsetY}`,\n )\n .join(' ');\n\n polygon.setAttribute('points', points);\n }\n\n destroy(): void {\n if (this.overlayEl && !this.config.customOverlay) {\n this.overlayEl.remove();\n }\n if (this.codeOutlineEl) {\n this.codeOutlineEl.remove();\n }\n this.overlayEl = null;\n this.codeOutlineEl = null;\n }\n\n private createScanRegionOverlay(): void {\n this.overlayEl = document.createElement('div');\n this.overlayEl.className = 'qr-scanner-region';\n\n // Overlay size: 3/4 of the smaller container dimension, centered.\n const cw = this.container.clientWidth;\n const ch = this.container.clientHeight;\n const size = Math.round((Math.min(cw, ch) * 3) / 4);\n\n Object.assign(this.overlayEl.style, {\n position: 'absolute',\n top: '50%',\n left: '50%',\n transform: 'translate(-50%, -50%)',\n width: `${size}px`,\n height: `${size}px`,\n border: '2px solid rgba(255, 255, 255, 0.5)',\n borderRadius: '8px',\n boxShadow: '0 0 0 9999px rgba(0, 0, 0, 0.5)',\n pointerEvents: 'none',\n zIndex: '10',\n });\n\n // Corner markers\n const corners = ['top-left', 'top-right', 'bottom-left', 'bottom-right'];\n for (const corner of corners) {\n const marker = document.createElement('div');\n marker.className = `qr-scanner-corner qr-scanner-corner-${corner}`;\n\n const [vertical, horizontal] = corner.split('-');\n\n Object.assign(marker.style, {\n position: 'absolute',\n width: '24px',\n height: '24px',\n [vertical]: '-2px',\n [horizontal]: '-2px',\n [`border-${vertical}`]: '3px solid white',\n [`border-${horizontal}`]: '3px solid white',\n [`border-${vertical}-${horizontal}-radius`]: '8px',\n });\n\n this.overlayEl.appendChild(marker);\n }\n\n this.container.appendChild(this.overlayEl);\n }\n\n private createCodeOutline(): void {\n const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');\n svg.setAttribute('class', 'qr-scanner-code-outline');\n\n Object.assign(svg.style, {\n position: 'absolute',\n top: '0',\n left: '0',\n width: '100%',\n height: '100%',\n pointerEvents: 'none',\n zIndex: '11',\n display: 'none',\n });\n\n const polygon = document.createElementNS(\n 'http://www.w3.org/2000/svg',\n 'polygon',\n );\n polygon.setAttribute('fill', 'none');\n polygon.setAttribute('stroke', '#00ff00');\n polygon.setAttribute('stroke-width', '3');\n polygon.setAttribute('stroke-linejoin', 'round');\n\n // Animated stroke\n const animate = document.createElementNS(\n 'http://www.w3.org/2000/svg',\n 'animate',\n );\n animate.setAttribute('attributeName', 'stroke-opacity');\n animate.setAttribute('values', '1;0.5;1');\n animate.setAttribute('dur', '1.5s');\n animate.setAttribute('repeatCount', 'indefinite');\n polygon.appendChild(animate);\n\n svg.appendChild(polygon);\n this.container.appendChild(svg);\n this.codeOutlineEl = svg;\n }\n\n private positionOverlay(): void {\n if (!this.overlayEl) return;\n\n Object.assign(this.overlayEl.style, {\n position: 'absolute',\n top: '0',\n left: '0',\n width: '100%',\n height: '100%',\n pointerEvents: 'none',\n zIndex: '10',\n });\n }\n\n private positionOverlayToRegion(_region: ScanRegion): void {\n if (!this.overlayEl) return;\n\n // Keep the overlay at a container-relative centered size rather than\n // mapping scan region coordinates to display coordinates. With\n // object-fit: cover on a portrait phone with a landscape camera, the\n // mapped region extends beyond the container — fine once the camera is\n // visible but causes a jarring jump from the initial placeholder.\n // The overlay is a visual guide; the actual scan area (in the frame\n // extractor) is unaffected and may be larger than what's shown.\n const cw = this.container.clientWidth;\n const ch = this.container.clientHeight;\n const size = Math.round((Math.min(cw, ch) * 3) / 4);\n\n // Only update size — initial CSS centering (top: 50%, left: 50%,\n // transform: translate(-50%, -50%)) persists and handles re-centering.\n Object.assign(this.overlayEl.style, {\n width: `${size}px`,\n height: `${size}px`,\n });\n }\n}\n","import type { ScanRegion } from './types.js';\n\n/**\n * Calculate the default scan region: a centered square covering\n * 2/3 of the smaller video dimension.\n */\nexport function calculateDefaultScanRegion(video: HTMLVideoElement): ScanRegion {\n const videoWidth = video.videoWidth || video.width;\n const videoHeight = video.videoHeight || video.height;\n\n const smallerDimension = Math.min(videoWidth, videoHeight);\n const size = Math.round((smallerDimension * 2) / 3);\n\n return {\n x: Math.round((videoWidth - size) / 2),\n y: Math.round((videoHeight - size) / 2),\n width: size,\n height: size,\n };\n}\n","import { CameraManager } from './camera.js';\nimport { FrameExtractor } from './frame-extractor.js';\nimport { ScanOverlay } from './overlay.js';\nimport { calculateDefaultScanRegion } from './scan-region.js';\nimport type {\n ScannerOptions,\n ScanResult,\n ScanRegion,\n InversionMode,\n WorkerRequest,\n WorkerResponse,\n} from './types.js';\nimport type { ReaderOptions } from 'zxing-wasm/reader';\n\ntype OnDecodeCallback = (result: ScanResult) => void;\n\n/**\n * Custom worker URL override. When set, this URL is used instead of the\n * default bundler-resolved worker. Useful for CJS consumers or non-standard\n * bundler setups.\n */\nlet customWorkerUrl: string | URL | null = null;\n\nexport function setWorkerUrl(url: string | URL): void {\n customWorkerUrl = url;\n}\n\nfunction resolveWorkerUrl(): string | URL {\n if (customWorkerUrl) {\n return customWorkerUrl;\n }\n // Standard pattern: modern bundlers (Vite, webpack 5, Parcel, esbuild)\n // resolve `new URL('./file', import.meta.url)` at build time,\n // copying worker.js to the output directory and returning the correct URL.\n // Falls back gracefully for CJS builds where import.meta is unavailable.\n try {\n return new URL('./worker.js', import.meta.url);\n } catch {\n throw new Error(\n '@agicash/qr-scanner: Could not resolve worker URL. ' +\n 'Call QrScanner.setWorkerUrl() with the path to the worker script before creating a scanner. ' +\n 'Example: QrScanner.setWorkerUrl(\"/path/to/@agicash/qr-scanner/dist/worker.js\")',\n );\n }\n}\n\nexport class Scanner {\n private video: HTMLVideoElement;\n private onDecode: OnDecodeCallback;\n private options: ScannerOptions;\n private camera: CameraManager;\n private frameExtractor: FrameExtractor | null = null;\n private worker: Worker | null = null;\n private overlay: ScanOverlay | null = null;\n private active = false;\n private paused = false;\n private destroyed = false;\n\n constructor(\n video: HTMLVideoElement,\n onDecode: OnDecodeCallback,\n options: ScannerOptions = {},\n ) {\n this.video = video;\n this.onDecode = onDecode;\n this.options = options;\n\n this.camera = new CameraManager({\n preferredCamera: options.preferredCamera,\n cameraResolution: options.cameraResolution,\n });\n }\n\n async start(): Promise<void> {\n if (this.destroyed) {\n throw new Error('Scanner has been destroyed');\n }\n\n if (this.active && !this.paused) {\n return; // Already running\n }\n\n const t0 = performance.now();\n\n // Show overlay immediately (CSS-centered placeholder) so the UI looks\n // ready while the camera is still loading.\n if (\n !this.overlay &&\n (this.options.highlightScanRegion ||\n this.options.highlightCodeOutline ||\n this.options.overlay)\n ) {\n try {\n this.overlay = new ScanOverlay(this.video, {\n highlightScanRegion: this.options.highlightScanRegion ?? false,\n highlightCodeOutline: this.options.highlightCodeOutline ?? false,\n customOverlay: this.options.overlay,\n });\n this.overlay.setup();\n } catch {\n // Overlay setup failed (e.g., no parent element) — continue without overlay\n }\n }\n\n // Start camera\n await this.camera.start(this.video);\n console.debug(\n `[QrScanner] start: camera ready ${(performance.now() - t0).toFixed(0)}ms`,\n );\n\n // Now that video dimensions are known, position overlay exactly\n if (this.overlay) {\n this.overlay.updateScanRegion(this.getCurrentScanRegion());\n }\n\n // Create worker if needed\n if (!this.worker) {\n const tw = performance.now();\n this.worker = this.createWorker();\n console.debug(\n `[QrScanner] start: worker created ${(performance.now() - tw).toFixed(0)}ms`,\n );\n }\n\n // Create frame extractor if needed\n if (!this.frameExtractor) {\n this.frameExtractor = new FrameExtractor(this.video, {\n maxScansPerSecond: this.options.maxScansPerSecond ?? 15,\n getScanRegion: () => this.getCurrentScanRegion(),\n });\n }\n\n // Start frame extraction loop\n this.frameExtractor.start((imageData) => {\n this.sendToWorker(imageData);\n });\n\n this.active = true;\n this.paused = false;\n\n console.debug(\n `[QrScanner] start: total ${(performance.now() - t0).toFixed(0)}ms`,\n );\n }\n\n stop(): void {\n this.frameExtractor?.stop();\n this.camera.stop();\n this.video.srcObject = null;\n this.active = false;\n this.paused = false;\n }\n\n destroy(): void {\n if (this.destroyed) return;\n this.stop();\n this.frameExtractor?.destroy();\n this.frameExtractor = null;\n this.overlay?.destroy();\n this.overlay = null;\n this.worker?.terminate();\n this.worker = null;\n this.destroyed = true;\n }\n\n async pause(stopStreamImmediately = true): Promise<boolean> {\n if (!this.active) return false;\n\n this.frameExtractor?.stop();\n this.paused = true;\n\n if (stopStreamImmediately) {\n this.camera.stop();\n this.video.srcObject = null;\n }\n\n return true;\n }\n\n async setCamera(facingModeOrDeviceId: string): Promise<void> {\n const wasActive = this.active && !this.paused;\n if (wasActive) {\n this.frameExtractor?.stop();\n }\n\n await this.camera.setCamera(facingModeOrDeviceId, this.video);\n\n if (wasActive) {\n this.frameExtractor?.start((imageData) => {\n this.sendToWorker(imageData);\n });\n }\n }\n\n async hasFlash(): Promise<boolean> {\n return this.camera.hasFlash();\n }\n\n isFlashOn(): boolean {\n return this.camera.isFlashOn();\n }\n\n async toggleFlash(): Promise<void> {\n return this.camera.toggleFlash();\n }\n\n async turnFlashOn(): Promise<void> {\n return this.camera.turnFlashOn();\n }\n\n async turnFlashOff(): Promise<void> {\n return this.camera.turnFlashOff();\n }\n\n setInversionMode(mode: InversionMode): void {\n if (!this.worker) return;\n\n const options: Partial<ReaderOptions> = {};\n switch (mode) {\n case 'original':\n options.tryInvert = false;\n break;\n case 'invert':\n options.tryInvert = true;\n break;\n case 'both':\n options.tryInvert = true;\n break;\n }\n\n const msg: WorkerRequest = { type: 'configure', options };\n this.worker.postMessage(msg);\n }\n\n isActive(): boolean {\n return this.active;\n }\n\n isPaused(): boolean {\n return this.paused;\n }\n\n isDestroyed(): boolean {\n return this.destroyed;\n }\n\n private getCurrentScanRegion(): ScanRegion {\n if (this.options.calculateScanRegion) {\n return this.options.calculateScanRegion(this.video);\n }\n return calculateDefaultScanRegion(this.video);\n }\n\n private createWorker(): Worker {\n const workerUrl = resolveWorkerUrl();\n const worker = new Worker(workerUrl, { type: 'module' });\n\n // Configure with custom decoder options\n if (this.options.decoderOptions) {\n const msg: WorkerRequest = {\n type: 'configure',\n options: this.options.decoderOptions,\n };\n worker.postMessage(msg);\n }\n\n worker.onmessage = (e: MessageEvent<WorkerResponse>) => {\n this.handleWorkerMessage(e.data);\n };\n\n worker.onerror = (err) => {\n console.error('QR Scanner worker error:', err);\n this.frameExtractor?.markWorkerIdle();\n };\n\n return worker;\n }\n\n private handleWorkerMessage(response: WorkerResponse): void {\n this.frameExtractor?.markWorkerIdle();\n\n if (response.type === 'ready') {\n return;\n }\n\n if (response.type === 'error') {\n this.options.onDecodeError?.(response.message);\n return;\n }\n\n if (response.type === 'result') {\n if (response.results.length > 0) {\n const result = response.results[0];\n this.onDecode(result);\n\n // Update overlay\n if (this.overlay) {\n const region = this.getCurrentScanRegion();\n this.overlay.updateScanRegion(region);\n this.overlay.updateCodeOutline(result.cornerPoints, region);\n }\n } else {\n this.options.onDecodeError?.('No QR code found');\n\n // Hide code outline when no QR found\n if (this.overlay) {\n this.overlay.updateCodeOutline(null);\n }\n }\n }\n }\n\n private sendToWorker(imageData: ImageData): void {\n if (!this.worker) return;\n\n const msg: WorkerRequest = { type: 'decode', imageData };\n this.worker.postMessage(msg, [imageData.data.buffer]);\n }\n}\n","import { readBarcodes, type ReaderOptions } from 'zxing-wasm/reader';\nimport type { ScanResult, ScanRegion, Point } from './types.js';\nimport { loadImageData } from './utils.js';\n\nconst defaultReaderOptions: ReaderOptions = {\n formats: ['QRCode'],\n tryHarder: true,\n tryInvert: true,\n tryRotate: true,\n tryDenoise: false,\n tryDownscale: true,\n maxNumberOfSymbols: 1,\n};\n\nfunction mapPosition(position: {\n topLeft: Point;\n topRight: Point;\n bottomLeft: Point;\n bottomRight: Point;\n}): Point[] {\n return [\n position.topLeft,\n position.topRight,\n position.bottomRight,\n position.bottomLeft,\n ];\n}\n\n/** Input types that zxing-wasm can handle directly (no canvas needed). */\ntype DirectInput = Blob | ArrayBuffer | Uint8Array | ImageData;\n\n/** Input types that need canvas-based pixel extraction. */\ntype CanvasInput = HTMLImageElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap;\n\nfunction isDirectInput(source: unknown): source is DirectInput {\n return (\n source instanceof Blob ||\n source instanceof ArrayBuffer ||\n source instanceof Uint8Array ||\n (typeof ImageData !== 'undefined' && source instanceof ImageData)\n );\n}\n\n/**\n * Scan a single image for QR codes. Does not require a camera or video stream.\n */\nexport async function scanImage(\n source:\n | CanvasInput\n | DirectInput\n | File\n | URL\n | string,\n options?: {\n scanRegion?: ScanRegion | null;\n canvas?: HTMLCanvasElement | null;\n decoderOptions?: Partial<ReaderOptions>;\n },\n): Promise<ScanResult> {\n const readerOptions: ReaderOptions = {\n ...defaultReaderOptions,\n ...options?.decoderOptions,\n formats: ['QRCode'],\n };\n\n let input: DirectInput;\n if (isDirectInput(source)) {\n input = source;\n } else if (typeof source === 'string' || source instanceof URL) {\n // URL string - fetch and pass as ArrayBuffer\n const url = source instanceof URL ? source.href : source;\n const response = await fetch(url);\n input = await response.arrayBuffer();\n } else {\n // Canvas-based sources - extract ImageData\n input = await loadImageData(source, options?.scanRegion, options?.canvas);\n }\n\n const results = await readBarcodes(input, readerOptions);\n const valid = results.filter((r) => r.isValid);\n\n if (valid.length === 0) {\n throw new Error('No QR code found in the image');\n }\n\n const first = valid[0];\n return {\n data: first.text,\n cornerPoints: mapPosition(first.position),\n };\n}\n","import type { ScanRegion } from './types.js';\n\n/**\n * Load an image source into an ImageData object, optionally cropping to a scan region.\n */\nexport async function loadImageData(\n source: HTMLImageElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap | File | Blob | URL | string,\n scanRegion?: ScanRegion | null,\n canvas?: HTMLCanvasElement | null,\n): Promise<ImageData> {\n const img = await resolveImageSource(source);\n\n const sx = scanRegion?.x ?? 0;\n const sy = scanRegion?.y ?? 0;\n const sw = scanRegion?.width ?? (img.width - sx);\n const sh = scanRegion?.height ?? (img.height - sy);\n\n let drawCanvas: HTMLCanvasElement | OffscreenCanvas;\n let ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D;\n\n if (canvas) {\n drawCanvas = canvas;\n canvas.width = sw;\n canvas.height = sh;\n ctx = canvas.getContext('2d')!;\n } else if (typeof OffscreenCanvas !== 'undefined') {\n drawCanvas = new OffscreenCanvas(sw, sh);\n ctx = drawCanvas.getContext('2d')! as OffscreenCanvasRenderingContext2D;\n } else {\n drawCanvas = document.createElement('canvas');\n drawCanvas.width = sw;\n drawCanvas.height = sh;\n ctx = drawCanvas.getContext('2d')!;\n }\n\n ctx.drawImage(img as CanvasImageSource, sx, sy, sw, sh, 0, 0, sw, sh);\n return ctx.getImageData(0, 0, sw, sh);\n}\n\nasync function resolveImageSource(\n source: HTMLImageElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap | File | Blob | URL | string,\n): Promise<HTMLImageElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap> {\n if (source instanceof HTMLImageElement || source instanceof HTMLCanvasElement || source instanceof ImageBitmap) {\n return source;\n }\n\n if (typeof OffscreenCanvas !== 'undefined' && source instanceof OffscreenCanvas) {\n return source;\n }\n\n if (source instanceof File || source instanceof Blob) {\n return createImageBitmapFromBlob(source);\n }\n\n // URL or string\n const url = source instanceof URL ? source.href : source as string;\n const response = await fetch(url);\n const blob = await response.blob();\n return createImageBitmapFromBlob(blob);\n}\n\nasync function createImageBitmapFromBlob(blob: Blob): Promise<ImageBitmap> {\n if (typeof createImageBitmap !== 'undefined') {\n return createImageBitmap(blob);\n }\n // Fallback for environments without createImageBitmap\n const url = URL.createObjectURL(blob);\n try {\n const img = new Image();\n img.src = url;\n await new Promise<void>((resolve, reject) => {\n img.onload = () => resolve();\n img.onerror = () => reject(new Error('Failed to load image'));\n });\n return img as unknown as ImageBitmap;\n } finally {\n URL.revokeObjectURL(url);\n }\n}\n","import { Scanner, setWorkerUrl } from './scanner.js';\nimport {\n CameraManager,\n CameraNotFoundError,\n CameraPermissionError,\n} from './camera.js';\nimport { scanImage } from './scan-image.js';\nimport { setZXingModuleOverrides } from 'zxing-wasm/reader';\nimport type {\n ScanResult,\n ScanRegion,\n ScannerOptions,\n Camera,\n Point,\n FacingMode,\n DeviceId,\n InversionMode,\n} from './types.js';\nimport type { ReaderOptions } from 'zxing-wasm/reader';\n\nexport { CameraNotFoundError, CameraPermissionError };\n\nexport type {\n ScanResult,\n ScanRegion,\n ScannerOptions,\n Camera,\n Point,\n FacingMode,\n DeviceId,\n InversionMode,\n};\n\n/**\n * High-performance QR code scanner for the web, powered by ZXing-C++ WebAssembly.\n */\nclass QrScanner {\n private scanner: Scanner;\n\n constructor(\n videoElement: HTMLVideoElement,\n onDecode: (result: ScanResult) => void,\n options: ScannerOptions = {},\n ) {\n this.scanner = new Scanner(videoElement, onDecode, options);\n }\n\n /** Start camera and begin scanning. Resolves when camera is ready. */\n async start(): Promise<void> {\n return this.scanner.start();\n }\n\n /** Stop scanning and release the camera stream. */\n stop(): void {\n this.scanner.stop();\n }\n\n /** Stop scanning, release camera, terminate worker, clean up DOM. */\n destroy(): void {\n this.scanner.destroy();\n }\n\n /** Pause scanning. If stopStreamImmediately is false, camera stays on. */\n async pause(stopStreamImmediately?: boolean): Promise<boolean> {\n return this.scanner.pause(stopStreamImmediately);\n }\n\n /** Switch to a different camera by facing mode or device ID. */\n async setCamera(facingModeOrDeviceId: FacingMode | DeviceId): Promise<void> {\n return this.scanner.setCamera(facingModeOrDeviceId);\n }\n\n /** Check if the current camera supports flash/torch. */\n async hasFlash(): Promise<boolean> {\n return this.scanner.hasFlash();\n }\n\n /** Whether flash is currently on. */\n isFlashOn(): boolean {\n return this.scanner.isFlashOn();\n }\n\n /** Toggle flash on/off. */\n async toggleFlash(): Promise<void> {\n return this.scanner.toggleFlash();\n }\n\n /** Turn flash on. */\n async turnFlashOn(): Promise<void> {\n return this.scanner.turnFlashOn();\n }\n\n /** Turn flash off. */\n async turnFlashOff(): Promise<void> {\n return this.scanner.turnFlashOff();\n }\n\n /** Set the inversion mode for detecting inverted QR codes. */\n setInversionMode(mode: InversionMode): void {\n this.scanner.setInversionMode(mode);\n }\n\n // --- Static methods ---\n\n /** Check if the device has at least one camera. */\n static hasCamera(): Promise<boolean> {\n return CameraManager.hasCamera();\n }\n\n /** List available cameras. Pass true to request labels (triggers permission prompt). */\n static listCameras(requestLabels?: boolean): Promise<Camera[]> {\n return CameraManager.listCameras(requestLabels);\n }\n\n /**\n * Pre-load the WASM binary so it's ready when the scanner starts.\n * Call this early (e.g., on app init) to avoid delay on first scan.\n */\n static async preload(): Promise<void> {\n // Trigger WASM loading by doing a minimal scan\n const pixel = new Uint8ClampedArray([255, 255, 255, 255]);\n const img = new ImageData(pixel, 1, 1);\n try {\n await scanImage(img);\n } catch {\n // Expected — no QR code in a 1x1 image. The point was to load WASM.\n }\n }\n\n /**\n * Configure WASM loading. Call before creating any scanner instance.\n * @example\n * QrScanner.configureWasm({ locateFile: (filename) => `/wasm/${filename}` });\n */\n static configureWasm(overrides: Partial<EmscriptenModule>): void {\n setZXingModuleOverrides(overrides);\n }\n\n /**\n * Set a custom URL for the worker script. Call before creating any scanner.\n * Needed for CJS consumers or non-standard bundler setups.\n * By default, the worker URL is resolved via `new URL('./worker.js', import.meta.url)`,\n * which works with Vite, webpack 5, Parcel, and other modern bundlers.\n * @example\n * QrScanner.setWorkerUrl('/assets/qr-scanner-worker.js');\n */\n static setWorkerUrl(url: string | URL): void {\n setWorkerUrl(url);\n }\n\n /** Scan a single image (not a video stream). */\n static scanImage(\n source:\n | HTMLImageElement\n | HTMLCanvasElement\n | OffscreenCanvas\n | ImageBitmap\n | ImageData\n | Blob\n | ArrayBuffer\n | Uint8Array\n | File\n | URL\n | string,\n options?: {\n scanRegion?: ScanRegion | null;\n canvas?: HTMLCanvasElement | null;\n decoderOptions?: Partial<ReaderOptions>;\n },\n ): Promise<ScanResult> {\n return scanImage(source, options);\n }\n}\n\nexport default QrScanner;\n"],"mappings":";AAEO,IAAM,wBAAN,cAAoC,MAAM;AAAA,EAC/C,YACE,UAAU,uEACV;AACA,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,sBAAN,cAAkC,MAAM;AAAA,EAC7C,YACE,UAAU,2DACV;AACA,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAEA,IAAM,mBAAmB;AAEzB,SAAS,kBAAkB,YAAmC;AAC5D,MAAI;AACF,WAAO,aAAa,QAAQ,GAAG,gBAAgB,GAAG,UAAU,EAAE;AAAA,EAChE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,kBAAkB,YAAoB,UAAwB;AACrE,MAAI;AACF,iBAAa,QAAQ,GAAG,gBAAgB,GAAG,UAAU,IAAI,QAAQ;AAAA,EACnE,QAAQ;AAAA,EAER;AACF;AAUO,IAAM,gBAAN,MAAoB;AAAA,EAKzB,YAAY,SAAuB,CAAC,GAAG;AAJvC,SAAQ,SAA6B;AAKnC,SAAK,aAAa,OAAO,mBAAmB;AAC5C,SAAK,aAAa,OAAO;AAAA,EAC3B;AAAA,EAEA,MAAM,MAAM,OAA+C;AACzD,QAAI,KAAK,QAAQ;AACf,aAAO,KAAK;AAAA,IACd;AAEA,UAAM,KAAK,YAAY,IAAI;AAE3B,SAAK,SAAS,MAAM,KAAK,cAAc;AACvC,UAAM,KAAK,YAAY,IAAI;AAC3B,YAAQ,MAAM,+BAA+B,KAAK,IAAI,QAAQ,CAAC,CAAC,IAAI;AAKpE,UAAM,KAAK,iBAAiB;AAC5B,UAAM,KAAK,YAAY,IAAI;AAC3B,YAAQ,MAAM,kCAAkC,KAAK,IAAI,QAAQ,CAAC,CAAC,IAAI;AAEvE,UAAM,YAAY,KAAK;AACvB,UAAM,aAAa,eAAe,MAAM;AACxC,UAAM,MAAM,KAAK;AACjB,UAAM,KAAK,YAAY,IAAI;AAC3B,YAAQ,MAAM,4BAA4B,KAAK,IAAI,QAAQ,CAAC,CAAC,IAAI;AACjE,YAAQ,MAAM,oCAAoC,KAAK,IAAI,QAAQ,CAAC,CAAC,IAAI;AAGzE,QAAI,KAAK,eAAe,iBAAiB,KAAK,eAAe,QAAQ;AACnE,YAAM,gBAAgB,KAAK,cAAc,GAAG,YAAY,EAAE;AAC1D,UAAI,eAAe;AACjB,0BAAkB,KAAK,YAAY,aAAa;AAAA,MAClD;AAAA,IACF;AAEA,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,OAAa;AACX,QAAI,KAAK,QAAQ;AACf,iBAAW,SAAS,KAAK,OAAO,UAAU,GAAG;AAC3C,cAAM,KAAK;AAAA,MACb;AACA,WAAK,SAAS;AAAA,IAChB;AAAA,EACF;AAAA,EAEA,MAAM,UACJ,sBACA,OACe;AACf,SAAK,KAAK;AACV,SAAK,aAAa;AAClB,UAAM,KAAK,MAAM,KAAK;AAAA,EACxB;AAAA,EAEA,YAAgC;AAC9B,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAM,WAA6B;AACjC,UAAM,QAAQ,KAAK,cAAc;AACjC,QAAI,CAAC,MAAO,QAAO;AAEnB,QAAI;AACF,YAAM,eAAe,MAAM,gBAAgB;AAG3C,aAAO,aAAa,UAAU;AAAA,IAChC,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,YAAqB;AACnB,UAAM,QAAQ,KAAK,cAAc;AACjC,QAAI,CAAC,MAAO,QAAO;AAEnB,UAAM,WAAW,MAAM,YAAY;AAGnC,WAAO,SAAS,UAAU;AAAA,EAC5B;AAAA,EAEA,MAAM,cAA6B;AACjC,QAAI,KAAK,UAAU,GAAG;AACpB,YAAM,KAAK,aAAa;AAAA,IAC1B,OAAO;AACL,YAAM,KAAK,YAAY;AAAA,IACzB;AAAA,EACF;AAAA,EAEA,MAAM,cAA6B;AACjC,UAAM,KAAK,SAAS,IAAI;AAAA,EAC1B;AAAA,EAEA,MAAM,eAA8B;AAClC,UAAM,KAAK,SAAS,KAAK;AAAA,EAC3B;AAAA,EAEA,MAAc,SAAS,IAA4B;AACjD,UAAM,QAAQ,KAAK,cAAc;AACjC,QAAI,CAAC,OAAO;AACV,YAAM,IAAI,MAAM,yBAAyB;AAAA,IAC3C;AAEA,QAAI;AACF,YAAM,MAAM,iBAAiB;AAAA,QAC3B,UAAU,CAAC,EAAE,OAAO,GAAG,CAA4B;AAAA,MACrD,CAAC;AAAA,IACH,QAAQ;AACN,YAAM,IAAI,MAAM,6CAA6C;AAAA,IAC/D;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,mBAAkC;AAC9C,QAAI,KAAK,eAAe,iBAAiB,KAAK,eAAe,QAAQ;AACnE,cAAQ;AAAA,QACN;AAAA,MACF;AACA;AAAA,IACF;AAEA,UAAM,QAAQ,KAAK,cAAc;AACjC,QAAI,CAAC,MAAO;AAEZ,QAAI;AACF,YAAM,eAAe,MAAM,gBAAgB;AAG3C,UACE,CAAC,aAAa,aACd,aAAa,UAAU,SAAS,YAAY,GAC5C;AAIA,gBAAQ;AAAA,UACN,qDAAqD,KAAK,UAAU,aAAa,SAAS,CAAC;AAAA,QAC7F;AACA;AAAA,MACF;AACA,cAAQ;AAAA,QACN,4EAA4E,KAAK,UAAU,aAAa,SAAS,CAAC;AAAA,MACpH;AAAA,IACF,QAAQ;AACN;AAAA,IACF;AAIA,UAAM,kBAAkB,MAAM,YAAY,EAAE;AAE5C,QAAI;AACJ,QAAI;AACF,gBAAU,MAAM,UAAU,aAAa,iBAAiB;AAAA,IAC1D,QAAQ;AACN;AAAA,IACF;AACA,QAAI,CAAC,MAAM,QAAQ,OAAO,EAAG;AAE7B,UAAM,aAAa,QAAQ;AAAA,MACzB,CAAC,MAAM,EAAE,SAAS,gBAAgB,EAAE,aAAa;AAAA,IACnD;AACA,YAAQ;AAAA,MACN,yCAAyC,WAAW,MAAM;AAAA,IAC5D;AACA,QAAI,WAAW,WAAW,EAAG;AAG7B,SAAK,KAAK;AAEV,eAAW,aAAa,YAAY;AAClC,YAAM,IAAI,YAAY,IAAI;AAC1B,UAAI;AACJ,UAAI;AACF,0BAAkB,MAAM,UAAU,aAAa,aAAa;AAAA,UAC1D,OAAO;AAAA,YACL,UAAU,EAAE,OAAO,UAAU,SAAS;AAAA,YACtC,OAAO,KAAK,YAAY,SAAS,EAAE,OAAO,KAAK;AAAA,YAC/C,QAAQ,KAAK,YAAY,UAAU,EAAE,OAAO,KAAK;AAAA,UACnD;AAAA,UACA,OAAO;AAAA,QACT,CAAC;AACD,gBAAQ;AAAA,UACN,2CAA2C,UAAU,SAAS,UAAU,SAAS,MAAM,GAAG,CAAC,CAAC,mBAAmB,YAAY,IAAI,IAAI,GAAG,QAAQ,CAAC,CAAC;AAAA,QAClJ;AAAA,MACF,QAAQ;AACN,gBAAQ;AAAA,UACN,2CAA2C,UAAU,SAAS,UAAU,SAAS,MAAM,GAAG,CAAC,CAAC,0BAA0B,YAAY,IAAI,IAAI,GAAG,QAAQ,CAAC,CAAC;AAAA,QACzJ;AACA;AAAA,MACF;AAEA,YAAM,iBAAiB,gBAAgB,eAAe,EAAE,CAAC;AACzD,UAAI,CAAC,gBAAgB;AACnB,mBAAWA,MAAK,gBAAgB,UAAU,EAAG,CAAAA,GAAE,KAAK;AACpD;AAAA,MACF;AAGA,YAAM,oBACJ,eAAe,YAAY;AAG7B,UACE,kBAAkB,cAClB,kBAAkB,eAAe,KAAK,YACtC;AACA,mBAAWA,MAAK,gBAAgB,UAAU,EAAG,CAAAA,GAAE,KAAK;AACpD;AAAA,MACF;AAGA,UAAI;AACF,cAAM,gBACJ,eAAe,gBAAgB;AAGjC,YAAI,cAAc,WAAW,SAAS,YAAY,GAAG;AACnD,eAAK,SAAS;AACd;AAAA,QACF;AAAA,MACF,QAAQ;AAAA,MAER;AAEA,iBAAWA,MAAK,gBAAgB,UAAU,EAAG,CAAAA,GAAE,KAAK;AAAA,IACtD;AAGA,QAAI;AACF,WAAK,SAAS,MAAM,UAAU,aAAa,aAAa;AAAA,QACtD,OAAO;AAAA,UACL,UAAU,kBAAkB,EAAE,OAAO,gBAAgB,IAAI;AAAA,UACzD,OAAO,KAAK,YAAY,SAAS,EAAE,OAAO,KAAK;AAAA,UAC/C,QAAQ,KAAK,YAAY,UAAU,EAAE,OAAO,KAAK;AAAA,QACnD;AAAA,QACA,OAAO;AAAA,MACT,CAAC;AAAA,IACH,QAAQ;AACN,UAAI;AACF,aAAK,SAAS,MAAM,UAAU,aAAa;AAAA,UACzC,KAAK,iBAAiB;AAAA,QACxB;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,gBAAyC;AAC/C,QAAI,CAAC,KAAK,OAAQ,QAAO;AACzB,UAAM,SAAS,KAAK,OAAO,eAAe;AAC1C,WAAO,OAAO,CAAC,KAAK;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAc,gBAAsC;AAClD,UAAM,SAAmB,CAAC;AAC1B,UAAM,WAAqC,CAAC;AAG5C,QAAI,KAAK,eAAe,iBAAiB,KAAK,eAAe,QAAQ;AACnE,YAAM,WAAW,kBAAkB,KAAK,UAAU;AAClD,UAAI,UAAU;AACZ,eAAO,KAAK,iBAAiB;AAC7B,cAAM,QAA+B;AAAA,UACnC,UAAU,EAAE,OAAO,SAAS;AAAA,QAC9B;AACA,YAAI,KAAK,YAAY,MAAO,OAAM,QAAQ,KAAK,WAAW;AAAA,YACrD,OAAM,QAAQ,EAAE,OAAO,KAAK;AACjC,YAAI,KAAK,YAAY,OAAQ,OAAM,SAAS,KAAK,WAAW;AAAA,YACvD,OAAM,SAAS,EAAE,OAAO,KAAK;AAClC,iBAAS,KAAK,EAAE,OAAO,OAAO,MAAM,CAAC;AAAA,MACvC;AAAA,IACF;AAGA,WAAO,KAAK,oBAAoB,iBAAiB,cAAc;AAC/D,aAAS;AAAA;AAAA,MAEP,KAAK,iBAAiB;AAAA;AAAA,MAEtB,KAAK,iBAAiB,KAAK;AAAA;AAAA,MAE3B,EAAE,OAAO,MAAM,OAAO,MAAM;AAAA,IAC9B;AAEA,QAAI;AACJ,aAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACxC,YAAM,IAAI,YAAY,IAAI;AAC1B,UAAI;AACF,cAAM,SAAS,MAAM,UAAU,aAAa,aAAa,SAAS,CAAC,CAAC;AACpE,gBAAQ;AAAA,UACN,4BAA4B,OAAO,CAAC,CAAC,OAAO,YAAY,IAAI,IAAI,GAAG,QAAQ,CAAC,CAAC;AAAA,QAC/E;AACA,eAAO;AAAA,MACT,SAAS,KAAK;AACZ,gBAAQ;AAAA,UACN,4BAA4B,OAAO,CAAC,CAAC,OAAO,YAAY,IAAI,IAAI,GAAG,QAAQ,CAAC,CAAC,aAAQ,eAAe,eAAe,IAAI,OAAO,GAAG;AAAA,QACnI;AACA,YAAI,eAAe,cAAc;AAC/B,cAAI,IAAI,SAAS,mBAAmB;AAClC,kBAAM,IAAI,sBAAsB;AAAA,UAClC;AACA,cAAI,IAAI,SAAS,iBAAiB;AAChC,kBAAM,IAAI,oBAAoB;AAAA,UAChC;AAEA,sBAAY;AACZ;AAAA,QACF;AACA,cAAM;AAAA,MACR;AAAA,IACF;AAEA,UAAM;AAAA,EACR;AAAA,EAEQ,iBAAiB,oBAAoB,MAA8B;AACzE,UAAM,QAA+B,CAAC;AAEtC,QAAI,mBAAmB;AACrB,YAAM,QAAQ,KAAK,YAAY,SAAS,EAAE,OAAO,KAAK;AACtD,YAAM,SAAS,KAAK,YAAY,UAAU,EAAE,OAAO,KAAK;AAAA,IAC1D;AAEA,QAAI,KAAK,eAAe,iBAAiB,KAAK,eAAe,QAAQ;AACnE,YAAM,aAAa,KAAK;AAAA,IAC1B,OAAO;AACL,YAAM,WAAW,EAAE,OAAO,KAAK,WAAW;AAAA,IAC5C;AAEA,WAAO,EAAE,OAAO,OAAO,MAAM;AAAA,EAC/B;AAAA,EAEA,aAAa,YAA8B;AACzC,QAAI;AACF,YAAM,UAAU,MAAM,UAAU,aAAa,iBAAiB;AAC9D,aAAO,QAAQ,KAAK,CAAC,MAAM,EAAE,SAAS,YAAY;AAAA,IACpD,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,aAAa,YAAY,gBAAgB,OAA0B;AACjE,QAAI,eAAe;AAEjB,UAAI;AACF,cAAM,SAAS,MAAM,UAAU,aAAa,aAAa;AAAA,UACvD,OAAO;AAAA,QACT,CAAC;AACD,mBAAW,SAAS,OAAO,UAAU,GAAG;AACtC,gBAAM,KAAK;AAAA,QACb;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF;AAEA,UAAM,UAAU,MAAM,UAAU,aAAa,iBAAiB;AAC9D,WAAO,QACJ,OAAO,CAAC,MAAM,EAAE,SAAS,YAAY,EACrC,IAAI,CAAC,OAAO;AAAA,MACX,IAAI,EAAE;AAAA,MACN,OAAO,EAAE,SAAS,UAAU,EAAE,SAAS,MAAM,GAAG,CAAC,CAAC;AAAA,IACpD,EAAE;AAAA,EACN;AACF;;;AC5aO,IAAM,iBAAN,MAAqB;AAAA,EAY1B,YAAY,OAAyB,QAA8B;AARnE,SAAQ,QAAuB;AAC/B,SAAQ,UAAU;AAClB,SAAQ,aAAa;AACrB,SAAQ,eAAe;AAGvB,SAAQ,UAAmD;AAgD3D,SAAQ,OAAO,MAAY;AACzB,UAAI,CAAC,KAAK,QAAS;AAEnB,WAAK,QAAQ,sBAAsB,KAAK,IAAI;AAG5C,UAAI,KAAK,WAAY;AAGrB,YAAM,MAAM,YAAY,IAAI;AAC5B,UAAI,MAAM,KAAK,eAAe,KAAK,YAAa;AAGhD,UAAI,KAAK,MAAM,aAAa,EAAG;AAE/B,WAAK,eAAe;AAEpB,YAAM,SAAS,KAAK,cAAc;AAClC,YAAM,KAAK,OAAO,KAAK;AACvB,YAAM,KAAK,OAAO,KAAK;AACvB,YAAM,KAAK,OAAO,SAAS,KAAK,MAAM;AACtC,YAAM,KAAK,OAAO,UAAU,KAAK,MAAM;AAEvC,UAAI,MAAM,KAAK,MAAM,EAAG;AAExB,WAAK,OAAO,QAAQ;AACpB,WAAK,OAAO,SAAS;AAErB,WAAK,IAAI,UAAU,KAAK,OAAO,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,EAAE;AAC3D,YAAM,YAAY,KAAK,IAAI,aAAa,GAAG,GAAG,IAAI,EAAE;AAEpD,WAAK,aAAa;AAClB,WAAK,UAAU,SAAS;AAAA,IAC1B;AA9EE,SAAK,QAAQ;AACb,SAAK,cAAc,MAAO,OAAO;AACjC,SAAK,gBAAgB,OAAO;AAE5B,QAAI,OAAO,oBAAoB,aAAa;AAC1C,WAAK,SAAS,IAAI,gBAAgB,GAAG,CAAC;AACtC,WAAK,MAAM,KAAK,OAAO,WAAW,IAAI;AAAA,IACxC,OAAO;AACL,WAAK,SAAS,SAAS,cAAc,QAAQ;AAC7C,WAAK,OAAO,MAAM,UAAU;AAC5B,WAAK,MAAM,KAAK,OAAO,WAAW,IAAI;AAAA,IACxC;AAAA,EACF;AAAA,EAEA,MAAM,SAA+C;AACnD,QAAI,KAAK,QAAS;AAClB,SAAK,UAAU;AACf,SAAK,UAAU;AACf,SAAK,QAAQ,sBAAsB,KAAK,IAAI;AAAA,EAC9C;AAAA,EAEA,OAAa;AACX,SAAK,UAAU;AACf,SAAK,UAAU;AACf,QAAI,KAAK,UAAU,MAAM;AACvB,2BAAqB,KAAK,KAAK;AAC/B,WAAK,QAAQ;AAAA,IACf;AAAA,EACF;AAAA,EAEA,UAAgB;AACd,SAAK,KAAK;AACV,QAAI,KAAK,kBAAkB,qBAAqB,KAAK,OAAO,YAAY;AACtE,WAAK,OAAO,WAAW,YAAY,KAAK,MAAM;AAAA,IAChD;AAAA,EACF;AAAA,EAEA,iBAAuB;AACrB,SAAK,aAAa;AAAA,EACpB;AAAA,EAEA,iBAAuB;AACrB,SAAK,aAAa;AAAA,EACpB;AAoCF;;;ACtFA,SAAS,qBAAqB,OAK5B;AACA,QAAM,eAAe,MAAM;AAC3B,QAAM,gBAAgB,MAAM;AAC5B,QAAM,aAAa,MAAM,cAAc;AACvC,QAAM,cAAc,MAAM,eAAe;AAEzC,QAAM,YAAY,iBAAiB,KAAK,EAAE;AAE1C,MAAI,cAAc,SAAS;AACzB,UAAM,QAAQ,KAAK;AAAA,MACjB,eAAe;AAAA,MACf,gBAAgB;AAAA,IAClB;AACA,UAAM,gBAAgB,aAAa;AACnC,UAAM,iBAAiB,cAAc;AACrC,WAAO;AAAA,MACL,UAAU,eAAe,iBAAiB;AAAA,MAC1C,UAAU,gBAAgB,kBAAkB;AAAA,MAC5C,OAAO;AAAA,MACP,QAAQ;AAAA,IACV;AAAA,EACF;AAEA,MAAI,cAAc,WAAW;AAC3B,UAAM,QAAQ,KAAK;AAAA,MACjB,eAAe;AAAA,MACf,gBAAgB;AAAA,IAClB;AACA,UAAM,gBAAgB,aAAa;AACnC,UAAM,iBAAiB,cAAc;AACrC,WAAO;AAAA,MACL,UAAU,eAAe,iBAAiB;AAAA,MAC1C,UAAU,gBAAgB,kBAAkB;AAAA,MAC5C,OAAO;AAAA,MACP,QAAQ;AAAA,IACV;AAAA,EACF;AAGA,SAAO,EAAE,SAAS,GAAG,SAAS,GAAG,OAAO,cAAc,QAAQ,cAAc;AAC9E;AAQO,IAAM,cAAN,MAAkB;AAAA,EAOvB,YAAY,OAAyB,QAAuB;AAL5D,SAAQ,YAAmC;AAC3C,SAAQ,gBAAmC;AAKzC,SAAK,QAAQ;AACb,SAAK,SAAS;AAEd,UAAM,SAAS,MAAM;AACrB,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI;AAAA,QACR;AAAA,MAEF;AAAA,IACF;AACA,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,QAAc;AACZ,QAAI,KAAK,OAAO,eAAe;AAC7B,WAAK,YAAY,KAAK,OAAO;AAC7B,WAAK,gBAAgB;AACrB;AAAA,IACF;AAEA,QAAI,KAAK,OAAO,qBAAqB;AACnC,WAAK,wBAAwB;AAAA,IAC/B;AAEA,QAAI,KAAK,OAAO,sBAAsB;AACpC,WAAK,kBAAkB;AAAA,IACzB;AAAA,EACF;AAAA,EAEA,iBAAiB,QAA0B;AACzC,QAAI,CAAC,KAAK,aAAa,KAAK,OAAO,cAAe;AAClD,SAAK,wBAAwB,MAAM;AAAA,EACrC;AAAA,EAEA,kBACE,cACA,YACM;AACN,QAAI,CAAC,KAAK,cAAe;AAEzB,QAAI,CAAC,gBAAgB,aAAa,SAAS,GAAG;AAC5C,WAAK,cAAc,MAAM,UAAU;AACnC;AAAA,IACF;AAEA,SAAK,cAAc,MAAM,UAAU;AAEnC,UAAM,UAAU,KAAK,cAAc,cAAc,SAAS;AAC1D,QAAI,CAAC,QAAS;AAKd,UAAM,UAAU,YAAY,KAAK;AACjC,UAAM,UAAU,YAAY,KAAK;AACjC,UAAM,WAAW,qBAAqB,KAAK,KAAK;AAChD,UAAM,SAAS,SAAS,QAAQ,KAAK,MAAM;AAC3C,UAAM,SAAS,SAAS,SAAS,KAAK,MAAM;AAE5C,UAAM,SAAS,aACZ;AAAA,MACC,CAAC,MACC,IAAI,EAAE,IAAI,WAAW,SAAS,SAAS,OAAO,KAAK,EAAE,IAAI,WAAW,SAAS,SAAS,OAAO;AAAA,IACjG,EACC,KAAK,GAAG;AAEX,YAAQ,aAAa,UAAU,MAAM;AAAA,EACvC;AAAA,EAEA,UAAgB;AACd,QAAI,KAAK,aAAa,CAAC,KAAK,OAAO,eAAe;AAChD,WAAK,UAAU,OAAO;AAAA,IACxB;AACA,QAAI,KAAK,eAAe;AACtB,WAAK,cAAc,OAAO;AAAA,IAC5B;AACA,SAAK,YAAY;AACjB,SAAK,gBAAgB;AAAA,EACvB;AAAA,EAEQ,0BAAgC;AACtC,SAAK,YAAY,SAAS,cAAc,KAAK;AAC7C,SAAK,UAAU,YAAY;AAG3B,UAAM,KAAK,KAAK,UAAU;AAC1B,UAAM,KAAK,KAAK,UAAU;AAC1B,UAAM,OAAO,KAAK,MAAO,KAAK,IAAI,IAAI,EAAE,IAAI,IAAK,CAAC;AAElD,WAAO,OAAO,KAAK,UAAU,OAAO;AAAA,MAClC,UAAU;AAAA,MACV,KAAK;AAAA,MACL,MAAM;AAAA,MACN,WAAW;AAAA,MACX,OAAO,GAAG,IAAI;AAAA,MACd,QAAQ,GAAG,IAAI;AAAA,MACf,QAAQ;AAAA,MACR,cAAc;AAAA,MACd,WAAW;AAAA,MACX,eAAe;AAAA,MACf,QAAQ;AAAA,IACV,CAAC;AAGD,UAAM,UAAU,CAAC,YAAY,aAAa,eAAe,cAAc;AACvE,eAAW,UAAU,SAAS;AAC5B,YAAM,SAAS,SAAS,cAAc,KAAK;AAC3C,aAAO,YAAY,uCAAuC,MAAM;AAEhE,YAAM,CAAC,UAAU,UAAU,IAAI,OAAO,MAAM,GAAG;AAE/C,aAAO,OAAO,OAAO,OAAO;AAAA,QAC1B,UAAU;AAAA,QACV,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,CAAC,QAAQ,GAAG;AAAA,QACZ,CAAC,UAAU,GAAG;AAAA,QACd,CAAC,UAAU,QAAQ,EAAE,GAAG;AAAA,QACxB,CAAC,UAAU,UAAU,EAAE,GAAG;AAAA,QAC1B,CAAC,UAAU,QAAQ,IAAI,UAAU,SAAS,GAAG;AAAA,MAC/C,CAAC;AAED,WAAK,UAAU,YAAY,MAAM;AAAA,IACnC;AAEA,SAAK,UAAU,YAAY,KAAK,SAAS;AAAA,EAC3C;AAAA,EAEQ,oBAA0B;AAChC,UAAM,MAAM,SAAS,gBAAgB,8BAA8B,KAAK;AACxE,QAAI,aAAa,SAAS,yBAAyB;AAEnD,WAAO,OAAO,IAAI,OAAO;AAAA,MACvB,UAAU;AAAA,MACV,KAAK;AAAA,MACL,MAAM;AAAA,MACN,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,eAAe;AAAA,MACf,QAAQ;AAAA,MACR,SAAS;AAAA,IACX,CAAC;AAED,UAAM,UAAU,SAAS;AAAA,MACvB;AAAA,MACA;AAAA,IACF;AACA,YAAQ,aAAa,QAAQ,MAAM;AACnC,YAAQ,aAAa,UAAU,SAAS;AACxC,YAAQ,aAAa,gBAAgB,GAAG;AACxC,YAAQ,aAAa,mBAAmB,OAAO;AAG/C,UAAM,UAAU,SAAS;AAAA,MACvB;AAAA,MACA;AAAA,IACF;AACA,YAAQ,aAAa,iBAAiB,gBAAgB;AACtD,YAAQ,aAAa,UAAU,SAAS;AACxC,YAAQ,aAAa,OAAO,MAAM;AAClC,YAAQ,aAAa,eAAe,YAAY;AAChD,YAAQ,YAAY,OAAO;AAE3B,QAAI,YAAY,OAAO;AACvB,SAAK,UAAU,YAAY,GAAG;AAC9B,SAAK,gBAAgB;AAAA,EACvB;AAAA,EAEQ,kBAAwB;AAC9B,QAAI,CAAC,KAAK,UAAW;AAErB,WAAO,OAAO,KAAK,UAAU,OAAO;AAAA,MAClC,UAAU;AAAA,MACV,KAAK;AAAA,MACL,MAAM;AAAA,MACN,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,eAAe;AAAA,MACf,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AAAA,EAEQ,wBAAwB,SAA2B;AACzD,QAAI,CAAC,KAAK,UAAW;AASrB,UAAM,KAAK,KAAK,UAAU;AAC1B,UAAM,KAAK,KAAK,UAAU;AAC1B,UAAM,OAAO,KAAK,MAAO,KAAK,IAAI,IAAI,EAAE,IAAI,IAAK,CAAC;AAIlD,WAAO,OAAO,KAAK,UAAU,OAAO;AAAA,MAClC,OAAO,GAAG,IAAI;AAAA,MACd,QAAQ,GAAG,IAAI;AAAA,IACjB,CAAC;AAAA,EACH;AACF;;;AC/QO,SAAS,2BAA2B,OAAqC;AAC9E,QAAM,aAAa,MAAM,cAAc,MAAM;AAC7C,QAAM,cAAc,MAAM,eAAe,MAAM;AAE/C,QAAM,mBAAmB,KAAK,IAAI,YAAY,WAAW;AACzD,QAAM,OAAO,KAAK,MAAO,mBAAmB,IAAK,CAAC;AAElD,SAAO;AAAA,IACL,GAAG,KAAK,OAAO,aAAa,QAAQ,CAAC;AAAA,IACrC,GAAG,KAAK,OAAO,cAAc,QAAQ,CAAC;AAAA,IACtC,OAAO;AAAA,IACP,QAAQ;AAAA,EACV;AACF;;;ACEA,IAAI,kBAAuC;AAEpC,SAAS,aAAa,KAAyB;AACpD,oBAAkB;AACpB;AAEA,SAAS,mBAAiC;AACxC,MAAI,iBAAiB;AACnB,WAAO;AAAA,EACT;AAKA,MAAI;AACF,WAAO,IAAI,IAAI,eAAe,YAAY,GAAG;AAAA,EAC/C,QAAQ;AACN,UAAM,IAAI;AAAA,MACR;AAAA,IAGF;AAAA,EACF;AACF;AAEO,IAAM,UAAN,MAAc;AAAA,EAYnB,YACE,OACA,UACA,UAA0B,CAAC,GAC3B;AAXF,SAAQ,iBAAwC;AAChD,SAAQ,SAAwB;AAChC,SAAQ,UAA8B;AACtC,SAAQ,SAAS;AACjB,SAAQ,SAAS;AACjB,SAAQ,YAAY;AAOlB,SAAK,QAAQ;AACb,SAAK,WAAW;AAChB,SAAK,UAAU;AAEf,SAAK,SAAS,IAAI,cAAc;AAAA,MAC9B,iBAAiB,QAAQ;AAAA,MACzB,kBAAkB,QAAQ;AAAA,IAC5B,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,QAAuB;AAC3B,QAAI,KAAK,WAAW;AAClB,YAAM,IAAI,MAAM,4BAA4B;AAAA,IAC9C;AAEA,QAAI,KAAK,UAAU,CAAC,KAAK,QAAQ;AAC/B;AAAA,IACF;AAEA,UAAM,KAAK,YAAY,IAAI;AAI3B,QACE,CAAC,KAAK,YACL,KAAK,QAAQ,uBACZ,KAAK,QAAQ,wBACb,KAAK,QAAQ,UACf;AACA,UAAI;AACF,aAAK,UAAU,IAAI,YAAY,KAAK,OAAO;AAAA,UACzC,qBAAqB,KAAK,QAAQ,uBAAuB;AAAA,UACzD,sBAAsB,KAAK,QAAQ,wBAAwB;AAAA,UAC3D,eAAe,KAAK,QAAQ;AAAA,QAC9B,CAAC;AACD,aAAK,QAAQ,MAAM;AAAA,MACrB,QAAQ;AAAA,MAER;AAAA,IACF;AAGA,UAAM,KAAK,OAAO,MAAM,KAAK,KAAK;AAClC,YAAQ;AAAA,MACN,oCAAoC,YAAY,IAAI,IAAI,IAAI,QAAQ,CAAC,CAAC;AAAA,IACxE;AAGA,QAAI,KAAK,SAAS;AAChB,WAAK,QAAQ,iBAAiB,KAAK,qBAAqB,CAAC;AAAA,IAC3D;AAGA,QAAI,CAAC,KAAK,QAAQ;AAChB,YAAM,KAAK,YAAY,IAAI;AAC3B,WAAK,SAAS,KAAK,aAAa;AAChC,cAAQ;AAAA,QACN,sCAAsC,YAAY,IAAI,IAAI,IAAI,QAAQ,CAAC,CAAC;AAAA,MAC1E;AAAA,IACF;AAGA,QAAI,CAAC,KAAK,gBAAgB;AACxB,WAAK,iBAAiB,IAAI,eAAe,KAAK,OAAO;AAAA,QACnD,mBAAmB,KAAK,QAAQ,qBAAqB;AAAA,QACrD,eAAe,MAAM,KAAK,qBAAqB;AAAA,MACjD,CAAC;AAAA,IACH;AAGA,SAAK,eAAe,MAAM,CAAC,cAAc;AACvC,WAAK,aAAa,SAAS;AAAA,IAC7B,CAAC;AAED,SAAK,SAAS;AACd,SAAK,SAAS;AAEd,YAAQ;AAAA,MACN,6BAA6B,YAAY,IAAI,IAAI,IAAI,QAAQ,CAAC,CAAC;AAAA,IACjE;AAAA,EACF;AAAA,EAEA,OAAa;AACX,SAAK,gBAAgB,KAAK;AAC1B,SAAK,OAAO,KAAK;AACjB,SAAK,MAAM,YAAY;AACvB,SAAK,SAAS;AACd,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,UAAgB;AACd,QAAI,KAAK,UAAW;AACpB,SAAK,KAAK;AACV,SAAK,gBAAgB,QAAQ;AAC7B,SAAK,iBAAiB;AACtB,SAAK,SAAS,QAAQ;AACtB,SAAK,UAAU;AACf,SAAK,QAAQ,UAAU;AACvB,SAAK,SAAS;AACd,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,MAAM,MAAM,wBAAwB,MAAwB;AAC1D,QAAI,CAAC,KAAK,OAAQ,QAAO;AAEzB,SAAK,gBAAgB,KAAK;AAC1B,SAAK,SAAS;AAEd,QAAI,uBAAuB;AACzB,WAAK,OAAO,KAAK;AACjB,WAAK,MAAM,YAAY;AAAA,IACzB;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,UAAU,sBAA6C;AAC3D,UAAM,YAAY,KAAK,UAAU,CAAC,KAAK;AACvC,QAAI,WAAW;AACb,WAAK,gBAAgB,KAAK;AAAA,IAC5B;AAEA,UAAM,KAAK,OAAO,UAAU,sBAAsB,KAAK,KAAK;AAE5D,QAAI,WAAW;AACb,WAAK,gBAAgB,MAAM,CAAC,cAAc;AACxC,aAAK,aAAa,SAAS;AAAA,MAC7B,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,MAAM,WAA6B;AACjC,WAAO,KAAK,OAAO,SAAS;AAAA,EAC9B;AAAA,EAEA,YAAqB;AACnB,WAAO,KAAK,OAAO,UAAU;AAAA,EAC/B;AAAA,EAEA,MAAM,cAA6B;AACjC,WAAO,KAAK,OAAO,YAAY;AAAA,EACjC;AAAA,EAEA,MAAM,cAA6B;AACjC,WAAO,KAAK,OAAO,YAAY;AAAA,EACjC;AAAA,EAEA,MAAM,eAA8B;AAClC,WAAO,KAAK,OAAO,aAAa;AAAA,EAClC;AAAA,EAEA,iBAAiB,MAA2B;AAC1C,QAAI,CAAC,KAAK,OAAQ;AAElB,UAAM,UAAkC,CAAC;AACzC,YAAQ,MAAM;AAAA,MACZ,KAAK;AACH,gBAAQ,YAAY;AACpB;AAAA,MACF,KAAK;AACH,gBAAQ,YAAY;AACpB;AAAA,MACF,KAAK;AACH,gBAAQ,YAAY;AACpB;AAAA,IACJ;AAEA,UAAM,MAAqB,EAAE,MAAM,aAAa,QAAQ;AACxD,SAAK,OAAO,YAAY,GAAG;AAAA,EAC7B;AAAA,EAEA,WAAoB;AAClB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,WAAoB;AAClB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,cAAuB;AACrB,WAAO,KAAK;AAAA,EACd;AAAA,EAEQ,uBAAmC;AACzC,QAAI,KAAK,QAAQ,qBAAqB;AACpC,aAAO,KAAK,QAAQ,oBAAoB,KAAK,KAAK;AAAA,IACpD;AACA,WAAO,2BAA2B,KAAK,KAAK;AAAA,EAC9C;AAAA,EAEQ,eAAuB;AAC7B,UAAM,YAAY,iBAAiB;AACnC,UAAM,SAAS,IAAI,OAAO,WAAW,EAAE,MAAM,SAAS,CAAC;AAGvD,QAAI,KAAK,QAAQ,gBAAgB;AAC/B,YAAM,MAAqB;AAAA,QACzB,MAAM;AAAA,QACN,SAAS,KAAK,QAAQ;AAAA,MACxB;AACA,aAAO,YAAY,GAAG;AAAA,IACxB;AAEA,WAAO,YAAY,CAAC,MAAoC;AACtD,WAAK,oBAAoB,EAAE,IAAI;AAAA,IACjC;AAEA,WAAO,UAAU,CAAC,QAAQ;AACxB,cAAQ,MAAM,4BAA4B,GAAG;AAC7C,WAAK,gBAAgB,eAAe;AAAA,IACtC;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,oBAAoB,UAAgC;AAC1D,SAAK,gBAAgB,eAAe;AAEpC,QAAI,SAAS,SAAS,SAAS;AAC7B;AAAA,IACF;AAEA,QAAI,SAAS,SAAS,SAAS;AAC7B,WAAK,QAAQ,gBAAgB,SAAS,OAAO;AAC7C;AAAA,IACF;AAEA,QAAI,SAAS,SAAS,UAAU;AAC9B,UAAI,SAAS,QAAQ,SAAS,GAAG;AAC/B,cAAM,SAAS,SAAS,QAAQ,CAAC;AACjC,aAAK,SAAS,MAAM;AAGpB,YAAI,KAAK,SAAS;AAChB,gBAAM,SAAS,KAAK,qBAAqB;AACzC,eAAK,QAAQ,iBAAiB,MAAM;AACpC,eAAK,QAAQ,kBAAkB,OAAO,cAAc,MAAM;AAAA,QAC5D;AAAA,MACF,OAAO;AACL,aAAK,QAAQ,gBAAgB,kBAAkB;AAG/C,YAAI,KAAK,SAAS;AAChB,eAAK,QAAQ,kBAAkB,IAAI;AAAA,QACrC;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,aAAa,WAA4B;AAC/C,QAAI,CAAC,KAAK,OAAQ;AAElB,UAAM,MAAqB,EAAE,MAAM,UAAU,UAAU;AACvD,SAAK,OAAO,YAAY,KAAK,CAAC,UAAU,KAAK,MAAM,CAAC;AAAA,EACtD;AACF;;;AC9TA,SAAS,oBAAwC;;;ACKjD,eAAsB,cACpB,QACA,YACA,QACoB;AACpB,QAAM,MAAM,MAAM,mBAAmB,MAAM;AAE3C,QAAM,KAAK,YAAY,KAAK;AAC5B,QAAM,KAAK,YAAY,KAAK;AAC5B,QAAM,KAAK,YAAY,SAAU,IAAI,QAAQ;AAC7C,QAAM,KAAK,YAAY,UAAW,IAAI,SAAS;AAE/C,MAAI;AACJ,MAAI;AAEJ,MAAI,QAAQ;AACV,iBAAa;AACb,WAAO,QAAQ;AACf,WAAO,SAAS;AAChB,UAAM,OAAO,WAAW,IAAI;AAAA,EAC9B,WAAW,OAAO,oBAAoB,aAAa;AACjD,iBAAa,IAAI,gBAAgB,IAAI,EAAE;AACvC,UAAM,WAAW,WAAW,IAAI;AAAA,EAClC,OAAO;AACL,iBAAa,SAAS,cAAc,QAAQ;AAC5C,eAAW,QAAQ;AACnB,eAAW,SAAS;AACpB,UAAM,WAAW,WAAW,IAAI;AAAA,EAClC;AAEA,MAAI,UAAU,KAA0B,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,EAAE;AACpE,SAAO,IAAI,aAAa,GAAG,GAAG,IAAI,EAAE;AACtC;AAEA,eAAe,mBACb,QAC+E;AAC/E,MAAI,kBAAkB,oBAAoB,kBAAkB,qBAAqB,kBAAkB,aAAa;AAC9G,WAAO;AAAA,EACT;AAEA,MAAI,OAAO,oBAAoB,eAAe,kBAAkB,iBAAiB;AAC/E,WAAO;AAAA,EACT;AAEA,MAAI,kBAAkB,QAAQ,kBAAkB,MAAM;AACpD,WAAO,0BAA0B,MAAM;AAAA,EACzC;AAGA,QAAM,MAAM,kBAAkB,MAAM,OAAO,OAAO;AAClD,QAAM,WAAW,MAAM,MAAM,GAAG;AAChC,QAAM,OAAO,MAAM,SAAS,KAAK;AACjC,SAAO,0BAA0B,IAAI;AACvC;AAEA,eAAe,0BAA0B,MAAkC;AACzE,MAAI,OAAO,sBAAsB,aAAa;AAC5C,WAAO,kBAAkB,IAAI;AAAA,EAC/B;AAEA,QAAM,MAAM,IAAI,gBAAgB,IAAI;AACpC,MAAI;AACF,UAAM,MAAM,IAAI,MAAM;AACtB,QAAI,MAAM;AACV,UAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,UAAI,SAAS,MAAM,QAAQ;AAC3B,UAAI,UAAU,MAAM,OAAO,IAAI,MAAM,sBAAsB,CAAC;AAAA,IAC9D,CAAC;AACD,WAAO;AAAA,EACT,UAAE;AACA,QAAI,gBAAgB,GAAG;AAAA,EACzB;AACF;;;AD1EA,IAAM,uBAAsC;AAAA,EAC1C,SAAS,CAAC,QAAQ;AAAA,EAClB,WAAW;AAAA,EACX,WAAW;AAAA,EACX,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,cAAc;AAAA,EACd,oBAAoB;AACtB;AAEA,SAAS,YAAY,UAKT;AACV,SAAO;AAAA,IACL,SAAS;AAAA,IACT,SAAS;AAAA,IACT,SAAS;AAAA,IACT,SAAS;AAAA,EACX;AACF;AAQA,SAAS,cAAc,QAAwC;AAC7D,SACE,kBAAkB,QAClB,kBAAkB,eAClB,kBAAkB,cACjB,OAAO,cAAc,eAAe,kBAAkB;AAE3D;AAKA,eAAsB,UACpB,QAMA,SAKqB;AACrB,QAAM,gBAA+B;AAAA,IACnC,GAAG;AAAA,IACH,GAAG,SAAS;AAAA,IACZ,SAAS,CAAC,QAAQ;AAAA,EACpB;AAEA,MAAI;AACJ,MAAI,cAAc,MAAM,GAAG;AACzB,YAAQ;AAAA,EACV,WAAW,OAAO,WAAW,YAAY,kBAAkB,KAAK;AAE9D,UAAM,MAAM,kBAAkB,MAAM,OAAO,OAAO;AAClD,UAAM,WAAW,MAAM,MAAM,GAAG;AAChC,YAAQ,MAAM,SAAS,YAAY;AAAA,EACrC,OAAO;AAEL,YAAQ,MAAM,cAAc,QAAQ,SAAS,YAAY,SAAS,MAAM;AAAA,EAC1E;AAEA,QAAM,UAAU,MAAM,aAAa,OAAO,aAAa;AACvD,QAAM,QAAQ,QAAQ,OAAO,CAAC,MAAM,EAAE,OAAO;AAE7C,MAAI,MAAM,WAAW,GAAG;AACtB,UAAM,IAAI,MAAM,+BAA+B;AAAA,EACjD;AAEA,QAAM,QAAQ,MAAM,CAAC;AACrB,SAAO;AAAA,IACL,MAAM,MAAM;AAAA,IACZ,cAAc,YAAY,MAAM,QAAQ;AAAA,EAC1C;AACF;;;AEnFA,SAAS,+BAA+B;AA6BxC,IAAM,YAAN,MAAgB;AAAA,EAGd,YACE,cACA,UACA,UAA0B,CAAC,GAC3B;AACA,SAAK,UAAU,IAAI,QAAQ,cAAc,UAAU,OAAO;AAAA,EAC5D;AAAA;AAAA,EAGA,MAAM,QAAuB;AAC3B,WAAO,KAAK,QAAQ,MAAM;AAAA,EAC5B;AAAA;AAAA,EAGA,OAAa;AACX,SAAK,QAAQ,KAAK;AAAA,EACpB;AAAA;AAAA,EAGA,UAAgB;AACd,SAAK,QAAQ,QAAQ;AAAA,EACvB;AAAA;AAAA,EAGA,MAAM,MAAM,uBAAmD;AAC7D,WAAO,KAAK,QAAQ,MAAM,qBAAqB;AAAA,EACjD;AAAA;AAAA,EAGA,MAAM,UAAU,sBAA4D;AAC1E,WAAO,KAAK,QAAQ,UAAU,oBAAoB;AAAA,EACpD;AAAA;AAAA,EAGA,MAAM,WAA6B;AACjC,WAAO,KAAK,QAAQ,SAAS;AAAA,EAC/B;AAAA;AAAA,EAGA,YAAqB;AACnB,WAAO,KAAK,QAAQ,UAAU;AAAA,EAChC;AAAA;AAAA,EAGA,MAAM,cAA6B;AACjC,WAAO,KAAK,QAAQ,YAAY;AAAA,EAClC;AAAA;AAAA,EAGA,MAAM,cAA6B;AACjC,WAAO,KAAK,QAAQ,YAAY;AAAA,EAClC;AAAA;AAAA,EAGA,MAAM,eAA8B;AAClC,WAAO,KAAK,QAAQ,aAAa;AAAA,EACnC;AAAA;AAAA,EAGA,iBAAiB,MAA2B;AAC1C,SAAK,QAAQ,iBAAiB,IAAI;AAAA,EACpC;AAAA;AAAA;AAAA,EAKA,OAAO,YAA8B;AACnC,WAAO,cAAc,UAAU;AAAA,EACjC;AAAA;AAAA,EAGA,OAAO,YAAY,eAA4C;AAC7D,WAAO,cAAc,YAAY,aAAa;AAAA,EAChD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,aAAa,UAAyB;AAEpC,UAAM,QAAQ,IAAI,kBAAkB,CAAC,KAAK,KAAK,KAAK,GAAG,CAAC;AACxD,UAAM,MAAM,IAAI,UAAU,OAAO,GAAG,CAAC;AACrC,QAAI;AACF,YAAM,UAAU,GAAG;AAAA,IACrB,QAAQ;AAAA,IAER;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,OAAO,cAAc,WAA4C;AAC/D,4BAAwB,SAAS;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,OAAO,aAAa,KAAyB;AAC3C,iBAAa,GAAG;AAAA,EAClB;AAAA;AAAA,EAGA,OAAO,UACL,QAYA,SAKqB;AACrB,WAAO,UAAU,QAAQ,OAAO;AAAA,EAClC;AACF;AAEA,IAAO,gBAAQ;","names":["t"]}
1
+ {"version":3,"sources":["../src/debug.ts","../src/camera.ts","../src/frame-extractor.ts","../src/overlay.ts","../src/scan-region.ts","../src/scanner.ts","../src/scan-image.ts","../src/utils.ts","../src/index.ts"],"sourcesContent":["let enabled = false;\n\nexport function setDebug(on: boolean): void {\n enabled = on;\n}\n\nexport function debug(...args: unknown[]): void {\n if (enabled) console.debug(...args);\n}\n","import { debug } from './debug.js';\nimport type { FacingMode, DeviceId, Camera } from './types.js';\n\nexport class CameraPermissionError extends Error {\n constructor(\n message = 'Camera access denied. Please grant camera permission and try again.',\n ) {\n super(message);\n this.name = 'CameraPermissionError';\n }\n}\n\nexport class CameraNotFoundError extends Error {\n constructor(\n message = 'No camera found. Please connect a camera and try again.',\n ) {\n super(message);\n this.name = 'CameraNotFoundError';\n }\n}\n\nconst CACHE_KEY_PREFIX = '@agicash/qr-scanner:camera:';\n\nfunction getCachedDeviceId(facingMode: string): string | null {\n try {\n return localStorage.getItem(`${CACHE_KEY_PREFIX}${facingMode}`);\n } catch {\n return null;\n }\n}\n\nfunction setCachedDeviceId(facingMode: string, deviceId: string): void {\n try {\n localStorage.setItem(`${CACHE_KEY_PREFIX}${facingMode}`, deviceId);\n } catch {\n // localStorage unavailable or full — ignore\n }\n}\n\nexport interface CameraConfig {\n preferredCamera?: FacingMode | DeviceId;\n cameraResolution?: {\n width?: MediaTrackConstraintSet['width'];\n height?: MediaTrackConstraintSet['height'];\n };\n}\n\nexport class CameraManager {\n private stream: MediaStream | null = null;\n private facingMode: FacingMode | DeviceId;\n private resolution: CameraConfig['cameraResolution'];\n\n constructor(config: CameraConfig = {}) {\n this.facingMode = config.preferredCamera ?? 'environment';\n this.resolution = config.cameraResolution;\n }\n\n async start(video: HTMLVideoElement): Promise<MediaStream> {\n if (this.stream) {\n return this.stream;\n }\n\n const t0 = performance.now();\n\n this.stream = await this.acquireStream();\n const t1 = performance.now();\n debug(`[QrScanner] acquireStream: ${(t1 - t0).toFixed(0)}ms`);\n\n // On some devices (e.g. Samsung S24 + Brave), facingMode: 'environment'\n // picks an ultrawide camera that lacks autofocus. Check and switch to a\n // better camera BEFORE showing on screen to avoid visible flicker.\n await this.ensureBestCamera();\n const t2 = performance.now();\n debug(`[QrScanner] ensureBestCamera: ${(t2 - t1).toFixed(0)}ms`);\n\n video.srcObject = this.stream;\n video.setAttribute('playsinline', 'true');\n await video.play();\n const t3 = performance.now();\n debug(`[QrScanner] video.play: ${(t3 - t2).toFixed(0)}ms`);\n debug(`[QrScanner] camera.start total: ${(t3 - t0).toFixed(0)}ms`);\n\n // Cache the final camera so subsequent starts skip ensureBestCamera\n if (this.facingMode === 'environment' || this.facingMode === 'user') {\n const finalDeviceId = this.getVideoTrack()?.getSettings().deviceId;\n if (finalDeviceId) {\n setCachedDeviceId(this.facingMode, finalDeviceId);\n }\n }\n\n return this.stream;\n }\n\n stop(): void {\n if (this.stream) {\n for (const track of this.stream.getTracks()) {\n track.stop();\n }\n this.stream = null;\n }\n }\n\n async setCamera(\n facingModeOrDeviceId: FacingMode | DeviceId,\n video: HTMLVideoElement,\n ): Promise<void> {\n this.stop();\n this.facingMode = facingModeOrDeviceId;\n await this.start(video);\n }\n\n getStream(): MediaStream | null {\n return this.stream;\n }\n\n async hasFlash(): Promise<boolean> {\n const track = this.getVideoTrack();\n if (!track) return false;\n\n try {\n const capabilities = track.getCapabilities() as MediaTrackCapabilities & {\n torch?: boolean;\n };\n return capabilities.torch === true;\n } catch {\n return false;\n }\n }\n\n isFlashOn(): boolean {\n const track = this.getVideoTrack();\n if (!track) return false;\n\n const settings = track.getSettings() as MediaTrackSettings & {\n torch?: boolean;\n };\n return settings.torch === true;\n }\n\n async toggleFlash(): Promise<void> {\n if (this.isFlashOn()) {\n await this.turnFlashOff();\n } else {\n await this.turnFlashOn();\n }\n }\n\n async turnFlashOn(): Promise<void> {\n await this.setTorch(true);\n }\n\n async turnFlashOff(): Promise<void> {\n await this.setTorch(false);\n }\n\n private async setTorch(on: boolean): Promise<void> {\n const track = this.getVideoTrack();\n if (!track) {\n throw new Error('No active camera stream');\n }\n\n try {\n await track.applyConstraints({\n advanced: [{ torch: on } as MediaTrackConstraintSet],\n });\n } catch {\n throw new Error('Flash/torch is not supported on this device');\n }\n }\n\n /**\n * If the current camera lacks continuous autofocus (e.g. an ultrawide sensor\n * picked by facingMode: 'environment'), find a better camera with the same\n * facing mode and replace this.stream. Called before assigning to the video\n * element so the user never sees the wrong camera.\n */\n private async ensureBestCamera(): Promise<void> {\n if (this.facingMode !== 'environment' && this.facingMode !== 'user') {\n debug('[QrScanner] ensureBestCamera: skipped (specific deviceId)');\n return;\n }\n\n const track = this.getVideoTrack();\n if (!track) return;\n\n try {\n const capabilities = track.getCapabilities() as MediaTrackCapabilities & {\n focusMode?: string[];\n };\n if (\n !capabilities.focusMode ||\n capabilities.focusMode.includes('continuous')\n ) {\n // focusMode not reported (e.g. Safari/iOS) or has autofocus — skip.\n // Only enter the candidate loop when the browser explicitly reports\n // focusMode without 'continuous' (e.g. S24 + Brave ultrawide).\n debug(\n `[QrScanner] ensureBestCamera: skipped (focusMode: ${JSON.stringify(capabilities.focusMode)})`,\n );\n return;\n }\n debug(\n `[QrScanner] ensureBestCamera: current camera lacks autofocus (focusMode: ${JSON.stringify(capabilities.focusMode)})`,\n );\n } catch {\n return;\n }\n\n // Current camera lacks continuous autofocus.\n // Enumerate devices while stream is active (ensures deviceIds are available).\n const currentDeviceId = track.getSettings().deviceId;\n\n let devices: MediaDeviceInfo[];\n try {\n devices = await navigator.mediaDevices.enumerateDevices();\n } catch {\n return;\n }\n if (!Array.isArray(devices)) return;\n\n const candidates = devices.filter(\n (d) => d.kind === 'videoinput' && d.deviceId !== currentDeviceId,\n );\n debug(\n `[QrScanner] ensureBestCamera: testing ${candidates.length} candidate camera(s)`,\n );\n if (candidates.length === 0) return;\n\n // Stop current stream — mobile devices only allow one active camera\n this.stop();\n\n for (const candidate of candidates) {\n const t = performance.now();\n let candidateStream: MediaStream;\n try {\n candidateStream = await navigator.mediaDevices.getUserMedia({\n video: {\n deviceId: { exact: candidate.deviceId },\n width: this.resolution?.width ?? { ideal: 1920 },\n height: this.resolution?.height ?? { ideal: 1080 },\n },\n audio: false,\n });\n debug(\n `[QrScanner] ensureBestCamera: candidate ${candidate.label || candidate.deviceId.slice(0, 8)}: getUserMedia ${(performance.now() - t).toFixed(0)}ms`,\n );\n } catch {\n debug(\n `[QrScanner] ensureBestCamera: candidate ${candidate.label || candidate.deviceId.slice(0, 8)}: getUserMedia failed ${(performance.now() - t).toFixed(0)}ms`,\n );\n continue;\n }\n\n const candidateTrack = candidateStream.getVideoTracks()[0];\n if (!candidateTrack) {\n for (const t of candidateStream.getTracks()) t.stop();\n continue;\n }\n\n // Must match the desired facing mode\n const candidateSettings =\n candidateTrack.getSettings() as MediaTrackSettings & {\n facingMode?: string;\n };\n if (\n candidateSettings.facingMode &&\n candidateSettings.facingMode !== this.facingMode\n ) {\n for (const t of candidateStream.getTracks()) t.stop();\n continue;\n }\n\n // Check if this camera supports continuous autofocus\n try {\n const candidateCaps =\n candidateTrack.getCapabilities() as MediaTrackCapabilities & {\n focusMode?: string[];\n };\n if (candidateCaps.focusMode?.includes('continuous')) {\n this.stream = candidateStream;\n return;\n }\n } catch {\n // Can't check capabilities, skip\n }\n\n for (const t of candidateStream.getTracks()) t.stop();\n }\n\n // No better camera found — re-open the original\n try {\n this.stream = await navigator.mediaDevices.getUserMedia({\n video: {\n deviceId: currentDeviceId ? { exact: currentDeviceId } : undefined,\n width: this.resolution?.width ?? { ideal: 1920 },\n height: this.resolution?.height ?? { ideal: 1080 },\n },\n audio: false,\n });\n } catch {\n try {\n this.stream = await navigator.mediaDevices.getUserMedia(\n this.buildConstraints(),\n );\n } catch {\n // Could not recover camera\n }\n }\n }\n\n private getVideoTrack(): MediaStreamTrack | null {\n if (!this.stream) return null;\n const tracks = this.stream.getVideoTracks();\n return tracks[0] ?? null;\n }\n\n /**\n * Try getUserMedia with progressively simpler constraints.\n *\n * Some browsers (e.g. Brave on Samsung Galaxy S24) throw NotReadableError\n * when facingMode and resolution constraints are combined. Falling back to\n * fewer constraints lets us still open the camera on those browsers.\n */\n private async acquireStream(): Promise<MediaStream> {\n const labels: string[] = [];\n const attempts: MediaStreamConstraints[] = [];\n\n // If we've previously found a good camera for this facingMode, try it first\n if (this.facingMode === 'environment' || this.facingMode === 'user') {\n const cachedId = getCachedDeviceId(this.facingMode);\n if (cachedId) {\n labels.push('cached deviceId');\n const video: MediaTrackConstraints = {\n deviceId: { exact: cachedId },\n };\n if (this.resolution?.width) video.width = this.resolution.width;\n else video.width = { ideal: 1920 };\n if (this.resolution?.height) video.height = this.resolution.height;\n else video.height = { ideal: 1080 };\n attempts.push({ video, audio: false });\n }\n }\n\n // Standard fallback chain\n labels.push('full constraints', 'no resolution', 'bare minimum');\n attempts.push(\n // facingMode/deviceId + resolution\n this.buildConstraints(),\n // facingMode/deviceId only, no resolution\n this.buildConstraints(false),\n // Bare minimum\n { video: true, audio: false },\n );\n\n let lastError: unknown;\n for (let i = 0; i < attempts.length; i++) {\n const t = performance.now();\n try {\n const stream = await navigator.mediaDevices.getUserMedia(attempts[i]);\n debug(\n `[QrScanner] getUserMedia(${labels[i]}): ${(performance.now() - t).toFixed(0)}ms ✓`,\n );\n return stream;\n } catch (err) {\n debug(\n `[QrScanner] getUserMedia(${labels[i]}): ${(performance.now() - t).toFixed(0)}ms ✗ ${err instanceof DOMException ? err.name : err}`,\n );\n if (err instanceof DOMException) {\n if (err.name === 'NotAllowedError') {\n throw new CameraPermissionError();\n }\n if (err.name === 'NotFoundError') {\n throw new CameraNotFoundError();\n }\n // NotReadableError or OverconstrainedError — try next fallback\n lastError = err;\n continue;\n }\n throw err;\n }\n }\n\n throw lastError;\n }\n\n private buildConstraints(includeResolution = true): MediaStreamConstraints {\n const video: MediaTrackConstraints = {};\n\n if (includeResolution) {\n video.width = this.resolution?.width ?? { ideal: 1920 };\n video.height = this.resolution?.height ?? { ideal: 1080 };\n }\n\n if (this.facingMode === 'environment' || this.facingMode === 'user') {\n video.facingMode = this.facingMode;\n } else {\n video.deviceId = { exact: this.facingMode };\n }\n\n return { video, audio: false };\n }\n\n static async hasCamera(): Promise<boolean> {\n try {\n const devices = await navigator.mediaDevices.enumerateDevices();\n return devices.some((d) => d.kind === 'videoinput');\n } catch {\n return false;\n }\n }\n\n static async listCameras(requestLabels = false): Promise<Camera[]> {\n if (requestLabels) {\n // Requesting labels requires a temporary stream to trigger the permission prompt\n try {\n const stream = await navigator.mediaDevices.getUserMedia({\n video: true,\n });\n for (const track of stream.getTracks()) {\n track.stop();\n }\n } catch {\n // Permission denied — fall through with empty labels\n }\n }\n\n const devices = await navigator.mediaDevices.enumerateDevices();\n return devices\n .filter((d) => d.kind === 'videoinput')\n .map((d) => ({\n id: d.deviceId,\n label: d.label || `Camera ${d.deviceId.slice(0, 8)}`,\n }));\n }\n}\n","import type { ScanRegion } from './types.js';\n\nexport interface FrameExtractorConfig {\n maxScansPerSecond: number;\n getScanRegion: () => ScanRegion;\n}\n\nexport class FrameExtractor {\n private video: HTMLVideoElement;\n private canvas: HTMLCanvasElement | OffscreenCanvas;\n private ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D;\n private rafId: number | null = null;\n private running = false;\n private workerBusy = false;\n private lastScanTime = -Infinity;\n private minInterval: number;\n private getScanRegion: () => ScanRegion;\n private onFrame: ((imageData: ImageData) => void) | null = null;\n\n constructor(video: HTMLVideoElement, config: FrameExtractorConfig) {\n this.video = video;\n this.minInterval = 1000 / config.maxScansPerSecond;\n this.getScanRegion = config.getScanRegion;\n\n if (typeof OffscreenCanvas !== 'undefined') {\n this.canvas = new OffscreenCanvas(1, 1);\n this.ctx = this.canvas.getContext('2d')! as OffscreenCanvasRenderingContext2D;\n } else {\n this.canvas = document.createElement('canvas');\n this.canvas.style.display = 'none';\n this.ctx = this.canvas.getContext('2d')!;\n }\n }\n\n start(onFrame: (imageData: ImageData) => void): void {\n if (this.running) return;\n this.running = true;\n this.onFrame = onFrame;\n this.rafId = requestAnimationFrame(this.tick);\n }\n\n stop(): void {\n this.running = false;\n this.onFrame = null;\n if (this.rafId !== null) {\n cancelAnimationFrame(this.rafId);\n this.rafId = null;\n }\n }\n\n destroy(): void {\n this.stop();\n if (this.canvas instanceof HTMLCanvasElement && this.canvas.parentNode) {\n this.canvas.parentNode.removeChild(this.canvas);\n }\n }\n\n markWorkerIdle(): void {\n this.workerBusy = false;\n }\n\n markWorkerBusy(): void {\n this.workerBusy = true;\n }\n\n private tick = (): void => {\n if (!this.running) return;\n\n this.rafId = requestAnimationFrame(this.tick);\n\n // Skip if worker is still processing previous frame\n if (this.workerBusy) return;\n\n // Rate limiting\n const now = performance.now();\n if (now - this.lastScanTime < this.minInterval) return;\n\n // Skip if video isn't ready\n if (this.video.readyState < 2) return;\n\n this.lastScanTime = now;\n\n const region = this.getScanRegion();\n const sx = region.x ?? 0;\n const sy = region.y ?? 0;\n const sw = region.width ?? this.video.videoWidth;\n const sh = region.height ?? this.video.videoHeight;\n\n if (sw <= 0 || sh <= 0) return;\n\n this.canvas.width = sw;\n this.canvas.height = sh;\n\n this.ctx.drawImage(this.video, sx, sy, sw, sh, 0, 0, sw, sh);\n const imageData = this.ctx.getImageData(0, 0, sw, sh);\n\n this.workerBusy = true;\n this.onFrame?.(imageData);\n };\n}\n","import type { ScanRegion, Point } from './types.js';\n\n/**\n * Compute the actual rendered position and size of the video content\n * within the element, accounting for object-fit: cover.\n *\n * With object-fit: cover the video is scaled up to fill the element and\n * cropped. The rendered content is larger than the element, centered,\n * with negative offsets for the cropped portions.\n *\n * When object-fit is the default (fill), the rendered size equals the\n * element size and offsets are zero — so this is backwards-compatible.\n */\nfunction getRenderedVideoRect(video: HTMLVideoElement): {\n offsetX: number;\n offsetY: number;\n width: number;\n height: number;\n} {\n const elementWidth = video.clientWidth;\n const elementHeight = video.clientHeight;\n const videoWidth = video.videoWidth || 1;\n const videoHeight = video.videoHeight || 1;\n\n const objectFit = getComputedStyle(video).objectFit;\n\n if (objectFit === 'cover') {\n const scale = Math.max(\n elementWidth / videoWidth,\n elementHeight / videoHeight,\n );\n const renderedWidth = videoWidth * scale;\n const renderedHeight = videoHeight * scale;\n return {\n offsetX: (elementWidth - renderedWidth) / 2,\n offsetY: (elementHeight - renderedHeight) / 2,\n width: renderedWidth,\n height: renderedHeight,\n };\n }\n\n if (objectFit === 'contain') {\n const scale = Math.min(\n elementWidth / videoWidth,\n elementHeight / videoHeight,\n );\n const renderedWidth = videoWidth * scale;\n const renderedHeight = videoHeight * scale;\n return {\n offsetX: (elementWidth - renderedWidth) / 2,\n offsetY: (elementHeight - renderedHeight) / 2,\n width: renderedWidth,\n height: renderedHeight,\n };\n }\n\n // Default (fill / none / scale-down with no scaling needed): element dimensions\n return { offsetX: 0, offsetY: 0, width: elementWidth, height: elementHeight };\n}\n\nexport interface OverlayConfig {\n highlightScanRegion: boolean;\n highlightCodeOutline: boolean;\n customOverlay?: HTMLDivElement;\n}\n\nexport class ScanOverlay {\n private container: HTMLElement;\n private overlayEl: HTMLDivElement | null = null;\n private codeOutlineEl: SVGElement | null = null;\n private config: OverlayConfig;\n private video: HTMLVideoElement;\n\n constructor(video: HTMLVideoElement, config: OverlayConfig) {\n this.video = video;\n this.config = config;\n\n const parent = video.parentElement;\n if (!parent) {\n throw new Error(\n 'QrScanner: video element must have a parent element. ' +\n 'The parent should have position: relative.',\n );\n }\n this.container = parent;\n }\n\n setup(): void {\n if (this.config.customOverlay) {\n this.overlayEl = this.config.customOverlay;\n this.positionOverlay();\n return;\n }\n\n if (this.config.highlightScanRegion) {\n this.createScanRegionOverlay();\n }\n\n if (this.config.highlightCodeOutline) {\n this.createCodeOutline();\n }\n }\n\n updateScanRegion(region: ScanRegion): void {\n if (!this.overlayEl || this.config.customOverlay) return;\n this.positionOverlayToRegion(region);\n }\n\n updateCodeOutline(\n cornerPoints: Point[] | null,\n scanRegion?: ScanRegion,\n ): void {\n if (!this.codeOutlineEl) return;\n\n if (!cornerPoints || cornerPoints.length < 4) {\n this.codeOutlineEl.style.display = 'none';\n return;\n }\n\n this.codeOutlineEl.style.display = 'block';\n\n const polygon = this.codeOutlineEl.querySelector('polygon');\n if (!polygon) return;\n\n // Corner points are relative to the cropped scan region.\n // Add the scan region offset to get full video coordinates,\n // then scale to display coordinates (accounting for object-fit).\n const regionX = scanRegion?.x ?? 0;\n const regionY = scanRegion?.y ?? 0;\n const rendered = getRenderedVideoRect(this.video);\n const scaleX = rendered.width / this.video.videoWidth;\n const scaleY = rendered.height / this.video.videoHeight;\n\n const points = cornerPoints\n .map(\n (p) =>\n `${(p.x + regionX) * scaleX + rendered.offsetX},${(p.y + regionY) * scaleY + rendered.offsetY}`,\n )\n .join(' ');\n\n polygon.setAttribute('points', points);\n }\n\n destroy(): void {\n if (this.overlayEl && !this.config.customOverlay) {\n this.overlayEl.remove();\n }\n if (this.codeOutlineEl) {\n this.codeOutlineEl.remove();\n }\n this.overlayEl = null;\n this.codeOutlineEl = null;\n }\n\n private createScanRegionOverlay(): void {\n this.overlayEl = document.createElement('div');\n this.overlayEl.className = 'qr-scanner-region';\n\n // Overlay size: 3/4 of the smaller container dimension, centered.\n const cw = this.container.clientWidth;\n const ch = this.container.clientHeight;\n const size = Math.round((Math.min(cw, ch) * 3) / 4);\n\n Object.assign(this.overlayEl.style, {\n position: 'absolute',\n top: '50%',\n left: '50%',\n transform: 'translate(-50%, -50%)',\n width: `${size}px`,\n height: `${size}px`,\n border: '2px solid rgba(255, 255, 255, 0.5)',\n borderRadius: '8px',\n boxShadow: '0 0 0 9999px rgba(0, 0, 0, 0.5)',\n pointerEvents: 'none',\n zIndex: '10',\n });\n\n // Corner markers\n const corners = ['top-left', 'top-right', 'bottom-left', 'bottom-right'];\n for (const corner of corners) {\n const marker = document.createElement('div');\n marker.className = `qr-scanner-corner qr-scanner-corner-${corner}`;\n\n const [vertical, horizontal] = corner.split('-');\n\n Object.assign(marker.style, {\n position: 'absolute',\n width: '24px',\n height: '24px',\n [vertical]: '-2px',\n [horizontal]: '-2px',\n [`border-${vertical}`]: '3px solid white',\n [`border-${horizontal}`]: '3px solid white',\n [`border-${vertical}-${horizontal}-radius`]: '8px',\n });\n\n this.overlayEl.appendChild(marker);\n }\n\n this.container.appendChild(this.overlayEl);\n }\n\n private createCodeOutline(): void {\n const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');\n svg.setAttribute('class', 'qr-scanner-code-outline');\n\n Object.assign(svg.style, {\n position: 'absolute',\n top: '0',\n left: '0',\n width: '100%',\n height: '100%',\n pointerEvents: 'none',\n zIndex: '11',\n display: 'none',\n });\n\n const polygon = document.createElementNS(\n 'http://www.w3.org/2000/svg',\n 'polygon',\n );\n polygon.setAttribute('fill', 'none');\n polygon.setAttribute('stroke', '#00ff00');\n polygon.setAttribute('stroke-width', '3');\n polygon.setAttribute('stroke-linejoin', 'round');\n\n // Animated stroke\n const animate = document.createElementNS(\n 'http://www.w3.org/2000/svg',\n 'animate',\n );\n animate.setAttribute('attributeName', 'stroke-opacity');\n animate.setAttribute('values', '1;0.5;1');\n animate.setAttribute('dur', '1.5s');\n animate.setAttribute('repeatCount', 'indefinite');\n polygon.appendChild(animate);\n\n svg.appendChild(polygon);\n this.container.appendChild(svg);\n this.codeOutlineEl = svg;\n }\n\n private positionOverlay(): void {\n if (!this.overlayEl) return;\n\n Object.assign(this.overlayEl.style, {\n position: 'absolute',\n top: '0',\n left: '0',\n width: '100%',\n height: '100%',\n pointerEvents: 'none',\n zIndex: '10',\n });\n }\n\n private positionOverlayToRegion(_region: ScanRegion): void {\n if (!this.overlayEl) return;\n\n // Keep the overlay at a container-relative centered size rather than\n // mapping scan region coordinates to display coordinates. With\n // object-fit: cover on a portrait phone with a landscape camera, the\n // mapped region extends beyond the container — fine once the camera is\n // visible but causes a jarring jump from the initial placeholder.\n // The overlay is a visual guide; the actual scan area (in the frame\n // extractor) is unaffected and may be larger than what's shown.\n const cw = this.container.clientWidth;\n const ch = this.container.clientHeight;\n const size = Math.round((Math.min(cw, ch) * 3) / 4);\n\n // Only update size — initial CSS centering (top: 50%, left: 50%,\n // transform: translate(-50%, -50%)) persists and handles re-centering.\n Object.assign(this.overlayEl.style, {\n width: `${size}px`,\n height: `${size}px`,\n });\n }\n}\n","import type { ScanRegion } from './types.js';\n\n/**\n * Calculate the default scan region: a centered square covering\n * 2/3 of the smaller video dimension.\n */\nexport function calculateDefaultScanRegion(video: HTMLVideoElement): ScanRegion {\n const videoWidth = video.videoWidth || video.width;\n const videoHeight = video.videoHeight || video.height;\n\n const smallerDimension = Math.min(videoWidth, videoHeight);\n const size = Math.round((smallerDimension * 2) / 3);\n\n return {\n x: Math.round((videoWidth - size) / 2),\n y: Math.round((videoHeight - size) / 2),\n width: size,\n height: size,\n };\n}\n","import { CameraManager } from './camera.js';\nimport { debug } from './debug.js';\nimport { FrameExtractor } from './frame-extractor.js';\nimport { ScanOverlay } from './overlay.js';\nimport { calculateDefaultScanRegion } from './scan-region.js';\nimport type {\n ScannerOptions,\n ScanResult,\n ScanRegion,\n InversionMode,\n WorkerRequest,\n WorkerResponse,\n} from './types.js';\nimport type { ReaderOptions } from 'zxing-wasm/reader';\n\ntype OnDecodeCallback = (result: ScanResult) => void;\n\n/**\n * Custom worker URL override. When set, this URL is used instead of the\n * default bundler-resolved worker. Useful for CJS consumers or non-standard\n * bundler setups.\n */\nlet customWorkerUrl: string | URL | null = null;\n\nexport function setWorkerUrl(url: string | URL): void {\n customWorkerUrl = url;\n}\n\nfunction resolveWorkerUrl(): string | URL {\n if (customWorkerUrl) {\n return customWorkerUrl;\n }\n // Standard pattern: modern bundlers (Vite, webpack 5, Parcel, esbuild)\n // resolve `new URL('./file', import.meta.url)` at build time,\n // copying worker.js to the output directory and returning the correct URL.\n // Falls back gracefully for CJS builds where import.meta is unavailable.\n try {\n return new URL('./worker.js', import.meta.url);\n } catch {\n throw new Error(\n '@agicash/qr-scanner: Could not resolve worker URL. ' +\n 'Call QrScanner.setWorkerUrl() with the path to the worker script before creating a scanner. ' +\n 'Example: QrScanner.setWorkerUrl(\"/path/to/@agicash/qr-scanner/dist/worker.js\")',\n );\n }\n}\n\nexport class Scanner {\n private video: HTMLVideoElement;\n private onDecode: OnDecodeCallback;\n private options: ScannerOptions;\n private camera: CameraManager;\n private frameExtractor: FrameExtractor | null = null;\n private worker: Worker | null = null;\n private overlay: ScanOverlay | null = null;\n private active = false;\n private paused = false;\n private destroyed = false;\n\n constructor(\n video: HTMLVideoElement,\n onDecode: OnDecodeCallback,\n options: ScannerOptions = {},\n ) {\n this.video = video;\n this.onDecode = onDecode;\n this.options = options;\n\n this.camera = new CameraManager({\n preferredCamera: options.preferredCamera,\n cameraResolution: options.cameraResolution,\n });\n }\n\n async start(): Promise<void> {\n if (this.destroyed) {\n throw new Error('Scanner has been destroyed');\n }\n\n if (this.active && !this.paused) {\n return; // Already running\n }\n\n const t0 = performance.now();\n\n // Show overlay immediately (CSS-centered placeholder) so the UI looks\n // ready while the camera is still loading.\n if (\n !this.overlay &&\n (this.options.highlightScanRegion ||\n this.options.highlightCodeOutline ||\n this.options.overlay)\n ) {\n try {\n this.overlay = new ScanOverlay(this.video, {\n highlightScanRegion: this.options.highlightScanRegion ?? false,\n highlightCodeOutline: this.options.highlightCodeOutline ?? false,\n customOverlay: this.options.overlay,\n });\n this.overlay.setup();\n } catch {\n // Overlay setup failed (e.g., no parent element) — continue without overlay\n }\n }\n\n // Start camera\n await this.camera.start(this.video);\n debug(\n `[QrScanner] start: camera ready ${(performance.now() - t0).toFixed(0)}ms`,\n );\n\n // Now that video dimensions are known, position overlay exactly\n if (this.overlay) {\n this.overlay.updateScanRegion(this.getCurrentScanRegion());\n }\n\n // Create worker if needed\n if (!this.worker) {\n const tw = performance.now();\n this.worker = this.createWorker();\n debug(\n `[QrScanner] start: worker created ${(performance.now() - tw).toFixed(0)}ms`,\n );\n }\n\n // Create frame extractor if needed\n if (!this.frameExtractor) {\n this.frameExtractor = new FrameExtractor(this.video, {\n maxScansPerSecond: this.options.maxScansPerSecond ?? 15,\n getScanRegion: () => this.getCurrentScanRegion(),\n });\n }\n\n // Start frame extraction loop\n this.frameExtractor.start((imageData) => {\n this.sendToWorker(imageData);\n });\n\n this.active = true;\n this.paused = false;\n\n debug(`[QrScanner] start: total ${(performance.now() - t0).toFixed(0)}ms`);\n }\n\n stop(): void {\n this.frameExtractor?.stop();\n this.camera.stop();\n this.video.srcObject = null;\n this.active = false;\n this.paused = false;\n }\n\n destroy(): void {\n if (this.destroyed) return;\n this.stop();\n this.frameExtractor?.destroy();\n this.frameExtractor = null;\n this.overlay?.destroy();\n this.overlay = null;\n this.worker?.terminate();\n this.worker = null;\n this.destroyed = true;\n }\n\n async pause(stopStreamImmediately = true): Promise<boolean> {\n if (!this.active) return false;\n\n this.frameExtractor?.stop();\n this.paused = true;\n\n if (stopStreamImmediately) {\n this.camera.stop();\n this.video.srcObject = null;\n }\n\n return true;\n }\n\n async setCamera(facingModeOrDeviceId: string): Promise<void> {\n const wasActive = this.active && !this.paused;\n if (wasActive) {\n this.frameExtractor?.stop();\n }\n\n await this.camera.setCamera(facingModeOrDeviceId, this.video);\n\n if (wasActive) {\n this.frameExtractor?.start((imageData) => {\n this.sendToWorker(imageData);\n });\n }\n }\n\n async hasFlash(): Promise<boolean> {\n return this.camera.hasFlash();\n }\n\n isFlashOn(): boolean {\n return this.camera.isFlashOn();\n }\n\n async toggleFlash(): Promise<void> {\n return this.camera.toggleFlash();\n }\n\n async turnFlashOn(): Promise<void> {\n return this.camera.turnFlashOn();\n }\n\n async turnFlashOff(): Promise<void> {\n return this.camera.turnFlashOff();\n }\n\n setInversionMode(mode: InversionMode): void {\n if (!this.worker) return;\n\n const options: Partial<ReaderOptions> = {};\n switch (mode) {\n case 'original':\n options.tryInvert = false;\n break;\n case 'invert':\n options.tryInvert = true;\n break;\n case 'both':\n options.tryInvert = true;\n break;\n }\n\n const msg: WorkerRequest = { type: 'configure', options };\n this.worker.postMessage(msg);\n }\n\n isActive(): boolean {\n return this.active;\n }\n\n isPaused(): boolean {\n return this.paused;\n }\n\n isDestroyed(): boolean {\n return this.destroyed;\n }\n\n private getCurrentScanRegion(): ScanRegion {\n if (this.options.calculateScanRegion) {\n return this.options.calculateScanRegion(this.video);\n }\n return calculateDefaultScanRegion(this.video);\n }\n\n private createWorker(): Worker {\n const workerUrl = resolveWorkerUrl();\n const worker = new Worker(workerUrl, { type: 'module' });\n\n // Configure with custom decoder options\n if (this.options.decoderOptions) {\n const msg: WorkerRequest = {\n type: 'configure',\n options: this.options.decoderOptions,\n };\n worker.postMessage(msg);\n }\n\n worker.onmessage = (e: MessageEvent<WorkerResponse>) => {\n this.handleWorkerMessage(e.data);\n };\n\n worker.onerror = (err) => {\n console.error('QR Scanner worker error:', err);\n this.frameExtractor?.markWorkerIdle();\n };\n\n return worker;\n }\n\n private handleWorkerMessage(response: WorkerResponse): void {\n this.frameExtractor?.markWorkerIdle();\n\n if (response.type === 'ready') {\n return;\n }\n\n if (response.type === 'error') {\n this.options.onDecodeError?.(response.message);\n return;\n }\n\n if (response.type === 'result') {\n if (response.results.length > 0) {\n const result = response.results[0];\n this.onDecode(result);\n\n // Update overlay\n if (this.overlay) {\n const region = this.getCurrentScanRegion();\n this.overlay.updateScanRegion(region);\n this.overlay.updateCodeOutline(result.cornerPoints, region);\n }\n } else {\n this.options.onDecodeError?.('No QR code found');\n\n // Hide code outline when no QR found\n if (this.overlay) {\n this.overlay.updateCodeOutline(null);\n }\n }\n }\n }\n\n private sendToWorker(imageData: ImageData): void {\n if (!this.worker) return;\n\n const msg: WorkerRequest = { type: 'decode', imageData };\n this.worker.postMessage(msg, [imageData.data.buffer]);\n }\n}\n","import { readBarcodes, type ReaderOptions } from 'zxing-wasm/reader';\nimport type { ScanResult, ScanRegion, Point } from './types.js';\nimport { loadImageData } from './utils.js';\n\nconst defaultReaderOptions: ReaderOptions = {\n formats: ['QRCode'],\n tryHarder: true,\n tryInvert: true,\n tryRotate: true,\n tryDenoise: false,\n tryDownscale: true,\n maxNumberOfSymbols: 1,\n};\n\nfunction mapPosition(position: {\n topLeft: Point;\n topRight: Point;\n bottomLeft: Point;\n bottomRight: Point;\n}): Point[] {\n return [\n position.topLeft,\n position.topRight,\n position.bottomRight,\n position.bottomLeft,\n ];\n}\n\n/** Input types that zxing-wasm can handle directly (no canvas needed). */\ntype DirectInput = Blob | ArrayBuffer | Uint8Array | ImageData;\n\n/** Input types that need canvas-based pixel extraction. */\ntype CanvasInput = HTMLImageElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap;\n\nfunction isDirectInput(source: unknown): source is DirectInput {\n return (\n source instanceof Blob ||\n source instanceof ArrayBuffer ||\n source instanceof Uint8Array ||\n (typeof ImageData !== 'undefined' && source instanceof ImageData)\n );\n}\n\n/**\n * Scan a single image for QR codes. Does not require a camera or video stream.\n */\nexport async function scanImage(\n source:\n | CanvasInput\n | DirectInput\n | File\n | URL\n | string,\n options?: {\n scanRegion?: ScanRegion | null;\n canvas?: HTMLCanvasElement | null;\n decoderOptions?: Partial<ReaderOptions>;\n },\n): Promise<ScanResult> {\n const readerOptions: ReaderOptions = {\n ...defaultReaderOptions,\n ...options?.decoderOptions,\n formats: ['QRCode'],\n };\n\n let input: DirectInput;\n if (isDirectInput(source)) {\n input = source;\n } else if (typeof source === 'string' || source instanceof URL) {\n // URL string - fetch and pass as ArrayBuffer\n const url = source instanceof URL ? source.href : source;\n const response = await fetch(url);\n input = await response.arrayBuffer();\n } else {\n // Canvas-based sources - extract ImageData\n input = await loadImageData(source, options?.scanRegion, options?.canvas);\n }\n\n const results = await readBarcodes(input, readerOptions);\n const valid = results.filter((r) => r.isValid);\n\n if (valid.length === 0) {\n throw new Error('No QR code found in the image');\n }\n\n const first = valid[0];\n return {\n data: first.text,\n cornerPoints: mapPosition(first.position),\n };\n}\n","import type { ScanRegion } from './types.js';\n\n/**\n * Load an image source into an ImageData object, optionally cropping to a scan region.\n */\nexport async function loadImageData(\n source: HTMLImageElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap | File | Blob | URL | string,\n scanRegion?: ScanRegion | null,\n canvas?: HTMLCanvasElement | null,\n): Promise<ImageData> {\n const img = await resolveImageSource(source);\n\n const sx = scanRegion?.x ?? 0;\n const sy = scanRegion?.y ?? 0;\n const sw = scanRegion?.width ?? (img.width - sx);\n const sh = scanRegion?.height ?? (img.height - sy);\n\n let drawCanvas: HTMLCanvasElement | OffscreenCanvas;\n let ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D;\n\n if (canvas) {\n drawCanvas = canvas;\n canvas.width = sw;\n canvas.height = sh;\n ctx = canvas.getContext('2d')!;\n } else if (typeof OffscreenCanvas !== 'undefined') {\n drawCanvas = new OffscreenCanvas(sw, sh);\n ctx = drawCanvas.getContext('2d')! as OffscreenCanvasRenderingContext2D;\n } else {\n drawCanvas = document.createElement('canvas');\n drawCanvas.width = sw;\n drawCanvas.height = sh;\n ctx = drawCanvas.getContext('2d')!;\n }\n\n ctx.drawImage(img as CanvasImageSource, sx, sy, sw, sh, 0, 0, sw, sh);\n return ctx.getImageData(0, 0, sw, sh);\n}\n\nasync function resolveImageSource(\n source: HTMLImageElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap | File | Blob | URL | string,\n): Promise<HTMLImageElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap> {\n if (source instanceof HTMLImageElement || source instanceof HTMLCanvasElement || source instanceof ImageBitmap) {\n return source;\n }\n\n if (typeof OffscreenCanvas !== 'undefined' && source instanceof OffscreenCanvas) {\n return source;\n }\n\n if (source instanceof File || source instanceof Blob) {\n return createImageBitmapFromBlob(source);\n }\n\n // URL or string\n const url = source instanceof URL ? source.href : source as string;\n const response = await fetch(url);\n const blob = await response.blob();\n return createImageBitmapFromBlob(blob);\n}\n\nasync function createImageBitmapFromBlob(blob: Blob): Promise<ImageBitmap> {\n if (typeof createImageBitmap !== 'undefined') {\n return createImageBitmap(blob);\n }\n // Fallback for environments without createImageBitmap\n const url = URL.createObjectURL(blob);\n try {\n const img = new Image();\n img.src = url;\n await new Promise<void>((resolve, reject) => {\n img.onload = () => resolve();\n img.onerror = () => reject(new Error('Failed to load image'));\n });\n return img as unknown as ImageBitmap;\n } finally {\n URL.revokeObjectURL(url);\n }\n}\n","import { Scanner, setWorkerUrl } from './scanner.js';\nimport {\n CameraManager,\n CameraNotFoundError,\n CameraPermissionError,\n} from './camera.js';\nimport { setDebug } from './debug.js';\nimport { scanImage } from './scan-image.js';\nimport { setZXingModuleOverrides } from 'zxing-wasm/reader';\nimport type {\n ScanResult,\n ScanRegion,\n ScannerOptions,\n Camera,\n Point,\n FacingMode,\n DeviceId,\n InversionMode,\n} from './types.js';\nimport type { ReaderOptions } from 'zxing-wasm/reader';\n\nexport { CameraNotFoundError, CameraPermissionError };\n\nexport type {\n ScanResult,\n ScanRegion,\n ScannerOptions,\n Camera,\n Point,\n FacingMode,\n DeviceId,\n InversionMode,\n};\n\n/**\n * High-performance QR code scanner for the web, powered by ZXing-C++ WebAssembly.\n */\nclass QrScanner {\n private scanner: Scanner;\n\n constructor(\n videoElement: HTMLVideoElement,\n onDecode: (result: ScanResult) => void,\n options: ScannerOptions = {},\n ) {\n this.scanner = new Scanner(videoElement, onDecode, options);\n }\n\n /** Start camera and begin scanning. Resolves when camera is ready. */\n async start(): Promise<void> {\n return this.scanner.start();\n }\n\n /** Stop scanning and release the camera stream. */\n stop(): void {\n this.scanner.stop();\n }\n\n /** Stop scanning, release camera, terminate worker, clean up DOM. */\n destroy(): void {\n this.scanner.destroy();\n }\n\n /** Pause scanning. If stopStreamImmediately is false, camera stays on. */\n async pause(stopStreamImmediately?: boolean): Promise<boolean> {\n return this.scanner.pause(stopStreamImmediately);\n }\n\n /** Switch to a different camera by facing mode or device ID. */\n async setCamera(facingModeOrDeviceId: FacingMode | DeviceId): Promise<void> {\n return this.scanner.setCamera(facingModeOrDeviceId);\n }\n\n /** Check if the current camera supports flash/torch. */\n async hasFlash(): Promise<boolean> {\n return this.scanner.hasFlash();\n }\n\n /** Whether flash is currently on. */\n isFlashOn(): boolean {\n return this.scanner.isFlashOn();\n }\n\n /** Toggle flash on/off. */\n async toggleFlash(): Promise<void> {\n return this.scanner.toggleFlash();\n }\n\n /** Turn flash on. */\n async turnFlashOn(): Promise<void> {\n return this.scanner.turnFlashOn();\n }\n\n /** Turn flash off. */\n async turnFlashOff(): Promise<void> {\n return this.scanner.turnFlashOff();\n }\n\n /** Set the inversion mode for detecting inverted QR codes. */\n setInversionMode(mode: InversionMode): void {\n this.scanner.setInversionMode(mode);\n }\n\n // --- Static methods ---\n\n /** Check if the device has at least one camera. */\n static hasCamera(): Promise<boolean> {\n return CameraManager.hasCamera();\n }\n\n /** List available cameras. Pass true to request labels (triggers permission prompt). */\n static listCameras(requestLabels?: boolean): Promise<Camera[]> {\n return CameraManager.listCameras(requestLabels);\n }\n\n /**\n * Pre-load the WASM binary so it's ready when the scanner starts.\n * Call this early (e.g., on app init) to avoid delay on first scan.\n */\n static async preload(): Promise<void> {\n // Trigger WASM loading by doing a minimal scan\n const pixel = new Uint8ClampedArray([255, 255, 255, 255]);\n const img = new ImageData(pixel, 1, 1);\n try {\n await scanImage(img);\n } catch {\n // Expected — no QR code in a 1x1 image. The point was to load WASM.\n }\n }\n\n /**\n * Configure WASM loading. Call before creating any scanner instance.\n * @example\n * QrScanner.configureWasm({ locateFile: (filename) => `/wasm/${filename}` });\n */\n static configureWasm(overrides: Partial<EmscriptenModule>): void {\n setZXingModuleOverrides(overrides);\n }\n\n /**\n * Set a custom URL for the worker script. Call before creating any scanner.\n * Needed for CJS consumers or non-standard bundler setups.\n * By default, the worker URL is resolved via `new URL('./worker.js', import.meta.url)`,\n * which works with Vite, webpack 5, Parcel, and other modern bundlers.\n * @example\n * QrScanner.setWorkerUrl('/assets/qr-scanner-worker.js');\n */\n static setWorkerUrl(url: string | URL): void {\n setWorkerUrl(url);\n }\n\n /**\n * Enable or disable debug logging (performance timings, camera selection).\n * Off by default. Useful for diagnosing camera issues in the browser console.\n * @example\n * QrScanner.setDebug(true);\n */\n static setDebug(enabled: boolean): void {\n setDebug(enabled);\n }\n\n /** Scan a single image (not a video stream). */\n static scanImage(\n source:\n | HTMLImageElement\n | HTMLCanvasElement\n | OffscreenCanvas\n | ImageBitmap\n | ImageData\n | Blob\n | ArrayBuffer\n | Uint8Array\n | File\n | URL\n | string,\n options?: {\n scanRegion?: ScanRegion | null;\n canvas?: HTMLCanvasElement | null;\n decoderOptions?: Partial<ReaderOptions>;\n },\n ): Promise<ScanResult> {\n return scanImage(source, options);\n }\n}\n\nexport default QrScanner;\n"],"mappings":"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","names":["t","enabled"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agicash/qr-scanner",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "High-performance QR code scanner for the web, powered by ZXing-C++ WebAssembly",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -19,9 +19,7 @@
19
19
  },
20
20
  "./dist/worker.js": "./dist/worker.js"
21
21
  },
22
- "files": [
23
- "dist"
24
- ],
22
+ "files": ["dist"],
25
23
  "scripts": {
26
24
  "build": "tsup",
27
25
  "test": "vitest run",
@@ -42,6 +40,10 @@
42
40
  ],
43
41
  "author": "",
44
42
  "license": "MIT",
43
+ "repository": {
44
+ "type": "git",
45
+ "url": "https://github.com/MakePrisms/qr-scanner"
46
+ },
45
47
  "dependencies": {
46
48
  "zxing-wasm": "2.2.4"
47
49
  },