openvibers/openvibe.vip Commits

The shared update system and one account UI, on the OpenVibe Frame (openvibe-shared 1.11.0, openvibe/frame): the home shows what shipped, /updates is the shared log and the footer links it; the shared navbar handles sign in and out (logoutUrl ends this site's session) and carries this site's account links in its menu, while the page's own account bar is only for no-JS visitors

OP OpenVibers committed 24 Sept 2026 · dcb754630a · 4 files, +18 −10


Changes

ddiff --git a/package-lock.json b/package-lock.json
iindex 7b13a23..bad7946 100644
---- a/package-lock.json
++++ b/package-lock.json
@@@ -16,7 +16,7 @@
"jsonwebtoken": "^9.0.2",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.33.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.5.0",
-- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.5.1"
++ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.11.0"
},
"engines": {
"node": ">=22.0.0"
@@@ -1008,9 +1008,9 @@
}
},
"node_modules/openvibe-shared": {
-- "version": "1.5.1",
-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.5.1",
-- "integrity": "sha512-1XWRFwN8br3iiy2Z8fmnXQMgR76VlfMpHjP1xr5uKLCJmtvtNSWO1BgA1kQmVtyBY7g+1n6YFIjahaD4qWbD/Q==",
++ "version": "1.11.0",
++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.11.0",
++ "integrity": "sha512-edMW0I25HNFReLcfZ9ak+4q5sy2t7qq7EPG52tOerTBV2fU994C5we/02AD/9i2Vuq0+ayNBBisW0cg60L6DLQ==",
"license": "MIT",
"dependencies": {
"jsonwebtoken": "^9.0.0"
ddiff --git a/package.json b/package.json
iindex 277cdfa..5741ee1 100644
---- a/package.json
++++ b/package.json
@@@ -28,7 +28,7 @@
"jsonwebtoken": "^9.0.2",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.33.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.5.0",
-- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.5.1"
++ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.11.0"
},
"engines": {
"node": ">=22.0.0"
ddiff --git a/server/web/layout.js b/server/web/layout.js
iindex dbd5fd6..d36831f 100644
---- a/server/web/layout.js
++++ b/server/web/layout.js
@@@ -2,7 +2,7 @@
/**
* Page shell for every server-rendered page: full <head> SEO (title, description, canonical,
-- * robots, Open Graph), the shared OpenVibe chrome (inline critical canvas + app icon from
++ * robots, Open Graph), the shared OpenVibe Frame (inline critical canvas + app icon from
* openvibe-shared, navbar.js from the Network, the SSR footer and a <noscript> navigation), this
* site's stylesheet and its small progressive script. Everything is useful without JavaScript.
*/
@@@ -10,7 +10,7 @@ const crypto = require('crypto');
const fs = require('fs');
const path = require('path');
const appIcon = require('openvibe-shared/app-icon');
--const chrome = require('openvibe-shared/chrome-ssr');
++const frame = require('openvibe-shared/frame');
const SITE_NAME = 'OpenVibe.VIP';
const NETWORK_URL = 'https://openvibe.network';
@@@ -54,7 +54,9 @@ function createLayout({ config, release }) {
silentLogin: `${config.baseUrl}/auth/login?silent=1&next={url}`,
sessionUrl: '/auth/me',
loginUrl: `/auth/login?next=${encodeURIComponent(o.canonicalPath || '/')}`,
++ logoutUrl: '/auth/logout?next={path}', // Sign out in the shared navbar ends this site's session too
};
++ const footer = { service: 'vip', variant: 'full', mount: '#ov-footer', brandName: SITE_NAME, updates: '/updates' };
const jsonLd = (o.jsonLd || []).map((x) => `<script type="application/ld+json">${JSON.stringify(x).replace(/</g, '\\u003c')}</script>`).join('\n');
const who = o.viewer
? `<span class="who">Signed in as <b>${esc(o.viewer.name || o.viewer.username || 'you')}</b> · <a href="/auth/logout?next=/">Sign out</a></span>`
@@@ -82,20 +84,23 @@ ${release ? release.metaTag() : ''}
<link rel="stylesheet" href="${asset('css/vip.css')}">
${jsonLd}
<script src="${NETWORK_URL}/shared/navbar.js" defer></script>
++<script src="${NETWORK_URL}/shared/footer.js" defer></script>
<script src="${asset('js/vip.js')}" defer></script>
</head>
<body>
<div id="navbar-mount"></div>
--${chrome.noscriptNav({ name: SITE_NAME, home: '/', links: NAV_LINKS })}
++${frame.noscriptNav({ name: SITE_NAME, home: '/', links: NAV_LINKS })}
<header class="site-head"><a class="brand" href="/">${SITE_NAME}</a><nav>${NAV_LINKS.map((l) => `<a href="${l.href}"${o.active === l.label.toLowerCase() ? ' aria-current="page"' : ''}>${l.label}</a>`).join('')}</nav>${who}</header>
<main id="main" class="page">
${o.body || ''}
++${o.canonicalPath === '/' && o.active === 'home' ? frame.shipped({ service: 'vip', title: `Recently shipped on ${SITE_NAME}` }) : ''}
</main>
--${chrome.footer({ service: 'vip', variant: 'full' })}
++${frame.footer(footer)}
<script>
--window.__OV_PAGE = ${JSON.stringify({ navbar: nav }).replace(/</g, '\\u003c')};
++window.__OV_PAGE = ${JSON.stringify({ navbar: nav, footer }).replace(/</g, '\\u003c')};
document.addEventListener('DOMContentLoaded', function () {
try { if (window.OpenVibeNavbar) { OpenVibeNavbar.init(window.__OV_PAGE.navbar); document.documentElement.classList.add('ov-has-navbar'); } } catch (e) { /* the SSR header stays */ }
++ try { if (window.OpenVibeFooter) OpenVibeFooter.init(window.__OV_PAGE.footer); } catch (e) { /* */ }
});
</script>
</body>
ddiff --git a/server/web/routes.js b/server/web/routes.js
iindex 105c104..6087f82 100644
---- a/server/web/routes.js
++++ b/server/web/routes.js
@@@ -14,6 +14,7 @@
* domain calls as the API and redirects back with a notice (?ok= / ?error=).
*/
const crypto = require('crypto');
++const frame = require('openvibe-shared/frame');
const express = require('express');
const { VipError, bool } = require('../util');
const { viewerMiddleware } = require('./session');
@@@ -77,6 +78,8 @@ function createWebRoutes({ domain, config, layout, userAuth }) {
res.type('application/xml').send(`<?xml version="1.0" encoding="UTF-8"?>\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n${urls.map((u) => `<url><loc>${u.replace(/&/g, '&amp;')}</loc></url>`).join('\n')}\n</urlset>\n`);
});
++ // What shipped on OpenVibe.VIP: the shared update log every OpenVibe site has.
++ router.get('/updates', (req, res) => send(res, 200, { viewer: req.viewer, canonicalPath: '/updates', title: 'What shipped on OpenVibe.VIP', body: frame.updatesBody({ service: 'vip', siteName: 'OpenVibe.VIP' }) + frame.shippedScript() }));
router.get('/', (req, res) => send(res, 200, {
active: 'home', viewer: req.viewer, canonicalPath: '/',
body: pages.home({ creators: creators.listPublic().map(creators.present) }),