- Add Import pk from camera: scan QR → restore pb → auto login → refresh - Add scanner.js (jsQR) for camera QR decode - QR visibility: pk shown by default, pb hidden by default (toggles) - Update docs/frontend-development.md with scanner, Import pk, QR behavior Made-with: Cursor
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* QR code scanner from camera. Decodes private key (pk) from QR.
|
||||
*/
|
||||
import jsQR from "https://esm.sh/jsqr@1.4.0";
|
||||
|
||||
/**
|
||||
* 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();
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user