CI / test (push) Successful in 3s
This switches demo pages and modules to local web/vendor assets, fixes Three GLTFLoader local import resolution, and documents the runtime-data/agent commit workflow updates. Made-with: Cursor
60 lines
1.8 KiB
JavaScript
60 lines
1.8 KiB
JavaScript
/**
|
|
* QR code scanner from camera. Decodes private key (pk) from QR.
|
|
*/
|
|
import jsQR from "./vendor/qr/jsqr.bundle.mjs";
|
|
|
|
/**
|
|
* Scan QR code from camera video stream.
|
|
* @param {HTMLVideoElement} videoEl - Video element to render the stream.
|
|
* @param {AbortSignal} [signal] - AbortSignal to stop scanning (e.g. when dialog closes).
|
|
* @returns {Promise<string>} Decoded QR text.
|
|
*/
|
|
export async function scanQRFromCamera(videoEl, signal) {
|
|
const stream = await navigator.mediaDevices.getUserMedia({
|
|
video: { facingMode: "environment" },
|
|
});
|
|
videoEl.srcObject = stream;
|
|
await videoEl.play();
|
|
|
|
const canvas = document.createElement("canvas");
|
|
const ctx = canvas.getContext("2d");
|
|
|
|
return new Promise((resolve, reject) => {
|
|
if (signal?.aborted) {
|
|
stream.getTracks().forEach((t) => t.stop());
|
|
reject(new DOMException("Aborted", "AbortError"));
|
|
return;
|
|
}
|
|
signal?.addEventListener("abort", () => {
|
|
stream.getTracks().forEach((t) => t.stop());
|
|
videoEl.srcObject = null;
|
|
reject(new DOMException("Aborted", "AbortError"));
|
|
});
|
|
|
|
function tick() {
|
|
if (signal?.aborted) return;
|
|
if (videoEl.readyState !== videoEl.HAVE_ENOUGH_DATA) {
|
|
requestAnimationFrame(tick);
|
|
return;
|
|
}
|
|
canvas.width = videoEl.videoWidth;
|
|
canvas.height = videoEl.videoHeight;
|
|
if (canvas.width === 0 || canvas.height === 0) {
|
|
requestAnimationFrame(tick);
|
|
return;
|
|
}
|
|
ctx.drawImage(videoEl, 0, 0);
|
|
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
|
const result = jsQR(imageData.data, imageData.width, imageData.height);
|
|
if (result) {
|
|
stream.getTracks().forEach((t) => t.stop());
|
|
videoEl.srcObject = null;
|
|
resolve(result.data);
|
|
return;
|
|
}
|
|
requestAnimationFrame(tick);
|
|
}
|
|
tick();
|
|
});
|
|
}
|