aboutsummaryrefslogtreecommitdiffstats
path: root/build.js
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-06-23 23:05:52 -0700
committerAdam Soltys <[email protected]>2026-06-23 23:05:52 -0700
commitd5e55c9c577feb6f8000bad3c37045a2cd7c9b4c (patch)
treec4260128cae53c949c29e1fe802c4d4a5aca94d0 /build.js
parentca7d85e8603ad5ee6a31afbf56777cedfc3931b5 (diff)
downloadcoinosv3-d5e55c9c577feb6f8000bad3c37045a2cd7c9b4c.tar.gz
coinosv3-d5e55c9c577feb6f8000bad3c37045a2cd7c9b4c.zip
Add QR scanner to the send page (lazy-loaded decoder)
Scan a Bitcoin address, a BIP21 URI (fills address + amount), or a signed-transaction QR (opens a broadcast confirmation, then relays it) from a viewfinder button on the Send form. Decoder strategy keeps the main bundle small: - Prefer the native BarcodeDetector (Chrome desktop/Android) — no lib. - Otherwise lazy-load jsQR from a separate dist/jsqr.js, only when the scanner is opened. jsQR stays out of index.html (~424 KB vs ~551 KB) and off the initial load. - The service worker precaches jsqr.js so offline scanning works for installed PWAs on browsers without BarcodeDetector. The scanner needs a camera + secure context, so it never shows on a standalone file:// page; offline air-gapped signing is unaffected. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Diffstat (limited to 'build.js')
-rw-r--r--build.js24
1 files changed, 22 insertions, 2 deletions
diff --git a/build.js b/build.js
index f11a9f6..34d3d19 100644
--- a/build.js
+++ b/build.js
@@ -42,7 +42,9 @@ const MANIFEST = {
// cross-origin explorer/Nostr requests. CACHE carries a build token so each
// deploy supersedes the previous cache. {{VERSION}} is filled in at build time.
const SW = `const CACHE = 'cold-{{VERSION}}';
-const SHELL = ['./', 'manifest.webmanifest', 'icon-192.png', 'icon-512.png'];
+// jsqr.js is precached so the lazy QR decoder is available offline too (for
+// installed PWAs on browsers without a native BarcodeDetector).
+const SHELL = ['./', 'manifest.webmanifest', 'icon-192.png', 'icon-512.png', 'jsqr.js'];
self.addEventListener('install', (e) => {
self.skipWaiting();
@@ -91,6 +93,20 @@ const PWA_HEAD = `<link rel="manifest" href="manifest.webmanifest">
const SW_REGISTER =
`<script>if('serviceWorker'in navigator)addEventListener('load',function(){navigator.serviceWorker.register('sw.js').catch(function(){})});</script>`;
+// Bundle the standalone jsQR decoder (sets window.jsQR) for lazy loading.
+export async function buildJsQr({ minify = true } = {}) {
+ const result = await Bun.build({
+ entrypoints: ['./src/jsqr-global.js'],
+ target: 'browser',
+ minify,
+ });
+ if (!result.success) {
+ for (const log of result.logs) console.error(log);
+ throw new Error('jsqr bundle failed');
+ }
+ return result.outputs[0].text();
+}
+
export async function buildHtml({ minify = true, pwa = minify } = {}) {
const result = await Bun.build({
entrypoints: ['./src/app.js'],
@@ -128,6 +144,10 @@ if (import.meta.main) {
const html = await buildHtml({ minify: true });
await Bun.write('dist/index.html', html);
+ // Lazy-loaded QR decoder — kept out of index.html, fetched only when a
+ // browser without BarcodeDetector opens the scanner.
+ await Bun.write('dist/jsqr.js', await buildJsQr());
+
// PWA sidecars.
await Bun.write('dist/manifest.webmanifest', JSON.stringify(MANIFEST, null, 2));
const version = Bun.hash(html).toString(36);
@@ -136,5 +156,5 @@ if (import.meta.main) {
const kb = (Buffer.byteLength(html) / 1024).toFixed(0);
console.log(`✓ dist/index.html written (${kb} KB) — open it offline, no server needed`);
- console.log(`✓ PWA: manifest.webmanifest, sw.js (cold-${version}), ${STATIC.length} icons`);
+ console.log(`✓ PWA: manifest.webmanifest, sw.js (cold-${version}), ${STATIC.length} icons, jsqr.js`);
}