openvibers/openvibe.shared Commits
Shared 4.1.9: avatars are asked for in the size they are shown (Media's /o/:id?w=&fmt=) (#77)
Changes
@@@ -4,6 +4,14 @@ 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.1.9 — 2026-10-15++++- **Avatars are asked for in the size they are shown.** When a person's picture is an openvibe.media object++ (`/o/med_…`), the navbar asks for `?w=<size>&fmt=webp` (64 for the chip, 72 for the menu, 48 for the account++ switcher, already twice the shown size). The footer's 24 px avatar asks for 64. Media answers a resized WebP (Fabric++ F4, Media's `/o/:id?w=&fmt=`): a sampled 18.6 KB avatar is under 1 KB at 96 px. Any other picture address is used as++ it is.++## 4.1.8 — 2026-10-15- **Four showcase steps sit two by two below four columns.** In a section narrower than four steps across (under@@@ -276,8 +276,10 @@// ── Signed-in row: the same account, wherever you are ──────function accountHTML(user, service) {const name = esc(user.display_name || user.username || 'you');++ // An openvibe.media object picture comes in sizes (Media's /o/:id?w=&fmt=): 64 px as WebP for the 24 px avatar.++ const sized = /^https:\/\/openvibe\.media\/o\/med_[0-9A-Za-z]{10,40}$/.test(user.avatar_url || '') ? `${user.avatar_url}?w=64&fmt=webp` : user.avatar_url;const avatar = user.avatar_url-- ? `<img src="${esc(user.avatar_url)}" alt="">`++ ? `<img src="${esc(sized)}" alt="">`: `<span>${esc(String(name).charAt(0).toUpperCase())}</span>`;const quick = [service !== 'live' ? { name: 'My channel', url: `https://openvibe.live/@${esc(user.username || '')}`, icon: 'channel' } : { name: 'My dashboard', url: '/dashboard', icon: 'dashboard' },@@@ -1287,7 +1287,8 @@}function avatarSrc(user, size = 64) {-- return user?.avatar_url || makeAvatarPlaceholder(user, size);++ const url = user?.avatar_url;++ return /^https:\/\/openvibe\.media\/o\/med_\w+$/.test(url || '') ? `${url}?w=${size}&fmt=webp` : url || makeAvatarPlaceholder(user, size);}function avatarImg(user, size = 64, className = 'openvibe-navbar-avatar', id = '') {@@@ -1,6 +1,6 @@{"name": "openvibe-shared",-- "version": "4.1.8",++ "version": "4.1.9","description": "Shared navbar, footer, themes, icons, legal pages, SEO, auth and analytics for every OpenVibe site","main": "index.js","bin": {@@@ -162,6 +162,23 @@ const USER = { id: 7, username: 'vibe', display_name: 'Vibe', avatar_url: null,assert.strictEqual(calls.length, 1, 'exactly one auth call');}++ // ── 5b. An openvibe.media object avatar is asked for in the chip's size, as WebP (Media's /o/:id?w=&fmt=) ─++ for (const [avatar, want] of [++ ['https://openvibe.media/o/med_01KZK5GRQ088Y3CW1TMEF4Z4RN', 'src="https://openvibe.media/o/med_01KZK5GRQ088Y3CW1TMEF4Z4RN?w=64&fmt=webp"'],++ ['https://openvibe.media/f/b.png', 'src="https://openvibe.media/f/b.png"'],++ ]) {++ const { navbar } = loadNavbar({++ hostname: 'net.openvibe.tools', href: 'https://net.openvibe.tools/', cookie: 'ov_token=valid-jwt',++ fetch: async (url) => (url === 'https://openvibe.network/api/auth/me' ? jsonResponse(200, { user: { ...USER, avatar_url: avatar }, preferences: {} }) : jsonResponse(404, {})),++ });++ navbar.init({ service: 'net', apiBase: 'https://openvibe.network', token: 'valid-jwt' });++ await navbar.refreshAuth();++ assert.ok(navbar.getElement().innerHTML.includes(want), `${avatar}: ${want}`);++ }++++ // The footer's 24 px signed-in avatar asks for 64 px WebP of an object picture too.++ assert.match(require('fs').readFileSync(require('path').join(__dirname, '..', 'footer.js'), 'utf8'), /\? `\$\{user\.avatar_url\}\?w=64&fmt=webp` : user\.avatar_url;/, 'the footer sizes an object avatar');++// ── 6. No token, no sessionUrl → signed out, zero fetches ─{const { navbar, calls } = loadNavbar({ hostname: 'maps.openvibe.tools' });
@@ -4,6 +4,14 @@ All notable changes to `openvibe-shared`. Versions follow [semver](https://semve
@@ -4,6 +4,14 @@ 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.1.9 — 2026-10-15
+
+- **Avatars are asked for in the size they are shown.** When a person's picture is an openvibe.media object
+ (`/o/med_…`), the navbar asks for `?w=<size>&fmt=webp` (64 for the chip, 72 for the menu, 48 for the account
+ switcher, already twice the shown size). The footer's 24 px avatar asks for 64. Media answers a resized WebP (Fabric
+ F4, Media's `/o/:id?w=&fmt=`): a sampled 18.6 KB avatar is under 1 KB at 96 px. Any other picture address is used as
+ it is.
+
## 4.1.8 — 2026-10-15
## 4.1.8 — 2026-10-15
- **Four showcase steps sit two by two below four columns.** In a section narrower than four steps across (under
- **Four showcase steps sit two by two below four columns.** In a section narrower than four steps across (under
@@ -276,8 +276,10 @@
@@ -276,8 +276,10 @@
// ── Signed-in row: the same account, wherever you are ──────
// ── Signed-in row: the same account, wherever you are ──────
function accountHTML(user, service) {
function accountHTML(user, service) {
const name = esc(user.display_name || user.username || 'you');
const name = esc(user.display_name || user.username || 'you');
+ // An openvibe.media object picture comes in sizes (Media's /o/:id?w=&fmt=): 64 px as WebP for the 24 px avatar.
+ const sized = /^https:\/\/openvibe\.media\/o\/med_[0-9A-Za-z]{10,40}$/.test(user.avatar_url || '') ? `${user.avatar_url}?w=64&fmt=webp` : user.avatar_url;
const avatar = user.avatar_url
const avatar = user.avatar_url
- ? `<img src="${esc(user.avatar_url)}" alt="">`
+ ? `<img src="${esc(sized)}" alt="">`
: `<span>${esc(String(name).charAt(0).toUpperCase())}</span>`;
: `<span>${esc(String(name).charAt(0).toUpperCase())}</span>`;
const quick = [
const quick = [
service !== 'live' ? { name: 'My channel', url: `https://openvibe.live/@${esc(user.username || '')}`, icon: 'channel' } : { name: 'My dashboard', url: '/dashboard', icon: 'dashboard' },
service !== 'live' ? { name: 'My channel', url: `https://openvibe.live/@${esc(user.username || '')}`, icon: 'channel' } : { name: 'My dashboard', url: '/dashboard', icon: 'dashboard' },
@@ -1287,7 +1287,8 @@
@@ -1287,7 +1287,8 @@
}
}
function avatarSrc(user, size = 64) {
function avatarSrc(user, size = 64) {
- return user?.avatar_url || makeAvatarPlaceholder(user, size);
+ const url = user?.avatar_url;
+ return /^https:\/\/openvibe\.media\/o\/med_\w+$/.test(url || '') ? `${url}?w=${size}&fmt=webp` : url || makeAvatarPlaceholder(user, size);
}
}
function avatarImg(user, size = 64, className = 'openvibe-navbar-avatar', id = '') {
function avatarImg(user, size = 64, className = 'openvibe-navbar-avatar', id = '') {
@@ -1,6 +1,6 @@
@@ -1,6 +1,6 @@
{
{
"name": "openvibe-shared",
"name": "openvibe-shared",
- "version": "4.1.8",
+ "version": "4.1.9",
"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": {
@@ -162,6 +162,23 @@ const USER = { id: 7, username: 'vibe', display_name: 'Vibe', avatar_url: null,
@@ -162,6 +162,23 @@ const USER = { id: 7, username: 'vibe', display_name: 'Vibe', avatar_url: null,
assert.strictEqual(calls.length, 1, 'exactly one auth call');
assert.strictEqual(calls.length, 1, 'exactly one auth call');
}
}
+ // ── 5b. An openvibe.media object avatar is asked for in the chip's size, as WebP (Media's /o/:id?w=&fmt=) ─
+ for (const [avatar, want] of [
+ ['https://openvibe.media/o/med_01KZK5GRQ088Y3CW1TMEF4Z4RN', 'src="https://openvibe.media/o/med_01KZK5GRQ088Y3CW1TMEF4Z4RN?w=64&fmt=webp"'],
+ ['https://openvibe.media/f/b.png', 'src="https://openvibe.media/f/b.png"'],
+ ]) {
+ const { navbar } = loadNavbar({
+ hostname: 'net.openvibe.tools', href: 'https://net.openvibe.tools/', cookie: 'ov_token=valid-jwt',
+ fetch: async (url) => (url === 'https://openvibe.network/api/auth/me' ? jsonResponse(200, { user: { ...USER, avatar_url: avatar }, preferences: {} }) : jsonResponse(404, {})),
+ });
+ navbar.init({ service: 'net', apiBase: 'https://openvibe.network', token: 'valid-jwt' });
+ await navbar.refreshAuth();
+ assert.ok(navbar.getElement().innerHTML.includes(want), `${avatar}: ${want}`);
+ }
+
+ // The footer's 24 px signed-in avatar asks for 64 px WebP of an object picture too.
+ assert.match(require('fs').readFileSync(require('path').join(__dirname, '..', 'footer.js'), 'utf8'), /\? `\$\{user\.avatar_url\}\?w=64&fmt=webp` : user\.avatar_url;/, 'the footer sizes an object avatar');
+
// ── 6. No token, no sessionUrl → signed out, zero fetches ─
// ── 6. No token, no sessionUrl → signed out, zero fetches ─
{
{
const { navbar, calls } = loadNavbar({ hostname: 'maps.openvibe.tools' });
const { navbar, calls } = loadNavbar({ hostname: 'maps.openvibe.tools' });