aboutsummaryrefslogtreecommitdiffstats
path: root/src/features/messages.js
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-09-04 18:37:53 -0700
committerAdam Soltys <[email protected]>2026-09-04 18:37:53 -0700
commit4431198900b2d1a5cfb73bd02ddfd1e377d9acb9 (patch)
treeb285d431f7d17d6ceee8f630dad4b8a043c628f5 /src/features/messages.js
parentff5c5d2ac5c017c7c134f3969416306cd8742d1c (diff)
downloadcoinosv3-4431198900b2d1a5cfb73bd02ddfd1e377d9acb9.tar.gz
coinosv3-4431198900b2d1a5cfb73bd02ddfd1e377d9acb9.zip
Hats no longer pop in a beat after page load. The hats feature is a deferred chunk, so every boot painted bare heads until it arrived — but the core bundle can't carry the hat art. Instead, wrapAvatar now leaves a STAMP per head in localStorage (art + percent geometry, size-independent, capped at 40, cleared when a head goes bare so removals can't ghost), and the core avatar factory replays stamps until hatsReady answers — from then on a null from wrapAvatar genuinely means bare. Verified headless: a stamped hat is on the header avatar 15ms after DOM-ready, and once the chunk lands with a bare-head answer the stamp is cleared with no double-wrap
Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
Diffstat (limited to 'src/features/messages.js')
-rw-r--r--src/features/messages.js18
1 files changed, 17 insertions, 1 deletions
diff --git a/src/features/messages.js b/src/features/messages.js
index d6eacc9..0aa7291 100644
--- a/src/features/messages.js
+++ b/src/features/messages.js
@@ -1592,9 +1592,25 @@ export function messagesFeature(ctx) {
node.classList.add('clickable');
node.addEventListener('click', (e) => { e.stopPropagation(); openProfile(pk); });
}
- return hook('wrapAvatar', pk, node) || node;
+ return hook('wrapAvatar', pk, node) || hatStamp(pk, node) || node;
};
+ // The hats feature is a deferred chunk — until it lands, replay last
+ // session's boot stamps (hat art + geometry it persisted) so a hat sits on
+ // the head from the FIRST frame instead of popping in a beat later. Once
+ // hatsReady answers, the stamps stand down: a null from wrapAvatar then
+ // genuinely means "bare head".
+ let _hatStamps = null;
+ function hatStamp(pk, node) {
+ if (hook('hatsReady')) return null;
+ if (_hatStamps === null) {
+ try { _hatStamps = JSON.parse(localStorage.getItem('hat-stamps') || '{}'); } catch { _hatStamps = {}; }
+ }
+ const s = _hatStamps[pk];
+ if (!s || !s.a) return null;
+ return h('span', { class: 'hat-wrap' }, node, h('span', { class: 'ava-hat', style: s.s, html: s.a }));
+ }
+
// ---- profiles: view + own kind-0 editor ---------------------------------
const fullProfiles = new Map(); // pk -> { raw kind0 content object, fetched_at }