openvibers/openvibe.shared Commits

4.46.1: six or nine apps are three a row (#188)

OP OpenVibers committed 15 days ago · 59726612da · 7 files, +19 −7

showcase.apps left one tile alone on the last row for nine (4 + 4 + 1, on a
phone and on wide screens); six, nine and fifteen now get sc-apps--c3.

Changes

ddiff --git a/CHANGELOG.md b/CHANGELOG.md
iindex 9f6b885..bc15cbe 100644
---- a/CHANGELOG.md
++++ b/CHANGELOG.md
@@@ -6,6 +6,11 @@ A release is the git tag `vX.Y.Z`; consumers pin the tag's tarball (see README).
## Unreleased
++## 4.46.1 — 2026-10-29
++
++- `showcase.apps`: a set of six or nine is three a row (`sc-apps--c3`), on a phone and from 600 px. Four columns left one
++ tile alone on the last row (nine was 4 + 4 + 1).
++
## 4.46.0 — 2026-10-29
- **Touch targets from the fleet audit** (effective hit areas at 390 px: a 7x7 grid of `elementFromPoint` probes over
ddiff --git a/README.md b/README.md
iindex dd83004..bfd13c9 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.46.0 (every release is a tag; see [CHANGELOG.md](CHANGELOG.md)). Every deployed
++**Status:** alpha, v4.46.1 (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 f77fdd6..6c13c92 100644
---- a/STATUS.json
++++ b/STATUS.json
@@@ -4,7 +4,7 @@
"code": true,
"wave": "W2",
"package": "openvibe-shared",
-- "version": "4.46.0",
++ "version": "4.46.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)",
ddiff --git a/package-lock.json b/package-lock.json
iindex 77b8858..d5bbe2b 100644
---- a/package-lock.json
++++ b/package-lock.json
@@@ -1,12 +1,12 @@
{
"name": "openvibe-shared",
-- "version": "4.46.0",
++ "version": "4.46.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "openvibe-shared",
-- "version": "4.46.0",
++ "version": "4.46.1",
"license": "MIT",
"dependencies": {
"jsonwebtoken": "^9.0.0",
ddiff --git a/package.json b/package.json
iindex 2ba0404..55eba4b 100644
---- a/package.json
++++ b/package.json
@@@ -1,6 +1,6 @@
{
"name": "openvibe-shared",
-- "version": "4.46.0",
++ "version": "4.46.1",
"description": "Shared navbar, footer, themes, icons, legal pages, SEO, auth and analytics for every OpenVibe site",
"main": "index.js",
"bin": {
ddiff --git a/showcase.js b/showcase.js
iindex 66c9b6c..596b268 100644
---- a/showcase.js
++++ b/showcase.js
@@@ -353,7 +353,8 @@ function apps({ id: sid, title = '', lede = '', groups = null, items = null, mor
if (!sets.length) return '';
const h = `sc-${slug(title || 'apps')}`;
const tile = (x) => `<li><a href="${esc(safeHref(x.href))}"><span class="sc-ic sc-ic-ov">${icon(x.icon || 'ov:spark', 40)}</span><span class="sc-app-t"><b>${esc(x.name)}</b>${x.text ? `<small>${esc(x.text)}</small>` : ''}</span></a></li>`;
-- const cols = (n) => (n >= 2 && n <= 4 ? n : n % 4 === 0 ? 4 : 0);
++ // Six or nine are three a row (4.46.1): four columns left one tile alone on the last row.
++ const cols = (n) => (n >= 2 && n <= 4 ? n : n % 4 === 0 ? 4 : n % 3 === 0 ? 3 : 0);
const body = sets.map((g) => `${g.name ? `<h3 class="sc-apps-group">${esc(g.name)}</h3>` : ''}<ul class="sc-apps${cols(g.items.length) ? ` sc-apps--c${cols(g.items.length)}` : ''}">${g.items.map(tile).join('')}</ul>`).join('');
const end = more && safeHref(more.href) ? `<p class="sc-apps-more"><a href="${esc(safeHref(more.href))}">${esc(more.label || 'See all')} →</a></p>` : '';
return `<section class="sc-sec sc-apps-sec"${id(sid)}${labelled(title, h)}>${head(title, lede, h)}${body}${end}</section>`;
ddiff --git a/test/showcase.test.js b/test/showcase.test.js
iindex acd1772..97db5d7 100644
---- a/test/showcase.test.js
++++ b/test/showcase.test.js
@@@ -30,6 +30,11 @@ assert.strictEqual(sc.fold({ title: 'T', html: '' }), '', 'nothing to fold rende
assert.strictEqual(sc.fold({ html: '<p>x</p>' }), '', 'a fold needs a heading');
assert.ok(sc.CSS.includes('.sc-row.sc-row{display:flex;flex-wrap:nowrap'), 'the phone row does not inherit a wrap');
++// A set of six or nine apps is three a row; eight is four; seven keeps the grid's own columns.
++const appsN = (n) => sc.apps({ items: Array.from({ length: n }, (_, i) => ({ name: `App ${i}`, href: `/a${i}` })) });
++assert.ok(appsN(9).includes('sc-apps sc-apps--c3') && appsN(6).includes('sc-apps sc-apps--c3'), 'six and nine are three a row');
++assert.ok(appsN(8).includes('sc-apps sc-apps--c4') && !/sc-apps--c\d/.test(appsN(7)), 'eight is four; seven keeps the grid');
++
const c = sc.compare({ title: 'Compare', columns: ['Us', 'Them'], rows: [{ label: 'Open', values: [true, false] }, { label: 'Price', values: ['Free', 'Paid'] }] });
assert.ok(c.includes('aria-label="yes"') && c.includes('aria-label="no"') && c.includes('<td class="sc-us">Free</td>'));
const p = sc.pricing({ title: 'Plans', tiers: [{ name: 'Free', price: 0, per: 'month', items: ['a'] }, { name: 'Pro', price: '$5', per: 'month', highlight: true, action: { label: 'Buy', href: '/buy' } }] });
@@@ -179,7 +184,8 @@ assert.ok(/@container \(min-width:520px\) and \(max-width:1035px\)\{\.sc-grid:ha
// 4.39.0: no tile alone on a row. Four go four across on a phone and two by two from 600 px; eight like four.
const n = (k) => sc.apps({ items: Array.from({ length: k }, (_, i) => ({ name: `A${i}`, href: `/a${i}` })) });
assert.ok(n(4).includes('<ul class="sc-apps sc-apps--c4">') && n(3).includes('sc-apps--c3') && n(2).includes('sc-apps--c2') && n(8).includes('sc-apps--c4'));
-- assert.ok(n(7).includes('<ul class="sc-apps">') && n(15).includes('<ul class="sc-apps">'), 'other counts keep the auto grid');
++ // 4.46.1: six, nine and fifteen are three a row; seven and eleven keep the auto grid.
++ assert.ok(n(15).includes('sc-apps--c3') && n(7).includes('<ul class="sc-apps">') && n(11).includes('<ul class="sc-apps">'), 'other counts keep the auto grid');
assert.match(sc.CSS, /@media \(max-width:599px\)\{\.sc-apps--c2\{[^}]*\}\.sc-apps--c3\{[^}]*\}\.sc-apps--c4\{grid-template-columns:repeat\(4,minmax\(0,1fr\)\)\}\}/);
assert.match(sc.CSS, /\.sc-apps-more a\{display:inline-flex;align-items:center;min-height:44px;/, 'the launcher\'s See all is 44 px');
const css = require('fs').readFileSync(require('path').join(__dirname, '..', 'showcase.css'), 'utf8');