openvibers/openvibe.space Commits

openvibe.website starts a site with no account (#151)

OP OpenVibers committed 12 days ago · 1887dd8625 · 4 files, +25 −6

Changes

ddiff --git a/server/render/website.js b/server/render/website.js
iindex 150b4fa..1c6532e 100644
---- a/server/render/website.js
++++ b/server/render/website.js
@@@ -19,12 +19,13 @@ const { asset, escapeHtml: esc } = require('./layout');
const SPACE = 'https://openvibe.space';
const NAME = 'OpenVibe.Website';
const DESCRIPTION = 'Websites that look finished from the first preview: start from a real shop, portfolio, event, link page or launch page, or describe yours, and change anything with the preview beside it.';
--const startHref = (id) => `${SPACE}/auth/login?next=${encodeURIComponent(`/spaces/new${id ? `?template=${id}` : ''}`)}`;
++// No account to start (guest drafts, Space #150): a private edit link, and an OpenVibe account only to keep it.
++const startHref = (id) => `${SPACE}/spaces/guest${id ? `?template=${encodeURIComponent(id)}` : '#sp-prompt'}`;
const FAQ = [
['Do I need to know how to code?', 'No. Pick a template and change the words, colours and pictures, or describe the site and OpenVibe AI writes a first version. The code is all there when you want it.'],
['When can I put it on my own address?', 'Publishing at yourname.openvibe.website or on your own domain opens with public hosting, once the automatic abuse checks, a designated DMCA agent and the domain\'s Public Suffix List entry are in place. Until then you build, edit and preview, and OpenVibe staff publish.'],
-- ['Why do I sign in on openvibe.space?', 'Your sites are kept with your OpenVibe account on OpenVibe.Space, the engine that builds and hosts them. openvibe.website itself keeps no cookies, so no site here can ever touch your sign-in.'],
++ ['Do I need an account?', 'Not to start. Pick a template or describe your site and you get a private link to edit it, good for a week. Sign in with OpenVibe on openvibe.space to keep it; openvibe.website itself keeps no cookies, so no site here can touch your sign-in.'],
['Can I take my site somewhere else?', 'Yes. A site is plain HTML, CSS and JavaScript; copy its files out of the editor whenever you like.'],
];
@@@ -70,7 +71,7 @@ ${sc.hero({
{ label: 'Pick a template', href: '#templates', primary: true },
{ label: 'Describe your site', href: startHref('') },
],
-- note: 'Building and previewing are open to everyone with an OpenVibe account. Publishing at your own address opens with public hosting.',
++ note: 'No account needed to start: you get a private link to edit your site, and keep it with an OpenVibe account. Publishing at your own address opens with public hosting.',
aside: { html: stage() },
})}
<section class="sc-sec" id="templates" aria-labelledby="wb-tpl-h">
ddiff --git a/server/spaces/pages.js b/server/spaces/pages.js
iindex e862639..8a11f58 100644
---- a/server/spaces/pages.js
++++ b/server/spaces/pages.js
@@@ -217,7 +217,10 @@ function createSpacesPages(ctx) {
// A visitor who is not signed in makes a draft here, on openvibe.space (never on openvibe.website, which
// reads no cookie and sets none). The answer is the secret edit link; the cookie only remembers it.
router.get('/spaces/guest', view(async (req, res) => {
-- html(res, 200, render.guestPage({ templates: templates.sites(), error: null, prompt: '', aiOn: ai.enabled() }));
++ // ?template=<id>: a website template picked on openvibe.website leads the page, one press from a draft.
++ const picked = req.query.template ? templates.get(String(req.query.template)) : null;
++ const pick = picked && picked.kind === 'site' ? picked : null;
++ html(res, 200, render.guestPage({ templates: templates.sites(), error: null, prompt: '', aiOn: ai.enabled(), pick }));
}));
router.post('/spaces/guest', ...guestAction(async (req, res) => {
const body = req.body || {};
ddiff --git a/server/spaces/render.js b/server/spaces/render.js
iindex fd1629c..22f0158 100644
---- a/server/spaces/render.js
++++ b/server/spaces/render.js
@@@ -233,7 +233,12 @@ function editorPage({ space, files, csrf, canPublish, url, aiOn, buildsLeft, tem
* The start page of a guest draft (openvibe.website, plan 5.9): no account, a template or a sentence, and a
* private link to edit it. Plain forms in the spaces markup; the design is the orchestrator's.
*/
--function guestPage({ templates, error, prompt, aiOn }) {
++function guestPage({ templates, error, prompt, aiOn, pick = null }) {
++ const picked = pick ? `<section class="sp-pick" style="--tpl:${esc(pick.accent)}" aria-labelledby="sp-pick-h">
++ <span class="sp-template-icon"><i class="fa-solid ${esc(pick.icon)}" aria-hidden="true"></i></span>
++ <div class="sp-pick-text"><p class="eyebrow">The template you picked</p><h2 id="sp-pick-h">${esc(pick.name)}</h2><p>${esc(pick.description)}</p></div>
++ <form method="post" action="/spaces/guest"><input type="hidden" name="template" value="${esc(pick.id)}"><button class="sp-btn">Start from this</button></form>
++</section>` : '';
const cards = templates.map((t) => `<div class="sp-template" style="--tpl:${esc(t.accent)}">
<span class="sp-template-icon"><i class="fa-solid ${esc(t.icon)}" aria-hidden="true"></i></span>
<h3>${esc(t.name)}</h3><p>${esc(t.description)}</p>
@@@ -244,6 +249,7 @@ function guestPage({ templates, error, prompt, aiOn }) {
<h1 class="sp-title">Make a site.</h1>
<p class="sp-lede">No account needed. Start from a template or describe what you want and OpenVibe AI writes it. You get a private link to edit it; sign in later to keep it.</p>
${error ? `<p class="sp-flash is-bad" role="alert">${esc(error)}</p>` : ''}
++ ${picked}
<form class="sp-ai-form" method="post" action="/spaces/guest">
<label for="sp-prompt">Describe it and OpenVibe AI builds it</label>
<textarea id="sp-prompt" name="prompt" rows="4" maxlength="4000" placeholder="A bakery site with today&rsquo;s hours, where to find us and how to order…" ${aiOn ? '' : 'disabled'}>${esc(prompt || '')}</textarea>
ddiff --git a/test/website-home.test.js b/test/website-home.test.js
iindex 903f384..d136543 100644
---- a/test/website-home.test.js
++++ b/test/website-home.test.js
@@@ -31,9 +31,10 @@ assert.match(hostingSrc, /target\.kind === 'dashboard' \|\| target\.kind === 'we
assert.strictEqual(sites.length, 5, 'five website templates');
for (const s of sites) {
assert.ok(r.text.includes(`<iframe src="/templates/${s.id}"`) && r.text.includes('sandbox=""'), `${s.id} previews with no script`);
-- assert.ok(r.text.includes(`https://openvibe.space/auth/login?next=${encodeURIComponent(`/spaces/new?template=${s.id}`)}`), `${s.id} is one press from a new space`);
++ assert.ok(r.text.includes(`https://openvibe.space/spaces/guest?template=${s.id}`), `${s.id} starts a draft with no account`);
}
assert.match(r.text, /Publishing at your own address opens with public hosting/, 'it claims no publishing it does not have');
++ assert.match(r.text, /No account needed to start/, 'and says what is true now: no account to start');
assert.match(r.text, /<link rel="canonical" href="https:\/\/openvibe\.website\/">/);
assert.strictEqual((r.text.match(/<h1[\s>]/g) || []).length, 1, 'one h1');
assert.ok(r.text.includes('"@type":"FAQPage"'), 'its questions are structured data');
@@@ -80,6 +81,14 @@ assert.match(hostingSrc, /target\.kind === 'dashboard' \|\| target\.kind === 'we
for (const s of templates.sites()) assert.ok(llms.includes(`https://openvibe.website/templates/${s.id}`), s.id);
});
++ await check('the guest start page leads with a website template picked on the front door', async () => {
++ const r = await t.request({ method: 'GET', host: 'openvibe.space', path: '/spaces/guest?template=site-event' });
++ assert.strictEqual(r.status, 200, r.text.slice(0, 200));
++ assert.match(r.text, /<section class="sp-pick"[^>]*>[\s\S]*?<h2 id="sp-pick-h">Event page<\/h2>[\s\S]*?name="template" value="site-event"/);
++ const app = await t.request({ method: 'GET', host: 'openvibe.space', path: '/spaces/guest?template=poll' });
++ assert.ok(!app.text.includes('class="sp-pick"'), 'an app template is not a website to start from here');
++ });
++
await check('openvibe.space leads with a template picked on the front door', async () => {
const r = await t.request({ method: 'GET', host: 'openvibe.space', path: '/spaces/new?template=site-portfolio', session: alice });
assert.strictEqual(r.status, 200, r.text.slice(0, 200));