openvibers/openvibe.community Commits

Re-sync vendored openvibe-shared with the Network's current copy

OP OpenVibers committed 18 Sept 2026 · ff4a6b6f9d · 5 files, +177 −13

Picks up the community entries (footer network column, hostname-derived
OpenVibe.Community brand, ov-mark variant) and the verbiage pass that drops the
"free" claims from the shared brand strings.

Changes

ddiff --git a/vendor/openvibe-shared/brand.js b/vendor/openvibe-shared/brand.js
iindex 61ab011..bfa2a1a 100644
---- a/vendor/openvibe-shared/brand.js
++++ b/vendor/openvibe-shared/brand.js
@@@ -19,7 +19,7 @@ const BRAND_DEFAULTS = Object.freeze({
gamesServiceName: 'OpenVibe.Games',
mediaServiceName: 'OpenVibe.Media',
tagline: 'One Account. All of OpenVibe.',
-- campTagline: 'Free & Open Live Streaming',
++ campTagline: 'Open Live Streaming, Community Run',
toolsSubdomainBase: 'openvibe.tools',
discord: 'https://discord.gg/M6MuRUaeJj',
github: 'https://github.com/OpenVibe.Live',
@@@ -99,8 +99,8 @@ function buildBrandFromRegistry(registry = {}, env = process.env) {
// Build service list using registry URLs where available
const services = [
-- { id: 'live', name: streamerServiceName, url: liveUrl, description: 'Free & Open Live Streaming' },
-- { id: 'tools', name: toolsServiceName, url: toolsUrl, description: 'Free Online Tools' },
++ { id: 'live', name: streamerServiceName, url: liveUrl, description: 'Open live streaming, community run' },
++ { id: 'tools', name: toolsServiceName, url: toolsUrl, description: 'Online tools for everyone' },
{ id: 'games', name: gamesServiceName, url: gamesUrl, description: 'Games & Community Canvas' },
{ id: 'media', name: mediaServiceName, url: mediaUrl, description: 'VODs, Clips, Pastes & Files' },
{ id: 'network', name: BRAND_DEFAULTS.networkServiceName, url: networkUrl, description: 'Accounts, SSO & OpenCoins' },
@@@ -155,8 +155,8 @@ const BRAND = Object.freeze({
// Primary services in the network (for dashboard display, health checks, etc.)
// Static list with OpenVibe defaults — use buildBrandFromRegistry() for live values.
services: Object.freeze([
-- { id: 'live', name: 'OpenVibe.Live', url: 'https://openvibe.live', description: 'Free & Open Live Streaming' },
-- { id: 'tools', name: 'OpenVibe.Tools', url: 'https://openvibe.tools', description: 'Free Online Tools' },
++ { id: 'live', name: 'OpenVibe.Live', url: 'https://openvibe.live', description: 'Open live streaming, community run' },
++ { id: 'tools', name: 'OpenVibe.Tools', url: 'https://openvibe.tools', description: 'Online tools for everyone' },
{ id: 'games', name: 'OpenVibe.Games', url: 'https://openvibe.games', description: 'Games & Community Canvas' },
{ id: 'media', name: 'OpenVibe.Media', url: 'https://openvibe.media', description: 'VODs, Clips, Pastes & Files' },
{ id: 'network', name: 'OpenVibe.Network', url: 'https://openvibe.network', description: 'Accounts, SSO & OpenCoins' },
ddiff --git a/vendor/openvibe-shared/footer.js b/vendor/openvibe-shared/footer.js
iindex bccc6a1..ed13bbd 100644
---- a/vendor/openvibe-shared/footer.js
++++ b/vendor/openvibe-shared/footer.js
@@@ -34,7 +34,7 @@
// the link, with no edit anywhere else.
const NETWORK = [
{ id: 'live', name: 'Live streaming', url: 'https://openvibe.live', desc: 'Go live, restream and clip' },
-- { id: 'tools', name: 'Free online tools', url: 'https://openvibe.tools', desc: 'The whole toolbox' },
++ { id: 'tools', name: 'Online tools', url: 'https://openvibe.tools', desc: 'The whole toolbox' },
{ id: 'dev', name: 'Developer tools', url: 'https://dev.openvibe.tools', desc: 'Formatters, encoders, validators' },
{ id: 'net', name: 'Network diagnostics', url: 'https://net.openvibe.tools', desc: 'Ping, DNS, Whois, SSL' },
{ id: 'img', name: 'Image converter', url: 'https://img.openvibe.tools', desc: 'Convert, resize, compress' },
@@@ -42,7 +42,11 @@
{ id: 'docs', name: 'PDF & document tools', url: 'https://docs.openvibe.tools', desc: 'Merge, split, compress' },
{ id: 'text', name: 'Text tools', url: 'https://text.openvibe.tools', desc: 'Fancy text, case, ASCII' },
{ id: 'yt', name: 'YouTube downloader', url: 'https://yt.openvibe.tools', desc: 'Video and audio' },
++ { id: 'community', name: 'Community & pastes', url: 'https://openvibe.community', desc: 'Pastes, posts and the people of OpenVibe' },
++ { id: 'games', name: 'Games', url: 'https://openvibe.games', desc: 'Scraplandia and browser games' },
++ { id: 'media', name: 'Media', url: 'https://openvibe.media', desc: 'VODs, clips and files for every site' },
{ id: 'network', name: 'Account & themes', url: NETWORK_URL, desc: 'One account, every site' },
++ { id: 'more', name: 'The whole network →', url: NETWORK_URL + '/#network', desc: 'Every OpenVibe site, live and upcoming' },
];
const LEGAL = [
@@@ -59,7 +63,7 @@
const DEFAULTS = {
service: null, // 'live' | 'tools' | 'dev' | … (auto-detected when omitted)
brandName: null,
-- tagline: 'Free, open source and community run. Built in the open by the people using it.',
++ tagline: 'Open source and community run. Built in the open by the people using it.',
variant: 'full',
mount: '#ov-footer',
links: [], // [{ heading, items: [{ name, url, onclick? }] }]
@@@ -84,9 +88,17 @@
return sub === h ? 'tools' : (sub || 'tools');
}
++ const TLD_LABELS = { live: 'Live', tools: 'Tools', network: 'Network', media: 'Media', games: 'Games', community: 'Community', chat: 'Chat', codes: 'Codes', blog: 'Blog', wiki: 'Wiki', news: 'News', reviews: 'Reviews', tips: 'Tips', vip: 'VIP', trade: 'Trade', host: 'Host', deals: 'Deals', coupons: 'Coupons' };
++ /** The full site name from the hostname — the same spelling the navbar uses (Pastes.OpenVibe.Tools). */
function brandFor(service) {
++ const h = typeof location !== 'undefined' ? location.hostname.toLowerCase() : '';
++ const cap = (w) => w ? w.charAt(0).toUpperCase() + w.slice(1) : '';
++ let m = h.match(/^(?:(.+)\.)?openvibe\.([a-z]+)$/);
++ if (m) { const sub = m[1] && m[1] !== 'www' ? m[1] : null; return [sub ? (sub.length <= 4 ? sub.toUpperCase() : cap(sub)) : null, 'OpenVibe', TLD_LABELS[m[2]] || cap(m[2])].filter(Boolean).join('.'); }
++ if ((m = h.match(/^(?:(.+)\.)?openre\.stream$/))) return 'OpenRe.Stream';
const found = NETWORK.find(n => n.id === service);
-- if (found) return found.name === 'Account & themes' ? 'OpenVibe' : found.name;
++ if (service && TLD_LABELS[service]) return `OpenVibe.${TLD_LABELS[service]}`;
++ if (found && found.name !== 'Account & themes' && found.name !== 'The whole network →') return `${cap(service)}.OpenVibe.Tools`;
return 'OpenVibe';
}
ddiff --git a/vendor/openvibe-shared/navbar.js b/vendor/openvibe-shared/navbar.js
iindex 8402a57..47afb98 100644
---- a/vendor/openvibe-shared/navbar.js
++++ b/vendor/openvibe-shared/navbar.js
@@@ -896,11 +896,11 @@
items.slice(0, 4).map(h => `<a class="item" href="${escapeAttr(h.url)}"><span class="icon"><i class="fa-solid ${escapeAttr(h.icon || 'fa-clock-rotate-left')}"></i></span><span class="t">${escapeAttr(h.title || h.url)}</span><span class="s">${escapeAttr(h.service_label || h.service || '')}</span></a>`).join('');
};
const H = root.OpenVibeHistory;
-- if (H && typeof H.recent === 'function') { H.recent({ limit: 4, token: _config.token }).then(draw).catch(() => draw(null)); return; }
++ if (H && typeof H.recent === 'function') { H.recent({ limit: 4, token: _config.token, apiBase: _config.apiBase }).then(draw).catch(() => draw(null)); return; }
if (document.getElementById('ov-history-loader')) return;
const sc = document.createElement('script'); sc.id = 'ov-history-loader'; sc.async = true;
sc.src = `${_config.apiBase}/shared/history.js`;
-- sc.onload = () => { try { root.OpenVibeHistory.recent({ limit: 4, token: _config.token }).then(draw).catch(() => draw(null)); } catch { /* */ } };
++ sc.onload = () => { try { root.OpenVibeHistory.recent({ limit: 4, token: _config.token, apiBase: _config.apiBase }).then(draw).catch(() => draw(null)); } catch { /* */ } };
document.head.appendChild(sc);
}
@@@ -912,8 +912,7 @@
const svc = _config.service;
const brand = resolveBrand();
-- nav.setAttribute('data-compact', _config.compact || 'auto');
-- nav.setAttribute('data-service', svc);
++ if (typeof nav.setAttribute === 'function') { nav.setAttribute('data-compact', _config.compact || 'auto'); nav.setAttribute('data-service', svc); }
const links = currentLinks();
const u = _config.user;
ddiff --git a/vendor/openvibe-shared/ov-mark.js b/vendor/openvibe-shared/ov-mark.js
iindex 2c018cf..5e5bf5b 100644
---- a/vendor/openvibe-shared/ov-mark.js
++++ b/vendor/openvibe-shared/ov-mark.js
@@@ -2,6 +2,7 @@
* ov-mark.js — the OpenVibe "OV" brand mark as a drop-in.
* <span class="ov-mark"></span> → 32px animated mark
* <span class="ov-mark" data-size="56"> → any size; add data-static="1" for no motion
++ * <span class="ov-mark" data-variant="live"> → the site's own twist on the motion (see VARIANTS)
* Self-contained: injects its own CSS once, works on any page (Live, Network, static pages).
* The O is a ring with a comet arc running around it, the V is drawn inside with a light
* sweep, and a dot orbits the ring. Transform/opacity/stroke motion only.
@@@ -17,7 +18,30 @@
.ov-mark .r{fill:none;stroke:currentColor;stroke-width:4;opacity:.28}
.ov-mark .c{fill:none;stroke-width:4;stroke-linecap:round;stroke-dasharray:34 79;transform-box:fill-box;transform-origin:center;animation:ovmComet 3.6s linear infinite}
.ov-mark .v{fill:none;stroke-width:4.6;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:46;animation:ovmDraw 1.1s cubic-bezier(.2,.8,.2,1) both}
--.ov-mark .d{fill:#fff;transform-box:fill-box;transform-origin:center;animation:ovmDot 2.4s ease-in-out infinite}
++.ov-mark .d{fill:var(--ovm-dot,#fff);transform-box:fill-box;transform-origin:center;animation:ovmDot 2.4s ease-in-out infinite}
++/* Per-site variants: one family, each site with its own motion so the network reads as one brand
++ with many rooms. Colours stay with the theme (currentColor); only the dot and the rhythm change. */
++.ov-mark[data-variant=live]{--ovm-dot:#ef4444}
++.ov-mark[data-variant=live] .d{animation:ovmOnAir 1.4s ease-in-out infinite}
++.ov-mark[data-variant=tools] .c{animation-duration:2.2s;stroke-dasharray:22 91}
++.ov-mark[data-variant=tools] .o2{display:none}
++.ov-mark[data-variant=media] .c{stroke-dasharray:14 14 14 71;animation-duration:4.4s}
++.ov-mark[data-variant=games] svg{animation:ovmBounce 1.6s cubic-bezier(.3,1.4,.4,1) infinite}
++.ov-mark[data-variant=games] .d{animation-duration:1.6s}
++.ov-mark[data-variant=community] .o2{opacity:.9;r:1.8}
++.ov-mark[data-variant=community] .c{animation-direction:reverse}
++.ov-mark[data-variant=network] .g{animation-duration:2.4s}
++.ov-mark[data-variant=chat] .d{animation:ovmDot 1s ease-in-out infinite}
++.ov-mark[data-variant=stream] .c{stroke-dasharray:60 53;animation-duration:2.8s}
++.ov-mark[data-variant=codes] .v{animation:ovmDraw 1.1s cubic-bezier(.2,.8,.2,1) infinite alternate}
++.ov-mark[data-variant=blog] .c,.ov-mark[data-variant=wiki] .c,.ov-mark[data-variant=news] .c{animation-duration:6s}
++.ov-mark[data-variant=vip]{--ovm-dot:#facc15}
++.ov-mark[data-variant=tips]{--ovm-dot:#22c55e}
++.ov-mark[data-variant=deals],.ov-mark[data-variant=coupons]{--ovm-dot:#f59e0b}
++.ov-mark[data-variant=trade] .c{animation-timing-function:cubic-bezier(.4,0,.2,1)}
++.ov-mark[data-variant=host] svg{animation:none}
++@keyframes ovmOnAir{0%,100%{transform:scale(1);opacity:.7}50%{transform:scale(1.7);opacity:1}}
++@keyframes ovmBounce{0%,100%{transform:translateY(0)}50%{transform:translateY(-2.5px)}}
.ov-mark .o{fill:#fff;filter:drop-shadow(0 0 3px currentColor)}
.ov-mark .o2{opacity:.5}
.ov-mark:hover svg,a:hover>.ov-mark svg,button:hover>.ov-mark svg{animation:ovmSpin .9s cubic-bezier(.2,1.5,.3,1) 1}
ddiff --git a/vendor/openvibe-shared/test/navbar-brand.test.js b/vendor/openvibe-shared/test/navbar-brand.test.js
nnew file mode 100644
iindex 0000000..7715653
---- /dev/null
++++ b/vendor/openvibe-shared/test/navbar-brand.test.js
@@@ -0,0 +1,129 @@
++'use strict';
++// navbar.js — brand resolution from the hostname, compact/short form, overrides and the
++// modular menu/links API (plain node with the same stubbed globals as navbar-auth.test.js).
++const assert = require('assert');
++const path = require('path');
++const NAVBAR_PATH = path.join(__dirname, '..', 'navbar.js');
++
++function stubElement() {
++ const el = {
++ style: {}, textContent: '', className: '', id: '', innerHTML: '', dataset: {}, attrs: {}, children: [],
++ classList: { add() {}, remove() {}, toggle() {} },
++ setAttribute(k, v) { this.attrs[k] = String(v); }, getAttribute(k) { return this.attrs[k] ?? null; },
++ appendChild(c) { this.children.push(c); }, prepend(c) { this.children.unshift(c); }, remove() {}, addEventListener() {},
++ querySelector() { return stubElement(); }, querySelectorAll() { return []; },
++ };
++ return el;
++}
++
++function load(hostname, opts = {}) {
++ global.location = { hostname, href: `https://${hostname}/`, pathname: opts.pathname || '/', protocol: 'https:' };
++ global.window = { location: global.location };
++ try { Object.defineProperty(global, 'navigator', { value: { userAgent: 'test' }, configurable: true }); } catch { /* read-only in some runtimes */ }
++ const created = [];
++ global.document = {
++ cookie: opts.cookie || '', head: stubElement(), body: stubElement(), title: 'T',
++ getElementById: () => null, createElement: (tag) => { const e = stubElement(); e.tag = tag; created.push(e); return e; },
++ addEventListener() {}, removeEventListener() {}, dispatchEvent() {},
++ };
++ global.CustomEvent = class { constructor(t, i) { this.type = t; this.detail = i && i.detail; } };
++ const store = {};
++ global.localStorage = { getItem: (k) => (k in store ? store[k] : null), setItem: (k, v) => { store[k] = String(v); }, removeItem: (k) => { delete store[k]; } };
++ global.sessionStorage = { getItem: () => null, setItem() {}, removeItem() {} };
++ global.fetch = async () => ({ ok: false, status: 404, json: async () => ({}) });
++ delete require.cache[require.resolve(NAVBAR_PATH)];
++ const navbar = require(NAVBAR_PATH);
++ return { navbar, created };
++}
++
++// ── hostname → segments ──────────────────────────────────────
++{
++ const { navbar } = load('pastes.openvibe.tools');
++ navbar.init({ service: 'paste', user: { id: 1, username: 'x' } });
++ const b = navbar.brand();
++ assert.strictEqual(b.name, 'Pastes.OpenVibe.Tools');
++ assert.strictEqual(b.short, 'Pastes');
++ assert.strictEqual(b.tld, 'tools');
++ assert.strictEqual(b.variant, 'tools');
++}
++{
++ const { navbar } = load('json.openvibe.tools');
++ navbar.init({ service: 'dev' });
++ assert.strictEqual(navbar.brand().name, 'JSON.OpenVibe.Tools', 'known acronyms keep their casing');
++}
++{
++ const { navbar } = load('mergepdf.openvibe.tools');
++ navbar.init({ service: 'docs' });
++ assert.strictEqual(navbar.brand().name, 'MergePDF.OpenVibe.Tools');
++}
++{
++ const { navbar } = load('openvibe.live');
++ navbar.init({ service: 'live' });
++ const b = navbar.brand();
++ assert.strictEqual(b.name, 'OpenVibe.Live');
++ assert.strictEqual(b.short, 'OpenVibe.Live', 'no subdomain: nothing to shorten to');
++ assert.strictEqual(b.variant, 'live');
++}
++{
++ const { navbar } = load('www.openvibe.community');
++ navbar.init({ service: 'community' });
++ assert.strictEqual(navbar.brand().name, 'OpenVibe.Community', 'www is not a subdomain brand');
++}
++{
++ const { navbar } = load('play.openvibe.games');
++ navbar.init({ service: 'games' });
++ assert.strictEqual(navbar.brand().name, 'Play.OpenVibe.Games');
++}
++{
++ const { navbar } = load('ingest.openre.stream');
++ navbar.init({ service: 'live' });
++ const b = navbar.brand();
++ assert.strictEqual(b.name, 'Ingest.OpenRe.Stream');
++ assert.strictEqual(b.variant, 'stream');
++}
++{
++ const { navbar } = load('openvibe.vip');
++ navbar.init({ service: 'network' });
++ assert.strictEqual(navbar.brand().name, 'OpenVibe.VIP');
++}
++// ── off-network hosts fall back to the service id ────────────
++{
++ const { navbar } = load('localhost');
++ navbar.init({ service: 'net' });
++ assert.strictEqual(navbar.brand().name, 'Net.OpenVibe.Tools');
++ const { navbar: n2 } = load('localhost');
++ n2.init({ service: 'live' });
++ assert.strictEqual(n2.brand().name, 'OpenVibe.Live');
++}
++// ── overrides ────────────────────────────────────────────────
++{
++ const { navbar } = load('openvibe.tools');
++ navbar.init({ service: 'tools', brandName: 'Paste.OpenVibe' });
++ assert.strictEqual(navbar.brand().name, 'Pastes.OpenVibe.Tools', 'legacy brandName steers the subdomain segment (paste → Pastes, the host name)');
++ const { navbar: n2 } = load('openvibe.tools');
++ n2.init({ service: 'tools', brand: { sub: 'logo', tag: 'beta' } });
++ assert.strictEqual(n2.brand().name, 'Logo.OpenVibe.Tools');
++ assert.strictEqual(n2.brand().tag, 'beta');
++ const { navbar: n3 } = load('openvibe.tools');
++ n3.init({ service: 'tools', brand: { name: 'Custom Name', variant: 'games' } });
++ assert.strictEqual(n3.brand().name, 'Custom Name');
++ assert.strictEqual(n3.brand().variant, 'games');
++}
++// ── rendered markup carries the segments, compact attr and the variant ────
++{
++ const { navbar, created } = load('pastes.openvibe.tools', { pathname: '/mine' });
++ navbar.init({ service: 'paste', user: { id: 1, username: 'x' }, links: [{ label: 'Mine', href: '/mine' }, { label: 'New', href: '/new', icon: 'fa-plus' }], menu: { after: [{ id: 'mp', label: 'My pastes', href: '/my', icon: 'fa-paste' }] }, compact: 'always' });
++ const nav = created.find(e => e.tag === 'nav');
++ assert.ok(nav, 'nav element created');
++ assert.strictEqual(nav.getAttribute('data-compact'), 'always');
++ assert.ok(nav.innerHTML.includes('class="b-sub">Pastes<') && nav.innerHTML.includes('class="b-tld">Tools<'), 'segments rendered');
++ assert.ok(nav.innerHTML.includes('data-variant="tools"'), 'ov-mark variant');
++ assert.ok(nav.innerHTML.includes('href="/mine" class="active"'), 'current path marked active');
++ assert.ok(nav.innerHTML.includes('fa-plus icon'), 'link icon rendered');
++ const dd = created.find(e => e.tag === 'div' && /openvibe-navbar-dropdown-menu/.test(e.innerHTML));
++ assert.ok(dd && dd.innerHTML.includes('data-menu-id="mp"') && dd.innerHTML.includes('My pastes'), 'custom dropdown row rendered');
++ assert.ok(dd.innerHTML.includes('/my#history'), 'History link present');
++ const id = navbar.addMenuItem({ label: 'Runtime row', href: '/r', position: 'before' });
++ assert.ok(id, 'addMenuItem returns an id');
++}
++console.log('navbar brand: all checks passed');