openvibers/openvibe.shared Commits
One page is marked in the navbar after a boost page move (4.57.2) (#209)
Changes
@@@ -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,@@@ -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 deployedconsumer 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` namesthe tag it pins.@@@ -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)",@@@ -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();});@@@ -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": {@@@ -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 };
@@ -6,6 +6,13 @@ A release is the git tag `vX.Y.Z`; consumers pin the tag's tarball (see README).
@@ -6,6 +6,13 @@ A release is the git tag `vX.Y.Z`; consumers pin the tag's tarball (see README).
## Unreleased
## 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
## 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,
- **A tool's terms line wraps cleanly.** The facts in `kit.toolHead({ facts })` are spaced apart, not joined by dots,
@@ -2,7 +2,7 @@
@@ -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.
> 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
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
([docs/migration-plan.md](docs/migration-plan.md) is done); each repository's `package.json` names
the tag it pins.
the tag it pins.
@@ -4,7 +4,7 @@
@@ -4,7 +4,7 @@
"code": true,
"code": true,
"wave": "W2",
"wave": "W2",
"package": "openvibe-shared",
"package": "openvibe-shared",
- "version": "4.57.1",
+ "version": "4.57.2",
"plan": "OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026)",
"plan": "OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026)",
"deployed": false,
"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)",
"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)",
@@ -1972,6 +1972,18 @@
@@ -1972,6 +1972,18 @@
root.addEventListener('resize', later, { passive: true });
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
// 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.
// active-link state, and a login from here returns here.
let _boostHooked = false;
let _boostHooked = false;
@@ -1980,6 +1992,9 @@
@@ -1980,6 +1992,9 @@
_boostHooked = true;
_boostHooked = true;
document.addEventListener('ov:boost:load', () => {
document.addEventListener('ov:boost:load', () => {
if (_config.history) _config.history = Object.assign({}, _config.history, { title: document.title, url: location.href });
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 */ }
try { render(); } catch { /* keep the old bar */ }
if (_config.user) recordHistory();
if (_config.user) recordHistory();
});
});
@@ -1,6 +1,6 @@
@@ -1,6 +1,6 @@
{
{
"name": "openvibe-shared",
"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",
"description": "Shared navbar, footer, themes, icons, legal pages, SEO, auth and analytics for every OpenVibe site",
"main": "index.js",
"main": "index.js",
"bin": {
"bin": {
@@ -18,13 +18,14 @@ if (process.env.OV_SKIP_BROWSER === '1' || !h.findChrome()) {
@@ -18,13 +18,14 @@ if (process.env.OV_SKIP_BROWSER === '1' || !h.findChrome()) {
const ROOT = path.join(__dirname, '..');
const ROOT = path.join(__dirname, '..');
const LINKS = [{ label: 'Home', href: '/' }, { label: 'Two', href: '/two' }, { label: 'Three', href: '/three' }];
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:,">
<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>
<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>
<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>`;
</body></html>`;
-const PAGES = { '/': page('Home'), '/two': page('Two') };
+const PAGES = { '/': page('Home', '/'), '/two': page('Two', '/two') };
const server = http.createServer((req, res) => {
const server = http.createServer((req, res) => {
const u = new URL(req.url, 'http://x');
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]); }
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) => {
@@ -64,13 +65,19 @@ const server = http.createServer((req, res) => {
const pr = await p.evaluate('__ovProbe()');
const pr = await p.evaluate('__ovProbe()');
return { nodes: m.Nodes, listeners: m.JSEventListeners, doc: pr.documentListeners, win: pr.windowListeners };
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 = [];
const samples = [];
for (let lap = 0; lap < 6; lap++) {
for (let lap = 0; lap < 6; lap++) {
assert.strictEqual(await move('to-two'), '/two');
assert.strictEqual(await move('to-two'), '/two');
assert.strictEqual(await move('to-home'), '/');
assert.strictEqual(await move('to-home'), '/');
samples.push(await sample());
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');
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 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 };
const d = { nodes: b.nodes - a.nodes, listeners: b.listeners - a.listeners, doc: b.doc - a.doc, win: b.win - a.win };