From 82cd8da7f5ab285b3a91f7284fb8d845400012ca Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Thu, 10 Sep 2026 22:51:56 -0700 Subject: Ship the punks at the size we actually draw them. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The art is 240x240 (20KB each, 1.5MB for the set) because that's what a punk published as someone's nostr picture should be — but the circles the app draws are 18 to 64px, so a new wallet with nothing cached was fetching eight times the pixels it could show, one request per face. punks-sm carries a 128px copy of each: 4KB instead of 20KB, indistinguishable at 64px. Small circles and the wizard's picker tiles load those; only the 64px profile avatar and the value the wizard publishes stay full-size. The bigger win is that a punk picture is our own art wherever it turns up. Everyone who keeps the default publishes v3.coinos.io/punks/N.webp as their nostr picture, so those faces now paint straight off disk at the size being drawn — no request, no thumbnail to make, and no broken circle when the legacy coinos.io copies 502 (which they do for a third of the set). Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_019mrt228Upn9nNCwGixzSLx --- src/recipient-search.js | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'src/recipient-search.js') diff --git a/src/recipient-search.js b/src/recipient-search.js index a5c5f46..be3c70e 100644 --- a/src/recipient-search.js +++ b/src/recipient-search.js @@ -15,12 +15,23 @@ import { queryOn, parseNostrPubkey, npubOf, PROFILE_RELAYS } from './nostr.js'; // if the image can't load (offline). // Self-hosted (dist/punks) — coinos.io's own copies 502 for a third of the // set. Same deterministic formula the coinos app uses. -export const punkUrl = (pk) => - `punks/${Math.floor((parseInt(pk.slice(-2), 16) / 256) * 64) + 1}.webp`; +const punkN = (pk) => Math.floor((parseInt(pk.slice(-2), 16) / 256) * 64) + 1; +export const punkUrl = (pk) => `punks/${punkN(pk)}.webp`; +// The art is 240px, which is what a punk published as someone's nostr +// picture should be — and eight times what a 30px circle needs. We ship a +// 128px copy of each (a fifth of the bytes, 20KB → 4KB) and paint the circles +// from that, so a brand-new wallet with nothing cached fills its faces in +// straight away. Only the 64px profile avatar takes the full-size one. +export const punkSmallUrl = (pk) => `punks-sm/${punkN(pk)}.webp`; +export const punkSmallN = (n) => `punks-sm/${n}.webp`; export const fallbackAvatar = (h, pk, name, cls) => h('div', { class: cls + ' fallback' }, - h('img', { class: 'punk', src: punkUrl(pk), alt: '', onError: (e) => { e.target.style.display = 'none'; } }), + h('img', { + class: 'punk', alt: '', + src: cls.includes('profile-avatar') ? punkUrl(pk) : punkSmallUrl(pk), + onError: (e) => { e.target.style.display = 'none'; }, + }), (name || npubOf(pk) || '??').slice(0, 2)); const REGISTRAR = 'https://names.coinos.io'; -- cgit v1.2.3