openvibers/openvibe.tips 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 · f5c0783365 · 4 files, +18 −10


Changes

ddiff --git a/package-lock.json b/package-lock.json
iindex c2a028e..f77cd6e 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 64615a1..db62735 100644
---- a/package.json
++++ b/package.json
@@@ -23,7 +23,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 177819b..2bb9d7b 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.Tips';
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: 'tips', 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/tips.css')}">
${jsonLd}
<script src="${NETWORK_URL}/shared/navbar.js" defer></script>
++<script src="${NETWORK_URL}/shared/footer.js" defer></script>
<script src="${asset('js/tips.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: 'tips', title: `Recently shipped on ${SITE_NAME}` }) : ''}
</main>
--${chrome.footer({ service: 'tips', 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 f7a72dc..34835c0 100644
---- a/server/web/routes.js
++++ b/server/web/routes.js
@@@ -22,6 +22,7 @@
* GET /robots.txt, /sitemap.xml
*/
const crypto = require('crypto');
++const frame = require('openvibe-shared/frame');
const express = require('express');
const { TipsError, json: parseJson } = require('../util');
const { viewerMiddleware } = require('./session');
@@@ -66,6 +67,8 @@ function createWebRoutes({ domain, config, layout, userAuth }) {
const creators = db.prepare(`SELECT handle, display_name, avatar_url FROM creator_tip_profiles WHERE page_enabled = 1 ORDER BY updated_at DESC LIMIT 24`).all();
html(res, pageFor(req, { active: 'home', canonicalPath: '/', body: pages.home({ creators }) }));
});
++ // What shipped on OpenVibe.Tips: the shared update log every OpenVibe site has.
++ r.get('/updates', withViewer, (req, res) => html(res, pageFor(req, { canonicalPath: '/updates', title: 'What shipped on OpenVibe.Tips', body: frame.updatesBody({ service: 'tips', siteName: 'OpenVibe.Tips' }) + frame.shippedScript() })));
r.get('/robots.txt', (req, res) => res.type('text/plain').set('Cache-Control', 'public, max-age=3600').send(
`User-agent: *\nAllow: /\nDisallow: /dashboard\nDisallow: /receipts\nDisallow: /overlay/\nDisallow: /moderate/\nDisallow: /auth/\nDisallow: /api/\nDisallow: /internal/\nSitemap: ${config.baseUrl}/sitemap.xml\n`));
r.get('/sitemap.xml', (req, res) => {