import { useMemo } from "react"; import qrcode from "qrcode-generator"; // The only QR encoder in the client; a screen that shows a QR uses this, never the library directly. // Medium error correction survives a phone camera at arm's length across a desk. export const qrModules = (text: string): boolean[][] => { const qr = qrcode(0, "M"); qr.addData(text, "Byte"); qr.make(); const size = qr.getModuleCount(); return Array.from({ length: size }, (_, row) => Array.from({ length: size }, (_, col) => qr.isDark(row, col))); }; const QUIET_ZONE = 4; interface QrCodeProps { value: string; /** What the code is for, read to a screen reader in place of the picture. */ label: string; className?: string; } export const QrCode = ({ value, label, className }: QrCodeProps) => { const { path, extent } = useMemo(() => { const modules = qrModules(value); const squares: string[] = []; modules.forEach((row, y) => row.forEach((dark, x) => { if (dark) squares.push(`M${x + QUIET_ZONE} ${y + QUIET_ZONE}h1v1h-1z`); }), ); return { path: squares.join(""), extent: modules.length + QUIET_ZONE * 2 }; }, [value]); return ( {/* Pure black on white is for the scanner, not the palette: a camera reads contrast, not tokens. */} ); };