From 11a7919939f30318c6f2bf81058e57e2bb604b67 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Wed, 2 Sep 2026 13:42:24 -0700 Subject: Three render hogs put on a leash for the sluggish-tabs hunt: qrSvg is memoized (make() runs eight mask trials over a version-20 grid — 30ms+ per call on a phone, paid on EVERY render of the receive pane), the header's unread count reuses its answer behind a revision counter bumped only when messages or read-marks actually change, and ark's history list rebuilds at most every 1.5s instead of per repaint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr --- src/qr.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'src/qr.js') diff --git a/src/qr.js b/src/qr.js index d02aed3..416252d 100644 --- a/src/qr.js +++ b/src/qr.js @@ -9,7 +9,14 @@ import qrcode from 'qrcode-generator'; // mode: pass 'Alphanumeric' for an all-uppercase QR-alphanumeric string (0-9 A-Z // space $%*+-./:) to pack it at 5.5 bits/char instead of byte mode's 8 — a much // smaller grid for the same data. Falls back to byte mode if the text doesn't fit. +// Memoized: a QR is a pure function of its input, and make() runs eight +// mask-penalty trials over the whole grid — 30ms+ on a phone for a long +// bitcoin: URI. The receive pane used to pay that on EVERY render. +const _memo = new Map(); export function qrSvg(text, { margin = 2, ec = 'M', mode } = {}) { + const key = `${ec}|${mode || ''}|${margin}|${text}`; + const hit = _memo.get(key); + if (hit) return hit; let qr; try { qr = qrcode(0, ec); @@ -33,8 +40,11 @@ export function qrSvg(text, { margin = 2, ec = 'M', mode } = {}) { } } - return ` + const svg = ` `; + _memo.set(key, svg); + if (_memo.size > 24) _memo.delete(_memo.keys().next().value); + return svg; } -- cgit v1.2.3