openvibers/openvibe.codes 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 · df5e0a4a8b · 5 files, +24 −13


Changes

ddiff --git a/package-lock.json b/package-lock.json
iindex 09fa1c4..6e6a047 100644
---- a/package-lock.json
++++ b/package-lock.json
@@@ -18,7 +18,7 @@
"helmet": "^8.1.0",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.34.2",
"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"
@@@ -1048,9 +1048,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 0ba1df3..45f2db1 100644
---- a/package.json
++++ b/package.json
@@@ -29,7 +29,7 @@
"helmet": "^8.1.0",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.34.2",
"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/app.js b/server/app.js
iindex e0af2de..e7cda1e 100644
---- a/server/app.js
++++ b/server/app.js
@@@ -73,7 +73,7 @@ function createApp(opts = {}) {
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
-- // The shared chrome (theme-loader, navbar) comes from the Network; the inline init is ours.
++ // The OpenVibe Frame (theme-loader, navbar, footer) comes from the Network; the inline init is ours.
scriptSrc: ["'self'", "'unsafe-inline'", 'https://openvibe.network'],
styleSrc: ["'self'", "'unsafe-inline'", 'https://openvibe.network', 'https://fonts.googleapis.com', 'https://cdnjs.cloudflare.com'],
fontSrc: ["'self'", 'data:', 'https://fonts.gstatic.com', 'https://cdnjs.cloudflare.com'],
ddiff --git a/server/http/pages.js b/server/http/pages.js
iindex 6007496..335d0f7 100644
---- a/server/http/pages.js
++++ b/server/http/pages.js
@@@ -11,6 +11,7 @@
* Markdown files as they are.
*/
const fs = require('fs');
++const frame = require('openvibe-shared/frame');
const path = require('path');
const express = require('express');
const { asyncRouter } = require('./router');
@@@ -82,6 +83,8 @@ ${table(['App', 'Kind', 'Version', 'Status', 'Trust', 'Published'], recent.map((
// ── Policy ──────────────────────────────────────────────
const adr = (id) => docs.adrs.find((a) => a.id === id);
++ // What shipped on OpenVibe.Codes: the shared update log every OpenVibe site has.
++ r.get('/updates', (req, res) => page(req, res, { index: true, cache: PUBLIC_CACHE, title: 'What shipped on OpenVibe.Codes', body: raw(frame.updatesBody({ service: 'codes', siteName: 'OpenVibe.Codes' }) + frame.shippedScript()) }));
r.get('/policy', (req, res) => page(req, res, {
index: true, cache: PUBLIC_CACHE, title: 'Policy', crumbs: [{ label: 'Policy' }],
body: html`<h1>Policy</h1><ul class="cards">
ddiff --git a/server/render/layout.js b/server/render/layout.js
iindex 60e0c06..fb0c954 100644
---- a/server/render/layout.js
++++ b/server/render/layout.js
@@@ -3,7 +3,7 @@
/**
* Page shell. Every page is server-rendered through this and is complete without JavaScript:
* - <head>: title, description, canonical, robots (portal pages are noindex), the shared app icon
-- * - the shared chrome: navbar.js and theme-loader.js from the Network (progressive), a <noscript>
++ * - the OpenVibe Frame: navbar.js and theme-loader.js from the Network (progressive), a <noscript>
* navigation bar and the server-rendered shared footer (openvibe-shared)
* - Codes' own script only where a page offers an optional in-browser convenience (webhook
* signature computed locally, copy buttons); every form works without it
@@@ -12,7 +12,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 { html, raw, esc } = require('./html');
const NETWORK_URL = 'https://openvibe.network';
@@@ -55,7 +55,12 @@ function renderPage(o) {
silentLogin: `${o.config.baseUrl}/auth/login?silent=1&next={url}`,
sessionUrl: '/auth/me',
loginUrl: `/auth/login?next=${loginNext}`,
++ logoutUrl: '/auth/logout?next={path}', // Sign out in the shared navbar ends this site's session too
};
++ // This site's own account links live in the shared navbar's account menu (the page's account
++ // bar below is only for visitors without JavaScript).
++ if (signedIn) nav.menu = { before: [...(viewer.staff ? [{ label: 'Staff', href: '/staff', icon: 'fa-shield' }] : []), { label: 'Your projects', href: '/projects', icon: 'fa-folder' }] };
++ const footer = { service: 'codes', variant: 'full', mount: '#ov-footer', brandName: SITE_NAME, updates: '/updates' };
const account = signedIn
? html`Signed in as <strong>${viewer.displayName || viewer.username || 'you'}</strong>${viewer.staff ? html` · <a href="/staff">Staff</a>` : ''} · <a href="/projects">Your projects</a> · <a href="/auth/logout?next=${loginNext}">Sign out</a>`
: html`<a href="/auth/login?next=${loginNext}">Sign in with OpenVibe</a> to manage projects and apps`;
@@@ -78,22 +83,25 @@ ${appIcon.headTags({ site: 'codes' })}
<link rel="stylesheet" href="${asset('css/codes.css')}">
<script src="${NETWORK_URL}/shared/theme-loader.js" defer></script>
<script src="${NETWORK_URL}/shared/navbar.js" defer></script>
++<script src="${NETWORK_URL}/shared/footer.js" defer></script>
${render(scripts)}
</head>
<body>
<a class="skip" href="#main">Skip to content</a>
<div id="navbar-mount"></div>
--${chrome.noscriptNav({ name: SITE_NAME, home: '/', links: NAV })}
--<div class="account-bar" role="navigation" aria-label="Account">${render(account)}</div>
++${frame.noscriptNav({ name: SITE_NAME, home: '/', links: NAV })}
++<noscript><div class="account-bar" role="navigation" aria-label="Account">${render(account)}</div></noscript>
<main id="main" class="page">
${render(crumbs)}
${render(o.body)}
++${o.path === '/' ? frame.shipped({ service: 'codes', title: `Recently shipped on ${SITE_NAME}` }) : ''}
</main>
--${chrome.footer({ service: 'codes', 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); } catch (e) { /* the chrome is optional */ }
++ try { if (window.OpenVibeNavbar) OpenVibeNavbar.init(window.__OV_PAGE.navbar); } catch (e) { /* the Frame is optional */ }
++ try { if (window.OpenVibeFooter) OpenVibeFooter.init(window.__OV_PAGE.footer); } catch (e) { /* */ }
});
</script>
</body>