openvibers/openvibe.shared Commits
Tabs below the fold no longer scroll the page to themselves (4.77.1) (#230)
Changes
@@@ -4,6 +4,12 @@ All notable changes to `openvibe-shared`. Versions follow [semver](https://semvebreaking change to any exported module, browser global or served file name is a new major.A release is the git tag `vX.Y.Z`; consumers pin the tag's tarball (see README).++## 4.77.1 — 2026-11-01++++- **Tabs below the fold no longer scroll the page to themselves.** kit-web.js kept the open tab in view with++ `scrollIntoView({ block: 'nearest' })`, which also scrolled the page when a tab strip sat below the fold (a settings++ section lower down, openvibe.design/kit's samples). Only the strip moves now, sideways.++## 4.77.0 — 2026-11-01- **Talk to Vibe in any browser** (plan L48 V2). Where the browser has no speech recognition of its own (Firefox),@@@ -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.77.0 (every release is a tag; see [CHANGELOG.md](CHANGELOG.md)). Every deployed++**Status:** alpha, v4.77.1 (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.77.0",++ "version": "4.77.1","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)",@@@ -143,7 +143,14 @@d.addEventListener('click', (e) => { if (e.target === d || (e.target.closest && e.target.closest('[data-ovk-close]'))) d.close(); });});// Tabs that scroll sideways keep the open one in view.-- all('.ovk-tabs [aria-current="page"]').forEach((a) => { if (once(a, 'ovkTab') && a.scrollIntoView) a.scrollIntoView({ block: 'nearest', inline: 'center' }); });++ // Only the strip moves, sideways: scrollIntoView would also scroll the page to a strip below the fold.++ all('.ovk-tabs [aria-current="page"]').forEach((a) => {++ if (!once(a, 'ovkTab')) return;++ const strip = a.closest('.ovk-tabs');++ if (!strip || strip.scrollWidth <= strip.clientWidth) return;++ const ar = a.getBoundingClientRect(), sr = strip.getBoundingClientRect();++ strip.scrollLeft += (ar.left + ar.width / 2) - (sr.left + sr.width / 2);++ });// Rails: ‹ › move a page of cards where there is a pointer to click them; each hides at its end.const fine = typeof matchMedia === 'function' && matchMedia('(hover: hover) and (pointer: fine)').matches;const still = () => (typeof matchMedia === 'function' && matchMedia('(prefers-reduced-motion: reduce)').matches) || document.documentElement.getAttribute('data-ov-motion') === 'reduced';@@@ -1,6 +1,6 @@{"name": "openvibe-shared",-- "version": "4.77.0",++ "version": "4.77.1","description": "Shared navbar, footer, themes, icons, legal pages, SEO, auth and analytics for every OpenVibe site","main": "index.js","bin": {@@@ -23,8 +23,11 @@ const BODY = kit.snippet({ code: 'hello', id: 'sn' })+ kit.action({ action: '/gone', label: 'Delete', confirm: 'Delete it?' })+ kit.tool({ head: kit.toolHead({ title: 'Whois Lookup', crumb: { label: 'Network tools' } }),input: kit.toolInput({ name: 'q', label: 'Domain', button: 'Look up', tries: { items: [{ value: 'openvibe.network' }] } }) });++// Tabs far below the fold, the open one off to the right of a narrow strip (4.77.1: scrollIntoView moved the page).++const LOW_TABS = `<div style="height:2400px"></div><div style="width:240px"><style>.ovk-tabs{display:flex;overflow-x:auto;white-space:nowrap}.ovk-tabs a{flex:none;padding:0 18px}</style>${++ kit.tabs({ base: '/s', active: 'h', items: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'].map((k) => ({ key: k, label: `Section ${k}` })) })}</div>`;const PAGE = `<!doctype html><html lang="en"><head><meta charset="utf-8"><title>Kit</title><link rel="icon" href="data:,"></head>--<body>${BODY}<script>++<body>${BODY}${LOW_TABS}<script>window.__asked = []; window.confirm = (m) => { window.__asked.push(m); return false; };window.__sent = [];document.addEventListener('submit', (e) => { if (!e.defaultPrevented) { e.preventDefault(); window.__sent.push([e.target.getAttribute('action'), new FormData(e.target).get('q')]); } });@@@ -47,6 +50,8 @@ const server = http.createServer((req, res) => {const p = await h.openPage(browser, context, { width: 390 });await p.goto(`${base}/`, { maxMs: 5000 });await wait(200);++ assert.strictEqual(await p.evaluate('Math.round(scrollY)'), 0, 'tabs below the fold never scroll the page to themselves');++ assert.ok(await p.evaluate("document.querySelector('.ovk-tabs').scrollLeft") > 0, 'but their strip shows the open tab');const hasClipboard = await p.evaluate('!!navigator.clipboard');if (hasClipboard) assert.strictEqual(await p.evaluate("document.querySelector('[data-ovk-copy]').hidden"), false, 'the copy button shows where the clipboard works');await p.evaluate("document.querySelector('[data-ovk-sheet]').click()");
@@ -4,6 +4,12 @@ All notable changes to `openvibe-shared`. Versions follow [semver](https://semve
@@ -4,6 +4,12 @@ All notable changes to `openvibe-shared`. Versions follow [semver](https://semve
breaking change to any exported module, browser global or served file name is a new major.
breaking change to any exported module, browser global or served file name is a new major.
A release is the git tag `vX.Y.Z`; consumers pin the tag's tarball (see README).
A release is the git tag `vX.Y.Z`; consumers pin the tag's tarball (see README).
+## 4.77.1 — 2026-11-01
+
+- **Tabs below the fold no longer scroll the page to themselves.** kit-web.js kept the open tab in view with
+ `scrollIntoView({ block: 'nearest' })`, which also scrolled the page when a tab strip sat below the fold (a settings
+ section lower down, openvibe.design/kit's samples). Only the strip moves now, sideways.
+
## 4.77.0 — 2026-11-01
## 4.77.0 — 2026-11-01
- **Talk to Vibe in any browser** (plan L48 V2). Where the browser has no speech recognition of its own (Firefox),
- **Talk to Vibe in any browser** (plan L48 V2). Where the browser has no speech recognition of its own (Firefox),
@@ -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.77.0 (every release is a tag; see [CHANGELOG.md](CHANGELOG.md)). Every deployed
+**Status:** alpha, v4.77.1 (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.77.0",
+ "version": "4.77.1",
"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)",
@@ -143,7 +143,14 @@
@@ -143,7 +143,14 @@
d.addEventListener('click', (e) => { if (e.target === d || (e.target.closest && e.target.closest('[data-ovk-close]'))) d.close(); });
d.addEventListener('click', (e) => { if (e.target === d || (e.target.closest && e.target.closest('[data-ovk-close]'))) d.close(); });
});
});
// Tabs that scroll sideways keep the open one in view.
// Tabs that scroll sideways keep the open one in view.
- all('.ovk-tabs [aria-current="page"]').forEach((a) => { if (once(a, 'ovkTab') && a.scrollIntoView) a.scrollIntoView({ block: 'nearest', inline: 'center' }); });
+ // Only the strip moves, sideways: scrollIntoView would also scroll the page to a strip below the fold.
+ all('.ovk-tabs [aria-current="page"]').forEach((a) => {
+ if (!once(a, 'ovkTab')) return;
+ const strip = a.closest('.ovk-tabs');
+ if (!strip || strip.scrollWidth <= strip.clientWidth) return;
+ const ar = a.getBoundingClientRect(), sr = strip.getBoundingClientRect();
+ strip.scrollLeft += (ar.left + ar.width / 2) - (sr.left + sr.width / 2);
+ });
// Rails: ‹ › move a page of cards where there is a pointer to click them; each hides at its end.
// Rails: ‹ › move a page of cards where there is a pointer to click them; each hides at its end.
const fine = typeof matchMedia === 'function' && matchMedia('(hover: hover) and (pointer: fine)').matches;
const fine = typeof matchMedia === 'function' && matchMedia('(hover: hover) and (pointer: fine)').matches;
const still = () => (typeof matchMedia === 'function' && matchMedia('(prefers-reduced-motion: reduce)').matches) || document.documentElement.getAttribute('data-ov-motion') === 'reduced';
const still = () => (typeof matchMedia === 'function' && matchMedia('(prefers-reduced-motion: reduce)').matches) || document.documentElement.getAttribute('data-ov-motion') === 'reduced';
@@ -1,6 +1,6 @@
@@ -1,6 +1,6 @@
{
{
"name": "openvibe-shared",
"name": "openvibe-shared",
- "version": "4.77.0",
+ "version": "4.77.1",
"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": {
@@ -23,8 +23,11 @@ const BODY = kit.snippet({ code: 'hello', id: 'sn' })
@@ -23,8 +23,11 @@ const BODY = kit.snippet({ code: 'hello', id: 'sn' })
+ kit.action({ action: '/gone', label: 'Delete', confirm: 'Delete it?' })
+ kit.action({ action: '/gone', label: 'Delete', confirm: 'Delete it?' })
+ kit.tool({ head: kit.toolHead({ title: 'Whois Lookup', crumb: { label: 'Network tools' } }),
+ kit.tool({ head: kit.toolHead({ title: 'Whois Lookup', crumb: { label: 'Network tools' } }),
input: kit.toolInput({ name: 'q', label: 'Domain', button: 'Look up', tries: { items: [{ value: 'openvibe.network' }] } }) });
input: kit.toolInput({ name: 'q', label: 'Domain', button: 'Look up', tries: { items: [{ value: 'openvibe.network' }] } }) });
+// Tabs far below the fold, the open one off to the right of a narrow strip (4.77.1: scrollIntoView moved the page).
+const LOW_TABS = `<div style="height:2400px"></div><div style="width:240px"><style>.ovk-tabs{display:flex;overflow-x:auto;white-space:nowrap}.ovk-tabs a{flex:none;padding:0 18px}</style>${
+ kit.tabs({ base: '/s', active: 'h', items: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'].map((k) => ({ key: k, label: `Section ${k}` })) })}</div>`;
const PAGE = `<!doctype html><html lang="en"><head><meta charset="utf-8"><title>Kit</title><link rel="icon" href="data:,"></head>
const PAGE = `<!doctype html><html lang="en"><head><meta charset="utf-8"><title>Kit</title><link rel="icon" href="data:,"></head>
-<body>${BODY}<script>
+<body>${BODY}${LOW_TABS}<script>
window.__asked = []; window.confirm = (m) => { window.__asked.push(m); return false; };
window.__asked = []; window.confirm = (m) => { window.__asked.push(m); return false; };
window.__sent = [];
window.__sent = [];
document.addEventListener('submit', (e) => { if (!e.defaultPrevented) { e.preventDefault(); window.__sent.push([e.target.getAttribute('action'), new FormData(e.target).get('q')]); } });
document.addEventListener('submit', (e) => { if (!e.defaultPrevented) { e.preventDefault(); window.__sent.push([e.target.getAttribute('action'), new FormData(e.target).get('q')]); } });
@@ -47,6 +50,8 @@ const server = http.createServer((req, res) => {
@@ -47,6 +50,8 @@ const server = http.createServer((req, res) => {
const p = await h.openPage(browser, context, { width: 390 });
const p = await h.openPage(browser, context, { width: 390 });
await p.goto(`${base}/`, { maxMs: 5000 });
await p.goto(`${base}/`, { maxMs: 5000 });
await wait(200);
await wait(200);
+ assert.strictEqual(await p.evaluate('Math.round(scrollY)'), 0, 'tabs below the fold never scroll the page to themselves');
+ assert.ok(await p.evaluate("document.querySelector('.ovk-tabs').scrollLeft") > 0, 'but their strip shows the open tab');
const hasClipboard = await p.evaluate('!!navigator.clipboard');
const hasClipboard = await p.evaluate('!!navigator.clipboard');
if (hasClipboard) assert.strictEqual(await p.evaluate("document.querySelector('[data-ovk-copy]').hidden"), false, 'the copy button shows where the clipboard works');
if (hasClipboard) assert.strictEqual(await p.evaluate("document.querySelector('[data-ovk-copy]').hidden"), false, 'the copy button shows where the clipboard works');
await p.evaluate("document.querySelector('[data-ovk-sheet]').click()");
await p.evaluate("document.querySelector('[data-ovk-sheet]').click()");