openvibers/openvibe.shared Commits

One page is marked in the navbar after a boost page move (4.57.2) (#209)

OP OpenVibers committed 13 days ago · 9e7a6d63e8 · 6 files, +36 −7

A site that marks its links on the server (Codes' workspace) marked them for the page it served; after a boost swap
the old page's link stayed lit beside the new one (Usage and Agent catalogue both lit on openvibe.codes). The navbar
moves a server mark to the link that leads to the new page.

Changes

ddiff --git a/CHANGELOG.md b/CHANGELOG.md
iindex 1af9107..fb6898a 100644
---- a/CHANGELOG.md
++++ b/CHANGELOG.md
@@@ -6,6 +6,13 @@ A release is the git tag `vX.Y.Z`; consumers pin the tag's tarball (see README).
## Unreleased
++## 4.57.2 — 2026-10-31
++
++- **One page is marked in the navbar after a boost page move.** A site that marks its links on the server (Codes'
++ workspace) marked them for the page it served; after `openvibe-shared/boost` swapped in the next page, the old
++ page's link stayed lit beside the new one (Usage and Agent catalogue both lit on openvibe.codes). The navbar now
++ moves a server mark to the link that leads to the new page (or that the new page sits under).
++
## 4.57.1 — 2026-10-31
- **A tool's terms line wraps cleanly.** The facts in `kit.toolHead({ facts })` are spaced apart, not joined by dots,
ddiff --git a/README.md b/README.md
iindex 7ab9732..c056355 100644
---- a/README.md
++++ b/README.md
@@@ -2,7 +2,7 @@
> Versioned UI, the OpenVibe Frame (the navbar, footer and "shipped" views every site sits in), SEO, legal and release-client packages every OpenVibe site renders.
--**Status:** alpha, v4.57.1 (every release is a tag; see [CHANGELOG.md](CHANGELOG.md)). Every deployed
++**Status:** alpha, v4.57.2 (every release is a tag; see [CHANGELOG.md](CHANGELOG.md)). Every deployed
consumer installs a tagged release and none keeps a vendored copy
([docs/migration-plan.md](docs/migration-plan.md) is done); each repository's `package.json` names
the tag it pins.
ddiff --git a/STATUS.json b/STATUS.json
iindex 91a409f..71405e5 100644
---- a/STATUS.json
++++ b/STATUS.json
@@@ -4,7 +4,7 @@
"code": true,
"wave": "W2",
"package": "openvibe-shared",
-- "version": "4.57.1",
++ "version": "4.57.2",
"plan": "OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026)",
"deployed": false,
"deployment": "none of its own: a package released as git tags; OpenVibe.Network serves /shared/* from its pinned tag, and a site can serve its own pinned copy (openvibe-shared/serve)",
ddiff --git a/navbar.js b/navbar.js
iindex 497192d..05b9b59 100644
---- a/navbar.js
++++ b/navbar.js
@@@ -1972,6 +1972,18 @@
root.addEventListener('resize', later, { passive: true });
}
++ // The page a link leads to is this page, or this page sits under it (/tasks marks /tasks/42; / marks only /).
++ const leadsHere = (href, path) => {
++ let p = null;
++ try { p = new URL(href, location.href); } catch { return false; }
++ if (p.origin !== location.origin) return false;
++ return p.pathname === path || (p.pathname !== '/' && path.startsWith(`${p.pathname.replace(/\/+$/, '')}/`));
++ };
++ const remark = (list, path) => list.map((l) => {
++ const out = l.active === undefined ? l : Object.assign({}, l, { active: leadsHere(l.href || '', path) });
++ return Array.isArray(l.children) ? Object.assign({}, out, { children: remark(l.children, path) }) : out;
++ });
++
// openvibe-shared/boost swapped the page in place: this is a new page for the history and the
// active-link state, and a login from here returns here.
let _boostHooked = false;
@@@ -1980,6 +1992,9 @@
_boostHooked = true;
document.addEventListener('ov:boost:load', () => {
if (_config.history) _config.history = Object.assign({}, _config.history, { title: document.title, url: location.href });
++ // A site that marked its links on the server marked them for the page it served; after a swap the mark moves
++ // to the new page, or the old page's link stays lit beside the new one (Codes, 2026-10-30).
++ if (Array.isArray(_config.links)) _config.links = remark(_config.links, location.pathname || '/');
try { render(); } catch { /* keep the old bar */ }
if (_config.user) recordHistory();
});
ddiff --git a/package.json b/package.json
iindex 536c0ed..c9584e2 100644
---- a/package.json
++++ b/package.json
@@@ -1,6 +1,6 @@
{
"name": "openvibe-shared",
-- "version": "4.57.1",
++ "version": "4.57.2",
"description": "Shared navbar, footer, themes, icons, legal pages, SEO, auth and analytics for every OpenVibe site",
"main": "index.js",
"bin": {
ddiff --git a/test/boost-navbar-chrome.test.js b/test/boost-navbar-chrome.test.js
iindex 0cdd167..a3afa42 100644
---- a/test/boost-navbar-chrome.test.js
++++ b/test/boost-navbar-chrome.test.js
@@@ -18,13 +18,14 @@ if (process.env.OV_SKIP_BROWSER === '1' || !h.findChrome()) {
const ROOT = path.join(__dirname, '..');
const LINKS = [{ label: 'Home', href: '/' }, { label: 'Two', href: '/two' }, { label: 'Three', href: '/three' }];
--const page = (title) => `<!doctype html><html lang="en"><head><meta charset="utf-8"><title>${title}</title>
++// The site marks its links on the server, for the page it serves (Codes does): a swap must move the mark.
++const page = (title, here) => `<!doctype html><html lang="en"><head><meta charset="utf-8"><title>${title}</title>
<meta name="ov-boost" content="site@r1"><link rel="icon" href="data:,">
<script src="/shared/navbar.js" defer></script><script src="/shared/boost.js" data-main="#main" defer></script></head>
<body><div id="navbar-mount"></div><main id="main"><h1>${title}</h1><p><a id="to-home" href="/">Home</a> <a id="to-two" href="/two">Two</a></p></main>
--<script>document.addEventListener('DOMContentLoaded', function () { OpenVibeNavbar.init({ service: 'site', user: null, links: ${JSON.stringify(LINKS)}, networkLinks: false, countViews: false, notifications: false }); });</script>
++<script>document.addEventListener('DOMContentLoaded', function () { OpenVibeNavbar.init({ service: 'site', user: null, links: ${JSON.stringify(LINKS.map((l) => ({ ...l, active: l.href === here })))}, networkLinks: false, countViews: false, notifications: false }); });</script>
</body></html>`;
--const PAGES = { '/': page('Home'), '/two': page('Two') };
++const PAGES = { '/': page('Home', '/'), '/two': page('Two', '/two') };
const server = http.createServer((req, res) => {
const u = new URL(req.url, 'http://x');
if (PAGES[u.pathname]) { res.writeHead(200, { 'content-type': 'text/html; charset=utf-8', 'cache-control': 'no-store' }); return res.end(PAGES[u.pathname]); }
@@@ -64,13 +65,19 @@ const server = http.createServer((req, res) => {
const pr = await p.evaluate('__ovProbe()');
return { nodes: m.Nodes, listeners: m.JSEventListeners, doc: pr.documentListeners, win: pr.windowListeners };
};
++ const lit = () => p.evaluate('[...document.querySelectorAll("nav.openvibe-navbar .ovnav-link.active")].map((a) => a.getAttribute("href")).filter((h, i, all) => all.indexOf(h) === i)');
++ assert.deepStrictEqual(await lit(), ['/'], 'the served page marks Home');
++ assert.strictEqual(await move('to-two'), '/two');
++ assert.deepStrictEqual(await lit(), ['/two'], 'after a swap only the new page is marked, not the old one beside it');
++ assert.strictEqual(await move('to-home'), '/');
++ assert.deepStrictEqual(await lit(), ['/'], 'and back');
const samples = [];
for (let lap = 0; lap < 6; lap++) {
assert.strictEqual(await move('to-two'), '/two');
assert.strictEqual(await move('to-home'), '/');
samples.push(await sample());
}
-- assert.strictEqual(await p.evaluate('OVBoost.controller.stats().moves'), 12, 'every move was a boost swap (no page load)');
++ assert.strictEqual(await p.evaluate('OVBoost.controller.stats().moves'), 14, 'every move was a boost swap (no page load)');
assert.strictEqual(await p.evaluate('document.querySelectorAll("nav.openvibe-navbar").length'), 1, 'one bar on the page');
const a = samples[1], b = samples[samples.length - 1];
const d = { nodes: b.nodes - a.nodes, listeners: b.listeners - a.listeners, doc: b.doc - a.doc, win: b.win - a.win };