From 570dd4f601d2bab8249f108d2910f8197ade3a0b Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Thu, 3 Sep 2026 22:03:37 -0700 Subject: The DM backfill yields to the main thread: decrypting a gift-wrap backlog (NIP-44 ECDH, ~500ms of secp256k1 in a boot profile — the last of the carousel jank) ran as one uninterrupted loop. Each synchronous run is now capped to ~8ms, one frame's budget, then yields so input and paint get a turn; the backfill just spreads over a few extra frames instead of freezing them 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/features/messages.js | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'src/features/messages.js') diff --git a/src/features/messages.js b/src/features/messages.js index f0e3069..fe20ac4 100644 --- a/src/features/messages.js +++ b/src/features/messages.js @@ -1206,6 +1206,12 @@ export function messagesFeature(ctx) { if (!decryptorsComplete()) await Promise.resolve(hook('nostrLoginResume')).catch(() => null); if (!dmDecryptors().length) return; const complete = decryptorsComplete(); + // Each openInboxWrap decrypts a gift wrap — NIP-44 ECDH, real + // secp256k1 work. A backlog decrypted in one tight loop blocks the main + // thread for the whole burst (the boot-time jank that made a carousel + // drag lurch). Cap each synchronous run to a frame's budget and yield so + // input and paint get a turn; the backfill just takes a few extra frames. + let chunkStart = performance.now(); for (const [id, p] of [...pendingWraps]) { // A remote signer can die mid-pass; counting the remaining wraps as // full-strength failures would evict messages that were never really @@ -1213,6 +1219,10 @@ export function messagesFeature(ctx) { if (complete && !decryptorsComplete()) break; if (await openInboxWrap(p.wrap)) pendingWraps.delete(id); else if (complete && ++p.tries >= PENDING_TRIES) pendingWraps.delete(id); + if (performance.now() - chunkStart > 8) { + await new Promise((r) => setTimeout(r)); + chunkStart = performance.now(); + } } } finally { draining = false; } // still holding wraps: the signer may connect later — keep a slow retry -- cgit v1.2.3