openvibers/openvibe.chat Commits

openvibe.chat's front page leads to Vibe: Chat with Vibe first in the hero, an Ask Vibe box that opens a Vibe chat with the question in it (never sent for you), and What is Vibe? in the questions (#188)

OP OpenVibers committed 19 days ago · 8c9c860d2a · 4 files, +48 −4

Changes

ddiff --git a/public/web/chat.css b/public/web/chat.css
iindex 49899a4..cf0a9af 100644
---- a/public/web/chat.css
++++ b/public/web/chat.css
@@@ -279,6 +279,12 @@ h2 { font-size: 1.15rem; margin: 1.4em 0 .5em; }
.oc-hero ~ .oc-room > h2, .oc-home-sec h2 { font-size: clamp(22px, 2.6vw, 30px); }
.oc-home-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 4px 16px; }
.oc-home-head a { font-weight: 700; font-size: .92rem; }
++/* Ask Vibe on the front page (server/web/home.js vibeSection): one line that opens a Vibe chat with the question in it. */
++.oc-vibe-card { margin-top: 32px; padding: 18px 20px; border: 1px solid color-mix(in srgb, var(--oc-accent) 35%, var(--oc-line)); border-radius: 16px; background: color-mix(in srgb, var(--oc-accent) 6%, var(--oc-panel)); }
++.oc-vibe-card p { margin: 0 0 12px; }
++.oc-vibe-ask { display: flex; gap: 8px; }
++.oc-vibe-ask input { flex: 1; min-width: 0; padding: 10px 14px; border-radius: 12px; border: 1px solid var(--oc-line); background: var(--oc-bg); color: var(--oc-text); font: inherit; }
++.oc-vibe-ask input:focus-visible { outline: 2px solid var(--oc-accent); outline-offset: 1px; border-color: transparent; }
.oc-streams, .oc-more { list-style: none; margin: 12px 0 0; padding: 0; display: grid; gap: 10px; grid-template-columns: repeat(auto-fill, minmax(min(260px, 100%), 1fr)); }
.oc-stream, .oc-more a { display: grid; gap: 2px; height: 100%; box-sizing: border-box; padding: 12px 14px; border: 1px solid var(--oc-line); border-radius: 12px; background: var(--oc-panel); color: var(--oc-text); text-decoration: none; }
.oc-stream:hover, .oc-stream:focus-visible, .oc-more a:hover, .oc-more a:focus-visible { border-color: var(--oc-accent); }
ddiff --git a/server/web/home.js b/server/web/home.js
iindex 75767c5..eccc70b 100644
---- a/server/web/home.js
++++ b/server/web/home.js
@@@ -91,6 +91,7 @@ const LIVE = 'https://openvibe.live';
const FAQ = [
{ q: 'Do I need an account to read?', a: 'No. Global chat, public rooms and public servers read without an account. To write, sign in with your free OpenVibe account, the same one every OpenVibe site uses.' },
{ q: 'How do I start a server?', a: 'Sign in, open Servers and give it a name. It starts with text and voice channels; add more, give people roles, and share an invite link. A server can be public or private.' },
++ { q: 'What is Vibe?', a: 'OpenVibe\'s own assistant, free with your account: it searches the web and all of OpenVibe, reads pages and pictures you share, remembers what you ask it to, and sets up work that runs every day for you. Your chats with it are private.' },
{ q: 'What are AI members?', a: 'An AI you add to a room or channel, with a name, a persona and a memory of that room. It answers when someone writes its @name, with OpenVibe\'s model or one from your own AI key.' },
{ q: 'Can I send pictures?', a: 'Yes, in rooms and server channels. A picture goes to OpenVibe.Pics as yours, with its hidden location and camera details removed, and shows inline in the conversation.' },
{ q: 'How many people fit in a call?', a: 'Up to eight in a voice or video call, with mute, camera and kick controls. Call one person from your messages, or open a call room.' },
@@@ -128,6 +129,13 @@ function placesSection(a) {
].join('\n');
}
++/** Ask Vibe from the front page: the question opens a new Vibe chat with it in the box (never sent for you). */
++function vibeSection() {
++ return `<section class="oc-home-sec oc-vibe-card" aria-labelledby="oc-h-vibe"><h2 id="oc-h-vibe">Ask Vibe, OpenVibe's assistant</h2>
++<p class="oc-muted">Free with your account. It searches the web and all of OpenVibe, reads the pages and pictures you share, remembers what you ask it to, and sets up work that runs every day for you. Your chats with it are private.</p>
++<form class="oc-vibe-ask" action="/vibe" method="get" role="search"><label for="oc-vibe-q" class="oc-sr">Ask Vibe</label><input id="oc-vibe-q" name="ask" type="text" maxlength="1200" placeholder="Ask Vibe anything" autocomplete="off" enterkeyhint="go" required><button type="submit" class="oc-button">Ask</button></form></section>`;
++}
++
function stepsSection() {
const steps = [
['Sign in once', 'Your OpenVibe account works on every OpenVibe site, and your chat settings follow it.'],
@@@ -151,9 +159,9 @@ function moreSection() {
return `<section class="oc-home-sec" aria-labelledby="oc-h-more"><h2 id="oc-h-more">More on OpenVibe</h2><ul class="oc-more" role="list">${more.map(([href, name, text]) => `<li><a href="${href}/"><strong>${esc(name)}</strong><span>${esc(text)}</span></a></li>`).join('')}</ul></section>`;
}
--/** Everything around global chat: the numbers, then stream chats, servers and rooms, how it works, questions, more. */
++/** Everything around global chat: the numbers, Ask Vibe, then stream chats, servers and rooms, how it works, questions, more. */
function sections(a) {
-- return { stats: stats(a), streams: streamsSection(a), places: placesSection(a), steps: stepsSection(), faq: faqSection(), more: moreSection() };
++ return { stats: stats(a), vibe: vibeSection(), streams: streamsSection(a), places: placesSection(a), steps: stepsSection(), faq: faqSection(), more: moreSection() };
}
module.exports = { activity, sections, FAQ, _reset: () => { cached = null; } };
ddiff --git a/server/web/pages.js b/server/web/pages.js
iindex 2ecafd9..aa88070 100644
---- a/server/web/pages.js
++++ b/server/web/pages.js
@@@ -253,9 +253,10 @@ function createWebRoutes({ config }) {
page: { view: 'global', latest: latest_id, signedIn, me: signedIn ? actor.user.username : null },
body: `<header class="oc-hero"><p class="oc-eyebrow">OpenVibe.Chat</p>
<h1>Chat with everyone on OpenVibe, or start your own server</h1>
--<p class="oc-lede">Global chat for the whole network, the chat of every stream on OpenVibe.Live, servers with text and voice channels, rooms, private messages, calls of up to eight people, and AI members that join in. One free account, everywhere.</p>
--<p class="oc-hero-actions"><a class="oc-button" href="${signedIn ? '/servers#oc-new-server' : '/auth/login?next=%2Fservers'}">Start a server</a> <a class="oc-button oc-button--quiet" href="/rooms">Rooms</a> <a class="oc-button oc-button--quiet" href="${signedIn ? '/messages' : '/auth/login?next=%2Fmessages'}">Messages</a></p>
++<p class="oc-lede">Vibe, OpenVibe's own assistant, global chat for the whole network, the chat of every stream on OpenVibe.Live, servers with text and voice channels, rooms, private messages, calls of up to eight people, and AI members that join in. One free account, everywhere.</p>
++<p class="oc-hero-actions"><a class="oc-button" href="/vibe">Chat with Vibe</a> <a class="oc-button oc-button--quiet" href="${signedIn ? '/servers#oc-new-server' : '/auth/login?next=%2Fservers'}">Start a server</a> <a class="oc-button oc-button--quiet" href="/rooms">Rooms</a> <a class="oc-button oc-button--quiet" href="${signedIn ? '/messages' : '/auth/login?next=%2Fmessages'}">Messages</a></p>
${parts.stats}</header>
++${parts.vibe}
<section class="oc-room" aria-labelledby="oc-h">
<h2 id="oc-h">Global chat</h2>
<p class="oc-muted">One room for everyone on OpenVibe: streamers, viewers and players, from every site. <span class="oc-live" id="oc-live" hidden>● live</span></p>
ddiff --git a/test/home-vibe.test.js b/test/home-vibe.test.js
nnew file mode 100644
iindex 0000000..f37f2ed
---- /dev/null
++++ b/test/home-vibe.test.js
@@@ -0,0 +1,29 @@
++'use strict';
++/**
++ * Vibe on openvibe.chat's front page (server/web/pages.js, home.js vibeSection; plan track K: "turn openvibe.chat into
++ * the main chat app"): the hero's first action is Chat with Vibe, an Ask Vibe box opens /vibe with the question in its
++ * box (never sent for the person), and the questions answer what Vibe is (in the page's FAQ JSON-LD too).
++ */
++const assert = require('assert');
++const { boot, suite } = require('./helpers');
++
++const t = suite('home vibe');
++let h;
++
++t('boot', async () => { h = await boot({ env: { CHAT_WEB_URL: 'https://openvibe.chat' } }); });
++
++t('the front page leads to Vibe: the first action, an Ask Vibe box, the question in the FAQ', async () => {
++ const page = await fetch(`${h.base}/`).then((r) => r.text());
++ assert.match(page, /<p class="oc-hero-actions"><a class="oc-button" href="\/vibe">Chat with Vibe<\/a> /, 'the first action');
++ assert.match(page, /<form class="oc-vibe-ask" action="\/vibe" method="get" role="search"><label for="oc-vibe-q" class="oc-sr">Ask Vibe<\/label><input id="oc-vibe-q" name="ask" /);
++ assert.ok(page.indexOf('oc-vibe-card') < page.indexOf('id="oc-h">Global chat'), 'before global chat');
++ assert.match(page, /<summary>What is Vibe\?<\/summary>/);
++ assert.match(page, /"name":"What is Vibe\?"/, 'in the FAQ JSON-LD');
++});
++
++t('the box opens Vibe with the question in it, signed out too', async () => {
++ const vibe = await fetch(`${h.base}/vibe?ask=${encodeURIComponent('Plan a picnic for six')}`).then((r) => r.text());
++ assert.match(vibe, /<textarea id="vb-input"[^>]*>Plan a picnic for six<\/textarea>/);
++});
++
++t.run(async () => { if (h && h.close) await h.close(); });