diff --git a/public/suite-page.html b/public/suite-page.html index 197a048..d4d671d 100644 --- a/public/suite-page.html +++ b/public/suite-page.html @@ -60,6 +60,7 @@ +
diff --git a/public/suite-page.js b/public/suite-page.js index 8142a1c..7ce3828 100644 --- a/public/suite-page.js +++ b/public/suite-page.js @@ -24,15 +24,46 @@ $('pgMeter').innerHTML = '' + m.remaining + ' of ' + m.limit + ' page builds left this month'; } + var built = false; + function showLive(url) { + built = true; $('pgUrl').textContent = url.replace(/^https:\/\//, ''); $('pgOpen').href = url; $('pgLive').style.display = 'block'; - $('pgFrame').src = url + '?preview=1'; + // cache-bust so a rebuild always shows the NEW page, not the old one + $('pgFrame').src = url + '?preview=' + Date.now(); $('pgPrev').style.display = 'block'; $('pgGo').textContent = '🔁 Rebuild my page'; } + // Live status under the button. A silent 40-second wait reads as "frozen", + // so we narrate it and count the seconds. + var progTimer = null; + function startProgress() { + var el = $('pgProg'), t0 = Date.now(); + var steps = [ + 'Reading your notes…', + 'Writing your headline and story…', + 'Choosing your angle video…', + 'Laying out your page…', + 'Almost there — finishing up…' + ]; + el.style.display = 'block'; + var tick = function () { + var s = Math.round((Date.now() - t0) / 1000); + var i = Math.min(steps.length - 1, Math.floor(s / 9)); + el.innerHTML = '' + steps[i] + ' (' + s + 's)'; + }; + tick(); + progTimer = setInterval(tick, 1000); + } + function stopProgress() { + if (progTimer) clearInterval(progTimer); + progTimer = null; + $('pgProg').style.display = 'none'; + } + function gate(msg) { var g = $('pgGate'); g.style.display = 'block'; @@ -74,8 +105,12 @@ busy = true; $('pgErr').style.display = 'none'; $('pgGo').disabled = true; - var label = $('pgGo').textContent; - $('pgGo').innerHTML = 'Writing your page… (up to a minute)'; + // Hide any previous preview while the new one is being written, so a stale + // or half-loaded frame can't look like a broken result. + $('pgPrev').style.display = 'none'; + $('pgFrame').removeAttribute('src'); + $('pgGo').innerHTML = 'Writing your page…'; + startProgress(); try { var r = await fetch('/api/public/suite-page', { method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -100,9 +135,10 @@ $('pgErr').textContent = 'Connection hiccup — try again.'; $('pgErr').style.display = 'block'; } + stopProgress(); busy = false; $('pgGo').disabled = false; - $('pgGo').textContent = label.indexOf('Rebuild') !== -1 ? '🔁 Rebuild my page' : '🧱 Build my page'; + $('pgGo').textContent = built ? '🔁 Rebuild my page' : '🧱 Build my page'; } document.addEventListener('DOMContentLoaded', function () { diff --git a/server.js b/server.js index e9429c9..a6ec70d 100644 --- a/server.js +++ b/server.js @@ -464,7 +464,9 @@ async function handleTranslate(req,res){ return json(res,200,{t:out}); } -const CSP_BASE="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; font-src 'self' data:; form-action 'self'; frame-src https://www.youtube-nocookie.com"; +// frame-src includes 'self' so our own pages (the Page Builder preview framing +// a member's /p/