openvibers/openvibe.live Commits
Home hero: signed-in streamers with setup left see their next task under the CTA buttons
Changes
@@@ -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; }++}@@@ -218,6 +218,9 @@data-ovtip-foot="<i class="fa-solid fa-heart"></i> We love what people build here — 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 & 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)@@@ -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;@@@ -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 = () => {
@@ -488,3 +488,21 @@
@@ -488,3 +488,21 @@
.tour-port { fill: var(--c, #fde68a); opacity: 0.9; filter: drop-shadow(0 0 3px var(--c, #fde68a)); }
.tour-port { fill: var(--c, #fde68a); opacity: 0.9; filter: drop-shadow(0 0 3px var(--c, #fde68a)); }
.tour-port--in { --c: #fde68a; }
.tour-port--in { --c: #fde68a; }
.rs-lite .tour-hub-ring, .rs-lite .tour-port { filter: none; }
.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; }
+}
@@ -218,6 +218,9 @@
@@ -218,6 +218,9 @@
data-ovtip-foot="<i class="fa-solid fa-heart"></i> We love what people build here — come make something"
data-ovtip-foot="<i class="fa-solid fa-heart"></i> We love what people build here — 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 & Mod <small>API docs</small></span><i class="fa-solid fa-chevron-right hb-go" aria-hidden="true"></i></a>
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 & Mod <small>API docs</small></span><i class="fa-solid fa-chevron-right hb-go" aria-hidden="true"></i></a>
</div>
</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">
<div class="hero-philosophy-links">
<a href="#home-cta-banner" class="hero-philosophy-btn" onclick="event.preventDefault();openHomeAbout();">
<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)
<i class="fa-solid fa-book-open"></i> What Is This? (Philosophy)
@@ -338,6 +338,8 @@
@@ -338,6 +338,8 @@
try {
try {
const slot = document.getElementById('tour-next-up');
const slot = document.getElementById('tour-next-up');
if (slot && typeof window.setupNextUp === 'function') await window.setupNextUp(slot);
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 { /* */ }
} catch { /* */ }
}
}
window.refreshSetupProgress = refreshSetupProgress;
window.refreshSetupProgress = refreshSetupProgress;
@@ -533,6 +535,22 @@
@@ -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) {
window.setupNextUp = async function (el) {
// The home page slot: setup progress for signed-in streamers, the pitch for everyone else.
// The home page slot: setup progress for signed-in streamers, the pitch for everyone else.
if (!el) return;
if (!el) return;
@@ -86,7 +86,10 @@
@@ -86,7 +86,10 @@
// sits well below the fold, and measuring it during page load forced a full synchronous
// 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.
// 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.
// 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);
setTimeout(nextUp, 900); setTimeout(nextUp, 3500);
let raf = 0, dirty = true;
let raf = 0, dirty = true;
const redraw = () => {
const redraw = () => {