openvibers/openvibe.live Commits

Home hero: signed-in streamers with setup left see their next task under the CTA buttons

OP OpenVibers committed 19 Sept 2026 · ac64530f20 · 4 files, +43 −1

The setup quest (Next up: … n/m) lived only in the tour card a screen below. The hero now
carries the same component, rendered by the same function, directly under the Stream/Tools/
Game row and at that row's width. It appears only for a signed-in user whose setup is not
complete; signed-out visitors and finished streamers see nothing there. Repaints with the
tour slot whenever progress is re-read (guide closed, page ticks).

Changes

ddiff --git a/public/css/home-tour.css b/public/css/home-tour.css
iindex a7582e6c..2501248f 100644
---- a/public/css/home-tour.css
++++ b/public/css/home-tour.css
@@@ -488,3 +488,21 @@
.tour-port { fill: var(--c, #fde68a); opacity: 0.9; filter: drop-shadow(0 0 3px var(--c, #fde68a)); }
.tour-port--in { --c: #fde68a; }
.rs-lite .tour-hub-ring, .rs-lite .tour-port { filter: none; }
++
++/* ── Hero copy of the quest ───────────────────────────────────────────────────────────────────
++ Same component, one row below the Stream/Tools/Game buttons. It takes the width of that button
++ row (the hero column), sits flush with it, and keeps the water animation gated by the same
++ viewport class as the tour slot. */
++.hero-quest-slot { display: flex; justify-content: center; width: 100%; max-width: 720px; margin: 14px auto 0; }
++.hero-quest-slot[hidden] { display: none; }
++.hero-quest-slot .ovg-quest { width: 100%; padding: 11px 14px; border-radius: 14px;
++ background: linear-gradient(180deg, color-mix(in srgb, var(--q) 10%, color-mix(in srgb, var(--bg-card) 78%, transparent)), color-mix(in srgb, var(--bg-secondary) 84%, transparent));
++ backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
++ box-shadow: 0 8px 22px rgba(0,0,0,0.28), inset 0 1px 0 rgba(255,255,255,0.05); }
++.hero-quest-slot .ovg-quest-text b { font-size: 0.9rem; }
++.hero-quest-slot .ovg-quest-text small { font-size: 0.73rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
++.hero-quest-slot .ovg-quest-ico { width: 38px; height: 38px; }
++@media (max-width: 640px) {
++ .hero-quest-slot { margin-top: 10px; }
++ .hero-quest-slot .ovg-quest { padding: 10px 12px; }
++}
ddiff --git a/public/index.html b/public/index.html
iindex 31501b06..ac387601 100644
---- a/public/index.html
++++ b/public/index.html
@@@ -218,6 +218,9 @@
data-ovtip-foot="<i class=&quot;fa-solid fa-heart&quot;></i> We love what people build here &mdash; come make something"
data-ovtip-go="Open the API docs"><span class="hb-ico"><i class="fa-solid fa-wand-magic-sparkles"></i></span><span class="hb-label">Build &amp; Mod <small>API docs</small></span><i class="fa-solid fa-chevron-right hb-go" aria-hidden="true"></i></a>
</div>
++ <!-- Signed-in streamers with setup left to do see their next task here, in the row of
++ things to do rather than a screen below (public/js/guides-live.js → setupHeroNextUp). -->
++ <div class="tour-quest-slot hero-quest-slot" id="hero-next-up" data-fx-viewport hidden></div>
<div class="hero-philosophy-links">
<a href="#home-cta-banner" class="hero-philosophy-btn" onclick="event.preventDefault();openHomeAbout();">
<i class="fa-solid fa-book-open"></i> What Is This? (Philosophy)
ddiff --git a/public/js/guides-live.js b/public/js/guides-live.js
iindex 401ea0fa..d4ecbeab 100644
---- a/public/js/guides-live.js
++++ b/public/js/guides-live.js
@@@ -338,6 +338,8 @@
try {
const slot = document.getElementById('tour-next-up');
if (slot && typeof window.setupNextUp === 'function') await window.setupNextUp(slot);
++ const hero = document.getElementById('hero-next-up');
++ if (hero && typeof window.setupHeroNextUp === 'function') await window.setupHeroNextUp(hero);
} catch { /* */ }
}
window.refreshSetupProgress = refreshSetupProgress;
@@@ -533,6 +535,22 @@
});
}
++ /**
++ * The hero copy of the quest. Only a signed-in streamer with tasks left sees it: it sits under
++ * the Stream/Tools/Game buttons so the next setup step is in the row of things to do, not a
++ * screen below. Everyone else (signed out, or all done) sees nothing there — the tour card
++ * further down still carries the pitch and the "start streaming" button.
++ */
++ window.setupHeroNextUp = async function (el) {
++ if (!el) return;
++ const hide = () => { el.hidden = true; el.innerHTML = ''; };
++ if (!me()) return hide();
++ const p = await loadProgress();
++ if (!p || !p.total || p.done >= p.total) return hide();
++ renderQuest(el, p);
++ el.hidden = false;
++ };
++
window.setupNextUp = async function (el) {
// The home page slot: setup progress for signed-in streamers, the pitch for everyone else.
if (!el) return;
ddiff --git a/public/js/home-tour.js b/public/js/home-tour.js
iindex e840b017..1b60810d 100644
---- a/public/js/home-tour.js
++++ b/public/js/home-tour.js
@@@ -86,7 +86,10 @@
// sits well below the fold, and measuring it during page load forced a full synchronous
// layout (~165ms on a 4x-throttled phone profile) for something nobody could see yet.
// Signed-in streamers get a context-aware button: the next thing they haven't set up.
-- const nextUp = () => { if (typeof setupNextUp === 'function') setupNextUp(document.getElementById('tour-next-up')); };
++ const nextUp = () => {
++ if (typeof setupNextUp === 'function') setupNextUp(document.getElementById('tour-next-up'));
++ if (typeof setupHeroNextUp === 'function') setupHeroNextUp(document.getElementById('hero-next-up'));
++ };
setTimeout(nextUp, 900); setTimeout(nextUp, 3500);
let raf = 0, dirty = true;
const redraw = () => {