The boot gate learns about the profile: skeleton plaque, locked splash, a 4s cap

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Fable 5 committed 2026-08-29 16:55:00 +02:00
1 parent 04ef49ef3b
commit 7a9b61fc6b
3 files changed
+29 -4

No files matched your search

+10
View File
@@ -491,6 +491,16 @@ zombie (now an occasional opponent, `TUNE.racketEvery`, not the every-game fixtu
`RETURNED`, `DOUBLE KILL`) is a footnote to something you already watched happen. Sharing the loud
FACE with the milestone cards is right; sharing their SIZE made every footnote read as a milestone.
## Session log: the boot gate learns about the profile (2026-08-29, same day, STAGING ONLY)
Oliver: on a fresh load the player/build requests are still in flight while the splash is already
interactive. The existing `#splash.busy` gate (sprites + fonts) now ALSO waits for the profile
mirror's pull — capped at 4 s, because a dead network must never park the player on the title
(the sprite loader's own rule). While busy the PLAQUE is a skeleton: ghost crest, blank pills
where the words go, empty bar, no chevron, `pointer-events: none` — and the splash keyboard
shortcuts respect the gate too (they didn't). Half-true numbers neither show nor navigate; the
pull's `refreshSplash` repaints the real values the moment the gate lifts.
## Session log: THE CUP CLIMBS — the shuffle retires too (2026-08-29, same day, STAGING ONLY)
Oliver, straight after the draw screen went: "warum sollten wir noch mischen? wäre es nicht gut
+11 -1
View File
@@ -483,6 +483,16 @@ if (location.hostname !== 'splatter-tennis.wuwi.one' && !window.Capacitor?.isNat
font: 400 9px Silkscreen, monospace; color: var(--dim); letter-spacing: 2px; }
#splash.busy #boot { display: block; }
#splash.busy .btn, #splash.busy .two, #splash.busy #build { display: none; }
/* ...and the PLAQUE boots as a SKELETON: no tap, ghost crest, blank pills where the words go.
The profile mirror may still be in flight, and half-true numbers must neither show nor
navigate (main.js holds .busy until the pull settles or its 4 s cap fires). */
#splash.busy #plaque { pointer-events: none; }
#splash.busy #plaque em { visibility: hidden; }
#splash.busy #plBadge { opacity: 0.14; filter: grayscale(1); }
#splash.busy #plName, #splash.busy #plLv, #splash.busy #plGoal {
color: transparent; text-shadow: none; background: rgba(255,255,255,0.08);
border-radius: 4px; }
#splash.busy #plRow s i { width: 0% !important; }
#sSum, #brSum { display: flex; justify-content: space-between; gap: 8px; margin: 0 0 8px;
font: 400 9px Silkscreen, monospace; color: var(--dim); letter-spacing: 1px; }
.tabs { display: flex; gap: 6px; margin: 12px 0 14px; }
@@ -854,6 +864,6 @@ if (location.hostname !== 'splatter-tennis.wuwi.one' && !window.Capacitor?.isNat
</div>
</div>
</div>
<script type="module" src="src/main.js?v=331"></script>
<script type="module" src="src/main.js?v=332"></script>
</body>
</html>
+8 -3
View File
@@ -906,8 +906,12 @@ document.getElementById('stModes').addEventListener('click', e => {
});
refreshSplash();
// the profile mirror: adopt a server copy that is ahead (a wiped phone, a second device), then
// let the strip show whatever came back. Fire-and-forget — the menu never waits on the network.
profile.pull().then(refreshSplash);
// let the plaque show whatever came back. The BOOT GATE holds the splash locked (skeleton
// plaque, no buttons, no keys) until this settles — capped at 4 s, because a dead network must
// never park the player on the title (the sprite loader's own rule).
let profileDone = false;
profile.pull().then(() => { profileDone = true; refreshSplash(); });
setTimeout(() => { profileDone = true; }, 4000);
// THE BOOT GATE. render.js fires all ~230 sprite requests at import; this only watches them and
// keeps the splash's buttons out of reach until the last one has landed and both faces are ready.
@@ -925,7 +929,7 @@ const loaded = () => !splash.classList.contains('busy');
const tick = setInterval(function watchLoad() {
const frac = LOAD.total ? LOAD.done / LOAD.total : 1;
bootFill.style.width = (frac * 100).toFixed(1) + '%';
if (frac < 1) return;
if (frac < 1 || !profileDone) return; // sprites AND the profile mirror (or its 4 s cap)
clearInterval(tick);
// fonts.ready resolves once nothing is pending — including the three faces warmed above — so the
// first canvas frame is never the system serif.
@@ -950,6 +954,7 @@ addEventListener('keydown', e => {
if (!menuUp()) return;
menuMusic(); // a key is a gesture too — unlock the theme on a desktop that never taps
const stage = intro.dataset.stage;
if (stage === 'splash' && !loaded()) return; // the boot gate holds the keys too
if (e.key === 'Escape') return go('splash');
if (stage === 'splash') {
if (!loaded()) return; // the boot bar hides the buttons; a key must not walk past it